multi_heap.c 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811
  1. // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. // http://www.apache.org/licenses/LICENSE-2.0
  7. //
  8. // Unless required by applicable law or agreed to in writing, software
  9. // distributed under the License is distributed on an "AS IS" BASIS,
  10. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. // See the License for the specific language governing permissions and
  12. // limitations under the License.
  13. #include <stdint.h>
  14. #include <stdlib.h>
  15. #include <stdbool.h>
  16. #include <assert.h>
  17. #include <string.h>
  18. #include <stddef.h>
  19. #include <stdio.h>
  20. #include <multi_heap.h>
  21. #include "multi_heap_internal.h"
  22. /* Note: Keep platform-specific parts in this header, this source
  23. file should depend on libc only */
  24. #include "multi_heap_platform.h"
  25. /* Defines compile-time configuration macros */
  26. #include "multi_heap_config.h"
  27. #ifndef MULTI_HEAP_POISONING
  28. /* if no heap poisoning, public API aliases directly to these implementations */
  29. void *multi_heap_malloc(multi_heap_handle_t heap, size_t size)
  30. __attribute__((alias("multi_heap_malloc_impl")));
  31. void *multi_heap_aligned_alloc(multi_heap_handle_t heap, size_t size, size_t alignment)
  32. __attribute__((alias("multi_heap_aligned_alloc_impl")));
  33. void multi_heap_free(multi_heap_handle_t heap, void *p)
  34. __attribute__((alias("multi_heap_free_impl")));
  35. void multi_heap_aligned_free(multi_heap_handle_t heap, void *p)
  36. __attribute__((alias("multi_heap_aligned_free_impl")));
  37. void *multi_heap_realloc(multi_heap_handle_t heap, void *p, size_t size)
  38. __attribute__((alias("multi_heap_realloc_impl")));
  39. size_t multi_heap_get_allocated_size(multi_heap_handle_t heap, void *p)
  40. __attribute__((alias("multi_heap_get_allocated_size_impl")));
  41. multi_heap_handle_t multi_heap_register(void *start, size_t size)
  42. __attribute__((alias("multi_heap_register_impl")));
  43. void multi_heap_get_info(multi_heap_handle_t heap, multi_heap_info_t *info)
  44. __attribute__((alias("multi_heap_get_info_impl")));
  45. size_t multi_heap_free_size(multi_heap_handle_t heap)
  46. __attribute__((alias("multi_heap_free_size_impl")));
  47. size_t multi_heap_minimum_free_size(multi_heap_handle_t heap)
  48. __attribute__((alias("multi_heap_minimum_free_size_impl")));
  49. void *multi_heap_get_block_address(multi_heap_block_handle_t block)
  50. __attribute__((alias("multi_heap_get_block_address_impl")));
  51. void *multi_heap_get_block_owner(multi_heap_block_handle_t block)
  52. {
  53. return NULL;
  54. }
  55. #endif
  56. #define ALIGN(X) ((X) & ~(sizeof(void *)-1))
  57. #define ALIGN_UP(X) ALIGN((X)+sizeof(void *)-1)
  58. #define ALIGN_UP_BY(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
  59. struct heap_block;
  60. /* Block in the heap
  61. Heap implementation uses two single linked lists, a block list (all blocks) and a free list (free blocks).
  62. 'header' holds a pointer to the next block (used or free) ORed with a free flag (the LSB of the pointer.) is_free() and get_next_block() utility functions allow typed access to these values.
  63. 'next_free' is valid if the block is free and is a pointer to the next block in the free list.
  64. */
  65. typedef struct heap_block {
  66. intptr_t header; /* Encodes next block in heap (used or unused) and also free/used flag */
  67. union {
  68. uint8_t data[1]; /* First byte of data, valid if block is used. Actual size of data is 'block_data_size(block)' */
  69. struct heap_block *next_free; /* Pointer to next free block, valid if block is free */
  70. };
  71. } heap_block_t;
  72. /* These masks apply to the 'header' field of heap_block_t */
  73. #define BLOCK_FREE_FLAG 0x1 /* If set, this block is free & next_free pointer is valid */
  74. #define NEXT_BLOCK_MASK (~3) /* AND header with this mask to get pointer to next block (free or used) */
  75. /* Metadata header for the heap, stored at the beginning of heap space.
  76. 'first_block' is a "fake" first block, minimum length, used to provide a pointer to the first used & free block in
  77. the heap. This block is never allocated or merged into an adjacent block.
  78. 'last_block' is a pointer to a final free block of length 0, which is added at the end of the heap when it is
  79. registered. This block is also never allocated or merged into an adjacent block.
  80. */
  81. typedef struct multi_heap_info {
  82. void *lock;
  83. size_t free_bytes;
  84. size_t minimum_free_bytes;
  85. heap_block_t *last_block;
  86. heap_block_t first_block; /* initial 'free block', never allocated */
  87. } heap_t;
  88. /* Given a pointer to the 'data' field of a block (ie the previous malloc/realloc result), return a pointer to the
  89. containing block.
  90. */
  91. static inline heap_block_t *get_block(const void *data_ptr)
  92. {
  93. return (heap_block_t *)((char *)data_ptr - offsetof(heap_block_t, data));
  94. }
  95. /* Return the next sequential block in the heap.
  96. */
  97. static inline heap_block_t *get_next_block(const heap_block_t *block)
  98. {
  99. intptr_t next = block->header & NEXT_BLOCK_MASK;
  100. if (next == 0) {
  101. return NULL; /* last_block */
  102. }
  103. assert(next > (intptr_t)block);
  104. return (heap_block_t *)next;
  105. }
  106. /* Return true if this block is free. */
  107. static inline bool is_free(const heap_block_t *block)
  108. {
  109. return block->header & BLOCK_FREE_FLAG;
  110. }
  111. /* Return true if this block is the first in the heap */
  112. static inline bool is_first_block(const heap_t *heap, const heap_block_t *block)
  113. {
  114. return (block == &heap->first_block);
  115. }
  116. /* Return true if this block is the last_block in the heap
  117. (the only block with no next pointer) */
  118. static inline bool is_last_block(const heap_block_t *block)
  119. {
  120. return (block->header & NEXT_BLOCK_MASK) == 0;
  121. }
  122. /* Data size of the block (excludes this block's header) */
  123. static inline size_t block_data_size(const heap_block_t *block)
  124. {
  125. intptr_t next = (intptr_t)block->header & NEXT_BLOCK_MASK;
  126. intptr_t this = (intptr_t)block;
  127. if (next == 0) {
  128. return 0; /* this is the last block in the heap */
  129. }
  130. return next - this - sizeof(block->header);
  131. }
  132. /* Check a block is valid for this heap. Used to verify parameters. */
  133. static void assert_valid_block(const heap_t *heap, const heap_block_t *block)
  134. {
  135. MULTI_HEAP_ASSERT(block >= &heap->first_block && block <= heap->last_block,
  136. block); // block not in heap
  137. if (heap < (const heap_t *)heap->last_block) {
  138. const heap_block_t *next = get_next_block(block);
  139. MULTI_HEAP_ASSERT(next >= &heap->first_block && next <= heap->last_block, block); // Next block not in heap
  140. if (is_free(block)) {
  141. // Check block->next_free is valid
  142. MULTI_HEAP_ASSERT(block->next_free >= &heap->first_block && block->next_free <= heap->last_block, &block->next_free);
  143. }
  144. }
  145. }
  146. /* Get the first free block before 'block' in the heap. 'block' can be a free block or in use.
  147. Result is always the closest free block to 'block' in the heap, that is located before 'block'. There may be multiple
  148. allocated blocks between the result and 'block'.
  149. If 'block' is free, the result's 'next_free' pointer will already point to 'block'.
  150. Result will never be NULL, but it may be the header block heap->first_block.
  151. */
  152. static heap_block_t *get_prev_free_block(heap_t *heap, const heap_block_t *block)
  153. {
  154. assert(!is_first_block(heap, block)); /* can't look for a block before first_block */
  155. for (heap_block_t *b = &heap->first_block; b != NULL && b < block; b = b->next_free) {
  156. MULTI_HEAP_ASSERT(is_free(b), b); // Block should be free
  157. if (b->next_free == NULL || b->next_free >= block) {
  158. if (is_free(block)) {
  159. /* if block is on freelist, 'b' should be the item before it. */
  160. MULTI_HEAP_ASSERT(b->next_free == block, &b->next_free);
  161. }
  162. return b; /* b is the last free block before 'block' */
  163. }
  164. }
  165. abort(); /* There should always be a previous free block, even if it's heap->first_block */
  166. }
  167. /* Merge some block 'a' into the following block 'b'.
  168. If both blocks are free, resulting block is marked free.
  169. If only one block is free, resulting block is marked in use. No data is moved.
  170. This operation may fail if block 'a' is the first block or 'b' is the last block,
  171. the caller should check block_data_size() to know if anything happened here or not.
  172. */
  173. static heap_block_t *merge_adjacent(heap_t *heap, heap_block_t *a, heap_block_t *b)
  174. {
  175. assert(a < b);
  176. /* Can't merge header blocks, just return the non-header block as-is */
  177. if (is_last_block(b)) {
  178. return a;
  179. }
  180. if (is_first_block(heap, a)) {
  181. return b;
  182. }
  183. MULTI_HEAP_ASSERT(get_next_block(a) == b, a); // Blocks should be in order
  184. bool free = is_free(a) && is_free(b); /* merging two free blocks creates a free block */
  185. if (!free && (is_free(a) || is_free(b))) {
  186. /* only one of these blocks is free, so resulting block will be a used block.
  187. means we need to take the free block out of the free list
  188. */
  189. heap_block_t *free_block = is_free(a) ? a : b;
  190. heap_block_t *prev_free = get_prev_free_block(heap, free_block);
  191. MULTI_HEAP_ASSERT(free_block->next_free > prev_free, &free_block->next_free); // Next free block should be after prev one
  192. prev_free->next_free = free_block->next_free;
  193. heap->free_bytes -= block_data_size(free_block);
  194. }
  195. a->header = b->header & NEXT_BLOCK_MASK;
  196. MULTI_HEAP_ASSERT(a->header != 0, a);
  197. if (free) {
  198. a->header |= BLOCK_FREE_FLAG;
  199. if (b->next_free != NULL) {
  200. MULTI_HEAP_ASSERT(b->next_free > a, &b->next_free);
  201. MULTI_HEAP_ASSERT(b->next_free > b, &b->next_free);
  202. }
  203. a->next_free = b->next_free;
  204. /* b's header can be put into the pool of free bytes */
  205. heap->free_bytes += sizeof(a->header);
  206. }
  207. #ifdef MULTI_HEAP_POISONING_SLOW
  208. /* b's former block header needs to be replaced with a fill pattern */
  209. multi_heap_internal_poison_fill_region(b, sizeof(heap_block_t), free);
  210. #endif
  211. return a;
  212. }
  213. /* Split a block so it can hold at least 'size' bytes of data, making any spare
  214. space into a new free block.
  215. 'block' should be marked in-use when this function is called (implementation detail, this function
  216. doesn't set the next_free pointer).
  217. 'prev_free_block' is the free block before 'block', if already known. Can be NULL if not yet known.
  218. (This is a performance optimisation to avoid walking the freelist twice when possible.)
  219. */
  220. static void split_if_necessary(heap_t *heap, heap_block_t *block, size_t size, heap_block_t *prev_free_block)
  221. {
  222. const size_t block_size = block_data_size(block);
  223. MULTI_HEAP_ASSERT(!is_free(block), block); // split block shouldn't be free
  224. MULTI_HEAP_ASSERT(size <= block_size, block); // size should be valid
  225. size = ALIGN_UP(size);
  226. /* can't split the head or tail block */
  227. assert(!is_first_block(heap, block));
  228. assert(!is_last_block(block));
  229. heap_block_t *new_block = (heap_block_t *)(block->data + size);
  230. heap_block_t *next_block = get_next_block(block);
  231. if (is_free(next_block) && !is_last_block(next_block)) {
  232. /* The next block is free, just extend it upwards. */
  233. new_block->header = next_block->header;
  234. new_block->next_free = next_block->next_free;
  235. if (prev_free_block == NULL) {
  236. prev_free_block = get_prev_free_block(heap, block);
  237. }
  238. /* prev_free_block should point to the next block (which we found to be free). */
  239. MULTI_HEAP_ASSERT(prev_free_block->next_free == next_block,
  240. &prev_free_block->next_free); // free blocks should be in order
  241. /* Note: We have not introduced a new block header, hence the simple math. */
  242. heap->free_bytes += block_size - size;
  243. #ifdef MULTI_HEAP_POISONING_SLOW
  244. /* next_block header needs to be replaced with a fill pattern */
  245. multi_heap_internal_poison_fill_region(next_block, sizeof(heap_block_t), true /* free */);
  246. #endif
  247. } else {
  248. /* Insert a free block between the current and the next one. */
  249. if (block_data_size(block) < size + sizeof(heap_block_t)) {
  250. /* Can't split 'block' if we're not going to get a usable free block afterwards */
  251. return;
  252. }
  253. if (prev_free_block == NULL) {
  254. prev_free_block = get_prev_free_block(heap, block);
  255. }
  256. new_block->header = block->header | BLOCK_FREE_FLAG;
  257. new_block->next_free = prev_free_block->next_free;
  258. /* prev_free_block should point to a free block after new_block */
  259. MULTI_HEAP_ASSERT(prev_free_block->next_free > new_block,
  260. &prev_free_block->next_free); // free blocks should be in order
  261. heap->free_bytes += block_data_size(new_block);
  262. }
  263. block->header = (intptr_t)new_block;
  264. prev_free_block->next_free = new_block;
  265. }
  266. void *multi_heap_get_block_address_impl(multi_heap_block_handle_t block)
  267. {
  268. return ((char *)block + offsetof(heap_block_t, data));
  269. }
  270. size_t multi_heap_get_allocated_size_impl(multi_heap_handle_t heap, void *p)
  271. {
  272. heap_block_t *pb = get_block(p);
  273. assert_valid_block(heap, pb);
  274. MULTI_HEAP_ASSERT(!is_free(pb), pb); // block shouldn't be free
  275. return block_data_size(pb);
  276. }
  277. multi_heap_handle_t multi_heap_register_impl(void *start_ptr, size_t size)
  278. {
  279. uintptr_t start = ALIGN_UP((uintptr_t)start_ptr);
  280. uintptr_t end = ALIGN((uintptr_t)start_ptr + size);
  281. heap_t *heap = (heap_t *)start;
  282. size = end - start;
  283. if (end < start || size < sizeof(heap_t) + 2*sizeof(heap_block_t)) {
  284. return NULL; /* 'size' is too small to fit a heap here */
  285. }
  286. heap->lock = NULL;
  287. heap->last_block = (heap_block_t *)(end - sizeof(heap_block_t));
  288. /* first 'real' (allocatable) free block goes after the heap structure */
  289. heap_block_t *first_free_block = (heap_block_t *)(start + sizeof(heap_t));
  290. first_free_block->header = (intptr_t)heap->last_block | BLOCK_FREE_FLAG;
  291. first_free_block->next_free = heap->last_block;
  292. /* last block is 'free' but has a NULL next pointer */
  293. heap->last_block->header = BLOCK_FREE_FLAG;
  294. heap->last_block->next_free = NULL;
  295. /* first block also 'free' but has legitimate length,
  296. malloc will never allocate into this block. */
  297. heap->first_block.header = (intptr_t)first_free_block | BLOCK_FREE_FLAG;
  298. heap->first_block.next_free = first_free_block;
  299. /* free bytes is:
  300. - total bytes in heap
  301. - minus heap_t header at top (includes heap->first_block)
  302. - minus header of first_free_block
  303. - minus whole block at heap->last_block
  304. */
  305. heap->free_bytes = size - sizeof(heap_t) - sizeof(first_free_block->header) - sizeof(heap_block_t);
  306. heap->minimum_free_bytes = heap->free_bytes;
  307. return heap;
  308. }
  309. void multi_heap_set_lock(multi_heap_handle_t heap, void *lock)
  310. {
  311. heap->lock = lock;
  312. }
  313. void inline multi_heap_internal_lock(multi_heap_handle_t heap)
  314. {
  315. MULTI_HEAP_LOCK(heap->lock);
  316. }
  317. void inline multi_heap_internal_unlock(multi_heap_handle_t heap)
  318. {
  319. MULTI_HEAP_UNLOCK(heap->lock);
  320. }
  321. multi_heap_block_handle_t multi_heap_get_first_block(multi_heap_handle_t heap)
  322. {
  323. return &heap->first_block;
  324. }
  325. multi_heap_block_handle_t multi_heap_get_next_block(multi_heap_handle_t heap, multi_heap_block_handle_t block)
  326. {
  327. heap_block_t *next = get_next_block(block);
  328. /* check for valid free last block to avoid assert in assert_valid_block */
  329. if (next == heap->last_block && is_last_block(next) && is_free(next)) {
  330. return NULL;
  331. }
  332. assert_valid_block(heap, next);
  333. return next;
  334. }
  335. bool multi_heap_is_free(multi_heap_block_handle_t block)
  336. {
  337. return is_free(block);
  338. }
  339. void *multi_heap_malloc_impl(multi_heap_handle_t heap, size_t size)
  340. {
  341. heap_block_t *best_block = NULL;
  342. heap_block_t *prev_free = NULL;
  343. heap_block_t *prev = NULL;
  344. size_t best_size = SIZE_MAX;
  345. size = ALIGN_UP(size);
  346. if (size == 0 || heap == NULL) {
  347. return NULL;
  348. }
  349. multi_heap_internal_lock(heap);
  350. /* Note: this check must be done while holding the lock as both
  351. malloc & realloc may temporarily shrink the free_bytes value
  352. before they split a large block. This can result in false negatives,
  353. especially if the heap is unfragmented.
  354. */
  355. if (heap->free_bytes < size) {
  356. MULTI_HEAP_UNLOCK(heap->lock);
  357. return NULL;
  358. }
  359. /* Find best free block to perform the allocation in */
  360. prev = &heap->first_block;
  361. for (heap_block_t *b = heap->first_block.next_free; b != NULL; b = b->next_free) {
  362. MULTI_HEAP_ASSERT(b > prev, &prev->next_free); // free blocks should be ascending in address
  363. MULTI_HEAP_ASSERT(is_free(b), b); // block should be free
  364. size_t bs = block_data_size(b);
  365. if (bs >= size && bs < best_size) {
  366. best_block = b;
  367. best_size = bs;
  368. prev_free = prev;
  369. if (bs == size) {
  370. break; /* we've found a perfect sized block */
  371. }
  372. }
  373. prev = b;
  374. }
  375. if (best_block == NULL) {
  376. multi_heap_internal_unlock(heap);
  377. return NULL; /* No room in heap */
  378. }
  379. prev_free->next_free = best_block->next_free;
  380. best_block->header &= ~BLOCK_FREE_FLAG;
  381. heap->free_bytes -= block_data_size(best_block);
  382. split_if_necessary(heap, best_block, size, prev_free);
  383. if (heap->free_bytes < heap->minimum_free_bytes) {
  384. heap->minimum_free_bytes = heap->free_bytes;
  385. }
  386. multi_heap_internal_unlock(heap);
  387. return best_block->data;
  388. }
  389. void *multi_heap_aligned_alloc_impl(multi_heap_handle_t heap, size_t size, size_t alignment)
  390. {
  391. if (heap == NULL) {
  392. return NULL;
  393. }
  394. if (!size) {
  395. return NULL;
  396. }
  397. if (!alignment) {
  398. return NULL;
  399. }
  400. //Alignment must be a power of two...
  401. if ((alignment & (alignment - 1)) != 0) {
  402. return NULL;
  403. }
  404. uint32_t overhead = (sizeof(uint32_t) + (alignment - 1));
  405. multi_heap_internal_lock(heap);
  406. void *head = multi_heap_malloc_impl(heap, size + overhead);
  407. if (head == NULL) {
  408. multi_heap_internal_unlock(heap);
  409. return NULL;
  410. }
  411. //Lets align our new obtained block address:
  412. //and save information to recover original block pointer
  413. //to allow us to deallocate the memory when needed
  414. void *ptr = (void *)ALIGN_UP_BY((uintptr_t)head + sizeof(uint32_t), alignment);
  415. *((uint32_t *)ptr - 1) = (uint32_t)((uintptr_t)ptr - (uintptr_t)head);
  416. multi_heap_internal_unlock(heap);
  417. return ptr;
  418. }
  419. void multi_heap_aligned_free_impl(multi_heap_handle_t heap, void *p)
  420. {
  421. if (p == NULL) {
  422. return;
  423. }
  424. multi_heap_internal_lock(heap);
  425. uint32_t offset = *((uint32_t *)p - 1);
  426. void *block_head = (void *)((uint8_t *)p - offset);
  427. #ifdef MULTI_HEAP_POISONING_SLOW
  428. multi_heap_internal_poison_fill_region(block_head, multi_heap_get_allocated_size_impl(heap, block_head), true /* free */);
  429. #endif
  430. multi_heap_free_impl(heap, block_head);
  431. multi_heap_internal_unlock(heap);
  432. }
  433. void multi_heap_free_impl(multi_heap_handle_t heap, void *p)
  434. {
  435. heap_block_t *pb = get_block(p);
  436. if (heap == NULL || p == NULL) {
  437. return;
  438. }
  439. multi_heap_internal_lock(heap);
  440. assert_valid_block(heap, pb);
  441. MULTI_HEAP_ASSERT(!is_free(pb), pb); // block should not be free
  442. MULTI_HEAP_ASSERT(!is_last_block(pb), pb); // block should not be last block
  443. MULTI_HEAP_ASSERT(!is_first_block(heap, pb), pb); // block should not be first block
  444. heap_block_t *next = get_next_block(pb);
  445. /* Update freelist pointers */
  446. heap_block_t *prev_free = get_prev_free_block(heap, pb);
  447. // freelist validity check
  448. MULTI_HEAP_ASSERT(prev_free->next_free == NULL || prev_free->next_free > pb, &prev_free->next_free);
  449. pb->next_free = prev_free->next_free;
  450. prev_free->next_free = pb;
  451. /* Mark this block as free */
  452. pb->header |= BLOCK_FREE_FLAG;
  453. heap->free_bytes += block_data_size(pb);
  454. /* Try and merge previous free block into this one */
  455. if (get_next_block(prev_free) == pb) {
  456. pb = merge_adjacent(heap, prev_free, pb);
  457. }
  458. /* If next block is free, try to merge the two */
  459. if (is_free(next)) {
  460. pb = merge_adjacent(heap, pb, next);
  461. }
  462. multi_heap_internal_unlock(heap);
  463. }
  464. void *multi_heap_realloc_impl(multi_heap_handle_t heap, void *p, size_t size)
  465. {
  466. heap_block_t *pb = get_block(p);
  467. void *result;
  468. size = ALIGN_UP(size);
  469. assert(heap != NULL);
  470. if (p == NULL) {
  471. return multi_heap_malloc_impl(heap, size);
  472. }
  473. assert_valid_block(heap, pb);
  474. // non-null realloc arg should be allocated
  475. MULTI_HEAP_ASSERT(!is_free(pb), pb);
  476. if (size == 0) {
  477. /* note: calling multi_free_impl() here as we've already been
  478. through any poison-unwrapping */
  479. multi_heap_free_impl(heap, p);
  480. return NULL;
  481. }
  482. if (heap == NULL) {
  483. return NULL;
  484. }
  485. multi_heap_internal_lock(heap);
  486. result = NULL;
  487. if (size <= block_data_size(pb)) {
  488. // Shrinking....
  489. split_if_necessary(heap, pb, size, NULL);
  490. result = pb->data;
  491. }
  492. else if (heap->free_bytes < size - block_data_size(pb)) {
  493. // Growing, but there's not enough total free space in the heap
  494. multi_heap_internal_unlock(heap);
  495. return NULL;
  496. }
  497. // New size is larger than existing block
  498. if (result == NULL) {
  499. // See if we can grow into one or both adjacent blocks
  500. heap_block_t *orig_pb = pb;
  501. size_t orig_size = block_data_size(orig_pb);
  502. heap_block_t *next = get_next_block(pb);
  503. heap_block_t *prev = get_prev_free_block(heap, pb);
  504. // Can only grow into the previous free block if it's adjacent
  505. size_t prev_grow_size = (get_next_block(prev) == pb) ? block_data_size(prev) : 0;
  506. // Can grow into next block? (we may also need to grow into 'prev' to get to our desired size)
  507. if (is_free(next) && (block_data_size(pb) + block_data_size(next) + prev_grow_size >= size)) {
  508. pb = merge_adjacent(heap, pb, next);
  509. }
  510. // Can grow into previous block?
  511. // (try this even if we're already big enough from growing into 'next', as it reduces fragmentation)
  512. if (prev_grow_size > 0 && (block_data_size(pb) + prev_grow_size >= size)) {
  513. pb = merge_adjacent(heap, prev, pb);
  514. // this doesn't guarantee we'll be left with a big enough block, as it's
  515. // possible for the merge to fail if prev == heap->first_block
  516. }
  517. if (block_data_size(pb) >= size) {
  518. memmove(pb->data, orig_pb->data, orig_size);
  519. split_if_necessary(heap, pb, size, NULL);
  520. result = pb->data;
  521. }
  522. }
  523. if (result == NULL) {
  524. // Need to allocate elsewhere and copy data over
  525. //
  526. // (Calling _impl versions here as we've already been through any
  527. // unwrapping for heap poisoning features.)
  528. result = multi_heap_malloc_impl(heap, size);
  529. if (result != NULL) {
  530. memcpy(result, pb->data, block_data_size(pb));
  531. multi_heap_free_impl(heap, pb->data);
  532. }
  533. }
  534. if (heap->free_bytes < heap->minimum_free_bytes) {
  535. heap->minimum_free_bytes = heap->free_bytes;
  536. }
  537. multi_heap_internal_unlock(heap);
  538. return result;
  539. }
  540. #define FAIL_PRINT(MSG, ...) do { \
  541. if (print_errors) { \
  542. MULTI_HEAP_STDERR_PRINTF(MSG, __VA_ARGS__); \
  543. } \
  544. valid = false; \
  545. } \
  546. while(0)
  547. bool multi_heap_check(multi_heap_handle_t heap, bool print_errors)
  548. {
  549. bool valid = true;
  550. size_t total_free_bytes = 0;
  551. assert(heap != NULL);
  552. multi_heap_internal_lock(heap);
  553. heap_block_t *prev = NULL;
  554. heap_block_t *prev_free = NULL;
  555. heap_block_t *expected_free = NULL;
  556. /* note: not using get_next_block() in loop, so that assertions aren't checked here */
  557. for(heap_block_t *b = &heap->first_block; b != NULL; b = (heap_block_t *)(b->header & NEXT_BLOCK_MASK)) {
  558. if (b == prev) {
  559. FAIL_PRINT("CORRUPT HEAP: Block %p points to itself\n", b);
  560. goto done;
  561. }
  562. if (b < prev) {
  563. FAIL_PRINT("CORRUPT HEAP: Block %p is before prev block %p\n", b, prev);
  564. goto done;
  565. }
  566. if (b > heap->last_block || b < &heap->first_block) {
  567. FAIL_PRINT("CORRUPT HEAP: Block %p is outside heap (last valid block %p)\n", b, prev);
  568. goto done;
  569. }
  570. if (is_free(b)) {
  571. if (prev != NULL && is_free(prev) && !is_first_block(heap, prev) && !is_last_block(b)) {
  572. FAIL_PRINT("CORRUPT HEAP: Two adjacent free blocks found, %p and %p\n", prev, b);
  573. }
  574. if (expected_free != NULL && expected_free != b) {
  575. FAIL_PRINT("CORRUPT HEAP: Prev free block %p pointed to next free %p but this free block is %p\n",
  576. prev_free, expected_free, b);
  577. }
  578. prev_free = b;
  579. expected_free = b->next_free;
  580. if (!is_first_block(heap, b)) {
  581. total_free_bytes += block_data_size(b);
  582. }
  583. }
  584. prev = b;
  585. #ifdef MULTI_HEAP_POISONING
  586. if (!is_last_block(b)) {
  587. /* For slow heap poisoning, any block should contain correct poisoning patterns and/or fills */
  588. bool poison_ok;
  589. if (is_free(b) && b != heap->last_block) {
  590. uint32_t block_len = (intptr_t)get_next_block(b) - (intptr_t)b - sizeof(heap_block_t);
  591. poison_ok = multi_heap_internal_check_block_poisoning(&b[1], block_len, true, print_errors);
  592. }
  593. else {
  594. poison_ok = multi_heap_internal_check_block_poisoning(b->data, block_data_size(b), false, print_errors);
  595. }
  596. valid = poison_ok && valid;
  597. }
  598. #endif
  599. } /* for(heap_block_t b = ... */
  600. if (prev != heap->last_block) {
  601. FAIL_PRINT("CORRUPT HEAP: Last block %p not %p\n", prev, heap->last_block);
  602. }
  603. if (!is_free(heap->last_block)) {
  604. FAIL_PRINT("CORRUPT HEAP: Expected prev block %p to be free\n", heap->last_block);
  605. }
  606. if (heap->free_bytes != total_free_bytes) {
  607. FAIL_PRINT("CORRUPT HEAP: Expected %u free bytes counted %u\n", (unsigned)heap->free_bytes, (unsigned)total_free_bytes);
  608. }
  609. done:
  610. multi_heap_internal_unlock(heap);
  611. return valid;
  612. }
  613. void multi_heap_dump(multi_heap_handle_t heap)
  614. {
  615. assert(heap != NULL);
  616. multi_heap_internal_lock(heap);
  617. MULTI_HEAP_STDERR_PRINTF("Heap start %p end %p\nFirst free block %p\n", &heap->first_block, heap->last_block, heap->first_block.next_free);
  618. for(heap_block_t *b = &heap->first_block; b != NULL; b = get_next_block(b)) {
  619. MULTI_HEAP_STDERR_PRINTF("Block %p data size 0x%08x bytes next block %p", b, block_data_size(b), get_next_block(b));
  620. if (is_free(b)) {
  621. MULTI_HEAP_STDERR_PRINTF(" FREE. Next free %p\n", b->next_free);
  622. } else {
  623. MULTI_HEAP_STDERR_PRINTF("%s", "\n"); /* C macros & optional __VA_ARGS__ */
  624. }
  625. }
  626. multi_heap_internal_unlock(heap);
  627. }
  628. size_t multi_heap_free_size_impl(multi_heap_handle_t heap)
  629. {
  630. if (heap == NULL) {
  631. return 0;
  632. }
  633. return heap->free_bytes;
  634. }
  635. size_t multi_heap_minimum_free_size_impl(multi_heap_handle_t heap)
  636. {
  637. if (heap == NULL) {
  638. return 0;
  639. }
  640. return heap->minimum_free_bytes;
  641. }
  642. void multi_heap_get_info_impl(multi_heap_handle_t heap, multi_heap_info_t *info)
  643. {
  644. memset(info, 0, sizeof(multi_heap_info_t));
  645. if (heap == NULL) {
  646. return;
  647. }
  648. multi_heap_internal_lock(heap);
  649. for(heap_block_t *b = get_next_block(&heap->first_block); !is_last_block(b); b = get_next_block(b)) {
  650. info->total_blocks++;
  651. if (is_free(b)) {
  652. size_t s = block_data_size(b);
  653. info->total_free_bytes += s;
  654. if (s > info->largest_free_block) {
  655. info->largest_free_block = s;
  656. }
  657. info->free_blocks++;
  658. } else {
  659. info->total_allocated_bytes += block_data_size(b);
  660. info->allocated_blocks++;
  661. }
  662. }
  663. info->minimum_free_bytes = heap->minimum_free_bytes;
  664. // heap has wrong total size (address printed here is not indicative of the real error)
  665. MULTI_HEAP_ASSERT(info->total_free_bytes == heap->free_bytes, heap);
  666. multi_heap_internal_unlock(heap);
  667. }