almalloc.h 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  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. void *al_malloc(size_t alignment, size_t size);
  13. void *al_calloc(size_t alignment, size_t size);
  14. void al_free(void *ptr) noexcept;
  15. #define DEF_NEWDEL(T) \
  16. void *operator new(size_t size) \
  17. { \
  18. void *ret = al_malloc(alignof(T), size); \
  19. if(!ret) throw std::bad_alloc(); \
  20. return ret; \
  21. } \
  22. void operator delete(void *block) noexcept { al_free(block); }
  23. #define DEF_PLACE_NEWDEL() \
  24. void *operator new(size_t /*size*/, void *ptr) noexcept { return ptr; } \
  25. void operator delete(void *block, void*) noexcept { al_free(block); } \
  26. void operator delete(void *block) noexcept { al_free(block); }
  27. struct FamCount { size_t mCount; };
  28. #define DEF_FAM_NEWDEL(T, FamMem) \
  29. static constexpr size_t Sizeof(size_t count) noexcept \
  30. { return decltype(FamMem)::Sizeof(count, offsetof(T, FamMem)); } \
  31. \
  32. void *operator new(size_t /*size*/, FamCount fam) \
  33. { \
  34. if(void *ret{al_malloc(alignof(T), T::Sizeof(fam.mCount))}) \
  35. return ret; \
  36. throw std::bad_alloc(); \
  37. } \
  38. void operator delete(void *block, FamCount) { al_free(block); } \
  39. void operator delete(void *block) noexcept { al_free(block); }
  40. namespace al {
  41. #define REQUIRES(...) typename std::enable_if<(__VA_ARGS__),int>::type = 0
  42. template<typename T, std::size_t alignment=alignof(T)>
  43. struct allocator {
  44. using value_type = T;
  45. using reference = T&;
  46. using const_reference = const T&;
  47. using pointer = T*;
  48. using const_pointer = const T*;
  49. using size_type = std::size_t;
  50. using difference_type = std::ptrdiff_t;
  51. using is_always_equal = std::true_type;
  52. template<typename U>
  53. struct rebind {
  54. using other = allocator<U, (alignment<alignof(U))?alignof(U):alignment>;
  55. };
  56. allocator() = default;
  57. template<typename U, std::size_t N>
  58. constexpr allocator(const allocator<U,N>&) noexcept { }
  59. T *allocate(std::size_t n)
  60. {
  61. if(n > std::numeric_limits<std::size_t>::max()/sizeof(T)) throw std::bad_alloc();
  62. if(auto p = static_cast<T*>(al_malloc(alignment, n*sizeof(T)))) return p;
  63. throw std::bad_alloc();
  64. }
  65. void deallocate(T *p, std::size_t) noexcept { al_free(p); }
  66. };
  67. template<typename T, std::size_t N, typename U, std::size_t M>
  68. bool operator==(const allocator<T,N>&, const allocator<U,M>&) noexcept { return true; }
  69. template<typename T, std::size_t N, typename U, std::size_t M>
  70. bool operator!=(const allocator<T,N>&, const allocator<U,M>&) noexcept { return false; }
  71. template<size_t alignment, typename T>
  72. inline T* assume_aligned(T *ptr) noexcept
  73. {
  74. static_assert((alignment & (alignment-1)) == 0, "alignment must be a power of 2");
  75. #ifdef __GNUC__
  76. return static_cast<T*>(__builtin_assume_aligned(ptr, alignment));
  77. #elif defined(_MSC_VER)
  78. auto ptrval = reinterpret_cast<uintptr_t>(ptr);
  79. if((ptrval&(alignment-1)) != 0) __assume(0);
  80. return reinterpret_cast<T*>(ptrval);
  81. #else
  82. return ptr;
  83. #endif
  84. }
  85. /* At least VS 2015 complains that 'ptr' is unused when the given type's
  86. * destructor is trivial (a no-op). So disable that warning for this call.
  87. */
  88. DIAGNOSTIC_PUSH
  89. msc_pragma(warning(disable : 4100))
  90. template<typename T>
  91. inline void destroy_at(T *ptr) { ptr->~T(); }
  92. DIAGNOSTIC_POP
  93. template<typename T>
  94. inline void destroy(T first, const T end)
  95. {
  96. while(first != end)
  97. {
  98. al::destroy_at(std::addressof(*first));
  99. ++first;
  100. }
  101. }
  102. template<typename T, typename N, REQUIRES(std::is_integral<N>::value)>
  103. inline T destroy_n(T first, N count)
  104. {
  105. if(count != 0)
  106. {
  107. do {
  108. al::destroy_at(std::addressof(*first));
  109. ++first;
  110. } while(--count);
  111. }
  112. return first;
  113. }
  114. template<typename T>
  115. inline void uninitialized_default_construct(T first, const T last)
  116. {
  117. using ValueT = typename std::iterator_traits<T>::value_type;
  118. T current{first};
  119. try {
  120. while(current != last)
  121. {
  122. ::new (static_cast<void*>(std::addressof(*current))) ValueT;
  123. ++current;
  124. }
  125. }
  126. catch(...) {
  127. destroy(first, current);
  128. throw;
  129. }
  130. }
  131. template<typename T, typename N, REQUIRES(std::is_integral<N>::value)>
  132. inline T uninitialized_default_construct_n(T first, N count)
  133. {
  134. using ValueT = typename std::iterator_traits<T>::value_type;
  135. T current{first};
  136. if(count != 0)
  137. {
  138. try {
  139. do {
  140. ::new (static_cast<void*>(std::addressof(*current))) ValueT;
  141. ++current;
  142. } while(--count);
  143. }
  144. catch(...) {
  145. destroy(first, current);
  146. throw;
  147. }
  148. }
  149. return current;
  150. }
  151. template<typename T0, typename T1>
  152. inline T1 uninitialized_move(T0 first, const T0 last, const T1 output)
  153. {
  154. using ValueT = typename std::iterator_traits<T1>::value_type;
  155. T1 current{output};
  156. try {
  157. while(first != last)
  158. {
  159. ::new (static_cast<void*>(std::addressof(*current))) ValueT{std::move(*first)};
  160. ++current;
  161. ++first;
  162. }
  163. }
  164. catch(...) {
  165. destroy(output, current);
  166. throw;
  167. }
  168. return current;
  169. }
  170. template<typename T0, typename N, typename T1, REQUIRES(std::is_integral<N>::value)>
  171. inline T1 uninitialized_move_n(T0 first, N count, const T1 output)
  172. {
  173. using ValueT = typename std::iterator_traits<T1>::value_type;
  174. T1 current{output};
  175. if(count != 0)
  176. {
  177. try {
  178. do {
  179. ::new (static_cast<void*>(std::addressof(*current))) ValueT{std::move(*first)};
  180. ++current;
  181. ++first;
  182. } while(--count);
  183. }
  184. catch(...) {
  185. destroy(output, current);
  186. throw;
  187. }
  188. }
  189. return current;
  190. }
  191. /* std::make_unique was added with C++14, so until we rely on that, make our
  192. * own version.
  193. */
  194. template<typename T, typename ...ArgsT>
  195. std::unique_ptr<T> make_unique(ArgsT&&...args)
  196. { return std::unique_ptr<T>{new T{std::forward<ArgsT>(args)...}}; }
  197. /* A flexible array type. Used either standalone or at the end of a parent
  198. * struct, with placement new, to have a run-time-sized array that's embedded
  199. * with its size.
  200. */
  201. template<typename T, size_t alignment=alignof(T)>
  202. struct FlexArray {
  203. using element_type = T;
  204. using value_type = typename std::remove_cv<T>::type;
  205. using index_type = size_t;
  206. using difference_type = ptrdiff_t;
  207. using pointer = T*;
  208. using const_pointer = const T*;
  209. using reference = T&;
  210. using const_reference = const T&;
  211. using iterator = pointer;
  212. using const_iterator = const_pointer;
  213. using reverse_iterator = std::reverse_iterator<iterator>;
  214. using const_reverse_iterator = std::reverse_iterator<const_iterator>;
  215. const index_type mSize;
  216. DIAGNOSTIC_PUSH
  217. std_pragma("GCC diagnostic ignored \"-Wpedantic\"")
  218. msc_pragma(warning(disable : 4200))
  219. alignas(alignment) element_type mArray[0];
  220. DIAGNOSTIC_POP
  221. static std::unique_ptr<FlexArray> Create(index_type count)
  222. {
  223. void *ptr{al_calloc(alignof(FlexArray), Sizeof(count))};
  224. return std::unique_ptr<FlexArray>{new (ptr) FlexArray{count}};
  225. }
  226. static constexpr index_type Sizeof(index_type count, index_type base=0u) noexcept
  227. {
  228. return base +
  229. std::max<index_type>(offsetof(FlexArray, mArray) + sizeof(T)*count, sizeof(FlexArray));
  230. }
  231. FlexArray(index_type size) : mSize{size}
  232. { uninitialized_default_construct_n(mArray, mSize); }
  233. ~FlexArray() { destroy_n(mArray, mSize); }
  234. FlexArray(const FlexArray&) = delete;
  235. FlexArray& operator=(const FlexArray&) = delete;
  236. index_type size() const noexcept { return mSize; }
  237. bool empty() const noexcept { return mSize == 0; }
  238. pointer data() noexcept { return mArray; }
  239. const_pointer data() const noexcept { return mArray; }
  240. reference operator[](index_type i) noexcept { return mArray[i]; }
  241. const_reference operator[](index_type i) const noexcept { return mArray[i]; }
  242. reference front() noexcept { return mArray[0]; }
  243. const_reference front() const noexcept { return mArray[0]; }
  244. reference back() noexcept { return mArray[mSize-1]; }
  245. const_reference back() const noexcept { return mArray[mSize-1]; }
  246. iterator begin() noexcept { return mArray; }
  247. const_iterator begin() const noexcept { return mArray; }
  248. const_iterator cbegin() const noexcept { return mArray; }
  249. iterator end() noexcept { return mArray + mSize; }
  250. const_iterator end() const noexcept { return mArray + mSize; }
  251. const_iterator cend() const noexcept { return mArray + mSize; }
  252. reverse_iterator rbegin() noexcept { return end(); }
  253. const_reverse_iterator rbegin() const noexcept { return end(); }
  254. const_reverse_iterator crbegin() const noexcept { return cend(); }
  255. reverse_iterator rend() noexcept { return begin(); }
  256. const_reverse_iterator rend() const noexcept { return begin(); }
  257. const_reverse_iterator crend() const noexcept { return cbegin(); }
  258. DEF_PLACE_NEWDEL()
  259. };
  260. #undef REQUIRES
  261. } // namespace al
  262. #endif /* AL_MALLOC_H */