BsMemStack.h 8.5 KB

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