BsStaticAlloc.h 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. #pragma once
  2. namespace BansheeEngine
  3. {
  4. /** @cond INTERNAL */
  5. /** @addtogroup Memory
  6. * @{
  7. */
  8. /**
  9. * Static allocator that attempts to perform zero heap allocations by always keeping an active stack-allocated buffer.
  10. * If the size of allocated data goes over the set limit dynamic allocations will occur however.
  11. *
  12. * @note This kind of allocator is only able to free all of its memory at once. Freeing individual elements
  13. * will not free the memory until a call to clear().
  14. *
  15. * @tparam BlockSize Size of the initially allocated static block, and minimum size of any dynamically allocated memory.
  16. * @tparam MaxDynamicMemory Maximum amount of unused memory allowed in the buffer after a call to clear(). Keeping active dynamic
  17. * buffers can help prevent further memory allocations at the cost of memory. This is not relevant
  18. * if you stay within the bounds of the statically allocated memory.
  19. */
  20. template<int BlockSize = 512, int MaxDynamicMemory = 512>
  21. class StaticAlloc
  22. {
  23. private:
  24. /** A single block of memory within a static allocator. */
  25. class MemBlock
  26. {
  27. public:
  28. MemBlock(UINT8* data, UINT32 size)
  29. :mData(data), mFreePtr(0), mSize(size),
  30. mPrevBlock(nullptr), mNextBlock(nullptr)
  31. { }
  32. /** Allocates a piece of memory within the block. Caller must ensure the block has enough empty space. */
  33. UINT8* alloc(UINT32 amount)
  34. {
  35. UINT8* freePtr = &mData[mFreePtr];
  36. mFreePtr += amount;
  37. return freePtr;
  38. }
  39. /** Releases all allocations within a block but doesn't actually free the memory. */
  40. void clear()
  41. {
  42. mFreePtr = 0;
  43. }
  44. UINT8* mData;
  45. UINT32 mFreePtr;
  46. UINT32 mSize;
  47. MemBlock* mPrevBlock;
  48. MemBlock* mNextBlock;
  49. };
  50. public:
  51. StaticAlloc()
  52. :mStaticBlock(mStaticData, BlockSize), mFreeBlock(&mStaticBlock),
  53. mTotalAllocBytes(0)
  54. {
  55. }
  56. ~StaticAlloc()
  57. {
  58. assert(mFreeBlock == &mStaticBlock && mStaticBlock.mFreePtr == 0);
  59. freeBlocks(mFreeBlock);
  60. }
  61. /**
  62. * Allocates a new piece of memory of the specified size.
  63. *
  64. * @param[in] amount Amount of memory to allocate, in bytes.
  65. */
  66. UINT8* alloc(UINT32 amount)
  67. {
  68. #if BS_DEBUG_MODE
  69. amount += sizeof(UINT32);
  70. #endif
  71. UINT32 freeMem = mFreeBlock->mSize - mFreeBlock->mFreePtr;
  72. if (amount > freeMem)
  73. allocBlock(amount);
  74. UINT8* data = mFreeBlock->alloc(amount);
  75. #if BS_DEBUG_MODE
  76. mTotalAllocBytes += amount;
  77. UINT32* storedSize = reinterpret_cast<UINT32*>(data);
  78. *storedSize = amount;
  79. return data + sizeof(UINT32);
  80. #else
  81. return data;
  82. #endif
  83. }
  84. /** Deallocates a previously allocated piece of memory. */
  85. void free(void* data)
  86. {
  87. // Dealloc is only used for debug and can be removed if needed. All the actual deallocation
  88. // happens in clear()
  89. #if BS_DEBUG_MODE
  90. UINT8* dataPtr = (UINT8*)data;
  91. dataPtr -= sizeof(UINT32);
  92. UINT32* storedSize = (UINT32*)(dataPtr);
  93. mTotalAllocBytes -= *storedSize;
  94. #endif
  95. }
  96. /** Frees the internal memory buffers. All external allocations must be freed before calling this. */
  97. void clear()
  98. {
  99. assert(mTotalAllocBytes == 0);
  100. MemBlock* dynamicBlock = mStaticBlock.mNextBlock;
  101. INT32 totalDynamicMemAmount = 0;
  102. UINT32 numDynamicBlocks = 0;
  103. while (dynamicBlock != nullptr)
  104. {
  105. totalDynamicMemAmount += dynamicBlock->mFreePtr;
  106. dynamicBlock->clear();
  107. dynamicBlock = dynamicBlock->mNextBlock;
  108. numDynamicBlocks++;
  109. }
  110. mFreeBlock = &mStaticBlock;
  111. mStaticBlock.clear();
  112. if (numDynamicBlocks > 1)
  113. {
  114. freeBlocks(&mStaticBlock);
  115. allocBlock(std::min(totalDynamicMemAmount, MaxDynamicMemory));
  116. mFreeBlock = &mStaticBlock;
  117. }
  118. else if (numDynamicBlocks == 1 && MaxDynamicMemory == 0)
  119. {
  120. freeBlocks(&mStaticBlock);
  121. }
  122. }
  123. private:
  124. UINT8 mStaticData[BlockSize];
  125. MemBlock mStaticBlock;
  126. MemBlock* mFreeBlock;
  127. UINT32 mTotalAllocBytes;
  128. /**
  129. * Allocates a dynamic block of memory of the wanted size. The exact allocation size might be slightly higher in
  130. * order to store block meta data.
  131. */
  132. MemBlock* allocBlock(UINT32 wantedSize)
  133. {
  134. UINT32 blockSize = BlockSize;
  135. if (wantedSize > blockSize)
  136. blockSize = wantedSize;
  137. MemBlock* dynamicBlock = mFreeBlock->mNextBlock;
  138. MemBlock* newBlock = nullptr;
  139. while (dynamicBlock != nullptr)
  140. {
  141. if (dynamicBlock->mSize >= blockSize)
  142. {
  143. newBlock = dynamicBlock;
  144. break;
  145. }
  146. dynamicBlock = dynamicBlock->mNextBlock;
  147. }
  148. if (newBlock == nullptr)
  149. {
  150. UINT8* data = (UINT8*)reinterpret_cast<UINT8*>(bs_alloc(blockSize + sizeof(MemBlock)));
  151. newBlock = new (data)MemBlock(data + sizeof(MemBlock), blockSize);
  152. newBlock->mPrevBlock = mFreeBlock;
  153. mFreeBlock->mNextBlock = newBlock;
  154. }
  155. mFreeBlock = newBlock;
  156. return newBlock;
  157. }
  158. /** Releases memory for any dynamic blocks following the provided block (if there are any). */
  159. void freeBlocks(MemBlock* start)
  160. {
  161. MemBlock* dynamicBlock = start->mNextBlock;
  162. while (dynamicBlock != nullptr)
  163. {
  164. MemBlock* nextBlock = dynamicBlock->mNextBlock;
  165. dynamicBlock->~MemBlock();
  166. bs_free(dynamicBlock);
  167. dynamicBlock = nextBlock;
  168. }
  169. start->mNextBlock = nullptr;
  170. }
  171. };
  172. /** @} */
  173. /** @endcond */
  174. }