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 MAX_BLOCK_SIZE 0x7FFF
33#define HEAP_HEADER_SIZE \
34 ((sizeof(heap_t) + PAYLOAD_DATA_ALIGNEMENT - 1) & ~(PAYLOAD_DATA_ALIGNEMENT - 1))
37#define NB_LINEAR_SEGMENTS 5
50#define NB_MAX_SEGMENTS 10
52#define NB_SUB_SEGMENTS 4
55#define GET_PTR(_heap, index) ((header_t *) (((uint8_t *) _heap) + ((index) << 3)))
56#define GET_IDX(_heap, _ptr) ((((uint8_t *) (_ptr)) - ((uint8_t *) _heap)) >> 3)
57#define GET_PREV(_heap, _ptr) ((header_t *) (((uint8_t *) _heap) + ((_ptr)->fprev << 3)))
58#define GET_NEXT(_heap, _ptr) ((header_t *) (((uint8_t *) _heap) + ((_ptr)->fnext << 3)))
60#define GET_SEGMENT(_size) MAX(NB_LINEAR_SEGMENTS, (31 - __builtin_clz(size)))
92static inline size_t align_alloc_size(
size_t size)
109static inline int seglist_index(
heap_t *heap,
size_t size)
118 sub_segment = (size >> (seg - 1)) & 0x3;
122 sub_segment = (size >> (seg - 2)) & 0x3;
136static inline int seglist_index_up(
heap_t *heap,
size_t size)
140 size_t sub_segment_size;
146 sub_segment_size = (1 << (seg - 1));
150 sub_segment_size = (1 << (seg - 2));
154 size += sub_segment_size - 1;
155 size &= ~(sub_segment_size - 1);
161 sub_segment = (size >> (seg - 1)) & 0x3;
165 sub_segment = (size >> (seg - 2)) & 0x3;
180static inline void list_remove(
heap_t *heap, uint16_t *first_free, uint16_t elem)
184 if (*first_free == elem) {
185 *first_free = elem_ptr->
fnext;
188 GET_PTR(heap, *first_free)->fprev = 0;
193 if (elem_ptr->
fprev) {
197 if (elem_ptr->
fnext) {
205 int seg_index = seglist_index(heap, header->
size);
211 if (first_idx ==
GET_IDX(heap, header)) {
215 header->
fnext = first_idx;
219 if (first_idx != 0) {
228static inline void ensure_chunk_valid(
heap_t *heap,
header_t *header)
230 uint8_t *block = (uint8_t *) header;
233 || ((block + header->
size) > (uint8_t *) heap->
end)) {
234 PRINTF(
"invalid size: header->size = %d!\n", header->
size);
235 THROW(EXCEPTION_CORRUPT);
240 block = (uint8_t *) heap;
242 PRINTF(
"corrupted prev_chunk\n");
243 THROW(EXCEPTION_CORRUPT);
246 if ((((uint8_t *) prev) + prev->size) != (uint8_t *) header) {
247 PRINTF(
"corrupted prev_chunk\n");
248 THROW(EXCEPTION_CORRUPT);
256 uint16_t neighbour_idx =
GET_IDX(heap, neighbour);
257 ensure_chunk_valid(heap, neighbour);
263 heap, &heap->
free_segments[seglist_index(heap, neighbour->
size)], neighbour_idx);
266 if (header < neighbour) {
267 next = (
header_t *) (((uint8_t *) neighbour) + neighbour->size);
268 if ((
void *) next < heap->
end) {
271 header->
size += neighbour->size;
273 memset(neighbour, 0,
sizeof(*neighbour));
276 next = (
header_t *) (((uint8_t *) header) + header->size);
278 if ((
void *) next < heap->
end) {
281 neighbour->size += header->size;
283 memset(header, 0,
sizeof(*header));
291static bool parse_callback(
void *data, uint8_t *addr,
bool allocated,
size_t size)
330 if (heap_start == NULL) {
340 heap->
end = ((uint8_t *) heap_start) + heap_size;
353 list_push(heap, first_free);
385 nb_bytes = align_alloc_size(nb_bytes);
390 seg = seglist_index_up(heap, nb_bytes);
398 if (chunk_idx != 0) {
402 ensure_chunk_valid(heap, header);
404 if (nb_bytes > header->
size) {
408 uint8_t *block = (uint8_t *) header;
416 new_free->
size = header->
size - nb_bytes;
421 if ((
void *) next < heap->end) {
424 list_push(heap, new_free);
426 header->
size = nb_bytes;
467 PRINTF(
"invalid pointer passed to realloc: 0x%p!\n", ptr);
490 ensure_chunk_valid(heap, header);
493 size_t old_block_size = header->
size;
497 size_t new_block_size = align_alloc_size(size);
500 if (old_payload_size >= size) {
503 size_t remainder = old_block_size - new_block_size;
510 new_free->
size = remainder;
514 if ((
void *) next < heap->end) {
517 list_push(heap, new_free);
519 header->
size = new_block_size;
527 if (new_ptr == NULL) {
534 memcpy(new_ptr, ptr, old_payload_size);
556 ensure_chunk_valid(heap, header);
563 if ((block + header->
size) < (uint8_t *) heap->
end) {
564 header = coalesce(heap, header, (
header_t *) (block + header->
size));
572 list_push(heap, header);
586#ifdef DEBUG_FREE_SEGMENTS
587 for (
size_t i = 0; i < heap->
nb_segs; i++) {
598 while (header != NULL) {
599 uint8_t *block = (uint8_t *) header;
601 if (callback(data, block, header->
allocated, header->
size)) {
604 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