From 1b75c44508220a1dbb065d23b436ce21d6e7e6fe Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 23 Jun 2026 12:18:15 +0100 Subject: [PATCH] Change `mem.Dynamic_Arena`'s `alignment` to be `minimum_alignment` --- core/mem/allocators.odin | 74 ++++++++++--------- ..._pool.odin => test_mem_dynamic_arena.odin} | 21 +++--- 2 files changed, 50 insertions(+), 45 deletions(-) rename tests/core/mem/{test_mem_dynamic_pool.odin => test_mem_dynamic_arena.odin} (87%) diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index 7257f3894..40c320115 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -1637,7 +1637,7 @@ Dynamic arena allocator data. Dynamic_Arena :: struct { block_size: int, out_band_size: int, - alignment: int, + minimum_alignment: int, unused_blocks: [dynamic]rawptr, used_blocks: [dynamic]rawptr, out_band_allocations: [dynamic]rawptr, @@ -1654,19 +1654,19 @@ This procedure initializes a dynamic arena. The specified `block_allocator` will be used to allocate arena blocks, and `array_allocator` to allocate arrays of blocks and out-band blocks. The blocks have the default size of `block_size` and out-band threshold will be `out_band_size`. All allocations -will be aligned to a boundary specified by `alignment`. +will be aligned at a minimum to a boundary specified by `minimum_alignment`. */ dynamic_arena_init :: proc( arena: ^Dynamic_Arena, - block_allocator := context.allocator, - array_allocator := context.allocator, - block_size := DYNAMIC_ARENA_BLOCK_SIZE_DEFAULT, - out_band_size := DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT, - alignment := DEFAULT_ALIGNMENT, + block_allocator := context.allocator, + array_allocator := context.allocator, + block_size := DYNAMIC_ARENA_BLOCK_SIZE_DEFAULT, + out_band_size := DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT, + minimum_alignment := DEFAULT_ALIGNMENT, ) { arena.block_size = block_size arena.out_band_size = out_band_size - arena.alignment = alignment + arena.minimum_alignment = minimum_alignment arena.block_allocator = block_allocator arena.out_band_allocations.allocator = array_allocator arena.unused_blocks.allocator = array_allocator @@ -1714,7 +1714,7 @@ dynamic_arena_destroy :: proc(a: ^Dynamic_Arena) { } @(private="file") -_dynamic_arena_cycle_new_block :: proc(a: ^Dynamic_Arena, loc := #caller_location) -> (err: Allocator_Error) { +_dynamic_arena_cycle_new_block :: proc(a: ^Dynamic_Arena, alignment: int, loc := #caller_location) -> (err: Allocator_Error) { if a.block_allocator.procedure == nil { panic("You must call `dynamic_arena_init` on a Dynamic Arena before using it.", loc) } @@ -1730,7 +1730,7 @@ _dynamic_arena_cycle_new_block :: proc(a: ^Dynamic_Arena, loc := #caller_locatio a.block_allocator.data, Allocator_Mode.Alloc, a.block_size, - a.alignment, + max(a.minimum_alignment, alignment), nil, 0, ) @@ -1752,8 +1752,8 @@ zero-initialized. This procedure returns a pointer to the newly allocated memory region. */ @(require_results) -dynamic_arena_alloc :: proc(a: ^Dynamic_Arena, size: int, loc := #caller_location) -> (rawptr, Allocator_Error) { - data, err := dynamic_arena_alloc_bytes(a, size, loc) +dynamic_arena_alloc :: proc(a: ^Dynamic_Arena, size: int, alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location) -> (rawptr, Allocator_Error) { + data, err := dynamic_arena_alloc_bytes(a, size, alignment, loc) return raw_data(data), err } @@ -1766,8 +1766,8 @@ zero-initialized. This procedure returns a slice of the newly allocated memory region. */ @(require_results) -dynamic_arena_alloc_bytes :: proc(a: ^Dynamic_Arena, size: int, loc := #caller_location) -> ([]byte, Allocator_Error) { - bytes, err := dynamic_arena_alloc_bytes_non_zeroed(a, size, loc) +dynamic_arena_alloc_bytes :: proc(a: ^Dynamic_Arena, size: int, alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location) -> ([]byte, Allocator_Error) { + bytes, err := dynamic_arena_alloc_bytes_non_zeroed(a, size, alignment, loc) if bytes != nil { zero_slice(bytes) } @@ -1783,8 +1783,8 @@ zero-initialized. This procedure returns a pointer to the newly allocated memory region. */ @(require_results) -dynamic_arena_alloc_non_zeroed :: proc(a: ^Dynamic_Arena, size: int, loc := #caller_location) -> (rawptr, Allocator_Error) { - data, err := dynamic_arena_alloc_bytes_non_zeroed(a, size, loc) +dynamic_arena_alloc_non_zeroed :: proc(a: ^Dynamic_Arena, size: int, alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location) -> (rawptr, Allocator_Error) { + data, err := dynamic_arena_alloc_bytes_non_zeroed(a, size, alignment, loc) return raw_data(data), err } @@ -1797,21 +1797,21 @@ zero-initialized. This procedure returns a slice of the newly allocated memory region. */ @(require_results) -dynamic_arena_alloc_bytes_non_zeroed :: proc(a: ^Dynamic_Arena, size: int, loc := #caller_location) -> ([]byte, Allocator_Error) { +dynamic_arena_alloc_bytes_non_zeroed :: proc(a: ^Dynamic_Arena, size: int, alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location) -> ([]byte, Allocator_Error) { if size >= a.out_band_size { assert(a.out_band_allocations.allocator.procedure != nil, "Backing array allocator must be initialized", loc=loc) - memory, err := alloc_bytes_non_zeroed(size, a.alignment, a.out_band_allocations.allocator, loc) + memory, err := alloc_bytes_non_zeroed(size, alignment, a.out_band_allocations.allocator, loc) if memory != nil { append(&a.out_band_allocations, raw_data(memory), loc = loc) } return memory, err } - n := align_formula(size, a.alignment) + n := align_formula(size, max(a.minimum_alignment, alignment)) if n > a.block_size { return nil, .Invalid_Argument } if a.bytes_left < n { - err := _dynamic_arena_cycle_new_block(a, loc) + err := _dynamic_arena_cycle_new_block(a, alignment, loc) if err != nil { return nil, err } @@ -1886,9 +1886,10 @@ dynamic_arena_resize :: proc( old_memory: rawptr, old_size: int, size: int, + alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location, ) -> (rawptr, Allocator_Error) { - bytes, err := dynamic_arena_resize_bytes(a, byte_slice(old_memory, old_size), size, loc) + bytes, err := dynamic_arena_resize_bytes(a, byte_slice(old_memory, old_size), size, alignment, loc) return raw_data(bytes), err } @@ -1907,16 +1908,17 @@ This procedure returns the slice of the resized memory region. */ @(require_results) dynamic_arena_resize_bytes :: proc( - a: ^Dynamic_Arena, - old_data: []byte, - size: int, + a: ^Dynamic_Arena, + old_data: []byte, + size: int, + alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location, ) -> ([]byte, Allocator_Error) { if size == 0 { // NOTE: This allocator has no Free mode. return nil, nil } - bytes, err := dynamic_arena_resize_bytes_non_zeroed(a, old_data, size, loc) + bytes, err := dynamic_arena_resize_bytes_non_zeroed(a, old_data, size, alignment, loc) if bytes != nil { if old_data == nil { zero_slice(bytes) @@ -1946,9 +1948,10 @@ dynamic_arena_resize_non_zeroed :: proc( old_memory: rawptr, old_size: int, size: int, + alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location, ) -> (rawptr, Allocator_Error) { - bytes, err := dynamic_arena_resize_bytes_non_zeroed(a, byte_slice(old_memory, old_size), size, loc) + bytes, err := dynamic_arena_resize_bytes_non_zeroed(a, byte_slice(old_memory, old_size), size, alignment, loc) return raw_data(bytes), err } @@ -1967,9 +1970,10 @@ This procedure returns the slice of the resized memory region. */ @(require_results) dynamic_arena_resize_bytes_non_zeroed :: proc( - a: ^Dynamic_Arena, - old_data: []byte, - size: int, + a: ^Dynamic_Arena, + old_data: []byte, + size: int, + alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location, ) -> ([]byte, Allocator_Error) { if size == 0 { @@ -1984,7 +1988,7 @@ dynamic_arena_resize_bytes_non_zeroed :: proc( } // No information is kept about allocations in this allocator, thus we // cannot truly resize anything and must reallocate. - data, err := dynamic_arena_alloc_bytes_non_zeroed(a, size, loc) + data, err := dynamic_arena_alloc_bytes_non_zeroed(a, size, alignment, loc) if err == nil { runtime.copy(data, byte_slice(old_memory, old_size)) } @@ -2003,17 +2007,17 @@ dynamic_arena_allocator_proc :: proc( arena := (^Dynamic_Arena)(allocator_data) switch mode { case .Alloc: - return dynamic_arena_alloc_bytes(arena, size, loc) + return dynamic_arena_alloc_bytes(arena, size, alignment, loc) case .Alloc_Non_Zeroed: - return dynamic_arena_alloc_bytes_non_zeroed(arena, size, loc) + return dynamic_arena_alloc_bytes_non_zeroed(arena, size, alignment, loc) case .Free: return nil, .Mode_Not_Implemented case .Free_All: dynamic_arena_free_all(arena, loc) case .Resize: - return dynamic_arena_resize_bytes(arena, byte_slice(old_memory, old_size), size, loc) + return dynamic_arena_resize_bytes(arena, byte_slice(old_memory, old_size), size, alignment, loc) case .Resize_Non_Zeroed: - return dynamic_arena_resize_bytes_non_zeroed(arena, byte_slice(old_memory, old_size), size, loc) + return dynamic_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 { @@ -2024,7 +2028,7 @@ dynamic_arena_allocator_proc :: proc( info := (^Allocator_Query_Info)(old_memory) if info != nil && info.pointer != nil { info.size = arena.block_size - info.alignment = arena.alignment + info.alignment = arena.minimum_alignment return byte_slice(info, size_of(info^)), nil } return nil, nil diff --git a/tests/core/mem/test_mem_dynamic_pool.odin b/tests/core/mem/test_mem_dynamic_arena.odin similarity index 87% rename from tests/core/mem/test_mem_dynamic_pool.odin rename to tests/core/mem/test_mem_dynamic_arena.odin index 82cee89af..20853c9cf 100644 --- a/tests/core/mem/test_mem_dynamic_pool.odin +++ b/tests/core/mem/test_mem_dynamic_arena.odin @@ -6,7 +6,7 @@ import "core:mem" expect_pool_allocation :: proc(t: ^testing.T, expected_used_bytes, num_bytes, alignment: int) { pool: mem.Dynamic_Pool - mem.dynamic_pool_init(&pool, alignment = alignment) + mem.dynamic_pool_init(&pool, minimum_alignment = alignment) pool_allocator := mem.dynamic_pool_allocator(&pool) element, err := mem.alloc(num_bytes, alignment, pool_allocator) @@ -18,20 +18,20 @@ expect_pool_allocation :: proc(t: ^testing.T, expected_used_bytes, num_bytes, al ` Allocated data with size %v bytes, expected %v bytes left, got %v bytes left, off by %v bytes. Pool: - block_size = %v - out_band_size = %v - alignment = %v - unused_blocks = %v - used_blocks = %v + block_size = %v + out_band_size = %v + minimum_alignment = %v + unused_blocks = %v + used_blocks = %v out_band_allocations = %v - current_block = %v - current_pos = %v - bytes_left = %v + current_block = %v + current_pos = %v + bytes_left = %v `, num_bytes, expected_bytes_left, pool.bytes_left, expected_bytes_left - pool.bytes_left, pool.block_size, pool.out_band_size, - pool.alignment, + pool.minimum_alignment, pool.unused_blocks, pool.used_blocks, pool.out_band_allocations, @@ -39,6 +39,7 @@ expect_pool_allocation :: proc(t: ^testing.T, expected_used_bytes, num_bytes, al pool.current_pos, pool.bytes_left, ) + testing.expectf(t, uintptr(element) % uint(alignment) == 0, "Expected allocation to be aligned to %d byte boundary, got %v", alignment, element) mem.dynamic_pool_destroy(&pool) testing.expect(t, pool.used_blocks == nil)