1 #ifndef AL_MALLOC_H
2 #define AL_MALLOC_H
3
4 #include <algorithm>
5 #include <cstddef>
6 #include <iterator>
7 #include <limits>
8 #include <memory>
9 #include <new>
10 #include <type_traits>
11 #include <utility>
12
13 #include "pragmadefs.h"
14
15
16 [[gnu::alloc_align(1), gnu::alloc_size(2)]] void *al_malloc(size_t alignment, size_t size);
17 [[gnu::alloc_align(1), gnu::alloc_size(2)]] void *al_calloc(size_t alignment, size_t size);
18 void al_free(void *ptr) noexcept;
19
20
21 #define DISABLE_ALLOC() \
22 void *operator new(size_t) = delete; \
23 void *operator new[](size_t) = delete; \
24 void operator delete(void*) noexcept = delete; \
25 void operator delete[](void*) noexcept = delete;
26
27 #define DEF_NEWDEL(T) \
28 void *operator new(size_t size) \
29 { \
30 void *ret = al_malloc(alignof(T), size); \
31 if(!ret) throw std::bad_alloc(); \
32 return ret; \
33 } \
34 void *operator new[](size_t size) { return operator new(size); } \
35 void operator delete(void *block) noexcept { al_free(block); } \
36 void operator delete[](void *block) noexcept { operator delete(block); }
37
38 #define DEF_PLACE_NEWDEL() \
39 void *operator new(size_t /*size*/, void *ptr) noexcept { return ptr; } \
40 void *operator new[](size_t /*size*/, void *ptr) noexcept { return ptr; } \
41 void operator delete(void *block, void*) noexcept { al_free(block); } \
42 void operator delete(void *block) noexcept { al_free(block); } \
43 void operator delete[](void *block, void*) noexcept { al_free(block); } \
44 void operator delete[](void *block) noexcept { al_free(block); }
45
46 enum FamCount : size_t { };
47
48 #define DEF_FAM_NEWDEL(T, FamMem) \
49 static constexpr size_t Sizeof(size_t count) noexcept \
50 { \
51 return std::max<size_t>(sizeof(T), \
52 decltype(FamMem)::Sizeof(count, offsetof(T, FamMem))); \
53 } \
54 \
55 void *operator new(size_t /*size*/, FamCount count) \
56 { \
57 if(void *ret{al_malloc(alignof(T), T::Sizeof(count))}) \
58 return ret; \
59 throw std::bad_alloc(); \
60 } \
61 void *operator new[](size_t /*size*/) = delete; \
62 void operator delete(void *block, FamCount) { al_free(block); } \
63 void operator delete(void *block) noexcept { al_free(block); } \
64 void operator delete[](void* /*block*/) = delete;
65
66
67 namespace al {
68
69 template<typename T, std::size_t alignment=alignof(T)>
70 struct allocator {
71 using value_type = T;
72 using reference = T&;
73 using const_reference = const T&;
74 using pointer = T*;
75 using const_pointer = const T*;
76 using size_type = std::size_t;
77 using difference_type = std::ptrdiff_t;
78 using is_always_equal = std::true_type;
79
80 template<typename U>
81 struct rebind {
82 using other = allocator<U, (alignment<alignof(U))?alignof(U):alignment>;
83 };
84
85 constexpr explicit allocator() noexcept = default;
86 template<typename U, std::size_t N>
allocatorallocator87 constexpr explicit allocator(const allocator<U,N>&) noexcept { }
88
allocateallocator89 T *allocate(std::size_t n)
90 {
91 if(n > std::numeric_limits<std::size_t>::max()/sizeof(T)) throw std::bad_alloc();
92 if(auto p = al_malloc(alignment, n*sizeof(T))) return static_cast<T*>(p);
93 throw std::bad_alloc();
94 }
deallocateallocator95 void deallocate(T *p, std::size_t) noexcept { al_free(p); }
96 };
97 template<typename T, std::size_t N, typename U, std::size_t M>
98 bool operator==(const allocator<T,N>&, const allocator<U,M>&) noexcept { return true; }
99 template<typename T, std::size_t N, typename U, std::size_t M>
100 bool operator!=(const allocator<T,N>&, const allocator<U,M>&) noexcept { return false; }
101
102 template<size_t alignment, typename T>
assume_aligned(T * ptr)103 [[gnu::assume_aligned(alignment)]] inline T* assume_aligned(T *ptr) noexcept { return ptr; }
104
105 /* At least VS 2015 complains that 'ptr' is unused when the given type's
106 * destructor is trivial (a no-op). So disable that warning for this call.
107 */
108 DIAGNOSTIC_PUSH
109 msc_pragma(warning(disable : 4100))
110 template<typename T>
111 constexpr std::enable_if_t<!std::is_array<T>::value>
destroy_at(T * ptr)112 destroy_at(T *ptr) noexcept(std::is_nothrow_destructible<T>::value)
113 { ptr->~T(); }
114 DIAGNOSTIC_POP
115 template<typename T>
116 constexpr std::enable_if_t<std::is_array<T>::value>
destroy_at(T * ptr)117 destroy_at(T *ptr) noexcept(std::is_nothrow_destructible<T>::value)
118 {
119 for(auto &elem : *ptr)
120 al::destroy_at(std::addressof(elem));
121 }
122
123 template<typename T>
destroy(T first,T end)124 constexpr void destroy(T first, T end)
125 {
126 while(first != end)
127 {
128 al::destroy_at(std::addressof(*first));
129 ++first;
130 }
131 }
132
133 template<typename T, typename N>
134 constexpr std::enable_if_t<std::is_integral<N>::value,T>
destroy_n(T first,N count)135 destroy_n(T first, N count)
136 {
137 if(count != 0)
138 {
139 do {
140 al::destroy_at(std::addressof(*first));
141 ++first;
142 } while(--count);
143 }
144 return first;
145 }
146
147
148 template<typename T, typename N>
149 inline std::enable_if_t<std::is_integral<N>::value,T>
uninitialized_default_construct_n(T first,N count)150 uninitialized_default_construct_n(T first, N count)
151 {
152 using ValueT = typename std::iterator_traits<T>::value_type;
153 T current{first};
154 if(count != 0)
155 {
156 try {
157 do {
158 ::new(static_cast<void*>(std::addressof(*current))) ValueT;
159 ++current;
160 } while(--count);
161 }
162 catch(...) {
163 al::destroy(first, current);
164 throw;
165 }
166 }
167 return current;
168 }
169
170
171 /* Storage for flexible array data. This is trivially destructible if type T is
172 * trivially destructible.
173 */
174 template<typename T, size_t alignment, bool = std::is_trivially_destructible<T>::value>
175 struct FlexArrayStorage;
176
177 template<typename T, size_t alignment>
178 struct FlexArrayStorage<T,alignment,true> {
179 const size_t mSize;
180 union {
181 char mDummy;
182 alignas(alignment) T mArray[1];
183 };
184
185 static constexpr size_t Sizeof(size_t count, size_t base=0u) noexcept
186 {
187 return std::max<size_t>(offsetof(FlexArrayStorage, mArray) + sizeof(T)*count,
188 sizeof(FlexArrayStorage)) + base;
189 }
190
191 FlexArrayStorage(size_t size) : mSize{size}
192 { al::uninitialized_default_construct_n(mArray, mSize); }
193 ~FlexArrayStorage() = default;
194
195 FlexArrayStorage(const FlexArrayStorage&) = delete;
196 FlexArrayStorage& operator=(const FlexArrayStorage&) = delete;
197 };
198
199 template<typename T, size_t alignment>
200 struct FlexArrayStorage<T,alignment,false> {
201 const size_t mSize;
202 union {
203 char mDummy;
204 alignas(alignment) T mArray[1];
205 };
206
207 static constexpr size_t Sizeof(size_t count, size_t base) noexcept
208 {
209 return std::max<size_t>(offsetof(FlexArrayStorage, mArray) + sizeof(T)*count,
210 sizeof(FlexArrayStorage)) + base;
211 }
212
213 FlexArrayStorage(size_t size) : mSize{size}
214 { al::uninitialized_default_construct_n(mArray, mSize); }
215 ~FlexArrayStorage() { al::destroy_n(mArray, mSize); }
216
217 FlexArrayStorage(const FlexArrayStorage&) = delete;
218 FlexArrayStorage& operator=(const FlexArrayStorage&) = delete;
219 };
220
221 /* A flexible array type. Used either standalone or at the end of a parent
222 * struct, with placement new, to have a run-time-sized array that's embedded
223 * with its size.
224 */
225 template<typename T, size_t alignment=alignof(T)>
226 struct FlexArray {
227 using element_type = T;
228 using value_type = std::remove_cv_t<T>;
229 using index_type = size_t;
230 using difference_type = ptrdiff_t;
231
232 using pointer = T*;
233 using const_pointer = const T*;
234 using reference = T&;
235 using const_reference = const T&;
236
237 using iterator = pointer;
238 using const_iterator = const_pointer;
239 using reverse_iterator = std::reverse_iterator<iterator>;
240 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
241
242 using Storage_t_ = FlexArrayStorage<element_type,alignment>;
243
244 Storage_t_ mStore;
245
246 static constexpr index_type Sizeof(index_type count, index_type base=0u) noexcept
247 { return Storage_t_::Sizeof(count, base); }
248 static std::unique_ptr<FlexArray> Create(index_type count)
249 {
250 void *ptr{al_calloc(alignof(FlexArray), Sizeof(count))};
251 return std::unique_ptr<FlexArray>{new(ptr) FlexArray{count}};
252 }
253
254 FlexArray(index_type size) : mStore{size} { }
255 ~FlexArray() = default;
256
257 index_type size() const noexcept { return mStore.mSize; }
258 bool empty() const noexcept { return mStore.mSize == 0; }
259
260 pointer data() noexcept { return mStore.mArray; }
261 const_pointer data() const noexcept { return mStore.mArray; }
262
263 reference operator[](index_type i) noexcept { return mStore.mArray[i]; }
264 const_reference operator[](index_type i) const noexcept { return mStore.mArray[i]; }
265
266 reference front() noexcept { return mStore.mArray[0]; }
267 const_reference front() const noexcept { return mStore.mArray[0]; }
268
269 reference back() noexcept { return mStore.mArray[mStore.mSize-1]; }
270 const_reference back() const noexcept { return mStore.mArray[mStore.mSize-1]; }
271
272 iterator begin() noexcept { return mStore.mArray; }
273 const_iterator begin() const noexcept { return mStore.mArray; }
274 const_iterator cbegin() const noexcept { return mStore.mArray; }
275 iterator end() noexcept { return mStore.mArray + mStore.mSize; }
276 const_iterator end() const noexcept { return mStore.mArray + mStore.mSize; }
277 const_iterator cend() const noexcept { return mStore.mArray + mStore.mSize; }
278
279 reverse_iterator rbegin() noexcept { return end(); }
280 const_reverse_iterator rbegin() const noexcept { return end(); }
281 const_reverse_iterator crbegin() const noexcept { return cend(); }
282 reverse_iterator rend() noexcept { return begin(); }
283 const_reverse_iterator rend() const noexcept { return begin(); }
284 const_reverse_iterator crend() const noexcept { return cbegin(); }
285
286 DEF_PLACE_NEWDEL()
287 };
288
289 } // namespace al
290
291 #endif /* AL_MALLOC_H */
292