mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-05 15:18:49 +00:00
[mem]: Rollback allocator API consistency
This commit is contained in:
@@ -134,36 +134,65 @@ rb_free_all :: proc(stack: ^Rollback_Stack) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Resize an allocation made on a rollback stack allocator.
|
Allocate memory using the rollback stack allocator.
|
||||||
*/
|
*/
|
||||||
@(private="file", require_results)
|
@(require_results)
|
||||||
rb_resize_non_zeroed :: proc(stack: ^Rollback_Stack, ptr: rawptr, old_size, size, alignment: int) -> (result: []byte, err: Allocator_Error) {
|
rb_alloc :: proc(
|
||||||
if ptr != nil {
|
stack: ^Rollback_Stack,
|
||||||
if block, _, ok := rb_find_last_alloc(stack, ptr); ok {
|
size: int,
|
||||||
// `block.offset` should never underflow because it is contingent
|
alignment := DEFAULT_ALIGNMENT,
|
||||||
// on `old_size` in the first place, assuming sane arguments.
|
loc := #caller_location,
|
||||||
assert(block.offset >= cast(uintptr)old_size, "Rollback Stack Allocator received invalid `old_size`.")
|
) -> (rawptr, Allocator_Error) {
|
||||||
if block.offset + cast(uintptr)size - cast(uintptr)old_size < cast(uintptr)len(block.buffer) {
|
bytes, err := rb_alloc_bytes_non_zeroed(stack, size, alignment, loc)
|
||||||
// Prevent singleton allocations from fragmenting by forbidding
|
if bytes != nil {
|
||||||
// them to shrink, removing the possibility of overflow bugs.
|
zero_slice(bytes)
|
||||||
if len(block.buffer) <= stack.block_size {
|
|
||||||
block.offset += cast(uintptr)size - cast(uintptr)old_size
|
|
||||||
}
|
|
||||||
#no_bounds_check return (cast([^]byte)ptr)[:size], nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
result = rb_alloc_non_zeroed(stack, size, alignment) or_return
|
return raw_data(bytes), err
|
||||||
runtime.mem_copy_non_overlapping(raw_data(result), ptr, old_size)
|
|
||||||
err = rb_free(stack, ptr)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Allocate memory using the rollback stack allocator.
|
Allocate memory using the rollback stack allocator.
|
||||||
*/
|
*/
|
||||||
@(private="file", require_results)
|
@(require_results)
|
||||||
rb_alloc_non_zeroed :: proc(stack: ^Rollback_Stack, size, alignment: int) -> (result: []byte, err: Allocator_Error) {
|
rb_alloc_bytes :: proc(
|
||||||
|
stack: ^Rollback_Stack,
|
||||||
|
size: int,
|
||||||
|
alignment := DEFAULT_ALIGNMENT,
|
||||||
|
loc := #caller_location,
|
||||||
|
) -> ([]byte, Allocator_Error) {
|
||||||
|
bytes, err := rb_alloc_bytes_non_zeroed(stack, size, alignment, loc)
|
||||||
|
if bytes != nil {
|
||||||
|
zero_slice(bytes)
|
||||||
|
}
|
||||||
|
return bytes, err
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Allocate non-initialized memory using the rollback stack allocator.
|
||||||
|
*/
|
||||||
|
@(require_results)
|
||||||
|
rb_alloc_non_zeroed :: proc(
|
||||||
|
stack: ^Rollback_Stack,
|
||||||
|
size: int,
|
||||||
|
alignment := DEFAULT_ALIGNMENT,
|
||||||
|
loc := #caller_location,
|
||||||
|
) -> (rawptr, Allocator_Error) {
|
||||||
|
bytes, err := rb_alloc_bytes_non_zeroed(stack, size, alignment, loc)
|
||||||
|
return raw_data(bytes), err
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Allocate non-initialized memory using the rollback stack allocator.
|
||||||
|
*/
|
||||||
|
@(require_results)
|
||||||
|
rb_alloc_bytes_non_zeroed :: proc(
|
||||||
|
stack: ^Rollback_Stack,
|
||||||
|
size: int,
|
||||||
|
alignment := DEFAULT_ALIGNMENT,
|
||||||
|
loc := #caller_location,
|
||||||
|
) -> (result: []byte, err: Allocator_Error) {
|
||||||
|
assert(size >= 0, "Size must be positive or zero.", loc)
|
||||||
|
assert(is_power_of_two(cast(uintptr)alignment), "Alignment must be a power of two.", loc)
|
||||||
parent: ^Rollback_Stack_Block
|
parent: ^Rollback_Stack_Block
|
||||||
for block := stack.head; /**/; block = block.next_block {
|
for block := stack.head; /**/; block = block.next_block {
|
||||||
when !ODIN_DISABLE_ASSERT {
|
when !ODIN_DISABLE_ASSERT {
|
||||||
@@ -211,6 +240,106 @@ rb_alloc_non_zeroed :: proc(stack: ^Rollback_Stack, size, alignment: int) -> (re
|
|||||||
return nil, .Out_Of_Memory
|
return nil, .Out_Of_Memory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Resize an allocation owned by rollback stack allocator.
|
||||||
|
*/
|
||||||
|
@(require_results)
|
||||||
|
rb_resize :: proc(
|
||||||
|
stack: ^Rollback_Stack,
|
||||||
|
old_ptr: rawptr,
|
||||||
|
old_size: int,
|
||||||
|
size: int,
|
||||||
|
alignment := DEFAULT_ALIGNMENT,
|
||||||
|
loc := #caller_location,
|
||||||
|
) -> (rawptr, Allocator_Error) {
|
||||||
|
bytes, err := rb_resize_bytes_non_zeroed(stack, byte_slice(old_ptr, old_size), size, alignment, loc)
|
||||||
|
if bytes != nil {
|
||||||
|
if old_ptr == nil {
|
||||||
|
zero_slice(bytes)
|
||||||
|
} else if size > old_size {
|
||||||
|
zero_slice(bytes[old_size:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return raw_data(bytes), err
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Resize an allocation owned by rollback stack allocator.
|
||||||
|
*/
|
||||||
|
@(require_results)
|
||||||
|
rb_resize_bytes :: proc(
|
||||||
|
stack: ^Rollback_Stack,
|
||||||
|
old_memory: []byte,
|
||||||
|
size: int,
|
||||||
|
alignment := DEFAULT_ALIGNMENT,
|
||||||
|
loc := #caller_location,
|
||||||
|
) -> ([]u8, Allocator_Error) {
|
||||||
|
bytes, err := rb_resize_bytes_non_zeroed(stack, old_memory, size, alignment, loc)
|
||||||
|
if bytes != nil {
|
||||||
|
if old_memory == nil {
|
||||||
|
zero_slice(bytes)
|
||||||
|
} else if size > len(old_memory) {
|
||||||
|
zero_slice(bytes[len(old_memory):])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bytes, err
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Resize an allocation owned by rollback stack allocator without explicit
|
||||||
|
zero-initialization.
|
||||||
|
*/
|
||||||
|
@(require_results)
|
||||||
|
rb_resize_non_zeroed :: proc(
|
||||||
|
stack: ^Rollback_Stack,
|
||||||
|
old_ptr: rawptr,
|
||||||
|
old_size: int,
|
||||||
|
size: int,
|
||||||
|
alignment := DEFAULT_ALIGNMENT,
|
||||||
|
loc := #caller_location,
|
||||||
|
) -> (rawptr, Allocator_Error) {
|
||||||
|
bytes, err := rb_resize_bytes_non_zeroed(stack, byte_slice(old_ptr, old_size), size, alignment, loc)
|
||||||
|
return raw_data(bytes), err
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Resize an allocation owned by rollback stack allocator without explicit
|
||||||
|
zero-initialization.
|
||||||
|
*/
|
||||||
|
@(require_results)
|
||||||
|
rb_resize_bytes_non_zeroed :: proc(
|
||||||
|
stack: ^Rollback_Stack,
|
||||||
|
old_memory: []byte,
|
||||||
|
size: int,
|
||||||
|
alignment := DEFAULT_ALIGNMENT,
|
||||||
|
loc := #caller_location,
|
||||||
|
) -> (result: []byte, err: Allocator_Error) {
|
||||||
|
old_size := len(old_memory)
|
||||||
|
ptr := raw_data(old_memory)
|
||||||
|
assert(size >= 0, "Size must be positive or zero.", loc)
|
||||||
|
assert(old_size >= 0, "Old size must be positive or zero.", loc)
|
||||||
|
assert(is_power_of_two(cast(uintptr)alignment), "Alignment must be a power of two.", loc)
|
||||||
|
if ptr != nil {
|
||||||
|
if block, _, ok := rb_find_last_alloc(stack, ptr); ok {
|
||||||
|
// `block.offset` should never underflow because it is contingent
|
||||||
|
// on `old_size` in the first place, assuming sane arguments.
|
||||||
|
assert(block.offset >= cast(uintptr)old_size, "Rollback Stack Allocator received invalid `old_size`.")
|
||||||
|
if block.offset + cast(uintptr)size - cast(uintptr)old_size < cast(uintptr)len(block.buffer) {
|
||||||
|
// Prevent singleton allocations from fragmenting by forbidding
|
||||||
|
// them to shrink, removing the possibility of overflow bugs.
|
||||||
|
if len(block.buffer) <= stack.block_size {
|
||||||
|
block.offset += cast(uintptr)size - cast(uintptr)old_size
|
||||||
|
}
|
||||||
|
#no_bounds_check return (ptr)[:size], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = rb_alloc_bytes_non_zeroed(stack, size, alignment) or_return
|
||||||
|
runtime.mem_copy_non_overlapping(raw_data(result), ptr, old_size)
|
||||||
|
err = rb_free(stack, ptr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
@(private="file", require_results)
|
@(private="file", require_results)
|
||||||
rb_make_block :: proc(size: int, allocator: Allocator) -> (block: ^Rollback_Stack_Block, err: Allocator_Error) {
|
rb_make_block :: proc(size: int, allocator: Allocator) -> (block: ^Rollback_Stack_Block, err: Allocator_Error) {
|
||||||
buffer := runtime.mem_alloc(size_of(Rollback_Stack_Block) + size, align_of(Rollback_Stack_Block), allocator) or_return
|
buffer := runtime.mem_alloc(size_of(Rollback_Stack_Block) + size, align_of(Rollback_Stack_Block), allocator) or_return
|
||||||
@@ -321,31 +450,23 @@ rollback_stack_allocator_proc :: proc(
|
|||||||
size, alignment: int,
|
size, alignment: int,
|
||||||
old_memory: rawptr,
|
old_memory: rawptr,
|
||||||
old_size: int,
|
old_size: int,
|
||||||
location := #caller_location,
|
loc := #caller_location,
|
||||||
) -> (result: []byte, err: Allocator_Error) {
|
) -> (result: []byte, err: Allocator_Error) {
|
||||||
stack := cast(^Rollback_Stack)allocator_data
|
stack := cast(^Rollback_Stack)allocator_data
|
||||||
|
|
||||||
switch mode {
|
switch mode {
|
||||||
case .Alloc, .Alloc_Non_Zeroed:
|
case .Alloc:
|
||||||
assert(size >= 0, "Size must be positive or zero.", location)
|
return rb_alloc_bytes(stack, size, alignment, loc)
|
||||||
assert(is_power_of_two(cast(uintptr)alignment), "Alignment must be a power of two.", location)
|
case .Alloc_Non_Zeroed:
|
||||||
result = rb_alloc_non_zeroed(stack, size, alignment) or_return
|
return rb_alloc_bytes_non_zeroed(stack, size, alignment, loc)
|
||||||
if mode == .Alloc {
|
|
||||||
zero_slice(result)
|
|
||||||
}
|
|
||||||
case .Free:
|
case .Free:
|
||||||
err = rb_free(stack, old_memory)
|
return nil, rb_free(stack, old_memory)
|
||||||
|
|
||||||
case .Free_All:
|
case .Free_All:
|
||||||
rb_free_all(stack)
|
rb_free_all(stack)
|
||||||
case .Resize, .Resize_Non_Zeroed:
|
return nil, nil
|
||||||
assert(size >= 0, "Size must be positive or zero.", location)
|
case .Resize:
|
||||||
assert(old_size >= 0, "Old size must be positive or zero.", location)
|
return rb_resize_bytes(stack, byte_slice(old_memory, old_size), size, alignment, loc)
|
||||||
assert(is_power_of_two(cast(uintptr)alignment), "Alignment must be a power of two.", location)
|
case .Resize_Non_Zeroed:
|
||||||
result = rb_resize_non_zeroed(stack, old_memory, old_size, size, alignment) or_return
|
return rb_resize_bytes_non_zeroed(stack, byte_slice(old_memory, old_size), size, alignment, loc)
|
||||||
#no_bounds_check if mode == .Resize && size > old_size {
|
|
||||||
zero_slice(result[old_size:])
|
|
||||||
}
|
|
||||||
case .Query_Features:
|
case .Query_Features:
|
||||||
set := (^Allocator_Mode_Set)(old_memory)
|
set := (^Allocator_Mode_Set)(old_memory)
|
||||||
if set != nil {
|
if set != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user