Add support for initialising arena with backing buffer

This commit is contained in:
2026-01-03 18:36:30 +00:00
parent 83b879a180
commit 326265722e
12 changed files with 195 additions and 61 deletions

View File

@@ -6,7 +6,6 @@
#include "../../../common/assert/assert.h"
#include "../../../common/misc/misc_utils.h"
#include "../../../base/mem/utils/mem_utils.h"
#include <stdlib.h>
#include <string.h>
#ifndef DEFAULT_ALIGNMENT
@@ -17,43 +16,68 @@
#define ARENA_MINIMUM_CAPACITY KiB(16) // Allocate minimum of 4 pages
typedef enum {
ARENA_STORAGE_TYPE_ALLOCATED,
ARENA_STORAGE_TYPE_BUFFER,
} ArenaStorageType;
struct Arena {
u8 *buf;
u8 *offset;
u64 capacity;
ArenaStorageType type;
b8 committed;
wapp_misc_utils_reserve_padding(sizeof(u8 *) * 2 + sizeof(u64) + sizeof(b8));
wapp_misc_utils_reserve_padding(sizeof(u8 *) * 2 + sizeof(u64) + sizeof(ArenaStorageType) + sizeof(b8));
};
b8 wapp_mem_arena_init_custom(Arena **arena, u64 base_capacity, MemAllocFlags flags, b8 zero_buffer) {
b8 wapp_mem_arena_init_buffer(Arena **arena, u8 *buffer, u64 buffer_size) {
if (!arena || *arena || buffer_size < sizeof(Arena)) {
return false;
}
*arena = (Arena *)buffer;
Arena *arena_ptr = *arena;
arena_ptr->buf = (u8 *)(arena_ptr + 1);
arena_ptr->offset = arena_ptr->buf;
arena_ptr->capacity = buffer_size - sizeof(Arena);
arena_ptr->type = ARENA_STORAGE_TYPE_BUFFER;
arena_ptr->committed = true;
return true;
}
b8 wapp_mem_arena_init_allocated_custom(Arena **arena, u64 base_capacity, MemAllocFlags flags, b8 zero_buffer) {
if (!arena || *arena || base_capacity == 0) {
return false;
}
*arena = (Arena *)calloc(1, sizeof(Arena));
Arena *arena_ptr = *arena;
if (!arena_ptr) {
u64 size = sizeof(Arena) + (base_capacity >= ARENA_MINIMUM_CAPACITY ? base_capacity : ARENA_MINIMUM_CAPACITY);
u64 alloc_size = wapp_misc_utils_u64_round_up_pow2(size);
u8 *allocated = (u8 *)wapp_os_mem_alloc(NULL, alloc_size, WAPP_MEM_ACCESS_READ_WRITE, flags,
zero_buffer ? WAPP_MEM_INIT_INITIALISED : WAPP_MEM_INIT_UNINITIALISED);
if (!allocated) {
return false;
}
u64 arena_capacity = wapp_misc_utils_u64_round_up_pow2(
base_capacity >= ARENA_MINIMUM_CAPACITY ?
base_capacity :
ARENA_MINIMUM_CAPACITY
);
b8 committed = (flags & WAPP_MEM_ALLOC_COMMIT) == WAPP_MEM_ALLOC_COMMIT;
arena_ptr->buf = (u8 *)wapp_os_mem_alloc(NULL, arena_capacity, WAPP_MEM_ACCESS_READ_WRITE, flags,
zero_buffer ? WAPP_MEM_INIT_INITIALISED : WAPP_MEM_INIT_UNINITIALISED);
#ifdef WAPP_PLATFORM_WINDOWS
if (!committed) {
wapp_os_mem_alloc(allocated, sizeof(Arena), WAPP_MEM_ACCESS_READ_WRITE, WAPP_MEM_ALLOC_COMMIT,
WAPP_MEM_INIT_INITIALISED);
}
#endif // ifdef WAPP_PLATFORM_WINDOWS
if (!(arena_ptr->buf)) {
if (!wapp_mem_arena_init_buffer(arena, allocated, alloc_size)) {
wapp_mem_arena_destroy(arena);
return false;
}
arena_ptr->capacity = arena_capacity;
arena_ptr->offset = arena_ptr->buf;
arena_ptr->committed = (flags & WAPP_MEM_ALLOC_COMMIT) == WAPP_MEM_ALLOC_COMMIT;
Arena *arena_ptr = *arena;
arena_ptr->type = ARENA_STORAGE_TYPE_ALLOCATED;
arena_ptr->committed = committed;
return true;
}
@@ -75,7 +99,7 @@ void *wapp_mem_arena_alloc_aligned(Arena *arena, u64 size, u64 alignment) {
arena->offset = output + size;
#ifdef WAPP_PLATFORM_WINDOWS
if (!(arena->committed)) {
if (arena_ptr->type == ARENA_STORAGE_TYPE_ALLOCATED && !(arena->committed)) {
wapp_os_mem_alloc(alloc_start, (uptr)(arena->offset) - (uptr)(alloc_start),
WAPP_MEM_ACCESS_READ_WRITE, WAPP_MEM_ALLOC_COMMIT,
WAPP_MEM_INIT_UNINITIALISED);
@@ -132,13 +156,10 @@ void wapp_mem_arena_destroy(Arena **arena) {
wapp_debug_assert(arena != NULL && (*arena) != NULL, "`arena` double pointer is not valid");
Arena *arena_ptr = *arena;
if (arena_ptr->buf) {
wapp_os_mem_free(arena_ptr->buf, arena_ptr->capacity);
if (arena_ptr->type == ARENA_STORAGE_TYPE_ALLOCATED) {
wapp_os_mem_free(*arena, sizeof(Arena) + arena_ptr->capacity);
}
arena_ptr->buf = arena_ptr->offset = NULL;
arena_ptr->capacity = 0;
free(*arena);
*arena = NULL;
}

View File

@@ -13,21 +13,22 @@ BEGIN_C_LINKAGE
typedef struct Arena Arena;
#define wapp_mem_arena_init(arena_dptr, base_capacity) \
(wapp_mem_arena_init_custom(arena_dptr, base_capacity, WAPP_MEM_ALLOC_RESERVE, false))
#define wapp_mem_arena_init_commit(arena_dptr, base_capacity) \
(wapp_mem_arena_init_custom(arena_dptr, base_capacity, WAPP_MEM_ALLOC_RESERVE | WAPP_MEM_ALLOC_COMMIT, false))
#define wapp_mem_arena_init_zero(arena_dptr, base_capacity) \
(wapp_mem_arena_init_custom(arena_dptr, base_capacity, WAPP_MEM_ALLOC_RESERVE, true))
#define wapp_mem_arena_init_commit_and_zero(arena_dptr, base_capacity) \
(wapp_mem_arena_init_custom(arena_dptr, base_capacity, WAPP_MEM_ALLOC_RESERVE | WAPP_MEM_ALLOC_COMMIT, true))
#define wapp_mem_arena_init_allocated(arena_dptr, base_capacity) \
(wapp_mem_arena_init_allocated_custom(arena_dptr, base_capacity, WAPP_MEM_ALLOC_RESERVE, false))
#define wapp_mem_arena_init_allocated_commit(arena_dptr, base_capacity) \
(wapp_mem_arena_init_allocated_custom(arena_dptr, base_capacity, WAPP_MEM_ALLOC_RESERVE | WAPP_MEM_ALLOC_COMMIT, false))
#define wapp_mem_arena_init_allocated_zero(arena_dptr, base_capacity) \
(wapp_mem_arena_init_allocated_custom(arena_dptr, base_capacity, WAPP_MEM_ALLOC_RESERVE, true))
#define wapp_mem_arena_init_allocated_commit_and_zero(arena_dptr, base_capacity) \
(wapp_mem_arena_init_allocated_custom(arena_dptr, base_capacity, WAPP_MEM_ALLOC_RESERVE | WAPP_MEM_ALLOC_COMMIT, true))
b8 wapp_mem_arena_init_buffer(Arena **arena, u8 *buffer, u64 buffer_size);
/**
* Arena initialisation function. `wapp_mem_arena_init_custom` provides the most
* Arena initialisation function. `wapp_mem_arena_init_allocated_custom` provides the most
* control over how the Arena is initialised. Wrapper macros are provided for
* easier use.
*/
b8 wapp_mem_arena_init_custom(Arena **arena, u64 base_capacity, MemAllocFlags flags, b8 zero_buffer);
b8 wapp_mem_arena_init_allocated_custom(Arena **arena, u64 base_capacity, MemAllocFlags flags, b8 zero_buffer);
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_realloc(Arena *arena, void *ptr, u64 old_size, u64 new_size);

View File

@@ -5,24 +5,33 @@
#include "../../mem/mem_os.h"
#include "../../../common/aliases/aliases.h"
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_aligned(u64 size, u64 alignment, void *alloc_obj);
wapp_intern void *mem_arena_realloc(void *ptr, u64 old_size, u64 new_size, void *alloc_obj);
wapp_intern void *mem_arena_realloc_aligned(void *ptr, u64 old_size, u64 new_size, u64 alignment,
void *alloc_obj);
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 allocator = {0};
b8 initialised = wapp_mem_arena_init_custom((Arena **)(&allocator.obj), base_capacity, flags, zero_buffer);
b8 initialised = wapp_mem_arena_init_buffer((Arena **)(&allocator.obj), buffer, buffer_size);
if (!initialised) {
return allocator;
}
allocator.alloc = mem_arena_alloc;
allocator.alloc_aligned = mem_arena_alloc_aligned;
allocator.realloc = mem_arena_realloc;
allocator.realloc_aligned = mem_arena_realloc_aligned;
initialise_arena_allocator(&allocator);
return allocator;
}
Allocator wapp_mem_arena_allocator_init_custom(u64 base_capacity, MemAllocFlags flags, b8 zero_buffer) {
Allocator allocator = {0};
b8 initialised = wapp_mem_arena_init_allocated_custom((Arena **)(&allocator.obj), base_capacity, flags, zero_buffer);
if (!initialised) {
return allocator;
}
initialise_arena_allocator(&allocator);
return allocator;
}
@@ -36,6 +45,12 @@ void wapp_mem_arena_allocator_destroy(Allocator *allocator) {
*allocator = (Allocator){0};
}
wapp_intern void initialise_arena_allocator(Allocator *allocator) {
allocator->alloc = mem_arena_alloc;
allocator->alloc_aligned = mem_arena_alloc_aligned;
allocator->realloc = mem_arena_realloc;
allocator->realloc_aligned = mem_arena_realloc_aligned;
}
wapp_intern void *mem_arena_alloc(u64 size, void *alloc_obj) {
Arena *arena = (Arena *)alloc_obj;

View File

@@ -21,6 +21,7 @@ BEGIN_C_LINKAGE
#define wapp_mem_arena_allocator_init_commit_and_zero(base_capacity) \
(wapp_mem_arena_allocator_init_custom(base_capacity, WAPP_MEM_ALLOC_RESERVE | WAPP_MEM_ALLOC_COMMIT, true))
Allocator wapp_mem_arena_allocator_init_with_buffer(u8 *buffer, u64 buffer_size);
/**
* Wraps an Arena in an Allocator object. It attempts to initialise the Arena
* and, if successful, defines the operations supported by it to be used by the

View File

@@ -15,3 +15,19 @@ TestFuncResult test_arena_allocator(void) {
return wapp_tester_result(result);
}
TestFuncResult test_arena_allocator_with_buffer(void) {
u8 buffer[KiB(4)] = {0};
Allocator allocator = wapp_mem_arena_allocator_init_with_buffer(buffer, KiB(4));
b8 result = allocator.obj != NULL && allocator.alloc != NULL &&
allocator.alloc_aligned != NULL &&
allocator.realloc != NULL && allocator.realloc_aligned != NULL &&
allocator.free == NULL;
void *ptr = wapp_mem_allocator_alloc(&allocator, 20);
result = result && (ptr != NULL);
wapp_mem_arena_allocator_destroy(&allocator);
return wapp_tester_result(result);
}

View File

@@ -15,3 +15,19 @@ TestFuncResult test_arena_allocator(void) {
return wapp_tester_result(result);
}
TestFuncResult test_arena_allocator_with_buffer(void) {
u8 buffer[KiB(4)] = {0};
Allocator allocator = wapp_mem_arena_allocator_init_with_buffer(buffer, KiB(4));
b8 result = allocator.obj != NULL && allocator.alloc != NULL &&
allocator.alloc_aligned != NULL &&
allocator.realloc != NULL && allocator.realloc_aligned != NULL &&
allocator.free == NULL;
void *ptr = wapp_mem_allocator_alloc(&allocator, 20);
result = result && (ptr != NULL);
wapp_mem_arena_allocator_destroy(&allocator);
return wapp_tester_result(result);
}

View File

@@ -4,5 +4,6 @@
#include "wapp.h"
TestFuncResult test_arena_allocator(void);
TestFuncResult test_arena_allocator_with_buffer(void);
#endif // !TEST_ALLOCATOR_H

View File

@@ -3,13 +3,21 @@
#include <stdlib.h>
#define ARENA_CAPACITY KiB(16)
#define ARENA_BUF_SIZE KiB(4)
wapp_intern Arena *arena = NULL;
wapp_intern i32 count = 20;
wapp_intern i32 *array = NULL;
wapp_intern Arena *arena = NULL;
wapp_intern i32 count = 20;
wapp_intern i32 *array = NULL;
wapp_intern Arena *buf_arena = NULL;
wapp_intern u8 buf[ARENA_BUF_SIZE] = {0};
TestFuncResult test_arena_init(void) {
b8 result = wapp_mem_arena_init(&arena, ARENA_CAPACITY);
TestFuncResult test_arena_init_buffer(void) {
b8 result = wapp_mem_arena_init_buffer(&buf_arena, buf, ARENA_BUF_SIZE);
return wapp_tester_result(result);
}
TestFuncResult test_arena_init_allocated(void) {
b8 result = wapp_mem_arena_init_allocated(&arena, ARENA_CAPACITY);
return wapp_tester_result(result);
}
@@ -17,7 +25,7 @@ TestFuncResult test_arena_init(void) {
TestFuncResult test_arena_init_succeeds_when_reserving_very_large_size(void) {
Arena *large_arena = NULL;
u64 capacity = GiB(512);
b8 result = wapp_mem_arena_init(&large_arena, capacity);
b8 result = wapp_mem_arena_init_allocated(&large_arena, capacity);
if (result) {
wapp_mem_arena_destroy(&large_arena);
}
@@ -25,6 +33,17 @@ TestFuncResult test_arena_init_succeeds_when_reserving_very_large_size(void) {
return wapp_tester_result(result);
}
TestFuncResult test_arena_alloc_with_buffer(void) {
i32 *array = (i32 *)wapp_mem_arena_alloc(arena, count * sizeof(i32));
b8 result = array != NULL;
for (i32 i = 0; i < count; ++i) {
array[i] = i * 10;
}
return wapp_tester_result(result);
}
TestFuncResult test_arena_alloc_succeeds_when_within_capacity(void) {
array = wapp_mem_arena_alloc(arena, count * sizeof(i32));
b8 result = array != NULL;
@@ -103,7 +122,14 @@ TestFuncResult test_arena_clear(void) {
return wapp_tester_result(result);
}
TestFuncResult test_arena_destroy(void) {
TestFuncResult test_arena_destroy_buffer(void) {
wapp_mem_arena_destroy(&buf_arena);
b8 result = buf_arena == NULL;
return wapp_tester_result(result);
}
TestFuncResult test_arena_destroy_allocated(void) {
wapp_mem_arena_destroy(&arena);
b8 result = arena == NULL;

View File

@@ -3,13 +3,21 @@
#include <stdlib.h>
#define ARENA_CAPACITY KiB(16)
#define ARENA_BUF_SIZE KiB(4)
wapp_intern Arena *arena = nullptr;
wapp_intern i32 count = 20;
wapp_intern i32 *array = nullptr;
wapp_intern Arena *arena = NULL;
wapp_intern i32 count = 20;
wapp_intern i32 *array = NULL;
wapp_intern Arena *buf_arena = NULL;
wapp_intern u8 buf[ARENA_BUF_SIZE] = {0};
TestFuncResult test_arena_init(void) {
b8 result = wapp_mem_arena_init(&arena, ARENA_CAPACITY);
TestFuncResult test_arena_init_buffer(void) {
b8 result = wapp_mem_arena_init_buffer(&buf_arena, buf, ARENA_BUF_SIZE);
return wapp_tester_result(result);
}
TestFuncResult test_arena_init_allocated(void) {
b8 result = wapp_mem_arena_init_allocated(&arena, ARENA_CAPACITY);
return wapp_tester_result(result);
}
@@ -17,7 +25,7 @@ TestFuncResult test_arena_init(void) {
TestFuncResult test_arena_init_succeeds_when_reserving_very_large_size(void) {
Arena *large_arena = nullptr;
u64 capacity = GiB(512);
b8 result = wapp_mem_arena_init(&large_arena, capacity);
b8 result = wapp_mem_arena_init_allocated(&large_arena, capacity);
if (result) {
wapp_mem_arena_destroy(&large_arena);
}
@@ -25,6 +33,17 @@ TestFuncResult test_arena_init_succeeds_when_reserving_very_large_size(void) {
return wapp_tester_result(result);
}
TestFuncResult test_arena_alloc_with_buffer(void) {
i32 *array = (i32 *)wapp_mem_arena_alloc(arena, count * sizeof(i32));
b8 result = array != NULL;
for (i32 i = 0; i < count; ++i) {
array[i] = i * 10;
}
return wapp_tester_result(result);
}
TestFuncResult test_arena_alloc_succeeds_when_within_capacity(void) {
array = (i32 *)wapp_mem_arena_alloc(arena, count * sizeof(i32));
b8 result = array != nullptr;
@@ -103,7 +122,14 @@ TestFuncResult test_arena_clear(void) {
return wapp_tester_result(result);
}
TestFuncResult test_arena_destroy(void) {
TestFuncResult test_arena_destroy_buffer(void) {
wapp_mem_arena_destroy(&buf_arena);
b8 result = buf_arena == NULL;
return wapp_tester_result(result);
}
TestFuncResult test_arena_destroy_allocated(void) {
wapp_mem_arena_destroy(&arena);
b8 result = arena == nullptr;

View File

@@ -3,13 +3,16 @@
#include "wapp.h"
TestFuncResult test_arena_init(void);
TestFuncResult test_arena_init_buffer(void);
TestFuncResult test_arena_init_allocated(void);
TestFuncResult test_arena_init_succeeds_when_reserving_very_large_size(void);
TestFuncResult test_arena_alloc_with_buffer(void);
TestFuncResult test_arena_alloc_succeeds_when_within_capacity(void);
TestFuncResult test_arena_alloc_fails_when_over_capacity(void);
TestFuncResult test_arena_realloc_bigger_size(void);
TestFuncResult test_arena_realloc_smaller_size(void);
TestFuncResult test_arena_clear(void);
TestFuncResult test_arena_destroy(void);
TestFuncResult test_arena_destroy_buffer(void);
TestFuncResult test_arena_destroy_allocated(void);
#endif // !TEST_ARENA_H

View File

@@ -13,14 +13,18 @@
int main(void) {
wapp_tester_run_tests(
test_arena_allocator,
test_arena_init,
test_arena_allocator_with_buffer,
test_arena_init_buffer,
test_arena_init_allocated,
test_arena_init_succeeds_when_reserving_very_large_size,
test_arena_alloc_with_buffer,
test_arena_alloc_succeeds_when_within_capacity,
test_arena_alloc_fails_when_over_capacity,
test_arena_realloc_bigger_size,
test_arena_realloc_smaller_size,
test_arena_clear,
test_arena_destroy,
test_arena_destroy_buffer,
test_arena_destroy_allocated,
test_str8_array,
test_i32_array,
test_i32_array_with_capacity,

View File

@@ -13,14 +13,18 @@
int main(void) {
wapp_tester_run_tests(
test_arena_allocator,
test_arena_init,
test_arena_allocator_with_buffer,
test_arena_init_buffer,
test_arena_init_allocated,
test_arena_init_succeeds_when_reserving_very_large_size,
test_arena_alloc_with_buffer,
test_arena_alloc_succeeds_when_within_capacity,
test_arena_alloc_fails_when_over_capacity,
test_arena_realloc_bigger_size,
test_arena_realloc_smaller_size,
test_arena_clear,
test_arena_destroy,
test_arena_destroy_buffer,
test_arena_destroy_allocated,
test_str8_array,
test_i32_array,
test_i32_array_with_capacity,