11#include "exceptions.h"
13#include "os_helpers.h"
23#define PAYLOAD_ALIGNEMENT 4
25#define PAYLOAD_DATA_ALIGNEMENT 8
27#define ALLOC_CHUNK_HEADER_SIZE 4
29#define FREE_CHUNK_HEADER_SIZE 8
31#define HEAP_HEADER_SIZE \
32 ((sizeof(heap_t) + PAYLOAD_DATA_ALIGNEMENT - 1) & ~(PAYLOAD_DATA_ALIGNEMENT - 1))
35#define NB_LINEAR_SEGMENTS 5
48#define NB_MAX_SEGMENTS 10
50#define NB_SUB_SEGMENTS 4
53#define GET_PTR(_heap, index) ((header_t *) (((uint8_t *) _heap) + ((index) << 3)))
54#define GET_IDX(_heap, _ptr) ((((uint8_t *) (_ptr)) - ((uint8_t *) _heap)) >> 3)
55#define GET_PREV(_heap, _ptr) ((header_t *) (((uint8_t *) _heap) + ((_ptr)->fprev << 3)))
56#define GET_NEXT(_heap, _ptr) ((header_t *) (((uint8_t *) _heap) + ((_ptr)->fnext << 3)))
58#define GET_SEGMENT(_size) MAX(NB_LINEAR_SEGMENTS, (31 - __builtin_clz(size)))
90static inline size_t align_alloc_size(
size_t size)
107static inline int seglist_index(
heap_t *heap,
size_t size)
116 sub_segment = (size >> (seg - 1)) & 0x3;
120 sub_segment = (size >> (seg - 2)) & 0x3;
134static inline int seglist_index_up(
heap_t *heap,
size_t size)
138 size_t sub_segment_size;
144 sub_segment_size = (1 << (seg - 1));
148 sub_segment_size = (1 << (seg - 2));
152 size += sub_segment_size - 1;
153 size &= ~(sub_segment_size - 1);
159 sub_segment = (size >> (seg - 1)) & 0x3;
163 sub_segment = (size >> (seg - 2)) & 0x3;
178static inline void list_remove(
heap_t *heap, uint16_t *first_free, uint16_t elem)
182 if (*first_free == elem) {
183 *first_free = elem_ptr->
fnext;
186 GET_PTR(heap, *first_free)->fprev = 0;
191 if (elem_ptr->
fprev) {
195 if (elem_ptr->
fnext) {
203 int seg_index = seglist_index(heap, header->
size);
209 if (first_idx ==
GET_IDX(heap, header)) {
213 header->
fnext = first_idx;
217 if (first_idx != 0) {
226static inline void ensure_chunk_valid(
heap_t *heap,
header_t *header)
228 uint8_t *block = (uint8_t *) header;
231 || ((block + header->
size) > (uint8_t *) heap->
end)) {
232 PRINTF(
"invalid size: header->size = %d!\n", header->
size);
233 THROW(EXCEPTION_CORRUPT);
238 block = (uint8_t *) heap;
240 PRINTF(
"corrupted prev_chunk\n");
241 THROW(EXCEPTION_CORRUPT);
244 if ((((uint8_t *) prev) + prev->size) != (uint8_t *) header) {
245 PRINTF(
"corrupted prev_chunk\n");
246 THROW(EXCEPTION_CORRUPT);
254 uint16_t neighbour_idx =
GET_IDX(heap, neighbour);
255 ensure_chunk_valid(heap, neighbour);
261 heap, &heap->
free_segments[seglist_index(heap, neighbour->
size)], neighbour_idx);
264 if (header < neighbour) {
265 next = (
header_t *) (((uint8_t *) neighbour) + neighbour->size);
266 if ((
void *) next < heap->
end) {
269 header->
size += neighbour->size;
271 memset(neighbour, 0,
sizeof(*neighbour));
274 next = (
header_t *) (((uint8_t *) header) + header->size);
276 if ((
void *) next < heap->
end) {
279 neighbour->size += header->size;
281 memset(header, 0,
sizeof(*header));
289static bool parse_callback(
void *data, uint8_t *addr,
bool allocated,
size_t size)
328 if (heap_start == NULL) {
338 heap->
end = ((uint8_t *) heap_start) + heap_size;
351 list_push(heap, first_free);
376 nb_bytes = align_alloc_size(nb_bytes);
381 seg = seglist_index_up(heap, nb_bytes);
389 if (chunk_idx != 0) {
393 ensure_chunk_valid(heap, header);
395 if (nb_bytes > header->
size) {
399 uint8_t *block = (uint8_t *) header;
407 new_free->
size = header->
size - nb_bytes;
412 if ((
void *) next < heap->end) {
415 list_push(heap, new_free);
417 header->
size = nb_bytes;
458 PRINTF(
"invalid pointer passed to realloc: 0x%p!\n", ptr);
473 ensure_chunk_valid(heap, header);
476 size_t old_block_size = header->
size;
480 size_t new_block_size = align_alloc_size(size);
483 if (old_payload_size >= size) {
486 size_t remainder = old_block_size - new_block_size;
493 new_free->
size = remainder;
497 if ((
void *) next < heap->end) {
500 list_push(heap, new_free);
502 header->
size = new_block_size;
510 if (new_ptr == NULL) {
517 memcpy(new_ptr, ptr, old_payload_size);
539 ensure_chunk_valid(heap, header);
546 if ((block + header->
size) < (uint8_t *) heap->
end) {
547 header = coalesce(heap, header, (
header_t *) (block + header->
size));
555 list_push(heap, header);
569#ifdef DEBUG_FREE_SEGMENTS
570 for (
size_t i = 0; i < heap->
nb_segs; i++) {
581 while (header != NULL) {
582 uint8_t *block = (uint8_t *) header;
584 if (callback(data, block, header->
allocated, header->
size)) {
587 if (&block[header->
size] < (uint8_t *) heap->
end) {
#define GET_NEXT(_heap, _ptr)
#define GET_PTR(_heap, index)
#define ALLOC_CHUNK_HEADER_SIZE
void * mem_realloc(mem_ctx_t ctx, void *ptr, size_t size)
Reallocates a buffer to a new size.
#define PAYLOAD_ALIGNEMENT
#define GET_SEGMENT(_size)
mem_ctx_t mem_init(void *heap_start, size_t heap_size)
initializes the heap for this allocator
#define NB_LINEAR_SEGMENTS
void * mem_alloc(mem_ctx_t ctx, size_t nb_bytes)
allocates a buffer of the given size, if possible
#define FREE_CHUNK_HEADER_SIZE
void mem_free(mem_ctx_t ctx, void *ptr)
frees the given buffer
void mem_stat(mem_ctx_t *ctx, mem_stat_t *stat)
function used to get statistics (nb chunks, nb allocated chunks, total size) about the heap
void mem_parse(mem_ctx_t ctx, mem_parse_callback_t callback, void *data)
parse the heap
#define GET_PREV(_heap, _ptr)
#define GET_IDX(_heap, _ptr)
Dynamic memory allocation API.
void * mem_ctx_t
type shared externally
bool(* mem_parse_callback_t)(void *data, uint8_t *addr, bool allocated, size_t size)
function called for each chunk found in the heap.
void * end
end of headp buffer, for consistency check
size_t nb_segs
actual number of used segments
uint16_t free_segments[NB_MAX_SEGMENTS *NB_SUB_SEGMENTS]
this structure, filled by mem_stat
size_t allocated_size
nb bytes allocated in the heap (including headers)
size_t total_size
total size of the heap (including allocator internal data)
uint32_t nb_allocated
number of allocated chunks
uint32_t nb_chunks
total number of chunks