2
0

dataChunker.h 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #ifndef _DATACHUNKER_H_
  23. #define _DATACHUNKER_H_
  24. #ifndef _PLATFORM_H_
  25. # include "platform/platform.h"
  26. #endif
  27. //----------------------------------------------------------------------------
  28. /// Implements a chunked data allocator.
  29. ///
  30. /// Calling new/malloc all the time is a time consuming operation. Therefore,
  31. /// we provide the DataChunker, which allocates memory in blocks of
  32. /// chunkSize (by default 16k, see ChunkSize, though it can be set in
  33. /// the constructor), then doles it out as requested, in chunks of up to
  34. /// chunkSize in size.
  35. ///
  36. /// It will assert if you try to get more than ChunkSize bytes at a time,
  37. /// and it deals with the logic of allocating new blocks and giving out
  38. /// word-aligned chunks.
  39. ///
  40. /// Note that new/free/realloc WILL NOT WORK on memory gotten from the
  41. /// DataChunker. This also only grows (you can call freeBlocks to deallocate
  42. /// and reset things).
  43. class DataChunker
  44. {
  45. public:
  46. /// Block of allocated memory.
  47. ///
  48. /// <b>This has nothing to do with datablocks as used in the rest of Torque.</b>
  49. struct DataBlock
  50. {
  51. DataBlock* next; ///< linked list pointer to the next DataBlock for this chunker
  52. S32 curIndex; ///< current allocation point within this DataBlock
  53. DataBlock();
  54. ~DataBlock();
  55. inline U8 *getData();
  56. };
  57. enum {
  58. PaddDBSize = (sizeof(DataBlock) + 3) & ~3, ///< Padded size of DataBlock
  59. ChunkSize = 16384 - PaddDBSize ///< Default size of each DataBlock page in the DataChunker
  60. };
  61. /// Return a pointer to a chunk of memory from a pre-allocated block.
  62. ///
  63. /// This memory goes away when you call freeBlocks.
  64. ///
  65. /// This memory is word-aligned.
  66. /// @param size Size of chunk to return. This must be less than chunkSize or else
  67. /// an assertion will occur.
  68. void *alloc(S32 size);
  69. /// Free all allocated memory blocks.
  70. ///
  71. /// This invalidates all pointers returned from alloc().
  72. void freeBlocks(bool keepOne = false);
  73. /// Initialize using blocks of a given size.
  74. ///
  75. /// One new block is allocated at constructor-time.
  76. ///
  77. /// @param size Size in bytes of the space to allocate for each block.
  78. DataChunker(S32 size=ChunkSize);
  79. ~DataChunker();
  80. /// Swaps the memory allocated in one data chunker for another. This can be used to implement
  81. /// packing of memory stored in a DataChunker.
  82. void swap(DataChunker &d)
  83. {
  84. DataBlock *temp = d.mCurBlock;
  85. d.mCurBlock = mCurBlock;
  86. mCurBlock = temp;
  87. }
  88. public:
  89. U32 countUsedBlocks()
  90. {
  91. U32 count = 0;
  92. if (!mCurBlock)
  93. return 0;
  94. for (DataBlock *ptr = mCurBlock; ptr != NULL; ptr = ptr->next)
  95. {
  96. count++;
  97. }
  98. return count;
  99. }
  100. void setChunkSize(U32 size)
  101. {
  102. AssertFatal(mCurBlock == NULL, "Cant resize now");
  103. mChunkSize = size;
  104. }
  105. public:
  106. DataBlock* mCurBlock; ///< current page we're allocating data from. If the
  107. ///< data size request is greater than the memory space currently
  108. ///< available in the current page, a new page will be allocated.
  109. S32 mChunkSize; ///< The size allocated for each page in the DataChunker
  110. };
  111. inline U8 *DataChunker::DataBlock::getData()
  112. {
  113. return (U8*)this + DataChunker::PaddDBSize;
  114. }
  115. //----------------------------------------------------------------------------
  116. template<class T>
  117. class Chunker: private DataChunker
  118. {
  119. public:
  120. Chunker(S32 size = DataChunker::ChunkSize) : DataChunker(size) {};
  121. T* alloc() { return reinterpret_cast<T*>(DataChunker::alloc(S32(sizeof(T)))); }
  122. void clear() { freeBlocks(); }
  123. };
  124. //----------------------------------------------------------------------------
  125. /// This class is similar to the Chunker<> class above. But it allows for multiple
  126. /// types of structs to be stored.
  127. /// CodeReview: This could potentially go into DataChunker directly, but I wasn't sure if
  128. /// CodeReview: That would be polluting it. BTR
  129. class MultiTypedChunker : private DataChunker
  130. {
  131. public:
  132. MultiTypedChunker(S32 size = DataChunker::ChunkSize) : DataChunker(size) {};
  133. /// Use like so: MyType* t = chunker.alloc<MyType>();
  134. template<typename T>
  135. T* alloc() { return reinterpret_cast<T*>(DataChunker::alloc(S32(sizeof(T)))); }
  136. void clear() { freeBlocks(true); }
  137. };
  138. //----------------------------------------------------------------------------
  139. /// Templatized data chunker class with proper construction and destruction of its elements.
  140. ///
  141. /// DataChunker just allocates space. This subclass actually constructs/destructs the
  142. /// elements. This class is appropriate for more complex classes.
  143. template<class T>
  144. class ClassChunker: private DataChunker
  145. {
  146. public:
  147. ClassChunker(S32 size = DataChunker::ChunkSize) : DataChunker(size)
  148. {
  149. mElementSize = getMax(U32(sizeof(T)), U32(sizeof(T *)));
  150. mFreeListHead = NULL;
  151. }
  152. /// Allocates and properly constructs in place a new element.
  153. T *alloc()
  154. {
  155. if(mFreeListHead == NULL)
  156. return constructInPlace(reinterpret_cast<T*>(DataChunker::alloc(mElementSize)));
  157. T* ret = mFreeListHead;
  158. mFreeListHead = *(reinterpret_cast<T**>(mFreeListHead));
  159. return constructInPlace(ret);
  160. }
  161. /// Properly destructs and frees an element allocated with the alloc method.
  162. void free(T* elem)
  163. {
  164. destructInPlace(elem);
  165. *(reinterpret_cast<T**>(elem)) = mFreeListHead;
  166. mFreeListHead = elem;
  167. }
  168. void freeBlocks( bool keepOne = false )
  169. {
  170. DataChunker::freeBlocks( keepOne );
  171. mFreeListHead = NULL;
  172. }
  173. private:
  174. S32 mElementSize; ///< the size of each element, or the size of a pointer, whichever is greater
  175. T *mFreeListHead; ///< a pointer to a linked list of freed elements for reuse
  176. };
  177. //----------------------------------------------------------------------------
  178. template<class T>
  179. class FreeListChunker
  180. {
  181. public:
  182. FreeListChunker(DataChunker *inChunker)
  183. : mChunker( inChunker ),
  184. mOwnChunker( false ),
  185. mFreeListHead( NULL )
  186. {
  187. mElementSize = getMax(U32(sizeof(T)), U32(sizeof(T *)));
  188. }
  189. FreeListChunker(S32 size = DataChunker::ChunkSize)
  190. : mFreeListHead( NULL )
  191. {
  192. mChunker = new DataChunker( size );
  193. mOwnChunker = true;
  194. mElementSize = getMax(U32(sizeof(T)), U32(sizeof(T *)));
  195. }
  196. ~FreeListChunker()
  197. {
  198. if ( mOwnChunker )
  199. delete mChunker;
  200. }
  201. T *alloc()
  202. {
  203. if(mFreeListHead == NULL)
  204. return reinterpret_cast<T*>(mChunker->alloc(mElementSize));
  205. T* ret = mFreeListHead;
  206. mFreeListHead = *(reinterpret_cast<T**>(mFreeListHead));
  207. return ret;
  208. }
  209. void free(T* elem)
  210. {
  211. *(reinterpret_cast<T**>(elem)) = mFreeListHead;
  212. mFreeListHead = elem;
  213. }
  214. /// Allow people to free all their memory if they want.
  215. void freeBlocks( bool keepOne = false )
  216. {
  217. mChunker->freeBlocks( keepOne );
  218. mFreeListHead = NULL;
  219. }
  220. private:
  221. DataChunker *mChunker;
  222. bool mOwnChunker;
  223. S32 mElementSize;
  224. T *mFreeListHead;
  225. };
  226. class FreeListChunkerUntyped
  227. {
  228. public:
  229. FreeListChunkerUntyped(U32 inElementSize, DataChunker *inChunker)
  230. : mChunker( inChunker ),
  231. mOwnChunker( false ),
  232. mElementSize( inElementSize ),
  233. mFreeListHead( NULL )
  234. {
  235. }
  236. FreeListChunkerUntyped(U32 inElementSize, S32 size = DataChunker::ChunkSize)
  237. : mElementSize( inElementSize ),
  238. mFreeListHead( NULL )
  239. {
  240. mChunker = new DataChunker( size );
  241. mOwnChunker = true;
  242. }
  243. ~FreeListChunkerUntyped()
  244. {
  245. if ( mOwnChunker )
  246. delete mChunker;
  247. }
  248. void *alloc()
  249. {
  250. if(mFreeListHead == NULL)
  251. return mChunker->alloc(mElementSize);
  252. void *ret = mFreeListHead;
  253. mFreeListHead = *(reinterpret_cast<void**>(mFreeListHead));
  254. return ret;
  255. }
  256. void free(void* elem)
  257. {
  258. *(reinterpret_cast<void**>(elem)) = mFreeListHead;
  259. mFreeListHead = elem;
  260. }
  261. // Allow people to free all their memory if they want.
  262. void freeBlocks()
  263. {
  264. mChunker->freeBlocks();
  265. // We have to terminate the freelist as well or else we'll run
  266. // into crazy unused memory.
  267. mFreeListHead = NULL;
  268. }
  269. U32 getElementSize() const { return mElementSize; }
  270. private:
  271. DataChunker *mChunker;
  272. bool mOwnChunker;
  273. const U32 mElementSize;
  274. void *mFreeListHead;
  275. };
  276. #endif