From 0c2e819c2bd2999eed418b33d4c541f5e6f8c6d4 Mon Sep 17 00:00:00 2001 From: LaEntropiaa Date: Fri, 3 Apr 2026 12:44:01 -0600 Subject: [PATCH] refactor(all): Fixed alloc and moved growing logic to another function OK, so evrything was a fucknig mess, it still kinda is, AI helped me to clean my own mess, it really sucks but the fucking debugger and address sanitizer were not working, also, there were small small errors so no big deal but still, a bit sad, also changed a the way i cast from uint8_t to double and int in the tests, may change that i guess, verything else i might say is good :) --- CMakeLists.txt | 6 +++ include/arena.h | 5 +- src/arena.c | 120 ++++++++++++++++++++++++++------------------ test/CMakeLists.txt | 5 ++ test/test_arena.c | 46 +++++++++++++++-- 5 files changed, 129 insertions(+), 53 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3e3f88f..a8ecfae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,12 @@ add_executable(arena_main src/main.c) target_link_libraries(arena_main arena_lib) +function(enable_sanitizers target) + target_compile_options(${target} PRIVATE -fsanitize=address -fno-omit-frame-pointer) + target_link_options(${target} PRIVATE -fsanitize=address) +endfunction() + + # ------------------------ # Testing # ------------------------ diff --git a/include/arena.h b/include/arena.h index 5b743fd..ac9bb1b 100644 --- a/include/arena.h +++ b/include/arena.h @@ -53,10 +53,11 @@ ArenaPointer arena_alloc(Arena *arena, size_t size, size_t alignment); ArenaPointer arena_push(Arena *arena, void *data, size_t size, size_t alignment); ArenaErr arena_realloc(Arena *arena, size_t new_capacity); -SizeResult align_arena_offset(Arena *arena, size_t alignment); - +SizeResult get_arena_align_padding(Arena *arena, size_t alignment); +ArenaErr arena_ensure_capacity(Arena *arena, size_t size, size_t alignment); // Should be moved to something like general utilities, // i should make one for all my c projects bool mul_size_t_safe(size_t a, size_t b, size_t *out); + #endif // !ARENA_H diff --git a/src/arena.c b/src/arena.c index 9bd16b3..36c430a 100644 --- a/src/arena.c +++ b/src/arena.c @@ -6,6 +6,13 @@ #include ArenaResult arena_init(size_t capacity) { + if (capacity < 1) { + ArenaResult err = { + .is_valid = false, + .err = ARENA_INVALID_SIZE, + }; + return err; + } void *buffer = malloc(capacity); if (buffer == NULL) { @@ -16,14 +23,6 @@ ArenaResult arena_init(size_t capacity) { return err; } - if (capacity < 1) { - ArenaResult err = { - .is_valid = false, - .err = ARENA_INVALID_SIZE, - }; - return err; - } - Arena new_arena = { .buffer = buffer, .capacity = capacity, @@ -38,13 +37,10 @@ ArenaResult arena_init(size_t capacity) { } void arena_destroy(Arena *arena) { - if (arena == NULL) { - return; - } - + free(arena->buffer); + arena->buffer = NULL; arena->offset = 0; arena->capacity = 0; - free(arena->buffer); } ArenaPointer arena_alloc(Arena *arena, size_t size, size_t alignment) { @@ -56,16 +52,17 @@ ArenaPointer arena_alloc(Arena *arena, size_t size, size_t alignment) { return err; } - SizeResult new_offset = align_arena_offset(arena, alignment); - if (!new_offset.is_valid) { + SizeResult padding = get_arena_align_padding(arena, alignment); + if (!padding.is_valid) { ArenaPointer err = { .is_valid = false, - .err = new_offset.err, + .err = padding.err, }; return err; } - if (new_offset.val + size >= arena->capacity) { + + if (arena->offset + padding.val >= arena->capacity) { ArenaPointer err = { .is_valid = false, .err = ARENA_OUT_OF_SPACE, @@ -73,15 +70,57 @@ ArenaPointer arena_alloc(Arena *arena, size_t size, size_t alignment) { return err; } - arena->offset = new_offset.val; + if (arena->offset > SIZE_MAX - padding.val - size) { + ArenaPointer err = { + .is_valid = false, + .err = ARENA_CAPACITY_OVERFLOW, + }; + return err; + } + size_t aligned_offset = arena->offset + padding.val; + + arena->offset = aligned_offset + size; ArenaPointer val = { .is_valid = true, - .address = arena->buffer + arena->offset, + .address = arena->buffer + aligned_offset, }; return val; } +ArenaErr arena_ensure_capacity(Arena *arena, size_t size, size_t alignment) { + if (arena == NULL) { + return ARENA_NULL_ARG; + } + + while (true) { + SizeResult padding = get_arena_align_padding(arena, alignment); + if (!padding.is_valid) { + return padding.err; + } + + if (arena->offset > SIZE_MAX - padding.val - size) { + return ARENA_CAPACITY_OVERFLOW; + } + + size_t required = arena->offset + padding.val + size; + + if (required <= arena->capacity) { + return ARENA_OK; + } + + size_t new_capacity; + if (mul_size_t_safe(arena->capacity, 2, &new_capacity)) { + return ARENA_CAPACITY_OVERFLOW; + } + + ArenaErr err = arena_realloc(arena, new_capacity); + if (err != ARENA_OK) { + return err; + } + } +} + ArenaPointer arena_push(Arena *arena, void *data, size_t size, size_t alignment) { if (arena == NULL || data == NULL) { ArenaPointer bad_pointer = { @@ -91,35 +130,20 @@ ArenaPointer arena_push(Arena *arena, void *data, size_t size, size_t alignment) return bad_pointer; } - ArenaPointer pointer = arena_alloc(arena, size, alignment); + ArenaErr err = arena_ensure_capacity(arena, size, alignment); + if (err != ARENA_OK) { + ArenaPointer bad_pointer = { + .is_valid = false, + .err = err, + }; + return bad_pointer; + } + ArenaPointer pointer = arena_alloc(arena, size, alignment); if (!pointer.is_valid) { return pointer; } - while (!pointer.is_valid && pointer.err == ARENA_OUT_OF_SPACE) { - size_t new_capacity; - - bool is_overflow = mul_size_t_safe(arena->capacity, 2, &new_capacity); - if (is_overflow) { - ArenaPointer bad_pointer = { - .is_valid = false, - .err = ARENA_CAPACITY_OVERFLOW, - }; - return bad_pointer; - } - - ArenaErr err = arena_realloc(arena, new_capacity); - if (err != ARENA_OK) { - ArenaPointer bad_pointer = { - .is_valid = false, - .err = err, - }; - return bad_pointer; - } - - pointer = arena_alloc(arena, size, alignment); - } - + memcpy( pointer.address, data, @@ -143,7 +167,7 @@ ArenaErr arena_realloc(Arena *arena, size_t new_capacity) { return ARENA_OK; } -SizeResult align_arena_offset(Arena *arena, size_t alignment) { +SizeResult get_arena_align_padding(Arena *arena, size_t alignment) { if (arena == NULL) { SizeResult err = { .is_valid = false, @@ -160,11 +184,11 @@ SizeResult align_arena_offset(Arena *arena, size_t alignment) { return err; } + uintptr_t current_address = (uintptr_t) (arena->buffer + arena->offset); + uintptr_t aligned = ((current_address + (alignment - 1)) & ~(alignment - 1)); SizeResult val = { .is_valid = true, - .val = ((arena->offset + (alignment - 1)) & ~(alignment - 1)), - // Formula for alignment, found at stackoverflow, i came up with one - // but it involved 3 divisions, this one is way faster :) + .val = aligned - current_address, }; return val; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index aef968f..4db3995 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -7,4 +7,9 @@ target_link_libraries(test_arena cmocka::cmocka ) +enable_sanitizers(test_arena) + +target_compile_options(test_arena PRIVATE -fsanitize=address -fno-omit-frame-pointer) +target_link_options(test_arena PRIVATE -fsanitize=address) + add_test(NAME arena_tests COMMAND test_arena) diff --git a/test/test_arena.c b/test/test_arena.c index c170407..c7b6941 100644 --- a/test/test_arena.c +++ b/test/test_arena.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "arena.h" @@ -19,22 +20,61 @@ static void test_push_3_ints(void **state) { int int_to_push = 20; ArenaPointer result = arena_push(&arena, &int_to_push, sizeof(int), alignof(int)); assert_true(result.is_valid); - assert_int_equal(20, (int) *result.address); + assert_int_equal(20, *(int*)result.address); int_to_push = 30; result = arena_push(&arena, &int_to_push, sizeof(int), alignof(int)); assert_true(result.is_valid); - assert_int_equal(30, (int) *result.address); + assert_int_equal(30, *(int*) result.address); int_to_push = 40; result = arena_push(&arena, &int_to_push, sizeof(int), alignof(int)); assert_true(result.is_valid); - assert_int_equal(40, (int) *result.address); + assert_int_equal(40, *(int*) result.address); + + arena_destroy(&arena); } +static void test_push_3_ints_2_doubles(void **state) { + (void) state; + + ArenaResult value = arena_init((sizeof(int) * 3) + (sizeof(double) * 2)); + assert_true(value.is_valid); + Arena arena = value.arena; + + int int_to_push = 20; + ArenaPointer result = arena_push(&arena, &int_to_push, sizeof(int), alignof(int)); + assert_true(result.is_valid); + assert_int_equal(20, *(int*)result.address); + + double double_to_push = 4.57; + result = arena_push(&arena, &double_to_push, sizeof(double), alignof(double)); + assert_true(result.is_valid); + assert_double_equal(4.57, *(double*)result.address, 1e-6); + + int_to_push = 30; + result = arena_push(&arena, &int_to_push, sizeof(int), alignof(int)); + assert_true(result.is_valid); + assert_int_equal(30, *(int*)result.address); + + int_to_push = 40; + result = arena_push(&arena, &int_to_push, sizeof(int), alignof(int)); + assert_true(result.is_valid); + assert_int_equal(40, *(int*) result.address); + + double_to_push = 267.33; + result = arena_push(&arena, &double_to_push, sizeof(double), alignof(double)); + assert_true(result.is_valid); + assert_double_equal(267.33, *(double*) result.address, 1e-6); + + arena_destroy(&arena); +} + + int main(void) { const struct CMUnitTest tests[] = { cmocka_unit_test(test_push_3_ints), + cmocka_unit_test(test_push_3_ints_2_doubles), };