Contiki-NG
Loading...
Searching...
No Matches
heapmem.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2005, Nicolas Tsiftes
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the author nor the names of the contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR
21 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
24 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
27 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31/**
32 * \file
33 * HeapMem: a dynamic memory allocation module for
34 * resource-constrained devices.
35 * \author
36 * Nicolas Tsiftes <nvt@acm.org>
37 */
38
39#include <inttypes.h>
40#include <stddef.h>
41#include <stdint.h>
42#include <string.h>
43
44#include "contiki.h"
45#include "lib/heapmem.h"
46#include "sys/cc.h"
47
48/* Log configuration */
49#include "sys/log.h"
50#define LOG_MODULE "HeapMem"
51#define LOG_LEVEL LOG_LEVEL_WARN
52
53/* The HEAPMEM_CONF_PRINTF parameter determines which function to use for
54 printing debug information. It defaults to LOG_OUTPUT. */
55#ifdef HEAPMEM_CONF_PRINTF
56#define HEAPMEM_PRINTF(...) HEAPMEM_CONF_PRINTF(__VA_ARGS__)
57#else
58#define HEAPMEM_PRINTF(...) LOG_OUTPUT(__VA_ARGS__)
59#endif /* HEAPMEM_CONF_PRINTF */
60
61/*
62 * The HEAPMEM_CONF_SEARCH_MAX parameter limits the time spent on
63 * chunk allocation and defragmentation. The lower this number is, the
64 * faster the operations become. The cost of this speedup, however, is
65 * that the space overhead might increase.
66 */
67#ifdef HEAPMEM_CONF_SEARCH_MAX
68#define CHUNK_SEARCH_MAX HEAPMEM_CONF_SEARCH_MAX
69#else
70#define CHUNK_SEARCH_MAX 16
71#endif /* HEAPMEM_CONF_SEARCH_MAX */
72
73_Static_assert((HEAPMEM_ALIGNMENT & (HEAPMEM_ALIGNMENT - 1)) == 0,
74 "HEAPMEM_ALIGNMENT must be a power of 2");
75
76static inline size_t
77align_size(size_t size)
78{
79 if(size > SIZE_MAX - (HEAPMEM_ALIGNMENT - 1)) {
80 return 0;
81 }
82 return ((size + (HEAPMEM_ALIGNMENT - 1)) & ~(HEAPMEM_ALIGNMENT - 1));
83}
84
85#define ALIGN(size) align_size(size)
86
87/* Macros for chunk iteration. */
88#define NEXT_CHUNK(chunk) \
89 ((chunk_t *)((char *)(chunk) + sizeof(chunk_t) + (chunk)->size))
90#define IS_LAST_CHUNK(zone, chunk) \
91 ((char *)NEXT_CHUNK(chunk) == (zone)->heap_base + (zone)->heap_usage)
92
93/* Macros for retrieving the data pointer from a chunk, and the other
94 way around. */
95#define GET_CHUNK(ptr) \
96 ((chunk_t *)((char *)(ptr) - sizeof(chunk_t)))
97#define GET_PTR(chunk) \
98 (char *)((chunk) + 1)
99
100/*
101 * We use a double-linked list of chunks, with a slight space overhead
102 * compared to a single-linked list, but with the advantage of having
103 * much faster list removals.
104 */
105typedef struct heapmem_chunk {
106 struct heapmem_chunk *prev;
107 struct heapmem_chunk *next;
108 size_t size;
109 bool allocated;
110#if HEAPMEM_DEBUG
111 const char *file;
112 unsigned line;
113#endif
114} chunk_t;
115
116_Static_assert(sizeof(chunk_t) % HEAPMEM_ALIGNMENT == 0,
117 "sizeof(chunk_t) must be a multiple of HEAPMEM_ALIGNMENT");
118
119/* The general zone with a statically allocated arena. */
120#ifdef HEAPMEM_CONF_ARENA_SIZE
121#define HEAPMEM_ARENA_SIZE HEAPMEM_CONF_ARENA_SIZE
122static char heapmem_general_buf_[HEAPMEM_ARENA_SIZE] CC_ALIGN(HEAPMEM_ALIGNMENT);
123static heapmem_zone_t heapmem_zone_general = {
124 .name = "GENERAL",
125 .heap_base = heapmem_general_buf_,
126 .arena_size = HEAPMEM_ARENA_SIZE,
127};
128#endif /* HEAPMEM_CONF_ARENA_SIZE */
129
130/*
131 * resolve_zone: Map a NULL zone pointer to the general zone (when
132 * available). This allows convenience macros to pass NULL instead of
133 * exposing the general zone as a global variable.
134 */
135static inline heapmem_zone_t *
136resolve_zone(heapmem_zone_t *zone)
137{
138#ifdef HEAPMEM_CONF_ARENA_SIZE
139 if(zone == NULL) {
140 return &heapmem_zone_general;
141 }
142#endif
143 return zone;
144}
145
146#define IN_ZONE(zone, ptr) ((ptr) != NULL && \
147 (char *)(ptr) >= (zone)->heap_base + sizeof(chunk_t) && \
148 (char *)(ptr) < (zone)->heap_base + (zone)->heap_usage)
149
150/* extend_space: Increases the current footprint used in the zone's heap, and
151 returns a pointer to the old end. */
152static void *
153extend_space(heapmem_zone_t *zone, size_t size)
154{
155 if(size > zone->arena_size - zone->heap_usage) {
156 return NULL;
157 }
158
159 char *old_usage = zone->heap_base + zone->heap_usage;
160 zone->heap_usage += size;
161 if(zone->heap_usage > zone->max_heap_usage) {
162 zone->max_heap_usage = zone->heap_usage;
163 }
164
165 return old_usage;
166}
167
168/* free_chunk: Mark a chunk as being free, and put it on the free list. */
169static void
170free_chunk(heapmem_zone_t *zone, chunk_t * const chunk)
171{
172 chunk->allocated = false;
173
174 if(IS_LAST_CHUNK(zone, chunk)) {
175 /* Release the chunk back into the wilderness. */
176 zone->heap_usage -= sizeof(chunk_t) + chunk->size;
177 } else {
178 /* Put the chunk on the free list. */
179 chunk->prev = NULL;
180 chunk->next = zone->free_list;
181 if(zone->free_list != NULL) {
182 zone->free_list->prev = chunk;
183 }
184 zone->free_list = chunk;
185 }
186}
187
188/* remove_chunk_from_free_list: Mark a chunk as being allocated, and
189 remove it from the free list. */
190static void
191remove_chunk_from_free_list(heapmem_zone_t *zone, chunk_t * const chunk)
192{
193 if(chunk == zone->free_list) {
194 zone->free_list = chunk->next;
195 if(zone->free_list != NULL) {
196 zone->free_list->prev = NULL;
197 }
198 } else {
199 chunk->prev->next = chunk->next;
200 }
201
202 if(chunk->next != NULL) {
203 chunk->next->prev = chunk->prev;
204 }
205}
206
207/*
208 * split_chunk: When allocating a chunk, we may have found one that is
209 * larger than needed, so this function is called to keep the rest of
210 * the original chunk free.
211 */
212static void
213split_chunk(heapmem_zone_t *zone, chunk_t * const chunk, size_t offset)
214{
215 offset = ALIGN(offset);
216
217 if(offset + sizeof(chunk_t) < chunk->size) {
218 chunk_t *new_chunk = (chunk_t *)(GET_PTR(chunk) + offset);
219 new_chunk->size = chunk->size - sizeof(chunk_t) - offset;
220 new_chunk->allocated = false;
221 free_chunk(zone, new_chunk);
222
223 chunk->size = offset;
224 chunk->next = chunk->prev = NULL;
225 }
226}
227
228/* coalesce_chunks: Coalesce a specific free chunk with as many
229 adjacent free chunks as possible. */
230static void
231coalesce_chunks(heapmem_zone_t *zone, chunk_t *chunk)
232{
233 for(chunk_t *next = NEXT_CHUNK(chunk);
234 (char *)next < zone->heap_base + zone->heap_usage && !next->allocated;
235 next = NEXT_CHUNK(next)) {
236 chunk->size += sizeof(chunk_t) + next->size;
237 LOG_DBG("Coalesce chunk of %zu bytes\n", next->size);
238 remove_chunk_from_free_list(zone, next);
239 }
240}
241
242/* defrag_chunks: Scan the free list for chunks that can be coalesced,
243 and stop within a bounded time. */
244static void
245defrag_chunks(heapmem_zone_t *zone)
246{
247 /* Limit the time we spend on searching the free list. */
248 int i = CHUNK_SEARCH_MAX;
249 for(chunk_t *chunk = zone->free_list; chunk != NULL; chunk = chunk->next) {
250 if(i-- == 0) {
251 break;
252 }
253 coalesce_chunks(zone, chunk);
254 }
255}
256
257/* get_free_chunk: Search the free list for the most suitable chunk,
258 as determined by its size, to satisfy an allocation request. */
259static chunk_t *
260get_free_chunk(heapmem_zone_t *zone, const size_t size)
261{
262 /* Defragment chunks only right before they are needed for allocation. */
263 defrag_chunks(zone);
264
265 chunk_t *best = NULL;
266 /* Limit the time we spend on searching the free list. */
267 int i = CHUNK_SEARCH_MAX;
268 for(chunk_t *chunk = zone->free_list; chunk != NULL; chunk = chunk->next) {
269 if(i-- == 0) {
270 break;
271 }
272
273 /* To avoid fragmenting large chunks, we select the chunk with the
274 smallest size that is larger than or equal to the requested size. */
275 if(size <= chunk->size) {
276 if(best == NULL || chunk->size < best->size) {
277 best = chunk;
278 }
279 if(best->size == size) {
280 /* We found a perfect chunk -- stop the search. */
281 break;
282 }
283 }
284 }
285
286 if(best != NULL) {
287 /* We found a chunk that can hold an object of the requested
288 allocation size. Split it if possible. */
289 remove_chunk_from_free_list(zone, best);
290 split_chunk(zone, best, size);
291 }
292
293 return best;
294}
295
296/*
297 * heapmem_zone_alloc: Allocate an object of the specified size from the
298 * given zone, returning a pointer to it in case of success, and NULL
299 * in case of failure.
300 *
301 * When allocating memory, heapmem_zone_alloc() will first try to find a
302 * free chunk of the same size as the requested one. If none can be
303 * found, we pick a larger chunk that is as close in size as possible,
304 * and possibly split it so that the remaining part becomes a chunk
305 * available for allocation. At most CHUNK_SEARCH_MAX chunks on the
306 * free list will be examined.
307 *
308 * As a last resort, heapmem_zone_alloc() will try to extend the heap
309 * space, and thereby create a new chunk available for use.
310 */
311static void *
312zone_alloc(heapmem_zone_t *zone, size_t size,
313 const char *file, const unsigned line)
314{
315 zone = resolve_zone(zone);
316 if(zone == NULL || zone->heap_base == NULL) {
317 LOG_WARN("Attempt to allocate from invalid zone\n");
318 return NULL;
319 }
320
321 if(size > zone->arena_size || size == 0) {
322 return NULL;
323 }
324
325 size = ALIGN(size);
326 if(size == 0) {
327 LOG_ERR("Size overflow in alignment\n");
328 return NULL;
329 }
330
331 chunk_t *chunk = get_free_chunk(zone, size);
332 if(chunk == NULL) {
333 chunk = extend_space(zone, sizeof(chunk_t) + size);
334 if(chunk == NULL) {
335 return NULL;
336 }
337 chunk->size = size;
338 }
339
340 chunk->allocated = true;
341
342#if HEAPMEM_DEBUG
343 chunk->file = file;
344 chunk->line = line;
345#endif
346
347 LOG_DBG("zone_alloc: zone \"%s\" ptr %p size %zu\n",
348 zone->name, GET_PTR(chunk), chunk->size);
349
350 return GET_PTR(chunk);
351}
352
353#if HEAPMEM_DEBUG
354void *
355heapmem_zone_alloc_debug(heapmem_zone_t *zone, size_t size,
356 const char *file, const unsigned line)
357{
358 return zone_alloc(zone, size, file, line);
359}
360#else
361void *
362heapmem_zone_alloc(heapmem_zone_t *zone, size_t size)
363{
364 return zone_alloc(zone, size, NULL, 0);
365}
366#endif
367
368/*
369 * heapmem_zone_free: Deallocate a previously allocated object.
370 *
371 * The pointer must exactly match one returned from an earlier call
372 * from heapmem_zone_alloc or heapmem_zone_realloc, without any call to
373 * heapmem_zone_free in between.
374 *
375 * When deallocating a chunk, the chunk will be inserted into the free
376 * list. Moreover, all free chunks that are adjacent in memory will be
377 * merged into a single chunk in order to mitigate fragmentation.
378 */
379static bool
380zone_free(heapmem_zone_t *zone, void *ptr,
381 const char *file, const unsigned line)
382{
383 zone = resolve_zone(zone);
384 if(zone == NULL || !IN_ZONE(zone, ptr)) {
385 if(ptr) {
386 LOG_WARN("zone_free: ptr %p is not in the zone\n", ptr);
387 }
388 return false;
389 }
390
391 chunk_t *chunk = GET_CHUNK(ptr);
392 if(!chunk->allocated) {
393 LOG_WARN("zone_free: ptr %p has already been deallocated\n", ptr);
394 return false;
395 }
396
397#if HEAPMEM_DEBUG
398 LOG_DBG("zone_free: ptr %p, allocated at %s:%u\n", ptr,
399 chunk->file, chunk->line);
400#endif
401
402 free_chunk(zone, chunk);
403 return true;
404}
405
406#if HEAPMEM_DEBUG
407bool
408heapmem_zone_free_debug(heapmem_zone_t *zone, void *ptr,
409 const char *file, const unsigned line)
410{
411 return zone_free(zone, ptr, file, line);
412}
413#else
414bool
415heapmem_zone_free(heapmem_zone_t *zone, void *ptr)
416{
417 return zone_free(zone, ptr, NULL, 0);
418}
419#endif
420
421/*
422 * heapmem_zone_realloc: Reallocate an object with a different size,
423 * possibly moving it in memory. In case of success, the function
424 * returns a pointer to the object's new location. In case of failure,
425 * it returns NULL.
426 *
427 * If the size of the new chunk is larger than that of the allocated
428 * chunk, heapmem_zone_realloc() will first attempt to extend the
429 * currently allocated chunk. If the adjacent memory is not free,
430 * heapmem_zone_realloc() will attempt to allocate a completely new
431 * chunk, copy the old data to the new chunk, and deallocate the old
432 * chunk.
433 *
434 * If the size of the new chunk is smaller than the allocated one, we
435 * split the allocated chunk if the remaining chunk would be large
436 * enough to justify the overhead of creating a new chunk.
437 */
438static void *
439zone_realloc(heapmem_zone_t *zone, void *ptr, size_t size,
440 const char *file, const unsigned line)
441{
442 zone = resolve_zone(zone);
443
444 /* Allow the special case of ptr being NULL as an alias
445 for heapmem_zone_alloc(). */
446 if(zone == NULL) {
447 LOG_WARN("Attempt to use invalid zone\n");
448 return NULL;
449 }
450 if(ptr != NULL && !IN_ZONE(zone, ptr)) {
451 LOG_WARN("zone_realloc: ptr %p is not in the zone\n", ptr);
452 return NULL;
453 }
454
455#if HEAPMEM_DEBUG
456 LOG_DBG("zone_realloc: ptr %p size %zu at %s:%u\n",
457 ptr, size, file, line);
458#endif
459
460 /* Fail early on too large allocation requests to prevent wrapping values. */
461 if(size > zone->arena_size) {
462 return NULL;
463 }
464
465 /* Special cases in which we can hand off the execution to other functions. */
466 if(ptr == NULL) {
467 return zone_alloc(zone, size, file, line);
468 } else if(size == 0) {
469 zone_free(zone, ptr, file, line);
470 return NULL;
471 }
472
473 chunk_t *chunk = GET_CHUNK(ptr);
474 if(!chunk->allocated) {
475 LOG_WARN("zone_realloc: ptr %p is not allocated\n", ptr);
476 return NULL;
477 }
478
479#if HEAPMEM_DEBUG
480 chunk->file = file;
481 chunk->line = line;
482#endif
483
484 size = ALIGN(size);
485 if(size == 0) {
486 LOG_ERR("Size overflow in alignment\n");
487 return NULL;
488 }
489
490 size_t old_size = chunk->size;
491
492 if(size <= old_size) {
493 /* Request to make the object smaller or to keep its size.
494 In the former case, the chunk will be split if possible. */
495 split_chunk(zone, chunk, size);
496 return ptr;
497 }
498
499 /* Request to make the object larger. */
500 size_t size_increase = size - old_size;
501
502 if(IS_LAST_CHUNK(zone, chunk)) {
503 /*
504 * If the object belongs to the last allocated chunk (i.e., the
505 * one before the end of the heap footprint, we just attempt to
506 * extend the heap.
507 */
508 if(extend_space(zone, size_increase) != NULL) {
509 chunk->size = size;
510 return ptr;
511 }
512 } else {
513 /*
514 * Here we attempt to enlarge an allocated object, whose
515 * adjacent space may already be allocated. We attempt to
516 * coalesce chunks in order to make as much room as possible.
517 */
518 coalesce_chunks(zone, chunk);
519 if(chunk->size >= size) {
520 /* There was enough free adjacent space to extend the chunk in
521 its current place. */
522 split_chunk(zone, chunk, size);
523 return ptr;
524 }
525 }
526
527 /*
528 * Failed to enlarge the object in its current place, since the
529 * adjacent chunk is allocated. Hence, we try to place the new
530 * object elsewhere in the heap, and remove the old chunk that was
531 * holding it.
532 */
533 void *newptr = zone_alloc(zone, size, file, line);
534 if(newptr == NULL) {
535 return NULL;
536 }
537
538 memcpy(newptr, ptr, old_size);
539 zone_free(zone, ptr, file, line);
540
541 return newptr;
542}
543
544#if HEAPMEM_DEBUG
545void *
546heapmem_zone_realloc_debug(heapmem_zone_t *zone, void *ptr, size_t size,
547 const char *file, const unsigned line)
548{
549 return zone_realloc(zone, ptr, size, file, line);
550}
551#else
552void *
553heapmem_zone_realloc(heapmem_zone_t *zone, void *ptr, size_t size)
554{
555 return zone_realloc(zone, ptr, size, NULL, 0);
556}
557#endif
558
559static void *
560zone_calloc(heapmem_zone_t *zone, size_t nmemb, size_t size,
561 const char *file, const unsigned line)
562{
563 size_t total_size = nmemb * size;
564
565 /* Overflow check. */
566 if(size == 0 || total_size / size != nmemb) {
567 return NULL;
568 }
569
570 void *ptr = zone_alloc(zone, total_size, file, line);
571 if(ptr != NULL) {
572 memset(ptr, 0, total_size);
573 }
574 return ptr;
575}
576
577#if HEAPMEM_DEBUG
578void *
579heapmem_zone_calloc_debug(heapmem_zone_t *zone, size_t nmemb, size_t size,
580 const char *file, const unsigned line)
581{
582 return zone_calloc(zone, nmemb, size, file, line);
583}
584#else
585void *
586heapmem_zone_calloc(heapmem_zone_t *zone, size_t nmemb, size_t size)
587{
588 return zone_calloc(zone, nmemb, size, NULL, 0);
589}
590#endif
591
592/* heapmem_zone_stats: Provides statistics regarding zone memory usage. */
593void
594heapmem_zone_stats(heapmem_zone_t *zone, heapmem_stats_t *stats)
595{
596 zone = resolve_zone(zone);
597 memset(stats, 0, sizeof(*stats));
598 if(zone == NULL) {
599 return;
600 }
601
602 for(chunk_t *chunk = (chunk_t *)zone->heap_base;
603 (char *)chunk < zone->heap_base + zone->heap_usage;
604 chunk = NEXT_CHUNK(chunk)) {
605 stats->overhead += sizeof(chunk_t);
606 if(chunk->allocated) {
607 stats->allocated += chunk->size;
608 stats->chunks++;
609 } else {
610 coalesce_chunks(zone, chunk);
611 stats->available += chunk->size;
612 }
613 }
614 stats->available += zone->arena_size - zone->heap_usage;
615 stats->heap_usage = zone->heap_usage;
616 stats->max_heap_usage = zone->max_heap_usage;
617}
618
619/* heapmem_zone_print_debug_info: Print statistics and optionally
620 chunk details for a specific zone. */
621void
622heapmem_zone_print_debug_info(heapmem_zone_t *zone, bool print_chunks)
623{
624 zone = resolve_zone(zone);
625 if(zone == NULL) {
626 return;
627 }
628
629 heapmem_stats_t stats;
630 heapmem_zone_stats(zone, &stats);
631
632 HEAPMEM_PRINTF("* HeapMem zone \"%s\" statistics\n", zone->name);
633 HEAPMEM_PRINTF("* Arena size: %zu\n", zone->arena_size);
634 HEAPMEM_PRINTF("* Allocated memory: %zu\n", stats.allocated);
635 HEAPMEM_PRINTF("* Available memory: %zu\n", stats.available);
636 HEAPMEM_PRINTF("* Heap usage: %zu\n", stats.heap_usage);
637 HEAPMEM_PRINTF("* Max heap usage: %zu\n", stats.max_heap_usage);
638 HEAPMEM_PRINTF("* Allocated chunks: %zu\n", stats.chunks);
639 HEAPMEM_PRINTF("* Chunk size: %zu\n", sizeof(chunk_t));
640 HEAPMEM_PRINTF("* Total chunk overhead: %zu\n", stats.overhead);
641
642 if(print_chunks) {
643 HEAPMEM_PRINTF("* Allocated chunks:\n");
644 for(chunk_t *chunk = (chunk_t *)zone->heap_base;
645 (char *)chunk < zone->heap_base + zone->heap_usage;
646 chunk = NEXT_CHUNK(chunk)) {
647 if(chunk->allocated) {
648#if HEAPMEM_DEBUG
649 HEAPMEM_PRINTF("* Chunk: heap offset %"PRIuPTR", obj %p, size %zu (%s:%u)\n",
650 (uintptr_t)((char *)chunk - zone->heap_base),
651 GET_PTR(chunk), chunk->size, chunk->file, chunk->line);
652#else
653 HEAPMEM_PRINTF("* Chunk: heap offset %"PRIuPTR", obj %p, size %zu\n",
654 (uintptr_t)((char *)chunk - zone->heap_base),
655 GET_PTR(chunk), chunk->size);
656#endif /* HEAPMEM_DEBUG */
657 }
658 }
659 }
660}
661
Default definitions of C compiler quirk work-arounds.
void * heapmem_zone_alloc(heapmem_zone_t *zone, size_t size)
Allocate a chunk of memory in the specified zone.
Definition heapmem.c:362
void heapmem_zone_stats(heapmem_zone_t *zone, heapmem_stats_t *stats)
Obtain internal statistics for a heapmem zone.
Definition heapmem.c:594
void * heapmem_zone_realloc(heapmem_zone_t *zone, void *ptr, size_t size)
Reallocate a chunk of memory in the specified zone.
Definition heapmem.c:553
void * heapmem_zone_calloc(heapmem_zone_t *zone, size_t nmemb, size_t size)
Allocate memory for a zero-initialized array in the specified zone.
Definition heapmem.c:586
void heapmem_zone_print_debug_info(heapmem_zone_t *zone, bool print_chunks)
Print debugging information for a heapmem zone.
Definition heapmem.c:622
bool heapmem_zone_free(heapmem_zone_t *zone, void *ptr)
Deallocate a chunk of memory in the specified zone.
Definition heapmem.c:415
Header file for the dynamic heap memory allocator.
Header file for the logging system.