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