BsMemStack.h 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. #pragma once
  2. #include <stack>
  3. #include <assert.h>
  4. #include "BsStdHeaders.h"
  5. #include "BsThreadDefines.h"
  6. namespace BansheeEngine
  7. {
  8. /** @addtogroup Memory
  9. * @{
  10. */
  11. /** @cond INTERNAL */
  12. /**
  13. * Describes a memory stack of a certain block capacity. See ::MemStack for more information.
  14. *
  15. * @tparam BlockCapacity Minimum size of a block. Larger blocks mean less memory allocations, but also potentially
  16. * more wasted memory. If an allocation requests more bytes than BlockCapacity, first largest
  17. * multiple is used instead.
  18. */
  19. template <int BlockCapacity = 1024 * 1024>
  20. class MemStackInternal
  21. {
  22. private:
  23. /** A single block of memory of BlockCapacity size. A pointer to the first free address is stored, and a remaining
  24. * size.
  25. */
  26. class MemBlock
  27. {
  28. public:
  29. MemBlock(UINT32 size)
  30. :mData(nullptr), mFreePtr(0), mSize(size),
  31. mNextBlock(nullptr), mPrevBlock(nullptr)
  32. { }
  33. ~MemBlock()
  34. { }
  35. /**
  36. * Returns the first free address and increments the free pointer. Caller needs to ensure the remaining block
  37. * size is adequate before calling.
  38. */
  39. UINT8* alloc(UINT32 amount)
  40. {
  41. UINT8* freePtr = &mData[mFreePtr];
  42. mFreePtr += amount;
  43. return freePtr;
  44. }
  45. /**
  46. * Deallocates the provided pointer. Deallocation must happen in opposite order from allocation otherwise
  47. * corruption will occur.
  48. *
  49. * @note Pointer to @p data isn't actually needed, but is provided for debug purposes in order to more
  50. * easily track out-of-order deallocations.
  51. */
  52. void dealloc(UINT8* data, UINT32 amount)
  53. {
  54. mFreePtr -= amount;
  55. assert((&mData[mFreePtr]) == data && "Out of order stack deallocation detected. Deallocations need to happen in order opposite of allocations.");
  56. }
  57. UINT8* mData;
  58. UINT32 mFreePtr;
  59. UINT32 mSize;
  60. MemBlock* mNextBlock;
  61. MemBlock* mPrevBlock;
  62. };
  63. public:
  64. MemStackInternal()
  65. :mFreeBlock(nullptr)
  66. {
  67. mFreeBlock = allocBlock(BlockCapacity);
  68. }
  69. ~MemStackInternal()
  70. {
  71. assert(mFreeBlock->mFreePtr == 0 && "Not all blocks were released before shutting down the stack allocator.");
  72. MemBlock* curBlock = mFreeBlock;
  73. while (curBlock != nullptr)
  74. {
  75. MemBlock* nextBlock = curBlock->mNextBlock;
  76. deallocBlock(curBlock);
  77. curBlock = nextBlock;
  78. }
  79. }
  80. /**
  81. * Allocates the given amount of memory on the stack.
  82. *
  83. * @param[in] amount The amount to allocate in bytes.
  84. *
  85. * @note
  86. * Allocates the memory in the currently active block if it is large enough, otherwise a new block is allocated.
  87. * If the allocation is larger than default block size a separate block will be allocated only for that allocation,
  88. * making it essentially a slower heap allocator.
  89. * @note
  90. * Each allocation comes with a 4 byte overhead.
  91. */
  92. UINT8* alloc(UINT32 amount)
  93. {
  94. amount += sizeof(UINT32);
  95. UINT32 freeMem = mFreeBlock->mSize - mFreeBlock->mFreePtr;
  96. if(amount > freeMem)
  97. allocBlock(amount);
  98. UINT8* data = mFreeBlock->alloc(amount);
  99. UINT32* storedSize = reinterpret_cast<UINT32*>(data);
  100. *storedSize = amount;
  101. return data + sizeof(UINT32);
  102. }
  103. /** Deallocates the given memory. Data must be deallocated in opposite order then when it was allocated. */
  104. void dealloc(UINT8* data)
  105. {
  106. data -= sizeof(UINT32);
  107. UINT32* storedSize = reinterpret_cast<UINT32*>(data);
  108. mFreeBlock->dealloc(data, *storedSize);
  109. if (mFreeBlock->mFreePtr == 0)
  110. {
  111. MemBlock* emptyBlock = mFreeBlock;
  112. if (emptyBlock->mPrevBlock != nullptr)
  113. mFreeBlock = emptyBlock->mPrevBlock;
  114. // Merge with next block
  115. if (emptyBlock->mNextBlock != nullptr)
  116. {
  117. UINT32 totalSize = emptyBlock->mSize + emptyBlock->mNextBlock->mSize;
  118. if (emptyBlock->mPrevBlock != nullptr)
  119. emptyBlock->mPrevBlock->mNextBlock = nullptr;
  120. else
  121. mFreeBlock = nullptr;
  122. deallocBlock(emptyBlock->mNextBlock);
  123. deallocBlock(emptyBlock);
  124. allocBlock(totalSize);
  125. }
  126. }
  127. }
  128. private:
  129. MemBlock* mFreeBlock;
  130. /**
  131. * Allocates a new block of memory using a heap allocator. Block will never be smaller than BlockCapacity no matter
  132. * the @p wantedSize.
  133. */
  134. MemBlock* allocBlock(UINT32 wantedSize)
  135. {
  136. UINT32 blockSize = BlockCapacity;
  137. if(wantedSize > blockSize)
  138. blockSize = wantedSize;
  139. MemBlock* newBlock = nullptr;
  140. MemBlock* curBlock = mFreeBlock;
  141. while (curBlock != nullptr)
  142. {
  143. MemBlock* nextBlock = curBlock->mNextBlock;
  144. if (nextBlock != nullptr && nextBlock->mSize >= blockSize)
  145. {
  146. newBlock = nextBlock;
  147. break;
  148. }
  149. curBlock = nextBlock;
  150. }
  151. if (newBlock == nullptr)
  152. {
  153. UINT8* data = (UINT8*)reinterpret_cast<UINT8*>(bs_alloc(blockSize + sizeof(MemBlock)));
  154. newBlock = new (data)MemBlock(blockSize);
  155. data += sizeof(MemBlock);
  156. newBlock->mData = data;
  157. newBlock->mPrevBlock = mFreeBlock;
  158. if (mFreeBlock != nullptr)
  159. {
  160. if(mFreeBlock->mNextBlock != nullptr)
  161. mFreeBlock->mNextBlock->mPrevBlock = newBlock;
  162. newBlock->mNextBlock = mFreeBlock->mNextBlock;
  163. mFreeBlock->mNextBlock = newBlock;
  164. }
  165. }
  166. mFreeBlock = newBlock;
  167. return newBlock;
  168. }
  169. /** Deallocates a block of memory. */
  170. void deallocBlock(MemBlock* block)
  171. {
  172. block->~MemBlock();
  173. bs_free(block);
  174. }
  175. };
  176. /**
  177. * One of the fastest, but also very limiting type of allocator. All deallocations must happen in opposite order from
  178. * allocations.
  179. *
  180. * @note
  181. * It's mostly useful when you need to allocate something temporarily on the heap, usually something that gets
  182. * allocated and freed within the same method.
  183. * @note
  184. * Each allocation comes with a pretty hefty 4 byte memory overhead, so don't use it for small allocations.
  185. * @note
  186. * Thread safe. But you cannot allocate on one thread and deallocate on another. Threads will keep
  187. * separate stacks internally. Make sure to call beginThread()/endThread() for any thread this stack is used on.
  188. */
  189. class MemStack
  190. {
  191. public:
  192. /**
  193. * Sets up the stack with the currently active thread. You need to call this on any thread before doing any
  194. * allocations or deallocations.
  195. */
  196. static BS_UTILITY_EXPORT void beginThread();
  197. /**
  198. * Cleans up the stack for the current thread. You may not perform any allocations or deallocations after this is
  199. * called, unless you call beginThread again.
  200. */
  201. static BS_UTILITY_EXPORT void endThread();
  202. /** @copydoc MemoryStackInternal::alloc() */
  203. static BS_UTILITY_EXPORT UINT8* alloc(UINT32 numBytes);
  204. /** @copydoc MemoryStackInternal::dealloc() */
  205. static BS_UTILITY_EXPORT void deallocLast(UINT8* data);
  206. private:
  207. static BS_THREADLOCAL MemStackInternal<1024 * 1024>* ThreadMemStack;
  208. };
  209. /** @endcond */
  210. /** @copydoc MemoryStackInternal::alloc() */
  211. BS_UTILITY_EXPORT inline void* bs_stack_alloc(UINT32 numBytes);
  212. /**
  213. * Allocates enough memory to hold the specified type, on the stack, but does not initialize the object.
  214. *
  215. * @see MemoryStackInternal::alloc()
  216. */
  217. template<class T>
  218. T* bs_stack_alloc()
  219. {
  220. return (T*)MemStack::alloc(sizeof(T));
  221. }
  222. /**
  223. * Allocates enough memory to hold N objects of the specified type, on the stack, but does not initialize the objects.
  224. *
  225. * @see MemoryStackInternal::alloc()
  226. */
  227. template<class T>
  228. T* bs_stack_alloc(UINT32 count)
  229. {
  230. return (T*)MemStack::alloc(sizeof(T) * count);
  231. }
  232. /**
  233. * Allocates enough memory to hold the specified type, on the stack, and constructs the object.
  234. *
  235. * @see MemoryStackInternal::alloc()
  236. */
  237. template<class T>
  238. T* bs_stack_new(UINT32 count = 0)
  239. {
  240. T* data = bs_stack_alloc<T>(count);
  241. for(unsigned int i = 0; i < count; i++)
  242. new ((void*)&data[i]) T;
  243. return data;
  244. }
  245. /**
  246. * Allocates enough memory to hold the specified type, on the stack, and constructs the object.
  247. *
  248. * @see MemoryStackInternal::alloc()
  249. */
  250. template<class T, class... Args>
  251. T* bs_stack_new(Args &&...args, UINT32 count = 0)
  252. {
  253. T* data = bs_stack_alloc<T>(count);
  254. for(unsigned int i = 0; i < count; i++)
  255. new ((void*)&data[i]) T(std::forward<Args>(args)...);
  256. return data;
  257. }
  258. /**
  259. * Destructs and deallocates last allocated entry currently located on stack.
  260. *
  261. * @see MemoryStackInternal::dealloc()
  262. */
  263. template<class T>
  264. void bs_stack_delete(T* data)
  265. {
  266. data->~T();
  267. MemStack::deallocLast((UINT8*)data);
  268. }
  269. /**
  270. * Destructs an array of objects and deallocates last allocated entry currently located on stack.
  271. *
  272. * @see MemoryStackInternal::dealloc()
  273. */
  274. template<class T>
  275. void bs_stack_delete(T* data, UINT32 count)
  276. {
  277. for(unsigned int i = 0; i < count; i++)
  278. data[i].~T();
  279. MemStack::deallocLast((UINT8*)data);
  280. }
  281. /** @copydoc MemoryStackInternal::dealloc() */
  282. BS_UTILITY_EXPORT inline void bs_stack_free(void* data);
  283. /** @cond INTERNAL */
  284. /**
  285. * Allows use of a stack allocator by using normal new/delete/free/dealloc operators.
  286. *
  287. * @see MemStack
  288. */
  289. class StackAlloc
  290. { };
  291. /**
  292. * Specialized memory allocator implementations that allows use of a stack allocator in normal new/delete/free/dealloc
  293. * operators.
  294. *
  295. * @see MemStack
  296. */
  297. template<>
  298. class MemoryAllocator<StackAlloc> : public MemoryAllocatorBase
  299. {
  300. public:
  301. static void* allocate(size_t bytes)
  302. {
  303. return bs_stack_alloc((UINT32)bytes);
  304. }
  305. static void* allocateArray(size_t bytes, UINT32 count)
  306. {
  307. return bs_stack_alloc((UINT32)(bytes * count));
  308. }
  309. static void free(void* ptr)
  310. {
  311. bs_stack_free(ptr);
  312. }
  313. static void freeArray(void* ptr, UINT32 count)
  314. {
  315. bs_stack_free(ptr);
  316. }
  317. };
  318. /** @endcond */
  319. /** @} */
  320. }