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 :)
This commit is contained in:
@@ -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
|
||||
# ------------------------
|
||||
|
||||
@@ -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
|
||||
|
||||
118
src/arena.c
118
src/arena.c
@@ -6,6 +6,13 @@
|
||||
#include <string.h>
|
||||
|
||||
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,34 +130,19 @@ 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,
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <stdint.h>
|
||||
#include <setjmp.h>
|
||||
#include <cmocka.h>
|
||||
#include <string.h>
|
||||
|
||||
#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),
|
||||
};
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user