diff --git a/core/mem/virtual/static_arena.odin b/core/mem/virtual/static_arena.odin new file mode 100644 index 000000000..cd9b4ba86 --- /dev/null +++ b/core/mem/virtual/static_arena.odin @@ -0,0 +1,134 @@ +package mem_virtual + +import "core:mem" + +Static_Arena :: struct { + block: ^Memory_Block, + minimum_reserve_size: uint, + + temp_count: int, +} + +STATIC_ARENA_DEFAULT_COMMIT_SIZE :: 1<<20 +STATIC_ARENA_DEFAULT_RESERVE_SIZE :: 1<<30 when size_of(uintptr) == 8 else 1<<26 + +static_arena_init :: proc(arena: ^Static_Arena, reserved: uint, commit_size: uint = STATIC_ARENA_DEFAULT_COMMIT_SIZE) -> (err: Allocator_Error) { + reserved := reserved + reserved = max(reserved, STATIC_ARENA_DEFAULT_COMMIT_SIZE) + data := reserve(uint(reserved)) or_return + committed := max(commit_size, STATIC_ARENA_DEFAULT_COMMIT_SIZE) + committed = min(committed, reserved) + + ptr := raw_data(data) + commit(ptr, uint(committed)) + + arena.block = memory_block_alloc(commit_size, reserved, {}) or_return + return +} + +static_arena_destroy :: proc(arena: ^Static_Arena) { + memory_block_dealloc(arena.block) + arena^ = {} +} + + +static_arena_alloc :: proc(arena: ^Static_Arena, size: int, alignment: int) -> (data: []byte, err: Allocator_Error) { + align_forward :: #force_inline proc "contextless" (ptr: uint, align: uint) -> uint { + mask := align-1 + return (ptr + mask) &~ mask + } + + if arena.block == nil { + reserve_size := max(arena.minimum_reserve_size, STATIC_ARENA_DEFAULT_RESERVE_SIZE) + static_arena_init(arena, reserve_size, STATIC_ARENA_DEFAULT_COMMIT_SIZE) or_return + } + + MINIMUM_ALIGN :: 2*align_of(uintptr) + + align := max(MINIMUM_ALIGN, alignment) + + return alloc_from_memory_block(arena.block, size, align) +} + +static_arena_reset_to :: proc(arena: ^Static_Arena, pos: uint) -> bool { + if arena.block != nil { + prev_pos := arena.block.used + arena.block.used = clamp(pos, 0, arena.block.reserved) + + if prev_pos < pos { + mem.zero_slice(arena.block.base[arena.block.used:][:pos-prev_pos]) + } + return true + } else if pos == 0 { + return true + } + return false +} + +static_arena_free_all :: proc(arena: ^Static_Arena) { + static_arena_reset_to(arena, 0) +} + +static_arena_allocator :: proc(arena: ^Static_Arena) -> mem.Allocator { + return mem.Allocator{static_arena_allocator_proc, arena} +} + +static_arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode, + size, alignment: int, + old_memory: rawptr, old_size: int, + location := #caller_location) -> (data: []byte, err: Allocator_Error) { + arena := (^Static_Arena)(allocator_data) + + switch mode { + case .Alloc: + return static_arena_alloc(arena, size, alignment) + case .Free: + err = .Mode_Not_Implemented + return + case .Free_All: + static_arena_free_all(arena) + return + case .Resize: + return mem.default_resize_bytes_align(mem.byte_slice(old_memory, old_size), size, alignment, static_arena_allocator(arena), location) + + case .Query_Features, .Query_Info: + err = .Mode_Not_Implemented + return + } + + err = .Mode_Not_Implemented + return +} + + +Static_Arena_Temp :: struct { + arena: ^Static_Arena, + used: uint, +} + + +static_arena_temp_begin :: proc(arena: ^Static_Arena) -> (temp: Static_Arena_Temp) { + temp.arena = arena + temp.used = arena.block.used if arena.block != nil else 0 + arena.temp_count += 1 + return +} + +static_arena_temp_end :: proc(temp: Static_Arena_Temp, loc := #caller_location) { + assert(temp.arena != nil, "nil arena", loc) + arena := temp.arena + + used := arena.block.used if arena.block != nil else 0 + + assert(temp.used >= used, "invalid Static_Arena_Temp", loc) + + static_arena_reset_to(arena, temp.used) + + assert(arena.temp_count > 0, "double-use of growing_arena_temp_end", loc) + arena.temp_count -= 1 +} + + +static_arena_check_temp :: proc(arena: ^Static_Arena, loc := #caller_location) { + assert(arena.temp_count == 0, "Static_Arena_Temp not been ended", loc) +} \ No newline at end of file