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