dataChunker.h 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2023 tgemit contributors.
  3. // See AUTHORS file and git repository for contributor information.
  4. //
  5. // SPDX-License-Identifier: MIT
  6. //-----------------------------------------------------------------------------
  7. #ifndef _DATACHUNKER_H_
  8. #define _DATACHUNKER_H_
  9. #ifndef _PLATFORM_H_
  10. # include "platform/platform.h"
  11. #endif
  12. #ifndef _PLATFORMASSERT_H_
  13. # include "platform/platformAssert.h"
  14. #endif
  15. #ifndef _FRAMEALLOCATOR_H_
  16. #include "core/frameAllocator.h"
  17. #endif
  18. #include <algorithm>
  19. #include <stdint.h>
  20. /// Implements a chunked data allocator.
  21. ///
  22. /// This memory allocator allocates data in chunks of bytes,
  23. /// the default size being ChunkSize.
  24. /// Bytes are sourced from the current head chunk until expended,
  25. /// in which case a new chunk of bytes will be allocated from
  26. /// the system memory allocator.
  27. ///
  28. template<class T> class BaseDataChunker
  29. {
  30. public:
  31. enum
  32. {
  33. ChunkSize = 16384
  34. };
  35. typedef T AlignmentType;
  36. struct alignas(uintptr_t) DataBlock : public AlignedBufferAllocator<T>
  37. {
  38. DataBlock* mNext = NULL;
  39. inline DataBlock* getEnd()
  40. {
  41. return this + 1;
  42. }
  43. };
  44. protected:
  45. dsize_t mChunkSize;
  46. DataBlock* mChunkHead;
  47. public:
  48. BaseDataChunker(U32 chunkSize = BaseDataChunker<T>::ChunkSize) : mChunkSize(chunkSize), mChunkHead(NULL)
  49. {
  50. }
  51. virtual ~BaseDataChunker()
  52. {
  53. freeBlocks(false);
  54. }
  55. DataBlock* allocChunk(dsize_t chunkSize)
  56. {
  57. DataBlock* newChunk = (DataBlock*)dMalloc(sizeof(DataBlock) + chunkSize);
  58. constructInPlace(newChunk);
  59. newChunk->initWithBytes((T*)newChunk->getEnd(), chunkSize);
  60. newChunk->mNext = mChunkHead;
  61. mChunkHead = newChunk;
  62. return newChunk;
  63. }
  64. void* alloc(dsize_t numBytes)
  65. {
  66. void* theAlloc = mChunkHead ? mChunkHead->allocBytes(numBytes) : NULL;
  67. if (theAlloc == NULL)
  68. {
  69. dsize_t actualSize = std::max<dsize_t>(mChunkSize, numBytes);
  70. allocChunk(actualSize);
  71. theAlloc = mChunkHead->allocBytes(numBytes);
  72. AssertFatal(theAlloc != NULL, "Something really odd going on here");
  73. }
  74. return theAlloc;
  75. }
  76. void freeBlocks(bool keepOne = false)
  77. {
  78. DataBlock* itr = mChunkHead;
  79. while (itr)
  80. {
  81. DataBlock* nextItr = itr->mNext;
  82. if (nextItr == NULL && keepOne)
  83. {
  84. itr->setPosition(0);
  85. break;
  86. }
  87. dFree(itr);
  88. itr = nextItr;
  89. }
  90. mChunkHead = itr;
  91. }
  92. U32 countUsedBlocks()
  93. {
  94. U32 count = 0;
  95. for (DataBlock* itr = mChunkHead; itr; itr = itr->mNext)
  96. {
  97. count++;
  98. }
  99. return count;
  100. }
  101. dsize_t countUsedBytes()
  102. {
  103. dsize_t count = 0;
  104. for (DataBlock* itr = mChunkHead; itr; itr = itr->mNext)
  105. {
  106. count += itr->getPositionBytes();
  107. }
  108. return count;
  109. }
  110. void setChunkSize(dsize_t size)
  111. {
  112. AssertFatal(mChunkHead == NULL, "Tried setting AFTER init");
  113. mChunkSize = size;
  114. }
  115. bool isManagedByChunker(void* ptr) const
  116. {
  117. U8* chkPtr = (U8*)ptr;
  118. for (DataBlock* itr = mChunkHead; itr; itr = itr->mNext)
  119. {
  120. const U8* blockStart = (U8*)itr->getAlignedBuffer();
  121. const U8* blockEnd = (U8*)itr->getAlignedBufferEnd();
  122. if (chkPtr >= blockStart && chkPtr < blockEnd)
  123. return true;
  124. }
  125. return false;
  126. }
  127. };
  128. class DataChunker : public BaseDataChunker<uintptr_t>
  129. {
  130. public:
  131. DataChunker() : BaseDataChunker<uintptr_t>(BaseDataChunker<uintptr_t>::ChunkSize) { ; }
  132. explicit DataChunker(dsize_t size) : BaseDataChunker<uintptr_t>(size) { ; }
  133. };
  134. /// Implements a derivative of BaseDataChunker designed for
  135. /// allocating structs of type T without initialization.
  136. template<class T> class Chunker : private BaseDataChunker<T>
  137. {
  138. public:
  139. Chunker(dsize_t size = BaseDataChunker<T>::ChunkSize) : BaseDataChunker<T>(std::max(sizeof(T), size))
  140. {
  141. }
  142. T* alloc()
  143. {
  144. return (T*)BaseDataChunker<T>::alloc(sizeof(T));
  145. }
  146. void clear()
  147. {
  148. BaseDataChunker<T>::freeBlocks();
  149. }
  150. };
  151. /// Implements a derivative of BaseDataChunker designed for
  152. /// allocating structs of various types Y without initialization.
  153. /// @note: this is horribly suboptimal for types not multiples of uintptr_t in size.
  154. class MultiTypedChunker : private BaseDataChunker<uintptr_t>
  155. {
  156. public:
  157. typedef uintptr_t AlignmentType;
  158. MultiTypedChunker(dsize_t size = BaseDataChunker<uintptr_t>::ChunkSize) : BaseDataChunker<uintptr_t>(std::max<uintptr_t>(sizeof(uintptr_t), size))
  159. {
  160. }
  161. template<typename Y> Y* alloc()
  162. {
  163. return (Y*)BaseDataChunker<uintptr_t>::alloc(sizeof(Y));
  164. }
  165. void clear()
  166. {
  167. BaseDataChunker<uintptr_t>::freeBlocks(true);
  168. }
  169. };
  170. /// Implements a simple linked list for ClassChunker and FreeListChunker.
  171. template<class T> struct ChunkerFreeClassList
  172. {
  173. ChunkerFreeClassList<T>* mNextList;
  174. ChunkerFreeClassList() : mNextList(NULL)
  175. {
  176. }
  177. void reset()
  178. {
  179. mNextList = NULL;
  180. }
  181. bool isEmpty() const
  182. {
  183. return mNextList == NULL;
  184. }
  185. T* pop()
  186. {
  187. ChunkerFreeClassList<T>* oldNext = mNextList;
  188. mNextList = mNextList ? mNextList->mNextList : NULL;
  189. return (T*)oldNext;
  190. }
  191. void push(ChunkerFreeClassList<T>* other)
  192. {
  193. other->mNextList = mNextList;
  194. mNextList = other;
  195. }
  196. };
  197. /// Implements a derivative of BaseDataChunker designed for
  198. /// allocating structs or classes of type T with initialization.
  199. template<class T> class ClassChunker : private BaseDataChunker<T>
  200. {
  201. protected:
  202. ChunkerFreeClassList<T> mFreeListHead;
  203. public:
  204. ClassChunker(dsize_t size = BaseDataChunker<T>::ChunkSize)
  205. {
  206. }
  207. T* alloc()
  208. {
  209. if (mFreeListHead.isEmpty())
  210. {
  211. return constructInPlace((T*)BaseDataChunker<T>::alloc(sizeof(T)));
  212. }
  213. else
  214. {
  215. return constructInPlace(mFreeListHead.pop());
  216. }
  217. }
  218. void free(T* item)
  219. {
  220. destructInPlace(item);
  221. mFreeListHead.push(reinterpret_cast<ChunkerFreeClassList<T>*>(item));
  222. }
  223. void freeBlocks(bool keepOne = false)
  224. {
  225. BaseDataChunker<T>::freeBlocks(keepOne);
  226. mFreeListHead.reset();
  227. }
  228. inline bool isManagedByChunker(void* ptr) const
  229. {
  230. return BaseDataChunker<T>::isManagedByChunker(ptr);
  231. }
  232. inline ChunkerFreeClassList<T>& getFreeListHead() { return mFreeListHead; }
  233. };
  234. /// Implements a chunker which uses the data of another BaseDataChunker
  235. /// as underlying storage.
  236. template<class T> class FreeListChunker
  237. {
  238. protected:
  239. BaseDataChunker<T>* mChunker;
  240. bool mOwnsChunker;
  241. ChunkerFreeClassList<T> mFreeListHead;
  242. public:
  243. FreeListChunker(BaseDataChunker<T>* otherChunker) :
  244. mChunker(otherChunker),
  245. mOwnsChunker(false)
  246. {
  247. }
  248. FreeListChunker(dsize_t size = BaseDataChunker<T>::ChunkSize)
  249. {
  250. mChunker = new BaseDataChunker<T>(size);
  251. mOwnsChunker = true;
  252. }
  253. BaseDataChunker<T>* getChunker()
  254. {
  255. return mChunker;
  256. }
  257. T* alloc()
  258. {
  259. if (mFreeListHead.isEmpty())
  260. {
  261. return constructInPlace((T*)mChunker->alloc(sizeof(T)));
  262. }
  263. else
  264. {
  265. return constructInPlace(mFreeListHead.pop());
  266. }
  267. }
  268. void free(T* item)
  269. {
  270. destructInPlace(item);
  271. mFreeListHead.push(reinterpret_cast<ChunkerFreeClassList<T>*>(item));
  272. }
  273. void freeBlocks(bool keepOne = false)
  274. {
  275. mChunker->freeBlocks(keepOne);
  276. }
  277. };
  278. template<const U32 byteSize> struct DWordDataBlob
  279. {
  280. U32 data[(byteSize + 3)/ 4];
  281. };
  282. /// Implements a three-tiered chunker
  283. /// K1..3 should be ordered from low to high
  284. template<class K1, class K2, class K3> class ThreeTieredChunker
  285. {
  286. public:
  287. struct Handle
  288. {
  289. U32 tier;
  290. void* ptr;
  291. Handle() : tier(0), ptr(NULL) { ; }
  292. Handle(const Handle& other) : tier(other.tier), ptr(other.ptr) { ; }
  293. Handle(U32 in_tier, void* in_ptr) : tier(in_tier), ptr(in_ptr) { ; }
  294. Handle& operator=(const Handle& other) {
  295. tier = other.tier;
  296. ptr = other.ptr;
  297. return *this;
  298. }
  299. };
  300. protected:
  301. ClassChunker<K1> mT1;
  302. ClassChunker<K2> mT2;
  303. ClassChunker<K3> mT3;
  304. public:
  305. Handle alloc(U32 byteSize)
  306. {
  307. Handle outH;
  308. if (byteSize > sizeof(K3))
  309. {
  310. const U32 wordSize = (byteSize + 3) / 4;
  311. outH = Handle(0, (void*)(new U32[wordSize]));
  312. }
  313. else
  314. {
  315. if (byteSize <= sizeof(K1))
  316. {
  317. outH = Handle(1, (void*)mT1.alloc());
  318. }
  319. else if (byteSize <= sizeof(K2))
  320. {
  321. outH = Handle(2, (void*)mT2.alloc());
  322. }
  323. else if (byteSize <= sizeof(K3))
  324. {
  325. outH = Handle(3, (void*)mT3.alloc());
  326. }
  327. else
  328. {
  329. outH = Handle(0, NULL);
  330. }
  331. }
  332. return outH;
  333. }
  334. void free(Handle& item)
  335. {
  336. if (item.ptr == NULL)
  337. return;
  338. switch (item.tier)
  339. {
  340. case 0:
  341. delete[] ((U32*)item.ptr);
  342. break;
  343. case 1:
  344. mT1.free((K1*)item.ptr);
  345. break;
  346. case 2:
  347. mT2.free((K2*)item.ptr);
  348. break;
  349. case 3:
  350. mT3.free((K3*)item.ptr);
  351. break;
  352. default:
  353. break;
  354. }
  355. item.ptr = NULL;
  356. }
  357. void freeBlocks(bool keepOne = false)
  358. {
  359. mT1.freeBlocks(keepOne);
  360. mT2.freeBlocks(keepOne);
  361. mT3.freeBlocks(keepOne);
  362. }
  363. inline ClassChunker<K1>& getT1Chunker() { return mT1; }
  364. inline ClassChunker<K2>& getT2Chunker() { return mT2; }
  365. inline ClassChunker<K3>& getT3Chunker() { return mT3; }
  366. };
  367. #endif