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)
|
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
|
||||||
# ------------------------
|
# ------------------------
|
||||||
|
|||||||
@@ -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);
|
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
|
||||||
|
|||||||
120
src/arena.c
120
src/arena.c
@@ -6,6 +6,13 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
ArenaResult arena_init(size_t capacity) {
|
ArenaResult arena_init(size_t capacity) {
|
||||||
|
if (capacity < 1) {
|
||||||
|
ArenaResult err = {
|
||||||
|
.is_valid = false,
|
||||||
|
.err = ARENA_INVALID_SIZE,
|
||||||
|
};
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
void *buffer = malloc(capacity);
|
void *buffer = malloc(capacity);
|
||||||
if (buffer == NULL) {
|
if (buffer == NULL) {
|
||||||
@@ -16,14 +23,6 @@ ArenaResult arena_init(size_t capacity) {
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (capacity < 1) {
|
|
||||||
ArenaResult err = {
|
|
||||||
.is_valid = false,
|
|
||||||
.err = ARENA_INVALID_SIZE,
|
|
||||||
};
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
Arena new_arena = {
|
Arena new_arena = {
|
||||||
.buffer = buffer,
|
.buffer = buffer,
|
||||||
.capacity = capacity,
|
.capacity = capacity,
|
||||||
@@ -38,13 +37,10 @@ ArenaResult arena_init(size_t capacity) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
@@ -56,16 +52,17 @@ ArenaPointer arena_alloc(Arena *arena, size_t size, size_t alignment) {
|
|||||||
return err;
|
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 = {
|
ArenaPointer err = {
|
||||||
.is_valid = false,
|
.is_valid = false,
|
||||||
.err = new_offset.err,
|
.err = padding.err,
|
||||||
};
|
};
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new_offset.val + size >= arena->capacity) {
|
|
||||||
|
if (arena->offset + padding.val >= arena->capacity) {
|
||||||
ArenaPointer err = {
|
ArenaPointer err = {
|
||||||
.is_valid = false,
|
.is_valid = false,
|
||||||
.err = ARENA_OUT_OF_SPACE,
|
.err = ARENA_OUT_OF_SPACE,
|
||||||
@@ -73,15 +70,57 @@ ArenaPointer arena_alloc(Arena *arena, size_t size, size_t alignment) {
|
|||||||
return err;
|
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 = {
|
ArenaPointer val = {
|
||||||
.is_valid = true,
|
.is_valid = true,
|
||||||
.address = arena->buffer + arena->offset,
|
.address = arena->buffer + aligned_offset,
|
||||||
};
|
};
|
||||||
return val;
|
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) {
|
ArenaPointer arena_push(Arena *arena, void *data, size_t size, size_t alignment) {
|
||||||
if (arena == NULL || data == NULL) {
|
if (arena == NULL || data == NULL) {
|
||||||
ArenaPointer bad_pointer = {
|
ArenaPointer bad_pointer = {
|
||||||
@@ -91,35 +130,20 @@ ArenaPointer arena_push(Arena *arena, void *data, size_t size, size_t alignment)
|
|||||||
return bad_pointer;
|
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) {
|
if (!pointer.is_valid) {
|
||||||
return pointer;
|
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(
|
memcpy(
|
||||||
pointer.address,
|
pointer.address,
|
||||||
data,
|
data,
|
||||||
@@ -143,7 +167,7 @@ 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 = {
|
SizeResult err = {
|
||||||
.is_valid = false,
|
.is_valid = false,
|
||||||
@@ -160,11 +184,11 @@ SizeResult align_arena_offset(Arena *arena, size_t alignment) {
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uintptr_t current_address = (uintptr_t) (arena->buffer + arena->offset);
|
||||||
|
uintptr_t aligned = ((current_address + (alignment - 1)) & ~(alignment - 1));
|
||||||
SizeResult val = {
|
SizeResult val = {
|
||||||
.is_valid = true,
|
.is_valid = true,
|
||||||
.val = ((arena->offset + (alignment - 1)) & ~(alignment - 1)),
|
.val = aligned - current_address,
|
||||||
// Formula for alignment, found at stackoverflow, i came up with one
|
|
||||||
// but it involved 3 divisions, this one is way faster :)
|
|
||||||
};
|
};
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <setjmp.h>
|
#include <setjmp.h>
|
||||||
#include <cmocka.h>
|
#include <cmocka.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "arena.h"
|
#include "arena.h"
|
||||||
|
|
||||||
@@ -19,22 +20,61 @@ static void test_push_3_ints(void **state) {
|
|||||||
int int_to_push = 20;
|
int int_to_push = 20;
|
||||||
ArenaPointer result = arena_push(&arena, &int_to_push, sizeof(int), alignof(int));
|
ArenaPointer result = arena_push(&arena, &int_to_push, sizeof(int), alignof(int));
|
||||||
assert_true(result.is_valid);
|
assert_true(result.is_valid);
|
||||||
assert_int_equal(20, (int) *result.address);
|
assert_int_equal(20, *(int*)result.address);
|
||||||
|
|
||||||
int_to_push = 30;
|
int_to_push = 30;
|
||||||
result = arena_push(&arena, &int_to_push, sizeof(int), alignof(int));
|
result = arena_push(&arena, &int_to_push, sizeof(int), alignof(int));
|
||||||
assert_true(result.is_valid);
|
assert_true(result.is_valid);
|
||||||
assert_int_equal(30, (int) *result.address);
|
assert_int_equal(30, *(int*) result.address);
|
||||||
|
|
||||||
int_to_push = 40;
|
int_to_push = 40;
|
||||||
result = arena_push(&arena, &int_to_push, sizeof(int), alignof(int));
|
result = arena_push(&arena, &int_to_push, sizeof(int), alignof(int));
|
||||||
assert_true(result.is_valid);
|
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) {
|
int main(void) {
|
||||||
const struct CMUnitTest tests[] = {
|
const struct CMUnitTest tests[] = {
|
||||||
cmocka_unit_test(test_push_3_ints),
|
cmocka_unit_test(test_push_3_ints),
|
||||||
|
cmocka_unit_test(test_push_3_ints_2_doubles),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user