constexpr size_t ARENA_BLOCK_SIZE = mib(1); 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 *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; }