diff --git a/include/lae_arena.h b/include/lae_arena.h index cd52638..d367551 100644 --- a/include/lae_arena.h +++ b/include/lae_arena.h @@ -17,6 +17,7 @@ typedef enum { ARENA_INVALID_SIZE, ARENA_INVALID_ALIGN, ARENA_CAPACITY_OVERFLOW, + ARENA_LOCKED, } ArenaErr; typedef struct { @@ -37,7 +38,7 @@ typedef struct { ArenaErr err; Allocator allocator; }; -} ArenaAllocResult; +} ArenaAllocator; typedef struct { bool is_valid; @@ -63,6 +64,9 @@ ArenaSizeResult arena_get_align_padding(Arena *arena, size_t alignment); // I shouldn't cover. ArenaErr arena_ensure_capacity(Arena *arena, size_t size, size_t alignment); -ArenaAllocResult arena_get_allocator(Arena *arena); +ArenaErr arena_lock_growth(Arena *arena); +ArenaErr arena_unlock_growth(Arena *arena); + +ArenaAllocator arena_get_allocator(Arena *arena); #endif // !RENA_H diff --git a/src/lae_arena.c b/src/lae_arena.c index e8ee80c..6214628 100644 --- a/src/lae_arena.c +++ b/src/lae_arena.c @@ -12,6 +12,7 @@ struct Arena { size_t capacity; size_t offset; + bool growth_locked; Allocator allocator; }; @@ -50,6 +51,7 @@ ArenaErr arena_init(Arena **arena, Allocator allocator, size_t capacity) { .buffer = buffer, .capacity = capacity, .offset = 0, + .growth_locked = false, .allocator = allocator, }; @@ -68,6 +70,7 @@ ArenaErr arena_destroy(Arena **arena) { tmp->buffer = NULL; tmp->offset = 0; tmp->capacity = 0; + tmp->growth_locked = false; tmp->allocator .free(tmp->allocator.ctx, *arena, sizeof(Arena), alignof(Arena)); *arena = NULL; @@ -209,6 +212,10 @@ ArenaErr arena_resize(Arena *arena, size_t new_capacity) { return ARENA_INVALID_SIZE; } + if (arena->growth_locked) { + return ARENA_LOCKED; + } + uint8_t *tmp = arena->allocator.realloc( arena->allocator.ctx, arena->buffer, @@ -241,6 +248,24 @@ ArenaSizeResult arena_get_align_padding(Arena *arena, size_t alignment) { return (ArenaSizeResult){.is_valid = true, .val = padding}; } +ArenaErr arena_lock_growth(Arena *arena) { + if (arena == NULL) { + return ARENA_NULL_ARG; + } + + arena->growth_locked = true; + return ARENA_OK; +} + +ArenaErr arena_unlock_growth(Arena *arena) { + if (arena == NULL) { + return ARENA_NULL_ARG; + } + + arena->growth_locked = false; + return ARENA_OK; +} + void *arena_unwrap_pointer(ArenaPointer p) { if (!p.is_valid) { return NULL; @@ -268,19 +293,21 @@ arena_allocator_free(void *ctx, void *pointer, size_t size, size_t alignment) { return; } -ArenaAllocResult arena_get_allocator(Arena *arena) { +ArenaAllocator arena_get_allocator(Arena *arena) { if (arena == NULL) { - return (ArenaAllocResult){ + return (ArenaAllocator){ .is_valid = false, .err = ARENA_NULL_ARG, }; } + arena->growth_locked = true; + Allocator allocator = { .alloc = arena_allocator_alloc, .realloc = arena_allocator_realloc, .free = arena_allocator_free, .ctx = arena}; - return (ArenaAllocResult){.is_valid = true, .allocator = allocator}; + return (ArenaAllocator){.is_valid = true, .allocator = allocator}; } diff --git a/test/test_arena.c b/test/test_arena.c index 63e84bb..18697d6 100644 --- a/test/test_arena.c +++ b/test/test_arena.c @@ -209,6 +209,59 @@ static void test_arena_ensure_capacity_align_max(void **state) { arena_destroy(&arena); } +static void test_arena_allocator_valid_allocation(void **state) { + (void)state; + + Arena *arena; + assert_uint_equal(arena_init(&arena, allocator_default(), 64), ARENA_OK); + + ArenaAllocator result = arena_get_allocator(arena); + assert_true(result.is_valid); + + Allocator arena_allocator = result.allocator; + + int *x = + arena_allocator.alloc(arena_allocator.ctx, sizeof(int), alignof(int)); + + assert_non_null(x); + + *x = 34; + assert_int_equal(*x, 34); + + arena_destroy(&arena); +} + +static void test_arena_allocatro_many_allocations(void **state) { + (void)state; + + Arena *arena; + assert_uint_equal(arena_init(&arena, allocator_default(), 64), ARENA_OK); + + ArenaAllocator result = arena_get_allocator(arena); + assert_true(result.is_valid); + + Allocator arena_allocator = result.allocator; + for (int i = 0; i < 10; i++) { + int *x = arena_allocator.alloc( + arena_allocator.ctx, + sizeof(int), + alignof(int)); + + assert_non_null(x); + } + + arena_destroy(&arena); +} + +static void test_arena_allocator_from_null_arena(void **state) { + (void)state; + + Arena *arena = NULL; + + ArenaAllocator result = arena_get_allocator(arena); + assert_false(result.is_valid); +} + int main(void) { const struct CMUnitTest tests[] = { cmocka_unit_test(test_push_3_ints), @@ -223,6 +276,7 @@ int main(void) { cmocka_unit_test(test_arena_ensure_capacity_align_max), cmocka_unit_test(test_arena_ensure_capacity_size_0), cmocka_unit_test(test_arena_ensure_capacity_size_max), + cmocka_unit_test(test_arena_allocator_valid_allocation), }; return cmocka_run_group_tests(tests, NULL, NULL);