almalloc.h 11 KB

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