BsMemStack.h 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  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->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. mFreeBlock->mNextBlock = newBlock;
  160. }
  161. mFreeBlock = newBlock;
  162. return newBlock;
  163. }
  164. /** Deallocates a block of memory. */
  165. void deallocBlock(MemBlock* block)
  166. {
  167. block->~MemBlock();
  168. bs_free(block);
  169. }
  170. };
  171. /**
  172. * One of the fastest, but also very limiting type of allocator. All deallocations must happen in opposite order from
  173. * allocations.
  174. *
  175. * @note
  176. * It's mostly useful when you need to allocate something temporarily on the heap, usually something that gets
  177. * allocated and freed within the same method.
  178. * @note
  179. * Each allocation comes with a pretty hefty 4 byte memory overhead, so don't use it for small allocations.
  180. * @note
  181. * Thread safe. But you cannot allocate on one thread and deallocate on another. Threads will keep
  182. * separate stacks internally. Make sure to call beginThread()/endThread() for any thread this stack is used on.
  183. */
  184. class MemStack
  185. {
  186. public:
  187. /**
  188. * Sets up the stack with the currently active thread. You need to call this on any thread before doing any
  189. * allocations or deallocations.
  190. */
  191. static BS_UTILITY_EXPORT void beginThread();
  192. /**
  193. * Cleans up the stack for the current thread. You may not perform any allocations or deallocations after this is
  194. * called, unless you call beginThread again.
  195. */
  196. static BS_UTILITY_EXPORT void endThread();
  197. /** @copydoc MemoryStackInternal::alloc() */
  198. static BS_UTILITY_EXPORT UINT8* alloc(UINT32 numBytes);
  199. /** @copydoc MemoryStackInternal::dealloc() */
  200. static BS_UTILITY_EXPORT void deallocLast(UINT8* data);
  201. private:
  202. static BS_THREADLOCAL MemStackInternal<1024 * 1024>* ThreadMemStack;
  203. };
  204. /** @endcond */
  205. /** @copydoc MemoryStackInternal::alloc() */
  206. BS_UTILITY_EXPORT inline void* bs_stack_alloc(UINT32 numBytes);
  207. /**
  208. * Allocates enough memory to hold the specified type, on the stack, but does not initialize the object.
  209. *
  210. * @see MemoryStackInternal::alloc()
  211. */
  212. template<class T>
  213. T* bs_stack_alloc()
  214. {
  215. return (T*)MemStack::alloc(sizeof(T));
  216. }
  217. /**
  218. * Allocates enough memory to hold N objects of the specified type, on the stack, but does not initialize the objects.
  219. *
  220. * @see MemoryStackInternal::alloc()
  221. */
  222. template<class T>
  223. T* bs_stack_alloc(UINT32 count)
  224. {
  225. return (T*)MemStack::alloc(sizeof(T) * count);
  226. }
  227. /**
  228. * Allocates enough memory to hold the specified type, on the stack, and constructs the object.
  229. *
  230. * @see MemoryStackInternal::alloc()
  231. */
  232. template<class T>
  233. T* bs_stack_new(UINT32 count = 0)
  234. {
  235. T* data = bs_stack_alloc<T>(count);
  236. for(unsigned int i = 0; i < count; i++)
  237. new ((void*)&data[i]) T;
  238. return data;
  239. }
  240. /**
  241. * Allocates enough memory to hold the specified type, on the stack, and constructs the object.
  242. *
  243. * @see MemoryStackInternal::alloc()
  244. */
  245. template<class T, class... Args>
  246. T* bs_stack_new(Args &&...args, UINT32 count = 0)
  247. {
  248. T* data = bs_stack_alloc<T>(count);
  249. for(unsigned int i = 0; i < count; i++)
  250. new ((void*)&data[i]) T(std::forward<Args>(args)...);
  251. return data;
  252. }
  253. /**
  254. * Destructs and deallocates last allocated entry currently located on stack.
  255. *
  256. * @see MemoryStackInternal::dealloc()
  257. */
  258. template<class T>
  259. void bs_stack_delete(T* data)
  260. {
  261. data->~T();
  262. MemStack::deallocLast((UINT8*)data);
  263. }
  264. /**
  265. * Destructs an array of objects 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, UINT32 count)
  271. {
  272. for(unsigned int i = 0; i < count; i++)
  273. data[i].~T();
  274. MemStack::deallocLast((UINT8*)data);
  275. }
  276. /** @copydoc MemoryStackInternal::dealloc() */
  277. BS_UTILITY_EXPORT inline void bs_stack_free(void* data);
  278. /** @cond INTERNAL */
  279. /**
  280. * Allows use of a stack allocator by using normal new/delete/free/dealloc operators.
  281. *
  282. * @see MemStack
  283. */
  284. class StackAlloc
  285. { };
  286. /**
  287. * Specialized memory allocator implementations that allows use of a stack allocator in normal new/delete/free/dealloc
  288. * operators.
  289. *
  290. * @see MemStack
  291. */
  292. template<>
  293. class MemoryAllocator<StackAlloc> : public MemoryAllocatorBase
  294. {
  295. public:
  296. static void* allocate(size_t bytes)
  297. {
  298. return bs_stack_alloc((UINT32)bytes);
  299. }
  300. static void* allocateArray(size_t bytes, UINT32 count)
  301. {
  302. return bs_stack_alloc((UINT32)(bytes * count));
  303. }
  304. static void free(void* ptr)
  305. {
  306. bs_stack_free(ptr);
  307. }
  308. static void freeArray(void* ptr, UINT32 count)
  309. {
  310. bs_stack_free(ptr);
  311. }
  312. };
  313. /** @endcond */
  314. /** @} */
  315. }