diff options
author | Linnnus <[email protected]> | 2025-04-08 01:07:22 +0000 |
---|---|---|
committer | Linnnus <[email protected]> | 2025-04-08 01:07:34 +0000 |
commit | 8dd0c4f27aae02dd60f029db4cf03f9902cba26f (patch) | |
tree | 829c4bc0917f6615c55ae61d49932179c038c1e5 /src/core/arena_allocator.c |
feat: Initial commit
At this point we have some allocation routines but no work on the actual
language has been done.
Diffstat (limited to 'src/core/arena_allocator.c')
-rw-r--r-- | src/core/arena_allocator.c | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/src/core/arena_allocator.c b/src/core/arena_allocator.c new file mode 100644 index 0000000..cbde038 --- /dev/null +++ b/src/core/arena_allocator.c @@ -0,0 +1,98 @@ +#include "arena_allocator.h" + +#include <assert.h> +#include <stddef.h> +#include <stdbool.h> + +SandArena sand_create_arena(SandAllocator *a) { + return (SandArena) { + .parent = a, + .current_region = NULL, + }; +} + +#define MAX(a, b) ({ \ + __auto_type _a = (a);\ + __auto_type _b = (b);\ + _a > _b ? _a : _b;\ + }) + +#define REGION_DEFUALT_SIZE ((size_t)2048) + +static SandRegion *allocate_new_region(SandAllocator *a, size_t size, size_t alignment) { + size_t padded_size = size + (alignment - 1); + size_t capacity = MAX(padded_size, REGION_DEFUALT_SIZE) ; + size_t region_size = offsetof(SandRegion, data) + capacity; + SandRegion *result = sand_allocate_aligned(a, region_size, _Alignof(SandRegion)); + if (result == NULL) { + return NULL; + } + result->capacity = capacity; + result->used = 0; + return result; +} + +static void *allocate(size_t size, size_t alignment, void *user_data) { + SandArena *arena = user_data; + + // This is the first allocation made within the arena. Let's allocate the initial region. + if (arena->current_region == NULL) { + SandRegion *first_region = allocate_new_region(arena->parent, size, alignment); + if (first_region == NULL) { + return NULL; + } + first_region->prev = NULL; + arena->current_region = first_region; + } + + SandRegion *iter = arena->current_region; + while (true) { // Loop looking for a region. + void *aligned_ptr = sand_align_pointer_forward(iter->data + iter->used, alignment); + void *end_ptr = iter->data + iter->capacity; + if (aligned_ptr + size > end_ptr) { // This pointer region doesn't have room; allocate a new one and try again. + SandRegion *new_region = allocate_new_region(arena->parent, size, alignment); + if (new_region == NULL) { + return NULL; + } + + new_region->prev = arena->current_region; + arena->current_region = iter; + + iter = new_region; // Next time is guaranteed to succeed. + continue; + } else { // Yay, we found a spot! + iter->used += aligned_ptr - (void *)iter->data; + return aligned_ptr; + } + } + + assert(false); // unreachable +} + +static void deallocate(void *old_ptr, size_t old_size, void *user_data) { + (void)old_ptr; + (void)old_size; + (void)user_data; + return; // No-op, as all allocations are freed when the arena is freed. +} + +// Returns a `SandAllocator` which is valid as long as the arena is. +// The arena must not be moved afterwards, as the allocator retains a pointer to the arena. +SandAllocator sand_get_allocator_for_arena(SandArena *arena) { + return (SandAllocator) { + .allocate = allocate, + .reallocate = NULL, // TODO: Special case for if it is the last allocation of the current region. + .deallocate = deallocate, + .user_data = arena, + }; +} + +void sand_destroy_arena(SandArena *arena) { + SandRegion *iter = arena->current_region; + while (iter != NULL) { + SandRegion *next = iter->prev; + size_t region_size = offsetof(SandRegion, data) + iter->capacity; + sand_deallocate(arena->parent, iter, region_size); + iter = next; + } +} |