diff --git a/src/arena.c b/src/arena.c index 6dead73..8b7a1a9 100644 --- a/src/arena.c +++ b/src/arena.c @@ -174,58 +174,3 @@ void arena_reset(Arena *arena) arena_reset_to(arena, 0); } - -// Pool - -APool *apool_create_ex(Arena *arena, U32 n_elements, U32 element_size, U32 element_align) -{ - Assert(IsPow2(element_align)); - U32 align = element_align; - if (element_align < alignof(APoolElement)) { - element_align = alignof(APoolElement); - } - U32 size = AlignUp(element_size, element_align); - - APool *pool = arena_push_type(arena, APool); - Assert(pool != NULL); - pool->align = align; - pool->element_size = size; - pool->arena = arena; - pool->n_elements = n_elements; - pool->free_list = NULL; - pool->start = NULL; - - if (size != APOOL_AUTOGROW) { - U32 total_size = n_elements * size; // Note(nos): guard against unreasonable sizes - U8 * start = arena_push(arena, total_size, align); - for (U32 i = 0; i < total_size; i += size) { - APoolElement *element = (APoolElement *)&start[i]; - element->next = pool->free_list; - pool->free_list = element; - } - pool->start = start; - } - - return pool; -} - -void *apool_alloc(APool *pool) -{ - APoolElement *element = pool->free_list; - if (element != NULL) { - pool->free_list = element->next; - } else if(pool->n_elements == APOOL_AUTOGROW) { - element = arena_push(pool->arena, pool->element_size, pool->align); - } - - return element; -} - -void apool_free(APool *pool, void *ptr) -{ - if (ptr != NULL) { - APoolElement *element = (APoolElement *)ptr; - element->next = pool->free_list; - pool->free_list = element; - } -} diff --git a/src/arena.h b/src/arena.h index 638ae11..14e64b8 100644 --- a/src/arena.h +++ b/src/arena.h @@ -73,26 +73,28 @@ static INLINE U64 arena_used(const Arena *arena) } -typedef union APoolElement APoolElement; -union APoolElement { - APoolElement *next; // offset from start to next free block (when not allocated) -}; +static INLINE void *arena_allocator_func(Allocator *allocator, AllocOperation operation , void *memory, U32 size, U32 align) +{ + switch (operation) { + case Alloc_Op_Aligned: + return arena_push(allocator->data, size, align); -typedef struct APool APool; -struct APool { - APoolElement *free_list; // free list - U8 *start; // start of user buffer - Arena *arena; - U32 element_size; // aligned size of each block - U32 n_elements; // total number of blocks - U32 align; // alignment -}; -enum { - APOOL_AUTOGROW = 0 -}; + case Alloc_Op_Free: // can't free from an marena + return NULL; -#define apool_create(arena, n_elements, type) \ - apool_create_ex(arena, n_elements, sizeof(type), alignof(type)); -APool *apool_create_ex(Arena *arena, U32 n_elements, U32 element_size, uint32_t element_align); -void *apool_alloc(APool *pool); -void apool_free(APool *pool, void *element); + case Alloc_Op_Free_All: + arena_free(allocator->data); + return NULL; + } + + Assert(false); +} + +static INLINE Allocator arena_allocator(Arena *arena) +{ + Allocator allocator = {}; + allocator.op_func_ptr = arena_allocator_func; + allocator.data = arena; + + return allocator; +} diff --git a/src/base_core.h b/src/base_core.h index 31bde68..f903913 100644 --- a/src/base_core.h +++ b/src/base_core.h @@ -344,6 +344,46 @@ static INLINE U64 SaturatingMult64(U64 a, U64 b) return SaturatingMult(a, b, U64, U64_Max); } +// Allocators +typedef enum AllocOperation AllocOperation; +enum AllocOperation { + Alloc_Op_Aligned = 1, + Alloc_Op_Free, + Alloc_Op_Free_All, +}; + +typedef struct Allocator Allocator; +typedef void *(*AllocOpFunc)(Allocator *allocator, AllocOperation operation, void *memory, U32 size, U32 align); +struct Allocator { + AllocOpFunc op_func_ptr; + void *data; +}; + +static INLINE void *allocator_alloc_aligned(Allocator *allocator, U32 size, U32 align) +{ + return allocator->op_func_ptr(allocator, Alloc_Op_Aligned, NULL, size, align); +} + +static INLINE void allocator_free(Allocator *allocator, void *memory) +{ + allocator->op_func_ptr(allocator, Alloc_Op_Free, memory, 0, 0); +} + +static INLINE void allocator_free_all(Allocator *allocator) +{ + allocator->op_func_ptr(allocator, Alloc_Op_Free_All, NULL, 0, 0); +} + + +#define allocator_push_type(allocator, type_)\ + (allocator)->op_func_ptr((allocator), Alloc_Op_Aligned, NULL, sizeof(type_), _Alignof(type_)) + +#define allocator_push_array(allocator, type_, nr_elements)\ + (allocator)->op_func_ptr((allocator), Alloc_Op_Aligned, NULL, sizeof(type_) * (nr_elements), _Alignof(type_) ) + +#define allocator_push(allocator, size, align)\ + (allocator)->op_func_ptr((allocator), Alloc_Op_Aligned, NULL, size, align) + // address-sanitizer helpers (-fsanitize=address) #ifdef USE_ASAN #include diff --git a/src/main.c b/src/main.c index 8210d79..04b21e3 100644 --- a/src/main.c +++ b/src/main.c @@ -2,6 +2,7 @@ #include "arena.h" #include "mini_arena.h" #include "platform.h" +#include "pool.h" #include #include @@ -163,6 +164,40 @@ static void arena_test_4(void) arena_free(&arena); } +static void arena_test_allocator_interface(void) +{ + TRACEF("\n"); + + U32 page_size = platform_page_size(); + ArenaOptions arena_params = {}; + U32 arena_size = MB(1) + MemoryBlock_Header_Size; + U32 commit_size = KB(64); + arena_params.size = arena_size; + arena_params.commit_size = commit_size; + Arena arena; + arena_init(&arena, &arena_params); + Allocator allocator = arena_allocator(&arena); + + // block_size is the page-aligned arena_size. Request nearly the whole block + // but with page alignment, so the alignment gap (a full page) forces a grow. + U32 block_size = arena.block_size; + U32 push_size = block_size - MemoryBlock_Header_Size; + + void *ptr = allocator_push(&allocator, push_size, page_size); + Assert(IsAlignedTo((uintptr_t)ptr, page_size)); + MemoryZero(ptr, push_size); + printf("test_4: allocated %u bytes page-aligned at %p\n", push_size, ptr); + arena_dump(&arena); + + // A second page-aligned push to make sure the cursor math still holds. + ptr = allocator_push(&allocator, page_size, page_size); + Assert(IsAlignedTo((uintptr_t)ptr, page_size)); + MemoryZero(ptr, page_size); + printf("test_4: allocated %u bytes page-aligned at %p\n", page_size, ptr); + + arena_free(&arena); +} + static void arena_test_decommit(void) { TRACEF("\n"); @@ -192,6 +227,8 @@ static void arena_test_decommit(void) void miniarena_test1(void) { + TRACEF("\n"); + #define arena_size 4096 MARENA_DEFINE(arena, arena_size, 8); int i; @@ -228,9 +265,12 @@ struct PoolTest{ void apool_test(void) { + TRACEF("\n"); + Arena arena; arena_init(&arena, ArenaOptions_Default); - APool *pool = apool_create_ex(&arena, 16, sizeof(PoolTest), alignof(PoolTest)); + Allocator allocator = arena_allocator(&arena); + APool *pool = apool_create_ex(&allocator, 16, sizeof(PoolTest), alignof(PoolTest)); PoolTest *list = NULL; for (U32 i = 0; i < U32_Max; i++) { PoolTest *pt = apool_alloc(pool); @@ -271,9 +311,13 @@ void apool_test(void) void apool_test_autogrow(void) { + TRACEF("\n"); + Arena arena; arena_init(&arena, ArenaOptions_Default); - APool *pool = apool_create(&arena, APOOL_AUTOGROW, PoolTest); + Allocator allocator = arena_allocator(&arena); + + APool *pool = apool_create(&allocator, APOOL_AUTOGROW, PoolTest); PoolTest *list = NULL; for (U32 i = 0; i < 4; i++) { PoolTest *pt = apool_alloc(pool); @@ -319,9 +363,9 @@ int main(int argc, char *argv[]) // arena_test_3(); // arena_test_4(); arena_test_decommit(); - + arena_test_allocator_interface(); // miniarena_test1(); - // apool_test(); - // apool_test_autogrow(); + apool_test(); + apool_test_autogrow(); } diff --git a/src/mini_arena.h b/src/mini_arena.h index 6cc8edf..d211115 100644 --- a/src/mini_arena.h +++ b/src/mini_arena.h @@ -71,7 +71,7 @@ static INLINE void *marena_push(MArena *arena, U32 size) } [[gnu::alloc_align(3)]] -static INLINE void *maren_push_aligned(MArena *arena, U32 size, U32 align) +static INLINE void *marena_push_aligned(MArena *arena, U32 size, U32 align) { Assert(IsPow2(align)); @@ -94,3 +94,28 @@ static INLINE void marena_reset(MArena *arena) { arena->first_free = 0; } + +static INLINE void *marena_allocator_func(Allocator *allocator, AllocOperation operation , void *memory, U32 size, U32 align) +{ + switch (operation) { + case Alloc_Op_Aligned: + return marena_push_aligned(allocator->data, size, align); + + case Alloc_Op_Free: // can't free/Free_all from an marena + return NULL; + + case Alloc_Op_Free_All: + return NULL; + } + + Assert(false); +} + +static INLINE Allocator marena_allocator(MArena *arena) +{ + Allocator allocator = {}; + allocator.op_func_ptr = marena_allocator_func; + allocator.data = arena; + + return allocator; +} diff --git a/src/pool.c b/src/pool.c new file mode 100644 index 0000000..a2264f1 --- /dev/null +++ b/src/pool.c @@ -0,0 +1,54 @@ +#include "pool.h" + +APool *apool_create_ex(Allocator *allocator, U32 n_elements, U32 element_size, U32 element_align) +{ + Assert(IsPow2(element_align)); + U32 align = element_align; + if (element_align < alignof(APoolElement)) { + element_align = alignof(APoolElement); + } + U32 size = AlignUp(element_size, element_align); + + APool *pool = allocator_push_type(allocator, APool); + Assert(pool != NULL); + pool->align = align; + pool->element_size = size; + pool->allocator = allocator; + pool->n_elements = n_elements; + pool->free_list = NULL; + pool->start = NULL; + + if (size != APOOL_AUTOGROW) { + U32 total_size = n_elements * size; // Note(nos): guard against unreasonable sizes + U8 * start = allocator_push(allocator, total_size, align); + for (U32 i = 0; i < total_size; i += size) { + APoolElement *element = (APoolElement *)&start[i]; + element->next = pool->free_list; + pool->free_list = element; + } + pool->start = start; + } + + return pool; +} + +void *apool_alloc(APool *pool) +{ + APoolElement *element = pool->free_list; + if (element != NULL) { + pool->free_list = element->next; + } else if(pool->n_elements == APOOL_AUTOGROW) { + element = allocator_push(pool->allocator, pool->element_size, pool->align); + } + + return element; +} + +void apool_free(APool *pool, void *ptr) +{ + if (ptr != NULL) { + APoolElement *element = (APoolElement *)ptr; + element->next = pool->free_list; + pool->free_list = element; + } +} diff --git a/src/pool.h b/src/pool.h new file mode 100644 index 0000000..a9373f6 --- /dev/null +++ b/src/pool.h @@ -0,0 +1,26 @@ +#pragma once +#include "base_core.h" + +typedef union APoolElement APoolElement; +union APoolElement { + APoolElement *next; // offset from start to next free block (when not allocated) +}; + +typedef struct APool APool; +struct APool { + APoolElement *free_list; // free list + U8 *start; // start of user buffer + Allocator *allocator; + U32 element_size; // aligned size of each block + U32 n_elements; // total number of blocks + U32 align; // alignment +}; +enum { + APOOL_AUTOGROW = 0 +}; + +#define apool_create(arena, n_elements, type) \ + apool_create_ex(arena, n_elements, sizeof(type), alignof(type)); +APool *apool_create_ex(Allocator *allocator, U32 n_elements, U32 element_size, uint32_t element_align); +void *apool_alloc(APool *pool); +void apool_free(APool *pool, void *element);