constexpr size_t ARENA_BLOCK_SIZE = mib(4); constexpr size_t ARENA_ALIGNMENT = 8; struct Scratch_Arena : Allocator { int cap, len; uint8_t memory[]; }; struct Scratch_Scope { Scratch_Arena *arena; int pos; Scratch_Scope(Scratch_Arena *arena) { this->arena = arena; this->pos = arena->len; } ~Scratch_Scope() { this->arena->len = this->pos; } }; static void *scratch_arena_push_size(Scratch_Arena *arena, size_t size) { size_t generous_size = size + ARENA_ALIGNMENT; if (arena->len + generous_size > arena->cap) { assert(size < arena->cap); assert_message(0, "Scratch arena is too small, failed to handle a request, capacity: %d, occupied: %d, allocation: %d", generous_size); return 0; } size_t aligned_fill = align_up((size_t)arena->memory + arena->len, ARENA_ALIGNMENT) - (size_t)arena->memory; assert(((int64_t)aligned_fill - (int64_t)arena->len) >= 0); assert(((int64_t)aligned_fill - (int64_t)arena->len) <= ARENA_ALIGNMENT); arena->len = aligned_fill; uint8_t *result = arena->memory + arena->len; arena->len += size; return (void *)result; } static Scratch_Arena *make_scratch_arena(void *buff, size_t size) { Scratch_Arena *scratch = (Scratch_Arena *)buff; scratch->len = 0; scratch->cap = size - sizeof(Scratch_Arena); scratch->allocate = (Allocator::Allocate *)scratch_arena_push_size; scratch->deallocate = deallocate_stub; return scratch; } static Scratch_Arena *allocate_scratch_arena(Allocator *allocator, size_t size_without_members) { Scratch_Arena *scratch = (Scratch_Arena *)allocate_size(allocator, size_without_members + sizeof(Scratch_Arena), false); scratch->cap = size_without_members; scratch->len = 0; scratch->allocate = (Allocator::Allocate *)scratch_arena_push_size; scratch->deallocate = deallocate_stub; return scratch; } struct Push_Arena_Block { Push_Arena_Block *next; uint8_t memory[]; }; struct Push_Arena : Allocator { Allocator *block_allocator; Push_Arena_Block *blocks; size_t allocated_block_count; size_t block_fill; size_t wasted_memory; }; static void push_arena_add_block(Push_Arena *arena) { Push_Arena_Block *block = (Push_Arena_Block *)allocate_size(arena->block_allocator, sizeof(Push_Arena_Block) + ARENA_BLOCK_SIZE, false); memory_zero(block, sizeof(Push_Arena_Block)); SLL_STACK_ADD(arena->blocks, block); if (arena->allocated_block_count) arena->wasted_memory += ARENA_BLOCK_SIZE - arena->block_fill; arena->allocated_block_count += 1; arena->block_fill = 0; } static void push_arena_deallocate(Push_Arena *arena) { for(Push_Arena_Block *it = arena->blocks; it; it = it->next) { deallocate(arena->block_allocator, it); } memory_zero(arena, sizeof(*arena)); } static void *push_arena_push_size(Push_Arena *arena, size_t size) { assert(arena->block_allocator); assert(size < ARENA_BLOCK_SIZE / 2); size_t generous_size = size + ARENA_ALIGNMENT; if (arena->blocks == 0 || (arena->block_fill + generous_size > ARENA_BLOCK_SIZE)) { push_arena_add_block(arena); } Push_Arena_Block *L = arena->blocks; size_t aligned_fill = align_up((size_t)L->memory + arena->block_fill, ARENA_ALIGNMENT) - (size_t)L->memory; assert(((int64_t)aligned_fill - (int64_t)arena->block_fill) >= 0); assert(((int64_t)aligned_fill - (int64_t)arena->block_fill) <= ARENA_ALIGNMENT); arena->block_fill = aligned_fill; uint8_t *result = L->memory + arena->block_fill; arena->block_fill += size; return (void *)result; } static Push_Arena make_push_arena(Allocator *allocator) { Push_Arena result = {}; result.block_allocator = allocator; result.allocate = (Allocator::Allocate *)push_arena_push_size; result.deallocate = (Allocator::Deallocate *)deallocate_stub; return result; } struct CRT_Heap : Allocator {}; static void *crt_allocate(Allocator *allocator, size_t size) { return malloc(size); } static void crt_deallocate(Allocator *allocator, void *p) { return free(p); } static CRT_Heap make_crt_heap() { CRT_Heap result = {}; result.allocate = crt_allocate; result.deallocate = crt_deallocate; return result; } //----------------------------------------------------------------------------- // Memory OS //----------------------------------------------------------------------------- struct OS_Memory{ size_t commit, reserve; U8 *data; }; CORE_Static OS_Memory os_reserve(size_t size); CORE_Static B32 os_commit(OS_Memory *m, size_t size); CORE_Static void os_release(OS_Memory *m); CORE_Static B32 os_decommit_pos(OS_Memory *m, size_t pos); //----------------------------------------------------------------------------- // Memory arenas //----------------------------------------------------------------------------- global const size_t default_reserve_size = gib(4); global const size_t default_alignment = 8; global const size_t additional_commit_size = mib(1); struct Arena : Allocator { OS_Memory memory; size_t alignment; size_t len; String debug_string; }; CORE_Static void arena_pop_pos(Arena *arena, size_t pos){ pos = clamp_top(pos, arena->len); arena->len = pos; } CORE_Static void * arena_pop(Arena *arena, size_t size){ size = clamp_top(size, arena->len); arena->len -= size; return arena->memory.data + arena->len; } CORE_Static void arena_release(Arena *arena){ os_release(&arena->memory); } CORE_Static void arena_clear(Arena *arena){ arena_pop_pos(arena, 0); } CORE_Static void * arena_push_size(Arena *a, size_t size){ size_t generous_size = size + a->alignment; if(a->len+generous_size>a->memory.commit){ assert(a->memory.reserve > 0); B32 result = os_commit(&a->memory, generous_size+additional_commit_size); assert(result); } a->len = align_up(a->len, a->alignment); assert(a->memory.reserve > a->len + size); void *result = (U8*)a->memory.data + a->len; a->len += size; return result; } CORE_Static Arena push_arena(Allocator *allocator, size_t size, String debug_name) { Arena result = {}; result.memory.data = (U8 *)allocate_size(allocator, size); result.memory.reserve = size; result.alignment = default_alignment; result.debug_string = debug_name; result.allocate = (Allocator::Allocate *)arena_push_size; result.deallocate = (Allocator::Deallocate *)deallocate_stub; return result; } CORE_Static void arena_init(Arena *a, String debug_name){ a->memory = os_reserve(default_reserve_size); a->alignment = default_alignment; a->debug_string = debug_name; a->allocate = (Allocator::Allocate *)arena_push_size; a->deallocate = (Allocator::Deallocate *)deallocate_stub; } CORE_Static Arena arena_sub(Arena *base, size_t size, String debug_name) { Arena result = {}; result.memory.data = (U8 *)arena_push_size(base, size); result.memory.commit = size; result.memory.reserve = size; result.alignment = default_alignment; result.len = 0; return result; }