BsStaticAlloc.h 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #pragma once
  4. namespace BansheeEngine
  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.
  16. *
  17. * @note This kind of allocator is only able to free all of its memory at once. Freeing individual elements
  18. * will not free the memory until a call to clear().
  19. *
  20. * @tparam BlockSize Size of the initially allocated static block, and minimum size of any dynamically
  21. * allocated memory.
  22. * @tparam MaxDynamicMemory Maximum amount of unused memory allowed in the buffer after a call to clear(). Keeping
  23. * active dynamic buffers can help prevent further memory allocations at the cost of
  24. * memory. This is not relevant if you stay within the bounds of the statically allocated
  25. * memory.
  26. */
  27. template<int BlockSize = 512, int MaxDynamicMemory = 512>
  28. class StaticAlloc
  29. {
  30. private:
  31. /** A single block of memory within a static allocator. */
  32. class MemBlock
  33. {
  34. public:
  35. MemBlock(UINT8* data, UINT32 size)
  36. :mData(data), mFreePtr(0), mSize(size),
  37. mPrevBlock(nullptr), mNextBlock(nullptr)
  38. { }
  39. /** Allocates a piece of memory within the block. Caller must ensure the block has enough empty space. */
  40. UINT8* alloc(UINT32 amount)
  41. {
  42. UINT8* freePtr = &mData[mFreePtr];
  43. mFreePtr += amount;
  44. return freePtr;
  45. }
  46. /** Releases all allocations within a block but doesn't actually free the memory. */
  47. void clear()
  48. {
  49. mFreePtr = 0;
  50. }
  51. UINT8* mData;
  52. UINT32 mFreePtr;
  53. UINT32 mSize;
  54. MemBlock* mPrevBlock;
  55. MemBlock* mNextBlock;
  56. };
  57. public:
  58. StaticAlloc()
  59. :mStaticBlock(mStaticData, BlockSize), mFreeBlock(&mStaticBlock),
  60. mTotalAllocBytes(0)
  61. {
  62. }
  63. ~StaticAlloc()
  64. {
  65. assert(mFreeBlock == &mStaticBlock && mStaticBlock.mFreePtr == 0);
  66. freeBlocks(mFreeBlock);
  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 = mFreeBlock->mSize - mFreeBlock->mFreePtr;
  81. if (amount > freeMem)
  82. allocBlock(amount);
  83. UINT8* data = mFreeBlock->alloc(amount);
  84. #if BS_DEBUG_MODE
  85. mTotalAllocBytes += amount;
  86. UINT32* storedSize = reinterpret_cast<UINT32*>(data);
  87. *storedSize = amount;
  88. return data + sizeof(UINT32);
  89. #else
  90. return data;
  91. #endif
  92. }
  93. /** Deallocates a previously allocated piece of memory. */
  94. void free(void* data)
  95. {
  96. if (data == nullptr)
  97. return;
  98. // Dealloc is only used for debug and can be removed if needed. All the actual deallocation
  99. // happens in clear()
  100. #if BS_DEBUG_MODE
  101. UINT8* dataPtr = (UINT8*)data;
  102. dataPtr -= sizeof(UINT32);
  103. UINT32* storedSize = (UINT32*)(dataPtr);
  104. mTotalAllocBytes -= *storedSize;
  105. #endif
  106. }
  107. /**
  108. * Allocates enough memory to hold the object(s) of specified type using the static allocator, and constructs them.
  109. */
  110. template<class T>
  111. T* construct(UINT32 count = 0)
  112. {
  113. T* data = (T*)alloc(sizeof(T) * count);
  114. for(unsigned int i = 0; i < count; i++)
  115. new ((void*)&data[i]) T;
  116. return data;
  117. }
  118. /**
  119. * Allocates enough memory to hold the object(s) of specified type using the static allocator, and constructs them.
  120. */
  121. template<class T, class... Args>
  122. T* construct(Args &&...args, UINT32 count = 0)
  123. {
  124. T* data = (T*)alloc(sizeof(T) * count);
  125. for(unsigned int i = 0; i < count; i++)
  126. new ((void*)&data[i]) T(std::forward<Args>(args)...);
  127. return data;
  128. }
  129. /** Destructs and deallocates an object allocated with the static allocator. */
  130. template<class T>
  131. void destruct(T* data)
  132. {
  133. data->~T();
  134. free(data);
  135. }
  136. /** Destructs and deallocates an array of objects allocated with the static frame allocator. */
  137. template<class T>
  138. void destruct(T* data, UINT32 count)
  139. {
  140. for(unsigned int i = 0; i < count; i++)
  141. data[i].~T();
  142. free(data);
  143. }
  144. /** Frees the internal memory buffers. All external allocations must be freed before calling this. */
  145. void clear()
  146. {
  147. assert(mTotalAllocBytes == 0);
  148. MemBlock* dynamicBlock = mStaticBlock.mNextBlock;
  149. INT32 totalDynamicMemAmount = 0;
  150. UINT32 numDynamicBlocks = 0;
  151. while (dynamicBlock != nullptr)
  152. {
  153. totalDynamicMemAmount += dynamicBlock->mFreePtr;
  154. dynamicBlock->clear();
  155. dynamicBlock = dynamicBlock->mNextBlock;
  156. numDynamicBlocks++;
  157. }
  158. mFreeBlock = &mStaticBlock;
  159. mStaticBlock.clear();
  160. if (numDynamicBlocks > 1)
  161. {
  162. freeBlocks(&mStaticBlock);
  163. allocBlock(std::min(totalDynamicMemAmount, MaxDynamicMemory));
  164. mFreeBlock = &mStaticBlock;
  165. }
  166. else if (numDynamicBlocks == 1 && MaxDynamicMemory == 0)
  167. {
  168. freeBlocks(&mStaticBlock);
  169. }
  170. }
  171. private:
  172. UINT8 mStaticData[BlockSize];
  173. MemBlock mStaticBlock;
  174. MemBlock* mFreeBlock;
  175. UINT32 mTotalAllocBytes;
  176. /**
  177. * Allocates a dynamic block of memory of the wanted size. The exact allocation size might be slightly higher in
  178. * order to store block meta data.
  179. */
  180. MemBlock* allocBlock(UINT32 wantedSize)
  181. {
  182. UINT32 blockSize = BlockSize;
  183. if (wantedSize > blockSize)
  184. blockSize = wantedSize;
  185. MemBlock* dynamicBlock = mFreeBlock->mNextBlock;
  186. MemBlock* newBlock = nullptr;
  187. while (dynamicBlock != nullptr)
  188. {
  189. if (dynamicBlock->mSize >= blockSize)
  190. {
  191. newBlock = dynamicBlock;
  192. break;
  193. }
  194. dynamicBlock = dynamicBlock->mNextBlock;
  195. }
  196. if (newBlock == nullptr)
  197. {
  198. UINT8* data = (UINT8*)reinterpret_cast<UINT8*>(bs_alloc(blockSize + sizeof(MemBlock)));
  199. newBlock = new (data)MemBlock(data + sizeof(MemBlock), blockSize);
  200. newBlock->mPrevBlock = mFreeBlock;
  201. mFreeBlock->mNextBlock = newBlock;
  202. }
  203. mFreeBlock = newBlock;
  204. return newBlock;
  205. }
  206. /** Releases memory for any dynamic blocks following the provided block (if there are any). */
  207. void freeBlocks(MemBlock* start)
  208. {
  209. MemBlock* dynamicBlock = start->mNextBlock;
  210. while (dynamicBlock != nullptr)
  211. {
  212. MemBlock* nextBlock = dynamicBlock->mNextBlock;
  213. dynamicBlock->~MemBlock();
  214. bs_free(dynamicBlock);
  215. dynamicBlock = nextBlock;
  216. }
  217. start->mNextBlock = nullptr;
  218. }
  219. };
  220. /** @} */
  221. /** @} */
  222. }