Use doubly linked list for O(1) free()
This commit is contained in:
+25
-24
@@ -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 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,
|
_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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user