Use doubly linked list for O(1) free()
This commit is contained in:
+25
-24
@@ -11,12 +11,14 @@
|
||||
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
|
||||
// (for 64 bit systems, 32 bit systems might need other adjustments)
|
||||
uint8_t pad[32 - (sizeof(size_t) + 2*sizeof(void*))];
|
||||
} BlockHeader;
|
||||
|
||||
|
||||
//Note:
|
||||
// On 64‑bit systems, allocated pointers are 16‑byte aligned.
|
||||
// On 32‑bit systems, allocated pointers are 8‑byte aligned.
|
||||
_Static_assert(sizeof(BlockHeader) % ALIGNMENT == 0,
|
||||
"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;
|
||||
|
||||
BlockHeader* first = (BlockHeader*)a->base;
|
||||
size_t usable = size - sizeof(BlockHeader);
|
||||
usable = ALIGN(usable);
|
||||
size_t usable = ALIGN(size - sizeof(BlockHeader));
|
||||
|
||||
first->size_and_flags = usable | BLOCK_FREE;
|
||||
first->next = NULL;
|
||||
first->prev = NULL;
|
||||
|
||||
a->heap_start = first;
|
||||
a->last_alloc = first;
|
||||
}
|
||||
|
||||
/* --- Block splitting --- */
|
||||
/* Splitting */
|
||||
|
||||
static void split_block(BlockHeader* block, size_t size) {
|
||||
size = ALIGN(size);
|
||||
@@ -81,35 +83,40 @@ static void split_block(BlockHeader* block, size_t size) {
|
||||
BlockHeader* new_block =
|
||||
(BlockHeader*)(base + sizeof(BlockHeader) + size);
|
||||
|
||||
size_t new_size = bsize - size - sizeof(BlockHeader);
|
||||
new_size = ALIGN(new_size);
|
||||
size_t new_size = ALIGN(bsize - size - sizeof(BlockHeader));
|
||||
|
||||
block_set_size(block, size);
|
||||
block_set_used(block);
|
||||
|
||||
new_block->size_and_flags = new_size | BLOCK_FREE;
|
||||
new_block->next = block->next;
|
||||
new_block->prev = block;
|
||||
|
||||
if (block->next)
|
||||
block->next->prev = new_block;
|
||||
|
||||
block->next = new_block;
|
||||
}
|
||||
|
||||
/* --- Adjacent-only coalescing --- */
|
||||
/* Coalescing */
|
||||
|
||||
static void merge_with_next(BlockHeader* block) {
|
||||
BlockHeader* next = block->next;
|
||||
if (!next || !block_is_free(next))
|
||||
return;
|
||||
|
||||
size_t new_size = block_size(block) +
|
||||
sizeof(BlockHeader) +
|
||||
block_size(next);
|
||||
size_t new_size = ALIGN(block_size(block) +
|
||||
sizeof(BlockHeader) +
|
||||
block_size(next));
|
||||
|
||||
new_size = ALIGN(new_size);
|
||||
block_set_size(block, new_size);
|
||||
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) {
|
||||
if (size == 0)
|
||||
@@ -133,7 +140,7 @@ void* small_alloc(SmallAllocator* a, size_t size) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* --- Free --- */
|
||||
/* Free */
|
||||
|
||||
void small_free(SmallAllocator* a, void* ptr) {
|
||||
if (!ptr)
|
||||
@@ -146,16 +153,10 @@ void small_free(SmallAllocator* a, void* ptr) {
|
||||
|
||||
merge_with_next(block);
|
||||
|
||||
BlockHeader* prev = NULL;
|
||||
BlockHeader* curr = a->heap_start;
|
||||
|
||||
while (curr && curr != block) {
|
||||
prev = curr;
|
||||
curr = curr->next;
|
||||
}
|
||||
|
||||
if (prev && block_is_free(prev)) {
|
||||
if (block->prev && block_is_free(block->prev)) {
|
||||
BlockHeader* prev = block->prev;
|
||||
merge_with_next(prev);
|
||||
|
||||
if (a->last_alloc == block)
|
||||
a->last_alloc = prev;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user