Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added functionality to the mem.Arena allocator procedure, allowing for in-place growth. #4863

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 151 additions & 2 deletions core/mem/allocators.odin
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,155 @@ arena_free_all :: proc(a: ^Arena) {
a.offset = 0
}

/*
Resize an allocation.

This procedure resizes a memory region, specified by `old_data`, to have a size
`size` and alignment `alignment`. The newly allocated memory, if any
is not explicityly zero-initialized.

If `old_memory` is `nil`, this procedure acts just like `arena_alloc_bytes_non_zeroed()`,
allocating a memory region `size` bytes in size, aligned on a boundary specified
by `alignment`.

If `size` is 0 and there is no memory allocated after the `old_data`, this procedure
will free the memory.

This procedure returns the slice of the resized memory region.
*/
arena_resize_bytes_non_zeroed :: proc(
a: ^Arena,
old_data: []byte,
size: int,
alignment := DEFAULT_ALIGNMENT,
loc := #caller_location,
) -> ([]byte, Allocator_Error) {
if old_data == nil {
return arena_alloc_bytes_non_zeroed(a, size, loc=loc)
}
if size == len(old_data) {
return old_data, nil
}

old_data_offset := cast(int)(cast(uintptr)raw_data(old_data) - cast(uintptr)raw_data(a.data))
if a.offset - len(old_data) == old_data_offset {
if size == 0 {
a.offset = old_data_offset
return nil, nil
}

new_offset := old_data_offset + size
if new_offset > len(a.data) {
return old_data, .Out_Of_Memory
}

a.offset = new_offset
a.peak_used = max(a.peak_used, a.offset)
return a.data[old_data_offset:][:size], nil
} else {
if size == 0 {
return nil, nil
}
new_data, err := arena_alloc_bytes_non_zeroed(a, size, alignment, loc)
if err != nil {
return old_data, err
}
copy_non_overlapping(raw_data(new_data), raw_data(old_data), len(old_data))
return new_data, nil
}
}

/*
Resize an allocation.

This procedure resizes a memory region, specified by `old_data` and `old_size`,
to have a size `size` and alignment `alignment`. The newly allocated memory,
if any is not explicityly zero-initialized.

If `old_memory` is `nil`, this procedure acts just like `arena_alloc_non_zeroed()`,
allocating a memory region `size` bytes in size, aligned on a boundary specified
by `alignment`.

If `size` is 0 and there is no memory allocated after the `old_data`, this procedure
will free the memory.

This procedure returns a raw pointer of the resized memory region.
*/
arena_resize_non_zeroed :: proc(
a: ^Arena,
old_data: rawptr,
old_size: int,
size: int,
alignment := DEFAULT_ALIGNMENT,
loc := #caller_location,
) -> (rawptr, Allocator_Error) {
bytes, err := arena_resize_bytes(a, byte_slice(old_data, old_size), size, alignment, loc)
return raw_data(bytes), err
}

/*
Resize an allocation.

This procedure resizes a memory region, specified by `old_data`, to have a size
`size` and alignment `alignment`. The newly allocated memory, if any
is zero-initialized.

If `old_memory` is `nil`, this procedure acts just like `arena_alloc_bytes()`,
allocating a memory region `size` bytes in size, aligned on a boundary specified
by `alignment`.

If `size` is 0 and there is no memory allocated after the `old_data`, this procedure
will free the memory.

This procedure returns the slice of the resized memory region.
*/
@(require_results)
arena_resize_bytes :: proc(
a: ^Arena,
old_data: []byte,
size: int,
alignment := DEFAULT_ALIGNMENT,
loc := #caller_location,
) -> ([]byte, Allocator_Error) {
bytes, err := arena_resize_bytes_non_zeroed(a, old_data, size, alignment, loc)
if bytes != nil {
if old_data == nil {
zero_slice(bytes)
} else if size > len(old_data) {
zero_slice(bytes[len(old_data):])
}
}
return bytes, err
}

/*
Resize an allocation.

This procedure resizes a memory region, specified by `old_data` and `old_size`,
to have a size `size` and alignment `alignment`. The newly allocated memory,
if any is zero-initialized.

If `old_memory` is `nil`, this procedure acts just like `arena_alloc()`,
allocating a memory region `size` bytes in size, aligned on a boundary specified
by `alignment`.

If `size` is 0 and there is no memory allocated after the `old_data`, this procedure
will free the memory.

This procedure returns a raw pointer of the resized memory region.
*/
arena_resize :: proc(
a: ^Arena,
old_data: rawptr,
old_size: int,
size: int,
alignment := DEFAULT_ALIGNMENT,
loc := #caller_location,
) -> (rawptr, Allocator_Error) {
bytes, err := arena_resize_bytes(a, byte_slice(old_data, old_size), size, alignment, loc)
return raw_data(bytes), err
}

arena_allocator_proc :: proc(
allocator_data: rawptr,
mode: Allocator_Mode,
Expand All @@ -254,9 +403,9 @@ arena_allocator_proc :: proc(
case .Free_All:
arena_free_all(arena)
case .Resize:
return default_resize_bytes_align(byte_slice(old_memory, old_size), size, alignment, arena_allocator(arena), loc)
return arena_resize_bytes(arena, byte_slice(old_memory, old_size), size, alignment, loc)
case .Resize_Non_Zeroed:
return default_resize_bytes_align_non_zeroed(byte_slice(old_memory, old_size), size, alignment, arena_allocator(arena), loc)
return arena_resize_bytes_non_zeroed(arena, byte_slice(old_memory, old_size), size, alignment, loc)
case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory)
if set != nil {
Expand Down