diff --git a/.gitignore b/.gitignore index 0bd57ed..d1a83ec 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,12 @@ out/ out/Debug/ out/Release/ +# xmake +xmake/ +xmake/* +.xmake +.xmake/* + # Cmake files CMakeCache.txt CMakeFiles/ diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index b350ff7..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,97 +0,0 @@ -cmake_minimum_required(VERSION 3.20) -project( - arena - VERSION 1.0 - LANGUAGES C) - -# ------------------------------------------------ -# C standard — no compiler extensions -# ------------------------------------------------ -set(CMAKE_C_STANDARD 11) -set(CMAKE_C_STANDARD_REQUIRED ON) -set(CMAKE_C_EXTENSIONS OFF) - -# Export compile_commands.json (clangd / IDEs) -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - -# ------------------------------------------------ -# Is this the root project or embedded (CPM / FetchContent)? -# ------------------------------------------------ -if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) - set(ARENA_IS_TOP_LEVEL ON) -else() - set(ARENA_IS_TOP_LEVEL OFF) -endif() - -# ------------------------------------------------ -# Options -# ------------------------------------------------ -option(ARENA_BUILD_TESTS "Build arena tests" ${ARENA_IS_TOP_LEVEL}) -option(ARENA_BUILD_EXAMPLE "Build arena example" ${ARENA_IS_TOP_LEVEL}) -option(ARENA_ENABLE_INSTALL "Install arena" ${ARENA_IS_TOP_LEVEL}) -option(ARENA_ENABLE_SANITIZERS "Enable Address Sanitizer in tests" ON) - -# ------------------------------------------------ -# Portable warning flags -# ------------------------------------------------ -if(MSVC) - set(ARENA_WARNING_FLAGS /W4) -else() - set(ARENA_WARNING_FLAGS -Wall -Wextra -Wpedantic) -endif() - -# ================================================================ -# LIBRARY -# ================================================================ -add_library(arena src/arena.c) - -# Namespaced alias → consumers can write arena::arena -add_library(arena::arena ALIAS arena) - -target_include_directories( - arena PUBLIC $ - $) - -# Propagate C11 requirement to consumers -target_compile_features(arena PUBLIC c_std_11) - -# Warnings only for the lib itself, not propagated -target_compile_options(arena PRIVATE ${ARENA_WARNING_FLAGS}) - -# ================================================================ -# EXAMPLE (optional, only when top-level) -# ================================================================ -if(ARENA_BUILD_EXAMPLE) - add_executable(arena_example src/main.c) - target_link_libraries(arena_example PRIVATE arena) -endif() - -# ================================================================ -# TESTS -# ================================================================ -if(ARENA_BUILD_TESTS) - enable_testing() - add_subdirectory(test) -endif() - -# ================================================================ -# INSTALL -# ================================================================ -if(ARENA_ENABLE_INSTALL) - include(GNUInstallDirs) - - install( - TARGETS arena - EXPORT arenaTargets - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) - - install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - - install( - EXPORT arenaTargets - FILE arenaTargets.cmake - NAMESPACE arena:: - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/arena) -endif() diff --git a/include/arena.h b/include/lae_arena.h similarity index 51% rename from include/arena.h rename to include/lae_arena.h index a9a03fe..d367551 100644 --- a/include/arena.h +++ b/include/lae_arena.h @@ -1,6 +1,7 @@ -#ifndef ARENA_H -#define ARENA_H +#ifndef LAE_ARENA_H +#define LAE_ARENA_H +#include "lae_allocator.h" #include #include #include @@ -16,6 +17,7 @@ typedef enum { ARENA_INVALID_SIZE, ARENA_INVALID_ALIGN, ARENA_CAPACITY_OVERFLOW, + ARENA_LOCKED, } ArenaErr; typedef struct { @@ -23,7 +25,7 @@ typedef struct { union { ArenaErr err; struct { - Arena* arena; + Arena *arena; size_t offset; }; }; @@ -31,34 +33,40 @@ typedef struct { typedef struct { bool is_valid; + union { ArenaErr err; - Arena* arena; + Allocator allocator; }; -} ArenaResult; +} ArenaAllocator; typedef struct { bool is_valid; union { ArenaErr err; - size_t val; + size_t val; }; } ArenaSizeResult; -ArenaErr arena_init(Arena** arena, size_t capacity); -ArenaErr arena_destroy(Arena** arena); +ArenaErr arena_init(Arena **arena, Allocator allocator, size_t capacity); +ArenaErr 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); ArenaPointer - arena_push(Arena* arena, void* data, size_t size, size_t alignment); -ArenaErr arena_realloc(Arena* arena, size_t new_capacity); -void* arena_unwrap_pointer(ArenaPointer p); +arena_push_data(Arena *arena, void *data, size_t size, size_t alignment); +ArenaErr arena_resize(Arena *arena, size_t new_capacity); +void *arena_unwrap_pointer(ArenaPointer p); -ArenaSizeResult arena_get_align_padding(Arena* arena, size_t alignment); +ArenaSizeResult arena_get_align_padding(Arena *arena, size_t alignment); // can take big sizes but of sourse there is a limit, // its protected against SIZE_MAX but anything that // is big may break it, still, is an edge case that // I shouldn't cover. -ArenaErr arena_ensure_capacity(Arena* arena, size_t size, size_t alignment); +ArenaErr arena_ensure_capacity(Arena *arena, size_t size, size_t alignment); -#endif // !ARENA_H +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/arena.c b/src/arena.c deleted file mode 100644 index 7d7c34d..0000000 --- a/src/arena.c +++ /dev/null @@ -1,232 +0,0 @@ -#include "arena.h" -#include -#include -#include -#include -#include - -struct Arena { - uint8_t *buffer; - size_t capacity; - size_t offset; -}; - - -ArenaErr arena_init(Arena **arena, size_t capacity) { - if (arena == NULL) { - return ARENA_NULL_ARG; - } - - if (capacity < 1) { - return ARENA_INVALID_SIZE; - } - - Arena *new_arena = malloc(sizeof(Arena)); - if (new_arena == NULL) { - return ARENA_BAD_ALLOC; - } - - void *buffer = malloc(capacity); - if (buffer == NULL) { - return ARENA_BAD_ALLOC; - } - - *new_arena = (Arena) { - .buffer = buffer, - .capacity = capacity, - .offset = 0, - }; - - *arena = new_arena; - - return ARENA_OK; -} - -ArenaErr arena_destroy(Arena **arena) { - if (arena == NULL || *arena == NULL) { - return ARENA_NULL_ARG; - } - - free((*arena)->buffer); - (*arena)->buffer = NULL; - (*arena)->offset = 0; - (*arena)->capacity = 0; - free(*arena); - *arena = NULL; - - return ARENA_OK; -} - -ArenaPointer arena_alloc(Arena *arena, size_t size, size_t alignment) { - if (arena == NULL) { - return (ArenaPointer) {.is_valid = false, .err = ARENA_NULL_ARG}; - } - - if (size < 1) { - return (ArenaPointer) {.is_valid = false, .err = ARENA_INVALID_SIZE}; - } - - if (alignment < 1) { - return (ArenaPointer) {.is_valid = false, .err = ARENA_INVALID_ALIGN}; - } - - ArenaSizeResult padding = arena_get_align_padding(arena, alignment); - if (!padding.is_valid) { - return (ArenaPointer) {.is_valid = false, .err = padding.err}; - } - - ArenaErr cap_err = arena_ensure_capacity(arena, size, alignment); - if(cap_err != ARENA_OK) { - return (ArenaPointer) {.is_valid = false, .err = ARENA_OUT_OF_SPACE}; - } - - if (arena->offset + padding.val + size > arena->capacity) { - return (ArenaPointer) {.is_valid = false, .err = ARENA_OUT_OF_SPACE}; - } - - if (arena->offset > SIZE_MAX - padding.val - size) { - return (ArenaPointer) {.is_valid = false, .err = ARENA_CAPACITY_OVERFLOW}; - } - - size_t aligned_offset = arena->offset + padding.val; - - arena->offset = aligned_offset + size; - return (ArenaPointer) {.is_valid = true, .offset = aligned_offset, .arena = arena}; -} - -ArenaErr arena_ensure_capacity(Arena *arena, size_t size, size_t alignment) { - if (arena == NULL) { - return ARENA_NULL_ARG; - } - - if (size == 0 || size == SIZE_MAX) { - return ARENA_INVALID_SIZE; - } - - if (alignment == 0 || (alignment & (alignment - 1)) != 0) { - return ARENA_INVALID_ALIGN; - } - - ArenaSizeResult padding_res = arena_get_align_padding(arena, alignment); - if (!padding_res.is_valid) { - return padding_res.err; - } - - size_t padding = padding_res.val; - - while (true) { - size_t required; - - if (arena->offset > SIZE_MAX - padding) { - return ARENA_CAPACITY_OVERFLOW; - } - required = arena->offset + padding; - - if (required > SIZE_MAX - size) { - return ARENA_CAPACITY_OVERFLOW; - } - required += size; - - if (required <= arena->capacity) { - return ARENA_OK; - } - - size_t new_capacity; - - if (arena->capacity > SIZE_MAX / 2) { - return ARENA_CAPACITY_OVERFLOW; - } - - new_capacity = arena->capacity * 2; - - if (new_capacity < required) { - new_capacity = required; - } - - 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) { - return (ArenaPointer) {.is_valid = false, .err = ARENA_NULL_ARG}; - } - - if (size < 1) { - return (ArenaPointer) {.is_valid = false, .err = ARENA_INVALID_SIZE}; - } - - if (size < 1) { - return (ArenaPointer) {.is_valid = false, .err = ARENA_INVALID_ALIGN}; - } - - 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( - arena_unwrap_pointer(pointer), - data, - size); - - return pointer; -} - -ArenaErr arena_realloc(Arena *arena, size_t new_capacity) { - if (arena == NULL) { - return ARENA_NULL_ARG; - } - - if (new_capacity < 1) { - return ARENA_INVALID_SIZE; - } - - uint8_t *tmp = realloc(arena->buffer, new_capacity); - if (tmp == NULL) { - return ARENA_BAD_ALLOC; - } - - arena->buffer = tmp; - arena->capacity = new_capacity; - return ARENA_OK; -} - - - -ArenaSizeResult arena_get_align_padding(Arena *arena, size_t alignment) { - if (arena == NULL) { - return (ArenaSizeResult){ .is_valid = false, .err = ARENA_NULL_ARG }; - } - - if (alignment == 0 || (alignment & (alignment - 1)) != 0) { - return (ArenaSizeResult){ .is_valid = false, .err = ARENA_INVALID_ALIGN }; - } - - uintptr_t current_address = (uintptr_t)(arena->buffer + arena->offset); - - size_t mask = alignment - 1; - size_t padding = (size_t)(-current_address) & mask; - - return (ArenaSizeResult){ - .is_valid = true, - .val = padding - }; -} - -void *arena_unwrap_pointer(ArenaPointer p) { - if (!p.is_valid) { - return NULL; - } - return p.arena->buffer + p.offset; -} - - diff --git a/src/lae_arena.c b/src/lae_arena.c new file mode 100644 index 0000000..6214628 --- /dev/null +++ b/src/lae_arena.c @@ -0,0 +1,313 @@ +#include "lae_arena.h" +#include "lae_allocator.h" +#include +#include +#include +#include +#include +#include + +struct Arena { + uint8_t *buffer; + size_t capacity; + size_t offset; + + bool growth_locked; + Allocator allocator; +}; + +static void *arena_allocator_alloc(void *ctx, size_t size, size_t alignment); +static void *arena_allocator_realloc( + void *ctx, + void *pointer, + size_t old_size, + size_t new_size, + size_t alignment); +static void +arena_allocator_free(void *ctx, void *pointer, size_t size, size_t alignment); + +ArenaErr arena_init(Arena **arena, Allocator allocator, size_t capacity) { + if (arena == NULL) { + return ARENA_NULL_ARG; + } + + if (capacity < 1) { + return ARENA_INVALID_SIZE; + } + + Arena *new_arena = + allocator.alloc(allocator.ctx, sizeof(Arena), alignof(Arena)); + if (new_arena == NULL) { + return ARENA_BAD_ALLOC; + } + + void *buffer = allocator.alloc(allocator.ctx, capacity, 0); + if (buffer == NULL) { + allocator.free(allocator.ctx, new_arena, capacity, 0); + return ARENA_BAD_ALLOC; + } + + *new_arena = (Arena){ + .buffer = buffer, + .capacity = capacity, + .offset = 0, + .growth_locked = false, + .allocator = allocator, + }; + + *arena = new_arena; + + return ARENA_OK; +} + +ArenaErr arena_destroy(Arena **arena) { + if (arena == NULL || *arena == NULL) { + return ARENA_NULL_ARG; + } + + Arena *tmp = *arena; + tmp->allocator.free(tmp->allocator.ctx, tmp->buffer, tmp->capacity, 0); + 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; + + return ARENA_OK; +} + +ArenaPointer arena_alloc(Arena *arena, size_t size, size_t alignment) { + if (arena == NULL) { + return (ArenaPointer){.is_valid = false, .err = ARENA_NULL_ARG}; + } + + if (size < 1) { + return (ArenaPointer){.is_valid = false, .err = ARENA_INVALID_SIZE}; + } + + if (alignment < 1) { + return (ArenaPointer){.is_valid = false, .err = ARENA_INVALID_ALIGN}; + } + + ArenaSizeResult padding = arena_get_align_padding(arena, alignment); + if (!padding.is_valid) { + return (ArenaPointer){.is_valid = false, .err = padding.err}; + } + + ArenaErr cap_err = arena_ensure_capacity(arena, size, alignment); + if (cap_err != ARENA_OK) { + return (ArenaPointer){.is_valid = false, .err = ARENA_OUT_OF_SPACE}; + } + + if (arena->offset + padding.val + size > arena->capacity) { + return (ArenaPointer){.is_valid = false, .err = ARENA_OUT_OF_SPACE}; + } + + if (arena->offset > SIZE_MAX - padding.val - size) { + return (ArenaPointer){.is_valid = false, + .err = ARENA_CAPACITY_OVERFLOW}; + } + + size_t aligned_offset = arena->offset + padding.val; + + arena->offset = aligned_offset + size; + return (ArenaPointer){.is_valid = true, + .offset = aligned_offset, + .arena = arena}; +} + +ArenaErr arena_ensure_capacity(Arena *arena, size_t size, size_t alignment) { + if (arena == NULL) { + return ARENA_NULL_ARG; + } + + if (size == 0 || size == SIZE_MAX) { + return ARENA_INVALID_SIZE; + } + + if (alignment == 0 || (alignment & (alignment - 1)) != 0) { + return ARENA_INVALID_ALIGN; + } + + ArenaSizeResult padding_res = arena_get_align_padding(arena, alignment); + if (!padding_res.is_valid) { + return padding_res.err; + } + + size_t padding = padding_res.val; + + while (true) { + size_t required; + + if (arena->offset > SIZE_MAX - padding) { + return ARENA_CAPACITY_OVERFLOW; + } + required = arena->offset + padding; + + if (required > SIZE_MAX - size) { + return ARENA_CAPACITY_OVERFLOW; + } + required += size; + + if (required <= arena->capacity) { + return ARENA_OK; + } + + size_t new_capacity; + + if (arena->capacity > SIZE_MAX / 2) { + return ARENA_CAPACITY_OVERFLOW; + } + + new_capacity = arena->capacity * 2; + + if (new_capacity < required) { + new_capacity = required; + } + + ArenaErr err = arena_resize(arena, new_capacity); + if (err != ARENA_OK) { + return err; + } + } +} + +ArenaPointer +arena_push_data(Arena *arena, void *data, size_t size, size_t alignment) { + if (arena == NULL || data == NULL) { + return (ArenaPointer){.is_valid = false, .err = ARENA_NULL_ARG}; + } + + if (size < 1) { + return (ArenaPointer){.is_valid = false, .err = ARENA_INVALID_SIZE}; + } + + if (size < 1) { + return (ArenaPointer){.is_valid = false, .err = ARENA_INVALID_ALIGN}; + } + + 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(arena_unwrap_pointer(pointer), data, size); + + return pointer; +} + +ArenaErr arena_resize(Arena *arena, size_t new_capacity) { + if (arena == NULL) { + return ARENA_NULL_ARG; + } + + if (new_capacity < 1) { + return ARENA_INVALID_SIZE; + } + + if (arena->growth_locked) { + return ARENA_LOCKED; + } + + uint8_t *tmp = arena->allocator.realloc( + arena->allocator.ctx, + arena->buffer, + arena->capacity, + new_capacity, + 0); + if (tmp == NULL) { + return ARENA_BAD_ALLOC; + } + + arena->buffer = tmp; + arena->capacity = new_capacity; + return ARENA_OK; +} + +ArenaSizeResult arena_get_align_padding(Arena *arena, size_t alignment) { + if (arena == NULL) { + return (ArenaSizeResult){.is_valid = false, .err = ARENA_NULL_ARG}; + } + + if (alignment == 0 || (alignment & (alignment - 1)) != 0) { + return (ArenaSizeResult){.is_valid = false, .err = ARENA_INVALID_ALIGN}; + } + + uintptr_t current_address = (uintptr_t)(arena->buffer + arena->offset); + + size_t mask = alignment - 1; + size_t padding = (size_t)(-current_address) & mask; + + 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; + } + return p.arena->buffer + p.offset; +} + +static void *arena_allocator_alloc(void *ctx, size_t size, size_t alignment) { + Arena *arena = ctx; + + return arena_unwrap_pointer(arena_alloc(arena, size, alignment)); +} + +static void *arena_allocator_realloc( + void *ctx, + void *pointer, + size_t old_size, + size_t new_size, + size_t alignment) { + return NULL; +} + +static void +arena_allocator_free(void *ctx, void *pointer, size_t size, size_t alignment) { + return; +} + +ArenaAllocator arena_get_allocator(Arena *arena) { + if (arena == NULL) { + 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 (ArenaAllocator){.is_valid = true, .allocator = allocator}; +} diff --git a/src/main.c b/src/main.c index 02a75d8..c5bc80b 100644 --- a/src/main.c +++ b/src/main.c @@ -1,4 +1,4 @@ -#include "arena.h" +#include "lae_arena.h" #include int main(void) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt deleted file mode 100644 index 94b0088..0000000 --- a/test/CMakeLists.txt +++ /dev/null @@ -1,107 +0,0 @@ -# ================================================================ -# FIND CMOCKA (system → FetchContent as fallback) -# ================================================================ -find_package(cmocka QUIET) - -if(NOT cmocka_FOUND) - message( - STATUS "[arena] cmocka not found on system — fetching with FetchContent...") - - include(FetchContent) - FetchContent_Declare( - cmocka - GIT_REPOSITORY https://git.cryptomilk.org/projects/cmocka.git - GIT_TAG cmocka-1.1.7 - GIT_SHALLOW TRUE) - - # Static lib only, no cmocka examples or self-tests - set(WITH_STATIC_LIB - ON - CACHE BOOL "" FORCE) - set(WITH_SHARED_LIB - OFF - CACHE BOOL "" FORCE) - set(WITH_CMOCKERY_SUPPORT - OFF - CACHE BOOL "" FORCE) - set(WITH_EXAMPLES - OFF - CACHE BOOL "" FORCE) - set(UNIT_TESTING - OFF - CACHE BOOL "" FORCE) - set(PICKY_DEVELOPER - OFF - CACHE BOOL "" FORCE) - - FetchContent_MakeAvailable(cmocka) - - # Normalize the target name (varies across cmocka versions) - if(NOT TARGET cmocka::cmocka) - if(TARGET cmocka-static) - add_library(cmocka::cmocka ALIAS cmocka-static) - elseif(TARGET cmocka) - add_library(cmocka::cmocka ALIAS cmocka) - else() - message( - WARNING - "[arena] Could not create cmocka::cmocka target — skipping tests.") - return() - endif() - endif() -endif() - -# ================================================================ -# DETECT ADDRESS SANITIZER SUPPORT -# ================================================================ -set(ARENA_USE_ASAN OFF) - -if(ARENA_ENABLE_SANITIZERS) - if(MSVC) - if(MSVC_VERSION GREATER_EQUAL 1928) - set(ARENA_USE_ASAN ON) - else() - message(STATUS "[arena] MSVC < 16.9: ASAN not available.") - endif() - elseif(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang" AND NOT WIN32) - include(CheckCCompilerFlag) - check_c_compiler_flag(-fsanitize=address ARENA_COMPILER_HAS_ASAN) - if(ARENA_COMPILER_HAS_ASAN) - set(ARENA_USE_ASAN ON) - else() - message(STATUS "[arena] Compiler does not support -fsanitize=address.") - endif() - else() - message(STATUS "[arena] Unknown platform/compiler — ASAN skipped.") - endif() - - if(ARENA_USE_ASAN) - message(STATUS "[arena] Address Sanitizer enabled.") - endif() -endif() - -# ================================================================ -# TEST EXECUTABLE -# ================================================================ -add_executable(test_arena test_arena.c) - -target_link_libraries(test_arena PRIVATE arena cmocka::cmocka) - -target_compile_features(test_arena PRIVATE c_std_11) - -# Apply ASAN flags per compiler -if(ARENA_USE_ASAN) - if(MSVC) - target_compile_options(test_arena PRIVATE /fsanitize=address) - else() - target_compile_options(test_arena PRIVATE -fsanitize=address - -fno-omit-frame-pointer) - target_link_options(test_arena PRIVATE -fsanitize=address) - endif() -endif() - -# ================================================================ -# CTEST REGISTRATION -# ================================================================ -add_test(NAME arena_tests COMMAND test_arena) -set_tests_properties(arena_tests PROPERTIES TIMEOUT 30) diff --git a/test/test_arena.c b/test/test_arena.c index 756b524..18697d6 100644 --- a/test/test_arena.c +++ b/test/test_arena.c @@ -1,90 +1,106 @@ +#include +#include +#include #include -#include #include #include #include #include -#include -#include #include -#include -#include "arena.h" +#include "lae_allocator.h" +#include "lae_arena.h" static void test_push_3_ints(void **state) { - (void) state; + (void)state; Arena *arena; - assert_uint_equal(arena_init(&arena, sizeof(int) * 3), ARENA_OK); + assert_uint_equal( + arena_init(&arena, allocator_default(), sizeof(int) * 3), + ARENA_OK); int int_to_push = 20; - ArenaPointer result = arena_push(arena, &int_to_push, sizeof(int), alignof(int)); + ArenaPointer result = + arena_push_data(arena, &int_to_push, sizeof(int), alignof(int)); assert_true(result.is_valid); - assert_int_equal(20, *(int*)arena_unwrap_pointer(result)); - assert_int_equal(20, *(int*)arena_unwrap_pointer(result)); + assert_int_equal(20, *(int *)arena_unwrap_pointer(result)); + assert_int_equal(20, *(int *)arena_unwrap_pointer(result)); int_to_push = 30; - result = arena_push(arena, &int_to_push, sizeof(int), alignof(int)); + result = arena_push_data(arena, &int_to_push, sizeof(int), alignof(int)); assert_true(result.is_valid); - assert_int_equal(30, *(int*)arena_unwrap_pointer(result)); + assert_int_equal(30, *(int *)arena_unwrap_pointer(result)); int_to_push = 40; - result = arena_push(arena, &int_to_push, sizeof(int), alignof(int)); + result = arena_push_data(arena, &int_to_push, sizeof(int), alignof(int)); assert_true(result.is_valid); - assert_int_equal(40, *(int*)arena_unwrap_pointer(result)); + assert_int_equal(40, *(int *)arena_unwrap_pointer(result)); arena_destroy(&arena); } static void test_push_3_ints_2_doubles(void **state) { - (void) state; + (void)state; Arena *arena; assert_uint_equal( - arena_init(&arena, (sizeof(int) * 3) + (sizeof(double) * 2)), - ARENA_OK - ); + arena_init( + &arena, + allocator_default(), + (sizeof(int) * 3) + (sizeof(double) * 2)), + ARENA_OK); int int_to_push = 20; - ArenaPointer result = arena_push(arena, &int_to_push, sizeof(int), alignof(int)); + ArenaPointer result = + arena_push_data(arena, &int_to_push, sizeof(int), alignof(int)); assert_true(result.is_valid); - assert_int_equal(20, *(int*)arena_unwrap_pointer(result)); + assert_int_equal(20, *(int *)arena_unwrap_pointer(result)); double double_to_push = 4.57; - result = arena_push(arena, &double_to_push, sizeof(double), alignof(double)); + result = arena_push_data( + arena, + &double_to_push, + sizeof(double), + alignof(double)); assert_true(result.is_valid); - assert_double_equal(4.57, *(double*)arena_unwrap_pointer(result), 1e-6); + assert_double_equal(4.57, *(double *)arena_unwrap_pointer(result), 1e-6); int_to_push = 30; - result = arena_push(arena, &int_to_push, sizeof(int), alignof(int)); + result = arena_push_data(arena, &int_to_push, sizeof(int), alignof(int)); assert_true(result.is_valid); - assert_int_equal(30, *(int*)arena_unwrap_pointer(result)); + assert_int_equal(30, *(int *)arena_unwrap_pointer(result)); int_to_push = 40; - result = arena_push(arena, &int_to_push, sizeof(int), alignof(int)); + result = arena_push_data(arena, &int_to_push, sizeof(int), alignof(int)); assert_true(result.is_valid); - assert_int_equal(40, *(int*)arena_unwrap_pointer(result)); + assert_int_equal(40, *(int *)arena_unwrap_pointer(result)); double_to_push = 267.33; - result = arena_push(arena, &double_to_push, sizeof(double), alignof(double)); + result = arena_push_data( + arena, + &double_to_push, + sizeof(double), + alignof(double)); assert_true(result.is_valid); - assert_double_equal(267.33, *(double*)arena_unwrap_pointer(result), 1e-6); + assert_double_equal(267.33, *(double *)arena_unwrap_pointer(result), 1e-6); arena_destroy(&arena); } static void test_init_arena_0_cap(void **state) { - (void) state; + (void)state; Arena *arena; - assert_int_equal(arena_init(&arena, 0), ARENA_INVALID_SIZE); + assert_int_equal( + arena_init(&arena, allocator_default(), 0), + ARENA_INVALID_SIZE); } static void test_arena_alloc_size_0(void **state) { - (void) state; + (void)state; Arena *arena; - assert_uint_equal(arena_init(&arena, 64), ARENA_OK); + assert_uint_equal(arena_init(&arena, allocator_default(), 64), ARENA_OK); ArenaPointer pointer = arena_alloc(arena, 0, alignof(int)); assert_false(pointer.is_valid); @@ -94,10 +110,10 @@ static void test_arena_alloc_size_0(void **state) { } static void test_arena_alloc_size_max(void **state) { - (void) state; + (void)state; Arena *arena; - assert_uint_equal(arena_init(&arena, 64), ARENA_OK); + assert_uint_equal(arena_init(&arena, allocator_default(), 64), ARENA_OK); ArenaPointer pointer = arena_alloc(arena, SIZE_MAX, alignof(int)); assert_false(pointer.is_valid); @@ -107,10 +123,10 @@ static void test_arena_alloc_size_max(void **state) { } static void test_arena_alloc_align_0(void **state) { - (void) state; + (void)state; Arena *arena; - assert_uint_equal(arena_init(&arena, 64), ARENA_OK); + assert_uint_equal(arena_init(&arena, allocator_default(), 64), ARENA_OK); ArenaPointer pointer = arena_alloc(arena, sizeof(int), 0); assert_false(pointer.is_valid); @@ -120,10 +136,10 @@ static void test_arena_alloc_align_0(void **state) { } static void test_arena_alloc_align_max(void **state) { - (void) state; + (void)state; Arena *arena; - assert_uint_equal(arena_init(&arena, 64), ARENA_OK); + assert_uint_equal(arena_init(&arena, allocator_default(), 64), ARENA_OK); ArenaPointer pointer = arena_alloc(arena, sizeof(int), SIZE_MAX); assert_false(pointer.is_valid); @@ -133,10 +149,10 @@ static void test_arena_alloc_align_max(void **state) { } static void test_arena_align_size_0(void **state) { - (void) state; + (void)state; Arena *arena; - assert_uint_equal(arena_init(&arena, 64), ARENA_OK); + assert_uint_equal(arena_init(&arena, allocator_default(), 64), ARENA_OK); ArenaPointer pointer = arena_alloc(arena, sizeof(int), 0); assert_false(pointer.is_valid); @@ -146,10 +162,10 @@ static void test_arena_align_size_0(void **state) { } static void test_arena_ensure_capacity_size_0(void **state) { - (void) state; + (void)state; Arena *arena; - assert_uint_equal(arena_init(&arena, 64), ARENA_OK); + assert_uint_equal(arena_init(&arena, allocator_default(), 64), ARENA_OK); ArenaErr err = arena_ensure_capacity(arena, 0, alignof(int)); assert_int_equal(err, ARENA_INVALID_SIZE); @@ -158,10 +174,10 @@ static void test_arena_ensure_capacity_size_0(void **state) { } static void test_arena_ensure_capacity_size_max(void **state) { - (void) state; + (void)state; Arena *arena; - assert_uint_equal(arena_init(&arena, 64), ARENA_OK); + assert_uint_equal(arena_init(&arena, allocator_default(), 64), ARENA_OK); ArenaErr err = arena_ensure_capacity(arena, SIZE_MAX, alignof(int)); assert_int_equal(err, ARENA_INVALID_SIZE); @@ -170,10 +186,10 @@ static void test_arena_ensure_capacity_size_max(void **state) { } static void test_arena_ensure_capacity_align_0(void **state) { - (void) state; + (void)state; Arena *arena; - assert_uint_equal(arena_init(&arena, 64), ARENA_OK); + assert_uint_equal(arena_init(&arena, allocator_default(), 64), ARENA_OK); ArenaErr err = arena_ensure_capacity(arena, sizeof(int), 0); assert_int_equal(err, ARENA_INVALID_ALIGN); @@ -182,10 +198,10 @@ static void test_arena_ensure_capacity_align_0(void **state) { } static void test_arena_ensure_capacity_align_max(void **state) { - (void) state; + (void)state; Arena *arena; - assert_uint_equal(arena_init(&arena, 64), ARENA_OK); + assert_uint_equal(arena_init(&arena, allocator_default(), 64), ARENA_OK); ArenaErr err = arena_ensure_capacity(arena, sizeof(int), SIZE_MAX); assert_int_equal(err, ARENA_INVALID_ALIGN); @@ -193,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), @@ -207,8 +276,8 @@ 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); } diff --git a/xmake.lua b/xmake.lua new file mode 100644 index 0000000..6001592 --- /dev/null +++ b/xmake.lua @@ -0,0 +1,94 @@ +set_project("lae_arena") +set_version("1.0.0") + +set_languages("c11") + +add_rules("mode.debug", "mode.release") + +-- clang compile commands +add_rules("plugin.compile_commands.autoupdate") + +-- ========================================================= +-- Options +-- ========================================================= + +option("tests") +set_default(false) +set_showmenu(true) +option_end() + +option("example") +set_default(false) +set_showmenu(true) +option_end() + +option("asan") +set_default(false) +set_showmenu(true) +option_end() + +-- ========================================================= +-- Packages +-- ========================================================= + +-- Run xmake repo --add lae-repo https://laentropia-homelab.tail7368da.ts.net/laentropia/xmake-repo.git +-- to add my repo :) + +add_requires("cmocka", { optional = true }) -- For tests +add_requires("lae_allocator") + +-- ========================================================= +-- Library +-- ========================================================= + +target("lae_arena") +set_kind("static") + +add_packages("lae_allocator", { public = true }) + +add_files("src/lae_arena.c") + +add_headerfiles("include/*.h") + +add_includedirs("include", { public = true }) + +if is_mode("debug") then + add_cflags("-Wall", "-Wextra", "-Wpedantic") +end + +-- ========================================================= +-- Example executable +-- ========================================================= + +if has_config("example") then + target("example") + set_kind("binary") + + add_deps("lae_arena") + add_packages("lae_allocator") + + add_files("src/main.c") +end + +-- ========================================================= +-- Tests +-- ========================================================= + +if has_config("tests") then + target("test_arena") + set_kind("binary") + + add_files("test/test_arena.c") + + add_deps("lae_arena") + + add_packages("cmocka") + + add_tests("default") + + if has_config("asan") then + add_cflags("-fsanitize=address", "-fno-omit-frame-pointer", { force = true }) + + add_ldflags("-fsanitize=address", { force = true }) + end +end