#add_context arena := Arena.{ proc = PagingArenaProc }; #add_context temp_arena := Arena.{ proc = TempArenaProc }; Arena :: struct { proc: ArenaProc; data: *void; } ArenaProc :: #type ( mode: ArenaMode, arena_data: *void, new_size: int, old_size: int, align: int, new_ptr: *void, old_ptr: *void, caller: Source_Code_Location ) -> *void; ArenaMode :: enum { Setup; Teardown; Acquire; Reset; Save; Restore; } /// ArenaPush pushes a value of type T to the current context.arena. ArenaPush :: ($T: Type, loc := #caller_location) -> *T { return ArenaPushSize(size_of(T), loc = loc).(*T); } /// ArenaPushSize pushes size bytes to the current context.arena. ArenaPushSize :: (size: int, align := DefaultAlign, loc := #caller_location) -> *void { if size < 0 { Panic("jc: size cannot be negative", loc = loc); } TrySetupArena(*context.arena); return context.arena.proc(.Acquire, context.arena.data, size, 0, align, null, null, loc); } ArenaSave :: (loc := #caller_location) -> ArenaSaveResult { TrySetupArena(*context.arena, loc); return context.arena.proc(.Save, context.arena.data, 0, 0, 0, null, null, loc).(ArenaSaveResult); } ArenaRestore :: (savepoint: ArenaSaveResult, loc := #caller_location) { context.arena.proc(.Restore, context.arena.data, 0, 0, 0, null, savepoint.(*void), loc); } ArenaAutoRestore :: () #expand { wm := ArenaSave(); `defer ArenaRestore(wm); } /// ArenaSaveResult is an arena-specific value used for saving/restoring. /// see: ArenaSave ArenaSaveResult :: #type,distinct *void; /// ArenaReset resets arena's state, allowing its memory to be reused. ArenaReset :: (loc := #caller_location) { TrySetupArena(*context.arena); context.arena.proc(.Reset, context.arena.data, 0, 0, 0, null, null, loc); } /// ArenaRelease frees an arena's memory. ArenaRelease :: (loc := #caller_location) { TrySetupArena(*context.arena, loc); context.arena.proc(.Teardown, context.arena.data, 0, 0, 0, null, null, loc); } /// ArenaToAllocator wraps an arena, allowing it to be used with Jai's builtin Allocator system. ArenaToAllocator :: (arena: *Arena) -> Allocator { return .{ proc = JaiAllocatorProc, data = arena }; } PanicArena :: () -> Arena { return .{ proc = PanicArenaProc }; } BumpData :: struct { memory: []u8; offset: int; } BumpArena :: (bump: *BumpData) -> Arena { return .{ proc = BumpArenaProc, data = bump }; } PagingArena :: () -> Arena { return .{ proc = PagingArenaProc }; } #scope_module TrySetupArena :: (arena: *Arena, loc := #caller_location) #expand { if arena.data != null { return; } data := arena.proc(.Setup, null, 0, 0, 0, null, null, loc); if data != null { arena.data = data; } } PanicArenaProc :: ( mode: ArenaMode, arena_data: *void, new_size: int, old_size: int, align: int, new_ptr: *void, old_ptr: *void, caller: Source_Code_Location ) -> *void { if mode == { case .Acquire; if new_size > 0 { Panic("jc: cannot acquire memory using the PanicArena", loc = caller); } case .Save; Panic("jc: cannot save using the PanicArean", loc = caller); case .Restore; Panic("jc: cannot restore using the PanicArean", loc = caller); } return null; } @jc.nodocs BumpArenaProc :: ( mode: ArenaMode, arena_data: *void, new_size: int, old_size: int, align: int, new_ptr: *void, old_ptr: *void, caller: Source_Code_Location ) -> *void #no_abc { bump := arena_data.(*BumpData); if mode == { case .Setup; if bump.memory.data == null { Panic("jc: BumpArena has no memory", loc = caller); } return arena_data; case .Teardown; // Do nothing, we don't own this memory case .Acquire; end := bump.memory.data + bump.offset; if new_size == 0 { return end; } ptr := MemAlignForward(end, align); size := new_size + (ptr - end); if bump.offset + size > bump.memory.count { Panic("jc: BumpArena out of memory", loc = caller); } bump.offset += size; return ptr; case .Reset; bump.offset = 0; case .Save; return bump.offset.(*void); case .Restore; wm := old_ptr.(int); if wm < 0 || wm >= bump.memory.count { Panic("jc: BumpArena restored invalid savepoint", loc = caller); } bump.offset = wm; } return null; } @jc.nodocs PagingArenaProc :: ( mode: ArenaMode, arena_data: *void, new_size: int, old_size: int, align: int, new_ptr: *void, old_ptr: *void, caller: Source_Code_Location ) -> *void { Panic("not implemented for PagingArena", loc = caller); return null; } @jc.nodocs // @note(judah): TempArenaProc just wraps BumpArenaProc, but provides its own backing memory. // This is needed so we can use TempArenaProc without forcing the user to set everything up. TempArenaProc :: ( mode: ArenaMode, arena_data: *void, new_size: int, old_size: int, align: int, new_ptr: *void, old_ptr: *void, caller: Source_Code_Location ) -> *void { // @temp(judah) libc :: #library,system "libc"; malloc :: (size: int) -> *void #c_call #foreign libc; free :: (ptr: *void) #c_call #foreign libc; if mode == { // @note(judah): allows the temp arena to initialize itself without relying on another arena to provide its memory. case .Setup; mcount := AlignForward(size_of(BumpData) + 32768).(int); memory := malloc(mcount).(*u8); MemZero(memory, mcount); bump := memory.(*BumpData); bump.memory.data = memory + size_of(BumpData); bump.memory.count = mcount - size_of(BumpData); return bump; case .Teardown; free(arena_data); } return BumpArenaProc(mode, arena_data, new_size, old_size, align, new_ptr, old_ptr, caller); } @jc.nodocs #scope_file JaiAllocatorProc :: (mode: Allocator_Mode, requested_size: s64, old_size: s64, old_memory: *void, allocator_data: *void) -> *void { CallerFromStackTrace :: () -> Source_Code_Location #expand { node := context.stack_trace; if node.next != null { node = node.next; } return node.info.location; } arena := allocator_data.(*Arena); if mode == { case .STARTUP; TrySetupArena(arena); return null; case .ALLOCATE; loc := CallerFromStackTrace(); return ArenaPushSize(requested_size, loc = loc,, arena = arena); case .RESIZE; loc := CallerFromStackTrace(); new := ArenaPushSize(requested_size, loc = loc,, arena = arena); MemCopy(new, old_memory, old_size); return new; case .FREE; // Arenas don't free single pointers so give this a value that's obviously incorrect MemOverwrite(old_memory, old_size, 0xAA); } return null; } @jc.nodocs