Files
raddebugger/src/base/base_arena.c
T
2024-10-22 15:06:39 -07:00

249 lines
6.3 KiB
C

// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Arena Functions
//- rjf: arena creation/destruction
internal Arena *
arena_alloc_(ArenaParams *params)
{
// rjf: round up reserve/commit sizes
U64 reserve_size = params->reserve_size;
U64 commit_size = params->commit_size;
if(params->flags & ArenaFlag_LargePages)
{
reserve_size = AlignPow2(reserve_size, os_get_system_info()->large_page_size);
commit_size = AlignPow2(commit_size, os_get_system_info()->large_page_size);
}
else
{
reserve_size = AlignPow2(reserve_size, os_get_system_info()->page_size);
commit_size = AlignPow2(commit_size, os_get_system_info()->page_size);
}
// rjf: reserve/commit initial block
void *base = params->optional_backing_buffer;
if(base == 0)
{
if(params->flags & ArenaFlag_LargePages)
{
base = os_reserve_large(reserve_size);
os_commit_large(base, commit_size);
}
else
{
base = os_reserve(reserve_size);
os_commit(base, commit_size);
}
}
// rjf: panic on arena creation failure
#if OS_FEATURE_GRAPHICAL
if(Unlikely(base == 0))
{
os_graphical_message(1, str8_lit("Fatal Allocation Failure"), str8_lit("Unexpected memory allocation failure."));
os_abort(1);
}
#endif
// rjf: extract arena header & fill
Arena *arena = (Arena *)base;
arena->current = arena;
arena->flags = params->flags;
arena->cmt_size = (U32)params->commit_size;
arena->res_size = params->reserve_size;
arena->base_pos = 0;
arena->pos = ARENA_HEADER_SIZE;
arena->cmt = commit_size;
arena->res = reserve_size;
#if ARENA_FREE_LIST
arena->free_size = 0;
arena->free_last = 0;
#endif
AsanPoisonMemoryRegion(base, commit_size);
AsanUnpoisonMemoryRegion(base, ARENA_HEADER_SIZE);
return arena;
}
internal void
arena_release(Arena *arena)
{
for(Arena *n = arena->current, *prev = 0; n != 0; n = prev)
{
prev = n->prev;
os_release(n, n->res);
}
}
//- rjf: arena push/pop core functions
internal void *
arena_push(Arena *arena, U64 size, U64 align)
{
Arena *current = arena->current;
U64 pos_pre = AlignPow2(current->pos, align);
U64 pos_pst = pos_pre + size;
// rjf: chain, if needed
if(current->res < pos_pst && !(arena->flags & ArenaFlag_NoChain))
{
Arena *new_block = 0;
#if ARENA_FREE_LIST
Arena *prev_block;
for(new_block = arena->free_last, prev_block = 0; new_block != 0; prev_block = new_block, new_block = new_block->prev)
{
if(new_block->res >= AlignPow2(size, align))
{
if(prev_block)
{
prev_block->prev = new_block->prev;
}
else
{
arena->free_last = new_block->prev;
}
arena->free_size -= new_block->res_size;
AsanUnpoisonMemoryRegion((U8*)new_block + ARENA_HEADER_SIZE, new_block->res_size - ARENA_HEADER_SIZE);
break;
}
}
#endif
if(new_block == 0)
{
U64 res_size = current->res_size;
U64 cmt_size = current->cmt_size;
if(size + ARENA_HEADER_SIZE > res_size)
{
res_size = AlignPow2(size + ARENA_HEADER_SIZE, align);
cmt_size = AlignPow2(size + ARENA_HEADER_SIZE, align);
}
new_block = arena_alloc(.reserve_size = res_size,
.commit_size = cmt_size,
.flags = current->flags);
}
new_block->base_pos = current->base_pos + current->res;
SLLStackPush_N(arena->current, new_block, prev);
current = new_block;
pos_pre = AlignPow2(current->pos, align);
pos_pst = pos_pre + size;
}
// rjf: commit new pages, if needed
if(current->cmt < pos_pst)
{
U64 cmt_pst_aligned = pos_pst + current->cmt_size-1;
cmt_pst_aligned -= cmt_pst_aligned%current->cmt_size;
U64 cmt_pst_clamped = ClampTop(cmt_pst_aligned, current->res);
U64 cmt_size = cmt_pst_clamped - current->cmt;
U8 *cmt_ptr = (U8 *)current + current->cmt;
if(current->flags & ArenaFlag_LargePages)
{
os_commit_large(cmt_ptr, cmt_size);
}
else
{
os_commit(cmt_ptr, cmt_size);
}
current->cmt = cmt_pst_clamped;
}
// rjf: push onto current block
void *result = 0;
if(current->cmt >= pos_pst)
{
result = (U8 *)current+pos_pre;
current->pos = pos_pst;
AsanUnpoisonMemoryRegion(result, size);
}
// rjf: panic on failure
#if OS_FEATURE_GRAPHICAL
if(Unlikely(result == 0))
{
os_graphical_message(1, str8_lit("Fatal Allocation Failure"), str8_lit("Unexpected memory allocation failure."));
os_abort(1);
}
#endif
return result;
}
internal U64
arena_pos(Arena *arena)
{
Arena *current = arena->current;
U64 pos = current->base_pos + current->pos;
return pos;
}
internal void
arena_pop_to(Arena *arena, U64 pos)
{
U64 big_pos = ClampBot(ARENA_HEADER_SIZE, pos);
Arena *current = arena->current;
#if ARENA_FREE_LIST
for(Arena *prev = 0; current->base_pos >= big_pos; current = prev)
{
prev = current->prev;
current->pos = ARENA_HEADER_SIZE;
arena->free_size += current->res_size;
SLLStackPush_N(arena->free_last, current, prev);
AsanPoisonMemoryRegion((U8*)current + ARENA_HEADER_SIZE, current->res_size - ARENA_HEADER_SIZE);
}
#else
for(Arena *prev = 0; current->base_pos >= big_pos; current = prev)
{
prev = current->prev;
os_release(current, current->res);
}
#endif
arena->current = current;
U64 new_pos = big_pos - current->base_pos;
AssertAlways(new_pos <= current->pos);
AsanPoisonMemoryRegion((U8*)current + new_pos, (current->pos - new_pos));
current->pos = new_pos;
}
//- rjf: arena push/pop helpers
internal void
arena_clear(Arena *arena)
{
arena_pop_to(arena, 0);
}
internal void
arena_pop(Arena *arena, U64 amt)
{
U64 pos_old = arena_pos(arena);
U64 pos_new = pos_old;
if(amt < pos_old)
{
pos_new = pos_old - amt;
}
arena_pop_to(arena, pos_new);
}
//- rjf: temporary arena scopes
internal Temp
temp_begin(Arena *arena)
{
U64 pos = arena_pos(arena);
Temp temp = {arena, pos};
return temp;
}
internal void
temp_end(Temp temp)
{
arena_pop_to(temp.arena, temp.pos);
}