#module_parameters(RUN_TESTS := false); make_crash_allocator :: () -> Allocator { return .{ proc = crash_allocator_proc }; } crash_allocator_proc :: (mode: Allocator_Mode, size: s64, old_size: s64, old_memory: *void, allocator_data: *void) -> *void { message: string; if mode == { case .ALLOCATE; message = basic.tprint("Attempt to allocate % byte(s) using the crash allocator!", size); case .RESIZE; message = basic.tprint("Attempt to resize (from % to % byte(s)) using the crash allocator!", old_size, size); case .FREE; message = basic.tprint("Attempt to free % byte(s) using the crash allocator!", size); } loc := meta.get_stack_trace_caller_location(); basic.assert(false, message, loc = loc); debug_break(); return null; } Arena :: struct { memory: *void; memory_size: u64; offset: u64; } init_arena :: (a: *Arena, memory: *void, size: u64) { a.memory = memory; a.memory_size = size; a.offset = 0; } make_arena_allocator :: (arena: *Arena) -> Allocator { return .{ data = arena, proc = xx arena_allocator_proc, }; } Extended_Allocator_Mode :: enum { using Allocator_Mode; request_memory :: Allocator_Mode.ALLOCATE; resize_memory :: Allocator_Mode.RESIZE; release_memory :: Allocator_Mode.FREE; first_time_used :: Allocator_Mode.STARTUP; release_everything :: Allocator_Mode.SHUTDOWN; reset_state; save_point; // should return *s64 restore_save_point; // 'old_size' will be the dereferenced return value of 'save_point' } arena_allocator_proc :: (mode: Extended_Allocator_Mode, size: s64, old_size: s64, old_memory: *void, allocator_data: *void) -> *void { arena := allocator_data.(*Arena); if mode == { case .request_memory; return arena_alloc(arena, size); case .resize_memory; if old_memory == null { return arena_alloc(arena, size); } if size == 0 { return null; } if size == old_size { return old_memory; } new_memory := arena_alloc(arena, size); memcpy(new_memory, old_memory, old_size); return new_memory; case .save_point; return *arena.offset; case .restore_save_point; arena.offset = old_size.(u64); } return null; } arena_alloc :: (a: *Arena, count: int, alignment := mem.Default_Align, loc := #caller_location) -> *void { basic.assert(a.memory != null, "arena: not initialized", loc = loc); basic.assert(mem.power_of_two(alignment)); end := a.memory.(*u8) + a.offset; ptr := mem.align_to(end.(int), alignment); total_size := (count + ptr.(*u8) - end.(*u8)).(u64); basic.assert(a.offset + total_size <= a.memory_size, "arena: out of memory", loc = loc); a.offset += total_size; return ptr.(*void); } #scope_file; mem :: #import "jc/memory"; meta :: #import "jc/meta"; basic :: #import "Basic"; // @future // ---------------------------------------------------------- // TESTS // ---------------------------------------------------------- #if RUN_TESTS #run { test :: #import "jc/meta/test"; test.run("arena:basic", t => { memory := mem.request_memory(1 * mem.Kilobyte); defer mem.release_memory(memory); arena: Arena; init_arena(*arena, memory, 1 * mem.Kilobyte); context.allocator = make_arena_allocator(*arena); save_point := mem.allocator_save(); i := mem.request_memory(int); basic.assert(i != null); basic.assert(arena.offset == size_of(int)); mem.allocator_restore(save_point); }); }