258 lines
6.9 KiB
C
258 lines
6.9 KiB
C
/*************************************************************************
|
|
*
|
|
* Orca
|
|
* Copyright 2023 Martin Fouilleul and the Orca project contributors
|
|
* See LICENSE.txt for licensing information
|
|
*
|
|
**************************************************************************/
|
|
#include "memory.h"
|
|
#include "macros.h"
|
|
#include "platform/platform.h"
|
|
#include "platform/platform_memory.h"
|
|
|
|
#if OC_PLATFORM_ORCA
|
|
enum
|
|
{
|
|
OC_ARENA_DEFAULT_RESERVE_SIZE = 1 << 20,
|
|
};
|
|
#else
|
|
enum
|
|
{
|
|
OC_ARENA_DEFAULT_RESERVE_SIZE = 1 << 30,
|
|
};
|
|
#endif
|
|
|
|
enum
|
|
{
|
|
OC_ARENA_COMMIT_ALIGNMENT = 4 << 10,
|
|
};
|
|
|
|
//--------------------------------------------------------------------------------
|
|
//NOTE(martin): memory arena
|
|
//--------------------------------------------------------------------------------
|
|
|
|
oc_arena_chunk* oc_arena_chunk_alloc(oc_arena* arena, u64 chunkMinSize)
|
|
{
|
|
u64 reserveSize = oc_align_up_pow2(chunkMinSize + sizeof(oc_arena_chunk), OC_ARENA_COMMIT_ALIGNMENT);
|
|
u64 commitSize = oc_align_up_pow2(sizeof(oc_arena_chunk), OC_ARENA_COMMIT_ALIGNMENT);
|
|
|
|
char* mem = oc_base_reserve(arena->base, reserveSize);
|
|
oc_base_commit(arena->base, mem, commitSize);
|
|
|
|
oc_arena_chunk* chunk = (oc_arena_chunk*)mem;
|
|
|
|
chunk->ptr = mem;
|
|
chunk->cap = reserveSize;
|
|
chunk->offset = sizeof(oc_arena_chunk);
|
|
chunk->committed = commitSize;
|
|
|
|
oc_list_push_back(&arena->chunks, &chunk->listElt);
|
|
|
|
return (chunk);
|
|
}
|
|
|
|
void oc_arena_init(oc_arena* arena)
|
|
{
|
|
oc_arena_init_with_options(arena, &(oc_arena_options){ 0 });
|
|
}
|
|
|
|
void oc_arena_init_with_options(oc_arena* arena, oc_arena_options* options)
|
|
{
|
|
memset(arena, 0, sizeof(oc_arena));
|
|
|
|
arena->base = options->base ? options->base : oc_base_allocator_default();
|
|
|
|
u64 reserveSize = options->reserve ? (options->reserve + sizeof(oc_arena_chunk)) : OC_ARENA_DEFAULT_RESERVE_SIZE;
|
|
|
|
arena->currentChunk = oc_arena_chunk_alloc(arena, reserveSize);
|
|
}
|
|
|
|
void oc_arena_cleanup(oc_arena* arena)
|
|
{
|
|
oc_list_for_safe(arena->chunks, chunk, oc_arena_chunk, listElt)
|
|
{
|
|
oc_base_release(arena->base, chunk, chunk->cap);
|
|
}
|
|
memset(arena, 0, sizeof(oc_arena));
|
|
}
|
|
|
|
void* oc_arena_push(oc_arena* arena, u64 size)
|
|
{
|
|
return oc_arena_push_aligned(arena, size, 1);
|
|
}
|
|
|
|
void* oc_arena_push_aligned(oc_arena* arena, u64 size, u32 alignment)
|
|
{
|
|
oc_arena_chunk* chunk = arena->currentChunk;
|
|
OC_ASSERT(chunk);
|
|
|
|
u64 alignedOffset = oc_align_up_pow2(chunk->offset, alignment);
|
|
u64 nextOffset = alignedOffset + size;
|
|
u64 lastCap = chunk->cap;
|
|
while(nextOffset > chunk->cap)
|
|
{
|
|
chunk = oc_list_next_entry(arena->chunks, chunk, oc_arena_chunk, listElt);
|
|
if(chunk)
|
|
{
|
|
alignedOffset = oc_align_up_pow2(chunk->offset, alignment);
|
|
nextOffset = alignedOffset + size;
|
|
lastCap = chunk->cap;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if(!chunk)
|
|
{
|
|
u64 chunkMinSize = oc_max(lastCap * 1.5, size + alignment);
|
|
|
|
chunk = oc_arena_chunk_alloc(arena, chunkMinSize);
|
|
alignedOffset = oc_align_up_pow2(chunk->offset, alignment);
|
|
nextOffset = alignedOffset + size;
|
|
}
|
|
OC_ASSERT(nextOffset <= chunk->cap);
|
|
|
|
arena->currentChunk = chunk;
|
|
|
|
if(nextOffset > chunk->committed)
|
|
{
|
|
u64 nextCommitted = oc_align_up_pow2(nextOffset, OC_ARENA_COMMIT_ALIGNMENT);
|
|
nextCommitted = oc_clamp_high(nextCommitted, chunk->cap);
|
|
u64 commitSize = nextCommitted - chunk->committed;
|
|
oc_base_commit(arena->base, chunk->ptr + chunk->committed, commitSize);
|
|
chunk->committed = nextCommitted;
|
|
}
|
|
char* p = chunk->ptr + alignedOffset;
|
|
chunk->offset = nextOffset;
|
|
|
|
return (p);
|
|
}
|
|
|
|
void oc_arena_clear(oc_arena* arena)
|
|
{
|
|
oc_list_for(arena->chunks, chunk, oc_arena_chunk, listElt)
|
|
{
|
|
chunk->offset = sizeof(oc_arena_chunk);
|
|
}
|
|
arena->currentChunk = oc_list_first_entry(arena->chunks, oc_arena_chunk, listElt);
|
|
}
|
|
|
|
oc_arena_scope oc_arena_scope_begin(oc_arena* arena)
|
|
{
|
|
oc_arena_scope scope = { .arena = arena,
|
|
.chunk = arena->currentChunk,
|
|
.offset = arena->currentChunk->offset };
|
|
return (scope);
|
|
}
|
|
|
|
void oc_arena_scope_end(oc_arena_scope scope)
|
|
{
|
|
scope.arena->currentChunk = scope.chunk;
|
|
scope.arena->currentChunk->offset = scope.offset;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------
|
|
//NOTE(martin): memory pool
|
|
//--------------------------------------------------------------------------------
|
|
void oc_pool_init(oc_pool* pool, u64 blockSize)
|
|
{
|
|
oc_pool_init_with_options(pool, blockSize, &(oc_pool_options){ 0 });
|
|
}
|
|
|
|
void oc_pool_init_with_options(oc_pool* pool, u64 blockSize, oc_pool_options* options)
|
|
{
|
|
oc_arena_init_with_options(&pool->arena, &(oc_arena_options){ .base = options->base, .reserve = options->reserve });
|
|
pool->blockSize = oc_clamp_low(blockSize, sizeof(oc_list));
|
|
oc_list_init(&pool->freeList);
|
|
}
|
|
|
|
void oc_pool_cleanup(oc_pool* pool)
|
|
{
|
|
oc_arena_cleanup(&pool->arena);
|
|
memset(pool, 0, sizeof(oc_pool));
|
|
}
|
|
|
|
void* oc_pool_alloc(oc_pool* pool)
|
|
{
|
|
if(oc_list_empty(pool->freeList))
|
|
{
|
|
return (oc_arena_push(&pool->arena, pool->blockSize));
|
|
}
|
|
else
|
|
{
|
|
return (oc_list_pop(&pool->freeList));
|
|
}
|
|
}
|
|
|
|
void oc_pool_recycle(oc_pool* pool, void* ptr)
|
|
{
|
|
oc_list_push(&pool->freeList, (oc_list_elt*)ptr);
|
|
}
|
|
|
|
void oc_pool_clear(oc_pool* pool)
|
|
{
|
|
oc_arena_clear(&pool->arena);
|
|
oc_list_init(&pool->freeList);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------
|
|
//NOTE(martin): per-thread scratch arena
|
|
//--------------------------------------------------------------------------------
|
|
|
|
enum
|
|
{
|
|
OC_SCRATCH_POOL_SIZE = 8,
|
|
OC_SCRATCH_DEFAULT_SIZE = 4096,
|
|
};
|
|
|
|
oc_thread_local oc_arena __scratchPool[OC_SCRATCH_POOL_SIZE] = { 0 };
|
|
|
|
static oc_arena* oc_scratch_at_index(int index)
|
|
{
|
|
oc_arena* scratch = 0;
|
|
|
|
if(index >= 0 && index < OC_SCRATCH_POOL_SIZE)
|
|
{
|
|
if(__scratchPool[index].base == 0)
|
|
{
|
|
oc_arena_options options = { .reserve = OC_SCRATCH_DEFAULT_SIZE };
|
|
oc_arena_init_with_options(&__scratchPool[index], &options);
|
|
}
|
|
scratch = &__scratchPool[index];
|
|
}
|
|
return (scratch);
|
|
}
|
|
|
|
ORCA_API oc_arena_scope oc_scratch_begin(void)
|
|
{
|
|
oc_arena* scratch = oc_scratch_at_index(0);
|
|
oc_arena_scope scope = oc_arena_scope_begin(scratch);
|
|
return (scope);
|
|
}
|
|
|
|
ORCA_API oc_arena_scope oc_scratch_begin_next(oc_arena* used)
|
|
{
|
|
oc_arena_scope scope = { 0 };
|
|
oc_arena* arena = 0;
|
|
if((used >= __scratchPool)
|
|
&& (used - __scratchPool < OC_SCRATCH_POOL_SIZE))
|
|
{
|
|
u64 index = used - __scratchPool;
|
|
if(index + 1 < OC_SCRATCH_POOL_SIZE)
|
|
{
|
|
arena = oc_scratch_at_index(index + 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
arena = oc_scratch_at_index(0);
|
|
}
|
|
|
|
OC_ASSERT(arena);
|
|
|
|
scope = oc_arena_scope_begin(arena);
|
|
|
|
return (scope);
|
|
}
|