From 055338b1e9e9bcb531a0f85d467127ad3bc3e11c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20O=2E=20Sel=C3=A5sdal?= Date: Wed, 8 Apr 2026 23:26:28 +0200 Subject: [PATCH] Cleanups --- src/small_allocator.c | 71 +++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/src/small_allocator.c b/src/small_allocator.c index 695c813..e67ab11 100644 --- a/src/small_allocator.c +++ b/src/small_allocator.c @@ -2,7 +2,18 @@ #include #include #include - +#include +/** + * General purpose allocator. + * - Allocated pointers are aligned to 16 bytes. + * - An allocation "wastes" 32 bytes for housekeeping + * - Aallocation of > 0 bytes returns 16 bytes as a minimum + * - Allocation of 0 bytes returns a valid pointer with 0 usable space + * + * The allocator maintains a doubly linked list of blocks, + * when blocks are free'd, adjacent blocks are merged to keep + * fragmentation to a minimum + */ #define ALIGNMENT 16 #define ALIGN(n) (((n) + (ALIGNMENT - 1)) & ~(ALIGNMENT - 1)) @@ -12,8 +23,8 @@ typedef struct BlockHeader { size_t size_and_flags; // size | free_bit struct BlockHeader* next; struct BlockHeader* prev; - // Padding to ensure we always return 16 byte aligned ponters - // remove padding & set ALIGNMENT to 8 to get 8 byte alignmet + // Padding to ensure we always return 16 byte aligned pointers. + // Remove padding & set ALIGNMENT to 8 to get 8 byte alignmet // (for 64 bit systems, 32 bit systems might need other adjustments) uint8_t pad[32 - (sizeof(size_t) + 2*sizeof(void*))]; } BlockHeader; @@ -32,30 +43,37 @@ typedef struct { /* --- Header helpers --- */ -static inline size_t block_size(BlockHeader* b) { +static inline size_t block_size(BlockHeader* b) +{ return b->size_and_flags & ~BLOCK_FREE; } -static inline int block_is_free(BlockHeader* b) { +static inline int block_is_free(BlockHeader* b) +{ return (b->size_and_flags & BLOCK_FREE) != 0; } -static inline void block_set_free(BlockHeader* b) { +static inline void block_set_free(BlockHeader* b) +{ b->size_and_flags |= BLOCK_FREE; } -static inline void block_set_used(BlockHeader* b) { +static inline void block_set_used(BlockHeader* b) +{ b->size_and_flags &= ~BLOCK_FREE; } -static inline void block_set_size(BlockHeader* b, size_t size) { +static inline void block_set_size(BlockHeader* b, size_t size) +{ size_t flags = b->size_and_flags & BLOCK_FREE; b->size_and_flags = size | flags; } -/* --- Allocator initialization --- */ +void small_allocator_init(SmallAllocator* a, void* backing_store, size_t size) +{ + // backing store must be aligned, otherwise all alignment guarantees are lies + assert(((uintptr_t)backing_store & (ALIGNMENT - 1)) == 0); -void small_allocator_init(SmallAllocator* a, void* backing_store, size_t size) { a->base = (uint8_t*)backing_store; a->size = size; @@ -70,9 +88,8 @@ void small_allocator_init(SmallAllocator* a, void* backing_store, size_t size) { a->last_alloc = first; } -/* Splitting */ - -static void split_block(BlockHeader* block, size_t size) { +static void split_block(BlockHeader* block, size_t size) +{ size = ALIGN(size); size_t bsize = block_size(block); @@ -98,9 +115,8 @@ static void split_block(BlockHeader* block, size_t size) { block->next = new_block; } -/* Coalescing */ - -static void merge_with_next(BlockHeader* block) { +static void merge_with_next(BlockHeader* block) +{ BlockHeader* next = block->next; if (!next || !block_is_free(next)) return; @@ -116,12 +132,8 @@ static void merge_with_next(BlockHeader* block) { next->next->prev = block; } -/* Allocation (next-fit) */ - -void* small_alloc(SmallAllocator* a, size_t size) { - if (size == 0) - return NULL; - +void* small_alloc(SmallAllocator* a, size_t size) +{ size = ALIGN(size); BlockHeader* start = a->last_alloc ? a->last_alloc : a->heap_start; @@ -142,15 +154,20 @@ void* small_alloc(SmallAllocator* a, size_t size) { /* Free */ -void small_free(SmallAllocator* a, void* ptr) { - if (!ptr) +void small_free(SmallAllocator* a, void* ptr) +{ + if (!ptr) { return; + } - BlockHeader* block = - (BlockHeader*)((uint8_t*)ptr - sizeof(BlockHeader)); + + BlockHeader* block = (BlockHeader*)((uint8_t*)ptr - sizeof(BlockHeader)); + + assert((uint8_t*)ptr >= a->base && (uint8_t*)ptr < a->base + a->size); + // double free detection + assert(!block_is_free(block)); block_set_free(block); - merge_with_next(block); if (block->prev && block_is_free(block->prev)) {