2
0

BsMemStack.h 10 KB

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