Use doubly linked list for O(1) free()

This commit is contained in:
Nils O. Selåsdal
2026-04-08 22:26:33 +02:00
parent 88e9f95988
commit 271c2a611f
+24 -23
View File
@@ -11,12 +11,14 @@
typedef struct BlockHeader { typedef struct BlockHeader {
size_t size_and_flags; // size | free_bit size_t size_and_flags; // size | free_bit
struct BlockHeader* next; 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
// (for 64 bit systems, 32 bit systems might need other adjustments)
uint8_t pad[32 - (sizeof(size_t) + 2*sizeof(void*))];
} BlockHeader; } BlockHeader;
//Note:
// On 64bit systems, allocated pointers are 16byte aligned.
// On 32bit systems, allocated pointers are 8byte aligned.
_Static_assert(sizeof(BlockHeader) % ALIGNMENT == 0, _Static_assert(sizeof(BlockHeader) % ALIGNMENT == 0,
"BlockHeader size must be a multiple of ALIGNMENT"); "BlockHeader size must be a multiple of ALIGNMENT");
@@ -58,17 +60,17 @@ void small_allocator_init(SmallAllocator* a, void* backing_store, size_t size) {
a->size = size; a->size = size;
BlockHeader* first = (BlockHeader*)a->base; BlockHeader* first = (BlockHeader*)a->base;
size_t usable = size - sizeof(BlockHeader); size_t usable = ALIGN(size - sizeof(BlockHeader));
usable = ALIGN(usable);
first->size_and_flags = usable | BLOCK_FREE; first->size_and_flags = usable | BLOCK_FREE;
first->next = NULL; first->next = NULL;
first->prev = NULL;
a->heap_start = first; a->heap_start = first;
a->last_alloc = first; a->last_alloc = first;
} }
/* --- Block splitting --- */ /* Splitting */
static void split_block(BlockHeader* block, size_t size) { static void split_block(BlockHeader* block, size_t size) {
size = ALIGN(size); size = ALIGN(size);
@@ -81,35 +83,40 @@ static void split_block(BlockHeader* block, size_t size) {
BlockHeader* new_block = BlockHeader* new_block =
(BlockHeader*)(base + sizeof(BlockHeader) + size); (BlockHeader*)(base + sizeof(BlockHeader) + size);
size_t new_size = bsize - size - sizeof(BlockHeader); size_t new_size = ALIGN(bsize - size - sizeof(BlockHeader));
new_size = ALIGN(new_size);
block_set_size(block, size); block_set_size(block, size);
block_set_used(block); block_set_used(block);
new_block->size_and_flags = new_size | BLOCK_FREE; new_block->size_and_flags = new_size | BLOCK_FREE;
new_block->next = block->next; new_block->next = block->next;
new_block->prev = block;
if (block->next)
block->next->prev = new_block;
block->next = new_block; block->next = new_block;
} }
/* --- Adjacent-only coalescing --- */ /* Coalescing */
static void merge_with_next(BlockHeader* block) { static void merge_with_next(BlockHeader* block) {
BlockHeader* next = block->next; BlockHeader* next = block->next;
if (!next || !block_is_free(next)) if (!next || !block_is_free(next))
return; return;
size_t new_size = block_size(block) + size_t new_size = ALIGN(block_size(block) +
sizeof(BlockHeader) + sizeof(BlockHeader) +
block_size(next); block_size(next));
new_size = ALIGN(new_size);
block_set_size(block, new_size); block_set_size(block, new_size);
block->next = next->next; block->next = next->next;
if (next->next)
next->next->prev = block;
} }
/* --- Allocation (next-fit) --- */ /* Allocation (next-fit) */
void* small_alloc(SmallAllocator* a, size_t size) { void* small_alloc(SmallAllocator* a, size_t size) {
if (size == 0) if (size == 0)
@@ -133,7 +140,7 @@ void* small_alloc(SmallAllocator* a, size_t size) {
return NULL; return NULL;
} }
/* --- Free --- */ /* Free */
void small_free(SmallAllocator* a, void* ptr) { void small_free(SmallAllocator* a, void* ptr) {
if (!ptr) if (!ptr)
@@ -146,16 +153,10 @@ void small_free(SmallAllocator* a, void* ptr) {
merge_with_next(block); merge_with_next(block);
BlockHeader* prev = NULL; if (block->prev && block_is_free(block->prev)) {
BlockHeader* curr = a->heap_start; BlockHeader* prev = block->prev;
while (curr && curr != block) {
prev = curr;
curr = curr->next;
}
if (prev && block_is_free(prev)) {
merge_with_next(prev); merge_with_next(prev);
if (a->last_alloc == block) if (a->last_alloc == block)
a->last_alloc = prev; a->last_alloc = prev;
} }