heap.cpp 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. /* SCE CONFIDENTIAL
  2. $PSLibId$
  3. * Copyright (C) 2011 Sony Computer Entertainment Inc.
  4. * All Rights Reserved.
  5. */
  6. #include "heap.h"
  7. #include <libdbg.h>
  8. #include <stdlib.h>
  9. // Align value
  10. #define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1))
  11. struct HeapBlock
  12. {
  13. HeapBlock *next;
  14. int32_t type;
  15. uintptr_t base;
  16. uint32_t offset;
  17. uint32_t size;
  18. };
  19. struct HeapContext
  20. {
  21. HeapBlock *allocList; ///< Not sorted, LIFO.
  22. HeapBlock *freeList; ///< Sorted by base address, always fully merged.
  23. };
  24. static HeapContext g_context;
  25. static HeapBlock *heapBlockAlloc()
  26. {
  27. // NOTE: these blocks could be pooled to improve performance
  28. return (HeapBlock *)malloc(sizeof(HeapBlock));
  29. }
  30. static void heapBlockFree(HeapBlock *block)
  31. {
  32. // NOTE: these blocks could be pooled to improve performance
  33. free(block);
  34. }
  35. static bool heapBlockCanMerge(HeapBlock *a, HeapBlock *b)
  36. {
  37. return a->type == b->type
  38. && a->base + a->size == b->base
  39. && a->offset + a->size == b->offset;
  40. }
  41. static void heapInsertFreeBlock(HeapContext *ctx, HeapBlock *block)
  42. {
  43. // advance through free list until prev < block < curr
  44. HeapBlock *curr = ctx->freeList;
  45. HeapBlock *prev = NULL;
  46. while (curr && curr->base < block->base) {
  47. prev = curr;
  48. curr = curr->next;
  49. }
  50. // sanity check no overlap with adjacent blocks
  51. SCE_DBG_ASSERT(!prev || prev->base + prev->size <= block->base);
  52. SCE_DBG_ASSERT(!curr || block->base + block->size <= curr->base);
  53. // insert into list
  54. if (prev) {
  55. prev->next = block;
  56. } else {
  57. ctx->freeList = block;
  58. }
  59. block->next = curr;
  60. // check merge with current
  61. if (curr && heapBlockCanMerge(block, curr)) {
  62. block->size += curr->size;
  63. block->next = curr->next;
  64. heapBlockFree(curr);
  65. }
  66. // check merge with prev
  67. if (prev && heapBlockCanMerge(prev, block)) {
  68. prev->size += block->size;
  69. prev->next = block->next;
  70. heapBlockFree(block);
  71. }
  72. }
  73. static HeapBlock *heapAllocBlock(HeapContext *ctx, int32_t type, uint32_t size, uint32_t alignment)
  74. {
  75. // find a suitable block in the free list
  76. HeapBlock *curr = ctx->freeList;
  77. HeapBlock *prev = NULL;
  78. while (curr) {
  79. // check this block can handle alignment and size
  80. uint32_t const skip = ALIGN(curr->base, alignment) - curr->base;
  81. if (curr->type == type && skip + size <= curr->size) {
  82. // allocate any blocks we need now to avoid complicated rollback
  83. HeapBlock *skipBlock = NULL;
  84. HeapBlock *unusedBlock = NULL;
  85. if (skip != 0) {
  86. skipBlock = heapBlockAlloc();
  87. if (!skipBlock) {
  88. return NULL;
  89. }
  90. }
  91. if (skip + size != curr->size) {
  92. unusedBlock = heapBlockAlloc();
  93. if (!unusedBlock) {
  94. if (skipBlock) {
  95. heapBlockFree(skipBlock);
  96. }
  97. return NULL;
  98. }
  99. }
  100. // add block for skipped memory
  101. if (skip != 0) {
  102. // link in
  103. if (prev) {
  104. prev->next = skipBlock;
  105. } else {
  106. ctx->freeList = skipBlock;
  107. }
  108. skipBlock->next = curr;
  109. // set sizes
  110. skipBlock->type = curr->type;
  111. skipBlock->base = curr->base;
  112. skipBlock->offset = curr->offset;
  113. skipBlock->size = skip;
  114. curr->base += skip;
  115. curr->offset += skip;
  116. curr->size -= skip;
  117. // update prev
  118. prev = skipBlock;
  119. }
  120. // add block for unused memory
  121. if (size != curr->size) {
  122. // link in
  123. unusedBlock->next = curr->next;
  124. curr->next = unusedBlock;
  125. // set sizes
  126. unusedBlock->type = curr->type;
  127. unusedBlock->base = curr->base + size;
  128. unusedBlock->offset = curr->offset + size;
  129. unusedBlock->size = curr->size - size;
  130. curr->size = size;
  131. }
  132. // unlink from free list
  133. if (prev) {
  134. prev->next = curr->next;
  135. } else {
  136. ctx->freeList = curr->next;
  137. }
  138. // push onto alloc list
  139. curr->next = ctx->allocList;
  140. ctx->allocList = curr;
  141. return curr;
  142. }
  143. // advance
  144. prev = curr;
  145. curr = curr->next;
  146. }
  147. // no block found
  148. return NULL;
  149. }
  150. static void heapFreeBlock(HeapContext *ctx, uintptr_t base)
  151. {
  152. // find in the allocate block list
  153. HeapBlock *curr = ctx->allocList;
  154. HeapBlock *prev = NULL;
  155. while (curr && curr->base != base) {
  156. prev = curr;
  157. curr = curr->next;
  158. }
  159. // early out if not found
  160. if (!curr)
  161. return;
  162. // unlink from allocated list
  163. if (prev) {
  164. prev->next = curr->next;
  165. } else {
  166. ctx->allocList = curr->next;
  167. }
  168. curr->next = NULL;
  169. // add as free block
  170. heapInsertFreeBlock(&g_context, curr);
  171. }
  172. void heapInitialize()
  173. {
  174. g_context.allocList = NULL;
  175. g_context.freeList = NULL;
  176. }
  177. void heapTerminate()
  178. {
  179. // should be empty
  180. SCE_DBG_ASSERT(!g_context.allocList);
  181. }
  182. void heapExtend(int32_t type, void *base, uint32_t size, uint32_t offset)
  183. {
  184. HeapBlock *block = heapBlockAlloc();
  185. SCE_DBG_ASSERT(block);
  186. block->next = NULL;
  187. block->type = type;
  188. block->base = (uintptr_t)base;
  189. block->offset = offset;
  190. block->size = size;
  191. heapInsertFreeBlock(&g_context, block);
  192. }
  193. void *heapAlloc(int32_t type, uint32_t size, uint32_t alignment, uint32_t *offset)
  194. {
  195. // try to allocate
  196. HeapBlock *block = heapAllocBlock(&g_context, type, size, alignment);
  197. // early out if failed
  198. if (!block)
  199. return NULL;
  200. // write offset and return base address
  201. if (offset) {
  202. *offset = block->offset;
  203. }
  204. return (void *)block->base;
  205. }
  206. void heapFree(void *addr)
  207. {
  208. heapFreeBlock(&g_context, (uintptr_t)addr);
  209. }