Heap.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. #include "Heap.h"
  2. #include "DLIList.h"
  3. #include "HashSet.h"
  4. USING_NS_BF;
  5. //////////////////////////////////////////////////////////////////////////
  6. #define CH_REL_TO_ABS(VAL) ((ChBlock*)((uint8*)mMetadata + (VAL)))
  7. #define CH_REL_TO_ABS(VAL) ((ChBlock*)((uint8*)mMetadata + (VAL)))
  8. #define CH_ABS_TO_REL(VAL) (int)((uint8*)(VAL) - (uint8*)mMetadata)
  9. enum ChBlockKind
  10. {
  11. ChBlockKind_Bad = 0xBEEF0BAD,
  12. ChBlockKind_Unused = 0xBEEF1212,
  13. ChBlockKind_Used = 0xBEEF2323,
  14. ChBlockKind_Merged = 0xBEEF3434,
  15. };
  16. struct ChBlock
  17. {
  18. ContiguousHeap::AllocRef mPrev;
  19. ContiguousHeap::AllocRef mNext;
  20. int mSize;
  21. ChBlockKind mKind;
  22. ChBlock()
  23. {
  24. mPrev = -1;
  25. mNext = -1;
  26. mSize = 0;
  27. mKind = ChBlockKind_Bad;
  28. }
  29. };
  30. class ChList
  31. {
  32. public:
  33. void* mMetadata;
  34. int32 mHead;
  35. int32 mTail;
  36. public:
  37. ChList()
  38. {
  39. mHead = -1;
  40. mTail = -1;
  41. }
  42. void Size()
  43. {
  44. int size = 0;
  45. int checkNode = mHead;
  46. while (checkNode != -1)
  47. {
  48. size++;
  49. checkNode = CH_REL_TO_ABS(checkNode)->mNext;
  50. }
  51. }
  52. void PushBack(int node)
  53. {
  54. BF_ASSERT(CH_REL_TO_ABS(node)->mNext == -1);
  55. if (mHead == -1)
  56. mHead = node;
  57. else
  58. {
  59. CH_REL_TO_ABS(mTail)->mNext = node;
  60. CH_REL_TO_ABS(node)->mPrev = mTail;
  61. }
  62. mTail = node;
  63. }
  64. void AddAfter(int refNode, int newNode)
  65. {
  66. int32 prevNext = CH_REL_TO_ABS(refNode)->mNext;
  67. CH_REL_TO_ABS(refNode)->mNext = newNode;
  68. CH_REL_TO_ABS(newNode)->mPrev = refNode;
  69. CH_REL_TO_ABS(newNode)->mNext = prevNext;
  70. if (prevNext != -1)
  71. CH_REL_TO_ABS(prevNext)->mPrev = newNode;
  72. if (refNode == mTail)
  73. mTail = newNode;
  74. }
  75. void Remove(int node)
  76. {
  77. if (CH_REL_TO_ABS(node)->mPrev == -1)
  78. {
  79. mHead = CH_REL_TO_ABS(node)->mNext;
  80. if (mHead != -1)
  81. CH_REL_TO_ABS(mHead)->mPrev = -1;
  82. }
  83. else
  84. CH_REL_TO_ABS(CH_REL_TO_ABS(node)->mPrev)->mNext = CH_REL_TO_ABS(node)->mNext;
  85. if (CH_REL_TO_ABS(node)->mNext == -1)
  86. {
  87. mTail = CH_REL_TO_ABS(node)->mPrev;
  88. if (mTail != -1)
  89. CH_REL_TO_ABS(mTail)->mNext = -1;
  90. }
  91. else
  92. CH_REL_TO_ABS(CH_REL_TO_ABS(node)->mNext)->mPrev = CH_REL_TO_ABS(node)->mPrev;
  93. CH_REL_TO_ABS(node)->mPrev = -1;
  94. CH_REL_TO_ABS(node)->mNext = -1;
  95. }
  96. bool IsEmpty()
  97. {
  98. return mHead == -1;
  99. }
  100. };
  101. //////////////////////////////////////////////////////////////////////////
  102. ContiguousHeap::ContiguousHeap()
  103. {
  104. mMetadata = NULL;
  105. mMemorySize = 0;
  106. mBlockDataOfs = 0;
  107. mFreeIdx = 0;
  108. }
  109. ContiguousHeap::~ContiguousHeap()
  110. {
  111. free(mMetadata);
  112. }
  113. //#define BFCE_DEBUG_HEAP
  114. void ContiguousHeap::Clear(int maxAllocSize)
  115. {
  116. #ifdef BFCE_DEBUG_HEAP
  117. OutputDebugStrF("ContiguousHeap::Clear\n");
  118. #endif
  119. if (mBlockDataOfs == 0)
  120. return;
  121. if ((mMemorySize != -1) && (mMemorySize > maxAllocSize))
  122. {
  123. free(mMetadata);
  124. mMetadata = NULL;
  125. mMemorySize = 0;
  126. mBlockDataOfs = 0;
  127. mFreeList.Clear();
  128. return;
  129. }
  130. for (auto idx : mFreeList)
  131. {
  132. auto block = CH_REL_TO_ABS(idx);
  133. block->mKind = (ChBlockKind)0;
  134. }
  135. auto blockList = (ChList*)mMetadata;
  136. if (blockList->mHead != -1)
  137. {
  138. auto block = CH_REL_TO_ABS(blockList->mHead);
  139. while (block != NULL)
  140. {
  141. block->mKind = (ChBlockKind)0;
  142. if (block->mNext == -1)
  143. break;
  144. block = CH_REL_TO_ABS(block->mNext);
  145. }
  146. }
  147. blockList->mHead = -1;
  148. blockList->mTail = -1;
  149. mBlockDataOfs = 0;
  150. mFreeList.Clear();
  151. Validate();
  152. }
  153. static int gAllocCount = 0;
  154. ContiguousHeap::AllocRef ContiguousHeap::Alloc(int size)
  155. {
  156. if (size == 0)
  157. return 0;
  158. #ifdef BFCE_DEBUG_HEAP
  159. int allocCount = ++gAllocCount;
  160. if (allocCount == 358)
  161. {
  162. NOP;
  163. }
  164. #endif
  165. size = BF_ALIGN(size, 16);
  166. auto blockList = (ChList*)mMetadata;
  167. if (mFreeIdx >= mFreeList.mSize)
  168. mFreeIdx = 0;
  169. while (true)
  170. {
  171. for (int itr = 0; itr < (int)mFreeList.size(); itr++)
  172. {
  173. auto block = (ChBlock*)((uint8*)mMetadata + mFreeList[mFreeIdx]);
  174. if (block->mKind == ChBlockKind_Merged)
  175. {
  176. #ifdef BFCE_DEBUG_HEAP
  177. OutputDebugStrF("ContiguousHeap::Alloc %d removing merged %d\n", allocCount, (uint8*)block - (uint8*)mMetadata);
  178. #endif
  179. itr--;
  180. block->mKind = (ChBlockKind)0;
  181. mFreeList.RemoveAtFast(mFreeIdx);
  182. if (mFreeIdx >= mFreeList.mSize)
  183. mFreeIdx = 0;
  184. continue;
  185. }
  186. BF_ASSERT(block->mKind == ChBlockKind_Unused);
  187. if (block->mSize >= size)
  188. {
  189. mFreeList.RemoveAtFast(mFreeIdx);
  190. if (block->mSize >= size + 64)
  191. {
  192. int oldSize = block->mSize;
  193. // Split block
  194. auto newBlock = (ChBlock*)((uint8*)block + size);
  195. if (newBlock->mKind == 0)
  196. {
  197. mFreeList.Add(CH_ABS_TO_REL(newBlock));
  198. }
  199. else
  200. {
  201. BF_ASSERT(newBlock->mKind == ChBlockKind_Merged);
  202. #ifdef BFCE_DEBUG_HEAP
  203. BF_ASSERT(mFreeList.Contains(CH_ABS_TO_REL(newBlock)));
  204. #endif
  205. }
  206. newBlock->mPrev = -1;
  207. newBlock->mNext = -1;
  208. newBlock->mSize = block->mSize - size;
  209. newBlock->mKind = ChBlockKind_Unused;
  210. blockList->AddAfter(CH_ABS_TO_REL(block), CH_ABS_TO_REL(newBlock));
  211. block->mSize = size;
  212. #ifdef BFCE_DEBUG_HEAP
  213. OutputDebugStrF("ContiguousHeap::Alloc %d alloc %d size: %d remainder in %d size: %d\n", allocCount, CH_ABS_TO_REL(block), size, CH_ABS_TO_REL(newBlock), newBlock->mSize);
  214. #endif
  215. }
  216. else
  217. {
  218. #ifdef BFCE_DEBUG_HEAP
  219. OutputDebugStrF("ContiguousHeap::Alloc %d alloc %d size: %d\n", allocCount, CH_ABS_TO_REL(block), size);
  220. #endif
  221. }
  222. block->mKind = ChBlockKind_Used;
  223. #ifdef BFCE_DEBUG_HEAP
  224. Validate();
  225. #endif
  226. return CH_ABS_TO_REL(block);
  227. }
  228. mFreeIdx = (mFreeIdx + 1) % mFreeList.mSize;
  229. }
  230. int wantSize = BF_MAX(mMemorySize + mMemorySize / 2, mMemorySize + BF_MAX(size, 64 * 1024));
  231. wantSize = BF_ALIGN(wantSize, 16);
  232. mMetadata = realloc(mMetadata, wantSize);
  233. int prevSize = mMemorySize;
  234. memset((uint8*)mMetadata + prevSize, 0, wantSize - prevSize);
  235. blockList = (ChList*)mMetadata;
  236. mMemorySize = wantSize;
  237. ChBlock* block;
  238. if (mBlockDataOfs == 0)
  239. {
  240. blockList = new (mMetadata) ChList();
  241. mBlockDataOfs = BF_ALIGN(sizeof(ChList), 16);
  242. prevSize = mBlockDataOfs;
  243. }
  244. blockList->mMetadata = mMetadata;
  245. block = new ((uint8*)mMetadata + prevSize) ChBlock();
  246. block->mSize = wantSize - prevSize;
  247. block->mKind = ChBlockKind_Unused;
  248. blockList->PushBack(CH_ABS_TO_REL(block));
  249. mFreeList.Add(CH_ABS_TO_REL(block));
  250. #ifdef BFCE_DEBUG_HEAP
  251. Validate();
  252. OutputDebugStrF("ContiguousHeap::Alloc %d alloc %d size: %d\n", allocCount, (uint8*)block - (uint8*)mMetadata, block->mSize);
  253. #endif
  254. if (mFreeIdx >= mFreeList.mSize)
  255. mFreeIdx = 0;
  256. }
  257. }
  258. bool ContiguousHeap::Free(AllocRef ref)
  259. {
  260. if ((ref < 0) || (ref > mMemorySize - sizeof(ChBlock)))
  261. return false;
  262. #ifdef BFCE_DEBUG_HEAP
  263. int allocCount = ++gAllocCount;
  264. if (allocCount == 64)
  265. {
  266. NOP;
  267. }
  268. #endif
  269. auto blockList = (ChList*)mMetadata;
  270. auto block = CH_REL_TO_ABS(ref);
  271. if (block->mKind != ChBlockKind_Used)
  272. {
  273. #ifdef BFCE_DEBUG_HEAP
  274. OutputDebugStrF("Invalid free\n");
  275. Validate();
  276. #endif
  277. return false;
  278. }
  279. #ifdef BFCE_DEBUG_HEAP
  280. OutputDebugStrF("ContiguousHeap::Free %d block:%d size: %d\n", allocCount, CH_ABS_TO_REL(block), block->mSize);
  281. #endif
  282. int headAccSize = 0;
  283. auto mergeHead = block;
  284. while (mergeHead->mPrev != -1)
  285. {
  286. auto checkBlock = CH_REL_TO_ABS(mergeHead->mPrev);
  287. if (checkBlock->mKind != ChBlockKind_Unused)
  288. break;
  289. headAccSize += mergeHead->mSize;
  290. // Mark PREVIOUS as merged, only leave the current alive
  291. mergeHead->mKind = ChBlockKind_Merged;
  292. blockList->Remove(CH_ABS_TO_REL(mergeHead));
  293. mergeHead = checkBlock;
  294. #ifdef BFCE_DEBUG_HEAP
  295. OutputDebugStrF(" Setting Merged block %d\n", CH_ABS_TO_REL(mergeHead));
  296. #endif
  297. }
  298. int tailAccSize = 0;
  299. if (mergeHead->mNext != -1)
  300. {
  301. auto mergeTail = CH_REL_TO_ABS(mergeHead->mNext);
  302. while (mergeTail->mKind == ChBlockKind_Unused)
  303. {
  304. ChBlock* nextBlock = NULL;
  305. if (mergeTail->mNext != -1)
  306. nextBlock = CH_REL_TO_ABS(mergeTail->mNext);
  307. tailAccSize += mergeTail->mSize;
  308. mergeTail->mKind = ChBlockKind_Merged;
  309. blockList->Remove(CH_ABS_TO_REL(mergeTail));
  310. #ifdef BFCE_DEBUG_HEAP
  311. OutputDebugStrF(" Setting Merged block %d\n", CH_ABS_TO_REL(mergeTail));
  312. #endif
  313. if (nextBlock == NULL)
  314. break;
  315. mergeTail = nextBlock;
  316. }
  317. }
  318. mergeHead->mSize += tailAccSize + headAccSize;
  319. if ((mergeHead->mKind != ChBlockKind_Unused) && (mergeHead->mKind != ChBlockKind_Merged))
  320. {
  321. // If it were MERGED that means it's still in the free list
  322. mFreeList.Add(CH_ABS_TO_REL(mergeHead));
  323. }
  324. mergeHead->mKind = ChBlockKind_Unused;
  325. if (mergeHead != block)
  326. {
  327. // We weren't in the free list so don't mark as Merged
  328. block->mKind = (ChBlockKind)0;
  329. }
  330. #ifdef BFCE_DEBUG_HEAP
  331. Validate();
  332. #endif
  333. return true;
  334. }
  335. void ContiguousHeap::DebugDump()
  336. {
  337. String str = "Heap Dump:\n";
  338. auto blockList = (ChList*)mMetadata;
  339. if (blockList->mHead != -1)
  340. {
  341. int totalSize = 0;
  342. auto block = CH_REL_TO_ABS(blockList->mHead);
  343. while (block != NULL)
  344. {
  345. str += StrFormat("@%d: %d ", CH_ABS_TO_REL(block), block->mSize);
  346. switch (block->mKind)
  347. {
  348. case ChBlockKind_Unused:
  349. str += "Unused";
  350. break;
  351. case ChBlockKind_Used:
  352. str += "Used";
  353. break;
  354. case ChBlockKind_Merged:
  355. str += "Merged";
  356. break;
  357. default:
  358. str += "??????";
  359. }
  360. str += "\n";
  361. totalSize += block->mSize;
  362. if (block->mNext == -1)
  363. break;
  364. block = CH_REL_TO_ABS(block->mNext);
  365. }
  366. str += StrFormat("Sum: %d Allocated: %d\n", totalSize, mMemorySize);
  367. }
  368. str += "\nFree List:\n";
  369. for (auto idx : mFreeList)
  370. {
  371. auto block = CH_REL_TO_ABS(idx);
  372. const char* kind = "??";
  373. if (block->mKind == ChBlockKind_Unused)
  374. kind = "Unused";
  375. else
  376. kind = "Merged";
  377. str += StrFormat("@%d %s\n", idx, kind);
  378. }
  379. str += "\n";
  380. OutputDebugStrF(str.c_str());
  381. }
  382. void ContiguousHeap::Validate()
  383. {
  384. if (!mFreeList.IsEmpty())
  385. {
  386. BF_ASSERT_REL(mMetadata != NULL);
  387. }
  388. HashSet<int> freeSet;
  389. for (auto idx : mFreeList)
  390. freeSet.Add(idx);
  391. auto blockList = (ChList*)mMetadata;
  392. bool deepValidate = true;
  393. if (deepValidate)
  394. {
  395. if (blockList->mHead != -1)
  396. {
  397. int totalSize = 0;
  398. auto block = CH_REL_TO_ABS(blockList->mHead);
  399. auto blockEnd = (ChBlock*)((uint8*)blockList + mMemorySize);
  400. while (block != blockEnd)
  401. {
  402. switch (block->mKind)
  403. {
  404. case (ChBlockKind)0:
  405. BF_ASSERT_REL(!freeSet.Contains(CH_ABS_TO_REL(block)));
  406. break;
  407. case ChBlockKind_Unused:
  408. BF_ASSERT_REL(freeSet.Remove(CH_ABS_TO_REL(block)));
  409. break;
  410. case ChBlockKind_Merged:
  411. BF_ASSERT_REL(freeSet.Remove(CH_ABS_TO_REL(block)));
  412. break;
  413. case ChBlockKind_Used:
  414. BF_ASSERT_REL(!freeSet.Contains(CH_ABS_TO_REL(block)));
  415. break;
  416. default:
  417. BF_FATAL("Invalid state");
  418. }
  419. block = (ChBlock*)((uint8*)block + 16);
  420. }
  421. BF_ASSERT_REL(freeSet.IsEmpty());
  422. }
  423. }
  424. else
  425. {
  426. if (blockList->mHead != -1)
  427. {
  428. int totalSize = 0;
  429. auto block = CH_REL_TO_ABS(blockList->mHead);
  430. while (block != NULL)
  431. {
  432. switch (block->mKind)
  433. {
  434. case ChBlockKind_Unused:
  435. BF_ASSERT_REL(freeSet.Remove(CH_ABS_TO_REL(block)));
  436. break;
  437. case ChBlockKind_Used:
  438. BF_ASSERT_REL(!freeSet.Contains(CH_ABS_TO_REL(block)));
  439. break;
  440. default:
  441. BF_FATAL("Invalid state");
  442. }
  443. if (block->mNext == -1)
  444. break;
  445. block = CH_REL_TO_ABS(block->mNext);
  446. }
  447. }
  448. }
  449. for (auto idx : freeSet)
  450. {
  451. auto block = CH_REL_TO_ABS(idx);
  452. BF_ASSERT_REL(block->mKind == ChBlockKind_Merged);
  453. }
  454. //BF_ASSERT(freeSet.IsEmpty());
  455. }