BsMemStack.h 9.4 KB

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