Compare commits

..

4 Commits

Author SHA1 Message Date
cf848f2e37 refactor: Changed Result types return sytaxis 2026-04-03 15:57:54 -06:00
a207018511 fix: Moved padding declaration in arena_ensure_capacity
descripcion mas detallada
2026-04-03 12:51:49 -06:00
0c2e819c2b 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 :)
2026-04-03 12:44:01 -06:00
39dfded924 test and rework: Added push test and cahnged arena_push return
So added a little test that just adds 3 ints to an arena, nothing much
but the basics work, also changed arena_push because it only pushed and
didn't returned a ponter (wich is fucking useless) so also fixed that,
everything seems fine now, need more tests.

# Tipos:
# feat, fix, refactor, docs, style, test, chore
2026-03-31 19:15:37 -06:00
5 changed files with 157 additions and 82 deletions

View File

@@ -24,6 +24,12 @@ add_executable(arena_main src/main.c)
target_link_libraries(arena_main arena_lib) 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 # Testing
# ------------------------ # ------------------------

View File

@@ -50,13 +50,17 @@ ArenaResult arena_init(size_t capacity);
void arena_destroy(Arena *arena); void arena_destroy(Arena *arena);
ArenaPointer arena_alloc(Arena *arena, size_t size, size_t alignment); ArenaPointer arena_alloc(Arena *arena, size_t size, size_t alignment);
ArenaErr arena_push(Arena *arena, void *data, 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); 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, // Should be moved to something like general utilities,
// i should make one for all my c projects // i should make one for all my c projects
bool mul_size_t_safe(size_t a, size_t b, size_t *out); bool mul_size_t_safe(size_t a, size_t b, size_t *out);
#endif // !ARENA_H #endif // !ARENA_H

View File

@@ -6,22 +6,13 @@
#include <string.h> #include <string.h>
ArenaResult arena_init(size_t capacity) { ArenaResult arena_init(size_t capacity) {
if (capacity < 1) {
return (ArenaResult) {.is_valid = false, .err = ARENA_INVALID_SIZE};
}
void *buffer = malloc(capacity); void *buffer = malloc(capacity);
if (buffer == NULL) { if (buffer == NULL) {
ArenaResult err = { return (ArenaResult) {.is_valid = false, .err = ARENA_BAD_ALLOC};
.is_valid = false,
.err = ARENA_BAD_ALLOC,
};
return err;
}
if (capacity < 1) {
ArenaResult err = {
.is_valid = false,
.err = ARENA_INVALID_SIZE,
};
return err;
} }
Arena new_arena = { Arena new_arena = {
@@ -29,74 +20,64 @@ ArenaResult arena_init(size_t capacity) {
.capacity = capacity, .capacity = capacity,
.offset = 0, .offset = 0,
}; };
ArenaResult val = {
.is_valid = true,
.arena = new_arena,
};
return val; return (ArenaResult) {.is_valid = true, .arena = new_arena};
} }
void arena_destroy(Arena *arena) { void arena_destroy(Arena *arena) {
if (arena == NULL) { free(arena->buffer);
return; arena->buffer = NULL;
}
arena->offset = 0; arena->offset = 0;
arena->capacity = 0; arena->capacity = 0;
free(arena->buffer);
} }
ArenaPointer arena_alloc(Arena *arena, size_t size, size_t alignment) { ArenaPointer arena_alloc(Arena *arena, size_t size, size_t alignment) {
if (arena == NULL) { if (arena == NULL) {
ArenaPointer err = { return (ArenaPointer) {.is_valid = false, .err = ARENA_NULL_ARG};
.is_valid = false,
.err = ARENA_NULL_ARG,
};
return err;
} }
SizeResult new_offset = align_arena_offset(arena, alignment); SizeResult padding = get_arena_align_padding(arena, alignment);
if (!new_offset.is_valid) { if (!padding.is_valid) {
ArenaPointer err = { return (ArenaPointer) {.is_valid = false, .err = padding.err};
.is_valid = false,
.err = new_offset.err,
};
return err;
} }
if (new_offset.val + size >= arena->capacity) {
ArenaPointer err = { if (arena->offset + padding.val >= arena->capacity) {
.is_valid = false, return (ArenaPointer) {.is_valid = false, .err = ARENA_OUT_OF_SPACE};
.err = ARENA_OUT_OF_SPACE,
};
return err;
} }
arena->offset = new_offset.val; if (arena->offset > SIZE_MAX - padding.val - size) {
return (ArenaPointer) {.is_valid = false, .err = ARENA_CAPACITY_OVERFLOW};
}
ArenaPointer val = { size_t aligned_offset = arena->offset + padding.val;
.is_valid = true,
.address = arena->buffer + arena->offset, arena->offset = aligned_offset + size;
}; return (ArenaPointer) {.is_valid = true, .address = arena->buffer + aligned_offset};
return val;
} }
ArenaErr arena_push(Arena *arena, void *data, size_t size, size_t alignment) { ArenaErr arena_ensure_capacity(Arena *arena, size_t size, size_t alignment) {
if (arena == NULL || data == NULL) { if (arena == NULL) {
return ARENA_NULL_ARG; return ARENA_NULL_ARG;
} }
ArenaPointer pointer = arena_alloc(arena, size, alignment); SizeResult padding = get_arena_align_padding(arena, alignment);
if (!padding.is_valid) {
if (!pointer.is_valid) { return padding.err;
return pointer.err;
} }
while (!pointer.is_valid && pointer.err == ARENA_OUT_OF_SPACE) { while (true) {
size_t new_capacity; if (arena->offset > SIZE_MAX - padding.val - size) {
return ARENA_CAPACITY_OVERFLOW;
}
bool is_overflow = mul_size_t_safe(arena->capacity, 2, &new_capacity); size_t required = arena->offset + padding.val + size;
if (is_overflow) {
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; return ARENA_CAPACITY_OVERFLOW;
} }
@@ -104,16 +85,30 @@ ArenaErr arena_push(Arena *arena, void *data, size_t size, size_t alignment) {
if (err != ARENA_OK) { if (err != ARENA_OK) {
return err; return err;
} }
}
}
pointer = arena_alloc(arena, size, alignment); ArenaPointer arena_push(Arena *arena, void *data, size_t size, size_t alignment) {
if (arena == NULL || data == NULL) {
return (ArenaPointer) {.is_valid = false, .err = ARENA_NULL_ARG};
} }
ArenaErr err = arena_ensure_capacity(arena, size, alignment);
if (err != ARENA_OK) {
return (ArenaPointer) {.is_valid = false, .err = err};
}
ArenaPointer pointer = arena_alloc(arena, size, alignment);
if (!pointer.is_valid) {
return pointer;
}
memcpy( memcpy(
pointer.address, pointer.address,
data, data,
size); size);
return ARENA_OK; return pointer;
} }
ArenaErr arena_realloc(Arena *arena, size_t new_capacity) { ArenaErr arena_realloc(Arena *arena, size_t new_capacity) {
@@ -131,30 +126,19 @@ ArenaErr arena_realloc(Arena *arena, size_t new_capacity) {
return ARENA_OK; 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) { if (arena == NULL) {
SizeResult err = { return (SizeResult) {.is_valid = false, .err = ARENA_NULL_ARG};
.is_valid = false,
.err = ARENA_NULL_ARG,
};
return err;
} }
if (alignment < 1) { if (alignment < 1) {
SizeResult err = { return (SizeResult) {.is_valid = false, .err = ARENA_INVALID_ALIGN};
.is_valid = false,
.err = ARENA_INVALID_ALIGN,
};
return err;
} }
SizeResult val = { uintptr_t current_address = (uintptr_t) (arena->buffer + arena->offset);
.is_valid = true, uintptr_t aligned = ((current_address + (alignment - 1)) & ~(alignment - 1));
.val = ((arena->offset + (alignment - 1)) & ~(alignment - 1)),
// Formula for alignment, found at stackoverflow, i came up with one return (SizeResult) {.is_valid = true, .err = aligned - current_address};
// but it involved 3 divisions, this one is way faster :)
};
return val;
} }
bool mul_size_t_safe(size_t a, size_t b, size_t *out) { bool mul_size_t_safe(size_t a, size_t b, size_t *out) {

View File

@@ -7,4 +7,9 @@ target_link_libraries(test_arena
cmocka::cmocka 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) add_test(NAME arena_tests COMMAND test_arena)

View File

@@ -1,6 +1,82 @@
#include "arena.h" #include <stdalign.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <setjmp.h>
#include <cmocka.h>
#include <string.h>
#include "arena.h"
static void test_push_3_ints(void **state) {
(void) state;
ArenaResult value = arena_init(sizeof(int) * 3);
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);
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);
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) { int main(void) {
return EXIT_SUCCESS; const struct CMUnitTest tests[] = {
cmocka_unit_test(test_push_3_ints),
cmocka_unit_test(test_push_3_ints_2_doubles),
};
return cmocka_run_group_tests(tests, NULL, NULL);
} }