Add temp arena support

This commit is contained in:
2026-01-04 01:21:42 +00:00
parent d2b4ec2052
commit 24069529c3
12 changed files with 202 additions and 16 deletions

View File

@@ -24,11 +24,12 @@ typedef enum {
struct Arena { struct Arena {
u8 *buf; u8 *buf;
u8 *offset; u8 *offset;
u8 *prev_offset;
u64 capacity; u64 capacity;
ArenaStorageType type; ArenaStorageType type;
b8 committed; b8 committed;
wapp_misc_utils_reserve_padding(sizeof(u8 *) * 2 + sizeof(u64) + sizeof(ArenaStorageType) + sizeof(b8)); wapp_misc_utils_reserve_padding(sizeof(u8 *) * 3 + sizeof(u64) + sizeof(ArenaStorageType) + sizeof(b8));
}; };
b8 wapp_mem_arena_init_buffer(Arena **arena, u8 *buffer, u64 buffer_size) { b8 wapp_mem_arena_init_buffer(Arena **arena, u8 *buffer, u64 buffer_size) {
@@ -39,11 +40,12 @@ b8 wapp_mem_arena_init_buffer(Arena **arena, u8 *buffer, u64 buffer_size) {
*arena = (Arena *)buffer; *arena = (Arena *)buffer;
Arena *arena_ptr = *arena; Arena *arena_ptr = *arena;
arena_ptr->buf = (u8 *)(arena_ptr + 1); arena_ptr->buf = (u8 *)(arena_ptr + 1);
arena_ptr->offset = arena_ptr->buf; arena_ptr->offset = arena_ptr->buf;
arena_ptr->capacity = buffer_size - sizeof(Arena); arena_ptr->prev_offset = NULL;
arena_ptr->type = ARENA_STORAGE_TYPE_BUFFER; arena_ptr->capacity = buffer_size - sizeof(Arena);
arena_ptr->committed = true; arena_ptr->type = ARENA_STORAGE_TYPE_BUFFER;
arena_ptr->committed = true;
return true; return true;
} }
@@ -112,6 +114,8 @@ void *wapp_mem_arena_alloc_aligned(Arena *arena, u64 size, u64 alignment) {
} }
void *wapp_mem_arena_realloc(Arena *arena, void *ptr, u64 old_size, u64 new_size) { void *wapp_mem_arena_realloc(Arena *arena, void *ptr, u64 old_size, u64 new_size) {
wapp_debug_assert(arena != NULL, "`arena` should not be NULL");
if ((u8*)ptr < arena->buf || (u8*)ptr > arena->offset || if ((u8*)ptr < arena->buf || (u8*)ptr > arena->offset ||
arena->offset + new_size >= arena->buf + arena->capacity) { arena->offset + new_size >= arena->buf + arena->capacity) {
return NULL; return NULL;
@@ -129,6 +133,8 @@ void *wapp_mem_arena_realloc(Arena *arena, void *ptr, u64 old_size, u64 new_size
} }
void *wapp_mem_arena_realloc_aligned(Arena *arena, void *ptr, u64 old_size, u64 new_size, u64 alignment) { void *wapp_mem_arena_realloc_aligned(Arena *arena, void *ptr, u64 old_size, u64 new_size, u64 alignment) {
wapp_debug_assert(arena != NULL, "`arena` should not be NULL");
if ((u8*)ptr < arena->buf || (u8*)ptr > arena->offset || if ((u8*)ptr < arena->buf || (u8*)ptr > arena->offset ||
arena->offset + new_size >= arena->buf + arena->capacity) { arena->offset + new_size >= arena->buf + arena->capacity) {
return NULL; return NULL;
@@ -145,6 +151,27 @@ void *wapp_mem_arena_realloc_aligned(Arena *arena, void *ptr, u64 old_size, u64
return new_ptr; return new_ptr;
} }
void wapp_mem_arena_temp_begin(Arena *arena) {
wapp_debug_assert(arena != NULL, "`arena` should not be NULL");
if (arena->prev_offset != NULL) {
return;
}
arena->prev_offset = arena->offset;
}
void wapp_mem_arena_temp_end(Arena *arena) {
wapp_debug_assert(arena != NULL, "`arena` should not be NULL");
if (arena->prev_offset == NULL) {
return;
}
arena->offset = arena->prev_offset;
arena->prev_offset = NULL;
}
void wapp_mem_arena_clear(Arena *arena) { void wapp_mem_arena_clear(Arena *arena) {
wapp_debug_assert(arena != NULL, "`arena` should not be NULL"); wapp_debug_assert(arena != NULL, "`arena` should not be NULL");

View File

@@ -33,6 +33,8 @@ void *wapp_mem_arena_alloc(Arena *arena, u64 size);
void *wapp_mem_arena_alloc_aligned(Arena *arena, u64 size, u64 alignment); void *wapp_mem_arena_alloc_aligned(Arena *arena, u64 size, u64 alignment);
void *wapp_mem_arena_realloc(Arena *arena, void *ptr, u64 old_size, u64 new_size); void *wapp_mem_arena_realloc(Arena *arena, void *ptr, u64 old_size, u64 new_size);
void *wapp_mem_arena_realloc_aligned(Arena *arena, void *ptr, u64 old_size, u64 new_size, u64 alignment); void *wapp_mem_arena_realloc_aligned(Arena *arena, void *ptr, u64 old_size, u64 new_size, u64 alignment);
void wapp_mem_arena_temp_begin(Arena *arena);
void wapp_mem_arena_temp_end(Arena *arena);
void wapp_mem_arena_clear(Arena *arena); void wapp_mem_arena_clear(Arena *arena);
void wapp_mem_arena_destroy(Arena **arena); void wapp_mem_arena_destroy(Arena **arena);

View File

@@ -4,6 +4,7 @@
#include "mem_arena.h" #include "mem_arena.h"
#include "../../mem/mem_os.h" #include "../../mem/mem_os.h"
#include "../../../common/aliases/aliases.h" #include "../../../common/aliases/aliases.h"
#include "../../../common/assert/assert.h"
wapp_intern void initialise_arena_allocator(Allocator *allocator); wapp_intern void initialise_arena_allocator(Allocator *allocator);
wapp_intern void *mem_arena_alloc(u64 size, void *alloc_obj); wapp_intern void *mem_arena_alloc(u64 size, void *alloc_obj);
@@ -36,11 +37,23 @@ Allocator wapp_mem_arena_allocator_init_custom(u64 base_capacity, MemAllocFlags
return allocator; return allocator;
} }
void wapp_mem_arena_allocator_temp_begin(const Allocator *allocator) {
wapp_debug_assert(allocator != NULL, "`allocator` should not be NULL");
wapp_mem_arena_temp_begin((Arena *)(allocator->obj));
}
void wapp_mem_arena_allocator_temp_end(const Allocator *allocator) {
wapp_debug_assert(allocator != NULL, "`allocator` should not be NULL");
wapp_mem_arena_temp_end((Arena *)(allocator->obj));
}
void wapp_mem_arena_allocator_clear(Allocator *allocator) { void wapp_mem_arena_allocator_clear(Allocator *allocator) {
wapp_debug_assert(allocator != NULL, "`allocator` should not be NULL");
wapp_mem_arena_clear((Arena *)(allocator->obj)); wapp_mem_arena_clear((Arena *)(allocator->obj));
} }
void wapp_mem_arena_allocator_destroy(Allocator *allocator) { void wapp_mem_arena_allocator_destroy(Allocator *allocator) {
wapp_debug_assert(allocator != NULL, "`allocator` should not be NULL");
wapp_mem_arena_destroy((Arena **)(&(allocator->obj))); wapp_mem_arena_destroy((Arena **)(&(allocator->obj)));
*allocator = (Allocator){0}; *allocator = (Allocator){0};
} }

View File

@@ -34,6 +34,8 @@ BEGIN_C_LINKAGE
*/ */
Allocator wapp_mem_arena_allocator_init_custom(u64 base_capacity, MemAllocFlags flags, b8 zero_buffer); Allocator wapp_mem_arena_allocator_init_custom(u64 base_capacity, MemAllocFlags flags, b8 zero_buffer);
Allocator wapp_mem_arena_allocator_init_with_buffer(u8 *buffer, u64 buffer_size); Allocator wapp_mem_arena_allocator_init_with_buffer(u8 *buffer, u64 buffer_size);
void wapp_mem_arena_allocator_temp_begin(const Allocator *allocator);
void wapp_mem_arena_allocator_temp_end(const Allocator *allocator);
void wapp_mem_arena_allocator_clear(Allocator *allocator); void wapp_mem_arena_allocator_clear(Allocator *allocator);
void wapp_mem_arena_allocator_destroy(Allocator *allocator); void wapp_mem_arena_allocator_destroy(Allocator *allocator);

View File

@@ -2,6 +2,13 @@
#include "wapp.h" #include "wapp.h"
#include <stdlib.h> #include <stdlib.h>
// NOTE (Abdelrahman): Cannot query size of Arena here so it's hardcoded. Similarly, since arena
// allocation are aligned to power of 2 boundaries, the number of i32 values needed is hardcoded.
#define TEMP_BUF_SIZE (40 + sizeof(i32) * 8)
wapp_intern u8 temp_buf[TEMP_BUF_SIZE] = {0};
wapp_intern Allocator temp_allocator = {0};
TestFuncResult test_arena_allocator(void) { TestFuncResult test_arena_allocator(void) {
Allocator allocator = wapp_mem_arena_allocator_init(4096); Allocator allocator = wapp_mem_arena_allocator_init(4096);
b8 result = allocator.obj != NULL && allocator.alloc != NULL && b8 result = allocator.obj != NULL && allocator.alloc != NULL &&
@@ -31,3 +38,30 @@ TestFuncResult test_arena_allocator_with_buffer(void) {
return wapp_tester_result(result); return wapp_tester_result(result);
} }
TestFuncResult test_arena_allocator_temp_begin(void) {
temp_allocator = wapp_mem_arena_allocator_init_with_buffer(temp_buf, TEMP_BUF_SIZE);
i32 *num1 = (i32 *)wapp_mem_allocator_alloc(&temp_allocator, sizeof(i32));
b8 result = num1 != NULL;
wapp_mem_arena_allocator_temp_begin(&temp_allocator);
i32 *num2 = (i32 *)wapp_mem_allocator_alloc(&temp_allocator, sizeof(i32));
result = result && num2 != NULL;
i32 *num3 = (i32 *)wapp_mem_allocator_alloc(&temp_allocator, sizeof(i32));
result = result && num3 == NULL;
return wapp_tester_result(result);
}
TestFuncResult test_arena_allocator_temp_end(void) {
wapp_mem_arena_allocator_temp_end(&temp_allocator);
i32 *num1 = (i32 *)wapp_mem_allocator_alloc(&temp_allocator, sizeof(i32));
b8 result = num1 != NULL;
i32 *num2 = (i32 *)wapp_mem_allocator_alloc(&temp_allocator, sizeof(i32));
result = result && num2 == NULL;
wapp_mem_arena_allocator_destroy(&temp_allocator);
return wapp_tester_result(result);
}

View File

@@ -2,6 +2,13 @@
#include "wapp.h" #include "wapp.h"
#include <stdlib.h> #include <stdlib.h>
// NOTE (Abdelrahman): Cannot query size of Arena here so it's hardcoded. Similarly, since arena
// allocation are aligned to power of 2 boundaries, the number of i32 values needed is hardcoded.
#define TEMP_BUF_SIZE (40 + sizeof(i32) * 8)
wapp_intern u8 temp_buf[TEMP_BUF_SIZE] = {};
wapp_intern Allocator temp_allocator = {};
TestFuncResult test_arena_allocator(void) { TestFuncResult test_arena_allocator(void) {
Allocator allocator = wapp_mem_arena_allocator_init(4096); Allocator allocator = wapp_mem_arena_allocator_init(4096);
b8 result = allocator.obj != nullptr && allocator.alloc != nullptr && b8 result = allocator.obj != nullptr && allocator.alloc != nullptr &&
@@ -31,3 +38,30 @@ TestFuncResult test_arena_allocator_with_buffer(void) {
return wapp_tester_result(result); return wapp_tester_result(result);
} }
TestFuncResult test_arena_allocator_temp_begin(void) {
temp_allocator = wapp_mem_arena_allocator_init_with_buffer(temp_buf, TEMP_BUF_SIZE);
i32 *num1 = (i32 *)wapp_mem_allocator_alloc(&temp_allocator, sizeof(i32));
b8 result = num1 != NULL;
wapp_mem_arena_allocator_temp_begin(&temp_allocator);
i32 *num2 = (i32 *)wapp_mem_allocator_alloc(&temp_allocator, sizeof(i32));
result = result && num2 != NULL;
i32 *num3 = (i32 *)wapp_mem_allocator_alloc(&temp_allocator, sizeof(i32));
result = result && num3 == NULL;
return wapp_tester_result(result);
}
TestFuncResult test_arena_allocator_temp_end(void) {
wapp_mem_arena_allocator_temp_end(&temp_allocator);
i32 *num1 = (i32 *)wapp_mem_allocator_alloc(&temp_allocator, sizeof(i32));
b8 result = num1 != NULL;
i32 *num2 = (i32 *)wapp_mem_allocator_alloc(&temp_allocator, sizeof(i32));
result = result && num2 == NULL;
wapp_mem_arena_allocator_destroy(&temp_allocator);
return wapp_tester_result(result);
}

View File

@@ -5,5 +5,7 @@
TestFuncResult test_arena_allocator(void); TestFuncResult test_arena_allocator(void);
TestFuncResult test_arena_allocator_with_buffer(void); TestFuncResult test_arena_allocator_with_buffer(void);
TestFuncResult test_arena_allocator_temp_begin(void);
TestFuncResult test_arena_allocator_temp_end(void);
#endif // !TEST_ALLOCATOR_H #endif // !TEST_ALLOCATOR_H

View File

@@ -4,12 +4,17 @@
#define ARENA_CAPACITY KiB(16) #define ARENA_CAPACITY KiB(16)
#define ARENA_BUF_SIZE KiB(4) #define ARENA_BUF_SIZE KiB(4)
// NOTE (Abdelrahman): Cannot query size of Arena here so it's hardcoded. Similarly, since arena
// allocation are aligned to power of 2 boundaries, the number of i32 values needed is hardcoded.
#define TEMP_BUF_SIZE (40 + sizeof(i32) * 8)
wapp_intern Arena *arena = NULL; wapp_intern Arena *arena = NULL;
wapp_intern i32 count = 20; wapp_intern i32 count = 20;
wapp_intern i32 *array = NULL; wapp_intern i32 *array = NULL;
wapp_intern Arena *buf_arena = NULL; wapp_intern Arena *buf_arena = NULL;
wapp_intern u8 buf[ARENA_BUF_SIZE] = {0}; wapp_intern u8 buf[ARENA_BUF_SIZE] = {0};
wapp_intern Arena *temp_arena = NULL;
wapp_intern u8 temp_buf[TEMP_BUF_SIZE] = {0};
TestFuncResult test_arena_init_buffer(void) { TestFuncResult test_arena_init_buffer(void) {
b8 result = wapp_mem_arena_init_buffer(&buf_arena, buf, ARENA_BUF_SIZE); b8 result = wapp_mem_arena_init_buffer(&buf_arena, buf, ARENA_BUF_SIZE);
@@ -108,6 +113,32 @@ TestFuncResult test_arena_realloc_smaller_size(void) {
return wapp_tester_result(true); return wapp_tester_result(true);
} }
TestFuncResult test_arena_temp_begin(void) {
b8 result = wapp_mem_arena_init_buffer(&temp_arena, temp_buf, TEMP_BUF_SIZE);
i32 *num1 = (i32 *)wapp_mem_arena_alloc(temp_arena, sizeof(i32));
result = result && num1 != NULL;
wapp_mem_arena_temp_begin(temp_arena);
i32 *num2 = (i32 *)wapp_mem_arena_alloc(temp_arena, sizeof(i32));
result = result && num2 != NULL;
i32 *num3 = (i32 *)wapp_mem_arena_alloc(temp_arena, sizeof(i32));
result = result && num3 == NULL;
return wapp_tester_result(result);
}
TestFuncResult test_arena_temp_end(void) {
wapp_mem_arena_temp_end(temp_arena);
i32 *num1 = (i32 *)wapp_mem_arena_alloc(temp_arena, sizeof(i32));
b8 result = num1 != NULL;
i32 *num2 = (i32 *)wapp_mem_arena_alloc(temp_arena, sizeof(i32));
result = result && num2 == NULL;
wapp_mem_arena_destroy(&temp_arena);
return wapp_tester_result(result);
}
TestFuncResult test_arena_clear(void) { TestFuncResult test_arena_clear(void) {
wapp_mem_arena_clear(arena); wapp_mem_arena_clear(arena);
b8 result = true; b8 result = true;

View File

@@ -4,12 +4,17 @@
#define ARENA_CAPACITY KiB(16) #define ARENA_CAPACITY KiB(16)
#define ARENA_BUF_SIZE KiB(4) #define ARENA_BUF_SIZE KiB(4)
// NOTE (Abdelrahman): Cannot query size of Arena here so it's hardcoded. Similarly, since arena
// allocation are aligned to power of 2 boundaries, the number of i32 values needed is hardcoded.
#define TEMP_BUF_SIZE (40 + sizeof(i32) * 8)
wapp_intern Arena *arena = NULL; wapp_intern Arena *arena = NULL;
wapp_intern i32 count = 20; wapp_intern i32 count = 20;
wapp_intern i32 *array = NULL; wapp_intern i32 *array = NULL;
wapp_intern Arena *buf_arena = NULL; wapp_intern Arena *buf_arena = NULL;
wapp_intern u8 buf[ARENA_BUF_SIZE] = {0}; wapp_intern u8 buf[ARENA_BUF_SIZE] = {};
wapp_intern Arena *temp_arena = NULL;
wapp_intern u8 temp_buf[TEMP_BUF_SIZE] = {};
TestFuncResult test_arena_init_buffer(void) { TestFuncResult test_arena_init_buffer(void) {
b8 result = wapp_mem_arena_init_buffer(&buf_arena, buf, ARENA_BUF_SIZE); b8 result = wapp_mem_arena_init_buffer(&buf_arena, buf, ARENA_BUF_SIZE);
@@ -108,6 +113,32 @@ TestFuncResult test_arena_realloc_smaller_size(void) {
return wapp_tester_result(true); return wapp_tester_result(true);
} }
TestFuncResult test_arena_temp_begin(void) {
b8 result = wapp_mem_arena_init_buffer(&temp_arena, temp_buf, TEMP_BUF_SIZE);
i32 *num1 = (i32 *)wapp_mem_arena_alloc(temp_arena, sizeof(i32));
result = result && num1 != NULL;
wapp_mem_arena_temp_begin(temp_arena);
i32 *num2 = (i32 *)wapp_mem_arena_alloc(temp_arena, sizeof(i32));
result = result && num2 != NULL;
i32 *num3 = (i32 *)wapp_mem_arena_alloc(temp_arena, sizeof(i32));
result = result && num3 == NULL;
return wapp_tester_result(result);
}
TestFuncResult test_arena_temp_end(void) {
wapp_mem_arena_temp_end(temp_arena);
i32 *num1 = (i32 *)wapp_mem_arena_alloc(temp_arena, sizeof(i32));
b8 result = num1 != NULL;
i32 *num2 = (i32 *)wapp_mem_arena_alloc(temp_arena, sizeof(i32));
result = result && num2 == NULL;
wapp_mem_arena_destroy(&temp_arena);
return wapp_tester_result(result);
}
TestFuncResult test_arena_clear(void) { TestFuncResult test_arena_clear(void) {
wapp_mem_arena_clear(arena); wapp_mem_arena_clear(arena);
b8 result = true; b8 result = true;

View File

@@ -12,6 +12,8 @@ TestFuncResult test_arena_alloc_fails_when_over_capacity(void);
TestFuncResult test_arena_realloc_bigger_size(void); TestFuncResult test_arena_realloc_bigger_size(void);
TestFuncResult test_arena_realloc_smaller_size(void); TestFuncResult test_arena_realloc_smaller_size(void);
TestFuncResult test_arena_clear(void); TestFuncResult test_arena_clear(void);
TestFuncResult test_arena_temp_begin(void);
TestFuncResult test_arena_temp_end(void);
TestFuncResult test_arena_destroy_buffer(void); TestFuncResult test_arena_destroy_buffer(void);
TestFuncResult test_arena_destroy_allocated(void); TestFuncResult test_arena_destroy_allocated(void);

View File

@@ -14,6 +14,8 @@ int main(void) {
wapp_tester_run_tests( wapp_tester_run_tests(
test_arena_allocator, test_arena_allocator,
test_arena_allocator_with_buffer, test_arena_allocator_with_buffer,
test_arena_allocator_temp_begin,
test_arena_allocator_temp_end,
test_arena_init_buffer, test_arena_init_buffer,
test_arena_init_allocated, test_arena_init_allocated,
test_arena_init_succeeds_when_reserving_very_large_size, test_arena_init_succeeds_when_reserving_very_large_size,
@@ -22,6 +24,8 @@ int main(void) {
test_arena_alloc_fails_when_over_capacity, test_arena_alloc_fails_when_over_capacity,
test_arena_realloc_bigger_size, test_arena_realloc_bigger_size,
test_arena_realloc_smaller_size, test_arena_realloc_smaller_size,
test_arena_temp_begin,
test_arena_temp_end,
test_arena_clear, test_arena_clear,
test_arena_destroy_buffer, test_arena_destroy_buffer,
test_arena_destroy_allocated, test_arena_destroy_allocated,

View File

@@ -14,6 +14,8 @@ int main(void) {
wapp_tester_run_tests( wapp_tester_run_tests(
test_arena_allocator, test_arena_allocator,
test_arena_allocator_with_buffer, test_arena_allocator_with_buffer,
test_arena_allocator_temp_begin,
test_arena_allocator_temp_end,
test_arena_init_buffer, test_arena_init_buffer,
test_arena_init_allocated, test_arena_init_allocated,
test_arena_init_succeeds_when_reserving_very_large_size, test_arena_init_succeeds_when_reserving_very_large_size,
@@ -22,6 +24,8 @@ int main(void) {
test_arena_alloc_fails_when_over_capacity, test_arena_alloc_fails_when_over_capacity,
test_arena_realloc_bigger_size, test_arena_realloc_bigger_size,
test_arena_realloc_smaller_size, test_arena_realloc_smaller_size,
test_arena_temp_begin,
test_arena_temp_end,
test_arena_clear, test_arena_clear,
test_arena_destroy_buffer, test_arena_destroy_buffer,
test_arena_destroy_allocated, test_arena_destroy_allocated,