diff options
65 files changed, 2352 insertions, 362 deletions
diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index 952f927bd..d34519f63 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -244,6 +244,11 @@ constant_utf16_cstring :: proc($literal: string) -> [^]u16 --- constant_log2 :: proc($v: $T) -> T where type_is_integer(T) --- +constant_floor :: proc($v: $T) -> T where type_is_integer(T) || type_is_float(T) --- +constant_trunc :: proc($v: $T) -> T where type_is_integer(T) || type_is_float(T) --- +constant_ceil :: proc($v: $T) -> T where type_is_integer(T) || type_is_float(T) --- +constant_round :: proc($v: $T) -> T where type_is_integer(T) || type_is_float(T) --- + // SIMD related simd_add :: proc(a, b: #simd[N]T) -> #simd[N]T --- simd_sub :: proc(a, b: #simd[N]T) -> #simd[N]T --- diff --git a/base/runtime/core_builtin_soa.odin b/base/runtime/core_builtin_soa.odin index 331254ac7..0ccc8fd9b 100644 --- a/base/runtime/core_builtin_soa.odin +++ b/base/runtime/core_builtin_soa.odin @@ -501,6 +501,121 @@ append_soa :: proc{ } +// `append_nothing_soa` appends an empty value to a dynamic SOA array. It returns `1, nil` if successful, and `0, err` when it was not possible, +// whatever `err` happens to be. +@builtin +append_nothing_soa :: proc(array: ^$T/#soa[dynamic]$E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { + if array == nil { + return 0, nil + } + prev_len := len(array) + resize_soa(array, len(array)+1, loc) or_return + return len(array)-prev_len, nil +} + + +// `inject_at_elem_soa` injects an element in a dynamic SOA array at a specified index and moves the previous elements after that index "across" +@builtin +inject_at_elem_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int index: int, #no_broadcast arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { + when !ODIN_NO_BOUNDS_CHECK { + ensure(index >= 0, "Index must be positive.", loc) + } + if array == nil { + return + } + n := max(len(array), index) + m :: 1 + new_len := n + m + + resize_soa(array, new_len, loc) or_return + + when size_of(E) != 0 { + ti := type_info_base(type_info_of(typeid_of(T))) + si := &ti.variant.(Type_Info_Struct) + + field_count := len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E) + + item_offset := 0 + + arg_copy := arg + arg_ptr := &arg_copy + + for i in 0..<field_count { + data := (^uintptr)(uintptr(array) + uintptr(si.offsets[i]))^ + type := si.types[i].variant.(Type_Info_Multi_Pointer).elem + item_offset = align_forward_int(item_offset, type.align) + + src := data + uintptr(index * type.size) + dst := data + uintptr((index + m) * type.size) + mem_copy(rawptr(dst), rawptr(src), (n - index) * type.size) + + mem_copy(rawptr(src), rawptr(uintptr(arg_ptr) + uintptr(item_offset)), type.size) + + item_offset += type.size + } + } + + ok = true + return +} + +// `inject_at_elems_soa` injects multiple elements in a dynamic SOA array at a specified index and moves the previous elements after that index "across" +@builtin +inject_at_elems_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { + when !ODIN_NO_BOUNDS_CHECK { + ensure(index >= 0, "Index must be positive.", loc) + } + if array == nil { + return + } + if len(args) == 0 { + ok = true + return + } + + n := max(len(array), index) + m := len(args) + new_len := n + m + + resize_soa(array, new_len, loc) or_return + + when size_of(E) != 0 { + ti := type_info_base(type_info_of(typeid_of(T))) + si := &ti.variant.(Type_Info_Struct) + + field_count := len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E) + + item_offset := 0 + + args_ptr := &args[0] + + for i in 0..<field_count { + data := (^uintptr)(uintptr(array) + uintptr(si.offsets[i]))^ + type := si.types[i].variant.(Type_Info_Multi_Pointer).elem + item_offset = align_forward_int(item_offset, type.align) + + src := data + uintptr(index * type.size) + dst := data + uintptr((index + m) * type.size) + mem_copy(rawptr(dst), rawptr(src), (n - index) * type.size) + + for j in 0..<len(args) { + d := rawptr(src + uintptr(j*type.size)) + s := rawptr(uintptr(args_ptr) + uintptr(item_offset) + uintptr(j*size_of(E))) + mem_copy(d, s, type.size) + } + + item_offset += type.size + } + } + + ok = true + return +} + +// `inject_at_soa` injects something into a dynamic SOA array at a specified index and moves the previous elements after that index "across" +@builtin inject_at_soa :: proc{inject_at_elem_soa, inject_at_elems_soa} + + delete_soa_slice :: proc(array: $T/#soa[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error { field_count :: len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E) when field_count != 0 { diff --git a/base/runtime/os_specific.odin b/base/runtime/os_specific.odin index 2807eaf90..b6c1288d0 100644 --- a/base/runtime/os_specific.odin +++ b/base/runtime/os_specific.odin @@ -5,3 +5,7 @@ _OS_Errno :: distinct int stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) { return _stderr_write(data) } + +exit :: proc "contextless" (code: int) -> ! { + _exit(code) +}
\ No newline at end of file diff --git a/base/runtime/os_specific_bsd.odin b/base/runtime/os_specific_bsd.odin index 466001ada..de300f1e0 100644 --- a/base/runtime/os_specific_bsd.odin +++ b/base/runtime/os_specific_bsd.odin @@ -24,3 +24,11 @@ _stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) { } return int(ret), 0 } + +_exit :: proc "contextless" (code: int) -> ! { + @(default_calling_convention="c") + foreign libc { + exit :: proc(status: i32) -> ! --- + } + exit(i32(code)) +}
\ No newline at end of file diff --git a/base/runtime/os_specific_darwin.odin b/base/runtime/os_specific_darwin.odin index 907899d7c..37315240f 100644 --- a/base/runtime/os_specific_darwin.odin +++ b/base/runtime/os_specific_darwin.odin @@ -26,3 +26,13 @@ _stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) { return 0, _OS_Errno(__error()^) } } + +foreign import libc "system:System" + +_exit :: proc "contextless" (code: int) -> ! { + @(default_calling_convention="c") + foreign libc { + exit :: proc(status: i32) -> ! --- + } + exit(i32(code)) +}
\ No newline at end of file diff --git a/base/runtime/os_specific_freestanding.odin b/base/runtime/os_specific_freestanding.odin index f975f61b8..b5a5fb146 100644 --- a/base/runtime/os_specific_freestanding.odin +++ b/base/runtime/os_specific_freestanding.odin @@ -6,3 +6,7 @@ package runtime _stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) { return 0, -1 } + +_exit :: proc "contextless" (code: int) -> ! { + trap() +}
\ No newline at end of file diff --git a/base/runtime/os_specific_haiku.odin b/base/runtime/os_specific_haiku.odin index 0d1ec7a03..74ff58cde 100644 --- a/base/runtime/os_specific_haiku.odin +++ b/base/runtime/os_specific_haiku.odin @@ -19,3 +19,9 @@ _stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) { } return int(ret), 0 } + + + +_exit :: proc "contextless" (code: int) -> ! { + trap() +}
\ No newline at end of file diff --git a/base/runtime/os_specific_js.odin b/base/runtime/os_specific_js.odin index f1d12c808..bd88b1871 100644 --- a/base/runtime/os_specific_js.odin +++ b/base/runtime/os_specific_js.odin @@ -11,3 +11,8 @@ _stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) { write(1, data) return len(data), 0 } + + +_exit :: proc "contextless" (code: int) -> ! { + trap() +}
\ No newline at end of file diff --git a/base/runtime/os_specific_linux.odin b/base/runtime/os_specific_linux.odin index d7b7371a7..dfe3c8841 100644 --- a/base/runtime/os_specific_linux.odin +++ b/base/runtime/os_specific_linux.odin @@ -24,3 +24,17 @@ _stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) { } return ret, 0 } + + +_exit :: proc "contextless" (code: int) -> ! { + SYS_exit_group :: + 231 when ODIN_ARCH == .amd64 else + 248 when ODIN_ARCH == .arm32 else + 94 when ODIN_ARCH == .arm64 else + 252 when ODIN_ARCH == .i386 else + 94 when ODIN_ARCH == .riscv64 else + 0 + + intrinsics.syscall(uintptr(SYS_exit_group), uintptr(i32(code))) + unreachable() +} diff --git a/base/runtime/os_specific_orca.odin b/base/runtime/os_specific_orca.odin index 876d7d44b..491edcfa4 100644 --- a/base/runtime/os_specific_orca.odin +++ b/base/runtime/os_specific_orca.odin @@ -41,3 +41,8 @@ _stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) { return len(data), 0 } + + +_exit :: proc "contextless" (code: int) -> ! { + trap() +}
\ No newline at end of file diff --git a/base/runtime/os_specific_wasi.odin b/base/runtime/os_specific_wasi.odin index aa562050c..194034865 100644 --- a/base/runtime/os_specific_wasi.odin +++ b/base/runtime/os_specific_wasi.odin @@ -23,6 +23,9 @@ foreign wasi { argv: [^]cstring, argv_buf: [^]byte, ) -> u16 --- + + @(private="file") + proc_exit :: proc(rval: u32) -> ! --- } _stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) { @@ -53,3 +56,8 @@ _wasi_setup_args :: proc() { delete(args_buf) } } + + +_exit :: proc "contextless" (code: int) -> ! { + proc_exit(u32(code)) +}
\ No newline at end of file diff --git a/base/runtime/os_specific_windows.odin b/base/runtime/os_specific_windows.odin index b966193ca..c5ca1e4c5 100644 --- a/base/runtime/os_specific_windows.odin +++ b/base/runtime/os_specific_windows.odin @@ -14,6 +14,8 @@ foreign kernel32 { SetHandleInformation :: proc(hObject: rawptr, dwMask: u32, dwFlags: u32) -> b32 --- WriteFile :: proc(hFile: rawptr, lpBuffer: rawptr, nNumberOfBytesToWrite: u32, lpNumberOfBytesWritten: ^u32, lpOverlapped: rawptr) -> b32 --- GetLastError :: proc() -> u32 --- + + ExitProcess :: proc(code: u32) -> ! --- } _stderr_write :: proc "contextless" (data: []byte) -> (n: int, err: _OS_Errno) #no_bounds_check { @@ -49,3 +51,7 @@ _stderr_write :: proc "contextless" (data: []byte) -> (n: int, err: _OS_Errno) # n = int(total_write) return } + +_exit :: proc "contextless" (code: int) -> ! { + ExitProcess(u32(code)) +}
\ No newline at end of file diff --git a/core/container/small_array/small_array.odin b/core/container/small_array/small_array.odin index 1d9795db8..a7e046fcf 100644 --- a/core/container/small_array/small_array.odin +++ b/core/container/small_array/small_array.odin @@ -309,7 +309,7 @@ Example: import "core:container/small_array" import "core:fmt" - non_zero_resize :: proc() { + non_zero_resize_example :: proc() { a: small_array.Small_Array(5, int) small_array.push_back(&a, 1) diff --git a/core/encoding/cbor/tags.odin b/core/encoding/cbor/tags.odin index be07b926a..fa456673d 100644 --- a/core/encoding/cbor/tags.odin +++ b/core/encoding/cbor/tags.odin @@ -130,9 +130,9 @@ tag_time_unmarshal :: proc(_: ^Tag_Implementation, d: Decoder, _: Tag_Number, v: case .U8, .U16, .U32, .U64, .Neg_U8, .Neg_U16, .Neg_U32, .Neg_U64: switch &dst in v { case time.Time: - i: i64 - _unmarshal_any_ptr(d, &i, hdr) or_return - dst = time.unix(i64(i), 0) + secs: i64 + _unmarshal_any_ptr(d, &secs, hdr) or_return + dst = time.unix(i64(secs), 0) return case: return _unmarshal_value(d, v, hdr) @@ -152,19 +152,23 @@ tag_time_unmarshal :: proc(_: ^Tag_Implementation, d: Decoder, _: Tag_Number, v: case: maj, add := _header_split(hdr) - if maj == .Other { - i := _decode_tiny_u8(add) or_return - - switch &dst in v { - case time.Time: - dst = time.unix(i64(i), 0) - case: - if _assign_int(v, i) { return } - } + secs: u8 + #partial switch maj { + case .Unsigned: + secs = _decode_tiny_u8(add) or_return + case .Other: + secs = u8(_decode_tiny_simple(add) or_return) + case: + return .Bad_Tag_Value } - // Only numbers and floats are allowed in this tag. - return .Bad_Tag_Value + switch &dst in v { + case time.Time: + dst = time.unix(i64(secs), 0) + return + case: + if _assign_int(v, secs) { return } + } } return _unsupported(v, hdr) diff --git a/core/math/ease/ease.odin b/core/math/ease/ease.odin index c6dd56dbc..98d549c7a 100644 --- a/core/math/ease/ease.odin +++ b/core/math/ease/ease.odin @@ -1,9 +1,8 @@ -// Easing procedures and flux easing used for animations. +// Easing procedures used for animations. package ease -import "core:math" +@require import "core:math" import "base:intrinsics" -import "core:time" @(private) PI_2 :: math.PI / 2 @@ -174,7 +173,7 @@ exponential_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_f if p == 0.0 || p == 1.0 { return p } - + if p < 0.5 { return 0.5 * math.pow(2, (20 * p) - 10) } else { @@ -307,224 +306,51 @@ Ease :: enum { } @(require_results) -ease :: proc "contextless" (type: Ease, p: $T) -> T - where intrinsics.type_is_float(T) { +ease :: proc "contextless" (type: Ease, p: $T) -> T where intrinsics.type_is_float(T) { switch type { - case .Linear: return p + case .Linear: return p - case .Quadratic_In: return quadratic_in(p) - case .Quadratic_Out: return quadratic_out(p) - case .Quadratic_In_Out: return quadratic_in_out(p) + case .Quadratic_In: return quadratic_in(p) + case .Quadratic_Out: return quadratic_out(p) + case .Quadratic_In_Out: return quadratic_in_out(p) - case .Cubic_In: return cubic_in(p) - case .Cubic_Out: return cubic_out(p) - case .Cubic_In_Out: return cubic_in_out(p) + case .Cubic_In: return cubic_in(p) + case .Cubic_Out: return cubic_out(p) + case .Cubic_In_Out: return cubic_in_out(p) - case .Quartic_In: return quartic_in(p) - case .Quartic_Out: return quartic_out(p) - case .Quartic_In_Out: return quartic_in_out(p) + case .Quartic_In: return quartic_in(p) + case .Quartic_Out: return quartic_out(p) + case .Quartic_In_Out: return quartic_in_out(p) - case .Quintic_In: return quintic_in(p) - case .Quintic_Out: return quintic_out(p) - case .Quintic_In_Out: return quintic_in_out(p) + case .Quintic_In: return quintic_in(p) + case .Quintic_Out: return quintic_out(p) + case .Quintic_In_Out: return quintic_in_out(p) - case .Sine_In: return sine_in(p) - case .Sine_Out: return sine_out(p) - case .Sine_In_Out: return sine_in_out(p) + case .Sine_In: return sine_in(p) + case .Sine_Out: return sine_out(p) + case .Sine_In_Out: return sine_in_out(p) - case .Circular_In: return circular_in(p) - case .Circular_Out: return circular_out(p) - case .Circular_In_Out: return circular_in_out(p) + case .Circular_In: return circular_in(p) + case .Circular_Out: return circular_out(p) + case .Circular_In_Out: return circular_in_out(p) - case .Exponential_In: return exponential_in(p) - case .Exponential_Out: return exponential_out(p) + case .Exponential_In: return exponential_in(p) + case .Exponential_Out: return exponential_out(p) case .Exponential_In_Out: return exponential_in_out(p) - case .Elastic_In: return elastic_in(p) - case .Elastic_Out: return elastic_out(p) - case .Elastic_In_Out: return elastic_in_out(p) + case .Elastic_In: return elastic_in(p) + case .Elastic_Out: return elastic_out(p) + case .Elastic_In_Out: return elastic_in_out(p) - case .Back_In: return back_in(p) - case .Back_Out: return back_out(p) - case .Back_In_Out: return back_in_out(p) + case .Back_In: return back_in(p) + case .Back_Out: return back_out(p) + case .Back_In_Out: return back_in_out(p) - case .Bounce_In: return bounce_in(p) - case .Bounce_Out: return bounce_out(p) - case .Bounce_In_Out: return bounce_in_out(p) + case .Bounce_In: return bounce_in(p) + case .Bounce_Out: return bounce_out(p) + case .Bounce_In_Out: return bounce_in_out(p) } // in case type was invalid return 0 } -Flux_Map :: struct($T: typeid) { - values: map[^T]Flux_Tween(T), - keys_to_be_deleted: [dynamic]^T, -} - -Flux_Tween :: struct($T: typeid) { - value: ^T, - start: T, - diff: T, - goal: T, - - delay: f64, // in seconds - duration: time.Duration, - - progress: f64, - rate: f64, - type: Ease, - - inited: bool, - - // callbacks, data can be set, will be pushed to callback - data: rawptr, // by default gets set to value input - on_start: proc(flux: ^Flux_Map(T), data: rawptr), - on_update: proc(flux: ^Flux_Map(T), data: rawptr), - on_complete: proc(flux: ^Flux_Map(T), data: rawptr), -} - -// init flux map to a float type and a wanted cap -@(require_results) -flux_init :: proc($T: typeid, value_capacity := 8) -> Flux_Map(T) where intrinsics.type_is_float(T) { - return { - values = make(map[^T]Flux_Tween(T), value_capacity), - keys_to_be_deleted = make([dynamic]^T, 0, value_capacity), - } -} - -// delete map content -flux_destroy :: proc(flux: Flux_Map($T)) where intrinsics.type_is_float(T) { - delete(flux.values) - delete(flux.keys_to_be_deleted) -} - -// clear map content, stops all animations -flux_clear :: proc(flux: ^Flux_Map($T)) where intrinsics.type_is_float(T) { - clear(&flux.values) -} - -// append / overwrite existing tween value to parameters -// rest is initialized in flux_tween_init, inside update -// return value can be used to set callbacks -@(require_results) -flux_to :: proc( - flux: ^Flux_Map($T), - value: ^T, - goal: T, - type: Ease = .Quadratic_Out, - duration: time.Duration = time.Second, - delay: f64 = 0, -) -> (tween: ^Flux_Tween(T)) where intrinsics.type_is_float(T) { - if res, ok := &flux.values[value]; ok { - tween = res - } else { - flux.values[value] = {} - tween = &flux.values[value] - } - - tween^ = { - value = value, - goal = goal, - duration = duration, - delay = delay, - type = type, - data = value, - } - - return -} - -// init internal properties -flux_tween_init :: proc(tween: ^Flux_Tween($T), duration: time.Duration) where intrinsics.type_is_float(T) { - tween.inited = true - tween.start = tween.value^ - tween.diff = tween.goal - tween.value^ - s := time.duration_seconds(duration) - tween.rate = duration > 0 ? 1.0 / s : 0 - tween.progress = duration > 0 ? 0 : 1 -} - -// update all tweens, wait for their delay if one exists -// calls callbacks in all stages, when they're filled -// deletes tween from the map after completion -flux_update :: proc(flux: ^Flux_Map($T), dt: f64) where intrinsics.type_is_float(T) { - clear(&flux.keys_to_be_deleted) - - for key, &tween in flux.values { - delay_remainder := f64(0) - - // Update delay if necessary. - if tween.delay > 0 { - tween.delay -= dt - - if tween.delay < 0 { - // We finished the delay, but in doing so consumed part of this frame's `dt` budget. - // Keep track of it so we can apply it to this tween without affecting others. - delay_remainder = tween.delay - // We're done with this delay. - tween.delay = 0 - } - } - - // We either had no delay, or the delay has been consumed. - if tween.delay <= 0 { - if !tween.inited { - flux_tween_init(&tween, tween.duration) - - if tween.on_start != nil { - tween.on_start(flux, tween.data) - } - } - - // If part of the `dt` budget was consumed this frame, then `delay_remainder` will be - // that remainder, a negative value. Adding it to `dt` applies what's left of the `dt` - // to the tween so it advances properly, instead of too much or little. - tween.progress += tween.rate * (dt + delay_remainder) - x := tween.progress >= 1 ? 1 : ease(tween.type, tween.progress) - tween.value^ = tween.start + tween.diff * T(x) - - if tween.on_update != nil { - tween.on_update(flux, tween.data) - } - - if tween.progress >= 1 { - // append keys to array that will be deleted after the loop - append(&flux.keys_to_be_deleted, key) - - if tween.on_complete != nil { - tween.on_complete(flux, tween.data) - } - } - } - } - - // loop through keys that should be deleted from the map - if len(flux.keys_to_be_deleted) != 0 { - for key in flux.keys_to_be_deleted { - delete_key(&flux.values, key) - } - } -} - -// stop a specific key inside the map -// returns true when it successfully removed the key -@(require_results) -flux_stop :: proc(flux: ^Flux_Map($T), key: ^T) -> bool where intrinsics.type_is_float(T) { - if key in flux.values { - delete_key(&flux.values, key) - return true - } - - return false -} - -// returns the amount of time left for the tween animation, if the key exists in the map -// returns 0 if the tween doesnt exist on the map -@(require_results) -flux_tween_time_left :: proc(flux: Flux_Map($T), key: ^T) -> f64 { - if tween, ok := flux.values[key]; ok { - return ((1 - tween.progress) * tween.rate) + tween.delay - } else { - return 0 - } -} diff --git a/core/math/ease/ease_inverse.odin b/core/math/ease/ease_inverse.odin new file mode 100644 index 000000000..12d6394eb --- /dev/null +++ b/core/math/ease/ease_inverse.odin @@ -0,0 +1,250 @@ +// Inverse easing procedures +// These are the mathematical inverses of the corresponding easing functions, +// allowing you to reverse the transformation: +// if y = ease_fn(x), then x = ease_fn_inverse(y) + some_imprecision +package ease + +@require import "core:math" +import "base:intrinsics" + +// Helper for handling negative bases with fractional exponents +// since math.pow(negative, fraction) returns NaN +@(private) +_signed_pow :: proc "contextless" (x, exp: $T) -> T where intrinsics.type_is_float(T) { + if x >= 0 { + return math.pow(x, exp) + } else { + return -math.pow(-x, exp) + } +} + +@(private) PI_2_INV :: 2 / math.PI + +// Inverse of quadratic_in +// x = sqrt(y) +@(require_results) +quadratic_in_inverse :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return math.sqrt(p) +} + +// Inverse of quadratic_out +// x = 1 - sqrt(1 - y) +@(require_results) +quadratic_out_inverse :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return 1 - math.sqrt(1 - p) +} + +// Inverse of quadratic_in_out +// x = sqrt(y/2) ; [0, 0.5) +// x = 1 - sqrt((1-y)/2) ; [0.5, 1] +@(require_results) +quadratic_in_out_inverse :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + if p < 0.5 { + return math.sqrt(p * 0.5) + } else { + return 1 - math.sqrt((1 - p) * 0.5) + } +} + +// Inverse of cubic_in +// x = y^(1/3) +@(require_results) +cubic_in_inverse :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return math.pow(p, 1.0/3.0) +} + +// Inverse of cubic_out +// x = (y - 1)^(1/3) + 1 +@(require_results) +cubic_out_inverse :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return _signed_pow(p - 1, 1.0/3.0) + 1 +} + +// Inverse of cubic_in_out +// x = (y/4)^(1/3) ; [0, 0.5) +// x = ((y-1)*2)^(1/3)/2 + 1 ; [0.5, 1] +@(require_results) +cubic_in_out_inverse :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + if p < 0.5 { + return math.pow(p * 0.25, 1.0/3.0) + } else { + return _signed_pow((p - 1) * 2, 1.0/3.0) * 0.5 + 1 + } +} + +// Inverse of quartic_in +// x = y^(1/4) +@(require_results) +quartic_in_inverse :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return math.pow(p, 0.25) +} + +// Inverse of quartic_out +// x = 1 - (1 - y)^(1/4) +@(require_results) +quartic_out_inverse :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return 1 - math.pow(1 - p, 0.25) +} + +// Inverse of quartic_in_out +// x = (y/8)^(1/4) ; [0, 0.5) +// x = 1 - ((1-y)/8)^(1/4) ; [0.5, 1] +@(require_results) +quartic_in_out_inverse :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + if p < 0.5 { + return math.pow(p * 0.125, 0.25) + } else { + return 1 - math.pow((1 - p) * 0.125, 0.25) + } +} + +// Inverse of quintic_in +// x = y^(1/5) +@(require_results) +quintic_in_inverse :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return math.pow(p, 0.2) +} + +// Inverse of quintic_out +// x = (y - 1)^(1/5) + 1 +@(require_results) +quintic_out_inverse :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return _signed_pow(p - 1, 0.2) + 1 +} + +// Inverse of quintic_in_out +// x = (y/16)^(1/5) ; [0, 0.5) +// x = ((y-1)*2)^(1/5)/2 + 1 ; [0.5, 1] +@(require_results) +quintic_in_out_inverse :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + if p < 0.5 { + return math.pow(0.0625 * p, 0.2) + } else { + return _signed_pow((p - 1) * 2, 0.2) * 0.5 + 1 + } +} + +// Inverse of sine_in +// x = asin(y - 1) * 2/π + 1 +@(require_results) +sine_in_inverse :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return math.asin(p - 1) * PI_2_INV + 1 +} + +// Inverse of sine_out +// x = asin(y) * 2/π +@(require_results) +sine_out_inverse :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return math.asin(p) * PI_2_INV +} + +// Inverse of sine_in_out +// x = acos(1 - 2y) / π +@(require_results) +sine_in_out_inverse :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return math.acos(1 - 2*p) / math.PI +} + +// Inverse of circular_in +// x = sqrt(2y - y²) +@(require_results) +circular_in_inverse :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return math.sqrt(2*p - p*p) +} + +// Inverse of circular_out +// x = 1 - sqrt(1 - y²) +@(require_results) +circular_out_inverse :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return 1 - math.sqrt(1 - p*p) +} + +// Inverse of circular_in_out +// x = sqrt(1 - (1-2y)²) / 2 ; [0, 0.5) +// x = 1 - sqrt(1 - (2y-1)²) / 2 ; [0.5, 1] +@(require_results) +circular_in_out_inverse :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + if p < 0.5 { + q := 1 - 2*p + return 0.5 * math.sqrt(1 - q*q) + } else { + q := 2*p - 1 + return 1 - 0.5 * math.sqrt(1 - q*q) + } +} + +// Inverse of exponential_in +// x = log₂(y) / 10 + 1 +@(require_results) +exponential_in_inverse :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return p == 0.0 ? 0.0 : 0.1 * math.log2(p) + 1 +} + +// Inverse of exponential_out +// x = -log₂(1 - y) / 10 +@(require_results) +exponential_out_inverse :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + return p == 1.0 ? 1.0 : 0.1 * -math.log2(1 - p) +} + +// Inverse of exponential_in_out +// x = (log₂(2y) + 10) / 20 ; [0, 0.5) +// x = (10 - log₂(2(1-y))) / 20 ; [0.5, 1] +@(require_results) +exponential_in_out_inverse :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) { + if p == 0.0 || p == 1.0 { + return p + } + + if p < 0.5 { + return 0.05 * (math.log2(2*p) + 10) + } else { + return 0.05 * (10 - math.log2(2*(1-p))) + } +} + +// Additional enum variant + +@(require_results) +ease_inverse :: proc "contextless" (type: Ease, p: $T) -> T where intrinsics.type_is_float(T) { + switch type { + case .Linear: return p + + case .Quadratic_In: return quadratic_in_inverse(p) + case .Quadratic_Out: return quadratic_out_inverse(p) + case .Quadratic_In_Out: return quadratic_in_out_inverse(p) + + case .Cubic_In: return cubic_in_inverse(p) + case .Cubic_Out: return cubic_out_inverse(p) + case .Cubic_In_Out: return cubic_in_out_inverse(p) + + case .Quartic_In: return quartic_in_inverse(p) + case .Quartic_Out: return quartic_out_inverse(p) + case .Quartic_In_Out: return quartic_in_out_inverse(p) + + case .Quintic_In: return quintic_in_inverse(p) + case .Quintic_Out: return quintic_out_inverse(p) + case .Quintic_In_Out: return quintic_in_out_inverse(p) + + case .Sine_In: return sine_in_inverse(p) + case .Sine_Out: return sine_out_inverse(p) + case .Sine_In_Out: return sine_in_out_inverse(p) + + case .Circular_In: return circular_in_inverse(p) + case .Circular_Out: return circular_out_inverse(p) + case .Circular_In_Out: return circular_in_out_inverse(p) + + case .Exponential_In: return exponential_in_inverse(p) + case .Exponential_Out: return exponential_out_inverse(p) + case .Exponential_In_Out: return exponential_in_out_inverse(p) + + case .Elastic_In, .Elastic_Out, .Elastic_In_Out, + .Back_In, .Back_Out, .Back_In_Out, + .Bounce_In, .Bounce_Out, .Bounce_In_Out: + // These do not have simple closed-form inverses + return 0 + } + + // In case type was invalid + return 0 +} diff --git a/core/math/ease/flux.odin b/core/math/ease/flux.odin new file mode 100644 index 000000000..137f3dff6 --- /dev/null +++ b/core/math/ease/flux.odin @@ -0,0 +1,177 @@ +// Flux easing used for animations +package ease + +import "core:time" + +Flux_Map :: struct($T: typeid) { + values: map[^T]Flux_Tween(T), + keys_to_be_deleted: [dynamic]^T, +} + +Flux_Tween :: struct($T: typeid) { + value: ^T, + start: T, + diff: T, + goal: T, + + delay: f64, // in seconds + duration: time.Duration, + + progress: f64, + rate: f64, + type: Ease, + + inited: bool, + + // callbacks, data can be set, will be pushed to callback + data: rawptr, // by default gets set to value input + on_start: proc(flux: ^Flux_Map(T), data: rawptr), + on_update: proc(flux: ^Flux_Map(T), data: rawptr), + on_complete: proc(flux: ^Flux_Map(T), data: rawptr), +} + +// init flux map to a float type and a wanted cap +@(require_results) +flux_init :: proc($T: typeid, value_capacity := 8, allocator := context.allocator, loc := #caller_location) -> Flux_Map(T) where intrinsics.type_is_float(T) { + return { + values = make(map[^T]Flux_Tween(T), value_capacity, allocator, loc), + keys_to_be_deleted = make([dynamic]^T, 0, value_capacity, allocator, loc), + } +} + +// delete map content +flux_destroy :: proc(flux: Flux_Map($T), allocator := context.allocator, loc := #caller_location) where intrinsics.type_is_float(T) { + delete(flux.values, allocator, loc) + delete(flux.keys_to_be_deleted, allocator, loc) +} + +// clear map content, stops all animations +flux_clear :: proc(flux: ^Flux_Map($T)) where intrinsics.type_is_float(T) { + clear(&flux.values) +} + +// append / overwrite existing tween value to parameters +// rest is initialized in flux_tween_init, inside update +// return value can be used to set callbacks +@(require_results) +flux_to :: proc( + flux: ^Flux_Map($T), + value: ^T, + goal: T, + type: Ease = .Quadratic_Out, + duration: time.Duration = time.Second, + delay: f64 = 0, +) -> (tween: ^Flux_Tween(T)) where intrinsics.type_is_float(T) { + if res, ok := &flux.values[value]; ok { + tween = res + } else { + flux.values[value] = {} + tween = &flux.values[value] + } + + tween^ = { + value = value, + goal = goal, + duration = duration, + delay = delay, + type = type, + data = value, + } + + return +} + +// init internal properties +flux_tween_init :: proc(tween: ^Flux_Tween($T), duration: time.Duration) where intrinsics.type_is_float(T) { + tween.inited = true + tween.start = tween.value^ + tween.diff = tween.goal - tween.value^ + s := time.duration_seconds(duration) + tween.rate = duration > 0 ? 1.0 / s : 0 + tween.progress = duration > 0 ? 0 : 1 +} + +// update all tweens, wait for their delay if one exists +// calls callbacks in all stages, when they're filled +// deletes tween from the map after completion +flux_update :: proc(flux: ^Flux_Map($T), dt: f64) where intrinsics.type_is_float(T) { + clear(&flux.keys_to_be_deleted) + + for key, &tween in flux.values { + delay_remainder := f64(0) + + // Update delay if necessary. + if tween.delay > 0 { + tween.delay -= dt + + if tween.delay < 0 { + // We finished the delay, but in doing so consumed part of this frame's `dt` budget. + // Keep track of it so we can apply it to this tween without affecting others. + delay_remainder = tween.delay + // We're done with this delay. + tween.delay = 0 + } + } + + // We either had no delay, or the delay has been consumed. + if tween.delay <= 0 { + if !tween.inited { + flux_tween_init(&tween, tween.duration) + + if tween.on_start != nil { + tween.on_start(flux, tween.data) + } + } + + // If part of the `dt` budget was consumed this frame, then `delay_remainder` will be + // that remainder, a negative value. Adding it to `dt` applies what's left of the `dt` + // to the tween so it advances properly, instead of too much or little. + tween.progress += tween.rate * (dt + delay_remainder) + x := tween.progress >= 1 ? 1 : ease(tween.type, tween.progress) + tween.value^ = tween.start + tween.diff * T(x) + + if tween.on_update != nil { + tween.on_update(flux, tween.data) + } + + if tween.progress >= 1 { + // append keys to array that will be deleted after the loop + append(&flux.keys_to_be_deleted, key) + + if tween.on_complete != nil { + tween.on_complete(flux, tween.data) + } + } + } + } + + // loop through keys that should be deleted from the map + if len(flux.keys_to_be_deleted) != 0 { + for key in flux.keys_to_be_deleted { + delete_key(&flux.values, key) + } + } +} + +// stop a specific key inside the map +// returns true when it successfully removed the key +@(require_results) +flux_stop :: proc(flux: ^Flux_Map($T), key: ^T) -> bool where intrinsics.type_is_float(T) { + if key in flux.values { + delete_key(&flux.values, key) + return true + } + + return false +} + +// returns the amount of time left for the tween animation, if the key exists in the map +// returns 0 if the tween doesn't exist on the map +@(require_results) +flux_tween_time_left :: proc(flux: Flux_Map($T), key: ^T) -> f64 { + if tween, ok := flux.values[key]; ok { + return ((1 - tween.progress) * tween.rate) + tween.delay + } else { + return 0 + } +} diff --git a/core/math/linalg/hlsl/linalg_hlsl.odin b/core/math/linalg/hlsl/linalg_hlsl.odin index 5c78bd8b1..8666f6f60 100644 --- a/core/math/linalg/hlsl/linalg_hlsl.odin +++ b/core/math/linalg/hlsl/linalg_hlsl.odin @@ -22,6 +22,7 @@ FLOAT_EPSILON :: 1e-7 DOUBLE_EPSILON :: 1e-15 // Aliases (not distict) of types +half :: f16 float :: f32 double :: f64 int :: builtin.i32 @@ -46,6 +47,30 @@ float4x1 :: matrix[4, 1]float float4x2 :: matrix[4, 2]float float4x3 :: matrix[4, 3]float +// Half Precision (half) Floating Point Types + +half2 :: [2]half +half3 :: [3]half +half4 :: [4]half + +half1x1 :: matrix[1, 1]half +half2x2 :: matrix[2, 2]half +half3x3 :: matrix[3, 3]half +half4x4 :: matrix[4, 4]half + +half1x2 :: matrix[1, 2]half +half1x3 :: matrix[1, 3]half +half1x4 :: matrix[1, 4]half +half2x1 :: matrix[2, 1]half +half2x3 :: matrix[2, 3]half +half2x4 :: matrix[2, 4]half +half3x1 :: matrix[3, 1]half +half3x2 :: matrix[3, 2]half +half3x4 :: matrix[3, 4]half +half4x1 :: matrix[4, 1]half +half4x2 :: matrix[4, 2]half +half4x3 :: matrix[4, 3]half + float2 :: [2]float float3 :: [3]float float4 :: [4]float @@ -106,8 +131,12 @@ int4x2 :: matrix[4, 2]int int4x3 :: matrix[4, 3]int cos :: proc{ + cos_half, cos_float, cos_double, + cos_half2, + cos_half3, + cos_half4, cos_float2, cos_float3, cos_float4, @@ -115,6 +144,9 @@ cos :: proc{ cos_double3, cos_double4, } +@(require_results) cos_half2 :: proc "c" (x: half2) -> half2 { return {cos(x.x), cos(x.y)} } +@(require_results) cos_half3 :: proc "c" (x: half3) -> half3 { return {cos(x.x), cos(x.y), cos(x.z)} } +@(require_results) cos_half4 :: proc "c" (x: half4) -> half4 { return {cos(x.x), cos(x.y), cos(x.z), cos(x.w)} } @(require_results) cos_float2 :: proc "c" (x: float2) -> float2 { return {cos(x.x), cos(x.y)} } @(require_results) cos_float3 :: proc "c" (x: float3) -> float3 { return {cos(x.x), cos(x.y), cos(x.z)} } @(require_results) cos_float4 :: proc "c" (x: float4) -> float4 { return {cos(x.x), cos(x.y), cos(x.z), cos(x.w)} } @@ -123,8 +155,12 @@ cos :: proc{ @(require_results) cos_double4 :: proc "c" (x: double4) -> double4 { return {cos(x.x), cos(x.y), cos(x.z), cos(x.w)} } sin :: proc{ + sin_half, sin_float, sin_double, + sin_half2, + sin_half3, + sin_half4, sin_float2, sin_float3, sin_float4, @@ -132,6 +168,9 @@ sin :: proc{ sin_double3, sin_double4, } +@(require_results) sin_half2 :: proc "c" (x: half2) -> half2 { return {sin(x.x), sin(x.y)} } +@(require_results) sin_half3 :: proc "c" (x: half3) -> half3 { return {sin(x.x), sin(x.y), sin(x.z)} } +@(require_results) sin_half4 :: proc "c" (x: half4) -> half4 { return {sin(x.x), sin(x.y), sin(x.z), sin(x.w)} } @(require_results) sin_float2 :: proc "c" (x: float2) -> float2 { return {sin(x.x), sin(x.y)} } @(require_results) sin_float3 :: proc "c" (x: float3) -> float3 { return {sin(x.x), sin(x.y), sin(x.z)} } @(require_results) sin_float4 :: proc "c" (x: float4) -> float4 { return {sin(x.x), sin(x.y), sin(x.z), sin(x.w)} } @@ -140,8 +179,12 @@ sin :: proc{ @(require_results) sin_double4 :: proc "c" (x: double4) -> double4 { return {sin(x.x), sin(x.y), sin(x.z), sin(x.w)} } tan :: proc{ + tan_half, tan_float, tan_double, + tan_half2, + tan_half3, + tan_half4, tan_float2, tan_float3, tan_float4, @@ -149,6 +192,9 @@ tan :: proc{ tan_double3, tan_double4, } +@(require_results) tan_half2 :: proc "c" (x: half2) -> half2 { return {tan(x.x), tan(x.y)} } +@(require_results) tan_half3 :: proc "c" (x: half3) -> half3 { return {tan(x.x), tan(x.y), tan(x.z)} } +@(require_results) tan_half4 :: proc "c" (x: half4) -> half4 { return {tan(x.x), tan(x.y), tan(x.z), tan(x.w)} } @(require_results) tan_float2 :: proc "c" (x: float2) -> float2 { return {tan(x.x), tan(x.y)} } @(require_results) tan_float3 :: proc "c" (x: float3) -> float3 { return {tan(x.x), tan(x.y), tan(x.z)} } @(require_results) tan_float4 :: proc "c" (x: float4) -> float4 { return {tan(x.x), tan(x.y), tan(x.z), tan(x.w)} } @@ -157,8 +203,12 @@ tan :: proc{ @(require_results) tan_double4 :: proc "c" (x: double4) -> double4 { return {tan(x.x), tan(x.y), tan(x.z), tan(x.w)} } acos :: proc{ + acos_half, acos_float, acos_double, + acos_half2, + acos_half3, + acos_half4, acos_float2, acos_float3, acos_float4, @@ -166,6 +216,9 @@ acos :: proc{ acos_double3, acos_double4, } +@(require_results) acos_half2 :: proc "c" (x: half2) -> half2 { return {acos(x.x), acos(x.y)} } +@(require_results) acos_half3 :: proc "c" (x: half3) -> half3 { return {acos(x.x), acos(x.y), acos(x.z)} } +@(require_results) acos_half4 :: proc "c" (x: half4) -> half4 { return {acos(x.x), acos(x.y), acos(x.z), acos(x.w)} } @(require_results) acos_float2 :: proc "c" (x: float2) -> float2 { return {acos(x.x), acos(x.y)} } @(require_results) acos_float3 :: proc "c" (x: float3) -> float3 { return {acos(x.x), acos(x.y), acos(x.z)} } @(require_results) acos_float4 :: proc "c" (x: float4) -> float4 { return {acos(x.x), acos(x.y), acos(x.z), acos(x.w)} } @@ -174,8 +227,12 @@ acos :: proc{ @(require_results) acos_double4 :: proc "c" (x: double4) -> double4 { return {acos(x.x), acos(x.y), acos(x.z), acos(x.w)} } asin :: proc{ + asin_half, asin_float, asin_double, + asin_half2, + asin_half3, + asin_half4, asin_float2, asin_float3, asin_float4, @@ -183,6 +240,9 @@ asin :: proc{ asin_double3, asin_double4, } +@(require_results) asin_half2 :: proc "c" (x: half2) -> half2 { return {asin(x.x), asin(x.y)} } +@(require_results) asin_half3 :: proc "c" (x: half3) -> half3 { return {asin(x.x), asin(x.y), asin(x.z)} } +@(require_results) asin_half4 :: proc "c" (x: half4) -> half4 { return {asin(x.x), asin(x.y), asin(x.z), asin(x.w)} } @(require_results) asin_float2 :: proc "c" (x: float2) -> float2 { return {asin(x.x), asin(x.y)} } @(require_results) asin_float3 :: proc "c" (x: float3) -> float3 { return {asin(x.x), asin(x.y), asin(x.z)} } @(require_results) asin_float4 :: proc "c" (x: float4) -> float4 { return {asin(x.x), asin(x.y), asin(x.z), asin(x.w)} } @@ -191,8 +251,12 @@ asin :: proc{ @(require_results) asin_double4 :: proc "c" (x: double4) -> double4 { return {asin(x.x), asin(x.y), asin(x.z), asin(x.w)} } atan :: proc{ + atan_half, atan_float, atan_double, + atan_half2, + atan_half3, + atan_half4, atan_float2, atan_float3, atan_float4, @@ -208,6 +272,9 @@ atan :: proc{ atan2_double3, atan2_double4, } +@(require_results) atan_half2 :: proc "c" (x: half2) -> half2 { return {atan(x.x), atan(x.y)} } +@(require_results) atan_half3 :: proc "c" (x: half3) -> half3 { return {atan(x.x), atan(x.y), atan(x.z)} } +@(require_results) atan_half4 :: proc "c" (x: half4) -> half4 { return {atan(x.x), atan(x.y), atan(x.z), atan(x.w)} } @(require_results) atan_float2 :: proc "c" (x: float2) -> float2 { return {atan(x.x), atan(x.y)} } @(require_results) atan_float3 :: proc "c" (x: float3) -> float3 { return {atan(x.x), atan(x.y), atan(x.z)} } @(require_results) atan_float4 :: proc "c" (x: float4) -> float4 { return {atan(x.x), atan(x.y), atan(x.z), atan(x.w)} } @@ -216,8 +283,12 @@ atan :: proc{ @(require_results) atan_double4 :: proc "c" (x: double4) -> double4 { return {atan(x.x), atan(x.y), atan(x.z), atan(x.w)} } atan2 :: proc{ + atan2_half, atan2_float, atan2_double, + atan2_half2, + atan2_half3, + atan2_half4, atan2_float2, atan2_float3, atan2_float4, @@ -225,6 +296,9 @@ atan2 :: proc{ atan2_double3, atan2_double4, } +@(require_results) atan2_half2 :: proc "c" (y, x: half2) -> half2 { return {atan2(y.x, x.x), atan2(y.y, x.y)} } +@(require_results) atan2_half3 :: proc "c" (y, x: half3) -> half3 { return {atan2(y.x, x.x), atan2(y.y, x.y), atan2(y.z, x.z)} } +@(require_results) atan2_half4 :: proc "c" (y, x: half4) -> half4 { return {atan2(y.x, x.x), atan2(y.y, x.y), atan2(y.z, x.z), atan2(y.w, x.w)} } @(require_results) atan2_float2 :: proc "c" (y, x: float2) -> float2 { return {atan2(y.x, x.x), atan2(y.y, x.y)} } @(require_results) atan2_float3 :: proc "c" (y, x: float3) -> float3 { return {atan2(y.x, x.x), atan2(y.y, x.y), atan2(y.z, x.z)} } @(require_results) atan2_float4 :: proc "c" (y, x: float4) -> float4 { return {atan2(y.x, x.x), atan2(y.y, x.y), atan2(y.z, x.z), atan2(y.w, x.w)} } @@ -235,8 +309,12 @@ atan2 :: proc{ cosh :: proc{ + cosh_half, cosh_float, cosh_double, + cosh_half2, + cosh_half3, + cosh_half4, cosh_float2, cosh_float3, cosh_float4, @@ -244,6 +322,9 @@ cosh :: proc{ cosh_double3, cosh_double4, } +@(require_results) cosh_half2 :: proc "c" (x: half2) -> half2 { return {cosh(x.x), cosh(x.y)} } +@(require_results) cosh_half3 :: proc "c" (x: half3) -> half3 { return {cosh(x.x), cosh(x.y), cosh(x.z)} } +@(require_results) cosh_half4 :: proc "c" (x: half4) -> half4 { return {cosh(x.x), cosh(x.y), cosh(x.z), cosh(x.w)} } @(require_results) cosh_float2 :: proc "c" (x: float2) -> float2 { return {cosh(x.x), cosh(x.y)} } @(require_results) cosh_float3 :: proc "c" (x: float3) -> float3 { return {cosh(x.x), cosh(x.y), cosh(x.z)} } @(require_results) cosh_float4 :: proc "c" (x: float4) -> float4 { return {cosh(x.x), cosh(x.y), cosh(x.z), cosh(x.w)} } @@ -253,8 +334,12 @@ cosh :: proc{ sinh :: proc{ + sinh_half, sinh_float, sinh_double, + sinh_half2, + sinh_half3, + sinh_half4, sinh_float2, sinh_float3, sinh_float4, @@ -262,6 +347,9 @@ sinh :: proc{ sinh_double3, sinh_double4, } +@(require_results) sinh_half2 :: proc "c" (x: half2) -> half2 { return {sinh(x.x), sinh(x.y)} } +@(require_results) sinh_half3 :: proc "c" (x: half3) -> half3 { return {sinh(x.x), sinh(x.y), sinh(x.z)} } +@(require_results) sinh_half4 :: proc "c" (x: half4) -> half4 { return {sinh(x.x), sinh(x.y), sinh(x.z), sinh(x.w)} } @(require_results) sinh_float2 :: proc "c" (x: float2) -> float2 { return {sinh(x.x), sinh(x.y)} } @(require_results) sinh_float3 :: proc "c" (x: float3) -> float3 { return {sinh(x.x), sinh(x.y), sinh(x.z)} } @(require_results) sinh_float4 :: proc "c" (x: float4) -> float4 { return {sinh(x.x), sinh(x.y), sinh(x.z), sinh(x.w)} } @@ -270,8 +358,12 @@ sinh :: proc{ @(require_results) sinh_double4 :: proc "c" (x: double4) -> double4 { return {sinh(x.x), sinh(x.y), sinh(x.z), sinh(x.w)} } tanh :: proc{ + tanh_half, tanh_float, tanh_double, + tanh_half2, + tanh_half3, + tanh_half4, tanh_float2, tanh_float3, tanh_float4, @@ -279,6 +371,9 @@ tanh :: proc{ tanh_double3, tanh_double4, } +@(require_results) tanh_half2 :: proc "c" (x: half2) -> half2 { return {tanh(x.x), tanh(x.y)} } +@(require_results) tanh_half3 :: proc "c" (x: half3) -> half3 { return {tanh(x.x), tanh(x.y), tanh(x.z)} } +@(require_results) tanh_half4 :: proc "c" (x: half4) -> half4 { return {tanh(x.x), tanh(x.y), tanh(x.z), tanh(x.w)} } @(require_results) tanh_float2 :: proc "c" (x: float2) -> float2 { return {tanh(x.x), tanh(x.y)} } @(require_results) tanh_float3 :: proc "c" (x: float3) -> float3 { return {tanh(x.x), tanh(x.y), tanh(x.z)} } @(require_results) tanh_float4 :: proc "c" (x: float4) -> float4 { return {tanh(x.x), tanh(x.y), tanh(x.z), tanh(x.w)} } @@ -287,8 +382,12 @@ tanh :: proc{ @(require_results) tanh_double4 :: proc "c" (x: double4) -> double4 { return {tanh(x.x), tanh(x.y), tanh(x.z), tanh(x.w)} } acosh :: proc{ + acosh_half, acosh_float, acosh_double, + acosh_half2, + acosh_half3, + acosh_half4, acosh_float2, acosh_float3, acosh_float4, @@ -296,6 +395,9 @@ acosh :: proc{ acosh_double3, acosh_double4, } +@(require_results) acosh_half2 :: proc "c" (x: half2) -> half2 { return {acosh(x.x), acosh(x.y)} } +@(require_results) acosh_half3 :: proc "c" (x: half3) -> half3 { return {acosh(x.x), acosh(x.y), acosh(x.z)} } +@(require_results) acosh_half4 :: proc "c" (x: half4) -> half4 { return {acosh(x.x), acosh(x.y), acosh(x.z), acosh(x.w)} } @(require_results) acosh_float2 :: proc "c" (x: float2) -> float2 { return {acosh(x.x), acosh(x.y)} } @(require_results) acosh_float3 :: proc "c" (x: float3) -> float3 { return {acosh(x.x), acosh(x.y), acosh(x.z)} } @(require_results) acosh_float4 :: proc "c" (x: float4) -> float4 { return {acosh(x.x), acosh(x.y), acosh(x.z), acosh(x.w)} } @@ -304,8 +406,12 @@ acosh :: proc{ @(require_results) acosh_double4 :: proc "c" (x: double4) -> double4 { return {acosh(x.x), acosh(x.y), acosh(x.z), acosh(x.w)} } asinh :: proc{ + asinh_half, asinh_float, asinh_double, + asinh_half2, + asinh_half3, + asinh_half4, asinh_float2, asinh_float3, asinh_float4, @@ -313,6 +419,9 @@ asinh :: proc{ asinh_double3, asinh_double4, } +@(require_results) asinh_half2 :: proc "c" (x: half2) -> half2 { return {asinh(x.x), asinh(x.y)} } +@(require_results) asinh_half3 :: proc "c" (x: half3) -> half3 { return {asinh(x.x), asinh(x.y), asinh(x.z)} } +@(require_results) asinh_half4 :: proc "c" (x: half4) -> half4 { return {asinh(x.x), asinh(x.y), asinh(x.z), asinh(x.w)} } @(require_results) asinh_float2 :: proc "c" (x: float2) -> float2 { return {asinh(x.x), asinh(x.y)} } @(require_results) asinh_float3 :: proc "c" (x: float3) -> float3 { return {asinh(x.x), asinh(x.y), asinh(x.z)} } @(require_results) asinh_float4 :: proc "c" (x: float4) -> float4 { return {asinh(x.x), asinh(x.y), asinh(x.z), asinh(x.w)} } @@ -321,8 +430,12 @@ asinh :: proc{ @(require_results) asinh_double4 :: proc "c" (x: double4) -> double4 { return {asinh(x.x), asinh(x.y), asinh(x.z), asinh(x.w)} } atanh :: proc{ + atanh_half, atanh_float, atanh_double, + atanh_half2, + atanh_half3, + atanh_half4, atanh_float2, atanh_float3, atanh_float4, @@ -330,6 +443,9 @@ atanh :: proc{ atanh_double3, atanh_double4, } +@(require_results) atanh_half2 :: proc "c" (x: half2) -> half2 { return {atanh(x.x), atanh(x.y)} } +@(require_results) atanh_half3 :: proc "c" (x: half3) -> half3 { return {atanh(x.x), atanh(x.y), atanh(x.z)} } +@(require_results) atanh_half4 :: proc "c" (x: half4) -> half4 { return {atanh(x.x), atanh(x.y), atanh(x.z), atanh(x.w)} } @(require_results) atanh_float2 :: proc "c" (x: float2) -> float2 { return {atanh(x.x), atanh(x.y)} } @(require_results) atanh_float3 :: proc "c" (x: float3) -> float3 { return {atanh(x.x), atanh(x.y), atanh(x.z)} } @(require_results) atanh_float4 :: proc "c" (x: float4) -> float4 { return {atanh(x.x), atanh(x.y), atanh(x.z), atanh(x.w)} } @@ -338,8 +454,12 @@ atanh :: proc{ @(require_results) atanh_double4 :: proc "c" (x: double4) -> double4 { return {atanh(x.x), atanh(x.y), atanh(x.z), atanh(x.w)} } sqrt :: proc{ + sqrt_half, sqrt_float, sqrt_double, + sqrt_half2, + sqrt_half3, + sqrt_half4, sqrt_float2, sqrt_float3, sqrt_float4, @@ -347,6 +467,9 @@ sqrt :: proc{ sqrt_double3, sqrt_double4, } +@(require_results) sqrt_half2 :: proc "c" (x: half2) -> half2 { return {sqrt(x.x), sqrt(x.y)} } +@(require_results) sqrt_half3 :: proc "c" (x: half3) -> half3 { return {sqrt(x.x), sqrt(x.y), sqrt(x.z)} } +@(require_results) sqrt_half4 :: proc "c" (x: half4) -> half4 { return {sqrt(x.x), sqrt(x.y), sqrt(x.z), sqrt(x.w)} } @(require_results) sqrt_float2 :: proc "c" (x: float2) -> float2 { return {sqrt(x.x), sqrt(x.y)} } @(require_results) sqrt_float3 :: proc "c" (x: float3) -> float3 { return {sqrt(x.x), sqrt(x.y), sqrt(x.z)} } @(require_results) sqrt_float4 :: proc "c" (x: float4) -> float4 { return {sqrt(x.x), sqrt(x.y), sqrt(x.z), sqrt(x.w)} } @@ -355,8 +478,12 @@ sqrt :: proc{ @(require_results) sqrt_double4 :: proc "c" (x: double4) -> double4 { return {sqrt(x.x), sqrt(x.y), sqrt(x.z), sqrt(x.w)} } rsqrt :: proc{ + rsqrt_half, rsqrt_float, rsqrt_double, + rsqrt_half2, + rsqrt_half3, + rsqrt_half4, rsqrt_float2, rsqrt_float3, rsqrt_float4, @@ -364,6 +491,9 @@ rsqrt :: proc{ rsqrt_double3, rsqrt_double4, } +@(require_results) rsqrt_half2 :: proc "c" (x: half2) -> half2 { return {rsqrt(x.x), rsqrt(x.y)} } +@(require_results) rsqrt_half3 :: proc "c" (x: half3) -> half3 { return {rsqrt(x.x), rsqrt(x.y), rsqrt(x.z)} } +@(require_results) rsqrt_half4 :: proc "c" (x: half4) -> half4 { return {rsqrt(x.x), rsqrt(x.y), rsqrt(x.z), rsqrt(x.w)} } @(require_results) rsqrt_float2 :: proc "c" (x: float2) -> float2 { return {rsqrt(x.x), rsqrt(x.y)} } @(require_results) rsqrt_float3 :: proc "c" (x: float3) -> float3 { return {rsqrt(x.x), rsqrt(x.y), rsqrt(x.z)} } @(require_results) rsqrt_float4 :: proc "c" (x: float4) -> float4 { return {rsqrt(x.x), rsqrt(x.y), rsqrt(x.z), rsqrt(x.w)} } @@ -372,8 +502,12 @@ rsqrt :: proc{ @(require_results) rsqrt_double4 :: proc "c" (x: double4) -> double4 { return {rsqrt(x.x), rsqrt(x.y), rsqrt(x.z), rsqrt(x.w)} } rcp :: proc{ + rcp_half, rcp_float, rcp_double, + rcp_half2, + rcp_half3, + rcp_half4, rcp_float2, rcp_float3, rcp_float4, @@ -381,6 +515,9 @@ rcp :: proc{ rcp_double3, rcp_double4, } +@(require_results) rcp_half2 :: proc "c" (x: half2) -> half2 { return {rcp(x.x), rcp(x.y)} } +@(require_results) rcp_half3 :: proc "c" (x: half3) -> half3 { return {rcp(x.x), rcp(x.y), rcp(x.z)} } +@(require_results) rcp_half4 :: proc "c" (x: half4) -> half4 { return {rcp(x.x), rcp(x.y), rcp(x.z), rcp(x.w)} } @(require_results) rcp_float2 :: proc "c" (x: float2) -> float2 { return {rcp(x.x), rcp(x.y)} } @(require_results) rcp_float3 :: proc "c" (x: float3) -> float3 { return {rcp(x.x), rcp(x.y), rcp(x.z)} } @(require_results) rcp_float4 :: proc "c" (x: float4) -> float4 { return {rcp(x.x), rcp(x.y), rcp(x.z), rcp(x.w)} } @@ -390,8 +527,12 @@ rcp :: proc{ pow :: proc{ + pow_half, pow_float, pow_double, + pow_half2, + pow_half3, + pow_half4, pow_float2, pow_float3, pow_float4, @@ -399,6 +540,9 @@ pow :: proc{ pow_double3, pow_double4, } +@(require_results) pow_half2 :: proc "c" (x, y: half2) -> half2 { return {pow(x.x, y.x), pow(x.y, y.y)} } +@(require_results) pow_half3 :: proc "c" (x, y: half3) -> half3 { return {pow(x.x, y.x), pow(x.y, y.y), pow(x.z, y.z)} } +@(require_results) pow_half4 :: proc "c" (x, y: half4) -> half4 { return {pow(x.x, y.x), pow(x.y, y.y), pow(x.z, y.z), pow(x.w, y.w)} } @(require_results) pow_float2 :: proc "c" (x, y: float2) -> float2 { return {pow(x.x, y.x), pow(x.y, y.y)} } @(require_results) pow_float3 :: proc "c" (x, y: float3) -> float3 { return {pow(x.x, y.x), pow(x.y, y.y), pow(x.z, y.z)} } @(require_results) pow_float4 :: proc "c" (x, y: float4) -> float4 { return {pow(x.x, y.x), pow(x.y, y.y), pow(x.z, y.z), pow(x.w, y.w)} } @@ -409,8 +553,12 @@ pow :: proc{ exp :: proc{ + exp_half, exp_float, exp_double, + exp_half2, + exp_half3, + exp_half4, exp_float2, exp_float3, exp_float4, @@ -418,6 +566,9 @@ exp :: proc{ exp_double3, exp_double4, } +@(require_results) exp_half2 :: proc "c" (x: half2) -> half2 { return {exp(x.x), exp(x.y)} } +@(require_results) exp_half3 :: proc "c" (x: half3) -> half3 { return {exp(x.x), exp(x.y), exp(x.z)} } +@(require_results) exp_half4 :: proc "c" (x: half4) -> half4 { return {exp(x.x), exp(x.y), exp(x.z), exp(x.w)} } @(require_results) exp_float2 :: proc "c" (x: float2) -> float2 { return {exp(x.x), exp(x.y)} } @(require_results) exp_float3 :: proc "c" (x: float3) -> float3 { return {exp(x.x), exp(x.y), exp(x.z)} } @(require_results) exp_float4 :: proc "c" (x: float4) -> float4 { return {exp(x.x), exp(x.y), exp(x.z), exp(x.w)} } @@ -428,8 +579,12 @@ exp :: proc{ log :: proc{ + log_half, log_float, log_double, + log_half2, + log_half3, + log_half4, log_float2, log_float3, log_float4, @@ -437,6 +592,9 @@ log :: proc{ log_double3, log_double4, } +@(require_results) log_half2 :: proc "c" (x: half2) -> half2 { return {log(x.x), log(x.y)} } +@(require_results) log_half3 :: proc "c" (x: half3) -> half3 { return {log(x.x), log(x.y), log(x.z)} } +@(require_results) log_half4 :: proc "c" (x: half4) -> half4 { return {log(x.x), log(x.y), log(x.z), log(x.w)} } @(require_results) log_float2 :: proc "c" (x: float2) -> float2 { return {log(x.x), log(x.y)} } @(require_results) log_float3 :: proc "c" (x: float3) -> float3 { return {log(x.x), log(x.y), log(x.z)} } @(require_results) log_float4 :: proc "c" (x: float4) -> float4 { return {log(x.x), log(x.y), log(x.z), log(x.w)} } @@ -446,8 +604,12 @@ log :: proc{ log2 :: proc{ + log2_half, log2_float, log2_double, + log2_half2, + log2_half3, + log2_half4, log2_float2, log2_float3, log2_float4, @@ -455,6 +617,9 @@ log2 :: proc{ log2_double3, log2_double4, } +@(require_results) log2_half2 :: proc "c" (x: half2) -> half2 { return {log2(x.x), log2(x.y)} } +@(require_results) log2_half3 :: proc "c" (x: half3) -> half3 { return {log2(x.x), log2(x.y), log2(x.z)} } +@(require_results) log2_half4 :: proc "c" (x: half4) -> half4 { return {log2(x.x), log2(x.y), log2(x.z), log2(x.w)} } @(require_results) log2_float2 :: proc "c" (x: float2) -> float2 { return {log2(x.x), log2(x.y)} } @(require_results) log2_float3 :: proc "c" (x: float3) -> float3 { return {log2(x.x), log2(x.y), log2(x.z)} } @(require_results) log2_float4 :: proc "c" (x: float4) -> float4 { return {log2(x.x), log2(x.y), log2(x.z), log2(x.w)} } @@ -465,8 +630,12 @@ log2 :: proc{ log10 :: proc{ + log10_half, log10_float, log10_double, + log10_half2, + log10_half3, + log10_half4, log10_float2, log10_float3, log10_float4, @@ -474,6 +643,9 @@ log10 :: proc{ log10_double3, log10_double4, } +@(require_results) log10_half2 :: proc "c" (x: half2) -> half2 { return {log10(x.x), log10(x.y)} } +@(require_results) log10_half3 :: proc "c" (x: half3) -> half3 { return {log10(x.x), log10(x.y), log10(x.z)} } +@(require_results) log10_half4 :: proc "c" (x: half4) -> half4 { return {log10(x.x), log10(x.y), log10(x.z), log10(x.w)} } @(require_results) log10_float2 :: proc "c" (x: float2) -> float2 { return {log10(x.x), log10(x.y)} } @(require_results) log10_float3 :: proc "c" (x: float3) -> float3 { return {log10(x.x), log10(x.y), log10(x.z)} } @(require_results) log10_float4 :: proc "c" (x: float4) -> float4 { return {log10(x.x), log10(x.y), log10(x.z), log10(x.w)} } @@ -485,8 +657,12 @@ log10 :: proc{ exp2 :: proc{ + exp2_half, exp2_float, exp2_double, + exp2_half2, + exp2_half3, + exp2_half4, exp2_float2, exp2_float3, exp2_float4, @@ -494,6 +670,9 @@ exp2 :: proc{ exp2_double3, exp2_double4, } +@(require_results) exp2_half2 :: proc "c" (x: half2) -> half2 { return {exp2(x.x), exp2(x.y)} } +@(require_results) exp2_half3 :: proc "c" (x: half3) -> half3 { return {exp2(x.x), exp2(x.y), exp2(x.z)} } +@(require_results) exp2_half4 :: proc "c" (x: half4) -> half4 { return {exp2(x.x), exp2(x.y), exp2(x.z), exp2(x.w)} } @(require_results) exp2_float2 :: proc "c" (x: float2) -> float2 { return {exp2(x.x), exp2(x.y)} } @(require_results) exp2_float3 :: proc "c" (x: float3) -> float3 { return {exp2(x.x), exp2(x.y), exp2(x.z)} } @(require_results) exp2_float4 :: proc "c" (x: float4) -> float4 { return {exp2(x.x), exp2(x.y), exp2(x.z), exp2(x.w)} } @@ -503,10 +682,14 @@ exp2 :: proc{ sign :: proc{ + sign_half, sign_int, sign_uint, sign_float, sign_double, + sign_half2, + sign_half3, + sign_half4, sign_float2, sign_float3, sign_float4, @@ -522,6 +705,9 @@ sign :: proc{ } @(require_results) sign_int :: proc "c" (x: int) -> int { return -1 if x < 0 else +1 if x > 0 else 0 } @(require_results) sign_uint :: proc "c" (x: uint) -> uint { return +1 if x > 0 else 0 } +@(require_results) sign_half2 :: proc "c" (x: half2) -> half2 { return {sign(x.x), sign(x.y)} } +@(require_results) sign_half3 :: proc "c" (x: half3) -> half3 { return {sign(x.x), sign(x.y), sign(x.z)} } +@(require_results) sign_half4 :: proc "c" (x: half4) -> half4 { return {sign(x.x), sign(x.y), sign(x.z), sign(x.w)} } @(require_results) sign_float2 :: proc "c" (x: float2) -> float2 { return {sign(x.x), sign(x.y)} } @(require_results) sign_float3 :: proc "c" (x: float3) -> float3 { return {sign(x.x), sign(x.y), sign(x.z)} } @(require_results) sign_float4 :: proc "c" (x: float4) -> float4 { return {sign(x.x), sign(x.y), sign(x.z), sign(x.w)} } @@ -536,8 +722,12 @@ sign :: proc{ @(require_results) sign_uint4 :: proc "c" (x: uint4) -> uint4 { return {sign(x.x), sign(x.y), sign(x.z), sign(x.w)} } floor :: proc{ + floor_half, floor_float, floor_double, + floor_half2, + floor_half3, + floor_half4, floor_float2, floor_float3, floor_float4, @@ -545,6 +735,9 @@ floor :: proc{ floor_double3, floor_double4, } +@(require_results) floor_half2 :: proc "c" (x: half2) -> half2 { return {floor(x.x), floor(x.y)} } +@(require_results) floor_half3 :: proc "c" (x: half3) -> half3 { return {floor(x.x), floor(x.y), floor(x.z)} } +@(require_results) floor_half4 :: proc "c" (x: half4) -> half4 { return {floor(x.x), floor(x.y), floor(x.z), floor(x.w)} } @(require_results) floor_float2 :: proc "c" (x: float2) -> float2 { return {floor(x.x), floor(x.y)} } @(require_results) floor_float3 :: proc "c" (x: float3) -> float3 { return {floor(x.x), floor(x.y), floor(x.z)} } @(require_results) floor_float4 :: proc "c" (x: float4) -> float4 { return {floor(x.x), floor(x.y), floor(x.z), floor(x.w)} } @@ -553,8 +746,12 @@ floor :: proc{ @(require_results) floor_double4 :: proc "c" (x: double4) -> double4 { return {floor(x.x), floor(x.y), floor(x.z), floor(x.w)} } round :: proc{ + round_half, round_float, round_double, + round_half2, + round_half3, + round_half4, round_float2, round_float3, round_float4, @@ -562,6 +759,9 @@ round :: proc{ round_double3, round_double4, } +@(require_results) round_half2 :: proc "c" (x: half2) -> half2 { return {round(x.x), round(x.y)} } +@(require_results) round_half3 :: proc "c" (x: half3) -> half3 { return {round(x.x), round(x.y), round(x.z)} } +@(require_results) round_half4 :: proc "c" (x: half4) -> half4 { return {round(x.x), round(x.y), round(x.z), round(x.w)} } @(require_results) round_float2 :: proc "c" (x: float2) -> float2 { return {round(x.x), round(x.y)} } @(require_results) round_float3 :: proc "c" (x: float3) -> float3 { return {round(x.x), round(x.y), round(x.z)} } @(require_results) round_float4 :: proc "c" (x: float4) -> float4 { return {round(x.x), round(x.y), round(x.z), round(x.w)} } @@ -571,8 +771,12 @@ round :: proc{ ceil :: proc{ + ceil_half, ceil_float, ceil_double, + ceil_half2, + ceil_half3, + ceil_half4, ceil_float2, ceil_float3, ceil_float4, @@ -580,6 +784,9 @@ ceil :: proc{ ceil_double3, ceil_double4, } +@(require_results) ceil_half2 :: proc "c" (x: half2) -> half2 { return {ceil(x.x), ceil(x.y)} } +@(require_results) ceil_half3 :: proc "c" (x: half3) -> half3 { return {ceil(x.x), ceil(x.y), ceil(x.z)} } +@(require_results) ceil_half4 :: proc "c" (x: half4) -> half4 { return {ceil(x.x), ceil(x.y), ceil(x.z), ceil(x.w)} } @(require_results) ceil_float2 :: proc "c" (x: float2) -> float2 { return {ceil(x.x), ceil(x.y)} } @(require_results) ceil_float3 :: proc "c" (x: float3) -> float3 { return {ceil(x.x), ceil(x.y), ceil(x.z)} } @(require_results) ceil_float4 :: proc "c" (x: float4) -> float4 { return {ceil(x.x), ceil(x.y), ceil(x.z), ceil(x.w)} } @@ -588,6 +795,10 @@ ceil :: proc{ @(require_results) ceil_double4 :: proc "c" (x: double4) -> double4 { return {ceil(x.x), ceil(x.y), ceil(x.z), ceil(x.w)} } +@(require_results) isfinite_half :: proc "c" (x: half) -> bool { return !isinf_half(x) } +@(require_results) isfinite_half2 :: proc "c" (x: half2) -> bool2 { return {isfinite_half(x.x), isfinite_half(x.y)} } +@(require_results) isfinite_half3 :: proc "c" (x: half3) -> bool3 { return {isfinite_half(x.x), isfinite_half(x.y), isfinite_half(x.z)} } +@(require_results) isfinite_half4 :: proc "c" (x: half4) -> bool4 { return {isfinite_half(x.x), isfinite_half(x.y), isfinite_half(x.z), isfinite_half(x.w)} } @(require_results) isfinite_float :: proc "c" (x: float) -> bool { return !isinf_float(x) } @(require_results) isfinite_float2 :: proc "c" (x: float2) -> bool2 { return {isfinite_float(x.x), isfinite_float(x.y)} } @(require_results) isfinite_float3 :: proc "c" (x: float3) -> bool3 { return {isfinite_float(x.x), isfinite_float(x.y), isfinite_float(x.z)} } @@ -599,6 +810,10 @@ ceil :: proc{ // isfinite is the opposite of isinf and returns true if the number is neither positive-infinite or negative-infinite isfinite :: proc{ + isfinite_half, + isfinite_half2, + isfinite_half3, + isfinite_half4, isfinite_float, isfinite_float2, isfinite_float3, @@ -610,6 +825,10 @@ isfinite :: proc{ } +@(require_results) isinf_half :: proc "c" (x: half) -> bool { return x * 0.5 == x } +@(require_results) isinf_half2 :: proc "c" (x: half2) -> bool2 { return {isinf_half(x.x), isinf_half(x.y)} } +@(require_results) isinf_half3 :: proc "c" (x: half3) -> bool3 { return {isinf_half(x.x), isinf_half(x.y), isinf_half(x.z)} } +@(require_results) isinf_half4 :: proc "c" (x: half4) -> bool4 { return {isinf_half(x.x), isinf_half(x.y), isinf_half(x.z), isinf_half(x.w)} } @(require_results) isinf_float :: proc "c" (x: float) -> bool { return x * 0.5 == x } @(require_results) isinf_float2 :: proc "c" (x: float2) -> bool2 { return {isinf_float(x.x), isinf_float(x.y)} } @(require_results) isinf_float3 :: proc "c" (x: float3) -> bool3 { return {isinf_float(x.x), isinf_float(x.y), isinf_float(x.z)} } @@ -621,6 +840,10 @@ isfinite :: proc{ // isinf is the opposite of isfinite and returns true if the number is either positive-infinite or negative-infinite isinf :: proc{ + isinf_half, + isinf_half2, + isinf_half3, + isinf_half4, isinf_float, isinf_float2, isinf_float3, @@ -632,6 +855,9 @@ isinf :: proc{ } +@(require_results) isnan_half2 :: proc "c" (x: half2) -> bool2 { return {isnan_half(x.x), isnan_half(x.y)} } +@(require_results) isnan_half3 :: proc "c" (x: half3) -> bool3 { return {isnan_half(x.x), isnan_half(x.y), isnan_half(x.z)} } +@(require_results) isnan_half4 :: proc "c" (x: half4) -> bool4 { return {isnan_half(x.x), isnan_half(x.y), isnan_half(x.z), isnan_half(x.w)} } @(require_results) isnan_float2 :: proc "c" (x: float2) -> bool2 { return {isnan_float(x.x), isnan_float(x.y)} } @(require_results) isnan_float3 :: proc "c" (x: float3) -> bool3 { return {isnan_float(x.x), isnan_float(x.y), isnan_float(x.z)} } @(require_results) isnan_float4 :: proc "c" (x: float4) -> bool4 { return {isnan_float(x.x), isnan_float(x.y), isnan_float(x.z), isnan_float(x.w)} } @@ -641,6 +867,10 @@ isinf :: proc{ // isnan returns true if the input value is the special case of Not-A-Number isnan :: proc{ + isnan_half, + isnan_half2, + isnan_half3, + isnan_half4, isnan_float, isnan_float2, isnan_float3, @@ -652,8 +882,12 @@ isnan :: proc{ } fmod :: proc{ + fmod_half, fmod_float, fmod_double, + fmod_half2, + fmod_half3, + fmod_half4, fmod_float2, fmod_float3, fmod_float4, @@ -661,6 +895,9 @@ fmod :: proc{ fmod_double3, fmod_double4, } +@(require_results) fmod_half2 :: proc "c" (x, y: half2) -> half2 { return {fmod(x.x, y.x), fmod(x.y, y.y)} } +@(require_results) fmod_half3 :: proc "c" (x, y: half3) -> half3 { return {fmod(x.x, y.x), fmod(x.y, y.y), fmod(x.z, y.z)} } +@(require_results) fmod_half4 :: proc "c" (x, y: half4) -> half4 { return {fmod(x.x, y.x), fmod(x.y, y.y), fmod(x.z, y.z), fmod(x.w, y.w)} } @(require_results) fmod_float2 :: proc "c" (x, y: float2) -> float2 { return {fmod(x.x, y.x), fmod(x.y, y.y)} } @(require_results) fmod_float3 :: proc "c" (x, y: float3) -> float3 { return {fmod(x.x, y.x), fmod(x.y, y.y), fmod(x.z, y.z)} } @(require_results) fmod_float4 :: proc "c" (x, y: float4) -> float4 { return {fmod(x.x, y.x), fmod(x.y, y.y), fmod(x.z, y.z), fmod(x.w, y.w)} } @@ -670,8 +907,12 @@ fmod :: proc{ frac :: proc{ + frac_half, frac_float, frac_double, + frac_half2, + frac_half3, + frac_half4, frac_float2, frac_float3, frac_float4, @@ -679,6 +920,9 @@ frac :: proc{ frac_double3, frac_double4, } +@(require_results) frac_half2 :: proc "c" (x: half2) -> half2 { return {frac(x.x), frac(x.y)} } +@(require_results) frac_half3 :: proc "c" (x: half3) -> half3 { return {frac(x.x), frac(x.y), frac(x.z)} } +@(require_results) frac_half4 :: proc "c" (x: half4) -> half4 { return {frac(x.x), frac(x.y), frac(x.z), frac(x.w)} } @(require_results) frac_float2 :: proc "c" (x: float2) -> float2 { return {frac(x.x), frac(x.y)} } @(require_results) frac_float3 :: proc "c" (x: float3) -> float3 { return {frac(x.x), frac(x.y), frac(x.z)} } @(require_results) frac_float4 :: proc "c" (x: float4) -> float4 { return {frac(x.x), frac(x.y), frac(x.z), frac(x.w)} } @@ -689,8 +933,12 @@ frac :: proc{ radians :: proc{ + radians_half, radians_float, radians_double, + radians_half2, + radians_half3, + radians_half4, radians_float2, radians_float3, radians_float4, @@ -698,8 +946,12 @@ radians :: proc{ radians_double3, radians_double4, } +@(require_results) radians_half :: proc "c" (degrees: half) -> half { return degrees * TAU / 360.0 } @(require_results) radians_float :: proc "c" (degrees: float) -> float { return degrees * TAU / 360.0 } @(require_results) radians_double :: proc "c" (degrees: double) -> double { return degrees * TAU / 360.0 } +@(require_results) radians_half2 :: proc "c" (degrees: half2) -> half2 { return degrees * TAU / 360.0 } +@(require_results) radians_half3 :: proc "c" (degrees: half3) -> half3 { return degrees * TAU / 360.0 } +@(require_results) radians_half4 :: proc "c" (degrees: half4) -> half4 { return degrees * TAU / 360.0 } @(require_results) radians_float2 :: proc "c" (degrees: float2) -> float2 { return degrees * TAU / 360.0 } @(require_results) radians_float3 :: proc "c" (degrees: float3) -> float3 { return degrees * TAU / 360.0 } @(require_results) radians_float4 :: proc "c" (degrees: float4) -> float4 { return degrees * TAU / 360.0 } @@ -709,8 +961,12 @@ radians :: proc{ degrees :: proc{ + degrees_half, degrees_float, degrees_double, + degrees_half2, + degrees_half3, + degrees_half4, degrees_float2, degrees_float3, degrees_float4, @@ -718,8 +974,12 @@ degrees :: proc{ degrees_double3, degrees_double4, } +@(require_results) degrees_half :: proc "c" (radians: half) -> half { return radians * 360.0 / TAU } @(require_results) degrees_float :: proc "c" (radians: float) -> float { return radians * 360.0 / TAU } @(require_results) degrees_double :: proc "c" (radians: double) -> double { return radians * 360.0 / TAU } +@(require_results) degrees_half2 :: proc "c" (radians: half2) -> half2 { return radians * 360.0 / TAU } +@(require_results) degrees_half3 :: proc "c" (radians: half3) -> half3 { return radians * 360.0 / TAU } +@(require_results) degrees_half4 :: proc "c" (radians: half4) -> half4 { return radians * 360.0 / TAU } @(require_results) degrees_float2 :: proc "c" (radians: float2) -> float2 { return radians * 360.0 / TAU } @(require_results) degrees_float3 :: proc "c" (radians: float3) -> float3 { return radians * 360.0 / TAU } @(require_results) degrees_float4 :: proc "c" (radians: float4) -> float4 { return radians * 360.0 / TAU } @@ -728,10 +988,14 @@ degrees :: proc{ @(require_results) degrees_double4 :: proc "c" (radians: double4) -> double4 { return radians * 360.0 / TAU } min :: proc{ + min_half, min_int, min_uint, min_float, min_double, + min_half2, + min_half3, + min_half4, min_float2, min_float3, min_float4, @@ -747,8 +1011,12 @@ min :: proc{ } @(require_results) min_int :: proc "c" (x, y: int) -> int { return builtin.min(x, y) } @(require_results) min_uint :: proc "c" (x, y: uint) -> uint { return builtin.min(x, y) } +@(require_results) min_half :: proc "c" (x, y: half) -> half { return builtin.min(x, y) } @(require_results) min_float :: proc "c" (x, y: float) -> float { return builtin.min(x, y) } @(require_results) min_double :: proc "c" (x, y: double) -> double { return builtin.min(x, y) } +@(require_results) min_half2 :: proc "c" (x, y: half2) -> half2 { return {min(x.x, y.x), min(x.y, y.y)} } +@(require_results) min_half3 :: proc "c" (x, y: half3) -> half3 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z)} } +@(require_results) min_half4 :: proc "c" (x, y: half4) -> half4 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z), min(x.w, y.w)} } @(require_results) min_float2 :: proc "c" (x, y: float2) -> float2 { return {min(x.x, y.x), min(x.y, y.y)} } @(require_results) min_float3 :: proc "c" (x, y: float3) -> float3 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z)} } @(require_results) min_float4 :: proc "c" (x, y: float4) -> float4 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z), min(x.w, y.w)} } @@ -765,9 +1033,13 @@ min :: proc{ max :: proc{ max_int, - max_uint, + max_uint, + max_half, max_float, max_double, + max_half2, + max_half3, + max_half4, max_float2, max_float3, max_float4, @@ -784,7 +1056,11 @@ max :: proc{ @(require_results) max_int :: proc "c" (x, y: int) -> int { return builtin.max(x, y) } @(require_results) max_uint :: proc "c" (x, y: uint) -> uint { return builtin.max(x, y) } @(require_results) max_float :: proc "c" (x, y: float) -> float { return builtin.max(x, y) } +@(require_results) max_half :: proc "c" (x, y: half) -> half { return builtin.max(x, y) } @(require_results) max_double :: proc "c" (x, y: double) -> double { return builtin.max(x, y) } +@(require_results) max_half2 :: proc "c" (x, y: half2) -> half2 { return {max(x.x, y.x), max(x.y, y.y)} } +@(require_results) max_half3 :: proc "c" (x, y: half3) -> half3 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z)} } +@(require_results) max_half4 :: proc "c" (x, y: half4) -> half4 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z), max(x.w, y.w)} } @(require_results) max_float2 :: proc "c" (x, y: float2) -> float2 { return {max(x.x, y.x), max(x.y, y.y)} } @(require_results) max_float3 :: proc "c" (x, y: float3) -> float3 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z)} } @(require_results) max_float4 :: proc "c" (x, y: float4) -> float4 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z), max(x.w, y.w)} } @@ -803,8 +1079,12 @@ max :: proc{ clamp :: proc{ clamp_int, clamp_uint, - clamp_float, + clamp_half, + clamp_float, clamp_double, + clamp_half2, + clamp_half3, + clamp_half4, clamp_float2, clamp_float3, clamp_float4, @@ -820,8 +1100,12 @@ clamp :: proc{ } @(require_results) clamp_int :: proc "c" (x, y, z: int) -> int { return builtin.clamp(x, y, z) } @(require_results) clamp_uint :: proc "c" (x, y, z: uint) -> uint { return builtin.clamp(x, y, z) } +@(require_results) clamp_half :: proc "c" (x, y, z: half) -> half { return builtin.clamp(x, y, z) } @(require_results) clamp_float :: proc "c" (x, y, z: float) -> float { return builtin.clamp(x, y, z) } @(require_results) clamp_double :: proc "c" (x, y, z: double) -> double { return builtin.clamp(x, y, z) } +@(require_results) clamp_half2 :: proc "c" (x, y, z: half2) -> half2 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y)} } +@(require_results) clamp_half3 :: proc "c" (x, y, z: half3) -> half3 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z)} } +@(require_results) clamp_half4 :: proc "c" (x, y, z: half4) -> half4 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z), clamp(x.w, y.w, z.w)} } @(require_results) clamp_float2 :: proc "c" (x, y, z: float2) -> float2 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y)} } @(require_results) clamp_float3 :: proc "c" (x, y, z: float3) -> float3 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z)} } @(require_results) clamp_float4 :: proc "c" (x, y, z: float4) -> float4 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z), clamp(x.w, y.w, z.w)} } @@ -838,8 +1122,12 @@ clamp :: proc{ saturate :: proc{ saturate_int, saturate_uint, + saturate_half, saturate_float, saturate_double, + saturate_half2, + saturate_half3, + saturate_half4, saturate_float2, saturate_float3, saturate_float4, @@ -855,8 +1143,12 @@ saturate :: proc{ } @(require_results) saturate_int :: proc "c" (v: int) -> int { return builtin.clamp(v, 0, 1) } @(require_results) saturate_uint :: proc "c" (v: uint) -> uint { return builtin.clamp(v, 0, 1) } +@(require_results) saturate_half :: proc "c" (v: half) -> half { return builtin.clamp(v, 0, 1) } @(require_results) saturate_float :: proc "c" (v: float) -> float { return builtin.clamp(v, 0, 1) } @(require_results) saturate_double :: proc "c" (v: double) -> double { return builtin.clamp(v, 0, 1) } +@(require_results) saturate_half2 :: proc "c" (v: half2) -> half2 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1)} } +@(require_results) saturate_half3 :: proc "c" (v: half3) -> half3 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1)} } +@(require_results) saturate_half4 :: proc "c" (v: half4) -> half4 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1), builtin.clamp(v.w, 0, 1)} } @(require_results) saturate_float2 :: proc "c" (v: float2) -> float2 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1)} } @(require_results) saturate_float3 :: proc "c" (v: float3) -> float3 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1)} } @(require_results) saturate_float4 :: proc "c" (v: float4) -> float4 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1), builtin.clamp(v.w, 0, 1)} } @@ -872,8 +1164,12 @@ saturate :: proc{ lerp :: proc{ + lerp_half, lerp_float, lerp_double, + lerp_half2, + lerp_half3, + lerp_half4, lerp_float2, lerp_float3, lerp_float4, @@ -881,8 +1177,12 @@ lerp :: proc{ lerp_double3, lerp_double4, } +@(require_results) lerp_half :: proc "c" (x, y, t: half) -> half { return x*(1-t) + y*t } @(require_results) lerp_float :: proc "c" (x, y, t: float) -> float { return x*(1-t) + y*t } @(require_results) lerp_double :: proc "c" (x, y, t: double) -> double { return x*(1-t) + y*t } +@(require_results) lerp_half2 :: proc "c" (x, y, t: half2) -> half2 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, t.y)} } +@(require_results) lerp_half3 :: proc "c" (x, y, t: half3) -> half3 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, t.y), lerp(x.z, y.z, t.z)} } +@(require_results) lerp_half4 :: proc "c" (x, y, t: half4) -> half4 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, y.y), lerp(x.z, y.z, t.z), lerp(x.w, y.w, t.w)} } @(require_results) lerp_float2 :: proc "c" (x, y, t: float2) -> float2 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, t.y)} } @(require_results) lerp_float3 :: proc "c" (x, y, t: float3) -> float3 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, t.y), lerp(x.z, y.z, t.z)} } @(require_results) lerp_float4 :: proc "c" (x, y, t: float4) -> float4 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, y.y), lerp(x.z, y.z, t.z), lerp(x.w, y.w, t.w)} } @@ -892,8 +1192,12 @@ lerp :: proc{ step :: proc{ + step_half, step_float, step_double, + step_half2, + step_half3, + step_half4, step_float2, step_float3, step_float4, @@ -901,8 +1205,12 @@ step :: proc{ step_double3, step_double4, } +@(require_results) step_half :: proc "c" (edge, x: half) -> half { return 0 if x < edge else 1 } @(require_results) step_float :: proc "c" (edge, x: float) -> float { return 0 if x < edge else 1 } @(require_results) step_double :: proc "c" (edge, x: double) -> double { return 0 if x < edge else 1 } +@(require_results) step_half2 :: proc "c" (edge, x: half2) -> half2 { return {step(edge.x, x.x), step(edge.y, x.y)} } +@(require_results) step_half3 :: proc "c" (edge, x: half3) -> half3 { return {step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z)} } +@(require_results) step_half4 :: proc "c" (edge, x: half4) -> half4 { return {step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z), step(edge.w, x.w)} } @(require_results) step_float2 :: proc "c" (edge, x: float2) -> float2 { return {step(edge.x, x.x), step(edge.y, x.y)} } @(require_results) step_float3 :: proc "c" (edge, x: float3) -> float3 { return {step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z)} } @(require_results) step_float4 :: proc "c" (edge, x: float4) -> float4 { return {step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z), step(edge.w, x.w)} } @@ -911,8 +1219,12 @@ step :: proc{ @(require_results) step_double4 :: proc "c" (edge, x: double4) -> double4 { return {step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z), step(edge.w, x.w)} } smoothstep :: proc{ + smoothstep_half, smoothstep_float, smoothstep_double, + smoothstep_half2, + smoothstep_half3, + smoothstep_half4, smoothstep_float2, smoothstep_float3, smoothstep_float4, @@ -920,6 +1232,10 @@ smoothstep :: proc{ smoothstep_double3, smoothstep_double4, } +@(require_results) smoothstep_half :: proc "c" (edge0, edge1, x: half) -> half { + y := clamp(((x-edge0) / (edge1 - edge0)), 0, 1) + return y * y * (3 - 2*y) +} @(require_results) smoothstep_float :: proc "c" (edge0, edge1, x: float) -> float { y := clamp(((x-edge0) / (edge1 - edge0)), 0, 1) return y * y * (3 - 2*y) @@ -928,6 +1244,9 @@ smoothstep :: proc{ y := clamp(((x-edge0) / (edge1 - edge0)), 0, 1) return y * y * (3 - 2*y) } +@(require_results) smoothstep_half2 :: proc "c" (edge0, edge1, x: half2) -> half2 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y)} } +@(require_results) smoothstep_half3 :: proc "c" (edge0, edge1, x: half3) -> half3 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y), smoothstep(edge0.z, edge1.z, x.z)} } +@(require_results) smoothstep_half4 :: proc "c" (edge0, edge1, x: half4) -> half4 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y), smoothstep(edge0.z, edge1.z, x.z), smoothstep(edge0.w, edge1.w, x.w)} } @(require_results) smoothstep_float2 :: proc "c" (edge0, edge1, x: float2) -> float2 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y)} } @(require_results) smoothstep_float3 :: proc "c" (edge0, edge1, x: float3) -> float3 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y), smoothstep(edge0.z, edge1.z, x.z)} } @(require_results) smoothstep_float4 :: proc "c" (edge0, edge1, x: float4) -> float4 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y), smoothstep(edge0.z, edge1.z, x.z), smoothstep(edge0.w, edge1.w, x.w)} } @@ -939,8 +1258,12 @@ smoothstep :: proc{ abs :: proc{ abs_int, abs_uint, + abs_half, abs_float, abs_double, + abs_half2, + abs_half3, + abs_half4, abs_float2, abs_float3, abs_float4, @@ -956,8 +1279,12 @@ abs :: proc{ } @(require_results) abs_int :: proc "c" (x: int) -> int { return builtin.abs(x) } @(require_results) abs_uint :: proc "c" (x: uint) -> uint { return x } +@(require_results) abs_half :: proc "c" (x: half) -> half { return builtin.abs(x) } @(require_results) abs_float :: proc "c" (x: float) -> float { return builtin.abs(x) } @(require_results) abs_double :: proc "c" (x: double) -> double { return builtin.abs(x) } +@(require_results) abs_half2 :: proc "c" (x: half2) -> half2 { return {abs(x.x), abs(x.y)} } +@(require_results) abs_half3 :: proc "c" (x: half3) -> half3 { return {abs(x.x), abs(x.y), abs(x.z)} } +@(require_results) abs_half4 :: proc "c" (x: half4) -> half4 { return {abs(x.x), abs(x.y), abs(x.z), abs(x.w)} } @(require_results) abs_float2 :: proc "c" (x: float2) -> float2 { return {abs(x.x), abs(x.y)} } @(require_results) abs_float3 :: proc "c" (x: float3) -> float3 { return {abs(x.x), abs(x.y), abs(x.z)} } @(require_results) abs_float4 :: proc "c" (x: float4) -> float4 { return {abs(x.x), abs(x.y), abs(x.z), abs(x.w)} } @@ -974,8 +1301,12 @@ abs :: proc{ dot :: proc{ dot_int, dot_uint, + dot_half, dot_float, dot_double, + dot_half2, + dot_half3, + dot_half4, dot_float2, dot_float3, dot_float4, @@ -991,8 +1322,12 @@ dot :: proc{ } @(require_results) dot_int :: proc "c" (a, b: int) -> int { return a*b } @(require_results) dot_uint :: proc "c" (a, b: uint) -> uint { return a*b } +@(require_results) dot_half :: proc "c" (a, b: half) -> half { return a*b } @(require_results) dot_float :: proc "c" (a, b: float) -> float { return a*b } @(require_results) dot_double :: proc "c" (a, b: double) -> double { return a*b } +@(require_results) dot_half2 :: proc "c" (a, b: half2) -> half { return a.x*b.x + a.y*b.y } +@(require_results) dot_half3 :: proc "c" (a, b: half3) -> half { return a.x*b.x + a.y*b.y + a.z*b.z } +@(require_results) dot_half4 :: proc "c" (a, b: half4) -> half { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w } @(require_results) dot_float2 :: proc "c" (a, b: float2) -> float { return a.x*b.x + a.y*b.y } @(require_results) dot_float3 :: proc "c" (a, b: float3) -> float { return a.x*b.x + a.y*b.y + a.z*b.z } @(require_results) dot_float4 :: proc "c" (a, b: float4) -> float { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w } @@ -1007,8 +1342,12 @@ dot :: proc{ @(require_results) dot_uint4 :: proc "c" (a, b: uint4) -> uint { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w } length :: proc{ + length_half, length_float, length_double, + length_half2, + length_half3, + length_half4, length_float2, length_float3, length_float4, @@ -1016,8 +1355,12 @@ length :: proc{ length_double3, length_double4, } +@(require_results) length_half :: proc "c" (x: half) -> half { return builtin.abs(x) } @(require_results) length_float :: proc "c" (x: float) -> float { return builtin.abs(x) } @(require_results) length_double :: proc "c" (x: double) -> double { return builtin.abs(x) } +@(require_results) length_half2 :: proc "c" (x: half2) -> half { return sqrt(x.x*x.x + x.y*x.y) } +@(require_results) length_half3 :: proc "c" (x: half3) -> half { return sqrt(x.x*x.x + x.y*x.y + x.z*x.z) } +@(require_results) length_half4 :: proc "c" (x: half4) -> half { return sqrt(x.x*x.x + x.y*x.y + x.z*x.z + x.w*x.w) } @(require_results) length_float2 :: proc "c" (x: float2) -> float { return sqrt(x.x*x.x + x.y*x.y) } @(require_results) length_float3 :: proc "c" (x: float3) -> float { return sqrt(x.x*x.x + x.y*x.y + x.z*x.z) } @(require_results) length_float4 :: proc "c" (x: float4) -> float { return sqrt(x.x*x.x + x.y*x.y + x.z*x.z + x.w*x.w) } @@ -1027,8 +1370,12 @@ length :: proc{ distance :: proc{ + distance_half, distance_float, distance_double, + distance_half2, + distance_half3, + distance_half4, distance_float2, distance_float3, distance_float4, @@ -1036,8 +1383,12 @@ distance :: proc{ distance_double3, distance_double4, } +@(require_results) distance_half :: proc "c" (x, y: half) -> half { return length(y-x) } @(require_results) distance_float :: proc "c" (x, y: float) -> float { return length(y-x) } @(require_results) distance_double :: proc "c" (x, y: double) -> double { return length(y-x) } +@(require_results) distance_half2 :: proc "c" (x, y: half2) -> half { return length(y-x) } +@(require_results) distance_half3 :: proc "c" (x, y: half3) -> half { return length(y-x) } +@(require_results) distance_half4 :: proc "c" (x, y: half4) -> half { return length(y-x) } @(require_results) distance_float2 :: proc "c" (x, y: float2) -> float { return length(y-x) } @(require_results) distance_float3 :: proc "c" (x, y: float3) -> float { return length(y-x) } @(require_results) distance_float4 :: proc "c" (x, y: float4) -> float { return length(y-x) } @@ -1047,11 +1398,18 @@ distance :: proc{ cross :: proc{ + cross_half3, cross_float3, cross_double3, cross_int3, } +@(require_results) cross_half3 :: proc "c" (a, b: half3) -> (c: half3) { + c.x = a.y*b.z - b.y*a.z + c.y = a.z*b.x - b.z*a.x + c.z = a.x*b.y - b.x*a.y + return +} @(require_results) cross_float3 :: proc "c" (a, b: float3) -> (c: float3) { c.x = a.y*b.z - b.y*a.z c.y = a.z*b.x - b.z*a.x @@ -1072,8 +1430,12 @@ cross :: proc{ } normalize :: proc{ + normalize_half, normalize_float, normalize_double, + normalize_half2, + normalize_half3, + normalize_half4, normalize_float2, normalize_float3, normalize_float4, @@ -1081,8 +1443,12 @@ normalize :: proc{ normalize_double3, normalize_double4, } +@(require_results) normalize_half :: proc "c" (x: half) -> half { return 1.0 } @(require_results) normalize_float :: proc "c" (x: float) -> float { return 1.0 } @(require_results) normalize_double :: proc "c" (x: double) -> double { return 1.0 } +@(require_results) normalize_half2 :: proc "c" (x: half2) -> half2 { return x / length(x) } +@(require_results) normalize_half3 :: proc "c" (x: half3) -> half3 { return x / length(x) } +@(require_results) normalize_half4 :: proc "c" (x: half4) -> half4 { return x / length(x) } @(require_results) normalize_float2 :: proc "c" (x: float2) -> float2 { return x / length(x) } @(require_results) normalize_float3 :: proc "c" (x: float3) -> float3 { return x / length(x) } @(require_results) normalize_float4 :: proc "c" (x: float4) -> float4 { return x / length(x) } @@ -1092,8 +1458,12 @@ normalize :: proc{ faceforward :: proc{ + faceforward_half, faceforward_float, faceforward_double, + faceforward_half2, + faceforward_half3, + faceforward_half4, faceforward_float2, faceforward_float3, faceforward_float4, @@ -1101,8 +1471,12 @@ faceforward :: proc{ faceforward_double3, faceforward_double4, } +@(require_results) faceforward_half :: proc "c" (N, I, Nref: half) -> half { return N if dot(I, Nref) < 0 else -N } @(require_results) faceforward_float :: proc "c" (N, I, Nref: float) -> float { return N if dot(I, Nref) < 0 else -N } @(require_results) faceforward_double :: proc "c" (N, I, Nref: double) -> double { return N if dot(I, Nref) < 0 else -N } +@(require_results) faceforward_half2 :: proc "c" (N, I, Nref: half2) -> half2 { return N if dot(I, Nref) < 0 else -N } +@(require_results) faceforward_half3 :: proc "c" (N, I, Nref: half3) -> half3 { return N if dot(I, Nref) < 0 else -N } +@(require_results) faceforward_half4 :: proc "c" (N, I, Nref: half4) -> half4 { return N if dot(I, Nref) < 0 else -N } @(require_results) faceforward_float2 :: proc "c" (N, I, Nref: float2) -> float2 { return N if dot(I, Nref) < 0 else -N } @(require_results) faceforward_float3 :: proc "c" (N, I, Nref: float3) -> float3 { return N if dot(I, Nref) < 0 else -N } @(require_results) faceforward_float4 :: proc "c" (N, I, Nref: float4) -> float4 { return N if dot(I, Nref) < 0 else -N } @@ -1112,8 +1486,12 @@ faceforward :: proc{ reflect :: proc{ + reflect_half, reflect_float, reflect_double, + reflect_half2, + reflect_half3, + reflect_half4, reflect_float2, reflect_float3, reflect_float4, @@ -1121,8 +1499,12 @@ reflect :: proc{ reflect_double3, reflect_double4, } +@(require_results) reflect_half :: proc "c" (I, N: half) -> half { return I - 2*N*dot(N, I) } @(require_results) reflect_float :: proc "c" (I, N: float) -> float { return I - 2*N*dot(N, I) } @(require_results) reflect_double :: proc "c" (I, N: double) -> double { return I - 2*N*dot(N, I) } +@(require_results) reflect_half2 :: proc "c" (I, N: half2) -> half2 { return I - 2*N*dot(N, I) } +@(require_results) reflect_half3 :: proc "c" (I, N: half3) -> half3 { return I - 2*N*dot(N, I) } +@(require_results) reflect_half4 :: proc "c" (I, N: half4) -> half4 { return I - 2*N*dot(N, I) } @(require_results) reflect_float2 :: proc "c" (I, N: float2) -> float2 { return I - 2*N*dot(N, I) } @(require_results) reflect_float3 :: proc "c" (I, N: float3) -> float3 { return I - 2*N*dot(N, I) } @(require_results) reflect_float4 :: proc "c" (I, N: float4) -> float4 { return I - 2*N*dot(N, I) } @@ -1134,8 +1516,12 @@ reflect :: proc{ refract :: proc{ + refract_half, refract_float, refract_double, + refract_half2, + refract_half3, + refract_half4, refract_float2, refract_float3, refract_float4, @@ -1144,6 +1530,13 @@ refract :: proc{ refract_double4, } @(require_results) +refract_half :: proc "c" (i, n, eta: half) -> half { + cosi := dot(-i, n) + cost2 := 1 - eta*eta*(1 - cosi*cosi) + t := eta*i + ((eta*cosi - sqrt(abs(cost2))) * n) + return t * half(int(cost2 > 0)) +} +@(require_results) refract_float :: proc "c" (i, n, eta: float) -> float { cosi := dot(-i, n) cost2 := 1 - eta*eta*(1 - cosi*cosi) @@ -1158,6 +1551,27 @@ refract_double :: proc "c" (i, n, eta: double) -> double { return t * double(int(cost2 > 0)) } @(require_results) +refract_half2 :: proc "c" (i, n, eta: half2) -> half2 { + cosi := dot(-i, n) + cost2 := 1 - eta*eta*(1 - cosi*cosi) + t := eta*i + ((eta*cosi - sqrt(abs(cost2))) * n) + return t * half2{half(int(cost2.x > 0)), half(int(cost2.y > 0))} +} +@(require_results) +refract_half3 :: proc "c" (i, n, eta: half3) -> half3 { + cosi := dot(-i, n) + cost2 := 1 - eta*eta*(1 - cosi*cosi) + t := eta*i + ((eta*cosi - sqrt(abs(cost2))) * n) + return t * half3{half(int(cost2.x > 0)), half(int(cost2.y > 0)), half(int(cost2.z > 0))} +} +@(require_results) +refract_half4 :: proc "c" (i, n, eta: half4) -> half4 { + cosi := dot(-i, n) + cost2 := 1 - eta*eta*(1 - cosi*cosi) + t := eta*i + ((eta*cosi - sqrt(abs(cost2))) * n) + return t * half4{half(int(cost2.x > 0)), half(int(cost2.y > 0)), half(int(cost2.z > 0)), half(int(cost2.w > 0))} +} +@(require_results) refract_float2 :: proc "c" (i, n, eta: float2) -> float2 { cosi := dot(-i, n) cost2 := 1 - eta*eta*(1 - cosi*cosi) @@ -1201,19 +1615,23 @@ refract_double4 :: proc "c" (i, n, eta: double4) -> double4 { } scalarTripleProduct :: proc{ + scalarTripleProduct_half3, scalarTripleProduct_float3, scalarTripleProduct_double3, scalarTripleProduct_int3, } +@(require_results) scalarTripleProduct_half3 :: proc "c" (a, b, c: half3) -> half { return dot(a, cross(b, c)) } @(require_results) scalarTripleProduct_float3 :: proc "c" (a, b, c: float3) -> float { return dot(a, cross(b, c)) } @(require_results) scalarTripleProduct_double3 :: proc "c" (a, b, c: double3) -> double { return dot(a, cross(b, c)) } @(require_results) scalarTripleProduct_int3 :: proc "c" (a, b, c: int3) -> int { return dot(a, cross(b, c)) } vectorTripleProduct :: proc { + vectorTripleProduct_half3, vectorTripleProduct_float3, vectorTripleProduct_double3, vectorTripleProduct_int3, } +@(require_results) vectorTripleProduct_half3 :: proc "c" (a, b, c: half3) -> half3 { return cross(a, cross(b, c)) } @(require_results) vectorTripleProduct_float3 :: proc "c" (a, b, c: float3) -> float3 { return cross(a, cross(b, c)) } @(require_results) vectorTripleProduct_double3 :: proc "c" (a, b, c: double3) -> double3 { return cross(a, cross(b, c)) } @(require_results) vectorTripleProduct_int3 :: proc "c" (a, b, c: int3) -> int3 { return cross(a, cross(b, c)) } @@ -1222,35 +1640,43 @@ vectorTripleProduct :: proc { // Vector Relational Procedures lessThan :: proc{ + lessThan_half, lessThan_float, lessThan_double, lessThan_int, lessThan_uint, + lessThan_half2, lessThan_float2, lessThan_double2, lessThan_int2, lessThan_uint2, + lessThan_half3, lessThan_float3, lessThan_double3, lessThan_int3, lessThan_uint3, + lessThan_half4, lessThan_float4, lessThan_double4, lessThan_int4, lessThan_uint4, } +@(require_results) lessThan_half :: proc "c" (a, b: half) -> bool { return a < b } @(require_results) lessThan_float :: proc "c" (a, b: float) -> bool { return a < b } @(require_results) lessThan_double :: proc "c" (a, b: double) -> bool { return a < b } @(require_results) lessThan_int :: proc "c" (a, b: int) -> bool { return a < b } @(require_results) lessThan_uint :: proc "c" (a, b: uint) -> bool { return a < b } +@(require_results) lessThan_half2 :: proc "c" (a, b: half2) -> bool2 { return {a.x < b.x, a.y < b.y} } @(require_results) lessThan_float2 :: proc "c" (a, b: float2) -> bool2 { return {a.x < b.x, a.y < b.y} } @(require_results) lessThan_double2 :: proc "c" (a, b: double2) -> bool2 { return {a.x < b.x, a.y < b.y} } @(require_results) lessThan_int2 :: proc "c" (a, b: int2) -> bool2 { return {a.x < b.x, a.y < b.y} } @(require_results) lessThan_uint2 :: proc "c" (a, b: uint2) -> bool2 { return {a.x < b.x, a.y < b.y} } +@(require_results) lessThan_half3 :: proc "c" (a, b: half3) -> bool3 { return {a.x < b.x, a.y < b.y, a.z < b.z} } @(require_results) lessThan_float3 :: proc "c" (a, b: float3) -> bool3 { return {a.x < b.x, a.y < b.y, a.z < b.z} } @(require_results) lessThan_double3 :: proc "c" (a, b: double3) -> bool3 { return {a.x < b.x, a.y < b.y, a.z < b.z} } @(require_results) lessThan_int3 :: proc "c" (a, b: int3) -> bool3 { return {a.x < b.x, a.y < b.y, a.z < b.z} } @(require_results) lessThan_uint3 :: proc "c" (a, b: uint3) -> bool3 { return {a.x < b.x, a.y < b.y, a.z < b.z} } +@(require_results) lessThan_half4 :: proc "c" (a, b: half4) -> bool4 { return {a.x < b.x, a.y < b.y, a.z < b.z, a.w < b.w} } @(require_results) lessThan_float4 :: proc "c" (a, b: float4) -> bool4 { return {a.x < b.x, a.y < b.y, a.z < b.z, a.w < b.w} } @(require_results) lessThan_double4 :: proc "c" (a, b: double4) -> bool4 { return {a.x < b.x, a.y < b.y, a.z < b.z, a.w < b.w} } @(require_results) lessThan_int4 :: proc "c" (a, b: int4) -> bool4 { return {a.x < b.x, a.y < b.y, a.z < b.z, a.w < b.w} } @@ -1258,35 +1684,43 @@ lessThan :: proc{ lessThanEqual :: proc{ + lessThanEqual_half, lessThanEqual_float, lessThanEqual_double, lessThanEqual_int, lessThanEqual_uint, + lessThanEqual_half2, lessThanEqual_float2, lessThanEqual_double2, lessThanEqual_int2, lessThanEqual_uint2, + lessThanEqual_half3, lessThanEqual_float3, lessThanEqual_double3, lessThanEqual_int3, lessThanEqual_uint3, + lessThanEqual_half4, lessThanEqual_float4, lessThanEqual_double4, lessThanEqual_int4, lessThanEqual_uint4, } +@(require_results) lessThanEqual_half :: proc "c" (a, b: half) -> bool { return a <= b } @(require_results) lessThanEqual_float :: proc "c" (a, b: float) -> bool { return a <= b } @(require_results) lessThanEqual_double :: proc "c" (a, b: double) -> bool { return a <= b } @(require_results) lessThanEqual_int :: proc "c" (a, b: int) -> bool { return a <= b } @(require_results) lessThanEqual_uint :: proc "c" (a, b: uint) -> bool { return a <= b } +@(require_results) lessThanEqual_half2 :: proc "c" (a, b: half2) -> bool2 { return {a.x <= b.x, a.y <= b.y} } @(require_results) lessThanEqual_float2 :: proc "c" (a, b: float2) -> bool2 { return {a.x <= b.x, a.y <= b.y} } @(require_results) lessThanEqual_double2 :: proc "c" (a, b: double2) -> bool2 { return {a.x <= b.x, a.y <= b.y} } @(require_results) lessThanEqual_int2 :: proc "c" (a, b: int2) -> bool2 { return {a.x <= b.x, a.y <= b.y} } @(require_results) lessThanEqual_uint2 :: proc "c" (a, b: uint2) -> bool2 { return {a.x <= b.x, a.y <= b.y} } +@(require_results) lessThanEqual_half3 :: proc "c" (a, b: half3) -> bool3 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z} } @(require_results) lessThanEqual_float3 :: proc "c" (a, b: float3) -> bool3 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z} } @(require_results) lessThanEqual_double3 :: proc "c" (a, b: double3) -> bool3 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z} } @(require_results) lessThanEqual_int3 :: proc "c" (a, b: int3) -> bool3 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z} } @(require_results) lessThanEqual_uint3 :: proc "c" (a, b: uint3) -> bool3 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z} } +@(require_results) lessThanEqual_half4 :: proc "c" (a, b: half4) -> bool4 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z, a.w <= b.w} } @(require_results) lessThanEqual_float4 :: proc "c" (a, b: float4) -> bool4 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z, a.w <= b.w} } @(require_results) lessThanEqual_double4 :: proc "c" (a, b: double4) -> bool4 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z, a.w <= b.w} } @(require_results) lessThanEqual_int4 :: proc "c" (a, b: int4) -> bool4 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z, a.w <= b.w} } @@ -1294,35 +1728,43 @@ lessThanEqual :: proc{ greaterThan :: proc{ + greaterThan_half, greaterThan_float, greaterThan_double, greaterThan_int, greaterThan_uint, + greaterThan_half2, greaterThan_float2, greaterThan_double2, greaterThan_int2, greaterThan_uint2, + greaterThan_half3, greaterThan_float3, greaterThan_double3, greaterThan_int3, greaterThan_uint3, + greaterThan_half4, greaterThan_float4, greaterThan_double4, greaterThan_int4, greaterThan_uint4, } +@(require_results) greaterThan_half :: proc "c" (a, b: half) -> bool { return a > b } @(require_results) greaterThan_float :: proc "c" (a, b: float) -> bool { return a > b } @(require_results) greaterThan_double :: proc "c" (a, b: double) -> bool { return a > b } @(require_results) greaterThan_int :: proc "c" (a, b: int) -> bool { return a > b } @(require_results) greaterThan_uint :: proc "c" (a, b: uint) -> bool { return a > b } +@(require_results) greaterThan_half2 :: proc "c" (a, b: half2) -> bool2 { return {a.x > b.x, a.y > b.y} } @(require_results) greaterThan_float2 :: proc "c" (a, b: float2) -> bool2 { return {a.x > b.x, a.y > b.y} } @(require_results) greaterThan_double2 :: proc "c" (a, b: double2) -> bool2 { return {a.x > b.x, a.y > b.y} } @(require_results) greaterThan_int2 :: proc "c" (a, b: int2) -> bool2 { return {a.x > b.x, a.y > b.y} } @(require_results) greaterThan_uint2 :: proc "c" (a, b: uint2) -> bool2 { return {a.x > b.x, a.y > b.y} } +@(require_results) greaterThan_half3 :: proc "c" (a, b: half3) -> bool3 { return {a.x > b.x, a.y > b.y, a.z > b.z} } @(require_results) greaterThan_float3 :: proc "c" (a, b: float3) -> bool3 { return {a.x > b.x, a.y > b.y, a.z > b.z} } @(require_results) greaterThan_double3 :: proc "c" (a, b: double3) -> bool3 { return {a.x > b.x, a.y > b.y, a.z > b.z} } @(require_results) greaterThan_int3 :: proc "c" (a, b: int3) -> bool3 { return {a.x > b.x, a.y > b.y, a.z > b.z} } @(require_results) greaterThan_uint3 :: proc "c" (a, b: uint3) -> bool3 { return {a.x > b.x, a.y > b.y, a.z > b.z} } +@(require_results) greaterThan_half4 :: proc "c" (a, b: half4) -> bool4 { return {a.x > b.x, a.y > b.y, a.z > b.z, a.w > b.w} } @(require_results) greaterThan_float4 :: proc "c" (a, b: float4) -> bool4 { return {a.x > b.x, a.y > b.y, a.z > b.z, a.w > b.w} } @(require_results) greaterThan_double4 :: proc "c" (a, b: double4) -> bool4 { return {a.x > b.x, a.y > b.y, a.z > b.z, a.w > b.w} } @(require_results) greaterThan_int4 :: proc "c" (a, b: int4) -> bool4 { return {a.x > b.x, a.y > b.y, a.z > b.z, a.w > b.w} } @@ -1330,35 +1772,43 @@ greaterThan :: proc{ greaterThanEqual :: proc{ + greaterThanEqual_half, greaterThanEqual_float, greaterThanEqual_double, greaterThanEqual_int, greaterThanEqual_uint, + greaterThanEqual_half2, greaterThanEqual_float2, greaterThanEqual_double2, greaterThanEqual_int2, greaterThanEqual_uint2, + greaterThanEqual_half3, greaterThanEqual_float3, greaterThanEqual_double3, greaterThanEqual_int3, greaterThanEqual_uint3, + greaterThanEqual_half4, greaterThanEqual_float4, greaterThanEqual_double4, greaterThanEqual_int4, greaterThanEqual_uint4, } +@(require_results) greaterThanEqual_half :: proc "c" (a, b: half) -> bool { return a >= b } @(require_results) greaterThanEqual_float :: proc "c" (a, b: float) -> bool { return a >= b } @(require_results) greaterThanEqual_double :: proc "c" (a, b: double) -> bool { return a >= b } @(require_results) greaterThanEqual_int :: proc "c" (a, b: int) -> bool { return a >= b } @(require_results) greaterThanEqual_uint :: proc "c" (a, b: uint) -> bool { return a >= b } +@(require_results) greaterThanEqual_half2 :: proc "c" (a, b: half2) -> bool2 { return {a.x >= b.x, a.y >= b.y} } @(require_results) greaterThanEqual_float2 :: proc "c" (a, b: float2) -> bool2 { return {a.x >= b.x, a.y >= b.y} } @(require_results) greaterThanEqual_double2 :: proc "c" (a, b: double2) -> bool2 { return {a.x >= b.x, a.y >= b.y} } @(require_results) greaterThanEqual_int2 :: proc "c" (a, b: int2) -> bool2 { return {a.x >= b.x, a.y >= b.y} } @(require_results) greaterThanEqual_uint2 :: proc "c" (a, b: uint2) -> bool2 { return {a.x >= b.x, a.y >= b.y} } +@(require_results) greaterThanEqual_half3 :: proc "c" (a, b: half3) -> bool3 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z} } @(require_results) greaterThanEqual_float3 :: proc "c" (a, b: float3) -> bool3 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z} } @(require_results) greaterThanEqual_double3 :: proc "c" (a, b: double3) -> bool3 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z} } @(require_results) greaterThanEqual_int3 :: proc "c" (a, b: int3) -> bool3 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z} } @(require_results) greaterThanEqual_uint3 :: proc "c" (a, b: uint3) -> bool3 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z} } +@(require_results) greaterThanEqual_half4 :: proc "c" (a, b: half4) -> bool4 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z, a.w >= b.w} } @(require_results) greaterThanEqual_float4 :: proc "c" (a, b: float4) -> bool4 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z, a.w >= b.w} } @(require_results) greaterThanEqual_double4 :: proc "c" (a, b: double4) -> bool4 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z, a.w >= b.w} } @(require_results) greaterThanEqual_int4 :: proc "c" (a, b: int4) -> bool4 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z, a.w >= b.w} } @@ -1366,70 +1816,86 @@ greaterThanEqual :: proc{ equal :: proc{ + equal_half, equal_float, equal_double, equal_int, equal_uint, + equal_half2, equal_float2, equal_double2, equal_int2, equal_uint2, + equal_half3, equal_float3, equal_double3, equal_int3, equal_uint3, + equal_half4, equal_float4, equal_double4, equal_int4, equal_uint4, } +@(require_results) equal_half :: proc "c" (a, b: half) -> bool { return a == b } @(require_results) equal_float :: proc "c" (a, b: float) -> bool { return a == b } @(require_results) equal_double :: proc "c" (a, b: double) -> bool { return a == b } @(require_results) equal_int :: proc "c" (a, b: int) -> bool { return a == b } @(require_results) equal_uint :: proc "c" (a, b: uint) -> bool { return a == b } +@(require_results) equal_half2 :: proc "c" (a, b: half2) -> bool2 { return {a.x == b.x, a.y == b.y} } @(require_results) equal_float2 :: proc "c" (a, b: float2) -> bool2 { return {a.x == b.x, a.y == b.y} } @(require_results) equal_double2 :: proc "c" (a, b: double2) -> bool2 { return {a.x == b.x, a.y == b.y} } @(require_results) equal_int2 :: proc "c" (a, b: int2) -> bool2 { return {a.x == b.x, a.y == b.y} } @(require_results) equal_uint2 :: proc "c" (a, b: uint2) -> bool2 { return {a.x == b.x, a.y == b.y} } +@(require_results) equal_half3 :: proc "c" (a, b: half3) -> bool3 { return {a.x == b.x, a.y == b.y, a.z == b.z} } @(require_results) equal_float3 :: proc "c" (a, b: float3) -> bool3 { return {a.x == b.x, a.y == b.y, a.z == b.z} } @(require_results) equal_double3 :: proc "c" (a, b: double3) -> bool3 { return {a.x == b.x, a.y == b.y, a.z == b.z} } @(require_results) equal_int3 :: proc "c" (a, b: int3) -> bool3 { return {a.x == b.x, a.y == b.y, a.z == b.z} } @(require_results) equal_uint3 :: proc "c" (a, b: uint3) -> bool3 { return {a.x == b.x, a.y == b.y, a.z == b.z} } +@(require_results) equal_half4 :: proc "c" (a, b: half4) -> bool4 { return {a.x == b.x, a.y == b.y, a.z == b.z, a.w == b.w} } @(require_results) equal_float4 :: proc "c" (a, b: float4) -> bool4 { return {a.x == b.x, a.y == b.y, a.z == b.z, a.w == b.w} } @(require_results) equal_double4 :: proc "c" (a, b: double4) -> bool4 { return {a.x == b.x, a.y == b.y, a.z == b.z, a.w == b.w} } @(require_results) equal_int4 :: proc "c" (a, b: int4) -> bool4 { return {a.x == b.x, a.y == b.y, a.z == b.z, a.w == b.w} } @(require_results) equal_uint4 :: proc "c" (a, b: uint4) -> bool4 { return {a.x == b.x, a.y == b.y, a.z == b.z, a.w == b.w} } notEqual :: proc{ + notEqual_half, notEqual_float, notEqual_double, notEqual_int, notEqual_uint, + notEqual_half2, notEqual_float2, notEqual_double2, notEqual_int2, notEqual_uint2, + notEqual_half3, notEqual_float3, notEqual_double3, notEqual_int3, notEqual_uint3, + notEqual_half4, notEqual_float4, notEqual_double4, notEqual_int4, notEqual_uint4, } +@(require_results) notEqual_half :: proc "c" (a, b: half) -> bool { return a != b } @(require_results) notEqual_float :: proc "c" (a, b: float) -> bool { return a != b } @(require_results) notEqual_double :: proc "c" (a, b: double) -> bool { return a != b } @(require_results) notEqual_int :: proc "c" (a, b: int) -> bool { return a != b } @(require_results) notEqual_uint :: proc "c" (a, b: uint) -> bool { return a != b } +@(require_results) notEqual_half2 :: proc "c" (a, b: half2) -> bool2 { return {a.x != b.x, a.y != b.y} } @(require_results) notEqual_float2 :: proc "c" (a, b: float2) -> bool2 { return {a.x != b.x, a.y != b.y} } @(require_results) notEqual_double2 :: proc "c" (a, b: double2) -> bool2 { return {a.x != b.x, a.y != b.y} } @(require_results) notEqual_int2 :: proc "c" (a, b: int2) -> bool2 { return {a.x != b.x, a.y != b.y} } @(require_results) notEqual_uint2 :: proc "c" (a, b: uint2) -> bool2 { return {a.x != b.x, a.y != b.y} } +@(require_results) notEqual_half3 :: proc "c" (a, b: half3) -> bool3 { return {a.x != b.x, a.y != b.y, a.z != b.z} } @(require_results) notEqual_float3 :: proc "c" (a, b: float3) -> bool3 { return {a.x != b.x, a.y != b.y, a.z != b.z} } @(require_results) notEqual_double3 :: proc "c" (a, b: double3) -> bool3 { return {a.x != b.x, a.y != b.y, a.z != b.z} } @(require_results) notEqual_int3 :: proc "c" (a, b: int3) -> bool3 { return {a.x != b.x, a.y != b.y, a.z != b.z} } @(require_results) notEqual_uint3 :: proc "c" (a, b: uint3) -> bool3 { return {a.x != b.x, a.y != b.y, a.z != b.z} } +@(require_results) notEqual_half4 :: proc "c" (a, b: half4) -> bool4 { return {a.x != b.x, a.y != b.y, a.z != b.z, a.w != b.w} } @(require_results) notEqual_float4 :: proc "c" (a, b: float4) -> bool4 { return {a.x != b.x, a.y != b.y, a.z != b.z, a.w != b.w} } @(require_results) notEqual_double4 :: proc "c" (a, b: double4) -> bool4 { return {a.x != b.x, a.y != b.y, a.z != b.z, a.w != b.w} } @(require_results) notEqual_int4 :: proc "c" (a, b: int4) -> bool4 { return {a.x != b.x, a.y != b.y, a.z != b.z, a.w != b.w} } @@ -1472,6 +1938,10 @@ not :: proc{ +@(require_results) inverse_half1x1 :: proc "c" (m: half1x1) -> half1x1 { return inverse_matrix1x1(m) } +@(require_results) inverse_half2x2 :: proc "c" (m: half2x2) -> half2x2 { return inverse_matrix2x2(m) } +@(require_results) inverse_half3x3 :: proc "c" (m: half3x3) -> half3x3 { return inverse_matrix3x3(m) } +@(require_results) inverse_half4x4 :: proc "c" (m: half4x4) -> half4x4 { return inverse_matrix4x4(m) } @(require_results) inverse_float1x1 :: proc "c" (m: float1x1) -> float1x1 { return inverse_matrix1x1(m) } @(require_results) inverse_float2x2 :: proc "c" (m: float2x2) -> float2x2 { return inverse_matrix2x2(m) } @(require_results) inverse_float3x3 :: proc "c" (m: float3x3) -> float3x3 { return inverse_matrix3x3(m) } @@ -1482,6 +1952,10 @@ not :: proc{ @(require_results) inverse_double4x4 :: proc "c" (m: double4x4) -> double4x4 { return inverse_matrix4x4(m) } inverse :: proc{ + inverse_half1x1, + inverse_half2x2, + inverse_half3x3, + inverse_half4x4, inverse_float1x1, inverse_float2x2, inverse_float3x3, @@ -1807,12 +2281,176 @@ inverse_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bou +ashalf :: proc{ + ashalf_half, + ashalf_float, + ashalf_double, + ashalf_int, + ashalf_uint, + ashalf_half1x1, + ashalf_half2x2, + ashalf_half3x3, + ashalf_half4x4, + ashalf_half1x2, + ashalf_half1x3, + ashalf_half1x4, + ashalf_half2x1, + ashalf_half2x3, + ashalf_half2x4, + ashalf_half3x1, + ashalf_half3x2, + ashalf_half3x4, + ashalf_half4x1, + ashalf_half4x2, + ashalf_half4x3, + ashalf_half2, + ashalf_half3, + ashalf_half4, + ashalf_float1x1, + ashalf_float2x2, + ashalf_float3x3, + ashalf_float4x4, + ashalf_float1x2, + ashalf_float1x3, + ashalf_float1x4, + ashalf_float2x1, + ashalf_float2x3, + ashalf_float2x4, + ashalf_float3x1, + ashalf_float3x2, + ashalf_float3x4, + ashalf_float4x1, + ashalf_float4x2, + ashalf_float4x3, + ashalf_float2, + ashalf_float3, + ashalf_float4, + ashalf_int2, + ashalf_int3, + ashalf_int4, + ashalf_uint2, + ashalf_uint3, + ashalf_uint4, + ashalf_bool2, + ashalf_bool3, + ashalf_bool4, + ashalf_double1x1, + ashalf_double2x2, + ashalf_double3x3, + ashalf_double4x4, + ashalf_double1x2, + ashalf_double1x3, + ashalf_double1x4, + ashalf_double2x1, + ashalf_double2x3, + ashalf_double2x4, + ashalf_double3x1, + ashalf_double3x2, + ashalf_double3x4, + ashalf_double4x1, + ashalf_double4x2, + ashalf_double4x3, + ashalf_double2, + ashalf_double3, + ashalf_double4, +} +@(require_results) ashalf_half :: proc "c" (v: half) -> half { return half(v) } +@(require_results) ashalf_float :: proc "c" (v: float) -> half { return half(v) } +@(require_results) ashalf_double :: proc "c" (v: double) -> half { return half(v) } +@(require_results) ashalf_int :: proc "c" (v: int) -> half { return half(v) } +@(require_results) ashalf_uint :: proc "c" (v: uint) -> half { return half(v) } +@(require_results) ashalf_half1x1 :: proc "c" (v: half1x1) -> half1x1 { return half1x1(v) } +@(require_results) ashalf_half2x2 :: proc "c" (v: half2x2) -> half2x2 { return half2x2(v) } +@(require_results) ashalf_half3x3 :: proc "c" (v: half3x3) -> half3x3 { return half3x3(v) } +@(require_results) ashalf_half4x4 :: proc "c" (v: half4x4) -> half4x4 { return half4x4(v) } +@(require_results) ashalf_half1x2 :: proc "c" (v: half1x2) -> half1x2 { return half1x2(v) } +@(require_results) ashalf_half1x3 :: proc "c" (v: half1x3) -> half1x3 { return half1x3(v) } +@(require_results) ashalf_half1x4 :: proc "c" (v: half1x4) -> half1x4 { return half1x4(v) } +@(require_results) ashalf_half2x1 :: proc "c" (v: half2x1) -> half2x1 { return half2x1(v) } +@(require_results) ashalf_half2x3 :: proc "c" (v: half2x3) -> half2x3 { return half2x3(v) } +@(require_results) ashalf_half2x4 :: proc "c" (v: half2x4) -> half2x4 { return half2x4(v) } +@(require_results) ashalf_half3x1 :: proc "c" (v: half3x1) -> half3x1 { return half3x1(v) } +@(require_results) ashalf_half3x2 :: proc "c" (v: half3x2) -> half3x2 { return half3x2(v) } +@(require_results) ashalf_half3x4 :: proc "c" (v: half3x4) -> half3x4 { return half3x4(v) } +@(require_results) ashalf_half4x1 :: proc "c" (v: half4x1) -> half4x1 { return half4x1(v) } +@(require_results) ashalf_half4x2 :: proc "c" (v: half4x2) -> half4x2 { return half4x2(v) } +@(require_results) ashalf_half4x3 :: proc "c" (v: half4x3) -> half4x3 { return half4x3(v) } +@(require_results) ashalf_half2 :: proc "c" (v: half2) -> half2 { return half2(v) } +@(require_results) ashalf_half3 :: proc "c" (v: half3) -> half3 { return half3(v) } +@(require_results) ashalf_half4 :: proc "c" (v: half4) -> half4 { return half4(v) } +@(require_results) ashalf_float1x1 :: proc "c" (v: float1x1) -> half1x1 { return half1x1(v) } +@(require_results) ashalf_float2x2 :: proc "c" (v: float2x2) -> half2x2 { return half2x2(v) } +@(require_results) ashalf_float3x3 :: proc "c" (v: float3x3) -> half3x3 { return half3x3(v) } +@(require_results) ashalf_float4x4 :: proc "c" (v: float4x4) -> half4x4 { return half4x4(v) } +@(require_results) ashalf_float1x2 :: proc "c" (v: float1x2) -> half1x2 { return half1x2(v) } +@(require_results) ashalf_float1x3 :: proc "c" (v: float1x3) -> half1x3 { return half1x3(v) } +@(require_results) ashalf_float1x4 :: proc "c" (v: float1x4) -> half1x4 { return half1x4(v) } +@(require_results) ashalf_float2x1 :: proc "c" (v: float2x1) -> half2x1 { return half2x1(v) } +@(require_results) ashalf_float2x3 :: proc "c" (v: float2x3) -> half2x3 { return half2x3(v) } +@(require_results) ashalf_float2x4 :: proc "c" (v: float2x4) -> half2x4 { return half2x4(v) } +@(require_results) ashalf_float3x1 :: proc "c" (v: float3x1) -> half3x1 { return half3x1(v) } +@(require_results) ashalf_float3x2 :: proc "c" (v: float3x2) -> half3x2 { return half3x2(v) } +@(require_results) ashalf_float3x4 :: proc "c" (v: float3x4) -> half3x4 { return half3x4(v) } +@(require_results) ashalf_float4x1 :: proc "c" (v: float4x1) -> half4x1 { return half4x1(v) } +@(require_results) ashalf_float4x2 :: proc "c" (v: float4x2) -> half4x2 { return half4x2(v) } +@(require_results) ashalf_float4x3 :: proc "c" (v: float4x3) -> half4x3 { return half4x3(v) } +@(require_results) ashalf_float2 :: proc "c" (v: float2) -> half2 { return half2{half(v.x), half(v.y)} } +@(require_results) ashalf_float3 :: proc "c" (v: float3) -> half3 { return half3{half(v.x), half(v.y), half(v.z)} } +@(require_results) ashalf_float4 :: proc "c" (v: float4) -> half4 { return half4{half(v.x), half(v.y), half(v.z), half(v.w)} } +@(require_results) ashalf_int2 :: proc "c" (v: int2) -> half2 { return half2{half(v.x), half(v.y)} } +@(require_results) ashalf_int3 :: proc "c" (v: int3) -> half3 { return half3{half(v.x), half(v.y), half(v.z)} } +@(require_results) ashalf_int4 :: proc "c" (v: int4) -> half4 { return half4{half(v.x), half(v.y), half(v.z), half(v.w)} } +@(require_results) ashalf_uint2 :: proc "c" (v: uint2) -> half2 { return half2{half(v.x), half(v.y)} } +@(require_results) ashalf_uint3 :: proc "c" (v: uint3) -> half3 { return half3{half(v.x), half(v.y), half(v.z)} } +@(require_results) ashalf_uint4 :: proc "c" (v: uint4) -> half4 { return half4{half(v.x), half(v.y), half(v.z), half(v.w)} } +@(require_results) ashalf_bool2 :: proc "c" (v: bool2) -> half2 { return half2{half(int(v.x)), half(int(v.y))} } +@(require_results) ashalf_bool3 :: proc "c" (v: bool3) -> half3 { return half3{half(int(v.x)), half(int(v.y)), half(int(v.z))} } +@(require_results) ashalf_bool4 :: proc "c" (v: bool4) -> half4 { return half4{half(int(v.x)), half(int(v.y)), half(int(v.z)), half(int(v.w))} } +@(require_results) ashalf_double1x1 :: proc "c" (v: double1x1) -> half1x1 { return half1x1(v) } +@(require_results) ashalf_double2x2 :: proc "c" (v: double2x2) -> half2x2 { return half2x2(v) } +@(require_results) ashalf_double3x3 :: proc "c" (v: double3x3) -> half3x3 { return half3x3(v) } +@(require_results) ashalf_double4x4 :: proc "c" (v: double4x4) -> half4x4 { return half4x4(v) } +@(require_results) ashalf_double1x2 :: proc "c" (v: double1x2) -> half1x2 { return half1x2(v) } +@(require_results) ashalf_double1x3 :: proc "c" (v: double1x3) -> half1x3 { return half1x3(v) } +@(require_results) ashalf_double1x4 :: proc "c" (v: double1x4) -> half1x4 { return half1x4(v) } +@(require_results) ashalf_double2x1 :: proc "c" (v: double2x1) -> half2x1 { return half2x1(v) } +@(require_results) ashalf_double2x3 :: proc "c" (v: double2x3) -> half2x3 { return half2x3(v) } +@(require_results) ashalf_double2x4 :: proc "c" (v: double2x4) -> half2x4 { return half2x4(v) } +@(require_results) ashalf_double3x1 :: proc "c" (v: double3x1) -> half3x1 { return half3x1(v) } +@(require_results) ashalf_double3x2 :: proc "c" (v: double3x2) -> half3x2 { return half3x2(v) } +@(require_results) ashalf_double3x4 :: proc "c" (v: double3x4) -> half3x4 { return half3x4(v) } +@(require_results) ashalf_double4x1 :: proc "c" (v: double4x1) -> half4x1 { return half4x1(v) } +@(require_results) ashalf_double4x2 :: proc "c" (v: double4x2) -> half4x2 { return half4x2(v) } +@(require_results) ashalf_double4x3 :: proc "c" (v: double4x3) -> half4x3 { return half4x3(v) } +@(require_results) ashalf_double2 :: proc "c" (v: double2) -> half2 { return half2{half(v.x), half(v.y)} } +@(require_results) ashalf_double3 :: proc "c" (v: double3) -> half3 { return half3{half(v.x), half(v.y), half(v.z)} } +@(require_results) ashalf_double4 :: proc "c" (v: double4) -> half4 { return half4{half(v.x), half(v.y), half(v.z), half(v.w)} } asfloat :: proc{ + asfloat_half, asfloat_float, asfloat_double, asfloat_int, asfloat_uint, + asfloat_half1x1, + asfloat_half2x2, + asfloat_half3x3, + asfloat_half4x4, + asfloat_half1x2, + asfloat_half1x3, + asfloat_half1x4, + asfloat_half2x1, + asfloat_half2x3, + asfloat_half2x4, + asfloat_half3x1, + asfloat_half3x2, + asfloat_half3x4, + asfloat_half4x1, + asfloat_half4x2, + asfloat_half4x3, + asfloat_half2, + asfloat_half3, + asfloat_half4, asfloat_float1x1, asfloat_float2x2, asfloat_float3x3, @@ -1861,10 +2499,30 @@ asfloat :: proc{ asfloat_double3, asfloat_double4, } +@(require_results) asfloat_half :: proc "c" (v: half) -> float { return float(v) } @(require_results) asfloat_float :: proc "c" (v: float) -> float { return float(v) } @(require_results) asfloat_double :: proc "c" (v: double) -> float { return float(v) } @(require_results) asfloat_int :: proc "c" (v: int) -> float { return float(v) } @(require_results) asfloat_uint :: proc "c" (v: uint) -> float { return float(v) } +@(require_results) asfloat_half1x1 :: proc "c" (v: half1x1) -> float1x1 { return float1x1(v) } +@(require_results) asfloat_half2x2 :: proc "c" (v: half2x2) -> float2x2 { return float2x2(v) } +@(require_results) asfloat_half3x3 :: proc "c" (v: half3x3) -> float3x3 { return float3x3(v) } +@(require_results) asfloat_half4x4 :: proc "c" (v: half4x4) -> float4x4 { return float4x4(v) } +@(require_results) asfloat_half1x2 :: proc "c" (v: half1x2) -> float1x2 { return float1x2(v) } +@(require_results) asfloat_half1x3 :: proc "c" (v: half1x3) -> float1x3 { return float1x3(v) } +@(require_results) asfloat_half1x4 :: proc "c" (v: half1x4) -> float1x4 { return float1x4(v) } +@(require_results) asfloat_half2x1 :: proc "c" (v: half2x1) -> float2x1 { return float2x1(v) } +@(require_results) asfloat_half2x3 :: proc "c" (v: half2x3) -> float2x3 { return float2x3(v) } +@(require_results) asfloat_half2x4 :: proc "c" (v: half2x4) -> float2x4 { return float2x4(v) } +@(require_results) asfloat_half3x1 :: proc "c" (v: half3x1) -> float3x1 { return float3x1(v) } +@(require_results) asfloat_half3x2 :: proc "c" (v: half3x2) -> float3x2 { return float3x2(v) } +@(require_results) asfloat_half3x4 :: proc "c" (v: half3x4) -> float3x4 { return float3x4(v) } +@(require_results) asfloat_half4x1 :: proc "c" (v: half4x1) -> float4x1 { return float4x1(v) } +@(require_results) asfloat_half4x2 :: proc "c" (v: half4x2) -> float4x2 { return float4x2(v) } +@(require_results) asfloat_half4x3 :: proc "c" (v: half4x3) -> float4x3 { return float4x3(v) } +@(require_results) asfloat_half2 :: proc "c" (v: half2) -> float2 { return float2{float(v.x), float(v.y)} } +@(require_results) asfloat_half3 :: proc "c" (v: half3) -> float3 { return float3{float(v.x), float(v.y), float(v.z)} } +@(require_results) asfloat_half4 :: proc "c" (v: half4) -> float4 { return float4{float(v.x), float(v.y), float(v.z), float(v.w)} } @(require_results) asfloat_float1x1 :: proc "c" (v: float1x1) -> float1x1 { return float1x1(v) } @(require_results) asfloat_float2x2 :: proc "c" (v: float2x2) -> float2x2 { return float2x2(v) } @(require_results) asfloat_float3x3 :: proc "c" (v: float3x3) -> float3x3 { return float3x3(v) } @@ -1914,10 +2572,30 @@ asfloat :: proc{ @(require_results) asfloat_double4 :: proc "c" (v: double4) -> float4 { return float4{float(v.x), float(v.y), float(v.z), float(v.w)} } asdouble :: proc{ + asdouble_half, asdouble_float, asdouble_double, asdouble_int, asdouble_uint, + asdouble_half1x1, + asdouble_half2x2, + asdouble_half3x3, + asdouble_half4x4, + asdouble_half1x2, + asdouble_half1x3, + asdouble_half1x4, + asdouble_half2x1, + asdouble_half2x3, + asdouble_half2x4, + asdouble_half3x1, + asdouble_half3x2, + asdouble_half3x4, + asdouble_half4x1, + asdouble_half4x2, + asdouble_half4x3, + asdouble_half2, + asdouble_half3, + asdouble_half4, asdouble_float1x1, asdouble_float2x2, asdouble_float3x3, @@ -1966,10 +2644,30 @@ asdouble :: proc{ asdouble_double3, asdouble_double4, } +@(require_results) asdouble_half :: proc "c" (v: half) -> double { return double(v) } @(require_results) asdouble_float :: proc "c" (v: float) -> double { return double(v) } @(require_results) asdouble_double :: proc "c" (v: double) -> double { return double(v) } @(require_results) asdouble_int :: proc "c" (v: int) -> double { return double(v) } @(require_results) asdouble_uint :: proc "c" (v: uint) -> double { return double(v) } +@(require_results) asdouble_half1x1 :: proc "c" (v: half1x1) -> double1x1 { return double1x1(v) } +@(require_results) asdouble_half2x2 :: proc "c" (v: half2x2) -> double2x2 { return double2x2(v) } +@(require_results) asdouble_half3x3 :: proc "c" (v: half3x3) -> double3x3 { return double3x3(v) } +@(require_results) asdouble_half4x4 :: proc "c" (v: half4x4) -> double4x4 { return double4x4(v) } +@(require_results) asdouble_half1x2 :: proc "c" (v: half1x2) -> double1x2 { return double1x2(v) } +@(require_results) asdouble_half1x3 :: proc "c" (v: half1x3) -> double1x3 { return double1x3(v) } +@(require_results) asdouble_half1x4 :: proc "c" (v: half1x4) -> double1x4 { return double1x4(v) } +@(require_results) asdouble_half2x1 :: proc "c" (v: half2x1) -> double2x1 { return double2x1(v) } +@(require_results) asdouble_half2x3 :: proc "c" (v: half2x3) -> double2x3 { return double2x3(v) } +@(require_results) asdouble_half2x4 :: proc "c" (v: half2x4) -> double2x4 { return double2x4(v) } +@(require_results) asdouble_half3x1 :: proc "c" (v: half3x1) -> double3x1 { return double3x1(v) } +@(require_results) asdouble_half3x2 :: proc "c" (v: half3x2) -> double3x2 { return double3x2(v) } +@(require_results) asdouble_half3x4 :: proc "c" (v: half3x4) -> double3x4 { return double3x4(v) } +@(require_results) asdouble_half4x1 :: proc "c" (v: half4x1) -> double4x1 { return double4x1(v) } +@(require_results) asdouble_half4x2 :: proc "c" (v: half4x2) -> double4x2 { return double4x2(v) } +@(require_results) asdouble_half4x3 :: proc "c" (v: half4x3) -> double4x3 { return double4x3(v) } +@(require_results) asdouble_half2 :: proc "c" (v: half2) -> double2 { return double2{double(v.x), double(v.y)} } +@(require_results) asdouble_half3 :: proc "c" (v: half3) -> double3 { return double3{double(v.x), double(v.y), double(v.z)} } +@(require_results) asdouble_half4 :: proc "c" (v: half4) -> double4 { return double4{double(v.x), double(v.y), double(v.z), double(v.w)} } @(require_results) asdouble_float1x1 :: proc "c" (v: float1x1) -> double1x1 { return double1x1(v) } @(require_results) asdouble_float2x2 :: proc "c" (v: float2x2) -> double2x2 { return double2x2(v) } @(require_results) asdouble_float3x3 :: proc "c" (v: float3x3) -> double3x3 { return double3x3(v) } @@ -2019,10 +2717,30 @@ asdouble :: proc{ @(require_results) asdouble_double4 :: proc "c" (v: double4) -> double4 { return double4{double(v.x), double(v.y), double(v.z), double(v.w)} } asint :: proc{ + asint_half, asint_float, asint_double, asint_int, asint_uint, + asint_half1x1, + asint_half2x2, + asint_half3x3, + asint_half4x4, + asint_half1x2, + asint_half1x3, + asint_half1x4, + asint_half2x1, + asint_half2x3, + asint_half2x4, + asint_half3x1, + asint_half3x2, + asint_half3x4, + asint_half4x1, + asint_half4x2, + asint_half4x3, + asint_half2, + asint_half3, + asint_half4, asint_float1x1, asint_float2x2, asint_float3x3, @@ -2071,10 +2789,30 @@ asint :: proc{ asint_double3, asint_double4, } +@(require_results) asint_half :: proc "c" (v: half) -> int { return int(v) } @(require_results) asint_float :: proc "c" (v: float) -> int { return int(v) } @(require_results) asint_double :: proc "c" (v: double) -> int { return int(v) } @(require_results) asint_int :: proc "c" (v: int) -> int { return int(v) } @(require_results) asint_uint :: proc "c" (v: uint) -> int { return int(v) } +@(require_results) asint_half1x1 :: proc "c" (v: half1x1) -> int1x1 { return int1x1(v) } +@(require_results) asint_half2x2 :: proc "c" (v: half2x2) -> int2x2 { return int2x2(v) } +@(require_results) asint_half3x3 :: proc "c" (v: half3x3) -> int3x3 { return int3x3(v) } +@(require_results) asint_half4x4 :: proc "c" (v: half4x4) -> int4x4 { return int4x4(v) } +@(require_results) asint_half1x2 :: proc "c" (v: half1x2) -> int1x2 { return int1x2(v) } +@(require_results) asint_half1x3 :: proc "c" (v: half1x3) -> int1x3 { return int1x3(v) } +@(require_results) asint_half1x4 :: proc "c" (v: half1x4) -> int1x4 { return int1x4(v) } +@(require_results) asint_half2x1 :: proc "c" (v: half2x1) -> int2x1 { return int2x1(v) } +@(require_results) asint_half2x3 :: proc "c" (v: half2x3) -> int2x3 { return int2x3(v) } +@(require_results) asint_half2x4 :: proc "c" (v: half2x4) -> int2x4 { return int2x4(v) } +@(require_results) asint_half3x1 :: proc "c" (v: half3x1) -> int3x1 { return int3x1(v) } +@(require_results) asint_half3x2 :: proc "c" (v: half3x2) -> int3x2 { return int3x2(v) } +@(require_results) asint_half3x4 :: proc "c" (v: half3x4) -> int3x4 { return int3x4(v) } +@(require_results) asint_half4x1 :: proc "c" (v: half4x1) -> int4x1 { return int4x1(v) } +@(require_results) asint_half4x2 :: proc "c" (v: half4x2) -> int4x2 { return int4x2(v) } +@(require_results) asint_half4x3 :: proc "c" (v: half4x3) -> int4x3 { return int4x3(v) } +@(require_results) asint_half2 :: proc "c" (v: half2) -> int2 { return int2{int(v.x), int(v.y)} } +@(require_results) asint_half3 :: proc "c" (v: half3) -> int3 { return int3{int(v.x), int(v.y), int(v.z)} } +@(require_results) asint_half4 :: proc "c" (v: half4) -> int4 { return int4{int(v.x), int(v.y), int(v.z), int(v.w)} } @(require_results) asint_float1x1 :: proc "c" (v: float1x1) -> int1x1 { return int1x1(v) } @(require_results) asint_float2x2 :: proc "c" (v: float2x2) -> int2x2 { return int2x2(v) } @(require_results) asint_float3x3 :: proc "c" (v: float3x3) -> int3x3 { return int3x3(v) } @@ -2125,10 +2863,14 @@ asint :: proc{ asuint :: proc{ + asuint_half, asuint_float, asuint_double, asuint_int, asuint_uint, + asuint_half2, + asuint_half3, + asuint_half4, asuint_float2, asuint_float3, asuint_float4, @@ -2145,10 +2887,14 @@ asuint :: proc{ asuint_double3, asuint_double4, } +@(require_results) asuint_half :: proc "c" (v: half) -> uint { return uint(v) } @(require_results) asuint_float :: proc "c" (v: float) -> uint { return uint(v) } @(require_results) asuint_double :: proc "c" (v: double) -> uint { return uint(v) } @(require_results) asuint_int :: proc "c" (v: int) -> uint { return uint(v) } @(require_results) asuint_uint :: proc "c" (v: uint) -> uint { return uint(v) } +@(require_results) asuint_half2 :: proc "c" (v: half2) -> uint2 { return uint2{uint(v.x), uint(v.y)} } +@(require_results) asuint_half3 :: proc "c" (v: half3) -> uint3 { return uint3{uint(v.x), uint(v.y), uint(v.z)} } +@(require_results) asuint_half4 :: proc "c" (v: half4) -> uint4 { return uint4{uint(v.x), uint(v.y), uint(v.z), uint(v.w)} } @(require_results) asuint_float2 :: proc "c" (v: float2) -> uint2 { return uint2{uint(v.x), uint(v.y)} } @(require_results) asuint_float3 :: proc "c" (v: float3) -> uint3 { return uint3{uint(v.x), uint(v.y), uint(v.z)} } @(require_results) asuint_float4 :: proc "c" (v: float4) -> uint4 { return uint4{uint(v.x), uint(v.y), uint(v.z), uint(v.w)} } diff --git a/core/math/linalg/hlsl/linalg_hlsl_math.odin b/core/math/linalg/hlsl/linalg_hlsl_math.odin index 5b8004342..92c741f03 100644 --- a/core/math/linalg/hlsl/linalg_hlsl_math.odin +++ b/core/math/linalg/hlsl/linalg_hlsl_math.odin @@ -2,6 +2,42 @@ package math_linalg_hlsl import "core:math" +@(require_results) cos_half :: proc "c" (x: half) -> half { return math.cos(x) } +@(require_results) sin_half :: proc "c" (x: half) -> half { return math.sin(x) } +@(require_results) tan_half :: proc "c" (x: half) -> half { return math.tan(x) } +@(require_results) acos_half :: proc "c" (x: half) -> half { return math.acos(x) } +@(require_results) asin_half :: proc "c" (x: half) -> half { return math.asin(x) } +@(require_results) atan_half :: proc "c" (x: half) -> half { return math.atan(x) } +@(require_results) atan2_half :: proc "c" (y, x: half) -> half { return math.atan2(y, x) } +@(require_results) cosh_half :: proc "c" (x: half) -> half { return math.cosh(x) } +@(require_results) sinh_half :: proc "c" (x: half) -> half { return math.sinh(x) } +@(require_results) tanh_half :: proc "c" (x: half) -> half { return math.tanh(x) } +@(require_results) acosh_half :: proc "c" (x: half) -> half { return math.acosh(x) } +@(require_results) asinh_half :: proc "c" (x: half) -> half { return math.asinh(x) } +@(require_results) atanh_half :: proc "c" (x: half) -> half { return math.atanh(x) } +@(require_results) sqrt_half :: proc "c" (x: half) -> half { return math.sqrt(x) } +@(require_results) rsqrt_half :: proc "c" (x: half) -> half { return 1.0/math.sqrt(x) } +@(require_results) rcp_half :: proc "c" (x: half) -> half { return 1.0/x } +@(require_results) pow_half :: proc "c" (x, y: half) -> half { return math.pow(x, y) } +@(require_results) exp_half :: proc "c" (x: half) -> half { return math.exp(x) } +@(require_results) log_half :: proc "c" (x: half) -> half { return math.ln(x) } +@(require_results) log2_half :: proc "c" (x: half) -> half { return math.log(x, 2) } +@(require_results) log10_half :: proc "c" (x: half) -> half { return math.log(x, 10) } +@(require_results) exp2_half :: proc "c" (x: half) -> half { return math.pow(half(2), x) } +@(require_results) sign_half :: proc "c" (x: half) -> half { return math.sign(x) } +@(require_results) floor_half :: proc "c" (x: half) -> half { return math.floor(x) } +@(require_results) round_half :: proc "c" (x: half) -> half { return math.round(x) } +@(require_results) ceil_half :: proc "c" (x: half) -> half { return math.ceil(x) } +@(require_results) isnan_half :: proc "c" (x: half) -> bool { return math.classify(x) == .NaN} +@(require_results) fmod_half :: proc "c" (x, y: half) -> half { return math.mod(x, y) } +@(require_results) +frac_half :: proc "c" (x: half) -> half { + if x >= 0 { + return x - math.trunc(x) + } + return math.trunc(-x) + x +} + @(require_results) cos_float :: proc "c" (x: float) -> float { return math.cos(x) } @(require_results) sin_float :: proc "c" (x: float) -> float { return math.sin(x) } @(require_results) tan_float :: proc "c" (x: float) -> float { return math.tan(x) } diff --git a/core/math/rand/rand.odin b/core/math/rand/rand.odin index 8e270b7df..e8383ca9e 100644 --- a/core/math/rand/rand.odin +++ b/core/math/rand/rand.odin @@ -208,7 +208,7 @@ Inputs: Returns: - val: A random 31 bit value in the range `[0, n)` -WARNING: Panics if n is less than 0 +WARNING: Panics if n is less than or equal to 0 Example: import "core:math/rand" @@ -249,7 +249,7 @@ Inputs: Returns: - val: A random 63 bit value in the range `[0, n)` -WARNING: Panics if n is less than 0 +WARNING: Panics if n is less than or equal to 0 Example: import "core:math/rand" @@ -290,7 +290,7 @@ Inputs: Returns: - val: A random 127 bit value in the range `[0, n)` -WARNING: Panics if n is less than 0 +WARNING: Panics if n is less than or equal to 0 Example: import "core:math/rand" @@ -331,7 +331,7 @@ Inputs: Returns: - val: A random integer value in the range `[0, n)` -WARNING: Panics if n is less than 0 +WARNING: Panics if n is less than or equal to 0 Example: import "core:math/rand" diff --git a/core/mem/mem.odin b/core/mem/mem.odin index 96ec1990a..8ce48d489 100644 --- a/core/mem/mem.odin +++ b/core/mem/mem.odin @@ -332,7 +332,7 @@ and returns an integer count of the `T` between them. - `b`: A pointer to a type T **Returns** -- `b` - `a` in items of T as an `int`. +- `a` - `b` in items of T as an `int`. Example: @@ -704,4 +704,4 @@ calc_padding_with_header :: proc "contextless" (ptr: uintptr, align: uintptr, he } } return int(padding) -}
\ No newline at end of file +} diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 7ebe275ed..9ce484a10 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -3210,6 +3210,17 @@ parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Expr { return ce } +empty_selector_expr :: proc(tok: tokenizer.Token, operand: ^ast.Expr) -> ^ast.Selector_Expr { + field := ast.new(ast.Ident, tok.pos, end_pos(tok)) + field.name = "" + + sel := ast.new(ast.Selector_Expr, operand.pos, field) + sel.expr = operand + sel.op = tok + sel.field = field + + return sel +} parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^ast.Expr) { operand = value @@ -3343,8 +3354,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a case: error(p, p.curr_tok.pos, "expected a selector") - advance_token(p) - operand = ast.new(ast.Bad_Expr, operand.pos, end_pos(tok)) + operand = empty_selector_expr(tok, operand) } case .Arrow_Right: @@ -3361,8 +3371,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a operand = sel case: error(p, p.curr_tok.pos, "expected a selector") - advance_token(p) - operand = ast.new(ast.Bad_Expr, operand.pos, end_pos(tok)) + operand = empty_selector_expr(tok, operand) } case .Pointer: diff --git a/core/os/os2/dir.odin b/core/os/os2/dir.odin index 10b06a8ce..f63754273 100644 --- a/core/os/os2/dir.odin +++ b/core/os/os2/dir.odin @@ -6,6 +6,10 @@ import "core:strings" read_dir :: read_directory +/* + Reads the file `f` (assuming it is a directory) and returns the unsorted directory entries. + This returns up to `n` entries OR all of them if `n <= 0`. +*/ @(require_results) read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (files: []File_Info, err: Error) { if f == nil { @@ -47,11 +51,18 @@ read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (files } +/* + Reads the file `f` (assuming it is a directory) and returns all of the unsorted directory entries. +*/ @(require_results) read_all_directory :: proc(f: ^File, allocator: runtime.Allocator) -> (fi: []File_Info, err: Error) { return read_directory(f, -1, allocator) } +/* + Reads the named directory by path (assuming it is a directory) and returns the unsorted directory entries. + This returns up to `n` entries OR all of them if `n <= 0`. +*/ @(require_results) read_directory_by_path :: proc(path: string, n: int, allocator: runtime.Allocator) -> (fi: []File_Info, err: Error) { f := open(path) or_return @@ -59,6 +70,9 @@ read_directory_by_path :: proc(path: string, n: int, allocator: runtime.Allocato return read_directory(f, n, allocator) } +/* + Reads the named directory by path (assuming it is a directory) and returns all of the unsorted directory entries. +*/ @(require_results) read_all_directory_by_path :: proc(path: string, allocator: runtime.Allocator) -> (fi: []File_Info, err: Error) { return read_directory_by_path(path, -1, allocator) diff --git a/core/os/os2/dir_windows.odin b/core/os/os2/dir_windows.odin index 6c754a677..a4dadca75 100644 --- a/core/os/os2/dir_windows.odin +++ b/core/os/os2/dir_windows.odin @@ -18,7 +18,7 @@ find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW, al temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) path := concatenate({base_path, `\`, win32_wstring_to_utf8(cstring16(raw_data(d.cFileName[:])), temp_allocator) or_else ""}, allocator) or_return - handle := win32.HANDLE(_open_internal(path, {.Read}, 0o666) or_else 0) + handle := win32.HANDLE(_open_internal(path, {.Read}, Permissions_Read_Write_All) or_else 0) defer win32.CloseHandle(handle) fi.fullpath = path diff --git a/core/os/os2/errors.odin b/core/os/os2/errors.odin index 1cf7d765c..077697c0d 100644 --- a/core/os/os2/errors.odin +++ b/core/os/os2/errors.odin @@ -3,6 +3,10 @@ package os2 import "core:io" import "base:runtime" +/* + General errors that are common within this package which cannot + be categorized by `io.Error` nor `runtime.Allocator_Error`. +*/ General_Error :: enum u32 { None, @@ -33,8 +37,12 @@ General_Error :: enum u32 { Unsupported, } +// A platform specific error Platform_Error :: _Platform_Error +/* + `Error` is a union of different classes of errors that could be returned from procedures in this package. +*/ Error :: union #shared_nil { General_Error, io.Error, @@ -46,6 +54,7 @@ Error :: union #shared_nil { ERROR_NONE :: Error{} +// Attempts to convert an `Error` into a platform specific error as an integer. `ok` is false if not possible @(require_results) is_platform_error :: proc(ferr: Error) -> (err: i32, ok: bool) { v := ferr.(Platform_Error) or_else {} @@ -53,6 +62,7 @@ is_platform_error :: proc(ferr: Error) -> (err: i32, ok: bool) { } +// Attempts to return the error `ferr` as a string without any allocation @(require_results) error_string :: proc(ferr: Error) -> string { if ferr == nil { @@ -112,6 +122,9 @@ error_string :: proc(ferr: Error) -> string { return "unknown error" } +/* + `print_error` is a utility procedure which will print an error `ferr` to a specified file `f`. +*/ print_error :: proc(f: ^File, ferr: Error, msg: string) { temp_allocator := TEMP_ALLOCATOR_GUARD({}) err_str := error_string(ferr) @@ -127,3 +140,14 @@ print_error :: proc(f: ^File, ferr: Error, msg: string) { buf[length - 1] = '\n' write(f, buf) } + + + +// Attempts to convert an `Error` `ferr` into an `io.Error` +@(private) +error_to_io_error :: proc(ferr: Error) -> io.Error { + if ferr == nil { + return .None + } + return ferr.(io.Error) or_else .Unknown +} diff --git a/core/os/os2/file.odin b/core/os/os2/file.odin index a9878a563..85ebfcece 100644 --- a/core/os/os2/file.odin +++ b/core/os/os2/file.odin @@ -56,6 +56,7 @@ File_Type :: enum { Character_Device, } +// Represents the file flags for a file handle File_Flags :: distinct bit_set[File_Flag; uint] File_Flag :: enum { Read, @@ -90,17 +91,77 @@ O_SPARSE :: File_Flags{.Sparse} */ O_INHERITABLE :: File_Flags{.Inheritable} +Permissions :: distinct bit_set[Permission_Flag; u32] +Permission_Flag :: enum u32 { + Execute_Other = 0, + Write_Other = 1, + Read_Other = 2, + + Execute_Group = 3, + Write_Group = 4, + Read_Group = 5, + + Execute_User = 6, + Write_User = 7, + Read_User = 8, +} + +Permissions_Execute_All :: Permissions{.Execute_User, .Execute_Group, .Execute_Other} +Permissions_Write_All :: Permissions{.Write_User, .Write_Group, .Write_Other} +Permissions_Read_All :: Permissions{.Read_User, .Read_Group, .Read_Other} + +Permissions_Read_Write_All :: Permissions_Read_All + Permissions_Write_All + +Permissions_All :: Permissions_Read_All + Permissions_Write_All + Permissions_Execute_All + +Permissions_Default_File :: Permissions_Read_All + Permissions_Write_All +Permissions_Default_Directory :: Permissions_Read_All + Permissions_Write_All + Permissions_Execute_All +Permissions_Default :: Permissions_Default_Directory + +perm :: proc{ + perm_number, +} + +/* + `perm_number` converts an integer value `perm` to the bit set `Permissions` +*/ +@(require_results) +perm_number :: proc "contextless" (perm: int) -> Permissions { + return transmute(Permissions)u32(perm & 0o777) +} + + + +// `stdin` is an open file pointing to the standard input file stream stdin: ^File = nil // OS-Specific + +// `stdout` is an open file pointing to the standard output file stream stdout: ^File = nil // OS-Specific + +// `stderr` is an open file pointing to the standard error file stream stderr: ^File = nil // OS-Specific +/* + `create` creates or truncates a named file `name`. + If the file already exists, it is truncated. + If the file does not exist, it is created with the `Permissions_Default_File` permissions. + If successful, a `^File` is return which can be used for I/O. + And error is returned if any is encountered. +*/ @(require_results) create :: proc(name: string) -> (^File, Error) { - return open(name, {.Read, .Write, .Create}, 0o777) + return open(name, {.Read, .Write, .Create, .Trunc}, Permissions_Default_File) } +/* + `open` is a generalized open call, which defaults to opening for reading. + If the file does not exist, and the `{.Create}` flag is passed, it is created with the permissions `perm`, + and please note that the containing directory must exist otherwise and an error will be returned. + If successful, a `^File` is return which can be used for I/O. + And error is returned if any is encountered. +*/ @(require_results) -open :: proc(name: string, flags := File_Flags{.Read}, perm := 0o777) -> (^File, Error) { +open :: proc(name: string, flags := File_Flags{.Read}, perm := Permissions_Default) -> (^File, Error) { return _open(name, flags, perm) } @@ -112,7 +173,10 @@ open :: proc(name: string, flags := File_Flags{.Read}, perm := 0o777) -> (^File, // return _open_buffered(name, buffer_size, flags, perm) // } - +/* + `new_file` returns a new `^File` with the given file descriptor `handle` and `name`. + The return value will only be `nil` IF the `handle` is not a valid file descriptor. +*/ @(require_results) new_file :: proc(handle: uintptr, name: string) -> ^File { file, err := _new_file(handle, name, file_allocator()) @@ -122,16 +186,25 @@ new_file :: proc(handle: uintptr, name: string) -> ^File { return file } +/* + `clone` returns a new `^File` based on the passed file `f` with the same underlying file descriptor. +*/ @(require_results) clone :: proc(f: ^File) -> (^File, Error) { return _clone(f) } +/* + `fd` returns the file descriptor of the file `f` passed. If the file is not valid, an invalid handle will be returned. +*/ @(require_results) fd :: proc(f: ^File) -> uintptr { return _fd(f) } +/* + `name` returns the name of the file. The lifetime of this string lasts as long as the file handle itself. +*/ @(require_results) name :: proc(f: ^File) -> string { return _name(f) @@ -150,6 +223,16 @@ close :: proc(f: ^File) -> Error { return nil } +/* + seek sets the offsets for the next read or write on a file to a specified `offset`, + according to what `whence` is set. + `.Start` is relative to the origin of the file. + `.Current` is relative to the current offset. + `.End` is relative to the end. + It returns the new offset and an error, if any is encountered. + Prefer `read_at` or `write_at` if the offset does not want to be changed. + +*/ seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) { if f != nil { return io.seek(f.stream, offset, whence) @@ -157,6 +240,11 @@ seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Err return 0, .Invalid_File } +/* + `read` reads up to len(p) bytes from the file `f`, and then stores them in `p`. + It returns the number of bytes read and an error, if any is encountered. + At the end of a file, it returns `0, io.EOF`. +*/ read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) { if f != nil { return io.read(f.stream, p) @@ -164,6 +252,12 @@ read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) { return 0, .Invalid_File } +/* + `read_at` reads up to len(p) bytes from the file `f` at the byte offset `offset`, and then stores them in `p`. + It returns the number of bytes read and an error, if any is encountered. + `read_at` always returns a non-nil error when `n < len(p)`. + At the end of a file, the error is `io.EOF`. +*/ read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) { if f != nil { return io.read_at(f.stream, p, offset) @@ -171,6 +265,11 @@ read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) { return 0, .Invalid_File } +/* + `write` writes `len(p)` bytes from `p` to the file `f`. It returns the number of bytes written to + and an error, if any is encountered. + `write` returns a non-nil error when `n != len(p)`. +*/ write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) { if f != nil { return io.write(f.stream, p) @@ -178,6 +277,11 @@ write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) { return 0, .Invalid_File } +/* + `write_at` writes `len(p)` bytes from `p` to the file `f` starting at byte offset `offset`. + It returns the number of bytes written to and an error, if any is encountered. + `write_at` returns a non-nil error when `n != len(p)`. +*/ write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) { if f != nil { return io.write_at(f.stream, p, offset) @@ -185,6 +289,9 @@ write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) { return 0, .Invalid_File } +/* + `file_size` returns the length of the file `f` in bytes and an error, if any is encountered. +*/ file_size :: proc(f: ^File) -> (n: i64, err: Error) { if f != nil { return io.size(f.stream) @@ -192,6 +299,9 @@ file_size :: proc(f: ^File) -> (n: i64, err: Error) { return 0, .Invalid_File } +/* + `flush` flushes a file `f` +*/ flush :: proc(f: ^File) -> Error { if f != nil { return io.flush(f.stream) @@ -199,31 +309,54 @@ flush :: proc(f: ^File) -> Error { return nil } +/* + `sync` commits the current contents of the file `f` to stable storage. + This usually means flushing the file system's in-memory copy to disk. +*/ sync :: proc(f: ^File) -> Error { return _sync(f) } +/* + `truncate` changes the size of the file `f` to `size` in bytes. + This can be used to shorten or lengthen a file. + It does not change the "offset" of the file. +*/ truncate :: proc(f: ^File, size: i64) -> Error { return _truncate(f, size) } +/* + `remove` removes a named file or (empty) directory. +*/ remove :: proc(name: string) -> Error { return _remove(name) } +/* + `rename` renames (moves) `old_path` to `new_path`. +*/ rename :: proc(old_path, new_path: string) -> Error { return _rename(old_path, new_path) } - +/* + `link` creates a `new_name` as a hard link to the `old_name` file. +*/ link :: proc(old_name, new_name: string) -> Error { return _link(old_name, new_name) } +/* + `symlink` creates a `new_name` as a symbolic link to the `old_name` file. +*/ symlink :: proc(old_name, new_name: string) -> Error { return _symlink(old_name, new_name) } +/* + `read_link` returns the destinction of the named symbolic link `name`. +*/ read_link :: proc(name: string, allocator: runtime.Allocator) -> (string, Error) { return _read_link(name,allocator) } @@ -231,36 +364,65 @@ read_link :: proc(name: string, allocator: runtime.Allocator) -> (string, Error) chdir :: change_directory +/* + Changes the current working directory to the named directory. +*/ change_directory :: proc(name: string) -> Error { return _chdir(name) } chmod :: change_mode -change_mode :: proc(name: string, mode: int) -> Error { +/* + Changes the mode/permissions of the named file to `mode`. + If the file is a symbolic link, it changes the mode of the link's target. + + On Windows, only `{.Write_User}` of `mode` is used, and controls whether or not + the file has a read-only attribute. Use `{.Read_User}` for a read-only file and + `{.Read_User, .Write_User}` for a readable & writable file. +*/ +change_mode :: proc(name: string, mode: Permissions) -> Error { return _chmod(name, mode) } chown :: change_owner +/* + Changes the numeric `uid` and `gid` of a named file. If the file is a symbolic link, + it changes the `uid` and `gid` of the link's target. + + On Windows, it always returns an error. +*/ change_owner :: proc(name: string, uid, gid: int) -> Error { return _chown(name, uid, gid) } fchdir :: fchange_directory +/* + Changes the current working directory to the file, which must be a directory. +*/ fchange_directory :: proc(f: ^File) -> Error { return _fchdir(f) } fchmod :: fchange_mode -fchange_mode :: proc(f: ^File, mode: int) -> Error { +/* + Changes the current `mode` permissions of the file `f`. +*/ +fchange_mode :: proc(f: ^File, mode: Permissions) -> Error { return _fchmod(f, mode) } fchown :: fchange_owner +/* + Changes the numeric `uid` and `gid` of the file `f`. If the file is a symbolic link, + it changes the `uid` and `gid` of the link's target. + + On Windows, it always returns an error. +*/ fchange_owner :: proc(f: ^File, uid, gid: int) -> Error { return _fchown(f, uid, gid) } @@ -268,27 +430,45 @@ fchange_owner :: proc(f: ^File, uid, gid: int) -> Error { lchown :: change_owner_do_not_follow_links +/* + Changes the numeric `uid` and `gid` of the file `f`. If the file is a symbolic link, + it changes the `uid` and `gid` of the lin itself. + + On Windows, it always returns an error. +*/ change_owner_do_not_follow_links :: proc(name: string, uid, gid: int) -> Error { return _lchown(name, uid, gid) } chtimes :: change_times +/* + Changes the access `atime` and modification `mtime` times of a named file. +*/ change_times :: proc(name: string, atime, mtime: time.Time) -> Error { return _chtimes(name, atime, mtime) } fchtimes :: fchange_times +/* + Changes the access `atime` and modification `mtime` times of the file `f`. +*/ fchange_times :: proc(f: ^File, atime, mtime: time.Time) -> Error { return _fchtimes(f, atime, mtime) } +/* + `exists` returns whether or not a named file exists. +*/ @(require_results) exists :: proc(path: string) -> bool { return _exists(path) } +/* + `is_file` returns whether or not the type of a named file is a `File_Type.Regular` file. +*/ @(require_results) is_file :: proc(path: string) -> bool { temp_allocator := TEMP_ALLOCATOR_GUARD({}) @@ -301,6 +481,9 @@ is_file :: proc(path: string) -> bool { is_dir :: is_directory +/* + Returns whether or not the type of a named file is a `File_Type.Directory` file. +*/ @(require_results) is_directory :: proc(path: string) -> bool { temp_allocator := TEMP_ALLOCATOR_GUARD({}) @@ -311,7 +494,9 @@ is_directory :: proc(path: string) -> bool { return fi.type == .Directory } - +/* + `copy_file` copies a file from `src_path` to `dst_path` and returns an error if any was encountered. +*/ copy_file :: proc(dst_path, src_path: string) -> Error { when #defined(_copy_file_native) { return _copy_file_native(dst_path, src_path) @@ -331,7 +516,7 @@ _copy_file :: proc(dst_path, src_path: string) -> Error { return .Invalid_File } - dst := open(dst_path, {.Read, .Write, .Create, .Trunc}, info.mode & 0o777) or_return + dst := open(dst_path, {.Read, .Write, .Create, .Trunc}, info.mode & Permissions_All) or_return defer close(dst) _, err := io.copy(to_writer(dst), to_reader(src)) diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin index 85f639c8b..b2350d9b5 100644 --- a/core/os/os2/file_linux.odin +++ b/core/os/os2/file_linux.odin @@ -65,7 +65,7 @@ _standard_stream_init :: proc "contextless" () { stderr = new_std(&files[2], 2, "/proc/self/fd/2") } -_open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) { +_open :: proc(name: string, flags: File_Flags, perm: Permissions) -> (f: ^File, err: Error) { temp_allocator := TEMP_ALLOCATOR_GUARD({}) name_cstr := clone_to_cstring(name, temp_allocator) or_return @@ -88,7 +88,7 @@ _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Err if .Trunc in flags { sys_flags += {.TRUNC} } if .Inheritable in flags { sys_flags -= {.CLOEXEC} } - fd, errno := linux.open(name_cstr, sys_flags, transmute(linux.Mode)u32(perm)) + fd, errno := linux.open(name_cstr, sys_flags, transmute(linux.Mode)transmute(u32)perm) if errno != .NONE { return nil, _get_platform_error(errno) } @@ -132,7 +132,7 @@ _clone :: proc(f: ^File) -> (clone: ^File, err: Error) { @(require_results) -_open_buffered :: proc(name: string, buffer_size: uint, flags := File_Flags{.Read}, perm := 0o777) -> (f: ^File, err: Error) { +_open_buffered :: proc(name: string, buffer_size: uint, flags := File_Flags{.Read}, perm: Permissions) -> (f: ^File, err: Error) { assert(buffer_size > 0) f, err = _open(name, flags, perm) if f != nil && err == nil { @@ -369,15 +369,15 @@ _fchdir :: proc(f: ^File) -> Error { return _get_platform_error(linux.fchdir(impl.fd)) } -_chmod :: proc(name: string, mode: int) -> Error { +_chmod :: proc(name: string, mode: Permissions) -> Error { temp_allocator := TEMP_ALLOCATOR_GUARD({}) name_cstr := clone_to_cstring(name, temp_allocator) or_return - return _get_platform_error(linux.chmod(name_cstr, transmute(linux.Mode)(u32(mode)))) + return _get_platform_error(linux.chmod(name_cstr, transmute(linux.Mode)transmute(u32)mode)) } -_fchmod :: proc(f: ^File, mode: int) -> Error { +_fchmod :: proc(f: ^File, mode: Permissions) -> Error { impl := (^File_Impl)(f.impl) - return _get_platform_error(linux.fchmod(impl.fd, transmute(linux.Mode)(u32(mode)))) + return _get_platform_error(linux.fchmod(impl.fd, transmute(linux.Mode)transmute(u32)mode)) } // NOTE: will throw error without super user priviledges diff --git a/core/os/os2/file_posix.odin b/core/os/os2/file_posix.odin index a251a3ae7..fd409b9d4 100644 --- a/core/os/os2/file_posix.odin +++ b/core/os/os2/file_posix.odin @@ -46,7 +46,7 @@ init_std_files :: proc "contextless" () { stderr = new_std(&files[2], posix.STDERR_FILENO, "/dev/stderr") } -_open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) { +_open :: proc(name: string, flags: File_Flags, perm: Permissions) -> (f: ^File, err: Error) { if name == "" { err = .Invalid_Path return @@ -72,7 +72,7 @@ _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Err temp_allocator := TEMP_ALLOCATOR_GUARD({}) cname := clone_to_cstring(name, temp_allocator) or_return - fd := posix.open(cname, sys_flags, transmute(posix.mode_t)posix._mode_t(perm)) + fd := posix.open(cname, sys_flags, transmute(posix.mode_t)posix._mode_t(transmute(u32)perm)) if fd < 0 { err = _get_platform_error() return @@ -284,17 +284,17 @@ _fchdir :: proc(f: ^File) -> Error { return nil } -_fchmod :: proc(f: ^File, mode: int) -> Error { - if posix.fchmod(__fd(f), transmute(posix.mode_t)posix._mode_t(mode)) != .OK { +_fchmod :: proc(f: ^File, mode: Permissions) -> Error { + if posix.fchmod(__fd(f), transmute(posix.mode_t)posix._mode_t(transmute(u32)mode)) != .OK { return _get_platform_error() } return nil } -_chmod :: proc(name: string, mode: int) -> (err: Error) { +_chmod :: proc(name: string, mode: Permissions) -> (err: Error) { temp_allocator := TEMP_ALLOCATOR_GUARD({}) cname := clone_to_cstring(name, temp_allocator) or_return - if posix.chmod(cname, transmute(posix.mode_t)posix._mode_t(mode)) != .OK { + if posix.chmod(cname, transmute(posix.mode_t)posix._mode_t(transmute(u32)mode)) != .OK { return _get_platform_error() } return nil diff --git a/core/os/os2/file_posix_other.odin b/core/os/os2/file_posix_other.odin index d2946098b..8871a0062 100644 --- a/core/os/os2/file_posix_other.odin +++ b/core/os/os2/file_posix_other.odin @@ -8,7 +8,7 @@ import "core:sys/posix" _posix_absolute_path :: proc(fd: posix.FD, name: string, allocator: runtime.Allocator) -> (path: cstring, err: Error) { temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) - cname := clone_to_cstring(name, temp_allocator) + cname := clone_to_cstring(name, temp_allocator) or_return buf: [posix.PATH_MAX]byte path = posix.realpath(cname, raw_data(buf[:])) diff --git a/core/os/os2/file_stream.odin b/core/os/os2/file_stream.odin index 84176928d..e1c29a792 100644 --- a/core/os/os2/file_stream.odin +++ b/core/os/os2/file_stream.odin @@ -2,6 +2,7 @@ package os2 import "core:io" +// Converts a file `f` into an `io.Stream` to_stream :: proc(f: ^File) -> (s: io.Stream) { if f != nil { assert(f.stream.procedure != nil) @@ -10,14 +11,16 @@ to_stream :: proc(f: ^File) -> (s: io.Stream) { return } +/* + This is an alias of `to_stream` which converts a file `f` to an `io.Stream`. + It can be useful to indicate what the stream is meant to be used for as a writer, + even if it has no logical difference. +*/ to_writer :: to_stream -to_reader :: to_stream - -@(private) -error_to_io_error :: proc(ferr: Error) -> io.Error { - if ferr == nil { - return .None - } - return ferr.(io.Error) or_else .Unknown -} +/* + This is an alias of `to_stream` which converts a file `f` to an `io.Stream`. + It can be useful to indicate what the stream is meant to be used for as a reader, + even if it has no logical difference. +*/ +to_reader :: to_stream diff --git a/core/os/os2/file_util.odin b/core/os/os2/file_util.odin index 5934f02f5..c2cf7c121 100644 --- a/core/os/os2/file_util.odin +++ b/core/os/os2/file_util.odin @@ -4,10 +4,18 @@ import "base:runtime" import "core:strconv" import "core:unicode/utf8" +/* + `write_string` writes a string `s` to file `f`. + Returns the number of bytes written and an error, if any is encountered. +*/ write_string :: proc(f: ^File, s: string) -> (n: int, err: Error) { return write(f, transmute([]byte)s) } +/* + `write_strings` writes a variadic list of strings `strings` to file `f`. + Returns the number of bytes written and an error, if any is encountered. +*/ write_strings :: proc(f: ^File, strings: ..string) -> (n: int, err: Error) { for s in strings { m: int @@ -19,11 +27,18 @@ write_strings :: proc(f: ^File, strings: ..string) -> (n: int, err: Error) { } return } - +/* + `write_byte` writes a byte `b` to file `f`. + Returns the number of bytes written and an error, if any is encountered. +*/ write_byte :: proc(f: ^File, b: byte) -> (n: int, err: Error) { return write(f, []byte{b}) } +/* + `write_rune` writes a rune `r` as an UTF-8 encoded string to file `f`. + Returns the number of bytes written and an error, if any is encountered. +*/ write_rune :: proc(f: ^File, r: rune) -> (n: int, err: Error) { if r < utf8.RUNE_SELF { return write_byte(f, byte(r)) @@ -34,6 +49,10 @@ write_rune :: proc(f: ^File, r: rune) -> (n: int, err: Error) { return write(f, b[:n]) } +/* + `write_encoded_rune` writes a rune `r` as an UTF-8 encoded string which with escaped control codes to file `f`. + Returns the number of bytes written and an error, if any is encountered. +*/ write_encoded_rune :: proc(f: ^File, r: rune) -> (n: int, err: Error) { wrap :: proc(m: int, merr: Error, n: ^int, err: ^Error) -> bool { n^ += m @@ -73,6 +92,31 @@ write_encoded_rune :: proc(f: ^File, r: rune) -> (n: int, err: Error) { return } +/* + `write_ptr` is a utility procedure that writes the bytes points at `data` with length `len`. + + It is equivalent to: `write(f, ([^]byte)(data)[:len])` +*/ +write_ptr :: proc(f: ^File, data: rawptr, len: int) -> (n: int, err: Error) { + return write(f, ([^]byte)(data)[:len]) +} + +/* + `read_ptr` is a utility procedure that reads the bytes points at `data` with length `len`. + + It is equivalent to: `read(f, ([^]byte)(data)[:len])` +*/ +read_ptr :: proc(f: ^File, data: rawptr, len: int) -> (n: int, err: Error) { + return read(f, ([^]byte)(data)[:len]) +} + + + +/* + `read_at_least` reads from `f` into `buf` until it has read at least `min` bytes. + It returns the number of bytes copied and an error if fewer bytes were read. + The error is only an `io.EOF` if no bytes were read. +*/ read_at_least :: proc(f: ^File, buf: []byte, min: int) -> (n: int, err: Error) { if len(buf) < min { return 0, .Short_Buffer @@ -88,17 +132,17 @@ read_at_least :: proc(f: ^File, buf: []byte, min: int) -> (n: int, err: Error) { return } +/* + `read_full` reads exactly `len(buf)` bytes from `f` into `buf`. + It returns the number of bytes copied and an error if fewer bytes were read. + The error is only an `io.EOF` if no bytes were read. + + It is equivalent to `read_at_least(f, buf, len(buf))`. +*/ read_full :: proc(f: ^File, buf: []byte) -> (n: int, err: Error) { return read_at_least(f, buf, len(buf)) } -write_ptr :: proc(f: ^File, data: rawptr, len: int) -> (n: int, err: Error) { - return write(f, ([^]byte)(data)[:len]) -} - -read_ptr :: proc(f: ^File, data: rawptr, len: int) -> (n: int, err: Error) { - return read(f, ([^]byte)(data)[:len]) -} read_entire_file :: proc{ @@ -106,6 +150,10 @@ read_entire_file :: proc{ read_entire_file_from_file, } +/* + `read_entire_file_from_path` reads the entire named file `name` into memory allocated with `allocator`. + A slice of bytes and an error is returned, if any error is encountered. +*/ @(require_results) read_entire_file_from_path :: proc(name: string, allocator: runtime.Allocator, loc := #caller_location) -> (data: []byte, err: Error) { f := open(name) or_return @@ -113,6 +161,10 @@ read_entire_file_from_path :: proc(name: string, allocator: runtime.Allocator, l return read_entire_file_from_file(f, allocator, loc) } +/* + `read_entire_file_from_file` reads the entire file `f` into memory allocated with `allocator`. + A slice of bytes and an error is returned, if any error is encountered. +*/ @(require_results) read_entire_file_from_file :: proc(f: ^File, allocator: runtime.Allocator, loc := #caller_location) -> (data: []byte, err: Error) { size: int @@ -160,8 +212,23 @@ read_entire_file_from_file :: proc(f: ^File, allocator: runtime.Allocator, loc : } } +/* + `write_entire_file` writes the contents of `data` into named file `name`. + It defaults with the permssions `perm := Permissions_Read_All + {.Write_User}`, and `truncate`s by default. + An error is returned if any is encountered. +*/ +write_entire_file :: proc{ + write_entire_file_from_bytes, + write_entire_file_from_string, +} + +/* + `write_entire_file_from_bytes` writes the contents of `data` into named file `name`. + It defaults with the permssions `perm := Permissions_Read_All + {.Write_User}`, and `truncate`s by default. + An error is returned if any is encountered. +*/ @(require_results) -write_entire_file :: proc(name: string, data: []byte, perm: int = 0o644, truncate := true) -> Error { +write_entire_file_from_bytes :: proc(name: string, data: []byte, perm := Permissions_Read_All + {.Write_User}, truncate := true) -> Error { flags := O_WRONLY|O_CREATE if truncate { flags |= O_TRUNC @@ -174,3 +241,14 @@ write_entire_file :: proc(name: string, data: []byte, perm: int = 0o644, truncat return err } + + +/* + `write_entire_file_from_string` writes the contents of `data` into named file `name`. + It defaults with the permssions `perm := Permissions_Read_All + {.Write_User}`, and `truncate`s by default. + An error is returned if any is encountered. +*/ +@(require_results) +write_entire_file_from_string :: proc(name: string, data: string, perm := Permissions_Read_All + {.Write_User}, truncate := true) -> Error { + return write_entire_file(name, transmute([]byte)data, perm, truncate) +} diff --git a/core/os/os2/file_wasi.odin b/core/os/os2/file_wasi.odin index 1d417ffb1..ec464fc52 100644 --- a/core/os/os2/file_wasi.odin +++ b/core/os/os2/file_wasi.odin @@ -51,8 +51,8 @@ init_std_files :: proc "contextless" () { } @(init) -init_preopens :: proc() { - strip_prefixes :: proc(path: string) -> string { +init_preopens :: proc "contextless" () { + strip_prefixes :: proc "contextless" (path: string) -> string { path := path loop: for len(path) > 0 { switch { @@ -69,6 +69,8 @@ init_preopens :: proc() { return path } + context = runtime.default_context() + n: int n_loop: for fd := wasi.fd_t(3); ; fd += 1 { _, err := wasi.fd_prestat_get(fd) @@ -171,7 +173,7 @@ match_preopen :: proc(path: string) -> (wasi.fd_t, string, bool) { return match.fd, relative, true } -_open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) { +_open :: proc(name: string, flags: File_Flags, perm: Permissions) -> (f: ^File, err: Error) { dir_fd, relative, ok := match_preopen(name) if !ok { return nil, .Invalid_Path @@ -373,11 +375,11 @@ _fchdir :: proc(f: ^File) -> Error { return .Unsupported } -_fchmod :: proc(f: ^File, mode: int) -> Error { +_fchmod :: proc(f: ^File, mode: Permissions) -> Error { return .Unsupported } -_chmod :: proc(name: string, mode: int) -> Error { +_chmod :: proc(name: string, mode: Permissions) -> Error { return .Unsupported } diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index b39e65fe2..be5aeb8ab 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -12,7 +12,6 @@ import win32 "core:sys/windows" INVALID_HANDLE :: ~uintptr(0) -S_IWRITE :: 0o200 _ERROR_BAD_NETPATH :: 53 MAX_RW :: 1<<30 @@ -81,7 +80,7 @@ _handle :: proc "contextless" (f: ^File) -> win32.HANDLE { return win32.HANDLE(_fd(f)) } -_open_internal :: proc(name: string, flags: File_Flags, perm: int) -> (handle: uintptr, err: Error) { +_open_internal :: proc(name: string, flags: File_Flags, perm: Permissions) -> (handle: uintptr, err: Error) { if len(name) == 0 { err = .Not_Exist return @@ -122,7 +121,7 @@ _open_internal :: proc(name: string, flags: File_Flags, perm: int) -> (handle: u } attrs: u32 = win32.FILE_ATTRIBUTE_NORMAL|win32.FILE_FLAG_BACKUP_SEMANTICS - if perm & S_IWRITE == 0 { + if .Write_User not_in perm { attrs = win32.FILE_ATTRIBUTE_READONLY if create_mode == win32.CREATE_ALWAYS { // NOTE(bill): Open has just asked to create a file in read-only mode. @@ -150,7 +149,7 @@ _open_internal :: proc(name: string, flags: File_Flags, perm: int) -> (handle: u } -_open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) { +_open :: proc(name: string, flags: File_Flags, perm: Permissions) -> (f: ^File, err: Error) { flags := flags if flags != nil else {.Read} handle := _open_internal(name, flags, perm) or_return return _new_file(handle, name, file_allocator()) @@ -193,7 +192,7 @@ _new_file :: proc(handle: uintptr, name: string, allocator: runtime.Allocator) - @(require_results) -_open_buffered :: proc(name: string, buffer_size: uint, flags := File_Flags{.Read}, perm := 0o777) -> (f: ^File, err: Error) { +_open_buffered :: proc(name: string, buffer_size: uint, flags := File_Flags{.Read}, perm: Permissions) -> (f: ^File, err: Error) { assert(buffer_size > 0) flags := flags if flags != nil else {.Read} handle := _open_internal(name, flags, perm) or_return @@ -744,7 +743,7 @@ _fchdir :: proc(f: ^File) -> Error { return nil } -_fchmod :: proc(f: ^File, mode: int) -> Error { +_fchmod :: proc(f: ^File, mode: Permissions) -> Error { if f == nil || f.impl == nil { return nil } @@ -753,7 +752,7 @@ _fchmod :: proc(f: ^File, mode: int) -> Error { return _get_platform_error() } attrs := d.dwFileAttributes - if mode & S_IWRITE != 0 { + if .Write_User in mode { attrs &~= win32.FILE_ATTRIBUTE_READONLY } else { attrs |= win32.FILE_ATTRIBUTE_READONLY @@ -780,7 +779,7 @@ _chdir :: proc(name: string) -> Error { return nil } -_chmod :: proc(name: string, mode: int) -> Error { +_chmod :: proc(name: string, mode: Permissions) -> Error { f := open(name, {.Write}) or_return defer close(f) return _fchmod(f, mode) diff --git a/core/os/os2/heap.odin b/core/os/os2/heap.odin index 8f9c7680a..b1db54dc7 100644 --- a/core/os/os2/heap.odin +++ b/core/os/os2/heap.odin @@ -2,6 +2,9 @@ package os2 import "base:runtime" +/* + Returns the default `heap_allocator` for this specific platform. +*/ @(require_results) heap_allocator :: proc() -> runtime.Allocator { return runtime.Allocator{ diff --git a/core/os/os2/path_wasi.odin b/core/os/os2/path_wasi.odin index b8240e188..f26e16158 100644 --- a/core/os/os2/path_wasi.odin +++ b/core/os/os2/path_wasi.odin @@ -34,7 +34,7 @@ _mkdir_all :: proc(path: string, perm: int) -> Error { return .Exist } - clean_path := clean_path(path, temp_allocator) + clean_path := clean_path(path, temp_allocator) or_return return internal_mkdir_all(clean_path) internal_mkdir_all :: proc(path: string) -> Error { @@ -114,3 +114,7 @@ _get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err return concatenate({"/", arg}, allocator) } + +_get_absolute_path :: proc(path: string, allocator: runtime.Allocator) -> (absolute_path: string, err: Error) { + return "", .Unsupported +} diff --git a/core/os/os2/process.odin b/core/os/os2/process.odin index 635befc64..def561e28 100644 --- a/core/os/os2/process.odin +++ b/core/os/os2/process.odin @@ -35,7 +35,7 @@ delete_args :: proc "contextless" () { Exit the current process. */ exit :: proc "contextless" (code: int) -> ! { - _exit(code) + runtime.exit(code) } /* diff --git a/core/os/os2/process_linux.odin b/core/os/os2/process_linux.odin index 43ab78bdb..197693dc3 100644 --- a/core/os/os2/process_linux.odin +++ b/core/os/os2/process_linux.odin @@ -14,11 +14,6 @@ import "core:sys/linux" PIDFD_UNASSIGNED :: ~uintptr(0) @(private="package") -_exit :: proc "contextless" (code: int) -> ! { - linux.exit_group(i32(code)) -} - -@(private="package") _get_uid :: proc() -> int { return int(linux.getuid()) } diff --git a/core/os/os2/process_posix.odin b/core/os/os2/process_posix.odin index fcacdf654..a48e44900 100644 --- a/core/os/os2/process_posix.odin +++ b/core/os/os2/process_posix.odin @@ -10,10 +10,6 @@ import "core:strings" import kq "core:sys/kqueue" import "core:sys/posix" -_exit :: proc "contextless" (code: int) -> ! { - posix.exit(i32(code)) -} - _get_uid :: proc() -> int { return int(posix.getuid()) } diff --git a/core/os/os2/process_wasi.odin b/core/os/os2/process_wasi.odin index 9f4d61649..52fdb1680 100644 --- a/core/os/os2/process_wasi.odin +++ b/core/os/os2/process_wasi.odin @@ -4,11 +4,7 @@ package os2 import "base:runtime" import "core:time" -import "core:sys/wasm/wasi" - -_exit :: proc "contextless" (code: int) -> ! { - wasi.proc_exit(wasi.exitcode_t(code)) -} +// import "core:sys/wasm/wasi" _get_uid :: proc() -> int { return 0 diff --git a/core/os/os2/process_windows.odin b/core/os/os2/process_windows.odin index 990da6616..05ac9da93 100644 --- a/core/os/os2/process_windows.odin +++ b/core/os/os2/process_windows.odin @@ -8,11 +8,6 @@ import win32 "core:sys/windows" import "core:time" @(private="package") -_exit :: proc "contextless" (code: int) -> ! { - win32.ExitProcess(u32(code)) -} - -@(private="package") _get_uid :: proc() -> int { return -1 } diff --git a/core/os/os2/stat.odin b/core/os/os2/stat.odin index d6b524684..f87afc4c9 100644 --- a/core/os/os2/stat.odin +++ b/core/os/os2/stat.odin @@ -6,13 +6,16 @@ import "core:time" Fstat_Callback :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) +/* + `File_Info` describes a file and is returned from `stat`, `fstat`, and `lstat`. +*/ File_Info :: struct { - fullpath: string, - name: string, + fullpath: string, // fullpath of the file + name: string, // base name of the file - inode: u128, // might be zero if cannot be determined - size: i64 `fmt:"M"`, - mode: int `fmt:"o"`, + inode: u128, // might be zero if cannot be determined + size: i64 `fmt:"M"`, // length in bytes for regular files; system-dependent for other file types + mode: Permissions, // file permission flags type: File_Type, creation_time: time.Time, @@ -49,6 +52,10 @@ fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) { return {}, .Invalid_Callback } +/* + `stat` returns a `File_Info` describing the named file from the file system. + The resulting `File_Info` must be deleted with `file_info_delete`. +*/ @(require_results) stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { return _stat(name, allocator) @@ -56,12 +63,21 @@ stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { lstat :: stat_do_not_follow_links +/* + Returns a `File_Info` describing the named file from the file system. + If the file is a symbolic link, the `File_Info` returns describes the symbolic link, + rather than following the link. + The resulting `File_Info` must be deleted with `file_info_delete`. +*/ @(require_results) stat_do_not_follow_links :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { return _lstat(name, allocator) } +/* + Returns true if two `File_Info`s are equivalent. +*/ @(require_results) same_file :: proc(fi1, fi2: File_Info) -> bool { return _same_file(fi1, fi2) @@ -71,6 +87,10 @@ same_file :: proc(fi1, fi2: File_Info) -> bool { last_write_time :: modification_time last_write_time_by_name :: modification_time_by_path +/* + Returns the modification time of the file `f`. + The resolution of the timestamp is system-dependent. +*/ @(require_results) modification_time :: proc(f: ^File) -> (time.Time, Error) { temp_allocator := TEMP_ALLOCATOR_GUARD({}) @@ -78,6 +98,10 @@ modification_time :: proc(f: ^File) -> (time.Time, Error) { return fi.modification_time, err } +/* + Returns the modification time of the named file `path`. + The resolution of the timestamp is system-dependent. +*/ @(require_results) modification_time_by_path :: proc(path: string) -> (time.Time, Error) { temp_allocator := TEMP_ALLOCATOR_GUARD({}) diff --git a/core/os/os2/stat_linux.odin b/core/os/os2/stat_linux.odin index 373765be5..6185252cf 100644 --- a/core/os/os2/stat_linux.odin +++ b/core/os/os2/stat_linux.odin @@ -26,7 +26,7 @@ _fstat_internal :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> (fi: File case linux.S_IFREG: type = .Regular case linux.S_IFSOCK: type = .Socket } - mode := int(0o7777 & transmute(u32)s.mode) + mode := transmute(Permissions)(0o7777 & transmute(u32)s.mode) // TODO: As of Linux 4.11, the new statx syscall can retrieve creation_time fi = File_Info { diff --git a/core/os/os2/stat_posix.odin b/core/os/os2/stat_posix.odin index 6ffbdf1da..4ed96b389 100644 --- a/core/os/os2/stat_posix.odin +++ b/core/os/os2/stat_posix.odin @@ -14,7 +14,7 @@ internal_stat :: proc(stat: posix.stat_t, fullpath: string) -> (fi: File_Info) { fi.inode = u128(stat.st_ino) fi.size = i64(stat.st_size) - fi.mode = int(transmute(posix._mode_t)(stat.st_mode - posix.S_IFMT)) + fi.mode = transmute(Permissions)u32(transmute(posix._mode_t)(stat.st_mode - posix.S_IFMT)) fi.type = .Undetermined switch { diff --git a/core/os/os2/stat_windows.odin b/core/os/os2/stat_windows.odin index 3dee42be6..20a708145 100644 --- a/core/os/os2/stat_windows.odin +++ b/core/os/os2/stat_windows.odin @@ -211,11 +211,11 @@ _file_type_from_create_file :: proc(wname: win32.wstring, create_file_attributes return file_type(h) } -_file_type_mode_from_file_attributes :: proc(file_attributes: win32.DWORD, h: win32.HANDLE, ReparseTag: win32.DWORD) -> (type: File_Type, mode: int) { +_file_type_mode_from_file_attributes :: proc(file_attributes: win32.DWORD, h: win32.HANDLE, ReparseTag: win32.DWORD) -> (type: File_Type, mode: Permissions) { if file_attributes & win32.FILE_ATTRIBUTE_READONLY != 0 { - mode |= 0o444 + mode += Permissions_Write_All } else { - mode |= 0o666 + mode += Permissions_Read_Write_All } is_sym := false @@ -229,7 +229,7 @@ _file_type_mode_from_file_attributes :: proc(file_attributes: win32.DWORD, h: wi type = .Symlink } else if file_attributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 { type = .Directory - mode |= 0o111 + mode += Permissions_Execute_All } else if h != nil { type = file_type(h) } diff --git a/core/os/os2/temp_file.odin b/core/os/os2/temp_file.odin index e3e74bd11..f0bc3788e 100644 --- a/core/os/os2/temp_file.odin +++ b/core/os/os2/temp_file.odin @@ -7,7 +7,7 @@ MAX_ATTEMPTS :: 1<<13 // Should be enough for everyone, right? // Creates a new temperatory file in the directory `dir`. // -// Opens the file for reading and writing, with 0o666 permissions, and returns the new `^File`. +// Opens the file for reading and writing, with `Permissions_Read_Write_All` permissions, and returns the new `^File`. // The filename is generated by taking a pattern, and adding a randomized string to the end. // If the pattern includes an "*", the random string replaces the last "*". // If `dir` is an empty string, `temp_directory()` will be used. @@ -26,7 +26,7 @@ create_temp_file :: proc(dir, pattern: string) -> (f: ^File, err: Error) { attempts := 0 for { name := concatenate_strings_from_buffer(name_buf[:], prefix, random_string(rand_buf[:]), suffix) - f, err = open(name, {.Read, .Write, .Create, .Excl}, 0o666) + f, err = open(name, {.Read, .Write, .Create, .Excl}, Permissions_Read_Write_All) if err == .Exist { close(f) attempts += 1 @@ -80,6 +80,19 @@ make_directory_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) } temp_dir :: temp_directory + +/* + Returns the default directory to use for temporary files. + + On Unix systems, it typically returns $TMPDIR if non-empty, otherwlse `/tmp`. + On Windows, it uses `GetTempPathW`, returning the first non-empty value from one of the following: + * `%TMP%` + * `%TEMP%` + * `%USERPROFILE %` + * or the Windows directory + See https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppathw for more information. + On wasi, it returns `/tmp`. +*/ @(require_results) temp_directory :: proc(allocator: runtime.Allocator) -> (string, Error) { return _temp_dir(allocator) diff --git a/core/sys/darwin/Foundation/NSApplication.odin b/core/sys/darwin/Foundation/NSApplication.odin index a5e9f2e24..6cb3df979 100644 --- a/core/sys/darwin/Foundation/NSApplication.odin +++ b/core/sys/darwin/Foundation/NSApplication.odin @@ -132,6 +132,11 @@ Application_finishLaunching :: proc "c" (self: ^Application) { msgSend(nil, self, "finishLaunching") } +@(objc_type=Application, objc_name="stop") +Application_stop :: proc "c" (self: ^Application, sender: ^Object) { + msgSend(nil, self, "stop:", sender) +} + @(objc_type=Application, objc_name="terminate") Application_terminate :: proc "c" (self: ^Application, sender: ^Object) { msgSend(nil, self, "terminate:", sender) @@ -156,6 +161,12 @@ Application_nextEventMatchingMask :: proc "c" (self: ^Application, mask: EventMa Application_sendEvent :: proc "c" (self: ^Application, event: ^Event) { msgSend(nil, self, "sendEvent:", event) } + +@(objc_type=Application, objc_name="postEvent") +Application_postEvent :: proc "c" (self: ^Application, event: ^Event, atStart: BOOL) { + msgSend(nil, self, "postEvent:atStart:", event, atStart) +} + @(objc_type=Application, objc_name="updateWindows") Application_updateWindows :: proc "c" (self: ^Application) { msgSend(nil, self, "updateWindows") @@ -175,6 +186,11 @@ RunningApplication_localizedName :: proc "c" (self: ^RunningApplication) -> ^Str return msgSend(^String, self, "localizedName") } +@(objc_type=RunningApplication, objc_name="finishedLaunching") +RunningApplication_finishedLaunching :: proc "c" (self: ^RunningApplication) -> BOOL { + return msgSend(BOOL, self, "isFinishedLaunching") +} + ApplicationDelegateTemplate :: struct { // Launching Applications applicationWillFinishLaunching: proc(notification: ^Notification), diff --git a/core/sys/darwin/Foundation/NSEvent.odin b/core/sys/darwin/Foundation/NSEvent.odin index 548c5c172..3bd0c1879 100644 --- a/core/sys/darwin/Foundation/NSEvent.odin +++ b/core/sys/darwin/Foundation/NSEvent.odin @@ -250,6 +250,35 @@ kVK :: enum { ISO_Section = 0x0A, } +/* class methods for creating events */ + +@(objc_type=Event, objc_name="otherEventWithType", objc_is_class_method=true) +Event_otherEventWithType :: proc "c" ( + type: EventType, + location: Point, + flags: EventModifierFlags, + time: TimeInterval, + window_number: Integer, + ctx: id, + subtype: i16, + data1: Integer, + data2: Integer, +) -> ^Event { + return msgSend( + ^Event, + Event, + "otherEventWithType:location:modifierFlags:timestamp:windowNumber:context:subtype:data1:data2:", + type, + location, + flags, + time, + window_number, + ctx, + subtype, + data1, + data2, + ) +} /* these messages are valid for all events */ diff --git a/core/sys/darwin/Foundation/NSWindow.odin b/core/sys/darwin/Foundation/NSWindow.odin index e1b027a89..e5bc10074 100644 --- a/core/sys/darwin/Foundation/NSWindow.odin +++ b/core/sys/darwin/Foundation/NSWindow.odin @@ -56,6 +56,60 @@ BackingStoreType :: enum UInteger { Buffered = 2, } +WindowCollectionBehaviorFlag :: enum UInteger { + CanJoinAllSpaces = 0, + MoveToActiveSpace = 1, + Managed = 2, + Transient = 3, + Stationary = 4, + ParticipatesInCycle = 5, + IgnoresCycle = 6, + FullScreenPrimary = 7, + FullScreenAuxiliary = 8, + FullScreenNone = 9, + FullScreenAllowsTiling = 11, + FullScreenDisallowsTiling = 12, + Primary = 16, + Auxiliary = 17, + CanJoinAllApplications = 18, +} +WindowCollectionBehavior :: distinct bit_set[WindowCollectionBehaviorFlag; UInteger] +WindowCollectionBehaviorDefault :: WindowCollectionBehavior{} +WindowCollectionBehaviorPrimary :: WindowCollectionBehavior{.Primary, .FullScreenAuxiliary} +WindowCollectionBehaviorAuxiliary :: WindowCollectionBehavior{.Auxiliary, .FullScreenNone} +WindowCollectionBehaviorCanJoinAllApplications :: WindowCollectionBehavior{.CanJoinAllApplications} +WindowCollectionBehaviorCanJoinAllSpaces :: WindowCollectionBehavior{.CanJoinAllSpaces} +WindowCollectionBehaviorMoveToActiveSpace :: WindowCollectionBehavior{.MoveToActiveSpace} +WindowCollectionBehaviorStationary :: WindowCollectionBehavior{.Stationary} +WindowCollectionBehaviorManaged :: WindowCollectionBehavior{.Managed} +WindowCollectionBehaviorTransient :: WindowCollectionBehavior{.Transient} +WindowCollectionBehaviorFullScreenPrimary :: WindowCollectionBehavior{.FullScreenPrimary} +WindowCollectionBehaviorFullScreenAuxiliary :: WindowCollectionBehavior{.FullScreenAuxiliary} +WindowCollectionBehaviorFullScreenNone :: WindowCollectionBehavior{.FullScreenNone} +WindowCollectionBehaviorFullScreenAllowsTiling :: WindowCollectionBehavior{.FullScreenAllowsTiling} +WindowCollectionBehaviorFullScreenDisallowsTiling :: WindowCollectionBehavior{.FullScreenDisallowsTiling} +WindowCollectionBehaviorParticipatesInCycle :: WindowCollectionBehavior{.ParticipatesInCycle} +WindowCollectionBehaviorIgnoresCycle :: WindowCollectionBehavior{.IgnoresCycle} + +WindowLevel :: enum Integer { + Normal = 0, + Floating = 3, + Submenu = 3, + TornOffMenu = 3, + ModalPanel = 8, + MainMenu = 24, + Status = 25, + PopUpMenu = 101, + ScreenSaver = 1000, +} + +WindowTabbingMode :: enum Integer { + Automatic = 0, + Preferred = 1, + Disallowed = 2, +} + + WindowDelegateTemplate :: struct { // Managing Sheets windowWillPositionSheetUsingRect: proc(window: ^Window, sheet: ^Window, rect: Rect) -> Rect, @@ -600,6 +654,10 @@ Responder :: struct {using _: Object} @(objc_class="NSView") View :: struct {using _: Responder} +@(objc_type=View, objc_name="alloc", objc_is_class_method=true) +View_alloc :: proc "c" () -> ^View { + return msgSend(^View, View, "alloc") +} @(objc_type=View, objc_name="initWithFrame") View_initWithFrame :: proc "c" (self: ^View, frame: Rect) -> ^View { @@ -670,6 +728,10 @@ Window_setFrame :: proc "c" (self: ^Window, frame: Rect, display: BOOL) { Window_setFrameOrigin :: proc "c" (self: ^Window, origin: Point) { msgSend(nil, self, "setFrameOrigin:", origin) } +@(objc_type=Window, objc_name="center") +Window_center :: proc "c" (self: ^Window) { + msgSend(nil, self, "center") +} @(objc_type=Window, objc_name="opaque") Window_opaque :: proc "c" (self: ^Window) -> BOOL { return msgSend(BOOL, self, "opaque") @@ -686,6 +748,14 @@ Window_backgroundColor :: proc "c" (self: ^Window) -> ^Color { Window_setBackgroundColor :: proc "c" (self: ^Window, color: ^Color) { msgSend(nil, self, "setBackgroundColor:", color) } +@(objc_type = Window, objc_name = "orderFront") +Window_orderFront :: proc "c" (self: ^Window, sender: id) { + msgSend(nil, self, "orderFront:", sender) +} +@(objc_type = Window, objc_name = "orderOut") +Window_orderOut :: proc "c" (self: ^Window, sender: id) { + msgSend(nil, self, "orderOut:", sender) +} @(objc_type=Window, objc_name="makeKeyAndOrderFront") Window_makeKeyAndOrderFront :: proc "c" (self: ^Window, key: ^Object) { msgSend(nil, self, "makeKeyAndOrderFront:", key) @@ -722,6 +792,10 @@ Window_close :: proc "c" (self: ^Window) { Window_setDelegate :: proc "c" (self: ^Window, delegate: ^WindowDelegate) { msgSend(nil, self, "setDelegate:", delegate) } +@(objc_type = Window, objc_name = "delegate") +Window_delegate :: proc "c" (self: ^Window) -> ^WindowDelegate { + return msgSend(^WindowDelegate, self, "delegate") +} @(objc_type=Window, objc_name="backingScaleFactor") Window_backingScaleFactor :: proc "c" (self: ^Window) -> Float { return msgSend(Float, self, "backingScaleFactor") @@ -798,3 +872,35 @@ Window_performWindowDragWithEvent :: proc "c" (self: ^Window, event: ^Event) { Window_setToolbar :: proc "c" (self: ^Window, toolbar: ^Toolbar) { msgSend(nil, self, "setToolbar:", toolbar) } +@(objc_type = Window, objc_name = "setCollectionBehavior") +Window_setCollectionBehavior :: proc "c" (self: ^Window, behavior: WindowCollectionBehavior) { + msgSend(nil, self, "setCollectionBehavior:", behavior) +} +@(objc_type = Window, objc_name = "collectionBehavior") +Window_collectionBehavior :: proc "c" (self: ^Window) -> WindowCollectionBehavior { + return msgSend(WindowCollectionBehavior, self, "collectionBehavior") +} +@(objc_type = Window, objc_name = "setLevel") +Window_setLevel :: proc "c" (self: ^Window, level: WindowLevel) { + msgSend(nil, self, "setLevel:", level) +} +@(objc_type = Window, objc_name = "setReleasedWhenClosed") +Window_setReleasedWhenClosed :: proc "c" (self: ^Window, flag: BOOL) { + msgSend(nil, self, "setReleasedWhenClosed:", flag) +} +@(objc_type = Window, objc_name = "makeFirstResponder") +Window_makeFirstResponder :: proc "c" (self: ^Window, responder: ^Responder) -> BOOL { + return msgSend(BOOL, self, "makeFirstResponder:", responder) +} +@(objc_type = Window, objc_name = "setRestorable") +Window_setRestorable :: proc "c" (self: ^Window, flag: BOOL) { + msgSend(nil, self, "setRestorable:", flag) +} +@(objc_type = Window, objc_name = "setTabbingMode") +Window_setTabbingMode :: proc "c" (self: ^Window, mode: WindowTabbingMode) { + msgSend(nil, self, "setTabbingMode:", mode) +} +@(objc_type = Window, objc_name = "toggleFullScreen") +Window_toggleFullScreen :: proc "c" (self: ^Window, sender: id) { + msgSend(nil, self, "toggleFullScreen:", sender) +} diff --git a/core/testing/logging.odin b/core/testing/logging.odin index 428e50afe..3138a745b 100644 --- a/core/testing/logging.odin +++ b/core/testing/logging.odin @@ -83,7 +83,31 @@ format_log_text :: proc(level: runtime.Logger_Level, text: string, options: runt log.do_level_header(options, &buf, level) log.do_time_header(options, &buf, at_time) - log.do_location_header(options, &buf, location) - + when GO_TO_ERROR { + do_go_to_error_friendly_location(options, &buf, location) + } else { + log.do_location_header(options, &buf, location) + } return fmt.aprintf("%s%s", strings.to_string(buf), text, allocator = allocator) } + +do_go_to_error_friendly_location :: proc(opts: log.Options, buf: ^strings.Builder, location := #caller_location) { + if log.Location_Header_Opts & opts == nil { + return + } + fmt.sbprint(buf, "\n") + + file := location.file_path + fmt.sbprint(buf, file) + + fmt.sbprint(buf, "(") + fmt.sbprint(buf, location.line) + fmt.sbprint(buf, ":") + fmt.sbprint(buf, location.column) + fmt.sbprint(buf, ")") + + if .Procedure in opts { + fmt.sbprintf(buf, ":%s()", location.procedure) + } + fmt.sbprint(buf, " ") +}
\ No newline at end of file diff --git a/core/testing/runner.odin b/core/testing/runner.odin index 0897e4fed..9ce4f35cb 100644 --- a/core/testing/runner.odin +++ b/core/testing/runner.odin @@ -63,6 +63,9 @@ LOG_STATE_CHANGES : bool : #config(ODIN_TEST_LOG_STATE_CHANGES, false) USING_SHORT_LOGS : bool : #config(ODIN_TEST_SHORT_LOGS, false) // Output a report of the tests to the given path. JSON_REPORT : string : #config(ODIN_TEST_JSON_REPORT, "") +// Print the full file path for failed test cases on a new line +// in a way that's friendly to regex capture for an editor's "go to error". +GO_TO_ERROR : bool : #config(ODIN_TEST_GO_TO_ERROR, false) get_log_level :: #force_inline proc() -> runtime.Logger_Level { when LOG_LEVEL == "debug" { return .Debug } else @@ -803,7 +806,7 @@ runner :: proc(internal_tests: []Internal_Test) -> bool { } } else { if total_done_count != last_done_count { - if !global_ansi_disabled { + if !(global_ansi_disabled || !FANCY_OUTPUT) { fmt.wprintf(stdout, OSC_WINDOW_TITLE, total_done_count, total_test_count) } last_done_count = total_done_count diff --git a/core/text/scanner/scanner.odin b/core/text/scanner/scanner.odin index 649b4d2d7..9445df58c 100644 --- a/core/text/scanner/scanner.odin +++ b/core/text/scanner/scanner.odin @@ -341,8 +341,10 @@ scan_number :: proc(s: ^Scanner, ch: rune, seen_dot: bool) -> (rune, rune) { case 'x': ch = advance(s) base, prefix = 16, 'x' - case: + case 'o': + ch = advance(s) base, prefix = 8, 'o' + case: digsep = 1 // Leading zero } } else { diff --git a/examples/all/all_main.odin b/examples/all/all_main.odin index 205eb2a3c..cda04278e 100644 --- a/examples/all/all_main.odin +++ b/examples/all/all_main.odin @@ -111,6 +111,7 @@ package all @(require) import "core:prof/spall" @(require) import "core:os" +@(require) import "core:os/os2" @(require) import "core:path/slashpath" @(require) import "core:path/filepath" diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 530efb3c8..b2d28afc0 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -13,6 +13,7 @@ gb_global BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_bool nullptr, // BuiltinProc__type_simple_boolean_begin is_type_boolean, + is_type_bit_field, is_type_integer, is_type_rune, is_type_float, @@ -24,6 +25,7 @@ gb_global BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_bool is_type_cstring16, is_type_typeid, is_type_any, + is_type_endian_platform, is_type_endian_little, is_type_endian_big, @@ -34,8 +36,8 @@ gb_global BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_bool is_type_indexable, is_type_sliceable, is_type_comparable, - is_type_simple_compare, - is_type_nearly_simple_compare, + is_type_simple_compare, // easily compared using memcmp + is_type_nearly_simple_compare, // easily compared using memcmp (including floats) is_type_dereferenceable, is_type_valid_for_keys, is_type_valid_for_matrix_elems, @@ -47,14 +49,12 @@ gb_global BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_bool is_type_enumerated_array, is_type_slice, is_type_dynamic_array, - is_type_map, is_type_struct, is_type_union, is_type_enum, is_type_proc, is_type_bit_set, - is_type_bit_field, is_type_simd_vector, is_type_matrix, is_type_raw_union, @@ -4768,6 +4768,42 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As break; } + case BuiltinProc_constant_floor: + case BuiltinProc_constant_trunc: + case BuiltinProc_constant_ceil: + case BuiltinProc_constant_round: + { + Operand o = {}; + check_expr(c, &o, ce->args[0]); + + if (!is_type_integer_or_float(o.type) && (o.mode != Addressing_Constant)) { + error(ce->args[0], "Expected a constant number for '%.*s'", LIT(builtin_name)); + return false; + } + operand->mode = Addressing_Constant; + operand->type = o.type; + + ExactValue value = o.value; + if (value.kind == ExactValue_Integer) { + // do nothing + } else if (value.kind == ExactValue_Float) { + f64 f = value.value_float; + switch (id) { + case BuiltinProc_constant_floor: f = floor(f); break; + case BuiltinProc_constant_trunc: f = trunc(f); break; + case BuiltinProc_constant_ceil: f = ceil(f); break; + case BuiltinProc_constant_round: f = round(f); break; + default: + GB_PANIC("Unhandled built-in: %.*s", LIT(builtin_name)); + break; + } + value = exact_value_float(f); + } + + operand->value = value; + break; + } + case BuiltinProc_soa_struct: { Operand x = {}; Operand y = {}; @@ -6552,17 +6588,18 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As case BuiltinProc_type_is_boolean: + case BuiltinProc_type_is_bit_field: case BuiltinProc_type_is_integer: case BuiltinProc_type_is_rune: case BuiltinProc_type_is_float: case BuiltinProc_type_is_complex: case BuiltinProc_type_is_quaternion: - case BuiltinProc_type_is_typeid: - case BuiltinProc_type_is_any: case BuiltinProc_type_is_string: case BuiltinProc_type_is_string16: case BuiltinProc_type_is_cstring: case BuiltinProc_type_is_cstring16: + case BuiltinProc_type_is_typeid: + case BuiltinProc_type_is_any: case BuiltinProc_type_is_endian_platform: case BuiltinProc_type_is_endian_little: case BuiltinProc_type_is_endian_big: @@ -6573,8 +6610,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As case BuiltinProc_type_is_indexable: case BuiltinProc_type_is_sliceable: case BuiltinProc_type_is_comparable: - case BuiltinProc_type_is_simple_compare: - case BuiltinProc_type_is_nearly_simple_compare: + case BuiltinProc_type_is_simple_compare: // easily compared using memcmp + case BuiltinProc_type_is_nearly_simple_compare: // easily compared using memcmp (including floats) case BuiltinProc_type_is_dereferenceable: case BuiltinProc_type_is_valid_map_key: case BuiltinProc_type_is_valid_matrix_elements: @@ -6591,7 +6628,6 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As case BuiltinProc_type_is_enum: case BuiltinProc_type_is_proc: case BuiltinProc_type_is_bit_set: - case BuiltinProc_type_is_bit_field: case BuiltinProc_type_is_simd_vector: case BuiltinProc_type_is_matrix: case BuiltinProc_type_is_raw_union: diff --git a/src/check_expr.cpp b/src/check_expr.cpp index e22f12323..8ac277917 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -9200,6 +9200,52 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A return kind; } + +gb_internal void check_expr_as_value_for_ternary(CheckerContext *c, Operand *o, Ast *e, Type *type_hint) { + check_expr_base(c, o, e, type_hint); + check_not_tuple(c, o); + error_operand_no_value(o); + + switch (o->mode) { + case Addressing_Type: { + ERROR_BLOCK(); + gbString expr_str = expr_to_string(o->expr); + defer (gb_string_free(expr_str)); + + error(o->expr, "A type '%s' cannot be used as a runtime value", expr_str); + + error_line("\tSuggestion: If a runtime 'typeid' is wanted, use 'typeid_of' to convert a type\n"); + + o->mode = Addressing_Invalid; + + } break; + + case Addressing_Builtin: { + ERROR_BLOCK(); + gbString expr_str = expr_to_string(o->expr); + defer (gb_string_free(expr_str)); + + error(o->expr, "A built-in procedure '%s' cannot be used as a runtime value", expr_str); + + error_line("\tNote: Built-in procedures are implemented by the compiler and might not be actually instantiated procedures\n"); + + o->mode = Addressing_Invalid; + } break; + + case Addressing_ProcGroup: { + ERROR_BLOCK(); + gbString expr_str = expr_to_string(o->expr); + defer (gb_string_free(expr_str)); + + error(o->expr, "Cannot use overloaded procedure '%s' as a runtime value", expr_str); + + error_line("\tNote: Please specify which procedure in the procedure group to use, via cast or type inference\n"); + + o->mode = Addressing_Invalid; + } break; + } +} + gb_internal ExprKind check_ternary_if_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { ExprKind kind = Expr_Expr; Operand cond = {Addressing_Invalid}; @@ -9213,7 +9259,7 @@ gb_internal ExprKind check_ternary_if_expr(CheckerContext *c, Operand *o, Ast *n Operand x = {Addressing_Invalid}; Operand y = {Addressing_Invalid}; - check_expr_or_type(c, &x, te->x, type_hint); + check_expr_as_value_for_ternary(c, &x, te->x, type_hint); node->viral_state_flags |= te->x->viral_state_flags; if (te->y != nullptr) { @@ -9221,7 +9267,7 @@ gb_internal ExprKind check_ternary_if_expr(CheckerContext *c, Operand *o, Ast *n if (type_hint == nullptr && is_type_typed(x.type)) { th = x.type; } - check_expr_or_type(c, &y, te->y, th); + check_expr_as_value_for_ternary(c, &y, te->y, th); node->viral_state_flags |= te->y->viral_state_flags; } else { error(node, "A ternary expression must have an else clause"); @@ -9248,6 +9294,20 @@ gb_internal ExprKind check_ternary_if_expr(CheckerContext *c, Operand *o, Ast *n return kind; } + if (x.mode == Addressing_Builtin && y.mode == Addressing_Builtin) { + if (type_hint == nullptr) { + error(node, "Built-in procedures cannot be used within a ternary expression since they have no well-defined signature"); + return kind; + } + } + + if (x.mode == Addressing_ProcGroup && y.mode == Addressing_ProcGroup) { + if (type_hint == nullptr) { + error(node, "Procedure groups cannot be used within a ternary expression since they have no well-defined signature that can be inferred without a context"); + return kind; + } + } + // NOTE(bill, 2023-01-30): Allow for expression like this: // x: union{f32} = f32(123) if cond else nil if (type_hint && !is_type_any(type_hint)) { diff --git a/src/check_type.cpp b/src/check_type.cpp index f1a2e9e22..5accfbd9f 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1633,6 +1633,8 @@ gb_internal bool is_expr_from_a_parameter(CheckerContext *ctx, Ast *expr) { } else if (expr->kind == Ast_Ident) { Operand x= {}; Entity *e = check_ident(ctx, &x, expr, nullptr, nullptr, true); + GB_ASSERT(e != nullptr); + if (e->flags & EntityFlag_Param) { return true; } diff --git a/src/checker.cpp b/src/checker.cpp index 8b3638c9d..1daacd9ce 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -923,6 +923,22 @@ gb_internal AstPackage *get_core_package(CheckerInfo *info, String name) { return *found; } + +gb_internal AstPackage *try_get_core_package(CheckerInfo *info, String name) { + if (name == "runtime") { + return get_runtime_package(info); + } + + gbAllocator a = heap_allocator(); + String path = get_fullpath_core_collection(a, name, nullptr); + defer (gb_free(a, path.text)); + auto found = string_map_get(&info->packages, path); + if (found == nullptr) { + return nullptr; + } + return *found; +} + gb_internal void add_package_dependency(CheckerContext *c, char const *package_name, char const *name, bool required=false) { String n = make_string_c(name); AstPackage *p = get_core_package(&c->checker->info, make_string_c(package_name)); diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 01502128a..663274cdc 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -49,6 +49,11 @@ enum BuiltinProcId { BuiltinProc_constant_log2, + BuiltinProc_constant_floor, + BuiltinProc_constant_trunc, + BuiltinProc_constant_ceil, + BuiltinProc_constant_round, + BuiltinProc_transpose, BuiltinProc_outer_product, BuiltinProc_hadamard_product, @@ -420,7 +425,11 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("has_target_feature"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("constant_log2"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("constant_log2"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("constant_floor"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("constant_trunc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("constant_ceil"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("constant_round"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("transpose"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("outer_product"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, @@ -615,17 +624,18 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_boolean"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_bit_field"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_integer"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_rune"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_float"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_complex"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_quaternion"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("type_is_typeid"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("type_is_any"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_string"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_string16"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("type_is_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("type_is_cstring16"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_cstring16"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_typeid"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_any"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_endian_platform"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_endian_little"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, @@ -656,7 +666,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("type_is_enum"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_bit_set"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("type_is_bit_field"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_simd_vector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_matrix"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_raw_union"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 7742fb39d..d6bd7d72d 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2553,6 +2553,8 @@ gb_internal WORKER_TASK_PROC(lb_generate_missing_procedures_to_check_worker_proc } gb_internal void lb_generate_missing_procedures(lbGenerator *gen, bool do_threading) { + isize retry_count = 0; +retry:; if (do_threading) { for (auto const &entry : gen->modules) { lbModule *m = entry.value; @@ -2570,6 +2572,14 @@ gb_internal void lb_generate_missing_procedures(lbGenerator *gen, bool do_thread for (auto const &entry : gen->modules) { lbModule *m = entry.value; + if (m->missing_procedures_to_check.count != 0) { + if (retry_count > gen->modules.count) { + GB_ASSERT(m->missing_procedures_to_check.count == 0); + } + + retry_count += 1; + goto retry; + } GB_ASSERT(m->missing_procedures_to_check.count == 0); GB_ASSERT(m->procedures_to_generate.count == 0); } @@ -2890,7 +2900,18 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star args[0] = lb_addr_load(p, all_tests_slice); lbValue result = lb_emit_call(p, runner, args); - lbValue exit_runner = lb_find_package_value(m, str_lit("os"), str_lit("exit")); + lbValue exit_runner = {}; + { + AstPackage *pkg = get_runtime_package(m->info); + + String name = str_lit("exit"); + Entity *e = scope_lookup_current(pkg->scope, name); + if (e == nullptr) { + compiler_error("Could not find type declaration for '%.*s.%.*s'\n", LIT(pkg->name), LIT(name)); + } + exit_runner = lb_find_value_from_entity(m, e); + } + auto exit_args = array_make<lbValue>(temporary_allocator(), 1); exit_args[0] = lb_emit_select(p, result, lb_const_int(m, t_int, 0), lb_const_int(m, t_int, 1)); lb_emit_call(p, exit_runner, exit_args, ProcInlining_none); diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index a7117fe0b..c014adc05 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -691,11 +691,20 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb } else if (bt->Union.variants.count == 1) { if (value.kind == ExactValue_Compound) { ast_node(cl, CompoundLit, value.value_compound); - if (cl->elems.count == 0 && cl->type == nullptr) { - return lb_const_nil(m, original_type); + if (cl->elems.count == 0) { + if (cl->type == nullptr) { + return lb_const_nil(m, original_type); + } + if (are_types_identical(type_of_expr(cl->type), original_type)) { + return lb_const_nil(m, original_type); + } } } + if (value_type == t_untyped_nil) { + return lb_const_nil(m, original_type); + } + Type *t = bt->Union.variants[0]; lbValue cv = lb_const_value(m, t, value, cc); GB_ASSERT(LLVMIsConstant(cv.value)); @@ -736,6 +745,8 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb } else if (value.kind == ExactValue_Invalid) { return lb_const_nil(m, original_type); } + } else if (value_type == t_untyped_nil) { + return lb_const_nil(m, original_type); } GB_ASSERT_MSG(value_type != nullptr, "%s :: %s", type_to_string(original_type), exact_value_to_string(value)); diff --git a/src/main.cpp b/src/main.cpp index 0af589339..83e7d688c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2296,9 +2296,10 @@ gb_internal void export_linked_libraries(LinkerData *gen) { for (auto *e : gen->foreign_libraries) { GB_ASSERT(e->kind == Entity_LibraryName); + ast_node(imp, ForeignImportDecl, e->LibraryName.decl); - for (auto lib_path : e->LibraryName.paths) { - lib_path = string_trim_whitespace(lib_path); + for (isize i = 0; i < e->LibraryName.paths.count; i++) { + String lib_path = string_trim_whitespace(e->LibraryName.paths[i]); if (lib_path.len == 0) { continue; } @@ -2319,16 +2320,15 @@ gb_internal void export_linked_libraries(LinkerData *gen) { } gb_fprintf(&f, "\t"); - ast_node(imp, ForeignImportDecl, e->LibraryName.decl); - for (Ast* file_path : imp->filepaths) { - GB_ASSERT(file_path->tav.mode == Addressing_Constant && file_path->tav.value.kind == ExactValue_String); - String file_path_str = file_path->tav.value.value_string; - if (string_starts_with(file_path_str, str_lit("system:"))) { - gb_fprintf(&f, "system"); - } else { - gb_fprintf(&f, "user"); - } + Ast *file_path = imp->filepaths[i]; + GB_ASSERT(file_path->tav.mode == Addressing_Constant && file_path->tav.value.kind == ExactValue_String); + String file_path_str = file_path->tav.value.value_string; + + if (string_starts_with(file_path_str, str_lit("system:"))) { + gb_fprintf(&f, "system"); + } else { + gb_fprintf(&f, "user"); } gb_fprintf(&f, "\n"); diff --git a/src/name_canonicalization.cpp b/src/name_canonicalization.cpp index 8bacfabc6..c2acf83f5 100644 --- a/src/name_canonicalization.cpp +++ b/src/name_canonicalization.cpp @@ -417,7 +417,7 @@ gb_internal gbString string_canonical_entity_name(gbAllocator allocator, Entity gb_internal void write_canonical_parent_prefix(TypeWriter *w, Entity *e) { GB_ASSERT(e != nullptr); - if (e->kind == Entity_Procedure || e->kind == Entity_TypeName) { + if (e->kind == Entity_Procedure || e->kind == Entity_TypeName || e->kind == Entity_Variable) { if (e->kind == Entity_Procedure && (e->Procedure.is_export || e->Procedure.is_foreign)) { // no prefix return; @@ -756,6 +756,14 @@ gb_internal void write_type_to_canonical_string(TypeWriter *w, Type *type) { if (i > 0) { type_writer_appendc(w, CANONICAL_FIELD_SEPARATOR); } + + if (f->flags & EntityFlags_IsSubtype) { + type_writer_appendc(w, "#subtype "); + } + + if (f->flags & EntityFlag_Using) { + type_writer_appendc(w, "using "); + } type_writer_append(w, f->token.string.text, f->token.string.len); type_writer_appendc(w, CANONICAL_TYPE_SEPARATOR); write_type_to_canonical_string(w, f->type); diff --git a/src/parser.cpp b/src/parser.cpp index 94c6083f7..152e55f8b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2739,7 +2739,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) { while (allow_token(f, Token_Comma)) { Ast *dummy_name = parse_ident(f); if (!err_once) { - error(dummy_name, "'bit_field' fields do not support multiple names per field"); + syntax_error(dummy_name, "'bit_field' fields do not support multiple names per field"); err_once = true; } } @@ -3299,8 +3299,16 @@ gb_internal Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) { open = expect_token(f, Token_OpenBracket); if (f->curr_token.kind == Token_CloseBracket) { - error(f->curr_token, "Expected an operand, got ]"); + ERROR_BLOCK(); + syntax_error(f->curr_token, "Expected an operand, got ]"); close = expect_token(f, Token_CloseBracket); + + if (f->allow_type) { + gbString s = expr_to_string(operand); + error_line("\tSuggestion: If a type was wanted, did you mean '[]%s'?", s); + gb_string_free(s); + } + operand = ast_index_expr(f, operand, nullptr, open, close); break; } @@ -6594,7 +6602,7 @@ gb_internal bool parse_file_tag(const String &lc, const Token &tok, AstFile *f) } else if (lc == "no-instrumentation") { f->flags |= AstFile_NoInstrumentation; } else { - error(tok, "Unknown tag '%.*s'", LIT(lc)); + syntax_error(tok, "Unknown tag '%.*s'", LIT(lc)); } return true; diff --git a/src/types.cpp b/src/types.cpp index bf668e5f6..a1311ba5d 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1296,6 +1296,15 @@ gb_internal bool is_type_rune(Type *t) { } return false; } +gb_internal bool is_type_integer_or_float(Type *t) { + t = base_type(t); + if (t == nullptr) { return false; } + if (t->kind == Type_Basic) { + return (t->Basic.flags & (BasicFlag_Integer|BasicFlag_Float)) != 0; + } + return false; +} + gb_internal bool is_type_numeric(Type *t) { t = base_type(t); if (t == nullptr) { return false; } diff --git a/tests/core/runtime/test_core_runtime.odin b/tests/core/runtime/test_core_runtime.odin index 881b5f41a..f3559954d 100644 --- a/tests/core/runtime/test_core_runtime.odin +++ b/tests/core/runtime/test_core_runtime.odin @@ -293,6 +293,65 @@ test_soa_array_allocator_resize_overlapping :: proc(t: ^testing.T) { } @(test) +test_soa_array_inject_at_elem :: proc(t: ^testing.T) { + + V :: struct {a: u8, b: f32} + + array := make(#soa[dynamic]V, 0, 2) + defer delete(array) + + append(&array, V{1, 1.5}, V{2, 2.5}, V{3, 3.5}) + + expect_inject(t, &array, 0, {0, 0.5}, {{0, 0.5}, {1, 1.5}, {2, 2.5}, {3, 3.5}}) + expect_inject(t, &array, 2, {5, 5.5}, {{0, 0.5}, {1, 1.5}, {5, 5.5}, {2, 2.5}, {3, 3.5}}) + expect_inject(t, &array, 5, {9, 9.5}, {{0, 0.5}, {1, 1.5}, {5, 5.5}, {2, 2.5}, {3, 3.5}, {9, 9.5}}) + + expect_inject :: proc(t: ^testing.T, arr: ^#soa[dynamic]V, index: int, arg: V, expected_slice: []V) { + ok, err := inject_at_soa(arr, index, arg) + testing.expectf(t, ok == true, "Injection of %v at index %d failed", arg, index) + testing.expectf(t, err == nil, "Injection allocation of %v at index %d failed: %v", arg, index, err) + equals := len(arr) == len(expected_slice) + for e, i in expected_slice { + if arr[i] != e { + equals = false + break + } + } + testing.expectf(t, equals, "After injection of %v at index %d, expected array to be\n&%v, got\n%v", arg, index, expected_slice, arr) + } +} + +@(test) +test_soa_array_inject_at_elems :: proc(t: ^testing.T) { + + V :: struct {a: u8, b: f32} + + array := make(#soa[dynamic]V, 0, 2) + defer delete(array) + + append(&array, V{1, 1.5}, V{2, 2.5}, V{3, 3.5}) + + expect_inject(t, &array, 0, {{0, 0.5}}, {{0, 0.5}, {1, 1.5}, {2, 2.5}, {3, 3.5}}) + expect_inject(t, &array, 2, {{5, 5.5}, {6, 6.5}}, {{0, 0.5}, {1, 1.5}, {5, 5.5}, {6, 6.5}, {2, 2.5}, {3, 3.5}}) + expect_inject(t, &array, 6, {{9, 9.5}, {10, 10.5}}, {{0, 0.5}, {1, 1.5}, {5, 5.5}, {6, 6.5}, {2, 2.5}, {3, 3.5}, {9, 9.5}, {10, 10.5}}) + expect_inject(t, &array, 6, {}, {{0, 0.5}, {1, 1.5}, {5, 5.5}, {6, 6.5}, {2, 2.5}, {3, 3.5}, {9, 9.5}, {10, 10.5}}) + + expect_inject :: proc(t: ^testing.T, arr: ^#soa[dynamic]V, index: int, args: []V, expected_slice: []V) { + ok, err := inject_at_soa(arr, index, ..args) + testing.expectf(t, ok == true, "Injection of %v at index %d failed", args, index) + testing.expectf(t, err == nil, "Injection allocation of %v at index %d failed: %v", args, index, err) + equals := len(arr) == len(expected_slice) + for e, i in expected_slice { + if arr[i] != e { + equals = false + break + } + } + testing.expectf(t, equals, "After injection of %v at index %d, expected array to be\n&%v, got\n%v", args, index, expected_slice, arr) + } +} + +@(test) test_memory_equal :: proc(t: ^testing.T) { data: [256]u8 cmp: [256]u8 diff --git a/vendor/raylib/raylib.odin b/vendor/raylib/raylib.odin index 7dae19f4b..37589f165 100644 --- a/vendor/raylib/raylib.odin +++ b/vendor/raylib/raylib.odin @@ -96,6 +96,7 @@ _ :: linalg MAX_TEXTFORMAT_BUFFERS :: #config(RAYLIB_MAX_TEXTFORMAT_BUFFERS, 4) MAX_TEXT_BUFFER_LENGTH :: #config(RAYLIB_MAX_TEXT_BUFFER_LENGTH, 1024) +MAX_MATERIAL_MAPS :: #config(RAYLIB_MAX_MATERIAL_MAPS, 12) #assert(size_of(rune) == size_of(c.int)) |