diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index af4e20652..76b7bd065 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -215,6 +215,8 @@ type_proc_return_count :: proc($T: typeid) -> int where type_is_proc(T) --- type_proc_parameter_type :: proc($T: typeid, index: int) -> typeid where type_is_proc(T) --- type_proc_return_type :: proc($T: typeid, index: int) -> typeid where type_is_proc(T) --- +type_proc_calling_convention :: proc($T: typeid) -> Odin_Calling_Convention where type_is_proc(T) --- + type_struct_field_count :: proc($T: typeid) -> int where type_is_struct(T) --- type_struct_has_implicit_padding :: proc($T: typeid) -> bool where type_is_struct(T) --- @@ -249,6 +251,8 @@ type_integer_to_signed :: proc($T: typeid) -> type where type_is_integer(T), t type_has_shared_fields :: proc($U, $V: typeid) -> bool where type_is_struct(U), type_is_struct(V) --- + + // Returns the canonicalized name of the type, of which is used to produce the pseudo-unique 'typeid' type_canonical_name :: proc($T: typeid) -> string --- diff --git a/base/runtime/core.odin b/base/runtime/core.odin index c1ba7aa3e..41d620bdd 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -41,7 +41,9 @@ Fast_Math_Flags :: intrinsics.Fast_Math_Flags // NOTE(bill): This must match the compiler's -Calling_Convention :: enum u8 { + +/* +enum u8 { Invalid = 0, Odin = 1, Contextless = 2, @@ -61,6 +63,8 @@ Calling_Convention :: enum u8 { Preserve_Most = 12, Preserve_All = 13, } +*/ +Calling_Convention :: type_of(ODIN_DEFAULT_CALLING_CONVENTION) Type_Info_Enum_Value :: distinct i64 @@ -296,7 +300,79 @@ when ODIN_OS == .Windows { dll_instance: rawptr } -// IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it) + +// This is safe to change. The log2 size of a cache-line. At minimum it has to +// be six though. Higher cache line sizes are permitted. +MAP_CACHE_LINE_LOG2 :: 6 + +// The size of a cache-line. +MAP_CACHE_LINE_SIZE :: 1 << MAP_CACHE_LINE_LOG2 + +// The minimum cache-line size allowed by this implementation is 64 bytes since +// we need 6 bits in the base pointer to store the integer log2 capacity, which +// at maximum is 63. Odin uses signed integers to represent length and capacity, +// so only 63 bits are needed in the maximum case. +#assert(MAP_CACHE_LINE_SIZE >= 64) + +// Map_Cell type that packs multiple T in such a way to ensure that each T stays +// aligned by align_of(T) and such that align_of(Map_Cell(T)) % MAP_CACHE_LINE_SIZE == 0 +// +// This means a value of type T will never straddle a cache-line. +// +// When multiple Ts can fit in a single cache-line the data array will have more +// than one element. When it cannot, the data array will have one element and +// an array of Map_Cell(T) will be padded to stay a multiple of MAP_CACHE_LINE_SIZE. +// +// We rely on the type system to do all the arithmetic and padding for us here. +// +// The usual array[index] indexing for []T backed by a []Map_Cell(T) becomes a bit +// more involved as there now may be internal padding. The indexing now becomes +// +// N :: len(Map_Cell(T){}.data) +// i := index / N +// j := index % N +// cell[i].data[j] +// +// However, since len(Map_Cell(T){}.data) is a compile-time constant, there are some +// optimizations we can do to eliminate the need for any divisions as N will +// be bounded by [1, 64). +// +// In the optimal case, len(Map_Cell(T){}.data) = 1 so the cell array can be treated +// as a regular array of T, which is the case for hashes. +Map_Cell :: struct($T: typeid) #align(MAP_CACHE_LINE_SIZE) { + data: [MAP_CACHE_LINE_SIZE / size_of(T) when 0 < size_of(T) && size_of(T) < MAP_CACHE_LINE_SIZE else 1]T, +} + +// So we can operate on a cell data structure at runtime without any type +// information, we have a simple table that stores some traits about the cell. +// +// 32-bytes on 64-bit +// 16-bytes on 32-bit +Map_Cell_Info :: struct { + size_of_type: uintptr, // 8-bytes on 64-bit, 4-bytes on 32-bits + align_of_type: uintptr, // 8-bytes on 64-bit, 4-bytes on 32-bits + size_of_cell: uintptr, // 8-bytes on 64-bit, 4-bytes on 32-bits + elements_per_cell: uintptr, // 8-bytes on 64-bit, 4-bytes on 32-bits +} + +Map_Hash :: uintptr + +// When working with the type-erased structure at runtime we need information +// about the map to make working with it possible. This info structure stores +// that. +// +// `Map_Info` and `Map_Cell_Info` are read only data structures and cannot be +// modified after creation +// +// 32-bytes on 64-bit +// 16-bytes on 32-bit +Map_Info :: struct { + ks: ^Map_Cell_Info, // 8-bytes on 64-bit, 4-bytes on 32-bit + vs: ^Map_Cell_Info, // 8-bytes on 64-bit, 4-bytes on 32-bit + key_hasher: proc "contextless" (key: rawptr, seed: Map_Hash) -> Map_Hash, // 8-bytes on 64-bit, 4-bytes on 32-bit + key_equal: proc "contextless" (lhs, rhs: rawptr) -> bool, // 8-bytes on 64-bit, 4-bytes on 32-bit +} + Source_Code_Location :: struct { diff --git a/base/runtime/core_builtin.odin b/base/runtime/core_builtin.odin index cdf0fbcab..d84216720 100644 --- a/base/runtime/core_builtin.odin +++ b/base/runtime/core_builtin.odin @@ -2,6 +2,8 @@ package runtime import "base:intrinsics" +MAP_ENABLED :: !ODIN_BEDROCK + @builtin Maybe :: union($T: typeid) {T} @@ -65,7 +67,7 @@ when !NO_DEFAULT_TEMP_ALLOCATOR { // Initializes the global temporary allocator used as the default `context.temp_allocator`. // This is ignored when `NO_DEFAULT_TEMP_ALLOCATOR` is true. @(builtin, disabled=NO_DEFAULT_TEMP_ALLOCATOR) -init_global_temporary_allocator :: proc(size: int, backup_allocator := context.allocator) { +init_global_temporary_allocator :: proc "odin" (size: int, backup_allocator := context.allocator) { when !NO_DEFAULT_TEMP_ALLOCATOR { default_temp_allocator_init(&global_default_temp_allocator_data, size, backup_allocator) } @@ -387,7 +389,7 @@ pop_front_safe :: proc { @builtin clear :: proc{ clear_dynamic_array, - clear_map, + clear_map where MAP_ENABLED, clear_fixed_capacity_dynamic_array, clear_soa_dynamic_array, @@ -397,7 +399,7 @@ clear :: proc{ @builtin reserve :: proc{ reserve_dynamic_array, - reserve_map, + reserve_map where MAP_ENABLED, reserve_soa, } @@ -430,7 +432,7 @@ non_zero_resize :: proc{ @builtin shrink :: proc{ shrink_dynamic_array, - shrink_map, + shrink_map where MAP_ENABLED, } // `free` will try to free the passed pointer, with the given `allocator` if the allocator supports this operation. @@ -471,14 +473,6 @@ delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) -> delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { return mem_free_with_size(raw_data(array), len(array)*size_of(E), allocator, loc) } -// `delete_map` will try to free the underlying data of the passed map, with the given `allocator` if the allocator supports this operation. -// -// Note: Prefer the procedure group `delete`. -@builtin -delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error { - return map_free_dynamic(transmute(Raw_Map)m, map_info(T), loc) -} - @builtin delete_string16 :: proc(str: string16, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { @@ -489,6 +483,16 @@ delete_cstring16 :: proc(str: cstring16, allocator := context.allocator, loc := return mem_free((^u16)(str), allocator, loc) } +when MAP_ENABLED { + // `delete_map` will try to free the underlying data of the passed map, with the given `allocator` if the allocator supports this operation. + // + // Note: Prefer the procedure group `delete`. + @builtin + delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error { + return map_free_dynamic(transmute(Raw_Map)m, map_info(T), loc) + } +} + // `delete` will try to free the underlying data of the passed built-in data structure (string, cstring, dynamic array, slice, or map), with the given `allocator` if the allocator supports this operation. // // Note: Prefer `delete` over the specific `delete_*` procedures where possible. @@ -498,7 +502,7 @@ delete :: proc{ delete_cstring, delete_dynamic_array, delete_slice, - delete_map, + delete_map where MAP_ENABLED, delete_soa_slice, delete_soa_dynamic_array, delete_string16, @@ -597,29 +601,32 @@ _make_dynamic_array_len_cap :: proc(array: ^Raw_Dynamic_Array, size_of_elem, ali return } -// `make_map` initializes a map with an allocator. Like `new`, the first argument is a type, not a value. -// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it. -// -// Note: Prefer using the procedure group `make`. -@(builtin, require_results) -make_map :: proc($T: typeid/map[$K]$E, allocator := context.allocator, loc := #caller_location) -> (m: T) { - m.allocator = allocator - return m +when MAP_ENABLED { + // `make_map` initializes a map with an allocator. Like `new`, the first argument is a type, not a value. + // Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it. + // + // Note: Prefer using the procedure group `make`. + @(builtin, require_results) + make_map :: proc($T: typeid/map[$K]$E, allocator := context.allocator, loc := #caller_location) -> (m: T) { + m.allocator = allocator + return m + } + + // `make_map_cap` initializes a map with an allocator and allocates space using `capacity`. + // Like `new`, the first argument is a type, not a value. + // Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it. + // + // Note: Prefer using the procedure group `make`. + @(builtin, require_results) + make_map_cap :: proc($T: typeid/map[$K]$E, #any_int capacity: int, allocator := context.allocator, loc := #caller_location) -> (m: T, err: Allocator_Error) #optional_allocator_error { + make_map_expr_error_loc(loc, capacity) + context.allocator = allocator + + err = reserve_map(&m, capacity, loc) + return + } } -// `make_map_cap` initializes a map with an allocator and allocates space using `capacity`. -// Like `new`, the first argument is a type, not a value. -// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it. -// -// Note: Prefer using the procedure group `make`. -@(builtin, require_results) -make_map_cap :: proc($T: typeid/map[$K]$E, #any_int capacity: int, allocator := context.allocator, loc := #caller_location) -> (m: T, err: Allocator_Error) #optional_allocator_error { - make_map_expr_error_loc(loc, capacity) - context.allocator = allocator - - err = reserve_map(&m, capacity, loc) - return -} // `make_multi_pointer` allocates and initializes a multi-pointer. Like `new`, the first argument is a type, not a value. // Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it. // @@ -649,8 +656,8 @@ make :: proc{ make_dynamic_array, make_dynamic_array_len, make_dynamic_array_len_cap, - make_map, - make_map_cap, + make_map where MAP_ENABLED, + make_map_cap where MAP_ENABLED, make_multi_pointer, make_soa_slice, @@ -659,53 +666,54 @@ make :: proc{ make_soa_dynamic_array_len_cap, } +when MAP_ENABLED { + // `clear_map` will set the length of a passed map to `0` + // + // Note: Prefer the procedure group `clear` + @builtin + clear_map :: proc "contextless" (m: ^$T/map[$K]$V) { + if m == nil { + return + } + map_clear_dynamic((^Raw_Map)(m), map_info(T)) + } -// `clear_map` will set the length of a passed map to `0` -// -// Note: Prefer the procedure group `clear` -@builtin -clear_map :: proc "contextless" (m: ^$T/map[$K]$V) { - if m == nil { + // `reserve_map` will try to reserve memory of a passed map to the requested element count (setting the `cap`). + // + // Note: Prefer the procedure group `reserve` + @builtin + reserve_map :: proc(m: ^$T/map[$K]$V, #any_int capacity: int, loc := #caller_location) -> Allocator_Error { + return __dynamic_map_reserve((^Raw_Map)(m), map_info(T), uint(capacity), loc) + } + + // Shrinks the capacity of a map down to the current length. + // + // Note: Prefer the procedure group `shrink` + @builtin + shrink_map :: proc(m: ^$T/map[$K]$V, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) { + if m != nil { + return map_shrink_dynamic((^Raw_Map)(m), map_info(T), loc) + } return } - map_clear_dynamic((^Raw_Map)(m), map_info(T)) -} -// `reserve_map` will try to reserve memory of a passed map to the requested element count (setting the `cap`). -// -// Note: Prefer the procedure group `reserve` -@builtin -reserve_map :: proc(m: ^$T/map[$K]$V, #any_int capacity: int, loc := #caller_location) -> Allocator_Error { - return __dynamic_map_reserve((^Raw_Map)(m), map_info(T), uint(capacity), loc) -} - -// Shrinks the capacity of a map down to the current length. -// -// Note: Prefer the procedure group `shrink` -@builtin -shrink_map :: proc(m: ^$T/map[$K]$V, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) { - if m != nil { - return map_shrink_dynamic((^Raw_Map)(m), map_info(T), loc) - } - return -} - -// The delete_key built-in procedure deletes the element with the specified key (m[key]) from the map. -// If m is nil, or there is no such element, this procedure is a no-op -// It is safe to use `delete_key` while iterating a map. -// But if you iterate across a map and insert a new key, it could resize which means you are not iterating across all of the elements. -@builtin -delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value: V) { - if m != nil { - key := key - old_k, old_v, ok := map_erase_dynamic((^Raw_Map)(m), map_info(T), uintptr(&key)) - if ok { - deleted_key = (^K)(old_k)^ - deleted_value = (^V)(old_v)^ + // The delete_key built-in procedure deletes the element with the specified key (m[key]) from the map. + // If m is nil, or there is no such element, this procedure is a no-op + // It is safe to use `delete_key` while iterating a map. + // But if you iterate across a map and insert a new key, it could resize which means you are not iterating across all of the elements. + @builtin + delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value: V) { + if m != nil { + key := key + old_k, old_v, ok := map_erase_dynamic((^Raw_Map)(m), map_info(T), uintptr(&key)) + if ok { + deleted_key = (^K)(old_k)^ + deleted_value = (^V)(old_v)^ + } } + return } - return } _append_elem :: #force_no_inline proc(array: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, arg_ptr: rawptr, should_zero: bool, loc := #caller_location) -> (num_appended: int, err: Allocator_Error) #optional_allocator_error { @@ -1525,53 +1533,54 @@ _shrink_dynamic_array :: proc(a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem return true, nil } -@builtin -map_insert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (value_ptr: ^V, err: Allocator_Error) #optional_allocator_error { - key, value := key, value - value_ptr_raw, err_set :=__dynamic_map_set_without_hash((^Raw_Map)(m), map_info(T), rawptr(&key), rawptr(&value), loc) - return (^V)(value_ptr_raw), err_set -} - -// Explicitly inserts a key and value into a map `m`, the same as `map_insert`, but the return values differ. -// - `prev_key` will return the previous pointer of a key if it exists, check `found_previous` if was previously found -// - `value_ptr` will return the pointer of the memory where the insertion happens, and `nil` if the map failed to resize -// - `found_previous` will be true a previous key was found -@(builtin, require_results) -map_upsert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (prev_key: K, value_ptr: ^V, found_previous: bool) { - key, value := key, value - kp, vp := __dynamic_map_set_extra_without_hash((^Raw_Map)(m), map_info(T), rawptr(&key), rawptr(&value), loc) - if kp != nil { - prev_key = (^K)(kp)^ - found_previous = true +when MAP_ENABLED { + @builtin + map_insert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (ptr: ^V) { + key, value := key, value + return (^V)(__dynamic_map_set_without_hash((^Raw_Map)(m), map_info(T), rawptr(&key), rawptr(&value), loc)) } - value_ptr = (^V)(vp) - return -} -/* -Retrieves a pointer to the key and value for a possibly just inserted entry into the map. + // Explicitly inserts a key and value into a map `m`, the same as `map_insert`, but the return values differ. + // - `prev_key` will return the previous pointer of a key if it exists, check `found_previous` if was previously found + // - `value_ptr` will return the pointer of the memory where the insertion happens, and `nil` if the map failed to resize + // - `found_previous` will be true a previous key was found + @(builtin, require_results) + map_upsert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (prev_key: K, value_ptr: ^V, found_previous: bool) { + key, value := key, value + kp, vp := __dynamic_map_set_extra_without_hash((^Raw_Map)(m), map_info(T), rawptr(&key), rawptr(&value), loc) + if kp != nil { + prev_key = (^K)(kp)^ + found_previous = true + } + value_ptr = (^V)(vp) + return + } -If the `key` was not in the map `m`, an entry is inserted with the zero value and `just_inserted` will be `true`. -Otherwise the existing entry is left untouched and pointers to its key and value are returned. + /* + Retrieves a pointer to the key and value for a possibly just inserted entry into the map. -If the map has to grow in order to insert the entry and the allocation fails, `err` is set and returned. + If the `key` was not in the map `m`, an entry is inserted with the zero value and `just_inserted` will be `true`. + Otherwise the existing entry is left untouched and pointers to its key and value are returned. -If `err` is `nil`, `key_ptr` and `value_ptr` are valid pointers and will not be `nil`. + If the map has to grow in order to insert the entry and the allocation fails, `err` is set and returned. -WARN: User modification of the key pointed at by `key_ptr` should only be done if the new key is equal to (in hash) the old key. -If that is not the case you will corrupt the map. -*/ -@(builtin, require_results) -map_entry :: proc(m: ^$T/map[$K]$V, key: K, loc := #caller_location) -> (key_ptr: ^K, value_ptr: ^V, just_inserted: bool, err: Allocator_Error) { - key := key - zero: V + If `err` is `nil`, `key_ptr` and `value_ptr` are valid pointers and will not be `nil`. - _key_ptr, _value_ptr: rawptr - _key_ptr, _value_ptr, just_inserted, err = __dynamic_map_entry((^Raw_Map)(m), map_info(T), &key, &zero, loc) + WARN: User modification of the key pointed at by `key_ptr` should only be done if the new key is equal to (in hash) the old key. + If that is not the case you will corrupt the map. + */ + @(builtin, require_results) + map_entry :: proc(m: ^$T/map[$K]$V, key: K, loc := #caller_location) -> (key_ptr: ^K, value_ptr: ^V, just_inserted: bool, err: Allocator_Error) { + key := key + zero: V - key_ptr = (^K)(_key_ptr) - value_ptr = (^V)(_value_ptr) - return + _key_ptr, _value_ptr: rawptr + _key_ptr, _value_ptr, just_inserted, err = __dynamic_map_entry((^Raw_Map)(m), map_info(T), &key, &zero, loc) + + key_ptr = (^K)(_key_ptr) + value_ptr = (^V)(_value_ptr) + return + } } diff --git a/base/runtime/default_temporary_allocator.odin b/base/runtime/default_temporary_allocator.odin index 2017570bb..857463912 100644 --- a/base/runtime/default_temporary_allocator.odin +++ b/base/runtime/default_temporary_allocator.odin @@ -7,7 +7,7 @@ when NO_DEFAULT_TEMP_ALLOCATOR { // `Default_Temp_Allocator` is a `nil_allocator` when `NO_DEFAULT_TEMP_ALLOCATOR` is `true`. Default_Temp_Allocator :: struct {} - default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backing_allocator := context.allocator) {} + default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backing_allocator: Allocator) {} default_temp_allocator_destroy :: proc "contextless" (s: ^Default_Temp_Allocator) {} @@ -30,7 +30,7 @@ when NO_DEFAULT_TEMP_ALLOCATOR { arena: Arena, } - default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backing_allocator := context.allocator) { + default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backing_allocator: Allocator) { _ = arena_init(&s.arena, uint(size), backing_allocator) } diff --git a/base/runtime/dynamic_map_internal.odin b/base/runtime/dynamic_map_internal.odin index cc38da598..c9b3668c3 100644 --- a/base/runtime/dynamic_map_internal.odin +++ b/base/runtime/dynamic_map_internal.odin @@ -1,3 +1,4 @@ +#+build !bedrock package runtime import "base:intrinsics" @@ -47,60 +48,6 @@ MAP_MIN_LOG2_CAPACITY :: 3 // 8 elements // Has to be less than 100% though. #assert(MAP_LOAD_FACTOR < 100) -// This is safe to change. The log2 size of a cache-line. At minimum it has to -// be six though. Higher cache line sizes are permitted. -MAP_CACHE_LINE_LOG2 :: 6 - -// The size of a cache-line. -MAP_CACHE_LINE_SIZE :: 1 << MAP_CACHE_LINE_LOG2 - -// The minimum cache-line size allowed by this implementation is 64 bytes since -// we need 6 bits in the base pointer to store the integer log2 capacity, which -// at maximum is 63. Odin uses signed integers to represent length and capacity, -// so only 63 bits are needed in the maximum case. -#assert(MAP_CACHE_LINE_SIZE >= 64) - -// Map_Cell type that packs multiple T in such a way to ensure that each T stays -// aligned by align_of(T) and such that align_of(Map_Cell(T)) % MAP_CACHE_LINE_SIZE == 0 -// -// This means a value of type T will never straddle a cache-line. -// -// When multiple Ts can fit in a single cache-line the data array will have more -// than one element. When it cannot, the data array will have one element and -// an array of Map_Cell(T) will be padded to stay a multiple of MAP_CACHE_LINE_SIZE. -// -// We rely on the type system to do all the arithmetic and padding for us here. -// -// The usual array[index] indexing for []T backed by a []Map_Cell(T) becomes a bit -// more involved as there now may be internal padding. The indexing now becomes -// -// N :: len(Map_Cell(T){}.data) -// i := index / N -// j := index % N -// cell[i].data[j] -// -// However, since len(Map_Cell(T){}.data) is a compile-time constant, there are some -// optimizations we can do to eliminate the need for any divisions as N will -// be bounded by [1, 64). -// -// In the optimal case, len(Map_Cell(T){}.data) = 1 so the cell array can be treated -// as a regular array of T, which is the case for hashes. -Map_Cell :: struct($T: typeid) #align(MAP_CACHE_LINE_SIZE) { - data: [MAP_CACHE_LINE_SIZE / size_of(T) when 0 < size_of(T) && size_of(T) < MAP_CACHE_LINE_SIZE else 1]T, -} - -// So we can operate on a cell data structure at runtime without any type -// information, we have a simple table that stores some traits about the cell. -// -// 32-bytes on 64-bit -// 16-bytes on 32-bit -Map_Cell_Info :: struct { - size_of_type: uintptr, // 8-bytes on 64-bit, 4-bytes on 32-bits - align_of_type: uintptr, // 8-bytes on 64-bit, 4-bytes on 32-bits - size_of_cell: uintptr, // 8-bytes on 64-bit, 4-bytes on 32-bits - elements_per_cell: uintptr, // 8-bytes on 64-bit, 4-bytes on 32-bits -} - // map_cell_info :: proc "contextless" ($T: typeid) -> ^Map_Cell_Info {...} map_cell_info :: intrinsics.type_map_cell_info @@ -226,8 +173,6 @@ map_data :: #force_inline proc "contextless" (m: Raw_Map) -> uintptr { } -Map_Hash :: uintptr - TOMBSTONE_MASK :: 1<<(size_of(Map_Hash)*8 - 1) // Procedure to check if a slot is empty for a given hash. This is represented @@ -288,23 +233,6 @@ map_probe_distance :: #force_inline proc "contextless" (m: Raw_Map, hash: Map_Ha return (slot - uintptr(hash)) & (capacity - 1) // NOTE(bill): this is equivalent to the above, but less operations } -// When working with the type-erased structure at runtime we need information -// about the map to make working with it possible. This info structure stores -// that. -// -// `Map_Info` and `Map_Cell_Info` are read only data structures and cannot be -// modified after creation -// -// 32-bytes on 64-bit -// 16-bytes on 32-bit -Map_Info :: struct { - ks: ^Map_Cell_Info, // 8-bytes on 64-bit, 4-bytes on 32-bit - vs: ^Map_Cell_Info, // 8-bytes on 64-bit, 4-bytes on 32-bit - key_hasher: proc "contextless" (key: rawptr, seed: Map_Hash) -> Map_Hash, // 8-bytes on 64-bit, 4-bytes on 32-bit - key_equal: proc "contextless" (lhs, rhs: rawptr) -> bool, // 8-bytes on 64-bit, 4-bytes on 32-bit -} - - // The Map_Info structure is basically a pseudo-table of information for a given K and V pair. // map_info :: proc "contextless" ($T: typeid/map[$K]$V) -> ^Map_Info {...} map_info :: intrinsics.type_map_info diff --git a/base/runtime/entry_unix.odin b/base/runtime/entry_unix.odin index f63ff3793..f02ead242 100644 --- a/base/runtime/entry_unix.odin +++ b/base/runtime/entry_unix.odin @@ -9,13 +9,15 @@ when ODIN_BUILD_MODE == .Dynamic { @(link_name="_odin_entry_point", linkage="strong", require/*, link_section=".init"*/) _odin_entry_point :: proc "c" () { context = default_context() - #force_no_inline _startup_runtime() + when !ODIN_BEDROCK { #force_no_inline _startup_runtime() } intrinsics.__entry_point() } @(link_name="_odin_exit_point", linkage="strong", require/*, link_section=".fini"*/) _odin_exit_point :: proc "c" () { context = default_context() - #force_no_inline _cleanup_runtime() + when !ODIN_BEDROCK { + #force_no_inline _cleanup_runtime() + } } @(link_name="main", linkage="strong", require) main :: proc "c" (argc: i32, argv: [^]cstring) -> i32 { @@ -42,9 +44,9 @@ when ODIN_BUILD_MODE == .Dynamic { _start_odin :: proc "c" (argc: i32, argv: [^]cstring) -> ! { args__ = argv[:argc] context = default_context() - #force_no_inline _startup_runtime() + when !ODIN_BEDROCK { #force_no_inline _startup_runtime() } intrinsics.__entry_point() - #force_no_inline _cleanup_runtime() + when !ODIN_BEDROCK { #force_no_inline _cleanup_runtime() } intrinsics.syscall(SYS_exit, 0) unreachable() } @@ -53,9 +55,9 @@ when ODIN_BUILD_MODE == .Dynamic { main :: proc "c" (argc: i32, argv: [^]cstring) -> i32 { args__ = argv[:argc] context = default_context() - #force_no_inline _startup_runtime() + when !ODIN_BEDROCK { #force_no_inline _startup_runtime() } intrinsics.__entry_point() - #force_no_inline _cleanup_runtime() + when !ODIN_BEDROCK { #force_no_inline _cleanup_runtime() } return 0 } } diff --git a/base/runtime/entry_windows.odin b/base/runtime/entry_windows.odin index dc8e9b82c..d3b38bc9c 100644 --- a/base/runtime/entry_windows.odin +++ b/base/runtime/entry_windows.odin @@ -16,10 +16,10 @@ when ODIN_BUILD_MODE == .Dynamic { switch dll_forward_reason { case .Process_Attach: - #force_no_inline _startup_runtime() + when !ODIN_BEDROCK { #force_no_inline _startup_runtime() } intrinsics.__entry_point() case .Process_Detach: - #force_no_inline _cleanup_runtime() + when !ODIN_BEDROCK { #force_no_inline _cleanup_runtime() } case .Thread_Attach: break case .Thread_Detach: @@ -35,18 +35,18 @@ when ODIN_BUILD_MODE == .Dynamic { main :: proc "c" (argc: i32, argv: [^]cstring) -> i32 { args__ = argv[:argc] context = default_context() - #force_no_inline _startup_runtime() + when !ODIN_BEDROCK { #force_no_inline _startup_runtime() } intrinsics.__entry_point() - #force_no_inline _cleanup_runtime() + when !ODIN_BEDROCK { #force_no_inline _cleanup_runtime() } return 0 } } else when ODIN_NO_CRT { @(link_name="mainCRTStartup", linkage="strong", require) mainCRTStartup :: proc "system" () -> i32 { context = default_context() - #force_no_inline _startup_runtime() + when !ODIN_BEDROCK { #force_no_inline _startup_runtime() } intrinsics.__entry_point() - #force_no_inline _cleanup_runtime() + when !ODIN_BEDROCK { #force_no_inline _cleanup_runtime() } return 0 } } else { @@ -54,9 +54,9 @@ when ODIN_BUILD_MODE == .Dynamic { main :: proc "c" (argc: i32, argv: [^]cstring) -> i32 { args__ = argv[:argc] context = default_context() - #force_no_inline _startup_runtime() + when !ODIN_BEDROCK { #force_no_inline _startup_runtime() } intrinsics.__entry_point() - #force_no_inline _cleanup_runtime() + when !ODIN_BEDROCK { #force_no_inline _cleanup_runtime() } return 0 } } diff --git a/base/runtime/internal.odin b/base/runtime/internal.odin index 2b9cdae62..e08d0e01d 100644 --- a/base/runtime/internal.odin +++ b/base/runtime/internal.odin @@ -1164,217 +1164,6 @@ extendhfsf2 :: proc "c" (value: __float16) -> f32 { return gnu_h2f_ieee(value) } - - -@(link_name="__floattidf", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) -floattidf :: proc "c" (a: i128) -> f64 { - DBL_MANT_DIG :: 53 - if a == 0 { - return 0.0 - } - a := a - N :: size_of(i128) * 8 - s := a >> (N-1) - a = (a ~ s) - s - sd: = N - intrinsics.count_leading_zeros(a) // number of significant digits - e := i32(sd - 1) // exponent - if sd > DBL_MANT_DIG { - switch sd { - case DBL_MANT_DIG + 1: - a <<= 1 - case DBL_MANT_DIG + 2: - // okay - case: - a = i128(u128(a) >> u128(sd - (DBL_MANT_DIG+2))) | - i128(u128(a) & (~u128(0) >> u128(N + DBL_MANT_DIG+2 - sd)) != 0) - } - - a |= i128((a & 4) != 0) - a += 1 - a >>= 2 - - if a & (i128(1) << DBL_MANT_DIG) != 0 { - a >>= 1 - e += 1 - } - } else { - a <<= u128(DBL_MANT_DIG - sd) & 127 - } - fb: [2]u32 - fb[1] = (u32(s) & 0x80000000) | // sign - (u32(e + 1023) << 20) | // exponent - u32((u64(a) >> 32) & 0x000FFFFF) // mantissa-high - fb[0] = u32(a) // mantissa-low - return transmute(f64)fb -} - - -@(link_name="__floattidf_unsigned", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) -floattidf_unsigned :: proc "c" (a: u128) -> f64 { - DBL_MANT_DIG :: 53 - if a == 0 { - return 0.0 - } - a := a - N :: size_of(u128) * 8 - sd: = N - intrinsics.count_leading_zeros(a) // number of significant digits - e := i32(sd - 1) // exponent - if sd > DBL_MANT_DIG { - switch sd { - case DBL_MANT_DIG + 1: - a <<= 1 - case DBL_MANT_DIG + 2: - // okay - case: - a = u128(u128(a) >> u128(sd - (DBL_MANT_DIG+2))) | - u128(u128(a) & (~u128(0) >> u128(N + DBL_MANT_DIG+2 - sd)) != 0) - } - - a |= u128((a & 4) != 0) - a += 1 - a >>= 2 - - if a & (1 << DBL_MANT_DIG) != 0 { - a >>= 1 - e += 1 - } - } else { - a <<= u128(DBL_MANT_DIG - sd) - } - fb: [2]u32 - fb[1] = (0) | // sign - u32((e + 1023) << 20) | // exponent - u32((u64(a) >> 32) & 0x000FFFFF) // mantissa-high - fb[0] = u32(a) // mantissa-low - return transmute(f64)fb -} - - - -@(link_name="__fixunsdfti", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) -fixunsdfti :: #force_no_inline proc "c" (a: f64) -> u128 { - // TODO(bill): implement `fixunsdfti` correctly - x := u64(a) - return u128(x) -} - -@(link_name="__fixunsdfdi", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) -fixunsdfdi :: #force_no_inline proc "c" (a: f64) -> i128 { - // TODO(bill): implement `fixunsdfdi` correctly - x := i64(a) - return i128(x) -} - - - - -@(link_name="__umodti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) -umodti3 :: proc "c" (a, b: u128) -> u128 { - r: u128 = --- - _ = udivmod128(a, b, &r) - return r -} - - -@(link_name="__udivmodti4", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) -udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 { - return udivmod128(a, b, rem) -} - -when !IS_WASM { - @(link_name="__udivti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) - udivti3 :: proc "c" (a, b: u128) -> u128 { - return udivmodti4(a, b, nil) - } -} - - -@(link_name="__modti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) -modti3 :: proc "c" (a, b: i128) -> i128 { - s_a := a >> (128 - 1) - s_b := b >> (128 - 1) - an := (a ~ s_a) - s_a - bn := (b ~ s_b) - s_b - - r: u128 = --- - _ = udivmod128(u128(an), u128(bn), &r) - return (i128(r) ~ s_a) - s_a -} - - -@(link_name="__divmodti4", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) -divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 { - s_a := a >> (128 - 1) // -1 if negative or 0 - s_b := b >> (128 - 1) - an := (a ~ s_a) - s_a // absolute - bn := (b ~ s_b) - s_b - - s_b ~= s_a // quotient sign - u_s_b := u128(s_b) - u_s_a := u128(s_a) - - r: u128 = --- - u := i128((udivmodti4(u128(an), u128(bn), &r) ~ u_s_b) - u_s_b) // negate if negative - rem^ = i128((r ~ u_s_a) - u_s_a) - return u -} - -@(link_name="__divti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) -divti3 :: proc "c" (a, b: i128) -> i128 { - s_a := a >> (128 - 1) // -1 if negative or 0 - s_b := b >> (128 - 1) - an := (a ~ s_a) - s_a // absolute - bn := (b ~ s_b) - s_b - - s_a ~= s_b // quotient sign - u_s_a := u128(s_a) - - return i128((udivmodti4(u128(an), u128(bn), nil) ~ u_s_a) - u_s_a) // negate if negative -} - - -@(link_name="__fixdfti", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) -fixdfti :: proc "c" (a: u64) -> i128 { - significandBits :: 52 - typeWidth :: (size_of(u64)*8) - exponentBits :: (typeWidth - significandBits - 1) - maxExponent :: ((1 << exponentBits) - 1) - exponentBias :: (maxExponent >> 1) - - implicitBit :: (u64(1) << significandBits) - significandMask :: (implicitBit - 1) - signBit :: (u64(1) << (significandBits + exponentBits)) - absMask :: (signBit - 1) - exponentMask :: (absMask ~ significandMask) - - // Break a into sign, exponent, significand - aRep := a - aAbs := aRep & absMask - sign := i128(-1 if aRep & signBit != 0 else 1) - exponent := u64((aAbs >> significandBits) - exponentBias) - significand := u64((aAbs & significandMask) | implicitBit) - - // If exponent is negative, the result is zero. - if exponent < 0 { - return 0 - } - - // If the value is too large for the integer type, saturate. - if exponent >= size_of(i128) * 8 { - return max(i128) if sign == 1 else min(i128) - } - - // If 0 <= exponent < significandBits, right shift to get the result. - // Otherwise, shift left. - if exponent < significandBits { - return sign * i128(significand >> (significandBits - exponent)) - } else { - return sign * (i128(significand) << (exponent - significandBits)) - } - -} - - when .Address in ODIN_SANITIZER_FLAGS { foreign { @(require) diff --git a/base/runtime/internal_i128.odin b/base/runtime/internal_i128.odin new file mode 100644 index 000000000..f4a0f90ed --- /dev/null +++ b/base/runtime/internal_i128.odin @@ -0,0 +1,217 @@ +#+vet !cast +#+build !bedrock +package runtime + +import "base:intrinsics" + +@(private="file") +IS_WASM :: ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 + +@(link_name="__floattidf", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) +floattidf :: proc "c" (a: i128) -> f64 { + DBL_MANT_DIG :: 53 + if a == 0 { + return 0.0 + } + a := a + N :: size_of(i128) * 8 + s := a >> (N-1) + a = (a ~ s) - s + sd: = N - intrinsics.count_leading_zeros(a) // number of significant digits + e := i32(sd - 1) // exponent + if sd > DBL_MANT_DIG { + switch sd { + case DBL_MANT_DIG + 1: + a <<= 1 + case DBL_MANT_DIG + 2: + // okay + case: + a = i128(u128(a) >> u128(sd - (DBL_MANT_DIG+2))) | + i128(u128(a) & (~u128(0) >> u128(N + DBL_MANT_DIG+2 - sd)) != 0) + } + + a |= i128((a & 4) != 0) + a += 1 + a >>= 2 + + if a & (i128(1) << DBL_MANT_DIG) != 0 { + a >>= 1 + e += 1 + } + } else { + a <<= u128(DBL_MANT_DIG - sd) & 127 + } + fb: [2]u32 + fb[1] = (u32(s) & 0x80000000) | // sign + (u32(e + 1023) << 20) | // exponent + u32((u64(a) >> 32) & 0x000FFFFF) // mantissa-high + fb[0] = u32(a) // mantissa-low + return transmute(f64)fb +} + + +@(link_name="__floattidf_unsigned", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) +floattidf_unsigned :: proc "c" (a: u128) -> f64 { + DBL_MANT_DIG :: 53 + if a == 0 { + return 0.0 + } + a := a + N :: size_of(u128) * 8 + sd: = N - intrinsics.count_leading_zeros(a) // number of significant digits + e := i32(sd - 1) // exponent + if sd > DBL_MANT_DIG { + switch sd { + case DBL_MANT_DIG + 1: + a <<= 1 + case DBL_MANT_DIG + 2: + // okay + case: + a = u128(u128(a) >> u128(sd - (DBL_MANT_DIG+2))) | + u128(u128(a) & (~u128(0) >> u128(N + DBL_MANT_DIG+2 - sd)) != 0) + } + + a |= u128((a & 4) != 0) + a += 1 + a >>= 2 + + if a & (1 << DBL_MANT_DIG) != 0 { + a >>= 1 + e += 1 + } + } else { + a <<= u128(DBL_MANT_DIG - sd) + } + fb: [2]u32 + fb[1] = (0) | // sign + u32((e + 1023) << 20) | // exponent + u32((u64(a) >> 32) & 0x000FFFFF) // mantissa-high + fb[0] = u32(a) // mantissa-low + return transmute(f64)fb +} + + + +@(link_name="__fixunsdfti", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) +fixunsdfti :: #force_no_inline proc "c" (a: f64) -> u128 { + // TODO(bill): implement `fixunsdfti` correctly + x := u64(a) + return u128(x) +} + +@(link_name="__fixunsdfdi", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) +fixunsdfdi :: #force_no_inline proc "c" (a: f64) -> i128 { + // TODO(bill): implement `fixunsdfdi` correctly + x := i64(a) + return i128(x) +} + + + + +@(link_name="__umodti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) +umodti3 :: proc "c" (a, b: u128) -> u128 { + r: u128 = --- + _ = udivmod128(a, b, &r) + return r +} + + +@(link_name="__udivmodti4", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) +udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 { + return udivmod128(a, b, rem) +} + +when !IS_WASM { + @(link_name="__udivti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) + udivti3 :: proc "c" (a, b: u128) -> u128 { + return udivmodti4(a, b, nil) + } +} + + +@(link_name="__modti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) +modti3 :: proc "c" (a, b: i128) -> i128 { + s_a := a >> (128 - 1) + s_b := b >> (128 - 1) + an := (a ~ s_a) - s_a + bn := (b ~ s_b) - s_b + + r: u128 = --- + _ = udivmod128(u128(an), u128(bn), &r) + return (i128(r) ~ s_a) - s_a +} + + +@(link_name="__divmodti4", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) +divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 { + s_a := a >> (128 - 1) // -1 if negative or 0 + s_b := b >> (128 - 1) + an := (a ~ s_a) - s_a // absolute + bn := (b ~ s_b) - s_b + + s_b ~= s_a // quotient sign + u_s_b := u128(s_b) + u_s_a := u128(s_a) + + r: u128 = --- + u := i128((udivmodti4(u128(an), u128(bn), &r) ~ u_s_b) - u_s_b) // negate if negative + rem^ = i128((r ~ u_s_a) - u_s_a) + return u +} + +@(link_name="__divti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) +divti3 :: proc "c" (a, b: i128) -> i128 { + s_a := a >> (128 - 1) // -1 if negative or 0 + s_b := b >> (128 - 1) + an := (a ~ s_a) - s_a // absolute + bn := (b ~ s_b) - s_b + + s_a ~= s_b // quotient sign + u_s_a := u128(s_a) + + return i128((udivmodti4(u128(an), u128(bn), nil) ~ u_s_a) - u_s_a) // negate if negative +} + + +@(link_name="__fixdfti", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE) +fixdfti :: proc "c" (a: u64) -> i128 { + significandBits :: 52 + typeWidth :: (size_of(u64)*8) + exponentBits :: (typeWidth - significandBits - 1) + maxExponent :: ((1 << exponentBits) - 1) + exponentBias :: (maxExponent >> 1) + + implicitBit :: (u64(1) << significandBits) + significandMask :: (implicitBit - 1) + signBit :: (u64(1) << (significandBits + exponentBits)) + absMask :: (signBit - 1) + exponentMask :: (absMask ~ significandMask) + + // Break a into sign, exponent, significand + aRep := a + aAbs := aRep & absMask + sign := i128(-1 if aRep & signBit != 0 else 1) + exponent := u64((aAbs >> significandBits) - exponentBias) + significand := u64((aAbs & significandMask) | implicitBit) + + // If exponent is negative, the result is zero. + if exponent < 0 { + return 0 + } + + // If the value is too large for the integer type, saturate. + if exponent >= size_of(i128) * 8 { + return max(i128) if sign == 1 else min(i128) + } + + // If 0 <= exponent < significandBits, right shift to get the result. + // Otherwise, shift left. + if exponent < significandBits { + return sign * i128(significand >> (significandBits - exponent)) + } else { + return sign * (i128(significand) << (exponent - significandBits)) + } + +} + diff --git a/base/runtime/udivmod128.odin b/base/runtime/udivmod128.odin index 8cc70df55..107dc605d 100644 --- a/base/runtime/udivmod128.odin +++ b/base/runtime/udivmod128.odin @@ -1,3 +1,4 @@ +#+build !bedrock package runtime import "base:intrinsics" diff --git a/core/crypto/argon2id/argon2id.odin b/core/crypto/argon2id/argon2id.odin index 3bff5a3a9..e2f5e487b 100644 --- a/core/crypto/argon2id/argon2id.odin +++ b/core/crypto/argon2id/argon2id.odin @@ -143,7 +143,7 @@ derive :: proc( m_ := 4 * u64(p) * (m / u64(4 * p)) b := mem.alloc_bytes_non_zeroed( int(m_) * BLOCK_SIZE_BYTES, - alignment = mem.DEFAULT_PAGE_SIZE, + alignment = mem.PAGE_SIZE, allocator = allocator, ) or_return defer delete(b, allocator) diff --git a/core/encoding/json/types.odin b/core/encoding/json/types.odin index 77cc7db85..6b6d8aae9 100644 --- a/core/encoding/json/types.odin +++ b/core/encoding/json/types.odin @@ -113,6 +113,7 @@ destroy_value :: proc(value: Value, allocator := context.allocator, loc := #call } clone_value :: proc(value: Value, allocator := context.allocator) -> Value { + value := value context.allocator = allocator #partial switch &v in value { diff --git a/core/encoding/json/unparse.odin b/core/encoding/json/unparse.odin new file mode 100644 index 000000000..1b00f1b0e --- /dev/null +++ b/core/encoding/json/unparse.odin @@ -0,0 +1,89 @@ +package encoding_json + +import "base:runtime" +import "core:strings" +import "core:io" +import "core:slice" + +Unparse_Error :: union #shared_nil { + io.Error, + runtime.Allocator_Error, +} + +@(require_results) +unparse :: proc(v: Value, opt: Marshal_Options = {}, allocator := context.allocator, loc := #caller_location) -> (data: string, err: Unparse_Error) { + b := strings.builder_make(allocator, loc) + defer if err != nil { + strings.builder_destroy(&b) + } + + // temp guard in case we are sorting map keys, which will use temp allocations + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = allocator == context.temp_allocator) + + opt := opt + unparse_to_builder(&b, v, &opt) or_return + data = string(b.buf[:]) + return +} + +@(require_results) +unparse_to_builder :: proc(b: ^strings.Builder, v: Value, opt: ^Marshal_Options) -> Unparse_Error { + return unparse_to_writer(strings.to_writer(b), v, opt) +} + +@(require_results) +unparse_to_writer :: proc(w: io.Writer, value: Value, opt: ^Marshal_Options) -> Unparse_Error { + switch v in value { + case nil, Null: + io.write_string(w, "null") or_return + case Integer: + base := 16 if opt.write_uint_as_hex && (opt.spec == .JSON5 || opt.spec == .MJSON) else 10 + io.write_i64(w, v, base) or_return + case Float: + io.write_f64(w, v) or_return + case Boolean: + io.write_string(w, "true" if v else "false") or_return + case String: + io.write_quoted_string(w, v, '"', nil, true) or_return + case Array: + opt_write_start(w, opt, '[') or_return + for e, i in v { + opt_write_iteration(w, opt, i == 0) or_return + unparse_to_writer (w, e, opt) or_return + } + opt_write_end(w, opt, ']') or_return + case Object: + if !opt.sort_maps_by_key { + opt_write_start(w, opt, '{') or_return + for first_iteration := true; key, val in v { + opt_write_iteration(w, opt, first_iteration) or_return + opt_write_key (w, opt, key) or_return + unparse_to_writer (w, val, opt) or_return + first_iteration = false + } + opt_write_end(w, opt, '}') or_return + } else { + Map_Entry :: struct { + key: string, + value: Value, + } + + entries := make([dynamic]Map_Entry, 0, len(v), context.temp_allocator) or_return + for key, val in v { + _, _ = append(&entries, Map_Entry{key, val}) + } + + slice.sort_by(entries[:], proc(i, j: Map_Entry) -> bool { return i.key < j.key }) + + opt_write_start(w, opt, '{') or_return + for e, i in entries { + opt_write_iteration(w, opt, i == 0) or_return + opt_write_key (w, opt, e.key) or_return + unparse_to_writer (w, e.value, opt) or_return + } + opt_write_end(w, opt, '}') or_return + } + return nil + } + return nil +} diff --git a/core/flags/doc.odin b/core/flags/doc.odin index 183b86f41..e17cff21b 100644 --- a/core/flags/doc.odin +++ b/core/flags/doc.odin @@ -11,13 +11,21 @@ Command-Line Syntax: Arguments are treated differently depending on how they're formatted. The format is similar to the Odin binary's way of handling compiler flags. - type handling - ------------ ------------------------ - depends on struct layout - - set a bool true - - set flag to option - - set flag to option, alternative syntax - -:= set map[key] to value + type handling + ------------ ------------------------ + depends on struct layout + - set a bool true + - set flag to option + - set flag to option, alternative syntax + - set bit_set flag to one or more options + - set bit_set flag to one or more options, alternative syntax + -:= set map[key] to value + +Underscores (`_`) in a flag will be replaced with dashes (`-`). + +Bit sets may be set with a strictly comma-separated list of options. + +Bit sets may also be set with a binary string of 0s and 1s, and will be parsed from left to right, from least significant bit to most significant bit. Underscores are allowed and will be ignored. However, starting with an underscore is disallowed. Unhandled Arguments: diff --git a/core/flags/example/example.odin b/core/flags/example/example.odin index 6ace3d852..204fca8e9 100644 --- a/core/flags/example/example.odin +++ b/core/flags/example/example.odin @@ -20,6 +20,14 @@ Optimization_Level :: enum { Ludicrous_Speed, } +Vet_Flag :: enum { + Unused, + Unused_Variables, + Unused_Imports, + Shadowing, + Using_Stmt, +} + // It's simple but powerful. my_custom_type_setter :: proc( data: rawptr, @@ -83,6 +91,8 @@ main :: proc() { schedule: datetime.DateTime `usage:"Launch tasks at this time."`, opt: Optimization_Level `usage:"Optimization level."`, + vet_flags: bit_set[Vet_Flag] `usage:"Vet flags. Example usage: + -vet-flags:Unused,Shadowing"`, todo: [dynamic]string `usage:"Todo items."`, accuracy: Fixed_Point1_1 `args:"required" usage:"Lenience in FLOP calculations."`, diff --git a/core/flags/internal_rtti.odin b/core/flags/internal_rtti.odin index 1d86f4bce..8a79f75a8 100644 --- a/core/flags/internal_rtti.odin +++ b/core/flags/internal_rtti.odin @@ -110,7 +110,7 @@ parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info: case f32be: (^f32be)(ptr)^ = cast(f32be) value case f64be: (^f64be)(ptr)^ = cast(f64be) value } - + case runtime.Type_Info_Complex: value := strconv.parse_complex128(str) or_return switch type_info.id { @@ -118,7 +118,7 @@ parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info: case complex64: (^complex64) (ptr)^ = (complex64)(value) case complex128: (^complex128)(ptr)^ = value } - + case runtime.Type_Info_Quaternion: value := strconv.parse_quaternion256(str) or_return switch type_info.id { @@ -152,15 +152,16 @@ parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info: } case runtime.Type_Info_Bit_Set: - // Parse a string of 1's and 0's, from left to right, + // Parse a string of 1s and 0s, from left to right, // least significant bit to most significant bit. value: u128 // NOTE: `upper` is inclusive, i.e: `0..=31` - max_bit_index := u128(1 + specific_type_info.upper - specific_type_info.lower) + underscores := u128(strings.count(str, "_")) + bit_index_limit := u128(1 + specific_type_info.upper - specific_type_info.lower) + underscores bit_index := u128(0) #no_bounds_check for string_index in 0.. Logger { data := new(File_Console_Logger_Data, allocator) data.file_handle = f @@ -74,6 +117,13 @@ create_file_logger :: proc(f: ^os.File, lowest := Level.Debug, opt := Default_Fi return Logger{file_logger_proc, data, lowest, opt} } +/* +Free the state allocated with `create_file_logger` and close the file handle. + +Inputs: +- `log`: Logger created with `create_file_logger` +- `allocator`: Allocator passed to `create_file_logger` (default is `context.allocator`) +*/ destroy_file_logger :: proc(log: Logger, allocator := context.allocator) { data := cast(^File_Console_Logger_Data)log.data if data.file_handle != nil { @@ -82,6 +132,19 @@ destroy_file_logger :: proc(log: Logger, allocator := context.allocator) { free(data, allocator) } +/* +Create a logger that outputs to the terminal. + +*Allocates Using Provided Allocator* + +When no longer needed can be destroyed with `destroy_console_logger`. + +Inputs: +- `lowest`: Log level to use (default is `.Debug`) +- `opt`: Specifies additional data present in the log output (default is `log.Default_Console_Logger_Opts`) +- `ident`: Identifier to include in the output (default is `""`) +- `allocator`: Allocator to use for data backing the logger (default is `context.allocator`) +*/ create_console_logger :: proc(lowest := Level.Debug, opt := Default_Console_Logger_Opts, ident := "", allocator := context.allocator) -> Logger { data := new(File_Console_Logger_Data, allocator) data.file_handle = nil @@ -89,6 +152,13 @@ create_console_logger :: proc(lowest := Level.Debug, opt := Default_Console_Logg return Logger{console_logger_proc, data, lowest, opt} } +/* +Free the state allocated with `create_console_logger`. + +Inputs: +- `log`: Logger created with `create_console_logger` +- `allocator`: Allocator passed to `create_console_logger` (default is `context.allocator`) +*/ destroy_console_logger :: proc(log: Logger, allocator := context.allocator) { free(log.data, allocator) } @@ -117,6 +187,7 @@ _file_console_logger_proc :: proc(h: ^os.File, ident: string, level: Level, text fmt.fprintf(h, "%s%s\n", strings.to_string(buf), text) } + file_logger_proc :: proc(logger_data: rawptr, level: Level, text: string, options: Options, location := #caller_location) { data := cast(^File_Console_Logger_Data)logger_data _file_console_logger_proc(data.file_handle, data.ident, level, text, options, location) @@ -136,6 +207,7 @@ console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string, opt _file_console_logger_proc(h, data.ident, level, text, options, location) } +// Helper used to build the part of the message including the log level. do_level_header :: proc(opts: Options, str: ^strings.Builder, level: Level) { RESET :: ansi.CSI + ansi.RESET + ansi.SGR @@ -162,6 +234,7 @@ do_level_header :: proc(opts: Options, str: ^strings.Builder, level: Level) { } } +// Helper used to build the part of the message including the data and time. do_time_header :: proc(opts: Options, buf: ^strings.Builder, t: time.Time) { when time.IS_SUPPORTED { if Full_Timestamp_Opts & opts != nil { @@ -180,6 +253,7 @@ do_time_header :: proc(opts: Options, buf: ^strings.Builder, t: time.Time) { } } +// Helper used to build the part of the message including the file location. do_location_header :: proc(opts: Options, buf: ^strings.Builder, location := #caller_location) { if Location_Header_Opts & opts == nil { return diff --git a/core/log/log.odin b/core/log/log.odin index 4209a2850..ce0f49124 100644 --- a/core/log/log.odin +++ b/core/log/log.odin @@ -1,52 +1,102 @@ -// Implementations of the `context.Logger` interface. package log import "base:runtime" import "core:fmt" -// 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. +//These are defined in package `base:runtime` as they are used in the `context`. This is to prevent an import definition cycle. /* -Logger_Level :: enum { - Debug = 0, - Info = 10, - Warning = 20, - Error = 30, - Fatal = 40, -} + Logger_Level :: enum { + Debug = 0, + Info = 10, + Warning = 20, + Error = 30, + Fatal = 40, + } */ Level :: runtime.Logger_Level /* -Option :: enum { - Level, - Date, - Time, - Short_File_Path, - Long_File_Path, - Line, - Procedure, - Terminal_Color -} +Specifies additional data present in the log output. + +Defined in `package runtime` as it is used in the `context`. This is to prevent an import definition cycle. + + Option :: enum { + // The log level, e.g. "[DEBUG] ---" + Level, + // The date, e.g. [2025-01-02] + Date, + // The time, e.g. [12:34:56] + Time, + // Just the filename, e.g. [main.odin] + Short_File_Path, + // Full file path, e.g. [/tmp/project/main.odin] + Long_File_Path, + // File line of the log statement, e.g. [8] + Line, + // Calling procedure, e.g. [main()] + Procedure, + // Enables colored output + Terminal_Color + } */ Option :: runtime.Logger_Option /* -Options :: bit_set[Option]; +Specifies additional data present in the log output. + +Defined in `package runtime` as it is used in the `context`. This is to prevent an import definition cycle. + + Options :: bit_set[Option]; */ Options :: runtime.Logger_Options +/* +A preset option set for a logger. + +When you use this set of options you can expect the following output: + + [YYYY-MM-DD HH:MM:SS] Message + +For example: + + [2025-01-02 12:34:56] Hello World! +*/ Full_Timestamp_Opts :: Options{ .Date, .Time, } + +/* +A preset option set for a logger. + +When you use this set of options you can expect the following output: + + [file.odin:L:proc()] Message + +For example: + + [main.odin:8:main()] Hello World! +*/ Location_Header_Opts :: Options{ .Short_File_Path, .Long_File_Path, .Line, .Procedure, } + +/* +A preset option set for a logger. + +When you use this set of options you can expect the following output: + + [file.odin] Message + +For example: + + [main.odin] Hello World! +*/ Location_File_Opts :: Options{ .Short_File_Path, .Long_File_Path, @@ -54,67 +104,206 @@ Location_File_Opts :: Options{ /* -Logger_Proc :: #type proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location); +Implementation of the logger. + +Defined in `package runtime` as it is used in the `context`. This is to prevent an import definition cycle. + + Logger_Proc :: #type proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location); + */ Logger_Proc :: runtime.Logger_Proc /* -Logger :: struct { - procedure: Logger_Proc, - data: rawptr, - lowest_level: Level, - options: Logger_Options, -} +Data backing the logger. + +Defined in `package runtime` as it is used in the `context`. This is to prevent an import definition cycle. + + Logger :: struct { + // Implementation + procedure: Logger_Proc, + // Configuration data passed to the implementation + data: rawptr, + // Minimum level for messages passed to the implementation + lowest_level: Level, + // Additional data present in the log output + options: Logger_Options, + } */ Logger :: runtime.Logger +/* +Do nothing. + +Defined in `package runtime` as it is used in the `context`. This is to prevent an import definition cycle. +*/ nil_logger_proc :: runtime.default_logger_proc +/* +Create a logger that does nothing. + +Returns: +- A logger that does nothing +*/ nil_logger :: proc() -> Logger { return Logger{nil_logger_proc, nil, Level.Debug, nil} } + +/* +Log a formatted message at the `Debug` level. + +Inputs: +- `fmt_str`: A format string, e.g. `"a: %v, b: %v" +- `args`: Arguments for the format string +- `location`: Location of the caller (default is #caller_location) +*/ debugf :: proc(fmt_str: string, args: ..any, location := #caller_location) { logf(.Debug, fmt_str, ..args, location=location) } + +/* +Log a formatted message at the `Info` level. + +Inputs: +- `fmt_str`: A format string, e.g. `"a: %v, b: %v" +- `args`: Arguments for the format string +- `location`: Location of the caller (default is #caller_location) +*/ infof :: proc(fmt_str: string, args: ..any, location := #caller_location) { logf(.Info, fmt_str, ..args, location=location) } + +/* +Log a formatted message at the `Warn` level. + +Inputs: +- `fmt_str`: A format string, e.g. `"a: %v, b: %v" +- `args`: Arguments for the format string +- `location`: Location of the caller (default is #caller_location) +*/ warnf :: proc(fmt_str: string, args: ..any, location := #caller_location) { logf(.Warning, fmt_str, ..args, location=location) } + +/* +Log a formatted message at the `Error` level. + +Inputs: +- `fmt_str`: A format string, e.g. `"a: %v, b: %v" +- `args`: Arguments for the format string +- `location`: Location of the caller (default is #caller_location) +*/ errorf :: proc(fmt_str: string, args: ..any, location := #caller_location) { logf(.Error, fmt_str, ..args, location=location) } + +/* +Log a formatted message at the `Fatal` level. + +Inputs: +- `fmt_str`: A format string, e.g. `"a: %v, b: %v" +- `args`: Arguments for the format string +- `location`: Location of the caller (default is #caller_location) +*/ fatalf :: proc(fmt_str: string, args: ..any, location := #caller_location) { logf(.Fatal, fmt_str, ..args, location=location) } +/* +Log a message at the `Debug` level. + +Inputs: +- `args`: values to be concatenated into the output +- `sep`: separator to use when concatenating (default is `" "`) +- `location`: Location of the caller (default is #caller_location) +*/ debug :: proc(args: ..any, sep := " ", location := #caller_location) { log(.Debug, ..args, sep=sep, location=location) } + +/* +Log a message at the `Info` level. + +Inputs: +- `args`: values to be concatenated into the output +- `sep`: separator to use when concatenating (default is `" "`) +- `location`: Location of the caller (default is #caller_location) +*/ info :: proc(args: ..any, sep := " ", location := #caller_location) { log(.Info, ..args, sep=sep, location=location) } + +/* +Log a message at the `Warn` level. + +Inputs: +- `args`: values to be concatenated into the output +- `sep`: separator to use when concatenating (default is `" "`) +- `location`: Location of the caller (default is #caller_location) +*/ warn :: proc(args: ..any, sep := " ", location := #caller_location) { log(.Warning, ..args, sep=sep, location=location) } + +/* +Log a message at the `Error` level. + +Inputs: +- `args`: values to be concatenated into the output +- `sep`: separator to use when concatenating (default is `" "`) +- `location`: Location of the caller (default is #caller_location) +*/ error :: proc(args: ..any, sep := " ", location := #caller_location) { log(.Error, ..args, sep=sep, location=location) } + +/* +Log a message at the `Fatal` level. + +Inputs: +- `args`: values to be concatenated into the output +- `sep`: separator to use when concatenating (default is `" "`) +- `location`: Location of the caller (default is #caller_location) +*/ fatal :: proc(args: ..any, sep := " ", location := #caller_location) { log(.Fatal, ..args, sep=sep, location=location) } +/* +Log a message at the `Fatal` level and abort the program. + +Inputs: +- `args`: values to be concatenated into the output +- `location`: Location of the caller (default is #caller_location) +*/ panic :: proc(args: ..any, location := #caller_location) -> ! { log(.Fatal, ..args, location=location) runtime.panic("log.panic", location) } + +/* +Log a formatted message at the `Fatal` level and abort the program. + +Inputs: +- `fmt_str`: A format string, e.g. `"a: %v, b: %v" +- `args`: Arguments for the format string +- `location`: Location of the caller (default is #caller_location) +*/ panicf :: proc(fmt_str: string, args: ..any, location := #caller_location) -> ! { logf(.Fatal, fmt_str, ..args, location=location) runtime.panic("log.panicf", location) } +/* +When condition is `false` log a message at the `Fatal` level and abort the program. + +Can be disabled using `ODIN_DISABLE_ASSERT`. + +Inputs: +- `condition`: A boolean to check +- `message`: Message to log when condition is false (a default is provided) +- `loc`: Location of the caller (default is #caller_location) +*/ @(disabled=ODIN_DISABLE_ASSERT) assert :: proc(condition: bool, message := #caller_expression(condition), loc := #caller_location) { if !condition { @@ -131,6 +320,17 @@ assert :: proc(condition: bool, message := #caller_expression(condition), loc := } } +/* +When condition is `false` log a formatted message at the `Fatal` level and abort the program. + +Can be disabled using `ODIN_DISABLE_ASSERT`. + +Inputs: +- `condition`: A boolean to check +- `fmt_str`: A format string to use when condition is false, e.g. `"a: %v, b: %v" +- `args`: Arguments for the format string +- `loc`: Location of the caller (default is #caller_location) +*/ @(disabled=ODIN_DISABLE_ASSERT) assertf :: proc(condition: bool, fmt_str: string, args: ..any, loc := #caller_location) { if !condition { @@ -152,6 +352,16 @@ assertf :: proc(condition: bool, fmt_str: string, args: ..any, loc := #caller_lo } } +/* +When condition is `false` log a message at the `Fatal` level and abort the program. + +Unlike `assert` this procedure cannot be disabled with `ODIN_DISABLE_ASSERT` and will always execute. + +Inputs: +- `condition`: A boolean to check +- `message`: Message to log when condition is false (a default is provided) +- `loc`: Location of the caller (default is #caller_location) +*/ ensure :: proc(condition: bool, message := #caller_expression(condition), loc := #caller_location) { if !condition { @(cold) @@ -167,6 +377,17 @@ ensure :: proc(condition: bool, message := #caller_expression(condition), loc := } } +/* +When condition is `false` log a formatted message at the `Fatal` level and abort the program. + +Unlike `assertf` this procedure cannot be disabled with `ODIN_DISABLE_ASSERT` and will always execute. + +Inputs: +- `condition`: A boolean to check +- `fmt_str`: A format string to use when condition is false, e.g. `"a: %v, b: %v" +- `args`: Arguments for the format string +- `loc`: Location of the caller (default is #caller_location) +*/ ensuref :: proc(condition: bool, fmt_str: string, args: ..any, loc := #caller_location) { if !condition { @(cold) @@ -184,7 +405,15 @@ ensuref :: proc(condition: bool, fmt_str: string, args: ..any, loc := #caller_lo } +/* +Log a message at the desired level. +Inputs: +- `level`: The level of the message +- `args`: values to be concatenated into the output +- `sep`: separator to use when concatenating (default is `" "`) +- `location`: Location of the caller (default is #caller_location) +*/ log :: proc(level: Level, args: ..any, sep := " ", location := #caller_location) { logger := context.logger if logger.procedure == nil || logger.procedure == nil_logger_proc { @@ -198,6 +427,15 @@ log :: proc(level: Level, args: ..any, sep := " ", location := #caller_location) logger.procedure(logger.data, level, str, logger.options, location) } +/* +Log a formatted message at the desired level. + +Inputs: +- `level`: The level of the message +- `fmt_str`: A format string, e.g. `"a: %v, b: %v" +- `args`: Arguments for the format string +- `location`: Location of the caller (default is #caller_location) +*/ logf :: proc(level: Level, fmt_str: string, args: ..any, location := #caller_location) { logger := context.logger if logger.procedure == nil || logger.procedure == nil_logger_proc { diff --git a/core/log/log_allocator.odin b/core/log/log_allocator.odin index c4c54f38f..a6784133b 100644 --- a/core/log/log_allocator.odin +++ b/core/log/log_allocator.odin @@ -6,6 +6,7 @@ import "base:runtime" import "core:fmt" import "core:sync" +// Format to use when logging allocations. Log_Allocator_Format :: enum { Bytes, // Actual number of bytes. Human, // Bytes in human units like bytes, kibibytes, etc. as appropriate. @@ -15,13 +16,23 @@ Log_Allocator_Format :: enum { // Log_Allocator is an allocator which calls `context.logger` on each of its allocations operations. // The format can be changed by setting the `size_fmt: Log_Allocator_Format` field to either `Bytes` or `Human`. Log_Allocator :: struct { - allocator: runtime.Allocator, - level: Level, - prefix: string, + allocator: runtime.Allocator, // Wrapped allocator + level: Level, // Log Level used for allocations + prefix: string, // Prefix to use in log messages lock: sync.Mutex, - size_fmt: Log_Allocator_Format, + size_fmt: Log_Allocator_Format, // Format to use when logging allocations } +/* +Initialize the backing data for the allocator that logs all allocations. + +Inputs: +- `la`: Pointer to the data structure to initialize +- `level`: Log level to use for allocations +- `size_fmt`: Format to use when logging allocations (default is `.Bytes`) +- `allocator`: Wrapped allocator (default is `context.allocator`) +- `prefix`: Prefix to use in log messages (default is `""`) +*/ log_allocator_init :: proc(la: ^Log_Allocator, level: Level, size_fmt := Log_Allocator_Format.Bytes, allocator := context.allocator, prefix := "") { la.allocator = allocator @@ -31,7 +42,15 @@ log_allocator_init :: proc(la: ^Log_Allocator, level: Level, size_fmt := Log_All la.size_fmt = size_fmt } +/* +Create an allocator that logs all allocations. +Inputs: +- `la`: Pointer to the data structure backing the allocator + +Returns: +- An allocator that logs all allocations +*/ log_allocator :: proc(la: ^Log_Allocator) -> runtime.Allocator { return runtime.Allocator{ procedure = log_allocator_proc, @@ -39,6 +58,7 @@ log_allocator :: proc(la: ^Log_Allocator) -> runtime.Allocator { } } +// Backing procedure for allocator that logs all allocations. log_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode, size, alignment: int, old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, runtime.Allocator_Error) { diff --git a/core/log/multi_logger.odin b/core/log/multi_logger.odin index 6122554bb..99d7377db 100644 --- a/core/log/multi_logger.odin +++ b/core/log/multi_logger.odin @@ -1,10 +1,27 @@ package log +// A container backing for multiple loggers. Multi_Logger_Data :: struct { loggers: []Logger, } +/* +Create a logger that logs to all backing loggers. + +*Allocates Using Provided Allocator* + +When no longer needed can be destroyed with `destroy_multi_logger`. + +Note: Logs using a multi logger take both the multi logger and the backing loggers' log levels into account. + +Inputs: +- `logs` - Backing loggers passed as multiple arguments +- `allocator` - An allocator used to allocate data to store backing loggers (default is `context.allocator`) + +Returns: +- A multi logger +*/ create_multi_logger :: proc(logs: ..Logger, allocator := context.allocator) -> Logger { data := new(Multi_Logger_Data, allocator) data.loggers = make([]Logger, len(logs), allocator) @@ -12,12 +29,20 @@ create_multi_logger :: proc(logs: ..Logger, allocator := context.allocator) -> L return Logger{multi_logger_proc, data, Level.Debug, nil} } +/* +Free the state allocated with `create_multi_logger`. + +Inputs: +- `log`: Logger created with `create_multi_logger` +- `allocator`: Allocator passed to `create_multi_logger` (default is `context.allocator`) +*/ destroy_multi_logger :: proc(log: Logger, allocator := context.allocator) { data := (^Multi_Logger_Data)(log.data) delete(data.loggers, allocator) free(data, allocator) } +// Backing procedure for the multi logger. multi_logger_proc :: proc(logger_data: rawptr, level: Level, text: string, options: Options, location := #caller_location) { data := cast(^Multi_Logger_Data)logger_data diff --git a/core/math/math.odin b/core/math/math.odin index 1ecb1da00..6fe872907 100644 --- a/core/math/math.odin +++ b/core/math/math.odin @@ -1613,17 +1613,11 @@ is_power_of_two :: proc "contextless" (x: int) -> bool { @(require_results) next_power_of_two :: proc "contextless" (x: int) -> int { - k := x -1 - when size_of(int) == 8 { - k = k | (k >> 32) + if x <= 1 { + return 1 } - k = k | (k >> 16) - k = k | (k >> 8) - k = k | (k >> 4) - k = k | (k >> 2) - k = k | (k >> 1) - k += 1 + int(x <= 0) - return k + n := uint(size_of(x) * 8) - uint(intrinsics.count_leading_zeros(uint(x) - 1)) + return int(1) << n } @(require_results) diff --git a/core/mem/alloc.odin b/core/mem/alloc.odin index 48cc39245..282c8d0b4 100644 --- a/core/mem/alloc.odin +++ b/core/mem/alloc.odin @@ -255,11 +255,10 @@ 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. +On platforms where we were able to query a configurable size, we use that value instead. +See `query_page_size_init()` */ -DEFAULT_PAGE_SIZE :: +PAGE_SIZE: int = 64 * 1024 when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 else 16 * 1024 when ODIN_OS == .Darwin && ODIN_ARCH == .arm64 else 4 * 1024 diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index 68b6102b6..dcd126230 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -1621,20 +1621,6 @@ small_stack_allocator_proc :: proc( return nil, nil } - -/* 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_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 - /* Default block size for dynamic arena. */ @@ -1651,7 +1637,7 @@ Dynamic arena allocator data. Dynamic_Arena :: struct { block_size: int, out_band_size: int, - alignment: int, + minimum_alignment: int, unused_blocks: [dynamic]rawptr, used_blocks: [dynamic]rawptr, out_band_allocations: [dynamic]rawptr, @@ -1668,23 +1654,23 @@ This procedure initializes a dynamic arena. The specified `block_allocator` will be used to allocate arena blocks, and `array_allocator` to allocate arrays of blocks and out-band blocks. The blocks have the default size of `block_size` and out-band threshold will be `out_band_size`. All allocations -will be aligned to a boundary specified by `alignment`. +will be aligned at a minimum to a boundary specified by `minimum_alignment`. */ dynamic_arena_init :: proc( - 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, + arena: ^Dynamic_Arena, + block_allocator := context.allocator, + array_allocator := context.allocator, + block_size := DYNAMIC_ARENA_BLOCK_SIZE_DEFAULT, + out_band_size := DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT, + minimum_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 + arena.block_size = block_size + arena.out_band_size = out_band_size + arena.minimum_alignment = minimum_alignment + arena.block_allocator = block_allocator + arena.out_band_allocations.allocator = array_allocator + arena.unused_blocks.allocator = array_allocator + arena.used_blocks.allocator = array_allocator } /* @@ -1728,7 +1714,7 @@ dynamic_arena_destroy :: proc(a: ^Dynamic_Arena) { } @(private="file") -_dynamic_arena_cycle_new_block :: proc(a: ^Dynamic_Arena, loc := #caller_location) -> (err: Allocator_Error) { +_dynamic_arena_cycle_new_block :: proc(a: ^Dynamic_Arena, alignment: int, loc := #caller_location) -> (err: Allocator_Error) { if a.block_allocator.procedure == nil { panic("You must call `dynamic_arena_init` on a Dynamic Arena before using it.", loc) } @@ -1744,7 +1730,7 @@ _dynamic_arena_cycle_new_block :: proc(a: ^Dynamic_Arena, loc := #caller_locatio a.block_allocator.data, Allocator_Mode.Alloc, a.block_size, - a.alignment, + max(a.minimum_alignment, alignment), nil, 0, ) @@ -1766,8 +1752,8 @@ zero-initialized. This procedure returns a pointer to the newly allocated memory region. */ @(require_results) -dynamic_arena_alloc :: proc(a: ^Dynamic_Arena, size: int, loc := #caller_location) -> (rawptr, Allocator_Error) { - data, err := dynamic_arena_alloc_bytes(a, size, loc) +dynamic_arena_alloc :: proc(a: ^Dynamic_Arena, size: int, alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location) -> (rawptr, Allocator_Error) { + data, err := dynamic_arena_alloc_bytes(a, size, alignment, loc) return raw_data(data), err } @@ -1780,8 +1766,8 @@ zero-initialized. This procedure returns a slice of the newly allocated memory region. */ @(require_results) -dynamic_arena_alloc_bytes :: proc(a: ^Dynamic_Arena, size: int, loc := #caller_location) -> ([]byte, Allocator_Error) { - bytes, err := dynamic_arena_alloc_bytes_non_zeroed(a, size, loc) +dynamic_arena_alloc_bytes :: proc(a: ^Dynamic_Arena, size: int, alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location) -> ([]byte, Allocator_Error) { + bytes, err := dynamic_arena_alloc_bytes_non_zeroed(a, size, alignment, loc) if bytes != nil { zero_slice(bytes) } @@ -1797,8 +1783,8 @@ zero-initialized. This procedure returns a pointer to the newly allocated memory region. */ @(require_results) -dynamic_arena_alloc_non_zeroed :: proc(a: ^Dynamic_Arena, size: int, loc := #caller_location) -> (rawptr, Allocator_Error) { - data, err := dynamic_arena_alloc_bytes_non_zeroed(a, size, loc) +dynamic_arena_alloc_non_zeroed :: proc(a: ^Dynamic_Arena, size: int, alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location) -> (rawptr, Allocator_Error) { + data, err := dynamic_arena_alloc_bytes_non_zeroed(a, size, alignment, loc) return raw_data(data), err } @@ -1811,31 +1797,35 @@ zero-initialized. This procedure returns a slice of the newly allocated memory region. */ @(require_results) -dynamic_arena_alloc_bytes_non_zeroed :: proc(a: ^Dynamic_Arena, size: int, loc := #caller_location) -> ([]byte, Allocator_Error) { +dynamic_arena_alloc_bytes_non_zeroed :: proc(a: ^Dynamic_Arena, size: int, alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location) -> ([]byte, Allocator_Error) { if size >= a.out_band_size { assert(a.out_band_allocations.allocator.procedure != nil, "Backing array allocator must be initialized", loc=loc) - memory, err := alloc_bytes_non_zeroed(size, a.alignment, a.out_band_allocations.allocator, loc) + memory, err := alloc_bytes_non_zeroed(size, alignment, a.out_band_allocations.allocator, loc) if memory != nil { append(&a.out_band_allocations, raw_data(memory), loc = loc) } return memory, err } - n := align_formula(size, a.alignment) + actual_alignment := max(a.minimum_alignment, alignment) + n := align_formula(size, actual_alignment) if n > a.block_size { return nil, .Invalid_Argument } - if a.bytes_left < n { - err := _dynamic_arena_cycle_new_block(a, loc) + memory := align_forward(a.current_pos, uintptr(actual_alignment)) + margin := int(uintptr(memory) - uintptr(a.current_pos)) + if a.bytes_left < margin + n { + err := _dynamic_arena_cycle_new_block(a, alignment, loc) if err != nil { return nil, err } if a.current_block == nil { return nil, .Out_Of_Memory } + margin = 0 + memory = a.current_pos } - memory := a.current_pos - a.current_pos = ([^]byte)(a.current_pos)[n:] - a.bytes_left -= n + a.current_pos = ([^]byte)(memory)[n:] + a.bytes_left -= margin + n result := ([^]byte)(memory)[:size] // ensure_poisoned(result) // sanitizer.address_unpoison(result) @@ -1900,9 +1890,10 @@ dynamic_arena_resize :: proc( old_memory: rawptr, old_size: int, size: int, + alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location, ) -> (rawptr, Allocator_Error) { - bytes, err := dynamic_arena_resize_bytes(a, byte_slice(old_memory, old_size), size, loc) + bytes, err := dynamic_arena_resize_bytes(a, byte_slice(old_memory, old_size), size, alignment, loc) return raw_data(bytes), err } @@ -1921,16 +1912,17 @@ This procedure returns the slice of the resized memory region. */ @(require_results) dynamic_arena_resize_bytes :: proc( - a: ^Dynamic_Arena, - old_data: []byte, - size: int, + a: ^Dynamic_Arena, + old_data: []byte, + size: int, + alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location, ) -> ([]byte, Allocator_Error) { if size == 0 { // NOTE: This allocator has no Free mode. return nil, nil } - bytes, err := dynamic_arena_resize_bytes_non_zeroed(a, old_data, size, loc) + bytes, err := dynamic_arena_resize_bytes_non_zeroed(a, old_data, size, alignment, loc) if bytes != nil { if old_data == nil { zero_slice(bytes) @@ -1960,9 +1952,10 @@ dynamic_arena_resize_non_zeroed :: proc( old_memory: rawptr, old_size: int, size: int, + alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location, ) -> (rawptr, Allocator_Error) { - bytes, err := dynamic_arena_resize_bytes_non_zeroed(a, byte_slice(old_memory, old_size), size, loc) + bytes, err := dynamic_arena_resize_bytes_non_zeroed(a, byte_slice(old_memory, old_size), size, alignment, loc) return raw_data(bytes), err } @@ -1981,9 +1974,10 @@ This procedure returns the slice of the resized memory region. */ @(require_results) dynamic_arena_resize_bytes_non_zeroed :: proc( - a: ^Dynamic_Arena, - old_data: []byte, - size: int, + a: ^Dynamic_Arena, + old_data: []byte, + size: int, + alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location, ) -> ([]byte, Allocator_Error) { if size == 0 { @@ -1998,7 +1992,7 @@ dynamic_arena_resize_bytes_non_zeroed :: proc( } // No information is kept about allocations in this allocator, thus we // cannot truly resize anything and must reallocate. - data, err := dynamic_arena_alloc_bytes_non_zeroed(a, size, loc) + data, err := dynamic_arena_alloc_bytes_non_zeroed(a, size, alignment, loc) if err == nil { runtime.copy(data, byte_slice(old_memory, old_size)) } @@ -2017,17 +2011,17 @@ dynamic_arena_allocator_proc :: proc( arena := (^Dynamic_Arena)(allocator_data) switch mode { case .Alloc: - return dynamic_arena_alloc_bytes(arena, size, loc) + return dynamic_arena_alloc_bytes(arena, size, alignment, loc) case .Alloc_Non_Zeroed: - return dynamic_arena_alloc_bytes_non_zeroed(arena, size, loc) + return dynamic_arena_alloc_bytes_non_zeroed(arena, size, alignment, loc) case .Free: return nil, .Mode_Not_Implemented case .Free_All: dynamic_arena_free_all(arena, loc) case .Resize: - return dynamic_arena_resize_bytes(arena, byte_slice(old_memory, old_size), size, loc) + return dynamic_arena_resize_bytes(arena, byte_slice(old_memory, old_size), size, alignment, loc) case .Resize_Non_Zeroed: - return dynamic_arena_resize_bytes_non_zeroed(arena, byte_slice(old_memory, old_size), size, loc) + return dynamic_arena_resize_bytes_non_zeroed(arena, byte_slice(old_memory, old_size), size, alignment, loc) case .Query_Features: set := (^Allocator_Mode_Set)(old_memory) if set != nil { @@ -2038,7 +2032,7 @@ dynamic_arena_allocator_proc :: proc( info := (^Allocator_Query_Info)(old_memory) if info != nil && info.pointer != nil { info.size = arena.block_size - info.alignment = arena.alignment + info.alignment = arena.minimum_alignment return byte_slice(info, size_of(info^)), nil } return nil, nil diff --git a/core/mem/mem_posix.odin b/core/mem/mem_posix.odin new file mode 100644 index 000000000..f652e6a25 --- /dev/null +++ b/core/mem/mem_posix.odin @@ -0,0 +1,13 @@ +#+build linux, darwin, netbsd, freebsd, openbsd +package mem + +import "core:sys/posix" + +@(init, private, no_sanitize_address) +query_page_size_init :: proc "contextless" () { + size := posix.sysconf(._PAGESIZE) + PAGE_SIZE = max(PAGE_SIZE, int(size)) + + // is power of two + assert_contextless(PAGE_SIZE != 0 && (PAGE_SIZE & (PAGE_SIZE-1)) == 0) +} \ No newline at end of file diff --git a/core/mem/mem_windows.odin b/core/mem/mem_windows.odin new file mode 100644 index 000000000..09f14d5d0 --- /dev/null +++ b/core/mem/mem_windows.odin @@ -0,0 +1,16 @@ +#+build windows +package mem + +import "core:sys/windows" + +@(init, private, no_sanitize_address) +query_page_size_init :: proc "contextless" () { + info: windows.SYSTEM_INFO + windows.GetSystemInfo(&info) + + size := info.dwPageSize + PAGE_SIZE = max(PAGE_SIZE, int(size)) + + // is power of two + assert_contextless(PAGE_SIZE != 0 && (PAGE_SIZE & (PAGE_SIZE-1)) == 0) +} \ No newline at end of file diff --git a/core/mem/virtual/arena.odin b/core/mem/virtual/arena.odin index 1ee7cba6c..3d3827885 100644 --- a/core/mem/virtual/arena.odin +++ b/core/mem/virtual/arena.odin @@ -126,11 +126,11 @@ arena_alloc_unguarded :: proc(arena: ^Arena, size: uint, alignment: uint, loc := if err == .Out_Of_Memory { if arena.minimum_block_size == 0 { arena.minimum_block_size = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE - arena.minimum_block_size = mem.align_forward_uint(arena.minimum_block_size, DEFAULT_PAGE_SIZE) + arena.minimum_block_size = mem.align_forward_uint(arena.minimum_block_size, uint(mem.PAGE_SIZE)) } if arena.default_commit_size == 0 { arena.default_commit_size = min(DEFAULT_ARENA_GROWING_COMMIT_SIZE, arena.minimum_block_size) - arena.default_commit_size = mem.align_forward_uint(arena.default_commit_size, DEFAULT_PAGE_SIZE) + arena.default_commit_size = mem.align_forward_uint(arena.default_commit_size, uint(mem.PAGE_SIZE)) } if arena.default_commit_size != 0 { diff --git a/core/mem/virtual/virtual.odin b/core/mem/virtual/virtual.odin index a97e00731..ae6206de7 100644 --- a/core/mem/virtual/virtual.odin +++ b/core/mem/virtual/virtual.odin @@ -6,13 +6,6 @@ import "base:intrinsics" import "base:runtime" _ :: runtime -DEFAULT_PAGE_SIZE := uint(4096) - -@(init, private) -platform_memory_init :: proc "contextless" () { - _platform_memory_init() -} - Allocator_Error :: mem.Allocator_Error @(require_results, no_sanitize_address) @@ -79,7 +72,7 @@ align_formula :: #force_inline proc "contextless" (size, align: uint) -> uint { @(require_results, no_sanitize_address) memory_block_alloc :: proc(committed, reserved: uint, alignment: uint = 0, flags: Memory_Block_Flags = {}) -> (block: ^Memory_Block, err: Allocator_Error) { - page_size := DEFAULT_PAGE_SIZE + page_size := uint(mem.PAGE_SIZE) assert(mem.is_power_of_two(uintptr(page_size))) committed := committed @@ -144,7 +137,7 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: uint, // TODO(bill): determine a better heuristic for this behaviour extra_size := max(size, block.committed>>1) platform_total_commit := base_offset + block.used + extra_size - platform_total_commit = align_formula(platform_total_commit, DEFAULT_PAGE_SIZE) + platform_total_commit = align_formula(platform_total_commit, uint(mem.PAGE_SIZE)) platform_total_commit = min(max(platform_total_commit, default_commit_size), pmblock.reserved) assert(pmblock.committed <= pmblock.reserved) diff --git a/core/mem/virtual/virtual_linux.odin b/core/mem/virtual/virtual_linux.odin index 92f55c4a3..665749489 100644 --- a/core/mem/virtual/virtual_linux.odin +++ b/core/mem/virtual/virtual_linux.odin @@ -43,12 +43,6 @@ _protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) return errno == .NONE } -_platform_memory_init :: proc "contextless" () { - DEFAULT_PAGE_SIZE = 4096 - // is power of two - assert_contextless(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0) -} - _map_file :: proc "contextless" (fd: uintptr, size: i64, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) { prot: linux.Mem_Protection if .Read in flags { diff --git a/core/mem/virtual/virtual_other.odin b/core/mem/virtual/virtual_other.odin index 6f9c1327a..a8ba29d14 100644 --- a/core/mem/virtual/virtual_other.odin +++ b/core/mem/virtual/virtual_other.odin @@ -25,10 +25,6 @@ _protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) return false } -_platform_memory_init :: proc "contextless" () { - -} - _map_file :: proc "contextless" (f: any, size: i64, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) { return nil, .Map_Failure } diff --git a/core/mem/virtual/virtual_posix.odin b/core/mem/virtual/virtual_posix.odin index 6f257c385..623a2b911 100644 --- a/core/mem/virtual/virtual_posix.odin +++ b/core/mem/virtual/virtual_posix.odin @@ -28,15 +28,6 @@ _protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) return posix.mprotect(data, size, transmute(posix.Prot_Flags)flags) == .OK } -_platform_memory_init :: proc "contextless" () { - // NOTE: `posix.PAGESIZE` due to legacy reasons could be wrong so we use `sysconf`. - size := posix.sysconf(._PAGESIZE) - DEFAULT_PAGE_SIZE = uint(max(size, posix.PAGESIZE)) - - // is power of two - assert_contextless(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0) -} - _map_file :: proc "contextless" (fd: uintptr, size: i64, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) { #assert(i32(posix.Prot_Flag_Bits.READ) == i32(Map_File_Flag.Read)) #assert(i32(posix.Prot_Flag_Bits.WRITE) == i32(Map_File_Flag.Write)) diff --git a/core/mem/virtual/virtual_windows.odin b/core/mem/virtual/virtual_windows.odin index 14fc35f62..768eae711 100644 --- a/core/mem/virtual/virtual_windows.odin +++ b/core/mem/virtual/virtual_windows.odin @@ -146,18 +146,6 @@ _protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) return bool(ok) } - -@(no_sanitize_address) -_platform_memory_init :: proc "contextless" () { - sys_info: SYSTEM_INFO - GetSystemInfo(&sys_info) - DEFAULT_PAGE_SIZE = max(DEFAULT_PAGE_SIZE, uint(sys_info.dwPageSize)) - - // is power of two - assert_contextless(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0) -} - - @(no_sanitize_address) _map_file :: proc "contextless" (fd: uintptr, size: i64, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) { page_flags: u32 diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 2339e0d6e..5e681728d 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -1792,6 +1792,13 @@ is_token_field_prefix :: proc(p: ^Parser) -> ast.Field_Flag { advance_token(p) return .Using case .Hash: + if tok := peek_token(p); tok.kind == .Ident { + switch tok.text { + case "simd", "type", "row_major", "column_major", "sparse", "soa": + return .Invalid + } + } + tok: tokenizer.Token advance_token(p) tok = p.curr_tok @@ -2546,6 +2553,17 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF { elem := parse_expr(p, false) + + if p.curr_tok.kind == .Where { + tok_where := expect_token(p, .Where) + cond := parse_expr(p, false) + + be := ast.new(ast.Binary_Expr, elem.pos, end_pos(p.prev_tok)) + be.left = elem + be.op = tok_where + be.right = cond + elem = be + } append(&args, elem) allow_token(p, .Comma) or_break diff --git a/core/os/file_util.odin b/core/os/file_util.odin index 854977b01..94e1a5841 100644 --- a/core/os/file_util.odin +++ b/core/os/file_util.odin @@ -216,18 +216,18 @@ read_entire_file_from_file :: proc(f: ^File, allocator: runtime.Allocator, loc : return } else { buffer: [1024]u8 - out_buffer := make([dynamic]u8, 0, 0, allocator, loc) - total := 0 + out_buffer := make([dynamic]u8, 0, 0, allocator, loc) or_return for { n: int n, err = read(f, buffer[:]) - total += n - append_elems(&out_buffer, ..buffer[:n], loc=loc) or_return + if _, aerr := append_elems(&out_buffer, ..buffer[:n], loc=loc); aerr != nil { + return out_buffer[:], aerr + } if err != nil { if err == .EOF || err == .Broken_Pipe { err = nil } - data = out_buffer[:total] + data = out_buffer[:] return } } diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin index 24205c53c..9b08d7ba6 100644 --- a/core/sys/windows/kernel32.odin +++ b/core/sys/windows/kernel32.odin @@ -571,17 +571,18 @@ foreign kernel32 { DisconnectNamedPipe :: proc(hNamedPipe: HANDLE) -> BOOL --- WaitNamedPipeW :: proc(lpNamedPipeName: LPCWSTR, nTimeOut: DWORD) -> BOOL --- - AllocConsole :: proc() -> BOOL --- - AttachConsole :: proc(dwProcessId: DWORD) -> BOOL --- - SetConsoleCtrlHandler :: proc(HandlerRoutine: PHANDLER_ROUTINE, Add: BOOL) -> BOOL --- - GenerateConsoleCtrlEvent :: proc(dwCtrlEvent: DWORD, dwProcessGroupId: DWORD) -> BOOL --- - FreeConsole :: proc() -> BOOL --- - GetConsoleWindow :: proc() -> HWND --- - GetConsoleScreenBufferInfo :: proc(hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: PCONSOLE_SCREEN_BUFFER_INFO) -> BOOL --- - SetConsoleScreenBufferSize :: proc(hConsoleOutput: HANDLE, dwSize: COORD) -> BOOL --- - SetConsoleWindowInfo :: proc(hConsoleOutput: HANDLE, bAbsolute: BOOL, lpConsoleWindow: ^SMALL_RECT) -> BOOL --- - GetConsoleCursorInfo :: proc(hConsoleOutput: HANDLE, lpConsoleCursorInfo: PCONSOLE_CURSOR_INFO) -> BOOL --- - SetConsoleCursorInfo :: proc(hConsoleOutput: HANDLE, lpConsoleCursorInfo: PCONSOLE_CURSOR_INFO) -> BOOL --- + AllocConsole :: proc() -> BOOL --- + AttachConsole :: proc(dwProcessId: DWORD) -> BOOL --- + SetConsoleCtrlHandler :: proc(HandlerRoutine: PHANDLER_ROUTINE, Add: BOOL) -> BOOL --- + GenerateConsoleCtrlEvent :: proc(dwCtrlEvent: DWORD, dwProcessGroupId: DWORD) -> BOOL --- + FreeConsole :: proc() -> BOOL --- + GetConsoleWindow :: proc() -> HWND --- + GetConsoleScreenBufferInfo :: proc(hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: PCONSOLE_SCREEN_BUFFER_INFO) -> BOOL --- + GetConsoleScreenBufferInfoEx :: proc(hConsoleOutput: HANDLE, lpConsoleScreenBufferInfoEx: PCONSOLE_SCREEN_BUFFER_INFOEX) -> BOOL --- + SetConsoleScreenBufferSize :: proc(hConsoleOutput: HANDLE, dwSize: COORD) -> BOOL --- + SetConsoleWindowInfo :: proc(hConsoleOutput: HANDLE, bAbsolute: BOOL, lpConsoleWindow: ^SMALL_RECT) -> BOOL --- + GetConsoleCursorInfo :: proc(hConsoleOutput: HANDLE, lpConsoleCursorInfo: PCONSOLE_CURSOR_INFO) -> BOOL --- + SetConsoleCursorInfo :: proc(hConsoleOutput: HANDLE, lpConsoleCursorInfo: PCONSOLE_CURSOR_INFO) -> BOOL --- GetDiskFreeSpaceExW :: proc( lpDirectoryName: LPCWSTR, diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin index ab707e43a..0f80e0b18 100644 --- a/core/sys/windows/types.odin +++ b/core/sys/windows/types.odin @@ -3411,9 +3411,21 @@ CONSOLE_READCONSOLE_CONTROL :: struct { dwCtrlWakeupMask: ULONG, dwControlKeyState: ULONG, } - PCONSOLE_READCONSOLE_CONTROL :: ^CONSOLE_READCONSOLE_CONTROL +CONSOLE_SCREEN_BUFFER_INFOEX :: struct { + cbSize: ULONG, + dwSize: COORD, + dwCursorPosition: COORD, + wAttributes: WORD, + srWindow: SMALL_RECT, + dwMaximumWindowSize: COORD, + wPopupAttributes: WORD, + bFullscreenSupported: BOOL, + ColorTable: [16]COLORREF, +} +PCONSOLE_SCREEN_BUFFER_INFOEX :: ^CONSOLE_SCREEN_BUFFER_INFOEX + BY_HANDLE_FILE_INFORMATION :: struct { dwFileAttributes: DWORD, ftCreationTime: FILETIME, @@ -3426,7 +3438,6 @@ BY_HANDLE_FILE_INFORMATION :: struct { nFileIndexHigh: DWORD, nFileIndexLow: DWORD, } - LPBY_HANDLE_FILE_INFORMATION :: ^BY_HANDLE_FILE_INFORMATION FILE_STANDARD_INFO :: struct { diff --git a/core/text/i18n/doc.odin b/core/text/i18n/doc.odin index f316c5a6d..14a095229 100644 --- a/core/text/i18n/doc.odin +++ b/core/text/i18n/doc.odin @@ -51,8 +51,6 @@ Example: Tn :: i18n.get_n mo :: proc() { - using fmt - err: i18n.Error // Parse MO file and set it as the active translation so we can omit `get`'s "catalog" parameter. @@ -62,26 +60,24 @@ Example: if err != .None { return } // These are in the .MO catalog. - println("-----") - println(T("")) - println("-----") - println(T("There are 69,105 leaves here.")) - println("-----") - println(T("Hellope, World!")) - println("-----") + fmt.println("-----") + fmt.println(T("")) + fmt.println("-----") + fmt.println(T("There are 69,105 leaves here.")) + fmt.println("-----") + fmt.println(T("Hellope, World!")) + fmt.println("-----") // We pass 1 into `T` to get the singular format string, then 1 again into printf. - printf(Tn("There is %d leaf.\n", 1), 1) + fmt.printf(Tn("There is %d leaf.\n", 1), 1) // We pass 42 into `T` to get the plural format string, then 42 again into printf. - printf(Tn("There is %d leaf.\n", 42), 42) + fmt.printf(Tn("There is %d leaf.\n", 42), 42) // This isn't in the translation catalog, so the key is passed back untranslated. - println("-----") - println(T("Come visit us on Discord!")) + fmt.println("-----") + fmt.println(T("Come visit us on Discord!")) } qt :: proc() { - using fmt - err: i18n.Error // Parse QT file and set it as the active translation so we can omit `get`'s "catalog" parameter. @@ -91,18 +87,18 @@ Example: if err != .None { return } // These are in the .TS catalog. As you can see they have sections. - println("--- Page section ---") - println("Page:Text for translation =", T("Page", "Text for translation")) - println("-----") - println("Page:Also text to translate =", T("Page", "Also text to translate")) - println("-----") - println("--- installscript section ---") - println("installscript:99 bottles of beer on the wall =", T("installscript", "99 bottles of beer on the wall")) - println("-----") - println("--- apple_count section ---") - println("apple_count:%d apple(s) =") - println("\t 1 =", Tn("apple_count", "%d apple(s)", 1)) - println("\t 42 =", Tn("apple_count", "%d apple(s)", 42)) + fmt.println("--- Page section ---") + fmt.println("Page:Text for translation =", T("Page", "Text for translation")) + fmt.println("-----") + fmt.println("Page:Also text to translate =", T("Page", "Also text to translate")) + fmt.println("-----") + fmt.println("--- installscript section ---") + fmt.println("installscript:99 bottles of beer on the wall =", T("installscript", "99 bottles of beer on the wall")) + fmt.println("-----") + fmt.println("--- apple_count section ---") + fmt.println("apple_count:%d apple(s) =") + fmt.println("\t 1 =", Tn("apple_count", "%d apple(s)", 1)) + fmt.println("\t 42 =", Tn("apple_count", "%d apple(s)", 42)) } */ package i18n diff --git a/core/text/regex/compiler/compiler.odin b/core/text/regex/compiler/compiler.odin index dbfe3fe1c..3b38cc1b8 100644 --- a/core/text/regex/compiler/compiler.odin +++ b/core/text/regex/compiler/compiler.odin @@ -43,9 +43,16 @@ Node_Match_All_And_Escape :: parser.Node_Match_All_And_Escape Opcode :: virtual_machine.Opcode Program :: [dynamic]Opcode -JUMP_SIZE :: size_of(Opcode) + 1 * size_of(u16) -SPLIT_SIZE :: size_of(Opcode) + 2 * size_of(u16) +Jump :: virtual_machine.Jump +Split :: virtual_machine.Split +Wait_For_Byte :: virtual_machine.Wait_For_Byte +Wait_For_Rune :: virtual_machine.Wait_For_Rune +Wait_For_Rune_Class :: virtual_machine.Wait_For_Rune_Class +Wait_For_Rune_Class_Negated :: virtual_machine.Wait_For_Rune_Class_Negated +Save :: virtual_machine.Save +JUMP_SIZE :: size_of(Jump) +SPLIT_SIZE :: size_of(Split) Compiler :: struct { flags: common.Flags, @@ -141,15 +148,13 @@ map_all_classes :: proc(tree: Node, collection: ^[dynamic]Rune_Class_Data) { append_raw :: #force_inline proc(code: ^Program, data: $T) { // NOTE: This is system-dependent endian. - for b in transmute([size_of(T)]byte)data { - append(code, cast(Opcode)b) - } + data := transmute([size_of(T)]Opcode)data + append(code, ..data[:]) } inject_raw :: #force_inline proc(code: ^Program, start: int, data: $T) { // NOTE: This is system-dependent endian. - for b, i in transmute([size_of(T)]byte)data { - inject_at(code, start + i, cast(Opcode)b) - } + data := transmute([size_of(T)]Opcode)data + inject_at(code, start, ..data[:]) } @require_results @@ -220,8 +225,8 @@ generate_code :: proc(c: ^Compiler, node: Node) -> (code: Program) { code = generate_code(c, specific.inner) if specific.capture && .No_Capture not_in c.flags { - inject_at(&code, 0, Opcode.Save) - inject_at(&code, 1, Opcode(2 * specific.capture_id)) + save := Save{.Save, Opcode(2 * specific.capture_id)} + inject_raw(&code, 0, save) append(&code, Opcode.Save) append(&code, Opcode(2 * specific.capture_id + 1)) @@ -236,9 +241,8 @@ generate_code :: proc(c: ^Compiler, node: Node) -> (code: Program) { // Avoiding duplicate allocation by reusing `left`. code = left - inject_at(&code, 0, Opcode.Split) - inject_raw(&code, size_of(byte) , i16(SPLIT_SIZE)) - inject_raw(&code, size_of(byte) + size_of(i16), i16(SPLIT_SIZE + left_len + JUMP_SIZE)) + split := Split{.Split, i16(SPLIT_SIZE), i16(SPLIT_SIZE + left_len + JUMP_SIZE)} + inject_raw(&code, 0, split) append(&code, Opcode.Jump) append_raw(&code, i16(len(right) + JUMP_SIZE)) @@ -259,9 +263,8 @@ generate_code :: proc(c: ^Compiler, node: Node) -> (code: Program) { code = generate_code(c, specific.inner) original_len := len(code) - inject_at(&code, 0, Opcode.Split) - inject_raw(&code, size_of(byte) , i16(SPLIT_SIZE)) - inject_raw(&code, size_of(byte) + size_of(i16), i16(SPLIT_SIZE + original_len + JUMP_SIZE)) + split := Split{.Split, i16(SPLIT_SIZE), i16(SPLIT_SIZE + original_len + JUMP_SIZE)} + inject_raw(&code, 0, split) append(&code, Opcode.Jump) append_raw(&code, i16(-original_len - SPLIT_SIZE)) @@ -270,9 +273,8 @@ generate_code :: proc(c: ^Compiler, node: Node) -> (code: Program) { code = generate_code(c, specific.inner) original_len := len(code) - inject_at(&code, 0, Opcode.Split) - inject_raw(&code, size_of(byte) , i16(SPLIT_SIZE + original_len + JUMP_SIZE)) - inject_raw(&code, size_of(byte) + size_of(i16), i16(SPLIT_SIZE)) + split := Split{.Split, i16(SPLIT_SIZE + original_len + JUMP_SIZE), i16(SPLIT_SIZE)} + inject_raw(&code, 0, split) append(&code, Opcode.Jump) append_raw(&code, i16(-original_len - SPLIT_SIZE)) @@ -359,17 +361,15 @@ generate_code :: proc(c: ^Compiler, node: Node) -> (code: Program) { code = generate_code(c, specific.inner) original_len := len(code) - inject_at(&code, 0, Opcode.Split) - inject_raw(&code, size_of(byte) , i16(SPLIT_SIZE)) - inject_raw(&code, size_of(byte) + size_of(i16), i16(SPLIT_SIZE + original_len)) + split := Split{.Split, i16(SPLIT_SIZE), i16(SPLIT_SIZE + original_len)} + inject_raw(&code, 0, split) case ^Node_Optional_Non_Greedy: code = generate_code(c, specific.inner) original_len := len(code) - inject_at(&code, 0, Opcode.Split) - inject_raw(&code, size_of(byte) , i16(SPLIT_SIZE + original_len)) - inject_raw(&code, size_of(byte) + size_of(i16), i16(SPLIT_SIZE)) + split := Split{.Split, i16(SPLIT_SIZE + original_len), i16(SPLIT_SIZE)} + inject_raw(&code, 0, split) case ^Node_Match_All_And_Escape: append(&code, Opcode.Match_All_And_Escape) @@ -412,32 +412,28 @@ compile :: proc(tree: Node, flags: common.Flags) -> (code: Program, class_data: seek_loop: for opcode, pc in virtual_machine.iterate_opcodes(&iter) { #partial switch opcode { case .Byte: - inject_at(&code, pc_open, Opcode.Wait_For_Byte) - pc_open += size_of(Opcode) - inject_at(&code, pc_open, Opcode(code[pc + size_of(Opcode) + pc_open])) - pc_open += size_of(u8) + wait := Wait_For_Byte{.Wait_For_Byte, code[pc + size_of(Opcode) + pc_open]} + inject_raw(&code, pc_open, wait) + pc_open += size_of(Wait_For_Byte) break optimize_opening case .Rune: operand := intrinsics.unaligned_load(cast(^rune)&code[pc+1]) - inject_at(&code, pc_open, Opcode.Wait_For_Rune) - pc_open += size_of(Opcode) - inject_raw(&code, pc_open, operand) - pc_open += size_of(rune) + wait := Wait_For_Rune{.Wait_For_Rune, operand} + inject_raw(&code, pc_open, wait) + pc_open += size_of(Wait_For_Rune) break optimize_opening case .Rune_Class: - inject_at(&code, pc_open, Opcode.Wait_For_Rune_Class) - pc_open += size_of(Opcode) - inject_at(&code, pc_open, Opcode(code[pc + size_of(Opcode) + pc_open])) - pc_open += size_of(u8) + wait := Wait_For_Rune_Class{.Wait_For_Rune_Class, code[pc + size_of(Opcode) + pc_open]} + inject_raw(&code, pc_open, wait) + pc_open += size_of(Wait_For_Rune_Class) break optimize_opening case .Rune_Class_Negated: - inject_at(&code, pc_open, Opcode.Wait_For_Rune_Class_Negated) - pc_open += size_of(Opcode) - inject_at(&code, pc_open, Opcode(code[pc + size_of(Opcode) + pc_open])) - pc_open += size_of(u8) + wait := Wait_For_Rune_Class_Negated{.Wait_For_Rune_Class_Negated, code[pc + size_of(Opcode) + pc_open]} + inject_raw(&code, pc_open, wait) + pc_open += size_of(Wait_For_Rune_Class_Negated) break optimize_opening case .Save: @@ -452,27 +448,21 @@ compile :: proc(tree: Node, flags: common.Flags) -> (code: Program, class_data: } // `.*?` - inject_at(&code, pc_open, Opcode.Split) - pc_open += size_of(byte) - inject_raw(&code, pc_open, i16(SPLIT_SIZE + size_of(byte) + JUMP_SIZE)) - pc_open += size_of(i16) - inject_raw(&code, pc_open, i16(SPLIT_SIZE)) - pc_open += size_of(i16) - - inject_at(&code, pc_open, Opcode.Wildcard) - pc_open += size_of(byte) - - inject_at(&code, pc_open, Opcode.Jump) - pc_open += size_of(byte) - inject_raw(&code, pc_open, i16(-size_of(byte) - SPLIT_SIZE)) - pc_open += size_of(i16) - + split := Split{.Split, i16(SPLIT_SIZE + size_of(byte) + JUMP_SIZE), i16(SPLIT_SIZE)} + jump := Jump{.Jump, i16(-size_of(byte) - SPLIT_SIZE)} + pack := struct { + a: Split, + b: Opcode, + c: Jump, + } { split, Opcode.Wildcard, jump } + inject_raw(&code, pc_open, pack) + pc_open += size_of(Split) + size_of(byte) + size_of(Jump) } if .No_Capture not_in flags { // `(` - inject_at(&code, pc_open, Opcode.Save) - inject_at(&code, pc_open + size_of(byte), Opcode(0x00)) + save := Save{.Save, Opcode(0x00)} + inject_raw(&code, pc_open, save) // `)` append(&code, Opcode.Save); append(&code, Opcode(0x01)) diff --git a/core/text/regex/virtual_machine/virtual_machine.odin b/core/text/regex/virtual_machine/virtual_machine.odin index ab2e9515c..3751e0974 100644 --- a/core/text/regex/virtual_machine/virtual_machine.odin +++ b/core/text/regex/virtual_machine/virtual_machine.odin @@ -49,6 +49,35 @@ Opcode :: enum u8 { Wait_For_Rune_Class_Negated = 0x14, // | u8 Match_All_And_Escape = 0x15, // | } +Jump :: struct #packed { + opcode: Opcode, + target: i16, +} +Split :: struct #packed { + opcode: Opcode, + left: i16, + right: i16, +} +Wait_For_Byte :: struct #packed { + opcode: Opcode, + operand: Opcode, +} +Wait_For_Rune :: struct #packed { + opcode: Opcode, + operand: rune, +} +Wait_For_Rune_Class :: struct #packed { + opcode: Opcode, + operand: Opcode, +} +Wait_For_Rune_Class_Negated :: struct #packed { + opcode: Opcode, + operand: Opcode, +} +Save :: struct #packed { + opcode: Opcode, + operand: Opcode, +} Thread :: struct { pc: int, diff --git a/src/array.cpp b/src/array.cpp index ec2c97d0e..9cf7c6ce3 100644 --- a/src/array.cpp +++ b/src/array.cpp @@ -449,6 +449,20 @@ gb_internal void array_unordered_remove(Array *array, isize index) { array_pop(array); } +template +gb_internal void array_inject_at(Array *array, isize index, T value) { + GB_ASSERT(0 <= index); + + isize n = gb_max(array->count, index); + isize new_size = n+1; + array_resize(array, new_size); + + gb_memmove(array->data+index+1, array->data+index, gb_size_of(T)*(array->count-index-1)); + array->data[index] = value; +} + + + template diff --git a/src/big_int.cpp b/src/big_int.cpp index e2ebb5c76..4f20f2ad9 100644 --- a/src/big_int.cpp +++ b/src/big_int.cpp @@ -432,6 +432,14 @@ gb_internal void big_int_rem(BigInt *z, BigInt const *x, BigInt const *y) { big_int_quo_rem(x, y, &q, z); big_int_dealloc(&q); } +gb_internal void big_int_mod_mod(BigInt *z, BigInt const *x, BigInt const *y) { + BigInt q = {}; + big_int_rem(&q, x, y); + big_int_add(&q, &q, y); + big_int_rem(z, &q, y); + big_int_dealloc(&q); + +} gb_internal void big_int_euclidean_mod(BigInt *z, BigInt const *x, BigInt const *y) { BigInt y0 = {}; diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 16b6f2641..6b943c289 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -617,6 +617,10 @@ struct BuildContext { isize max_error_count; + bool bedrock; + bool disable_non_constant_globals; + bool disable_init_fini; + u32 cmd_doc_flags; Array extra_packages; @@ -1858,8 +1862,10 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta bc->no_entry_point = true; } else { if (bc->no_rtti) { - gb_printf_err("-no-rtti is only allowed on freestanding targets\n"); - gb_exit(1); + if (!bc->bedrock) { + gb_printf_err("-no-rtti is only allowed on freestanding targets or '-bedrock'\n"); + gb_exit(1); + } } } diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 00b22e84b..f8f02f715 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -7789,6 +7789,28 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As break; + case BuiltinProc_type_proc_calling_convention: + if (operand->mode != Addressing_Type || !is_type_proc(operand->type)) { + error(operand->expr, "Expected a procedure type for '%.*s'", LIT(builtin_name)); + return false; + } else { + if (is_type_polymorphic(operand->type)) { + error(operand->expr, "Expected a non-polymorphic procedure type for '%.*s'", LIT(builtin_name)); + return false; + } + + Type *pt = base_type(operand->type); + GB_ASSERT(pt->kind == Type_Proc); + ProcCallingConvention cc = pt->Proc.calling_convention; + + operand->mode = Addressing_Constant; + operand->type = t_odin_calling_convention; + operand->value = exact_value_i64(cc); + } + + break; + + case BuiltinProc_type_polymorphic_record_parameter_count: operand->value = exact_value_i64(0); if (operand->mode != Addressing_Type) { @@ -8249,7 +8271,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As Ast *call_expr = unparen_expr(ce->args[0]); Operand op = {}; check_expr_base(c, &op, ce->args[0], nullptr); - if (op.mode != Addressing_Value && !(call_expr && call_expr->kind == Ast_CallExpr)) { + if (op.mode != Addressing_Value || call_expr == nullptr || call_expr->kind != Ast_CallExpr) { error(ce->args[0], "Expected a call expression for '%.*s'", LIT(builtin_name)); return false; } diff --git a/src/check_decl.cpp b/src/check_decl.cpp index b74a43d62..edb9f4883 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1334,6 +1334,10 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { e->flags |= EntityFlag_Fini; } + if (build_context.disable_init_fini && (e->flags & (EntityFlag_Init|EntityFlag_Fini))) { + error(e->token, "@(init) and @(fini) have been disabled with '-disable-init-fini'"); + } + if (ac.set_cold) { e->flags |= EntityFlag_Cold; } @@ -1530,10 +1534,26 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { error(e->token, "Procedure type of 'main' was expected to be 'proc()', got %s", str); gb_string_free(str); } - if (pt->calling_convention != default_calling_convention()) { - error(e->token, "Procedure 'main' cannot have a custom calling convention"); + if (build_context.bedrock) { + switch (pt->calling_convention) { + case ProcCC_Odin: + case ProcCC_Contextless: + // Okay + break; + default: + error(e->token, "Procedure 'main' cannot have a custom calling convention beyond \"odin\" and \"contextless\" with '-bedrock'"); + pt->calling_convention = ProcCC_Odin; + break; + } + + } else { + if (pt->calling_convention != default_calling_convention()) { + error(e->token, "Procedure 'main' cannot have a custom calling convention"); + } + pt->calling_convention = default_calling_convention(); + } - pt->calling_convention = default_calling_convention(); + if (e->pkg->kind == Package_Init) { if (ctx->info->entry_point != nullptr) { error(e->token, "Redeclaration of the entry pointer procedure 'main'"); @@ -1829,9 +1849,25 @@ gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, D PtrSet entity_set = {}; ptr_set_init(&entity_set, 2*pg->args.count); - for (Ast *arg : pg->args) { + for (Ast *arg_ : pg->args) { + Ast *arg = arg_; Entity *e = nullptr; Operand o = {}; + if (arg->kind == Ast_BinaryExpr && arg->BinaryExpr.op.kind == Token_where) { + Ast *cond_expr = arg->BinaryExpr.right; + Operand cond = {}; + check_expr(ctx, &cond, cond_expr); + if (cond.mode != Addressing_Invalid) { + if (cond.mode != Addressing_Constant || !is_type_boolean(cond.type) || cond.value.kind != ExactValue_Bool) { + error(arg, "Expected a constant binary expression for the 'where' clause"); + } else if (!cond.value.value_bool) { + continue; + } + } + + arg = arg->BinaryExpr.left; + } + if (arg->kind == Ast_Ident) { e = check_ident(ctx, &o, arg, nullptr, nullptr, true); } else if (arg->kind == Ast_SelectorExpr) { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index cd38ff69a..79e1154a1 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6595,11 +6595,16 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A positional_operand_count = gb_min(positional_operands.count, pt->variadic_index); } else if (positional_operand_count > pt->param_count) { err = CallArgumentError_TooManyArguments; - char const *err_fmt = "Too many arguments for '%s', expected %td arguments, got %td"; if (show_error) { gbString proc_str = expr_to_string(ce->proc); defer (gb_string_free(proc_str)); - error(call, err_fmt, proc_str, param_count_excluding_defaults, positional_operands.count); + if (param_count_excluding_defaults != pt->param_count) { + char const *err_fmt = "Too many arguments for '%s', expected %td..=%td arguments, got %td"; + error(call, err_fmt, proc_str, param_count_excluding_defaults, pt->param_count, positional_operands.count); + } else { + char const *err_fmt = "Too many arguments for '%s', expected %td arguments, got %td"; + error(call, err_fmt, proc_str, pt->param_count, positional_operands.count); + } } return err; } @@ -7835,7 +7840,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, break; } } - if (all_the_same) { + if (all_the_same && first_results != nullptr) { GB_ASSERT_MSG(is_type_tuple(first_results), "%s", type_to_string(first_results)); data.result_type = first_results; } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 53dae5fd5..222222559 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1476,6 +1476,14 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_ return; } + if (switch_kind == TypeSwitch_Union) { + if (is_addressed) { + if (x.mode != Addressing_Variable && !is_type_pointer(x.type)) { + error(lhs->Ident.token, "The element variable '%.*s' cannot be made addressable", LIT(lhs->Ident.token.string)); + } + } + } + Ast *nil_seen = nullptr; TypeSet seen = {}; diff --git a/src/check_type.cpp b/src/check_type.cpp index 128ce3ba5..212f7dbe4 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -3068,6 +3068,10 @@ gb_internal void check_map_type(CheckerContext *ctx, Type *type, Ast *node) { init_core_map_type(ctx->checker); init_map_internal_types(type); + + if (build_context.bedrock) { + error(node, "'map' is not a valid type when using '-bedrock'"); + } } gb_internal void check_matrix_type(CheckerContext *ctx, Type **type, Ast *node) { @@ -3807,6 +3811,7 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T *type = alloc_type_dynamic_array(elem); } set_base_type(named_type, *type); + return true; case_end; diff --git a/src/checker.cpp b/src/checker.cpp index 0624c65ac..dd61f6fc3 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1121,6 +1121,14 @@ gb_internal void init_universal(void) { // Types for (isize i = 0; i < gb_count_of(basic_types); i++) { String const &name = basic_types[i].Basic.name; + if (build_context.bedrock) { + if ((basic_types[i].Basic.flags & BasicFlag_Integer) != 0 && + basic_types[i].Basic.size == 16) { + // disallow 128-bit integers + continue; + } + } + add_global_type_entity(name, &basic_types[i]); } add_global_type_entity(str_lit("byte"), &basic_types[Basic_u8]); @@ -1147,6 +1155,8 @@ gb_internal void init_universal(void) { add_global_string_constant("ODIN_ROOT", bc->ODIN_ROOT); add_global_string_constant("ODIN_BUILD_PROJECT_NAME", bc->ODIN_BUILD_PROJECT_NAME); + add_global_bool_constant("ODIN_BEDROCK", bc->bedrock); + { GlobalEnumValue values[Windows_Subsystem_COUNT] = { {"Unknown", Windows_Subsystem_UNKNOWN}, @@ -1303,6 +1313,32 @@ gb_internal void init_universal(void) { scope_insert(intrinsics_pkg->scope, t_atomic_memory_order->Named.type_name); } + { + GlobalEnumValue values[ProcCC_MAX] = { + {"Invalid", ProcCC_Invalid}, + {"Odin", ProcCC_Odin}, + {"Contextless", ProcCC_Contextless}, + {"CDecl", ProcCC_CDecl}, + {"Std_Call", ProcCC_StdCall}, + {"Fast_Call", ProcCC_FastCall}, + + {"None", ProcCC_None}, + {"Naked", ProcCC_Naked}, + + {"_", ProcCC_InlineAsm}, + + {"Win64", ProcCC_Win64}, + {"SysV", ProcCC_SysV}, + + {"PreserveNone", ProcCC_PreserveNone}, + {"PreserveMost", ProcCC_PreserveMost}, + {"PreserveAll", ProcCC_PreserveAll}, + }; + + auto fields = add_global_enum_type(str_lit("Odin_Calling_Convention"), values, gb_count_of(values), &t_odin_calling_convention, t_u8); + add_global_enum_constant(fields, "ODIN_DEFAULT_CALLING_CONVENTION", default_calling_convention()); + } + { int minimum_os_version = 0; if (build_context.minimum_os_version_string != "") { @@ -7670,6 +7706,14 @@ gb_internal void check_parsed_files(Checker *c) { Type *t = &basic_types[i]; if (t->Basic.size > 0 && (t->Basic.flags & BasicFlag_LLVM) == 0) { + if (build_context.bedrock) { + if ((t->Basic.flags & BasicFlag_Integer) != 0 && + t->Basic.size == 16) { + // disallow 128-bit integers + continue; + } + } + add_type_info_type(&c->builtin_ctx, t); } } diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 049d29a2a..4a6b901a6 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -353,6 +353,8 @@ BuiltinProc__type_simple_boolean_end, BuiltinProc_type_proc_parameter_type, BuiltinProc_type_proc_return_type, + BuiltinProc_type_proc_calling_convention, + BuiltinProc_type_polymorphic_record_parameter_count, BuiltinProc_type_polymorphic_record_parameter_value, @@ -754,6 +756,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("type_proc_parameter_type"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_proc_return_type"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_proc_calling_convention"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_polymorphic_record_parameter_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_polymorphic_record_parameter_value"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/exact_value.cpp b/src/exact_value.cpp index fa26ec4b0..f232f95e0 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -780,7 +780,7 @@ gb_internal ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, E case Token_Quo: return exact_value_float(fmod(big_int_to_f64(a), big_int_to_f64(b))); case Token_QuoEq: big_int_quo(&c, a, b); break; // NOTE(bill): Integer division case Token_Mod: big_int_rem(&c, a, b); break; - case Token_ModMod: big_int_euclidean_mod(&c, a, b); break; + case Token_ModMod: big_int_mod_mod(&c, a, b); break; case Token_And: big_int_and(&c, a, b); break; case Token_Or: big_int_or(&c, a, b); break; case Token_Xor: big_int_xor(&c, a, b); break; diff --git a/src/gb/gb.h b/src/gb/gb.h index 3413d1367..84bbc2e0e 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -2493,7 +2493,13 @@ extern "C" { #pragma warning(disable:4127) // Conditional expression is constant #endif +gb_internal void print_all_errors(void); +gb_internal bool any_errors(void); +gb_internal bool any_warnings(void); void gb_assert_handler(char const *prefix, char const *condition, char const *file, i32 line, char const *msg, ...) { + if (any_errors() || any_warnings()) { + print_all_errors(); + } gb_printf_err("%s(%d): %s: ", file, line, prefix); if (condition) gb_printf_err( "`%s` ", condition); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 8c4fd9264..f9e049621 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1346,12 +1346,12 @@ String lb_get_objc_type_encoding(Type *t, isize pointer_depth = 0) { s = gb_string_append_length(s, "=", 1); if (!is_union) { - for( auto& f : base->Struct.fields ) { + for (auto &f : base->Struct.fields) { String field_type = lb_get_objc_type_encoding(f->type, pointer_depth); s = gb_string_append_length(s, field_type.text, field_type.len); } } else { - for( auto& v : base->Union.variants ) { + for (auto &v : base->Union.variants) { String variant_type = lb_get_objc_type_encoding(v, pointer_depth); s = gb_string_append_length(s, variant_type.text, variant_type.len); } @@ -1518,7 +1518,7 @@ gb_internal void lb_register_objc_thing( auto &tn = g.class_impl_type->Named.type_name->TypeName; Type *superclass = tn.objc_superclass; if (superclass != nullptr) { - auto& superclass_global = string_map_must_get(&class_map, superclass->Named.type_name->TypeName.objc_class_name); + auto &superclass_global = string_map_must_get(&class_map, superclass->Named.type_name->TypeName.objc_class_name); lb_register_objc_thing(handled, m, args, class_impls, class_map, p, superclass_global.g, call); GB_ASSERT(superclass_global.class_global.addr.value); } @@ -1571,6 +1571,7 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { for (Entity *e = {}; mpsc_dequeue(&gen->info->objc_class_implementations, &e); /**/) { GB_ASSERT(e->kind == Entity_TypeName && e->TypeName.objc_is_implementation); lb_handle_objc_find_or_register_class(p, e->TypeName.objc_class_name, e->type); + error(e->token, "Objective-C related things are not allowed with '-bedrock'"); } // Ensure classes that have been implicitly referenced through @@ -1595,12 +1596,18 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { } for (auto pair : class_set) { - auto& tn = pair.type->Named.type_name->TypeName; + Entity *e = pair.type->Named.type_name; + GB_ASSERT(e->kind == Entity_TypeName); + auto &tn = e->TypeName; Type *class_impl = !tn.objc_is_implementation ? nullptr : pair.type; lb_handle_objc_find_or_register_class(p, tn.objc_class_name, class_impl); + + if (build_context.bedrock) { + error(e->token, "Objective-C related things are not allowed with '-bedrock'"); + } } for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_classes, &g); /**/) { - array_add( &referenced_classes, g ); + array_add(&referenced_classes, g); } // Add all class globals to a map so that we can look them up dynamically @@ -1618,21 +1625,21 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { lb_begin_procedure_body(p); // Register class globals, gathering classes that must be implemented - for (auto& kv : global_class_map) { + for (auto &kv : global_class_map) { lb_register_objc_thing(handled, m, args, class_impls, global_class_map, p, kv.value.g, "objc_lookUpClass"); } // Prefetch selectors for implemented methods so that they can also be registered. - for (const auto& cd : class_impls) { - auto& g = cd.g; + for (auto const &cd : class_impls) { + auto &g = cd.g; Type *class_type = g.class_impl_type; - Array* methods = map_get(&m->info->objc_method_implementations, class_type); + Array *methods = map_get(&m->info->objc_method_implementations, class_type); if (!methods) { continue; } - for (const ObjcMethodData& md : *methods) { + for (ObjcMethodData const &md : *methods) { lb_handle_objc_find_or_register_selector(p, md.ac.objc_selector); } } @@ -1655,11 +1662,17 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { map_set(&ivar_map, g.class_impl_type, g); } - for (const auto &cd : class_impls) { + for (auto const &cd : class_impls) { auto &g = cd.g; Type *class_type = g.class_impl_type; Type *class_ptr_type = alloc_type_pointer(class_type); + Entity *e = class_type->Named.type_name; + GB_ASSERT(e->kind == Entity_TypeName); + + if (build_context.bedrock) { + error(e->token, "Objective-C related things are not allowed with '-bedrock'"); + } // Begin class registration: create class pair and update global reference lbValue class_value = {}; @@ -1667,11 +1680,11 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { { lbValue superclass_value = lb_const_nil(m, t_objc_Class); - auto& tn = class_type->Named.type_name->TypeName; + auto &tn = e->TypeName; Type *superclass = tn.objc_superclass; if (superclass != nullptr) { - auto& superclass_global = string_map_must_get(&global_class_map, superclass->Named.type_name->TypeName.objc_class_name); + auto& superclass_global = string_map_must_get(&global_class_map, tn.objc_class_name); superclass_value = superclass_global.class_value; } @@ -1727,13 +1740,13 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { } for (const ObjcMethodData &md : *methods) { - GB_ASSERT( md.proc_entity->kind == Entity_Procedure); + GB_ASSERT(md.proc_entity->kind == Entity_Procedure); Type *method_type = md.proc_entity->type; String proc_name = make_string_c("__$objc_method::"); proc_name = concatenate_strings(temporary_allocator(), proc_name, g.name); proc_name = concatenate_strings(temporary_allocator(), proc_name, str_lit("::")); - proc_name = concatenate_strings( permanent_allocator(), proc_name, md.ac.objc_name); + proc_name = concatenate_strings(permanent_allocator(), proc_name, md.ac.objc_name); wrapper_args.count = 2; wrapper_args[0] = md.ac.objc_is_class_method ? t_objc_Class : class_ptr_type; @@ -1934,7 +1947,10 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { ivar_addr = lb_addr(global); } - String class_name = g.class_impl_type->Named.type_name->TypeName.objc_class_name; + Entity *e = g.class_impl_type->Named.type_name; + GB_ASSERT(e->kind == Entity_TypeName); + + String class_name = e->TypeName.objc_class_name; lbValue class_value = string_map_must_get(&global_class_map, class_name).class_value; args.count = 2; @@ -1948,6 +1964,10 @@ gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) { lbValue ivar_offset_int = lb_emit_conv(p, ivar_offset, t_int); lb_addr_store(p, ivar_addr, ivar_offset_int); + + if (build_context.bedrock) { + error(e->token, "Objective-C related things are not allowed with '-bedrock'"); + } } lb_end_procedure_body(p); @@ -2072,6 +2092,10 @@ gb_internal bool lb_init_global_var(lbModule *m, lbProcedure *p, Entity *e, Ast } var.is_initialized = true; + + if (build_context.disable_non_constant_globals) { + error(e->token, "Non-constant initialization of a global variable is disallowed with '-disable_non_constant_globals'"); + } } return false; } diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 73e927e08..a314791c9 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -1609,7 +1609,17 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty if (elem_count == 0 || !elem_type_can_be_constant(elem_type)) { return lb_const_nil(m, original_type); } - if (cl->elems[0]->kind == Ast_FieldValue) { + if (are_types_identical(value.value_compound->tav.type, elem_type)) { + // Compound is of array item type; expand its value to all items in array. + LLVMValueRef* values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)type->Array.count); + + for (isize i = 0; i < type->Array.count; i++) { + values[i] = lb_const_value(m, elem_type, value, elem_type, cc).value; + } + + res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, cc); + return res; + } else if (cl->elems[0]->kind == Ast_FieldValue) { // TODO(bill): This is O(N*M) and will be quite slow; it should probably be sorted before hand LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)type->Array.count); @@ -1663,16 +1673,6 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, Ty } } - res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, cc); - return res; - } else if (are_types_identical(value.value_compound->tav.type, elem_type)) { - // Compound is of array item type; expand its value to all items in array. - LLVMValueRef* values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)type->Array.count); - - for (isize i = 0; i < type->Array.count; i++) { - values[i] = lb_const_value(m, elem_type, value, elem_type, cc).value; - } - res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, cc); return res; } else { diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index e02f743ca..7eb32279e 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -6571,11 +6571,12 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) { } else { item = lb_emit_ptr_offset(p, lb_emit_load(p, arr), index); } + + // make sure it's ^T and not [^]T + item.type = alloc_type_multi_pointer_to_pointer(item.type); if (sub_sel.index.count > 0) { item = lb_emit_deep_field_gep(p, item, sub_sel); } - // make sure it's ^T and not [^]T - item.type = alloc_type_multi_pointer_to_pointer(item.type); return lb_addr(item); } else if (addr.kind == lbAddr_Swizzle) { diff --git a/src/main.cpp b/src/main.cpp index 51dade656..da0e6c5b4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -429,6 +429,10 @@ enum BuildFlagKind { BuildFlag_BuildDiagnostics, + BuildFlag_Bedrock, + BuildFlag_DisableNonConstantGlobals, + BuildFlag_DisableInitFini, + // internal use only BuildFlag_InternalFastISel, BuildFlag_InternalIgnoreLazy, @@ -664,6 +668,10 @@ gb_internal bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_BuildDiagnostics, str_lit("build-diagnostics"), BuildFlagParam_None, Command__does_build); + add_flag(&build_flags, BuildFlag_Bedrock, str_lit("bedrock"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_DisableNonConstantGlobals, str_lit("disable-non-constant-globals"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_DisableInitFini, str_lit("disable-init-fini"), BuildFlagParam_None, Command__does_check); + add_flag(&build_flags, BuildFlag_InternalFastISel, str_lit("internal-fast-isel"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalIgnoreLazy, str_lit("internal-ignore-lazy"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalIgnoreLLVMBuild, str_lit("internal-ignore-llvm-build"),BuildFlagParam_None, Command_all); @@ -1659,6 +1667,20 @@ gb_internal bool parse_build_flags(Array args) { build_context.build_diagnostics = true; break; + case BuildFlag_Bedrock: + build_context.bedrock = true; + build_context.no_rtti = true; + build_context.disable_non_constant_globals = true; + build_context.disable_init_fini = true; + break; + + case BuildFlag_DisableNonConstantGlobals: + build_context.disable_non_constant_globals = true; + break; + case BuildFlag_DisableInitFini: + build_context.disable_init_fini = true; + break; + case BuildFlag_InternalFastISel: build_context.fast_isel = true; break; @@ -2685,6 +2707,19 @@ gb_internal int print_show_help(String const arg0, String command, String option } } + if (check) { + if (print_flag("-bedrock")) { + print_usage_line(2, "Disables numerous features. List of disabled features:"); + print_usage_line(3, "`map` types"); + print_usage_line(3, "128-bit integer types"); + print_usage_line(3, "runtime type information (-no-rtti)"); + print_usage_line(3, "non-constant global variables (-disable-non-constant-globals)"); + print_usage_line(3, "@(init) @(fini) (-disable-init-fini)"); + print_usage_line(3, "Anything Objective-C related"); + print_usage_line(3, "The default paths to the library collections 'core' and 'vendor'"); + } + } + if (build) { if (print_flag("-build-mode:")) { print_usage_line(2, "Sets the build mode."); @@ -2751,7 +2786,15 @@ gb_internal int print_show_help(String const arg0, String command, String option if (print_flag("-disable-assert")) { print_usage_line(2, "Disables the code generation of the built-in run-time 'assert' procedure, and defines the global constant ODIN_DISABLE_ASSERT to be 'true'."); } + } + if (check) { + if (print_flag("-disable-init-fini")) { + print_usage_line(2, "Disables the ability to use @(init) and @(fini) procedures"); + } + } + + if (run_or_build) { if (print_flag("-disable-red-zone")) { print_usage_line(2, "Disables red zone on a supported freestanding target."); } @@ -2761,8 +2804,13 @@ gb_internal int print_show_help(String const arg0, String command, String option if (print_flag("-disallow-do")) { print_usage_line(2, "Disallows the 'do' keyword in the project."); } + + if (print_flag("-disable-non-constant-globals")) { + print_usage_line(2, "Disables any global variables which are not initialized with global constants"); + } } + if (doc) { if (print_flag("-doc-format")) { print_usage_line(2, "Generates documentation as the .odin-doc format (useful for external tooling)."); @@ -3595,6 +3643,32 @@ gb_internal int strip_semicolons(Parser *parser) { return cast(int)failed; } +gb_internal void setup_bedrock_mode(void) { + if (!build_context.bedrock) { + return; + } + + bool seen_core = false; + bool seen_vendor = false; + for (isize i = 0; i < library_collections.count; /**/) { + if (!seen_core && library_collections[i].name == "core") { + array_ordered_remove(&library_collections, i); + seen_core = true; + continue; + } + + if (!seen_vendor && library_collections[i].name == "vendor") { + array_ordered_remove(&library_collections, i); + seen_vendor = true; + continue; + } + + i += 1; + } + + build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR = true; +} + gb_internal void init_terminal(void) { TIME_SECTION("init terminal"); build_context.has_ansi_terminal_colours = false; @@ -3693,16 +3767,41 @@ int main(int arg_count, char const **arg_ptr) { String init_filename = {}; isize last_non_run_arg = args.count; + isize double_dash_pos = -1; for_array(i, args) { if (args[i] == "--") { + double_dash_pos = i; break; } + if (args[i] == "-help" || args[i] == "--help") { build_context.show_help = true; return print_show_help(args[0], command); } } + if (args.count > 2) { + // NOTE(bill): Allow for both `odin command path -flags` and `odin command -flags path` + // To do this, if the first argument after the command and last argument is NOT a flag, + // then put that last parameter first + isize end_arg = double_dash_pos >= 0 ? double_dash_pos : args.count-1; + if (args[1] == "bundle" && args.count > 4) { + if (string_starts_with(args[3], str_lit("-")) && + !string_starts_with(args[end_arg], str_lit("-"))) { + String possible_path = args[end_arg]; + array_ordered_remove(&args, end_arg); + array_inject_at(&args, 3, possible_path); + } + } else if (args.count > 3) { + if (string_starts_with(args[2], str_lit("-")) && + !string_starts_with(args[end_arg], str_lit("-"))) { + String possible_path = args[end_arg]; + array_ordered_remove(&args, end_arg); + array_inject_at(&args, 2, possible_path); + } + } + } + bool run_output = false; if (command == "run" || command == "test") { if (args.count < 3) { @@ -3874,6 +3973,10 @@ int main(int arg_count, char const **arg_ptr) { return print_show_help(args[0], command); } + if (build_context.bedrock) { + setup_bedrock_mode(); + } + if (init_filename.len > 0 && !build_context.show_help) { // The command must be build, run, test, check, or another that takes a directory or filename. if (!path_is_directory(init_filename)) { diff --git a/src/parser.cpp b/src/parser.cpp index 14258b5c6..03e9073ca 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2528,8 +2528,14 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { while (f->curr_token.kind != Token_CloseBrace && f->curr_token.kind != Token_EOF) { Ast *elem = parse_expr(f, false); - array_add(&args, elem); + if (f->curr_token.kind == Token_where) { + Token where = expect_token(f, Token_where); + Ast *cond = parse_expr(f, false); + elem = ast_binary_expr(f, where, elem, cond); + } + + array_add(&args, elem); if (!allow_field_separator(f)) { break; } @@ -4214,18 +4220,33 @@ gb_internal FieldFlag is_token_field_prefix(AstFile *f) { return FieldFlag_using; case Token_Hash: - advance_token(f); - switch (f->curr_token.kind) { - case Token_Ident: - for (i32 i = 0; i < gb_count_of(parse_field_prefix_mappings); i++) { - auto const &mapping = parse_field_prefix_mappings[i]; - if (mapping.token_kind == Token_Hash) { - if (f->curr_token.string == mapping.name) { - return mapping.flag; - } + { + // Check for types first before fields + Token tok = peek_token(f); + if (tok.kind == Token_Ident) { + if (tok.string == "simd" || + tok.string == "type" || + tok.string == "row_major" || + tok.string == "column_major" || + tok.string == "sparse" || + tok.string == "soa") { + return FieldFlag_Invalid; } } - break; + + advance_token(f); + switch (f->curr_token.kind) { + case Token_Ident: + for (i32 i = 0; i < gb_count_of(parse_field_prefix_mappings); i++) { + auto const &mapping = parse_field_prefix_mappings[i]; + if (mapping.token_kind == Token_Hash) { + if (f->curr_token.string == mapping.name) { + return mapping.flag; + } + } + } + break; + } } return FieldFlag_Unknown; } @@ -6389,6 +6410,11 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) { continue; } + if (p == "bedrock") { + this_kind_correct = build_context.bedrock == !is_notted; + continue; + } + Subtarget subtarget = Subtarget_Invalid; String subtarget_str = {}; diff --git a/src/parser.hpp b/src/parser.hpp index 149cf6330..e423cb9b2 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -329,6 +329,9 @@ gb_global char const *proc_calling_convention_strings[ProcCC_MAX] = { }; gb_internal ProcCallingConvention default_calling_convention(void) { + if (build_context.bedrock) { + // return ProcCC_Contextless; + } return ProcCC_Odin; } diff --git a/src/types.cpp b/src/types.cpp index 45486e6bb..32f5a10a9 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -781,6 +781,9 @@ gb_global Type *t_c_va_list = nullptr; gb_global Type *t_c_va_list_ptr = nullptr; +gb_global Type *t_odin_calling_convention = nullptr; + + enum OdinAtomicMemoryOrder : i32 { OdinAtomicMemoryOrder_relaxed = 0, // unordered OdinAtomicMemoryOrder_consume = 1, // monotonic diff --git a/tests/core/crypto/wycheproof/main.odin b/tests/core/crypto/wycheproof/main.odin index 654ac2a38..bfb4884cd 100644 --- a/tests/core/crypto/wycheproof/main.odin +++ b/tests/core/crypto/wycheproof/main.odin @@ -73,7 +73,7 @@ import "core:testing" // - crypto/legacy/md5 // - crypto/tuplehash -ARENA_SIZE :: 4 * 1024 * 1024 // There is no kill like overkill. +ARENA_SIZE :: 8 * 1024 * 1024 // There is no kill like overkill. BASE_PATH :: ODIN_ROOT + "tests/core/assets/Wycheproof" SUFFIX_TEST_JSON :: "_test.json" diff --git a/tests/core/crypto/wycheproof/pqc.odin b/tests/core/crypto/wycheproof/pqc.odin index d485f621f..07ce2d02c 100644 --- a/tests/core/crypto/wycheproof/pqc.odin +++ b/tests/core/crypto/wycheproof/pqc.odin @@ -494,6 +494,7 @@ test_mldsa :: proc(t: ^testing.T) { test_mldsa_sign :: proc(t: ^testing.T, test_vectors: ^Test_Vectors(Mldsa_Test_Group)) -> bool { FLAG_INTERNAL :: "Internal" + FLAG_RANDOMIZED :: "Randomized" dummy_rnd: [_mldsa.RNDBYTES]byte @@ -511,17 +512,31 @@ test_mldsa_sign :: proc(t: ^testing.T, test_vectors: ^Test_Vectors(Mldsa_Test_Gr priv_key: mldsa.Private_Key tg_len := len(test_group.tests) - if !testing.expectf( - t, - mldsa.private_key_set_bytes(&priv_key, params, seed), - "%s/Sign/%d: failed to set private key from seed: %s", - params_str, - tg_id, - test_group.private_seed, - ) { - num_ran += tg_len - num_failed += tg_len + switch len(test_group.public_key) { + case 0: + for &test_vector in test_group.tests { + num_ran += 1 + switch result_is_invalid(test_vector.result) { + case true: + num_passed += 1 + case false: + num_failed += 1 + } + } continue + case: + if !testing.expectf( + t, + mldsa.private_key_set_bytes(&priv_key, params, seed), + "%s/Sign/%d: failed to set private key from seed: %s", + params_str, + tg_id, + test_group.private_seed, + ) { + num_ran += tg_len + num_failed += tg_len + continue + } } pub_bytes := make([]byte, mldsa.PUBLIC_KEY_SIZES[params]) @@ -575,7 +590,7 @@ test_mldsa_sign :: proc(t: ^testing.T, test_vectors: ^Test_Vectors(Mldsa_Test_Gr ctx := common.hexbytes_decode(test_vector.ctx) msg := common.hexbytes_decode(test_vector.msg) - is_external_mu := slice.contains(test_vector.flags, FLAG_INTERNAL) + is_external_mu := slice.contains(test_vector.flags, FLAG_INTERNAL) || slice.contains(test_vector.flags, FLAG_RANDOMIZED) switch is_external_mu { case false: ok = mldsa.sign( @@ -586,11 +601,16 @@ test_mldsa_sign :: proc(t: ^testing.T, test_vectors: ^Test_Vectors(Mldsa_Test_Gr true, ) case true: + rnd := dummy_rnd[:] + if len(test_vector.rnd) != 0 { + rnd = common.hexbytes_decode(test_vector.rnd) + } + ok = _mldsa.dsa_sign_internal( sig, msg, ctx, - dummy_rnd[:], + rnd, &priv_key, common.hexbytes_decode(test_vector.mu), ) diff --git a/tests/core/crypto/wycheproof/schemas.odin b/tests/core/crypto/wycheproof/schemas.odin index 36fc22d10..2e72a1098 100644 --- a/tests/core/crypto/wycheproof/schemas.odin +++ b/tests/core/crypto/wycheproof/schemas.odin @@ -234,6 +234,7 @@ Mldsa_Test_Vector :: struct { tc_id: int `json:"tcId"`, comment: string `json:"comment"`, msg: common.Hex_Bytes `json:"msg"`, + rnd: common.Hex_Bytes `json:"rnd"`, ctx: common.Hex_Bytes `json:"ctx"`, mu: common.Hex_Bytes `json:"mu"`, sig: common.Hex_Bytes `json:"sig"`, diff --git a/tests/core/flags/test_core_flags.odin b/tests/core/flags/test_core_flags.odin index 834f6b630..f6399461f 100644 --- a/tests/core/flags/test_core_flags.odin +++ b/tests/core/flags/test_core_flags.odin @@ -307,7 +307,7 @@ test_all_bit_sets :: proc(t: ^testing.T) { "-a:10101", "-b:0000_0000_0000_0001", "-c:11", - "-d:___1", + "-d:1", "-e:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", "-f:1", "-g:01", @@ -333,6 +333,65 @@ test_all_bit_sets :: proc(t: ^testing.T) { testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Bad_Value) } } + { + // Starting a binary string with underscore is disallowed. + args := [?]string { "-d:_1" } + result := flags.parse(&s, args[:]) + err, ok := result.(flags.Parse_Error) + testing.expectf(t, ok, "unexpected result: %v", result) + if ok { + testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Bad_Value) + } + } + + E2 :: enum { + Option_A=5, + Option_B, + Option_C=3, + Option_D, + } + R :: struct { + a: bit_set[E2], + } + r: R + { + // Least significant bit > 0 + args := [?]string { + "-a:Option_A,Option_D", + } + result := flags.parse(&r, args[:]) + testing.expect_value(t, result, nil) + testing.expect_value(t, r.a, bit_set[E2]{E2.Option_A, E2.Option_D}) + } + { + args := [?]string { "-a:Option_Non_Existent" } + result := flags.parse(&r, args[:]) + err, ok := result.(flags.Parse_Error) + testing.expectf(t, ok, "unexpected result: %v", result) + if ok { + testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Bad_Value) + } + } + { + // Value names list must be strictly comma-separated. + args := [?]string { "-a:Option_A, Option_B" } + result := flags.parse(&r, args[:]) + err, ok := result.(flags.Parse_Error) + testing.expectf(t, ok, "unexpected result: %v", result) + if ok { + testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Bad_Value) + } + } + { + // Value names list must not end with a comma. + args := [?]string { "-a:Option_A,Option_B," } + result := flags.parse(&r, args[:]) + err, ok := result.(flags.Parse_Error) + testing.expectf(t, ok, "unexpected result: %v", result) + if ok { + testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Bad_Value) + } + } } @(test) @@ -1288,13 +1347,11 @@ test_distinct_types :: proc(t: ^testing.T) { unmodified_i: I, } s: S - { args := [?]string {"-base-i:1"} result := flags.parse(&s, args[:]) testing.expect_value(t, result, nil) } - { args := [?]string {"-unmodified-i:1"} result := flags.parse(&s, args[:]) diff --git a/tests/core/mem/test_mem_dynamic_arena.odin b/tests/core/mem/test_mem_dynamic_arena.odin new file mode 100644 index 000000000..7d2b32f1a --- /dev/null +++ b/tests/core/mem/test_mem_dynamic_arena.odin @@ -0,0 +1,103 @@ +package test_core_mem + +import "core:testing" +import "core:mem" + + +expect_arena_allocation :: proc(t: ^testing.T, expected_used_bytes, num_bytes, alignment: int) { + arena: mem.Dynamic_Arena + mem.dynamic_arena_init(&arena, minimum_alignment = alignment) + arena_allocator := mem.dynamic_arena_allocator(&arena) + + element, err := mem.alloc(num_bytes, alignment, arena_allocator) + testing.expect(t, err == .None) + testing.expect(t, element != nil) + + expected_bytes_left := arena.block_size - expected_used_bytes + testing.expectf(t, arena.bytes_left == expected_bytes_left, + ` + Allocated data with size %v bytes, expected %v bytes left, got %v bytes left, off by %v bytes. + Pool: + block_size = %v + out_band_size = %v + minimum_alignment = %v + unused_blocks = %v + used_blocks = %v + out_band_allocations = %v + current_block = %v + current_pos = %v + bytes_left = %v + `, + num_bytes, expected_bytes_left, arena.bytes_left, expected_bytes_left - arena.bytes_left, + arena.block_size, + arena.out_band_size, + arena.minimum_alignment, + arena.unused_blocks, + arena.used_blocks, + arena.out_band_allocations, + arena.current_block, + arena.current_pos, + arena.bytes_left, + ) + testing.expectf(t, uintptr(element) % uintptr(alignment) == 0, "Expected allocation to be aligned to %d byte boundary, got %v", alignment, element) + + mem.dynamic_arena_destroy(&arena) + testing.expect(t, arena.used_blocks == nil) +} + +expect_arena_allocation_out_of_band :: proc(t: ^testing.T, num_bytes, block_size, out_band_size: int) { + testing.expect(t, num_bytes >= out_band_size, "Sanity check failed, your test call is flawed! Make sure that num_bytes >= out_band_size!") + + arena: mem.Dynamic_Arena + mem.dynamic_arena_init(&arena, block_size = block_size, out_band_size = out_band_size) + arena_allocator := mem.dynamic_arena_allocator(&arena) + + element, err := mem.alloc(num_bytes, allocator = arena_allocator) + testing.expect(t, err == .None) + testing.expect(t, element != nil) + testing.expectf(t, arena.out_band_allocations != nil, + "Allocated data with size %v bytes, which is >= out_of_band_size and it should be in arena.out_band_allocations, but isn't!", + ) + + mem.dynamic_arena_destroy(&arena) + testing.expect(t, arena.out_band_allocations == nil) +} + +@(test) +test_dynamic_arena_alloc_aligned :: proc(t: ^testing.T) { + expect_arena_allocation(t, expected_used_bytes = 16, num_bytes = 16, alignment=8) +} + +@(test) +test_dynamic_arena_alloc_unaligned :: proc(t: ^testing.T) { + expect_arena_allocation(t, expected_used_bytes = 8, num_bytes = 1, alignment = 8) + expect_arena_allocation(t, expected_used_bytes = 16, num_bytes = 9, alignment = 8) +} + +@(test) +test_dynamic_arena_alloc_out_of_band :: proc(t: ^testing.T) { + expect_arena_allocation_out_of_band(t, num_bytes = 128, block_size = 512, out_band_size = 128) + expect_arena_allocation_out_of_band(t, num_bytes = 129, block_size = 512, out_band_size = 128) + expect_arena_allocation_out_of_band(t, num_bytes = 513, block_size = 512, out_band_size = 128) +} + +@(test) +test_intentional_leaks :: proc(t: ^testing.T) { + testing.expect_leaks(t, intentionally_leaky_test, leak_verifier) +} + +// Not tagged with @(test) because it's run through `test_intentional_leaks` +intentionally_leaky_test :: proc(t: ^testing.T) { + a: [dynamic]int + // Intentional leak + append(&a, 42) + + // Intentional bad free + b := uintptr(&a[0]) + 42 + free(rawptr(b)) +} + +leak_verifier :: proc(t: ^testing.T, ta: ^mem.Tracking_Allocator) { + testing.expect_value(t, len(ta.allocation_map), 1) + testing.expect_value(t, len(ta.bad_free_array), 1) +} diff --git a/tests/core/mem/test_mem_dynamic_pool.odin b/tests/core/mem/test_mem_dynamic_pool.odin deleted file mode 100644 index 82cee89af..000000000 --- a/tests/core/mem/test_mem_dynamic_pool.odin +++ /dev/null @@ -1,102 +0,0 @@ -package test_core_mem - -import "core:testing" -import "core:mem" - - -expect_pool_allocation :: proc(t: ^testing.T, expected_used_bytes, num_bytes, alignment: int) { - pool: mem.Dynamic_Pool - mem.dynamic_pool_init(&pool, alignment = alignment) - pool_allocator := mem.dynamic_pool_allocator(&pool) - - element, err := mem.alloc(num_bytes, alignment, pool_allocator) - testing.expect(t, err == .None) - testing.expect(t, element != nil) - - expected_bytes_left := pool.block_size - expected_used_bytes - testing.expectf(t, pool.bytes_left == expected_bytes_left, - ` - Allocated data with size %v bytes, expected %v bytes left, got %v bytes left, off by %v bytes. - Pool: - block_size = %v - out_band_size = %v - alignment = %v - unused_blocks = %v - used_blocks = %v - out_band_allocations = %v - current_block = %v - current_pos = %v - bytes_left = %v - `, - num_bytes, expected_bytes_left, pool.bytes_left, expected_bytes_left - pool.bytes_left, - pool.block_size, - pool.out_band_size, - pool.alignment, - pool.unused_blocks, - pool.used_blocks, - pool.out_band_allocations, - pool.current_block, - pool.current_pos, - pool.bytes_left, - ) - - mem.dynamic_pool_destroy(&pool) - testing.expect(t, pool.used_blocks == nil) -} - -expect_pool_allocation_out_of_band :: proc(t: ^testing.T, num_bytes, block_size, out_band_size: int) { - 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, block_size = block_size, out_band_size = out_band_size) - pool_allocator := mem.dynamic_pool_allocator(&pool) - - element, err := mem.alloc(num_bytes, allocator = pool_allocator) - testing.expect(t, err == .None) - testing.expect(t, element != nil) - testing.expectf(t, pool.out_band_allocations != nil, - "Allocated data with size %v bytes, which is >= out_of_band_size and it should be in pool.out_band_allocations, but isn't!", - ) - - mem.dynamic_pool_destroy(&pool) - testing.expect(t, pool.out_band_allocations == nil) -} - -@(test) -test_dynamic_pool_alloc_aligned :: proc(t: ^testing.T) { - expect_pool_allocation(t, expected_used_bytes = 16, num_bytes = 16, alignment=8) -} - -@(test) -test_dynamic_pool_alloc_unaligned :: proc(t: ^testing.T) { - expect_pool_allocation(t, expected_used_bytes = 8, num_bytes = 1, alignment = 8) - expect_pool_allocation(t, expected_used_bytes = 16, num_bytes = 9, alignment = 8) -} - -@(test) -test_dynamic_pool_alloc_out_of_band :: proc(t: ^testing.T) { - expect_pool_allocation_out_of_band(t, num_bytes = 128, block_size = 512, out_band_size = 128) - expect_pool_allocation_out_of_band(t, num_bytes = 129, block_size = 512, out_band_size = 128) - expect_pool_allocation_out_of_band(t, num_bytes = 513, block_size = 512, out_band_size = 128) -} - -@(test) -test_intentional_leaks :: proc(t: ^testing.T) { - testing.expect_leaks(t, intentionally_leaky_test, leak_verifier) -} - -// Not tagged with @(test) because it's run through `test_intentional_leaks` -intentionally_leaky_test :: proc(t: ^testing.T) { - a: [dynamic]int - // Intentional leak - append(&a, 42) - - // Intentional bad free - b := uintptr(&a[0]) + 42 - free(rawptr(b)) -} - -leak_verifier :: proc(t: ^testing.T, ta: ^mem.Tracking_Allocator) { - testing.expect_value(t, len(ta.allocation_map), 1) - testing.expect_value(t, len(ta.bad_free_array), 1) -} diff --git a/tests/issues/run.bat b/tests/issues/run.bat index 0b92f07ff..43a7136b7 100644 --- a/tests/issues/run.bat +++ b/tests/issues/run.bat @@ -36,6 +36,7 @@ set COMMON=-define:ODIN_TEST_FANCY=false -file -vet -strict-style -ignore-unused ..\..\..\odin test ..\test_pr_6470.odin -define:TEST_EXPECT_FAILURE=true %COMMON% 2>&1 | find /c "Error:" | findstr /x "1" || exit /b ..\..\..\odin test ..\test_pr_6476.odin %COMMON% || exit /b ..\..\..\odin check ..\test_issue_6484.odin -no-entry-point %COMMON% || exit /b +..\..\..\odin check ..\test_issue_6874.odin %COMMON% 2>&1 | find /c "Error:" | findstr /x "1" || exit /b @echo off diff --git a/tests/issues/run.sh b/tests/issues/run.sh index f630738f9..3633a7606 100755 --- a/tests/issues/run.sh +++ b/tests/issues/run.sh @@ -73,6 +73,12 @@ else exit 1 fi $ODIN check ../test_issue_6484.odin -no-entry-point $COMMON +if [[ $($ODIN check ../test_issue_6874.odin $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 1 ]] ; then + echo "SUCCESSFUL 1/1" +else + echo "SUCCESSFUL 0/1" + exit 1 +fi set +x diff --git a/tests/issues/test_issue_6874.odin b/tests/issues/test_issue_6874.odin new file mode 100644 index 000000000..946fef39d --- /dev/null +++ b/tests/issues/test_issue_6874.odin @@ -0,0 +1,35 @@ +// Test for issue #6874 https://github.com/odin-lang/Odin/issues/6874 + +package test_issues + +import "core:fmt" + +PersonData :: struct { + health: int, + age: int, +} + +MyUnion :: union { + f32, + int, + PersonData, +} + +change_union_data :: proc(data: MyUnion) { + switch &v in data { + case int: + v = 10 + case f32: + fmt.println("f32") + case PersonData: + fmt.println("PersonData") + fmt.println(v) + } +} + +main :: proc() { + val: MyUnion = int(12) + fmt.printfln("Before the call: %v", val) + change_union_data(val) + fmt.printfln("After the call: %v", val) +} diff --git a/vendor/raylib/rlgl/rlgl.odin b/vendor/raylib/rlgl/rlgl.odin index 14a7cf5b0..a08479680 100644 --- a/vendor/raylib/rlgl/rlgl.odin +++ b/vendor/raylib/rlgl/rlgl.odin @@ -431,17 +431,19 @@ foreign lib { EnableTextureCubemap :: proc(id: c.uint) --- // Enable texture cubemap DisableTextureCubemap :: proc() --- // Disable texture cubemap TextureParameters :: proc(id: c.uint, param: c.int, value: c.int) --- // Set texture parameters (filter, wrap) - CubemapParameters :: proc(id: i32, param: c.int, value: c.int) --- // Set cubemap parameters (filter, wrap) + CubemapParameters :: proc(id: c.uint, param: c.int, value: c.int) --- // Set cubemap parameters (filter, wrap) // Shader state EnableShader :: proc(id: c.uint) --- // Enable shader program DisableShader :: proc() --- // Disable shader program // Framebuffer state - EnableFramebuffer :: proc(id: c.uint) --- // Enable render texture (fbo) - DisableFramebuffer :: proc() --- // Disable render texture (fbo), return to default framebuffer - ActiveDrawBuffers :: proc(count: c.int) --- // Activate multiple draw color buffers - BlitFramebuffer :: proc(srcX, srcY, srcWidth, srcHeight, dstX, dstY, dstWidth, dstHeight, bufferMask: c.int) --- // Blit active framebuffer to main framebuffer + EnableFramebuffer :: proc(id: c.uint) --- // Enable render texture (fbo) + DisableFramebuffer :: proc() --- // Disable render texture (fbo), return to default framebuffer + GetActiveFramebuffer :: proc() -> c.uint --- // Get the currently active render texture (fbo), 0 for default framebuffer + ActiveDrawBuffers :: proc(count: c.int) --- // Activate multiple draw color buffers + BlitFramebuffer :: proc(srcX, srcY, srcWidth, srcHeight: c.int, dstX, dstY, dstWidth, dstHeight: c.int, bufferMask: c.int) --- // Blit active framebuffer to main framebuffer + BindFramebuffer :: proc(target, framebuffer: c.uint) --- // Bind framebuffer (FBO) // General render state EnableColorBlend :: proc() --- // Enable color blending @@ -452,12 +454,13 @@ foreign lib { DisableDepthMask :: proc() --- // Disable depth write EnableBackfaceCulling :: proc() --- // Enable backface culling DisableBackfaceCulling :: proc() --- // Disable backface culling + ColorMask :: proc(r, g, b, a: bool) --- // Color mask control SetCullFace :: proc(mode: CullMode) --- // Set face culling mode EnableScissorTest :: proc() --- // Enable scissor test DisableScissorTest :: proc() --- // Disable scissor test Scissor :: proc(x, y, width, height: c.int) --- // Scissor test EnableWireMode :: proc() --- // Enable wire mode - EnablePointMode :: proc() --- // Enable point mode + EnablePointMode :: proc() --- // Enable point mode DisableWireMode :: proc() --- // Disable wire and point modes SetLineWidth :: proc(width: f32) --- // Set the line drawing width GetLineWidth :: proc() -> f32 --- // Get the line drawing width @@ -503,7 +506,7 @@ foreign lib { DrawRenderBatch :: proc(batch: ^RenderBatch) --- // Draw render batch data (Update->Draw->Reset) SetRenderBatchActive :: proc(batch: ^RenderBatch) --- // Set the active render batch for rlgl (NULL for default internal) DrawRenderBatchActive :: proc() --- // Update and draw internal render batch - CheckRenderBatchLimit :: proc(vCount: c.int) -> c.int --- // Check internal buffer overflow for a given number of vertex + CheckRenderBatchLimit :: proc(vCount: c.int) -> bool --- // Check internal buffer overflow for a given number of vertex SetTexture :: proc(id: c.uint) --- // Set current texture for render batch and check buffers limits @@ -528,7 +531,7 @@ foreign lib { // Textures management LoadTexture :: proc(data: rawptr, width, height: c.int, format: c.int, mipmapCount: c.int) -> c.uint --- // Load texture in GPU LoadTextureDepth :: proc(width, height: c.int, useRenderBuffer: bool) -> c.uint --- // Load depth texture/renderbuffer (to be attached to fbo) - LoadTextureCubemap :: proc(data: rawptr, size: c.int, format: c.int) -> c.uint --- // Load texture cubemap + LoadTextureCubemap :: proc(data: rawptr, size: c.int, format: c.int, mipmapCount: c.int) -> c.uint --- // Load texture cubemap UpdateTexture :: proc(id: c.uint, offsetX, offsetY: c.int, width, height: c.int, format: c.int, data: rawptr) --- // Update GPU texture with new data GetGlTextureFormats :: proc(format: c.int, glInternalFormat, glFormat, glType: ^c.uint) --- // Get OpenGL internal formats GetPixelFormatName :: proc(format: c.uint) -> cstring --- // Get name string for pixel format @@ -552,6 +555,7 @@ foreign lib { GetLocationAttrib :: proc(shaderId: c.uint, attribName: cstring) -> c.int --- // Get shader location attribute SetUniform :: proc(locIndex: c.int, value: rawptr, uniformType: c.int, count: c.int) --- // Set shader value uniform SetUniformMatrix :: proc(locIndex: c.int, mat: Matrix) --- // Set shader value matrix + SetUniformMatrices :: proc(locIndex: c.int, matrices: [^]Matrix, count: c.int) --- // Set shader value matrices SetUniformSampler :: proc(locIndex: c.int, textureId: c.uint) --- // Set shader value sampler SetShader :: proc(id: c.uint, locs: [^]c.int) --- // Set shader currently active (id and locations) diff --git a/vendor/sdl3/sdl3_audio.odin b/vendor/sdl3/sdl3_audio.odin index 277aefde7..72afd8c98 100644 --- a/vendor/sdl3/sdl3_audio.odin +++ b/vendor/sdl3/sdl3_audio.odin @@ -38,14 +38,14 @@ AudioFormat :: enum c.int { F32 = F32LE when BYTEORDER == LIL_ENDIAN else F32BE, } -@(require_results) AUDIO_BITSIZE :: proc "c" (x: AudioFormat) -> Uint16 { return (Uint16(x) & AUDIO_MASK_BITSIZE) } -@(require_results) AUDIO_BYTESIZE :: proc "c" (x: AudioFormat) -> Uint16 { return AUDIO_BITSIZE(x) / 8 } -@(require_results) AUDIO_ISFLOAT :: proc "c" (x: AudioFormat) -> bool { return (Uint16(x) & AUDIO_MASK_FLOAT) != 0 } -@(require_results) AUDIO_ISBIGENDIAN :: proc "c" (x: AudioFormat) -> bool { return (Uint16(x) & AUDIO_MASK_BIG_ENDIAN) != 0 } -@(require_results) AUDIO_ISLITTLEENDIAN :: proc "c" (x: AudioFormat) -> bool { return !AUDIO_ISBIGENDIAN(x) } -@(require_results) AUDIO_ISSIGNED :: proc "c" (x: AudioFormat) -> bool { return (Uint16(x) & AUDIO_MASK_SIGNED) != 0 } -@(require_results) AUDIO_ISINT :: proc "c" (x: AudioFormat) -> bool { return !AUDIO_ISFLOAT(x) } -@(require_results) AUDIO_ISUNSIGNED :: proc "c" (x: AudioFormat) -> bool { return !AUDIO_ISSIGNED(x) } +@(require_results) AUDIO_BITSIZE :: #force_inline proc "c" (x: AudioFormat) -> Uint16 { return (Uint16(x) & AUDIO_MASK_BITSIZE) } +@(require_results) AUDIO_BYTESIZE :: #force_inline proc "c" (x: AudioFormat) -> Uint16 { return AUDIO_BITSIZE(x) / 8 } +@(require_results) AUDIO_ISFLOAT :: #force_inline proc "c" (x: AudioFormat) -> bool { return (Uint16(x) & AUDIO_MASK_FLOAT) != 0 } +@(require_results) AUDIO_ISBIGENDIAN :: #force_inline proc "c" (x: AudioFormat) -> bool { return (Uint16(x) & AUDIO_MASK_BIG_ENDIAN) != 0 } +@(require_results) AUDIO_ISLITTLEENDIAN :: #force_inline proc "c" (x: AudioFormat) -> bool { return !AUDIO_ISBIGENDIAN(x) } +@(require_results) AUDIO_ISSIGNED :: #force_inline proc "c" (x: AudioFormat) -> bool { return (Uint16(x) & AUDIO_MASK_SIGNED) != 0 } +@(require_results) AUDIO_ISINT :: #force_inline proc "c" (x: AudioFormat) -> bool { return !AUDIO_ISFLOAT(x) } +@(require_results) AUDIO_ISUNSIGNED :: #force_inline proc "c" (x: AudioFormat) -> bool { return !AUDIO_ISSIGNED(x) } AudioDeviceID :: distinct Uint32 @@ -60,7 +60,7 @@ AudioSpec :: struct { } @(require_results) -AUDIO_FRAMESIZE :: proc "c" (x: AudioSpec) -> c.int { +AUDIO_FRAMESIZE :: #force_inline proc "c" (x: AudioSpec) -> c.int { return c.int(AUDIO_BYTESIZE(x.format)) * x.channels } diff --git a/vendor/sdl3/sdl3_hints.odin b/vendor/sdl3/sdl3_hints.odin index ba7e55d3d..64fe486a1 100644 --- a/vendor/sdl3/sdl3_hints.odin +++ b/vendor/sdl3/sdl3_hints.odin @@ -20,6 +20,7 @@ HINT_AUDIO_DEVICE_APP_ICON_NAME :: "SDL_AUDIO_DEVICE_APP_ICON_NAME" HINT_AUDIO_DEVICE_SAMPLE_FRAMES :: "SDL_AUDIO_DEVICE_SAMPLE_FRAMES" HINT_AUDIO_DEVICE_STREAM_NAME :: "SDL_AUDIO_DEVICE_STREAM_NAME" HINT_AUDIO_DEVICE_STREAM_ROLE :: "SDL_AUDIO_DEVICE_STREAM_ROLE" +HINT_AUDIO_DEVICE_RAW_STREAM :: "SDL_AUDIO_DEVICE_RAW_STREAM" HINT_AUDIO_DISK_INPUT_FILE :: "SDL_AUDIO_DISK_INPUT_FILE" HINT_AUDIO_DISK_OUTPUT_FILE :: "SDL_AUDIO_DISK_OUTPUT_FILE" HINT_AUDIO_DISK_TIMESCALE :: "SDL_AUDIO_DISK_TIMESCALE" @@ -36,6 +37,7 @@ HINT_CPU_FEATURE_MASK :: "SDL_CPU_FEATURE_MASK" HINT_JOYSTICK_DIRECTINPUT :: "SDL_JOYSTICK_DIRECTINPUT" HINT_FILE_DIALOG_DRIVER :: "SDL_FILE_DIALOG_DRIVER" HINT_DISPLAY_USABLE_BOUNDS :: "SDL_DISPLAY_USABLE_BOUNDS" +HINT_INVALID_PARAM_CHECKS :: "SDL_INVALID_PARAM_CHECKS" HINT_EMSCRIPTEN_ASYNCIFY :: "SDL_EMSCRIPTEN_ASYNCIFY" HINT_EMSCRIPTEN_CANVAS_SELECTOR :: "SDL_EMSCRIPTEN_CANVAS_SELECTOR" HINT_EMSCRIPTEN_KEYBOARD_ELEMENT :: "SDL_EMSCRIPTEN_KEYBOARD_ELEMENT" @@ -56,6 +58,7 @@ HINT_GDK_TEXTINPUT_MAX_LENGTH :: "SDL_GDK_TEXTINPUT_MAX_LENGTH" HINT_GDK_TEXTINPUT_SCOPE :: "SDL_GDK_TEXTINPUT_SCOPE" HINT_GDK_TEXTINPUT_TITLE :: "SDL_GDK_TEXTINPUT_TITLE" HINT_HIDAPI_LIBUSB :: "SDL_HIDAPI_LIBUSB" +HINT_HIDAPI_LIBUSB_GAMECUBE :: "SDL_HIDAPI_LIBUSB_GAMECUBE" HINT_HIDAPI_LIBUSB_WHITELIST :: "SDL_HIDAPI_LIBUSB_WHITELIST" HINT_HIDAPI_UDEV :: "SDL_HIDAPI_UDEV" HINT_GPU_DRIVER :: "SDL_GPU_DRIVER" @@ -247,6 +250,7 @@ HINT_WINDOWS_ENABLE_MENU_MNEMONICS :: "SDL_WINDOWS_ENABLE_MENU_MNEMONI HINT_WINDOWS_ENABLE_MESSAGELOOP :: "SDL_WINDOWS_ENABLE_MESSAGELOOP" HINT_WINDOWS_GAMEINPUT :: "SDL_WINDOWS_GAMEINPUT" HINT_WINDOWS_RAW_KEYBOARD :: "SDL_WINDOWS_RAW_KEYBOARD" +HINT_WINDOWS_RAW_KEYBOARD_EXCLUDE_HOTKEYS :: "SDL_WINDOWS_RAW_KEYBOARD_EXCLUDE_HOTKEYS" HINT_WINDOWS_FORCE_SEMAPHORE_KERNEL :: "SDL_WINDOWS_FORCE_SEMAPHORE_KERNEL" HINT_WINDOWS_INTRESOURCE_ICON :: "SDL_WINDOWS_INTRESOURCE_ICON" HINT_WINDOWS_INTRESOURCE_ICON_SMALL :: "SDL_WINDOWS_INTRESOURCE_ICON_SMALL" diff --git a/vendor/sdl3/sdl3_joystick.odin b/vendor/sdl3/sdl3_joystick.odin index 78da26923..ea998d8f9 100644 --- a/vendor/sdl3/sdl3_joystick.odin +++ b/vendor/sdl3/sdl3_joystick.odin @@ -2,10 +2,6 @@ package sdl3 import "core:c" -@(link_prefix="SDL_") -foreign lib { - joystick_lock: ^Mutex -} Joystick :: struct {} JoystickID :: distinct Uint32 diff --git a/vendor/sdl3/sdl3_main.odin b/vendor/sdl3/sdl3_main.odin index 103372bed..8c737b5b8 100644 --- a/vendor/sdl3/sdl3_main.odin +++ b/vendor/sdl3/sdl3_main.odin @@ -3,7 +3,7 @@ package sdl3 import "core:c" -main_func :: #type proc(argc: c.int, argv: [^]cstring) +main_func :: #type proc "c" (argc: c.int, argv: [^]cstring) -> c.int @(default_calling_convention="c", link_prefix="SDL_") foreign lib { diff --git a/vendor/sdl3/sdl3_mutex.odin b/vendor/sdl3/sdl3_mutex.odin index 8067473f3..7d48c0b5d 100644 --- a/vendor/sdl3/sdl3_mutex.odin +++ b/vendor/sdl3/sdl3_mutex.odin @@ -1,8 +1,24 @@ package sdl3 +import "core:c" + Mutex :: struct {} RWLock :: struct {} Semaphore :: struct {} +Condition :: struct {} + +InitStatus :: enum c.int { + UNINITIALIZED, + INITIALIZING, + INITIALIZED, + UNINITIALIZING, +} + +InitState :: struct { + status: AtomicInt, + thread: ThreadID, + reserved: rawptr, +} @(default_calling_convention="c", link_prefix="SDL_", require_results) foreign lib { @@ -27,4 +43,15 @@ foreign lib { TryWaitSemaphore :: proc(sem: ^Semaphore) -> bool --- WaitSemaphore :: proc(sem: ^Semaphore) --- WaitSemaphoreTimeout :: proc(sem: ^Semaphore, timeout_ms: Sint32) --- + + CreateCondition :: proc() -> ^Condition --- + DestroyCondition :: proc(cond: ^Condition) --- + SignalCondition :: proc(cond: ^Condition) --- + BroadcastCondition :: proc(cond: ^Condition) --- + WaitCondition :: proc(cond: ^Condition, mutex: ^Mutex) --- + WaitConditionTimeout :: proc(cond: ^Condition, mutex: ^Mutex, timeout_ms: Sint32) -> bool --- + + ShouldInit :: proc(state: ^InitState) -> bool --- + ShouldQuit :: proc(state: ^InitState) -> bool --- + SetInitialized :: proc(state: ^InitState, initialized: bool) --- } diff --git a/vendor/sdl3/sdl3_pixels.odin b/vendor/sdl3/sdl3_pixels.odin index e3a1c1537..af6541757 100644 --- a/vendor/sdl3/sdl3_pixels.odin +++ b/vendor/sdl3/sdl3_pixels.odin @@ -75,20 +75,20 @@ DEFINE_PIXELFORMAT :: #force_inline proc "c" (type: PixelType, order: PackedOrde return PixelFormat(((1 << 28) | (Uint32(type) << 24) | (Uint32(order) << 20) | (Uint32(layout) << 16) | (Uint32(bits) << 8) | (Uint32(bytes) << 0))) } -@(require_results) PIXELFLAG :: proc "c" (format: PixelFormat) -> Uint32 { return ((Uint32(format) >> 28) & 0x0F) } -@(require_results) PIXELTYPE :: proc "c" (format: PixelFormat) -> PixelType { return PixelType((Uint32(format) >> 24) & 0x0F) } -@(require_results) PIXELORDER :: proc "c" (format: PixelFormat) -> PackedOrder { return PackedOrder((Uint32(format) >> 20) & 0x0F) } -@(require_results) PIXELLAYOUT :: proc "c" (format: PixelFormat) -> PackedLayout { return PackedLayout((Uint32(format) >> 16) & 0x0F) } -@(require_results) PIXELARRAYORDER :: proc "c" (format: PixelFormat) -> ArrayOrder { return ArrayOrder((Uint32(format) >> 20) & 0x0F) } +@(require_results) PIXELFLAG :: #force_inline proc "c" (format: PixelFormat) -> Uint32 { return ((Uint32(format) >> 28) & 0x0F) } +@(require_results) PIXELTYPE :: #force_inline proc "c" (format: PixelFormat) -> PixelType { return PixelType((Uint32(format) >> 24) & 0x0F) } +@(require_results) PIXELORDER :: #force_inline proc "c" (format: PixelFormat) -> PackedOrder { return PackedOrder((Uint32(format) >> 20) & 0x0F) } +@(require_results) PIXELLAYOUT :: #force_inline proc "c" (format: PixelFormat) -> PackedLayout { return PackedLayout((Uint32(format) >> 16) & 0x0F) } +@(require_results) PIXELARRAYORDER :: #force_inline proc "c" (format: PixelFormat) -> ArrayOrder { return ArrayOrder((Uint32(format) >> 20) & 0x0F) } @(require_results) -BITSPERPIXEL :: proc "c" (format: PixelFormat) -> Uint32 { +BITSPERPIXEL :: #force_inline proc "c" (format: PixelFormat) -> Uint32 { return ISPIXELFORMAT_FOURCC(format) ? 0 : ((Uint32(format) >> 8) & 0xFF) } @(require_results) -ISPIXELFORMAT_INDEXED :: proc "c" (format: PixelFormat) -> bool { +ISPIXELFORMAT_INDEXED :: #force_inline proc "c" (format: PixelFormat) -> bool { return (!ISPIXELFORMAT_FOURCC(format) && ((PIXELTYPE(format) == .INDEX1) || (PIXELTYPE(format) == .INDEX2) || @@ -98,7 +98,7 @@ ISPIXELFORMAT_INDEXED :: proc "c" (format: PixelFormat) -> bool { @(require_results) -ISPIXELFORMAT_PACKED :: proc "c" (format: PixelFormat) -> bool { +ISPIXELFORMAT_PACKED :: #force_inline proc "c" (format: PixelFormat) -> bool { return (!ISPIXELFORMAT_FOURCC(format) && ((PIXELTYPE(format) == .PACKED8) || (PIXELTYPE(format) == .PACKED16) || @@ -106,7 +106,7 @@ ISPIXELFORMAT_PACKED :: proc "c" (format: PixelFormat) -> bool { } @(require_results) -ISPIXELFORMAT_ARRAY :: proc "c" (format: PixelFormat) -> bool { +ISPIXELFORMAT_ARRAY :: #force_inline proc "c" (format: PixelFormat) -> bool { return (!ISPIXELFORMAT_FOURCC(format) && ((PIXELTYPE(format) == .ARRAYU8) || (PIXELTYPE(format) == .ARRAYU16) || @@ -116,21 +116,21 @@ ISPIXELFORMAT_ARRAY :: proc "c" (format: PixelFormat) -> bool { } @(require_results) -ISPIXELFORMAT_10BIT :: proc "c" (format: PixelFormat) -> bool { +ISPIXELFORMAT_10BIT :: #force_inline proc "c" (format: PixelFormat) -> bool { return (!ISPIXELFORMAT_FOURCC(format) && ((PIXELTYPE(format) == .PACKED32) && (PIXELLAYOUT(format) == .LAYOUT_2101010))) } @(require_results) -ISPIXELFORMAT_FLOAT :: proc "c" (format: PixelFormat) -> bool { +ISPIXELFORMAT_FLOAT :: #force_inline proc "c" (format: PixelFormat) -> bool { return (!ISPIXELFORMAT_FOURCC(format) && ((PIXELTYPE(format) == .ARRAYF16) || (PIXELTYPE(format) == .ARRAYF32))) } @(require_results) -ISPIXELFORMAT_ALPHA :: proc "c" (format: PixelFormat) -> bool { +ISPIXELFORMAT_ALPHA :: #force_inline proc "c" (format: PixelFormat) -> bool { return ((ISPIXELFORMAT_PACKED(format) && ((PIXELORDER(format) == .ARGB) || (PIXELORDER(format) == .RGBA) || @@ -144,7 +144,7 @@ ISPIXELFORMAT_ALPHA :: proc "c" (format: PixelFormat) -> bool { } @(require_results) -ISPIXELFORMAT_FOURCC :: proc "c" (format: PixelFormat) -> bool { +ISPIXELFORMAT_FOURCC :: #force_inline proc "c" (format: PixelFormat) -> bool { return format != nil && PIXELFLAG(format) != 1 } @@ -369,63 +369,63 @@ ChromaLocation :: enum c.int { @(require_results) -DEFINE_COLORSPACE :: proc "c" (type: ColorType, range: ColorRange, primaries: ColorPrimaries, transfer: TransferCharacteristics, matrix_: MatrixCoefficients, chroma: ChromaLocation) -> Colorspace { +DEFINE_COLORSPACE :: #force_inline proc "c" (type: ColorType, range: ColorRange, primaries: ColorPrimaries, transfer: TransferCharacteristics, matrix_: MatrixCoefficients, chroma: ChromaLocation) -> Colorspace { return Colorspace((Uint32(type) << 28) | (Uint32(range) << 24) | (Uint32(chroma) << 20) | (Uint32(primaries) << 10) | (Uint32(transfer) << 5) | (Uint32(matrix_) << 0)) } @(require_results) -COLORSPACETYPE :: proc "c" (cspace: Colorspace) -> ColorType { +COLORSPACETYPE :: #force_inline proc "c" (cspace: Colorspace) -> ColorType { return ColorType((Uint32(cspace) >> 28) & 0x0F) } @(require_results) -COLORSPACERANGE :: proc "c" (cspace: Colorspace) -> ColorRange { +COLORSPACERANGE :: #force_inline proc "c" (cspace: Colorspace) -> ColorRange { return ColorRange((Uint32(cspace) >> 24) & 0x0F) } @(require_results) -COLORSPACECHROMA :: proc "c" (cspace: Colorspace) -> ChromaLocation { +COLORSPACECHROMA :: #force_inline proc "c" (cspace: Colorspace) -> ChromaLocation { return ChromaLocation((Uint32(cspace) >> 20) & 0x0F) } @(require_results) -COLORSPACEPRIMARIES :: proc "c" (cspace: Colorspace) -> ColorPrimaries { +COLORSPACEPRIMARIES :: #force_inline proc "c" (cspace: Colorspace) -> ColorPrimaries { return ColorPrimaries((Uint32(cspace) >> 10) & 0x1F) } @(require_results) -COLORSPACETRANSFER :: proc "c" (cspace: Colorspace) -> TransferCharacteristics { +COLORSPACETRANSFER :: #force_inline proc "c" (cspace: Colorspace) -> TransferCharacteristics { return TransferCharacteristics((Uint32(cspace) >> 5) & 0x1F) } @(require_results) -COLORSPACEMATRIX :: proc "c" (cspace: Colorspace) -> MatrixCoefficients { +COLORSPACEMATRIX :: #force_inline proc "c" (cspace: Colorspace) -> MatrixCoefficients { return MatrixCoefficients(Uint32(cspace) & 0x1F) } @(require_results) -ISCOLORSPACE_MATRIX_BT601 :: proc "c" (cspace: Colorspace) -> bool { +ISCOLORSPACE_MATRIX_BT601 :: #force_inline proc "c" (cspace: Colorspace) -> bool { return COLORSPACEMATRIX(cspace) == .BT601 || COLORSPACEMATRIX(cspace) == .BT470BG } @(require_results) -ISCOLORSPACE_MATRIX_BT709 :: proc "c" (cspace: Colorspace) -> bool { +ISCOLORSPACE_MATRIX_BT709 :: #force_inline proc "c" (cspace: Colorspace) -> bool { return COLORSPACEMATRIX(cspace) == .BT709 } @(require_results) -ISCOLORSPACE_MATRIX_BT2020_NCL :: proc "c" (cspace: Colorspace) -> bool { +ISCOLORSPACE_MATRIX_BT2020_NCL :: #force_inline proc "c" (cspace: Colorspace) -> bool { return COLORSPACEMATRIX(cspace) == .BT2020_NCL } @(require_results) -ISCOLORSPACE_LIMITED_RANGE :: proc "c" (cspace: Colorspace) -> bool { +ISCOLORSPACE_LIMITED_RANGE :: #force_inline proc "c" (cspace: Colorspace) -> bool { return COLORSPACERANGE(cspace) != .FULL } @(require_results) -ISCOLORSPACE_FULL_RANGE :: proc "c" (cspace: Colorspace) -> bool { +ISCOLORSPACE_FULL_RANGE :: #force_inline proc "c" (cspace: Colorspace) -> bool { return COLORSPACERANGE(cspace) == .FULL } diff --git a/vendor/sdl3/sdl3_rect.odin b/vendor/sdl3/sdl3_rect.odin index 6af9dc937..be082119e 100644 --- a/vendor/sdl3/sdl3_rect.odin +++ b/vendor/sdl3/sdl3_rect.odin @@ -24,38 +24,51 @@ RectToFRect :: #force_inline proc "c" (rect: Rect, frect: ^FRect) { @(require_results) -PointInRect :: proc "c" (p: Point, r: Rect) -> bool { +PointInRect :: #force_inline proc "c" (p: Point, r: Rect) -> bool { return ( (p.x >= r.x) && (p.x < (r.x + r.w)) && (p.y >= r.y) && (p.y < (r.y + r.h)) ) } @(require_results) -PointInRectFloat :: proc "c" (p: FPoint, r: FRect) -> bool { +PointInRectFloat :: #force_inline proc "c" (p: FPoint, r: FRect) -> bool { return ( (p.x >= r.x) && (p.x <= (r.x + r.w)) && (p.y >= r.y) && (p.y <= (r.y + r.h)) ) } @(require_results) -RectEmpty :: proc "c" (r: Rect) -> bool { +RectEmpty :: #force_inline proc "c" (r: Rect) -> bool { return r.w <= 0 || r.h <= 0 } @(require_results) -RectEqual :: proc "c" (a, b: Rect) -> bool { +RectEqual :: #force_inline proc "c" (a, b: Rect) -> bool { return a == b } +@(require_results) +RectsEqualEpsilon :: #force_inline proc "c" (a, b: FRect, epsilon: f32) -> bool { + return abs(a.x - b.x) <= epsilon && + abs(a.y - b.y) <= epsilon && + abs(a.w - b.w) <= epsilon && + abs(a.h - b.h) <= epsilon +} + +@(require_results) +RectsEqualFloat :: #force_inline proc "c" (a, b: FRect) -> bool { + return RectsEqualEpsilon(a, b, FLT_EPSILON) +} + @(default_calling_convention="c", link_prefix="SDL_", require_results) foreign lib { HasRectIntersection :: proc(#by_ptr A, B: Rect) -> bool --- GetRectIntersection :: proc(#by_ptr A, B: Rect, result: ^Rect) -> bool --- GetRectUnion :: proc(#by_ptr A, B: Rect, result: ^Rect) -> bool --- - GetRectEnclosingPoints :: proc(points: [^]Point, count: c.int, #by_ptr clip: Rect, result: ^Rect) -> bool --- + GetRectEnclosingPoints :: proc(points: [^]Point, count: c.int, clip: Maybe(^Rect), result: ^Rect) -> bool --- GetRectAndLineIntersection :: proc(#by_ptr rect: Rect, X1, Y1, X2, Y2: ^c.int) -> bool --- HasRectIntersectionFloat :: proc(#by_ptr A, B: FRect) -> bool --- GetRectIntersectionFloat :: proc(#by_ptr A, B: FRect, result: ^FRect) -> bool --- GetRectUnionFloat :: proc(#by_ptr A, B: FRect, result: ^FRect) -> bool --- - GetRectEnclosingPointsFloat :: proc(points: [^]FPoint, count: c.int, #by_ptr clip: FRect, result: ^FRect) -> bool --- + GetRectEnclosingPointsFloat :: proc(points: [^]FPoint, count: c.int, clip: Maybe(^FRect), result: ^FRect) -> bool --- GetRectAndLineIntersectionFloat :: proc(#by_ptr rect: FRect, X1, Y1, X2, Y2: ^f32) -> bool --- } \ No newline at end of file diff --git a/vendor/sdl3/sdl3_render.odin b/vendor/sdl3/sdl3_render.odin index fd5b0705e..759683dfe 100644 --- a/vendor/sdl3/sdl3_render.odin +++ b/vendor/sdl3/sdl3_render.odin @@ -240,8 +240,8 @@ foreign lib { RenderTextureTiled :: proc(renderer: ^Renderer, texture: ^Texture, srcrect: Maybe(^FRect), scale: f32, dstrect: Maybe(^FRect)) -> bool --- RenderTexture9Grid :: proc(renderer: ^Renderer, texture: ^Texture, srcrect: Maybe(^FRect), left_width, right_width, top_height, bottom_height: f32, scale: f32, dstrect: Maybe(^FRect)) -> bool --- RenderTexture9GridTiled :: proc(renderer: ^Renderer, texture: ^Texture, srcrect: Maybe(^FRect), left_width, right_width, top_height, bottom_height: f32, scale: f32, dstrect: Maybe(^FRect), tileScale: f32) -> bool --- - RenderGeometry :: proc(renderer: ^Renderer, texture: ^Texture, vertices: [^]Vertex, num_vertices: c.int, indices: [^]c.int, num_indices: c.int) -> bool --- - RenderGeometryRaw :: proc(renderer: ^Renderer, texture: ^Texture, xy: [^]f32, xy_stride: c.int, color: [^]FColor, color_stride: c.int, uv: [^]f32, uv_stride: c.int, num_vertices: c.int, indices: rawptr, num_indices: c.int, size_indices: c.int) -> bool --- + RenderGeometry :: proc(renderer: ^Renderer, texture: Maybe(^Texture), vertices: [^]Vertex, num_vertices: c.int, indices: [^]c.int, num_indices: c.int) -> bool --- + RenderGeometryRaw :: proc(renderer: ^Renderer, texture: Maybe(^Texture), xy: [^]f32, xy_stride: c.int, color: [^]FColor, color_stride: c.int, uv: [^]f32, uv_stride: c.int, num_vertices: c.int, indices: rawptr, num_indices: c.int, size_indices: c.int) -> bool --- SetRenderTextureAddressMode :: proc(renderer: ^Renderer, u_mode, v_mode: TextureAddressMode) -> bool --- GetRenderTextureAddressMode :: proc(renderer: ^Renderer, u_mode, v_mode: ^TextureAddressMode) -> bool --- RenderPresent :: proc(renderer: ^Renderer) -> bool --- @@ -276,5 +276,5 @@ foreign lib { CreateGPURenderState :: proc(renderer: ^Renderer, #by_ptr createinfo: GPURenderStateCreateInfo) -> ^GPURenderState --- SetGPURenderStateFragmentUniforms :: proc(state: ^GPURenderState, slot_index: Uint32, data: rawptr, length: Uint32) -> bool --- SetGPURenderState :: proc(renderer: ^Renderer, state: ^GPURenderState) -> bool --- - DestroyGPURenderState :: proc(renderer: ^Renderer) --- + DestroyGPURenderState :: proc(renderer: ^GPURenderState) --- } diff --git a/vendor/sdl3/sdl3_system.odin b/vendor/sdl3/sdl3_system.odin index 44c026e82..63b0637d7 100644 --- a/vendor/sdl3/sdl3_system.odin +++ b/vendor/sdl3/sdl3_system.odin @@ -6,7 +6,7 @@ import "core:c" import win32 "core:sys/windows" -WindowsMessageHook :: #type proc(userdata: rawptr, msg: ^win32.MSG) -> bool +WindowsMessageHook :: #type proc "c" (userdata: rawptr, msg: ^win32.MSG) -> bool @(default_calling_convention="c", link_prefix="SDL_") foreign lib { @@ -46,6 +46,11 @@ foreign lib { RequestAndroidPermissionCallback :: #type proc "c" (userdata: rawptr, permission: cstring, granted: bool) +AndroidExternalStorageFlags :: distinct bit_set[AndroidExternalStorageFlag; Uint32] +AndroidExternalStorageFlag :: enum Uint32 { + Read, + Write, +} @(default_calling_convention="c", link_prefix="SDL_", require_results) foreign lib { @@ -56,7 +61,7 @@ foreign lib { IsDeXMode :: proc() -> bool --- SendAndroidBackButton :: proc() --- GetAndroidInternalStoragePath :: proc() -> cstring --- - GetAndroidExternalStorageState :: proc() -> Uint32 --- + GetAndroidExternalStorageState :: proc() -> AndroidExternalStorageFlags --- GetAndroidExternalStoragePath :: proc() -> cstring --- GetAndroidCachePath :: proc() -> cstring --- RequestAndroidPermission :: proc(permission: cstring, cb: RequestAndroidPermissionCallback, userdata: rawptr) -> bool --- diff --git a/vendor/sdl3/sdl3_video.odin b/vendor/sdl3/sdl3_video.odin index e4547d991..3ddcfedc6 100644 --- a/vendor/sdl3/sdl3_video.odin +++ b/vendor/sdl3/sdl3_video.odin @@ -151,7 +151,7 @@ EGLSurface :: distinct rawptr EGLAttrib :: distinct uintptr EGLint :: distinct c.int -EGLAttribArrayCallback :: #type proc "c" (userdata: rawptr) -> ^EGLint +EGLAttribArrayCallback :: #type proc "c" (userdata: rawptr) -> [^]EGLint EGLIntArrayCallback :: #type proc "c" (userdata: rawptr, display: EGLDisplay, config: EGLConfig) -> [^]EGLint GLAttr :: enum c.int { @@ -256,9 +256,12 @@ GL_CONTEXT_RESET_LOSE_CONTEXT :: GLContextResetNotification{.LOSE_CONTEXT} PROP_DISPLAY_HDR_ENABLED_BOOLEAN :: "SDL.display.HDR_enabled" PROP_DISPLAY_KMSDRM_PANEL_ORIENTATION_NUMBER :: "SDL.display.KMSDRM.panel_orientation" +PROP_DISPLAY_WAYLAND_WL_OUTPUT_POINTER :: "SDL.display.wayland.wl_output" +PROP_DISPLAY_WINDOWS_HMONITOR_POINTER :: "SDL.display.windows.hmonitor" PROP_WINDOW_CREATE_ALWAYS_ON_TOP_BOOLEAN :: "SDL.window.create.always_on_top" PROP_WINDOW_CREATE_BORDERLESS_BOOLEAN :: "SDL.window.create.borderless" +PROP_WINDOW_CREATE_CONSTRAIN_POPUP_BOOLEAN :: "SDL.window.create.constrain_popup" PROP_WINDOW_CREATE_FOCUSABLE_BOOLEAN :: "SDL.window.create.focusable" PROP_WINDOW_CREATE_EXTERNAL_GRAPHICS_CONTEXT_BOOLEAN :: "SDL.window.create.external_graphics_context" PROP_WINDOW_CREATE_FLAGS_NUMBER :: "SDL.window.create.flags" diff --git a/vendor/stb/truetype/stb_truetype.odin b/vendor/stb/truetype/stb_truetype.odin index d88fafda1..9d8674941 100644 --- a/vendor/stb/truetype/stb_truetype.odin +++ b/vendor/stb/truetype/stb_truetype.odin @@ -111,7 +111,7 @@ pack_range :: struct { first_unicode_codepoint_in_range: c.int, array_of_unicode_codepoints: [^]rune, num_chars: c.int, - chardata_for_range: ^packedchar, + chardata_for_range: [^]packedchar, _, _: u8, // used internally to store oversample info } @@ -138,7 +138,7 @@ foreign stbtt { // bilinear filtering). // // Returns 0 on failure, 1 on success. - PackBegin :: proc(spc: ^pack_context, pixels: [^]byte, width, height, stride_in_bytes, padding: c.int, alloc_context: rawptr) -> c.int --- + PackBegin :: proc(spc: ^pack_context, pixels: [^]byte, width, height, stride_in_bytes, padding: c.int, alloc_context: rawptr) -> b32 --- // Cleans up the packing context and frees all memory. PackEnd :: proc(spc: ^pack_context) --- @@ -155,13 +155,17 @@ foreign stbtt { // and pass that result as 'font_size': // ..., 20 , ... // font max minus min y is 20 pixels tall // ..., POINT_SIZE(20), ... // 'M' is 20 pixels tall - PackFontRange :: proc(spc: ^pack_context, fontdata: [^]byte, font_index: c.int, font_size: f32, first_unicode_char_in_range, num_chars_in_range: c.int, chardata_for_range: ^packedchar) -> c.int --- + // + // Returns 0 on failure, 1 on success. + PackFontRange :: proc(spc: ^pack_context, fontdata: [^]byte, font_index: c.int, font_size: f32, first_unicode_char_in_range, num_chars_in_range: c.int, chardata_for_range: [^]packedchar) -> b32 --- // Creates character bitmaps from multiple ranges of characters stored in // ranges. This will usually create a better-packed bitmap than multiple // calls to stbtt_PackFontRange. Note that you can call this multiple // times within a single PackBegin/PackEnd. - PackFontRanges :: proc(spc: ^pack_context, fontdata: [^]byte, font_index: c.int, ranges: [^]pack_range, num_ranges: c.int) -> c.int --- + // + // Returns 0 on failure, 1 on success. + PackFontRanges :: proc(spc: ^pack_context, fontdata: [^]byte, font_index: c.int, ranges: [^]pack_range, num_ranges: c.int) -> b32 --- // Oversampling a font increases the quality by allowing higher-quality subpixel // positioning, and is especially valuable at smaller text sizes. @@ -201,9 +205,13 @@ foreign stbtt { // then call RenderIntoRects repeatedly. This may result in a // better packing than calling PackFontRanges multiple times // (or it may not). - PackFontRangesGatherRects :: proc(spc: ^pack_context, info: ^fontinfo, ranges: ^pack_range, num_ranges: c.int, rects: [^]stbrp.Rect) -> c.int --- + // + // Returns the number of rects which were not skipped. + // PackSetSkipMissingCodepoints controls this behavior. + PackFontRangesGatherRects :: proc(spc: ^pack_context, info: ^fontinfo, ranges: [^]pack_range, num_ranges: c.int, rects: [^]stbrp.Rect) -> c.int --- PackFontRangesPackRects :: proc(spc: ^pack_context, rects: [^]stbrp.Rect, num_rects: c.int) --- - PackFontRangesRenderIntoRects :: proc(spc: ^pack_context, info: ^fontinfo, ranges: ^pack_range, num_ranges: c.int, rects: [^]stbrp.Rect) -> c.int --- + // Returns 0 on failure, 1 on success. + PackFontRangesRenderIntoRects :: proc(spc: ^pack_context, info: ^fontinfo, ranges: [^]pack_range, num_ranges: c.int, rects: [^]stbrp.Rect) -> b32 --- } ////////////////////////////////////////////////////////////////////////////// diff --git a/vendor/x11/xlib/xlib.odin b/vendor/x11/xlib/xlib.odin index a2d51c401..c4071055a 100644 --- a/vendor/x11/xlib/xlib.odin +++ b/vendor/x11/xlib/xlib.odin @@ -1,4 +1,4 @@ -// Bindings for [[ X11's Xlib (PDF) ; https://www.x.org/docs/X11/xlib.pdf ]]. +// Bindings for [[ X11's Xlib (PDF) ; https://xorg.freedesktop.org/archive/current/doc/libX11/libX11/libX11.pdf ]]. package xlib // Value, specifying whether `vendor:x11/xlib` is available on the current platform.