Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2019 Intel Corporation
3 : : *
4 : : * SPDX-License-Identifier: Apache-2.0
5 : : */
6 : : #include <sys/sys_heap.h>
7 : : #include <sys/util.h>
8 : : #include <sys/heap_listener.h>
9 : : #include <kernel.h>
10 : : #include <string.h>
11 : : #include "heap.h"
12 : :
13 : : #ifdef CONFIG_SYS_HEAP_RUNTIME_STATS
14 : : static inline void increase_allocated_bytes(struct z_heap *h, size_t num_bytes)
15 : : {
16 : : h->allocated_bytes += num_bytes;
17 : : h->max_allocated_bytes = MAX(h->max_allocated_bytes, h->allocated_bytes);
18 : : }
19 : : #endif
20 : :
21 : 86 : static void *chunk_mem(struct z_heap *h, chunkid_t c)
22 : : {
23 : 86 : chunk_unit_t *buf = chunk_buf(h);
24 : 86 : uint8_t *ret = ((uint8_t *)&buf[c]) + chunk_header_bytes(h);
25 : :
26 : : CHECK(!(((uintptr_t)ret) & (big_heap(h) ? 7 : 3)));
27 : :
28 : 86 : return ret;
29 : : }
30 : :
31 : 171 : static void free_list_remove_bidx(struct z_heap *h, chunkid_t c, int bidx)
32 : : {
33 : 171 : struct z_heap_bucket *b = &h->buckets[bidx];
34 : :
35 : : CHECK(!chunk_used(h, c));
36 : : CHECK(b->next != 0);
37 : : CHECK(h->avail_buckets & BIT(bidx));
38 : :
39 [ + - ]: 171 : if (next_free_chunk(h, c) == c) {
40 : : /* this is the last chunk */
41 : 171 : h->avail_buckets &= ~BIT(bidx);
42 : 171 : b->next = 0;
43 : : } else {
44 : 0 : chunkid_t first = prev_free_chunk(h, c),
45 : 0 : second = next_free_chunk(h, c);
46 : :
47 : 0 : b->next = second;
48 : 0 : set_next_free_chunk(h, first, second);
49 : 0 : set_prev_free_chunk(h, second, first);
50 : : }
51 : :
52 : : #ifdef CONFIG_SYS_HEAP_RUNTIME_STATS
53 : : h->free_bytes -= chunksz_to_bytes(h, chunk_size(h, c));
54 : : #endif
55 : 171 : }
56 : :
57 : 85 : static void free_list_remove(struct z_heap *h, chunkid_t c)
58 : : {
59 [ + - ]: 85 : if (!solo_free_header(h, c)) {
60 : 85 : int bidx = bucket_idx(h, chunk_size(h, c));
61 : 85 : free_list_remove_bidx(h, c, bidx);
62 : : }
63 : 85 : }
64 : :
65 : 172 : static void free_list_add_bidx(struct z_heap *h, chunkid_t c, int bidx)
66 : : {
67 : 172 : struct z_heap_bucket *b = &h->buckets[bidx];
68 : :
69 [ + - ]: 172 : if (b->next == 0U) {
70 : : CHECK((h->avail_buckets & BIT(bidx)) == 0);
71 : :
72 : : /* Empty list, first item */
73 : 172 : h->avail_buckets |= BIT(bidx);
74 : 172 : b->next = c;
75 : 172 : set_prev_free_chunk(h, c, c);
76 : 172 : set_next_free_chunk(h, c, c);
77 : : } else {
78 : : CHECK(h->avail_buckets & BIT(bidx));
79 : :
80 : : /* Insert before (!) the "next" pointer */
81 : 0 : chunkid_t second = b->next;
82 : 0 : chunkid_t first = prev_free_chunk(h, second);
83 : :
84 : 0 : set_prev_free_chunk(h, c, first);
85 : 0 : set_next_free_chunk(h, c, second);
86 : 0 : set_next_free_chunk(h, first, c);
87 : 0 : set_prev_free_chunk(h, second, c);
88 : : }
89 : :
90 : : #ifdef CONFIG_SYS_HEAP_RUNTIME_STATS
91 : : h->free_bytes += chunksz_to_bytes(h, chunk_size(h, c));
92 : : #endif
93 : 172 : }
94 : :
95 : 172 : static void free_list_add(struct z_heap *h, chunkid_t c)
96 : : {
97 [ + - ]: 172 : if (!solo_free_header(h, c)) {
98 : 172 : int bidx = bucket_idx(h, chunk_size(h, c));
99 : 172 : free_list_add_bidx(h, c, bidx);
100 : : }
101 : 172 : }
102 : :
103 : : /* Splits a chunk "lc" into a left chunk and a right chunk at "rc".
104 : : * Leaves both chunks marked "free"
105 : : */
106 : 86 : static void split_chunks(struct z_heap *h, chunkid_t lc, chunkid_t rc)
107 : : {
108 : : CHECK(rc > lc);
109 : : CHECK(rc - lc < chunk_size(h, lc));
110 : :
111 : 86 : chunksz_t sz0 = chunk_size(h, lc);
112 : 86 : chunksz_t lsz = rc - lc;
113 : 86 : chunksz_t rsz = sz0 - lsz;
114 : :
115 : 86 : set_chunk_size(h, lc, lsz);
116 : 86 : set_chunk_size(h, rc, rsz);
117 : 86 : set_left_chunk_size(h, rc, lsz);
118 : 86 : set_left_chunk_size(h, right_chunk(h, rc), rsz);
119 : 86 : }
120 : :
121 : : /* Does not modify free list */
122 : 85 : static void merge_chunks(struct z_heap *h, chunkid_t lc, chunkid_t rc)
123 : : {
124 : 85 : chunksz_t newsz = chunk_size(h, lc) + chunk_size(h, rc);
125 : :
126 : 85 : set_chunk_size(h, lc, newsz);
127 : 85 : set_left_chunk_size(h, right_chunk(h, rc), newsz);
128 : 85 : }
129 : :
130 : 85 : static void free_chunk(struct z_heap *h, chunkid_t c)
131 : : {
132 : : /* Merge with free right chunk? */
133 [ + - ]: 85 : if (!chunk_used(h, right_chunk(h, c))) {
134 : 85 : free_list_remove(h, right_chunk(h, c));
135 : 85 : merge_chunks(h, c, right_chunk(h, c));
136 : : }
137 : :
138 : : /* Merge with free left chunk? */
139 [ - + ]: 85 : if (!chunk_used(h, left_chunk(h, c))) {
140 : 0 : free_list_remove(h, left_chunk(h, c));
141 : 0 : merge_chunks(h, left_chunk(h, c), c);
142 : 0 : c = left_chunk(h, c);
143 : : }
144 : :
145 : 85 : free_list_add(h, c);
146 : 85 : }
147 : :
148 : : /*
149 : : * Return the closest chunk ID corresponding to given memory pointer.
150 : : * Here "closest" is only meaningful in the context of sys_heap_aligned_alloc()
151 : : * where wanted alignment might not always correspond to a chunk header
152 : : * boundary.
153 : : */
154 : 85 : static chunkid_t mem_to_chunkid(struct z_heap *h, void *p)
155 : : {
156 : 85 : uint8_t *mem = p, *base = (uint8_t *)chunk_buf(h);
157 : 85 : return (mem - chunk_header_bytes(h) - base) / CHUNK_UNIT;
158 : : }
159 : :
160 : 85 : void sys_heap_free(struct sys_heap *heap, void *mem)
161 : : {
162 [ - + ]: 85 : if (mem == NULL) {
163 : 0 : return; /* ISO C free() semantics */
164 : : }
165 : 85 : struct z_heap *h = heap->heap;
166 : 85 : chunkid_t c = mem_to_chunkid(h, mem);
167 : :
168 : : /*
169 : : * This should catch many double-free cases.
170 : : * This is cheap enough so let's do it all the time.
171 : : */
172 [ - + ]: 85 : __ASSERT(chunk_used(h, c),
173 : : "unexpected heap state (double-free?) for memory at %p", mem);
174 : :
175 : : /*
176 : : * It is easy to catch many common memory overflow cases with
177 : : * a quick check on this and next chunk header fields that are
178 : : * immediately before and after the freed memory.
179 : : */
180 [ - + ]: 85 : __ASSERT(left_chunk(h, right_chunk(h, c)) == c,
181 : : "corrupted heap bounds (buffer overflow?) for memory at %p",
182 : : mem);
183 : :
184 : 85 : set_chunk_used(h, c, false);
185 : : #ifdef CONFIG_SYS_HEAP_RUNTIME_STATS
186 : : h->allocated_bytes -= chunksz_to_bytes(h, chunk_size(h, c));
187 : : #endif
188 : :
189 : : #ifdef CONFIG_SYS_HEAP_LISTENER
190 : : heap_listener_notify_free(HEAP_ID_FROM_POINTER(heap), mem,
191 : : chunksz_to_bytes(h, chunk_size(h, c)));
192 : : #endif
193 : :
194 : 85 : free_chunk(h, c);
195 : : }
196 : :
197 : 0 : size_t sys_heap_usable_size(struct sys_heap *heap, void *mem)
198 : : {
199 : 0 : struct z_heap *h = heap->heap;
200 : 0 : chunkid_t c = mem_to_chunkid(h, mem);
201 : 0 : size_t addr = (size_t)mem;
202 : 0 : size_t chunk_base = (size_t)&chunk_buf(h)[c];
203 : 0 : size_t chunk_sz = chunk_size(h, c) * CHUNK_UNIT;
204 : :
205 : 0 : return chunk_sz - (addr - chunk_base);
206 : : }
207 : :
208 : 86 : static chunkid_t alloc_chunk(struct z_heap *h, chunksz_t sz)
209 : : {
210 : 86 : int bi = bucket_idx(h, sz);
211 : 86 : struct z_heap_bucket *b = &h->buckets[bi];
212 : :
213 : : CHECK(bi <= bucket_idx(h, h->end_chunk));
214 : :
215 : : /* First try a bounded count of items from the minimal bucket
216 : : * size. These may not fit, trying (e.g.) three means that
217 : : * (assuming that chunk sizes are evenly distributed[1]) we
218 : : * have a 7/8 chance of finding a match, thus keeping the
219 : : * number of such blocks consumed by allocation higher than
220 : : * the number of smaller blocks created by fragmenting larger
221 : : * ones.
222 : : *
223 : : * [1] In practice, they are never evenly distributed, of
224 : : * course. But even in pathological situations we still
225 : : * maintain our constant time performance and at worst see
226 : : * fragmentation waste of the order of the block allocated
227 : : * only.
228 : : */
229 [ + + ]: 86 : if (b->next) {
230 : 2 : chunkid_t first = b->next;
231 : 2 : int i = CONFIG_SYS_HEAP_ALLOC_LOOPS;
232 : : do {
233 : 2 : chunkid_t c = b->next;
234 [ + - ]: 2 : if (chunk_size(h, c) >= sz) {
235 : 2 : free_list_remove_bidx(h, c, bi);
236 : 2 : return c;
237 : : }
238 : 0 : b->next = next_free_chunk(h, c);
239 : : CHECK(b->next != 0);
240 [ # # # # ]: 0 : } while (--i && b->next != first);
241 : : }
242 : :
243 : : /* Otherwise pick the smallest non-empty bucket guaranteed to
244 : : * fit and use that unconditionally.
245 : : */
246 : 84 : uint32_t bmask = h->avail_buckets & ~BIT_MASK(bi + 1);
247 : :
248 [ + - ]: 84 : if (bmask != 0U) {
249 : 84 : int minbucket = __builtin_ctz(bmask);
250 : 84 : chunkid_t c = h->buckets[minbucket].next;
251 : :
252 : 84 : free_list_remove_bidx(h, c, minbucket);
253 : : CHECK(chunk_size(h, c) >= sz);
254 : 84 : return c;
255 : : }
256 : :
257 : 0 : return 0;
258 : : }
259 : :
260 : 86 : void *sys_heap_alloc(struct sys_heap *heap, size_t bytes)
261 : : {
262 : 86 : struct z_heap *h = heap->heap;
263 : : void *mem;
264 : :
265 [ + - - + ]: 86 : if (bytes == 0U || size_too_big(h, bytes)) {
266 : 0 : return NULL;
267 : : }
268 : :
269 : 86 : chunksz_t chunk_sz = bytes_to_chunksz(h, bytes);
270 : 86 : chunkid_t c = alloc_chunk(h, chunk_sz);
271 [ - + ]: 86 : if (c == 0U) {
272 : 0 : return NULL;
273 : : }
274 : :
275 : : /* Split off remainder if any */
276 [ + - ]: 86 : if (chunk_size(h, c) > chunk_sz) {
277 : 86 : split_chunks(h, c, c + chunk_sz);
278 : 86 : free_list_add(h, c + chunk_sz);
279 : : }
280 : :
281 : 86 : set_chunk_used(h, c, true);
282 : :
283 : 86 : mem = chunk_mem(h, c);
284 : :
285 : : #ifdef CONFIG_SYS_HEAP_RUNTIME_STATS
286 : : increase_allocated_bytes(h, chunksz_to_bytes(h, chunk_size(h, c)));
287 : : #endif
288 : :
289 : : #ifdef CONFIG_SYS_HEAP_LISTENER
290 : : heap_listener_notify_alloc(HEAP_ID_FROM_POINTER(heap), mem,
291 : : chunksz_to_bytes(h, chunk_size(h, c)));
292 : : #endif
293 : :
294 : 86 : return mem;
295 : : }
296 : :
297 : 86 : void *sys_heap_aligned_alloc(struct sys_heap *heap, size_t align, size_t bytes)
298 : : {
299 : 86 : struct z_heap *h = heap->heap;
300 : : size_t gap, rew;
301 : :
302 : : /*
303 : : * Split align and rewind values (if any).
304 : : * We allow for one bit of rewind in addition to the alignment
305 : : * value to efficiently accommodate z_heap_aligned_alloc().
306 : : * So if e.g. align = 0x28 (32 | 8) this means we align to a 32-byte
307 : : * boundary and then rewind 8 bytes.
308 : : */
309 : 86 : rew = align & -align;
310 [ - + ]: 86 : if (align != rew) {
311 : 0 : align -= rew;
312 [ # # ]: 0 : gap = MIN(rew, chunk_header_bytes(h));
313 : : } else {
314 [ + - ]: 86 : if (align <= chunk_header_bytes(h)) {
315 : 86 : return sys_heap_alloc(heap, bytes);
316 : : }
317 : 0 : rew = 0;
318 : 0 : gap = chunk_header_bytes(h);
319 : : }
320 [ # # ]: 0 : __ASSERT((align & (align - 1)) == 0, "align must be a power of 2");
321 : :
322 [ # # # # ]: 0 : if (bytes == 0 || size_too_big(h, bytes)) {
323 : 0 : return NULL;
324 : : }
325 : :
326 : : /*
327 : : * Find a free block that is guaranteed to fit.
328 : : * We over-allocate to account for alignment and then free
329 : : * the extra allocations afterwards.
330 : : */
331 : 0 : chunksz_t padded_sz = bytes_to_chunksz(h, bytes + align - gap);
332 : 0 : chunkid_t c0 = alloc_chunk(h, padded_sz);
333 : :
334 [ # # ]: 0 : if (c0 == 0) {
335 : 0 : return NULL;
336 : : }
337 : 0 : uint8_t *mem = chunk_mem(h, c0);
338 : :
339 : : /* Align allocated memory */
340 : 0 : mem = (uint8_t *) ROUND_UP(mem + rew, align) - rew;
341 : 0 : chunk_unit_t *end = (chunk_unit_t *) ROUND_UP(mem + bytes, CHUNK_UNIT);
342 : :
343 : : /* Get corresponding chunks */
344 : 0 : chunkid_t c = mem_to_chunkid(h, mem);
345 : 0 : chunkid_t c_end = end - chunk_buf(h);
346 : : CHECK(c >= c0 && c < c_end && c_end <= c0 + padded_sz);
347 : :
348 : : /* Split and free unused prefix */
349 [ # # ]: 0 : if (c > c0) {
350 : 0 : split_chunks(h, c0, c);
351 : 0 : free_list_add(h, c0);
352 : : }
353 : :
354 : : /* Split and free unused suffix */
355 [ # # ]: 0 : if (right_chunk(h, c) > c_end) {
356 : 0 : split_chunks(h, c, c_end);
357 : 0 : free_list_add(h, c_end);
358 : : }
359 : :
360 : 0 : set_chunk_used(h, c, true);
361 : : #ifdef CONFIG_SYS_HEAP_RUNTIME_STATS
362 : : increase_allocated_bytes(h, chunksz_to_bytes(h, chunk_size(h, c)));
363 : : #endif
364 : :
365 : : #ifdef CONFIG_SYS_HEAP_LISTENER
366 : : heap_listener_notify_alloc(HEAP_ID_FROM_POINTER(heap), mem,
367 : : chunksz_to_bytes(h, chunk_size(h, c)));
368 : : #endif
369 : :
370 : 0 : return mem;
371 : : }
372 : :
373 : 0 : void *sys_heap_aligned_realloc(struct sys_heap *heap, void *ptr,
374 : : size_t align, size_t bytes)
375 : : {
376 : 0 : struct z_heap *h = heap->heap;
377 : :
378 : : /* special realloc semantics */
379 [ # # ]: 0 : if (ptr == NULL) {
380 : 0 : return sys_heap_aligned_alloc(heap, align, bytes);
381 : : }
382 [ # # ]: 0 : if (bytes == 0) {
383 : 0 : sys_heap_free(heap, ptr);
384 : 0 : return NULL;
385 : : }
386 : :
387 [ # # ]: 0 : __ASSERT((align & (align - 1)) == 0, "align must be a power of 2");
388 : :
389 [ # # ]: 0 : if (size_too_big(h, bytes)) {
390 : 0 : return NULL;
391 : : }
392 : :
393 : 0 : chunkid_t c = mem_to_chunkid(h, ptr);
394 : 0 : chunkid_t rc = right_chunk(h, c);
395 : 0 : size_t align_gap = (uint8_t *)ptr - (uint8_t *)chunk_mem(h, c);
396 : 0 : chunksz_t chunks_need = bytes_to_chunksz(h, bytes + align_gap);
397 : :
398 [ # # # # ]: 0 : if (align && ((uintptr_t)ptr & (align - 1))) {
399 : : /* ptr is not sufficiently aligned */
400 [ # # ]: 0 : } else if (chunk_size(h, c) == chunks_need) {
401 : : /* We're good already */
402 : 0 : return ptr;
403 [ # # ]: 0 : } else if (chunk_size(h, c) > chunks_need) {
404 : : /* Shrink in place, split off and free unused suffix */
405 : : #ifdef CONFIG_SYS_HEAP_LISTENER
406 : : size_t bytes_freed = chunksz_to_bytes(h, chunk_size(h, c));
407 : : #endif
408 : :
409 : : #ifdef CONFIG_SYS_HEAP_RUNTIME_STATS
410 : : h->allocated_bytes -=
411 : : (chunk_size(h, c) - chunks_need) * CHUNK_UNIT;
412 : : #endif
413 : :
414 : 0 : split_chunks(h, c, c + chunks_need);
415 : 0 : set_chunk_used(h, c, true);
416 : 0 : free_chunk(h, c + chunks_need);
417 : :
418 : : #ifdef CONFIG_SYS_HEAP_LISTENER
419 : : heap_listener_notify_alloc(HEAP_ID_FROM_POINTER(heap), ptr,
420 : : chunksz_to_bytes(h, chunk_size(h, c)));
421 : : heap_listener_notify_free(HEAP_ID_FROM_POINTER(heap), ptr,
422 : : bytes_freed);
423 : : #endif
424 : :
425 : 0 : return ptr;
426 [ # # ]: 0 : } else if (!chunk_used(h, rc) &&
427 [ # # ]: 0 : (chunk_size(h, c) + chunk_size(h, rc) >= chunks_need)) {
428 : : /* Expand: split the right chunk and append */
429 : 0 : chunksz_t split_size = chunks_need - chunk_size(h, c);
430 : :
431 : : #ifdef CONFIG_SYS_HEAP_LISTENER
432 : : size_t bytes_freed = chunksz_to_bytes(h, chunk_size(h, c));
433 : : #endif
434 : :
435 : : #ifdef CONFIG_SYS_HEAP_RUNTIME_STATS
436 : : increase_allocated_bytes(h, split_size * CHUNK_UNIT);
437 : : #endif
438 : :
439 : 0 : free_list_remove(h, rc);
440 : :
441 [ # # ]: 0 : if (split_size < chunk_size(h, rc)) {
442 : 0 : split_chunks(h, rc, rc + split_size);
443 : 0 : free_list_add(h, rc + split_size);
444 : : }
445 : :
446 : 0 : merge_chunks(h, c, rc);
447 : 0 : set_chunk_used(h, c, true);
448 : :
449 : : #ifdef CONFIG_SYS_HEAP_LISTENER
450 : : heap_listener_notify_alloc(HEAP_ID_FROM_POINTER(heap), ptr,
451 : : chunksz_to_bytes(h, chunk_size(h, c)));
452 : : heap_listener_notify_free(HEAP_ID_FROM_POINTER(heap), ptr,
453 : : bytes_freed);
454 : : #endif
455 : :
456 : 0 : return ptr;
457 : : } else {
458 : : ;
459 : : }
460 : :
461 : : /*
462 : : * Fallback: allocate and copy
463 : : *
464 : : * Note for heap listener notification:
465 : : * The calls to allocation and free functions generate
466 : : * notification already, so there is no need to those here.
467 : : */
468 : 0 : void *ptr2 = sys_heap_aligned_alloc(heap, align, bytes);
469 : :
470 [ # # ]: 0 : if (ptr2 != NULL) {
471 : 0 : size_t prev_size = chunksz_to_bytes(h, chunk_size(h, c)) - align_gap;
472 : :
473 : 0 : memcpy(ptr2, ptr, MIN(prev_size, bytes));
474 : 0 : sys_heap_free(heap, ptr);
475 : : }
476 : 0 : return ptr2;
477 : : }
478 : :
479 : 1 : void sys_heap_init(struct sys_heap *heap, void *mem, size_t bytes)
480 : : {
481 : : if (IS_ENABLED(CONFIG_SYS_HEAP_SMALL_ONLY)) {
482 : : /* Must fit in a 15 bit count of HUNK_UNIT */
483 : : __ASSERT(bytes / CHUNK_UNIT <= 0x7fffU, "heap size is too big");
484 : : } else {
485 : : /* Must fit in a 31 bit count of HUNK_UNIT */
486 : : __ASSERT(bytes / CHUNK_UNIT <= 0x7fffffffU, "heap size is too big");
487 : : }
488 : :
489 : : /* Reserve the end marker chunk's header */
490 [ - + ]: 1 : __ASSERT(bytes > heap_footer_bytes(bytes), "heap size is too small");
491 : 1 : bytes -= heap_footer_bytes(bytes);
492 : :
493 : : /* Round the start up, the end down */
494 : 1 : uintptr_t addr = ROUND_UP(mem, CHUNK_UNIT);
495 : 1 : uintptr_t end = ROUND_DOWN((uint8_t *)mem + bytes, CHUNK_UNIT);
496 : 1 : chunksz_t heap_sz = (end - addr) / CHUNK_UNIT;
497 : :
498 : : CHECK(end > addr);
499 [ - + ]: 1 : __ASSERT(heap_sz > chunksz(sizeof(struct z_heap)), "heap size is too small");
500 : :
501 : 1 : struct z_heap *h = (struct z_heap *)addr;
502 : 1 : heap->heap = h;
503 : 1 : h->end_chunk = heap_sz;
504 : 1 : h->avail_buckets = 0;
505 : :
506 : : #ifdef CONFIG_SYS_HEAP_RUNTIME_STATS
507 : : h->free_bytes = 0;
508 : : h->allocated_bytes = 0;
509 : : h->max_allocated_bytes = 0;
510 : : #endif
511 : :
512 : 1 : int nb_buckets = bucket_idx(h, heap_sz) + 1;
513 : 1 : chunksz_t chunk0_size = chunksz(sizeof(struct z_heap) +
514 : : nb_buckets * sizeof(struct z_heap_bucket));
515 : :
516 [ - + ]: 1 : __ASSERT(chunk0_size + min_chunk_size(h) <= heap_sz, "heap size is too small");
517 : :
518 [ + + ]: 13 : for (int i = 0; i < nb_buckets; i++) {
519 : 12 : h->buckets[i].next = 0;
520 : : }
521 : :
522 : : /* chunk containing our struct z_heap */
523 : 1 : set_chunk_size(h, 0, chunk0_size);
524 : 1 : set_left_chunk_size(h, 0, 0);
525 : 1 : set_chunk_used(h, 0, true);
526 : :
527 : : /* chunk containing the free heap */
528 : 1 : set_chunk_size(h, chunk0_size, heap_sz - chunk0_size);
529 : 1 : set_left_chunk_size(h, chunk0_size, chunk0_size);
530 : :
531 : : /* the end marker chunk */
532 : 1 : set_chunk_size(h, heap_sz, 0);
533 : 1 : set_left_chunk_size(h, heap_sz, heap_sz - chunk0_size);
534 : 1 : set_chunk_used(h, heap_sz, true);
535 : :
536 : 1 : free_list_add(h, chunk0_size);
537 : 1 : }
|