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