|
|
|
@@ -81,7 +81,7 @@ AllocatorQueryFlag :: enum u64 {
|
|
|
|
|
Hint_Per_Frame_Temporary,
|
|
|
|
|
Hint_Debug_Support,
|
|
|
|
|
}
|
|
|
|
|
AllocatorError :: enum byte {
|
|
|
|
|
AllocatorError :: enum i32 {
|
|
|
|
|
None = 0,
|
|
|
|
|
Out_Of_Memory = 1,
|
|
|
|
|
Invalid_Pointer = 2,
|
|
|
|
@@ -121,37 +121,69 @@ AllocatorQueryInfo :: struct {
|
|
|
|
|
left: int,
|
|
|
|
|
max_alloc: int,
|
|
|
|
|
min_alloc: int,
|
|
|
|
|
continuity_break: b32,
|
|
|
|
|
alignment: i32,
|
|
|
|
|
}
|
|
|
|
|
AllocatorInfo :: struct {
|
|
|
|
|
procedure: AllocatorProc,
|
|
|
|
|
_ : struct #raw_union {
|
|
|
|
|
procedure: AllocatorProc,
|
|
|
|
|
proc_id: AllocatorProcID,
|
|
|
|
|
},
|
|
|
|
|
data: rawptr,
|
|
|
|
|
}
|
|
|
|
|
// #assert(size_of(AllocatorQueryInfo) == size_of(AllocatorProc_Out))
|
|
|
|
|
|
|
|
|
|
// Listing of every single allocator (used on hot-reloadable builds)
|
|
|
|
|
AllocatorProcID :: enum uintptr {
|
|
|
|
|
FArena,
|
|
|
|
|
VArena,
|
|
|
|
|
CArena,
|
|
|
|
|
Pool,
|
|
|
|
|
Slab,
|
|
|
|
|
Odin_Arena,
|
|
|
|
|
// Odin_VArena,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
resolve_allocator_proc :: #force_inline proc(procedure: Odin_AllocatorProc) -> AllocatorProc {
|
|
|
|
|
when ODIN_DEBUG {
|
|
|
|
|
switch (transmute(AllocatorProcID)procedure) {
|
|
|
|
|
case .FArena: return nil // farena_allocator_proc
|
|
|
|
|
case .VArena: return nil // varena_allocaotr_proc
|
|
|
|
|
case .CArena: return nil // carena_allocator_proc
|
|
|
|
|
case .Pool: return nil // pool_allocator_proc
|
|
|
|
|
case .Slab: return nil // slab_allocator_proc
|
|
|
|
|
case .Odin_Arena: return nil // odin_arena_allocator_proc
|
|
|
|
|
// case .Odin_VArena: return odin_varena_allocator_proc
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return transmute(AllocatorProc) procedure
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MEMORY_ALIGNMENT_DEFAULT :: 2 * size_of(rawptr)
|
|
|
|
|
|
|
|
|
|
allocator_query :: proc(ainfo := context.allocator) -> AllocatorQueryInfo {
|
|
|
|
|
assert(ainfo.procedure != nil)
|
|
|
|
|
out: AllocatorQueryInfo; (cast(AllocatorProc)ainfo.procedure)({data = ainfo.data, op = .Query}, transmute(^AllocatorProc_Out) & out)
|
|
|
|
|
out: AllocatorQueryInfo; resolve_allocator_proc(ainfo.procedure)({data = ainfo.data, op = .Query}, transmute(^AllocatorProc_Out) & out)
|
|
|
|
|
return out
|
|
|
|
|
}
|
|
|
|
|
mem_free :: proc(mem: []byte, ainfo := context.allocator) {
|
|
|
|
|
assert(ainfo.procedure != nil)
|
|
|
|
|
(cast(AllocatorProc)ainfo.procedure)({data = ainfo.data, op = .Free, old_allocation = mem}, & {})
|
|
|
|
|
resolve_allocator_proc(ainfo.procedure)({data = ainfo.data, op = .Free, old_allocation = mem}, & {})
|
|
|
|
|
}
|
|
|
|
|
mem_reset :: proc(ainfo := context.allocator) {
|
|
|
|
|
assert(ainfo.procedure != nil)
|
|
|
|
|
(cast(AllocatorProc)ainfo.procedure)({data = ainfo.data, op = .Reset}, &{})
|
|
|
|
|
resolve_allocator_proc(ainfo.procedure)({data = ainfo.data, op = .Reset}, &{})
|
|
|
|
|
}
|
|
|
|
|
mem_rewind :: proc(ainfo := context.allocator, save_point: AllocatorSP) {
|
|
|
|
|
assert(ainfo.procedure != nil)
|
|
|
|
|
(cast(AllocatorProc)ainfo.procedure)({data = ainfo.data, op = .Rewind, save_point = save_point}, & {})
|
|
|
|
|
resolve_allocator_proc(ainfo.procedure)({data = ainfo.data, op = .Rewind, save_point = save_point}, & {})
|
|
|
|
|
}
|
|
|
|
|
mem_save_point :: proc(ainfo := context.allocator) -> AllocatorSP {
|
|
|
|
|
assert(ainfo.procedure != nil)
|
|
|
|
|
out: AllocatorProc_Out
|
|
|
|
|
(cast(AllocatorProc)ainfo.procedure)({data = ainfo.data, op = .SavePoint}, & out)
|
|
|
|
|
resolve_allocator_proc(ainfo.procedure)({data = ainfo.data, op = .SavePoint}, & out)
|
|
|
|
|
return out.save_point
|
|
|
|
|
}
|
|
|
|
|
mem_alloc :: proc(size: int, alignment: int = MEMORY_ALIGNMENT_DEFAULT, no_zero: b32 = false, ainfo := context.allocator) -> []byte {
|
|
|
|
@@ -163,7 +195,7 @@ mem_alloc :: proc(size: int, alignment: int = MEMORY_ALIGNMENT_DEFAULT, no_zero:
|
|
|
|
|
alignment = alignment,
|
|
|
|
|
}
|
|
|
|
|
output: AllocatorProc_Out
|
|
|
|
|
(cast(AllocatorProc)ainfo.procedure)(input, & output)
|
|
|
|
|
resolve_allocator_proc(ainfo.procedure)(input, & output)
|
|
|
|
|
return output.allocation
|
|
|
|
|
}
|
|
|
|
|
mem_grow :: proc(mem: []byte, size: int, alignment: int = MEMORY_ALIGNMENT_DEFAULT, no_zero: b32 = false, ainfo := context.allocator) -> []byte {
|
|
|
|
@@ -176,7 +208,7 @@ mem_grow :: proc(mem: []byte, size: int, alignment: int = MEMORY_ALIGNMENT_DEFAU
|
|
|
|
|
old_allocation = mem,
|
|
|
|
|
}
|
|
|
|
|
output: AllocatorProc_Out
|
|
|
|
|
(cast(AllocatorProc)ainfo.procedure)(input, & output)
|
|
|
|
|
resolve_allocator_proc(ainfo.procedure)(input, & output)
|
|
|
|
|
return output.allocation
|
|
|
|
|
}
|
|
|
|
|
mem_resize :: proc(mem: []byte, size: int, alignment: int = MEMORY_ALIGNMENT_DEFAULT, no_zero: b32 = false, ainfo := context.allocator) -> []byte {
|
|
|
|
@@ -189,7 +221,7 @@ mem_resize :: proc(mem: []byte, size: int, alignment: int = MEMORY_ALIGNMENT_DEF
|
|
|
|
|
old_allocation = mem,
|
|
|
|
|
}
|
|
|
|
|
output: AllocatorProc_Out
|
|
|
|
|
(cast(AllocatorProc)ainfo.procedure)(input, & output)
|
|
|
|
|
resolve_allocator_proc(ainfo.procedure)(input, & output)
|
|
|
|
|
return output.allocation
|
|
|
|
|
}
|
|
|
|
|
mem_shrink :: proc(mem: []byte, size: int, alignment: int = MEMORY_ALIGNMENT_DEFAULT, no_zero: b32 = false, ainfo := context.allocator) -> []byte {
|
|
|
|
@@ -202,7 +234,7 @@ mem_shrink :: proc(mem: []byte, size: int, alignment: int = MEMORY_ALIGNMENT_DEF
|
|
|
|
|
old_allocation = mem,
|
|
|
|
|
}
|
|
|
|
|
output: AllocatorProc_Out
|
|
|
|
|
(cast(AllocatorProc)ainfo.procedure)(input, & output)
|
|
|
|
|
resolve_allocator_proc(ainfo.procedure)(input, & output)
|
|
|
|
|
return output.allocation
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -215,7 +247,7 @@ alloc_type :: proc($Type: typeid, alignment: int = MEMORY_ALIGNMENT_DEFAULT, no
|
|
|
|
|
alignment = alignment,
|
|
|
|
|
}
|
|
|
|
|
output: AllocatorProc_Out
|
|
|
|
|
(cast(AllocatorProc)ainfo.procedure)(input, & output)
|
|
|
|
|
resolve_allocator_proc(ainfo.procedure)(input, & output)
|
|
|
|
|
return transmute(^Type) raw_data(output.allocation)
|
|
|
|
|
}
|
|
|
|
|
alloc_slice :: proc($SliceType: typeid / []$Type, num : int, alignment: int = MEMORY_ALIGNMENT_DEFAULT, no_zero: b32 = false, ainfo := context.allocator) -> []Type {
|
|
|
|
@@ -227,7 +259,76 @@ alloc_slice :: proc($SliceType: typeid / []$Type, num : int, alignment: int = ME
|
|
|
|
|
alignment = alignment,
|
|
|
|
|
}
|
|
|
|
|
output: AllocatorProc_Out
|
|
|
|
|
(cast(AllocatorProc)ainfo.procedure)(input, & output)
|
|
|
|
|
resolve_allocator_proc(ainfo.procedure)(input, & output)
|
|
|
|
|
return transmute([]Type) slice(raw_data(output.allocation), num)
|
|
|
|
|
}
|
|
|
|
|
//endregion Allocator Interface
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Ideally we wrap all procedures that go to ideomatic odin with the following pattern:
|
|
|
|
|
|
|
|
|
|
Usually we do the following:
|
|
|
|
|
```
|
|
|
|
|
import "core:dynlib"
|
|
|
|
|
os_lib_load :: dynlib.load_library
|
|
|
|
|
```
|
|
|
|
|
Instead:
|
|
|
|
|
os_lib_load :: #force_inline proc "contextless" (... same signature as load_library, allocator := ...) { return dynlib.load_library(..., odin_ainfo_wrap(allocator)) }
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
odin_allocator_mode_to_allocator_op :: #force_inline proc "contextless" (mode: Odin_AllocatorMode, size_diff : int) -> AllocatorOp {
|
|
|
|
|
switch mode {
|
|
|
|
|
case .Alloc: return .Alloc
|
|
|
|
|
case .Alloc_Non_Zeroed: return .Alloc_NoZero
|
|
|
|
|
case .Free: return .Free
|
|
|
|
|
case .Free_All: return .Reset
|
|
|
|
|
case .Resize: return size_diff > 0 ? .Grow : .Shrink
|
|
|
|
|
case .Resize_Non_Zeroed: return size_diff > 0 ? .Grow_NoZero : .Shrink
|
|
|
|
|
case .Query_Features: return .Query
|
|
|
|
|
case .Query_Info: return .Query
|
|
|
|
|
}
|
|
|
|
|
panic_contextless("Impossible path")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
odin_allocator_wrap_proc :: proc(
|
|
|
|
|
allocator_data : rawptr,
|
|
|
|
|
mode : Odin_AllocatorMode,
|
|
|
|
|
size : int,
|
|
|
|
|
alignment : int,
|
|
|
|
|
old_memory : rawptr,
|
|
|
|
|
old_size : int,
|
|
|
|
|
loc := #caller_location
|
|
|
|
|
) -> ( data : []byte, alloc_error : Odin_AllocatorError)
|
|
|
|
|
{
|
|
|
|
|
input := AllocatorProc_In {
|
|
|
|
|
data = (transmute(^AllocatorInfo)allocator_data).data,
|
|
|
|
|
requested_size = size,
|
|
|
|
|
alignment = alignment,
|
|
|
|
|
old_allocation = slice(transmute([^]byte)old_memory, old_size),
|
|
|
|
|
op = odin_allocator_mode_to_allocator_op(mode, size - old_size),
|
|
|
|
|
}
|
|
|
|
|
output: AllocatorProc_Out
|
|
|
|
|
resolve_allocator_proc((transmute(^Odin_Allocator)allocator_data).procedure)(input, & output)
|
|
|
|
|
|
|
|
|
|
#partial switch mode {
|
|
|
|
|
case .Query_Features:
|
|
|
|
|
debug_trap() // TODO(Ed): Finish this...
|
|
|
|
|
return nil, nil
|
|
|
|
|
case .Query_Info:
|
|
|
|
|
info := (^Odin_AllocatorQueryInfo)(old_memory)
|
|
|
|
|
if info != nil && info.pointer != nil {
|
|
|
|
|
info.size = output.left
|
|
|
|
|
info.alignment = cast(int) (transmute(AllocatorQueryInfo)output).alignment
|
|
|
|
|
return slice(transmute(^byte)info, size_of(info^) ), nil
|
|
|
|
|
}
|
|
|
|
|
return nil, nil
|
|
|
|
|
}
|
|
|
|
|
return output.allocation, cast(Odin_AllocatorError)output.error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
odin_ainfo_wrap :: #force_inline proc(ainfo := context.allocator) -> Odin_Allocator {
|
|
|
|
|
@(thread_local)
|
|
|
|
|
cursed_allocator_wrap_ref : Odin_Allocator
|
|
|
|
|
cursed_allocator_wrap_ref = {ainfo.procedure, ainfo.data}
|
|
|
|
|
return {odin_allocator_wrap_proc, & cursed_allocator_wrap_ref}
|
|
|
|
|
}
|
|
|
|
|