1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
base / memory / aligned_memory.h [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_MEMORY_ALIGNED_MEMORY_H_
#define BASE_MEMORY_ALIGNED_MEMORY_H_
#include <stddef.h>
#include <stdint.h>
#include <algorithm>
#include <bit>
#include <ostream>
#include "base/base_export.h"
#include "base/check.h"
#include "base/containers/heap_array.h"
#include "base/containers/span.h"
#include "build/build_config.h"
#if defined(COMPILER_MSVC)
#include <malloc.h>
#else
#include <stdlib.h>
#endif
// A runtime sized aligned allocation for objects of type `T` with a runtime
// sized alignment:
//
// base::AlignedHeapArray<float> array = base::AlignedUninit<float>(
// size, alignment);
// CHECK(reinterpret_cast<uintptr_t>(array.data()) % alignment == 0);
//
// A runtime sized aligned allocation for objects of type `T` but represented as
// a char array, along with a span accessing that memory as `T*` for in-place
// construction:
//
// auto [a, s] = base::AlignedUninitCharArray<float>(size, alignment);
// base::AlignedHeapArray<char> array = std::move(a);
// base::span<float> span = s;
// CHECK(reinterpret_cast<uintptr_t>(array.data()) % alignment == 0);
// CHECK(reinterpret_cast<uintptr_t>(span.data()) % alignment == 0);
//
// With manual memory management, a runtime sized aligned allocation can be
// created:
//
// float* my_array = static_cast<float*>(AlignedAlloc(size, alignment));
// CHECK(reinterpret_cast<uintptr_t>(my_array) % alignment == 0);
// memset(my_array, 0, size); // fills entire object.
//
// // ... later, to release the memory:
// AlignedFree(my_array);
namespace base {
// Allocate memory of size `size` aligned to `alignment`.
//
// Prefer `AlignedUninit()` to make a `base::HeapArray` that has a runtime-sized
// alignment.
//
// When the caller will be managing the lifetimes of the objects in the array
// with in-place construction and destruction, `AlignedUninitCharArray()`
// provides safe ownership of the memory and access to memory aligned for `T` as
// `span<T>`.
//
// TODO(https://crbug.com/40255447): Convert usage to / convert to use
// `std::aligned_alloc` to the extent that it can be done (since
// `std::aligned_alloc` can't be used on Windows). When that happens, note that
// `std::aligned_alloc` requires the `size` parameter be an integral multiple of
// `alignment` while this implementation does not.
BASE_EXPORT void* AlignedAlloc(size_t size, size_t alignment);
// Deallocate memory allocated by `AlignedAlloc`.
inline void AlignedFree(void* ptr) {
#if defined(COMPILER_MSVC)
_aligned_free(ptr);
#else
free(ptr);
#endif
}
// Deleter for use with unique_ptr. E.g., use as
// std::unique_ptr<Foo, base::AlignedFreeDeleter> foo;
struct AlignedFreeDeleter {
inline void operator()(void* ptr) const {
AlignedFree(ptr);
}
};
template <class T>
using AlignedHeapArray = HeapArray<T, AlignedFreeDeleter>;
// Constructs a `base::AlignedHeapArray<T>` that is sized to hold `capacity`
// many objects of type `T` and is aligned to `alignment`. The memory is
// uninitialized.
//
// The `alignment` defaults to `alignof(T)` and can be omitted, but the
// alignment used will always be at least the alignment of a pointer.
template <class T>
AlignedHeapArray<T> AlignedUninit(size_t capacity,
size_t alignment = alignof(T)) {
alignment = std::max(alignment, alignof(void*));
CHECK_GE(alignment, alignof(T));
CHECK_LE(capacity, SIZE_MAX / sizeof(T));
const size_t bytes = capacity * sizeof(T);
// SAFETY: AlignedAlloc() allocates `bytes` many chars, which has room for
// `capacity` many `T` objects by construction, so we specify `capacity` as
// the size of the `HeapArray<T>`.
return UNSAFE_BUFFERS(HeapArray<T, AlignedFreeDeleter>::FromOwningPointer(
static_cast<T*>(AlignedAlloc(bytes, alignment)), capacity));
}
// Constructs a AlignedHeapArray<char> that is sized to hold `capacity` many
// objects of type `T` and is aligned to `alignment`.
//
// The `alignment` defaults to `alignof(T)` and can be omitted, but the
// alignment used will always be at least the alignment of a pointer.
//
// Returns a pair of `AlignedHeapArray<char>` and a `span<T>` for the entire
// _uninitialized_ `AlignedHeapArray`.
//
// It is up to the caller to construct objects of type `T` in the array
// in-place, and to destruct them before destroying the `AlignedHeapArray`.
//
// Note that using `span[index]` to make a reference to uninitialized memory is
// undefined behaviour. In-place construction must use the unsafe `span.data() +
// index` to avoid constructing a reference.
template <class T>
auto AlignedUninitCharArray(size_t capacity, size_t alignment = alignof(T)) {
alignment = std::max(alignment, alignof(void*));
CHECK_GE(alignment, alignof(T));
CHECK_LE(capacity, SIZE_MAX / sizeof(T));
const size_t bytes = capacity * sizeof(T);
// SAFETY: AlignedAlloc() allocates `bytes` many chars, and we give the same
// `bytes` as the size for `HeapArray`.
auto uninit_array =
UNSAFE_BUFFERS(HeapArray<char, AlignedFreeDeleter>::FromOwningPointer(
static_cast<char*>(AlignedAlloc(bytes, alignment)), bytes));
// SAFETY: `uninit_array` holds `capacity * sizeof(T)` bytes, so it has room
// for `capacity` many objects of type `T`.
auto uninit_span =
UNSAFE_BUFFERS(span(reinterpret_cast<T*>(uninit_array.data()), capacity));
return std::make_pair(std::move(uninit_array), std::move(uninit_span));
}
#ifdef __has_builtin
#define SUPPORTS_BUILTIN_IS_ALIGNED (__has_builtin(__builtin_is_aligned))
#else
#define SUPPORTS_BUILTIN_IS_ALIGNED 0
#endif
inline bool IsAligned(uintptr_t val, size_t alignment) {
// If the compiler supports builtin alignment checks prefer them.
#if SUPPORTS_BUILTIN_IS_ALIGNED
return __builtin_is_aligned(val, alignment);
#else
DCHECK(std::has_single_bit(alignment)) << alignment << " is not a power of 2";
return (val & (alignment - 1)) == 0;
#endif
}
#undef SUPPORTS_BUILTIN_IS_ALIGNED
inline bool IsAligned(const void* val, size_t alignment) {
return IsAligned(reinterpret_cast<uintptr_t>(val), alignment);
}
} // namespace base
#endif // BASE_MEMORY_ALIGNED_MEMORY_H_