BsStaticAlloc.h 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #pragma once
  4. namespace bs
  5. {
  6. /** @addtogroup Internal-Utility
  7. * @{
  8. */
  9. /** @addtogroup Memory-Internal
  10. * @{
  11. */
  12. /**
  13. * Static allocator that attempts to perform zero heap (dynamic) allocations by always keeping an active preallocated
  14. * buffer. The allocator provides a fixed amount of preallocated memory, and if the size of the allocated data goes over
  15. * that limit the allocator will fall back to dynamic heap allocations using the selected allocator.
  16. *
  17. * @note Static allocations can only be freed if memory is deallocated in opposite order it is allocated.
  18. * Otherwise static memory gets orphaned until a call to clear(). Dynamic memory allocations behave
  19. * depending on the selected allocator.
  20. *
  21. * @tparam BlockSize Size of the initially allocated static block, and minimum size of any dynamically
  22. * allocated memory.
  23. * @tparam DynamicAllocator Allocator to fall-back to when static buffer is full.
  24. */
  25. template<int BlockSize = 512, class DynamicAllocator = TFrameAlloc<BlockSize>>
  26. class StaticAlloc
  27. {
  28. private:
  29. /** A single block of memory within a static allocator. */
  30. class MemBlock
  31. {
  32. public:
  33. MemBlock(UINT8* data, UINT32 size)
  34. :mData(data), mFreePtr(0), mSize(size), mNextBlock(nullptr)
  35. { }
  36. /** Allocates a piece of memory within the block. Caller must ensure the block has enough empty space. */
  37. UINT8* alloc(UINT32 amount)
  38. {
  39. UINT8* freePtr = &mData[mFreePtr];
  40. mFreePtr += amount;
  41. return freePtr;
  42. }
  43. /**
  44. * Frees a piece of memory within the block. If the memory isn't the last allocated memory, no deallocation
  45. * happens and that memory is instead orphaned.
  46. */
  47. void free(UINT8* data, UINT32 allocSize)
  48. {
  49. if((data + allocSize) == (mData + mFreePtr))
  50. mFreePtr -= allocSize;
  51. }
  52. /** Releases all allocations within a block but doesn't actually free the memory. */
  53. void clear()
  54. {
  55. mFreePtr = 0;
  56. }
  57. UINT8* mData;
  58. UINT32 mFreePtr;
  59. UINT32 mSize;
  60. MemBlock* mNextBlock;
  61. };
  62. public:
  63. StaticAlloc()
  64. : mFreePtr(0), mTotalAllocBytes(0)
  65. { }
  66. ~StaticAlloc()
  67. { }
  68. /**
  69. * Allocates a new piece of memory of the specified size.
  70. *
  71. * @param[in] amount Amount of memory to allocate, in bytes.
  72. */
  73. UINT8* alloc(UINT32 amount)
  74. {
  75. if (amount == 0)
  76. return nullptr;
  77. #if BS_DEBUG_MODE
  78. amount += sizeof(UINT32);
  79. #endif
  80. UINT32 freeMem = BlockSize - mFreePtr;
  81. UINT8* data;
  82. if (amount > freeMem)
  83. data = mDynamicAlloc.alloc(amount);
  84. else
  85. {
  86. data = &mStaticData[mFreePtr];
  87. mFreePtr += amount;
  88. }
  89. #if BS_DEBUG_MODE
  90. mTotalAllocBytes += amount;
  91. UINT32* storedSize = reinterpret_cast<UINT32*>(data);
  92. *storedSize = amount;
  93. return data + sizeof(UINT32);
  94. #else
  95. return data;
  96. #endif
  97. }
  98. /** Deallocates a previously allocated piece of memory. */
  99. void free(void* data, UINT32 allocSize)
  100. {
  101. if (data == nullptr)
  102. return;
  103. UINT8* dataPtr = (UINT8*)data;
  104. #if BS_DEBUG_MODE
  105. dataPtr -= sizeof(UINT32);
  106. UINT32* storedSize = (UINT32*)(dataPtr);
  107. mTotalAllocBytes -= *storedSize;
  108. #endif
  109. if(data > mStaticData && data < (mStaticData + BlockSize))
  110. {
  111. if((((UINT8*)data) + allocSize) == (mStaticData + mFreePtr))
  112. mFreePtr -= allocSize;
  113. }
  114. else
  115. mDynamicAlloc.free(dataPtr);
  116. }
  117. /** Deallocates a previously allocated piece of memory. */
  118. void free(void* data)
  119. {
  120. if (data == nullptr)
  121. return;
  122. UINT8* dataPtr = (UINT8*)data;
  123. #if BS_DEBUG_MODE
  124. dataPtr -= sizeof(UINT32);
  125. UINT32* storedSize = (UINT32*)(dataPtr);
  126. mTotalAllocBytes -= *storedSize;
  127. #endif
  128. if(data < mStaticData || data >= (mStaticData + BlockSize))
  129. mDynamicAlloc.free(dataPtr);
  130. }
  131. /**
  132. * Allocates enough memory to hold the object(s) of specified type using the static allocator, and constructs them.
  133. */
  134. template<class T>
  135. T* construct(UINT32 count = 0)
  136. {
  137. T* data = (T*)alloc(sizeof(T) * count);
  138. for(unsigned int i = 0; i < count; i++)
  139. new ((void*)&data[i]) T;
  140. return data;
  141. }
  142. /**
  143. * Allocates enough memory to hold the object(s) of specified type using the static allocator, and constructs them.
  144. */
  145. template<class T, class... Args>
  146. T* construct(Args &&...args, UINT32 count = 0)
  147. {
  148. T* data = (T*)alloc(sizeof(T) * count);
  149. for(unsigned int i = 0; i < count; i++)
  150. new ((void*)&data[i]) T(std::forward<Args>(args)...);
  151. return data;
  152. }
  153. /** Destructs and deallocates an object allocated with the static allocator. */
  154. template<class T>
  155. void destruct(T* data)
  156. {
  157. data->~T();
  158. free(data, sizeof(T));
  159. }
  160. /** Destructs and deallocates an array of objects allocated with the static frame allocator. */
  161. template<class T>
  162. void destruct(T* data, UINT32 count)
  163. {
  164. for(unsigned int i = 0; i < count; i++)
  165. data[i].~T();
  166. free(data, sizeof(T) * count);
  167. }
  168. /** Frees the internal memory buffers. All external allocations must be freed before calling this. */
  169. void clear()
  170. {
  171. assert(mTotalAllocBytes == 0);
  172. mFreePtr = 0;
  173. mDynamicAlloc.clear();
  174. }
  175. private:
  176. UINT8 mStaticData[BlockSize];
  177. UINT32 mFreePtr;
  178. DynamicAllocator mDynamicAlloc;
  179. UINT32 mTotalAllocBytes;
  180. };
  181. /** Allocator for the standard library that internally uses a static allocator. */
  182. template <int BlockSize, class T>
  183. class StdStaticAlloc
  184. {
  185. public:
  186. typedef T value_type;
  187. typedef value_type* pointer;
  188. typedef const value_type* const_pointer;
  189. typedef value_type& reference;
  190. typedef const value_type& const_reference;
  191. typedef std::size_t size_type;
  192. typedef std::ptrdiff_t difference_type;
  193. StdStaticAlloc() = default;
  194. StdStaticAlloc(StaticAlloc<BlockSize, FreeAlloc>* alloc) noexcept
  195. :mStaticAlloc(alloc)
  196. { }
  197. template<class U> StdStaticAlloc(const StdStaticAlloc<BlockSize, U>& alloc) noexcept
  198. :mStaticAlloc(alloc.mStaticAlloc)
  199. { }
  200. template<class U> class rebind { public: typedef StdStaticAlloc<BlockSize, U> other; };
  201. /** Allocate but don't initialize number elements of type T.*/
  202. T* allocate(const size_t num) const
  203. {
  204. if (num == 0)
  205. return nullptr;
  206. if (num > static_cast<size_t>(-1) / sizeof(T))
  207. return nullptr; // Error
  208. void* const pv = mStaticAlloc->alloc((UINT32)(num * sizeof(T)));
  209. if (!pv)
  210. return nullptr; // Error
  211. return static_cast<T*>(pv);
  212. }
  213. /** Deallocate storage p of deleted elements. */
  214. void deallocate(T* p, size_t num) const noexcept
  215. {
  216. mStaticAlloc->free((UINT8*)p, (UINT32)num);
  217. }
  218. StaticAlloc<BlockSize, FreeAlloc>* mStaticAlloc = nullptr;
  219. size_t max_size() const { return std::numeric_limits<UINT32>::max() / sizeof(T); }
  220. void construct(pointer p, const_reference t) { new (p) T(t); }
  221. void destroy(pointer p) { p->~T(); }
  222. template<class U, class... Args>
  223. void construct(U* p, Args&&... args) { new(p) U(std::forward<Args>(args)...); }
  224. template <class T1, int N1, class T2, int N2>
  225. friend bool operator== (const StdStaticAlloc<N1, T1>& a, const StdStaticAlloc<N2, T2>& b) throw();
  226. };
  227. /** Return that all specializations of this allocator are interchangeable. */
  228. template <class T1, int N1, class T2, int N2>
  229. bool operator== (const StdStaticAlloc<N1, T1>& a, const StdStaticAlloc<N2, T2>& b) throw()
  230. {
  231. return N1 == N2 && a.mStaticAlloc == b.mStaticAlloc;
  232. }
  233. /** Return that all specializations of this allocator are interchangeable. */
  234. template <class T1, int N1, class T2, int N2>
  235. bool operator!= (const StdStaticAlloc<N1, T1>& a, const StdStaticAlloc<N2, T2>& b) throw()
  236. {
  237. return !(a == b);
  238. }
  239. /** @} */
  240. /** @} */
  241. /** @addtogroup Memory
  242. * @{
  243. */
  244. /**
  245. * Equivalent to Vector, except it avoids any dynamic allocations until the number of elements exceeds @p Count.
  246. * Requires allocator to be explicitly provided.
  247. */
  248. template <typename T, int Count>
  249. using StaticVector = std::vector<T, StdStaticAlloc<sizeof(T) * Count, T>>;
  250. /** @} */
  251. }