BsStaticAlloc.h 6.8 KB

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