From 2b9096517c0f69bce6308ea81ff50c9caa8b2fe8 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sat, 7 Sep 2024 09:20:56 +1100 Subject: [PATCH 01/72] [mem]: Code formatting --- core/mem/alloc.odin | 220 +++++++++++++++++++++---- core/mem/allocators.odin | 175 +++++++++++--------- core/mem/mem.odin | 16 +- core/mem/mutex_allocator.odin | 14 +- core/mem/raw.odin | 38 +++-- core/mem/rollback_stack_allocator.odin | 81 ++++----- core/mem/tracking_allocator.odin | 36 ++-- 7 files changed, 390 insertions(+), 190 deletions(-) diff --git a/core/mem/alloc.odin b/core/mem/alloc.odin index e51d971e1..558e810e3 100644 --- a/core/mem/alloc.odin +++ b/core/mem/alloc.odin @@ -63,30 +63,58 @@ DEFAULT_PAGE_SIZE :: 4 * 1024 @(require_results) -alloc :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (rawptr, Allocator_Error) { +alloc :: proc( + size: int, + alignment: int = DEFAULT_ALIGNMENT, + allocator := context.allocator, + loc := #caller_location, +) -> (rawptr, Allocator_Error) { data, err := runtime.mem_alloc(size, alignment, allocator, loc) return raw_data(data), err } @(require_results) -alloc_bytes :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) { +alloc_bytes :: proc( + size: int, + alignment: int = DEFAULT_ALIGNMENT, + allocator := context.allocator, + loc := #caller_location, +) -> ([]byte, Allocator_Error) { return runtime.mem_alloc(size, alignment, allocator, loc) } @(require_results) -alloc_bytes_non_zeroed :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) { +alloc_bytes_non_zeroed :: proc( + size: int, + alignment: int = DEFAULT_ALIGNMENT, + allocator := context.allocator, + loc := #caller_location, +) -> ([]byte, Allocator_Error) { return runtime.mem_alloc_non_zeroed(size, alignment, allocator, loc) } -free :: proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { +free :: proc( + ptr: rawptr, + allocator := context.allocator, + loc := #caller_location, +) -> Allocator_Error { return runtime.mem_free(ptr, allocator, loc) } -free_with_size :: proc(ptr: rawptr, byte_count: int, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { +free_with_size :: proc( + ptr: rawptr, + byte_count: int, + allocator := context.allocator, + loc := #caller_location, +) -> Allocator_Error { return runtime.mem_free_with_size(ptr, byte_count, allocator, loc) } -free_bytes :: proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { +free_bytes :: proc( + bytes: []byte, + allocator := context.allocator, + loc := #caller_location, +) -> Allocator_Error { return runtime.mem_free_bytes(bytes, allocator, loc) } @@ -95,13 +123,26 @@ free_all :: proc(allocator := context.allocator, loc := #caller_location) -> All } @(require_results) -resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (rawptr, Allocator_Error) { +resize :: proc( + ptr: rawptr, + old_size: int, + new_size: int, + alignment: int = DEFAULT_ALIGNMENT, + allocator := context.allocator, + loc := #caller_location, +) -> (rawptr, Allocator_Error) { data, err := runtime.mem_resize(ptr, old_size, new_size, alignment, allocator, loc) return raw_data(data), err } @(require_results) -resize_bytes :: proc(old_data: []byte, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) { +resize_bytes :: proc( + old_data: []byte, + new_size: int, + alignment: int = DEFAULT_ALIGNMENT, + allocator := context.allocator, + loc := #caller_location, +) -> ([]byte, Allocator_Error) { return runtime.mem_resize(raw_data(old_data), len(old_data), new_size, alignment, allocator, loc) } @@ -115,7 +156,11 @@ query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: A } @(require_results) -query_info :: proc(pointer: rawptr, allocator: Allocator, loc := #caller_location) -> (props: Allocator_Query_Info) { +query_info :: proc( + pointer: rawptr, + allocator: Allocator, + loc := #caller_location, +) -> (props: Allocator_Query_Info) { props.pointer = pointer if allocator.procedure != nil { allocator.procedure(allocator.data, .Query_Info, 0, 0, &props, 0, loc) @@ -123,25 +168,44 @@ query_info :: proc(pointer: rawptr, allocator: Allocator, loc := #caller_locatio return } - - -delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { +delete_string :: proc( + str: string, + allocator := context.allocator, + loc := #caller_location, +) -> Allocator_Error { return runtime.delete_string(str, allocator, loc) } -delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { + +delete_cstring :: proc( + str: cstring, + allocator := context.allocator, + loc := #caller_location, +) -> Allocator_Error { return runtime.delete_cstring(str, allocator, loc) } -delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) -> Allocator_Error { + +delete_dynamic_array :: proc( + array: $T/[dynamic]$E, + loc := #caller_location, +) -> Allocator_Error { return runtime.delete_dynamic_array(array, loc) } -delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { + +delete_slice :: proc( + array: $T/[]$E, + allocator := context.allocator, + loc := #caller_location, +) -> Allocator_Error { return runtime.delete_slice(array, allocator, loc) } -delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error { + +delete_map :: proc( + m: $T/map[$K]$V, + loc := #caller_location, +) -> Allocator_Error { return runtime.delete_map(m, loc) } - delete :: proc{ delete_string, delete_cstring, @@ -150,46 +214,102 @@ delete :: proc{ delete_map, } - @(require_results) -new :: proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> (^T, Allocator_Error) { +new :: proc( + $T: typeid, + allocator := context.allocator, + loc := #caller_location, +) -> (^T, Allocator_Error) { return new_aligned(T, align_of(T), allocator, loc) } + @(require_results) -new_aligned :: proc($T: typeid, alignment: int, allocator := context.allocator, loc := #caller_location) -> (t: ^T, err: Allocator_Error) { +new_aligned :: proc( + $T: typeid, + alignment: int, + allocator := context.allocator, + loc := #caller_location, +) -> (t: ^T, err: Allocator_Error) { return runtime.new_aligned(T, alignment, allocator, loc) } + @(require_results) -new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_location) -> (t: ^T, err: Allocator_Error) { +new_clone :: proc( + data: $T, + allocator := context.allocator, + loc := #caller_location, +) -> (t: ^T, err: Allocator_Error) { return runtime.new_clone(data, allocator, loc) } @(require_results) -make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (slice: T, err: Allocator_Error) { +make_aligned :: proc( + $T: typeid/[]$E, + #any_int len: int, + alignment: int, + allocator := context.allocator, + loc := #caller_location, +) -> (slice: T, err: Allocator_Error) { return runtime.make_aligned(T, len, alignment, allocator, loc) } + @(require_results) -make_slice :: proc($T: typeid/[]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) { +make_slice :: proc( + $T: typeid/[]$E, + #any_int len: int, + allocator := context.allocator, + loc := #caller_location, +) -> (T, Allocator_Error) { return runtime.make_slice(T, len, allocator, loc) } + @(require_results) -make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) { +make_dynamic_array :: proc( + $T: typeid/[dynamic]$E, + allocator := context.allocator, + loc := #caller_location, +) -> (T, Allocator_Error) { return runtime.make_dynamic_array(T, allocator, loc) } + @(require_results) -make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) { +make_dynamic_array_len :: proc( + $T: typeid/[dynamic]$E, + #any_int len: int, + allocator := context.allocator, + loc := #caller_location, +) -> (T, Allocator_Error) { return runtime.make_dynamic_array_len_cap(T, len, len, allocator, loc) } + @(require_results) -make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) { +make_dynamic_array_len_cap :: proc( + $T: typeid/[dynamic]$E, + #any_int len: int, + #any_int cap: int, + allocator := context.allocator, + loc := #caller_location, +) -> (array: T, err: Allocator_Error) { return runtime.make_dynamic_array_len_cap(T, len, cap, allocator, loc) } + @(require_results) -make_map :: proc($T: typeid/map[$K]$E, #any_int cap: int = 1< (m: T, err: Allocator_Error) { +make_map :: proc( + $T: typeid/map[$K]$E, + #any_int cap: int = 1< (m: T, err: Allocator_Error) { return runtime.make_map(T, cap, allocator, loc) } + @(require_results) -make_multi_pointer :: proc($T: typeid/[^]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (mp: T, err: Allocator_Error) { +make_multi_pointer :: proc( + $T: typeid/[^]$E, + #any_int len: int, + allocator := context.allocator, + loc := #caller_location +) -> (mp: T, err: Allocator_Error) { return runtime.make_multi_pointer(T, len, allocator, loc) } @@ -202,26 +322,58 @@ make :: proc{ make_multi_pointer, } - @(require_results) -default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment: int, allocator := context.allocator, loc := #caller_location) -> (res: rawptr, err: Allocator_Error) { +default_resize_align :: proc( + old_memory: rawptr, + old_size: int, + new_size: int, + alignment: int, + allocator := context.allocator, + loc := #caller_location, +) -> (res: rawptr, err: Allocator_Error) { data: []byte - data, err = default_resize_bytes_align(([^]byte)(old_memory)[:old_size], new_size, alignment, allocator, loc) + data, err = default_resize_bytes_align( + ([^]byte) (old_memory)[:old_size], + new_size, + alignment, + allocator, + loc, + ) res = raw_data(data) return } @(require_results) -default_resize_bytes_align_non_zeroed :: proc(old_data: []byte, new_size, alignment: int, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) { +default_resize_bytes_align_non_zeroed :: proc( + old_data: []byte, + new_size: int, + alignment: int, + allocator := context.allocator, + loc := #caller_location, +) -> ([]byte, Allocator_Error) { return _default_resize_bytes_align(old_data, new_size, alignment, false, allocator, loc) } + @(require_results) -default_resize_bytes_align :: proc(old_data: []byte, new_size, alignment: int, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) { +default_resize_bytes_align :: proc( + old_data: []byte, + new_size: int, + alignment: int, + allocator := context.allocator, + loc := #caller_location, +) -> ([]byte, Allocator_Error) { return _default_resize_bytes_align(old_data, new_size, alignment, true, allocator, loc) } @(require_results) -_default_resize_bytes_align :: #force_inline proc(old_data: []byte, new_size, alignment: int, should_zero: bool, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) { +_default_resize_bytes_align :: #force_inline proc( + old_data: []byte, + new_size: int, + alignment: int, + should_zero: bool, + allocator := context.allocator, + loc := #caller_location, +) -> ([]byte, Allocator_Error) { old_memory := raw_data(old_data) old_size := len(old_data) if old_memory == nil { diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index a5b93ad05..7bc1a6d77 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -3,9 +3,14 @@ package mem import "base:intrinsics" import "base:runtime" -nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, - size, alignment: int, - old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) { +nil_allocator_proc :: proc( + allocator_data: rawptr, + mode: Allocator_Mode, + size, alignment: int, + old_memory: rawptr, + old_size: int, + loc := #caller_location, +) -> ([]byte, Allocator_Error) { return nil, nil } @@ -16,8 +21,6 @@ nil_allocator :: proc() -> Allocator { } } -// Custom allocators - Arena :: struct { data: []byte, offset: int, @@ -30,7 +33,6 @@ Arena_Temp_Memory :: struct { prev_offset: int, } - arena_init :: proc(a: ^Arena, data: []byte) { a.data = data a.offset = 0 @@ -54,9 +56,15 @@ arena_allocator :: proc(arena: ^Arena) -> Allocator { } } -arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, - size, alignment: int, - old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, Allocator_Error) { +arena_allocator_proc :: proc( + allocator_data: rawptr, + mode: Allocator_Mode, + size: int, + alignment: int, + old_memory: rawptr, + old_size: int, + location := #caller_location, +) -> ([]byte, Allocator_Error) { arena := cast(^Arena)allocator_data switch mode { @@ -120,8 +128,6 @@ end_arena_temp_memory :: proc(tmp: Arena_Temp_Memory) { tmp.arena.temp_count -= 1 } - - Scratch_Allocator :: struct { data: []byte, curr_offset: int, @@ -151,9 +157,14 @@ scratch_allocator_destroy :: proc(s: ^Scratch_Allocator) { s^ = {} } -scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, - size, alignment: int, - old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) { +scratch_allocator_proc :: proc( + allocator_data: rawptr, + mode: Allocator_Mode, + size, alignment: int, + old_memory: rawptr, + old_size: int, + loc := #caller_location, +) -> ([]byte, Allocator_Error) { s := (^Scratch_Allocator)(allocator_data) @@ -299,10 +310,6 @@ scratch_allocator :: proc(allocator: ^Scratch_Allocator) -> Allocator { } } - - - - Stack_Allocation_Header :: struct { prev_offset: int, padding: int, @@ -339,34 +346,44 @@ stack_allocator :: proc(stack: ^Stack) -> Allocator { } } - -stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, - size, alignment: int, - old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, Allocator_Error) { +stack_allocator_proc :: proc( + allocator_data: rawptr, + mode: Allocator_Mode, + size: int, + alignment: int, + old_memory: rawptr, + old_size: int, + location := #caller_location, +) -> ([]byte, Allocator_Error) { s := cast(^Stack)allocator_data if s.data == nil { return nil, .Invalid_Argument } - raw_alloc :: proc(s: ^Stack, size, alignment: int, zero_memory: bool) -> ([]byte, Allocator_Error) { + raw_alloc :: proc( + s: ^Stack, + size: int, + alignment: int, + zero_memory: bool, + ) -> ([]byte, Allocator_Error) { curr_addr := uintptr(raw_data(s.data)) + uintptr(s.curr_offset) - padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Stack_Allocation_Header)) + padding := calc_padding_with_header( + curr_addr, + uintptr(alignment), + size_of(Stack_Allocation_Header), + ) if s.curr_offset + padding + size > len(s.data) { return nil, .Out_Of_Memory } s.prev_offset = s.curr_offset s.curr_offset += padding - next_addr := curr_addr + uintptr(padding) header := (^Stack_Allocation_Header)(next_addr - size_of(Stack_Allocation_Header)) header.padding = padding header.prev_offset = s.prev_offset - s.curr_offset += size - s.peak_used = max(s.peak_used, s.curr_offset) - if zero_memory { zero(rawptr(next_addr), size) } @@ -467,12 +484,6 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, return nil, nil } - - - - - - Small_Stack_Allocation_Header :: struct { padding: u8, } @@ -505,9 +516,14 @@ small_stack_allocator :: proc(stack: ^Small_Stack) -> Allocator { } } -small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, - size, alignment: int, - old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, Allocator_Error) { +small_stack_allocator_proc :: proc( + allocator_data: rawptr, + mode: Allocator_Mode, + size, alignment: int, + old_memory: rawptr, + old_size: int, + location := #caller_location, +) -> ([]byte, Allocator_Error) { s := cast(^Small_Stack)allocator_data if s.data == nil { @@ -612,10 +628,6 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, return nil, nil } - - - - Dynamic_Pool :: struct { block_size: int, out_band_size: int, @@ -632,15 +644,18 @@ Dynamic_Pool :: struct { block_allocator: Allocator, } - DYNAMIC_POOL_BLOCK_SIZE_DEFAULT :: 65536 DYNAMIC_POOL_OUT_OF_BAND_SIZE_DEFAULT :: 6554 - - -dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, - size, alignment: int, - old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) { +dynamic_pool_allocator_proc :: proc( + allocator_data: rawptr, + mode: Allocator_Mode, + size: int, + alignment: int, + old_memory: rawptr, + old_size: int, + loc := #caller_location, +) -> ([]byte, Allocator_Error) { pool := (^Dynamic_Pool)(allocator_data) switch mode { @@ -689,19 +704,21 @@ dynamic_pool_allocator :: proc(pool: ^Dynamic_Pool) -> Allocator { } } -dynamic_pool_init :: proc(pool: ^Dynamic_Pool, - block_allocator := context.allocator, - array_allocator := context.allocator, - block_size := DYNAMIC_POOL_BLOCK_SIZE_DEFAULT, - out_band_size := DYNAMIC_POOL_OUT_OF_BAND_SIZE_DEFAULT, - alignment := 8) { - pool.block_size = block_size - pool.out_band_size = out_band_size - pool.alignment = alignment +dynamic_pool_init :: proc( + pool: ^Dynamic_Pool, + block_allocator := context.allocator, + array_allocator := context.allocator, + block_size := DYNAMIC_POOL_BLOCK_SIZE_DEFAULT, + out_band_size := DYNAMIC_POOL_OUT_OF_BAND_SIZE_DEFAULT, + alignment := 8, +) { + pool.block_size = block_size + pool.out_band_size = out_band_size + pool.alignment = alignment pool.block_allocator = block_allocator pool.out_band_allocations.allocator = array_allocator - pool. unused_blocks.allocator = array_allocator - pool. used_blocks.allocator = array_allocator + pool.unused_blocks.allocator = array_allocator + pool.used_blocks.allocator = array_allocator } dynamic_pool_destroy :: proc(pool: ^Dynamic_Pool) { @@ -709,11 +726,9 @@ dynamic_pool_destroy :: proc(pool: ^Dynamic_Pool) { delete(pool.unused_blocks) delete(pool.used_blocks) delete(pool.out_band_allocations) - zero(pool, size_of(pool^)) } - @(require_results) dynamic_pool_alloc :: proc(pool: ^Dynamic_Pool, bytes: int) -> (rawptr, Allocator_Error) { data, err := dynamic_pool_alloc_bytes(pool, bytes) @@ -736,9 +751,14 @@ dynamic_pool_alloc_bytes :: proc(p: ^Dynamic_Pool, bytes: int) -> ([]byte, Alloc new_block = pop(&p.unused_blocks) } else { data: []byte - data, err = p.block_allocator.procedure(p.block_allocator.data, Allocator_Mode.Alloc, - p.block_size, p.alignment, - nil, 0) + data, err = p.block_allocator.procedure( + p.block_allocator.data, + Allocator_Mode.Alloc, + p.block_size, + p.alignment, + nil, + 0, + ) new_block = raw_data(data) } @@ -808,10 +828,14 @@ dynamic_pool_free_all :: proc(p: ^Dynamic_Pool) { clear(&p.unused_blocks) } - -panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, - size, alignment: int, - old_memory: rawptr, old_size: int,loc := #caller_location) -> ([]byte, Allocator_Error) { +panic_allocator_proc :: proc( + allocator_data: rawptr, + mode: Allocator_Mode, + size, alignment: int, + old_memory: rawptr, + old_size: int, + loc := #caller_location, +) -> ([]byte, Allocator_Error) { switch mode { case .Alloc: @@ -859,11 +883,6 @@ panic_allocator :: proc() -> Allocator { } } - - - - - Buddy_Block :: struct #align(align_of(uint)) { size: uint, is_free: bool, @@ -929,7 +948,6 @@ buddy_block_coalescence :: proc(head, tail: ^Buddy_Block) { } } - @(require_results) buddy_block_find_best :: proc(head, tail: ^Buddy_Block, size: uint) -> ^Buddy_Block { assert(size != 0) @@ -998,7 +1016,6 @@ buddy_block_find_best :: proc(head, tail: ^Buddy_Block, size: uint) -> ^Buddy_Bl return nil } - Buddy_Allocator :: struct { head: ^Buddy_Block, tail: ^Buddy_Block, @@ -1089,9 +1106,13 @@ buddy_allocator_free :: proc(b: ^Buddy_Allocator, ptr: rawptr) -> Allocator_Erro return nil } -buddy_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, - size, alignment: int, - old_memory: rawptr, old_size: int,loc := #caller_location) -> ([]byte, Allocator_Error) { +buddy_allocator_proc :: proc( + allocator_data: rawptr, mode: Allocator_Mode, + size, alignment: int, + old_memory: rawptr, + old_size: int, + loc := #caller_location, +) -> ([]byte, Allocator_Error) { b := (^Buddy_Allocator)(allocator_data) diff --git a/core/mem/mem.odin b/core/mem/mem.odin index d423cc1eb..9e47c9602 100644 --- a/core/mem/mem.odin +++ b/core/mem/mem.odin @@ -14,10 +14,12 @@ Exabyte :: runtime.Exabyte set :: proc "contextless" (data: rawptr, value: byte, len: int) -> rawptr { return runtime.memset(data, i32(value), len) } + zero :: proc "contextless" (data: rawptr, len: int) -> rawptr { intrinsics.mem_zero(data, len) return data } + zero_explicit :: proc "contextless" (data: rawptr, len: int) -> rawptr { // This routine tries to avoid the compiler optimizing away the call, // so that it is always executed. It is intended to provided @@ -27,20 +29,22 @@ zero_explicit :: proc "contextless" (data: rawptr, len: int) -> rawptr { intrinsics.atomic_thread_fence(.Seq_Cst) // Prevent reordering return data } + zero_item :: proc "contextless" (item: $P/^$T) -> P { intrinsics.mem_zero(item, size_of(T)) return item } + zero_slice :: proc "contextless" (data: $T/[]$E) -> T { zero(raw_data(data), size_of(E)*len(data)) return data } - copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr { intrinsics.mem_copy(dst, src, len) return dst } + copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr { intrinsics.mem_copy_non_overlapping(dst, src, len) return dst @@ -120,6 +124,7 @@ compare_ptrs :: proc "contextless" (a, b: rawptr, n: int) -> int { } ptr_offset :: intrinsics.ptr_offset + ptr_sub :: intrinsics.ptr_sub @(require_results) @@ -211,6 +216,7 @@ align_forward_uintptr :: proc(ptr, align: uintptr) -> uintptr { align_forward_int :: proc(ptr, align: int) -> int { return int(align_forward_uintptr(uintptr(ptr), uintptr(align))) } + @(require_results) align_forward_uint :: proc(ptr, align: uint) -> uint { return uint(align_forward_uintptr(uintptr(ptr), uintptr(align))) @@ -230,6 +236,7 @@ align_backward_uintptr :: proc(ptr, align: uintptr) -> uintptr { align_backward_int :: proc(ptr, align: int) -> int { return int(align_backward_uintptr(uintptr(ptr), uintptr(align))) } + @(require_results) align_backward_uint :: proc(ptr, align: uint) -> uint { return uint(align_backward_uintptr(uintptr(ptr), uintptr(align))) @@ -247,7 +254,6 @@ reinterpret_copy :: proc "contextless" ($T: typeid, ptr: rawptr) -> (value: T) { return } - Fixed_Byte_Buffer :: distinct [dynamic]byte @(require_results) @@ -264,8 +270,6 @@ make_fixed_byte_buffer :: proc "contextless" (backing: []byte) -> Fixed_Byte_Buf return transmute(Fixed_Byte_Buffer)d } - - @(require_results) align_formula :: proc "contextless" (size, align: int) -> int { result := size + align-1 @@ -276,12 +280,10 @@ align_formula :: proc "contextless" (size, align: int) -> int { calc_padding_with_header :: proc "contextless" (ptr: uintptr, align: uintptr, header_size: int) -> int { p, a := ptr, align modulo := p & (a-1) - padding := uintptr(0) if modulo != 0 { padding = a - modulo } - needed_space := uintptr(header_size) if padding < needed_space { needed_space -= padding @@ -296,8 +298,6 @@ calc_padding_with_header :: proc "contextless" (ptr: uintptr, align: uintptr, he return int(padding) } - - @(require_results, deprecated="prefer 'slice.clone'") clone_slice :: proc(slice: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> (new_slice: T) { new_slice, _ = make(T, len(slice), allocator, loc) diff --git a/core/mem/mutex_allocator.odin b/core/mem/mutex_allocator.odin index 591703eab..1cccc7dac 100644 --- a/core/mem/mutex_allocator.odin +++ b/core/mem/mutex_allocator.odin @@ -13,7 +13,6 @@ mutex_allocator_init :: proc(m: ^Mutex_Allocator, backing_allocator: Allocator) m.mutex = {} } - @(require_results) mutex_allocator :: proc(m: ^Mutex_Allocator) -> Allocator { return Allocator{ @@ -22,11 +21,16 @@ mutex_allocator :: proc(m: ^Mutex_Allocator) -> Allocator { } } -mutex_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, - size, alignment: int, - old_memory: rawptr, old_size: int, loc := #caller_location) -> (result: []byte, err: Allocator_Error) { +mutex_allocator_proc :: proc( + allocator_data: rawptr, + mode: Allocator_Mode, + size: int, + alignment: int, + old_memory: rawptr, + old_size: int, + loc := #caller_location, +) -> (result: []byte, err: Allocator_Error) { m := (^Mutex_Allocator)(allocator_data) - sync.mutex_guard(&m.mutex) return m.backing.procedure(m.backing.data, mode, size, alignment, old_memory, old_size, loc) } diff --git a/core/mem/raw.odin b/core/mem/raw.odin index f56206957..7fda3229d 100644 --- a/core/mem/raw.odin +++ b/core/mem/raw.odin @@ -3,22 +3,36 @@ package mem import "base:builtin" import "base:runtime" -Raw_Any :: runtime.Raw_Any -Raw_String :: runtime.Raw_String -Raw_Cstring :: runtime.Raw_Cstring -Raw_Slice :: runtime.Raw_Slice -Raw_Dynamic_Array :: runtime.Raw_Dynamic_Array -Raw_Map :: runtime.Raw_Map -Raw_Soa_Pointer :: runtime.Raw_Soa_Pointer +Raw_Any :: runtime.Raw_Any + +Raw_String :: runtime.Raw_String + +Raw_Cstring :: runtime.Raw_Cstring + +Raw_Slice :: runtime.Raw_Slice + +Raw_Dynamic_Array :: runtime.Raw_Dynamic_Array + +Raw_Map :: runtime.Raw_Map + +Raw_Soa_Pointer :: runtime.Raw_Soa_Pointer + +Raw_Complex32 :: runtime.Raw_Complex32 + +Raw_Complex64 :: runtime.Raw_Complex64 + +Raw_Complex128 :: runtime.Raw_Complex128 + +Raw_Quaternion64 :: runtime.Raw_Quaternion64 -Raw_Complex32 :: runtime.Raw_Complex32 -Raw_Complex64 :: runtime.Raw_Complex64 -Raw_Complex128 :: runtime.Raw_Complex128 -Raw_Quaternion64 :: runtime.Raw_Quaternion64 Raw_Quaternion128 :: runtime.Raw_Quaternion128 + Raw_Quaternion256 :: runtime.Raw_Quaternion256 -Raw_Quaternion64_Vector_Scalar :: runtime.Raw_Quaternion64_Vector_Scalar + +Raw_Quaternion64_Vector_Scalar :: runtime.Raw_Quaternion64_Vector_Scalar + Raw_Quaternion128_Vector_Scalar :: runtime.Raw_Quaternion128_Vector_Scalar + Raw_Quaternion256_Vector_Scalar :: runtime.Raw_Quaternion256_Vector_Scalar make_any :: proc "contextless" (data: rawptr, id: typeid) -> any { diff --git a/core/mem/rollback_stack_allocator.odin b/core/mem/rollback_stack_allocator.odin index f5e428d87..761435552 100644 --- a/core/mem/rollback_stack_allocator.odin +++ b/core/mem/rollback_stack_allocator.odin @@ -1,45 +1,47 @@ package mem -// The Rollback Stack Allocator was designed for the test runner to be fast, -// able to grow, and respect the Tracking Allocator's requirement for -// individual frees. It is not overly concerned with fragmentation, however. -// -// It has support for expansion when configured with a block allocator and -// limited support for out-of-order frees. -// -// Allocation has constant-time best and usual case performance. -// At worst, it is linear according to the number of memory blocks. -// -// Allocation follows a first-fit strategy when there are multiple memory -// blocks. -// -// Freeing has constant-time best and usual case performance. -// At worst, it is linear according to the number of memory blocks and number -// of freed items preceding the last item in a block. -// -// Resizing has constant-time performance, if it's the last item in a block, or -// the new size is smaller. Naturally, this becomes linear-time if there are -// multiple blocks to search for the pointer's owning block. Otherwise, the -// allocator defaults to a combined alloc & free operation internally. -// -// Out-of-order freeing is accomplished by collapsing a run of freed items -// from the last allocation backwards. -// -// Each allocation has an overhead of 8 bytes and any extra bytes to satisfy -// the requested alignment. +/* +The Rollback Stack Allocator was designed for the test runner to be fast, +able to grow, and respect the Tracking Allocator's requirement for +individual frees. It is not overly concerned with fragmentation, however. +It has support for expansion when configured with a block allocator and +limited support for out-of-order frees. + +Allocation has constant-time best and usual case performance. +At worst, it is linear according to the number of memory blocks. + +Allocation follows a first-fit strategy when there are multiple memory +blocks. + +Freeing has constant-time best and usual case performance. +At worst, it is linear according to the number of memory blocks and number +of freed items preceding the last item in a block. + +Resizing has constant-time performance, if it's the last item in a block, or +the new size is smaller. Naturally, this becomes linear-time if there are +multiple blocks to search for the pointer's owning block. Otherwise, the +allocator defaults to a combined alloc & free operation internally. + +Out-of-order freeing is accomplished by collapsing a run of freed items +from the last allocation backwards. + +Each allocation has an overhead of 8 bytes and any extra bytes to satisfy +the requested alignment. +*/ import "base:runtime" ROLLBACK_STACK_DEFAULT_BLOCK_SIZE :: 4 * Megabyte -// This limitation is due to the size of `prev_ptr`, but it is only for the -// head block; any allocation in excess of the allocator's `block_size` is -// valid, so long as the block allocator can handle it. -// -// This is because allocations over the block size are not split up if the item -// within is freed; they are immediately returned to the block allocator. -ROLLBACK_STACK_MAX_HEAD_BLOCK_SIZE :: 2 * Gigabyte +/* +This limitation is due to the size of `prev_ptr`, but it is only for the +head block; any allocation in excess of the allocator's `block_size` is +valid, so long as the block allocator can handle it. +This is because allocations over the block size are not split up if the item +within is freed; they are immediately returned to the block allocator. +*/ +ROLLBACK_STACK_MAX_HEAD_BLOCK_SIZE :: 2 * Gigabyte Rollback_Stack_Header :: bit_field u64 { prev_offset: uintptr | 32, @@ -60,7 +62,6 @@ Rollback_Stack :: struct { block_allocator: Allocator, } - @(private="file", require_results) rb_ptr_in_bounds :: proc(block: ^Rollback_Stack_Block, ptr: rawptr) -> bool { start := raw_data(block.buffer) @@ -294,9 +295,13 @@ rollback_stack_allocator :: proc(stack: ^Rollback_Stack) -> Allocator { } @(require_results) -rollback_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, - size, alignment: int, - old_memory: rawptr, old_size: int, location := #caller_location, +rollback_stack_allocator_proc :: proc( + allocator_data: rawptr, + mode: Allocator_Mode, + size, alignment: int, + old_memory: rawptr, + old_size: int, + location := #caller_location, ) -> (result: []byte, err: Allocator_Error) { stack := cast(^Rollback_Stack)allocator_data diff --git a/core/mem/tracking_allocator.odin b/core/mem/tracking_allocator.odin index 1b57e5fb4..356180be1 100644 --- a/core/mem/tracking_allocator.odin +++ b/core/mem/tracking_allocator.odin @@ -12,22 +12,23 @@ Tracking_Allocator_Entry :: struct { err: Allocator_Error, location: runtime.Source_Code_Location, } + Tracking_Allocator_Bad_Free_Entry :: struct { memory: rawptr, location: runtime.Source_Code_Location, } -Tracking_Allocator :: struct { - backing: Allocator, - allocation_map: map[rawptr]Tracking_Allocator_Entry, - bad_free_array: [dynamic]Tracking_Allocator_Bad_Free_Entry, - mutex: sync.Mutex, - clear_on_free_all: bool, - total_memory_allocated: i64, - total_allocation_count: i64, - total_memory_freed: i64, - total_free_count: i64, - peak_memory_allocated: i64, +Tracking_Allocator :: struct { + backing: Allocator, + allocation_map: map[rawptr]Tracking_Allocator_Entry, + bad_free_array: [dynamic]Tracking_Allocator_Bad_Free_Entry, + mutex: sync.Mutex, + clear_on_free_all: bool, + total_memory_allocated: i64, + total_allocation_count: i64, + total_memory_freed: i64, + total_free_count: i64, + peak_memory_allocated: i64, current_memory_allocated: i64, } @@ -35,7 +36,6 @@ tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Alloc t.backing = backing_allocator t.allocation_map.allocator = internals_allocator t.bad_free_array.allocator = internals_allocator - if .Free_All in query_features(t.backing) { t.clear_on_free_all = true } @@ -46,7 +46,6 @@ tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) { delete(t.bad_free_array) } - // Clear only the current allocation data while keeping the totals intact. tracking_allocator_clear :: proc(t: ^Tracking_Allocator) { sync.mutex_lock(&t.mutex) @@ -78,9 +77,14 @@ tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator { } } -tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, - size, alignment: int, - old_memory: rawptr, old_size: int, loc := #caller_location) -> (result: []byte, err: Allocator_Error) { +tracking_allocator_proc :: proc( + allocator_data: rawptr, + mode: Allocator_Mode, + size, alignment: int, + old_memory: rawptr, + old_size: int, + loc := #caller_location, +) -> (result: []byte, err: Allocator_Error) { track_alloc :: proc(data: ^Tracking_Allocator, entry: ^Tracking_Allocator_Entry) { data.total_memory_allocated += i64(entry.size) data.total_allocation_count += 1 From da6213196dd4c64cf53d163dd7392531e26f3ad2 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sat, 7 Sep 2024 09:42:04 +1100 Subject: [PATCH 02/72] [mem]: API for using arena directly --- core/mem/allocators.odin | 71 +++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 33 deletions(-) diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index 7bc1a6d77..52a05958f 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -3,6 +3,13 @@ package mem import "base:intrinsics" import "base:runtime" +nil_allocator :: proc() -> Allocator { + return Allocator{ + procedure = nil_allocator_proc, + data = nil, + } +} + nil_allocator_proc :: proc( allocator_data: rawptr, mode: Allocator_Mode, @@ -14,13 +21,6 @@ nil_allocator_proc :: proc( return nil, nil } -nil_allocator :: proc() -> Allocator { - return Allocator{ - procedure = nil_allocator_proc, - data = nil, - } -} - Arena :: struct { data: []byte, offset: int, @@ -56,6 +56,30 @@ arena_allocator :: proc(arena: ^Arena) -> Allocator { } } +arena_alloc_non_zeroed :: proc(a: ^Arena, size: int, alignment := DEFAULT_ALIGNMENT) -> ([]byte, Allocator_Error) { + #no_bounds_check end := &a.data[a.offset] + ptr := align_forward(end, uintptr(alignment)) + total_size := size + ptr_sub((^byte)(ptr), (^byte)(end)) + if a.offset + total_size > len(a.data) { + return nil, .Out_Of_Memory + } + a.offset += total_size + a.peak_used = max(a.peak_used, a.offset) + return byte_slice(ptr, size), nil +} + +arena_alloc :: proc(a: ^Arena, size: int, alignment := DEFAULT_ALIGNMENT) -> ([]byte, Allocator_Error) { + bytes, err := arena_alloc_non_zeroed(a, size, alignment) + if bytes != nil { + zero(raw_data(bytes), size) + } + return bytes, err +} + +arena_free_all :: proc(a: ^Arena) { + a.offset = 0 +} + arena_allocator_proc :: proc( allocator_data: rawptr, mode: Allocator_Mode, @@ -66,49 +90,28 @@ arena_allocator_proc :: proc( location := #caller_location, ) -> ([]byte, Allocator_Error) { arena := cast(^Arena)allocator_data - switch mode { - case .Alloc, .Alloc_Non_Zeroed: - #no_bounds_check end := &arena.data[arena.offset] - - ptr := align_forward(end, uintptr(alignment)) - - total_size := size + ptr_sub((^byte)(ptr), (^byte)(end)) - - if arena.offset + total_size > len(arena.data) { - return nil, .Out_Of_Memory - } - - arena.offset += total_size - arena.peak_used = max(arena.peak_used, arena.offset) - if mode != .Alloc_Non_Zeroed { - zero(ptr, size) - } - return byte_slice(ptr, size), nil - + case .Alloc: + return arena_alloc(arena, size, alignment) + case .Alloc_Non_Zeroed: + return arena_alloc_non_zeroed(arena, size, alignment) case .Free: return nil, .Mode_Not_Implemented - case .Free_All: - arena.offset = 0 - + arena_free_all(arena) case .Resize: return default_resize_bytes_align(byte_slice(old_memory, old_size), size, alignment, arena_allocator(arena)) - case .Resize_Non_Zeroed: return default_resize_bytes_align_non_zeroed(byte_slice(old_memory, old_size), size, alignment, arena_allocator(arena)) - case .Query_Features: set := (^Allocator_Mode_Set)(old_memory) if set != nil { set^ = {.Alloc, .Alloc_Non_Zeroed, .Free_All, .Resize, .Resize_Non_Zeroed, .Query_Features} } return nil, nil - case .Query_Info: return nil, .Mode_Not_Implemented } - return nil, nil } @@ -128,6 +131,8 @@ end_arena_temp_memory :: proc(tmp: Arena_Temp_Memory) { tmp.arena.temp_count -= 1 } + + Scratch_Allocator :: struct { data: []byte, curr_offset: int, From e5106e48a809a313be35c3554e9dc310c117eefe Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sat, 7 Sep 2024 10:09:05 +1100 Subject: [PATCH 03/72] [mem]: API for using scratch allocator directly --- core/mem/allocators.odin | 285 ++++++++++++++++++++++----------------- 1 file changed, 165 insertions(+), 120 deletions(-) diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index 52a05958f..2be4d5b61 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -56,6 +56,14 @@ arena_allocator :: proc(arena: ^Arena) -> Allocator { } } +arena_alloc :: proc(a: ^Arena, size: int, alignment := DEFAULT_ALIGNMENT) -> ([]byte, Allocator_Error) { + bytes, err := arena_alloc_non_zeroed(a, size, alignment) + if bytes != nil { + zero_slice(bytes) + } + return bytes, err +} + arena_alloc_non_zeroed :: proc(a: ^Arena, size: int, alignment := DEFAULT_ALIGNMENT) -> ([]byte, Allocator_Error) { #no_bounds_check end := &a.data[a.offset] ptr := align_forward(end, uintptr(alignment)) @@ -68,14 +76,6 @@ arena_alloc_non_zeroed :: proc(a: ^Arena, size: int, alignment := DEFAULT_ALIGNM return byte_slice(ptr, size), nil } -arena_alloc :: proc(a: ^Arena, size: int, alignment := DEFAULT_ALIGNMENT) -> ([]byte, Allocator_Error) { - bytes, err := arena_alloc_non_zeroed(a, size, alignment) - if bytes != nil { - zero(raw_data(bytes), size) - } - return bytes, err -} - arena_free_all :: proc(a: ^Arena) { a.offset = 0 } @@ -162,6 +162,153 @@ scratch_allocator_destroy :: proc(s: ^Scratch_Allocator) { s^ = {} } +scratch_allocator_alloc :: proc( + s: ^Scratch_Allocator, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location, +) -> ([]byte, Allocator_Error) { + bytes, err := scratch_allocator_alloc_non_zeroed(s, size, alignment, loc) + if bytes != nil { + zero_slice(bytes) + } + return bytes, err +} + +scratch_allocator_alloc_non_zeroed :: proc( + s: ^Scratch_Allocator, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location, +) -> ([]byte, Allocator_Error) { + if s.data == nil { + DEFAULT_BACKING_SIZE :: 4 * Megabyte + if !(context.allocator.procedure != scratch_allocator_proc && context.allocator.data != s) { + panic("cyclic initialization of the scratch allocator with itself", loc) + } + scratch_allocator_init(s, DEFAULT_BACKING_SIZE) + } + size := size + size = align_forward_int(size, alignment) + switch { + case s.curr_offset+size <= len(s.data): + start := uintptr(raw_data(s.data)) + ptr := start + uintptr(s.curr_offset) + ptr = align_forward_uintptr(ptr, uintptr(alignment)) + s.prev_allocation = rawptr(ptr) + offset := int(ptr - start) + s.curr_offset = offset + size + return byte_slice(rawptr(ptr), size), nil + case size <= len(s.data): + start := uintptr(raw_data(s.data)) + ptr := align_forward_uintptr(start, uintptr(alignment)) + s.prev_allocation = rawptr(ptr) + offset := int(ptr - start) + s.curr_offset = offset + size + return byte_slice(rawptr(ptr), size), nil + } + a := s.backup_allocator + if a.procedure == nil { + a = context.allocator + s.backup_allocator = a + } + ptr, err := alloc_bytes_non_zeroed(size, alignment, a, loc) + if err != nil { + return ptr, err + } + if s.leaked_allocations == nil { + s.leaked_allocations, err = make([dynamic][]byte, a) + } + append(&s.leaked_allocations, ptr) + if logger := context.logger; logger.lowest_level <= .Warning { + if logger.procedure != nil { + logger.procedure(logger.data, .Warning, "mem.Scratch_Allocator resorted to backup_allocator" , logger.options, loc) + } + } + return ptr, err +} + +scratch_allocator_free :: proc(s: ^Scratch_Allocator, ptr: rawptr, loc := #caller_location) -> Allocator_Error { + if s.data == nil { + panic("Free on an uninitialized scratch allocator", loc) + } + if ptr == nil { + return nil + } + start := uintptr(raw_data(s.data)) + end := start + uintptr(len(s.data)) + old_ptr := uintptr(ptr) + if s.prev_allocation == ptr { + s.curr_offset = int(uintptr(s.prev_allocation) - start) + s.prev_allocation = nil + return nil + } + if start <= old_ptr && old_ptr < end { + // NOTE(bill): Cannot free this pointer but it is valid + return nil + } + if len(s.leaked_allocations) != 0 { + for data, i in s.leaked_allocations { + ptr := raw_data(data) + if ptr == ptr { + free_bytes(data, s.backup_allocator, loc) + ordered_remove(&s.leaked_allocations, i, loc) + return nil + } + } + } + return .Invalid_Pointer +} + +scratch_allocator_free_all :: proc(s: ^Scratch_Allocator, loc := #caller_location) { + s.curr_offset = 0 + s.prev_allocation = nil + for ptr in s.leaked_allocations { + free_bytes(ptr, s.backup_allocator, loc) + } + clear(&s.leaked_allocations) +} + +scratch_allocator_resize_non_zeroed :: proc( + s: ^Scratch_Allocator, + old_memory: rawptr, + old_size: int, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location +) -> ([]byte, Allocator_Error) { + begin := uintptr(raw_data(s.data)) + end := begin + uintptr(len(s.data)) + old_ptr := uintptr(old_memory) + if begin <= old_ptr && old_ptr < end && old_ptr+uintptr(size) < end { + s.curr_offset = int(old_ptr-begin)+size + return byte_slice(old_memory, size), nil + } + data, err := scratch_allocator_alloc_non_zeroed(s, size, alignment, loc) + if err != nil { + return data, err + } + // TODO(flysand): OOB access on size < old_size. + runtime.copy(data, byte_slice(old_memory, old_size)) + err = scratch_allocator_free(s, old_memory, loc) + return data, err +} + +scratch_allocator_resize :: proc( + s: ^Scratch_Allocator, + old_memory: rawptr, + old_size: int, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location +) -> ([]byte, Allocator_Error) { + bytes, err := scratch_allocator_resize_non_zeroed(s, old_memory, old_size, size, alignment, loc) + if bytes != nil && size > old_size { + zero_slice(bytes[size:]) + } + return bytes, err +} + scratch_allocator_proc :: proc( allocator_data: rawptr, mode: Allocator_Mode, @@ -170,9 +317,7 @@ scratch_allocator_proc :: proc( old_size: int, loc := #caller_location, ) -> ([]byte, Allocator_Error) { - s := (^Scratch_Allocator)(allocator_data) - if s.data == nil { DEFAULT_BACKING_SIZE :: 4 * Megabyte if !(context.allocator.procedure != scratch_allocator_proc && @@ -181,129 +326,29 @@ scratch_allocator_proc :: proc( } scratch_allocator_init(s, DEFAULT_BACKING_SIZE) } - size := size - switch mode { - case .Alloc, .Alloc_Non_Zeroed: - size = align_forward_int(size, alignment) - - switch { - case s.curr_offset+size <= len(s.data): - start := uintptr(raw_data(s.data)) - ptr := start + uintptr(s.curr_offset) - ptr = align_forward_uintptr(ptr, uintptr(alignment)) - if mode != .Alloc_Non_Zeroed { - zero(rawptr(ptr), size) - } - - s.prev_allocation = rawptr(ptr) - offset := int(ptr - start) - s.curr_offset = offset + size - return byte_slice(rawptr(ptr), size), nil - - case size <= len(s.data): - start := uintptr(raw_data(s.data)) - ptr := align_forward_uintptr(start, uintptr(alignment)) - if mode != .Alloc_Non_Zeroed { - zero(rawptr(ptr), size) - } - - s.prev_allocation = rawptr(ptr) - offset := int(ptr - start) - s.curr_offset = offset + size - return byte_slice(rawptr(ptr), size), nil - } - a := s.backup_allocator - if a.procedure == nil { - a = context.allocator - s.backup_allocator = a - } - - ptr, err := alloc_bytes(size, alignment, a, loc) - if err != nil { - return ptr, err - } - if s.leaked_allocations == nil { - s.leaked_allocations, err = make([dynamic][]byte, a) - } - append(&s.leaked_allocations, ptr) - - if logger := context.logger; logger.lowest_level <= .Warning { - if logger.procedure != nil { - logger.procedure(logger.data, .Warning, "mem.Scratch_Allocator resorted to backup_allocator" , logger.options, loc) - } - } - - return ptr, err - + case .Alloc: + return scratch_allocator_alloc(s, size, alignment, loc) + case .Alloc_Non_Zeroed: + return scratch_allocator_alloc_non_zeroed(s, size, alignment, loc) case .Free: - if old_memory == nil { - return nil, nil - } - start := uintptr(raw_data(s.data)) - end := start + uintptr(len(s.data)) - old_ptr := uintptr(old_memory) - - if s.prev_allocation == old_memory { - s.curr_offset = int(uintptr(s.prev_allocation) - start) - s.prev_allocation = nil - return nil, nil - } - - if start <= old_ptr && old_ptr < end { - // NOTE(bill): Cannot free this pointer but it is valid - return nil, nil - } - - if len(s.leaked_allocations) != 0 { - for data, i in s.leaked_allocations { - ptr := raw_data(data) - if ptr == old_memory { - free_bytes(data, s.backup_allocator) - ordered_remove(&s.leaked_allocations, i) - return nil, nil - } - } - } - return nil, .Invalid_Pointer - // panic("invalid pointer passed to default_temp_allocator"); - + return nil, scratch_allocator_free(s, old_memory, loc) case .Free_All: - s.curr_offset = 0 - s.prev_allocation = nil - for ptr in s.leaked_allocations { - free_bytes(ptr, s.backup_allocator) - } - clear(&s.leaked_allocations) - - case .Resize, .Resize_Non_Zeroed: - begin := uintptr(raw_data(s.data)) - end := begin + uintptr(len(s.data)) - old_ptr := uintptr(old_memory) - if begin <= old_ptr && old_ptr < end && old_ptr+uintptr(size) < end { - s.curr_offset = int(old_ptr-begin)+size - return byte_slice(old_memory, size), nil - } - data, err := scratch_allocator_proc(allocator_data, .Alloc, size, alignment, old_memory, old_size, loc) - if err != nil { - return data, err - } - runtime.copy(data, byte_slice(old_memory, old_size)) - _, err = scratch_allocator_proc(allocator_data, .Free, 0, alignment, old_memory, old_size, loc) - return data, err - + scratch_allocator_free_all(s, loc) + case .Resize: + return scratch_allocator_resize(s, old_memory, old_size, size, alignment, loc) + case .Resize_Non_Zeroed: + return scratch_allocator_resize_non_zeroed(s, old_memory, old_size, size, alignment, loc) case .Query_Features: set := (^Allocator_Mode_Set)(old_memory) if set != nil { set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Resize_Non_Zeroed, .Query_Features} } return nil, nil - case .Query_Info: return nil, .Mode_Not_Implemented } - return nil, nil } From 834f082dbabb807e01f483c6a4d61a51a4dad47c Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sat, 7 Sep 2024 10:24:00 +1100 Subject: [PATCH 04/72] [mem]: Initialize scratch allocator during calls to free and resize --- core/mem/allocators.odin | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index 2be4d5b61..4ffb02085 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -261,6 +261,9 @@ scratch_allocator_free :: proc(s: ^Scratch_Allocator, ptr: rawptr, loc := #calle } scratch_allocator_free_all :: proc(s: ^Scratch_Allocator, loc := #caller_location) { + if s.data == nil { + panic("free_all called on an unitialized scratch allocator", loc) + } s.curr_offset = 0 s.prev_allocation = nil for ptr in s.leaked_allocations { @@ -277,6 +280,13 @@ scratch_allocator_resize_non_zeroed :: proc( alignment := DEFAULT_ALIGNMENT, loc := #caller_location ) -> ([]byte, Allocator_Error) { + if s.data == nil { + DEFAULT_BACKING_SIZE :: 4 * Megabyte + if !(context.allocator.procedure != scratch_allocator_proc && context.allocator.data != s) { + panic("cyclic initialization of the scratch allocator with itself", loc) + } + scratch_allocator_init(s, DEFAULT_BACKING_SIZE) + } begin := uintptr(raw_data(s.data)) end := begin + uintptr(len(s.data)) old_ptr := uintptr(old_memory) @@ -318,14 +328,6 @@ scratch_allocator_proc :: proc( loc := #caller_location, ) -> ([]byte, Allocator_Error) { s := (^Scratch_Allocator)(allocator_data) - if s.data == nil { - DEFAULT_BACKING_SIZE :: 4 * Megabyte - if !(context.allocator.procedure != scratch_allocator_proc && - context.allocator.data != allocator_data) { - panic("cyclic initialization of the scratch allocator with itself") - } - scratch_allocator_init(s, DEFAULT_BACKING_SIZE) - } size := size switch mode { case .Alloc: From 9750b64096024990ee84b5727d6db34ffc686948 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sat, 7 Sep 2024 10:55:54 +1100 Subject: [PATCH 05/72] [mem]: API for using stack allocator directly --- core/mem/allocators.odin | 283 +++++++++++++++++++++++---------------- 1 file changed, 169 insertions(+), 114 deletions(-) diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index 4ffb02085..bade70ce0 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -289,6 +289,7 @@ scratch_allocator_resize_non_zeroed :: proc( } begin := uintptr(raw_data(s.data)) end := begin + uintptr(len(s.data)) + // TODO(flysand): Doesn't handle old_memory == nil old_ptr := uintptr(old_memory) if begin <= old_ptr && old_ptr < end && old_ptr+uintptr(size) < end { s.curr_offset = int(old_ptr-begin)+size @@ -362,10 +363,7 @@ scratch_allocator :: proc(allocator: ^Scratch_Allocator) -> Allocator { } } -Stack_Allocation_Header :: struct { - prev_offset: int, - padding: int, -} + // Stack is a stack-like allocator which has a strict memory freeing order Stack :: struct { @@ -375,6 +373,11 @@ Stack :: struct { peak_used: int, } +Stack_Allocation_Header :: struct { + prev_offset: int, + padding: int, +} + stack_init :: proc(s: ^Stack, data: []byte) { s.data = data s.prev_offset = 0 @@ -398,6 +401,156 @@ stack_allocator :: proc(stack: ^Stack) -> Allocator { } } +stack_allocator_alloc_non_zeroed :: proc( + s: ^Stack, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location +) -> ([]byte, Allocator_Error) { + if s.data == nil { + panic("Stack allocation on an uninitialized stack allocator", loc) + } + curr_addr := uintptr(raw_data(s.data)) + uintptr(s.curr_offset) + padding := calc_padding_with_header( + curr_addr, + uintptr(alignment), + size_of(Stack_Allocation_Header), + ) + if s.curr_offset + padding + size > len(s.data) { + return nil, .Out_Of_Memory + } + s.prev_offset = s.curr_offset + s.curr_offset += padding + next_addr := curr_addr + uintptr(padding) + header := (^Stack_Allocation_Header)(next_addr - size_of(Stack_Allocation_Header)) + header.padding = padding + header.prev_offset = s.prev_offset + s.curr_offset += size + s.peak_used = max(s.peak_used, s.curr_offset) + return byte_slice(rawptr(next_addr), size), nil +} + +stack_allocator_alloc :: proc( + s: ^Stack, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location +) -> ([]byte, Allocator_Error) { + bytes, err := stack_allocator_alloc_non_zeroed(s, size, alignment, loc) + if bytes != nil { + zero_slice(bytes) + } + return bytes, err +} + +stack_allocator_free :: proc( + s: ^Stack, + old_memory: rawptr, + loc := #caller_location, +) -> (Allocator_Error) { + if s.data == nil { + panic("Stack free on an uninitialized stack allocator", loc) + } + if old_memory == nil { + return nil + } + start := uintptr(raw_data(s.data)) + end := start + uintptr(len(s.data)) + curr_addr := uintptr(old_memory) + if !(start <= curr_addr && curr_addr < end) { + panic("Out of bounds memory address passed to stack allocator (free)", loc) + } + if curr_addr >= start+uintptr(s.curr_offset) { + // NOTE(bill): Allow double frees + return nil + } + header := (^Stack_Allocation_Header)(curr_addr - size_of(Stack_Allocation_Header)) + old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data))) + if old_offset != header.prev_offset { + // panic("Out of order stack allocator free"); + return .Invalid_Pointer + } + s.curr_offset = old_offset + s.prev_offset = header.prev_offset + return nil +} + +stack_allocator_free_all :: proc(s: ^Stack) { + if s.data == nil { + panic("Stack free all on an uninitialized stack allocator", loc) + } + s.prev_offset = 0 + s.curr_offset = 0 +} + +stack_allocator_resize_non_zeroed :: proc( + s: ^Stack, + old_memory: rawptr, + old_size: int, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location, +) -> ([]byte, Allocator_Error) { + if s.data == nil { + panic("Stack free all on an uninitialized stack allocator", loc) + } + if old_memory == nil { + return stack_allocator_alloc_non_zeroed(s, size, alignment, loc) + } + if size == 0 { + return nil, nil + } + start := uintptr(raw_data(s.data)) + end := start + uintptr(len(s.data)) + curr_addr := uintptr(old_memory) + if !(start <= curr_addr && curr_addr < end) { + panic("Out of bounds memory address passed to stack allocator (resize)") + } + if curr_addr >= start+uintptr(s.curr_offset) { + // NOTE(bill): Allow double frees + return nil, nil + } + if old_size == size { + return byte_slice(old_memory, size), nil + } + header := (^Stack_Allocation_Header)(curr_addr - size_of(Stack_Allocation_Header)) + old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data))) + if old_offset != header.prev_offset { + data, err := stack_allocator_alloc_non_zeroed(s, size, alignment, loc) + if err == nil { + runtime.copy(data, byte_slice(old_memory, old_size)) + } + return data, err + } + old_memory_size := uintptr(s.curr_offset) - (curr_addr - start) + assert(old_memory_size == uintptr(old_size)) + diff := size - old_size + s.curr_offset += diff // works for smaller sizes too + if diff > 0 { + zero(rawptr(curr_addr + uintptr(diff)), diff) + } + return byte_slice(old_memory, size), nil +} + +stack_allocator_resize :: proc( + s: ^Stack, + old_memory: rawptr, + old_size: int, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location, +) -> ([]byte, Allocator_Error) { + bytes, err := stack_allocator_alloc_non_zeroed(s, size, alignment, loc) + if bytes != nil { + if old_memory == nil { + zero_slice(bytes) + } else if size > old_size { + zero_slice(bytes[old_size:]) + } + } + return bytes, err +} + stack_allocator_proc :: proc( allocator_data: rawptr, mode: Allocator_Mode, @@ -408,121 +561,22 @@ stack_allocator_proc :: proc( location := #caller_location, ) -> ([]byte, Allocator_Error) { s := cast(^Stack)allocator_data - if s.data == nil { return nil, .Invalid_Argument } - - raw_alloc :: proc( - s: ^Stack, - size: int, - alignment: int, - zero_memory: bool, - ) -> ([]byte, Allocator_Error) { - curr_addr := uintptr(raw_data(s.data)) + uintptr(s.curr_offset) - padding := calc_padding_with_header( - curr_addr, - uintptr(alignment), - size_of(Stack_Allocation_Header), - ) - if s.curr_offset + padding + size > len(s.data) { - return nil, .Out_Of_Memory - } - s.prev_offset = s.curr_offset - s.curr_offset += padding - next_addr := curr_addr + uintptr(padding) - header := (^Stack_Allocation_Header)(next_addr - size_of(Stack_Allocation_Header)) - header.padding = padding - header.prev_offset = s.prev_offset - s.curr_offset += size - s.peak_used = max(s.peak_used, s.curr_offset) - if zero_memory { - zero(rawptr(next_addr), size) - } - return byte_slice(rawptr(next_addr), size), nil - } - switch mode { - case .Alloc, .Alloc_Non_Zeroed: - return raw_alloc(s, size, alignment, mode == .Alloc) + case .Alloc: + return stack_allocator_alloc(s, size, alignment, loc) + case .Alloc_Non_Zeroed: + return stack_allocator_alloc_non_zeroed(s, size, alignment, loc) case .Free: - if old_memory == nil { - return nil, nil - } - start := uintptr(raw_data(s.data)) - end := start + uintptr(len(s.data)) - curr_addr := uintptr(old_memory) - - if !(start <= curr_addr && curr_addr < end) { - panic("Out of bounds memory address passed to stack allocator (free)") - } - - if curr_addr >= start+uintptr(s.curr_offset) { - // NOTE(bill): Allow double frees - return nil, nil - } - - header := (^Stack_Allocation_Header)(curr_addr - size_of(Stack_Allocation_Header)) - old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data))) - - if old_offset != header.prev_offset { - // panic("Out of order stack allocator free"); - return nil, .Invalid_Pointer - } - - s.curr_offset = old_offset - s.prev_offset = header.prev_offset - + return nil, stack_allocator_free(s, old_memory, loc) case .Free_All: - s.prev_offset = 0 - s.curr_offset = 0 - - case .Resize, .Resize_Non_Zeroed: - if old_memory == nil { - return raw_alloc(s, size, alignment, mode == .Resize) - } - if size == 0 { - return nil, nil - } - - start := uintptr(raw_data(s.data)) - end := start + uintptr(len(s.data)) - curr_addr := uintptr(old_memory) - if !(start <= curr_addr && curr_addr < end) { - panic("Out of bounds memory address passed to stack allocator (resize)") - } - - if curr_addr >= start+uintptr(s.curr_offset) { - // NOTE(bill): Allow double frees - return nil, nil - } - - if old_size == size { - return byte_slice(old_memory, size), nil - } - - header := (^Stack_Allocation_Header)(curr_addr - size_of(Stack_Allocation_Header)) - old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data))) - - if old_offset != header.prev_offset { - data, err := raw_alloc(s, size, alignment, mode == .Resize) - if err == nil { - runtime.copy(data, byte_slice(old_memory, old_size)) - } - return data, err - } - - old_memory_size := uintptr(s.curr_offset) - (curr_addr - start) - assert(old_memory_size == uintptr(old_size)) - - diff := size - old_size - s.curr_offset += diff // works for smaller sizes too - if diff > 0 { - zero(rawptr(curr_addr + uintptr(diff)), diff) - } - - return byte_slice(old_memory, size), nil - + stack_allocator_free_all(s) + case .Resize: + return stack_allocator_resize(s, old_memory, old_size, size, alignment, loc) + case .Resize_Non_Zeroed: + return stack_allocator_resize_non_zeroed(s, old_memory, old_size, size, alignment, loc) case .Query_Features: set := (^Allocator_Mode_Set)(old_memory) if set != nil { @@ -532,10 +586,11 @@ stack_allocator_proc :: proc( case .Query_Info: return nil, .Mode_Not_Implemented } - return nil, nil } + + Small_Stack_Allocation_Header :: struct { padding: u8, } From de220a9aa5382b50a828eef42eb8e5895909b661 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sat, 7 Sep 2024 11:06:59 +1100 Subject: [PATCH 06/72] [mem]: Remove the extra word 'allocator' in procedures --- core/mem/allocators.odin | 95 +++++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 46 deletions(-) diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index bade70ce0..97c6d03c9 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -131,9 +131,12 @@ end_arena_temp_memory :: proc(tmp: Arena_Temp_Memory) { tmp.arena.temp_count -= 1 } +/* old procedures */ +Scratch_Allocator :: Scratch +scratch_allocator_init :: scratch_init +scratch_allocator_destroy :: scratch_destroy - -Scratch_Allocator :: struct { +Scratch :: struct { data: []byte, curr_offset: int, prev_allocation: rawptr, @@ -141,7 +144,7 @@ Scratch_Allocator :: struct { leaked_allocations: [dynamic][]byte, } -scratch_allocator_init :: proc(s: ^Scratch_Allocator, size: int, backup_allocator := context.allocator) -> Allocator_Error { +scratch_init :: proc(s: ^Scratch, size: int, backup_allocator := context.allocator) -> Allocator_Error { s.data = make_aligned([]byte, size, 2*align_of(rawptr), backup_allocator) or_return s.curr_offset = 0 s.prev_allocation = nil @@ -150,7 +153,7 @@ scratch_allocator_init :: proc(s: ^Scratch_Allocator, size: int, backup_allocato return nil } -scratch_allocator_destroy :: proc(s: ^Scratch_Allocator) { +scratch_destroy :: proc(s: ^Scratch) { if s == nil { return } @@ -162,21 +165,21 @@ scratch_allocator_destroy :: proc(s: ^Scratch_Allocator) { s^ = {} } -scratch_allocator_alloc :: proc( - s: ^Scratch_Allocator, +scratch_alloc :: proc( + s: ^Scratch, size: int, alignment := DEFAULT_ALIGNMENT, loc := #caller_location, ) -> ([]byte, Allocator_Error) { - bytes, err := scratch_allocator_alloc_non_zeroed(s, size, alignment, loc) + bytes, err := scratch_alloc_non_zeroed(s, size, alignment, loc) if bytes != nil { zero_slice(bytes) } return bytes, err } -scratch_allocator_alloc_non_zeroed :: proc( - s: ^Scratch_Allocator, +scratch_alloc_non_zeroed :: proc( + s: ^Scratch, size: int, alignment := DEFAULT_ALIGNMENT, loc := #caller_location, @@ -186,7 +189,7 @@ scratch_allocator_alloc_non_zeroed :: proc( if !(context.allocator.procedure != scratch_allocator_proc && context.allocator.data != s) { panic("cyclic initialization of the scratch allocator with itself", loc) } - scratch_allocator_init(s, DEFAULT_BACKING_SIZE) + scratch_init(s, DEFAULT_BACKING_SIZE) } size := size size = align_forward_int(size, alignment) @@ -222,13 +225,13 @@ scratch_allocator_alloc_non_zeroed :: proc( append(&s.leaked_allocations, ptr) if logger := context.logger; logger.lowest_level <= .Warning { if logger.procedure != nil { - logger.procedure(logger.data, .Warning, "mem.Scratch_Allocator resorted to backup_allocator" , logger.options, loc) + logger.procedure(logger.data, .Warning, "mem.Scratch resorted to backup_allocator" , logger.options, loc) } } return ptr, err } -scratch_allocator_free :: proc(s: ^Scratch_Allocator, ptr: rawptr, loc := #caller_location) -> Allocator_Error { +scratch_free :: proc(s: ^Scratch, ptr: rawptr, loc := #caller_location) -> Allocator_Error { if s.data == nil { panic("Free on an uninitialized scratch allocator", loc) } @@ -260,7 +263,7 @@ scratch_allocator_free :: proc(s: ^Scratch_Allocator, ptr: rawptr, loc := #calle return .Invalid_Pointer } -scratch_allocator_free_all :: proc(s: ^Scratch_Allocator, loc := #caller_location) { +scratch_free_all :: proc(s: ^Scratch, loc := #caller_location) { if s.data == nil { panic("free_all called on an unitialized scratch allocator", loc) } @@ -272,8 +275,8 @@ scratch_allocator_free_all :: proc(s: ^Scratch_Allocator, loc := #caller_locatio clear(&s.leaked_allocations) } -scratch_allocator_resize_non_zeroed :: proc( - s: ^Scratch_Allocator, +scratch_resize_non_zeroed :: proc( + s: ^Scratch, old_memory: rawptr, old_size: int, size: int, @@ -285,7 +288,7 @@ scratch_allocator_resize_non_zeroed :: proc( if !(context.allocator.procedure != scratch_allocator_proc && context.allocator.data != s) { panic("cyclic initialization of the scratch allocator with itself", loc) } - scratch_allocator_init(s, DEFAULT_BACKING_SIZE) + scratch_init(s, DEFAULT_BACKING_SIZE) } begin := uintptr(raw_data(s.data)) end := begin + uintptr(len(s.data)) @@ -295,25 +298,25 @@ scratch_allocator_resize_non_zeroed :: proc( s.curr_offset = int(old_ptr-begin)+size return byte_slice(old_memory, size), nil } - data, err := scratch_allocator_alloc_non_zeroed(s, size, alignment, loc) + data, err := scratch_alloc_non_zeroed(s, size, alignment, loc) if err != nil { return data, err } // TODO(flysand): OOB access on size < old_size. runtime.copy(data, byte_slice(old_memory, old_size)) - err = scratch_allocator_free(s, old_memory, loc) + err = scratch_free(s, old_memory, loc) return data, err } -scratch_allocator_resize :: proc( - s: ^Scratch_Allocator, +scratch_resize :: proc( + s: ^Scratch, old_memory: rawptr, old_size: int, size: int, alignment := DEFAULT_ALIGNMENT, loc := #caller_location ) -> ([]byte, Allocator_Error) { - bytes, err := scratch_allocator_resize_non_zeroed(s, old_memory, old_size, size, alignment, loc) + bytes, err := scratch_resize_non_zeroed(s, old_memory, old_size, size, alignment, loc) if bytes != nil && size > old_size { zero_slice(bytes[size:]) } @@ -328,21 +331,21 @@ scratch_allocator_proc :: proc( old_size: int, loc := #caller_location, ) -> ([]byte, Allocator_Error) { - s := (^Scratch_Allocator)(allocator_data) + s := (^Scratch)(allocator_data) size := size switch mode { case .Alloc: - return scratch_allocator_alloc(s, size, alignment, loc) + return scratch_alloc(s, size, alignment, loc) case .Alloc_Non_Zeroed: - return scratch_allocator_alloc_non_zeroed(s, size, alignment, loc) + return scratch_alloc_non_zeroed(s, size, alignment, loc) case .Free: - return nil, scratch_allocator_free(s, old_memory, loc) + return nil, scratch_free(s, old_memory, loc) case .Free_All: - scratch_allocator_free_all(s, loc) + scratch_free_all(s, loc) case .Resize: - return scratch_allocator_resize(s, old_memory, old_size, size, alignment, loc) + return scratch_resize(s, old_memory, old_size, size, alignment, loc) case .Resize_Non_Zeroed: - return scratch_allocator_resize_non_zeroed(s, old_memory, old_size, size, alignment, loc) + return scratch_resize_non_zeroed(s, old_memory, old_size, size, alignment, loc) case .Query_Features: set := (^Allocator_Mode_Set)(old_memory) if set != nil { @@ -356,7 +359,7 @@ scratch_allocator_proc :: proc( } @(require_results) -scratch_allocator :: proc(allocator: ^Scratch_Allocator) -> Allocator { +scratch_allocator :: proc(allocator: ^Scratch) -> Allocator { return Allocator{ procedure = scratch_allocator_proc, data = allocator, @@ -401,7 +404,7 @@ stack_allocator :: proc(stack: ^Stack) -> Allocator { } } -stack_allocator_alloc_non_zeroed :: proc( +stack_alloc_non_zeroed :: proc( s: ^Stack, size: int, alignment := DEFAULT_ALIGNMENT, @@ -430,20 +433,20 @@ stack_allocator_alloc_non_zeroed :: proc( return byte_slice(rawptr(next_addr), size), nil } -stack_allocator_alloc :: proc( +stack_alloc :: proc( s: ^Stack, size: int, alignment := DEFAULT_ALIGNMENT, loc := #caller_location ) -> ([]byte, Allocator_Error) { - bytes, err := stack_allocator_alloc_non_zeroed(s, size, alignment, loc) + bytes, err := stack_alloc_non_zeroed(s, size, alignment, loc) if bytes != nil { zero_slice(bytes) } return bytes, err } -stack_allocator_free :: proc( +stack_free :: proc( s: ^Stack, old_memory: rawptr, loc := #caller_location, @@ -475,7 +478,7 @@ stack_allocator_free :: proc( return nil } -stack_allocator_free_all :: proc(s: ^Stack) { +stack_free_all :: proc(s: ^Stack, loc := #caller_location) { if s.data == nil { panic("Stack free all on an uninitialized stack allocator", loc) } @@ -483,7 +486,7 @@ stack_allocator_free_all :: proc(s: ^Stack) { s.curr_offset = 0 } -stack_allocator_resize_non_zeroed :: proc( +stack_resize_non_zeroed :: proc( s: ^Stack, old_memory: rawptr, old_size: int, @@ -495,7 +498,7 @@ stack_allocator_resize_non_zeroed :: proc( panic("Stack free all on an uninitialized stack allocator", loc) } if old_memory == nil { - return stack_allocator_alloc_non_zeroed(s, size, alignment, loc) + return stack_alloc_non_zeroed(s, size, alignment, loc) } if size == 0 { return nil, nil @@ -516,7 +519,7 @@ stack_allocator_resize_non_zeroed :: proc( header := (^Stack_Allocation_Header)(curr_addr - size_of(Stack_Allocation_Header)) old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data))) if old_offset != header.prev_offset { - data, err := stack_allocator_alloc_non_zeroed(s, size, alignment, loc) + data, err := stack_alloc_non_zeroed(s, size, alignment, loc) if err == nil { runtime.copy(data, byte_slice(old_memory, old_size)) } @@ -532,7 +535,7 @@ stack_allocator_resize_non_zeroed :: proc( return byte_slice(old_memory, size), nil } -stack_allocator_resize :: proc( +stack_resize :: proc( s: ^Stack, old_memory: rawptr, old_size: int, @@ -540,7 +543,7 @@ stack_allocator_resize :: proc( alignment := DEFAULT_ALIGNMENT, loc := #caller_location, ) -> ([]byte, Allocator_Error) { - bytes, err := stack_allocator_alloc_non_zeroed(s, size, alignment, loc) + bytes, err := stack_alloc_non_zeroed(s, size, alignment, loc) if bytes != nil { if old_memory == nil { zero_slice(bytes) @@ -558,7 +561,7 @@ stack_allocator_proc :: proc( alignment: int, old_memory: rawptr, old_size: int, - location := #caller_location, + loc := #caller_location, ) -> ([]byte, Allocator_Error) { s := cast(^Stack)allocator_data if s.data == nil { @@ -566,17 +569,17 @@ stack_allocator_proc :: proc( } switch mode { case .Alloc: - return stack_allocator_alloc(s, size, alignment, loc) + return stack_alloc(s, size, alignment, loc) case .Alloc_Non_Zeroed: - return stack_allocator_alloc_non_zeroed(s, size, alignment, loc) + return stack_alloc_non_zeroed(s, size, alignment, loc) case .Free: - return nil, stack_allocator_free(s, old_memory, loc) + return nil, stack_free(s, old_memory, loc) case .Free_All: - stack_allocator_free_all(s) + stack_free_all(s, loc) case .Resize: - return stack_allocator_resize(s, old_memory, old_size, size, alignment, loc) + return stack_resize(s, old_memory, old_size, size, alignment, loc) case .Resize_Non_Zeroed: - return stack_allocator_resize_non_zeroed(s, old_memory, old_size, size, alignment, loc) + return stack_resize_non_zeroed(s, old_memory, old_size, size, alignment, loc) case .Query_Features: set := (^Allocator_Mode_Set)(old_memory) if set != nil { From 4843db0960abb49de9357d048083a46bb603b2ae Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sat, 7 Sep 2024 12:23:55 +1100 Subject: [PATCH 07/72] [mem]: API for using small stack allocator directly --- core/mem/allocators.odin | 356 ++++++++++++++++++++++----------------- 1 file changed, 203 insertions(+), 153 deletions(-) diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index 97c6d03c9..a9a362014 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -33,6 +33,14 @@ Arena_Temp_Memory :: struct { prev_offset: int, } +@(require_results) +arena_allocator :: proc(arena: ^Arena) -> Allocator { + return Allocator{ + procedure = arena_allocator_proc, + data = arena, + } +} + arena_init :: proc(a: ^Arena, data: []byte) { a.data = data a.offset = 0 @@ -48,14 +56,6 @@ init_arena :: proc(a: ^Arena, data: []byte) { a.temp_count = 0 } -@(require_results) -arena_allocator :: proc(arena: ^Arena) -> Allocator { - return Allocator{ - procedure = arena_allocator_proc, - data = arena, - } -} - arena_alloc :: proc(a: ^Arena, size: int, alignment := DEFAULT_ALIGNMENT) -> ([]byte, Allocator_Error) { bytes, err := arena_alloc_non_zeroed(a, size, alignment) if bytes != nil { @@ -144,6 +144,14 @@ Scratch :: struct { leaked_allocations: [dynamic][]byte, } +@(require_results) +scratch_allocator :: proc(allocator: ^Scratch) -> Allocator { + return Allocator{ + procedure = scratch_allocator_proc, + data = allocator, + } +} + scratch_init :: proc(s: ^Scratch, size: int, backup_allocator := context.allocator) -> Allocator_Error { s.data = make_aligned([]byte, size, 2*align_of(rawptr), backup_allocator) or_return s.curr_offset = 0 @@ -275,6 +283,21 @@ scratch_free_all :: proc(s: ^Scratch, loc := #caller_location) { clear(&s.leaked_allocations) } +scratch_resize :: proc( + s: ^Scratch, + old_memory: rawptr, + old_size: int, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location +) -> ([]byte, Allocator_Error) { + bytes, err := scratch_resize_non_zeroed(s, old_memory, old_size, size, alignment, loc) + if bytes != nil && size > old_size { + zero_slice(bytes[size:]) + } + return bytes, err +} + scratch_resize_non_zeroed :: proc( s: ^Scratch, old_memory: rawptr, @@ -308,21 +331,6 @@ scratch_resize_non_zeroed :: proc( return data, err } -scratch_resize :: proc( - s: ^Scratch, - old_memory: rawptr, - old_size: int, - size: int, - alignment := DEFAULT_ALIGNMENT, - loc := #caller_location -) -> ([]byte, Allocator_Error) { - bytes, err := scratch_resize_non_zeroed(s, old_memory, old_size, size, alignment, loc) - if bytes != nil && size > old_size { - zero_slice(bytes[size:]) - } - return bytes, err -} - scratch_allocator_proc :: proc( allocator_data: rawptr, mode: Allocator_Mode, @@ -358,14 +366,6 @@ scratch_allocator_proc :: proc( return nil, nil } -@(require_results) -scratch_allocator :: proc(allocator: ^Scratch) -> Allocator { - return Allocator{ - procedure = scratch_allocator_proc, - data = allocator, - } -} - // Stack is a stack-like allocator which has a strict memory freeing order @@ -381,6 +381,14 @@ Stack_Allocation_Header :: struct { padding: int, } +@(require_results) +stack_allocator :: proc(stack: ^Stack) -> Allocator { + return Allocator{ + procedure = stack_allocator_proc, + data = stack, + } +} + stack_init :: proc(s: ^Stack, data: []byte) { s.data = data s.prev_offset = 0 @@ -396,12 +404,17 @@ init_stack :: proc(s: ^Stack, data: []byte) { s.peak_used = 0 } -@(require_results) -stack_allocator :: proc(stack: ^Stack) -> Allocator { - return Allocator{ - procedure = stack_allocator_proc, - data = stack, +stack_alloc :: proc( + s: ^Stack, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location +) -> ([]byte, Allocator_Error) { + bytes, err := stack_alloc_non_zeroed(s, size, alignment, loc) + if bytes != nil { + zero_slice(bytes) } + return bytes, err } stack_alloc_non_zeroed :: proc( @@ -433,19 +446,6 @@ stack_alloc_non_zeroed :: proc( return byte_slice(rawptr(next_addr), size), nil } -stack_alloc :: proc( - s: ^Stack, - size: int, - alignment := DEFAULT_ALIGNMENT, - loc := #caller_location -) -> ([]byte, Allocator_Error) { - bytes, err := stack_alloc_non_zeroed(s, size, alignment, loc) - if bytes != nil { - zero_slice(bytes) - } - return bytes, err -} - stack_free :: proc( s: ^Stack, old_memory: rawptr, @@ -486,6 +486,25 @@ stack_free_all :: proc(s: ^Stack, loc := #caller_location) { s.curr_offset = 0 } +stack_resize :: proc( + s: ^Stack, + old_memory: rawptr, + old_size: int, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location, +) -> ([]byte, Allocator_Error) { + bytes, err := stack_alloc_non_zeroed(s, size, alignment, loc) + if bytes != nil { + if old_memory == nil { + zero_slice(bytes) + } else if size > old_size { + zero_slice(bytes[old_size:]) + } + } + return bytes, err +} + stack_resize_non_zeroed :: proc( s: ^Stack, old_memory: rawptr, @@ -535,25 +554,6 @@ stack_resize_non_zeroed :: proc( return byte_slice(old_memory, size), nil } -stack_resize :: proc( - s: ^Stack, - old_memory: rawptr, - old_size: int, - size: int, - alignment := DEFAULT_ALIGNMENT, - loc := #caller_location, -) -> ([]byte, Allocator_Error) { - bytes, err := stack_alloc_non_zeroed(s, size, alignment, loc) - if bytes != nil { - if old_memory == nil { - zero_slice(bytes) - } else if size > old_size { - zero_slice(bytes[old_size:]) - } - } - return bytes, err -} - stack_allocator_proc :: proc( allocator_data: rawptr, mode: Allocator_Mode, @@ -626,6 +626,129 @@ small_stack_allocator :: proc(stack: ^Small_Stack) -> Allocator { } } +small_stack_alloc :: proc( + s: ^Small_Stack, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location, +) -> ([]byte, Allocator_Error) { + bytes, err := small_stack_alloc_non_zeroed(s, size, alignment, loc) + if bytes != nil { + zero_slice(bytes) + } + return bytes, err +} + +small_stack_alloc_non_zeroed :: proc( + s: ^Small_Stack, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location, +) -> ([]byte, Allocator_Error) { + if s.data == nil { + return nil, .Invalid_Argument + } + alignment := alignment + alignment := clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2) + curr_addr := uintptr(raw_data(s.data)) + uintptr(s.offset) + padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Small_Stack_Allocation_Header)) + if s.offset + padding + size > len(s.data) { + return nil, .Out_Of_Memory + } + s.offset += padding + next_addr := curr_addr + uintptr(padding) + header := (^Small_Stack_Allocation_Header)(next_addr - size_of(Small_Stack_Allocation_Header)) + header.padding = auto_cast padding + s.offset += size + s.peak_used = max(s.peak_used, s.offset) + return byte_slice(rawptr(next_addr), size), nil +} + +small_stack_free :: proc( + s: ^Small_Stack, + old_memory: rawptr, + loc := #caller_location, +) -> Allocator_Error { + if old_memory == nil { + return nil, nil + } + start := uintptr(raw_data(s.data)) + end := start + uintptr(len(s.data)) + curr_addr := uintptr(old_memory) + if !(start <= curr_addr && curr_addr < end) { + // panic("Out of bounds memory address passed to stack allocator (free)"); + return nil, .Invalid_Pointer + } + if curr_addr >= start+uintptr(s.offset) { + // NOTE(bill): Allow double frees + return nil, nil + } + header := (^Small_Stack_Allocation_Header)(curr_addr - size_of(Small_Stack_Allocation_Header)) + old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data))) + s.offset = old_offset +} + +small_stack_free_all :: proc(s: ^Small_Stack) { + s.offset = 0 +} + +small_stack_resize :: proc( + s: ^Small_Stack, + old_memory: rawptr, + old_size: int, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location, +) -> ([]byte, Allocator_Error) { + bytes, err := small_stack_resize_non_zeroed(s, old_memory, old_size, size, alignment, loc) + if bytes != nil { + if old_memory == nil { + zero_slice(bytes) + } else if size > old_size { + zero_slice(bytes[old_size:]) + } + } + return bytes, err +} + +small_stack_resize_non_zeroed :: proc( + s: ^Small_Stack, + old_memory: rawptr, + old_size: int, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location, +) -> ([]byte, Allocator_Error) { + if old_memory == nil { + return small_stack_alloc_non_zeroed(s, size, align, loc) + } + if size == 0 { + return nil, nil + } + alignment := alignment + alignment := clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2) + start := uintptr(raw_data(s.data)) + end := start + uintptr(len(s.data)) + curr_addr := uintptr(old_memory) + if !(start <= curr_addr && curr_addr < end) { + // panic("Out of bounds memory address passed to stack allocator (resize)"); + return nil, .Invalid_Pointer + } + if curr_addr >= start+uintptr(s.offset) { + // NOTE(bill): Treat as a double free + return nil, nil + } + if old_size == size { + return byte_slice(old_memory, size), nil + } + data, err := small_stack_alloc_non_zeroed(s, size, alignment, loc) + if err == nil { + runtime.copy(data, byte_slice(old_memory, old_size)) + } + return data, err + +} + small_stack_allocator_proc :: proc( allocator_data: rawptr, mode: Allocator_Mode, @@ -635,109 +758,36 @@ small_stack_allocator_proc :: proc( location := #caller_location, ) -> ([]byte, Allocator_Error) { s := cast(^Small_Stack)allocator_data - if s.data == nil { return nil, .Invalid_Argument } - - align := clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2) - - raw_alloc :: proc(s: ^Small_Stack, size, alignment: int, zero_memory: bool) -> ([]byte, Allocator_Error) { - curr_addr := uintptr(raw_data(s.data)) + uintptr(s.offset) - padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Small_Stack_Allocation_Header)) - if s.offset + padding + size > len(s.data) { - return nil, .Out_Of_Memory - } - s.offset += padding - - next_addr := curr_addr + uintptr(padding) - header := (^Small_Stack_Allocation_Header)(next_addr - size_of(Small_Stack_Allocation_Header)) - header.padding = auto_cast padding - - s.offset += size - - s.peak_used = max(s.peak_used, s.offset) - - if zero_memory { - zero(rawptr(next_addr), size) - } - return byte_slice(rawptr(next_addr), size), nil - } - switch mode { - case .Alloc, .Alloc_Non_Zeroed: - return raw_alloc(s, size, align, mode == .Alloc) + case .Alloc: + return small_stack_alloc(s, size, alignment, loc) + case .Alloc_Non_Zeroed: + return small_stack_alloc_non_zeroed(s, size, alignment, loc) case .Free: - if old_memory == nil { - return nil, nil - } - start := uintptr(raw_data(s.data)) - end := start + uintptr(len(s.data)) - curr_addr := uintptr(old_memory) - - if !(start <= curr_addr && curr_addr < end) { - // panic("Out of bounds memory address passed to stack allocator (free)"); - return nil, .Invalid_Pointer - } - - if curr_addr >= start+uintptr(s.offset) { - // NOTE(bill): Allow double frees - return nil, nil - } - - header := (^Small_Stack_Allocation_Header)(curr_addr - size_of(Small_Stack_Allocation_Header)) - old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data))) - - s.offset = old_offset - + return nil, small_stack_free(s, old_memory, loc) case .Free_All: - s.offset = 0 - - case .Resize, .Resize_Non_Zeroed: - if old_memory == nil { - return raw_alloc(s, size, align, mode == .Resize) - } - if size == 0 { - return nil, nil - } - - start := uintptr(raw_data(s.data)) - end := start + uintptr(len(s.data)) - curr_addr := uintptr(old_memory) - if !(start <= curr_addr && curr_addr < end) { - // panic("Out of bounds memory address passed to stack allocator (resize)"); - return nil, .Invalid_Pointer - } - - if curr_addr >= start+uintptr(s.offset) { - // NOTE(bill): Treat as a double free - return nil, nil - } - - if old_size == size { - return byte_slice(old_memory, size), nil - } - - data, err := raw_alloc(s, size, align, mode == .Resize) - if err == nil { - runtime.copy(data, byte_slice(old_memory, old_size)) - } - return data, err - + small_stack_free_all(s) + case .Resize: + return small_stack_resize(s, old_memory, old_size, size, alignment, loc) + case .Resize_Non_Zeroed: + return small_stack_resize_non_zeroed(s, old_memory, old_size, size, alignment, loc) case .Query_Features: set := (^Allocator_Mode_Set)(old_memory) if set != nil { set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Resize_Non_Zeroed, .Query_Features} } return nil, nil - case .Query_Info: return nil, .Mode_Not_Implemented } - return nil, nil } + + Dynamic_Pool :: struct { block_size: int, out_band_size: int, From aea3e9a585e07765a1d5c71448460665e25414e2 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sat, 7 Sep 2024 12:26:47 +1100 Subject: [PATCH 08/72] [mem]: Fix vet errors --- core/mem/allocators.odin | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index a9a362014..2d9d6d114 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -649,7 +649,7 @@ small_stack_alloc_non_zeroed :: proc( return nil, .Invalid_Argument } alignment := alignment - alignment := clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2) + alignment = clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2) curr_addr := uintptr(raw_data(s.data)) + uintptr(s.offset) padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Small_Stack_Allocation_Header)) if s.offset + padding + size > len(s.data) { @@ -670,22 +670,23 @@ small_stack_free :: proc( loc := #caller_location, ) -> Allocator_Error { if old_memory == nil { - return nil, nil + return nil } start := uintptr(raw_data(s.data)) end := start + uintptr(len(s.data)) curr_addr := uintptr(old_memory) if !(start <= curr_addr && curr_addr < end) { // panic("Out of bounds memory address passed to stack allocator (free)"); - return nil, .Invalid_Pointer + return .Invalid_Pointer } if curr_addr >= start+uintptr(s.offset) { // NOTE(bill): Allow double frees - return nil, nil + return nil } header := (^Small_Stack_Allocation_Header)(curr_addr - size_of(Small_Stack_Allocation_Header)) old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data))) s.offset = old_offset + return nil } small_stack_free_all :: proc(s: ^Small_Stack) { @@ -719,14 +720,14 @@ small_stack_resize_non_zeroed :: proc( alignment := DEFAULT_ALIGNMENT, loc := #caller_location, ) -> ([]byte, Allocator_Error) { + alignment := alignment + alignment = clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2) if old_memory == nil { - return small_stack_alloc_non_zeroed(s, size, align, loc) + return small_stack_alloc_non_zeroed(s, size, alignment, loc) } if size == 0 { return nil, nil } - alignment := alignment - alignment := clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2) start := uintptr(raw_data(s.data)) end := start + uintptr(len(s.data)) curr_addr := uintptr(old_memory) @@ -755,7 +756,7 @@ small_stack_allocator_proc :: proc( size, alignment: int, old_memory: rawptr, old_size: int, - location := #caller_location, + loc := #caller_location, ) -> ([]byte, Allocator_Error) { s := cast(^Small_Stack)allocator_data if s.data == nil { From f8641ddd1b096663ed45f9179014ff5201b65225 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sat, 7 Sep 2024 12:33:12 +1100 Subject: [PATCH 09/72] [mem]: Rename dynamic pool to dynamic arena --- core/mem/allocators.odin | 61 ++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index 2d9d6d114..7c33d1c9f 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -788,8 +788,19 @@ small_stack_allocator_proc :: proc( } +/* old stuff */ +Dynamic_Pool :: Dynamic_Arena +DYNAMIC_POOL_BLOCK_SIZE_DEFAULT :: DYNAMIC_ARENA_BLOCK_SIZE_DEFAULT +DYNAMIC_POOL_OUT_OF_BAND_SIZE_DEFAULT :: DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT +dynamic_pool_allocator_proc :: dynamic_arena_allocator_proc +dynamic_pool_free_all :: dynamic_arena_free_all +dynamic_pool_reset :: dynamic_arena_reset +dynamic_pool_alloc_bytes :: dynamic_arena_alloc_bytes +dynamic_pool_alloc :: dynamic_arena_alloc +dynamic_pool_init :: dynamic_arena_init +dynamic_pool_allocator :: dynamic_arena_allocator -Dynamic_Pool :: struct { +Dynamic_Arena :: struct { block_size: int, out_band_size: int, alignment: int, @@ -805,10 +816,10 @@ Dynamic_Pool :: struct { block_allocator: Allocator, } -DYNAMIC_POOL_BLOCK_SIZE_DEFAULT :: 65536 -DYNAMIC_POOL_OUT_OF_BAND_SIZE_DEFAULT :: 6554 +DYNAMIC_ARENA_BLOCK_SIZE_DEFAULT :: 65536 +DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT :: 6554 -dynamic_pool_allocator_proc :: proc( +dynamic_arena_allocator_proc :: proc( allocator_data: rawptr, mode: Allocator_Mode, size: int, @@ -817,21 +828,21 @@ dynamic_pool_allocator_proc :: proc( old_size: int, loc := #caller_location, ) -> ([]byte, Allocator_Error) { - pool := (^Dynamic_Pool)(allocator_data) + pool := (^Dynamic_Arena)(allocator_data) switch mode { case .Alloc, .Alloc_Non_Zeroed: - return dynamic_pool_alloc_bytes(pool, size) + return dynamic_arena_alloc_bytes(pool, size) case .Free: return nil, .Mode_Not_Implemented case .Free_All: - dynamic_pool_free_all(pool) + dynamic_arena_free_all(pool) return nil, nil case .Resize, .Resize_Non_Zeroed: if old_size >= size { return byte_slice(old_memory, size), nil } - data, err := dynamic_pool_alloc_bytes(pool, size) + data, err := dynamic_arena_alloc_bytes(pool, size) if err == nil { runtime.copy(data, byte_slice(old_memory, old_size)) } @@ -856,21 +867,20 @@ dynamic_pool_allocator_proc :: proc( return nil, nil } - @(require_results) -dynamic_pool_allocator :: proc(pool: ^Dynamic_Pool) -> Allocator { +dynamic_arena_allocator :: proc(pool: ^Dynamic_Arena) -> Allocator { return Allocator{ - procedure = dynamic_pool_allocator_proc, + procedure = dynamic_arena_allocator_proc, data = pool, } } -dynamic_pool_init :: proc( - pool: ^Dynamic_Pool, +dynamic_arena_init :: proc( + pool: ^Dynamic_Arena, block_allocator := context.allocator, array_allocator := context.allocator, - block_size := DYNAMIC_POOL_BLOCK_SIZE_DEFAULT, - out_band_size := DYNAMIC_POOL_OUT_OF_BAND_SIZE_DEFAULT, + block_size := DYNAMIC_ARENA_BLOCK_SIZE_DEFAULT, + out_band_size := DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT, alignment := 8, ) { pool.block_size = block_size @@ -882,8 +892,8 @@ dynamic_pool_init :: proc( pool.used_blocks.allocator = array_allocator } -dynamic_pool_destroy :: proc(pool: ^Dynamic_Pool) { - dynamic_pool_free_all(pool) +dynamic_arena_destroy :: proc(pool: ^Dynamic_Arena) { + dynamic_arena_free_all(pool) delete(pool.unused_blocks) delete(pool.used_blocks) delete(pool.out_band_allocations) @@ -891,14 +901,14 @@ dynamic_pool_destroy :: proc(pool: ^Dynamic_Pool) { } @(require_results) -dynamic_pool_alloc :: proc(pool: ^Dynamic_Pool, bytes: int) -> (rawptr, Allocator_Error) { - data, err := dynamic_pool_alloc_bytes(pool, bytes) +dynamic_arena_alloc :: proc(pool: ^Dynamic_Arena, bytes: int) -> (rawptr, Allocator_Error) { + data, err := dynamic_arena_alloc_bytes(pool, bytes) return raw_data(data), err } @(require_results) -dynamic_pool_alloc_bytes :: proc(p: ^Dynamic_Pool, bytes: int) -> ([]byte, Allocator_Error) { - cycle_new_block :: proc(p: ^Dynamic_Pool) -> (err: Allocator_Error) { +dynamic_arena_alloc_bytes :: proc(p: ^Dynamic_Arena, bytes: int) -> ([]byte, Allocator_Error) { + cycle_new_block :: proc(p: ^Dynamic_Arena) -> (err: Allocator_Error) { if p.block_allocator.procedure == nil { panic("You must call pool_init on a Pool before using it") } @@ -960,8 +970,7 @@ dynamic_pool_alloc_bytes :: proc(p: ^Dynamic_Pool, bytes: int) -> ([]byte, Alloc return ([^]byte)(memory)[:bytes], nil } - -dynamic_pool_reset :: proc(p: ^Dynamic_Pool) { +dynamic_arena_reset :: proc(p: ^Dynamic_Arena) { if p.current_block != nil { append(&p.unused_blocks, p.current_block) p.current_block = nil @@ -980,8 +989,8 @@ dynamic_pool_reset :: proc(p: ^Dynamic_Pool) { p.bytes_left = 0 // Make new allocations call `cycle_new_block` again. } -dynamic_pool_free_all :: proc(p: ^Dynamic_Pool) { - dynamic_pool_reset(p) +dynamic_arena_free_all :: proc(p: ^Dynamic_Arena) { + dynamic_arena_reset(p) for block in p.unused_blocks { free(block, p.block_allocator) @@ -989,6 +998,8 @@ dynamic_pool_free_all :: proc(p: ^Dynamic_Pool) { clear(&p.unused_blocks) } + + panic_allocator_proc :: proc( allocator_data: rawptr, mode: Allocator_Mode, From 03f6b9bbf6bdd9ccf2f566a2da108d1f8b3a38e1 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sat, 7 Sep 2024 12:59:19 +1100 Subject: [PATCH 10/72] [mem]: Add alloc_non_zeroed variant to dynamic pool --- core/mem/allocators.odin | 303 +++++++++++++++++++-------------------- 1 file changed, 151 insertions(+), 152 deletions(-) diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index 7c33d1c9f..4c6ab09b1 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -795,30 +795,158 @@ DYNAMIC_POOL_OUT_OF_BAND_SIZE_DEFAULT :: DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT dynamic_pool_allocator_proc :: dynamic_arena_allocator_proc dynamic_pool_free_all :: dynamic_arena_free_all dynamic_pool_reset :: dynamic_arena_reset -dynamic_pool_alloc_bytes :: dynamic_arena_alloc_bytes -dynamic_pool_alloc :: dynamic_arena_alloc +dynamic_pool_alloc_bytes :: dynamic_arena_alloc +dynamic_pool_alloc :: _dynamic_arena_alloc_ptr dynamic_pool_init :: dynamic_arena_init dynamic_pool_allocator :: dynamic_arena_allocator - -Dynamic_Arena :: struct { - block_size: int, - out_band_size: int, - alignment: int, - - unused_blocks: [dynamic]rawptr, - used_blocks: [dynamic]rawptr, - out_band_allocations: [dynamic]rawptr, - - current_block: rawptr, - current_pos: rawptr, - bytes_left: int, - - block_allocator: Allocator, -} +dynamic_pool_destroy :: dynamic_arena_destroy DYNAMIC_ARENA_BLOCK_SIZE_DEFAULT :: 65536 DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT :: 6554 +Dynamic_Arena :: struct { + block_size: int, + out_band_size: int, + alignment: int, + unused_blocks: [dynamic]rawptr, + used_blocks: [dynamic]rawptr, + out_band_allocations: [dynamic]rawptr, + current_block: rawptr, + current_pos: rawptr, + bytes_left: int, + block_allocator: Allocator, +} + +dynamic_arena_init :: proc( + pool: ^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, +) { + pool.block_size = block_size + pool.out_band_size = out_band_size + pool.alignment = alignment + pool.block_allocator = block_allocator + pool.out_band_allocations.allocator = array_allocator + pool.unused_blocks.allocator = array_allocator + pool.used_blocks.allocator = array_allocator +} + +@(require_results) +dynamic_arena_allocator :: proc(pool: ^Dynamic_Arena) -> Allocator { + return Allocator{ + procedure = dynamic_arena_allocator_proc, + data = pool, + } +} + +dynamic_arena_destroy :: proc(pool: ^Dynamic_Arena) { + dynamic_arena_free_all(pool) + delete(pool.unused_blocks) + delete(pool.used_blocks) + delete(pool.out_band_allocations) + zero(pool, size_of(pool^)) +} + +@(private="file") +_dynamic_arena_cycle_new_block :: proc(p: ^Dynamic_Arena, loc := #caller_location) -> (err: Allocator_Error) { + if p.block_allocator.procedure == nil { + panic("You must call pool_init on a Pool before using it", loc) + } + if p.current_block != nil { + append(&p.used_blocks, p.current_block, loc=loc) + } + new_block: rawptr + if len(p.unused_blocks) > 0 { + new_block = pop(&p.unused_blocks) + } else { + data: []byte + data, err = p.block_allocator.procedure( + p.block_allocator.data, + Allocator_Mode.Alloc, + p.block_size, + p.alignment, + nil, + 0, + ) + new_block = raw_data(data) + } + p.bytes_left = p.block_size + p.current_pos = new_block + p.current_block = new_block + return +} + +@(private, require_results) +_dynamic_arena_alloc_ptr :: proc(pool: ^Dynamic_Arena, size: int, loc := #caller_location) -> (rawptr, Allocator_Error) { + data, err := dynamic_arena_alloc(pool, size, loc) + return raw_data(data), err +} + +@(require_results) +dynamic_arena_alloc :: proc(p: ^Dynamic_Arena, size: int, loc := #caller_location) -> ([]byte, Allocator_Error) { + bytes, err := dynamic_arena_alloc_non_zeroed(p, size, loc) + if bytes != nil { + zero_slice(bytes) + } + return bytes, err +} + +@(require_results) +dynamic_arena_alloc_non_zeroed :: proc(p: ^Dynamic_Arena, size: int, loc := #caller_location) -> ([]byte, Allocator_Error) { + n := align_formula(size, p.alignment) + if n > p.block_size { + return nil, .Invalid_Argument + } + if n >= p.out_band_size { + assert(p.block_allocator.procedure != nil, "Backing block allocator must be initialized", loc=loc) + memory, err := alloc_bytes_non_zeroed(p.block_size, p.alignment, p.block_allocator, loc) + if memory != nil { + append(&p.out_band_allocations, raw_data(memory), loc = loc) + } + return memory, err + } + if p.bytes_left < n { + err := _dynamic_arena_cycle_new_block(p, loc) + if err != nil { + return nil, err + } + if p.current_block == nil { + return nil, .Out_Of_Memory + } + } + memory := p.current_pos + p.current_pos = ([^]byte)(p.current_pos)[n:] + p.bytes_left -= n + return ([^]byte)(memory)[:size], nil +} + +dynamic_arena_reset :: proc(p: ^Dynamic_Arena, loc := #caller_location) { + if p.current_block != nil { + append(&p.unused_blocks, p.current_block, loc=loc) + p.current_block = nil + } + for block in p.used_blocks { + append(&p.unused_blocks, block, loc=loc) + } + clear(&p.used_blocks) + for a in p.out_band_allocations { + free(a, p.block_allocator, loc=loc) + } + clear(&p.out_band_allocations) + p.bytes_left = 0 // Make new allocations call `_dynamic_arena_cycle_new_block` again. +} + +dynamic_arena_free_all :: proc(p: ^Dynamic_Arena) { + dynamic_arena_reset(p) + for block in p.unused_blocks { + free(block, p.block_allocator) + } + clear(&p.unused_blocks) +} + dynamic_arena_allocator_proc :: proc( allocator_data: rawptr, mode: Allocator_Mode, @@ -831,8 +959,10 @@ dynamic_arena_allocator_proc :: proc( pool := (^Dynamic_Arena)(allocator_data) switch mode { - case .Alloc, .Alloc_Non_Zeroed: - return dynamic_arena_alloc_bytes(pool, size) + case .Alloc: + return dynamic_arena_alloc(pool, size, loc) + case .Alloc_Non_Zeroed: + return dynamic_arena_alloc_non_zeroed(pool, size, loc) case .Free: return nil, .Mode_Not_Implemented case .Free_All: @@ -842,7 +972,7 @@ dynamic_arena_allocator_proc :: proc( if old_size >= size { return byte_slice(old_memory, size), nil } - data, err := dynamic_arena_alloc_bytes(pool, size) + data, err := dynamic_arena_alloc(pool, size) if err == nil { runtime.copy(data, byte_slice(old_memory, old_size)) } @@ -867,137 +997,6 @@ dynamic_arena_allocator_proc :: proc( return nil, nil } -@(require_results) -dynamic_arena_allocator :: proc(pool: ^Dynamic_Arena) -> Allocator { - return Allocator{ - procedure = dynamic_arena_allocator_proc, - data = pool, - } -} - -dynamic_arena_init :: proc( - pool: ^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 := 8, -) { - pool.block_size = block_size - pool.out_band_size = out_band_size - pool.alignment = alignment - pool.block_allocator = block_allocator - pool.out_band_allocations.allocator = array_allocator - pool.unused_blocks.allocator = array_allocator - pool.used_blocks.allocator = array_allocator -} - -dynamic_arena_destroy :: proc(pool: ^Dynamic_Arena) { - dynamic_arena_free_all(pool) - delete(pool.unused_blocks) - delete(pool.used_blocks) - delete(pool.out_band_allocations) - zero(pool, size_of(pool^)) -} - -@(require_results) -dynamic_arena_alloc :: proc(pool: ^Dynamic_Arena, bytes: int) -> (rawptr, Allocator_Error) { - data, err := dynamic_arena_alloc_bytes(pool, bytes) - return raw_data(data), err -} - -@(require_results) -dynamic_arena_alloc_bytes :: proc(p: ^Dynamic_Arena, bytes: int) -> ([]byte, Allocator_Error) { - cycle_new_block :: proc(p: ^Dynamic_Arena) -> (err: Allocator_Error) { - if p.block_allocator.procedure == nil { - panic("You must call pool_init on a Pool before using it") - } - - if p.current_block != nil { - append(&p.used_blocks, p.current_block) - } - - new_block: rawptr - if len(p.unused_blocks) > 0 { - new_block = pop(&p.unused_blocks) - } else { - data: []byte - data, err = p.block_allocator.procedure( - p.block_allocator.data, - Allocator_Mode.Alloc, - p.block_size, - p.alignment, - nil, - 0, - ) - new_block = raw_data(data) - } - - p.bytes_left = p.block_size - p.current_pos = new_block - p.current_block = new_block - return - } - - n := align_formula(bytes, p.alignment) - if n > p.block_size { - return nil, .Invalid_Argument - } - if n >= p.out_band_size { - assert(p.block_allocator.procedure != nil) - memory, err := p.block_allocator.procedure(p.block_allocator.data, Allocator_Mode.Alloc, - p.block_size, p.alignment, - nil, 0) - if memory != nil { - append(&p.out_band_allocations, raw_data(memory)) - } - return memory, err - } - - if p.bytes_left < n { - err := cycle_new_block(p) - if err != nil { - return nil, err - } - if p.current_block == nil { - return nil, .Out_Of_Memory - } - } - - memory := p.current_pos - p.current_pos = ([^]byte)(p.current_pos)[n:] - p.bytes_left -= n - return ([^]byte)(memory)[:bytes], nil -} - -dynamic_arena_reset :: proc(p: ^Dynamic_Arena) { - if p.current_block != nil { - append(&p.unused_blocks, p.current_block) - p.current_block = nil - } - - for block in p.used_blocks { - append(&p.unused_blocks, block) - } - clear(&p.used_blocks) - - for a in p.out_band_allocations { - free(a, p.block_allocator) - } - clear(&p.out_band_allocations) - - p.bytes_left = 0 // Make new allocations call `cycle_new_block` again. -} - -dynamic_arena_free_all :: proc(p: ^Dynamic_Arena) { - dynamic_arena_reset(p) - - for block in p.unused_blocks { - free(block, p.block_allocator) - } - clear(&p.unused_blocks) -} - panic_allocator_proc :: proc( From b350a35b7738c6f7ba7ee65dd403b86de32213c5 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sat, 7 Sep 2024 13:10:29 +1100 Subject: [PATCH 11/72] [mem]: Add resize_non_zeroed variant to dynamic arena, and rename pool to arena --- core/mem/allocators.odin | 126 +++++++++++++++++++++++---------------- 1 file changed, 76 insertions(+), 50 deletions(-) diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index 4c6ab09b1..d7e3cfbfd 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -880,14 +880,14 @@ _dynamic_arena_cycle_new_block :: proc(p: ^Dynamic_Arena, loc := #caller_locatio } @(private, require_results) -_dynamic_arena_alloc_ptr :: proc(pool: ^Dynamic_Arena, size: int, loc := #caller_location) -> (rawptr, Allocator_Error) { - data, err := dynamic_arena_alloc(pool, size, loc) +_dynamic_arena_alloc_ptr :: proc(a: ^Dynamic_Arena, size: int, loc := #caller_location) -> (rawptr, Allocator_Error) { + data, err := dynamic_arena_alloc(a, size, loc) return raw_data(data), err } @(require_results) -dynamic_arena_alloc :: proc(p: ^Dynamic_Arena, size: int, loc := #caller_location) -> ([]byte, Allocator_Error) { - bytes, err := dynamic_arena_alloc_non_zeroed(p, size, loc) +dynamic_arena_alloc :: proc(a: ^Dynamic_Arena, size: int, loc := #caller_location) -> ([]byte, Allocator_Error) { + bytes, err := dynamic_arena_alloc_non_zeroed(a, size, loc) if bytes != nil { zero_slice(bytes) } @@ -895,56 +895,91 @@ dynamic_arena_alloc :: proc(p: ^Dynamic_Arena, size: int, loc := #caller_locatio } @(require_results) -dynamic_arena_alloc_non_zeroed :: proc(p: ^Dynamic_Arena, size: int, loc := #caller_location) -> ([]byte, Allocator_Error) { - n := align_formula(size, p.alignment) - if n > p.block_size { +dynamic_arena_alloc_non_zeroed :: proc(a: ^Dynamic_Arena, size: int, loc := #caller_location) -> ([]byte, Allocator_Error) { + n := align_formula(size, a.alignment) + if n > a.block_size { return nil, .Invalid_Argument } - if n >= p.out_band_size { - assert(p.block_allocator.procedure != nil, "Backing block allocator must be initialized", loc=loc) - memory, err := alloc_bytes_non_zeroed(p.block_size, p.alignment, p.block_allocator, loc) + if n >= a.out_band_size { + assert(a.block_allocator.procedure != nil, "Backing block allocator must be initialized", loc=loc) + memory, err := alloc_bytes_non_zeroed(a.block_size, a.alignment, a.block_allocator, loc) if memory != nil { - append(&p.out_band_allocations, raw_data(memory), loc = loc) + append(&a.out_band_allocations, raw_data(memory), loc = loc) } return memory, err } - if p.bytes_left < n { - err := _dynamic_arena_cycle_new_block(p, loc) + if a.bytes_left < n { + err := _dynamic_arena_cycle_new_block(a, loc) if err != nil { return nil, err } - if p.current_block == nil { + if a.current_block == nil { return nil, .Out_Of_Memory } } - memory := p.current_pos - p.current_pos = ([^]byte)(p.current_pos)[n:] - p.bytes_left -= n + memory := a.current_pos + a.current_pos = ([^]byte)(a.current_pos)[n:] + a.bytes_left -= n return ([^]byte)(memory)[:size], nil } -dynamic_arena_reset :: proc(p: ^Dynamic_Arena, loc := #caller_location) { - if p.current_block != nil { - append(&p.unused_blocks, p.current_block, loc=loc) - p.current_block = nil +dynamic_arena_reset :: proc(a: ^Dynamic_Arena, loc := #caller_location) { + if a.current_block != nil { + append(&a.unused_blocks, a.current_block, loc=loc) + a.current_block = nil } - for block in p.used_blocks { - append(&p.unused_blocks, block, loc=loc) + for block in a.used_blocks { + append(&a.unused_blocks, block, loc=loc) } - clear(&p.used_blocks) - for a in p.out_band_allocations { - free(a, p.block_allocator, loc=loc) + clear(&a.used_blocks) + for a in a.out_band_allocations { + free(a, a.block_allocator, loc=loc) } - clear(&p.out_band_allocations) - p.bytes_left = 0 // Make new allocations call `_dynamic_arena_cycle_new_block` again. + clear(&a.out_band_allocations) + a.bytes_left = 0 // Make new allocations call `_dynamic_arena_cycle_new_block` again. } -dynamic_arena_free_all :: proc(p: ^Dynamic_Arena) { - dynamic_arena_reset(p) - for block in p.unused_blocks { - free(block, p.block_allocator) +dynamic_arena_free_all :: proc(a: ^Dynamic_Arena, loc := #caller_location) { + dynamic_arena_reset(a) + for block in a.unused_blocks { + free(block, a.block_allocator, loc) } - clear(&p.unused_blocks) + clear(&a.unused_blocks) +} + +dynamic_arena_resize :: proc( + a: ^Dynamic_Arena, + old_memory: rawptr, + old_size: int, + size: int, + loc := #caller_location, +) -> ([]byte, Allocator_Error) { + bytes, err := dynamic_arena_resize_non_zeroed(a, old_memory, old_size, size, loc) + if bytes != nil { + if old_memory == nil { + zero_slice(bytes) + } else if size > old_size { + zero_slice(bytes[old_size:]) + } + } + return bytes, err +} + +dynamic_arena_resize_non_zeroed :: proc( + a: ^Dynamic_Arena, + old_memory: rawptr, + old_size: int, + size: int, + loc := #caller_location, +) -> ([]byte, Allocator_Error) { + if old_size >= size { + return byte_slice(old_memory, size), nil + } + data, err := dynamic_arena_alloc_non_zeroed(a, size, loc) + if err == nil { + runtime.copy(data, byte_slice(old_memory, old_size)) + } + return data, err } dynamic_arena_allocator_proc :: proc( @@ -956,35 +991,26 @@ dynamic_arena_allocator_proc :: proc( old_size: int, loc := #caller_location, ) -> ([]byte, Allocator_Error) { - pool := (^Dynamic_Arena)(allocator_data) - + arena := (^Dynamic_Arena)(allocator_data) switch mode { case .Alloc: - return dynamic_arena_alloc(pool, size, loc) + return dynamic_arena_alloc(arena, size, loc) case .Alloc_Non_Zeroed: - return dynamic_arena_alloc_non_zeroed(pool, size, loc) + return dynamic_arena_alloc_non_zeroed(arena, size, loc) case .Free: return nil, .Mode_Not_Implemented case .Free_All: - dynamic_arena_free_all(pool) - return nil, nil - case .Resize, .Resize_Non_Zeroed: - if old_size >= size { - return byte_slice(old_memory, size), nil - } - data, err := dynamic_arena_alloc(pool, size) - if err == nil { - runtime.copy(data, byte_slice(old_memory, old_size)) - } - return data, err - + dynamic_arena_free_all(arena, loc) + case .Resize: + return dynamic_arena_resize(arena, old_memory, old_size, size, loc) + case .Resize_Non_Zeroed: + return dynamic_arena_resize_non_zeroed(arena, old_memory, old_size, size, loc) case .Query_Features: set := (^Allocator_Mode_Set)(old_memory) if set != nil { set^ = {.Alloc, .Alloc_Non_Zeroed, .Free_All, .Resize, .Resize_Non_Zeroed, .Query_Features, .Query_Info} } return nil, nil - case .Query_Info: info := (^Allocator_Query_Info)(old_memory) if info != nil && info.pointer != nil { From 6d3cffa13c4e43400eaaa33a0c551cef5cd3e44c Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sat, 7 Sep 2024 13:14:58 +1100 Subject: [PATCH 12/72] [mem]: Add @require_results to all functions returning values --- core/mem/allocators.odin | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index d7e3cfbfd..1efc60033 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -56,6 +56,7 @@ init_arena :: proc(a: ^Arena, data: []byte) { a.temp_count = 0 } +@(require_results) arena_alloc :: proc(a: ^Arena, size: int, alignment := DEFAULT_ALIGNMENT) -> ([]byte, Allocator_Error) { bytes, err := arena_alloc_non_zeroed(a, size, alignment) if bytes != nil { @@ -64,6 +65,7 @@ arena_alloc :: proc(a: ^Arena, size: int, alignment := DEFAULT_ALIGNMENT) -> ([] return bytes, err } +@(require_results) arena_alloc_non_zeroed :: proc(a: ^Arena, size: int, alignment := DEFAULT_ALIGNMENT) -> ([]byte, Allocator_Error) { #no_bounds_check end := &a.data[a.offset] ptr := align_forward(end, uintptr(alignment)) @@ -173,6 +175,7 @@ scratch_destroy :: proc(s: ^Scratch) { s^ = {} } +@(require_results) scratch_alloc :: proc( s: ^Scratch, size: int, @@ -186,6 +189,7 @@ scratch_alloc :: proc( return bytes, err } +@(require_results) scratch_alloc_non_zeroed :: proc( s: ^Scratch, size: int, @@ -239,6 +243,7 @@ scratch_alloc_non_zeroed :: proc( return ptr, err } +@(require_results) scratch_free :: proc(s: ^Scratch, ptr: rawptr, loc := #caller_location) -> Allocator_Error { if s.data == nil { panic("Free on an uninitialized scratch allocator", loc) @@ -283,6 +288,7 @@ scratch_free_all :: proc(s: ^Scratch, loc := #caller_location) { clear(&s.leaked_allocations) } +@(require_results) scratch_resize :: proc( s: ^Scratch, old_memory: rawptr, @@ -298,6 +304,7 @@ scratch_resize :: proc( return bytes, err } +@(require_results) scratch_resize_non_zeroed :: proc( s: ^Scratch, old_memory: rawptr, @@ -404,6 +411,7 @@ init_stack :: proc(s: ^Stack, data: []byte) { s.peak_used = 0 } +@(require_results) stack_alloc :: proc( s: ^Stack, size: int, @@ -417,6 +425,7 @@ stack_alloc :: proc( return bytes, err } +@(require_results) stack_alloc_non_zeroed :: proc( s: ^Stack, size: int, @@ -446,6 +455,7 @@ stack_alloc_non_zeroed :: proc( return byte_slice(rawptr(next_addr), size), nil } +@(require_results) stack_free :: proc( s: ^Stack, old_memory: rawptr, @@ -486,6 +496,7 @@ stack_free_all :: proc(s: ^Stack, loc := #caller_location) { s.curr_offset = 0 } +@(require_results) stack_resize :: proc( s: ^Stack, old_memory: rawptr, @@ -505,6 +516,7 @@ stack_resize :: proc( return bytes, err } +@(require_results) stack_resize_non_zeroed :: proc( s: ^Stack, old_memory: rawptr, @@ -626,6 +638,7 @@ small_stack_allocator :: proc(stack: ^Small_Stack) -> Allocator { } } +@(require_results) small_stack_alloc :: proc( s: ^Small_Stack, size: int, @@ -639,6 +652,7 @@ small_stack_alloc :: proc( return bytes, err } +@(require_results) small_stack_alloc_non_zeroed :: proc( s: ^Small_Stack, size: int, @@ -664,6 +678,7 @@ small_stack_alloc_non_zeroed :: proc( return byte_slice(rawptr(next_addr), size), nil } +@(require_results) small_stack_free :: proc( s: ^Small_Stack, old_memory: rawptr, @@ -693,6 +708,7 @@ small_stack_free_all :: proc(s: ^Small_Stack) { s.offset = 0 } +@(require_results) small_stack_resize :: proc( s: ^Small_Stack, old_memory: rawptr, @@ -712,6 +728,7 @@ small_stack_resize :: proc( return bytes, err } +@(require_results) small_stack_resize_non_zeroed :: proc( s: ^Small_Stack, old_memory: rawptr, @@ -932,8 +949,8 @@ dynamic_arena_reset :: proc(a: ^Dynamic_Arena, loc := #caller_location) { append(&a.unused_blocks, block, loc=loc) } clear(&a.used_blocks) - for a in a.out_band_allocations { - free(a, a.block_allocator, loc=loc) + for allocation in a.out_band_allocations { + free(allocation, a.block_allocator, loc=loc) } clear(&a.out_band_allocations) a.bytes_left = 0 // Make new allocations call `_dynamic_arena_cycle_new_block` again. @@ -947,6 +964,7 @@ dynamic_arena_free_all :: proc(a: ^Dynamic_Arena, loc := #caller_location) { clear(&a.unused_blocks) } +@(require_results) dynamic_arena_resize :: proc( a: ^Dynamic_Arena, old_memory: rawptr, @@ -965,6 +983,7 @@ dynamic_arena_resize :: proc( return bytes, err } +@(require_results) dynamic_arena_resize_non_zeroed :: proc( a: ^Dynamic_Arena, old_memory: rawptr, @@ -1014,8 +1033,8 @@ dynamic_arena_allocator_proc :: proc( case .Query_Info: info := (^Allocator_Query_Info)(old_memory) if info != nil && info.pointer != nil { - info.size = pool.block_size - info.alignment = pool.alignment + info.size = arena.block_size + info.alignment = arena.alignment return byte_slice(info, size_of(info^)), nil } return nil, nil @@ -1033,7 +1052,6 @@ panic_allocator_proc :: proc( old_size: int, loc := #caller_location, ) -> ([]byte, Allocator_Error) { - switch mode { case .Alloc: if size > 0 { @@ -1057,7 +1075,6 @@ panic_allocator_proc :: proc( } case .Free_All: panic("mem: panic allocator, .Free_All called", loc=loc) - case .Query_Features: set := (^Allocator_Mode_Set)(old_memory) if set != nil { @@ -1068,7 +1085,6 @@ panic_allocator_proc :: proc( case .Query_Info: panic("mem: panic allocator, .Query_Info called", loc=loc) } - return nil, nil } @@ -1080,6 +1096,8 @@ panic_allocator :: proc() -> Allocator { } } + + Buddy_Block :: struct #align(align_of(uint)) { size: uint, is_free: bool, From c0e17808d46be70b461c020c9c320349e2f99ad9 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sat, 7 Sep 2024 13:26:09 +1100 Subject: [PATCH 13/72] [mem]: Split alloc and alloc_non_zeroed for buddy allocator --- core/mem/allocators.odin | 59 ++++++++++++++-------------------------- 1 file changed, 21 insertions(+), 38 deletions(-) diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index 1efc60033..45c80e678 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -1133,7 +1133,6 @@ buddy_block_coalescence :: proc(head, tail: ^Buddy_Block) { // Keep looping until there are no more buddies to coalesce block := head buddy := buddy_block_next(block) - no_coalescence := true for block < tail && buddy < tail { // make sure the buddies are within the range if block.is_free && buddy.is_free && block.size == buddy.size { @@ -1156,7 +1155,6 @@ buddy_block_coalescence :: proc(head, tail: ^Buddy_Block) { } } } - if no_coalescence { return } @@ -1166,17 +1164,14 @@ buddy_block_coalescence :: proc(head, tail: ^Buddy_Block) { @(require_results) buddy_block_find_best :: proc(head, tail: ^Buddy_Block, size: uint) -> ^Buddy_Block { assert(size != 0) - best_block: ^Buddy_Block block := head // left buddy := buddy_block_next(block) // right - // The entire memory section between head and tail is free, // just call 'buddy_block_split' to get the allocation if buddy == tail && block.is_free { return buddy_block_split(block, size) } - // Find the block which is the 'best_block' to requested allocation sized for block < tail && buddy < tail { // make sure the buddies are within the range // If both buddies are free, coalesce them together @@ -1187,7 +1182,6 @@ buddy_block_find_best :: proc(head, tail: ^Buddy_Block, size: uint) -> ^Buddy_Bl if size <= block.size && (best_block == nil || block.size <= best_block.size) { best_block = block } - block = buddy_block_next(buddy) if block < tail { // Delay the buddy block for the next iteration @@ -1195,20 +1189,16 @@ buddy_block_find_best :: proc(head, tail: ^Buddy_Block, size: uint) -> ^Buddy_Bl } continue } - - if block.is_free && size <= block.size && (best_block == nil || block.size <= best_block.size) { best_block = block } - if buddy.is_free && size <= buddy.size && (best_block == nil || buddy.size < best_block.size) { // If each buddy are the same size, then it makes more sense // to pick the buddy as it "bounces around" less best_block = buddy } - if (block.size <= buddy.size) { block = buddy_block_next(buddy) if (block < tail) { @@ -1221,12 +1211,10 @@ buddy_block_find_best :: proc(head, tail: ^Buddy_Block, size: uint) -> ^Buddy_Bl buddy = buddy_block_next(buddy) } } - if best_block != nil { // This will handle the case if the 'best_block' is also the perfect fit return buddy_block_split(best_block, size) } - // Maybe out of memory return nil } @@ -1245,26 +1233,20 @@ buddy_allocator :: proc(b: ^Buddy_Allocator) -> Allocator { } } -buddy_allocator_init :: proc(b: ^Buddy_Allocator, data: []byte, alignment: uint) { +buddy_allocator_init :: proc(b: ^Buddy_Allocator, data: []byte, alignment: uint, loc := #caller_location) { assert(data != nil) - assert(is_power_of_two(uintptr(len(data)))) - assert(is_power_of_two(uintptr(alignment))) - + assert(is_power_of_two(uintptr(len(data))), "Size of the backing buffer must be power of two", loc) + assert(is_power_of_two(uintptr(alignment)), "Alignment must be a power of two", loc) alignment := alignment if alignment < size_of(Buddy_Block) { alignment = size_of(Buddy_Block) } - ptr := raw_data(data) - assert(uintptr(ptr) % uintptr(alignment) == 0, "data is not aligned to minimum alignment") - + assert(uintptr(ptr) % uintptr(alignment) == 0, "data is not aligned to minimum alignment", loc) b.head = (^Buddy_Block)(ptr) - b.head.size = len(data) b.head.is_free = true - b.tail = buddy_block_next(b.head) - b.alignment = alignment } @@ -1274,19 +1256,25 @@ buddy_block_size_required :: proc(b: ^Buddy_Allocator, size: uint) -> uint { actual_size := b.alignment size += size_of(Buddy_Block) size = align_forward_uint(size, b.alignment) - for size > actual_size { actual_size <<= 1 } - return actual_size } @(require_results) -buddy_allocator_alloc :: proc(b: ^Buddy_Allocator, size: uint, zeroed: bool) -> ([]byte, Allocator_Error) { +buddy_allocator_alloc :: proc(b: ^Buddy_Allocator, size: uint) -> ([]byte, Allocator_Error) { + bytes, err := buddy_allocator_alloc_non_zeroed(b, size) + if bytes != nil { + zero_slice(bytes) + } + return bytes, err +} + +@(require_results) +buddy_allocator_alloc_non_zeroed :: proc(b: ^Buddy_Allocator, size: uint) -> ([]byte, Allocator_Error) { if size != 0 { actual_size := buddy_block_size_required(b, size) - found := buddy_block_find_best(b.head, b.tail, actual_size) if found != nil { // Try to coalesce all the free buddy blocks and then search again @@ -1297,32 +1285,28 @@ buddy_allocator_alloc :: proc(b: ^Buddy_Allocator, size: uint, zeroed: bool) -> return nil, .Out_Of_Memory } found.is_free = false - data := ([^]byte)(found)[b.alignment:][:size] - if zeroed { - zero_slice(data) - } return data, nil } return nil, nil } +@(require_results) buddy_allocator_free :: proc(b: ^Buddy_Allocator, ptr: rawptr) -> Allocator_Error { if ptr != nil { if !(b.head <= ptr && ptr <= b.tail) { return .Invalid_Pointer } - block := (^Buddy_Block)(([^]byte)(ptr)[-b.alignment:]) block.is_free = true - buddy_block_coalescence(b.head, b.tail) } return nil } buddy_allocator_proc :: proc( - allocator_data: rawptr, mode: Allocator_Mode, + allocator_data: rawptr, + mode: Allocator_Mode, size, alignment: int, old_memory: rawptr, old_size: int, @@ -1330,10 +1314,11 @@ buddy_allocator_proc :: proc( ) -> ([]byte, Allocator_Error) { b := (^Buddy_Allocator)(allocator_data) - switch mode { - case .Alloc, .Alloc_Non_Zeroed: - return buddy_allocator_alloc(b, uint(size), mode == .Alloc) + case .Alloc: + return buddy_allocator_alloc(b, uint(size)) + case .Alloc_Non_Zeroed: + return buddy_allocator_alloc_non_zeroed(b, uint(size)) case .Resize: return default_resize_bytes_align(byte_slice(old_memory, old_size), size, alignment, buddy_allocator(b)) case .Resize_Non_Zeroed: @@ -1341,13 +1326,11 @@ buddy_allocator_proc :: proc( case .Free: return nil, buddy_allocator_free(b, old_memory) case .Free_All: - alignment := b.alignment head := ([^]byte)(b.head) tail := ([^]byte)(b.tail) data := head[:ptr_sub(tail, head)] buddy_allocator_init(b, data, alignment) - case .Query_Features: set := (^Allocator_Mode_Set)(old_memory) if set != nil { From c0112d1c70e369dd4f4704d577c7ff6e8ef17282 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sat, 7 Sep 2024 13:27:17 +1100 Subject: [PATCH 14/72] [mem]: Add free_all for buddy allocator --- core/mem/allocators.odin | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index 45c80e678..5fedbd4d6 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -1304,6 +1304,14 @@ buddy_allocator_free :: proc(b: ^Buddy_Allocator, ptr: rawptr) -> Allocator_Erro return nil } +buddy_allocator_free_all :: proc(b: ^Buddy_Allocator) { + alignment := b.alignment + head := ([^]byte)(b.head) + tail := ([^]byte)(b.tail) + data := head[:ptr_sub(tail, head)] + buddy_allocator_init(b, data, alignment) +} + buddy_allocator_proc :: proc( allocator_data: rawptr, mode: Allocator_Mode, @@ -1312,7 +1320,6 @@ buddy_allocator_proc :: proc( old_size: int, loc := #caller_location, ) -> ([]byte, Allocator_Error) { - b := (^Buddy_Allocator)(allocator_data) switch mode { case .Alloc: @@ -1326,18 +1333,13 @@ buddy_allocator_proc :: proc( case .Free: return nil, buddy_allocator_free(b, old_memory) case .Free_All: - alignment := b.alignment - head := ([^]byte)(b.head) - tail := ([^]byte)(b.tail) - data := head[:ptr_sub(tail, head)] - buddy_allocator_init(b, data, alignment) + buddy_allocator_free_all(b) case .Query_Features: set := (^Allocator_Mode_Set)(old_memory) if set != nil { set^ = {.Query_Features, .Alloc, .Alloc_Non_Zeroed, .Resize, .Resize_Non_Zeroed, .Free, .Free_All, .Query_Info} } return nil, nil - case .Query_Info: info := (^Allocator_Query_Info)(old_memory) if info != nil && info.pointer != nil { @@ -1345,7 +1347,6 @@ buddy_allocator_proc :: proc( if !(b.head <= ptr && ptr <= b.tail) { return nil, .Invalid_Pointer } - block := (^Buddy_Block)(([^]byte)(ptr)[-b.alignment:]) info.size = int(block.size) info.alignment = int(b.alignment) @@ -1353,6 +1354,5 @@ buddy_allocator_proc :: proc( } return nil, nil } - return nil, nil } From 64814f4199c8d89a3fb0ed7013aa20321a0b34d5 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sat, 7 Sep 2024 14:19:50 +1100 Subject: [PATCH 15/72] [mem]: Document the package --- core/mem/doc.odin | 111 ++++++++++++++++++++++++------- core/mem/tracking_allocator.odin | 31 +++++++++ 2 files changed, 119 insertions(+), 23 deletions(-) diff --git a/core/mem/doc.odin b/core/mem/doc.odin index 44c93f798..b152d0738 100644 --- a/core/mem/doc.odin +++ b/core/mem/doc.odin @@ -1,34 +1,99 @@ /* -package mem implements various types of allocators. +The `mem` package implements various allocators and provides utility functions +for dealing with memory, pointers and slices. +The documentation below describes basic concepts, applicable to the `mem` +package. -An example of how to use the `Tracking_Allocator` to track subsequent allocations -in your program and report leaks and bad frees: +## Pointers, multipointers, and slices -Example: - package foo +A *pointer* is an abstraction of an *address*, a numberic value representing the +location of an object in memory. That object is said to be *pointed to* by the +pointer. To obtain the address of a pointer, cast it to `uintptr`. - import "core:mem" - import "core:fmt" +A multipointer is a pointer that points to multiple objects. Unlike a pointer, +a multipointer can be indexed, but does not have a definite length. A slice is +a pointer that points to multiple objects equipped with the length, specifying +the amount of objects a slice points to. - _main :: proc() { - // do stuff - } +When object's values are read through a pointer, that operation is called a +*load* operation. When memory is read through a pointer, that operation is +called a *store* operation. Both of these operations can be called a *memory +access operation*. - main :: proc() { - track: mem.Tracking_Allocator - mem.tracking_allocator_init(&track, context.allocator) - defer mem.tracking_allocator_destroy(&track) - context.allocator = mem.tracking_allocator(&track) +## Allocators - _main() +In C and C++ memory models, allocations of objects in memory are typically +treated individually with a generic allocator (The `malloc` function). Which in +some scenarios can lead to poor cache utilization, slowdowns on individual +objects' memory management and growing complexity of the code needing to keep +track of the pointers and their lifetimes. - for _, leak in track.allocation_map { - fmt.printf("%v leaked %m\n", leak.location, leak.size) - } - for bad_free in track.bad_free_array { - fmt.printf("%v allocation %p was freed badly\n", bad_free.location, bad_free.memory) - } - } +Using different kinds of *allocators* for different purposes can solve these +problems. The allocators are typically optimized for specific use-cases and +can potentially simplify the memory management code. + +For example, in the context of making a game, having an Arena allocator could +simplify allocations of any temporary memory, because the programmer doesn't +have to keep track of which objects need to be freed every time they are +allocated, because at the end of every frame the whole allocator is reset to +its initial state and all objects are freed at once. + +The allocators have different kinds of restrictions on object lifetimes, sizes, +alignment and can be a significant gain, if used properly. Odin supports +allocators on a language level. + +Operations such as `new`, `free` and `delete` by default will use +`context.allocator`, which can be overridden by the user. When an override +happens all called functions will inherit the new context and use the same +allocator. + +## Alignment + +An address is said to be *aligned to `N` bytes*, if the addresses's numeric +value is divisible by `N`. The number `N` in this case can be referred to as +the *alignment boundary*. Typically an alignment is a power of two integer +value. + +A *natural alignment* of an object is typically equal to its size. For example +a 16 bit integer has a natural alignment of 2 bytes. When an object is not +located on its natural alignment boundary, accesses to that object are +considered *unaligned*. + +Some machines issue a hardware **exception**, or experience **slowdowns** when a +memory access operation occurs from an unaligned address. Examples of such +operations are: + +- SIMD instructions on x86. These instructions require all memory accesses to be + on an address that is aligned to 16 bytes. +- On ARM unaligned loads have an extra cycle penalty. + +As such, many operations that allocate memory in this package allow to +explicitly specify the alignment of allocated pointers/slices. The default +alignment for all operations is specified in a constant `mem.DEFAULT_ALIGNMENT`. + +## Zero by default + +Whenever new memory is allocated, via an allocator, or on the stack, by default +Odin will zero-initialize that memory, even if it wasn't explicitly +initialized. This allows for some convenience in certain scenarios and ease of +debugging, which will not be described in detail here. + +However zero-initialization can be a cause of slowdowns, when allocating large +buffers. For this reason, allocators have `*_non_zeroed` modes of allocation +that allow the user to request for uninitialized memory and will avoid a +relatively expensive zero-filling of the buffer. + +## Naming conventions + +The word `size` is used to denote the **size in bytes**. The word `length` is +used to denote the count of objects. + +Higher-level allocation functions follow the following naming scheme: + +- `new`: Allocates a single object +- `free`: Free a single object (opposite of `new`) +- `make`: Allocate a group of objects +- `delete`: Free a group of objects (opposite of `make`) */ package mem diff --git a/core/mem/tracking_allocator.odin b/core/mem/tracking_allocator.odin index 356180be1..e75844130 100644 --- a/core/mem/tracking_allocator.odin +++ b/core/mem/tracking_allocator.odin @@ -18,6 +18,37 @@ Tracking_Allocator_Bad_Free_Entry :: struct { location: runtime.Source_Code_Location, } +/* +An example of how to use the `Tracking_Allocator` to track subsequent allocations +in your program and report leaks and bad frees: + +Example: + + package foo + + import "core:mem" + import "core:fmt" + + _main :: proc() { + // do stuff + } + + main :: proc() { + track: mem.Tracking_Allocator + mem.tracking_allocator_init(&track, context.allocator) + defer mem.tracking_allocator_destroy(&track) + context.allocator = mem.tracking_allocator(&track) + + _main() + + for _, leak in track.allocation_map { + fmt.printf("%v leaked %m\n", leak.location, leak.size) + } + for bad_free in track.bad_free_array { + fmt.printf("%v allocation %p was freed badly\n", bad_free.location, bad_free.memory) + } + } +*/ Tracking_Allocator :: struct { backing: Allocator, allocation_map: map[rawptr]Tracking_Allocator_Entry, From 2d988bbc5f21fd7e07926c93b01996a392b5a92d Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sat, 7 Sep 2024 14:45:15 +1100 Subject: [PATCH 16/72] [mem]: Rename alloc to alloc_bytes and add alloc --- core/mem/allocators.odin | 295 ++++++++++++++++++++++++++++++++------- 1 file changed, 248 insertions(+), 47 deletions(-) diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index 5fedbd4d6..acbc202e6 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -57,8 +57,14 @@ init_arena :: proc(a: ^Arena, data: []byte) { } @(require_results) -arena_alloc :: proc(a: ^Arena, size: int, alignment := DEFAULT_ALIGNMENT) -> ([]byte, Allocator_Error) { - bytes, err := arena_alloc_non_zeroed(a, size, alignment) +arena_alloc :: proc(a: ^Arena, size: int, alignment := DEFAULT_ALIGNMENT) -> (rawptr, Allocator_Error) { + bytes, err := arena_alloc_bytes(a, size, alignment) + return raw_data(bytes), err +} + +@(require_results) +arena_alloc_bytes :: proc(a: ^Arena, size: int, alignment := DEFAULT_ALIGNMENT) -> ([]byte, Allocator_Error) { + bytes, err := arena_alloc_bytes_non_zeroed(a, size, alignment) if bytes != nil { zero_slice(bytes) } @@ -66,7 +72,13 @@ arena_alloc :: proc(a: ^Arena, size: int, alignment := DEFAULT_ALIGNMENT) -> ([] } @(require_results) -arena_alloc_non_zeroed :: proc(a: ^Arena, size: int, alignment := DEFAULT_ALIGNMENT) -> ([]byte, Allocator_Error) { +arena_alloc_non_zeroed :: proc(a: ^Arena, size: int, alignment := DEFAULT_ALIGNMENT) -> (rawptr, Allocator_Error) { + bytes, err := arena_alloc_bytes_non_zeroed(a, size, alignment) + return raw_data(bytes), err +} + +@(require_results) +arena_alloc_bytes_non_zeroed :: proc(a: ^Arena, size: int, alignment := DEFAULT_ALIGNMENT) -> ([]byte, Allocator_Error) { #no_bounds_check end := &a.data[a.offset] ptr := align_forward(end, uintptr(alignment)) total_size := size + ptr_sub((^byte)(ptr), (^byte)(end)) @@ -94,9 +106,9 @@ arena_allocator_proc :: proc( arena := cast(^Arena)allocator_data switch mode { case .Alloc: - return arena_alloc(arena, size, alignment) + return arena_alloc_bytes(arena, size, alignment) case .Alloc_Non_Zeroed: - return arena_alloc_non_zeroed(arena, size, alignment) + return arena_alloc_bytes_non_zeroed(arena, size, alignment) case .Free: return nil, .Mode_Not_Implemented case .Free_All: @@ -181,8 +193,19 @@ scratch_alloc :: proc( size: int, alignment := DEFAULT_ALIGNMENT, loc := #caller_location, +) -> (rawptr, Allocator_Error) { + bytes, err := scratch_alloc_bytes(s, size, alignment, loc) + return raw_data(bytes), err +} + +@(require_results) +scratch_alloc_bytes :: proc( + s: ^Scratch, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location, ) -> ([]byte, Allocator_Error) { - bytes, err := scratch_alloc_non_zeroed(s, size, alignment, loc) + bytes, err := scratch_alloc_bytes_non_zeroed(s, size, alignment, loc) if bytes != nil { zero_slice(bytes) } @@ -195,6 +218,17 @@ scratch_alloc_non_zeroed :: proc( size: int, alignment := DEFAULT_ALIGNMENT, loc := #caller_location, +) -> (rawptr, Allocator_Error) { + bytes, err := scratch_alloc_bytes_non_zeroed(s, size, alignment, loc) + return raw_data(bytes), err +} + +@(require_results) +scratch_alloc_bytes_non_zeroed :: proc( + s: ^Scratch, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location, ) -> ([]byte, Allocator_Error) { if s.data == nil { DEFAULT_BACKING_SIZE :: 4 * Megabyte @@ -296,8 +330,21 @@ scratch_resize :: proc( size: int, alignment := DEFAULT_ALIGNMENT, loc := #caller_location +) -> (rawptr, Allocator_Error) { + bytes, err := scratch_resize_bytes(s, old_memory, old_size, size, alignment, loc) + return raw_data(bytes), err +} + +@(require_results) +scratch_resize_bytes :: proc( + s: ^Scratch, + old_memory: rawptr, + old_size: int, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location ) -> ([]byte, Allocator_Error) { - bytes, err := scratch_resize_non_zeroed(s, old_memory, old_size, size, alignment, loc) + bytes, err := scratch_resize_bytes_non_zeroed(s, old_memory, old_size, size, alignment, loc) if bytes != nil && size > old_size { zero_slice(bytes[size:]) } @@ -312,6 +359,19 @@ scratch_resize_non_zeroed :: proc( size: int, alignment := DEFAULT_ALIGNMENT, loc := #caller_location +) -> (rawptr, Allocator_Error) { + bytes, err := scratch_resize_bytes_non_zeroed(s, old_memory, old_size, size, alignment, loc) + return raw_data(bytes), err +} + +@(require_results) +scratch_resize_bytes_non_zeroed :: proc( + s: ^Scratch, + old_memory: rawptr, + old_size: int, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location ) -> ([]byte, Allocator_Error) { if s.data == nil { DEFAULT_BACKING_SIZE :: 4 * Megabyte @@ -328,7 +388,7 @@ scratch_resize_non_zeroed :: proc( s.curr_offset = int(old_ptr-begin)+size return byte_slice(old_memory, size), nil } - data, err := scratch_alloc_non_zeroed(s, size, alignment, loc) + data, err := scratch_alloc_bytes_non_zeroed(s, size, alignment, loc) if err != nil { return data, err } @@ -350,17 +410,17 @@ scratch_allocator_proc :: proc( size := size switch mode { case .Alloc: - return scratch_alloc(s, size, alignment, loc) + return scratch_alloc_bytes(s, size, alignment, loc) case .Alloc_Non_Zeroed: - return scratch_alloc_non_zeroed(s, size, alignment, loc) + return scratch_alloc_bytes_non_zeroed(s, size, alignment, loc) case .Free: return nil, scratch_free(s, old_memory, loc) case .Free_All: scratch_free_all(s, loc) case .Resize: - return scratch_resize(s, old_memory, old_size, size, alignment, loc) + return scratch_resize_bytes(s, old_memory, old_size, size, alignment, loc) case .Resize_Non_Zeroed: - return scratch_resize_non_zeroed(s, old_memory, old_size, size, alignment, loc) + return scratch_resize_bytes_non_zeroed(s, old_memory, old_size, size, alignment, loc) case .Query_Features: set := (^Allocator_Mode_Set)(old_memory) if set != nil { @@ -417,8 +477,19 @@ stack_alloc :: proc( size: int, alignment := DEFAULT_ALIGNMENT, loc := #caller_location +) -> (rawptr, Allocator_Error) { + bytes, err := stack_alloc_bytes(s, size, alignment, loc) + return raw_data(bytes), err +} + +@(require_results) +stack_alloc_bytes :: proc( + s: ^Stack, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location ) -> ([]byte, Allocator_Error) { - bytes, err := stack_alloc_non_zeroed(s, size, alignment, loc) + bytes, err := stack_alloc_bytes_non_zeroed(s, size, alignment, loc) if bytes != nil { zero_slice(bytes) } @@ -431,6 +502,17 @@ stack_alloc_non_zeroed :: proc( size: int, alignment := DEFAULT_ALIGNMENT, loc := #caller_location +) -> (rawptr, Allocator_Error) { + bytes, err := stack_alloc_bytes_non_zeroed(s, size, alignment, loc) + return raw_data(bytes), err +} + +@(require_results) +stack_alloc_bytes_non_zeroed :: proc( + s: ^Stack, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location ) -> ([]byte, Allocator_Error) { if s.data == nil { panic("Stack allocation on an uninitialized stack allocator", loc) @@ -496,6 +578,7 @@ stack_free_all :: proc(s: ^Stack, loc := #caller_location) { s.curr_offset = 0 } + @(require_results) stack_resize :: proc( s: ^Stack, @@ -504,8 +587,21 @@ stack_resize :: proc( size: int, alignment := DEFAULT_ALIGNMENT, loc := #caller_location, +) -> (rawptr, Allocator_Error) { + bytes, err := stack_resize_bytes(s, old_memory, old_size, size, alignment) + return raw_data(bytes), err +} + +@(require_results) +stack_resize_bytes :: proc( + s: ^Stack, + old_memory: rawptr, + old_size: int, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location, ) -> ([]byte, Allocator_Error) { - bytes, err := stack_alloc_non_zeroed(s, size, alignment, loc) + bytes, err := stack_alloc_bytes_non_zeroed(s, size, alignment, loc) if bytes != nil { if old_memory == nil { zero_slice(bytes) @@ -524,12 +620,25 @@ stack_resize_non_zeroed :: proc( size: int, alignment := DEFAULT_ALIGNMENT, loc := #caller_location, +) -> (rawptr, Allocator_Error) { + bytes, err := stack_resize_bytes_non_zeroed(s, old_memory, old_size, size, alignment) + return raw_data(bytes), err +} + +@(require_results) +stack_resize_bytes_non_zeroed :: proc( + s: ^Stack, + old_memory: rawptr, + old_size: int, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location, ) -> ([]byte, Allocator_Error) { if s.data == nil { panic("Stack free all on an uninitialized stack allocator", loc) } if old_memory == nil { - return stack_alloc_non_zeroed(s, size, alignment, loc) + return stack_alloc_bytes_non_zeroed(s, size, alignment, loc) } if size == 0 { return nil, nil @@ -550,7 +659,7 @@ stack_resize_non_zeroed :: proc( header := (^Stack_Allocation_Header)(curr_addr - size_of(Stack_Allocation_Header)) old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data))) if old_offset != header.prev_offset { - data, err := stack_alloc_non_zeroed(s, size, alignment, loc) + data, err := stack_alloc_bytes_non_zeroed(s, size, alignment, loc) if err == nil { runtime.copy(data, byte_slice(old_memory, old_size)) } @@ -581,17 +690,17 @@ stack_allocator_proc :: proc( } switch mode { case .Alloc: - return stack_alloc(s, size, alignment, loc) + return stack_alloc_bytes(s, size, alignment, loc) case .Alloc_Non_Zeroed: - return stack_alloc_non_zeroed(s, size, alignment, loc) + return stack_alloc_bytes_non_zeroed(s, size, alignment, loc) case .Free: return nil, stack_free(s, old_memory, loc) case .Free_All: stack_free_all(s, loc) case .Resize: - return stack_resize(s, old_memory, old_size, size, alignment, loc) + return stack_resize_bytes(s, old_memory, old_size, size, alignment, loc) case .Resize_Non_Zeroed: - return stack_resize_non_zeroed(s, old_memory, old_size, size, alignment, loc) + return stack_resize_bytes_non_zeroed(s, old_memory, old_size, size, alignment, loc) case .Query_Features: set := (^Allocator_Mode_Set)(old_memory) if set != nil { @@ -644,8 +753,19 @@ small_stack_alloc :: proc( size: int, alignment := DEFAULT_ALIGNMENT, loc := #caller_location, +) -> (rawptr, Allocator_Error) { + bytes, err := small_stack_alloc_bytes(s, size, alignment, loc) + return raw_data(bytes), err +} + +@(require_results) +small_stack_alloc_bytes :: proc( + s: ^Small_Stack, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location, ) -> ([]byte, Allocator_Error) { - bytes, err := small_stack_alloc_non_zeroed(s, size, alignment, loc) + bytes, err := small_stack_alloc_bytes_non_zeroed(s, size, alignment, loc) if bytes != nil { zero_slice(bytes) } @@ -658,6 +778,17 @@ small_stack_alloc_non_zeroed :: proc( size: int, alignment := DEFAULT_ALIGNMENT, loc := #caller_location, +) -> (rawptr, Allocator_Error) { + bytes, err := small_stack_alloc_bytes_non_zeroed(s, size, alignment, loc) + return raw_data(bytes), err +} + +@(require_results) +small_stack_alloc_bytes_non_zeroed :: proc( + s: ^Small_Stack, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location, ) -> ([]byte, Allocator_Error) { if s.data == nil { return nil, .Invalid_Argument @@ -716,8 +847,21 @@ small_stack_resize :: proc( size: int, alignment := DEFAULT_ALIGNMENT, loc := #caller_location, +) -> (rawptr, Allocator_Error) { + bytes, err := small_stack_resize_bytes(s, old_memory, old_size, size, alignment, loc) + return raw_data(bytes), err +} + +@(require_results) +small_stack_resize_bytes :: proc( + s: ^Small_Stack, + old_memory: rawptr, + old_size: int, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location, ) -> ([]byte, Allocator_Error) { - bytes, err := small_stack_resize_non_zeroed(s, old_memory, old_size, size, alignment, loc) + bytes, err := small_stack_resize_bytes_non_zeroed(s, old_memory, old_size, size, alignment, loc) if bytes != nil { if old_memory == nil { zero_slice(bytes) @@ -736,11 +880,24 @@ small_stack_resize_non_zeroed :: proc( size: int, alignment := DEFAULT_ALIGNMENT, loc := #caller_location, +) -> (rawptr, Allocator_Error) { + bytes, err := small_stack_resize_bytes_non_zeroed(s, old_memory, old_size, size, alignment, loc) + return raw_data(bytes), err +} + +@(require_results) +small_stack_resize_bytes_non_zeroed :: proc( + s: ^Small_Stack, + old_memory: rawptr, + old_size: int, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location, ) -> ([]byte, Allocator_Error) { alignment := alignment alignment = clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2) if old_memory == nil { - return small_stack_alloc_non_zeroed(s, size, alignment, loc) + return small_stack_alloc_bytes_non_zeroed(s, size, alignment, loc) } if size == 0 { return nil, nil @@ -759,7 +916,7 @@ small_stack_resize_non_zeroed :: proc( if old_size == size { return byte_slice(old_memory, size), nil } - data, err := small_stack_alloc_non_zeroed(s, size, alignment, loc) + data, err := small_stack_alloc_bytes_non_zeroed(s, size, alignment, loc) if err == nil { runtime.copy(data, byte_slice(old_memory, old_size)) } @@ -781,17 +938,17 @@ small_stack_allocator_proc :: proc( } switch mode { case .Alloc: - return small_stack_alloc(s, size, alignment, loc) + return small_stack_alloc_bytes(s, size, alignment, loc) case .Alloc_Non_Zeroed: - return small_stack_alloc_non_zeroed(s, size, alignment, loc) + return small_stack_alloc_bytes_non_zeroed(s, size, alignment, loc) case .Free: return nil, small_stack_free(s, old_memory, loc) case .Free_All: small_stack_free_all(s) case .Resize: - return small_stack_resize(s, old_memory, old_size, size, alignment, loc) + return small_stack_resize_bytes(s, old_memory, old_size, size, alignment, loc) case .Resize_Non_Zeroed: - return small_stack_resize_non_zeroed(s, old_memory, old_size, size, alignment, loc) + return small_stack_resize_bytes_non_zeroed(s, old_memory, old_size, size, alignment, loc) case .Query_Features: set := (^Allocator_Mode_Set)(old_memory) if set != nil { @@ -805,19 +962,21 @@ small_stack_allocator_proc :: proc( } -/* old stuff */ +/* Preserved for compatibility */ Dynamic_Pool :: Dynamic_Arena DYNAMIC_POOL_BLOCK_SIZE_DEFAULT :: DYNAMIC_ARENA_BLOCK_SIZE_DEFAULT DYNAMIC_POOL_OUT_OF_BAND_SIZE_DEFAULT :: DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT dynamic_pool_allocator_proc :: dynamic_arena_allocator_proc dynamic_pool_free_all :: dynamic_arena_free_all dynamic_pool_reset :: dynamic_arena_reset -dynamic_pool_alloc_bytes :: dynamic_arena_alloc -dynamic_pool_alloc :: _dynamic_arena_alloc_ptr +dynamic_pool_alloc_bytes :: dynamic_arena_alloc_bytes +dynamic_pool_alloc :: dynamic_arena_alloc dynamic_pool_init :: dynamic_arena_init dynamic_pool_allocator :: dynamic_arena_allocator dynamic_pool_destroy :: dynamic_arena_destroy + + DYNAMIC_ARENA_BLOCK_SIZE_DEFAULT :: 65536 DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT :: 6554 @@ -897,14 +1056,14 @@ _dynamic_arena_cycle_new_block :: proc(p: ^Dynamic_Arena, loc := #caller_locatio } @(private, require_results) -_dynamic_arena_alloc_ptr :: proc(a: ^Dynamic_Arena, size: int, loc := #caller_location) -> (rawptr, Allocator_Error) { - data, err := dynamic_arena_alloc(a, size, loc) +dynamic_arena_alloc :: proc(a: ^Dynamic_Arena, size: int, loc := #caller_location) -> (rawptr, Allocator_Error) { + data, err := dynamic_arena_alloc_bytes(a, size, loc) return raw_data(data), err } @(require_results) -dynamic_arena_alloc :: proc(a: ^Dynamic_Arena, size: int, loc := #caller_location) -> ([]byte, Allocator_Error) { - bytes, err := dynamic_arena_alloc_non_zeroed(a, size, loc) +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) if bytes != nil { zero_slice(bytes) } @@ -912,7 +1071,13 @@ dynamic_arena_alloc :: proc(a: ^Dynamic_Arena, size: int, loc := #caller_locatio } @(require_results) -dynamic_arena_alloc_non_zeroed :: proc(a: ^Dynamic_Arena, size: int, loc := #caller_location) -> ([]byte, Allocator_Error) { +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) + return raw_data(data), err +} + +@(require_results) +dynamic_arena_alloc_bytes_non_zeroed :: proc(a: ^Dynamic_Arena, size: int, loc := #caller_location) -> ([]byte, Allocator_Error) { n := align_formula(size, a.alignment) if n > a.block_size { return nil, .Invalid_Argument @@ -971,8 +1136,20 @@ dynamic_arena_resize :: proc( old_size: int, size: int, loc := #caller_location, +) -> (rawptr, Allocator_Error) { + bytes, err := dynamic_arena_resize_bytes(a, old_memory, old_size, size, loc) + return raw_data(bytes), err +} + +@(require_results) +dynamic_arena_resize_bytes :: proc( + a: ^Dynamic_Arena, + old_memory: rawptr, + old_size: int, + size: int, + loc := #caller_location, ) -> ([]byte, Allocator_Error) { - bytes, err := dynamic_arena_resize_non_zeroed(a, old_memory, old_size, size, loc) + bytes, err := dynamic_arena_resize_bytes_non_zeroed(a, old_memory, old_size, size, loc) if bytes != nil { if old_memory == nil { zero_slice(bytes) @@ -990,11 +1167,23 @@ dynamic_arena_resize_non_zeroed :: proc( old_size: int, size: int, loc := #caller_location, +) -> (rawptr, Allocator_Error) { + bytes, err := dynamic_arena_resize_bytes_non_zeroed(a, old_memory, old_size, size, loc) + return raw_data(bytes), err +} + +@(require_results) +dynamic_arena_resize_bytes_non_zeroed :: proc( + a: ^Dynamic_Arena, + old_memory: rawptr, + old_size: int, + size: int, + loc := #caller_location, ) -> ([]byte, Allocator_Error) { if old_size >= size { return byte_slice(old_memory, size), nil } - data, err := dynamic_arena_alloc_non_zeroed(a, size, loc) + data, err := dynamic_arena_alloc_bytes_non_zeroed(a, size, loc) if err == nil { runtime.copy(data, byte_slice(old_memory, old_size)) } @@ -1013,17 +1202,17 @@ dynamic_arena_allocator_proc :: proc( arena := (^Dynamic_Arena)(allocator_data) switch mode { case .Alloc: - return dynamic_arena_alloc(arena, size, loc) + return dynamic_arena_alloc_bytes(arena, size, loc) case .Alloc_Non_Zeroed: - return dynamic_arena_alloc_non_zeroed(arena, size, loc) + return dynamic_arena_alloc_bytes_non_zeroed(arena, size, loc) case .Free: return nil, .Mode_Not_Implemented case .Free_All: dynamic_arena_free_all(arena, loc) case .Resize: - return dynamic_arena_resize(arena, old_memory, old_size, size, loc) + return dynamic_arena_resize_bytes(arena, old_memory, old_size, size, loc) case .Resize_Non_Zeroed: - return dynamic_arena_resize_non_zeroed(arena, old_memory, old_size, size, loc) + return dynamic_arena_resize_bytes_non_zeroed(arena, old_memory, old_size, size, loc) case .Query_Features: set := (^Allocator_Mode_Set)(old_memory) if set != nil { @@ -1263,8 +1452,14 @@ buddy_block_size_required :: proc(b: ^Buddy_Allocator, size: uint) -> uint { } @(require_results) -buddy_allocator_alloc :: proc(b: ^Buddy_Allocator, size: uint) -> ([]byte, Allocator_Error) { - bytes, err := buddy_allocator_alloc_non_zeroed(b, size) +buddy_allocator_alloc :: proc(b: ^Buddy_Allocator, size: uint) -> (rawptr, Allocator_Error) { + bytes, err := buddy_allocator_alloc_bytes(b, size) + return raw_data(bytes), err +} + +@(require_results) +buddy_allocator_alloc_bytes :: proc(b: ^Buddy_Allocator, size: uint) -> ([]byte, Allocator_Error) { + bytes, err := buddy_allocator_alloc_bytes_non_zeroed(b, size) if bytes != nil { zero_slice(bytes) } @@ -1272,7 +1467,13 @@ buddy_allocator_alloc :: proc(b: ^Buddy_Allocator, size: uint) -> ([]byte, Alloc } @(require_results) -buddy_allocator_alloc_non_zeroed :: proc(b: ^Buddy_Allocator, size: uint) -> ([]byte, Allocator_Error) { +buddy_allocator_alloc_non_zeroed :: proc(b: ^Buddy_Allocator, size: uint) -> (rawptr, Allocator_Error) { + bytes, err := buddy_allocator_alloc_bytes_non_zeroed(b, size) + return raw_data(bytes), err +} + +@(require_results) +buddy_allocator_alloc_bytes_non_zeroed :: proc(b: ^Buddy_Allocator, size: uint) -> ([]byte, Allocator_Error) { if size != 0 { actual_size := buddy_block_size_required(b, size) found := buddy_block_find_best(b.head, b.tail, actual_size) @@ -1323,9 +1524,9 @@ buddy_allocator_proc :: proc( b := (^Buddy_Allocator)(allocator_data) switch mode { case .Alloc: - return buddy_allocator_alloc(b, uint(size)) + return buddy_allocator_alloc_bytes(b, uint(size)) case .Alloc_Non_Zeroed: - return buddy_allocator_alloc_non_zeroed(b, uint(size)) + return buddy_allocator_alloc_bytes_non_zeroed(b, uint(size)) case .Resize: return default_resize_bytes_align(byte_slice(old_memory, old_size), size, alignment, buddy_allocator(b)) case .Resize_Non_Zeroed: From 6017a20e1cde4c218ab05a754c747b6864e87394 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sat, 7 Sep 2024 15:11:04 +1100 Subject: [PATCH 17/72] [mem]: Make resize_bytes take a slice for the old memory --- core/mem/allocators.odin | 90 ++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index acbc202e6..34b89fcb8 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -331,21 +331,20 @@ scratch_resize :: proc( alignment := DEFAULT_ALIGNMENT, loc := #caller_location ) -> (rawptr, Allocator_Error) { - bytes, err := scratch_resize_bytes(s, old_memory, old_size, size, alignment, loc) + bytes, err := scratch_resize_bytes(s, byte_slice(old_memory, old_size), size, alignment, loc) return raw_data(bytes), err } @(require_results) scratch_resize_bytes :: proc( s: ^Scratch, - old_memory: rawptr, - old_size: int, + old_data: []byte, size: int, alignment := DEFAULT_ALIGNMENT, loc := #caller_location ) -> ([]byte, Allocator_Error) { - bytes, err := scratch_resize_bytes_non_zeroed(s, old_memory, old_size, size, alignment, loc) - if bytes != nil && size > old_size { + bytes, err := scratch_resize_bytes_non_zeroed(s, old_data, size, alignment, loc) + if bytes != nil && size > len(old_data) { zero_slice(bytes[size:]) } return bytes, err @@ -360,19 +359,20 @@ scratch_resize_non_zeroed :: proc( alignment := DEFAULT_ALIGNMENT, loc := #caller_location ) -> (rawptr, Allocator_Error) { - bytes, err := scratch_resize_bytes_non_zeroed(s, old_memory, old_size, size, alignment, loc) + bytes, err := scratch_resize_bytes_non_zeroed(s, byte_slice(old_memory, old_size), size, alignment, loc) return raw_data(bytes), err } @(require_results) scratch_resize_bytes_non_zeroed :: proc( s: ^Scratch, - old_memory: rawptr, - old_size: int, + old_data: []byte, size: int, alignment := DEFAULT_ALIGNMENT, loc := #caller_location ) -> ([]byte, Allocator_Error) { + old_memory := raw_data(old_data) + old_size := len(old_data) if s.data == nil { DEFAULT_BACKING_SIZE :: 4 * Megabyte if !(context.allocator.procedure != scratch_allocator_proc && context.allocator.data != s) { @@ -418,9 +418,9 @@ scratch_allocator_proc :: proc( case .Free_All: scratch_free_all(s, loc) case .Resize: - return scratch_resize_bytes(s, old_memory, old_size, size, alignment, loc) + return scratch_resize_bytes(s, byte_slice(old_memory, old_size), size, alignment, loc) case .Resize_Non_Zeroed: - return scratch_resize_bytes_non_zeroed(s, old_memory, old_size, size, alignment, loc) + return scratch_resize_bytes_non_zeroed(s, byte_slice(old_memory, old_size), size, alignment, loc) case .Query_Features: set := (^Allocator_Mode_Set)(old_memory) if set != nil { @@ -588,25 +588,24 @@ stack_resize :: proc( alignment := DEFAULT_ALIGNMENT, loc := #caller_location, ) -> (rawptr, Allocator_Error) { - bytes, err := stack_resize_bytes(s, old_memory, old_size, size, alignment) + bytes, err := stack_resize_bytes(s, byte_slice(old_memory, old_size), size, alignment) return raw_data(bytes), err } @(require_results) stack_resize_bytes :: proc( s: ^Stack, - old_memory: rawptr, - old_size: int, + old_data: []byte, size: int, alignment := DEFAULT_ALIGNMENT, loc := #caller_location, ) -> ([]byte, Allocator_Error) { bytes, err := stack_alloc_bytes_non_zeroed(s, size, alignment, loc) if bytes != nil { - if old_memory == nil { + if old_data == nil { zero_slice(bytes) - } else if size > old_size { - zero_slice(bytes[old_size:]) + } else if size > len(old_data) { + zero_slice(bytes[len(old_data):]) } } return bytes, err @@ -621,19 +620,20 @@ stack_resize_non_zeroed :: proc( alignment := DEFAULT_ALIGNMENT, loc := #caller_location, ) -> (rawptr, Allocator_Error) { - bytes, err := stack_resize_bytes_non_zeroed(s, old_memory, old_size, size, alignment) + bytes, err := stack_resize_bytes_non_zeroed(s, byte_slice(old_memory, old_size), size, alignment) return raw_data(bytes), err } @(require_results) stack_resize_bytes_non_zeroed :: proc( s: ^Stack, - old_memory: rawptr, - old_size: int, + old_data: []byte, size: int, alignment := DEFAULT_ALIGNMENT, loc := #caller_location, ) -> ([]byte, Allocator_Error) { + old_memory := raw_data(old_data) + old_size := len(old_data) if s.data == nil { panic("Stack free all on an uninitialized stack allocator", loc) } @@ -698,9 +698,9 @@ stack_allocator_proc :: proc( case .Free_All: stack_free_all(s, loc) case .Resize: - return stack_resize_bytes(s, old_memory, old_size, size, alignment, loc) + return stack_resize_bytes(s, byte_slice(old_memory, old_size), size, alignment, loc) case .Resize_Non_Zeroed: - return stack_resize_bytes_non_zeroed(s, old_memory, old_size, size, alignment, loc) + return stack_resize_bytes_non_zeroed(s, byte_slice(old_memory, old_size), size, alignment, loc) case .Query_Features: set := (^Allocator_Mode_Set)(old_memory) if set != nil { @@ -848,25 +848,24 @@ small_stack_resize :: proc( alignment := DEFAULT_ALIGNMENT, loc := #caller_location, ) -> (rawptr, Allocator_Error) { - bytes, err := small_stack_resize_bytes(s, old_memory, old_size, size, alignment, loc) + bytes, err := small_stack_resize_bytes(s, byte_slice(old_memory, old_size), size, alignment, loc) return raw_data(bytes), err } @(require_results) small_stack_resize_bytes :: proc( s: ^Small_Stack, - old_memory: rawptr, - old_size: int, + old_data: []byte, size: int, alignment := DEFAULT_ALIGNMENT, loc := #caller_location, ) -> ([]byte, Allocator_Error) { - bytes, err := small_stack_resize_bytes_non_zeroed(s, old_memory, old_size, size, alignment, loc) + bytes, err := small_stack_resize_bytes_non_zeroed(s, old_data, size, alignment, loc) if bytes != nil { - if old_memory == nil { + if old_data == nil { zero_slice(bytes) - } else if size > old_size { - zero_slice(bytes[old_size:]) + } else if size > len(old_data) { + zero_slice(bytes[len(old_data):]) } } return bytes, err @@ -881,19 +880,20 @@ small_stack_resize_non_zeroed :: proc( alignment := DEFAULT_ALIGNMENT, loc := #caller_location, ) -> (rawptr, Allocator_Error) { - bytes, err := small_stack_resize_bytes_non_zeroed(s, old_memory, old_size, size, alignment, loc) + bytes, err := small_stack_resize_bytes_non_zeroed(s, byte_slice(old_memory, old_size), size, alignment, loc) return raw_data(bytes), err } @(require_results) small_stack_resize_bytes_non_zeroed :: proc( s: ^Small_Stack, - old_memory: rawptr, - old_size: int, + old_data: []byte, size: int, alignment := DEFAULT_ALIGNMENT, loc := #caller_location, ) -> ([]byte, Allocator_Error) { + old_memory := raw_data(old_data) + old_size := len(old_data) alignment := alignment alignment = clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2) if old_memory == nil { @@ -946,9 +946,9 @@ small_stack_allocator_proc :: proc( case .Free_All: small_stack_free_all(s) case .Resize: - return small_stack_resize_bytes(s, old_memory, old_size, size, alignment, loc) + return small_stack_resize_bytes(s, byte_slice(old_memory, old_size), size, alignment, loc) case .Resize_Non_Zeroed: - return small_stack_resize_bytes_non_zeroed(s, old_memory, old_size, size, alignment, loc) + return small_stack_resize_bytes_non_zeroed(s, byte_slice(old_memory, old_size), size, alignment, loc) case .Query_Features: set := (^Allocator_Mode_Set)(old_memory) if set != nil { @@ -1137,24 +1137,23 @@ dynamic_arena_resize :: proc( size: int, loc := #caller_location, ) -> (rawptr, Allocator_Error) { - bytes, err := dynamic_arena_resize_bytes(a, old_memory, old_size, size, loc) + bytes, err := dynamic_arena_resize_bytes(a, byte_slice(old_memory, old_size), size, loc) return raw_data(bytes), err } @(require_results) dynamic_arena_resize_bytes :: proc( a: ^Dynamic_Arena, - old_memory: rawptr, - old_size: int, + old_data: []byte, size: int, loc := #caller_location, ) -> ([]byte, Allocator_Error) { - bytes, err := dynamic_arena_resize_bytes_non_zeroed(a, old_memory, old_size, size, loc) + bytes, err := dynamic_arena_resize_bytes_non_zeroed(a, old_data, size, loc) if bytes != nil { - if old_memory == nil { + if old_data == nil { zero_slice(bytes) - } else if size > old_size { - zero_slice(bytes[old_size:]) + } else if size > len(old_data) { + zero_slice(bytes[len(old_data):]) } } return bytes, err @@ -1168,18 +1167,19 @@ dynamic_arena_resize_non_zeroed :: proc( size: int, loc := #caller_location, ) -> (rawptr, Allocator_Error) { - bytes, err := dynamic_arena_resize_bytes_non_zeroed(a, old_memory, old_size, size, loc) + bytes, err := dynamic_arena_resize_bytes_non_zeroed(a, byte_slice(old_memory, old_size), size, loc) return raw_data(bytes), err } @(require_results) dynamic_arena_resize_bytes_non_zeroed :: proc( a: ^Dynamic_Arena, - old_memory: rawptr, - old_size: int, + old_data: []byte, size: int, loc := #caller_location, ) -> ([]byte, Allocator_Error) { + old_memory := raw_data(old_data) + old_size := len(old_data) if old_size >= size { return byte_slice(old_memory, size), nil } @@ -1210,9 +1210,9 @@ dynamic_arena_allocator_proc :: proc( case .Free_All: dynamic_arena_free_all(arena, loc) case .Resize: - return dynamic_arena_resize_bytes(arena, old_memory, old_size, size, loc) + return dynamic_arena_resize_bytes(arena, byte_slice(old_memory, old_size), size, loc) case .Resize_Non_Zeroed: - return dynamic_arena_resize_bytes_non_zeroed(arena, old_memory, old_size, size, loc) + return dynamic_arena_resize_bytes_non_zeroed(arena, byte_slice(old_memory, old_size), size, loc) case .Query_Features: set := (^Allocator_Mode_Set)(old_memory) if set != nil { From 7c9d2f61f58f9ccb730da335dbd6573ec6e844b4 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sat, 7 Sep 2024 15:16:20 +1100 Subject: [PATCH 18/72] [mem]: Update package documentation --- core/mem/doc.odin | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/core/mem/doc.odin b/core/mem/doc.odin index b152d0738..5e8bcce6a 100644 --- a/core/mem/doc.odin +++ b/core/mem/doc.odin @@ -1,5 +1,5 @@ /* -The `mem` package implements various allocators and provides utility functions +The `mem` package implements various allocators and provides utility procedures for dealing with memory, pointers and slices. The documentation below describes basic concepts, applicable to the `mem` @@ -24,7 +24,7 @@ access operation*. ## Allocators In C and C++ memory models, allocations of objects in memory are typically -treated individually with a generic allocator (The `malloc` function). Which in +treated individually with a generic allocator (The `malloc` procedure). Which in some scenarios can lead to poor cache utilization, slowdowns on individual objects' memory management and growing complexity of the code needing to keep track of the pointers and their lifetimes. @@ -45,7 +45,7 @@ allocators on a language level. Operations such as `new`, `free` and `delete` by default will use `context.allocator`, which can be overridden by the user. When an override -happens all called functions will inherit the new context and use the same +happens all called procedures will inherit the new context and use the same allocator. ## Alignment @@ -89,7 +89,17 @@ relatively expensive zero-filling of the buffer. The word `size` is used to denote the **size in bytes**. The word `length` is used to denote the count of objects. -Higher-level allocation functions follow the following naming scheme: +The allocation procedures use the following conventions: + +- If the name contains `alloc_bytes` or `resize_bytes`, then the procedure takes + in slice parameters and returns slices. +- If the procedure name contains `alloc` or `resize`, then the procedure takes + in a raw pointer and returns raw pointers. +- If the procedure name contains `free_bytes`, then the procedure takes in a + slice. +- If the procedure name contains `free`, then the procedure takes in a pointer. + +Higher-level allocation procedures follow the following naming scheme: - `new`: Allocates a single object - `free`: Free a single object (opposite of `new`) From 3a351ec407af42aeb82ac4fb51f9b633422f59fb Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sat, 7 Sep 2024 18:01:41 +1100 Subject: [PATCH 19/72] [mem]: Document mem.odin --- core/mem/mem.odin | 458 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 409 insertions(+), 49 deletions(-) diff --git a/core/mem/mem.odin b/core/mem/mem.odin index 9e47c9602..c17ab43a9 100644 --- a/core/mem/mem.odin +++ b/core/mem/mem.odin @@ -3,23 +3,99 @@ package mem import "base:runtime" import "base:intrinsics" -Byte :: runtime.Byte -Kilobyte :: runtime.Kilobyte -Megabyte :: runtime.Megabyte -Gigabyte :: runtime.Gigabyte -Terabyte :: runtime.Terabyte -Petabyte :: runtime.Petabyte -Exabyte :: runtime.Exabyte +/* +The size, in bytes, of a single byte. +This constant is equal to the value of `1`. +*/ +Byte :: runtime.Byte + +/* +The size, in bytes, of one kilobyte. + +This constant is equal to the amount of bytes in one kilobyte (also known as +kibibyte), which is equal to 1024 bytes. +*/ +Kilobyte :: runtime.Kilobyte + +/* +The size, in bytes, of one megabyte. + +This constant is equal to the amount of bytes in one megabyte (also known as +mebibyte), which is equal to 1024 kilobyte. +*/ +Megabyte :: runtime.Megabyte + +/* +The size, in bytes, of one gigabyte. + +This constant is equal to the amount of bytes in one gigabyte (also known as +gibiibyte), which is equal to 1024 megabytes. +*/ +Gigabyte :: runtime.Gigabyte + +/* +The size, in bytes, of one terabyte. + +This constant is equal to the amount of bytes in one terabyte (also known as +tebiibyte), which is equal to 1024 gigabytes. +*/ +Terabyte :: runtime.Terabyte + +/* +The size, in bytes, of one petabyte. + +This constant is equal to the amount of bytes in one petabyte (also known as +pebiibyte), which is equal to 1024 terabytes. +*/ +Petabyte :: runtime.Petabyte + +/* +The size, in bytes, of one exabyte. + +This constant is equal to the amount of bytes in one exabyte (also known as +exbibyte), which is equal to 1024 petabytes. +*/ +Exabyte :: runtime.Exabyte + +/* +Set each byte of a memory range to a specific value. + +This procedure copies value specified by the `value` parameter into each of the +`len` bytes of a memory range, located at address `data`. + +This procedure returns the pointer to `data`. +*/ set :: proc "contextless" (data: rawptr, value: byte, len: int) -> rawptr { return runtime.memset(data, i32(value), len) } +/* +Set each byte of a memory range to zero. + +This procedure copies the value `0` into the `len` bytes of a memory range, +starting at address `data`. + +This procedure returns the pointer to `data`. +*/ zero :: proc "contextless" (data: rawptr, len: int) -> rawptr { intrinsics.mem_zero(data, len) return data } +/* +Set each byte of a memory range to zero. + +This procedure copies the value `0` into the `len` bytes of a memory range, +starting at address `data`. + +This procedure returns the pointer to `data`. + +Unlike the `zero()` procedure, which can be optimized away or reordered by the +compiler under certain circumstances, `zero_explicit()` procedure can not be +optimized away or reordered with other memory access operations, and the +compiler assumes volatile semantics of the memory. +*/ zero_explicit :: proc "contextless" (data: rawptr, len: int) -> rawptr { // This routine tries to avoid the compiler optimizing away the call, // so that it is always executed. It is intended to provided @@ -30,26 +106,82 @@ zero_explicit :: proc "contextless" (data: rawptr, len: int) -> rawptr { return data } +/* +Zero-fill the memory of an object. + +This procedure sets each byte of the object pointed to by the pointer `item` +to zero, and returns the pointer to `item`. +*/ zero_item :: proc "contextless" (item: $P/^$T) -> P { intrinsics.mem_zero(item, size_of(T)) return item } +/* +Zero-fill the memory of the slice. + +This procedure sets each byte of the slice pointed to by the slice `data` +to zero, and returns the slice `data`. +*/ zero_slice :: proc "contextless" (data: $T/[]$E) -> T { zero(raw_data(data), size_of(E)*len(data)) return data } +/* +Copy bytes from one memory range to another. + +This procedure copies `len` bytes of data, from the memory range pointed to by +the `src` pointer into the memory range pointed to by the `dst` pointer, and +returns the `dst` pointer. +*/ copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr { intrinsics.mem_copy(dst, src, len) return dst } +/* +Copy bytes between two non-overlapping memory ranges. + +This procedure copies `len` bytes of data, from the memory range pointed to by +the `src` pointer into the memory range pointed to by the `dst` pointer, and +returns the `dst` pointer. + +This is a slightly more optimized version of the `copy` procedure that requires +that memory ranges specified by the parameters to this procedure are not +overlapping. If the memory ranges specified by `dst` and `src` pointers overlap, +the behavior of this function may be unpredictable. +*/ copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr { intrinsics.mem_copy_non_overlapping(dst, src, len) return dst } +/* +Compare two memory ranges defined by slices. + +This procedure performs a byte-by-byte comparison between memory ranges +specified by slices `a` and `b`, and returns a value, specifying their relative +ordering. + +If the return value is: +- Equal to `-1`, then `a` is "smaller" than `b`. +- Equal to `+1`, then `a` is "bigger" than `b`. +- Equal to `0`, then `a` and `b` are equal. + +The comparison is performed as follows: +1. Each byte, upto `min(len(a), len(b))` bytes is compared between `a` and `b`. + - If the byte in slice `a` is smaller than a byte in slice `b`, then comparison + stops and this procedure returns `-1`. + - If the byte in slice `a` is bigger than a byte in slice `b`, then comparison + stops and this procedure returns `+1`. + - Otherwise the comparison continues until `min(len(a), len(b))` are compared. +2. If all the bytes in the range are equal, then the lengths of the slices are + compared. + - If the length of slice `a` is smaller than the length of slice `b`, then `-1` is returned. + - If the length of slice `b` is smaller than the length of slice `b`, then `+1` is returned. + - Otherwise `0` is returned. +*/ @(require_results) compare :: proc "contextless" (a, b: []byte) -> int { res := compare_byte_ptrs(raw_data(a), raw_data(b), min(len(a), len(b))) @@ -61,16 +193,89 @@ compare :: proc "contextless" (a, b: []byte) -> int { return res } +/* +Compare two memory ranges defined by byte pointers. + +This procedure performs a byte-by-byte comparison between memory ranges of size +`n` located at addresses `a` and `b`, and returns a value, specifying their relative +ordering. + +If the return value is: +- Equal to `-1`, then `a` is "smaller" than `b`. +- Equal to `+1`, then `a` is "bigger" than `b`. +- Equal to `0`, then `a` and `b` are equal. + +The comparison is performed as follows: +1. Each byte, upto `n` bytes is compared between `a` and `b`. + - If the byte in `a` is smaller than a byte in `b`, then comparison stops + and this procedure returns `-1`. + - If the byte in `a` is bigger than a byte in `b`, then comparison stops + and this procedure returns `+1`. + - Otherwise the comparison continues until `n` bytes are compared. +2. If all the bytes in the range are equal, this procedure returns `0`. +*/ @(require_results) compare_byte_ptrs :: proc "contextless" (a, b: ^byte, n: int) -> int #no_bounds_check { return runtime.memory_compare(a, b, n) } +/* +Compare two memory ranges defined by pointers. + +This procedure performs a byte-by-byte comparison between memory ranges of size +`n` located at addresses `a` and `b`, and returns a value, specifying their relative +ordering. + +If the return value is: +- Equal to `-1`, then `a` is "smaller" than `b`. +- Equal to `+1`, then `a` is "bigger" than `b`. +- Equal to `0`, then `a` and `b` are equal. + +The comparison is performed as follows: +1. Each byte, upto `n` bytes is compared between `a` and `b`. + - If the byte in `a` is smaller than a byte in `b`, then comparison stops + and this procedure returns `-1`. + - If the byte in `a` is bigger than a byte in `b`, then comparison stops + and this procedure returns `+1`. + - Otherwise the comparison continues until `n` bytes are compared. +2. If all the bytes in the range are equal, this procedure returns `0`. +*/ +@(require_results) +compare_ptrs :: proc "contextless" (a, b: rawptr, n: int) -> int { + return compare_byte_ptrs((^byte)(a), (^byte)(b), n) +} + +/* +Check whether two objects are equal on binary level. + +This procedure checks whether the memory ranges occupied by objects `a` and +`b` are equal. See `compare_byte_ptrs()` for how this comparison is done. +*/ +@(require_results) +simple_equal :: proc "contextless" (a, b: $T) -> bool where intrinsics.type_is_simple_compare(T) { + a, b := a, b + return compare_byte_ptrs((^byte)(&a), (^byte)(&b), size_of(T)) == 0 +} + +/* +Check if the memory range defined by a slice is zero-filled. + +This procedure checks whether every byte, pointed to by the slice, specified +by the parameter `data`, is zero. If all bytes of the slice are zero, this +procedure returns `true`. Otherwise this procedure returns `false`. +*/ @(require_results) check_zero :: proc(data: []byte) -> bool { return check_zero_ptr(raw_data(data), len(data)) } +/* +Check if the memory range defined defined by a pointer is zero-filled. + +This procedure checks whether each of the `len` bytes, starting at address +`ptr` is zero. If all bytes of this range are zero, this procedure returns +`true`. Otherwise this procedure returns `false`. +*/ @(require_results) check_zero_ptr :: proc(ptr: rawptr, len: int) -> bool { switch { @@ -85,58 +290,99 @@ check_zero_ptr :: proc(ptr: rawptr, len: int) -> bool { case 4: return intrinsics.unaligned_load((^u32)(ptr)) == 0 case 8: return intrinsics.unaligned_load((^u64)(ptr)) == 0 } - start := uintptr(ptr) start_aligned := align_forward_uintptr(start, align_of(uintptr)) end := start + uintptr(len) end_aligned := align_backward_uintptr(end, align_of(uintptr)) - for b in start.. bool where intrinsics.type_is_simple_compare(T) { - a, b := a, b - return compare_byte_ptrs((^byte)(&a), (^byte)(&b), size_of(T)) == 0 -} +/* +Offset a given pointer by a given amount. -@(require_results) -compare_ptrs :: proc "contextless" (a, b: rawptr, n: int) -> int { - return compare_byte_ptrs((^byte)(a), (^byte)(b), n) -} +This procedure offsets the pointer `ptr` to an object of type `T`, by the amount +of bytes specified by `offset*size_of(T)`, and returns the pointer `ptr`. +**Note**: Prefer to use multipointer types, if possible. +*/ ptr_offset :: intrinsics.ptr_offset +/* +Offset a given pointer by a given amount backwards. + +This procedure offsets the pointer `ptr` to an object of type `T`, by the amount +of bytes specified by `offset*size_of(T)` in the negative direction, and +returns the pointer `ptr`. +*/ ptr_sub :: intrinsics.ptr_sub +/* +Construct a slice from pointer and length. + +This procedure creates a slice, that points to `len` amount of objects located +at an address, specified by `ptr`. +*/ @(require_results) slice_ptr :: proc "contextless" (ptr: ^$T, len: int) -> []T { return ([^]T)(ptr)[:len] } +/* +Construct a byte slice from raw pointer and length. + +This procedure creates a byte slice, that points to `len` amount of bytes +located at an address specified by `data`. +*/ @(require_results) byte_slice :: #force_inline proc "contextless" (data: rawptr, #any_int len: int) -> []byte { return ([^]u8)(data)[:max(len, 0)] } +/* +Create a byte slice from pointer and length. + +This procedure creates a byte slice, pointing to `len` objects, starting from +the address specified by `ptr`. +*/ +@(require_results) +ptr_to_bytes :: proc "contextless" (ptr: ^$T, len := 1) -> []byte { + return transmute([]byte)Raw_Slice{ptr, len*size_of(T)} +} + +/* +Obtain the slice, pointing to the contents of `any`. + +This procedure returns the slice, pointing to the contents of the specified +value of the `any` type. +*/ +@(require_results) +any_to_bytes :: proc "contextless" (val: any) -> []byte { + ti := type_info_of(val.id) + size := ti != nil ? ti.size : 0 + return transmute([]byte)Raw_Slice{val.data, size} +} + +/* +Obtain a byte slice from any slice. + +This procedure returns a slice, that points to the same bytes as the slice, +specified by `slice` and returns the resulting byte slice. +*/ @(require_results) slice_to_bytes :: proc "contextless" (slice: $E/[]$T) -> []byte { s := transmute(Raw_Slice)slice @@ -144,6 +390,15 @@ slice_to_bytes :: proc "contextless" (slice: $E/[]$T) -> []byte { return transmute([]byte)s } +/* +Transmute slice to a different type. + +This procedure performs an operation similar to transmute, returning a slice of +type `T` that points to the same bytes as the slice specified by `slice` +parameter. Unlike plain transmute operation, this procedure adjusts the length +of the resulting slice, such that the resulting slice points to the correct +amount of objects to cover the memory region pointed to by `slice`. +*/ @(require_results) slice_data_cast :: proc "contextless" ($T: typeid/[]$A, slice: $S/[]$B) -> T { when size_of(A) == 0 || size_of(B) == 0 { @@ -155,12 +410,25 @@ slice_data_cast :: proc "contextless" ($T: typeid/[]$A, slice: $S/[]$B) -> T { } } +/* +Obtain data and length of a slice. + +This procedure returns the pointer to the start of the memory region pointed to +by slice `slice` and the length of the slice. +*/ @(require_results) slice_to_components :: proc "contextless" (slice: $E/[]$T) -> (data: ^T, len: int) { s := transmute(Raw_Slice)slice return (^T)(s.data), s.len } +/* +Create a dynamic array from slice. + +This procedure creates a dynamic array, using slice `backing` as the backing +buffer for the dynamic array. The resulting dynamic array can not grow beyond +the size of the specified slice. +*/ @(require_results) buffer_from_slice :: proc "contextless" (backing: $T/[]$E) -> [dynamic]E { return transmute([dynamic]E)Raw_Dynamic_Array{ @@ -174,19 +442,12 @@ buffer_from_slice :: proc "contextless" (backing: $T/[]$E) -> [dynamic]E { } } -@(require_results) -ptr_to_bytes :: proc "contextless" (ptr: ^$T, len := 1) -> []byte { - return transmute([]byte)Raw_Slice{ptr, len*size_of(T)} -} - -@(require_results) -any_to_bytes :: proc "contextless" (val: any) -> []byte { - ti := type_info_of(val.id) - size := ti != nil ? ti.size : 0 - return transmute([]byte)Raw_Slice{val.data, size} -} - +/* +Check whether a number is a power of two. +This procedure checks whether a given pointer-sized unsigned integer contains +a power-of-two value. +*/ @(require_results) is_power_of_two :: proc "contextless" (x: uintptr) -> bool { if x <= 0 { @@ -195,67 +456,156 @@ is_power_of_two :: proc "contextless" (x: uintptr) -> bool { return (x & (x-1)) == 0 } +/* +Align uintptr forward. + +This procedure returns the next address after `ptr`, that is located on the +alignment boundary specified by `align`. If `ptr` is already aligned to `align` +bytes, `ptr` is returned. + +The specified alignment must be a power of 2. +*/ +@(require_results) +align_forward_uintptr :: proc(ptr, align: uintptr) -> uintptr { + assert(is_power_of_two(align)) + return (p + align-1) & ~(align-1) +} + +/* +Align pointer forward. + +This procedure returns the next address after `ptr`, that is located on the +alignment boundary specified by `align`. If `ptr` is already aligned to `align` +bytes, `ptr` is returned. + +The specified alignment must be a power of 2. +*/ @(require_results) align_forward :: proc(ptr: rawptr, align: uintptr) -> rawptr { return rawptr(align_forward_uintptr(uintptr(ptr), align)) } -@(require_results) -align_forward_uintptr :: proc(ptr, align: uintptr) -> uintptr { - assert(is_power_of_two(align)) +/* +Align int forward. - p := ptr - modulo := p & (align-1) - if modulo != 0 { - p += align - modulo - } - return p -} +This procedure returns the next address after `ptr`, that is located on the +alignment boundary specified by `align`. If `ptr` is already aligned to `align` +bytes, `ptr` is returned. +The specified alignment must be a power of 2. +*/ @(require_results) align_forward_int :: proc(ptr, align: int) -> int { return int(align_forward_uintptr(uintptr(ptr), uintptr(align))) } +/* +Align uint forward. + +This procedure returns the next address after `ptr`, that is located on the +alignment boundary specified by `align`. If `ptr` is already aligned to `align` +bytes, `ptr` is returned. + +The specified alignment must be a power of 2. +*/ @(require_results) align_forward_uint :: proc(ptr, align: uint) -> uint { return uint(align_forward_uintptr(uintptr(ptr), uintptr(align))) } +/* +Align uintptr backwards. + +This procedure returns the previous address before `ptr`, that is located on the +alignment boundary specified by `align`. If `ptr` is already aligned to `align` +bytes, `ptr` is returned. + +The specified alignment must be a power of 2. +*/ +@(require_results) +align_backward_uintptr :: proc(ptr, align: uintptr) -> uintptr { + assert(is_power_of_two(align)) + return ptr & ~(align-1) +} + +/* +Align rawptr backwards. + +This procedure returns the previous address before `ptr`, that is located on the +alignment boundary specified by `align`. If `ptr` is already aligned to `align` +bytes, `ptr` is returned. + +The specified alignment must be a power of 2. +*/ @(require_results) align_backward :: proc(ptr: rawptr, align: uintptr) -> rawptr { return rawptr(align_backward_uintptr(uintptr(ptr), align)) } -@(require_results) -align_backward_uintptr :: proc(ptr, align: uintptr) -> uintptr { - return align_forward_uintptr(ptr - align + 1, align) -} +/* +Align int backwards. +This procedure returns the previous address before `ptr`, that is located on the +alignment boundary specified by `align`. If `ptr` is already aligned to `align` +bytes, `ptr` is returned. + +The specified alignment must be a power of 2. +*/ @(require_results) align_backward_int :: proc(ptr, align: int) -> int { return int(align_backward_uintptr(uintptr(ptr), uintptr(align))) } +/* +Align uint backwards. + +This procedure returns the previous address before `ptr`, that is located on the +alignment boundary specified by `align`. If `ptr` is already aligned to `align` +bytes, `ptr` is returned. + +The specified alignment must be a power of 2. +*/ @(require_results) align_backward_uint :: proc(ptr, align: uint) -> uint { return uint(align_backward_uintptr(uintptr(ptr), uintptr(align))) } +/* +Create a context with a given allocator. + +This procedure returns a copy of the current context with the allocator replaced +by the allocator `a`. +*/ @(require_results) context_from_allocator :: proc(a: Allocator) -> type_of(context) { context.allocator = a return context } +/* +Copy the value from a pointer into a value. + +This procedure copies the object of type `T` pointed to by the pointer `ptr` +into a new stack-allocated value and returns that value. +*/ @(require_results) reinterpret_copy :: proc "contextless" ($T: typeid, ptr: rawptr) -> (value: T) { copy(&value, ptr, size_of(T)) return } +/* +Dynamic array with a fixed capacity buffer. + +This type represents dynamic arrays with a fixed-size backing buffer. Upon +allocating memory beyond reaching the maximum capacity, allocations from fixed +byte buffers return `nil` and no error. +*/ Fixed_Byte_Buffer :: distinct [dynamic]byte +/* +Create a fixed byte buffer from a slice. +*/ @(require_results) make_fixed_byte_buffer :: proc "contextless" (backing: []byte) -> Fixed_Byte_Buffer { s := transmute(Raw_Slice)backing @@ -270,12 +620,24 @@ make_fixed_byte_buffer :: proc "contextless" (backing: []byte) -> Fixed_Byte_Buf return transmute(Fixed_Byte_Buffer)d } +/* +General-purpose align formula. + +This procedure is equivalent to `align_forward`, but it does not require the +alignment to be a power of two. +*/ @(require_results) align_formula :: proc "contextless" (size, align: int) -> int { result := size + align-1 return result - result%align } +/* +Calculate the padding after the pointer with a header. + +This procedure returns the next address, following `ptr` and `header_size` +bytes of space that is aligned to a boundary specified by `align`. +*/ @(require_results) calc_padding_with_header :: proc "contextless" (ptr: uintptr, align: uintptr, header_size: int) -> int { p, a := ptr, align @@ -287,14 +649,12 @@ calc_padding_with_header :: proc "contextless" (ptr: uintptr, align: uintptr, he needed_space := uintptr(header_size) if padding < needed_space { needed_space -= padding - if needed_space & (a-1) > 0 { padding += align * (1+(needed_space/align)) } else { padding += align * (needed_space/align) } } - return int(padding) } From f8cd13767e360092fb4b7df02508f2884cf82da5 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sat, 7 Sep 2024 18:08:11 +1100 Subject: [PATCH 20/72] [mem]: Fix the issue with unbranched version of ptr align --- core/mem/mem.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/mem/mem.odin b/core/mem/mem.odin index c17ab43a9..2212ee171 100644 --- a/core/mem/mem.odin +++ b/core/mem/mem.odin @@ -468,7 +468,7 @@ The specified alignment must be a power of 2. @(require_results) align_forward_uintptr :: proc(ptr, align: uintptr) -> uintptr { assert(is_power_of_two(align)) - return (p + align-1) & ~(align-1) + return (ptr + align-1) & ~(align-1) } /* From 1842cd6297ae60fba46b43758d5ffc655ef2e58c Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sun, 8 Sep 2024 00:09:18 +1100 Subject: [PATCH 21/72] Fix typo Co-authored-by: FourteenBrush <74827262+FourteenBrush@users.noreply.github.com> --- core/mem/mem.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/mem/mem.odin b/core/mem/mem.odin index 2212ee171..0554cee23 100644 --- a/core/mem/mem.odin +++ b/core/mem/mem.odin @@ -98,7 +98,7 @@ compiler assumes volatile semantics of the memory. */ zero_explicit :: proc "contextless" (data: rawptr, len: int) -> rawptr { // This routine tries to avoid the compiler optimizing away the call, - // so that it is always executed. It is intended to provided + // so that it is always executed. It is intended to provide // equivalent semantics to those provided by the C11 Annex K 3.7.4.1 // memset_s call. intrinsics.mem_zero_volatile(data, len) // Use the volatile mem_zero From c719a86312813c1eed8793aafe6f98eb5ed1b3e0 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sun, 8 Sep 2024 10:58:40 +1100 Subject: [PATCH 22/72] [mem]: Document alloc.odin --- core/mem/alloc.odin | 577 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 530 insertions(+), 47 deletions(-) diff --git a/core/mem/alloc.odin b/core/mem/alloc.odin index 558e810e3..dbf9af9b2 100644 --- a/core/mem/alloc.odin +++ b/core/mem/alloc.odin @@ -2,66 +2,272 @@ package mem import "base:runtime" -// NOTE(bill, 2019-12-31): These are defined in `package runtime` as they are used in the `context`. This is to prevent an import definition cycle. +//NOTE(bill, 2019-12-31): These are defined in `package runtime` as they are used in the `context`. This is to prevent an import definition cycle. + +/* +A request to allocator procedure. + +This type represents a type of allocation request made to an allocator +procedure. There is one allocator procedure per allocator, and this value is +used to discriminate between different functions of the allocator. + +The type is defined as follows: + + Allocator_Mode :: enum byte { + Alloc, + Alloc_Non_Zeroed, + Free, + Free_All, + Resize, + Resize_Non_Zeroed, + Query_Features, + } + +Depending on which value is used, the allocator procedure will perform different +functions: + +- `Alloc`: Allocates a memory region with a given `size` and `alignment`. +- `Alloc_Non_Zeroed`: Same as `Alloc` without explicit zero-initialization of + the memory region. +- `Free`: Free a memory region located at address `ptr` with a given `size`. +- `Free_All`: Free all memory allocated using this allocator. +- `Resize`: Resize a memory region located at address `old_ptr` with size + `old_size` to be `size` bytes in length and have the specified `alignment`, + in case a re-alllocation occurs. +- `Resize_Non_Zeroed`: Same as `Resize`, without explicit zero-initialization. + +*/ Allocator_Mode :: runtime.Allocator_Mode -/* -Allocator_Mode :: enum byte { - Alloc, - Free, - Free_All, - Resize, - Query_Features, - Alloc_Non_Zeroed, - Resize_Non_Zeroed, -} -*/ +/* +A set of allocator features. + +This type represents values that contain a set of features an allocator has. +Currently the type is defined as follows: + + Allocator_Mode_Set :: distinct bit_set[Allocator_Mode]; +*/ Allocator_Mode_Set :: runtime.Allocator_Mode_Set -/* -Allocator_Mode_Set :: distinct bit_set[Allocator_Mode]; -*/ +/* +Allocator information. + +This type represents information about a given allocator at a specific point +in time. Currently the type is defined as follows: + + Allocator_Query_Info :: struct { + pointer: rawptr, + size: Maybe(int), + alignment: Maybe(int), + } + +- `pointer`: Pointer to a backing buffer. +- `size`: Size of the backing buffer. +- `alignment`: The allocator's alignment. + +If not applicable, any of these fields may be `nil`. +*/ Allocator_Query_Info :: runtime.Allocator_Query_Info -/* -Allocator_Query_Info :: struct { - pointer: rawptr, - size: Maybe(int), - alignment: Maybe(int), -} -*/ -Allocator_Error :: runtime.Allocator_Error /* -Allocator_Error :: enum byte { - None = 0, - Out_Of_Memory = 1, - Invalid_Pointer = 2, - Invalid_Argument = 3, - Mode_Not_Implemented = 4, -} +An allocation request error. + +This type represents error values the allocators may return upon requests. + + Allocator_Error :: enum byte { + None = 0, + Out_Of_Memory = 1, + Invalid_Pointer = 2, + Invalid_Argument = 3, + Mode_Not_Implemented = 4, + } + +The meaning of the errors is as follows: + +- `None`: No error. +- `Out_Of_Memory`: Either: + 1. The allocator has ran out of the backing buffer, or the requested + allocation size is too large to fit into a backing buffer. + 2. The operating system error during memory allocation. + 3. The backing allocator was used to allocate a new backing buffer and the + backing allocator returned Out_Of_Memory. +- `Invalid_Pointer`: The pointer referring to a memory region does not belong + to any of the allocators backing buffers or does not point to a valid start + of an allocation made in that allocator. +- `Invalid_Argument`: Can occur if one of the arguments makes it impossible to + satisfy a request (i.e. having alignment larger than the backing buffer + of the allocation). +- `Mode_Not_Implemented`: The allocator does not support the specified + operation. For example, an arena does not support freeing individual + allocations. +*/ +Allocator_Error :: runtime.Allocator_Error + +/* +The allocator procedure. + +This type represents allocation procedures. An allocation procedure is a single +procedure, implementing all allocator functions such as allocating the memory, +freeing the memory, etc. + +Currently the type is defined as follows: + + Allocator_Proc :: #type proc( + allocator_data: rawptr, + mode: Allocator_Mode, + size: int, + alignment: int, + old_memory: rawptr, + old_size: int, + location: Source_Code_Location = #caller_location, + ) -> ([]byte, Allocator_Error); + +The function of this procedure and the meaning of parameters depends on the +value of the `mode` parameter. + +## 1. `.Alloc`, `.Alloc_Non_Zeroed` + +Allocates a memory region of size `size`, aligned on a boundary specified by +`alignment`. + +**Inputs**: +- `allocator_data`: Pointer to the allocator data. +- `mode`: `.Alloc` or `.Alloc_Non_Zeroed`. +- `size`: The desired size of the memory region. +- `alignment`: The desired alignmnet of the allocation. +- `old_memory`: Unused, should be `nil`. +- `old_size`: Unused, should be 0. + +**Returns**: +1. The memory region, if allocated successfully, or `nil` otherwise. +2. An error, if allocation failed. + +**Note**: Some allocators may return `nil`, even if no error is returned. +Always check both the error and the allocated buffer. + +Same as `.Alloc`. + +## 2. `Free` + +Frees a memory region located at the address specified by `old_memory`. If the +allocator does not track sizes of allocations, the size should be specified in +the `old_size` parameter. + +**Inputs**: +- `allocator_data`: Pointer to the allocator data. +- `mode`: `.Free`. +- `size`: Unused, should be 0. +- `alignment`: Unused, should be 0. +- `old_memory`: Pointer to the memory region to free. +- `old_size`: The size of the memory region to free. This parameter is optional + if the allocator keeps track of the sizes of allocations. + +**Returns**: +1. `nil` +2. Error, if freeing failed. + +## 3. `Free_All` + +Frees all allocations, associated with the allocator, making it available for +further allocations using the same backing buffers. + +**Inputs**: +- `allocator_data`: Pointer to the allocator data. +- `mode`: `.Free_All`. +- `size`: Unused, should be 0. +- `alignment`: Unused, should be 0. +- `old_memory`: Unused, should be `nil`. +- `old_size`: Unused, should be `0`. + +**Returns**: +1. `nil`. +2. Error, if freeing failed. + +## 4. `Resize`, `Resize_Non_Zeroed` + +Resizes the memory region, of the size `old_size` located at the address +specified by `old_memory` to have the new size `size`. The slice of the new +memory region is returned from the procedure. The allocator may attempt to +keep the new memory region at the same address as the previous allocation, +however no such guarantee is made. Do not assume the new memory region will +be at the same address as the old memory region. + +If `old_memory` pointer is `nil`, this function acts just like `.Alloc` or +`.Alloc_Non_Zeroed`, using `size` and `alignment` to allocate a new memory +region. + +If `new_size` is `nil`, the procedure acts just like `.Free`, freeing the +memory region `old_size` bytes in length, located at the address specified by +`old_memory`. + +**Inputs**: +- `allocator_data`: Pointer to the allocator data. +- `mode`: `.Resize` or `.Resize_All`. +- `size`: The desired new size of the memory region. +- `alignment`: The alignment of the new memory region, if its allocated +- `old_memory`: The pointer to the memory region to resize. +- `old_size`: The size of the memory region to resize. If the allocator + keeps track of the sizes of allocations, this parameter is optional. + +**Returns**: +1. The slice of the memory region after resize operation, if successfull, + `nil` otherwise. +2. An error, if the resize failed. + +**Note**: Some allocators may return `nil`, even if no error is returned. +Always check both the error and the allocated buffer. */ Allocator_Proc :: runtime.Allocator_Proc -/* -Allocator_Proc :: #type proc(allocator_data: rawptr, mode: Allocator_Mode, - size, alignment: int, - old_memory: rawptr, old_size: int, location: Source_Code_Location = #caller_location) -> ([]byte, Allocator_Error); -*/ +/* +Allocator. + +This type represents generic interface for all allocators. Currently this type +is defined as follows: + + Allocator :: struct { + procedure: Allocator_Proc, + data: rawptr, + } + +- `procedure`: Pointer to the allocation procedure. +- `data`: Pointer to the allocator data. +*/ Allocator :: runtime.Allocator -/* -Allocator :: struct { - procedure: Allocator_Proc, - data: rawptr, -} -*/ +/* +Default alignment. + +This value is the default alignment for all platforms that is used, if the +alignment is not specified explicitly. +*/ DEFAULT_ALIGNMENT :: 2*align_of(rawptr) +/* +Default page size. + +This value is the default page size for the current platform. +*/ DEFAULT_PAGE_SIZE :: 64 * 1024 when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 else 16 * 1024 when ODIN_OS == .Darwin && ODIN_ARCH == .arm64 else 4 * 1024 +/* +Allocate memory. + +This function allocates `size` bytes of memory, aligned to a boundary specified +by `alignment` using the allocator specified by `allocator`. + +**Inputs**: +- `size`: The desired size of the allocated memory region. +- `alignment`: The desired alignment of the allocated memory region. +- `allocator`: The allocator to allocate from. + +**Returns**: +1. Pointer to the allocated memory, or `nil` if allocation failed. +2. Error, if the allocation failed. +*/ @(require_results) alloc :: proc( size: int, @@ -73,6 +279,21 @@ alloc :: proc( return raw_data(data), err } +/* +Allocate memory. + +This function allocates `size` bytes of memory, aligned to a boundary specified +by `alignment` using the allocator specified by `allocator`. + +**Inputs**: +- `size`: The desired size of the allocated memory region. +- `alignment`: The desired alignment of the allocated memory region. +- `allocator`: The allocator to allocate from. + +**Returns**: +1. Slice of the allocated memory region, or `nil` if allocation failed. +2. Error, if the allocation failed. +*/ @(require_results) alloc_bytes :: proc( size: int, @@ -83,6 +304,22 @@ alloc_bytes :: proc( return runtime.mem_alloc(size, alignment, allocator, loc) } +/* +Allocate non-zeroed memory. + +This function allocates `size` bytes of memory, aligned to a boundary specified +by `alignment` using the allocator specified by `allocator`. This procedure +does not explicitly zero-initialize allocated memory region. + +**Inputs**: +- `size`: The desired size of the allocated memory region. +- `alignment`: The desired alignment of the allocated memory region. +- `allocator`: The allocator to allocate from. + +**Returns**: +1. Slice of the allocated memory region, or `nil` if allocation failed. +2. Error, if the allocation failed. +*/ @(require_results) alloc_bytes_non_zeroed :: proc( size: int, @@ -93,6 +330,16 @@ alloc_bytes_non_zeroed :: proc( return runtime.mem_alloc_non_zeroed(size, alignment, allocator, loc) } +/* +Free memory. + +This procedure frees memory region located at the address, specified by `ptr`, +allocated from the allocator specified by `allocator`. + +**Inputs**: +- `ptr`: Pointer to the memory region to free. +- `allocator`: The allocator to free to. +*/ free :: proc( ptr: rawptr, allocator := context.allocator, @@ -101,15 +348,42 @@ free :: proc( return runtime.mem_free(ptr, allocator, loc) } +/* +Free a memory region. + +This procedure frees `size` bytes of memory region located at the address, +specified by `ptr`, allocated from the allocator specified by `allocator`. + +**Inputs**: +- `ptr`: Pointer to the memory region to free. +- `size`: The size of the memory region to free. +- `allocator`: The allocator to free to. + +**Returns**: +- The error, if freeing failed. +*/ free_with_size :: proc( ptr: rawptr, - byte_count: int, + size: int, allocator := context.allocator, loc := #caller_location, ) -> Allocator_Error { - return runtime.mem_free_with_size(ptr, byte_count, allocator, loc) + return runtime.mem_free_with_size(ptr, size, allocator, loc) } +/* +Free a memory region. + +This procedure frees memory region, specified by `bytes`, allocated from the +allocator specified by `allocator`. + +**Inputs**: +- `bytes`: The memory region to free. +- `allocator`: The allocator to free to. + +**Returns**: +- The error, if freeing failed. +*/ free_bytes :: proc( bytes: []byte, allocator := context.allocator, @@ -118,10 +392,45 @@ free_bytes :: proc( return runtime.mem_free_bytes(bytes, allocator, loc) } +/* +Free all allocations. + +This procedure frees all allocations made on the allocator specified by +`allocator` to that allocator, making it available for further allocations. +*/ free_all :: proc(allocator := context.allocator, loc := #caller_location) -> Allocator_Error { return runtime.mem_free_all(allocator, loc) } +/* +Resize a memory region. + +This procedure resizes a memory region, `old_size` bytes in size, located at +the address specified by `ptr`, such that it has a new size, specified by +`new_size` and and is aligned on a boundary specified by `alignment`. + +If the `ptr` parameter is `nil`, `resize()` acts just like `alloc`, allocating +`new_size` bytes, aligned on a boundary specified by `alignment`. + +If the `new_size` parameter is `0`, `resize()` acts just like `free`, freeing +the memory region `old_size` bytes in length, located at the address specified +by `ptr`. + +**Inputs**: +- `ptr`: Pointer to the memory region to resize. +- `old_size`: Size of the memory region to resize. +- `new_size`: The desired size of the resized memory region. +- `alignment`: The desired alignment of the resized memory region. +- `allocator`: The owner of the memory region to resize. + +**Returns**: +1. The pointer to the resized memory region, if successfull, `nil` otherwise. +2. Error, if resize failed. + +**Note**: The `alignment` parameter is used to preserve the original alignment +of the allocation, if `resize()` needs to relocate the memory region. Do not +use `resize()` to change the alignment of the allocated memory region. +*/ @(require_results) resize :: proc( ptr: rawptr, @@ -135,6 +444,33 @@ resize :: proc( return raw_data(data), err } +/* +Resize a memory region. + +This procedure resizes a memory region, specified by `old_data`, such that it +has a new size, specified by `new_size` and and is aligned on a boundary +specified by `alignment`. + +If the `old_data` parameter is `nil`, `resize()` acts just like `alloc`, +allocating `new_size` bytes, aligned on a boundary specified by `alignment`. + +If the `new_size` parameter is `0`, `resize()` acts just like `free`, freeing +the memory region specified by `old_data`. + +**Inputs**: +- `old_data`: Pointer to the memory region to resize. +- `new_size`: The desired size of the resized memory region. +- `alignment`: The desired alignment of the resized memory region. +- `allocator`: The owner of the memory region to resize. + +**Returns**: +1. The resized memory region, if successfull, `nil` otherwise. +2. Error, if resize failed. + +**Note**: The `alignment` parameter is used to preserve the original alignment +of the allocation, if `resize()` needs to relocate the memory region. Do not +use `resize()` to change the alignment of the allocated memory region. +*/ @(require_results) resize_bytes :: proc( old_data: []byte, @@ -146,6 +482,9 @@ resize_bytes :: proc( return runtime.mem_resize(raw_data(old_data), len(old_data), new_size, alignment, allocator, loc) } +/* +Query allocator features. +*/ @(require_results) query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: Allocator_Mode_Set) { if allocator.procedure != nil { @@ -155,6 +494,9 @@ query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: A return nil } +/* +Query allocator information. +*/ @(require_results) query_info :: proc( pointer: rawptr, @@ -168,6 +510,9 @@ query_info :: proc( return } +/* +Free a string. +*/ delete_string :: proc( str: string, allocator := context.allocator, @@ -176,6 +521,9 @@ delete_string :: proc( return runtime.delete_string(str, allocator, loc) } +/* +Free a cstring. +*/ delete_cstring :: proc( str: cstring, allocator := context.allocator, @@ -184,6 +532,9 @@ delete_cstring :: proc( return runtime.delete_cstring(str, allocator, loc) } +/* +Free a dynamic array. +*/ delete_dynamic_array :: proc( array: $T/[dynamic]$E, loc := #caller_location, @@ -191,6 +542,9 @@ delete_dynamic_array :: proc( return runtime.delete_dynamic_array(array, loc) } +/* +Free a slice. +*/ delete_slice :: proc( array: $T/[]$E, allocator := context.allocator, @@ -199,6 +553,9 @@ delete_slice :: proc( return runtime.delete_slice(array, allocator, loc) } +/* +Free a map. +*/ delete_map :: proc( m: $T/map[$K]$V, loc := #caller_location, @@ -206,6 +563,9 @@ delete_map :: proc( return runtime.delete_map(m, loc) } +/* +Free. +*/ delete :: proc{ delete_string, delete_cstring, @@ -214,6 +574,13 @@ delete :: proc{ delete_map, } +/* +Allocate a new object. + +This procedure allocates a new object of type `T` using an allocator specified +by `allocator`, and returns a pointer to the allocated object, if allocated +successfully, or `nil` otherwise. +*/ @(require_results) new :: proc( $T: typeid, @@ -223,6 +590,14 @@ new :: proc( return new_aligned(T, align_of(T), allocator, loc) } +/* +Allocate a new object with alignment. + +This procedure allocates a new object of type `T` using an allocator specified +by `allocator`, and returns a pointer, aligned on a boundary specified by +`alignment` to the allocated object, if allocated successfully, or `nil` +otherwise. +*/ @(require_results) new_aligned :: proc( $T: typeid, @@ -233,6 +608,14 @@ new_aligned :: proc( return runtime.new_aligned(T, alignment, allocator, loc) } +/* +Allocate a new object and initialize it with a value. + +This procedure allocates a new object of type `T` using an allocator specified +by `allocator`, and returns a pointer, aligned on a boundary specified by +`alignment` to the allocated object, if allocated successfully, or `nil` +otherwise. The allocated object is initialized with `data`. +*/ @(require_results) new_clone :: proc( data: $T, @@ -242,6 +625,13 @@ new_clone :: proc( return runtime.new_clone(data, allocator, loc) } +/* +Allocate a new slice with alignment. + +This procedure allocates a new slice of type `T` with length `len`, aligned +on a boundary specified by `alignment` from an allocator specified by +`allocator`, and returns the allocated slice. +*/ @(require_results) make_aligned :: proc( $T: typeid/[]$E, @@ -253,6 +643,12 @@ make_aligned :: proc( return runtime.make_aligned(T, len, alignment, allocator, loc) } +/* +Allocate a new slice. + +This procedure allocates a new slice of type `T` with length `len`, from an +allocator specified by `allocator`, and returns the allocated slice. +*/ @(require_results) make_slice :: proc( $T: typeid/[]$E, @@ -263,6 +659,12 @@ make_slice :: proc( return runtime.make_slice(T, len, allocator, loc) } +/* +Allocate a dynamic array. + +This procedure creates a dynamic array of type `T`, with `allocator` as its +backing allocator, and initial length and capacity of `0`. +*/ @(require_results) make_dynamic_array :: proc( $T: typeid/[dynamic]$E, @@ -272,6 +674,13 @@ make_dynamic_array :: proc( return runtime.make_dynamic_array(T, allocator, loc) } +/* +Allocate a dynamic array with initial length. + +This procedure creates a dynamic array of type `T`, with `allocator` as its +backing allocator, and initial capacity of `0`, and initial length specified by +`len`. +*/ @(require_results) make_dynamic_array_len :: proc( $T: typeid/[dynamic]$E, @@ -282,6 +691,13 @@ make_dynamic_array_len :: proc( return runtime.make_dynamic_array_len_cap(T, len, len, allocator, loc) } +/* +Allocate a dynamic array with initial length and capacity. + +This procedure creates a dynamic array of type `T`, with `allocator` as its +backing allocator, and initial capacity specified by `cap`, and initial length +specified by `len`. +*/ @(require_results) make_dynamic_array_len_cap :: proc( $T: typeid/[dynamic]$E, @@ -293,6 +709,13 @@ make_dynamic_array_len_cap :: proc( return runtime.make_dynamic_array_len_cap(T, len, cap, allocator, loc) } +/* +Allocate a map. + +This procedure creates a map of type `T` with initial capacity specified by +`cap`, that is using an allocator specified by `allocator` as its backing +allocator. +*/ @(require_results) make_map :: proc( $T: typeid/map[$K]$E, @@ -303,6 +726,12 @@ make_map :: proc( return runtime.make_map(T, cap, allocator, loc) } +/* +Allocate a multi pointer. + +This procedure allocates a multipointer of type `T` pointing to `len` elements, +from an allocator specified by `allocator`. +*/ @(require_results) make_multi_pointer :: proc( $T: typeid/[^]$E, @@ -313,6 +742,9 @@ make_multi_pointer :: proc( return runtime.make_multi_pointer(T, len, allocator, loc) } +/* +Allocate. +*/ make :: proc{ make_slice, make_dynamic_array, @@ -322,6 +754,23 @@ make :: proc{ make_multi_pointer, } +/* +Default resize procedure. + +When allocator does not support resize operation, but supports `.Alloc` and +`.Free`, this procedure is used to implement allocator's default behavior on +resize. + +The behavior of the function is as follows: + +- If `new_size` is `0`, the function acts like `free()`, freeing the memory + region of `old_size` bytes located at `old_memory`. +- If `old_memory` is `nil`, the function acts like `alloc()`, allocating + `new_size` bytes of memory aligned on a boundary specified by `alignment`. +- Otherwise, a new memory region of size `new_size` is allocated, then the + data from the old memory region is copied and the old memory region is + freed. +*/ @(require_results) default_resize_align :: proc( old_memory: rawptr, @@ -343,6 +792,27 @@ default_resize_align :: proc( return } +/* +Default resize procedure. + +When allocator does not support resize operation, but supports +`.Alloc_Non_Zeroed` and `.Free`, this procedure is used to implement allocator's +default behavior on +resize. + +Unlike `default_resize_align` no new memory is being explicitly +zero-initialized. + +The behavior of the function is as follows: + +- If `new_size` is `0`, the function acts like `free()`, freeing the memory + region of `old_size` bytes located at `old_memory`. +- If `old_memory` is `nil`, the function acts like `alloc()`, allocating + `new_size` bytes of memory aligned on a boundary specified by `alignment`. +- Otherwise, a new memory region of size `new_size` is allocated, then the + data from the old memory region is copied and the old memory region is + freed. +*/ @(require_results) default_resize_bytes_align_non_zeroed :: proc( old_data: []byte, @@ -354,6 +824,23 @@ default_resize_bytes_align_non_zeroed :: proc( return _default_resize_bytes_align(old_data, new_size, alignment, false, allocator, loc) } +/* +Default resize procedure. + +When allocator does not support resize operation, but supports `.Alloc` and +`.Free`, this procedure is used to implement allocator's default behavior on +resize. + +The behavior of the function is as follows: + +- If `new_size` is `0`, the function acts like `free()`, freeing the memory + region specified by `old_data`. +- If `old_data` is `nil`, the function acts like `alloc()`, allocating + `new_size` bytes of memory aligned on a boundary specified by `alignment`. +- Otherwise, a new memory region of size `new_size` is allocated, then the + data from the old memory region is copied and the old memory region is + freed. +*/ @(require_results) default_resize_bytes_align :: proc( old_data: []byte, @@ -383,16 +870,13 @@ _default_resize_bytes_align :: #force_inline proc( return alloc_bytes_non_zeroed(new_size, alignment, allocator, loc) } } - if new_size == 0 { err := free_bytes(old_data, allocator, loc) return nil, err } - if new_size == old_size { return old_data, .None } - new_memory : []byte err : Allocator_Error if should_zero { @@ -403,7 +887,6 @@ _default_resize_bytes_align :: #force_inline proc( if new_memory == nil || err != nil { return nil, err } - runtime.copy(new_memory, old_data) free_bytes(old_data, allocator, loc) return new_memory, err From b78d54601021e4c22a0a62aa7a1cfa69405da455 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sun, 8 Sep 2024 11:02:17 +1100 Subject: [PATCH 23/72] [mem]: Add non_zeroed versions of resize --- core/mem/alloc.odin | 100 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 94 insertions(+), 6 deletions(-) diff --git a/core/mem/alloc.odin b/core/mem/alloc.odin index dbf9af9b2..c2e55541c 100644 --- a/core/mem/alloc.odin +++ b/core/mem/alloc.odin @@ -409,10 +409,10 @@ This procedure resizes a memory region, `old_size` bytes in size, located at the address specified by `ptr`, such that it has a new size, specified by `new_size` and and is aligned on a boundary specified by `alignment`. -If the `ptr` parameter is `nil`, `resize()` acts just like `alloc`, allocating +If the `ptr` parameter is `nil`, `resize()` acts just like `alloc()`, allocating `new_size` bytes, aligned on a boundary specified by `alignment`. -If the `new_size` parameter is `0`, `resize()` acts just like `free`, freeing +If the `new_size` parameter is `0`, `resize()` acts just like `free()`, freeing the memory region `old_size` bytes in length, located at the address specified by `ptr`. @@ -444,6 +444,51 @@ resize :: proc( return raw_data(data), err } +/* +Resize a memory region without zero-initialization. + +This procedure resizes a memory region, `old_size` bytes in size, located at +the address specified by `ptr`, such that it has a new size, specified by +`new_size` and and is aligned on a boundary specified by `alignment`. + +If the `ptr` parameter is `nil`, `resize()` acts just like `alloc()`, allocating +`new_size` bytes, aligned on a boundary specified by `alignment`. + +If the `new_size` parameter is `0`, `resize()` acts just like `free()`, freeing +the memory region `old_size` bytes in length, located at the address specified +by `ptr`. + +Unlike `resize()`, this procedure does not explicitly zero-initialize any new +memory. + +**Inputs**: +- `ptr`: Pointer to the memory region to resize. +- `old_size`: Size of the memory region to resize. +- `new_size`: The desired size of the resized memory region. +- `alignment`: The desired alignment of the resized memory region. +- `allocator`: The owner of the memory region to resize. + +**Returns**: +1. The pointer to the resized memory region, if successfull, `nil` otherwise. +2. Error, if resize failed. + +**Note**: The `alignment` parameter is used to preserve the original alignment +of the allocation, if `resize()` needs to relocate the memory region. Do not +use `resize()` to change the alignment of the allocated memory region. +*/ +@(require_results) +resize_non_zeroed :: proc( + ptr: rawptr, + old_size: int, + new_size: int, + alignment: int = DEFAULT_ALIGNMENT, + allocator := context.allocator, + loc := #caller_location, +) -> (rawptr, Allocator_Error) { + data, err := runtime.non_zero_mem_resize(ptr, old_size, new_size, alignment, allocator, loc) + return raw_data(data), err +} + /* Resize a memory region. @@ -451,11 +496,12 @@ This procedure resizes a memory region, specified by `old_data`, such that it has a new size, specified by `new_size` and and is aligned on a boundary specified by `alignment`. -If the `old_data` parameter is `nil`, `resize()` acts just like `alloc`, -allocating `new_size` bytes, aligned on a boundary specified by `alignment`. +If the `old_data` parameter is `nil`, `resize_bytes()` acts just like +`alloc_bytes()`, allocating `new_size` bytes, aligned on a boundary specified +by `alignment`. -If the `new_size` parameter is `0`, `resize()` acts just like `free`, freeing -the memory region specified by `old_data`. +If the `new_size` parameter is `0`, `resize_bytes()` acts just like +`free_bytes()`, freeing the memory region specified by `old_data`. **Inputs**: - `old_data`: Pointer to the memory region to resize. @@ -482,6 +528,48 @@ resize_bytes :: proc( return runtime.mem_resize(raw_data(old_data), len(old_data), new_size, alignment, allocator, loc) } +/* +Resize a memory region. + +This procedure resizes a memory region, specified by `old_data`, such that it +has a new size, specified by `new_size` and and is aligned on a boundary +specified by `alignment`. + +If the `old_data` parameter is `nil`, `resize_bytes()` acts just like +`alloc_bytes()`, allocating `new_size` bytes, aligned on a boundary specified +by `alignment`. + +If the `new_size` parameter is `0`, `resize_bytes()` acts just like +`free_bytes()`, freeing the memory region specified by `old_data`. + +Unlike `resize_bytes()`, this procedure does not explicitly zero-initialize +any new memory. + +**Inputs**: +- `old_data`: Pointer to the memory region to resize. +- `new_size`: The desired size of the resized memory region. +- `alignment`: The desired alignment of the resized memory region. +- `allocator`: The owner of the memory region to resize. + +**Returns**: +1. The resized memory region, if successfull, `nil` otherwise. +2. Error, if resize failed. + +**Note**: The `alignment` parameter is used to preserve the original alignment +of the allocation, if `resize()` needs to relocate the memory region. Do not +use `resize()` to change the alignment of the allocated memory region. +*/ +@(require_results) +resize_bytes_non_zeroed :: proc( + old_data: []byte, + new_size: int, + alignment: int = DEFAULT_ALIGNMENT, + allocator := context.allocator, + loc := #caller_location, +) -> ([]byte, Allocator_Error) { + return runtime.non_zero_mem_resize(raw_data(old_data), len(old_data), new_size, alignment, allocator, loc) +} + /* Query allocator features. */ From 6eb80831b5ec9e43f1b70eb305688e8cff60f0ce Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sun, 8 Sep 2024 11:12:28 +1100 Subject: [PATCH 24/72] [mem]: Panic when allocator is not initialized --- core/mem/allocators.odin | 67 +++++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 22 deletions(-) diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index 34b89fcb8..e7b5faa16 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -57,14 +57,24 @@ init_arena :: proc(a: ^Arena, data: []byte) { } @(require_results) -arena_alloc :: proc(a: ^Arena, size: int, alignment := DEFAULT_ALIGNMENT) -> (rawptr, Allocator_Error) { - bytes, err := arena_alloc_bytes(a, size, alignment) +arena_alloc :: proc( + a: ^Arena, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location, +) -> (rawptr, Allocator_Error) { + bytes, err := arena_alloc_bytes(a, size, alignment, loc) return raw_data(bytes), err } @(require_results) -arena_alloc_bytes :: proc(a: ^Arena, size: int, alignment := DEFAULT_ALIGNMENT) -> ([]byte, Allocator_Error) { - bytes, err := arena_alloc_bytes_non_zeroed(a, size, alignment) +arena_alloc_bytes :: proc( + a: ^Arena, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location, +) -> ([]byte, Allocator_Error) { + bytes, err := arena_alloc_bytes_non_zeroed(a, size, alignment, loc) if bytes != nil { zero_slice(bytes) } @@ -72,13 +82,26 @@ arena_alloc_bytes :: proc(a: ^Arena, size: int, alignment := DEFAULT_ALIGNMENT) } @(require_results) -arena_alloc_non_zeroed :: proc(a: ^Arena, size: int, alignment := DEFAULT_ALIGNMENT) -> (rawptr, Allocator_Error) { - bytes, err := arena_alloc_bytes_non_zeroed(a, size, alignment) +arena_alloc_non_zeroed :: proc( + a: ^Arena, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location, +) -> (rawptr, Allocator_Error) { + bytes, err := arena_alloc_bytes_non_zeroed(a, size, alignment, loc) return raw_data(bytes), err } @(require_results) -arena_alloc_bytes_non_zeroed :: proc(a: ^Arena, size: int, alignment := DEFAULT_ALIGNMENT) -> ([]byte, Allocator_Error) { +arena_alloc_bytes_non_zeroed :: proc( + a: ^Arena, + size: int, + alignment := DEFAULT_ALIGNMENT, + loc := #caller_location +) -> ([]byte, Allocator_Error) { + if a.data == nil { + panic("Arena is not initialized", loc) + } #no_bounds_check end := &a.data[a.offset] ptr := align_forward(end, uintptr(alignment)) total_size := size + ptr_sub((^byte)(ptr), (^byte)(end)) @@ -101,22 +124,22 @@ arena_allocator_proc :: proc( alignment: int, old_memory: rawptr, old_size: int, - location := #caller_location, + loc := #caller_location, ) -> ([]byte, Allocator_Error) { arena := cast(^Arena)allocator_data switch mode { case .Alloc: - return arena_alloc_bytes(arena, size, alignment) + return arena_alloc_bytes(arena, size, alignment, loc) case .Alloc_Non_Zeroed: - return arena_alloc_bytes_non_zeroed(arena, size, alignment) + return arena_alloc_bytes_non_zeroed(arena, size, alignment, loc) case .Free: return nil, .Mode_Not_Implemented 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)) + return default_resize_bytes_align(byte_slice(old_memory, old_size), size, alignment, arena_allocator(arena), loc) case .Resize_Non_Zeroed: - return default_resize_bytes_align_non_zeroed(byte_slice(old_memory, old_size), size, alignment, arena_allocator(arena)) + return default_resize_bytes_align_non_zeroed(byte_slice(old_memory, old_size), size, alignment, arena_allocator(arena), loc) case .Query_Features: set := (^Allocator_Mode_Set)(old_memory) if set != nil { @@ -311,9 +334,6 @@ scratch_free :: proc(s: ^Scratch, ptr: rawptr, loc := #caller_location) -> Alloc } scratch_free_all :: proc(s: ^Scratch, loc := #caller_location) { - if s.data == nil { - panic("free_all called on an unitialized scratch allocator", loc) - } s.curr_offset = 0 s.prev_allocation = nil for ptr in s.leaked_allocations { @@ -571,9 +591,6 @@ stack_free :: proc( } stack_free_all :: proc(s: ^Stack, loc := #caller_location) { - if s.data == nil { - panic("Stack free all on an uninitialized stack allocator", loc) - } s.prev_offset = 0 s.curr_offset = 0 } @@ -791,7 +808,7 @@ small_stack_alloc_bytes_non_zeroed :: proc( loc := #caller_location, ) -> ([]byte, Allocator_Error) { if s.data == nil { - return nil, .Invalid_Argument + panic("Small stack is not initialized", loc) } alignment := alignment alignment = clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2) @@ -815,6 +832,9 @@ small_stack_free :: proc( old_memory: rawptr, loc := #caller_location, ) -> Allocator_Error { + if s.data == nil { + panic("Small stack is not initialized", loc) + } if old_memory == nil { return nil } @@ -892,6 +912,9 @@ small_stack_resize_bytes_non_zeroed :: proc( alignment := DEFAULT_ALIGNMENT, loc := #caller_location, ) -> ([]byte, Allocator_Error) { + if s.data == nil { + panic("Small stack is not initialized", loc) + } old_memory := raw_data(old_data) old_size := len(old_data) alignment := alignment @@ -1029,7 +1052,7 @@ dynamic_arena_destroy :: proc(pool: ^Dynamic_Arena) { @(private="file") _dynamic_arena_cycle_new_block :: proc(p: ^Dynamic_Arena, loc := #caller_location) -> (err: Allocator_Error) { if p.block_allocator.procedure == nil { - panic("You must call pool_init on a Pool before using it", loc) + panic("You must call arena_init on a Pool before using it", loc) } if p.current_block != nil { append(&p.used_blocks, p.current_block, loc=loc) @@ -1528,9 +1551,9 @@ buddy_allocator_proc :: proc( case .Alloc_Non_Zeroed: return buddy_allocator_alloc_bytes_non_zeroed(b, uint(size)) case .Resize: - return default_resize_bytes_align(byte_slice(old_memory, old_size), size, alignment, buddy_allocator(b)) + return default_resize_bytes_align(byte_slice(old_memory, old_size), size, alignment, buddy_allocator(b), loc) case .Resize_Non_Zeroed: - return default_resize_bytes_align_non_zeroed(byte_slice(old_memory, old_size), size, alignment, buddy_allocator(b)) + return default_resize_bytes_align_non_zeroed(byte_slice(old_memory, old_size), size, alignment, buddy_allocator(b), loc) case .Free: return nil, buddy_allocator_free(b, old_memory) case .Free_All: From f1f5dc614e0451c65d7e036ecb92eb76a7b753bd Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sun, 8 Sep 2024 11:17:27 +1100 Subject: [PATCH 25/72] [mem]: Remove old comments --- core/mem/allocators.odin | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index e7b5faa16..2ee397316 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -402,7 +402,6 @@ scratch_resize_bytes_non_zeroed :: proc( } begin := uintptr(raw_data(s.data)) end := begin + uintptr(len(s.data)) - // TODO(flysand): Doesn't handle old_memory == nil old_ptr := uintptr(old_memory) if begin <= old_ptr && old_ptr < end && old_ptr+uintptr(size) < end { s.curr_offset = int(old_ptr-begin)+size @@ -412,7 +411,6 @@ scratch_resize_bytes_non_zeroed :: proc( if err != nil { return data, err } - // TODO(flysand): OOB access on size < old_size. runtime.copy(data, byte_slice(old_memory, old_size)) err = scratch_free(s, old_memory, loc) return data, err From 3b30bc305c9da737545c96173b566efb1e10b466 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sun, 8 Sep 2024 14:13:03 +1100 Subject: [PATCH 26/72] [mem]: Document raw.odin --- core/mem/raw.odin | 74 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 60 insertions(+), 14 deletions(-) diff --git a/core/mem/raw.odin b/core/mem/raw.odin index 7fda3229d..ab1148cea 100644 --- a/core/mem/raw.odin +++ b/core/mem/raw.odin @@ -3,40 +3,86 @@ package mem import "base:builtin" import "base:runtime" +/* +Mamory layout of the `any` type. +*/ Raw_Any :: runtime.Raw_Any +/* +Mamory layout of the `string` type. +*/ Raw_String :: runtime.Raw_String - +/* +Mamory layout of the `cstring` type. +*/ Raw_Cstring :: runtime.Raw_Cstring - +/* +Mamory layout of `[]T` types. +*/ Raw_Slice :: runtime.Raw_Slice - +/* +Mamory layout of `[dynamic]T` types. +*/ Raw_Dynamic_Array :: runtime.Raw_Dynamic_Array - +/* +Mamory layout of `map[K]V` types. +*/ Raw_Map :: runtime.Raw_Map - +/* +Mamory layout of `#soa []T` types. +*/ Raw_Soa_Pointer :: runtime.Raw_Soa_Pointer - +/* +Mamory layout of the `complex32` type. +*/ Raw_Complex32 :: runtime.Raw_Complex32 - +/* +Mamory layout of the `complex64` type. +*/ Raw_Complex64 :: runtime.Raw_Complex64 - +/* +Mamory layout of the `complex128` type. +*/ Raw_Complex128 :: runtime.Raw_Complex128 - +/* +Mamory layout of the `quaternion64` type. +*/ Raw_Quaternion64 :: runtime.Raw_Quaternion64 - +/* +Mamory layout of the `quaternion128` type. +*/ Raw_Quaternion128 :: runtime.Raw_Quaternion128 - +/* +Mamory layout of the `quaternion256` type. +*/ Raw_Quaternion256 :: runtime.Raw_Quaternion256 - +/* +Mamory layout of the `quaternion64` type. +*/ Raw_Quaternion64_Vector_Scalar :: runtime.Raw_Quaternion64_Vector_Scalar - +/* +Mamory layout of the `quaternion128` type. +*/ Raw_Quaternion128_Vector_Scalar :: runtime.Raw_Quaternion128_Vector_Scalar - +/* +Mamory layout of the `quaternion256` type. +*/ Raw_Quaternion256_Vector_Scalar :: runtime.Raw_Quaternion256_Vector_Scalar +/* +Create a value of the any type. + +This procedure creates a value with type `any` that points to an object with +typeid `id` located at an address specified by `data`. +*/ make_any :: proc "contextless" (data: rawptr, id: typeid) -> any { return transmute(any)Raw_Any{data, id} } +/* +Obtain pointer to the data. + +This procedure returns the pointer to the data of a slice, string, or a dynamic +array. +*/ raw_data :: builtin.raw_data From 299accb717ff376bbb063cde7585d582b658d405 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sun, 8 Sep 2024 14:17:32 +1100 Subject: [PATCH 27/72] [mem]: Put panic allocator after nil allocator, adjust @require_results --- core/mem/allocators.odin | 115 +++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 58 deletions(-) diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index 2ee397316..649d5466a 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -3,6 +3,7 @@ package mem import "base:intrinsics" import "base:runtime" +@(require_results) nil_allocator :: proc() -> Allocator { return Allocator{ procedure = nil_allocator_proc, @@ -21,6 +22,62 @@ nil_allocator_proc :: proc( return nil, nil } + + +@(require_results) +panic_allocator :: proc() -> Allocator { + return Allocator{ + procedure = panic_allocator_proc, + data = nil, + } +} + +panic_allocator_proc :: proc( + allocator_data: rawptr, + mode: Allocator_Mode, + size, alignment: int, + old_memory: rawptr, + old_size: int, + loc := #caller_location, +) -> ([]byte, Allocator_Error) { + switch mode { + case .Alloc: + if size > 0 { + panic("mem: panic allocator, .Alloc called", loc=loc) + } + case .Alloc_Non_Zeroed: + if size > 0 { + panic("mem: panic allocator, .Alloc_Non_Zeroed called", loc=loc) + } + case .Resize: + if size > 0 { + panic("mem: panic allocator, .Resize called", loc=loc) + } + case .Resize_Non_Zeroed: + if size > 0 { + panic("mem: panic allocator, .Resize_Non_Zeroed called", loc=loc) + } + case .Free: + if old_memory != nil { + panic("mem: panic allocator, .Free called", loc=loc) + } + case .Free_All: + panic("mem: panic allocator, .Free_All called", loc=loc) + case .Query_Features: + set := (^Allocator_Mode_Set)(old_memory) + if set != nil { + set^ = {.Query_Features} + } + return nil, nil + + case .Query_Info: + panic("mem: panic allocator, .Query_Info called", loc=loc) + } + return nil, nil +} + + + Arena :: struct { data: []byte, offset: int, @@ -300,7 +357,6 @@ scratch_alloc_bytes_non_zeroed :: proc( return ptr, err } -@(require_results) scratch_free :: proc(s: ^Scratch, ptr: rawptr, loc := #caller_location) -> Allocator_Error { if s.data == nil { panic("Free on an uninitialized scratch allocator", loc) @@ -555,7 +611,6 @@ stack_alloc_bytes_non_zeroed :: proc( return byte_slice(rawptr(next_addr), size), nil } -@(require_results) stack_free :: proc( s: ^Stack, old_memory: rawptr, @@ -824,7 +879,6 @@ small_stack_alloc_bytes_non_zeroed :: proc( return byte_slice(rawptr(next_addr), size), nil } -@(require_results) small_stack_free :: proc( s: ^Small_Stack, old_memory: rawptr, @@ -1254,60 +1308,6 @@ dynamic_arena_allocator_proc :: proc( -panic_allocator_proc :: proc( - allocator_data: rawptr, - mode: Allocator_Mode, - size, alignment: int, - old_memory: rawptr, - old_size: int, - loc := #caller_location, -) -> ([]byte, Allocator_Error) { - switch mode { - case .Alloc: - if size > 0 { - panic("mem: panic allocator, .Alloc called", loc=loc) - } - case .Alloc_Non_Zeroed: - if size > 0 { - panic("mem: panic allocator, .Alloc_Non_Zeroed called", loc=loc) - } - case .Resize: - if size > 0 { - panic("mem: panic allocator, .Resize called", loc=loc) - } - case .Resize_Non_Zeroed: - if size > 0 { - panic("mem: panic allocator, .Resize_Non_Zeroed called", loc=loc) - } - case .Free: - if old_memory != nil { - panic("mem: panic allocator, .Free called", loc=loc) - } - case .Free_All: - panic("mem: panic allocator, .Free_All called", loc=loc) - case .Query_Features: - set := (^Allocator_Mode_Set)(old_memory) - if set != nil { - set^ = {.Query_Features} - } - return nil, nil - - case .Query_Info: - panic("mem: panic allocator, .Query_Info called", loc=loc) - } - return nil, nil -} - -@(require_results) -panic_allocator :: proc() -> Allocator { - return Allocator{ - procedure = panic_allocator_proc, - data = nil, - } -} - - - Buddy_Block :: struct #align(align_of(uint)) { size: uint, is_free: bool, @@ -1513,7 +1513,6 @@ buddy_allocator_alloc_bytes_non_zeroed :: proc(b: ^Buddy_Allocator, size: uint) return nil, nil } -@(require_results) buddy_allocator_free :: proc(b: ^Buddy_Allocator, ptr: rawptr) -> Allocator_Error { if ptr != nil { if !(b.head <= ptr && ptr <= b.tail) { From 05df34f99c97eefc6957786150e19010cb84c230 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sun, 8 Sep 2024 18:44:33 +1100 Subject: [PATCH 28/72] [mem]: Start documenting allocators.odin --- core/mem/allocators.odin | 663 ++++++++++++++++++++++++++++++++++----- 1 file changed, 592 insertions(+), 71 deletions(-) diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index 649d5466a..972031a21 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -3,6 +3,13 @@ package mem import "base:intrinsics" import "base:runtime" +/* +Nil allocator. + +The `nil` allocator returns `nil` on every allocation attempt. This type of +allocator can be used in scenarios where memory doesn't need to be allocated, +but an attempt to allocate memory is not an error. +*/ @(require_results) nil_allocator :: proc() -> Allocator { return Allocator{ @@ -23,7 +30,13 @@ nil_allocator_proc :: proc( } +/* +Panic allocator. +The panic allocator is a type of allocator that panics on any allocation +attempt. This type of allocator can be used in scenarios where memory should +not be allocated, and an attempt to allocate memory is an error. +*/ @(require_results) panic_allocator :: proc() -> Allocator { return Allocator{ @@ -77,7 +90,9 @@ panic_allocator_proc :: proc( } - +/* +Arena allocator data. +*/ Arena :: struct { data: []byte, offset: int, @@ -85,11 +100,19 @@ Arena :: struct { temp_count: int, } -Arena_Temp_Memory :: struct { - arena: ^Arena, - prev_offset: int, -} +/* +Arena allocator. +The arena allocator (also known as a linear allocator, bump allocator, +region allocator) is an allocator that uses a single backing buffer for +allocations. + +The buffer is being used contiguously, from start by end. Each subsequent +allocation occupies the next adjacent region of memory in the buffer. Since +arena allocator does not keep track of any metadata associated with the +allocations and their locations, it is impossible to free individual +allocations. +*/ @(require_results) arena_allocator :: proc(arena: ^Arena) -> Allocator { return Allocator{ @@ -98,6 +121,12 @@ arena_allocator :: proc(arena: ^Arena) -> Allocator { } } +/* +Initialize an arena. + +This procedure initializes the arena `a` with memory region `data` as it's +backing buffer. +*/ arena_init :: proc(a: ^Arena, data: []byte) { a.data = data a.offset = 0 @@ -113,6 +142,13 @@ init_arena :: proc(a: ^Arena, data: []byte) { a.temp_count = 0 } +/* +Allocate memory from an arena. + +This procedure allocates `size` bytes of memory aligned on a boundary specified +by `alignment` from an arena `a`. The allocated memory is zero-initialized. +This procedure returns a pointer to the newly allocated memory region. +*/ @(require_results) arena_alloc :: proc( a: ^Arena, @@ -124,6 +160,13 @@ arena_alloc :: proc( return raw_data(bytes), err } +/* +Allocate memory from an arena. + +This procedure allocates `size` bytes of memory aligned on a boundary specified +by `alignment` from an arena `a`. The allocated memory is zero-initialized. +This procedure returns a slice of the newly allocated memory region. +*/ @(require_results) arena_alloc_bytes :: proc( a: ^Arena, @@ -138,6 +181,14 @@ arena_alloc_bytes :: proc( return bytes, err } +/* +Allocate non-initialized memory from an arena. + +This procedure allocates `size` bytes of memory aligned on a boundary specified +by `alignment` from an arena `a`. The allocated memory is not explicitly +zero-initialized. This procedure returns a pointer to the newly allocated +memory region. +*/ @(require_results) arena_alloc_non_zeroed :: proc( a: ^Arena, @@ -149,6 +200,14 @@ arena_alloc_non_zeroed :: proc( return raw_data(bytes), err } +/* +Allocate non-initialized memory from an arena. + +This procedure allocates `size` bytes of memory aligned on a boundary specified +by `alignment` from an arena `a`. The allocated memory is not explicitly +zero-initialized. This procedure returns a slice of the newly allocated +memory region. +*/ @(require_results) arena_alloc_bytes_non_zeroed :: proc( a: ^Arena, @@ -170,6 +229,9 @@ arena_alloc_bytes_non_zeroed :: proc( return byte_slice(ptr, size), nil } +/* +Free all memory to an arena. +*/ arena_free_all :: proc(a: ^Arena) { a.offset = 0 } @@ -209,6 +271,28 @@ arena_allocator_proc :: proc( return nil, nil } +/* +Temporary memory region of arena. + +Temporary memory regions of arena act as "savepoints" for arena. When one is +created, the subsequent allocations are done inside the temporary memory +region. When `end_arena_temp_memory` is called, the arena is rolled back, and +all of the memory that was allocated from the arena will be freed. + +Multiple temporary memory regions can exist at the same time for an arena. +*/ +Arena_Temp_Memory :: struct { + arena: ^Arena, + prev_offset: int, +} + +/* +Start a temporary memory region. + +This procedure creates a temporary memory region. After a temporary memory +region is created, all allocations are said to be *inside* the temporary memory +region, until `end_arena_temp_memory` is called. +*/ @(require_results) begin_arena_temp_memory :: proc(a: ^Arena) -> Arena_Temp_Memory { tmp: Arena_Temp_Memory @@ -218,6 +302,12 @@ begin_arena_temp_memory :: proc(a: ^Arena) -> Arena_Temp_Memory { return tmp } +/* +End a temporary memory region. + +This procedure ends the temporary memory region for an arena. All of the +allocations *inside* the temporary memory region will be freed to the arena. +*/ end_arena_temp_memory :: proc(tmp: Arena_Temp_Memory) { assert(tmp.arena.offset >= tmp.prev_offset) assert(tmp.arena.temp_count > 0) @@ -225,11 +315,14 @@ end_arena_temp_memory :: proc(tmp: Arena_Temp_Memory) { tmp.arena.temp_count -= 1 } -/* old procedures */ +/* Preserved for compatibility */ Scratch_Allocator :: Scratch scratch_allocator_init :: scratch_init scratch_allocator_destroy :: scratch_destroy +/* +Scratch allocator data. +*/ Scratch :: struct { data: []byte, curr_offset: int, @@ -238,6 +331,23 @@ Scratch :: struct { leaked_allocations: [dynamic][]byte, } +/* +Scratch allocator. + +The scratch allocator works in a similar way to the `Arena` allocator. The +scratch allocator has a backing buffer, that is being allocated in +contiguous regions, from start to end. + +Each subsequent allocation will be the next adjacent region of memory in the +backing buffer. If the allocation doesn't fit into the remaining space of the +backing buffer, this allocation is put at the start of the buffer, and all +previous allocations will become invalidated. If the allocation doesn't fit +into the backing buffer as a whole, it will be allocated using a backing +allocator, and pointer to the allocated memory region will be put into the +`leaked_allocations` array. + +The `leaked_allocations` array is managed by the `context` allocator. +*/ @(require_results) scratch_allocator :: proc(allocator: ^Scratch) -> Allocator { return Allocator{ @@ -246,6 +356,9 @@ scratch_allocator :: proc(allocator: ^Scratch) -> Allocator { } } +/* +Initialize scratch allocator. +*/ scratch_init :: proc(s: ^Scratch, size: int, backup_allocator := context.allocator) -> Allocator_Error { s.data = make_aligned([]byte, size, 2*align_of(rawptr), backup_allocator) or_return s.curr_offset = 0 @@ -255,6 +368,9 @@ scratch_init :: proc(s: ^Scratch, size: int, backup_allocator := context.allocat return nil } +/* +Free all data associated with a scratch allocator. +*/ scratch_destroy :: proc(s: ^Scratch) { if s == nil { return @@ -267,6 +383,13 @@ scratch_destroy :: proc(s: ^Scratch) { s^ = {} } +/* +Allocate memory from scratch allocator. + +This procedure allocates `size` bytes of memory aligned on a boundary specified +by `alignment`. The allocated memory region is zero-initialized. This procedure +returns a pointer to the allocated memory region. +*/ @(require_results) scratch_alloc :: proc( s: ^Scratch, @@ -278,6 +401,13 @@ scratch_alloc :: proc( return raw_data(bytes), err } +/* +Allocate memory from scratch allocator. + +This procedure allocates `size` bytes of memory aligned on a boundary specified +by `alignment`. The allocated memory region is zero-initialized. This procedure +returns a slice of the allocated memory region. +*/ @(require_results) scratch_alloc_bytes :: proc( s: ^Scratch, @@ -292,6 +422,13 @@ scratch_alloc_bytes :: proc( return bytes, err } +/* +Allocate memory from scratch allocator. + +This procedure allocates `size` bytes of memory aligned on a boundary specified +by `alignment`. The allocated memory region is not explicitly zero-initialized. +This procedure returns a pointer to the allocated memory region. +*/ @(require_results) scratch_alloc_non_zeroed :: proc( s: ^Scratch, @@ -303,6 +440,13 @@ scratch_alloc_non_zeroed :: proc( return raw_data(bytes), err } +/* +Allocate memory from scratch allocator. + +This procedure allocates `size` bytes of memory aligned on a boundary specified +by `alignment`. The allocated memory region is not explicitly zero-initialized. +This procedure returns a slice of the allocated memory region. +*/ @(require_results) scratch_alloc_bytes_non_zeroed :: proc( s: ^Scratch, @@ -319,44 +463,49 @@ scratch_alloc_bytes_non_zeroed :: proc( } size := size size = align_forward_int(size, alignment) - switch { - case s.curr_offset+size <= len(s.data): + if size <= len(s.data) { + offset := uintptr(0) + if s.curr_offset+size <= len(s.data) { + offset = uintptr(s.curr_offset) + } else { + offset = 0 + } start := uintptr(raw_data(s.data)) - ptr := start + uintptr(s.curr_offset) - ptr = align_forward_uintptr(ptr, uintptr(alignment)) + ptr := align_forward_uintptr(offset+start, uintptr(alignment)) s.prev_allocation = rawptr(ptr) - offset := int(ptr - start) - s.curr_offset = offset + size + s.curr_offset = int(offset) + size return byte_slice(rawptr(ptr), size), nil - case size <= len(s.data): - start := uintptr(raw_data(s.data)) - ptr := align_forward_uintptr(start, uintptr(alignment)) - s.prev_allocation = rawptr(ptr) - offset := int(ptr - start) - s.curr_offset = offset + size - return byte_slice(rawptr(ptr), size), nil - } - a := s.backup_allocator - if a.procedure == nil { - a = context.allocator - s.backup_allocator = a - } - ptr, err := alloc_bytes_non_zeroed(size, alignment, a, loc) - if err != nil { + } else { + a := s.backup_allocator + if a.procedure == nil { + a = context.allocator + s.backup_allocator = a + } + ptr, err := alloc_bytes_non_zeroed(size, alignment, a, loc) + if err != nil { + return ptr, err + } + if s.leaked_allocations == nil { + s.leaked_allocations, err = make([dynamic][]byte, a) + } + append(&s.leaked_allocations, ptr) + if logger := context.logger; logger.lowest_level <= .Warning { + if logger.procedure != nil { + logger.procedure(logger.data, .Warning, "mem.Scratch resorted to backup_allocator" , logger.options, loc) + } + } return ptr, err } - if s.leaked_allocations == nil { - s.leaked_allocations, err = make([dynamic][]byte, a) - } - append(&s.leaked_allocations, ptr) - if logger := context.logger; logger.lowest_level <= .Warning { - if logger.procedure != nil { - logger.procedure(logger.data, .Warning, "mem.Scratch resorted to backup_allocator" , logger.options, loc) - } - } - return ptr, err } +/* +Free memory to scratch allocator. + +This procedure frees the memory region allocated at pointer `ptr`. + +If `ptr` is not the latest allocation and is not a leaked allocation, this +operation is a no-op. +*/ scratch_free :: proc(s: ^Scratch, ptr: rawptr, loc := #caller_location) -> Allocator_Error { if s.data == nil { panic("Free on an uninitialized scratch allocator", loc) @@ -389,6 +538,9 @@ scratch_free :: proc(s: ^Scratch, ptr: rawptr, loc := #caller_location) -> Alloc return .Invalid_Pointer } +/* +Free all memory to the scratch allocator. +*/ scratch_free_all :: proc(s: ^Scratch, loc := #caller_location) { s.curr_offset = 0 s.prev_allocation = nil @@ -398,6 +550,22 @@ scratch_free_all :: proc(s: ^Scratch, loc := #caller_location) { clear(&s.leaked_allocations) } +/* +Resize an allocation. + +This procedure resizes a memory region, defined by its location, `old_memory`, +and its size, `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 `scratch_alloc()`, +allocating a memory region `size` bytes in size, aligned on a boundary specified +by `alignment`. + +If `size` is 0, this procedure acts just like `scratch_free()`, freeing the +memory region located at an address specified by `old_memory`. + +This procedure returns the pointer to the resized memory region. +*/ @(require_results) scratch_resize :: proc( s: ^Scratch, @@ -411,6 +579,22 @@ scratch_resize :: proc( 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 `scratch_alloc()`, +allocating a memory region `size` bytes in size, aligned on a boundary specified +by `alignment`. + +If `size` is 0, this procedure acts just like `scratch_free()`, freeing the +memory region located at an address specified by `old_memory`. + +This procedure returns the slice of the resized memory region. +*/ @(require_results) scratch_resize_bytes :: proc( s: ^Scratch, @@ -426,6 +610,22 @@ scratch_resize_bytes :: proc( return bytes, err } +/* +Resize an allocation without zero-initialization. + +This procedure resizes a memory region, defined by its location, `old_memory`, +and its size, `old_size` to have a size `size` and alignment `alignment`. The +newly allocated memory, if any is not explicitly zero-initialized. + +If `old_memory` is `nil`, this procedure acts just like `scratch_alloc()`, +allocating a memory region `size` bytes in size, aligned on a boundary specified +by `alignment`. + +If `size` is 0, this procedure acts just like `scratch_free()`, freeing the +memory region located at an address specified by `old_memory`. + +This procedure returns the pointer to the resized memory region. +*/ @(require_results) scratch_resize_non_zeroed :: proc( s: ^Scratch, @@ -439,6 +639,22 @@ scratch_resize_non_zeroed :: proc( 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 not +explicitly zero-initialized. + +If `old_memory` is `nil`, this procedure acts just like `scratch_alloc()`, +allocating a memory region `size` bytes in size, aligned on a boundary specified +by `alignment`. + +If `size` is 0, this procedure acts just like `scratch_free()`, freeing the +memory region located at an address specified by `old_memory`. + +This procedure returns the slice of the resized memory region. +*/ @(require_results) scratch_resize_bytes_non_zeroed :: proc( s: ^Scratch, @@ -509,7 +725,9 @@ scratch_allocator_proc :: proc( -// Stack is a stack-like allocator which has a strict memory freeing order +/* +Stack allocator data. +*/ Stack :: struct { data: []byte, prev_offset: int, @@ -517,11 +735,30 @@ Stack :: struct { peak_used: int, } +/* +Header of a stack allocation. +*/ Stack_Allocation_Header :: struct { prev_offset: int, padding: int, } +/* +Stack allocator. + +The stack allocator is an allocator that allocates data in the backing buffer +linearly, from start to end. Each subsequent allocation will get the next +adjacent memory region. + +Unlike arena allocator, the stack allocator saves allocation metadata and has +a strict freeing order. Only the last allocated element can be freed. After the +last allocated element is freed, the next previous allocated element becomes +available for freeing. + +The metadata is stored in the allocation headers, that are located before the +start of each allocated memory region. Each header points to the start of the +previous allocation header. +*/ @(require_results) stack_allocator :: proc(stack: ^Stack) -> Allocator { return Allocator{ @@ -530,6 +767,12 @@ stack_allocator :: proc(stack: ^Stack) -> Allocator { } } +/* +Initialize the stack allocator. + +This procedure initializes the stack allocator with a backing buffer specified +by `data` parameter. +*/ stack_init :: proc(s: ^Stack, data: []byte) { s.data = data s.prev_offset = 0 @@ -545,6 +788,13 @@ init_stack :: proc(s: ^Stack, data: []byte) { s.peak_used = 0 } +/* +Allocate memory from stack. + +This procedure allocates `size` bytes of memory, aligned to the boundary +specified by `alignment`. The allocated memory is zero-initialized. This +procedure returns the pointer to the allocated memory. +*/ @(require_results) stack_alloc :: proc( s: ^Stack, @@ -556,6 +806,13 @@ stack_alloc :: proc( return raw_data(bytes), err } +/* +Allocate memory from stack. + +This procedure allocates `size` bytes of memory, aligned to the boundary +specified by `alignment`. The allocated memory is zero-initialized. This +procedure returns the slice of the allocated memory. +*/ @(require_results) stack_alloc_bytes :: proc( s: ^Stack, @@ -570,6 +827,13 @@ stack_alloc_bytes :: proc( return bytes, err } +/* +Allocate memory from stack. + +This procedure allocates `size` bytes of memory, aligned to the boundary +specified by `alignment`. The allocated memory is not explicitly +zero-initialized. This procedure returns the pointer to the allocated memory. +*/ @(require_results) stack_alloc_non_zeroed :: proc( s: ^Stack, @@ -581,6 +845,13 @@ stack_alloc_non_zeroed :: proc( return raw_data(bytes), err } +/* +Allocate memory from stack. + +This procedure allocates `size` bytes of memory, aligned to the boundary +specified by `alignment`. The allocated memory is not explicitly +zero-initialized. This procedure returns the slice of the allocated memory. +*/ @(require_results) stack_alloc_bytes_non_zeroed :: proc( s: ^Stack, @@ -611,6 +882,13 @@ stack_alloc_bytes_non_zeroed :: proc( return byte_slice(rawptr(next_addr), size), nil } +/* +Free memory to the stack. + +This procedure frees the memory region starting at `old_memory` to the stack. +If the freeing does is an out of order freeing, the `.Invalid_Pointer` error +is returned. +*/ stack_free :: proc( s: ^Stack, old_memory: rawptr, @@ -643,12 +921,30 @@ stack_free :: proc( return nil } +/* +Free all allocations to the stack. +*/ stack_free_all :: proc(s: ^Stack, loc := #caller_location) { s.prev_offset = 0 s.curr_offset = 0 } +/* +Resize an allocation. +This procedure resizes a memory region, defined by its location, `old_memory`, +and its size, `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 `stack_alloc()`, +allocating a memory region `size` bytes in size, aligned on a boundary specified +by `alignment`. + +If `size` is 0, this procedure acts just like `stack_free()`, freeing the +memory region located at an address specified by `old_memory`. + +This procedure returns the pointer to the resized memory region. +*/ @(require_results) stack_resize :: proc( s: ^Stack, @@ -662,6 +958,22 @@ stack_resize :: proc( return raw_data(bytes), err } +/* +Resize an allocation. + +This procedure resizes a memory region, specified by the `old_data` parameter +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 `stack_alloc()`, +allocating a memory region `size` bytes in size, aligned on a boundary specified +by `alignment`. + +If `size` is 0, this procedure acts just like `stack_free()`, freeing the +memory region located at an address specified by `old_memory`. + +This procedure returns the slice of the resized memory region. +*/ @(require_results) stack_resize_bytes :: proc( s: ^Stack, @@ -681,6 +993,22 @@ stack_resize_bytes :: proc( return bytes, err } +/* +Resize an allocation without zero-initialization. + +This procedure resizes a memory region, defined by its location, `old_memory`, +and its size, `old_size` to have a size `size` and alignment `alignment`. The +newly allocated memory, if any is not explicitly zero-initialized. + +If `old_memory` is `nil`, this procedure acts just like `stack_alloc()`, +allocating a memory region `size` bytes in size, aligned on a boundary specified +by `alignment`. + +If `size` is 0, this procedure acts just like `stack_free()`, freeing the +memory region located at an address specified by `old_memory`. + +This procedure returns the pointer to the resized memory region. +*/ @(require_results) stack_resize_non_zeroed :: proc( s: ^Stack, @@ -694,6 +1022,22 @@ stack_resize_non_zeroed :: proc( return raw_data(bytes), err } +/* +Resize an allocation without zero-initialization. + +This procedure resizes a memory region, specified by the `old_data` parameter +to have a size `size` and alignment `alignment`. The newly allocated memory, +if any is not explicitly zero-initialized. + +If `old_memory` is `nil`, this procedure acts just like `stack_alloc()`, +allocating a memory region `size` bytes in size, aligned on a boundary specified +by `alignment`. + +If `size` is 0, this procedure acts just like `stack_free()`, freeing the +memory region located at an address specified by `old_memory`. + +This procedure returns the slice of the resized memory region. +*/ @(require_results) stack_resize_bytes_non_zeroed :: proc( s: ^Stack, @@ -784,18 +1128,28 @@ stack_allocator_proc :: proc( } - +/* +Allocation header of the small stack allocator. +*/ Small_Stack_Allocation_Header :: struct { padding: u8, } -// Small_Stack is a stack-like allocator which uses the smallest possible header but at the cost of non-strict memory freeing order +/* +Small stack allocator data. +*/ Small_Stack :: struct { data: []byte, offset: int, peak_used: int, } +/* +Initialize small stack. + +This procedure initializes the small stack allocator with `data` as its backing +buffer. +*/ small_stack_init :: proc(s: ^Small_Stack, data: []byte) { s.data = data s.offset = 0 @@ -809,6 +1163,28 @@ init_small_stack :: proc(s: ^Small_Stack, data: []byte) { s.peak_used = 0 } +/* +Small stack allocator. + +The small stack allocator is just like a stack allocator, with the only +difference being an extremely small header size. Unlike the stack allocator, +small stack allows out-of order freeing of memory. + +The memory is allocated in the backing buffer linearly, from start to end. +Each subsequent allocation will get the next adjacent memory region. + +The metadata is stored in the allocation headers, that are located before the +start of each allocated memory region. Each header contains the amount of +padding bytes between that header and end of the previous allocation. + +## Properties + +**Performance characteristics**: TODO + +**Has a backing allocator**: No + +**Saves metadata**: Allocation header before each allocation. +*/ @(require_results) small_stack_allocator :: proc(stack: ^Small_Stack) -> Allocator { return Allocator{ @@ -817,6 +1193,13 @@ small_stack_allocator :: proc(stack: ^Small_Stack) -> Allocator { } } +/* +Allocate memory from small stack. + +This procedure allocates `size` bytes of memory aligned to a boundary specified +by `alignment`. The allocated memory is zero-initialized. This procedure +returns a pointer to the allocated memory region. +*/ @(require_results) small_stack_alloc :: proc( s: ^Small_Stack, @@ -828,6 +1211,13 @@ small_stack_alloc :: proc( return raw_data(bytes), err } +/* +Allocate memory from small stack. + +This procedure allocates `size` bytes of memory aligned to a boundary specified +by `alignment`. The allocated memory is zero-initialized. This procedure +returns a slice of the allocated memory region. +*/ @(require_results) small_stack_alloc_bytes :: proc( s: ^Small_Stack, @@ -842,6 +1232,13 @@ small_stack_alloc_bytes :: proc( return bytes, err } +/* +Allocate memory from small stack. + +This procedure allocates `size` bytes of memory aligned to a boundary specified +by `alignment`. The allocated memory is not explicitly zero-initialized. This +procedure returns a pointer to the allocated memory region. +*/ @(require_results) small_stack_alloc_non_zeroed :: proc( s: ^Small_Stack, @@ -853,6 +1250,13 @@ small_stack_alloc_non_zeroed :: proc( return raw_data(bytes), err } +/* +Allocate memory from small stack. + +This procedure allocates `size` bytes of memory aligned to a boundary specified +by `alignment`. The allocated memory is not explicitly zero-initialized. This +procedure returns a slice of the allocated memory region. +*/ @(require_results) small_stack_alloc_bytes_non_zeroed :: proc( s: ^Small_Stack, @@ -879,6 +1283,13 @@ small_stack_alloc_bytes_non_zeroed :: proc( return byte_slice(rawptr(next_addr), size), nil } +/* +Allocate memory from small stack. + +This procedure allocates `size` bytes of memory aligned to a boundary specified +by `alignment`. The allocated memory is not explicitly zero-initialized. This +procedure returns a slice of the allocated memory region. +*/ small_stack_free :: proc( s: ^Small_Stack, old_memory: rawptr, @@ -907,10 +1318,29 @@ small_stack_free :: proc( return nil } +/* +Free all memory to small stack. +*/ small_stack_free_all :: proc(s: ^Small_Stack) { s.offset = 0 } +/* +Resize an allocation. + +This procedure resizes a memory region, defined by its location, `old_memory`, +and its size, `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 `small_stack_alloc()`, +allocating a memory region `size` bytes in size, aligned on a boundary specified +by `alignment`. + +If `size` is 0, this procedure acts just like `small_stack_free()`, freeing the +memory region located at an address specified by `old_memory`. + +This procedure returns the pointer to the resized memory region. +*/ @(require_results) small_stack_resize :: proc( s: ^Small_Stack, @@ -924,6 +1354,22 @@ small_stack_resize :: proc( return raw_data(bytes), err } +/* +Resize an allocation. + +This procedure resizes a memory region, specified by the `old_data` parameter +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 `small_stack_alloc()`, +allocating a memory region `size` bytes in size, aligned on a boundary specified +by `alignment`. + +If `size` is 0, this procedure acts just like `small_stack_free()`, freeing the +memory region located at an address specified by `old_memory`. + +This procedure returns the slice of the resized memory region. +*/ @(require_results) small_stack_resize_bytes :: proc( s: ^Small_Stack, @@ -943,6 +1389,22 @@ small_stack_resize_bytes :: proc( return bytes, err } +/* +Resize an allocation without zero-initialization. + +This procedure resizes a memory region, defined by its location, `old_memory`, +and its size, `old_size` to have a size `size` and alignment `alignment`. The +newly allocated memory, if any is not explicitly zero-initialized. + +If `old_memory` is `nil`, this procedure acts just like `small_stack_alloc()`, +allocating a memory region `size` bytes in size, aligned on a boundary specified +by `alignment`. + +If `size` is 0, this procedure acts just like `small_stack_free()`, freeing the +memory region located at an address specified by `old_memory`. + +This procedure returns the pointer to the resized memory region. +*/ @(require_results) small_stack_resize_non_zeroed :: proc( s: ^Small_Stack, @@ -956,6 +1418,22 @@ small_stack_resize_non_zeroed :: proc( return raw_data(bytes), err } +/* +Resize an allocation without zero-initialization. + +This procedure resizes a memory region, specified by the `old_data` parameter +to have a size `size` and alignment `alignment`. The newly allocated memory, +if any is not explicitly zero-initialized. + +If `old_memory` is `nil`, this procedure acts just like `small_stack_alloc()`, +allocating a memory region `size` bytes in size, aligned on a boundary specified +by `alignment`. + +If `size` is 0, this procedure acts just like `small_stack_free()`, freeing the +memory region located at an address specified by `old_memory`. + +This procedure returns the slice of the resized memory region. +*/ @(require_results) small_stack_resize_bytes_non_zeroed :: proc( s: ^Small_Stack, @@ -1050,11 +1528,19 @@ dynamic_pool_init :: dynamic_arena_init dynamic_pool_allocator :: dynamic_arena_allocator dynamic_pool_destroy :: dynamic_arena_destroy - - +/* +Default block size for dynamic arena. +*/ DYNAMIC_ARENA_BLOCK_SIZE_DEFAULT :: 65536 + +/* +Default out-band size of the dynamic arena. +*/ DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT :: 6554 +/* +Dynamic arena allocator data. +*/ Dynamic_Arena :: struct { block_size: int, out_band_size: int, @@ -1068,65 +1554,95 @@ Dynamic_Arena :: struct { block_allocator: Allocator, } +/* +Initialize a dynamic arena. + +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`. +*/ dynamic_arena_init :: proc( - pool: ^Dynamic_Arena, + a: ^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, ) { - pool.block_size = block_size - pool.out_band_size = out_band_size - pool.alignment = alignment - pool.block_allocator = block_allocator - pool.out_band_allocations.allocator = array_allocator - pool.unused_blocks.allocator = array_allocator - pool.used_blocks.allocator = array_allocator + a.block_size = block_size + a.out_band_size = out_band_size + a.alignment = alignment + a.block_allocator = block_allocator + a.out_band_allocations.allocator = array_allocator + a.unused_blocks.allocator = array_allocator + a.used_blocks.allocator = array_allocator } +/* +Dynamic arena allocator. + +The dynamic arena allocator uses blocks of a specific size, allocated on-demand +using the block allocator. This allocator acts similarly to arena. All +allocations in a block happen contiguously, from start to end. If an allocation +does not fit into the remaining space of the block, and its size is smaller +than the specified out-band size, a new block is allocated using the +`block_allocator` and the allocation is performed from a newly-allocated block. + +If an allocation has bigger size than the specified out-band size, a new block +is allocated such that the allocation fits into this new block. This is referred +to as an *out-band allocation*. The out-band blocks are kept separately from +normal blocks. + +Just like arena, the dynamic arena does not support freeing of individual +objects. +*/ @(require_results) -dynamic_arena_allocator :: proc(pool: ^Dynamic_Arena) -> Allocator { +dynamic_arena_allocator :: proc(a: ^Dynamic_Arena) -> Allocator { return Allocator{ procedure = dynamic_arena_allocator_proc, - data = pool, + data = a, } } -dynamic_arena_destroy :: proc(pool: ^Dynamic_Arena) { - dynamic_arena_free_all(pool) - delete(pool.unused_blocks) - delete(pool.used_blocks) - delete(pool.out_band_allocations) - zero(pool, size_of(pool^)) +/* +Destroy a dynamic arena. +*/ +dynamic_arena_destroy :: proc(a: ^Dynamic_Arena) { + dynamic_arena_free_all(a) + delete(a.unused_blocks) + delete(a.used_blocks) + delete(a.out_band_allocations) + zero(a, size_of(a^)) } @(private="file") -_dynamic_arena_cycle_new_block :: proc(p: ^Dynamic_Arena, loc := #caller_location) -> (err: Allocator_Error) { - if p.block_allocator.procedure == nil { +_dynamic_arena_cycle_new_block :: proc(a: ^Dynamic_Arena, loc := #caller_location) -> (err: Allocator_Error) { + if a.block_allocator.procedure == nil { panic("You must call arena_init on a Pool before using it", loc) } - if p.current_block != nil { - append(&p.used_blocks, p.current_block, loc=loc) + if a.current_block != nil { + append(&a.used_blocks, a.current_block, loc=loc) } new_block: rawptr - if len(p.unused_blocks) > 0 { - new_block = pop(&p.unused_blocks) + if len(a.unused_blocks) > 0 { + new_block = pop(&a.unused_blocks) } else { data: []byte - data, err = p.block_allocator.procedure( - p.block_allocator.data, + data, err = a.block_allocator.procedure( + a.block_allocator.data, Allocator_Mode.Alloc, - p.block_size, - p.alignment, + a.block_size, + a.alignment, nil, 0, ) new_block = raw_data(data) } - p.bytes_left = p.block_size - p.current_pos = new_block - p.current_block = new_block + a.bytes_left = a.block_size + a.current_pos = new_block + a.current_block = new_block return } @@ -1435,6 +1951,11 @@ Buddy_Allocator :: struct { alignment: uint, } +/* +Buddy allocator. + +TODO +*/ @(require_results) buddy_allocator :: proc(b: ^Buddy_Allocator) -> Allocator { return Allocator{ From 167ced8ad161e0c8a84fd7b4fc3c94afff27236e Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sun, 8 Sep 2024 18:50:44 +1100 Subject: [PATCH 29/72] [mem]: Don't use named params for dynamic pool in tests --- core/mem/allocators.odin | 16 ++++++++-------- tests/core/mem/test_mem_dynamic_pool.odin | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index 972031a21..f1e45d1a1 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -1564,20 +1564,20 @@ arrays of blocks and out-band blocks. The blocks have the default size of will be aligned to a boundary specified by `alignment`. */ dynamic_arena_init :: proc( - a: ^Dynamic_Arena, + pool: ^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, ) { - a.block_size = block_size - a.out_band_size = out_band_size - a.alignment = alignment - a.block_allocator = block_allocator - a.out_band_allocations.allocator = array_allocator - a.unused_blocks.allocator = array_allocator - a.used_blocks.allocator = array_allocator + pool.block_size = block_size + pool.out_band_size = out_band_size + pool.alignment = alignment + pool.block_allocator = block_allocator + pool.out_band_allocations.allocator = array_allocator + pool.unused_blocks.allocator = array_allocator + pool.used_blocks.allocator = array_allocator } /* diff --git a/tests/core/mem/test_mem_dynamic_pool.odin b/tests/core/mem/test_mem_dynamic_pool.odin index d1086cfe6..fa204d3b1 100644 --- a/tests/core/mem/test_mem_dynamic_pool.odin +++ b/tests/core/mem/test_mem_dynamic_pool.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 = &pool, alignment = alignment) + mem.dynamic_pool_init(&pool, alignment = alignment) pool_allocator := mem.dynamic_pool_allocator(&pool) element, err := mem.alloc(num_bytes, alignment, pool_allocator) @@ -48,7 +48,7 @@ expect_pool_allocation_out_of_band :: proc(t: ^testing.T, num_bytes, out_band_si testing.expect(t, num_bytes >= out_band_size, "Sanity check failed, your test call is flawed! Make sure that num_bytes >= out_band_size!") pool: mem.Dynamic_Pool - mem.dynamic_pool_init(pool = &pool, out_band_size = out_band_size) + mem.dynamic_pool_init(&pool, out_band_size = out_band_size) pool_allocator := mem.dynamic_pool_allocator(&pool) element, err := mem.alloc(num_bytes, allocator = pool_allocator) From 5ae27c6ebce707aa6a153cf01ab658c6b1cbdabf Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Thu, 5 Sep 2024 18:00:35 +0200 Subject: [PATCH 30/72] wasm: support more vendor libraries Adds support for: - box2d - cgltf - stb image - stb rect pack --- core/bytes/bytes.odin | 4 +- core/mem/allocators.odin | 78 + core/os/os_js.odin | 89 +- core/strings/strings.odin | 14 +- vendor/box2d/box2d.odin | 22 +- vendor/box2d/box2d_wasm.odin | 4 + vendor/box2d/build_box2d.sh | 2 + vendor/box2d/lib/box2d_wasm.o | Bin 0 -> 433031 bytes vendor/box2d/lib/box2d_wasm_simd.o | Bin 0 -> 405135 bytes vendor/box2d/wasm.Makefile | 32 + vendor/cgltf/cgltf.odin | 5 + vendor/cgltf/cgltf_wasm.odin | 4 + vendor/cgltf/lib/cgltf_wasm.o | Bin 0 -> 112327 bytes vendor/cgltf/src/Makefile | 4 + vendor/libc/README.md | 12 + vendor/libc/assert.odin | 15 + vendor/libc/include/assert.h | 16 + vendor/libc/include/math.h | 21 + vendor/libc/include/stdio.h | 47 + vendor/libc/include/stdlib.h | 19 + vendor/libc/include/string.h | 21 + vendor/libc/libc.odin | 25 + vendor/libc/math.odin | 100 + vendor/libc/stdio.odin | 106 + vendor/libc/stdlib.odin | 119 ++ vendor/libc/string.odin | 111 + vendor/stb/image/stb_image.odin | 57 +- vendor/stb/image/stb_image_resize.odin | 5 + vendor/stb/image/stb_image_wasm.odin | 4 + vendor/stb/image/stb_image_write.odin | 22 +- vendor/stb/lib/stb_image_resize_wasm.o | Bin 0 -> 27646 bytes vendor/stb/lib/stb_image_wasm.o | Bin 0 -> 78144 bytes vendor/stb/lib/stb_image_write_wasm.o | Bin 0 -> 24259 bytes vendor/stb/lib/stb_rect_pack_wasm.o | Bin 0 -> 3683 bytes vendor/stb/lib/stb_sprintf_wasm.o | Bin 0 -> 13793 bytes vendor/stb/lib/stb_truetype_wasm.o | Bin 41425 -> 46482 bytes vendor/stb/rect_pack/stb_rect_pack.odin | 5 + vendor/stb/rect_pack/stb_rect_pack_wasm.odin | 4 + vendor/stb/sprintf/stb_sprintf.odin | 37 + vendor/stb/src/Makefile | 14 +- vendor/stb/src/stb_sprintf.c | 2 + vendor/stb/src/stb_sprintf.h | 1906 ++++++++++++++++++ vendor/stb/src/stb_truetype_wasm.c | 46 - vendor/stb/truetype/stb_truetype.odin | 7 +- vendor/stb/truetype/stb_truetype_wasm.odin | 80 +- 45 files changed, 2828 insertions(+), 231 deletions(-) create mode 100644 vendor/box2d/box2d_wasm.odin create mode 100755 vendor/box2d/lib/box2d_wasm.o create mode 100755 vendor/box2d/lib/box2d_wasm_simd.o create mode 100644 vendor/box2d/wasm.Makefile create mode 100644 vendor/cgltf/cgltf_wasm.odin create mode 100644 vendor/cgltf/lib/cgltf_wasm.o create mode 100644 vendor/libc/README.md create mode 100644 vendor/libc/assert.odin create mode 100644 vendor/libc/include/assert.h create mode 100644 vendor/libc/include/math.h create mode 100644 vendor/libc/include/stdio.h create mode 100644 vendor/libc/include/stdlib.h create mode 100644 vendor/libc/include/string.h create mode 100644 vendor/libc/libc.odin create mode 100644 vendor/libc/math.odin create mode 100644 vendor/libc/stdio.odin create mode 100644 vendor/libc/stdlib.odin create mode 100644 vendor/libc/string.odin create mode 100644 vendor/stb/image/stb_image_wasm.odin create mode 100644 vendor/stb/lib/stb_image_resize_wasm.o create mode 100644 vendor/stb/lib/stb_image_wasm.o create mode 100644 vendor/stb/lib/stb_image_write_wasm.o create mode 100644 vendor/stb/lib/stb_rect_pack_wasm.o create mode 100644 vendor/stb/lib/stb_sprintf_wasm.o create mode 100644 vendor/stb/rect_pack/stb_rect_pack_wasm.odin create mode 100644 vendor/stb/sprintf/stb_sprintf.odin create mode 100644 vendor/stb/src/stb_sprintf.c create mode 100644 vendor/stb/src/stb_sprintf.h delete mode 100644 vendor/stb/src/stb_truetype_wasm.c diff --git a/core/bytes/bytes.odin b/core/bytes/bytes.odin index 45eb44307..c0d25bcce 100644 --- a/core/bytes/bytes.odin +++ b/core/bytes/bytes.odin @@ -334,7 +334,7 @@ Inputs: Returns: - index: The index of the byte `c`, or -1 if it was not found. */ -index_byte :: proc(s: []byte, c: byte) -> (index: int) #no_bounds_check { +index_byte :: proc "contextless" (s: []byte, c: byte) -> (index: int) #no_bounds_check { i, l := 0, len(s) // Guard against small strings. On modern systems, it is ALWAYS @@ -469,7 +469,7 @@ Inputs: Returns: - index: The index of the byte `c`, or -1 if it was not found. */ -last_index_byte :: proc(s: []byte, c: byte) -> int #no_bounds_check { +last_index_byte :: proc "contextless" (s: []byte, c: byte) -> int #no_bounds_check { i := len(s) // Guard against small strings. On modern systems, it is ALWAYS diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index a5b93ad05..cbed5fbe3 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -1137,3 +1137,81 @@ buddy_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, return nil, nil } + +// An allocator that keeps track of allocation sizes and passes it along to resizes. +// This is useful if you are using a library that needs an equivalent of `realloc` but want to use +// the Odin allocator interface. +// +// You want to wrap your allocator into this one if you are trying to use any allocator that relies +// on the old size to work. +// +// The overhead of this allocator is an extra max(alignment, size_of(Header)) bytes allocated for each allocation, these bytes are +// used to store the size and original pointer. +Compat_Allocator :: struct { + parent: Allocator, +} + +compat_allocator_init :: proc(rra: ^Compat_Allocator, allocator := context.allocator) { + rra.parent = allocator +} + +compat_allocator :: proc(rra: ^Compat_Allocator) -> Allocator { + return Allocator{ + data = rra, + procedure = compat_allocator_proc, + } +} + +compat_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, + size, alignment: int, + old_memory: rawptr, old_size: int, + location := #caller_location) -> (data: []byte, err: Allocator_Error) { + size, old_size := size, old_size + + Header :: struct { + size: int, + ptr: rawptr, + } + + rra := (^Compat_Allocator)(allocator_data) + switch mode { + case .Alloc, .Alloc_Non_Zeroed: + a := max(alignment, size_of(Header)) + size += a + assert(size >= 0, "overflow") + + allocation := rra.parent.procedure(rra.parent.data, mode, size, alignment, old_memory, old_size, location) or_return + #no_bounds_check data = allocation[a:] + + ([^]Header)(raw_data(data))[-1] = { + size = size, + ptr = raw_data(allocation), + } + return + + case .Free: + header := ([^]Header)(old_memory)[-1] + return rra.parent.procedure(rra.parent.data, mode, size, alignment, header.ptr, header.size, location) + + case .Resize, .Resize_Non_Zeroed: + header := ([^]Header)(old_memory)[-1] + + a := max(alignment, size_of(header)) + size += a + assert(size >= 0, "overflow") + + allocation := rra.parent.procedure(rra.parent.data, mode, size, alignment, header.ptr, header.size, location) or_return + #no_bounds_check data = allocation[a:] + + ([^]Header)(raw_data(data))[-1] = { + size = size, + ptr = raw_data(allocation), + } + return + + case .Free_All, .Query_Info, .Query_Features: + return rra.parent.procedure(rra.parent.data, mode, size, alignment, old_memory, old_size, location) + + case: unreachable() + } +} diff --git a/core/os/os_js.odin b/core/os/os_js.odin index eb434c727..02821c3e3 100644 --- a/core/os/os_js.odin +++ b/core/os/os_js.odin @@ -3,33 +3,38 @@ package os import "base:runtime" +foreign import "odin_env" + @(require_results) is_path_separator :: proc(c: byte) -> bool { return c == '/' || c == '\\' } +Handle :: distinct u32 + +stdout: Handle = 1 +stderr: Handle = 2 + @(require_results) open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Error) { unimplemented("core:os procedure not supported on JS target") } close :: proc(fd: Handle) -> Error { - unimplemented("core:os procedure not supported on JS target") + return nil } flush :: proc(fd: Handle) -> (err: Error) { - unimplemented("core:os procedure not supported on JS target") + return nil } - - write :: proc(fd: Handle, data: []byte) -> (int, Error) { - unimplemented("core:os procedure not supported on JS target") -} - -@(private="file") -read_console :: proc(handle: Handle, b: []byte) -> (n: int, err: Error) { - unimplemented("core:os procedure not supported on JS target") + foreign odin_env { + @(link_name="write") + _write :: proc "contextless" (fd: Handle, p: []byte) --- + } + _write(fd, data) + return len(data), nil } read :: proc(fd: Handle, data: []byte) -> (int, Error) { @@ -45,19 +50,6 @@ file_size :: proc(fd: Handle) -> (i64, Error) { unimplemented("core:os procedure not supported on JS target") } - -@(private) -MAX_RW :: 1<<30 - -@(private) -pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) { - unimplemented("core:os procedure not supported on JS target") -} -@(private) -pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) { - unimplemented("core:os procedure not supported on JS target") -} - read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) { unimplemented("core:os procedure not supported on JS target") } @@ -65,16 +57,6 @@ write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) unimplemented("core:os procedure not supported on JS target") } -stdout: Handle = 1 -stderr: Handle = 2 - -@(require_results) -get_std_handle :: proc "contextless" (h: uint) -> Handle { - context = runtime.default_context() - unimplemented("core:os procedure not supported on JS target") -} - - @(require_results) exists :: proc(path: string) -> bool { unimplemented("core:os procedure not supported on JS target") @@ -90,9 +72,6 @@ is_dir :: proc(path: string) -> bool { unimplemented("core:os procedure not supported on JS target") } -// NOTE(tetra): GetCurrentDirectory is not thread safe with SetCurrentDirectory and GetFullPathName -//@private cwd_lock := win32.SRWLOCK{} // zero is initialized - @(require_results) get_current_directory :: proc(allocator := context.allocator) -> string { unimplemented("core:os procedure not supported on JS target") @@ -118,18 +97,6 @@ remove_directory :: proc(path: string) -> (err: Error) { } - -@(private, require_results) -is_abs :: proc(path: string) -> bool { - unimplemented("core:os procedure not supported on JS target") -} - -@(private, require_results) -fix_long_path :: proc(path: string) -> string { - unimplemented("core:os procedure not supported on JS target") -} - - link :: proc(old_name, new_name: string) -> (err: Error) { unimplemented("core:os procedure not supported on JS target") } @@ -169,7 +136,6 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F unimplemented("core:os procedure not supported on JS target") } -Handle :: distinct uintptr File_Time :: distinct u64 _Platform_Error :: enum i32 { @@ -254,12 +220,7 @@ WSAECONNRESET :: Platform_Error.WSAECONNRESET ERROR_FILE_IS_PIPE :: General_Error.File_Is_Pipe ERROR_FILE_IS_NOT_DIR :: General_Error.Not_Dir -// "Argv" arguments converted to Odin strings -args := _alloc_command_line_arguments() - - - - +args: []string @(require_results) last_write_time :: proc(fd: Handle) -> (File_Time, Error) { @@ -279,26 +240,14 @@ get_page_size :: proc() -> int { @(private, require_results) _processor_core_count :: proc() -> int { - unimplemented("core:os procedure not supported on JS target") + return 1 } exit :: proc "contextless" (code: int) -> ! { - context = runtime.default_context() - unimplemented("core:os procedure not supported on JS target") + unimplemented_contextless("core:os procedure not supported on JS target") } - - @(require_results) current_thread_id :: proc "contextless" () -> int { - context = runtime.default_context() - unimplemented("core:os procedure not supported on JS target") + return 0 } - - - -@(require_results) -_alloc_command_line_arguments :: proc() -> []string { - return nil -} - diff --git a/core/strings/strings.odin b/core/strings/strings.odin index b69c4a0e0..dbc84f8b7 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -93,7 +93,7 @@ Inputs: Returns: - res: A string created from the null-terminated byte pointer and length */ -string_from_null_terminated_ptr :: proc(ptr: [^]byte, len: int) -> (res: string) { +string_from_null_terminated_ptr :: proc "contextless" (ptr: [^]byte, len: int) -> (res: string) { s := string(ptr[:len]) s = truncate_to_byte(s, 0) return s @@ -139,7 +139,7 @@ NOTE: Failure to find the byte results in returning the entire string. Returns: - res: The truncated string */ -truncate_to_byte :: proc(str: string, b: byte) -> (res: string) { +truncate_to_byte :: proc "contextless" (str: string, b: byte) -> (res: string) { n := index_byte(str, b) if n < 0 { n = len(str) @@ -261,7 +261,7 @@ Inputs: Returns: - result: `-1` if `lhs` comes first, `1` if `rhs` comes first, or `0` if they are equal */ -compare :: proc(lhs, rhs: string) -> (result: int) { +compare :: proc "contextless" (lhs, rhs: string) -> (result: int) { return mem.compare(transmute([]byte)lhs, transmute([]byte)rhs) } /* @@ -1447,7 +1447,7 @@ Output: -1 */ -index_byte :: proc(s: string, c: byte) -> (res: int) { +index_byte :: proc "contextless" (s: string, c: byte) -> (res: int) { return #force_inline bytes.index_byte(transmute([]u8)s, c) } /* @@ -1482,7 +1482,7 @@ Output: -1 */ -last_index_byte :: proc(s: string, c: byte) -> (res: int) { +last_index_byte :: proc "contextless" (s: string, c: byte) -> (res: int) { return #force_inline bytes.last_index_byte(transmute([]u8)s, c) } /* @@ -1576,8 +1576,8 @@ Output: -1 */ -index :: proc(s, substr: string) -> (res: int) { - hash_str_rabin_karp :: proc(s: string) -> (hash: u32 = 0, pow: u32 = 1) { +index :: proc "contextless" (s, substr: string) -> (res: int) { + hash_str_rabin_karp :: proc "contextless" (s: string) -> (hash: u32 = 0, pow: u32 = 1) { for i := 0; i < len(s); i += 1 { hash = hash*PRIME_RABIN_KARP + u32(s[i]) } diff --git a/vendor/box2d/box2d.odin b/vendor/box2d/box2d.odin index 081e0861b..c1d789273 100644 --- a/vendor/box2d/box2d.odin +++ b/vendor/box2d/box2d.odin @@ -3,7 +3,11 @@ package vendor_box2d import "base:intrinsics" import "core:c" -@(private) VECTOR_EXT :: "avx2" when #config(VENDOR_BOX2D_ENABLE_AVX2, intrinsics.has_target_feature("avx2")) else "sse2" +when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 { + @(private) VECTOR_EXT :: "_simd" when #config(VENDOR_BOX2D_ENABLE_SIMD128, intrinsics.has_target_feature("simd128")) else "" +} else { + @(private) VECTOR_EXT :: "avx2" when #config(VENDOR_BOX2D_ENABLE_AVX2, intrinsics.has_target_feature("avx2")) else "sse2" +} when ODIN_OS == .Windows { @(private) LIB_PATH :: "lib/box2d_windows_amd64_" + VECTOR_EXT + ".lib" @@ -13,6 +17,8 @@ when ODIN_OS == .Windows { @(private) LIB_PATH :: "lib/box2d_darwin_amd64_" + VECTOR_EXT + ".a" } else when ODIN_ARCH == .amd64 { @(private) LIB_PATH :: "lib/box2d_other_amd64_" + VECTOR_EXT + ".a" +} else when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 { + @(private) LIB_PATH :: "lib/box2d_wasm" + VECTOR_EXT + ".o" } else { @(private) LIB_PATH :: "lib/box2d_other.a" } @@ -21,8 +27,16 @@ when !#exists(LIB_PATH) { #panic("Could not find the compiled box2d libraries at \"" + LIB_PATH + "\", they can be compiled by running the `build.sh` script at `" + ODIN_ROOT + "vendor/box2d/build_box2d.sh\"`") } -foreign import lib { - LIB_PATH, +when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 { + when VECTOR_EXT == "_simd" { + foreign import lib "lib/box2d_wasm_simd.o" + } else { + foreign import lib "lib/box2d_wasm.o" + } +} else { + foreign import lib { + LIB_PATH, + } } @@ -1520,4 +1534,4 @@ IsValid :: proc{ Joint_IsValid, IsValidRay, -} \ No newline at end of file +} diff --git a/vendor/box2d/box2d_wasm.odin b/vendor/box2d/box2d_wasm.odin new file mode 100644 index 000000000..eab369a0d --- /dev/null +++ b/vendor/box2d/box2d_wasm.odin @@ -0,0 +1,4 @@ +//+build wasm32, wasm64p32 +package vendor_box2d + +@(require) import _ "vendor:libc" diff --git a/vendor/box2d/build_box2d.sh b/vendor/box2d/build_box2d.sh index 4fa64faa0..9513d3113 100755 --- a/vendor/box2d/build_box2d.sh +++ b/vendor/box2d/build_box2d.sh @@ -68,5 +68,7 @@ esac cd .. +make -f wasm.Makefile + rm -rf v3.0.0.tar.gz rm -rf box2d-3.0.0 diff --git a/vendor/box2d/lib/box2d_wasm.o b/vendor/box2d/lib/box2d_wasm.o new file mode 100755 index 0000000000000000000000000000000000000000..15d71c5a141c1667db29fedc403421da7946346c GIT binary patch literal 433031 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%0Dk3gW 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 -KL$R)fw(cK`{%Z3iDM!VDR5gW1l+ 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

Pz4<5Wh51k(GV!1V%=d6B8H@u`n>4o4~k`k>%S2 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

BgNJE6fO1LHF&FeyM1K4`_+ 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

BS_!M}- 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 literal 0 HcmV?d00001 diff --git a/vendor/box2d/wasm.Makefile b/vendor/box2d/wasm.Makefile new file mode 100644 index 000000000..929b61aea --- /dev/null +++ b/vendor/box2d/wasm.Makefile @@ -0,0 +1,32 @@ +# Custom Makefile to build box2d for Odin's WASM targets. +# I tried to make a cmake toolchain file for this / use cmake but this is far easier. +# NOTE: We are pretending to be emscripten to box2d so it takes WASM code paths, but we don't actually use emscripten. + +# CC = $(shell brew --prefix llvm)/bin/clang +# LD = $(shell brew --prefix llvm)/bin/wasm-ld + +VERSION = 3.0.0 +SRCS = $(wildcard box2d-$(VERSION)/src/*.c) +OBJS_SIMD = $(SRCS:.c=_simd.o) +OBJS = $(SRCS:.c=.o) +SYSROOT = $(shell odin root)/vendor/libc +CFLAGS = -Ibox2d-$(VERSION)/include -Ibox2d-$(VERSION)/Extern/simde --target=wasm32 -D__EMSCRIPTEN__ -DNDEBUG -O3 --sysroot=$(SYSROOT) + +all: lib/box2d_wasm.o lib/box2d_wasm_simd.o clean + +%.o: %.c + $(CC) -c $(CFLAGS) $< -o $@ + +%_simd.o: %.c + $(CC) -c $(CFLAGS) -msimd128 $< -o $@ + +lib/box2d_wasm.o: $(OBJS) + $(LD) -r -o lib/box2d_wasm.o $(OBJS) + +lib/box2d_wasm_simd.o: $(OBJS_SIMD) + $(LD) -r -o lib/box2d_wasm_simd.o $(OBJS_SIMD) + +clean: + rm -rf $(OBJS) $(OBJS_SIMD) + +.PHONY: clean diff --git a/vendor/cgltf/cgltf.odin b/vendor/cgltf/cgltf.odin index f4518360d..a24c36d64 100644 --- a/vendor/cgltf/cgltf.odin +++ b/vendor/cgltf/cgltf.odin @@ -5,6 +5,7 @@ LIB :: ( "lib/cgltf.lib" when ODIN_OS == .Windows else "lib/cgltf.a" when ODIN_OS == .Linux else "lib/darwin/cgltf.a" when ODIN_OS == .Darwin + else "lib/cgltf_wasm.o" when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 else "" ) @@ -13,7 +14,11 @@ when LIB != "" { // Windows library is shipped with the compiler, so a Windows specific message should not be needed. #panic("Could not find the compiled cgltf library, it can be compiled by running `make -C \"" + ODIN_ROOT + "vendor/cgltf/src\"`") } +} +when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 { + foreign import lib "lib/cgltf_wasm.o" +} else when LIB != "" { foreign import lib { LIB } } else { foreign import lib "system:cgltf" diff --git a/vendor/cgltf/cgltf_wasm.odin b/vendor/cgltf/cgltf_wasm.odin new file mode 100644 index 000000000..f2da86a2c --- /dev/null +++ b/vendor/cgltf/cgltf_wasm.odin @@ -0,0 +1,4 @@ +//+build wasm32, wasm64p32 +package cgltf + +@(require) import _ "vendor:libc" diff --git a/vendor/cgltf/lib/cgltf_wasm.o b/vendor/cgltf/lib/cgltf_wasm.o new file mode 100644 index 0000000000000000000000000000000000000000..54346d176c8e8c1335d00f418d7f554fe690e3f0 GIT binary patch literal 112327 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 ! { + context = g_ctx + loc := runtime.Source_Code_Location{ + file_path = string(file), + line = line, + column = 0, + procedure = string(func), + } + context.assertion_failure_proc("runtime assertion", string(expr), loc) +} diff --git a/vendor/libc/include/assert.h b/vendor/libc/include/assert.h new file mode 100644 index 000000000..a6fb6c696 --- /dev/null +++ b/vendor/libc/include/assert.h @@ -0,0 +1,16 @@ +#ifdef NDEBUG +#define assert(e) ((void)0) +#else + +#ifdef __FILE_NAME__ +#define __ASSERT_FILE_NAME __FILE_NAME__ +#else /* __FILE_NAME__ */ +#define __ASSERT_FILE_NAME __FILE__ +#endif /* __FILE_NAME__ */ + +void __odin_libc_assert_fail(const char *, const char *, int, const char *); + +#define assert(e) \ + (__builtin_expect(!(e), 0) ? __odin_libc_assert_fail(__func__, __ASSERT_FILE_NAME, __LINE__, #e) : (void)0) + +#endif /* NDEBUG */ diff --git a/vendor/libc/include/math.h b/vendor/libc/include/math.h new file mode 100644 index 000000000..3f60d698f --- /dev/null +++ b/vendor/libc/include/math.h @@ -0,0 +1,21 @@ +#include + +float sqrtf(float); +float cosf(float); +float sinf(float); +float atan2f(float, float); +bool isnan(float); +bool isinf(float); +double floor(double x); +double ceil(double x); +double sqrt(double x); +double pow(double x, double y); +double fmod(double x, double y); +double cos(double x); +double acos(double x); +double fabs(double x); +int abs(int); +double ldexp(double, int); +double exp(double); +float log(float); +float sin(float); diff --git a/vendor/libc/include/stdio.h b/vendor/libc/include/stdio.h new file mode 100644 index 000000000..807437f3c --- /dev/null +++ b/vendor/libc/include/stdio.h @@ -0,0 +1,47 @@ +#include +#include + +#pragma once + +typedef struct {} FILE; + +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 + +#define stdout ((FILE *)2) +#define stderr ((FILE *)3) + +FILE *fopen(const char *, char *); +int fclose(FILE *); +int fseek(FILE *, long, int); +long ftell(FILE *); +size_t fread(void *, size_t, size_t, FILE *); +size_t fwrite(const void *, size_t, size_t, FILE *); + +int vfprintf(FILE *, const char *, va_list); +int vsnprintf(char *, size_t, const char *, va_list); + +static inline int snprintf(char *buf, size_t size, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + int result = vsnprintf(buf, size, fmt, args); + va_end(args); + return result; +} + +static inline int fprintf(FILE *f, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + int result = vfprintf(f, fmt, args); + va_end(args); + return result; +} + +static inline int printf(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + int result = vfprintf(stdout, fmt, args); + va_end(args); + return result; +} diff --git a/vendor/libc/include/stdlib.h b/vendor/libc/include/stdlib.h new file mode 100644 index 000000000..22cfc528b --- /dev/null +++ b/vendor/libc/include/stdlib.h @@ -0,0 +1,19 @@ +#include + +void *malloc(size_t size); + +void *aligned_alloc(size_t alignment, size_t size); + +void free(void *); + +void *realloc(void *, size_t); + +void qsort(void* base, size_t num, size_t size, int (*compare)(const void*, const void*)); + +int atoi(const char *); +long atol(const char *); +long long atoll(const char *); + +double atof(const char *); + +long strtol(const char *, char **, int); diff --git a/vendor/libc/include/string.h b/vendor/libc/include/string.h new file mode 100644 index 000000000..4571f9454 --- /dev/null +++ b/vendor/libc/include/string.h @@ -0,0 +1,21 @@ +#include + +void *memcpy(void *, const void *, size_t); +void *memset(void *, int, size_t); +void *memmove(void *, void *, size_t); +int memcmp(const void *, const void *, size_t); + +unsigned long strlen(const char *str); + +char *strchr(const char *, int); +char *strrchr(const char *, int); + +char *strncpy(char *, const char *, size_t); +char *strcpy(char *, const char *); + +size_t strcspn(const char *, const char *); + +int strcmp(const char *, const char *); +int strncmp(const char *, const char *, size_t); + +char *strstr(const char *, const char *); diff --git a/vendor/libc/libc.odin b/vendor/libc/libc.odin new file mode 100644 index 000000000..00d687109 --- /dev/null +++ b/vendor/libc/libc.odin @@ -0,0 +1,25 @@ +package odin_libc + +import "base:runtime" + +import "core:mem" + +@(private) +g_ctx: runtime.Context +@(private) +g_allocator: mem.Compat_Allocator + +@(init) +init_context :: proc() { + g_ctx = context + + // Wrapping the allocator with the mem.Compat_Allocator so we can + // mimic the realloc semantics. + mem.compat_allocator_init(&g_allocator, g_ctx.allocator) + g_ctx.allocator = mem.compat_allocator(&g_allocator) +} + +// NOTE: the allocator must respect an `old_size` of `-1` on resizes! +set_context :: proc(ctx := context) { + g_ctx = ctx +} diff --git a/vendor/libc/math.odin b/vendor/libc/math.odin new file mode 100644 index 000000000..59f42dd67 --- /dev/null +++ b/vendor/libc/math.odin @@ -0,0 +1,100 @@ +package odin_libc + +import "base:builtin" + +import "core:math" + +@(require, linkage="strong", link_name="sqrtf") +sqrtf :: proc "c" (v: f32) -> f32 { + return math.sqrt(v) +} + +@(require, linkage="strong", link_name="cosf") +cosf :: proc "c" (v: f32) -> f32 { + return math.cos(v) +} + +@(require, linkage="strong", link_name="sinf") +sinf :: proc "c" (v: f32) -> f32 { + return math.sin(v) +} + +@(require, linkage="strong", link_name="atan2f") +atan2f :: proc "c" (v: f32, v2: f32) -> f32 { + return math.atan2(v, v2) +} + +@(require, linkage="strong", link_name="isnan") +isnan :: proc "c" (v: f32) -> bool { + return math.is_nan(v) +} + +@(require, linkage="strong", link_name="isinf") +isinf :: proc "c" (v: f32) -> bool { + return math.is_inf(v) +} + +@(require, linkage="strong", link_name="sqrt") +sqrt :: proc "c" (x: f64) -> f64 { + return math.sqrt(x) +} + +@(require, linkage="strong", link_name="floor") +floor :: proc "c" (x: f64) -> f64 { + return math.floor(x) +} + +@(require, linkage="strong", link_name="ceil") +ceil :: proc "c" (x: f64) -> f64 { + return math.ceil(x) +} + +@(require, linkage="strong", link_name="pow") +pow :: proc "c" (x, y: f64) -> f64 { + return math.pow(x, y) +} + +@(require, linkage="strong", link_name="fmod") +fmod :: proc "c" (x, y: f64) -> f64 { + return math.mod(x, y) +} + +@(require, linkage="strong", link_name="cos") +cos :: proc "c" (x: f64) -> f64 { + return math.cos(x) +} + +@(require, linkage="strong", link_name="acos") +acos :: proc "c" (x: f64) -> f64 { + return math.acos(x) +} + +@(require, linkage="strong", link_name="fabs") +fabs :: proc "c" (x: f64) -> f64 { + return math.abs(x) +} + +@(require, linkage="strong", link_name="abs") +abs :: proc "c" (x: i32) -> i32 { + return builtin.abs(x) +} + +@(require, linkage="strong", link_name="ldexp") +ldexp :: proc "c" (x: f64, y: i32) -> f64{ + return math.ldexp(x, int(y)) +} + +@(require, linkage="strong", link_name="exp") +exp :: proc "c" (x: f64) -> f64 { + return math.exp(x) +} + +@(require, linkage="strong", link_name="log") +log :: proc "c" (x: f32) -> f32 { + return math.ln(x) +} + +@(require, linkage="strong", link_name="sin") +sin :: proc "c" (x: f32) -> f32 { + return math.sin(x) +} diff --git a/vendor/libc/stdio.odin b/vendor/libc/stdio.odin new file mode 100644 index 000000000..10b95b96b --- /dev/null +++ b/vendor/libc/stdio.odin @@ -0,0 +1,106 @@ +package odin_libc + +import "core:c" +import "core:io" +import "core:os" + +import stb "vendor:stb/sprintf" + +FILE :: uintptr + +@(require, linkage="strong", link_name="fopen") +fopen :: proc "c" (path: cstring, mode: cstring) -> FILE { + context = g_ctx + unimplemented("odin_libc.fopen") +} + +@(require, linkage="strong", link_name="fseek") +fseek :: proc "c" (file: FILE, offset: c.long, whence: i32) -> i32 { + context = g_ctx + handle := os.Handle(file-1) + _, err := os.seek(handle, i64(offset), int(whence)) + if err != nil { + return -1 + } + return 0 +} + +@(require, linkage="strong", link_name="ftell") +ftell :: proc "c" (file: FILE) -> c.long { + context = g_ctx + handle := os.Handle(file-1) + off, err := os.seek(handle, 0, os.SEEK_CUR) + if err != nil { + return -1 + } + return c.long(off) +} + +@(require, linkage="strong", link_name="fclose") +fclose :: proc "c" (file: FILE) -> i32 { + context = g_ctx + handle := os.Handle(file-1) + if os.close(handle) != nil { + return -1 + } + return 0 +} + +@(require, linkage="strong", link_name="fread") +fread :: proc "c" (buffer: [^]byte, size: uint, count: uint, file: FILE) -> uint { + context = g_ctx + handle := os.Handle(file-1) + n, _ := os.read(handle, buffer[:min(size, count)]) + return uint(max(0, n)) +} + +@(require, linkage="strong", link_name="fwrite") +fwrite :: proc "c" (buffer: [^]byte, size: uint, count: uint, file: FILE) -> uint { + context = g_ctx + handle := os.Handle(file-1) + n, _ := os.write(handle, buffer[:min(size, count)]) + return uint(max(0, n)) +} + +@(require, linkage="strong", link_name="vsnprintf") +vsnprintf :: proc "c" (buf: [^]byte, count: uint, fmt: cstring, args: ^c.va_list) -> i32 { + i32_count := i32(count) + assert_contextless(i32_count >= 0) + return stb.vsnprintf(buf, i32_count, fmt, args) +} + +@(require, linkage="strong", link_name="vfprintf") +vfprintf :: proc "c" (file: FILE, fmt: cstring, args: ^c.va_list) -> i32 { + context = g_ctx + + handle := os.Handle(file-1) + + MAX_STACK :: 4096 + + buf: []byte + stack_buf: [MAX_STACK]byte = --- + { + n := stb.vsnprintf(&stack_buf[0], MAX_STACK, fmt, args) + if n <= 0 { + return n + } + + if n >= MAX_STACK { + buf = make([]byte, n) + n2 := stb.vsnprintf(raw_data(buf), i32(len(buf)), fmt, args) + assert(n == n2) + } else { + buf = stack_buf[:n] + } + } + defer if len(buf) > MAX_STACK { + delete(buf) + } + + _, err := io.write_full(os.stream_from_handle(handle), buf) + if err != nil { + return -1 + } + + return i32(len(buf)) +} diff --git a/vendor/libc/stdlib.odin b/vendor/libc/stdlib.odin new file mode 100644 index 000000000..f898de619 --- /dev/null +++ b/vendor/libc/stdlib.odin @@ -0,0 +1,119 @@ +package odin_libc + +import "base:runtime" + +import "core:c" +import "core:slice" +import "core:sort" +import "core:strconv" +import "core:strings" + +@(require, linkage="strong", link_name="malloc") +malloc :: proc "c" (size: uint) -> rawptr { + context = g_ctx + ptr, err := runtime.mem_alloc_non_zeroed(int(size)) + assert(err == nil, "allocation failure") + return raw_data(ptr) +} + +@(require, linkage="strong", link_name="aligned_alloc") +aligned_alloc :: proc "c" (alignment: uint, size: uint) -> rawptr { + context = g_ctx + ptr, err := runtime.mem_alloc_non_zeroed(int(size), int(alignment)) + assert(err == nil, "allocation failure") + return raw_data(ptr) +} + +@(require, linkage="strong", link_name="free") +free :: proc "c" (ptr: rawptr) { + context = g_ctx + runtime.mem_free(ptr) +} + +@(require, linkage="strong", link_name="realloc") +realloc :: proc "c" (ptr: rawptr, new_size: uint) -> rawptr { + context = g_ctx + // -1 for the old_size, assumed to be wrapped with the mem.Compat_Allocator to get the right size. + // Note that realloc does not actually care about alignment and is allowed to just align it to something + // else than the original allocation. + ptr, err := runtime.non_zero_mem_resize(ptr, -1, int(new_size)) + assert(err != nil, "realloc failure") + return raw_data(ptr) +} + +@(require, linkage="strong", link_name="qsort") +qsort :: proc "c" (base: rawptr, num: uint, size: uint, cmp: proc "c" (a, b: rawptr) -> i32) { + context = g_ctx + + Inputs :: struct { + base: rawptr, + num: uint, + size: uint, + cmp: proc "c" (a, b: rawptr) -> i32, + } + + sort.sort({ + collection = &Inputs{base, num, size, cmp}, + len = proc(it: sort.Interface) -> int { + inputs := (^Inputs)(it.collection) + return int(inputs.num) + }, + less = proc(it: sort.Interface, i, j: int) -> bool { + inputs := (^Inputs)(it.collection) + a := rawptr(uintptr(inputs.base) + (uintptr(i) * uintptr(inputs.size))) + b := rawptr(uintptr(inputs.base) + (uintptr(j) * uintptr(inputs.size))) + return inputs.cmp(a, b) < 0 + }, + swap = proc(it: sort.Interface, i, j: int) { + inputs := (^Inputs)(it.collection) + + a := rawptr(uintptr(inputs.base) + (uintptr(i) * uintptr(inputs.size))) + b := rawptr(uintptr(inputs.base) + (uintptr(j) * uintptr(inputs.size))) + + slice.ptr_swap_non_overlapping(a, b, int(inputs.size)) + }, + }) +} + +@(require, linkage="strong", link_name="atoi") +atoi :: proc "c" (str: cstring) -> i32 { + return i32(atoll(str)) +} + +@(require, linkage="strong", link_name="atol") +atol :: proc "c" (str: cstring) -> c.long { + return c.long(atoll(str)) +} + +@(require, linkage="strong", link_name="atoll") +atoll :: proc "c" (str: cstring) -> c.longlong { + context = g_ctx + + sstr := string(str) + sstr = strings.trim_left_space(sstr) + i, _ := strconv.parse_i64_of_base(sstr, 10) + return c.longlong(i) +} + +@(require, linkage="strong", link_name="atof") +atof :: proc "c" (str: cstring) -> f64 { + context = g_ctx + + sstr := string(str) + sstr = strings.trim_left_space(sstr) + f, _ := strconv.parse_f64(sstr) + return f +} + +@(require, linkage="strong", link_name="strtol") +strtol :: proc "c" (str: cstring, str_end: ^cstring, base: i32) -> c.long { + context = g_ctx + + sstr := string(str) + sstr = strings.trim_left_space(sstr) + + n: int + i, _ := strconv.parse_i64_of_base(sstr, int(base), &n) + str_end ^= cstring(raw_data(sstr)[n:]) + return c.long(clamp(i, i64(min(c.long)), i64(max(c.long)))) +} diff --git a/vendor/libc/string.odin b/vendor/libc/string.odin new file mode 100644 index 000000000..1ab0803da --- /dev/null +++ b/vendor/libc/string.odin @@ -0,0 +1,111 @@ +package odin_libc + +import "base:intrinsics" + +import "core:c" +import "core:strings" +import "core:mem" + +// NOTE: already defined by Odin. +// void *memcpy(void *, const void *, size_t); +// void *memset(void *, int, size_t); + +@(require, linkage="strong", link_name="memcmp") +memcmp :: proc "c" (lhs: [^]byte, rhs: [^]byte, count: uint) -> i32 { + icount := int(count) + assert_contextless(icount >= 0) + return i32(mem.compare(lhs[:icount], rhs[:icount])) +} + +@(require, linkage="strong", link_name="strlen") +strlen :: proc "c" (str: cstring) -> c.ulong { + return c.ulong(len(str)) +} + +@(require, linkage="strong", link_name="strchr") +strchr :: proc "c" (str: cstring, ch: i32) -> cstring { + bch := u8(ch) + sstr := string(str) + if bch == 0 { + return cstring(raw_data(sstr)[len(sstr):]) + } + + idx := strings.index_byte(sstr, bch) + if idx < 0 { + return nil + } + + return cstring(raw_data(sstr)[idx:]) +} + +@(require, linkage="strong", link_name="strrchr") +strrchr :: proc "c" (str: cstring, ch: i32) -> cstring { + bch := u8(ch) + sstr := string(str) + if bch == 0 { + return cstring(raw_data(sstr)[len(sstr):]) + } + + idx := strings.last_index_byte(sstr, bch) + if idx < 0 { + return nil + } + + return cstring(raw_data(sstr)[idx:]) +} + +@(require, linkage="strong", link_name="strncpy") +strncpy :: proc "c" (dst: [^]byte, src: cstring, count: uint) -> cstring { + icount := int(count) + assert_contextless(icount >= 0) + cnt := min(len(src), icount) + intrinsics.mem_copy_non_overlapping(dst, rawptr(src), cnt) + intrinsics.mem_zero(dst, icount-cnt) + return cstring(dst) +} + +@(require, linkage="strong", link_name="strcpy") +strcpy :: proc "c" (dst: [^]byte, src: cstring) -> cstring { + intrinsics.mem_copy_non_overlapping(dst, rawptr(src), len(src)+1) + return cstring(dst) +} + +@(require, linkage="strong", link_name="strcspn") +strcspn :: proc "c" (dst: cstring, src: cstring) -> uint { + context = g_ctx + sdst := string(dst) + idx := strings.index_any(sdst, string(src)) + if idx == -1 { + return len(sdst) + } + return uint(idx) +} + +@(require, linkage="strong", link_name="strncmp") +strncmp :: proc "c" (lhs: cstring, rhs: cstring, count: uint) -> i32 { + icount := int(count) + assert_contextless(icount >= 0) + lhss := strings.string_from_null_terminated_ptr(([^]byte)(lhs), icount) + rhss := strings.string_from_null_terminated_ptr(([^]byte)(rhs), icount) + return i32(strings.compare(lhss, rhss)) +} + +@(require, linkage="strong", link_name="strcmp") +strcmp :: proc "c" (lhs: cstring, rhs: cstring) -> i32 { + return i32(strings.compare(string(lhs), string(rhs))) +} + +@(require, linkage="strong", link_name="strstr") +strstr :: proc "c" (str: cstring, substr: cstring) -> cstring { + if substr == "" { + return str + } + + idx := strings.index(string(str), string(substr)) + if idx < 0 { + return nil + } + + return cstring(([^]byte)(str)[idx:]) +} + diff --git a/vendor/stb/image/stb_image.odin b/vendor/stb/image/stb_image.odin index 828a1c2bd..85d612354 100644 --- a/vendor/stb/image/stb_image.odin +++ b/vendor/stb/image/stb_image.odin @@ -7,6 +7,7 @@ LIB :: ( "../lib/stb_image.lib" when ODIN_OS == .Windows else "../lib/stb_image.a" when ODIN_OS == .Linux else "../lib/darwin/stb_image.a" when ODIN_OS == .Darwin + else "../lib/stb_image_wasm.o" when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 else "" ) @@ -15,12 +16,19 @@ when LIB != "" { // The STB libraries are shipped with the compiler on Windows so a Windows specific message should not be needed. #panic("Could not find the compiled STB libraries, they can be compiled by running `make -C \"" + ODIN_ROOT + "vendor/stb/src\"`") } +} +when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 { + foreign import stbi "../lib/stb_image_wasm.o" + foreign import stbi { LIB } +} else when LIB != "" { foreign import stbi { LIB } } else { foreign import stbi "system:stb_image" } +NO_STDIO :: ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 + #assert(size_of(c.int) == size_of(b32)) #assert(size_of(b32) == size_of(c.int)) @@ -33,14 +41,48 @@ Io_Callbacks :: struct { eof: proc "c" (user: rawptr) -> c.int, // returns nonzero if we are at end of file/data } +when !NO_STDIO { + @(default_calling_convention="c", link_prefix="stbi_") + foreign stbi { + //////////////////////////////////// + // + // 8-bits-per-channel interface + // + load :: proc(filename: cstring, x, y, channels_in_file: ^c.int, desired_channels: c.int) -> [^]byte --- + load_from_file :: proc(f: ^c.FILE, x, y, channels_in_file: ^c.int, desired_channels: c.int) -> [^]byte --- + + //////////////////////////////////// + // + // 16-bits-per-channel interface + // + load_16 :: proc(filename: cstring, x, y, channels_in_file: ^c.int, desired_channels: c.int) -> [^]u16 --- + load_16_from_file :: proc(f: ^c.FILE, x, y, channels_in_file: ^c.int, desired_channels: c.int) -> [^]u16 --- + + //////////////////////////////////// + // + // float-per-channel interface + // + loadf :: proc(filename: cstring, x, y, channels_in_file: ^c.int, desired_channels: c.int) -> [^]f32 --- + loadf_from_file :: proc(f: ^c.FILE, x, y, channels_in_file: ^c.int, desired_channels: c.int) -> [^]f32 --- + + is_hdr :: proc(filename: cstring) -> c.int --- + is_hdr_from_file :: proc(f: ^c.FILE) -> c.int --- + + // get image dimensions & components without fully decoding + info :: proc(filename: cstring, x, y, comp: ^c.int) -> c.int --- + info_from_file :: proc(f: ^c.FILE, x, y, comp: ^c.int) -> c.int --- + + is_16_bit :: proc(filename: cstring) -> b32 --- + is_16_bit_from_file :: proc(f: ^c.FILE) -> b32 --- + } +} + @(default_calling_convention="c", link_prefix="stbi_") foreign stbi { //////////////////////////////////// // // 8-bits-per-channel interface // - load :: proc(filename: cstring, x, y, channels_in_file: ^c.int, desired_channels: c.int) -> [^]byte --- - load_from_file :: proc(f: ^c.FILE, x, y, channels_in_file: ^c.int, desired_channels: c.int) -> [^]byte --- load_from_memory :: proc(buffer: [^]byte, len: c.int, x, y, channels_in_file: ^c.int, desired_channels: c.int) -> [^]byte --- load_from_callbacks :: proc(clbk: ^Io_Callbacks, user: rawptr, x, y, channels_in_file: ^c.int, desired_channels: c.int) -> [^]byte --- @@ -50,8 +92,6 @@ foreign stbi { // // 16-bits-per-channel interface // - load_16 :: proc(filename: cstring, x, y, channels_in_file: ^c.int, desired_channels: c.int) -> [^]u16 --- - load_16_from_file :: proc(f: ^c.FILE, x, y, channels_in_file: ^c.int, desired_channels: c.int) -> [^]u16 --- load_16_from_memory :: proc(buffer: [^]byte, len: c.int, x, y, channels_in_file: ^c.int, desired_channels: c.int) -> [^]u16 --- load_16_from_callbacks :: proc(clbk: ^Io_Callbacks, x, y, channels_in_file: ^c.int, desired_channels: c.int) -> [^]u16 --- @@ -59,8 +99,6 @@ foreign stbi { // // float-per-channel interface // - loadf :: proc(filename: cstring, x, y, channels_in_file: ^c.int, desired_channels: c.int) -> [^]f32 --- - loadf_from_file :: proc(f: ^c.FILE, x, y, channels_in_file: ^c.int, desired_channels: c.int) -> [^]f32 --- loadf_from_memory :: proc(buffer: [^]byte, len: c.int, x, y, channels_in_file: ^c.int, desired_channels: c.int) -> [^]f32 --- loadf_from_callbacks :: proc(clbk: ^Io_Callbacks, user: rawptr, x, y, channels_in_file: ^c.int, desired_channels: c.int) -> [^]f32 --- @@ -73,9 +111,6 @@ foreign stbi { is_hdr_from_callbacks :: proc(clbk: ^Io_Callbacks, user: rawptr) -> c.int --- is_hdr_from_memory :: proc(buffer: [^]byte, len: c.int) -> c.int --- - is_hdr :: proc(filename: cstring) -> c.int --- - is_hdr_from_file :: proc(f: ^c.FILE) -> c.int --- - // get a VERY brief reason for failure // NOT THREADSAFE failure_reason :: proc() -> cstring --- @@ -84,13 +119,9 @@ foreign stbi { image_free :: proc(retval_from_load: rawptr) --- // get image dimensions & components without fully decoding - info :: proc(filename: cstring, x, y, comp: ^c.int) -> c.int --- - info_from_file :: proc(f: ^c.FILE, x, y, comp: ^c.int) -> c.int --- info_from_memory :: proc(buffer: [^]byte, len: c.int, x, y, comp: ^c.int) -> c.int --- info_from_callbacks :: proc(clbk: ^Io_Callbacks, user: rawptr, x, y, comp: ^c.int) -> c.int --- - is_16_bit :: proc(filename: cstring) -> b32 --- - is_16_bit_from_file :: proc(f: ^c.FILE) -> b32 --- is_16_bit_from_memory :: proc(buffer: [^]byte, len: c.int) -> c.int --- // for image formats that explicitly notate that they have premultiplied alpha, diff --git a/vendor/stb/image/stb_image_resize.odin b/vendor/stb/image/stb_image_resize.odin index e22b587b2..a37c2e243 100644 --- a/vendor/stb/image/stb_image_resize.odin +++ b/vendor/stb/image/stb_image_resize.odin @@ -7,6 +7,7 @@ RESIZE_LIB :: ( "../lib/stb_image_resize.lib" when ODIN_OS == .Windows else "../lib/stb_image_resize.a" when ODIN_OS == .Linux else "../lib/darwin/stb_image_resize.a" when ODIN_OS == .Darwin + else "../lib/stb_image_resize_wasm.o" when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 else "" ) @@ -15,7 +16,11 @@ when RESIZE_LIB != "" { // The STB libraries are shipped with the compiler on Windows so a Windows specific message should not be needed. #panic("Could not find the compiled STB libraries, they can be compiled by running `make -C \"" + ODIN_ROOT + "vendor/stb/src\"`") } +} +when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 { + foreign import lib "../lib/stb_image_resize_wasm.o" +} else when RESIZE_LIB != "" { foreign import lib { RESIZE_LIB } } else { foreign import lib "system:stb_image_resize" diff --git a/vendor/stb/image/stb_image_wasm.odin b/vendor/stb/image/stb_image_wasm.odin new file mode 100644 index 000000000..77bb44f02 --- /dev/null +++ b/vendor/stb/image/stb_image_wasm.odin @@ -0,0 +1,4 @@ +//+build wasm32, wasm64p32 +package stb_image + +@(require) import _ "vendor:libc" diff --git a/vendor/stb/image/stb_image_write.odin b/vendor/stb/image/stb_image_write.odin index f030f1e28..a0c0b57a0 100644 --- a/vendor/stb/image/stb_image_write.odin +++ b/vendor/stb/image/stb_image_write.odin @@ -7,6 +7,7 @@ WRITE_LIB :: ( "../lib/stb_image_write.lib" when ODIN_OS == .Windows else "../lib/stb_image_write.a" when ODIN_OS == .Linux else "../lib/darwin/stb_image_write.a" when ODIN_OS == .Darwin + else "../lib/stb_image_write_wasm.o" when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 else "" ) @@ -15,7 +16,11 @@ when WRITE_LIB != "" { // The STB libraries are shipped with the compiler on Windows so a Windows specific message should not be needed. #panic("Could not find the compiled STB libraries, they can be compiled by running `make -C \"" + ODIN_ROOT + "vendor/stb/src\"`") } +} +when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 { + foreign import stbiw "../lib/stb_image_write_wasm.o" +} else when WRITE_LIB != "" { foreign import stbiw { WRITE_LIB } } else { foreign import stbiw "system:stb_image_write" @@ -25,12 +30,6 @@ write_func :: proc "c" (ctx: rawptr, data: rawptr, size: c.int) @(default_calling_convention="c", link_prefix="stbi_") foreign stbiw { - write_png :: proc(filename: cstring, w, h, comp: c.int, data: rawptr, stride_in_bytes: c.int) -> c.int --- - write_bmp :: proc(filename: cstring, w, h, comp: c.int, data: rawptr) -> c.int --- - write_tga :: proc(filename: cstring, w, h, comp: c.int, data: rawptr) -> c.int --- - write_hdr :: proc(filename: cstring, w, h, comp: c.int, data: [^]f32) -> c.int --- - write_jpg :: proc(filename: cstring, w, h, comp: c.int, data: rawptr, quality: c.int /*0..=100*/) -> c.int --- - write_png_to_func :: proc(func: write_func, ctx: rawptr, w, h, comp: c.int, data: rawptr, stride_in_bytes: c.int) -> c.int --- write_bmp_to_func :: proc(func: write_func, ctx: rawptr, w, h, comp: c.int, data: rawptr) -> c.int --- write_tga_to_func :: proc(func: write_func, ctx: rawptr, w, h, comp: c.int, data: rawptr) -> c.int --- @@ -39,3 +38,14 @@ foreign stbiw { flip_vertically_on_write :: proc(flip_boolean: b32) --- } + +when !NO_STDIO { + @(default_calling_convention="c", link_prefix="stbi_") + foreign stbiw { + write_png :: proc(filename: cstring, w, h, comp: c.int, data: rawptr, stride_in_bytes: c.int) -> c.int --- + write_bmp :: proc(filename: cstring, w, h, comp: c.int, data: rawptr) -> c.int --- + write_tga :: proc(filename: cstring, w, h, comp: c.int, data: rawptr) -> c.int --- + write_hdr :: proc(filename: cstring, w, h, comp: c.int, data: [^]f32) -> c.int --- + write_jpg :: proc(filename: cstring, w, h, comp: c.int, data: rawptr, quality: c.int /*0..=100*/) -> c.int --- + } +} diff --git a/vendor/stb/lib/stb_image_resize_wasm.o b/vendor/stb/lib/stb_image_resize_wasm.o new file mode 100644 index 0000000000000000000000000000000000000000..1bf91266bf948e08986d6d402f30216ca3f829f5 GIT binary patch literal 27646 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 literal 0 HcmV?d00001 diff --git a/vendor/stb/lib/stb_image_wasm.o b/vendor/stb/lib/stb_image_wasm.o new file mode 100644 index 0000000000000000000000000000000000000000..cd7fa31cebc2c011bd3c2185c41d7fb42cab5264 GIT binary patch literal 78144 zcmZQbEY4+QU|?YU(a_MqAeg|wSf9XH&yc`Y4+f0&39K*%JD5>l&sd+p0%9^GFoM_| zAT|gy)+aF6BcvJY6PUm}kRU?>Q%wzHO#)*r14tBZK1jTtv4-hwV?zT2A9HG68Gn3y zPG(+eVo`iocmYbTJ zTu=#-VlK!pXW)Sg=N3SO*@{bwO7e3c9QNXpqP%376l+dOYDEEq07#Eye0*kJN@h`N za!GtzXqwIJudbnAw;)nYg&P zczJnwIhh%`I5@aCm^nE(85tQFnK`*QL5P!+lbMT+gN==gnT?Z^kByCkjhT&^jfs;knV*}Dg^`nskC~a7nTv~yjgy0mn~$BFn~{T$m5+;ykBhSx6y1yrj*Q&w35iLW zS=l)}O(3DAj9hCsG&eAmb2B(HXfQQ^#JIT_6c_{`OkM^@1_cH)rWUY-1cM`kBcmBp z4~Pem=m0UfOBfmJmBBh34=`pcv8jOB3Tz;)I~Wz%92v}*)<9_$2v-CmyMYlzD==m$ zF_>F$-!o1NRdqMpl@u0@+He5D6v+ zCI2VZr=Vee}Q)K61 z;&$8t(%^U#O#bF{d)`yVdQpXaJ=5!(7<2@jV+K@Kye3-6jzWBHJHG$!pjKq z2!jSB@?3dAzE*&{lZ9J>9qa|LcNADaE}aDNAFHDPER@-~H)=6S)ic*ZV|T(trfem4 zNJuNNf})i}fz^q_oQc7)LXiXH6c`41&2bwjpJ(YZfD+Uxkm2sUj0zl%j9E$?uDnb< z4BQ~!fE7V~qrjlRpvkm_(VS@wBa?#yg8*2U;~@r6)WCRW8Qgf8xRI6epmG)1K?%=* z$w7g|k<}9vB#Io44339Du7O&_!@}*z0CBMbyQ4vtBZC5qzzL8Oc^MSgHJEG^SwUgV z0a60uJ1DU^a(eJGI0`6oD6lySxWP?5k8CO{w*spIhZz&t3bb_-)RC^h%^7J?N-9BZ=pSecoa zm>56_8C3SOf-;bZBcmHHE4Km%Bu_!ZroRIkE$rN&CKy?ToNQy;C!h@HA zhlLxISOsxJmx2hm#0JHjf{3F6D7P>uN-Bteass$;WB`S_q$7i(m?MLt6sY89P?TbF zP~dg^&*-MW=h*O%*{x84-?5?L54&5Tf>uA{(FSmMZ3s3-|e{g9C40|h59s60eXG}Dn14Zpxe zPy$qBRp1x61ZD{+@Il4c6!@TGf(pD)F)jsOsF<(<4^&K4fd?ukuOQ&a;mymaAPAv^ zA(Xr$r#~;F0;^+XmZG!*8;Fuo-~v&y3ZfuN&XG}33KUFe=~j^y6vXa|BH#q2DCEeX zsKD$1iXR40GGlU3km3ck-xxi3866oE5y{sPH7A1;DYTvkCt6stvSMIx{Lct7OF;}2 zT2M8LY>*7CC<=~SaKR}GuDTUC6hyNW*g%G`Lemqc0$Y(HrvfNZBa#&l6R33s%U7bH zoW|s!Ac80|U@mavC@E581C`y-DiUNjhXN}oiLxp17b-|A2nbvT#SF+!kR=Li3VekM zG75so!c18TYzn-E3bG2qaAC09d3Yf{1lho*z*DForyvhe0kQ~GYC)R`U{7+vJjsRT zNj3#ekS9SIO-U3S0kFIy3C%kSpuD31l0wQmI1(NUdO6k(OOl|b0;uW*mt5dp0w`^> zXE`#Lfr~H!NJ`*yWKd*tWKiSHqyr8h-1*w9B9XF_y1eKw@3<{jQ3L z21X@tKVl7|5=XWYmm@2vWyzqx3GQ<*qNq?{6#)005o+dRs1aBI5>n()0QaXE zz@1TWa{#%I3hE4jx@NZ^YGH1=4dSBMC~yZP1hx;_ui$pY>=fI5VbIED8vz`bl9ys}tgT!|Ar457dYZl^#! z(9{i0Ctx$N=yGI6B&rz*#h_#faWu5l0(HN^NftXF+|j-Z3O*%KMa&h zDDWr za)4q7OILxB8)QI^A|E)9iGrIF0&d{YQW7jw6acjcJrsEz85H?JWgnA+0>1*6CpZrZ zfKmxBFOy?EsGM|Ub_4fsK!s%qsI=n)myB!*qM*?iHb)MS7$>Mdz{v~hDRL|D@-m`p z1*w7S0~Mg)1}mcE1T`r@wG6021dl24g8IN1R$_@zY>oi6PdGpYF{rsN$jhL>r^pF% z6|)0q6hyJ;V!ZFS!nv-4NHH_C~gARxm=DckoaW< zhcc)V0N0U>3LJ_IpfsSs;K&Rbc~W8nB>*;1J06rCKxtiV14Dzo0vm{~cb+;` zNpT89L~*h+14F&@6eXoeO3EN1|5ybO&A^aQ2r@`{CAgIVYSn-O2GqU;r%6zUP6<@6 zZdOp7qJUd7C!}w|$*sVqz-h(60M*Qtg`!yzYBPw2*{rxy5nVI40t2XN%b>sqO0j&< z=mLiW15!A!W$80Az{BA`sL+5|tIvoW5dZ7l(E{Rsy*tqXVa<@3J4C+z$q*sz-ffVu^JR-V823=38+}&09XG!3PJ+Vku*>- zgb;*|xp9NW)xhO0WCYC-G@{0>z@#7qYRWJw@IcBhP`Svh!0tF`4NQ-KS{Q(y#X=KxV03d{i&g>e)FmSTSfQFo4Fd6%-g8HM10%K&gOHfhkK7+*)8VW7@!|zyR{$8b(@AZ%hM) z1jh8nEuyD4IPgzzfD%1uRs)=x86XXeH|^ z2~^85fsAJW4QX-0^+A+_vOBcz1+twd3pA9>pvVQO*0>ZHAssl-7$rzMk0S%PfyJ!A zp}};5QHd8+aIk`w)L3>}~z5~vXhYQaEL zGADRk4-~%Opn+-OVS`wpzyvWHGNs24az8()ZHL84tlXepH@5<(09SfK+y#Yb_E`kUJjIuL38i z$)>=d02(Ubfn-<(2GF1bg8~z%Wy1vVo#TQ@@LHCWyNHpo-jP8O6zQ;*^l_AF!jmYx zV<<3?s2v|NxG91B`H*`256$%Ir zt0RK~Y$yd>)oCz6+?N9yQ-%3Q#gVDZk-ZS)90eu?CQxrlfmzzIE{l(efsu(@fejRq zpc04)G&2Eiw=ilj2`I3FTH2t3gHeNt1Jr0?)L`OKV9L^9U@&7~;8tJ)HB*_ulSYhL zN}x6esE-6O4pdYSGL9LqS=`*9Am#xPkXd;Uiv`@RYz0M!6qpYp7;+TB(IEhur9ci6 zuvehTNQuc&5xgV-++ec?buE}2bwD%MPz9hi1|*p%vckGkpur%h7$;ne8O~ydv$)_a zZpWvc4Gj#6YzoW@?4Z$QC1wRC1y(SN2{cX!T412W?#0WhzzrH5;wn<$1koHtN*q~A zoFM$&6_Sn86N8U`%FA7r;DDP?}>kV>$xn zF)485C^4Ba?Sb-G6_};Xn9hKCP%nd|w}1s0H9%%4u{tuBD6lFpIUZmrls0490G4BN zlq_+SFLh)tG-FybfyuERlC~6>4mj3>#x@xpS8!x0vO0p27JFfdA`_E?0;4X2iX$Va zoeuJl0%)w80c5ZOo4_AX2VI z1r7yvH(t=#Cl5F|E3m=_grHV}Dk9KefdZ4`f5t*|NpL9)%Hc|^pjjM#ZpVY5iotOS zhy=~Icqws8gGP{fITXYk8H*hm3wb#}lPTa~a3%)@QOE!GKD-Q$n?M>A*?73P6?hdy zK`E0Pl%PdGF#xJBML=_gx(rN8Y*q}QG{T_(p3dL|Wkpb{gQ+CTag_k5%mF1AMg{IH zP(*_#dqCr}qKcd#*SPaCLL8{b#>2s_z{1N0N@3vn3NQ;={waYAHE?V}5*4!}1E@X% zrA<)T#sIPhw7LhP8)Wx?Mk@wp5Eoq5fmsqDCu=aF6?foK0Z_%m!wxE>6gWWXtWe-D zC}D$Q%CTO78Pw(hC8A=`k`KoQ1rJc~tENXE3oo&J2EJ+LDvE~GH}O$*XBSc!9Z3EK$}qv8caM8 zCMZ3Ct2hW36b7K|0v^3aojE{Xp*4e(k)s||nJ9qbMuCAJtkj6L(V#5lY ze3GCR05>-`GdBw>H#avoI|nx#i00(t2DMZfJb1Ykm=!^x4_dHe#lQ#}%YaTafr>^& zc2Io}8aw3%m6+VT3_LvCAT2zgmWl^2Xik_NJT;}nZNi+6i?%*aW!iIgm{NEo_0x z)G4C^+zikv5CwRAuz}qJ4FeXKj~E@NG3J1*XK*~rkdp;sF*(ZSWI1w_flUQ1Sp>NU z)ht$Ivp{245cfb+1wk{JAR&%wCTNid+>cD8x{n1V_Lz~~#|Sfv8It7KbQy?sA*!jM z!Fsq08DOS@ia}87fQLRbHK6F_hR;)j2Hru-Ss(=ssIY>q$3-hy92xkzQHqaOypGoq z#Rp6mY|)=1gX2w*ETY(>@w(l4pn48tVCz3KGiyCl4GXv@rNAJ7Yi1BssDj7raP>8r zxj{1EkpWj;(A*Qs>Q=~vBDi=4ty(LA^((=pFMNTp0C*4vT&jYVDnhh@o2E$fkl@mr zL4n1QMPMPwQ=p~9NWltS(+2J!gCd0qv;>(cM~NNOUjk`kDgm$K<8)-uVge6*?pEXi zvB4{wcPnx$uz*(J7Ao<$W-IZ6=JELySQVKS*dX&+3VfjUha!Ulzao2$L=GL7Xb15@^*Ed3VXzQ8 zTnIF&`>(# zx(p1w4BQHAyr7lapz(gkmmt;1<|{BLa6px_IsO63Ix<7df()jyIWjmlfCg+4nm~&< z9GSC}*g#_gptYDhS&m!+9U#9ca)Jh3Ky?gwtt6<%1kWvjm%V{n9&C;?K!!Oo3v`0i z6SRZfj0rT12U=;!0JnsT$w7e=KFbYX`w5O?aG)u0A!Nbc1`m25`G8RYG{B_51RjNB zasVv~VNhUpWCo3Vffj8la4LY-qq0aVfpXnPkW;~9EuhdqGYhU9DR+V*2(7pTbtM@T z*&*#41un;ihPo`rj)po<(Bd6XPGtrc6pEmNn5jgO2eb+sG>V|etH7oRTF3;{eA&hGxz|+4$vxo(Ac|xBNK>X2las&K?7^-Rt#(ktO^2F46LBl;#LeS3h;3R zD+YG(FbPYR0*k;Gkf#-xAoYMFgCe^lqoM#0D<~2{wP_(}GyvK`FH+zF^^Tz11d#NA zf@=;azF|Sn4$=;q1>u1XD1q2eeb@qx9W;(BsK5j+cDNKly(9396)0Cgo7o(Wjf|i` zgt|phK!F+5)nO=9;_&2U1&vREs(lVn$pgwROeI;0koJ-QqyYhLka2*D5DrjYVO9if z(EynN)eV{!nt%%H(M&{8&z zEJqfBPoM+?n%U=4V98Nn2PIz63TE(FSQcn#36ZJii}X zbbf^iG&{KiG@1t4tpuI~Q2~wWnlXuhM*hKbf}lDGzFP^bWeZ3POBSRZqX?>X4ZD7OUvZQ=*TFr8e|D%x`>&VL4iesNd~-J9khc?gGmA` zpv0ODnY9P=Af+m|0*m7Zc~}Z(<-RU}n0>n-3~x~=FeosaG0A{zH)9fjP$D2ofho(3 zNdlZVLF*wM85CK-9MIxTCCFABC1z0B$fO7wH)n%nS0-?rgQky|Kx;BVYwbb93kqPB zjF8D_21v>P%>;o4Nf{JbxE(J{1q~R3raYJwnEgTi1Z^5BDRPuA;AL=p0FhN-aZ~`U zPX{mG0yX16>-0I?APo@E3;`poDgaGzI5C(raVRi2wg`XMqe35kVD1ukob3iB9 z9qYiY7tk6?W=EznB}Paq16n(SYzH+43i((V7@0W0je9oG%miqD5fttMYeBA&f;a~f zeqcS|!937P^ZG0$M#qXmMess(kln0~Z&?Km)yRn6nfZvlN&>4uX^lkZOus zfmNU39W#i0!>q(u3>rjq=LKz?09gc@U;=LgV)5l=aQp*F13chboMT&|0cmFfX$S9$V**Ko#xa>8o9a*! zA`8gl;8e`!*vOQn2%4Y*jqy9yfTng77(vUl89@;Z>bkK&(zF7r6N5Q31B3#tU}P|3 z;8$RAgLd=tbato+Obn2MRe>24 zUSQ{PK%L8i$C2QElM2gw!+pfPjMGC9yBHK-0?G-Cp{l(|4NTHw^l0S;%- z<}dJW@%NyDPZ88E2erV!Q3uMh7ldIA3wG`XMn)F!(lrjyV21`156E_qn-v(#K*JGW zV?m1y!NVq?7y=jW;GzmzZ?HIm%3lTr7ErAL+JB?K=*W=;Dn~$NA2VpmPJyvRfvE^w zf+{dN3VMJlGet%P@RC0_Mn=#q1ZdXMRR_qGI%MIS0%JB(zGhNj1ZN^9$9e_E z636=DLheh9;NcsPM+HC$M1x5N6mgEdpyUOOG*BBB9<@r$*-9*qpv8=y;MC5dz?21P z8c8THXDc!K@!V zQ)WyOAOQuIEYQjcMo{>oS;2jfm5~e95S;*8A_?shFgr3h9$|nis8D19Ef{oc;DB;L z^)GZ`3nQr24_%BtBgR#XJFOF%>JYzp8>Z8paz3|R`?pxIS+&%NQcZcq$Q{;kFN8F(K%o0%B z$B`vViNVnZ+(Bk_e87?A*w6q<-j3kF0*$tSMpHN;O-uz&P!0qQ_=4vC!QN9~0F6B= zv4hqOf%YshI5L)iLZX2Kw3<0vf!Xm1n3bi(oaNZUnWX?8WM>82pXIoOJ4=BRysB7% z!I2YG{c(W?OSnKAISU;*%8Hbj9a+j8`{fE1m_ei?O9@DbsmyWnGX{_thyrgtV|HXL zbG*8W5hMtr92rZB90e2@vdfg19sh&&Z-Hzm0qweDb7WLtC{f~eWCRT!L7k!qngw7` zU;~9dg8~m|kutLaH`phj73|!gV;Df21wq4J;IQEYHBLDdK-E4EGdH*6LQuYfR9+0A zOz^;D z0xcy1g`xrzI84ET$pjBfCUBU70#lJyfzj~<2c&8K4V1ne%R#MX1!yW`0Zm}CKnCa8 zVJnH)63S^ZjusP1DholqGYVs^4Hpls({)i)J?1BxP zUUV54vLSUl$itAP0?e=AW(25V&sYLZO8Sfppk50Tw-Bf$%;?zA09uZ<3e?cT=)C)! zWHo4o1-NC(kp)_M-oWU{=*WQVf6$mDGsyp-9Vj3NgZl6w0VM{Q|3N&M|Cy2e&p?j< zp_UWre^8|Z@)39g8+7c$j0rrG!3{ej2i#kRoRPBvoKRpATR=RB1h}aVUdRXyOwi#u z&;$fYEjvKVxgf();L}!AK*uhCsy0@T{TmoTs-R1K_kcOz!*f6zoLd$d;Kcww?gENGCI`nE6BwZ}$O`f(N?i$xOt96UQ7u+SCV@R5^C9Cd;I26% zD3yX*E}-%ZlmS4s3KLWiRQ`j?9_W?|Mn`bM1~o_=nLVUII^c6^jF8bA@bXbM1vc1V z7)&12ie~_g7%(cbDzG^6fY#T7TJd{9&H=SDpnd`EAB8N|1)u$c;TO>MBW6cN$g)jd zM%Xbsp#Cn*N>D)yK7d34)Eh(`J^~trfqEI!zn}sa>>qGjl-ZFPe6|LQ29t^sgEXk| z1KQ$L;U=vB&Tu@A5^f;xLw3SK4P^tZCSn2~Cj>5+nLrC>!6689r6VYizj)EOv) zfnr|Z03;yb{DWxxLumZNX#67(K4>ToV+N1K(E!%2W#wMW1a1z3Pt-WluQ1Zw^k zm@_@;2Za!Wz+;d}jyD)VTNN1`A42wYfz&X7T8Xf^KJb=TP;(NZUWv)^5ThHYsieT< z_>ZyB@c>9axUm3Q{_`AUCMa=&CUa4A{Ri~`KxhFG&oBN<(fXvc3lCU}&GFXVR?i=DNB;!D9 zrWqL+LAwO7D1^<0ffiE3kA8u!BZovhBnd1AtziSTDH&lpU^Hkw8XgH2#0oZ0c?j1I zZtX*StO!~G3R)5d>cyKe$$$bC)Wimz3xv6I7ve$Csy-I@%q7H)(Ah??v*B?7*$@T_ zeO~aA0TCr8$kJ3u6G0;jw#wD99?+mVs`E(aqsw2=mJB9@b_K)p!Eg^*(wAoerx zlXxr>C@{bWGnp~5z>Z`Btpfm!_40uF#h_*gqoYU`NC4CT;4@=VfP@aLm+!a|%>|GG z6+|j9K@N9f0v+xI$_yIryo{iJ0+XYK8!t%U4FiP!1EN9WAsf*Q1C_6k06>pz=$sz1 zup)Gh3wrDZC`~|_pm9L(z&s129|h9|qd`dsT(CGYVE6}-e2`U}F@f?Y*k>RH)E*_! zfPf>Y`{~HwxEIYYpd{c38Z2^Ta6F1l4s;j*C|NNmv4A&UfyU<{g&}$v!{Q2b<`y{q z@G(G90mBMRj;}$dM}ShNqXuN$$HJW#9{Kpp0aeK0mF7&~6#@!Opw1>6w*oUbDiuKM zg~39M3XG2O;H>7<@T0Mz;SYm36AxrEn#mCqfmXETC2(1E{71 zoxcI9HkR~pmQhKFwUI-H&|HkpT+<_ivis!FmHio*g)I8K%+b0 zDiAIK8t;d;oiEm^QL92vRy zvNE#NJJx_)2%hU@aXb!c7=Q*hV5JOb;XP;|Pl*M30wSiV#E1BGP^MR4Ko0U73<@~I z{02i7XlXSlUqSXgA^8yGX7KJkNM#AyQVl7gk%JC&JO`750;qS!st8U7Odz{Kja0a! zL5&g6p(6?mXpJrK+>HY097<4ff+Ud*jGo{LGtlfbXwwKIXm<&ytY8NXfrEw?LF=m+9R)xeco?`9*txfX z$`55w8N=X+*td?HKw%z0@)R31h*=#kfOa}4usM2Wfrg?T|1v@*Ly;py5i}eCsxd)+ z1$B!+qbz3N&JVat2T5{P4E*5E7JrsN1LzPJ7^e}$0RT&R@#buy{c+kN%(8@6ZN6<9+e?~89MFvox5Wbz5fxC&Bkp(_{ z%z@IMRzR$81%(sXCjwc})qbE;@u17^plbWQ8gREj4KNRfM2^G<<;{=(G`(pkvWsB56?}LHisLs|i7tK?`m+NdE=2 zD-M*2APWM)6%I-|Vgp41^3tTkY>ce+u;d9kx(}NDlvuz=+Mv1y+@OYJKvqz+fG}tQ zE_myV0;}V9#zJtr5nUH3$)J`bph**OO~?i+F(4%gyCXAr1Rs> zWp(@pI^+!0@IlM9AV*70?bk$HwLsjzTQOHOQeL-y>QGkYz4lXM*SF)-Wou zWh-%j!UfblVn>v)ED9Xhoe4Tq2c!&C@?6va@;SLw7>#7;}sNqg;{(|OibYOvRE7?Kyk_9 zsNl{EPNd9bimc#0P+$YaL3Iq9BUl3?csiU7qJa&nfz1)B!I23xJO)mRphJsTK|8*f z!J}fJaUY1=K=YYk9*ZLf=saCe9}ZNqGAgixvJt340CAbXM-qcV64G9Q)Y*=VplpB~ zgxoXG7Mj7wBGDF_v4Up*6d+@fYZw(+krtYa$}3PeK#56#3D)oi=d6<;`ylOd(3)Uq!bWalIf4eTK#35f9=WUm4K;ys z*#<@>@K_II(;N%vv^P+mVg_XvkUXf>&4N8oF~M5hu=I~&u>d%ugQkoiSproOG$e$a z1;CS`42}!};E_Z}0XNY27NesAY-Es$`Q&DbA4rJZZ`UI;j$L;4bKlE6@S1pfh(MN2$S_ z19B2NR$zgsQ4(~#*s<^w3uIM?pySDwxwApbM;sXyd3boZ6$BlxG)+9k0y?`v(DBHe zsi#;#9Yju7UeHu1$n|fUkk)?ifikS1VWuQajuqpyIcpx#R#A?PQ017$K>2KhqrodX_IIW2VRHZ=2nLCmF53vEN zncI;aVaHr_WlRnV+~6*|BeS9aC=&>P1~EVjEpr<1nY|Ub9hnM?K=Z{yW=tGR4&dGqivnw&f>54ad~jV0I-LdTEtGT%mSlBoWJ5&QZ)A_~gBCLU z|Ib{gBeepnMBH9hMaq?4YGOpv;fV zhsKDa5b6QnOj!y-5ar;bz6C*RRt0$(czD4lgyHf6IO~Dta1asPfpq5r4_F=?7m%qh zP!od%G$PEw12&f(9`rMiwShtgbTFU-J1G4#fRFVORAA+0bYxK!;Nb&B3@E5qqUvFG zP!L4&DM+n?AZT@npaSTOStVAucUUx-IFtmziBy+a4^r>P3tsIC<$3UeQ;Gt!BOB;62L?q!P#Fz#BBLiI=~FoUM^SV21#z%6u0c0LC32xuH72XqoN$S_a> zP-2A}#t9C1ToK9(DobGzrhtf^^B`kk4^gcj#K6o6ERe=%g4Uk=+ zlmS)v3RNLwRXhhM@K}@tbHG6i;xObmf=_VHQ4q{h5(HHWp!GO>3W5sE3Oud?n?S7> zP&v*4I&KDBRkJ{*;6c_v>T%H7y5MvOJ}OyIfmeYaRAxyl@hX6zAoNrja9l_$2r4kS zODhS3tAEfUWD$^4I20iVS1So>FtI2KDF}i_cm%=c20_lvP~ZZ!xIjTG3_5n)twf0z zzCT{vl^0Z!NI=(9DRF`hx&fWepv3BUpar@FQHkBLz8JJ2rlA!kBABJbry!W8$fdvu zS~0`wxB#x01FHA|T!a;L^c-uRq7XPYgC+(*u?0#MoS@~joC*vIq6%W5`wSGg6hsub z1-^ko&Xt#4K}bO)M}ZR(%%z~ythsU&B|-bwKr5mc6nGU_a}@a$cohV56xkGb71(nW z*%ibUcojHuvcLzgbAaYH6`*$`a3~1nC~_)DfdUOQC8)rjlcgvK;_-lY8-TKvAZQV; z0%w*2Z%!8M2r6*Gf*x_AAOu?H3R;E42HJ8Bxts;OBM7tw8GibKf|!D2wxXDVsDfmc zq9`}$RO&1x2}j7*Q3eHZGbSETqZj6HP~0H-n+KHq!RY`LaiEa|P;(08P;f;JZofLR zWht-LAOOBN38WviKn>J>0B!mOFQNb!&KgYMRTSXsML;s(RT$twMvyg* z%%H7J3<5tv#&UynL$Wrg*~kF8TL?5023ipVT89m~*@73esDJ_FI!HOl08WJ9B$%Tl zsK8qUJ`uz5|NsC0*%f&66a>NMgO+m&CLbpFDae^yx)a3@y!j%_%YOjJIXmmqB zkyU{cd^S3-0;rl(0F4NN*1dt|{=wHYFoG_q;02ZTybAEj3$)8z5EPOE;KQ>((F|H` zCXl7T3tN3Cq|e9zzUVLbe;kzKPrNXD$u11aI?6%72ro*fOcYnV-XQkph5wZ zB+QsNz#A#xCuD#Qh~t1Y7^NWF4R95{kYZjz5Ztt2D&z&9#N)`|*Z|5;(8AY|K|#<_ zq6DM}yr`_C2y`_;8F;-ONT#f)$czcBfrkrLPolIcz?(5ZwI@n(%E7H54k@9)n!u~G z6~RgcK$jzd7EmyQ1|FdY;cz2-1iEyI8ENwyE4KxBp#!8n3R=SpWx)q^K@}ggLstZv zMJrKYbd)G5D&h`?&%}f0O2A{zpn-4j45J2<0=OmsFLYvno{9-M-y1w!16q;{?%IL2 zh=E2PLAL}H4)B&R?A_*{_|G|c3OaiLdd^83AJ%hDauis==L|!Z?10+Dp!PAOXr%b4 z6VNJ0_-Q9#4s_@mR4kxl5)V9Kf}bG8t^itb2x|C1w~#`YF~Seoz&!K>bnGcelM-Z! zt|JqCu`W~qTrxw=0gb^b;W+w)B@1jhhB=O)t6d-mT(Lvf0fUZ@0v+0cdWZ<@(tZ%^PzQXH8;3r!(sr06rk&PZ`&0gFP8NdeggH41tX z4m>qrIVJ@qEfHxtJgu>z9CCu~z!V})gr0?t?q1AeQy?J+K4Ti>Pe`Hy1qAri7!U(; zY79n*z}*a5^NR>$Q00bxgbFl{L5=~(IXsr2IRnK6at~5LE}s_jk#>-Z4I?vr@&Q}j z09p|bsV_j49J8Z9NfBrr4rqM3h&vr}k|k&)hf#q6(!v0ZF@oARkcI|ho&tjhxJdz8 z(*(L%kWqmNt`Ia$18tEo!4~)5qzi@v~EKgq4WWt!G#!~ z1Whobk56)ghoV7;iGb=l?h-~O@KzOG2FH4k3~0xrw-TcxO9^Q5z6i7#+fkqlw75}P z2~;0?D=|ugPC0|faKL5wp%P3G2^P2nGe`oo#Q-AW$mpHL9RR+55wzI<>MGVk1qR2f zt3V9UHU{v%fHO0hL86FLBUz@v01^f{EX&cpkh>jpBs!>9 zq`;V60&;8_XfcO}v=XCZ1NfSSEueD^p*7+|O~+#(F8t&#(9x$}ybO-WiXLk^o9BjNx!K5%mpbgTzTOP7&bfzRiVgfXt2C6Gzd?nBb z8zlJA0-v~b4CtW_o)?8&h~mhA;Y1Dvp&~^N1!#8$G=HtgrXUE~3<6rtzyVs$pePJq zVxS-lng~0@8AWK2qkvRu^ zCj%oNGZQoDWMFR40lOe?D+(!aD@rJcfrojZfdsnZ6J!8Y{13VX1$2q*0VOtYxeGR! z&9R{#$CMHH^lb$W$Hw|BMJ`Y(} zP~gf|k^rAe0J``OJ&~|-D{zD2n@yL2Ns&`QM3F;53{to#a4N9D33{;6_(6(R&F3<*540yu{ zcsnzCISsyc7Ul&uMIU;!he0w%%mI}1)hygso1F{0#BWCgD1??+j1KlNerk4@A zkekEt1Vfe*hvNd!=0R@I(lpR<{bo!p6F|$X7+{-qm_Xa(dcpUCusI$v$x`G}0Qa6i z2NQvJYk)4MhFFD4f(~_JEL7rfTwa&O$HL6O1U?fSG=0SZtv5g?nSt93iae03J)q?y z=vKB5{fv&s`alPm@G5YCPCxz6Sm^kN(UX@Ow6ldli4SykIEM#h_!?Bt@)Uu}j1ot- zEYNhtJ&0Bw9+1cQnH?1Ppmy?twL>|eD>p&oDxlpnU=x*iz{3Zi9cK!hAdi6#x@S<} z1x><&!%Go#ITdJst`cbZ8V^5c*i(VWjh7d6i;3e2h8$3cuIXjWQV?(iZHWY#F5qYn zI&9+=)JeRcBQrNZl<*gV{0dUU;m8b9^bMhiO@SXYxaat#pD_!PKKVfzjUVKJ5B;D6 zT|w6ZLH%XMbfF(~1|P^L5YL%0T|pC3;&9|IRN^sXx&xNr06Xvoh^+zA4Z4{ewCY3y zv=c=Mv<=3w33R%EBCi62qrDf%iy9y^mG~e$1_eHlAcy09MsHqT1s+FUUtUHYUT%ag zUeIB!{NB7g3Ou|#3T%${U^jsd#AYyKIsvj6w2FEUhyfZQ?13?k^np{L8PlI$5Ce38 zGHA09_+t1iU?B!GrUxL^ps;9}z^DW|Dj77!${`I}V%-5s!5T~-`WZnrE$D7TPz4EI zD*;JoU-}u91YohXr3Ji{1C0ULwZy7*`aG;u zjEdr*W5TnP#1sTUwF_tnh)siOLN6mE&$5D&LQgNGB?7*f6;vF6HtsMe3WMW;4V)rC z6KxPt35Y1D`h@2*P$=@l62_8V#w<{&zyPrUbRi8m2?)ZHfFvvlfYJgu34oFbI0=X; za5ydi>6ZdcM}f*^0kC!OJDJ5ni2+nDf#gA@2k87s1#Sf)P&9*60GEOo$T>W)^uU8i z5Bv&j;D~obPY?VGl92Qunxi0)1)hjd;>mGjF3D2hP!LvN$jJh;c_66|HHCmi?v*$c zKn_*{-9*X_PI8Jo+zO!6-;@LtctCSMpkM}NEMC~Gjsh=CxMKn%Xvi6qa_h4|m$!6G z0B`CeC6RGL>L6Mb4!qE0#;eGnAPg!^g+c!5>19;nfcauVFQX!l0j3!e1AXE(r-9weGMVATlZGHz(ngI6ws;IzP|z@{LGC+YEmqF)Sb z60C?3Rp4;E11fYt#n2591C;c@2@RBRKueK9#S0{BLy|e@bZSUyQQ%PE1C8B)$_fP@ z1px(+1vyzcSy`ZT1iIl5mi9oSsr&-ZKqZGOFUU^DEF}(b(uAfAkWNK0(4Fy&SxP(# zVz9sir3~=WCD5^nhy+7H%248f7eF+wIFQo@^u};d2Em>-kn;rixPDN@tAyFG;Q*x+ z1|=Ryovg^K0Aqp9tO7OFI7*5fSwJn8)zF$BQsk8=@DzcyYA~_5@v=K^hstt+>U<6X z@Rn9rUM|p8LtvXhdpSX44v;1oNavY;(2=;@;A0^bxIw81w8BXen(`!Jg&daJ4^+^h z)_z>D+7FZ#PJogfDE)zJKT!Gur$|uI2TqZoJ~C*<5cqOU(EZt$3Y^e%m6HX!Hv^JVLFv)4N^#1$J;E1s|*mO3^$D99f`k zu=jn_9yy=^We!IsPz$U9v@VhxTq`PYL+VwIoGb-)aOnri2B4UozzC}x1sod~ zK_`YHbwath6+j(Z&~FU{nxMpo9fF zn-z38fIyal04S7svOtb)>19;l(O_c81r_uFE zV1cp(z?wTinjKj{N^)UpBtQ|xSPJSZayW`)fiA_$QsB)4@3v%eP~df}&*B4*vN*PY zJgUeGzNQK!$O0OAVbfrmGJ#Q%7qkQ#e4~#dFF1S^xDf$^e%3E9w&34A?PmI`Ya_rUeKl{&=ugIaAi^AaAbsD zzyLbR1JwA03>1Jen<9$>mm{+Wc&&~wXeBALg910`N-fZu3?;D~MNx3&47&GLkw-y9 zQ9ywOaw-gDgA^zX9XS*@93`@pK&7w}sQD=X3O^-&N5)bQB~ED&M}fl;bW0_i1HQ}| zd_@?jeZT>_*%{n<1T8D$Re%(CpaACqP3wdDBYX<{pnS^m%pk)jk;7&ZV0*3-GXdIG5feX^Q1lL+113Y;dctEpiLZDN?Kmx9y-It0ypw;Fa zihLl71$3;s0^EFvIs6J>%RpA~L71ST3fVR$1?;x5fR>&?eDtE9k&l^ynGtk=fCdu- ziq-rOt0Bj8D}u%#;Vw`Rh1jUXn*%Dhl=vJu$`truH!N@{@G5XZ6ktj-DnK2s2ue1f zjuj|u_!OWf@q$d^g_wlL{ouf01ud0g1@*!~!KT0kDheh{U=-{J0tV%7IKMgHtTXGRP60Aj_CRi4as)aezl*!Fdi+ z0r5cxYCxfak|4n;kp)ybfqE=rAgAysN-{gZ5*tb=h(QvdBENzplm!~a0i`!k$iUMZ zBISbpzz<7@NaA4s2*4x|F^n9ZSaUTfJRzO{t(OPKCJ!V-gYW%@r9DMnc$N?Vk63}S zG$;>&)*NCtq=jFE0A@NqJsfkDIpRR*a^%I3cS$JK(-2etU2~H0WuUk8G(`} zKovBopAU^?EYS;^6$cd;MC4IWTSgcZfWn|gVwMtk;THI;F>vxwZ&w`+yDKwy{br9ncps^Qz@Ln(I*bCS>;J)deenur;N3k-;f9!=> zdW;N^WA8w#JsCiwd3+^iOyB`DkTnPT85Q`l&6q$mXrl~EnInH8sG-c^xSTyp;3xn(iJwh@Pv9NMFp$e0^fP8D^762P26#Y6 zM?*r8SKvKJfg*zfj{+Z7ccM6wTLIjgy3)_+!^;3V?GDr!1dsbSG_WWLlsF2MD)D85 z$|Ogq!xd0`x;nyjgAKxz;Xhf zp!T@}n}PtU*T9NEe&SP*RA3YM0P>ZzqOgL1B6xfn=4>SaP#lA1kU$L`P|9R-P~cJE zP-1Z0h#Wsk9Qur)YeQg0fu^VU6gWV!FHi_Nrx(=w1hvjUd7LlHni0Gl5X{L2aX?!$ z6?j3n-zo@$_{`kkMl+}p3m&>gxChj42gNN@mLdlzcm<##tH1`<0rnR>)Ir-+A&v(v z$AkyDKsKmGg7^f@9}MXJU{DZHU;z1pw-B@xL_t7-Pl4Bw!An|!7vv34@&FGtuz^RK zLCgL@MH!M~K&26MAQR*i@Nf>OeNDs|6VgZmXxS09hcXdE&|pV#!$yvTKoeu!3L>Bi zSVTb@Gc7l-MEr387t71rAVQ0A6bZ3O3Nx9|w3> zkt6c53edbDDDQ(SGCXP&7{U8s6_`Nf7X#P|1pyL=o zDYQaS05k^;Zf=3DssNpF1-jORQGpM%&kNLtSKv|vFDC)tu*v{RsbKp+=7Dw{fUX$? zE%X50pqr(|1iH1N-kBuFfDTayFZ!tfox}|~+gJf~<2^U1wE^lK^McOT%fdzCJF?R49FXX znL%SDkYi53TXohjf=*&)1#Q9yUAM&oT4@Jr5z08SD6pb0$OGwRh8#`c&dUr6H;_`q z4u8m*T;Lu4D2781B0w^n6N}-X?y&+h^dJI|RiLy7J%a$|RnX}J%-{^7v*gOhalcm7yh-N$76&xi+pkxOvVQ~n9q6KtXKWKptvtvaT zC{A3#M}#YYxJpbQe=s;QDlnD6`~f=p8EiGu3Ip!TJcyMA0`RqQFiQ!ofPtH z#sg>Z!dUp0p25^8^1)gBa2EWgE6`>m=s36#ToyD0CIUGd0Ne-%B_0q4T~-bnhvJ1y zO7c1uW`VZGGC49TiaIhXih&TQj!7>V}d$NpkiGNwB8Jwhb0uG6vUA3eMh?{ z4O}q6)Ie@`1uuF5?cGADGe9{+*s;FYkri~SD=5$v*dUiPJ2EIrL&DaP!Ce95w;~0R zB3=dsDNqYjLO~34;kl%O0I0+U-(3wl794a4GLr(Y0<(gkbWWB66SV#T744uax&yz$pc+YmL4jA`E2!nJz^fnznt1@7;|Dra6znes1!)Clh~E)zQIJM)6N;z6 z$8|_4NaR4SB;s)7@B}R_b7Xc?1kEgh)(3#nK4@eBJW~RS7*I0NNVDufXOGx{(tUfBXudJ07_eq(K%z6A&oUL=?mvnLWYT z5tM3pLDGznp?}cQM9^{Apz8ua!`ciA;CopZKnBHC*XpX)E)ruepFylVD{x@0hbp_EZ}4VI^z-CEdhlkBWSxKBm5Q( z@cz0iMOIM3+QA={DsZ4q0w4O$sKA0^1nBky@R?T%tXYbz+zLzz ztf1Q@7`#9Wei=arfHH&IP<^2FgP>)rpn+Z%u=|-6z{fm5b0Mf{1PXu1s&KLvCWHDJ z3QP(tS+Jl1uYd(z5CA$b9n>3S0gt_cZYKQz3S9;8?Ov>q>$^Z_Tror1Y@kimY@h|k zj!apgyRTS5US?2W1r1b!_G5r1^+1OrfR0~6r~pNPBNHf=7{FWV!AtGIy)8!2;rXCk z3cgkmbSMDmT(2Cky`a+-K+ecg;>dCYT@nbsw3$@_v>=KZbcu78BMU?bo1q-wEmied zpyQT6omsGJK=A--uz^m;ug?NGn?Zqr8|-87;zZCwHf9F}@a0x)kT75ey9KmdA3C7| z+ECU|pQXg<*s$dzLyiK6qezKkLxVz*5_^`TNU0KsBWSjf!Alyn5R4H#jO!r{$ta+e z(4dfMxObBQJd2E5IjD)x=Gd?-2&4`)`}-#ov_ik85NrqNE;M$~P=ylcHcbT(0kQ+M ztQ34zBr_;)gN_vihc5#t1%eKH0-f&%S|`ih#mOiHTL;>}l&u6k;0AOD9LW2i3v$7y zi-9(Sf(QE86qpNa!*o#10T0kW|gCgkoe*=&xFX&9l0}OEH1qAZ}g82c)WOaN1 zx-CqP5p+`q=nOYd!Fzx)3v}!lh`9j4Y(Ov{Fu)ZYKrj_xf?&;{aWBw@=nD*BXLBg9 zfg9%&mmH8iLJES}Af5aWodOEHj`JLIlz0`m9alX9-CoYAzzte7 z`+seHPL>jPmJ%nzC;k!gQvtMO;?_Zs8QfV)90+ZoVB}EXMCb#B zwy*+wHrVZ;i3c9XRS~x(#1L`G%BtR(< zJb|pllBLA&2#Q((1vW=;)bcAZIr4zb=TKmBlmWAtvXlfNF$-F##*_t*S)pttVMqB= z(DgpTj)J91!V1EU{~5idLHF1)C%a@R!(rl z@xUAlItvCAb)aB}L><2ZhXPBMBP8lT-pN+thnmd~GMgWK)C9=;{s@P+OA) zD$fFv2i@Hbkq5UZz%dUtmqmd~fe9Y>OrSGzA@Qcw|t42|SVpn$v=G+8|?y(33JCLQ0^?SvOGerohO(m>IecPXjao0lrg4fdP3w z19TQYWO{)Ka{me=XdIaly7dUO&mXj05VSuLbORG~ZUEftWd+X-Ic@g|PzbK}U`{DN^)d@G6$LsoUP6Hhw5w5rX-_|65oF4W$q{rM1~>O>kdq*e zxzZ0h*VB=~aRSnDsvws`q~X(K$jJ&K1)gC8)uo_Kla39rM8yh{RbYaKJtJsliUGUv zAd5k}E+K)d2=0jG$RGP!-4knf3wgy9FJU$f&>w z+RY4C1)AP)WaPfd#mEh71sq_=R)Sv63u$&DNCig6rFB_+%uFnd;LE6wFoMp=2jw>K z>EPf-{ClKEJP+tnQ_z?jgAyYoYl2$uP$qPNA*AX5A5|A*mo8Ra;GH0#5qcib<|9x& z05uOheg$QNEqvN>?qNs$qB&^y>JsP8~N1#kTU z_f`;oH{09wHfDQ>_P+-h* zY~jd)j39ynasrwfPzA@B<#@yK)Zp!J6ym?6zXhH*8&t&il9bxeHLgc z9K2G59h`F%poW8bw4fM;Ph^93|1mNJE&R6>ev8Y zv7!XJ2^BIf!Uj4HiwmSni8V`!2YmgSW4!`fi6dhX_yR8063{hb;CjqcT7lJ(Q-LkJ z4BVn;1f3iWIz|b!qmZKnbo~@#VUYq4=pZpj_d}5b-0cBvp$46-2wu~fp9Shva>AmW z6EyP50=jnMKPc(I5+rD}4HWZ^|3JN21xCk$LdOS;&{I>vNrV{^cc4TFWfpD`+C6#H8Sw9a6?6A!9+l#L|}VBhJ!jW;DZaR6&XQ&ZBV}e6b6o~ zLCQg4gp_F9q!pQ=z5{i6IUFlNhdD5VM)DXyrSp@1P={ZE!?6w|&gl3KQ91ZlpQOw6k!MULK6+-L@9y0pahA=MslwiXnCswhhtF|_#$7>Bo%B} zl2H+KBM7KYa%6A>I|UI9P|+F4kpsH)1T=pF?gqe)Tjib)tw$JPnG?JS1aey>gJVgS z61b5IPW#M|VLJr|#{%eDRq*xg;6wI;cSk4B%y= zpsEjaw=}4U#Qlqrk*yxmwFT8ni0(Zo*P(RipNlnb4=w?@71m|pU-yXcJgA=s!gE42 z*pV?y0i*)NVc^aH-!}*v1cK~J0NumI=mDCe02T0{<&dCJRRva%K_H72ITctGxxlN- zI6x+V?l}bC9$BBI#03gX1$I!68Prl@1UU$F0|#g?9ybev0wcE{_zWeeZ3+zFei18p zYylkns4j(;cTAxEJLr~Q@F7~DAO?;7g3X-D%*YO&GR4)b1Q(&8W+juOcp1nGpqmmw z1&3oj=psqJkOP_+*5Zl>DGJoj@6Qo|W0L z9O4tu9fzPeV9rtmjjOR_DY1iZfM#)A1MZ6XgJKNQ3W?+?_K1C5|LDrA)vf`qdiKY)$T0(Aq~9iiUf z$SzahaAYcRTmaJqQp^H6G6~eB)n{Y??QVDil7x=O9~N-@4dOz^<3UBlo_}AogNtlFNOd{r zGIh{p%8ZDh0bgxf2(|-UuwR$}T9ySW3z-}lJfuMl4myPf;Z{Pr};827d zln+~#13sP&q-F!7KR7ISL8INELY)h=tr%o2csL&%G)#{FK&K?4+5^&zVhgyE2b&M- zr+|VFqy)6;2Q*g>ZJ5B1Qec9VY|z>od_fks<9~Kg+67q-IbEBP`zs?8V?ARn*cptv z49rTP*mMCc!(d?eV6VXJ_@gdMpbHep+d$_Ef#yV*!HQv5v@5WHN>@;Y0HT?)Hi4E# zI5uPGGcqC$ zV}c*grwE!!Vsex(bo|el<0zj88cb#c9|43c23DlN06LfnG~0@#nvr{wETdw*0 zJ^&?-5=Bl>6oGayfY!off%a-Ca49gmflg;r;8pRgxE;iW6q2A) zj75P}ffK$`2Xyo#$orsmevs*3MLx(XPA>389qaGRpTLri4?rd=@+ojBu;eK5 zE3iRM7A;c(ZSYiJ%~4I%*+goOw7!nMFa}sAalU2hAc%D(3N$(5Ftm#GPo!cw*piYF2u+UW-6+H zq*TDOKF}NpogoD+T!i$j6r>fzvp`G5nH&^k!M+8}luIjcfqFJzol2mk*^COz;DI3~ z2L(AKRl<;C1VIO@C@BbMDKaW>f|Y?z>dpc;@WHNQ%2HHSkXB%VS_JVglY@c?s28Z{ zSf8aJoC69am;`7J6cm@pf)wTBZGo)wxWsxsIpc9w=hA6^r|3& z3Di=QRYNm@ZATS-Mh#ZkUgNu?OVkp@YC#%<+Gl{kvQ9B*kQH3c;VAq8m# zA&@3%kb3E21!)k6!&_QGT0zBI8dL* zH2HvLq2b1WR>Xk{ZP2C(NG}|`NeS$2W^5+H3OrXZ~#1!k!$ zh$?D;GruypCWSbUDN8|J0o+GHi&{0PD5+7aq@YAn)Cz-#a+SaX>7ZN<%6Xu5dkSg_ zoLP!$3hE$AT|pH@se<-jfz|^sLh_^rs1K#V%K+XbEb7Y3svru=I!q1#kvgO zi|KLN#|yH7R{?4?tRUe5B{xw;6;P-`X0t(M9WUtcWzfhBJPz( zW;v);%&>?87lDdYw+fV4MHPiWzG8w~#RxjhOOZ)ISW$)9L4gU}#Z+KYkOXBz1tv%V z4bMxUzON!^UJ5)X3-beLhqVGc3Q>bp2;@&8$o2vSs6QFumg5XkP{9R_KXCpbHB>>X z+@ZEX1}tb6f!y2*a=f7V2k`1)1`Q@21z`pGtSm@z!=eaIEZ}K(23SI70)+_^FKGCR zTR{P|azO#KG?f`NY70t>O3-#RXmza;XowN62yzV!$PNY2iX8bYMQKpekO9(&0Il%= zjij(Sa)1T}K=nBr=q3*EQdDqrg42-!)P#^!U;*{iYCu_y#gRG3u{KLl9drX%323xK zT!GCIa^SzBGHB(Uha$6My`n1Ud=1coH^_t)j}nstcwv*0l7b+las;i50^JLx3~I^p zf*dcI1)33q7FnPZ#Q0bk7@3)vxfS>!hs1&A+(C^G6$O4pAxQEAo$amy+PfmG!@%Ik zUZ%iX2&xP~g9-}5pivjbk^{)XOj!_NCRAZ&s4z3CFbhMQl7w;-C;fz6$57^2Scymx=kfqF#xezW2 zk3bYrc>KW>g67T@kph?<62LWCN_Yd95fs2E?f?fcTo~aNaQK2QHi8Bs6Wl%E8ApEb zbp!m6B|u;=GlEhvEYYffwl`!c34yF)fq5B}8o&qYfwql7LV*c17{~-^NrP?lR#Z_C z2F>>fDGDp7fCieFAnAk=L@{!MZW{m%HbGqlGaHo8L2X-5fdkq>0m@_wT%eu&pawT1 z=&%I_56~UL;0Cb*lY%O!Jqs(2z$5*jNC6c$;D!L01Iqoppp#Ftlt9H3Xj=_(7ix{F z<5HwX38?b~8YyK`Pyp z`v`QZBB*QYE)5!72f0NN%}38bUV!@Ow<<0l!Oa0}Z37M6gU0W{-qU4Z0+n=7zcE-b zfQD&MLynu9`xodUIY{gKP{>da18Cd`GL;T;5NK?N88m_dDnQDVKzZyxbD;twcsK>LI_^JX z4ru-WbiWf=6f`u%pvwT7=th+Q&t)-y%mjIo88jTj0NpwRO@i=N3aBjw$}SL>F@dL& zF+zm}l!#zjxVgD~A*X6H6CNT}V1kTrf)2}O$Wa0+}xRvv2Z3( zB@H_3kr7miK}Y;RWfizRg2ab1K?8Z9q7Rhb8QinMD$&-|)`O;`kwWSLXfGbfTcBZv zk}Ss+0^p^6pz&)^x(5djbD_XrP$~hH`V1OOXC^Q~25FB>U;>Y!gRZ>+5AlMpUpp~@ z39{COB{$0vlxjhyYcQ>t09qi$2tFWdwE(C%1~HXDLz9k;jG!ZwnZeq?ISb@+a2{g> zUbp)kN(3m`EY0?^yW{`>t6POejOF&j`fC=pZ z2{|&BW+^e7fkHJ}wWBjw}T>&@vLJy=;!vS&r3^ z1TYU%H4ll9cW85P+;Ywy74Ffn?7CTJBHz{ejdvVi0i*cF*U^2`q4 z1*_ntL<(%6Nn|f+MK%S{y^uKs$U}dO_Km&5^l8iN%a*2AIbL;xU^s zO#$;bK?_amvlLkJl(@}6M^Y+)P6=TKZKG0P&I6se+SALZz{Lw%sb3FiGS+8-k1_-8 zKLDLh%b>svTE`1ov7jUXOVAfU%N+$lN2G$P$|Il?V_6(Qhf+cUkUK|72;>Y_(7FT` zN6^uf3M}AI2gwU7@Z>0ofaJL#@}ToG6!;W`L6?ImFo90fQebfe9Z01BYE*+)976>` zM+7T?)*A5(`~sEYprQ+uSi#%PAaf1S1ggNG0NRNI*$EC>rw58UkUJS2`Li4g3L)ta zR0Dt)_9?J9J^;xIL0kknHcmkV#1;fw$K=QlJv9q-rU+&>Z=D@T?BJE-mfjWj8NPwN4#zXWY!;m86__CYcmsMZHB z&;}i5$C3rI4?ODzTI2(oNd+}k6qq5^o8uXtECtX-*O0&it>%U3&}9HE2!jlJG=T4? zWC0g|psfZ99H7QH90t{4=c_nk0iDY1ZDuE6ft2wr*sTCuAD8az~BE`wy9 z18Bv!0;6LK59q`%0R`sl5>OOzfEIvofR0O0ftH3S zu!B~;*JmlPfrD9rp-hn#=0niBU2f23I?w_z(2?>Apxy#Ew*m)u9}^>EJ;+jU5yk+S zuLYILIOb77w;5frN=%OBS)d^aP$OCa)F}hq>(J#R>$ zfPw;a#50J-5hS2K3bIX1++cf@m>u09Rn|8~&~Ydzo4J@AxxnrA0MNQ;(B5?>ZtiCb z8yXmvGBP+aaI=7ClO0)L3=SBB2gVTKW;uI?$AQ671SHa6&)_J*&7!yKg&l*V3`|4; z#9&}xa8!XYG++!J7{dU@Fo7{FxD|^_k}~7tbMg~Y;uG^y;tTSNOA3nelT(X};|$`MCuQ436g+xkW)j=ni3EaD2kZ&6iu6Q<7Pb zld9n9;S$8a;P?q@0*I@SlbXi>%Awp`CHeUZIf+H-AV+jEaf=x08|j!(QxZ!+dS5{lLtWCq!YvsQ5fPyf;ODN8oKc#W4fYT?_8Ax)7qD>4 zfRaBX4=^w=fjD4Gp$x|ZP!%9PR#lD^30SHPzLK~<7Q=GWME}u zaGb)%EeN(w+Yn?q*g^&d$E9rC++bmlpfLl3;|4ZvS*RcKa|=pKQsYaC67z}+5{pvv zk}JVk?<^a)Bv?O4j}eOVO&Ay)pRvJ10gDMBm$GvUK&(hfjn6E|$j<|1nThP&LeL+IZ2 z2B6}HotvjLueh|JAit<26_m-IvU7`LGa2m3KkVGxpd6T=mR6h!vaFqhn*$oY3=EF_ z9NZir>7)Ws0WzHflBshOi?cxyw2%Xo)Jqhcd;=I599KfsLUy8sRAu2(tK^UBJ86Y_Vl%m-{f*|a;g_Bzd;`!o?%(Rl?%=A2Ppd91m<|tFJQ7|$E zm9pnJxy3=HA}DkXK%oof*w`vqfQoNUNMYm&D$o^NQj3#|G7C!bix?Oje{ph)4`aDF@?-Lg`&jbl2nj8Za|F= z2zCL5BNsPcc~O2|x z#G-7l*Z*@vYCe}BPiQ68%>ya8T>?X3mE{Z`Za$EuU?FG>ZQ$YdXJF(I;ACRv5|mJo z6yj!KJ%X zM*|Fw|9QCi!4`RhC}gB2rhuK;#|v?yv%fFYiL;<0jsXEsk!8HxtX^*5l(CwZo6RvL zKM9m(Hu7@w!;%r)%LjP51wod8k_$+CdPxQ-gp-z%}mb##new;ZVm$lSZUhG$1MUeKDQu0FEy`30hUyz@^Lc@(em+q5Z0Cc9j-L<0@?(755+FUn z{=o{Mh6<{c_n?M=WFeLCE2xNzvqDB`S{eg`<2R^?BV443A5v@u`v)^HICk+v!V9E4 zH!%;KeL&SIKR0-7BB=0SW?*1oXJBC9XJBBEXJBBkXJBBcXJB9e6-ECU85sUEGcf#T zXJGix&%p3so`K=NJp;r4dIpC7AO((3_+ce%RzYfdJjkB-lvGgkFfcg&=I0L5(9p=z zwF3#nCzYn9r50%_*x4y)7#k_*DrD%|6=znZMkmK=YAUEHKnklQP!SuAkc!m=RYDmM zGoq7Y85kTd2yi=qia5}{i+}(B|Nry<|NkHV|NsB+|Ns97|NsBr`2YWZMiBn@|3BlO z|Nj|({QuAR;s1Ze2mk*w9{B&CvEl!J2FD44++xXzdFmw!xv53zsR}8{3W<3s3W>=K z435hLxrK5QD-_BSb4pVc>=evQO^r<%7#w#9a&s%&2RXWUI{G<-%JRd4-0aHsLGDf< z&M84iF$ZZxUJ~RMck>VObqujJHUg!>_@bQD_@Z=h>V7H6EsR|Bzzcytg4`^+kqQh9 zjtxTG639wG?Iwks#LE0qP{N%d#Lc1|0a7Wv~-M-29LrFG)`XH|!Z09Ip#;i-NeIB`3#tP?TDnTB4AWnG8w~??t(J!O9S|&>vB5!J<@9*C?^LL;;ff7#JLT#kfWCOG^q$ zVPXn7nYrNp+d?r&BL|czLB;B7F^Kh`+?$h{2Q6$5h;a)G2nsQ9uye9;v2wHUF!M6; zG4eAwJ`m%s0hK18W(X?-0|Tfj0xDTRO=(UB1_mw$1_o{h1_oYeX#?st2s1D+h%+!S zNJC2>Wd;TYZ3YGgV+ID$rRtzl;uwM%7#NZn7#NBf7#Nxv7#J2aFfbfuU|@I*YW9GN zFi=?l!j2EbxobcSd>Etw8H1Yt$Qa}UWDN2RG6wk!8Kc#MZmHt~DM*VaC9@cm zMqf&C^B_u3E(Qk2uTtC`km8ezf#Jxdh6V=qoXouJ%)E3ajyD?{8W>hEF|cq8gNv!O z#LS%1qSW}J)WqWaJVquluxNaGJgNi(0}BHS52R!R4;#d%6{V&!2rw|P@IuP3;?$D( zw4BU>cu-#>GdVFQr!qc24_pE=GO0jJMUnz5h04dL=j10P<}iZn;8O=H#ba80Nd_qY zF)~31PjDGpoLa)nz{1F+3^owiStxerTQFoCS*mjJ5-m&1l;@o7c*x$(KFx%owv zOacrHOac&pf_;Qzu!czx5<(C)xrv#1j7-{kKJoEsrFqHm@p@qMA(B`}d-xa>8JXDh zeDsP-iuf6L7@0UAY&}B(1~Uc$Mg{>UMkY?En30h>11AHM5F{Sq{sTt>1B)=EM~&hc zP^2U!CTABjFo{5Q6{V(S=Hx&d5ezJ%kS-%w7n1)On8f7qxE)kUF*0cp=TVU2$&3sN z3@qYE;SIJ7;XW1#Nw9rjhoonwAx8!SlO!KlG9DyRl3HBCAi&JPBn9;mND!S?94rcw21QIfc+>M!6nM?{uA|RCvOp1aKi*plma`Kboa}!fc7@1&+$S5PeI5{yV zl@*kXkp$Bdb8{0}7+4qt*bqs?NRUC8fkg=sZcrB@1*$Tn+n!k*4{{PX$RX*Qk%Mym2O|?SpfZedAR_UZY@kf5iJ=~B5Q=)RNPH$cNWB)M z)BsiL&?a>}H0;w$Q;Um1l{5pBHZRyy@hSQ7AcaWYHn5n+$RL0$U^$(Ufgh1l%nV`} z1Q?ifpwR`%U701t3{1Mv$ge6&O-{`$LkUd7nT!mA3`}}Zd#h3_3KH{D7?~iRi?2#a zO-n4zDT#-)7p5>WEMQ__(uXG6s-)7)oD^^`AvZCPkqHvZ2u+}RA~7#Hbt)r+0wa?) z#A~3YC}@-l)VIh@%!>!LMvC#PsAw&X^a?KVoGH@ch%3uZ~11|%MAtXRRF^L-G3@k=E;LyO7iif9$cu2Q9 zEhjOZfyEfpC}<0i#RO74ARCpJ577%vVGJy$c+|kmF_Qy32~+`=<`opBLPyb&DpMt} zf_SX5(5jRJl+?{3Cc(-y$cQ0$Y_%jG)cas$g47Z5@ZrRGkT0P|aDt4mz-t$*B8Jut z*d0`yTEYz~zaaSzhpWMrFc-*1OI5J%a5@@Z`$H-Zbhj6$mhggXgOp0>vS4d?K-O46 z(sX8CT0UysVhzo>U>Qg`ZUeUplu6Ebh%ZPiDo%waECwbAXu(iWoDvVRmVwC;CX$&96>)-zZ)g^ulvx5%0I3fcnB1X0fJj5a!2?>}KwJ;< zF?h_4$rD;?rKgq{8zrSOFnMu-<1juwwZwvf#Tzm%4lxOC5!@w=Ops(6pPZ4JoDEK% zkRa0o*-)IBF2ulrsO5}>k()&(A`H9;Ia5&vQ3fU-XgI@C5M&I%(2Rk}7ulUTsR)f0 z5)53365QC#k%60m$q$l3Kvl9as8%SB2T%PlF!@7kB51Xdo0wwEz!U)Wa&dNM0g_P` z&LGFbibZ2f7X~f{ra)*sWn`vjfI^;uDF`Z(lvx598er5e7}{;3N&Ium<6Xs9YsdW0ArgCqjdhwzfAc_f1f64xS%K@5p&84aq4Vi6H&Xa-7Xaj+Cw zoB}J^i$DkFUl`xU`m1pPi9JTNqkaH zesVSgQ!-R6J+&krG8BvCG_xcIb_CbLqMAVhiECK{$^|f&S{l@%a1HBFxJLC191Kh; zP-laq8d8D79FhttCF0YI^2?z;f%wwA;>5I62BtJ<*^Z$tzMv>SJw7EF$yH{R$o5*8 zS26H2Fr~wy2s*0`@hJmS2Bi1_cOxN1DyT&Q9;+>8V9JDsBx)do!Z-`64@D+DH7~U& zGns)Yn-4|Ykbx-&n@MHyM({QW)MtcO15++k3s?r!=7FXy2Bth{y9F!*iZ{@- z4g*s@)Q_nZC8>ES@zC}i15*LCazIwjz*LARSWHj~mLg~wlAc8T|p z`Pr#?h`_cqZ)arSW)MJ1B$gH(jL3<`(z25gl=|TfBm;vkMh0=D29kkcHzR{EvVc(! zBdGR7Xff_(WRPGG;6PSn(g*6P3veesGHuVVcePUg&GQTJ@U57!2kx7-X+Twy_J&;nu##Vrg)ds00Y-~kwL9#A`3D z5JuTEXc>rHu$dYLGcY1#&CHV-*cq7WppJvBbW1Br%!PG_kQ7^_Ag4kz%T(luGB-#A z#Y{c4Mn)R~fw|e-C=)q}nHy)Jm|>C);zFB0FxTXQR|4pjB&O$pQXVvNFlF`3O>-I8 z5biZM%R{ltFdaGc%q0yk}t z+DJ{{76#0hmL|=N3_*-cP?NHfG82ni7#SQGm>L)%1C{XwdAaeRPE&k&MrKKBaY15o zDg#p^BecQ?$)=~4fR^p0rxqc+XKB{P$RNZZz=hP@Ff^Xc2&xLm?NzLu*6y*$pNJO(a=d6AF-73MK{(BGiIM8kiW=h)`ObTEfI2st1~ghB`0D zC<8LaiA5YdmdV5*j;tPOOcaX@SVw#&6N5UEW3Z3azk$*7BN#SVrE#x%&~}BU=hRO4kl*9jIbmFBP_|l2um_BvS478fcX!-S%@WGv7``V0|rKYm=T~zK=lHaE`uQ!#~B+k zFroxBK~oqQm2lb$FWxXi%h-s4Q5~m7)b0ls-B=Q=F_x@pY|6l>Nu(`Ug9BR-VTnIu zEZNu?OC*_KDUM9AWKt6>nbZV}pG`~{7?olEgA@zk<}zflt{%K?&BCCHTNPLbq=C)C zpaN5ek%Yj?AO>SbhzXX&Z-OQ8Ot1u=36{V!!4i0;SOU)!OW>Je2|QCQfoF;(!I)z4 zhbb0+m}2pVDHea2V)2I=7Jryw@rM}}f0$wMhZz=sm|^jU85Vz-Vey9<7Jryw@rOAU zf0!FGFiOLc5F|l@8_uwKRu+sp${dR?&9V5>+=PKq0o7z!{{YdC!N?7mO7zUJ1dTbC zpfSf1H0D@>#vDt~SYQbn3oKD=f!V-*mOU{r%EB~33IVir9V`rJjaj%nXf*_A2?q-UW)NCnb0D_B zHpCX#hS&nz5L;jyVhe0T69z_j#|z>q*f=dH9pYSB!onbh78FQpL|7Qm3QLrAA}kE> zHX+19$eIw)S`iioNi@iDM0VB%+BSjHs4aED2dVKFl^%Q|Ku2Ij5Ij10_&nb{eb&ogr{FyCcn zWngGy5oVvn!o|Lbg^PVX3m40EmV6F|$1J%le^?3_n44J{85m}=<}xg2&1HGYTFAis z1|-tPmd&!8t%!m75E~-{!!&k9hSluF46oUl8NRVIGc$Yoe1Sjlinu#%xmn3-jQFe3xYG~sFnh9$x^4C{nz8FmQQFdP!D zWjG^T!*ETwmf?YL4Z|zpT8953xeWcHxeRkea~U>^<}yqZ%Vn4+mdmhStc2l&SRKQC zu{wrtVs#8N#OoNIiB~c-OE9x^N-#38^h;DTFwB);W?2m4t(2%|V0bQ(%g`v5%dkkQ zlHrt$B14O;G{YQO8HP==vJBs36&ZTvm>FiuF*7WYV`f+{m&0&FE{Em093unETRG4| z;Jf2(1@<-r zF7`=Vto*yovZF}yLCW@xmKVOVD& z%dpT=j$x~%Ji}#6Ifmz!@(fK@f()~)%PWSQp*+In=-HJ72!J(ppzdoIII_ezFFk6ean9+eF9JaSpKdo(aH zu=h4K(z;eU0fr)|PomVpx%U16eCI*Hh-nk4nymJ{|dgn5H^Uh`X=bg)N z&?lE|kuM*^2H#4Ci@v!GPkbwxfA}&oFm(7aGfehlW|-&4%&^vvnc=t}Gs^`(AqM8_ zevAw(fBfzf?--XGsB{AW|mdqLJZ6s!xtFTx)&GBCdnXJlai6VAon5y8c9EkcF;NdyO71|)yE0Dy(#0`ur>k~^En71V|GBBJ> zRA;%FD8#`00L0spl*_O$DVO0yQZ7SFaxTNPO4HK1TNW8C(ooGBz+WoX!wr zzn;Ox{y2k+VMnGY!}UxR_NSR#?4L5Z*#Bj6F&xQKWxtfg#qc&Omwib#7yE{6E{4_| zQHD7=s_d(BxR`h4XfiOL$kAY6zLR6h!2BV{oPqsM4i|ezE*HbJTtS9|xt0uHa+%p1 z^SIdi^SIb&=W($t&$nV=Iht?Hz;Y$uhJpD*J}8a~Y#DA9*fMM?v|~A0Y|p^(uGoR0 zr$m8eQ;8!3!|oC%mSZK(3=HQ=oEV;#*fPv5wPko*YRhn@%$DVEnF|ZU>T)NB)8(!V z3oC3HzE;>WY^k(mXsVK7SXt%7u&>IA<$RSJ1M{^iMh1p=Rjw@E)$R<;ld2gR7}i!h zuso^uU|?yi@nm4=uCZg7TjR>Ip~jtod3y~b1IzImF9w#!HQo#iZ)@yW`f7a`n5Wh< zGB9kebzrzxE5owC&Xi@IhM)DeEQcEymoqUip91ZiVd-sT6k=eW(g@l` zv#F6$j%6QM;3!CdqPc-hWq%dn-3(SrRz7b6$@nJz{yhFe{Xf()(QjFt>Lx*3_-k9IS1 zv0nla_qrLm7@B$*E!pSwFmkbP0TBm!7`YhE^?-Erf^_tPbo7FBfQWm&ART=m9es>k z%-i}HLl~Hk^f87pFkk9pj9_5C+s7Em!2G(8F`9w-TOVT#1AAjXBNuybKO+~z&3?u> z_U9n(R}j%Kfsu=0!URS^mN^qZA+dM@C?s}HV2o$zp2*0|uo^_1n8=vGaAzW;Aj^x1 zAVu#df)q7OVoYS{n#9P=ux1it62pecjLZxdCo?89Je{h+)Mj8_HU$($o2D>wG2EWQIFpg(T?!#$1NR*^Id?6J|4h zVq{>RJ{#nW#j_bp7}m^Y%w^aLBKCoZ;~?S^NaF5n#uAoEa~MA}GBD4a12Xdd9L6t< z49w3!9ELA*7;_o^fQat7jJXVxLBz7Tj3o>kLBvTA_u^c}T$V?3!6v+%3$pP$Ncca9 zm^qKJgk|+S#;=SF%$w$cOg}!4@f#xp^Eoi*+dQzYzaS1n=X}NzhDjh|Hi%dPBG!V4 zTOi^whCm*wU%#)*t93{RIa=Ca*b z&dA5`aXBbRLBzBbjJXU8S1{(X-C4oN#){n`QDk#wAP) z%(K>kGQqZWj9lzTK*WW0j9d(N)-kFwd|1Z_s_51;f@;D^>lwE&GR$AkxQUVBG>Bxm zyq*zM1>Ocr^lV_<&B$_i10$&FJH3H%FC)WGkP!Q>jf`9jr#3R?GF$}_Up6x4vc22H z$j5enGb0~E-xkJPwzjQ|d<+Y=GUhU@01>A^#3c~%2}Jza%9zXEyMvL7edZ2EE{1nI z7)2SncQVGY&)mt##l8$gY}(1l#qeV%V-|ZGs2?{4M9kmC$i=X87h^WVi(QO43_o`< z<}$SGX3S-~wVRQT;nQx$T!z0OqJIx4Qb5G+J&dXBXZA31vEKp_&-O5Ku`Jljn9sqm zW-ntd%gMcr1q{p=_JZPm?mosGhJ*VUb6DEFs}r2ZXRPi&&a_10LQjXUt`}ah&lYBLnk&kO<2!h;-u#ko2(=jJXUKK}73G#$1MJCmC~D z7M^6h#K^#M`6Sr3TPH!b{W!^ZnUR6{KZwKd<`knG%b!!AnxXkL$dr|*88`o4-(^N-hE-P>nHhFoVa#EsLVj`Fe%%9wP(u9}tIm*;NpSW$RUN(C@hlQgHez$e>FgHpAVkjLZy8*BC1qy00--eBZnd2)mC zF(U)Zs~e1b3@o2-FoN3oKW{J!F!bMK6lA#nkdc|;^+QHxmd_7CHvf7Eaz)D{u%50* zAU%^GF@hTWGaoSuFf4q;$jq?v5hF9p#z%~xM*ogSATJ+(1d2`&aT`QD1reVfGnO-S zK4BDO`}Ks8kLBAl#vhMGBPl9KW7wRnf)9rvG6%aV%2lTUyKY48$c5K zo-_UirHAK?e;659K0F7Ptbd*}<}%E9!I;ak@de`~Miz#>FBo&#KfYk(V*m4kk&B`G zC1WnbJP@%0L~H^PdtNf;vORps$i~w3ig7X{1M|dJAWzbGHDfNr1Q41mKWMgS~3%0HOEy%XnZ^2p?fjA7i-+|P;1F3lrQS%;T_L}#MxeVLiGv=~fhX~&T z3x9skIF*s*#|N;={~tgqCw~M99ry?~^Y}-Q&>N5t!@^IDxoihNF|x5d`vg}1_7h05 z=`-V0Mus(CK#~20QIY-U7e+3I-mi>;EHl4?{I=jLBO?Rjny-x07#VkdWt`5)c(}Zszqn3VSWM<#|jggCe|2IZ1hO^%o1zGNX0~zuJB+vN%8_1A9 z-#~_Reg_%y>^oyTL+1}rqwCNQ#$1LoAma89#$1*cKNx2-GO#TE$vB6RfnnQE#$1NO zKN)jbw*6w9%gDfT`xoOpMh1qy-;B8o3qZuC-;B8|FMl)6XJlZR{|7`dH2-H@z{s}t zKO;B8ga3>R85t%wGc90bJKxO2&2Y4ZX(1!S^LD01j12cXKN!@lOSW~M5g78jH@Oxtzcx> zIgx1@Bg@f=OhOFIXC^W+GB949$h4A?@%BWfRg8=e!D^OHVya*}HkpZ;ZSD*vezt@2 znfMt#&j%T|VF5_@o&_M?hZit0GBEsD2y)+@MNA7BSvD_bTFuD7aAz^o8b+3vi<#UR z7*;M}TFc0AY6;UiMwZ)4K+-IWmVzYKEoEB6$k4J3B)wo6(|Sgh%gdNnGcq!~26JYw z0CP64U|P?}uyQ5SLPoaM)l6&*YgaSnG8_aE=T|e$WMp}`nyHY1`Ne7`Mh50@Ad;bd z4alJr)-W-%&t1dB#js`#Q!c|c5YfGsDV1g8TBcG4<{fK6cdK0mv+sb|3~SdhZD3^C zz78b2Zym^Im)0?CT5lw>p=?MuLn6` z$_6HL2Il!2m`oWMzHVUBV)(d;iJ75wGgB_Z6cDj^GgB^G_ZB8TwijEN_!#=OGUc*; z*~-Mn(6bGs1Vk(b5vxJOu5C=Y?7iEWxY%cGXX0X5u$?KFVHJqj0wVT-h!Y^<5{S41 zBA$VW4o?yFtF+zK4mK@9Ow24Z z_JNI^zYk>Wi+xPF3@i3Cdx(jdVZ&i2MV5nynLuUTiNj2c4D449GjTCIJj|5K@CHOoJHnL9a0EpB zKLU!0qfBfpGmkNW%Gd?RKyKK0j479W_bDbW_TwO8_hlw7hL4w-av55#Fy%6=xdO_Q zyRU%K@%bxEG7QhIFs)-`Sbr6i722+W(mKPoYoM&~2Eu8$4$1{9t~0qaFt5K3vf%o4 zkXx=Z<+41w!8DhNf%)YPkZ|)&kjYDKGRZJ(x(PDi;7zdXiJKtVyEj4BOuxmno{|0F zEhaAZv$vSISlVtgbuck7_ud96nSUFUSZ*`rvh2Cd)XBuad>AZz6(sxsMEn8~ZFfM$ z$sML#mZNu=x|kT4&)flN{s9v1y~~u#GW#ymYQ_T$3{UPdXY3F)^@ge#11MiGgM38&K*w^oD5x69dDEH%tNy^WK6KpM48b#qbZz+4v5`VLA1V zX(1B>!}oWfNcr)BNr7$JM>hPU6C1Q`B(W2#`8_?=0JfnnKq zrgD~b-C!prz6p-|~ zDa=n985p)qVHRNdIFKM2Tj>b2T7cp&isOrf#J$@ zW&!3W)0ra}m_JNsj$~l|H=Q|zfnnM#W02I?g)0QqL?0_IW%<~<-b!bObpBu7lV{8TFjiwa&zk-FERx&SRWZAV6yX zxv5D-spSgEIf;4cY=#zkhI$qZJ3yNFOF$>4mc*x}CYF>Ir4}=5^XHa=SNH2e7V#Eq ZbAVPQ>84hc=yDZjBo?Kn=;o&80sxn8aEJf^ literal 0 HcmV?d00001 diff --git a/vendor/stb/lib/stb_image_write_wasm.o b/vendor/stb/lib/stb_image_write_wasm.o new file mode 100644 index 0000000000000000000000000000000000000000..279803330045877278c773e73afbcf3abfef2f89 GIT binary patch literal 24259 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 BF6RIM literal 0 HcmV?d00001 diff --git a/vendor/stb/lib/stb_sprintf_wasm.o b/vendor/stb/lib/stb_sprintf_wasm.o new file mode 100644 index 0000000000000000000000000000000000000000..4c8e140e5dafae0d1dfceb1b455cbb438bec1307 GIT binary patch literal 13793 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ø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 literal 0 HcmV?d00001 diff --git a/vendor/stb/lib/stb_truetype_wasm.o b/vendor/stb/lib/stb_truetype_wasm.o index d3380e8a2441184569d910562ff374d9d621d4f0..e545ef8e7f2ae4527168f2c970ef30248bb5fb44 100644 GIT binary patch delta 25437 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 T#LbCJMGW^QF%?bL-kJ^o_Nu|e diff --git a/vendor/stb/rect_pack/stb_rect_pack.odin b/vendor/stb/rect_pack/stb_rect_pack.odin index 6c0b56378..696b9c8c0 100644 --- a/vendor/stb/rect_pack/stb_rect_pack.odin +++ b/vendor/stb/rect_pack/stb_rect_pack.odin @@ -9,6 +9,7 @@ LIB :: ( "../lib/stb_rect_pack.lib" when ODIN_OS == .Windows else "../lib/stb_rect_pack.a" when ODIN_OS == .Linux else "../lib/darwin/stb_rect_pack.a" when ODIN_OS == .Darwin + else "../lib/stb_rect_pack_wasm.o" when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 else "" ) @@ -16,7 +17,11 @@ when LIB != "" { when !#exists(LIB) { #panic("Could not find the compiled STB libraries, they can be compiled by running `make -C \"" + ODIN_ROOT + "vendor/stb/src\"`") } +} +when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 { + foreign import lib "../lib/stb_rect_pack_wasm.o" +} else when LIB != "" { foreign import lib { LIB } } else { foreign import lib "system:stb_rect_pack" diff --git a/vendor/stb/rect_pack/stb_rect_pack_wasm.odin b/vendor/stb/rect_pack/stb_rect_pack_wasm.odin new file mode 100644 index 000000000..fb75552ec --- /dev/null +++ b/vendor/stb/rect_pack/stb_rect_pack_wasm.odin @@ -0,0 +1,4 @@ +//+build wasm32, wasm64p32 +package stb_rect_pack + +@(require) import _ "vendor:libc" diff --git a/vendor/stb/sprintf/stb_sprintf.odin b/vendor/stb/sprintf/stb_sprintf.odin new file mode 100644 index 000000000..ec5036e45 --- /dev/null +++ b/vendor/stb/sprintf/stb_sprintf.odin @@ -0,0 +1,37 @@ +package stb_sprintf + +import "core:c" + +@(private) +LIB :: ( + "../lib/stb_sprintf.lib" when ODIN_OS == .Windows + else "../lib/stb_sprintf.a" when ODIN_OS == .Linux + else "../lib/darwin/stb_sprintf.a" when ODIN_OS == .Darwin + else "../lib/stb_sprintf_wasm.o" when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 + else "" +) + +when LIB != "" { + when !#exists(LIB) { + #panic("Could not find the compiled STB libraries, they can be compiled by running `make -C \"" + ODIN_ROOT + "vendor/stb/src\"`") + } +} + +when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 { + foreign import stbpf "../lib/stb_sprintf_wasm.o" +} else when LIB != "" { + foreign import stbpf { LIB } +} else { + foreign import stbpf "system:stb_sprintf" +} + +@(link_prefix="stbsp_", default_calling_convention="c") +foreign stbpf { + sprintf :: proc(buf: [^]byte, fmt: cstring, #c_vararg args: ..any) -> i32 --- + snprintf :: proc(buf: [^]byte, count: i32, fmt: cstring, #c_vararg args: ..any) -> i32 --- + vsprintf :: proc(buf: [^]byte, fmt: cstring, va: c.va_list) -> i32 --- + vsnprintf :: proc(buf: [^]byte, count: i32, fmt: cstring, va: ^c.va_list) -> i32 --- + vsprintfcb :: proc(callback: SPRINTFCB, user: rawptr, buf: [^]byte, fmt: cstring, va: ^c.va_list) -> i32 --- +} + +SPRINTFCB :: #type proc "c" (buf: [^]byte, user: rawptr, len: i32) -> cstring diff --git a/vendor/stb/src/Makefile b/vendor/stb/src/Makefile index b7217d528..194ea5e75 100644 --- a/vendor/stb/src/Makefile +++ b/vendor/stb/src/Makefile @@ -8,17 +8,24 @@ endif wasm: mkdir -p ../lib - $(CC) -c -Os --target=wasm32 -nostdlib stb_truetype_wasm.c -o ../lib/stb_truetype_wasm.o + $(CC) -c -Os --target=wasm32 --sysroot=$(shell odin root)/vendor/libc stb_image.c -o ../lib/stb_image_wasm.o -DSTBI_NO_STDIO + $(CC) -c -Os --target=wasm32 --sysroot=$(shell odin root)/vendor/libc stb_image_write.c -o ../lib/stb_image_write_wasm.o -DSTBI_WRITE_NO_STDIO + $(CC) -c -Os --target=wasm32 --sysroot=$(shell odin root)/vendor/libc stb_image_resize.c -o ../lib/stb_image_resize_wasm.o + $(CC) -c -Os --target=wasm32 --sysroot=$(shell odin root)/vendor/libc stb_truetype.c -o ../lib/stb_truetype_wasm.o + # $(CC) -c -Os --target=wasm32 --sysroot=$(shell odin root)/vendor/libc stb_vorbis.c -o ../lib/stb_vorbis_wasm.o -DSTB_VORBIS_NO_STDIO + $(CC) -c -Os --target=wasm32 --sysroot=$(shell odin root)/vendor/libc stb_rect_pack.c -o ../lib/stb_rect_pack_wasm.o + $(CC) -c -Os --target=wasm32 stb_sprintf.c -o ../lib/stb_sprintf_wasm.o unix: mkdir -p ../lib - $(CC) -c -O2 -Os -fPIC stb_image.c stb_image_write.c stb_image_resize.c stb_truetype.c stb_rect_pack.c stb_vorbis.c + $(CC) -c -O2 -Os -fPIC stb_image.c stb_image_write.c stb_image_resize.c stb_truetype.c stb_rect_pack.c stb_vorbis.c stb_sprintf.c $(AR) rcs ../lib/stb_image.a stb_image.o $(AR) rcs ../lib/stb_image_write.a stb_image_write.o $(AR) rcs ../lib/stb_image_resize.a stb_image_resize.o $(AR) rcs ../lib/stb_truetype.a stb_truetype.o $(AR) rcs ../lib/stb_rect_pack.a stb_rect_pack.o $(AR) rcs ../lib/stb_vorbis.a stb_vorbis.o + $(AR) rcs ../lib/stb_sprintf.a stb_sprintf.o #$(CC) -fPIC -shared -Wl,-soname=stb_image.so -o ../lib/stb_image.so stb_image.o #$(CC) -fPIC -shared -Wl,-soname=stb_image_write.so -o ../lib/stb_image_write.so stb_image_write.o #$(CC) -fPIC -shared -Wl,-soname=stb_image_resize.so -o ../lib/stb_image_resize.so stb_image_resize.o @@ -47,4 +54,7 @@ darwin: $(CC) -arch x86_64 -c -O2 -Os -fPIC stb_vorbis.c -o stb_vorbis-x86_64.o -mmacosx-version-min=10.12 $(CC) -arch arm64 -c -O2 -Os -fPIC stb_vorbis.c -o stb_vorbis-arm64.o -mmacosx-version-min=10.12 lipo -create stb_vorbis-x86_64.o stb_vorbis-arm64.o -output ../lib/darwin/stb_vorbis.a + $(CC) -arch x86_64 -c -O2 -Os -fPIC stb_sprintf.c -o stb_sprintf-x86_64.o -mmacosx-version-min=10.12 + $(CC) -arch arm64 -c -O2 -Os -fPIC stb_sprintf.c -o stb_sprintf-arm64.o -mmacosx-version-min=10.12 + lipo -create stb_sprintf-x86_64.o stb_sprintf-arm64.o -output ../lib/darwin/stb_sprintf.a rm *.o diff --git a/vendor/stb/src/stb_sprintf.c b/vendor/stb/src/stb_sprintf.c new file mode 100644 index 000000000..d60a91bae --- /dev/null +++ b/vendor/stb/src/stb_sprintf.c @@ -0,0 +1,2 @@ +#define STB_SPRINTF_IMPLEMENTATION +#include "stb_sprintf.h" diff --git a/vendor/stb/src/stb_sprintf.h b/vendor/stb/src/stb_sprintf.h new file mode 100644 index 000000000..ca432a6bc --- /dev/null +++ b/vendor/stb/src/stb_sprintf.h @@ -0,0 +1,1906 @@ +// stb_sprintf - v1.10 - public domain snprintf() implementation +// originally by Jeff Roberts / RAD Game Tools, 2015/10/20 +// http://github.com/nothings/stb +// +// allowed types: sc uidBboXx p AaGgEef n +// lengths : hh h ll j z t I64 I32 I +// +// Contributors: +// Fabian "ryg" Giesen (reformatting) +// github:aganm (attribute format) +// +// Contributors (bugfixes): +// github:d26435 +// github:trex78 +// github:account-login +// Jari Komppa (SI suffixes) +// Rohit Nirmal +// Marcin Wojdyr +// Leonard Ritter +// Stefano Zanotti +// Adam Allison +// Arvid Gerstmann +// Markus Kolb +// +// LICENSE: +// +// See end of file for license information. + +#ifndef STB_SPRINTF_H_INCLUDE +#define STB_SPRINTF_H_INCLUDE + +/* +Single file sprintf replacement. + +Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20. +Hereby placed in public domain. + +This is a full sprintf replacement that supports everything that +the C runtime sprintfs support, including float/double, 64-bit integers, +hex floats, field parameters (%*.*d stuff), length reads backs, etc. + +Why would you need this if sprintf already exists? Well, first off, +it's *much* faster (see below). It's also much smaller than the CRT +versions code-space-wise. We've also added some simple improvements +that are super handy (commas in thousands, callbacks at buffer full, +for example). Finally, the format strings for MSVC and GCC differ +for 64-bit integers (among other small things), so this lets you use +the same format strings in cross platform code. + +It uses the standard single file trick of being both the header file +and the source itself. If you just include it normally, you just get +the header file function definitions. To get the code, you include +it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first. + +It only uses va_args macros from the C runtime to do it's work. It +does cast doubles to S64s and shifts and divides U64s, which does +drag in CRT code on most platforms. + +It compiles to roughly 8K with float support, and 4K without. +As a comparison, when using MSVC static libs, calling sprintf drags +in 16K. + +API: +==== +int stbsp_sprintf( char * buf, char const * fmt, ... ) +int stbsp_snprintf( char * buf, int count, char const * fmt, ... ) + Convert an arg list into a buffer. stbsp_snprintf always returns + a zero-terminated string (unlike regular snprintf). + +int stbsp_vsprintf( char * buf, char const * fmt, va_list va ) +int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va ) + Convert a va_list arg list into a buffer. stbsp_vsnprintf always returns + a zero-terminated string (unlike regular snprintf). + +int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va ) + typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len ); + Convert into a buffer, calling back every STB_SPRINTF_MIN chars. + Your callback can then copy the chars out, print them or whatever. + This function is actually the workhorse for everything else. + The buffer you pass in must hold at least STB_SPRINTF_MIN characters. + // you return the next buffer to use or 0 to stop converting + +void stbsp_set_separators( char comma, char period ) + Set the comma and period characters to use. + +FLOATS/DOUBLES: +=============== +This code uses a internal float->ascii conversion method that uses +doubles with error correction (double-doubles, for ~105 bits of +precision). This conversion is round-trip perfect - that is, an atof +of the values output here will give you the bit-exact double back. + +One difference is that our insignificant digits will be different than +with MSVC or GCC (but they don't match each other either). We also +don't attempt to find the minimum length matching float (pre-MSVC15 +doesn't either). + +If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT +and you'll save 4K of code space. + +64-BIT INTS: +============ +This library also supports 64-bit integers and you can use MSVC style or +GCC style indicators (%I64d or %lld). It supports the C99 specifiers +for size_t and ptr_diff_t (%jd %zd) as well. + +EXTRAS: +======= +Like some GCCs, for integers and floats, you can use a ' (single quote) +specifier and commas will be inserted on the thousands: "%'d" on 12345 +would print 12,345. + +For integers and floats, you can use a "$" specifier and the number +will be converted to float and then divided to get kilo, mega, giga or +tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is +"2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn +2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three +$:s: "%$$$d" -> "2.42 M". To remove the space between the number and the +suffix, add "_" specifier: "%_$d" -> "2.53M". + +In addition to octal and hexadecimal conversions, you can print +integers in binary: "%b" for 256 would print 100. + +PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC): +=================================================================== +"%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC) +"%24d" across all 32-bit ints (4.5x/4.2x faster) +"%x" across all 32-bit ints (4.5x/3.8x faster) +"%08x" across all 32-bit ints (4.3x/3.8x faster) +"%f" across e-10 to e+10 floats (7.3x/6.0x faster) +"%e" across e-10 to e+10 floats (8.1x/6.0x faster) +"%g" across e-10 to e+10 floats (10.0x/7.1x faster) +"%f" for values near e-300 (7.9x/6.5x faster) +"%f" for values near e+300 (10.0x/9.1x faster) +"%e" for values near e-300 (10.1x/7.0x faster) +"%e" for values near e+300 (9.2x/6.0x faster) +"%.320f" for values near e-300 (12.6x/11.2x faster) +"%a" for random values (8.6x/4.3x faster) +"%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster) +"%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster) +"%s%s%s" for 64 char strings (7.1x/7.3x faster) +"...512 char string..." ( 35.0x/32.5x faster!) +*/ + +#if defined(__clang__) + #if defined(__has_feature) && defined(__has_attribute) + #if __has_feature(address_sanitizer) + #if __has_attribute(__no_sanitize__) + #define STBSP__ASAN __attribute__((__no_sanitize__("address"))) + #elif __has_attribute(__no_sanitize_address__) + #define STBSP__ASAN __attribute__((__no_sanitize_address__)) + #elif __has_attribute(__no_address_safety_analysis__) + #define STBSP__ASAN __attribute__((__no_address_safety_analysis__)) + #endif + #endif + #endif +#elif defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + #if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__ + #define STBSP__ASAN __attribute__((__no_sanitize_address__)) + #endif +#endif + +#ifndef STBSP__ASAN +#define STBSP__ASAN +#endif + +#ifdef STB_SPRINTF_STATIC +#define STBSP__PUBLICDEC static +#define STBSP__PUBLICDEF static STBSP__ASAN +#else +#ifdef __cplusplus +#define STBSP__PUBLICDEC extern "C" +#define STBSP__PUBLICDEF extern "C" STBSP__ASAN +#else +#define STBSP__PUBLICDEC extern +#define STBSP__PUBLICDEF STBSP__ASAN +#endif +#endif + +#if defined(__has_attribute) + #if __has_attribute(format) + #define STBSP__ATTRIBUTE_FORMAT(fmt,va) __attribute__((format(printf,fmt,va))) + #endif +#endif + +#ifndef STBSP__ATTRIBUTE_FORMAT +#define STBSP__ATTRIBUTE_FORMAT(fmt,va) +#endif + +#ifdef _MSC_VER +#define STBSP__NOTUSED(v) (void)(v) +#else +#define STBSP__NOTUSED(v) (void)sizeof(v) +#endif + +#include // for va_arg(), va_list() +#include // size_t, ptrdiff_t + +#ifndef STB_SPRINTF_MIN +#define STB_SPRINTF_MIN 512 // how many characters per callback +#endif +typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len); + +#ifndef STB_SPRINTF_DECORATE +#define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names +#endif + +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2,3); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3,4); + +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va); +STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char period); + +#endif // STB_SPRINTF_H_INCLUDE + +#ifdef STB_SPRINTF_IMPLEMENTATION + +#define stbsp__uint32 unsigned int +#define stbsp__int32 signed int + +#ifdef _MSC_VER +#define stbsp__uint64 unsigned __int64 +#define stbsp__int64 signed __int64 +#else +#define stbsp__uint64 unsigned long long +#define stbsp__int64 signed long long +#endif +#define stbsp__uint16 unsigned short + +#ifndef stbsp__uintptr +#if defined(__ppc64__) || defined(__powerpc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) || defined(__s390x__) +#define stbsp__uintptr stbsp__uint64 +#else +#define stbsp__uintptr stbsp__uint32 +#endif +#endif + +#ifndef STB_SPRINTF_MSVC_MODE // used for MSVC2013 and earlier (MSVC2015 matches GCC) +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#define STB_SPRINTF_MSVC_MODE +#endif +#endif + +#ifdef STB_SPRINTF_NOUNALIGNED // define this before inclusion to force stbsp_sprintf to always use aligned accesses +#define STBSP__UNALIGNED(code) +#else +#define STBSP__UNALIGNED(code) code +#endif + +#ifndef STB_SPRINTF_NOFLOAT +// internal float utility functions +static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits); +static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value); +#define STBSP__SPECIAL 0x7000 +#endif + +static char stbsp__period = '.'; +static char stbsp__comma = ','; +static struct +{ + short temp; // force next field to be 2-byte aligned + char pair[201]; +} stbsp__digitpair = +{ + 0, + "00010203040506070809101112131415161718192021222324" + "25262728293031323334353637383940414243444546474849" + "50515253545556575859606162636465666768697071727374" + "75767778798081828384858687888990919293949596979899" +}; + +STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char pcomma, char pperiod) +{ + stbsp__period = pperiod; + stbsp__comma = pcomma; +} + +#define STBSP__LEFTJUST 1 +#define STBSP__LEADINGPLUS 2 +#define STBSP__LEADINGSPACE 4 +#define STBSP__LEADING_0X 8 +#define STBSP__LEADINGZERO 16 +#define STBSP__INTMAX 32 +#define STBSP__TRIPLET_COMMA 64 +#define STBSP__NEGATIVE 128 +#define STBSP__METRIC_SUFFIX 256 +#define STBSP__HALFWIDTH 512 +#define STBSP__METRIC_NOSPACE 1024 +#define STBSP__METRIC_1024 2048 +#define STBSP__METRIC_JEDEC 4096 + +static void stbsp__lead_sign(stbsp__uint32 fl, char *sign) +{ + sign[0] = 0; + if (fl & STBSP__NEGATIVE) { + sign[0] = 1; + sign[1] = '-'; + } else if (fl & STBSP__LEADINGSPACE) { + sign[0] = 1; + sign[1] = ' '; + } else if (fl & STBSP__LEADINGPLUS) { + sign[0] = 1; + sign[1] = '+'; + } +} + +static STBSP__ASAN stbsp__uint32 stbsp__strlen_limited(char const *s, stbsp__uint32 limit) +{ + char const * sn = s; + + // get up to 4-byte alignment + for (;;) { + if (((stbsp__uintptr)sn & 3) == 0) + break; + + if (!limit || *sn == 0) + return (stbsp__uint32)(sn - s); + + ++sn; + --limit; + } + + // scan over 4 bytes at a time to find terminating 0 + // this will intentionally scan up to 3 bytes past the end of buffers, + // but becase it works 4B aligned, it will never cross page boundaries + // (hence the STBSP__ASAN markup; the over-read here is intentional + // and harmless) + while (limit >= 4) { + stbsp__uint32 v = *(stbsp__uint32 *)sn; + // bit hack to find if there's a 0 byte in there + if ((v - 0x01010101) & (~v) & 0x80808080UL) + break; + + sn += 4; + limit -= 4; + } + + // handle the last few characters to find actual size + while (limit && *sn) { + ++sn; + --limit; + } + + return (stbsp__uint32)(sn - s); +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va) +{ + static char hex[] = "0123456789abcdefxp"; + static char hexu[] = "0123456789ABCDEFXP"; + char *bf; + char const *f; + int tlen = 0; + + bf = buf; + f = fmt; + for (;;) { + stbsp__int32 fw, pr, tz; + stbsp__uint32 fl; + + // macros for the callback buffer stuff + #define stbsp__chk_cb_bufL(bytes) \ + { \ + int len = (int)(bf - buf); \ + if ((len + (bytes)) >= STB_SPRINTF_MIN) { \ + tlen += len; \ + if (0 == (bf = buf = callback(buf, user, len))) \ + goto done; \ + } \ + } + #define stbsp__chk_cb_buf(bytes) \ + { \ + if (callback) { \ + stbsp__chk_cb_bufL(bytes); \ + } \ + } + #define stbsp__flush_cb() \ + { \ + stbsp__chk_cb_bufL(STB_SPRINTF_MIN - 1); \ + } // flush if there is even one byte in the buffer + #define stbsp__cb_buf_clamp(cl, v) \ + cl = v; \ + if (callback) { \ + int lg = STB_SPRINTF_MIN - (int)(bf - buf); \ + if (cl > lg) \ + cl = lg; \ + } + + // fast copy everything up to the next % (or end of string) + for (;;) { + while (((stbsp__uintptr)f) & 3) { + schk1: + if (f[0] == '%') + goto scandd; + schk2: + if (f[0] == 0) + goto endfmt; + stbsp__chk_cb_buf(1); + *bf++ = f[0]; + ++f; + } + for (;;) { + // Check if the next 4 bytes contain %(0x25) or end of string. + // Using the 'hasless' trick: + // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord + stbsp__uint32 v, c; + v = *(stbsp__uint32 *)f; + c = (~v) & 0x80808080; + if (((v ^ 0x25252525) - 0x01010101) & c) + goto schk1; + if ((v - 0x01010101) & c) + goto schk2; + if (callback) + if ((STB_SPRINTF_MIN - (int)(bf - buf)) < 4) + goto schk1; + #ifdef STB_SPRINTF_NOUNALIGNED + if(((stbsp__uintptr)bf) & 3) { + bf[0] = f[0]; + bf[1] = f[1]; + bf[2] = f[2]; + bf[3] = f[3]; + } else + #endif + { + *(stbsp__uint32 *)bf = v; + } + bf += 4; + f += 4; + } + } + scandd: + + ++f; + + // ok, we have a percent, read the modifiers first + fw = 0; + pr = -1; + fl = 0; + tz = 0; + + // flags + for (;;) { + switch (f[0]) { + // if we have left justify + case '-': + fl |= STBSP__LEFTJUST; + ++f; + continue; + // if we have leading plus + case '+': + fl |= STBSP__LEADINGPLUS; + ++f; + continue; + // if we have leading space + case ' ': + fl |= STBSP__LEADINGSPACE; + ++f; + continue; + // if we have leading 0x + case '#': + fl |= STBSP__LEADING_0X; + ++f; + continue; + // if we have thousand commas + case '\'': + fl |= STBSP__TRIPLET_COMMA; + ++f; + continue; + // if we have kilo marker (none->kilo->kibi->jedec) + case '$': + if (fl & STBSP__METRIC_SUFFIX) { + if (fl & STBSP__METRIC_1024) { + fl |= STBSP__METRIC_JEDEC; + } else { + fl |= STBSP__METRIC_1024; + } + } else { + fl |= STBSP__METRIC_SUFFIX; + } + ++f; + continue; + // if we don't want space between metric suffix and number + case '_': + fl |= STBSP__METRIC_NOSPACE; + ++f; + continue; + // if we have leading zero + case '0': + fl |= STBSP__LEADINGZERO; + ++f; + goto flags_done; + default: goto flags_done; + } + } + flags_done: + + // get the field width + if (f[0] == '*') { + fw = va_arg(va, stbsp__uint32); + ++f; + } else { + while ((f[0] >= '0') && (f[0] <= '9')) { + fw = fw * 10 + f[0] - '0'; + f++; + } + } + // get the precision + if (f[0] == '.') { + ++f; + if (f[0] == '*') { + pr = va_arg(va, stbsp__uint32); + ++f; + } else { + pr = 0; + while ((f[0] >= '0') && (f[0] <= '9')) { + pr = pr * 10 + f[0] - '0'; + f++; + } + } + } + + // handle integer size overrides + switch (f[0]) { + // are we halfwidth? + case 'h': + fl |= STBSP__HALFWIDTH; + ++f; + if (f[0] == 'h') + ++f; // QUARTERWIDTH + break; + // are we 64-bit (unix style) + case 'l': + fl |= ((sizeof(long) == 8) ? STBSP__INTMAX : 0); + ++f; + if (f[0] == 'l') { + fl |= STBSP__INTMAX; + ++f; + } + break; + // are we 64-bit on intmax? (c99) + case 'j': + fl |= (sizeof(size_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + // are we 64-bit on size_t or ptrdiff_t? (c99) + case 'z': + fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + case 't': + fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + // are we 64-bit (msft style) + case 'I': + if ((f[1] == '6') && (f[2] == '4')) { + fl |= STBSP__INTMAX; + f += 3; + } else if ((f[1] == '3') && (f[2] == '2')) { + f += 3; + } else { + fl |= ((sizeof(void *) == 8) ? STBSP__INTMAX : 0); + ++f; + } + break; + default: break; + } + + // handle each replacement + switch (f[0]) { + #define STBSP__NUMSZ 512 // big enough for e308 (with commas) or e-307 + char num[STBSP__NUMSZ]; + char lead[8]; + char tail[8]; + char *s; + char const *h; + stbsp__uint32 l, n, cs; + stbsp__uint64 n64; +#ifndef STB_SPRINTF_NOFLOAT + double fv; +#endif + stbsp__int32 dp; + char const *sn; + + case 's': + // get the string + s = va_arg(va, char *); + if (s == 0) + s = (char *)"null"; + // get the length, limited to desired precision + // always limit to ~0u chars since our counts are 32b + l = stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u); + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + // copy the string in + goto scopy; + + case 'c': // char + // get the character + s = num + STBSP__NUMSZ - 1; + *s = (char)va_arg(va, int); + l = 1; + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + goto scopy; + + case 'n': // weird write-bytes specifier + { + int *d = va_arg(va, int *); + *d = tlen + (int)(bf - buf); + } break; + +#ifdef STB_SPRINTF_NOFLOAT + case 'A': // float + case 'a': // hex float + case 'G': // float + case 'g': // float + case 'E': // float + case 'e': // float + case 'f': // float + va_arg(va, double); // eat it + s = (char *)"No float"; + l = 8; + lead[0] = 0; + tail[0] = 0; + pr = 0; + cs = 0; + STBSP__NOTUSED(dp); + goto scopy; +#else + case 'A': // hex float + case 'a': // hex float + h = (f[0] == 'A') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_parts((stbsp__int64 *)&n64, &dp, fv)) + fl |= STBSP__NEGATIVE; + + s = num + 64; + + stbsp__lead_sign(fl, lead); + + if (dp == -1023) + dp = (n64) ? -1022 : 0; + else + n64 |= (((stbsp__uint64)1) << 52); + n64 <<= (64 - 56); + if (pr < 15) + n64 += ((((stbsp__uint64)8) << 56) >> (pr * 4)); +// add leading chars + +#ifdef STB_SPRINTF_MSVC_MODE + *s++ = '0'; + *s++ = 'x'; +#else + lead[1 + lead[0]] = '0'; + lead[2 + lead[0]] = 'x'; + lead[0] += 2; +#endif + *s++ = h[(n64 >> 60) & 15]; + n64 <<= 4; + if (pr) + *s++ = stbsp__period; + sn = s; + + // print the bits + n = pr; + if (n > 13) + n = 13; + if (pr > (stbsp__int32)n) + tz = pr - n; + pr = 0; + while (n--) { + *s++ = h[(n64 >> 60) & 15]; + n64 <<= 4; + } + + // print the expo + tail[1] = h[17]; + if (dp < 0) { + tail[2] = '-'; + dp = -dp; + } else + tail[2] = '+'; + n = (dp >= 1000) ? 6 : ((dp >= 100) ? 5 : ((dp >= 10) ? 4 : 3)); + tail[0] = (char)n; + for (;;) { + tail[n] = '0' + dp % 10; + if (n <= 3) + break; + --n; + dp /= 10; + } + + dp = (int)(s - sn); + l = (int)(s - (num + 64)); + s = num + 64; + cs = 1 + (3 << 24); + goto scopy; + + case 'G': // float + case 'g': // float + h = (f[0] == 'G') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; + else if (pr == 0) + pr = 1; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, (pr - 1) | 0x80000000)) + fl |= STBSP__NEGATIVE; + + // clamp the precision and delete extra zeros after clamp + n = pr; + if (l > (stbsp__uint32)pr) + l = pr; + while ((l > 1) && (pr) && (sn[l - 1] == '0')) { + --pr; + --l; + } + + // should we use %e + if ((dp <= -4) || (dp > (stbsp__int32)n)) { + if (pr > (stbsp__int32)l) + pr = l - 1; + else if (pr) + --pr; // when using %e, there is one digit before the decimal + goto doexpfromg; + } + // this is the insane action to get the pr to match %g semantics for %f + if (dp > 0) { + pr = (dp < (stbsp__int32)l) ? l - dp : 0; + } else { + pr = -dp + ((pr > (stbsp__int32)l) ? (stbsp__int32) l : pr); + } + goto dofloatfromg; + + case 'E': // float + case 'e': // float + h = (f[0] == 'E') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr | 0x80000000)) + fl |= STBSP__NEGATIVE; + doexpfromg: + tail[0] = 0; + stbsp__lead_sign(fl, lead); + if (dp == STBSP__SPECIAL) { + s = (char *)sn; + cs = 0; + pr = 0; + goto scopy; + } + s = num + 64; + // handle leading chars + *s++ = sn[0]; + + if (pr) + *s++ = stbsp__period; + + // handle after decimal + if ((l - 1) > (stbsp__uint32)pr) + l = pr + 1; + for (n = 1; n < l; n++) + *s++ = sn[n]; + // trailing zeros + tz = pr - (l - 1); + pr = 0; + // dump expo + tail[1] = h[0xe]; + dp -= 1; + if (dp < 0) { + tail[2] = '-'; + dp = -dp; + } else + tail[2] = '+'; +#ifdef STB_SPRINTF_MSVC_MODE + n = 5; +#else + n = (dp >= 100) ? 5 : 4; +#endif + tail[0] = (char)n; + for (;;) { + tail[n] = '0' + dp % 10; + if (n <= 3) + break; + --n; + dp /= 10; + } + cs = 1 + (3 << 24); // how many tens + goto flt_lead; + + case 'f': // float + fv = va_arg(va, double); + doafloat: + // do kilos + if (fl & STBSP__METRIC_SUFFIX) { + double divisor; + divisor = 1000.0f; + if (fl & STBSP__METRIC_1024) + divisor = 1024.0; + while (fl < 0x4000000) { + if ((fv < divisor) && (fv > -divisor)) + break; + fv /= divisor; + fl += 0x1000000; + } + } + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr)) + fl |= STBSP__NEGATIVE; + dofloatfromg: + tail[0] = 0; + stbsp__lead_sign(fl, lead); + if (dp == STBSP__SPECIAL) { + s = (char *)sn; + cs = 0; + pr = 0; + goto scopy; + } + s = num + 64; + + // handle the three decimal varieties + if (dp <= 0) { + stbsp__int32 i; + // handle 0.000*000xxxx + *s++ = '0'; + if (pr) + *s++ = stbsp__period; + n = -dp; + if ((stbsp__int32)n > pr) + n = pr; + i = n; + while (i) { + if ((((stbsp__uintptr)s) & 3) == 0) + break; + *s++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)s = 0x30303030; + s += 4; + i -= 4; + } + while (i) { + *s++ = '0'; + --i; + } + if ((stbsp__int32)(l + n) > pr) + l = pr - n; + i = l; + while (i) { + *s++ = *sn++; + --i; + } + tz = pr - (n + l); + cs = 1 + (3 << 24); // how many tens did we write (for commas below) + } else { + cs = (fl & STBSP__TRIPLET_COMMA) ? ((600 - (stbsp__uint32)dp) % 3) : 0; + if ((stbsp__uint32)dp >= l) { + // handle xxxx000*000.0 + n = 0; + for (;;) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } else { + *s++ = sn[n]; + ++n; + if (n >= l) + break; + } + } + if (n < (stbsp__uint32)dp) { + n = dp - n; + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + while (n) { + if ((((stbsp__uintptr)s) & 3) == 0) + break; + *s++ = '0'; + --n; + } + while (n >= 4) { + *(stbsp__uint32 *)s = 0x30303030; + s += 4; + n -= 4; + } + } + while (n) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } else { + *s++ = '0'; + --n; + } + } + } + cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens + if (pr) { + *s++ = stbsp__period; + tz = pr; + } + } else { + // handle xxxxx.xxxx000*000 + n = 0; + for (;;) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } else { + *s++ = sn[n]; + ++n; + if (n >= (stbsp__uint32)dp) + break; + } + } + cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens + if (pr) + *s++ = stbsp__period; + if ((l - dp) > (stbsp__uint32)pr) + l = pr + dp; + while (n < l) { + *s++ = sn[n]; + ++n; + } + tz = pr - (l - dp); + } + } + pr = 0; + + // handle k,m,g,t + if (fl & STBSP__METRIC_SUFFIX) { + char idx; + idx = 1; + if (fl & STBSP__METRIC_NOSPACE) + idx = 0; + tail[0] = idx; + tail[1] = ' '; + { + if (fl >> 24) { // SI kilo is 'k', JEDEC and SI kibits are 'K'. + if (fl & STBSP__METRIC_1024) + tail[idx + 1] = "_KMGT"[fl >> 24]; + else + tail[idx + 1] = "_kMGT"[fl >> 24]; + idx++; + // If printing kibits and not in jedec, add the 'i'. + if (fl & STBSP__METRIC_1024 && !(fl & STBSP__METRIC_JEDEC)) { + tail[idx + 1] = 'i'; + idx++; + } + tail[0] = idx; + } + } + }; + + flt_lead: + // get the length that we copied + l = (stbsp__uint32)(s - (num + 64)); + s = num + 64; + goto scopy; +#endif + + case 'B': // upper binary + case 'b': // lower binary + h = (f[0] == 'B') ? hexu : hex; + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 2; + lead[1] = '0'; + lead[2] = h[0xb]; + } + l = (8 << 4) | (1 << 8); + goto radixnum; + + case 'o': // octal + h = hexu; + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 1; + lead[1] = '0'; + } + l = (3 << 4) | (3 << 8); + goto radixnum; + + case 'p': // pointer + fl |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0; + pr = sizeof(void *) * 2; + fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros + // fall through - to X + + case 'X': // upper hex + case 'x': // lower hex + h = (f[0] == 'X') ? hexu : hex; + l = (4 << 4) | (4 << 8); + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 2; + lead[1] = '0'; + lead[2] = h[16]; + } + radixnum: + // get the number + if (fl & STBSP__INTMAX) + n64 = va_arg(va, stbsp__uint64); + else + n64 = va_arg(va, stbsp__uint32); + + s = num + STBSP__NUMSZ; + dp = 0; + // clear tail, and clear leading if value is zero + tail[0] = 0; + if (n64 == 0) { + lead[0] = 0; + if (pr == 0) { + l = 0; + cs = 0; + goto scopy; + } + } + // convert to string + for (;;) { + *--s = h[n64 & ((1 << (l >> 8)) - 1)]; + n64 >>= (l >> 8); + if (!((n64) || ((stbsp__int32)((num + STBSP__NUMSZ) - s) < pr))) + break; + if (fl & STBSP__TRIPLET_COMMA) { + ++l; + if ((l & 15) == ((l >> 4) & 15)) { + l &= ~15; + *--s = stbsp__comma; + } + } + }; + // get the tens and the comma pos + cs = (stbsp__uint32)((num + STBSP__NUMSZ) - s) + ((((l >> 4) & 15)) << 24); + // get the length that we copied + l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); + // copy it + goto scopy; + + case 'u': // unsigned + case 'i': + case 'd': // integer + // get the integer and abs it + if (fl & STBSP__INTMAX) { + stbsp__int64 i64 = va_arg(va, stbsp__int64); + n64 = (stbsp__uint64)i64; + if ((f[0] != 'u') && (i64 < 0)) { + n64 = (stbsp__uint64)-i64; + fl |= STBSP__NEGATIVE; + } + } else { + stbsp__int32 i = va_arg(va, stbsp__int32); + n64 = (stbsp__uint32)i; + if ((f[0] != 'u') && (i < 0)) { + n64 = (stbsp__uint32)-i; + fl |= STBSP__NEGATIVE; + } + } + +#ifndef STB_SPRINTF_NOFLOAT + if (fl & STBSP__METRIC_SUFFIX) { + if (n64 < 1024) + pr = 0; + else if (pr == -1) + pr = 1; + fv = (double)(stbsp__int64)n64; + goto doafloat; + } +#endif + + // convert to string + s = num + STBSP__NUMSZ; + l = 0; + + for (;;) { + // do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators) + char *o = s - 8; + if (n64 >= 100000000) { + n = (stbsp__uint32)(n64 % 100000000); + n64 /= 100000000; + } else { + n = (stbsp__uint32)n64; + n64 = 0; + } + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + do { + s -= 2; + *(stbsp__uint16 *)s = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; + n /= 100; + } while (n); + } + while (n) { + if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { + l = 0; + *--s = stbsp__comma; + --o; + } else { + *--s = (char)(n % 10) + '0'; + n /= 10; + } + } + if (n64 == 0) { + if ((s[0] == '0') && (s != (num + STBSP__NUMSZ))) + ++s; + break; + } + while (s != o) + if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { + l = 0; + *--s = stbsp__comma; + --o; + } else { + *--s = '0'; + } + } + + tail[0] = 0; + stbsp__lead_sign(fl, lead); + + // get the length that we copied + l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); + if (l == 0) { + *--s = '0'; + l = 1; + } + cs = l + (3 << 24); + if (pr < 0) + pr = 0; + + scopy: + // get fw=leading/trailing space, pr=leading zeros + if (pr < (stbsp__int32)l) + pr = l; + n = pr + lead[0] + tail[0] + tz; + if (fw < (stbsp__int32)n) + fw = n; + fw -= n; + pr -= l; + + // handle right justify and leading zeros + if ((fl & STBSP__LEFTJUST) == 0) { + if (fl & STBSP__LEADINGZERO) // if leading zeros, everything is in pr + { + pr = (fw > pr) ? fw : pr; + fw = 0; + } else { + fl &= ~STBSP__TRIPLET_COMMA; // if no leading zeros, then no commas + } + } + + // copy the spaces and/or zeros + if (fw + pr) { + stbsp__int32 i; + stbsp__uint32 c; + + // copy leading spaces (or when doing %8.4d stuff) + if ((fl & STBSP__LEFTJUST) == 0) + while (fw > 0) { + stbsp__cb_buf_clamp(i, fw); + fw -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = ' '; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x20202020; + bf += 4; + i -= 4; + } + while (i) { + *bf++ = ' '; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy leader + sn = lead + 1; + while (lead[0]) { + stbsp__cb_buf_clamp(i, lead[0]); + lead[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy leading zeros + c = cs >> 24; + cs &= 0xffffff; + cs = (fl & STBSP__TRIPLET_COMMA) ? ((stbsp__uint32)(c - ((pr + cs) % (c + 1)))) : 0; + while (pr > 0) { + stbsp__cb_buf_clamp(i, pr); + pr -= i; + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x30303030; + bf += 4; + i -= 4; + } + } + while (i) { + if ((fl & STBSP__TRIPLET_COMMA) && (cs++ == c)) { + cs = 0; + *bf++ = stbsp__comma; + } else + *bf++ = '0'; + --i; + } + stbsp__chk_cb_buf(1); + } + } + + // copy leader if there is still one + sn = lead + 1; + while (lead[0]) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, lead[0]); + lead[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy the string + n = l; + while (n) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, n); + n -= i; + STBSP__UNALIGNED(while (i >= 4) { + *(stbsp__uint32 volatile *)bf = *(stbsp__uint32 volatile *)s; + bf += 4; + s += 4; + i -= 4; + }) + while (i) { + *bf++ = *s++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy trailing zeros + while (tz) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, tz); + tz -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x30303030; + bf += 4; + i -= 4; + } + while (i) { + *bf++ = '0'; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy tail if there is one + sn = tail + 1; + while (tail[0]) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, tail[0]); + tail[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // handle the left justify + if (fl & STBSP__LEFTJUST) + if (fw > 0) { + while (fw) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, fw); + fw -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = ' '; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x20202020; + bf += 4; + i -= 4; + } + while (i--) + *bf++ = ' '; + stbsp__chk_cb_buf(1); + } + } + break; + + default: // unknown, just copy code + s = num + STBSP__NUMSZ - 1; + *s = f[0]; + l = 1; + fw = fl = 0; + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + goto scopy; + } + ++f; + } +endfmt: + + if (!callback) + *bf = 0; + else + stbsp__flush_cb(); + +done: + return tlen + (int)(bf - buf); +} + +// cleanup +#undef STBSP__LEFTJUST +#undef STBSP__LEADINGPLUS +#undef STBSP__LEADINGSPACE +#undef STBSP__LEADING_0X +#undef STBSP__LEADINGZERO +#undef STBSP__INTMAX +#undef STBSP__TRIPLET_COMMA +#undef STBSP__NEGATIVE +#undef STBSP__METRIC_SUFFIX +#undef STBSP__NUMSZ +#undef stbsp__chk_cb_bufL +#undef stbsp__chk_cb_buf +#undef stbsp__flush_cb +#undef stbsp__cb_buf_clamp + +// ============================================================================ +// wrapper functions + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) +{ + int result; + va_list va; + va_start(va, fmt); + result = STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); + va_end(va); + return result; +} + +typedef struct stbsp__context { + char *buf; + int count; + int length; + char tmp[STB_SPRINTF_MIN]; +} stbsp__context; + +static char *stbsp__clamp_callback(const char *buf, void *user, int len) +{ + stbsp__context *c = (stbsp__context *)user; + c->length += len; + + if (len > c->count) + len = c->count; + + if (len) { + if (buf != c->buf) { + const char *s, *se; + char *d; + d = c->buf; + s = buf; + se = buf + len; + do { + *d++ = *s++; + } while (s < se); + } + c->buf += len; + c->count -= len; + } + + if (c->count <= 0) + return c->tmp; + return (c->count >= STB_SPRINTF_MIN) ? c->buf : c->tmp; // go direct into buffer if you can +} + +static char * stbsp__count_clamp_callback( const char * buf, void * user, int len ) +{ + stbsp__context * c = (stbsp__context*)user; + (void) sizeof(buf); + + c->length += len; + return c->tmp; // go direct into buffer if you can +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va ) +{ + stbsp__context c; + + if ( (count == 0) && !buf ) + { + c.length = 0; + + STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__count_clamp_callback, &c, c.tmp, fmt, va ); + } + else + { + int l; + + c.buf = buf; + c.count = count; + c.length = 0; + + STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__clamp_callback, &c, stbsp__clamp_callback(0,&c,0), fmt, va ); + + // zero-terminate + l = (int)( c.buf - buf ); + if ( l >= count ) // should never be greater, only equal (or less) than count + l = count - 1; + buf[l] = 0; + } + + return c.length; +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) +{ + int result; + va_list va; + va_start(va, fmt); + + result = STB_SPRINTF_DECORATE(vsnprintf)(buf, count, fmt, va); + va_end(va); + + return result; +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va) +{ + return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); +} + +// ======================================================================= +// low level float utility functions + +#ifndef STB_SPRINTF_NOFLOAT + +// copies d to bits w/ strict aliasing (this compiles to nothing on /Ox) +#define STBSP__COPYFP(dest, src) \ + { \ + int cn; \ + for (cn = 0; cn < 8; cn++) \ + ((char *)&dest)[cn] = ((char *)&src)[cn]; \ + } + +// get float info +static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value) +{ + double d; + stbsp__int64 b = 0; + + // load value and round at the frac_digits + d = value; + + STBSP__COPYFP(b, d); + + *bits = b & ((((stbsp__uint64)1) << 52) - 1); + *expo = (stbsp__int32)(((b >> 52) & 2047) - 1023); + + return (stbsp__int32)((stbsp__uint64) b >> 63); +} + +static double const stbsp__bot[23] = { + 1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, 1e+008, 1e+009, 1e+010, 1e+011, + 1e+012, 1e+013, 1e+014, 1e+015, 1e+016, 1e+017, 1e+018, 1e+019, 1e+020, 1e+021, 1e+022 +}; +static double const stbsp__negbot[22] = { + 1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, 1e-009, 1e-010, 1e-011, + 1e-012, 1e-013, 1e-014, 1e-015, 1e-016, 1e-017, 1e-018, 1e-019, 1e-020, 1e-021, 1e-022 +}; +static double const stbsp__negboterr[22] = { + -5.551115123125783e-018, -2.0816681711721684e-019, -2.0816681711721686e-020, -4.7921736023859299e-021, -8.1803053914031305e-022, 4.5251888174113741e-023, + 4.5251888174113739e-024, -2.0922560830128471e-025, -6.2281591457779853e-026, -3.6432197315497743e-027, 6.0503030718060191e-028, 2.0113352370744385e-029, + -3.0373745563400371e-030, 1.1806906454401013e-032, -7.7705399876661076e-032, 2.0902213275965398e-033, -7.1542424054621921e-034, -7.1542424054621926e-035, + 2.4754073164739869e-036, 5.4846728545790429e-037, 9.2462547772103625e-038, -4.8596774326570872e-039 +}; +static double const stbsp__top[13] = { + 1e+023, 1e+046, 1e+069, 1e+092, 1e+115, 1e+138, 1e+161, 1e+184, 1e+207, 1e+230, 1e+253, 1e+276, 1e+299 +}; +static double const stbsp__negtop[13] = { + 1e-023, 1e-046, 1e-069, 1e-092, 1e-115, 1e-138, 1e-161, 1e-184, 1e-207, 1e-230, 1e-253, 1e-276, 1e-299 +}; +static double const stbsp__toperr[13] = { + 8388608, + 6.8601809640529717e+028, + -7.253143638152921e+052, + -4.3377296974619174e+075, + -1.5559416129466825e+098, + -3.2841562489204913e+121, + -3.7745893248228135e+144, + -1.7356668416969134e+167, + -3.8893577551088374e+190, + -9.9566444326005119e+213, + 6.3641293062232429e+236, + -5.2069140800249813e+259, + -5.2504760255204387e+282 +}; +static double const stbsp__negtoperr[13] = { + 3.9565301985100693e-040, -2.299904345391321e-063, 3.6506201437945798e-086, 1.1875228833981544e-109, + -5.0644902316928607e-132, -6.7156837247865426e-155, -2.812077463003139e-178, -5.7778912386589953e-201, + 7.4997100559334532e-224, -4.6439668915134491e-247, -6.3691100762962136e-270, -9.436808465446358e-293, + 8.0970921678014997e-317 +}; + +#if defined(_MSC_VER) && (_MSC_VER <= 1200) +static stbsp__uint64 const stbsp__powten[20] = { + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000, + 100000000000, + 1000000000000, + 10000000000000, + 100000000000000, + 1000000000000000, + 10000000000000000, + 100000000000000000, + 1000000000000000000, + 10000000000000000000U +}; +#define stbsp__tento19th ((stbsp__uint64)1000000000000000000) +#else +static stbsp__uint64 const stbsp__powten[20] = { + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000ULL, + 100000000000ULL, + 1000000000000ULL, + 10000000000000ULL, + 100000000000000ULL, + 1000000000000000ULL, + 10000000000000000ULL, + 100000000000000000ULL, + 1000000000000000000ULL, + 10000000000000000000ULL +}; +#define stbsp__tento19th (1000000000000000000ULL) +#endif + +#define stbsp__ddmulthi(oh, ol, xh, yh) \ + { \ + double ahi = 0, alo, bhi = 0, blo; \ + stbsp__int64 bt; \ + oh = xh * yh; \ + STBSP__COPYFP(bt, xh); \ + bt &= ((~(stbsp__uint64)0) << 27); \ + STBSP__COPYFP(ahi, bt); \ + alo = xh - ahi; \ + STBSP__COPYFP(bt, yh); \ + bt &= ((~(stbsp__uint64)0) << 27); \ + STBSP__COPYFP(bhi, bt); \ + blo = yh - bhi; \ + ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; \ + } + +#define stbsp__ddtoS64(ob, xh, xl) \ + { \ + double ahi = 0, alo, vh, t; \ + ob = (stbsp__int64)xh; \ + vh = (double)ob; \ + ahi = (xh - vh); \ + t = (ahi - xh); \ + alo = (xh - (ahi - t)) - (vh + t); \ + ob += (stbsp__int64)(ahi + alo + xl); \ + } + +#define stbsp__ddrenorm(oh, ol) \ + { \ + double s; \ + s = oh + ol; \ + ol = ol - (s - oh); \ + oh = s; \ + } + +#define stbsp__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh); + +#define stbsp__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl); + +static void stbsp__raise_to_power10(double *ohi, double *olo, double d, stbsp__int32 power) // power can be -323 to +350 +{ + double ph, pl; + if ((power >= 0) && (power <= 22)) { + stbsp__ddmulthi(ph, pl, d, stbsp__bot[power]); + } else { + stbsp__int32 e, et, eb; + double p2h, p2l; + + e = power; + if (power < 0) + e = -e; + et = (e * 0x2c9) >> 14; /* %23 */ + if (et > 13) + et = 13; + eb = e - (et * 23); + + ph = d; + pl = 0.0; + if (power < 0) { + if (eb) { + --eb; + stbsp__ddmulthi(ph, pl, d, stbsp__negbot[eb]); + stbsp__ddmultlos(ph, pl, d, stbsp__negboterr[eb]); + } + if (et) { + stbsp__ddrenorm(ph, pl); + --et; + stbsp__ddmulthi(p2h, p2l, ph, stbsp__negtop[et]); + stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__negtop[et], stbsp__negtoperr[et]); + ph = p2h; + pl = p2l; + } + } else { + if (eb) { + e = eb; + if (eb > 22) + eb = 22; + e -= eb; + stbsp__ddmulthi(ph, pl, d, stbsp__bot[eb]); + if (e) { + stbsp__ddrenorm(ph, pl); + stbsp__ddmulthi(p2h, p2l, ph, stbsp__bot[e]); + stbsp__ddmultlos(p2h, p2l, stbsp__bot[e], pl); + ph = p2h; + pl = p2l; + } + } + if (et) { + stbsp__ddrenorm(ph, pl); + --et; + stbsp__ddmulthi(p2h, p2l, ph, stbsp__top[et]); + stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__top[et], stbsp__toperr[et]); + ph = p2h; + pl = p2l; + } + } + } + stbsp__ddrenorm(ph, pl); + *ohi = ph; + *olo = pl; +} + +// given a float value, returns the significant bits in bits, and the position of the +// decimal point in decimal_pos. +/-INF and NAN are specified by special values +// returned in the decimal_pos parameter. +// frac_digits is absolute normally, but if you want from first significant digits (got %g and %e), or in 0x80000000 +static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits) +{ + double d; + stbsp__int64 bits = 0; + stbsp__int32 expo, e, ng, tens; + + d = value; + STBSP__COPYFP(bits, d); + expo = (stbsp__int32)((bits >> 52) & 2047); + ng = (stbsp__int32)((stbsp__uint64) bits >> 63); + if (ng) + d = -d; + + if (expo == 2047) // is nan or inf? + { + *start = (bits & ((((stbsp__uint64)1) << 52) - 1)) ? "NaN" : "Inf"; + *decimal_pos = STBSP__SPECIAL; + *len = 3; + return ng; + } + + if (expo == 0) // is zero or denormal + { + if (((stbsp__uint64) bits << 1) == 0) // do zero + { + *decimal_pos = 1; + *start = out; + out[0] = '0'; + *len = 1; + return ng; + } + // find the right expo for denormals + { + stbsp__int64 v = ((stbsp__uint64)1) << 51; + while ((bits & v) == 0) { + --expo; + v >>= 1; + } + } + } + + // find the decimal exponent as well as the decimal bits of the value + { + double ph, pl; + + // log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046 + tens = expo - 1023; + tens = (tens < 0) ? ((tens * 617) / 2048) : (((tens * 1233) / 4096) + 1); + + // move the significant bits into position and stick them into an int + stbsp__raise_to_power10(&ph, &pl, d, 18 - tens); + + // get full as much precision from double-double as possible + stbsp__ddtoS64(bits, ph, pl); + + // check if we undershot + if (((stbsp__uint64)bits) >= stbsp__tento19th) + ++tens; + } + + // now do the rounding in integer land + frac_digits = (frac_digits & 0x80000000) ? ((frac_digits & 0x7ffffff) + 1) : (tens + frac_digits); + if ((frac_digits < 24)) { + stbsp__uint32 dg = 1; + if ((stbsp__uint64)bits >= stbsp__powten[9]) + dg = 10; + while ((stbsp__uint64)bits >= stbsp__powten[dg]) { + ++dg; + if (dg == 20) + goto noround; + } + if (frac_digits < dg) { + stbsp__uint64 r; + // add 0.5 at the right position and round + e = dg - frac_digits; + if ((stbsp__uint32)e >= 24) + goto noround; + r = stbsp__powten[e]; + bits = bits + (r / 2); + if ((stbsp__uint64)bits >= stbsp__powten[dg]) + ++tens; + bits /= r; + } + noround:; + } + + // kill long trailing runs of zeros + if (bits) { + stbsp__uint32 n; + for (;;) { + if (bits <= 0xffffffff) + break; + if (bits % 1000) + goto donez; + bits /= 1000; + } + n = (stbsp__uint32)bits; + while ((n % 1000) == 0) + n /= 1000; + bits = n; + donez:; + } + + // convert to string + out += 64; + e = 0; + for (;;) { + stbsp__uint32 n; + char *o = out - 8; + // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned) + if (bits >= 100000000) { + n = (stbsp__uint32)(bits % 100000000); + bits /= 100000000; + } else { + n = (stbsp__uint32)bits; + bits = 0; + } + while (n) { + out -= 2; + *(stbsp__uint16 *)out = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; + n /= 100; + e += 2; + } + if (bits == 0) { + if ((e) && (out[0] == '0')) { + ++out; + --e; + } + break; + } + while (out != o) { + *--out = '0'; + ++e; + } + } + + *decimal_pos = tens; + *start = out; + *len = e; + return ng; +} + +#undef stbsp__ddmulthi +#undef stbsp__ddrenorm +#undef stbsp__ddmultlo +#undef stbsp__ddmultlos +#undef STBSP__SPECIAL +#undef STBSP__COPYFP + +#endif // STB_SPRINTF_NOFLOAT + +// clean up +#undef stbsp__uint16 +#undef stbsp__uint32 +#undef stbsp__int32 +#undef stbsp__uint64 +#undef stbsp__int64 +#undef STBSP__UNALIGNED + +#endif // STB_SPRINTF_IMPLEMENTATION + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/vendor/stb/src/stb_truetype_wasm.c b/vendor/stb/src/stb_truetype_wasm.c deleted file mode 100644 index e0b1fdc77..000000000 --- a/vendor/stb/src/stb_truetype_wasm.c +++ /dev/null @@ -1,46 +0,0 @@ -#include - -void *stbtt_malloc(size_t size); -void stbtt_free(void *ptr); - -void stbtt_qsort(void* base, size_t num, size_t size, int (*compare)(const void*, const void*)); - -double stbtt_floor(double x); -double stbtt_ceil(double x); -double stbtt_sqrt(double x); -double stbtt_pow(double x, double y); -double stbtt_fmod(double x, double y); -double stbtt_cos(double x); -double stbtt_acos(double x); -double stbtt_fabs(double x); - -unsigned long stbtt_strlen(const char *str); - -void *memcpy(void *dst, const void *src, size_t count); -void *memset(void *dst, int x, size_t count); - -#define STBRP_SORT stbtt_qsort -#define STBRP_ASSERT(condition) ((void)0) - -#define STBTT_malloc(x,u) ((void)(u),stbtt_malloc(x)) -#define STBTT_free(x,u) ((void)(u),stbtt_free(x)) - -#define STBTT_assert(condition) ((void)0) - -#define STBTT_ifloor(x) ((int) stbtt_floor(x)) -#define STBTT_iceil(x) ((int) stbtt_ceil(x)) -#define STBTT_sqrt(x) stbtt_sqrt(x) -#define STBTT_pow(x,y) stbtt_pow(x,y) -#define STBTT_fmod(x,y) stbtt_fmod(x,y) -#define STBTT_cos(x) stbtt_cos(x) -#define STBTT_acos(x) stbtt_acos(x) -#define STBTT_fabs(x) stbtt_fabs(x) -#define STBTT_strlen(x) stbtt_strlen(x) -#define STBTT_memcpy memcpy -#define STBTT_memset memset - -#define STB_RECT_PACK_IMPLEMENTATION -#include "stb_rect_pack.h" - -#define STB_TRUETYPE_IMPLEMENTATION -#include "stb_truetype.h" diff --git a/vendor/stb/truetype/stb_truetype.odin b/vendor/stb/truetype/stb_truetype.odin index e6defff5f..f1dcdf2a2 100644 --- a/vendor/stb/truetype/stb_truetype.odin +++ b/vendor/stb/truetype/stb_truetype.odin @@ -8,6 +8,7 @@ LIB :: ( "../lib/stb_truetype.lib" when ODIN_OS == .Windows else "../lib/stb_truetype.a" when ODIN_OS == .Linux else "../lib/darwin/stb_truetype.a" when ODIN_OS == .Darwin + else "../lib/stb_truetype_wasm.o" when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 else "" ) @@ -15,10 +16,12 @@ when LIB != "" { when !#exists(LIB) { #panic("Could not find the compiled STB libraries, they can be compiled by running `make -C \"" + ODIN_ROOT + "vendor/stb/src\"`") } +} - foreign import stbtt { LIB } -} else when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 { +when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 { foreign import stbtt "../lib/stb_truetype_wasm.o" +} else when LIB != "" { + foreign import stbtt { LIB } } else { foreign import stbtt "system:stb_truetype" } diff --git a/vendor/stb/truetype/stb_truetype_wasm.odin b/vendor/stb/truetype/stb_truetype_wasm.odin index 472419ccb..d15f29f18 100644 --- a/vendor/stb/truetype/stb_truetype_wasm.odin +++ b/vendor/stb/truetype/stb_truetype_wasm.odin @@ -1,82 +1,4 @@ //+build wasm32, wasm64p32 package stb_truetype -import "base:builtin" -import "base:intrinsics" -import "base:runtime" - -import "core:c" -import "core:math" -import "core:slice" -import "core:sort" - -@(require, linkage="strong", link_name="stbtt_malloc") -malloc :: proc "c" (size: uint) -> rawptr { - context = runtime.default_context() - ptr, _ := runtime.mem_alloc_non_zeroed(int(size)) - return raw_data(ptr) -} - -@(require, linkage="strong", link_name="stbtt_free") -free :: proc "c" (ptr: rawptr) { - context = runtime.default_context() - builtin.free(ptr) -} - -@(require, linkage="strong", link_name="stbtt_qsort") -qsort :: proc "c" (base: rawptr, num: uint, size: uint, cmp: proc "c" (a, b: rawptr) -> i32) { - context = runtime.default_context() - - Inputs :: struct { - base: rawptr, - num: uint, - size: uint, - cmp: proc "c" (a, b: rawptr) -> i32, - } - - sort.sort({ - collection = &Inputs{base, num, size, cmp}, - len = proc(it: sort.Interface) -> int { - inputs := (^Inputs)(it.collection) - return int(inputs.num) - }, - less = proc(it: sort.Interface, i, j: int) -> bool { - inputs := (^Inputs)(it.collection) - a := rawptr(uintptr(inputs.base) + (uintptr(i) * uintptr(inputs.size))) - b := rawptr(uintptr(inputs.base) + (uintptr(j) * uintptr(inputs.size))) - return inputs.cmp(a, b) < 0 - }, - swap = proc(it: sort.Interface, i, j: int) { - inputs := (^Inputs)(it.collection) - - a := rawptr(uintptr(inputs.base) + (uintptr(i) * uintptr(inputs.size))) - b := rawptr(uintptr(inputs.base) + (uintptr(j) * uintptr(inputs.size))) - - slice.ptr_swap_non_overlapping(a, b, int(inputs.size)) - }, - }) -} - -@(require, linkage="strong", link_name="stbtt_floor") -floor :: proc "c" (x: f64) -> f64 { return math.floor(x) } -@(require, linkage="strong", link_name="stbtt_ceil") -ceil :: proc "c" (x: f64) -> f64 { return math.ceil(x) } -@(require, linkage="strong", link_name="stbtt_sqrt") -sqrt :: proc "c" (x: f64) -> f64 { return math.sqrt(x) } -@(require, linkage="strong", link_name="stbtt_pow") -pow :: proc "c" (x, y: f64) -> f64 { return math.pow(x, y) } -@(require, linkage="strong", link_name="stbtt_fmod") -fmod :: proc "c" (x, y: f64) -> f64 { return math.mod(x, y) } -@(require, linkage="strong", link_name="stbtt_cos") -cos :: proc "c" (x: f64) -> f64 { return math.cos(x) } -@(require, linkage="strong", link_name="stbtt_acos") -acos :: proc "c" (x: f64) -> f64 { return math.acos(x) } -@(require, linkage="strong", link_name="stbtt_fabs") -fabs :: proc "c" (x: f64) -> f64 { return math.abs(x) } - -@(require, linkage="strong", link_name="stbtt_strlen") -strlen :: proc "c" (str: cstring) -> c.ulong { return c.ulong(len(str)) } - -// NOTE: defined in runtime. -// void *memcpy(void *dst, const void *src, size_t count); -// void *memset(void *dst, int x, size_t count); +@(require) import _ "vendor:libc" From 9d6f71fd2ed0a290781e547c7573e86010ff660f Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sun, 8 Sep 2024 15:27:28 -0400 Subject: [PATCH 31/72] Fix `sync.Benaphore` The calls to `atomic_add*` return the value before adding, not after, so the previous code was causing the occasional data race. --- core/sync/extended.odin | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/sync/extended.odin b/core/sync/extended.odin index b446fefa0..ffba40ef8 100644 --- a/core/sync/extended.odin +++ b/core/sync/extended.odin @@ -355,7 +355,7 @@ from entering any critical sections associated with the same benaphore, until until the lock is released. */ benaphore_lock :: proc "contextless" (b: ^Benaphore) { - if atomic_add_explicit(&b.counter, 1, .Acquire) > 1 { + if atomic_add_explicit(&b.counter, 1, .Acquire) > 0 { sema_wait(&b.sema) } } @@ -384,7 +384,7 @@ are waiting on the lock, exactly one thread is allowed into a critical section associated with the same banaphore. */ benaphore_unlock :: proc "contextless" (b: ^Benaphore) { - if atomic_sub_explicit(&b.counter, 1, .Release) > 0 { + if atomic_sub_explicit(&b.counter, 1, .Release) > 1 { sema_post(&b.sema) } } @@ -740,4 +740,4 @@ Make event available. one_shot_event_signal :: proc "contextless" (e: ^One_Shot_Event) { atomic_store_explicit(&e.state, 1, .Release) futex_broadcast(&e.state) -} \ No newline at end of file +} From 74b28f1ff91d4776475f4009fa2bcda71c655cd5 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sun, 8 Sep 2024 17:25:48 -0400 Subject: [PATCH 32/72] Fix rare double-join possibility in POSIX `thread._join` This was occuring about 1/100 times with the test runner's thread pool. --- core/thread/thread_unix.odin | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/core/thread/thread_unix.odin b/core/thread/thread_unix.odin index ddc47244c..d165560ac 100644 --- a/core/thread/thread_unix.odin +++ b/core/thread/thread_unix.odin @@ -9,8 +9,6 @@ import "core:time" _IS_SUPPORTED :: true -CAS :: sync.atomic_compare_exchange_strong - // NOTE(tetra): Aligned here because of core/unix/pthread_linux.odin/pthread_t. // Also see core/sys/darwin/mach_darwin.odin/semaphore_t. Thread_Os_Specific :: struct #align(16) { @@ -140,24 +138,18 @@ _is_done :: proc(t: ^Thread) -> bool { } _join :: proc(t: ^Thread) { - // sync.guard(&t.mutex) - if unix.pthread_equal(unix.pthread_self(), t.unix_thread) { return } - // Preserve other flags besides `.Joined`, like `.Started`. - unjoined := sync.atomic_load(&t.flags) - {.Joined} - joined := unjoined + {.Joined} - - // Try to set `t.flags` from unjoined to joined. If it returns joined, - // it means the previous value had that flag set and we can return. - if res, ok := CAS(&t.flags, unjoined, joined); res == joined && !ok { + // If the previous value was already `Joined`, then we can return. + if .Joined in sync.atomic_or(&t.flags, {.Joined}) { return } + // Prevent non-started threads from blocking main thread with initial wait // condition. - if .Started not_in unjoined { + if .Started not_in sync.atomic_load(&t.flags) { _start(t) } unix.pthread_join(t.unix_thread, nil) From cbd4d5e765646ef07c4133ab65e06652a87a1916 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sun, 8 Sep 2024 17:54:45 -0400 Subject: [PATCH 33/72] Fix data race in `atomic_sema_wait_with_timeout` --- core/sync/primitives_atomic.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/sync/primitives_atomic.odin b/core/sync/primitives_atomic.odin index 1d8e423db..076a74b20 100644 --- a/core/sync/primitives_atomic.odin +++ b/core/sync/primitives_atomic.odin @@ -361,7 +361,7 @@ atomic_sema_wait_with_timeout :: proc "contextless" (s: ^Atomic_Sema, duration: if !futex_wait_with_timeout(&s.count, u32(original_count), remaining) { return false } - original_count = s.count + original_count = atomic_load_explicit(&s.count, .Relaxed) } if original_count == atomic_compare_exchange_strong_explicit(&s.count, original_count, original_count-1, .Acquire, .Acquire) { return true From 4d14b4257e7570216826c5cbcee94aa51116e3b3 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sun, 8 Sep 2024 18:05:34 -0400 Subject: [PATCH 34/72] Convert POSIX `Thread` to use semaphore instead One less value to store, and it should be less of a hack too. Semaphores will not wait around if they have the go-ahead; they depend on an internal value being non-zero, instead of whatever was loaded when they started waiting, which is the case with a `Cond`. --- core/thread/thread_unix.odin | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/core/thread/thread_unix.odin b/core/thread/thread_unix.odin index d165560ac..3d3b419b0 100644 --- a/core/thread/thread_unix.odin +++ b/core/thread/thread_unix.odin @@ -5,7 +5,6 @@ package thread import "base:runtime" import "core:sync" import "core:sys/unix" -import "core:time" _IS_SUPPORTED :: true @@ -13,8 +12,7 @@ _IS_SUPPORTED :: true // Also see core/sys/darwin/mach_darwin.odin/semaphore_t. Thread_Os_Specific :: struct #align(16) { unix_thread: unix.pthread_t, // NOTE: very large on Darwin, small on Linux. - cond: sync.Cond, - mutex: sync.Mutex, + start_ok: sync.Sema, } // // Creates a thread which will run the given procedure. @@ -27,14 +25,10 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread { // We need to give the thread a moment to start up before we enable cancellation. can_set_thread_cancel_state := unix.pthread_setcancelstate(unix.PTHREAD_CANCEL_ENABLE, nil) == 0 - sync.lock(&t.mutex) - t.id = sync.current_thread_id() - for (.Started not_in sync.atomic_load(&t.flags)) { - // HACK: use a timeout so in the event that the condition is signalled at THIS comment's exact point - // (after checking flags, before starting the wait) it gets itself out of that deadlock after a ms. - sync.wait_with_timeout(&t.cond, &t.mutex, time.Millisecond) + if .Started not_in sync.atomic_load(&t.flags) { + sync.wait(&t.start_ok) } if .Joined in sync.atomic_load(&t.flags) { @@ -64,8 +58,6 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread { sync.atomic_or(&t.flags, { .Done }) - sync.unlock(&t.mutex) - if .Self_Cleanup in sync.atomic_load(&t.flags) { res := unix.pthread_detach(t.unix_thread) assert_contextless(res == 0) @@ -130,7 +122,7 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread { _start :: proc(t: ^Thread) { sync.atomic_or(&t.flags, { .Started }) - sync.signal(&t.cond) + sync.post(&t.start_ok) } _is_done :: proc(t: ^Thread) -> bool { From 45da0093774276223e3724a89e5b0a9f8ef7c9f7 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sun, 8 Sep 2024 18:21:55 -0400 Subject: [PATCH 35/72] Use more atomic handling of thread flags This can prevent a data race on Linux with `Self_Cleanup`. --- core/thread/thread.odin | 12 ++++++------ core/thread/thread_windows.odin | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/core/thread/thread.odin b/core/thread/thread.odin index 17ba1a0a2..c1cbceb42 100644 --- a/core/thread/thread.odin +++ b/core/thread/thread.odin @@ -272,7 +272,7 @@ create_and_start :: proc(fn: proc(), init_context: Maybe(runtime.Context) = nil, t := create(thread_proc, priority) t.data = rawptr(fn) if self_cleanup { - t.flags += {.Self_Cleanup} + intrinsics.atomic_or(&t.flags, {.Self_Cleanup}) } t.init_context = init_context start(t) @@ -307,7 +307,7 @@ create_and_start_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_co t.user_index = 1 t.user_args[0] = data if self_cleanup { - t.flags += {.Self_Cleanup} + intrinsics.atomic_or(&t.flags, {.Self_Cleanup}) } t.init_context = init_context start(t) @@ -347,7 +347,7 @@ create_and_start_with_poly_data :: proc(data: $T, fn: proc(data: T), init_contex mem.copy(&t.user_args[0], &data, size_of(T)) if self_cleanup { - t.flags += {.Self_Cleanup} + intrinsics.atomic_or(&t.flags, {.Self_Cleanup}) } t.init_context = init_context @@ -394,7 +394,7 @@ create_and_start_with_poly_data2 :: proc(arg1: $T1, arg2: $T2, fn: proc(T1, T2), _ = copy(user_args[n:], mem.ptr_to_bytes(&arg2)) if self_cleanup { - t.flags += {.Self_Cleanup} + intrinsics.atomic_or(&t.flags, {.Self_Cleanup}) } t.init_context = init_context @@ -443,7 +443,7 @@ create_and_start_with_poly_data3 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, fn: pr _ = copy(user_args[n:], mem.ptr_to_bytes(&arg3)) if self_cleanup { - t.flags += {.Self_Cleanup} + intrinsics.atomic_or(&t.flags, {.Self_Cleanup}) } t.init_context = init_context @@ -494,7 +494,7 @@ create_and_start_with_poly_data4 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, arg4: _ = copy(user_args[n:], mem.ptr_to_bytes(&arg4)) if self_cleanup { - t.flags += {.Self_Cleanup} + intrinsics.atomic_or(&t.flags, {.Self_Cleanup}) } t.init_context = init_context diff --git a/core/thread/thread_windows.odin b/core/thread/thread_windows.odin index 50a4e5fbc..22c3eae65 100644 --- a/core/thread/thread_windows.odin +++ b/core/thread/thread_windows.odin @@ -27,7 +27,7 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread { __windows_thread_entry_proc :: proc "system" (t_: rawptr) -> win32.DWORD { t := (^Thread)(t_) - if .Joined in t.flags { + if .Joined in sync.atomic_load(&t.flags) { return 0 } @@ -48,9 +48,9 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread { t.procedure(t) } - intrinsics.atomic_store(&t.flags, t.flags + {.Done}) + intrinsics.atomic_or(&t.flags, {.Done}) - if .Self_Cleanup in t.flags { + if .Self_Cleanup in sync.atomic_load(&t.flags) { win32.CloseHandle(t.win32_thread) t.win32_thread = win32.INVALID_HANDLE // NOTE(ftphikari): It doesn't matter which context 'free' received, right? From dbb783fbf20df1bba899b7a2bcbd65f71eb32fef Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sun, 8 Sep 2024 18:59:55 -0400 Subject: [PATCH 36/72] Fix atomic memory order for `sync.ticket_mutex_unlock` --- core/sync/extended.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/sync/extended.odin b/core/sync/extended.odin index ffba40ef8..fd2bda08a 100644 --- a/core/sync/extended.odin +++ b/core/sync/extended.odin @@ -297,7 +297,7 @@ waiting to acquire the lock, exactly one of those threads is unblocked and allowed into the critical section. */ ticket_mutex_unlock :: #force_inline proc "contextless" (m: ^Ticket_Mutex) { - atomic_add_explicit(&m.serving, 1, .Relaxed) + atomic_add_explicit(&m.serving, 1, .Release) } /* From c3f363cfbcee453c7d90b37429c92115e91216af Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sun, 8 Sep 2024 21:59:55 -0400 Subject: [PATCH 37/72] Fix data race when `pool_stop_task` is called --- core/thread/thread_pool.odin | 1 + 1 file changed, 1 insertion(+) diff --git a/core/thread/thread_pool.odin b/core/thread/thread_pool.odin index 9bcc42968..d9166b450 100644 --- a/core/thread/thread_pool.odin +++ b/core/thread/thread_pool.odin @@ -60,6 +60,7 @@ pool_thread_runner :: proc(t: ^Thread) { if task, ok := pool_pop_waiting(pool); ok { data.task = task pool_do_work(pool, task) + sync.guard(&pool.mutex) data.task = {} } } From fdd488256896ab40025ebd394735d5a6a30bd8ee Mon Sep 17 00:00:00 2001 From: flysand7 Date: Tue, 10 Sep 2024 19:51:20 +1100 Subject: [PATCH 38/72] [mem]: Adjust docs for alloc --- core/mem/alloc.odin | 166 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 150 insertions(+), 16 deletions(-) diff --git a/core/mem/alloc.odin b/core/mem/alloc.odin index c2e55541c..1ede92837 100644 --- a/core/mem/alloc.odin +++ b/core/mem/alloc.odin @@ -35,7 +35,6 @@ functions: `old_size` to be `size` bytes in length and have the specified `alignment`, in case a re-alllocation occurs. - `Resize_Non_Zeroed`: Same as `Resize`, without explicit zero-initialization. - */ Allocator_Mode :: runtime.Allocator_Mode @@ -123,7 +122,11 @@ Currently the type is defined as follows: ) -> ([]byte, Allocator_Error); The function of this procedure and the meaning of parameters depends on the -value of the `mode` parameter. +value of the `mode` parameter. For any operation the following constraints +apply: + +- The `alignment` must be a power of two. +- The `size` must be a positive integer. ## 1. `.Alloc`, `.Alloc_Non_Zeroed` @@ -142,10 +145,11 @@ Allocates a memory region of size `size`, aligned on a boundary specified by 1. The memory region, if allocated successfully, or `nil` otherwise. 2. An error, if allocation failed. -**Note**: Some allocators may return `nil`, even if no error is returned. +**Note**: The nil allocator may return `nil`, even if no error is returned. Always check both the error and the allocated buffer. -Same as `.Alloc`. +**Note**: The `.Alloc` mode is required to be implemented for an allocator +and can not return a `.Mode_Not_Implemented` error. ## 2. `Free` @@ -200,6 +204,10 @@ If `new_size` is `nil`, the procedure acts just like `.Free`, freeing the memory region `old_size` bytes in length, located at the address specified by `old_memory`. +If the `old_memory` pointer is not aligned to the boundary specified by +`alignment`, the procedure relocates the buffer such that the reallocated +buffer is aligned to the boundary specified by `alignment`. + **Inputs**: - `allocator_data`: Pointer to the allocator data. - `mode`: `.Resize` or `.Resize_All`. @@ -216,6 +224,9 @@ memory region `old_size` bytes in length, located at the address specified by **Note**: Some allocators may return `nil`, even if no error is returned. Always check both the error and the allocated buffer. + +**Note**: if `old_size` is `0` and `old_memory` is `nil`, this operation is a +no-op, and should not return errors. */ Allocator_Proc :: runtime.Allocator_Proc @@ -259,6 +270,8 @@ Allocate memory. This function allocates `size` bytes of memory, aligned to a boundary specified by `alignment` using the allocator specified by `allocator`. +If the `size` parameter is `0`, the operation is a no-op. + **Inputs**: - `size`: The desired size of the allocated memory region. - `alignment`: The desired alignment of the allocated memory region. @@ -267,6 +280,14 @@ by `alignment` using the allocator specified by `allocator`. **Returns**: 1. Pointer to the allocated memory, or `nil` if allocation failed. 2. Error, if the allocation failed. + +**Errors**: +- `None`: If no error occurred. +- `Out_Of_Memory`: Occurs when the allocator runs out of space in any of its + backing buffers, the backing allocator has ran out of space, or an operating + system failure occurred. +- `Invalid_Argument`: If the supplied `size` is negative, alignment is not a + power of two. */ @(require_results) alloc :: proc( @@ -293,6 +314,14 @@ by `alignment` using the allocator specified by `allocator`. **Returns**: 1. Slice of the allocated memory region, or `nil` if allocation failed. 2. Error, if the allocation failed. + +**Errors**: +- `None`: If no error occurred. +- `Out_Of_Memory`: Occurs when the allocator runs out of space in any of its + backing buffers, the backing allocator has ran out of space, or an operating + system failure occurred. +- `Invalid_Argument`: If the supplied `size` is negative, alignment is not a + power of two. */ @(require_results) alloc_bytes :: proc( @@ -319,6 +348,14 @@ does not explicitly zero-initialize allocated memory region. **Returns**: 1. Slice of the allocated memory region, or `nil` if allocation failed. 2. Error, if the allocation failed. + +**Errors**: +- `None`: If no error occurred. +- `Out_Of_Memory`: Occurs when the allocator runs out of space in any of its + backing buffers, the backing allocator has ran out of space, or an operating + system failure occurred. +- `Invalid_Argument`: If the supplied `size` is negative, alignment is not a + power of two. */ @(require_results) alloc_bytes_non_zeroed :: proc( @@ -339,6 +376,16 @@ allocated from the allocator specified by `allocator`. **Inputs**: - `ptr`: Pointer to the memory region to free. - `allocator`: The allocator to free to. + +**Returns**: +- The error, if freeing failed. + +**Errors**: +- `None`: When no error has occurred. +- `Invalid_Pointer`: The specified pointer is not owned by the specified allocator, + or does not point to a valid allocation. +- `Mode_Not_Implemented`: If the specified allocator does not support the `.Free` +mode. */ free :: proc( ptr: rawptr, @@ -354,6 +401,8 @@ Free a memory region. This procedure frees `size` bytes of memory region located at the address, specified by `ptr`, allocated from the allocator specified by `allocator`. +If the `size` parameter is `0`, this call is equivalent to `free()`. + **Inputs**: - `ptr`: Pointer to the memory region to free. - `size`: The size of the memory region to free. @@ -361,6 +410,13 @@ specified by `ptr`, allocated from the allocator specified by `allocator`. **Returns**: - The error, if freeing failed. + +**Errors**: +- `None`: When no error has occurred. +- `Invalid_Pointer`: The specified pointer is not owned by the specified allocator, + or does not point to a valid allocation. +- `Mode_Not_Implemented`: If the specified allocator does not support the `.Free` +mode. */ free_with_size :: proc( ptr: rawptr, @@ -377,12 +433,22 @@ Free a memory region. This procedure frees memory region, specified by `bytes`, allocated from the allocator specified by `allocator`. +If the length of the specified slice is zero, the `.Invalid_Argument` error +is returned. + **Inputs**: - `bytes`: The memory region to free. - `allocator`: The allocator to free to. **Returns**: - The error, if freeing failed. + +**Errors**: +- `None`: When no error has occurred. +- `Invalid_Pointer`: The specified pointer is not owned by the specified allocator, + or does not point to a valid allocation. +- `Mode_Not_Implemented`: If the specified allocator does not support the `.Free` +mode. */ free_bytes :: proc( bytes: []byte, @@ -397,6 +463,14 @@ Free all allocations. This procedure frees all allocations made on the allocator specified by `allocator` to that allocator, making it available for further allocations. + +**Inputs**: +- `allocator`: The allocator to free to. + +**Errors**: +- `None`: When no error has occurred. +- `Mode_Not_Implemented`: If the specified allocator does not support the `.Free` +mode. */ free_all :: proc(allocator := context.allocator, loc := #caller_location) -> Allocator_Error { return runtime.mem_free_all(allocator, loc) @@ -416,6 +490,10 @@ If the `new_size` parameter is `0`, `resize()` acts just like `free()`, freeing the memory region `old_size` bytes in length, located at the address specified by `ptr`. +If the `old_memory` pointer is not aligned to the boundary specified by +`alignment`, the procedure relocates the buffer such that the reallocated +buffer is aligned to the boundary specified by `alignment`. + **Inputs**: - `ptr`: Pointer to the memory region to resize. - `old_size`: Size of the memory region to resize. @@ -427,9 +505,20 @@ by `ptr`. 1. The pointer to the resized memory region, if successfull, `nil` otherwise. 2. Error, if resize failed. -**Note**: The `alignment` parameter is used to preserve the original alignment -of the allocation, if `resize()` needs to relocate the memory region. Do not -use `resize()` to change the alignment of the allocated memory region. +**Errors**: +- `None`: No error. +- `Out_Of_Memory`: When the allocator's backing buffer or it's backing + allocator does not have enough space to fit in an allocation with the new + size, or an operating system failure occurs. +- `Invalid_Pointer`: The pointer referring to a memory region does not belong + to any of the allocators backing buffers or does not point to a valid start + of an allocation made in that allocator. +- `Invalid_Argument`: When `size` is negative, alignment is not a power of two, + or the `old_size` argument is incorrect. +- `Mode_Not_Implemented`: The allocator does not support the `.Realloc` mode. + +**Note**: if `old_size` is `0` and `old_memory` is `nil`, this operation is a +no-op, and should not return errors. */ @(require_results) resize :: proc( @@ -458,6 +547,10 @@ If the `new_size` parameter is `0`, `resize()` acts just like `free()`, freeing the memory region `old_size` bytes in length, located at the address specified by `ptr`. +If the `old_memory` pointer is not aligned to the boundary specified by +`alignment`, the procedure relocates the buffer such that the reallocated +buffer is aligned to the boundary specified by `alignment`. + Unlike `resize()`, this procedure does not explicitly zero-initialize any new memory. @@ -472,9 +565,20 @@ memory. 1. The pointer to the resized memory region, if successfull, `nil` otherwise. 2. Error, if resize failed. -**Note**: The `alignment` parameter is used to preserve the original alignment -of the allocation, if `resize()` needs to relocate the memory region. Do not -use `resize()` to change the alignment of the allocated memory region. +**Errors**: +- `None`: No error. +- `Out_Of_Memory`: When the allocator's backing buffer or it's backing + allocator does not have enough space to fit in an allocation with the new + size, or an operating system failure occurs. +- `Invalid_Pointer`: The pointer referring to a memory region does not belong + to any of the allocators backing buffers or does not point to a valid start + of an allocation made in that allocator. +- `Invalid_Argument`: When `size` is negative, alignment is not a power of two, + or the `old_size` argument is incorrect. +- `Mode_Not_Implemented`: The allocator does not support the `.Realloc` mode. + +**Note**: if `old_size` is `0` and `old_memory` is `nil`, this operation is a +no-op, and should not return errors. */ @(require_results) resize_non_zeroed :: proc( @@ -503,6 +607,10 @@ by `alignment`. If the `new_size` parameter is `0`, `resize_bytes()` acts just like `free_bytes()`, freeing the memory region specified by `old_data`. +If the `old_memory` pointer is not aligned to the boundary specified by +`alignment`, the procedure relocates the buffer such that the reallocated +buffer is aligned to the boundary specified by `alignment`. + **Inputs**: - `old_data`: Pointer to the memory region to resize. - `new_size`: The desired size of the resized memory region. @@ -513,9 +621,20 @@ If the `new_size` parameter is `0`, `resize_bytes()` acts just like 1. The resized memory region, if successfull, `nil` otherwise. 2. Error, if resize failed. -**Note**: The `alignment` parameter is used to preserve the original alignment -of the allocation, if `resize()` needs to relocate the memory region. Do not -use `resize()` to change the alignment of the allocated memory region. +**Errors**: +- `None`: No error. +- `Out_Of_Memory`: When the allocator's backing buffer or it's backing + allocator does not have enough space to fit in an allocation with the new + size, or an operating system failure occurs. +- `Invalid_Pointer`: The pointer referring to a memory region does not belong + to any of the allocators backing buffers or does not point to a valid start + of an allocation made in that allocator. +- `Invalid_Argument`: When `size` is negative, alignment is not a power of two, + or the `old_size` argument is incorrect. +- `Mode_Not_Implemented`: The allocator does not support the `.Realloc` mode. + +**Note**: if `old_size` is `0` and `old_memory` is `nil`, this operation is a +no-op, and should not return errors. */ @(require_results) resize_bytes :: proc( @@ -542,6 +661,10 @@ by `alignment`. If the `new_size` parameter is `0`, `resize_bytes()` acts just like `free_bytes()`, freeing the memory region specified by `old_data`. +If the `old_memory` pointer is not aligned to the boundary specified by +`alignment`, the procedure relocates the buffer such that the reallocated +buffer is aligned to the boundary specified by `alignment`. + Unlike `resize_bytes()`, this procedure does not explicitly zero-initialize any new memory. @@ -555,9 +678,20 @@ any new memory. 1. The resized memory region, if successfull, `nil` otherwise. 2. Error, if resize failed. -**Note**: The `alignment` parameter is used to preserve the original alignment -of the allocation, if `resize()` needs to relocate the memory region. Do not -use `resize()` to change the alignment of the allocated memory region. +**Errors**: +- `None`: No error. +- `Out_Of_Memory`: When the allocator's backing buffer or it's backing + allocator does not have enough space to fit in an allocation with the new + size, or an operating system failure occurs. +- `Invalid_Pointer`: The pointer referring to a memory region does not belong + to any of the allocators backing buffers or does not point to a valid start + of an allocation made in that allocator. +- `Invalid_Argument`: When `size` is negative, alignment is not a power of two, + or the `old_size` argument is incorrect. +- `Mode_Not_Implemented`: The allocator does not support the `.Realloc` mode. + +**Note**: if `old_size` is `0` and `old_memory` is `nil`, this operation is a +no-op, and should not return errors. */ @(require_results) resize_bytes_non_zeroed :: proc( From 0a594147afbfdacece1d221d2dee744e612362c6 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Mon, 9 Sep 2024 00:23:30 -0400 Subject: [PATCH 39/72] Use `contextless` procs in `core:sync` instead --- core/sync/extended.odin | 10 +++++----- core/sync/futex_darwin.odin | 12 ++++++------ core/sync/futex_freebsd.odin | 8 ++++---- core/sync/futex_linux.odin | 8 ++++---- core/sync/futex_netbsd.odin | 8 ++++---- core/sync/futex_openbsd.odin | 8 ++++---- core/sync/futex_wasm.odin | 8 ++++---- core/sync/primitives.odin | 18 +----------------- core/sync/primitives_atomic.odin | 2 +- 9 files changed, 33 insertions(+), 49 deletions(-) diff --git a/core/sync/extended.odin b/core/sync/extended.odin index fd2bda08a..83cc648b4 100644 --- a/core/sync/extended.odin +++ b/core/sync/extended.odin @@ -48,12 +48,12 @@ wait_group_add :: proc "contextless" (wg: ^Wait_Group, delta: int) { atomic_add(&wg.counter, delta) if wg.counter < 0 { - _panic("sync.Wait_Group negative counter") + panic_contextless("sync.Wait_Group negative counter") } if wg.counter == 0 { cond_broadcast(&wg.cond) if wg.counter != 0 { - _panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait") + panic_contextless("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait") } } } @@ -81,7 +81,7 @@ wait_group_wait :: proc "contextless" (wg: ^Wait_Group) { if wg.counter != 0 { cond_wait(&wg.cond, &wg.mutex) if wg.counter != 0 { - _panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait") + panic_contextless("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait") } } } @@ -105,7 +105,7 @@ wait_group_wait_with_timeout :: proc "contextless" (wg: ^Wait_Group, duration: t return false } if wg.counter != 0 { - _panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait") + panic_contextless("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait") } } return true @@ -494,7 +494,7 @@ for other threads for entering. */ recursive_benaphore_unlock :: proc "contextless" (b: ^Recursive_Benaphore) { tid := current_thread_id() - _assert(tid == b.owner, "tid != b.owner") + assert_contextless(tid == b.owner, "tid != b.owner") b.recursion -= 1 recursion := b.recursion if recursion == 0 { diff --git a/core/sync/futex_darwin.odin b/core/sync/futex_darwin.odin index fca9aadfe..daefd6699 100644 --- a/core/sync/futex_darwin.odin +++ b/core/sync/futex_darwin.odin @@ -48,7 +48,7 @@ _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, durati case -ETIMEDOUT: return false case: - _panic("darwin.os_sync_wait_on_address_with_timeout failure") + panic_contextless("darwin.os_sync_wait_on_address_with_timeout failure") } } else { @@ -63,7 +63,7 @@ _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, durati case ETIMEDOUT: return false case: - _panic("futex_wait failure") + panic_contextless("futex_wait failure") } return true @@ -83,7 +83,7 @@ _futex_signal :: proc "contextless" (f: ^Futex) { case -ENOENT: return case: - _panic("darwin.os_sync_wake_by_address_any failure") + panic_contextless("darwin.os_sync_wake_by_address_any failure") } } } else { @@ -99,7 +99,7 @@ _futex_signal :: proc "contextless" (f: ^Futex) { case ENOENT: return case: - _panic("futex_wake_single failure") + panic_contextless("futex_wake_single failure") } } @@ -119,7 +119,7 @@ _futex_broadcast :: proc "contextless" (f: ^Futex) { case -ENOENT: return case: - _panic("darwin.os_sync_wake_by_address_all failure") + panic_contextless("darwin.os_sync_wake_by_address_all failure") } } } else { @@ -135,7 +135,7 @@ _futex_broadcast :: proc "contextless" (f: ^Futex) { case ENOENT: return case: - _panic("futex_wake_all failure") + panic_contextless("futex_wake_all failure") } } diff --git a/core/sync/futex_freebsd.odin b/core/sync/futex_freebsd.odin index ac6e2400a..82021a71a 100644 --- a/core/sync/futex_freebsd.odin +++ b/core/sync/futex_freebsd.odin @@ -21,7 +21,7 @@ _futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool { continue } - _panic("_futex_wait failure") + panic_contextless("_futex_wait failure") } unreachable() @@ -44,14 +44,14 @@ _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, durati return false } - _panic("_futex_wait_with_timeout failure") + panic_contextless("_futex_wait_with_timeout failure") } _futex_signal :: proc "contextless" (f: ^Futex) { errno := freebsd._umtx_op(f, .WAKE, 1, nil, nil) if errno != nil { - _panic("_futex_signal failure") + panic_contextless("_futex_signal failure") } } @@ -59,6 +59,6 @@ _futex_broadcast :: proc "contextless" (f: ^Futex) { errno := freebsd._umtx_op(f, .WAKE, cast(c.ulong)max(i32), nil, nil) if errno != nil { - _panic("_futex_broadcast failure") + panic_contextless("_futex_broadcast failure") } } diff --git a/core/sync/futex_linux.odin b/core/sync/futex_linux.odin index fe57c12ed..4d9101b9f 100644 --- a/core/sync/futex_linux.odin +++ b/core/sync/futex_linux.odin @@ -15,7 +15,7 @@ _futex_wait :: proc "contextless" (futex: ^Futex, expected: u32) -> bool { return true case: // TODO(flysand): More descriptive panic messages based on the vlaue of `errno` - _panic("futex_wait failure") + panic_contextless("futex_wait failure") } } @@ -34,7 +34,7 @@ _futex_wait_with_timeout :: proc "contextless" (futex: ^Futex, expected: u32, du case .NONE, .EINTR, .EAGAIN: return true case: - _panic("futex_wait_with_timeout failure") + panic_contextless("futex_wait_with_timeout failure") } } @@ -44,7 +44,7 @@ _futex_signal :: proc "contextless" (futex: ^Futex) { case .NONE: return case: - _panic("futex_wake_single failure") + panic_contextless("futex_wake_single failure") } } @@ -57,6 +57,6 @@ _futex_broadcast :: proc "contextless" (futex: ^Futex) { case .NONE: return case: - _panic("_futex_wake_all failure") + panic_contextless("_futex_wake_all failure") } } diff --git a/core/sync/futex_netbsd.odin b/core/sync/futex_netbsd.odin index d12409f32..f81a12675 100644 --- a/core/sync/futex_netbsd.odin +++ b/core/sync/futex_netbsd.odin @@ -35,7 +35,7 @@ _futex_wait :: proc "contextless" (futex: ^Futex, expected: u32) -> bool { case EINTR, EAGAIN: return true case: - _panic("futex_wait failure") + panic_contextless("futex_wait failure") } } return true @@ -55,7 +55,7 @@ _futex_wait_with_timeout :: proc "contextless" (futex: ^Futex, expected: u32, du case ETIMEDOUT: return false case: - _panic("futex_wait_with_timeout failure") + panic_contextless("futex_wait_with_timeout failure") } } return true @@ -63,12 +63,12 @@ _futex_wait_with_timeout :: proc "contextless" (futex: ^Futex, expected: u32, du _futex_signal :: proc "contextless" (futex: ^Futex) { if _, ok := intrinsics.syscall_bsd(unix.SYS___futex, uintptr(futex), FUTEX_WAKE_PRIVATE, 1, 0, 0, 0); !ok { - _panic("futex_wake_single failure") + panic_contextless("futex_wake_single failure") } } _futex_broadcast :: proc "contextless" (futex: ^Futex) { if _, ok := intrinsics.syscall_bsd(unix.SYS___futex, uintptr(futex), FUTEX_WAKE_PRIVATE, uintptr(max(i32)), 0, 0, 0); !ok { - _panic("_futex_wake_all failure") + panic_contextless("_futex_wake_all failure") } } diff --git a/core/sync/futex_openbsd.odin b/core/sync/futex_openbsd.odin index 4883a0841..1ffe4a9a5 100644 --- a/core/sync/futex_openbsd.odin +++ b/core/sync/futex_openbsd.odin @@ -36,7 +36,7 @@ _futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool { return false } - _panic("futex_wait failure") + panic_contextless("futex_wait failure") } _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool { @@ -62,14 +62,14 @@ _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, durati return false } - _panic("futex_wait_with_timeout failure") + panic_contextless("futex_wait_with_timeout failure") } _futex_signal :: proc "contextless" (f: ^Futex) { res := _unix_futex(f, FUTEX_WAKE_PRIVATE, 1, nil) if res == -1 { - _panic("futex_wake_single failure") + panic_contextless("futex_wake_single failure") } } @@ -77,6 +77,6 @@ _futex_broadcast :: proc "contextless" (f: ^Futex) { res := _unix_futex(f, FUTEX_WAKE_PRIVATE, u32(max(i32)), nil) if res == -1 { - _panic("_futex_wake_all failure") + panic_contextless("_futex_wake_all failure") } } diff --git a/core/sync/futex_wasm.odin b/core/sync/futex_wasm.odin index de88e8198..27532587c 100644 --- a/core/sync/futex_wasm.odin +++ b/core/sync/futex_wasm.odin @@ -10,7 +10,7 @@ import "core:time" _futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool { when !intrinsics.has_target_feature("atomics") { - _panic("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it") + panic_contextless("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it") } else { s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, -1) return s != 0 @@ -19,7 +19,7 @@ _futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool { _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool { when !intrinsics.has_target_feature("atomics") { - _panic("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it") + panic_contextless("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it") } else { s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, i64(duration)) return s != 0 @@ -28,7 +28,7 @@ _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, durati _futex_signal :: proc "contextless" (f: ^Futex) { when !intrinsics.has_target_feature("atomics") { - _panic("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it") + panic_contextless("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it") } else { loop: for { s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), 1) @@ -41,7 +41,7 @@ _futex_signal :: proc "contextless" (f: ^Futex) { _futex_broadcast :: proc "contextless" (f: ^Futex) { when !intrinsics.has_target_feature("atomics") { - _panic("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it") + panic_contextless("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it") } else { loop: for { s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), ~u32(0)) diff --git a/core/sync/primitives.odin b/core/sync/primitives.odin index a22824481..8187c904b 100644 --- a/core/sync/primitives.odin +++ b/core/sync/primitives.odin @@ -1,6 +1,5 @@ package sync -import "base:runtime" import "core:time" /* @@ -560,7 +559,7 @@ futex_wait :: proc "contextless" (f: ^Futex, expected: u32) { return } ok := _futex_wait(f, expected) - _assert(ok, "futex_wait failure") + assert_contextless(ok, "futex_wait failure") } /* @@ -597,18 +596,3 @@ Wake up multiple threads waiting on a futex. futex_broadcast :: proc "contextless" (f: ^Futex) { _futex_broadcast(f) } - - -@(private) -_assert :: proc "contextless" (cond: bool, msg: string) { - if !cond { - _panic(msg) - } -} - -@(private) -_panic :: proc "contextless" (msg: string) -> ! { - runtime.print_string(msg) - runtime.print_byte('\n') - runtime.trap() -} diff --git a/core/sync/primitives_atomic.odin b/core/sync/primitives_atomic.odin index 076a74b20..3c4324eb7 100644 --- a/core/sync/primitives_atomic.odin +++ b/core/sync/primitives_atomic.odin @@ -240,7 +240,7 @@ atomic_recursive_mutex_lock :: proc "contextless" (m: ^Atomic_Recursive_Mutex) { atomic_recursive_mutex_unlock :: proc "contextless" (m: ^Atomic_Recursive_Mutex) { tid := current_thread_id() - _assert(tid == m.owner, "tid != m.owner") + assert_contextless(tid == m.owner, "tid != m.owner") m.recursion -= 1 recursion := m.recursion if recursion == 0 { From 73f5ab473c4129ae209838d7967286684ac3f462 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Mon, 9 Sep 2024 14:18:01 -0400 Subject: [PATCH 40/72] Keep `chan.can_recv` from deadlocking --- core/sync/chan/chan.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/sync/chan/chan.odin b/core/sync/chan/chan.odin index 53a3bff4b..aca08d82e 100644 --- a/core/sync/chan/chan.odin +++ b/core/sync/chan/chan.odin @@ -423,7 +423,7 @@ raw_queue_pop :: proc "contextless" (q: ^Raw_Queue) -> (data: rawptr) { can_recv :: proc "contextless" (c: ^Raw_Chan) -> bool { sync.guard(&c.mutex) if is_buffered(c) { - return len(c) > 0 + return c.queue.len > 0 } return sync.atomic_load(&c.w_waiting) > 0 } From 026aef69e3a42021c5d9666737c7401dc75dc89a Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Mon, 9 Sep 2024 14:42:50 -0400 Subject: [PATCH 41/72] Fix deadlock on sending to full, buffered, closed `Chan` This will also keep messages from being sent to closed, buffered channels in general. --- core/sync/chan/chan.odin | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/sync/chan/chan.odin b/core/sync/chan/chan.odin index aca08d82e..cb299f23f 100644 --- a/core/sync/chan/chan.odin +++ b/core/sync/chan/chan.odin @@ -164,12 +164,17 @@ send_raw :: proc "contextless" (c: ^Raw_Chan, msg_in: rawptr) -> (ok: bool) { } if c.queue != nil { // buffered sync.guard(&c.mutex) - for c.queue.len == c.queue.cap { + for !sync.atomic_load(&c.closed) && + c.queue.len == c.queue.cap { sync.atomic_add(&c.w_waiting, 1) sync.wait(&c.w_cond, &c.mutex) sync.atomic_sub(&c.w_waiting, 1) } + if sync.atomic_load(&c.closed) { + return false + } + ok = raw_queue_push(c.queue, msg_in) if sync.atomic_load(&c.r_waiting) > 0 { sync.signal(&c.r_cond) From e9a6a344809daf5c0a3b725dd52e1527382d8c41 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Mon, 9 Sep 2024 16:04:18 -0400 Subject: [PATCH 42/72] Forbid `chan.try_send` on closed buffered channels --- core/sync/chan/chan.odin | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/sync/chan/chan.odin b/core/sync/chan/chan.odin index cb299f23f..f0b04f3b4 100644 --- a/core/sync/chan/chan.odin +++ b/core/sync/chan/chan.odin @@ -260,6 +260,10 @@ try_send_raw :: proc "contextless" (c: ^Raw_Chan, msg_in: rawptr) -> (ok: bool) return false } + if sync.atomic_load(&c.closed) { + return false + } + ok = raw_queue_push(c.queue, msg_in) if sync.atomic_load(&c.r_waiting) > 0 { sync.signal(&c.r_cond) From 8a14a656fbd8843628b61e5a20877b40b772482c Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Mon, 9 Sep 2024 16:05:29 -0400 Subject: [PATCH 43/72] Fix `chan.can_send` for unbuffered channels `w_waiting` is the signal that says a caller is waiting to be able to send something. It is incremented upon send and - in the case of an unbuffered channel - it can only hold one message. Therefore, check that `w_waiting` is zero instead. --- core/sync/chan/chan.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/sync/chan/chan.odin b/core/sync/chan/chan.odin index f0b04f3b4..5b9a764b4 100644 --- a/core/sync/chan/chan.odin +++ b/core/sync/chan/chan.odin @@ -444,7 +444,7 @@ can_send :: proc "contextless" (c: ^Raw_Chan) -> bool { if is_buffered(c) { return c.queue.len < c.queue.cap } - return sync.atomic_load(&c.r_waiting) > 0 + return sync.atomic_load(&c.w_waiting) == 0 } From 074314b8874886f8956e9a7e9a773ac059051030 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Mon, 9 Sep 2024 18:38:29 -0400 Subject: [PATCH 44/72] Fix data race in `test_core_flags` --- tests/core/flags/test_core_flags.odin | 33 ++++++++++++++++----------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/tests/core/flags/test_core_flags.odin b/tests/core/flags/test_core_flags.odin index f8305f2ea..8fcd6a8a7 100644 --- a/tests/core/flags/test_core_flags.odin +++ b/tests/core/flags/test_core_flags.odin @@ -12,6 +12,26 @@ import "core:strings" import "core:testing" import "core:time/datetime" +Custom_Data :: struct { + a: int, +} + +@(init) +init_custom_type_setter :: proc() { + // NOTE: This is done here so it can be out of the flow of the + // multi-threaded test runner, to prevent any data races that could be + // reported by using `-sanitize:thread`. + // + // Do mind that this means every test here acknowledges the `Custom_Data` type. + flags.register_type_setter(proc (data: rawptr, data_type: typeid, _, _: string) -> (string, bool, runtime.Allocator_Error) { + if data_type == Custom_Data { + (cast(^Custom_Data)data).a = 32 + return "", true, nil + } + return "", false, nil + }) +} + @(test) test_no_args :: proc(t: ^testing.T) { S :: struct { @@ -1230,9 +1250,6 @@ test_net :: proc(t: ^testing.T) { @(test) test_custom_type_setter :: proc(t: ^testing.T) { Custom_Bool :: distinct bool - Custom_Data :: struct { - a: int, - } S :: struct { a: Custom_Data, @@ -1240,16 +1257,6 @@ test_custom_type_setter :: proc(t: ^testing.T) { } s: S - // NOTE: Mind that this setter is global state, and the test runner is multi-threaded. - // It should be fine so long as all type setter tests are in this one test proc. - flags.register_type_setter(proc (data: rawptr, data_type: typeid, _, _: string) -> (string, bool, runtime.Allocator_Error) { - if data_type == Custom_Data { - (cast(^Custom_Data)data).a = 32 - return "", true, nil - } - return "", false, nil - }) - defer flags.register_type_setter(nil) args := [?]string { "-a:hellope", "-b:true" } result := flags.parse(&s, args[:]) testing.expect_value(t, result, nil) From 3a6010918033e1548e84d57a07074cdbf802ff9b Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Mon, 9 Sep 2024 19:09:16 -0400 Subject: [PATCH 45/72] Fix signalling test child threads crashing test 0 A thread made inside a test does not share the test index of its parent, so any time one of those threads failed an assert, it would tell the runner to shutdown test index zero. --- core/testing/signal_handler_libc.odin | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/core/testing/signal_handler_libc.odin b/core/testing/signal_handler_libc.odin index 27d1a0735..e2d170595 100644 --- a/core/testing/signal_handler_libc.odin +++ b/core/testing/signal_handler_libc.odin @@ -26,6 +26,8 @@ import "core:os" @(private="file", thread_local) local_test_index: libc.sig_atomic_t +@(private="file", thread_local) +local_test_index_set: bool // Windows does not appear to have a SIGTRAP, so this is defined here, instead // of in the libc package, just so there's no confusion about it being @@ -45,6 +47,13 @@ stop_runner_callback :: proc "c" (sig: libc.int) { @(private="file") stop_test_callback :: proc "c" (sig: libc.int) { + if !local_test_index_set { + // We're a thread created by a test thread. + // + // There's nothing we can do to inform the test runner about who + // signalled, so hopefully the test will handle their own sub-threads. + return + } if local_test_index == -1 { // We're the test runner, and we ourselves have caught a signal from // which there is no recovery. @@ -114,6 +123,7 @@ This is a dire bug and should be reported to the Odin developers. _setup_signal_handler :: proc() { local_test_index = -1 + local_test_index_set = true // Catch user interrupt / CTRL-C. libc.signal(libc.SIGINT, stop_runner_callback) @@ -135,6 +145,7 @@ _setup_signal_handler :: proc() { _setup_task_signal_handler :: proc(test_index: int) { local_test_index = cast(libc.sig_atomic_t)test_index + local_test_index_set = true } _should_stop_runner :: proc() -> bool { From b2c2235e587bb8902dbf35ef84373bb5f616a814 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Mon, 9 Sep 2024 19:11:44 -0400 Subject: [PATCH 46/72] Fix `recursive_benaphore_try_lock` Previously, if the owner called this, it would fail. --- core/sync/extended.odin | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/sync/extended.odin b/core/sync/extended.odin index 83cc648b4..d5521935a 100644 --- a/core/sync/extended.odin +++ b/core/sync/extended.odin @@ -474,10 +474,10 @@ recursive_benaphore_try_lock :: proc "contextless" (b: ^Recursive_Benaphore) -> tid := current_thread_id() if b.owner == tid { atomic_add_explicit(&b.counter, 1, .Acquire) - } - - if v, _ := atomic_compare_exchange_strong_explicit(&b.counter, 0, 1, .Acquire, .Acquire); v != 0 { - return false + } else { + if v, _ := atomic_compare_exchange_strong_explicit(&b.counter, 0, 1, .Acquire, .Acquire); v != 0 { + return false + } } // inside the lock b.owner = tid From f16ed256eaf90fb0fed1e795f6c62cd356180422 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Wed, 11 Sep 2024 08:00:27 +1100 Subject: [PATCH 47/72] [mem]: Fix handling of default resize to check alignment --- base/runtime/internal.odin | 11 +++++++---- core/mem/alloc.odin | 2 +- core/mem/mem.odin | 11 +++++++++++ 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/base/runtime/internal.odin b/base/runtime/internal.odin index ff60cf547..a0bda9d40 100644 --- a/base/runtime/internal.odin +++ b/base/runtime/internal.odin @@ -118,16 +118,15 @@ mem_copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> r DEFAULT_ALIGNMENT :: 2*align_of(rawptr) mem_alloc_bytes :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) { - if size == 0 { - return nil, nil - } - if allocator.procedure == nil { + assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc) + if size == 0 || allocator.procedure == nil{ return nil, nil } return allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc) } mem_alloc :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) { + assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc) if size == 0 || allocator.procedure == nil { return nil, nil } @@ -135,6 +134,7 @@ mem_alloc :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, a } mem_alloc_non_zeroed :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) { + assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc) if size == 0 || allocator.procedure == nil { return nil, nil } @@ -174,6 +174,7 @@ mem_free_all :: #force_inline proc(allocator := context.allocator, loc := #calle } _mem_resize :: #force_inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, should_zero: bool, loc := #caller_location) -> (data: []byte, err: Allocator_Error) { + assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc) if allocator.procedure == nil { return nil, nil } @@ -215,9 +216,11 @@ _mem_resize :: #force_inline proc(ptr: rawptr, old_size, new_size: int, alignmen } mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Allocator_Error) { + assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc) return _mem_resize(ptr, old_size, new_size, alignment, allocator, true, loc) } non_zero_mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Allocator_Error) { + assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc) return _mem_resize(ptr, old_size, new_size, alignment, allocator, false, loc) } diff --git a/core/mem/alloc.odin b/core/mem/alloc.odin index 1ede92837..5f65e9ebc 100644 --- a/core/mem/alloc.odin +++ b/core/mem/alloc.odin @@ -1096,7 +1096,7 @@ _default_resize_bytes_align :: #force_inline proc( err := free_bytes(old_data, allocator, loc) return nil, err } - if new_size == old_size { + if new_size == old_size && is_aligned(old_memory, alignment) { return old_data, .None } new_memory : []byte diff --git a/core/mem/mem.odin b/core/mem/mem.odin index 0554cee23..b57b18ffc 100644 --- a/core/mem/mem.odin +++ b/core/mem/mem.odin @@ -456,6 +456,17 @@ is_power_of_two :: proc "contextless" (x: uintptr) -> bool { return (x & (x-1)) == 0 } +/* +Check if a pointer is aligned. + +This procedure checks whether a pointer `x` is aligned to a boundary specified +by `align`, and returns `true` if the pointer is aligned, and false otherwise. +*/ +is_aligned :: proc "contextless" (x: rawptr, align: int) -> bool { + p := uintptr(x) + return (p & (1< Date: Mon, 9 Sep 2024 19:15:06 -0400 Subject: [PATCH 48/72] Fix data races in `sync.Recursive_Benaphore` --- core/sync/extended.odin | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/core/sync/extended.odin b/core/sync/extended.odin index d5521935a..924ba92a6 100644 --- a/core/sync/extended.odin +++ b/core/sync/extended.odin @@ -449,13 +449,15 @@ recursive benaphore, until the lock is released. */ recursive_benaphore_lock :: proc "contextless" (b: ^Recursive_Benaphore) { tid := current_thread_id() - if atomic_add_explicit(&b.counter, 1, .Acquire) > 1 { - if tid != b.owner { - sema_wait(&b.sema) + check_owner: if tid != atomic_load_explicit(&b.owner, .Acquire) { + atomic_add_explicit(&b.counter, 1, .Relaxed) + if _, ok := atomic_compare_exchange_strong_explicit(&b.owner, 0, tid, .Release, .Relaxed); ok { + break check_owner } + sema_wait(&b.sema) + atomic_store_explicit(&b.owner, tid, .Release) } // inside the lock - b.owner = tid b.recursion += 1 } @@ -472,15 +474,14 @@ benaphore, until the lock is released. */ recursive_benaphore_try_lock :: proc "contextless" (b: ^Recursive_Benaphore) -> bool { tid := current_thread_id() - if b.owner == tid { - atomic_add_explicit(&b.counter, 1, .Acquire) - } else { - if v, _ := atomic_compare_exchange_strong_explicit(&b.counter, 0, 1, .Acquire, .Acquire); v != 0 { - return false + check_owner: if tid != atomic_load_explicit(&b.owner, .Acquire) { + if _, ok := atomic_compare_exchange_strong_explicit(&b.owner, 0, tid, .Release, .Relaxed); ok { + atomic_add_explicit(&b.counter, 1, .Relaxed) + break check_owner } + return false } // inside the lock - b.owner = tid b.recursion += 1 return true } @@ -494,14 +495,14 @@ for other threads for entering. */ recursive_benaphore_unlock :: proc "contextless" (b: ^Recursive_Benaphore) { tid := current_thread_id() - assert_contextless(tid == b.owner, "tid != b.owner") + assert_contextless(tid == atomic_load_explicit(&b.owner, .Relaxed), "tid != b.owner") b.recursion -= 1 recursion := b.recursion + if recursion == 0 { - b.owner = 0 - } - if atomic_sub_explicit(&b.counter, 1, .Release) > 0 { - if recursion == 0 { + if atomic_sub_explicit(&b.counter, 1, .Relaxed) == 1 { + atomic_store_explicit(&b.owner, 0, .Release) + } else { sema_post(&b.sema) } } From a1435a6a904b2eb2b154a463c5d60f6c1f55abbc Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Tue, 10 Sep 2024 14:51:00 -0400 Subject: [PATCH 49/72] Fix deadlock in `Auto_Reset_Event` --- core/sync/extended.odin | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/core/sync/extended.odin b/core/sync/extended.odin index 924ba92a6..0971516a3 100644 --- a/core/sync/extended.odin +++ b/core/sync/extended.odin @@ -228,15 +228,14 @@ thread. */ auto_reset_event_signal :: proc "contextless" (e: ^Auto_Reset_Event) { old_status := atomic_load_explicit(&e.status, .Relaxed) + new_status := old_status + 1 if old_status < 1 else 1 for { - new_status := old_status + 1 if old_status < 1 else 1 if _, ok := atomic_compare_exchange_weak_explicit(&e.status, old_status, new_status, .Release, .Relaxed); ok { break } - - if old_status < 0 { - sema_post(&e.sema) - } + } + if old_status < 0 { + sema_post(&e.sema) } } From b1db33b519bf52e2d0e6e42ca9daccc7470d6b8b Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Tue, 10 Sep 2024 19:04:44 -0400 Subject: [PATCH 50/72] Add `cpu_relax` to `sync.auto_reset_event_signal` --- core/sync/extended.odin | 1 + 1 file changed, 1 insertion(+) diff --git a/core/sync/extended.odin b/core/sync/extended.odin index 0971516a3..0b1f79df2 100644 --- a/core/sync/extended.odin +++ b/core/sync/extended.odin @@ -233,6 +233,7 @@ auto_reset_event_signal :: proc "contextless" (e: ^Auto_Reset_Event) { if _, ok := atomic_compare_exchange_weak_explicit(&e.status, old_status, new_status, .Release, .Relaxed); ok { break } + cpu_relax() } if old_status < 0 { sema_post(&e.sema) From 2938655a3d8801d1327b0076812edcf357d760df Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Wed, 11 Sep 2024 07:07:09 -0400 Subject: [PATCH 51/72] Fix CPU count detection in FreeBSD & NetBSD --- core/os/os_freebsd.odin | 2 +- core/os/os_netbsd.odin | 2 +- src/gb/gb.h | 12 ++++++++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/core/os/os_freebsd.odin b/core/os/os_freebsd.odin index c05a06129..241f42c0b 100644 --- a/core/os/os_freebsd.odin +++ b/core/os/os_freebsd.odin @@ -920,7 +920,7 @@ get_page_size :: proc() -> int { _processor_core_count :: proc() -> int { count : int = 0 count_size := size_of(count) - if _sysctlbyname("hw.logicalcpu", &count, &count_size, nil, 0) == 0 { + if _sysctlbyname("hw.ncpu", &count, &count_size, nil, 0) == 0 { if count > 0 { return count } diff --git a/core/os/os_netbsd.odin b/core/os/os_netbsd.odin index a56c0b784..ba9b40fc3 100644 --- a/core/os/os_netbsd.odin +++ b/core/os/os_netbsd.odin @@ -978,7 +978,7 @@ get_page_size :: proc() -> int { _processor_core_count :: proc() -> int { count : int = 0 count_size := size_of(count) - if _sysctlbyname("hw.logicalcpu", &count, &count_size, nil, 0) == 0 { + if _sysctlbyname("hw.ncpu", &count, &count_size, nil, 0) == 0 { if count > 0 { return count } diff --git a/src/gb/gb.h b/src/gb/gb.h index 0e65696e2..1fef4b4f5 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -3195,11 +3195,11 @@ void gb_affinity_init(gbAffinity *a) { a->core_count = 1; a->threads_per_core = 1; - if (sysctlbyname("hw.logicalcpu", &count, &count_size, NULL, 0) == 0) { + if (sysctlbyname("kern.smp.cpus", &count, &count_size, NULL, 0) == 0) { if (count > 0) { a->thread_count = count; // Get # of physical cores - if (sysctlbyname("hw.physicalcpu", &count, &count_size, NULL, 0) == 0) { + if (sysctlbyname("kern.smp.cores", &count, &count_size, NULL, 0) == 0) { if (count > 0) { a->core_count = count; a->threads_per_core = a->thread_count / count; @@ -3210,6 +3210,14 @@ void gb_affinity_init(gbAffinity *a) { } } } + } else if (sysctlbyname("hw.ncpu", &count, &count_size, NULL, 0) == 0) { + // SMP disabled or unavailable. + if (count > 0) { + a->is_accurate = true; + a->thread_count = count; + a->core_count = count; + a->threads_per_core = 1; + } } } From 16cd16b91e9cabfba2d12e271712ca55cb16fa7d Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sun, 8 Sep 2024 18:23:28 -0400 Subject: [PATCH 52/72] Fix comments --- core/sync/extended.odin | 18 +++++++++--------- core/sync/primitives.odin | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/core/sync/extended.odin b/core/sync/extended.odin index 0b1f79df2..30b1b2770 100644 --- a/core/sync/extended.odin +++ b/core/sync/extended.odin @@ -8,7 +8,7 @@ _ :: vg Wait group. Wait group is a synchronization primitive used by the waiting thread to wait, -until a all working threads finish work. +until all working threads finish work. The waiting thread first sets the number of working threads it will expect to wait for using `wait_group_add` call, and start waiting using `wait_group_wait` @@ -35,7 +35,7 @@ Wait_Group :: struct #no_copy { /* Increment an internal counter of a wait group. -This procedure atomicaly increments a number to the specified wait group's +This procedure atomically increments a number to the specified wait group's internal counter by a specified amount. This operation can be done on any thread. */ @@ -121,7 +121,7 @@ When `barrier_wait` procedure is called by any thread, that thread will block the execution, until all threads associated with the barrier reach the same point of execution and also call `barrier_wait`. -when barrier is initialized, a `thread_count` parameter is passed, signifying +When a barrier is initialized, a `thread_count` parameter is passed, signifying the amount of participant threads of the barrier. The barrier also keeps track of an internal atomic counter. When a thread calls `barrier_wait`, the internal counter is incremented. When the internal counter reaches `thread_count`, it is @@ -208,7 +208,7 @@ Represents a thread synchronization primitive that, when signalled, releases one single waiting thread and then resets automatically to a state where it can be signalled again. -When a thread calls `auto_reset_event_wait`, it's execution will be blocked, +When a thread calls `auto_reset_event_wait`, its execution will be blocked, until the event is signalled by another thread. The call to `auto_reset_event_signal` wakes up exactly one thread waiting for the event. */ @@ -331,8 +331,8 @@ Benaphore. A benaphore is a combination of an atomic variable and a semaphore that can improve locking efficiency in a no-contention system. Acquiring a benaphore -lock doesn't call into an internal semaphore, if no other thread in a middle of -a critical section. +lock doesn't call into an internal semaphore, if no other thread is in the +middle of a critical section. Once a lock on a benaphore is acquired by a thread, no other thread is allowed into any critical sections, associted with the same benaphore, until the lock @@ -381,7 +381,7 @@ Release a lock on a benaphore. This procedure releases a lock on the specified benaphore. If any of the threads are waiting on the lock, exactly one thread is allowed into a critical section -associated with the same banaphore. +associated with the same benaphore. */ benaphore_unlock :: proc "contextless" (b: ^Benaphore) { if atomic_sub_explicit(&b.counter, 1, .Release) > 1 { @@ -418,8 +418,8 @@ benaphore_guard :: proc "contextless" (m: ^Benaphore) -> bool { /* Recursive benaphore. -Recurisve benaphore is just like a plain benaphore, except it allows reentrancy -into the critical section. +A recursive benaphore is just like a plain benaphore, except it allows +reentrancy into the critical section. When a lock is acquired on a benaphore, all other threads attempting to acquire a lock on the same benaphore will be blocked from any critical sections, diff --git a/core/sync/primitives.odin b/core/sync/primitives.odin index 8187c904b..f091de045 100644 --- a/core/sync/primitives.odin +++ b/core/sync/primitives.odin @@ -389,7 +389,7 @@ recursive_mutex_guard :: proc "contextless" (m: ^Recursive_Mutex) -> bool { A condition variable. `Cond` implements a condition variable, a rendezvous point for threads waiting -for signalling the occurence of an event. Condition variables are used on +for signalling the occurence of an event. Condition variables are used in conjuction with mutexes to provide a shared access to one or more shared variable. From 7f7cfebc91cc319918bc0042789b0c7a931e56e2 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Mon, 9 Sep 2024 16:17:08 -0400 Subject: [PATCH 53/72] Add tests for `core:sync` and `core:sync/chan` --- tests/core/normal.odin | 2 + tests/core/sync/chan/test_core_sync_chan.odin | 274 +++++++ tests/core/sync/test_core_sync.odin | 718 ++++++++++++++++++ 3 files changed, 994 insertions(+) create mode 100644 tests/core/sync/chan/test_core_sync_chan.odin create mode 100644 tests/core/sync/test_core_sync.odin diff --git a/tests/core/normal.odin b/tests/core/normal.odin index 6174f2d5c..0670842ac 100644 --- a/tests/core/normal.odin +++ b/tests/core/normal.odin @@ -39,6 +39,8 @@ download_assets :: proc() { @(require) import "slice" @(require) import "strconv" @(require) import "strings" +@(require) import "sync" +@(require) import "sync/chan" @(require) import "sys/posix" @(require) import "sys/windows" @(require) import "text/i18n" diff --git a/tests/core/sync/chan/test_core_sync_chan.odin b/tests/core/sync/chan/test_core_sync_chan.odin new file mode 100644 index 000000000..9b8d9b354 --- /dev/null +++ b/tests/core/sync/chan/test_core_sync_chan.odin @@ -0,0 +1,274 @@ +package test_core_sync_chan + +import "base:runtime" +import "base:intrinsics" +import "core:log" +import "core:math/rand" +import "core:sync/chan" +import "core:testing" +import "core:thread" +import "core:time" + + +Message_Type :: enum i32 { + Result, + Add, + Multiply, + Subtract, + Divide, + End, +} + +Message :: struct { + type: Message_Type, + i: i64, +} + +Comm :: struct { + host: chan.Chan(Message), + client: chan.Chan(Message), + manual_buffering: bool, +} + +BUFFER_SIZE :: 8 +MAX_RAND :: 32 +FAIL_TIME :: 1 * time.Second +SLEEP_TIME :: 1 * time.Millisecond + +comm_client :: proc(th: ^thread.Thread) { + data := cast(^Comm)th.data + manual_buffering := data.manual_buffering + + n: i64 + + for manual_buffering && !chan.can_recv(data.host) { + thread.yield() + } + + recv_loop: for msg in chan.recv(data.host) { + #partial switch msg.type { + case .Add: n += msg.i + case .Multiply: n *= msg.i + case .Subtract: n -= msg.i + case .Divide: n /= msg.i + case .End: + break recv_loop + case: + panic("Unknown message type for client.") + } + + for manual_buffering && !chan.can_recv(data.host) { + thread.yield() + } + } + + for manual_buffering && !chan.can_send(data.host) { + thread.yield() + } + + chan.send(data.client, Message{.Result, n}) + chan.close(data.client) +} + +send_messages :: proc(t: ^testing.T, host: chan.Chan(Message), manual_buffering: bool = false) -> (expected: i64) { + expected = 1 + for manual_buffering && !chan.can_send(host) { + thread.yield() + } + chan.send(host, Message{.Add, 1}) + log.debug(Message{.Add, 1}) + + for _ in 0..<1+2*BUFFER_SIZE { + msg: Message + msg.i = 1 + rand.int63_max(MAX_RAND) + switch rand.int_max(4) { + case 0: + msg.type = .Add + expected += msg.i + case 1: + msg.type = .Multiply + expected *= msg.i + case 2: + msg.type = .Subtract + expected -= msg.i + case 3: + msg.type = .Divide + expected /= msg.i + } + + for manual_buffering && !chan.can_send(host) { + thread.yield() + } + if manual_buffering { + testing.expect(t, chan.len(host) == 0) + } + + chan.send(host, msg) + log.debug(msg) + } + + for manual_buffering && !chan.can_send(host) { + thread.yield() + } + chan.send(host, Message{.End, 0}) + log.debug(Message{.End, 0}) + chan.close(host) + + return +} + +@test +test_chan_buffered :: proc(t: ^testing.T) { + testing.set_fail_timeout(t, FAIL_TIME) + + comm: Comm + alloc_err: runtime.Allocator_Error + comm.host, alloc_err = chan.create_buffered(chan.Chan(Message), BUFFER_SIZE, context.allocator) + assert(alloc_err == nil, "allocation failed") + comm.client, alloc_err = chan.create_buffered(chan.Chan(Message), BUFFER_SIZE, context.allocator) + assert(alloc_err == nil, "allocation failed") + defer { + chan.destroy(comm.host) + chan.destroy(comm.client) + } + + testing.expect(t, chan.is_buffered(comm.host)) + testing.expect(t, chan.is_buffered(comm.client)) + testing.expect(t, !chan.is_unbuffered(comm.host)) + testing.expect(t, !chan.is_unbuffered(comm.client)) + testing.expect_value(t, chan.len(comm.host), 0) + testing.expect_value(t, chan.len(comm.client), 0) + testing.expect_value(t, chan.cap(comm.host), BUFFER_SIZE) + testing.expect_value(t, chan.cap(comm.client), BUFFER_SIZE) + + reckoner := thread.create(comm_client) + defer thread.destroy(reckoner) + reckoner.data = &comm + thread.start(reckoner) + + expected := send_messages(t, comm.host, manual_buffering = false) + + // Sleep so we can give the other thread enough time to buffer its message. + time.sleep(SLEEP_TIME) + + testing.expect_value(t, chan.len(comm.client), 1) + result, ok := chan.try_recv(comm.client) + + // One more sleep to ensure it has enough time to close. + time.sleep(SLEEP_TIME) + + testing.expect_value(t, chan.is_closed(comm.client), true) + testing.expect_value(t, ok, true) + testing.expect_value(t, result.i, expected) + log.debug(result, expected) + + // Make sure sending to closed channels fails. + testing.expect_value(t, chan.send(comm.host, Message{.End, 0}), false) + testing.expect_value(t, chan.send(comm.client, Message{.End, 0}), false) + testing.expect_value(t, chan.try_send(comm.host, Message{.End, 0}), false) + testing.expect_value(t, chan.try_send(comm.client, Message{.End, 0}), false) + _, ok = chan.recv(comm.host); testing.expect_value(t, ok, false) + _, ok = chan.recv(comm.client); testing.expect_value(t, ok, false) + _, ok = chan.try_recv(comm.host); testing.expect_value(t, ok, false) + _, ok = chan.try_recv(comm.client); testing.expect_value(t, ok, false) +} + +@test +test_chan_unbuffered :: proc(t: ^testing.T) { + testing.set_fail_timeout(t, FAIL_TIME) + + comm: Comm + comm.manual_buffering = true + alloc_err: runtime.Allocator_Error + comm.host, alloc_err = chan.create_unbuffered(chan.Chan(Message), context.allocator) + assert(alloc_err == nil, "allocation failed") + comm.client, alloc_err = chan.create_unbuffered(chan.Chan(Message), context.allocator) + assert(alloc_err == nil, "allocation failed") + defer { + chan.destroy(comm.host) + chan.destroy(comm.client) + } + + testing.expect(t, !chan.is_buffered(comm.host)) + testing.expect(t, !chan.is_buffered(comm.client)) + testing.expect(t, chan.is_unbuffered(comm.host)) + testing.expect(t, chan.is_unbuffered(comm.client)) + testing.expect_value(t, chan.len(comm.host), 0) + testing.expect_value(t, chan.len(comm.client), 0) + testing.expect_value(t, chan.cap(comm.host), 0) + testing.expect_value(t, chan.cap(comm.client), 0) + + reckoner := thread.create(comm_client) + defer thread.destroy(reckoner) + reckoner.data = &comm + thread.start(reckoner) + + for !chan.can_send(comm.client) { + thread.yield() + } + + expected := send_messages(t, comm.host) + testing.expect_value(t, chan.is_closed(comm.host), true) + + for !chan.can_recv(comm.client) { + thread.yield() + } + + result, ok := chan.try_recv(comm.client) + testing.expect_value(t, ok, true) + testing.expect_value(t, result.i, expected) + log.debug(result, expected) + + // Sleep so we can give the other thread enough time to close its side + // after we've received its message. + time.sleep(SLEEP_TIME) + + testing.expect_value(t, chan.is_closed(comm.client), true) + + // Make sure sending and receiving on closed channels fails. + testing.expect_value(t, chan.send(comm.host, Message{.End, 0}), false) + testing.expect_value(t, chan.send(comm.client, Message{.End, 0}), false) + testing.expect_value(t, chan.try_send(comm.host, Message{.End, 0}), false) + testing.expect_value(t, chan.try_send(comm.client, Message{.End, 0}), false) + _, ok = chan.recv(comm.host); testing.expect_value(t, ok, false) + _, ok = chan.recv(comm.client); testing.expect_value(t, ok, false) + _, ok = chan.try_recv(comm.host); testing.expect_value(t, ok, false) + _, ok = chan.try_recv(comm.client); testing.expect_value(t, ok, false) +} + +@test +test_full_buffered_closed_chan_deadlock :: proc(t: ^testing.T) { + testing.set_fail_timeout(t, FAIL_TIME) + + ch, alloc_err := chan.create_buffered(chan.Chan(int), 1, context.allocator) + assert(alloc_err == nil, "allocation failed") + defer chan.destroy(ch) + + testing.expect(t, chan.can_send(ch)) + testing.expect(t, chan.send(ch, 32)) + testing.expect(t, chan.close(ch)) + testing.expect(t, !chan.send(ch, 32)) +} + +// This test guarantees a buffered channel's messages can still be received +// even after closing. This is currently how the API works. If that changes, +// this test will need to change. +@test +test_accept_message_from_closed_buffered_chan :: proc(t: ^testing.T) { + testing.set_fail_timeout(t, FAIL_TIME) + + ch, alloc_err := chan.create_buffered(chan.Chan(int), 2, context.allocator) + assert(alloc_err == nil, "allocation failed") + defer chan.destroy(ch) + + testing.expect(t, chan.can_send(ch)) + testing.expect(t, chan.send(ch, 32)) + testing.expect(t, chan.send(ch, 64)) + testing.expect(t, chan.close(ch)) + result, ok := chan.recv(ch) + testing.expect_value(t, result, 32) + testing.expect(t, ok) + result, ok = chan.try_recv(ch) + testing.expect_value(t, result, 64) + testing.expect(t, ok) +} diff --git a/tests/core/sync/test_core_sync.odin b/tests/core/sync/test_core_sync.odin new file mode 100644 index 000000000..32c08f935 --- /dev/null +++ b/tests/core/sync/test_core_sync.odin @@ -0,0 +1,718 @@ +// NOTE(Feoramund): These tests should be run a few hundred times, with and +// without `-sanitize:thread` enabled, to ensure maximum safety. +// +// Keep in mind that running with the debug logs uncommented can result in +// failures disappearing due to the delay of sending the log message causing +// different synchronization patterns. +// +// These tests are temporarily disabled on Darwin, as there is currently a +// stall occurring which I cannot debug. + +//+build !darwin +package test_core_sync + +import "base:intrinsics" +// import "core:log" +import "core:sync" +import "core:testing" +import "core:thread" +import "core:time" + +FAIL_TIME :: 1 * time.Second +SLEEP_TIME :: 1 * time.Millisecond +SMALL_SLEEP_TIME :: 10 * time.Microsecond + +// This needs to be high enough to cause a data race if any of the +// synchronization primitives fail. +THREADS :: 8 + +// Manually wait on all threads to finish. +// +// This reduces a dependency on a `Wait_Group` or similar primitives. +// +// It's also important that we wait for every thread to finish, as it's +// possible for a thread to finish after the test if we don't check, despite +// joining it to the test thread. +wait_for :: proc(threads: []^thread.Thread) { + wait_loop: for { + count := len(threads) + for v in threads { + if thread.is_done(v) { + count -= 1 + } + } + if count == 0 { + break wait_loop + } + thread.yield() + } + for t in threads { + thread.join(t) + thread.destroy(t) + } +} + +// +// core:sync/primitives.odin +// + +@test +test_mutex :: proc(t: ^testing.T) { + testing.set_fail_timeout(t, FAIL_TIME) + + Data :: struct { + m: sync.Mutex, + number: int, + } + + p :: proc(th: ^thread.Thread) { + data := cast(^Data)th.data + + // log.debugf("MUTEX-%v> locking", th.id) + sync.mutex_lock(&data.m) + data.number += 1 + // log.debugf("MUTEX-%v> unlocking", th.id) + sync.mutex_unlock(&data.m) + // log.debugf("MUTEX-%v> leaving", th.id) + } + + data: Data + threads: [THREADS]^thread.Thread + + for &v in threads { + v = thread.create(p) + v.data = &data + v.init_context = context + thread.start(v) + } + + wait_for(threads[:]) + + testing.expect_value(t, data.number, THREADS) +} + +@test +test_rw_mutex :: proc(t: ^testing.T) { + testing.set_fail_timeout(t, FAIL_TIME) + + Data :: struct { + m1: sync.RW_Mutex, + m2: sync.RW_Mutex, + number1: int, + number2: int, + } + + p :: proc(th: ^thread.Thread) { + data := cast(^Data)th.data + + sync.rw_mutex_shared_lock(&data.m1) + n := data.number1 + sync.rw_mutex_shared_unlock(&data.m1) + + sync.rw_mutex_lock(&data.m2) + data.number2 += n + sync.rw_mutex_unlock(&data.m2) + } + + data: Data + threads: [THREADS]^thread.Thread + + sync.rw_mutex_lock(&data.m1) + + for &v in threads { + v = thread.create(p) + v.data = &data + v.init_context = context + thread.start(v) + } + + data.number1 = 1 + sync.rw_mutex_unlock(&data.m1) + + wait_for(threads[:]) + + testing.expect_value(t, data.number2, THREADS) +} + +@test +test_recursive_mutex :: proc(t: ^testing.T) { + testing.set_fail_timeout(t, FAIL_TIME) + + Data :: struct { + m: sync.Recursive_Mutex, + number: int, + } + + p :: proc(th: ^thread.Thread) { + data := cast(^Data)th.data + + // log.debugf("REC_MUTEX-%v> locking", th.id) + tried1 := sync.recursive_mutex_try_lock(&data.m) + for _ in 0..<3 { + sync.recursive_mutex_lock(&data.m) + } + tried2 := sync.recursive_mutex_try_lock(&data.m) + // log.debugf("REC_MUTEX-%v> locked", th.id) + data.number += 1 + // log.debugf("REC_MUTEX-%v> unlocking", th.id) + for _ in 0..<3 { + sync.recursive_mutex_unlock(&data.m) + } + if tried1 { sync.recursive_mutex_unlock(&data.m) } + if tried2 { sync.recursive_mutex_unlock(&data.m) } + // log.debugf("REC_MUTEX-%v> leaving", th.id) + } + + data: Data + threads: [THREADS]^thread.Thread + + for &v in threads { + v = thread.create(p) + v.data = &data + v.init_context = context + thread.start(v) + } + + wait_for(threads[:]) + + testing.expect_value(t, data.number, THREADS) +} + +@test +test_cond :: proc(t: ^testing.T) { + testing.set_fail_timeout(t, FAIL_TIME) + + Data :: struct { + c: sync.Cond, + m: sync.Mutex, + i: int, + number: int, + } + + p :: proc(th: ^thread.Thread) { + data := cast(^Data)th.data + + sync.mutex_lock(&data.m) + + for intrinsics.atomic_load(&data.i) != 1 { + sync.cond_wait(&data.c, &data.m) + } + + data.number += intrinsics.atomic_load(&data.i) + + sync.mutex_unlock(&data.m) + } + + data: Data + threads: [THREADS]^thread.Thread + data.i = -1 + + sync.mutex_lock(&data.m) + + for &v in threads { + v = thread.create(p) + v.data = &data + v.init_context = context + thread.start(v) + } + + time.sleep(SLEEP_TIME) + data.i = 1 + sync.mutex_unlock(&data.m) + sync.cond_broadcast(&data.c) + + wait_for(threads[:]) + + testing.expect_value(t, data.number, THREADS) +} + +@test +test_cond_with_timeout :: proc(t: ^testing.T) { + testing.set_fail_timeout(t, FAIL_TIME) + + c: sync.Cond + m: sync.Mutex + sync.mutex_lock(&m) + sync.cond_wait_with_timeout(&c, &m, SLEEP_TIME) +} + +@test +test_semaphore :: proc(t: ^testing.T) { + testing.set_fail_timeout(t, FAIL_TIME) + + Data :: struct { + s: sync.Sema, + number: int, + } + + p :: proc(th: ^thread.Thread) { + data := cast(^Data)th.data + + // log.debugf("SEM-%v> waiting", th.id) + sync.sema_wait(&data.s) + data.number += 1 + // log.debugf("SEM-%v> posting", th.id) + sync.sema_post(&data.s) + // log.debugf("SEM-%v> leaving", th.id) + } + + data: Data + threads: [THREADS]^thread.Thread + + for &v in threads { + v = thread.create(p) + v.data = &data + v.init_context = context + thread.start(v) + } + sync.sema_post(&data.s) + + wait_for(threads[:]) + + testing.expect_value(t, data.number, THREADS) +} + +@test +test_semaphore_with_timeout :: proc(t: ^testing.T) { + testing.set_fail_timeout(t, FAIL_TIME) + + s: sync.Sema + sync.sema_wait_with_timeout(&s, SLEEP_TIME) +} + +@test +test_futex :: proc(t: ^testing.T) { + testing.set_fail_timeout(t, FAIL_TIME) + + Data :: struct { + f: sync.Futex, + i: int, + number: int, + } + + p :: proc(th: ^thread.Thread) { + data := cast(^Data)th.data + + // log.debugf("FUTEX-%v> waiting", th.id) + sync.futex_wait(&data.f, 3) + // log.debugf("FUTEX-%v> done", th.id) + + n := data.i + intrinsics.atomic_add(&data.number, n) + } + + data: Data + data.i = -1 + data.f = 3 + threads: [THREADS]^thread.Thread + + for &v in threads { + v = thread.create(p) + v.data = &data + v.init_context = context + thread.start(v) + } + + data.i = 1 + // Change the futex variable to keep late-starters from stalling. + data.f = 0 + sync.futex_broadcast(&data.f) + + wait_for(threads[:]) + + testing.expect_value(t, data.number, THREADS) +} + +@test +test_futex_with_timeout :: proc(t: ^testing.T) { + testing.set_fail_timeout(t, FAIL_TIME) + + f: sync.Futex = 1 + sync.futex_wait_with_timeout(&f, 1, SLEEP_TIME) +} + +// +// core:sync/extended.odin +// + +@test +test_wait_group :: proc(t: ^testing.T) { + testing.set_fail_timeout(t, FAIL_TIME) + + Data :: struct { + step1: sync.Wait_Group, + step2: sync.Wait_Group, + i: int, + number: int, + } + + p :: proc(th: ^thread.Thread) { + data := cast(^Data)th.data + + sync.wait_group_wait(&data.step1) + + n := data.i + intrinsics.atomic_add(&data.number, n) + + sync.wait_group_done(&data.step2) + } + + data: Data + data.i = -1 + threads: [THREADS]^thread.Thread + + sync.wait_group_add(&data.step1, 1) + sync.wait_group_add(&data.step2, THREADS) + + for &v in threads { + v = thread.create(p) + v.data = &data + v.init_context = context + thread.start(v) + } + + time.sleep(SMALL_SLEEP_TIME) + data.i = 1 + sync.wait_group_done(&data.step1) + + sync.wait_group_wait(&data.step2) + + wait_for(threads[:]) + + testing.expect_value(t, data.step1.counter, 0) + testing.expect_value(t, data.step2.counter, 0) + testing.expect_value(t, data.number, THREADS) +} + +@test +test_wait_group_with_timeout :: proc(t: ^testing.T) { + testing.set_fail_timeout(t, FAIL_TIME) + + wg: sync.Wait_Group + sync.wait_group_wait_with_timeout(&wg, SLEEP_TIME) +} + +@test +test_barrier :: proc(t: ^testing.T) { + testing.set_fail_timeout(t, FAIL_TIME) + + Data :: struct { + b: sync.Barrier, + i: int, + number: int, + + } + + p :: proc(th: ^thread.Thread) { + data := cast(^Data)th.data + + sync.barrier_wait(&data.b) + + intrinsics.atomic_add(&data.number, data.i) + } + + data: Data + data.i = -1 + threads: [THREADS]^thread.Thread + + sync.barrier_init(&data.b, THREADS + 1) // +1 for this thread, of course. + + for &v in threads { + v = thread.create(p) + v.data = &data + v.init_context = context + thread.start(v) + } + time.sleep(SMALL_SLEEP_TIME) + data.i = 1 + sync.barrier_wait(&data.b) + + wait_for(threads[:]) + + testing.expect_value(t, data.b.index, 0) + testing.expect_value(t, data.b.generation_id, 1) + testing.expect_value(t, data.b.thread_count, THREADS + 1) + testing.expect_value(t, data.number, THREADS) +} + +@test +test_auto_reset :: proc(t: ^testing.T) { + testing.set_fail_timeout(t, FAIL_TIME) + + Data :: struct { + a: sync.Auto_Reset_Event, + number: int, + } + + p :: proc(th: ^thread.Thread) { + data := cast(^Data)th.data + + // log.debugf("AUR-%v> entering", th.id) + sync.auto_reset_event_wait(&data.a) + // log.debugf("AUR-%v> adding", th.id) + data.number += 1 + // log.debugf("AUR-%v> signalling", th.id) + sync.auto_reset_event_signal(&data.a) + // log.debugf("AUR-%v> leaving", th.id) + } + + data: Data + threads: [THREADS]^thread.Thread + + for &v in threads { + v = thread.create(p) + v.data = &data + v.init_context = context + thread.start(v) + } + + // There is a chance that this test can stall if a signal is sent before + // all threads are queued, because it's possible for some number of threads + // to get to the waiting state, the signal to fire, all of the waited + // threads to pass successfully, then the other threads come in with no-one + // to run a signal. + // + // So we'll just test a fully-waited queue of cascading threads. + for { + status := intrinsics.atomic_load(&data.a.status) + if status == -THREADS { + // log.debug("All Auto_Reset_Event threads have queued.") + break + } + intrinsics.cpu_relax() + } + + sync.auto_reset_event_signal(&data.a) + + wait_for(threads[:]) + + // The last thread should leave this primitive in a signalled state. + testing.expect_value(t, data.a.status, 1) + testing.expect_value(t, data.number, THREADS) +} + +@test +test_auto_reset_already_signalled :: proc(t: ^testing.T) { + testing.set_fail_timeout(t, FAIL_TIME) + + a: sync.Auto_Reset_Event + sync.auto_reset_event_signal(&a) + sync.auto_reset_event_wait(&a) + testing.expect_value(t, a.status, 0) +} + +@test +test_ticket_mutex :: proc(t: ^testing.T) { + testing.set_fail_timeout(t, FAIL_TIME) + + Data :: struct { + m: sync.Ticket_Mutex, + number: int, + } + + p :: proc(th: ^thread.Thread) { + data := cast(^Data)th.data + + // log.debugf("TIC-%i> entering", th.id) + // intrinsics.debug_trap() + sync.ticket_mutex_lock(&data.m) + // log.debugf("TIC-%i> locked", th.id) + data.number += 1 + // log.debugf("TIC-%i> unlocking", th.id) + sync.ticket_mutex_unlock(&data.m) + // log.debugf("TIC-%i> leaving", th.id) + } + + data: Data + threads: [THREADS]^thread.Thread + + for &v in threads { + v = thread.create(p) + v.data = &data + v.init_context = context + thread.start(v) + } + + wait_for(threads[:]) + + testing.expect_value(t, data.m.ticket, THREADS) + testing.expect_value(t, data.m.serving, THREADS) + testing.expect_value(t, data.number, THREADS) +} + +@test +test_benaphore :: proc(t: ^testing.T) { + testing.set_fail_timeout(t, FAIL_TIME) + + Data :: struct { + b: sync.Benaphore, + number: int, + } + + p :: proc(th: ^thread.Thread) { + data := cast(^Data)th.data + sync.benaphore_lock(&data.b) + data.number += 1 + sync.benaphore_unlock(&data.b) + } + + data: Data + threads: [THREADS]^thread.Thread + + for &v in threads { + v = thread.create(p) + v.data = &data + v.init_context = context + thread.start(v) + } + + wait_for(threads[:]) + + testing.expect_value(t, data.b.counter, 0) + testing.expect_value(t, data.number, THREADS) +} + +@test +test_recursive_benaphore :: proc(t: ^testing.T) { + testing.set_fail_timeout(t, FAIL_TIME) + + Data :: struct { + b: sync.Recursive_Benaphore, + number: int, + } + + p :: proc(th: ^thread.Thread) { + data := cast(^Data)th.data + + // log.debugf("REC_BEP-%i> entering", th.id) + tried1 := sync.recursive_benaphore_try_lock(&data.b) + for _ in 0..<3 { + sync.recursive_benaphore_lock(&data.b) + } + tried2 := sync.recursive_benaphore_try_lock(&data.b) + // log.debugf("REC_BEP-%i> locked", th.id) + data.number += 1 + for _ in 0..<3 { + sync.recursive_benaphore_unlock(&data.b) + } + if tried1 { sync.recursive_benaphore_unlock(&data.b) } + if tried2 { sync.recursive_benaphore_unlock(&data.b) } + // log.debugf("REC_BEP-%i> leaving", th.id) + } + + data: Data + threads: [THREADS]^thread.Thread + + for &v in threads { + v = thread.create(p) + v.data = &data + v.init_context = context + thread.start(v) + } + + wait_for(threads[:]) + + // The benaphore should be unowned at the end. + testing.expect_value(t, data.b.counter, 0) + testing.expect_value(t, data.b.owner, 0) + testing.expect_value(t, data.b.recursion, 0) + testing.expect_value(t, data.number, THREADS) +} + +@test +test_once :: proc(t: ^testing.T) { + testing.set_fail_timeout(t, FAIL_TIME) + + Data :: struct { + once: sync.Once, + number: int, + } + + write :: proc "contextless" (data: rawptr) { + data := cast(^Data)data + data.number += 1 + } + + p :: proc(th: ^thread.Thread) { + data := cast(^Data)th.data + // log.debugf("ONCE-%v> entering", th.id) + sync.once_do_with_data_contextless(&data.once, write, data) + // log.debugf("ONCE-%v> leaving", th.id) + } + + data: Data + threads: [THREADS]^thread.Thread + + for &v in threads { + v = thread.create(p) + v.data = &data + v.init_context = context + thread.start(v) + } + + wait_for(threads[:]) + + testing.expect_value(t, data.once.done, true) + testing.expect_value(t, data.number, 1) +} + +@test +test_park :: proc(t: ^testing.T) { + testing.set_fail_timeout(t, FAIL_TIME) + + Data :: struct { + car: sync.Parker, + number: int, + } + + data: Data + + th := thread.create_and_start_with_data(&data, proc(data: rawptr) { + data := cast(^Data)data + time.sleep(SLEEP_TIME) + sync.unpark(&data.car) + data.number += 1 + }) + + sync.park(&data.car) + + wait_for([]^thread.Thread{ th }) + + PARKER_EMPTY :: 0 + testing.expect_value(t, data.car.state, PARKER_EMPTY) + testing.expect_value(t, data.number, 1) +} + +@test +test_park_with_timeout :: proc(t: ^testing.T) { + testing.set_fail_timeout(t, FAIL_TIME) + + car: sync.Parker + sync.park_with_timeout(&car, SLEEP_TIME) +} + +@test +test_one_shot_event :: proc(t: ^testing.T) { + testing.set_fail_timeout(t, FAIL_TIME) + + Data :: struct { + event: sync.One_Shot_Event, + number: int, + } + + data: Data + + th := thread.create_and_start_with_data(&data, proc(data: rawptr) { + data := cast(^Data)data + time.sleep(SLEEP_TIME) + sync.one_shot_event_signal(&data.event) + data.number += 1 + }) + + sync.one_shot_event_wait(&data.event) + + wait_for([]^thread.Thread{ th }) + + testing.expect_value(t, data.event.state, 1) + testing.expect_value(t, data.number, 1) +} From e90f5d25281c082fc179cb0170edfe87b8e6c0c5 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sat, 14 Sep 2024 10:03:04 +1100 Subject: [PATCH 54/72] [mem]: Adjust the docs on the buddy allocator --- core/mem/allocators.odin | 205 ++++++++++++++++++++++++++++++++++++--- core/mem/doc.odin | 5 + 2 files changed, 197 insertions(+), 13 deletions(-) diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index f1e45d1a1..4b5efbacb 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -112,6 +112,12 @@ allocation occupies the next adjacent region of memory in the buffer. Since arena allocator does not keep track of any metadata associated with the allocations and their locations, it is impossible to free individual allocations. + +The arena allocator can be used for temporary allocations in frame-based memory +management. Games are one example of such applications. A global arena can be +used for any temporary memory allocations, and at the end of each frame all +temporary allocations are freed. Since no temporary object is going to live +longer than a frame, no lifetimes are violated. */ @(require_results) arena_allocator :: proc(arena: ^Arena) -> Allocator { @@ -423,7 +429,7 @@ scratch_alloc_bytes :: proc( } /* -Allocate memory from scratch allocator. +Allocate non-initialized memory from scratch allocator. This procedure allocates `size` bytes of memory aligned on a boundary specified by `alignment`. The allocated memory region is not explicitly zero-initialized. @@ -441,7 +447,7 @@ scratch_alloc_non_zeroed :: proc( } /* -Allocate memory from scratch allocator. +Allocate non-initialized memory from scratch allocator. This procedure allocates `size` bytes of memory aligned on a boundary specified by `alignment`. The allocated memory region is not explicitly zero-initialized. @@ -499,7 +505,7 @@ scratch_alloc_bytes_non_zeroed :: proc( } /* -Free memory to scratch allocator. +Free memory to the scratch allocator. This procedure frees the memory region allocated at pointer `ptr`. @@ -1176,14 +1182,6 @@ Each subsequent allocation will get the next adjacent memory region. The metadata is stored in the allocation headers, that are located before the start of each allocated memory region. Each header contains the amount of padding bytes between that header and end of the previous allocation. - -## Properties - -**Performance characteristics**: TODO - -**Has a backing allocator**: No - -**Saves metadata**: Allocation header before each allocation. */ @(require_results) small_stack_allocator :: proc(stack: ^Small_Stack) -> Allocator { @@ -1608,6 +1606,9 @@ dynamic_arena_allocator :: proc(a: ^Dynamic_Arena) -> Allocator { /* Destroy a dynamic arena. + +This procedure frees all allocations, made on a dynamic arena, including the +unused blocks, as well as the arrays for storing blocks. */ dynamic_arena_destroy :: proc(a: ^Dynamic_Arena) { dynamic_arena_free_all(a) @@ -1646,12 +1647,28 @@ _dynamic_arena_cycle_new_block :: proc(a: ^Dynamic_Arena, loc := #caller_locatio return } +/* +Allocate memory from a dynamic arena. + +This procedure allocates `size` bytes of memory aligned on a boundary specified +by `alignment` from a dynamic arena `a`. The allocated memory is +zero-initialized. This procedure returns a pointer to the newly allocated memory +region. +*/ @(private, 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) return raw_data(data), err } +/* +Allocate memory from a dynamic arena. + +This procedure allocates `size` bytes of memory aligned on a boundary specified +by `alignment` from a dynamic arena `a`. The allocated memory is +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) @@ -1661,12 +1678,28 @@ dynamic_arena_alloc_bytes :: proc(a: ^Dynamic_Arena, size: int, loc := #caller_l return bytes, err } +/* +Allocate non-initialized memory from a dynamic arena. + +This procedure allocates `size` bytes of memory aligned on a boundary specified +by `alignment` from a dynamic arena `a`. The allocated memory is not explicitly +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) return raw_data(data), err } +/* +Allocate non-initialized memory from a dynamic arena. + +This procedure allocates `size` bytes of memory aligned on a boundary specified +by `alignment` from a dynamic arena `a`. The allocated memory is not explicitly +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) { n := align_formula(size, a.alignment) @@ -1696,6 +1729,12 @@ dynamic_arena_alloc_bytes_non_zeroed :: proc(a: ^Dynamic_Arena, size: int, loc : return ([^]byte)(memory)[:size], nil } +/* +Reset the dynamic arena. + +This procedure frees all the allocations, owned by the dynamic arena, excluding +the unused blocks. +*/ dynamic_arena_reset :: proc(a: ^Dynamic_Arena, loc := #caller_location) { if a.current_block != nil { append(&a.unused_blocks, a.current_block, loc=loc) @@ -1712,6 +1751,12 @@ dynamic_arena_reset :: proc(a: ^Dynamic_Arena, loc := #caller_location) { a.bytes_left = 0 // Make new allocations call `_dynamic_arena_cycle_new_block` again. } +/* +Free all memory from a dynamic arena. + +This procedure frees all the allocations, owned by the dynamic arena, including +the unused blocks. +*/ dynamic_arena_free_all :: proc(a: ^Dynamic_Arena, loc := #caller_location) { dynamic_arena_reset(a) for block in a.unused_blocks { @@ -1720,6 +1765,22 @@ dynamic_arena_free_all :: proc(a: ^Dynamic_Arena, loc := #caller_location) { clear(&a.unused_blocks) } +/* +Resize an allocation. + +This procedure resizes a memory region, defined by its location, `old_memory`, +and its size, `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 `dynamic_arena_alloc()`, +allocating a memory region `size` bytes in size, aligned on a boundary specified +by `alignment`. + +If `size` is 0, this procedure acts just like `dynamic_arena_free()`, freeing +the memory region located at an address specified by `old_memory`. + +This procedure returns the pointer to the resized memory region. +*/ @(require_results) dynamic_arena_resize :: proc( a: ^Dynamic_Arena, @@ -1732,6 +1793,22 @@ dynamic_arena_resize :: proc( 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 `dynamic_arena_alloc()`, +allocating a memory region `size` bytes in size, aligned on a boundary specified +by `alignment`. + +If `size` is 0, this procedure acts just like `dynamic_arena_free()`, freeing the +memory region located at an address specified by `old_memory`. + +This procedure returns the slice of the resized memory region. +*/ @(require_results) dynamic_arena_resize_bytes :: proc( a: ^Dynamic_Arena, @@ -1750,6 +1827,22 @@ dynamic_arena_resize_bytes :: proc( return bytes, err } +/* +Resize an allocation without zero-initialization. + +This procedure resizes a memory region, defined by its location, `old_memory`, +and its size, `old_size` to have a size `size` and alignment `alignment`. The +newly allocated memory, if any is not explicitly zero-initialized. + +If `old_memory` is `nil`, this procedure acts just like `dynamic_arena_alloc()`, +allocating a memory region `size` bytes in size, aligned on a boundary specified +by `alignment`. + +If `size` is 0, this procedure acts just like `dynamic_arena_free()`, freeing the +memory region located at an address specified by `old_memory`. + +This procedure returns the pointer to the resized memory region. +*/ @(require_results) dynamic_arena_resize_non_zeroed :: proc( a: ^Dynamic_Arena, @@ -1762,6 +1855,22 @@ dynamic_arena_resize_non_zeroed :: proc( 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 not +explicitly zero-initialized. + +If `old_memory` is `nil`, this procedure acts just like `dynamic_arena_alloc()`, +allocating a memory region `size` bytes in size, aligned on a boundary specified +by `alignment`. + +If `size` is 0, this procedure acts just like `dynamic_arena_free()`, freeing +the memory region located at an address specified by `old_memory`. + +This procedure returns the slice of the resized memory region. +*/ @(require_results) dynamic_arena_resize_bytes_non_zeroed :: proc( a: ^Dynamic_Arena, @@ -1823,17 +1932,25 @@ dynamic_arena_allocator_proc :: proc( } - +/* +Header of the buddy block. +*/ Buddy_Block :: struct #align(align_of(uint)) { size: uint, is_free: bool, } +/* +Obtain the next buddy block. +*/ @(require_results) buddy_block_next :: proc(block: ^Buddy_Block) -> ^Buddy_Block { return (^Buddy_Block)(([^]byte)(block)[block.size:]) } +/* +Split the block into two, by truncating the given block to a given size. +*/ @(require_results) buddy_block_split :: proc(block: ^Buddy_Block, size: uint) -> ^Buddy_Block { block := block @@ -1854,6 +1971,9 @@ buddy_block_split :: proc(block: ^Buddy_Block, size: uint) -> ^Buddy_Block { return nil } +/* +Coalesce contiguous blocks in a range of blocks into one. +*/ buddy_block_coalescence :: proc(head, tail: ^Buddy_Block) { for { // Keep looping until there are no more buddies to coalesce @@ -1887,6 +2007,9 @@ buddy_block_coalescence :: proc(head, tail: ^Buddy_Block) { } } +/* +Find the best block for storing a given size in a range of blocks. +*/ @(require_results) buddy_block_find_best :: proc(head, tail: ^Buddy_Block, size: uint) -> ^Buddy_Block { assert(size != 0) @@ -1945,6 +2068,9 @@ buddy_block_find_best :: proc(head, tail: ^Buddy_Block, size: uint) -> ^Buddy_Bl return nil } +/* +The buddy allocator data. +*/ Buddy_Allocator :: struct { head: ^Buddy_Block, tail: ^Buddy_Block, @@ -1954,7 +2080,12 @@ Buddy_Allocator :: struct { /* Buddy allocator. -TODO +The buddy allocator is a type of allocator that splits the backing buffer into +multiple regions called buddy blocks. Initially, the allocator only has one +block with the size of the backing buffer. Upon each allocation, the allocator +finds the smallest block that can fit the size of requested memory region, and +splits the block according to the allocation size. If no block can be found, +the contiguous free blocks are coalesced and the search is performed again. */ @(require_results) buddy_allocator :: proc(b: ^Buddy_Allocator) -> Allocator { @@ -1964,6 +2095,12 @@ buddy_allocator :: proc(b: ^Buddy_Allocator) -> Allocator { } } +/* +Initialize the buddy allocator. + +This procedure initializes the buddy allocator `b` with a backing buffer `data` +and block alignment specified by `alignment`. +*/ buddy_allocator_init :: proc(b: ^Buddy_Allocator, data: []byte, alignment: uint, loc := #caller_location) { assert(data != nil) assert(is_power_of_two(uintptr(len(data))), "Size of the backing buffer must be power of two", loc) @@ -1981,6 +2118,9 @@ buddy_allocator_init :: proc(b: ^Buddy_Allocator, data: []byte, alignment: uint, b.alignment = alignment } +/* +Get required block size to fit in the allocation as well as the alignment padding. +*/ @(require_results) buddy_block_size_required :: proc(b: ^Buddy_Allocator, size: uint) -> uint { size := size @@ -1993,12 +2133,26 @@ buddy_block_size_required :: proc(b: ^Buddy_Allocator, size: uint) -> uint { return actual_size } +/* +Allocate memory from a buddy allocator. + +This procedure allocates `size` bytes of memory aligned on a boundary specified +by `alignment`. The allocated memory region is zero-initialized. This procedure +returns a pointer to the allocated memory region. +*/ @(require_results) buddy_allocator_alloc :: proc(b: ^Buddy_Allocator, size: uint) -> (rawptr, Allocator_Error) { bytes, err := buddy_allocator_alloc_bytes(b, size) return raw_data(bytes), err } +/* +Allocate memory from a buddy allocator. + +This procedure allocates `size` bytes of memory aligned on a boundary specified +by `alignment`. The allocated memory region is zero-initialized. This procedure +returns a slice of the allocated memory region. +*/ @(require_results) buddy_allocator_alloc_bytes :: proc(b: ^Buddy_Allocator, size: uint) -> ([]byte, Allocator_Error) { bytes, err := buddy_allocator_alloc_bytes_non_zeroed(b, size) @@ -2008,12 +2162,26 @@ buddy_allocator_alloc_bytes :: proc(b: ^Buddy_Allocator, size: uint) -> ([]byte, return bytes, err } +/* +Allocate non-initialized memory from a buddy allocator. + +This procedure allocates `size` bytes of memory aligned on a boundary specified +by `alignment`. The allocated memory region is not explicitly zero-initialized. +This procedure returns a pointer to the allocated memory region. +*/ @(require_results) buddy_allocator_alloc_non_zeroed :: proc(b: ^Buddy_Allocator, size: uint) -> (rawptr, Allocator_Error) { bytes, err := buddy_allocator_alloc_bytes_non_zeroed(b, size) return raw_data(bytes), err } +/* +Allocate non-initialized memory from a buddy allocator. + +This procedure allocates `size` bytes of memory aligned on a boundary specified +by `alignment`. The allocated memory region is not explicitly zero-initialized. +This procedure returns a slice of the allocated memory region. +*/ @(require_results) buddy_allocator_alloc_bytes_non_zeroed :: proc(b: ^Buddy_Allocator, size: uint) -> ([]byte, Allocator_Error) { if size != 0 { @@ -2034,6 +2202,14 @@ buddy_allocator_alloc_bytes_non_zeroed :: proc(b: ^Buddy_Allocator, size: uint) return nil, nil } +/* +Free memory to the buddy allocator. + +This procedure frees the memory region allocated at pointer `ptr`. + +If `ptr` is not the latest allocation and is not a leaked allocation, this +operation is a no-op. +*/ buddy_allocator_free :: proc(b: ^Buddy_Allocator, ptr: rawptr) -> Allocator_Error { if ptr != nil { if !(b.head <= ptr && ptr <= b.tail) { @@ -2046,6 +2222,9 @@ buddy_allocator_free :: proc(b: ^Buddy_Allocator, ptr: rawptr) -> Allocator_Erro return nil } +/* +Free all memory to the buddy allocator. +*/ buddy_allocator_free_all :: proc(b: ^Buddy_Allocator) { alignment := b.alignment head := ([^]byte)(b.head) diff --git a/core/mem/doc.odin b/core/mem/doc.odin index 5e8bcce6a..98755d797 100644 --- a/core/mem/doc.odin +++ b/core/mem/doc.odin @@ -48,6 +48,11 @@ Operations such as `new`, `free` and `delete` by default will use happens all called procedures will inherit the new context and use the same allocator. +We will define one concept to simplify the description of some allocator-related +procedures, which is ownership. If the memory was allocated via a specific +allocator, that allocator is said to be the *owner* of that memory region. To +note, unlike Rust, in Odin the memory ownership model is not strict. + ## Alignment An address is said to be *aligned to `N` bytes*, if the addresses's numeric From 3ed2ab6e2c18081d80961168a57155e6f31ac573 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sat, 14 Sep 2024 10:18:51 +1100 Subject: [PATCH 55/72] [mem]: Adjust the docs for calc_padding_with_header --- core/mem/alloc.odin | 3 +-- core/mem/mem.odin | 22 +++++++++++++++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/core/mem/alloc.odin b/core/mem/alloc.odin index 5f65e9ebc..fac58daaf 100644 --- a/core/mem/alloc.odin +++ b/core/mem/alloc.odin @@ -1019,8 +1019,7 @@ Default resize procedure. When allocator does not support resize operation, but supports `.Alloc_Non_Zeroed` and `.Free`, this procedure is used to implement allocator's -default behavior on -resize. +default behavior on resize. Unlike `default_resize_align` no new memory is being explicitly zero-initialized. diff --git a/core/mem/mem.odin b/core/mem/mem.odin index b57b18ffc..67ed56c39 100644 --- a/core/mem/mem.odin +++ b/core/mem/mem.odin @@ -644,10 +644,26 @@ align_formula :: proc "contextless" (size, align: int) -> int { } /* -Calculate the padding after the pointer with a header. +Calculate the padding for header preceding aligned data. -This procedure returns the next address, following `ptr` and `header_size` -bytes of space that is aligned to a boundary specified by `align`. +This procedure returns the padding, following the specified pointer `ptr` that +will be able to fit in a header of the size `header_size`, immediately +preceding the memory region, aligned on a boundary specified by `align`. See +the following diagram for a visual representation. + + header size + |<------>| + +---+--------+------------- - - - + | HEADER | DATA... + +---+--------+------------- - - - + ^ ^ + |<---------->| + | padding | + ptr aligned ptr + +The function takes in `ptr` and `header_size`, as well as the required +alignment for `DATA`. The return value of the function is the padding between +`ptr` and `aligned_ptr` that will be able to fit the header. */ @(require_results) calc_padding_with_header :: proc "contextless" (ptr: uintptr, align: uintptr, header_size: int) -> int { From 016d1a84d4e09ea06491d9e5a4661e313906e3aa Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sat, 14 Sep 2024 10:46:35 +1100 Subject: [PATCH 56/72] [mem]: Document mutex, rollback stack and tracking allocators --- core/mem/mutex_allocator.odin | 15 +++ core/mem/raw.odin | 46 ++++++--- core/mem/rollback_stack_allocator.odin | 135 ++++++++++++++----------- core/mem/tracking_allocator.odin | 114 ++++++++++++++------- 4 files changed, 197 insertions(+), 113 deletions(-) diff --git a/core/mem/mutex_allocator.odin b/core/mem/mutex_allocator.odin index 1cccc7dac..d2c527fdb 100644 --- a/core/mem/mutex_allocator.odin +++ b/core/mem/mutex_allocator.odin @@ -3,16 +3,31 @@ package mem import "core:sync" +/* +The data for mutex allocator. +*/ Mutex_Allocator :: struct { backing: Allocator, mutex: sync.Mutex, } +/* +Initialize the mutex allocator. + +This procedure initializes the mutex allocator using `backin_allocator` as the +allocator that will be used to pass all allocation requests through. +*/ mutex_allocator_init :: proc(m: ^Mutex_Allocator, backing_allocator: Allocator) { m.backing = backing_allocator m.mutex = {} } +/* +Mutex allocator. + +The mutex allocator is a wrapper for allocators that is used to serialize all +allocator requests across multiple threads. +*/ @(require_results) mutex_allocator :: proc(m: ^Mutex_Allocator) -> Allocator { return Allocator{ diff --git a/core/mem/raw.odin b/core/mem/raw.odin index ab1148cea..41c91555e 100644 --- a/core/mem/raw.odin +++ b/core/mem/raw.odin @@ -4,68 +4,82 @@ import "base:builtin" import "base:runtime" /* -Mamory layout of the `any` type. +Memory layout of the `any` type. */ Raw_Any :: runtime.Raw_Any /* -Mamory layout of the `string` type. +Memory layout of the `string` type. */ Raw_String :: runtime.Raw_String + /* -Mamory layout of the `cstring` type. +Memory layout of the `cstring` type. */ Raw_Cstring :: runtime.Raw_Cstring + /* -Mamory layout of `[]T` types. +Memory layout of `[]T` types. */ Raw_Slice :: runtime.Raw_Slice + /* -Mamory layout of `[dynamic]T` types. +Memory layout of `[dynamic]T` types. */ Raw_Dynamic_Array :: runtime.Raw_Dynamic_Array + /* -Mamory layout of `map[K]V` types. +Memory layout of `map[K]V` types. */ Raw_Map :: runtime.Raw_Map + /* -Mamory layout of `#soa []T` types. +Memory layout of `#soa []T` types. */ Raw_Soa_Pointer :: runtime.Raw_Soa_Pointer + /* -Mamory layout of the `complex32` type. +Memory layout of the `complex32` type. */ Raw_Complex32 :: runtime.Raw_Complex32 + /* -Mamory layout of the `complex64` type. +Memory layout of the `complex64` type. */ Raw_Complex64 :: runtime.Raw_Complex64 + /* -Mamory layout of the `complex128` type. +Memory layout of the `complex128` type. */ Raw_Complex128 :: runtime.Raw_Complex128 + /* -Mamory layout of the `quaternion64` type. +Memory layout of the `quaternion64` type. */ Raw_Quaternion64 :: runtime.Raw_Quaternion64 + /* -Mamory layout of the `quaternion128` type. +Memory layout of the `quaternion128` type. */ Raw_Quaternion128 :: runtime.Raw_Quaternion128 + /* -Mamory layout of the `quaternion256` type. +Memory layout of the `quaternion256` type. */ Raw_Quaternion256 :: runtime.Raw_Quaternion256 + /* -Mamory layout of the `quaternion64` type. +Memory layout of the `quaternion64` type. */ Raw_Quaternion64_Vector_Scalar :: runtime.Raw_Quaternion64_Vector_Scalar + /* -Mamory layout of the `quaternion128` type. +Memory layout of the `quaternion128` type. */ Raw_Quaternion128_Vector_Scalar :: runtime.Raw_Quaternion128_Vector_Scalar + /* -Mamory layout of the `quaternion256` type. +Memory layout of the `quaternion256` type. */ Raw_Quaternion256_Vector_Scalar :: runtime.Raw_Quaternion256_Vector_Scalar diff --git a/core/mem/rollback_stack_allocator.odin b/core/mem/rollback_stack_allocator.odin index 761435552..61ec73546 100644 --- a/core/mem/rollback_stack_allocator.odin +++ b/core/mem/rollback_stack_allocator.odin @@ -1,39 +1,15 @@ package mem -/* -The Rollback Stack Allocator was designed for the test runner to be fast, -able to grow, and respect the Tracking Allocator's requirement for -individual frees. It is not overly concerned with fragmentation, however. - -It has support for expansion when configured with a block allocator and -limited support for out-of-order frees. - -Allocation has constant-time best and usual case performance. -At worst, it is linear according to the number of memory blocks. - -Allocation follows a first-fit strategy when there are multiple memory -blocks. - -Freeing has constant-time best and usual case performance. -At worst, it is linear according to the number of memory blocks and number -of freed items preceding the last item in a block. - -Resizing has constant-time performance, if it's the last item in a block, or -the new size is smaller. Naturally, this becomes linear-time if there are -multiple blocks to search for the pointer's owning block. Otherwise, the -allocator defaults to a combined alloc & free operation internally. - -Out-of-order freeing is accomplished by collapsing a run of freed items -from the last allocation backwards. - -Each allocation has an overhead of 8 bytes and any extra bytes to satisfy -the requested alignment. -*/ import "base:runtime" +/* +Rollback stack default block size. +*/ ROLLBACK_STACK_DEFAULT_BLOCK_SIZE :: 4 * Megabyte /* +Rollback stack max head block size. + This limitation is due to the size of `prev_ptr`, but it is only for the head block; any allocation in excess of the allocator's `block_size` is valid, so long as the block allocator can handle it. @@ -43,12 +19,18 @@ within is freed; they are immediately returned to the block allocator. */ ROLLBACK_STACK_MAX_HEAD_BLOCK_SIZE :: 2 * Gigabyte +/* +Allocation header of the rollback stack allocator. +*/ Rollback_Stack_Header :: bit_field u64 { prev_offset: uintptr | 32, is_free: bool | 1, prev_ptr: uintptr | 31, } +/* +Block header of the rollback stack allocator. +*/ Rollback_Stack_Block :: struct { next_block: ^Rollback_Stack_Block, last_alloc: rawptr, @@ -56,6 +38,9 @@ Rollback_Stack_Block :: struct { buffer: []byte, } +/* +Rollback stack allocator data. +*/ Rollback_Stack :: struct { head: ^Rollback_Stack_Block, block_size: int, @@ -111,6 +96,9 @@ rb_rollback_block :: proc(block: ^Rollback_Stack_Block, header: ^Rollback_Stack_ } } +/* +Free memory to a rollback stack allocator. +*/ @(private="file", require_results) rb_free :: proc(stack: ^Rollback_Stack, ptr: rawptr) -> Allocator_Error { parent, block, header := rb_find_ptr(stack, ptr) or_return @@ -129,6 +117,9 @@ rb_free :: proc(stack: ^Rollback_Stack, ptr: rawptr) -> Allocator_Error { return nil } +/* +Free all memory owned by the rollback stack allocator. +*/ @(private="file") rb_free_all :: proc(stack: ^Rollback_Stack) { for block := stack.head.next_block; block != nil; /**/ { @@ -142,14 +133,16 @@ rb_free_all :: proc(stack: ^Rollback_Stack) { stack.head.offset = 0 } +/* +Resize an allocation made on a rollback stack allocator. +*/ @(private="file", require_results) -rb_resize :: proc(stack: ^Rollback_Stack, ptr: rawptr, old_size, size, alignment: int) -> (result: []byte, err: Allocator_Error) { +rb_resize_non_zeroed :: proc(stack: ^Rollback_Stack, ptr: rawptr, old_size, size, alignment: int) -> (result: []byte, err: Allocator_Error) { 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. @@ -160,27 +153,26 @@ rb_resize :: proc(stack: ^Rollback_Stack, ptr: rawptr, old_size, size, alignment } } } - - result = rb_alloc(stack, size, alignment) or_return + result = rb_alloc_non_zeroed(stack, size, alignment) or_return runtime.mem_copy_non_overlapping(raw_data(result), ptr, old_size) err = rb_free(stack, ptr) - return } +/* +Allocate memory using the rollback stack allocator. +*/ @(private="file", require_results) -rb_alloc :: proc(stack: ^Rollback_Stack, size, alignment: int) -> (result: []byte, err: Allocator_Error) { +rb_alloc_non_zeroed :: proc(stack: ^Rollback_Stack, size, alignment: int) -> (result: []byte, err: Allocator_Error) { parent: ^Rollback_Stack_Block for block := stack.head; /**/; block = block.next_block { when !ODIN_DISABLE_ASSERT { allocated_new_block: bool } - if block == nil { if stack.block_allocator.procedure == nil { return nil, .Out_Of_Memory } - minimum_size_required := size_of(Rollback_Stack_Header) + size + alignment - 1 new_block_size := max(minimum_size_required, stack.block_size) block = rb_make_block(new_block_size, stack.block_allocator) or_return @@ -189,10 +181,8 @@ rb_alloc :: proc(stack: ^Rollback_Stack, size, alignment: int) -> (result: []byt allocated_new_block = true } } - start := raw_data(block.buffer)[block.offset:] padding := cast(uintptr)calc_padding_with_header(cast(uintptr)start, cast(uintptr)alignment, size_of(Rollback_Stack_Header)) - if block.offset + padding + cast(uintptr)size > cast(uintptr)len(block.buffer) { when !ODIN_DISABLE_ASSERT { if allocated_new_block { @@ -202,54 +192,50 @@ rb_alloc :: proc(stack: ^Rollback_Stack, size, alignment: int) -> (result: []byt parent = block continue } - header := cast(^Rollback_Stack_Header)(start[padding - size_of(Rollback_Stack_Header):]) ptr := start[padding:] - header^ = { prev_offset = block.offset, prev_ptr = uintptr(0) if block.last_alloc == nil else cast(uintptr)block.last_alloc - cast(uintptr)raw_data(block.buffer), is_free = false, } - block.last_alloc = ptr block.offset += padding + cast(uintptr)size - if len(block.buffer) > stack.block_size { // This block exceeds the allocator's standard block size and is considered a singleton. // Prevent any further allocations on it. block.offset = cast(uintptr)len(block.buffer) } - #no_bounds_check return ptr[:size], nil } - return nil, .Out_Of_Memory } @(private="file", require_results) 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 - block = cast(^Rollback_Stack_Block)raw_data(buffer) #no_bounds_check block.buffer = buffer[size_of(Rollback_Stack_Block):] return } - +/* +Initialize the rollback stack allocator using a fixed backing buffer. +*/ rollback_stack_init_buffered :: proc(stack: ^Rollback_Stack, buffer: []byte, location := #caller_location) { MIN_SIZE :: size_of(Rollback_Stack_Block) + size_of(Rollback_Stack_Header) + size_of(rawptr) assert(len(buffer) >= MIN_SIZE, "User-provided buffer to Rollback Stack Allocator is too small.", location) - block := cast(^Rollback_Stack_Block)raw_data(buffer) block^ = {} #no_bounds_check block.buffer = buffer[size_of(Rollback_Stack_Block):] - stack^ = {} stack.head = block stack.block_size = len(block.buffer) } +/* +Initialize the rollback stack alocator using a backing block allocator. +*/ rollback_stack_init_dynamic :: proc( stack: ^Rollback_Stack, block_size : int = ROLLBACK_STACK_DEFAULT_BLOCK_SIZE, @@ -262,22 +248,25 @@ rollback_stack_init_dynamic :: proc( // size is insufficient; check only on platforms with big enough ints. assert(block_size <= ROLLBACK_STACK_MAX_HEAD_BLOCK_SIZE, "Rollback Stack Allocators cannot support head blocks larger than 2 gigabytes.", location) } - block := rb_make_block(block_size, block_allocator) or_return - stack^ = {} stack.head = block stack.block_size = block_size stack.block_allocator = block_allocator - return nil } +/* +Initialize the rollback stack. +*/ rollback_stack_init :: proc { rollback_stack_init_buffered, rollback_stack_init_dynamic, } +/* +Destroy a rollback stack. +*/ rollback_stack_destroy :: proc(stack: ^Rollback_Stack) { if stack.block_allocator.procedure != nil { rb_free_all(stack) @@ -286,6 +275,37 @@ rollback_stack_destroy :: proc(stack: ^Rollback_Stack) { stack^ = {} } +/* +Rollback stack allocator. + +The Rollback Stack Allocator was designed for the test runner to be fast, +able to grow, and respect the Tracking Allocator's requirement for +individual frees. It is not overly concerned with fragmentation, however. + +It has support for expansion when configured with a block allocator and +limited support for out-of-order frees. + +Allocation has constant-time best and usual case performance. +At worst, it is linear according to the number of memory blocks. + +Allocation follows a first-fit strategy when there are multiple memory +blocks. + +Freeing has constant-time best and usual case performance. +At worst, it is linear according to the number of memory blocks and number +of freed items preceding the last item in a block. + +Resizing has constant-time performance, if it's the last item in a block, or +the new size is smaller. Naturally, this becomes linear-time if there are +multiple blocks to search for the pointer's owning block. Otherwise, the +allocator defaults to a combined alloc & free operation internally. + +Out-of-order freeing is accomplished by collapsing a run of freed items +from the last allocation backwards. + +Each allocation has an overhead of 8 bytes and any extra bytes to satisfy +the requested alignment. +*/ @(require_results) rollback_stack_allocator :: proc(stack: ^Rollback_Stack) -> Allocator { return Allocator { @@ -309,38 +329,31 @@ rollback_stack_allocator_proc :: proc( case .Alloc, .Alloc_Non_Zeroed: assert(size >= 0, "Size must be positive or zero.", location) assert(is_power_of_two(cast(uintptr)alignment), "Alignment must be a power of two.", location) - result = rb_alloc(stack, size, alignment) or_return - + result = rb_alloc_non_zeroed(stack, size, alignment) or_return if mode == .Alloc { zero_slice(result) } - case .Free: err = rb_free(stack, old_memory) case .Free_All: rb_free_all(stack) - case .Resize, .Resize_Non_Zeroed: assert(size >= 0, "Size must be positive or zero.", location) assert(old_size >= 0, "Old size must be positive or zero.", location) assert(is_power_of_two(cast(uintptr)alignment), "Alignment must be a power of two.", location) - result = rb_resize(stack, old_memory, old_size, size, alignment) or_return - + result = rb_resize_non_zeroed(stack, old_memory, old_size, size, alignment) or_return #no_bounds_check if mode == .Resize && size > old_size { zero_slice(result[old_size:]) } - case .Query_Features: set := (^Allocator_Mode_Set)(old_memory) if set != nil { set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Resize_Non_Zeroed} } return nil, nil - case .Query_Info: return nil, .Mode_Not_Implemented } - return } diff --git a/core/mem/tracking_allocator.odin b/core/mem/tracking_allocator.odin index e75844130..e436fcb6d 100644 --- a/core/mem/tracking_allocator.odin +++ b/core/mem/tracking_allocator.odin @@ -4,50 +4,38 @@ package mem import "base:runtime" import "core:sync" +/* +Allocation entry for the tracking allocator. + +This structure stores the data related to an allocation. +*/ Tracking_Allocator_Entry :: struct { - memory: rawptr, - size: int, + // Pointer to an allocated region. + memory: rawptr, + // Size of the allocated memory region. + size: int, + // Requested alignment. alignment: int, - mode: Allocator_Mode, - err: Allocator_Error, + // Mode of the operation. + mode: Allocator_Mode, + // Error. + err: Allocator_Error, + // Location of the allocation. location: runtime.Source_Code_Location, } +/* +Bad free entry for a tracking allocator. +*/ Tracking_Allocator_Bad_Free_Entry :: struct { - memory: rawptr, + // Pointer, on which free operation was called. + memory: rawptr, + // The source location of where the operation was called. location: runtime.Source_Code_Location, } /* -An example of how to use the `Tracking_Allocator` to track subsequent allocations -in your program and report leaks and bad frees: - -Example: - - package foo - - import "core:mem" - import "core:fmt" - - _main :: proc() { - // do stuff - } - - main :: proc() { - track: mem.Tracking_Allocator - mem.tracking_allocator_init(&track, context.allocator) - defer mem.tracking_allocator_destroy(&track) - context.allocator = mem.tracking_allocator(&track) - - _main() - - for _, leak in track.allocation_map { - fmt.printf("%v leaked %m\n", leak.location, leak.size) - } - for bad_free in track.bad_free_array { - fmt.printf("%v allocation %p was freed badly\n", bad_free.location, bad_free.memory) - } - } +Tracking allocator data. */ Tracking_Allocator :: struct { backing: Allocator, @@ -63,6 +51,13 @@ Tracking_Allocator :: struct { current_memory_allocated: i64, } +/* +Initialize the tracking allocator. + +This procedure initializes the tracking allocator `t` with a backing allocator +specified with `backing_allocator`. The `internals_allocator` will used to +allocate the tracked data. +*/ tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Allocator, internals_allocator := context.allocator) { t.backing = backing_allocator t.allocation_map.allocator = internals_allocator @@ -72,12 +67,22 @@ tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Alloc } } +/* +Destroy the tracking allocator. +*/ tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) { delete(t.allocation_map) delete(t.bad_free_array) } -// Clear only the current allocation data while keeping the totals intact. +/* +Clear the tracking allocator. + +This procedure clears the tracked data from a tracking allocator. + +**Note**: This procedure clears only the current allocation data while keeping +the totals intact. +*/ tracking_allocator_clear :: proc(t: ^Tracking_Allocator) { sync.mutex_lock(&t.mutex) clear(&t.allocation_map) @@ -86,7 +91,11 @@ tracking_allocator_clear :: proc(t: ^Tracking_Allocator) { sync.mutex_unlock(&t.mutex) } -// Reset all of a Tracking Allocator's allocation data back to zero. +/* +Reset the tracking allocator. + +Reset all of a Tracking Allocator's allocation data back to zero. +*/ tracking_allocator_reset :: proc(t: ^Tracking_Allocator) { sync.mutex_lock(&t.mutex) clear(&t.allocation_map) @@ -100,6 +109,39 @@ tracking_allocator_reset :: proc(t: ^Tracking_Allocator) { sync.mutex_unlock(&t.mutex) } +/* +Tracking allocator. + +The tracking allocator is an allocator wrapper that tracks memory allocations. +This allocator stores all the allocations in a map. Whenever a pointer that's +not inside of the map is freed, the `bad_free_array` entry is added. + +An example of how to use the `Tracking_Allocator` to track subsequent allocations +in your program and report leaks and bad frees: + +Example: + + package foo + + import "core:mem" + import "core:fmt" + + main :: proc() { + track: mem.Tracking_Allocator + mem.tracking_allocator_init(&track, context.allocator) + defer mem.tracking_allocator_destroy(&track) + context.allocator = mem.tracking_allocator(&track) + + do_stuff() + + for _, leak in track.allocation_map { + fmt.printf("%v leaked %m\n", leak.location, leak.size) + } + for bad_free in track.bad_free_array { + fmt.printf("%v allocation %p was freed badly\n", bad_free.location, bad_free.memory) + } + } +*/ @(require_results) tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator { return Allocator{ From 466e29bb38040ce8737e125a8a13c97cd4e74252 Mon Sep 17 00:00:00 2001 From: flysand7 Date: Sat, 14 Sep 2024 12:13:56 +1100 Subject: [PATCH 57/72] [mem]: Rollback allocator API consistency --- core/mem/rollback_stack_allocator.odin | 205 ++++++++++++++++++++----- 1 file changed, 163 insertions(+), 42 deletions(-) diff --git a/core/mem/rollback_stack_allocator.odin b/core/mem/rollback_stack_allocator.odin index 61ec73546..43ef10fe9 100644 --- a/core/mem/rollback_stack_allocator.odin +++ b/core/mem/rollback_stack_allocator.odin @@ -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) -rb_resize_non_zeroed :: proc(stack: ^Rollback_Stack, ptr: rawptr, old_size, size, alignment: int) -> (result: []byte, err: Allocator_Error) { - 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 (cast([^]byte)ptr)[:size], nil - } - } +@(require_results) +rb_alloc :: 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) + if bytes != nil { + zero_slice(bytes) } - result = rb_alloc_non_zeroed(stack, size, alignment) or_return - runtime.mem_copy_non_overlapping(raw_data(result), ptr, old_size) - err = rb_free(stack, ptr) - return + return raw_data(bytes), err } /* Allocate memory using the rollback stack allocator. */ -@(private="file", require_results) -rb_alloc_non_zeroed :: proc(stack: ^Rollback_Stack, size, alignment: int) -> (result: []byte, err: Allocator_Error) { +@(require_results) +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 for block := stack.head; /**/; block = block.next_block { 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 } +/* +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) 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 @@ -321,31 +450,23 @@ rollback_stack_allocator_proc :: proc( size, alignment: int, old_memory: rawptr, old_size: int, - location := #caller_location, + loc := #caller_location, ) -> (result: []byte, err: Allocator_Error) { stack := cast(^Rollback_Stack)allocator_data - switch mode { - case .Alloc, .Alloc_Non_Zeroed: - assert(size >= 0, "Size must be positive or zero.", location) - assert(is_power_of_two(cast(uintptr)alignment), "Alignment must be a power of two.", location) - result = rb_alloc_non_zeroed(stack, size, alignment) or_return - if mode == .Alloc { - zero_slice(result) - } + case .Alloc: + return rb_alloc_bytes(stack, size, alignment, loc) + case .Alloc_Non_Zeroed: + return rb_alloc_bytes_non_zeroed(stack, size, alignment, loc) case .Free: - err = rb_free(stack, old_memory) - + return nil, rb_free(stack, old_memory) case .Free_All: rb_free_all(stack) - case .Resize, .Resize_Non_Zeroed: - assert(size >= 0, "Size must be positive or zero.", location) - assert(old_size >= 0, "Old size must be positive or zero.", location) - assert(is_power_of_two(cast(uintptr)alignment), "Alignment must be a power of two.", location) - result = rb_resize_non_zeroed(stack, old_memory, old_size, size, alignment) or_return - #no_bounds_check if mode == .Resize && size > old_size { - zero_slice(result[old_size:]) - } + return nil, nil + case .Resize: + return rb_resize_bytes(stack, byte_slice(old_memory, old_size), size, alignment, loc) + case .Resize_Non_Zeroed: + return rb_resize_bytes_non_zeroed(stack, byte_slice(old_memory, old_size), size, alignment, loc) case .Query_Features: set := (^Allocator_Mode_Set)(old_memory) if set != nil { From 4f3f256375e3ae7e9ac0420fa41a7e6cead4ad72 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Sat, 14 Sep 2024 15:52:37 +0200 Subject: [PATCH 58/72] improve bit field debug info --- src/llvm_backend_debug.cpp | 80 +++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 36 deletions(-) diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp index 68e1efc1c..bb6a1ba4f 100644 --- a/src/llvm_backend_debug.cpp +++ b/src/llvm_backend_debug.cpp @@ -552,6 +552,48 @@ gb_internal LLVMMetadataRef lb_debug_bitset(lbModule *m, Type *type, String name return final_decl; } +gb_internal LLVMMetadataRef lb_debug_bitfield(lbModule *m, Type *type, String name, LLVMMetadataRef scope, LLVMMetadataRef file, unsigned line) { + Type *bt = base_type(type); + GB_ASSERT(bt->kind == Type_BitField); + + lb_debug_file_line(m, bt->BitField.node, &file, &line); + + u64 size_in_bits = 8*type_size_of(bt); + u32 align_in_bits = 8*cast(u32)type_align_of(bt); + + unsigned element_count = cast(unsigned)bt->BitField.fields.count; + LLVMMetadataRef *elements = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, element_count); + + u64 offset_in_bits = 0; + for (unsigned i = 0; i < element_count; i++) { + Entity *f = bt->BitField.fields[i]; + u8 bit_size = bt->BitField.bit_sizes[i]; + GB_ASSERT(f->kind == Entity_Variable); + String name = f->token.string; + elements[i] = LLVMDIBuilderCreateBitFieldMemberType(m->debug_builder, scope, cast(char const *)name.text, name.len, file, line, + bit_size, offset_in_bits, offset_in_bits, + LLVMDIFlagZero, lb_debug_type(m, f->type) + ); + + offset_in_bits += bit_size; + } + + LLVMMetadataRef final_decl = LLVMDIBuilderCreateStructType( + m->debug_builder, scope, + cast(char const *)name.text, cast(size_t)name.len, + file, line, + size_in_bits, align_in_bits, + LLVMDIFlagZero, + nullptr, + elements, element_count, + 0, + nullptr, + "", 0 + ); + lb_set_llvm_metadata(m, type, final_decl); + return final_decl; +} + gb_internal LLVMMetadataRef lb_debug_enum(lbModule *m, Type *type, String name, LLVMMetadataRef scope, LLVMMetadataRef file, unsigned line) { Type *bt = base_type(type); GB_ASSERT(bt->kind == Type_Enum); @@ -816,6 +858,7 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) { case Type_Union: return lb_debug_union( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0); case Type_BitSet: return lb_debug_bitset( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0); case Type_Enum: return lb_debug_enum( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0); + case Type_BitField: return lb_debug_bitfield( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0); case Type_Tuple: if (type->Tuple.variables.count == 1) { @@ -901,42 +944,6 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) { lb_debug_type(m, type->Matrix.elem), subscripts, gb_count_of(subscripts)); } - - case Type_BitField: { - LLVMMetadataRef parent_scope = nullptr; - LLVMMetadataRef scope = nullptr; - LLVMMetadataRef file = nullptr; - unsigned line = 0; - u64 size_in_bits = 8*cast(u64)type_size_of(type); - u32 align_in_bits = 8*cast(u32)type_align_of(type); - LLVMDIFlags flags = LLVMDIFlagZero; - - unsigned element_count = cast(unsigned)type->BitField.fields.count; - LLVMMetadataRef *elements = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, element_count); - - u64 offset_in_bits = 0; - for (unsigned i = 0; i < element_count; i++) { - Entity *f = type->BitField.fields[i]; - u8 bit_size = type->BitField.bit_sizes[i]; - GB_ASSERT(f->kind == Entity_Variable); - String name = f->token.string; - unsigned field_line = 0; - LLVMDIFlags field_flags = LLVMDIFlagZero; - elements[i] = LLVMDIBuilderCreateBitFieldMemberType(m->debug_builder, scope, cast(char const *)name.text, name.len, file, field_line, - bit_size, offset_in_bits, offset_in_bits, - field_flags, lb_debug_type(m, f->type) - ); - - offset_in_bits += bit_size; - } - - - return LLVMDIBuilderCreateStructType(m->debug_builder, parent_scope, "", 0, file, line, - size_in_bits, align_in_bits, flags, - nullptr, elements, element_count, 0, nullptr, - "", 0 - ); - } } GB_PANIC("Invalid type %s", type_to_string(type)); @@ -1022,6 +1029,7 @@ gb_internal LLVMMetadataRef lb_debug_type(lbModule *m, Type *type) { case Type_Union: return lb_debug_union(m, type, name, scope, file, line); case Type_BitSet: return lb_debug_bitset(m, type, name, scope, file, line); case Type_Enum: return lb_debug_enum(m, type, name, scope, file, line); + case Type_BitField: return lb_debug_bitfield(m, type, name, scope, file, line); } } From 603efa860a5631f1708f6761d753146b6d47b4ba Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Sat, 14 Sep 2024 21:43:25 +0200 Subject: [PATCH 59/72] add '#caller_expression' --- base/runtime/core_builtin.odin | 4 +-- core/odin/parser/parser.odin | 10 ++++++ core/testing/testing.odin | 12 ++++--- src/check_builtin.cpp | 16 +++++++++ src/check_expr.cpp | 7 +++- src/check_type.cpp | 32 ++++++++++++++++++ src/entity.cpp | 1 + src/llvm_backend.hpp | 2 +- src/llvm_backend_proc.cpp | 62 +++++++++++++++++++++++++++++++--- 9 files changed, 133 insertions(+), 13 deletions(-) diff --git a/base/runtime/core_builtin.odin b/base/runtime/core_builtin.odin index 8157afe09..67d249d11 100644 --- a/base/runtime/core_builtin.odin +++ b/base/runtime/core_builtin.odin @@ -913,7 +913,7 @@ card :: proc "contextless" (s: $S/bit_set[$E; $U]) -> int { @builtin @(disabled=ODIN_DISABLE_ASSERT) -assert :: proc(condition: bool, message := "", loc := #caller_location) { +assert :: proc(condition: bool, message := #caller_expression(condition), loc := #caller_location) { if !condition { // NOTE(bill): This is wrapped in a procedure call // to improve performance to make the CPU not @@ -952,7 +952,7 @@ unimplemented :: proc(message := "", loc := #caller_location) -> ! { @builtin @(disabled=ODIN_DISABLE_ASSERT) -assert_contextless :: proc "contextless" (condition: bool, message := "", loc := #caller_location) { +assert_contextless :: proc "contextless" (condition: bool, message := #caller_expression(condition), loc := #caller_location) { if !condition { // NOTE(bill): This is wrapped in a procedure call // to improve performance to make the CPU not diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index aab59c29d..6f42c17db 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -2277,6 +2277,16 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { bd.name = name.text return bd + case "caller_expression": + bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name)) + bd.tok = tok + bd.name = name.text + + if peek_token_kind(p, .Open_Paren) { + return parse_call_expr(p, bd) + } + return bd + case "location", "exists", "load", "load_directory", "load_hash", "hash", "assert", "panic", "defined", "config": bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name)) bd.tok = tok diff --git a/core/testing/testing.odin b/core/testing/testing.odin index d5e7c6830..09bf6dc0e 100644 --- a/core/testing/testing.odin +++ b/core/testing/testing.odin @@ -105,9 +105,13 @@ cleanup :: proc(t: ^T, procedure: proc(rawptr), user_data: rawptr) { append(&t.cleanups, Internal_Cleanup{procedure, user_data, context}) } -expect :: proc(t: ^T, ok: bool, msg: string = "", loc := #caller_location) -> bool { +expect :: proc(t: ^T, ok: bool, msg := "", expr := #caller_expression(ok), loc := #caller_location) -> bool { if !ok { - log.error(msg, location=loc) + if msg == "" { + log.errorf("expected %v to be true", expr, location=loc) + } else { + log.error(msg, location=loc) + } } return ok } @@ -119,10 +123,10 @@ expectf :: proc(t: ^T, ok: bool, format: string, args: ..any, loc := #caller_loc return ok } -expect_value :: proc(t: ^T, value, expected: $T, loc := #caller_location) -> bool where intrinsics.type_is_comparable(T) { +expect_value :: proc(t: ^T, value, expected: $T, loc := #caller_location, value_expr := #caller_expression(value)) -> bool where intrinsics.type_is_comparable(T) { ok := value == expected || reflect.is_nil(value) && reflect.is_nil(expected) if !ok { - log.errorf("expected %v, got %v", expected, value, location=loc) + log.errorf("expected %v to be %v, got %v", value_expr, expected, value, location=loc) } return ok } diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 888aa074d..909eb668e 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1632,6 +1632,22 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o operand->type = t_source_code_location; operand->mode = Addressing_Value; + } else if (name == "caller_expression") { + if (ce->args.count > 1) { + error(ce->args[0], "'#caller_expression' expects either 0 or 1 arguments, got %td", ce->args.count); + } + if (ce->args.count > 0) { + Ast *arg = ce->args[0]; + Operand o = {}; + Entity *e = check_ident(c, &o, arg, nullptr, nullptr, true); + if (e == nullptr || (e->flags & EntityFlag_Param) == 0) { + error(ce->args[0], "'#caller_expression' expected a valid earlier parameter name"); + } + arg->Ident.entity = e; + } + + operand->type = t_string; + operand->mode = Addressing_Value; } else if (name == "exists") { if (ce->args.count != 1) { error(ce->close, "'#exists' expects 1 argument, got %td", ce->args.count); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 7f82fb58a..6776094bf 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7807,7 +7807,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c name == "load" || name == "load_directory" || name == "load_hash" || - name == "hash" + name == "hash" || + name == "caller_expression" ) { operand->mode = Addressing_Builtin; operand->builtin_id = BuiltinProc_DIRECTIVE; @@ -8725,6 +8726,10 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A error(node, "#caller_location may only be used as a default argument parameter"); o->type = t_source_code_location; o->mode = Addressing_Value; + } else if (name == "caller_expression") { + error(node, "#caller_expression may only be used as a default argument parameter"); + o->type = t_string; + o->mode = Addressing_Value; } else { if (name == "location") { init_core_source_code_location(c->checker); diff --git a/src/check_type.cpp b/src/check_type.cpp index 3767f7666..f0e0acb9b 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1605,6 +1605,25 @@ gb_internal bool is_expr_from_a_parameter(CheckerContext *ctx, Ast *expr) { return false; } +gb_internal bool is_caller_expression(Ast *expr) { + if (expr->kind == Ast_BasicDirective && expr->BasicDirective.name.string == "caller_expression") { + return true; + } + + Ast *call = unparen_expr(expr); + if (call->kind != Ast_CallExpr) { + return false; + } + + ast_node(ce, CallExpr, call); + if (ce->proc->kind != Ast_BasicDirective) { + return false; + } + + ast_node(bd, BasicDirective, ce->proc); + String name = bd->name.string; + return name == "caller_expression"; +} gb_internal ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type **out_type_, Ast *expr, bool allow_caller_location) { ParameterValue param_value = {}; @@ -1626,7 +1645,19 @@ gb_internal ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_ if (in_type) { check_assignment(ctx, &o, in_type, str_lit("parameter value")); } + } else if (is_caller_expression(expr)) { + if (expr->kind != Ast_BasicDirective) { + check_builtin_procedure_directive(ctx, &o, expr, t_string); + } + param_value.kind = ParameterValue_Expression; + o.type = t_string; + o.mode = Addressing_Value; + o.expr = expr; + + if (in_type) { + check_assignment(ctx, &o, in_type, str_lit("parameter value")); + } } else { if (in_type) { check_expr_with_type_hint(ctx, &o, expr, in_type); @@ -1858,6 +1889,7 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para case ParameterValue_Nil: break; case ParameterValue_Location: + case ParameterValue_Expression: case ParameterValue_Value: gbString str = type_to_string(type); error(params[i], "A default value for a parameter must not be a polymorphic constant type, got %s", str); diff --git a/src/entity.cpp b/src/entity.cpp index db6ffdd52..0c4a20df4 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -104,6 +104,7 @@ enum ParameterValueKind { ParameterValue_Constant, ParameterValue_Nil, ParameterValue_Location, + ParameterValue_Expression, ParameterValue_Value, }; diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp index 29d2ccfe6..68f95cb03 100644 --- a/src/llvm_backend.hpp +++ b/src/llvm_backend.hpp @@ -528,7 +528,7 @@ gb_internal lbAddr lb_store_range_stmt_val(lbProcedure *p, Ast *stmt_val, lbValu gb_internal lbValue lb_emit_source_code_location_const(lbProcedure *p, String const &procedure, TokenPos const &pos); gb_internal lbValue lb_const_source_code_location_const(lbModule *m, String const &procedure, TokenPos const &pos); -gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const ¶m_value, TokenPos const &pos); +gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const ¶m_value, TypeProc *procedure_type, Ast *call_expression); gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type); gb_internal lbValue lb_hasher_proc_for_type(lbModule *m, Type *type); diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index e850d3364..d84599eb0 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -699,7 +699,9 @@ gb_internal void lb_begin_procedure_body(lbProcedure *p) { } if (e->Variable.param_value.kind != ParameterValue_Invalid) { - lbValue c = lb_handle_param_value(p, e->type, e->Variable.param_value, e->token.pos); + GB_ASSERT(e->Variable.param_value.kind != ParameterValue_Location); + GB_ASSERT(e->Variable.param_value.kind != ParameterValue_Expression); + lbValue c = lb_handle_param_value(p, e->type, e->Variable.param_value, nullptr, nullptr); lb_addr_store(p, res, c); } @@ -3420,7 +3422,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu } -gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const ¶m_value, TokenPos const &pos) { +gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const ¶m_value, TypeProc *procedure_type, Ast* call_expression) { switch (param_value.kind) { case ParameterValue_Constant: if (is_type_constant_type(parameter_type)) { @@ -3446,8 +3448,60 @@ gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, if (p->entity != nullptr) { proc_name = p->entity->token.string; } + + ast_node(ce, CallExpr, call_expression); + TokenPos pos = ast_token(ce->proc).pos; + return lb_emit_source_code_location_as_global(p, proc_name, pos); } + case ParameterValue_Expression: + { + Ast *orig = param_value.original_ast_expr; + if (orig->kind == Ast_BasicDirective) { + gbString expr = expr_to_string(call_expression, temporary_allocator()); + return lb_const_string(p->module, make_string_c(expr)); + } + + isize param_idx = -1; + String param_str = {0}; + { + Ast *call = unparen_expr(orig); + GB_ASSERT(call->kind == Ast_CallExpr); + ast_node(ce, CallExpr, call); + GB_ASSERT(ce->proc->kind == Ast_BasicDirective); + GB_ASSERT(ce->args.count == 1); + Ast *target = ce->args[0]; + GB_ASSERT(target->kind == Ast_Ident); + String target_str = target->Ident.token.string; + + param_idx = lookup_procedure_parameter(procedure_type, target_str); + param_str = target_str; + } + GB_ASSERT(param_idx >= 0); + + + Ast *target_expr = nullptr; + ast_node(ce, CallExpr, call_expression); + + if (ce->split_args->positional.count > param_idx) { + target_expr = ce->split_args->positional[param_idx]; + } + + for_array(i, ce->split_args->named) { + Ast *arg = ce->split_args->named[i]; + ast_node(fv, FieldValue, arg); + GB_ASSERT(fv->field->kind == Ast_Ident); + String name = fv->field->Ident.token.string; + if (name == param_str) { + target_expr = fv->value; + break; + } + } + + gbString expr = expr_to_string(target_expr, temporary_allocator()); + return lb_const_string(p->module, make_string_c(expr)); + } + case ParameterValue_Value: return lb_build_expr(p, param_value.ast_value); } @@ -3739,8 +3793,6 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { } } - TokenPos pos = ast_token(ce->proc).pos; - if (pt->params != nullptr) { isize min_count = pt->params->Tuple.variables.count; @@ -3764,7 +3816,7 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) { args[arg_index] = lb_const_nil(p->module, e->type); break; case Entity_Variable: - args[arg_index] = lb_handle_param_value(p, e->type, e->Variable.param_value, pos); + args[arg_index] = lb_handle_param_value(p, e->type, e->Variable.param_value, pt, expr); break; case Entity_Constant: From d03d9e49a6290b29a3259f99c4cdf574988b5765 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Sun, 15 Sep 2024 00:03:20 +0200 Subject: [PATCH 60/72] fix #4243 --- src/llvm_backend_debug.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp index bb6a1ba4f..5cc79dcc8 100644 --- a/src/llvm_backend_debug.cpp +++ b/src/llvm_backend_debug.cpp @@ -571,7 +571,7 @@ gb_internal LLVMMetadataRef lb_debug_bitfield(lbModule *m, Type *type, String na GB_ASSERT(f->kind == Entity_Variable); String name = f->token.string; elements[i] = LLVMDIBuilderCreateBitFieldMemberType(m->debug_builder, scope, cast(char const *)name.text, name.len, file, line, - bit_size, offset_in_bits, offset_in_bits, + bit_size, offset_in_bits, 0, LLVMDIFlagZero, lb_debug_type(m, f->type) ); From d38f5ffb49733b5084b9419412acf9381e3073d4 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sun, 15 Sep 2024 22:00:53 -0400 Subject: [PATCH 61/72] Remove unneeded synchronizations in `Chan` Everything was already guarded by `c.mutex`. --- core/sync/chan/chan.odin | 78 ++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 43 deletions(-) diff --git a/core/sync/chan/chan.odin b/core/sync/chan/chan.odin index 5b9a764b4..c470d15f3 100644 --- a/core/sync/chan/chan.odin +++ b/core/sync/chan/chan.odin @@ -22,19 +22,17 @@ Raw_Chan :: struct { allocator: runtime.Allocator, allocation_size: int, msg_size: u16, - closed: b16, // atomic + closed: b16, // guarded by `mutex` mutex: sync.Mutex, r_cond: sync.Cond, w_cond: sync.Cond, - r_waiting: int, // atomic - w_waiting: int, // atomic + r_waiting: int, // guarded by `mutex` + w_waiting: int, // guarded by `mutex` // Buffered queue: ^Raw_Queue, // Unbuffered - r_mutex: sync.Mutex, - w_mutex: sync.Mutex, unbuffered_data: rawptr, } @@ -164,32 +162,30 @@ send_raw :: proc "contextless" (c: ^Raw_Chan, msg_in: rawptr) -> (ok: bool) { } if c.queue != nil { // buffered sync.guard(&c.mutex) - for !sync.atomic_load(&c.closed) && - c.queue.len == c.queue.cap { - sync.atomic_add(&c.w_waiting, 1) + for !c.closed && c.queue.len == c.queue.cap { + c.w_waiting += 1 sync.wait(&c.w_cond, &c.mutex) - sync.atomic_sub(&c.w_waiting, 1) + c.w_waiting -= 1 } - if sync.atomic_load(&c.closed) { + if c.closed { return false } ok = raw_queue_push(c.queue, msg_in) - if sync.atomic_load(&c.r_waiting) > 0 { + if c.r_waiting > 0 { sync.signal(&c.r_cond) } } else if c.unbuffered_data != nil { // unbuffered - sync.guard(&c.w_mutex) sync.guard(&c.mutex) - if sync.atomic_load(&c.closed) { + if c.closed { return false } mem.copy(c.unbuffered_data, msg_in, int(c.msg_size)) - sync.atomic_add(&c.w_waiting, 1) - if sync.atomic_load(&c.r_waiting) > 0 { + c.w_waiting += 1 + if c.r_waiting > 0 { sync.signal(&c.r_cond) } sync.wait(&c.w_cond, &c.mutex) @@ -206,13 +202,13 @@ recv_raw :: proc "contextless" (c: ^Raw_Chan, msg_out: rawptr) -> (ok: bool) { if c.queue != nil { // buffered sync.guard(&c.mutex) for c.queue.len == 0 { - if sync.atomic_load(&c.closed) { + if c.closed { return } - sync.atomic_add(&c.r_waiting, 1) + c.r_waiting += 1 sync.wait(&c.r_cond, &c.mutex) - sync.atomic_sub(&c.r_waiting, 1) + c.r_waiting -= 1 } msg := raw_queue_pop(c.queue) @@ -220,27 +216,26 @@ recv_raw :: proc "contextless" (c: ^Raw_Chan, msg_out: rawptr) -> (ok: bool) { mem.copy(msg_out, msg, int(c.msg_size)) } - if sync.atomic_load(&c.w_waiting) > 0 { + if c.w_waiting > 0 { sync.signal(&c.w_cond) } ok = true } else if c.unbuffered_data != nil { // unbuffered - sync.guard(&c.r_mutex) sync.guard(&c.mutex) - for !sync.atomic_load(&c.closed) && - sync.atomic_load(&c.w_waiting) == 0 { - sync.atomic_add(&c.r_waiting, 1) + for !c.closed && + c.w_waiting == 0 { + c.r_waiting += 1 sync.wait(&c.r_cond, &c.mutex) - sync.atomic_sub(&c.r_waiting, 1) + c.r_waiting -= 1 } - if sync.atomic_load(&c.closed) { + if c.closed { return } mem.copy(msg_out, c.unbuffered_data, int(c.msg_size)) - sync.atomic_sub(&c.w_waiting, 1) + c.w_waiting -= 1 sync.signal(&c.w_cond) ok = true @@ -260,25 +255,24 @@ try_send_raw :: proc "contextless" (c: ^Raw_Chan, msg_in: rawptr) -> (ok: bool) return false } - if sync.atomic_load(&c.closed) { + if c.closed { return false } ok = raw_queue_push(c.queue, msg_in) - if sync.atomic_load(&c.r_waiting) > 0 { + if c.r_waiting > 0 { sync.signal(&c.r_cond) } } else if c.unbuffered_data != nil { // unbuffered - sync.guard(&c.w_mutex) sync.guard(&c.mutex) - if sync.atomic_load(&c.closed) { + if c.closed { return false } mem.copy(c.unbuffered_data, msg_in, int(c.msg_size)) - sync.atomic_add(&c.w_waiting, 1) - if sync.atomic_load(&c.r_waiting) > 0 { + c.w_waiting += 1 + if c.r_waiting > 0 { sync.signal(&c.r_cond) } sync.wait(&c.w_cond, &c.mutex) @@ -303,21 +297,19 @@ try_recv_raw :: proc "contextless" (c: ^Raw_Chan, msg_out: rawptr) -> bool { mem.copy(msg_out, msg, int(c.msg_size)) } - if sync.atomic_load(&c.w_waiting) > 0 { + if c.w_waiting > 0 { sync.signal(&c.w_cond) } return true } else if c.unbuffered_data != nil { // unbuffered - sync.guard(&c.r_mutex) sync.guard(&c.mutex) - if sync.atomic_load(&c.closed) || - sync.atomic_load(&c.w_waiting) == 0 { + if c.closed || c.w_waiting == 0 { return false } mem.copy(msg_out, c.unbuffered_data, int(c.msg_size)) - sync.atomic_sub(&c.w_waiting, 1) + c.w_waiting -= 1 sync.signal(&c.w_cond) return true @@ -360,10 +352,10 @@ close :: proc "contextless" (c: ^Raw_Chan) -> bool { return false } sync.guard(&c.mutex) - if sync.atomic_load(&c.closed) { + if c.closed { return false } - sync.atomic_store(&c.closed, true) + c.closed = true sync.broadcast(&c.r_cond) sync.broadcast(&c.w_cond) return true @@ -375,7 +367,7 @@ is_closed :: proc "contextless" (c: ^Raw_Chan) -> bool { return true } sync.guard(&c.mutex) - return bool(sync.atomic_load(&c.closed)) + return bool(c.closed) } @@ -434,7 +426,7 @@ can_recv :: proc "contextless" (c: ^Raw_Chan) -> bool { if is_buffered(c) { return c.queue.len > 0 } - return sync.atomic_load(&c.w_waiting) > 0 + return c.w_waiting > 0 } @@ -444,7 +436,7 @@ can_send :: proc "contextless" (c: ^Raw_Chan) -> bool { if is_buffered(c) { return c.queue.len < c.queue.cap } - return sync.atomic_load(&c.w_waiting) == 0 + return c.w_waiting == 0 } @@ -493,4 +485,4 @@ select_raw :: proc "odin" (recvs: []^Raw_Chan, sends: []^Raw_Chan, send_msgs: [] ok = send_raw(sends[sel.idx], send_msgs[sel.idx]) } return -} \ No newline at end of file +} From 16ef59700b68989beff48039a450ef6153b2e6af Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sun, 15 Sep 2024 23:58:03 -0400 Subject: [PATCH 62/72] Check for `EINTR` in `sys/posix` test --- tests/core/sys/posix/structs.odin | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/core/sys/posix/structs.odin b/tests/core/sys/posix/structs.odin index bdb1c24e3..95b6258f4 100644 --- a/tests/core/sys/posix/structs.odin +++ b/tests/core/sys/posix/structs.odin @@ -63,6 +63,9 @@ execute_struct_checks :: proc(t: ^testing.T) { waiting: for { status: i32 wpid := posix.waitpid(pid, &status, {}) + if status == posix.EINTR { + continue + } if !testing.expectf(t, wpid != -1, "waitpid() failure: %v", posix.strerror()) { return false } From fff99c726e53808b5b75a89ae66e5e84ab19268e Mon Sep 17 00:00:00 2001 From: pkova Date: Tue, 17 Sep 2024 01:52:51 +0300 Subject: [PATCH 63/72] Fix core sync test deadlock on darwin --- core/sync/futex_darwin.odin | 10 ++++++++-- core/sys/darwin/sync.odin | 7 +++++++ tests/core/sync/test_core_sync.odin | 1 - 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/core/sync/futex_darwin.odin b/core/sync/futex_darwin.odin index daefd6699..3915a414d 100644 --- a/core/sync/futex_darwin.odin +++ b/core/sync/futex_darwin.odin @@ -12,6 +12,7 @@ foreign System { // __ulock_wait is not available on 10.15 // See https://github.com/odin-lang/Odin/issues/1959 __ulock_wait :: proc "c" (operation: u32, addr: rawptr, value: u64, timeout_us: u32) -> c.int --- + __ulock_wait2 :: proc "c" (operation: u32, addr: rawptr, value: u64, timeout_ns: u64, value2: u64) -> c.int --- __ulock_wake :: proc "c" (operation: u32, addr: rawptr, wake_value: u64) -> c.int --- } @@ -52,8 +53,13 @@ _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, durati } } else { - timeout_ns := u32(duration) - s := __ulock_wait(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, u64(expected), timeout_ns) + when darwin.ULOCK_WAIT_2_AVAILABLE { + timeout_ns := u64(duration) + s := __ulock_wait2(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, u64(expected), timeout_ns, 0) + } else { + timeout_us := u32(duration) + s := __ulock_wait(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, u64(expected), timeout_us) + } if s >= 0 { return true } diff --git a/core/sys/darwin/sync.odin b/core/sys/darwin/sync.odin index 121d3edef..e90f30162 100644 --- a/core/sys/darwin/sync.odin +++ b/core/sys/darwin/sync.odin @@ -12,8 +12,15 @@ when ODIN_OS == .Darwin { } else { WAIT_ON_ADDRESS_AVAILABLE :: false } + when ODIN_MINIMUM_OS_VERSION >= 11_00_00 { + ULOCK_WAIT_2_AVAILABLE :: true + } else { + ULOCK_WAIT_2_AVAILABLE :: false + } + } else { WAIT_ON_ADDRESS_AVAILABLE :: false + ULOCK_WAIT_2_AVAILABLE :: false } os_sync_wait_on_address_flag :: enum u32 { diff --git a/tests/core/sync/test_core_sync.odin b/tests/core/sync/test_core_sync.odin index 32c08f935..fdd865686 100644 --- a/tests/core/sync/test_core_sync.odin +++ b/tests/core/sync/test_core_sync.odin @@ -8,7 +8,6 @@ // These tests are temporarily disabled on Darwin, as there is currently a // stall occurring which I cannot debug. -//+build !darwin package test_core_sync import "base:intrinsics" From aa25714d43716a857219d42796ade19de6c5ef70 Mon Sep 17 00:00:00 2001 From: pkova Date: Tue, 17 Sep 2024 02:11:41 +0300 Subject: [PATCH 64/72] Remove comment from core sync tests now that they're fixed --- tests/core/sync/test_core_sync.odin | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/core/sync/test_core_sync.odin b/tests/core/sync/test_core_sync.odin index fdd865686..d6a7a9517 100644 --- a/tests/core/sync/test_core_sync.odin +++ b/tests/core/sync/test_core_sync.odin @@ -4,9 +4,6 @@ // Keep in mind that running with the debug logs uncommented can result in // failures disappearing due to the delay of sending the log message causing // different synchronization patterns. -// -// These tests are temporarily disabled on Darwin, as there is currently a -// stall occurring which I cannot debug. package test_core_sync From 4d6f7dcac01061ee3060c14bb10e27f101998140 Mon Sep 17 00:00:00 2001 From: Pyry Kovanen Date: Tue, 17 Sep 2024 02:21:00 +0300 Subject: [PATCH 65/72] Fix code alignment in futex_darwin.odin Co-authored-by: Feoramund <161657516+Feoramund@users.noreply.github.com> --- core/sync/futex_darwin.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/sync/futex_darwin.odin b/core/sync/futex_darwin.odin index 3915a414d..5567be963 100644 --- a/core/sync/futex_darwin.odin +++ b/core/sync/futex_darwin.odin @@ -12,7 +12,7 @@ foreign System { // __ulock_wait is not available on 10.15 // See https://github.com/odin-lang/Odin/issues/1959 __ulock_wait :: proc "c" (operation: u32, addr: rawptr, value: u64, timeout_us: u32) -> c.int --- - __ulock_wait2 :: proc "c" (operation: u32, addr: rawptr, value: u64, timeout_ns: u64, value2: u64) -> c.int --- + __ulock_wait2 :: proc "c" (operation: u32, addr: rawptr, value: u64, timeout_ns: u64, value2: u64) -> c.int --- __ulock_wake :: proc "c" (operation: u32, addr: rawptr, wake_value: u64) -> c.int --- } From 6e0f1cc866e8a566a46ccaf9f14879e7ac344fe2 Mon Sep 17 00:00:00 2001 From: pkova Date: Tue, 17 Sep 2024 02:35:00 +0300 Subject: [PATCH 66/72] Pass microseconds instead of nanoseconds to __ulock_wait --- core/sync/futex_darwin.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/sync/futex_darwin.odin b/core/sync/futex_darwin.odin index 5567be963..32fdb1552 100644 --- a/core/sync/futex_darwin.odin +++ b/core/sync/futex_darwin.odin @@ -57,7 +57,7 @@ _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, durati timeout_ns := u64(duration) s := __ulock_wait2(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, u64(expected), timeout_ns, 0) } else { - timeout_us := u32(duration) + timeout_us := u32(duration) * 1000 s := __ulock_wait(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, u64(expected), timeout_us) } if s >= 0 { From abf6ea7732b855dcb0ddb549a6454f99c40b7328 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 17 Sep 2024 10:24:19 +0100 Subject: [PATCH 67/72] Fix minor bug with addressability --- src/check_stmt.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index c8717ba98..74a9e8825 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1641,6 +1641,8 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) Ast *expr = unparen_expr(rs->expr); + Operand rhs_operand = {}; + bool is_range = false; bool is_possibly_addressable = true; isize max_val_count = 2; @@ -1698,7 +1700,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) } } } - bool is_ptr = is_type_pointer(type_deref(operand.type)); + bool is_ptr = is_type_pointer(operand.type); Type *t = base_type(type_deref(operand.type)); switch (t->kind) { @@ -1750,16 +1752,19 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) break; case Type_DynamicArray: + is_possibly_addressable = true; array_add(&vals, t->DynamicArray.elem); array_add(&vals, t_int); break; case Type_Slice: + is_possibly_addressable = true; array_add(&vals, t->Slice.elem); array_add(&vals, t_int); break; case Type_Map: + is_possibly_addressable = true; is_map = true; array_add(&vals, t->Map.key); array_add(&vals, t->Map.value); @@ -1781,6 +1786,8 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) case Type_Tuple: { + is_possibly_addressable = false; + isize count = t->Tuple.variables.count; if (count < 1) { ERROR_BLOCK(); @@ -1810,8 +1817,6 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) array_add(&vals, e->type); } - is_possibly_addressable = false; - bool do_break = false; for (isize i = rs->vals.count-1; i >= 0; i--) { if (rs->vals[i] != nullptr && count < i+2) { @@ -1831,6 +1836,11 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) case Type_Struct: if (t->Struct.soa_kind != StructSoa_None) { + if (t->Struct.soa_kind == StructSoa_Fixed) { + is_possibly_addressable = operand.mode == Addressing_Variable || is_ptr; + } else { + is_possibly_addressable = true; + } is_soa = true; array_add(&vals, t->Struct.soa_elem); array_add(&vals, t_int); @@ -1907,7 +1917,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) if (is_possibly_addressable && i == addressable_index) { entity->flags &= ~EntityFlag_Value; } else { - char const *idx_name = is_map ? "key" : is_bit_set ? "element" : "index"; + char const *idx_name = is_map ? "key" : (is_bit_set || i == 0) ? "element" : "index"; error(token, "The %s variable '%.*s' cannot be made addressable", idx_name, LIT(str)); } } From 19c1ed154cc9e36433fe23e1e34810f9c53ec01d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 17 Sep 2024 11:01:26 +0100 Subject: [PATCH 68/72] Add `-vet-packages:` --- src/build_settings.cpp | 3 +-- src/checker.cpp | 16 ++++------------ src/main.cpp | 30 +++++++++++++++++++++++++++++- src/parser.cpp | 31 ++++++++++++++++++++----------- 4 files changed, 54 insertions(+), 26 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 1aca5d424..341cbe3e1 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -383,6 +383,7 @@ struct BuildContext { u64 vet_flags; u32 sanitizer_flags; + StringSet vet_packages; bool has_resource; String link_flags; @@ -1462,8 +1463,6 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta bc->thread_count = gb_max(bc->affinity.thread_count, 1); } - string_set_init(&bc->custom_attributes); - bc->ODIN_VENDOR = str_lit("odin"); bc->ODIN_VERSION = ODIN_VERSION; bc->ODIN_ROOT = odin_root_dir(); diff --git a/src/checker.cpp b/src/checker.cpp index 64c66c8a6..deb83bf97 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -533,18 +533,13 @@ gb_internal u64 check_vet_flags(CheckerContext *c) { c->curr_proc_decl->proc_lit) { file = c->curr_proc_decl->proc_lit->file(); } - if (file && file->vet_flags_set) { - return file->vet_flags; - } - return build_context.vet_flags; + + return ast_file_vet_flags(file); } gb_internal u64 check_vet_flags(Ast *node) { AstFile *file = node->file(); - if (file && file->vet_flags_set) { - return file->vet_flags; - } - return build_context.vet_flags; + return ast_file_vet_flags(file); } enum VettedEntityKind { @@ -6497,10 +6492,7 @@ gb_internal void check_parsed_files(Checker *c) { TIME_SECTION("check scope usage"); for (auto const &entry : c->info.files) { AstFile *f = entry.value; - u64 vet_flags = build_context.vet_flags; - if (f->vet_flags_set) { - vet_flags = f->vet_flags; - } + u64 vet_flags = ast_file_vet_flags(f); check_scope_usage(c, f->scope, vet_flags); } diff --git a/src/main.cpp b/src/main.cpp index 06c200442..9b1dcf0fa 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -346,6 +346,7 @@ enum BuildFlagKind { BuildFlag_VetSemicolon, BuildFlag_VetCast, BuildFlag_VetTabs, + BuildFlag_VetPackages, BuildFlag_CustomAttribute, BuildFlag_IgnoreUnknownAttributes, @@ -555,6 +556,7 @@ gb_internal bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_VetSemicolon, str_lit("vet-semicolon"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetCast, str_lit("vet-cast"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetTabs, str_lit("vet-tabs"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_VetPackages, str_lit("vet-packages"), BuildFlagParam_String, Command__does_check); add_flag(&build_flags, BuildFlag_CustomAttribute, str_lit("custom-attribute"), BuildFlagParam_String, Command__does_check, true); add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None, Command__does_check); @@ -1221,6 +1223,29 @@ gb_internal bool parse_build_flags(Array args) { case BuildFlag_VetCast: build_context.vet_flags |= VetFlag_Cast; break; case BuildFlag_VetTabs: build_context.vet_flags |= VetFlag_Tabs; break; + case BuildFlag_VetPackages: + { + GB_ASSERT(value.kind == ExactValue_String); + String val = value.value_string; + String_Iterator it = {val, 0}; + for (;;) { + String pkg = string_split_iterator(&it, ','); + if (pkg.len == 0) { + break; + } + + pkg = string_trim_whitespace(pkg); + if (!string_is_valid_identifier(pkg)) { + gb_printf_err("-%.*s '%.*s' must be a valid identifier\n", LIT(name), LIT(pkg)); + bad_flags = true; + continue; + } + + string_set_add(&build_context.vet_packages, pkg); + } + } + break; + case BuildFlag_CustomAttribute: { GB_ASSERT(value.kind == ExactValue_String); @@ -1234,7 +1259,7 @@ gb_internal bool parse_build_flags(Array args) { attr = string_trim_whitespace(attr); if (!string_is_valid_identifier(attr)) { - gb_printf_err("-custom-attribute '%.*s' must be a valid identifier\n", LIT(attr)); + gb_printf_err("-%.*s '%.*s' must be a valid identifier\n", LIT(name), LIT(attr)); bad_flags = true; continue; } @@ -3150,6 +3175,9 @@ int main(int arg_count, char const **arg_ptr) { build_context.command = command; + string_set_init(&build_context.custom_attributes); + string_set_init(&build_context.vet_packages); + if (!parse_build_flags(args)) { return 1; } diff --git a/src/parser.cpp b/src/parser.cpp index 88147d465..56bea8131 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1,10 +1,28 @@ #include "parser_pos.cpp" +gb_internal bool in_vet_packages(AstFile *file) { + if (file == nullptr) { + return true; + } + if (file->pkg == nullptr) { + return true; + } + if (build_context.vet_packages.entries.count == 0) { + return true; + } + return string_set_exists(&build_context.vet_packages, file->pkg->name); +} + gb_internal u64 ast_file_vet_flags(AstFile *f) { if (f != nullptr && f->vet_flags_set) { return f->vet_flags; } - return build_context.vet_flags; + + bool found = in_vet_packages(f); + if (found) { + return build_context.vet_flags; + } + return 0; } gb_internal bool ast_file_vet_style(AstFile *f) { @@ -5372,18 +5390,9 @@ gb_internal Ast *parse_stmt(AstFile *f) { } - -gb_internal u64 check_vet_flags(AstFile *file) { - if (file && file->vet_flags_set) { - return file->vet_flags; - } - return build_context.vet_flags; -} - - gb_internal void parse_enforce_tabs(AstFile *f) { // Checks to see if tabs have been used for indentation - if ((check_vet_flags(f) & VetFlag_Tabs) == 0) { + if ((ast_file_vet_flags(f) & VetFlag_Tabs) == 0) { return; } From 09588836e73d2def550cf5b1f6dab4d9de237e37 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 17 Sep 2024 11:33:42 +0100 Subject: [PATCH 69/72] Add `-vet-unused-procedures` --- src/build_settings.cpp | 3 +++ src/checker.cpp | 55 ++++++++++++++++++++++++++++++++++++------ src/main.cpp | 21 +++++++++++++++- 3 files changed, 71 insertions(+), 8 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 341cbe3e1..7aff8e650 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -285,6 +285,7 @@ enum VetFlags : u64 { VetFlag_Deprecated = 1u<<7, VetFlag_Cast = 1u<<8, VetFlag_Tabs = 1u<<9, + VetFlag_UnusedProcedures = 1u<<10, VetFlag_Unused = VetFlag_UnusedVariables|VetFlag_UnusedImports, @@ -316,6 +317,8 @@ u64 get_vet_flag_from_name(String const &name) { return VetFlag_Cast; } else if (name == "tabs") { return VetFlag_Tabs; + } else if (name == "unused-procedures") { + return VetFlag_UnusedProcedures; } return VetFlag_NONE; } diff --git a/src/checker.cpp b/src/checker.cpp index deb83bf97..af1e0e675 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -676,20 +676,45 @@ gb_internal bool check_vet_unused(Checker *c, Entity *e, VettedEntity *ve) { return false; } -gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) { - bool vet_unused = (vet_flags & VetFlag_Unused) != 0; - bool vet_shadowing = (vet_flags & (VetFlag_Shadowing|VetFlag_Using)) != 0; - +gb_internal void check_scope_usage_internal(Checker *c, Scope *scope, u64 vet_flags, bool per_entity) { + u64 original_vet_flags = vet_flags; Array vetted_entities = {}; array_init(&vetted_entities, heap_allocator()); + defer (array_free(&vetted_entities)); rw_mutex_shared_lock(&scope->mutex); for (auto const &entry : scope->elements) { Entity *e = entry.value; if (e == nullptr) continue; + + vet_flags = original_vet_flags; + if (per_entity) { + vet_flags = ast_file_vet_flags(e->file); + } + + bool vet_unused = (vet_flags & VetFlag_Unused) != 0; + bool vet_shadowing = (vet_flags & (VetFlag_Shadowing|VetFlag_Using)) != 0; + bool vet_unused_procedures = (vet_flags & VetFlag_UnusedProcedures) != 0; + VettedEntity ve_unused = {}; VettedEntity ve_shadowed = {}; - bool is_unused = vet_unused && check_vet_unused(c, e, &ve_unused); + bool is_unused = false; + if (vet_unused && check_vet_unused(c, e, &ve_unused)) { + is_unused = true; + } else if (vet_unused_procedures && + e->kind == Entity_Procedure) { + if (e->flags&EntityFlag_Used) { + is_unused = false; + } else if (e->flags & EntityFlag_Require) { + is_unused = false; + } else if (e->pkg && e->pkg->kind == Package_Init && e->token.string == "main") { + is_unused = false; + } else { + is_unused = true; + ve_unused.kind = VettedEntity_Unused; + ve_unused.entity = e; + } + } bool is_shadowed = vet_shadowing && check_vet_shadowing(c, e, &ve_shadowed); if (is_unused && is_shadowed) { VettedEntity ve_both = ve_shadowed; @@ -712,13 +737,18 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) { } rw_mutex_shared_unlock(&scope->mutex); - gb_sort(vetted_entities.data, vetted_entities.count, gb_size_of(VettedEntity), vetted_entity_variable_pos_cmp); + array_sort(vetted_entities, vetted_entity_variable_pos_cmp); for (auto const &ve : vetted_entities) { Entity *e = ve.entity; Entity *other = ve.other; String name = e->token.string; + vet_flags = original_vet_flags; + if (per_entity) { + vet_flags = ast_file_vet_flags(e->file); + } + if (ve.kind == VettedEntity_Shadowed_And_Unused) { error(e->token, "'%.*s' declared but not used, possibly shadows declaration at line %d", LIT(name), other->token.pos.line); } else if (vet_flags) { @@ -727,6 +757,9 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) { if (e->kind == Entity_Variable && (vet_flags & VetFlag_UnusedVariables) != 0) { error(e->token, "'%.*s' declared but not used", LIT(name)); } + if (e->kind == Entity_Procedure && (vet_flags & VetFlag_UnusedProcedures) != 0) { + error(e->token, "'%.*s' declared but not used", LIT(name)); + } if ((e->kind == Entity_ImportName || e->kind == Entity_LibraryName) && (vet_flags & VetFlag_UnusedImports) != 0) { error(e->token, "'%.*s' declared but not used", LIT(name)); } @@ -744,7 +777,11 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) { } } - array_free(&vetted_entities); +} + + +gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) { + check_scope_usage_internal(c, scope, vet_flags, false); for (Scope *child = scope->head_child; child != nullptr; child = child->next) { if (child->flags & (ScopeFlag_Proc|ScopeFlag_Type|ScopeFlag_File)) { @@ -6495,6 +6532,10 @@ gb_internal void check_parsed_files(Checker *c) { u64 vet_flags = ast_file_vet_flags(f); check_scope_usage(c, f->scope, vet_flags); } + for (auto const &entry : c->info.packages) { + AstPackage *pkg = entry.value; + check_scope_usage_internal(c, pkg->scope, 0, true); + } TIME_SECTION("add basic type information"); // Add "Basic" type information diff --git a/src/main.cpp b/src/main.cpp index 9b1dcf0fa..a969e32a9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -340,6 +340,7 @@ enum BuildFlagKind { BuildFlag_VetUnused, BuildFlag_VetUnusedImports, BuildFlag_VetUnusedVariables, + BuildFlag_VetUnusedProcedures, BuildFlag_VetUsingStmt, BuildFlag_VetUsingParam, BuildFlag_VetStyle, @@ -548,6 +549,7 @@ gb_internal bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetUnused, str_lit("vet-unused"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetUnusedVariables, str_lit("vet-unused-variables"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_VetUnusedProcedures, str_lit("vet-unused-procedures"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetUnusedImports, str_lit("vet-unused-imports"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetShadowing, str_lit("vet-shadowing"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetUsingStmt, str_lit("vet-using-stmt"), BuildFlagParam_None, Command__does_check); @@ -1222,6 +1224,13 @@ gb_internal bool parse_build_flags(Array args) { case BuildFlag_VetSemicolon: build_context.vet_flags |= VetFlag_Semicolon; break; case BuildFlag_VetCast: build_context.vet_flags |= VetFlag_Cast; break; case BuildFlag_VetTabs: build_context.vet_flags |= VetFlag_Tabs; break; + case BuildFlag_VetUnusedProcedures: + build_context.vet_flags |= VetFlag_UnusedProcedures; + if (!set_flags[BuildFlag_VetPackages]) { + gb_printf_err("-%.*s must be used with -vet-packages\n", LIT(name)); + bad_flags = true; + } + break; case BuildFlag_VetPackages: { @@ -2389,7 +2398,7 @@ gb_internal void print_show_help(String const arg0, String const &command) { print_usage_line(0, ""); print_usage_line(1, "-vet-unused"); - print_usage_line(2, "Checks for unused declarations."); + print_usage_line(2, "Checks for unused declarations (variables and imports)."); print_usage_line(0, ""); print_usage_line(1, "-vet-unused-variables"); @@ -2431,6 +2440,16 @@ gb_internal void print_show_help(String const arg0, String const &command) { print_usage_line(1, "-vet-tabs"); print_usage_line(2, "Errs when the use of tabs has not been used for indentation."); print_usage_line(0, ""); + + print_usage_line(1, "-vet-packages:"); + print_usage_line(2, "Sets which packages by name will be vetted."); + print_usage_line(2, "Files with specific +vet tags will not be ignored if they are not in the packages set."); + print_usage_line(0, ""); + + print_usage_line(1, "-vet-unused-procedures"); + print_usage_line(2, "Checks for unused procedures."); + print_usage_line(2, "Must be used with -vet-packages or specified on a per file with +vet tags."); + print_usage_line(0, ""); } if (check) { From 0975820c48f8e876c2838a5ef94400fdb5db0f87 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Tue, 17 Sep 2024 15:52:35 +0200 Subject: [PATCH 70/72] fix wrong ulock timeout calculation, add version check for ios --- core/sync/futex_darwin.odin | 5 ++++- core/sys/darwin/sync.odin | 8 ++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/core/sync/futex_darwin.odin b/core/sync/futex_darwin.odin index 32fdb1552..87b7b96e6 100644 --- a/core/sync/futex_darwin.odin +++ b/core/sync/futex_darwin.odin @@ -12,6 +12,7 @@ foreign System { // __ulock_wait is not available on 10.15 // See https://github.com/odin-lang/Odin/issues/1959 __ulock_wait :: proc "c" (operation: u32, addr: rawptr, value: u64, timeout_us: u32) -> c.int --- + // >= MacOS 11. __ulock_wait2 :: proc "c" (operation: u32, addr: rawptr, value: u64, timeout_ns: u64, value2: u64) -> c.int --- __ulock_wake :: proc "c" (operation: u32, addr: rawptr, wake_value: u64) -> c.int --- } @@ -57,12 +58,14 @@ _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, durati timeout_ns := u64(duration) s := __ulock_wait2(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, u64(expected), timeout_ns, 0) } else { - timeout_us := u32(duration) * 1000 + timeout_us := u32(duration / time.Microsecond) s := __ulock_wait(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, u64(expected), timeout_us) } + if s >= 0 { return true } + switch s { case EINTR, EFAULT: return true diff --git a/core/sys/darwin/sync.odin b/core/sys/darwin/sync.odin index e90f30162..58fc7c9e4 100644 --- a/core/sys/darwin/sync.odin +++ b/core/sys/darwin/sync.odin @@ -5,6 +5,7 @@ foreign import system "system:System.framework" // #define OS_WAIT_ON_ADDR_AVAILABILITY \ // __API_AVAILABLE(macos(14.4), ios(17.4), tvos(17.4), watchos(10.4)) when ODIN_OS == .Darwin { + when ODIN_PLATFORM_SUBTARGET == .iOS && ODIN_MINIMUM_OS_VERSION >= 17_04_00 { WAIT_ON_ADDRESS_AVAILABLE :: true } else when ODIN_MINIMUM_OS_VERSION >= 14_04_00 { @@ -12,7 +13,10 @@ when ODIN_OS == .Darwin { } else { WAIT_ON_ADDRESS_AVAILABLE :: false } - when ODIN_MINIMUM_OS_VERSION >= 11_00_00 { + + when ODIN_PLATFORM_SUBTARGET == .iOS && ODIN_MINIMUM_OS_VERSION >= 14_00_00 { + ULOCK_WAIT_2_AVAILABLE :: true + } else when ODIN_MINIMUM_OS_VERSION >= 11_00_00 { ULOCK_WAIT_2_AVAILABLE :: true } else { ULOCK_WAIT_2_AVAILABLE :: false @@ -20,7 +24,7 @@ when ODIN_OS == .Darwin { } else { WAIT_ON_ADDRESS_AVAILABLE :: false - ULOCK_WAIT_2_AVAILABLE :: false + ULOCK_WAIT_2_AVAILABLE :: false } os_sync_wait_on_address_flag :: enum u32 { From c794f853e943ba0f70c0e927c50ada1bf5136117 Mon Sep 17 00:00:00 2001 From: avanspector Date: Tue, 17 Sep 2024 16:57:02 +0200 Subject: [PATCH 71/72] init ansi on a standalone testing exe --- core/testing/runner.odin | 4 ++++ core/testing/runner_windows.odin | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 core/testing/runner_windows.odin diff --git a/core/testing/runner.odin b/core/testing/runner.odin index 386ba8cb5..10d5dca5c 100644 --- a/core/testing/runner.odin +++ b/core/testing/runner.odin @@ -204,6 +204,10 @@ runner :: proc(internal_tests: []Internal_Test) -> bool { } } + when ODIN_OS == .Windows { + console_ansi_init() + } + stdout := io.to_writer(os.stream_from_handle(os.stdout)) stderr := io.to_writer(os.stream_from_handle(os.stderr)) diff --git a/core/testing/runner_windows.odin b/core/testing/runner_windows.odin new file mode 100644 index 000000000..fa233ff84 --- /dev/null +++ b/core/testing/runner_windows.odin @@ -0,0 +1,22 @@ +//+private +package testing + +import win32 "core:sys/windows" + +console_ansi_init :: proc() { + stdout := win32.GetStdHandle(win32.STD_OUTPUT_HANDLE) + if stdout != win32.INVALID_HANDLE && stdout != nil { + old_console_mode: u32 + if win32.GetConsoleMode(stdout, &old_console_mode) { + win32.SetConsoleMode(stdout, old_console_mode | win32.ENABLE_VIRTUAL_TERMINAL_PROCESSING) + } + } + + stderr := win32.GetStdHandle(win32.STD_ERROR_HANDLE) + if stderr != win32.INVALID_HANDLE && stderr != nil { + old_console_mode: u32 + if win32.GetConsoleMode(stderr, &old_console_mode) { + win32.SetConsoleMode(stderr, old_console_mode | win32.ENABLE_VIRTUAL_TERMINAL_PROCESSING) + } + } +} From 6ef779cd5c8260b2e6979e676d28489fd53dd599 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Tue, 17 Sep 2024 17:46:30 +0200 Subject: [PATCH 72/72] add new macos releases to 'odin report' and sys/info --- core/sys/info/platform_darwin.odin | 4 ++++ src/bug_report.cpp | 2 ++ 2 files changed, 6 insertions(+) diff --git a/core/sys/info/platform_darwin.odin b/core/sys/info/platform_darwin.odin index 3fd857bfe..493f038f0 100644 --- a/core/sys/info/platform_darwin.odin +++ b/core/sys/info/platform_darwin.odin @@ -530,6 +530,10 @@ macos_release_map: map[string]Darwin_To_Release = { "23F79" = {{23, 5, 0}, "macOS", {"Sonoma", {14, 5, 0}}}, "23G80" = {{23, 6, 0}, "macOS", {"Sonoma", {14, 6, 0}}}, "23G93" = {{23, 6, 0}, "macOS", {"Sonoma", {14, 6, 1}}}, + "23H124" = {{23, 6, 0}, "macOS", {"Sonoma", {14, 7, 0}}}, + + // MacOS Sequoia + "24A335" = {{24, 0, 0}, "macOS", {"Sequoia", {15, 0, 0}}}, } @(private) diff --git a/src/bug_report.cpp b/src/bug_report.cpp index 5f768f57f..fa7e156ce 100644 --- a/src/bug_report.cpp +++ b/src/bug_report.cpp @@ -919,6 +919,8 @@ gb_internal void report_os_info() { {"23F79", {23, 5, 0}, "macOS", {"Sonoma", {14, 5, 0}}}, {"23G80", {23, 6, 0}, "macOS", {"Sonoma", {14, 6, 0}}}, {"23G93", {23, 6, 0}, "macOS", {"Sonoma", {14, 6, 1}}}, + {"23H124", {23, 6, 0}, "macOS", {"Sonoma", {14, 7, 0}}}, + {"24A335", {24, 0, 0}, "macOS", {"Sequoia", {15, 0, 0}}}, };