Compare commits

...

1 Commits

Author SHA1 Message Date
Ed_
aef68be7e2 Misc adjustments/fixes to grime from a few nights ago 2025-11-08 10:36:07 -05:00
7 changed files with 133 additions and 64 deletions

View File

@@ -39,6 +39,11 @@ AllocatorOp :: enum u32 {
Rewind,
SavePoint,
Query, // Must always be implemented
Is_Owner,
Startup,
Shutdown,
Thread_Start,
Thread_Stop,
}
AllocatorQueryFlag :: enum u64 {
Alloc,
@@ -47,27 +52,29 @@ AllocatorQueryFlag :: enum u64 {
Shrink,
Grow,
Resize, // Supports both grow and shrink
Rewind, // Ability to rewind to a save point (ex: arenas, stack), must also be able to save such a point
// Actually_Resize,
// Is_This_Yours,
Actually_Resize,
Multiple_Threads,
Is_Owner,
Hint_Fast_Bump,
Hint_General_Heap,
Hint_Per_Frame_Temporary,
Hint_Debug_Support,
}
AllocatorError :: Odin_AllocatorError
// AllocatorError :: enum i32 {
// None = 0,
// Out_Of_Memory = 1,
// Invalid_Pointer = 2,
// Invalid_Argument = 3,
// Mode_Not_Implemented = 4,
// }
AllocatorQueryFlags :: bit_set[AllocatorQueryFlag; u64]
// AllocatorError :: Odin_AllocatorError
AllocatorError :: enum byte {
None = 0,
Out_Of_Memory = 1,
Invalid_Pointer = 2,
Invalid_Argument = 3,
Mode_Not_Implemented = 4,
Owner = 5,
}
AllocatorSP :: struct {
type_sig: AllocatorProc,
slot: int,

View File

@@ -2,13 +2,13 @@ package grime
// TODO(Ed): Below should be defined per-package?
ensure :: #force_inline proc(condition: bool, msg: string, location := #caller_location) -> bool {
if condition do return true
ensure :: #force_inline proc(condition: bool, msg := #caller_expression, location := #caller_location) -> bool {
if condition == false do return false
log_print( msg, LoggerLevel.Warning, location )
when ODIN_DEBUG == false do return false
when ODIN_DEBUG == false do return true
else {
debug_trap()
return false
return true
}
}
// TODO(Ed) : Setup exit codes!

View File

@@ -80,7 +80,7 @@ array_set_capacity :: proc( self : ^Array( $ Type ), new_capacity: int) -> Alloc
new_size := header_size + new_capacity * size_of(Type)
old_size := header_size + self.capacity * size_of(Type)
new_mem, result_code := mem_resize( slice(transmute(^u8)self.header, old_size), new_size, DEFAULT_ALIGNMENT, ainfo = self.backing )
if ensure( result_code != AllocatorError.None, "Failed to allocate for new array capacity" ) {
if ensure( result_code == AllocatorError.None, "Failed to allocate for new array capacity" ) {
log_print( "Failed to allocate for new array capacity", level = LoggerLevel.Warning )
return result_code
}
@@ -175,7 +175,7 @@ array_fill :: proc(self: Array($Type), begin, end: u64, value: Type) -> bool {
}
// Will push value into the array (will not grow if at capacity, use append instead for when that matters)
array_push_back :: #force_inline proc "contextless" (self: Array($Type)) -> bool {
array_push :: #force_inline proc "contextless" (self: Array($Type)) -> bool {
if self.num == self.capacity { return false }
self.data[self.num] = value
self.num += 1

View File

@@ -85,6 +85,13 @@ farena_rewind :: #force_inline proc "contextless" (arena: ^FArena, save_point: A
arena.used = save_point.slot
}
farena_save :: #force_inline proc "contextless" (arena: FArena) -> AllocatorSP { return AllocatorSP { type_sig = farena_allocator_proc, slot = arena.used } }
farena_is_owner :: #force_inline proc "contextless" (arena: FArena, memory: []byte) -> bool {
p0 := transmute(uintptr) cursor(memory)
p1 := transmute(uintptr) end(memory)
arena_p0 := transmute(uintptr) cursor(arena.mem)
arena_p1 := cast(uintptr) arena.used
return arena_p0 <= p0 && arena_p1 >= p1
}
farena_allocator_proc :: proc(input: AllocatorProc_In, output: ^AllocatorProc_Out) {
assert_contextless(output != nil)
assert_contextless(input.data != nil)
@@ -115,12 +122,16 @@ farena_allocator_proc :: proc(input: AllocatorProc_In, output: ^AllocatorProc_Ou
output.save_point = farena_save(arena^)
return
case .Query:
output.features = {.Alloc, .Reset, .Grow, .Shrink, .Rewind}
output.features = {.Alloc, .Reset, .Grow, .Shrink, .Rewind, .Actually_Resize, .Is_Owner, .Hint_Fast_Bump}
output.max_alloc = len(arena.mem) - arena.used
output.min_alloc = 0
output.left = output.max_alloc
output.save_point = farena_save(arena^)
return
case .Is_Owner:
output.error = farena_is_owner(arena ^, input.old_allocation) ? .Owner : .None
case .Startup, .Shutdown, .Thread_Start, .Thread_Stop:
output.error = .Mode_Not_Implemented
}
panic_contextless("Impossible path")
}
@@ -132,32 +143,29 @@ farena_odin_allocator_proc :: proc(
old_memory : rawptr,
old_size : int,
location : SourceCodeLocation = #caller_location
) -> ( data : []byte, alloc_error : AllocatorError)
) -> ( data : []byte, error : Odin_AllocatorError)
{
error_: AllocatorError
assert_contextless(allocator_data != nil)
arena := transmute(^FArena) allocator_data
switch mode {
case .Alloc, .Alloc_Non_Zeroed:
data, alloc_error = farena_push(arena, byte, size, alignment, location)
data, error_ = farena_push(arena, byte, size, alignment, location)
if mode == .Alloc {
zero(data)
}
return
case .Free:
return {}, .Mode_Not_Implemented
case .Free_All:
farena_reset(arena)
return
case .Resize, .Resize_Non_Zeroed:
if (size > old_size) do data, alloc_error = farena_grow (arena, slice(cursor(old_memory), old_size), size, alignment, mode == .Resize)
else do data, alloc_error = farena_shirnk(arena, slice(cursor(old_memory), old_size), size, alignment)
return
if (size > old_size) do data, error_ = farena_grow (arena, slice(cursor(old_memory), old_size), size, alignment, mode == .Resize)
else do data, error_ = farena_shirnk(arena, slice(cursor(old_memory), old_size), size, alignment)
case .Query_Features:
set := (^Odin_AllocatorModeSet)(old_memory)
if set != nil {
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free_All, .Resize, .Resize_Non_Zeroed, .Query_Features, .Query_Info}
}
return
case .Query_Info:
info := (^Odin_AllocatorQueryInfo)(old_memory)
info.pointer = transmute(rawptr) farena_save(arena^).slot
@@ -165,7 +173,8 @@ farena_odin_allocator_proc :: proc(
info.alignment = DEFAULT_ALIGNMENT
return to_bytes(info), nil
}
panic_contextless("Impossible path")
error = transmute(Odin_AllocatorError) error_
return
}
when ODIN_DEBUG {
farena_ainfo :: #force_inline proc "contextless" (arena: ^FArena) -> AllocatorInfo { return AllocatorInfo{proc_id = .FArena, data = arena} }

View File

@@ -40,7 +40,7 @@ varena_make :: proc(to_reserve, commit_size: int, base_address: uintptr, flags:
verify( to_reserve >= commit_size, "Attempted to commit more than there is to reserve" )
vmem : VirtualMemoryRegion
vmem, alloc_error = virtual_reserve_and_commit( base_address, uint(to_reserve), uint(commit_size) )
if ensure(vmem.base_address == nil || alloc_error != .None, "Failed to allocate requested virtual memory for virtual arena") {
if ensure(vmem.base_address != nil && alloc_error == .None, "Failed to allocate requested virtual memory for virtual arena") {
return
}
arena = transmute(^VArena) vmem.base_address;
@@ -59,7 +59,7 @@ varena_alloc :: proc(self: ^VArena,
verify( alignment & (alignment - 1) == 0, "Non-power of two alignment", location = location )
page_size := uint(virtual_get_page_size())
requested_size := uint(size)
if ensure(requested_size == 0, "Requested 0 size") do return nil, .Invalid_Argument
if ensure(requested_size > 0, "Requested 0 size") do return nil, .Invalid_Argument
// ensure( requested_size > page_size, "Requested less than a page size, going to allocate a page size")
// requested_size = max(requested_size, page_size)
@@ -106,11 +106,11 @@ varena_alloc :: proc(self: ^VArena,
varena_grow :: #force_inline proc(self: ^VArena, old_memory: []byte, requested_size: int, alignment: int = DEFAULT_ALIGNMENT, zero_memory := true, loc := #caller_location
) -> (data: []byte, error: AllocatorError)
{
if ensure(old_memory == nil, "Growing without old_memory?") {
if ensure(old_memory != nil, "Growing without old_memory?") {
data, error = varena_alloc(self, requested_size, alignment, zero_memory, loc)
return
}
if ensure(requested_size == len(old_memory), "Requested grow when none needed") {
if ensure(requested_size != len(old_memory), "Requested grow when none needed") {
data = old_memory
return
}
@@ -137,7 +137,7 @@ varena_grow :: #force_inline proc(self: ^VArena, old_memory: []byte, requested_s
// Give it new memory and copy the old over. Old memory is unrecoverable until clear.
new_region : []byte
new_region, error = varena_alloc( self, requested_size, alignment, zero_memory, loc )
if ensure(new_region == nil || error != .None, "Failed to grab new region") {
if ensure(new_region != nil && error == .None, "Failed to grab new region") {
data = old_memory
return
}
@@ -148,7 +148,7 @@ varena_grow :: #force_inline proc(self: ^VArena, old_memory: []byte, requested_s
}
new_region : []byte
new_region, error = varena_alloc( self, requested_size - len(old_memory), alignment, zero_memory, loc)
if ensure(new_region == nil || error != .None, "Failed to grab new region") {
if ensure(new_region != nil && error == .None, "Failed to grab new region") {
data = old_memory
return
}
@@ -158,7 +158,7 @@ varena_grow :: #force_inline proc(self: ^VArena, old_memory: []byte, requested_s
}
varena_shrink :: proc(self: ^VArena, memory: []byte, requested_size: int, loc := #caller_location) -> (data: []byte, error: AllocatorError) {
if requested_size == len(memory) { return memory, .None }
if ensure(memory == nil, "Shrinking without old_memory?") do return memory, .Invalid_Argument
if ensure(memory != nil, "Shrinking without old_memory?") do return memory, .Invalid_Argument
current_offset := self.reserve_start[self.commit_used:]
shrink_amount := len(memory) - requested_size
if shrink_amount < 0 { return memory, .None }
@@ -204,12 +204,16 @@ varena_allocator_proc :: proc(input: AllocatorProc_In, output: ^AllocatorProc_Ou
varena_rewind(arena, input.save_point)
case .SavePoint:
output.save_point = varena_save(arena)
case .Is_Owner:
output.error = .Mode_Not_Implemented
case .Query:
output.features = {.Alloc, .Reset, .Grow, .Shrink, .Rewind}
output.features = {.Alloc, .Reset, .Grow, .Shrink, .Rewind, .Actually_Resize, .Hint_Fast_Bump, .Is_Owner}
output.max_alloc = int(arena.reserved) - arena.commit_used
output.min_alloc = 0
output.left = output.max_alloc
output.save_point = varena_save(arena)
case .Startup, .Shutdown, .Thread_Start, .Thread_Stop:
output.error = .Mode_Not_Implemented
}
}
varena_odin_allocator_proc :: proc(
@@ -220,21 +224,21 @@ varena_odin_allocator_proc :: proc(
old_memory : rawptr,
old_size : int,
location : SourceCodeLocation = #caller_location
) -> (data: []byte, alloc_error: AllocatorError)
) -> (data: []byte, error: Odin_AllocatorError)
{
error_: AllocatorError
arena := transmute( ^VArena) allocator_data
page_size := uint(virtual_get_page_size())
switch mode {
case .Alloc, .Alloc_Non_Zeroed:
data, alloc_error = varena_alloc( arena, size, alignment, (mode == .Alloc), location )
return
data, error_ = varena_alloc( arena, size, alignment, (mode == .Alloc), location )
case .Free:
alloc_error = .Mode_Not_Implemented
error = .Mode_Not_Implemented
case .Free_All:
varena_reset( arena )
case .Resize, .Resize_Non_Zeroed:
if size > old_size do varena_grow (arena, slice(cursor(old_memory), old_size), size, alignment, (mode == .Alloc), location)
else do varena_shrink(arena, slice(cursor(old_memory), old_size), size, location)
if size > old_size do data, error_ = varena_grow (arena, slice(cursor(old_memory), old_size), size, alignment, (mode == .Alloc), location)
else do data, error_ = varena_shrink(arena, slice(cursor(old_memory), old_size), size, location)
case .Query_Features:
set := cast( ^Odin_AllocatorModeSet) old_memory
if set != nil do (set ^) = {.Alloc, .Alloc_Non_Zeroed, .Free_All, .Resize, .Query_Features}
@@ -245,6 +249,7 @@ varena_odin_allocator_proc :: proc(
info.alignment = DEFAULT_ALIGNMENT
return to_bytes(info), nil
}
error = transmute(Odin_AllocatorError) error_
return
}

View File

@@ -18,12 +18,12 @@ Arena :: struct {
flags: ArenaFlags,
}
arena_make :: proc(reserve_size : int = Mega * 64, commit_size : int = Mega * 64, base_addr: uintptr = 0, flags: ArenaFlags = {}) -> ^Arena {
arena_make :: proc(reserve_size : int = Mega * 64, commit_size : int = Mega * 64, base_addr: uintptr = 0, flags: ArenaFlags = {}) -> (^Arena, AllocatorError) {
header_size := align_pow2(size_of(Arena), DEFAULT_ALIGNMENT)
current, error := varena_make(reserve_size, commit_size, base_addr, transmute(VArenaFlags) flags)
assert(error == .None)
if ensure(error == .None) do return nil, error
arena: ^Arena; arena, error = varena_push_item(current, Arena, 1)
assert(error == .None)
if ensure(error == .None) do return nil, error
arena^ = Arena {
backing = current,
prev = nil,
@@ -32,9 +32,9 @@ arena_make :: proc(reserve_size : int = Mega * 64, commit_size : int = Mega * 64
pos = header_size,
flags = flags,
}
return arena
return arena, .None
}
arena_alloc :: proc(arena: ^Arena, size: int, alignment: int = DEFAULT_ALIGNMENT, should_zero := true) -> []byte {
arena_alloc :: proc(arena: ^Arena, size: int, alignment: int = DEFAULT_ALIGNMENT, should_zero := true, loc := #caller_location) -> (allocation: []byte, error: AllocatorError) {
assert(arena != nil)
active := arena.current
size_requested := size
@@ -44,20 +44,24 @@ arena_alloc :: proc(arena: ^Arena, size: int, alignment: int = DEFAULT_ALIGNMENT
reserved := int(active.backing.reserved)
should_chain := (.No_Chaining not_in arena.flags) && (reserved < pos_pst)
if should_chain {
new_arena := arena_make(reserved, active.backing.commit_size, 0, transmute(ArenaFlags) active.backing.flags)
new_arena: ^Arena; new_arena, error = arena_make(reserved, active.backing.commit_size, 0, transmute(ArenaFlags) active.backing.flags)
if ensure(error == .None) do return
new_arena.base_pos = active.base_pos + reserved
sll_stack_push_n(& arena.current, & new_arena, & new_arena.prev)
new_arena.prev = active
active = arena.current
}
result_ptr := transmute([^]byte) (uintptr(active) + uintptr(pos_pre))
vresult, error := varena_alloc(active.backing, size_aligned, alignment, should_zero)
assert(error == .None)
vresult: []byte; vresult, error = varena_alloc(active.backing, size_aligned, alignment, should_zero)
if ensure(error == .None) do return
assert(cursor(vresult) == result_ptr)
active.pos = pos_pst
return slice(result_ptr, size)
allocation = slice(result_ptr, size)
return
}
arena_grow :: proc(arena: ^Arena, old_allocation: []byte, requested_size: int, alignment: int = DEFAULT_ALIGNMENT, zero_memory := true) -> (allocation: []byte) {
arena_grow :: proc(arena: ^Arena, old_allocation: []byte, requested_size: int, alignment: int = DEFAULT_ALIGNMENT, zero_memory := true, loc := #caller_location
) -> (allocation: []byte, error: AllocatorError)
{
active := arena.current
if len(old_allocation) == 0 { allocation = {}; return }
alloc_end := end(old_allocation)
@@ -69,22 +73,37 @@ arena_grow :: proc(arena: ^Arena, old_allocation: []byte, requested_size: int, a
aligned_grow := align_pow2(grow_amount, alignment)
if active.pos + aligned_grow <= cast(int) active.backing.reserved
{
vresult, error := varena_alloc(active.backing, aligned_grow, alignment, zero_memory);
assert(error == .None)
if len(vresult) > 0 {
active.pos += aligned_grow
allocation = slice(cursor(old_allocation), requested_size)
return
}
vresult: []byte; vresult, error = varena_alloc(active.backing, aligned_grow, alignment, zero_memory)
if ensure(error == .None) do return
active.pos += aligned_grow
allocation = slice(cursor(old_allocation), requested_size)
return
}
}
// Can't grow in place, allocate new
allocation = arena_alloc(arena, requested_size, alignment, false)
if len(allocation) == 0 { allocation = {}; return }
allocation, error = arena_alloc(arena, requested_size, alignment, false)
if ensure(error == .None) do return
copy(allocation, old_allocation)
zero(cursor(allocation)[len(old_allocation):], (requested_size - len(old_allocation)) * int(zero_memory))
return
}
arena_shrink :: proc(arena: ^Arena, old_allocation: []byte, requested_size, alignment: int, loc := #caller_location) -> (result: []byte, error: AllocatorError) {
active := arena.current
if ensure(len(old_allocation) != 0) { return }
alloc_end := end(old_allocation)
arena_end := transmute([^]byte) (uintptr(active) + uintptr(active.pos))
if alloc_end != arena_end {
// Not at the end, can't shrink but return adjusted size
result = old_allocation[:requested_size]
}
// Calculate shrinkage
aligned_original := align_pow2(len(old_allocation), DEFAULT_ALIGNMENT)
aligned_new := align_pow2(requested_size, alignment)
pos_reduction := aligned_original - aligned_new
active.pos -= pos_reduction
result, error = varena_shrink(active.backing, old_allocation, aligned_new)
return
}
arena_release :: proc(arena: ^Arena) {
assert(arena != nil)
curr := arena.current
@@ -97,7 +116,7 @@ arena_release :: proc(arena: ^Arena) {
arena_reset :: proc(arena: ^Arena) {
arena_rewind(arena, AllocatorSP { type_sig = arena_allocator_proc, slot = 0 })
}
arena_rewind :: proc(arena: ^Arena, save_point: AllocatorSP) {
arena_rewind :: proc(arena: ^Arena, save_point: AllocatorSP, loc := #caller_location) {
assert(arena != nil)
assert(save_point.type_sig == arena_allocator_proc)
header_size := align_pow2(size_of(Arena), DEFAULT_ALIGNMENT)
@@ -117,7 +136,36 @@ arena_rewind :: proc(arena: ^Arena, save_point: AllocatorSP) {
arena_save :: #force_inline proc(arena: ^Arena) -> AllocatorSP { return { type_sig = arena_allocator_proc, slot = arena.base_pos + arena.current.pos } }
arena_allocator_proc :: proc(input: AllocatorProc_In, output: ^AllocatorProc_Out) {
panic("not implemented")
assert(output != nil)
assert(input.data != nil)
arena := transmute(^Arena) input.data
switch input.op {
case .Alloc, .Alloc_NoZero:
output.allocation, output.error = arena_alloc(arena, input.requested_size, input.alignment, input.op == .Alloc, input.loc)
return
case .Free:
output.error = .Mode_Not_Implemented
case .Reset:
arena_reset(arena)
case .Grow, .Grow_NoZero:
output.allocation, output.error = arena_grow(arena, input.old_allocation, input.requested_size, input.alignment, input.op == .Grow, input.loc)
case .Shrink:
output.allocation, output.error = arena_shrink(arena, input.old_allocation, input.requested_size, input.alignment, input.loc)
case .Rewind:
arena_rewind(arena, input.save_point, input.loc)
case .SavePoint:
output.save_point = arena_save(arena)
case .Query:
output.features = {.Alloc, .Reset, .Grow, .Shrink, .Rewind, .Actually_Resize, .Is_Owner, .Hint_Fast_Bump }
output.max_alloc = int(arena.backing.reserved)
output.min_alloc = 0
output.left = output.max_alloc
output.save_point = arena_save(arena)
case .Is_Owner:
output.error = .Mode_Not_Implemented
case .Startup, .Shutdown, .Thread_Start, .Thread_Stop:
output.error = .Mode_Not_Implemented
}
}
arena_odin_allocator_proc :: proc(
allocator_data : rawptr,
@@ -127,7 +175,7 @@ arena_odin_allocator_proc :: proc(
old_memory : rawptr,
old_size : int,
location : SourceCodeLocation = #caller_location
) -> (data: []byte, alloc_error: AllocatorError)
) -> (data: []byte, alloc_error: Odin_AllocatorError)
{
panic("not implemented")
}

View File

@@ -40,7 +40,7 @@ virtual_commit :: proc "contextless" ( using vmem : VirtualMemoryRegion, size :
page_size := uint(virtual_get_page_size())
to_commit := memory_align_formula( size, page_size )
alloc_error = core_virtual.commit( base_address, to_commit )
alloc_error = cast(AllocatorError) core_virtual.commit( base_address, to_commit )
if alloc_error != .None {
return alloc_error
}