diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 41f548119..1a1d18231 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -6,7 +6,7 @@ jobs:
name: NetBSD Build, Check, and Test
runs-on: ubuntu-latest
env:
- PKGSRC_BRANCH: 2024Q2
+ PKGSRC_BRANCH: 2024Q3
steps:
- uses: actions/checkout@v4
- name: Build, Check, and Test
@@ -32,6 +32,8 @@ jobs:
gmake -C vendor/miniaudio/src
./odin check examples/all -vet -strict-style -disallow-do -target:netbsd_amd64
./odin check examples/all -vet -strict-style -disallow-do -target:netbsd_arm64
+ ./odin check vendor/sdl3 -vet -strict-style -disallow-do -target:netbsd_amd64 -no-entry-point
+ ./odin check vendor/sdl3 -vet -strict-style -disallow-do -target:netbsd_arm64 -no-entry-point
./odin test tests/core/normal.odin -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
./odin test tests/core/speed.odin -file -all-packages -vet -strict-style -disallow-do -o:speed -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
./odin test tests/vendor -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
@@ -50,7 +52,7 @@ jobs:
usesh: true
copyback: false
prepare: |
- pkg install -y gmake git bash python3 libxml2 llvm17
+ pkg install -y gmake git bash python3 libxml2 llvm18
run: |
# `set -e` is needed for test failures to register. https://github.com/vmactions/freebsd-vm/issues/72
set -e -x
@@ -62,6 +64,7 @@ jobs:
gmake -C vendor/cgltf/src
gmake -C vendor/miniaudio/src
./odin check examples/all -vet -strict-style -disallow-do -target:freebsd_amd64
+ ./odin check vendor/sdl3 -vet -strict-style -disallow-do -target:freebsd_amd64 -no-entry-point
./odin test tests/core/normal.odin -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
./odin test tests/core/speed.odin -file -all-packages -vet -strict-style -disallow-do -o:speed -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
./odin test tests/vendor -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
@@ -84,20 +87,20 @@ jobs:
run: |
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
- sudo ./llvm.sh 17
- echo "/usr/lib/llvm-17/bin" >> $GITHUB_PATH
+ sudo ./llvm.sh 20
+ echo "/usr/lib/llvm-20/bin" >> $GITHUB_PATH
- name: Download LLVM (MacOS Intel)
if: matrix.os == 'macos-13'
run: |
- brew install llvm@17 lua@5.4
- echo "/usr/local/opt/llvm@17/bin" >> $GITHUB_PATH
+ brew update
+ brew install llvm@20 lua@5.4 lld
- name: Download LLVM (MacOS ARM)
if: matrix.os == 'macos-14'
run: |
- brew install llvm@17 wasmtime lua@5.4
- echo "/opt/homebrew/opt/llvm@17/bin" >> $GITHUB_PATH
+ brew update
+ brew install llvm@20 wasmtime lua@5.4 lld
- name: Build Odin
run: ./build_odin.sh release
@@ -117,7 +120,9 @@ jobs:
- name: Odin run -debug
run: ./odin run examples/demo -debug
- name: Odin check examples/all
- run: ./odin check examples/all -strict-style
+ run: ./odin check examples/all -strict-style -vet -disallow-do
+ - name: Odin check vendor/sdl3
+ run: ./odin check vendor/sdl3 -strict-style -vet -disallow-do -no-entry-point
- name: Normal Core library tests
run: ./odin test tests/core/normal.odin -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
- name: Optimized Core library tests
@@ -146,9 +151,23 @@ jobs:
run: ./odin check examples/all -vet -strict-style -disallow-do -target:openbsd_amd64
if: matrix.os == 'ubuntu-latest'
+ - name: Odin check vendor/sdl3 for Linux i386
+ run: ./odin check vendor/sdl3 -vet -strict-style -disallow-do -no-entry-point -target:linux_i386
+ if: matrix.os == 'ubuntu-latest'
+ - name: Odin check vendor/sdl3 for Linux arm64
+ run: ./odin check vendor/sdl3 -vet -strict-style -disallow-do -no-entry-point -target:linux_arm64
+ if: matrix.os == 'ubuntu-latest'
+ - name: Odin check vendor/sdl3 for FreeBSD amd64
+ run: ./odin check vendor/sdl3 -vet -strict-style -disallow-do -no-entry-point -target:freebsd_amd64
+ if: matrix.os == 'ubuntu-latest'
+ - name: Odin check vendor/sdl3 for OpenBSD amd64
+ run: ./odin check vendor/sdl3 -vet -strict-style -disallow-do -no-entry-point -target:openbsd_amd64
+ if: matrix.os == 'ubuntu-latest'
+
+
- name: Run demo on WASI WASM32
run: |
- ./odin build examples/demo -target:wasi_wasm32 -vet -strict-style -disallow-do -out:demo.wasm
+ ./odin build examples/demo -target:wasi_wasm32 -vet -strict-style -disallow-do -out:demo
wasmtime ./demo.wasm
if: matrix.os == 'macos-14'
@@ -187,6 +206,11 @@ jobs:
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
odin check examples/all -vet -strict-style -disallow-do
+ - name: Odin check vendor/sdl3
+ shell: cmd
+ run: |
+ call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
+ odin check vendor/sdl3 -vet -strict-style -disallow-do -no-entry-point
- name: Core library tests
shell: cmd
run: |
@@ -208,6 +232,12 @@ jobs:
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
odin test tests/internal -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
+ - name: Check issues
+ shell: cmd
+ run: |
+ call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
+ cd tests/issues
+ call run.bat
- name: Check benchmarks
shell: cmd
run: |
@@ -260,9 +290,12 @@ jobs:
make -C vendor/cgltf/src
make -C vendor/miniaudio/src
- - name: Odin check
+ - name: Odin check examples/all
run: ./odin check examples/all -target:linux_riscv64 -vet -strict-style -disallow-do
+ - name: Odin check vendor/sdl3
+ run: ./odin check vendor/sdl3 -target:linux_riscv64 -vet -strict-style -disallow-do -no-entry-point
+
- name: Install riscv64 toolchain and qemu
run: sudo apt-get install -y qemu-user qemu-user-static gcc-12-riscv64-linux-gnu libc6-riscv64-cross
diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml
index 314711efb..eb67eb209 100644
--- a/.github/workflows/nightly.yml
+++ b/.github/workflows/nightly.yml
@@ -49,12 +49,12 @@ jobs:
- uses: actions/checkout@v4
- uses: jirutka/setup-alpine@v1
with:
- branch: v3.20
+ branch: edge
- name: (Linux) Download LLVM
run: |
apk add --no-cache \
- musl-dev llvm18-dev clang18 git mold lz4 \
- libxml2-static llvm18-static zlib-static zstd-static \
+ musl-dev llvm20-dev clang20 git mold lz4 \
+ libxml2-static llvm20-static zlib-static zstd-static \
make
shell: alpine.sh --root {0}
- name: build odin
@@ -93,8 +93,9 @@ jobs:
- uses: actions/checkout@v4
- name: Download LLVM and setup PATH
run: |
- brew install llvm@18 dylibbundler
- echo "/usr/local/opt/llvm@18/bin" >> $GITHUB_PATH
+ brew update
+ brew install llvm@20 dylibbundler lld
+
- name: build odin
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
# not link with libunwind bundled with LLVM but link with libunwind on the system.
@@ -130,8 +131,9 @@ jobs:
- uses: actions/checkout@v4
- name: Download LLVM and setup PATH
run: |
- brew install llvm@18 dylibbundler
- echo "/opt/homebrew/opt/llvm@18/bin" >> $GITHUB_PATH
+ brew update
+ brew install llvm@20 dylibbundler lld
+
- name: build odin
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
# not link with libunwind bundled with LLVM but link with libunwind on the system.
diff --git a/LLVM-C.dll b/LLVM-C.dll
index ee03a2acd..b2f22ba8e 100644
Binary files a/LLVM-C.dll and b/LLVM-C.dll differ
diff --git a/README.md b/README.md
index b4bb6bac7..5e48f4168 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@
-
+
@@ -76,6 +76,10 @@ Answers to common questions about Odin.
Documentation for all the official packages part of the [core](https://pkg.odin-lang.org/core/) and [vendor](https://pkg.odin-lang.org/vendor/) library collections.
+#### [Examples](https://github.com/odin-lang/examples)
+
+Examples on how to write idiomatic Odin code. Shows how to accomplish specific tasks in Odin, as well as how to use packages from `core` and `vendor`.
+
#### [Odin Documentation](https://odin-lang.org/docs/)
Documentation for the Odin language itself.
diff --git a/base/builtin/builtin.odin b/base/builtin/builtin.odin
index c4a9b141f..227ceeb49 100644
--- a/base/builtin/builtin.odin
+++ b/base/builtin/builtin.odin
@@ -1,6 +1,8 @@
// This is purely for documentation
package builtin
+import "base:runtime"
+
nil :: nil
false :: 0!=0
true :: 0==0
diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin
index 744a899c0..bec452007 100644
--- a/base/intrinsics/intrinsics.odin
+++ b/base/intrinsics/intrinsics.odin
@@ -2,6 +2,8 @@
#+build ignore
package intrinsics
+import "base:runtime"
+
// Package-Related
is_package_imported :: proc(package_name: string) -> bool ---
@@ -72,7 +74,7 @@ prefetch_write_instruction :: proc(address: rawptr, #const locality: i32 /* 0..=
prefetch_write_data :: proc(address: rawptr, #const locality: i32 /* 0..=3 */) ---
// Compiler Hints
-expect :: proc(val, expected_val: T) -> T ---
+expect :: proc(val, expected_val: $T) -> T ---
// Linux and Darwin Only
syscall :: proc(id: uintptr, args: ..uintptr) -> uintptr ---
@@ -219,7 +221,7 @@ type_map_cell_info :: proc($T: typeid) -> ^runtime.Map_Cell_Info ---
type_convert_variants_to_pointers :: proc($T: typeid) -> typeid where type_is_union(T) ---
type_merge :: proc($U, $V: typeid) -> typeid where type_is_union(U), type_is_union(V) ---
-type_has_shared_fields :: proc($U, $V: typeid) -> bool typeid where type_is_struct(U), type_is_struct(V) ---
+type_has_shared_fields :: proc($U, $V: typeid) -> bool where type_is_struct(U), type_is_struct(V) ---
constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
@@ -283,6 +285,9 @@ simd_reduce_xor :: proc(a: #simd[N]T) -> T where type_is_integer(T) || t
simd_reduce_any :: proc(a: #simd[N]T) -> T where type_is_boolean(T) ---
simd_reduce_all :: proc(a: #simd[N]T) -> T where type_is_boolean(T) ---
+simd_extract_lsbs :: proc(a: #simd[N]T) -> bit_set[0.. bit_set[0.. #simd[N]T where type_is_integer(U) || type_is_boolean(U) ---
simd_scatter :: proc(ptr: #simd[N]rawptr, val: #simd[N]T, mask: #simd[N]U) where type_is_integer(U) || type_is_boolean(U) ---
diff --git a/base/runtime/core.odin b/base/runtime/core.odin
index e47f3ecbc..1dadbbf6f 100644
--- a/base/runtime/core.odin
+++ b/base/runtime/core.odin
@@ -110,7 +110,6 @@ Type_Info_Parameters :: struct { // Only used for procedures parameters and resu
types: []^Type_Info,
names: []string,
}
-Type_Info_Tuple :: Type_Info_Parameters // Will be removed eventually
Type_Info_Struct_Flags :: distinct bit_set[Type_Info_Struct_Flag; u8]
Type_Info_Struct_Flag :: enum u8 {
@@ -239,47 +238,6 @@ Type_Info :: struct {
},
}
-// NOTE(bill): This must match the compiler's
-Typeid_Kind :: enum u8 {
- Invalid,
- Integer,
- Rune,
- Float,
- Complex,
- Quaternion,
- String,
- Boolean,
- Any,
- Type_Id,
- Pointer,
- Multi_Pointer,
- Procedure,
- Array,
- Enumerated_Array,
- Dynamic_Array,
- Slice,
- Tuple,
- Struct,
- Union,
- Enum,
- Map,
- Bit_Set,
- Simd_Vector,
- Matrix,
- Soa_Pointer,
- Bit_Field,
-}
-#assert(len(Typeid_Kind) < 32)
-
-Typeid_Bit_Field :: bit_field uintptr {
- index: uintptr | 8*size_of(uintptr) - 8,
- kind: Typeid_Kind | 5, // Typeid_Kind
- named: bool | 1,
- special: bool | 1, // signed, cstring, etc
- reserved: bool | 1,
-}
-#assert(size_of(Typeid_Bit_Field) == size_of(uintptr))
-
// NOTE(bill): only the ones that are needed (not all types)
// This will be set by the compiler
type_table: []^Type_Info
@@ -483,10 +441,14 @@ Raw_Any :: struct {
data: rawptr,
id: typeid,
}
+when !ODIN_NO_RTTI {
+ #assert(size_of(Raw_Any) == size_of(any))
+}
Raw_Cstring :: struct {
data: [^]byte,
}
+#assert(size_of(Raw_Cstring) == size_of(cstring))
Raw_Soa_Pointer :: struct {
data: rawptr,
@@ -596,10 +558,14 @@ ALL_ODIN_OS_TYPES :: Odin_OS_Types{
Odin_Platform_Subtarget_Type :: enum int {
Default,
iOS,
+ Android,
}
*/
Odin_Platform_Subtarget_Type :: type_of(ODIN_PLATFORM_SUBTARGET)
+Odin_Platform_Subtarget_Types :: bit_set[Odin_Platform_Subtarget_Type]
+
+
/*
// Defined internally by the compiler
Odin_Sanitizer_Flag :: enum u32 {
@@ -686,13 +652,16 @@ type_info_core :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
type_info_base_without_enum :: type_info_core
__type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info #no_bounds_check {
- MASK :: 1<<(8*size_of(typeid) - 8) - 1
- data := transmute(uintptr)id
- n := int(data & MASK)
- if n < 0 || n >= len(type_table) {
- n = 0
+ n := u64(len(type_table))
+ i := transmute(u64)id % n
+ for _ in 0.. (key_ptr: ^K, value_ptr: ^V, just_inserted: bool, err: Allocator_Error) {
+ key := key
+ zero: V
+
+ _key_ptr, _value_ptr: rawptr
+ _key_ptr, _value_ptr, just_inserted, err = __dynamic_map_entry((^Raw_Map)(m), map_info(T), &key, &zero, loc)
+
+ key_ptr = (^K)(_key_ptr)
+ value_ptr = (^V)(_value_ptr)
+ return
+}
+
@builtin
card :: proc "contextless" (s: $S/bit_set[$E; $U]) -> int {
@@ -964,6 +992,24 @@ assert :: proc(condition: bool, message := #caller_expression(condition), loc :=
}
}
+// Evaluates the condition and aborts the program iff the condition is
+// false. This routine ignores `ODIN_DISABLE_ASSERT`, and will always
+// execute.
+@builtin
+ensure :: proc(condition: bool, message := #caller_expression(condition), loc := #caller_location) {
+ if !condition {
+ @(cold)
+ internal :: proc(message: string, loc: Source_Code_Location) {
+ p := context.assertion_failure_proc
+ if p == nil {
+ p = default_assertion_failure_proc
+ }
+ p("unsatisfied ensure", message, loc)
+ }
+ internal(message, loc)
+ }
+}
+
@builtin
panic :: proc(message: string, loc := #caller_location) -> ! {
p := context.assertion_failure_proc
@@ -999,6 +1045,17 @@ assert_contextless :: proc "contextless" (condition: bool, message := #caller_ex
}
}
+@builtin
+ensure_contextless :: proc "contextless" (condition: bool, message := #caller_expression(condition), loc := #caller_location) {
+ if !condition {
+ @(cold)
+ internal :: proc "contextless" (message: string, loc: Source_Code_Location) {
+ default_assertion_contextless_failure_proc("unsatisfied ensure", message, loc)
+ }
+ internal(message, loc)
+ }
+}
+
@builtin
panic_contextless :: proc "contextless" (message: string, loc := #caller_location) -> ! {
default_assertion_contextless_failure_proc("panic", message, loc)
diff --git a/base/runtime/core_builtin_soa.odin b/base/runtime/core_builtin_soa.odin
index 2980b7a9e..ff27a4559 100644
--- a/base/runtime/core_builtin_soa.odin
+++ b/base/runtime/core_builtin_soa.odin
@@ -142,6 +142,7 @@ make_soa_slice :: proc($T: typeid/#soa[]$E, #any_int length: int, allocator := c
@(builtin, require_results)
make_soa_dynamic_array :: proc($T: typeid/#soa[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
context.allocator = allocator
+ array.allocator = allocator
reserve_soa(&array, 0, loc) or_return
return array, nil
}
@@ -149,6 +150,7 @@ make_soa_dynamic_array :: proc($T: typeid/#soa[dynamic]$E, allocator := context.
@(builtin, require_results)
make_soa_dynamic_array_len :: proc($T: typeid/#soa[dynamic]$E, #any_int length: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
context.allocator = allocator
+ array.allocator = allocator
resize_soa(&array, length, loc) or_return
return array, nil
}
diff --git a/base/runtime/default_temp_allocator_arena.odin b/base/runtime/default_temp_allocator_arena.odin
index db157b267..5f25dac95 100644
--- a/base/runtime/default_temp_allocator_arena.odin
+++ b/base/runtime/default_temp_allocator_arena.odin
@@ -104,13 +104,15 @@ arena_alloc :: proc(arena: ^Arena, size, alignment: uint, loc := #caller_locatio
if size == 0 {
return
}
-
- needed := align_forward_uint(size, alignment)
- if arena.curr_block == nil || (safe_add(arena.curr_block.used, needed) or_else 0) > arena.curr_block.capacity {
+
+ prev_used := 0 if arena.curr_block == nil else arena.curr_block.used
+ data, err = alloc_from_memory_block(arena.curr_block, size, alignment)
+ if err == .Out_Of_Memory {
if arena.minimum_block_size == 0 {
arena.minimum_block_size = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE
}
+ needed := align_forward_uint(size, alignment)
block_size := max(needed, arena.minimum_block_size)
if arena.backing_allocator.procedure == nil {
@@ -121,10 +123,9 @@ arena_alloc :: proc(arena: ^Arena, size, alignment: uint, loc := #caller_locatio
new_block.prev = arena.curr_block
arena.curr_block = new_block
arena.total_capacity += new_block.capacity
+ prev_used = 0
+ data, err = alloc_from_memory_block(arena.curr_block, size, alignment)
}
-
- prev_used := arena.curr_block.used
- data, err = alloc_from_memory_block(arena.curr_block, size, alignment)
arena.total_used += arena.curr_block.used - prev_used
return
}
@@ -210,10 +211,24 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
case size == 0:
err = .Mode_Not_Implemented
return
- case (uintptr(old_data) & uintptr(alignment-1) == 0) && size < old_size:
- // shrink data in-place
- data = old_data[:size]
- return
+ case uintptr(old_data) & uintptr(alignment-1) == 0:
+ if size < old_size {
+ // shrink data in-place
+ data = old_data[:size]
+ return
+ }
+
+ if block := arena.curr_block; block != nil {
+ start := uint(uintptr(old_memory)) - uint(uintptr(block.base))
+ old_end := start + old_size
+ new_end := start + size
+ if start < old_end && old_end == block.used && new_end <= block.capacity {
+ // grow data in-place, adjusting next allocation
+ block.used = uint(new_end)
+ data = block.base[start:new_end]
+ return
+ }
+ }
}
new_memory := arena_alloc(arena, size, alignment, location) or_return
@@ -282,9 +297,10 @@ arena_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) {
if block := arena.curr_block; block != nil {
assert(block.used >= temp.used, "out of order use of arena_temp_end", loc)
- amount_to_zero := min(block.used-temp.used, block.capacity-block.used)
+ amount_to_zero := block.used-temp.used
intrinsics.mem_zero(block.base[temp.used:], amount_to_zero)
block.used = temp.used
+ arena.total_used -= amount_to_zero
}
}
diff --git a/base/runtime/dynamic_map_internal.odin b/base/runtime/dynamic_map_internal.odin
index 3dded7716..7b65a2fa0 100644
--- a/base/runtime/dynamic_map_internal.odin
+++ b/base/runtime/dynamic_map_internal.odin
@@ -158,21 +158,21 @@ map_cell_index_static :: #force_inline proc "contextless" (cells: [^]Map_Cell($T
} else when (N & (N - 1)) == 0 && N <= 8*size_of(uintptr) {
// Likely case, N is a power of two because T is a power of two.
+ // Unique case, no need to index data here since only one element.
+ when N == 1 {
+ return &cells[index].data[0]
+ }
+
// Compute the integer log 2 of N, this is the shift amount to index the
// correct cell. Odin's intrinsics.count_leading_zeros does not produce a
// constant, hence this approach. We only need to check up to N = 64.
- SHIFT :: 1 when N < 2 else
- 2 when N < 4 else
- 3 when N < 8 else
- 4 when N < 16 else
- 5 when N < 32 else 6
+ SHIFT :: 1 when N == 2 else
+ 2 when N == 4 else
+ 3 when N == 8 else
+ 4 when N == 16 else
+ 5 when N == 32 else 6
#assert(SHIFT <= MAP_CACHE_LINE_LOG2)
- // Unique case, no need to index data here since only one element.
- when N == 1 {
- return &cells[index >> SHIFT].data[0]
- } else {
- return &cells[index >> SHIFT].data[index & (N - 1)]
- }
+ return &cells[index >> SHIFT].data[index & (N - 1)]
} else {
// Least likely (and worst case), we pay for a division operation but we
// assume the compiler does not actually generate a division. N will be in the
@@ -400,7 +400,7 @@ map_alloc_dynamic :: proc "odin" (info: ^Map_Info, log2_capacity: uintptr, alloc
// This procedure returns the address of the just inserted value, and will
// return 'nil' if there was no room to insert the entry
@(require_results)
-map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, h: Map_Hash, ik: uintptr, iv: uintptr) -> (result: uintptr) {
+map_insert_hash_dynamic_with_key :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, h: Map_Hash, ik: uintptr, iv: uintptr) -> (key: uintptr, result: uintptr) {
h := h
pos := map_desired_position(m^, h)
distance := uintptr(0)
@@ -436,7 +436,11 @@ map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^
intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v), size_of_v)
hs[pos] = h
- return result if result != 0 else v_dst
+ if result == 0 {
+ key = k_dst
+ result = v_dst
+ }
+ return
}
if map_hash_is_deleted(element_hash) {
@@ -444,13 +448,14 @@ map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^
}
if probe_distance := map_probe_distance(m^, element_hash, pos); distance > probe_distance {
- if result == 0 {
- result = map_cell_index_dynamic(vs, info.vs, pos)
- }
-
kp := map_cell_index_dynamic(ks, info.ks, pos)
vp := map_cell_index_dynamic(vs, info.vs, pos)
+ if result == 0 {
+ key = kp
+ result = vp
+ }
+
intrinsics.mem_copy_non_overlapping(rawptr(tk), rawptr(k), size_of_k)
intrinsics.mem_copy_non_overlapping(rawptr(k), rawptr(kp), size_of_k)
intrinsics.mem_copy_non_overlapping(rawptr(kp), rawptr(tk), size_of_k)
@@ -491,7 +496,11 @@ map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^
intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v), size_of_v)
hs[pos] = h
- return result if result != 0 else v_dst
+ if result == 0 {
+ key = k_dst
+ result = v_dst
+ }
+ return
}
k_src := map_cell_index_dynamic(ks, info.ks, la_pos)
@@ -501,6 +510,7 @@ map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^
if probe_distance < look_ahead {
// probed can be made ideal while placing saved (ending condition)
if result == 0 {
+ key = k_dst
result = v_dst
}
intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k), size_of_k)
@@ -550,6 +560,7 @@ map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^
} else {
// place saved, save probed
if result == 0 {
+ key = k_dst
result = v_dst
}
intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k), size_of_k)
@@ -568,6 +579,12 @@ map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^
}
}
+@(require_results)
+map_insert_hash_dynamic :: #force_inline proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, h: Map_Hash, ik: uintptr, iv: uintptr) -> (result: uintptr) {
+ _, result = map_insert_hash_dynamic_with_key(m, info, h, ik, iv)
+ return
+}
+
@(require_results)
map_grow_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, loc := #caller_location) -> Allocator_Error {
log2_capacity := map_log2_cap(m^)
@@ -941,6 +958,29 @@ __dynamic_map_set_extra :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^
return nil, rawptr(result)
}
+__dynamic_map_entry :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, key: rawptr, zero: rawptr, loc := #caller_location) -> (key_ptr: rawptr, value_ptr: rawptr, just_inserted: bool, err: Allocator_Error) {
+ hash := info.key_hasher(key, map_seed(m^))
+
+ if key_ptr, value_ptr = __dynamic_map_get_key_and_value(m, info, hash, key); value_ptr != nil {
+ return
+ }
+
+ has_grown: bool
+ if err, has_grown = __dynamic_map_check_grow(m, info, loc); err != nil {
+ return
+ } else if has_grown {
+ hash = info.key_hasher(key, map_seed(m^))
+ }
+
+ kp, vp := map_insert_hash_dynamic_with_key(m, info, hash, uintptr(key), uintptr(zero))
+ key_ptr = rawptr(kp)
+ value_ptr = rawptr(vp)
+
+ m.len += 1
+ just_inserted = true
+ return
+}
+
// IMPORTANT: USED WITHIN THE COMPILER
@(private)
@@ -989,3 +1029,32 @@ default_hasher_cstring :: proc "contextless" (data: rawptr, seed: uintptr) -> ui
h &= HASH_MASK
return uintptr(h) | uintptr(uintptr(h) == 0)
}
+
+default_hasher_f64 :: proc "contextless" (f: f64, seed: uintptr) -> uintptr {
+ f := f
+ buf: [size_of(f)]u8
+ if f == 0 {
+ return default_hasher(&buf, seed, size_of(buf))
+ }
+ if f != f {
+ // TODO(bill): What should the logic be for NaNs?
+ return default_hasher(&f, seed, size_of(f))
+ }
+ return default_hasher(&f, seed, size_of(f))
+}
+
+default_hasher_complex128 :: proc "contextless" (x, y: f64, seed: uintptr) -> uintptr {
+ seed := seed
+ seed = default_hasher_f64(x, seed)
+ seed = default_hasher_f64(y, seed)
+ return seed
+}
+
+default_hasher_quaternion256 :: proc "contextless" (x, y, z, w: f64, seed: uintptr) -> uintptr {
+ seed := seed
+ seed = default_hasher_f64(x, seed)
+ seed = default_hasher_f64(y, seed)
+ seed = default_hasher_f64(z, seed)
+ seed = default_hasher_f64(w, seed)
+ return seed
+}
\ No newline at end of file
diff --git a/base/runtime/os_specific_bsd.odin b/base/runtime/os_specific_bsd.odin
index 5d198484b..466001ada 100644
--- a/base/runtime/os_specific_bsd.odin
+++ b/base/runtime/os_specific_bsd.odin
@@ -9,7 +9,7 @@ foreign libc {
@(link_name="write")
_unix_write :: proc(fd: i32, buf: rawptr, size: int) -> int ---
- when ODIN_OS == .NetBSD {
+ when ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
@(link_name="__errno") __error :: proc() -> ^i32 ---
} else {
__error :: proc() -> ^i32 ---
diff --git a/base/runtime/procs_darwin.odin b/base/runtime/procs_darwin.odin
index 4f4903d47..c3fc46af1 100644
--- a/base/runtime/procs_darwin.odin
+++ b/base/runtime/procs_darwin.odin
@@ -1,6 +1,7 @@
#+private
package runtime
+@(priority_index=-1e6)
foreign import "system:Foundation.framework"
import "base:intrinsics"
diff --git a/base/runtime/random_generator.odin b/base/runtime/random_generator.odin
index 0c4c92bd2..81432b330 100644
--- a/base/runtime/random_generator.odin
+++ b/base/runtime/random_generator.odin
@@ -119,6 +119,7 @@ default_random_generator_proc :: proc(data: rawptr, mode: Random_Generator_Mode,
}
}
+@(require_results)
default_random_generator :: proc "contextless" (state: ^Default_Random_State = nil) -> Random_Generator {
return {
procedure = default_random_generator_proc,
diff --git a/base/sanitizer/address.odin b/base/sanitizer/address.odin
new file mode 100644
index 000000000..43c04a15d
--- /dev/null
+++ b/base/sanitizer/address.odin
@@ -0,0 +1,299 @@
+#+no-instrumentation
+package sanitizer
+
+Address_Death_Callback :: #type proc "c" (pc: rawptr, bp: rawptr, sp: rawptr, addr: rawptr, is_write: i32, access_size: uint)
+
+@(private="file")
+ASAN_ENABLED :: .Address in ODIN_SANITIZER_FLAGS
+
+@(private="file")
+@(default_calling_convention="system")
+foreign {
+ __asan_poison_memory_region :: proc(address: rawptr, size: uint) ---
+ __asan_unpoison_memory_region :: proc(address: rawptr, size: uint) ---
+ __sanitizer_set_death_callback :: proc(callback: Address_Death_Callback) ---
+ __asan_region_is_poisoned :: proc(begin: rawptr, size: uint) -> rawptr ---
+ __asan_address_is_poisoned :: proc(addr: rawptr) -> i32 ---
+ __asan_describe_address :: proc(addr: rawptr) ---
+ __asan_report_present :: proc() -> i32 ---
+ __asan_get_report_pc :: proc() -> rawptr ---
+ __asan_get_report_bp :: proc() -> rawptr ---
+ __asan_get_report_sp :: proc() -> rawptr ---
+ __asan_get_report_address :: proc() -> rawptr ---
+ __asan_get_report_access_type :: proc() -> i32 ---
+ __asan_get_report_access_size :: proc() -> uint ---
+ __asan_get_report_description :: proc() -> cstring ---
+ __asan_locate_address :: proc(addr: rawptr, name: rawptr, name_size: uint, region_address: ^rawptr, region_size: ^uint) -> cstring ---
+ __asan_get_alloc_stack :: proc(addr: rawptr, trace: rawptr, size: uint, thread_id: ^i32) -> uint ---
+ __asan_get_free_stack :: proc(addr: rawptr, trace: rawptr, size: uint, thread_id: ^i32) -> uint ---
+ __asan_get_shadow_mapping :: proc(shadow_scale: ^uint, shadow_offset: ^uint) ---
+ __asan_print_accumulated_stats :: proc() ---
+ __asan_get_current_fake_stack :: proc() -> rawptr ---
+ __asan_addr_is_in_fake_stack :: proc(fake_stack: rawptr, addr: rawptr, beg: ^rawptr, end: ^rawptr) -> rawptr ---
+ __asan_handle_no_return :: proc() ---
+ __asan_update_allocation_context :: proc(addr: rawptr) -> i32 ---
+}
+
+Address_Access_Type :: enum {
+ none,
+ read,
+ write,
+}
+
+Address_Located_Address_String :: struct {
+ category: string,
+ name: string,
+}
+
+Address_Shadow_Mapping :: struct {
+ scale: uint,
+ offset: uint,
+}
+
+address_poison_slice :: proc "contextless" (region: $T/[]$E) {
+ when ASAN_ENABLED {
+ __asan_poison_memory_region(raw_data(region), size_of(E) * len(region))
+ }
+}
+
+address_unpoison_slice :: proc "contextless" (region: $T/[]$E) {
+ when ASAN_ENABLED {
+ __asan_unpoison_memory_region(raw_data(region), size_of(E) * len(region))
+ }
+}
+
+address_poison_ptr :: proc "contextless" (ptr: ^$T) {
+ when ASAN_ENABLED {
+ __asan_poison_memory_region(ptr, size_of(T))
+ }
+}
+
+address_unpoison_ptr :: proc "contextless" (ptr: ^$T) {
+ when ASAN_ENABLED {
+ __asan_unpoison_memory_region(ptr, size_of(T))
+ }
+}
+
+address_poison_rawptr :: proc "contextless" (ptr: rawptr, len: int) {
+ when ASAN_ENABLED {
+ assert_contextless(len >= 0)
+ __asan_poison_memory_region(ptr, uint(len))
+ }
+}
+
+address_unpoison_rawptr :: proc "contextless" (ptr: rawptr, len: int) {
+ when ASAN_ENABLED {
+ assert_contextless(len >= 0)
+ __asan_unpoison_memory_region(ptr, uint(len))
+ }
+}
+
+address_poison :: proc {
+ address_poison_slice,
+ address_poison_ptr,
+ address_poison_rawptr,
+}
+
+address_unpoison :: proc {
+ address_unpoison_slice,
+ address_unpoison_ptr,
+ address_unpoison_rawptr,
+}
+
+address_set_death_callback :: proc "contextless" (callback: Address_Death_Callback) {
+ when ASAN_ENABLED {
+ __sanitizer_set_death_callback(callback)
+ }
+}
+
+address_region_is_poisoned_slice :: proc "contextless" (region: []$T/$E) -> rawptr {
+ when ASAN_ENABLED {
+ return __asan_region_is_poisoned(raw_data(region), size_of(E) * len(region))
+ } else {
+ return nil
+ }
+}
+
+address_region_is_poisoned_ptr :: proc "contextless" (ptr: ^$T) -> rawptr {
+ when ASAN_ENABLED {
+ return __asan_region_is_poisoned(ptr, size_of(T))
+ } else {
+ return nil
+ }
+}
+
+address_region_is_poisoned_rawptr :: proc "contextless" (region: rawptr, len: int) -> rawptr {
+ when ASAN_ENABLED {
+ assert_contextless(len >= 0)
+ return __asan_region_is_poisoned(region, uint(len))
+ } else {
+ return nil
+ }
+}
+
+address_region_is_poisoned :: proc {
+ address_region_is_poisoned_slice,
+ address_region_is_poisoned_ptr,
+ address_region_is_poisoned_rawptr,
+}
+
+address_address_is_poisoned :: proc "contextless" (address: rawptr) -> bool {
+ when ASAN_ENABLED {
+ return __asan_address_is_poisoned(address) != 0
+ } else {
+ return false
+ }
+}
+
+address_describe_address :: proc "contextless" (address: rawptr) {
+ when ASAN_ENABLED {
+ __asan_describe_address(address)
+ }
+}
+
+address_report_present :: proc "contextless" () -> bool {
+ when ASAN_ENABLED {
+ return __asan_report_present() != 0
+ } else {
+ return false
+ }
+}
+
+address_get_report_pc :: proc "contextless" () -> rawptr {
+ when ASAN_ENABLED {
+ return __asan_get_report_pc()
+ } else {
+ return nil
+ }
+}
+
+address_get_report_bp :: proc "contextless" () -> rawptr {
+ when ASAN_ENABLED {
+ return __asan_get_report_bp()
+ } else {
+ return nil
+ }
+}
+
+address_get_report_sp :: proc "contextless" () -> rawptr {
+ when ASAN_ENABLED {
+ return __asan_get_report_sp()
+ } else {
+ return nil
+ }
+}
+
+address_get_report_address :: proc "contextless" () -> rawptr {
+ when ASAN_ENABLED {
+ return __asan_get_report_address()
+ } else {
+ return nil
+ }
+}
+
+address_get_report_access_type :: proc "contextless" () -> Address_Access_Type {
+ when ASAN_ENABLED {
+ return __asan_get_report_access_type() == 0 ? .read : .write
+ } else {
+ return .none
+ }
+}
+
+address_get_report_access_size :: proc "contextless" () -> uint {
+ when ASAN_ENABLED {
+ return __asan_get_report_access_size()
+ } else {
+ return 0
+ }
+}
+
+address_get_report_description :: proc "contextless" () -> string {
+ when ASAN_ENABLED {
+ return string(__asan_get_report_description())
+ } else {
+ return "unknown"
+ }
+}
+
+address_locate_address :: proc "contextless" (addr: rawptr, data: []byte) -> (Address_Located_Address_String, []byte) {
+ when ASAN_ENABLED {
+ out_addr: rawptr
+ out_size: uint
+ str := __asan_locate_address(addr, raw_data(data), len(data), &out_addr, &out_size)
+ return { string(str), string(cstring(raw_data(data))) }, (cast([^]byte)out_addr)[:out_size]
+ } else {
+ return { "", "" }, {}
+ }
+}
+
+address_get_alloc_stack_trace :: proc "contextless" (addr: rawptr, data: []rawptr) -> ([]rawptr, int) {
+ when ASAN_ENABLED {
+ out_thread: i32
+ __asan_get_alloc_stack(addr, raw_data(data), len(data), &out_thread)
+ return data, int(out_thread)
+ } else {
+ return {}, 0
+ }
+}
+
+address_get_free_stack_trace :: proc "contextless" (addr: rawptr, data: []rawptr) -> ([]rawptr, int) {
+ when ASAN_ENABLED {
+ out_thread: i32
+ __asan_get_free_stack(addr, raw_data(data), len(data), &out_thread)
+ return data, int(out_thread)
+ } else {
+ return {}, 0
+ }
+}
+
+address_get_shadow_mapping :: proc "contextless" () -> Address_Shadow_Mapping {
+ when ASAN_ENABLED {
+ result: Address_Shadow_Mapping
+ __asan_get_shadow_mapping(&result.scale, &result.offset)
+ return result
+ } else {
+ return {}
+ }
+}
+
+address_print_accumulated_stats :: proc "contextless" () {
+ when ASAN_ENABLED {
+ __asan_print_accumulated_stats()
+ }
+}
+
+address_get_current_fake_stack :: proc "contextless" () -> rawptr {
+ when ASAN_ENABLED {
+ return __asan_get_current_fake_stack()
+ } else {
+ return nil
+ }
+}
+
+address_is_in_fake_stack :: proc "contextless" (fake_stack: rawptr, addr: rawptr) -> ([]byte, bool) {
+ when ASAN_ENABLED {
+ begin: rawptr
+ end: rawptr
+ if __asan_addr_is_in_fake_stack(fake_stack, addr, &begin, &end) == nil {
+ return {}, false
+ }
+ return ((cast([^]byte)begin)[:uintptr(end)-uintptr(begin)]), true
+ } else {
+ return {}, false
+ }
+}
+
+address_handle_no_return :: proc "contextless" () {
+ when ASAN_ENABLED {
+ __asan_handle_no_return()
+ }
+}
+
+address_update_allocation_context :: proc "contextless" (addr: rawptr) -> bool {
+ when ASAN_ENABLED {
+ return __asan_update_allocation_context(addr) != 0
+ } else {
+ return false
+ }
+}
+
diff --git a/bin/lld-link.exe b/bin/lld-link.exe
index da6527264..ec739e0fd 100644
Binary files a/bin/lld-link.exe and b/bin/lld-link.exe differ
diff --git a/bin/llvm/windows/LLVM-C.lib b/bin/llvm/windows/LLVM-C.lib
index 0e5b6c624..6230de057 100644
Binary files a/bin/llvm/windows/LLVM-C.lib and b/bin/llvm/windows/LLVM-C.lib differ
diff --git a/bin/wasm-ld.exe b/bin/wasm-ld.exe
index da6527264..ec739e0fd 100644
Binary files a/bin/wasm-ld.exe and b/bin/wasm-ld.exe differ
diff --git a/build.bat b/build.bat
index 55c71ca9f..ae733ff2a 100644
--- a/build.bat
+++ b/build.bat
@@ -4,12 +4,12 @@ setlocal EnableDelayedExpansion
where /Q cl.exe || (
set __VSCMD_ARG_NO_LOGO=1
- for /f "tokens=*" %%i in ('"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -requires Microsoft.VisualStudio.Workload.NativeDesktop -property installationPath') do set VS=%%i
+ for /f "tokens=*" %%i in ('"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath') do set VS=%%i
if "!VS!" equ "" (
- echo ERROR: Visual Studio installation not found
+ echo ERROR: MSVC installation not found
exit /b 1
)
- call "!VS!\VC\Auxiliary\Build\vcvarsall.bat" amd64 || exit /b 1
+ call "!VS!\Common7\Tools\vsdevcmd.bat" -arch=x64 -host_arch=x64 || exit /b 1
)
if "%VSCMD_ARG_TGT_ARCH%" neq "x64" (
@@ -19,7 +19,11 @@ if "%VSCMD_ARG_TGT_ARCH%" neq "x64" (
)
)
-for /f %%i in ('powershell get-date -format "{yyyyMMdd}"') do (
+pushd misc
+cl /nologo get-date.c
+popd
+
+for /f %%i in ('misc\get-date') do (
set CURR_DATE_TIME=%%i
)
set curr_year=%CURR_DATE_TIME:~0,4%
@@ -58,7 +62,6 @@ set V4=0
set odin_version_full="%V1%.%V2%.%V3%.%V4%"
set odin_version_raw="dev-%V1%-%V2%"
-
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF
rem Parse source code as utf-8 even on shift-jis and other codepages
rem See https://learn.microsoft.com/en-us/cpp/build/reference/utf-8-set-source-and-executable-character-sets-to-utf-8?view=msvc-170
@@ -135,6 +138,7 @@ del *.ilk > NUL 2> NUL
rc %rc_flags% %odin_rc%
cl %compiler_settings% "src\main.cpp" "src\libtommath.cpp" /link %linker_settings% -OUT:%exe_name%
+if %errorlevel% neq 0 goto end_of_build
mt -nologo -inputresource:%exe_name%;#1 -manifest misc\odin.manifest -outputresource:%exe_name%;#1 -validate_manifest -identity:"odin, processorArchitecture=amd64, version=%odin_version_full%, type=win32"
if %errorlevel% neq 0 goto end_of_build
@@ -149,4 +153,4 @@ if %release_mode% EQU 0 echo: & echo Debug compiler built. Note: run "build.bat
del *.obj > NUL 2> NUL
-:end_of_build
\ No newline at end of file
+:end_of_build
diff --git a/build_odin.sh b/build_odin.sh
index 42ed2834f..19bb82a11 100755
--- a/build_odin.sh
+++ b/build_odin.sh
@@ -9,7 +9,7 @@ set -eu
CPPFLAGS="$CPPFLAGS -DODIN_VERSION_RAW=\"dev-$(date +"%Y-%m")\""
CXXFLAGS="$CXXFLAGS -std=c++14"
DISABLED_WARNINGS="-Wno-switch -Wno-macro-redefined -Wno-unused-value"
-LDFLAGS="$LDFLAGS -pthread -lm -lstdc++"
+LDFLAGS="$LDFLAGS -pthread -lm"
OS_ARCH="$(uname -m)"
OS_NAME="$(uname -s)"
@@ -25,7 +25,9 @@ error() {
# Brew advises people not to add llvm to their $PATH, so try and use brew to find it.
if [ -z "$LLVM_CONFIG" ] && [ -n "$(command -v brew)" ]; then
- if [ -n "$(command -v $(brew --prefix llvm@18)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@18)/bin/llvm-config"
+ if [ -n "$(command -v $(brew --prefix llvm@20)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@20)/bin/llvm-config"
+ elif [ -n "$(command -v $(brew --prefix llvm@19)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@19)/bin/llvm-config"
+ elif [ -n "$(command -v $(brew --prefix llvm@18)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@18)/bin/llvm-config"
elif [ -n "$(command -v $(brew --prefix llvm@17)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@17)/bin/llvm-config"
elif [ -n "$(command -v $(brew --prefix llvm@14)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@14)/bin/llvm-config"
fi
@@ -33,13 +35,17 @@ fi
if [ -z "$LLVM_CONFIG" ]; then
# darwin, linux, openbsd
- if [ -n "$(command -v llvm-config-18)" ]; then LLVM_CONFIG="llvm-config-18"
+ if [ -n "$(command -v llvm-config-20)" ]; then LLVM_CONFIG="llvm-config-20"
+ elif [ -n "$(command -v llvm-config-19)" ]; then LLVM_CONFIG="llvm-config-19"
+ elif [ -n "$(command -v llvm-config-18)" ]; then LLVM_CONFIG="llvm-config-18"
elif [ -n "$(command -v llvm-config-17)" ]; then LLVM_CONFIG="llvm-config-17"
elif [ -n "$(command -v llvm-config-14)" ]; then LLVM_CONFIG="llvm-config-14"
elif [ -n "$(command -v llvm-config-13)" ]; then LLVM_CONFIG="llvm-config-13"
elif [ -n "$(command -v llvm-config-12)" ]; then LLVM_CONFIG="llvm-config-12"
elif [ -n "$(command -v llvm-config-11)" ]; then LLVM_CONFIG="llvm-config-11"
# freebsd
+ elif [ -n "$(command -v llvm-config20)" ]; then LLVM_CONFIG="llvm-config20"
+ elif [ -n "$(command -v llvm-config19)" ]; then LLVM_CONFIG="llvm-config19"
elif [ -n "$(command -v llvm-config18)" ]; then LLVM_CONFIG="llvm-config18"
elif [ -n "$(command -v llvm-config17)" ]; then LLVM_CONFIG="llvm-config17"
elif [ -n "$(command -v llvm-config14)" ]; then LLVM_CONFIG="llvm-config14"
@@ -66,15 +72,15 @@ LLVM_VERSION_MAJOR="$(echo $LLVM_VERSION | awk -F. '{print $1}')"
LLVM_VERSION_MINOR="$(echo $LLVM_VERSION | awk -F. '{print $2}')"
LLVM_VERSION_PATCH="$(echo $LLVM_VERSION | awk -F. '{print $3}')"
-if [ $LLVM_VERSION_MAJOR -lt 11 ] || ([ $LLVM_VERSION_MAJOR -gt 14 ] && [ $LLVM_VERSION_MAJOR -lt 17 ]) || [ $LLVM_VERSION_MAJOR -gt 18 ]; then
- error "Invalid LLVM version $LLVM_VERSION: must be 11, 12, 13, 14, 17 or 18"
+if [ $LLVM_VERSION_MAJOR -lt 11 ] || ([ $LLVM_VERSION_MAJOR -gt 14 ] && [ $LLVM_VERSION_MAJOR -lt 17 ]) || [ $LLVM_VERSION_MAJOR -gt 20 ]; then
+ error "Invalid LLVM version $LLVM_VERSION: must be 11, 12, 13, 14, 17, 18, 19 or 20"
fi
case "$OS_NAME" in
Darwin)
if [ "$OS_ARCH" = "arm64" ]; then
if [ $LLVM_VERSION_MAJOR -lt 13 ]; then
- error "Invalid LLVM version $LLVM_VERSION: Darwin Arm64 requires LLVM 13, 14, 17 or 18"
+ error "Invalid LLVM version $LLVM_VERSION: Darwin Arm64 requires LLVM 13, 14, 17, 18, 19 or 20"
fi
fi
@@ -92,28 +98,28 @@ Darwin)
;;
FreeBSD)
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
- LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
+ LDFLAGS="$LDFLAGS -lstdc++ $($LLVM_CONFIG --libs core native --system-libs)"
;;
NetBSD)
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
- LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
+ LDFLAGS="$LDFLAGS -lstdc++ $($LLVM_CONFIG --libs core native --system-libs)"
;;
Linux)
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
- LDFLAGS="$LDFLAGS -ldl $($LLVM_CONFIG --libs core native --system-libs --libfiles)"
+ LDFLAGS="$LDFLAGS -lstdc++ -ldl $($LLVM_CONFIG --libs core native --system-libs --libfiles)"
# Copy libLLVM*.so into current directory for linking
# NOTE: This is needed by the Linux release pipeline!
# cp $(readlink -f $($LLVM_CONFIG --libfiles)) ./
LDFLAGS="$LDFLAGS -Wl,-rpath=\$ORIGIN"
;;
OpenBSD)
- CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
- LDFLAGS="$LDFLAGS -liconv"
+ CXXFLAGS="$CXXFLAGS -I/usr/local/include $($LLVM_CONFIG --cxxflags --ldflags)"
+ LDFLAGS="$LDFLAGS -lstdc++ -L/usr/local/lib -liconv"
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
;;
Haiku)
- CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags) -I/system/develop/headers/private/shared -I/system/develop/headers/private/kernel"
- LDFLAGS="$LDFLAGS -liconv"
+ CXXFLAGS="$CXXFLAGS -D_GNU_SOURCE $($LLVM_CONFIG --cxxflags --ldflags) -I/system/develop/headers/private/shared -I/system/develop/headers/private/kernel"
+ LDFLAGS="$LDFLAGS -lstdc++ -liconv"
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
;;
*)
diff --git a/ci/build_linux_static.sh b/ci/build_linux_static.sh
index f821cbb59..2eb99116e 100755
--- a/ci/build_linux_static.sh
+++ b/ci/build_linux_static.sh
@@ -1,8 +1,8 @@
#!/usr/bin/env sh
# Intended for use in Alpine containers, see the "nightly" Github action for a list of dependencies
-CXX="clang++-18"
-LLVM_CONFIG="llvm-config-18"
+CXX="clang++-20"
+LLVM_CONFIG="llvm-config-20"
DISABLED_WARNINGS="-Wno-switch -Wno-macro-redefined -Wno-unused-value"
diff --git a/core/c/c.odin b/core/c/c.odin
index 3dfc19ffc..73727d8d5 100644
--- a/core/c/c.odin
+++ b/core/c/c.odin
@@ -114,3 +114,5 @@ CHAR_BIT :: 8
va_list :: struct #align(16) {
_: [4096]u8,
}
+
+FILE :: struct {}
diff --git a/core/c/frontend/preprocessor/const_expr.odin b/core/c/frontend/preprocessor/const_expr.odin
deleted file mode 100644
index ff13f6432..000000000
--- a/core/c/frontend/preprocessor/const_expr.odin
+++ /dev/null
@@ -1,25 +0,0 @@
-package c_frontend_preprocess
-
-import "core:c/frontend/tokenizer"
-
-const_expr :: proc(rest: ^^Token, tok: ^Token) -> i64 {
- // TODO(bill): Handle const_expr correctly
- // This is effectively a mini-parser
-
- assert(rest != nil)
- assert(tok != nil)
- rest^ = tokenizer.new_eof(tok)
- switch v in tok.val {
- case i64:
- return v
- case f64:
- return i64(v)
- case string:
- return 0
- case []u16:
- // TODO
- case []u32:
- // TODO
- }
- return 0
-}
diff --git a/core/c/frontend/preprocessor/preprocess.odin b/core/c/frontend/preprocessor/preprocess.odin
deleted file mode 100644
index b5eab0bb3..000000000
--- a/core/c/frontend/preprocessor/preprocess.odin
+++ /dev/null
@@ -1,1510 +0,0 @@
-package c_frontend_preprocess
-
-import "../tokenizer"
-
-import "core:strings"
-import "core:strconv"
-import "core:path/filepath"
-import "core:unicode/utf8"
-import "core:unicode/utf16"
-import "core:os"
-import "core:io"
-
-@(private)
-Tokenizer :: tokenizer.Tokenizer
-@(private)
-Token :: tokenizer.Token
-
-Error_Handler :: tokenizer.Error_Handler
-
-Macro_Param :: struct {
- next: ^Macro_Param,
- name: string,
-}
-
-Macro_Arg :: struct {
- next: ^Macro_Arg,
- name: string,
- tok: ^Token,
- is_va_args: bool,
-}
-
-Macro_Kind :: enum u8 {
- Function_Like,
- Value_Like,
-}
-
-Macro_Handler :: #type proc(^Preprocessor, ^Token) -> ^Token
-
-Macro :: struct {
- name: string,
- kind: Macro_Kind,
- params: ^Macro_Param,
- va_args_name: string,
- body: ^Token,
- handler: Macro_Handler,
-}
-
-Cond_Incl_State :: enum u8 {
- In_Then,
- In_Elif,
- In_Else,
-}
-
-Cond_Incl :: struct {
- next: ^Cond_Incl,
- tok: ^Token,
- state: Cond_Incl_State,
- included: bool,
-}
-
-Pragma_Handler :: #type proc(^Preprocessor, ^Token)
-
-Preprocessor :: struct {
- // Lookup tables
- macros: map[string]^Macro,
- pragma_once: map[string]bool,
- include_guards: map[string]string,
- filepath_cache: map[string]string,
-
- // Include path data
- include_paths: []string,
-
- // Counter for __COUNTER__ macro
- counter: i64,
-
- // Include information
- cond_incl: ^Cond_Incl,
- include_level: int,
- include_next_index: int,
-
- wide_char_size: int,
-
- // Mutable data
- err: Error_Handler,
- warn: Error_Handler,
- pragma_handler: Pragma_Handler,
- error_count: int,
- warning_count: int,
-}
-
-MAX_INCLUDE_LEVEL :: 1024
-
-error :: proc(cpp: ^Preprocessor, tok: ^Token, msg: string, args: ..any) {
- if cpp.err != nil {
- cpp.err(tok.pos, msg, ..args)
- }
- cpp.error_count += 1
-}
-
-warn :: proc(cpp: ^Preprocessor, tok: ^Token, msg: string, args: ..any) {
- if cpp.warn != nil {
- cpp.warn(tok.pos, msg, ..args)
- }
- cpp.warning_count += 1
-}
-
-is_hash :: proc(tok: ^Token) -> bool {
- return tok.at_bol && tok.lit == "#"
-}
-
-skip_line :: proc(cpp: ^Preprocessor, tok: ^Token) -> ^Token {
- tok := tok
- if tok.at_bol {
- return tok
- }
- warn(cpp, tok, "extra token")
- for tok.at_bol {
- tok = tok.next
- }
- return tok
-}
-
-
-append_token :: proc(a, b: ^Token) -> ^Token {
- if a.kind == .EOF {
- return b
- }
-
- head: Token
- curr := &head
-
- for tok := a; tok.kind != .EOF; tok = tok.next {
- curr.next = tokenizer.copy_token(tok)
- curr = curr.next
- }
- curr.next = b
- return head.next
-}
-
-
-is_hex_digit :: proc(x: byte) -> bool {
- switch x {
- case '0'..='9', 'a'..='f', 'A'..='F':
- return true
- }
- return false
-}
-from_hex :: proc(x: byte) -> i32 {
- switch x {
- case '0'..='9':
- return i32(x) - '0'
- case 'a'..='f':
- return i32(x) - 'a' + 10
- case 'A'..='F':
- return i32(x) - 'A' + 10
- }
- return 16
-}
-
-
-convert_pp_number :: proc(tok: ^Token) {
- convert_pp_int :: proc(tok: ^Token) -> bool {
- p := tok.lit
- base := 10
- if len(p) > 2 {
- if strings.equal_fold(p[:2], "0x") && is_hex_digit(p[2]) {
- p = p[2:]
- base = 16
- } else if strings.equal_fold(p[:2], "0b") && p[2] == '0' || p[2] == '1' {
- p = p[2:]
- base = 2
- }
- }
- if base == 10 && p[0] == '0' {
- base = 8
- }
-
-
- tok.val, _ = strconv.parse_i64_of_base(p, base)
-
- l, u: int
-
- suf: [3]byte
- suf_n := 0
- i := len(p)-1
- for /**/; i >= 0 && suf_n < len(suf); i -= 1 {
- switch p[i] {
- case 'l', 'L':
- suf[suf_n] = 'l'
- l += 1
- suf_n += 1
- case 'u', 'U':
- suf[suf_n] = 'u'
- u += 1
- suf_n += 1
- }
- }
- if i < len(p) {
- if !is_hex_digit(p[i]) && p[i] != '.' {
- return false
- }
- }
- if u > 1 {
- return false
- }
-
- if l > 2 {
- return false
- }
-
- if u == 1 {
- switch l {
- case 0: tok.type_hint = .Unsigned_Int
- case 1: tok.type_hint = .Unsigned_Long
- case 2: tok.type_hint = .Unsigned_Long_Long
- }
- } else {
- switch l {
- case 0: tok.type_hint = .Int
- case 1: tok.type_hint = .Long
- case 2: tok.type_hint = .Long_Long
- }
- }
- return true
- }
-
- if convert_pp_int(tok) {
- return
- }
-
- fval, _ := strconv.parse_f64(tok.lit)
- tok.val = fval
-
- end := tok.lit[len(tok.lit)-1]
- switch end {
- case 'f', 'F':
- tok.type_hint = .Float
- case 'l', 'L':
- tok.type_hint = .Long_Double
- case:
- tok.type_hint = .Double
- }
-
-}
-
-convert_pp_char :: proc(tok: ^Token) {
- assert(len(tok.lit) >= 2)
- r, _, _, _ := unquote_char(tok.lit, tok.lit[0])
- tok.val = i64(r)
-
- tok.type_hint = .Int
- switch tok.prefix {
- case "u": tok.type_hint = .UTF_16
- case "U": tok.type_hint = .UTF_32
- case "L": tok.type_hint = .UTF_Wide
- }
-}
-
-wide_char_size :: proc(cpp: ^Preprocessor) -> int {
- char_size := 4
- if cpp.wide_char_size > 0 {
- char_size = clamp(cpp.wide_char_size, 1, 4)
- assert(char_size & (char_size-1) == 0)
- }
- return char_size
-}
-
-convert_pp_string :: proc(cpp: ^Preprocessor, tok: ^Token) {
- assert(len(tok.lit) >= 2)
- str, _, _ := unquote_string(tok.lit)
- tok.val = str
-
- char_size := 1
-
- switch tok.prefix {
- case "u8":
- tok.type_hint = .UTF_8
- char_size = 1
- case "u":
- tok.type_hint = .UTF_16
- char_size = 2
- case "U":
- tok.type_hint = .UTF_32
- char_size = 4
- case "L":
- tok.type_hint = .UTF_Wide
- char_size = wide_char_size(cpp)
- }
-
- switch char_size {
- case 2:
- n: int
- buf := make([]u16, len(str))
- for c in str {
- ch := c
- if ch < 0x10000 {
- buf[n] = u16(ch)
- n += 1
- } else {
- ch -= 0x10000
- buf[n+0] = 0xd800 + u16((ch >> 10) & 0x3ff)
- buf[n+1] = 0xdc00 + u16(ch & 0x3ff)
- n += 2
- }
- }
- tok.val = buf[:n]
- case 4:
- n: int
- buf := make([]u32, len(str))
- for ch in str {
- buf[n] = u32(ch)
- n += 1
- }
- tok.val = buf[:n]
- }
-
-}
-
-convert_pp_token :: proc(cpp: ^Preprocessor, t: ^Token, is_keyword: tokenizer.Is_Keyword_Proc) {
- switch {
- case t.kind == .Char:
- convert_pp_char(t)
- case t.kind == .String:
- convert_pp_string(cpp, t)
- case is_keyword != nil && is_keyword(t):
- t.kind = .Keyword
- case t.kind == .PP_Number:
- convert_pp_number(t)
- }
-}
-convert_pp_tokens :: proc(cpp: ^Preprocessor, tok: ^Token, is_keyword: tokenizer.Is_Keyword_Proc) {
- for t := tok; t != nil && t.kind != .EOF; t = t.next {
- convert_pp_token(cpp, tok, is_keyword)
- }
-}
-
-join_adjacent_string_literals :: proc(cpp: ^Preprocessor, initial_tok: ^Token) {
- for tok1 := initial_tok; tok1.kind != .EOF; /**/ {
- if tok1.kind != .String || tok1.next.kind != .String {
- tok1 = tok1.next
- continue
- }
-
- type_hint := tokenizer.Token_Type_Hint.None
- char_size := 1
-
- start := tok1
- for t := tok1; t != nil && t.kind == .String; t = t.next {
- if t.val == nil {
- convert_pp_string(cpp, t)
- }
- tok1 = t.next
- if type_hint != t.type_hint {
- if t.type_hint != .None && type_hint != .None {
- error(cpp, t, "unsupported non-standard concatenation of string literals of different types")
- }
- prev_char_size := char_size
-
- #partial switch type_hint {
- case .UTF_8: char_size = max(char_size, 1)
- case .UTF_16: char_size = max(char_size, 2)
- case .UTF_32: char_size = max(char_size, 4)
- case .UTF_Wide: char_size = max(char_size, wide_char_size(cpp))
- }
-
- if type_hint == .None || prev_char_size < char_size {
- type_hint = t.type_hint
- }
- }
- }
-
- // NOTE(bill): Verbose logic in order to correctly concantenate strings, even if they different in type
- max_len := 0
- switch char_size {
- case 1:
- for t := start; t != nil && t.kind == .String; t = t.next {
- #partial switch v in t.val {
- case string: max_len += len(v)
- case []u16: max_len += 2*len(v)
- case []u32: max_len += 4*len(v)
- }
- }
- n := 0
- buf := make([]byte, max_len)
- for t := start; t != nil && t.kind == .String; t = t.next {
- #partial switch v in t.val {
- case string:
- n += copy(buf[n:], v)
- case []u16:
- for i := 0; i < len(v); /**/ {
- c1 := v[i]
- r: rune
- if !utf16.is_surrogate(rune(c1)) {
- r = rune(c1)
- i += 1
- } else if i+1 == len(v) {
- r = utf16.REPLACEMENT_CHAR
- i += 1
- } else {
- c2 := v[i+1]
- i += 2
- r = utf16.decode_surrogate_pair(rune(c1), rune(c2))
- }
-
- b, w := utf8.encode_rune(r)
- n += copy(buf[n:], b[:w])
- }
- case []u32:
- for r in v {
- b, w := utf8.encode_rune(rune(r))
- n += copy(buf[n:], b[:w])
- }
- }
- }
-
- new_tok := tokenizer.copy_token(start)
- new_tok.lit = ""
- new_tok.val = string(buf[:n])
- new_tok.next = tok1
- new_tok.type_hint = type_hint
- start^ = new_tok^
- case 2:
- for t := start; t != nil && t.kind == .String; t = t.next {
- #partial switch v in t.val {
- case string: max_len += len(v)
- case []u16: max_len += len(v)
- case []u32: max_len += 2*len(v)
- }
- }
- n := 0
- buf := make([]u16, max_len)
- for t := start; t != nil && t.kind == .String; t = t.next {
- #partial switch v in t.val {
- case string:
- for r in v {
- if r >= 0x10000 {
- c1, c2 := utf16.encode_surrogate_pair(r)
- buf[n+0] = u16(c1)
- buf[n+1] = u16(c2)
- n += 2
- } else {
- buf[n] = u16(r)
- n += 1
- }
- }
- case []u16:
- n += copy(buf[n:], v)
- case []u32:
- for r in v {
- if r >= 0x10000 {
- c1, c2 := utf16.encode_surrogate_pair(rune(r))
- buf[n+0] = u16(c1)
- buf[n+1] = u16(c2)
- n += 2
- } else {
- buf[n] = u16(r)
- n += 1
- }
- }
- }
- }
-
- new_tok := tokenizer.copy_token(start)
- new_tok.lit = ""
- new_tok.val = buf[:n]
- new_tok.next = tok1
- new_tok.type_hint = type_hint
- start^ = new_tok^
- case 4:
- for t := start; t != nil && t.kind == .String; t = t.next {
- #partial switch v in t.val {
- case string: max_len += len(v)
- case []u16: max_len += len(v)
- case []u32: max_len += len(v)
- }
- }
- n := 0
- buf := make([]u32, max_len)
- for t := start; t != nil && t.kind == .String; t = t.next {
- #partial switch v in t.val {
- case string:
- for r in v {
- buf[n] = u32(r)
- n += 1
- }
- case []u16:
- for i := 0; i < len(v); /**/ {
- c1 := v[i]
- if !utf16.is_surrogate(rune(c1)) {
- buf[n] = u32(c1)
- n += 1
- i += 1
- } else if i+1 == len(v) {
- buf[n] = utf16.REPLACEMENT_CHAR
- n += 1
- i += 1
- } else {
- c2 := v[i+1]
- i += 2
- r := utf16.decode_surrogate_pair(rune(c1), rune(c2))
- buf[n] = u32(r)
- n += 1
- }
- }
- case []u32:
- n += copy(buf[n:], v)
- }
- }
-
- new_tok := tokenizer.copy_token(start)
- new_tok.lit = ""
- new_tok.val = buf[:n]
- new_tok.next = tok1
- new_tok.type_hint = type_hint
- start^ = new_tok^
- }
- }
-}
-
-
-quote_string :: proc(s: string) -> []byte {
- b := strings.builder_make(0, len(s)+2)
- io.write_quoted_string(strings.to_writer(&b), s, '"')
- return b.buf[:]
-}
-
-
-_init_tokenizer_from_preprocessor :: proc(t: ^Tokenizer, cpp: ^Preprocessor) -> ^Tokenizer {
- t.warn = cpp.warn
- t.err = cpp.err
- return t
-}
-
-new_string_token :: proc(cpp: ^Preprocessor, str: string, tok: ^Token) -> ^Token {
- assert(tok != nil)
- assert(str != "")
- t := _init_tokenizer_from_preprocessor(&Tokenizer{}, cpp)
- src := quote_string(str)
- return tokenizer.inline_tokenize(t, tok, src)
-}
-
-stringize :: proc(cpp: ^Preprocessor, hash, arg: ^Token) -> ^Token {
- s := join_tokens(arg, nil)
- return new_string_token(cpp, s, hash)
-}
-
-
-new_number_token :: proc(cpp: ^Preprocessor, i: i64, tok: ^Token) -> ^Token {
- t := _init_tokenizer_from_preprocessor(&Tokenizer{}, cpp)
- buf: [32]byte
- n := len(strconv.append_int(buf[:], i, 10))
- src := make([]byte, n)
- copy(src, buf[:n])
- return tokenizer.inline_tokenize(t, tok, src)
-}
-
-
-find_macro :: proc(cpp: ^Preprocessor, tok: ^Token) -> ^Macro {
- if tok.kind != .Ident {
- return nil
- }
- return cpp.macros[tok.lit]
-}
-
-add_macro :: proc(cpp: ^Preprocessor, name: string, kind: Macro_Kind, body: ^Token) -> ^Macro {
- m := new(Macro)
- m.name = name
- m.kind = kind
- m.body = body
- cpp.macros[name] = m
- return m
-}
-
-
-undef_macro :: proc(cpp: ^Preprocessor, name: string) {
- delete_key(&cpp.macros, name)
-}
-
-add_builtin :: proc(cpp: ^Preprocessor, name: string, handler: Macro_Handler) -> ^Macro {
- m := add_macro(cpp, name, .Value_Like, nil)
- m.handler = handler
- return m
-}
-
-
-skip :: proc(cpp: ^Preprocessor, tok: ^Token, op: string) -> ^Token {
- if tok.lit != op {
- error(cpp, tok, "expected '%q'", op)
- }
- return tok.next
-}
-
-consume :: proc(rest: ^^Token, tok: ^Token, lit: string) -> bool {
- if tok.lit == lit {
- rest^ = tok.next
- return true
- }
- rest^ = tok
- return false
-}
-
-read_macro_params :: proc(cpp: ^Preprocessor, rest: ^^Token, tok: ^Token) -> (param: ^Macro_Param, va_args_name: string) {
- head: Macro_Param
- curr := &head
-
- tok := tok
- for tok.lit != ")" && tok.kind != .EOF {
- if curr != &head {
- tok = skip(cpp, tok, ",")
- }
-
- if tok.lit == "..." {
- va_args_name = "__VA_ARGS__"
- rest^ = skip(cpp, tok.next, ")")
- param = head.next
- return
- }
-
- if tok.kind != .Ident {
- error(cpp, tok, "expected an identifier")
- }
-
- if tok.next.lit == "..." {
- va_args_name = tok.lit
- rest^ = skip(cpp, tok.next.next, ")")
- param = head.next
- return
- }
-
- m := new(Macro_Param)
- m.name = tok.lit
- curr.next = m
- curr = curr.next
- tok = tok.next
- }
-
-
- rest^ = tok.next
- param = head.next
- return
-}
-
-copy_line :: proc(rest: ^^Token, tok: ^Token) -> ^Token {
- head: Token
- curr := &head
-
- tok := tok
- for ; !tok.at_bol; tok = tok.next {
- curr.next = tokenizer.copy_token(tok)
- curr = curr.next
- }
- curr.next = tokenizer.new_eof(tok)
- rest^ = tok
- return head.next
-}
-
-read_macro_definition :: proc(cpp: ^Preprocessor, rest: ^^Token, tok: ^Token) {
- tok := tok
- if tok.kind != .Ident {
- error(cpp, tok, "macro name must be an identifier")
- }
- name := tok.lit
- tok = tok.next
-
- if !tok.has_space && tok.lit == "(" {
- params, va_args_name := read_macro_params(cpp, &tok, tok.next)
-
- m := add_macro(cpp, name, .Function_Like, copy_line(rest, tok))
- m.params = params
- m.va_args_name = va_args_name
- } else {
- add_macro(cpp, name, .Value_Like, copy_line(rest, tok))
- }
-}
-
-
-join_tokens :: proc(tok, end: ^Token) -> string {
- n := 1
- for t := tok; t != end && t.kind != .EOF; t = t.next {
- if t != tok && t.has_space {
- n += 1
- }
- n += len(t.lit)
- }
-
- buf := make([]byte, n)
-
- pos := 0
- for t := tok; t != end && t.kind != .EOF; t = t.next {
- if t != tok && t.has_space {
- buf[pos] = ' '
- pos += 1
- }
- copy(buf[pos:], t.lit)
- pos += len(t.lit)
- }
-
- return string(buf[:pos])
-}
-
-read_include_filename :: proc(cpp: ^Preprocessor, rest: ^^Token, tok: ^Token) -> (filename: string, is_quote: bool) {
- tok := tok
-
- if tok.kind == .String {
- rest^ = skip_line(cpp, tok.next)
- filename = tok.lit[1:len(tok.lit)-1]
- is_quote = true
- return
- }
-
- if tok.lit == "<" {
- start := tok
- for ; tok.kind != .EOF; tok = tok.next {
- if tok.at_bol || tok.kind == .EOF {
- error(cpp, tok, "expected '>'")
- }
- is_quote = false
- if tok.lit == ">" {
- break
- }
- }
- rest^ = skip_line(cpp, tok.next)
- filename = join_tokens(start.next, tok)
- return
- }
-
- if tok.kind == .Ident {
- tok2 := preprocess_internal(cpp, copy_line(rest, tok))
- return read_include_filename(cpp, &tok2, tok2)
- }
-
- error(cpp, tok, "expected a filename")
- return
-}
-
-skip_cond_incl :: proc(tok: ^Token) -> ^Token {
- next_skip :: proc(tok: ^Token) -> ^Token {
- tok := tok
- for tok.kind != .EOF {
- if is_hash(tok) {
- switch tok.next.lit {
- case "if", "ifdef", "ifndef":
- tok = next_skip(tok.next.next)
- continue
-
- case "endif":
- return tok.next.next
- }
- }
- tok = tok.next
- }
- return tok
- }
-
- tok := tok
-
- loop: for tok.kind != .EOF {
- if is_hash(tok) {
- switch tok.next.lit {
- case "if", "ifdef", "ifndef":
- tok = next_skip(tok.next.next)
- continue loop
-
- case "elif", "else", "endif":
- break loop
- }
- }
-
- tok = tok.next
- }
- return tok
-}
-
-check_for_include_guard :: proc(tok: ^Token) -> (guard: string, ok: bool) {
- if !is_hash(tok) || tok.next.lit != "ifndef" {
- return
- }
- tok := tok
- tok = tok.next.next
-
- if tok.kind != .Ident {
- return
- }
-
- m := tok.lit
- tok = tok.next
-
- if !is_hash(tok) || tok.next.lit != "define" || tok.next.lit != "macro" {
- return
- }
-
- for tok.kind != .EOF {
- if !is_hash(tok) {
- tok = tok.next
- continue
- }
-
- if tok.next.lit == "endif" && tok.next.next.kind == .EOF {
- return m, true
- }
-
- switch tok.lit {
- case "if", "ifdef", "ifndef":
- tok = skip_cond_incl(tok.next)
- case:
- tok = tok.next
- }
- }
- return
-}
-
-include_file :: proc(cpp: ^Preprocessor, tok: ^Token, path: string, filename_tok: ^Token) -> ^Token {
- if cpp.pragma_once[path] {
- return tok
- }
-
- guard_name, guard_name_found := cpp.include_guards[path]
- if guard_name_found && cpp.macros[guard_name] != nil {
- return tok
- }
-
- if !os.exists(path) {
- error(cpp, filename_tok, "%s: cannot open file", path)
- return tok
- }
-
- cpp.include_level += 1
- if cpp.include_level > MAX_INCLUDE_LEVEL {
- error(cpp, tok, "exceeded maximum nest amount: %d", MAX_INCLUDE_LEVEL)
- return tok
- }
-
- t := _init_tokenizer_from_preprocessor(&Tokenizer{}, cpp)
- tok2 := tokenizer.tokenize_file(t, path, /*file.id*/1)
- if tok2 == nil {
- error(cpp, filename_tok, "%s: cannot open file", path)
- }
- cpp.include_level -= 1
-
- guard_name, guard_name_found = check_for_include_guard(tok2)
- if guard_name_found {
- cpp.include_guards[path] = guard_name
- }
-
- return append_token(tok2, tok)
-}
-
-find_arg :: proc(args: ^Macro_Arg, tok: ^Token) -> ^Macro_Arg {
- for ap := args; ap != nil; ap = ap.next {
- if tok.lit == ap.name {
- return ap
- }
- }
- return nil
-}
-
-paste :: proc(cpp: ^Preprocessor, lhs, rhs: ^Token) -> ^Token {
- buf := strings.concatenate({lhs.lit, rhs.lit})
- t := _init_tokenizer_from_preprocessor(&Tokenizer{}, cpp)
- tok := tokenizer.inline_tokenize(t, lhs, transmute([]byte)buf)
- if tok.next.kind != .EOF {
- error(cpp, lhs, "pasting forms '%s', an invalid token", buf)
- }
- return tok
-}
-
-has_varargs :: proc(args: ^Macro_Arg) -> bool {
- for ap := args; ap != nil; ap = ap.next {
- if ap.name == "__VA_ARGS__" {
- return ap.tok.kind != .EOF
- }
- }
- return false
-}
-
-substitute_token :: proc(cpp: ^Preprocessor, tok: ^Token, args: ^Macro_Arg) -> ^Token {
- head: Token
- curr := &head
- tok := tok
- for tok.kind != .EOF {
- if tok.lit == "#" {
- arg := find_arg(args, tok.next)
- if arg == nil {
- error(cpp, tok.next, "'#' is not followed by a macro parameter")
- }
- arg_tok := arg.tok if arg != nil else tok.next
- curr.next = stringize(cpp, tok, arg_tok)
- curr = curr.next
- tok = tok.next.next
- continue
- }
-
- if tok.lit == "," && tok.next.lit == "##" {
- if arg := find_arg(args, tok.next.next); arg != nil && arg.is_va_args {
- if arg.tok.kind == .EOF {
- tok = tok.next.next.next
- } else {
- curr.next = tokenizer.copy_token(tok)
- curr = curr.next
- tok = tok.next.next
- }
- continue
- }
- }
-
- if tok.lit == "##" {
- if curr == &head {
- error(cpp, tok, "'##' cannot appear at start of macro expansion")
- }
- if tok.next.kind == .EOF {
- error(cpp, tok, "'##' cannot appear at end of macro expansion")
- }
-
- if arg := find_arg(args, tok.next); arg != nil {
- if arg.tok.kind != .EOF {
- curr^ = paste(cpp, curr, arg.tok)^
- for t := arg.tok.next; t.kind != .EOF; t = t.next {
- curr.next = tokenizer.copy_token(t)
- curr = curr.next
- }
- }
- tok = tok.next.next
- continue
- }
-
- curr^ = paste(cpp, curr, tok.next)^
- tok = tok.next.next
- continue
- }
-
- arg := find_arg(args, tok)
-
- if arg != nil && tok.next.lit == "##" {
- rhs := tok.next.next
-
- if arg.tok.kind == .EOF {
- args2 := find_arg(args, rhs)
- if args2 != nil {
- for t := args.tok; t.kind != .EOF; t = t.next {
- curr.next = tokenizer.copy_token(t)
- curr = curr.next
- }
- } else {
- curr.next = tokenizer.copy_token(rhs)
- curr = curr.next
- }
- tok = rhs.next
- continue
- }
-
- for t := arg.tok; t.kind != .EOF; t = t.next {
- curr.next = tokenizer.copy_token(t)
- curr = curr.next
- }
- tok = tok.next
- continue
- }
-
- if tok.lit == "__VA_OPT__" && tok.next.lit == "(" {
- opt_arg := read_macro_arg_one(cpp, &tok, tok.next.next, true)
- if has_varargs(args) {
- for t := opt_arg.tok; t.kind != .EOF; t = t.next {
- curr.next = t
- curr = curr.next
- }
- }
- tok = skip(cpp, tok, ")")
- continue
- }
-
- if arg != nil {
- t := preprocess_internal(cpp, arg.tok)
- t.at_bol = tok.at_bol
- t.has_space = tok.has_space
- for ; t.kind != .EOF; t = t.next {
- curr.next = tokenizer.copy_token(t)
- curr = curr.next
- }
- tok = tok.next
- continue
- }
-
- curr.next = tokenizer.copy_token(tok)
- curr = curr.next
- tok = tok.next
- continue
- }
-
- curr.next = tok
- return head.next
-}
-
-read_macro_arg_one :: proc(cpp: ^Preprocessor, rest: ^^Token, tok: ^Token, read_rest: bool) -> ^Macro_Arg {
- tok := tok
- head: Token
- curr := &head
- level := 0
- for {
- if level == 0 && tok.lit == ")" {
- break
- }
- if level == 0 && !read_rest && tok.lit == "," {
- break
- }
-
- if tok.kind == .EOF {
- error(cpp, tok, "premature end of input")
- }
-
- switch tok.lit {
- case "(": level += 1
- case ")": level -= 1
- }
-
- curr.next = tokenizer.copy_token(tok)
- curr = curr.next
- tok = tok.next
- }
- curr.next = tokenizer.new_eof(tok)
-
- arg := new(Macro_Arg)
- arg.tok = head.next
- rest^ = tok
- return arg
-}
-
-read_macro_args :: proc(cpp: ^Preprocessor, rest: ^^Token, tok: ^Token, params: ^Macro_Param, va_args_name: string) -> ^Macro_Arg {
- tok := tok
- start := tok
- tok = tok.next.next
-
- head: Macro_Arg
- curr := &head
-
- pp := params
- for ; pp != nil; pp = pp.next {
- if curr != &head {
- tok = skip(cpp, tok, ",")
- }
- curr.next = read_macro_arg_one(cpp, &tok, tok, false)
- curr = curr.next
- curr.name = pp.name
- }
-
- if va_args_name != "" {
- arg: ^Macro_Arg
- if tok.lit == ")" {
- arg = new(Macro_Arg)
- arg.tok = tokenizer.new_eof(tok)
- } else {
- if pp != params {
- tok = skip(cpp, tok, ",")
- }
- arg = read_macro_arg_one(cpp, &tok, tok, true)
- }
- arg.name = va_args_name
- arg.is_va_args = true
- curr.next = arg
- curr = curr.next
- } else if pp != nil {
- error(cpp, start, "too many arguments")
- }
-
- skip(cpp, tok, ")")
- rest^ = tok
- return head.next
-}
-
-expand_macro :: proc(cpp: ^Preprocessor, rest: ^^Token, tok: ^Token) -> bool {
- if tokenizer.hide_set_contains(tok.hide_set, tok.lit) {
- return false
- }
- tok := tok
- m := find_macro(cpp, tok)
- if m == nil {
- return false
- }
-
- if m.handler != nil {
- rest^ = m.handler(cpp, tok)
- rest^.next = tok.next
- return true
- }
-
- if m.kind == .Value_Like {
- hs := tokenizer.hide_set_union(tok.hide_set, tokenizer.new_hide_set(m.name))
- body := tokenizer.add_hide_set(m.body, hs)
- for t := body; t.kind != .EOF; t = t.next {
- t.origin = tok
- }
- rest^ = append_token(body, tok.next)
- rest^.at_bol = tok.at_bol
- rest^.has_space = tok.has_space
- return true
- }
-
- if tok.next.lit != "(" {
- return false
- }
-
- macro_token := tok
- args := read_macro_args(cpp, &tok, tok, m.params, m.va_args_name)
- close_paren := tok
-
- hs := tokenizer.hide_set_intersection(macro_token.hide_set, close_paren.hide_set)
- hs = tokenizer.hide_set_union(hs, tokenizer.new_hide_set(m.name))
-
- body := substitute_token(cpp, m.body, args)
- body = tokenizer.add_hide_set(body, hs)
- for t := body; t.kind != .EOF; t = t.next {
- t.origin = macro_token
- }
- rest^ = append_token(body, tok.next)
- rest^.at_bol = macro_token.at_bol
- rest^.has_space = macro_token.has_space
- return true
-}
-
-search_include_next :: proc(cpp: ^Preprocessor, filename: string) -> (path: string, ok: bool) {
- for ; cpp.include_next_index < len(cpp.include_paths); cpp.include_next_index += 1 {
- tpath := filepath.join({cpp.include_paths[cpp.include_next_index], filename}, allocator=context.temp_allocator)
- if os.exists(tpath) {
- return strings.clone(tpath), true
- }
- }
- return
-}
-
-search_include_paths :: proc(cpp: ^Preprocessor, filename: string) -> (path: string, ok: bool) {
- if filepath.is_abs(filename) {
- return filename, true
- }
-
- if path, ok = cpp.filepath_cache[filename]; ok {
- return
- }
-
- for include_path in cpp.include_paths {
- tpath := filepath.join({include_path, filename}, allocator=context.temp_allocator)
- if os.exists(tpath) {
- path, ok = strings.clone(tpath), true
- cpp.filepath_cache[filename] = path
- return
- }
- }
-
- return
-}
-
-read_const_expr :: proc(cpp: ^Preprocessor, rest: ^^Token, tok: ^Token) -> ^Token {
- tok := tok
- tok = copy_line(rest, tok)
- head: Token
- curr := &head
- for tok.kind != .EOF {
- if tok.lit == "defined" {
- start := tok
- has_paren := consume(&tok, tok.next, "(")
- if tok.kind != .Ident {
- error(cpp, start, "macro name must be an identifier")
- }
- m := find_macro(cpp, tok)
- tok = tok.next
-
- if has_paren {
- tok = skip(cpp, tok, ")")
- }
-
- curr.next = new_number_token(cpp, 1 if m != nil else 0, start)
- curr = curr.next
- continue
- }
-
- curr.next = tok
- curr = curr.next
- tok = tok.next
- }
-
- curr.next = tok
- return head.next
-}
-
-eval_const_expr :: proc(cpp: ^Preprocessor, rest: ^^Token, tok: ^Token) -> (val: i64) {
- tok := tok
- start := tok
- expr := read_const_expr(cpp, rest, tok.next)
- expr = preprocess_internal(cpp, expr)
-
- if expr.kind == .EOF {
- error(cpp, start, "no expression")
- }
-
- for t := expr; t.kind != .EOF; t = t.next {
- if t.kind == .Ident {
- next := t.next
- t^ = new_number_token(cpp, 0, t)^
- t.next = next
- }
- }
-
- val = 1
- convert_pp_tokens(cpp, expr, tokenizer.default_is_keyword)
-
- rest2: ^Token
- val = const_expr(&rest2, expr)
- if rest2 != nil && rest2.kind != .EOF {
- error(cpp, rest2, "extra token")
- }
- return
-}
-
-push_cond_incl :: proc(cpp: ^Preprocessor, tok: ^Token, included: bool) -> ^Cond_Incl {
- ci := new(Cond_Incl)
- ci.next = cpp.cond_incl
- ci.state = .In_Then
- ci.tok = tok
- ci.included = included
- cpp.cond_incl = ci
- return ci
-}
-
-read_line_marker:: proc(cpp: ^Preprocessor, rest: ^^Token, tok: ^Token) {
- tok := tok
- start := tok
- tok = preprocess(cpp, copy_line(rest, tok))
- if tok.kind != .Number {
- error(cpp, tok, "invalid line marker")
- }
- ival, _ := tok.val.(i64)
- start.file.line_delta = int(ival - i64(start.pos.line))
- tok = tok.next
- if tok.kind == .EOF {
- return
- }
-
- if tok.kind != .String {
- error(cpp, tok, "filename expected")
- }
- start.file.display_name = tok.lit
-}
-
-preprocess_internal :: proc(cpp: ^Preprocessor, tok: ^Token) -> ^Token {
- head: Token
- curr := &head
-
- tok := tok
- for tok != nil && tok.kind != .EOF {
- if expand_macro(cpp, &tok, tok) {
- continue
- }
-
- if !is_hash(tok) {
- if tok.file != nil {
- tok.line_delta = tok.file.line_delta
- }
- curr.next = tok
- curr = curr.next
- tok = tok.next
- continue
- }
-
- start := tok
- tok = tok.next
-
- switch tok.lit {
- case "include":
- filename, is_quote := read_include_filename(cpp, &tok, tok.next)
- is_absolute := filepath.is_abs(filename)
- if is_absolute {
- tok = include_file(cpp, tok, filename, start.next.next)
- continue
- }
-
- if is_quote {
- dir := ""
- if start.file != nil {
- dir = filepath.dir(start.file.name)
- }
- path := filepath.join({dir, filename})
- if os.exists(path) {
- tok = include_file(cpp, tok, path, start.next.next)
- continue
- }
- }
-
- path, ok := search_include_paths(cpp, filename)
- if !ok {
- path = filename
- }
- tok = include_file(cpp, tok, path, start.next.next)
- continue
-
- case "include_next":
- filename, _ := read_include_filename(cpp, &tok, tok.next)
- path, ok := search_include_next(cpp, filename)
- if !ok {
- path = filename
- }
- tok = include_file(cpp, tok, path, start.next.next)
- continue
-
- case "define":
- read_macro_definition(cpp, &tok, tok.next)
- continue
-
- case "undef":
- tok = tok.next
- if tok.kind != .Ident {
- error(cpp, tok, "macro name must be an identifier")
- }
- undef_macro(cpp, tok.lit)
- tok = skip_line(cpp, tok.next)
- continue
-
- case "if":
- val := eval_const_expr(cpp, &tok, tok)
- push_cond_incl(cpp, start, val != 0)
- if val == 0 {
- tok = skip_cond_incl(tok)
- }
- continue
-
- case "ifdef":
- defined := find_macro(cpp, tok.next)
- push_cond_incl(cpp, tok, defined != nil)
- tok = skip_line(cpp, tok.next.next)
- if defined == nil {
- tok = skip_cond_incl(tok)
- }
- continue
-
- case "ifndef":
- defined := find_macro(cpp, tok.next)
- push_cond_incl(cpp, tok, defined != nil)
- tok = skip_line(cpp, tok.next.next)
- if !(defined == nil) {
- tok = skip_cond_incl(tok)
- }
- continue
-
- case "elif":
- if cpp.cond_incl == nil || cpp.cond_incl.state == .In_Else {
- error(cpp, start, "stray #elif")
- }
- if cpp.cond_incl != nil {
- cpp.cond_incl.state = .In_Elif
- }
-
- if (cpp.cond_incl != nil && !cpp.cond_incl.included) && eval_const_expr(cpp, &tok, tok) != 0 {
- cpp.cond_incl.included = true
- } else {
- tok = skip_cond_incl(tok)
- }
- continue
-
- case "else":
- if cpp.cond_incl == nil || cpp.cond_incl.state == .In_Else {
- error(cpp, start, "stray #else")
- }
- if cpp.cond_incl != nil {
- cpp.cond_incl.state = .In_Else
- }
- tok = skip_line(cpp, tok.next)
-
- if cpp.cond_incl != nil {
- tok = skip_cond_incl(tok)
- }
- continue
-
- case "endif":
- if cpp.cond_incl == nil {
- error(cpp, start, "stray #endif")
- } else {
- cpp.cond_incl = cpp.cond_incl.next
- }
- tok = skip_line(cpp, tok.next)
- continue
-
- case "line":
- read_line_marker(cpp, &tok, tok.next)
- continue
-
- case "pragma":
- if tok.next.lit == "once" {
- cpp.pragma_once[tok.pos.file] = true
- tok = skip_line(cpp, tok.next.next)
- continue
- }
-
- pragma_tok, pragma_end := tok, tok
-
- for tok != nil && tok.kind != .EOF {
- pragma_end = tok
- tok = tok.next
- if tok.at_bol {
- break
- }
- }
- pragma_end.next = tokenizer.new_eof(tok)
- if cpp.pragma_handler != nil {
- cpp.pragma_handler(cpp, pragma_tok.next)
- continue
- }
-
- continue
-
- case "error":
- error(cpp, tok, "error")
- }
-
- if tok.kind == .PP_Number {
- read_line_marker(cpp, &tok, tok)
- continue
- }
-
- if !tok.at_bol {
- error(cpp, tok, "invalid preprocessor directive")
- }
- }
-
- curr.next = tok
- return head.next
-}
-
-
-preprocess :: proc(cpp: ^Preprocessor, tok: ^Token) -> ^Token {
- tok := tok
- tok = preprocess_internal(cpp, tok)
- if cpp.cond_incl != nil {
- error(cpp, tok, "unterminated conditional directive")
- }
- convert_pp_tokens(cpp, tok, tokenizer.default_is_keyword)
- join_adjacent_string_literals(cpp, tok)
- for t := tok; t != nil; t = t.next {
- t.pos.line += t.line_delta
- }
- return tok
-}
-
-
-define_macro :: proc(cpp: ^Preprocessor, name, def: string) {
- src := transmute([]byte)def
-
- file := new(tokenizer.File)
- file.id = -1
- file.src = src
- file.name = ""
- file.display_name = file.name
-
-
- t := _init_tokenizer_from_preprocessor(&Tokenizer{}, cpp)
- tok := tokenizer.tokenize(t, file)
- add_macro(cpp, name, .Value_Like, tok)
-}
-
-
-file_macro :: proc(cpp: ^Preprocessor, tok: ^Token) -> ^Token {
- tok := tok
- for tok.origin != nil {
- tok = tok.origin
- }
- i := i64(tok.pos.line + tok.file.line_delta)
- return new_number_token(cpp, i, tok)
-}
-line_macro :: proc(cpp: ^Preprocessor, tok: ^Token) -> ^Token {
- tok := tok
- for tok.origin != nil {
- tok = tok.origin
- }
- return new_string_token(cpp, tok.file.display_name, tok)
-}
-counter_macro :: proc(cpp: ^Preprocessor, tok: ^Token) -> ^Token {
- i := cpp.counter
- cpp.counter += 1
- return new_number_token(cpp, i, tok)
-}
-
-init_default_macros :: proc(cpp: ^Preprocessor) {
- define_macro(cpp, "__C99_MACRO_WITH_VA_ARGS", "1")
- define_macro(cpp, "__alignof__", "_Alignof")
- define_macro(cpp, "__const__", "const")
- define_macro(cpp, "__inline__", "inline")
- define_macro(cpp, "__signed__", "signed")
- define_macro(cpp, "__typeof__", "typeof")
- define_macro(cpp, "__volatile__", "volatile")
-
- add_builtin(cpp, "__FILE__", file_macro)
- add_builtin(cpp, "__LINE__", line_macro)
- add_builtin(cpp, "__COUNTER__", counter_macro)
-}
-
-init_lookup_tables :: proc(cpp: ^Preprocessor, allocator := context.allocator) {
- context.allocator = allocator
- reserve(&cpp.macros, max(16, cap(cpp.macros)))
- reserve(&cpp.pragma_once, max(16, cap(cpp.pragma_once)))
- reserve(&cpp.include_guards, max(16, cap(cpp.include_guards)))
- reserve(&cpp.filepath_cache, max(16, cap(cpp.filepath_cache)))
-}
-
-
-init_defaults :: proc(cpp: ^Preprocessor, lookup_tables_allocator := context.allocator) {
- if cpp.warn == nil {
- cpp.warn = tokenizer.default_warn_handler
- }
- if cpp.err == nil {
- cpp.err = tokenizer.default_error_handler
- }
- init_lookup_tables(cpp, lookup_tables_allocator)
- init_default_macros(cpp)
-}
diff --git a/core/c/frontend/preprocessor/unquote.odin b/core/c/frontend/preprocessor/unquote.odin
deleted file mode 100644
index 5869fa7ef..000000000
--- a/core/c/frontend/preprocessor/unquote.odin
+++ /dev/null
@@ -1,154 +0,0 @@
-package c_frontend_preprocess
-
-import "core:unicode/utf8"
-
-unquote_char :: proc(str: string, quote: byte) -> (r: rune, multiple_bytes: bool, tail_string: string, success: bool) {
- hex_to_int :: proc(c: byte) -> int {
- switch c {
- case '0'..='9': return int(c-'0')
- case 'a'..='f': return int(c-'a')+10
- case 'A'..='F': return int(c-'A')+10
- }
- return -1
- }
- w: int
-
- if str[0] == quote && quote == '"' {
- return
- } else if str[0] >= 0x80 {
- r, w = utf8.decode_rune_in_string(str)
- return r, true, str[w:], true
- } else if str[0] != '\\' {
- return rune(str[0]), false, str[1:], true
- }
-
- if len(str) <= 1 {
- return
- }
- s := str
- c := s[1]
- s = s[2:]
-
- switch c {
- case: r = rune(c)
-
- case 'a': r = '\a'
- case 'b': r = '\b'
- case 'e': r = '\e'
- case 'f': r = '\f'
- case 'n': r = '\n'
- case 'r': r = '\r'
- case 't': r = '\t'
- case 'v': r = '\v'
- case '\\': r = '\\'
-
- case '"': r = '"'
- case '\'': r = '\''
-
- case '0'..='7':
- v := int(c-'0')
- if len(s) < 2 {
- return
- }
- for i in 0.. 7 {
- return
- }
- v = (v<<3) | d
- }
- s = s[2:]
- if v > 0xff {
- return
- }
- r = rune(v)
-
- case 'x', 'u', 'U':
- count: int
- switch c {
- case 'x': count = 2
- case 'u': count = 4
- case 'U': count = 8
- }
-
- if len(s) < count {
- return
- }
-
- for i in 0.. utf8.MAX_RUNE {
- return
- }
- multiple_bytes = true
- }
-
- success = true
- tail_string = s
- return
-}
-
-unquote_string :: proc(lit: string, allocator := context.allocator) -> (res: string, allocated, success: bool) {
- contains_rune :: proc(s: string, r: rune) -> int {
- for c, offset in s {
- if c == r {
- return offset
- }
- }
- return -1
- }
-
- assert(len(lit) >= 2)
-
- s := lit
- quote := '"'
-
- if s == `""` {
- return "", false, true
- }
-
- if contains_rune(s, '\n') >= 0 {
- return s, false, false
- }
-
- if contains_rune(s, '\\') < 0 && contains_rune(s, quote) < 0 {
- if quote == '"' {
- return s, false, true
- }
- }
- s = s[1:len(s)-1]
-
-
- buf_len := 3*len(s) / 2
- buf := make([]byte, buf_len, allocator)
- offset := 0
- for len(s) > 0 {
- r, multiple_bytes, tail_string, ok := unquote_char(s, byte(quote))
- if !ok {
- delete(buf)
- return s, false, false
- }
- s = tail_string
- if r < 0x80 || !multiple_bytes {
- buf[offset] = byte(r)
- offset += 1
- } else {
- b, w := utf8.encode_rune(r)
- copy(buf[offset:], b[:w])
- offset += w
- }
- }
-
- new_string := string(buf[:offset])
-
- return new_string, true, true
-}
diff --git a/core/c/frontend/tokenizer/doc.odin b/core/c/frontend/tokenizer/doc.odin
deleted file mode 100644
index 43747dfe8..000000000
--- a/core/c/frontend/tokenizer/doc.odin
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
-Example:
- package demo
-
- import tokenizer "core:c/frontend/tokenizer"
- import preprocessor "core:c/frontend/preprocessor"
- import "core:fmt"
-
- main :: proc() {
- t := &tokenizer.Tokenizer{};
- tokenizer.init_defaults(t);
-
- cpp := &preprocessor.Preprocessor{};
- cpp.warn, cpp.err = t.warn, t.err;
- preprocessor.init_lookup_tables(cpp);
- preprocessor.init_default_macros(cpp);
- cpp.include_paths = {"my/path/to/include"};
-
- tok := tokenizer.tokenize_file(t, "the/source/file.c", 1);
-
- tok = preprocessor.preprocess(cpp, tok);
- if tok != nil {
- for t := tok; t.kind != .EOF; t = t.next {
- fmt.println(t.lit);
- }
- }
-
- fmt.println("[Done]");
- }
-*/
-package c_frontend_tokenizer
diff --git a/core/c/frontend/tokenizer/hide_set.odin b/core/c/frontend/tokenizer/hide_set.odin
deleted file mode 100644
index ec8b77e6e..000000000
--- a/core/c/frontend/tokenizer/hide_set.odin
+++ /dev/null
@@ -1,68 +0,0 @@
-package c_frontend_tokenizer
-
-// NOTE(bill): This is a really dumb approach for a hide set,
-// but it's really simple and probably fast enough in practice
-
-
-Hide_Set :: struct {
- next: ^Hide_Set,
- name: string,
-}
-
-
-new_hide_set :: proc(name: string) -> ^Hide_Set {
- hs := new(Hide_Set)
- hs.name = name
- return hs
-}
-
-hide_set_contains :: proc(hs: ^Hide_Set, name: string) -> bool {
- for h := hs; h != nil; h = h.next {
- if h.name == name {
- return true
- }
- }
- return false
-}
-
-
-hide_set_union :: proc(a, b: ^Hide_Set) -> ^Hide_Set {
- head: Hide_Set
- curr := &head
-
- for h := a; h != nil; h = h.next {
- curr.next = new_hide_set(h.name)
- curr = curr.next
- }
- curr.next = b
- return head.next
-}
-
-
-hide_set_intersection :: proc(a, b: ^Hide_Set) -> ^Hide_Set {
- head: Hide_Set
- curr := &head
-
- for h := a; h != nil; h = h.next {
- if hide_set_contains(b, h.name) {
- curr.next = new_hide_set(h.name)
- curr = curr.next
- }
- }
- return head.next
-}
-
-
-add_hide_set :: proc(tok: ^Token, hs: ^Hide_Set) -> ^Token {
- head: Token
- curr := &head
-
- tok := tok
- for ; tok != nil; tok = tok.next {
- t := copy_token(tok)
- t.hide_set = hide_set_union(t.hide_set, hs)
- curr.next = t
- curr = curr.next
- }
- return head.next
-}
diff --git a/core/c/frontend/tokenizer/token.odin b/core/c/frontend/tokenizer/token.odin
deleted file mode 100644
index 1376a651f..000000000
--- a/core/c/frontend/tokenizer/token.odin
+++ /dev/null
@@ -1,169 +0,0 @@
-package c_frontend_tokenizer
-
-
-Pos :: struct {
- file: string,
- line: int,
- column: int,
- offset: int,
-}
-
-Token_Kind :: enum {
- Invalid,
- Ident,
- Punct,
- Keyword,
- Char,
- String,
- Number,
- PP_Number,
- Comment,
- EOF,
-}
-
-File :: struct {
- name: string,
- id: int,
- src: []byte,
-
- display_name: string,
- line_delta: int,
-}
-
-
-Token_Type_Hint :: enum u8 {
- None,
-
- Int,
- Long,
- Long_Long,
-
- Unsigned_Int,
- Unsigned_Long,
- Unsigned_Long_Long,
-
- Float,
- Double,
- Long_Double,
-
- UTF_8,
- UTF_16,
- UTF_32,
- UTF_Wide,
-}
-
-Token_Value :: union {
- i64,
- f64,
- string,
- []u16,
- []u32,
-}
-
-Token :: struct {
- kind: Token_Kind,
- next: ^Token,
- lit: string,
-
- pos: Pos,
- file: ^File,
- line_delta: int,
- at_bol: bool,
- has_space: bool,
-
- type_hint: Token_Type_Hint,
- val: Token_Value,
- prefix: string,
-
- // Preprocessor values
- hide_set: ^Hide_Set,
- origin: ^Token,
-}
-
-Is_Keyword_Proc :: #type proc(tok: ^Token) -> bool
-
-copy_token :: proc(tok: ^Token) -> ^Token {
- t, _ := new_clone(tok^)
- t.next = nil
- return t
-}
-
-new_eof :: proc(tok: ^Token) -> ^Token {
- t, _ := new_clone(tok^)
- t.kind = .EOF
- t.lit = ""
- return t
-}
-
-default_is_keyword :: proc(tok: ^Token) -> bool {
- if tok.kind == .Keyword {
- return true
- }
- if len(tok.lit) > 0 {
- return default_keyword_set[tok.lit]
- }
- return false
-}
-
-
-token_name := [Token_Kind]string {
- .Invalid = "invalid",
- .Ident = "ident",
- .Punct = "punct",
- .Keyword = "keyword",
- .Char = "char",
- .String = "string",
- .Number = "number",
- .PP_Number = "preprocessor number",
- .Comment = "comment",
- .EOF = "eof",
-}
-
-default_keyword_set := map[string]bool{
- "auto" = true,
- "break" = true,
- "case" = true,
- "char" = true,
- "const" = true,
- "continue" = true,
- "default" = true,
- "do" = true,
- "double" = true,
- "else" = true,
- "enum" = true,
- "extern" = true,
- "float" = true,
- "for" = true,
- "goto" = true,
- "if" = true,
- "int" = true,
- "long" = true,
- "register" = true,
- "restrict" = true,
- "return" = true,
- "short" = true,
- "signed" = true,
- "sizeof" = true,
- "static" = true,
- "struct" = true,
- "switch" = true,
- "typedef" = true,
- "union" = true,
- "unsigned" = true,
- "void" = true,
- "volatile" = true,
- "while" = true,
- "_Alignas" = true,
- "_Alignof" = true,
- "_Atomic" = true,
- "_Bool" = true,
- "_Generic" = true,
- "_Noreturn" = true,
- "_Thread_local" = true,
- "__restrict" = true,
- "typeof" = true,
- "asm" = true,
- "__restrict__" = true,
- "__thread" = true,
- "__attribute__" = true,
-}
diff --git a/core/c/frontend/tokenizer/tokenizer.odin b/core/c/frontend/tokenizer/tokenizer.odin
deleted file mode 100644
index 558077717..000000000
--- a/core/c/frontend/tokenizer/tokenizer.odin
+++ /dev/null
@@ -1,667 +0,0 @@
-package c_frontend_tokenizer
-
-import "core:fmt"
-import "core:os"
-import "core:strings"
-import "core:unicode/utf8"
-
-
-Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any)
-
-
-Tokenizer :: struct {
- // Immutable data
- path: string,
- src: []byte,
-
-
- // Tokenizing state
- ch: rune,
- offset: int,
- read_offset: int,
- line_offset: int,
- line_count: int,
-
- // Extra information for tokens
- at_bol: bool,
- has_space: bool,
-
- // Mutable data
- err: Error_Handler,
- warn: Error_Handler,
- error_count: int,
- warning_count: int,
-}
-
-init_defaults :: proc(t: ^Tokenizer, err: Error_Handler = default_error_handler, warn: Error_Handler = default_warn_handler) {
- t.err = err
- t.warn = warn
-}
-
-
-@(private)
-offset_to_pos :: proc(t: ^Tokenizer, offset: int) -> (pos: Pos) {
- pos.file = t.path
- pos.offset = offset
- pos.line = t.line_count
- pos.column = offset - t.line_offset + 1
- return
-}
-
-default_error_handler :: proc(pos: Pos, msg: string, args: ..any) {
- fmt.eprintf("%s(%d:%d) ", pos.file, pos.line, pos.column)
- fmt.eprintf(msg, ..args)
- fmt.eprintf("\n")
-}
-
-default_warn_handler :: proc(pos: Pos, msg: string, args: ..any) {
- fmt.eprintf("%s(%d:%d) warning: ", pos.file, pos.line, pos.column)
- fmt.eprintf(msg, ..args)
- fmt.eprintf("\n")
-}
-
-error_offset :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
- pos := offset_to_pos(t, offset)
- if t.err != nil {
- t.err(pos, msg, ..args)
- }
- t.error_count += 1
-}
-
-warn_offset :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
- pos := offset_to_pos(t, offset)
- if t.warn != nil {
- t.warn(pos, msg, ..args)
- }
- t.warning_count += 1
-}
-
-error :: proc(t: ^Tokenizer, tok: ^Token, msg: string, args: ..any) {
- pos := tok.pos
- if t.err != nil {
- t.err(pos, msg, ..args)
- }
- t.error_count += 1
-}
-
-warn :: proc(t: ^Tokenizer, tok: ^Token, msg: string, args: ..any) {
- pos := tok.pos
- if t.warn != nil {
- t.warn(pos, msg, ..args)
- }
- t.warning_count += 1
-}
-
-
-advance_rune :: proc(t: ^Tokenizer) {
- if t.read_offset < len(t.src) {
- t.offset = t.read_offset
- if t.ch == '\n' {
- t.at_bol = true
- t.line_offset = t.offset
- t.line_count += 1
- }
- r, w := rune(t.src[t.read_offset]), 1
- switch {
- case r == 0:
- error_offset(t, t.offset, "illegal character NUL")
- case r >= utf8.RUNE_SELF:
- r, w = utf8.decode_rune(t.src[t.read_offset:])
- if r == utf8.RUNE_ERROR && w == 1 {
- error_offset(t, t.offset, "illegal UTF-8 encoding")
- } else if r == utf8.RUNE_BOM && t.offset > 0 {
- error_offset(t, t.offset, "illegal byte order mark")
- }
- }
- t.read_offset += w
- t.ch = r
- } else {
- t.offset = len(t.src)
- if t.ch == '\n' {
- t.at_bol = true
- t.line_offset = t.offset
- t.line_count += 1
- }
- t.ch = -1
- }
-}
-
-advance_rune_n :: proc(t: ^Tokenizer, n: int) {
- for _ in 0.. bool {
- return '0' <= r && r <= '9'
-}
-
-skip_whitespace :: proc(t: ^Tokenizer) {
- for {
- switch t.ch {
- case ' ', '\t', '\r', '\v', '\f', '\n':
- t.has_space = true
- advance_rune(t)
- case:
- return
- }
- }
-}
-
-scan_comment :: proc(t: ^Tokenizer) -> string {
- offset := t.offset-1
- next := -1
- general: {
- if t.ch == '/'{ // line comments
- advance_rune(t)
- for t.ch != '\n' && t.ch >= 0 {
- advance_rune(t)
- }
-
- next = t.offset
- if t.ch == '\n' {
- next += 1
- }
- break general
- }
-
- /* style comment */
- advance_rune(t)
- for t.ch >= 0 {
- ch := t.ch
- advance_rune(t)
- if ch == '*' && t.ch == '/' {
- advance_rune(t)
- next = t.offset
- break general
- }
- }
-
- error_offset(t, offset, "comment not terminated")
- }
-
- lit := t.src[offset : t.offset]
-
- // NOTE(bill): Strip CR for line comments
- for len(lit) > 2 && lit[1] == '/' && lit[len(lit)-1] == '\r' {
- lit = lit[:len(lit)-1]
- }
-
-
- return string(lit)
-}
-
-scan_identifier :: proc(t: ^Tokenizer) -> string {
- offset := t.offset
-
- for is_ident1(t.ch) {
- advance_rune(t)
- }
-
- return string(t.src[offset : t.offset])
-}
-
-scan_string :: proc(t: ^Tokenizer) -> string {
- offset := t.offset-1
-
- for {
- ch := t.ch
- if ch == '\n' || ch < 0 {
- error_offset(t, offset, "string literal was not terminated")
- break
- }
- advance_rune(t)
- if ch == '"' {
- break
- }
- if ch == '\\' {
- scan_escape(t)
- }
- }
-
- return string(t.src[offset : t.offset])
-}
-
-digit_val :: proc(r: rune) -> int {
- switch r {
- case '0'..='9':
- return int(r-'0')
- case 'A'..='F':
- return int(r-'A' + 10)
- case 'a'..='f':
- return int(r-'a' + 10)
- }
- return 16
-}
-
-scan_escape :: proc(t: ^Tokenizer) -> bool {
- offset := t.offset
-
- esc := t.ch
- n: int
- base, max: u32
- switch esc {
- case 'a', 'b', 'e', 'f', 'n', 't', 'v', 'r', '\\', '\'', '"':
- advance_rune(t)
- return true
-
- case '0'..='7':
- for digit_val(t.ch) < 8 {
- advance_rune(t)
- }
- return true
- case 'x':
- advance_rune(t)
- for digit_val(t.ch) < 16 {
- advance_rune(t)
- }
- return true
- case 'u':
- advance_rune(t)
- n, base, max = 4, 16, utf8.MAX_RUNE
- case 'U':
- advance_rune(t)
- n, base, max = 8, 16, utf8.MAX_RUNE
- case:
- if t.ch < 0 {
- error_offset(t, offset, "escape sequence was not terminated")
- } else {
- break
- }
- return false
- }
-
- x: u32
- main_loop: for n > 0 {
- d := u32(digit_val(t.ch))
- if d >= base {
- if t.ch == '"' || t.ch == '\'' {
- break main_loop
- }
- if t.ch < 0 {
- error_offset(t, t.offset, "escape sequence was not terminated")
- } else {
- error_offset(t, t.offset, "illegal character '%r' : %d in escape sequence", t.ch, t.ch)
- }
- return false
- }
-
- x = x*base + d
- advance_rune(t)
- n -= 1
- }
-
- if x > max || 0xd800 <= x && x <= 0xdfff {
- error_offset(t, offset, "escape sequence is an invalid Unicode code point")
- return false
- }
- return true
-}
-
-scan_rune :: proc(t: ^Tokenizer) -> string {
- offset := t.offset-1
- valid := true
- n := 0
- for {
- ch := t.ch
- if ch == '\n' || ch < 0 {
- if valid {
- error_offset(t, offset, "rune literal not terminated")
- valid = false
- }
- break
- }
- advance_rune(t)
- if ch == '\'' {
- break
- }
- n += 1
- if ch == '\\' {
- if !scan_escape(t) {
- valid = false
- }
- }
- }
-
- if valid && n != 1 {
- error_offset(t, offset, "illegal rune literal")
- }
-
- return string(t.src[offset : t.offset])
-}
-
-scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Token_Kind, string) {
- scan_mantissa :: proc(t: ^Tokenizer, base: int) {
- for digit_val(t.ch) < base {
- advance_rune(t)
- }
- }
- scan_exponent :: proc(t: ^Tokenizer) {
- if t.ch == 'e' || t.ch == 'E' || t.ch == 'p' || t.ch == 'P' {
- advance_rune(t)
- if t.ch == '-' || t.ch == '+' {
- advance_rune(t)
- }
- if digit_val(t.ch) < 10 {
- scan_mantissa(t, 10)
- } else {
- error_offset(t, t.offset, "illegal floating-point exponent")
- }
- }
- }
- scan_fraction :: proc(t: ^Tokenizer) -> (early_exit: bool) {
- if t.ch == '.' && peek(t) == '.' {
- return true
- }
- if t.ch == '.' {
- advance_rune(t)
- scan_mantissa(t, 10)
- }
- return false
- }
-
- check_end := true
-
-
- offset := t.offset
- seen_point := seen_decimal_point
-
- if seen_point {
- offset -= 1
- scan_mantissa(t, 10)
- scan_exponent(t)
- } else {
- if t.ch == '0' {
- int_base :: proc(t: ^Tokenizer, base: int, msg: string) {
- prev := t.offset
- advance_rune(t)
- scan_mantissa(t, base)
- if t.offset - prev <= 1 {
- error_offset(t, t.offset, msg)
- }
- }
-
- advance_rune(t)
- switch t.ch {
- case 'b', 'B':
- int_base(t, 2, "illegal binary integer")
- case 'x', 'X':
- int_base(t, 16, "illegal hexadecimal integer")
- case:
- seen_point = false
- scan_mantissa(t, 10)
- if t.ch == '.' {
- seen_point = true
- if scan_fraction(t) {
- check_end = false
- }
- }
- if check_end {
- scan_exponent(t)
- check_end = false
- }
- }
- }
- }
-
- if check_end {
- scan_mantissa(t, 10)
-
- if !scan_fraction(t) {
- scan_exponent(t)
- }
- }
-
- return .Number, string(t.src[offset : t.offset])
-}
-
-scan_punct :: proc(t: ^Tokenizer, ch: rune) -> (kind: Token_Kind) {
- kind = .Punct
- switch ch {
- case:
- kind = .Invalid
-
- case '<', '>':
- if t.ch == ch {
- advance_rune(t)
- }
- if t.ch == '=' {
- advance_rune(t)
- }
- case '!', '+', '-', '*', '/', '%', '^', '=':
- if t.ch == '=' {
- advance_rune(t)
- }
- case '#':
- if t.ch == '#' {
- advance_rune(t)
- }
- case '&':
- if t.ch == '=' || t.ch == '&' {
- advance_rune(t)
- }
- case '|':
- if t.ch == '=' || t.ch == '|' {
- advance_rune(t)
- }
- case '(', ')', '[', ']', '{', '}':
- // okay
- case '~', ',', ':', ';', '?':
- // okay
- case '`':
- // okay
- case '.':
- if t.ch == '.' && peek(t) == '.' {
- advance_rune(t)
- advance_rune(t) // consume last '.'
- }
- }
- return
-}
-
-peek :: proc(t: ^Tokenizer) -> byte {
- if t.read_offset < len(t.src) {
- return t.src[t.read_offset]
- }
- return 0
-}
-peek_str :: proc(t: ^Tokenizer, str: string) -> bool {
- if t.read_offset < len(t.src) {
- return strings.has_prefix(string(t.src[t.offset:]), str)
- }
- return false
-}
-
-scan_literal_prefix :: proc(t: ^Tokenizer, str: string, prefix: ^string) -> bool {
- if peek_str(t, str) {
- offset := t.offset
- for _ in str {
- advance_rune(t)
- }
- prefix^ = string(t.src[offset:][:len(str)-1])
- return true
- }
- return false
-}
-
-
-allow_next_to_be_newline :: proc(t: ^Tokenizer) -> bool {
- if t.ch == '\n' {
- advance_rune(t)
- return true
- } else if t.ch == '\r' && peek(t) == '\n' { // allow for MS-DOS style line endings
- advance_rune(t) // \r
- advance_rune(t) // \n
- return true
- }
- return false
-}
-
-scan :: proc(t: ^Tokenizer, f: ^File) -> ^Token {
- skip_whitespace(t)
-
- offset := t.offset
-
- kind: Token_Kind
- lit: string
- prefix: string
-
- switch ch := t.ch; {
- case scan_literal_prefix(t, `u8"`, &prefix):
- kind = .String
- lit = scan_string(t)
- case scan_literal_prefix(t, `u"`, &prefix):
- kind = .String
- lit = scan_string(t)
- case scan_literal_prefix(t, `L"`, &prefix):
- kind = .String
- lit = scan_string(t)
- case scan_literal_prefix(t, `U"`, &prefix):
- kind = .String
- lit = scan_string(t)
- case scan_literal_prefix(t, `u'`, &prefix):
- kind = .Char
- lit = scan_rune(t)
- case scan_literal_prefix(t, `L'`, &prefix):
- kind = .Char
- lit = scan_rune(t)
- case scan_literal_prefix(t, `U'`, &prefix):
- kind = .Char
- lit = scan_rune(t)
-
- case is_ident0(ch):
- lit = scan_identifier(t)
- kind = .Ident
- case '0' <= ch && ch <= '9':
- kind, lit = scan_number(t, false)
- case:
- advance_rune(t)
- switch ch {
- case -1:
- kind = .EOF
- case '\\':
- kind = .Punct
- if allow_next_to_be_newline(t) {
- t.at_bol = true
- t.has_space = false
- return scan(t, f)
- }
-
- case '.':
- if is_digit(t.ch) {
- kind, lit = scan_number(t, true)
- } else {
- kind = scan_punct(t, ch)
- }
- case '"':
- kind = .String
- lit = scan_string(t)
- case '\'':
- kind = .Char
- lit = scan_rune(t)
- case '/':
- if t.ch == '/' || t.ch == '*' {
- kind = .Comment
- lit = scan_comment(t)
- t.has_space = true
- break
- }
- fallthrough
- case:
- kind = scan_punct(t, ch)
- if kind == .Invalid && ch != utf8.RUNE_BOM {
- error_offset(t, t.offset, "illegal character '%r': %d", ch, ch)
- }
- }
- }
-
- if lit == "" {
- lit = string(t.src[offset : t.offset])
- }
-
- if kind == .Comment {
- return scan(t, f)
- }
-
- tok := new(Token)
- tok.kind = kind
- tok.lit = lit
- tok.pos = offset_to_pos(t, offset)
- tok.file = f
- tok.prefix = prefix
- tok.at_bol = t.at_bol
- tok.has_space = t.has_space
-
- t.at_bol, t.has_space = false, false
-
- return tok
-}
-
-tokenize :: proc(t: ^Tokenizer, f: ^File) -> ^Token {
- setup_tokenizer: {
- t.src = f.src
- t.ch = ' '
- t.offset = 0
- t.read_offset = 0
- t.line_offset = 0
- t.line_count = len(t.src) > 0 ? 1 : 0
- t.error_count = 0
- t.path = f.name
-
-
- advance_rune(t)
- if t.ch == utf8.RUNE_BOM {
- advance_rune(t)
- }
- }
-
-
- t.at_bol = true
- t.has_space = false
-
- head: Token
- curr := &head
- for {
- tok := scan(t, f)
- if tok == nil {
- break
- }
- curr.next = tok
- curr = curr.next
- if tok.kind == .EOF {
- break
- }
- }
-
- return head.next
-}
-
-add_new_file :: proc(t: ^Tokenizer, name: string, src: []byte, id: int) -> ^File {
- file := new(File)
- file.id = id
- file.src = src
- file.name = name
- file.display_name = name
- return file
-}
-
-tokenize_file :: proc(t: ^Tokenizer, path: string, id: int, loc := #caller_location) -> ^Token {
- src, ok := os.read_entire_file(path)
- if !ok {
- return nil
- }
- return tokenize(t, add_new_file(t, path, src, id))
-}
-
-
-inline_tokenize :: proc(t: ^Tokenizer, tok: ^Token, src: []byte) -> ^Token {
- file := new(File)
- file.src = src
- if tok.file != nil {
- file.id = tok.file.id
- file.name = tok.file.name
- file.display_name = tok.file.name
- }
-
- return tokenize(t, file)
-}
diff --git a/core/c/frontend/tokenizer/unicode.odin b/core/c/frontend/tokenizer/unicode.odin
deleted file mode 100644
index 317ee160e..000000000
--- a/core/c/frontend/tokenizer/unicode.odin
+++ /dev/null
@@ -1,116 +0,0 @@
-package c_frontend_tokenizer
-
-
-in_range :: proc(range: []rune, c: rune) -> bool #no_bounds_check {
- for i := 0; range[i] != -1; i += 2 {
- if range[i] <= c && c <= range[i+1] {
- return true
- }
- }
- return false
-}
-
-
-// [https://www.sigbus.info/n1570#D] C11 allows ASCII and some multibyte characters in certan Unicode ranges to be used in an identifier.
-//
-// is_ident0 returns true if a given character is acceptable as the first character of an identifier.
-is_ident0 :: proc(c: rune) -> bool {
- return in_range(_range_ident0, c)
-}
-// is_ident0 returns true if a given character is acceptable as a non-first character of an identifier.
-is_ident1 :: proc(c: rune) -> bool {
- return is_ident0(c) || in_range(_range_ident1, c)
-}
-
-// Returns the number of columns needed to display a given character in a fixed-width font.
-// Based on https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
-char_width :: proc(c: rune) -> int {
- switch {
- case in_range(_range_width0, c):
- return 0
- case in_range(_range_width2, c):
- return 2
- }
- return 1
-}
-
-display_width :: proc(str: string) -> (w: int) {
- for c in str {
- w += char_width(c)
- }
- return
-}
-
-
-
-_range_ident0 := []rune{
- '_', '_', 'a', 'z', 'A', 'Z', '$', '$',
- 0x00A8, 0x00A8, 0x00AA, 0x00AA, 0x00AD, 0x00AD, 0x00AF, 0x00AF,
- 0x00B2, 0x00B5, 0x00B7, 0x00BA, 0x00BC, 0x00BE, 0x00C0, 0x00D6,
- 0x00D8, 0x00F6, 0x00F8, 0x00FF, 0x0100, 0x02FF, 0x0370, 0x167F,
- 0x1681, 0x180D, 0x180F, 0x1DBF, 0x1E00, 0x1FFF, 0x200B, 0x200D,
- 0x202A, 0x202E, 0x203F, 0x2040, 0x2054, 0x2054, 0x2060, 0x206F,
- 0x2070, 0x20CF, 0x2100, 0x218F, 0x2460, 0x24FF, 0x2776, 0x2793,
- 0x2C00, 0x2DFF, 0x2E80, 0x2FFF, 0x3004, 0x3007, 0x3021, 0x302F,
- 0x3031, 0x303F, 0x3040, 0xD7FF, 0xF900, 0xFD3D, 0xFD40, 0xFDCF,
- 0xFDF0, 0xFE1F, 0xFE30, 0xFE44, 0xFE47, 0xFFFD,
- 0x10000, 0x1FFFD, 0x20000, 0x2FFFD, 0x30000, 0x3FFFD, 0x40000, 0x4FFFD,
- 0x50000, 0x5FFFD, 0x60000, 0x6FFFD, 0x70000, 0x7FFFD, 0x80000, 0x8FFFD,
- 0x90000, 0x9FFFD, 0xA0000, 0xAFFFD, 0xB0000, 0xBFFFD, 0xC0000, 0xCFFFD,
- 0xD0000, 0xDFFFD, 0xE0000, 0xEFFFD,
- -1,
-}
-
-_range_ident1 := []rune{
- '0', '9', '$', '$', 0x0300, 0x036F, 0x1DC0, 0x1DFF, 0x20D0, 0x20FF, 0xFE20, 0xFE2F,
- -1,
-}
-
-
-_range_width0 := []rune{
- 0x0000, 0x001F, 0x007f, 0x00a0, 0x0300, 0x036F, 0x0483, 0x0486,
- 0x0488, 0x0489, 0x0591, 0x05BD, 0x05BF, 0x05BF, 0x05C1, 0x05C2,
- 0x05C4, 0x05C5, 0x05C7, 0x05C7, 0x0600, 0x0603, 0x0610, 0x0615,
- 0x064B, 0x065E, 0x0670, 0x0670, 0x06D6, 0x06E4, 0x06E7, 0x06E8,
- 0x06EA, 0x06ED, 0x070F, 0x070F, 0x0711, 0x0711, 0x0730, 0x074A,
- 0x07A6, 0x07B0, 0x07EB, 0x07F3, 0x0901, 0x0902, 0x093C, 0x093C,
- 0x0941, 0x0948, 0x094D, 0x094D, 0x0951, 0x0954, 0x0962, 0x0963,
- 0x0981, 0x0981, 0x09BC, 0x09BC, 0x09C1, 0x09C4, 0x09CD, 0x09CD,
- 0x09E2, 0x09E3, 0x0A01, 0x0A02, 0x0A3C, 0x0A3C, 0x0A41, 0x0A42,
- 0x0A47, 0x0A48, 0x0A4B, 0x0A4D, 0x0A70, 0x0A71, 0x0A81, 0x0A82,
- 0x0ABC, 0x0ABC, 0x0AC1, 0x0AC5, 0x0AC7, 0x0AC8, 0x0ACD, 0x0ACD,
- 0x0AE2, 0x0AE3, 0x0B01, 0x0B01, 0x0B3C, 0x0B3C, 0x0B3F, 0x0B3F,
- 0x0B41, 0x0B43, 0x0B4D, 0x0B4D, 0x0B56, 0x0B56, 0x0B82, 0x0B82,
- 0x0BC0, 0x0BC0, 0x0BCD, 0x0BCD, 0x0C3E, 0x0C40, 0x0C46, 0x0C48,
- 0x0C4A, 0x0C4D, 0x0C55, 0x0C56, 0x0CBC, 0x0CBC, 0x0CBF, 0x0CBF,
- 0x0CC6, 0x0CC6, 0x0CCC, 0x0CCD, 0x0CE2, 0x0CE3, 0x0D41, 0x0D43,
- 0x0D4D, 0x0D4D, 0x0DCA, 0x0DCA, 0x0DD2, 0x0DD4, 0x0DD6, 0x0DD6,
- 0x0E31, 0x0E31, 0x0E34, 0x0E3A, 0x0E47, 0x0E4E, 0x0EB1, 0x0EB1,
- 0x0EB4, 0x0EB9, 0x0EBB, 0x0EBC, 0x0EC8, 0x0ECD, 0x0F18, 0x0F19,
- 0x0F35, 0x0F35, 0x0F37, 0x0F37, 0x0F39, 0x0F39, 0x0F71, 0x0F7E,
- 0x0F80, 0x0F84, 0x0F86, 0x0F87, 0x0F90, 0x0F97, 0x0F99, 0x0FBC,
- 0x0FC6, 0x0FC6, 0x102D, 0x1030, 0x1032, 0x1032, 0x1036, 0x1037,
- 0x1039, 0x1039, 0x1058, 0x1059, 0x1160, 0x11FF, 0x135F, 0x135F,
- 0x1712, 0x1714, 0x1732, 0x1734, 0x1752, 0x1753, 0x1772, 0x1773,
- 0x17B4, 0x17B5, 0x17B7, 0x17BD, 0x17C6, 0x17C6, 0x17C9, 0x17D3,
- 0x17DD, 0x17DD, 0x180B, 0x180D, 0x18A9, 0x18A9, 0x1920, 0x1922,
- 0x1927, 0x1928, 0x1932, 0x1932, 0x1939, 0x193B, 0x1A17, 0x1A18,
- 0x1B00, 0x1B03, 0x1B34, 0x1B34, 0x1B36, 0x1B3A, 0x1B3C, 0x1B3C,
- 0x1B42, 0x1B42, 0x1B6B, 0x1B73, 0x1DC0, 0x1DCA, 0x1DFE, 0x1DFF,
- 0x200B, 0x200F, 0x202A, 0x202E, 0x2060, 0x2063, 0x206A, 0x206F,
- 0x20D0, 0x20EF, 0x302A, 0x302F, 0x3099, 0x309A, 0xA806, 0xA806,
- 0xA80B, 0xA80B, 0xA825, 0xA826, 0xFB1E, 0xFB1E, 0xFE00, 0xFE0F,
- 0xFE20, 0xFE23, 0xFEFF, 0xFEFF, 0xFFF9, 0xFFFB, 0x10A01, 0x10A03,
- 0x10A05, 0x10A06, 0x10A0C, 0x10A0F, 0x10A38, 0x10A3A, 0x10A3F, 0x10A3F,
- 0x1D167, 0x1D169, 0x1D173, 0x1D182, 0x1D185, 0x1D18B, 0x1D1AA, 0x1D1AD,
- 0x1D242, 0x1D244, 0xE0001, 0xE0001, 0xE0020, 0xE007F, 0xE0100, 0xE01EF,
- -1,
-}
-
-_range_width2 := []rune{
- 0x1100, 0x115F, 0x2329, 0x2329, 0x232A, 0x232A, 0x2E80, 0x303E,
- 0x3040, 0xA4CF, 0xAC00, 0xD7A3, 0xF900, 0xFAFF, 0xFE10, 0xFE19,
- 0xFE30, 0xFE6F, 0xFF00, 0xFF60, 0xFFE0, 0xFFE6, 0x1F000, 0x1F644,
- 0x20000, 0x2FFFD, 0x30000, 0x3FFFD,
- -1,
-}
diff --git a/core/c/libc/errno.odin b/core/c/libc/errno.odin
index de429a6ec..5d1ca8248 100644
--- a/core/c/libc/errno.odin
+++ b/core/c/libc/errno.odin
@@ -88,14 +88,15 @@ when ODIN_OS == .Haiku {
_get_errno :: proc() -> ^int ---
}
- @(private="file")
- B_GENERAL_ERROR_BASE :: min(i32)
- @(private="file")
- B_POSIX_ERROR_BASE :: B_GENERAL_ERROR_BASE + 0x7000
+ _HAIKU_USE_POSITIVE_POSIX_ERRORS :: #config(HAIKU_USE_POSITIVE_POSIX_ERRORS, false)
+ _POSIX_ERROR_FACTOR :: -1 when _HAIKU_USE_POSITIVE_POSIX_ERRORS else 1
- EDOM :: B_POSIX_ERROR_BASE + 16
- EILSEQ :: B_POSIX_ERROR_BASE + 38
- ERANGE :: B_POSIX_ERROR_BASE + 17
+ @(private="file") _GENERAL_ERROR_BASE :: min(int)
+ @(private="file") _POSIX_ERROR_BASE :: _GENERAL_ERROR_BASE + 0x7000
+
+ EDOM :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 16)
+ EILSEQ :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 38)
+ ERANGE :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 17)
}
when ODIN_OS == .JS {
diff --git a/core/c/libc/locale.odin b/core/c/libc/locale.odin
index 371d755c5..d95f5c164 100644
--- a/core/c/libc/locale.odin
+++ b/core/c/libc/locale.odin
@@ -110,7 +110,7 @@ when ODIN_OS == .Windows {
}
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Windows {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku || ODIN_OS == .Windows {
LC_ALL :: 0
LC_COLLATE :: 1
diff --git a/core/c/libc/stdio.odin b/core/c/libc/stdio.odin
index a94a53696..56e4e8f66 100644
--- a/core/c/libc/stdio.odin
+++ b/core/c/libc/stdio.odin
@@ -1,5 +1,6 @@
package libc
+import "core:c"
import "core:io"
when ODIN_OS == .Windows {
@@ -15,7 +16,7 @@ when ODIN_OS == .Windows {
// 7.21 Input/output
-FILE :: struct {}
+FILE :: c.FILE
Whence :: enum int {
SET = SEEK_SET,
diff --git a/core/c/libc/stdlib.odin b/core/c/libc/stdlib.odin
index 98280e44b..c0e273872 100644
--- a/core/c/libc/stdlib.odin
+++ b/core/c/libc/stdlib.odin
@@ -42,6 +42,21 @@ when ODIN_OS == .Linux {
}
}
+when ODIN_OS == .Haiku {
+ RAND_MAX :: 0x7fffffff
+
+ // GLIBC and MUSL only
+ @(private="file")
+ @(default_calling_convention="c")
+ foreign libc {
+ __ctype_get_mb_cur_max :: proc() -> ushort ---
+ }
+
+ MB_CUR_MAX :: #force_inline proc() -> size_t {
+ return size_t(__ctype_get_mb_cur_max())
+ }
+}
+
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD {
RAND_MAX :: 0x7fffffff
diff --git a/core/c/libc/time.odin b/core/c/libc/time.odin
index 6828793ec..33f8dc3af 100644
--- a/core/c/libc/time.odin
+++ b/core/c/libc/time.odin
@@ -95,7 +95,7 @@ when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS =
time_t :: distinct i64
- when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD {
+ when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku {
clock_t :: distinct int32_t
} else {
clock_t :: distinct long
diff --git a/core/container/intrusive/list/intrusive_list.odin b/core/container/intrusive/list/intrusive_list.odin
index 5b29efb22..1e116ef18 100644
--- a/core/container/intrusive/list/intrusive_list.odin
+++ b/core/container/intrusive/list/intrusive_list.odin
@@ -278,19 +278,19 @@ Example:
iterate_next_example :: proc() {
l: list.List
- one := My_Struct{value=1}
- two := My_Struct{value=2}
+ one := My_Next_Struct{value=1}
+ two := My_Next_Struct{value=2}
list.push_back(&l, &one.node)
list.push_back(&l, &two.node)
- it := list.iterator_head(l, My_Struct, "node")
+ it := list.iterator_head(l, My_Next_Struct, "node")
for num in list.iterate_next(&it) {
fmt.println(num.value)
}
}
- My_Struct :: struct {
+ My_Next_Struct :: struct {
node : list.Node,
value: int,
}
@@ -325,22 +325,22 @@ Example:
import "core:fmt"
import "core:container/intrusive/list"
- iterate_next_example :: proc() {
+ iterate_prev_example :: proc() {
l: list.List
- one := My_Struct{value=1}
- two := My_Struct{value=2}
+ one := My_Prev_Struct{value=1}
+ two := My_Prev_Struct{value=2}
list.push_back(&l, &one.node)
list.push_back(&l, &two.node)
- it := list.iterator_tail(l, My_Struct, "node")
+ it := list.iterator_tail(l, My_Prev_Struct, "node")
for num in list.iterate_prev(&it) {
fmt.println(num.value)
}
}
- My_Struct :: struct {
+ My_Prev_Struct :: struct {
node : list.Node,
value: int,
}
diff --git a/core/container/priority_queue/priority_queue.odin b/core/container/priority_queue/priority_queue.odin
index 8a6d77288..7387a8d09 100644
--- a/core/container/priority_queue/priority_queue.odin
+++ b/core/container/priority_queue/priority_queue.odin
@@ -1,6 +1,7 @@
package container_priority_queue
import "base:builtin"
+import "base:runtime"
Priority_Queue :: struct($T: typeid) {
queue: [dynamic]T,
@@ -17,13 +18,14 @@ default_swap_proc :: proc($T: typeid) -> proc(q: []T, i, j: int) {
}
}
-init :: proc(pq: ^$Q/Priority_Queue($T), less: proc(a, b: T) -> bool, swap: proc(q: []T, i, j: int), capacity := DEFAULT_CAPACITY, allocator := context.allocator) {
+init :: proc(pq: ^$Q/Priority_Queue($T), less: proc(a, b: T) -> bool, swap: proc(q: []T, i, j: int), capacity := DEFAULT_CAPACITY, allocator := context.allocator) -> (err: runtime.Allocator_Error) {
if pq.queue.allocator.procedure == nil {
pq.queue.allocator = allocator
}
- reserve(pq, capacity)
+ reserve(pq, capacity) or_return
pq.less = less
pq.swap = swap
+ return .None
}
init_from_dynamic_array :: proc(pq: ^$Q/Priority_Queue($T), queue: [dynamic]T, less: proc(a, b: T) -> bool, swap: proc(q: []T, i, j: int)) {
@@ -41,8 +43,8 @@ destroy :: proc(pq: ^$Q/Priority_Queue($T)) {
delete(pq.queue)
}
-reserve :: proc(pq: ^$Q/Priority_Queue($T), capacity: int) {
- builtin.reserve(&pq.queue, capacity)
+reserve :: proc(pq: ^$Q/Priority_Queue($T), capacity: int) -> (err: runtime.Allocator_Error) {
+ return builtin.reserve(&pq.queue, capacity)
}
clear :: proc(pq: ^$Q/Priority_Queue($T)) {
builtin.clear(&pq.queue)
@@ -103,9 +105,10 @@ fix :: proc(pq: ^$Q/Priority_Queue($T), i: int) {
}
}
-push :: proc(pq: ^$Q/Priority_Queue($T), value: T) {
- append(&pq.queue, value)
+push :: proc(pq: ^$Q/Priority_Queue($T), value: T) -> (err: runtime.Allocator_Error) {
+ append(&pq.queue, value) or_return
_shift_up(pq, builtin.len(pq.queue)-1)
+ return .None
}
pop :: proc(pq: ^$Q/Priority_Queue($T), loc := #caller_location) -> (value: T) {
diff --git a/core/container/queue/queue.odin b/core/container/queue/queue.odin
index f83a5f2b7..d1040a7c9 100644
--- a/core/container/queue/queue.odin
+++ b/core/container/queue/queue.odin
@@ -46,8 +46,7 @@ init_with_contents :: proc(q: ^$Q/Queue($T), backing: []T) -> bool {
cap = builtin.len(backing),
allocator = {procedure=runtime.nil_allocator_proc, data=nil},
}
- q.len = len(backing)
- q.offset = len(backing)
+ q.len = builtin.len(backing)
return true
}
diff --git a/core/container/small_array/doc.odin b/core/container/small_array/doc.odin
new file mode 100644
index 000000000..f3e9acd57
--- /dev/null
+++ b/core/container/small_array/doc.odin
@@ -0,0 +1,55 @@
+/*
+Package small_array implements a dynamic array like
+interface on a stack-allocated, fixed-size array.
+
+The Small_Array type is optimal for scenarios where you need
+a container for a fixed number of elements of a specific type,
+with the total number known at compile time but the exact
+number to be used determined at runtime.
+
+Example:
+ import "core:fmt"
+ import "core:container/small_array"
+
+ create :: proc() -> (result: small_array.Small_Array(10, rune)) {
+ // appending single elements
+ small_array.push(&result, 'e')
+ // pushing a bunch of elements at once
+ small_array.push(&result, 'l', 'i', 'x', '-', 'e')
+ // pre-pending
+ small_array.push_front(&result, 'H')
+ // removing elements
+ small_array.ordered_remove(&result, 4)
+ // resizing to the desired length (the capacity will stay unchanged)
+ small_array.resize(&result, 7)
+ // inserting elements
+ small_array.inject_at(&result, 'p', 5)
+ // updating elements
+ small_array.set(&result, 3, 'l')
+ // getting pointers to elements
+ o := small_array.get_ptr(&result, 4)
+ o^ = 'o'
+ // and much more ....
+ return
+ }
+
+ // the Small_Array can be an ordinary parameter 'generic' over
+ // the actual length to be usable with different sizes
+ print_elements :: proc(arr: ^small_array.Small_Array($N, rune)) {
+ for r in small_array.slice(arr) {
+ fmt.print(r)
+ }
+ }
+
+ main :: proc() {
+ arr := create()
+ // ...
+ print_elements(&arr)
+ }
+
+Output:
+
+ Hellope
+
+*/
+package container_small_array
diff --git a/core/container/small_array/small_array.odin b/core/container/small_array/small_array.odin
index 77bb21cbc..49d441079 100644
--- a/core/container/small_array/small_array.odin
+++ b/core/container/small_array/small_array.odin
@@ -4,36 +4,171 @@ import "base:builtin"
import "base:runtime"
_ :: runtime
+/*
+A fixed-size stack-allocated array operated on in a dynamic fashion.
+
+Fields:
+- `data`: The underlying array
+- `len`: Amount of items that the `Small_Array` currently holds
+
+Example:
+
+ import "core:container/small_array"
+
+ example :: proc() {
+ a: small_array.Small_Array(100, int)
+ small_array.push_back(&a, 10)
+ }
+*/
Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
data: [N]T,
len: int,
}
+/*
+Returns the amount of items in the small-array.
+**Inputs**
+- `a`: The small-array
+
+**Returns**
+- the amount of items in the array
+*/
len :: proc "contextless" (a: $A/Small_Array) -> int {
return a.len
}
+/*
+Returns the capacity of the small-array.
+
+**Inputs**
+- `a`: The small-array
+
+**Returns** the capacity
+*/
cap :: proc "contextless" (a: $A/Small_Array) -> int {
return builtin.len(a.data)
}
+/*
+Returns how many more items the small-array could fit.
+
+**Inputs**
+- `a`: The small-array
+
+**Returns**
+- the number of unused slots
+*/
space :: proc "contextless" (a: $A/Small_Array) -> int {
return builtin.len(a.data) - a.len
}
+/*
+Returns a slice of the data.
+
+**Inputs**
+- `a`: The pointer to the small-array
+
+**Returns**
+- the slice
+
+Example:
+
+ import "core:container/small_array"
+ import "core:fmt"
+
+ slice_example :: proc() {
+ print :: proc(a: ^small_array.Small_Array($N, int)) {
+ for item in small_array.slice(a) {
+ fmt.println(item)
+ }
+ }
+
+ a: small_array.Small_Array(5, int)
+ small_array.push_back(&a, 1)
+ small_array.push_back(&a, 2)
+ print(&a)
+ }
+
+Output:
+
+ 1
+ 2
+*/
slice :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> []T {
return a.data[:a.len]
}
+/*
+Get a copy of the item at the specified position.
+This operation assumes that the small-array is large enough.
+This will result in:
+ - the value if 0 <= index < len
+ - the zero value of the type if len < index < capacity
+ - 'crash' if capacity < index or index < 0
+
+**Inputs**
+- `a`: The small-array
+- `index`: The position of the item to get
+
+**Returns**
+- the element at the specified position
+*/
get :: proc "contextless" (a: $A/Small_Array($N, $T), index: int) -> T {
return a.data[index]
}
+
+/*
+Get a pointer to the item at the specified position.
+This operation assumes that the small-array is large enough.
+
+This will result in:
+ - the pointer if 0 <= index < len
+ - the pointer to the zero value if len < index < capacity
+ - 'crash' if capacity < index or index < 0
+
+**Inputs**
+- `a`: A pointer to the small-array
+- `index`: The position of the item to get
+
+**Returns**
+- the pointer to the element at the specified position
+*/
get_ptr :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int) -> ^T {
return &a.data[index]
}
+/*
+Attempt to get a copy of the item at the specified position.
+
+**Inputs**
+- `a`: The small-array
+- `index`: The position of the item to get
+
+**Returns**
+- the element at the specified position
+- true if element exists, false otherwise
+
+Example:
+
+ import "core:container/small_array"
+ import "core:fmt"
+
+ get_safe_example :: proc() {
+ a: small_array.Small_Array(5, rune)
+ small_array.push_back(&a, 'A')
+
+ fmt.println(small_array.get_safe(a, 0) or_else 'x')
+ fmt.println(small_array.get_safe(a, 1) or_else 'x')
+ }
+
+Output:
+
+ A
+ x
+
+*/
get_safe :: proc(a: $A/Small_Array($N, $T), index: int) -> (T, bool) #no_bounds_check {
if index < 0 || index >= a.len {
return {}, false
@@ -41,6 +176,17 @@ get_safe :: proc(a: $A/Small_Array($N, $T), index: int) -> (T, bool) #no_bounds_
return a.data[index], true
}
+/*
+Get a pointer to the item at the specified position.
+
+**Inputs**
+- `a`: A pointer to the small-array
+- `index`: The position of the item to get
+
+**Returns**
+- the pointer to the element at the specified position
+- true if element exists, false otherwise
+*/
get_ptr_safe :: proc(a: ^$A/Small_Array($N, $T), index: int) -> (^T, bool) #no_bounds_check {
if index < 0 || index >= a.len {
return {}, false
@@ -48,15 +194,128 @@ get_ptr_safe :: proc(a: ^$A/Small_Array($N, $T), index: int) -> (^T, bool) #no_b
return &a.data[index], true
}
+/*
+Set the element at the specified position to the given value.
+This operation assumes that the small-array is large enough.
+
+This will result in:
+ - the value being set if 0 <= index < capacity
+ - 'crash' otherwise
+
+**Inputs**
+- `a`: A pointer to the small-array
+- `index`: The position of the item to set
+- `value`: The value to set the element to
+
+Example:
+
+ import "core:container/small_array"
+ import "core:fmt"
+
+ set_example :: proc() {
+ a: small_array.Small_Array(5, rune)
+ small_array.push_back(&a, 'A')
+ small_array.push_back(&a, 'B')
+ fmt.println(small_array.slice(&a))
+
+ // updates index 0
+ small_array.set(&a, 0, 'Z')
+ fmt.println(small_array.slice(&a))
+
+ // updates to a position x, where
+ // len <= x < cap are not visible since
+ // the length of the small-array remains unchanged
+ small_array.set(&a, 2, 'X')
+ small_array.set(&a, 3, 'Y')
+ small_array.set(&a, 4, 'Z')
+ fmt.println(small_array.slice(&a))
+
+ // resizing makes the change visible
+ small_array.resize(&a, 100)
+ fmt.println(small_array.slice(&a))
+ }
+
+Output:
+
+ [A, B]
+ [Z, B]
+ [Z, B]
+ [Z, B, X, Y, Z]
+
+*/
set :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, item: T) {
a.data[index] = item
}
+/*
+Tries to resize the small-array to the specified length.
+
+The new length will be:
+ - `length` if `length` <= capacity
+ - capacity if length > capacity
+
+**Inputs**
+- `a`: A pointer to the small-array
+- `length`: The new desired length
+
+Example:
+
+ import "core:container/small_array"
+ import "core:fmt"
+
+ resize_example :: proc() {
+ a: small_array.Small_Array(5, int)
+
+ small_array.push_back(&a, 1)
+ small_array.push_back(&a, 2)
+ fmt.println(small_array.slice(&a))
+
+ small_array.resize(&a, 1)
+ fmt.println(small_array.slice(&a))
+
+ small_array.resize(&a, 100)
+ fmt.println(small_array.slice(&a))
+ }
+
+Output:
+
+ [1, 2]
+ [1]
+ [1, 2, 0, 0, 0]
+*/
resize :: proc "contextless" (a: ^$A/Small_Array, length: int) {
a.len = min(length, builtin.len(a.data))
}
+/*
+Attempts to add the given element to the end.
+**Inputs**
+- `a`: A pointer to the small-array
+- `item`: The item to append
+
+**Returns**
+- true if there was enough space to fit the element, false otherwise
+
+Example:
+
+ import "core:container/small_array"
+ import "core:fmt"
+
+ push_back_example :: proc() {
+ a: small_array.Small_Array(2, int)
+
+ assert(small_array.push_back(&a, 1), "this should fit")
+ assert(small_array.push_back(&a, 2), "this should fit")
+ assert(!small_array.push_back(&a, 3), "this should not fit")
+
+ fmt.println(small_array.slice(&a))
+ }
+
+Output:
+
+ [1, 2]
+*/
push_back :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T) -> bool {
if a.len < cap(a^) {
a.data[a.len] = item
@@ -66,6 +325,39 @@ push_back :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T) -> bool {
return false
}
+/*
+Attempts to add the given element at the beginning.
+This operation assumes that the small-array is not empty.
+
+Note: Performing this operation will cause pointers obtained
+through get_ptr(_save) to reference incorrect elements.
+
+**Inputs**
+- `a`: A pointer to the small-array
+- `item`: The item to append
+
+**Returns**
+- true if there was enough space to fit the element, false otherwise
+
+Example:
+
+ import "core:container/small_array"
+ import "core:fmt"
+
+ push_front_example :: proc() {
+ a: small_array.Small_Array(2, int)
+
+ assert(small_array.push_front(&a, 2), "this should fit")
+ assert(small_array.push_front(&a, 1), "this should fit")
+ assert(!small_array.push_back(&a, 0), "this should not fit")
+
+ fmt.println(small_array.slice(&a))
+ }
+
+Output:
+
+ [1, 2]
+*/
push_front :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T) -> bool {
if a.len < cap(a^) {
a.len += 1
@@ -77,6 +369,35 @@ push_front :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T) -> bool {
return false
}
+/*
+Removes and returns the last element of the small-array.
+This operation assumes that the small-array is not empty.
+
+**Inputs**
+- `a`: A pointer to the small-array
+
+**Returns**
+- a copy of the element removed from the end of the small-array
+
+Example:
+
+ import "core:container/small_array"
+ import "core:fmt"
+
+ pop_back_example :: proc() {
+ a: small_array.Small_Array(5, int)
+ small_array.push(&a, 0, 1, 2)
+
+ fmt.println("BEFORE:", small_array.slice(&a))
+ small_array.pop_back(&a)
+ fmt.println("AFTER: ", small_array.slice(&a))
+ }
+
+Output:
+
+ BEFORE: [0, 1, 2]
+ AFTER: [0, 1]
+*/
pop_back :: proc "odin" (a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
assert(condition=(N > 0 && a.len > 0), loc=loc)
item := a.data[a.len-1]
@@ -84,6 +405,38 @@ pop_back :: proc "odin" (a: ^$A/Small_Array($N, $T), loc := #caller_location) ->
return item
}
+/*
+Removes and returns the first element of the small-array.
+This operation assumes that the small-array is not empty.
+
+Note: Performing this operation will cause pointers obtained
+through get_ptr(_save) to reference incorrect elements.
+
+**Inputs**
+- `a`: A pointer to the small-array
+
+**Returns**
+- a copy of the element removed from the beginning of the small-array
+
+Example:
+
+ import "core:container/small_array"
+ import "core:fmt"
+
+ pop_front_example :: proc() {
+ a: small_array.Small_Array(5, int)
+ small_array.push(&a, 0, 1, 2)
+
+ fmt.println("BEFORE:", small_array.slice(&a))
+ small_array.pop_front(&a)
+ fmt.println("AFTER: ", small_array.slice(&a))
+ }
+
+Output:
+
+ BEFORE: [0, 1, 2]
+ AFTER: [1, 2]
+*/
pop_front :: proc "odin" (a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
assert(condition=(N > 0 && a.len > 0), loc=loc)
item := a.data[0]
@@ -93,6 +446,32 @@ pop_front :: proc "odin" (a: ^$A/Small_Array($N, $T), loc := #caller_location) -
return item
}
+/*
+Attempts to remove and return the last element of the small array.
+Unlike `pop_back`, it does not assume that the array is non-empty.
+
+**Inputs**
+- `a`: A pointer to the small-array
+
+**Returns**
+- a copy of the element removed from the end of the small-array
+- true if the small-array was not empty, false otherwise
+
+Example:
+
+ import "core:container/small_array"
+
+ pop_back_safe_example :: proc() {
+ a: small_array.Small_Array(3, int)
+ small_array.push(&a, 1)
+
+ el, ok := small_array.pop_back_safe(&a)
+ assert(ok, "there was an element in the array")
+
+ el, ok = small_array.pop_back_safe(&a)
+ assert(!ok, "there was NO element in the array")
+ }
+*/
pop_back_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
if N > 0 && a.len > 0 {
item = a.data[a.len-1]
@@ -102,6 +481,35 @@ pop_back_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> (item: T, ok
return
}
+/*
+Attempts to remove and return the first element of the small array.
+Unlike `pop_front`, it does not assume that the array is non-empty.
+
+Note: Performing this operation will cause pointers obtained
+through get_ptr(_save) to reference incorrect elements.
+
+**Inputs**
+- `a`: A pointer to the small-array
+
+**Returns**
+- a copy of the element removed from the beginning of the small-array
+- true if the small-array was not empty, false otherwise
+
+Example:
+
+ import "core:container/small_array"
+
+ pop_front_safe_example :: proc() {
+ a: small_array.Small_Array(3, int)
+ small_array.push(&a, 1)
+
+ el, ok := small_array.pop_front_safe(&a)
+ assert(ok, "there was an element in the array")
+
+ el, ok = small_array.pop_front_(&a)
+ assert(!ok, "there was NO element in the array")
+ }
+*/
pop_front_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
if N > 0 && a.len > 0 {
item = a.data[0]
@@ -113,11 +521,70 @@ pop_front_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> (item: T, o
return
}
+/*
+Decreases the length of the small-array by the given amount.
+The elements are therefore not really removed and can be
+recovered by calling `resize`.
+
+Note: This procedure assumes that the array has a sufficient length.
+
+**Inputs**
+- `a`: A pointer to the small-array
+- `count`: The amount the length should be reduced by
+
+Example:
+
+ import "core:container/small_array"
+ import "core:fmt"
+
+ consume_example :: proc() {
+ a: small_array.Small_Array(3, int)
+ small_array.push(&a, 0, 1, 2)
+
+ fmt.println("BEFORE:", small_array.slice(&a))
+ small_array.consume(&a, 2)
+ fmt.println("AFTER :", small_array.slice(&a))
+ }
+
+Output:
+
+ BEFORE: [0, 1, 2]
+ AFTER : [0]
+*/
consume :: proc "odin" (a: ^$A/Small_Array($N, $T), count: int, loc := #caller_location) {
assert(condition=a.len >= count, loc=loc)
a.len -= count
}
+/*
+Removes the element at the specified index while retaining order.
+
+Note: Performing this operation will cause pointers obtained
+through get_ptr(_save) to reference incorrect elements.
+
+**Inputs**
+- `a`: A pointer to the small-array
+- `index`: The position of the element to remove
+
+Example:
+
+ import "core:container/small_array"
+ import "core:fmt"
+
+ ordered_remove_example :: proc() {
+ a: small_array.Small_Array(4, int)
+ small_array.push(&a, 0, 1, 2, 3)
+
+ fmt.println("BEFORE:", small_array.slice(&a))
+ small_array.ordered_remove(&a, 1)
+ fmt.println("AFTER :", small_array.slice(&a))
+ }
+
+Output:
+
+ BEFORE: [0, 1, 2, 3]
+ AFTER : [0, 2, 3]
+*/
ordered_remove :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, loc := #caller_location) #no_bounds_check {
runtime.bounds_check_error_loc(loc, index, a.len)
if index+1 < a.len {
@@ -126,6 +593,32 @@ ordered_remove :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, lo
a.len -= 1
}
+/*
+Removes the element at the specified index without retaining order.
+
+**Inputs**
+- `a`: A pointer to the small-array
+- `index`: The position of the element to remove
+
+Example:
+
+ import "core:container/small_array"
+ import "core:fmt"
+
+ unordered_remove_example :: proc() {
+ a: small_array.Small_Array(4, int)
+ small_array.push(&a, 0, 1, 2, 3)
+
+ fmt.println("BEFORE:", small_array.slice(&a))
+ small_array.unordered_remove(&a, 1)
+ fmt.println("AFTER :", small_array.slice(&a))
+ }
+
+Output:
+
+ BEFORE: [0, 1, 2, 3]
+ AFTER : [0, 3, 2]
+*/
unordered_remove :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, loc := #caller_location) #no_bounds_check {
runtime.bounds_check_error_loc(loc, index, a.len)
n := a.len-1
@@ -135,10 +628,63 @@ unordered_remove :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int,
a.len -= 1
}
+/*
+Sets the length of the small-array to 0.
+
+**Inputs**
+- `a`: A pointer to the small-array
+
+Example:
+
+ import "core:container/small_array"
+ import "core:fmt"
+
+ clear_example :: proc() {
+ a: small_array.Small_Array(4, int)
+ small_array.push(&a, 0, 1, 2, 3)
+
+ fmt.println("BEFORE:", small_array.slice(&a))
+ small_array.clear(&a)
+ fmt.println("AFTER :", small_array.slice(&a))
+ }
+
+Output:
+
+ BEFORE: [0, 1, 2, 3]
+ AFTER : []
+
+*/
clear :: proc "contextless" (a: ^$A/Small_Array($N, $T)) {
resize(a, 0)
}
+/*
+Attempts to append all elements to the small-array returning
+false if there is not enough space to fit all of them.
+
+**Inputs**
+- `a`: A pointer to the small-array
+- `item`: The item to append
+- ..:
+
+**Returns**
+- true if there was enough space to fit the element, false otherwise
+
+Example:
+
+ import "core:container/small_array"
+ import "core:fmt"
+
+ push_back_elems_example :: proc() {
+ a: small_array.Small_Array(100, int)
+ small_array.push_back_elems(&a, 0, 1, 2, 3, 4)
+ fmt.println(small_array.slice(&a))
+ }
+
+Output:
+
+ [0, 1, 2, 3, 4]
+*/
push_back_elems :: proc "contextless" (a: ^$A/Small_Array($N, $T), items: ..T) -> bool {
if a.len + builtin.len(items) <= cap(a^) {
n := copy(a.data[a.len:], items[:])
@@ -148,6 +694,36 @@ push_back_elems :: proc "contextless" (a: ^$A/Small_Array($N, $T), items: ..T) -
return false
}
+/*
+Tries to insert an element at the specified position.
+
+Note: Performing this operation will cause pointers obtained
+through get_ptr(_save) to reference incorrect elements.
+
+**Inputs**
+- `a`: A pointer to the small-array
+- `item`: The item to insert
+- `index`: The index to insert the item at
+
+**Returns**
+- true if there was enough space to fit the element, false otherwise
+
+Example:
+
+ import "core:container/small_array"
+ import "core:fmt"
+
+ inject_at_example :: proc() {
+ arr: small_array.Small_Array(100, rune)
+ small_array.push(&arr, 'A', 'C', 'D')
+ small_array.inject_at(&arr, 'B', 1)
+ fmt.println(small_array.slice(&arr))
+ }
+
+Output:
+
+ [A, B, C, D]
+*/
inject_at :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T, index: int) -> bool #no_bounds_check {
if a.len < cap(a^) && index >= 0 && index <= len(a^) {
a.len += 1
@@ -160,7 +736,38 @@ inject_at :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T, index: int
return false
}
+// Alias for `push_back`
append_elem :: push_back
+// Alias for `push_back_elems`
append_elems :: push_back_elems
+
+/*
+Tries to append the element(s) to the small-array.
+
+**Inputs**
+- `a`: A pointer to the small-array
+- `item`: The item to append
+- ..:
+
+**Returns**
+- true if there was enough space to fit the element, false otherwise
+
+Example:
+
+ import "core:container/small_array"
+ import "core:fmt"
+
+ push_example :: proc() {
+ a: small_array.Small_Array(100, int)
+ small_array.push(&a, 0)
+ small_array.push(&a, 1, 2, 3, 4)
+ fmt.println(small_array.slice(&a))
+ }
+
+Output:
+
+ [0, 1, 2, 3, 4]
+*/
push :: proc{push_back, push_back_elems}
+// Alias for `push`
append :: proc{push_back, push_back_elems}
diff --git a/core/crypto/_aes/aes.odin b/core/crypto/_aes/aes.odin
index 4f52485d2..f458a12fb 100644
--- a/core/crypto/_aes/aes.odin
+++ b/core/crypto/_aes/aes.odin
@@ -25,4 +25,5 @@ GHASH_BLOCK_SIZE :: 16
GHASH_TAG_SIZE :: 16
// RCON is the AES keyschedule round constants.
+@(rodata)
RCON := [10]byte{0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36}
diff --git a/core/crypto/_aes/ct64/ct64.odin b/core/crypto/_aes/ct64/ct64.odin
index f198cab81..af2b42c1e 100644
--- a/core/crypto/_aes/ct64/ct64.odin
+++ b/core/crypto/_aes/ct64/ct64.odin
@@ -22,8 +22,6 @@
package aes_ct64
-import "base:intrinsics"
-
// Bitsliced AES for 64-bit general purpose (integer) registers. Each
// invocation will process up to 4 blocks at a time. This implementation
// is derived from the BearSSL ct64 code, and distributed under a 1-clause
@@ -212,11 +210,8 @@ orthogonalize :: proc "contextless" (q: ^[8]u64) {
}
@(require_results)
-interleave_in :: proc "contextless" (w: []u32) -> (q0, q1: u64) #no_bounds_check {
- if len(w) < 4 {
- intrinsics.trap()
- }
- x0, x1, x2, x3 := u64(w[0]), u64(w[1]), u64(w[2]), u64(w[3])
+interleave_in :: proc "contextless" (w0, w1, w2, w3: u32) -> (q0, q1: u64) #no_bounds_check {
+ x0, x1, x2, x3 := u64(w0), u64(w1), u64(w2), u64(w3)
x0 |= (x0 << 16)
x1 |= (x1 << 16)
x2 |= (x2 << 16)
diff --git a/core/crypto/_aes/ct64/ct64_enc.odin b/core/crypto/_aes/ct64/ct64_enc.odin
index 36d4aebc8..bee6de722 100644
--- a/core/crypto/_aes/ct64/ct64_enc.odin
+++ b/core/crypto/_aes/ct64/ct64_enc.odin
@@ -22,12 +22,8 @@
package aes_ct64
-import "base:intrinsics"
-
add_round_key :: proc "contextless" (q: ^[8]u64, sk: []u64) #no_bounds_check {
- if len(sk) < 8 {
- intrinsics.trap()
- }
+ ensure_contextless(len(sk) >= 8, "aes/ct64: invalid round key size")
q[0] ~= sk[0]
q[1] ~= sk[1]
diff --git a/core/crypto/_aes/ct64/ct64_keysched.odin b/core/crypto/_aes/ct64/ct64_keysched.odin
index 060a2c03e..0f00bba57 100644
--- a/core/crypto/_aes/ct64/ct64_keysched.odin
+++ b/core/crypto/_aes/ct64/ct64_keysched.odin
@@ -22,7 +22,6 @@
package aes_ct64
-import "base:intrinsics"
import "core:crypto/_aes"
import "core:encoding/endian"
import "core:mem"
@@ -42,7 +41,7 @@ sub_word :: proc "contextless" (x: u32) -> u32 {
}
@(private, require_results)
-keysched :: proc(comp_skey: []u64, key: []byte) -> int {
+keysched :: proc "contextless" (comp_skey: []u64, key: []byte) -> int {
num_rounds, key_len := 0, len(key)
switch key_len {
case _aes.KEY_SIZE_128:
@@ -52,7 +51,7 @@ keysched :: proc(comp_skey: []u64, key: []byte) -> int {
case _aes.KEY_SIZE_256:
num_rounds = _aes.ROUNDS_256
case:
- panic("crypto/aes: invalid AES key size")
+ panic_contextless("crypto/aes: invalid AES key size")
}
skey: [60]u32 = ---
@@ -78,7 +77,7 @@ keysched :: proc(comp_skey: []u64, key: []byte) -> int {
q: [8]u64 = ---
for i, j := 0, 0; i < nkf; i, j = i + 4, j + 2 {
- q[0], q[4] = interleave_in(skey[i:])
+ q[0], q[4] = interleave_in(skey[i], skey[i+1], skey[i+2], skey[i+3])
q[1] = q[0]
q[2] = q[0]
q[3] = q[0]
@@ -123,57 +122,3 @@ skey_expand :: proc "contextless" (skey, comp_skey: []u64, num_rounds: int) {
skey[v + 3] = (x3 << 4) - x3
}
}
-
-orthogonalize_roundkey :: proc "contextless" (qq: []u64, key: []byte) {
- if len(qq) < 8 || len(key) != 16 {
- intrinsics.trap()
- }
-
- skey: [4]u32 = ---
- skey[0] = endian.unchecked_get_u32le(key[0:])
- skey[1] = endian.unchecked_get_u32le(key[4:])
- skey[2] = endian.unchecked_get_u32le(key[8:])
- skey[3] = endian.unchecked_get_u32le(key[12:])
-
- q: [8]u64 = ---
- q[0], q[4] = interleave_in(skey[:])
- q[1] = q[0]
- q[2] = q[0]
- q[3] = q[0]
- q[5] = q[4]
- q[6] = q[4]
- q[7] = q[4]
- orthogonalize(&q)
-
- comp_skey: [2]u64 = ---
- comp_skey[0] =
- (q[0] & 0x1111111111111111) |
- (q[1] & 0x2222222222222222) |
- (q[2] & 0x4444444444444444) |
- (q[3] & 0x8888888888888888)
- comp_skey[1] =
- (q[4] & 0x1111111111111111) |
- (q[5] & 0x2222222222222222) |
- (q[6] & 0x4444444444444444) |
- (q[7] & 0x8888888888888888)
-
- for x, u in comp_skey {
- x0 := x
- x1, x2, x3 := x0, x0, x0
- x0 &= 0x1111111111111111
- x1 &= 0x2222222222222222
- x2 &= 0x4444444444444444
- x3 &= 0x8888888888888888
- x1 >>= 1
- x2 >>= 2
- x3 >>= 3
- qq[u * 4 + 0] = (x0 << 4) - x0
- qq[u * 4 + 1] = (x1 << 4) - x1
- qq[u * 4 + 2] = (x2 << 4) - x2
- qq[u * 4 + 3] = (x3 << 4) - x3
- }
-
- mem.zero_explicit(&skey, size_of(skey))
- mem.zero_explicit(&q, size_of(q))
- mem.zero_explicit(&comp_skey, size_of(comp_skey))
-}
diff --git a/core/crypto/_aes/ct64/ghash.odin b/core/crypto/_aes/ct64/ghash.odin
index a522a481a..0c885d8ba 100644
--- a/core/crypto/_aes/ct64/ghash.odin
+++ b/core/crypto/_aes/ct64/ghash.odin
@@ -22,7 +22,6 @@
package aes_ct64
-import "base:intrinsics"
import "core:crypto/_aes"
import "core:encoding/endian"
@@ -64,9 +63,8 @@ rev64 :: proc "contextless" (x: u64) -> u64 {
// Note: `dst` is both an input and an output, to support easy implementation
// of GCM.
ghash :: proc "contextless" (dst, key, data: []byte) {
- if len(dst) != _aes.GHASH_BLOCK_SIZE || len(key) != _aes.GHASH_BLOCK_SIZE {
- intrinsics.trap()
- }
+ ensure_contextless(len(dst) == _aes.GHASH_BLOCK_SIZE)
+ ensure_contextless(len(key) == _aes.GHASH_BLOCK_SIZE)
buf := data
l := len(buf)
diff --git a/core/crypto/_aes/ct64/helpers.odin b/core/crypto/_aes/ct64/helpers.odin
index 169271f6d..7eec5bdc4 100644
--- a/core/crypto/_aes/ct64/helpers.odin
+++ b/core/crypto/_aes/ct64/helpers.odin
@@ -1,60 +1,61 @@
package aes_ct64
-import "base:intrinsics"
import "core:crypto/_aes"
import "core:encoding/endian"
-load_blockx1 :: proc "contextless" (q: ^[8]u64, src: []byte) {
- if len(src) != _aes.BLOCK_SIZE {
- intrinsics.trap()
- }
-
- w: [4]u32 = ---
- w[0] = endian.unchecked_get_u32le(src[0:])
- w[1] = endian.unchecked_get_u32le(src[4:])
- w[2] = endian.unchecked_get_u32le(src[8:])
- w[3] = endian.unchecked_get_u32le(src[12:])
- q[0], q[4] = interleave_in(w[:])
- orthogonalize(q)
+@(require_results)
+load_interleaved :: proc "contextless" (src: []byte) -> (u64, u64) #no_bounds_check {
+ w0 := endian.unchecked_get_u32le(src[0:])
+ w1 := endian.unchecked_get_u32le(src[4:])
+ w2 := endian.unchecked_get_u32le(src[8:])
+ w3 := endian.unchecked_get_u32le(src[12:])
+ return interleave_in(w0, w1, w2, w3)
}
-store_blockx1 :: proc "contextless" (dst: []byte, q: ^[8]u64) {
- if len(dst) != _aes.BLOCK_SIZE {
- intrinsics.trap()
- }
-
- orthogonalize(q)
- w0, w1, w2, w3 := interleave_out(q[0], q[4])
+store_interleaved :: proc "contextless" (dst: []byte, a0, a1: u64) #no_bounds_check {
+ w0, w1, w2, w3 := interleave_out(a0, a1)
endian.unchecked_put_u32le(dst[0:], w0)
endian.unchecked_put_u32le(dst[4:], w1)
endian.unchecked_put_u32le(dst[8:], w2)
endian.unchecked_put_u32le(dst[12:], w3)
}
+@(require_results)
+xor_interleaved :: #force_inline proc "contextless" (a0, a1, b0, b1: u64) -> (u64, u64) {
+ return a0 ~ b0, a1 ~ b1
+}
+
+@(require_results)
+and_interleaved :: #force_inline proc "contextless" (a0, a1, b0, b1: u64) -> (u64, u64) {
+ return a0 & b0, a1 & b1
+}
+
+load_blockx1 :: proc "contextless" (q: ^[8]u64, src: []byte) {
+ ensure_contextless(len(src) == _aes.BLOCK_SIZE, "aes/ct64: invalid block size")
+
+ q[0], q[4] = #force_inline load_interleaved(src)
+ orthogonalize(q)
+}
+
+store_blockx1 :: proc "contextless" (dst: []byte, q: ^[8]u64) {
+ ensure_contextless(len(dst) == _aes.BLOCK_SIZE, "aes/ct64: invalid block size")
+
+ orthogonalize(q)
+ #force_inline store_interleaved(dst, q[0], q[4])
+}
+
load_blocks :: proc "contextless" (q: ^[8]u64, src: [][]byte) {
- if n := len(src); n > STRIDE || n == 0 {
- intrinsics.trap()
- }
+ ensure_contextless(len(src) == 0 || len(src) <= STRIDE, "aes/ct64: invalid block(s) size")
- w: [4]u32 = ---
for s, i in src {
- if len(s) != _aes.BLOCK_SIZE {
- intrinsics.trap()
- }
-
- w[0] = endian.unchecked_get_u32le(s[0:])
- w[1] = endian.unchecked_get_u32le(s[4:])
- w[2] = endian.unchecked_get_u32le(s[8:])
- w[3] = endian.unchecked_get_u32le(s[12:])
- q[i], q[i + 4] = interleave_in(w[:])
+ ensure_contextless(len(s) == _aes.BLOCK_SIZE, "aes/ct64: invalid block size")
+ q[i], q[i + 4] = #force_inline load_interleaved(s)
}
orthogonalize(q)
}
store_blocks :: proc "contextless" (dst: [][]byte, q: ^[8]u64) {
- if n := len(dst); n > STRIDE || n == 0 {
- intrinsics.trap()
- }
+ ensure_contextless(len(dst) == 0 || len(dst) <= STRIDE, "aes/ct64: invalid block(s) size")
orthogonalize(q)
for d, i in dst {
@@ -62,14 +63,7 @@ store_blocks :: proc "contextless" (dst: [][]byte, q: ^[8]u64) {
if d == nil {
break
}
- if len(d) != _aes.BLOCK_SIZE {
- intrinsics.trap()
- }
-
- w0, w1, w2, w3 := interleave_out(q[i], q[i + 4])
- endian.unchecked_put_u32le(d[0:], w0)
- endian.unchecked_put_u32le(d[4:], w1)
- endian.unchecked_put_u32le(d[8:], w2)
- endian.unchecked_put_u32le(d[12:], w3)
+ ensure_contextless(len(d) == _aes.BLOCK_SIZE, "aes/ct64: invalid block size")
+ #force_inline store_interleaved(d, q[i], q[i + 4])
}
}
diff --git a/core/crypto/_aes/hw_intel/ghash.odin b/core/crypto/_aes/hw_intel/ghash.odin
index 4320dd59b..5f51b614b 100644
--- a/core/crypto/_aes/hw_intel/ghash.odin
+++ b/core/crypto/_aes/hw_intel/ghash.odin
@@ -52,7 +52,7 @@ GHASH_STRIDE_BYTES_HW :: GHASH_STRIDE_HW * _aes.GHASH_BLOCK_SIZE
// that it is right-shifted by 1 bit. The left-shift is relatively
// inexpensive, and it can be mutualised.
//
-// Since SSE2 opcodes do not have facilities for shitfting full 128-bit
+// Since SSE2 opcodes do not have facilities for shifting full 128-bit
// values with bit precision, we have to break down values into 64-bit
// chunks. We number chunks from 0 to 3 in left to right order.
@@ -155,7 +155,7 @@ square_f128 :: #force_inline proc "contextless" (kw: x86.__m128i) -> (x86.__m128
@(enable_target_feature = "sse2,ssse3,pclmul")
ghash :: proc "contextless" (dst, key, data: []byte) #no_bounds_check {
if len(dst) != _aes.GHASH_BLOCK_SIZE || len(key) != _aes.GHASH_BLOCK_SIZE {
- intrinsics.trap()
+ panic_contextless("aes/ghash: invalid dst or key size")
}
// Note: BearSSL opts to copy the remainder into a zero-filled
diff --git a/core/crypto/_blake2/blake2.odin b/core/crypto/_blake2/blake2.odin
index 2ad74843b..89fbe3a7a 100644
--- a/core/crypto/_blake2/blake2.odin
+++ b/core/crypto/_blake2/blake2.odin
@@ -18,6 +18,8 @@ BLAKE2S_SIZE :: 32
BLAKE2B_BLOCK_SIZE :: 128
BLAKE2B_SIZE :: 64
+MAX_SIZE :: 255
+
Blake2s_Context :: struct {
h: [8]u32,
t: [2]u32,
@@ -68,13 +70,13 @@ Blake2_Tree :: struct {
is_last_node: bool,
}
-@(private)
+@(private, rodata)
BLAKE2S_IV := [8]u32 {
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
}
-@(private)
+@(private, rodata)
BLAKE2B_IV := [8]u64 {
0x6a09e667f3bcc908, 0xbb67ae8584caa73b,
0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
@@ -82,16 +84,13 @@ BLAKE2B_IV := [8]u64 {
0x1f83d9abfb41bd6b, 0x5be0cd19137e2179,
}
-init :: proc(ctx: ^$T, cfg: ^Blake2_Config) {
+init :: proc "contextless" (ctx: ^$T, cfg: ^Blake2_Config) {
when T == Blake2s_Context {
max_size :: BLAKE2S_SIZE
} else when T == Blake2b_Context {
max_size :: BLAKE2B_SIZE
}
-
- if cfg.size > max_size {
- panic("blake2: requested output size exceeeds algorithm max")
- }
+ ensure_contextless(cfg.size <= max_size, "blake2: requested output size exceeeds algorithm max")
// To save having to allocate a scratch buffer, use the internal
// data buffer (`ctx.x`), as it is exactly the correct size.
@@ -167,8 +166,8 @@ init :: proc(ctx: ^$T, cfg: ^Blake2_Config) {
ctx.is_initialized = true
}
-update :: proc(ctx: ^$T, p: []byte) {
- assert(ctx.is_initialized)
+update :: proc "contextless" (ctx: ^$T, p: []byte) {
+ ensure_contextless(ctx.is_initialized)
p := p
when T == Blake2s_Context {
@@ -195,8 +194,8 @@ update :: proc(ctx: ^$T, p: []byte) {
ctx.nx += copy(ctx.x[ctx.nx:], p)
}
-final :: proc(ctx: ^$T, hash: []byte, finalize_clone: bool = false) {
- assert(ctx.is_initialized)
+final :: proc "contextless" (ctx: ^$T, hash: []byte, finalize_clone: bool = false) {
+ ensure_contextless(ctx.is_initialized)
ctx := ctx
if finalize_clone {
@@ -206,24 +205,19 @@ final :: proc(ctx: ^$T, hash: []byte, finalize_clone: bool = false) {
}
defer(reset(ctx))
+ ensure_contextless(len(hash) >= int(ctx.size), "crypto/blake2: invalid destination digest size")
when T == Blake2s_Context {
- if len(hash) < int(ctx.size) {
- panic("crypto/blake2s: invalid destination digest size")
- }
blake2s_final(ctx, hash)
} else when T == Blake2b_Context {
- if len(hash) < int(ctx.size) {
- panic("crypto/blake2b: invalid destination digest size")
- }
blake2b_final(ctx, hash)
}
}
-clone :: proc(ctx, other: ^$T) {
+clone :: proc "contextless" (ctx, other: ^$T) {
ctx^ = other^
}
-reset :: proc(ctx: ^$T) {
+reset :: proc "contextless" (ctx: ^$T) {
if !ctx.is_initialized {
return
}
diff --git a/core/crypto/_chacha20/chacha20.odin b/core/crypto/_chacha20/chacha20.odin
index a907209de..1a4b5a507 100644
--- a/core/crypto/_chacha20/chacha20.odin
+++ b/core/crypto/_chacha20/chacha20.odin
@@ -1,6 +1,5 @@
package _chacha20
-import "base:intrinsics"
import "core:encoding/endian"
import "core:math/bits"
import "core:mem"
@@ -46,9 +45,8 @@ Context :: struct {
// derivation is expected to be handled by the caller, so that the
// HChaCha call can be suitably accelerated.
init :: proc "contextless" (ctx: ^Context, key, iv: []byte, is_xchacha: bool) {
- if len(key) != KEY_SIZE || len(iv) != IV_SIZE {
- intrinsics.trap()
- }
+ ensure_contextless(len(key) == KEY_SIZE, "chacha20: invalid key size")
+ ensure_contextless(len(iv) == IV_SIZE, "chacha20: invalid key size")
k, n := key, iv
@@ -76,12 +74,10 @@ init :: proc "contextless" (ctx: ^Context, key, iv: []byte, is_xchacha: bool) {
// seek seeks the (X)ChaCha20 stream counter to the specified block.
seek :: proc(ctx: ^Context, block_nr: u64) {
- assert(ctx._is_initialized)
+ ensure(ctx._is_initialized)
if ctx._is_ietf_flavor {
- if block_nr > MAX_CTR_IETF {
- panic("crypto/chacha20: attempted to seek past maximum counter")
- }
+ ensure(block_nr <= MAX_CTR_IETF, "crypto/chacha20: attempted to seek past maximum counter")
} else {
ctx._s[13] = u32(block_nr >> 32)
}
@@ -102,7 +98,7 @@ check_counter_limit :: proc(ctx: ^Context, nr_blocks: int) {
// Enforce the maximum consumed keystream per IV.
//
// While all modern "standard" definitions of ChaCha20 use
- // the IETF 32-bit counter, for XChaCha20 most common
+ // the IETF 32-bit counter, for XChaCha20 historical
// implementations allow for a 64-bit counter.
//
// Honestly, the answer here is "use a MRAE primitive", but
@@ -110,14 +106,14 @@ check_counter_limit :: proc(ctx: ^Context, nr_blocks: int) {
ERR_CTR_EXHAUSTED :: "crypto/chacha20: maximum (X)ChaCha20 keystream per IV reached"
+ ctr_ok: bool
if ctx._is_ietf_flavor {
- if u64(ctx._s[12]) + u64(nr_blocks) > MAX_CTR_IETF {
- panic(ERR_CTR_EXHAUSTED)
- }
+ ctr_ok = u64(ctx._s[12]) + u64(nr_blocks) <= MAX_CTR_IETF
} else {
ctr := (u64(ctx._s[13]) << 32) | u64(ctx._s[12])
- if _, carry := bits.add_u64(ctr, u64(nr_blocks), 0); carry != 0 {
- panic(ERR_CTR_EXHAUSTED)
- }
+ _, carry := bits.add_u64(ctr, u64(nr_blocks), 0)
+ ctr_ok = carry == 0
}
+
+ ensure(ctr_ok, "crypto/chacha20: maximum (X)ChaCha20 keystream per IV reached")
}
diff --git a/core/crypto/_chacha20/simd128/chacha20_simd128.odin b/core/crypto/_chacha20/simd128/chacha20_simd128.odin
index fe0d0d518..cf78541d1 100644
--- a/core/crypto/_chacha20/simd128/chacha20_simd128.odin
+++ b/core/crypto/_chacha20/simd128/chacha20_simd128.odin
@@ -29,11 +29,24 @@ when ODIN_ARCH == .arm64 || ODIN_ARCH == .arm32 {
// explicitly using simd.u8x16 shuffles.
@(private = "file")
TARGET_SIMD_FEATURES :: "sse2,ssse3"
+} else when ODIN_ARCH == .riscv64 {
+ @(private = "file")
+ TARGET_SIMD_FEATURES :: "v"
} else {
@(private = "file")
TARGET_SIMD_FEATURES :: ""
}
+// Some targets lack runtime feature detection, and will flat out refuse
+// to load binaries that have unknown instructions. This is distinct from
+// `simd.IS_EMULATED` as actually good designs support runtime feature
+// detection and that constant establishes a baseline.
+//
+// See:
+// - https://github.com/WebAssembly/design/issues/1161
+@(private = "file")
+TARGET_IS_DESIGNED_BY_IDIOTS :: (ODIN_ARCH == .wasm64p32 || ODIN_ARCH == .wasm32) && !intrinsics.has_target_feature("simd128")
+
@(private = "file")
_ROT_7L: simd.u32x4 : {7, 7, 7, 7}
@(private = "file")
@@ -205,11 +218,13 @@ _store_simd128 :: #force_inline proc "contextless" (
// is_performant returns true iff the target and current host both support
// "enough" 128-bit SIMD to make this implementation performant.
is_performant :: proc "contextless" () -> bool {
- when ODIN_ARCH == .arm64 || ODIN_ARCH == .arm32 || ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 {
+ when ODIN_ARCH == .arm64 || ODIN_ARCH == .arm32 || ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 || ODIN_ARCH == .riscv64 {
when ODIN_ARCH == .arm64 || ODIN_ARCH == .arm32 {
req_features :: info.CPU_Features{.asimd}
} else when ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 {
req_features :: info.CPU_Features{.sse2, .ssse3}
+ } else when ODIN_ARCH == .riscv64 {
+ req_features :: info.CPU_Features{.V}
}
features, ok := info.cpu_features.?
@@ -245,8 +260,17 @@ stream_blocks :: proc(ctx: ^_chacha20.Context, dst, src: []byte, nr_blocks: int)
// 8 blocks at a time.
//
- // Note: This is only worth it on Aarch64.
- when ODIN_ARCH == .arm64 {
+ // Note:
+ // This uses a ton of registers so it is only worth it on targets
+ // that have something like 32 128-bit registers. This is currently
+ // all ARMv8 targets, and RISC-V Zvl128b (`V` application profile)
+ // targets.
+ //
+ // While our current definition of `.arm32` is 32-bit ARMv8, this
+ // may change in the future (ARMv7 is still relevant), and things
+ // like Cortex-A8/A9 does "pretend" 128-bit SIMD 64-bits at a time
+ // thus needs bemchmarking.
+ when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
for ; n >= 8; n = n - 8 {
v0, v1, v2, v3 := s0, s1, s2, s3
@@ -354,9 +378,11 @@ stream_blocks :: proc(ctx: ^_chacha20.Context, dst, src: []byte, nr_blocks: int)
// 4 blocks at a time.
//
- // Note: The i386 target lacks the required number of registers
- // for this to be performant, so it is skipped.
- when ODIN_ARCH != .i386 {
+ // Note: This is skipped on several targets for various reasons.
+ // - i386 lacks the required number of registers
+ // - Generating code when runtime "hardware" SIMD support is impossible
+ // to detect is pointless, since this will be emulated using GP regs.
+ when ODIN_ARCH != .i386 && !TARGET_IS_DESIGNED_BY_IDIOTS {
for ; n >= 4; n = n - 4 {
v0, v1, v2, v3 := s0, s1, s2, s3
diff --git a/core/crypto/_chacha20/simd256/chacha20_simd256_stub.odin b/core/crypto/_chacha20/simd256/chacha20_simd256_stub.odin
index ce673b42b..287ddd885 100644
--- a/core/crypto/_chacha20/simd256/chacha20_simd256_stub.odin
+++ b/core/crypto/_chacha20/simd256/chacha20_simd256_stub.odin
@@ -13,5 +13,5 @@ stream_blocks :: proc(ctx: ^_chacha20.Context, dst, src: []byte, nr_blocks: int)
}
hchacha20 :: proc "contextless" (dst, key, iv: []byte) {
- intrinsics.trap()
+ panic_contextless("crypto/chacha20: simd256 implementation unsupported")
}
\ No newline at end of file
diff --git a/core/crypto/_edwards25519/edwards25519.odin b/core/crypto/_edwards25519/edwards25519.odin
index 6495f7a3a..d6f01d497 100644
--- a/core/crypto/_edwards25519/edwards25519.odin
+++ b/core/crypto/_edwards25519/edwards25519.odin
@@ -11,7 +11,6 @@ See:
- https://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html
*/
-import "base:intrinsics"
import "core:crypto"
import field "core:crypto/_fiat/field_curve25519"
import "core:mem"
@@ -32,6 +31,7 @@ import "core:mem"
// - The group element decoding routine takes the opinionated stance of
// rejecting non-canonical encodings.
+@(rodata)
FE_D := field.Tight_Field_Element {
929955233495203,
466365720129213,
@@ -39,7 +39,7 @@ FE_D := field.Tight_Field_Element {
2033849074728123,
1442794654840575,
}
-@(private)
+@(private, rodata)
FE_A := field.Tight_Field_Element {
2251799813685228,
2251799813685247,
@@ -47,7 +47,7 @@ FE_A := field.Tight_Field_Element {
2251799813685247,
2251799813685247,
}
-@(private)
+@(private, rodata)
FE_D2 := field.Tight_Field_Element {
1859910466990425,
932731440258426,
@@ -55,7 +55,7 @@ FE_D2 := field.Tight_Field_Element {
1815898335770999,
633789495995903,
}
-@(private)
+@(private, rodata)
GE_BASEPOINT := Group_Element {
field.Tight_Field_Element {
1738742601995546,
@@ -80,6 +80,7 @@ GE_BASEPOINT := Group_Element {
1821297809914039,
},
}
+@(rodata)
GE_IDENTITY := Group_Element {
field.Tight_Field_Element{0, 0, 0, 0, 0},
field.Tight_Field_Element{1, 0, 0, 0, 0},
@@ -107,9 +108,7 @@ ge_set :: proc "contextless" (ge, a: ^Group_Element) {
@(require_results)
ge_set_bytes :: proc "contextless" (ge: ^Group_Element, b: []byte) -> bool {
- if len(b) != 32 {
- intrinsics.trap()
- }
+ ensure_contextless(len(b) == 32, "edwards25519: invalid group element size")
b_ := (^[32]byte)(raw_data(b))
// Do the work in a scratch element, so that ge is unchanged on
@@ -166,9 +165,7 @@ ge_set_bytes :: proc "contextless" (ge: ^Group_Element, b: []byte) -> bool {
}
ge_bytes :: proc "contextless" (ge: ^Group_Element, dst: []byte) {
- if len(dst) != 32 {
- intrinsics.trap()
- }
+ ensure_contextless(len(dst) == 32, "edwards25519: invalid group element size")
dst_ := (^[32]byte)(raw_data(dst))
// Convert the element to affine (x, y) representation.
diff --git a/core/crypto/_edwards25519/edwards25519_scalar.odin b/core/crypto/_edwards25519/edwards25519_scalar.odin
index e21fa3755..68c79a6e8 100644
--- a/core/crypto/_edwards25519/edwards25519_scalar.odin
+++ b/core/crypto/_edwards25519/edwards25519_scalar.odin
@@ -1,6 +1,5 @@
package _edwards25519
-import "base:intrinsics"
import field "core:crypto/_fiat/field_scalar25519"
import "core:mem"
@@ -8,7 +7,7 @@ Scalar :: field.Montgomery_Domain_Field_Element
// WARNING: This is non-canonical and only to be used when checking if
// a group element is on the prime-order subgroup.
-@(private)
+@(private, rodata)
SC_ELL := field.Non_Montgomery_Domain_Field_Element {
field.ELL[0],
field.ELL[1],
@@ -25,17 +24,13 @@ sc_set_u64 :: proc "contextless" (sc: ^Scalar, i: u64) {
@(require_results)
sc_set_bytes :: proc "contextless" (sc: ^Scalar, b: []byte) -> bool {
- if len(b) != 32 {
- intrinsics.trap()
- }
+ ensure_contextless(len(b) == 32, "edwards25519: invalid scalar size")
b_ := (^[32]byte)(raw_data(b))
return field.fe_from_bytes(sc, b_)
}
sc_set_bytes_rfc8032 :: proc "contextless" (sc: ^Scalar, b: []byte) {
- if len(b) != 32 {
- intrinsics.trap()
- }
+ ensure_contextless(len(b) == 32, "edwards25519: invalid scalar size")
b_ := (^[32]byte)(raw_data(b))
field.fe_from_bytes_rfc8032(sc, b_)
}
diff --git a/core/crypto/_fiat/field_curve25519/field51.odin b/core/crypto/_fiat/field_curve25519/field51.odin
index d039bd411..6716fa158 100644
--- a/core/crypto/_fiat/field_curve25519/field51.odin
+++ b/core/crypto/_fiat/field_curve25519/field51.odin
@@ -42,9 +42,12 @@ import "core:math/bits"
Loose_Field_Element :: distinct [5]u64
Tight_Field_Element :: distinct [5]u64
+@(rodata)
FE_ZERO := Tight_Field_Element{0, 0, 0, 0, 0}
+@(rodata)
FE_ONE := Tight_Field_Element{1, 0, 0, 0, 0}
+@(rodata)
FE_SQRT_M1 := Tight_Field_Element {
1718705420411056,
234908883556509,
diff --git a/core/crypto/_fiat/field_curve448/field.odin b/core/crypto/_fiat/field_curve448/field.odin
new file mode 100644
index 000000000..540d88f28
--- /dev/null
+++ b/core/crypto/_fiat/field_curve448/field.odin
@@ -0,0 +1,235 @@
+package field_curve448
+
+import "core:mem"
+
+fe_relax_cast :: #force_inline proc "contextless" (
+ arg1: ^Tight_Field_Element,
+) -> ^Loose_Field_Element {
+ return (^Loose_Field_Element)(arg1)
+}
+
+fe_tighten_cast :: #force_inline proc "contextless" (
+ arg1: ^Loose_Field_Element,
+) -> ^Tight_Field_Element {
+ return (^Tight_Field_Element)(arg1)
+}
+
+fe_clear :: proc "contextless" (
+ arg1: $T,
+) where T == ^Tight_Field_Element || T == ^Loose_Field_Element {
+ mem.zero_explicit(arg1, size_of(arg1^))
+}
+
+fe_clear_vec :: proc "contextless" (
+ arg1: $T,
+) where T == []^Tight_Field_Element || T == []^Loose_Field_Element {
+ for fe in arg1 {
+ fe_clear(fe)
+ }
+}
+
+fe_carry_mul_small :: proc "contextless" (
+ out1: ^Tight_Field_Element,
+ arg1: ^Loose_Field_Element,
+ arg2: u64,
+) {
+ arg2_ := Loose_Field_Element{arg2, 0, 0, 0, 0, 0, 0, 0}
+ fe_carry_mul(out1, arg1, &arg2_)
+}
+
+fe_carry_pow2k :: proc "contextless" (
+ out1: ^Tight_Field_Element,
+ arg1: ^Loose_Field_Element,
+ arg2: uint,
+) {
+ // Special case: `arg1^(2 * 0) = 1`, though this should never happen.
+ if arg2 == 0 {
+ fe_one(out1)
+ return
+ }
+
+ fe_carry_square(out1, arg1)
+ for _ in 1 ..< arg2 {
+ fe_carry_square(out1, fe_relax_cast(out1))
+ }
+}
+
+fe_carry_inv :: proc "contextless" (
+ out1: ^Tight_Field_Element,
+ arg1: ^Loose_Field_Element,
+) {
+ // Inversion computation is derived from the addition chain:
+ //
+ // _10 = 2*1
+ // _11 = 1 + _10
+ // _110 = 2*_11
+ // _111 = 1 + _110
+ // _111000 = _111 << 3
+ // _111111 = _111 + _111000
+ // x12 = _111111 << 6 + _111111
+ // x24 = x12 << 12 + x12
+ // i34 = x24 << 6
+ // x30 = _111111 + i34
+ // x48 = i34 << 18 + x24
+ // x96 = x48 << 48 + x48
+ // x192 = x96 << 96 + x96
+ // x222 = x192 << 30 + x30
+ // x223 = 2*x222 + 1
+ // return (x223 << 223 + x222) << 2 + 1
+ //
+ // Operations: 447 squares 13 multiplies
+ //
+ // Generated by github.com/mmcloughlin/addchain v0.4.0.
+
+ t0, t1, t2: Tight_Field_Element = ---, ---, ---
+
+ // Step 1: t0 = x^0x2
+ fe_carry_square(&t0, arg1)
+
+ // Step 2: t0 = x^0x3
+ fe_carry_mul(&t0, arg1, fe_relax_cast(&t0))
+
+ // t0.Sqr(t0)
+ fe_carry_square(&t0, fe_relax_cast(&t0))
+
+ // Step 4: t0 = x^0x7
+ fe_carry_mul(&t0, arg1, fe_relax_cast(&t0))
+
+ // Step 7: t1 = x^0x38
+ fe_carry_pow2k(&t1, fe_relax_cast(&t0), 3)
+
+ // Step 8: t0 = x^0x3f
+ fe_carry_mul(&t0, fe_relax_cast(&t0), fe_relax_cast(&t1))
+
+ // Step 14: t1 = x^0xfc0
+ fe_carry_pow2k(&t1, fe_relax_cast(&t0), 6)
+
+ // Step 15: t1 = x^0xfff
+ fe_carry_mul(&t1, fe_relax_cast(&t0), fe_relax_cast(&t1))
+
+ // Step 27: t2 = x^0xfff000
+ fe_carry_pow2k(&t2, fe_relax_cast(&t1), 12)
+
+ // Step 28: t1 = x^0xffffff
+ fe_carry_mul(&t1, fe_relax_cast(&t1), fe_relax_cast(&t2))
+
+ // Step 34: t2 = x^0x3fffffc0
+ fe_carry_pow2k(&t2, fe_relax_cast(&t1), 6)
+
+ // Step 35: t0 = x^0x3fffffff
+ fe_carry_mul(&t0, fe_relax_cast(&t0), fe_relax_cast(&t2))
+
+ // Step 53: t2 = x^0xffffff000000
+ fe_carry_pow2k(&t2, fe_relax_cast(&t2), 18)
+
+ // Step 54: t1 = x^0xffffffffffff
+ fe_carry_mul(&t1, fe_relax_cast(&t1), fe_relax_cast(&t2))
+
+ // Step 102: t2 = x^0xffffffffffff000000000000
+ fe_carry_pow2k(&t2, fe_relax_cast(&t1), 48)
+
+ // Step 103: t1 = x^0xffffffffffffffffffffffff
+ fe_carry_mul(&t1, fe_relax_cast(&t1), fe_relax_cast(&t2))
+
+ // Step 199: t2 = x^0xffffffffffffffffffffffff000000000000000000000000
+ fe_carry_pow2k(&t2, fe_relax_cast(&t1), 96)
+
+ // Step 200: t1 = x^0xffffffffffffffffffffffffffffffffffffffffffffffff
+ fe_carry_mul(&t1, fe_relax_cast(&t1), fe_relax_cast(&t2))
+
+ // Step 230: t1 = x^0x3fffffffffffffffffffffffffffffffffffffffffffffffc0000000
+ fe_carry_pow2k(&t1, fe_relax_cast(&t1), 30)
+
+ // Step 231: t0 = x^0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ fe_carry_mul(&t0, fe_relax_cast(&t0), fe_relax_cast(&t1))
+
+ // Step 232: t1 = x^0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffe
+ fe_carry_square(&t1, fe_relax_cast(&t0))
+
+ // Step 233: t1 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ fe_carry_mul(&t1, arg1, fe_relax_cast(&t1))
+
+ // Step 456: t1 = x^0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000000000000000000000000000000000000000000000000000
+ fe_carry_pow2k(&t1, fe_relax_cast(&t1), 223)
+
+ // Step 457: t0 = x^0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffffffffffffffffffffffffffffffffffffffffffffffffffffff
+ fe_carry_mul(&t0, fe_relax_cast(&t0), fe_relax_cast(&t1))
+
+ // Step 459: t0 = x^0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffffffffffffffffffffffffffffffffffffffffffffffffffffc
+ fe_carry_pow2k(&t0, fe_relax_cast(&t0), 2)
+
+ // Step 460: z = x^0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffffffffffffffffffffffffffffffffffffffffffffffffffffd
+ fe_carry_mul(out1, arg1, fe_relax_cast(&t0))
+
+ fe_clear_vec([]^Tight_Field_Element{&t0, &t1, &t2})
+}
+
+fe_zero :: proc "contextless" (out1: ^Tight_Field_Element) {
+ out1[0] = 0
+ out1[1] = 0
+ out1[2] = 0
+ out1[3] = 0
+ out1[4] = 0
+ out1[5] = 0
+ out1[6] = 0
+ out1[7] = 0
+}
+
+fe_one :: proc "contextless" (out1: ^Tight_Field_Element) {
+ out1[0] = 1
+ out1[1] = 0
+ out1[2] = 0
+ out1[3] = 0
+ out1[4] = 0
+ out1[5] = 0
+ out1[6] = 0
+ out1[7] = 0
+}
+
+fe_set :: proc "contextless" (out1, arg1: ^Tight_Field_Element) {
+ x1 := arg1[0]
+ x2 := arg1[1]
+ x3 := arg1[2]
+ x4 := arg1[3]
+ x5 := arg1[4]
+ x6 := arg1[5]
+ x7 := arg1[6]
+ x8 := arg1[7]
+ out1[0] = x1
+ out1[1] = x2
+ out1[2] = x3
+ out1[3] = x4
+ out1[4] = x5
+ out1[5] = x6
+ out1[6] = x7
+ out1[7] = x8
+}
+
+@(optimization_mode = "none")
+fe_cond_swap :: #force_no_inline proc "contextless" (out1, out2: ^Tight_Field_Element, arg1: int) {
+ mask := (u64(arg1) * 0xffffffffffffffff)
+ x := (out1[0] ~ out2[0]) & mask
+ x1, y1 := out1[0] ~ x, out2[0] ~ x
+ x = (out1[1] ~ out2[1]) & mask
+ x2, y2 := out1[1] ~ x, out2[1] ~ x
+ x = (out1[2] ~ out2[2]) & mask
+ x3, y3 := out1[2] ~ x, out2[2] ~ x
+ x = (out1[3] ~ out2[3]) & mask
+ x4, y4 := out1[3] ~ x, out2[3] ~ x
+ x = (out1[4] ~ out2[4]) & mask
+ x5, y5 := out1[4] ~ x, out2[4] ~ x
+ x = (out1[5] ~ out2[5]) & mask
+ x6, y6 := out1[5] ~ x, out2[5] ~ x
+ x = (out1[6] ~ out2[6]) & mask
+ x7, y7 := out1[6] ~ x, out2[6] ~ x
+ x = (out1[7] ~ out2[7]) & mask
+ x8, y8 := out1[7] ~ x, out2[7] ~ x
+ out1[0], out2[0] = x1, y1
+ out1[1], out2[1] = x2, y2
+ out1[2], out2[2] = x3, y3
+ out1[3], out2[3] = x4, y4
+ out1[4], out2[4] = x5, y5
+ out1[5], out2[5] = x6, y6
+ out1[6], out2[6] = x7, y7
+ out1[7], out2[7] = x8, y8
+}
\ No newline at end of file
diff --git a/core/crypto/_fiat/field_curve448/field51.odin b/core/crypto/_fiat/field_curve448/field51.odin
new file mode 100644
index 000000000..d8e49e04d
--- /dev/null
+++ b/core/crypto/_fiat/field_curve448/field51.odin
@@ -0,0 +1,1060 @@
+// The BSD 1-Clause License (BSD-1-Clause)
+//
+// Copyright (c) 2015-2020 the fiat-crypto authors (see the AUTHORS file)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// THIS SOFTWARE IS PROVIDED BY the fiat-crypto authors "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design,
+// Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package field_curve448
+
+// The file provides arithmetic on the field Z/(2^448 - 2^224 - 1) using
+// unsaturated 64-bit integer arithmetic. It is derived primarily
+// from the machine generated Golang output from the fiat-crypto project.
+//
+// While the base implementation is provably correct, this implementation
+// makes no such claims as the port and optimizations were done by hand.
+//
+// TODO:
+// * When fiat-crypto supports it, using a saturated 64-bit limbs
+// instead of 56-bit limbs will be faster, though the gains are
+// minimal unless adcx/adox/mulx are used.
+
+import fiat "core:crypto/_fiat"
+import "core:math/bits"
+
+Loose_Field_Element :: distinct [8]u64
+Tight_Field_Element :: distinct [8]u64
+
+@(rodata)
+FE_ZERO := Tight_Field_Element{0, 0, 0, 0, 0, 0, 0, 0}
+@(rodata)
+FE_ONE := Tight_Field_Element{1, 0, 0, 0, 0, 0, 0, 0}
+
+_addcarryx_u56 :: #force_inline proc "contextless" (
+ arg1: fiat.u1,
+ arg2, arg3: u64,
+) -> (
+ out1: u64,
+ out2: fiat.u1,
+) {
+ x1 := ((u64(arg1) + arg2) + arg3)
+ x2 := (x1 & 0xffffffffffffff)
+ x3 := fiat.u1((x1 >> 56))
+ out1 = x2
+ out2 = x3
+ return
+}
+
+_subborrowx_u56 :: #force_inline proc "contextless" (
+ arg1: fiat.u1,
+ arg2, arg3: u64,
+) -> (
+ out1: u64,
+ out2: fiat.u1,
+) {
+ x1 := ((i64(arg2) - i64(arg1)) - i64(arg3))
+ x2 := fiat.u1((x1 >> 56))
+ x3 := (u64(x1) & 0xffffffffffffff)
+ out1 = x3
+ out2 = (0x0 - fiat.u1(x2))
+ return
+}
+
+fe_carry_mul :: proc "contextless" (out1: ^Tight_Field_Element, arg1, arg2: ^Loose_Field_Element) {
+ x2, x1 := bits.mul_u64(arg1[7], arg2[7])
+ x4, x3 := bits.mul_u64(arg1[7], arg2[6])
+ x6, x5 := bits.mul_u64(arg1[7], arg2[5])
+ x8, x7 := bits.mul_u64(arg1[6], arg2[7])
+ x10, x9 := bits.mul_u64(arg1[6], arg2[6])
+ x12, x11 := bits.mul_u64(arg1[5], arg2[7])
+ x14, x13 := bits.mul_u64(arg1[7], arg2[7])
+ x16, x15 := bits.mul_u64(arg1[7], arg2[6])
+ x18, x17 := bits.mul_u64(arg1[7], arg2[5])
+ x20, x19 := bits.mul_u64(arg1[6], arg2[7])
+ x22, x21 := bits.mul_u64(arg1[6], arg2[6])
+ x24, x23 := bits.mul_u64(arg1[5], arg2[7])
+ x26, x25 := bits.mul_u64(arg1[7], arg2[7])
+ x28, x27 := bits.mul_u64(arg1[7], arg2[6])
+ x30, x29 := bits.mul_u64(arg1[7], arg2[5])
+ x32, x31 := bits.mul_u64(arg1[7], arg2[4])
+ x34, x33 := bits.mul_u64(arg1[7], arg2[3])
+ x36, x35 := bits.mul_u64(arg1[7], arg2[2])
+ x38, x37 := bits.mul_u64(arg1[7], arg2[1])
+ x40, x39 := bits.mul_u64(arg1[6], arg2[7])
+ x42, x41 := bits.mul_u64(arg1[6], arg2[6])
+ x44, x43 := bits.mul_u64(arg1[6], arg2[5])
+ x46, x45 := bits.mul_u64(arg1[6], arg2[4])
+ x48, x47 := bits.mul_u64(arg1[6], arg2[3])
+ x50, x49 := bits.mul_u64(arg1[6], arg2[2])
+ x52, x51 := bits.mul_u64(arg1[5], arg2[7])
+ x54, x53 := bits.mul_u64(arg1[5], arg2[6])
+ x56, x55 := bits.mul_u64(arg1[5], arg2[5])
+ x58, x57 := bits.mul_u64(arg1[5], arg2[4])
+ x60, x59 := bits.mul_u64(arg1[5], arg2[3])
+ x62, x61 := bits.mul_u64(arg1[4], arg2[7])
+ x64, x63 := bits.mul_u64(arg1[4], arg2[6])
+ x66, x65 := bits.mul_u64(arg1[4], arg2[5])
+ x68, x67 := bits.mul_u64(arg1[4], arg2[4])
+ x70, x69 := bits.mul_u64(arg1[3], arg2[7])
+ x72, x71 := bits.mul_u64(arg1[3], arg2[6])
+ x74, x73 := bits.mul_u64(arg1[3], arg2[5])
+ x76, x75 := bits.mul_u64(arg1[2], arg2[7])
+ x78, x77 := bits.mul_u64(arg1[2], arg2[6])
+ x80, x79 := bits.mul_u64(arg1[1], arg2[7])
+ x82, x81 := bits.mul_u64(arg1[7], arg2[4])
+ x84, x83 := bits.mul_u64(arg1[7], arg2[3])
+ x86, x85 := bits.mul_u64(arg1[7], arg2[2])
+ x88, x87 := bits.mul_u64(arg1[7], arg2[1])
+ x90, x89 := bits.mul_u64(arg1[6], arg2[5])
+ x92, x91 := bits.mul_u64(arg1[6], arg2[4])
+ x94, x93 := bits.mul_u64(arg1[6], arg2[3])
+ x96, x95 := bits.mul_u64(arg1[6], arg2[2])
+ x98, x97 := bits.mul_u64(arg1[5], arg2[6])
+ x100, x99 := bits.mul_u64(arg1[5], arg2[5])
+ x102, x101 := bits.mul_u64(arg1[5], arg2[4])
+ x104, x103 := bits.mul_u64(arg1[5], arg2[3])
+ x106, x105 := bits.mul_u64(arg1[4], arg2[7])
+ x108, x107 := bits.mul_u64(arg1[4], arg2[6])
+ x110, x109 := bits.mul_u64(arg1[4], arg2[5])
+ x112, x111 := bits.mul_u64(arg1[4], arg2[4])
+ x114, x113 := bits.mul_u64(arg1[3], arg2[7])
+ x116, x115 := bits.mul_u64(arg1[3], arg2[6])
+ x118, x117 := bits.mul_u64(arg1[3], arg2[5])
+ x120, x119 := bits.mul_u64(arg1[2], arg2[7])
+ x122, x121 := bits.mul_u64(arg1[2], arg2[6])
+ x124, x123 := bits.mul_u64(arg1[1], arg2[7])
+ x126, x125 := bits.mul_u64(arg1[7], arg2[0])
+ x128, x127 := bits.mul_u64(arg1[6], arg2[1])
+ x130, x129 := bits.mul_u64(arg1[6], arg2[0])
+ x132, x131 := bits.mul_u64(arg1[5], arg2[2])
+ x134, x133 := bits.mul_u64(arg1[5], arg2[1])
+ x136, x135 := bits.mul_u64(arg1[5], arg2[0])
+ x138, x137 := bits.mul_u64(arg1[4], arg2[3])
+ x140, x139 := bits.mul_u64(arg1[4], arg2[2])
+ x142, x141 := bits.mul_u64(arg1[4], arg2[1])
+ x144, x143 := bits.mul_u64(arg1[4], arg2[0])
+ x146, x145 := bits.mul_u64(arg1[3], arg2[4])
+ x148, x147 := bits.mul_u64(arg1[3], arg2[3])
+ x150, x149 := bits.mul_u64(arg1[3], arg2[2])
+ x152, x151 := bits.mul_u64(arg1[3], arg2[1])
+ x154, x153 := bits.mul_u64(arg1[3], arg2[0])
+ x156, x155 := bits.mul_u64(arg1[2], arg2[5])
+ x158, x157 := bits.mul_u64(arg1[2], arg2[4])
+ x160, x159 := bits.mul_u64(arg1[2], arg2[3])
+ x162, x161 := bits.mul_u64(arg1[2], arg2[2])
+ x164, x163 := bits.mul_u64(arg1[2], arg2[1])
+ x166, x165 := bits.mul_u64(arg1[2], arg2[0])
+ x168, x167 := bits.mul_u64(arg1[1], arg2[6])
+ x170, x169 := bits.mul_u64(arg1[1], arg2[5])
+ x172, x171 := bits.mul_u64(arg1[1], arg2[4])
+ x174, x173 := bits.mul_u64(arg1[1], arg2[3])
+ x176, x175 := bits.mul_u64(arg1[1], arg2[2])
+ x178, x177 := bits.mul_u64(arg1[1], arg2[1])
+ x180, x179 := bits.mul_u64(arg1[1], arg2[0])
+ x182, x181 := bits.mul_u64(arg1[0], arg2[7])
+ x184, x183 := bits.mul_u64(arg1[0], arg2[6])
+ x186, x185 := bits.mul_u64(arg1[0], arg2[5])
+ x188, x187 := bits.mul_u64(arg1[0], arg2[4])
+ x190, x189 := bits.mul_u64(arg1[0], arg2[3])
+ x192, x191 := bits.mul_u64(arg1[0], arg2[2])
+ x194, x193 := bits.mul_u64(arg1[0], arg2[1])
+ x196, x195 := bits.mul_u64(arg1[0], arg2[0])
+ x197, x198 := bits.add_u64(x43, x31, u64(0x0))
+ x199, _ := bits.add_u64(x44, x32, u64(fiat.u1(x198)))
+ x201, x202 := bits.add_u64(x53, x197, u64(0x0))
+ x203, _ := bits.add_u64(x54, x199, u64(fiat.u1(x202)))
+ x205, x206 := bits.add_u64(x61, x201, u64(0x0))
+ x207, _ := bits.add_u64(x62, x203, u64(fiat.u1(x206)))
+ x209, x210 := bits.add_u64(x153, x205, u64(0x0))
+ x211, _ := bits.add_u64(x154, x207, u64(fiat.u1(x210)))
+ x213, x214 := bits.add_u64(x163, x209, u64(0x0))
+ x215, _ := bits.add_u64(x164, x211, u64(fiat.u1(x214)))
+ x217, x218 := bits.add_u64(x175, x213, u64(0x0))
+ x219, _ := bits.add_u64(x176, x215, u64(fiat.u1(x218)))
+ x221, x222 := bits.add_u64(x189, x217, u64(0x0))
+ x223, _ := bits.add_u64(x190, x219, u64(fiat.u1(x222)))
+ x225 := ((x221 >> 56) | ((x223 << 8) & 0xffffffffffffffff))
+ x226 := (x221 & 0xffffffffffffff)
+ x227, x228 := bits.add_u64(x89, x81, u64(0x0))
+ x229, _ := bits.add_u64(x90, x82, u64(fiat.u1(x228)))
+ x231, x232 := bits.add_u64(x97, x227, u64(0x0))
+ x233, _ := bits.add_u64(x98, x229, u64(fiat.u1(x232)))
+ x235, x236 := bits.add_u64(x105, x231, u64(0x0))
+ x237, _ := bits.add_u64(x106, x233, u64(fiat.u1(x236)))
+ x239, x240 := bits.add_u64(x125, x235, u64(0x0))
+ x241, _ := bits.add_u64(x126, x237, u64(fiat.u1(x240)))
+ x243, x244 := bits.add_u64(x127, x239, u64(0x0))
+ x245, _ := bits.add_u64(x128, x241, u64(fiat.u1(x244)))
+ x247, x248 := bits.add_u64(x131, x243, u64(0x0))
+ x249, _ := bits.add_u64(x132, x245, u64(fiat.u1(x248)))
+ x251, x252 := bits.add_u64(x137, x247, u64(0x0))
+ x253, _ := bits.add_u64(x138, x249, u64(fiat.u1(x252)))
+ x255, x256 := bits.add_u64(x145, x251, u64(0x0))
+ x257, _ := bits.add_u64(x146, x253, u64(fiat.u1(x256)))
+ x259, x260 := bits.add_u64(x155, x255, u64(0x0))
+ x261, _ := bits.add_u64(x156, x257, u64(fiat.u1(x260)))
+ x263, x264 := bits.add_u64(x167, x259, u64(0x0))
+ x265, _ := bits.add_u64(x168, x261, u64(fiat.u1(x264)))
+ x267, x268 := bits.add_u64(x181, x263, u64(0x0))
+ x269, _ := bits.add_u64(x182, x265, u64(fiat.u1(x268)))
+ x271, x272 := bits.add_u64(x25, x13, u64(0x0))
+ x273, _ := bits.add_u64(x26, x14, u64(fiat.u1(x272)))
+ x275, x276 := bits.add_u64(x83, x271, u64(0x0))
+ x277, _ := bits.add_u64(x84, x273, u64(fiat.u1(x276)))
+ x279, x280 := bits.add_u64(x91, x275, u64(0x0))
+ x281, _ := bits.add_u64(x92, x277, u64(fiat.u1(x280)))
+ x283, x284 := bits.add_u64(x99, x279, u64(0x0))
+ x285, _ := bits.add_u64(x100, x281, u64(fiat.u1(x284)))
+ x287, x288 := bits.add_u64(x107, x283, u64(0x0))
+ x289, _ := bits.add_u64(x108, x285, u64(fiat.u1(x288)))
+ x291, x292 := bits.add_u64(x113, x287, u64(0x0))
+ x293, _ := bits.add_u64(x114, x289, u64(fiat.u1(x292)))
+ x295, x296 := bits.add_u64(x129, x291, u64(0x0))
+ x297, _ := bits.add_u64(x130, x293, u64(fiat.u1(x296)))
+ x299, x300 := bits.add_u64(x133, x295, u64(0x0))
+ x301, _ := bits.add_u64(x134, x297, u64(fiat.u1(x300)))
+ x303, x304 := bits.add_u64(x139, x299, u64(0x0))
+ x305, _ := bits.add_u64(x140, x301, u64(fiat.u1(x304)))
+ x307, x308 := bits.add_u64(x147, x303, u64(0x0))
+ x309, _ := bits.add_u64(x148, x305, u64(fiat.u1(x308)))
+ x311, x312 := bits.add_u64(x157, x307, u64(0x0))
+ x313, _ := bits.add_u64(x158, x309, u64(fiat.u1(x312)))
+ x315, x316 := bits.add_u64(x169, x311, u64(0x0))
+ x317, _ := bits.add_u64(x170, x313, u64(fiat.u1(x316)))
+ x319, x320 := bits.add_u64(x183, x315, u64(0x0))
+ x321, _ := bits.add_u64(x184, x317, u64(fiat.u1(x320)))
+ x323, x324 := bits.add_u64(x19, x15, u64(0x0))
+ x325, _ := bits.add_u64(x20, x16, u64(fiat.u1(x324)))
+ x327, x328 := bits.add_u64(x27, x323, u64(0x0))
+ x329, _ := bits.add_u64(x28, x325, u64(fiat.u1(x328)))
+ x331, x332 := bits.add_u64(x39, x327, u64(0x0))
+ x333, _ := bits.add_u64(x40, x329, u64(fiat.u1(x332)))
+ x335, x336 := bits.add_u64(x85, x331, u64(0x0))
+ x337, _ := bits.add_u64(x86, x333, u64(fiat.u1(x336)))
+ x339, x340 := bits.add_u64(x93, x335, u64(0x0))
+ x341, _ := bits.add_u64(x94, x337, u64(fiat.u1(x340)))
+ x343, x344 := bits.add_u64(x101, x339, u64(0x0))
+ x345, _ := bits.add_u64(x102, x341, u64(fiat.u1(x344)))
+ x347, x348 := bits.add_u64(x109, x343, u64(0x0))
+ x349, _ := bits.add_u64(x110, x345, u64(fiat.u1(x348)))
+ x351, x352 := bits.add_u64(x115, x347, u64(0x0))
+ x353, _ := bits.add_u64(x116, x349, u64(fiat.u1(x352)))
+ x355, x356 := bits.add_u64(x119, x351, u64(0x0))
+ x357, _ := bits.add_u64(x120, x353, u64(fiat.u1(x356)))
+ x359, x360 := bits.add_u64(x135, x355, u64(0x0))
+ x361, _ := bits.add_u64(x136, x357, u64(fiat.u1(x360)))
+ x363, x364 := bits.add_u64(x141, x359, u64(0x0))
+ x365, _ := bits.add_u64(x142, x361, u64(fiat.u1(x364)))
+ x367, x368 := bits.add_u64(x149, x363, u64(0x0))
+ x369, _ := bits.add_u64(x150, x365, u64(fiat.u1(x368)))
+ x371, x372 := bits.add_u64(x159, x367, u64(0x0))
+ x373, _ := bits.add_u64(x160, x369, u64(fiat.u1(x372)))
+ x375, x376 := bits.add_u64(x171, x371, u64(0x0))
+ x377, _ := bits.add_u64(x172, x373, u64(fiat.u1(x376)))
+ x379, x380 := bits.add_u64(x185, x375, u64(0x0))
+ x381, _ := bits.add_u64(x186, x377, u64(fiat.u1(x380)))
+ x383, x384 := bits.add_u64(x21, x17, u64(0x0))
+ x385, _ := bits.add_u64(x22, x18, u64(fiat.u1(x384)))
+ x387, x388 := bits.add_u64(x23, x383, u64(0x0))
+ x389, _ := bits.add_u64(x24, x385, u64(fiat.u1(x388)))
+ x391, x392 := bits.add_u64(x29, x387, u64(0x0))
+ x393, _ := bits.add_u64(x30, x389, u64(fiat.u1(x392)))
+ x395, x396 := bits.add_u64(x41, x391, u64(0x0))
+ x397, _ := bits.add_u64(x42, x393, u64(fiat.u1(x396)))
+ x399, x400 := bits.add_u64(x51, x395, u64(0x0))
+ x401, _ := bits.add_u64(x52, x397, u64(fiat.u1(x400)))
+ x403, x404 := bits.add_u64(x87, x399, u64(0x0))
+ x405, _ := bits.add_u64(x88, x401, u64(fiat.u1(x404)))
+ x407, x408 := bits.add_u64(x95, x403, u64(0x0))
+ x409, _ := bits.add_u64(x96, x405, u64(fiat.u1(x408)))
+ x411, x412 := bits.add_u64(x103, x407, u64(0x0))
+ x413, _ := bits.add_u64(x104, x409, u64(fiat.u1(x412)))
+ x415, x416 := bits.add_u64(x111, x411, u64(0x0))
+ x417, _ := bits.add_u64(x112, x413, u64(fiat.u1(x416)))
+ x419, x420 := bits.add_u64(x117, x415, u64(0x0))
+ x421, _ := bits.add_u64(x118, x417, u64(fiat.u1(x420)))
+ x423, x424 := bits.add_u64(x121, x419, u64(0x0))
+ x425, _ := bits.add_u64(x122, x421, u64(fiat.u1(x424)))
+ x427, x428 := bits.add_u64(x123, x423, u64(0x0))
+ x429, _ := bits.add_u64(x124, x425, u64(fiat.u1(x428)))
+ x431, x432 := bits.add_u64(x143, x427, u64(0x0))
+ x433, _ := bits.add_u64(x144, x429, u64(fiat.u1(x432)))
+ x435, x436 := bits.add_u64(x151, x431, u64(0x0))
+ x437, _ := bits.add_u64(x152, x433, u64(fiat.u1(x436)))
+ x439, x440 := bits.add_u64(x161, x435, u64(0x0))
+ x441, _ := bits.add_u64(x162, x437, u64(fiat.u1(x440)))
+ x443, x444 := bits.add_u64(x173, x439, u64(0x0))
+ x445, _ := bits.add_u64(x174, x441, u64(fiat.u1(x444)))
+ x447, x448 := bits.add_u64(x187, x443, u64(0x0))
+ x449, _ := bits.add_u64(x188, x445, u64(fiat.u1(x448)))
+ x451, x452 := bits.add_u64(x33, x1, u64(0x0))
+ x453, _ := bits.add_u64(x34, x2, u64(fiat.u1(x452)))
+ x455, x456 := bits.add_u64(x45, x451, u64(0x0))
+ x457, _ := bits.add_u64(x46, x453, u64(fiat.u1(x456)))
+ x459, x460 := bits.add_u64(x55, x455, u64(0x0))
+ x461, _ := bits.add_u64(x56, x457, u64(fiat.u1(x460)))
+ x463, x464 := bits.add_u64(x63, x459, u64(0x0))
+ x465, _ := bits.add_u64(x64, x461, u64(fiat.u1(x464)))
+ x467, x468 := bits.add_u64(x69, x463, u64(0x0))
+ x469, _ := bits.add_u64(x70, x465, u64(fiat.u1(x468)))
+ x471, x472 := bits.add_u64(x165, x467, u64(0x0))
+ x473, _ := bits.add_u64(x166, x469, u64(fiat.u1(x472)))
+ x475, x476 := bits.add_u64(x177, x471, u64(0x0))
+ x477, _ := bits.add_u64(x178, x473, u64(fiat.u1(x476)))
+ x479, x480 := bits.add_u64(x191, x475, u64(0x0))
+ x481, _ := bits.add_u64(x192, x477, u64(fiat.u1(x480)))
+ x483, x484 := bits.add_u64(x7, x3, u64(0x0))
+ x485, _ := bits.add_u64(x8, x4, u64(fiat.u1(x484)))
+ x487, x488 := bits.add_u64(x35, x483, u64(0x0))
+ x489, _ := bits.add_u64(x36, x485, u64(fiat.u1(x488)))
+ x491, x492 := bits.add_u64(x47, x487, u64(0x0))
+ x493, _ := bits.add_u64(x48, x489, u64(fiat.u1(x492)))
+ x495, x496 := bits.add_u64(x57, x491, u64(0x0))
+ x497, _ := bits.add_u64(x58, x493, u64(fiat.u1(x496)))
+ x499, x500 := bits.add_u64(x65, x495, u64(0x0))
+ x501, _ := bits.add_u64(x66, x497, u64(fiat.u1(x500)))
+ x503, x504 := bits.add_u64(x71, x499, u64(0x0))
+ x505, _ := bits.add_u64(x72, x501, u64(fiat.u1(x504)))
+ x507, x508 := bits.add_u64(x75, x503, u64(0x0))
+ x509, _ := bits.add_u64(x76, x505, u64(fiat.u1(x508)))
+ x511, x512 := bits.add_u64(x179, x507, u64(0x0))
+ x513, _ := bits.add_u64(x180, x509, u64(fiat.u1(x512)))
+ x515, x516 := bits.add_u64(x193, x511, u64(0x0))
+ x517, _ := bits.add_u64(x194, x513, u64(fiat.u1(x516)))
+ x519, x520 := bits.add_u64(x9, x5, u64(0x0))
+ x521, _ := bits.add_u64(x10, x6, u64(fiat.u1(x520)))
+ x523, x524 := bits.add_u64(x11, x519, u64(0x0))
+ x525, _ := bits.add_u64(x12, x521, u64(fiat.u1(x524)))
+ x527, x528 := bits.add_u64(x37, x523, u64(0x0))
+ x529, _ := bits.add_u64(x38, x525, u64(fiat.u1(x528)))
+ x531, x532 := bits.add_u64(x49, x527, u64(0x0))
+ x533, _ := bits.add_u64(x50, x529, u64(fiat.u1(x532)))
+ x535, x536 := bits.add_u64(x59, x531, u64(0x0))
+ x537, _ := bits.add_u64(x60, x533, u64(fiat.u1(x536)))
+ x539, x540 := bits.add_u64(x67, x535, u64(0x0))
+ x541, _ := bits.add_u64(x68, x537, u64(fiat.u1(x540)))
+ x543, x544 := bits.add_u64(x73, x539, u64(0x0))
+ x545, _ := bits.add_u64(x74, x541, u64(fiat.u1(x544)))
+ x547, x548 := bits.add_u64(x77, x543, u64(0x0))
+ x549, _ := bits.add_u64(x78, x545, u64(fiat.u1(x548)))
+ x551, x552 := bits.add_u64(x79, x547, u64(0x0))
+ x553, _ := bits.add_u64(x80, x549, u64(fiat.u1(x552)))
+ x555, x556 := bits.add_u64(x195, x551, u64(0x0))
+ x557, _ := bits.add_u64(x196, x553, u64(fiat.u1(x556)))
+ x559, x560 := bits.add_u64(x225, x447, u64(0x0))
+ x561 := (u64(fiat.u1(x560)) + x449)
+ x562 := ((x267 >> 56) | ((x269 << 8) & 0xffffffffffffffff))
+ x563 := (x267 & 0xffffffffffffff)
+ x564, x565 := bits.add_u64(x559, x562, u64(0x0))
+ x566 := (u64(fiat.u1(x565)) + x561)
+ x567 := ((x564 >> 56) | ((x566 << 8) & 0xffffffffffffffff))
+ x568 := (x564 & 0xffffffffffffff)
+ x569, x570 := bits.add_u64(x555, x562, u64(0x0))
+ x571 := (u64(fiat.u1(x570)) + x557)
+ x572, x573 := bits.add_u64(x567, x379, u64(0x0))
+ x574 := (u64(fiat.u1(x573)) + x381)
+ x575 := ((x569 >> 56) | ((x571 << 8) & 0xffffffffffffffff))
+ x576 := (x569 & 0xffffffffffffff)
+ x577, x578 := bits.add_u64(x575, x515, u64(0x0))
+ x579 := (u64(fiat.u1(x578)) + x517)
+ x580 := ((x572 >> 56) | ((x574 << 8) & 0xffffffffffffffff))
+ x581 := (x572 & 0xffffffffffffff)
+ x582, x583 := bits.add_u64(x580, x319, u64(0x0))
+ x584 := (u64(fiat.u1(x583)) + x321)
+ x585 := ((x577 >> 56) | ((x579 << 8) & 0xffffffffffffffff))
+ x586 := (x577 & 0xffffffffffffff)
+ x587, x588 := bits.add_u64(x585, x479, u64(0x0))
+ x589 := (u64(fiat.u1(x588)) + x481)
+ x590 := ((x582 >> 56) | ((x584 << 8) & 0xffffffffffffffff))
+ x591 := (x582 & 0xffffffffffffff)
+ x592 := (x590 + x563)
+ x593 := ((x587 >> 56) | ((x589 << 8) & 0xffffffffffffffff))
+ x594 := (x587 & 0xffffffffffffff)
+ x595 := (x593 + x226)
+ x596 := (x592 >> 56)
+ x597 := (x592 & 0xffffffffffffff)
+ x598 := (x595 >> 56)
+ x599 := (x595 & 0xffffffffffffff)
+ x600 := (x568 + x596)
+ x601 := (x576 + x596)
+ x602 := (x598 + x600)
+ x603 := fiat.u1((x602 >> 56))
+ x604 := (x602 & 0xffffffffffffff)
+ x605 := (u64(x603) + x581)
+ x606 := fiat.u1((x601 >> 56))
+ x607 := (x601 & 0xffffffffffffff)
+ x608 := (u64(x606) + x586)
+ out1[0] = x607
+ out1[1] = x608
+ out1[2] = x594
+ out1[3] = x599
+ out1[4] = x604
+ out1[5] = x605
+ out1[6] = x591
+ out1[7] = x597
+}
+
+fe_carry_square :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
+ x1 := arg1[7]
+ x2 := arg1[7]
+ x3 := (x1 * 0x2)
+ x4 := (x2 * 0x2)
+ x5 := (arg1[7] * 0x2)
+ x6 := arg1[6]
+ x7 := arg1[6]
+ x8 := (x6 * 0x2)
+ x9 := (x7 * 0x2)
+ x10 := (arg1[6] * 0x2)
+ x11 := arg1[5]
+ x12 := arg1[5]
+ x13 := (x11 * 0x2)
+ x14 := (x12 * 0x2)
+ x15 := (arg1[5] * 0x2)
+ x16 := arg1[4]
+ x17 := arg1[4]
+ x18 := (arg1[4] * 0x2)
+ x19 := (arg1[3] * 0x2)
+ x20 := (arg1[2] * 0x2)
+ x21 := (arg1[1] * 0x2)
+ x23, x22 := bits.mul_u64(arg1[7], x1)
+ x25, x24 := bits.mul_u64(arg1[6], x3)
+ x27, x26 := bits.mul_u64(arg1[6], x6)
+ x29, x28 := bits.mul_u64(arg1[5], x3)
+ x31, x30 := bits.mul_u64(arg1[7], x1)
+ x33, x32 := bits.mul_u64(arg1[6], x3)
+ x35, x34 := bits.mul_u64(arg1[6], x6)
+ x37, x36 := bits.mul_u64(arg1[5], x3)
+ x39, x38 := bits.mul_u64(arg1[7], x2)
+ x41, x40 := bits.mul_u64(arg1[6], x4)
+ x43, x42 := bits.mul_u64(arg1[6], x7)
+ x45, x44 := bits.mul_u64(arg1[5], x4)
+ x47, x46 := bits.mul_u64(arg1[5], x9)
+ x49, x48 := bits.mul_u64(arg1[5], x8)
+ x51, x50 := bits.mul_u64(arg1[5], x12)
+ x53, x52 := bits.mul_u64(arg1[5], x11)
+ x55, x54 := bits.mul_u64(arg1[4], x4)
+ x57, x56 := bits.mul_u64(arg1[4], x3)
+ x59, x58 := bits.mul_u64(arg1[4], x9)
+ x61, x60 := bits.mul_u64(arg1[4], x8)
+ x63, x62 := bits.mul_u64(arg1[4], x14)
+ x65, x64 := bits.mul_u64(arg1[4], x13)
+ x67, x66 := bits.mul_u64(arg1[4], x17)
+ x69, x68 := bits.mul_u64(arg1[4], x16)
+ x71, x70 := bits.mul_u64(arg1[3], x4)
+ x73, x72 := bits.mul_u64(arg1[3], x3)
+ x75, x74 := bits.mul_u64(arg1[3], x9)
+ x77, x76 := bits.mul_u64(arg1[3], x8)
+ x79, x78 := bits.mul_u64(arg1[3], x14)
+ x81, x80 := bits.mul_u64(arg1[3], x13)
+ x83, x82 := bits.mul_u64(arg1[3], x18)
+ x85, x84 := bits.mul_u64(arg1[3], arg1[3])
+ x87, x86 := bits.mul_u64(arg1[2], x4)
+ x89, x88 := bits.mul_u64(arg1[2], x3)
+ x91, x90 := bits.mul_u64(arg1[2], x9)
+ x93, x92 := bits.mul_u64(arg1[2], x8)
+ x95, x94 := bits.mul_u64(arg1[2], x15)
+ x97, x96 := bits.mul_u64(arg1[2], x18)
+ x99, x98 := bits.mul_u64(arg1[2], x19)
+ x101, x100 := bits.mul_u64(arg1[2], arg1[2])
+ x103, x102 := bits.mul_u64(arg1[1], x4)
+ x105, x104 := bits.mul_u64(arg1[1], x3)
+ x107, x106 := bits.mul_u64(arg1[1], x10)
+ x109, x108 := bits.mul_u64(arg1[1], x15)
+ x111, x110 := bits.mul_u64(arg1[1], x18)
+ x113, x112 := bits.mul_u64(arg1[1], x19)
+ x115, x114 := bits.mul_u64(arg1[1], x20)
+ x117, x116 := bits.mul_u64(arg1[1], arg1[1])
+ x119, x118 := bits.mul_u64(arg1[0], x5)
+ x121, x120 := bits.mul_u64(arg1[0], x10)
+ x123, x122 := bits.mul_u64(arg1[0], x15)
+ x125, x124 := bits.mul_u64(arg1[0], x18)
+ x127, x126 := bits.mul_u64(arg1[0], x19)
+ x129, x128 := bits.mul_u64(arg1[0], x20)
+ x131, x130 := bits.mul_u64(arg1[0], x21)
+ x133, x132 := bits.mul_u64(arg1[0], arg1[0])
+ x134, x135 := bits.add_u64(x54, x46, u64(0x0))
+ x136, _ := bits.add_u64(x55, x47, u64(fiat.u1(x135)))
+ x138, x139 := bits.add_u64(x114, x134, u64(0x0))
+ x140, _ := bits.add_u64(x115, x136, u64(fiat.u1(x139)))
+ x142, x143 := bits.add_u64(x126, x138, u64(0x0))
+ x144, _ := bits.add_u64(x127, x140, u64(fiat.u1(x143)))
+ x146 := ((x142 >> 56) | ((x144 << 8) & 0xffffffffffffffff))
+ x147 := (x142 & 0xffffffffffffff)
+ x148, x149 := bits.add_u64(x56, x48, u64(0x0))
+ x150, _ := bits.add_u64(x57, x49, u64(fiat.u1(x149)))
+ x152, x153 := bits.add_u64(x82, x148, u64(0x0))
+ x154, _ := bits.add_u64(x83, x150, u64(fiat.u1(x153)))
+ x156, x157 := bits.add_u64(x94, x152, u64(0x0))
+ x158, _ := bits.add_u64(x95, x154, u64(fiat.u1(x157)))
+ x160, x161 := bits.add_u64(x106, x156, u64(0x0))
+ x162, _ := bits.add_u64(x107, x158, u64(fiat.u1(x161)))
+ x164, x165 := bits.add_u64(x118, x160, u64(0x0))
+ x166, _ := bits.add_u64(x119, x162, u64(fiat.u1(x165)))
+ x168, x169 := bits.add_u64(x38, x30, u64(0x0))
+ x170, _ := bits.add_u64(x39, x31, u64(fiat.u1(x169)))
+ x172, x173 := bits.add_u64(x52, x168, u64(0x0))
+ x174, _ := bits.add_u64(x53, x170, u64(fiat.u1(x173)))
+ x176, x177 := bits.add_u64(x60, x172, u64(0x0))
+ x178, _ := bits.add_u64(x61, x174, u64(fiat.u1(x177)))
+ x180, x181 := bits.add_u64(x72, x176, u64(0x0))
+ x182, _ := bits.add_u64(x73, x178, u64(fiat.u1(x181)))
+ x184, x185 := bits.add_u64(x84, x180, u64(0x0))
+ x186, _ := bits.add_u64(x85, x182, u64(fiat.u1(x185)))
+ x188, x189 := bits.add_u64(x96, x184, u64(0x0))
+ x190, _ := bits.add_u64(x97, x186, u64(fiat.u1(x189)))
+ x192, x193 := bits.add_u64(x108, x188, u64(0x0))
+ x194, _ := bits.add_u64(x109, x190, u64(fiat.u1(x193)))
+ x196, x197 := bits.add_u64(x120, x192, u64(0x0))
+ x198, _ := bits.add_u64(x121, x194, u64(fiat.u1(x197)))
+ x200, x201 := bits.add_u64(x40, x32, u64(0x0))
+ x202, _ := bits.add_u64(x41, x33, u64(fiat.u1(x201)))
+ x204, x205 := bits.add_u64(x64, x200, u64(0x0))
+ x206, _ := bits.add_u64(x65, x202, u64(fiat.u1(x205)))
+ x208, x209 := bits.add_u64(x76, x204, u64(0x0))
+ x210, _ := bits.add_u64(x77, x206, u64(fiat.u1(x209)))
+ x212, x213 := bits.add_u64(x88, x208, u64(0x0))
+ x214, _ := bits.add_u64(x89, x210, u64(fiat.u1(x213)))
+ x216, x217 := bits.add_u64(x98, x212, u64(0x0))
+ x218, _ := bits.add_u64(x99, x214, u64(fiat.u1(x217)))
+ x220, x221 := bits.add_u64(x110, x216, u64(0x0))
+ x222, _ := bits.add_u64(x111, x218, u64(fiat.u1(x221)))
+ x224, x225 := bits.add_u64(x122, x220, u64(0x0))
+ x226, _ := bits.add_u64(x123, x222, u64(fiat.u1(x225)))
+ x228, x229 := bits.add_u64(x36, x34, u64(0x0))
+ x230, _ := bits.add_u64(x37, x35, u64(fiat.u1(x229)))
+ x232, x233 := bits.add_u64(x42, x228, u64(0x0))
+ x234, _ := bits.add_u64(x43, x230, u64(fiat.u1(x233)))
+ x236, x237 := bits.add_u64(x44, x232, u64(0x0))
+ x238, _ := bits.add_u64(x45, x234, u64(fiat.u1(x237)))
+ x240, x241 := bits.add_u64(x68, x236, u64(0x0))
+ x242, _ := bits.add_u64(x69, x238, u64(fiat.u1(x241)))
+ x244, x245 := bits.add_u64(x80, x240, u64(0x0))
+ x246, _ := bits.add_u64(x81, x242, u64(fiat.u1(x245)))
+ x248, x249 := bits.add_u64(x92, x244, u64(0x0))
+ x250, _ := bits.add_u64(x93, x246, u64(fiat.u1(x249)))
+ x252, x253 := bits.add_u64(x100, x248, u64(0x0))
+ x254, _ := bits.add_u64(x101, x250, u64(fiat.u1(x253)))
+ x256, x257 := bits.add_u64(x104, x252, u64(0x0))
+ x258, _ := bits.add_u64(x105, x254, u64(fiat.u1(x257)))
+ x260, x261 := bits.add_u64(x112, x256, u64(0x0))
+ x262, _ := bits.add_u64(x113, x258, u64(fiat.u1(x261)))
+ x264, x265 := bits.add_u64(x124, x260, u64(0x0))
+ x266, _ := bits.add_u64(x125, x262, u64(fiat.u1(x265)))
+ x268, x269 := bits.add_u64(x50, x22, u64(0x0))
+ x270, _ := bits.add_u64(x51, x23, u64(fiat.u1(x269)))
+ x272, x273 := bits.add_u64(x58, x268, u64(0x0))
+ x274, _ := bits.add_u64(x59, x270, u64(fiat.u1(x273)))
+ x276, x277 := bits.add_u64(x70, x272, u64(0x0))
+ x278, _ := bits.add_u64(x71, x274, u64(fiat.u1(x277)))
+ x280, x281 := bits.add_u64(x116, x276, u64(0x0))
+ x282, _ := bits.add_u64(x117, x278, u64(fiat.u1(x281)))
+ x284, x285 := bits.add_u64(x128, x280, u64(0x0))
+ x286, _ := bits.add_u64(x129, x282, u64(fiat.u1(x285)))
+ x288, x289 := bits.add_u64(x62, x24, u64(0x0))
+ x290, _ := bits.add_u64(x63, x25, u64(fiat.u1(x289)))
+ x292, x293 := bits.add_u64(x74, x288, u64(0x0))
+ x294, _ := bits.add_u64(x75, x290, u64(fiat.u1(x293)))
+ x296, x297 := bits.add_u64(x86, x292, u64(0x0))
+ x298, _ := bits.add_u64(x87, x294, u64(fiat.u1(x297)))
+ x300, x301 := bits.add_u64(x130, x296, u64(0x0))
+ x302, _ := bits.add_u64(x131, x298, u64(fiat.u1(x301)))
+ x304, x305 := bits.add_u64(x28, x26, u64(0x0))
+ x306, _ := bits.add_u64(x29, x27, u64(fiat.u1(x305)))
+ x308, x309 := bits.add_u64(x66, x304, u64(0x0))
+ x310, _ := bits.add_u64(x67, x306, u64(fiat.u1(x309)))
+ x312, x313 := bits.add_u64(x78, x308, u64(0x0))
+ x314, _ := bits.add_u64(x79, x310, u64(fiat.u1(x313)))
+ x316, x317 := bits.add_u64(x90, x312, u64(0x0))
+ x318, _ := bits.add_u64(x91, x314, u64(fiat.u1(x317)))
+ x320, x321 := bits.add_u64(x102, x316, u64(0x0))
+ x322, _ := bits.add_u64(x103, x318, u64(fiat.u1(x321)))
+ x324, x325 := bits.add_u64(x132, x320, u64(0x0))
+ x326, _ := bits.add_u64(x133, x322, u64(fiat.u1(x325)))
+ x328, x329 := bits.add_u64(x146, x264, u64(0x0))
+ x330 := (u64(fiat.u1(x329)) + x266)
+ x331 := ((x164 >> 56) | ((x166 << 8) & 0xffffffffffffffff))
+ x332 := (x164 & 0xffffffffffffff)
+ x333, x334 := bits.add_u64(x328, x331, u64(0x0))
+ x335 := (u64(fiat.u1(x334)) + x330)
+ x336 := ((x333 >> 56) | ((x335 << 8) & 0xffffffffffffffff))
+ x337 := (x333 & 0xffffffffffffff)
+ x338, x339 := bits.add_u64(x324, x331, u64(0x0))
+ x340 := (u64(fiat.u1(x339)) + x326)
+ x341, x342 := bits.add_u64(x336, x224, u64(0x0))
+ x343 := (u64(fiat.u1(x342)) + x226)
+ x344 := ((x338 >> 56) | ((x340 << 8) & 0xffffffffffffffff))
+ x345 := (x338 & 0xffffffffffffff)
+ x346, x347 := bits.add_u64(x344, x300, u64(0x0))
+ x348 := (u64(fiat.u1(x347)) + x302)
+ x349 := ((x341 >> 56) | ((x343 << 8) & 0xffffffffffffffff))
+ x350 := (x341 & 0xffffffffffffff)
+ x351, x352 := bits.add_u64(x349, x196, u64(0x0))
+ x353 := (u64(fiat.u1(x352)) + x198)
+ x354 := ((x346 >> 56) | ((x348 << 8) & 0xffffffffffffffff))
+ x355 := (x346 & 0xffffffffffffff)
+ x356, x357 := bits.add_u64(x354, x284, u64(0x0))
+ x358 := (u64(fiat.u1(x357)) + x286)
+ x359 := ((x351 >> 56) | ((x353 << 8) & 0xffffffffffffffff))
+ x360 := (x351 & 0xffffffffffffff)
+ x361 := (x359 + x332)
+ x362 := ((x356 >> 56) | ((x358 << 8) & 0xffffffffffffffff))
+ x363 := (x356 & 0xffffffffffffff)
+ x364 := (x362 + x147)
+ x365 := (x361 >> 56)
+ x366 := (x361 & 0xffffffffffffff)
+ x367 := (x364 >> 56)
+ x368 := (x364 & 0xffffffffffffff)
+ x369 := (x337 + x365)
+ x370 := (x345 + x365)
+ x371 := (x367 + x369)
+ x372 := fiat.u1((x371 >> 56))
+ x373 := (x371 & 0xffffffffffffff)
+ x374 := (u64(x372) + x350)
+ x375 := fiat.u1((x370 >> 56))
+ x376 := (x370 & 0xffffffffffffff)
+ x377 := (u64(x375) + x355)
+ out1[0] = x376
+ out1[1] = x377
+ out1[2] = x363
+ out1[3] = x368
+ out1[4] = x373
+ out1[5] = x374
+ out1[6] = x360
+ out1[7] = x366
+}
+
+fe_carry :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
+ x1 := arg1[3]
+ x2 := arg1[7]
+ x3 := (x2 >> 56)
+ x4 := (((x1 >> 56) + arg1[4]) + x3)
+ x5 := (arg1[0] + x3)
+ x6 := ((x4 >> 56) + arg1[5])
+ x7 := ((x5 >> 56) + arg1[1])
+ x8 := ((x6 >> 56) + arg1[6])
+ x9 := ((x7 >> 56) + arg1[2])
+ x10 := ((x8 >> 56) + (x2 & 0xffffffffffffff))
+ x11 := ((x9 >> 56) + (x1 & 0xffffffffffffff))
+ x12 := fiat.u1((x10 >> 56))
+ x13 := ((x5 & 0xffffffffffffff) + u64(x12))
+ x14 := (u64(fiat.u1((x11 >> 56))) + ((x4 & 0xffffffffffffff) + u64(x12)))
+ x15 := (x13 & 0xffffffffffffff)
+ x16 := (u64(fiat.u1((x13 >> 56))) + (x7 & 0xffffffffffffff))
+ x17 := (x9 & 0xffffffffffffff)
+ x18 := (x11 & 0xffffffffffffff)
+ x19 := (x14 & 0xffffffffffffff)
+ x20 := (u64(fiat.u1((x14 >> 56))) + (x6 & 0xffffffffffffff))
+ x21 := (x8 & 0xffffffffffffff)
+ x22 := (x10 & 0xffffffffffffff)
+ out1[0] = x15
+ out1[1] = x16
+ out1[2] = x17
+ out1[3] = x18
+ out1[4] = x19
+ out1[5] = x20
+ out1[6] = x21
+ out1[7] = x22
+}
+
+fe_add :: proc "contextless" (out1: ^Loose_Field_Element, arg1, arg2: ^Tight_Field_Element) {
+ x1 := (arg1[0] + arg2[0])
+ x2 := (arg1[1] + arg2[1])
+ x3 := (arg1[2] + arg2[2])
+ x4 := (arg1[3] + arg2[3])
+ x5 := (arg1[4] + arg2[4])
+ x6 := (arg1[5] + arg2[5])
+ x7 := (arg1[6] + arg2[6])
+ x8 := (arg1[7] + arg2[7])
+ out1[0] = x1
+ out1[1] = x2
+ out1[2] = x3
+ out1[3] = x4
+ out1[4] = x5
+ out1[5] = x6
+ out1[6] = x7
+ out1[7] = x8
+}
+
+fe_sub :: proc "contextless" (out1: ^Loose_Field_Element, arg1, arg2: ^Tight_Field_Element) {
+ x1 := ((0x1fffffffffffffe + arg1[0]) - arg2[0])
+ x2 := ((0x1fffffffffffffe + arg1[1]) - arg2[1])
+ x3 := ((0x1fffffffffffffe + arg1[2]) - arg2[2])
+ x4 := ((0x1fffffffffffffe + arg1[3]) - arg2[3])
+ x5 := ((0x1fffffffffffffc + arg1[4]) - arg2[4])
+ x6 := ((0x1fffffffffffffe + arg1[5]) - arg2[5])
+ x7 := ((0x1fffffffffffffe + arg1[6]) - arg2[6])
+ x8 := ((0x1fffffffffffffe + arg1[7]) - arg2[7])
+ out1[0] = x1
+ out1[1] = x2
+ out1[2] = x3
+ out1[3] = x4
+ out1[4] = x5
+ out1[5] = x6
+ out1[6] = x7
+ out1[7] = x8
+}
+
+fe_opp :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_Element) {
+ x1 := (0x1fffffffffffffe - arg1[0])
+ x2 := (0x1fffffffffffffe - arg1[1])
+ x3 := (0x1fffffffffffffe - arg1[2])
+ x4 := (0x1fffffffffffffe - arg1[3])
+ x5 := (0x1fffffffffffffc - arg1[4])
+ x6 := (0x1fffffffffffffe - arg1[5])
+ x7 := (0x1fffffffffffffe - arg1[6])
+ x8 := (0x1fffffffffffffe - arg1[7])
+ out1[0] = x1
+ out1[1] = x2
+ out1[2] = x3
+ out1[3] = x4
+ out1[4] = x5
+ out1[5] = x6
+ out1[6] = x7
+ out1[7] = x8
+}
+
+@(optimization_mode = "none")
+fe_cond_assign :: #force_no_inline proc "contextless" (
+ out1, arg1: ^Tight_Field_Element,
+ arg2: int,
+) {
+ x1 := fiat.cmovznz_u64(fiat.u1(arg2), out1[0], arg1[0])
+ x2 := fiat.cmovznz_u64(fiat.u1(arg2), out1[1], arg1[1])
+ x3 := fiat.cmovznz_u64(fiat.u1(arg2), out1[2], arg1[2])
+ x4 := fiat.cmovznz_u64(fiat.u1(arg2), out1[3], arg1[3])
+ x5 := fiat.cmovznz_u64(fiat.u1(arg2), out1[4], arg1[4])
+ x6 := fiat.cmovznz_u64(fiat.u1(arg2), out1[5], arg1[5])
+ x7 := fiat.cmovznz_u64(fiat.u1(arg2), out1[6], arg1[6])
+ x8 := fiat.cmovznz_u64(fiat.u1(arg2), out1[7], arg1[7])
+ out1[0] = x1
+ out1[1] = x2
+ out1[2] = x3
+ out1[3] = x4
+ out1[4] = x5
+ out1[5] = x6
+ out1[6] = x7
+ out1[7] = x8
+}
+
+fe_to_bytes :: proc "contextless" (out1: ^[56]byte, arg1: ^Tight_Field_Element) {
+ x1, x2 := _subborrowx_u56(0x0, arg1[0], 0xffffffffffffff)
+ x3, x4 := _subborrowx_u56(x2, arg1[1], 0xffffffffffffff)
+ x5, x6 := _subborrowx_u56(x4, arg1[2], 0xffffffffffffff)
+ x7, x8 := _subborrowx_u56(x6, arg1[3], 0xffffffffffffff)
+ x9, x10 := _subborrowx_u56(x8, arg1[4], 0xfffffffffffffe)
+ x11, x12 := _subborrowx_u56(x10, arg1[5], 0xffffffffffffff)
+ x13, x14 := _subborrowx_u56(x12, arg1[6], 0xffffffffffffff)
+ x15, x16 := _subborrowx_u56(x14, arg1[7], 0xffffffffffffff)
+ x17 := fiat.cmovznz_u64(x16, u64(0x0), 0xffffffffffffffff)
+ x18, x19 := _addcarryx_u56(0x0, x1, (x17 & 0xffffffffffffff))
+ x20, x21 := _addcarryx_u56(x19, x3, (x17 & 0xffffffffffffff))
+ x22, x23 := _addcarryx_u56(x21, x5, (x17 & 0xffffffffffffff))
+ x24, x25 := _addcarryx_u56(x23, x7, (x17 & 0xffffffffffffff))
+ x26, x27 := _addcarryx_u56(x25, x9, (x17 & 0xfffffffffffffe))
+ x28, x29 := _addcarryx_u56(x27, x11, (x17 & 0xffffffffffffff))
+ x30, x31 := _addcarryx_u56(x29, x13, (x17 & 0xffffffffffffff))
+ x32, _ := _addcarryx_u56(x31, x15, (x17 & 0xffffffffffffff))
+ x34 := (u8(x18) & 0xff)
+ x35 := (x18 >> 8)
+ x36 := (u8(x35) & 0xff)
+ x37 := (x35 >> 8)
+ x38 := (u8(x37) & 0xff)
+ x39 := (x37 >> 8)
+ x40 := (u8(x39) & 0xff)
+ x41 := (x39 >> 8)
+ x42 := (u8(x41) & 0xff)
+ x43 := (x41 >> 8)
+ x44 := (u8(x43) & 0xff)
+ x45 := u8((x43 >> 8))
+ x46 := (u8(x20) & 0xff)
+ x47 := (x20 >> 8)
+ x48 := (u8(x47) & 0xff)
+ x49 := (x47 >> 8)
+ x50 := (u8(x49) & 0xff)
+ x51 := (x49 >> 8)
+ x52 := (u8(x51) & 0xff)
+ x53 := (x51 >> 8)
+ x54 := (u8(x53) & 0xff)
+ x55 := (x53 >> 8)
+ x56 := (u8(x55) & 0xff)
+ x57 := u8((x55 >> 8))
+ x58 := (u8(x22) & 0xff)
+ x59 := (x22 >> 8)
+ x60 := (u8(x59) & 0xff)
+ x61 := (x59 >> 8)
+ x62 := (u8(x61) & 0xff)
+ x63 := (x61 >> 8)
+ x64 := (u8(x63) & 0xff)
+ x65 := (x63 >> 8)
+ x66 := (u8(x65) & 0xff)
+ x67 := (x65 >> 8)
+ x68 := (u8(x67) & 0xff)
+ x69 := u8((x67 >> 8))
+ x70 := (u8(x24) & 0xff)
+ x71 := (x24 >> 8)
+ x72 := (u8(x71) & 0xff)
+ x73 := (x71 >> 8)
+ x74 := (u8(x73) & 0xff)
+ x75 := (x73 >> 8)
+ x76 := (u8(x75) & 0xff)
+ x77 := (x75 >> 8)
+ x78 := (u8(x77) & 0xff)
+ x79 := (x77 >> 8)
+ x80 := (u8(x79) & 0xff)
+ x81 := u8((x79 >> 8))
+ x82 := (u8(x26) & 0xff)
+ x83 := (x26 >> 8)
+ x84 := (u8(x83) & 0xff)
+ x85 := (x83 >> 8)
+ x86 := (u8(x85) & 0xff)
+ x87 := (x85 >> 8)
+ x88 := (u8(x87) & 0xff)
+ x89 := (x87 >> 8)
+ x90 := (u8(x89) & 0xff)
+ x91 := (x89 >> 8)
+ x92 := (u8(x91) & 0xff)
+ x93 := u8((x91 >> 8))
+ x94 := (u8(x28) & 0xff)
+ x95 := (x28 >> 8)
+ x96 := (u8(x95) & 0xff)
+ x97 := (x95 >> 8)
+ x98 := (u8(x97) & 0xff)
+ x99 := (x97 >> 8)
+ x100 := (u8(x99) & 0xff)
+ x101 := (x99 >> 8)
+ x102 := (u8(x101) & 0xff)
+ x103 := (x101 >> 8)
+ x104 := (u8(x103) & 0xff)
+ x105 := u8((x103 >> 8))
+ x106 := (u8(x30) & 0xff)
+ x107 := (x30 >> 8)
+ x108 := (u8(x107) & 0xff)
+ x109 := (x107 >> 8)
+ x110 := (u8(x109) & 0xff)
+ x111 := (x109 >> 8)
+ x112 := (u8(x111) & 0xff)
+ x113 := (x111 >> 8)
+ x114 := (u8(x113) & 0xff)
+ x115 := (x113 >> 8)
+ x116 := (u8(x115) & 0xff)
+ x117 := u8((x115 >> 8))
+ x118 := (u8(x32) & 0xff)
+ x119 := (x32 >> 8)
+ x120 := (u8(x119) & 0xff)
+ x121 := (x119 >> 8)
+ x122 := (u8(x121) & 0xff)
+ x123 := (x121 >> 8)
+ x124 := (u8(x123) & 0xff)
+ x125 := (x123 >> 8)
+ x126 := (u8(x125) & 0xff)
+ x127 := (x125 >> 8)
+ x128 := (u8(x127) & 0xff)
+ x129 := u8((x127 >> 8))
+ out1[0] = x34
+ out1[1] = x36
+ out1[2] = x38
+ out1[3] = x40
+ out1[4] = x42
+ out1[5] = x44
+ out1[6] = x45
+ out1[7] = x46
+ out1[8] = x48
+ out1[9] = x50
+ out1[10] = x52
+ out1[11] = x54
+ out1[12] = x56
+ out1[13] = x57
+ out1[14] = x58
+ out1[15] = x60
+ out1[16] = x62
+ out1[17] = x64
+ out1[18] = x66
+ out1[19] = x68
+ out1[20] = x69
+ out1[21] = x70
+ out1[22] = x72
+ out1[23] = x74
+ out1[24] = x76
+ out1[25] = x78
+ out1[26] = x80
+ out1[27] = x81
+ out1[28] = x82
+ out1[29] = x84
+ out1[30] = x86
+ out1[31] = x88
+ out1[32] = x90
+ out1[33] = x92
+ out1[34] = x93
+ out1[35] = x94
+ out1[36] = x96
+ out1[37] = x98
+ out1[38] = x100
+ out1[39] = x102
+ out1[40] = x104
+ out1[41] = x105
+ out1[42] = x106
+ out1[43] = x108
+ out1[44] = x110
+ out1[45] = x112
+ out1[46] = x114
+ out1[47] = x116
+ out1[48] = x117
+ out1[49] = x118
+ out1[50] = x120
+ out1[51] = x122
+ out1[52] = x124
+ out1[53] = x126
+ out1[54] = x128
+ out1[55] = x129
+}
+
+fe_from_bytes :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^[56]byte) {
+ x1 := (u64(arg1[55]) << 48)
+ x2 := (u64(arg1[54]) << 40)
+ x3 := (u64(arg1[53]) << 32)
+ x4 := (u64(arg1[52]) << 24)
+ x5 := (u64(arg1[51]) << 16)
+ x6 := (u64(arg1[50]) << 8)
+ x7 := arg1[49]
+ x8 := (u64(arg1[48]) << 48)
+ x9 := (u64(arg1[47]) << 40)
+ x10 := (u64(arg1[46]) << 32)
+ x11 := (u64(arg1[45]) << 24)
+ x12 := (u64(arg1[44]) << 16)
+ x13 := (u64(arg1[43]) << 8)
+ x14 := arg1[42]
+ x15 := (u64(arg1[41]) << 48)
+ x16 := (u64(arg1[40]) << 40)
+ x17 := (u64(arg1[39]) << 32)
+ x18 := (u64(arg1[38]) << 24)
+ x19 := (u64(arg1[37]) << 16)
+ x20 := (u64(arg1[36]) << 8)
+ x21 := arg1[35]
+ x22 := (u64(arg1[34]) << 48)
+ x23 := (u64(arg1[33]) << 40)
+ x24 := (u64(arg1[32]) << 32)
+ x25 := (u64(arg1[31]) << 24)
+ x26 := (u64(arg1[30]) << 16)
+ x27 := (u64(arg1[29]) << 8)
+ x28 := arg1[28]
+ x29 := (u64(arg1[27]) << 48)
+ x30 := (u64(arg1[26]) << 40)
+ x31 := (u64(arg1[25]) << 32)
+ x32 := (u64(arg1[24]) << 24)
+ x33 := (u64(arg1[23]) << 16)
+ x34 := (u64(arg1[22]) << 8)
+ x35 := arg1[21]
+ x36 := (u64(arg1[20]) << 48)
+ x37 := (u64(arg1[19]) << 40)
+ x38 := (u64(arg1[18]) << 32)
+ x39 := (u64(arg1[17]) << 24)
+ x40 := (u64(arg1[16]) << 16)
+ x41 := (u64(arg1[15]) << 8)
+ x42 := arg1[14]
+ x43 := (u64(arg1[13]) << 48)
+ x44 := (u64(arg1[12]) << 40)
+ x45 := (u64(arg1[11]) << 32)
+ x46 := (u64(arg1[10]) << 24)
+ x47 := (u64(arg1[9]) << 16)
+ x48 := (u64(arg1[8]) << 8)
+ x49 := arg1[7]
+ x50 := (u64(arg1[6]) << 48)
+ x51 := (u64(arg1[5]) << 40)
+ x52 := (u64(arg1[4]) << 32)
+ x53 := (u64(arg1[3]) << 24)
+ x54 := (u64(arg1[2]) << 16)
+ x55 := (u64(arg1[1]) << 8)
+ x56 := arg1[0]
+ x57 := (x55 + u64(x56))
+ x58 := (x54 + x57)
+ x59 := (x53 + x58)
+ x60 := (x52 + x59)
+ x61 := (x51 + x60)
+ x62 := (x50 + x61)
+ x63 := (x48 + u64(x49))
+ x64 := (x47 + x63)
+ x65 := (x46 + x64)
+ x66 := (x45 + x65)
+ x67 := (x44 + x66)
+ x68 := (x43 + x67)
+ x69 := (x41 + u64(x42))
+ x70 := (x40 + x69)
+ x71 := (x39 + x70)
+ x72 := (x38 + x71)
+ x73 := (x37 + x72)
+ x74 := (x36 + x73)
+ x75 := (x34 + u64(x35))
+ x76 := (x33 + x75)
+ x77 := (x32 + x76)
+ x78 := (x31 + x77)
+ x79 := (x30 + x78)
+ x80 := (x29 + x79)
+ x81 := (x27 + u64(x28))
+ x82 := (x26 + x81)
+ x83 := (x25 + x82)
+ x84 := (x24 + x83)
+ x85 := (x23 + x84)
+ x86 := (x22 + x85)
+ x87 := (x20 + u64(x21))
+ x88 := (x19 + x87)
+ x89 := (x18 + x88)
+ x90 := (x17 + x89)
+ x91 := (x16 + x90)
+ x92 := (x15 + x91)
+ x93 := (x13 + u64(x14))
+ x94 := (x12 + x93)
+ x95 := (x11 + x94)
+ x96 := (x10 + x95)
+ x97 := (x9 + x96)
+ x98 := (x8 + x97)
+ x99 := (x6 + u64(x7))
+ x100 := (x5 + x99)
+ x101 := (x4 + x100)
+ x102 := (x3 + x101)
+ x103 := (x2 + x102)
+ x104 := (x1 + x103)
+ out1[0] = x62
+ out1[1] = x68
+ out1[2] = x74
+ out1[3] = x80
+ out1[4] = x86
+ out1[5] = x92
+ out1[6] = x98
+ out1[7] = x104
+}
+
+fe_relax :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_Element) {
+ x1 := arg1[0]
+ x2 := arg1[1]
+ x3 := arg1[2]
+ x4 := arg1[3]
+ x5 := arg1[4]
+ x6 := arg1[5]
+ x7 := arg1[6]
+ x8 := arg1[7]
+ out1[0] = x1
+ out1[1] = x2
+ out1[2] = x3
+ out1[3] = x4
+ out1[4] = x5
+ out1[5] = x6
+ out1[6] = x7
+ out1[7] = x8
+}
diff --git a/core/crypto/_fiat/field_poly1305/field.odin b/core/crypto/_fiat/field_poly1305/field.odin
index b12046858..caaece98e 100644
--- a/core/crypto/_fiat/field_poly1305/field.odin
+++ b/core/crypto/_fiat/field_poly1305/field.odin
@@ -1,6 +1,5 @@
package field_poly1305
-import "base:intrinsics"
import "core:encoding/endian"
import "core:mem"
@@ -29,9 +28,7 @@ fe_from_bytes :: #force_inline proc "contextless" (
// makes implementing the actual MAC block processing considerably
// neater.
- if len(arg1) != 16 {
- intrinsics.trap()
- }
+ ensure_contextless(len(arg1) == 16, "poly1305: invalid field element size")
// While it may be unwise to do deserialization here on our
// own when fiat-crypto provides equivalent functionality,
diff --git a/core/crypto/_fiat/field_scalar25519/field.odin b/core/crypto/_fiat/field_scalar25519/field.odin
index 9b40661b7..933637c54 100644
--- a/core/crypto/_fiat/field_scalar25519/field.odin
+++ b/core/crypto/_fiat/field_scalar25519/field.odin
@@ -1,18 +1,17 @@
package field_scalar25519
-import "base:intrinsics"
import "core:encoding/endian"
import "core:math/bits"
import "core:mem"
-@(private)
+@(private, rodata)
_TWO_168 := Montgomery_Domain_Field_Element {
0x5b8ab432eac74798,
0x38afddd6de59d5d7,
0xa2c131b399411b7c,
0x6329a7ed9ce5a30,
}
-@(private)
+@(private, rodata)
_TWO_336 := Montgomery_Domain_Field_Element {
0xbd3d108e2b35ecc5,
0x5c3a3718bdf9c90b,
@@ -95,9 +94,8 @@ fe_from_bytes_wide :: proc "contextless" (
@(private)
_fe_from_bytes_short :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element, arg1: []byte) {
// INVARIANT: len(arg1) < 32.
- if len(arg1) >= 32 {
- intrinsics.trap()
- }
+ ensure_contextless(len(arg1) < 32, "edwards25519: oversized short scalar")
+
tmp: [32]byte
copy(tmp[:], arg1)
@@ -106,9 +104,7 @@ _fe_from_bytes_short :: proc "contextless" (out1: ^Montgomery_Domain_Field_Eleme
}
fe_to_bytes :: proc "contextless" (out1: []byte, arg1: ^Montgomery_Domain_Field_Element) {
- if len(out1) != 32 {
- intrinsics.trap()
- }
+ ensure_contextless(len(out1) == 32, "edwards25519: oversized scalar output buffer")
tmp: Non_Montgomery_Domain_Field_Element
fe_from_montgomery(&tmp, arg1)
diff --git a/core/crypto/_sha3/sha3.odin b/core/crypto/_sha3/sha3.odin
index 2db76fce0..52b3fbda9 100644
--- a/core/crypto/_sha3/sha3.odin
+++ b/core/crypto/_sha3/sha3.odin
@@ -44,7 +44,7 @@ Context :: struct {
is_finalized: bool, // For SHAKE (unlimited squeeze is allowed)
}
-@(private)
+@(private, rodata)
keccakf_rndc := [?]u64 {
0x0000000000000001, 0x0000000000008082, 0x800000000000808a,
0x8000000080008000, 0x000000000000808b, 0x0000000080000001,
@@ -56,13 +56,13 @@ keccakf_rndc := [?]u64 {
0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
}
-@(private)
+@(private, rodata)
keccakf_rotc := [?]int {
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14,
27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
}
-@(private)
+@(private, rodata)
keccakf_piln := [?]i32 {
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4,
15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
@@ -122,7 +122,7 @@ keccakf :: proc "contextless" (st: ^[25]u64) {
}
}
-init :: proc(ctx: ^Context) {
+init :: proc "contextless" (ctx: ^Context) {
for i := 0; i < 25; i += 1 {
ctx.st.q[i] = 0
}
@@ -133,9 +133,9 @@ init :: proc(ctx: ^Context) {
ctx.is_finalized = false
}
-update :: proc(ctx: ^Context, data: []byte) {
- assert(ctx.is_initialized)
- assert(!ctx.is_finalized)
+update :: proc "contextless" (ctx: ^Context, data: []byte) {
+ ensure_contextless(ctx.is_initialized)
+ ensure_contextless(!ctx.is_finalized)
j := ctx.pt
for i := 0; i < len(data); i += 1 {
@@ -149,12 +149,9 @@ update :: proc(ctx: ^Context, data: []byte) {
ctx.pt = j
}
-final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
- assert(ctx.is_initialized)
-
- if len(hash) < ctx.mdlen {
- panic("crypto/sha3: invalid destination digest size")
- }
+final :: proc "contextless" (ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
+ ensure_contextless(ctx.is_initialized)
+ ensure_contextless(len(hash) >= ctx.mdlen, "crypto/sha3: invalid destination digest size")
ctx := ctx
if finalize_clone {
@@ -173,11 +170,11 @@ final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
}
}
-clone :: proc(ctx, other: ^Context) {
+clone :: proc "contextless" (ctx, other: ^Context) {
ctx^ = other^
}
-reset :: proc(ctx: ^Context) {
+reset :: proc "contextless" (ctx: ^Context) {
if !ctx.is_initialized {
return
}
@@ -185,9 +182,9 @@ reset :: proc(ctx: ^Context) {
mem.zero_explicit(ctx, size_of(ctx^))
}
-shake_xof :: proc(ctx: ^Context) {
- assert(ctx.is_initialized)
- assert(!ctx.is_finalized)
+shake_xof :: proc "contextless" (ctx: ^Context) {
+ ensure_contextless(ctx.is_initialized)
+ ensure_contextless(!ctx.is_finalized)
ctx.st.b[ctx.pt] ~= ctx.dsbyte
ctx.st.b[ctx.rsiz - 1] ~= 0x80
@@ -197,9 +194,9 @@ shake_xof :: proc(ctx: ^Context) {
ctx.is_finalized = true // No more absorb, unlimited squeeze.
}
-shake_out :: proc(ctx: ^Context, hash: []byte) {
- assert(ctx.is_initialized)
- assert(ctx.is_finalized)
+shake_out :: proc "contextless" (ctx: ^Context, hash: []byte) {
+ ensure_contextless(ctx.is_initialized)
+ ensure_contextless(ctx.is_finalized)
j := ctx.pt
for i := 0; i < len(hash); i += 1 {
diff --git a/core/crypto/_sha3/sp800_185.odin b/core/crypto/_sha3/sp800_185.odin
index a96f78cc1..8390d8490 100644
--- a/core/crypto/_sha3/sp800_185.odin
+++ b/core/crypto/_sha3/sp800_185.odin
@@ -3,7 +3,7 @@ package _sha3
import "core:encoding/endian"
import "core:math/bits"
-init_cshake :: proc(ctx: ^Context, n, s: []byte, sec_strength: int) {
+init_cshake :: proc "contextless" (ctx: ^Context, n, s: []byte, sec_strength: int) {
ctx.mdlen = sec_strength / 8
// No domain separator is equivalent to vanilla SHAKE.
@@ -18,7 +18,7 @@ init_cshake :: proc(ctx: ^Context, n, s: []byte, sec_strength: int) {
bytepad(ctx, [][]byte{n, s}, rate_cshake(sec_strength))
}
-final_cshake :: proc(ctx: ^Context, dst: []byte, finalize_clone: bool = false) {
+final_cshake :: proc "contextless" (ctx: ^Context, dst: []byte, finalize_clone: bool = false) {
ctx := ctx
if finalize_clone {
tmp_ctx: Context
@@ -32,7 +32,7 @@ final_cshake :: proc(ctx: ^Context, dst: []byte, finalize_clone: bool = false) {
shake_out(ctx, dst)
}
-rate_cshake :: #force_inline proc(sec_strength: int) -> int {
+rate_cshake :: #force_inline proc "contextless" (sec_strength: int) -> int {
switch sec_strength {
case 128:
return RATE_128
@@ -40,7 +40,7 @@ rate_cshake :: #force_inline proc(sec_strength: int) -> int {
return RATE_256
}
- panic("crypto/sha3: invalid security strength")
+ panic_contextless("crypto/sha3: invalid security strength")
}
// right_encode and left_encode are defined to support 0 <= x < 2^2040
@@ -52,10 +52,10 @@ rate_cshake :: #force_inline proc(sec_strength: int) -> int {
//
// Thus we support 0 <= x < 2^128.
-@(private)
+@(private, rodata)
_PAD: [RATE_128]byte // Biggest possible value of w per spec.
-bytepad :: proc(ctx: ^Context, x_strings: [][]byte, w: int) {
+bytepad :: proc "contextless" (ctx: ^Context, x_strings: [][]byte, w: int) {
// 1. z = left_encode(w) || X.
z_hi: u64
z_lo := left_right_encode(ctx, 0, u64(w), true)
@@ -70,9 +70,7 @@ bytepad :: proc(ctx: ^Context, x_strings: [][]byte, w: int) {
// This isn't actually possible, at least with the currently
// defined SP 800-185 routines.
- if carry != 0 {
- panic("crypto/sha3: bytepad input length overflow")
- }
+ ensure_contextless(carry == 0, "crypto/sha3: bytepad input length overflow")
}
// We skip this step as we are doing a byte-oriented implementation
@@ -95,7 +93,7 @@ bytepad :: proc(ctx: ^Context, x_strings: [][]byte, w: int) {
}
}
-encode_string :: #force_inline proc(ctx: ^Context, s: []byte) -> (u64, u64) {
+encode_string :: #force_inline proc "contextless" (ctx: ^Context, s: []byte) -> (u64, u64) {
l := encode_byte_len(ctx, len(s), true) // left_encode
update(ctx, s)
@@ -104,13 +102,13 @@ encode_string :: #force_inline proc(ctx: ^Context, s: []byte) -> (u64, u64) {
return hi, lo
}
-encode_byte_len :: #force_inline proc(ctx: ^Context, l: int, is_left: bool) -> u64 {
+encode_byte_len :: #force_inline proc "contextless" (ctx: ^Context, l: int, is_left: bool) -> u64 {
hi, lo := bits.mul_u64(u64(l), 8)
return left_right_encode(ctx, hi, lo, is_left)
}
@(private)
-left_right_encode :: proc(ctx: ^Context, hi, lo: u64, is_left: bool) -> u64 {
+left_right_encode :: proc "contextless" (ctx: ^Context, hi, lo: u64, is_left: bool) -> u64 {
HI_OFFSET :: 1
LO_OFFSET :: HI_OFFSET + 8
RIGHT_OFFSET :: LO_OFFSET + 8
diff --git a/core/crypto/aead/aead.odin b/core/crypto/aead/aead.odin
index 9b7d810e4..c8f324929 100644
--- a/core/crypto/aead/aead.odin
+++ b/core/crypto/aead/aead.odin
@@ -16,7 +16,7 @@ seal_oneshot :: proc(algo: Algorithm, dst, tag, key, iv, aad, plaintext: []byte,
// returning true iff the authentication was successful. If authentication
// fails, the destination buffer will be zeroed.
//
-// dst and plaintext MUST alias exactly or not at all.
+// dst and ciphertext MUST alias exactly or not at all.
@(require_results)
open_oneshot :: proc(algo: Algorithm, dst, key, iv, aad, ciphertext, tag: []byte, impl: Implementation = nil) -> bool {
ctx: Context
diff --git a/core/crypto/aead/low_level.odin b/core/crypto/aead/low_level.odin
index 38a0c84ba..c80574a0d 100644
--- a/core/crypto/aead/low_level.odin
+++ b/core/crypto/aead/low_level.odin
@@ -1,8 +1,10 @@
package aead
+import "core:crypto/aegis"
import "core:crypto/aes"
import "core:crypto/chacha20"
import "core:crypto/chacha20poly1305"
+import "core:crypto/deoxysii"
import "core:reflect"
// Implementation is an AEAD implementation. Most callers will not need
@@ -15,7 +17,7 @@ Implementation :: union {
// MAX_TAG_SIZE is the maximum size tag that can be returned by any of the
// Algorithms supported via this package.
-MAX_TAG_SIZE :: 16
+MAX_TAG_SIZE :: 32
// Algorithm is the algorithm identifier associated with a given Context.
Algorithm :: enum {
@@ -25,9 +27,14 @@ Algorithm :: enum {
AES_GCM_256,
CHACHA20POLY1305,
XCHACHA20POLY1305,
+ AEGIS_128L,
+ AEGIS_128L_256, // AEGIS-128L (256-bit tag)
+ AEGIS_256,
+ AEGIS_256_256, // AEGIS-256 (256-bit tag)
+ DEOXYS_II_256,
}
-// ALGORITM_NAMES is the Agorithm to algorithm name string.
+// ALGORITM_NAMES is the Algorithm to algorithm name string.
ALGORITHM_NAMES := [Algorithm]string {
.Invalid = "Invalid",
.AES_GCM_128 = "AES-GCM-128",
@@ -35,6 +42,11 @@ ALGORITHM_NAMES := [Algorithm]string {
.AES_GCM_256 = "AES-GCM-256",
.CHACHA20POLY1305 = "chacha20poly1305",
.XCHACHA20POLY1305 = "xchacha20poly1305",
+ .AEGIS_128L = "AEGIS-128L",
+ .AEGIS_128L_256 = "AEGIS-128L-256",
+ .AEGIS_256 = "AEGIS-256",
+ .AEGIS_256_256 = "AEGIS-256-256",
+ .DEOXYS_II_256 = "Deoxys-II-256",
}
// TAG_SIZES is the Algorithm to tag size in bytes.
@@ -45,6 +57,11 @@ TAG_SIZES := [Algorithm]int {
.AES_GCM_256 = aes.GCM_TAG_SIZE,
.CHACHA20POLY1305 = chacha20poly1305.TAG_SIZE,
.XCHACHA20POLY1305 = chacha20poly1305.TAG_SIZE,
+ .AEGIS_128L = aegis.TAG_SIZE_128,
+ .AEGIS_128L_256 = aegis.TAG_SIZE_256,
+ .AEGIS_256 = aegis.TAG_SIZE_128,
+ .AEGIS_256_256 = aegis.TAG_SIZE_256,
+ .DEOXYS_II_256 = deoxysii.TAG_SIZE,
}
// KEY_SIZES is the Algorithm to key size in bytes.
@@ -55,6 +72,11 @@ KEY_SIZES := [Algorithm]int {
.AES_GCM_256 = aes.KEY_SIZE_256,
.CHACHA20POLY1305 = chacha20poly1305.KEY_SIZE,
.XCHACHA20POLY1305 = chacha20poly1305.KEY_SIZE,
+ .AEGIS_128L = aegis.KEY_SIZE_128L,
+ .AEGIS_128L_256 = aegis.KEY_SIZE_128L,
+ .AEGIS_256 = aegis.KEY_SIZE_256,
+ .AEGIS_256_256 = aegis.KEY_SIZE_256,
+ .DEOXYS_II_256 = deoxysii.KEY_SIZE,
}
// IV_SIZES is the Algorithm to initialization vector size in bytes.
@@ -67,6 +89,11 @@ IV_SIZES := [Algorithm]int {
.AES_GCM_256 = aes.GCM_IV_SIZE,
.CHACHA20POLY1305 = chacha20poly1305.IV_SIZE,
.XCHACHA20POLY1305 = chacha20poly1305.XIV_SIZE,
+ .AEGIS_128L = aegis.IV_SIZE_128L,
+ .AEGIS_128L_256 = aegis.IV_SIZE_128L,
+ .AEGIS_256 = aegis.IV_SIZE_256,
+ .AEGIS_256_256 = aegis.IV_SIZE_256,
+ .DEOXYS_II_256 = deoxysii.IV_SIZE,
}
// Context is a concrete instantiation of a specific AEAD algorithm.
@@ -75,6 +102,8 @@ Context :: struct {
_impl: union {
aes.Context_GCM,
chacha20poly1305.Context,
+ aegis.Context,
+ deoxysii.Context,
},
}
@@ -86,6 +115,11 @@ _IMPL_IDS := [Algorithm]typeid {
.AES_GCM_256 = typeid_of(aes.Context_GCM),
.CHACHA20POLY1305 = typeid_of(chacha20poly1305.Context),
.XCHACHA20POLY1305 = typeid_of(chacha20poly1305.Context),
+ .AEGIS_128L = typeid_of(aegis.Context),
+ .AEGIS_128L_256 = typeid_of(aegis.Context),
+ .AEGIS_256 = typeid_of(aegis.Context),
+ .AEGIS_256_256 = typeid_of(aegis.Context),
+ .DEOXYS_II_256 = typeid_of(deoxysii.Context),
}
// init initializes a Context with a specific AEAD Algorithm.
@@ -94,9 +128,7 @@ init :: proc(ctx: ^Context, algorithm: Algorithm, key: []byte, impl: Implementat
reset(ctx)
}
- if len(key) != KEY_SIZES[algorithm] {
- panic("crypto/aead: invalid key size")
- }
+ ensure(len(key) == KEY_SIZES[algorithm], "crypto/aead: invalid key size")
// Directly specialize the union by setting the type ID (save a copy).
reflect.set_union_variant_typeid(
@@ -113,6 +145,12 @@ init :: proc(ctx: ^Context, algorithm: Algorithm, key: []byte, impl: Implementat
case .XCHACHA20POLY1305:
impl_ := impl != nil ? impl.(chacha20.Implementation) : chacha20.DEFAULT_IMPLEMENTATION
chacha20poly1305.init_xchacha(&ctx._impl.(chacha20poly1305.Context), key, impl_)
+ case .AEGIS_128L, .AEGIS_128L_256, .AEGIS_256, .AEGIS_256_256:
+ impl_ := impl != nil ? impl.(aes.Implementation) : aes.DEFAULT_IMPLEMENTATION
+ aegis.init(&ctx._impl.(aegis.Context), key, impl_)
+ case .DEOXYS_II_256:
+ impl_ := impl != nil ? impl.(aes.Implementation) : aes.DEFAULT_IMPLEMENTATION
+ deoxysii.init(&ctx._impl.(deoxysii.Context), key, impl_)
case .Invalid:
panic("crypto/aead: uninitialized algorithm")
case:
@@ -127,11 +165,17 @@ init :: proc(ctx: ^Context, algorithm: Algorithm, key: []byte, impl: Implementat
//
// dst and plaintext MUST alias exactly or not at all.
seal_ctx :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
+ ensure(len(tag) == TAG_SIZES[ctx._algo], "crypto/aead: invalid tag size")
+
switch &impl in ctx._impl {
case aes.Context_GCM:
aes.seal_gcm(&impl, dst, tag, iv, aad, plaintext)
case chacha20poly1305.Context:
chacha20poly1305.seal(&impl, dst, tag, iv, aad, plaintext)
+ case aegis.Context:
+ aegis.seal(&impl, dst, tag, iv, aad, plaintext)
+ case deoxysii.Context:
+ deoxysii.seal(&impl, dst, tag, iv, aad, plaintext)
case:
panic("crypto/aead: uninitialized algorithm")
}
@@ -145,11 +189,17 @@ seal_ctx :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
// dst and plaintext MUST alias exactly or not at all.
@(require_results)
open_ctx :: proc(ctx: ^Context, dst, iv, aad, ciphertext, tag: []byte) -> bool {
+ ensure(len(tag) == TAG_SIZES[ctx._algo], "crypto/aead: invalid tag size")
+
switch &impl in ctx._impl {
case aes.Context_GCM:
return aes.open_gcm(&impl, dst, iv, aad, ciphertext, tag)
case chacha20poly1305.Context:
return chacha20poly1305.open(&impl, dst, iv, aad, ciphertext, tag)
+ case aegis.Context:
+ return aegis.open(&impl, dst, iv, aad, ciphertext, tag)
+ case deoxysii.Context:
+ return deoxysii.open(&impl, dst, iv, aad, ciphertext, tag)
case:
panic("crypto/aead: uninitialized algorithm")
}
@@ -163,6 +213,10 @@ reset :: proc(ctx: ^Context) {
aes.reset_gcm(&impl)
case chacha20poly1305.Context:
chacha20poly1305.reset(&impl)
+ case aegis.Context:
+ aegis.reset(&impl)
+ case deoxysii.Context:
+ deoxysii.reset(&impl)
case:
// Calling reset repeatedly is fine.
}
diff --git a/core/crypto/aegis/aegis.odin b/core/crypto/aegis/aegis.odin
new file mode 100644
index 000000000..adecce91f
--- /dev/null
+++ b/core/crypto/aegis/aegis.odin
@@ -0,0 +1,213 @@
+/*
+package aegis implements the AEGIS-128L and AEGIS-256 Authenticated
+Encryption with Additional Data algorithms.
+
+See:
+- [[ https://www.ietf.org/archive/id/draft-irtf-cfrg-aegis-aead-12.txt ]]
+*/
+package aegis
+
+import "core:bytes"
+import "core:crypto"
+import "core:crypto/aes"
+import "core:mem"
+
+// KEY_SIZE_128L is the AEGIS-128L key size in bytes.
+KEY_SIZE_128L :: 16
+// KEY_SIZE_256 is the AEGIS-256 key size in bytes.
+KEY_SIZE_256 :: 32
+// IV_SIZE_128L is the AEGIS-128L IV size in bytes.
+IV_SIZE_128L :: 16
+// IV_SIZE_256 is the AEGIS-256 IV size in bytes.
+IV_SIZE_256 :: 32
+// TAG_SIZE_128 is the AEGIS-128L or AEGIS-256 128-bit tag size in bytes.
+TAG_SIZE_128 :: 16
+// TAG_SIZE_256 is the AEGIS-128L or AEGIS-256 256-bit tag size in bytes.
+TAG_SIZE_256 :: 32
+
+@(private)
+_RATE_128L :: 32
+@(private)
+_RATE_256 :: 16
+@(private)
+_RATE_MAX :: _RATE_128L
+
+@(private, rodata)
+_C0 := [16]byte{
+ 0x00, 0x01, 0x01, 0x02, 0x03, 0x05, 0x08, 0x0d,
+ 0x15, 0x22, 0x37, 0x59, 0x90, 0xe9, 0x79, 0x62,
+}
+
+@(private, rodata)
+_C1 := [16]byte {
+ 0xdb, 0x3d, 0x18, 0x55, 0x6d, 0xc2, 0x2f, 0xf1,
+ 0x20, 0x11, 0x31, 0x42, 0x73, 0xb5, 0x28, 0xdd,
+}
+
+// Context is a keyed AEGIS-128L or AEGIS-256 instance.
+Context :: struct {
+ _key: [KEY_SIZE_256]byte,
+ _key_len: int,
+ _impl: aes.Implementation,
+ _is_initialized: bool,
+}
+
+@(private)
+_validate_common_slice_sizes :: proc (ctx: ^Context, tag, iv, aad, text: []byte) {
+ switch len(tag) {
+ case TAG_SIZE_128, TAG_SIZE_256:
+ case:
+ panic("crypto/aegis: invalid tag size")
+ }
+
+ iv_ok: bool
+ switch ctx._key_len {
+ case KEY_SIZE_128L:
+ iv_ok = len(iv) == IV_SIZE_128L
+ case KEY_SIZE_256:
+ iv_ok = len(iv) == IV_SIZE_256
+ }
+ ensure(iv_ok,"crypto/aegis: invalid IV size")
+
+ #assert(size_of(int) == 8 || size_of(int) <= 4)
+ // As A_MAX and P_MAX are both defined to be 2^61 - 1 bytes, and
+ // the maximum length of a slice is bound by `size_of(int)`, where
+ // `int` is register sized, there is no need to check AAD/text
+ // lengths.
+}
+
+// init initializes a Context with the provided key, for AEGIS-128L or AEGIS-256.
+init :: proc(ctx: ^Context, key: []byte, impl := aes.DEFAULT_IMPLEMENTATION) {
+ switch len(key) {
+ case KEY_SIZE_128L, KEY_SIZE_256:
+ case:
+ panic("crypto/aegis: invalid key size")
+ }
+
+ copy(ctx._key[:], key)
+ ctx._key_len = len(key)
+ ctx._impl = impl
+ if ctx._impl == .Hardware && !is_hardware_accelerated() {
+ ctx._impl = .Portable
+ }
+ ctx._is_initialized = true
+}
+
+// seal encrypts the plaintext and authenticates the aad and ciphertext,
+// with the provided Context and iv, stores the output in dst and tag.
+//
+// dst and plaintext MUST alias exactly or not at all.
+seal :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
+ ensure(ctx._is_initialized)
+
+ _validate_common_slice_sizes(ctx, tag, iv, aad, plaintext)
+ ensure(len(dst) == len(plaintext), "crypto/aegis: invalid destination ciphertext size")
+ ensure(!bytes.alias_inexactly(dst, plaintext), "crypto/aegis: dst and plaintext alias inexactly")
+
+ switch ctx._impl {
+ case .Hardware:
+ st: State_HW
+ defer reset_state_hw(&st)
+
+ init_hw(ctx, &st, iv)
+
+ aad_len, pt_len := len(aad), len(plaintext)
+ if aad_len > 0 {
+ absorb_hw(&st, aad)
+ }
+
+ if pt_len > 0 {
+ enc_hw(&st, dst, plaintext)
+ }
+
+ finalize_hw(&st, tag, aad_len, pt_len)
+ case .Portable:
+ st: State_SW
+ defer reset_state_sw(&st)
+
+ init_sw(ctx, &st, iv)
+
+ aad_len, pt_len := len(aad), len(plaintext)
+ if aad_len > 0 {
+ absorb_sw(&st, aad)
+ }
+
+ if pt_len > 0 {
+ enc_sw(&st, dst, plaintext)
+ }
+
+ finalize_sw(&st, tag, aad_len, pt_len)
+ case:
+ panic("core/crypto/aegis: not implemented")
+ }
+}
+
+// open authenticates the aad and ciphertext, and decrypts the ciphertext,
+// with the provided Context, iv, and tag, and stores the output in dst,
+// returning true iff the authentication was successful. If authentication
+// fails, the destination buffer will be zeroed.
+//
+// dst and plaintext MUST alias exactly or not at all.
+@(require_results)
+open :: proc(ctx: ^Context, dst, iv, aad, ciphertext, tag: []byte) -> bool {
+ ensure(ctx._is_initialized)
+
+ _validate_common_slice_sizes(ctx, tag, iv, aad, ciphertext)
+ ensure(len(dst) == len(ciphertext), "crypto/aegis: invalid destination plaintext size")
+ ensure(!bytes.alias_inexactly(dst, ciphertext), "crypto/aegis: dst and ciphertext alias inexactly")
+
+ tmp: [TAG_SIZE_256]byte
+ derived_tag := tmp[:len(tag)]
+ aad_len, ct_len := len(aad), len(ciphertext)
+
+ switch ctx._impl {
+ case .Hardware:
+ st: State_HW
+ defer reset_state_hw(&st)
+
+ init_hw(ctx, &st, iv)
+
+ if aad_len > 0 {
+ absorb_hw(&st, aad)
+ }
+
+ if ct_len > 0 {
+ dec_hw(&st, dst, ciphertext)
+ }
+
+ finalize_hw(&st, derived_tag, aad_len, ct_len)
+ case .Portable:
+ st: State_SW
+ defer reset_state_sw(&st)
+
+ init_sw(ctx, &st, iv)
+
+ if aad_len > 0 {
+ absorb_sw(&st, aad)
+ }
+
+ if ct_len > 0 {
+ dec_sw(&st, dst, ciphertext)
+ }
+
+ finalize_sw(&st, derived_tag, aad_len, ct_len)
+ case:
+ panic("core/crypto/aegis: not implemented")
+ }
+
+ if crypto.compare_constant_time(tag, derived_tag) != 1 {
+ mem.zero_explicit(raw_data(derived_tag), len(derived_tag))
+ mem.zero_explicit(raw_data(dst), ct_len)
+ return false
+ }
+
+ return true
+}
+
+// reset sanitizes the Context. The Context must be
+// re-initialized to be used again.
+reset :: proc "contextless" (ctx: ^Context) {
+ mem.zero_explicit(&ctx._key, len(ctx._key))
+ ctx._key_len = 0
+ ctx._is_initialized = false
+}
diff --git a/core/crypto/aegis/aegis_impl_ct64.odin b/core/crypto/aegis/aegis_impl_ct64.odin
new file mode 100644
index 000000000..4813b37ec
--- /dev/null
+++ b/core/crypto/aegis/aegis_impl_ct64.odin
@@ -0,0 +1,452 @@
+package aegis
+
+import aes "core:crypto/_aes/ct64"
+import "core:encoding/endian"
+import "core:mem"
+
+// This uses the bitlsiced 64-bit general purpose register SWAR AES
+// round function. The intermediate state is stored in interleaved
+// but NOT orthogonalized form, as leaving things in the orthgonalized
+// format would overly complicate the update implementation.
+//
+// Note/perf: Per Frank Denis and a review of the specification, it is
+// possible to gain slightly more performance by leaving the state in
+// orthogonalized form while doing initialization, finalization, and
+// absorbing AAD. This implementation opts out of those optimizations
+// for the sake of simplicity.
+//
+// The update function leverages the paralleism (4xblocks) at once.
+
+@(private)
+State_SW :: struct {
+ s0_0, s0_1: u64,
+ s1_0, s1_1: u64,
+ s2_0, s2_1: u64,
+ s3_0, s3_1: u64,
+ s4_0, s4_1: u64,
+ s5_0, s5_1: u64,
+ s6_0, s6_1: u64,
+ s7_0, s7_1: u64,
+ q_k, q_b: [8]u64,
+ rate: int,
+}
+
+@(private)
+init_sw :: proc "contextless" (ctx: ^Context, st: ^State_SW, iv: []byte) {
+ switch ctx._key_len {
+ case KEY_SIZE_128L:
+ key_0, key_1 := aes.load_interleaved(ctx._key[:16])
+ iv_0, iv_1 := aes.load_interleaved(iv)
+
+ st.s0_0, st.s0_1 = aes.xor_interleaved(key_0, key_1, iv_0, iv_1)
+ st.s1_0, st.s1_1 = aes.load_interleaved(_C1[:])
+ st.s2_0, st.s2_1 = aes.load_interleaved(_C0[:])
+ st.s3_0, st.s3_1 = st.s1_0, st.s1_1
+ st.s4_0, st.s4_1 = st.s0_0, st.s0_1
+ st.s5_0, st.s5_1 = aes.xor_interleaved(key_0, key_1, st.s2_0, st.s2_1)
+ st.s6_0, st.s6_1 = aes.xor_interleaved(key_0, key_1, st.s1_0, st.s1_1)
+ st.s7_0, st.s7_1 = st.s5_0, st.s5_1
+ st.rate = _RATE_128L
+
+ for _ in 0 ..< 10 {
+ update_sw_128l(st, iv_0, iv_1, key_0, key_1)
+ }
+ case KEY_SIZE_256:
+ k0_0, k0_1 := aes.load_interleaved(ctx._key[:16])
+ k1_0, k1_1 := aes.load_interleaved(ctx._key[16:])
+ n0_0, n0_1 := aes.load_interleaved(iv[:16])
+ n1_0, n1_1 := aes.load_interleaved(iv[16:])
+
+ st.s0_0, st.s0_1 = aes.xor_interleaved(k0_0, k0_1, n0_0, n0_1)
+ st.s1_0, st.s1_1 = aes.xor_interleaved(k1_0, k1_1, n1_0, n1_1)
+ st.s2_0, st.s2_1 = aes.load_interleaved(_C1[:])
+ st.s3_0, st.s3_1 = aes.load_interleaved(_C0[:])
+ st.s4_0, st.s4_1 = aes.xor_interleaved(k0_0, k0_1, st.s3_0, st.s3_1)
+ st.s5_0, st.s5_1 = aes.xor_interleaved(k1_0, k1_1, st.s2_0, st.s2_1)
+ st.rate = _RATE_256
+
+ u0_0, u0_1, u1_0, u1_1 := st.s0_0, st.s0_1, st.s1_0, st.s1_1
+ for _ in 0 ..< 4 {
+ update_sw_256(st, k0_0, k0_1)
+ update_sw_256(st, k1_0, k1_1)
+ update_sw_256(st, u0_0, u0_1)
+ update_sw_256(st, u1_0, u1_1)
+ }
+ }
+}
+
+@(private = "file")
+update_sw_128l :: proc "contextless" (st: ^State_SW, m0_0, m0_1, m1_0, m1_1: u64) {
+ st.q_k[0], st.q_k[4] = aes.xor_interleaved(st.s0_0, st.s0_1, m0_0, m0_1)
+ st.q_k[1], st.q_k[5] = st.s1_0, st.s1_1
+ st.q_k[2], st.q_k[6] = st.s2_0, st.s2_1
+ st.q_k[3], st.q_k[7] = st.s3_0, st.s3_1
+ aes.orthogonalize(&st.q_k)
+
+ st.q_b[0], st.q_b[4] = st.s7_0, st.s7_1
+ st.q_b[1], st.q_b[5] = st.s0_0, st.s0_1
+ st.q_b[2], st.q_b[6] = st.s1_0, st.s1_1
+ st.q_b[3], st.q_b[7] = st.s2_0, st.s2_1
+ aes.orthogonalize(&st.q_b)
+
+ aes.sub_bytes(&st.q_b)
+ aes.shift_rows(&st.q_b)
+ aes.mix_columns(&st.q_b)
+ aes.add_round_key(&st.q_b, st.q_k[:])
+ aes.orthogonalize(&st.q_b)
+
+ st.s0_0, st.s0_1 = st.q_b[0], st.q_b[4]
+ st.s1_0, st.s1_1 = st.q_b[1], st.q_b[5]
+ st.s2_0, st.s2_1 = st.q_b[2], st.q_b[6]
+ s3_0, s3_1 := st.q_b[3], st.q_b[7]
+
+ st.q_k[0], st.q_k[4] = aes.xor_interleaved(st.s4_0, st.s4_1, m1_0, m1_1)
+ st.q_k[1], st.q_k[5] = st.s5_0, st.s5_1
+ st.q_k[2], st.q_k[6] = st.s6_0, st.s6_1
+ st.q_k[3], st.q_k[7] = st.s7_0, st.s7_1
+ aes.orthogonalize(&st.q_k)
+
+ st.q_b[0], st.q_b[4] = st.s3_0, st.s3_1
+ st.q_b[1], st.q_b[5] = st.s4_0, st.s4_1
+ st.q_b[2], st.q_b[6] = st.s5_0, st.s5_1
+ st.q_b[3], st.q_b[7] = st.s6_0, st.s6_1
+ aes.orthogonalize(&st.q_b)
+
+ aes.sub_bytes(&st.q_b)
+ aes.shift_rows(&st.q_b)
+ aes.mix_columns(&st.q_b)
+ aes.add_round_key(&st.q_b, st.q_k[:])
+ aes.orthogonalize(&st.q_b)
+
+ st.s3_0, st.s3_1 = s3_0, s3_1
+ st.s4_0, st.s4_1 = st.q_b[0], st.q_b[4]
+ st.s5_0, st.s5_1 = st.q_b[1], st.q_b[5]
+ st.s6_0, st.s6_1 = st.q_b[2], st.q_b[6]
+ st.s7_0, st.s7_1 = st.q_b[3], st.q_b[7]
+}
+
+@(private = "file")
+update_sw_256 :: proc "contextless" (st: ^State_SW, m_0, m_1: u64) {
+ st.q_k[0], st.q_k[4] = aes.xor_interleaved(st.s0_0, st.s0_1, m_0, m_1)
+ st.q_k[1], st.q_k[5] = st.s1_0, st.s1_1
+ st.q_k[2], st.q_k[6] = st.s2_0, st.s2_1
+ st.q_k[3], st.q_k[7] = st.s3_0, st.s3_1
+ aes.orthogonalize(&st.q_k)
+
+ st.q_b[0], st.q_b[4] = st.s5_0, st.s5_1
+ st.q_b[1], st.q_b[5] = st.s0_0, st.s0_1
+ st.q_b[2], st.q_b[6] = st.s1_0, st.s1_1
+ st.q_b[3], st.q_b[7] = st.s2_0, st.s2_1
+ aes.orthogonalize(&st.q_b)
+
+ aes.sub_bytes(&st.q_b)
+ aes.shift_rows(&st.q_b)
+ aes.mix_columns(&st.q_b)
+ aes.add_round_key(&st.q_b, st.q_k[:])
+ aes.orthogonalize(&st.q_b)
+
+ st.s0_0, st.s0_1 = st.q_b[0], st.q_b[4]
+ st.s1_0, st.s1_1 = st.q_b[1], st.q_b[5]
+ st.s2_0, st.s2_1 = st.q_b[2], st.q_b[6]
+ s3_0, s3_1 := st.q_b[3], st.q_b[7]
+
+ st.q_k[0], st.q_k[4] = st.s4_0, st.s4_1
+ st.q_k[1], st.q_k[5] = st.s5_0, st.s5_1
+ aes.orthogonalize(&st.q_k)
+
+ st.q_b[0], st.q_b[4] = st.s3_0, st.s3_1
+ st.q_b[1], st.q_b[5] = st.s4_0, st.s4_1
+ aes.orthogonalize(&st.q_b)
+
+ aes.sub_bytes(&st.q_b)
+ aes.shift_rows(&st.q_b)
+ aes.mix_columns(&st.q_b)
+ aes.add_round_key(&st.q_b, st.q_k[:])
+ aes.orthogonalize(&st.q_b)
+
+ st.s3_0, st.s3_1 = s3_0, s3_1
+ st.s4_0, st.s4_1 = st.q_b[0], st.q_b[4]
+ st.s5_0, st.s5_1 = st.q_b[1], st.q_b[5]
+}
+
+@(private = "file")
+absorb_sw_128l :: #force_inline proc "contextless" (st: ^State_SW, ai: []byte) #no_bounds_check {
+ t0_0, t0_1 := aes.load_interleaved(ai[:16])
+ t1_0, t1_1 := aes.load_interleaved(ai[16:])
+ update_sw_128l(st, t0_0, t0_1, t1_0, t1_1)
+}
+
+@(private = "file")
+absorb_sw_256 :: #force_inline proc "contextless" (st: ^State_SW, ai: []byte) {
+ m_0, m_1 := aes.load_interleaved(ai)
+ update_sw_256(st, m_0, m_1)
+}
+
+@(private)
+absorb_sw :: proc "contextless" (st: ^State_SW, aad: []byte) #no_bounds_check {
+ ai, l := aad, len(aad)
+
+ switch st.rate {
+ case _RATE_128L:
+ for l >= _RATE_128L {
+ absorb_sw_128l(st, ai)
+ ai = ai[_RATE_128L:]
+ l -= _RATE_128L
+ }
+ case _RATE_256:
+ for l >= _RATE_256 {
+ absorb_sw_256(st, ai)
+
+ ai = ai[_RATE_256:]
+ l -= _RATE_256
+ }
+ }
+
+ // Pad out the remainder with `0`s till it is rate sized.
+ if l > 0 {
+ tmp: [_RATE_MAX]byte // AAD is not confidential.
+ copy(tmp[:], ai)
+ switch st.rate {
+ case _RATE_128L:
+ absorb_sw_128l(st, tmp[:])
+ case _RATE_256:
+ absorb_sw_256(st, tmp[:])
+ }
+ }
+}
+
+@(private = "file", require_results)
+z_sw_128l :: proc "contextless" (st: ^State_SW) -> (u64, u64, u64, u64) {
+ z0_0, z0_1 := aes.and_interleaved(st.s2_0, st.s2_1, st.s3_0, st.s3_1)
+ z0_0, z0_1 = aes.xor_interleaved(st.s1_0, st.s1_1, z0_0, z0_1)
+ z0_0, z0_1 = aes.xor_interleaved(st.s6_0, st.s6_1, z0_0, z0_1)
+
+ z1_0, z1_1 := aes.and_interleaved(st.s6_0, st.s6_1, st.s7_0, st.s7_1)
+ z1_0, z1_1 = aes.xor_interleaved(st.s5_0, st.s5_1, z1_0, z1_1)
+ z1_0, z1_1 = aes.xor_interleaved(st.s2_0, st.s2_1, z1_0, z1_1)
+
+ return z0_0, z0_1, z1_0, z1_1
+}
+
+@(private = "file", require_results)
+z_sw_256 :: proc "contextless" (st: ^State_SW) -> (u64, u64) {
+ z_0, z_1 := aes.and_interleaved(st.s2_0, st.s2_1, st.s3_0, st.s3_1)
+ z_0, z_1 = aes.xor_interleaved(st.s5_0, st.s5_1, z_0, z_1)
+ z_0, z_1 = aes.xor_interleaved(st.s4_0, st.s4_1, z_0, z_1)
+ return aes.xor_interleaved(st.s1_0, st.s1_1, z_0, z_1)
+}
+
+@(private = "file")
+enc_sw_128l :: #force_inline proc "contextless" (st: ^State_SW, ci, xi: []byte) #no_bounds_check {
+ z0_0, z0_1, z1_0, z1_1 := z_sw_128l(st)
+
+ t0_0, t0_1 := aes.load_interleaved(xi[:16])
+ t1_0, t1_1 := aes.load_interleaved(xi[16:])
+ update_sw_128l(st, t0_0, t0_1, t1_0, t1_1)
+
+ out0_0, out0_1 := aes.xor_interleaved(t0_0, t0_1, z0_0, z0_1)
+ out1_0, out1_1 := aes.xor_interleaved(t1_0, t1_1, z1_0, z1_1)
+ aes.store_interleaved(ci[:16], out0_0, out0_1)
+ aes.store_interleaved(ci[16:], out1_0, out1_1)
+}
+
+@(private = "file")
+enc_sw_256 :: #force_inline proc "contextless" (st: ^State_SW, ci, xi: []byte) #no_bounds_check {
+ z_0, z_1 := z_sw_256(st)
+
+ xi_0, xi_1 := aes.load_interleaved(xi)
+ update_sw_256(st, xi_0, xi_1)
+
+ ci_0, ci_1 := aes.xor_interleaved(xi_0, xi_1, z_0, z_1)
+ aes.store_interleaved(ci, ci_0, ci_1)
+}
+
+@(private)
+enc_sw :: proc "contextless" (st: ^State_SW, dst, src: []byte) #no_bounds_check {
+ ci, xi, l := dst, src, len(src)
+
+ switch st.rate {
+ case _RATE_128L:
+ for l >= _RATE_128L {
+ enc_sw_128l(st, ci, xi)
+ ci = ci[_RATE_128L:]
+ xi = xi[_RATE_128L:]
+ l -= _RATE_128L
+ }
+ case _RATE_256:
+ for l >= _RATE_256 {
+ enc_sw_256(st, ci, xi)
+ ci = ci[_RATE_256:]
+ xi = xi[_RATE_256:]
+ l -= _RATE_256
+ }
+ }
+
+ // Pad out the remainder with `0`s till it is rate sized.
+ if l > 0 {
+ tmp: [_RATE_MAX]byte // Ciphertext is not confidential.
+ copy(tmp[:], xi)
+ switch st.rate {
+ case _RATE_128L:
+ enc_sw_128l(st, tmp[:], tmp[:])
+ case _RATE_256:
+ enc_sw_256(st, tmp[:], tmp[:])
+ }
+ copy(ci, tmp[:l])
+ }
+}
+
+@(private = "file")
+dec_sw_128l :: #force_inline proc "contextless" (st: ^State_SW, xi, ci: []byte) #no_bounds_check {
+ z0_0, z0_1, z1_0, z1_1 := z_sw_128l(st)
+
+ t0_0, t0_1 := aes.load_interleaved(ci[:16])
+ t1_0, t1_1 := aes.load_interleaved(ci[16:])
+ out0_0, out0_1 := aes.xor_interleaved(t0_0, t0_1, z0_0, z0_1)
+ out1_0, out1_1 := aes.xor_interleaved(t1_0, t1_1, z1_0, z1_1)
+
+ update_sw_128l(st, out0_0, out0_1, out1_0, out1_1)
+ aes.store_interleaved(xi[:16], out0_0, out0_1)
+ aes.store_interleaved(xi[16:], out1_0, out1_1)
+}
+
+@(private = "file")
+dec_sw_256 :: #force_inline proc "contextless" (st: ^State_SW, xi, ci: []byte) #no_bounds_check {
+ z_0, z_1 := z_sw_256(st)
+
+ ci_0, ci_1 := aes.load_interleaved(ci)
+ xi_0, xi_1 := aes.xor_interleaved(ci_0, ci_1, z_0, z_1)
+
+ update_sw_256(st, xi_0, xi_1)
+ aes.store_interleaved(xi, xi_0, xi_1)
+}
+
+@(private = "file")
+dec_partial_sw_128l :: proc "contextless" (st: ^State_SW, xn, cn: []byte) #no_bounds_check {
+ tmp: [_RATE_128L]byte
+ defer mem.zero_explicit(&tmp, size_of(tmp))
+
+ z0_0, z0_1, z1_0, z1_1 := z_sw_128l(st)
+ copy(tmp[:], cn)
+
+ t0_0, t0_1 := aes.load_interleaved(tmp[:16])
+ t1_0, t1_1 := aes.load_interleaved(tmp[16:])
+ out0_0, out0_1 := aes.xor_interleaved(t0_0, t0_1, z0_0, z0_1)
+ out1_0, out1_1 := aes.xor_interleaved(t1_0, t1_1, z1_0, z1_1)
+
+ aes.store_interleaved(tmp[:16], out0_0, out0_1)
+ aes.store_interleaved(tmp[16:], out1_0, out1_1)
+ copy(xn, tmp[:])
+
+ for off := len(xn); off < _RATE_128L; off += 1 {
+ tmp[off] = 0
+ }
+ out0_0, out0_1 = aes.load_interleaved(tmp[:16])
+ out1_0, out1_1 = aes.load_interleaved(tmp[16:])
+ update_sw_128l(st, out0_0, out0_1, out1_0, out1_1)
+}
+
+@(private = "file")
+dec_partial_sw_256 :: proc "contextless" (st: ^State_SW, xn, cn: []byte) #no_bounds_check {
+ tmp: [_RATE_256]byte
+ defer mem.zero_explicit(&tmp, size_of(tmp))
+
+ z_0, z_1 := z_sw_256(st)
+ copy(tmp[:], cn)
+
+ cn_0, cn_1 := aes.load_interleaved(tmp[:])
+ xn_0, xn_1 := aes.xor_interleaved(cn_0, cn_1, z_0, z_1)
+
+ aes.store_interleaved(tmp[:], xn_0, xn_1)
+ copy(xn, tmp[:])
+
+ for off := len(xn); off < _RATE_256; off += 1 {
+ tmp[off] = 0
+ }
+ xn_0, xn_1 = aes.load_interleaved(tmp[:])
+ update_sw_256(st, xn_0, xn_1)
+}
+
+@(private)
+dec_sw :: proc "contextless" (st: ^State_SW, dst, src: []byte) #no_bounds_check {
+ xi, ci, l := dst, src, len(src)
+
+ switch st.rate {
+ case _RATE_128L:
+ for l >= _RATE_128L {
+ dec_sw_128l(st, xi, ci)
+ xi = xi[_RATE_128L:]
+ ci = ci[_RATE_128L:]
+ l -= _RATE_128L
+ }
+ case _RATE_256:
+ for l >= _RATE_256 {
+ dec_sw_256(st, xi, ci)
+ xi = xi[_RATE_256:]
+ ci = ci[_RATE_256:]
+ l -= _RATE_256
+ }
+ }
+
+ // Process the remainder.
+ if l > 0 {
+ switch st.rate {
+ case _RATE_128L:
+ dec_partial_sw_128l(st, xi, ci)
+ case _RATE_256:
+ dec_partial_sw_256(st, xi, ci)
+ }
+ }
+}
+
+@(private)
+finalize_sw :: proc "contextless" (st: ^State_SW, tag: []byte, ad_len, msg_len: int) {
+ tmp: [16]byte
+ endian.unchecked_put_u64le(tmp[0:], u64(ad_len) * 8)
+ endian.unchecked_put_u64le(tmp[8:], u64(msg_len) * 8)
+
+ t_0, t_1 := aes.load_interleaved(tmp[:])
+
+ t0_0, t0_1, t1_0, t1_1: u64 = ---, ---, ---, ---
+ switch st.rate {
+ case _RATE_128L:
+ t_0, t_1 = aes.xor_interleaved(st.s2_0, st.s2_1, t_0, t_1)
+ for _ in 0 ..< 7 {
+ update_sw_128l(st, t_0, t_1, t_0, t_1)
+ }
+
+ t0_0, t0_1 = aes.xor_interleaved(st.s0_0, st.s0_1, st.s1_0, st.s1_1)
+ t0_0, t0_1 = aes.xor_interleaved(t0_0, t0_1, st.s2_0, st.s2_1)
+ t0_0, t0_1 = aes.xor_interleaved(t0_0, t0_1, st.s3_0, st.s3_1)
+
+ t1_0, t1_1 = aes.xor_interleaved(st.s4_0, st.s4_1, st.s5_0, st.s5_1)
+ t1_0, t1_1 = aes.xor_interleaved(t1_0, t1_1, st.s6_0, st.s6_1)
+ if len(tag) == TAG_SIZE_256 {
+ t1_0, t1_1 = aes.xor_interleaved(t1_0, t1_1, st.s7_0, st.s7_1)
+ }
+ case _RATE_256:
+ t_0, t_1 = aes.xor_interleaved(st.s3_0, st.s3_1, t_0, t_1)
+ for _ in 0 ..< 7 {
+ update_sw_256(st, t_0, t_1)
+ }
+
+ t0_0, t0_1 = aes.xor_interleaved(st.s0_0, st.s0_1, st.s1_0, st.s1_1)
+ t0_0, t0_1 = aes.xor_interleaved(t0_0, t0_1, st.s2_0, st.s2_1)
+
+ t1_0, t1_1 = aes.xor_interleaved(st.s3_0, st.s3_1, st.s4_0, st.s4_1)
+ t1_0, t1_1 = aes.xor_interleaved(t1_0, t1_1, st.s5_0, st.s5_1)
+ }
+ switch len(tag) {
+ case TAG_SIZE_128:
+ t0_0, t0_1 = aes.xor_interleaved(t0_0, t0_1, t1_0, t1_1)
+ aes.store_interleaved(tag, t0_0, t0_1)
+ case TAG_SIZE_256:
+ aes.store_interleaved(tag[:16], t0_0, t0_1)
+ aes.store_interleaved(tag[16:], t1_0, t1_1)
+ }
+}
+
+@(private)
+reset_state_sw :: proc "contextless" (st: ^State_SW) {
+ mem.zero_explicit(st, size_of(st^))
+}
diff --git a/core/crypto/aegis/aegis_impl_hw_gen.odin b/core/crypto/aegis/aegis_impl_hw_gen.odin
new file mode 100644
index 000000000..5ec2f3d6e
--- /dev/null
+++ b/core/crypto/aegis/aegis_impl_hw_gen.odin
@@ -0,0 +1,44 @@
+#+build !amd64
+package aegis
+
+@(private = "file")
+ERR_HW_NOT_SUPPORTED :: "crypto/aegis: hardware implementation unsupported"
+
+@(private)
+State_HW :: struct {}
+
+// is_hardware_accelerated returns true iff hardware accelerated AEGIS
+// is supported.
+is_hardware_accelerated :: proc "contextless" () -> bool {
+ return false
+}
+
+@(private)
+init_hw :: proc "contextless" (ctx: ^Context, st: ^State_HW, iv: []byte) {
+ panic_contextless(ERR_HW_NOT_SUPPORTED)
+}
+
+@(private)
+absorb_hw :: proc "contextless" (st: ^State_HW, aad: []byte) {
+ panic_contextless(ERR_HW_NOT_SUPPORTED)
+}
+
+@(private)
+enc_hw :: proc "contextless" (st: ^State_HW, dst, src: []byte) {
+ panic_contextless(ERR_HW_NOT_SUPPORTED)
+}
+
+@(private)
+dec_hw :: proc "contextless" (st: ^State_HW, dst, src: []byte) {
+ panic_contextless(ERR_HW_NOT_SUPPORTED)
+}
+
+@(private)
+finalize_hw :: proc "contextless" (st: ^State_HW, tag: []byte, ad_len, msg_len: int) {
+ panic_contextless(ERR_HW_NOT_SUPPORTED)
+}
+
+@(private)
+reset_state_hw :: proc "contextless" (st: ^State_HW) {
+ panic_contextless(ERR_HW_NOT_SUPPORTED)
+}
diff --git a/core/crypto/aegis/aegis_impl_hw_intel.odin b/core/crypto/aegis/aegis_impl_hw_intel.odin
new file mode 100644
index 000000000..5334f3258
--- /dev/null
+++ b/core/crypto/aegis/aegis_impl_hw_intel.odin
@@ -0,0 +1,389 @@
+#+build amd64
+package aegis
+
+import "base:intrinsics"
+import "core:crypto/aes"
+import "core:encoding/endian"
+import "core:mem"
+import "core:simd/x86"
+
+@(private)
+State_HW :: struct {
+ s0: x86.__m128i,
+ s1: x86.__m128i,
+ s2: x86.__m128i,
+ s3: x86.__m128i,
+ s4: x86.__m128i,
+ s5: x86.__m128i,
+ s6: x86.__m128i,
+ s7: x86.__m128i,
+ rate: int,
+}
+
+// is_hardware_accelerated returns true iff hardware accelerated AEGIS
+// is supported.
+is_hardware_accelerated :: proc "contextless" () -> bool {
+ return aes.is_hardware_accelerated()
+}
+
+@(private, enable_target_feature = "sse2,aes")
+init_hw :: proc "contextless" (ctx: ^Context, st: ^State_HW, iv: []byte) {
+ switch ctx._key_len {
+ case KEY_SIZE_128L:
+ key := intrinsics.unaligned_load((^x86.__m128i)(&ctx._key[0]))
+ iv := intrinsics.unaligned_load((^x86.__m128i)(raw_data(iv)))
+
+ st.s0 = x86._mm_xor_si128(key, iv)
+ st.s1 = intrinsics.unaligned_load((^x86.__m128i)(&_C1[0]))
+ st.s2 = intrinsics.unaligned_load((^x86.__m128i)(&_C0[0]))
+ st.s3 = st.s1
+ st.s4 = st.s0
+ st.s5 = x86._mm_xor_si128(key, st.s2) // key ^ C0
+ st.s6 = x86._mm_xor_si128(key, st.s1) // key ^ C1
+ st.s7 = st.s5
+ st.rate = _RATE_128L
+
+ for _ in 0 ..< 10 {
+ update_hw_128l(st, iv, key)
+ }
+ case KEY_SIZE_256:
+ k0 := intrinsics.unaligned_load((^x86.__m128i)(&ctx._key[0]))
+ k1 := intrinsics.unaligned_load((^x86.__m128i)(&ctx._key[16]))
+ n0 := intrinsics.unaligned_load((^x86.__m128i)(&iv[0]))
+ n1 := intrinsics.unaligned_load((^x86.__m128i)(&iv[16]))
+
+ st.s0 = x86._mm_xor_si128(k0, n0)
+ st.s1 = x86._mm_xor_si128(k1, n1)
+ st.s2 = intrinsics.unaligned_load((^x86.__m128i)(&_C1[0]))
+ st.s3 = intrinsics.unaligned_load((^x86.__m128i)(&_C0[0]))
+ st.s4 = x86._mm_xor_si128(k0, st.s3) // k0 ^ C0
+ st.s5 = x86._mm_xor_si128(k1, st.s2) // k1 ^ C1
+ st.rate = _RATE_256
+
+ u0, u1 := st.s0, st.s1
+ for _ in 0 ..< 4 {
+ update_hw_256(st, k0)
+ update_hw_256(st, k1)
+ update_hw_256(st, u0)
+ update_hw_256(st, u1)
+ }
+ }
+}
+
+@(private = "file", enable_target_feature = "sse2,aes")
+update_hw_128l :: #force_inline proc "contextless" (st: ^State_HW, m0, m1: x86.__m128i) {
+ s0_ := x86._mm_aesenc_si128(st.s7, x86._mm_xor_si128(st.s0, m0))
+ s1_ := x86._mm_aesenc_si128(st.s0, st.s1)
+ s2_ := x86._mm_aesenc_si128(st.s1, st.s2)
+ s3_ := x86._mm_aesenc_si128(st.s2, st.s3)
+ s4_ := x86._mm_aesenc_si128(st.s3, x86._mm_xor_si128(st.s4, m1))
+ s5_ := x86._mm_aesenc_si128(st.s4, st.s5)
+ s6_ := x86._mm_aesenc_si128(st.s5, st.s6)
+ s7_ := x86._mm_aesenc_si128(st.s6, st.s7)
+ st.s0, st.s1, st.s2, st.s3, st.s4, st.s5, st.s6, st.s7 = s0_, s1_, s2_, s3_, s4_, s5_, s6_, s7_
+}
+
+@(private = "file", enable_target_feature = "sse2,aes")
+update_hw_256 :: #force_inline proc "contextless" (st: ^State_HW, m: x86.__m128i) {
+ s0_ := x86._mm_aesenc_si128(st.s5, x86._mm_xor_si128(st.s0, m))
+ s1_ := x86._mm_aesenc_si128(st.s0, st.s1)
+ s2_ := x86._mm_aesenc_si128(st.s1, st.s2)
+ s3_ := x86._mm_aesenc_si128(st.s2, st.s3)
+ s4_ := x86._mm_aesenc_si128(st.s3, st.s4)
+ s5_ := x86._mm_aesenc_si128(st.s4, st.s5)
+ st.s0, st.s1, st.s2, st.s3, st.s4, st.s5 = s0_, s1_, s2_, s3_, s4_, s5_
+}
+
+@(private = "file", enable_target_feature = "sse2,aes")
+absorb_hw_128l :: #force_inline proc "contextless" (st: ^State_HW, ai: []byte) {
+ t0 := intrinsics.unaligned_load((^x86.__m128i)(&ai[0]))
+ t1 := intrinsics.unaligned_load((^x86.__m128i)(&ai[16]))
+ update_hw_128l(st, t0, t1)
+}
+
+@(private = "file", enable_target_feature = "sse2,aes")
+absorb_hw_256 :: #force_inline proc "contextless" (st: ^State_HW, ai: []byte) {
+ m := intrinsics.unaligned_load((^x86.__m128i)(&ai[0]))
+ update_hw_256(st, m)
+}
+
+@(private, enable_target_feature = "sse2,aes")
+absorb_hw :: proc "contextless" (st: ^State_HW, aad: []byte) #no_bounds_check {
+ ai, l := aad, len(aad)
+
+ switch st.rate {
+ case _RATE_128L:
+ for l >= _RATE_128L {
+ absorb_hw_128l(st, ai)
+ ai = ai[_RATE_128L:]
+ l -= _RATE_128L
+ }
+ case _RATE_256:
+ for l >= _RATE_256 {
+ absorb_hw_256(st, ai)
+
+ ai = ai[_RATE_256:]
+ l -= _RATE_256
+ }
+ }
+
+ // Pad out the remainder with `0`s till it is rate sized.
+ if l > 0 {
+ tmp: [_RATE_MAX]byte // AAD is not confidential.
+ copy(tmp[:], ai)
+ switch st.rate {
+ case _RATE_128L:
+ absorb_hw_128l(st, tmp[:])
+ case _RATE_256:
+ absorb_hw_256(st, tmp[:])
+ }
+ }
+}
+
+@(private = "file", enable_target_feature = "sse2", require_results)
+z_hw_128l :: #force_inline proc "contextless" (st: ^State_HW) -> (x86.__m128i, x86.__m128i) {
+ z0 := x86._mm_xor_si128(
+ st.s6,
+ x86._mm_xor_si128(
+ st.s1,
+ x86._mm_and_si128(st.s2, st.s3),
+ ),
+ )
+ z1 := x86._mm_xor_si128(
+ st.s2,
+ x86._mm_xor_si128(
+ st.s5,
+ x86._mm_and_si128(st.s6, st.s7),
+ ),
+ )
+ return z0, z1
+}
+
+@(private = "file", enable_target_feature = "sse2", require_results)
+z_hw_256 :: #force_inline proc "contextless" (st: ^State_HW) -> x86.__m128i {
+ return x86._mm_xor_si128(
+ st.s1,
+ x86._mm_xor_si128(
+ st.s4,
+ x86._mm_xor_si128(
+ st.s5,
+ x86._mm_and_si128(st.s2, st.s3),
+ ),
+ ),
+ )
+}
+
+@(private = "file", enable_target_feature = "sse2,aes")
+enc_hw_128l :: #force_inline proc "contextless" (st: ^State_HW, ci, xi: []byte) #no_bounds_check {
+ z0, z1 := z_hw_128l(st)
+
+ t0 := intrinsics.unaligned_load((^x86.__m128i)(&xi[0]))
+ t1 := intrinsics.unaligned_load((^x86.__m128i)(&xi[16]))
+ update_hw_128l(st, t0, t1)
+
+ out0 := x86._mm_xor_si128(t0, z0)
+ out1 := x86._mm_xor_si128(t1, z1)
+ intrinsics.unaligned_store((^x86.__m128i)(&ci[0]), out0)
+ intrinsics.unaligned_store((^x86.__m128i)(&ci[16]), out1)
+}
+
+@(private = "file", enable_target_feature = "sse2,aes")
+enc_hw_256 :: #force_inline proc "contextless" (st: ^State_HW, ci, xi: []byte) #no_bounds_check {
+ z := z_hw_256(st)
+
+ xi_ := intrinsics.unaligned_load((^x86.__m128i)(raw_data(xi)))
+ update_hw_256(st, xi_)
+
+ ci_ := x86._mm_xor_si128(xi_, z)
+ intrinsics.unaligned_store((^x86.__m128i)(raw_data(ci)), ci_)
+}
+
+@(private, enable_target_feature = "sse2,aes")
+enc_hw :: proc "contextless" (st: ^State_HW, dst, src: []byte) #no_bounds_check {
+ ci, xi, l := dst, src, len(src)
+
+ switch st.rate {
+ case _RATE_128L:
+ for l >= _RATE_128L {
+ enc_hw_128l(st, ci, xi)
+ ci = ci[_RATE_128L:]
+ xi = xi[_RATE_128L:]
+ l -= _RATE_128L
+ }
+ case _RATE_256:
+ for l >= _RATE_256 {
+ enc_hw_256(st, ci, xi)
+ ci = ci[_RATE_256:]
+ xi = xi[_RATE_256:]
+ l -= _RATE_256
+ }
+ }
+
+ // Pad out the remainder with `0`s till it is rate sized.
+ if l > 0 {
+ tmp: [_RATE_MAX]byte // Ciphertext is not confidential.
+ copy(tmp[:], xi)
+ switch st.rate {
+ case _RATE_128L:
+ enc_hw_128l(st, tmp[:], tmp[:])
+ case _RATE_256:
+ enc_hw_256(st, tmp[:], tmp[:])
+ }
+ copy(ci, tmp[:l])
+ }
+}
+
+@(private = "file", enable_target_feature = "sse2,aes")
+dec_hw_128l :: #force_inline proc "contextless" (st: ^State_HW, xi, ci: []byte) #no_bounds_check {
+ z0, z1 := z_hw_128l(st)
+
+ t0 := intrinsics.unaligned_load((^x86.__m128i)(&ci[0]))
+ t1 := intrinsics.unaligned_load((^x86.__m128i)(&ci[16]))
+ out0 := x86._mm_xor_si128(t0, z0)
+ out1 := x86._mm_xor_si128(t1, z1)
+
+ update_hw_128l(st, out0, out1)
+ intrinsics.unaligned_store((^x86.__m128i)(&xi[0]), out0)
+ intrinsics.unaligned_store((^x86.__m128i)(&xi[16]), out1)
+}
+
+@(private = "file", enable_target_feature = "sse2,aes")
+dec_hw_256 :: #force_inline proc "contextless" (st: ^State_HW, xi, ci: []byte) #no_bounds_check {
+ z := z_hw_256(st)
+
+ ci_ := intrinsics.unaligned_load((^x86.__m128i)(raw_data(ci)))
+ xi_ := x86._mm_xor_si128(ci_, z)
+
+ update_hw_256(st, xi_)
+ intrinsics.unaligned_store((^x86.__m128i)(raw_data(xi)), xi_)
+}
+
+@(private = "file", enable_target_feature = "sse2,aes")
+dec_partial_hw_128l :: #force_inline proc "contextless" (st: ^State_HW, xn, cn: []byte) #no_bounds_check {
+ tmp: [_RATE_128L]byte
+ defer mem.zero_explicit(&tmp, size_of(tmp))
+
+ z0, z1 := z_hw_128l(st)
+ copy(tmp[:], cn)
+
+ t0 := intrinsics.unaligned_load((^x86.__m128i)(&tmp[0]))
+ t1 := intrinsics.unaligned_load((^x86.__m128i)(&tmp[16]))
+ out0 := x86._mm_xor_si128(t0, z0)
+ out1 := x86._mm_xor_si128(t1, z1)
+
+ intrinsics.unaligned_store((^x86.__m128i)(&tmp[0]), out0)
+ intrinsics.unaligned_store((^x86.__m128i)(&tmp[16]), out1)
+ copy(xn, tmp[:])
+
+ for off := len(xn); off < _RATE_128L; off += 1 {
+ tmp[off] = 0
+ }
+ out0 = intrinsics.unaligned_load((^x86.__m128i)(&tmp[0])) // v0
+ out1 = intrinsics.unaligned_load((^x86.__m128i)(&tmp[16])) // v1
+ update_hw_128l(st, out0, out1)
+}
+
+@(private = "file", enable_target_feature = "sse2,aes")
+dec_partial_hw_256 :: #force_inline proc "contextless" (st: ^State_HW, xn, cn: []byte) #no_bounds_check {
+ tmp: [_RATE_256]byte
+ defer mem.zero_explicit(&tmp, size_of(tmp))
+
+ z := z_hw_256(st)
+ copy(tmp[:], cn)
+
+ cn_ := intrinsics.unaligned_load((^x86.__m128i)(&tmp[0]))
+ xn_ := x86._mm_xor_si128(cn_, z)
+
+ intrinsics.unaligned_store((^x86.__m128i)(&tmp[0]), xn_)
+ copy(xn, tmp[:])
+
+ for off := len(xn); off < _RATE_256; off += 1 {
+ tmp[off] = 0
+ }
+ xn_ = intrinsics.unaligned_load((^x86.__m128i)(&tmp[0]))
+ update_hw_256(st, xn_)
+}
+
+@(private, enable_target_feature = "sse2,aes")
+dec_hw :: proc "contextless" (st: ^State_HW, dst, src: []byte) #no_bounds_check {
+ xi, ci, l := dst, src, len(src)
+
+ switch st.rate {
+ case _RATE_128L:
+ for l >= _RATE_128L {
+ dec_hw_128l(st, xi, ci)
+ xi = xi[_RATE_128L:]
+ ci = ci[_RATE_128L:]
+ l -= _RATE_128L
+ }
+ case _RATE_256:
+ for l >= _RATE_256 {
+ dec_hw_256(st, xi, ci)
+ xi = xi[_RATE_256:]
+ ci = ci[_RATE_256:]
+ l -= _RATE_256
+ }
+ }
+
+ // Process the remainder.
+ if l > 0 {
+ switch st.rate {
+ case _RATE_128L:
+ dec_partial_hw_128l(st, xi, ci)
+ case _RATE_256:
+ dec_partial_hw_256(st, xi, ci)
+ }
+ }
+}
+
+@(private, enable_target_feature = "sse2,aes")
+finalize_hw :: proc "contextless" (st: ^State_HW, tag: []byte, ad_len, msg_len: int) {
+ tmp: [16]byte
+ endian.unchecked_put_u64le(tmp[0:], u64(ad_len) * 8)
+ endian.unchecked_put_u64le(tmp[8:], u64(msg_len) * 8)
+
+ t := intrinsics.unaligned_load((^x86.__m128i)(&tmp[0]))
+
+ t0, t1: x86.__m128i = ---, ---
+ switch st.rate {
+ case _RATE_128L:
+ t = x86._mm_xor_si128(st.s2, t)
+ for _ in 0 ..< 7 {
+ update_hw_128l(st, t, t)
+ }
+
+ t0 = x86._mm_xor_si128(st.s0, st.s1)
+ t0 = x86._mm_xor_si128(t0, st.s2)
+ t0 = x86._mm_xor_si128(t0, st.s3)
+
+ t1 = x86._mm_xor_si128(st.s4, st.s5)
+ t1 = x86._mm_xor_si128(t1, st.s6)
+ if len(tag) == TAG_SIZE_256 {
+ t1 = x86._mm_xor_si128(t1, st.s7)
+ }
+ case _RATE_256:
+ t = x86._mm_xor_si128(st.s3, t)
+ for _ in 0 ..< 7 {
+ update_hw_256(st, t)
+ }
+
+ t0 = x86._mm_xor_si128(st.s0, st.s1)
+ t0 = x86._mm_xor_si128(t0, st.s2)
+
+ t1 = x86._mm_xor_si128(st.s3, st.s4)
+ t1 = x86._mm_xor_si128(t1, st.s5)
+ }
+ switch len(tag) {
+ case TAG_SIZE_128:
+ t0 = x86._mm_xor_si128(t0, t1)
+ intrinsics.unaligned_store((^x86.__m128i)(&tag[0]), t0)
+ case TAG_SIZE_256:
+ intrinsics.unaligned_store((^x86.__m128i)(&tag[0]), t0)
+ intrinsics.unaligned_store((^x86.__m128i)(&tag[16]), t1)
+ }
+}
+
+@(private)
+reset_state_hw :: proc "contextless" (st: ^State_HW) {
+ mem.zero_explicit(st, size_of(st^))
+}
diff --git a/core/crypto/aes/aes_ctr.odin b/core/crypto/aes/aes_ctr.odin
index 20b75e57f..a74133235 100644
--- a/core/crypto/aes/aes_ctr.odin
+++ b/core/crypto/aes/aes_ctr.odin
@@ -21,9 +21,7 @@ Context_CTR :: struct {
// init_ctr initializes a Context_CTR with the provided key and IV.
init_ctr :: proc(ctx: ^Context_CTR, key, iv: []byte, impl := DEFAULT_IMPLEMENTATION) {
- if len(iv) != CTR_IV_SIZE {
- panic("crypto/aes: invalid CTR IV size")
- }
+ ensure(len(iv) == CTR_IV_SIZE, "crypto/aes: invalid CTR IV size")
init_impl(&ctx._impl, key, impl)
ctx._off = BLOCK_SIZE
@@ -36,16 +34,14 @@ init_ctr :: proc(ctx: ^Context_CTR, key, iv: []byte, impl := DEFAULT_IMPLEMENTAT
// keystream, and writes the resulting output to dst. dst and src MUST
// alias exactly or not at all.
xor_bytes_ctr :: proc(ctx: ^Context_CTR, dst, src: []byte) {
- assert(ctx._is_initialized)
+ ensure(ctx._is_initialized)
src, dst := src, dst
if dst_len := len(dst); dst_len < len(src) {
src = src[:dst_len]
}
- if bytes.alias_inexactly(dst, src) {
- panic("crypto/aes: dst and src alias inexactly")
- }
+ ensure(!bytes.alias_inexactly(dst, src), "crypto/aes: dst and src alias inexactly")
#no_bounds_check for remaining := len(src); remaining > 0; {
// Process multiple blocks at once
@@ -82,7 +78,7 @@ xor_bytes_ctr :: proc(ctx: ^Context_CTR, dst, src: []byte) {
// keystream_bytes_ctr fills dst with the raw AES-CTR keystream output.
keystream_bytes_ctr :: proc(ctx: ^Context_CTR, dst: []byte) {
- assert(ctx._is_initialized)
+ ensure(ctx._is_initialized)
dst := dst
#no_bounds_check for remaining := len(dst); remaining > 0; {
diff --git a/core/crypto/aes/aes_ecb.odin b/core/crypto/aes/aes_ecb.odin
index 32476006c..cac62de5d 100644
--- a/core/crypto/aes/aes_ecb.odin
+++ b/core/crypto/aes/aes_ecb.odin
@@ -19,11 +19,9 @@ init_ecb :: proc(ctx: ^Context_ECB, key: []byte, impl := DEFAULT_IMPLEMENTATION)
// encrypt_ecb encrypts the BLOCK_SIZE buffer src, and writes the result to dst.
encrypt_ecb :: proc(ctx: ^Context_ECB, dst, src: []byte) {
- assert(ctx._is_initialized)
-
- if len(dst) != BLOCK_SIZE || len(src) != BLOCK_SIZE {
- panic("crypto/aes: invalid buffer size(s)")
- }
+ ensure(ctx._is_initialized)
+ ensure(len(dst) == BLOCK_SIZE, "crypto/aes: invalid dst size")
+ ensure(len(dst) == BLOCK_SIZE, "crypto/aes: invalid src size")
switch &impl in ctx._impl {
case ct64.Context:
@@ -35,11 +33,9 @@ encrypt_ecb :: proc(ctx: ^Context_ECB, dst, src: []byte) {
// decrypt_ecb decrypts the BLOCK_SIZE buffer src, and writes the result to dst.
decrypt_ecb :: proc(ctx: ^Context_ECB, dst, src: []byte) {
- assert(ctx._is_initialized)
-
- if len(dst) != BLOCK_SIZE || len(src) != BLOCK_SIZE {
- panic("crypto/aes: invalid buffer size(s)")
- }
+ ensure(ctx._is_initialized)
+ ensure(len(dst) == BLOCK_SIZE, "crypto/aes: invalid dst size")
+ ensure(len(dst) == BLOCK_SIZE, "crypto/aes: invalid src size")
switch &impl in ctx._impl {
case ct64.Context:
diff --git a/core/crypto/aes/aes_gcm.odin b/core/crypto/aes/aes_gcm.odin
index 8616821ce..d349aa353 100644
--- a/core/crypto/aes/aes_gcm.odin
+++ b/core/crypto/aes/aes_gcm.odin
@@ -36,15 +36,11 @@ init_gcm :: proc(ctx: ^Context_GCM, key: []byte, impl := DEFAULT_IMPLEMENTATION)
//
// dst and plaintext MUST alias exactly or not at all.
seal_gcm :: proc(ctx: ^Context_GCM, dst, tag, iv, aad, plaintext: []byte) {
- assert(ctx._is_initialized)
+ ensure(ctx._is_initialized)
gcm_validate_common_slice_sizes(tag, iv, aad, plaintext)
- if len(dst) != len(plaintext) {
- panic("crypto/aes: invalid destination ciphertext size")
- }
- if bytes.alias_inexactly(dst, plaintext) {
- panic("crypto/aes: dst and plaintext alias inexactly")
- }
+ ensure(len(dst) == len(plaintext), "crypto/aes: invalid destination ciphertext size")
+ ensure(!bytes.alias_inexactly(dst, plaintext), "crypto/aes: dst and plaintext alias inexactly")
if impl, is_hw := ctx._impl.(Context_Impl_Hardware); is_hw {
gcm_seal_hw(&impl, dst, tag, iv, aad, plaintext)
@@ -76,15 +72,11 @@ seal_gcm :: proc(ctx: ^Context_GCM, dst, tag, iv, aad, plaintext: []byte) {
// dst and plaintext MUST alias exactly or not at all.
@(require_results)
open_gcm :: proc(ctx: ^Context_GCM, dst, iv, aad, ciphertext, tag: []byte) -> bool {
- assert(ctx._is_initialized)
+ ensure(ctx._is_initialized)
gcm_validate_common_slice_sizes(tag, iv, aad, ciphertext)
- if len(dst) != len(ciphertext) {
- panic("crypto/aes: invalid destination plaintext size")
- }
- if bytes.alias_inexactly(dst, ciphertext) {
- panic("crypto/aes: dst and ciphertext alias inexactly")
- }
+ ensure(len(dst) == len(ciphertext), "crypto/aes: invalid destination plaintext size")
+ ensure(!bytes.alias_inexactly(dst, ciphertext), "crypto/aes: dst and ciphertext alias inexactly")
if impl, is_hw := ctx._impl.(Context_Impl_Hardware); is_hw {
return gcm_open_hw(&impl, dst, iv, aad, ciphertext, tag)
@@ -122,21 +114,13 @@ reset_gcm :: proc "contextless" (ctx: ^Context_GCM) {
@(private = "file")
gcm_validate_common_slice_sizes :: proc(tag, iv, aad, text: []byte) {
- if len(tag) != GCM_TAG_SIZE {
- panic("crypto/aes: invalid GCM tag size")
- }
+ ensure(len(tag) == GCM_TAG_SIZE, "crypto/aes: invalid GCM tag size")
// The specification supports IVs in the range [1, 2^64) bits.
- if l := len(iv); l == 0 || u64(l) >= GCM_IV_SIZE_MAX {
- panic("crypto/aes: invalid GCM IV size")
- }
+ ensure(len(iv) == 0 || u64(len(iv)) <= GCM_IV_SIZE_MAX, "crypto/aes: invalid GCM IV size")
- if aad_len := u64(len(aad)); aad_len > GCM_A_MAX {
- panic("crypto/aes: oversized GCM aad")
- }
- if text_len := u64(len(text)); text_len > GCM_P_MAX {
- panic("crypto/aes: oversized GCM src data")
- }
+ ensure(u64(len(aad)) <= GCM_A_MAX, "crypto/aes: oversized GCM aad")
+ ensure(u64(len(text)) <= GCM_P_MAX, "crypto/aes: oversized GCM data")
}
@(private = "file")
diff --git a/core/crypto/aes/aes_gcm_hw_intel.odin b/core/crypto/aes/aes_gcm_hw_intel.odin
index 4cb5ab3b2..3982d1452 100644
--- a/core/crypto/aes/aes_gcm_hw_intel.odin
+++ b/core/crypto/aes/aes_gcm_hw_intel.odin
@@ -235,7 +235,7 @@ gctr_hw :: proc(
// BUG: Sticking this in gctr_hw (like the other implementations) crashes
// the compiler.
//
-// src/check_expr.cpp(7892): Assertion Failure: `c->curr_proc_decl->entity`
+// src/check_expr.cpp(8104): Assertion Failure: `c->curr_proc_decl->entity`
@(private = "file", enable_target_feature = "sse4.1")
hw_inc_ctr32 :: #force_inline proc "contextless" (src: ^x86.__m128i, ctr: u32) -> (x86.__m128i, u32) {
ret := x86._mm_insert_epi32(src^, i32(intrinsics.byte_swap(ctr)), 3)
diff --git a/core/crypto/blake2b/blake2b.odin b/core/crypto/blake2b/blake2b.odin
index 74396b103..3b3fc6649 100644
--- a/core/crypto/blake2b/blake2b.odin
+++ b/core/crypto/blake2b/blake2b.odin
@@ -18,7 +18,7 @@ package blake2b
import "../_blake2"
// DIGEST_SIZE is the BLAKE2b digest size in bytes.
-DIGEST_SIZE :: 64
+DIGEST_SIZE :: _blake2.BLAKE2B_SIZE
// BLOCK_SIZE is the BLAKE2b block size in bytes.
BLOCK_SIZE :: _blake2.BLAKE2B_BLOCK_SIZE
@@ -27,9 +27,11 @@ BLOCK_SIZE :: _blake2.BLAKE2B_BLOCK_SIZE
Context :: _blake2.Blake2b_Context
// init initializes a Context with the default BLAKE2b config.
-init :: proc(ctx: ^Context) {
+init :: proc(ctx: ^Context, digest_size := DIGEST_SIZE) {
+ ensure(digest_size <= _blake2.MAX_SIZE, "crypto/blake2b: invalid digest size")
+
cfg: _blake2.Blake2_Config
- cfg.size = _blake2.BLAKE2B_SIZE
+ cfg.size = u8(digest_size)
_blake2.init(ctx, &cfg)
}
diff --git a/core/crypto/blake2s/blake2s.odin b/core/crypto/blake2s/blake2s.odin
index 339ddf027..9bbd44541 100644
--- a/core/crypto/blake2s/blake2s.odin
+++ b/core/crypto/blake2s/blake2s.odin
@@ -18,7 +18,7 @@ package blake2s
import "../_blake2"
// DIGEST_SIZE is the BLAKE2s digest size in bytes.
-DIGEST_SIZE :: 32
+DIGEST_SIZE :: _blake2.BLAKE2S_SIZE
// BLOCK_SIZE is the BLAKE2s block size in bytes.
BLOCK_SIZE :: _blake2.BLAKE2S_BLOCK_SIZE
@@ -27,9 +27,11 @@ BLOCK_SIZE :: _blake2.BLAKE2S_BLOCK_SIZE
Context :: _blake2.Blake2s_Context
// init initializes a Context with the default BLAKE2s config.
-init :: proc(ctx: ^Context) {
+init :: proc(ctx: ^Context, digest_size := DIGEST_SIZE) {
+ ensure(digest_size <= _blake2.MAX_SIZE, "crypto/blake2s: invalid digest size")
+
cfg: _blake2.Blake2_Config
- cfg.size = _blake2.BLAKE2S_SIZE
+ cfg.size = u8(digest_size)
_blake2.init(ctx, &cfg)
}
diff --git a/core/crypto/chacha20/chacha20.odin b/core/crypto/chacha20/chacha20.odin
index dfab2bc65..e8d67eb3e 100644
--- a/core/crypto/chacha20/chacha20.odin
+++ b/core/crypto/chacha20/chacha20.odin
@@ -27,12 +27,8 @@ Context :: struct {
// init inititializes a Context for ChaCha20 or XChaCha20 with the provided
// key and iv.
init :: proc(ctx: ^Context, key, iv: []byte, impl := DEFAULT_IMPLEMENTATION) {
- if len(key) != KEY_SIZE {
- panic("crypto/chacha20: invalid (X)ChaCha20 key size")
- }
- if l := len(iv); l != IV_SIZE && l != XIV_SIZE {
- panic("crypto/chacha20: invalid (X)ChaCha20 IV size")
- }
+ ensure(len(key) == KEY_SIZE, "crypto/chacha20: invalid (X)ChaCha20 key size")
+ ensure(len(iv) == IV_SIZE || len(iv) == XIV_SIZE, "crypto/chacha20: invalid (X)ChaCha20 IV size")
k, n := key, iv
@@ -67,16 +63,14 @@ seek :: proc(ctx: ^Context, block_nr: u64) {
// keystream, and writes the resulting output to dst. Dst and src MUST
// alias exactly or not at all.
xor_bytes :: proc(ctx: ^Context, dst, src: []byte) {
- assert(ctx._state._is_initialized)
+ ensure(ctx._state._is_initialized)
src, dst := src, dst
if dst_len := len(dst); dst_len < len(src) {
src = src[:dst_len]
}
- if bytes.alias_inexactly(dst, src) {
- panic("crypto/chacha20: dst and src alias inexactly")
- }
+ ensure(!bytes.alias_inexactly(dst, src), "crypto/chacha20: dst and src alias inexactly")
st := &ctx._state
#no_bounds_check for remaining := len(src); remaining > 0; {
@@ -114,7 +108,7 @@ xor_bytes :: proc(ctx: ^Context, dst, src: []byte) {
// keystream_bytes fills dst with the raw (X)ChaCha20 keystream output.
keystream_bytes :: proc(ctx: ^Context, dst: []byte) {
- assert(ctx._state._is_initialized)
+ ensure(ctx._state._is_initialized)
dst, st := dst, &ctx._state
#no_bounds_check for remaining := len(dst); remaining > 0; {
diff --git a/core/crypto/chacha20poly1305/chacha20poly1305.odin b/core/crypto/chacha20poly1305/chacha20poly1305.odin
index 3de2532dd..6706b3820 100644
--- a/core/crypto/chacha20poly1305/chacha20poly1305.odin
+++ b/core/crypto/chacha20poly1305/chacha20poly1305.odin
@@ -29,13 +29,9 @@ _P_MAX :: 64 * 0xffffffff // 64 * (2^32-1)
@(private)
_validate_common_slice_sizes :: proc (tag, iv, aad, text: []byte, is_xchacha: bool) {
- if len(tag) != TAG_SIZE {
- panic("crypto/chacha20poly1305: invalid destination tag size")
- }
expected_iv_len := is_xchacha ? XIV_SIZE : IV_SIZE
- if len(iv) != expected_iv_len {
- panic("crypto/chacha20poly1305: invalid IV size")
- }
+ ensure(len(tag) == TAG_SIZE, "crypto/chacha20poly1305: invalid destination tag size")
+ ensure(len(iv) == expected_iv_len, "crypto/chacha20poly1305: invalid IV size")
#assert(size_of(int) == 8 || size_of(int) <= 4)
when size_of(int) == 8 {
@@ -45,13 +41,11 @@ _validate_common_slice_sizes :: proc (tag, iv, aad, text: []byte, is_xchacha: bo
// A_MAX is limited by size_of(int), so there is no need to
// enforce it. P_MAX only needs to be checked on 64-bit targets,
// for reasons that should be obvious.
- if text_len := len(text); text_len > _P_MAX {
- panic("crypto/chacha20poly1305: oversized src data")
- }
+ ensure(len(text) <= _P_MAX, "crypto/chacha20poly1305: oversized src data")
}
}
-@(private)
+@(private, rodata)
_PAD: [16]byte
@(private)
@@ -71,9 +65,7 @@ Context :: struct {
// init initializes a Context with the provided key, for AEAD_CHACHA20_POLY1305.
init :: proc(ctx: ^Context, key: []byte, impl := chacha20.DEFAULT_IMPLEMENTATION) {
- if len(key) != KEY_SIZE {
- panic("crypto/chacha20poly1305: invalid key size")
- }
+ ensure(len(key) == KEY_SIZE, "crypto/chacha20poly1305: invalid key size")
copy(ctx._key[:], key)
ctx._impl = impl
@@ -96,11 +88,11 @@ init_xchacha :: proc(ctx: ^Context, key: []byte, impl := chacha20.DEFAULT_IMPLEM
//
// dst and plaintext MUST alias exactly or not at all.
seal :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
+ ensure(ctx._is_initialized)
+
ciphertext := dst
_validate_common_slice_sizes(tag, iv, aad, plaintext, ctx._is_xchacha)
- if len(ciphertext) != len(plaintext) {
- panic("crypto/chacha20poly1305: invalid destination ciphertext size")
- }
+ ensure(len(ciphertext) == len(plaintext), "crypto/chacha20poly1305: invalid destination ciphertext size")
stream_ctx: chacha20.Context = ---
chacha20.init(&stream_ctx, ctx._key[:],iv, ctx._impl)
@@ -151,11 +143,11 @@ seal :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
// dst and plaintext MUST alias exactly or not at all.
@(require_results)
open :: proc(ctx: ^Context, dst, iv, aad, ciphertext, tag: []byte) -> bool {
+ ensure(ctx._is_initialized)
+
plaintext := dst
_validate_common_slice_sizes(tag, iv, aad, ciphertext, ctx._is_xchacha)
- if len(ciphertext) != len(plaintext) {
- panic("crypto/chacha20poly1305: invalid destination plaintext size")
- }
+ ensure(len(ciphertext) == len(plaintext), "crypto/chacha20poly1305: invalid destination plaintext size")
// Note: Unlike encrypt, this can fail early, so use defer for
// sanitization rather than assuming control flow reaches certain
diff --git a/core/crypto/deoxysii/deoxysii.odin b/core/crypto/deoxysii/deoxysii.odin
new file mode 100644
index 000000000..cead770e2
--- /dev/null
+++ b/core/crypto/deoxysii/deoxysii.odin
@@ -0,0 +1,280 @@
+/*
+package deoxysii implements the Deoxys-II-256 Authenticated Encryption
+with Additional Data algorithm.
+
+- [[ https://sites.google.com/view/deoxyscipher ]]
+- [[ https://thomaspeyrin.github.io/web/assets/docs/papers/Jean-etal-JoC2021.pdf ]]
+*/
+package deoxysii
+
+import "base:intrinsics"
+import "core:bytes"
+import "core:crypto/aes"
+import "core:mem"
+import "core:simd"
+
+// KEY_SIZE is the Deoxys-II-256 key size in bytes.
+KEY_SIZE :: 32
+// IV_SIZE iss the Deoxys-II-256 IV size in bytes.
+IV_SIZE :: 15 // 120-bits
+// TAG_SIZE is the Deoxys-II-256 tag size in bytes.
+TAG_SIZE :: 16
+
+@(private)
+PREFIX_AD_BLOCK :: 0b0010
+@(private)
+PREFIX_AD_FINAL :: 0b0110
+@(private)
+PREFIX_MSG_BLOCK :: 0b0000
+@(private)
+PREFIX_MSG_FINAL :: 0b0100
+@(private)
+PREFIX_TAG :: 0b0001
+@(private)
+PREFIX_SHIFT :: 4
+
+@(private)
+BC_ROUNDS :: 16
+@(private)
+BLOCK_SIZE :: aes.BLOCK_SIZE
+
+@(private = "file")
+_LFSR2_MASK :: simd.u8x16{
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+}
+@(private = "file")
+_LFSR3_MASK :: simd.u8x16{
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+}
+@(private = "file")
+_LFSR_SH1 :: _LFSR2_MASK
+@(private = "file")
+_LFSR_SH5 :: simd.u8x16{
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+}
+@(private = "file")
+_LFSR_SH7 :: simd.u8x16{
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+}
+@(private = "file", rodata)
+_RCONS := []byte {
+ 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a,
+ 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39,
+ 0x72,
+}
+
+// Context is a keyed Deoxys-II-256 instance.
+Context :: struct {
+ _subkeys: [BC_ROUNDS+1][16]byte,
+ _impl: aes.Implementation,
+ _is_initialized: bool,
+}
+
+@(private)
+_validate_common_slice_sizes :: proc (ctx: ^Context, tag, iv, aad, text: []byte) {
+ ensure(len(tag) == TAG_SIZE, "crypto/deoxysii: invalid tag size")
+ ensure(len(iv) == IV_SIZE, "crypto/deoxysii: invalid IV size")
+
+ #assert(size_of(int) == 8 || size_of(int) <= 4)
+ // For the nonce-misuse resistant mode, the total size of the
+ // associated data and the total size of the message do not exceed
+ // `16 * 2^max_l * 2^max_m bytes`, thus 2^128 bytes for all variants
+ // of Deoxys-II. Moreover, the maximum number of messages that can
+ // be handled for a same key is 2^max_m, that is 2^64 for all variants
+ // of Deoxys.
+}
+
+// init initializes a Context with the provided key.
+init :: proc(ctx: ^Context, key: []byte, impl := aes.DEFAULT_IMPLEMENTATION) {
+ ensure(len(key) == KEY_SIZE, "crypto/deoxysii: invalid key size")
+
+ ctx._impl = impl
+ if ctx._impl == .Hardware && !is_hardware_accelerated() {
+ ctx._impl = .Portable
+ }
+
+ derive_ks(ctx, key)
+
+ ctx._is_initialized = true
+}
+
+// seal encrypts the plaintext and authenticates the aad and ciphertext,
+// with the provided Context and iv, stores the output in dst and tag.
+//
+// dst and plaintext MUST alias exactly or not at all.
+seal :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
+ ensure(ctx._is_initialized)
+
+ _validate_common_slice_sizes(ctx, tag, iv, aad, plaintext)
+ ensure(len(dst) == len(plaintext), "crypto/deoxysii: invalid destination ciphertext size")
+ ensure(!bytes.alias_inexactly(dst, plaintext), "crypto/deoxysii: dst and plaintext alias inexactly")
+
+ switch ctx._impl {
+ case .Hardware:
+ e_hw(ctx, dst, tag, iv, aad, plaintext)
+ case .Portable:
+ e_ref(ctx, dst, tag, iv, aad, plaintext)
+ }
+}
+
+// open authenticates the aad and ciphertext, and decrypts the ciphertext,
+// with the provided Context, iv, and tag, and stores the output in dst,
+// returning true iff the authentication was successful. If authentication
+// fails, the destination buffer will be zeroed.
+//
+// dst and plaintext MUST alias exactly or not at all.
+@(require_results)
+open :: proc(ctx: ^Context, dst, iv, aad, ciphertext, tag: []byte) -> bool {
+ ensure(ctx._is_initialized)
+
+ _validate_common_slice_sizes(ctx, tag, iv, aad, ciphertext)
+ ensure(len(dst) == len(ciphertext), "crypto/deoxysii: invalid destination plaintext size")
+ ensure(!bytes.alias_inexactly(dst, ciphertext), "crypto/deoxysii: dst and ciphertext alias inexactly")
+
+ ok: bool
+ switch ctx._impl {
+ case .Hardware:
+ ok = d_hw(ctx, dst, iv, aad, ciphertext, tag)
+ case .Portable:
+ ok = d_ref(ctx, dst, iv, aad, ciphertext, tag)
+ }
+ if !ok {
+ mem.zero_explicit(raw_data(dst), len(ciphertext))
+ }
+
+ return ok
+}
+
+// reset sanitizes the Context. The Context must be
+// re-initialized to be used again.
+reset :: proc "contextless" (ctx: ^Context) {
+ mem.zero_explicit(&ctx._subkeys, len(ctx._subkeys))
+ ctx._is_initialized = false
+}
+
+@(private = "file")
+derive_ks :: proc "contextless" (ctx: ^Context, key: []byte) {
+ // Derive the constant component of each subtweakkey.
+ //
+ // The key schedule is as thus:
+ //
+ // STK_i = TK1_i ^ TK2_i ^ TK3_i ^ RC_i
+ //
+ // TK1_i = h(TK1_(i-1))
+ // TK2_i = h(LFSR2(TK2_(i-1)))
+ // TK3_i = h(LFSR3(TK2_(i-1)))
+ //
+ // where:
+ //
+ // KT = K || T
+ // W3 = KT[:16]
+ // W2 = KT[16:32]
+ // W1 = KT[32:]
+ //
+ // TK1_0 = W1
+ // TK2_0 = W2
+ // TK3_0 = W3
+ //
+ // As `K` is fixed per Context, the XORs of `TK3_0 .. TK3_n`,
+ // `TK2_0 .. TK2_n` and RC_i can be precomputed in advance like
+ // thus:
+ //
+ // subkey_i = TK3_i ^ TK2_i ^ RC_i
+ //
+ // When it is time to actually call Deoxys-BC-384, it is then
+ // a simple matter of deriving each round subtweakkey via:
+ //
+ // TK1_0 = T (Tweak)
+ // STK_0 = subkey_0 ^ TK1_0
+ // STK_i = subkey_i (precomputed) ^ H(TK1_(i-1))
+ //
+ // We opt to use SIMD here and for the subtweakkey deriviation
+ // as `H()` is typically a single vector instruction.
+
+ tk2 := intrinsics.unaligned_load((^simd.u8x16)(raw_data(key[16:])))
+ tk3 := intrinsics.unaligned_load((^simd.u8x16)(raw_data(key)))
+
+ // subkey_0 does not apply LFSR2/3 or H.
+ intrinsics.unaligned_store(
+ (^simd.u8x16)(&ctx._subkeys[0]),
+ simd.bit_xor(
+ tk2,
+ simd.bit_xor(
+ tk3,
+ rcon(0),
+ ),
+ ),
+ )
+
+ // Precompute k_1 .. k_16.
+ for i in 1 ..< BC_ROUNDS+1 {
+ tk2 = h(lfsr2(tk2))
+ tk3 = h(lfsr3(tk3))
+ intrinsics.unaligned_store(
+ (^simd.u8x16)(&ctx._subkeys[i]),
+ simd.bit_xor(
+ tk2,
+ simd.bit_xor(
+ tk3,
+ rcon(i),
+ ),
+ ),
+ )
+ }
+}
+
+@(private = "file")
+lfsr2 :: #force_inline proc "contextless" (tk: simd.u8x16) -> simd.u8x16 {
+ // LFSR2 is a application of the following LFSR to each byte of input.
+ // (x7||x6||x5||x4||x3||x2||x1||x0) -> (x6||x5||x4||x3||x2||x1||x0||x7 ^ x5)
+ return simd.bit_or(
+ simd.shl(tk, _LFSR_SH1),
+ simd.bit_and(
+ simd.bit_xor(
+ simd.shr(tk, _LFSR_SH7), // x7
+ simd.shr(tk, _LFSR_SH5), // x5
+ ),
+ _LFSR2_MASK,
+ ),
+ )
+}
+
+@(private = "file")
+lfsr3 :: #force_inline proc "contextless" (tk: simd.u8x16) -> simd.u8x16 {
+ // LFSR3 is a application of the following LFSR to each byte of input.
+ // (x7||x6||x5||x4||x3||x2||x1||x0) -> (x0 ^ x6||x7||x6||x5||x4||x3||x2||x1)
+ return simd.bit_or(
+ simd.shr(tk, _LFSR_SH1),
+ simd.bit_and(
+ simd.bit_xor(
+ simd.shl(tk, _LFSR_SH7), // x0
+ simd.shl(tk, _LFSR_SH1), // x6
+ ),
+ _LFSR3_MASK,
+ ),
+ )
+}
+
+@(private)
+h :: #force_inline proc "contextless" (tk: simd.u8x16) -> simd.u8x16 {
+ return simd.swizzle(
+ tk,
+ 0x01, 0x06, 0x0b, 0x0c, 0x05, 0x0a, 0x0f, 0x00,
+ 0x09, 0x0e, 0x03, 0x04, 0x0d, 0x02, 0x07, 0x08,
+ )
+}
+
+@(private = "file")
+rcon :: #force_inline proc "contextless" (rd: int) -> simd.u8x16 #no_bounds_check {
+ rc := _RCONS[rd]
+ return simd.u8x16{
+ 1, 2, 4, 8,
+ rc, rc, rc, rc,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ }
+}
\ No newline at end of file
diff --git a/core/crypto/deoxysii/deoxysii_impl_ct64.odin b/core/crypto/deoxysii/deoxysii_impl_ct64.odin
new file mode 100644
index 000000000..c4d0edb03
--- /dev/null
+++ b/core/crypto/deoxysii/deoxysii_impl_ct64.odin
@@ -0,0 +1,399 @@
+package deoxysii
+
+import "base:intrinsics"
+import "core:crypto"
+import aes "core:crypto/_aes/ct64"
+import "core:encoding/endian"
+import "core:mem"
+import "core:simd"
+
+// This uses the bitlsiced 64-bit general purpose register SWAR AES
+// round function. The encryption pass skips orthogonalizing the
+// AES round function input as it is aways going to be the leading 0
+// padded IV, and doing a 64-byte copy is faster.
+
+@(private = "file")
+TWEAK_SIZE :: 16
+
+@(private = "file")
+State_SW :: struct {
+ ctx: ^Context,
+ q_stk, q_b: [8]u64,
+}
+
+@(private = "file")
+auth_tweak :: #force_inline proc "contextless" (
+ dst: ^[TWEAK_SIZE]byte,
+ prefix: byte,
+ block_nr: int,
+) {
+ endian.unchecked_put_u64be(dst[8:], u64(block_nr))
+ endian.unchecked_put_u64le(dst[0:], u64(prefix) << PREFIX_SHIFT) // dst[0] = prefix << PREFIX_SHIFT
+}
+
+@(private = "file")
+enc_tweak :: #force_inline proc "contextless" (
+ dst: ^[TWEAK_SIZE]byte,
+ tag: ^[TAG_SIZE]byte,
+ block_nr: int,
+) {
+ tmp: [8]byte
+ endian.unchecked_put_u64be(tmp[:], u64(block_nr))
+
+ copy(dst[:], tag[:])
+ dst[0] |= 0x80
+ for i in 0 ..< 8 {
+ dst[i+8] ~= tmp[i]
+ }
+}
+
+@(private = "file")
+enc_plaintext :: #force_inline proc "contextless" (
+ dst: ^[8]u64,
+ iv: []byte,
+) {
+ tmp: [BLOCK_SIZE]byte = ---
+ tmp[0] = 0
+ copy(tmp[1:], iv[:])
+
+ q_0, q_1 := aes.load_interleaved(tmp[:])
+ for i in 0 ..< 4 {
+ dst[i], dst[i+4] = q_0, q_1
+ }
+ aes.orthogonalize(dst)
+}
+
+@(private = "file")
+bc_x4 :: proc "contextless" (
+ ctx: ^Context,
+ dst: []byte,
+ tweaks: ^[4][TWEAK_SIZE]byte,
+ q_stk: ^[8]u64,
+ q_b: ^[8]u64, // Orthogonalized
+ n: int,
+) {
+ tk1s: [4]simd.u8x16
+ for j in 0 ..< n {
+ tk1s[j] = intrinsics.unaligned_load((^simd.u8x16)(&tweaks[j]))
+ }
+
+ // Deoxys-BC-384
+ for i in 0 ..= BC_ROUNDS {
+ // Derive the round's subtweakkey
+ sk := intrinsics.unaligned_load((^simd.u8x16)(&ctx._subkeys[i]))
+ for j in 0 ..< n {
+ if i != 0 {
+ tk1s[j] = h(tk1s[j])
+ }
+ intrinsics.unaligned_store(
+ (^simd.u8x16)(raw_data(dst)),
+ simd.bit_xor(sk, tk1s[j]),
+ )
+ q_stk[j], q_stk[j+4] = aes.load_interleaved(dst[:])
+ }
+ aes.orthogonalize(q_stk)
+
+ if i != 0 {
+ aes.sub_bytes(q_b)
+ aes.shift_rows(q_b)
+ aes.mix_columns(q_b)
+ }
+ aes.add_round_key(q_b, q_stk[:])
+ }
+
+ aes.orthogonalize(q_b)
+ for i in 0 ..< n {
+ aes.store_interleaved(dst[i*BLOCK_SIZE:], q_b[i], q_b[i+4])
+ }
+}
+
+@(private = "file", require_results)
+bc_absorb :: proc "contextless" (
+ st: ^State_SW,
+ dst: []byte,
+ src: []byte,
+ tweak_prefix: byte,
+ stk_block_nr: int,
+) -> int {
+ tweaks: [4][TWEAK_SIZE]byte = ---
+ tmp: [BLOCK_SIZE*4]byte = ---
+
+ src, stk_block_nr := src, stk_block_nr
+ dst_ := intrinsics.unaligned_load((^simd.u8x16)(raw_data(dst)))
+
+ nr_blocks := len(src) / BLOCK_SIZE
+ for nr_blocks > 0 {
+ // Derive the tweak(s), orthogonalize the plaintext
+ n := min(nr_blocks, 4)
+ for i in 0 ..< n {
+ auth_tweak(&tweaks[i], tweak_prefix, stk_block_nr + i)
+ st.q_b[i], st.q_b[i + 4] = aes.load_interleaved(src)
+ src = src[BLOCK_SIZE:]
+ }
+ aes.orthogonalize(&st.q_b)
+
+ // Deoxys-BC-384
+ bc_x4(st.ctx, tmp[:], &tweaks, &st.q_stk, &st.q_b, n)
+
+ // XOR in the existing Auth/tag
+ for i in 0 ..< n {
+ dst_ = simd.bit_xor(
+ dst_,
+ intrinsics.unaligned_load((^simd.u8x16)(raw_data(tmp[i*BLOCK_SIZE:]))),
+ )
+ }
+
+ stk_block_nr += n
+ nr_blocks -= n
+ }
+
+ intrinsics.unaligned_store((^simd.u8x16)(raw_data(dst)), dst_)
+
+ mem.zero_explicit(&tweaks, size_of(tweaks))
+ mem.zero_explicit(&tmp, size_of(tmp))
+
+ return stk_block_nr
+}
+
+@(private = "file")
+bc_final :: proc "contextless" (
+ st: ^State_SW,
+ dst: []byte,
+ iv: []byte,
+) {
+ tweaks: [4][TWEAK_SIZE]byte = ---
+
+ tweaks[0][0] = PREFIX_TAG << PREFIX_SHIFT
+ copy(tweaks[0][1:], iv)
+
+ st.q_b[0], st.q_b[4] = aes.load_interleaved(dst)
+ aes.orthogonalize(&st.q_b)
+
+ bc_x4(st.ctx, dst, &tweaks, &st.q_stk, &st.q_b, 1)
+}
+
+@(private = "file", require_results)
+bc_encrypt :: proc "contextless" (
+ st: ^State_SW,
+ dst: []byte,
+ src: []byte,
+ q_n: ^[8]u64, // Orthogonalized
+ tweak_tag: ^[TAG_SIZE]byte,
+ stk_block_nr: int,
+) -> int {
+ tweaks: [4][TWEAK_SIZE]byte = ---
+ tmp: [BLOCK_SIZE*4]byte = ---
+
+ dst, src, stk_block_nr := dst, src, stk_block_nr
+
+ nr_blocks := len(src) / BLOCK_SIZE
+ for nr_blocks > 0 {
+ // Derive the tweak(s)
+ n := min(nr_blocks, 4)
+ for i in 0 ..< n {
+ enc_tweak(&tweaks[i], tweak_tag, stk_block_nr + i)
+ }
+ st.q_b = q_n^ // The plaintext is always `0^8 || N`
+
+ // Deoxys-BC-384
+ bc_x4(st.ctx, tmp[:], &tweaks, &st.q_stk, &st.q_b, n)
+
+ // XOR the ciphertext
+ for i in 0 ..< n {
+ intrinsics.unaligned_store(
+ (^simd.u8x16)(raw_data(dst[i*BLOCK_SIZE:])),
+ simd.bit_xor(
+ intrinsics.unaligned_load((^simd.u8x16)(raw_data(src[i*BLOCK_SIZE:]))),
+ intrinsics.unaligned_load((^simd.u8x16)(raw_data(tmp[i*BLOCK_SIZE:]))),
+ ),
+ )
+ }
+
+ dst, src = dst[n*BLOCK_SIZE:], src[n*BLOCK_SIZE:]
+ stk_block_nr += n
+ nr_blocks -= n
+ }
+
+ mem.zero_explicit(&tweaks, size_of(tweaks))
+ mem.zero_explicit(&tmp, size_of(tmp))
+
+ return stk_block_nr
+}
+
+@(private)
+e_ref :: proc "contextless" (ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) #no_bounds_check {
+ st: State_SW = ---
+ st.ctx = ctx
+
+ // Algorithm 3
+ //
+ // Associated data
+ // A_1 || ... || A_la || A_∗ <- A where each |A_i| = n and |A_∗| < n
+ // Auth <- 0^n
+ // for i = 0 to la − 1 do
+ // Auth <- Auth ^ EK(0010 || i, A_i+1)
+ // end
+ // if A_∗ != nil then
+ // Auth <- Auth ^ EK(0110 || la, pad10∗(A_∗))
+ // end
+ auth: [TAG_SIZE]byte
+ aad := aad
+ n := bc_absorb(&st, auth[:], aad, PREFIX_AD_BLOCK, 0)
+ aad = aad[n*BLOCK_SIZE:]
+ if l := len(aad); l > 0 {
+ a_star: [BLOCK_SIZE]byte
+
+ copy(a_star[:], aad)
+ a_star[l] = 0x80
+
+ _ = bc_absorb(&st, auth[:], a_star[:], PREFIX_AD_FINAL, n)
+ }
+
+ // Message authentication and tag generation
+ // M_1 || ... || M_l || M_∗ <- M where each |M_j| = n and |M_∗| < n
+ // tag <- Auth
+ // for j = 0 to l − 1 do
+ // tag <- tag ^ EK(0000 || j, M_j+1)
+ // end
+ // if M_∗ != nil then
+ // tag <- tag ^ EK(0100 || l, pad10∗(M_∗))
+ // end
+ // tag <- EK(0001 || 0^4 || N, tag)
+ m := plaintext
+ n = bc_absorb(&st, auth[:], m, PREFIX_MSG_BLOCK, 0)
+ m = m[n*BLOCK_SIZE:]
+ if l := len(m); l > 0 {
+ m_star: [BLOCK_SIZE]byte
+
+ copy(m_star[:], m)
+ m_star[l] = 0x80
+
+ _ = bc_absorb(&st, auth[:], m_star[:], PREFIX_MSG_FINAL, n)
+ }
+ bc_final(&st, auth[:], iv)
+
+ // Message encryption
+ // for j = 0 to l − 1 do
+ // C_j <- M_j ^ EK(1 || tag ^ j, 0^8 || N)
+ // end
+ // if M_∗ != nil then
+ // C_∗ <- M_* ^ EK(1 || tag ^ l, 0^8 || N)
+ // end
+ //
+ // return (C_1 || ... || C_l || C_∗, tag)
+ q_iv: [8]u64 = ---
+ enc_plaintext(&q_iv, iv)
+
+ m = plaintext
+ n = bc_encrypt(&st, dst, m, &q_iv, &auth, 0)
+ m = m[n*BLOCK_SIZE:]
+ if l := len(m); l > 0 {
+ m_star: [BLOCK_SIZE]byte
+
+ copy(m_star[:], m)
+ _ = bc_encrypt(&st, m_star[:], m_star[:], &q_iv, &auth, n)
+
+ copy(dst[n*BLOCK_SIZE:], m_star[:])
+
+ mem.zero_explicit(&m_star, size_of(m_star))
+ }
+
+ copy(tag, auth[:])
+
+ mem.zero_explicit(&st.q_stk, size_of(st.q_stk))
+ mem.zero_explicit(&st.q_b, size_of(st.q_b))
+}
+
+@(private, require_results)
+d_ref :: proc "contextless" (ctx: ^Context, dst, iv, aad, ciphertext, tag: []byte) -> bool {
+ st: State_SW = ---
+ st.ctx = ctx
+
+ // Algorithm 4
+ //
+ // Message decryption
+ // C_1 || ... || C_l || C_∗ <- C where each |C_j| = n and |C_∗| < n
+ // for j = 0 to l − 1 do
+ // M_j <- C_j ^ EK(1 || tag ^ j, 0^8 || N)
+ // end
+ // if C_∗ != nil then
+ // M_∗ <- C_∗ ^ EK(1 || tag ^ l, 0^8 || N)
+ // end
+ q_iv: [8]u64 = ---
+ enc_plaintext(&q_iv, iv)
+
+ auth: [TAG_SIZE]byte
+ copy(auth[:], tag)
+
+ m := ciphertext
+ n := bc_encrypt(&st, dst, m, &q_iv, &auth, 0)
+ m = m[n*BLOCK_SIZE:]
+ if l := len(m); l > 0 {
+ m_star: [BLOCK_SIZE]byte
+
+ copy(m_star[:], m)
+ _ = bc_encrypt(&st, m_star[:], m_star[:], &q_iv, &auth, n)
+
+ copy(dst[n*BLOCK_SIZE:], m_star[:])
+
+ mem.zero_explicit(&m_star, size_of(m_star))
+ }
+
+ // Associated data
+ // A_1 || ... || Al_a || A_∗ <- A where each |Ai_| = n and |A_∗| < n
+ // Auth <- 0
+ // for i = 0 to la − 1 do
+ // Auth <- Auth ^ EK(0010 || i, A_i+1)
+ // end
+ // if A∗ != nil then
+ // Auth <- Auth ^ EK(0110| | l_a, pad10∗(A_∗))
+ // end
+ auth = 0
+ aad := aad
+ n = bc_absorb(&st, auth[:], aad, PREFIX_AD_BLOCK, 0)
+ aad = aad[n*BLOCK_SIZE:]
+ if l := len(aad); l > 0 {
+ a_star: [BLOCK_SIZE]byte
+
+ copy(a_star[:], aad)
+ a_star[l] = 0x80
+
+ _ = bc_absorb(&st, auth[:], a_star[:], PREFIX_AD_FINAL, n)
+ }
+
+ // Message authentication and tag generation
+ // M_1 || ... || M_l || M_∗ <- M where each |M_j| = n and |M_∗| < n
+ // tag0 <- Auth
+ // for j = 0 to l − 1 do
+ // tag0 <- tag0 ^ EK(0000 || j, M_j+1)
+ // end
+ // if M_∗ != nil then
+ // tag0 <- tag0 ^ EK(0100 || l, pad10∗(M_∗))
+ // end
+ // tag0 <- EK(0001 || 0^4 || N, tag0)
+ m = dst[:len(ciphertext)]
+ n = bc_absorb(&st, auth[:], m, PREFIX_MSG_BLOCK, 0)
+ m = m[n*BLOCK_SIZE:]
+ if l := len(m); l > 0 {
+ m_star: [BLOCK_SIZE]byte
+
+ copy(m_star[:], m)
+ m_star[l] = 0x80
+
+ _ = bc_absorb(&st, auth[:], m_star[:], PREFIX_MSG_FINAL, n)
+
+ mem.zero_explicit(&m_star, size_of(m_star))
+ }
+ bc_final(&st, auth[:], iv)
+
+ // Tag verification
+ // if tag0 = tag then return (M_1 || ... || M_l || M_∗)
+ // else return false
+ ok := crypto.compare_constant_time(auth[:], tag) == 1
+
+ mem.zero_explicit(&auth, size_of(auth))
+ mem.zero_explicit(&st.q_stk, size_of(st.q_stk))
+ mem.zero_explicit(&st.q_b, size_of(st.q_b))
+
+ return ok
+}
diff --git a/core/crypto/deoxysii/deoxysii_impl_hw_gen.odin b/core/crypto/deoxysii/deoxysii_impl_hw_gen.odin
new file mode 100644
index 000000000..b0705ca62
--- /dev/null
+++ b/core/crypto/deoxysii/deoxysii_impl_hw_gen.odin
@@ -0,0 +1,21 @@
+#+build !amd64
+package deoxysii
+
+@(private = "file")
+ERR_HW_NOT_SUPPORTED :: "crypto/deoxysii: hardware implementation unsupported"
+
+// is_hardware_accelerated returns true iff hardware accelerated Deoxys-II
+// is supported.
+is_hardware_accelerated :: proc "contextless" () -> bool {
+ return false
+}
+
+@(private)
+e_hw :: proc "contextless" (ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) #no_bounds_check {
+ panic_contextless(ERR_HW_NOT_SUPPORTED)
+}
+
+@(private, require_results)
+d_hw :: proc "contextless" (ctx: ^Context, dst, iv, aad, ciphertext, tag: []byte) -> bool {
+ panic_contextless(ERR_HW_NOT_SUPPORTED)
+}
diff --git a/core/crypto/deoxysii/deoxysii_impl_hw_intel.odin b/core/crypto/deoxysii/deoxysii_impl_hw_intel.odin
new file mode 100644
index 000000000..d268009a2
--- /dev/null
+++ b/core/crypto/deoxysii/deoxysii_impl_hw_intel.odin
@@ -0,0 +1,434 @@
+#+build amd64
+package deoxysii
+
+import "base:intrinsics"
+import "core:crypto"
+import "core:crypto/aes"
+import "core:mem"
+import "core:simd"
+import "core:simd/x86"
+
+// This processes a maximum of 4 blocks at a time, as that is suitable
+// for most current hardware that doesn't say "Xeon".
+
+@(private = "file")
+_BIT_ENC :: x86.__m128i{0x80, 0}
+@(private = "file")
+_PREFIX_AD_BLOCK :: x86.__m128i{PREFIX_AD_BLOCK << PREFIX_SHIFT, 0}
+@(private = "file")
+_PREFIX_AD_FINAL :: x86.__m128i{PREFIX_AD_FINAL << PREFIX_SHIFT, 0}
+@(private = "file")
+_PREFIX_MSG_BLOCK :: x86.__m128i{PREFIX_MSG_BLOCK << PREFIX_SHIFT, 0}
+@(private = "file")
+_PREFIX_MSG_FINAL :: x86.__m128i{PREFIX_MSG_FINAL << PREFIX_SHIFT, 0}
+
+// is_hardware_accelerated returns true iff hardware accelerated Deoxys-II
+// is supported.
+is_hardware_accelerated :: proc "contextless" () -> bool {
+ return aes.is_hardware_accelerated()
+}
+
+@(private = "file", enable_target_feature = "sse4.1", require_results)
+auth_tweak :: #force_inline proc "contextless" (
+ prefix: x86.__m128i,
+ block_nr: int,
+) -> x86.__m128i {
+ return x86._mm_insert_epi64(prefix, i64(intrinsics.byte_swap(u64(block_nr))), 1)
+}
+
+@(private = "file", enable_target_feature = "sse2", require_results)
+enc_tweak :: #force_inline proc "contextless" (
+ tag: x86.__m128i,
+ block_nr: int,
+) -> x86.__m128i {
+ return x86._mm_xor_si128(
+ x86._mm_or_si128(tag, _BIT_ENC),
+ x86.__m128i{0, i64(intrinsics.byte_swap(u64(block_nr)))},
+ )
+}
+
+@(private = "file", enable_target_feature = "ssse3", require_results)
+h_ :: #force_inline proc "contextless" (tk1: x86.__m128i) -> x86.__m128i {
+ return transmute(x86.__m128i)h(transmute(simd.u8x16)tk1)
+}
+
+@(private = "file", enable_target_feature = "sse2,ssse3,aes", require_results)
+bc_x4 :: #force_inline proc "contextless" (
+ ctx: ^Context,
+ s_0, s_1, s_2, s_3: x86.__m128i,
+ tweak_0, tweak_1, tweak_2, tweak_3: x86.__m128i,
+) -> (x86.__m128i, x86.__m128i, x86.__m128i, x86.__m128i) #no_bounds_check {
+ s_0, s_1, s_2, s_3 := s_0, s_1, s_2, s_3
+ tk1_0, tk1_1, tk1_2, tk1_3 := tweak_0, tweak_1, tweak_2, tweak_3
+
+ sk := intrinsics.unaligned_load((^x86.__m128i)(&ctx._subkeys[0]))
+ stk_0 := x86._mm_xor_si128(tk1_0, sk)
+ stk_1 := x86._mm_xor_si128(tk1_1, sk)
+ stk_2 := x86._mm_xor_si128(tk1_2, sk)
+ stk_3 := x86._mm_xor_si128(tk1_3, sk)
+
+ s_0 = x86._mm_xor_si128(s_0, stk_0)
+ s_1 = x86._mm_xor_si128(s_1, stk_1)
+ s_2 = x86._mm_xor_si128(s_2, stk_2)
+ s_3 = x86._mm_xor_si128(s_3, stk_3)
+
+ for i in 1 ..= BC_ROUNDS {
+ sk = intrinsics.unaligned_load((^x86.__m128i)(&ctx._subkeys[i]))
+
+ tk1_0 = h_(tk1_0)
+ tk1_1 = h_(tk1_1)
+ tk1_2 = h_(tk1_2)
+ tk1_3 = h_(tk1_3)
+
+ stk_0 = x86._mm_xor_si128(tk1_0, sk)
+ stk_1 = x86._mm_xor_si128(tk1_1, sk)
+ stk_2 = x86._mm_xor_si128(tk1_2, sk)
+ stk_3 = x86._mm_xor_si128(tk1_3, sk)
+
+ s_0 = x86._mm_aesenc_si128(s_0, stk_0)
+ s_1 = x86._mm_aesenc_si128(s_1, stk_1)
+ s_2 = x86._mm_aesenc_si128(s_2, stk_2)
+ s_3 = x86._mm_aesenc_si128(s_3, stk_3)
+ }
+
+ return s_0, s_1, s_2, s_3
+}
+
+@(private = "file", enable_target_feature = "sse2,ssse3,aes", require_results)
+bc_x1 :: #force_inline proc "contextless" (
+ ctx: ^Context,
+ s: x86.__m128i,
+ tweak: x86.__m128i,
+) -> x86.__m128i #no_bounds_check {
+ s, tk1 := s, tweak
+
+ sk := intrinsics.unaligned_load((^x86.__m128i)(&ctx._subkeys[0]))
+ stk := x86._mm_xor_si128(tk1, sk)
+
+ s = x86._mm_xor_si128(s, stk)
+
+ for i in 1 ..= BC_ROUNDS {
+ sk = intrinsics.unaligned_load((^x86.__m128i)(&ctx._subkeys[i]))
+
+ tk1 = h_(tk1)
+
+ stk = x86._mm_xor_si128(tk1, sk)
+
+ s = x86._mm_aesenc_si128(s, stk)
+ }
+
+ return s
+}
+
+@(private = "file", enable_target_feature = "sse2,ssse3,sse4.1,aes", require_results)
+bc_absorb :: proc "contextless" (
+ ctx: ^Context,
+ tag: x86.__m128i,
+ src: []byte,
+ tweak_prefix: x86.__m128i,
+ stk_block_nr: int,
+) -> (x86.__m128i, int) #no_bounds_check {
+ src, stk_block_nr, tag := src, stk_block_nr, tag
+
+ nr_blocks := len(src) / BLOCK_SIZE
+ for nr_blocks >= 4 {
+ d_0, d_1, d_2, d_3 := bc_x4(
+ ctx,
+ intrinsics.unaligned_load((^x86.__m128i)(raw_data(src))),
+ intrinsics.unaligned_load((^x86.__m128i)(raw_data(src[BLOCK_SIZE:]))),
+ intrinsics.unaligned_load((^x86.__m128i)(raw_data(src[2*BLOCK_SIZE:]))),
+ intrinsics.unaligned_load((^x86.__m128i)(raw_data(src[3*BLOCK_SIZE:]))),
+ auth_tweak(tweak_prefix, stk_block_nr),
+ auth_tweak(tweak_prefix, stk_block_nr + 1),
+ auth_tweak(tweak_prefix, stk_block_nr + 2),
+ auth_tweak(tweak_prefix, stk_block_nr + 3),
+ )
+
+ tag = x86._mm_xor_si128(tag, d_0)
+ tag = x86._mm_xor_si128(tag, d_1)
+ tag = x86._mm_xor_si128(tag, d_2)
+ tag = x86._mm_xor_si128(tag, d_3)
+
+ src = src[4*BLOCK_SIZE:]
+ stk_block_nr += 4
+ nr_blocks -= 4
+ }
+
+ for nr_blocks > 0 {
+ d := bc_x1(
+ ctx,
+ intrinsics.unaligned_load((^x86.__m128i)(raw_data(src))),
+ auth_tweak(tweak_prefix, stk_block_nr),
+ )
+
+ tag = x86._mm_xor_si128(tag, d)
+
+ src = src[BLOCK_SIZE:]
+ stk_block_nr += 1
+ nr_blocks -= 1
+ }
+
+ return tag, stk_block_nr
+}
+
+@(private = "file", enable_target_feature = "sse2,ssse3,aes", require_results)
+bc_final :: proc "contextless" (
+ ctx: ^Context,
+ tag: x86.__m128i,
+ iv: []byte,
+) -> x86.__m128i {
+ tmp: [BLOCK_SIZE]byte
+
+ tmp[0] = PREFIX_TAG << PREFIX_SHIFT
+ copy(tmp[1:], iv)
+
+ tweak := intrinsics.unaligned_load((^x86.__m128i)(&tmp))
+
+ return bc_x1(ctx, tag, tweak)
+}
+
+@(private = "file", enable_target_feature = "sse2,ssse3,aes", require_results)
+bc_encrypt :: proc "contextless" (
+ ctx: ^Context,
+ dst: []byte,
+ src: []byte,
+ iv: x86.__m128i,
+ tweak_tag: x86.__m128i,
+ stk_block_nr: int,
+) -> int {
+ dst, src, stk_block_nr := dst, src, stk_block_nr
+
+ nr_blocks := len(src) / BLOCK_SIZE
+ for nr_blocks >= 4 {
+ d_0, d_1, d_2, d_3 := bc_x4(
+ ctx,
+ iv, iv, iv, iv,
+ enc_tweak(tweak_tag, stk_block_nr),
+ enc_tweak(tweak_tag, stk_block_nr + 1),
+ enc_tweak(tweak_tag, stk_block_nr + 2),
+ enc_tweak(tweak_tag, stk_block_nr + 3),
+ )
+
+ intrinsics.unaligned_store(
+ (^x86.__m128i)(raw_data(dst)),
+ x86._mm_xor_si128(
+ d_0,
+ intrinsics.unaligned_load((^x86.__m128i)(raw_data(src))),
+ ),
+ )
+ intrinsics.unaligned_store(
+ (^x86.__m128i)(raw_data(dst[BLOCK_SIZE:])),
+ x86._mm_xor_si128(
+ d_1,
+ intrinsics.unaligned_load((^x86.__m128i)(raw_data(src[BLOCK_SIZE:]))),
+ ),
+ )
+ intrinsics.unaligned_store(
+ (^x86.__m128i)(raw_data(dst[2*BLOCK_SIZE:])),
+ x86._mm_xor_si128(
+ d_2,
+ intrinsics.unaligned_load((^x86.__m128i)(raw_data(src[2*BLOCK_SIZE:]))),
+ ),
+ )
+ intrinsics.unaligned_store(
+ (^x86.__m128i)(raw_data(dst[3*BLOCK_SIZE:])),
+ x86._mm_xor_si128(
+ d_3,
+ intrinsics.unaligned_load((^x86.__m128i)(raw_data(src[3*BLOCK_SIZE:]))),
+ ),
+ )
+
+ src, dst = src[4*BLOCK_SIZE:], dst[4*BLOCK_SIZE:]
+ stk_block_nr += 4
+ nr_blocks -= 4
+ }
+
+ for nr_blocks > 0 {
+ d := bc_x1(
+ ctx,
+ iv,
+ enc_tweak(tweak_tag, stk_block_nr),
+ )
+
+ intrinsics.unaligned_store(
+ (^x86.__m128i)(raw_data(dst)),
+ x86._mm_xor_si128(
+ d,
+ intrinsics.unaligned_load((^x86.__m128i)(raw_data(src))),
+ ),
+ )
+
+ src, dst = src[BLOCK_SIZE:], dst[BLOCK_SIZE:]
+ stk_block_nr += 1
+ nr_blocks -= 1
+ }
+
+ return stk_block_nr
+}
+
+@(private)
+e_hw :: proc "contextless" (ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) #no_bounds_check {
+ tmp: [BLOCK_SIZE]byte
+ copy(tmp[1:], iv)
+ iv_ := intrinsics.unaligned_load((^x86.__m128i)(raw_data(&tmp)))
+
+ // Algorithm 3
+ //
+ // Associated data
+ // A_1 || ... || A_la || A_∗ <- A where each |A_i| = n and |A_∗| < n
+ // Auth <- 0^n
+ // for i = 0 to la − 1 do
+ // Auth <- Auth ^ EK(0010 || i, A_i+1)
+ // end
+ // if A_∗ != nil then
+ // Auth <- Auth ^ EK(0110 || la, pad10∗(A_∗))
+ // end
+ auth: x86.__m128i
+ n: int
+
+ aad := aad
+ auth, n = bc_absorb(ctx, auth, aad, _PREFIX_AD_BLOCK, 0)
+ aad = aad[n*BLOCK_SIZE:]
+ if l := len(aad); l > 0 {
+ a_star: [BLOCK_SIZE]byte
+
+ copy(a_star[:], aad)
+ a_star[l] = 0x80
+
+ auth, _ = bc_absorb(ctx, auth, a_star[:], _PREFIX_AD_FINAL, n)
+ }
+
+ // Message authentication and tag generation
+ // M_1 || ... || M_l || M_∗ <- M where each |M_j| = n and |M_∗| < n
+ // tag <- Auth
+ // for j = 0 to l − 1 do
+ // tag <- tag ^ EK(0000 || j, M_j+1)
+ // end
+ // if M_∗ != nil then
+ // tag <- tag ^ EK(0100 || l, pad10∗(M_∗))
+ // end
+ // tag <- EK(0001 || 0^4 ||N, tag)
+ m := plaintext
+ auth, n = bc_absorb(ctx, auth, m, _PREFIX_MSG_BLOCK, 0)
+ m = m[n*BLOCK_SIZE:]
+ if l := len(m); l > 0 {
+ m_star: [BLOCK_SIZE]byte
+
+ copy(m_star[:], m)
+ m_star[l] = 0x80
+
+ auth, _ = bc_absorb(ctx, auth, m_star[:], _PREFIX_MSG_FINAL, n)
+ }
+ auth = bc_final(ctx, auth, iv)
+
+ // Message encryption
+ // for j = 0 to l − 1 do
+ // C_j <- M_j ^ EK(1 || tag ^ j, 0^8 || N)
+ // end
+ // if M_∗ != nil then
+ // C_∗ <- M_* ^ EK(1 || tag ^ l, 0^8 || N)
+ // end
+ //
+ // return (C_1 || ... || C_l || C_∗, tag)
+ m = plaintext
+ n = bc_encrypt(ctx, dst, m, iv_, auth, 0)
+ m = m[n*BLOCK_SIZE:]
+ if l := len(m); l > 0 {
+ m_star: [BLOCK_SIZE]byte
+
+ copy(m_star[:], m)
+ _ = bc_encrypt(ctx, m_star[:], m_star[:], iv_, auth, n)
+
+ copy(dst[n*BLOCK_SIZE:], m_star[:])
+ }
+
+ intrinsics.unaligned_store((^x86.__m128i)(raw_data(tag)), auth)
+}
+
+@(private, require_results)
+d_hw :: proc "contextless" (ctx: ^Context, dst, iv, aad, ciphertext, tag: []byte) -> bool {
+ tmp: [BLOCK_SIZE]byte
+ copy(tmp[1:], iv)
+ iv_ := intrinsics.unaligned_load((^x86.__m128i)(raw_data(&tmp)))
+
+ // Algorithm 4
+ //
+ // Message decryption
+ // C_1 || ... || C_l || C_∗ <- C where each |C_j| = n and |C_∗| < n
+ // for j = 0 to l − 1 do
+ // M_j <- C_j ^ EK(1 || tag ^ j, 0^8 || N)
+ // end
+ // if C_∗ != nil then
+ // M_∗ <- C_∗ ^ EK(1 || tag ^ l, 0^8 || N)
+ // end
+ auth := intrinsics.unaligned_load((^x86.__m128i)(raw_data(tag)))
+
+ m := ciphertext
+ n := bc_encrypt(ctx, dst, m, iv_, auth, 0)
+ m = m[n*BLOCK_SIZE:]
+ if l := len(m); l > 0 {
+ m_star: [BLOCK_SIZE]byte
+
+ copy(m_star[:], m)
+ _ = bc_encrypt(ctx, m_star[:], m_star[:], iv_, auth, n)
+
+ copy(dst[n*BLOCK_SIZE:], m_star[:])
+
+ mem.zero_explicit(&m_star, size_of(m_star))
+ }
+
+ // Associated data
+ // A_1 || ... || Al_a || A_∗ <- A where each |Ai_| = n and |A_∗| < n
+ // Auth <- 0
+ // for i = 0 to la − 1 do
+ // Auth <- Auth ^ EK(0010 || i, A_i+1)
+ // end
+ // if A∗ != nil then
+ // Auth <- Auth ^ EK(0110| | l_a, pad10∗(A_∗))
+ // end
+ auth = x86.__m128i{0, 0}
+ aad := aad
+ auth, n = bc_absorb(ctx, auth, aad, _PREFIX_AD_BLOCK, 0)
+ aad = aad[BLOCK_SIZE*n:]
+ if l := len(aad); l > 0 {
+ a_star: [BLOCK_SIZE]byte
+
+ copy(a_star[:], aad)
+ a_star[l] = 0x80
+
+ auth, _ = bc_absorb(ctx, auth, a_star[:], _PREFIX_AD_FINAL, n)
+ }
+
+ // Message authentication and tag generation
+ // M_1 || ... || M_l || M_∗ <- M where each |M_j| = n and |M_∗| < n
+ // tag0 <- Auth
+ // for j = 0 to l − 1 do
+ // tag0 <- tag0 ^ EK(0000 || j, M_j+1)
+ // end
+ // if M_∗ != nil then
+ // tag0 <- tag0 ^ EK(0100 || l, pad10∗(M_∗))
+ // end
+ // tag0 <- EK(0001 || 0^4 || N, tag0)
+ m = dst[:len(ciphertext)]
+ auth, n = bc_absorb(ctx, auth, m, _PREFIX_MSG_BLOCK, 0)
+ m = m[n*BLOCK_SIZE:]
+ if l := len(m); l > 0 {
+ m_star: [BLOCK_SIZE]byte
+
+ copy(m_star[:], m)
+ m_star[l] = 0x80
+
+ auth, _ = bc_absorb(ctx, auth, m_star[:], _PREFIX_MSG_FINAL, n)
+ }
+ auth = bc_final(ctx, auth, iv)
+
+ // Tag verification
+ // if tag0 = tag then return (M_1 || ... || M_l || M_∗)
+ // else return false
+ intrinsics.unaligned_store((^x86.__m128i)(raw_data(&tmp)), auth)
+ ok := crypto.compare_constant_time(tmp[:], tag) == 1
+
+ mem.zero_explicit(&tmp, size_of(tmp))
+
+ return ok
+}
diff --git a/core/crypto/ed25519/ed25519.odin b/core/crypto/ed25519/ed25519.odin
index 460a19563..deeb80685 100644
--- a/core/crypto/ed25519/ed25519.odin
+++ b/core/crypto/ed25519/ed25519.odin
@@ -81,12 +81,8 @@ private_key_set_bytes :: proc(priv_key: ^Private_Key, b: []byte) -> bool {
// private_key_bytes sets dst to byte-encoding of priv_key.
private_key_bytes :: proc(priv_key: ^Private_Key, dst: []byte) {
- if !priv_key._is_initialized {
- panic("crypto/ed25519: uninitialized private key")
- }
- if len(dst) != PRIVATE_KEY_SIZE {
- panic("crypto/ed25519: invalid destination size")
- }
+ ensure(priv_key._is_initialized, "crypto/ed25519: uninitialized private key")
+ ensure(len(dst) == PRIVATE_KEY_SIZE, "crypto/ed25519: invalid destination size")
copy(dst, priv_key._b[:])
}
@@ -98,12 +94,8 @@ private_key_clear :: proc "contextless" (priv_key: ^Private_Key) {
// sign writes the signature by priv_key over msg to sig.
sign :: proc(priv_key: ^Private_Key, msg, sig: []byte) {
- if !priv_key._is_initialized {
- panic("crypto/ed25519: uninitialized private key")
- }
- if len(sig) != SIGNATURE_SIZE {
- panic("crypto/ed25519: invalid destination size")
- }
+ ensure(priv_key._is_initialized, "crypto/ed25519: uninitialized private key")
+ ensure(len(sig) == SIGNATURE_SIZE, "crypto/ed25519: invalid destination size")
// 1. Compute the hash of the private key d, H(d) = (h_0, h_1, ..., h_2b-1)
// using SHA-512 for Ed25519. H(d) may be precomputed.
@@ -178,9 +170,7 @@ public_key_set_bytes :: proc "contextless" (pub_key: ^Public_Key, b: []byte) ->
// public_key_set_priv sets pub_key to the public component of priv_key.
public_key_set_priv :: proc(pub_key: ^Public_Key, priv_key: ^Private_Key) {
- if !priv_key._is_initialized {
- panic("crypto/ed25519: uninitialized public key")
- }
+ ensure(priv_key._is_initialized, "crypto/ed25519: uninitialized public key")
src := &priv_key._pub_key
copy(pub_key._b[:], src._b[:])
@@ -191,21 +181,15 @@ public_key_set_priv :: proc(pub_key: ^Public_Key, priv_key: ^Private_Key) {
// public_key_bytes sets dst to byte-encoding of pub_key.
public_key_bytes :: proc(pub_key: ^Public_Key, dst: []byte) {
- if !pub_key._is_initialized {
- panic("crypto/ed25519: uninitialized public key")
- }
- if len(dst) != PUBLIC_KEY_SIZE {
- panic("crypto/ed25519: invalid destination size")
- }
+ ensure(pub_key._is_initialized, "crypto/ed25519: uninitialized public key")
+ ensure(len(dst) == PUBLIC_KEY_SIZE, "crypto/ed25519: invalid destination size")
copy(dst, pub_key._b[:])
}
// public_key_equal returns true iff pub_key is equal to other.
public_key_equal :: proc(pub_key, other: ^Public_Key) -> bool {
- if !pub_key._is_initialized || !other._is_initialized {
- panic("crypto/ed25519: uninitialized public key")
- }
+ ensure(pub_key._is_initialized && other._is_initialized, "crypto/ed25519: uninitialized public key")
return crypto.compare_constant_time(pub_key._b[:], other._b[:]) == 1
}
diff --git a/core/crypto/hmac/hmac.odin b/core/crypto/hmac/hmac.odin
index 4813a9938..f74d6492f 100644
--- a/core/crypto/hmac/hmac.odin
+++ b/core/crypto/hmac/hmac.odin
@@ -56,7 +56,7 @@ init :: proc(ctx: ^Context, algorithm: hash.Algorithm, key: []byte) {
// update adds more data to the Context.
update :: proc(ctx: ^Context, data: []byte) {
- assert(ctx._is_initialized)
+ ensure(ctx._is_initialized)
hash.update(&ctx._i_hash, data)
}
@@ -64,13 +64,10 @@ update :: proc(ctx: ^Context, data: []byte) {
// final finalizes the Context, writes the tag to dst, and calls
// reset on the Context.
final :: proc(ctx: ^Context, dst: []byte) {
- assert(ctx._is_initialized)
-
defer (reset(ctx))
- if len(dst) != ctx._tag_sz {
- panic("crypto/hmac: invalid destination tag size")
- }
+ ensure(ctx._is_initialized)
+ ensure(len(dst) == ctx._tag_sz, "crypto/hmac: invalid destination tag size")
hash.final(&ctx._i_hash, dst) // H((k ^ ipad) || text)
@@ -105,14 +102,14 @@ reset :: proc(ctx: ^Context) {
// algorithm returns the Algorithm used by a Context instance.
algorithm :: proc(ctx: ^Context) -> hash.Algorithm {
- assert(ctx._is_initialized)
+ ensure(ctx._is_initialized)
return hash.algorithm(&ctx._i_hash)
}
// tag_size returns the tag size of a Context instance in bytes.
tag_size :: proc(ctx: ^Context) -> int {
- assert(ctx._is_initialized)
+ ensure(ctx._is_initialized)
return ctx._tag_sz
}
diff --git a/core/crypto/kmac/kmac.odin b/core/crypto/kmac/kmac.odin
index e8bf42946..6f58e20a7 100644
--- a/core/crypto/kmac/kmac.odin
+++ b/core/crypto/kmac/kmac.odin
@@ -36,6 +36,7 @@ sum :: proc(sec_strength: int, dst, msg, key, domain_sep: []byte) {
// tag is valid.
verify :: proc(sec_strength: int, tag, msg, key, domain_sep: []byte, allocator := context.temp_allocator) -> bool {
derived_tag := make([]byte, len(tag), allocator)
+ defer(delete(derived_tag))
sum(sec_strength, derived_tag, msg, key, domain_sep)
@@ -59,8 +60,6 @@ init_256 :: proc(ctx: ^Context, key, domain_sep: []byte) {
// update adds more data to the Context.
update :: proc(ctx: ^Context, data: []byte) {
- assert(ctx.is_initialized)
-
shake.write((^shake.Context)(ctx), data)
}
@@ -68,12 +67,9 @@ update :: proc(ctx: ^Context, data: []byte) {
// on the Context. This routine will panic if the dst length is less than
// MIN_TAG_SIZE.
final :: proc(ctx: ^Context, dst: []byte) {
- assert(ctx.is_initialized)
defer reset(ctx)
- if len(dst) < MIN_TAG_SIZE {
- panic("crypto/kmac: invalid KMAC tag_size, too short")
- }
+ ensure(len(dst) >= MIN_TAG_SIZE, "crypto/kmac: invalid KMAC tag_size, too short")
_sha3.final_cshake((^_sha3.Context)(ctx), dst)
}
@@ -103,14 +99,12 @@ _init_kmac :: proc(ctx: ^Context, key, s: []byte, sec_strength: int) {
reset(ctx)
}
- if len(key) < sec_strength / 8 {
- panic("crypto/kmac: invalid KMAC key, too short")
- }
+ ensure(len(key) >= sec_strength / 8, "crypto/kmac: invalid KMAC key, too short")
ctx_ := (^_sha3.Context)(ctx)
_sha3.init_cshake(ctx_, N_KMAC, s, sec_strength)
_sha3.bytepad(ctx_, [][]byte{key}, _sha3.rate_cshake(sec_strength))
}
-@(private)
+@(private, rodata)
N_KMAC := []byte{'K', 'M', 'A', 'C'}
diff --git a/core/crypto/legacy/keccak/keccak.odin b/core/crypto/legacy/keccak/keccak.odin
index 6ca66b7ca..40fc2729f 100644
--- a/core/crypto/legacy/keccak/keccak.odin
+++ b/core/crypto/legacy/keccak/keccak.odin
@@ -40,37 +40,37 @@ BLOCK_SIZE_512 :: _sha3.RATE_512
Context :: distinct _sha3.Context
// init_224 initializes a Context for Keccak-224.
-init_224 :: proc(ctx: ^Context) {
+init_224 :: proc "contextless" (ctx: ^Context) {
ctx.mdlen = DIGEST_SIZE_224
_init(ctx)
}
// init_256 initializes a Context for Keccak-256.
-init_256 :: proc(ctx: ^Context) {
+init_256 :: proc "contextless" (ctx: ^Context) {
ctx.mdlen = DIGEST_SIZE_256
_init(ctx)
}
// init_384 initializes a Context for Keccak-384.
-init_384 :: proc(ctx: ^Context) {
+init_384 :: proc "contextless" (ctx: ^Context) {
ctx.mdlen = DIGEST_SIZE_384
_init(ctx)
}
// init_512 initializes a Context for Keccak-512.
-init_512 :: proc(ctx: ^Context) {
+init_512 :: proc "contextless" (ctx: ^Context) {
ctx.mdlen = DIGEST_SIZE_512
_init(ctx)
}
@(private)
-_init :: proc(ctx: ^Context) {
+_init :: proc "contextless" (ctx: ^Context) {
ctx.dsbyte = _sha3.DS_KECCAK
_sha3.init((^_sha3.Context)(ctx))
}
// update adds more data to the Context.
-update :: proc(ctx: ^Context, data: []byte) {
+update :: proc "contextless" (ctx: ^Context, data: []byte) {
_sha3.update((^_sha3.Context)(ctx), data)
}
@@ -79,17 +79,17 @@ update :: proc(ctx: ^Context, data: []byte) {
//
// Iff finalize_clone is set, final will work on a copy of the Context,
// which is useful for for calculating rolling digests.
-final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
+final :: proc "contextless" (ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
_sha3.final((^_sha3.Context)(ctx), hash, finalize_clone)
}
// clone clones the Context other into ctx.
-clone :: proc(ctx, other: ^Context) {
+clone :: proc "contextless" (ctx, other: ^Context) {
_sha3.clone((^_sha3.Context)(ctx), (^_sha3.Context)(other))
}
// reset sanitizes the Context. The Context must be re-initialized to
// be used again.
-reset :: proc(ctx: ^Context) {
+reset :: proc "contextless" (ctx: ^Context) {
_sha3.reset((^_sha3.Context)(ctx))
}
diff --git a/core/crypto/legacy/md5/md5.odin b/core/crypto/legacy/md5/md5.odin
index 28b47e0b3..050501d98 100644
--- a/core/crypto/legacy/md5/md5.odin
+++ b/core/crypto/legacy/md5/md5.odin
@@ -53,7 +53,7 @@ init :: proc(ctx: ^Context) {
// update adds more data to the Context.
update :: proc(ctx: ^Context, data: []byte) {
- assert(ctx.is_initialized)
+ ensure(ctx.is_initialized)
for i := 0; i < len(data); i += 1 {
ctx.data[ctx.datalen] = data[i]
@@ -72,11 +72,8 @@ update :: proc(ctx: ^Context, data: []byte) {
// Iff finalize_clone is set, final will work on a copy of the Context,
// which is useful for for calculating rolling digests.
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
- assert(ctx.is_initialized)
-
- if len(hash) < DIGEST_SIZE {
- panic("crypto/md5: invalid destination digest size")
- }
+ ensure(ctx.is_initialized)
+ ensure(len(hash) >= DIGEST_SIZE, "crypto/md5: invalid destination digest size")
ctx := ctx
if finalize_clone {
diff --git a/core/crypto/legacy/sha1/sha1.odin b/core/crypto/legacy/sha1/sha1.odin
index 1025ecb5b..5a2b57005 100644
--- a/core/crypto/legacy/sha1/sha1.odin
+++ b/core/crypto/legacy/sha1/sha1.odin
@@ -60,7 +60,7 @@ init :: proc(ctx: ^Context) {
// update adds more data to the Context.
update :: proc(ctx: ^Context, data: []byte) {
- assert(ctx.is_initialized)
+ ensure(ctx.is_initialized)
for i := 0; i < len(data); i += 1 {
ctx.data[ctx.datalen] = data[i]
@@ -79,11 +79,8 @@ update :: proc(ctx: ^Context, data: []byte) {
// Iff finalize_clone is set, final will work on a copy of the Context,
// which is useful for for calculating rolling digests.
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
- assert(ctx.is_initialized)
-
- if len(hash) < DIGEST_SIZE {
- panic("crypto/sha1: invalid destination digest size")
- }
+ ensure(ctx.is_initialized)
+ ensure(len(hash) >= DIGEST_SIZE, "crypto/sha1: invalid destination digest size")
ctx := ctx
if finalize_clone {
diff --git a/core/crypto/poly1305/poly1305.odin b/core/crypto/poly1305/poly1305.odin
index ea0e6c907..3dd915da7 100644
--- a/core/crypto/poly1305/poly1305.odin
+++ b/core/crypto/poly1305/poly1305.odin
@@ -60,9 +60,7 @@ Context :: struct {
// init initializes a Context with the specified key. The key SHOULD be
// unique and MUST be unpredictable for each invocation.
init :: proc(ctx: ^Context, key: []byte) {
- if len(key) != KEY_SIZE {
- panic("crypto/poly1305: invalid key size")
- }
+ ensure(len(key) == KEY_SIZE, "crypto/poly1305: invalid key size")
// r = le_bytes_to_num(key[0..15])
// r = clamp(r) (r &= 0xffffffc0ffffffc0ffffffc0fffffff)
@@ -85,7 +83,7 @@ init :: proc(ctx: ^Context, key: []byte) {
// update adds more data to the Context.
update :: proc(ctx: ^Context, data: []byte) {
- assert(ctx._is_initialized)
+ ensure(ctx._is_initialized)
msg := data
msg_len := len(data)
@@ -124,12 +122,10 @@ update :: proc(ctx: ^Context, data: []byte) {
// final finalizes the Context, writes the tag to dst, and calls
// reset on the Context.
final :: proc(ctx: ^Context, dst: []byte) {
- assert(ctx._is_initialized)
defer reset(ctx)
- if len(dst) != TAG_SIZE {
- panic("poly1305: invalid destination tag size")
- }
+ ensure(ctx._is_initialized)
+ ensure(len(dst) == TAG_SIZE, "poly1305: invalid destination tag size")
// Process remaining block
if ctx._leftover > 0 {
diff --git a/core/crypto/rand_generic.odin b/core/crypto/rand_generic.odin
index ef578f5c0..8266f8ffc 100644
--- a/core/crypto/rand_generic.odin
+++ b/core/crypto/rand_generic.odin
@@ -5,6 +5,7 @@
#+build !netbsd
#+build !darwin
#+build !js
+#+build !wasi
package crypto
HAS_RAND_BYTES :: false
diff --git a/core/crypto/rand_wasi.odin b/core/crypto/rand_wasi.odin
new file mode 100644
index 000000000..9653fb985
--- /dev/null
+++ b/core/crypto/rand_wasi.odin
@@ -0,0 +1,13 @@
+package crypto
+
+import "core:fmt"
+import "core:sys/wasm/wasi"
+
+HAS_RAND_BYTES :: true
+
+@(private)
+_rand_bytes :: proc(dst: []byte) {
+ if err := wasi.random_get(dst); err != nil {
+ fmt.panicf("crypto: wasi.random_get failed: %v", err)
+ }
+}
diff --git a/core/crypto/ristretto255/ristretto255.odin b/core/crypto/ristretto255/ristretto255.odin
index 7b0944e33..20a002900 100644
--- a/core/crypto/ristretto255/ristretto255.odin
+++ b/core/crypto/ristretto255/ristretto255.odin
@@ -16,7 +16,7 @@ ELEMENT_SIZE :: 32
// group element.
WIDE_ELEMENT_SIZE :: 64
-@(private)
+@(private, rodata)
FE_NEG_ONE := field.Tight_Field_Element {
2251799813685228,
2251799813685247,
@@ -24,7 +24,7 @@ FE_NEG_ONE := field.Tight_Field_Element {
2251799813685247,
2251799813685247,
}
-@(private)
+@(private, rodata)
FE_INVSQRT_A_MINUS_D := field.Tight_Field_Element {
278908739862762,
821645201101625,
@@ -32,7 +32,7 @@ FE_INVSQRT_A_MINUS_D := field.Tight_Field_Element {
1777959178193151,
2118520810568447,
}
-@(private)
+@(private, rodata)
FE_ONE_MINUS_D_SQ := field.Tight_Field_Element {
1136626929484150,
1998550399581263,
@@ -40,7 +40,7 @@ FE_ONE_MINUS_D_SQ := field.Tight_Field_Element {
118527312129759,
45110755273534,
}
-@(private)
+@(private, rodata)
FE_D_MINUS_ONE_SQUARED := field.Tight_Field_Element {
1507062230895904,
1572317787530805,
@@ -48,7 +48,7 @@ FE_D_MINUS_ONE_SQUARED := field.Tight_Field_Element {
317374165784489,
1572899562415810,
}
-@(private)
+@(private, rodata)
FE_SQRT_AD_MINUS_ONE := field.Tight_Field_Element {
2241493124984347,
425987919032274,
@@ -76,7 +76,7 @@ ge_clear :: proc "contextless" (ge: ^Group_Element) {
// ge_set sets `ge = a`.
ge_set :: proc(ge, a: ^Group_Element) {
- _ge_assert_initialized([]^Group_Element{a})
+ _ge_ensure_initialized([]^Group_Element{a})
grp.ge_set(&ge._p, &a._p)
ge._is_initialized = true
@@ -199,9 +199,7 @@ ge_set_bytes :: proc "contextless" (ge: ^Group_Element, b: []byte) -> bool {
// ge_set_wide_bytes sets ge to the result of deriving a ristretto255
// group element, from a wide (512-bit) byte string.
ge_set_wide_bytes :: proc(ge: ^Group_Element, b: []byte) {
- if len(b) != WIDE_ELEMENT_SIZE {
- panic("crypto/ristretto255: invalid wide input size")
- }
+ ensure(len(b) == WIDE_ELEMENT_SIZE, "crypto/ristretto255: invalid wide input size")
// The element derivation function on an input string b proceeds as
// follows:
@@ -222,10 +220,8 @@ ge_set_wide_bytes :: proc(ge: ^Group_Element, b: []byte) {
// ge_bytes sets dst to the canonical encoding of ge.
ge_bytes :: proc(ge: ^Group_Element, dst: []byte) {
- _ge_assert_initialized([]^Group_Element{ge})
- if len(dst) != ELEMENT_SIZE {
- panic("crypto/ristretto255: invalid destination size")
- }
+ _ge_ensure_initialized([]^Group_Element{ge})
+ ensure(len(dst) == ELEMENT_SIZE, "crypto/ristretto255: invalid destination size")
x0, y0, z0, t0 := &ge._p.x, &ge._p.y, &ge._p.z, &ge._p.t
@@ -306,7 +302,7 @@ ge_bytes :: proc(ge: ^Group_Element, dst: []byte) {
// ge_add sets `ge = a + b`.
ge_add :: proc(ge, a, b: ^Group_Element) {
- _ge_assert_initialized([]^Group_Element{a, b})
+ _ge_ensure_initialized([]^Group_Element{a, b})
grp.ge_add(&ge._p, &a._p, &b._p)
ge._is_initialized = true
@@ -314,7 +310,7 @@ ge_add :: proc(ge, a, b: ^Group_Element) {
// ge_double sets `ge = a + a`.
ge_double :: proc(ge, a: ^Group_Element) {
- _ge_assert_initialized([]^Group_Element{a})
+ _ge_ensure_initialized([]^Group_Element{a})
grp.ge_double(&ge._p, &a._p)
ge._is_initialized = true
@@ -322,7 +318,7 @@ ge_double :: proc(ge, a: ^Group_Element) {
// ge_negate sets `ge = -a`.
ge_negate :: proc(ge, a: ^Group_Element) {
- _ge_assert_initialized([]^Group_Element{a})
+ _ge_ensure_initialized([]^Group_Element{a})
grp.ge_negate(&ge._p, &a._p)
ge._is_initialized = true
@@ -330,7 +326,7 @@ ge_negate :: proc(ge, a: ^Group_Element) {
// ge_scalarmult sets `ge = A * sc`.
ge_scalarmult :: proc(ge, A: ^Group_Element, sc: ^Scalar) {
- _ge_assert_initialized([]^Group_Element{A})
+ _ge_ensure_initialized([]^Group_Element{A})
grp.ge_scalarmult(&ge._p, &A._p, sc)
ge._is_initialized = true
@@ -344,7 +340,7 @@ ge_scalarmult_generator :: proc "contextless" (ge: ^Group_Element, sc: ^Scalar)
// ge_scalarmult_vartime sets `ge = A * sc` in variable time.
ge_scalarmult_vartime :: proc(ge, A: ^Group_Element, sc: ^Scalar) {
- _ge_assert_initialized([]^Group_Element{A})
+ _ge_ensure_initialized([]^Group_Element{A})
grp.ge_scalarmult_vartime(&ge._p, &A._p, sc)
ge._is_initialized = true
@@ -358,7 +354,7 @@ ge_double_scalarmult_generator_vartime :: proc(
A: ^Group_Element,
b: ^Scalar,
) {
- _ge_assert_initialized([]^Group_Element{A})
+ _ge_ensure_initialized([]^Group_Element{A})
grp.ge_double_scalarmult_basepoint_vartime(&ge._p, a, &A._p, b)
ge._is_initialized = true
@@ -367,7 +363,7 @@ ge_double_scalarmult_generator_vartime :: proc(
// ge_cond_negate sets `ge = a` iff `ctrl == 0` and `ge = -a` iff `ctrl == 1`.
// Behavior for all other values of ctrl are undefined,
ge_cond_negate :: proc(ge, a: ^Group_Element, ctrl: int) {
- _ge_assert_initialized([]^Group_Element{a})
+ _ge_ensure_initialized([]^Group_Element{a})
grp.ge_cond_negate(&ge._p, &a._p, ctrl)
ge._is_initialized = true
@@ -376,7 +372,7 @@ ge_cond_negate :: proc(ge, a: ^Group_Element, ctrl: int) {
// ge_cond_assign sets `ge = ge` iff `ctrl == 0` and `ge = a` iff `ctrl == 1`.
// Behavior for all other values of ctrl are undefined,
ge_cond_assign :: proc(ge, a: ^Group_Element, ctrl: int) {
- _ge_assert_initialized([]^Group_Element{ge, a})
+ _ge_ensure_initialized([]^Group_Element{ge, a})
grp.ge_cond_assign(&ge._p, &a._p, ctrl)
}
@@ -384,7 +380,7 @@ ge_cond_assign :: proc(ge, a: ^Group_Element, ctrl: int) {
// ge_cond_select sets `ge = a` iff `ctrl == 0` and `ge = b` iff `ctrl == 1`.
// Behavior for all other values of ctrl are undefined,
ge_cond_select :: proc(ge, a, b: ^Group_Element, ctrl: int) {
- _ge_assert_initialized([]^Group_Element{a, b})
+ _ge_ensure_initialized([]^Group_Element{a, b})
grp.ge_cond_select(&ge._p, &a._p, &b._p, ctrl)
ge._is_initialized = true
@@ -393,7 +389,7 @@ ge_cond_select :: proc(ge, a, b: ^Group_Element, ctrl: int) {
// ge_equal returns 1 iff `a == b`, and 0 otherwise.
@(require_results)
ge_equal :: proc(a, b: ^Group_Element) -> int {
- _ge_assert_initialized([]^Group_Element{a, b})
+ _ge_ensure_initialized([]^Group_Element{a, b})
// CT_EQ(x1 * y2, y1 * x2) | CT_EQ(y1 * y2, x1 * x2)
ax_by, ay_bx, ay_by, ax_bx: field.Tight_Field_Element = ---, ---, ---, ---
@@ -501,10 +497,8 @@ ge_map :: proc "contextless" (ge: ^Group_Element, b: []byte) {
}
@(private)
-_ge_assert_initialized :: proc(ges: []^Group_Element) {
+_ge_ensure_initialized :: proc(ges: []^Group_Element) {
for ge in ges {
- if !ge._is_initialized {
- panic("crypto/ristretto255: uninitialized group element")
- }
+ ensure(ge._is_initialized, "crypto/ristretto255: uninitialized group element")
}
}
diff --git a/core/crypto/ristretto255/ristretto255_scalar.odin b/core/crypto/ristretto255/ristretto255_scalar.odin
index 1ecb490e0..75844b3f4 100644
--- a/core/crypto/ristretto255/ristretto255_scalar.odin
+++ b/core/crypto/ristretto255/ristretto255_scalar.odin
@@ -42,9 +42,7 @@ sc_set_bytes :: proc(sc: ^Scalar, b: []byte) -> bool {
// scalar, from a wide (512-bit) byte string by interpreting b as a
// little-endian value, and reducing it mod the group order.
sc_set_bytes_wide :: proc(sc: ^Scalar, b: []byte) {
- if len(b) != WIDE_SCALAR_SIZE {
- panic("crypto/ristretto255: invalid wide input size")
- }
+ ensure(len(b) == WIDE_SCALAR_SIZE, "crypto/ristretto255: invalid wide input size")
b_ := (^[WIDE_SCALAR_SIZE]byte)(raw_data(b))
grp.sc_set_bytes_wide(sc, b_)
@@ -52,9 +50,7 @@ sc_set_bytes_wide :: proc(sc: ^Scalar, b: []byte) {
// sc_bytes sets dst to the canonical encoding of sc.
sc_bytes :: proc(sc: ^Scalar, dst: []byte) {
- if len(dst) != SCALAR_SIZE {
- panic("crypto/ristretto255: invalid destination size")
- }
+ ensure(len(dst) == SCALAR_SIZE, "crypto/ristretto255: invalid destination size")
grp.sc_bytes(dst, sc)
}
diff --git a/core/crypto/sha2/sha2.odin b/core/crypto/sha2/sha2.odin
index 4230851ab..bf9b81601 100644
--- a/core/crypto/sha2/sha2.odin
+++ b/core/crypto/sha2/sha2.odin
@@ -15,9 +15,9 @@ package sha2
zhibog, dotbmp: Initial implementation.
*/
-import "core:encoding/endian"
+@(require) import "core:encoding/endian"
import "core:math/bits"
-import "core:mem"
+@(require) import "core:mem"
// DIGEST_SIZE_224 is the SHA-224 digest size in bytes.
DIGEST_SIZE_224 :: 28
@@ -158,7 +158,7 @@ _init :: proc(ctx: ^$T) {
// update adds more data to the Context.
update :: proc(ctx: ^$T, data: []byte) {
- assert(ctx.is_initialized)
+ ensure(ctx.is_initialized)
when T == Context_256 {
CURR_BLOCK_SIZE :: BLOCK_SIZE_256
@@ -194,11 +194,8 @@ update :: proc(ctx: ^$T, data: []byte) {
// Iff finalize_clone is set, final will work on a copy of the Context,
// which is useful for for calculating rolling digests.
final :: proc(ctx: ^$T, hash: []byte, finalize_clone: bool = false) {
- assert(ctx.is_initialized)
-
- if len(hash) * 8 < ctx.md_bits {
- panic("crypto/sha2: invalid destination digest size")
- }
+ ensure(ctx.is_initialized)
+ ensure(len(hash) * 8 >= ctx.md_bits, "crypto/sha2: invalid destination digest size")
ctx := ctx
if finalize_clone {
@@ -238,7 +235,7 @@ final :: proc(ctx: ^$T, hash: []byte, finalize_clone: bool = false) {
endian.unchecked_put_u64be(pad[8:], length_lo)
update(ctx, pad[0:16])
}
- assert(ctx.bitlength == 0)
+ assert(ctx.bitlength == 0) // Check for bugs
when T == Context_256 {
for i := 0; i < ctx.md_bits / 32; i += 1 {
@@ -270,8 +267,8 @@ reset :: proc(ctx: ^$T) {
SHA2 implementation
*/
-@(private)
-sha256_k := [64]u32 {
+@(private, rodata)
+SHA256_K := [64]u32 {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
@@ -290,8 +287,8 @@ sha256_k := [64]u32 {
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
}
-@(private)
-sha512_k := [80]u64 {
+@(private, rodata)
+SHA512_K := [80]u64 {
0x428a2f98d728ae22, 0x7137449123ef65cd,
0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
0x3956c25bf348b538, 0x59f111f1b605d019,
@@ -334,6 +331,11 @@ sha512_k := [80]u64 {
0x5fcb6fab3ad6faec, 0x6c44198c4a475817,
}
+@(private)
+SHA256_ROUNDS :: 64
+@(private)
+SHA512_ROUNDS :: 80
+
@(private)
SHA256_CH :: #force_inline proc "contextless" (x, y, z: u32) -> u32 {
return (x & y) ~ (~x & z)
@@ -395,22 +397,29 @@ SHA512_F4 :: #force_inline proc "contextless" (x: u64) -> u64 {
}
@(private)
-sha2_transf :: proc "contextless" (ctx: ^$T, data: []byte) {
+sha2_transf :: proc "contextless" (ctx: ^$T, data: []byte) #no_bounds_check {
when T == Context_256 {
- w: [64]u32
+ if is_hardware_accelerated_256() {
+ sha256_transf_hw(ctx, data)
+ return
+ }
+
+ w: [SHA256_ROUNDS]u32
wv: [8]u32
t1, t2: u32
+
CURR_BLOCK_SIZE :: BLOCK_SIZE_256
} else when T == Context_512 {
- w: [80]u64
+ w: [SHA512_ROUNDS]u64
wv: [8]u64
t1, t2: u64
+
CURR_BLOCK_SIZE :: BLOCK_SIZE_512
}
data := data
for len(data) >= CURR_BLOCK_SIZE {
- for i := 0; i < 16; i += 1 {
+ for i in 0 ..< 16 {
when T == Context_256 {
w[i] = endian.unchecked_get_u32be(data[i * 4:])
} else when T == Context_512 {
@@ -419,22 +428,22 @@ sha2_transf :: proc "contextless" (ctx: ^$T, data: []byte) {
}
when T == Context_256 {
- for i := 16; i < 64; i += 1 {
+ for i in 16 ..< SHA256_ROUNDS {
w[i] = SHA256_F4(w[i - 2]) + w[i - 7] + SHA256_F3(w[i - 15]) + w[i - 16]
}
} else when T == Context_512 {
- for i := 16; i < 80; i += 1 {
+ for i in 16 ..< SHA512_ROUNDS {
w[i] = SHA512_F4(w[i - 2]) + w[i - 7] + SHA512_F3(w[i - 15]) + w[i - 16]
}
}
- for i := 0; i < 8; i += 1 {
+ for i in 0 ..< 8 {
wv[i] = ctx.h[i]
}
when T == Context_256 {
- for i := 0; i < 64; i += 1 {
- t1 = wv[7] + SHA256_F2(wv[4]) + SHA256_CH(wv[4], wv[5], wv[6]) + sha256_k[i] + w[i]
+ for i in 0 ..< SHA256_ROUNDS {
+ t1 = wv[7] + SHA256_F2(wv[4]) + SHA256_CH(wv[4], wv[5], wv[6]) + SHA256_K[i] + w[i]
t2 = SHA256_F1(wv[0]) + SHA256_MAJ(wv[0], wv[1], wv[2])
wv[7] = wv[6]
wv[6] = wv[5]
@@ -446,8 +455,8 @@ sha2_transf :: proc "contextless" (ctx: ^$T, data: []byte) {
wv[0] = t1 + t2
}
} else when T == Context_512 {
- for i := 0; i < 80; i += 1 {
- t1 = wv[7] + SHA512_F2(wv[4]) + SHA512_CH(wv[4], wv[5], wv[6]) + sha512_k[i] + w[i]
+ for i in 0 ..< SHA512_ROUNDS {
+ t1 = wv[7] + SHA512_F2(wv[4]) + SHA512_CH(wv[4], wv[5], wv[6]) + SHA512_K[i] + w[i]
t2 = SHA512_F1(wv[0]) + SHA512_MAJ(wv[0], wv[1], wv[2])
wv[7] = wv[6]
wv[6] = wv[5]
@@ -460,7 +469,7 @@ sha2_transf :: proc "contextless" (ctx: ^$T, data: []byte) {
}
}
- for i := 0; i < 8; i += 1 {
+ for i in 0 ..< 8 {
ctx.h[i] += wv[i]
}
diff --git a/core/crypto/sha2/sha2_impl_hw_gen.odin b/core/crypto/sha2/sha2_impl_hw_gen.odin
new file mode 100644
index 000000000..85c7f8b28
--- /dev/null
+++ b/core/crypto/sha2/sha2_impl_hw_gen.odin
@@ -0,0 +1,15 @@
+#+build !amd64
+package sha2
+
+@(private = "file")
+ERR_HW_NOT_SUPPORTED :: "crypto/sha2: hardware implementation unsupported"
+
+// is_hardware_accelerated_256 returns true iff hardware accelerated
+// SHA-224/SHA-256 is supported.
+is_hardware_accelerated_256 :: proc "contextless" () -> bool {
+ return false
+}
+
+sha256_transf_hw :: proc "contextless" (ctx: ^Context_256, data: []byte) {
+ panic_contextless(ERR_HW_NOT_SUPPORTED)
+}
diff --git a/core/crypto/sha2/sha2_impl_hw_intel.odin b/core/crypto/sha2/sha2_impl_hw_intel.odin
new file mode 100644
index 000000000..f16f353df
--- /dev/null
+++ b/core/crypto/sha2/sha2_impl_hw_intel.odin
@@ -0,0 +1,260 @@
+#+build amd64
+package sha2
+
+// Based on the public domain code by Jeffrey Walton, though
+// realistically, there only is one sensible way to write this
+// and Intel's whitepaper covers it.
+//
+// See: https://github.com/noloader/SHA-Intrinsics
+
+import "base:intrinsics"
+import "core:simd"
+import "core:simd/x86"
+import "core:sys/info"
+
+@(private = "file")
+MASK :: x86.__m128i{0x0405060700010203, 0x0c0d0e0f08090a0b}
+
+@(private = "file")
+K_0 :: simd.u64x2{0x71374491428a2f98, 0xe9b5dba5b5c0fbcf}
+@(private = "file")
+K_1 :: simd.u64x2{0x59f111f13956c25b, 0xab1c5ed5923f82a4}
+@(private = "file")
+K_2 :: simd.u64x2{0x12835b01d807aa98, 0x550c7dc3243185be}
+@(private = "file")
+K_3 :: simd.u64x2{0x80deb1fe72be5d74, 0xc19bf1749bdc06a7}
+@(private = "file")
+K_4 :: simd.u64x2{0xefbe4786e49b69c1, 0x240ca1cc0fc19dc6}
+@(private = "file")
+K_5 :: simd.u64x2{0x4a7484aa2de92c6f, 0x76f988da5cb0a9dc}
+@(private = "file")
+K_6 :: simd.u64x2{0xa831c66d983e5152, 0xbf597fc7b00327c8}
+@(private = "file")
+K_7 :: simd.u64x2{0xd5a79147c6e00bf3, 0x1429296706ca6351}
+@(private = "file")
+K_8 :: simd.u64x2{0x2e1b213827b70a85, 0x53380d134d2c6dfc}
+@(private = "file")
+K_9 :: simd.u64x2{0x766a0abb650a7354, 0x92722c8581c2c92e}
+@(private = "file")
+K_10 :: simd.u64x2{0xa81a664ba2bfe8a1, 0xc76c51a3c24b8b70}
+@(private = "file")
+K_11 :: simd.u64x2{0xd6990624d192e819, 0x106aa070f40e3585}
+@(private = "file")
+K_12 :: simd.u64x2{0x1e376c0819a4c116, 0x34b0bcb52748774c}
+@(private = "file")
+K_13 :: simd.u64x2{0x4ed8aa4a391c0cb3, 0x682e6ff35b9cca4f}
+@(private = "file")
+K_14 :: simd.u64x2{0x78a5636f748f82ee, 0x8cc7020884c87814}
+@(private = "file")
+K_15 :: simd.u64x2{0xa4506ceb90befffa, 0xc67178f2bef9a3f7}
+
+
+// is_hardware_accelerated_256 returns true iff hardware accelerated
+// SHA-224/SHA-256 is supported.
+is_hardware_accelerated_256 :: proc "contextless" () -> bool {
+ features, ok := info.cpu_features.?
+ if !ok {
+ return false
+ }
+
+ req_features :: info.CPU_Features{
+ .sse2,
+ .ssse3,
+ .sse41,
+ .sha,
+ }
+ return features >= req_features
+}
+
+@(private, enable_target_feature="sse2,ssse3,sse4.1,sha")
+sha256_transf_hw :: proc "contextless" (ctx: ^Context_256, data: []byte) #no_bounds_check {
+ // Load the state
+ tmp := intrinsics.unaligned_load((^x86.__m128i)(&ctx.h[0]))
+ state_1 := intrinsics.unaligned_load((^x86.__m128i)(&ctx.h[4]))
+
+ tmp = x86._mm_shuffle_epi32(tmp, 0xb1) // CDAB
+ state_1 = x86._mm_shuffle_epi32(state_1, 0x1b) // EFGH
+ state_0 := x86._mm_alignr_epi8(tmp, state_1, 8) // ABEF
+ // state_1 = x86._mm_blend_epi16(state_1, tmp, 0xf0) // CDGH
+ state_1 = kludge_mm_blend_epi16_0xf0(state_1, tmp)
+
+ data := data
+ for len(data) >= BLOCK_SIZE_256 {
+ state_0_save, state_1_save := state_0, state_1
+
+ // Rounds 0-3
+ msg := intrinsics.unaligned_load((^x86.__m128i)(raw_data(data)))
+ msg_0 := x86._mm_shuffle_epi8(msg, MASK)
+ msg = x86._mm_add_epi32(msg_0, x86.__m128i(K_0))
+ state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
+ msg = x86._mm_shuffle_epi32(msg, 0xe)
+ state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
+
+ // Rounds 4-7
+ msg_1 := intrinsics.unaligned_load((^x86.__m128i)(raw_data(data[16:])))
+ msg_1 = x86._mm_shuffle_epi8(msg_1, MASK)
+ msg = x86._mm_add_epi32(msg_1, x86.__m128i(K_1))
+ state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
+ msg = x86._mm_shuffle_epi32(msg, 0xe)
+ state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
+ msg_0 = x86._mm_sha256msg1_epu32(msg_0, msg_1)
+
+ // Rounds 8-11
+ msg_2 := intrinsics.unaligned_load((^x86.__m128i)(raw_data(data[32:])))
+ msg_2 = x86._mm_shuffle_epi8(msg_2, MASK)
+ msg = x86._mm_add_epi32(msg_2, x86.__m128i(K_2))
+ state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
+ msg = x86._mm_shuffle_epi32(msg, 0xe)
+ state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
+ msg_1 = x86._mm_sha256msg1_epu32(msg_1, msg_2)
+
+ // Rounds 12-15
+ msg_3 := intrinsics.unaligned_load((^x86.__m128i)(raw_data(data[48:])))
+ msg_3 = x86._mm_shuffle_epi8(msg_3, MASK)
+ msg = x86._mm_add_epi32(msg_3, x86.__m128i(K_3))
+ state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
+ tmp = x86._mm_alignr_epi8(msg_3, msg_2, 4)
+ msg_0 = x86._mm_add_epi32(msg_0, tmp)
+ msg_0 = x86._mm_sha256msg2_epu32(msg_0, msg_3)
+ msg = x86._mm_shuffle_epi32(msg, 0x0e)
+ state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
+ msg_2 = x86._mm_sha256msg1_epu32(msg_2, msg_3)
+
+ // Rounds 16-19
+ msg = x86._mm_add_epi32(msg_0, x86.__m128i(K_4))
+ state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
+ tmp = x86._mm_alignr_epi8(msg_0, msg_3, 4)
+ msg_1 = x86._mm_add_epi32(msg_1, tmp)
+ msg_1 = x86._mm_sha256msg2_epu32(msg_1, msg_0)
+ msg = x86._mm_shuffle_epi32(msg, 0x0e)
+ state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
+ msg_3 = x86._mm_sha256msg1_epu32(msg_3, msg_0)
+
+ // Rounds 20-23
+ msg = x86._mm_add_epi32(msg_1, x86.__m128i(K_5))
+ state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
+ tmp = x86._mm_alignr_epi8(msg_1, msg_0, 4)
+ msg_2 = x86._mm_add_epi32(msg_2, tmp)
+ msg_2 = x86._mm_sha256msg2_epu32(msg_2, msg_1)
+ msg = x86._mm_shuffle_epi32(msg, 0x0e)
+ state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
+ msg_0 = x86._mm_sha256msg1_epu32(msg_0, msg_1)
+
+ // Rounds 24-27
+ msg = x86._mm_add_epi32(msg_2, x86.__m128i(K_6))
+ state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
+ tmp = x86._mm_alignr_epi8(msg_2, msg_1, 4)
+ msg_3 = x86._mm_add_epi32(msg_3, tmp)
+ msg_3 = x86._mm_sha256msg2_epu32(msg_3, msg_2)
+ msg = x86._mm_shuffle_epi32(msg, 0x0e)
+ state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
+ msg_1 = x86._mm_sha256msg1_epu32(msg_1, msg_2)
+
+ // Rounds 28-31
+ msg = x86._mm_add_epi32(msg_3, x86.__m128i(K_7))
+ state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
+ tmp = x86._mm_alignr_epi8(msg_3, msg_2, 4)
+ msg_0 = x86._mm_add_epi32(msg_0, tmp)
+ msg_0 = x86._mm_sha256msg2_epu32(msg_0, msg_3)
+ msg = x86._mm_shuffle_epi32(msg, 0x0e)
+ state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
+ msg_2 = x86._mm_sha256msg1_epu32(msg_2, msg_3)
+
+ // Rounds 32-35
+ msg = x86._mm_add_epi32(msg_0, x86.__m128i(K_8))
+ state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
+ tmp = x86._mm_alignr_epi8(msg_0, msg_3, 4)
+ msg_1 = x86._mm_add_epi32(msg_1, tmp)
+ msg_1 = x86._mm_sha256msg2_epu32(msg_1, msg_0)
+ msg = x86._mm_shuffle_epi32(msg, 0x0e)
+ state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
+ msg_3 = x86._mm_sha256msg1_epu32(msg_3, msg_0)
+
+ // Rounds 36-39
+ msg = x86._mm_add_epi32(msg_1, x86.__m128i(K_9))
+ state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
+ tmp = x86._mm_alignr_epi8(msg_1, msg_0, 4)
+ msg_2 = x86._mm_add_epi32(msg_2, tmp)
+ msg_2 = x86._mm_sha256msg2_epu32(msg_2, msg_1)
+ msg = x86._mm_shuffle_epi32(msg, 0x0e)
+ state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
+ msg_0 = x86._mm_sha256msg1_epu32(msg_0, msg_1)
+
+ // Rounds 40-43
+ msg = x86._mm_add_epi32(msg_2, x86.__m128i(K_10))
+ state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
+ tmp = x86._mm_alignr_epi8(msg_2, msg_1, 4)
+ msg_3 = x86._mm_add_epi32(msg_3, tmp)
+ msg_3 = x86._mm_sha256msg2_epu32(msg_3, msg_2)
+ msg = x86._mm_shuffle_epi32(msg, 0x0e)
+ state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
+ msg_1 = x86._mm_sha256msg1_epu32(msg_1, msg_2)
+
+ // Rounds 44-47
+ msg = x86._mm_add_epi32(msg_3, x86.__m128i(K_11))
+ state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
+ tmp = x86._mm_alignr_epi8(msg_3, msg_2, 4)
+ msg_0 = x86._mm_add_epi32(msg_0, tmp)
+ msg_0 = x86._mm_sha256msg2_epu32(msg_0, msg_3)
+ msg = x86._mm_shuffle_epi32(msg, 0x0e)
+ state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
+ msg_2 = x86._mm_sha256msg1_epu32(msg_2, msg_3)
+
+ // Rounds 48-51
+ msg = x86._mm_add_epi32(msg_0, x86.__m128i(K_12))
+ state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
+ tmp = x86._mm_alignr_epi8(msg_0, msg_3, 4)
+ msg_1 = x86._mm_add_epi32(msg_1, tmp)
+ msg_1 = x86._mm_sha256msg2_epu32(msg_1, msg_0)
+ msg = x86._mm_shuffle_epi32(msg, 0x0e)
+ state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
+ msg_3 = x86._mm_sha256msg1_epu32(msg_3, msg_0)
+
+ // Rounds 52-55
+ msg = x86._mm_add_epi32(msg_1, x86.__m128i(K_13))
+ state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
+ tmp = x86._mm_alignr_epi8(msg_1, msg_0, 4)
+ msg_2 = x86._mm_add_epi32(msg_2, tmp)
+ msg_2 = x86._mm_sha256msg2_epu32(msg_2, msg_1)
+ msg = x86._mm_shuffle_epi32(msg, 0x0e)
+ state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
+
+ /* Rounds 56-59 */
+ msg = x86._mm_add_epi32(msg_2, x86.__m128i(K_14))
+ state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
+ tmp = x86._mm_alignr_epi8(msg_2, msg_1, 4)
+ msg_3 = x86._mm_add_epi32(msg_3, tmp)
+ msg_3 = x86._mm_sha256msg2_epu32(msg_3, msg_2)
+ msg = x86._mm_shuffle_epi32(msg, 0x0e)
+ state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
+
+ // Rounds 60-63
+ msg = x86._mm_add_epi32(msg_3, x86.__m128i(K_15))
+ state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
+ msg = x86._mm_shuffle_epi32(msg, 0x0e)
+ state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
+
+ state_0 = x86._mm_add_epi32(state_0, state_0_save)
+ state_1 = x86._mm_add_epi32(state_1, state_1_save)
+
+ data = data[BLOCK_SIZE_256:]
+ }
+
+ // Write back the updated state
+ tmp = x86._mm_shuffle_epi32(state_0, 0x1b) // FEBA
+ state_1 = x86._mm_shuffle_epi32(state_1, 0xb1) // DCHG
+ // state_0 = x86._mm_blend_epi16(tmp, state_1, 0xf0) // DCBA
+ state_0 = kludge_mm_blend_epi16_0xf0(tmp, state_1)
+ state_1 = x86._mm_alignr_epi8(state_1, tmp, 8) // ABEF
+
+ intrinsics.unaligned_store((^x86.__m128i)(&ctx.h[0]), state_0)
+ intrinsics.unaligned_store((^x86.__m128i)(&ctx.h[4]), state_1)
+}
+
+@(private = "file")
+kludge_mm_blend_epi16_0xf0 :: #force_inline proc "contextless"(a, b: x86.__m128i) -> x86.__m128i {
+ // HACK HACK HACK: LLVM got rid of `llvm.x86.sse41.pblendw`.
+ a_ := simd.to_array(a)
+ b_ := simd.to_array(b)
+ return x86.__m128i{a_[0], b_[1]}
+}
diff --git a/core/crypto/siphash/siphash.odin b/core/crypto/siphash/siphash.odin
index c145ab3f0..f9fe50cb0 100644
--- a/core/crypto/siphash/siphash.odin
+++ b/core/crypto/siphash/siphash.odin
@@ -219,18 +219,14 @@ verify_4_8 :: proc {
*/
init :: proc(ctx: ^Context, key: []byte, c_rounds, d_rounds: int) {
- if len(key) != KEY_SIZE {
- panic("crypto/siphash; invalid key size")
- }
+ ensure(len(key) == KEY_SIZE,"crypto/siphash; invalid key size")
ctx.c_rounds = c_rounds
ctx.d_rounds = d_rounds
is_valid_setting :=
(ctx.c_rounds == 1 && ctx.d_rounds == 3) ||
(ctx.c_rounds == 2 && ctx.d_rounds == 4) ||
(ctx.c_rounds == 4 && ctx.d_rounds == 8)
- if !is_valid_setting {
- panic("crypto/siphash: incorrect rounds set up")
- }
+ ensure(is_valid_setting, "crypto/siphash: incorrect rounds set up")
ctx.k0 = endian.unchecked_get_u64le(key[:8])
ctx.k1 = endian.unchecked_get_u64le(key[8:])
ctx.v0 = 0x736f6d6570736575 ~ ctx.k0
@@ -245,7 +241,7 @@ init :: proc(ctx: ^Context, key: []byte, c_rounds, d_rounds: int) {
}
update :: proc(ctx: ^Context, data: []byte) {
- assert(ctx.is_initialized, "crypto/siphash: context is not initialized")
+ ensure(ctx.is_initialized)
data := data
ctx.total_length += len(data)
@@ -269,7 +265,7 @@ update :: proc(ctx: ^Context, data: []byte) {
}
final :: proc(ctx: ^Context, dst: ^u64) {
- assert(ctx.is_initialized, "crypto/siphash: context is not initialized")
+ ensure(ctx.is_initialized)
tmp: [BLOCK_SIZE]byte
copy(tmp[:], ctx.buf[:ctx.last_block])
@@ -336,9 +332,8 @@ _get_byte :: #force_inline proc "contextless" (byte_num: byte, into: u64) -> byt
@(private)
_collect_output :: #force_inline proc(dst: []byte, hash: u64) {
- if len(dst) < DIGEST_SIZE {
- panic("crypto/siphash: invalid tag size")
- }
+ ensure(len(dst) >= DIGEST_SIZE, "crypto/siphash: invalid tag size")
+
dst[0] = _get_byte(7, hash)
dst[1] = _get_byte(6, hash)
dst[2] = _get_byte(5, hash)
diff --git a/core/crypto/sm3/sm3.odin b/core/crypto/sm3/sm3.odin
index f910d735b..6487c5e8c 100644
--- a/core/crypto/sm3/sm3.odin
+++ b/core/crypto/sm3/sm3.odin
@@ -53,7 +53,7 @@ init :: proc(ctx: ^Context) {
// update adds more data to the Context.
update :: proc(ctx: ^Context, data: []byte) {
- assert(ctx.is_initialized)
+ ensure(ctx.is_initialized)
data := data
ctx.length += u64(len(data))
@@ -83,11 +83,8 @@ update :: proc(ctx: ^Context, data: []byte) {
// Iff finalize_clone is set, final will work on a copy of the Context,
// which is useful for for calculating rolling digests.
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
- assert(ctx.is_initialized)
-
- if len(hash) < DIGEST_SIZE {
- panic("crypto/sm3: invalid destination digest size")
- }
+ ensure(ctx.is_initialized)
+ ensure(len(hash) >= DIGEST_SIZE, "crypto/sm3: invalid destination digest size")
ctx := ctx
if finalize_clone {
@@ -110,7 +107,7 @@ final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
length <<= 3
endian.unchecked_put_u64be(pad[:], length)
update(ctx, pad[0:8])
- assert(ctx.bitlength == 0)
+ assert(ctx.bitlength == 0) // Check for bugs
for i := 0; i < DIGEST_SIZE / 4; i += 1 {
endian.unchecked_put_u32be(hash[i * 4:], ctx.state[i])
@@ -136,7 +133,7 @@ reset :: proc(ctx: ^Context) {
SM3 implementation
*/
-@(private)
+@(private, rodata)
IV := [8]u32 {
0x7380166f, 0x4914b2b9, 0x172442d7, 0xda8a0600,
0xa96f30bc, 0x163138aa, 0xe38dee4d, 0xb0fb0e4e,
diff --git a/core/crypto/x25519/x25519.odin b/core/crypto/x25519/x25519.odin
index 412a767b8..6805c3ff8 100644
--- a/core/crypto/x25519/x25519.odin
+++ b/core/crypto/x25519/x25519.odin
@@ -15,7 +15,7 @@ SCALAR_SIZE :: 32
// POINT_SIZE is the size of a X25519 point (public key/shared secret) in bytes.
POINT_SIZE :: 32
-@(private)
+@(private, rodata)
_BASE_POINT: [32]byte = {9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
@(private)
@@ -101,15 +101,9 @@ _scalarmult :: proc "contextless" (out, scalar, point: ^[32]byte) {
// scalarmult "multiplies" the provided scalar and point, and writes the
// resulting point to dst.
scalarmult :: proc(dst, scalar, point: []byte) {
- if len(scalar) != SCALAR_SIZE {
- panic("crypto/x25519: invalid scalar size")
- }
- if len(point) != POINT_SIZE {
- panic("crypto/x25519: invalid point size")
- }
- if len(dst) != POINT_SIZE {
- panic("crypto/x25519: invalid destination point size")
- }
+ ensure(len(scalar) == SCALAR_SIZE, "crypto/x25519: invalid scalar size")
+ ensure(len(point) == POINT_SIZE, "crypto/x25519: invalid point size")
+ ensure(len(dst) == POINT_SIZE, "crypto/x25519: invalid destination point size")
// "clamp" the scalar
e: [32]byte = ---
diff --git a/core/crypto/x448/x448.odin b/core/crypto/x448/x448.odin
new file mode 100644
index 000000000..43c5d25e0
--- /dev/null
+++ b/core/crypto/x448/x448.odin
@@ -0,0 +1,155 @@
+/*
+package x448 implements the X448 (aka curve448) Elliptic-Curve
+Diffie-Hellman key exchange protocol.
+
+See:
+- [[ https://www.rfc-editor.org/rfc/rfc7748 ]]
+*/
+package x448
+
+import field "core:crypto/_fiat/field_curve448"
+import "core:mem"
+
+// SCALAR_SIZE is the size of a X448 scalar (private key) in bytes.
+SCALAR_SIZE :: 56
+// POINT_SIZE is the size of a X448 point (public key/shared secret) in bytes.
+POINT_SIZE :: 56
+
+@(private, rodata)
+_BASE_POINT: [56]byte = {
+ 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+}
+
+@(private)
+_scalar_bit :: #force_inline proc "contextless" (s: ^[56]byte, i: int) -> u8 {
+ if i < 0 {
+ return 0
+ }
+ return (s[i >> 3] >> uint(i & 7)) & 1
+}
+
+@(private)
+_scalarmult :: proc "contextless" (out, scalar, point: ^[56]byte) {
+ // Montgomery pseudo-multiplication, using the RFC 7748 formula.
+ t1, t2: field.Loose_Field_Element = ---, ---
+
+ // x_1 = u
+ // x_2 = 1
+ // z_2 = 0
+ // x_3 = u
+ // z_3 = 1
+ x1: field.Tight_Field_Element = ---
+ field.fe_from_bytes(&x1, point)
+
+ x2, x3, z2, z3: field.Tight_Field_Element = ---, ---, ---, ---
+ field.fe_one(&x2)
+ field.fe_zero(&z2)
+ field.fe_set(&x3, &x1)
+ field.fe_one(&z3)
+
+ // swap = 0
+ swap: int
+
+ // For t = bits-1 down to 0:a
+ for t := 448 - 1; t >= 0; t -= 1 {
+ // k_t = (k >> t) & 1
+ k_t := int(_scalar_bit(scalar, t))
+ // swap ^= k_t
+ swap ~= k_t
+ // Conditional swap; see text below.
+ // (x_2, x_3) = cswap(swap, x_2, x_3)
+ field.fe_cond_swap(&x2, &x3, swap)
+ // (z_2, z_3) = cswap(swap, z_2, z_3)
+ field.fe_cond_swap(&z2, &z3, swap)
+ // swap = k_t
+ swap = k_t
+
+ // Note: This deliberately omits reductions after add/sub operations
+ // if the result is only ever used as the input to a mul/square since
+ // the implementations of those can deal with non-reduced inputs.
+ //
+ // fe_tighten_cast is only used to store a fully reduced
+ // output in a Loose_Field_Element, or to provide such a
+ // Loose_Field_Element as a Tight_Field_Element argument.
+
+ // A = x_2 + z_2
+ field.fe_add(&t1, &x2, &z2)
+ // B = x_2 - z_2
+ field.fe_sub(&t2, &x2, &z2)
+ // D = x_3 - z_3
+ field.fe_sub(field.fe_relax_cast(&z2), &x3, &z3) // (z2 unreduced)
+ // DA = D * A
+ field.fe_carry_mul(&x2, field.fe_relax_cast(&z2), &t1)
+ // C = x_3 + z_3
+ field.fe_add(field.fe_relax_cast(&z3), &x3, &z3) // (z3 unreduced)
+ // CB = C * B
+ field.fe_carry_mul(&x3, &t2, field.fe_relax_cast(&z3))
+ // z_3 = x_1 * (DA - CB)^2
+ field.fe_sub(field.fe_relax_cast(&z3), &x2, &x3) // (z3 unreduced)
+ field.fe_carry_square(&z3, field.fe_relax_cast(&z3))
+ field.fe_carry_mul(&z3, field.fe_relax_cast(&x1), field.fe_relax_cast(&z3))
+ // x_3 = (DA + CB)^2
+ field.fe_add(field.fe_relax_cast(&z2), &x2, &x3) // (z2 unreduced)
+ field.fe_carry_square(&x3, field.fe_relax_cast(&z2))
+
+ // AA = A^2
+ field.fe_carry_square(&z2, &t1)
+ // BB = B^2
+ field.fe_carry_square(field.fe_tighten_cast(&t1), &t2) // (t1 reduced)
+ // x_2 = AA * BB
+ field.fe_carry_mul(&x2, field.fe_relax_cast(&z2), &t1)
+ // E = AA - BB
+ field.fe_sub(&t2, &z2, field.fe_tighten_cast(&t1)) // (t1 (input) is reduced)
+ // z_2 = E * (AA + a24 * E)
+ field.fe_carry_mul_small(field.fe_tighten_cast(&t1), &t2, 39081) // (t1 reduced)
+ field.fe_add(&t1, &z2, field.fe_tighten_cast(&t1)) // (t1 (input) is reduced)
+ field.fe_carry_mul(&z2, &t2, &t1)
+ }
+
+ // Conditional swap; see text below.
+ // (x_2, x_3) = cswap(swap, x_2, x_3)
+ field.fe_cond_swap(&x2, &x3, swap)
+ // (z_2, z_3) = cswap(swap, z_2, z_3)
+ field.fe_cond_swap(&z2, &z3, swap)
+
+ // Return x_2 * (z_2^(p - 2))
+ field.fe_carry_inv(&z2, field.fe_relax_cast(&z2))
+ field.fe_carry_mul(&x2, field.fe_relax_cast(&x2), field.fe_relax_cast(&z2))
+ field.fe_to_bytes(out, &x2)
+
+ field.fe_clear_vec([]^field.Tight_Field_Element{&x1, &x2, &x3, &z2, &z3})
+ field.fe_clear_vec([]^field.Loose_Field_Element{&t1, &t2})
+}
+
+// scalarmult "multiplies" the provided scalar and point, and writes the
+// resulting point to dst.
+scalarmult :: proc(dst, scalar, point: []byte) {
+ ensure(len(scalar) == SCALAR_SIZE, "crypto/x448: invalid scalar size")
+ ensure(len(point) == POINT_SIZE, "crypto/x448: invalid point size")
+ ensure(len(dst) == POINT_SIZE, "crypto/x448: invalid destination point size")
+
+ // "clamp" the scalar
+ e: [56]byte = ---
+ copy_slice(e[:], scalar)
+ e[0] &= 252
+ e[55] |= 128
+
+ p: [56]byte = ---
+ copy_slice(p[:], point)
+
+ d: [56]byte = ---
+ _scalarmult(&d, &e, &p)
+ copy_slice(dst, d[:])
+
+ mem.zero_explicit(&e, size_of(e))
+ mem.zero_explicit(&d, size_of(d))
+}
+
+// scalarmult_basepoint "multiplies" the provided scalar with the X448
+// base point and writes the resulting point to dst.
+scalarmult_basepoint :: proc(dst, scalar: []byte) {
+ scalarmult(dst, scalar, _BASE_POINT[:])
+}
diff --git a/core/debug/trace/trace_windows.odin b/core/debug/trace/trace_windows.odin
index c9868e338..96507714c 100644
--- a/core/debug/trace/trace_windows.odin
+++ b/core/debug/trace/trace_windows.odin
@@ -49,7 +49,9 @@ _resolve :: proc(ctx: ^Context, frame: Frame, allocator: runtime.Allocator) -> (
data: [size_of(win32.SYMBOL_INFOW) + size_of([256]win32.WCHAR)]byte
symbol := (^win32.SYMBOL_INFOW)(&data[0])
- symbol.SizeOfStruct = size_of(symbol)
+ // The value of SizeOfStruct must be the size of the whole struct,
+ // not just the size of the pointer
+ symbol.SizeOfStruct = size_of(symbol^)
symbol.MaxNameLen = 255
if win32.SymFromAddrW(ctx.impl.hProcess, win32.DWORD64(frame), &{}, symbol) {
fl.procedure, _ = win32.wstring_to_utf8(&symbol.Name[0], -1, allocator)
diff --git a/core/dynlib/example/example.odin b/core/dynlib/example/example.odin
index f12233b0a..78fb5a98c 100644
--- a/core/dynlib/example/example.odin
+++ b/core/dynlib/example/example.odin
@@ -21,12 +21,14 @@ Symbols :: struct {
main :: proc() {
sym: Symbols
+ LIB_PATH :: "lib." + dynlib.LIBRARY_FILE_EXTENSION
+
// Load symbols from `lib.dll` into Symbols struct.
// Each struct field is prefixed with `foo_` before lookup in the DLL's symbol table.
// The library's Handle (to unload) will be stored in `sym._my_lib_handle`. This way you can load multiple DLLs in one struct.
- count, ok := dynlib.initialize_symbols(&sym, "lib.dll", "foo_", "_my_lib_handle")
+ count, ok := dynlib.initialize_symbols(&sym, LIB_PATH, "foo_", "_my_lib_handle")
defer dynlib.unload_library(sym._my_lib_handle)
- fmt.printf("(Initial DLL Load) ok: %v. %v symbols loaded from lib.dll (%p).\n", ok, count, sym._my_lib_handle)
+ fmt.printf("(Initial DLL Load) ok: %v. %v symbols loaded from " + LIB_PATH + " (%p).\n", ok, count, sym._my_lib_handle)
if count > 0 {
fmt.println("42 + 42 =", sym.add(42, 42))
@@ -34,12 +36,12 @@ main :: proc() {
fmt.println("hellope =", sym.hellope^)
}
- count, ok = dynlib.initialize_symbols(&sym, "lib.dll", "foo_", "_my_lib_handle")
- fmt.printf("(DLL Reload) ok: %v. %v symbols loaded from lib.dll (%p).\n", ok, count, sym._my_lib_handle)
+ count, ok = dynlib.initialize_symbols(&sym, LIB_PATH, "foo_", "_my_lib_handle")
+ fmt.printf("(DLL Reload) ok: %v. %v symbols loaded from " + LIB_PATH + " (%p).\n", ok, count, sym._my_lib_handle)
if count > 0 {
fmt.println("42 + 42 =", sym.add(42, 42))
fmt.println("84 - 13 =", sym.sub(84, 13))
fmt.println("hellope =", sym.hellope^)
}
-}
\ No newline at end of file
+}
diff --git a/core/dynlib/lib.odin b/core/dynlib/lib.odin
index 84675a560..e6b39be4b 100644
--- a/core/dynlib/lib.odin
+++ b/core/dynlib/lib.odin
@@ -12,6 +12,11 @@ A handle to a dynamically loaded library.
*/
Library :: distinct rawptr
+/*
+The file extension for dynamic libraries on the target OS.
+*/
+LIBRARY_FILE_EXTENSION :: _LIBRARY_FILE_EXTENSION
+
/*
Loads a dynamic library from the filesystem. The paramater `global_symbols` makes the symbols in the loaded
library available to resolve references in subsequently loaded libraries.
@@ -123,31 +128,41 @@ initialize_symbols :: proc(
) -> (count: int = -1, ok: bool = false) where intrinsics.type_is_struct(T) {
assert(symbol_table != nil)
- handle := load_library(library_path) or_return
+ // First, (re)load the library.
+ handle: Library
+ for field in reflect.struct_fields_zipped(T) {
+ if field.name == handle_field_name {
+ field_ptr := rawptr(uintptr(symbol_table) + field.offset)
+
+ // We appear to be hot reloading. Unload previous incarnation of the library.
+ if old_handle := (^Library)(field_ptr)^; old_handle != nil {
+ unload_library(old_handle) or_return
+ }
+
+ handle = load_library(library_path) or_return
+ (^Library)(field_ptr)^ = handle
+ break
+ }
+ }
+
+ // No field for it in the struct.
+ if handle == nil {
+ handle = load_library(library_path) or_return
+ }
// Buffer to concatenate the prefix + symbol name.
prefixed_symbol_buf: [2048]u8 = ---
count = 0
for field in reflect.struct_fields_zipped(T) {
+ // If we're not the library handle, the field needs to be a pointer type, be it a procedure pointer or an exported global.
+ if field.name == handle_field_name || !(reflect.is_procedure(field.type) || reflect.is_pointer(field.type)) {
+ continue
+ }
+
// Calculate address of struct member
field_ptr := rawptr(uintptr(symbol_table) + field.offset)
- // If we've come across the struct member for the handle, store it and continue scanning for other symbols.
- if field.name == handle_field_name {
- // We appear to be hot reloading. Unload previous incarnation of the library.
- if old_handle := (^Library)(field_ptr)^; old_handle != nil {
- unload_library(old_handle) or_return
- }
- (^Library)(field_ptr)^ = handle
- continue
- }
-
- // We're not the library handle, so the field needs to be a pointer type, be it a procedure pointer or an exported global.
- if !(reflect.is_procedure(field.type) || reflect.is_pointer(field.type)) {
- continue
- }
-
// Let's look up or construct the symbol name to find in the library
prefixed_name: string
diff --git a/core/dynlib/lib_js.odin b/core/dynlib/lib_js.odin
index b99143ba0..99f486dd0 100644
--- a/core/dynlib/lib_js.odin
+++ b/core/dynlib/lib_js.odin
@@ -4,6 +4,8 @@ package dynlib
import "base:runtime"
+_LIBRARY_FILE_EXTENSION :: ""
+
_load_library :: proc(path: string, global_symbols: bool, allocator: runtime.Allocator) -> (Library, bool) {
return nil, false
}
diff --git a/core/dynlib/lib_unix.odin b/core/dynlib/lib_unix.odin
index 337bf496d..1a6a737a4 100644
--- a/core/dynlib/lib_unix.odin
+++ b/core/dynlib/lib_unix.odin
@@ -7,10 +7,14 @@ import "base:runtime"
import "core:strings"
import "core:sys/posix"
+_LIBRARY_FILE_EXTENSION :: "dylib" when ODIN_OS == .Darwin else "so"
+
_load_library :: proc(path: string, global_symbols: bool, allocator: runtime.Allocator) -> (Library, bool) {
flags := posix.RTLD_Flags{.NOW}
if global_symbols {
flags += {.GLOBAL}
+ } else {
+ flags += posix.RTLD_LOCAL
}
cpath := strings.clone_to_cstring(path, allocator)
diff --git a/core/dynlib/lib_windows.odin b/core/dynlib/lib_windows.odin
index 928a1510d..05cd2cb3c 100644
--- a/core/dynlib/lib_windows.odin
+++ b/core/dynlib/lib_windows.odin
@@ -8,6 +8,8 @@ import win32 "core:sys/windows"
import "core:strings"
import "core:reflect"
+_LIBRARY_FILE_EXTENSION :: "dll"
+
_load_library :: proc(path: string, global_symbols: bool, allocator: runtime.Allocator) -> (Library, bool) {
// NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL
wide_path := win32.utf8_to_wstring(path, allocator)
diff --git a/core/encoding/base32/base32.odin b/core/encoding/base32/base32.odin
index f3320428d..2267a872b 100644
--- a/core/encoding/base32/base32.odin
+++ b/core/encoding/base32/base32.odin
@@ -1,148 +1,230 @@
-package encoding_base32
-
-// @note(zh): Encoding utility for Base32
-// A secondary param can be used to supply a custom alphabet to
-// @link(encode) and a matching decoding table to @link(decode).
-// If none is supplied it just uses the standard Base32 alphabet.
-// Incase your specific version does not use padding, you may
-// truncate it from the encoded output.
-
-ENC_TABLE := [32]byte {
- 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
- 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
- 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
- 'Y', 'Z', '2', '3', '4', '5', '6', '7',
-}
-
-PADDING :: '='
-
-DEC_TABLE := [?]u8 {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 26, 27, 28, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
- 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
- 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
- 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-}
-
-encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string {
- out_length := (len(data) + 4) / 5 * 8
- out := make([]byte, out_length)
- _encode(out, data)
- return string(out)
-}
-
-@private
-_encode :: proc(out, data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) {
- out := out
- data := data
-
- for len(data) > 0 {
- carry: byte
- switch len(data) {
- case:
- out[7] = ENC_TABLE[data[4] & 0x1f]
- carry = data[4] >> 5
- fallthrough
- case 4:
- out[6] = ENC_TABLE[carry | (data[3] << 3) & 0x1f]
- out[5] = ENC_TABLE[(data[3] >> 2) & 0x1f]
- carry = data[3] >> 7
- fallthrough
- case 3:
- out[4] = ENC_TABLE[carry | (data[2] << 1) & 0x1f]
- carry = (data[2] >> 4) & 0x1f
- fallthrough
- case 2:
- out[3] = ENC_TABLE[carry | (data[1] << 4) & 0x1f]
- out[2] = ENC_TABLE[(data[1] >> 1) & 0x1f]
- carry = (data[1] >> 6) & 0x1f
- fallthrough
- case 1:
- out[1] = ENC_TABLE[carry | (data[0] << 2) & 0x1f]
- out[0] = ENC_TABLE[data[0] >> 3]
- }
-
- if len(data) < 5 {
- out[7] = byte(PADDING)
- if len(data) < 4 {
- out[6] = byte(PADDING)
- out[5] = byte(PADDING)
- if len(data) < 3 {
- out[4] = byte(PADDING)
- if len(data) < 2 {
- out[3] = byte(PADDING)
- out[2] = byte(PADDING)
- }
- }
- }
- break
- }
- data = data[5:]
- out = out[8:]
- }
-}
-
-decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check{
- if len(data) == 0 {
- return nil
- }
-
- outi := 0
- data := data
-
- out := make([]byte, len(data) / 8 * 5, allocator)
- end := false
- for len(data) > 0 && !end {
- dbuf : [8]byte
- dlen := 8
-
- for j := 0; j < 8; {
- if len(data) == 0 {
- dlen, end = j, true
- break
- }
- input := data[0]
- data = data[1:]
- if input == byte(PADDING) && j >= 2 && len(data) < 8 {
- assert(!(len(data) + j < 8 - 1), "Corrupted input")
- for k := 0; k < 8-1-j; k +=1 {
- assert(len(data) < k || data[k] == byte(PADDING), "Corrupted input")
- }
- dlen, end = j, true
- assert(dlen != 1 && dlen != 3 && dlen != 6, "Corrupted input")
- break
- }
- dbuf[j] = DEC_TABLE[input]
- assert(dbuf[j] != 0xff, "Corrupted input")
- j += 1
- }
-
- switch dlen {
- case 8:
- out[outi + 4] = dbuf[6] << 5 | dbuf[7]
- fallthrough
- case 7:
- out[outi + 3] = dbuf[4] << 7 | dbuf[5] << 2 | dbuf[6] >> 3
- fallthrough
- case 5:
- out[outi + 2] = dbuf[3] << 4 | dbuf[4] >> 1
- fallthrough
- case 4:
- out[outi + 1] = dbuf[1] << 6 | dbuf[2] << 1 | dbuf[3] >> 4
- fallthrough
- case 2:
- out[outi + 0] = dbuf[0] << 3 | dbuf[1] >> 2
- }
- outi += 5
- }
- return out
-}
+// Base32 encoding/decoding implementation as specified in RFC 4648.
+// [[ More; https://www.rfc-editor.org/rfc/rfc4648.html ]]
+package encoding_base32
+
+// @note(zh): Encoding utility for Base32
+// A secondary param can be used to supply a custom alphabet to
+// @link(encode) and a matching decoding table to @link(decode).
+// If none is supplied it just uses the standard Base32 alphabet.
+// In case your specific version does not use padding, you may
+// truncate it from the encoded output.
+
+// Error represents errors that can occur during base32 decoding operations.
+// As per RFC 4648:
+// - Section 3.3: Invalid character handling
+// - Section 3.2: Padding requirements
+// - Section 6: Base32 encoding specifics (including block size requirements)
+Error :: enum {
+ None,
+ Invalid_Character, // Input contains characters outside the specified alphabet
+ Invalid_Length, // Input length is not valid for base32 (must be a multiple of 8 with proper padding)
+ Malformed_Input, // Input has improper structure (wrong padding position or incomplete groups)
+}
+
+Validate_Proc :: #type proc(c: byte) -> bool
+
+@private
+_validate_default :: proc(c: byte) -> bool {
+ return (c >= 'A' && c <= 'Z') || (c >= '2' && c <= '7')
+}
+
+@(rodata)
+ENC_TABLE := [32]byte {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+ 'Y', 'Z', '2', '3', '4', '5', '6', '7',
+}
+
+PADDING :: '='
+
+@(rodata)
+DEC_TABLE := [256]u8 {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 26, 27, 28, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
+ 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+}
+
+encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string {
+ out_length := (len(data) + 4) / 5 * 8
+ out := make([]byte, out_length, allocator)
+ _encode(out, data, ENC_TBL)
+ return string(out[:])
+}
+
+@private
+_encode :: proc(out, data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) {
+ out := out
+ data := data
+
+ for len(data) > 0 {
+ carry: byte
+ switch len(data) {
+ case:
+ out[7] = ENC_TBL[data[4] & 0x1f]
+ carry = data[4] >> 5
+ fallthrough
+ case 4:
+ out[6] = ENC_TBL[carry | (data[3] << 3) & 0x1f]
+ out[5] = ENC_TBL[(data[3] >> 2) & 0x1f]
+ carry = data[3] >> 7
+ fallthrough
+ case 3:
+ out[4] = ENC_TBL[carry | (data[2] << 1) & 0x1f]
+ carry = (data[2] >> 4) & 0x1f
+ fallthrough
+ case 2:
+ out[3] = ENC_TBL[carry | (data[1] << 4) & 0x1f]
+ out[2] = ENC_TBL[(data[1] >> 1) & 0x1f]
+ carry = (data[1] >> 6) & 0x1f
+ fallthrough
+ case 1:
+ out[1] = ENC_TBL[carry | (data[0] << 2) & 0x1f]
+ out[0] = ENC_TBL[data[0] >> 3]
+ }
+
+ if len(data) < 5 {
+ out[7] = byte(PADDING)
+ if len(data) < 4 {
+ out[6] = byte(PADDING)
+ out[5] = byte(PADDING)
+ if len(data) < 3 {
+ out[4] = byte(PADDING)
+ if len(data) < 2 {
+ out[3] = byte(PADDING)
+ out[2] = byte(PADDING)
+ }
+ }
+ }
+ break
+ }
+ data = data[5:]
+ out = out[8:]
+ }
+}
+
+@(optimization_mode="favor_size")
+decode :: proc(
+ data: string,
+ DEC_TBL := DEC_TABLE,
+ validate: Validate_Proc = _validate_default,
+ allocator := context.allocator) -> (out: []byte, err: Error) {
+ if len(data) == 0 {
+ return nil, .None
+ }
+
+ // Check minimum length requirement first
+ if len(data) < 2 {
+ return nil, .Invalid_Length
+ }
+
+ // Validate characters using provided validation function
+ for i := 0; i < len(data); i += 1 {
+ c := data[i]
+ if c == byte(PADDING) {
+ break
+ }
+ if !validate(c) {
+ return nil, .Invalid_Character
+ }
+ }
+
+ // Validate padding and length
+ data_len := len(data)
+ padding_count := 0
+ for i := data_len - 1; i >= 0; i -= 1 {
+ if data[i] != byte(PADDING) {
+ break
+ }
+ padding_count += 1
+ }
+
+ // Check for proper padding and length combinations
+ if padding_count > 0 {
+ // Verify no padding in the middle
+ for i := 0; i < data_len - padding_count; i += 1 {
+ if data[i] == byte(PADDING) {
+ return nil, .Malformed_Input
+ }
+ }
+
+ content_len := data_len - padding_count
+ mod8 := content_len % 8
+ required_padding: int
+ switch mod8 {
+ case 2: required_padding = 6 // 2 chars need 6 padding chars
+ case 4: required_padding = 4 // 4 chars need 4 padding chars
+ case 5: required_padding = 3 // 5 chars need 3 padding chars
+ case 7: required_padding = 1 // 7 chars need 1 padding char
+ case: required_padding = 0
+ }
+
+ if required_padding > 0 {
+ if padding_count != required_padding {
+ return nil, .Malformed_Input
+ }
+ } else if mod8 != 0 {
+ return nil, .Malformed_Input
+ }
+ } else {
+ // No padding - must be multiple of 8
+ if data_len % 8 != 0 {
+ return nil, .Malformed_Input
+ }
+ }
+
+ // Calculate decoded length: 5 bytes for every 8 input chars
+ input_chars := data_len - padding_count
+ out_len := input_chars * 5 / 8
+ out = make([]byte, out_len, allocator)
+ defer if err != .None {
+ delete(out)
+ }
+
+ // Process input in 8-byte blocks
+ outi := 0
+ for i := 0; i < input_chars; i += 8 {
+ buf: [8]byte
+ block_size := min(8, input_chars - i)
+
+ // Decode block
+ for j := 0; j < block_size; j += 1 {
+ buf[j] = DEC_TBL[data[i + j]]
+ }
+
+ // Convert to output bytes based on block size
+ bytes_to_write := block_size * 5 / 8
+ switch block_size {
+ case 8:
+ out[outi + 4] = (buf[6] << 5) | buf[7]
+ fallthrough
+ case 7:
+ out[outi + 3] = (buf[4] << 7) | (buf[5] << 2) | (buf[6] >> 3)
+ fallthrough
+ case 5:
+ out[outi + 2] = (buf[3] << 4) | (buf[4] >> 1)
+ fallthrough
+ case 4:
+ out[outi + 1] = (buf[1] << 6) | (buf[2] << 1) | (buf[3] >> 4)
+ fallthrough
+ case 2:
+ out[outi] = (buf[0] << 3) | (buf[1] >> 2)
+ }
+ outi += bytes_to_write
+ }
+
+ return
+}
diff --git a/core/encoding/base32/base32_test.odin b/core/encoding/base32/base32_test.odin
new file mode 100644
index 000000000..ea41ae36f
--- /dev/null
+++ b/core/encoding/base32/base32_test.odin
@@ -0,0 +1,227 @@
+package encoding_base32
+
+import "core:testing"
+import "core:bytes"
+
+@(test)
+test_base32_decode_valid :: proc(t: ^testing.T) {
+ // RFC 4648 Section 10 - Test vectors
+ cases := [?]struct {
+ input, expected: string,
+ }{
+ {"", ""},
+ {"MY======", "f"},
+ {"MZXQ====", "fo"},
+ {"MZXW6===", "foo"},
+ {"MZXW6YQ=", "foob"},
+ {"MZXW6YTB", "fooba"},
+ {"MZXW6YTBOI======", "foobar"},
+ }
+
+ for c in cases {
+ output, err := decode(c.input)
+ if output != nil {
+ defer delete(output)
+ }
+ testing.expect_value(t, err, Error.None)
+ expected := transmute([]u8)c.expected
+ if output != nil {
+ testing.expect(t, bytes.equal(output, expected))
+ } else {
+ testing.expect(t, len(c.expected) == 0)
+ }
+ }
+}
+
+@(test)
+test_base32_encode :: proc(t: ^testing.T) {
+ // RFC 4648 Section 10 - Test vectors
+ cases := [?]struct {
+ input, expected: string,
+ }{
+ {"", ""},
+ {"f", "MY======"},
+ {"fo", "MZXQ===="},
+ {"foo", "MZXW6==="},
+ {"foob", "MZXW6YQ="},
+ {"fooba", "MZXW6YTB"},
+ {"foobar", "MZXW6YTBOI======"},
+ }
+
+ for c in cases {
+ output := encode(transmute([]byte)c.input)
+ defer delete(output)
+ testing.expect(t, output == c.expected)
+ }
+}
+
+@(test)
+test_base32_decode_invalid :: proc(t: ^testing.T) {
+ // Section 3.3 - Non-alphabet characters
+ {
+ // Characters outside alphabet
+ input := "MZ1W6YTB" // '1' not in alphabet (A-Z, 2-7)
+ output, err := decode(input)
+ if output != nil {
+ defer delete(output)
+ }
+ testing.expect_value(t, err, Error.Invalid_Character)
+ }
+ {
+ // Lowercase not allowed
+ input := "mzxq===="
+ output, err := decode(input)
+ if output != nil {
+ defer delete(output)
+ }
+ testing.expect_value(t, err, Error.Invalid_Character)
+ }
+
+ // Section 3.2 - Padding requirements
+ {
+ // Padding must only be at end
+ input := "MZ=Q===="
+ output, err := decode(input)
+ if output != nil {
+ defer delete(output)
+ }
+ testing.expect_value(t, err, Error.Malformed_Input)
+ }
+ {
+ // Missing padding
+ input := "MZXQ" // Should be MZXQ====
+ output, err := decode(input)
+ if output != nil {
+ defer delete(output)
+ }
+ testing.expect_value(t, err, Error.Malformed_Input)
+ }
+ {
+ // Incorrect padding length
+ input := "MZXQ=" // Needs 4 padding chars
+ output, err := decode(input)
+ if output != nil {
+ defer delete(output)
+ }
+ testing.expect_value(t, err, Error.Malformed_Input)
+ }
+ {
+ // Too much padding
+ input := "MY=========" // Extra padding chars
+ output, err := decode(input)
+ if output != nil {
+ defer delete(output)
+ }
+ testing.expect_value(t, err, Error.Malformed_Input)
+ }
+
+ // Section 6 - Base32 block size requirements
+ {
+ // Single character (invalid block)
+ input := "M"
+ output, err := decode(input)
+ if output != nil {
+ defer delete(output)
+ }
+ testing.expect_value(t, err, Error.Invalid_Length)
+ }
+}
+
+@(test)
+test_base32_roundtrip :: proc(t: ^testing.T) {
+ cases := [?]string{
+ "",
+ "f",
+ "fo",
+ "foo",
+ "foob",
+ "fooba",
+ "foobar",
+ }
+
+ for input in cases {
+ encoded := encode(transmute([]byte)input)
+ defer delete(encoded)
+ decoded, err := decode(encoded)
+ if decoded != nil {
+ defer delete(decoded)
+ }
+ testing.expect_value(t, err, Error.None)
+ testing.expect(t, bytes.equal(decoded, transmute([]byte)input))
+ }
+}
+
+@(test)
+test_base32_custom_alphabet :: proc(t: ^testing.T) {
+ custom_enc_table := [32]byte{
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
+ 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
+ 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
+ }
+
+ custom_dec_table: [256]u8
+ for i := 0; i < len(custom_enc_table); i += 1 {
+ custom_dec_table[custom_enc_table[i]] = u8(i)
+ }
+
+ /*
+ custom_dec_table := [256]u8{
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00-0x0f
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x10-0x1f
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x20-0x2f
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, // 0x30-0x3f ('0'-'9')
+ 0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 0x40-0x4f ('A'-'O')
+ 25, 26, 27, 28, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x50-0x5f ('P'-'V')
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x60-0x6f
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x70-0x7f
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80-0x8f
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90-0x9f
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xa0-0xaf
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xb0-0xbf
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xc0-0xcf
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xd0-0xdf
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xe0-0xef
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xf0-0xff
+ }
+ */
+
+ custom_validate :: proc(c: byte) -> bool {
+ return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'V') || c == byte(PADDING)
+ }
+
+ cases := [?]struct {
+ input: string,
+ enc_expected: string,
+ }{
+ {"f", "CO======"},
+ {"fo", "CPNG===="},
+ {"foo", "CPNMU==="},
+ }
+
+ for c in cases {
+ // Test encoding
+ encoded := encode(transmute([]byte)c.input, custom_enc_table)
+ defer delete(encoded)
+ testing.expect(t, encoded == c.enc_expected)
+
+ // Test decoding
+ decoded, err := decode(encoded, custom_dec_table, custom_validate)
+ defer if decoded != nil {
+ delete(decoded)
+ }
+
+ testing.expect_value(t, err, Error.None)
+ testing.expect(t, bytes.equal(decoded, transmute([]byte)c.input))
+ }
+
+ // Test invalid character detection
+ {
+ input := "WXY=====" // Contains chars not in our alphabet
+ output, err := decode(input, custom_dec_table, custom_validate)
+ if output != nil {
+ delete(output)
+ }
+ testing.expect_value(t, err, Error.Invalid_Character)
+ }
+}
diff --git a/core/encoding/entity/entity.odin b/core/encoding/entity/entity.odin
index d2f1d46b2..cb8fa8611 100644
--- a/core/encoding/entity/entity.odin
+++ b/core/encoding/entity/entity.odin
@@ -108,7 +108,7 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator :=
it couldn't have been part of an XML tag body to be decoded here.
Keep in mind that we could already *be* inside a CDATA tag.
- If so, write `>` as a literal and continue.
+ If so, write `<` as a literal and continue.
*/
if in_data {
write_rune(&builder, '<')
@@ -119,11 +119,9 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator :=
case ']':
// If we're unboxing _and_ decoding CDATA, we'll have to check for the end tag.
if in_data {
- if t.read_offset + len(CDATA_END) < len(t.src) {
- if string(t.src[t.offset:][:len(CDATA_END)]) == CDATA_END {
- in_data = false
- t.read_offset += len(CDATA_END) - 1
- }
+ if strings.has_prefix(t.src[t.offset:], CDATA_END) {
+ in_data = false
+ t.read_offset += len(CDATA_END) - 1
}
continue
} else {
@@ -297,40 +295,40 @@ _handle_xml_special :: proc(t: ^Tokenizer, builder: ^strings.Builder, options: X
assert(t != nil && t.r == '<')
if t.read_offset + len(CDATA_START) >= len(t.src) { return false, .None }
- if string(t.src[t.offset:][:len(CDATA_START)]) == CDATA_START {
- t.read_offset += len(CDATA_START) - 1
-
+ s := string(t.src[t.offset:])
+ if strings.has_prefix(s, CDATA_START) {
if .Unbox_CDATA in options && .Decode_CDATA in options {
// We're unboxing _and_ decoding CDATA
+ t.read_offset += len(CDATA_START) - 1
return true, .None
}
- // CDATA is passed through.
- offset := t.offset
-
- // Scan until end of CDATA.
+ // CDATA is passed through. Scan until end of CDATA.
+ start_offset := t.offset
+ t.read_offset += len(CDATA_START)
for {
- advance(t) or_return
- if t.r < 0 { return true, .CDATA_Not_Terminated }
+ advance(t)
+ if t.r < 0 {
+ // error(t, offset, "[scan_string] CDATA was not terminated\n")
+ return true, .CDATA_Not_Terminated
+ }
- if t.read_offset + len(CDATA_END) < len(t.src) {
- if string(t.src[t.offset:][:len(CDATA_END)]) == CDATA_END {
- t.read_offset += len(CDATA_END) - 1
+ // Scan until the end of a CDATA tag.
+ if s = string(t.src[t.read_offset:]); strings.has_prefix(s, CDATA_END) {
+ t.read_offset += len(CDATA_END)
+ cdata := string(t.src[start_offset:t.read_offset])
- cdata := string(t.src[offset : t.read_offset])
-
- if .Unbox_CDATA in options {
- cdata = cdata[len(CDATA_START):]
- cdata = cdata[:len(cdata) - len(CDATA_END)]
- }
-
- write_string(builder, cdata)
- return false, .None
+ if .Unbox_CDATA in options {
+ cdata = cdata[len(CDATA_START):]
+ cdata = cdata[:len(cdata) - len(CDATA_END)]
}
+ write_string(builder, cdata)
+ return false, .None
}
}
- } else if string(t.src[t.offset:][:len(COMMENT_START)]) == COMMENT_START {
+
+ } else if strings.has_prefix(s, COMMENT_START) {
t.read_offset += len(COMMENT_START)
// Comment is passed through by default.
offset := t.offset
diff --git a/core/encoding/hxa/read.odin b/core/encoding/hxa/read.odin
index a679946f8..6dde16848 100644
--- a/core/encoding/hxa/read.odin
+++ b/core/encoding/hxa/read.odin
@@ -79,7 +79,6 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato
read_meta :: proc(r: ^Reader, capacity: u32le, allocator := context.allocator, loc := #caller_location) -> (meta_data: []Meta, err: Read_Error) {
meta_data = make([]Meta, int(capacity), allocator=allocator)
count := 0
- defer meta_data = meta_data[:count]
for &m in meta_data {
m.name = read_name(r) or_return
@@ -105,6 +104,7 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato
count += 1
}
+ meta_data = meta_data[:count]
return
}
@@ -112,7 +112,6 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato
stack_count := read_value(r, u32le) or_return
layer_count := 0
layers = make(Layer_Stack, stack_count, allocator=allocator, loc=loc)
- defer layers = layers[:layer_count]
for &layer in layers {
layer.name = read_name(r) or_return
layer.components = read_value(r, u8) or_return
@@ -136,6 +135,7 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato
layer_count += 1
}
+ layers = layers[:layer_count]
return
}
diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin
index f0f0927a1..020facd14 100644
--- a/core/encoding/json/marshal.odin
+++ b/core/encoding/json/marshal.odin
@@ -209,13 +209,23 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
opt_write_end(w, opt, ']') or_return
case runtime.Type_Info_Enumerated_Array:
- opt_write_start(w, opt, '[') or_return
+ index_type := reflect.type_info_base(info.index)
+ enum_type := index_type.variant.(reflect.Type_Info_Enum)
+
+ opt_write_start(w, opt, '{') or_return
for i in 0.. u128 {
}
return u
-}
\ No newline at end of file
+}
diff --git a/core/encoding/json/tokenizer.odin b/core/encoding/json/tokenizer.odin
index 5c20a2cc3..e46d879a7 100644
--- a/core/encoding/json/tokenizer.odin
+++ b/core/encoding/json/tokenizer.odin
@@ -259,6 +259,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
skip_digits(t)
}
if t.r == 'e' || t.r == 'E' {
+ token.kind = .Float
switch r := next_rune(t); r {
case '+', '-':
next_rune(t)
@@ -485,7 +486,7 @@ is_valid_string_literal :: proc(str: string, spec: Specification) -> bool {
case '"':
// okay
case '\'':
- if spec != .JSON {
+ if spec == .JSON {
return false
}
// okay
diff --git a/core/encoding/json/unmarshal.odin b/core/encoding/json/unmarshal.odin
index ea33badae..151bd69c3 100644
--- a/core/encoding/json/unmarshal.odin
+++ b/core/encoding/json/unmarshal.odin
@@ -117,9 +117,25 @@ assign_int :: proc(val: any, i: $T) -> bool {
case uint: dst = uint (i)
case uintptr: dst = uintptr(i)
case:
+ is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool {
+ if ti == nil {
+ return false
+ }
+ t := runtime.type_info_base(ti)
+ #partial switch info in t.variant {
+ case runtime.Type_Info_Integer:
+ switch info.endianness {
+ case .Platform: return false
+ case .Little: return ODIN_ENDIAN != .Little
+ case .Big: return ODIN_ENDIAN != .Big
+ }
+ }
+ return false
+ }
+
ti := type_info_of(v.id)
- if _, ok := ti.variant.(runtime.Type_Info_Bit_Set); ok {
- do_byte_swap := !reflect.bit_set_is_big_endian(v)
+ if info, ok := ti.variant.(runtime.Type_Info_Bit_Set); ok {
+ do_byte_swap := is_bit_set_different_endian_to_platform(info.underlying)
switch ti.size * 8 {
case 0: // no-op.
case 8:
@@ -417,15 +433,15 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
if .raw_union in t.flags {
return UNSUPPORTED_TYPE
}
-
+
+ fields := reflect.struct_fields_zipped(ti.id)
+
struct_loop: for p.curr_token.kind != end_token {
- key, _ := parse_object_key(p, p.allocator)
+ key := parse_object_key(p, p.allocator) or_return
defer delete(key, p.allocator)
unmarshal_expect_token(p, .Colon)
- fields := reflect.struct_fields_zipped(ti.id)
-
field_test :: #force_inline proc "contextless" (field_used: [^]byte, offset: uintptr) -> bool {
prev_set := field_used[offset/8] & byte(offset&7) != 0
field_used[offset/8] |= byte(offset&7)
@@ -433,13 +449,13 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
}
field_used_bytes := (reflect.size_of_typeid(ti.id)+7)/8
- field_used := intrinsics.alloca(field_used_bytes, 1)
+ field_used := intrinsics.alloca(field_used_bytes + 1, 1) // + 1 to not overflow on size_of 0 types.
intrinsics.mem_zero(field_used, field_used_bytes)
use_field_idx := -1
for field, field_idx in fields {
- tag_value := string(reflect.struct_tag_get(field.tag, "json"))
+ tag_value := reflect.struct_tag_get(field.tag, "json")
json_name, _ := json_name_from_tag_value(tag_value)
if key == json_name {
use_field_idx = field_idx
@@ -470,7 +486,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
}
}
- if field.name == key {
+ if field.name == key || (field.tag != "" && reflect.struct_tag_get(field.tag, "json") == key) {
offset = field.offset
type = field.type
found = true
diff --git a/core/encoding/uuid/generation.odin b/core/encoding/uuid/generation.odin
index 7c9d4b80c..b210f6a52 100644
--- a/core/encoding/uuid/generation.odin
+++ b/core/encoding/uuid/generation.odin
@@ -240,7 +240,7 @@ Example:
import "core:encoding/uuid"
import "core:fmt"
- main :: proc() {
+ generate_v8_hash_bytes_example :: proc() {
my_uuid := uuid.generate_v8_hash(uuid.Namespace_DNS, "www.odin-lang.org", .SHA256)
my_uuid_string := uuid.to_string(my_uuid, context.temp_allocator)
fmt.println(my_uuid_string)
@@ -306,7 +306,7 @@ Example:
import "core:encoding/uuid"
import "core:fmt"
- main :: proc() {
+ generate_v8_hash_string_example :: proc() {
my_uuid := uuid.generate_v8_hash(uuid.Namespace_DNS, "www.odin-lang.org", .SHA256)
my_uuid_string := uuid.to_string(my_uuid, context.temp_allocator)
fmt.println(my_uuid_string)
diff --git a/core/encoding/xml/tokenizer.odin b/core/encoding/xml/tokenizer.odin
index a2bbaf28e..3ef9a6388 100644
--- a/core/encoding/xml/tokenizer.odin
+++ b/core/encoding/xml/tokenizer.odin
@@ -16,6 +16,7 @@ package encoding_xml
import "core:fmt"
import "core:unicode"
import "core:unicode/utf8"
+import "core:strings"
Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any)
@@ -121,7 +122,7 @@ default_error_handler :: proc(pos: Pos, msg: string, args: ..any) {
error :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
pos := offset_to_pos(t, offset)
if t.err != nil {
- t.err(pos, msg, ..args)
+ t.err(pos=pos, fmt=msg, args=args)
}
t.error_count += 1
}
@@ -268,32 +269,27 @@ scan_comment :: proc(t: ^Tokenizer) -> (comment: string, err: Error) {
// Skip CDATA
skip_cdata :: proc(t: ^Tokenizer) -> (err: Error) {
- if t.read_offset + len(CDATA_START) >= len(t.src) {
- // Can't be the start of a CDATA tag.
+ if s := string(t.src[t.offset:]); !strings.has_prefix(s, CDATA_START) {
return .None
}
- if string(t.src[t.offset:][:len(CDATA_START)]) == CDATA_START {
- t.read_offset += len(CDATA_START)
- offset := t.offset
+ t.read_offset += len(CDATA_START)
+ offset := t.offset
- cdata_scan: for {
- advance_rune(t)
- if t.ch < 0 {
- error(t, offset, "[scan_string] CDATA was not terminated\n")
- return .Premature_EOF
- }
+ cdata_scan: for {
+ advance_rune(t)
+ if t.ch < 0 {
+ error(t, offset, "[scan_string] CDATA was not terminated\n")
+ return .Premature_EOF
+ }
- // Scan until the end of a CDATA tag.
- if t.read_offset + len(CDATA_END) < len(t.src) {
- if string(t.src[t.offset:][:len(CDATA_END)]) == CDATA_END {
- t.read_offset += len(CDATA_END)
- break cdata_scan
- }
- }
+ // Scan until the end of a CDATA tag.
+ if s := string(t.src[t.read_offset:]); strings.has_prefix(s, CDATA_END) {
+ t.read_offset += len(CDATA_END)
+ break cdata_scan
}
}
- return
+ return .None
}
@(optimization_mode="favor_size")
@@ -393,6 +389,8 @@ scan :: proc(t: ^Tokenizer, multiline_string := false) -> Token {
case '/': kind = .Slash
case '-': kind = .Dash
case ':': kind = .Colon
+ case '[': kind = .Open_Bracket
+ case ']': kind = .Close_Bracket
case '"', '\'':
kind = .Invalid
diff --git a/core/encoding/xml/xml_reader.odin b/core/encoding/xml/xml_reader.odin
index b8c8b13a4..d616be9dc 100644
--- a/core/encoding/xml/xml_reader.odin
+++ b/core/encoding/xml/xml_reader.odin
@@ -56,7 +56,7 @@ Option_Flag :: enum {
Option_Flags :: bit_set[Option_Flag; u16]
Document :: struct {
- elements: [dynamic]Element,
+ elements: [dynamic]Element `fmt:"v,element_count"`,
element_count: Element_ID,
prologue: Attributes,
@@ -70,15 +70,15 @@ Document :: struct {
// If we encounter comments before the root node, and the option to intern comments is given, this is where they'll live.
// Otherwise they'll be in the element tree.
- comments: [dynamic]string,
+ comments: [dynamic]string `fmt:"-"`,
// Internal
- tokenizer: ^Tokenizer,
- allocator: mem.Allocator,
+ tokenizer: ^Tokenizer `fmt:"-"`,
+ allocator: mem.Allocator `fmt:"-"`,
// Input. Either the original buffer, or a copy if `.Input_May_Be_Modified` isn't specified.
- input: []u8,
- strings_to_free: [dynamic]string,
+ input: []u8 `fmt:"-"`,
+ strings_to_free: [dynamic]string `fmt:"-"`,
}
Element :: struct {
@@ -195,7 +195,6 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
loop: for {
skip_whitespace(t)
- // NOTE(Jeroen): This is faster as a switch.
switch t.ch {
case '<':
// Consume peeked `<`
@@ -306,9 +305,17 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
}
}
+ case .Open_Bracket:
+ // This could be a CDATA tag part of a tag's body. Unread the ` (attr: Attribute, offset: int, err: E
t := doc.tokenizer
key := expect(t, .Ident) or_return
- offset = t.offset - len(key.text)
-
_ = expect(t, .Eq) or_return
value := expect(t, .String, multiline_string=true) or_return
@@ -591,6 +565,47 @@ parse_doctype :: proc(doc: ^Document) -> (err: Error) {
return .None
}
+parse_body :: proc(doc: ^Document, element: Element_ID, opts: Options) -> (err: Error) {
+ assert(doc != nil)
+ context.allocator = doc.allocator
+ t := doc.tokenizer
+
+ body_text := scan_string(t, t.offset) or_return
+ needs_processing := .Unbox_CDATA in opts.flags
+ needs_processing |= .Decode_SGML_Entities in opts.flags
+
+ if !needs_processing {
+ append(&doc.elements[element].value, body_text)
+ return
+ }
+
+ decode_opts := entity.XML_Decode_Options{}
+ if .Keep_Tag_Body_Comments not_in opts.flags {
+ decode_opts += { .Comment_Strip }
+ }
+
+ if .Decode_SGML_Entities not_in opts.flags {
+ decode_opts += { .No_Entity_Decode }
+ }
+
+ if .Unbox_CDATA in opts.flags {
+ decode_opts += { .Unbox_CDATA }
+ if .Decode_SGML_Entities in opts.flags {
+ decode_opts += { .Decode_CDATA }
+ }
+ }
+
+ decoded, decode_err := entity.decode_xml(body_text, decode_opts)
+ if decode_err == .None {
+ append(&doc.elements[element].value, decoded)
+ append(&doc.strings_to_free, decoded)
+ } else {
+ append(&doc.elements[element].value, body_text)
+ }
+
+ return
+}
+
Element_ID :: u32
new_element :: proc(doc: ^Document) -> (id: Element_ID) {
@@ -609,4 +624,4 @@ new_element :: proc(doc: ^Document) -> (id: Element_ID) {
cur := doc.element_count
doc.element_count += 1
return cur
-}
+}
\ No newline at end of file
diff --git a/core/flags/util.odin b/core/flags/util.odin
index f1bd60e75..ce7e2e36c 100644
--- a/core/flags/util.odin
+++ b/core/flags/util.odin
@@ -36,7 +36,7 @@ parse_or_exit :: proc(
args = program_args[1:]
}
- error := parse(model, args, style)
+ error := parse(model, args, style, true, true, allocator, loc)
if error != nil {
stderr := os.stream_from_handle(os.stderr)
@@ -95,7 +95,7 @@ Example:
import "core:flags"
import "core:fmt"
- subtag_example :: proc() {
+ get_subtag_example :: proc() {
args_tag := "precision=3,signed"
precision, has_precision := flags.get_subtag(args_tag, "precision")
diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin
index 49e9f2e6d..9c07847dd 100644
--- a/core/fmt/fmt.odin
+++ b/core/fmt/fmt.odin
@@ -116,11 +116,12 @@ register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Regist
}
// Creates a formatted string
//
-// *Allocates Using Context's Allocator*
+// *Allocates Using Provided Allocator*
//
// Inputs:
// - args: A variadic list of arguments to be formatted.
// - sep: An optional separator string (default is a single space).
+// - allocator: (default: context.allocator)
//
// Returns: A formatted string.
//
@@ -132,11 +133,12 @@ aprint :: proc(args: ..any, sep := " ", allocator := context.allocator) -> strin
}
// Creates a formatted string with a newline character at the end
//
-// *Allocates Using Context's Allocator*
+// *Allocates Using Provided Allocator*
//
// Inputs:
// - args: A variadic list of arguments to be formatted.
// - sep: An optional separator string (default is a single space).
+// - allocator: (default: context.allocator)
//
// Returns: A formatted string with a newline character at the end.
//
@@ -148,11 +150,12 @@ aprintln :: proc(args: ..any, sep := " ", allocator := context.allocator) -> str
}
// Creates a formatted string using a format string and arguments
//
-// *Allocates Using Context's Allocator*
+// *Allocates Using Provided Allocator*
//
// Inputs:
// - fmt: A format string with placeholders for the provided arguments.
// - args: A variadic list of arguments to be formatted.
+// - allocator: (default: context.allocator)
// - newline: Whether the string should end with a newline. (See `aprintfln`.)
//
// Returns: A formatted string. The returned string must be freed accordingly.
@@ -165,11 +168,12 @@ aprintf :: proc(fmt: string, args: ..any, allocator := context.allocator, newlin
}
// Creates a formatted string using a format string and arguments, followed by a newline.
//
-// *Allocates Using Context's Allocator*
+// *Allocates Using Provided Allocator*
//
// Inputs:
// - fmt: A format string with placeholders for the provided arguments.
// - args: A variadic list of arguments to be formatted.
+// - allocator: (default: context.allocator)
//
// Returns: A formatted string. The returned string must be freed accordingly.
//
@@ -314,7 +318,29 @@ assertf :: proc(condition: bool, fmt: string, args: ..any, loc := #caller_locati
p = runtime.default_assertion_failure_proc
}
message := tprintf(fmt, ..args)
- p("Runtime assertion", message, loc)
+ p("runtime assertion", message, loc)
+ }
+ internal(loc, fmt, ..args)
+ }
+}
+// Runtime ensure with a formatted message
+//
+// Inputs:
+// - condition: The boolean condition to be asserted
+// - fmt: A format string with placeholders for the provided arguments
+// - args: A variadic list of arguments to be formatted
+// - loc: The location of the caller
+//
+ensuref :: proc(condition: bool, fmt: string, args: ..any, loc := #caller_location) {
+ if !condition {
+ @(cold)
+ internal :: proc(loc: runtime.Source_Code_Location, fmt: string, args: ..any) {
+ p := context.assertion_failure_proc
+ if p == nil {
+ p = runtime.default_assertion_failure_proc
+ }
+ message := tprintf(fmt, ..args)
+ p("unsatisfied ensure", message, loc)
}
internal(loc, fmt, ..args)
}
@@ -332,16 +358,17 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
p = runtime.default_assertion_failure_proc
}
message := tprintf(fmt, ..args)
- p("Panic", message, loc)
+ p("panic", message, loc)
}
// Creates a formatted C string
//
-// *Allocates Using Context's Allocator*
+// *Allocates Using Provided Allocator*
//
// Inputs:
// - args: A variadic list of arguments to be formatted.
// - sep: An optional separator string (default is a single space).
+// - allocator: (default: context.allocator)
//
// Returns: A formatted C string.
//
@@ -357,11 +384,12 @@ caprint :: proc(args: ..any, sep := " ", allocator := context.allocator) -> cstr
// Creates a formatted C string
//
-// *Allocates Using Context's Allocator*
+// *Allocates Using Provided Allocator*
//
// Inputs:
// - format: A format string with placeholders for the provided arguments
// - args: A variadic list of arguments to be formatted
+// - allocator: (default: context.allocator)
// - newline: Whether the string should end with a newline. (See `caprintfln`.)
//
// Returns: A formatted C string
@@ -377,11 +405,12 @@ caprintf :: proc(format: string, args: ..any, allocator := context.allocator, ne
}
// Creates a formatted C string, followed by a newline.
//
-// *Allocates Using Context's Allocator*
+// *Allocates Using Provided Allocator*
//
// Inputs:
// - format: A format string with placeholders for the provided arguments
// - args: A variadic list of arguments to be formatted
+// - allocator: (default: context.allocator)
//
// Returns: A formatted C string
//
@@ -591,6 +620,10 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true, newline :
i += 1
width_index, _, index_ok := _arg_number(fmt, &i, len(args))
+ if !index_ok {
+ width_index, index_ok = error_check_arg(fi, false, unused_args^)
+ }
+
if index_ok {
unused_args^ -= {width_index}
@@ -616,6 +649,10 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true, newline :
i += 1
precision_index, _, index_ok := _arg_number(fmt, &i, len(args))
+ if !index_ok {
+ precision_index, index_ok = error_check_arg(fi, false, unused_args^)
+ }
+
if index_ok {
unused_args^ -= {precision_index}
fi.prec, _, fi.prec_set = int_from_arg(args, precision_index)
@@ -1267,7 +1304,7 @@ fmt_rune :: proc(fi: ^Info, r: rune, verb: rune) {
case 'q', 'w':
fi.n += io.write_quoted_rune(fi.writer, r)
case:
- fmt_int(fi, u64(r), false, 32, verb)
+ fmt_int(fi, u64(u32(r)), false, 32, verb)
}
}
// Formats an integer value according to the specified formatting verb.
@@ -1357,9 +1394,9 @@ _pad :: proc(fi: ^Info, s: string) {
if fi.minus { // right pad
io.write_string(fi.writer, s, &fi.n)
fmt_write_padding(fi, width)
- } else if !fi.space && s != "" && s[0] == '-' {
+ } else if !fi.space && s != "" && (s[0] == '-' || s[0] == '+') {
// left pad accounting for zero pad of negative number
- io.write_byte(fi.writer, '-', &fi.n)
+ io.write_byte(fi.writer, s[0], &fi.n)
fmt_write_padding(fi, width)
io.write_string(fi.writer, s[1:], &fi.n)
} else { // left pad
@@ -1412,9 +1449,12 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
_fmt_float_as(fi, v, bit_size, verb, 'g', -1)
case 'f', 'F':
_fmt_float_as(fi, v, bit_size, verb, 'f', 3)
- case 'e', 'E':
+ case 'e':
// BUG(): "%.3e" returns "3.000e+00"
_fmt_float_as(fi, v, bit_size, verb, 'e', 6)
+ case 'E':
+ // BUG(): "%.3E" returns "3.000E+00"
+ _fmt_float_as(fi, v, bit_size, verb, 'E', 6)
case 'h', 'H':
prev_fi := fi^
@@ -1765,11 +1805,8 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "", verb: rune = 'v') {
e, is_enum := et.variant.(runtime.Type_Info_Enum)
commas := 0
- loop: for i in 0 ..< bit_size {
- if bits & (1< 0 {
io.write_string(fi.writer, ", ", &fi.n)
}
@@ -1792,8 +1829,7 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "", verb: rune = 'v') {
}
}
}
- v := i64(i) + info.lower
- io.write_i64(fi.writer, v, 10, &fi.n)
+ io.write_i64(fi.writer, i, 10, &fi.n)
commas += 1
}
}
diff --git a/core/image/general.odin b/core/image/general.odin
index c4a884071..e92b54f18 100644
--- a/core/image/general.odin
+++ b/core/image/general.odin
@@ -146,7 +146,7 @@ which_bytes :: proc(data: []byte) -> Which_File_Type {
case s[6:10] == "JFIF", s[6:10] == "Exif":
return .JPEG
case s[:3] == "\xff\xd8\xff":
- switch s[4] {
+ switch s[3] {
case 0xdb, 0xee, 0xe1, 0xe0:
return .JPEG
}
diff --git a/core/image/png/helpers.odin b/core/image/png/helpers.odin
index f094b54a9..a9495ed4d 100644
--- a/core/image/png/helpers.odin
+++ b/core/image/png/helpers.odin
@@ -396,132 +396,4 @@ exif :: proc(c: image.PNG_Chunk) -> (res: Exif, ok: bool) {
General helper functions
*/
-compute_buffer_size :: image.compute_buffer_size
-
-/*
- PNG save helpers
-*/
-
-when false {
-
- make_chunk :: proc(c: any, t: Chunk_Type) -> (res: Chunk) {
-
- data: []u8
- if v, ok := c.([]u8); ok {
- data = v
- } else {
- data = mem.any_to_bytes(c)
- }
-
- res.header.length = u32be(len(data))
- res.header.type = t
- res.data = data
-
- // CRC the type
- crc := hash.crc32(mem.any_to_bytes(res.header.type))
- // Extend the CRC with the data
- res.crc = u32be(hash.crc32(data, crc))
- return
- }
-
- write_chunk :: proc(fd: os.Handle, chunk: Chunk) {
- c := chunk
- // Write length + type
- os.write_ptr(fd, &c.header, 8)
- // Write data
- os.write_ptr(fd, mem.raw_data(c.data), int(c.header.length))
- // Write CRC32
- os.write_ptr(fd, &c.crc, 4)
- }
-
- write_image_as_png :: proc(filename: string, image: Image) -> (err: Error) {
- profiler.timed_proc()
- using image
- using os
- flags: int = O_WRONLY|O_CREATE|O_TRUNC
-
- if len(image.pixels) == 0 || len(image.pixels) < image.width * image.height * int(image.channels) {
- return .Invalid_Image_Dimensions
- }
-
- mode: int = 0
- when ODIN_OS == .Linux || ODIN_OS == .Darwin {
- // NOTE(justasd): 644 (owner read, write; group read; others read)
- mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
- }
-
- fd, fderr := open(filename, flags, mode)
- if fderr != nil {
- return .Cannot_Open_File
- }
- defer close(fd)
-
- magic := Signature
-
- write_ptr(fd, &magic, 8)
-
- ihdr := IHDR{
- width = u32be(width),
- height = u32be(height),
- bit_depth = depth,
- compression_method = 0,
- filter_method = 0,
- interlace_method = .None,
- }
-
- switch channels {
- case 1: ihdr.color_type = Color_Type{}
- case 2: ihdr.color_type = Color_Type{.Alpha}
- case 3: ihdr.color_type = Color_Type{.Color}
- case 4: ihdr.color_type = Color_Type{.Color, .Alpha}
- case:// Unhandled
- return .Unknown_Color_Type
- }
- h := make_chunk(ihdr, .IHDR)
- write_chunk(fd, h)
-
- bytes_needed := width * height * int(channels) + height
- filter_bytes := mem.make_dynamic_array_len_cap([dynamic]u8, bytes_needed, bytes_needed, context.allocator)
- defer delete(filter_bytes)
-
- i := 0; j := 0
- // Add a filter byte 0 per pixel row
- for y := 0; y < height; y += 1 {
- filter_bytes[j] = 0; j += 1
- for x := 0; x < width; x += 1 {
- for z := 0; z < channels; z += 1 {
- filter_bytes[j+z] = image.pixels[i+z]
- }
- i += channels; j += channels
- }
- }
- assert(j == bytes_needed)
-
- a: []u8 = filter_bytes[:]
-
- out_buf: ^[dynamic]u8
- defer free(out_buf)
-
- ctx := zlib.ZLIB_Context{
- in_buf = &a,
- out_buf = out_buf,
- }
- err = zlib.write_zlib_stream_from_memory(&ctx)
-
- b: []u8
- if err == nil {
- b = ctx.out_buf[:]
- } else {
- return err
- }
-
- idat := make_chunk(b, .IDAT)
-
- write_chunk(fd, idat)
-
- iend := make_chunk([]u8{}, .IEND)
- write_chunk(fd, iend)
-
- return nil
- }
-}
+compute_buffer_size :: image.compute_buffer_size
\ No newline at end of file
diff --git a/core/io/io.odin b/core/io/io.odin
index 6072aec6d..c2b44cbdb 100644
--- a/core/io/io.odin
+++ b/core/io/io.odin
@@ -126,7 +126,7 @@ _i64_err :: #force_inline proc "contextless" (n: int, err: Error) -> (i64, Error
}
-// read reads up to len(p) bytes into s. It returns the number of bytes read and any error if occurred.
+// read reads up to len(p) bytes into p. It returns the number of bytes read and any error if occurred.
//
// When read encounters an .EOF or error after successfully reading n > 0 bytes, it returns the number of
// bytes read along with the error.
@@ -142,7 +142,7 @@ read :: proc(s: Reader, p: []byte, n_read: ^int = nil) -> (n: int, err: Error) {
return
}
-// write writes up to len(p) bytes into s. It returns the number of bytes written and any error if occurred.
+// write writes up to len(p) bytes into p. It returns the number of bytes written and any error if occurred.
write :: proc(s: Writer, p: []byte, n_written: ^int = nil) -> (n: int, err: Error) {
if s.procedure != nil {
n64: i64
diff --git a/core/io/util.odin b/core/io/util.odin
index 296be7bc0..fdbbd5b9f 100644
--- a/core/io/util.odin
+++ b/core/io/util.odin
@@ -132,9 +132,13 @@ write_encoded_rune :: proc(w: Writer, r: rune, write_quote := true, n_written: ^
buf: [2]byte
s := strconv.append_bits(buf[:], u64(r), 16, true, 64, strconv.digits, nil)
switch len(s) {
- case 0: write_string(w, "00", &n) or_return
- case 1: write_byte(w, '0', &n) or_return
- case 2: write_string(w, s, &n) or_return
+ case 0:
+ write_string(w, "00", &n) or_return
+ case 1:
+ write_byte(w, '0', &n) or_return
+ fallthrough
+ case 2:
+ write_string(w, s, &n) or_return
}
} else {
write_rune(w, r, &n) or_return
diff --git a/core/log/file_console_logger.odin b/core/log/file_console_logger.odin
index e45f99523..6d93fb879 100644
--- a/core/log/file_console_logger.odin
+++ b/core/log/file_console_logger.odin
@@ -37,30 +37,30 @@ File_Console_Logger_Data :: struct {
ident: string,
}
-create_file_logger :: proc(h: os.Handle, lowest := Level.Debug, opt := Default_File_Logger_Opts, ident := "") -> Logger {
- data := new(File_Console_Logger_Data)
+create_file_logger :: proc(h: os.Handle, lowest := Level.Debug, opt := Default_File_Logger_Opts, ident := "", allocator := context.allocator) -> Logger {
+ data := new(File_Console_Logger_Data, allocator)
data.file_handle = h
data.ident = ident
return Logger{file_console_logger_proc, data, lowest, opt}
}
-destroy_file_logger :: proc(log: Logger) {
+destroy_file_logger :: proc(log: Logger, allocator := context.allocator) {
data := cast(^File_Console_Logger_Data)log.data
if data.file_handle != os.INVALID_HANDLE {
os.close(data.file_handle)
}
- free(data)
+ free(data, allocator)
}
-create_console_logger :: proc(lowest := Level.Debug, opt := Default_Console_Logger_Opts, ident := "") -> Logger {
- data := new(File_Console_Logger_Data)
+create_console_logger :: proc(lowest := Level.Debug, opt := Default_Console_Logger_Opts, ident := "", allocator := context.allocator) -> Logger {
+ data := new(File_Console_Logger_Data, allocator)
data.file_handle = os.INVALID_HANDLE
data.ident = ident
return Logger{file_console_logger_proc, data, lowest, opt}
}
-destroy_console_logger :: proc(log: Logger) {
- free(log.data)
+destroy_console_logger :: proc(log: Logger, allocator := context.allocator) {
+ free(log.data, allocator)
}
file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string, options: Options, location := #caller_location) {
diff --git a/core/log/log.odin b/core/log/log.odin
index cbb2e922b..2b6317060 100644
--- a/core/log/log.odin
+++ b/core/log/log.odin
@@ -115,7 +115,7 @@ panicf :: proc(fmt_str: string, args: ..any, location := #caller_location) -> !
}
@(disabled=ODIN_DISABLE_ASSERT)
-assert :: proc(condition: bool, message := "", loc := #caller_location) {
+assert :: proc(condition: bool, message := #caller_expression(condition), loc := #caller_location) {
if !condition {
@(cold)
internal :: proc(message: string, loc: runtime.Source_Code_Location) {
@@ -145,7 +145,38 @@ assertf :: proc(condition: bool, fmt_str: string, args: ..any, loc := #caller_lo
}
message := fmt.tprintf(fmt_str, ..args)
log(.Fatal, message, location=loc)
- p("Runtime assertion", message, loc)
+ p("runtime assertion", message, loc)
+ }
+ internal(loc, fmt_str, ..args)
+ }
+}
+
+ensure :: proc(condition: bool, message := #caller_expression(condition), loc := #caller_location) {
+ if !condition {
+ @(cold)
+ internal :: proc(message: string, loc: runtime.Source_Code_Location) {
+ p := context.assertion_failure_proc
+ if p == nil {
+ p = runtime.default_assertion_failure_proc
+ }
+ log(.Fatal, message, location=loc)
+ p("unsatisfied ensure", message, loc)
+ }
+ internal(message, loc)
+ }
+}
+
+ensuref :: proc(condition: bool, fmt_str: string, args: ..any, loc := #caller_location) {
+ if !condition {
+ @(cold)
+ internal :: proc(loc: runtime.Source_Code_Location, fmt_str: string, args: ..any) {
+ p := context.assertion_failure_proc
+ if p == nil {
+ p = runtime.default_assertion_failure_proc
+ }
+ message := fmt.tprintf(fmt_str, ..args)
+ log(.Fatal, message, location=loc)
+ p("unsatisfied ensure", message, loc)
}
internal(loc, fmt_str, ..args)
}
diff --git a/core/log/multi_logger.odin b/core/log/multi_logger.odin
index 96d0f3dbd..6122554bb 100644
--- a/core/log/multi_logger.odin
+++ b/core/log/multi_logger.odin
@@ -5,17 +5,17 @@ Multi_Logger_Data :: struct {
loggers: []Logger,
}
-create_multi_logger :: proc(logs: ..Logger) -> Logger {
- data := new(Multi_Logger_Data)
- data.loggers = make([]Logger, len(logs))
+create_multi_logger :: proc(logs: ..Logger, allocator := context.allocator) -> Logger {
+ data := new(Multi_Logger_Data, allocator)
+ data.loggers = make([]Logger, len(logs), allocator)
copy(data.loggers, logs)
return Logger{multi_logger_proc, data, Level.Debug, nil}
}
-destroy_multi_logger :: proc(log: Logger) {
+destroy_multi_logger :: proc(log: Logger, allocator := context.allocator) {
data := (^Multi_Logger_Data)(log.data)
- delete(data.loggers)
- free(data)
+ delete(data.loggers, allocator)
+ free(data, allocator)
}
multi_logger_proc :: proc(logger_data: rawptr, level: Level, text: string,
diff --git a/core/math/linalg/general.odin b/core/math/linalg/general.odin
index f82d75bff..4a0150972 100644
--- a/core/math/linalg/general.odin
+++ b/core/math/linalg/general.odin
@@ -167,6 +167,18 @@ vector_triple_product :: proc "contextless" (a, b, c: $T/[$N]$E) -> T where IS_N
length :: proc{vector_length, quaternion_length}
length2 :: proc{vector_length2, quaternion_length2}
+
+@(require_results)
+clamp_length :: proc "contextless" (v: $T/[$N]$E, a: E) -> T where IS_FLOAT(E) {
+ if a <= 0 {
+ return 0
+ }
+
+ m2 := length2(v)
+ return v if (m2 <= a*a) else (v / sqrt(m2) * a) // returns original when m2 is 0
+}
+
+
@(require_results)
projection :: proc "contextless" (x, normal: $T/[$N]$E) -> T where IS_NUMERIC(E) {
return dot(x, normal) / dot(normal, normal) * normal
@@ -405,6 +417,13 @@ adjugate :: proc{
matrix4x4_adjugate,
}
+cofactor :: proc{
+ matrix1x1_cofactor,
+ matrix2x2_cofactor,
+ matrix3x3_cofactor,
+ matrix4x4_cofactor,
+}
+
inverse_transpose :: proc{
matrix1x1_inverse_transpose,
matrix2x2_inverse_transpose,
@@ -467,9 +486,9 @@ matrix3x3_determinant :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (det: T) #
}
@(require_results)
matrix4x4_determinant :: proc "contextless" (m: $M/matrix[4, 4]$T) -> (det: T) #no_bounds_check {
- a := adjugate(m)
+ c := cofactor(m)
for i in 0..<4 {
- det += m[0, i] * a[0, i]
+ det += m[0, i] * c[0, i]
}
return
}
@@ -485,6 +504,47 @@ matrix1x1_adjugate :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) #no_bo
@(require_results)
matrix2x2_adjugate :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) #no_bounds_check {
+ y[0, 0] = +x[1, 1]
+ y[0, 1] = -x[0, 1]
+ y[1, 0] = -x[1, 0]
+ y[1, 1] = +x[0, 0]
+ return
+}
+
+@(require_results)
+matrix3x3_adjugate :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
+ y[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
+ y[1, 0] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
+ y[2, 0] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
+ y[0, 1] = -(m[0, 1] * m[2, 2] - m[2, 1] * m[0, 2])
+ y[1, 1] = +(m[0, 0] * m[2, 2] - m[2, 0] * m[0, 2])
+ y[2, 1] = -(m[0, 0] * m[2, 1] - m[2, 0] * m[0, 1])
+ y[0, 2] = +(m[0, 1] * m[1, 2] - m[1, 1] * m[0, 2])
+ y[1, 2] = -(m[0, 0] * m[1, 2] - m[1, 0] * m[0, 2])
+ y[2, 2] = +(m[0, 0] * m[1, 1] - m[1, 0] * m[0, 1])
+ return
+}
+
+@(require_results)
+matrix4x4_adjugate :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
+ for i in 0..<4 {
+ for j in 0..<4 {
+ sign: T = 1 if (i + j) % 2 == 0 else -1
+ y[i, j] = sign * matrix_minor(x, j, i)
+ }
+ }
+ return
+}
+
+
+@(require_results)
+matrix1x1_cofactor :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) #no_bounds_check {
+ y = x
+ return
+}
+
+@(require_results)
+matrix2x2_cofactor :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) #no_bounds_check {
y[0, 0] = +x[1, 1]
y[0, 1] = -x[1, 0]
y[1, 0] = -x[0, 1]
@@ -493,7 +553,7 @@ matrix2x2_adjugate :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) #no_bo
}
@(require_results)
-matrix3x3_adjugate :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
+matrix3x3_cofactor :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
y[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
y[0, 1] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
y[0, 2] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
@@ -508,7 +568,7 @@ matrix3x3_adjugate :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) #no_bo
@(require_results)
-matrix4x4_adjugate :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
+matrix4x4_cofactor :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
for i in 0..<4 {
for j in 0..<4 {
sign: T = 1 if (i + j) % 2 == 0 else -1
@@ -544,19 +604,19 @@ matrix2x2_inverse_transpose :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y:
@(require_results)
matrix3x3_inverse_transpose :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
- a := adjugate(x)
+ c := cofactor(x)
d := determinant(x)
when intrinsics.type_is_integer(T) {
for i in 0..<3 {
for j in 0..<3 {
- y[i, j] = a[i, j] / d
+ y[i, j] = c[i, j] / d
}
}
} else {
id := 1/d
for i in 0..<3 {
for j in 0..<3 {
- y[i, j] = a[i, j] * id
+ y[i, j] = c[i, j] * id
}
}
}
@@ -565,22 +625,22 @@ matrix3x3_inverse_transpose :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y:
@(require_results)
matrix4x4_inverse_transpose :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
- a := adjugate(x)
+ c := cofactor(x)
d: T
for i in 0..<4 {
- d += x[0, i] * a[0, i]
+ d += x[0, i] * c[0, i]
}
when intrinsics.type_is_integer(T) {
for i in 0..<4 {
for j in 0..<4 {
- y[i, j] = a[i, j] / d
+ y[i, j] = c[i, j] / d
}
}
} else {
id := 1/d
for i in 0..<4 {
for j in 0..<4 {
- y[i, j] = a[i, j] * id
+ y[i, j] = c[i, j] * id
}
}
}
@@ -613,19 +673,19 @@ matrix2x2_inverse :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) #no_bou
@(require_results)
matrix3x3_inverse :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
- a := adjugate(x)
+ c := cofactor(x)
d := determinant(x)
when intrinsics.type_is_integer(T) {
for i in 0..<3 {
for j in 0..<3 {
- y[i, j] = a[j, i] / d
+ y[i, j] = c[j, i] / d
}
}
} else {
id := 1/d
for i in 0..<3 {
for j in 0..<3 {
- y[i, j] = a[j, i] * id
+ y[i, j] = c[j, i] * id
}
}
}
@@ -634,22 +694,22 @@ matrix3x3_inverse :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bou
@(require_results)
matrix4x4_inverse :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
- a := adjugate(x)
+ c := cofactor(x)
d: T
for i in 0..<4 {
- d += x[0, i] * a[0, i]
+ d += x[0, i] * c[0, i]
}
when intrinsics.type_is_integer(T) {
for i in 0..<4 {
for j in 0..<4 {
- y[i, j] = a[j, i] / d
+ y[i, j] = c[j, i] / d
}
}
} else {
id := 1/d
for i in 0..<4 {
for j in 0..<4 {
- y[i, j] = a[j, i] * id
+ y[i, j] = c[j, i] * id
}
}
}
diff --git a/core/math/linalg/glsl/linalg_glsl.odin b/core/math/linalg/glsl/linalg_glsl.odin
index 5444f89e2..bd2cf416a 100644
--- a/core/math/linalg/glsl/linalg_glsl.odin
+++ b/core/math/linalg/glsl/linalg_glsl.odin
@@ -473,6 +473,22 @@ floor :: proc{
@(require_results) floor_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {floor(x.x), floor(x.y), floor(x.z)} }
@(require_results) floor_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {floor(x.x), floor(x.y), floor(x.z), floor(x.w)} }
+trunc :: proc{
+ trunc_f32,
+ trunc_f64,
+ trunc_vec2,
+ trunc_vec3,
+ trunc_vec4,
+ trunc_dvec2,
+ trunc_dvec3,
+ trunc_dvec4,
+}
+@(require_results) trunc_vec2 :: proc "c" (x: vec2) -> vec2 { return {trunc(x.x), trunc(x.y)} }
+@(require_results) trunc_vec3 :: proc "c" (x: vec3) -> vec3 { return {trunc(x.x), trunc(x.y), trunc(x.z)} }
+@(require_results) trunc_vec4 :: proc "c" (x: vec4) -> vec4 { return {trunc(x.x), trunc(x.y), trunc(x.z), trunc(x.w)} }
+@(require_results) trunc_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {trunc(x.x), trunc(x.y)} }
+@(require_results) trunc_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {trunc(x.x), trunc(x.y), trunc(x.z)} }
+@(require_results) trunc_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {trunc(x.x), trunc(x.y), trunc(x.z), trunc(x.w)} }
round :: proc{
@@ -1866,6 +1882,13 @@ adjugate :: proc{
adjugate_matrix4x4,
}
+cofactor :: proc{
+ cofactor_matrix1x1,
+ cofactor_matrix2x2,
+ cofactor_matrix3x3,
+ cofactor_matrix4x4,
+}
+
inverse_transpose :: proc{
inverse_transpose_matrix1x1,
inverse_transpose_matrix2x2,
@@ -1928,9 +1951,9 @@ determinant_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (det: T) {
}
@(require_results)
determinant_matrix4x4 :: proc "contextless" (m: $M/matrix[4, 4]$T) -> (det: T) {
- a := adjugate(m)
+ c := cofactor(m)
#no_bounds_check for i in 0..<4 {
- det += m[0, i] * a[0, i]
+ det += m[0, i] * c[0, i]
}
return
}
@@ -1946,6 +1969,47 @@ adjugate_matrix1x1 :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
@(require_results)
adjugate_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
+ y[0, 0] = +x[1, 1]
+ y[0, 1] = -x[0, 1]
+ y[1, 0] = -x[1, 0]
+ y[1, 1] = +x[0, 0]
+ return
+}
+
+@(require_results)
+adjugate_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
+ y[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
+ y[1, 0] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
+ y[2, 0] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
+ y[0, 1] = -(m[0, 1] * m[2, 2] - m[2, 1] * m[0, 2])
+ y[1, 1] = +(m[0, 0] * m[2, 2] - m[2, 0] * m[0, 2])
+ y[2, 1] = -(m[0, 0] * m[2, 1] - m[2, 0] * m[0, 1])
+ y[0, 2] = +(m[0, 1] * m[1, 2] - m[1, 1] * m[0, 2])
+ y[1, 2] = -(m[0, 0] * m[1, 2] - m[1, 0] * m[0, 2])
+ y[2, 2] = +(m[0, 0] * m[1, 1] - m[1, 0] * m[0, 1])
+ return
+}
+
+@(require_results)
+adjugate_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
+ for i in 0..<4 {
+ for j in 0..<4 {
+ sign: T = 1 if (i + j) % 2 == 0 else -1
+ y[i, j] = sign * matrix_minor(x, j, i)
+ }
+ }
+ return
+}
+
+
+@(require_results)
+cofactor_matrix1x1 :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
+ y = x
+ return
+}
+
+@(require_results)
+cofactor_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
y[0, 0] = +x[1, 1]
y[0, 1] = -x[1, 0]
y[1, 0] = -x[0, 1]
@@ -1954,7 +2018,7 @@ adjugate_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
}
@(require_results)
-adjugate_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
+cofactor_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
y[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
y[0, 1] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
y[0, 2] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
@@ -1969,7 +2033,7 @@ adjugate_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
@(require_results)
-adjugate_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
+cofactor_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
for i in 0..<4 {
for j in 0..<4 {
sign: T = 1 if (i + j) % 2 == 0 else -1
@@ -2005,19 +2069,19 @@ inverse_transpose_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y:
@(require_results)
inverse_transpose_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
- a := adjugate(x)
+ c := cofactor(x)
d := determinant(x)
when intrinsics.type_is_integer(T) {
for i in 0..<3 {
for j in 0..<3 {
- y[i, j] = a[i, j] / d
+ y[i, j] = c[i, j] / d
}
}
} else {
id := 1/d
for i in 0..<3 {
for j in 0..<3 {
- y[i, j] = a[i, j] * id
+ y[i, j] = c[i, j] * id
}
}
}
@@ -2026,22 +2090,22 @@ inverse_transpose_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y:
@(require_results)
inverse_transpose_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
- a := adjugate(x)
+ c := cofactor(x)
d: T
for i in 0..<4 {
- d += x[0, i] * a[0, i]
+ d += x[0, i] * c[0, i]
}
when intrinsics.type_is_integer(T) {
for i in 0..<4 {
for j in 0..<4 {
- y[i, j] = a[i, j] / d
+ y[i, j] = c[i, j] / d
}
}
} else {
id := 1/d
for i in 0..<4 {
for j in 0..<4 {
- y[i, j] = a[i, j] * id
+ y[i, j] = c[i, j] * id
}
}
}
@@ -2074,19 +2138,19 @@ inverse_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
@(require_results)
inverse_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
- a := adjugate(x)
+ c := cofactor(x)
d := determinant(x)
when intrinsics.type_is_integer(T) {
for i in 0..<3 {
for j in 0..<3 {
- y[i, j] = a[j, i] / d
+ y[i, j] = c[j, i] / d
}
}
} else {
id := 1/d
for i in 0..<3 {
for j in 0..<3 {
- y[i, j] = a[j, i] * id
+ y[i, j] = c[j, i] * id
}
}
}
@@ -2095,22 +2159,22 @@ inverse_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bou
@(require_results)
inverse_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
- a := adjugate(x)
+ c := cofactor(x)
d: T
for i in 0..<4 {
- d += x[0, i] * a[0, i]
+ d += x[0, i] * c[0, i]
}
when intrinsics.type_is_integer(T) {
for i in 0..<4 {
for j in 0..<4 {
- y[i, j] = a[j, i] / d
+ y[i, j] = c[j, i] / d
}
}
} else {
id := 1/d
for i in 0..<4 {
for j in 0..<4 {
- y[i, j] = a[j, i] * id
+ y[i, j] = c[j, i] * id
}
}
}
diff --git a/core/math/linalg/glsl/linalg_glsl_math.odin b/core/math/linalg/glsl/linalg_glsl_math.odin
index 82b1857ab..b4461ca3b 100644
--- a/core/math/linalg/glsl/linalg_glsl_math.odin
+++ b/core/math/linalg/glsl/linalg_glsl_math.odin
@@ -23,6 +23,7 @@ import "core:math"
@(require_results) exp2_f32 :: proc "c" (x: f32) -> f32 { return math.pow(f32(2), x) }
@(require_results) sign_f32 :: proc "c" (x: f32) -> f32 { return math.sign(x) }
@(require_results) floor_f32 :: proc "c" (x: f32) -> f32 { return math.floor(x) }
+@(require_results) trunc_f32 :: proc "c" (x: f32) -> f32 { return math.trunc(x) }
@(require_results) round_f32 :: proc "c" (x: f32) -> f32 { return math.round(x) }
@(require_results) ceil_f32 :: proc "c" (x: f32) -> f32 { return math.ceil(x) }
@(require_results) mod_f32 :: proc "c" (x, y: f32) -> f32 { return math.mod(x, y) }
@@ -55,6 +56,7 @@ fract_f32 :: proc "c" (x: f32) -> f32 {
@(require_results) exp2_f64 :: proc "c" (x: f64) -> f64 { return math.pow(f64(2), x) }
@(require_results) sign_f64 :: proc "c" (x: f64) -> f64 { return math.sign(x) }
@(require_results) floor_f64 :: proc "c" (x: f64) -> f64 { return math.floor(x) }
+@(require_results) trunc_f64 :: proc "c" (x: f64) -> f64 { return math.trunc(x) }
@(require_results) round_f64 :: proc "c" (x: f64) -> f64 { return math.round(x) }
@(require_results) ceil_f64 :: proc "c" (x: f64) -> f64 { return math.ceil(x) }
@(require_results) mod_f64 :: proc "c" (x, y: f64) -> f64 { return math.mod(x, y) }
diff --git a/core/math/linalg/hlsl/linalg_hlsl.odin b/core/math/linalg/hlsl/linalg_hlsl.odin
index a89fdddd3..cca70f9c8 100644
--- a/core/math/linalg/hlsl/linalg_hlsl.odin
+++ b/core/math/linalg/hlsl/linalg_hlsl.odin
@@ -1514,6 +1514,13 @@ adjugate :: proc{
adjugate_matrix4x4,
}
+cofactor :: proc{
+ cofactor_matrix1x1,
+ cofactor_matrix2x2,
+ cofactor_matrix3x3,
+ cofactor_matrix4x4,
+}
+
inverse_transpose :: proc{
inverse_transpose_matrix1x1,
inverse_transpose_matrix2x2,
@@ -1568,9 +1575,9 @@ determinant_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (det: T) {
}
@(require_results)
determinant_matrix4x4 :: proc "contextless" (m: $M/matrix[4, 4]$T) -> (det: T) {
- a := adjugate(m)
+ c := cofactor(m)
#no_bounds_check for i in 0..<4 {
- det += m[0, i] * a[0, i]
+ det += m[0, i] * c[0, i]
}
return
}
@@ -1586,6 +1593,47 @@ adjugate_matrix1x1 :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
@(require_results)
adjugate_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
+ y[0, 0] = +x[1, 1]
+ y[0, 1] = -x[0, 1]
+ y[1, 0] = -x[1, 0]
+ y[1, 1] = +x[0, 0]
+ return
+}
+
+@(require_results)
+adjugate_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
+ y[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
+ y[1, 0] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
+ y[2, 0] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
+ y[0, 1] = -(m[0, 1] * m[2, 2] - m[2, 1] * m[0, 2])
+ y[1, 1] = +(m[0, 0] * m[2, 2] - m[2, 0] * m[0, 2])
+ y[2, 1] = -(m[0, 0] * m[2, 1] - m[2, 0] * m[0, 1])
+ y[0, 2] = +(m[0, 1] * m[1, 2] - m[1, 1] * m[0, 2])
+ y[1, 2] = -(m[0, 0] * m[1, 2] - m[1, 0] * m[0, 2])
+ y[2, 2] = +(m[0, 0] * m[1, 1] - m[1, 0] * m[0, 1])
+ return
+}
+
+@(require_results)
+adjugate_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
+ for i in 0..<4 {
+ for j in 0..<4 {
+ sign: T = 1 if (i + j) % 2 == 0 else -1
+ y[i, j] = sign * matrix_minor(x, j, i)
+ }
+ }
+ return
+}
+
+
+@(require_results)
+cofactor_matrix1x1 :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
+ y = x
+ return
+}
+
+@(require_results)
+cofactor_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
y[0, 0] = +x[1, 1]
y[0, 1] = -x[1, 0]
y[1, 0] = -x[0, 1]
@@ -1594,7 +1642,7 @@ adjugate_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
}
@(require_results)
-adjugate_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
+cofactor_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
y[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
y[0, 1] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
y[0, 2] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
@@ -1609,7 +1657,7 @@ adjugate_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
@(require_results)
-adjugate_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
+cofactor_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
for i in 0..<4 {
for j in 0..<4 {
sign: T = 1 if (i + j) % 2 == 0 else -1
@@ -1645,19 +1693,19 @@ inverse_transpose_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y:
@(require_results)
inverse_transpose_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
- a := adjugate(x)
+ c := cofactor(x)
d := determinant(x)
when intrinsics.type_is_integer(T) {
for i in 0..<3 {
for j in 0..<3 {
- y[i, j] = a[i, j] / d
+ y[i, j] = c[i, j] / d
}
}
} else {
id := 1/d
for i in 0..<3 {
for j in 0..<3 {
- y[i, j] = a[i, j] * id
+ y[i, j] = c[i, j] * id
}
}
}
@@ -1666,22 +1714,22 @@ inverse_transpose_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y:
@(require_results)
inverse_transpose_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
- a := adjugate(x)
+ c := cofactor(x)
d: T
for i in 0..<4 {
- d += x[0, i] * a[0, i]
+ d += x[0, i] * c[0, i]
}
when intrinsics.type_is_integer(T) {
for i in 0..<4 {
for j in 0..<4 {
- y[i, j] = a[i, j] / d
+ y[i, j] = c[i, j] / d
}
}
} else {
id := 1/d
for i in 0..<4 {
for j in 0..<4 {
- y[i, j] = a[i, j] * id
+ y[i, j] = c[i, j] * id
}
}
}
@@ -1714,19 +1762,19 @@ inverse_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
@(require_results)
inverse_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
- a := adjugate(x)
+ c := cofactor(x)
d := determinant(x)
when intrinsics.type_is_integer(T) {
for i in 0..<3 {
for j in 0..<3 {
- y[i, j] = a[j, i] / d
+ y[i, j] = c[j, i] / d
}
}
} else {
id := 1/d
for i in 0..<3 {
for j in 0..<3 {
- y[i, j] = a[j, i] * id
+ y[i, j] = c[j, i] * id
}
}
}
@@ -1735,22 +1783,22 @@ inverse_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bou
@(require_results)
inverse_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
- a := adjugate(x)
+ c := cofactor(x)
d: T
for i in 0..<4 {
- d += x[0, i] * a[0, i]
+ d += x[0, i] * c[0, i]
}
when intrinsics.type_is_integer(T) {
for i in 0..<4 {
for j in 0..<4 {
- y[i, j] = a[j, i] / d
+ y[i, j] = c[j, i] / d
}
}
} else {
id := 1/d
for i in 0..<4 {
for j in 0..<4 {
- y[i, j] = a[j, i] * id
+ y[i, j] = c[j, i] * id
}
}
}
diff --git a/core/math/linalg/specific.odin b/core/math/linalg/specific.odin
index b841f0610..c23feddce 100644
--- a/core/math/linalg/specific.odin
+++ b/core/math/linalg/specific.odin
@@ -1207,8 +1207,8 @@ matrix2_inverse_f16 :: proc "contextless" (m: Matrix2f16) -> (c: Matrix2f16) #no
d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
id := 1.0/d
c[0, 0] = +m[1, 1] * id
- c[0, 1] = -m[1, 0] * id
- c[1, 0] = -m[0, 1] * id
+ c[0, 1] = -m[0, 1] * id
+ c[1, 0] = -m[1, 0] * id
c[1, 1] = +m[0, 0] * id
return c
}
@@ -1217,8 +1217,8 @@ matrix2_inverse_f32 :: proc "contextless" (m: Matrix2f32) -> (c: Matrix2f32) #no
d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
id := 1.0/d
c[0, 0] = +m[1, 1] * id
- c[0, 1] = -m[1, 0] * id
- c[1, 0] = -m[0, 1] * id
+ c[0, 1] = -m[0, 1] * id
+ c[1, 0] = -m[1, 0] * id
c[1, 1] = +m[0, 0] * id
return c
}
@@ -1227,8 +1227,8 @@ matrix2_inverse_f64 :: proc "contextless" (m: Matrix2f64) -> (c: Matrix2f64) #no
d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
id := 1.0/d
c[0, 0] = +m[1, 1] * id
- c[0, 1] = -m[1, 0] * id
- c[1, 0] = -m[0, 1] * id
+ c[0, 1] = -m[0, 1] * id
+ c[1, 0] = -m[1, 0] * id
c[1, 1] = +m[0, 0] * id
return c
}
diff --git a/core/math/math.odin b/core/math/math.odin
index 0e21afa67..b99a97bc1 100644
--- a/core/math/math.odin
+++ b/core/math/math.odin
@@ -1271,7 +1271,7 @@ binomial :: proc "contextless" (n, k: int) -> int {
}
b := n
- for i in 2.. (r: f16) {
case x == y:
r = x
case x == 0:
- r = copy_sign_f16(1, y)
+ r = copy_sign_f16(transmute(f16)u16(1), y)
case (y > x) == (x > 0):
r = transmute(f16)(transmute(u16)x + 1)
case:
@@ -2312,7 +2312,7 @@ nextafter_f32 :: proc "contextless" (x, y: f32) -> (r: f32) {
case x == y:
r = x
case x == 0:
- r = copy_sign_f32(1, y)
+ r = copy_sign_f32(transmute(f32)u32(1), y)
case (y > x) == (x > 0):
r = transmute(f32)(transmute(u32)x + 1)
case:
@@ -2328,7 +2328,7 @@ nextafter_f64 :: proc "contextless" (x, y: f64) -> (r: f64) {
case x == y:
r = x
case x == 0:
- r = copy_sign_f64(1, y)
+ r = copy_sign_f64(transmute(f64)u64(1), y)
case (y > x) == (x > 0):
r = transmute(f64)(transmute(u64)x + 1)
case:
@@ -2349,32 +2349,6 @@ nextafter :: proc{
nextafter_f64, nextafter_f64le, nextafter_f64be,
}
-@(require_results)
-signbit_f16 :: proc "contextless" (x: f16) -> bool {
- return (transmute(u16)x)&(1<<15) != 0
-}
-@(require_results)
-signbit_f32 :: proc "contextless" (x: f32) -> bool {
- return (transmute(u32)x)&(1<<31) != 0
-}
-@(require_results)
-signbit_f64 :: proc "contextless" (x: f64) -> bool {
- return (transmute(u64)x)&(1<<63) != 0
-}
-@(require_results) signbit_f16le :: proc "contextless" (x: f16le) -> bool { return signbit_f16(f16(x)) }
-@(require_results) signbit_f32le :: proc "contextless" (x: f32le) -> bool { return signbit_f32(f32(x)) }
-@(require_results) signbit_f64le :: proc "contextless" (x: f64le) -> bool { return signbit_f64(f64(x)) }
-@(require_results) signbit_f16be :: proc "contextless" (x: f16be) -> bool { return signbit_f16(f16(x)) }
-@(require_results) signbit_f32be :: proc "contextless" (x: f32be) -> bool { return signbit_f32(f32(x)) }
-@(require_results) signbit_f64be :: proc "contextless" (x: f64be) -> bool { return signbit_f64(f64(x)) }
-
-signbit :: proc{
- signbit_f16, signbit_f16le, signbit_f16be,
- signbit_f32, signbit_f32le, signbit_f32be,
- signbit_f64, signbit_f64le, signbit_f64be,
-}
-
-
@(require_results)
hypot_f16 :: proc "contextless" (x, y: f16) -> (r: f16) {
p, q := abs(x), abs(y)
diff --git a/core/math/math_gamma.odin b/core/math/math_gamma.odin
index 9f5a364d3..9e8c2a909 100644
--- a/core/math/math_gamma.odin
+++ b/core/math/math_gamma.odin
@@ -132,7 +132,7 @@ gamma_f64 :: proc "contextless" (x: f64) -> f64 {
case is_inf(x, 1):
return inf_f64(1)
case x == 0:
- if signbit(x) {
+ if sign_bit(x) {
return inf_f64(-1)
}
return inf_f64(1)
diff --git a/core/math/rand/rand.odin b/core/math/rand/rand.odin
index 474277e84..56208931f 100644
--- a/core/math/rand/rand.odin
+++ b/core/math/rand/rand.odin
@@ -16,9 +16,9 @@ Generator_Query_Info :: runtime.Random_Generator_Query_Info
Default_Random_State :: runtime.Default_Random_State
default_random_generator :: runtime.default_random_generator
+@(require_results)
create :: proc(seed: u64) -> (state: Default_Random_State) {
seed := seed
- runtime.default_random_generator(&state)
runtime.default_random_generator_proc(&state, .Reset, ([^]byte)(&seed)[:size_of(seed)])
return
}
@@ -33,31 +33,7 @@ Example:
import "core:math/rand"
import "core:fmt"
- set_global_seed_example :: proc() {
- rand.set_global_seed(1)
- fmt.println(rand.uint64())
- }
-
-Possible Output:
-
- 10
-*/
-@(deprecated="Prefer `rand.reset`")
-set_global_seed :: proc(seed: u64) {
- runtime.random_generator_reset_u64(context.random_generator, seed)
-}
-
-/*
-Reset the seed used by the context.random_generator.
-
-Inputs:
-- seed: The seed value
-
-Example:
- import "core:math/rand"
- import "core:fmt"
-
- set_global_seed_example :: proc() {
+ reset_example :: proc() {
rand.reset(1)
fmt.println(rand.uint64())
}
@@ -456,7 +432,7 @@ Example:
Possible Output:
15.312
- 673.130
+ 273.15
*/
@(require_results) float64_range :: proc(low, high: f64, gen := context.random_generator) -> (val: f64) {
@@ -491,7 +467,7 @@ Example:
Possible Output:
15.312
- 673.130
+ 273.15
*/
@(require_results) float32_range :: proc(low, high: f32, gen := context.random_generator) -> (val: f32) {
diff --git a/core/mem/alloc.odin b/core/mem/alloc.odin
index fac58daaf..48cc39245 100644
--- a/core/mem/alloc.odin
+++ b/core/mem/alloc.odin
@@ -785,6 +785,27 @@ delete_map :: proc(
return runtime.delete_map(m, loc)
}
+/*
+Free an SoA slice.
+*/
+delete_soa_slice :: proc(
+ array: $T/#soa[]$E,
+ allocator := context.allocator,
+ loc := #caller_location,
+) -> Allocator_Error {
+ return runtime.delete_soa_slice(array, allocator, loc)
+}
+
+/*
+Free an SoA dynamic array.
+*/
+delete_soa_dynamic_array :: proc(
+ array: $T/#soa[dynamic]$E,
+ loc := #caller_location,
+) -> Allocator_Error {
+ return runtime.delete_soa_dynamic_array(array, loc)
+}
+
/*
Free.
*/
@@ -794,6 +815,8 @@ delete :: proc{
delete_dynamic_array,
delete_slice,
delete_map,
+ delete_soa_slice,
+ delete_soa_dynamic_array,
}
/*
@@ -865,6 +888,34 @@ make_aligned :: proc(
return runtime.make_aligned(T, len, alignment, allocator, loc)
}
+
+/*
+Allocate a new slice with alignment for allocators that might not support the
+specified alignment requirement.
+
+This procedure allocates a new slice of type `T` with length `len`, aligned
+on a boundary specified by `alignment` from an allocator specified by
+`allocator`, and returns the allocated slice.
+
+The user should `delete` the return `original_data` slice not the typed `slice`.
+*/
+@(require_results)
+make_over_aligned :: proc(
+ $T: typeid/[]$E,
+ #any_int len: int,
+ alignment: int,
+ allocator: runtime.Allocator,
+ loc := #caller_location,
+) -> (slice: T, original_data: []byte, err: Allocator_Error) {
+ size := size_of(E)*len + alignment-1
+ original_data, err = runtime.make([]byte, size, allocator, loc)
+ if err == nil {
+ ptr := align_forward(raw_data(original_data), uintptr(alignment))
+ slice = ([^]E)(ptr)[:len]
+ }
+ return
+}
+
/*
Allocate a new slice.
@@ -900,8 +951,7 @@ make_dynamic_array :: proc(
Allocate a dynamic array with initial length.
This procedure creates a dynamic array of type `T`, with `allocator` as its
-backing allocator, and initial capacity of `0`, and initial length specified by
-`len`.
+backing allocator, and initial capacity and length specified by `len`.
*/
@(require_results)
make_dynamic_array_len :: proc(
@@ -910,7 +960,7 @@ make_dynamic_array_len :: proc(
allocator := context.allocator,
loc := #caller_location,
) -> (T, Allocator_Error) {
- return runtime.make_dynamic_array_len_cap(T, len, len, allocator, loc)
+ return runtime.make_dynamic_array_len(T, len, allocator, loc)
}
/*
@@ -931,6 +981,22 @@ make_dynamic_array_len_cap :: proc(
return runtime.make_dynamic_array_len_cap(T, len, cap, allocator, loc)
}
+/*
+Create a map with no initial allocation.
+
+This procedure creates a map of type `T` with no initial allocation, which will
+use the allocator specified by `allocator` as its backing allocator when it
+allocates.
+*/
+@(require_results)
+make_map :: proc(
+ $T: typeid/map[$K]$E,
+ allocator := context.allocator,
+ loc := #caller_location,
+) -> (m: T) {
+ return runtime.make_map(T, allocator, loc)
+}
+
/*
Allocate a map.
@@ -939,13 +1005,13 @@ This procedure creates a map of type `T` with initial capacity specified by
allocator.
*/
@(require_results)
-make_map :: proc(
+make_map_cap :: proc(
$T: typeid/map[$K]$E,
- #any_int cap: int = 1< (m: T, err: Allocator_Error) {
- return runtime.make_map(T, cap, allocator, loc)
+ return runtime.make_map_cap(T, cap, allocator, loc)
}
/*
@@ -964,6 +1030,71 @@ make_multi_pointer :: proc(
return runtime.make_multi_pointer(T, len, allocator, loc)
}
+/*
+Allocate an SoA slice.
+
+This procedure allocates an SoA slice of type `T` with length `len`, from an
+allocator specified by `allocator`, and returns the allocated SoA slice.
+*/
+@(require_results)
+make_soa_slice :: proc(
+ $T: typeid/#soa[]$E,
+ #any_int len: int,
+ allocator := context.allocator,
+ loc := #caller_location
+) -> (array: T, err: Allocator_Error) {
+ return runtime.make_soa_slice(T, len, allocator, loc)
+}
+
+/*
+Allocate an SoA dynamic array.
+
+This procedure creates an SoA dynamic array of type `T`, with `allocator` as
+its backing allocator, and initial length and capacity of `0`.
+*/
+@(require_results)
+make_soa_dynamic_array :: proc(
+ $T: typeid/#soa[dynamic]$E,
+ allocator := context.allocator,
+ loc := #caller_location
+) -> (array: T, err: Allocator_Error) {
+ return runtime.make_soa_dynamic_array(T, allocator, loc)
+}
+
+/*
+Allocate an SoA dynamic array with initial length.
+
+This procedure creates an SoA dynamic array of type `T`, with `allocator` as its
+backing allocator, and initial capacity and length specified by `len`.
+*/
+@(require_results)
+make_soa_dynamic_array_len :: proc(
+ $T: typeid/#soa[dynamic]$E,
+ #any_int len: int,
+ allocator := context.allocator,
+ loc := #caller_location
+) -> (array: T, err: Allocator_Error) {
+ return runtime.make_soa_dynamic_array_len(T, len, allocator, loc)
+}
+
+/*
+Allocate an SoA dynamic array with initial length and capacity.
+
+This procedure creates an SoA dynamic array of type `T`, with `allocator` as its
+backing allocator, and initial capacity specified by `cap`, and initial length
+specified by `len`.
+*/
+@(require_results)
+make_soa_dynamic_array_len_cap :: proc(
+ $T: typeid/#soa[dynamic]$E,
+ #any_int len: int,
+ #any_int cap: int,
+ allocator := context.allocator,
+ loc := #caller_location
+) -> (array: T, err: Allocator_Error) {
+ return runtime.make_soa_dynamic_array_len_cap(T, len, cap, allocator, loc)
+}
+
/*
Allocate.
*/
@@ -973,7 +1104,12 @@ make :: proc{
make_dynamic_array_len,
make_dynamic_array_len_cap,
make_map,
+ make_map_cap,
make_multi_pointer,
+ make_soa_slice,
+ make_soa_dynamic_array,
+ make_soa_dynamic_array_len,
+ make_soa_dynamic_array_len_cap,
}
/*
diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin
index 13d509f1e..1bb887212 100644
--- a/core/mem/allocators.odin
+++ b/core/mem/allocators.odin
@@ -2,6 +2,7 @@ package mem
import "base:intrinsics"
import "base:runtime"
+import "base:sanitizer"
/*
Nil allocator.
@@ -138,14 +139,7 @@ arena_init :: proc(a: ^Arena, data: []byte) {
a.offset = 0
a.peak_used = 0
a.temp_count = 0
-}
-
-@(deprecated="prefer 'mem.arena_init'")
-init_arena :: proc(a: ^Arena, data: []byte) {
- a.data = data
- a.offset = 0
- a.peak_used = 0
- a.temp_count = 0
+ sanitizer.address_poison(a.data)
}
/*
@@ -232,7 +226,9 @@ arena_alloc_bytes_non_zeroed :: proc(
}
a.offset += total_size
a.peak_used = max(a.peak_used, a.offset)
- return byte_slice(ptr, size), nil
+ result := byte_slice(ptr, size)
+ sanitizer.address_unpoison(result)
+ return result, nil
}
/*
@@ -240,6 +236,7 @@ Free all memory to an arena.
*/
arena_free_all :: proc(a: ^Arena) {
a.offset = 0
+ sanitizer.address_poison(a.data)
}
arena_allocator_proc :: proc(
@@ -317,6 +314,7 @@ allocations *inside* the temporary memory region will be freed to the arena.
end_arena_temp_memory :: proc(tmp: Arena_Temp_Memory) {
assert(tmp.arena.offset >= tmp.prev_offset)
assert(tmp.arena.temp_count > 0)
+ sanitizer.address_poison(tmp.arena.data[tmp.prev_offset:tmp.arena.offset])
tmp.arena.offset = tmp.prev_offset
tmp.arena.temp_count -= 1
}
@@ -371,6 +369,7 @@ scratch_init :: proc(s: ^Scratch, size: int, backup_allocator := context.allocat
s.prev_allocation = nil
s.backup_allocator = backup_allocator
s.leaked_allocations.allocator = backup_allocator
+ sanitizer.address_poison(s.data)
return nil
}
@@ -385,6 +384,7 @@ scratch_destroy :: proc(s: ^Scratch) {
free_bytes(ptr, s.backup_allocator)
}
delete(s.leaked_allocations)
+ sanitizer.address_unpoison(s.data)
delete(s.data, s.backup_allocator)
s^ = {}
}
@@ -480,7 +480,9 @@ scratch_alloc_bytes_non_zeroed :: proc(
ptr := align_forward_uintptr(offset+start, uintptr(alignment))
s.prev_allocation = rawptr(ptr)
s.curr_offset = int(offset) + size
- return byte_slice(rawptr(ptr), size), nil
+ result := byte_slice(rawptr(ptr), size)
+ sanitizer.address_unpoison(result)
+ return result, nil
} else {
a := s.backup_allocator
if a.procedure == nil {
@@ -524,6 +526,7 @@ scratch_free :: proc(s: ^Scratch, ptr: rawptr, loc := #caller_location) -> Alloc
old_ptr := uintptr(ptr)
if s.prev_allocation == ptr {
s.curr_offset = int(uintptr(s.prev_allocation) - start)
+ sanitizer.address_poison(s.data[s.curr_offset:])
s.prev_allocation = nil
return nil
}
@@ -554,6 +557,7 @@ scratch_free_all :: proc(s: ^Scratch, loc := #caller_location) {
free_bytes(ptr, s.backup_allocator, loc)
}
clear(&s.leaked_allocations)
+ sanitizer.address_poison(s.data)
}
/*
@@ -683,7 +687,9 @@ scratch_resize_bytes_non_zeroed :: proc(
old_ptr := uintptr(old_memory)
if begin <= old_ptr && old_ptr < end && old_ptr+uintptr(size) < end {
s.curr_offset = int(old_ptr-begin)+size
- return byte_slice(old_memory, size), nil
+ result := byte_slice(old_memory, size)
+ sanitizer.address_unpoison(result)
+ return result, nil
}
data, err := scratch_alloc_bytes_non_zeroed(s, size, alignment, loc)
if err != nil {
@@ -784,14 +790,7 @@ stack_init :: proc(s: ^Stack, data: []byte) {
s.prev_offset = 0
s.curr_offset = 0
s.peak_used = 0
-}
-
-@(deprecated="prefer 'mem.stack_init'")
-init_stack :: proc(s: ^Stack, data: []byte) {
- s.data = data
- s.prev_offset = 0
- s.curr_offset = 0
- s.peak_used = 0
+ sanitizer.address_poison(data)
}
/*
@@ -877,15 +876,19 @@ stack_alloc_bytes_non_zeroed :: proc(
if s.curr_offset + padding + size > len(s.data) {
return nil, .Out_Of_Memory
}
+ old_offset := s.prev_offset
s.prev_offset = s.curr_offset
s.curr_offset += padding
next_addr := curr_addr + uintptr(padding)
header := (^Stack_Allocation_Header)(next_addr - size_of(Stack_Allocation_Header))
+ sanitizer.address_unpoison(header)
header.padding = padding
- header.prev_offset = s.prev_offset
+ header.prev_offset = old_offset
s.curr_offset += size
s.peak_used = max(s.peak_used, s.curr_offset)
- return byte_slice(rawptr(next_addr), size), nil
+ result := byte_slice(rawptr(next_addr), size)
+ sanitizer.address_unpoison(result)
+ return result, nil
}
/*
@@ -918,12 +921,15 @@ stack_free :: proc(
}
header := (^Stack_Allocation_Header)(curr_addr - size_of(Stack_Allocation_Header))
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)))
- if old_offset != header.prev_offset {
+ if old_offset != s.prev_offset {
// panic("Out of order stack allocator free");
return .Invalid_Pointer
}
- s.curr_offset = old_offset
+
s.prev_offset = header.prev_offset
+ sanitizer.address_poison(s.data[old_offset:s.curr_offset])
+ s.curr_offset = old_offset
+
return nil
}
@@ -933,6 +939,7 @@ Free all allocations to the stack.
stack_free_all :: proc(s: ^Stack, loc := #caller_location) {
s.prev_offset = 0
s.curr_offset = 0
+ sanitizer.address_poison(s.data)
}
/*
@@ -1092,7 +1099,9 @@ stack_resize_bytes_non_zeroed :: proc(
if diff > 0 {
zero(rawptr(curr_addr + uintptr(diff)), diff)
}
- return byte_slice(old_memory, size), nil
+ result := byte_slice(old_memory, size)
+ sanitizer.address_unpoison(result)
+ return result, nil
}
stack_allocator_proc :: proc(
@@ -1160,13 +1169,7 @@ small_stack_init :: proc(s: ^Small_Stack, data: []byte) {
s.data = data
s.offset = 0
s.peak_used = 0
-}
-
-@(deprecated="prefer 'small_stack_init'")
-init_small_stack :: proc(s: ^Small_Stack, data: []byte) {
- s.data = data
- s.offset = 0
- s.peak_used = 0
+ sanitizer.address_poison(data)
}
/*
@@ -1275,10 +1278,13 @@ small_stack_alloc_bytes_non_zeroed :: proc(
s.offset += padding
next_addr := curr_addr + uintptr(padding)
header := (^Small_Stack_Allocation_Header)(next_addr - size_of(Small_Stack_Allocation_Header))
+ sanitizer.address_unpoison(header)
header.padding = auto_cast padding
s.offset += size
s.peak_used = max(s.peak_used, s.offset)
- return byte_slice(rawptr(next_addr), size), nil
+ result := byte_slice(rawptr(next_addr), size)
+ sanitizer.address_unpoison(result)
+ return result, nil
}
/*
@@ -1312,6 +1318,7 @@ small_stack_free :: proc(
}
header := (^Small_Stack_Allocation_Header)(curr_addr - size_of(Small_Stack_Allocation_Header))
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)))
+ sanitizer.address_poison(s.data[old_offset:s.offset])
s.offset = old_offset
return nil
}
@@ -1321,6 +1328,7 @@ Free all memory to small stack.
*/
small_stack_free_all :: proc(s: ^Small_Stack) {
s.offset = 0
+ sanitizer.address_poison(s.data)
}
/*
@@ -1465,7 +1473,9 @@ small_stack_resize_bytes_non_zeroed :: proc(
return nil, nil
}
if old_size == size {
- return byte_slice(old_memory, size), nil
+ result := byte_slice(old_memory, size)
+ sanitizer.address_unpoison(result)
+ return result, nil
}
data, err := small_stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
if err == nil {
diff --git a/core/mem/doc.odin b/core/mem/doc.odin
index 98755d797..580b08c96 100644
--- a/core/mem/doc.odin
+++ b/core/mem/doc.odin
@@ -16,8 +16,8 @@ a multipointer can be indexed, but does not have a definite length. A slice is
a pointer that points to multiple objects equipped with the length, specifying
the amount of objects a slice points to.
-When object's values are read through a pointer, that operation is called a
-*load* operation. When memory is read through a pointer, that operation is
+When an object's values are read through a pointer, that operation is called a
+*load* operation. When memory is written to through a pointer, that operation is
called a *store* operation. Both of these operations can be called a *memory
access operation*.
diff --git a/core/mem/mem.odin b/core/mem/mem.odin
index ccbc77798..5789309f7 100644
--- a/core/mem/mem.odin
+++ b/core/mem/mem.odin
@@ -171,16 +171,15 @@ If the return value is:
The comparison is performed as follows:
1. Each byte, upto `min(len(a), len(b))` bytes is compared between `a` and `b`.
- - If the byte in slice `a` is smaller than a byte in slice `b`, then comparison
- stops and this procedure returns `-1`.
- - If the byte in slice `a` is bigger than a byte in slice `b`, then comparison
- stops and this procedure returns `+1`.
- - Otherwise the comparison continues until `min(len(a), len(b))` are compared.
-2. If all the bytes in the range are equal, then the lengths of the slices are
- compared.
- - If the length of slice `a` is smaller than the length of slice `b`, then `-1` is returned.
- - If the length of slice `b` is smaller than the length of slice `b`, then `+1` is returned.
- - Otherwise `0` is returned.
+ - If the byte in slice `a` is smaller than a byte in slice `b`, then comparison
+ stops and this procedure returns `-1`.
+ - If the byte in slice `a` is bigger than a byte in slice `b`, then comparison
+ stops and this procedure returns `+1`.
+ - Otherwise the comparison continues until `min(len(a), len(b))` are compared.
+2. If all the bytes in the range are equal, then the lengths of the slices are compared.
+ - If the length of slice `a` is smaller than the length of slice `b`, then `-1` is returned.
+ - If the length of slice `b` is smaller than the length of slice `b`, then `+1` is returned.
+ - Otherwise `0` is returned.
*/
@(require_results)
compare :: proc "contextless" (a, b: []byte) -> int {
@@ -207,11 +206,11 @@ If the return value is:
The comparison is performed as follows:
1. Each byte, upto `n` bytes is compared between `a` and `b`.
- - If the byte in `a` is smaller than a byte in `b`, then comparison stops
- and this procedure returns `-1`.
- - If the byte in `a` is bigger than a byte in `b`, then comparison stops
- and this procedure returns `+1`.
- - Otherwise the comparison continues until `n` bytes are compared.
+ - If the byte in `a` is smaller than a byte in `b`, then comparison stops
+ and this procedure returns `-1`.
+ - If the byte in `a` is bigger than a byte in `b`, then comparison stops
+ and this procedure returns `+1`.
+ - Otherwise the comparison continues until `n` bytes are compared.
2. If all the bytes in the range are equal, this procedure returns `0`.
*/
@(require_results)
@@ -233,11 +232,11 @@ If the return value is:
The comparison is performed as follows:
1. Each byte, upto `n` bytes is compared between `a` and `b`.
- - If the byte in `a` is smaller than a byte in `b`, then comparison stops
- and this procedure returns `-1`.
- - If the byte in `a` is bigger than a byte in `b`, then comparison stops
- and this procedure returns `+1`.
- - Otherwise the comparison continues until `n` bytes are compared.
+ - If the byte in `a` is smaller than a byte in `b`, then comparison stops
+ and this procedure returns `-1`.
+ - If the byte in `a` is bigger than a byte in `b`, then comparison stops
+ and this procedure returns `+1`.
+ - Otherwise the comparison continues until `n` bytes are compared.
2. If all the bytes in the range are equal, this procedure returns `0`.
*/
@(require_results)
@@ -685,11 +684,4 @@ calc_padding_with_header :: proc "contextless" (ptr: uintptr, align: uintptr, he
}
}
return int(padding)
-}
-
-@(require_results, deprecated="prefer 'slice.clone'")
-clone_slice :: proc(slice: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> (new_slice: T) {
- new_slice, _ = make(T, len(slice), allocator, loc)
- runtime.copy(new_slice, slice)
- return new_slice
-}
+}
\ No newline at end of file
diff --git a/core/mem/tlsf/tlsf.odin b/core/mem/tlsf/tlsf.odin
index 8ec5f52b7..4ce6e54d9 100644
--- a/core/mem/tlsf/tlsf.odin
+++ b/core/mem/tlsf/tlsf.odin
@@ -10,6 +10,7 @@
// package mem_tlsf implements a Two Level Segregated Fit memory allocator.
package mem_tlsf
+import "base:intrinsics"
import "base:runtime"
Error :: enum byte {
@@ -21,7 +22,6 @@ Error :: enum byte {
Backing_Allocator_Error = 5,
}
-
Allocator :: struct {
// Empty lists point at this block to indicate they are free.
block_null: Block_Header,
@@ -39,12 +39,13 @@ Allocator :: struct {
// statistics like how much memory is still available,
// fragmentation, etc.
pool: Pool,
+
+ // If we're expected to grow when we run out of memory,
+ // how much should we ask the backing allocator for?
+ new_pool_size: uint,
}
#assert(size_of(Allocator) % ALIGN_SIZE == 0)
-
-
-
@(require_results)
allocator :: proc(t: ^Allocator) -> runtime.Allocator {
return runtime.Allocator{
@@ -53,6 +54,21 @@ allocator :: proc(t: ^Allocator) -> runtime.Allocator {
}
}
+// Tries to estimate a pool size sufficient for `count` allocations, each of `size` and with `alignment`.
+estimate_pool_from_size_alignment :: proc(count: int, size: int, alignment: int) -> (pool_size: int) {
+ per_allocation := align_up(uint(size + alignment) + BLOCK_HEADER_OVERHEAD, ALIGN_SIZE)
+ return count * int(per_allocation) + int(INITIAL_POOL_OVERHEAD)
+}
+
+// Tries to estimate a pool size sufficient for `count` allocations of `type`.
+estimate_pool_from_typeid :: proc(count: int, type: typeid) -> (pool_size: int) {
+ ti := type_info_of(type)
+ return estimate_pool_size(count, ti.size, ti.align)
+}
+
+estimate_pool_size :: proc{estimate_pool_from_size_alignment, estimate_pool_from_typeid}
+
+
@(require_results)
init_from_buffer :: proc(control: ^Allocator, buf: []byte) -> Error {
assert(control != nil)
@@ -60,21 +76,25 @@ init_from_buffer :: proc(control: ^Allocator, buf: []byte) -> Error {
return .Invalid_Alignment
}
- pool_bytes := align_down(len(buf) - POOL_OVERHEAD, ALIGN_SIZE)
+ pool_bytes := align_down(len(buf) - INITIAL_POOL_OVERHEAD, ALIGN_SIZE)
if pool_bytes < BLOCK_SIZE_MIN {
return .Backing_Buffer_Too_Small
} else if pool_bytes > BLOCK_SIZE_MAX {
return .Backing_Buffer_Too_Large
}
- clear(control)
- return pool_add(control, buf[:])
+ control.pool = Pool{
+ data = buf,
+ allocator = {},
+ }
+
+ return free_all(control)
}
@(require_results)
init_from_allocator :: proc(control: ^Allocator, backing: runtime.Allocator, initial_pool_size: int, new_pool_size := 0) -> Error {
assert(control != nil)
- pool_bytes := align_up(uint(initial_pool_size) + POOL_OVERHEAD, ALIGN_SIZE)
+ pool_bytes := uint(estimate_pool_size(1, initial_pool_size, ALIGN_SIZE))
if pool_bytes < BLOCK_SIZE_MIN {
return .Backing_Buffer_Too_Small
} else if pool_bytes > BLOCK_SIZE_MAX {
@@ -85,12 +105,15 @@ init_from_allocator :: proc(control: ^Allocator, backing: runtime.Allocator, ini
if backing_err != nil {
return .Backing_Allocator_Error
}
- err := init_from_buffer(control, buf)
+
control.pool = Pool{
data = buf,
allocator = backing,
}
- return err
+
+ control.new_pool_size = uint(new_pool_size)
+
+ return free_all(control)
}
init :: proc{init_from_buffer, init_from_allocator}
@@ -103,8 +126,6 @@ destroy :: proc(control: ^Allocator) {
// No need to call `pool_remove` or anything, as they're they're embedded in the backing memory.
// We do however need to free the `Pool` tracking entities and the backing memory itself.
- // As `Allocator` is embedded in the first backing slice, the `control` pointer will be
- // invalid after this call.
for p := control.pool.next; p != nil; {
next := p.next
@@ -136,9 +157,8 @@ allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
return nil, nil
case .Free_All:
- // NOTE: this doesn't work right at the moment, Jeroen has it on his to-do list :)
- // clear(control)
- return nil, .Mode_Not_Implemented
+ free_all(control)
+ return nil, nil
case .Resize:
return resize(control, old_memory, uint(old_size), uint(size), uint(alignment))
@@ -159,3 +179,23 @@ allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
return nil, nil
}
+
+// Exported solely to facilitate testing
+@(require_results)
+ffs :: proc "contextless" (word: u32) -> (bit: i32) {
+ return -1 if word == 0 else i32(intrinsics.count_trailing_zeros(word))
+}
+
+// Exported solely to facilitate testing
+@(require_results)
+fls :: proc "contextless" (word: u32) -> (bit: i32) {
+ N :: (size_of(u32) * 8) - 1
+ return i32(N - intrinsics.count_leading_zeros(word))
+}
+
+// Exported solely to facilitate testing
+@(require_results)
+fls_uint :: proc "contextless" (size: uint) -> (bit: i32) {
+ N :: (size_of(uint) * 8) - 1
+ return i32(N - intrinsics.count_leading_zeros(size))
+}
\ No newline at end of file
diff --git a/core/mem/tlsf/tlsf_internal.odin b/core/mem/tlsf/tlsf_internal.odin
index cac151183..f8a9bf60c 100644
--- a/core/mem/tlsf/tlsf_internal.odin
+++ b/core/mem/tlsf/tlsf_internal.odin
@@ -7,12 +7,10 @@
Jeroen van Rijn: Source port
*/
-
package mem_tlsf
import "base:intrinsics"
import "base:runtime"
-// import "core:fmt"
// log2 of number of linear subdivisions of block sizes.
// Larger values require more memory in the control structure.
@@ -58,7 +56,6 @@ Pool :: struct {
next: ^Pool,
}
-
/*
Block header structure.
@@ -95,6 +92,7 @@ The `prev_phys_block` field is stored *inside* the previous free block.
BLOCK_HEADER_OVERHEAD :: uint(size_of(uint))
POOL_OVERHEAD :: 2 * BLOCK_HEADER_OVERHEAD
+INITIAL_POOL_OVERHEAD :: 48
// User data starts directly after the size field in a used block.
BLOCK_START_OFFSET :: offset_of(Block_Header, size) + size_of(Block_Header{}.size)
@@ -107,436 +105,12 @@ bits for `FL_INDEX`.
BLOCK_SIZE_MIN :: uint(size_of(Block_Header) - size_of(^Block_Header))
BLOCK_SIZE_MAX :: uint(1) << FL_INDEX_MAX
-/*
- TLSF achieves O(1) cost for `alloc` and `free` operations by limiting
- the search for a free block to a free list of guaranteed size
- adequate to fulfill the request, combined with efficient free list
- queries using bitmasks and architecture-specific bit-manipulation
- routines.
-
- NOTE: TLSF spec relies on ffs/fls returning value 0..31.
-*/
-
-@(require_results)
-ffs :: proc "contextless" (word: u32) -> (bit: i32) {
- return -1 if word == 0 else i32(intrinsics.count_trailing_zeros(word))
-}
-
-@(require_results)
-fls :: proc "contextless" (word: u32) -> (bit: i32) {
- N :: (size_of(u32) * 8) - 1
- return i32(N - intrinsics.count_leading_zeros(word))
-}
-
-@(require_results)
-fls_uint :: proc "contextless" (size: uint) -> (bit: i32) {
- N :: (size_of(uint) * 8) - 1
- return i32(N - intrinsics.count_leading_zeros(size))
-}
-
-@(require_results)
-block_size :: proc "contextless" (block: ^Block_Header) -> (size: uint) {
- return block.size &~ (BLOCK_HEADER_FREE | BLOCK_HEADER_PREV_FREE)
-}
-
-block_set_size :: proc "contextless" (block: ^Block_Header, size: uint) {
- old_size := block.size
- block.size = size | (old_size & (BLOCK_HEADER_FREE | BLOCK_HEADER_PREV_FREE))
-}
-
-@(require_results)
-block_is_last :: proc "contextless" (block: ^Block_Header) -> (is_last: bool) {
- return block_size(block) == 0
-}
-
-@(require_results)
-block_is_free :: proc "contextless" (block: ^Block_Header) -> (is_free: bool) {
- return (block.size & BLOCK_HEADER_FREE) == BLOCK_HEADER_FREE
-}
-
-block_set_free :: proc "contextless" (block: ^Block_Header) {
- block.size |= BLOCK_HEADER_FREE
-}
-
-block_set_used :: proc "contextless" (block: ^Block_Header) {
- block.size &~= BLOCK_HEADER_FREE
-}
-
-@(require_results)
-block_is_prev_free :: proc "contextless" (block: ^Block_Header) -> (is_prev_free: bool) {
- return (block.size & BLOCK_HEADER_PREV_FREE) == BLOCK_HEADER_PREV_FREE
-}
-
-block_set_prev_free :: proc "contextless" (block: ^Block_Header) {
- block.size |= BLOCK_HEADER_PREV_FREE
-}
-
-block_set_prev_used :: proc "contextless" (block: ^Block_Header) {
- block.size &~= BLOCK_HEADER_PREV_FREE
-}
-
-@(require_results)
-block_from_ptr :: proc(ptr: rawptr) -> (block_ptr: ^Block_Header) {
- return (^Block_Header)(uintptr(ptr) - BLOCK_START_OFFSET)
-}
-
-@(require_results)
-block_to_ptr :: proc(block: ^Block_Header) -> (ptr: rawptr) {
- return rawptr(uintptr(block) + BLOCK_START_OFFSET)
-}
-
-// Return location of next block after block of given size.
-@(require_results)
-offset_to_block :: proc(ptr: rawptr, size: uint) -> (block: ^Block_Header) {
- return (^Block_Header)(uintptr(ptr) + uintptr(size))
-}
-
-@(require_results)
-offset_to_block_backwards :: proc(ptr: rawptr, size: uint) -> (block: ^Block_Header) {
- return (^Block_Header)(uintptr(ptr) - uintptr(size))
-}
-
-// Return location of previous block.
-@(require_results)
-block_prev :: proc(block: ^Block_Header) -> (prev: ^Block_Header) {
- assert(block_is_prev_free(block), "previous block must be free")
- return block.prev_phys_block
-}
-
-// Return location of next existing block.
-@(require_results)
-block_next :: proc(block: ^Block_Header) -> (next: ^Block_Header) {
- return offset_to_block(block_to_ptr(block), block_size(block) - BLOCK_HEADER_OVERHEAD)
-}
-
-// Link a new block with its physical neighbor, return the neighbor.
-@(require_results)
-block_link_next :: proc(block: ^Block_Header) -> (next: ^Block_Header) {
- next = block_next(block)
- next.prev_phys_block = block
- return
-}
-
-block_mark_as_free :: proc(block: ^Block_Header) {
- // Link the block to the next block, first.
- next := block_link_next(block)
- block_set_prev_free(next)
- block_set_free(block)
-}
-
-block_mark_as_used :: proc(block: ^Block_Header) {
- next := block_next(block)
- block_set_prev_used(next)
- block_set_used(block)
-}
-
-@(require_results)
-align_up :: proc(x, align: uint) -> (aligned: uint) {
- assert(0 == (align & (align - 1)), "must align to a power of two")
- return (x + (align - 1)) &~ (align - 1)
-}
-
-@(require_results)
-align_down :: proc(x, align: uint) -> (aligned: uint) {
- assert(0 == (align & (align - 1)), "must align to a power of two")
- return x - (x & (align - 1))
-}
-
-@(require_results)
-align_ptr :: proc(ptr: rawptr, align: uint) -> (aligned: rawptr) {
- assert(0 == (align & (align - 1)), "must align to a power of two")
- align_mask := uintptr(align) - 1
- _ptr := uintptr(ptr)
- _aligned := (_ptr + align_mask) &~ (align_mask)
- return rawptr(_aligned)
-}
-
-// Adjust an allocation size to be aligned to word size, and no smaller than internal minimum.
-@(require_results)
-adjust_request_size :: proc(size, align: uint) -> (adjusted: uint) {
- if size == 0 {
- return 0
- }
-
- // aligned size must not exceed `BLOCK_SIZE_MAX`, or we'll go out of bounds on `sl_bitmap`.
- if aligned := align_up(size, align); aligned < BLOCK_SIZE_MAX {
- adjusted = min(aligned, BLOCK_SIZE_MAX)
- }
- return
-}
-
-// Adjust an allocation size to be aligned to word size, and no smaller than internal minimum.
-@(require_results)
-adjust_request_size_with_err :: proc(size, align: uint) -> (adjusted: uint, err: runtime.Allocator_Error) {
- if size == 0 {
- return 0, nil
- }
-
- // aligned size must not exceed `BLOCK_SIZE_MAX`, or we'll go out of bounds on `sl_bitmap`.
- if aligned := align_up(size, align); aligned < BLOCK_SIZE_MAX {
- adjusted = min(aligned, BLOCK_SIZE_MAX)
- } else {
- err = .Out_Of_Memory
- }
- return
-}
-
-// TLSF utility functions. In most cases these are direct translations of
-// the documentation in the research paper.
-
-@(optimization_mode="favor_size", require_results)
-mapping_insert :: proc(size: uint) -> (fl, sl: i32) {
- if size < SMALL_BLOCK_SIZE {
- // Store small blocks in first list.
- sl = i32(size) / (SMALL_BLOCK_SIZE / SL_INDEX_COUNT)
- } else {
- fl = fls_uint(size)
- sl = i32(size >> (uint(fl) - TLSF_SL_INDEX_COUNT_LOG2)) ~ (1 << TLSF_SL_INDEX_COUNT_LOG2)
- fl -= (FL_INDEX_SHIFT - 1)
- }
- return
-}
-
-@(optimization_mode="favor_size", require_results)
-mapping_round :: #force_inline proc(size: uint) -> (rounded: uint) {
- rounded = size
- if size >= SMALL_BLOCK_SIZE {
- round := uint(1 << (uint(fls_uint(size) - TLSF_SL_INDEX_COUNT_LOG2))) - 1
- rounded += round
- }
- return
-}
-
-// This version rounds up to the next block size (for allocations)
-@(optimization_mode="favor_size", require_results)
-mapping_search :: proc(size: uint) -> (fl, sl: i32) {
- return mapping_insert(mapping_round(size))
-}
-
-@(require_results)
-search_suitable_block :: proc(control: ^Allocator, fli, sli: ^i32) -> (block: ^Block_Header) {
- // First, search for a block in the list associated with the given fl/sl index.
- fl := fli^; sl := sli^
-
- sl_map := control.sl_bitmap[fli^] & (~u32(0) << uint(sl))
- if sl_map == 0 {
- // No block exists. Search in the next largest first-level list.
- fl_map := control.fl_bitmap & (~u32(0) << uint(fl + 1))
- if fl_map == 0 {
- // No free blocks available, memory has been exhausted.
- return {}
- }
-
- fl = ffs(fl_map)
- fli^ = fl
- sl_map = control.sl_bitmap[fl]
- }
- assert(sl_map != 0, "internal error - second level bitmap is null")
- sl = ffs(sl_map)
- sli^ = sl
-
- // Return the first block in the free list.
- return control.blocks[fl][sl]
-}
-
-// Remove a free block from the free list.
-remove_free_block :: proc(control: ^Allocator, block: ^Block_Header, fl: i32, sl: i32) {
- prev := block.prev_free
- next := block.next_free
- assert(prev != nil, "prev_free can not be nil")
- assert(next != nil, "next_free can not be nil")
- next.prev_free = prev
- prev.next_free = next
-
- // If this block is the head of the free list, set new head.
- if control.blocks[fl][sl] == block {
- control.blocks[fl][sl] = next
-
- // If the new head is nil, clear the bitmap
- if next == &control.block_null {
- control.sl_bitmap[fl] &~= (u32(1) << uint(sl))
-
- // If the second bitmap is now empty, clear the fl bitmap
- if control.sl_bitmap[fl] == 0 {
- control.fl_bitmap &~= (u32(1) << uint(fl))
- }
- }
- }
-}
-
-// Insert a free block into the free block list.
-insert_free_block :: proc(control: ^Allocator, block: ^Block_Header, fl: i32, sl: i32) {
- current := control.blocks[fl][sl]
- assert(current != nil, "free lists cannot have a nil entry")
- assert(block != nil, "cannot insert a nil entry into the free list")
- block.next_free = current
- block.prev_free = &control.block_null
- current.prev_free = block
-
- assert(block_to_ptr(block) == align_ptr(block_to_ptr(block), ALIGN_SIZE), "block not properly aligned")
-
- // Insert the new block at the head of the list, and mark the first- and second-level bitmaps appropriately.
- control.blocks[fl][sl] = block
- control.fl_bitmap |= (u32(1) << uint(fl))
- control.sl_bitmap[fl] |= (u32(1) << uint(sl))
-}
-
-// Remove a given block from the free list.
-block_remove :: proc(control: ^Allocator, block: ^Block_Header) {
- fl, sl := mapping_insert(block_size(block))
- remove_free_block(control, block, fl, sl)
-}
-
-// Insert a given block into the free list.
-block_insert :: proc(control: ^Allocator, block: ^Block_Header) {
- fl, sl := mapping_insert(block_size(block))
- insert_free_block(control, block, fl, sl)
-}
-
-@(require_results)
-block_can_split :: proc(block: ^Block_Header, size: uint) -> (can_split: bool) {
- return block_size(block) >= size_of(Block_Header) + size
-}
-
-// Split a block into two, the second of which is free.
-@(require_results)
-block_split :: proc(block: ^Block_Header, size: uint) -> (remaining: ^Block_Header) {
- // Calculate the amount of space left in the remaining block.
- remaining = offset_to_block(block_to_ptr(block), size - BLOCK_HEADER_OVERHEAD)
-
- remain_size := block_size(block) - (size + BLOCK_HEADER_OVERHEAD)
-
- assert(block_to_ptr(remaining) == align_ptr(block_to_ptr(remaining), ALIGN_SIZE),
- "remaining block not aligned properly")
-
- assert(block_size(block) == remain_size + size + BLOCK_HEADER_OVERHEAD)
- block_set_size(remaining, remain_size)
- assert(block_size(remaining) >= BLOCK_SIZE_MIN, "block split with invalid size")
-
- block_set_size(block, size)
- block_mark_as_free(remaining)
-
- return remaining
-}
-
-// Absorb a free block's storage into an adjacent previous free block.
-@(require_results)
-block_absorb :: proc(prev: ^Block_Header, block: ^Block_Header) -> (absorbed: ^Block_Header) {
- assert(!block_is_last(prev), "previous block can't be last")
- // Note: Leaves flags untouched.
- prev.size += block_size(block) + BLOCK_HEADER_OVERHEAD
- _ = block_link_next(prev)
- return prev
-}
-
-// Merge a just-freed block with an adjacent previous free block.
-@(require_results)
-block_merge_prev :: proc(control: ^Allocator, block: ^Block_Header) -> (merged: ^Block_Header) {
- merged = block
- if (block_is_prev_free(block)) {
- prev := block_prev(block)
- assert(prev != nil, "prev physical block can't be nil")
- assert(block_is_free(prev), "prev block is not free though marked as such")
- block_remove(control, prev)
- merged = block_absorb(prev, block)
- }
- return merged
-}
-
-// Merge a just-freed block with an adjacent free block.
-@(require_results)
-block_merge_next :: proc(control: ^Allocator, block: ^Block_Header) -> (merged: ^Block_Header) {
- merged = block
- next := block_next(block)
- assert(next != nil, "next physical block can't be nil")
-
- if (block_is_free(next)) {
- assert(!block_is_last(block), "previous block can't be last")
- block_remove(control, next)
- merged = block_absorb(block, next)
- }
- return merged
-}
-
-// Trim any trailing block space off the end of a free block, return to pool.
-block_trim_free :: proc(control: ^Allocator, block: ^Block_Header, size: uint) {
- assert(block_is_free(block), "block must be free")
- if (block_can_split(block, size)) {
- remaining_block := block_split(block, size)
- _ = block_link_next(block)
- block_set_prev_free(remaining_block)
- block_insert(control, remaining_block)
- }
-}
-
-// Trim any trailing block space off the end of a used block, return to pool.
-block_trim_used :: proc(control: ^Allocator, block: ^Block_Header, size: uint) {
- assert(!block_is_free(block), "Block must be used")
- if (block_can_split(block, size)) {
- // If the next block is free, we must coalesce.
- remaining_block := block_split(block, size)
- block_set_prev_used(remaining_block)
-
- remaining_block = block_merge_next(control, remaining_block)
- block_insert(control, remaining_block)
- }
-}
-
-// Trim leading block space, return to pool.
-@(require_results)
-block_trim_free_leading :: proc(control: ^Allocator, block: ^Block_Header, size: uint) -> (remaining: ^Block_Header) {
- remaining = block
- if block_can_split(block, size) {
- // We want the 2nd block.
- remaining = block_split(block, size - BLOCK_HEADER_OVERHEAD)
- block_set_prev_free(remaining)
-
- _ = block_link_next(block)
- block_insert(control, block)
- }
- return remaining
-}
-
-@(require_results)
-block_locate_free :: proc(control: ^Allocator, size: uint) -> (block: ^Block_Header) {
- fl, sl: i32
- if size != 0 {
- fl, sl = mapping_search(size)
-
- /*
- `mapping_search` can futz with the size, so for excessively large sizes it can sometimes wind up
- with indices that are off the end of the block array. So, we protect against that here,
- since this is the only call site of `mapping_search`. Note that we don't need to check `sl`,
- as it comes from a modulo operation that guarantees it's always in range.
- */
- if fl < FL_INDEX_COUNT {
- block = search_suitable_block(control, &fl, &sl)
- }
- }
-
- if block != nil {
- assert(block_size(block) >= size)
- remove_free_block(control, block, fl, sl)
- }
- return block
-}
-
-@(require_results)
-block_prepare_used :: proc(control: ^Allocator, block: ^Block_Header, size: uint) -> (res: []byte, err: runtime.Allocator_Error) {
- if block != nil {
- assert(size != 0, "Size must be non-zero")
- block_trim_free(control, block, size)
- block_mark_as_used(block)
- res = ([^]byte)(block_to_ptr(block))[:size]
- }
- return
-}
-
// Clear control structure and point all empty lists at the null block
-clear :: proc(control: ^Allocator) {
+@(private)
+free_all :: proc(control: ^Allocator) -> (err: Error) {
+ // Clear internal structures
control.block_null.next_free = &control.block_null
control.block_null.prev_free = &control.block_null
-
control.fl_bitmap = 0
for i in 0.. (err: Error) {
assert(uintptr(raw_data(pool)) % ALIGN_SIZE == 0, "Added memory must be aligned")
@@ -574,9 +154,11 @@ pool_add :: proc(control: ^Allocator, pool: []u8) -> (err: Error) {
block_set_size(next, 0)
block_set_used(next)
block_set_prev_free(next)
+
return
}
+@(private)
pool_remove :: proc(control: ^Allocator, pool: []u8) {
block := offset_to_block_backwards(raw_data(pool), BLOCK_HEADER_OVERHEAD)
@@ -588,7 +170,7 @@ pool_remove :: proc(control: ^Allocator, pool: []u8) {
remove_free_block(control, block, fl, sl)
}
-@(require_results)
+@(private, require_results)
alloc_bytes_non_zeroed :: proc(control: ^Allocator, size: uint, align: uint) -> (res: []byte, err: runtime.Allocator_Error) {
assert(control != nil)
adjust := adjust_request_size(size, ALIGN_SIZE)
@@ -601,9 +183,54 @@ alloc_bytes_non_zeroed :: proc(control: ^Allocator, size: uint, align: uint) ->
return nil, .Out_Of_Memory
}
- block := block_locate_free(control, aligned_size)
+ block := block_locate_free(control, aligned_size)
if block == nil {
- return nil, .Out_Of_Memory
+ // OOM: Couldn't find block of `aligned_size` bytes.
+ if control.new_pool_size > 0 && control.pool.allocator.procedure != nil {
+ // TLSF is configured to grow.
+
+ /*
+ This implementation doesn't allow for out-of-band allocations to be passed through, as it's not designed to
+ track those. Nor is it able to signal those allocations then need to be freed on the backing allocator,
+ as opposed to regular allocations handled for you when you `destroy` the TLSF instance.
+
+ So if we're asked for more than we're configured to grow by, we can fail with an OOM error early, without adding a new pool.
+ */
+ if aligned_size > control.new_pool_size {
+ return nil, .Out_Of_Memory
+ }
+
+ // Trying to allocate a new pool of `control.new_pool_size` bytes.
+ new_pool_buf := runtime.make_aligned([]byte, control.new_pool_size, ALIGN_SIZE, control.pool.allocator) or_return
+
+ // Add new pool to control structure
+ if pool_add_err := pool_add(control, new_pool_buf); pool_add_err != .None {
+ delete(new_pool_buf, control.pool.allocator)
+ return nil, .Out_Of_Memory
+ }
+
+ // Allocate a new link in the `control.pool` tracking structure.
+ new_pool := new_clone(Pool{
+ data = new_pool_buf,
+ allocator = control.pool.allocator,
+ next = nil,
+ }, control.pool.allocator) or_return
+
+ p := &control.pool
+ for p.next != nil {
+ p = p.next
+ }
+ p.next = new_pool
+
+ // Try again to find free block
+ block = block_locate_free(control, aligned_size)
+ if block == nil {
+ return nil, .Out_Of_Memory
+ }
+ } else {
+ // TLSF is non-growing. We're done.
+ return nil, .Out_Of_Memory
+ }
}
ptr := block_to_ptr(block)
aligned := align_ptr(ptr, align)
@@ -627,7 +254,7 @@ alloc_bytes_non_zeroed :: proc(control: ^Allocator, size: uint, align: uint) ->
return block_prepare_used(control, block, adjust)
}
-@(require_results)
+@(private, require_results)
alloc_bytes :: proc(control: ^Allocator, size: uint, align: uint) -> (res: []byte, err: runtime.Allocator_Error) {
res, err = alloc_bytes_non_zeroed(control, size, align)
if err == nil {
@@ -653,7 +280,7 @@ free_with_size :: proc(control: ^Allocator, ptr: rawptr, size: uint) {
}
-@(require_results)
+@(private, require_results)
resize :: proc(control: ^Allocator, ptr: rawptr, old_size, new_size: uint, alignment: uint) -> (res: []byte, err: runtime.Allocator_Error) {
assert(control != nil)
if ptr != nil && new_size == 0 {
@@ -697,7 +324,7 @@ resize :: proc(control: ^Allocator, ptr: rawptr, old_size, new_size: uint, align
return
}
-@(require_results)
+@(private, require_results)
resize_non_zeroed :: proc(control: ^Allocator, ptr: rawptr, old_size, new_size: uint, alignment: uint) -> (res: []byte, err: runtime.Allocator_Error) {
assert(control != nil)
if ptr != nil && new_size == 0 {
@@ -736,3 +363,424 @@ resize_non_zeroed :: proc(control: ^Allocator, ptr: rawptr, old_size, new_size:
res = ([^]byte)(ptr)[:new_size]
return
}
+
+/*
+ TLSF achieves O(1) cost for `alloc` and `free` operations by limiting
+ the search for a free block to a free list of guaranteed size
+ adequate to fulfill the request, combined with efficient free list
+ queries using bitmasks and architecture-specific bit-manipulation
+ routines.
+
+ NOTE: TLSF spec relies on ffs/fls returning a value in the range 0..31.
+*/
+
+@(private, require_results)
+block_size :: proc "contextless" (block: ^Block_Header) -> (size: uint) {
+ return block.size &~ (BLOCK_HEADER_FREE | BLOCK_HEADER_PREV_FREE)
+}
+
+@(private)
+block_set_size :: proc "contextless" (block: ^Block_Header, size: uint) {
+ old_size := block.size
+ block.size = size | (old_size & (BLOCK_HEADER_FREE | BLOCK_HEADER_PREV_FREE))
+}
+
+@(private, require_results)
+block_is_last :: proc "contextless" (block: ^Block_Header) -> (is_last: bool) {
+ return block_size(block) == 0
+}
+
+@(private, require_results)
+block_is_free :: proc "contextless" (block: ^Block_Header) -> (is_free: bool) {
+ return (block.size & BLOCK_HEADER_FREE) == BLOCK_HEADER_FREE
+}
+
+@(private)
+block_set_free :: proc "contextless" (block: ^Block_Header) {
+ block.size |= BLOCK_HEADER_FREE
+}
+
+@(private)
+block_set_used :: proc "contextless" (block: ^Block_Header) {
+ block.size &~= BLOCK_HEADER_FREE
+}
+
+@(private, require_results)
+block_is_prev_free :: proc "contextless" (block: ^Block_Header) -> (is_prev_free: bool) {
+ return (block.size & BLOCK_HEADER_PREV_FREE) == BLOCK_HEADER_PREV_FREE
+}
+
+@(private)
+block_set_prev_free :: proc "contextless" (block: ^Block_Header) {
+ block.size |= BLOCK_HEADER_PREV_FREE
+}
+
+@(private)
+block_set_prev_used :: proc "contextless" (block: ^Block_Header) {
+ block.size &~= BLOCK_HEADER_PREV_FREE
+}
+
+@(private, require_results)
+block_from_ptr :: proc(ptr: rawptr) -> (block_ptr: ^Block_Header) {
+ return (^Block_Header)(uintptr(ptr) - BLOCK_START_OFFSET)
+}
+
+@(private, require_results)
+block_to_ptr :: proc(block: ^Block_Header) -> (ptr: rawptr) {
+ return rawptr(uintptr(block) + BLOCK_START_OFFSET)
+}
+
+// Return location of next block after block of given size.
+@(private, require_results)
+offset_to_block :: proc(ptr: rawptr, size: uint) -> (block: ^Block_Header) {
+ return (^Block_Header)(uintptr(ptr) + uintptr(size))
+}
+
+@(private, require_results)
+offset_to_block_backwards :: proc(ptr: rawptr, size: uint) -> (block: ^Block_Header) {
+ return (^Block_Header)(uintptr(ptr) - uintptr(size))
+}
+
+// Return location of previous block.
+@(private, require_results)
+block_prev :: proc(block: ^Block_Header) -> (prev: ^Block_Header) {
+ assert(block_is_prev_free(block), "previous block must be free")
+ return block.prev_phys_block
+}
+
+// Return location of next existing block.
+@(private, require_results)
+block_next :: proc(block: ^Block_Header) -> (next: ^Block_Header) {
+ return offset_to_block(block_to_ptr(block), block_size(block) - BLOCK_HEADER_OVERHEAD)
+}
+
+// Link a new block with its physical neighbor, return the neighbor.
+@(private, require_results)
+block_link_next :: proc(block: ^Block_Header) -> (next: ^Block_Header) {
+ next = block_next(block)
+ next.prev_phys_block = block
+ return
+}
+
+@(private)
+block_mark_as_free :: proc(block: ^Block_Header) {
+ // Link the block to the next block, first.
+ next := block_link_next(block)
+ block_set_prev_free(next)
+ block_set_free(block)
+}
+
+@(private)
+block_mark_as_used :: proc(block: ^Block_Header) {
+ next := block_next(block)
+ block_set_prev_used(next)
+ block_set_used(block)
+}
+
+@(private, require_results)
+align_up :: proc(x, align: uint) -> (aligned: uint) {
+ assert(0 == (align & (align - 1)), "must align to a power of two")
+ return (x + (align - 1)) &~ (align - 1)
+}
+
+@(private, require_results)
+align_down :: proc(x, align: uint) -> (aligned: uint) {
+ assert(0 == (align & (align - 1)), "must align to a power of two")
+ return x - (x & (align - 1))
+}
+
+@(private, require_results)
+align_ptr :: proc(ptr: rawptr, align: uint) -> (aligned: rawptr) {
+ assert(0 == (align & (align - 1)), "must align to a power of two")
+ align_mask := uintptr(align) - 1
+ _ptr := uintptr(ptr)
+ _aligned := (_ptr + align_mask) &~ (align_mask)
+ return rawptr(_aligned)
+}
+
+// Adjust an allocation size to be aligned to word size, and no smaller than internal minimum.
+@(private, require_results)
+adjust_request_size :: proc(size, align: uint) -> (adjusted: uint) {
+ if size == 0 {
+ return 0
+ }
+
+ // aligned size must not exceed `BLOCK_SIZE_MAX`, or we'll go out of bounds on `sl_bitmap`.
+ if aligned := align_up(size, align); aligned < BLOCK_SIZE_MAX {
+ adjusted = max(aligned, BLOCK_SIZE_MIN)
+ }
+ return
+}
+
+// Adjust an allocation size to be aligned to word size, and no smaller than internal minimum.
+@(private, require_results)
+adjust_request_size_with_err :: proc(size, align: uint) -> (adjusted: uint, err: runtime.Allocator_Error) {
+ if size == 0 {
+ return 0, nil
+ }
+
+ // aligned size must not exceed `BLOCK_SIZE_MAX`, or we'll go out of bounds on `sl_bitmap`.
+ if aligned := align_up(size, align); aligned < BLOCK_SIZE_MAX {
+ adjusted = min(aligned, BLOCK_SIZE_MAX)
+ } else {
+ err = .Out_Of_Memory
+ }
+ return
+}
+
+// TLSF utility functions. In most cases these are direct translations of
+// the documentation in the research paper.
+
+@(optimization_mode="favor_size", private, require_results)
+mapping_insert :: proc(size: uint) -> (fl, sl: i32) {
+ if size < SMALL_BLOCK_SIZE {
+ // Store small blocks in first list.
+ sl = i32(size) / (SMALL_BLOCK_SIZE / SL_INDEX_COUNT)
+ } else {
+ fl = fls_uint(size)
+ sl = i32(size >> (uint(fl) - TLSF_SL_INDEX_COUNT_LOG2)) ~ (1 << TLSF_SL_INDEX_COUNT_LOG2)
+ fl -= (FL_INDEX_SHIFT - 1)
+ }
+ return
+}
+
+@(optimization_mode="favor_size", private, require_results)
+mapping_round :: #force_inline proc(size: uint) -> (rounded: uint) {
+ rounded = size
+ if size >= SMALL_BLOCK_SIZE {
+ round := uint(1 << (uint(fls_uint(size) - TLSF_SL_INDEX_COUNT_LOG2))) - 1
+ rounded += round
+ }
+ return
+}
+
+// This version rounds up to the next block size (for allocations)
+@(optimization_mode="favor_size", private, require_results)
+mapping_search :: proc(size: uint) -> (fl, sl: i32) {
+ return mapping_insert(mapping_round(size))
+}
+
+@(private, require_results)
+search_suitable_block :: proc(control: ^Allocator, fli, sli: ^i32) -> (block: ^Block_Header) {
+ // First, search for a block in the list associated with the given fl/sl index.
+ fl := fli^; sl := sli^
+
+ sl_map := control.sl_bitmap[fli^] & (~u32(0) << uint(sl))
+ if sl_map == 0 {
+ // No block exists. Search in the next largest first-level list.
+ fl_map := control.fl_bitmap & (~u32(0) << uint(fl + 1))
+ if fl_map == 0 {
+ // No free blocks available, memory has been exhausted.
+ return {}
+ }
+
+ fl = ffs(fl_map)
+ fli^ = fl
+ sl_map = control.sl_bitmap[fl]
+ }
+ assert(sl_map != 0, "internal error - second level bitmap is null")
+ sl = ffs(sl_map)
+ sli^ = sl
+
+ // Return the first block in the free list.
+ return control.blocks[fl][sl]
+}
+
+// Remove a free block from the free list.
+@(private)
+remove_free_block :: proc(control: ^Allocator, block: ^Block_Header, fl: i32, sl: i32) {
+ prev := block.prev_free
+ next := block.next_free
+ assert(prev != nil, "prev_free can not be nil")
+ assert(next != nil, "next_free can not be nil")
+ next.prev_free = prev
+ prev.next_free = next
+
+ // If this block is the head of the free list, set new head.
+ if control.blocks[fl][sl] == block {
+ control.blocks[fl][sl] = next
+
+ // If the new head is nil, clear the bitmap
+ if next == &control.block_null {
+ control.sl_bitmap[fl] &~= (u32(1) << uint(sl))
+
+ // If the second bitmap is now empty, clear the fl bitmap
+ if control.sl_bitmap[fl] == 0 {
+ control.fl_bitmap &~= (u32(1) << uint(fl))
+ }
+ }
+ }
+}
+
+// Insert a free block into the free block list.
+@(private)
+insert_free_block :: proc(control: ^Allocator, block: ^Block_Header, fl: i32, sl: i32) {
+ current := control.blocks[fl][sl]
+ assert(current != nil, "free lists cannot have a nil entry")
+ assert(block != nil, "cannot insert a nil entry into the free list")
+ block.next_free = current
+ block.prev_free = &control.block_null
+ current.prev_free = block
+
+ assert(block_to_ptr(block) == align_ptr(block_to_ptr(block), ALIGN_SIZE), "block not properly aligned")
+
+ // Insert the new block at the head of the list, and mark the first- and second-level bitmaps appropriately.
+ control.blocks[fl][sl] = block
+ control.fl_bitmap |= (u32(1) << uint(fl))
+ control.sl_bitmap[fl] |= (u32(1) << uint(sl))
+}
+
+// Remove a given block from the free list.
+@(private)
+block_remove :: proc(control: ^Allocator, block: ^Block_Header) {
+ fl, sl := mapping_insert(block_size(block))
+ remove_free_block(control, block, fl, sl)
+}
+
+// Insert a given block into the free list.
+@(private)
+block_insert :: proc(control: ^Allocator, block: ^Block_Header) {
+ fl, sl := mapping_insert(block_size(block))
+ insert_free_block(control, block, fl, sl)
+}
+
+@(private, require_results)
+block_can_split :: proc(block: ^Block_Header, size: uint) -> (can_split: bool) {
+ return block_size(block) >= size_of(Block_Header) + size
+}
+
+// Split a block into two, the second of which is free.
+@(private, require_results)
+block_split :: proc(block: ^Block_Header, size: uint) -> (remaining: ^Block_Header) {
+ // Calculate the amount of space left in the remaining block.
+ remaining = offset_to_block(block_to_ptr(block), size - BLOCK_HEADER_OVERHEAD)
+
+ remain_size := block_size(block) - (size + BLOCK_HEADER_OVERHEAD)
+
+ assert(block_to_ptr(remaining) == align_ptr(block_to_ptr(remaining), ALIGN_SIZE),
+ "remaining block not aligned properly")
+
+ assert(block_size(block) == remain_size + size + BLOCK_HEADER_OVERHEAD)
+ block_set_size(remaining, remain_size)
+ assert(block_size(remaining) >= BLOCK_SIZE_MIN, "block split with invalid size")
+
+ block_set_size(block, size)
+ block_mark_as_free(remaining)
+
+ return remaining
+}
+
+// Absorb a free block's storage into an adjacent previous free block.
+@(private, require_results)
+block_absorb :: proc(prev: ^Block_Header, block: ^Block_Header) -> (absorbed: ^Block_Header) {
+ assert(!block_is_last(prev), "previous block can't be last")
+ // Note: Leaves flags untouched.
+ prev.size += block_size(block) + BLOCK_HEADER_OVERHEAD
+ _ = block_link_next(prev)
+ return prev
+}
+
+// Merge a just-freed block with an adjacent previous free block.
+@(private, require_results)
+block_merge_prev :: proc(control: ^Allocator, block: ^Block_Header) -> (merged: ^Block_Header) {
+ merged = block
+ if (block_is_prev_free(block)) {
+ prev := block_prev(block)
+ assert(prev != nil, "prev physical block can't be nil")
+ assert(block_is_free(prev), "prev block is not free though marked as such")
+ block_remove(control, prev)
+ merged = block_absorb(prev, block)
+ }
+ return merged
+}
+
+// Merge a just-freed block with an adjacent free block.
+@(private, require_results)
+block_merge_next :: proc(control: ^Allocator, block: ^Block_Header) -> (merged: ^Block_Header) {
+ merged = block
+ next := block_next(block)
+ assert(next != nil, "next physical block can't be nil")
+
+ if (block_is_free(next)) {
+ assert(!block_is_last(block), "previous block can't be last")
+ block_remove(control, next)
+ merged = block_absorb(block, next)
+ }
+ return merged
+}
+
+// Trim any trailing block space off the end of a free block, return to pool.
+@(private)
+block_trim_free :: proc(control: ^Allocator, block: ^Block_Header, size: uint) {
+ assert(block_is_free(block), "block must be free")
+ if (block_can_split(block, size)) {
+ remaining_block := block_split(block, size)
+ _ = block_link_next(block)
+ block_set_prev_free(remaining_block)
+ block_insert(control, remaining_block)
+ }
+}
+
+// Trim any trailing block space off the end of a used block, return to pool.
+@(private)
+block_trim_used :: proc(control: ^Allocator, block: ^Block_Header, size: uint) {
+ assert(!block_is_free(block), "Block must be used")
+ if (block_can_split(block, size)) {
+ // If the next block is free, we must coalesce.
+ remaining_block := block_split(block, size)
+ block_set_prev_used(remaining_block)
+
+ remaining_block = block_merge_next(control, remaining_block)
+ block_insert(control, remaining_block)
+ }
+}
+
+// Trim leading block space, return to pool.
+@(private, require_results)
+block_trim_free_leading :: proc(control: ^Allocator, block: ^Block_Header, size: uint) -> (remaining: ^Block_Header) {
+ remaining = block
+ if block_can_split(block, size) {
+ // We want the 2nd block.
+ remaining = block_split(block, size - BLOCK_HEADER_OVERHEAD)
+ block_set_prev_free(remaining)
+
+ _ = block_link_next(block)
+ block_insert(control, block)
+ }
+ return remaining
+}
+
+@(private, require_results)
+block_locate_free :: proc(control: ^Allocator, size: uint) -> (block: ^Block_Header) {
+ fl, sl: i32
+ if size != 0 {
+ fl, sl = mapping_search(size)
+
+ /*
+ `mapping_search` can futz with the size, so for excessively large sizes it can sometimes wind up
+ with indices that are off the end of the block array. So, we protect against that here,
+ since this is the only call site of `mapping_search`. Note that we don't need to check `sl`,
+ as it comes from a modulo operation that guarantees it's always in range.
+ */
+ if fl < FL_INDEX_COUNT {
+ block = search_suitable_block(control, &fl, &sl)
+ }
+ }
+
+ if block != nil {
+ assert(block_size(block) >= size)
+ remove_free_block(control, block, fl, sl)
+ }
+ return block
+}
+
+@(private, require_results)
+block_prepare_used :: proc(control: ^Allocator, block: ^Block_Header, size: uint) -> (res: []byte, err: runtime.Allocator_Error) {
+ if block != nil {
+ assert(size != 0, "Size must be non-zero")
+ block_trim_free(control, block, size)
+ block_mark_as_used(block)
+ res = ([^]byte)(block_to_ptr(block))[:size]
+ }
+ return
+}
\ No newline at end of file
diff --git a/core/mem/tracking_allocator.odin b/core/mem/tracking_allocator.odin
index cf780de3f..25c547471 100644
--- a/core/mem/tracking_allocator.odin
+++ b/core/mem/tracking_allocator.odin
@@ -34,12 +34,18 @@ Tracking_Allocator_Bad_Free_Entry :: struct {
location: runtime.Source_Code_Location,
}
+/*
+Callback type for when tracking allocator runs into a bad free.
+*/
+Tracking_Allocator_Bad_Free_Callback :: proc(t: ^Tracking_Allocator, memory: rawptr, location: runtime.Source_Code_Location)
+
/*
Tracking allocator data.
*/
Tracking_Allocator :: struct {
backing: Allocator,
allocation_map: map[rawptr]Tracking_Allocator_Entry,
+ bad_free_callback: Tracking_Allocator_Bad_Free_Callback,
bad_free_array: [dynamic]Tracking_Allocator_Bad_Free_Entry,
mutex: sync.Mutex,
clear_on_free_all: bool,
@@ -61,6 +67,7 @@ allocate the tracked data.
tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Allocator, internals_allocator := context.allocator) {
t.backing = backing_allocator
t.allocation_map.allocator = internals_allocator
+ t.bad_free_callback = tracking_allocator_bad_free_callback_panic
t.bad_free_array.allocator = internals_allocator
if .Free_All in query_features(t.backing) {
t.clear_on_free_all = true
@@ -109,6 +116,33 @@ tracking_allocator_reset :: proc(t: ^Tracking_Allocator) {
sync.mutex_unlock(&t.mutex)
}
+/*
+Default behavior for a bad free: Crash with error message that says where the
+bad free happened.
+
+Override Tracking_Allocator.bad_free_callback to have something else happen. For
+example, you can use tracking_allocator_bad_free_callback_add_to_array to return
+the tracking allocator to the old behavior, where the bad_free_array was used.
+*/
+tracking_allocator_bad_free_callback_panic :: proc(t: ^Tracking_Allocator, memory: rawptr, location: runtime.Source_Code_Location) {
+ runtime.print_caller_location(location)
+ runtime.print_string(" Tracking allocator error: Bad free of pointer ")
+ runtime.print_uintptr(uintptr(memory))
+ runtime.print_string("\n")
+ runtime.trap()
+}
+
+/*
+Alternative behavior for a bad free: Store in `bad_free_array`. If you use this,
+then you must make sure to check Tracking_Allocator.bad_free_array at some point.
+*/
+tracking_allocator_bad_free_callback_add_to_array :: proc(t: ^Tracking_Allocator, memory: rawptr, location: runtime.Source_Code_Location) {
+ append(&t.bad_free_array, Tracking_Allocator_Bad_Free_Entry {
+ memory = memory,
+ location = location,
+ })
+}
+
/*
Tracking allocator.
@@ -116,8 +150,10 @@ The tracking allocator is an allocator wrapper that tracks memory allocations.
This allocator stores all the allocations in a map. Whenever a pointer that's
not inside of the map is freed, the `bad_free_array` entry is added.
-An example of how to use the `Tracking_Allocator` to track subsequent allocations
-in your program and report leaks and bad frees:
+Here follows an example of how to use the `Tracking_Allocator` to track
+subsequent allocations in your program and report leaks. By default, the
+tracking allocator will crash on bad frees. You can override that behavior by
+overriding `track.bad_free_callback`.
Example:
@@ -137,9 +173,6 @@ Example:
for _, leak in track.allocation_map {
fmt.printf("%v leaked %m\n", leak.location, leak.size)
}
- for bad_free in track.bad_free_array {
- fmt.printf("%v allocation %p was freed badly\n", bad_free.location, bad_free.memory)
- }
}
*/
@(require_results)
@@ -191,10 +224,9 @@ tracking_allocator_proc :: proc(
}
if mode == .Free && old_memory != nil && old_memory not_in data.allocation_map {
- append(&data.bad_free_array, Tracking_Allocator_Bad_Free_Entry{
- memory = old_memory,
- location = loc,
- })
+ if data.bad_free_callback != nil {
+ data.bad_free_callback(data, old_memory, loc)
+ }
} else {
result = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc) or_return
}
diff --git a/core/mem/virtual/arena.odin b/core/mem/virtual/arena.odin
index 79407d80d..4fc2e0e35 100644
--- a/core/mem/virtual/arena.odin
+++ b/core/mem/virtual/arena.odin
@@ -107,8 +107,9 @@ arena_alloc :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_l
switch arena.kind {
case .Growing:
- needed := mem.align_forward_uint(size, alignment)
- if arena.curr_block == nil || (safe_add(arena.curr_block.used, needed) or_else 0) > arena.curr_block.reserved {
+ prev_used := 0 if arena.curr_block == nil else arena.curr_block.used
+ data, err = alloc_from_memory_block(arena.curr_block, size, alignment, default_commit_size=arena.default_commit_size)
+ if err == .Out_Of_Memory {
if arena.minimum_block_size == 0 {
arena.minimum_block_size = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE
arena.minimum_block_size = mem.align_forward_uint(arena.minimum_block_size, DEFAULT_PAGE_SIZE)
@@ -124,6 +125,7 @@ arena_alloc :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_l
max(arena.default_commit_size, arena.minimum_block_size)
}
+ needed := mem.align_forward_uint(size, alignment)
needed = max(needed, arena.default_commit_size)
block_size := max(needed, arena.minimum_block_size)
@@ -131,10 +133,10 @@ arena_alloc :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_l
new_block.prev = arena.curr_block
arena.curr_block = new_block
arena.total_reserved += new_block.reserved
- }
- prev_used := arena.curr_block.used
- data, err = alloc_from_memory_block(arena.curr_block, size, alignment, default_commit_size=arena.default_commit_size)
+ prev_used = 0
+ data, err = alloc_from_memory_block(arena.curr_block, size, alignment, default_commit_size=arena.default_commit_size)
+ }
arena.total_used += arena.curr_block.used - prev_used
case .Static:
if arena.curr_block == nil {
@@ -204,8 +206,9 @@ arena_free_all :: proc(arena: ^Arena, loc := #caller_location) {
}
// Zero the first block's memory
if arena.curr_block != nil {
- mem.zero(arena.curr_block.base, int(arena.curr_block.used))
+ curr_block_used := int(arena.curr_block.used)
arena.curr_block.used = 0
+ mem.zero(arena.curr_block.base, curr_block_used)
}
arena.total_used = 0
case .Static, .Buffer:
@@ -327,10 +330,26 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
case size == 0:
err = .Mode_Not_Implemented
return
- case (uintptr(old_data) & uintptr(alignment-1) == 0) && size < old_size:
- // shrink data in-place
- data = old_data[:size]
- return
+ case uintptr(old_data) & uintptr(alignment-1) == 0:
+ if size < old_size {
+ // shrink data in-place
+ data = old_data[:size]
+ return
+ }
+
+ if block := arena.curr_block; block != nil {
+ start := uint(uintptr(old_memory)) - uint(uintptr(block.base))
+ old_end := start + old_size
+ new_end := start + size
+ if start < old_end && old_end == block.used && new_end <= block.reserved {
+ // grow data in-place, adjusting next allocation
+ prev_used := block.used
+ _ = alloc_from_memory_block(block, new_end - old_end, 1, default_commit_size=arena.default_commit_size) or_return
+ arena.total_used += block.used - prev_used
+ data = block.base[start:new_end]
+ return
+ }
+ }
}
new_memory := arena_alloc(arena, size, alignment, location) or_return
@@ -401,9 +420,10 @@ arena_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) {
if block := arena.curr_block; block != nil {
assert(block.used >= temp.used, "out of order use of arena_temp_end", loc)
- amount_to_zero := min(block.used-temp.used, block.reserved-block.used)
+ amount_to_zero := block.used-temp.used
mem.zero_slice(block.base[temp.used:][:amount_to_zero])
block.used = temp.used
+ arena.total_used -= amount_to_zero
}
}
diff --git a/core/mem/virtual/virtual_darwin.odin b/core/mem/virtual/virtual_darwin.odin
new file mode 100644
index 000000000..0635c83d4
--- /dev/null
+++ b/core/mem/virtual/virtual_darwin.odin
@@ -0,0 +1,20 @@
+package mem_virtual
+
+import "core:sys/posix"
+
+_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
+ result := posix.mmap(nil, size, {}, {.ANONYMOUS, .PRIVATE})
+ if result == posix.MAP_FAILED {
+ assert_contextless(posix.errno() == .ENOMEM)
+ return nil, .Out_Of_Memory
+ }
+
+ return ([^]byte)(uintptr(result))[:size], nil
+}
+
+_decommit :: proc "contextless" (data: rawptr, size: uint) {
+ MADV_FREE :: 5
+
+ posix.mprotect(data, size, {})
+ posix.posix_madvise(data, size, transmute(posix.MAdvice)i32(MADV_FREE))
+}
diff --git a/core/mem/virtual/virtual_freebsd.odin b/core/mem/virtual/virtual_freebsd.odin
new file mode 100644
index 000000000..af0f31733
--- /dev/null
+++ b/core/mem/virtual/virtual_freebsd.odin
@@ -0,0 +1,26 @@
+package mem_virtual
+
+import "core:sys/posix"
+
+_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
+
+ PROT_MAX :: proc "contextless" (flags: posix.Prot_Flags) -> posix.Prot_Flags {
+ _PROT_MAX_SHIFT :: 16
+ return transmute(posix.Prot_Flags)(transmute(i32)flags << _PROT_MAX_SHIFT)
+ }
+
+ result := posix.mmap(nil, size, PROT_MAX({.READ, .WRITE, .EXEC}), {.ANONYMOUS, .PRIVATE})
+ if result == posix.MAP_FAILED {
+ assert_contextless(posix.errno() == .ENOMEM)
+ return nil, .Out_Of_Memory
+ }
+
+ return ([^]byte)(uintptr(result))[:size], nil
+}
+
+_decommit :: proc "contextless" (data: rawptr, size: uint) {
+ MADV_FREE :: 5
+
+ posix.mprotect(data, size, {})
+ posix.posix_madvise(data, size, transmute(posix.MAdvice)i32(MADV_FREE))
+}
diff --git a/core/mem/virtual/virtual_netbsd.odin b/core/mem/virtual/virtual_netbsd.odin
new file mode 100644
index 000000000..588625cc7
--- /dev/null
+++ b/core/mem/virtual/virtual_netbsd.odin
@@ -0,0 +1,25 @@
+package mem_virtual
+
+import "core:sys/posix"
+
+_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
+
+ PROT_MPROTECT :: proc "contextless" (flags: posix.Prot_Flags) -> posix.Prot_Flags {
+ return transmute(posix.Prot_Flags)(transmute(i32)flags << 3)
+ }
+
+ result := posix.mmap(nil, size, PROT_MPROTECT({.READ, .WRITE, .EXEC}), {.ANONYMOUS, .PRIVATE})
+ if result == posix.MAP_FAILED {
+ assert_contextless(posix.errno() == .ENOMEM)
+ return nil, .Out_Of_Memory
+ }
+
+ return ([^]byte)(uintptr(result))[:size], nil
+}
+
+_decommit :: proc "contextless" (data: rawptr, size: uint) {
+ MADV_FREE :: 6
+
+ posix.mprotect(data, size, {})
+ posix.posix_madvise(data, size, transmute(posix.MAdvice)i32(MADV_FREE))
+}
diff --git a/core/mem/virtual/virtual_openbsd.odin b/core/mem/virtual/virtual_openbsd.odin
new file mode 100644
index 000000000..83f7ca9ca
--- /dev/null
+++ b/core/mem/virtual/virtual_openbsd.odin
@@ -0,0 +1,20 @@
+package mem_virtual
+
+import "core:sys/posix"
+
+_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
+ result := posix.mmap(nil, size, {}, {.ANONYMOUS, .PRIVATE})
+ if result == posix.MAP_FAILED {
+ assert_contextless(posix.errno() == .ENOMEM)
+ return nil, .Out_Of_Memory
+ }
+
+ return ([^]byte)(uintptr(result))[:size], nil
+}
+
+_decommit :: proc "contextless" (data: rawptr, size: uint) {
+ MADV_FREE :: 6
+
+ posix.mprotect(data, size, {})
+ posix.posix_madvise(data, size, transmute(posix.MAdvice)i32(MADV_FREE))
+}
diff --git a/core/mem/virtual/virtual_platform.odin b/core/mem/virtual/virtual_platform.odin
index 54c42ce4b..31e9cfca8 100644
--- a/core/mem/virtual/virtual_platform.odin
+++ b/core/mem/virtual/virtual_platform.odin
@@ -15,7 +15,9 @@ platform_memory_alloc :: proc "contextless" (to_commit, to_reserve: uint) -> (bl
to_commit = clamp(to_commit, size_of(Platform_Memory_Block), total_to_reserved)
data := reserve(total_to_reserved) or_return
- commit(raw_data(data), to_commit)
+
+ commit_err := commit(raw_data(data), to_commit)
+ assert_contextless(commit_err == nil)
block = (^Platform_Memory_Block)(raw_data(data))
block.committed = to_commit
diff --git a/core/mem/virtual/virtual_posix.odin b/core/mem/virtual/virtual_posix.odin
index c3d6a9095..0b304a5e7 100644
--- a/core/mem/virtual/virtual_posix.odin
+++ b/core/mem/virtual/virtual_posix.odin
@@ -4,36 +4,18 @@ package mem_virtual
import "core:sys/posix"
-// Define non-posix needed flags:
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD {
- MADV_FREE :: 5 /* pages unneeded, discard contents */
-} else when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
- MADV_FREE :: 6
-}
-
-_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
- flags := posix.Map_Flags{ .ANONYMOUS, .PRIVATE }
- result := posix.mmap(nil, size, {}, flags)
- if result == posix.MAP_FAILED {
- return nil, .Out_Of_Memory
- }
-
- return ([^]byte)(uintptr(result))[:size], nil
-}
-
_commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
if posix.mprotect(data, size, { .READ, .WRITE }) != .OK {
- return .Out_Of_Memory
+ #partial switch posix.errno() {
+ case .EACCES, .EPERM: return .Invalid_Pointer
+ case .ENOTSUP, .EINVAL: return .Invalid_Argument
+ case: return .Out_Of_Memory
+ }
}
return nil
}
-_decommit :: proc "contextless" (data: rawptr, size: uint) {
- posix.mprotect(data, size, {})
- posix.posix_madvise(data, size, transmute(posix.MAdvice)i32(MADV_FREE))
-}
-
_release :: proc "contextless" (data: rawptr, size: uint) {
posix.munmap(data, size)
}
diff --git a/core/net/common.odin b/core/net/common.odin
index 263fc770f..fb6b17511 100644
--- a/core/net/common.odin
+++ b/core/net/common.odin
@@ -53,8 +53,6 @@ ODIN_NET_TCP_NODELAY_DEFAULT :: #config(ODIN_NET_TCP_NODELAY_DEFAULT, true)
Maybe :: runtime.Maybe
Network_Error :: union #shared_nil {
- General_Error,
- Platform_Error,
Create_Socket_Error,
Dial_Error,
Listen_Error,
@@ -65,6 +63,7 @@ Network_Error :: union #shared_nil {
TCP_Recv_Error,
UDP_Recv_Error,
Shutdown_Error,
+ Interfaces_Error,
Socket_Option_Error,
Set_Blocking_Error,
Parse_Endpoint_Error,
@@ -74,14 +73,13 @@ Network_Error :: union #shared_nil {
#assert(size_of(Network_Error) == 8)
-General_Error :: enum u32 {
- None = 0,
- Unable_To_Enumerate_Network_Interfaces = 1,
+Interfaces_Error :: enum u32 {
+ None,
+ Unable_To_Enumerate_Network_Interfaces,
+ Allocation_Failure,
+ Unknown,
}
-// `Platform_Error` is used to wrap errors returned by the different platforms that don't fit a common error.
-Platform_Error :: enum u32 {}
-
Parse_Endpoint_Error :: enum u32 {
None = 0,
Bad_Port = 1,
@@ -95,6 +93,7 @@ Resolve_Error :: enum u32 {
}
DNS_Error :: enum u32 {
+ None = 0,
Invalid_Hostname_Error = 1,
Invalid_Hosts_Config_Error,
Invalid_Resolv_Config_Error,
@@ -108,7 +107,7 @@ TCP_Options :: struct {
no_delay: bool,
}
-default_tcp_options := TCP_Options {
+DEFAULT_TCP_OPTIONS :: TCP_Options {
no_delay = ODIN_NET_TCP_NODELAY_DEFAULT,
}
@@ -147,6 +146,9 @@ IP6_Loopback :: IP6_Address{0, 0, 0, 0, 0, 0, 0, 1}
IP4_Any := IP4_Address{}
IP6_Any := IP6_Address{}
+IP4_mDNS_Broadcast := Endpoint{address=IP4_Address{224, 0, 0, 251}, port=5353}
+IP6_mDNS_Broadcast := Endpoint{address=IP6_Address{65282, 0, 0, 0, 0, 0, 0, 251}, port = 5353}
+
Endpoint :: struct {
address: Address,
port: int,
diff --git a/core/net/dns.odin b/core/net/dns.odin
index ffb97fc5b..7eb543db3 100644
--- a/core/net/dns.odin
+++ b/core/net/dns.odin
@@ -7,10 +7,11 @@ package net
*/
/*
- Copyright 2022 Tetralux
- Copyright 2022 Colin Davidson
- Copyright 2022 Jeroen van Rijn .
- Copyright 2024 Feoramund .
+ Copyright 2022 Tetralux
+ Copyright 2022 Colin Davidson
+ Copyright 2022 Jeroen van Rijn .
+ Copyright 2024 Feoramund .
+ Copyright 2025 Christiano Haesbaert .
Made available under Odin's BSD-3 license.
List of contributors:
@@ -18,12 +19,14 @@ package net
Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
Jeroen van Rijn: Cross platform unification, code style, documentation
Feoramund: FreeBSD platform code
+ Haesbaert: Security fixes
*/
import "core:mem"
import "core:strings"
import "core:time"
import "core:os"
+import "core:math/rand"
/*
Default configuration for DNS resolution.
*/
@@ -50,9 +53,12 @@ init_dns_configuration :: proc() {
dns_configuration.hosts_file, _ = replace_environment_path(dns_configuration.hosts_file)
}
+@(fini, private)
destroy_dns_configuration :: proc() {
delete(dns_configuration.resolv_conf)
+ dns_configuration.resolv_conf = ""
delete(dns_configuration.hosts_file)
+ dns_configuration.hosts_file = ""
}
dns_configuration := DEFAULT_DNS_CONFIGURATION
@@ -132,7 +138,14 @@ resolve_ip4 :: proc(hostname_and_maybe_port: string) -> (ep4: Endpoint, err: Net
return
}
case Host:
- recs, _ := get_dns_records_from_os(t.hostname, .IP4, context.temp_allocator)
+ recs: []DNS_Record
+
+ if ODIN_OS != .Windows && strings.has_suffix(t.hostname, ".local") {
+ recs, _ = get_dns_records_from_nameservers(t.hostname, .IP4, {IP4_mDNS_Broadcast}, nil, context.temp_allocator)
+ } else {
+ recs, _ = get_dns_records_from_os(t.hostname, .IP4, context.temp_allocator)
+ }
+
if len(recs) == 0 {
err = .Unable_To_Resolve
return
@@ -159,7 +172,14 @@ resolve_ip6 :: proc(hostname_and_maybe_port: string) -> (ep6: Endpoint, err: Net
return t, nil
}
case Host:
- recs, _ := get_dns_records_from_os(t.hostname, .IP6, context.temp_allocator)
+ recs: []DNS_Record
+
+ if ODIN_OS != .Windows && strings.has_suffix(t.hostname, ".local") {
+ recs, _ = get_dns_records_from_nameservers(t.hostname, .IP6, {IP6_mDNS_Broadcast}, nil, context.temp_allocator)
+ } else {
+ recs, _ = get_dns_records_from_os(t.hostname, .IP6, context.temp_allocator)
+ }
+
if len(recs) == 0 {
err = .Unable_To_Resolve
return
@@ -210,7 +230,7 @@ get_dns_records_from_nameservers :: proc(hostname: string, type: DNS_Record_Type
}
hdr := DNS_Header{
- id = 0,
+ id = u16be(rand.uint32()),
is_response = false,
opcode = 0,
is_authoritative = false,
@@ -255,23 +275,23 @@ get_dns_records_from_nameservers :: proc(hostname: string, type: DNS_Record_Type
return nil, .Connection_Error
}
- // recv_sz, _, recv_err := recv_udp(conn, dns_response_buf[:])
- // if recv_err == UDP_Recv_Error.Timeout {
- // continue
- // } else if recv_err != nil {
- // continue
- // }
- recv_sz, _ := recv_udp(conn, dns_response_buf[:]) or_continue
+ recv_sz, src := recv_udp(conn, dns_response_buf[:]) or_continue
if recv_sz == 0 {
continue
}
+ if src != name_server {
+ continue
+ }
dns_response = dns_response_buf[:recv_sz]
- rsp, _ok := parse_response(dns_response, type)
+ rsp, xid, _ok := parse_response(dns_response, type)
if !_ok {
return nil, .Server_Error
}
+ if id != xid {
+ continue
+ }
if len(rsp) == 0 {
continue
@@ -525,18 +545,21 @@ decode_hostname :: proc(packet: []u8, start_idx: int, allocator := context.alloc
return
}
- if packet[cur_idx] > 63 && packet[cur_idx] != 0xC0 {
- return
- }
+ switch {
- switch packet[cur_idx] {
-
- // This is a offset to more data in the packet, jump to it
- case 0xC0:
+ // A pointer is when the two higher bits are set.
+ case packet[cur_idx] & 0xC0 == 0xC0:
+ if len(packet[cur_idx:]) < 2 {
+ return
+ }
pkt := packet[cur_idx:cur_idx+2]
val := (^u16be)(raw_data(pkt))^
offset := int(val & 0x3FFF)
- if offset > len(packet) {
+ // RFC 9267 a ptr should only point backwards, enough to avoid infinity.
+ // "The offset at which this octet is located must be smaller than the offset
+ // at which the compression pointer is located". Still keep iteration_max to
+ // avoid tiny jumps.
+ if offset > len(packet) || offset >= cur_idx {
return
}
@@ -547,6 +570,10 @@ decode_hostname :: proc(packet: []u8, start_idx: int, allocator := context.alloc
level += 1
}
+ // Validate label len
+ case packet[cur_idx] > LABEL_MAX:
+ return
+
// This is a label, insert it into the hostname
case:
label_size := int(packet[cur_idx])
@@ -785,7 +812,7 @@ parse_record :: proc(packet: []u8, cur_off: ^int, filter: DNS_Record_Type = nil)
- Data[]
*/
-parse_response :: proc(response: []u8, filter: DNS_Record_Type = nil, allocator := context.allocator) -> (records: []DNS_Record, ok: bool) {
+parse_response :: proc(response: []u8, filter: DNS_Record_Type = nil, allocator := context.allocator) -> (records: []DNS_Record, xid: u16be, ok: bool) {
context.allocator = allocator
HEADER_SIZE_BYTES :: 12
@@ -798,11 +825,13 @@ parse_response :: proc(response: []u8, filter: DNS_Record_Type = nil, allocator
dns_hdr_chunks := mem.slice_data_cast([]u16be, response[:HEADER_SIZE_BYTES])
hdr := unpack_dns_header(dns_hdr_chunks[0], dns_hdr_chunks[1])
if !hdr.is_response {
+ delete(_records)
return
}
question_count := int(dns_hdr_chunks[2])
if question_count != 1 {
+ delete(_records)
return
}
answer_count := int(dns_hdr_chunks[3])
@@ -854,6 +883,7 @@ parse_response :: proc(response: []u8, filter: DNS_Record_Type = nil, allocator
append(&_records, rec)
}
}
+ xid = hdr.id
- return _records[:], true
+ return _records[:], xid, true
}
diff --git a/core/net/dns_windows.odin b/core/net/dns_windows.odin
index 2f3831767..7736851b8 100644
--- a/core/net/dns_windows.odin
+++ b/core/net/dns_windows.odin
@@ -29,9 +29,14 @@ import win "core:sys/windows"
_get_dns_records_os :: proc(hostname: string, type: DNS_Record_Type, allocator := context.allocator) -> (records: []DNS_Record, err: DNS_Error) {
context.allocator = allocator
+ options := win.DNS_QUERY_OPTIONS{}
+ if strings.has_suffix(hostname, ".local") {
+ options = {.MULTICAST_ONLY, .MULTICAST_WAIT} // 0x00020500
+ }
+
host_cstr := strings.clone_to_cstring(hostname, context.temp_allocator)
rec: ^win.DNS_RECORD
- res := win.DnsQuery_UTF8(host_cstr, u16(type), 0, nil, &rec, nil)
+ res := win.DnsQuery_UTF8(host_cstr, u16(type), options, nil, &rec, nil)
switch u32(res) {
case 0:
diff --git a/core/net/errors.odin b/core/net/errors.odin
new file mode 100644
index 000000000..53c936a66
--- /dev/null
+++ b/core/net/errors.odin
@@ -0,0 +1,275 @@
+package net
+
+/*
+Retrieve a platform specific error code, for when the categorized cross-platform errors are not enough.
+
+Platforms specific returns:
+- Darwin: `posix.Errno` (`core:sys/posix`)
+- Linux: `linux.Errno` (`core:sys/linux`)
+- FreeBSD: `freebsd.Errno` (`core:sys/freebsd`)
+- Windows: `windows.System_Error` (`core:sys/windows`)
+*/
+@(require_results)
+last_platform_error :: proc() -> i32 {
+ return _last_platform_error()
+}
+
+/*
+Retrieve a stringified version of the last platform error.
+*/
+@(require_results)
+last_platform_error_string :: proc() -> string {
+ return _last_platform_error_string()
+}
+
+set_last_platform_error :: proc(err: i32) {
+ _set_last_platform_error(err)
+}
+
+Create_Socket_Error :: enum i32 {
+ None,
+ // No network connection, or the network stack is not initialized.
+ Network_Unreachable,
+ // Not enough space in internal tables/buffers to create a new socket, or an unsupported protocol is given.
+ Insufficient_Resources,
+ // Invalid/unsupported family or protocol.
+ Invalid_Argument,
+ // The user has no permission to create a socket of this type and/or protocol.
+ Insufficient_Permissions,
+
+ // An error unable to be categorized in above categories, `last_platform_error` may have more info.
+ Unknown,
+}
+
+Dial_Error :: enum i32 {
+ None,
+ // No network connection, or the network stack is not initialized.
+ Network_Unreachable,
+ // Not enough space in internal tables/buffers to create a new socket, or an unsupported protocol is given.
+ Insufficient_Resources,
+ // Invalid endpoint and/or options.
+ Invalid_Argument,
+ // An attempt was made to connect to a broadcast socket on a socket that doesn't support it.
+ Broadcast_Not_Supported,
+ // The socket is already connected.
+ Already_Connected,
+ // The socket is already in the progress of making a connection.
+ Already_Connecting,
+ // The address is already in use.
+ Address_In_Use,
+ // Could not reach the remote host.
+ Host_Unreachable,
+ // The remote host refused the connection or isn't listening.
+ Refused,
+ // The connection was reset by the remote host.
+ Reset,
+ // Timed out before making a connection.
+ Timeout,
+ // Non-blocking socket that would need to block waiting to connect.
+ Would_Block,
+ // Interrupted by a signal or other method of cancellation like WSACancelBlockingCall on Windows.
+ Interrupted,
+ // Endpoint given without a port, which is required.
+ Port_Required,
+
+ // An error unable to be categorized in above categories, `last_platform_error` may have more info.
+ Unknown,
+}
+
+Bind_Error :: enum i32 {
+ None,
+ // No network connection, or the network stack is not initialized.
+ Network_Unreachable,
+ // Not enough space in internal tables/buffers to create a new socket, or an unsupported protocol is given.
+ Insufficient_Resources,
+ // Invalid socket or endpoint, or invalid combination of the two.
+ Invalid_Argument,
+ // The socket is already bound to an address.
+ Already_Bound,
+ // The address is protected and the current user has insufficient permissions to access it.
+ Insufficient_Permissions_For_Address,
+ // The address is already in use.
+ Address_In_Use,
+
+ // An error unable to be categorized in above categories, `last_platform_error` may have more info.
+ Unknown,
+}
+
+Listen_Error :: enum i32 {
+ None,
+ // No network connection, or the network stack is not initialized.
+ Network_Unreachable,
+ // Not enough space in internal tables/buffers to create a new socket, or an unsupported protocol is given.
+ Insufficient_Resources,
+ // The socket or backlog is invalid.
+ Invalid_Argument,
+ // The socket is valid, but does not support listening.
+ Unsupported_Socket,
+ // The socket is already connected.
+ Already_Connected,
+ // The address is already in use.
+ Address_In_Use,
+
+ // An error unable to be categorized in above categories, `last_platform_error` may have more info.
+ Unknown,
+}
+
+Accept_Error :: enum i32 {
+ None,
+ // No network connection, or the network stack is not initialized.
+ Network_Unreachable,
+ // Not enough space in internal tables/buffers to create a new socket, or an unsupported protocol is given.
+ Insufficient_Resources,
+ // Invalid socket, or options.
+ Invalid_Argument,
+ // The given socket does not support accepting connections.
+ Unsupported_Socket,
+ // accept called on a socket which is not listening.
+ Not_Listening,
+ // A connection arrived but was closed while in the listen queue.
+ Aborted,
+ // Timed out before being able to accept a connection.
+ Timeout,
+ // Non-blocking socket that would need to block waiting for a connection.
+ Would_Block,
+ // Interrupted by a signal or other method of cancellation like WSACancelBlockingCall on Windows.
+ Interrupted,
+
+ // An error unable to be categorized in above categories, `last_platform_error` may have more info.
+ Unknown,
+}
+
+TCP_Recv_Error :: enum i32 {
+ None,
+ // No network connection, or the network stack is not initialized.
+ Network_Unreachable,
+ // Not enough space in internal tables/buffers to create a new socket, or an unsupported protocol is given.
+ Insufficient_Resources,
+ // Invalid socket or buffer given.
+ Invalid_Argument,
+ // The socket is not connected.
+ Not_Connected,
+ // Connection was closed/broken/shutdown while receiving data.
+ Connection_Closed,
+ // Timed out before being able to receive any data.
+ Timeout,
+ // Non-blocking socket that would need to block waiting on data.
+ Would_Block,
+ // Interrupted by a signal or other method of cancellation like WSACancelBlockingCall on Windows.
+ Interrupted,
+
+ // An error unable to be categorized in above categories, `last_platform_error` may have more info.
+ Unknown,
+}
+
+UDP_Recv_Error :: enum i32 {
+ None,
+ // No network connection, or the network stack is not initialized.
+ Network_Unreachable,
+ // Not enough space in internal tables/buffers to create a new socket, or an unsupported protocol is given.
+ Insufficient_Resources,
+ // Invalid socket or buffer given.
+ Invalid_Argument,
+ // "Connection" was refused by remote, or closed/broken/shutdown while receiving data.
+ Connection_Refused,
+ // Timed out before being able to receive any data.
+ Timeout,
+ // Non-blocking socket that would need to block waiting on data.
+ Would_Block,
+ // Interrupted by a signal or other method of cancellation like WSACancelBlockingCall on Windows.
+ Interrupted,
+ // Linux and UDP only: indicates the buffer was too small to receive all data, and the excess is truncated and discarded.
+ Excess_Truncated,
+
+ // An error unable to be categorized in above categories, `last_platform_error` may have more info.
+ Unknown,
+}
+
+TCP_Send_Error :: enum i32 {
+ None,
+ // No network connection, or the network stack is not initialized.
+ Network_Unreachable,
+ // Not enough space in internal tables/buffers to create a new socket, or an unsupported protocol is given.
+ Insufficient_Resources,
+ // Invalid socket or buffer given.
+ Invalid_Argument,
+ // Connection was closed/broken/shutdown while receiving data.
+ Connection_Closed,
+ // The socket is not connected.
+ Not_Connected,
+ // Could not reach the remote host.
+ Host_Unreachable,
+ // Timed out before being able to send any data.
+ Timeout,
+ // Non-blocking socket that would need to block waiting on the remote to be able to receive the data.
+ Would_Block,
+ // Interrupted by a signal or other method of cancellation like WSACancelBlockingCall on Windows.
+ Interrupted,
+
+ // An error unable to be categorized in above categories, `last_platform_error` may have more info.
+ Unknown,
+}
+
+UDP_Send_Error :: enum i32 {
+ None,
+ // No network connection, or the network stack is not initialized.
+ Network_Unreachable,
+ // Not enough space in internal tables/buffers to create a new socket, or an unsupported protocol is given.
+ Insufficient_Resources,
+ // Invalid socket or buffer given.
+ Invalid_Argument,
+ // Could not reach the remote host.
+ Host_Unreachable,
+ // "Connection" was refused by remote, or closed/broken/shutdown while sending data.
+ Connection_Refused,
+ // Timed out before being able to send any data.
+ Timeout,
+ // Non-blocking socket that would need to block waiting on the remote to be able to receive the data.
+ Would_Block,
+ // Interrupted by a signal or other method of cancellation like WSACancelBlockingCall on Windows.
+ Interrupted,
+
+ // An error unable to be categorized in above categories, `last_platform_error` may have more info.
+ Unknown,
+}
+
+Shutdown_Error :: enum i32 {
+ None,
+ // No network connection, or the network stack is not initialized.
+ Network_Unreachable,
+ // Socket is invalid or not connected, or the manner given is invalid.
+ Invalid_Argument,
+ // Connection was closed/aborted/shutdown.
+ Connection_Closed,
+
+ // An error unable to be categorized in above categories, `last_platform_error` may have more info.
+ Unknown,
+}
+
+Socket_Option_Error :: enum i32 {
+ None,
+ // No network connection, or the network stack is not initialized.
+ Network_Unreachable,
+ // Not enough space in internal tables/buffers to create a new socket, or an unsupported protocol is given.
+ Insufficient_Resources,
+ // Socket is invalid, not connected, or the connection has been closed/reset/shutdown.
+ Invalid_Socket,
+ // Unknown or unsupported option for the socket.
+ Invalid_Option,
+ // Invalid level or value.
+ Invalid_Value,
+
+ // An error unable to be categorized in above categories, `last_platform_error` may have more info.
+ Unknown,
+}
+
+Set_Blocking_Error :: enum i32 {
+ None,
+ // No network connection, or the network stack is not initialized.
+ Network_Unreachable,
+ // Socket is invalid.
+ Invalid_Argument,
+
+ // An error unable to be categorized in above categories, `last_platform_error` may have more info.
+ Unknown,
+}
diff --git a/core/net/errors_darwin.odin b/core/net/errors_darwin.odin
index 2905b44bc..7aaa220e9 100644
--- a/core/net/errors_darwin.odin
+++ b/core/net/errors_darwin.odin
@@ -20,189 +20,232 @@ package net
Feoramund: FreeBSD platform code
*/
-import "core:c"
-import "core:os"
+import "core:reflect"
+import "core:sys/posix"
-Create_Socket_Error :: enum c.int {
- None = 0,
- Family_Not_Supported_For_This_Socket = c.int(os.EAFNOSUPPORT),
- No_Socket_Descriptors_Available = c.int(os.EMFILE),
- No_Buffer_Space_Available = c.int(os.ENOBUFS),
- No_Memory_Available_Available = c.int(os.ENOMEM),
- Protocol_Unsupported_By_System = c.int(os.EPROTONOSUPPORT),
- Wrong_Protocol_For_Socket = c.int(os.EPROTONOSUPPORT),
- Family_And_Socket_Type_Mismatch = c.int(os.EPROTONOSUPPORT),
+_last_platform_error :: proc() -> i32 {
+ return i32(posix.errno())
}
-Dial_Error :: enum c.int {
- None = 0,
- Port_Required = -1, // Attempted to dial an endpointing without a port being set.
-
- Address_In_Use = c.int(os.EADDRINUSE),
- In_Progress = c.int(os.EINPROGRESS),
- Cannot_Use_Any_Address = c.int(os.EADDRNOTAVAIL),
- Wrong_Family_For_Socket = c.int(os.EAFNOSUPPORT),
- Refused = c.int(os.ECONNREFUSED),
- Is_Listening_Socket = c.int(os.EACCES),
- Already_Connected = c.int(os.EISCONN),
- Network_Unreachable = c.int(os.ENETUNREACH), // Device is offline
- Host_Unreachable = c.int(os.EHOSTUNREACH), // Remote host cannot be reached
- No_Buffer_Space_Available = c.int(os.ENOBUFS),
- Not_Socket = c.int(os.ENOTSOCK),
- Timeout = c.int(os.ETIMEDOUT),
-
- // TODO: we may need special handling for this; maybe make a socket a struct with metadata?
- Would_Block = c.int(os.EWOULDBLOCK),
+_last_platform_error_string :: proc() -> string {
+ description, _ := reflect.enum_name_from_value(posix.errno())
+ return description
}
-Bind_Error :: enum c.int {
- None = 0,
- Privileged_Port_Without_Root = -1, // Attempted to bind to a port less than 1024 without root access.
-
- Address_In_Use = c.int(os.EADDRINUSE), // Another application is currently bound to this endpoint.
- Given_Nonlocal_Address = c.int(os.EADDRNOTAVAIL), // The address is not a local address on this machine.
- Broadcast_Disabled = c.int(os.EACCES), // To bind a UDP socket to the broadcast address, the appropriate socket option must be set.
- Address_Family_Mismatch = c.int(os.EFAULT), // The address family of the address does not match that of the socket.
- Already_Bound = c.int(os.EINVAL), // The socket is already bound to an address.
- No_Ports_Available = c.int(os.ENOBUFS), // There are not enough ephemeral ports available.
+_set_last_platform_error :: proc(err: i32) {
+ posix.errno(posix.Errno(err))
}
-Listen_Error :: enum c.int {
- None = 0,
- Address_In_Use = c.int(os.EADDRINUSE),
- Already_Connected = c.int(os.EISCONN),
- No_Socket_Descriptors_Available = c.int(os.EMFILE),
- No_Buffer_Space_Available = c.int(os.ENOBUFS),
- Nonlocal_Address = c.int(os.EADDRNOTAVAIL),
- Not_Socket = c.int(os.ENOTSOCK),
- Listening_Not_Supported_For_This_Socket = c.int(os.EOPNOTSUPP),
+_create_socket_error :: proc() -> Create_Socket_Error {
+ #partial switch posix.errno() {
+ case .EMFILE, .ENOBUFS, .ENOMEM, .EPROTONOSUPPORT, .EISCONN, .ENFILE:
+ return .Insufficient_Resources
+ case .EAFNOSUPPORT, .EPROTOTYPE:
+ return .Invalid_Argument
+ case .EACCES:
+ return .Insufficient_Permissions
+ case:
+ return .Unknown
+ }
}
-Accept_Error :: enum c.int {
- None = 0,
- // TODO(tetra): Is this error actually possible here? Or is like Linux, in which case we can remove it.
- Reset = c.int(os.ECONNRESET),
- Not_Listening = c.int(os.EINVAL),
- No_Socket_Descriptors_Available_For_Client_Socket = c.int(os.EMFILE),
- No_Buffer_Space_Available = c.int(os.ENOBUFS),
- Not_Socket = c.int(os.ENOTSOCK),
- Not_Connection_Oriented_Socket = c.int(os.EOPNOTSUPP),
-
- // TODO: we may need special handling for this; maybe make a socket a struct with metadata?
- Would_Block = c.int(os.EWOULDBLOCK),
+_dial_error :: proc() -> Dial_Error {
+ #partial switch posix.errno() {
+ case .ENOBUFS:
+ return .Insufficient_Resources
+ case .EAFNOSUPPORT, .EBADF, .EFAULT, .EINVAL, .ENOTSOCK, .EPROTOTYPE, .EADDRNOTAVAIL:
+ return .Invalid_Argument
+ case .EISCONN:
+ return .Already_Connected
+ case .EALREADY:
+ return .Already_Connecting
+ case .EADDRINUSE:
+ return .Address_In_Use
+ case .ENETDOWN:
+ return .Network_Unreachable
+ case .EHOSTUNREACH:
+ return .Host_Unreachable
+ case .ECONNREFUSED:
+ return .Refused
+ case .ECONNRESET:
+ return .Reset
+ case .ETIMEDOUT:
+ return .Timeout
+ case .EINPROGRESS:
+ return .Would_Block
+ case .EINTR:
+ return .Interrupted
+ case .EACCES:
+ return .Broadcast_Not_Supported
+ case:
+ return .Unknown
+ }
}
-TCP_Recv_Error :: enum c.int {
- None = 0,
- Shutdown = c.int(os.ESHUTDOWN),
- Not_Connected = c.int(os.ENOTCONN),
-
- // TODO(tetra): Is this error actually possible here?
- Connection_Broken = c.int(os.ENETRESET),
- Not_Socket = c.int(os.ENOTSOCK),
- Aborted = c.int(os.ECONNABORTED),
-
- // TODO(tetra): Determine when this is different from the syscall returning n=0 and maybe normalize them?
- Connection_Closed = c.int(os.ECONNRESET),
- Offline = c.int(os.ENETDOWN),
- Host_Unreachable = c.int(os.EHOSTUNREACH),
- Interrupted = c.int(os.EINTR),
-
- // NOTE: No, really. Presumably this means something different for nonblocking sockets...
- Timeout = c.int(os.EWOULDBLOCK),
+_bind_error :: proc() -> Bind_Error {
+ #partial switch posix.errno() {
+ case .EADDRNOTAVAIL, .EAFNOSUPPORT, .EBADF, .EDESTADDRREQ, .EFAULT, .ENOTSOCK, .EOPNOTSUPP:
+ return .Invalid_Argument
+ case .EINVAL:
+ return .Already_Bound
+ case .EACCES:
+ return .Insufficient_Permissions_For_Address
+ case .EADDRINUSE:
+ return .Address_In_Use
+ case:
+ return .Unknown
+ }
}
-UDP_Recv_Error :: enum c.int {
- None = 0,
- Buffer_Too_Small = c.int(os.EMSGSIZE), // The buffer is too small to fit the entire message, and the message was truncated. When this happens, the rest of message is lost.
- Not_Socket = c.int(os.ENOTSOCK), // The so-called socket is not an open socket.
- Not_Descriptor = c.int(os.EBADF), // The so-called socket is, in fact, not even a valid descriptor.
- Bad_Buffer = c.int(os.EFAULT), // The buffer did not point to a valid location in memory.
- Interrupted = c.int(os.EINTR), // A signal occurred before any data was transmitted. See signal(7).
-
- // The send timeout duration passed before all data was sent. See Socket_Option.Send_Timeout.
- // NOTE: No, really. Presumably this means something different for nonblocking sockets...
- Timeout = c.int(os.EWOULDBLOCK),
- Socket_Not_Bound = c.int(os.EINVAL), // The socket must be bound for this operation, but isn't.
+_listen_error :: proc() -> Listen_Error {
+ #partial switch posix.errno() {
+ case .EBADF, .ENOTSOCK:
+ return .Invalid_Argument
+ case .EDESTADDRREQ, .EOPNOTSUPP:
+ return .Unsupported_Socket
+ case .EINVAL:
+ return .Already_Connected
+ case:
+ return .Unknown
+ }
}
-TCP_Send_Error :: enum c.int {
- None = 0,
-
- Aborted = c.int(os.ECONNABORTED),
- Connection_Closed = c.int(os.ECONNRESET),
- Not_Connected = c.int(os.ENOTCONN),
- Shutdown = c.int(os.ESHUTDOWN),
-
- // The send queue was full.
- // This is usually a transient issue.
- //
- // This also shouldn't normally happen on Linux, as data is dropped if it
- // doesn't fit in the send queue.
- No_Buffer_Space_Available = c.int(os.ENOBUFS),
- Offline = c.int(os.ENETDOWN),
- Host_Unreachable = c.int(os.EHOSTUNREACH),
- Interrupted = c.int(os.EINTR), // A signal occurred before any data was transmitted. See signal(7).
-
- // NOTE: No, really. Presumably this means something different for nonblocking sockets...
- // The send timeout duration passed before all data was sent. See Socket_Option.Send_Timeout.
- Timeout = c.int(os.EWOULDBLOCK),
- Not_Socket = c.int(os.ENOTSOCK), // The so-called socket is not an open socket.
+_accept_error :: proc() -> Accept_Error {
+ #partial switch posix.errno() {
+ case .EMFILE, .ENFILE, .ENOMEM:
+ return .Insufficient_Resources
+ case .EBADF, .ENOTSOCK, .EFAULT:
+ return .Invalid_Argument
+ case .EOPNOTSUPP:
+ return .Unsupported_Socket
+ case .ECONNABORTED:
+ return .Aborted
+ case .EWOULDBLOCK:
+ return .Would_Block
+ case .EINTR:
+ return .Interrupted
+ case:
+ return .Unknown
+ }
}
-// TODO
-UDP_Send_Error :: enum c.int {
- None = 0,
- Message_Too_Long = c.int(os.EMSGSIZE), // The message is larger than the maximum UDP packet size. No data was sent.
-
- // TODO: not sure what the exact circumstances for this is yet
- Network_Unreachable = c.int(os.ENETUNREACH),
- No_Outbound_Ports_Available = c.int(os.EAGAIN), // There are no more emphemeral outbound ports available to bind the socket to, in order to send.
-
- // The send timeout duration passed before all data was sent. See Socket_Option.Send_Timeout.
- // NOTE: No, really. Presumably this means something different for nonblocking sockets...
- Timeout = c.int(os.EWOULDBLOCK),
- Not_Socket = c.int(os.ENOTSOCK), // The so-called socket is not an open socket.
- Not_Descriptor = c.int(os.EBADF), // The so-called socket is, in fact, not even a valid descriptor.
- Bad_Buffer = c.int(os.EFAULT), // The buffer did not point to a valid location in memory.
- Interrupted = c.int(os.EINTR), // A signal occurred before any data was transmitted. See signal(7).
-
- // The send queue was full.
- // This is usually a transient issue.
- //
- // This also shouldn't normally happen on Linux, as data is dropped if it
- // doesn't fit in the send queue.
- No_Buffer_Space_Available = c.int(os.ENOBUFS),
- No_Memory_Available = c.int(os.ENOMEM), // No memory was available to properly manage the send queue.
+_tcp_recv_error :: proc() -> TCP_Recv_Error {
+ #partial switch posix.errno() {
+ case .EBADF, .EFAULT, .EINVAL, .ENOTSOCK, .EOPNOTSUPP:
+ return .Invalid_Argument
+ case .ENOBUFS:
+ return .Insufficient_Resources
+ case .ENOTCONN:
+ return .Not_Connected
+ case .ECONNRESET:
+ return .Connection_Closed
+ case .ETIMEDOUT:
+ return .Timeout
+ case .EAGAIN:
+ return .Would_Block
+ case .EINTR:
+ return .Interrupted
+ case:
+ return .Unknown
+ }
}
-Shutdown_Manner :: enum c.int {
- Receive = c.int(os.SHUT_RD),
- Send = c.int(os.SHUT_WR),
- Both = c.int(os.SHUT_RDWR),
+_udp_recv_error :: proc() -> UDP_Recv_Error {
+ #partial switch posix.errno() {
+ case .EBADF, .EFAULT, .EINVAL, .ENOTSOCK, .EOPNOTSUPP, .EMSGSIZE:
+ return .Invalid_Argument
+ case .ENOBUFS, .ENOMEM:
+ return .Insufficient_Resources
+ case .ECONNRESET, .ENOTCONN:
+ return .Connection_Refused
+ case .ETIMEDOUT:
+ return .Timeout
+ case .EAGAIN:
+ return .Would_Block
+ case .EINTR:
+ return .Interrupted
+ case:
+ return .Unknown
+ }
}
-Shutdown_Error :: enum c.int {
- None = 0,
- Aborted = c.int(os.ECONNABORTED),
- Reset = c.int(os.ECONNRESET),
- Offline = c.int(os.ENETDOWN),
- Not_Connected = c.int(os.ENOTCONN),
- Not_Socket = c.int(os.ENOTSOCK),
- Invalid_Manner = c.int(os.EINVAL),
+_tcp_send_error :: proc() -> TCP_Send_Error {
+ #partial switch posix.errno() {
+ case .EACCES, .EBADF, .EFAULT, .EMSGSIZE, .ENOTSOCK, .EOPNOTSUPP:
+ return .Invalid_Argument
+ case .ENOBUFS:
+ return .Insufficient_Resources
+ case .ECONNRESET, .EPIPE:
+ return .Connection_Closed
+ case .ENOTCONN:
+ return .Not_Connected
+ case .EHOSTUNREACH:
+ return .Host_Unreachable
+ case .ENETDOWN, .ENETUNREACH:
+ return .Network_Unreachable
+ case .ETIMEDOUT:
+ return .Timeout
+ case .EAGAIN:
+ return .Would_Block
+ case .EINTR:
+ return .Interrupted
+ case:
+ return .Unknown
+ }
}
-Socket_Option_Error :: enum c.int {
- None = 0,
- Offline = c.int(os.ENETDOWN),
- Timeout_When_Keepalive_Set = c.int(os.ENETRESET),
- Invalid_Option_For_Socket = c.int(os.ENOPROTOOPT),
- Reset_When_Keepalive_Set = c.int(os.ENOTCONN),
- Not_Socket = c.int(os.ENOTSOCK),
+_udp_send_error :: proc() -> UDP_Send_Error {
+ #partial switch posix.errno() {
+ case .EACCES, .EBADF, .EFAULT, .EMSGSIZE, .ENOTSOCK, .EOPNOTSUPP, .EAFNOSUPPORT, .EDESTADDRREQ:
+ return .Invalid_Argument
+ case .ENOBUFS, .ENOMEM:
+ return .Insufficient_Resources
+ case .ECONNRESET, .EPIPE:
+ return .Connection_Refused
+ case .EHOSTUNREACH:
+ return .Host_Unreachable
+ case .ENETDOWN, .ENETUNREACH:
+ return .Network_Unreachable
+ case .ETIMEDOUT:
+ return .Timeout
+ case .EAGAIN:
+ return .Would_Block
+ case .EINTR:
+ return .Interrupted
+ case:
+ return .Unknown
+ }
}
-Set_Blocking_Error :: enum c.int {
- None = 0,
+_shutdown_error :: proc() -> Shutdown_Error {
+ #partial switch posix.errno() {
+ case .EBADF, .EINVAL, .ENOTSOCK, .ENOTCONN:
+ return .Invalid_Argument
+ case:
+ return .Unknown
+ }
+}
- // TODO: Add errors for `set_blocking`
-}
\ No newline at end of file
+_socket_option_error :: proc() -> Socket_Option_Error {
+ #partial switch posix.errno() {
+ case .ENOBUFS:
+ return .Insufficient_Resources
+ case .EBADF, .ENOTSOCK, .EISCONN:
+ return .Invalid_Socket
+ case .EINVAL, .ENOPROTOOPT:
+ return .Invalid_Option
+ case .EFAULT, .EDOM:
+ return .Invalid_Value
+ case:
+ return .Unknown
+ }
+}
+
+_set_blocking_error :: proc() -> Set_Blocking_Error {
+ #partial switch posix.errno() {
+ case .EBADF:
+ return .Invalid_Argument
+ case:
+ return .Unknown
+ }
+}
diff --git a/core/net/errors_freebsd.odin b/core/net/errors_freebsd.odin
index 486732a95..707ffd0dd 100644
--- a/core/net/errors_freebsd.odin
+++ b/core/net/errors_freebsd.odin
@@ -20,198 +20,267 @@ package net
Feoramund: FreeBSD platform code
*/
-import "core:c"
+import "core:reflect"
import "core:sys/freebsd"
-Create_Socket_Error :: enum c.int {
- None = 0,
- Access_Denied = cast(c.int)freebsd.Errno.EACCES,
- Family_Not_Supported_For_This_Socket = cast(c.int)freebsd.Errno.EAFNOSUPPORT,
- Full_Per_Process_Descriptor_Table = cast(c.int)freebsd.Errno.EMFILE,
- Full_System_File_Table = cast(c.int)freebsd.Errno.ENFILE,
- No_Buffer_Space_Available = cast(c.int)freebsd.Errno.ENOBUFS,
- Insufficient_Permission = cast(c.int)freebsd.Errno.EPERM,
- Protocol_Unsupported_In_Family = cast(c.int)freebsd.Errno.EPROTONOSUPPORT,
- Socket_Type_Unsupported_By_Protocol = cast(c.int)freebsd.Errno.EPROTOTYPE,
+@(private="file", thread_local)
+_last_error: freebsd.Errno
+
+_last_platform_error :: proc() -> i32 {
+ return i32(_last_error)
}
-Dial_Error :: enum c.int {
- None = 0,
- Port_Required = -1,
- Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
- Invalid_Namelen = cast(c.int)freebsd.Errno.EINVAL,
- Not_Socket = cast(c.int)freebsd.Errno.ENOTSOCK,
- Address_Unavailable = cast(c.int)freebsd.Errno.EADDRNOTAVAIL,
- Wrong_Family_For_Socket = cast(c.int)freebsd.Errno.EAFNOSUPPORT,
- Already_Connected = cast(c.int)freebsd.Errno.EISCONN,
- Timeout = cast(c.int)freebsd.Errno.ETIMEDOUT,
- Refused_By_Remote_Host = cast(c.int)freebsd.Errno.ECONNREFUSED,
- // `Refused` alias for `core:net` tests.
- // The above default name `Refused_By_Remote_Host` is more explicit.
- Refused = Refused_By_Remote_Host,
- Reset_By_Remote_Host = cast(c.int)freebsd.Errno.ECONNRESET,
- Network_Unreachable = cast(c.int)freebsd.Errno.ENETUNREACH,
- Host_Unreachable = cast(c.int)freebsd.Errno.EHOSTUNREACH,
- Address_In_Use = cast(c.int)freebsd.Errno.EADDRINUSE,
- Invalid_Address_Space = cast(c.int)freebsd.Errno.EFAULT,
- In_Progress = cast(c.int)freebsd.Errno.EINPROGRESS,
- Interrupted_By_Signal = cast(c.int)freebsd.Errno.EINTR,
- Previous_Attempt_Incomplete = cast(c.int)freebsd.Errno.EALREADY,
- Broadcast_Unavailable = cast(c.int)freebsd.Errno.EACCES,
- Auto_Port_Unavailable = cast(c.int)freebsd.Errno.EAGAIN,
-
- // NOTE: There are additional connect() error possibilities, but they are
- // strictly for addresses in the UNIX domain.
+_last_platform_error_string :: proc() -> string {
+ description, _ := reflect.enum_name_from_value(_last_error)
+ return description
}
-Bind_Error :: enum c.int {
- None = 0,
- Kernel_Resources_Unavailable = cast(c.int)freebsd.Errno.EAGAIN,
- Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
-
- // NOTE: bind() can also return EINVAL if the underlying `addrlen` is an
- // invalid length for the address family. This shouldn't happen for the net
- // package, but it's worth noting.
- Already_Bound = cast(c.int)freebsd.Errno.EINVAL,
- Not_Socket = cast(c.int)freebsd.Errno.ENOTSOCK,
- Given_Nonlocal_Address = cast(c.int)freebsd.Errno.EADDRNOTAVAIL,
- Address_In_Use = cast(c.int)freebsd.Errno.EADDRINUSE,
- Address_Family_Mismatch = cast(c.int)freebsd.Errno.EAFNOSUPPORT,
- Protected_Address = cast(c.int)freebsd.Errno.EACCES,
- Invalid_Address_Space = cast(c.int)freebsd.Errno.EFAULT,
-
- // NOTE: There are additional bind() error possibilities, but they are
- // strictly for addresses in the UNIX domain.
+_set_last_platform_error :: proc(err: i32) {
+ _last_error = freebsd.Errno(err)
}
-Listen_Error :: enum c.int {
- None = 0,
- Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
- Socket_Not_Bound = cast(c.int)freebsd.Errno.EDESTADDRREQ,
- Already_Connected = cast(c.int)freebsd.Errno.EINVAL,
- Not_Socket = cast(c.int)freebsd.Errno.ENOTSOCK,
- Listening_Not_Supported_For_This_Socket = cast(c.int)freebsd.Errno.EOPNOTSUPP,
+_create_socket_error :: proc(errno: freebsd.Errno) -> Create_Socket_Error {
+ assert(errno != nil)
+ _last_error = errno
+
+ #partial switch errno {
+ case .EMFILE, .ENFILE, .ENOBUFS, .EPROTONOSUPPORT:
+ return .Insufficient_Resources
+ case .EAFNOSUPPORT, .EPROTOTYPE:
+ return .Invalid_Argument
+ case .EACCES, .EPERM:
+ return .Insufficient_Permissions
+ case:
+ return .Unknown
+ }
}
-Accept_Error :: enum c.int {
- None = 0,
- Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
- Interrupted = cast(c.int)freebsd.Errno.EINTR,
- Full_Per_Process_Descriptor_Table = cast(c.int)freebsd.Errno.EMFILE,
- Full_System_File_Table = cast(c.int)freebsd.Errno.ENFILE,
- Not_Socket = cast(c.int)freebsd.Errno.ENOTSOCK,
- Listen_Not_Called_On_Socket_Yet = cast(c.int)freebsd.Errno.EINVAL,
- Address_Not_Writable = cast(c.int)freebsd.Errno.EFAULT,
+_dial_error :: proc(errno: freebsd.Errno) -> Dial_Error {
+ assert(errno != nil)
+ _last_error = errno
- // NOTE: This is the same as EWOULDBLOCK.
- No_Connections_Available = cast(c.int)freebsd.Errno.EAGAIN,
- // `Would_Block` alias for `core:net` tests.
- Would_Block = cast(c.int)freebsd.Errno.EAGAIN,
-
- New_Connection_Aborted = cast(c.int)freebsd.Errno.ECONNABORTED,
+ #partial switch errno {
+ case .EBADF, .EINVAL, .ENOTSOCK, .EADDRNOTAVAIL, .EAFNOSUPPORT, .EFAULT, .EAGAIN:
+ return .Invalid_Argument
+ case .EISCONN:
+ return .Already_Connected
+ case .EALREADY:
+ return .Already_Connecting
+ case .EADDRINUSE:
+ return .Address_In_Use
+ case .ENETUNREACH:
+ return .Network_Unreachable
+ case .EHOSTUNREACH:
+ return .Host_Unreachable
+ case .ECONNREFUSED:
+ return .Refused
+ case .ECONNRESET:
+ return .Reset
+ case .ETIMEDOUT:
+ return .Timeout
+ case .EINPROGRESS:
+ return .Would_Block
+ case .EINTR:
+ return .Interrupted
+ case .EACCES:
+ return .Broadcast_Not_Supported
+ case:
+ return .Unknown
+ }
}
-TCP_Recv_Error :: enum c.int {
- None = 0,
- Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
- Connection_Closed = cast(c.int)freebsd.Errno.ECONNRESET,
- Not_Connected = cast(c.int)freebsd.Errno.ENOTCONN,
- Not_Socket = cast(c.int)freebsd.Errno.ENOTSOCK,
+_bind_error :: proc(errno: freebsd.Errno) -> Bind_Error {
+ assert(errno != nil)
+ _last_error = errno
- // NOTE(Feoramund): The next two errors are only relevant for recvmsg(),
- // but I'm including them for completeness's sake.
- Full_Table_And_Pending_Data = cast(c.int)freebsd.Errno.EMFILE,
- Invalid_Message_Size = cast(c.int)freebsd.Errno.EMSGSIZE,
-
- Timeout = cast(c.int)freebsd.Errno.EAGAIN,
- Interrupted_By_Signal = cast(c.int)freebsd.Errno.EINTR,
- Buffer_Pointer_Outside_Address_Space = cast(c.int)freebsd.Errno.EFAULT,
+ #partial switch errno {
+ case .EAGAIN, .ENOTSOCK, .EADDRNOTAVAIL, .EAFNOSUPPORT, .EFAULT:
+ return .Insufficient_Resources
+ case .EBADF:
+ return .Invalid_Argument
+ case .EINVAL:
+ return .Already_Bound
+ case .EACCES:
+ return .Insufficient_Permissions_For_Address
+ case .EADDRINUSE:
+ return .Address_In_Use
+ case:
+ return .Unknown
+ }
}
-UDP_Recv_Error :: enum c.int {
- None = 0,
- Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
- Connection_Closed = cast(c.int)freebsd.Errno.ECONNRESET,
- Not_Connected = cast(c.int)freebsd.Errno.ENOTCONN,
- Not_Socket = cast(c.int)freebsd.Errno.ENOTSOCK,
+_listen_error :: proc(errno: freebsd.Errno) -> Listen_Error {
+ assert(errno != nil)
+ _last_error = errno
- // NOTE(Feoramund): The next two errors are only relevant for recvmsg(),
- // but I'm including them for completeness's sake.
- Full_Table_And_Data_Discarded = cast(c.int)freebsd.Errno.EMFILE,
- Invalid_Message_Size = cast(c.int)freebsd.Errno.EMSGSIZE,
-
- Timeout = cast(c.int)freebsd.Errno.EAGAIN,
- Interrupted_By_Signal = cast(c.int)freebsd.Errno.EINTR,
- Buffer_Pointer_Outside_Address_Space = cast(c.int)freebsd.Errno.EFAULT,
+ #partial switch errno {
+ case .EBADF, .ENOTSOCK:
+ return .Invalid_Argument
+ case .EDESTADDRREQ, .EOPNOTSUPP:
+ return .Unsupported_Socket
+ case .EINVAL:
+ return .Already_Connected
+ case:
+ return .Unknown
+ }
}
-TCP_Send_Error :: enum c.int {
- None = 0,
- Connection_Closed = cast(c.int)freebsd.Errno.ECONNRESET,
- Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
- Broadcast_Status_Mismatch = cast(c.int)freebsd.Errno.EACCES,
- Not_Connected = cast(c.int)freebsd.Errno.ENOTCONN,
- Not_Socket = cast(c.int)freebsd.Errno.ENOTSOCK,
- Argument_In_Invalid_Address_Space = cast(c.int)freebsd.Errno.EFAULT,
+_accept_error :: proc(errno: freebsd.Errno) -> Accept_Error {
+ assert(errno != nil)
+ _last_error = errno
- Message_Size_Breaks_Atomicity = cast(c.int)freebsd.Errno.EMSGSIZE,
-
- /* The socket is marked non-blocking, or MSG_DONTWAIT is
- specified, and the requested operation would block. */
- Would_Block = cast(c.int)freebsd.Errno.EAGAIN,
-
- /* NOTE: This error arises for two distinct reasons:
-
- 1. The system was unable to allocate an internal buffer.
- The operation may succeed when buffers become available.
-
- 2. The output queue for a network interface was full.
- This generally indicates that the interface has stopped
- sending, but may be caused by transient congestion.
- */
- No_Buffer_Space_Available = cast(c.int)freebsd.Errno.ENOBUFS,
-
- Host_Unreachable = cast(c.int)freebsd.Errno.EHOSTUNREACH,
- Already_Connected = cast(c.int)freebsd.Errno.EISCONN,
- ICMP_Unreachable = cast(c.int)freebsd.Errno.ECONNREFUSED,
- Host_Down = cast(c.int)freebsd.Errno.EHOSTDOWN,
- Network_Down = cast(c.int)freebsd.Errno.ENETDOWN,
- Jailed_Socket_Tried_To_Escape = cast(c.int)freebsd.Errno.EADDRNOTAVAIL,
- Cannot_Send_More_Data = cast(c.int)freebsd.Errno.EPIPE,
+ #partial switch errno {
+ case .EMFILE, .ENFILE:
+ return .Insufficient_Resources
+ case .EBADF, .ENOTSOCK, .EFAULT:
+ return .Invalid_Argument
+ case .EINVAL:
+ return .Not_Listening
+ case .ECONNABORTED:
+ return .Aborted
+ case .EWOULDBLOCK:
+ return .Would_Block
+ case .EINTR:
+ return .Interrupted
+ case:
+ return .Unknown
+ }
}
-// NOTE(Feoramund): The same as TCP errors go, as far as I'm aware.
-UDP_Send_Error :: distinct TCP_Send_Error
+_tcp_recv_error :: proc(errno: freebsd.Errno) -> TCP_Recv_Error {
+ assert(errno != nil)
+ _last_error = errno
-Shutdown_Manner :: enum c.int {
- Receive = cast(c.int)freebsd.Shutdown_Method.RD,
- Send = cast(c.int)freebsd.Shutdown_Method.WR,
- Both = cast(c.int)freebsd.Shutdown_Method.RDWR,
+ #partial switch errno {
+ case .EBADF, .ENOTSOCK, .EFAULT:
+ return .Invalid_Argument
+ case .ENOTCONN:
+ return .Not_Connected
+ case .ECONNRESET:
+ return .Connection_Closed
+ case .ETIMEDOUT:
+ return .Timeout
+ case .EAGAIN:
+ return .Would_Block
+ case .EINTR:
+ return .Interrupted
+ case:
+ return .Unknown
+ }
}
-Shutdown_Error :: enum c.int {
- None = 0,
- Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
- Invalid_Manner = cast(c.int)freebsd.Errno.EINVAL,
- Not_Connected = cast(c.int)freebsd.Errno.ENOTCONN,
- Not_Socket = cast(c.int)freebsd.Errno.ENOTSOCK,
+_udp_recv_error :: proc(errno: freebsd.Errno) -> UDP_Recv_Error {
+ assert(errno != nil)
+ _last_error = errno
+
+ #partial switch errno {
+ case .EBADF, .ENOTSOCK, .EFAULT:
+ return .Invalid_Argument
+ case .ECONNRESET, .ENOTCONN:
+ return .Connection_Refused
+ case .ETIMEDOUT:
+ return .Timeout
+ case .EAGAIN:
+ return .Would_Block
+ case .EINTR:
+ return .Interrupted
+ case:
+ return .Unknown
+ }
}
-Socket_Option_Error :: enum c.int {
- None = 0,
- Value_Out_Of_Range = -1,
- Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
- Not_Socket = cast(c.int)freebsd.Errno.ENOTSOCK,
- Unknown_Option_For_Level = cast(c.int)freebsd.Errno.ENOPROTOOPT,
- Argument_In_Invalid_Address_Space = cast(c.int)freebsd.Errno.EFAULT,
- // This error can arise for many different reasons.
- Invalid_Value = cast(c.int)freebsd.Errno.EINVAL,
- System_Memory_Allocation_Failed = cast(c.int)freebsd.Errno.ENOMEM,
- Insufficient_System_Resources = cast(c.int)freebsd.Errno.ENOBUFS,
+_tcp_send_error :: proc(errno: freebsd.Errno) -> TCP_Send_Error {
+ assert(errno != nil)
+ _last_error = errno
+
+ #partial switch errno {
+ case .EBADF, .EACCES, .ENOTSOCK, .EFAULT, .EMSGSIZE:
+ return .Invalid_Argument
+ case .ENOBUFS:
+ return .Insufficient_Resources
+ case .ECONNRESET, .EPIPE:
+ return .Connection_Closed
+ case .ENOTCONN:
+ return .Not_Connected
+ case .EHOSTUNREACH:
+ return .Host_Unreachable
+ case .EHOSTDOWN:
+ return .Host_Unreachable
+ case .ENETDOWN:
+ return .Network_Unreachable
+ case .EAGAIN:
+ return .Would_Block
+ case .EINTR:
+ return .Interrupted
+ case:
+ return .Unknown
+ }
}
-Set_Blocking_Error :: enum c.int {
- None = 0,
- Not_Descriptor = cast(c.int)freebsd.Errno.EBADF,
- Wrong_Descriptor = cast(c.int)freebsd.Errno.ENOTTY,
+_udp_send_error :: proc(errno: freebsd.Errno) -> UDP_Send_Error {
+ assert(errno != nil)
+ _last_error = errno
+
+ #partial switch errno {
+ case .EBADF, .EACCES, .ENOTSOCK, .EFAULT, .EMSGSIZE:
+ return .Invalid_Argument
+ case .ENOBUFS:
+ return .Insufficient_Resources
+ case .ECONNRESET, .EPIPE:
+ return .Connection_Refused
+ case .EHOSTUNREACH:
+ return .Host_Unreachable
+ case .EHOSTDOWN:
+ return .Host_Unreachable
+ case .ENETDOWN:
+ return .Network_Unreachable
+ case .EAGAIN:
+ return .Would_Block
+ case .EINTR:
+ return .Interrupted
+ case:
+ return .Unknown
+ }
+}
+
+_shutdown_error :: proc(errno: freebsd.Errno) -> Shutdown_Error {
+ assert(errno != nil)
+ _last_error = errno
+
+ #partial switch errno {
+ case .EBADF, .EINVAL, .ENOTSOCK, .ENOTCONN:
+ return .Invalid_Argument
+ case:
+ return .Unknown
+ }
+}
+
+_socket_option_error :: proc(errno: freebsd.Errno) -> Socket_Option_Error {
+ assert(errno != nil)
+ _last_error = errno
+
+ #partial switch errno {
+ case .ENOMEM, .ENOBUFS:
+ return .Insufficient_Resources
+ case .EBADF, .ENOTSOCK:
+ return .Invalid_Socket
+ case .ENOPROTOOPT:
+ return .Invalid_Option
+ case .EINVAL, .EFAULT:
+ return .Invalid_Value
+ case:
+ return .Unknown
+ }
+}
+
+_set_blocking_error :: proc(errno: freebsd.Errno) -> Set_Blocking_Error {
+ assert(errno != nil)
+ _last_error = errno
+
+ #partial switch errno {
+ case .EBADF, .ENOTTY:
+ return .Invalid_Argument
+ case:
+ return .Unknown
+ }
}
diff --git a/core/net/errors_linux.odin b/core/net/errors_linux.odin
index 3cd51e6fd..237579f28 100644
--- a/core/net/errors_linux.odin
+++ b/core/net/errors_linux.odin
@@ -21,181 +21,269 @@ package net
Feoramund: FreeBSD platform code
*/
-import "core:c"
+import "core:reflect"
import "core:sys/linux"
-Create_Socket_Error :: enum c.int {
- None = 0,
- Family_Not_Supported_For_This_Socket = c.int(linux.Errno.EAFNOSUPPORT),
- No_Socket_Descriptors_Available = c.int(linux.Errno.EMFILE),
- No_Buffer_Space_Available = c.int(linux.Errno.ENOBUFS),
- No_Memory_Available_Available = c.int(linux.Errno.ENOMEM),
- Protocol_Unsupported_By_System = c.int(linux.Errno.EPROTONOSUPPORT),
- Wrong_Protocol_For_Socket = c.int(linux.Errno.EPROTONOSUPPORT),
- Family_And_Socket_Type_Mismatch = c.int(linux.Errno.EPROTONOSUPPORT),
+@(private="file", thread_local)
+_last_error: linux.Errno
+
+_last_platform_error :: proc() -> i32 {
+ return i32(_last_error)
}
-Dial_Error :: enum c.int {
- None = 0,
- Port_Required = -1,
-
- Address_In_Use = c.int(linux.Errno.EADDRINUSE),
- In_Progress = c.int(linux.Errno.EINPROGRESS),
- Cannot_Use_Any_Address = c.int(linux.Errno.EADDRNOTAVAIL),
- Wrong_Family_For_Socket = c.int(linux.Errno.EAFNOSUPPORT),
- Refused = c.int(linux.Errno.ECONNREFUSED),
- Is_Listening_Socket = c.int(linux.Errno.EACCES),
- Already_Connected = c.int(linux.Errno.EISCONN),
- Network_Unreachable = c.int(linux.Errno.ENETUNREACH), // Device is offline
- Host_Unreachable = c.int(linux.Errno.EHOSTUNREACH), // Remote host cannot be reached
- No_Buffer_Space_Available = c.int(linux.Errno.ENOBUFS),
- Not_Socket = c.int(linux.Errno.ENOTSOCK),
- Timeout = c.int(linux.Errno.ETIMEDOUT),
-
- // TODO: we may need special handling for this; maybe make a socket a struct with metadata?
- Would_Block = c.int(linux.Errno.EWOULDBLOCK),
+_last_platform_error_string :: proc() -> string {
+ description, _ := reflect.enum_name_from_value(_last_error)
+ return description
}
-Bind_Error :: enum c.int {
- None = 0,
- Address_In_Use = c.int(linux.Errno.EADDRINUSE), // Another application is currently bound to this endpoint.
- Given_Nonlocal_Address = c.int(linux.Errno.EADDRNOTAVAIL), // The address is not a local address on this machine.
- Broadcast_Disabled = c.int(linux.Errno.EACCES), // To bind a UDP socket to the broadcast address, the appropriate socket option must be set.
- Address_Family_Mismatch = c.int(linux.Errno.EFAULT), // The address family of the address does not match that of the socket.
- Already_Bound = c.int(linux.Errno.EINVAL), // The socket is already bound to an address.
- No_Ports_Available = c.int(linux.Errno.ENOBUFS), // There are not enough ephemeral ports available.
+_set_last_platform_error :: proc(err: i32) {
+ _last_error = linux.Errno(err)
}
-Listen_Error :: enum c.int {
- None = 0,
- Address_In_Use = c.int(linux.Errno.EADDRINUSE),
- Already_Connected = c.int(linux.Errno.EISCONN),
- No_Socket_Descriptors_Available = c.int(linux.Errno.EMFILE),
- No_Buffer_Space_Available = c.int(linux.Errno.ENOBUFS),
- Nonlocal_Address = c.int(linux.Errno.EADDRNOTAVAIL),
- Not_Socket = c.int(linux.Errno.ENOTSOCK),
- Listening_Not_Supported_For_This_Socket = c.int(linux.Errno.EOPNOTSUPP),
+_create_socket_error :: proc(errno: linux.Errno) -> Create_Socket_Error {
+ assert(errno != nil)
+ _last_error = errno
+
+ #partial switch errno {
+ case .EMFILE, .ENFILE, .ENOBUFS, .EPROTONOSUPPORT:
+ return .Insufficient_Resources
+ case .EAFNOSUPPORT, .EPROTOTYPE:
+ return .Invalid_Argument
+ case .EACCES, .EPERM:
+ return .Insufficient_Permissions
+ case:
+ return .Unknown
+ }
}
-Accept_Error :: enum c.int {
- None = 0,
- Not_Listening = c.int(linux.Errno.EINVAL),
- No_Socket_Descriptors_Available_For_Client_Socket = c.int(linux.Errno.EMFILE),
- No_Buffer_Space_Available = c.int(linux.Errno.ENOBUFS),
- Not_Socket = c.int(linux.Errno.ENOTSOCK),
- Not_Connection_Oriented_Socket = c.int(linux.Errno.EOPNOTSUPP),
+_dial_error :: proc(errno: linux.Errno) -> Dial_Error {
+ assert(errno != nil)
+ _last_error = errno
- // TODO: we may need special handling for this; maybe make a socket a struct with metadata?
- Would_Block = c.int(linux.Errno.EWOULDBLOCK),
+ #partial switch errno {
+ case .EAGAIN:
+ return .Insufficient_Resources
+ case .EBADF, .EINVAL, .ENOTSOCK, .EADDRNOTAVAIL, .EAFNOSUPPORT, .EFAULT:
+ return .Invalid_Argument
+ case .EISCONN:
+ return .Already_Connected
+ case .EALREADY:
+ return .Already_Connecting
+ case .EADDRINUSE:
+ return .Address_In_Use
+ case .ENETUNREACH:
+ return .Network_Unreachable
+ case .EHOSTUNREACH:
+ return .Host_Unreachable
+ case .ECONNREFUSED:
+ return .Refused
+ case .ECONNRESET:
+ return .Reset
+ case .ETIMEDOUT:
+ return .Timeout
+ case .EINPROGRESS:
+ return .Would_Block
+ case .EINTR:
+ return .Interrupted
+ case .EACCES:
+ return .Broadcast_Not_Supported
+ case:
+ return .Unknown
+ }
}
-TCP_Recv_Error :: enum c.int {
- None = 0,
- Shutdown = c.int(linux.Errno.ESHUTDOWN),
- Not_Connected = c.int(linux.Errno.ENOTCONN),
- Connection_Broken = c.int(linux.Errno.ENETRESET),
- Not_Socket = c.int(linux.Errno.ENOTSOCK),
- Aborted = c.int(linux.Errno.ECONNABORTED),
+_bind_error :: proc(errno: linux.Errno) -> Bind_Error {
+ assert(errno != nil)
+ _last_error = errno
- // TODO(tetra): Determine when this is different from the syscall returning n=0 and maybe normalize them?
- Connection_Closed = c.int(linux.Errno.ECONNRESET),
- Offline = c.int(linux.Errno.ENETDOWN),
- Host_Unreachable = c.int(linux.Errno.EHOSTUNREACH),
- Interrupted = c.int(linux.Errno.EINTR),
- Timeout = c.int(linux.Errno.EWOULDBLOCK), // NOTE: No, really. Presumably this means something different for nonblocking sockets...
+ #partial switch errno {
+ case .EAGAIN, .ENOTSOCK, .EADDRNOTAVAIL, .EAFNOSUPPORT, .EFAULT:
+ return .Insufficient_Resources
+ case .EINVAL:
+ return .Already_Bound
+ case .EBADF:
+ return .Invalid_Argument
+ case .EACCES:
+ return .Insufficient_Permissions_For_Address
+ case .EADDRINUSE:
+ return .Address_In_Use
+ case:
+ return .Unknown
+ }
}
-UDP_Recv_Error :: enum c.int {
- None = 0,
+_listen_error :: proc(errno: linux.Errno) -> Listen_Error {
+ assert(errno != nil)
+ _last_error = errno
- Buffer_Too_Small = c.int(linux.Errno.EMSGSIZE), // The buffer is too small to fit the entire message, and the message was truncated. When this happens, the rest of message is lost.
- Not_Socket = c.int(linux.Errno.ENOTSOCK), // The so-called socket is not an open socket.
- Not_Descriptor = c.int(linux.Errno.EBADF), // The so-called socket is, in fact, not even a valid descriptor.
- Bad_Buffer = c.int(linux.Errno.EFAULT), // The buffer did not point to a valid location in memory.
- Interrupted = c.int(linux.Errno.EINTR), // A signal occurred before any data was transmitted. See signal(7).
-
- // The send timeout duration passed before all data was received. See Socket_Option.Receive_Timeout.
- // NOTE: No, really. Presumably this means something different for nonblocking sockets...
- Timeout = c.int(linux.Errno.EWOULDBLOCK),
- Socket_Not_Bound = c.int(linux.Errno.EINVAL), // The socket must be bound for this operation, but isn't.
+ #partial switch errno {
+ case .EBADF, .ENOTSOCK:
+ return .Invalid_Argument
+ case .EDESTADDRREQ, .EOPNOTSUPP:
+ return .Unsupported_Socket
+ case .EINVAL:
+ return .Already_Connected
+ case:
+ return .Unknown
+ }
}
-TCP_Send_Error :: enum c.int {
- None = 0,
- Aborted = c.int(linux.Errno.ECONNABORTED),
- Connection_Closed = c.int(linux.Errno.ECONNRESET),
- Not_Connected = c.int(linux.Errno.ENOTCONN),
- Shutdown = c.int(linux.Errno.ESHUTDOWN),
+_accept_error :: proc(errno: linux.Errno) -> Accept_Error {
+ assert(errno != nil)
+ _last_error = errno
- // The send queue was full.
- // This is usually a transient issue.
- //
- // This also shouldn't normally happen on Linux, as data is dropped if it
- // doesn't fit in the send queue.
- No_Buffer_Space_Available = c.int(linux.Errno.ENOBUFS),
- Offline = c.int(linux.Errno.ENETDOWN),
- Host_Unreachable = c.int(linux.Errno.EHOSTUNREACH),
- Interrupted = c.int(linux.Errno.EINTR), // A signal occurred before any data was transmitted. See signal(7).
- Timeout = c.int(linux.Errno.EWOULDBLOCK), // The send timeout duration passed before all data was sent. See Socket_Option.Send_Timeout.
- Not_Socket = c.int(linux.Errno.ENOTSOCK), // The so-called socket is not an open socket.
+ #partial switch errno {
+ case .EMFILE, .ENFILE, .ENOBUFS, .ENOMEM:
+ return .Insufficient_Resources
+ case .EBADF, .ENOTSOCK, .EFAULT:
+ return .Invalid_Argument
+ case .EINVAL:
+ return .Not_Listening
+ case .ECONNABORTED:
+ return .Aborted
+ case .EWOULDBLOCK:
+ return .Would_Block
+ case .EINTR:
+ return .Interrupted
+ case:
+ return .Unknown
+ }
}
-// TODO
-UDP_Send_Error :: enum c.int {
- None = 0,
- Message_Too_Long = c.int(linux.Errno.EMSGSIZE), // The message is larger than the maximum UDP packet size. No data was sent.
+_tcp_recv_error :: proc(errno: linux.Errno) -> TCP_Recv_Error {
+ assert(errno != nil)
+ _last_error = errno
- // TODO: not sure what the exact circumstances for this is yet
- Network_Unreachable = c.int(linux.Errno.ENETUNREACH),
- No_Outbound_Ports_Available = c.int(linux.Errno.EAGAIN), // There are no more emphemeral outbound ports available to bind the socket to, in order to send.
-
- // The send timeout duration passed before all data was sent. See Socket_Option.Send_Timeout.
- // NOTE: No, really. Presumably this means something different for nonblocking sockets...
- Timeout = c.int(linux.Errno.EWOULDBLOCK),
- Not_Socket = c.int(linux.Errno.ENOTSOCK), // The so-called socket is not an open socket.
- Not_Descriptor = c.int(linux.Errno.EBADF), // The so-called socket is, in fact, not even a valid descriptor.
- Bad_Buffer = c.int(linux.Errno.EFAULT), // The buffer did not point to a valid location in memory.
- Interrupted = c.int(linux.Errno.EINTR), // A signal occurred before any data was transmitted. See signal(7).
-
- // The send queue was full.
- // This is usually a transient issue.
- //
- // This also shouldn't normally happen on Linux, as data is dropped if it
- // doesn't fit in the send queue.
- No_Buffer_Space_Available = c.int(linux.Errno.ENOBUFS),
- No_Memory_Available = c.int(linux.Errno.ENOMEM), // No memory was available to properly manage the send queue.
+ #partial switch errno {
+ case .EBADF, .ENOTSOCK, .EFAULT:
+ return .Invalid_Argument
+ case .ENOTCONN:
+ return .Not_Connected
+ case .ECONNREFUSED, .ECONNRESET:
+ return .Connection_Closed
+ case .ETIMEDOUT:
+ return .Timeout
+ case .EAGAIN:
+ return .Would_Block
+ case .EINTR:
+ return .Interrupted
+ case:
+ return .Unknown
+ }
}
-// TODO(flysand): slight regression
-Shutdown_Manner :: enum c.int {
- Receive = c.int(linux.Shutdown_How.RD),
- Send = c.int(linux.Shutdown_How.WR),
- Both = c.int(linux.Shutdown_How.RDWR),
+_udp_recv_error :: proc(errno: linux.Errno) -> UDP_Recv_Error {
+ assert(errno != nil)
+ _last_error = errno
+
+ #partial switch errno {
+ case .EBADF, .ENOTSOCK, .EFAULT:
+ return .Invalid_Argument
+ case .ECONNREFUSED, .ENOTCONN, .ECONNRESET:
+ return .Connection_Refused
+ case .ETIMEDOUT:
+ return .Timeout
+ case .EAGAIN:
+ return .Would_Block
+ case .EINTR:
+ return .Interrupted
+ case:
+ return .Unknown
+ }
}
-Shutdown_Error :: enum c.int {
- None = 0,
- Aborted = c.int(linux.Errno.ECONNABORTED),
- Reset = c.int(linux.Errno.ECONNRESET),
- Offline = c.int(linux.Errno.ENETDOWN),
- Not_Connected = c.int(linux.Errno.ENOTCONN),
- Not_Socket = c.int(linux.Errno.ENOTSOCK),
- Invalid_Manner = c.int(linux.Errno.EINVAL),
+_tcp_send_error :: proc(errno: linux.Errno) -> TCP_Send_Error {
+ assert(errno != nil)
+ _last_error = errno
+
+ #partial switch errno {
+ case .EBADF, .EACCES, .ENOTSOCK, .EFAULT, .EMSGSIZE, .EDESTADDRREQ, .EINVAL, .EISCONN, .EOPNOTSUPP:
+ return .Invalid_Argument
+ case .ENOBUFS, .ENOMEM:
+ return .Insufficient_Resources
+ case .ECONNRESET, .EPIPE:
+ return .Connection_Closed
+ case .ENOTCONN:
+ return .Not_Connected
+ case .EHOSTUNREACH:
+ return .Host_Unreachable
+ case .EHOSTDOWN:
+ return .Host_Unreachable
+ case .ENETDOWN:
+ return .Network_Unreachable
+ case .EAGAIN:
+ return .Would_Block
+ case .EINTR:
+ return .Interrupted
+ case:
+ return .Unknown
+ }
}
-Socket_Option_Error :: enum c.int {
- None = 0,
- Offline = c.int(linux.Errno.ENETDOWN),
- Timeout_When_Keepalive_Set = c.int(linux.Errno.ENETRESET),
- Invalid_Option_For_Socket = c.int(linux.Errno.ENOPROTOOPT),
- Reset_When_Keepalive_Set = c.int(linux.Errno.ENOTCONN),
- Not_Socket = c.int(linux.Errno.ENOTSOCK),
+_udp_send_error :: proc(errno: linux.Errno) -> UDP_Send_Error {
+ assert(errno != nil)
+ _last_error = errno
+
+ #partial switch errno {
+ case .EBADF, .EACCES, .ENOTSOCK, .EFAULT, .EMSGSIZE, .EDESTADDRREQ, .EINVAL, .EISCONN, .EOPNOTSUPP:
+ return .Invalid_Argument
+ case .ENOBUFS, .ENOMEM:
+ return .Insufficient_Resources
+ case .ECONNRESET, .EPIPE:
+ return .Connection_Refused
+ case .EHOSTUNREACH:
+ return .Host_Unreachable
+ case .EHOSTDOWN:
+ return .Host_Unreachable
+ case .ENETDOWN:
+ return .Network_Unreachable
+ case .EAGAIN:
+ return .Would_Block
+ case .EINTR:
+ return .Interrupted
+ case:
+ return .Unknown
+ }
}
-Set_Blocking_Error :: enum c.int {
- None = 0,
+_shutdown_error :: proc(errno: linux.Errno) -> Shutdown_Error {
+ assert(errno != nil)
+ _last_error = errno
- // TODO: add errors occuring on followig calls:
- // flags, _ := linux.Errno.fcntl(sd, linux.Errno.F_GETFL, 0)
- // linux.Errno.fcntl(sd, linux.Errno.F_SETFL, flags | int(linux.Errno.O_NONBLOCK))
-}
\ No newline at end of file
+ #partial switch errno {
+ case .EBADF, .EINVAL, .ENOTSOCK, .ENOTCONN:
+ return .Invalid_Argument
+ case:
+ return .Unknown
+ }
+}
+
+_socket_option_error :: proc(errno: linux.Errno) -> Socket_Option_Error {
+ assert(errno != nil)
+ _last_error = errno
+
+ #partial switch errno {
+ case .ENOMEM, .ENOBUFS:
+ return .Insufficient_Resources
+ case .EBADF, .ENOTSOCK:
+ return .Invalid_Socket
+ case .ENOPROTOOPT, .EINVAL:
+ return .Invalid_Option
+ case .EFAULT, .EDOM:
+ return .Invalid_Value
+ case:
+ return .Unknown
+ }
+}
+
+_set_blocking_error :: proc(errno: linux.Errno) -> Set_Blocking_Error {
+ assert(errno != nil)
+ _last_error = errno
+
+ #partial switch errno {
+ case .EBADF:
+ return .Invalid_Argument
+ case:
+ return .Unknown
+ }
+}
diff --git a/core/net/errors_others.odin b/core/net/errors_others.odin
new file mode 100644
index 000000000..bda0fd28f
--- /dev/null
+++ b/core/net/errors_others.odin
@@ -0,0 +1,20 @@
+#+build !darwin
+#+build !linux
+#+build !freebsd
+#+build !windows
+package net
+
+@(private="file", thread_local)
+_last_error: i32
+
+_last_platform_error :: proc() -> i32 {
+ return _last_error
+}
+
+_last_platform_error_string :: proc() -> string {
+ return ""
+}
+
+_set_last_platform_error :: proc(err: i32) {
+ _last_error = err
+}
diff --git a/core/net/errors_windows.odin b/core/net/errors_windows.odin
index f41bcf888..b30046a17 100644
--- a/core/net/errors_windows.odin
+++ b/core/net/errors_windows.odin
@@ -20,250 +20,242 @@ package net
Feoramund: FreeBSD platform code
*/
-import "core:c"
+import "core:reflect"
import win "core:sys/windows"
-Create_Socket_Error :: enum c.int {
- None = 0,
- Network_Subsystem_Failure = win.WSAENETDOWN,
- Family_Not_Supported_For_This_Socket = win.WSAEAFNOSUPPORT,
- No_Socket_Descriptors_Available = win.WSAEMFILE,
- No_Buffer_Space_Available = win.WSAENOBUFS,
- Protocol_Unsupported_By_System = win.WSAEPROTONOSUPPORT,
- Wrong_Protocol_For_Socket = win.WSAEPROTOTYPE,
- Family_And_Socket_Type_Mismatch = win.WSAESOCKTNOSUPPORT,
+_last_platform_error :: proc() -> i32 {
+ return i32(win.WSAGetLastError())
}
-Dial_Error :: enum c.int {
- None = 0,
- Port_Required = -1,
- Address_In_Use = win.WSAEADDRINUSE,
- In_Progress = win.WSAEALREADY,
- Cannot_Use_Any_Address = win.WSAEADDRNOTAVAIL,
- Wrong_Family_For_Socket = win.WSAEAFNOSUPPORT,
- Refused = win.WSAECONNREFUSED,
- Is_Listening_Socket = win.WSAEINVAL,
- Already_Connected = win.WSAEISCONN,
- Network_Unreachable = win.WSAENETUNREACH, // Device is offline
- Host_Unreachable = win.WSAEHOSTUNREACH, // Remote host cannot be reached
- No_Buffer_Space_Available = win.WSAENOBUFS,
- Not_Socket = win.WSAENOTSOCK,
- Timeout = win.WSAETIMEDOUT,
- Would_Block = win.WSAEWOULDBLOCK, // TODO: we may need special handling for this; maybe make a socket a struct with metadata?
+_last_platform_error_string :: proc() -> string {
+ description, _ := reflect.enum_name_from_value(win.System_Error(win.WSAGetLastError()))
+ return description
}
-Bind_Error :: enum c.int {
- None = 0,
- Address_In_Use = win.WSAEADDRINUSE, // Another application is currently bound to this endpoint.
- Given_Nonlocal_Address = win.WSAEADDRNOTAVAIL, // The address is not a local address on this machine.
- Broadcast_Disabled = win.WSAEACCES, // To bind a UDP socket to the broadcast address, the appropriate socket option must be set.
- Address_Family_Mismatch = win.WSAEFAULT, // The address family of the address does not match that of the socket.
- Already_Bound = win.WSAEINVAL, // The socket is already bound to an address.
- No_Ports_Available = win.WSAENOBUFS, // There are not enough ephemeral ports available.
+_set_last_platform_error :: proc(err: i32) {
+ win.WSASetLastError(err)
}
-Listen_Error :: enum c.int {
- None = 0,
- Address_In_Use = win.WSAEADDRINUSE,
- Already_Connected = win.WSAEISCONN,
- No_Socket_Descriptors_Available = win.WSAEMFILE,
- No_Buffer_Space_Available = win.WSAENOBUFS,
- Nonlocal_Address = win.WSAEADDRNOTAVAIL,
- Not_Socket = win.WSAENOTSOCK,
- Listening_Not_Supported_For_This_Socket = win.WSAEOPNOTSUPP,
+_create_socket_error :: proc() -> Create_Socket_Error {
+ #partial switch win.System_Error(win.WSAGetLastError()) {
+ case .WSANOTINITIALISED, .WSAENETDOWN, .WSAEINVALIDPROVIDER, .WSAEINVALIDPROCTABLE, .WSAEPROVIDERFAILEDINIT:
+ return .Network_Unreachable
+ case .WSAEAFNOSUPPORT, .WSAEINPROGRESS, .WSAEINVAL, .WSAEPROTOTYPE, .WSAESOCKTNOSUPPORT:
+ return .Invalid_Argument
+ case .WSAEMFILE, .WSAENOBUFS, .WSAEPROTONOSUPPORT:
+ return .Insufficient_Resources
+ case:
+ return .Unknown
+ }
}
-Accept_Error :: enum c.int {
- None = 0,
- Not_Listening = win.WSAEINVAL,
- No_Socket_Descriptors_Available_For_Client_Socket = win.WSAEMFILE,
- No_Buffer_Space_Available = win.WSAENOBUFS,
- Not_Socket = win.WSAENOTSOCK,
- Not_Connection_Oriented_Socket = win.WSAEOPNOTSUPP,
-
- // TODO: we may need special handling for this; maybe make a socket a struct with metadata?
- Would_Block = win.WSAEWOULDBLOCK,
+_dial_error :: proc() -> Dial_Error {
+ #partial switch win.System_Error(win.WSAGetLastError()) {
+ case .WSANOTINITIALISED, .WSAENETDOWN:
+ return .Network_Unreachable
+ case .WSAEADDRINUSE:
+ return .Address_In_Use
+ case .WSAEINTR:
+ return .Interrupted
+ case .WSAEWOULDBLOCK:
+ return .Would_Block
+ case .WSAEALREADY:
+ return .Already_Connecting
+ case .WSAEADDRNOTAVAIL, .WSAEAFNOSUPPORT, .WSAEFAULT, .WSAENOTSOCK, .WSAEINPROGRESS, .WSAEINVAL:
+ return .Invalid_Argument
+ case .WSAECONNREFUSED:
+ return .Refused
+ case .WSAEISCONN:
+ return .Already_Connected
+ case .WSAEHOSTUNREACH:
+ return .Host_Unreachable
+ case .WSAENOBUFS:
+ return .Insufficient_Resources
+ case .WSAETIMEDOUT:
+ return .Timeout
+ case .WSAEACCES:
+ return .Broadcast_Not_Supported
+ case:
+ return .Unknown
+ }
}
-TCP_Recv_Error :: enum c.int {
- None = 0,
- Network_Subsystem_Failure = win.WSAENETDOWN,
- Not_Connected = win.WSAENOTCONN,
- Bad_Buffer = win.WSAEFAULT,
- Keepalive_Failure = win.WSAENETRESET,
- Not_Socket = win.WSAENOTSOCK,
- Shutdown = win.WSAESHUTDOWN,
- Would_Block = win.WSAEWOULDBLOCK,
- Aborted = win.WSAECONNABORTED,
- Timeout = win.WSAETIMEDOUT,
-
- // TODO(tetra): Determine when this is different from the syscall returning n=0 and maybe normalize them?
- Connection_Closed = win.WSAECONNRESET,
-
- // TODO: verify can actually happen
- Host_Unreachable = win.WSAEHOSTUNREACH,
+_bind_error :: proc() -> Bind_Error {
+ #partial switch win.System_Error(win.WSAGetLastError()) {
+ case .WSANOTINITIALISED, .WSAENETDOWN:
+ return .Network_Unreachable
+ case .WSAEADDRINUSE:
+ return .Address_In_Use
+ case .WSAEADDRNOTAVAIL, .WSAEFAULT, .WSAEINPROGRESS, .WSAEACCES, .WSAEINVAL, .WSAENOTSOCK:
+ return .Invalid_Argument
+ case:
+ return .Unknown
+ }
}
-UDP_Recv_Error :: enum c.int {
- None = 0,
- Network_Subsystem_Failure = win.WSAENETDOWN,
- Aborted = win.WSAECONNABORTED,
- Buffer_Too_Small = win.WSAEMSGSIZE, // The buffer is too small to fit the entire message, and the message was truncated. When this happens, the rest of message is lost.
- Remote_Not_Listening = win.WSAECONNRESET, // The machine at the remote endpoint doesn't have the given port open to receiving UDP data.
- Shutdown = win.WSAESHUTDOWN,
- Broadcast_Disabled = win.WSAEACCES, // A broadcast address was specified, but the .Broadcast socket option isn't set.
- Bad_Buffer = win.WSAEFAULT,
- No_Buffer_Space_Available = win.WSAENOBUFS,
- Not_Socket = win.WSAENOTSOCK, // The socket is not valid socket handle.
- Would_Block = win.WSAEWOULDBLOCK,
- Host_Unreachable = win.WSAEHOSTUNREACH, // The remote host cannot be reached from this host at this time.
- Offline = win.WSAENETUNREACH, // The network cannot be reached from this host at this time.
- Timeout = win.WSAETIMEDOUT,
-
- // TODO: can this actually happen? The socket isn't bound; an unknown flag specified; or MSG_OOB specified with SO_OOBINLINE enabled.
- Incorrectly_Configured = win.WSAEINVAL,
- TTL_Expired = win.WSAENETRESET, // The message took more hops than was allowed (the Time To Live) to reach the remote endpoint.
+_listen_error :: proc() -> Listen_Error {
+ #partial switch win.System_Error(win.WSAGetLastError()) {
+ case .WSANOTINITIALISED, .WSAENETDOWN:
+ return .Network_Unreachable
+ case .WSAEMFILE, .WSAENOBUFS:
+ return .Insufficient_Resources
+ case .WSAEADDRINUSE:
+ return .Address_In_Use
+ case .WSAEINPROGRESS, .WSAENOTSOCK:
+ return .Invalid_Argument
+ case .WSAEISCONN:
+ return .Already_Connected
+ case .WSAEOPNOTSUPP, .WSAEINVAL:
+ return .Unsupported_Socket
+ case:
+ return .Unknown
+ }
}
-// TODO: consider merging some errors to make handling them easier
-// TODO: verify once more what errors to actually expose
-TCP_Send_Error :: enum c.int {
- None = 0,
-
- Aborted = win.WSAECONNABORTED,
- Not_Connected = win.WSAENOTCONN,
- Shutdown = win.WSAESHUTDOWN,
- Connection_Closed = win.WSAECONNRESET,
- No_Buffer_Space_Available = win.WSAENOBUFS,
- Network_Subsystem_Failure = win.WSAENETDOWN,
- Host_Unreachable = win.WSAEHOSTUNREACH,
- Would_Block = win.WSAEWOULDBLOCK,
-
- // TODO: verify possible, as not mentioned in docs
- Offline = win.WSAENETUNREACH,
- Timeout = win.WSAETIMEDOUT,
-
- // A broadcast address was specified, but the .Broadcast socket option isn't set.
- Broadcast_Disabled = win.WSAEACCES,
- Bad_Buffer = win.WSAEFAULT,
-
- // Connection is broken due to keepalive activity detecting a failure during the operation.
- Keepalive_Failure = win.WSAENETRESET, // TODO: not functionally different from Reset; merge?
- Not_Socket = win.WSAENOTSOCK, // The so-called socket is not an open socket.
+_accept_error :: proc() -> Accept_Error {
+ #partial switch win.System_Error(win.WSAGetLastError()) {
+ case .WSANOTINITIALISED, .WSAENETDOWN:
+ return .Network_Unreachable
+ case .WSAEMFILE, .WSAENOBUFS:
+ return .Insufficient_Resources
+ case .WSAECONNRESET:
+ return .Aborted
+ case .WSAEFAULT, .WSAEINPROGRESS, .WSAENOTSOCK:
+ return .Invalid_Argument
+ case .WSAEINTR:
+ return .Interrupted
+ case .WSAEINVAL:
+ return .Not_Listening
+ case .WSAEWOULDBLOCK:
+ return .Would_Block
+ case .WSAEOPNOTSUPP:
+ return .Unsupported_Socket
+ case:
+ return .Unknown
+ }
}
-UDP_Send_Error :: enum c.int {
- None = 0,
- Network_Subsystem_Failure = win.WSAENETDOWN,
-
- Aborted = win.WSAECONNABORTED,
- Message_Too_Long = win.WSAEMSGSIZE, // The message is larger than the maximum UDP packet size.
- Remote_Not_Listening = win.WSAECONNRESET, // The machine at the remote endpoint doesn't have the given port open to receiving UDP data.
- Shutdown = win.WSAESHUTDOWN, // A broadcast address was specified, but the .Broadcast socket option isn't set.
- Broadcast_Disabled = win.WSAEACCES,
- Bad_Buffer = win.WSAEFAULT, // Connection is broken due to keepalive activity detecting a failure during the operation.
-
- // TODO: not functionally different from Reset; merge?
- Keepalive_Failure = win.WSAENETRESET,
- No_Buffer_Space_Available = win.WSAENOBUFS,
- Not_Socket = win.WSAENOTSOCK, // The socket is not valid socket handle.
-
- // This socket is unidirectional and cannot be used to send any data.
- // TODO: verify possible; decide whether to keep if not
- Receive_Only = win.WSAEOPNOTSUPP,
- Would_Block = win.WSAEWOULDBLOCK,
- Host_Unreachable = win.WSAEHOSTUNREACH, // The remote host cannot be reached from this host at this time.
- Cannot_Use_Any_Address = win.WSAEADDRNOTAVAIL, // Attempt to send to the Any address.
- Family_Not_Supported_For_This_Socket = win.WSAEAFNOSUPPORT, // The address is of an incorrect address family for this socket.
- Offline = win.WSAENETUNREACH, // The network cannot be reached from this host at this time.
- Timeout = win.WSAETIMEDOUT,
+_tcp_recv_error :: proc() -> TCP_Recv_Error {
+ #partial switch win.System_Error(win.WSAGetLastError()) {
+ case .WSANOTINITIALISED, .WSAENETDOWN:
+ return .Network_Unreachable
+ case .WSAEFAULT, .WSAEINPROGRESS, .WSAENOTSOCK, .WSAEMSGSIZE, .WSAEINVAL, .WSAEOPNOTSUPP:
+ return .Invalid_Argument
+ case .WSAENOTCONN:
+ return .Not_Connected
+ case .WSAEINTR:
+ return .Interrupted
+ case .WSAENETRESET, .WSAESHUTDOWN, .WSAECONNABORTED, .WSAECONNRESET:
+ return .Connection_Closed
+ case .WSAEWOULDBLOCK:
+ return .Would_Block
+ case .WSAETIMEDOUT:
+ return .Timeout
+ case:
+ return .Unknown
+ }
}
-Shutdown_Manner :: enum c.int {
- Receive = win.SD_RECEIVE,
- Send = win.SD_SEND,
- Both = win.SD_BOTH,
+_udp_recv_error :: proc() -> UDP_Recv_Error {
+ #partial switch win.System_Error(win.WSAGetLastError()) {
+ case .WSANOTINITIALISED, .WSAENETDOWN:
+ return .Network_Unreachable
+ case .WSAEFAULT, .WSAEINPROGRESS, .WSAEINVAL, .WSAEISCONN, .WSAENOTSOCK, .WSAEOPNOTSUPP, .WSAEMSGSIZE:
+ return .Invalid_Argument
+ case .WSAEINTR:
+ return .Interrupted
+ case .WSAENETRESET, .WSAESHUTDOWN, .WSAECONNRESET:
+ return .Connection_Refused
+ case .WSAEWOULDBLOCK:
+ return .Would_Block
+ case .WSAETIMEDOUT:
+ return .Timeout
+ case:
+ return .Unknown
+ }
}
-Shutdown_Error :: enum c.int {
- None = 0,
- Aborted = win.WSAECONNABORTED,
- Reset = win.WSAECONNRESET,
- Offline = win.WSAENETDOWN,
- Not_Connected = win.WSAENOTCONN,
- Not_Socket = win.WSAENOTSOCK,
- Invalid_Manner = win.WSAEINVAL,
+_tcp_send_error :: proc() -> TCP_Send_Error {
+ #partial switch win.System_Error(win.WSAGetLastError()) {
+ case .WSANOTINITIALISED, .WSAENETDOWN:
+ return .Network_Unreachable
+ case .WSAENOBUFS:
+ return .Insufficient_Resources
+ case .WSAEACCES, .WSAEINPROGRESS, .WSAEFAULT, .WSAENOTSOCK, .WSAEOPNOTSUPP, .WSAEMSGSIZE, .WSAEINVAL:
+ return .Invalid_Argument
+ case .WSAEINTR:
+ return .Interrupted
+ case .WSAENETRESET, .WSAESHUTDOWN, .WSAECONNABORTED, .WSAECONNRESET:
+ return .Connection_Closed
+ case .WSAENOTCONN:
+ return .Not_Connected
+ case .WSAEWOULDBLOCK:
+ return .Would_Block
+ case .WSAETIMEDOUT:
+ return .Timeout
+ case .WSAEHOSTUNREACH:
+ return .Host_Unreachable
+ case:
+ return .Unknown
+ }
}
-Socket_Option :: enum c.int {
- // bool: Whether the address that this socket is bound to can be reused by other sockets.
- // This allows you to bypass the cooldown period if a program dies while the socket is bound.
- Reuse_Address = win.SO_REUSEADDR,
-
- // bool: Whether other programs will be inhibited from binding the same endpoint as this socket.
- Exclusive_Addr_Use = win.SO_EXCLUSIVEADDRUSE,
-
- // bool: When true, keepalive packets will be automatically be sent for this connection. TODO: verify this understanding
- Keep_Alive = win.SO_KEEPALIVE,
-
- // bool: When true, client connections will immediately be sent a TCP/IP RST response, rather than being accepted.
- Conditional_Accept = win.SO_CONDITIONAL_ACCEPT,
-
- // bool: If true, when the socket is closed, but data is still waiting to be sent, discard that data.
- Dont_Linger = win.SO_DONTLINGER,
-
- // bool: When true, 'out-of-band' data sent over the socket will be read by a normal net.recv() call, the same as normal 'in-band' data.
- Out_Of_Bounds_Data_Inline = win.SO_OOBINLINE,
-
- // bool: When true, disables send-coalescing, therefore reducing latency.
- TCP_Nodelay = win.TCP_NODELAY,
-
- // win.LINGER: Customizes how long (if at all) the socket will remain open when there
- // is some remaining data waiting to be sent, and net.close() is called.
- Linger = win.SO_LINGER,
-
- // win.DWORD: The size, in bytes, of the OS-managed receive-buffer for this socket.
- Receive_Buffer_Size = win.SO_RCVBUF,
-
- // win.DWORD: The size, in bytes, of the OS-managed send-buffer for this socket.
- Send_Buffer_Size = win.SO_SNDBUF,
-
- // win.DWORD: For blocking sockets, the time in milliseconds to wait for incoming data to be received, before giving up and returning .Timeout.
- // For non-blocking sockets, ignored.
- // Use a value of zero to potentially wait forever.
- Receive_Timeout = win.SO_RCVTIMEO,
-
- // win.DWORD: For blocking sockets, the time in milliseconds to wait for outgoing data to be sent, before giving up and returning .Timeout.
- // For non-blocking sockets, ignored.
- // Use a value of zero to potentially wait forever.
- Send_Timeout = win.SO_SNDTIMEO,
-
- // bool: Allow sending to, receiving from, and binding to, a broadcast address.
- Broadcast = win.SO_BROADCAST,
+_udp_send_error :: proc() -> UDP_Send_Error {
+ #partial switch win.System_Error(win.WSAGetLastError()) {
+ case .WSANOTINITIALISED, .WSAENETDOWN, .WSAENETUNREACH:
+ return .Network_Unreachable
+ case .WSAENOBUFS:
+ return .Insufficient_Resources
+ case .WSAEACCES, .WSAEINVAL, .WSAEINPROGRESS, .WSAEFAULT, .WSAENOTCONN, .WSAENOTSOCK, .WSAEOPNOTSUPP, .WSAEADDRNOTAVAIL, .WSAEAFNOSUPPORT, .WSAEDESTADDRREQ:
+ return .Invalid_Argument
+ case .WSAEINTR:
+ return .Interrupted
+ case .WSAENETRESET, .WSAESHUTDOWN, .WSAECONNRESET:
+ return .Connection_Refused
+ case .WSAEWOULDBLOCK:
+ return .Would_Block
+ case .WSAETIMEDOUT:
+ return .Timeout
+ case:
+ return .Unknown
+ }
}
-Socket_Option_Error :: enum c.int {
- None = 0,
- Linger_Only_Supports_Whole_Seconds = 1,
-
- // The given value is too big or small to be given to the OS.
- Value_Out_Of_Range,
-
- Network_Subsystem_Failure = win.WSAENETDOWN,
- Timeout_When_Keepalive_Set = win.WSAENETRESET,
- Invalid_Option_For_Socket = win.WSAENOPROTOOPT,
- Reset_When_Keepalive_Set = win.WSAENOTCONN,
- Not_Socket = win.WSAENOTSOCK,
+_shutdown_error :: proc() -> Shutdown_Error {
+ #partial switch win.System_Error(win.WSAGetLastError()) {
+ case .WSAENETDOWN, .WSANOTINITIALISED:
+ return .Network_Unreachable
+ case .WSAECONNABORTED, .WSAECONNRESET:
+ return .Connection_Closed
+ case .WSAEINPROGRESS, .WSAEINVAL, .WSAENOTCONN, .WSAENOTSOCK:
+ return .Invalid_Argument
+ case:
+ return .Unknown
+ }
}
-Set_Blocking_Error :: enum c.int {
- None = 0,
+_socket_option_error :: proc() -> Socket_Option_Error {
+ #partial switch win.System_Error(win.WSAGetLastError()) {
+ case .WSAENETDOWN, .WSANOTINITIALISED:
+ return .Network_Unreachable
+ case .WSAEFAULT, .WSAEINVAL:
+ return .Invalid_Value
+ case .WSAENETRESET, .WSAENOTCONN, .WSAENOTSOCK:
+ return .Invalid_Socket
+ case .WSAENOPROTOOPT:
+ return .Invalid_Option
+ case:
+ return .Unknown
+ }
+}
- Network_Subsystem_Failure = win.WSAENETDOWN,
- Blocking_Call_In_Progress = win.WSAEINPROGRESS,
- Not_Socket = win.WSAENOTSOCK,
-
- // TODO: are those errors possible?
- Network_Subsystem_Not_Initialized = win.WSAENOTINITIALISED,
- Invalid_Argument_Pointer = win.WSAEFAULT,
-}
\ No newline at end of file
+_set_blocking_error :: proc() -> Set_Blocking_Error {
+ #partial switch win.System_Error(win.WSAGetLastError()) {
+ case .WSAENETDOWN, .WSANOTINITIALISED:
+ return .Network_Unreachable
+ case .WSAEINPROGRESS, .WSAENOTSOCK, .WSAEFAULT:
+ return .Invalid_Argument
+ case:
+ return .Unknown
+ }
+}
diff --git a/core/net/interface.odin b/core/net/interface.odin
index 775a812f3..4d499a008 100644
--- a/core/net/interface.odin
+++ b/core/net/interface.odin
@@ -27,7 +27,7 @@ MAX_INTERFACE_ENUMERATION_TRIES :: 3
/*
`enumerate_interfaces` retrieves a list of network interfaces with their associated properties.
*/
-enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Network_Error) {
+enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Interfaces_Error) {
return _enumerate_interfaces(allocator)
}
diff --git a/core/net/interface_darwin.odin b/core/net/interface_darwin.odin
index 4921bc3fe..f189e5844 100644
--- a/core/net/interface_darwin.odin
+++ b/core/net/interface_darwin.odin
@@ -20,60 +20,57 @@ package net
Feoramund: FreeBSD platform code
*/
-import "core:os"
import "core:strings"
+import "core:sys/posix"
+
+foreign import lib "system:System.framework"
@(private)
-_enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Network_Error) {
+_enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Interfaces_Error) {
context.allocator = allocator
- head: ^os.ifaddrs
-
- if res := os._getifaddrs(&head); res < 0 {
+ head: ^ifaddrs
+ if getifaddrs(&head) != .OK {
return {}, .Unable_To_Enumerate_Network_Interfaces
}
+ defer freeifaddrs(head)
- /*
- Unlike Windows, *nix regrettably doesn't return all it knows about an interface in one big struct.
- We're going to have to iterate over a list and coalesce information as we go.
- */
- ifaces: map[string]^Network_Interface
+ ifaces: map[string]Network_Interface
defer delete(ifaces)
for ifaddr := head; ifaddr != nil; ifaddr = ifaddr.next {
adapter_name := string(ifaddr.name)
- /*
- Check if we have seen this interface name before so we can reuse the `Network_Interface`.
- Else, create a new one.
- */
- if adapter_name not_in ifaces {
- ifaces[adapter_name] = new(Network_Interface)
- ifaces[adapter_name].adapter_name = strings.clone(adapter_name)
+ key_ptr, iface, inserted, mem_err := map_entry(&ifaces, adapter_name)
+ if mem_err == nil && inserted {
+ key_ptr^, mem_err = strings.clone(adapter_name)
+ iface.adapter_name = key_ptr^
+ }
+ if mem_err != nil {
+ return {}, .Allocation_Failure
}
- iface := ifaces[adapter_name]
address: Address
netmask: Netmask
- if ifaddr.address != nil {
- switch int(ifaddr.address.family) {
- case os.AF_INET, os.AF_INET6:
- address = _sockaddr_basic_to_endpoint(ifaddr.address).address
+ if ifaddr.addr != nil {
+ #partial switch ifaddr.addr.sa_family {
+ case .INET, .INET6:
+ address = _sockaddr_basic_to_endpoint(ifaddr.addr).address
}
}
if ifaddr.netmask != nil {
- switch int(ifaddr.netmask.family) {
- case os.AF_INET, os.AF_INET6:
+ #partial switch ifaddr.netmask.sa_family {
+ case .INET, .INET6:
netmask = Netmask(_sockaddr_basic_to_endpoint(ifaddr.netmask).address)
}
}
- if ifaddr.broadcast_or_dest != nil && .BROADCAST in ifaddr.flags {
- switch int(ifaddr.broadcast_or_dest.family) {
- case os.AF_INET, os.AF_INET6:
- broadcast := _sockaddr_basic_to_endpoint(ifaddr.broadcast_or_dest).address
+ if ifaddr.dstaddr != nil && .BROADCAST in ifaddr.flags {
+ #partial switch ifaddr.dstaddr.sa_family {
+ case .INET, .INET6:
+ broadcast := _sockaddr_basic_to_endpoint(ifaddr.dstaddr).address
append(&iface.multicast, broadcast)
}
}
@@ -105,18 +102,51 @@ _enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []
iface.link.state = state
}
- /*
- Free the OS structures.
- */
- os._freeifaddrs(head)
-
- /*
- Turn the map into a slice to return.
- */
- _interfaces := make([dynamic]Network_Interface, 0, allocator)
+ interfaces = make([]Network_Interface, len(ifaces))
+ i: int
for _, iface in ifaces {
- append(&_interfaces, iface^)
- free(iface)
+ interfaces[i] = iface
+ i += 1
}
- return _interfaces[:], {}
+ return interfaces, nil
+}
+
+@(private)
+IF_Flag :: enum u32 {
+ UP,
+ BROADCAST,
+ DEBUG,
+ LOOPBACK,
+ POINTTOPOINT,
+ NOTRAILERS,
+ RUNNING,
+ NOARP,
+ PROMISC,
+ ALLMULTI,
+ OACTIVE,
+ SIMPLEX,
+ LINK0,
+ LINK1,
+ LINK2,
+ MULTICAST,
+}
+
+@(private)
+IF_Flags :: bit_set[IF_Flag; u32]
+
+@(private)
+ifaddrs :: struct {
+ next: ^ifaddrs,
+ name: cstring,
+ flags: IF_Flags,
+ addr: ^posix.sockaddr,
+ netmask: ^posix.sockaddr,
+ dstaddr: ^posix.sockaddr,
+ data: rawptr,
+}
+
+@(private)
+foreign lib {
+ getifaddrs :: proc(ifap: ^^ifaddrs) -> posix.result ---
+ freeifaddrs :: proc(ifp: ^ifaddrs) ---
}
diff --git a/core/net/interface_freebsd.odin b/core/net/interface_freebsd.odin
index 50e2d1a96..90a538a04 100644
--- a/core/net/interface_freebsd.odin
+++ b/core/net/interface_freebsd.odin
@@ -25,7 +25,7 @@ import "core:strings"
import "core:sys/freebsd"
@(private)
-_enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Network_Error) {
+_enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Interfaces_Error) {
// This is a simplified implementation of `getifaddrs` from the FreeBSD
// libc using only Odin and syscalls.
context.allocator = allocator
@@ -50,7 +50,7 @@ _enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []
// Allocate and get the entries.
buf, alloc_err := make([]byte, needed)
if alloc_err != nil {
- return nil, .Unable_To_Enumerate_Network_Interfaces
+ return nil, .Allocation_Failure
}
defer delete(buf)
diff --git a/core/net/interface_linux.odin b/core/net/interface_linux.odin
index 28724735b..e329803c5 100644
--- a/core/net/interface_linux.odin
+++ b/core/net/interface_linux.odin
@@ -30,7 +30,7 @@ package net
// NOTE(flysand): https://man7.org/linux/man-pages/man7/netlink.7.html
// apparently musl libc uses this to enumerate network interfaces
@(private)
-_enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Network_Error) {
+_enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Interfaces_Error) {
context.allocator = allocator
// head: ^os.ifaddrs
@@ -143,4 +143,4 @@ _enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []
// }
// return _interfaces[:], {}
return nil, {}
-}
\ No newline at end of file
+}
diff --git a/core/net/interface_windows.odin b/core/net/interface_windows.odin
index a6eb72846..571fb322f 100644
--- a/core/net/interface_windows.odin
+++ b/core/net/interface_windows.odin
@@ -23,7 +23,7 @@ package net
import sys "core:sys/windows"
import strings "core:strings"
-_enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Network_Error) {
+_enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Interfaces_Error) {
context.allocator = allocator
buf: []u8
@@ -52,7 +52,8 @@ _enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []
case 0:
break gaa
case:
- return {}, Platform_Error(res)
+ set_last_platform_error(i32(res))
+ return {}, .Unknown
}
}
@@ -63,13 +64,13 @@ _enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []
_interfaces := make([dynamic]Network_Interface, 0, allocator)
for adapter := (^sys.IP_Adapter_Addresses)(raw_data(buf)); adapter != nil; adapter = adapter.Next {
friendly_name, err1 := sys.wstring_to_utf8(sys.wstring(adapter.FriendlyName), 256, allocator)
- if err1 != nil { return {}, Platform_Error(err1) }
+ if err1 != nil { return {}, .Allocation_Failure }
description, err2 := sys.wstring_to_utf8(sys.wstring(adapter.Description), 256, allocator)
- if err2 != nil { return {}, Platform_Error(err2) }
+ if err2 != nil { return {}, .Allocation_Failure }
dns_suffix, err3 := sys.wstring_to_utf8(sys.wstring(adapter.DnsSuffix), 256, allocator)
- if err3 != nil { return {}, Platform_Error(err3) }
+ if err3 != nil { return {}, .Allocation_Failure }
interface := Network_Interface{
adapter_name = strings.clone(string(adapter.AdapterName)),
@@ -176,4 +177,4 @@ parse_socket_address :: proc(addr_in: sys.SOCKET_ADDRESS) -> (addr: Endpoint) {
case: return // Empty or invalid address type
}
unreachable()
-}
\ No newline at end of file
+}
diff --git a/core/net/socket.odin b/core/net/socket.odin
index 950c7ac11..801693962 100644
--- a/core/net/socket.odin
+++ b/core/net/socket.odin
@@ -34,23 +34,14 @@ any_socket_to_socket :: proc "contextless" (socket: Any_Socket) -> Socket {
Expects both hostname and port to be present in the `hostname_and_port` parameter, either as:
`a.host.name:9999`, or as `1.2.3.4:9999`, or IP6 equivalent.
- Calls `parse_hostname_or_endpoint` and `resolve`, then `dial_tcp_from_endpoint`.
+ Calls `parse_hostname_or_endpoint` and `dial_tcp_from_host_or_endpoint`.
+
+ Errors that can be returned: `Parse_Endpoint_Error`, `Resolve_Error`, `DNS_Error`, `Create_Socket_Error`, or `Dial_Error`
*/
-dial_tcp_from_hostname_and_port_string :: proc(hostname_and_port: string, options := default_tcp_options) -> (socket: TCP_Socket, err: Network_Error) {
+dial_tcp_from_hostname_and_port_string :: proc(hostname_and_port: string, options := DEFAULT_TCP_OPTIONS) -> (socket: TCP_Socket, err: Network_Error) {
target := parse_hostname_or_endpoint(hostname_and_port) or_return
- switch t in target {
- case Endpoint:
- return dial_tcp_from_endpoint(t, options)
- case Host:
- if t.port == 0 {
- return 0, .Port_Required
- }
- ep4, ep6 := resolve(t.hostname) or_return
- ep := ep4 if ep4.address != nil else ep6 // NOTE(tetra): We don't know what family the server uses, so we just default to IP4.
- ep.port = t.port
- return dial_tcp_from_endpoint(ep, options)
- }
- unreachable()
+
+ return dial_tcp_from_host_or_endpoint(target, options)
}
/*
@@ -58,30 +49,67 @@ dial_tcp_from_hostname_and_port_string :: proc(hostname_and_port: string, option
`parse_hostname_or_endpoint` is called and the `hostname` will be resolved into an IP.
If a `hostname` of form `a.host.name:9999` is given, the port will be ignored in favor of the explicit `port` param.
+
+ Errors that can be returned: `Parse_Endpoint_Error`, `Resolve_Error`, `DNS_Error`, `Create_Socket_Error`, or `Dial_Error`
*/
-dial_tcp_from_hostname_with_port_override :: proc(hostname: string, port: int, options := default_tcp_options) -> (socket: TCP_Socket, err: Network_Error) {
+dial_tcp_from_hostname_with_port_override :: proc(hostname: string, port: int, options := DEFAULT_TCP_OPTIONS) -> (socket: TCP_Socket, err: Network_Error) {
target := parse_hostname_or_endpoint(hostname) or_return
+ switch &t in target {
+ case Endpoint:
+ t.port = port
+ case Host:
+ t.port = port
+ }
+
+ return dial_tcp_from_host_or_endpoint(target, options)
+}
+
+/*
+ Expects the `host` as Host.
+
+ Errors that can be returned: `Resolve_Error`, `DNS_Error`, `Create_Socket_Error`, or `Dial_Error`
+*/
+dial_tcp_from_host :: proc(host: Host, options := DEFAULT_TCP_OPTIONS) -> (socket: TCP_Socket, err: Network_Error) {
+ if host.port == 0 {
+ return 0, .Port_Required
+ }
+ ep4, ep6 := resolve(host.hostname) or_return
+ ep := ep4 if ep4.address != nil else ep6 // NOTE(tetra): We don't know what family the server uses, so we just default to IP4.
+ ep.port = host.port
+ return dial_tcp_from_endpoint(ep, options)
+}
+
+/*
+ Expects the `target` as a Host_OrEndpoint.
+ Unwraps the underlying type and calls `dial_tcp_from_host` or `dial_tcp_from_endpoint`.
+
+ Errors that can be returned: `Parse_Endpoint_Error`, `Resolve_Error`, `DNS_Error`, `Create_Socket_Error`, or `Dial_Error`
+*/
+dial_tcp_from_host_or_endpoint :: proc(target: Host_Or_Endpoint, options := DEFAULT_TCP_OPTIONS) -> (socket: TCP_Socket, err: Network_Error) {
switch t in target {
case Endpoint:
- return dial_tcp_from_endpoint({t.address, port}, options)
+ return dial_tcp_from_endpoint(t, options)
case Host:
- if port == 0 {
- return 0, .Port_Required
- }
- ep4, ep6 := resolve(t.hostname) or_return
- ep := ep4 if ep4.address != nil else ep6 // NOTE(tetra): We don't know what family the server uses, so we just default to IP4.
- ep.port = port
- return dial_tcp_from_endpoint(ep, options)
+ return dial_tcp_from_host(t, options)
}
unreachable()
}
-// Dial from an Address
-dial_tcp_from_address_and_port :: proc(address: Address, port: int, options := default_tcp_options) -> (socket: TCP_Socket, err: Network_Error) {
+/*
+ Dial from an Address.
+
+ Errors that can be returned: `Create_Socket_Error`, or `Dial_Error`
+*/
+dial_tcp_from_address_and_port :: proc(address: Address, port: int, options := DEFAULT_TCP_OPTIONS) -> (socket: TCP_Socket, err: Network_Error) {
return dial_tcp_from_endpoint({address, port}, options)
}
-dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_options) -> (socket: TCP_Socket, err: Network_Error) {
+/*
+ Dial from an Endpoint.
+
+ Errors that can be returned: `Create_Socket_Error`, or `Dial_Error`
+*/
+dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := DEFAULT_TCP_OPTIONS) -> (socket: TCP_Socket, err: Network_Error) {
return _dial_tcp_from_endpoint(endpoint, options)
}
@@ -90,13 +118,15 @@ dial_tcp :: proc{
dial_tcp_from_address_and_port,
dial_tcp_from_hostname_and_port_string,
dial_tcp_from_hostname_with_port_override,
+ dial_tcp_from_host,
+ dial_tcp_from_host_or_endpoint,
}
-create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Network_Error) {
+create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Create_Socket_Error) {
return _create_socket(family, protocol)
}
-bind :: proc(socket: Any_Socket, ep: Endpoint) -> (err: Network_Error) {
+bind :: proc(socket: Any_Socket, ep: Endpoint) -> (err: Bind_Error) {
return _bind(socket, ep)
}
@@ -106,7 +136,7 @@ bind :: proc(socket: Any_Socket, ep: Endpoint) -> (err: Network_Error) {
This is like a client TCP socket, except that it can send data to any remote endpoint without needing to establish a connection first.
*/
-make_unbound_udp_socket :: proc(family: Address_Family) -> (socket: UDP_Socket, err: Network_Error) {
+make_unbound_udp_socket :: proc(family: Address_Family) -> (socket: UDP_Socket, err: Create_Socket_Error) {
sock := create_socket(family, .UDP) or_return
socket = sock.(UDP_Socket)
return
@@ -118,6 +148,8 @@ make_unbound_udp_socket :: proc(family: Address_Family) -> (socket: UDP_Socket,
This is like a listening TCP socket, except that data packets can be sent and received without needing to establish a connection first.
The `bound_address` is the address of the network interface that you want to use, or a loopback address if you don't care which to use.
+
+ Errors that can be returned: `Parse_Endpoint_Error`, `Create_Socket_Error`, or `Bind_Error`
*/
make_bound_udp_socket :: proc(bound_address: Address, port: int) -> (socket: UDP_Socket, err: Network_Error) {
if bound_address == nil {
@@ -128,6 +160,11 @@ make_bound_udp_socket :: proc(bound_address: Address, port: int) -> (socket: UDP
return
}
+/*
+ Creates a TCP socket and starts listening on the given endpoint.
+
+ Errors that can be returned: `Create_Socket_Error`, `Bind_Error`, or `Listen_Error`
+*/
listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (socket: TCP_Socket, err: Network_Error) {
assert(backlog > 0 && backlog < int(max(i32)))
@@ -137,11 +174,11 @@ listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (socket: TC
/*
Returns the endpoint that the given socket is listening / bound on.
*/
-bound_endpoint :: proc(socket: Any_Socket) -> (endpoint: Endpoint, err: Network_Error) {
+bound_endpoint :: proc(socket: Any_Socket) -> (endpoint: Endpoint, err: Listen_Error) {
return _bound_endpoint(socket)
}
-accept_tcp :: proc(socket: TCP_Socket, options := default_tcp_options) -> (client: TCP_Socket, source: Endpoint, err: Network_Error) {
+accept_tcp :: proc(socket: TCP_Socket, options := DEFAULT_TCP_OPTIONS) -> (client: TCP_Socket, source: Endpoint, err: Accept_Error) {
return _accept_tcp(socket, options)
}
@@ -149,11 +186,11 @@ close :: proc(socket: Any_Socket) {
_close(socket)
}
-recv_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_read: int, err: Network_Error) {
+recv_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_read: int, err: TCP_Recv_Error) {
return _recv_tcp(socket, buf)
}
-recv_udp :: proc(socket: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: Network_Error) {
+recv_udp :: proc(socket: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: UDP_Recv_Error) {
return _recv_udp(socket, buf)
}
@@ -162,6 +199,8 @@ recv_udp :: proc(socket: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_en
Note: `remote_endpoint` parameter is non-nil only if the socket type is UDP. On TCP sockets it
will always return `nil`.
+
+ Errors that can be returned: `TCP_Recv_Error`, or `UDP_Recv_Error`
*/
recv_any :: proc(socket: Any_Socket, buf: []byte) -> (
bytes_read: int,
@@ -184,7 +223,7 @@ recv :: proc{recv_tcp, recv_udp, recv_any}
Repeatedly sends data until the entire buffer is sent.
If a send fails before all data is sent, returns the amount sent up to that point.
*/
-send_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_written: int, err: Network_Error) {
+send_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_written: int, err: TCP_Send_Error) {
return _send_tcp(socket, buf)
}
@@ -194,10 +233,15 @@ send_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_written: int, err: N
Datagrams are limited in size; attempting to send more than this limit at once will result in a Message_Too_Long error.
UDP packets are not guarenteed to be received in order.
*/
-send_udp :: proc(socket: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: int, err: Network_Error) {
+send_udp :: proc(socket: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: int, err: UDP_Send_Error) {
return _send_udp(socket, buf, to)
}
+/*
+ Sends data over the socket.
+
+ Errors that can be returned: `TCP_Send_Error`, or `UDP_Send_Error`
+*/
send_any :: proc(socket: Any_Socket, buf: []byte, to: Maybe(Endpoint) = nil) -> (
bytes_written: int,
err: Network_Error,
@@ -213,14 +257,14 @@ send_any :: proc(socket: Any_Socket, buf: []byte, to: Maybe(Endpoint) = nil) ->
send :: proc{send_tcp, send_udp, send_any}
-shutdown :: proc(socket: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Error) {
+shutdown :: proc(socket: Any_Socket, manner: Shutdown_Manner) -> (err: Shutdown_Error) {
return _shutdown(socket, manner)
}
-set_option :: proc(socket: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Network_Error {
+set_option :: proc(socket: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Socket_Option_Error {
return _set_option(socket, option, value, loc)
}
-set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_Error) {
+set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Set_Blocking_Error) {
return _set_blocking(socket, should_block)
}
diff --git a/core/net/socket_darwin.odin b/core/net/socket_darwin.odin
index 27927e973..e63f1844a 100644
--- a/core/net/socket_darwin.odin
+++ b/core/net/socket_darwin.odin
@@ -21,44 +21,51 @@ package net
*/
import "core:c"
-import "core:os"
import "core:sys/posix"
import "core:time"
Socket_Option :: enum c.int {
- Broadcast = c.int(os.SO_BROADCAST),
- Reuse_Address = c.int(os.SO_REUSEADDR),
- Keep_Alive = c.int(os.SO_KEEPALIVE),
- Out_Of_Bounds_Data_Inline = c.int(os.SO_OOBINLINE),
- TCP_Nodelay = c.int(os.TCP_NODELAY),
- Linger = c.int(os.SO_LINGER),
- Receive_Buffer_Size = c.int(os.SO_RCVBUF),
- Send_Buffer_Size = c.int(os.SO_SNDBUF),
- Receive_Timeout = c.int(os.SO_RCVTIMEO),
- Send_Timeout = c.int(os.SO_SNDTIMEO),
+ Broadcast = c.int(posix.Sock_Option.BROADCAST),
+ Reuse_Address = c.int(posix.Sock_Option.REUSEADDR),
+ Keep_Alive = c.int(posix.Sock_Option.KEEPALIVE),
+ Out_Of_Bounds_Data_Inline = c.int(posix.Sock_Option.OOBINLINE),
+ TCP_Nodelay = c.int(posix.TCP_NODELAY),
+ Linger = c.int(posix.Sock_Option.LINGER),
+ Receive_Buffer_Size = c.int(posix.Sock_Option.RCVBUF),
+ Send_Buffer_Size = c.int(posix.Sock_Option.SNDBUF),
+ Receive_Timeout = c.int(posix.Sock_Option.RCVTIMEO),
+ Send_Timeout = c.int(posix.Sock_Option.SNDTIMEO),
+}
+
+Shutdown_Manner :: enum c.int {
+ Receive = c.int(posix.SHUT_RD),
+ Send = c.int(posix.SHUT_WR),
+ Both = c.int(posix.SHUT_RDWR),
}
@(private)
-_create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Network_Error) {
- c_type, c_protocol, c_family: int
+_create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Create_Socket_Error) {
+ c_type: posix.Sock
+ c_protocol: posix.Protocol
+ c_family: posix.AF
switch family {
- case .IP4: c_family = os.AF_INET
- case .IP6: c_family = os.AF_INET6
+ case .IP4: c_family = .INET
+ case .IP6: c_family = .INET6
case:
unreachable()
}
switch protocol {
- case .TCP: c_type = os.SOCK_STREAM; c_protocol = os.IPPROTO_TCP
- case .UDP: c_type = os.SOCK_DGRAM; c_protocol = os.IPPROTO_UDP
+ case .TCP: c_type = .STREAM; c_protocol = .TCP
+ case .UDP: c_type = .DGRAM; c_protocol = .UDP
case:
unreachable()
}
- sock, sock_err := os.socket(c_family, c_type, c_protocol)
- if sock_err != nil {
- err = Create_Socket_Error(os.is_platform_error(sock_err) or_else -1)
+ sock := posix.socket(c_family, c_type, c_protocol)
+ if sock < 0 {
+ err = _create_socket_error()
return
}
@@ -71,7 +78,7 @@ _create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (so
}
@(private)
-_dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_options) -> (skt: TCP_Socket, err: Network_Error) {
+_dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := DEFAULT_TCP_OPTIONS) -> (skt: TCP_Socket, err: Network_Error) {
if endpoint.port == 0 {
return 0, .Port_Required
}
@@ -86,30 +93,22 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
_ = set_option(skt, .Reuse_Address, true)
sockaddr := _endpoint_to_sockaddr(endpoint)
- res := os.connect(os.Socket(skt), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len))
- if res != nil {
+ if posix.connect(posix.FD(skt), (^posix.sockaddr)(&sockaddr), posix.socklen_t(sockaddr.ss_len)) != .OK {
+ err = _dial_error()
close(skt)
- return {}, Dial_Error(os.is_platform_error(res) or_else -1)
}
return
}
-// On Darwin, any port below 1024 is 'privileged' - which means that you need root access in order to use it.
-MAX_PRIVILEGED_PORT :: 1023
-
@(private)
-_bind :: proc(skt: Any_Socket, ep: Endpoint) -> (err: Network_Error) {
+_bind :: proc(skt: Any_Socket, ep: Endpoint) -> (err: Bind_Error) {
sockaddr := _endpoint_to_sockaddr(ep)
s := any_socket_to_socket(skt)
- res := os.bind(os.Socket(s), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len))
- if res != nil {
- if res == os.EACCES && ep.port <= MAX_PRIVILEGED_PORT {
- err = .Privileged_Port_Without_Root
- } else {
- err = Bind_Error(os.is_platform_error(res) or_else -1)
- }
+ if posix.bind(posix.FD(s), (^posix.sockaddr)(&sockaddr), posix.socklen_t(sockaddr.ss_len)) != .OK {
+ err = _bind_error()
}
+
return
}
@@ -126,78 +125,77 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (skt: TCP_
// bypass the cooldown period, and allow the next run of the program to
// use the same address immediately.
//
- // TODO(tetra, 2022-02-15): Confirm that this doesn't mean other processes can hijack the address!
- set_option(sock, .Reuse_Address, true) or_return
+ _ = set_option(sock, .Reuse_Address, true)
bind(sock, interface_endpoint) or_return
- res := os.listen(os.Socket(skt), backlog)
- if res != nil {
- err = Listen_Error(os.is_platform_error(res) or_else -1)
- return
+ if posix.listen(posix.FD(skt), i32(backlog)) != .OK {
+ err = _listen_error()
}
return
}
@(private)
-_bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Network_Error) {
+_bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Listen_Error) {
addr: posix.sockaddr_storage
addr_len := posix.socklen_t(size_of(addr))
- res := posix.getsockname(posix.FD(any_socket_to_socket(sock)), (^posix.sockaddr)(&addr), &addr_len)
- if res != .OK {
- err = Listen_Error(posix.errno())
+ if posix.getsockname(posix.FD(any_socket_to_socket(sock)), (^posix.sockaddr)(&addr), &addr_len) != .OK {
+ err = _listen_error()
return
}
- ep = _sockaddr_to_endpoint((^os.SOCKADDR_STORAGE_LH)(&addr))
+
+ ep = _sockaddr_to_endpoint(&addr)
return
}
@(private)
-_accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (client: TCP_Socket, source: Endpoint, err: Network_Error) {
- sockaddr: os.SOCKADDR_STORAGE_LH
- sockaddrlen := c.int(size_of(sockaddr))
-
- client_sock, client_sock_err := os.accept(os.Socket(sock), cast(^os.SOCKADDR) &sockaddr, &sockaddrlen)
- if client_sock_err != nil {
- err = Accept_Error(os.is_platform_error(client_sock_err) or_else -1)
+_accept_tcp :: proc(sock: TCP_Socket, options := DEFAULT_TCP_OPTIONS) -> (client: TCP_Socket, source: Endpoint, err: Accept_Error) {
+ addr: posix.sockaddr_storage
+ addr_len := posix.socklen_t(size_of(addr))
+ client_sock := posix.accept(posix.FD(sock), (^posix.sockaddr)(&addr), &addr_len)
+ if client_sock < 0 {
+ err = _accept_error()
return
}
+
client = TCP_Socket(client_sock)
- source = _sockaddr_to_endpoint(&sockaddr)
+ source = _sockaddr_to_endpoint(&addr)
return
}
@(private)
_close :: proc(skt: Any_Socket) {
s := any_socket_to_socket(skt)
- os.close(os.Handle(os.Socket(s)))
+ posix.close(posix.FD(s))
}
@(private)
-_recv_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_read: int, err: Network_Error) {
+_recv_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_read: int, err: TCP_Recv_Error) {
if len(buf) <= 0 {
return
}
- res, res_err := os.recv(os.Socket(skt), buf, 0)
- if res_err != nil {
- err = TCP_Recv_Error(os.is_platform_error(res_err) or_else -1)
+
+ res := posix.recv(posix.FD(skt), raw_data(buf), len(buf), {})
+ if res < 0 {
+ err = _tcp_recv_error()
return
}
+
return int(res), nil
}
@(private)
-_recv_udp :: proc(skt: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: Network_Error) {
+_recv_udp :: proc(skt: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: UDP_Recv_Error) {
if len(buf) <= 0 {
return
}
- from: os.SOCKADDR_STORAGE_LH
- fromsize := c.int(size_of(from))
- res, res_err := os.recvfrom(os.Socket(skt), buf, 0, cast(^os.SOCKADDR) &from, &fromsize)
- if res_err != nil {
- err = UDP_Recv_Error(os.is_platform_error(res_err) or_else -1)
+ from: posix.sockaddr_storage
+ fromsize := posix.socklen_t(size_of(from))
+ res := posix.recvfrom(posix.FD(skt), raw_data(buf), len(buf), {}, (^posix.sockaddr)(&from), &fromsize)
+ if res < 0 {
+ err = _udp_recv_error()
return
}
@@ -207,67 +205,60 @@ _recv_udp :: proc(skt: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endp
}
@(private)
-_send_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_written: int, err: Network_Error) {
+_send_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_written: int, err: TCP_Send_Error) {
for bytes_written < len(buf) {
limit := min(int(max(i32)), len(buf) - bytes_written)
remaining := buf[bytes_written:][:limit]
- res, res_err := os.send(os.Socket(skt), remaining, os.MSG_NOSIGNAL)
- if res_err == os.EPIPE {
- // EPIPE arises if the socket has been closed remotely.
- err = TCP_Send_Error.Connection_Closed
- return
- } else if res_err != nil {
- err = TCP_Send_Error(os.is_platform_error(res_err) or_else -1)
+ res := posix.send(posix.FD(skt), raw_data(remaining), len(remaining), {.NOSIGNAL})
+ if res < 0 {
+ err = _tcp_send_error()
return
}
+
bytes_written += int(res)
}
return
}
@(private)
-_send_udp :: proc(skt: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: int, err: Network_Error) {
+_send_udp :: proc(skt: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: int, err: UDP_Send_Error) {
toaddr := _endpoint_to_sockaddr(to)
for bytes_written < len(buf) {
limit := min(1<<31, len(buf) - bytes_written)
remaining := buf[bytes_written:][:limit]
- res, res_err := os.sendto(os.Socket(skt), remaining, os.MSG_NOSIGNAL, cast(^os.SOCKADDR)&toaddr, i32(toaddr.len))
- if res_err == os.EPIPE {
- // EPIPE arises if the socket has been closed remotely.
- err = UDP_Send_Error.Not_Socket
- return
- } else if res_err != nil {
- err = UDP_Send_Error(os.is_platform_error(res_err) or_else -1)
+ res := posix.sendto(posix.FD(skt), raw_data(remaining), len(remaining), {.NOSIGNAL}, (^posix.sockaddr)(&toaddr), posix.socklen_t(toaddr.ss_len))
+ if res < 0 {
+ err = _udp_send_error()
return
}
+
bytes_written += int(res)
}
return
}
@(private)
-_shutdown :: proc(skt: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Error) {
+_shutdown :: proc(skt: Any_Socket, manner: Shutdown_Manner) -> (err: Shutdown_Error) {
s := any_socket_to_socket(skt)
- res := os.shutdown(os.Socket(s), int(manner))
- if res != nil {
- return Shutdown_Error(os.is_platform_error(res) or_else -1)
+ if posix.shutdown(posix.FD(s), posix.Shut(manner)) != .OK {
+ err = _shutdown_error()
}
return
}
@(private)
-_set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Network_Error {
- level := os.SOL_SOCKET if option != .TCP_Nodelay else os.IPPROTO_TCP
+_set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Socket_Option_Error {
+ level := posix.SOL_SOCKET if option != .TCP_Nodelay else posix.IPPROTO_TCP
// NOTE(tetra, 2022-02-15): On Linux, you cannot merely give a single byte for a bool;
// it _has_ to be a b32.
// I haven't tested if you can give more than that.
bool_value: b32
- int_value: i32
- timeval_value: os.Timeval
+ int_value: posix.socklen_t
+ timeval_value: posix.timeval
ptr: rawptr
- len: os.socklen_t
+ len: posix.socklen_t
switch option {
case
@@ -302,8 +293,8 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca
t := value.(time.Duration) or_else panic("set_option() value must be a time.Duration here", loc)
micros := i64(time.duration_microseconds(t))
- timeval_value.microseconds = int(micros % 1e6)
- timeval_value.seconds = (micros - i64(timeval_value.microseconds)) / 1e6
+ timeval_value.tv_usec = posix.suseconds_t(micros % 1e6)
+ timeval_value.tv_sec = posix.time_t(micros - i64(timeval_value.tv_usec)) / 1e6
ptr = &timeval_value
len = size_of(timeval_value)
@@ -312,12 +303,12 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca
.Send_Buffer_Size:
// TODO: check for out of range values and return .Value_Out_Of_Range?
switch i in value {
- case i8, u8: i2 := i; int_value = os.socklen_t((^u8)(&i2)^)
- case i16, u16: i2 := i; int_value = os.socklen_t((^u16)(&i2)^)
- case i32, u32: i2 := i; int_value = os.socklen_t((^u32)(&i2)^)
- case i64, u64: i2 := i; int_value = os.socklen_t((^u64)(&i2)^)
- case i128, u128: i2 := i; int_value = os.socklen_t((^u128)(&i2)^)
- case int, uint: i2 := i; int_value = os.socklen_t((^uint)(&i2)^)
+ case i8, u8: i2 := i; int_value = posix.socklen_t((^u8)(&i2)^)
+ case i16, u16: i2 := i; int_value = posix.socklen_t((^u16)(&i2)^)
+ case i32, u32: i2 := i; int_value = posix.socklen_t((^u32)(&i2)^)
+ case i64, u64: i2 := i; int_value = posix.socklen_t((^u64)(&i2)^)
+ case i128, u128: i2 := i; int_value = posix.socklen_t((^u128)(&i2)^)
+ case int, uint: i2 := i; int_value = posix.socklen_t((^uint)(&i2)^)
case:
panic("set_option() value must be an integer here", loc)
}
@@ -326,54 +317,53 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca
}
skt := any_socket_to_socket(s)
- res := os.setsockopt(os.Socket(skt), int(level), int(option), ptr, len)
- if res != nil {
- return Socket_Option_Error(os.is_platform_error(res) or_else -1)
+ if posix.setsockopt(posix.FD(skt), i32(level), posix.Sock_Option(option), ptr, len) != .OK {
+ return _socket_option_error()
}
return nil
}
@(private)
-_set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_Error) {
+_set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Set_Blocking_Error) {
socket := any_socket_to_socket(socket)
- flags, getfl_err := os.fcntl(int(socket), os.F_GETFL, 0)
- if getfl_err != nil {
- return Set_Blocking_Error(os.is_platform_error(getfl_err) or_else -1)
+ flags_ := posix.fcntl(posix.FD(socket), .GETFL, 0)
+ if flags_ < 0 {
+ return _set_blocking_error()
}
+ flags := transmute(posix.O_Flags)flags_
if should_block {
- flags &~= int(os.O_NONBLOCK)
+ flags -= {.NONBLOCK}
} else {
- flags |= int(os.O_NONBLOCK)
+ flags += {.NONBLOCK}
}
- _, setfl_err := os.fcntl(int(socket), os.F_SETFL, flags)
- if setfl_err != nil {
- return Set_Blocking_Error(os.is_platform_error(setfl_err) or_else -1)
+ if posix.fcntl(posix.FD(socket), .SETFL, flags) < 0 {
+ return _set_blocking_error()
}
return nil
}
@private
-_endpoint_to_sockaddr :: proc(ep: Endpoint) -> (sockaddr: os.SOCKADDR_STORAGE_LH) {
+_endpoint_to_sockaddr :: proc(ep: Endpoint) -> (sockaddr: posix.sockaddr_storage) {
switch a in ep.address {
case IP4_Address:
- (^os.sockaddr_in)(&sockaddr)^ = os.sockaddr_in {
+ (^posix.sockaddr_in)(&sockaddr)^ = posix.sockaddr_in {
sin_port = u16be(ep.port),
- sin_addr = transmute(os.in_addr) a,
- sin_family = u8(os.AF_INET),
- sin_len = size_of(os.sockaddr_in),
+ sin_addr = transmute(posix.in_addr)a,
+ sin_family = .INET,
+ sin_len = size_of(posix.sockaddr_in),
}
return
case IP6_Address:
- (^os.sockaddr_in6)(&sockaddr)^ = os.sockaddr_in6 {
+ (^posix.sockaddr_in6)(&sockaddr)^ = posix.sockaddr_in6 {
sin6_port = u16be(ep.port),
- sin6_addr = transmute(os.in6_addr) a,
- sin6_family = u8(os.AF_INET6),
- sin6_len = size_of(os.sockaddr_in6),
+ sin6_addr = transmute(posix.in6_addr)a,
+ sin6_family = .INET6,
+ sin6_len = size_of(posix.sockaddr_in6),
}
return
}
@@ -381,21 +371,21 @@ _endpoint_to_sockaddr :: proc(ep: Endpoint) -> (sockaddr: os.SOCKADDR_STORAGE_LH
}
@private
-_sockaddr_to_endpoint :: proc(native_addr: ^os.SOCKADDR_STORAGE_LH) -> (ep: Endpoint) {
- switch native_addr.family {
- case u8(os.AF_INET):
- addr := cast(^os.sockaddr_in) native_addr
+_sockaddr_to_endpoint :: proc(native_addr: ^posix.sockaddr_storage) -> (ep: Endpoint) {
+ #partial switch native_addr.ss_family {
+ case .INET:
+ addr := cast(^posix.sockaddr_in)native_addr
port := int(addr.sin_port)
ep = Endpoint {
- address = IP4_Address(transmute([4]byte) addr.sin_addr),
- port = port,
+ address = IP4_Address(transmute([4]byte)addr.sin_addr),
+ port = port,
}
- case u8(os.AF_INET6):
- addr := cast(^os.sockaddr_in6) native_addr
+ case .INET6:
+ addr := cast(^posix.sockaddr_in6)native_addr
port := int(addr.sin6_port)
ep = Endpoint {
- address = IP6_Address(transmute([8]u16be) addr.sin6_addr),
- port = port,
+ address = IP6_Address(transmute([8]u16be)addr.sin6_addr),
+ port = port,
}
case:
panic("native_addr is neither IP4 or IP6 address")
@@ -404,21 +394,21 @@ _sockaddr_to_endpoint :: proc(native_addr: ^os.SOCKADDR_STORAGE_LH) -> (ep: Endp
}
@(private)
-_sockaddr_basic_to_endpoint :: proc(native_addr: ^os.SOCKADDR) -> (ep: Endpoint) {
- switch u16(native_addr.family) {
- case u16(os.AF_INET):
- addr := cast(^os.sockaddr_in) native_addr
+_sockaddr_basic_to_endpoint :: proc(native_addr: ^posix.sockaddr) -> (ep: Endpoint) {
+ #partial switch native_addr.sa_family {
+ case .INET:
+ addr := cast(^posix.sockaddr_in)native_addr
port := int(addr.sin_port)
ep = Endpoint {
- address = IP4_Address(transmute([4]byte) addr.sin_addr),
- port = port,
+ address = IP4_Address(transmute([4]byte)addr.sin_addr),
+ port = port,
}
- case u16(os.AF_INET6):
- addr := cast(^os.sockaddr_in6) native_addr
+ case .INET6:
+ addr := cast(^posix.sockaddr_in6)native_addr
port := int(addr.sin6_port)
ep = Endpoint {
- address = IP6_Address(transmute([8]u16be) addr.sin6_addr),
- port = port,
+ address = IP6_Address(transmute([8]u16be)addr.sin6_addr),
+ port = port,
}
case:
panic("native_addr is neither IP4 or IP6 address")
diff --git a/core/net/socket_freebsd.odin b/core/net/socket_freebsd.odin
index 3a3774007..b510346ba 100644
--- a/core/net/socket_freebsd.odin
+++ b/core/net/socket_freebsd.odin
@@ -54,8 +54,14 @@ Socket_Option :: enum c.int {
Receive_Timeout = cast(c.int)freebsd.Socket_Option.RCVTIMEO,
}
+Shutdown_Manner :: enum c.int {
+ Receive = cast(c.int)freebsd.Shutdown_Method.RD,
+ Send = cast(c.int)freebsd.Shutdown_Method.WR,
+ Both = cast(c.int)freebsd.Shutdown_Method.RDWR,
+}
+
@(private)
-_create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Network_Error) {
+_create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Create_Socket_Error) {
sys_family: freebsd.Protocol_Family = ---
sys_protocol: freebsd.Protocol = ---
sys_socket_type: freebsd.Socket_Type = ---
@@ -72,24 +78,7 @@ _create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (so
new_socket, errno := freebsd.socket(sys_family, sys_socket_type, sys_protocol)
if errno != nil {
- err = cast(Create_Socket_Error)errno
- return
- }
-
- // NOTE(Feoramund): By default, FreeBSD will generate SIGPIPE if an EPIPE
- // error is raised during the writing of a socket that may be closed.
- // This behavior is unlikely to be expected by general users.
- //
- // There are two workarounds. One is to apply the .NOSIGNAL flag when using
- // the `sendto` syscall. However, that would prevent users of this library
- // from re-enabling the SIGPIPE-raising functionality, if they really
- // wanted it.
- //
- // So I have disabled it here with this socket option for all sockets.
- truth: b32 = true
- errno = freebsd.setsockopt(new_socket, .SOCKET, .NOSIGPIPE, &truth, size_of(truth))
- if errno != nil {
- err = cast(Socket_Option_Error)errno
+ err = _create_socket_error(errno)
return
}
@@ -102,7 +91,7 @@ _create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (so
}
@(private)
-_dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_options) -> (socket: TCP_Socket, err: Network_Error) {
+_dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := DEFAULT_TCP_OPTIONS) -> (socket: TCP_Socket, err: Network_Error) {
if endpoint.port == 0 {
return 0, .Port_Required
}
@@ -115,19 +104,19 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
errno := freebsd.connect(cast(Fd)socket, &sockaddr, cast(freebsd.socklen_t)sockaddr.len)
if errno != nil {
close(socket)
- return {}, cast(Dial_Error)errno
+ return {}, _dial_error(errno)
}
return
}
@(private)
-_bind :: proc(socket: Any_Socket, ep: Endpoint) -> (err: Network_Error) {
+_bind :: proc(socket: Any_Socket, ep: Endpoint) -> (err: Bind_Error) {
sockaddr := _endpoint_to_sockaddr(ep)
real_socket := any_socket_to_socket(socket)
errno := freebsd.bind(cast(Fd)real_socket, &sockaddr, cast(freebsd.socklen_t)sockaddr.len)
if errno != nil {
- err = cast(Bind_Error)errno
+ err = _bind_error(errno)
}
return
}
@@ -143,7 +132,7 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (socket: T
errno := freebsd.listen(cast(Fd)socket, backlog)
if errno != nil {
- err = cast(Listen_Error)errno
+ err = _listen_error(errno)
return
}
@@ -151,12 +140,12 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (socket: T
}
@(private)
-_bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Network_Error) {
+_bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Listen_Error) {
sockaddr: freebsd.Socket_Address_Storage
errno := freebsd.getsockname(cast(Fd)any_socket_to_socket(sock), &sockaddr)
if errno != nil {
- err = cast(Listen_Error)errno
+ err = _listen_error(errno)
return
}
@@ -165,12 +154,12 @@ _bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Network_Error)
}
@(private)
-_accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (client: TCP_Socket, source: Endpoint, err: Network_Error) {
+_accept_tcp :: proc(sock: TCP_Socket, options := DEFAULT_TCP_OPTIONS) -> (client: TCP_Socket, source: Endpoint, err: Accept_Error) {
sockaddr: freebsd.Socket_Address_Storage
result, errno := freebsd.accept(cast(Fd)sock, &sockaddr)
if errno != nil {
- err = cast(Accept_Error)errno
+ err = _accept_error(errno)
return
}
@@ -187,20 +176,20 @@ _close :: proc(socket: Any_Socket) {
}
@(private)
-_recv_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_read: int, err: Network_Error) {
+_recv_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_read: int, err: TCP_Recv_Error) {
if len(buf) == 0 {
return
}
result, errno := freebsd.recv(cast(Fd)socket, buf, .NONE)
if errno != nil {
- err = cast(TCP_Recv_Error)errno
+ err = _tcp_recv_error(errno)
return
}
return result, nil
}
@(private)
-_recv_udp :: proc(socket: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: Network_Error) {
+_recv_udp :: proc(socket: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: UDP_Recv_Error) {
if len(buf) == 0 {
return
}
@@ -208,21 +197,21 @@ _recv_udp :: proc(socket: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_e
result, errno := freebsd.recvfrom(cast(Fd)socket, buf, .NONE, &from)
if errno != nil {
- err = cast(UDP_Recv_Error)errno
+ err = _udp_recv_error(errno)
return
}
return result, _sockaddr_to_endpoint(&from), nil
}
@(private)
-_send_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_written: int, err: Network_Error) {
+_send_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_written: int, err: TCP_Send_Error) {
for bytes_written < len(buf) {
limit := min(int(max(i32)), len(buf) - bytes_written)
remaining := buf[bytes_written:][:limit]
- result, errno := freebsd.send(cast(Fd)socket, remaining, .NONE)
+ result, errno := freebsd.send(cast(Fd)socket, remaining, .NOSIGNAL)
if errno != nil {
- err = cast(TCP_Send_Error)errno
+ err = _tcp_send_error(errno)
return
}
bytes_written += result
@@ -231,15 +220,15 @@ _send_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_written: int, err:
}
@(private)
-_send_udp :: proc(socket: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: int, err: Network_Error) {
+_send_udp :: proc(socket: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: int, err: UDP_Send_Error) {
toaddr := _endpoint_to_sockaddr(to)
for bytes_written < len(buf) {
limit := min(int(max(i32)), len(buf) - bytes_written)
remaining := buf[bytes_written:][:limit]
- result, errno := freebsd.sendto(cast(Fd)socket, remaining, .NONE, &toaddr)
+ result, errno := freebsd.sendto(cast(Fd)socket, remaining, .NOSIGNAL, &toaddr)
if errno != nil {
- err = cast(UDP_Send_Error)errno
+ err = _udp_send_error(errno)
return
}
bytes_written += result
@@ -248,17 +237,17 @@ _send_udp :: proc(socket: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_writt
}
@(private)
-_shutdown :: proc(socket: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Error) {
+_shutdown :: proc(socket: Any_Socket, manner: Shutdown_Manner) -> (err: Shutdown_Error) {
real_socket := cast(Fd)any_socket_to_socket(socket)
errno := freebsd.shutdown(real_socket, cast(freebsd.Shutdown_Method)manner)
if errno != nil {
- return cast(Shutdown_Error)errno
+ return _shutdown_error(errno)
}
return
}
@(private)
-_set_option :: proc(socket: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Network_Error {
+_set_option :: proc(socket: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Socket_Option_Error {
// NOTE(Feoramund): I found that FreeBSD, like Linux, requires at least 32
// bits for a boolean socket option value. Nothing less will work.
bool_value: b32
@@ -315,25 +304,25 @@ _set_option :: proc(socket: Any_Socket, option: Socket_Option, value: any, loc :
case u16: int_value = cast(i32)real
case i32: int_value = real
case u32:
- if real > u32(max(i32)) { return .Value_Out_Of_Range }
+ if real > u32(max(i32)) { return .Invalid_Value }
int_value = cast(i32)real
case i64:
- if real > i64(max(i32)) || real < i64(min(i32)) { return .Value_Out_Of_Range }
+ if real > i64(max(i32)) || real < i64(min(i32)) { return .Invalid_Value }
int_value = cast(i32)real
case u64:
- if real > u64(max(i32)) { return .Value_Out_Of_Range }
+ if real > u64(max(i32)) { return .Invalid_Value }
int_value = cast(i32)real
case i128:
- if real > i128(max(i32)) || real < i128(min(i32)) { return .Value_Out_Of_Range }
+ if real > i128(max(i32)) || real < i128(min(i32)) { return .Invalid_Value }
int_value = cast(i32)real
case u128:
- if real > u128(max(i32)) { return .Value_Out_Of_Range }
+ if real > u128(max(i32)) { return .Invalid_Value }
int_value = cast(i32)real
case int:
- if real > int(max(i32)) || real < int(min(i32)) { return .Value_Out_Of_Range }
+ if real > int(max(i32)) || real < int(min(i32)) { return .Invalid_Value }
int_value = cast(i32)real
case uint:
- if real > uint(max(i32)) { return .Value_Out_Of_Range }
+ if real > uint(max(i32)) { return .Invalid_Value }
int_value = cast(i32)real
case:
panic("set_option() value must be an integer here", loc)
@@ -347,19 +336,19 @@ _set_option :: proc(socket: Any_Socket, option: Socket_Option, value: any, loc :
real_socket := any_socket_to_socket(socket)
errno := freebsd.setsockopt(cast(Fd)real_socket, .SOCKET, cast(freebsd.Socket_Option)option, ptr, len)
if errno != nil {
- return cast(Socket_Option_Error)errno
+ return _socket_option_error(errno)
}
return nil
}
@(private)
-_set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_Error) {
+_set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Set_Blocking_Error) {
real_socket := any_socket_to_socket(socket)
flags, errno := freebsd.fcntl_getfl(cast(freebsd.Fd)real_socket)
if errno != nil {
- return cast(Set_Blocking_Error)errno
+ return _set_blocking_error(errno)
}
if should_block {
@@ -370,7 +359,7 @@ _set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_E
errno = freebsd.fcntl_setfl(cast(freebsd.Fd)real_socket, flags)
if errno != nil {
- return cast(Set_Blocking_Error)errno
+ return _set_blocking_error(errno)
}
return
diff --git a/core/net/socket_linux.odin b/core/net/socket_linux.odin
index b7816b0b6..3ec3521f0 100644
--- a/core/net/socket_linux.odin
+++ b/core/net/socket_linux.odin
@@ -35,17 +35,24 @@ Socket_Option :: enum c.int {
Send_Buffer_Size = c.int(linux.Socket_Option.SNDBUF),
Receive_Timeout = c.int(linux.Socket_Option.RCVTIMEO),
Send_Timeout = c.int(linux.Socket_Option.SNDTIMEO),
+ Broadcast = c.int(linux.Socket_Option.BROADCAST),
+}
+
+Shutdown_Manner :: enum c.int {
+ Receive = c.int(linux.Shutdown_How.RD),
+ Send = c.int(linux.Shutdown_How.WR),
+ Both = c.int(linux.Shutdown_How.RDWR),
}
// Wrappers and unwrappers for system-native types
@(private="file")
-_unwrap_os_socket :: proc "contextless" (sock: Any_Socket)->linux.Fd {
+_unwrap_os_socket :: proc "contextless" (sock: Any_Socket) -> linux.Fd {
return linux.Fd(any_socket_to_socket(sock))
}
@(private="file")
-_wrap_os_socket :: proc "contextless" (sock: linux.Fd, protocol: Socket_Protocol)->Any_Socket {
+_wrap_os_socket :: proc "contextless" (sock: linux.Fd, protocol: Socket_Protocol) -> Any_Socket {
switch protocol {
case .TCP: return TCP_Socket(Socket(sock))
case .UDP: return UDP_Socket(Socket(sock))
@@ -55,7 +62,7 @@ _wrap_os_socket :: proc "contextless" (sock: linux.Fd, protocol: Socket_Protocol
}
@(private="file")
-_unwrap_os_family :: proc "contextless" (family: Address_Family)->linux.Address_Family {
+_unwrap_os_family :: proc "contextless" (family: Address_Family) -> linux.Address_Family {
switch family {
case .IP4: return .INET
case .IP6: return .INET6
@@ -65,7 +72,7 @@ _unwrap_os_family :: proc "contextless" (family: Address_Family)->linux.Address_
}
@(private="file")
-_unwrap_os_proto_socktype :: proc "contextless" (protocol: Socket_Protocol)->(linux.Protocol, linux.Socket_Type) {
+_unwrap_os_proto_socktype :: proc "contextless" (protocol: Socket_Protocol) -> (linux.Protocol, linux.Socket_Type) {
switch protocol {
case .TCP: return .TCP, .STREAM
case .UDP: return .UDP, .DGRAM
@@ -75,7 +82,7 @@ _unwrap_os_proto_socktype :: proc "contextless" (protocol: Socket_Protocol)->(li
}
@(private="file")
-_unwrap_os_addr :: proc "contextless" (endpoint: Endpoint)->(linux.Sock_Addr_Any) {
+_unwrap_os_addr :: proc "contextless" (endpoint: Endpoint) -> linux.Sock_Addr_Any {
switch address in endpoint.address {
case IP4_Address:
return {
@@ -99,7 +106,7 @@ _unwrap_os_addr :: proc "contextless" (endpoint: Endpoint)->(linux.Sock_Addr_Any
}
@(private="file")
-_wrap_os_addr :: proc "contextless" (addr: linux.Sock_Addr_Any)->(Endpoint) {
+_wrap_os_addr :: proc "contextless" (addr: linux.Sock_Addr_Any) -> Endpoint {
#partial switch addr.family {
case .INET:
return {
@@ -116,18 +123,18 @@ _wrap_os_addr :: proc "contextless" (addr: linux.Sock_Addr_Any)->(Endpoint) {
}
}
-_create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (Any_Socket, Network_Error) {
+_create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (Any_Socket, Create_Socket_Error) {
family := _unwrap_os_family(family)
proto, socktype := _unwrap_os_proto_socktype(protocol)
sock, errno := linux.socket(family, socktype, {.CLOEXEC}, proto)
if errno != .NONE {
- return {}, Create_Socket_Error(errno)
+ return {}, _create_socket_error(errno)
}
return _wrap_os_socket(sock, protocol), nil
}
@(private)
-_dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_options) -> (TCP_Socket, Network_Error) {
+_dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := DEFAULT_TCP_OPTIONS) -> (TCP_Socket, Network_Error) {
errno: linux.Errno
if endpoint.port == 0 {
return 0, .Port_Required
@@ -137,7 +144,7 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
os_sock, errno = linux.socket(_unwrap_os_family(family_from_endpoint(endpoint)), .STREAM, {.CLOEXEC}, .TCP)
if errno != .NONE {
// TODO(flysand): should return invalid file descriptor here casted as TCP_Socket
- return {}, Create_Socket_Error(errno)
+ return {}, _create_socket_error(errno)
}
// NOTE(tetra): This is so that if we crash while the socket is open, we can
// bypass the cooldown period, and allow the next run of the program to
@@ -148,7 +155,7 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
errno = linux.connect(linux.Fd(os_sock), &addr)
if errno != .NONE {
close(cast(TCP_Socket) os_sock)
- return {}, Dial_Error(errno)
+ return {}, _dial_error(errno)
}
// NOTE(tetra): Not vital to succeed; error ignored
no_delay: b32 = cast(b32) options.no_delay
@@ -157,11 +164,11 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
}
@(private)
-_bind :: proc(sock: Any_Socket, endpoint: Endpoint) -> (Network_Error) {
+_bind :: proc(sock: Any_Socket, endpoint: Endpoint) -> (Bind_Error) {
addr := _unwrap_os_addr(endpoint)
errno := linux.bind(_unwrap_os_socket(sock), &addr)
if errno != .NONE {
- return Bind_Error(errno)
+ return _bind_error(errno)
}
return nil
}
@@ -179,7 +186,7 @@ _listen_tcp :: proc(endpoint: Endpoint, backlog := 1000) -> (socket: TCP_Socket,
os_sock: linux.Fd
os_sock, errno = linux.socket(ep_family, .STREAM, {.CLOEXEC}, .TCP)
if errno != .NONE {
- err = Create_Socket_Error(errno)
+ err = _create_socket_error(errno)
return
}
socket = cast(TCP_Socket)os_sock
@@ -192,31 +199,30 @@ _listen_tcp :: proc(endpoint: Endpoint, backlog := 1000) -> (socket: TCP_Socket,
// TODO(tetra, 2022-02-15): Confirm that this doesn't mean other processes can hijack the address!
do_reuse_addr: b32 = true
if errno = linux.setsockopt(os_sock, linux.SOL_SOCKET, linux.Socket_Option.REUSEADDR, &do_reuse_addr); errno != .NONE {
- err = Listen_Error(errno)
+ err = _listen_error(errno)
return
}
// Bind the socket to endpoint address
if errno = linux.bind(os_sock, &ep_address); errno != .NONE {
- err = Bind_Error(errno)
+ err = _bind_error(errno)
return
}
// Listen on bound socket
if errno = linux.listen(os_sock, cast(i32) backlog); errno != .NONE {
- err = Listen_Error(errno)
- return
+ err = _listen_error(errno)
}
return
}
@(private)
-_bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Network_Error) {
+_bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Listen_Error) {
addr: linux.Sock_Addr_Any
errno := linux.getsockname(_unwrap_os_socket(sock), &addr)
if errno != .NONE {
- err = Listen_Error(errno)
+ err = _listen_error(errno)
return
}
@@ -225,11 +231,11 @@ _bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Network_Error)
}
@(private)
-_accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (tcp_client: TCP_Socket, endpoint: Endpoint, err: Network_Error) {
+_accept_tcp :: proc(sock: TCP_Socket, options := DEFAULT_TCP_OPTIONS) -> (tcp_client: TCP_Socket, endpoint: Endpoint, err: Accept_Error) {
addr: linux.Sock_Addr_Any
client_sock, errno := linux.accept(linux.Fd(sock), &addr)
if errno != .NONE {
- return {}, {}, Accept_Error(errno)
+ return {}, {}, _accept_error(errno)
}
// NOTE(tetra): Not vital to succeed; error ignored
val: b32 = cast(b32) options.no_delay
@@ -243,19 +249,19 @@ _close :: proc(sock: Any_Socket) {
}
@(private)
-_recv_tcp :: proc(tcp_sock: TCP_Socket, buf: []byte) -> (int, Network_Error) {
+_recv_tcp :: proc(tcp_sock: TCP_Socket, buf: []byte) -> (int, TCP_Recv_Error) {
if len(buf) <= 0 {
return 0, nil
}
bytes_read, errno := linux.recv(linux.Fd(tcp_sock), buf, {})
if errno != .NONE {
- return 0, TCP_Recv_Error(errno)
+ return 0, _tcp_recv_error(errno)
}
return int(bytes_read), nil
}
@(private)
-_recv_udp :: proc(udp_sock: UDP_Socket, buf: []byte) -> (int, Endpoint, Network_Error) {
+_recv_udp :: proc(udp_sock: UDP_Socket, buf: []byte) -> (int, Endpoint, UDP_Recv_Error) {
if len(buf) <= 0 {
// NOTE(flysand): It was returning no error, I didn't change anything
return 0, {}, {}
@@ -267,28 +273,24 @@ _recv_udp :: proc(udp_sock: UDP_Socket, buf: []byte) -> (int, Endpoint, Network_
from_addr: linux.Sock_Addr_Any
bytes_read, errno := linux.recvfrom(linux.Fd(udp_sock), buf, {.TRUNC}, &from_addr)
if errno != .NONE {
- return 0, {}, UDP_Recv_Error(errno)
+ return 0, {}, _udp_recv_error(errno)
}
if bytes_read > len(buf) {
// NOTE(tetra): The buffer has been filled, with a partial message.
- return len(buf), {}, .Buffer_Too_Small
+ return len(buf), {}, .Excess_Truncated
}
return bytes_read, _wrap_os_addr(from_addr), nil
}
@(private)
-_send_tcp :: proc(tcp_sock: TCP_Socket, buf: []byte) -> (int, Network_Error) {
+_send_tcp :: proc(tcp_sock: TCP_Socket, buf: []byte) -> (int, TCP_Send_Error) {
total_written := 0
for total_written < len(buf) {
limit := min(int(max(i32)), len(buf) - total_written)
remaining := buf[total_written:][:limit]
res, errno := linux.send(linux.Fd(tcp_sock), remaining, {.NOSIGNAL})
- if errno == .EPIPE {
- // If the peer is disconnected when we are trying to send we will get an `EPIPE` error,
- // so we turn that into a clearer error
- return total_written, TCP_Send_Error.Connection_Closed
- } else if errno != .NONE {
- return total_written, TCP_Send_Error(errno)
+ if errno != .NONE {
+ return total_written, _tcp_send_error(errno)
}
total_written += int(res)
}
@@ -296,28 +298,28 @@ _send_tcp :: proc(tcp_sock: TCP_Socket, buf: []byte) -> (int, Network_Error) {
}
@(private)
-_send_udp :: proc(udp_sock: UDP_Socket, buf: []byte, to: Endpoint) -> (int, Network_Error) {
+_send_udp :: proc(udp_sock: UDP_Socket, buf: []byte, to: Endpoint) -> (int, UDP_Send_Error) {
to_addr := _unwrap_os_addr(to)
bytes_written, errno := linux.sendto(linux.Fd(udp_sock), buf, {}, &to_addr)
if errno != .NONE {
- return bytes_written, UDP_Send_Error(errno)
+ return bytes_written, _udp_send_error(errno)
}
return int(bytes_written), nil
}
@(private)
-_shutdown :: proc(sock: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Error) {
+_shutdown :: proc(sock: Any_Socket, manner: Shutdown_Manner) -> (err: Shutdown_Error) {
os_sock := _unwrap_os_socket(sock)
errno := linux.shutdown(os_sock, cast(linux.Shutdown_How) manner)
if errno != .NONE {
- return Shutdown_Error(errno)
+ return _shutdown_error(errno)
}
return nil
}
// TODO(flysand): Figure out what we want to do with this on core:sys/ level.
@(private)
-_set_option :: proc(sock: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Network_Error {
+_set_option :: proc(sock: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Socket_Option_Error {
level: int
if option == .TCP_Nodelay {
level = int(linux.SOL_TCP)
@@ -337,7 +339,8 @@ _set_option :: proc(sock: Any_Socket, option: Socket_Option, value: any, loc :=
.Reuse_Address,
.Keep_Alive,
.Out_Of_Bounds_Data_Inline,
- .TCP_Nodelay:
+ .TCP_Nodelay,
+ .Broadcast:
// TODO: verify whether these are options or not on Linux
// .Broadcast, <-- yes
// .Conditional_Accept,
@@ -386,19 +389,19 @@ _set_option :: proc(sock: Any_Socket, option: Socket_Option, value: any, loc :=
errno = linux.setsockopt(os_sock, level, int(option), &int_value)
}
if errno != .NONE {
- return Socket_Option_Error(errno)
+ return _socket_option_error(errno)
}
return nil
}
@(private)
-_set_blocking :: proc(sock: Any_Socket, should_block: bool) -> (err: Network_Error) {
+_set_blocking :: proc(sock: Any_Socket, should_block: bool) -> (err: Set_Blocking_Error) {
errno: linux.Errno
flags: linux.Open_Flags
os_sock := _unwrap_os_socket(sock)
flags, errno = linux.fcntl(os_sock, linux.F_GETFL)
if errno != .NONE {
- return Set_Blocking_Error(errno)
+ return _set_blocking_error(errno)
}
if should_block {
flags -= {.NONBLOCK}
@@ -407,7 +410,7 @@ _set_blocking :: proc(sock: Any_Socket, should_block: bool) -> (err: Network_Err
}
errno = linux.fcntl(os_sock, linux.F_SETFL, flags)
if errno != .NONE {
- return Set_Blocking_Error(errno)
+ return _set_blocking_error(errno)
}
return nil
}
diff --git a/core/net/socket_windows.odin b/core/net/socket_windows.odin
index 747d5cab3..4576149de 100644
--- a/core/net/socket_windows.odin
+++ b/core/net/socket_windows.odin
@@ -24,13 +24,67 @@ import "core:c"
import win "core:sys/windows"
import "core:time"
+Socket_Option :: enum c.int {
+ // bool: Whether the address that this socket is bound to can be reused by other sockets.
+ // This allows you to bypass the cooldown period if a program dies while the socket is bound.
+ Reuse_Address = win.SO_REUSEADDR,
+
+ // bool: Whether other programs will be inhibited from binding the same endpoint as this socket.
+ Exclusive_Addr_Use = win.SO_EXCLUSIVEADDRUSE,
+
+ // bool: When true, keepalive packets will be automatically be sent for this connection. TODO: verify this understanding
+ Keep_Alive = win.SO_KEEPALIVE,
+
+ // bool: When true, client connections will immediately be sent a TCP/IP RST response, rather than being accepted.
+ Conditional_Accept = win.SO_CONDITIONAL_ACCEPT,
+
+ // bool: If true, when the socket is closed, but data is still waiting to be sent, discard that data.
+ Dont_Linger = win.SO_DONTLINGER,
+
+ // bool: When true, 'out-of-band' data sent over the socket will be read by a normal net.recv() call, the same as normal 'in-band' data.
+ Out_Of_Bounds_Data_Inline = win.SO_OOBINLINE,
+
+ // bool: When true, disables send-coalescing, therefore reducing latency.
+ TCP_Nodelay = win.TCP_NODELAY,
+
+ // win.LINGER: Customizes how long (if at all) the socket will remain open when there
+ // is some remaining data waiting to be sent, and net.close() is called.
+ Linger = win.SO_LINGER,
+
+ // win.DWORD: The size, in bytes, of the OS-managed receive-buffer for this socket.
+ Receive_Buffer_Size = win.SO_RCVBUF,
+
+ // win.DWORD: The size, in bytes, of the OS-managed send-buffer for this socket.
+ Send_Buffer_Size = win.SO_SNDBUF,
+
+ // win.DWORD: For blocking sockets, the time in milliseconds to wait for incoming data to be received, before giving up and returning .Timeout.
+ // For non-blocking sockets, ignored.
+ // Use a value of zero to potentially wait forever.
+ Receive_Timeout = win.SO_RCVTIMEO,
+
+ // win.DWORD: For blocking sockets, the time in milliseconds to wait for outgoing data to be sent, before giving up and returning .Timeout.
+ // For non-blocking sockets, ignored.
+ // Use a value of zero to potentially wait forever.
+ Send_Timeout = win.SO_SNDTIMEO,
+
+ // bool: Allow sending to, receiving from, and binding to, a broadcast address.
+ Broadcast = win.SO_BROADCAST,
+}
+
+
+Shutdown_Manner :: enum c.int {
+ Receive = win.SD_RECEIVE,
+ Send = win.SD_SEND,
+ Both = win.SD_BOTH,
+}
+
@(init, private)
ensure_winsock_initialized :: proc() {
win.ensure_winsock_initialized()
}
@(private)
-_create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Network_Error) {
+_create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Create_Socket_Error) {
c_type, c_protocol, c_family: c.int
switch family {
@@ -49,7 +103,7 @@ _create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (so
sock := win.socket(c_family, c_type, c_protocol)
if sock == win.INVALID_SOCKET {
- err = Create_Socket_Error(win.WSAGetLastError())
+ err = _create_socket_error()
return
}
@@ -62,7 +116,7 @@ _create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (so
}
@(private)
-_dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_options) -> (socket: TCP_Socket, err: Network_Error) {
+_dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := DEFAULT_TCP_OPTIONS) -> (socket: TCP_Socket, err: Network_Error) {
if endpoint.port == 0 {
err = .Port_Required
return
@@ -80,8 +134,9 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
sockaddr := _endpoint_to_sockaddr(endpoint)
res := win.connect(win.SOCKET(socket), &sockaddr, size_of(sockaddr))
if res < 0 {
+ err = _dial_error()
close(socket)
- return {}, Dial_Error(win.WSAGetLastError())
+ return {}, err
}
if options.no_delay {
@@ -92,12 +147,12 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
}
@(private)
-_bind :: proc(socket: Any_Socket, ep: Endpoint) -> (err: Network_Error) {
+_bind :: proc(socket: Any_Socket, ep: Endpoint) -> (err: Bind_Error) {
sockaddr := _endpoint_to_sockaddr(ep)
sock := any_socket_to_socket(socket)
res := win.bind(win.SOCKET(sock), &sockaddr, size_of(sockaddr))
if res < 0 {
- err = Bind_Error(win.WSAGetLastError())
+ err = _bind_error()
}
return
}
@@ -116,17 +171,17 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (socket: T
bind(sock, interface_endpoint) or_return
if res := win.listen(win.SOCKET(socket), i32(backlog)); res == win.SOCKET_ERROR {
- err = Listen_Error(win.WSAGetLastError())
+ err = _listen_error()
}
return
}
@(private)
-_bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Network_Error) {
+_bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Listen_Error) {
sockaddr: win.SOCKADDR_STORAGE_LH
sockaddrlen := c.int(size_of(sockaddr))
if win.getsockname(win.SOCKET(any_socket_to_socket(sock)), &sockaddr, &sockaddrlen) == win.SOCKET_ERROR {
- err = Listen_Error(win.WSAGetLastError())
+ err = _listen_error()
return
}
@@ -135,7 +190,7 @@ _bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Network_Error)
}
@(private)
-_accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (client: TCP_Socket, source: Endpoint, err: Network_Error) {
+_accept_tcp :: proc(sock: TCP_Socket, options := DEFAULT_TCP_OPTIONS) -> (client: TCP_Socket, source: Endpoint, err: Accept_Error) {
for {
sockaddr: win.SOCKADDR_STORAGE_LH
sockaddrlen := c.int(size_of(sockaddr))
@@ -149,7 +204,7 @@ _accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (client
// can do this to match the behaviour.
continue
}
- err = Accept_Error(e)
+ err = _accept_error()
return
}
client = TCP_Socket(client_sock)
@@ -169,20 +224,20 @@ _close :: proc(socket: Any_Socket) {
}
@(private)
-_recv_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_read: int, err: Network_Error) {
+_recv_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_read: int, err: TCP_Recv_Error) {
if len(buf) <= 0 {
return
}
res := win.recv(win.SOCKET(socket), raw_data(buf), c.int(len(buf)), 0)
if res < 0 {
- err = TCP_Recv_Error(win.WSAGetLastError())
+ err = _tcp_recv_error()
return
}
return int(res), nil
}
@(private)
-_recv_udp :: proc(socket: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: Network_Error) {
+_recv_udp :: proc(socket: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: UDP_Recv_Error) {
if len(buf) <= 0 {
return
}
@@ -191,7 +246,7 @@ _recv_udp :: proc(socket: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_e
fromsize := c.int(size_of(from))
res := win.recvfrom(win.SOCKET(socket), raw_data(buf), c.int(len(buf)), 0, &from, &fromsize)
if res < 0 {
- err = UDP_Recv_Error(win.WSAGetLastError())
+ err = _udp_recv_error()
return
}
@@ -201,13 +256,13 @@ _recv_udp :: proc(socket: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_e
}
@(private)
-_send_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_written: int, err: Network_Error) {
+_send_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_written: int, err: TCP_Send_Error) {
for bytes_written < len(buf) {
limit := min(int(max(i32)), len(buf) - bytes_written)
remaining := buf[bytes_written:]
res := win.send(win.SOCKET(socket), raw_data(remaining), c.int(limit), 0)
if res < 0 {
- err = TCP_Send_Error(win.WSAGetLastError())
+ err = _tcp_send_error()
return
}
bytes_written += int(res)
@@ -216,34 +271,34 @@ _send_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_written: int, err:
}
@(private)
-_send_udp :: proc(socket: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: int, err: Network_Error) {
- if len(buf) > int(max(c.int)) {
- // NOTE(tetra): If we don't guard this, we'll return (0, nil) instead, which is misleading.
- err = .Message_Too_Long
- return
- }
+_send_udp :: proc(socket: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: int, err: UDP_Send_Error) {
toaddr := _endpoint_to_sockaddr(to)
- res := win.sendto(win.SOCKET(socket), raw_data(buf), c.int(len(buf)), 0, &toaddr, size_of(toaddr))
- if res < 0 {
- err = UDP_Send_Error(win.WSAGetLastError())
- return
+ for bytes_written < len(buf) {
+ limit := min(int(max(i32)), len(buf) - bytes_written)
+ remaining := buf[bytes_written:]
+ res := win.sendto(win.SOCKET(socket), raw_data(remaining), c.int(limit), 0, &toaddr, size_of(toaddr))
+ if res < 0 {
+ err = _udp_send_error()
+ return
+ }
+
+ bytes_written += int(res)
}
- bytes_written = int(res)
return
}
@(private)
-_shutdown :: proc(socket: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Error) {
+_shutdown :: proc(socket: Any_Socket, manner: Shutdown_Manner) -> (err: Shutdown_Error) {
s := any_socket_to_socket(socket)
res := win.shutdown(win.SOCKET(s), c.int(manner))
if res < 0 {
- return Shutdown_Error(win.WSAGetLastError())
+ return _shutdown_error()
}
return
}
@(private)
-_set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Network_Error {
+_set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Socket_Option_Error {
level := win.SOL_SOCKET if option != .TCP_Nodelay else win.IPPROTO_TCP
bool_value: b32
@@ -282,11 +337,8 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca
t := value.(time.Duration) or_else panic("set_option() value must be a time.Duration here", loc)
num_secs := i64(time.duration_seconds(t))
- if time.Duration(num_secs * 1e9) != t {
- return .Linger_Only_Supports_Whole_Seconds
- }
if num_secs > i64(max(u16)) {
- return .Value_Out_Of_Range
+ return .Invalid_Value
}
linger_value.l_onoff = 1
linger_value.l_linger = c.ushort(num_secs)
@@ -322,19 +374,19 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca
socket := any_socket_to_socket(s)
res := win.setsockopt(win.SOCKET(socket), c.int(level), c.int(option), ptr, len)
if res < 0 {
- return Socket_Option_Error(win.WSAGetLastError())
+ return _socket_option_error()
}
return nil
}
@(private)
-_set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_Error) {
+_set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Set_Blocking_Error) {
socket := any_socket_to_socket(socket)
arg: win.DWORD = 0 if should_block else 1
res := win.ioctlsocket(win.SOCKET(socket), transmute(win.c_long)win.FIONBIO, &arg)
if res == win.SOCKET_ERROR {
- return Set_Blocking_Error(win.WSAGetLastError())
+ return _set_blocking_error()
}
return nil
diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin
index f62feec8c..3b8998b31 100644
--- a/core/odin/ast/ast.odin
+++ b/core/odin/ast/ast.odin
@@ -432,10 +432,13 @@ Range_Stmt :: struct {
reverse: bool,
}
-Inline_Range_Stmt :: struct {
+Inline_Range_Stmt :: Unroll_Range_Stmt
+
+Unroll_Range_Stmt :: struct {
using node: Stmt,
label: ^Expr,
- inline_pos: tokenizer.Pos,
+ unroll_pos: tokenizer.Pos,
+ args: []^Expr,
for_pos: tokenizer.Pos,
val0: ^Expr,
val1: ^Expr,
diff --git a/core/odin/ast/clone.odin b/core/odin/ast/clone.odin
index 67f7ffa95..b7501e6ca 100644
--- a/core/odin/ast/clone.odin
+++ b/core/odin/ast/clone.odin
@@ -242,8 +242,9 @@ clone_node :: proc(node: ^Node) -> ^Node {
r.vals = clone(r.vals)
r.expr = clone(r.expr)
r.body = clone(r.body)
- case ^Inline_Range_Stmt:
+ case ^Unroll_Range_Stmt:
r.label = clone(r.label)
+ r.args = clone(r.args)
r.val0 = clone(r.val0)
r.val1 = clone(r.val1)
r.expr = clone(r.expr)
diff --git a/core/odin/doc-format/doc_format.odin b/core/odin/doc-format/doc_format.odin
index c2d86a0ba..e6804c981 100644
--- a/core/odin/doc-format/doc_format.odin
+++ b/core/odin/doc-format/doc_format.odin
@@ -180,7 +180,7 @@ Type_Kind :: enum u32le {
Struct = 10,
Union = 11,
Enum = 12,
- Tuple = 13,
+ Parameters = 13,
Proc = 14,
Bit_Set = 15,
Simd_Vector = 16,
@@ -256,10 +256,10 @@ Type :: struct {
types: Array(Type_Index),
// Used by:
- // .Named - 1 field for the definition
- // .Struct - fields
- // .Enum - fields
- // .Tuple - parameters (procedures only)
+ // .Named - 1 field for the definition
+ // .Struct - fields
+ // .Enum - fields
+ // .Parameters - parameters (procedures only)
entities: Array(Entity_Index),
// Used By: .Struct, .Union
diff --git a/core/odin/parser/file_tags.odin b/core/odin/parser/file_tags.odin
index 84b172148..24aea3b9e 100644
--- a/core/odin/parser/file_tags.odin
+++ b/core/odin/parser/file_tags.odin
@@ -17,6 +17,9 @@ Build_Kind :: struct {
arch: runtime.Odin_Arch_Types,
}
+// empty build kind acts as a marker for separating multiple lines with build tags
+BUILD_KIND_NEWLINE_MARKER :: Build_Kind{}
+
File_Tags :: struct {
build_project_name: [][]string,
build: []Build_Kind,
@@ -27,14 +30,27 @@ File_Tags :: struct {
}
@require_results
-get_build_os_from_string :: proc(str: string) -> runtime.Odin_OS_Type {
+get_build_os_from_string :: proc(str: string) -> (found_os: runtime.Odin_OS_Type, found_subtarget: runtime.Odin_Platform_Subtarget_Type) {
+ str_os, _, str_subtarget := strings.partition(str, ":")
+
fields := reflect.enum_fields_zipped(runtime.Odin_OS_Type)
for os in fields {
- if strings.equal_fold(os.name, str) {
- return runtime.Odin_OS_Type(os.value)
+ if strings.equal_fold(os.name, str_os) {
+ found_os = runtime.Odin_OS_Type(os.value)
+ break
}
}
- return .Unknown
+ if str_subtarget != "" {
+ st_fields := reflect.enum_fields_zipped(runtime.Odin_Platform_Subtarget_Type)
+ for subtarget in st_fields {
+ if strings.equal_fold(subtarget.name, str_subtarget) {
+ found_subtarget = runtime.Odin_Platform_Subtarget_Type(subtarget.value)
+ break
+ }
+ }
+ }
+
+ return
}
@require_results
get_build_arch_from_string :: proc(str: string) -> runtime.Odin_Arch_Type {
@@ -147,6 +163,11 @@ parse_file_tags :: proc(file: ast.File, allocator := context.allocator) -> (tags
append(build_project_names, build_project_name_strings[index_start:])
}
case "build":
+
+ if len(build_kinds) > 0 {
+ append(build_kinds, BUILD_KIND_NEWLINE_MARKER)
+ }
+
kinds_loop: for {
os_positive: runtime.Odin_OS_Types
os_negative: runtime.Odin_OS_Types
@@ -179,7 +200,8 @@ parse_file_tags :: proc(file: ast.File, allocator := context.allocator) -> (tags
if value == "ignore" {
tags.ignore = true
- } else if os := get_build_os_from_string(value); os != .Unknown {
+ } else if os, subtarget := get_build_os_from_string(value); os != .Unknown {
+ _ = subtarget // TODO(bill): figure out how to handle the subtarget logic
if is_notted {
os_negative += {os}
} else {
@@ -248,10 +270,20 @@ match_build_tags :: proc(file_tags: File_Tags, target: Build_Target) -> bool {
project_name_correct ||= group_correct
}
- os_and_arch_correct := len(file_tags.build) == 0
+ os_and_arch_correct := true
- for kind in file_tags.build {
- os_and_arch_correct ||= target.os in kind.os && target.arch in kind.arch
+ if len(file_tags.build) > 0 {
+ os_and_arch_correct_line := false
+
+ for kind in file_tags.build {
+ if kind == BUILD_KIND_NEWLINE_MARKER {
+ os_and_arch_correct &&= os_and_arch_correct_line
+ os_and_arch_correct_line = false
+ } else {
+ os_and_arch_correct_line ||= target.os in kind.os && target.arch in kind.arch
+ }
+ }
+ os_and_arch_correct &&= os_and_arch_correct_line
}
return !file_tags.ignore && project_name_correct && os_and_arch_correct
diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin
index 5a7440339..63c7e388f 100644
--- a/core/odin/parser/parser.odin
+++ b/core/odin/parser/parser.odin
@@ -1262,11 +1262,49 @@ parse_foreign_decl :: proc(p: ^Parser) -> ^ast.Decl {
parse_unrolled_for_loop :: proc(p: ^Parser, inline_tok: tokenizer.Token) -> ^ast.Stmt {
- for_tok := expect_token(p, .For)
val0, val1: ^ast.Expr
in_tok: tokenizer.Token
expr: ^ast.Expr
body: ^ast.Stmt
+ args: [dynamic]^ast.Expr
+
+ if allow_token(p, .Open_Paren) {
+ p.expr_level += 1
+ if p.curr_tok.kind == .Close_Paren {
+ error(p, p.curr_tok.pos, "#unroll expected at least 1 argument, got 0")
+ } else {
+ args = make([dynamic]^ast.Expr)
+ for p.curr_tok.kind != .Close_Paren &&
+ p.curr_tok.kind != .EOF {
+ arg := parse_value(p)
+
+ if p.curr_tok.kind == .Eq {
+ eq := expect_token(p, .Eq)
+ if arg != nil {
+ if _, ok := arg.derived.(^ast.Ident); !ok {
+ error(p, arg.pos, "expected an identifier for 'key=value'")
+ }
+ }
+ value := parse_value(p)
+ fv := ast.new(ast.Field_Value, arg.pos, value)
+ fv.field = arg
+ fv.sep = eq.pos
+ fv.value = value
+
+ arg = fv
+ }
+
+ append(&args, arg)
+
+ allow_token(p, .Comma) or_break
+ }
+ }
+
+ p.expr_level -= 1
+ _ = expect_token_after(p, .Close_Paren, "#unroll")
+ }
+
+ for_tok := expect_token(p, .For)
bad_stmt := false
@@ -1309,7 +1347,8 @@ parse_unrolled_for_loop :: proc(p: ^Parser, inline_tok: tokenizer.Token) -> ^ast
}
range_stmt := ast.new(ast.Inline_Range_Stmt, inline_tok.pos, body)
- range_stmt.inline_pos = inline_tok.pos
+ range_stmt.unroll_pos = inline_tok.pos
+ range_stmt.args = args[:]
range_stmt.for_pos = for_tok.pos
range_stmt.val0 = val0
range_stmt.val1 = val1
diff --git a/core/os/dir_unix.odin b/core/os/dir_unix.odin
index 26e865204..c3dd844ef 100644
--- a/core/os/dir_unix.odin
+++ b/core/os/dir_unix.odin
@@ -1,13 +1,14 @@
-#+build darwin, linux, netbsd, freebsd, openbsd
+#+build darwin, linux, netbsd, freebsd, openbsd, haiku
package os
import "core:strings"
@(require_results)
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Error) {
- dupfd := _dup(fd) or_return
+ context.allocator = allocator
- dirp := _fdopendir(dupfd) or_return
+ dupfd := _dup(fd) or_return
+ dirp := _fdopendir(dupfd) or_return
defer _closedir(dirp)
dirpath := absolute_path_from_handle(dupfd) or_return
diff --git a/core/os/file_windows.odin b/core/os/file_windows.odin
deleted file mode 100644
index 375da6aff..000000000
--- a/core/os/file_windows.odin
+++ /dev/null
@@ -1,584 +0,0 @@
-package os
-
-import win32 "core:sys/windows"
-import "base:intrinsics"
-import "base:runtime"
-import "core:unicode/utf16"
-
-@(require_results)
-is_path_separator :: proc(c: byte) -> bool {
- return c == '/' || c == '\\'
-}
-
-@(require_results)
-open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Error) {
- if len(path) == 0 {
- return INVALID_HANDLE, General_Error.Not_Exist
- }
-
- access: u32
- switch mode & (O_RDONLY|O_WRONLY|O_RDWR) {
- case O_RDONLY: access = win32.FILE_GENERIC_READ
- case O_WRONLY: access = win32.FILE_GENERIC_WRITE
- case O_RDWR: access = win32.FILE_GENERIC_READ | win32.FILE_GENERIC_WRITE
- }
-
- if mode&O_CREATE != 0 {
- access |= win32.FILE_GENERIC_WRITE
- }
- if mode&O_APPEND != 0 {
- access &~= win32.FILE_GENERIC_WRITE
- access |= win32.FILE_APPEND_DATA
- }
-
- share_mode := win32.FILE_SHARE_READ|win32.FILE_SHARE_WRITE
- sa: ^win32.SECURITY_ATTRIBUTES = nil
- sa_inherit := win32.SECURITY_ATTRIBUTES{nLength = size_of(win32.SECURITY_ATTRIBUTES), bInheritHandle = true}
- if mode&O_CLOEXEC == 0 {
- sa = &sa_inherit
- }
-
- create_mode: u32
- switch {
- case mode&(O_CREATE|O_EXCL) == (O_CREATE | O_EXCL):
- create_mode = win32.CREATE_NEW
- case mode&(O_CREATE|O_TRUNC) == (O_CREATE | O_TRUNC):
- create_mode = win32.CREATE_ALWAYS
- case mode&O_CREATE == O_CREATE:
- create_mode = win32.OPEN_ALWAYS
- case mode&O_TRUNC == O_TRUNC:
- create_mode = win32.TRUNCATE_EXISTING
- case:
- create_mode = win32.OPEN_EXISTING
- }
- wide_path := win32.utf8_to_wstring(path)
- handle := Handle(win32.CreateFileW(wide_path, access, share_mode, sa, create_mode, win32.FILE_ATTRIBUTE_NORMAL|win32.FILE_FLAG_BACKUP_SEMANTICS, nil))
- if handle != INVALID_HANDLE {
- return handle, nil
- }
-
- return INVALID_HANDLE, get_last_error()
-}
-
-close :: proc(fd: Handle) -> Error {
- if !win32.CloseHandle(win32.HANDLE(fd)) {
- return get_last_error()
- }
- return nil
-}
-
-flush :: proc(fd: Handle) -> (err: Error) {
- if !win32.FlushFileBuffers(win32.HANDLE(fd)) {
- err = get_last_error()
- }
- return
-}
-
-
-
-write :: proc(fd: Handle, data: []byte) -> (int, Error) {
- if len(data) == 0 {
- return 0, nil
- }
-
- single_write_length: win32.DWORD
- total_write: i64
- length := i64(len(data))
-
- for total_write < length {
- remaining := length - total_write
- to_write := win32.DWORD(min(i32(remaining), MAX_RW))
-
- e := win32.WriteFile(win32.HANDLE(fd), &data[total_write], to_write, &single_write_length, nil)
- if single_write_length <= 0 || !e {
- return int(total_write), get_last_error()
- }
- total_write += i64(single_write_length)
- }
- return int(total_write), nil
-}
-
-@(private="file", require_results)
-read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Error) {
- if len(b) == 0 {
- return 0, nil
- }
-
- BUF_SIZE :: 386
- buf16: [BUF_SIZE]u16
- buf8: [4*BUF_SIZE]u8
-
- for n < len(b) && err == nil {
- min_read := max(len(b)/4, 1 if len(b) > 0 else 0)
- max_read := u32(min(BUF_SIZE, min_read))
- if max_read == 0 {
- break
- }
-
- single_read_length: u32
- ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil)
- if !ok {
- err = get_last_error()
- }
-
- buf8_len := utf16.decode_to_utf8(buf8[:], buf16[:single_read_length])
- src := buf8[:buf8_len]
-
- ctrl_z := false
- for i := 0; i < len(src) && n < len(b); i += 1 {
- x := src[i]
- if x == 0x1a { // ctrl-z
- ctrl_z = true
- break
- }
- b[n] = x
- n += 1
- }
- if ctrl_z || single_read_length < max_read {
- break
- }
-
- // NOTE(bill): if the last two values were a newline, then it is expected that
- // this is the end of the input
- if n >= 2 && single_read_length == max_read && string(b[n-2:n]) == "\r\n" {
- break
- }
-
- }
-
- return
-}
-
-read :: proc(fd: Handle, data: []byte) -> (total_read: int, err: Error) {
- if len(data) == 0 {
- return 0, nil
- }
-
- handle := win32.HANDLE(fd)
-
- m: u32
- is_console := win32.GetConsoleMode(handle, &m)
- length := len(data)
-
- // NOTE(Jeroen): `length` can't be casted to win32.DWORD here because it'll overflow if > 4 GiB and return 0 if exactly that.
- to_read := min(i64(length), MAX_RW)
-
- if is_console {
- total_read, err = read_console(handle, data[total_read:][:to_read])
- if err != nil {
- return total_read, err
- }
- } else {
- // NOTE(Jeroen): So we cast it here *after* we've ensured that `to_read` is at most MAX_RW (1 GiB)
- bytes_read: win32.DWORD
- if e := win32.ReadFile(handle, &data[total_read], win32.DWORD(to_read), &bytes_read, nil); e {
- // Successful read can mean two things, including EOF, see:
- // https://learn.microsoft.com/en-us/windows/win32/fileio/testing-for-the-end-of-a-file
- if bytes_read == 0 {
- return 0, .EOF
- } else {
- return int(bytes_read), nil
- }
- } else {
- return 0, get_last_error()
- }
- }
- return total_read, nil
-}
-
-seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
- w: u32
- switch whence {
- case 0: w = win32.FILE_BEGIN
- case 1: w = win32.FILE_CURRENT
- case 2: w = win32.FILE_END
- case:
- return 0, .Invalid_Whence
- }
- hi := i32(offset>>32)
- lo := i32(offset)
- ft := win32.GetFileType(win32.HANDLE(fd))
- if ft == win32.FILE_TYPE_PIPE {
- return 0, .File_Is_Pipe
- }
-
- dw_ptr := win32.SetFilePointer(win32.HANDLE(fd), lo, &hi, w)
- if dw_ptr == win32.INVALID_SET_FILE_POINTER {
- err := get_last_error()
- return 0, err
- }
- return i64(hi)<<32 + i64(dw_ptr), nil
-}
-
-@(require_results)
-file_size :: proc(fd: Handle) -> (i64, Error) {
- length: win32.LARGE_INTEGER
- err: Error
- if !win32.GetFileSizeEx(win32.HANDLE(fd), &length) {
- err = get_last_error()
- }
- return i64(length), err
-}
-
-
-@(private)
-MAX_RW :: 1<<30
-
-@(private)
-pread :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
- curr_off := seek(fd, 0, 1) or_return
- defer seek(fd, curr_off, 0)
-
- buf := data
- if len(buf) > MAX_RW {
- buf = buf[:MAX_RW]
- }
-
- o := win32.OVERLAPPED{
- OffsetHigh = u32(offset>>32),
- Offset = u32(offset),
- }
-
- // TODO(bill): Determine the correct behaviour for consoles
-
- h := win32.HANDLE(fd)
- done: win32.DWORD
- e: Error
- if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
- e = get_last_error()
- done = 0
- }
- return int(done), e
-}
-@(private)
-pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
- curr_off := seek(fd, 0, 1) or_return
- defer seek(fd, curr_off, 0)
-
- buf := data
- if len(buf) > MAX_RW {
- buf = buf[:MAX_RW]
- }
-
- o := win32.OVERLAPPED{
- OffsetHigh = u32(offset>>32),
- Offset = u32(offset),
- }
-
- h := win32.HANDLE(fd)
- done: win32.DWORD
- e: Error
- if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
- e = get_last_error()
- done = 0
- }
- return int(done), e
-}
-
-/*
-read_at returns n: 0, err: 0 on EOF
-*/
-read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
- if offset < 0 {
- return 0, .Invalid_Offset
- }
-
- b, offset := data, offset
- for len(b) > 0 {
- m, e := pread(fd, b, offset)
- if e == ERROR_EOF {
- err = nil
- break
- }
- if e != nil {
- err = e
- break
- }
- n += m
- b = b[m:]
- offset += i64(m)
- }
- return
-}
-
-write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
- if offset < 0 {
- return 0, .Invalid_Offset
- }
-
- b, offset := data, offset
- for len(b) > 0 {
- m := pwrite(fd, b, offset) or_return
- n += m
- b = b[m:]
- offset += i64(m)
- }
- return
-}
-
-
-
-// NOTE(bill): Uses startup to initialize it
-stdin := get_std_handle(uint(win32.STD_INPUT_HANDLE))
-stdout := get_std_handle(uint(win32.STD_OUTPUT_HANDLE))
-stderr := get_std_handle(uint(win32.STD_ERROR_HANDLE))
-
-
-@(require_results)
-get_std_handle :: proc "contextless" (h: uint) -> Handle {
- fd := win32.GetStdHandle(win32.DWORD(h))
- return Handle(fd)
-}
-
-
-exists :: proc(path: string) -> bool {
- runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
- wpath := win32.utf8_to_wstring(path, context.temp_allocator)
- attribs := win32.GetFileAttributesW(wpath)
-
- return attribs != win32.INVALID_FILE_ATTRIBUTES
-}
-
-@(require_results)
-is_file :: proc(path: string) -> bool {
- runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
- wpath := win32.utf8_to_wstring(path, context.temp_allocator)
- attribs := win32.GetFileAttributesW(wpath)
-
- if attribs != win32.INVALID_FILE_ATTRIBUTES {
- return attribs & win32.FILE_ATTRIBUTE_DIRECTORY == 0
- }
- return false
-}
-
-@(require_results)
-is_dir :: proc(path: string) -> bool {
- runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
- wpath := win32.utf8_to_wstring(path, context.temp_allocator)
- attribs := win32.GetFileAttributesW(wpath)
-
- if attribs != win32.INVALID_FILE_ATTRIBUTES {
- return attribs & win32.FILE_ATTRIBUTE_DIRECTORY != 0
- }
- return false
-}
-
-// NOTE(tetra): GetCurrentDirectory is not thread safe with SetCurrentDirectory and GetFullPathName
-@private cwd_lock := win32.SRWLOCK{} // zero is initialized
-
-@(require_results)
-get_current_directory :: proc(allocator := context.allocator) -> string {
- win32.AcquireSRWLockExclusive(&cwd_lock)
-
- runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
-
- sz_utf16 := win32.GetCurrentDirectoryW(0, nil)
- dir_buf_wstr, _ := make([]u16, sz_utf16, context.temp_allocator) // the first time, it _includes_ the NUL.
-
- sz_utf16 = win32.GetCurrentDirectoryW(win32.DWORD(len(dir_buf_wstr)), raw_data(dir_buf_wstr))
- assert(int(sz_utf16)+1 == len(dir_buf_wstr)) // the second time, it _excludes_ the NUL.
-
- win32.ReleaseSRWLockExclusive(&cwd_lock)
-
- return win32.utf16_to_utf8(dir_buf_wstr, allocator) or_else ""
-}
-
-set_current_directory :: proc(path: string) -> (err: Error) {
- runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
- wstr := win32.utf8_to_wstring(path, context.temp_allocator)
-
- win32.AcquireSRWLockExclusive(&cwd_lock)
-
- if !win32.SetCurrentDirectoryW(wstr) {
- err = get_last_error()
- }
-
- win32.ReleaseSRWLockExclusive(&cwd_lock)
-
- return
-}
-change_directory :: set_current_directory
-
-make_directory :: proc(path: string, mode: u32 = 0) -> (err: Error) {
- runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
- // Mode is unused on Windows, but is needed on *nix
- wpath := win32.utf8_to_wstring(path, context.temp_allocator)
-
- if !win32.CreateDirectoryW(wpath, nil) {
- err = get_last_error()
- }
- return
-}
-
-
-remove_directory :: proc(path: string) -> (err: Error) {
- runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
- wpath := win32.utf8_to_wstring(path, context.temp_allocator)
-
- if !win32.RemoveDirectoryW(wpath) {
- err = get_last_error()
- }
- return
-}
-
-
-
-@(private, require_results)
-is_abs :: proc(path: string) -> bool {
- if len(path) > 0 && path[0] == '/' {
- return true
- }
- when ODIN_OS == .Windows {
- if len(path) > 2 {
- switch path[0] {
- case 'A'..='Z', 'a'..='z':
- return path[1] == ':' && is_path_separator(path[2])
- }
- }
- }
- return false
-}
-
-@(private, require_results)
-fix_long_path :: proc(path: string) -> string {
- if len(path) < 248 {
- return path
- }
-
- if len(path) >= 2 && path[:2] == `\\` {
- return path
- }
- if !is_abs(path) {
- return path
- }
-
- prefix :: `\\?`
-
- path_buf, _ := make([]byte, len(prefix)+len(path)+len(`\`), context.temp_allocator)
- copy(path_buf, prefix)
- n := len(path)
- r, w := 0, len(prefix)
- for r < n {
- switch {
- case is_path_separator(path[r]):
- r += 1
- case path[r] == '.' && (r+1 == n || is_path_separator(path[r+1])):
- r += 1
- case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || is_path_separator(path[r+2])):
- return path
- case:
- path_buf[w] = '\\'
- w += 1
- for ; r < n && !is_path_separator(path[r]); r += 1 {
- path_buf[w] = path[r]
- w += 1
- }
- }
- }
-
- if w == len(`\\?\c:`) {
- path_buf[w] = '\\'
- w += 1
- }
- return string(path_buf[:w])
-}
-
-
-link :: proc(old_name, new_name: string) -> (err: Error) {
- runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
- n := win32.utf8_to_wstring(fix_long_path(new_name))
- o := win32.utf8_to_wstring(fix_long_path(old_name))
- return Platform_Error(win32.CreateHardLinkW(n, o, nil))
-}
-
-unlink :: proc(path: string) -> (err: Error) {
- runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
- wpath := win32.utf8_to_wstring(path, context.temp_allocator)
-
- if !win32.DeleteFileW(wpath) {
- err = get_last_error()
- }
- return
-}
-
-
-
-rename :: proc(old_path, new_path: string) -> (err: Error) {
- runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
- from := win32.utf8_to_wstring(old_path, context.temp_allocator)
- to := win32.utf8_to_wstring(new_path, context.temp_allocator)
-
- if !win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) {
- err = get_last_error()
- }
- return
-}
-
-
-ftruncate :: proc(fd: Handle, length: i64) -> (err: Error) {
- curr_off := seek(fd, 0, 1) or_return
- defer seek(fd, curr_off, 0)
- _= seek(fd, length, 0) or_return
- ok := win32.SetEndOfFile(win32.HANDLE(fd))
- if !ok {
- return get_last_error()
- }
- return nil
-}
-
-truncate :: proc(path: string, length: i64) -> (err: Error) {
- fd := open(path, O_WRONLY|O_CREATE, 0o666) or_return
- defer close(fd)
- return ftruncate(fd, length)
-}
-
-
-remove :: proc(name: string) -> Error {
- p := win32.utf8_to_wstring(fix_long_path(name))
- err, err1: win32.DWORD
- if !win32.DeleteFileW(p) {
- err = win32.GetLastError()
- }
- if err == 0 {
- return nil
- }
- if !win32.RemoveDirectoryW(p) {
- err1 = win32.GetLastError()
- }
- if err1 == 0 {
- return nil
- }
-
- if err != err1 {
- a := win32.GetFileAttributesW(p)
- if a == ~u32(0) {
- err = win32.GetLastError()
- } else {
- if a & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
- err = err1
- } else if a & win32.FILE_ATTRIBUTE_READONLY != 0 {
- if win32.SetFileAttributesW(p, a &~ win32.FILE_ATTRIBUTE_READONLY) {
- err = 0
- if !win32.DeleteFileW(p) {
- err = win32.GetLastError()
- }
- }
- }
- }
- }
-
- return Platform_Error(err)
-}
-
-
-@(require_results)
-pipe :: proc() -> (r, w: Handle, err: Error) {
- sa: win32.SECURITY_ATTRIBUTES
- sa.nLength = size_of(win32.SECURITY_ATTRIBUTES)
- sa.bInheritHandle = true
- if !win32.CreatePipe((^win32.HANDLE)(&r), (^win32.HANDLE)(&w), &sa, 0) {
- err = get_last_error()
- }
- return
-}
-
diff --git a/core/os/os2/dir.odin b/core/os/os2/dir.odin
index a41ef68f9..9e6eaab72 100644
--- a/core/os/os2/dir.odin
+++ b/core/os/os2/dir.odin
@@ -20,7 +20,7 @@ read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (files
TEMP_ALLOCATOR_GUARD()
- it := read_directory_iterator_create(f) or_return
+ it := read_directory_iterator_create(f)
defer _read_directory_iterator_destroy(&it)
dfi := make([dynamic]File_Info, 0, size, temp_allocator())
@@ -34,9 +34,14 @@ read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (files
if n > 0 && index == n {
break
}
+
+ _ = read_directory_iterator_error(&it) or_break
+
append(&dfi, file_info_clone(fi, allocator) or_return)
}
+ _ = read_directory_iterator_error(&it) or_return
+
return slice.clone(dfi[:], allocator)
}
@@ -61,22 +66,156 @@ read_all_directory_by_path :: proc(path: string, allocator: runtime.Allocator) -
Read_Directory_Iterator :: struct {
- f: ^File,
+ f: ^File,
+ err: struct {
+ err: Error,
+ path: [dynamic]byte,
+ },
+ index: int,
impl: Read_Directory_Iterator_Impl,
}
+/*
+Creates a directory iterator with the given directory.
-@(require_results)
-read_directory_iterator_create :: proc(f: ^File) -> (Read_Directory_Iterator, Error) {
- return _read_directory_iterator_create(f)
+For an example on how to use the iterator, see `read_directory_iterator`.
+*/
+read_directory_iterator_create :: proc(f: ^File) -> (it: Read_Directory_Iterator) {
+ read_directory_iterator_init(&it, f)
+ return
}
+/*
+Initialize a directory iterator with the given directory.
+
+This procedure may be called on an existing iterator to reuse it for another directory.
+
+For an example on how to use the iterator, see `read_directory_iterator`.
+*/
+read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) {
+ it.err.err = nil
+ it.err.path.allocator = file_allocator()
+ clear(&it.err.path)
+
+ it.f = f
+ it.index = 0
+
+ _read_directory_iterator_init(it, f)
+}
+
+/*
+Destroys a directory iterator.
+*/
read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
+ if it == nil {
+ return
+ }
+
+ delete(it.err.path)
+
_read_directory_iterator_destroy(it)
}
-// NOTE(bill): `File_Info` does not need to deleted on each iteration. Any copies must be manually copied with `file_info_clone`
+/*
+Retrieve the last error that happened during iteration.
+*/
+@(require_results)
+read_directory_iterator_error :: proc(it: ^Read_Directory_Iterator) -> (path: string, err: Error) {
+ return string(it.err.path[:]), it.err.err
+}
+
+@(private)
+read_directory_iterator_set_error :: proc(it: ^Read_Directory_Iterator, path: string, err: Error) {
+ if err == nil {
+ return
+ }
+
+ resize(&it.err.path, len(path))
+ copy(it.err.path[:], path)
+
+ it.err.err = err
+}
+
+/*
+Returns the next file info entry for the iterator's directory.
+
+The given `File_Info` is reused in subsequent calls so a copy (`file_info_clone`) has to be made to
+extend its lifetime.
+
+Example:
+ package main
+
+ import "core:fmt"
+ import os "core:os/os2"
+
+ main :: proc() {
+ f, oerr := os.open("core")
+ ensure(oerr == nil)
+ defer os.close(f)
+
+ it := os.read_directory_iterator_create(f)
+ defer os.read_directory_iterator_destroy(&it)
+
+ for info in os.read_directory_iterator(&it) {
+ // Optionally break on the first error:
+ // Supports not doing this, and keeping it going with remaining items.
+ // _ = os.read_directory_iterator_error(&it) or_break
+
+ // Handle error as we go:
+ // Again, no need to do this as it will keep going with remaining items.
+ if path, err := os.read_directory_iterator_error(&it); err != nil {
+ fmt.eprintfln("failed reading %s: %s", path, err)
+ continue
+ }
+
+ // Or, do not handle errors during iteration, and just check the error at the end.
+
+
+ fmt.printfln("%#v", info)
+ }
+
+ // Handle error if one happened during iteration at the end:
+ if path, err := os.read_directory_iterator_error(&it); err != nil {
+ fmt.eprintfln("read directory failed at %s: %s", path, err)
+ }
+ }
+*/
@(require_results)
read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
+ if it.f == nil {
+ return
+ }
+
+ if it.index == 0 && it.err.err != nil {
+ return
+ }
+
return _read_directory_iterator(it)
}
+
+// Recursively copies a directory to `dst` from `src`
+copy_directory :: proc(dst, src: string, dst_perm := 0o755) -> Error {
+ switch err := make_directory_all(dst, dst_perm); err {
+ case nil, .Exist:
+ // okay
+ case:
+ return err
+ }
+
+ TEMP_ALLOCATOR_GUARD()
+
+ file_infos := read_all_directory_by_path(src, temp_allocator()) or_return
+ for fi in file_infos {
+ TEMP_ALLOCATOR_GUARD()
+
+ dst_path := join_path({dst, fi.name}, temp_allocator()) or_return
+ src_path := fi.fullpath
+
+ if fi.type == .Directory {
+ copy_directory(dst_path, src_path) or_return
+ } else {
+ copy_file(dst_path, src_path) or_return
+ }
+ }
+ return nil
+}
\ No newline at end of file
diff --git a/core/os/os2/dir_linux.odin b/core/os/os2/dir_linux.odin
index f26b4fc79..a868a02c4 100644
--- a/core/os/os2/dir_linux.odin
+++ b/core/os/os2/dir_linux.odin
@@ -1,20 +1,119 @@
#+private
package os2
+import "core:sys/linux"
+
Read_Directory_Iterator_Impl :: struct {
-
+ prev_fi: File_Info,
+ dirent_backing: []u8,
+ dirent_buflen: int,
+ dirent_off: int,
}
-
@(require_results)
_read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
+ scan_entries :: proc(it: ^Read_Directory_Iterator, dfd: linux.Fd, entries: []u8, offset: ^int) -> (fd: linux.Fd, file_name: string) {
+ for d in linux.dirent_iterate_buf(entries, offset) {
+ file_name = linux.dirent_name(d)
+ if file_name == "." || file_name == ".." {
+ continue
+ }
+
+ file_name_cstr := cstring(raw_data(file_name))
+ entry_fd, errno := linux.openat(dfd, file_name_cstr, {.NOFOLLOW, .PATH})
+ if errno == .NONE {
+ return entry_fd, file_name
+ } else {
+ read_directory_iterator_set_error(it, file_name, _get_platform_error(errno))
+ }
+ }
+
+ return -1, ""
+ }
+
+ index = it.index
+ it.index += 1
+
+ dfd := linux.Fd(_fd(it.f))
+
+ entries := it.impl.dirent_backing[:it.impl.dirent_buflen]
+ entry_fd, file_name := scan_entries(it, dfd, entries, &it.impl.dirent_off)
+
+ for entry_fd == -1 {
+ if len(it.impl.dirent_backing) == 0 {
+ it.impl.dirent_backing = make([]u8, 512, file_allocator())
+ }
+
+ loop: for {
+ buflen, errno := linux.getdents(linux.Fd(dfd), it.impl.dirent_backing[:])
+ #partial switch errno {
+ case .EINVAL:
+ delete(it.impl.dirent_backing, file_allocator())
+ n := len(it.impl.dirent_backing) * 2
+ it.impl.dirent_backing = make([]u8, n, file_allocator())
+ continue
+ case .NONE:
+ if buflen == 0 {
+ return
+ }
+ it.impl.dirent_off = 0
+ it.impl.dirent_buflen = buflen
+ entries = it.impl.dirent_backing[:buflen]
+ break loop
+ case:
+ read_directory_iterator_set_error(it, name(it.f), _get_platform_error(errno))
+ return
+ }
+ }
+
+ entry_fd, file_name = scan_entries(it, dfd, entries, &it.impl.dirent_off)
+ }
+ defer linux.close(entry_fd)
+
+ // PERF: reuse the fullpath string like on posix and wasi.
+ file_info_delete(it.impl.prev_fi, file_allocator())
+
+ err: Error
+ fi, err = _fstat_internal(entry_fd, file_allocator())
+ it.impl.prev_fi = fi
+
+ if err != nil {
+ path, _ := _get_full_path(entry_fd, temp_allocator())
+ read_directory_iterator_set_error(it, path, err)
+ }
+
+ ok = true
return
}
-@(require_results)
-_read_directory_iterator_create :: proc(f: ^File) -> (Read_Directory_Iterator, Error) {
- return {}, .Unsupported
+_read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) {
+ // NOTE: Allow calling `init` to target a new directory with the same iterator.
+ it.impl.dirent_buflen = 0
+ it.impl.dirent_off = 0
+
+ if f == nil || f.impl == nil {
+ read_directory_iterator_set_error(it, "", .Invalid_File)
+ return
+ }
+
+ stat: linux.Stat
+ errno := linux.fstat(linux.Fd(fd(f)), &stat)
+ if errno != .NONE {
+ read_directory_iterator_set_error(it, name(f), _get_platform_error(errno))
+ return
+ }
+
+ if (stat.mode & linux.S_IFMT) != linux.S_IFDIR {
+ read_directory_iterator_set_error(it, name(f), .Invalid_Dir)
+ return
+ }
}
_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
+ if it == nil {
+ return
+ }
+
+ delete(it.impl.dirent_backing, file_allocator())
+ file_info_delete(it.impl.prev_fi, file_allocator())
}
diff --git a/core/os/os2/dir_posix.odin b/core/os/os2/dir_posix.odin
index 14fddde50..d9fa16f8d 100644
--- a/core/os/os2/dir_posix.odin
+++ b/core/os/os2/dir_posix.odin
@@ -6,7 +6,6 @@ import "core:sys/posix"
Read_Directory_Iterator_Impl :: struct {
dir: posix.DIR,
- idx: int,
fullpath: [dynamic]byte,
}
@@ -14,14 +13,16 @@ Read_Directory_Iterator_Impl :: struct {
_read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
fimpl := (^File_Impl)(it.f.impl)
- index = it.impl.idx
- it.impl.idx += 1
+ index = it.index
+ it.index += 1
for {
+ posix.set_errno(nil)
entry := posix.readdir(it.impl.dir)
if entry == nil {
- // NOTE(laytan): would be good to have an `error` field on the `Read_Directory_Iterator`
- // There isn't a way to now know if it failed or if we are at the end.
+ if errno := posix.errno(); errno != nil {
+ read_directory_iterator_set_error(it, name(it.f), _get_platform_error(errno))
+ }
return
}
@@ -31,16 +32,20 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
}
sname := string(cname)
- stat: posix.stat_t
- if posix.fstatat(posix.dirfd(it.impl.dir), cname, &stat, { .SYMLINK_NOFOLLOW }) != .OK {
- // NOTE(laytan): would be good to have an `error` field on the `Read_Directory_Iterator`
- // There isn't a way to now know if it failed or if we are at the end.
+ n := len(fimpl.name)+1
+ if err := non_zero_resize(&it.impl.fullpath, n+len(sname)); err != nil {
+ read_directory_iterator_set_error(it, sname, err)
+ ok = true
return
}
+ copy(it.impl.fullpath[n:], sname)
- n := len(fimpl.name)+1
- non_zero_resize(&it.impl.fullpath, n+len(sname))
- n += copy(it.impl.fullpath[n:], sname)
+ stat: posix.stat_t
+ if posix.fstatat(posix.dirfd(it.impl.dir), cname, &stat, { .SYMLINK_NOFOLLOW }) != .OK {
+ read_directory_iterator_set_error(it, string(it.impl.fullpath[:]), _get_platform_error())
+ ok = true
+ return
+ }
fi = internal_stat(stat, string(it.impl.fullpath[:]))
ok = true
@@ -48,34 +53,41 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
}
}
-@(require_results)
-_read_directory_iterator_create :: proc(f: ^File) -> (iter: Read_Directory_Iterator, err: Error) {
+_read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) {
if f == nil || f.impl == nil {
- err = .Invalid_File
+ read_directory_iterator_set_error(it, "", .Invalid_File)
return
}
impl := (^File_Impl)(f.impl)
- iter.f = f
- iter.impl.idx = 0
+ // NOTE: Allow calling `init` to target a new directory with the same iterator.
+ it.impl.fullpath.allocator = file_allocator()
+ clear(&it.impl.fullpath)
+ if err := reserve(&it.impl.fullpath, len(impl.name)+128); err != nil {
+ read_directory_iterator_set_error(it, name(f), err)
+ return
+ }
- iter.impl.fullpath.allocator = file_allocator()
- append(&iter.impl.fullpath, impl.name)
- append(&iter.impl.fullpath, "/")
- defer if err != nil { delete(iter.impl.fullpath) }
+ append(&it.impl.fullpath, impl.name)
+ append(&it.impl.fullpath, "/")
// `fdopendir` consumes the file descriptor so we need to `dup` it.
dupfd := posix.dup(impl.fd)
if dupfd == -1 {
- err = _get_platform_error()
+ read_directory_iterator_set_error(it, name(f), _get_platform_error())
return
}
- defer if err != nil { posix.close(dupfd) }
+ defer if it.err.err != nil { posix.close(dupfd) }
- iter.impl.dir = posix.fdopendir(dupfd)
- if iter.impl.dir == nil {
- err = _get_platform_error()
+ // NOTE: Allow calling `init` to target a new directory with the same iterator.
+ if it.impl.dir != nil {
+ posix.closedir(it.impl.dir)
+ }
+
+ it.impl.dir = posix.fdopendir(dupfd)
+ if it.impl.dir == nil {
+ read_directory_iterator_set_error(it, name(f), _get_platform_error())
return
}
@@ -83,7 +95,7 @@ _read_directory_iterator_create :: proc(f: ^File) -> (iter: Read_Directory_Itera
}
_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
- if it == nil || it.impl.dir == nil {
+ if it.impl.dir == nil {
return
}
diff --git a/core/os/os2/dir_walker.odin b/core/os/os2/dir_walker.odin
new file mode 100644
index 000000000..0af751f31
--- /dev/null
+++ b/core/os/os2/dir_walker.odin
@@ -0,0 +1,230 @@
+package os2
+
+import "core:container/queue"
+
+/*
+A recursive directory walker.
+
+Note that none of the fields should be accessed directly.
+*/
+Walker :: struct {
+ todo: queue.Queue(string),
+ skip_dir: bool,
+ err: struct {
+ path: [dynamic]byte,
+ err: Error,
+ },
+ iter: Read_Directory_Iterator,
+}
+
+walker_init_path :: proc(w: ^Walker, path: string) {
+ cloned_path, err := clone_string(path, file_allocator())
+ if err != nil {
+ walker_set_error(w, path, err)
+ return
+ }
+
+ walker_clear(w)
+
+ if _, err = queue.push(&w.todo, cloned_path); err != nil {
+ walker_set_error(w, cloned_path, err)
+ return
+ }
+}
+
+walker_init_file :: proc(w: ^Walker, f: ^File) {
+ handle, err := clone(f)
+ if err != nil {
+ path, _ := clone_string(name(f), file_allocator())
+ walker_set_error(w, path, err)
+ return
+ }
+
+ walker_clear(w)
+
+ read_directory_iterator_init(&w.iter, handle)
+}
+
+/*
+Initializes a walker, either using a path or a file pointer to a directory the walker will start at.
+
+You are allowed to repeatedly call this to reuse it for later walks.
+
+For an example on how to use the walker, see `walker_walk`.
+*/
+walker_init :: proc {
+ walker_init_path,
+ walker_init_file,
+}
+
+@(require_results)
+walker_create_path :: proc(path: string) -> (w: Walker) {
+ walker_init_path(&w, path)
+ return
+}
+
+@(require_results)
+walker_create_file :: proc(f: ^File) -> (w: Walker) {
+ walker_init_file(&w, f)
+ return
+}
+
+/*
+Creates a walker, either using a path or a file pointer to a directory the walker will start at.
+
+For an example on how to use the walker, see `walker_walk`.
+*/
+walker_create :: proc {
+ walker_create_path,
+ walker_create_file,
+}
+
+/*
+Returns the last error that occurred during the walker's operations.
+
+Can be called while iterating, or only at the end to check if anything failed.
+*/
+@(require_results)
+walker_error :: proc(w: ^Walker) -> (path: string, err: Error) {
+ return string(w.err.path[:]), w.err.err
+}
+
+@(private)
+walker_set_error :: proc(w: ^Walker, path: string, err: Error) {
+ if err == nil {
+ return
+ }
+
+ resize(&w.err.path, len(path))
+ copy(w.err.path[:], path)
+
+ w.err.err = err
+}
+
+@(private)
+walker_clear :: proc(w: ^Walker) {
+ w.iter.f = nil
+ w.skip_dir = false
+
+ w.err.path.allocator = file_allocator()
+ clear(&w.err.path)
+
+ w.todo.data.allocator = file_allocator()
+ for path in queue.pop_front_safe(&w.todo) {
+ delete(path, file_allocator())
+ }
+}
+
+walker_destroy :: proc(w: ^Walker) {
+ walker_clear(w)
+ queue.destroy(&w.todo)
+ delete(w.err.path)
+ read_directory_iterator_destroy(&w.iter)
+}
+
+// Marks the current directory to be skipped (not entered into).
+walker_skip_dir :: proc(w: ^Walker) {
+ w.skip_dir = true
+}
+
+/*
+Returns the next file info in the iterator, files are iterated in breadth-first order.
+
+If an error occurred opening a directory, you may get zero'd info struct and
+`walker_error` will return the error.
+
+Example:
+ package main
+
+ import "core:fmt"
+ import "core:strings"
+ import os "core:os/os2"
+
+ main :: proc() {
+ w := os.walker_create("core")
+ defer os.walker_destroy(&w)
+
+ for info in os.walker_walk(&w) {
+ // Optionally break on the first error:
+ // _ = walker_error(&w) or_break
+
+ // Or, handle error as we go:
+ if path, err := os.walker_error(&w); err != nil {
+ fmt.eprintfln("failed walking %s: %s", path, err)
+ continue
+ }
+
+ // Or, do not handle errors during iteration, and just check the error at the end.
+
+
+
+ // Skip a directory:
+ if strings.has_suffix(info.fullpath, ".git") {
+ os.walker_skip_dir(&w)
+ continue
+ }
+
+ fmt.printfln("%#v", info)
+ }
+
+ // Handle error if one happened during iteration at the end:
+ if path, err := os.walker_error(&w); err != nil {
+ fmt.eprintfln("failed walking %s: %v", path, err)
+ }
+ }
+*/
+@(require_results)
+walker_walk :: proc(w: ^Walker) -> (fi: File_Info, ok: bool) {
+ if w.skip_dir {
+ w.skip_dir = false
+ if skip, sok := queue.pop_back_safe(&w.todo); sok {
+ delete(skip, file_allocator())
+ }
+ }
+
+ if w.iter.f == nil {
+ if queue.len(w.todo) == 0 {
+ return
+ }
+
+ next := queue.pop_front(&w.todo)
+
+ handle, err := open(next)
+ if err != nil {
+ walker_set_error(w, next, err)
+ return {}, true
+ }
+
+ read_directory_iterator_init(&w.iter, handle)
+
+ delete(next, file_allocator())
+ }
+
+ info, _, iter_ok := read_directory_iterator(&w.iter)
+
+ if path, err := read_directory_iterator_error(&w.iter); err != nil {
+ walker_set_error(w, path, err)
+ }
+
+ if !iter_ok {
+ close(w.iter.f)
+ w.iter.f = nil
+ return walker_walk(w)
+ }
+
+ if info.type == .Directory {
+ path, err := clone_string(info.fullpath, file_allocator())
+ if err != nil {
+ walker_set_error(w, "", err)
+ return
+ }
+
+ _, err = queue.push_back(&w.todo, path)
+ if err != nil {
+ walker_set_error(w, path, err)
+ return
+ }
+ }
+
+ return info, iter_ok
+}
diff --git a/core/os/os2/dir_wasi.odin b/core/os/os2/dir_wasi.odin
new file mode 100644
index 000000000..61c005674
--- /dev/null
+++ b/core/os/os2/dir_wasi.odin
@@ -0,0 +1,124 @@
+#+private
+package os2
+
+import "base:runtime"
+import "core:slice"
+import "base:intrinsics"
+import "core:sys/wasm/wasi"
+
+Read_Directory_Iterator_Impl :: struct {
+ fullpath: [dynamic]byte,
+ buf: []byte,
+ off: int,
+}
+
+@(require_results)
+_read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
+ fimpl := (^File_Impl)(it.f.impl)
+
+ buf := it.impl.buf[it.impl.off:]
+
+ index = it.index
+ it.index += 1
+
+ for {
+ if len(buf) < size_of(wasi.dirent_t) {
+ return
+ }
+
+ entry := intrinsics.unaligned_load((^wasi.dirent_t)(raw_data(buf)))
+ buf = buf[size_of(wasi.dirent_t):]
+
+ assert(len(buf) < int(entry.d_namlen))
+
+ name := string(buf[:entry.d_namlen])
+ buf = buf[entry.d_namlen:]
+ it.impl.off += size_of(wasi.dirent_t) + int(entry.d_namlen)
+
+ if name == "." || name == ".." {
+ continue
+ }
+
+ n := len(fimpl.name)+1
+ if alloc_err := non_zero_resize(&it.impl.fullpath, n+len(name)); alloc_err != nil {
+ read_directory_iterator_set_error(it, name, alloc_err)
+ ok = true
+ return
+ }
+ copy(it.impl.fullpath[n:], name)
+
+ stat, err := wasi.path_filestat_get(__fd(it.f), {}, name)
+ if err != nil {
+ // Can't stat, fill what we have from dirent.
+ stat = {
+ ino = entry.d_ino,
+ filetype = entry.d_type,
+ }
+ read_directory_iterator_set_error(it, string(it.impl.fullpath[:]), _get_platform_error(err))
+ }
+
+ fi = internal_stat(stat, string(it.impl.fullpath[:]))
+ ok = true
+ return
+ }
+}
+
+_read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) {
+ // NOTE: Allow calling `init` to target a new directory with the same iterator.
+ it.impl.off = 0
+
+ if f == nil || f.impl == nil {
+ read_directory_iterator_set_error(it, "", .Invalid_File)
+ return
+ }
+
+ impl := (^File_Impl)(f.impl)
+
+ buf: [dynamic]byte
+ // NOTE: Allow calling `init` to target a new directory with the same iterator.
+ if it.impl.buf != nil {
+ buf = slice.into_dynamic(it.impl.buf)
+ }
+ buf.allocator = file_allocator()
+
+ defer if it.err.err != nil { delete(buf) }
+
+ for {
+ if err := non_zero_resize(&buf, 512 if len(buf) == 0 else len(buf)*2); err != nil {
+ read_directory_iterator_set_error(it, name(f), err)
+ return
+ }
+
+ n, err := wasi.fd_readdir(__fd(f), buf[:], 0)
+ if err != nil {
+ read_directory_iterator_set_error(it, name(f), _get_platform_error(err))
+ return
+ }
+
+ if n < len(buf) {
+ non_zero_resize(&buf, n)
+ break
+ }
+
+ assert(n == len(buf))
+ }
+ it.impl.buf = buf[:]
+
+ // NOTE: Allow calling `init` to target a new directory with the same iterator.
+ it.impl.fullpath.allocator = file_allocator()
+ clear(&it.impl.fullpath)
+ if err := reserve(&it.impl.fullpath, len(impl.name)+128); err != nil {
+ read_directory_iterator_set_error(it, name(f), err)
+ return
+ }
+
+ append(&it.impl.fullpath, impl.name)
+ append(&it.impl.fullpath, "/")
+
+ return
+}
+
+_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
+ delete(it.impl.buf, file_allocator())
+ delete(it.impl.fullpath)
+}
diff --git a/core/os/os2/dir_windows.odin b/core/os/os2/dir_windows.odin
index f71e7e763..d592e8036 100644
--- a/core/os/os2/dir_windows.odin
+++ b/core/os/os2/dir_windows.odin
@@ -44,16 +44,11 @@ Read_Directory_Iterator_Impl :: struct {
path: string,
prev_fi: File_Info,
no_more_files: bool,
- index: int,
}
@(require_results)
_read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
- if it.f == nil {
- return
- }
-
TEMP_ALLOCATOR_GUARD()
for !it.impl.no_more_files {
@@ -63,19 +58,21 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
fi, err = find_data_to_file_info(it.impl.path, &it.impl.find_data, file_allocator())
if err != nil {
+ read_directory_iterator_set_error(it, it.impl.path, err)
return
}
+
if fi.name != "" {
it.impl.prev_fi = fi
ok = true
- index = it.impl.index
- it.impl.index += 1
+ index = it.index
+ it.index += 1
}
if !win32.FindNextFileW(it.impl.find_handle, &it.impl.find_data) {
e := _get_platform_error()
- if pe, _ := is_platform_error(e); pe == i32(win32.ERROR_NO_MORE_FILES) {
- it.impl.no_more_files = true
+ if pe, _ := is_platform_error(e); pe != i32(win32.ERROR_NO_MORE_FILES) {
+ read_directory_iterator_set_error(it, it.impl.path, e)
}
it.impl.no_more_files = true
}
@@ -86,16 +83,27 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
return
}
-@(require_results)
-_read_directory_iterator_create :: proc(f: ^File) -> (it: Read_Directory_Iterator, err: Error) {
- if f == nil {
+_read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) {
+ it.impl.no_more_files = false
+
+ if f == nil || f.impl == nil {
+ read_directory_iterator_set_error(it, "", .Invalid_File)
return
}
+
it.f = f
impl := (^File_Impl)(f.impl)
+ // NOTE: Allow calling `init` to target a new directory with the same iterator - reset idx.
+ if it.impl.find_handle != nil {
+ win32.FindClose(it.impl.find_handle)
+ }
+ if it.impl.path != "" {
+ delete(it.impl.path, file_allocator())
+ }
+
if !is_directory(impl.name) {
- err = .Invalid_Dir
+ read_directory_iterator_set_error(it, impl.name, .Invalid_Dir)
return
}
@@ -118,14 +126,19 @@ _read_directory_iterator_create :: proc(f: ^File) -> (it: Read_Directory_Iterato
it.impl.find_handle = win32.FindFirstFileW(raw_data(wpath_search), &it.impl.find_data)
if it.impl.find_handle == win32.INVALID_HANDLE_VALUE {
- err = _get_platform_error()
+ read_directory_iterator_set_error(it, impl.name, _get_platform_error())
return
}
- defer if err != nil {
+ defer if it.err.err != nil {
win32.FindClose(it.impl.find_handle)
}
- it.impl.path = _cleanpath_from_buf(wpath, file_allocator()) or_return
+ err: Error
+ it.impl.path, err = _cleanpath_from_buf(wpath, file_allocator())
+ if err != nil {
+ read_directory_iterator_set_error(it, impl.name, err)
+ }
+
return
}
diff --git a/core/os/os2/env.odin b/core/os/os2/env.odin
index c8d39b270..13c107f3c 100644
--- a/core/os/os2/env.odin
+++ b/core/os/os2/env.odin
@@ -22,8 +22,8 @@ lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string,
}
// set_env sets the value of the environment variable named by the key
-// Returns true on success, false on failure
-set_env :: proc(key, value: string) -> bool {
+// Returns Error on failure
+set_env :: proc(key, value: string) -> Error {
return _set_env(key, value)
}
@@ -41,7 +41,7 @@ clear_env :: proc() {
// environ returns a copy of strings representing the environment, in the form "key=value"
// NOTE: the slice of strings and the strings with be allocated using the supplied allocator
@(require_results)
-environ :: proc(allocator: runtime.Allocator) -> []string {
+environ :: proc(allocator: runtime.Allocator) -> ([]string, Error) {
return _environ(allocator)
}
diff --git a/core/os/os2/env_linux.odin b/core/os/os2/env_linux.odin
index c248a323c..2ed43dd91 100644
--- a/core/os/os2/env_linux.odin
+++ b/core/os/os2/env_linux.odin
@@ -20,19 +20,18 @@ NOT_FOUND :: -1
// the environment is a 0 delimited list of = strings
_env: [dynamic]string
-_env_mutex: sync.Mutex
+_env_mutex: sync.Recursive_Mutex
// We need to be able to figure out if the environment variable
// is contained in the original environment or not. This also
// serves as a flag to determine if we have built _env.
-_org_env_begin: uintptr
-_org_env_end: uintptr
+_org_env_begin: uintptr // atomic
+_org_env_end: uintptr // guarded by _env_mutex
// Returns value + index location into _env
// or -1 if not found
_lookup :: proc(key: string) -> (value: string, idx: int) {
- sync.mutex_lock(&_env_mutex)
- defer sync.mutex_unlock(&_env_mutex)
+ sync.guard(&_env_mutex)
for entry, i in _env {
if k, v := _kv_from_entry(entry); k == key {
@@ -43,7 +42,7 @@ _lookup :: proc(key: string) -> (value: string, idx: int) {
}
_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
- if _org_env_begin == 0 {
+ if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
_build_env()
}
@@ -54,19 +53,18 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string
return
}
-_set_env :: proc(key, v_new: string) -> bool {
- if _org_env_begin == 0 {
+_set_env :: proc(key, v_new: string) -> Error {
+ if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
_build_env()
}
+ sync.guard(&_env_mutex)
// all key values are stored as "key=value\x00"
kv_size := len(key) + len(v_new) + 2
if v_curr, idx := _lookup(key); idx != NOT_FOUND {
if v_curr == v_new {
- return true
+ return nil
}
- sync.mutex_lock(&_env_mutex)
- defer sync.mutex_unlock(&_env_mutex)
unordered_remove(&_env, idx)
@@ -76,9 +74,9 @@ _set_env :: proc(key, v_new: string) -> bool {
// wasn't in the environment in the first place.
k_addr, v_addr := _kv_addr_from_val(v_curr, key)
if len(v_new) > len(v_curr) {
- k_addr = ([^]u8)(heap_resize(k_addr, kv_size))
+ k_addr = ([^]u8)(runtime.heap_resize(k_addr, kv_size))
if k_addr == nil {
- return false
+ return .Out_Of_Memory
}
v_addr = &k_addr[len(key) + 1]
}
@@ -86,13 +84,13 @@ _set_env :: proc(key, v_new: string) -> bool {
v_addr[len(v_new)] = 0
append(&_env, string(k_addr[:kv_size]))
- return true
+ return nil
}
}
- k_addr := ([^]u8)(heap_alloc(kv_size))
+ k_addr := ([^]u8)(runtime.heap_alloc(kv_size))
if k_addr == nil {
- return false
+ return .Out_Of_Memory
}
intrinsics.mem_copy_non_overlapping(k_addr, raw_data(key), len(key))
k_addr[len(key)] = '='
@@ -101,16 +99,15 @@ _set_env :: proc(key, v_new: string) -> bool {
intrinsics.mem_copy_non_overlapping(&val_slice[0], raw_data(v_new), len(v_new))
val_slice[len(v_new)] = 0
- sync.mutex_lock(&_env_mutex)
append(&_env, string(k_addr[:kv_size - 1]))
- sync.mutex_unlock(&_env_mutex)
- return true
+ return nil
}
_unset_env :: proc(key: string) -> bool {
- if _org_env_begin == 0 {
+ if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
_build_env()
}
+ sync.guard(&_env_mutex)
v: string
i: int
@@ -118,55 +115,60 @@ _unset_env :: proc(key: string) -> bool {
return false
}
- sync.mutex_lock(&_env_mutex)
unordered_remove(&_env, i)
- sync.mutex_unlock(&_env_mutex)
if _is_in_org_env(v) {
return true
}
- // if we got this far, the envrionment variable
+ // if we got this far, the environment variable
// existed AND was allocated by us.
k_addr, _ := _kv_addr_from_val(v, key)
- heap_free(k_addr)
+ runtime.heap_free(k_addr)
return true
}
_clear_env :: proc() {
- sync.mutex_lock(&_env_mutex)
- defer sync.mutex_unlock(&_env_mutex)
+ sync.guard(&_env_mutex)
for kv in _env {
if !_is_in_org_env(kv) {
- heap_free(raw_data(kv))
+ runtime.heap_free(raw_data(kv))
}
}
clear(&_env)
// nothing resides in the original environment either
- _org_env_begin = ~uintptr(0)
+ intrinsics.atomic_store_explicit(&_org_env_begin, ~uintptr(0), .Release)
_org_env_end = ~uintptr(0)
}
-_environ :: proc(allocator: runtime.Allocator) -> []string {
- if _org_env_begin == 0 {
+_environ :: proc(allocator: runtime.Allocator) -> (environ: []string, err: Error) {
+ if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
_build_env()
}
- env := make([]string, len(_env), allocator)
+ sync.guard(&_env_mutex)
- sync.mutex_lock(&_env_mutex)
- defer sync.mutex_unlock(&_env_mutex)
- for entry, i in _env {
- env[i], _ = clone_string(entry, allocator)
+ env := make([dynamic]string, 0, len(_env), allocator) or_return
+ defer if err != nil {
+ for e in env {
+ delete(e, allocator)
+ }
+ delete(env)
}
- return env
+
+ for entry in _env {
+ s := clone_string(entry, allocator) or_return
+ append(&env, s)
+ }
+ environ = env[:]
+ return
}
// The entire environment is stored as 0 terminated strings,
// so there is no need to clone/free individual variables
export_cstring_environment :: proc(allocator: runtime.Allocator) -> []cstring {
- if _org_env_begin == 0 {
+ if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
// The environment has not been modified, so we can just
// send the original environment
org_env := _get_original_env()
@@ -174,12 +176,11 @@ export_cstring_environment :: proc(allocator: runtime.Allocator) -> []cstring {
for ; org_env[n] != nil; n += 1 {}
return slice.clone(org_env[:n + 1], allocator)
}
+ sync.guard(&_env_mutex)
// NOTE: already terminated by nil pointer via + 1
env := make([]cstring, len(_env) + 1, allocator)
- sync.mutex_lock(&_env_mutex)
- defer sync.mutex_unlock(&_env_mutex)
for entry, i in _env {
env[i] = cstring(raw_data(entry))
}
@@ -187,15 +188,14 @@ export_cstring_environment :: proc(allocator: runtime.Allocator) -> []cstring {
}
_build_env :: proc() {
- sync.mutex_lock(&_env_mutex)
- defer sync.mutex_unlock(&_env_mutex)
- if _org_env_begin != 0 {
+ sync.guard(&_env_mutex)
+ if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) != 0 {
return
}
- _env = make(type_of(_env), heap_allocator())
+ _env = make(type_of(_env), runtime.heap_allocator())
cstring_env := _get_original_env()
- _org_env_begin = uintptr(rawptr(cstring_env[0]))
+ intrinsics.atomic_store_explicit(&_org_env_begin, uintptr(rawptr(cstring_env[0])), .Release)
for i := 0; cstring_env[i] != nil; i += 1 {
bytes := ([^]u8)(cstring_env[i])
n := len(cstring_env[i])
@@ -227,5 +227,5 @@ _kv_addr_from_val :: #force_inline proc(val: string, key: string) -> ([^]u8, [^]
_is_in_org_env :: #force_inline proc(env_data: string) -> bool {
addr := uintptr(raw_data(env_data))
- return addr >= _org_env_begin && addr < _org_env_end
+ return addr >= intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) && addr < _org_env_end
}
diff --git a/core/os/os2/env_posix.odin b/core/os/os2/env_posix.odin
index e2080485d..9661768b4 100644
--- a/core/os/os2/env_posix.odin
+++ b/core/os/os2/env_posix.odin
@@ -26,13 +26,15 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string
return
}
-_set_env :: proc(key, value: string) -> (ok: bool) {
+_set_env :: proc(key, value: string) -> (err: Error) {
TEMP_ALLOCATOR_GUARD()
- ckey := strings.clone_to_cstring(key, temp_allocator())
- cval := strings.clone_to_cstring(key, temp_allocator())
+ ckey := strings.clone_to_cstring(key, temp_allocator()) or_return
+ cval := strings.clone_to_cstring(key, temp_allocator()) or_return
- ok = posix.setenv(ckey, cval, true) == .OK
+ if posix.setenv(ckey, cval, true) != nil {
+ err = _get_platform_error_from_errno()
+ }
return
}
@@ -54,23 +56,23 @@ _clear_env :: proc() {
}
}
-_environ :: proc(allocator: runtime.Allocator) -> (environ: []string) {
- n := 0
+_environ :: proc(allocator: runtime.Allocator) -> (environ: []string, err: Error) {
+ n := 0
for entry := posix.environ[0]; entry != nil; n, entry = n+1, posix.environ[n] {}
- err: runtime.Allocator_Error
- if environ, err = make([]string, n, allocator); err != nil {
- // NOTE(laytan): is the environment empty or did allocation fail, how does the user know?
- return
+ r := make([dynamic]string, 0, n, allocator) or_return
+ defer if err != nil {
+ for e in r {
+ delete(e, allocator)
+ }
+ delete(r)
}
for i, entry := 0, posix.environ[0]; entry != nil; i, entry = i+1, posix.environ[i] {
- if environ[i], err = strings.clone(string(entry), allocator); err != nil {
- // NOTE(laytan): is the entire environment returned or did allocation fail, how does the user know?
- return
- }
+ append(&r, strings.clone(string(entry), allocator) or_return)
}
+ environ = r[:]
return
}
diff --git a/core/os/os2/env_wasi.odin b/core/os/os2/env_wasi.odin
new file mode 100644
index 000000000..305192c92
--- /dev/null
+++ b/core/os/os2/env_wasi.odin
@@ -0,0 +1,159 @@
+#+private
+package os2
+
+import "base:runtime"
+
+import "core:strings"
+import "core:sync"
+import "core:sys/wasm/wasi"
+
+g_env: map[string]string
+g_env_buf: []byte
+g_env_mutex: sync.RW_Mutex
+g_env_error: Error
+g_env_built: bool
+
+build_env :: proc() -> (err: Error) {
+ if g_env_built || g_env_error != nil {
+ return g_env_error
+ }
+
+ sync.guard(&g_env_mutex)
+
+ if g_env_built || g_env_error != nil {
+ return g_env_error
+ }
+
+ defer if err != nil {
+ g_env_error = err
+ }
+
+ num_envs, size_of_envs, _err := wasi.environ_sizes_get()
+ if _err != nil {
+ return _get_platform_error(_err)
+ }
+
+ g_env = make(map[string]string, num_envs, file_allocator()) or_return
+ defer if err != nil { delete(g_env) }
+
+ g_env_buf = make([]byte, size_of_envs, file_allocator()) or_return
+ defer if err != nil { delete(g_env_buf, file_allocator()) }
+
+ TEMP_ALLOCATOR_GUARD()
+
+ envs := make([]cstring, num_envs, temp_allocator()) or_return
+
+ _err = wasi.environ_get(raw_data(envs), raw_data(g_env_buf))
+ if _err != nil {
+ return _get_platform_error(_err)
+ }
+
+ for env in envs {
+ key, _, value := strings.partition(string(env), "=")
+ g_env[key] = value
+ }
+
+ g_env_built = true
+ return
+}
+
+delete_string_if_not_original :: proc(str: string) {
+ start := uintptr(raw_data(g_env_buf))
+ end := start + uintptr(len(g_env_buf))
+ ptr := uintptr(raw_data(str))
+ if ptr < start || ptr > end {
+ delete(str, file_allocator())
+ }
+}
+
+@(require_results)
+_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
+ if err := build_env(); err != nil {
+ return
+ }
+
+ sync.shared_guard(&g_env_mutex)
+
+ value = g_env[key] or_return
+ value, _ = clone_string(value, allocator)
+ return
+}
+
+@(require_results)
+_set_env :: proc(key, value: string) -> (err: Error) {
+ build_env() or_return
+
+ sync.guard(&g_env_mutex)
+
+ defer if err != nil {
+ delete_key(&g_env, key)
+ }
+
+ key_ptr, value_ptr, just_inserted := map_entry(&g_env, key) or_return
+
+ if just_inserted {
+ key_ptr^ = clone_string(key, file_allocator()) or_return
+ defer if err != nil {
+ delete(key_ptr^, file_allocator())
+ }
+ value_ptr^ = clone_string(value, file_allocator()) or_return
+ return
+ }
+
+ delete_string_if_not_original(value_ptr^)
+
+ value_ptr^ = clone_string(value, file_allocator()) or_return
+ return
+}
+
+@(require_results)
+_unset_env :: proc(key: string) -> bool {
+ if err := build_env(); err != nil {
+ return false
+ }
+
+ sync.guard(&g_env_mutex)
+
+ dkey, dval := delete_key(&g_env, key)
+ delete_string_if_not_original(dkey)
+ delete_string_if_not_original(dval)
+ return true
+}
+
+_clear_env :: proc() {
+ sync.guard(&g_env_mutex)
+
+ for k, v in g_env {
+ delete_string_if_not_original(k)
+ delete_string_if_not_original(v)
+ }
+
+ delete(g_env_buf, file_allocator())
+ g_env_buf = {}
+
+ clear(&g_env)
+
+ g_env_built = true
+}
+
+@(require_results)
+_environ :: proc(allocator: runtime.Allocator) -> (environ: []string, err: Error) {
+ build_env() or_return
+
+ sync.shared_guard(&g_env_mutex)
+
+ envs := make([dynamic]string, 0, len(g_env), allocator) or_return
+ defer if err != nil {
+ for env in envs {
+ delete(env, allocator)
+ }
+ delete(envs)
+ }
+
+ for k, v in g_env {
+ append(&envs, concatenate({k, "=", v}, allocator) or_return)
+ }
+
+ environ = envs[:]
+ return
+}
diff --git a/core/os/os2/env_windows.odin b/core/os/os2/env_windows.odin
index a1e8c969d..3ac26a261 100644
--- a/core/os/os2/env_windows.odin
+++ b/core/os/os2/env_windows.odin
@@ -36,12 +36,15 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string
return
}
-_set_env :: proc(key, value: string) -> bool {
+_set_env :: proc(key, value: string) -> Error {
TEMP_ALLOCATOR_GUARD()
- k, _ := win32_utf8_to_wstring(key, temp_allocator())
- v, _ := win32_utf8_to_wstring(value, temp_allocator())
+ k := win32_utf8_to_wstring(key, temp_allocator()) or_return
+ v := win32_utf8_to_wstring(value, temp_allocator()) or_return
- return bool(win32.SetEnvironmentVariableW(k, v))
+ if !win32.SetEnvironmentVariableW(k, v) {
+ return _get_platform_error()
+ }
+ return nil
}
_unset_env :: proc(key: string) -> bool {
@@ -52,7 +55,7 @@ _unset_env :: proc(key: string) -> bool {
_clear_env :: proc() {
TEMP_ALLOCATOR_GUARD()
- envs := environ(temp_allocator())
+ envs, _ := environ(temp_allocator())
for env in envs {
for j in 1.. []string {
+_environ :: proc(allocator: runtime.Allocator) -> (environ: []string, err: Error) {
envs := win32.GetEnvironmentStringsW()
if envs == nil {
- return nil
+ return
}
defer win32.FreeEnvironmentStringsW(envs)
@@ -82,7 +85,13 @@ _environ :: proc(allocator: runtime.Allocator) -> []string {
}
}
- r := make([dynamic]string, 0, n, allocator)
+ r := make([dynamic]string, 0, n, allocator) or_return
+ defer if err != nil {
+ for e in r {
+ delete(e, allocator)
+ }
+ delete(r)
+ }
for from, i, p := 0, 0, envs; true; i += 1 {
c := ([^]u16)(p)[i]
if c == 0 {
@@ -90,12 +99,14 @@ _environ :: proc(allocator: runtime.Allocator) -> []string {
break
}
w := ([^]u16)(p)[from:i]
- append(&r, win32_utf16_to_utf8(w, allocator) or_else "")
+ s := win32_utf16_to_utf8(w, allocator) or_return
+ append(&r, s)
from = i + 1
}
}
- return r[:]
+ environ = r[:]
+ return
}
diff --git a/core/os/os2/errors_posix.odin b/core/os/os2/errors_posix.odin
index 0b5876c0b..8a9ca07df 100644
--- a/core/os/os2/errors_posix.odin
+++ b/core/os/os2/errors_posix.odin
@@ -10,8 +10,12 @@ _error_string :: proc(errno: i32) -> string {
return string(posix.strerror(posix.Errno(errno)))
}
-_get_platform_error :: proc() -> Error {
- #partial switch errno := posix.errno(); errno {
+_get_platform_error_from_errno :: proc() -> Error {
+ return _get_platform_error_existing(posix.errno())
+}
+
+_get_platform_error_existing :: proc(errno: posix.Errno) -> Error {
+ #partial switch errno {
case .EPERM:
return .Permission_Denied
case .EEXIST:
@@ -32,3 +36,8 @@ _get_platform_error :: proc() -> Error {
return Platform_Error(errno)
}
}
+
+_get_platform_error :: proc{
+ _get_platform_error_existing,
+ _get_platform_error_from_errno,
+}
diff --git a/core/os/os2/errors_wasi.odin b/core/os/os2/errors_wasi.odin
new file mode 100644
index 000000000..b88e5b81e
--- /dev/null
+++ b/core/os/os2/errors_wasi.odin
@@ -0,0 +1,47 @@
+#+private
+package os2
+
+import "base:runtime"
+
+import "core:slice"
+import "core:sys/wasm/wasi"
+
+_Platform_Error :: wasi.errno_t
+
+_error_string :: proc(errno: i32) -> string {
+ e := wasi.errno_t(errno)
+ if e == .NONE {
+ return ""
+ }
+
+ err := runtime.Type_Info_Enum_Value(e)
+
+ ti := &runtime.type_info_base(type_info_of(wasi.errno_t)).variant.(runtime.Type_Info_Enum)
+ if idx, ok := slice.binary_search(ti.values, err); ok {
+ return ti.names[idx]
+ }
+ return ""
+}
+
+_get_platform_error :: proc(errno: wasi.errno_t) -> Error {
+ #partial switch errno {
+ case .PERM:
+ return .Permission_Denied
+ case .EXIST:
+ return .Exist
+ case .NOENT:
+ return .Not_Exist
+ case .TIMEDOUT:
+ return .Timeout
+ case .PIPE:
+ return .Broken_Pipe
+ case .BADF:
+ return .Invalid_File
+ case .NOMEM:
+ return .Out_Of_Memory
+ case .NOSYS:
+ return .Unsupported
+ case:
+ return Platform_Error(errno)
+ }
+}
diff --git a/core/os/os2/file.odin b/core/os/os2/file.odin
index eedf8570c..28d2bc69b 100644
--- a/core/os/os2/file.odin
+++ b/core/os/os2/file.odin
@@ -115,13 +115,18 @@ open :: proc(name: string, flags := File_Flags{.Read}, perm := 0o777) -> (^File,
@(require_results)
new_file :: proc(handle: uintptr, name: string) -> ^File {
- file, err := _new_file(handle, name)
+ file, err := _new_file(handle, name, file_allocator())
if err != nil {
panic(error_string(err))
}
return file
}
+@(require_results)
+clone :: proc(f: ^File) -> (^File, Error) {
+ return _clone(f)
+}
+
@(require_results)
fd :: proc(f: ^File) -> uintptr {
return _fd(f)
diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin
index e9ce13447..811ee7055 100644
--- a/core/os/os2/file_linux.odin
+++ b/core/os/os2/file_linux.odin
@@ -7,6 +7,13 @@ import "core:time"
import "core:sync"
import "core:sys/linux"
+// Most implementations will EINVAL at some point when doing big writes.
+// In practice a read/write call would probably never read/write these big buffers all at once,
+// which is why the number of bytes is returned and why there are procs that will call this in a
+// loop for you.
+// We set a max of 1GB to keep alignment and to be safe.
+MAX_RW :: 1 << 30
+
File_Impl :: struct {
file: File,
name: string,
@@ -39,37 +46,23 @@ _stderr := File{
@init
_standard_stream_init :: proc() {
- @static stdin_impl := File_Impl {
- name = "/proc/self/fd/0",
- fd = 0,
+ new_std :: proc(impl: ^File_Impl, fd: linux.Fd, name: string) -> ^File {
+ impl.file.impl = impl
+ impl.fd = linux.Fd(fd)
+ impl.allocator = runtime.nil_allocator()
+ impl.name = name
+ impl.file.stream = {
+ data = impl,
+ procedure = _file_stream_proc,
+ }
+ impl.file.fstat = _fstat
+ return &impl.file
}
- @static stdout_impl := File_Impl {
- name = "/proc/self/fd/1",
- fd = 1,
- }
-
- @static stderr_impl := File_Impl {
- name = "/proc/self/fd/2",
- fd = 2,
- }
-
- stdin_impl.allocator = file_allocator()
- stdout_impl.allocator = file_allocator()
- stderr_impl.allocator = file_allocator()
-
- _stdin.impl = &stdin_impl
- _stdout.impl = &stdout_impl
- _stderr.impl = &stderr_impl
-
- // cannot define these initially because cyclic reference
- _stdin.stream.data = &stdin_impl
- _stdout.stream.data = &stdout_impl
- _stderr.stream.data = &stderr_impl
-
- stdin = &_stdin
- stdout = &_stdout
- stderr = &_stderr
+ @(static) files: [3]File_Impl
+ stdin = new_std(&files[0], 0, "/proc/self/fd/0")
+ stdout = new_std(&files[1], 1, "/proc/self/fd/1")
+ stderr = new_std(&files[2], 2, "/proc/self/fd/2")
}
_open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
@@ -80,6 +73,9 @@ _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Err
// terminal would be incredibly rare. This has no effect on files while
// allowing us to open serial devices.
sys_flags: linux.Open_Flags = {.NOCTTY, .CLOEXEC}
+ when size_of(rawptr) == 4 {
+ sys_flags += {.LARGEFILE}
+ }
switch flags & (O_RDONLY|O_WRONLY|O_RDWR) {
case O_RDONLY:
case O_WRONLY: sys_flags += {.WRONLY}
@@ -97,18 +93,18 @@ _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Err
return nil, _get_platform_error(errno)
}
- return _new_file(uintptr(fd), name)
+ return _new_file(uintptr(fd), name, file_allocator())
}
-_new_file :: proc(fd: uintptr, _: string = "") -> (f: ^File, err: Error) {
- impl := new(File_Impl, file_allocator()) or_return
+_new_file :: proc(fd: uintptr, _: string, allocator: runtime.Allocator) -> (f: ^File, err: Error) {
+ impl := new(File_Impl, allocator) or_return
defer if err != nil {
- free(impl, file_allocator())
+ free(impl, allocator)
}
impl.file.impl = impl
impl.fd = linux.Fd(fd)
- impl.allocator = file_allocator()
- impl.name = _get_full_path(impl.fd, file_allocator()) or_return
+ impl.allocator = allocator
+ impl.name = _get_full_path(impl.fd, impl.allocator) or_return
impl.file.stream = {
data = impl,
procedure = _file_stream_proc,
@@ -117,6 +113,23 @@ _new_file :: proc(fd: uintptr, _: string = "") -> (f: ^File, err: Error) {
return &impl.file, nil
}
+_clone :: proc(f: ^File) -> (clone: ^File, err: Error) {
+ if f == nil || f.impl == nil {
+ return
+ }
+
+ fd := (^File_Impl)(f.impl).fd
+
+ clonefd, errno := linux.dup(fd)
+ if errno != nil {
+ err = _get_platform_error(errno)
+ return
+ }
+ defer if err != nil { linux.close(clonefd) }
+
+ return _new_file(uintptr(clonefd), "", file_allocator())
+}
+
@(require_results)
_open_buffered :: proc(name: string, buffer_size: uint, flags := File_Flags{.Read}, perm := 0o777) -> (f: ^File, err: Error) {
@@ -190,10 +203,11 @@ _seek :: proc(f: ^File_Impl, offset: i64, whence: io.Seek_From) -> (ret: i64, er
}
_read :: proc(f: ^File_Impl, p: []byte) -> (i64, Error) {
- if len(p) == 0 {
+ if len(p) <= 0 {
return 0, nil
}
- n, errno := linux.read(f.fd, p[:])
+
+ n, errno := linux.read(f.fd, p[:min(len(p), MAX_RW)])
if errno != .NONE {
return -1, _get_platform_error(errno)
}
@@ -201,13 +215,13 @@ _read :: proc(f: ^File_Impl, p: []byte) -> (i64, Error) {
}
_read_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (i64, Error) {
- if len(p) == 0 {
+ if len(p) <= 0 {
return 0, nil
}
if offset < 0 {
return 0, .Invalid_Offset
}
- n, errno := linux.pread(f.fd, p[:], offset)
+ n, errno := linux.pread(f.fd, p[:min(len(p), MAX_RW)], offset)
if errno != .NONE {
return -1, _get_platform_error(errno)
}
@@ -217,29 +231,42 @@ _read_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (i64, Error) {
return i64(n), nil
}
-_write :: proc(f: ^File_Impl, p: []byte) -> (i64, Error) {
- if len(p) == 0 {
- return 0, nil
+_write :: proc(f: ^File_Impl, p: []byte) -> (nt: i64, err: Error) {
+ p := p
+ for len(p) > 0 {
+ n, errno := linux.write(f.fd, p[:min(len(p), MAX_RW)])
+ if errno != .NONE {
+ err = _get_platform_error(errno)
+ return
+ }
+
+ p = p[n:]
+ nt += i64(n)
}
- n, errno := linux.write(f.fd, p[:])
- if errno != .NONE {
- return -1, _get_platform_error(errno)
- }
- return i64(n), nil
+
+ return
}
-_write_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (i64, Error) {
- if len(p) == 0 {
- return 0, nil
- }
+_write_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (nt: i64, err: Error) {
if offset < 0 {
return 0, .Invalid_Offset
}
- n, errno := linux.pwrite(f.fd, p[:], offset)
- if errno != .NONE {
- return -1, _get_platform_error(errno)
+
+ p := p
+ offset := offset
+ for len(p) > 0 {
+ n, errno := linux.pwrite(f.fd, p[:min(len(p), MAX_RW)], offset)
+ if errno != .NONE {
+ err = _get_platform_error(errno)
+ return
+ }
+
+ p = p[n:]
+ nt += i64(n)
+ offset += i64(n)
}
- return i64(n), nil
+
+ return
}
_file_size :: proc(f: ^File_Impl) -> (n: i64, err: Error) {
@@ -272,28 +299,12 @@ _truncate :: proc(f: ^File, size: i64) -> Error {
}
_remove :: proc(name: string) -> Error {
- is_dir_fd :: proc(fd: linux.Fd) -> bool {
- s: linux.Stat
- if linux.fstat(fd, &s) != .NONE {
- return false
- }
- return linux.S_ISDIR(s.mode)
- }
-
TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return
- fd, errno := linux.open(name_cstr, {.NOFOLLOW})
- #partial switch (errno) {
- case .ELOOP:
- /* symlink */
- case .NONE:
- defer linux.close(fd)
- if is_dir_fd(fd) {
- return _get_platform_error(linux.rmdir(name_cstr))
- }
- case:
- return _get_platform_error(errno)
+ if fd, errno := linux.open(name_cstr, _OPENDIR_FLAGS + {.NOFOLLOW}); errno == .NONE {
+ linux.close(fd)
+ return _get_platform_error(linux.rmdir(name_cstr))
}
return _get_platform_error(linux.unlink(name_cstr))
diff --git a/core/os/os2/file_posix.odin b/core/os/os2/file_posix.odin
index b7dc43287..43d5866b1 100644
--- a/core/os/os2/file_posix.odin
+++ b/core/os/os2/file_posix.odin
@@ -21,23 +21,29 @@ File_Impl :: struct {
name: string,
cname: cstring,
fd: posix.FD,
+ allocator: runtime.Allocator,
}
@(init)
init_std_files :: proc() {
- // NOTE: is this (paths) also the case on non darwin?
+ new_std :: proc(impl: ^File_Impl, fd: posix.FD, name: cstring) -> ^File {
+ impl.file.impl = impl
+ impl.fd = fd
+ impl.allocator = runtime.nil_allocator()
+ impl.cname = name
+ impl.name = string(name)
+ impl.file.stream = {
+ data = impl,
+ procedure = _file_stream_proc,
+ }
+ impl.file.fstat = _fstat
+ return &impl.file
+ }
- stdin = __new_file(posix.STDIN_FILENO)
- (^File_Impl)(stdin.impl).name = "/dev/stdin"
- (^File_Impl)(stdin.impl).cname = "/dev/stdin"
-
- stdout = __new_file(posix.STDIN_FILENO)
- (^File_Impl)(stdout.impl).name = "/dev/stdout"
- (^File_Impl)(stdout.impl).cname = "/dev/stdout"
-
- stderr = __new_file(posix.STDIN_FILENO)
- (^File_Impl)(stderr.impl).name = "/dev/stderr"
- (^File_Impl)(stderr.impl).cname = "/dev/stderr"
+ @(static) files: [3]File_Impl
+ stdin = new_std(&files[0], posix.STDIN_FILENO, "/dev/stdin")
+ stdout = new_std(&files[1], posix.STDOUT_FILENO, "/dev/stdout")
+ stderr = new_std(&files[2], posix.STDERR_FILENO, "/dev/stderr")
}
_open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
@@ -72,10 +78,10 @@ _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Err
return
}
- return _new_file(uintptr(fd), name)
+ return _new_file(uintptr(fd), name, file_allocator())
}
-_new_file :: proc(handle: uintptr, name: string) -> (f: ^File, err: Error) {
+_new_file :: proc(handle: uintptr, name: string, allocator: runtime.Allocator) -> (f: ^File, err: Error) {
if name == "" {
err = .Invalid_Path
return
@@ -84,10 +90,10 @@ _new_file :: proc(handle: uintptr, name: string) -> (f: ^File, err: Error) {
return
}
- crname := _posix_absolute_path(posix.FD(handle), name, file_allocator()) or_return
+ crname := _posix_absolute_path(posix.FD(handle), name, allocator) or_return
rname := string(crname)
- f = __new_file(posix.FD(handle))
+ f = __new_file(posix.FD(handle), allocator)
impl := (^File_Impl)(f.impl)
impl.name = rname
impl.cname = crname
@@ -95,10 +101,11 @@ _new_file :: proc(handle: uintptr, name: string) -> (f: ^File, err: Error) {
return f, nil
}
-__new_file :: proc(handle: posix.FD) -> ^File {
- impl := new(File_Impl, file_allocator())
+__new_file :: proc(handle: posix.FD, allocator: runtime.Allocator) -> ^File {
+ impl := new(File_Impl, allocator)
impl.file.impl = impl
impl.fd = posix.FD(handle)
+ impl.allocator = allocator
impl.file.stream = {
data = impl,
procedure = _file_stream_proc,
@@ -107,6 +114,29 @@ __new_file :: proc(handle: posix.FD) -> ^File {
return &impl.file
}
+_clone :: proc(f: ^File) -> (clone: ^File, err: Error) {
+ if f == nil || f.impl == nil {
+ err = .Invalid_Pointer
+ return
+ }
+
+ impl := (^File_Impl)(f.impl)
+
+ fd := posix.dup(impl.fd)
+ if fd <= 0 {
+ err = _get_platform_error()
+ return
+ }
+ defer if err != nil { posix.close(fd) }
+
+ clone = __new_file(fd, file_allocator())
+ clone_impl := (^File_Impl)(clone.impl)
+ clone_impl.cname = clone_to_cstring(impl.name, file_allocator()) or_return
+ clone_impl.name = string(clone_impl.cname)
+
+ return
+}
+
_close :: proc(f: ^File_Impl) -> (err: Error) {
if f == nil { return nil }
@@ -114,8 +144,10 @@ _close :: proc(f: ^File_Impl) -> (err: Error) {
err = _get_platform_error()
}
- delete(f.cname, file_allocator())
- free(f, file_allocator())
+ allocator := f.allocator
+
+ delete(f.cname, allocator)
+ free(f, allocator)
return
}
diff --git a/core/os/os2/file_wasi.odin b/core/os/os2/file_wasi.odin
new file mode 100644
index 000000000..0245841e3
--- /dev/null
+++ b/core/os/os2/file_wasi.odin
@@ -0,0 +1,560 @@
+#+private
+package os2
+
+import "base:runtime"
+
+import "core:io"
+import "core:sys/wasm/wasi"
+import "core:time"
+
+// NOTE: Don't know if there is a max in wasi.
+MAX_RW :: 1 << 30
+
+File_Impl :: struct {
+ file: File,
+ name: string,
+ fd: wasi.fd_t,
+ allocator: runtime.Allocator,
+}
+
+// WASI works with "preopened" directories, the environment retrieves directories
+// (for example with `wasmtime --dir=. module.wasm`) and those given directories
+// are the only ones accessible by the application.
+//
+// So in order to facilitate the `os` API (absolute paths etc.) we keep a list
+// of the given directories and match them when needed (notably `os.open`).
+Preopen :: struct {
+ fd: wasi.fd_t,
+ prefix: string,
+}
+preopens: []Preopen
+
+@(init)
+init_std_files :: proc() {
+ new_std :: proc(impl: ^File_Impl, fd: wasi.fd_t, name: string) -> ^File {
+ impl.file.impl = impl
+ impl.allocator = runtime.nil_allocator()
+ impl.fd = fd
+ impl.name = string(name)
+ impl.file.stream = {
+ data = impl,
+ procedure = _file_stream_proc,
+ }
+ impl.file.fstat = _fstat
+ return &impl.file
+ }
+
+ @(static) files: [3]File_Impl
+ stdin = new_std(&files[0], 0, "/dev/stdin")
+ stdout = new_std(&files[1], 1, "/dev/stdout")
+ stderr = new_std(&files[2], 2, "/dev/stderr")
+}
+
+@(init)
+init_preopens :: proc() {
+ strip_prefixes :: proc(path: string) -> string {
+ path := path
+ loop: for len(path) > 0 {
+ switch {
+ case path[0] == '/':
+ path = path[1:]
+ case len(path) > 2 && path[0] == '.' && path[1] == '/':
+ path = path[2:]
+ case len(path) == 1 && path[0] == '.':
+ path = path[1:]
+ case:
+ break loop
+ }
+ }
+ return path
+ }
+
+ n: int
+ n_loop: for fd := wasi.fd_t(3); ; fd += 1 {
+ _, err := wasi.fd_prestat_get(fd)
+ #partial switch err {
+ case .BADF: break n_loop
+ case .SUCCESS: n += 1
+ case:
+ print_error(stderr, _get_platform_error(err), "unexpected error from wasi_prestat_get")
+ break n_loop
+ }
+ }
+
+ alloc_err: runtime.Allocator_Error
+ preopens, alloc_err = make([]Preopen, n, file_allocator())
+ if alloc_err != nil {
+ print_error(stderr, alloc_err, "could not allocate memory for wasi preopens")
+ return
+ }
+
+ loop: for &preopen, i in preopens {
+ fd := wasi.fd_t(3 + i)
+
+ desc, err := wasi.fd_prestat_get(fd)
+ assert(err == .SUCCESS)
+
+ switch desc.tag {
+ case .DIR:
+ buf: []byte
+ buf, alloc_err = make([]byte, desc.dir.pr_name_len, file_allocator())
+ if alloc_err != nil {
+ print_error(stderr, alloc_err, "could not allocate memory for wasi preopen dir name")
+ continue loop
+ }
+
+ if err = wasi.fd_prestat_dir_name(fd, buf); err != .SUCCESS {
+ print_error(stderr, _get_platform_error(err), "could not get filesystem preopen dir name")
+ continue loop
+ }
+
+ preopen.fd = fd
+ preopen.prefix = strip_prefixes(string(buf))
+ }
+ }
+}
+
+@(require_results)
+match_preopen :: proc(path: string) -> (wasi.fd_t, string, bool) {
+ @(require_results)
+ prefix_matches :: proc(prefix, path: string) -> bool {
+ // Empty is valid for any relative path.
+ if len(prefix) == 0 && len(path) > 0 && path[0] != '/' {
+ return true
+ }
+
+ if len(path) < len(prefix) {
+ return false
+ }
+
+ if path[:len(prefix)] != prefix {
+ return false
+ }
+
+ // Only match on full components.
+ i := len(prefix)
+ for i > 0 && prefix[i-1] == '/' {
+ i -= 1
+ }
+ return path[i] == '/'
+ }
+
+ path := path
+ if path == "" {
+ return 0, "", false
+ }
+
+ for len(path) > 0 && path[0] == '/' {
+ path = path[1:]
+ }
+
+ match: Preopen
+ #reverse for preopen in preopens {
+ if (match.fd == 0 || len(preopen.prefix) > len(match.prefix)) && prefix_matches(preopen.prefix, path) {
+ match = preopen
+ }
+ }
+
+ if match.fd == 0 {
+ return 0, "", false
+ }
+
+ relative := path[len(match.prefix):]
+ for len(relative) > 0 && relative[0] == '/' {
+ relative = relative[1:]
+ }
+
+ if len(relative) == 0 {
+ relative = "."
+ }
+
+ return match.fd, relative, true
+}
+
+_open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
+ dir_fd, relative, ok := match_preopen(name)
+ if !ok {
+ return nil, .Invalid_Path
+ }
+
+ oflags: wasi.oflags_t
+ if .Create in flags { oflags += {.CREATE} }
+ if .Excl in flags { oflags += {.EXCL} }
+ if .Trunc in flags { oflags += {.TRUNC} }
+
+ fdflags: wasi.fdflags_t
+ if .Append in flags { fdflags += {.APPEND} }
+ if .Sync in flags { fdflags += {.SYNC} }
+
+ // NOTE: rights are adjusted to what this package's functions might want to call.
+ rights: wasi.rights_t
+ if .Read in flags { rights += {.FD_READ, .FD_FILESTAT_GET, .PATH_FILESTAT_GET} }
+ if .Write in flags { rights += {.FD_WRITE, .FD_SYNC, .FD_FILESTAT_SET_SIZE, .FD_FILESTAT_SET_TIMES, .FD_SEEK} }
+
+ fd, fderr := wasi.path_open(dir_fd, {.SYMLINK_FOLLOW}, relative, oflags, rights, {}, fdflags)
+ if fderr != nil {
+ err = _get_platform_error(fderr)
+ return
+ }
+
+ return _new_file(uintptr(fd), name, file_allocator())
+}
+
+_new_file :: proc(handle: uintptr, name: string, allocator: runtime.Allocator) -> (f: ^File, err: Error) {
+ if name == "" {
+ err = .Invalid_Path
+ return
+ }
+
+ impl := new(File_Impl, allocator) or_return
+ defer if err != nil { free(impl, allocator) }
+
+ impl.allocator = allocator
+ // NOTE: wasi doesn't really do full paths afact.
+ impl.name = clone_string(name, allocator) or_return
+ impl.fd = wasi.fd_t(handle)
+ impl.file.impl = impl
+ impl.file.stream = {
+ data = impl,
+ procedure = _file_stream_proc,
+ }
+ impl.file.fstat = _fstat
+
+ return &impl.file, nil
+}
+
+_clone :: proc(f: ^File) -> (clone: ^File, err: Error) {
+ if f == nil || f.impl == nil {
+ return
+ }
+
+ dir_fd, relative, ok := match_preopen(name(f))
+ if !ok {
+ return nil, .Invalid_Path
+ }
+
+ fd, fderr := wasi.path_open(dir_fd, {.SYMLINK_FOLLOW}, relative, {}, {}, {}, {})
+ if fderr != nil {
+ err = _get_platform_error(fderr)
+ return
+ }
+ defer if err != nil { wasi.fd_close(fd) }
+
+ fderr = wasi.fd_renumber((^File_Impl)(f.impl).fd, fd)
+ if fderr != nil {
+ err = _get_platform_error(fderr)
+ return
+ }
+
+ return _new_file(uintptr(fd), name(f), file_allocator())
+}
+
+_close :: proc(f: ^File_Impl) -> (err: Error) {
+ if errno := wasi.fd_close(f.fd); errno != nil {
+ err = _get_platform_error(errno)
+ }
+
+ delete(f.name, f.allocator)
+ free(f, f.allocator)
+ return
+}
+
+_fd :: proc(f: ^File) -> uintptr {
+ return uintptr(__fd(f))
+}
+
+__fd :: proc(f: ^File) -> wasi.fd_t {
+ if f != nil && f.impl != nil {
+ return (^File_Impl)(f.impl).fd
+ }
+ return -1
+}
+
+_name :: proc(f: ^File) -> string {
+ if f != nil && f.impl != nil {
+ return (^File_Impl)(f.impl).name
+ }
+ return ""
+}
+
+_sync :: proc(f: ^File) -> Error {
+ return _get_platform_error(wasi.fd_sync(__fd(f)))
+}
+
+_truncate :: proc(f: ^File, size: i64) -> Error {
+ return _get_platform_error(wasi.fd_filestat_set_size(__fd(f), wasi.filesize_t(size)))
+}
+
+_remove :: proc(name: string) -> Error {
+ dir_fd, relative, ok := match_preopen(name)
+ if !ok {
+ return .Invalid_Path
+ }
+
+ err := wasi.path_remove_directory(dir_fd, relative)
+ if err == .NOTDIR {
+ err = wasi.path_unlink_file(dir_fd, relative)
+ }
+
+ return _get_platform_error(err)
+}
+
+_rename :: proc(old_path, new_path: string) -> Error {
+ src_dir_fd, src_relative, src_ok := match_preopen(old_path)
+ if !src_ok {
+ return .Invalid_Path
+ }
+
+ new_dir_fd, new_relative, new_ok := match_preopen(new_path)
+ if !new_ok {
+ return .Invalid_Path
+ }
+
+ return _get_platform_error(wasi.path_rename(src_dir_fd, src_relative, new_dir_fd, new_relative))
+}
+
+_link :: proc(old_name, new_name: string) -> Error {
+ src_dir_fd, src_relative, src_ok := match_preopen(old_name)
+ if !src_ok {
+ return .Invalid_Path
+ }
+
+ new_dir_fd, new_relative, new_ok := match_preopen(new_name)
+ if !new_ok {
+ return .Invalid_Path
+ }
+
+ return _get_platform_error(wasi.path_link(src_dir_fd, {.SYMLINK_FOLLOW}, src_relative, new_dir_fd, new_relative))
+}
+
+_symlink :: proc(old_name, new_name: string) -> Error {
+ src_dir_fd, src_relative, src_ok := match_preopen(old_name)
+ if !src_ok {
+ return .Invalid_Path
+ }
+
+ new_dir_fd, new_relative, new_ok := match_preopen(new_name)
+ if !new_ok {
+ return .Invalid_Path
+ }
+
+ if src_dir_fd != new_dir_fd {
+ return .Invalid_Path
+ }
+
+ return _get_platform_error(wasi.path_symlink(src_relative, src_dir_fd, new_relative))
+}
+
+_read_link :: proc(name: string, allocator: runtime.Allocator) -> (s: string, err: Error) {
+ dir_fd, relative, ok := match_preopen(name)
+ if !ok {
+ return "", .Invalid_Path
+ }
+
+ n, _err := wasi.path_readlink(dir_fd, relative, nil)
+ if _err != nil {
+ err = _get_platform_error(_err)
+ return
+ }
+
+ buf := make([]byte, n, allocator) or_return
+
+ _, _err = wasi.path_readlink(dir_fd, relative, buf)
+ s = string(buf)
+ err = _get_platform_error(_err)
+ return
+}
+
+_chdir :: proc(name: string) -> Error {
+ return .Unsupported
+}
+
+_fchdir :: proc(f: ^File) -> Error {
+ return .Unsupported
+}
+
+_fchmod :: proc(f: ^File, mode: int) -> Error {
+ return .Unsupported
+}
+
+_chmod :: proc(name: string, mode: int) -> Error {
+ return .Unsupported
+}
+
+_fchown :: proc(f: ^File, uid, gid: int) -> Error {
+ return .Unsupported
+}
+
+_chown :: proc(name: string, uid, gid: int) -> Error {
+ return .Unsupported
+}
+
+_lchown :: proc(name: string, uid, gid: int) -> Error {
+ return .Unsupported
+}
+
+_chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
+ dir_fd, relative, ok := match_preopen(name)
+ if !ok {
+ return .Invalid_Path
+ }
+
+ _atime := wasi.timestamp_t(atime._nsec)
+ _mtime := wasi.timestamp_t(mtime._nsec)
+
+ return _get_platform_error(wasi.path_filestat_set_times(dir_fd, {.SYMLINK_FOLLOW}, relative, _atime, _mtime, {.MTIM, .ATIM}))
+}
+
+_fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
+ _atime := wasi.timestamp_t(atime._nsec)
+ _mtime := wasi.timestamp_t(mtime._nsec)
+
+ return _get_platform_error(wasi.fd_filestat_set_times(__fd(f), _atime, _mtime, {.ATIM, .MTIM}))
+}
+
+_exists :: proc(path: string) -> bool {
+ dir_fd, relative, ok := match_preopen(path)
+ if !ok {
+ return false
+ }
+
+ _, err := wasi.path_filestat_get(dir_fd, {.SYMLINK_FOLLOW}, relative)
+ if err != nil {
+ return false
+ }
+
+ return true
+}
+
+_file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
+ f := (^File_Impl)(stream_data)
+ fd := f.fd
+
+ switch mode {
+ case .Read:
+ if len(p) <= 0 {
+ return
+ }
+
+ to_read := min(len(p), MAX_RW)
+ _n, _err := wasi.fd_read(fd, {p[:to_read]})
+ n = i64(_n)
+
+ if _err != nil {
+ err = .Unknown
+ } else if n == 0 {
+ err = .EOF
+ }
+
+ return
+
+ case .Read_At:
+ if len(p) <= 0 {
+ return
+ }
+
+ if offset < 0 {
+ err = .Invalid_Offset
+ return
+ }
+
+ to_read := min(len(p), MAX_RW)
+ _n, _err := wasi.fd_pread(fd, {p[:to_read]}, wasi.filesize_t(offset))
+ n = i64(_n)
+
+ if _err != nil {
+ err = .Unknown
+ } else if n == 0 {
+ err = .EOF
+ }
+
+ return
+
+ case .Write:
+ p := p
+ for len(p) > 0 {
+ to_write := min(len(p), MAX_RW)
+ _n, _err := wasi.fd_write(fd, {p[:to_write]})
+ if _err != nil {
+ err = .Unknown
+ return
+ }
+ p = p[_n:]
+ n += i64(_n)
+ }
+ return
+
+ case .Write_At:
+ p := p
+ offset := offset
+
+ if offset < 0 {
+ err = .Invalid_Offset
+ return
+ }
+
+ for len(p) > 0 {
+ to_write := min(len(p), MAX_RW)
+ _n, _err := wasi.fd_pwrite(fd, {p[:to_write]}, wasi.filesize_t(offset))
+ if _err != nil {
+ err = .Unknown
+ return
+ }
+
+ p = p[_n:]
+ n += i64(_n)
+ offset += i64(_n)
+ }
+ return
+
+ case .Seek:
+ #assert(int(wasi.whence_t.SET) == int(io.Seek_From.Start))
+ #assert(int(wasi.whence_t.CUR) == int(io.Seek_From.Current))
+ #assert(int(wasi.whence_t.END) == int(io.Seek_From.End))
+
+ switch whence {
+ case .Start, .Current, .End:
+ break
+ case:
+ err = .Invalid_Whence
+ return
+ }
+
+ _n, _err := wasi.fd_seek(fd, wasi.filedelta_t(offset), wasi.whence_t(whence))
+ #partial switch _err {
+ case .INVAL:
+ err = .Invalid_Offset
+ case:
+ err = .Unknown
+ case .SUCCESS:
+ n = i64(_n)
+ }
+ return
+
+ case .Size:
+ stat, _err := wasi.fd_filestat_get(fd)
+ if _err != nil {
+ err = .Unknown
+ return
+ }
+
+ n = i64(stat.size)
+ return
+
+ case .Flush:
+ ferr := _sync(&f.file)
+ err = error_to_io_error(ferr)
+ return
+
+ case .Close, .Destroy:
+ ferr := _close(f)
+ err = error_to_io_error(ferr)
+ return
+
+ case .Query:
+ return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Destroy, .Query})
+
+ case:
+ return 0, .Empty
+ }
+}
diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin
index b91a1bc3b..390affd82 100644
--- a/core/os/os2/file_windows.odin
+++ b/core/os/os2/file_windows.odin
@@ -44,17 +44,38 @@ File_Impl :: struct {
@(init)
init_std_files :: proc() {
- stdin = new_file(uintptr(win32.GetStdHandle(win32.STD_INPUT_HANDLE)), "")
- stdout = new_file(uintptr(win32.GetStdHandle(win32.STD_OUTPUT_HANDLE)), "")
- stderr = new_file(uintptr(win32.GetStdHandle(win32.STD_ERROR_HANDLE)), "")
-}
-@(fini)
-fini_std_files :: proc() {
- _destroy((^File_Impl)(stdin.impl))
- _destroy((^File_Impl)(stdout.impl))
- _destroy((^File_Impl)(stderr.impl))
-}
+ new_std :: proc(impl: ^File_Impl, code: u32, name: string) -> ^File {
+ impl.file.impl = impl
+ impl.allocator = runtime.nil_allocator()
+ impl.fd = win32.GetStdHandle(code)
+ impl.name = name
+ impl.wname = nil
+
+ handle := _handle(&impl.file)
+ kind := File_Impl_Kind.File
+ if m: u32; win32.GetConsoleMode(handle, &m) {
+ kind = .Console
+ }
+ if win32.GetFileType(handle) == win32.FILE_TYPE_PIPE {
+ kind = .Pipe
+ }
+ impl.kind = kind
+
+ impl.file.stream = {
+ data = impl,
+ procedure = _file_stream_proc,
+ }
+ impl.file.fstat = _fstat
+
+ return &impl.file
+ }
+
+ @(static) files: [3]File_Impl
+ stdin = new_std(&files[0], win32.STD_INPUT_HANDLE, "")
+ stdout = new_std(&files[1], win32.STD_OUTPUT_HANDLE, "")
+ stderr = new_std(&files[2], win32.STD_ERROR_HANDLE, "")
+}
_handle :: proc(f: ^File) -> win32.HANDLE {
return win32.HANDLE(_fd(f))
@@ -132,21 +153,21 @@ _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) {
flags := flags if flags != nil else {.Read}
handle := _open_internal(name, flags, perm) or_return
- return _new_file(handle, name)
+ return _new_file(handle, name, file_allocator())
}
-_new_file :: proc(handle: uintptr, name: string) -> (f: ^File, err: Error) {
+_new_file :: proc(handle: uintptr, name: string, allocator: runtime.Allocator) -> (f: ^File, err: Error) {
if handle == INVALID_HANDLE {
return
}
- impl := new(File_Impl, file_allocator()) or_return
+ impl := new(File_Impl, allocator) or_return
defer if err != nil {
- free(impl, file_allocator())
+ free(impl, allocator)
}
impl.file.impl = impl
- impl.allocator = file_allocator()
+ impl.allocator = allocator
impl.fd = rawptr(handle)
impl.name = clone_string(name, impl.allocator) or_return
impl.wname = win32_utf8_to_wstring(name, impl.allocator) or_return
@@ -180,7 +201,7 @@ _open_buffered :: proc(name: string, buffer_size: uint, flags := File_Flags{.Rea
}
_new_file_buffered :: proc(handle: uintptr, name: string, buffer_size: uint) -> (f: ^File, err: Error) {
- f, err = _new_file(handle, name)
+ f, err = _new_file(handle, name, file_allocator())
if f != nil && err == nil {
impl := (^File_Impl)(f.impl)
impl.r_buf = make([]byte, buffer_size, file_allocator())
@@ -189,6 +210,29 @@ _new_file_buffered :: proc(handle: uintptr, name: string, buffer_size: uint) ->
return
}
+_clone :: proc(f: ^File) -> (clone: ^File, err: Error) {
+ if f == nil || f.impl == nil {
+ return
+ }
+
+ clonefd: win32.HANDLE
+ process := win32.GetCurrentProcess()
+ if !win32.DuplicateHandle(
+ process,
+ win32.HANDLE(_fd(f)),
+ process,
+ &clonefd,
+ 0,
+ false,
+ win32.DUPLICATE_SAME_ACCESS,
+ ) {
+ err = _get_platform_error()
+ return
+ }
+ defer if err != nil { win32.CloseHandle(clonefd) }
+
+ return _new_file(uintptr(clonefd), name(f), file_allocator())
+}
_fd :: proc(f: ^File) -> uintptr {
if f == nil || f.impl == nil {
@@ -462,10 +506,15 @@ _write_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (n: i64, err: Error)
_file_size :: proc(f: ^File_Impl) -> (n: i64, err: Error) {
length: win32.LARGE_INTEGER
- if f.kind == .Pipe {
- return 0, .No_Size
- }
handle := _handle(&f.file)
+ if f.kind == .Pipe {
+ bytesAvail: u32
+ if win32.PeekNamedPipe(handle, nil, 0, nil, &bytesAvail, nil) {
+ return i64(bytesAvail), nil
+ } else {
+ return 0, .No_Size
+ }
+ }
if !win32.GetFileSizeEx(handle, &length) {
err = _get_platform_error()
}
diff --git a/core/os/os2/heap_linux.odin b/core/os/os2/heap_linux.odin
index ede5eb2ac..1d1f12726 100644
--- a/core/os/os2/heap_linux.odin
+++ b/core/os/os2/heap_linux.odin
@@ -1,726 +1,6 @@
#+private
package os2
-import "core:sys/linux"
-import "core:sync"
-import "core:mem"
-
-// NOTEs
-//
-// All allocations below DIRECT_MMAP_THRESHOLD exist inside of memory "Regions." A region
-// consists of a Region_Header and the memory that will be divided into allocations to
-// send to the user. The memory is an array of "Allocation_Headers" which are 8 bytes.
-// Allocation_Headers are used to navigate the memory in the region. The "next" member of
-// the Allocation_Header points to the next header, and the space between the headers
-// can be used to send to the user. This space between is referred to as "blocks" in the
-// code. The indexes in the header refer to these blocks instead of bytes. This allows us
-// to index all the memory in the region with a u16.
-//
-// When an allocation request is made, it will use the first free block that can contain
-// the entire block. If there is an excess number of blocks (as specified by the constant
-// BLOCK_SEGMENT_THRESHOLD), this extra space will be segmented and left in the free_list.
-//
-// To keep the implementation simple, there can never exist 2 free blocks adjacent to each
-// other. Any freeing will result in attempting to merge the blocks before and after the
-// newly free'd blocks.
-//
-// Any request for size above the DIRECT_MMAP_THRESHOLD will result in the allocation
-// getting its own individual mmap. Individual mmaps will still get an Allocation_Header
-// that contains the size with the last bit set to 1 to indicate it is indeed a direct
-// mmap allocation.
-
-// Why not brk?
-// glibc's malloc utilizes a mix of the brk and mmap system calls. This implementation
-// does *not* utilize the brk system call to avoid possible conflicts with foreign C
-// code. Just because we aren't directly using libc, there is nothing stopping the user
-// from doing it.
-
-// What's with all the #no_bounds_check?
-// When memory is returned from mmap, it technically doesn't get written ... well ... anywhere
-// until that region is written to by *you*. So, when a new region is created, we call mmap
-// to get a pointer to some memory, and we claim that memory is a ^Region. Therefor, the
-// region itself is never formally initialized by the compiler as this would result in writing
-// zeros to memory that we can already assume are 0. This would also have the effect of
-// actually commiting this data to memory whether it gets used or not.
-
-
-//
-// Some variables to play with
-//
-
-// Minimum blocks used for any one allocation
-MINIMUM_BLOCK_COUNT :: 2
-
-// Number of extra blocks beyond the requested amount where we would segment.
-// E.g. (blocks) |H0123456| 7 available
-// |H01H0123| Ask for 2, now 4 available
-BLOCK_SEGMENT_THRESHOLD :: 4
-
-// Anything above this threshold will get its own memory map. Since regions
-// are indexed by 16 bit integers, this value should not surpass max(u16) * 6
-DIRECT_MMAP_THRESHOLD_USER :: int(max(u16))
-
-// The point at which we convert direct mmap to region. This should be a decent
-// amount less than DIRECT_MMAP_THRESHOLD to avoid jumping in and out of regions.
-MMAP_TO_REGION_SHRINK_THRESHOLD :: DIRECT_MMAP_THRESHOLD - PAGE_SIZE * 4
-
-// free_list is dynamic and is initialized in the begining of the region memory
-// when the region is initialized. Once resized, it can be moved anywhere.
-FREE_LIST_DEFAULT_CAP :: 32
-
-
-//
-// Other constants that should not be touched
-//
-
-// This universally seems to be 4096 outside of uncommon archs.
-PAGE_SIZE :: 4096
-
-// just rounding up to nearest PAGE_SIZE
-DIRECT_MMAP_THRESHOLD :: (DIRECT_MMAP_THRESHOLD_USER-1) + PAGE_SIZE - (DIRECT_MMAP_THRESHOLD_USER-1) % PAGE_SIZE
-
-// Regions must be big enough to hold DIRECT_MMAP_THRESHOLD - 1 as well
-// as end right on a page boundary as to not waste space.
-SIZE_OF_REGION :: DIRECT_MMAP_THRESHOLD + 4 * int(PAGE_SIZE)
-
-// size of user memory blocks
-BLOCK_SIZE :: size_of(Allocation_Header)
-
-// number of allocation sections (call them blocks) of the region used for allocations
-BLOCKS_PER_REGION :: u16((SIZE_OF_REGION - size_of(Region_Header)) / BLOCK_SIZE)
-
-// minimum amount of space that can used by any individual allocation (includes header)
-MINIMUM_ALLOCATION :: (MINIMUM_BLOCK_COUNT * BLOCK_SIZE) + BLOCK_SIZE
-
-// This is used as a boolean value for Region_Header.local_addr.
-CURRENTLY_ACTIVE :: (^^Region)(~uintptr(0))
-
-FREE_LIST_ENTRIES_PER_BLOCK :: BLOCK_SIZE / size_of(u16)
-
-MMAP_FLAGS : linux.Map_Flags : {.ANONYMOUS, .PRIVATE}
-MMAP_PROT : linux.Mem_Protection : {.READ, .WRITE}
-
-@thread_local _local_region: ^Region
-global_regions: ^Region
-
-
-// There is no way of correctly setting the last bit of free_idx or
-// the last bit of requested, so we can safely use it as a flag to
-// determine if we are interacting with a direct mmap.
-REQUESTED_MASK :: 0x7FFFFFFFFFFFFFFF
-IS_DIRECT_MMAP :: 0x8000000000000000
-
-// Special free_idx value that does not index the free_list.
-NOT_FREE :: 0x7FFF
-Allocation_Header :: struct #raw_union {
- using _: struct {
- // Block indicies
- idx: u16,
- prev: u16,
- next: u16,
- free_idx: u16,
- },
- requested: u64,
-}
-
-Region_Header :: struct #align(16) {
- next_region: ^Region, // points to next region in global_heap (linked list)
- local_addr: ^^Region, // tracks region ownership via address of _local_region
- reset_addr: ^^Region, // tracks old local addr for reset
- free_list: []u16,
- free_list_len: u16,
- free_blocks: u16, // number of free blocks in region (includes headers)
- last_used: u16, // farthest back block that has been used (need zeroing?)
- _reserved: u16,
-}
-
-Region :: struct {
- hdr: Region_Header,
- memory: [BLOCKS_PER_REGION]Allocation_Header,
-}
-
-_heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
- size, alignment: int,
- old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, mem.Allocator_Error) {
- //
- // NOTE(tetra, 2020-01-14): The heap doesn't respect alignment.
- // Instead, we overallocate by `alignment + size_of(rawptr) - 1`, and insert
- // padding. We also store the original pointer returned by heap_alloc right before
- // the pointer we return to the user.
- //
-
- aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) {
- a := max(alignment, align_of(rawptr))
- space := size + a - 1
-
- allocated_mem: rawptr
- if old_ptr != nil {
- original_old_ptr := mem.ptr_offset((^rawptr)(old_ptr), -1)^
- allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
- } else {
- allocated_mem = heap_alloc(space+size_of(rawptr))
- }
- aligned_mem := rawptr(mem.ptr_offset((^u8)(allocated_mem), size_of(rawptr)))
-
- ptr := uintptr(aligned_mem)
- aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a)
- diff := int(aligned_ptr - ptr)
- if (size + diff) > space || allocated_mem == nil {
- return nil, .Out_Of_Memory
- }
-
- aligned_mem = rawptr(aligned_ptr)
- mem.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem
-
- return mem.byte_slice(aligned_mem, size), nil
- }
-
- aligned_free :: proc(p: rawptr) {
- if p != nil {
- heap_free(mem.ptr_offset((^rawptr)(p), -1)^)
- }
- }
-
- aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> (new_memory: []byte, err: mem.Allocator_Error) {
- if p == nil {
- return nil, nil
- }
-
- return aligned_alloc(new_size, new_alignment, p)
- }
-
- switch mode {
- case .Alloc, .Alloc_Non_Zeroed:
- return aligned_alloc(size, alignment)
-
- case .Free:
- aligned_free(old_memory)
-
- case .Free_All:
- return nil, .Mode_Not_Implemented
-
- case .Resize, .Resize_Non_Zeroed:
- if old_memory == nil {
- return aligned_alloc(size, alignment)
- }
- return aligned_resize(old_memory, old_size, size, alignment)
-
- case .Query_Features:
- set := (^mem.Allocator_Mode_Set)(old_memory)
- if set != nil {
- set^ = {.Alloc, .Free, .Resize, .Query_Features}
- }
- return nil, nil
-
- case .Query_Info:
- return nil, .Mode_Not_Implemented
- }
-
- return nil, nil
-}
-
-heap_alloc :: proc(size: int) -> rawptr {
- if size >= DIRECT_MMAP_THRESHOLD {
- return _direct_mmap_alloc(size)
- }
-
- // atomically check if the local region has been stolen
- if _local_region != nil {
- res := sync.atomic_compare_exchange_strong_explicit(
- &_local_region.hdr.local_addr,
- &_local_region,
- CURRENTLY_ACTIVE,
- .Acquire,
- .Relaxed,
- )
- if res != &_local_region {
- // At this point, the region has been stolen and res contains the unexpected value
- expected := res
- if res != CURRENTLY_ACTIVE {
- expected = res
- res = sync.atomic_compare_exchange_strong_explicit(
- &_local_region.hdr.local_addr,
- expected,
- CURRENTLY_ACTIVE,
- .Acquire,
- .Relaxed,
- )
- }
- if res != expected {
- _local_region = nil
- }
- }
- }
-
- size := size
- size = _round_up_to_nearest(size, BLOCK_SIZE)
- blocks_needed := u16(max(MINIMUM_BLOCK_COUNT, size / BLOCK_SIZE))
-
- // retrieve a region if new thread or stolen
- if _local_region == nil {
- _local_region, _ = _region_retrieve_with_space(blocks_needed)
- if _local_region == nil {
- return nil
- }
- }
- defer sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release)
-
- // At this point we have a usable region. Let's find the user some memory
- idx: u16
- local_region_idx := _region_get_local_idx()
- back_idx := -1
- infinite: for {
- for i := 0; i < int(_local_region.hdr.free_list_len); i += 1 {
- idx = _local_region.hdr.free_list[i]
- #no_bounds_check if _get_block_count(_local_region.memory[idx]) >= blocks_needed {
- break infinite
- }
- }
- sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release)
- _local_region, back_idx = _region_retrieve_with_space(blocks_needed, local_region_idx, back_idx)
- }
- user_ptr, used := _region_get_block(_local_region, idx, blocks_needed)
-
- sync.atomic_sub_explicit(&_local_region.hdr.free_blocks, used + 1, .Release)
-
- // If this memory was ever used before, it now needs to be zero'd.
- if idx < _local_region.hdr.last_used {
- mem.zero(user_ptr, int(used) * BLOCK_SIZE)
- } else {
- _local_region.hdr.last_used = idx + used
- }
-
- return user_ptr
-}
-
-heap_resize :: proc(old_memory: rawptr, new_size: int) -> rawptr #no_bounds_check {
- alloc := _get_allocation_header(old_memory)
- if alloc.requested & IS_DIRECT_MMAP > 0 {
- return _direct_mmap_resize(alloc, new_size)
- }
-
- if new_size > DIRECT_MMAP_THRESHOLD {
- return _direct_mmap_from_region(alloc, new_size)
- }
-
- return _region_resize(alloc, new_size)
-}
-
-heap_free :: proc(memory: rawptr) {
- alloc := _get_allocation_header(memory)
- if sync.atomic_load(&alloc.requested) & IS_DIRECT_MMAP == IS_DIRECT_MMAP {
- _direct_mmap_free(alloc)
- return
- }
-
- assert(alloc.free_idx == NOT_FREE)
-
- _region_find_and_assign_local(alloc)
- _region_local_free(alloc)
- sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release)
-}
-
-//
-// Regions
-//
-_new_region :: proc() -> ^Region #no_bounds_check {
- ptr, errno := linux.mmap(0, uint(SIZE_OF_REGION), MMAP_PROT, MMAP_FLAGS, -1, 0)
- if errno != .NONE {
- return nil
- }
- new_region := (^Region)(ptr)
-
- new_region.hdr.local_addr = CURRENTLY_ACTIVE
- new_region.hdr.reset_addr = &_local_region
-
- free_list_blocks := _round_up_to_nearest(FREE_LIST_DEFAULT_CAP, FREE_LIST_ENTRIES_PER_BLOCK)
- _region_assign_free_list(new_region, &new_region.memory[1], u16(free_list_blocks) * FREE_LIST_ENTRIES_PER_BLOCK)
-
- // + 2 to account for free_list's allocation header
- first_user_block := len(new_region.hdr.free_list) / FREE_LIST_ENTRIES_PER_BLOCK + 2
-
- // first allocation header (this is a free list)
- new_region.memory[0].next = u16(first_user_block)
- new_region.memory[0].free_idx = NOT_FREE
- new_region.memory[first_user_block].idx = u16(first_user_block)
- new_region.memory[first_user_block].next = BLOCKS_PER_REGION - 1
-
- // add the first user block to the free list
- new_region.hdr.free_list[0] = u16(first_user_block)
- new_region.hdr.free_list_len = 1
- new_region.hdr.free_blocks = _get_block_count(new_region.memory[first_user_block]) + 1
-
- for r := sync.atomic_compare_exchange_strong(&global_regions, nil, new_region);
- r != nil;
- r = sync.atomic_compare_exchange_strong(&r.hdr.next_region, nil, new_region) {}
-
- return new_region
-}
-
-_region_resize :: proc(alloc: ^Allocation_Header, new_size: int, alloc_is_free_list: bool = false) -> rawptr #no_bounds_check {
- assert(alloc.free_idx == NOT_FREE)
-
- old_memory := mem.ptr_offset(alloc, 1)
-
- old_block_count := _get_block_count(alloc^)
- new_block_count := u16(
- max(MINIMUM_BLOCK_COUNT, _round_up_to_nearest(new_size, BLOCK_SIZE) / BLOCK_SIZE),
- )
- if new_block_count < old_block_count {
- if new_block_count - old_block_count >= MINIMUM_BLOCK_COUNT {
- _region_find_and_assign_local(alloc)
- _region_segment(_local_region, alloc, new_block_count, alloc.free_idx)
- new_block_count = _get_block_count(alloc^)
- sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release)
- }
- // need to zero anything within the new block that that lies beyond new_size
- extra_bytes := int(new_block_count * BLOCK_SIZE) - new_size
- extra_bytes_ptr := mem.ptr_offset((^u8)(alloc), new_size + BLOCK_SIZE)
- mem.zero(extra_bytes_ptr, extra_bytes)
- return old_memory
- }
-
- if !alloc_is_free_list {
- _region_find_and_assign_local(alloc)
- }
- defer if !alloc_is_free_list {
- sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release)
- }
-
- // First, let's see if we can grow in place.
- if alloc.next != BLOCKS_PER_REGION - 1 && _local_region.memory[alloc.next].free_idx != NOT_FREE {
- next_alloc := _local_region.memory[alloc.next]
- total_available := old_block_count + _get_block_count(next_alloc) + 1
- if total_available >= new_block_count {
- alloc.next = next_alloc.next
- _local_region.memory[alloc.next].prev = alloc.idx
- if total_available - new_block_count > BLOCK_SEGMENT_THRESHOLD {
- _region_segment(_local_region, alloc, new_block_count, next_alloc.free_idx)
- } else {
- _region_free_list_remove(_local_region, next_alloc.free_idx)
- }
- mem.zero(&_local_region.memory[next_alloc.idx], int(alloc.next - next_alloc.idx) * BLOCK_SIZE)
- _local_region.hdr.last_used = max(alloc.next, _local_region.hdr.last_used)
- _local_region.hdr.free_blocks -= (_get_block_count(alloc^) - old_block_count)
- if alloc_is_free_list {
- _region_assign_free_list(_local_region, old_memory, _get_block_count(alloc^))
- }
- return old_memory
- }
- }
-
- // If we made it this far, we need to resize, copy, zero and free.
- region_iter := _local_region
- local_region_idx := _region_get_local_idx()
- back_idx := -1
- idx: u16
- infinite: for {
- for i := 0; i < len(region_iter.hdr.free_list); i += 1 {
- idx = region_iter.hdr.free_list[i]
- if _get_block_count(region_iter.memory[idx]) >= new_block_count {
- break infinite
- }
- }
- if region_iter != _local_region {
- sync.atomic_store_explicit(
- ®ion_iter.hdr.local_addr,
- region_iter.hdr.reset_addr,
- .Release,
- )
- }
- region_iter, back_idx = _region_retrieve_with_space(new_block_count, local_region_idx, back_idx)
- }
- if region_iter != _local_region {
- sync.atomic_store_explicit(
- ®ion_iter.hdr.local_addr,
- region_iter.hdr.reset_addr,
- .Release,
- )
- }
-
- // copy from old memory
- new_memory, used_blocks := _region_get_block(region_iter, idx, new_block_count)
- mem.copy(new_memory, old_memory, int(old_block_count * BLOCK_SIZE))
-
- // zero any new memory
- addon_section := mem.ptr_offset((^Allocation_Header)(new_memory), old_block_count)
- new_blocks := used_blocks - old_block_count
- mem.zero(addon_section, int(new_blocks) * BLOCK_SIZE)
-
- region_iter.hdr.free_blocks -= (used_blocks + 1)
-
- // Set free_list before freeing.
- if alloc_is_free_list {
- _region_assign_free_list(_local_region, new_memory, used_blocks)
- }
-
- // free old memory
- _region_local_free(alloc)
- return new_memory
-}
-
-_region_local_free :: proc(alloc: ^Allocation_Header) #no_bounds_check {
- alloc := alloc
- add_to_free_list := true
-
- idx := sync.atomic_load(&alloc.idx)
- prev := sync.atomic_load(&alloc.prev)
- next := sync.atomic_load(&alloc.next)
- block_count := next - idx - 1
- free_blocks := sync.atomic_load(&_local_region.hdr.free_blocks) + block_count + 1
- sync.atomic_store_explicit(&_local_region.hdr.free_blocks, free_blocks, .Release)
-
- // try to merge with prev
- if idx > 0 && sync.atomic_load(&_local_region.memory[prev].free_idx) != NOT_FREE {
- sync.atomic_store_explicit(&_local_region.memory[prev].next, next, .Release)
- _local_region.memory[next].prev = prev
- alloc = &_local_region.memory[prev]
- add_to_free_list = false
- }
-
- // try to merge with next
- if next < BLOCKS_PER_REGION - 1 && sync.atomic_load(&_local_region.memory[next].free_idx) != NOT_FREE {
- old_next := next
- sync.atomic_store_explicit(&alloc.next, sync.atomic_load(&_local_region.memory[old_next].next), .Release)
-
- sync.atomic_store_explicit(&_local_region.memory[next].prev, idx, .Release)
-
- if add_to_free_list {
- sync.atomic_store_explicit(&_local_region.hdr.free_list[_local_region.memory[old_next].free_idx], idx, .Release)
- sync.atomic_store_explicit(&alloc.free_idx, _local_region.memory[old_next].free_idx, .Release)
- } else {
- // NOTE: We have aleady merged with prev, and now merged with next.
- // Now, we are actually going to remove from the free_list.
- _region_free_list_remove(_local_region, _local_region.memory[old_next].free_idx)
- }
- add_to_free_list = false
- }
-
- // This is the only place where anything is appended to the free list.
- if add_to_free_list {
- fl := _local_region.hdr.free_list
- fl_len := sync.atomic_load(&_local_region.hdr.free_list_len)
- sync.atomic_store_explicit(&alloc.free_idx, fl_len, .Release)
- fl[alloc.free_idx] = idx
- sync.atomic_store_explicit(&_local_region.hdr.free_list_len, fl_len + 1, .Release)
- if int(fl_len + 1) == len(fl) {
- free_alloc := _get_allocation_header(mem.raw_data(_local_region.hdr.free_list))
- _region_resize(free_alloc, len(fl) * 2 * size_of(fl[0]), true)
- }
- }
-}
-
-_region_assign_free_list :: proc(region: ^Region, memory: rawptr, blocks: u16) {
- raw_free_list := transmute(mem.Raw_Slice)region.hdr.free_list
- raw_free_list.len = int(blocks) * FREE_LIST_ENTRIES_PER_BLOCK
- raw_free_list.data = memory
- region.hdr.free_list = transmute([]u16)(raw_free_list)
-}
-
-_region_retrieve_with_space :: proc(blocks: u16, local_idx: int = -1, back_idx: int = -1) -> (^Region, int) {
- r: ^Region
- idx: int
- for r = sync.atomic_load(&global_regions); r != nil; r = r.hdr.next_region {
- if idx == local_idx || idx < back_idx || sync.atomic_load(&r.hdr.free_blocks) < blocks {
- idx += 1
- continue
- }
- idx += 1
- local_addr: ^^Region = sync.atomic_load(&r.hdr.local_addr)
- if local_addr != CURRENTLY_ACTIVE {
- res := sync.atomic_compare_exchange_strong_explicit(
- &r.hdr.local_addr,
- local_addr,
- CURRENTLY_ACTIVE,
- .Acquire,
- .Relaxed,
- )
- if res == local_addr {
- r.hdr.reset_addr = local_addr
- return r, idx
- }
- }
- }
-
- return _new_region(), idx
-}
-
-_region_retrieve_from_addr :: proc(addr: rawptr) -> ^Region {
- r: ^Region
- for r = global_regions; r != nil; r = r.hdr.next_region {
- if _region_contains_mem(r, addr) {
- return r
- }
- }
- unreachable()
-}
-
-_region_get_block :: proc(region: ^Region, idx, blocks_needed: u16) -> (rawptr, u16) #no_bounds_check {
- alloc := ®ion.memory[idx]
-
- assert(alloc.free_idx != NOT_FREE)
- assert(alloc.next > 0)
-
- block_count := _get_block_count(alloc^)
- if block_count - blocks_needed > BLOCK_SEGMENT_THRESHOLD {
- _region_segment(region, alloc, blocks_needed, alloc.free_idx)
- } else {
- _region_free_list_remove(region, alloc.free_idx)
- }
-
- alloc.free_idx = NOT_FREE
- return mem.ptr_offset(alloc, 1), _get_block_count(alloc^)
-}
-
-_region_segment :: proc(region: ^Region, alloc: ^Allocation_Header, blocks, new_free_idx: u16) #no_bounds_check {
- old_next := alloc.next
- alloc.next = alloc.idx + blocks + 1
- region.memory[old_next].prev = alloc.next
-
- // Initialize alloc.next allocation header here.
- region.memory[alloc.next].prev = alloc.idx
- region.memory[alloc.next].next = old_next
- region.memory[alloc.next].idx = alloc.next
- region.memory[alloc.next].free_idx = new_free_idx
-
- // Replace our original spot in the free_list with new segment.
- region.hdr.free_list[new_free_idx] = alloc.next
-}
-
-_region_get_local_idx :: proc() -> int {
- idx: int
- for r := sync.atomic_load(&global_regions); r != nil; r = r.hdr.next_region {
- if r == _local_region {
- return idx
- }
- idx += 1
- }
-
- return -1
-}
-
-_region_find_and_assign_local :: proc(alloc: ^Allocation_Header) {
- // Find the region that contains this memory
- if !_region_contains_mem(_local_region, alloc) {
- _local_region = _region_retrieve_from_addr(alloc)
- }
-
- // At this point, _local_region is set correctly. Spin until acquire
- res := CURRENTLY_ACTIVE
-
- for res == CURRENTLY_ACTIVE {
- res = sync.atomic_compare_exchange_strong_explicit(
- &_local_region.hdr.local_addr,
- &_local_region,
- CURRENTLY_ACTIVE,
- .Acquire,
- .Relaxed,
- )
- }
-}
-
-_region_contains_mem :: proc(r: ^Region, memory: rawptr) -> bool #no_bounds_check {
- if r == nil {
- return false
- }
- mem_int := uintptr(memory)
- return mem_int >= uintptr(&r.memory[0]) && mem_int <= uintptr(&r.memory[BLOCKS_PER_REGION - 1])
-}
-
-_region_free_list_remove :: proc(region: ^Region, free_idx: u16) #no_bounds_check {
- // pop, swap and update allocation hdr
- if n := region.hdr.free_list_len - 1; free_idx != n {
- region.hdr.free_list[free_idx] = sync.atomic_load(®ion.hdr.free_list[n])
- alloc_idx := region.hdr.free_list[free_idx]
- sync.atomic_store_explicit(®ion.memory[alloc_idx].free_idx, free_idx, .Release)
- }
- region.hdr.free_list_len -= 1
-}
-
-//
-// Direct mmap
-//
-_direct_mmap_alloc :: proc(size: int) -> rawptr {
- mmap_size := _round_up_to_nearest(size + BLOCK_SIZE, PAGE_SIZE)
- new_allocation, errno := linux.mmap(0, uint(mmap_size), MMAP_PROT, MMAP_FLAGS, -1, 0)
- if errno != .NONE {
- return nil
- }
-
- alloc := (^Allocation_Header)(uintptr(new_allocation))
- alloc.requested = u64(size) // NOTE: requested = requested size
- alloc.requested += IS_DIRECT_MMAP
- return rawptr(mem.ptr_offset(alloc, 1))
-}
-
-_direct_mmap_resize :: proc(alloc: ^Allocation_Header, new_size: int) -> rawptr {
- old_requested := int(alloc.requested & REQUESTED_MASK)
- old_mmap_size := _round_up_to_nearest(old_requested + BLOCK_SIZE, PAGE_SIZE)
- new_mmap_size := _round_up_to_nearest(new_size + BLOCK_SIZE, PAGE_SIZE)
- if int(new_mmap_size) < MMAP_TO_REGION_SHRINK_THRESHOLD {
- return _direct_mmap_to_region(alloc, new_size)
- } else if old_requested == new_size {
- return mem.ptr_offset(alloc, 1)
- }
-
- new_allocation, errno := linux.mremap(alloc, uint(old_mmap_size), uint(new_mmap_size), {.MAYMOVE})
- if errno != .NONE {
- return nil
- }
-
- new_header := (^Allocation_Header)(uintptr(new_allocation))
- new_header.requested = u64(new_size)
- new_header.requested += IS_DIRECT_MMAP
-
- if new_mmap_size > old_mmap_size {
- // new section may not be pointer aligned, so cast to ^u8
- new_section := mem.ptr_offset((^u8)(new_header), old_requested + BLOCK_SIZE)
- mem.zero(new_section, new_mmap_size - old_mmap_size)
- }
- return mem.ptr_offset(new_header, 1)
-
-}
-
-_direct_mmap_from_region :: proc(alloc: ^Allocation_Header, new_size: int) -> rawptr {
- new_memory := _direct_mmap_alloc(new_size)
- if new_memory != nil {
- old_memory := mem.ptr_offset(alloc, 1)
- mem.copy(new_memory, old_memory, int(_get_block_count(alloc^)) * BLOCK_SIZE)
- }
- _region_find_and_assign_local(alloc)
- _region_local_free(alloc)
- sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release)
- return new_memory
-}
-
-_direct_mmap_to_region :: proc(alloc: ^Allocation_Header, new_size: int) -> rawptr {
- new_memory := heap_alloc(new_size)
- if new_memory != nil {
- mem.copy(new_memory, mem.ptr_offset(alloc, -1), new_size)
- _direct_mmap_free(alloc)
- }
- return new_memory
-}
-
-_direct_mmap_free :: proc(alloc: ^Allocation_Header) {
- requested := int(alloc.requested & REQUESTED_MASK)
- mmap_size := _round_up_to_nearest(requested + BLOCK_SIZE, PAGE_SIZE)
- linux.munmap(alloc, uint(mmap_size))
-}
-
-//
-// Util
-//
-
-_get_block_count :: #force_inline proc(alloc: Allocation_Header) -> u16 {
- return alloc.next - alloc.idx - 1
-}
-
-_get_allocation_header :: #force_inline proc(raw_mem: rawptr) -> ^Allocation_Header {
- return mem.ptr_offset((^Allocation_Header)(raw_mem), -1)
-}
-
-_round_up_to_nearest :: #force_inline proc(size, round: int) -> int {
- return (size-1) + round - (size-1) % round
-}
+import "base:runtime"
+_heap_allocator_proc :: runtime.heap_allocator_proc
diff --git a/core/os/os2/heap_wasi.odin b/core/os/os2/heap_wasi.odin
new file mode 100644
index 000000000..7da3c4845
--- /dev/null
+++ b/core/os/os2/heap_wasi.odin
@@ -0,0 +1,6 @@
+#+private
+package os2
+
+import "base:runtime"
+
+_heap_allocator_proc :: runtime.wasm_allocator_proc
diff --git a/core/os/os2/internal_util.odin b/core/os/os2/internal_util.odin
index 164e1e1be..ce253d17b 100644
--- a/core/os/os2/internal_util.odin
+++ b/core/os/os2/internal_util.odin
@@ -3,6 +3,7 @@ package os2
import "base:intrinsics"
import "base:runtime"
+import "core:math/rand"
// Splits pattern by the last wildcard "*", if it exists, and returns the prefix and suffix
@@ -84,45 +85,15 @@ concatenate :: proc(strings: []string, allocator: runtime.Allocator) -> (res: st
return string(buf), nil
}
-
-
-@(private="file")
-random_string_seed: [2]u64
-
-@(init, private="file")
-init_random_string_seed :: proc() {
- seed := u64(intrinsics.read_cycle_counter())
- s := &random_string_seed
- s[0] = 0
- s[1] = (seed << 1) | 1
- _ = next_random(s)
- s[1] += seed
- _ = next_random(s)
-}
-
-@(require_results)
-next_random :: proc(r: ^[2]u64) -> u64 {
- old_state := r[0]
- r[0] = old_state * 6364136223846793005 + (r[1]|1)
- xor_shifted := (((old_state >> 59) + 5) ~ old_state) * 12605985483714917081
- rot := (old_state >> 59)
- return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 63))
-}
-
@(require_results)
random_string :: proc(buf: []byte) -> string {
- @(static, rodata) digits := "0123456789"
-
- u := next_random(&random_string_seed)
-
- b :: 10
- i := len(buf)
- for u >= b {
- i -= 1
- buf[i] = digits[u % b]
- u /= b
+ for i := 0; i < len(buf); i += 16 {
+ n := rand.uint64()
+ end := min(i + 16, len(buf))
+ for j := i; j < end; j += 1 {
+ buf[j] = '0' + u8(n) % 10
+ n >>= 4
+ }
}
- i -= 1
- buf[i] = digits[u % b]
- return string(buf[i:])
+ return string(buf)
}
diff --git a/core/os/os2/path.odin b/core/os/os2/path.odin
index 254950d68..47ac0236d 100644
--- a/core/os/os2/path.odin
+++ b/core/os/os2/path.odin
@@ -2,10 +2,18 @@ package os2
import "base:runtime"
+import "core:strings"
+
Path_Separator :: _Path_Separator // OS-Specific
Path_Separator_String :: _Path_Separator_String // OS-Specific
Path_List_Separator :: _Path_List_Separator // OS-Specific
+#assert(_Path_Separator <= rune(0x7F), "The system-specific path separator rune is expected to be within the 7-bit ASCII character set.")
+
+/*
+Return true if `c` is a character used to separate paths into directory and
+file hierarchies on the current system.
+*/
@(require_results)
is_path_separator :: proc(c: byte) -> bool {
return _is_path_separator(c)
@@ -13,22 +21,42 @@ is_path_separator :: proc(c: byte) -> bool {
mkdir :: make_directory
+/*
+Make a new directory.
+
+If `path` is relative, it will be relative to the process's current working directory.
+*/
make_directory :: proc(name: string, perm: int = 0o755) -> Error {
return _mkdir(name, perm)
}
mkdir_all :: make_directory_all
+/*
+Make a new directory, creating new intervening directories when needed.
+
+If `path` is relative, it will be relative to the process's current working directory.
+*/
make_directory_all :: proc(path: string, perm: int = 0o755) -> Error {
return _mkdir_all(path, perm)
}
+/*
+Delete `path` and all files and directories inside of `path` if it is a directory.
+
+If `path` is relative, it will be relative to the process's current working directory.
+*/
remove_all :: proc(path: string) -> Error {
return _remove_all(path)
}
getwd :: get_working_directory
+/*
+Get the working directory of the current process.
+
+*Allocates Using Provided Allocator*
+*/
@(require_results)
get_working_directory :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
return _get_working_directory(allocator)
@@ -36,6 +64,399 @@ get_working_directory :: proc(allocator: runtime.Allocator) -> (dir: string, err
setwd :: set_working_directory
+/*
+Change the working directory of the current process.
+
+*Allocates Using Provided Allocator*
+*/
set_working_directory :: proc(dir: string) -> (err: Error) {
return _set_working_directory(dir)
}
+
+/*
+Get the path for the currently running executable.
+
+*Allocates Using Provided Allocator*
+*/
+@(require_results)
+get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err: Error) {
+ return _get_executable_path(allocator)
+}
+
+/*
+Get the directory for the currently running executable.
+
+*Allocates Using Provided Allocator*
+*/
+@(require_results)
+get_executable_directory :: proc(allocator: runtime.Allocator) -> (path: string, err: Error) {
+ path = _get_executable_path(allocator) or_return
+ path, _ = split_path(path)
+ return
+}
+
+/*
+Compare two paths for exactness without normalization.
+
+This procedure takes into account case-sensitivity on differing systems.
+*/
+@(require_results)
+are_paths_identical :: proc(a, b: string) -> (identical: bool) {
+ return _are_paths_identical(a, b)
+}
+
+/*
+Normalize a path.
+
+*Allocates Using Provided Allocator*
+
+This will remove duplicate separators and unneeded references to the current or
+parent directory.
+*/
+@(require_results)
+clean_path :: proc(path: string, allocator: runtime.Allocator) -> (cleaned: string, err: Error) {
+ if path == "" || path == "." {
+ return strings.clone(".", allocator)
+ }
+
+ TEMP_ALLOCATOR_GUARD()
+
+ // The extra byte is to simplify appending path elements by letting the
+ // loop to end each with a separator. We'll trim the last one when we're done.
+ buffer := make([]u8, len(path) + 1, temp_allocator()) or_return
+
+ // This is the only point where Windows and POSIX differ, as Windows has
+ // alphabet-based volumes for root paths.
+ rooted, start := _clean_path_handle_start(path, buffer)
+
+ head, buffer_i := start, start
+ for i, j := start, start; i <= len(path); i += 1 {
+ if i == len(path) || _is_path_separator(path[i]) {
+ elem := path[j:i]
+ j = i + 1
+
+ switch elem {
+ case "", ".":
+ // Skip duplicate path separators and current directory references.
+ case "..":
+ if !rooted && buffer_i == head {
+ // Only allow accessing further parent directories when the path is relative.
+ buffer[buffer_i] = '.'
+ buffer[buffer_i+1] = '.'
+ buffer[buffer_i+2] = _Path_Separator
+ buffer_i += 3
+ head = buffer_i
+ } else {
+ // Roll back to the last separator or the head of the buffer.
+ back_to := head
+ // `buffer_i` will be equal to 1 + the last set byte, so
+ // skipping two bytes avoids the final separator we just
+ // added.
+ for k := buffer_i-2; k >= head; k -= 1 {
+ if _is_path_separator(buffer[k]) {
+ back_to = k + 1
+ break
+ }
+ }
+ buffer_i = back_to
+ }
+ case:
+ // Copy the path element verbatim and add a separator.
+ copy(buffer[buffer_i:], elem)
+ buffer_i += len(elem)
+ buffer[buffer_i] = _Path_Separator
+ buffer_i += 1
+ }
+ }
+ }
+
+ // Trim the final separator.
+ // NOTE: No need to check if the last byte is a separator, as we always add it.
+ if buffer_i > start {
+ buffer_i -= 1
+ }
+
+ if buffer_i == 0 {
+ return strings.clone(".", allocator)
+ }
+
+ compact := make([]u8, buffer_i, allocator) or_return
+ copy(compact, buffer) // NOTE(bill): buffer[:buffer_i] is redundant here
+ return string(compact), nil
+}
+
+/*
+Return true if `path` is an absolute path as opposed to a relative one.
+*/
+@(require_results)
+is_absolute_path :: proc(path: string) -> bool {
+ return _is_absolute_path(path)
+}
+
+/*
+Get the absolute path to `path` with respect to the process's current directory.
+
+*Allocates Using Provided Allocator*
+*/
+@(require_results)
+get_absolute_path :: proc(path: string, allocator: runtime.Allocator) -> (absolute_path: string, err: Error) {
+ return _get_absolute_path(path, allocator)
+}
+
+/*
+Get the relative path needed to change directories from `base` to `target`.
+
+*Allocates Using Provided Allocator*
+
+The result is such that `join_path(base, get_relative_path(base, target))` is equivalent to `target`.
+
+NOTE: This procedure expects both `base` and `target` to be normalized first,
+which can be done by calling `clean_path` on them if needed.
+
+This procedure will return an `Invalid_Path` error if `base` begins with a
+reference to the parent directory (`".."`). Use `get_working_directory` with
+`join_path` to construct absolute paths for both arguments instead.
+*/
+@(require_results)
+get_relative_path :: proc(base, target: string, allocator: runtime.Allocator) -> (path: string, err: Error) {
+ if _are_paths_identical(base, target) {
+ return strings.clone(".", allocator)
+ }
+ if base == "." {
+ return strings.clone(target, allocator)
+ }
+
+ // This is the first point where Windows and POSIX differ, as Windows has
+ // alphabet-based volumes for root paths.
+ if !_get_relative_path_handle_start(base, target) {
+ return "", .Invalid_Path
+ }
+ if strings.has_prefix(base, "..") && (len(base) == 2 || _is_path_separator(base[2])) {
+ // We could do the work for the user of getting absolute paths for both
+ // arguments, but that could make something costly (repeatedly
+ // normalizing paths) convenient, when it would be better for the user
+ // to store already-finalized paths and operate on those instead.
+ return "", .Invalid_Path
+ }
+
+ // This is the other point where Windows and POSIX differ, as Windows is
+ // case-insensitive.
+ common := _get_common_path_len(base, target)
+
+ // Get the result of splitting `base` and `target` on _Path_Separator,
+ // comparing them up to their most common elements, then count how many
+ // unshared parts are in the split `base`.
+ seps := 0
+ size := 0
+ if len(base)-common > 0 {
+ seps = 1
+ size = 2
+ }
+ // This range skips separators on the ends of the string.
+ for i in common+1.. 0 {
+ // Account for leading separators on the target after cutting the common part.
+ // (i.e. base == `/home`, target == `/home/a`)
+ if _is_path_separator(trailing[0]) {
+ trailing = trailing[1:]
+ }
+ size += len(trailing)
+ if seps > 0 {
+ size += 1
+ }
+ }
+ if trailing == "." {
+ trailing = ""
+ size -= 2
+ }
+
+ // Build the string.
+ buf := make([]u8, size, allocator) or_return
+ n := 0
+ if seps > 0 {
+ buf[0] = '.'
+ buf[1] = '.'
+ n = 2
+ }
+ for _ in 1.. 0 {
+ if seps > 0 {
+ buf[n] = _Path_Separator
+ n += 1
+ }
+ copy(buf[n:], trailing)
+ }
+
+ path = string(buf)
+
+ return
+}
+
+/*
+Split a path into a directory hierarchy and a filename.
+
+For example, `split_path("/home/foo/bar.tar.gz")` will return `"/home/foo"` and `"bar.tar.gz"`.
+*/
+@(require_results)
+split_path :: proc(path: string) -> (dir, filename: string) {
+ return _split_path(path)
+}
+
+/*
+Join all `elems` with the system's path separator and normalize the result.
+
+*Allocates Using Provided Allocator*
+
+For example, `join_path({"/home", "foo", "bar.txt"})` will result in `"/home/foo/bar.txt"`.
+*/
+@(require_results)
+join_path :: proc(elems: []string, allocator: runtime.Allocator) -> (joined: string, err: Error) {
+ for e, i in elems {
+ if e != "" {
+ TEMP_ALLOCATOR_GUARD()
+ p := strings.join(elems[i:], Path_Separator_String, temp_allocator()) or_return
+ return clean_path(p, allocator)
+ }
+ }
+ return "", nil
+}
+
+/*
+Split a filename from its extension.
+
+This procedure splits on the last separator.
+
+If the filename begins with a separator, such as `".readme.txt"`, the separator
+will be included in the filename, resulting in `".readme"` and `"txt"`.
+
+For example, `split_filename("foo.tar.gz")` will return `"foo.tar"` and `"gz"`.
+*/
+@(require_results)
+split_filename :: proc(filename: string) -> (base, ext: string) {
+ i := strings.last_index_byte(filename, '.')
+ if i <= 0 {
+ return filename, ""
+ }
+ return filename[:i], filename[i+1:]
+}
+
+/*
+Split a filename from its extension.
+
+This procedure splits on the first separator.
+
+If the filename begins with a separator, such as `".readme.txt.gz"`, the separator
+will be included in the filename, resulting in `".readme"` and `"txt.gz"`.
+
+For example, `split_filename_all("foo.tar.gz")` will return `"foo"` and `"tar.gz"`.
+*/
+@(require_results)
+split_filename_all :: proc(filename: string) -> (base, ext: string) {
+ i := strings.index_byte(filename, '.')
+ if i == 0 {
+ j := strings.index_byte(filename[1:], '.')
+ if j != -1 {
+ j += 1
+ }
+ i = j
+ }
+ if i == -1 {
+ return filename, ""
+ }
+ return filename[:i], filename[i+1:]
+}
+
+/*
+Join `base` and `ext` with the system's filename extension separator.
+
+*Allocates Using Provided Allocator*
+
+For example, `join_filename("foo", "tar.gz")` will result in `"foo.tar.gz"`.
+*/
+@(require_results)
+join_filename :: proc(base: string, ext: string, allocator: runtime.Allocator) -> (joined: string, err: Error) {
+ if len(base) == 0 {
+ return strings.clone(ext, allocator)
+ } else if len(ext) == 0 {
+ return strings.clone(base, allocator)
+ }
+
+ buf := make([]u8, len(base) + 1 + len(ext), allocator) or_return
+ copy(buf, base)
+ buf[len(base)] = '.'
+ copy(buf[1+len(base):], ext)
+
+ return string(buf), nil
+}
+
+/*
+Split a string that is separated by a system-specific separator, typically used
+for environment variables specifying multiple directories.
+
+*Allocates Using Provided Allocator*
+
+For example, there is the "PATH" environment variable on POSIX systems which
+this procedure can split into separate entries.
+*/
+@(require_results)
+split_path_list :: proc(path: string, allocator: runtime.Allocator) -> (list: []string, err: Error) {
+ if path == "" {
+ return nil, nil
+ }
+
+ start: int
+ quote: bool
+
+ start, quote = 0, false
+ count := 0
+
+ for i := 0; i < len(path); i += 1 {
+ c := path[i]
+ switch {
+ case c == '"':
+ quote = !quote
+ case c == Path_List_Separator && !quote:
+ count += 1
+ }
+ }
+
+ start, quote = 0, false
+ list = make([]string, count + 1, allocator) or_return
+ index := 0
+ for i := 0; i < len(path); i += 1 {
+ c := path[i]
+ switch {
+ case c == '"':
+ quote = !quote
+ case c == Path_List_Separator && !quote:
+ list[index] = path[start:i]
+ index += 1
+ start = i + 1
+ }
+ }
+ assert(index == count)
+ list[index] = path[start:]
+
+ for s0, i in list {
+ s, new := strings.replace_all(s0, `"`, ``, allocator)
+ if !new {
+ s = strings.clone(s, allocator) or_return
+ }
+ list[i] = s
+ }
+
+ return list, nil
+}
diff --git a/core/os/os2/path_darwin.odin b/core/os/os2/path_darwin.odin
new file mode 100644
index 000000000..65aaf1e95
--- /dev/null
+++ b/core/os/os2/path_darwin.odin
@@ -0,0 +1,17 @@
+package os2
+
+import "base:runtime"
+
+import "core:sys/darwin"
+import "core:sys/posix"
+
+_get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err: Error) {
+ buffer: [darwin.PIDPATHINFO_MAXSIZE]byte = ---
+ ret := darwin.proc_pidpath(posix.getpid(), raw_data(buffer[:]), len(buffer))
+ if ret > 0 {
+ return clone_string(string(buffer[:ret]), allocator)
+ }
+
+ err = _get_platform_error()
+ return
+}
diff --git a/core/os/os2/path_freebsd.odin b/core/os/os2/path_freebsd.odin
new file mode 100644
index 000000000..e7e4f63c9
--- /dev/null
+++ b/core/os/os2/path_freebsd.odin
@@ -0,0 +1,29 @@
+package os2
+
+import "base:runtime"
+
+import "core:sys/freebsd"
+import "core:sys/posix"
+
+_get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err: Error) {
+ req := []freebsd.MIB_Identifier{.CTL_KERN, .KERN_PROC, .KERN_PROC_PATHNAME, freebsd.MIB_Identifier(-1)}
+
+ size: uint
+ if ret := freebsd.sysctl(req, nil, &size, nil, 0); ret != .NONE {
+ err = _get_platform_error(posix.Errno(ret))
+ return
+ }
+ assert(size > 0)
+
+ buf := make([]byte, size, allocator) or_return
+ defer if err != nil { delete(buf, allocator) }
+
+ assert(uint(len(buf)) == size)
+
+ if ret := freebsd.sysctl(req, raw_data(buf), &size, nil, 0); ret != .NONE {
+ err = _get_platform_error(posix.Errno(ret))
+ return
+ }
+
+ return string(buf[:size-1]), nil
+}
diff --git a/core/os/os2/path_linux.odin b/core/os/os2/path_linux.odin
index 7be4121ae..410b4cb28 100644
--- a/core/os/os2/path_linux.odin
+++ b/core/os/os2/path_linux.odin
@@ -1,9 +1,10 @@
#+private
package os2
+import "base:runtime"
+
import "core:strings"
import "core:strconv"
-import "base:runtime"
import "core:sys/linux"
_Path_Separator :: '/'
@@ -13,7 +14,7 @@ _Path_List_Separator :: ':'
_OPENDIR_FLAGS : linux.Open_Flags : {.NONBLOCK, .DIRECTORY, .LARGEFILE, .CLOEXEC}
_is_path_separator :: proc(c: byte) -> bool {
- return c == '/'
+ return c == _Path_Separator
}
_mkdir :: proc(path: string, perm: int) -> Error {
@@ -77,8 +78,6 @@ _mkdir_all :: proc(path: string, perm: int) -> Error {
}
_remove_all :: proc(path: string) -> Error {
- DT_DIR :: 4
-
remove_all_dir :: proc(dfd: linux.Fd) -> Error {
n := 64
buf := make([]u8, n)
@@ -173,6 +172,25 @@ _set_working_directory :: proc(dir: string) -> Error {
return _get_platform_error(linux.chdir(dir_cstr))
}
+_get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err: Error) {
+ TEMP_ALLOCATOR_GUARD()
+
+ buf := make([dynamic]byte, 1024, temp_allocator()) or_return
+ for {
+ n, errno := linux.readlink("/proc/self/exe", buf[:])
+ if errno != .NONE {
+ err = _get_platform_error(errno)
+ return
+ }
+
+ if n < len(buf) {
+ return clone_string(string(buf[:n]), allocator)
+ }
+
+ resize(&buf, len(buf)*2) or_return
+ }
+}
+
_get_full_path :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> (fullpath: string, err: Error) {
PROC_FD_PATH :: "/proc/self/fd/"
diff --git a/core/os/os2/path_netbsd.odin b/core/os/os2/path_netbsd.odin
new file mode 100644
index 000000000..f56a91fd6
--- /dev/null
+++ b/core/os/os2/path_netbsd.odin
@@ -0,0 +1,24 @@
+package os2
+
+import "base:runtime"
+
+import "core:sys/posix"
+
+_get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err: Error) {
+ TEMP_ALLOCATOR_GUARD()
+
+ buf := make([dynamic]byte, 1024, temp_allocator()) or_return
+ for {
+ n := posix.readlink("/proc/curproc/exe", raw_data(buf), len(buf))
+ if n < 0 {
+ err = _get_platform_error()
+ return
+ }
+
+ if n < len(buf) {
+ return clone_string(string(buf[:n]), allocator)
+ }
+
+ resize(&buf, len(buf)*2) or_return
+ }
+}
diff --git a/core/os/os2/path_openbsd.odin b/core/os/os2/path_openbsd.odin
new file mode 100644
index 000000000..37b5de927
--- /dev/null
+++ b/core/os/os2/path_openbsd.odin
@@ -0,0 +1,57 @@
+package os2
+
+import "base:runtime"
+
+import "core:strings"
+import "core:sys/posix"
+
+_get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err: Error) {
+ // OpenBSD does not have an API for this, we do our best below.
+
+ if len(runtime.args__) <= 0 {
+ err = .Invalid_Path
+ return
+ }
+
+ real :: proc(path: cstring, allocator: runtime.Allocator) -> (out: string, err: Error) {
+ real := posix.realpath(path)
+ if real == nil {
+ err = _get_platform_error()
+ return
+ }
+ defer posix.free(real)
+ return clone_string(string(real), allocator)
+ }
+
+ arg := runtime.args__[0]
+ sarg := string(arg)
+
+ if len(sarg) == 0 {
+ err = .Invalid_Path
+ return
+ }
+
+ if sarg[0] == '.' || sarg[0] == '/' {
+ return real(arg, allocator)
+ }
+
+ TEMP_ALLOCATOR_GUARD()
+
+ buf := strings.builder_make(temp_allocator())
+
+ paths := get_env("PATH", temp_allocator())
+ for dir in strings.split_iterator(&paths, ":") {
+ strings.builder_reset(&buf)
+ strings.write_string(&buf, dir)
+ strings.write_string(&buf, "/")
+ strings.write_string(&buf, sarg)
+
+ cpath := strings.to_cstring(&buf) or_return
+ if posix.access(cpath, {.X_OK}) == .OK {
+ return real(cpath, allocator)
+ }
+ }
+
+ err = .Invalid_Path
+ return
+}
diff --git a/core/os/os2/path_posix.odin b/core/os/os2/path_posix.odin
index 5ffdac28e..39bd0a188 100644
--- a/core/os/os2/path_posix.odin
+++ b/core/os/os2/path_posix.odin
@@ -3,7 +3,6 @@
package os2
import "base:runtime"
-import "core:path/filepath"
import "core:sys/posix"
@@ -35,11 +34,11 @@ _mkdir_all :: proc(path: string, perm: int) -> Error {
return .Exist
}
- clean_path := filepath.clean(path, temp_allocator())
+ clean_path := clean_path(path, temp_allocator()) or_return
return internal_mkdir_all(clean_path, perm)
internal_mkdir_all :: proc(path: string, perm: int) -> Error {
- dir, file := filepath.split(path)
+ dir, file := split_path(path)
if file != path && dir != "/" {
if len(dir) > 1 && dir[len(dir) - 1] == '/' {
dir = dir[:len(dir) - 1]
@@ -81,7 +80,7 @@ _remove_all :: proc(path: string) -> Error {
fullpath, _ := concatenate({path, "/", string(cname), "\x00"}, temp_allocator())
if entry.d_type == .DIR {
- _remove_all(fullpath[:len(fullpath)-1])
+ _remove_all(fullpath[:len(fullpath)-1]) or_return
} else {
if posix.unlink(cstring(raw_data(fullpath))) != .OK {
return _get_platform_error()
diff --git a/core/os/os2/path_posixfs.odin b/core/os/os2/path_posixfs.odin
new file mode 100644
index 000000000..8f9d43d63
--- /dev/null
+++ b/core/os/os2/path_posixfs.odin
@@ -0,0 +1,78 @@
+#+private
+#+build linux, darwin, netbsd, freebsd, openbsd, wasi
+package os2
+
+// This implementation is for all systems that have POSIX-compliant filesystem paths.
+
+import "base:runtime"
+import "core:strings"
+import "core:sys/posix"
+
+_are_paths_identical :: proc(a, b: string) -> (identical: bool) {
+ return a == b
+}
+
+_clean_path_handle_start :: proc(path: string, buffer: []u8) -> (rooted: bool, start: int) {
+ // Preserve rooted paths.
+ if _is_path_separator(path[0]) {
+ rooted = true
+ buffer[0] = _Path_Separator
+ start = 1
+ }
+ return
+}
+
+_is_absolute_path :: proc(path: string) -> bool {
+ return len(path) > 0 && _is_path_separator(path[0])
+}
+
+_get_absolute_path :: proc(path: string, allocator: runtime.Allocator) -> (absolute_path: string, err: Error) {
+ rel := path
+ if rel == "" {
+ rel = "."
+ }
+ TEMP_ALLOCATOR_GUARD()
+ rel_cstr := strings.clone_to_cstring(rel, temp_allocator())
+ path_ptr := posix.realpath(rel_cstr, nil)
+ if path_ptr == nil {
+ return "", Platform_Error(posix.errno())
+ }
+ defer posix.free(path_ptr)
+
+ path_str := strings.clone(string(path_ptr), allocator)
+ return path_str, nil
+}
+
+_get_relative_path_handle_start :: proc(base, target: string) -> bool {
+ base_rooted := len(base) > 0 && _is_path_separator(base[0])
+ target_rooted := len(target) > 0 && _is_path_separator(target[0])
+ return base_rooted == target_rooted
+}
+
+_get_common_path_len :: proc(base, target: string) -> int {
+ i := 0
+ end := min(len(base), len(target))
+ for j in 0..=end {
+ if j == end || _is_path_separator(base[j]) {
+ if base[i:j] == target[i:j] {
+ i = j
+ } else {
+ break
+ }
+ }
+ }
+ return i
+}
+
+_split_path :: proc(path: string) -> (dir, file: string) {
+ i := len(path) - 1
+ for i >= 0 && !_is_path_separator(path[i]) {
+ i -= 1
+ }
+ if i == 0 {
+ return path[:i+1], path[i+1:]
+ } else if i > 0 {
+ return path[:i], path[i+1:]
+ }
+ return "", path
+}
diff --git a/core/os/os2/path_wasi.odin b/core/os/os2/path_wasi.odin
new file mode 100644
index 000000000..7aee8fcc0
--- /dev/null
+++ b/core/os/os2/path_wasi.odin
@@ -0,0 +1,116 @@
+#+private
+package os2
+
+import "base:runtime"
+
+import "core:sync"
+import "core:sys/wasm/wasi"
+
+_Path_Separator :: '/'
+_Path_Separator_String :: "/"
+_Path_List_Separator :: ':'
+
+_is_path_separator :: proc(c: byte) -> bool {
+ return c == _Path_Separator
+}
+
+_mkdir :: proc(name: string, perm: int) -> Error {
+ dir_fd, relative, ok := match_preopen(name)
+ if !ok {
+ return .Invalid_Path
+ }
+
+ return _get_platform_error(wasi.path_create_directory(dir_fd, relative))
+}
+
+_mkdir_all :: proc(path: string, perm: int) -> Error {
+ if path == "" {
+ return .Invalid_Path
+ }
+
+ TEMP_ALLOCATOR_GUARD()
+
+ if exists(path) {
+ return .Exist
+ }
+
+ clean_path := clean_path(path, temp_allocator())
+ return internal_mkdir_all(clean_path)
+
+ internal_mkdir_all :: proc(path: string) -> Error {
+ dir, file := split_path(path)
+ if file != path && dir != "/" {
+ if len(dir) > 1 && dir[len(dir) - 1] == '/' {
+ dir = dir[:len(dir) - 1]
+ }
+ internal_mkdir_all(dir) or_return
+ }
+
+ err := _mkdir(path, 0)
+ if err == .Exist { err = nil }
+ return err
+ }
+}
+
+_remove_all :: proc(path: string) -> (err: Error) {
+ // PERF: this works, but wastes a bunch of memory using the read_directory_iterator API
+ // and using open instead of wasi fds directly.
+ {
+ dir := open(path) or_return
+ defer close(dir)
+
+ iter := read_directory_iterator_create(dir)
+ defer read_directory_iterator_destroy(&iter)
+
+ for fi in read_directory_iterator(&iter) {
+ _ = read_directory_iterator_error(&iter) or_break
+
+ if fi.type == .Directory {
+ _remove_all(fi.fullpath) or_return
+ } else {
+ remove(fi.fullpath) or_return
+ }
+ }
+
+ _ = read_directory_iterator_error(&iter) or_return
+ }
+
+ return remove(path)
+}
+
+g_wd: string
+g_wd_mutex: sync.Mutex
+
+_get_working_directory :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
+ sync.guard(&g_wd_mutex)
+
+ return clone_string(g_wd if g_wd != "" else "/", allocator)
+}
+
+_set_working_directory :: proc(dir: string) -> (err: Error) {
+ sync.guard(&g_wd_mutex)
+
+ if dir == g_wd {
+ return
+ }
+
+ if g_wd != "" {
+ delete(g_wd, file_allocator())
+ }
+
+ g_wd = clone_string(dir, file_allocator()) or_return
+ return
+}
+
+_get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err: Error) {
+ if len(args) <= 0 {
+ return clone_string("/", allocator)
+ }
+
+ arg := args[0]
+ if len(arg) > 0 && (arg[0] == '.' || arg[0] == '/') {
+ return clone_string(arg, allocator)
+ }
+
+ return concatenate({"/", arg}, allocator)
+}
diff --git a/core/os/os2/path_windows.odin b/core/os/os2/path_windows.odin
index 3e92cb6f3..dd9b7748c 100644
--- a/core/os/os2/path_windows.odin
+++ b/core/os/os2/path_windows.odin
@@ -1,8 +1,9 @@
#+private
package os2
-import win32 "core:sys/windows"
import "base:runtime"
+import "core:strings"
+import win32 "core:sys/windows"
_Path_Separator :: '\\'
_Path_Separator_String :: "\\"
@@ -136,6 +137,26 @@ _set_working_directory :: proc(dir: string) -> (err: Error) {
return
}
+_get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err: Error) {
+ TEMP_ALLOCATOR_GUARD()
+
+ buf := make([dynamic]u16, 512, temp_allocator()) or_return
+ for {
+ ret := win32.GetModuleFileNameW(nil, raw_data(buf), win32.DWORD(len(buf)))
+ if ret == 0 {
+ err = _get_platform_error()
+ return
+ }
+
+ if ret == win32.DWORD(len(buf)) && win32.GetLastError() == win32.ERROR_INSUFFICIENT_BUFFER {
+ resize(&buf, len(buf)*2) or_return
+ continue
+ }
+
+ return win32_utf16_to_utf8(buf[:ret], allocator)
+ }
+}
+
can_use_long_paths: bool
@(init)
@@ -197,7 +218,7 @@ _fix_long_path_internal :: proc(path: string) -> string {
return path
}
- if !_is_abs(path) { // relative path
+ if !_is_absolute_path(path) { // relative path
return path
}
@@ -237,3 +258,93 @@ _fix_long_path_internal :: proc(path: string) -> string {
return string(path_buf[:w])
}
+
+_are_paths_identical :: strings.equal_fold
+
+_clean_path_handle_start :: proc(path: string, buffer: []u8) -> (rooted: bool, start: int) {
+ // Preserve rooted paths.
+ start = _volume_name_len(path)
+ if start > 0 {
+ rooted = true
+ if len(path) > start && _is_path_separator(path[start]) {
+ // Take `C:` to `C:\`.
+ start += 1
+ }
+ copy(buffer, path[:start])
+ }
+ return
+}
+
+_is_absolute_path :: proc(path: string) -> bool {
+ if _is_reserved_name(path) {
+ return true
+ }
+ l := _volume_name_len(path)
+ if l == 0 {
+ return false
+ }
+
+ path := path
+ path = path[l:]
+ if path == "" {
+ return false
+ }
+ return _is_path_separator(path[0])
+}
+
+_get_absolute_path :: proc(path: string, allocator: runtime.Allocator) -> (absolute_path: string, err: Error) {
+ rel := path
+ if rel == "" {
+ rel = "."
+ }
+ TEMP_ALLOCATOR_GUARD()
+ rel_utf16 := win32.utf8_to_utf16(rel, temp_allocator())
+ n := win32.GetFullPathNameW(raw_data(rel_utf16), 0, nil, nil)
+ if n == 0 {
+ return "", Platform_Error(win32.GetLastError())
+ }
+
+ buf := make([]u16, n, temp_allocator()) or_return
+ n = win32.GetFullPathNameW(raw_data(rel_utf16), u32(n), raw_data(buf), nil)
+ if n == 0 {
+ return "", Platform_Error(win32.GetLastError())
+ }
+
+ return win32.utf16_to_utf8(buf, allocator)
+}
+
+_get_relative_path_handle_start :: proc(base, target: string) -> bool {
+ base_root := base[:_volume_name_len(base)]
+ target_root := target[:_volume_name_len(target)]
+ return strings.equal_fold(base_root, target_root)
+}
+
+_get_common_path_len :: proc(base, target: string) -> int {
+ i := 0
+ end := min(len(base), len(target))
+ for j in 0..=end {
+ if j == end || _is_path_separator(base[j]) {
+ if strings.equal_fold(base[i:j], target[i:j]) {
+ i = j
+ } else {
+ break
+ }
+ }
+ }
+ return i
+}
+
+_split_path :: proc(path: string) -> (dir, file: string) {
+ vol_len := _volume_name_len(path)
+
+ i := len(path) - 1
+ for i >= vol_len && !_is_path_separator(path[i]) {
+ i -= 1
+ }
+ if i == vol_len {
+ return path[:i+1], path[i+1:]
+ } else if i > vol_len {
+ return path[:i], path[i+1:]
+ }
+ return "", path
+}
diff --git a/core/os/os2/pipe_linux.odin b/core/os/os2/pipe_linux.odin
index 852674c69..bb4456e1c 100644
--- a/core/os/os2/pipe_linux.odin
+++ b/core/os/os2/pipe_linux.odin
@@ -10,8 +10,8 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
return nil, nil,_get_platform_error(errno)
}
- r = _new_file(uintptr(fds[0])) or_return
- w = _new_file(uintptr(fds[1])) or_return
+ r = _new_file(uintptr(fds[0]), "", file_allocator()) or_return
+ w = _new_file(uintptr(fds[1]), "", file_allocator()) or_return
return
}
diff --git a/core/os/os2/pipe_posix.odin b/core/os/os2/pipe_posix.odin
index df9425339..7c07bc068 100644
--- a/core/os/os2/pipe_posix.odin
+++ b/core/os/os2/pipe_posix.odin
@@ -21,7 +21,7 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
return
}
- r = __new_file(fds[0])
+ r = __new_file(fds[0], file_allocator())
ri := (^File_Impl)(r.impl)
rname := strings.builder_make(file_allocator())
@@ -29,9 +29,9 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
strings.write_string(&rname, "/dev/fd/")
strings.write_int(&rname, int(fds[0]))
ri.name = strings.to_string(rname)
- ri.cname = strings.to_cstring(&rname)
+ ri.cname = strings.to_cstring(&rname) or_return
- w = __new_file(fds[1])
+ w = __new_file(fds[1], file_allocator())
wi := (^File_Impl)(w.impl)
wname := strings.builder_make(file_allocator())
@@ -39,7 +39,7 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
strings.write_string(&wname, "/dev/fd/")
strings.write_int(&wname, int(fds[1]))
wi.name = strings.to_string(wname)
- wi.cname = strings.to_cstring(&wname)
+ wi.cname = strings.to_cstring(&wname) or_return
return
}
diff --git a/core/os/os2/pipe_wasi.odin b/core/os/os2/pipe_wasi.odin
new file mode 100644
index 000000000..19c11b51d
--- /dev/null
+++ b/core/os/os2/pipe_wasi.odin
@@ -0,0 +1,13 @@
+#+private
+package os2
+
+_pipe :: proc() -> (r, w: ^File, err: Error) {
+ err = .Unsupported
+ return
+}
+
+@(require_results)
+_pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) {
+ err = .Unsupported
+ return
+}
diff --git a/core/os/os2/process.odin b/core/os/os2/process.odin
index 5b5a6e844..98995981b 100644
--- a/core/os/os2/process.odin
+++ b/core/os/os2/process.odin
@@ -290,12 +290,21 @@ process_open :: proc(pid: int, flags := Process_Open_Flags {}) -> (Process, Erro
return _process_open(pid, flags)
}
+
+/*
+OS-specific process attributes.
+*/
+Process_Attributes :: struct {
+ sys_attr: _Sys_Process_Attributes,
+}
+
/*
The description of how a process should be created.
*/
Process_Desc :: struct {
// OS-specific attributes.
- sys_attr: _Sys_Process_Attributes,
+ sys_attr: Process_Attributes,
+
// The working directory of the process. If the string has length 0, the
// working directory is assumed to be the current working directory of the
// current process.
@@ -398,11 +407,9 @@ process_exec :: proc(
{
stdout_b: [dynamic]byte
stdout_b.allocator = allocator
- defer stdout = stdout_b[:]
stderr_b: [dynamic]byte
stderr_b.allocator = allocator
- defer stderr = stderr_b[:]
buf: [1024]u8 = ---
@@ -441,6 +448,9 @@ process_exec :: proc(
}
}
}
+
+ stdout = stdout_b[:]
+ stderr = stderr_b[:]
}
if err != nil {
diff --git a/core/os/os2/process_linux.odin b/core/os/os2/process_linux.odin
index 7eb4dfa44..6d654008b 100644
--- a/core/os/os2/process_linux.odin
+++ b/core/os/os2/process_linux.odin
@@ -10,7 +10,6 @@ import "core:slice"
import "core:strings"
import "core:strconv"
import "core:sys/linux"
-import "core:path/filepath"
PIDFD_UNASSIGNED :: ~uintptr(0)
@@ -111,7 +110,7 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
strings.write_string(&path_builder, "/proc/")
strings.write_int(&path_builder, pid)
- proc_fd, errno := linux.open(strings.to_cstring(&path_builder), _OPENDIR_FLAGS)
+ proc_fd, errno := linux.open(strings.to_cstring(&path_builder) or_return, _OPENDIR_FLAGS)
if errno != .NONE {
err = _get_platform_error(errno)
return
@@ -169,7 +168,7 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
strings.write_int(&path_builder, pid)
strings.write_string(&path_builder, "/cmdline")
- cmdline_bytes, cmdline_err := _read_entire_pseudo_file(strings.to_cstring(&path_builder), temp_allocator())
+ cmdline_bytes, cmdline_err := _read_entire_pseudo_file(strings.to_cstring(&path_builder) or_return, temp_allocator())
if cmdline_err != nil || len(cmdline_bytes) == 0 {
err = cmdline_err
break cmdline_if
@@ -190,7 +189,7 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
strings.write_int(&path_builder, pid)
strings.write_string(&path_builder, "/cwd")
- cwd, cwd_err = _read_link_cstr(strings.to_cstring(&path_builder), temp_allocator()) // allowed to fail
+ cwd, cwd_err = _read_link_cstr(strings.to_cstring(&path_builder) or_return, temp_allocator()) // allowed to fail
if cwd_err == nil && .Working_Dir in selection {
info.working_dir = strings.clone(cwd, allocator) or_return
info.fields += {.Working_Dir}
@@ -205,7 +204,7 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
info.executable_path = strings.clone(cmdline[:terminator], allocator) or_return
info.fields += {.Executable_Path}
} else if cwd_err == nil {
- info.executable_path = filepath.join({ cwd, cmdline[:terminator] }, allocator) or_return
+ info.executable_path = join_path({ cwd, cmdline[:terminator] }, allocator) or_return
info.fields += {.Executable_Path}
} else {
break cmdline_if
@@ -258,7 +257,7 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
strings.write_int(&path_builder, pid)
strings.write_string(&path_builder, "/stat")
- proc_stat_bytes, stat_err := _read_entire_pseudo_file(strings.to_cstring(&path_builder), temp_allocator())
+ proc_stat_bytes, stat_err := _read_entire_pseudo_file(strings.to_cstring(&path_builder) or_return, temp_allocator())
if stat_err != nil {
err = stat_err
break stat_if
@@ -330,7 +329,7 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
strings.write_int(&path_builder, pid)
strings.write_string(&path_builder, "/environ")
- if env_bytes, env_err := _read_entire_pseudo_file(strings.to_cstring(&path_builder), temp_allocator()); env_err == nil {
+ if env_bytes, env_err := _read_entire_pseudo_file(strings.to_cstring(&path_builder) or_return, temp_allocator()); env_err == nil {
env := string(env_bytes)
env_list := make([dynamic]string, allocator) or_return
@@ -384,14 +383,6 @@ _Sys_Process_Attributes :: struct {}
@(private="package")
_process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
- has_executable_permissions :: proc(fd: linux.Fd) -> bool {
- backing: [48]u8
- b := strings.builder_from_bytes(backing[:])
- strings.write_string(&b, "/proc/self/fd/")
- strings.write_int(&b, int(fd))
- return linux.access(strings.to_cstring(&b), linux.X_OK) == .NONE
- }
-
TEMP_ALLOCATOR_GUARD()
if len(desc.command) == 0 {
@@ -411,11 +402,11 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
}
// search PATH if just a plain name is provided
- exe_fd: linux.Fd
+ exe_path: cstring
executable_name := desc.command[0]
if strings.index_byte(executable_name, '/') < 0 {
path_env := get_env("PATH", temp_allocator())
- path_dirs := filepath.split_list(path_env, temp_allocator()) or_return
+ path_dirs := split_path_list(path_env, temp_allocator()) or_return
exe_builder := strings.builder_make(temp_allocator()) or_return
@@ -426,16 +417,11 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
strings.write_byte(&exe_builder, '/')
strings.write_string(&exe_builder, executable_name)
- exe_path := strings.to_cstring(&exe_builder)
- if exe_fd, errno = linux.openat(dir_fd, exe_path, {.PATH, .CLOEXEC}); errno != .NONE {
- continue
+ exe_path = strings.to_cstring(&exe_builder) or_return
+ if linux.access(exe_path, linux.X_OK) == .NONE {
+ found = true
+ break
}
- if !has_executable_permissions(exe_fd) {
- linux.close(exe_fd)
- continue
- }
- found = true
- break
}
if !found {
// check in cwd to match windows behavior
@@ -443,29 +429,18 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
strings.write_string(&exe_builder, "./")
strings.write_string(&exe_builder, executable_name)
- exe_path := strings.to_cstring(&exe_builder)
- if exe_fd, errno = linux.openat(dir_fd, exe_path, {.PATH, .CLOEXEC}); errno != .NONE {
+ exe_path = strings.to_cstring(&exe_builder) or_return
+ if linux.access(exe_path, linux.X_OK) != .NONE {
return process, .Not_Exist
}
- if !has_executable_permissions(exe_fd) {
- linux.close(exe_fd)
- return process, .Permission_Denied
- }
}
} else {
- exe_path := temp_cstring(executable_name) or_return
- if exe_fd, errno = linux.openat(dir_fd, exe_path, {.PATH, .CLOEXEC}); errno != .NONE {
- return process, _get_platform_error(errno)
- }
- if !has_executable_permissions(exe_fd) {
- linux.close(exe_fd)
- return process, .Permission_Denied
+ exe_path = temp_cstring(executable_name) or_return
+ if linux.access(exe_path, linux.X_OK) != .NONE {
+ return process, .Not_Exist
}
}
- // At this point, we have an executable.
- defer linux.close(exe_fd)
-
// args and environment need to be a list of cstrings
// that are terminated by a nil pointer.
cargs := make([]cstring, len(desc.command) + 1, temp_allocator()) or_return
@@ -492,7 +467,6 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
}
defer linux.close(child_pipe_fds[READ])
-
// TODO: This is the traditional textbook implementation with fork.
// A more efficient implementation with vfork:
//
@@ -572,8 +546,13 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
if _, errno = linux.dup2(stderr_fd, STDERR); errno != .NONE {
write_errno_to_parent_and_abort(child_pipe_fds[WRITE], errno)
}
+ if dir_fd != linux.AT_FDCWD {
+ if errno = linux.fchdir(dir_fd); errno != .NONE {
+ write_errno_to_parent_and_abort(child_pipe_fds[WRITE], errno)
+ }
+ }
- errno = linux.execveat(exe_fd, "", &cargs[0], env, {.AT_EMPTY_PATH})
+ errno = linux.execveat(dir_fd, exe_path, &cargs[0], env)
assert(errno != nil)
write_errno_to_parent_and_abort(child_pipe_fds[WRITE], errno)
}
@@ -614,7 +593,7 @@ _process_state_update_times :: proc(state: ^Process_State) -> (err: Error) {
strings.write_string(&path_builder, "/stat")
stat_buf: []u8
- stat_buf, err = _read_entire_pseudo_file(strings.to_cstring(&path_builder), temp_allocator())
+ stat_buf, err = _read_entire_pseudo_file(strings.to_cstring(&path_builder) or_return, temp_allocator())
if err != nil {
return
}
diff --git a/core/os/os2/process_posix.odin b/core/os/os2/process_posix.odin
index b54374cec..cd451781f 100644
--- a/core/os/os2/process_posix.odin
+++ b/core/os/os2/process_posix.odin
@@ -6,7 +6,6 @@ import "base:runtime"
import "core:time"
import "core:strings"
-import "core:path/filepath"
import kq "core:sys/kqueue"
import "core:sys/posix"
@@ -62,7 +61,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
exe_name := desc.command[0]
if strings.index_byte(exe_name, '/') < 0 {
path_env := get_env("PATH", temp_allocator())
- path_dirs := filepath.split_list(path_env, temp_allocator())
+ path_dirs := split_path_list(path_env, temp_allocator()) or_return
found: bool
for dir in path_dirs {
@@ -71,7 +70,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
strings.write_byte(&exe_builder, '/')
strings.write_string(&exe_builder, exe_name)
- if exe_fd := posix.open(strings.to_cstring(&exe_builder), {.CLOEXEC, .EXEC}); exe_fd == -1 {
+ if exe_fd := posix.open(strings.to_cstring(&exe_builder) or_return, {.CLOEXEC, .EXEC}); exe_fd == -1 {
continue
} else {
posix.close(exe_fd)
@@ -91,7 +90,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
// "hello/./world" is fine right?
- if exe_fd := posix.open(strings.to_cstring(&exe_builder), {.CLOEXEC, .EXEC}); exe_fd == -1 {
+ if exe_fd := posix.open(strings.to_cstring(&exe_builder) or_return, {.CLOEXEC, .EXEC}); exe_fd == -1 {
err = .Not_Exist
return
} else {
@@ -102,7 +101,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
strings.builder_reset(&exe_builder)
strings.write_string(&exe_builder, exe_name)
- if exe_fd := posix.open(strings.to_cstring(&exe_builder), {.CLOEXEC, .EXEC}); exe_fd == -1 {
+ if exe_fd := posix.open(strings.to_cstring(&exe_builder) or_return, {.CLOEXEC, .EXEC}); exe_fd == -1 {
err = .Not_Exist
return
} else {
@@ -181,7 +180,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
if posix.chdir(cwd) != .OK { abort(pipe[WRITE]) }
}
- res := posix.execve(strings.to_cstring(&exe_builder), raw_data(cmd), env)
+ res := posix.execve(strings.to_cstring(&exe_builder) or_return, raw_data(cmd), env)
assert(res == -1)
abort(pipe[WRITE])
diff --git a/core/os/os2/process_wasi.odin b/core/os/os2/process_wasi.odin
new file mode 100644
index 000000000..6ebfe3788
--- /dev/null
+++ b/core/os/os2/process_wasi.odin
@@ -0,0 +1,89 @@
+#+private
+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))
+}
+
+_get_uid :: proc() -> int {
+ return 0
+}
+
+_get_euid :: proc() -> int {
+ return 0
+}
+
+_get_gid :: proc() -> int {
+ return 0
+}
+
+_get_egid :: proc() -> int {
+ return 0
+}
+
+_get_pid :: proc() -> int {
+ return 0
+}
+
+_get_ppid :: proc() -> int {
+ return 0
+}
+
+_process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
+ err = .Unsupported
+ return
+}
+
+_current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
+ err = .Unsupported
+ return
+}
+
+_Sys_Process_Attributes :: struct {}
+
+_process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
+ err = .Unsupported
+ return
+}
+
+_process_wait :: proc(process: Process, timeout: time.Duration) -> (process_state: Process_State, err: Error) {
+ err = .Unsupported
+ return
+}
+
+_process_close :: proc(process: Process) -> Error {
+ return .Unsupported
+}
+
+_process_kill :: proc(process: Process) -> (err: Error) {
+ return .Unsupported
+}
+
+_process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
+ err = .Unsupported
+ return
+}
+
+_process_list :: proc(allocator: runtime.Allocator) -> (list: []int, err: Error) {
+ err = .Unsupported
+ return
+}
+
+_process_open :: proc(pid: int, flags: Process_Open_Flags) -> (process: Process, err: Error) {
+ process.pid = pid
+ err = .Unsupported
+ return
+}
+
+_process_handle_still_valid :: proc(p: Process) -> Error {
+ return nil
+}
+
+_process_state_update_times :: proc(p: Process, state: ^Process_State) {
+ return
+}
diff --git a/core/os/os2/process_windows.odin b/core/os/os2/process_windows.odin
index 1984cbfdf..536930ee1 100644
--- a/core/os/os2/process_windows.odin
+++ b/core/os/os2/process_windows.odin
@@ -427,7 +427,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
command_line_w := win32_utf8_to_wstring(command_line, temp_allocator()) or_return
environment := desc.env
if desc.env == nil {
- environment = environ(temp_allocator())
+ environment = environ(temp_allocator()) or_return
}
environment_block := _build_environment_block(environment, temp_allocator())
environment_block_w := win32_utf8_to_utf16(environment_block, temp_allocator()) or_return
diff --git a/core/os/os2/stat.odin b/core/os/os2/stat.odin
index b53ebb3ab..7d76902eb 100644
--- a/core/os/os2/stat.odin
+++ b/core/os/os2/stat.odin
@@ -1,7 +1,6 @@
package os2
import "base:runtime"
-import "core:path/filepath"
import "core:strings"
import "core:time"
@@ -24,8 +23,8 @@ File_Info :: struct {
@(require_results)
file_info_clone :: proc(fi: File_Info, allocator: runtime.Allocator) -> (cloned: File_Info, err: runtime.Allocator_Error) {
cloned = fi
- cloned.fullpath = strings.clone(fi.fullpath) or_return
- cloned.name = filepath.base(cloned.fullpath)
+ cloned.fullpath = strings.clone(fi.fullpath, allocator) or_return
+ _, cloned.name = split_path(cloned.fullpath)
return
}
diff --git a/core/os/os2/stat_linux.odin b/core/os/os2/stat_linux.odin
index 0433c1a61..7bff08f29 100644
--- a/core/os/os2/stat_linux.odin
+++ b/core/os/os2/stat_linux.odin
@@ -4,7 +4,6 @@ package os2
import "core:time"
import "base:runtime"
import "core:sys/linux"
-import "core:path/filepath"
_fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) {
impl := (^File_Impl)(f.impl)
@@ -42,7 +41,7 @@ _fstat_internal :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> (fi: File
creation_time = time.Time{i64(s.ctime.time_sec) * i64(time.Second) + i64(s.ctime.time_nsec)}, // regular stat does not provide this
}
fi.creation_time = fi.modification_time
- fi.name = filepath.base(fi.fullpath)
+ _, fi.name = split_path(fi.fullpath)
return
}
diff --git a/core/os/os2/stat_posix.odin b/core/os/os2/stat_posix.odin
index 88029c1f5..260dc7b52 100644
--- a/core/os/os2/stat_posix.odin
+++ b/core/os/os2/stat_posix.odin
@@ -4,13 +4,12 @@ package os2
import "base:runtime"
-import "core:path/filepath"
import "core:sys/posix"
import "core:time"
internal_stat :: proc(stat: posix.stat_t, fullpath: string) -> (fi: File_Info) {
fi.fullpath = fullpath
- fi.name = filepath.base(fi.fullpath)
+ _, fi.name = split_path(fi.fullpath)
fi.inode = u128(stat.st_ino)
fi.size = i64(stat.st_size)
@@ -104,7 +103,7 @@ _lstat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, er
// NOTE: This might not be correct when given "/symlink/foo.txt",
// you would want that to resolve "/symlink", but not resolve "foo.txt".
- fullpath := filepath.clean(name, temp_allocator())
+ fullpath := clean_path(name, temp_allocator()) or_return
assert(len(fullpath) > 0)
switch {
case fullpath[0] == '/':
diff --git a/core/os/os2/stat_wasi.odin b/core/os/os2/stat_wasi.odin
new file mode 100644
index 000000000..bf18d8273
--- /dev/null
+++ b/core/os/os2/stat_wasi.odin
@@ -0,0 +1,100 @@
+#+private
+package os2
+
+import "base:runtime"
+
+import "core:sys/wasm/wasi"
+import "core:time"
+
+internal_stat :: proc(stat: wasi.filestat_t, fullpath: string) -> (fi: File_Info) {
+ fi.fullpath = fullpath
+ _, fi.name = split_path(fi.fullpath)
+
+ fi.inode = u128(stat.ino)
+ fi.size = i64(stat.size)
+
+ switch stat.filetype {
+ case .BLOCK_DEVICE: fi.type = .Block_Device
+ case .CHARACTER_DEVICE: fi.type = .Character_Device
+ case .DIRECTORY: fi.type = .Directory
+ case .REGULAR_FILE: fi.type = .Regular
+ case .SOCKET_DGRAM, .SOCKET_STREAM: fi.type = .Socket
+ case .SYMBOLIC_LINK: fi.type = .Symlink
+ case .UNKNOWN: fi.type = .Undetermined
+ case: fi.type = .Undetermined
+ }
+
+ fi.creation_time = time.Time{_nsec=i64(stat.ctim)}
+ fi.modification_time = time.Time{_nsec=i64(stat.mtim)}
+ fi.access_time = time.Time{_nsec=i64(stat.atim)}
+
+ return
+}
+
+_fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
+ if f == nil || f.impl == nil {
+ err = .Invalid_File
+ return
+ }
+
+ impl := (^File_Impl)(f.impl)
+
+ stat, _err := wasi.fd_filestat_get(__fd(f))
+ if _err != nil {
+ err = _get_platform_error(_err)
+ return
+ }
+
+ fullpath := clone_string(impl.name, allocator) or_return
+ return internal_stat(stat, fullpath), nil
+}
+
+_stat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
+ if name == "" {
+ err = .Invalid_Path
+ return
+ }
+
+ dir_fd, relative, ok := match_preopen(name)
+ if !ok {
+ err = .Invalid_Path
+ return
+ }
+
+ stat, _err := wasi.path_filestat_get(dir_fd, {.SYMLINK_FOLLOW}, relative)
+ if _err != nil {
+ err = _get_platform_error(_err)
+ return
+ }
+
+ // NOTE: wasi doesn't really do full paths afact.
+ fullpath := clone_string(name, allocator) or_return
+ return internal_stat(stat, fullpath), nil
+}
+
+_lstat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
+ if name == "" {
+ err = .Invalid_Path
+ return
+ }
+
+ dir_fd, relative, ok := match_preopen(name)
+ if !ok {
+ err = .Invalid_Path
+ return
+ }
+
+ stat, _err := wasi.path_filestat_get(dir_fd, {}, relative)
+ if _err != nil {
+ err = _get_platform_error(_err)
+ return
+ }
+
+ // NOTE: wasi doesn't really do full paths afact.
+ fullpath := clone_string(name, allocator) or_return
+ return internal_stat(stat, fullpath), nil
+}
+
+_same_file :: proc(fi1, fi2: File_Info) -> bool {
+ return fi1.fullpath == fi2.fullpath
+}
diff --git a/core/os/os2/stat_windows.odin b/core/os/os2/stat_windows.odin
index 0a019e9da..7d8dd3843 100644
--- a/core/os/os2/stat_windows.odin
+++ b/core/os/os2/stat_windows.odin
@@ -72,7 +72,11 @@ internal_stat :: proc(name: string, create_file_attributes: u32, allocator: runt
ok := win32.GetFileAttributesExW(wname, win32.GetFileExInfoStandard, &fa)
if ok && fa.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
// Not a symlink
- return _file_info_from_win32_file_attribute_data(&fa, name, allocator)
+ fi = _file_info_from_win32_file_attribute_data(&fa, name, allocator) or_return
+ if fi.type == .Undetermined {
+ fi.type = _file_type_from_create_file(wname, create_file_attributes)
+ }
+ return
}
err := 0 if ok else win32.GetLastError()
@@ -86,7 +90,11 @@ internal_stat :: proc(name: string, create_file_attributes: u32, allocator: runt
}
win32.FindClose(sh)
- return _file_info_from_win32_find_data(&fd, name, allocator)
+ fi = _file_info_from_win32_find_data(&fd, name, allocator) or_return
+ if fi.type == .Undetermined {
+ fi.type = _file_type_from_create_file(wname, create_file_attributes)
+ }
+ return
}
h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil)
@@ -194,6 +202,15 @@ file_type :: proc(h: win32.HANDLE) -> File_Type {
return .Undetermined
}
+_file_type_from_create_file :: proc(wname: win32.wstring, create_file_attributes: u32) -> File_Type {
+ h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil)
+ if h == win32.INVALID_HANDLE_VALUE {
+ return .Undetermined
+ }
+ defer win32.CloseHandle(h)
+ 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) {
if file_attributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
mode |= 0o444
@@ -266,7 +283,7 @@ _file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HA
fi.name = basename(path)
fi.inode = u128(u64(d.nFileIndexHigh)<<32 + u64(d.nFileIndexLow))
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
- type, mode := _file_type_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
+ type, mode := _file_type_mode_from_file_attributes(d.dwFileAttributes, h, 0)
fi.type = type
fi.mode |= mode
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
@@ -298,57 +315,37 @@ _is_UNC :: proc(path: string) -> bool {
}
_volume_name_len :: proc(path: string) -> int {
- if ODIN_OS == .Windows {
- if len(path) < 2 {
- return 0
- }
- c := path[0]
- if path[1] == ':' {
- switch c {
- case 'a'..='z', 'A'..='Z':
- return 2
- }
+ if len(path) < 2 {
+ return 0
+ }
+ c := path[0]
+ if path[1] == ':' {
+ switch c {
+ case 'a'..='z', 'A'..='Z':
+ return 2
}
+ }
- // URL: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
- if l := len(path); l >= 5 && _is_path_separator(path[0]) && _is_path_separator(path[1]) &&
- !_is_path_separator(path[2]) && path[2] != '.' {
- for n := 3; n < l-1; n += 1 {
- if _is_path_separator(path[n]) {
- n += 1
- if !_is_path_separator(path[n]) {
- if path[n] == '.' {
- break
- }
+ // URL: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
+ if l := len(path); l >= 5 && _is_path_separator(path[0]) && _is_path_separator(path[1]) &&
+ !_is_path_separator(path[2]) && path[2] != '.' {
+ for n := 3; n < l-1; n += 1 {
+ if _is_path_separator(path[n]) {
+ n += 1
+ if !_is_path_separator(path[n]) {
+ if path[n] == '.' {
+ break
}
- for ; n < l; n += 1 {
- if _is_path_separator(path[n]) {
- break
- }
- }
- return n
}
- break
+ for ; n < l; n += 1 {
+ if _is_path_separator(path[n]) {
+ break
+ }
+ }
+ return n
}
+ break
}
}
return 0
}
-
-_is_abs :: proc(path: string) -> bool {
- if _is_reserved_name(path) {
- return true
- }
- l := _volume_name_len(path)
- if l == 0 {
- return false
- }
-
- path := path
- path = path[l:]
- if path == "" {
- return false
- }
- return is_path_separator(path[0])
-}
-
diff --git a/core/os/os2/temp_file.odin b/core/os/os2/temp_file.odin
index 5ca4e1453..e93117f02 100644
--- a/core/os/os2/temp_file.odin
+++ b/core/os/os2/temp_file.odin
@@ -20,7 +20,7 @@ create_temp_file :: proc(dir, pattern: string) -> (f: ^File, err: Error) {
prefix, suffix := _prefix_and_suffix(pattern) or_return
prefix = temp_join_path(dir, prefix) or_return
- rand_buf: [32]byte
+ rand_buf: [10]byte
name_buf := make([]byte, len(prefix)+len(rand_buf)+len(suffix), temp_allocator())
attempts := 0
@@ -52,7 +52,7 @@ make_directory_temp :: proc(dir, pattern: string, allocator: runtime.Allocator)
prefix, suffix := _prefix_and_suffix(pattern) or_return
prefix = temp_join_path(dir, prefix) or_return
- rand_buf: [32]byte
+ rand_buf: [10]byte
name_buf := make([]byte, len(prefix)+len(rand_buf)+len(suffix), temp_allocator())
attempts := 0
diff --git a/core/os/os2/temp_file_wasi.odin b/core/os/os2/temp_file_wasi.odin
new file mode 100644
index 000000000..d5628d300
--- /dev/null
+++ b/core/os/os2/temp_file_wasi.odin
@@ -0,0 +1,9 @@
+#+private
+package os2
+
+import "base:runtime"
+
+_temp_dir :: proc(allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) {
+ // NOTE: requires user to add /tmp to their preopen dirs, no standard way exists.
+ return clone_string("/tmp", allocator)
+}
diff --git a/core/os/os2/user.odin b/core/os/os2/user.odin
index a0a7a839d..7fcc87bf8 100644
--- a/core/os/os2/user.odin
+++ b/core/os/os2/user.odin
@@ -49,7 +49,7 @@ user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Erro
dir = concatenate({dir, "/.config"}, allocator) or_return
}
case: // All other UNIX systems
- dir = get_env("XDG_CACHE_HOME", allocator)
+ dir = get_env("XDG_CONFIG_HOME", allocator)
if dir == "" {
dir = get_env("HOME", temp_allocator())
if dir == "" {
diff --git a/core/os/os_darwin.odin b/core/os/os_darwin.odin
index b09a41fbf..1e5571fab 100644
--- a/core/os/os_darwin.odin
+++ b/core/os/os_darwin.odin
@@ -714,7 +714,7 @@ spawnp :: proc(path: string, args: []string, envs: []string, file_actions: rawpt
}
@(require_results)
-open :: proc(path: string, flags: int = O_RDWR, mode: int = 0) -> (handle: Handle, err: Error) {
+open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (handle: Handle, err: Error) {
isDir := is_dir_path(path)
flags := flags
if isDir {
@@ -1322,7 +1322,7 @@ sendto :: proc(sd: Socket, data: []u8, flags: int, addr: ^SOCKADDR, addrlen: soc
}
send :: proc(sd: Socket, data: []byte, flags: int) -> (u32, Error) {
- result := _unix_send(c.int(sd), raw_data(data), len(data), 0)
+ result := _unix_send(c.int(sd), raw_data(data), len(data), i32(flags))
if result < 0 {
return 0, get_last_error()
}
diff --git a/core/os/os_freebsd.odin b/core/os/os_freebsd.odin
index 837e79f4d..87a56b057 100644
--- a/core/os/os_freebsd.odin
+++ b/core/os/os_freebsd.odin
@@ -624,6 +624,14 @@ is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
is_file :: proc {is_file_path, is_file_handle}
is_dir :: proc {is_dir_path, is_dir_handle}
+@(require_results)
+exists :: proc(path: string) -> bool {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
+ cpath := strings.clone_to_cstring(path, context.temp_allocator)
+ res := _unix_access(cpath, O_RDONLY)
+ return res == 0
+}
+
// NOTE(bill): Uses startup to initialize it
stdin: Handle = 0
diff --git a/core/os/os_haiku.odin b/core/os/os_haiku.odin
index 4ad370724..4a57afb87 100644
--- a/core/os/os_haiku.odin
+++ b/core/os/os_haiku.odin
@@ -1,11 +1,13 @@
package os
-foreign import libc "system:c"
+foreign import lib "system:c"
import "base:runtime"
import "core:c"
+import "core:c/libc"
import "core:strings"
import "core:sys/haiku"
+import "core:sys/posix"
Handle :: i32
Pid :: i32
@@ -14,7 +16,7 @@ _Platform_Error :: haiku.Errno
MAX_PATH :: haiku.PATH_MAX
-ENOSYS :: _Platform_Error(i32(haiku.Errno.POSIX_ERROR_BASE) + 9)
+ENOSYS :: _Platform_Error(haiku.Errno.ENOSYS)
INVALID_HANDLE :: ~Handle(0)
@@ -117,14 +119,13 @@ S_ISBLK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK
S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO }
S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK }
+__error :: libc.errno
+_unix_open :: posix.open
-foreign libc {
- @(link_name="_errorp") __error :: proc() -> ^c.int ---
-
+foreign lib {
@(link_name="fork") _unix_fork :: proc() -> pid_t ---
@(link_name="getthrid") _unix_getthrid :: proc() -> int ---
- @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, #c_vararg mode: ..u16) -> Handle ---
@(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---
@(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
@(link_name="pread") _unix_pread :: proc(fd: Handle, buf: rawptr, size: c.size_t, offset: i64) -> c.ssize_t ---
@@ -150,6 +151,7 @@ foreign libc {
@(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int ---
@(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) ---
@(link_name="readdir_r") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int ---
+ @(link_name="dup") _unix_dup :: proc(fd: Handle) -> Handle ---
@(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr ---
@(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr ---
@@ -157,7 +159,7 @@ foreign libc {
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
- @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
+ @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: [^]byte = nil) -> cstring ---
@(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---
@@ -203,7 +205,7 @@ fork :: proc() -> (Pid, Error) {
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
- handle := _unix_open(cstr, c.int(flags), u16(mode))
+ handle := cast(Handle)_unix_open(cstr, transmute(posix.O_Flags)i32(flags), transmute(posix.mode_t)i32(mode))
if handle == -1 {
return INVALID_HANDLE, get_last_error()
}
@@ -444,7 +446,7 @@ absolute_path_from_relative :: proc(rel: string, allocator := context.allocator)
if path_ptr == nil {
return "", get_last_error()
}
- defer _unix_free(path_ptr)
+ defer _unix_free(rawptr(path_ptr))
path_cstr := cstring(path_ptr)
return strings.clone(string(path_cstr), allocator)
@@ -488,3 +490,17 @@ exit :: proc "contextless" (code: int) -> ! {
runtime._cleanup_runtime_contextless()
_unix_exit(i32(code))
}
+
+@(require_results)
+current_thread_id :: proc "contextless" () -> int {
+ return int(haiku.find_thread(nil))
+}
+
+@(private, require_results)
+_dup :: proc(fd: Handle) -> (Handle, Error) {
+ dup := _unix_dup(fd)
+ if dup == -1 {
+ return INVALID_HANDLE, get_last_error()
+ }
+ return dup, nil
+}
diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin
index e023ce7cb..2281e6a82 100644
--- a/core/os/os_linux.odin
+++ b/core/os/os_linux.odin
@@ -1155,7 +1155,7 @@ sendto :: proc(sd: Socket, data: []u8, flags: int, addr: ^SOCKADDR, addrlen: soc
}
send :: proc(sd: Socket, data: []byte, flags: int) -> (u32, Error) {
- result := unix.sys_sendto(int(sd), raw_data(data), len(data), 0, nil, 0)
+ result := unix.sys_sendto(int(sd), raw_data(data), len(data), flags, nil, 0)
if result < 0 {
return 0, _get_errno(int(result))
}
diff --git a/core/os/os_openbsd.odin b/core/os/os_openbsd.odin
index 3c377968c..6548a57dc 100644
--- a/core/os/os_openbsd.odin
+++ b/core/os/os_openbsd.odin
@@ -343,7 +343,7 @@ AT_REMOVEDIR :: 0x08
@(default_calling_convention="c")
foreign libc {
- @(link_name="__error") __error :: proc() -> ^c.int ---
+ @(link_name="__errno") __error :: proc() -> ^c.int ---
@(link_name="fork") _unix_fork :: proc() -> pid_t ---
@(link_name="getthrid") _unix_getthrid :: proc() -> int ---
diff --git a/core/os/os_windows.odin b/core/os/os_windows.odin
index 552508f3b..0c532bf14 100644
--- a/core/os/os_windows.odin
+++ b/core/os/os_windows.odin
@@ -4,15 +4,13 @@ package os
import win32 "core:sys/windows"
import "base:runtime"
import "base:intrinsics"
+import "core:unicode/utf16"
Handle :: distinct uintptr
File_Time :: distinct u64
-
INVALID_HANDLE :: ~Handle(0)
-
-
O_RDONLY :: 0x00000
O_WRONLY :: 0x00001
O_RDWR :: 0x00002
@@ -278,3 +276,580 @@ is_windows_11 :: proc "contextless" () -> bool {
osvi := get_windows_version_w()
return (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber >= WINDOWS_11_BUILD_CUTOFF)
}
+
+@(require_results)
+is_path_separator :: proc(c: byte) -> bool {
+ return c == '/' || c == '\\'
+}
+
+@(require_results)
+open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Error) {
+ if len(path) == 0 {
+ return INVALID_HANDLE, General_Error.Not_Exist
+ }
+
+ access: u32
+ switch mode & (O_RDONLY|O_WRONLY|O_RDWR) {
+ case O_RDONLY: access = win32.FILE_GENERIC_READ
+ case O_WRONLY: access = win32.FILE_GENERIC_WRITE
+ case O_RDWR: access = win32.FILE_GENERIC_READ | win32.FILE_GENERIC_WRITE
+ }
+
+ if mode&O_CREATE != 0 {
+ access |= win32.FILE_GENERIC_WRITE
+ }
+ if mode&O_APPEND != 0 {
+ access &~= win32.FILE_GENERIC_WRITE
+ access |= win32.FILE_APPEND_DATA
+ }
+
+ share_mode := win32.FILE_SHARE_READ|win32.FILE_SHARE_WRITE
+ sa: ^win32.SECURITY_ATTRIBUTES = nil
+ sa_inherit := win32.SECURITY_ATTRIBUTES{nLength = size_of(win32.SECURITY_ATTRIBUTES), bInheritHandle = true}
+ if mode&O_CLOEXEC == 0 {
+ sa = &sa_inherit
+ }
+
+ create_mode: u32
+ switch {
+ case mode&(O_CREATE|O_EXCL) == (O_CREATE | O_EXCL):
+ create_mode = win32.CREATE_NEW
+ case mode&(O_CREATE|O_TRUNC) == (O_CREATE | O_TRUNC):
+ create_mode = win32.CREATE_ALWAYS
+ case mode&O_CREATE == O_CREATE:
+ create_mode = win32.OPEN_ALWAYS
+ case mode&O_TRUNC == O_TRUNC:
+ create_mode = win32.TRUNCATE_EXISTING
+ case:
+ create_mode = win32.OPEN_EXISTING
+ }
+ wide_path := win32.utf8_to_wstring(path)
+ handle := Handle(win32.CreateFileW(wide_path, access, share_mode, sa, create_mode, win32.FILE_ATTRIBUTE_NORMAL|win32.FILE_FLAG_BACKUP_SEMANTICS, nil))
+ if handle != INVALID_HANDLE {
+ return handle, nil
+ }
+
+ return INVALID_HANDLE, get_last_error()
+}
+
+close :: proc(fd: Handle) -> Error {
+ if !win32.CloseHandle(win32.HANDLE(fd)) {
+ return get_last_error()
+ }
+ return nil
+}
+
+flush :: proc(fd: Handle) -> (err: Error) {
+ if !win32.FlushFileBuffers(win32.HANDLE(fd)) {
+ err = get_last_error()
+ }
+ return
+}
+
+
+
+write :: proc(fd: Handle, data: []byte) -> (int, Error) {
+ if len(data) == 0 {
+ return 0, nil
+ }
+
+ single_write_length: win32.DWORD
+ total_write: i64
+ length := i64(len(data))
+
+ for total_write < length {
+ remaining := length - total_write
+ to_write := win32.DWORD(min(i32(remaining), MAX_RW))
+
+ e := win32.WriteFile(win32.HANDLE(fd), &data[total_write], to_write, &single_write_length, nil)
+ if single_write_length <= 0 || !e {
+ return int(total_write), get_last_error()
+ }
+ total_write += i64(single_write_length)
+ }
+ return int(total_write), nil
+}
+
+@(private="file", require_results)
+read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Error) {
+ if len(b) == 0 {
+ return 0, nil
+ }
+
+ BUF_SIZE :: 386
+ buf16: [BUF_SIZE]u16
+ buf8: [4*BUF_SIZE]u8
+
+ for n < len(b) && err == nil {
+ min_read := max(len(b)/4, 1 if len(b) > 0 else 0)
+ max_read := u32(min(BUF_SIZE, min_read))
+ if max_read == 0 {
+ break
+ }
+
+ single_read_length: u32
+ ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil)
+ if !ok {
+ err = get_last_error()
+ }
+
+ buf8_len := utf16.decode_to_utf8(buf8[:], buf16[:single_read_length])
+ src := buf8[:buf8_len]
+
+ ctrl_z := false
+ for i := 0; i < len(src) && n < len(b); i += 1 {
+ x := src[i]
+ if x == 0x1a { // ctrl-z
+ ctrl_z = true
+ break
+ }
+ b[n] = x
+ n += 1
+ }
+ if ctrl_z || single_read_length < max_read {
+ break
+ }
+
+ // NOTE(bill): if the last two values were a newline, then it is expected that
+ // this is the end of the input
+ if n >= 2 && single_read_length == max_read && string(b[n-2:n]) == "\r\n" {
+ break
+ }
+
+ }
+
+ return
+}
+
+read :: proc(fd: Handle, data: []byte) -> (total_read: int, err: Error) {
+ if len(data) == 0 {
+ return 0, nil
+ }
+
+ handle := win32.HANDLE(fd)
+
+ m: u32
+ is_console := win32.GetConsoleMode(handle, &m)
+ length := len(data)
+
+ // NOTE(Jeroen): `length` can't be casted to win32.DWORD here because it'll overflow if > 4 GiB and return 0 if exactly that.
+ to_read := min(i64(length), MAX_RW)
+
+ if is_console {
+ total_read, err = read_console(handle, data[total_read:][:to_read])
+ if err != nil {
+ return total_read, err
+ }
+ } else {
+ // NOTE(Jeroen): So we cast it here *after* we've ensured that `to_read` is at most MAX_RW (1 GiB)
+ bytes_read: win32.DWORD
+ if e := win32.ReadFile(handle, &data[total_read], win32.DWORD(to_read), &bytes_read, nil); e {
+ // Successful read can mean two things, including EOF, see:
+ // https://learn.microsoft.com/en-us/windows/win32/fileio/testing-for-the-end-of-a-file
+ if bytes_read == 0 {
+ return 0, .EOF
+ } else {
+ return int(bytes_read), nil
+ }
+ } else {
+ return 0, get_last_error()
+ }
+ }
+ return total_read, nil
+}
+
+seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
+ w: u32
+ switch whence {
+ case 0: w = win32.FILE_BEGIN
+ case 1: w = win32.FILE_CURRENT
+ case 2: w = win32.FILE_END
+ case:
+ return 0, .Invalid_Whence
+ }
+ hi := i32(offset>>32)
+ lo := i32(offset)
+ ft := win32.GetFileType(win32.HANDLE(fd))
+ if ft == win32.FILE_TYPE_PIPE {
+ return 0, .File_Is_Pipe
+ }
+
+ dw_ptr := win32.SetFilePointer(win32.HANDLE(fd), lo, &hi, w)
+ if dw_ptr == win32.INVALID_SET_FILE_POINTER {
+ err := get_last_error()
+ return 0, err
+ }
+ return i64(hi)<<32 + i64(dw_ptr), nil
+}
+
+@(require_results)
+file_size :: proc(fd: Handle) -> (i64, Error) {
+ length: win32.LARGE_INTEGER
+ err: Error
+ if !win32.GetFileSizeEx(win32.HANDLE(fd), &length) {
+ err = get_last_error()
+ }
+ return i64(length), err
+}
+
+
+@(private)
+MAX_RW :: 1<<30
+
+@(private)
+pread :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
+ curr_off := seek(fd, 0, 1) or_return
+ defer seek(fd, curr_off, 0)
+
+ buf := data
+ if len(buf) > MAX_RW {
+ buf = buf[:MAX_RW]
+ }
+
+ o := win32.OVERLAPPED{
+ OffsetHigh = u32(offset>>32),
+ Offset = u32(offset),
+ }
+
+ // TODO(bill): Determine the correct behaviour for consoles
+
+ h := win32.HANDLE(fd)
+ done: win32.DWORD
+ e: Error
+ if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
+ e = get_last_error()
+ done = 0
+ }
+ return int(done), e
+}
+@(private)
+pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
+ curr_off := seek(fd, 0, 1) or_return
+ defer seek(fd, curr_off, 0)
+
+ buf := data
+ if len(buf) > MAX_RW {
+ buf = buf[:MAX_RW]
+ }
+
+ o := win32.OVERLAPPED{
+ OffsetHigh = u32(offset>>32),
+ Offset = u32(offset),
+ }
+
+ h := win32.HANDLE(fd)
+ done: win32.DWORD
+ e: Error
+ if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
+ e = get_last_error()
+ done = 0
+ }
+ return int(done), e
+}
+
+/*
+read_at returns n: 0, err: 0 on EOF
+*/
+read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
+ if offset < 0 {
+ return 0, .Invalid_Offset
+ }
+
+ b, offset := data, offset
+ for len(b) > 0 {
+ m, e := pread(fd, b, offset)
+ if e == ERROR_EOF {
+ err = nil
+ break
+ }
+ if e != nil {
+ err = e
+ break
+ }
+ n += m
+ b = b[m:]
+ offset += i64(m)
+ }
+ return
+}
+
+write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
+ if offset < 0 {
+ return 0, .Invalid_Offset
+ }
+
+ b, offset := data, offset
+ for len(b) > 0 {
+ m := pwrite(fd, b, offset) or_return
+ n += m
+ b = b[m:]
+ offset += i64(m)
+ }
+ return
+}
+
+
+
+// NOTE(bill): Uses startup to initialize it
+stdin := get_std_handle(uint(win32.STD_INPUT_HANDLE))
+stdout := get_std_handle(uint(win32.STD_OUTPUT_HANDLE))
+stderr := get_std_handle(uint(win32.STD_ERROR_HANDLE))
+
+
+@(require_results)
+get_std_handle :: proc "contextless" (h: uint) -> Handle {
+ fd := win32.GetStdHandle(win32.DWORD(h))
+ return Handle(fd)
+}
+
+
+exists :: proc(path: string) -> bool {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
+ wpath := win32.utf8_to_wstring(path, context.temp_allocator)
+ attribs := win32.GetFileAttributesW(wpath)
+
+ return attribs != win32.INVALID_FILE_ATTRIBUTES
+}
+
+@(require_results)
+is_file :: proc(path: string) -> bool {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
+ wpath := win32.utf8_to_wstring(path, context.temp_allocator)
+ attribs := win32.GetFileAttributesW(wpath)
+
+ if attribs != win32.INVALID_FILE_ATTRIBUTES {
+ return attribs & win32.FILE_ATTRIBUTE_DIRECTORY == 0
+ }
+ return false
+}
+
+@(require_results)
+is_dir :: proc(path: string) -> bool {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
+ wpath := win32.utf8_to_wstring(path, context.temp_allocator)
+ attribs := win32.GetFileAttributesW(wpath)
+
+ if attribs != win32.INVALID_FILE_ATTRIBUTES {
+ return attribs & win32.FILE_ATTRIBUTE_DIRECTORY != 0
+ }
+ return false
+}
+
+// NOTE(tetra): GetCurrentDirectory is not thread safe with SetCurrentDirectory and GetFullPathName
+@private cwd_lock := win32.SRWLOCK{} // zero is initialized
+
+@(require_results)
+get_current_directory :: proc(allocator := context.allocator) -> string {
+ win32.AcquireSRWLockExclusive(&cwd_lock)
+
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
+
+ sz_utf16 := win32.GetCurrentDirectoryW(0, nil)
+ dir_buf_wstr, _ := make([]u16, sz_utf16, context.temp_allocator) // the first time, it _includes_ the NUL.
+
+ sz_utf16 = win32.GetCurrentDirectoryW(win32.DWORD(len(dir_buf_wstr)), raw_data(dir_buf_wstr))
+ assert(int(sz_utf16)+1 == len(dir_buf_wstr)) // the second time, it _excludes_ the NUL.
+
+ win32.ReleaseSRWLockExclusive(&cwd_lock)
+
+ return win32.utf16_to_utf8(dir_buf_wstr, allocator) or_else ""
+}
+
+set_current_directory :: proc(path: string) -> (err: Error) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
+ wstr := win32.utf8_to_wstring(path, context.temp_allocator)
+
+ win32.AcquireSRWLockExclusive(&cwd_lock)
+
+ if !win32.SetCurrentDirectoryW(wstr) {
+ err = get_last_error()
+ }
+
+ win32.ReleaseSRWLockExclusive(&cwd_lock)
+
+ return
+}
+change_directory :: set_current_directory
+
+make_directory :: proc(path: string, mode: u32 = 0) -> (err: Error) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
+ // Mode is unused on Windows, but is needed on *nix
+ wpath := win32.utf8_to_wstring(path, context.temp_allocator)
+
+ if !win32.CreateDirectoryW(wpath, nil) {
+ err = get_last_error()
+ }
+ return
+}
+
+
+remove_directory :: proc(path: string) -> (err: Error) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
+ wpath := win32.utf8_to_wstring(path, context.temp_allocator)
+
+ if !win32.RemoveDirectoryW(wpath) {
+ err = get_last_error()
+ }
+ return
+}
+
+
+
+@(private, require_results)
+is_abs :: proc(path: string) -> bool {
+ if len(path) > 0 && path[0] == '/' {
+ return true
+ }
+ when ODIN_OS == .Windows {
+ if len(path) > 2 {
+ switch path[0] {
+ case 'A'..='Z', 'a'..='z':
+ return path[1] == ':' && is_path_separator(path[2])
+ }
+ }
+ }
+ return false
+}
+
+@(private, require_results)
+fix_long_path :: proc(path: string) -> string {
+ if len(path) < 248 {
+ return path
+ }
+
+ if len(path) >= 2 && path[:2] == `\\` {
+ return path
+ }
+ if !is_abs(path) {
+ return path
+ }
+
+ prefix :: `\\?`
+
+ path_buf, _ := make([]byte, len(prefix)+len(path)+len(`\`), context.temp_allocator)
+ copy(path_buf, prefix)
+ n := len(path)
+ r, w := 0, len(prefix)
+ for r < n {
+ switch {
+ case is_path_separator(path[r]):
+ r += 1
+ case path[r] == '.' && (r+1 == n || is_path_separator(path[r+1])):
+ r += 1
+ case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || is_path_separator(path[r+2])):
+ return path
+ case:
+ path_buf[w] = '\\'
+ w += 1
+ for ; r < n && !is_path_separator(path[r]); r += 1 {
+ path_buf[w] = path[r]
+ w += 1
+ }
+ }
+ }
+
+ if w == len(`\\?\c:`) {
+ path_buf[w] = '\\'
+ w += 1
+ }
+ return string(path_buf[:w])
+}
+
+
+link :: proc(old_name, new_name: string) -> (err: Error) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
+ n := win32.utf8_to_wstring(fix_long_path(new_name))
+ o := win32.utf8_to_wstring(fix_long_path(old_name))
+ return Platform_Error(win32.CreateHardLinkW(n, o, nil))
+}
+
+unlink :: proc(path: string) -> (err: Error) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
+ wpath := win32.utf8_to_wstring(path, context.temp_allocator)
+
+ if !win32.DeleteFileW(wpath) {
+ err = get_last_error()
+ }
+ return
+}
+
+
+
+rename :: proc(old_path, new_path: string) -> (err: Error) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
+ from := win32.utf8_to_wstring(old_path, context.temp_allocator)
+ to := win32.utf8_to_wstring(new_path, context.temp_allocator)
+
+ if !win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) {
+ err = get_last_error()
+ }
+ return
+}
+
+
+ftruncate :: proc(fd: Handle, length: i64) -> (err: Error) {
+ curr_off := seek(fd, 0, 1) or_return
+ defer seek(fd, curr_off, 0)
+ _= seek(fd, length, 0) or_return
+ ok := win32.SetEndOfFile(win32.HANDLE(fd))
+ if !ok {
+ return get_last_error()
+ }
+ return nil
+}
+
+truncate :: proc(path: string, length: i64) -> (err: Error) {
+ fd := open(path, O_WRONLY|O_CREATE, 0o666) or_return
+ defer close(fd)
+ return ftruncate(fd, length)
+}
+
+
+remove :: proc(name: string) -> Error {
+ p := win32.utf8_to_wstring(fix_long_path(name))
+ err, err1: win32.DWORD
+ if !win32.DeleteFileW(p) {
+ err = win32.GetLastError()
+ }
+ if err == 0 {
+ return nil
+ }
+ if !win32.RemoveDirectoryW(p) {
+ err1 = win32.GetLastError()
+ }
+ if err1 == 0 {
+ return nil
+ }
+
+ if err != err1 {
+ a := win32.GetFileAttributesW(p)
+ if a == ~u32(0) {
+ err = win32.GetLastError()
+ } else {
+ if a & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
+ err = err1
+ } else if a & win32.FILE_ATTRIBUTE_READONLY != 0 {
+ if win32.SetFileAttributesW(p, a &~ win32.FILE_ATTRIBUTE_READONLY) {
+ err = 0
+ if !win32.DeleteFileW(p) {
+ err = win32.GetLastError()
+ }
+ }
+ }
+ }
+ }
+
+ return Platform_Error(err)
+}
+
+
+@(require_results)
+pipe :: proc() -> (r, w: Handle, err: Error) {
+ sa: win32.SECURITY_ATTRIBUTES
+ sa.nLength = size_of(win32.SECURITY_ATTRIBUTES)
+ sa.bInheritHandle = true
+ if !win32.CreatePipe((^win32.HANDLE)(&r), (^win32.HANDLE)(&w), &sa, 0) {
+ err = get_last_error()
+ }
+ return
+}
\ No newline at end of file
diff --git a/core/os/stat_unix.odin b/core/os/stat_unix.odin
index 7f7985e83..648987a07 100644
--- a/core/os/stat_unix.odin
+++ b/core/os/stat_unix.odin
@@ -53,7 +53,7 @@ File_Info :: struct {
@(private, require_results)
_make_time_from_unix_file_time :: proc(uft: Unix_File_Time) -> time.Time {
return time.Time{
- _nsec = uft.nanoseconds + uft.seconds * 1_000_000_000,
+ _nsec = i64(uft.nanoseconds) + i64(uft.seconds) * 1_000_000_000,
}
}
diff --git a/core/path/filepath/match.odin b/core/path/filepath/match.odin
index 003f8046d..1f0ac9287 100644
--- a/core/path/filepath/match.odin
+++ b/core/path/filepath/match.odin
@@ -1,3 +1,4 @@
+#+build !wasi
package filepath
import "core:os"
diff --git a/core/path/filepath/path_unix.odin b/core/path/filepath/path_unix.odin
index 35b98a7ae..8bf412599 100644
--- a/core/path/filepath/path_unix.odin
+++ b/core/path/filepath/path_unix.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, freebsd, openbsd, netbsd
+#+build linux, darwin, freebsd, openbsd, netbsd, haiku
package filepath
import "base:runtime"
diff --git a/core/path/filepath/path_wasi.odin b/core/path/filepath/path_wasi.odin
new file mode 100644
index 000000000..74cc6ca1e
--- /dev/null
+++ b/core/path/filepath/path_wasi.odin
@@ -0,0 +1,36 @@
+package filepath
+
+import "base:runtime"
+
+import "core:strings"
+
+SEPARATOR :: '/'
+SEPARATOR_STRING :: `/`
+LIST_SEPARATOR :: ':'
+
+is_reserved_name :: proc(path: string) -> bool {
+ return false
+}
+
+is_abs :: proc(path: string) -> bool {
+ return strings.has_prefix(path, "/")
+}
+
+abs :: proc(path: string, allocator := context.allocator) -> (string, bool) {
+ if is_abs(path) {
+ return strings.clone(string(path), allocator), true
+ }
+
+ return path, false
+}
+
+join :: proc(elems: []string, allocator := context.allocator) -> (joined: string, err: runtime.Allocator_Error) #optional_allocator_error {
+ for e, i in elems {
+ if e != "" {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
+ p := strings.join(elems[i:], SEPARATOR_STRING, context.temp_allocator) or_return
+ return clean(p, allocator)
+ }
+ }
+ return "", nil
+}
diff --git a/core/path/filepath/walk.odin b/core/path/filepath/walk.odin
index 51dfa71d2..53b10eed7 100644
--- a/core/path/filepath/walk.odin
+++ b/core/path/filepath/walk.odin
@@ -1,3 +1,4 @@
+#+build !wasi
package filepath
import "core:os"
diff --git a/core/prof/spall/doc.odin b/core/prof/spall/doc.odin
index c81bad05f..b007ad4cb 100644
--- a/core/prof/spall/doc.odin
+++ b/core/prof/spall/doc.odin
@@ -18,6 +18,8 @@ Example:
defer spall.context_destroy(&spall_ctx)
buffer_backing := make([]u8, spall.BUFFER_DEFAULT_SIZE)
+ defer delete(buffer_backing)
+
spall_buffer = spall.buffer_create(buffer_backing, u32(sync.current_thread_id()))
defer spall.buffer_destroy(&spall_ctx, &spall_buffer)
diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin
index 7f79acb77..b3315a0c3 100644
--- a/core/reflect/reflect.odin
+++ b/core/reflect/reflect.odin
@@ -24,7 +24,6 @@ Type_Info_Enumerated_Array :: runtime.Type_Info_Enumerated_Array
Type_Info_Dynamic_Array :: runtime.Type_Info_Dynamic_Array
Type_Info_Slice :: runtime.Type_Info_Slice
Type_Info_Parameters :: runtime.Type_Info_Parameters
-Type_Info_Tuple :: runtime.Type_Info_Parameters
Type_Info_Struct :: runtime.Type_Info_Struct
Type_Info_Union :: runtime.Type_Info_Union
Type_Info_Enum :: runtime.Type_Info_Enum
@@ -58,7 +57,7 @@ Type_Kind :: enum {
Enumerated_Array,
Dynamic_Array,
Slice,
- Tuple,
+ Parameters,
Struct,
Union,
Enum,
@@ -93,7 +92,7 @@ type_kind :: proc(T: typeid) -> Type_Kind {
case Type_Info_Enumerated_Array: return .Enumerated_Array
case Type_Info_Dynamic_Array: return .Dynamic_Array
case Type_Info_Slice: return .Slice
- case Type_Info_Parameters: return .Tuple
+ case Type_Info_Parameters: return .Parameters
case Type_Info_Struct: return .Struct
case Type_Info_Union: return .Union
case Type_Info_Enum: return .Enum
@@ -176,6 +175,7 @@ typeid_elem :: proc(id: typeid) -> typeid {
case Type_Info_Enumerated_Array: return v.elem.id
case Type_Info_Slice: return v.elem.id
case Type_Info_Dynamic_Array: return v.elem.id
+ case Type_Info_Simd_Vector: return v.elem.id
}
return id
}
@@ -260,7 +260,11 @@ length :: proc(val: any) -> int {
} else {
return (^runtime.Raw_String)(val.data).len
}
+
+ case Type_Info_Simd_Vector:
+ return a.count
}
+
return 0
}
@@ -286,7 +290,11 @@ capacity :: proc(val: any) -> int {
case Type_Info_Map:
return runtime.map_cap((^runtime.Raw_Map)(val.data)^)
+
+ case Type_Info_Simd_Vector:
+ return a.count
}
+
return 0
}
@@ -1431,6 +1439,11 @@ as_f64 :: proc(a: any) -> (value: f64, valid: bool) {
case Type_Info_Complex:
switch v in a {
+ case complex32:
+ if imag(v) == 0 {
+ value = f64(real(v))
+ valid = true
+ }
case complex64:
if imag(v) == 0 {
value = f64(real(v))
@@ -1445,6 +1458,11 @@ as_f64 :: proc(a: any) -> (value: f64, valid: bool) {
case Type_Info_Quaternion:
switch v in a {
+ case quaternion64:
+ if imag(v) == 0 && jmag(v) == 0 && kmag(v) == 0 {
+ value = f64(real(v))
+ valid = true
+ }
case quaternion128:
if imag(v) == 0 && jmag(v) == 0 && kmag(v) == 0 {
value = f64(real(v))
@@ -1638,13 +1656,40 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
return equal(va, vb, including_indirect_array_recursion, recursion_level+1)
case Type_Info_Map:
return false
+ case Type_Info_Float:
+ x, _ := as_f64(a)
+ y, _ := as_f64(b)
+ return x == y
+ case Type_Info_Complex:
+ switch x in a {
+ case complex32:
+ #no_type_assert y := b.(complex32)
+ return x == y
+ case complex64:
+ #no_type_assert y := b.(complex64)
+ return x == y
+ case complex128:
+ #no_type_assert y := b.(complex128)
+ return x == y
+ }
+ return false
+ case Type_Info_Quaternion:
+ switch x in a {
+ case quaternion64:
+ #no_type_assert y := b.(quaternion64)
+ return x == y
+ case quaternion128:
+ #no_type_assert y := b.(quaternion128)
+ return x == y
+ case quaternion256:
+ #no_type_assert y := b.(quaternion256)
+ return x == y
+ }
+ return false
case
Type_Info_Boolean,
Type_Info_Integer,
Type_Info_Rune,
- Type_Info_Float,
- Type_Info_Complex,
- Type_Info_Quaternion,
Type_Info_Type_Id,
Type_Info_Pointer,
Type_Info_Multi_Pointer,
diff --git a/core/reflect/types.odin b/core/reflect/types.odin
index cb31a27e2..ba47fee4d 100644
--- a/core/reflect/types.odin
+++ b/core/reflect/types.odin
@@ -348,12 +348,6 @@ is_parameters :: proc(info: ^Type_Info) -> bool {
_, ok := type_info_base(info).variant.(Type_Info_Parameters)
return ok
}
-@(require_results, deprecated="prefer is_parameters")
-is_tuple :: proc(info: ^Type_Info) -> bool {
- if info == nil { return false }
- _, ok := type_info_base(info).variant.(Type_Info_Parameters)
- return ok
-}
@(require_results)
is_struct :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
diff --git a/core/simd/simd.odin b/core/simd/simd.odin
index 01d11dfbe..37cc19ebd 100644
--- a/core/simd/simd.odin
+++ b/core/simd/simd.odin
@@ -1,182 +1,2424 @@
+/*
+The SIMD support package.
+
+SIMD (Single Instruction Multiple Data), is a CPU hardware feature that
+introduce special registers and instructions which operate on multiple units
+of data at the same time, which enables faster data processing for
+applications with heavy computational workloads.
+
+In Odin SIMD is exposed via a special kinds of arrays, called the *SIMD
+vectors*. The types of SIMD vectors is written as `#simd [N]T`, where N is a
+power of two, and T could be any basic type (integers, floats, etc.). The
+documentation of this package will call *SIMD vectors* just *vectors*.
+
+SIMD vectors consist of elements, called *scalar values*, or
+*scalars*, each occupying a *lane* of the SIMD vector. In the type declaration,
+`N` specifies the amount of lanes, or values, that a vector stores.
+
+This package implements procedures for working with vectors.
+*/
package simd
import "base:builtin"
import "base:intrinsics"
-// IS_EMULATED is true iff the compile-time target lacks hardware support
-// for at least 128-bit SIMD.
+/*
+Check if SIMD is software-emulated on a target platform.
+
+This value is `false`, when the compile-time target has the hardware support for
+at 128-bit (or wider) SIMD. If the compile-time target lacks the hardware support
+for 128-bit SIMD, this value is `true`, and all SIMD operations will likely be
+emulated.
+*/
IS_EMULATED :: true when (ODIN_ARCH == .amd64 || ODIN_ARCH == .i386) && !intrinsics.has_target_feature("sse2") else
true when (ODIN_ARCH == .arm64 || ODIN_ARCH == .arm32) && !intrinsics.has_target_feature("neon") else
true when (ODIN_ARCH == .wasm64p32 || ODIN_ARCH == .wasm32) && !intrinsics.has_target_feature("simd128") else
true when (ODIN_ARCH == .riscv64) && !intrinsics.has_target_feature("v") else
false
-// 128-bit vector aliases
+/*
+Vector of 16 `u8` lanes (128 bits).
+*/
u8x16 :: #simd[16]u8
+
+/*
+Vector of 16 `i8` lanes (128 bits).
+*/
i8x16 :: #simd[16]i8
+
+/*
+Vector of 8 `u16` lanes (128 bits).
+*/
u16x8 :: #simd[8]u16
+
+/*
+Vector of 8 `i16` lanes (128 bits).
+*/
i16x8 :: #simd[8]i16
+
+/*
+Vector of 4 `u32` lanes (128 bits).
+*/
u32x4 :: #simd[4]u32
+
+/*
+Vector of 4 `i32` lanes (128 bits).
+*/
i32x4 :: #simd[4]i32
+
+/*
+Vector of 2 `u64` lanes (128 bits).
+*/
u64x2 :: #simd[2]u64
+
+/*
+Vector of 2 `i64` lanes (128 bits).
+*/
i64x2 :: #simd[2]i64
+
+/*
+Vector of 4 `f32` lanes (128 bits).
+*/
f32x4 :: #simd[4]f32
+
+/*
+Vector of 2 `f64` lanes (128 bits).
+*/
f64x2 :: #simd[2]f64
+/*
+Vector of 16 `bool` lanes (128 bits).
+*/
boolx16 :: #simd[16]bool
+
+/*
+Vector of 16 `b8` lanes (128 bits).
+*/
b8x16 :: #simd[16]b8
+
+/*
+Vector of 8 `b16` lanes (128 bits).
+*/
b16x8 :: #simd[8]b16
+
+/*
+Vector of 4 `b32` lanes (128 bits).
+*/
b32x4 :: #simd[4]b32
+
+/*
+Vector of 2 `b64` lanes (128 bits).
+*/
b64x2 :: #simd[2]b64
-// 256-bit vector aliases
+/*
+Vector of 32 `u8` lanes (256 bits).
+*/
u8x32 :: #simd[32]u8
+
+/*
+Vector of 32 `i8` lanes (256 bits).
+*/
i8x32 :: #simd[32]i8
+
+/*
+Vector of 16 `u16` lanes (256 bits).
+*/
u16x16 :: #simd[16]u16
+
+/*
+Vector of 16 `i16` lanes (256 bits).
+*/
i16x16 :: #simd[16]i16
+
+/*
+Vector of 8 `u32` lanes (256 bits).
+*/
u32x8 :: #simd[8]u32
+
+/*
+Vector of 8 `i32` lanes (256 bits).
+*/
i32x8 :: #simd[8]i32
+
+/*
+Vector of 4 `u64` lanes (256 bits).
+*/
u64x4 :: #simd[4]u64
+
+/*
+Vector of 4 `i64` lanes (256 bits).
+*/
i64x4 :: #simd[4]i64
+
+/*
+Vector of 8 `f32` lanes (256 bits).
+*/
f32x8 :: #simd[8]f32
+
+/*
+Vector of 4 `f64` lanes (256 bits).
+*/
f64x4 :: #simd[4]f64
+/*
+Vector of 32 `bool` lanes (256 bits).
+*/
boolx32 :: #simd[32]bool
+
+/*
+Vector of 32 `b8` lanes (256 bits).
+*/
b8x32 :: #simd[32]b8
+
+/*
+Vector of 16 `b16` lanes (256 bits).
+*/
b16x16 :: #simd[16]b16
+
+/*
+Vector of 8 `b32` lanes (256 bits).
+*/
b32x8 :: #simd[8]b32
+
+/*
+Vector of 4 `b64` lanes (256 bits).
+*/
b64x4 :: #simd[4]b64
-// 512-bit vector aliases
+/*
+Vector of 64 `u8` lanes (512 bits).
+*/
u8x64 :: #simd[64]u8
+
+/*
+Vector of 64 `i8` lanes (512 bits).
+*/
i8x64 :: #simd[64]i8
+
+/*
+Vector of 32 `u16` lanes (512 bits).
+*/
u16x32 :: #simd[32]u16
+
+/*
+Vector of 32 `i16` lanes (512 bits).
+*/
i16x32 :: #simd[32]i16
+
+/*
+Vector of 16 `u32` lanes (512 bits).
+*/
u32x16 :: #simd[16]u32
+
+/*
+Vector of 16 `i32` lanes (512 bits).
+*/
i32x16 :: #simd[16]i32
+
+/*
+Vector of 8 `u64` lanes (512 bits).
+*/
u64x8 :: #simd[8]u64
+
+/*
+Vector of 8 `i64` lanes (512 bits).
+*/
i64x8 :: #simd[8]i64
+
+/*
+Vector of 16 `f32` lanes (512 bits).
+*/
f32x16 :: #simd[16]f32
+
+/*
+Vector of 8 `f64` lanes (512 bits).
+*/
f64x8 :: #simd[8]f64
+/*
+Vector of 64 `bool` lanes (512 bits).
+*/
boolx64 :: #simd[64]bool
+
+/*
+Vector of 64 `b8` lanes (512 bits).
+*/
b8x64 :: #simd[64]b8
+
+/*
+Vector of 32 `b16` lanes (512 bits).
+*/
b16x32 :: #simd[32]b16
+
+/*
+Vector of 16 `b32` lanes (512 bits).
+*/
b32x16 :: #simd[16]b32
+
+/*
+Vector of 8 `b64` lanes (512 bits).
+*/
b64x8 :: #simd[8]b64
+/*
+Add SIMD vectors.
+This procedure returns a vector, where each lane holds the sum of the
+corresponding `a` and `b` vectors' lanes.
+
+Inputs:
+- `a`: An integer or a float vector.
+- `b`: An integer or a float vector.
+
+Returns:
+- A vector that is the sum of two input vectors.
+
+**Operation**:
+
+ for i in 0 ..< len(res) {
+ res[i] = a[i] + b[i]
+ }
+ return res
+
+Example:
+
+ +-----+-----+-----+-----+
+ a: | 0 | 1 | 2 | 3 |
+ +-----+-----+-----+-----+
+ +-----+-----+-----+-----+
+ b: | 0 | 1 | 2 | -1 |
+ +-----+-----+-----+-----+
+ res:
+ +-----+-----+-----+-----+
+ | 0 | 2 | 4 | 2 |
+ +-----+-----+-----+-----+
+*/
add :: intrinsics.simd_add
-sub :: intrinsics.simd_sub
-mul :: intrinsics.simd_mul
-div :: intrinsics.simd_div // floats only
-// Keeps Odin's Behaviour
-// (x << y) if y <= mask else 0
+/*
+Subtract SIMD vectors.
+
+This procedure returns a vector, where each lane holds the difference between
+the corresponding lanes of the vectors `a` and `b`. The lanes from the vector
+`b` are subtracted from the corresponding lanes of the vector `a`.
+
+Inputs:
+- `a`: An integer or a float vector to subtract from.
+- `b`: An integer or a float vector.
+
+Returns:
+- A vector that is the difference of two vectors, `a` - `b`.
+
+**Operation**:
+
+ for i in 0 ..< len(res) {
+ res[i] = a[i] - b[i]
+ }
+ return res
+
+Example:
+
+ +-----+-----+-----+-----+
+ a: | 2 | 2 | 2 | 2 |
+ +-----+-----+-----+-----+
+ +-----+-----+-----+-----+
+ b: | 0 | 1 | 2 | 3 |
+ +-----+-----+-----+-----+
+ res:
+ +-----+-----+-----+-----+
+ | 2 | 1 | 0 | -1 |
+ +-----+-----+-----+-----+
+*/
+sub :: intrinsics.simd_sub
+
+/*
+Multiply (component-wise) SIMD vectors.
+
+This procedure returns a vector, where each lane holds the product of the
+corresponding lanes of the vectors `a` and `b`.
+
+Inputs:
+- `a`: An integer or a float vector.
+- `b`: An integer or a float vector.
+
+Returns:
+- A vector that is the product of two vectors.
+
+**Operation**:
+
+ for i in 0 ..< len(res) {
+ res[i] = a[i] * b[i]
+ }
+ return res
+
+Example:
+
+ +-----+-----+-----+-----+
+ a: | 2 | 2 | 2 | 2 |
+ +-----+-----+-----+-----+
+ +-----+-----+-----+-----+
+ b: | 0 | -1 | 2 | -3 |
+ +-----+-----+-----+-----+
+ res:
+ +-----+-----+-----+-----+
+ | 0 | -2 | 4 | -6 |
+ +-----+-----+-----+-----+
+*/
+mul :: intrinsics.simd_mul
+
+/*
+Divide SIMD vectors.
+
+This procedure returns a vector, where each lane holds the quotient (result
+of division) between the corresponding lanes of the vectors `a` and `b`. Each
+lane of the vector `a` is divided by the corresponding lane of the vector `b`.
+
+This operation performs a standard floating-point division for each lane.
+
+Inputs:
+- `a`: A float vector.
+- `b`: A float vector to divide by.
+
+Returns:
+- A vector that is the quotient of two vectors, `a` / `b`.
+
+**Operation**:
+
+ for i in 0 ..< len(res) {
+ res[i] = a[i] / b[i]
+ }
+ return res
+
+Example:
+
+ +-----+-----+-----+-----+
+ a: | 2 | 2 | 2 | 2 |
+ +-----+-----+-----+-----+
+ +-----+-----+-----+-----+
+ b: | 0 | -1 | 2 | -3 |
+ +-----+-----+-----+-----+
+ res:
+ +-----+-----+-----+------+
+ | +∞ | -2 | 1 | -2/3 |
+ +-----+-----+-----+------+
+*/
+div :: intrinsics.simd_div
+
+/*
+Shift left lanes of a vector.
+
+This procedure returns a vector, such that each lane holds the result of a
+shift-left (aka shift-up) operation of the corresponding lane from vector `a` by the shift
+amount from the corresponding lane of the vector `b`.
+
+If the shift amount is greater than the bit-width of a lane, the result is `0`
+in the corresponding positions of the result.
+
+Inputs:
+- `a`: An integer vector of values to shift.
+- `b`: An unsigned integer vector of the shift amounts.
+
+Result:
+- A vector, where each lane is the lane from `a` shifted left by the amount
+specified in the corresponding lane of the vector `b`.
+
+**Operation**:
+
+ for i in 0 ..< len(res) {
+ if b[i] < 8*size_of(a[i]) {
+ res[i] = a[i] << b[i]
+ } else {
+ res[i] = 0
+ }
+ }
+ return res
+
+Example:
+
+ // An example for a 4-lane 8-bit signed integer vector `a`.
+
+ +-------+-------+-------+-------+
+ a: | 0x11 | 0x55 | 0x03 | 0xff |
+ +-------+-------+-------+-------+
+ +-------+-------+-------+-------+
+ b: | 2 | 1 | 33 | 1 |
+ +-------+-------+-------+-------+
+ res:
+ +-------+-------+-------+--------+
+ | 0x44 | 0xaa | 0 | 0xfe |
+ +-------+-------+-------+--------+
+*/
shl :: intrinsics.simd_shl
+
+/*
+Shift right lanes of a vector.
+
+This procedure returns a vector, such that each lane holds the result of a
+shift-right (aka shift-down) operation, of lane from the vector `a` by the shift
+amount from the corresponding lane of the vector `b`.
+
+If the shift amount is greater than the bit-width of a lane, the result is `0`
+in the corresponding positions of the result.
+
+If the first vector is a vector of signed integers, the arithmetic shift
+operation is performed. Otherwise, if the first vector is a vector of unsigned
+integers, a logical shift is performed.
+
+Inputs:
+- `a`: An integer vector of values to shift.
+- `b`: An unsigned integer vector of the shift amounts.
+
+Result:
+- A vector, where each lane is the lane from `a` shifted right by the amount
+specified in the corresponding lane of the vector `b`.
+
+**Operation**:
+
+ for i in 0 ..< len(res) {
+ if b[i] < 8*size_of(a[i]) {
+ res[i] = a[i] >> b[i]
+ } else {
+ res[i] = 0
+ }
+ }
+ return res
+
+Example:
+
+ // An example for a 4-lane 8-bit signed integer vector `a`.
+
+ +-------+-------+-------+-------+
+ a: | 0x11 | 0x55 | 0x03 | 0xff |
+ +-------+-------+-------+-------+
+ +-------+-------+-------+-------+
+ b: | 2 | 1 | 33 | 1 |
+ +-------+-------+-------+-------+
+ res:
+ +-------+-------+-------+--------+
+ | 0x04 | 0x2a | 0 | 0xff |
+ +-------+-------+-------+--------+
+*/
shr :: intrinsics.simd_shr
-// Similar to C's Behaviour
-// x << (y & mask)
+/*
+Shift left lanes of a vector (masked).
+
+This procedure returns a vector, such that each lane holds the result of a
+shift-left (aka shift-up) operation, of lane from the vector `a` by the shift
+amount from the corresponding lane of the vector `b`.
+
+The shift amount is wrapped (masked) to the bit-width of the lane.
+
+Inputs:
+- `a`: An integer vector of values to shift.
+- `b`: An unsigned integer vector of the shift amounts.
+
+Result:
+- A vector, where each lane is the lane from `a` shifted left by the amount
+specified in the corresponding lane of the vector `b`.
+
+**Operation**:
+
+ for i in 0 ..< len(res) {
+ mask := 8*size_of(a[i]) - 1
+ res[i] = a[i] << (b[i] & mask)
+ }
+ return res
+
+Example:
+
+ // An example for a 4-lane vector `a` of 8-bit signed integers.
+
+ +-------+-------+-------+-------+
+ a: | 0x11 | 0x55 | 0x03 | 0xff |
+ +-------+-------+-------+-------+
+ +-------+-------+-------+-------+
+ b: | 2 | 1 | 33 | 1 |
+ +-------+-------+-------+-------+
+ res:
+ +-------+-------+-------+--------+
+ | 0x44 | 0xaa | 0x06 | 0xfe |
+ +-------+-------+-------+--------+
+*/
shl_masked :: intrinsics.simd_shl_masked
+
+/*
+Shift right lanes of a vector (masked).
+
+This procedure returns a vector, such that each lane holds the result of a
+shift-right (aka shift-down) operation, of lane from the vector `a` by the shift
+amount from the corresponding lane of the vector `b`.
+
+The shift amount is wrapped (masked) to the bit-width of the lane.
+
+If the first vector is a vector of signed integers, the arithmetic shift
+operation is performed. Otherwise, if the first vector is a vector of unsigned
+integers, a logical shift is performed.
+
+Inputs:
+- `a`: An integer vector of values to shift.
+- `b`: An unsigned integer vector of the shift amounts.
+
+Result:
+- A vector, where each lane is the lane from `a` shifted right by the amount
+specified in the corresponding lane of the vector `b`.
+
+**Operation**:
+
+ for i in 0 ..< len(res) {
+ mask := 8*size_of(a[i]) - 1
+ res[i] = a[i] >> (b[i] & mask)
+ }
+ return res
+
+Example:
+
+ // An example for a 4-lane vector `a` of 8-bit signed integers.
+
+ +-------+-------+-------+-------+
+ a: | 0x11 | 0x55 | 0x03 | 0xff |
+ +-------+-------+-------+-------+
+ +-------+-------+-------+-------+
+ b: | 2 | 1 | 33 | 1 |
+ +-------+-------+-------+-------+
+ res:
+ +-------+-------+-------+--------+
+ | 0x04 | 0x2a | 0x01 | 0xff |
+ +-------+-------+-------+--------+
+*/
shr_masked :: intrinsics.simd_shr_masked
-// Saturation Arithmetic
+/*
+Saturated addition of SIMD vectors.
+
+The *saturated sum* is a just like a normal sum, except the treatment of the
+result upon overflow or underflow is different. In saturated operations, the
+result is not wrapped to the bit-width of the lane, and instead is kept clamped
+between the minimum and the maximum values of the lane type.
+
+This procedure returns a vector where each lane is the saturated sum of the
+corresponding lanes of vectors `a` and `b`.
+
+Inputs:
+- `a`: An integer vector.
+- `b`: An integer vector.
+
+Returns:
+- The saturated sum of the two vectors.
+
+**Operation**:
+
+ for i in 0 ..< len(res) {
+ switch {
+ case b[i] >= max(type_of(a[i])) - a[i]: // (overflow of a[i])
+ res[i] = max(type_of(a[i]))
+ case b[i] <= min(type_of(a[i])) - a[i]: // (underflow of a[i])
+ res[i] = min(type_of(a[i]))
+ } else {
+ res[i] = a[i] + b[i]
+ }
+ }
+ return res
+
+Example:
+
+ // An example for a 4-lane vector `a` of 8-bit signed integers.
+
+ +-----+-----+-----+-----+
+ a: | 0 | 255 | 2 | 3 |
+ +-----+-----+-----+-----+
+ +-----+-----+-----+-----+
+ b: | 1 | 3 | 2 | -1 |
+ +-----+-----+-----+-----+
+ res:
+ +-----+-----+-----+-----+
+ | 1 | 255 | 4 | 2 |
+ +-----+-----+-----+-----+
+*/
saturating_add :: intrinsics.simd_saturating_add
+
+/*
+Saturated subtraction of 2 lanes of vectors.
+
+The *saturated difference* is a just like a normal difference, except the treatment of the
+result upon overflow or underflow is different. In saturated operations, the
+result is not wrapped to the bit-width of the lane, and instead is kept clamped
+between the minimum and the maximum values of the lane type.
+
+This procedure returns a vector where each lane is the saturated difference of
+the corresponding lanes of vectors `a` and `b`.
+
+Inputs:
+- `a`: An integer vector to subtract from.
+- `b`: An integer vector.
+
+Returns:
+- The saturated difference of the two vectors.
+
+**Operation**:
+
+ for i in 0 ..< len(res) {
+ switch {
+ case b[i] >= max(type_of(a[i])) + a[i]: // (overflow of a[i])
+ res[i] = max(type_of(a[i]))
+ case b[i] <= min(type_of(a[i])) + a[i]: // (underflow of a[i])
+ res[i] = min(type_of(a[i]))
+ } else {
+ res[i] = a[i] - b[i]
+ }
+ }
+ return res
+
+Example:
+
+ // An example for a 4-lane vector `a` of 8-bit signed integers.
+
+ +-----+-----+-----+-----+
+ a: | 0 | 255 | 2 | 3 |
+ +-----+-----+-----+-----+
+ +-----+-----+-----+-----+
+ b: | 3 | 3 | 2 | -1 |
+ +-----+-----+-----+-----+
+ res:
+ +-----+-----+-----+-----+
+ | 0 | 252 | 0 | 4 |
+ +-----+-----+-----+-----+
+*/
saturating_sub :: intrinsics.simd_saturating_sub
+/*
+Bitwise AND of vectors.
+
+This procedure returns a vector, such that each lane has the result of a bitwise
+AND operation between the corresponding lanes of the vectors `a` and `b`.
+
+Inputs:
+- `a`: An integer or a boolean vector.
+- `b`: An integer or a boolean vector.
+
+Returns:
+- A vector that is the result of the bitwise AND operation between two vectors.
+
+**Operation**:
+
+ for i in 0 ..< len(res) {
+ res[i] = a[i] & b[i]
+ }
+ return res
+
+Example:
+
+ +------+------+------+------+
+ a: | 0x11 | 0x33 | 0x55 | 0xaa |
+ +------+------+------+------+
+ +------+------+------+------+
+ b: | 0xff | 0xf0 | 0x0f | 0x00 |
+ +------+------+------+------+
+ res:
+ +------+------+------+------+
+ | 0x11 | 0x30 | 0x05 | 0x00 |
+ +------+------+------+------+
+*/
bit_and :: intrinsics.simd_bit_and
+
+/*
+Bitwise OR of vectors.
+
+This procedure returns a vector, such that each lane has the result of a bitwise
+OR operation between the corresponding lanes of the vectors `a` and `b`.
+
+Inputs:
+- `a`: An integer or a boolean vector.
+- `b`: An integer or a boolean vector.
+
+Returns:
+- A vector that is the result of the bitwise OR operation between two vectors.
+
+**Operation**:
+
+ for i in 0 ..< len(res) {
+ res[i] = a[i] | b[i]
+ }
+ return res
+
+Example:
+
+ +------+------+------+------+
+ a: | 0x11 | 0x33 | 0x55 | 0xaa |
+ +------+------+------+------+
+ +------+------+------+------+
+ b: | 0xff | 0xf0 | 0x0f | 0x00 |
+ +------+------+------+------+
+ res:
+ +------+------+------+------+
+ | 0xff | 0xf3 | 0x5f | 0xaa |
+ +------+------+------+------+
+*/
bit_or :: intrinsics.simd_bit_or
+
+/*
+Bitwise XOR of vectors.
+
+This procedure returns a vector, such that each lane has the result of a bitwise
+XOR operation between the corresponding lanes of the vectors `a` and `b`.
+
+Inputs:
+- `a`: An integer or a boolean vector.
+- `b`: An integer or a boolean vector.
+
+Returns:
+- A vector that is the result of the bitwise XOR operation between two vectors.
+
+**Operation**:
+
+ for i in 0 ..< len(res) {
+ res[i] = a[i] ~ b[i]
+ }
+ return res
+
+Example:
+
+ +------+------+------+------+
+ a: | 0x11 | 0x33 | 0x55 | 0xaa |
+ +------+------+------+------+
+ +------+------+------+------+
+ b: | 0xff | 0xf0 | 0x0f | 0x00 |
+ +------+------+------+------+
+ res:
+ +------+------+------+------+
+ | 0xee | 0xc3 | 0x5a | 0xaa |
+ +------+------+------+------+
+*/
bit_xor :: intrinsics.simd_bit_xor
+
+/*
+Bitwise AND NOT of vectors.
+
+This procedure returns a vector, such that each lane has the result of a bitwise
+AND NOT operation between the corresponding lanes of the vectors `a` and `b`.
+
+Inputs:
+- `a`: An integer or a boolean vector.
+- `b`: An integer or a boolean vector.
+
+Returns:
+- A vector that is the result of the bitwise AND NOT operation between two vectors.
+
+**Operation**:
+
+ for i in 0 ..< len(res) {
+ res[i] = a[i] &~ b[i]
+ }
+ return res
+
+Example:
+
+ +------+------+------+------+
+ a: | 0x11 | 0x33 | 0x55 | 0xaa |
+ +------+------+------+------+
+ +------+------+------+------+
+ b: | 0xff | 0xf0 | 0x0f | 0x00 |
+ +------+------+------+------+
+ res:
+ +------+------+------+------+
+ | 0x00 | 0x03 | 0x50 | 0xaa |
+ +------+------+------+------+
+*/
bit_and_not :: intrinsics.simd_bit_and_not
+/*
+Negation of a SIMD vector.
+
+This procedure returns a vector where each lane is the negation of the
+corresponding lane in the vector `a`.
+
+Inputs:
+- `a`: An integer or a float vector to negate.
+
+Returns:
+- The negated version of the vector `a`.
+
+**Operation**:
+
+ for i in 0 ..< len(res) {
+ res[i] = -a[i]
+ }
+ return res
+
+Example:
+
+ +------+------+------+------+
+ a: | 0 | 1 | 2 | 3 |
+ +------+------+------+------+
+ res:
+ +------+------+------+------+
+ | 0 | -1 | -2 | -3 |
+ +------+------+------+------+
+*/
neg :: intrinsics.simd_neg
+/*
+Absolute value of a SIMD vector.
+
+This procedure returns a vector where each lane has the absolute value of the
+corresponding lane in the vector `a`.
+
+Inputs:
+- `a`: An integer or a float vector to negate
+
+Returns:
+- The absolute value of a vector.
+
+**Operation**:
+
+ for i in 0 ..< len(res) {
+ switch {
+ case a[i] < 0: res[i] = -a[i]
+ case a[i] > 0: res[i] = a[i]
+ case a[i] == 0: res[i] = 0
+ }
+ }
+ return res
+
+Example:
+
+ +------+------+------+------+
+ a: | 0 | -1 | 2 | -3 |
+ +------+------+------+------+
+ res:
+ +------+------+------+------+
+ | 0 | 1 | 2 | 3 |
+ +------+------+------+------+
+*/
abs :: intrinsics.simd_abs
+/*
+Minimum of each lane of vectors.
+
+This procedure returns a vector, such that each lane has the minimum value
+between the corresponding lanes in vectors `a` and `b`.
+
+Inputs:
+- `a`: An integer or a float vector.
+- `b`: An integer or a float vector.
+
+Returns:
+- A vector containing with minimum values from corresponding lanes of `a` and `b`.
+
+**Operation**:
+
+ for i in 0 ..< len(res) {
+ if a[i] < b[i] {
+ res[i] = a[i]
+ } else {
+ res[i] = b[i]
+ }
+ }
+ return res
+
+Example:
+
+ +-----+-----+-----+-----+
+ a: | 0 | 1 | 2 | 3 |
+ +-----+-----+-----+-----+
+ +-----+-----+-----+-----+
+ b: | 0 | 2 | 1 | -1 |
+ +-----+-----+-----+-----+
+ res:
+ +-----+-----+-----+-----+
+ | 0 | 1 | 1 | -1 |
+ +-----+-----+-----+-----+
+*/
min :: intrinsics.simd_min
+
+/*
+Maximum of each lane of vectors.
+
+This procedure returns a vector, such that each lane has the maximum value
+between the corresponding lanes in vectors `a` and `b`.
+
+Inputs:
+- `a`: An integer or a float vector.
+- `b`: An integer or a float vector.
+
+Returns:
+- A vector containing with maximum values from corresponding lanes of `a` and `b`.
+
+**Operation**:
+
+ for i in 0 ..< len(res) {
+ if a[i] > b[i] {
+ res[i] = a[i]
+ } else {
+ res[i] = b[i]
+ }
+ }
+ return res
+
+Example:
+
+ +-----+-----+-----+-----+
+ a: | 0 | 1 | 2 | 3 |
+ +-----+-----+-----+-----+
+ +-----+-----+-----+-----+
+ b: | 0 | 2 | 1 | -1 |
+ +-----+-----+-----+-----+
+ res:
+ +-----+-----+-----+-----+
+ | 0 | 2 | 2 | 3 |
+ +-----+-----+-----+-----+
+*/
max :: intrinsics.simd_max
+
+/*
+Clamp lanes of vector.
+
+This procedure returns a vector, where each lane is the result of the
+clamping of the lane from the vector `v` between the values in the corresponding
+lanes of vectors `min` and `max`.
+
+Inputs:
+- `v`: An integer or a float vector with values to be clamped.
+- `min`: An integer or a float vector with minimum bounds.
+- `max`: An integer or a float vectoe with maximum bounds.
+
+Returns:
+- A vector containing clamped values in each lane.
+
+**Operation**:
+
+ for i in 0 ..< len(res) {
+ val := v[i]
+ switch {
+ case val < min: val = min
+ case val > max: val = max
+ }
+ res[i] = val
+ }
+ return res
+
+Example:
+
+ +-------+-------+-------+-------+
+ v: | -1 | 0.3 | 1.2 | 1 |
+ +-------+-------+-------+-------+
+ +-------+-------+-------+-------+
+ min: | 0 | 0 | 0 | 0 |
+ +-------+-------+-------+-------+
+ +-------+-------+-------+-------+
+ max: | 1 | 1 | 1 | 1 |
+ +-------+-------+-------+-------+
+ res:
+ +-------+-------+-------+-------+
+ | 0 | 0.3 | 1 | 1 |
+ +-------+-------+-------+-------+
+*/
clamp :: intrinsics.simd_clamp
-// Return an unsigned integer of the same size as the input type
-// NOT A BOOLEAN
-// element-wise:
-// false => 0x00...00
-// true => 0xff...ff
+/*
+Check if lanes of vectors are equal.
+
+This procedure checks each pair of lanes from vectors `a` and `b` for whether
+they are equal, and if they are, the corresponding lane of the result vector
+will have a value with all bits set (`0xff..ff`). Otherwise the lane of the
+result vector will have the value `0`.
+
+Inputs:
+- `a`: An integer, a float or a boolean vector.
+- `b`: An integer, a float or a boolean vector.
+
+Returns:
+- A vector of unsigned integers of the same size as the input vector's lanes,
+containing the comparison results for each lane.
+
+**Operation**:
+
+ for i in 0 ..< len(res) {
+ if a[i] == b[i] {
+ res[i] = max(T)
+ } else {
+ res[i] = 0
+ }
+ }
+ return res
+
+Example:
+
+ +-------+-------+-------+-------+
+ a: | 0 | 1 | 2 | 3 |
+ +-------+-------+-------+-------+
+ +-------+-------+-------+-------+
+ b: | 0 | 2 | 2 | 2 |
+ +-------+-------+-------+-------+
+ res:
+ +-------+-------+-------+-------+
+ | 0xff | 0x00 | 0xff | 0x00 |
+ +-------+-------+-------+-------+
+*/
lanes_eq :: intrinsics.simd_lanes_eq
+
+/*
+Check if lanes of vectors are not equal.
+
+This procedure checks each pair of lanes from vectors `a` and `b` for whether
+they are not equal, and if they are, the corresponding lane of the result
+vector will have a value with all bits set (`0xff..ff`). Otherwise the lane of
+the result vector will have the value `0`.
+
+Inputs:
+- `a`: An integer, a float or a boolean vector.
+- `b`: An integer, a float or a boolean vector.
+
+Returns:
+- A vector of unsigned integers of the same size as the input vector's lanes,
+containing the comparison results for each lane.
+
+**Operation**:
+
+ for i in 0 ..< len(res) {
+ if a[i] != b[i] {
+ res[i] = unsigned(-1)
+ } else {
+ res[i] = 0
+ }
+ }
+ return res
+
+Example:
+
+ +-------+-------+-------+-------+
+ a: | 0 | 1 | 2 | 3 |
+ +-------+-------+-------+-------+
+ +-------+-------+-------+-------+
+ b: | 0 | 2 | 2 | 2 |
+ +-------+-------+-------+-------+
+ res:
+ +-------+-------+-------+-------+
+ | 0x00 | 0xff | 0x00 | 0xff |
+ +-------+-------+-------+-------+
+*/
lanes_ne :: intrinsics.simd_lanes_ne
+
+/*
+Check if lanes of a vector are less than another.
+
+This procedure checks each pair of lanes from vectors `a` and `b` for whether
+the lane of `a` is less than the lane of `b`, and if so, the corresponding lane
+of the result vector will have a value with all bits set (`0xff..ff`). Otherwise
+the lane of the result vector will have the value `0`.
+
+Inputs:
+- `a`: An integer or a float vector.
+- `b`: An integer or a float vector.
+
+Returns:
+- A vector of unsigned integers of the same size as the input vector's lanes,
+containing the comparison results for each lane.
+
+**Operation**:
+
+ for i in 0 ..< len(res) {
+ if a[i] < b[i] {
+ res[i] = unsigned(-1)
+ } else {
+ res[i] = 0
+ }
+ }
+ return res
+
+Example:
+
+ +-------+-------+-------+-------+
+ a: | 0 | 1 | 2 | 3 |
+ +-------+-------+-------+-------+
+ +-------+-------+-------+-------+
+ b: | 0 | 2 | 2 | 2 |
+ +-------+-------+-------+-------+
+ res:
+ +-------+-------+-------+-------+
+ r: | 0x00 | 0xff | 0x00 | 0x00 |
+ +-------+-------+-------+-------+
+*/
lanes_lt :: intrinsics.simd_lanes_lt
+
+/*
+Check if lanes of a vector are less than or equal than another.
+SIMD vector.
+
+This procedure checks each pair of lanes from vectors `a` and `b` for whether the
+lane of `a` is less than or equal to the lane of `b`, and if so, the
+corresponding lane of the result vector will have a value with all bits set
+(`0xff..ff`). Otherwise the lane of the result vector will have the value `0`.
+
+Inputs:
+- `a`: An integer or a float vector.
+- `b`: An integer or a float vector.
+
+Returns:
+- A vector of unsigned integers of the same size as the input vector's lanes,
+containing the comparison results for each lane.
+
+**Operation**:
+
+ for i in 0 ..< len(res) {
+ if a[i] <= b[i] {
+ res[i] = unsigned(-1)
+ } else {
+ res[i] = 0
+ }
+ }
+ return res
+
+Example:
+
+ +-------+-------+-------+-------+
+ a: | 0 | 1 | 2 | 3 |
+ +-------+-------+-------+-------+
+ +-------+-------+-------+-------+
+ b: | 0 | 2 | 2 | 2 |
+ +-------+-------+-------+-------+
+ res:
+ +-------+-------+-------+-------+
+ | 0xff | 0xff | 0xff | 0x00 |
+ +-------+-------+-------+-------+
+*/
lanes_le :: intrinsics.simd_lanes_le
+
+/*
+Check if lanes of a vector are greater than another.
+vector.
+
+This procedure checks each pair of lanes from vectors `a` and `b` for whether the
+lane of `a` is greater than to the lane of `b`, and if so, the corresponding
+lane of the result vector will have a value with all bits set (`0xff..ff`).
+Otherwise the lane of the result vector will have the value `0`.
+
+Inputs:
+- `a`: An integer or a float vector.
+- `b`: An integer or a float vector.
+
+Returns:
+- A vector of unsigned integers of the same size as the input vector's lanes,
+containing the comparison results for each lane.
+
+**Operation**:
+
+ for i in 0 ..< len(res) {
+ if a[i] > b[i] {
+ res[i] = unsigned(-1)
+ } else {
+ res[i] = 0
+ }
+ }
+ return res
+
+Example:
+
+ +-------+-------+-------+-------+
+ a: | 0 | 1 | 2 | 3 |
+ +-------+-------+-------+-------+
+ +-------+-------+-------+-------+
+ b: | 0 | 2 | 2 | 2 |
+ +-------+-------+-------+-------+
+ res:
+ +-------+-------+-------+-------+
+ | 0x00 | 0x00 | 0x00 | 0xff |
+ +-------+-------+-------+-------+
+*/
lanes_gt :: intrinsics.simd_lanes_gt
+
+/*
+Check if lanes of a vector are greater than or equal than another.
+SIMD vector.
+
+This procedure checks each pair of lanes from vectors `a` and `b` for whether the
+lane of `a` is greater than or equal to the lane of `b`, and if so, the
+corresponding lane of the result vector will have a value with all bits set
+(`0xff..ff`). Otherwise the lane of the result vector will have the value `0`.
+
+Inputs:
+- `a`: An integer or a float vector.
+- `b`: An integer or a float vector.
+
+Returns:
+- A vector of unsigned integers of the same size as the input vector's lanes,
+containing the comparison results for each lane.
+
+**Operation**:
+
+ for i in 0 ..< len(res) {
+ if a[i] >= b[i] {
+ res[i] = unsigned(-1)
+ } else {
+ res[i] = 0
+ }
+ }
+ return res
+
+Example:
+
+ +-------+-------+-------+-------+
+ a: | 0 | 1 | 2 | 3 |
+ +-------+-------+-------+-------+
+ +-------+-------+-------+-------+
+ b: | 0 | 2 | 2 | 2 |
+ +-------+-------+-------+-------+
+ res:
+ +-------+-------+-------+-------+
+ | 0xff | 0x00 | 0xff | 0xff |
+ +-------+-------+-------+-------+
+*/
lanes_ge :: intrinsics.simd_lanes_ge
+/*
+Perform a gather load into a vector.
-// Gather and Scatter intrinsics
+A *gather* operation is memory load operation, that loads values from an vector
+of addresses into a single value vector. This can be used to achieve the
+following results:
+
+- Accessing every N'th element of an array (strided access)
+- Access of elements according to some computed offsets (indexed access).
+- Access of elements in a different order (shuffling access).
+
+When used alongside other SIMD procedures in order to compute the offsets
+for the `ptr` and `mask` parameters.
+
+Inputs:
+- `ptr`: A vector of memory locations. Each pointer points to a single value,
+ of a SIMD vector's lane type that will be loaded into the vector. Pointer
+ in this vector can be `nil` or any other invalid value, if the corresponding
+ value in the `mask` parameter is zero.
+- `val`: A vector of values that will be used at corresponding positions
+ of the result vector, if the corresponding memory location has been
+ masked out.
+- `mask`: A vector of booleans or unsigned integers that determines which memory
+ locations to read from. If the value at an index has the value true
+ (lowest bit set), the value at that index will be loaded into the result
+ vector from the corresponding memory location in the `ptr` vector. Otherwise
+ the value will be loaded from the `val` vector.
+
+Returns:
+- A vector with all values from unmasked indices
+loaded from the pointer vector `ptr`, and all values from masked indices loaded
+from the value vector `val`.
+
+**Operation**:
+
+ for i in 0 ..< len(res) {
+ if mask[i]&1 == 1 {
+ res[i] = ptr[i]^
+ } else {
+ res[i] = val[i]
+ }
+ }
+ return res
+
+Example:
+
+ // Example below loads 2 lanes of values from 2 lanes of float vectors, `v1` and
+ // `v2`. From each of these vectors we're loading the second value, into the first
+ // and the third position of the result vector.
+
+ // Therefore the `ptrs` argument is initialized such that the first and the third
+ // value are the addresses of the values that we want to load into the result
+ // vector, and we'll fill in `nil` for the rest of them. To prevent CPU from
+ // dereferencing those `nil` addresses we provide the mask that only allows us
+ // to load valid positions of the `ptrs` array, and the array of defaults which
+ // will have `127` in each position as the default value.
+
+ import "core:fmt"
+ import "core:simd"
+
+ simd_gather_example :: proc() {
+ v1 := [4] f32 {1, 2, 3, 4};
+ v2 := [4] f32 {9, 10,11,12};
+ ptrs := #simd [4]rawptr { &v1[1], nil, &v2[1], nil }
+ mask := #simd [4]bool { true, false, true, false }
+ defaults := #simd [4]f32 { 0x7f, 0x7f, 0x7f, 0x7f }
+ res := simd.gather(ptrs, defaults, mask)
+ fmt.println(res)
+ }
+
+Output:
+
+ <2, 127, 10, 127>
+
+The first and the third positions came from the `ptrs` array, and the other
+2 lanes of from the default vector. The graphic below shows how the values of
+the result are decided based on the mask:
+
+ +-------------------------------+
+ mask: | 1 | 0 | 1 | 0 |
+ +-------------------------------+
+ | | | `----------------------------.
+ | | | |
+ | `---- | ------------------------. |
+ v v v v
+ +-------------------------------+ +-------------------+
+ ptrs: | &m0 | nil | &m2 | nil | vals: | d0 | d1 | d2 | d3 |
+ +-------------------------------+ +-------------------+
+ | | | |
+ | .--- | -------------------------' |
+ | | | ,-------------------------'
+ v v v v
+ +-------------------------------+
+ result: | m0 | d1 | m2 | d3 |
+ +-------------------------------+
+*/
gather :: intrinsics.simd_gather
+
+/*
+Perform a scatter store from a vector.
+
+A *scatter* operation is a memory store operation that stores values from a
+vector into multiple memory locations. This operation is effectively the
+opposite of the *gather* operation.
+
+Inputs:
+- `ptr`: A vector of memory locations. Each masked location will be written
+ to with a value from the `val` vector. Pointers in this vector can be `nil`
+ or any other invalid value if the corresponding value in the `mask`
+ parameter is zero.
+- `val`: A vector of values to write to the memory locations.
+- `mask`: A vector of booleans or unsigned integers that decides which lanes
+ get written to memory. If the value of the mask is `true` (the lowest bit
+ set), the corresponding lane is written into memory. Otherwise it's not
+ written into memory.
+
+**Operation**:
+
+ for i in 0 ..< len(ptr) {
+ if mask[i]&1 == 1 {
+ ptr[i]^ = val[i]
+ }
+ }
+
+Example:
+
+ // Example below writes value `127` to the second element of two different
+ // vectors. The addresses of store destinations are written to the first and the
+ // third argument of the `ptr` vector, and the `mask` is set accordingly.
+
+ import "core:fmt"
+ import "core:simd"
+
+ simd_scatter_example :: proc() {
+ v1 := [4] f32 {1, 2, 3, 4};
+ v2 := [4] f32 {5, 6, 7, 8};
+ ptrs := #simd [4]rawptr { &v1[1], nil, &v2[1], nil }
+ mask := #simd [4]bool { true, false, true, false }
+ vals := #simd [4]f32 { 0x7f, 0x7f, 0x7f, 0x7f }
+ simd.scatter(ptrs, vals, mask)
+ fmt.println(v1)
+ fmt.println(v2)
+ }
+
+Output:
+
+ [1, 127, 3, 4]
+ [5, 127, 7, 8]
+
+The graphic below shows how the data gets written into memory.
+
+
+ +-------------------+
+ mask: | 1 | 0 | 1 | 0 |
+ +-------------------+
+ | | | |
+ v X v X
+ +-------------------+
+ vals: | d0 | d1 | d2 | d3 |
+ +-------------------+
+ | \
+ v v
+ +-----------------------+
+ ptrs: | &m0 | nil | &m2 | nil |
+ +-----------------------+
+*/
scatter :: intrinsics.simd_scatter
+
+/*
+Perform a masked load into the vector.
+
+This procedure performs a masked load from memory, into the vector. The `ptr`
+argument specifies the base address from which the values of the vector
+will be loaded. The mask selects the source for the result vector's lanes. If
+the mask for the corresponding lane has the value `true` (lowest bit set), the
+result lane is loaded from memory. Otherwise the result lane is loaded from the
+corresponding lane of the `val` vector.
+
+Inputs:
+- `ptr`: The address of the vector values to load. Masked-off values are not
+ accessed.
+- `val`: The vector of values that will be loaded into the masked slots of the
+ result vector.
+- `mask`: The mask that selects where to load the values from.
+
+Returns:
+- The loaded vector. The lanes for which the mask was set are loaded from
+memory, and the other lanes are loaded from the `val` vector.
+
+**Operation**:
+
+ for i in 0 ..< len(res) {
+ if mask[i]&1 == 1 {
+ res[i] = ptr[i]
+ } else {
+ res[i] = vals[i]
+ }
+ }
+ return res
+
+Example:
+
+ // The following code loads two values from the `src` vector, the first and the
+ // third value (selected by the mask). The masked-off values are given the value
+ // of 127 (`0x7f`).
+
+ import "core:fmt"
+ import "core:simd"
+
+ simd_masked_load_example :: proc() {
+ src := [4] f32 {1, 2, 3, 4};
+ mask := #simd [4]bool { true, false, true, false }
+ vals := #simd [4]f32 { 0x7f, 0x7f, 0x7f, 0x7f }
+ res := simd.masked_load(&src, vals, mask)
+ fmt.println(res)
+ }
+
+Output:
+
+ <1, 127, 3, 127>
+
+The graphic below demonstrates the flow of lanes.
+
+ +-------------------------------+
+ mask: | 1 | 0 | 1 | 0 |
+ +-------------------------------+
+ | | | `----------------------------.
+ | | | |
+ | `---- | ------------------------. |
+ ptr v v v v
+ +---->+-------------------------------+ +-------------------+
+ | v1 | v2 | v3 | v4 | vals: | d0 | d1 | d2 | d3 |
+ +-------------------------------+ +-------------------+
+ | | | |
+ | .--- | -------------------------' |
+ | | | ,-------------------------'
+ v v v v
+ +-------------------------------+
+ result: | v1 | d1 | v3 | d3 |
+ +-------------------------------+
+*/
masked_load :: intrinsics.simd_masked_load
+
+/*
+Perform a masked store to memory.
+
+This procedure performs a masked store from a vector `val`, into memory at
+address `ptr`, with the `mask` deciding which lanes are going to be stored,
+and which aren't. If the mask at a corresponding lane has the value `true`
+(lowest bit set), the lane is stored into memory. Otherwise the lane is not
+stored into memory.
+
+Inputs:
+- `ptr`: The base address of the store.
+- `val`: The vector to store.
+- `mask`: The mask, selecting which lanes of the vector to store into memory.
+
+**Operation**:
+
+ for i in 0 ..< len(val) {
+ if mask[i]&1 == 1 {
+ ptr[i] = val
+ }
+ }
+
+Example:
+
+ // Example below stores the value 127 into the first and the third slot of the
+ // vector `v`.
+
+ import "core:fmt"
+ import "core:simd"
+
+ simd_masked_store_example :: proc() {
+ v := [4] f32 {1, 2, 3, 4};
+ mask := #simd [4]bool { true, false, true, false }
+ vals := #simd [4]f32 { 0x7f, 0x7f, 0x7f, 0x7f }
+ simd.masked_store(&v, vals, mask)
+ fmt.println(v)
+ }
+
+Output:
+
+ [127, 2, 127, 4]
+
+The graphic below shows the flow of lanes:
+
+ +-------------------+
+ mask: | 1 | 0 | 1 | 0 |
+ +-------------------+
+ | | | |
+ v X v X
+ +-------------------+
+ vals: | v0 | v1 | v2 | v3 |
+ +-------------------+
+ | \
+ ptr v v
+ +--->+-----------------------+
+ | v0 | ... | v2 | ... |
+ +-----------------------+
+*/
masked_store :: intrinsics.simd_masked_store
+
+/*
+Load consecutive scalar values and expand into a vector.
+
+This procedure loads a number of consecutive scalar values from an address,
+specified by the `ptr` parameter, and stores them in a result vector, according
+to the mask. The number of values read from memory is the number of set bits
+in the mask. The lanes for which the mask has the value `true` get the next
+consecutive value from memory, otherwise if the mask is `false` for the
+lane, its value is filled from the corresponding lane of the `val` parameter.
+
+This procedure acts like `masked_store`, except the values from memory are
+read consecutively, and not according to the lanes. The memory values are read
+and assigned to the result vector's masked lanes in order of increasing
+addresses.
+
+Inputs:
+- `ptr`: The pointer to the memory to read from.
+- `vals`: The default values for masked-off entries.
+- `mask`: The mask that determines which lanes get consecutive memory values.
+
+Returns:
+- The result vector, holding masked memory values unmasked default values.
+
+**Operation**:
+
+ mem_idx := 0
+ for i in 0 ..< len(mask) {
+ if mask[i]&1 == 1 {
+ res[i] = ptr[mem_idx]
+ mem_idx += 1
+ } else {
+ res[i] = val[i]
+ }
+ }
+ return res
+
+Example:
+
+ // The example below loads two values from memory of the vector `v`. Two values in
+ // the mask are set to `true`, meaning only two memory items will be loaded into
+ // the result vector. The mask is set to `true` in the first and the third
+ // position, which specifies that the first memory item will be read into the
+ // first lane of the result vector, and the second memory item will be read into
+ // the third lane of the result vector. All the other lanes of the result vector
+ // will be initialized to the default value `127`.
+
+ import "core:fmt"
+ import "core:simd"
+
+ simd_masked_expand_load_example :: proc() {
+ v := [2] f64 {1, 2};
+ mask := #simd [4]bool { true, false, true, false }
+ vals := #simd [4]f64 { 0x7f, 0x7f, 0x7f, 0x7f }
+ res := simd.masked_expand_load(&v, vals, mask)
+ fmt.println(res)
+ }
+
+Output:
+
+ <1, 127, 2, 127>
+
+Graphical representation of the operation:
+
+
+ ptr --->+-----------+-----
+ | m0 | m1 | ...
+ +-----------+-----
+ | `--.
+ v v
+ +-------------------+ +-------------------+
+ mask: | 1 | 0 | 1 | 0 | vals: | v0 | v1 | v2 | v3 |
+ +-------------------+ +-------------------+
+ | | | |
+ | .-- | -----------------------' |
+ | | | ,----------------------------'
+ v v v v
+ +-------------------+
+ result: | m0 | v1 | m1 | v3 |
+ +-------------------+
+*/
masked_expand_load :: intrinsics.simd_masked_expand_load
+
+/*
+Store masked values to consecutive memory locations.
+
+This procedure stores values from masked lanes of a vector `val` consecutively
+into memory. This operation is the opposite of `masked_expand_load`. The number
+of items stored into memory is the number of set bits in the mask. If the value
+in a lane of a mask is `true`, that lane is stored into memory. Otherwise
+nothing is stored.
+
+Inputs:
+- `ptr`: The pointer to the memory of a store.
+- `val`: The vector to store into memory.
+- `mask`: The mask that selects which values to store into memory.
+
+**Operation**:
+
+ mem_idx := 0
+ for i in 0 ..< len(mask) {
+ if mask[i]&1 == 1 {
+ ptr[mem_idx] = val[i]
+ mem_idx += 1
+ }
+ }
+
+Example:
+
+ // The code below fills the vector `v` with two values from a 4-element SIMD
+ // vector, the first and the third value. The items in the mask are set to `true`
+ // in those lanes.
+
+ import "core:fmt"
+ import "core:simd"
+
+ simd_masked_compress_store_example :: proc() {
+ v := [2] f64 { };
+ mask := #simd [4]bool { true, false, true, false }
+ vals := #simd [4]f64 { 1, 2, 3, 4 }
+ simd.masked_compress_store(&v, vals, mask)
+ fmt.println(v)
+ }
+
+Output:
+
+ [1, 3]
+
+Graphical representation of the operation:
+
+ +-------------------+
+ mask: | 1 | 0 | 1 | 0 |
+ +-------------------+
+ | |
+ v v
+ +-------------------+
+ vals: | v0 | v1 | v2 | v3 |
+ +-------------------+
+ | ,--'
+ ptr v v
+ +--->+-----------------
+ | v0 | v2 | ...
+ +-----------------
+*/
masked_compress_store :: intrinsics.simd_masked_compress_store
-// extract :: proc(a: #simd[N]T, idx: uint) -> T
+/*
+Extract scalar from a vector's lane.
+
+This procedure returns the scalar from the lane at the specified index of the
+vector.
+
+Inputs:
+- `a`: The vector to extract from.
+- `idx`: The lane index.
+
+Returns:
+- The value of the lane at the specified index.
+
+**Operation**:
+
+ return a[idx]
+*/
extract :: intrinsics.simd_extract
-// replace :: proc(a: #simd[N]T, idx: uint, elem: T) -> #simd[N]T
+
+/*
+Replace the value in a vector's lane.
+
+This procedure places a scalar value at the lane corresponding to the given index of
+the vector.
+
+Inputs:
+- `a`: The vector to replace a lane in.
+- `idx`: The lane index.
+- `elem`: The scalar to place.
+
+Returns:
+- Vector with the specified lane replaced.
+
+**Operation**:
+
+ a[idx] = elem
+*/
replace :: intrinsics.simd_replace
+/*
+Reduce a vector to a scalar by adding up all the lanes.
+
+This procedure returns a scalar that is the ordered sum of all lanes. The
+ordered sum may be important for accounting for precision errors in
+floating-point computation, as floating-point addition is not associative,
+that is `(a+b)+c` may not be equal to `a+(b+c)`.
+
+Inputs:
+- `a`: The vector to reduce.
+
+Result:
+- Sum of all lanes, as a scalar.
+
+**Operation**:
+
+ res := 0
+ for i in 0 ..< len(a) {
+ res += a[i]
+ }
+*/
reduce_add_ordered :: intrinsics.simd_reduce_add_ordered
+
+/*
+Reduce a vector to a scalar by multiplying all the lanes.
+
+This procedure returns a scalar that is the ordered product of all lanes.
+The ordered product may be important for accounting for precision errors in
+floating-point computation, as floating-point multiplication is not associative,
+that is `(a*b)*c` may not be equal to `a*(b*c)`.
+
+Inputs:
+- `a`: The vector to reduce.
+
+Result:
+- Product of all lanes, as a scalar.
+
+**Operation**:
+
+ res := 1
+ for i in 0 ..< len(a) {
+ res *= a[i]
+ }
+*/
reduce_mul_ordered :: intrinsics.simd_reduce_mul_ordered
-reduce_min :: intrinsics.simd_reduce_min
-reduce_max :: intrinsics.simd_reduce_max
-reduce_and :: intrinsics.simd_reduce_and
-reduce_or :: intrinsics.simd_reduce_or
-reduce_xor :: intrinsics.simd_reduce_xor
-reduce_any :: intrinsics.simd_reduce_any
-reduce_all :: intrinsics.simd_reduce_all
+/*
+Reduce a vector to a scalar by finding the minimum value between all of the lanes.
-// swizzle :: proc(a: #simd[N]T, indices: ..int) -> #simd[len(indices)]T
+This procedure returns a scalar that is the minimum value of all the lanes
+in a vector.
+
+Inputs:
+- `a`: The vector to reduce.
+
+Result:
+- Minimum value of all lanes, as a scalar.
+
+**Operation**:
+
+ res := 0
+ for i in 0 ..< len(a) {
+ res = min(res, a[i])
+ }
+*/
+reduce_min :: intrinsics.simd_reduce_min
+
+/*
+Reduce a vector to a scalar by finding the maximum value between all of the lanes.
+
+This procedure returns a scalar that is the maximum value of all the lanes
+in a vector.
+
+Inputs:
+- `a`: The vector to reduce.
+
+Result:
+- Maximum value of all lanes, as a scalar.
+
+**Operation**:
+
+ res := 0
+ for i in 0 ..< len(a) {
+ res = max(res, a[i])
+ }
+*/
+reduce_max :: intrinsics.simd_reduce_max
+
+/*
+Reduce a vector to a scalar by performing bitwise AND of all of the lanes.
+
+This procedure returns a scalar that is the result of the bitwise AND operation
+between all of the lanes in a vector.
+
+Inputs:
+- `a`: The vector to reduce.
+
+Result:
+- Bitwise AND of all lanes, as a scalar.
+
+**Operation**:
+
+ res := 0
+ for i in 0 ..< len(a) {
+ res &= a[i]
+ }
+*/
+reduce_and :: intrinsics.simd_reduce_and
+
+/*
+Reduce a vector to a scalar by performing bitwise OR of all of the lanes.
+
+This procedure returns a scalar that is the result of the bitwise OR operation
+between all of the lanes in a vector.
+
+Inputs:
+- `a`: The vector to reduce.
+
+Result:
+- Bitwise OR of all lanes, as a scalar.
+
+**Operation**:
+
+ res := 0
+ for i in 0 ..< len(a) {
+ res |= a[i]
+ }
+*/
+reduce_or :: intrinsics.simd_reduce_or
+
+/*
+Reduce SIMD vector to a scalar by performing bitwise XOR of all of the lanes.
+
+This procedure returns a scalar that is the result of the bitwise XOR operation
+between all of the lanes in a vector.
+
+Inputs:
+- `a`: The vector to reduce.
+
+Result:
+- Bitwise XOR of all lanes, as a scalar.
+
+**Operation**:
+
+ res := 0
+ for i in 0 ..< len(a) {
+ res ~= a[i]
+ }
+*/
+reduce_xor :: intrinsics.simd_reduce_xor
+
+/*
+Reduce SIMD vector to a scalar by performing bitwise OR of all of the lanes.
+
+This procedure returns a scalar that is the result of the bitwise OR operation
+between all of the lanes in a vector.
+
+Inputs:
+- `a`: The vector to reduce.
+
+Result:
+- Bitwise OR of all lanes, as a scalar.
+
+**Operation**:
+
+ res := 0
+ for i in 0 ..< len(a) {
+ res |= a[i]
+ }
+*/
+reduce_any :: intrinsics.simd_reduce_any
+
+/*
+Reduce SIMD vector to a scalar by performing bitwise AND of all of the lanes.
+
+This procedure returns a scalar that is the result of the bitwise AND operation
+between all of the lanes in a vector.
+
+Inputs:
+- `a`: The vector to reduce.
+
+Result:
+- Bitwise AND of all lanes, as a scalar.
+
+**Operation**:
+
+ res := 0
+ for i in 0 ..< len(a) {
+ res &= a[i]
+ }
+*/
+reduce_all :: intrinsics.simd_reduce_all
+
+/*
+Reorder the lanes of a SIMD vector.
+
+This procedure reorders the lanes of a vector, according to the provided
+indices. The number of indices correspond to the number of lanes in the
+result vector and must be the same as the number of lanes of the input vector.
+Each index specifies, the lane of the scalar from the input vector, which
+will be written at the corresponding position of the result vector.
+
+Inputs:
+- `x`: The input vector.
+- `indices`: The indices of lanes to write to the result vector.
+
+Result:
+- Swizzled input vector.
+
+**Operation**:
+
+ res = {}
+ for i in 0 ..< len(indices) {
+ res[i] = x[indices[i]]
+ }
+ return res
+
+Example:
+
+ // The example below shows how the indices are used to determine which lanes of the
+ // input vector get written into the result vector.
+
+ import "core:fmt"
+ import "core:simd"
+
+ swizzle_example :: proc() {
+ x := #simd [4]f32 { 1.5, 2.5, 3.5, 4.5 }
+ res := simd.swizzle(x, 0, 3, 1, 1)
+ fmt.println(res)
+ }
+
+Output:
+
+ <1.5, 4.5, 2.5, 2.5>
+
+The graphical representation of the operation is as follows. The `idx` vector in
+the picture represents the `indices` parameter:
+
+ 0 1 2 3
+ +-----+-----+-----+-----+
+ x: | 1.5 | 2.5 | 3.5 | 4.5 |
+ +-----+-----+-----+-----+
+ ^ ^ ^
+ | | |
+ | '----. |
+ | .---- | ---'
+ | | |
+ | | +------.
+ +-----+-----+-----+-----+
+ idx: | 0 | 3 | 1 | 1 |
+ +-----+-----+-----+-----+
+ ^ ^ ^ ^
+ | | | |
+ +-----+-----+-----+-----+
+ res: | 1.5 | 3.5 | 2.5 | 2.5 |
+ +-----+-----+-----+-----+
+*/
swizzle :: builtin.swizzle
-// shuffle :: proc(a, b: #simd[N]T, indices: #simd[max 2*N]u32) -> #simd[len(indices)]T
+/*
+Extract the set of most-significant bits of a SIMD vector.
+
+This procedure checks the the most-significant bit (MSB) for each lane of vector
+and returns the numbers of lanes with the most-significant bit set. This procedure
+can be used in conjuction with `lanes_eq` (and other similar procedures) to
+count the number of matched lanes by computing the cardinality of the resulting
+set.
+
+Inputs:
+- `a`: An input vector.
+
+Result:
+- A bitset of integers, corresponding to the indexes of the lanes, whose MSBs
+ are set.
+
+**Operation**:
+
+ bits_per_lane = 8*size_of(a[0])
+ res = bit_set {}
+ for i in 0 ..< len(a) {
+ if a[i] & 1<<(bits_per_lane-1) != 0 {
+ res |= i
+ }
+ }
+ return res
+
+Example:
+
+ // Since lanes 0, 1, 4, 7 contain negative numbers, the most significant
+ // bits for them will be set.
+
+ import "core:fmt"
+ import "core:simd"
+
+ simd_extract_msbs_example :: proc() {
+ v := #simd [8]i32 { -1, -2, +3, +4, -5, +6, +7, -8 }
+ fmt.println(simd.extract_msbs(v))
+ }
+
+Output:
+
+ bit_set[0..=7]{0, 1, 4, 7}
+*/
+extract_msbs :: intrinsics.simd_extract_msbs
+
+/*
+Extract the set of least-significant bits of a SIMD vector.
+
+This procedure checks the the least-significant bit (LSB) for each lane of vector
+and returns the numbers of lanes with the least-significant bit set. This procedure
+can be used in conjuction with `lanes_eq` (and other similar procedures) to
+count the number of matched lanes by computing the cardinality of the resulting
+set.
+
+Inputs:
+- `a`: An input vector.
+
+Result:
+- A bitset of integers, corresponding to the indexes of the lanes, whose LSBs
+ are set.
+
+**Operation**:
+
+ res = bit_set {}
+ for i in 0 ..< len(a) {
+ if a[i] & 1 != 0 {
+ res |= i
+ }
+ }
+ return res
+
+Example:
+
+ // Since lanes 0, 2, 4, 6 contain odd integers, the least significant bits
+ // for these lanes are set.
+
+ import "core:fmt"
+ import "core:simd"
+
+ simd_extract_lsbs_example :: proc() {
+ v := #simd [8]i32 { -1, -2, +3, +4, -5, +6, +7, -8 }
+ fmt.println(simd.extract_lsbs(v))
+ }
+
+Output:
+
+ bit_set[0..=7]{0, 2, 4, 6}
+*/
+extract_lsbs :: intrinsics.simd_extract_lsbs
+
+/*
+Reorder the lanes of two SIMD vectors.
+
+This procedure returns a vector, containing the scalars from the lanes of two
+vectors, according to the provided indices vector. Each index in the indices
+vector specifies, the lane of the scalar from one of the two input vectors,
+which will be written at the corresponding position of the result vector. If
+the index is within bounds 0 ..< len(A), it corresponds to the indices of the
+first input vector. Otherwise the index corresponds to the indices of the second
+input vector.
+
+Inputs:
+- `a`: The first input vector.
+- `b`: The second input vector.
+- `indices`: The indices.
+
+Result:
+- Input vectors, shuffled according to the indices.
+
+**Operation**:
+
+ res = {}
+ for i in 0 ..< len(indices) {
+ idx = indices[i];
+ if idx < len(a) {
+ res[i] = a[idx]
+ } else {
+ res[i] = b[idx]
+ }
+ }
+ return res
+
+Example:
+
+ // The example below shows how the indices are used to determine lanes of the
+ // input vector that are shuffled into the result vector.
+
+ import "core:fmt"
+ import "core:simd"
+
+ simd_shuffle_example :: proc() {
+ a := #simd [4]f32 { 1, 2, 3, 4 }
+ b := #simd [4]f32 { 5, 6, 7, 8 }
+ res := simd.shuffle(a, b, 0, 4, 2, 5)
+ fmt.println(res)
+ }
+
+Output:
+
+ <1, 5, 3, 6>
+
+The graphical representation of the operation is as follows. The `idx` vector in
+the picture represents the `indices` parameter:
+
+ 0 1 2 3 4 5 6 7
+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
+ a: | 1 | 2 | 3 | 4 | b: | 5 | 6 | 7 | 8 |
+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
+ ^ ^ ^ ^
+ | | | |
+ | | | |
+ | .--- | ----------------' |
+ | | | .-----------------'
+ +-----+-----+-----+-----+
+ idx: | 0 | 4 | 2 | 5 |
+ +-----+-----+-----+-----+
+ ^ ^ ^ ^
+ | | | |
+ +-----+-----+-----+-----+
+ res: | 1 | 5 | 3 | 6 |
+ +-----+-----+-----+-----+
+*/
shuffle :: intrinsics.simd_shuffle
-// select :: proc(cond: #simd[N]boolean_or_integer, true, false: #simd[N]T) -> #simd[N]T
+/*
+Select values from one of the two vectors.
+
+This procedure returns a vector, which has, on each lane a value from one of the
+corresponding lanes in one of the two input vectors based on the `cond`
+parameter. On each lane, if the value of the `cond` parameter is `true` (or
+non-zero), the result lane will have a value from the `true` input vector,
+otherwise the result lane will have a value from the `false` input vector.
+
+Inputs:
+- `cond`: The condition vector.
+- `true`: The first input vector.
+- `false`: The second input vector.
+
+Result:
+- The result of selecting values from the two input vectors.
+
+**Operation**:
+
+ res = {}
+ for i in 0 ..< len(cond) {
+ if cond[i] {
+ res[i] = true[i]
+ } else {
+ res[i] = false[i]
+ }
+ }
+ return res
+
+Example:
+
+ // The following example selects values from the two input vectors, `a` and `b`
+ // into a single vector.
+
+ import "core:fmt"
+ import "core:simd"
+
+ simd_select_example :: proc() {
+ a := #simd [4] f64 { 1,2,3,4 }
+ b := #simd [4] f64 { 5,6,7,8 }
+ cond := #simd[4] int { 1, 0, 1, 0 }
+ fmt.println(simd.select(cond,a,b))
+ }
+
+Output:
+
+ <1, 6, 3, 8>
+
+Graphically, the operation looks as follows. The `t` and `f` represent the
+`true` and `false` vectors respectively:
+
+ 0 1 2 3 0 1 2 3
+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
+ t: | 1 | 2 | 3 | 4 | f: | 5 | 6 | 7 | 8 |
+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
+ ^ ^ ^ ^
+ | | | |
+ | | | |
+ | .--- | ----------------------' |
+ | | | .-----------------------------'
+ +-----+-----+-----+-----+
+ cond: | 1 | 0 | 1 | 0 |
+ +-----+-----+-----+-----+
+ ^ ^ ^ ^
+ | | | |
+ +-----+-----+-----+-----+
+ res: | 1 | 5 | 3 | 6 |
+ +-----+-----+-----+-----+
+*/
select :: intrinsics.simd_select
-
+/*
+Compute the square root of each lane in a SIMD vector.
+*/
sqrt :: intrinsics.sqrt
+
+/*
+Ceil each lane in a SIMD vector.
+*/
ceil :: intrinsics.simd_ceil
+
+/*
+Floor each lane in a SIMD vector.
+*/
floor :: intrinsics.simd_floor
+
+/*
+Truncate each lane in a SIMD vector.
+*/
trunc :: intrinsics.simd_trunc
+
+/*
+Compute the nearest integer of each lane in a SIMD vector.
+*/
nearest :: intrinsics.simd_nearest
+/*
+Transmute a SIMD vector into an integer vector.
+*/
to_bits :: intrinsics.simd_to_bits
+/*
+Reverse the lanes of a SIMD vector.
+
+This procedure reverses the lanes of a vector, putting last lane in the
+first spot, etc. This procedure is equivalent to the following call (for
+4-element vectors):
+
+ swizzle(a, 3, 2, 1, 0)
+*/
lanes_reverse :: intrinsics.simd_lanes_reverse
-lanes_rotate_left :: intrinsics.simd_lanes_rotate_left
+/*
+Rotate the lanes of a SIMD vector left.
+
+This procedure rotates the lanes of a vector, putting the first lane of the
+last spot, second lane in the first spot, third lane in the second spot, etc.
+For 4-element vectors, this procedure is equvalent to the following:
+
+ swizzle(a, 1, 2, 3, 0)
+*/
+lanes_rotate_left :: intrinsics.simd_lanes_rotate_left
+
+/*
+Rotate the lanes of a SIMD vector right.
+
+This procedure rotates the lanes of a SIMD vector, putting the first lane of the
+second spot, second lane in the third spot, etc. For 4-element vectors, this
+procedure is equvalent to the following:
+
+ swizzle(a, 3, 0, 1, 2)
+*/
lanes_rotate_right :: intrinsics.simd_lanes_rotate_right
-count_ones :: intrinsics.count_ones
-count_zeros :: intrinsics.count_zeros
+/*
+Count the number of set bits in each lane of a SIMD vector.
+*/
+count_ones :: intrinsics.count_ones
+
+/*
+Count the number of unset bits in each lane of a SIMD vector.
+*/
+count_zeros :: intrinsics.count_zeros
+
+/*
+Count the number of trailing unset bits in each lane of a SIMD vector.
+*/
count_trailing_zeros :: intrinsics.count_trailing_zeros
-count_leading_zeros :: intrinsics.count_leading_zeros
-reverse_bits :: intrinsics.reverse_bits
+/*
+Count the number of leading unset bits in each lane of a SIMD vector.
+*/
+count_leading_zeros :: intrinsics.count_leading_zeros
+
+/*
+Reverse the bit pattern of a SIMD vector.
+*/
+reverse_bits :: intrinsics.reverse_bits
+
+/*
+Perform a FMA (Fused multiply-add) operation on each lane of SIMD vectors.
+
+A fused multiply-add is a ternary operation that for three operands, `a`, `b`
+and `c` performs the operation `a*b+c`. This operation is a hardware feature
+that allows to minimize floating-point error and allow for faster computation.
+
+This procedure performs a FMA operation on each lane of the SIMD vectors.
+
+Inputs:
+- `a`: The multiplier
+- `b`: The multiplicand
+- `c`: The addend
+
+Returns:
+- `a*b+c`
+
+**Operation**
+
+ res := 0
+ for i in 0 ..< len(a) {
+ res[i] = fma(a[i], b[i], c[i])
+ }
+ return res
+*/
fused_mul_add :: intrinsics.fused_mul_add
-fma :: intrinsics.fused_mul_add
+/*
+Perform a FMA (Fused multiply-add) operation on each lane of SIMD vectors.
+
+A fused multiply-add is a ternary operation that for three operands, `a`, `b`
+and `c` performs the operation `a*b+c`. This operation is a hardware feature
+that allows to minimize floating-point error and allow for faster computation.
+
+This procedure performs a FMA operation on each lane of the SIMD vectors.
+
+Inputs:
+- `a`: The multiplier.
+- `b`: The multiplicand.
+- `c`: The addend.
+
+Returns:
+- `a*b+c`
+
+**Operation**
+
+ res := 0
+ for i in 0 ..< len(a) {
+ res[i] = fma(a[i], b[i], c[i])
+ }
+ return res
+*/
+fma :: intrinsics.fused_mul_add
+
+/*
+Convert pointer to SIMD vector to an array pointer.
+*/
to_array_ptr :: #force_inline proc "contextless" (v: ^#simd[$LANES]$E) -> ^[LANES]E {
return (^[LANES]E)(v)
}
+
+/*
+Convert SIMD vector to an array.
+*/
to_array :: #force_inline proc "contextless" (v: #simd[$LANES]$E) -> [LANES]E {
return transmute([LANES]E)(v)
}
+
+/*
+Convert array to SIMD vector.
+*/
from_array :: #force_inline proc "contextless" (v: $A/[$LANES]$E) -> #simd[LANES]E {
return transmute(#simd[LANES]E)v
}
+/*
+Convert slice to SIMD vector.
+*/
from_slice :: proc($T: typeid/#simd[$LANES]$E, slice: []E) -> T {
assert(len(slice) >= LANES, "slice length must be a least the number of lanes")
array: [LANES]E
@@ -186,10 +2428,36 @@ from_slice :: proc($T: typeid/#simd[$LANES]$E, slice: []E) -> T {
return transmute(T)array
}
+/*
+Perform binary not operation on a SIMD vector.
+
+This procedure returns a vector where each lane is the result of the binary
+NOT operation of the corresponding lane in the vector `a`.
+
+**Operation**:
+
+ for i in 0 ..< len(res) {
+ res[i] = ~a[i]
+ }
+ return res
+
+Example:
+
+ +------+------+------+------+
+ a: | 0x00 | 0x50 | 0x80 | 0xff |
+ +------+------+------+------+
+ res:
+ +------+------+------+------+
+ | 0xff | 0xaf | 0x7f | 0x00 |
+ +------+------+------+------+
+*/
bit_not :: #force_inline proc "contextless" (v: $T/#simd[$LANES]$E) -> T where intrinsics.type_is_integer(E) {
return xor(v, T(~E(0)))
}
+/*
+Copy the signs from lanes of one SIMD vector into another SIMD vector.
+*/
copysign :: #force_inline proc "contextless" (v, sign: $T/#simd[$LANES]$E) -> T where intrinsics.type_is_float(E) {
neg_zero := to_bits(T(-0.0))
sign_bit := to_bits(sign) & neg_zero
@@ -197,11 +2465,48 @@ copysign :: #force_inline proc "contextless" (v, sign: $T/#simd[$LANES]$E) -> T
return transmute(T)(sign_bit|magnitude)
}
+/*
+Return signs of SIMD lanes.
+
+This procedure returns a vector, each lane of which contains either +1.0 or
+-1.0 depending on the sign of the value in the corresponding lane of the
+input vector. If the lane of the input vector has NaN, then the result vector
+will contain this NaN value as-is.
+*/
signum :: #force_inline proc "contextless" (v: $T/#simd[$LANES]$E) -> T where intrinsics.type_is_float(E) {
is_nan := lanes_ne(v, v)
return select(is_nan, v, copysign(T(1), v))
}
+/*
+Calculate reciprocals of SIMD lanes.
+
+This procedure returns a vector where each lane is the reciprocal of the
+corresponding lane in the vector `a`.
+
+Inputs:
+- `a`: An integer or a float vector to negate.
+
+Returns:
+- Negated vector.
+
+**Operation**:
+
+ for i in 0 ..< len(res) {
+ res[i] = 1.0 / a[i]
+ }
+ return res
+
+Example:
+
+ +------+------+------+------+
+ a: | 2 | 1 | 3 | 5 |
+ +------+------+------+------+
+ res:
+ +------+------+------+------+
+ | 0.5 | 1 | 0.33 | 0.2 |
+ +------+------+------+------+
+*/
recip :: #force_inline proc "contextless" (v: $T/#simd[$LANES]$E) -> T where intrinsics.type_is_float(E) {
return T(1) / v
}
diff --git a/core/simd/x86/ssse3.odin b/core/simd/x86/ssse3.odin
index 07c846e7b..03ba5dcfb 100644
--- a/core/simd/x86/ssse3.odin
+++ b/core/simd/x86/ssse3.odin
@@ -21,7 +21,7 @@ _mm_abs_epi32 :: #force_inline proc "c" (a: __m128i) -> __m128i {
_mm_shuffle_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
return transmute(__m128i)pshufb128(transmute(u8x16)a, transmute(u8x16)b)
}
-@(require_results, enable_target_feature="ssse3")
+@(require_results, enable_target_feature="sse2,ssse3")
_mm_alignr_epi8 :: #force_inline proc "c" (a, b: __m128i, $IMM8: u32) -> __m128i {
shift :: IMM8
diff --git a/core/slice/slice.odin b/core/slice/slice.odin
index c31edf281..c328fd267 100644
--- a/core/slice/slice.odin
+++ b/core/slice/slice.odin
@@ -48,22 +48,41 @@ to_type :: proc(buf: []u8, $T: typeid) -> (T, bool) #optional_ok {
}
/*
- Turn a slice of one type, into a slice of another type.
+Turn a slice of one type, into a slice of another type.
- Only converts the type and length of the slice itself.
- The length is rounded down to the nearest whole number of items.
+Only converts the type and length of the slice itself.
+The length is rounded down to the nearest whole number of items.
+
+Example:
+
+ import "core:fmt"
+ import "core:slice"
+
+ i64s_as_i32s :: proc() {
+ large_items := []i64{1, 2, 3, 4}
+ small_items := slice.reinterpret([]i32, large_items)
+ assert(len(small_items) == 8)
+ fmt.println(large_items, "->", small_items)
+ }
+
+ bytes_as_i64s :: proc() {
+ small_items := [12]byte{}
+ small_items[0] = 1
+ small_items[8] = 2
+ large_items := slice.reinterpret([]i64, small_items[:])
+ assert(len(large_items) == 1) // only enough bytes to make 1 x i64; two would need at least 8 bytes.
+ fmt.println(small_items, "->", large_items)
+ }
+
+ reinterpret_example :: proc() {
+ i64s_as_i32s()
+ bytes_as_i64s()
+ }
+
+Output:
+ [1, 2, 3, 4] -> [1, 0, 2, 0, 3, 0, 4, 0]
+ [1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0] -> [1]
- ```
- large_items := []i64{1, 2, 3, 4}
- small_items := slice.reinterpret([]i32, large_items)
- assert(len(small_items) == 8)
- ```
- ```
- small_items := []byte{1, 0, 0, 0, 0, 0, 0, 0,
- 2, 0, 0, 0}
- large_items := slice.reinterpret([]i64, small_items)
- assert(len(large_items) == 1) // only enough bytes to make 1 x i64; two would need at least 8 bytes.
- ```
*/
@(require_results)
reinterpret :: proc "contextless" ($T: typeid/[]$U, s: []$V) -> []U {
@@ -879,8 +898,7 @@ bitset_to_enum_slice_with_buffer :: proc(buf: []$E, bs: $T) -> (slice: []E) wher
// sl := slice.bitset_to_enum_slice(bs)
@(require_results)
bitset_to_enum_slice_with_make :: proc(bs: $T, $E: typeid, allocator := context.allocator) -> (slice: []E) where intrinsics.type_is_enum(E), intrinsics.type_bit_set_elem_type(T) == E {
- ones := intrinsics.count_ones(transmute(E)bs)
- buf := make([]E, int(ones), allocator)
+ buf := make([]E, card(bs), allocator)
return bitset_to_enum_slice(buf, bs)
}
diff --git a/core/strconv/decimal/decimal.odin b/core/strconv/decimal/decimal.odin
index 06503d01a..cb9285083 100644
--- a/core/strconv/decimal/decimal.odin
+++ b/core/strconv/decimal/decimal.odin
@@ -399,7 +399,7 @@ shift_left :: proc(a: ^Decimal, k: uint) #no_bounds_check {
a.decimal_point += delta
- a.count = clamp(a.count, 0, len(a.digits))
+ a.count = clamp(a.count+delta, 0, len(a.digits))
trim(a)
}
/*
@@ -562,4 +562,3 @@ rounded_integer :: proc(a: ^Decimal) -> u64 {
}
return n
}
-
diff --git a/core/strconv/generic_float.odin b/core/strconv/generic_float.odin
index b049f0fe1..b126dc3b6 100644
--- a/core/strconv/generic_float.odin
+++ b/core/strconv/generic_float.odin
@@ -339,45 +339,37 @@ Converts a decimal number to its floating-point representation with the given fo
- b: The bits representing the floating-point number
- overflow: A boolean indicating whether an overflow occurred during conversion
*/
-@(private)
decimal_to_float_bits :: proc(d: ^decimal.Decimal, info: ^Float_Info) -> (b: u64, overflow: bool) {
- end :: proc "contextless" (d: ^decimal.Decimal, mant: u64, exp: int, info: ^Float_Info) -> (bits: u64) {
+ overflow_end :: proc "contextless" (d: ^decimal.Decimal, info: ^Float_Info) -> (u64, bool) {
+ mant: u64 = 0
+ exp: int = 1< (bits: u64, overflow: bool) {
bits = mant & (u64(1)< bool {
- mant^ = 0
- exp^ = 1< 310 {
- set_overflow(&mant, &exp, info)
- b = end(d, mant, exp, info)
- return
+ return overflow_end(d, info)
} else if d.decimal_point < -330 {
- mant = 0
- exp = info.bias
- b = end(d, mant, exp, info)
- return
+ return end(d, 0, info.bias, info, false)
}
@(static, rodata) power_table := [?]int{1, 3, 6, 9, 13, 16, 19, 23, 26}
- exp = 0
+
+ exp := 0
for d.decimal_point > 0 {
n := 27 if d.decimal_point >= len(power_table) else power_table[d.decimal_point]
decimal.shift(d, -n)
@@ -392,35 +384,34 @@ decimal_to_float_bits :: proc(d: ^decimal.Decimal, info: ^Float_Info) -> (b: u64
// go from [0.5, 1) to [1, 2)
exp -= 1
+ // Min rep exp is 1+bias
if exp < info.bias + 1 {
n := info.bias + 1 - exp
- decimal.shift(d, n)
+ decimal.shift(d, -n)
exp += n
}
if (exp-info.bias) >= (1<>= 1
exp += 1
if (exp-info.bias) >= (1< (res: string) {
/*
Appends a trailing null byte after the end of the current Builder byte buffer and then casts it to a cstring
+NOTE: This procedure will not check if the backing buffer has enough space to include the extra null byte.
+
Inputs:
- b: A pointer to builder
Returns:
- res: A cstring of the Builder's buffer
*/
-to_cstring :: proc(b: ^Builder) -> (res: cstring) {
+unsafe_to_cstring :: proc(b: ^Builder) -> (res: cstring) {
append(&b.buf, 0)
pop(&b.buf)
return cstring(raw_data(b.buf))
}
/*
+Appends a trailing null byte after the end of the current Builder byte buffer and then casts it to a cstring
+
+Inputs:
+- b: A pointer to builder
+
+Returns:
+- res: A cstring of the Builder's buffer upon success
+- err: An optional allocator error if one occured, `nil` otherwise
+*/
+to_cstring :: proc(b: ^Builder) -> (res: cstring, err: mem.Allocator_Error) {
+ n := append(&b.buf, 0) or_return
+ if n != 1 {
+ return nil, .Out_Of_Memory
+ }
+ pop(&b.buf)
+ #no_bounds_check {
+ assert(b.buf[len(b.buf)] == 0)
+ }
+ return cstring(raw_data(b.buf)), nil
+}
+/*
Returns the length of the Builder's buffer, in bytes
Inputs:
diff --git a/core/strings/intern.odin b/core/strings/intern.odin
index 4c270980c..0b8ed173e 100644
--- a/core/strings/intern.odin
+++ b/core/strings/intern.odin
@@ -89,6 +89,7 @@ intern_get_cstring :: proc(m: ^Intern, text: string) -> (str: cstring, err: runt
entry := _intern_get_entry(m, text) or_return
return cstring(&entry.str[0]), nil
}
+
/*
Internal function to lookup whether the text string exists in the map, returns the entry
Sets and allocates the entry if it wasn't set yet
@@ -104,13 +105,15 @@ Returns:
- err: An allocator error if one occured, `nil` otherwise
*/
_intern_get_entry :: proc(m: ^Intern, text: string) -> (new_entry: ^Intern_Entry, err: runtime.Allocator_Error) #no_bounds_check {
- if prev, ok := m.entries[text]; ok {
- return prev, nil
- }
if m.allocator.procedure == nil {
m.allocator = context.allocator
}
+ key_ptr, val_ptr, inserted := map_entry(&m.entries, text) or_return
+ if !inserted {
+ return val_ptr^, nil
+ }
+
entry_size := int(offset_of(Intern_Entry, str)) + len(text) + 1
bytes := runtime.mem_alloc(entry_size, align_of(Intern_Entry), m.allocator) or_return
new_entry = (^Intern_Entry)(raw_data(bytes))
@@ -120,6 +123,9 @@ _intern_get_entry :: proc(m: ^Intern, text: string) -> (new_entry: ^Intern_Entry
new_entry.str[new_entry.len] = 0
key := string(new_entry.str[:new_entry.len])
- m.entries[key] = new_entry
- return new_entry, nil
+
+ key_ptr^ = key
+ val_ptr^ = new_entry
+
+ return
}
diff --git a/core/strings/strings.odin b/core/strings/strings.odin
index af93ff33c..e15754dff 100644
--- a/core/strings/strings.odin
+++ b/core/strings/strings.odin
@@ -25,7 +25,7 @@ Returns:
clone :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
c := make([]byte, len(s), allocator, loc) or_return
copy(c, s)
- return string(c[:len(s)]), nil
+ return string(c), nil
}
/*
Clones a string safely (returns early with an allocation error on failure)
@@ -787,7 +787,7 @@ Example:
import "core:fmt"
import "core:strings"
- cut_example :: proc() {
+ cut_clone_example :: proc() {
fmt.println(strings.cut_clone("some example text", 0, 4)) // -> "some"
fmt.println(strings.cut_clone("some example text", 2, 2)) // -> "me"
fmt.println(strings.cut_clone("some example text", 5, 7)) // -> "example"
@@ -1031,14 +1031,17 @@ Returns:
*/
@private
_split_iterator :: proc(s: ^string, sep: string, sep_save: int) -> (res: string, ok: bool) {
+ m: int
if sep == "" {
- res = s[:]
- ok = true
- s^ = s[len(s):]
- return
+ if len(s) == 0 {
+ m = -1
+ } else {
+ _, w := utf8.decode_rune_in_string(s^)
+ m = w
+ }
+ } else {
+ m = index(s^, sep)
}
-
- m := index(s^, sep)
if m < 0 {
// not found
res = s[:]
@@ -1872,7 +1875,8 @@ index_multi :: proc(s: string, substrs: []string) -> (idx: int, width: int) {
lowest_index := len(s)
found := false
for substr in substrs {
- if i := index(s, substr); i >= 0 {
+ haystack := s[:min(len(s), lowest_index + len(substr))]
+ if i := index(haystack, substr); i >= 0 {
if i < lowest_index {
lowest_index = i
width = len(substr)
@@ -3040,7 +3044,6 @@ left_justify :: proc(str: string, length: int, pad: string, allocator := context
pad_len := rune_count(pad)
b: Builder
- builder_init(&b, allocator)
builder_init(&b, 0, len(str) + (remains/pad_len + 1)*len(pad), allocator) or_return
w := to_writer(&b)
@@ -3075,7 +3078,6 @@ right_justify :: proc(str: string, length: int, pad: string, allocator := contex
pad_len := rune_count(pad)
b: Builder
- builder_init(&b, allocator)
builder_init(&b, 0, len(str) + (remains/pad_len + 1)*len(pad), allocator) or_return
w := to_writer(&b)
diff --git a/core/sync/chan/chan.odin b/core/sync/chan/chan.odin
index c470d15f3..eca4c28d7 100644
--- a/core/sync/chan/chan.odin
+++ b/core/sync/chan/chan.odin
@@ -7,16 +7,62 @@ import "core:mem"
import "core:sync"
import "core:math/rand"
+/*
+Determines what operations `Chan` supports.
+*/
Direction :: enum {
Send = -1,
Both = 0,
Recv = +1,
}
+/*
+A typed wrapper around `Raw_Chan` which should be used
+preferably.
+
+Note: all procedures accepting `Raw_Chan` also accept `Chan`.
+
+**Inputs**
+- `$T`: The type of the messages
+- `Direction`: what `Direction` the channel supports
+
+Example:
+
+ import "core:sync/chan"
+
+ chan_example :: proc() {
+ // Create an unbuffered channel with messages of type int,
+ // supporting both sending and receiving.
+ // Creating unidirectional channels, although possible, is useless.
+ c, _ := chan.create(chan.Chan(int), context.allocator)
+ defer chan.destroy(c)
+
+ // This channel can now only be used for receiving messages
+ recv_only_channel: chan.Chan(int, .Recv) = chan.as_recv(c)
+ // This channel can now only be used for sending messages
+ send_only_channel: chan.Chan(int, .Send) = chan.as_send(c)
+ }
+*/
Chan :: struct($T: typeid, $D: Direction = Direction.Both) {
#subtype impl: ^Raw_Chan `fmt:"-"`,
}
+/*
+`Raw_Chan` allows for thread-safe communication using fixed-size messages.
+This is the low-level implementation of `Chan`, which does not include
+the concept of Direction.
+
+Example:
+
+ import "core:sync/chan"
+
+ raw_chan_example :: proc() {
+ // Create an unbuffered channel with messages of type int,
+ c, _ := chan.create_raw(size_of(int), align_of(int), context.allocator)
+ defer chan.destroy(c)
+ }
+
+*/
Raw_Chan :: struct {
// Shared
allocator: runtime.Allocator,
@@ -36,12 +82,66 @@ Raw_Chan :: struct {
unbuffered_data: rawptr,
}
+/*
+Creates a buffered or unbuffered `Chan` instance.
+*Allocates Using Provided Allocator*
+
+**Inputs**
+- `$C`: Type of `Chan` to create
+- [`cap`: The capacity of the channel] omit for creating unbuffered channels
+- `allocator`: The allocator to use
+
+**Returns**:
+- The initialized `Chan`
+- An `Allocator_Error`
+
+Example:
+
+ import "core:sync/chan"
+
+ create_example :: proc() {
+ unbuffered: chan.Chan(int)
+ buffered: chan.Chan(int)
+ err: runtime.Allocator_Error
+
+ unbuffered, err = chan.create(chan.Chan(int), context.allocator)
+ assert(err == .None)
+ defer chan.destroy(unbuffered)
+
+ buffered, err = chan.create(chan.Chan(int), 10, context.allocator)
+ assert(err == .None)
+ defer chan.destroy(buffered)
+ }
+*/
create :: proc{
create_unbuffered,
create_buffered,
}
+/*
+Creates an unbuffered version of the specified `Chan` type.
+
+*Allocates Using Provided Allocator*
+
+**Inputs**
+- `$C`: Type of `Chan` to create
+- `allocator`: The allocator to use
+
+**Returns**:
+- The initialized `Chan`
+- An `Allocator_Error`
+
+Example:
+
+ import "core:sync/chan"
+
+ create_unbuffered_example :: proc() {
+ c, err := chan.create_unbuffered(chan.Chan(int), context.allocator)
+ assert(err == .None)
+ defer chan.destroy(c)
+ }
+*/
@(require_results)
create_unbuffered :: proc($C: typeid/Chan($T), allocator: runtime.Allocator) -> (c: C, err: runtime.Allocator_Error)
where size_of(T) <= int(max(u16)) {
@@ -49,6 +149,30 @@ create_unbuffered :: proc($C: typeid/Chan($T), allocator: runtime.Allocator) ->
return
}
+/*
+Creates a buffered version of the specified `Chan` type.
+
+*Allocates Using Provided Allocator*
+
+**Inputs**
+- `$C`: Type of `Chan` to create
+- `cap`: The capacity of the channel
+- `allocator`: The allocator to use
+
+**Returns**:
+- The initialized `Chan`
+- An `Allocator_Error`
+
+Example:
+
+ import "core:sync/chan"
+
+ create_buffered_example :: proc() {
+ c, err := chan.create_buffered(chan.Chan(int), 10, context.allocator)
+ assert(err == .None)
+ defer chan.destroy(c)
+ }
+*/
@(require_results)
create_buffered :: proc($C: typeid/Chan($T), #any_int cap: int, allocator: runtime.Allocator) -> (c: C, err: runtime.Allocator_Error)
where size_of(T) <= int(max(u16)) {
@@ -56,11 +180,70 @@ create_buffered :: proc($C: typeid/Chan($T), #any_int cap: int, allocator: runti
return
}
+/*
+Creates a buffered or unbuffered `Raw_Chan` for messages of the specified
+size and alignment.
+
+*Allocates Using Provided Allocator*
+
+**Inputs**
+- `msg_size`: The size of the messages the messages being sent
+- `msg_alignment`: The alignment of the messages being sent
+- [`cap`: The capacity of the channel] omit for creating unbuffered channels
+- `allocator`: The allocator to use
+
+**Returns**:
+- The initialized `Raw_Chan`
+- An `Allocator_Error`
+
+Example:
+
+ import "core:sync/chan"
+
+ create_raw_example :: proc() {
+ unbuffered: ^chan.Raw_Chan
+ buffered: ^chan.Raw_Chan
+ err: runtime.Allocator_Error
+
+ unbuffered, err = chan.create_raw(size_of(int), align_of(int), context.allocator)
+ assert(err == .None)
+ defer chan.destroy(unbuffered)
+
+ buffered, err = chan.create_raw(size_of(int), align_of(int), 10, context.allocator)
+ assert(err == .None)
+ defer chan.destroy(buffered)
+ }
+*/
create_raw :: proc{
create_raw_unbuffered,
create_raw_buffered,
}
+/*
+Creates an unbuffered `Raw_Chan` for messages of the specified
+size and alignment.
+
+*Allocates Using Provided Allocator*
+
+**Inputs**
+- `msg_size`: The size of the messages the messages being sent
+- `msg_alignment`: The alignment of the messages being sent
+- `allocator`: The allocator to use
+
+**Returns**:
+- The initialized `Raw_Chan`
+- An `Allocator_Error`
+
+Example:
+
+ import "core:sync/chan"
+
+ create_raw_unbuffered_example :: proc() {
+ unbuffered, err := chan.create_raw(size_of(int), align_of(int), context.allocator)
+ assert(err == .None)
+ defer chan.destroy(unbuffered)
+ }
+*/
@(require_results)
create_raw_unbuffered :: proc(#any_int msg_size, msg_alignment: int, allocator: runtime.Allocator) -> (c: ^Raw_Chan, err: runtime.Allocator_Error) {
assert(msg_size <= int(max(u16)))
@@ -80,6 +263,32 @@ create_raw_unbuffered :: proc(#any_int msg_size, msg_alignment: int, allocator:
return
}
+/*
+Creates a buffered `Raw_Chan` for messages of the specified
+size and alignment.
+
+*Allocates Using Provided Allocator*
+
+**Inputs**
+- `msg_size`: The size of the messages the messages being sent
+- `msg_alignment`: The alignment of the messages being sent
+- `cap`: The capacity of the channel
+- `allocator`: The allocator to use
+
+**Returns**:
+- The initialized `Raw_Chan`
+- An `Allocator_Error`
+
+Example:
+
+ import "core:sync/chan"
+
+ create_raw_unbuffered_example :: proc() {
+ c, err := chan.create_raw_buffered(size_of(int), align_of(int), 10, context.allocator)
+ assert(err == .None)
+ defer chan.destroy(c)
+ }
+*/
@(require_results)
create_raw_buffered :: proc(#any_int msg_size, msg_alignment: int, #any_int cap: int, allocator: runtime.Allocator) -> (c: ^Raw_Chan, err: runtime.Allocator_Error) {
assert(msg_size <= int(max(u16)))
@@ -110,6 +319,16 @@ create_raw_buffered :: proc(#any_int msg_size, msg_alignment: int, #any_int cap:
return
}
+
+/*
+Destroys the Channel.
+
+**Inputs**
+- `c`: The channel to destroy
+
+**Returns**:
+- An `Allocator_Error`
+*/
destroy :: proc(c: ^Raw_Chan) -> (err: runtime.Allocator_Error) {
if c != nil {
allocator := c.allocator
@@ -118,22 +337,142 @@ destroy :: proc(c: ^Raw_Chan) -> (err: runtime.Allocator_Error) {
return
}
+/*
+Creates a version of a channel that can only be used for sending
+not receiving.
+
+**Inputs**
+- `c`: The channel
+
+**Returns**:
+- An `Allocator_Error`
+
+Example:
+
+ import "core:sync/chan"
+
+ as_send_example :: proc() {
+ // this procedure takes a channel that can only
+ // be used for sending not receiving.
+ producer :: proc(c: chan.Chan(int, .Send)) {
+ chan.send(c, 112)
+
+ // compile-time error:
+ // value, ok := chan.recv(c)
+ }
+
+ c, err := chan.create(chan.Chan(int), 1, context.allocator)
+ assert(err == .None)
+ defer chan.destroy(c)
+
+ producer(chan.as_send(c))
+ }
+*/
@(require_results)
as_send :: #force_inline proc "contextless" (c: $C/Chan($T, $D)) -> (s: Chan(T, .Send)) where C.D <= .Both {
return transmute(type_of(s))c
}
+
+/*
+Creates a version of a channel that can only be used for receiving
+not sending.
+
+**Inputs**
+- `c`: The channel
+
+**Returns**:
+- An `Allocator_Error`
+
+Example:
+
+ import "core:sync/chan"
+
+ as_recv_example :: proc() {
+ consumer :: proc(c: chan.Chan(int, .Recv)) {
+ value, ok := chan.recv(c)
+
+ // compile-time error:
+ // chan.send(c, 22)
+ }
+
+ c, err := chan.create(chan.Chan(int), 1, context.allocator)
+ assert(err == .None)
+ defer chan.destroy(c)
+
+ chan.send(c, 112)
+ consumer(chan.as_recv(c))
+ }
+*/
@(require_results)
as_recv :: #force_inline proc "contextless" (c: $C/Chan($T, $D)) -> (r: Chan(T, .Recv)) where C.D >= .Both {
return transmute(type_of(r))c
}
+/*
+Sends the specified message, blocking the current thread if:
+- the channel is unbuffered
+- the channel's buffer is full
+until the channel is being read from. `send` will return
+`false` when attempting to send on an already closed channel.
+**Inputs**
+- `c`: The channel
+- `data`: The message to send
+
+**Returns**
+- `true` if the message was sent, `false` when the channel was already closed
+
+Example:
+
+ import "core:sync/chan"
+
+ send_example :: proc() {
+ c, err := chan.create(chan.Chan(int), 1, context.allocator)
+ assert(err == .None)
+ defer chan.destroy(c)
+
+ assert(chan.send(c, 2))
+
+ // this would block since the channel has a buffersize of 1
+ // assert(chan.send(c, 2))
+
+ // sending on a closed channel returns false
+ chan.close(c)
+ assert(! chan.send(c, 2))
+ }
+*/
send :: proc "contextless" (c: $C/Chan($T, $D), data: T) -> (ok: bool) where C.D <= .Both {
data := data
ok = send_raw(c, &data)
return
}
+/*
+Tries sending the specified message which is:
+- blocking: given the channel is unbuffered
+- non-blocking: given the channel is buffered
+
+**Inputs**
+- `c`: The channel
+- `data`: The message to send
+
+**Returns**
+- `true` if the message was sent, `false` when the channel was
+already closed or the channel's buffer was full
+
+Example:
+
+ import "core:sync/chan"
+
+ try_send_example :: proc() {
+ c, err := chan.create(chan.Chan(int), 1, context.allocator)
+ assert(err == .None)
+ defer chan.destroy(c)
+
+ assert(chan.try_send(c, 2), "there is enough space")
+ assert(!chan.try_send(c, 2), "the buffer is already full")
+ }
+*/
@(require_results)
try_send :: proc "contextless" (c: $C/Chan($T, $D), data: T) -> (ok: bool) where C.D <= .Both {
data := data
@@ -141,6 +480,43 @@ try_send :: proc "contextless" (c: $C/Chan($T, $D), data: T) -> (ok: bool) where
return
}
+/*
+Reads a message from the channel, blocking the current thread if:
+- the channel is unbuffered
+- the channel's buffer is empty
+until the channel is being written to. `recv` will return
+`false` when attempting to receive a message on an already closed channel.
+
+**Inputs**
+- `c`: The channel
+
+**Returns**
+- The message
+- `true` if a message was received, `false` when the channel was already closed
+
+Example:
+
+ import "core:sync/chan"
+
+ recv_example :: proc() {
+ c, err := chan.create(chan.Chan(int), 1, context.allocator)
+ assert(err == .None)
+ defer chan.destroy(c)
+
+ assert(chan.send(c, 2))
+
+ value, ok := chan.recv(c)
+ assert(ok, "the value was received")
+
+ // this would block since the channel is now empty
+ // value, ok = chan.recv(c)
+
+ // reading from a closed channel returns false
+ chan.close(c)
+ value, ok = chan.recv(c)
+ assert(!ok, "the channel is closed")
+ }
+*/
@(require_results)
recv :: proc "contextless" (c: $C/Chan($T)) -> (data: T, ok: bool) where C.D >= .Both {
ok = recv_raw(c, &data)
@@ -148,6 +524,29 @@ recv :: proc "contextless" (c: $C/Chan($T)) -> (data: T, ok: bool) where C.D >=
}
+/*
+Tries reading a message from the channel in a non-blocking fashion.
+
+**Inputs**
+- `c`: The channel
+
+**Returns**
+- The message
+- `true` if a message was received, `false` when the channel was already closed or no message was available
+
+Example:
+
+ import "core:sync/chan"
+
+ try_recv_example :: proc() {
+ c, err := chan.create(chan.Chan(int), context.allocator)
+ assert(err == .None)
+ defer chan.destroy(c)
+
+ _, ok := chan.try_recv(c)
+ assert(!ok, "there is not value to read")
+ }
+*/
@(require_results)
try_recv :: proc "contextless" (c: $C/Chan($T)) -> (data: T, ok: bool) where C.D >= .Both {
ok = try_recv_raw(c, &data)
@@ -155,6 +554,43 @@ try_recv :: proc "contextless" (c: $C/Chan($T)) -> (data: T, ok: bool) where C.D
}
+/*
+Sends the specified message, blocking the current thread if:
+- the channel is unbuffered
+- the channel's buffer is full
+until the channel is being read from. `send_raw` will return
+`false` when attempting to send on an already closed channel.
+
+Note: The message referenced by `msg_out` must match the size
+and alignment used when the `Raw_Chan` was created.
+
+**Inputs**
+- `c`: The channel
+- `msg_out`: Pointer to the data to send
+
+**Returns**
+- `true` if the message was sent, `false` when the channel was already closed
+
+Example:
+
+ import "core:sync/chan"
+
+ send_raw_example :: proc() {
+ c, err := chan.create_raw(size_of(int), align_of(int), 1, context.allocator)
+ assert(err == .None)
+ defer chan.destroy(c)
+
+ value := 2
+ assert(chan.send_raw(c, &value))
+
+ // this would block since the channel has a buffersize of 1
+ // assert(chan.send_raw(c, &value))
+
+ // sending on a closed channel returns false
+ chan.close(c)
+ assert(! chan.send_raw(c, &value))
+ }
+*/
@(require_results)
send_raw :: proc "contextless" (c: ^Raw_Chan, msg_in: rawptr) -> (ok: bool) {
if c == nil {
@@ -194,6 +630,45 @@ send_raw :: proc "contextless" (c: ^Raw_Chan, msg_in: rawptr) -> (ok: bool) {
return
}
+/*
+Reads a message from the channel, blocking the current thread if:
+- the channel is unbuffered
+- the channel's buffer is empty
+until the channel is being written to. `recv_raw` will return
+`false` when attempting to receive a message on an already closed channel.
+
+Note: The location pointed to by `msg_out` must match the size
+and alignment used when the `Raw_Chan` was created.
+
+**Inputs**
+- `c`: The channel
+- `msg_out`: Pointer to where the message should be stored
+
+**Returns**
+- `true` if a message was received, `false` when the channel was already closed
+
+Example:
+
+ import "core:sync/chan"
+
+ recv_raw_example :: proc() {
+ c, err := chan.create_raw(size_of(int), align_of(int), 1, context.allocator)
+ assert(err == .None)
+ defer chan.destroy(c)
+
+ value := 2
+ assert(chan.send_raw(c, &value))
+
+ assert(chan.recv_raw(c, &value))
+
+ // this would block since the channel is now empty
+ // assert(chan.recv_raw(c, &value))
+
+ // reading from a closed channel returns false
+ chan.close(c)
+ assert(! chan.recv_raw(c, &value))
+ }
+*/
@(require_results)
recv_raw :: proc "contextless" (c: ^Raw_Chan, msg_out: rawptr) -> (ok: bool) {
if c == nil {
@@ -244,6 +719,36 @@ recv_raw :: proc "contextless" (c: ^Raw_Chan, msg_out: rawptr) -> (ok: bool) {
}
+/*
+Tries sending the specified message which is:
+- blocking: given the channel is unbuffered
+- non-blocking: given the channel is buffered
+
+Note: The message referenced by `msg_out` must match the size
+and alignment used when the `Raw_Chan` was created.
+
+**Inputs**
+- `c`: the channel
+- `msg_out`: pointer to the data to send
+
+**Returns**
+- `true` if the message was sent, `false` when the channel was
+already closed or the channel's buffer was full
+
+Example:
+
+ import "core:sync/chan"
+
+ try_send_raw_example :: proc() {
+ c, err := chan.create_raw(size_of(int), align_of(int), 1, context.allocator)
+ assert(err == .None)
+ defer chan.destroy(c)
+
+ value := 2
+ assert(chan.try_send_raw(c, &value), "there is enough space")
+ assert(!chan.try_send_raw(c, &value), "the buffer is already full")
+ }
+*/
@(require_results)
try_send_raw :: proc "contextless" (c: ^Raw_Chan, msg_in: rawptr) -> (ok: bool) {
if c == nil {
@@ -281,6 +786,32 @@ try_send_raw :: proc "contextless" (c: ^Raw_Chan, msg_in: rawptr) -> (ok: bool)
return
}
+/*
+Reads a message from the channel if one is available.
+
+Note: The location pointed to by `msg_out` must match the size
+and alignment used when the `Raw_Chan` was created.
+
+**Inputs**
+- `c`: The channel
+- `msg_out`: Pointer to where the message should be stored
+
+**Returns**
+- `true` if a message was received, `false` when the channel was already closed or no message was available
+
+Example:
+
+ import "core:sync/chan"
+
+ try_recv_raw_example :: proc() {
+ c, err := chan.create_raw(size_of(int), align_of(int), context.allocator)
+ assert(err == .None)
+ defer chan.destroy(c)
+
+ value: int
+ assert(!chan.try_recv_raw(c, &value))
+ }
+*/
@(require_results)
try_recv_raw :: proc "contextless" (c: ^Raw_Chan, msg_out: rawptr) -> bool {
if c == nil {
@@ -319,16 +850,85 @@ try_recv_raw :: proc "contextless" (c: ^Raw_Chan, msg_out: rawptr) -> bool {
+/*
+Checks if the given channel is buffered.
+
+**Inputs**
+- `c`: The channel
+
+**Returns**:
+- `true` if the channel is buffered, `false` otherwise
+
+Example:
+
+ import "core:sync/chan"
+
+ is_buffered_example :: proc() {
+ c, _ := chan.create(chan.Chan(int), 1, context.allocator)
+ defer chan.destroy(c)
+ assert(chan.is_buffered(c))
+ }
+*/
@(require_results)
is_buffered :: proc "contextless" (c: ^Raw_Chan) -> bool {
return c != nil && c.queue != nil
}
+/*
+Checks if the given channel is unbuffered.
+
+**Inputs**
+- `c`: The channel
+
+**Returns**:
+- `true` if the channel is unbuffered, `false` otherwise
+
+Example:
+
+ import "core:sync/chan"
+
+ is_buffered_example :: proc() {
+ c, _ := chan.create(chan.Chan(int), context.allocator)
+ defer chan.destroy(c)
+ assert(chan.is_unbuffered(c))
+ }
+*/
@(require_results)
is_unbuffered :: proc "contextless" (c: ^Raw_Chan) -> bool {
return c != nil && c.unbuffered_data != nil
}
+/*
+Returns the number of elements currently in the channel.
+
+Note: Unbuffered channels will always return `0`
+because they cannot hold elements.
+
+**Inputs**
+- `c`: The channel
+
+**Returns**:
+- Number of elements
+
+Example:
+
+ import "core:sync/chan"
+ import "core:fmt"
+
+ len_example :: proc() {
+ c, _ := chan.create(chan.Chan(int), 2, context.allocator)
+ defer chan.destroy(c)
+
+ fmt.println(chan.len(c))
+ assert(chan.send(c, 1)) // add an element
+ fmt.println(chan.len(c))
+ }
+
+Output:
+
+ 0
+ 1
+*/
@(require_results)
len :: proc "contextless" (c: ^Raw_Chan) -> int {
if c != nil && c.queue != nil {
@@ -338,6 +938,34 @@ len :: proc "contextless" (c: ^Raw_Chan) -> int {
return 0
}
+/*
+Returns the number of elements the channel could hold.
+
+Note: Unbuffered channels will always return `0`
+because they cannot hold elements.
+
+**Inputs**
+- `c`: The channel
+
+**Returns**:
+- Number of elements
+
+Example:
+
+ import "core:sync/chan"
+ import "core:fmt"
+
+ cap_example :: proc() {
+ c, _ := chan.create(chan.Chan(int), 2, context.allocator)
+ defer chan.destroy(c)
+
+ fmt.println(chan.cap(c))
+ }
+
+Output:
+
+ 2
+*/
@(require_results)
cap :: proc "contextless" (c: ^Raw_Chan) -> int {
if c != nil && c.queue != nil {
@@ -347,6 +975,36 @@ cap :: proc "contextless" (c: ^Raw_Chan) -> int {
return 0
}
+/*
+Closes the channel, preventing new messages from being added.
+
+**Inputs**
+- `c`: The channel
+
+**Returns**:
+- `true` if the channel was closed by this operation, `false` if it was already closed
+
+Example:
+
+ import "core:sync/chan"
+
+ close_example :: proc() {
+ c, _ := chan.create(chan.Chan(int), 2, context.allocator)
+ defer chan.destroy(c)
+
+ // Sending a message to an open channel
+ assert(chan.send(c, 1), "allowed to send")
+
+ // Closing the channel successfully
+ assert(chan.close(c), "successfully closed")
+
+ // Trying to send a message after the channel is closed (should fail)
+ assert(!chan.send(c, 1), "not allowed to send after close")
+
+ // Trying to close the channel again (should fail since it's already closed)
+ assert(!chan.close(c), "was already closed")
+ }
+*/
close :: proc "contextless" (c: ^Raw_Chan) -> bool {
if c == nil {
return false
@@ -361,6 +1019,15 @@ close :: proc "contextless" (c: ^Raw_Chan) -> bool {
return true
}
+/*
+Returns if the channel is closed or not
+
+**Inputs**
+- `c`: The channel
+
+**Returns**:
+- `true` if the channel is closed, `false` otherwise
+*/
@(require_results)
is_closed :: proc "contextless" (c: ^Raw_Chan) -> bool {
if c == nil {
@@ -370,56 +1037,30 @@ is_closed :: proc "contextless" (c: ^Raw_Chan) -> bool {
return bool(c.closed)
}
+/*
+Returns whether a message is ready to be read, i.e.,
+if a call to `recv` or `recv_raw` would block
+**Inputs**
+- `c`: The channel
+**Returns**
+- `true` if a message can be read, `false` otherwise
-Raw_Queue :: struct {
- data: [^]byte,
- len: int,
- cap: int,
- next: int,
- size: int, // element size
-}
+Example:
-raw_queue_init :: proc "contextless" (q: ^Raw_Queue, data: rawptr, cap: int, size: int) {
- q.data = ([^]byte)(data)
- q.len = 0
- q.cap = cap
- q.next = 0
- q.size = size
-}
+ import "core:sync/chan"
+ can_recv_example :: proc() {
+ c, err := chan.create(chan.Chan(int), 1, context.allocator)
+ assert(err == .None)
+ defer chan.destroy(c)
-@(require_results)
-raw_queue_push :: proc "contextless" (q: ^Raw_Queue, data: rawptr) -> bool {
- if q.len == q.cap {
- return false
+ assert(!chan.can_recv(c), "the cannel is empty")
+ assert(chan.send(c, 2))
+ assert(chan.can_recv(c), "there is message to read")
}
- pos := q.next + q.len
- if pos >= q.cap {
- pos -= q.cap
- }
-
- val_ptr := q.data[pos*q.size:]
- mem.copy(val_ptr, data, q.size)
- q.len += 1
- return true
-}
-
-@(require_results)
-raw_queue_pop :: proc "contextless" (q: ^Raw_Queue) -> (data: rawptr) {
- if q.len > 0 {
- data = q.data[q.next*q.size:]
- q.next += 1
- q.len -= 1
- if q.next >= q.cap {
- q.next -= q.cap
- }
- }
- return
-}
-
-
+*/
@(require_results)
can_recv :: proc "contextless" (c: ^Raw_Chan) -> bool {
sync.guard(&c.mutex)
@@ -430,6 +1071,31 @@ can_recv :: proc "contextless" (c: ^Raw_Chan) -> bool {
}
+/*
+Returns whether a message can be sent without blocking the current
+thread. Specifically, it checks if the channel is buffered and not full,
+or if there is already a reader waiting for a message.
+
+**Inputs**
+- `c`: The channel
+
+**Returns**
+- `true` if a message can be send, `false` otherwise
+
+Example:
+
+ import "core:sync/chan"
+
+ can_send_example :: proc() {
+ c, err := chan.create(chan.Chan(int), 1, context.allocator)
+ assert(err == .None)
+ defer chan.destroy(c)
+
+ assert(chan.can_send(c), "the channel's buffer is not full")
+ assert(chan.send(c, 2))
+ assert(!chan.can_send(c), "the channel's buffer is full")
+ }
+*/
@(require_results)
can_send :: proc "contextless" (c: ^Raw_Chan) -> bool {
sync.guard(&c.mutex)
@@ -440,7 +1106,69 @@ can_send :: proc "contextless" (c: ^Raw_Chan) -> bool {
}
+/*
+Attempts to either send or receive messages on the specified channels.
+`select_raw` first identifies which channels have messages ready to be received
+and which are available for sending. It then randomly selects one operation
+(either a send or receive) to perform.
+
+Note: Each message in `send_msgs` corresponds to the send channel at the same index in `sends`.
+
+**Inputs**
+- `recv`: A slice of channels to read from
+- `sends`: A slice of channels to send messages on
+- `send_msgs`: A slice of messages to send
+- `recv_out`: A pointer to the location where, when receiving, the message should be stored
+
+**Returns**
+- Position of the available channel which was used for receiving or sending
+- `true` if sending/receiving was successfull, `false` if the channel was closed or no channel was available
+
+Example:
+
+ import "core:sync/chan"
+ import "core:fmt"
+
+ select_raw_example :: proc() {
+ c, err := chan.create(chan.Chan(int), 1, context.allocator)
+ assert(err == .None)
+ defer chan.destroy(c)
+
+ // sending value '1' on the channel
+ value1 := 1
+ msgs := [?]rawptr{&value1}
+ send_chans := [?]^chan.Raw_Chan{c}
+
+ // for simplicity the same channel used for sending is also used for receiving
+ receive_chans := [?]^chan.Raw_Chan{c}
+ // where the value from the read should be stored
+ received_value: int
+
+ idx, ok := chan.select_raw(receive_chans[:], send_chans[:], msgs[:], &received_value)
+ fmt.println("SELECT: ", idx, ok)
+ fmt.println("RECEIVED VALUE ", received_value)
+
+ idx, ok = chan.select_raw(receive_chans[:], send_chans[:], msgs[:], &received_value)
+ fmt.println("SELECT: ", idx, ok)
+ fmt.println("RECEIVED VALUE ", received_value)
+
+ // closing of a channel also affects the select operation
+ chan.close(c)
+
+ idx, ok = chan.select_raw(receive_chans[:], send_chans[:], msgs[:], &received_value)
+ fmt.println("SELECT: ", idx, ok)
+ }
+
+Output:
+
+ SELECT: 0 true
+ RECEIVED VALUE 0
+ SELECT: 0 true
+ RECEIVED VALUE 1
+ SELECT: 0 false
+
+*/
@(require_results)
select_raw :: proc "odin" (recvs: []^Raw_Chan, sends: []^Raw_Chan, send_msgs: []rawptr, recv_out: rawptr) -> (select_idx: int, ok: bool) #no_bounds_check {
Select_Op :: struct {
@@ -486,3 +1214,137 @@ select_raw :: proc "odin" (recvs: []^Raw_Chan, sends: []^Raw_Chan, send_msgs: []
}
return
}
+
+
+/*
+`Raw_Queue` is a non-thread-safe queue implementation designed to store messages
+of fixed size and alignment.
+
+Note: For most use cases, it is recommended to use `core:container/queue` instead,
+as `Raw_Queue` is used internally by `Raw_Chan` and may not provide the desired
+level of convenience for typical applications.
+*/
+@(private)
+Raw_Queue :: struct {
+ data: [^]byte,
+ len: int,
+ cap: int,
+ next: int,
+ size: int, // element size
+}
+
+/*
+Initializes a `Raw_Queue`
+
+**Inputs**
+- `q`: A pointert to the `Raw_Queue` to initialize
+- `data`: The pointer to backing slice storing the messages
+- `cap`: The capacity of the queue
+- `size`: The size of a message
+
+Example:
+
+ import "core:sync/chan"
+
+ raw_queue_init_example :: proc() {
+ // use a stack allocated array as backing storage
+ storage: [100]int
+
+ rq: chan.Raw_Queue
+ chan.raw_queue_init(&rq, &storage, cap(storage), size_of(int))
+ }
+*/
+@(private)
+raw_queue_init :: proc "contextless" (q: ^Raw_Queue, data: rawptr, cap: int, size: int) {
+ q.data = ([^]byte)(data)
+ q.len = 0
+ q.cap = cap
+ q.next = 0
+ q.size = size
+}
+
+/*
+Add an element to the queue.
+
+Note: The message referenced by `data` must match the size
+and alignment used when the `Raw_Queue` was initialized.
+
+**Inputs**
+- `q`: A pointert to the `Raw_Queue`
+- `data`: The pointer to message to add
+
+**Returns**
+- `true` if the element was added, `false` when the queue is already full
+
+Example:
+
+ import "core:sync/chan"
+
+ raw_queue_push_example :: proc() {
+ storage: [100]int
+ rq: chan.Raw_Queue
+ chan.raw_queue_init(&rq, &storage, cap(storage), size_of(int))
+
+ value := 2
+ assert(chan.raw_queue_push(&rq, &value), "there was enough space")
+ }
+*/
+@(private, require_results)
+raw_queue_push :: proc "contextless" (q: ^Raw_Queue, data: rawptr) -> bool {
+ if q.len == q.cap {
+ return false
+ }
+ pos := q.next + q.len
+ if pos >= q.cap {
+ pos -= q.cap
+ }
+
+ val_ptr := q.data[pos*q.size:]
+ mem.copy(val_ptr, data, q.size)
+ q.len += 1
+ return true
+}
+
+/*
+Removes and returns the first element of the queue.
+
+Note: The returned element is only guaranteed to be valid until the next
+`raw_queue_push` operation. Accessing it after that point may result in
+undefined behavior.
+
+**Inputs**
+- `c`: A pointer to the `Raw_Queue`.
+
+**Returns**
+- A pointer to the first element in the queue, or `nil` if the queue is empty.
+
+Example:
+
+ import "core:sync/chan"
+
+ raw_queue_pop_example :: proc() {
+ storage: [100]int
+ rq: chan.Raw_Queue
+ chan.raw_queue_init(&rq, &storage, cap(storage), size_of(int))
+
+ assert(chan.raw_queue_pop(&rq) == nil, "queue was empty")
+
+ // add an element to the queue
+ value := 2
+ assert(chan.raw_queue_push(&rq, &value), "there was enough space")
+
+ assert((cast(^int)chan.raw_queue_pop(&rq))^ == 2, "retrieved the element")
+ }
+*/
+@(private, require_results)
+raw_queue_pop :: proc "contextless" (q: ^Raw_Queue) -> (data: rawptr) {
+ if q.len > 0 {
+ data = q.data[q.next*q.size:]
+ q.next += 1
+ q.len -= 1
+ if q.next >= q.cap {
+ q.next -= q.cap
+ }
+ }
+ return
+}
diff --git a/core/sync/chan/doc.odin b/core/sync/chan/doc.odin
new file mode 100644
index 000000000..5d65d7410
--- /dev/null
+++ b/core/sync/chan/doc.odin
@@ -0,0 +1,69 @@
+/*
+This package provides both high-level and low-level channel types
+for thread-safe communication.
+
+While channels are essentially thread-safe queues under the hood,
+their primary purpose is to facilitate safe communication between
+multiple readers and multiple writers.
+Although they can be used like queues, channels are designed with
+synchronization and concurrent messaging patterns in mind.
+
+Provided types:
+- `Chan` a high level channel
+- `Raw_Chan` a low level channel
+- `Raw_Queue` a low level non-threadsafe queue implementation used internally
+
+Example:
+
+ import "core:sync/chan"
+ import "core:fmt"
+ import "core:thread"
+
+ // The consumer reads from the channel until it's closed.
+ // Closing the channel acts as a signal to stop.
+ consumer :: proc(recv_chan: chan.Chan(int, .Recv)) {
+ for {
+ value, ok := chan.recv(recv_chan)
+ if !ok {
+ break // More idiomatic than return here
+ }
+ fmt.println("[CONSUMER] Received:", value)
+ }
+ fmt.println("[CONSUMER] Channel closed, stopping.")
+ }
+
+ // The producer sends `count` number of messages.
+ producer :: proc(send_chan: chan.Chan(int, .Send), count: int) {
+ for i in 0.. (ok: bool) {
head := &waitq.list
waiter := Wait_Node{
- thread = unix.pthread_self(),
+ thread = posix.pthread_self(),
futex = f,
prev = head,
next = head.next,
@@ -67,25 +66,24 @@ _futex_wait :: proc "contextless" (f: ^Futex, expect: u32) -> (ok: bool) {
waiter.prev.next = &waiter
waiter.next.prev = &waiter
- old_mask, mask: haiku.sigset_t
- haiku.sigemptyset(&mask)
- haiku.sigaddset(&mask, haiku.SIGCONT)
- unix.pthread_sigmask(haiku.SIG_BLOCK, &mask, &old_mask)
+ old_mask, mask: posix.sigset_t
+ posix.sigemptyset(&mask)
+ posix.sigaddset(&mask, .SIGCONT)
+ posix.pthread_sigmask(.BLOCK, &mask, &old_mask)
if u32(atomic_load_explicit(f, .Acquire)) == expect {
waitq_unlock(waitq)
defer waitq_lock(waitq)
- sig: c.int
- haiku.sigwait(&mask, &sig)
- errno := haiku.errno()
- ok = errno == .OK
+ sig: posix.Signal
+ errno := posix.sigwait(&mask, &sig)
+ ok = errno == nil
}
waiter.prev.next = waiter.next
waiter.next.prev = waiter.prev
- _ = unix.pthread_sigmask(haiku.SIG_SETMASK, &old_mask, nil)
+ _ = posix.pthread_sigmask(.SETMASK, &old_mask, nil)
// FIXME: Add error handling!
return
@@ -101,7 +99,7 @@ _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expect: u32, duration
head := &waitq.list
waiter := Wait_Node{
- thread = unix.pthread_self(),
+ thread = posix.pthread_self(),
futex = f,
prev = head,
next = head.next,
@@ -110,29 +108,29 @@ _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expect: u32, duration
waiter.prev.next = &waiter
waiter.next.prev = &waiter
- old_mask, mask: haiku.sigset_t
- haiku.sigemptyset(&mask)
- haiku.sigaddset(&mask, haiku.SIGCONT)
- unix.pthread_sigmask(haiku.SIG_BLOCK, &mask, &old_mask)
+ old_mask, mask: posix.sigset_t
+ posix.sigemptyset(&mask)
+ posix.sigaddset(&mask, .SIGCONT)
+ posix.pthread_sigmask(.BLOCK, &mask, &old_mask)
if u32(atomic_load_explicit(f, .Acquire)) == expect {
waitq_unlock(waitq)
defer waitq_lock(waitq)
- info: haiku.siginfo_t
- ts := unix.timespec{
- tv_sec = i64(duration / 1e9),
+ info: posix.siginfo_t
+ ts := posix.timespec{
+ tv_sec = posix.time_t(i64(duration / 1e9)),
tv_nsec = i64(duration % 1e9),
}
haiku.sigtimedwait(&mask, &info, &ts)
- errno := haiku.errno()
- ok = errno == .EAGAIN || errno == .OK
+ errno := posix.errno()
+ ok = errno == .EAGAIN || errno == nil
}
waiter.prev.next = waiter.next
waiter.next.prev = waiter.prev
- unix.pthread_sigmask(haiku.SIG_SETMASK, &old_mask, nil)
+ posix.pthread_sigmask(.SETMASK, &old_mask, nil)
// FIXME: Add error handling!
return
@@ -146,7 +144,7 @@ _futex_signal :: proc "contextless" (f: ^Futex) {
head := &waitq.list
for waiter := head.next; waiter != head; waiter = waiter.next {
if waiter.futex == f {
- unix.pthread_kill(waiter.thread, haiku.SIGCONT)
+ posix.pthread_kill(waiter.thread, .SIGCONT)
break
}
}
@@ -160,7 +158,7 @@ _futex_broadcast :: proc "contextless" (f: ^Futex) {
head := &waitq.list
for waiter := head.next; waiter != head; waiter = waiter.next {
if waiter.futex == f {
- unix.pthread_kill(waiter.thread, haiku.SIGCONT)
+ posix.pthread_kill(waiter.thread, .SIGCONT)
}
}
}
diff --git a/core/sync/futex_linux.odin b/core/sync/futex_linux.odin
index 52143880b..2f4b5af72 100644
--- a/core/sync/futex_linux.odin
+++ b/core/sync/futex_linux.odin
@@ -49,9 +49,6 @@ _futex_signal :: proc "contextless" (futex: ^Futex) {
}
_futex_broadcast :: proc "contextless" (futex: ^Futex) {
- // NOTE(flysand): This code was kinda funny and I don't want to remove it, but here I will
- // record history of what has been in here before
- // FUTEX_WAKE_PRIVATE | FUTEX_WAKE
_, errno := linux.futex(cast(^linux.Futex) futex, linux.FUTEX_WAKE, {.PRIVATE}, max(i32))
#partial switch errno {
case .NONE:
diff --git a/core/sync/futex_wasm.odin b/core/sync/futex_wasm.odin
index 0f9659a02..16e69ca74 100644
--- a/core/sync/futex_wasm.odin
+++ b/core/sync/futex_wasm.odin
@@ -12,8 +12,8 @@ _futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool {
when !intrinsics.has_target_feature("atomics") {
panic_contextless("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it")
} else {
- s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, -1)
- return s != 0
+ _ = intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, -1)
+ return true
}
}
@@ -22,7 +22,7 @@ _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, durati
panic_contextless("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it")
} else {
s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, i64(duration))
- return s != 0
+ return s != 2
}
}
@@ -30,12 +30,7 @@ _futex_signal :: proc "contextless" (f: ^Futex) {
when !intrinsics.has_target_feature("atomics") {
panic_contextless("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it")
} else {
- loop: for {
- s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), 1)
- if s >= 1 {
- return
- }
- }
+ _ = intrinsics.wasm_memory_atomic_notify32((^u32)(f), 1)
}
}
@@ -43,12 +38,7 @@ _futex_broadcast :: proc "contextless" (f: ^Futex) {
when !intrinsics.has_target_feature("atomics") {
panic_contextless("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it")
} else {
- loop: for {
- s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), ~u32(0))
- if s >= 0 {
- return
- }
- }
+ _ = intrinsics.wasm_memory_atomic_notify32((^u32)(f), max(u32))
}
}
diff --git a/core/sync/futex_windows.odin b/core/sync/futex_windows.odin
index bb9686a1a..927e6781e 100644
--- a/core/sync/futex_windows.odin
+++ b/core/sync/futex_windows.odin
@@ -26,7 +26,7 @@ foreign Ntdll {
BUT requires taking the return value of it and if it is non-zero
converting that status to a DOS error and then SetLastError
If this is not done, then things don't work as expected when
- and error occurs
+ an error occurs
GODDAMN MICROSOFT!
*/
@@ -46,7 +46,7 @@ _futex_wait :: proc "contextless" (f: ^Futex, expect: u32) -> bool {
_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expect: u32, duration: time.Duration) -> bool {
expect := expect
- // NOTE(bill): for some bizarre reason, this has be a negative number
+ // NOTE(bill): for some bizarre reason, this has to be a negative number
timeout := -i64(duration / 100)
return CustomWaitOnAddress(f, &expect, size_of(expect), &timeout)
}
diff --git a/core/sync/primitives_atomic.odin b/core/sync/primitives_atomic.odin
index 3c4324eb7..a8a84b2bc 100644
--- a/core/sync/primitives_atomic.odin
+++ b/core/sync/primitives_atomic.odin
@@ -67,7 +67,7 @@ atomic_mutex_unlock :: proc "contextless" (m: ^Atomic_Mutex) {
switch atomic_exchange_explicit(&m.state, .Unlocked, .Release) {
case .Unlocked:
- unreachable()
+ // Kind of okay - unlocking while already unlocked.
case .Locked:
// Okay
case .Waiting:
diff --git a/core/sys/darwin/Foundation/NSBlock.odin b/core/sys/darwin/Foundation/NSBlock.odin
index b9d94bfee..1ef5e8a9b 100644
--- a/core/sys/darwin/Foundation/NSBlock.odin
+++ b/core/sys/darwin/Foundation/NSBlock.odin
@@ -25,6 +25,10 @@ Block_createLocalWithParam :: proc (user_data: rawptr, user_proc: proc "c" (user
b, _ := Block_createInternalWithParam(false, user_data, user_proc, {})
return b
}
+@(objc_type=Block, objc_name="invoke")
+Block_invoke :: proc "c" (self: ^Block, args: ..any) -> ^Object {
+ return msgSend(^Object, self, "invoke:", ..args)
+}
@(private)
Internal_Block_Literal_Base :: struct {
diff --git a/core/sys/darwin/Foundation/NSData.odin b/core/sys/darwin/Foundation/NSData.odin
index 04c1ce25d..8baaf3486 100644
--- a/core/sys/darwin/Foundation/NSData.odin
+++ b/core/sys/darwin/Foundation/NSData.odin
@@ -13,6 +13,23 @@ Data_init :: proc "c" (self: ^Data) -> ^Data {
return msgSend(^Data, self, "init")
}
+@(objc_type=Data, objc_name="initWithBytes")
+Data_initWithBytes :: proc "c" (self: ^Data, bytes: []byte) -> ^Data {
+ return msgSend(^Data, self, "initWithBytes:length:", raw_data(bytes), len(bytes))
+}
+
+@(objc_type=Data, objc_name="initWithBytesNoCopy")
+Data_initWithBytesNoCopy :: proc "c" (self: ^Data, bytes: []byte, freeWhenDone: BOOL) -> ^Data {
+ return msgSend(
+ ^Data,
+ self,
+ "initWithBytesNoCopy:length:freeWhenDone:",
+ raw_data(bytes),
+ len(bytes),
+ freeWhenDone,
+ )
+}
+
@(objc_type=Data, objc_name="mutableBytes")
Data_mutableBytes :: proc "c" (self: ^Data) -> rawptr {
return msgSend(rawptr, self, "mutableBytes")
diff --git a/core/sys/darwin/Foundation/NSDate.odin b/core/sys/darwin/Foundation/NSDate.odin
index 41efb0cf5..4ba539aa4 100644
--- a/core/sys/darwin/Foundation/NSDate.odin
+++ b/core/sys/darwin/Foundation/NSDate.odin
@@ -18,6 +18,11 @@ Date_dateWithTimeIntervalSinceNow :: proc "c" (secs: TimeInterval) -> ^Date {
return msgSend(^Date, Date, "dateWithTimeIntervalSinceNow:", secs)
}
+@(objc_type=Date, objc_name="timeIntervalSince1970")
+Date_timeIntervalSince1970 :: proc "c" (self: ^Date) -> f64 {
+ return msgSend(f64, self, "timeIntervalSince1970")
+}
+
@(objc_type=Date, objc_name="distantFuture", objc_is_class_method=true)
Date_distantFuture :: proc "c" () -> ^Date {
return msgSend(^Date, Date, "distantFuture")
diff --git a/core/sys/darwin/Foundation/NSDictionary.odin b/core/sys/darwin/Foundation/NSDictionary.odin
index 8af58cf62..ed98f3168 100644
--- a/core/sys/darwin/Foundation/NSDictionary.odin
+++ b/core/sys/darwin/Foundation/NSDictionary.odin
@@ -15,7 +15,7 @@ Dictionary_dictionaryWithObject :: proc "c" (object: ^Object, forKey: ^Object) -
@(objc_type=Dictionary, objc_name="dictionaryWithObjects", objc_is_class_method=true)
Dictionary_dictionaryWithObjects :: proc "c" (objects: [^]^Object, forKeys: [^]^Object, count: UInteger) -> ^Dictionary {
- return msgSend(^Dictionary, Dictionary, "dictionaryWithObjects:forKeys:count", objects, forKeys, count)
+ return msgSend(^Dictionary, Dictionary, "dictionaryWithObjects:forKeys:count:", objects, forKeys, count)
}
@@ -31,7 +31,7 @@ Dictionary_init :: proc "c" (self: ^Dictionary) -> ^Dictionary {
@(objc_type=Dictionary, objc_name="initWithObjects")
Dictionary_initWithObjects :: proc "c" (self: ^Dictionary, objects: [^]^Object, forKeys: [^]^Object, count: UInteger) -> ^Dictionary {
- return msgSend(^Dictionary, self, "initWithObjects:forKeys:count", objects, forKeys, count)
+ return msgSend(^Dictionary, self, "initWithObjects:forKeys:count:", objects, forKeys, count)
}
@(objc_type=Dictionary, objc_name="objectForKey")
diff --git a/core/sys/darwin/Foundation/NSMenu.odin b/core/sys/darwin/Foundation/NSMenu.odin
index e49162a7f..9a74151b0 100644
--- a/core/sys/darwin/Foundation/NSMenu.odin
+++ b/core/sys/darwin/Foundation/NSMenu.odin
@@ -30,6 +30,7 @@ MenuItem :: struct {using _: Object}
MenuItem_alloc :: proc "c" () -> ^MenuItem {
return msgSend(^MenuItem, MenuItem, "alloc")
}
+
@(objc_type=MenuItem, objc_name="registerActionCallback", objc_is_class_method=true)
MenuItem_registerActionCallback :: proc "c" (name: cstring, callback: MenuItemCallback) -> SEL {
s := string(name)
@@ -50,11 +51,21 @@ MenuItem_registerActionCallback :: proc "c" (name: cstring, callback: MenuItemCa
return sel
}
+@(objc_type=MenuItem, objc_name="separatorItem", objc_is_class_method=true)
+MenuItem_separatorItem :: proc "c" () -> ^MenuItem {
+ return msgSend(^MenuItem, MenuItem, "separatorItem")
+}
+
@(objc_type=MenuItem, objc_name="init")
MenuItem_init :: proc "c" (self: ^MenuItem) -> ^MenuItem {
return msgSend(^MenuItem, self, "init")
}
+@(objc_type=MenuItem, objc_name="initWithTitle")
+MenuItem_initWithTitle :: proc "c" (self: ^MenuItem, title: ^String, action: SEL, keyEquivalent: ^String) -> ^MenuItem {
+ return msgSend(^MenuItem, self, "initWithTitle:action:keyEquivalent:", title, action, keyEquivalent)
+}
+
@(objc_type=MenuItem, objc_name="setKeyEquivalentModifierMask")
MenuItem_setKeyEquivalentModifierMask :: proc "c" (self: ^MenuItem, modifierMask: KeyEquivalentModifierMask) {
msgSend(nil, self, "setKeyEquivalentModifierMask:", modifierMask)
@@ -75,6 +86,11 @@ MenuItem_title :: proc "c" (self: ^MenuItem) -> ^String {
return msgSend(^String, self, "title")
}
+@(objc_type=MenuItem, objc_name="setTitle")
+MenuItem_setTitle :: proc "c" (self: ^MenuItem, title: ^String) -> ^String {
+ return msgSend(^String, self, "title:", title)
+}
+
@(objc_class="NSMenu")
diff --git a/core/sys/darwin/Foundation/NSObjectProtocol.odin b/core/sys/darwin/Foundation/NSObjectProtocol.odin
new file mode 100644
index 000000000..99d942579
--- /dev/null
+++ b/core/sys/darwin/Foundation/NSObjectProtocol.odin
@@ -0,0 +1,5 @@
+package objc_Foundation
+
+@(objc_class="NSObjectProtocol")
+ObjectProtocol :: struct {using _: Object}
+// TODO: implement NSObjectProtocol
diff --git a/core/sys/darwin/Foundation/NSProcessInfo.odin b/core/sys/darwin/Foundation/NSProcessInfo.odin
new file mode 100644
index 000000000..e070bf8e2
--- /dev/null
+++ b/core/sys/darwin/Foundation/NSProcessInfo.odin
@@ -0,0 +1,203 @@
+package objc_Foundation
+
+import "base:intrinsics"
+
+import "core:c"
+
+@(objc_class="NSProcessInfo")
+ProcessInfo :: struct {using _: Object}
+
+// Getting the Process Information Agent
+
+@(objc_type=ProcessInfo, objc_name="processInfo", objc_is_class_method=true)
+ProcessInfo_processInfo :: proc "c" () -> ^ProcessInfo {
+ return msgSend(^ProcessInfo, ProcessInfo, "processInfo")
+}
+
+// Accessing Process Information
+
+@(objc_type=ProcessInfo, objc_name="arguments")
+ProcessInfo_arguments :: proc "c" (self: ^ProcessInfo) -> ^Array {
+ return msgSend(^Array, self, "arguments")
+}
+
+@(objc_type=ProcessInfo, objc_name="environment")
+ProcessInfo_environment :: proc "c" (self: ^ProcessInfo) -> ^Dictionary {
+ return msgSend(^Dictionary, self, "environment")
+}
+
+@(objc_type=ProcessInfo, objc_name="globallyUniqueString")
+ProcessInfo_globallyUniqueString :: proc "c" (self: ^ProcessInfo) -> ^String {
+ return msgSend(^String, self, "globallyUniqueString")
+}
+
+@(objc_type=ProcessInfo, objc_name="isMacCatalystApp")
+ProcessInfo_isMacCatalystApp :: proc "c" (self: ^ProcessInfo) -> bool {
+ return msgSend(bool, self, "isMacCatalystApp")
+}
+
+@(objc_type=ProcessInfo, objc_name="isiOSAppOnMac")
+ProcessInfo_isiOSAppOnMac :: proc "c" (self: ^ProcessInfo) -> bool {
+ return msgSend(bool, self, "isiOSAppOnMac")
+}
+
+@(objc_type=ProcessInfo, objc_name="processIdentifier")
+ProcessInfo_processIdentifier :: proc "c" (self: ^ProcessInfo) -> c.int {
+ return msgSend(c.int, self, "processIdentifier")
+}
+
+@(objc_type=ProcessInfo, objc_name="processName")
+ProcessInfo_processName :: proc "c" (self: ^ProcessInfo) -> ^String {
+ return msgSend(^String, self, "processName")
+}
+
+// Accessing User Information
+
+@(objc_type=ProcessInfo, objc_name="userName")
+ProcessInfo_userName :: proc "c" (self: ^ProcessInfo) -> ^String {
+ return msgSend(^String, self, "userName")
+}
+
+@(objc_type=ProcessInfo, objc_name="fullUserName")
+ProcessInfo_fullUserName :: proc "c" (self: ^ProcessInfo) -> ^String {
+ return msgSend(^String, self, "fullUserName")
+}
+
+// Sudden Application Termination
+
+@(objc_type=ProcessInfo, objc_name="disableSuddenTermination")
+ProcessInfo_disableSuddenTermination :: proc "c" (self: ^ProcessInfo) {
+ msgSend(nil, self, "disableSuddenTermination")
+}
+
+@(objc_type=ProcessInfo, objc_name="enableSuddenTermination")
+ProcessInfo_enableSuddenTermination :: proc "c" (self: ^ProcessInfo) {
+ msgSend(nil, self, "enableSuddenTermination")
+}
+
+// Controlling Automatic Termination
+
+@(objc_type=ProcessInfo, objc_name="disableAutomaticTermination")
+ProcessInfo_disableAutomaticTermination :: proc "c" (self: ^ProcessInfo, reason: ^String) {
+ msgSend(nil, self, "disableAutomaticTermination:", reason)
+}
+
+@(objc_type=ProcessInfo, objc_name="enableAutomaticTermination")
+ProcessInfo_enableAutomaticTermination :: proc "c" (self: ^ProcessInfo, reason: ^String) {
+ msgSend(nil, self, "enableAutomaticTermination:", reason)
+}
+
+@(objc_type=ProcessInfo, objc_name="automaticTerminationSupportEnabled")
+ProcessInfo_automaticTerminationSupportEnabled :: proc "c" (self: ^ProcessInfo) -> bool {
+ return msgSend(bool, self, "automaticTerminationSupportEnabled")
+}
+
+@(objc_type=ProcessInfo, objc_name="setAutomaticTerminationSupportEnabled")
+ProcessInfo_setAutomaticTerminationSupportEnabled :: proc "c" (self: ^ProcessInfo, automaticTerminationSupportEnabled: bool) {
+ msgSend(nil, self, "setAutomaticTerminationSupportEnabled:", automaticTerminationSupportEnabled)
+}
+
+// Getting Host Information
+
+@(objc_type=ProcessInfo, objc_name="hostName")
+ProcessInfo_hostName :: proc "c" (self: ^ProcessInfo) -> ^String {
+ return msgSend(^String, self, "hostName")
+}
+
+@(objc_type=ProcessInfo, objc_name="operatingSystemVersionString")
+ProcessInfo_operatingSystemVersionString :: proc "c" (self: ^ProcessInfo) -> ^String {
+ return msgSend(^String, self, "operatingSystemVersionString")
+}
+
+@(objc_type=ProcessInfo, objc_name="operatingSystemVersion")
+ProcessInfo_operatingSystemVersion :: proc "c" (self: ^ProcessInfo) -> OperatingSystemVersion {
+ return msgSend(OperatingSystemVersion, self, "operatingSystemVersion")
+}
+
+@(objc_type=ProcessInfo, objc_name="isOperatingSystemAtLeastVersion")
+ProcessInfo_isOperatingSystemAtLeastVersion :: proc "c" (self: ^ProcessInfo, version: OperatingSystemVersion) -> bool {
+ return msgSend(bool, self, "isOperatingSystemAtLeastVersion:", version)
+}
+
+// Getting Computer Information
+
+@(objc_type=ProcessInfo, objc_name="processorCount")
+ProcessInfo_processorCount :: proc "c" (self: ^ProcessInfo) -> UInteger {
+ return msgSend(UInteger, self, "processorCount")
+}
+
+@(objc_type=ProcessInfo, objc_name="activeProcessorCount")
+ProcessInfo_activeProcessorCount :: proc "c" (self: ^ProcessInfo) -> UInteger {
+ return msgSend(UInteger, self, "activeProcessorCount")
+}
+
+@(objc_type=ProcessInfo, objc_name="physicalMemory")
+ProcessInfo_physicalMemory :: proc "c" (self: ^ProcessInfo) -> c.ulonglong {
+ return msgSend(c.ulonglong, self, "physicalMemory")
+}
+
+@(objc_type=ProcessInfo, objc_name="systemUptime")
+ProcessInfo_systemUptime :: proc "c" (self: ^ProcessInfo) -> TimeInterval {
+ return msgSend(TimeInterval, self, "systemUptime")
+}
+
+// Managing Activities
+
+@(private)
+log2 :: intrinsics.constant_log2
+
+ActivityOptionsBits :: enum u64 {
+ IdleDisplaySleepDisabled = log2(1099511627776), // Require the screen to stay powered on.
+ IdleSystemSleepDisabled = log2(1048576), // Prevent idle sleep.
+ SuddenTerminationDisabled = log2(16384), // Prevent sudden termination.
+ AutomaticTerminationDisabled = log2(32768), // Prevent automatic termination.
+ AnimationTrackingEnabled = log2(35184372088832), // Track activity with an animation signpost interval.
+ TrackingEnabled = log2(70368744177664), // Track activity with a signpost interval.
+ UserInitiated = log2(16777215), // Performing a user-requested action.
+ UserInitiatedAllowingIdleSystemSleep = log2(15728639), // Performing a user-requested action, but the system can sleep on idle.
+ Background = log2(255), // Initiated some kind of work, but not as the direct result of a user request.
+ LatencyCritical = log2(1095216660480), // Requires the highest amount of timer and I/O precision available.
+ UserInteractive = log2(1095233437695), // Responding to user interaction.
+}
+ActivityOptions :: bit_set[ActivityOptionsBits; u64]
+
+@(objc_type=ProcessInfo, objc_name="beginActivityWithOptions")
+ProcessInfo_beginActivityWithOptions :: proc "c" (self: ^ProcessInfo, options: ActivityOptions, reason: ^String) -> ^ObjectProtocol {
+ return msgSend(^ObjectProtocol, self, "beginActivityWithOptions:reason:", options, reason)
+}
+
+@(objc_type=ProcessInfo, objc_name="endActivity")
+ProcessInfo_endActivity :: proc "c" (self: ^ProcessInfo, activity: ^ObjectProtocol) {
+ msgSend(nil, self, "endActivity:", activity)
+}
+
+@(objc_type=ProcessInfo, objc_name="performActivityWithOptions")
+ProcessInfo_performActivityWithOptions :: proc "c" (self: ^ProcessInfo, options: ActivityOptions, reason: ^String, block: proc "c" ()) {
+ msgSend(nil, self, "performActivityWithOptions:reason:usingBlock:", options, reason, block)
+}
+
+@(objc_type=ProcessInfo, objc_name="performExpiringActivityWithReason")
+ProcessInfo_performExpiringActivityWithReason :: proc "c" (self: ^ProcessInfo, reason: ^String, block: proc "c" (expired: bool)) {
+ msgSend(nil, self, "performExpiringActivityWithReason:usingBlock:", reason, block)
+}
+
+// Getting the Thermal State
+
+ProcessInfoThermalState :: enum c.long {
+ Nominal,
+ Fair,
+ Serious,
+ Critical,
+}
+
+@(objc_type=ProcessInfo, objc_name="thermalState")
+ProcessInfo_thermalState :: proc "c" (self: ^ProcessInfo) -> ProcessInfoThermalState {
+ return msgSend(ProcessInfoThermalState, self, "thermalState")
+}
+
+// Determining Whether Low Power Mode is Enabled
+
+@(objc_type=ProcessInfo, objc_name="isLowPowerModeEnabled")
+ProcessInfo_isLowPowerModeEnabled :: proc "c" (self: ^ProcessInfo) -> bool {
+ return msgSend(bool, self, "isLowPowerModeEnabled")
+}
diff --git a/core/sys/darwin/Foundation/NSSavePanel.odin b/core/sys/darwin/Foundation/NSSavePanel.odin
index 8e4d7a07b..d40b3ecd5 100644
--- a/core/sys/darwin/Foundation/NSSavePanel.odin
+++ b/core/sys/darwin/Foundation/NSSavePanel.odin
@@ -7,3 +7,13 @@ SavePanel :: struct{ using _: Panel }
SavePanel_runModal :: proc "c" (self: ^SavePanel) -> ModalResponse {
return msgSend(ModalResponse, self, "runModal")
}
+
+@(objc_type=SavePanel, objc_name="savePanel", objc_is_class_method=true)
+SavePanel_savePanel :: proc "c" () -> ^SavePanel {
+ return msgSend(^SavePanel, SavePanel, "savePanel")
+}
+
+@(objc_type=SavePanel, objc_name="URL")
+SavePanel_URL :: proc "c" (self: ^SavePanel) -> ^URL {
+ return msgSend(^URL, self, "URL")
+}
diff --git a/core/sys/darwin/Foundation/NSString.odin b/core/sys/darwin/Foundation/NSString.odin
index a10b33fc0..eac855c3b 100644
--- a/core/sys/darwin/Foundation/NSString.odin
+++ b/core/sys/darwin/Foundation/NSString.odin
@@ -134,6 +134,11 @@ String_isEqualToString :: proc "c" (self, other: ^String) -> BOOL {
return msgSend(BOOL, self, "isEqualToString:", other)
}
+@(objc_type=String, objc_name="stringByAppendingString")
+String_stringByAppendingString :: proc "c" (self, other: ^String) -> ^String {
+ return msgSend(^String, self, "stringByAppendingString:", other)
+}
+
@(objc_type=String, objc_name="rangeOfString")
String_rangeOfString :: proc "c" (self, other: ^String, options: StringCompareOptions) -> Range {
return msgSend(Range, self, "rangeOfString:options:", other, options)
diff --git a/core/sys/darwin/Foundation/NSToolbar.odin b/core/sys/darwin/Foundation/NSToolbar.odin
new file mode 100644
index 000000000..be6613df4
--- /dev/null
+++ b/core/sys/darwin/Foundation/NSToolbar.odin
@@ -0,0 +1,14 @@
+package objc_Foundation
+@(objc_class = "NSToolbar")
+
+Toolbar :: struct { using _: Object }
+
+@(objc_type = Toolbar, objc_name = "alloc", objc_is_class_method = true)
+Toolbar_alloc :: proc "c" () -> ^Toolbar {
+ return msgSend(^Toolbar, Toolbar, "alloc")
+}
+
+@(objc_type = Toolbar, objc_name = "init")
+Toolbar_init :: proc "c" (self: ^Toolbar) -> ^Toolbar {
+ return msgSend(^Toolbar, self, "init")
+}
diff --git a/core/sys/darwin/Foundation/NSTypes.odin b/core/sys/darwin/Foundation/NSTypes.odin
index fbd883a8f..822a07ab1 100644
--- a/core/sys/darwin/Foundation/NSTypes.odin
+++ b/core/sys/darwin/Foundation/NSTypes.odin
@@ -20,7 +20,7 @@ BOOL :: bool // TODO(bill): should this be `distinct`?
YES :: true
NO :: false
-OperatingSystemVersion :: struct #packed {
+OperatingSystemVersion :: struct #align(8) {
majorVersion: Integer,
minorVersion: Integer,
patchVersion: Integer,
@@ -58,4 +58,4 @@ when size_of(Float) == 8 {
} else {
_POINT_ENCODING :: "{NSPoint=ff}"
_SIZE_ENCODING :: "{NSSize=ff}"
-}
\ No newline at end of file
+}
diff --git a/core/sys/darwin/Foundation/NSURL.odin b/core/sys/darwin/Foundation/NSURL.odin
index 9e9081219..fb9ebca9e 100644
--- a/core/sys/darwin/Foundation/NSURL.odin
+++ b/core/sys/darwin/Foundation/NSURL.odin
@@ -28,3 +28,8 @@ URL_initFileURLWithPath :: proc "c" (self: ^URL, path: ^String) -> ^URL {
URL_fileSystemRepresentation :: proc "c" (self: ^URL) -> cstring {
return msgSend(cstring, self, "fileSystemRepresentation")
}
+
+@(objc_type=URL, objc_name="relativePath")
+URL_relativePath :: proc "c" (self: ^URL) -> ^String {
+ return msgSend(^String, self, "relativePath")
+}
diff --git a/core/sys/darwin/Foundation/NSURLRequest.odin b/core/sys/darwin/Foundation/NSURLRequest.odin
new file mode 100644
index 000000000..6b2819c67
--- /dev/null
+++ b/core/sys/darwin/Foundation/NSURLRequest.odin
@@ -0,0 +1,24 @@
+package objc_Foundation
+
+@(objc_class = "URLRequest")
+URLRequest :: struct { using _: Object }
+
+@(objc_type = URLRequest, objc_name = "alloc", objc_is_class_method = true)
+URLRequest_alloc :: proc "c" () -> ^URLRequest {
+ return msgSend(^URLRequest, URLRequest, "alloc")
+}
+
+@(objc_type = URLRequest, objc_name = "requestWithURL", objc_is_class_method = true)
+URLRequest_requestWithURL :: proc "c" (url: ^URL) -> ^URLRequest {
+ return msgSend(^URLRequest, URLRequest, "requestWithURL:", url)
+}
+
+@(objc_type = URLRequest, objc_name = "init")
+URLRequest_init :: proc "c" (self: ^URLRequest) -> ^URLRequest {
+ return msgSend(^URLRequest, URLRequest, "init")
+}
+
+@(objc_type = URLRequest, objc_name = "url")
+URLRequest_url :: proc "c" (self: ^URLRequest) -> ^URL {
+ return msgSend(^URL, self, "URL")
+}
\ No newline at end of file
diff --git a/core/sys/darwin/Foundation/NSURLResponse.odin b/core/sys/darwin/Foundation/NSURLResponse.odin
new file mode 100644
index 000000000..6295817e8
--- /dev/null
+++ b/core/sys/darwin/Foundation/NSURLResponse.odin
@@ -0,0 +1,19 @@
+package objc_Foundation
+
+@(objc_class = "NSURLResponse")
+URLResponse :: struct { using _: Object }
+
+@(objc_type = URLResponse, objc_name = "alloc", objc_is_class_method = true)
+URLResponse_alloc :: proc "c" () -> ^URLResponse {
+ return msgSend(^URLResponse, URLResponse, "alloc")
+}
+
+@(objc_type = URLResponse, objc_name = "init")
+URLResponse_init :: proc "c" (self: ^URLResponse) -> ^URLResponse {
+ return msgSend(^URLResponse, URLResponse, "init")
+}
+
+@(objc_type = URLResponse, objc_name = "initWithURL")
+URLResponse_initWithURL :: proc "c" (self: ^URLResponse, url: ^URL, mime_type: ^String, length: int, encoding: ^String ) -> ^URLResponse {
+ return msgSend(^URLResponse, self, "initWithURL:MIMEType:expectedContentLength:textEncodingName:", url, mime_type, Integer(length), encoding)
+}
\ No newline at end of file
diff --git a/core/sys/darwin/Foundation/NSWindow.odin b/core/sys/darwin/Foundation/NSWindow.odin
index 0fe334207..57ac2b6f6 100644
--- a/core/sys/darwin/Foundation/NSWindow.odin
+++ b/core/sys/darwin/Foundation/NSWindow.odin
@@ -129,6 +129,10 @@ WindowDelegateTemplate :: struct {
windowDidExitVersionBrowser: proc(notification: ^Notification),
}
+Window_Title_Visibility :: enum UInteger {
+ Visible,
+ Hidden,
+}
WindowDelegate :: struct { using _: Object } // This is not the same as NSWindowDelegate
_WindowDelegateInternal :: struct {
@@ -616,6 +620,10 @@ View_setWantsLayer :: proc "c" (self: ^View, wantsLayer: BOOL) {
View_convertPointFromView :: proc "c" (self: ^View, point: Point, view: ^View) -> Point {
return msgSend(Point, self, "convertPoint:fromView:", point, view)
}
+@(objc_type=View, objc_name="addSubview")
+View_addSubview :: proc "c" (self: ^View, view: ^View) {
+ msgSend(nil, self, "addSubview:", view)
+}
@(objc_class="NSWindow")
Window :: struct {using _: Responder}
@@ -748,4 +756,28 @@ Window_hasTitleBar :: proc "c" (self: ^Window) -> BOOL {
@(objc_type=Window, objc_name="orderedIndex")
Window_orderedIndex :: proc "c" (self: ^Window) -> Integer {
return msgSend(Integer, self, "orderedIndex")
+}
+@(objc_type=Window, objc_name="setMinSize")
+Window_setMinSize :: proc "c" (self: ^Window, size: Size) {
+ msgSend(nil, self, "setMinSize:", size)
+}
+@(objc_type=Window, objc_name="setTitleVisibility")
+Window_setTitleVisibility :: proc "c" (self: ^Window, visibility: Window_Title_Visibility) {
+ msgSend(nil, self, "setTitleVisibility:", visibility)
+}
+@(objc_type=Window, objc_name="performZoom")
+Window_performZoom :: proc "c" (self: ^Window) {
+ msgSend(nil, self, "performZoom:", self)
+}
+@(objc_type=Window, objc_name="setFrameAutosaveName")
+NSWindow_setFrameAutosaveName :: proc "c" (self: ^Window, name: ^String) {
+ msgSend(nil, self, "setFrameAutosaveName:", name)
+}
+@(objc_type=Window, objc_name="performWindowDragWithEvent")
+Window_performWindowDragWithEvent :: proc "c" (self: ^Window, event: ^Event) {
+ msgSend(nil, self, "performWindowDragWithEvent:", event)
+}
+@(objc_type=Window, objc_name="setToolbar")
+Window_setToolbar :: proc "c" (self: ^Window, toolbar: ^Toolbar) {
+ msgSend(nil, self, "setToolbar:", toolbar)
}
\ No newline at end of file
diff --git a/core/sys/freebsd/syscalls.odin b/core/sys/freebsd/syscalls.odin
index 83b51138a..405d1e47c 100644
--- a/core/sys/freebsd/syscalls.odin
+++ b/core/sys/freebsd/syscalls.odin
@@ -21,6 +21,7 @@ SYS_close : uintptr : 6
SYS_getpid : uintptr : 20
SYS_recvfrom : uintptr : 29
SYS_accept : uintptr : 30
+SYS_getpeername: uintptr : 31
SYS_getsockname: uintptr : 32
SYS_fcntl : uintptr : 92
SYS_fsync : uintptr : 95
@@ -202,24 +203,36 @@ accept_nil :: proc "contextless" (s: Fd) -> (Fd, Errno) {
accept :: proc { accept_T, accept_nil }
+getsockname_or_peername :: proc "contextless" (s: Fd, sockaddr: ^$T, is_peer: bool) -> Errno {
+ // sockaddr must contain a valid pointer, or this will segfault because
+ // we're telling the syscall that there's memory available to write to.
+ addrlen: socklen_t = size_of(T)
+
+ result, ok := intrinsics.syscall_bsd(
+ is_peer ? SYS_getpeername : SYS_getsockname,
+ cast(uintptr)s,
+ cast(uintptr)sockaddr,
+ cast(uintptr)&addrlen)
+
+ if !ok {
+ return cast(Errno)result
+ }
+
+ return nil
+}
+
+// Get name of connected peer
+//
+// The getpeername() system call appeared in 4.2BSD.
+getpeername :: proc "contextless" (s: Fd, sockaddr: ^$T) -> Errno {
+ return getsockname_or_peername(s, sockaddr, true)
+}
+
// Get socket name.
//
// The getsockname() system call appeared in 4.2BSD.
getsockname :: proc "contextless" (s: Fd, sockaddr: ^$T) -> Errno {
- // sockaddr must contain a valid pointer, or this will segfault because
- // we're telling the syscall that there's memory available to write to.
- addrlen: socklen_t = size_of(T)
-
- result, ok := intrinsics.syscall_bsd(SYS_getsockname,
- cast(uintptr)s,
- cast(uintptr)sockaddr,
- cast(uintptr)&addrlen)
-
- if !ok {
- return cast(Errno)result
- }
-
- return nil
+ return getsockname_or_peername(s, sockaddr, false)
}
// Synchronize changes to a file.
diff --git a/core/sys/haiku/errors.odin b/core/sys/haiku/errno.odin
similarity index 63%
rename from core/sys/haiku/errors.odin
rename to core/sys/haiku/errno.odin
index febe647ea..ef5a360bd 100644
--- a/core/sys/haiku/errors.odin
+++ b/core/sys/haiku/errno.odin
@@ -1,26 +1,31 @@
#+build haiku
package sys_haiku
-import "core:c"
+import "core:sys/posix"
-Errno :: enum c.int {
- // Error baselines
- GENERAL_ERROR_BASE = min(c.int),
- OS_ERROR_BASE = GENERAL_ERROR_BASE + 0x1000,
- APP_ERROR_BASE = GENERAL_ERROR_BASE + 0x2000,
- INTERFACE_ERROR_BASE = GENERAL_ERROR_BASE + 0x3000,
- MEDIA_ERROR_BASE = GENERAL_ERROR_BASE + 0x4000,
- TRANSLATION_ERROR_BASE = GENERAL_ERROR_BASE + 0x4800,
- MIDI_ERROR_BASE = GENERAL_ERROR_BASE + 0x5000,
- STORAGE_ERROR_BASE = GENERAL_ERROR_BASE + 0x6000,
- POSIX_ERROR_BASE = GENERAL_ERROR_BASE + 0x7000,
- MAIL_ERROR_BASE = GENERAL_ERROR_BASE + 0x8000,
- PRINT_ERROR_BASE = GENERAL_ERROR_BASE + 0x9000,
- DEVICE_ERROR_BASE = GENERAL_ERROR_BASE + 0xa000,
+foreign import libroot "system:c"
- // Developer-defined errors start at (ERRORS_END+1)
- ERRORS_END = GENERAL_ERROR_BASE + 0xffff,
+USE_POSITIVE_POSIX_ERRORS :: posix._HAIKU_USE_POSITIVE_POSIX_ERRORS
+POSIX_ERROR_FACTOR :: posix._POSIX_ERROR_FACTOR
+// Error baselines
+GENERAL_ERROR_BASE :: min(i32)
+OS_ERROR_BASE :: GENERAL_ERROR_BASE + 0x1000
+APP_ERROR_BASE :: GENERAL_ERROR_BASE + 0x2000
+INTERFACE_ERROR_BASE :: GENERAL_ERROR_BASE + 0x3000
+MEDIA_ERROR_BASE :: GENERAL_ERROR_BASE + 0x4000
+TRANSLATION_ERROR_BASE :: GENERAL_ERROR_BASE + 0x4800
+MIDI_ERROR_BASE :: GENERAL_ERROR_BASE + 0x5000
+STORAGE_ERROR_BASE :: GENERAL_ERROR_BASE + 0x6000
+POSIX_ERROR_BASE :: GENERAL_ERROR_BASE + 0x7000
+MAIL_ERROR_BASE :: GENERAL_ERROR_BASE + 0x8000
+PRINT_ERROR_BASE :: GENERAL_ERROR_BASE + 0x9000
+DEVICE_ERROR_BASE :: GENERAL_ERROR_BASE + 0xA000
+
+// Developer-defined errors start at (ERRORS_END+1)
+ERRORS_END :: GENERAL_ERROR_BASE + 0xFFFF
+
+Errno :: enum i32 {
// General Errors
NO_MEMORY = GENERAL_ERROR_BASE + 0,
IO_ERROR = GENERAL_ERROR_BASE + 1,
@@ -107,31 +112,95 @@ Errno :: enum c.int {
PARTIAL_READ = STORAGE_ERROR_BASE + 16,
PARTIAL_WRITE = STORAGE_ERROR_BASE + 17,
- // Some POSIX errors
- E2BIG = POSIX_ERROR_BASE + 1,
- EFBIG = POSIX_ERROR_BASE + 4,
- ENODEV = POSIX_ERROR_BASE + 7,
- ERANGE = POSIX_ERROR_BASE + 17,
- EOVERFLOW = POSIX_ERROR_BASE + 41,
- EOPNOTSUPP = POSIX_ERROR_BASE + 43,
-
- ENOSYS = POSIX_ERROR_BASE + 9,
- EAGAIN = WOULD_BLOCK,
+ EIO = posix.EIO,
+ EACCES = posix.EACCES,
+ EINVAL = posix.EINVAL,
+ ETIMEDOUT = posix.ETIMEDOUT,
+ EINTR = posix.EINTR,
+ EAGAIN = posix.EAGAIN,
+ EWOULDBLOCK = posix.EWOULDBLOCK,
+ EBUSY = posix.EBUSY,
+ EPERM = posix.EPERM,
+ EFAULT = posix.EFAULT,
+ ENOEXEC = posix.ENOEXEC,
+ EBADF = posix.EBADF,
+ EEXIST = posix.EEXIST,
+ ENOENT = posix.ENOENT,
+ ENAMETOOLONG = posix.ENAMETOOLONG,
+ ENOTDIR = posix.ENOTDIR,
+ ENOTEMPTY = posix.ENOTEMPTY,
+ ENOSPC = posix.ENOSPC,
+ EROFS = posix.EROFS,
+ EISDIR = posix.EISDIR,
+ EMFILE = posix.EMFILE,
+ EXDEV = posix.EXDEV,
+ ELOOP = posix.ELOOP,
+ EPIPE = posix.EPIPE,
+ ENOMEM = posix.ENOMEM,
+ E2BIG = posix.E2BIG,
+ ECHILD = posix.ECHILD,
+ EDEADLK = posix.EDEADLK,
+ EFBIG = posix.EFBIG,
+ EMLINK = posix.EMLINK,
+ ENFILE = posix.ENFILE,
+ ENODEV = posix.ENODEV,
+ ENOLCK = posix.ENOLCK,
+ ENOSYS = posix.ENOSYS,
+ ENOTTY = posix.ENOTTY,
+ ENXIO = posix.ENXIO,
+ ESPIPE = posix.ESPIPE,
+ ESRCH = posix.ESRCH,
+ EDOM = posix.EDOM,
+ ERANGE = posix.ERANGE,
+ EPROTOTYPE = posix.EPROTOTYPE,
+ EPROTONOSUPPORT = posix.EPROTONOSUPPORT,
+ EAFNOSUPPORT = posix.EAFNOSUPPORT,
+ EADDRINUSE = posix.EADDRINUSE,
+ EADDRNOTAVAIL = posix.EADDRNOTAVAIL,
+ ENETDOWN = posix.ENETDOWN,
+ ENETUNREACH = posix.ENETUNREACH,
+ ENETRESET = posix.ENETRESET,
+ ECONNABORTED = posix.ECONNABORTED,
+ ECONNRESET = posix.ECONNRESET,
+ EISCONN = posix.EISCONN,
+ ENOTCONN = posix.ENOTCONN,
+ ECONNREFUSED = posix.ECONNREFUSED,
+ EHOSTUNREACH = posix.EHOSTUNREACH,
+ ENOPROTOOPT = posix.ENOPROTOOPT,
+ ENOBUFS = posix.ENOBUFS,
+ EINPROGRESS = posix.EINPROGRESS,
+ EALREADY = posix.EALREADY,
+ EILSEQ = posix.EILSEQ,
+ ENOMSG = posix.ENOMSG,
+ ESTALE = posix.ESTALE,
+ EOVERFLOW = posix.EOVERFLOW,
+ EMSGSIZE = posix.EMSGSIZE,
+ EOPNOTSUPP = posix.EOPNOTSUPP,
+ ENOTSOCK = posix.ENOTSOCK,
+ EBADMSG = posix.EBADMSG,
+ ECANCELED = posix.ECANCELED,
+ EDESTADDRREQ = posix.EDESTADDRREQ,
+ EDQUOT = posix.EDQUOT,
+ EIDRM = posix.EIDRM,
+ EMULTIHOP = posix.EMULTIHOP,
+ ENODATA = posix.ENODATA,
+ ENOLINK = posix.ENOLINK,
+ ENOSR = posix.ENOSR,
+ ENOSTR = posix.ENOSTR,
+ ENOTSUP = posix.ENOTSUP,
+ EPROTO = posix.EPROTO,
+ ETIME = posix.ETIME,
+ ETXTBSY = posix.ETXTBSY,
+ ENOTRECOVERABLE = posix.ENOTRECOVERABLE,
+ EOWNERDEAD = posix.EOWNERDEAD,
// New error codes that can be mapped to POSIX errors
- TOO_MANY_ARGS_NEG = E2BIG,
- FILE_TOO_LARGE_NEG = EFBIG,
- DEVICE_NOT_FOUND_NEG = ENODEV,
- RESULT_NOT_REPRESENTABLE_NEG = ERANGE,
- BUFFER_OVERFLOW_NEG = EOVERFLOW,
- NOT_SUPPORTED_NEG = EOPNOTSUPP,
-
- TOO_MANY_ARGS_POS = -E2BIG,
- FILE_TOO_LARGE_POS = -EFBIG,
- DEVICE_NOT_FOUND_POS = -ENODEV,
- RESULT_NOT_REPRESENTABLE_POS = -ERANGE,
- BUFFER_OVERFLOW_POS = -EOVERFLOW,
- NOT_SUPPORTED_POS = -EOPNOTSUPP,
+ TOO_MANY_ARGS = POSIX_ERROR_FACTOR * E2BIG,
+ FILE_TOO_LARGE = POSIX_ERROR_FACTOR * EFBIG,
+ DEVICE_NOT_FOUND = POSIX_ERROR_FACTOR * ENODEV,
+ RESULT_NOT_REPRESENTABLE = POSIX_ERROR_FACTOR * ERANGE,
+ BUFFER_OVERFLOW = POSIX_ERROR_FACTOR * EOVERFLOW,
+ NOT_SUPPORTED = POSIX_ERROR_FACTOR * EOPNOTSUPP,
// Media Kit Errors
STREAM_NOT_FOUND = MEDIA_ERROR_BASE + 0,
@@ -226,14 +295,8 @@ Errno :: enum c.int {
ILLEGAL_DATA = TRANSLATION_ERROR_BASE + 2,
}
-errno :: #force_inline proc "contextless" () -> Errno {
- return Errno(_errnop()^)
-}
-
-foreign import libroot "system:c"
+@(default_calling_convention="c")
foreign libroot {
- _to_positive_error :: proc(error: c.int) -> c.int ---
- _to_negative_error :: proc(error: c.int) -> c.int ---
-
- _errnop :: proc() -> ^c.int ---
+ _to_positive_error :: proc(error: i32) -> i32 ---
+ _to_negative_error :: proc(error: i32) -> i32 ---
}
diff --git a/core/sys/haiku/find_directory.odin b/core/sys/haiku/find_directory.odin
index 758c4dff4..c700bd53b 100644
--- a/core/sys/haiku/find_directory.odin
+++ b/core/sys/haiku/find_directory.odin
@@ -1,9 +1,11 @@
#+build haiku
package sys_haiku
-import "core:c"
+import "base:intrinsics"
-directory_which :: enum c.int {
+foreign import libroot "system:c"
+
+directory_which :: enum i32 {
// Per volume directories
DESKTOP_DIRECTORY = 0,
TRASH_DIRECTORY,
@@ -110,17 +112,18 @@ directory_which :: enum c.int {
BEOS_SOUNDS_DIRECTORY,
}
-find_path_flags :: enum c.int {
- CREATE_DIRECTORY = 0x0001,
- CREATE_PARENT_DIRECTORY = 0x0002,
- EXISTING_ONLY = 0x0004,
+find_path_flag :: enum u32 {
+ CREATE_DIRECTORY = intrinsics.constant_log2(0x0001),
+ CREATE_PARENT_DIRECTORY = intrinsics.constant_log2(0x0002),
+ EXISTING_ONLY = intrinsics.constant_log2(0x0004),
- // find_paths() only!
- SYSTEM_ONLY = 0x0010,
- USER_ONLY = 0x0020,
+ // find_paths() only
+ SYSTEM_ONLY = intrinsics.constant_log2(0x0010),
+ USER_ONLY = intrinsics.constant_log2(0x0020),
}
+find_path_flags :: distinct bit_set[find_path_flag; u32]
-path_base_directory :: enum c.int {
+path_base_directory :: enum i32 {
INSTALLATION_LOCATION_DIRECTORY,
ADD_ONS_DIRECTORY,
APPS_DIRECTORY,
@@ -146,7 +149,7 @@ path_base_directory :: enum c.int {
TRANSLATORS_DIRECTORY,
VAR_DIRECTORY,
- // find_path() only!
+ // find_path() only
IMAGE_PATH = 1000,
PACKAGE_PATH,
}
@@ -154,15 +157,15 @@ path_base_directory :: enum c.int {
// value that can be used instead of a pointer to a symbol in the program image
APP_IMAGE_SYMBOL :: rawptr(addr_t(0))
// pointer to a symbol in the callers image (same as B_CURRENT_IMAGE_SYMBOL)
-current_image_symbol :: proc() -> rawptr { return rawptr(current_image_symbol) }
+current_image_symbol :: proc "contextless" () -> rawptr { return rawptr(current_image_symbol) }
-foreign import libroot "system:c"
+@(default_calling_convention="c")
foreign libroot {
- find_directory :: proc(which: directory_which, volume: dev_t, createIt: bool, pathString: [^]c.char, length: i32) -> status_t ---
- find_path :: proc(codePointer: rawptr, baseDirectory: path_base_directory, subPath: cstring, pathBuffer: [^]c.char, bufferSize: c.size_t) -> status_t ---
- find_path_etc :: proc(codePointer: rawptr, dependency: cstring, architecture: cstring, baseDirectory: path_base_directory, subPath: cstring, flags: find_path_flags, pathBuffer: [^]c.char, bufferSize: c.size_t) -> status_t ---
- find_path_for_path :: proc(path: cstring, baseDirectory: path_base_directory, subPath: cstring, pathBuffer: [^]c.char, bufferSize: c.size_t) -> status_t ---
- find_path_for_path_etc :: proc(path: cstring, dependency: cstring, architecture: cstring, baseDirectory: path_base_directory, subPath: cstring, flags: find_path_flags, pathBuffer: [^]c.char, bufferSize: c.size_t) -> status_t ---
- find_paths :: proc(baseDirectory: path_base_directory, subPath: cstring, _paths: ^[^][^]c.char, _pathCount: ^c.size_t) -> status_t ---
- find_paths_etc :: proc(architecture: cstring, baseDirectory: path_base_directory, subPath: cstring, flags: find_path_flags, _paths: ^[^][^]c.char, _pathCount: ^c.size_t) -> status_t ---
+ find_directory :: proc(which: directory_which, volume: dev_t, createIt: bool, pathString: [^]byte, length: i32) -> status_t ---
+ find_path :: proc(codePointer: rawptr, baseDirectory: path_base_directory, subPath: cstring, pathBuffer: [^]byte, bufferSize: uint) -> status_t ---
+ find_path_etc :: proc(codePointer: rawptr, dependency: cstring, architecture: cstring, baseDirectory: path_base_directory, subPath: cstring, flags: find_path_flags, pathBuffer: [^]byte, bufferSize: uint) -> status_t ---
+ find_path_for_path :: proc(path: cstring, baseDirectory: path_base_directory, subPath: cstring, pathBuffer: [^]byte, bufferSize: uint) -> status_t ---
+ find_path_for_path_etc :: proc(path: cstring, dependency: cstring, architecture: cstring, baseDirectory: path_base_directory, subPath: cstring, flags: find_path_flags, pathBuffer: [^]byte, bufferSize: uint) -> status_t ---
+ find_paths :: proc(baseDirectory: path_base_directory, subPath: cstring, _paths: ^[^][^]byte, _pathCount: ^uint) -> status_t ---
+ find_paths_etc :: proc(architecture: cstring, baseDirectory: path_base_directory, subPath: cstring, flags: find_path_flags, _paths: ^[^][^]byte, _pathCount: ^uint) -> status_t ---
}
diff --git a/core/sys/haiku/os.odin b/core/sys/haiku/os.odin
index 6ab3ef573..3edee88b5 100644
--- a/core/sys/haiku/os.odin
+++ b/core/sys/haiku/os.odin
@@ -1,8 +1,8 @@
#+build haiku
package sys_haiku
-import "core:c"
-import "core:sys/unix"
+import "base:intrinsics"
+import "core:sys/posix"
foreign import libroot "system:c"
@@ -18,8 +18,8 @@ OS_NAME_LENGTH :: 32
area_info :: struct {
area: area_id,
- name: [OS_NAME_LENGTH]c.char,
- size: c.size_t,
+ name: [OS_NAME_LENGTH]byte,
+ size: uint,
lock: u32,
protection: u32,
team: team_id,
@@ -31,11 +31,11 @@ area_info :: struct {
}
area_locking :: enum u32 {
- NO_LOCK = 0,
- LAZY_LOCK = 1,
- FULL_LOCK = 2,
- CONTIGUOUS = 3,
- LOMEM = 4, // CONTIGUOUS, < 16 MB physical address
+ NO_LOCK = 0,
+ LAZY_LOCK = 1,
+ FULL_LOCK = 2,
+ CONTIGUOUS = 3,
+ LOMEM = 4, // CONTIGUOUS, < 16 MB physical address
_32_BIT_FULL_LOCK = 5, // FULL_LOCK, < 4 GB physical addresses
_32_BIT_CONTIGUOUS = 6, // CONTIGUOUS, < 4 GB physical address
}
@@ -52,27 +52,29 @@ address_spec :: enum u32 {
RANDOMIZED_BASE_ADDRESS = 7,
}
-area_protection_flags :: enum u32 {
- READ_AREA = 1 << 0,
- WRITE_AREA = 1 << 1,
- EXECUTE_AREA = 1 << 2,
+area_protection_flag :: enum u32 {
+ READ_AREA = 0,
+ WRITE_AREA = 1,
+ EXECUTE_AREA = 2,
// "stack" protection is not available on most platforms - it's used
// to only commit memory as needed, and have guard pages at the
// bottom of the stack.
- STACK_AREA = 1 << 3,
- CLONEABLE_AREA = 1 << 8,
+ STACK_AREA = 3,
+ CLONEABLE_AREA = 8,
}
+area_protection_flags :: distinct bit_set[area_protection_flag; u32]
+@(default_calling_convention="c")
foreign libroot {
- create_area :: proc(name: cstring, startAddress: ^rawptr, addressSpec: address_spec, size: c.size_t, lock: area_locking, protection: area_protection_flags) -> area_id ---
+ create_area :: proc(name: cstring, startAddress: ^rawptr, addressSpec: address_spec, size: uint, lock: area_locking, protection: area_protection_flags) -> area_id ---
clone_area :: proc(name: cstring, destAddress: ^rawptr, addressSpec: address_spec, protection: area_protection_flags, source: area_id) -> area_id ---
find_area :: proc(name: cstring) -> area_id ---
area_for :: proc(address: rawptr) -> area_id ---
delete_area :: proc(id: area_id) -> status_t ---
- resize_area :: proc(id: area_id, newSize: c.size_t) -> status_t ---
+ resize_area :: proc(id: area_id, newSize: uint) -> status_t ---
set_area_protection :: proc(id: area_id, newProtection: area_protection_flags) -> status_t ---
- _get_area_info :: proc(id: area_id, areaInfo: ^area_info, size: c.size_t) -> status_t ---
- _get_next_area_info :: proc(team: team_id, cookie: ^c.ssize_t, areaInfo: ^area_info, size: c.size_t) -> status_t ---
+ _get_area_info :: proc(id: area_id, areaInfo: ^area_info, size: uint) -> status_t ---
+ _get_next_area_info :: proc(team: team_id, cookie: ^int, areaInfo: ^area_info, size: uint) -> status_t ---
}
// Ports
@@ -80,33 +82,35 @@ foreign libroot {
port_info :: struct {
port: port_id,
team: team_id,
- name: [OS_NAME_LENGTH]c.char,
+ name: [OS_NAME_LENGTH]byte,
capacity: i32, // queue depth
queue_count: i32, // # msgs waiting to be read
total_count: i32, // total # msgs read so far
}
-port_flags :: enum u32 {
- USE_USER_MEMCPY = 0x80000000,
+port_flag :: enum u32 {
+ USE_USER_MEMCPY = intrinsics.constant_log2(0x80000000),
// read the message, but don't remove it; kernel-only; memory must be locked
- PEEK_PORT_MESSAGE = 0x100,
+ PEEK_PORT_MESSAGE = intrinsics.constant_log2(0x100),
}
+port_flags :: distinct bit_set[port_flag; u32]
+@(default_calling_convention="c")
foreign libroot {
create_port :: proc(capacity: i32, name: cstring) -> port_id ---
find_port :: proc(name: cstring) -> port_id ---
- read_port :: proc(port: port_id, code: ^i32, buffer: rawptr, bufferSize: c.size_t) -> c.ssize_t ---
- read_port_etc :: proc(port: port_id, code: ^i32, buffer: rawptr, bufferSize: c.size_t, flags: port_flags, timeout: bigtime_t) -> c.ssize_t ---
- write_port :: proc(port: port_id, code: i32, buffer: rawptr, bufferSize: c.size_t) -> status_t ---
- write_port_etc :: proc(port: port_id, code: i32, buffer: rawptr, bufferSize: c.size_t, flags: port_flags, timeout: bigtime_t) -> status_t ---
+ read_port :: proc(port: port_id, code: ^i32, buffer: rawptr, bufferSize: uint) -> int ---
+ read_port_etc :: proc(port: port_id, code: ^i32, buffer: rawptr, bufferSize: uint, flags: port_flags, timeout: bigtime_t) -> int ---
+ write_port :: proc(port: port_id, code: i32, buffer: rawptr, bufferSize: uint) -> status_t ---
+ write_port_etc :: proc(port: port_id, code: i32, buffer: rawptr, bufferSize: uint, flags: port_flags, timeout: bigtime_t) -> status_t ---
close_port :: proc(port: port_id) -> status_t ---
delete_port :: proc(port: port_id) -> status_t ---
- port_buffer_size :: proc(port: port_id) -> c.ssize_t ---
- port_buffer_size_etc :: proc(port: port_id, flags: port_flags, timeout: bigtime_t) -> c.ssize_t ---
- port_count :: proc(port: port_id) -> c.ssize_t ---
+ port_buffer_size :: proc(port: port_id) -> int ---
+ port_buffer_size_etc :: proc(port: port_id, flags: port_flags, timeout: bigtime_t) -> int ---
+ port_count :: proc(port: port_id) -> int ---
set_port_owner :: proc(port: port_id, team: team_id) -> status_t ---
- _get_port_info :: proc(port: port_id, portInfo: ^port_info, portInfoSize: c.size_t) -> status_t ---
- _get_next_port_info :: proc(team: team_id, cookie: ^i32, portInfo: ^port_info, portInfoSize: c.size_t) -> status_t ---
+ _get_port_info :: proc(port: port_id, portInfo: ^port_info, portInfoSize: uint) -> status_t ---
+ _get_next_port_info :: proc(team: team_id, cookie: ^i32, portInfo: ^port_info, portInfoSize: uint) -> status_t ---
}
// Semaphores
@@ -114,22 +118,24 @@ foreign libroot {
sem_info :: struct {
sem: sem_id,
team: team_id,
- name: [OS_NAME_LENGTH]c.char,
+ name: [OS_NAME_LENGTH]byte,
count: i32,
latest_holder: thread_id,
}
-semaphore_flags :: enum u32 {
- CAN_INTERRUPT = 0x01, // acquisition of the semaphore can be interrupted (system use only)
- CHECK_PERMISSION = 0x04, // ownership will be checked (system use only)
- KILL_CAN_INTERRUPT = 0x20, // acquisition of the semaphore can be interrupted by SIGKILL[THR], even if not CAN_INTERRUPT (system use only)
+semaphore_flag :: enum u32 {
+ CAN_INTERRUPT = intrinsics.constant_log2(0x01), // acquisition of the semaphore can be interrupted (system use only)
+ CHECK_PERMISSION = intrinsics.constant_log2(0x04), // ownership will be checked (system use only)
+ KILL_CAN_INTERRUPT = intrinsics.constant_log2(0x20), // acquisition of the semaphore can be interrupted by SIGKILL[THR], even if not CAN_INTERRUPT (system use only)
// release_sem_etc() only flags
- DO_NOT_RESCHEDULE = 0x02, // thread is not rescheduled
- RELEASE_ALL = 0x08, // all waiting threads will be woken up, count will be zeroed
- RELEASE_IF_WAITING_ONLY = 0x10, // release count only if there are any threads waiting
+ DO_NOT_RESCHEDULE = intrinsics.constant_log2(0x02), // thread is not rescheduled
+ RELEASE_ALL = intrinsics.constant_log2(0x08), // all waiting threads will be woken up, count will be zeroed
+ RELEASE_IF_WAITING_ONLY = intrinsics.constant_log2(0x10), // release count only if there are any threads waiting
}
+semaphore_flags :: distinct bit_set[semaphore_flag; u32]
+@(default_calling_convention="c")
foreign libroot {
create_sem :: proc(count: i32, name: cstring) -> sem_id ---
delete_sem :: proc(id: sem_id) -> status_t ---
@@ -141,8 +147,8 @@ foreign libroot {
switch_sem_etc :: proc(semToBeReleased: sem_id, id: sem_id, count: i32, flags: semaphore_flags, timeout: bigtime_t) -> status_t ---
get_sem_count :: proc(id: sem_id, threadCount: ^i32) -> status_t ---
set_sem_owner :: proc(id: sem_id, team: team_id) -> status_t ---
- _get_sem_info :: proc(id: sem_id, info: ^sem_info, infoSize: c.size_t) -> status_t ---
- _get_next_sem_info :: proc(team: team_id, cookie: ^i32, info: ^sem_info, infoSize: c.size_t) -> status_t ---
+ _get_sem_info :: proc(id: sem_id, info: ^sem_info, infoSize: uint) -> status_t ---
+ _get_next_sem_info :: proc(team: team_id, cookie: ^i32, info: ^sem_info, infoSize: uint) -> status_t ---
}
// Teams
@@ -155,7 +161,7 @@ team_info :: struct {
debugger_nub_thread: thread_id,
debugger_nub_port: port_id,
argc: i32,
- args: [64]c.char,
+ args: [64]byte,
uid: uid_t,
gid: gid_t,
@@ -165,7 +171,7 @@ team_info :: struct {
group_id: pid_t,
session_id: pid_t,
parent: team_id,
- name: [OS_NAME_LENGTH]c.char,
+ name: [OS_NAME_LENGTH]byte,
start_time: bigtime_t,
}
@@ -183,17 +189,18 @@ team_usage_who :: enum i32 {
CHILDREN = -1,
}
+@(default_calling_convention="c")
foreign libroot {
// see also: send_signal()
kill_team :: proc(team: team_id) -> status_t ---
- _get_team_info :: proc(id: team_id, info: ^team_info, size: c.size_t) -> status_t ---
- _get_next_team_info :: proc(cookie: ^i32, info: ^team_info, size: c.size_t) -> status_t ---
- _get_team_usage_info :: proc(id: team_id, who: team_usage_who, info: ^team_usage_info, size: c.size_t) -> status_t ---
+ _get_team_info :: proc(id: team_id, info: ^team_info, size: uint) -> status_t ---
+ _get_next_team_info :: proc(cookie: ^i32, info: ^team_info, size: uint) -> status_t ---
+ _get_team_usage_info :: proc(id: team_id, who: team_usage_who, info: ^team_usage_info, size: uint) -> status_t ---
}
// Threads
-thread_state :: enum c.int {
+thread_state :: enum i32 {
RUNNING = 1,
READY,
RECEIVING,
@@ -205,7 +212,7 @@ thread_state :: enum c.int {
thread_info :: struct {
thread: thread_id,
team: team_id,
- name: [OS_NAME_LENGTH]c.char,
+ name: [OS_NAME_LENGTH]byte,
state: thread_state,
priority: thread_priority,
sem: sem_id,
@@ -234,6 +241,7 @@ SYSTEM_TIMEBASE :: 0
thread_func :: #type proc "c" (rawptr) -> status_t
+@(default_calling_convention="c")
foreign libroot {
spawn_thread :: proc(thread_func, name: cstring, priority: thread_priority, data: rawptr) -> thread_id ---
kill_thread :: proc(thread: thread_id) -> status_t ---
@@ -247,24 +255,25 @@ foreign libroot {
wait_for_thread_etc :: proc(id: thread_id, flags: u32, timeout: bigtime_t, _returnCode: ^status_t) -> status_t ---
on_exit_thread :: proc(callback: proc "c" (rawptr), data: rawptr) -> status_t ---
find_thread :: proc(name: cstring) -> thread_id ---
- send_data :: proc(thread: thread_id, code: i32, buffer: rawptr, bufferSize: c.size_t) -> status_t ---
- receive_data :: proc(sender: ^thread_id, buffer: rawptr, bufferSize: c.size_t) -> i32 ---
+ send_data :: proc(thread: thread_id, code: i32, buffer: rawptr, bufferSize: uint) -> status_t ---
+ receive_data :: proc(sender: ^thread_id, buffer: rawptr, bufferSize: uint) -> i32 ---
has_data :: proc(thread: thread_id) -> bool ---
snooze :: proc(amount: bigtime_t) -> status_t ---
// FIXME: Find and define those flags.
- snooze_etc :: proc(amount: bigtime_t, timeBase: c.int, flags: u32) -> status_t ---
- snooze_until :: proc(time: bigtime_t, timeBase: c.int) -> status_t ---
- _get_thread_info :: proc(id: thread_id, info: ^thread_info, size: c.size_t) -> status_t ---
- _get_next_thread_info :: proc(team: team_id, cookie: ^i32, info: ^thread_info, size: c.size_t) -> status_t ---
+ snooze_etc :: proc(amount: bigtime_t, timeBase: i32, flags: u32) -> status_t ---
+ snooze_until :: proc(time: bigtime_t, timeBase: i32) -> status_t ---
+ _get_thread_info :: proc(id: thread_id, info: ^thread_info, size: uint) -> status_t ---
+ _get_next_thread_info :: proc(team: team_id, cookie: ^i32, info: ^thread_info, size: uint) -> status_t ---
// bridge to the pthread API
get_pthread_thread_id :: proc(thread: pthread_t) -> thread_id ---
}
// Time
+@(default_calling_convention="c")
foreign libroot {
- real_time_clock :: proc() -> c.ulong ---
- set_real_time_clock :: proc(secsSinceJan1st1970: c.ulong) ---
+ real_time_clock :: proc() -> uint ---
+ set_real_time_clock :: proc(secsSinceJan1st1970: uint) ---
real_time_clock_usecs :: proc() -> bigtime_t ---
// time since booting in microseconds
system_time :: proc() -> bigtime_t ---
@@ -280,12 +289,14 @@ alarm_mode :: enum u32 {
PERIODIC_ALARM, // "when" specifies the period
}
+@(default_calling_convention="c")
foreign libroot {
set_alarm :: proc(_when: bigtime_t, mode: alarm_mode) -> bigtime_t ---
}
// Debugger
+@(default_calling_convention="c")
foreign libroot {
debugger :: proc(message: cstring) ---
/*
@@ -296,7 +307,7 @@ foreign libroot {
to re-enable the default debugger pass a zero.
*/
- disable_debugger :: proc(state: c.int) -> c.int ---
+ disable_debugger :: proc(state: i32) -> i32 ---
}
// System information
@@ -338,15 +349,15 @@ system_info :: struct {
max_teams: u32,
used_teams: u32,
- kernel_name: [FILE_NAME_LENGTH]c.char,
- kernel_build_date: [OS_NAME_LENGTH]c.char,
- kernel_build_time: [OS_NAME_LENGTH]c.char,
+ kernel_name: [FILE_NAME_LENGTH]byte,
+ kernel_build_date: [OS_NAME_LENGTH]byte,
+ kernel_build_time: [OS_NAME_LENGTH]byte,
kernel_version: i64,
abi: u32, // the system API
}
-topology_level_type :: enum c.int {
+topology_level_type :: enum i32 {
UNKNOWN,
ROOT,
SMT,
@@ -354,7 +365,7 @@ topology_level_type :: enum c.int {
PACKAGE,
}
-cpu_platform :: enum c.int {
+cpu_platform :: enum i32 {
UNKNOWN,
x86,
x86_64,
@@ -370,7 +381,7 @@ cpu_platform :: enum c.int {
RISC_V,
}
-cpu_vendor :: enum c.int {
+cpu_vendor :: enum i32 {
UNKNOWN,
AMD,
CYRIX,
@@ -408,95 +419,80 @@ cpu_topology_node_info :: struct {
},
}
-// FIXME: Add cpuid_info when bit fields are ready.
+when ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 {
+ cpuid_info :: struct #raw_union {
+ eax_0: struct {
+ max_eax: u32,
+ vendor_id: [12]byte,
+ },
+ eax_1: struct {
+ using _: bit_field u32 {
+ stepping: u32 | 4,
+ model: u32 | 4,
+ family: u32 | 4,
+ type: u32 | 2,
+ reserved_0: u32 | 2,
+ extended_model: u32 | 4,
+ extended_family: u32 | 8,
+ reserved_1: u32 | 4,
+ },
+
+ using _: bit_field u32 {
+ brand_index: u32 | 8,
+ clflush: u32 | 8,
+ logical_cpus: u32 | 8,
+ apic_id: u32 | 8,
+ },
+
+ features: u32,
+ extended_features: u32,
+ },
+
+ eax_2: struct {
+ call_num: u8,
+ cache_descriptors: [15]u8,
+ },
+
+ eax_3: struct {
+ reserved: [2]u32,
+ serial_number_high: u32,
+ serial_number_low: u32,
+ },
+
+ as_chars: [16]byte,
+
+ regs: struct {
+ eax: u32,
+ ebx: u32,
+ edx: u32,
+ ecx: u32,
+ },
+ }
+}
+
+@(default_calling_convention="c")
foreign libroot {
get_system_info :: proc(info: ^system_info) -> status_t ---
- _get_cpu_info_etc :: proc(firstCPU: u32, cpuCount: u32, info: ^cpu_info, size: c.size_t) -> status_t ---
+ _get_cpu_info_etc :: proc(firstCPU: u32, cpuCount: u32, info: ^cpu_info, size: uint) -> status_t ---
get_cpu_topology_info :: proc(topologyInfos: [^]cpu_topology_node_info, topologyInfoCount: ^u32) -> status_t ---
- is_computer_on :: proc() -> i32 ---
- is_computer_on_fire :: proc() -> f64 ---
+ when ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 {
+ get_cpuid :: proc(info: ^cpuid_info, eaxRegister: u32, cpuNum: u32) -> status_t ---
+ }
+
+ is_computer_on :: proc() -> i32 ---
+ is_computer_on_fire :: proc() -> f64 ---
}
-// Signal.h
-
-SIG_BLOCK :: 1
-SIG_UNBLOCK :: 2
-SIG_SETMASK :: 3
-
-/*
- * The list of all defined signals:
- *
- * The numbering of signals for Haiku attempts to maintain
- * some consistency with UN*X conventions so that things
- * like "kill -9" do what you expect.
- */
-
-SIGHUP :: 1 // hangup -- tty is gone!
-SIGINT :: 2 // interrupt
-SIGQUIT :: 3 // `quit' special character typed in tty
-SIGILL :: 4 // illegal instruction
-SIGCHLD :: 5 // child process exited
-SIGABRT :: 6 // abort() called, dont' catch
-SIGPIPE :: 7 // write to a pipe w/no readers
-SIGFPE :: 8 // floating point exception
-SIGKILL :: 9 // kill a team (not catchable)
-SIGSTOP :: 10 // suspend a thread (not catchable)
-SIGSEGV :: 11 // segmentation violation (read: invalid pointer)
-SIGCONT :: 12 // continue execution if suspended
-SIGTSTP :: 13 // `stop' special character typed in tty
-SIGALRM :: 14 // an alarm has gone off (see alarm())
-SIGTERM :: 15 // termination requested
-SIGTTIN :: 16 // read of tty from bg process
-SIGTTOU :: 17 // write to tty from bg process
-SIGUSR1 :: 18 // app defined signal 1
-SIGUSR2 :: 19 // app defined signal 2
-SIGWINCH :: 20 // tty window size changed
-SIGKILLTHR :: 21 // be specific: kill just the thread, not team
-SIGTRAP :: 22 // Trace/breakpoint trap
-SIGPOLL :: 23 // Pollable event
-SIGPROF :: 24 // Profiling timer expired
-SIGSYS :: 25 // Bad system call
-SIGURG :: 26 // High bandwidth data is available at socket
-SIGVTALRM :: 27 // Virtual timer expired
-SIGXCPU :: 28 // CPU time limit exceeded
-SIGXFSZ :: 29 // File size limit exceeded
-SIGBUS :: 30 // access to undefined portion of a memory object
-
-sigval :: struct #raw_union {
- sival_int: c.int,
- sival_ptr: rawptr,
-}
-
-siginfo_t :: struct {
- si_signo: c.int, // signal number
- si_code: c.int, // signal code
- si_errno: c.int, // if non zero, an error number associated with this signal
-
- si_pid: pid_t, // sending process ID
- si_uid: uid_t, // real user ID of sending process
- si_addr: rawptr, // address of faulting instruction
- si_status: c.int, // exit value or signal
- si_band: c.long, // band event for SIGPOLL
- si_value: sigval, // signal value
-}
+// POSIX signals
+@(default_calling_convention="c")
foreign libroot {
- // signal set (sigset_t) manipulation
- sigemptyset :: proc(set: ^sigset_t) -> c.int ---
- sigfillset :: proc(set: ^sigset_t) -> c.int ---
- sigaddset :: proc(set: ^sigset_t, _signal: c.int) -> c.int ---
- sigdelset :: proc(set: ^sigset_t, _signal: c.int) -> c.int ---
- sigismember :: proc(set: ^sigset_t, _signal: c.int) -> c.int ---
- // querying and waiting for signals
- sigpending :: proc(set: ^sigset_t) -> c.int ---
- sigsuspend :: proc(mask: ^sigset_t) -> c.int ---
- sigpause :: proc(_signal: c.int) -> c.int ---
- sigwait :: proc(set: ^sigset_t, _signal: ^c.int) -> c.int ---
- sigwaitinfo :: proc(set: ^sigset_t, info: ^siginfo_t) -> c.int ---
- sigtimedwait :: proc(set: ^sigset_t, info: ^siginfo_t, timeout: ^unix.timespec) -> c.int ---
+ /*
+ Wait for queued signals.
- send_signal :: proc(threadID: thread_id, signal: c.uint) -> c.int ---
- set_signal_stack :: proc(base: rawptr, size: c.size_t) ---
+ [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigtimedwait.html ]]
+ */
+ sigtimedwait :: proc(set: ^posix.sigset_t, info: ^posix.siginfo_t, timeout: ^posix.timespec) -> posix.result ---
}
diff --git a/core/sys/haiku/types.odin b/core/sys/haiku/types.odin
index 47755b0b7..f15ffb00d 100644
--- a/core/sys/haiku/types.odin
+++ b/core/sys/haiku/types.odin
@@ -1,9 +1,7 @@
#+build haiku
package sys_haiku
-import "core:c"
-
-status_t :: i32
+status_t :: Errno
bigtime_t :: i64
nanotime_t :: i64
type_code :: u32
@@ -37,16 +35,20 @@ mode_t :: u32
umode_t :: u32
nlink_t :: i32
-caddr_t :: ^c.char
+caddr_t :: [^]byte
addr_t :: phys_addr_t
key_t :: i32
clockid_t :: i32
-time_t :: i64 when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 else i32
+time_t :: int
+timespec :: struct {
+ tv_sec: time_t,
+ tv_nsec: int,
+}
-sig_atomic_t :: c.int
+sig_atomic_t :: i32
sigset_t :: u64
image_id :: i32
diff --git a/core/sys/info/cpu_intel.odin b/core/sys/info/cpu_intel.odin
index 95b53dda0..c8b8282fe 100644
--- a/core/sys/info/cpu_intel.odin
+++ b/core/sys/info/cpu_intel.odin
@@ -23,6 +23,7 @@ CPU_Feature :: enum u64 {
popcnt, // Hamming weight instruction POPCNT.
rdrand, // RDRAND instruction (on-chip random number generator)
rdseed, // RDSEED instruction (on-chip random number generator)
+ sha, // SHA Extensions (SHA-1, SHA-224, SHA-256)
sse2, // Streaming SIMD extension 2 (always available on amd64)
sse3, // Streaming SIMD extension 3
ssse3, // Supplemental streaming SIMD extension 3
@@ -115,6 +116,7 @@ init_cpu_features :: proc "c" () {
_, ebx7, ecx7, edx7 := cpuid(7, 0)
try_set(&set, .bmi1, 3, ebx7)
+ try_set(&set, .sha, 29, ebx7)
if os_supports_avx {
try_set(&set, .avx2, 5, ebx7)
}
diff --git a/core/sys/info/doc.odin b/core/sys/info/doc.odin
index b5cd62d81..2fd34b864 100644
--- a/core/sys/info/doc.odin
+++ b/core/sys/info/doc.odin
@@ -4,7 +4,7 @@ Made available under Odin's BSD-3 license.
List of contributors:
Jeroen van Rijn: Initial implementation.
- Laytan: ARM and RISC-V CPU feature detection.
+ Laytan: ARM and RISC-V CPU feature detection, iOS/macOS platform overhaul.
*/
/*
diff --git a/core/sys/info/platform_darwin.odin b/core/sys/info/platform_darwin.odin
index 97a2199ab..7dc49bcd1 100644
--- a/core/sys/info/platform_darwin.odin
+++ b/core/sys/info/platform_darwin.odin
@@ -1,596 +1,101 @@
package sysinfo
-import sys "core:sys/unix"
-import "core:strconv"
-import "core:strings"
-import "base:runtime"
+import "core:strconv"
+import "core:strings"
+import "core:sys/unix"
+import NS "core:sys/darwin/Foundation"
@(private)
version_string_buf: [1024]u8
@(init, private)
-init_os_version :: proc () {
- os_version.platform = .MacOS
+init_platform :: proc() {
+ ws :: strings.write_string
+ wi :: strings.write_int
- // Start building display version
b := strings.builder_from_bytes(version_string_buf[:])
- mib := []i32{sys.CTL_KERN, sys.KERN_OSVERSION}
- build_buf: [12]u8
+ version: NS.OperatingSystemVersion
+ {
+ NS.scoped_autoreleasepool()
- ok := sys.sysctl(mib, &build_buf)
- if !ok {
- strings.write_string(&b, "macOS Unknown")
- os_version.as_string = strings.to_string(b)
- return
+ info := NS.ProcessInfo.processInfo()
+ version = info->operatingSystemVersion()
+ mem := info->physicalMemory()
+
+ ram.total_ram = int(mem)
}
- build := string(cstring(&build_buf[0]))
+ macos_version = {int(version.majorVersion), int(version.minorVersion), int(version.patchVersion)}
- // Do we have an exact match?
- match: Darwin_Match
- rel, exact := macos_release_map[build]
-
- if exact {
- match = .Exact
+ when ODIN_PLATFORM_SUBTARGET == .iOS {
+ os_version.platform = .iOS
+ ws(&b, "iOS")
} else {
+ os_version.platform = .MacOS
+ switch version.majorVersion {
+ case 15: ws(&b, "macOS Sequoia")
+ case 14: ws(&b, "macOS Sonoma")
+ case 13: ws(&b, "macOS Ventura")
+ case 12: ws(&b, "macOS Monterey")
+ case 11: ws(&b, "macOS Big Sur")
+ case 10:
+ switch version.minorVersion {
+ case 15: ws(&b, "macOS Catalina")
+ case 14: ws(&b, "macOS Mojave")
+ case 13: ws(&b, "macOS High Sierra")
+ case 12: ws(&b, "macOS Sierra")
+ case 11: ws(&b, "OS X El Capitan")
+ case 10: ws(&b, "OS X Yosemite")
+ case:
+ // `ProcessInfo.operatingSystemVersion` is 10.10 and up.
+ unreachable()
+ }
+ case:
+ // New version not yet added here.
+ assert(version.majorVersion > 15)
+ ws(&b, "macOS Unknown")
+ }
+ }
+
+ ws(&b, " ")
+ wi(&b, int(version.majorVersion))
+ ws(&b, ".")
+ wi(&b, int(version.minorVersion))
+ ws(&b, ".")
+ wi(&b, int(version.patchVersion))
+
+ {
+ build_buf: [12]u8
+ mib := []i32{unix.CTL_KERN, unix.KERN_OSVERSION}
+ ok := unix.sysctl(mib, &build_buf)
+ build := string(cstring(raw_data(build_buf[:]))) if ok else "Unknown"
+
+ ws(&b, " (build ")
+
+ build_start := len(b.buf)
+ ws(&b, build)
+ os_version.version = string(b.buf[build_start:][:len(build)])
+ }
+
+ {
// Match on XNU kernel version
- mib = []i32{sys.CTL_KERN, sys.KERN_OSRELEASE}
version_bits: [12]u8 // enough for 999.999.999\x00
- have_kernel_version := sys.sysctl(mib, &version_bits)
+ mib := []i32{unix.CTL_KERN, unix.KERN_OSRELEASE}
+ ok := unix.sysctl(mib, &version_bits)
+ kernel := string(cstring(raw_data(version_bits[:]))) if ok else "Unknown"
- major_ok, minor_ok, patch_ok: bool
+ major, _, tail := strings.partition(kernel, ".")
+ minor, _, patch := strings.partition(tail, ".")
- tmp := runtime.default_temp_allocator_temp_begin()
+ os_version.major, _ = strconv.parse_int(major, 10)
+ os_version.minor, _ = strconv.parse_int(minor, 10)
+ os_version.patch, _ = strconv.parse_int(patch, 10)
- triplet := strings.split(string(cstring(&version_bits[0])), ".", context.temp_allocator)
- if len(triplet) != 3 {
- have_kernel_version = false
- } else {
- rel.darwin.x, major_ok = strconv.parse_int(triplet[0])
- rel.darwin.y, minor_ok = strconv.parse_int(triplet[1])
- rel.darwin.z, patch_ok = strconv.parse_int(triplet[2])
-
- if !(major_ok && minor_ok && patch_ok) {
- have_kernel_version = false
- }
- }
-
- runtime.default_temp_allocator_temp_end(tmp)
-
- if !have_kernel_version {
- // We don't know the kernel version, but we do know the build
- strings.write_string(&b, "macOS Unknown (build ")
- l := strings.builder_len(b)
- strings.write_string(&b, build)
- os_version.version = strings.to_string(b)[l:]
- strings.write_rune(&b, ')')
- os_version.as_string = strings.to_string(b)
- return
- }
- rel, match = map_darwin_kernel_version_to_macos_release(build, rel.darwin)
+ ws(&b, ", kernel ")
+ ws(&b, kernel)
+ ws(&b, ")")
}
- os_version.major = rel.darwin.x
- os_version.minor = rel.darwin.y
- os_version.patch = rel.darwin.z
-
- macos_version = transmute(Version)rel.release.version
-
- strings.write_string(&b, rel.os_name)
- if match == .Exact || match == .Nearest {
- strings.write_rune(&b, ' ')
- strings.write_string(&b, rel.release.name)
- strings.write_rune(&b, ' ')
- strings.write_int(&b, rel.release.version.x)
- if rel.release.version.y > 0 || rel.release.version.z > 0 {
- strings.write_rune(&b, '.')
- strings.write_int(&b, rel.release.version.y)
- }
- if rel.release.version.z > 0 {
- strings.write_rune(&b, '.')
- strings.write_int(&b, rel.release.version.z)
- }
- if match == .Nearest {
- strings.write_rune(&b, '?')
- }
- } else {
- strings.write_string(&b, " Unknown")
- }
-
- strings.write_string(&b, " (build ")
- l := strings.builder_len(b)
- strings.write_string(&b, build)
- os_version.version = strings.to_string(b)[l:]
-
- strings.write_string(&b, ", kernel ")
- strings.write_int(&b, rel.darwin.x)
- strings.write_rune(&b, '.')
- strings.write_int(&b, rel.darwin.y)
- strings.write_rune(&b, '.')
- strings.write_int(&b, rel.darwin.z)
- strings.write_rune(&b, ')')
-
- os_version.as_string = strings.to_string(b)
-}
-
-@(init, private)
-init_ram :: proc() {
- // Retrieve RAM info using `sysctl`
-
- mib := []i32{sys.CTL_HW, sys.HW_MEMSIZE}
- mem_size: u64
- if sys.sysctl(mib, &mem_size) {
- ram.total_ram = int(mem_size)
- }
-}
-
-@(private)
-Darwin_To_Release :: struct {
- darwin: [3]int, // Darwin kernel triplet
- os_name: string, // OS X, MacOS
- release: struct {
- name: string, // Monterey, Mojave, etc.
- version: [3]int, // 12.4, etc.
- },
-}
-
-// Important: Order from lowest to highest kernel version
-@(private)
-macos_release_map: map[string]Darwin_To_Release = {
- // MacOS Tiger
- "8A428" = {{8, 0, 0}, "macOS", {"Tiger", {10, 4, 0}}},
- "8A432" = {{8, 0, 0}, "macOS", {"Tiger", {10, 4, 0}}},
- "8B15" = {{8, 1, 0}, "macOS", {"Tiger", {10, 4, 1}}},
- "8B17" = {{8, 1, 0}, "macOS", {"Tiger", {10, 4, 1}}},
- "8C46" = {{8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}},
- "8C47" = {{8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}},
- "8E102" = {{8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}},
- "8E45" = {{8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}},
- "8E90" = {{8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}},
- "8F46" = {{8, 3, 0}, "macOS", {"Tiger", {10, 4, 3}}},
- "8G32" = {{8, 4, 0}, "macOS", {"Tiger", {10, 4, 4}}},
- "8G1165" = {{8, 4, 0}, "macOS", {"Tiger", {10, 4, 4}}},
- "8H14" = {{8, 5, 0}, "macOS", {"Tiger", {10, 4, 5}}},
- "8G1454" = {{8, 5, 0}, "macOS", {"Tiger", {10, 4, 5}}},
- "8I127" = {{8, 6, 0}, "macOS", {"Tiger", {10, 4, 6}}},
- "8I1119" = {{8, 6, 0}, "macOS", {"Tiger", {10, 4, 6}}},
- "8J135" = {{8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}},
- "8J2135a" = {{8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}},
- "8K1079" = {{8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}},
- "8N5107" = {{8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}},
- "8L127" = {{8, 8, 0}, "macOS", {"Tiger", {10, 4, 8}}},
- "8L2127" = {{8, 8, 0}, "macOS", {"Tiger", {10, 4, 8}}},
- "8P135" = {{8, 9, 0}, "macOS", {"Tiger", {10, 4, 9}}},
- "8P2137" = {{8, 9, 0}, "macOS", {"Tiger", {10, 4, 9}}},
- "8R218" = {{8, 10, 0}, "macOS", {"Tiger", {10, 4, 10}}},
- "8R2218" = {{8, 10, 0}, "macOS", {"Tiger", {10, 4, 10}}},
- "8R2232" = {{8, 10, 0}, "macOS", {"Tiger", {10, 4, 10}}},
- "8S165" = {{8, 11, 0}, "macOS", {"Tiger", {10, 4, 11}}},
- "8S2167" = {{8, 11, 0}, "macOS", {"Tiger", {10, 4, 11}}},
-
- // MacOS Leopard
- "9A581" = {{9, 0, 0}, "macOS", {"Leopard", {10, 5, 0}}},
- "9B18" = {{9, 1, 0}, "macOS", {"Leopard", {10, 5, 1}}},
- "9B2117" = {{9, 1, 1}, "macOS", {"Leopard", {10, 5, 1}}},
- "9C31" = {{9, 2, 0}, "macOS", {"Leopard", {10, 5, 2}}},
- "9C7010" = {{9, 2, 0}, "macOS", {"Leopard", {10, 5, 2}}},
- "9D34" = {{9, 3, 0}, "macOS", {"Leopard", {10, 5, 3}}},
- "9E17" = {{9, 4, 0}, "macOS", {"Leopard", {10, 5, 4}}},
- "9F33" = {{9, 5, 0}, "macOS", {"Leopard", {10, 5, 5}}},
- "9G55" = {{9, 6, 0}, "macOS", {"Leopard", {10, 5, 6}}},
- "9G66" = {{9, 6, 0}, "macOS", {"Leopard", {10, 5, 6}}},
- "9G71" = {{9, 6, 0}, "macOS", {"Leopard", {10, 5, 6}}},
- "9J61" = {{9, 7, 0}, "macOS", {"Leopard", {10, 5, 7}}},
- "9L30" = {{9, 8, 0}, "macOS", {"Leopard", {10, 5, 8}}},
- "9L34" = {{9, 8, 0}, "macOS", {"Leopard", {10, 5, 8}}},
-
- // MacOS Snow Leopard
- "10A432" = {{10, 0, 0}, "macOS", {"Snow Leopard", {10, 6, 0}}},
- "10A433" = {{10, 0, 0}, "macOS", {"Snow Leopard", {10, 6, 0}}},
- "10B504" = {{10, 1, 0}, "macOS", {"Snow Leopard", {10, 6, 1}}},
- "10C540" = {{10, 2, 0}, "macOS", {"Snow Leopard", {10, 6, 2}}},
- "10D573" = {{10, 3, 0}, "macOS", {"Snow Leopard", {10, 6, 3}}},
- "10D575" = {{10, 3, 0}, "macOS", {"Snow Leopard", {10, 6, 3}}},
- "10D578" = {{10, 3, 0}, "macOS", {"Snow Leopard", {10, 6, 3}}},
- "10F569" = {{10, 4, 0}, "macOS", {"Snow Leopard", {10, 6, 4}}},
- "10H574" = {{10, 5, 0}, "macOS", {"Snow Leopard", {10, 6, 5}}},
- "10J567" = {{10, 6, 0}, "macOS", {"Snow Leopard", {10, 6, 6}}},
- "10J869" = {{10, 7, 0}, "macOS", {"Snow Leopard", {10, 6, 7}}},
- "10J3250" = {{10, 7, 0}, "macOS", {"Snow Leopard", {10, 6, 7}}},
- "10J4138" = {{10, 7, 0}, "macOS", {"Snow Leopard", {10, 6, 7}}},
- "10K540" = {{10, 8, 0}, "macOS", {"Snow Leopard", {10, 6, 8}}},
- "10K549" = {{10, 8, 0}, "macOS", {"Snow Leopard", {10, 6, 8}}},
-
- // MacOS Lion
- "11A511" = {{11, 0, 0}, "macOS", {"Lion", {10, 7, 0}}},
- "11A511s" = {{11, 0, 0}, "macOS", {"Lion", {10, 7, 0}}},
- "11A2061" = {{11, 0, 2}, "macOS", {"Lion", {10, 7, 0}}},
- "11A2063" = {{11, 0, 2}, "macOS", {"Lion", {10, 7, 0}}},
- "11B26" = {{11, 1, 0}, "macOS", {"Lion", {10, 7, 1}}},
- "11B2118" = {{11, 1, 0}, "macOS", {"Lion", {10, 7, 1}}},
- "11C74" = {{11, 2, 0}, "macOS", {"Lion", {10, 7, 2}}},
- "11D50" = {{11, 3, 0}, "macOS", {"Lion", {10, 7, 3}}},
- "11E53" = {{11, 4, 0}, "macOS", {"Lion", {10, 7, 4}}},
- "11G56" = {{11, 4, 2}, "macOS", {"Lion", {10, 7, 5}}},
- "11G63" = {{11, 4, 2}, "macOS", {"Lion", {10, 7, 5}}},
-
- // MacOS Mountain Lion
- "12A269" = {{12, 0, 0}, "macOS", {"Mountain Lion", {10, 8, 0}}},
- "12B19" = {{12, 1, 0}, "macOS", {"Mountain Lion", {10, 8, 1}}},
- "12C54" = {{12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}},
- "12C60" = {{12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}},
- "12C2034" = {{12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}},
- "12C3104" = {{12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}},
- "12D78" = {{12, 3, 0}, "macOS", {"Mountain Lion", {10, 8, 3}}},
- "12E55" = {{12, 4, 0}, "macOS", {"Mountain Lion", {10, 8, 4}}},
- "12E3067" = {{12, 4, 0}, "macOS", {"Mountain Lion", {10, 8, 4}}},
- "12E4022" = {{12, 4, 0}, "macOS", {"Mountain Lion", {10, 8, 4}}},
- "12F37" = {{12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
- "12F45" = {{12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
- "12F2501" = {{12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
- "12F2518" = {{12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
- "12F2542" = {{12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
- "12F2560" = {{12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
-
- // MacOS Mavericks
- "13A603" = {{13, 0, 0}, "macOS", {"Mavericks", {10, 9, 0}}},
- "13B42" = {{13, 0, 0}, "macOS", {"Mavericks", {10, 9, 1}}},
- "13C64" = {{13, 1, 0}, "macOS", {"Mavericks", {10, 9, 2}}},
- "13C1021" = {{13, 1, 0}, "macOS", {"Mavericks", {10, 9, 2}}},
- "13D65" = {{13, 2, 0}, "macOS", {"Mavericks", {10, 9, 3}}},
- "13E28" = {{13, 3, 0}, "macOS", {"Mavericks", {10, 9, 4}}},
- "13F34" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- "13F1066" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- "13F1077" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- "13F1096" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- "13F1112" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- "13F1134" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- "13F1507" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- "13F1603" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- "13F1712" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- "13F1808" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- "13F1911" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
-
- // MacOS Yosemite
- "14A389" = {{14, 0, 0}, "macOS", {"Yosemite", {10, 10, 0}}},
- "14B25" = {{14, 0, 0}, "macOS", {"Yosemite", {10, 10, 1}}},
- "14C109" = {{14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}},
- "14C1510" = {{14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}},
- "14C2043" = {{14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}},
- "14C1514" = {{14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}},
- "14C2513" = {{14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}},
- "14D131" = {{14, 3, 0}, "macOS", {"Yosemite", {10, 10, 3}}},
- "14D136" = {{14, 3, 0}, "macOS", {"Yosemite", {10, 10, 3}}},
- "14E46" = {{14, 4, 0}, "macOS", {"Yosemite", {10, 10, 4}}},
- "14F27" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- "14F1021" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- "14F1505" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- "14F1509" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- "14F1605" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- "14F1713" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- "14F1808" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- "14F1909" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- "14F1912" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- "14F2009" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- "14F2109" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- "14F2315" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- "14F2411" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- "14F2511" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
-
- // MacOS El Capitan
- "15A284" = {{15, 0, 0}, "macOS", {"El Capitan", {10, 11, 0}}},
- "15B42" = {{15, 0, 0}, "macOS", {"El Capitan", {10, 11, 1}}},
- "15C50" = {{15, 2, 0}, "macOS", {"El Capitan", {10, 11, 2}}},
- "15D21" = {{15, 3, 0}, "macOS", {"El Capitan", {10, 11, 3}}},
- "15E65" = {{15, 4, 0}, "macOS", {"El Capitan", {10, 11, 4}}},
- "15F34" = {{15, 5, 0}, "macOS", {"El Capitan", {10, 11, 5}}},
- "15G31" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- "15G1004" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- "15G1011" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- "15G1108" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- "15G1212" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- "15G1217" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- "15G1421" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- "15G1510" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- "15G1611" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- "15G17023" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- "15G18013" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- "15G19009" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- "15G20015" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- "15G21013" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- "15G22010" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
-
- // MacOS Sierra
- "16A323" = {{16, 0, 0}, "macOS", {"Sierra", {10, 12, 0}}},
- "16B2555" = {{16, 1, 0}, "macOS", {"Sierra", {10, 12, 1}}},
- "16B2657" = {{16, 1, 0}, "macOS", {"Sierra", {10, 12, 1}}},
- "16C67" = {{16, 3, 0}, "macOS", {"Sierra", {10, 12, 2}}},
- "16C68" = {{16, 3, 0}, "macOS", {"Sierra", {10, 12, 2}}},
- "16D32" = {{16, 4, 0}, "macOS", {"Sierra", {10, 12, 3}}},
- "16E195" = {{16, 5, 0}, "macOS", {"Sierra", {10, 12, 4}}},
- "16F73" = {{16, 6, 0}, "macOS", {"Sierra", {10, 12, 5}}},
- "16F2073" = {{16, 6, 0}, "macOS", {"Sierra", {10, 12, 5}}},
- "16G29" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- "16G1036" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- "16G1114" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- "16G1212" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- "16G1314" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- "16G1408" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- "16G1510" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- "16G1618" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- "16G1710" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- "16G1815" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- "16G1917" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- "16G1918" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- "16G2016" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- "16G2127" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- "16G2128" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- "16G2136" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
-
- // MacOS High Sierra
- "17A365" = {{17, 0, 0}, "macOS", {"High Sierra", {10, 13, 0}}},
- "17A405" = {{17, 0, 0}, "macOS", {"High Sierra", {10, 13, 0}}},
- "17B48" = {{17, 2, 0}, "macOS", {"High Sierra", {10, 13, 1}}},
- "17B1002" = {{17, 2, 0}, "macOS", {"High Sierra", {10, 13, 1}}},
- "17B1003" = {{17, 2, 0}, "macOS", {"High Sierra", {10, 13, 1}}},
- "17C88" = {{17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}},
- "17C89" = {{17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}},
- "17C205" = {{17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}},
- "17C2205" = {{17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}},
- "17D47" = {{17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}},
- "17D2047" = {{17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}},
- "17D102" = {{17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}},
- "17D2102" = {{17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}},
- "17E199" = {{17, 5, 0}, "macOS", {"High Sierra", {10, 13, 4}}},
- "17E202" = {{17, 5, 0}, "macOS", {"High Sierra", {10, 13, 4}}},
- "17F77" = {{17, 6, 0}, "macOS", {"High Sierra", {10, 13, 5}}},
- "17G65" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- "17G2208" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- "17G2307" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- "17G3025" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- "17G4015" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- "17G5019" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- "17G6029" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- "17G6030" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- "17G7024" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- "17G8029" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- "17G8030" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- "17G8037" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- "17G9016" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- "17G10021" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- "17G11023" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- "17G12034" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- "17G13033" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- "17G13035" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- "17G14019" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- "17G14033" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- "17G14042" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
-
- // MacOS Mojave
- "18A391" = {{18, 0, 0}, "macOS", {"Mojave", {10, 14, 0}}},
- "18B75" = {{18, 2, 0}, "macOS", {"Mojave", {10, 14, 1}}},
- "18B2107" = {{18, 2, 0}, "macOS", {"Mojave", {10, 14, 1}}},
- "18B3094" = {{18, 2, 0}, "macOS", {"Mojave", {10, 14, 1}}},
- "18C54" = {{18, 2, 0}, "macOS", {"Mojave", {10, 14, 2}}},
- "18D42" = {{18, 2, 0}, "macOS", {"Mojave", {10, 14, 3}}},
- "18D43" = {{18, 2, 0}, "macOS", {"Mojave", {10, 14, 3}}},
- "18D109" = {{18, 2, 0}, "macOS", {"Mojave", {10, 14, 3}}},
- "18E226" = {{18, 5, 0}, "macOS", {"Mojave", {10, 14, 4}}},
- "18E227" = {{18, 5, 0}, "macOS", {"Mojave", {10, 14, 4}}},
- "18F132" = {{18, 6, 0}, "macOS", {"Mojave", {10, 14, 5}}},
- "18G84" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- "18G87" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- "18G95" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- "18G103" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- "18G1012" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- "18G2022" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- "18G3020" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- "18G4032" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- "18G5033" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- "18G6020" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- "18G6032" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- "18G6042" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- "18G7016" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- "18G8012" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- "18G8022" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- "18G9028" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- "18G9216" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- "18G9323" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
-
- // MacOS Catalina
- "19A583" = {{19, 0, 0}, "macOS", {"Catalina", {10, 15, 0}}},
- "19A602" = {{19, 0, 0}, "macOS", {"Catalina", {10, 15, 0}}},
- "19A603" = {{19, 0, 0}, "macOS", {"Catalina", {10, 15, 0}}},
- "19B88" = {{19, 0, 0}, "macOS", {"Catalina", {10, 15, 1}}},
- "19C57" = {{19, 2, 0}, "macOS", {"Catalina", {10, 15, 2}}},
- "19C58" = {{19, 2, 0}, "macOS", {"Catalina", {10, 15, 2}}},
- "19D76" = {{19, 3, 0}, "macOS", {"Catalina", {10, 15, 3}}},
- "19E266" = {{19, 4, 0}, "macOS", {"Catalina", {10, 15, 4}}},
- "19E287" = {{19, 4, 0}, "macOS", {"Catalina", {10, 15, 4}}},
- "19F96" = {{19, 5, 0}, "macOS", {"Catalina", {10, 15, 5}}},
- "19F101" = {{19, 5, 0}, "macOS", {"Catalina", {10, 15, 5}}},
- "19G73" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 6}}},
- "19G2021" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 6}}},
- "19H2" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- "19H4" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- "19H15" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- "19H114" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- "19H512" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- "19H524" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- "19H1030" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- "19H1217" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- "19H1323" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- "19H1417" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- "19H1419" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- "19H1519" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- "19H1615" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- "19H1713" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- "19H1715" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- "19H1824" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- "19H1922" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- "19H2026" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
-
- // MacOS Big Sur
- "20A2411" = {{20, 1, 0}, "macOS", {"Big Sur", {11, 0, 0}}},
- "20B29" = {{20, 1, 0}, "macOS", {"Big Sur", {11, 0, 1}}},
- "20B50" = {{20, 1, 0}, "macOS", {"Big Sur", {11, 0, 1}}},
- "20C69" = {{20, 2, 0}, "macOS", {"Big Sur", {11, 1, 0}}},
- "20D64" = {{20, 3, 0}, "macOS", {"Big Sur", {11, 2, 0}}},
- "20D74" = {{20, 3, 0}, "macOS", {"Big Sur", {11, 2, 1}}},
- "20D75" = {{20, 3, 0}, "macOS", {"Big Sur", {11, 2, 1}}},
- "20D80" = {{20, 3, 0}, "macOS", {"Big Sur", {11, 2, 2}}},
- "20D91" = {{20, 3, 0}, "macOS", {"Big Sur", {11, 2, 3}}},
- "20E232" = {{20, 4, 0}, "macOS", {"Big Sur", {11, 3, 0}}},
- "20E241" = {{20, 4, 0}, "macOS", {"Big Sur", {11, 3, 1}}},
- "20F71" = {{20, 5, 0}, "macOS", {"Big Sur", {11, 4, 0}}},
- "20G71" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 5, 0}}},
- "20G80" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 5, 1}}},
- "20G95" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 5, 2}}},
- "20G165" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 6, 0}}},
- "20G224" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 6, 1}}},
- "20G314" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 6, 2}}},
- "20G415" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 6, 3}}},
- "20G417" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 6, 4}}},
- "20G527" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 6, 5}}},
- "20G624" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 6, 6}}},
- "20G630" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 6, 7}}},
- "20G730" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 6, 8}}},
- "20G817" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 7, 0}}},
- "20G918" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 7, 1}}},
- "20G1020" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 7, 2}}},
- "20G1116" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 7, 3}}},
- "20G1120" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 7, 4}}},
- "20G1225" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 7, 5}}},
- "20G1231" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 7, 6}}},
- "20G1345" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 7, 7}}},
- "20G1351" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 7, 8}}},
- "20G1426" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 7, 9}}},
- "20G1427" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 7, 10}}},
-
- // MacOS Monterey
- "21A344" = {{21, 0, 1}, "macOS", {"Monterey", {12, 0, 0}}},
- "21A559" = {{21, 1, 0}, "macOS", {"Monterey", {12, 0, 1}}},
- "21C52" = {{21, 2, 0}, "macOS", {"Monterey", {12, 1, 0}}},
- "21D49" = {{21, 3, 0}, "macOS", {"Monterey", {12, 2, 0}}},
- "21D62" = {{21, 3, 0}, "macOS", {"Monterey", {12, 2, 1}}},
- "21E230" = {{21, 4, 0}, "macOS", {"Monterey", {12, 3, 0}}},
- "21E258" = {{21, 4, 0}, "macOS", {"Monterey", {12, 3, 1}}},
- "21F79" = {{21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}},
- "21F2081" = {{21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}},
- "21F2092" = {{21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}},
- "21G72" = {{21, 6, 0}, "macOS", {"Monterey", {12, 5, 0}}},
- "21G83" = {{21, 6, 0}, "macOS", {"Monterey", {12, 5, 1}}},
- "21G115" = {{21, 6, 0}, "macOS", {"Monterey", {12, 6, 0}}},
- "21G217" = {{21, 6, 0}, "macOS", {"Monterey", {12, 6, 1}}},
- "21G320" = {{21, 6, 0}, "macOS", {"Monterey", {12, 6, 2}}},
- "21G419" = {{21, 6, 0}, "macOS", {"Monterey", {12, 6, 3}}},
- "21G526" = {{21, 6, 0}, "macOS", {"Monterey", {12, 6, 4}}},
- "21G531" = {{21, 6, 0}, "macOS", {"Monterey", {12, 6, 5}}},
- "21G646" = {{21, 6, 0}, "macOS", {"Monterey", {12, 6, 6}}},
- "21G651" = {{21, 6, 0}, "macOS", {"Monterey", {12, 6, 7}}},
- "21G725" = {{21, 6, 0}, "macOS", {"Monterey", {12, 6, 8}}},
- "21G726" = {{21, 6, 0}, "macOS", {"Monterey", {12, 6, 9}}},
- "21G816" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 0}}},
- "21G920" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 1}}},
- "21G1974" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 2}}},
- "21H1015" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 3}}},
- "21H1123" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 4}}},
- "21H1222" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 5}}},
- "21H1320" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 6}}},
-
- // MacOS Ventura
- "22A380" = {{22, 1, 0}, "macOS", {"Ventura", {13, 0, 0}}},
- "22A400" = {{22, 1, 0}, "macOS", {"Ventura", {13, 0, 1}}},
- "22C65" = {{22, 2, 0}, "macOS", {"Ventura", {13, 1, 0}}},
- "22D49" = {{22, 3, 0}, "macOS", {"Ventura", {13, 2, 0}}},
- "22D68" = {{22, 3, 0}, "macOS", {"Ventura", {13, 2, 1}}},
- "22E252" = {{22, 4, 0}, "macOS", {"Ventura", {13, 3, 0}}},
- "22E261" = {{22, 4, 0}, "macOS", {"Ventura", {13, 3, 1}}},
- "22F66" = {{22, 5, 0}, "macOS", {"Ventura", {13, 4, 0}}},
- "22F82" = {{22, 5, 0}, "macOS", {"Ventura", {13, 4, 1}}},
- "22E772610a" = {{22, 5, 0}, "macOS", {"Ventura", {13, 4, 1}}},
- "22F770820d" = {{22, 5, 0}, "macOS", {"Ventura", {13, 4, 1}}},
- "22G74" = {{22, 6, 0}, "macOS", {"Ventura", {13, 5, 0}}},
- "22G90" = {{22, 6, 0}, "macOS", {"Ventura", {13, 5, 1}}},
- "22G91" = {{22, 6, 0}, "macOS", {"Ventura", {13, 5, 2}}},
- "22G120" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 0}}},
- "22G313" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 1}}},
- "22G320" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 2}}},
- "22G436" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 3}}},
- "22G513" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 4}}},
- "22G621" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 5}}},
- "22G630" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 6}}},
- "22G720" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 7}}},
- "22G820" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 8}}},
- "22G830" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 9}}},
- "22H123" = {{22, 6, 0}, "macOS", {"Ventura", {13, 7, 0}}},
- "22H221" = {{22, 6, 0}, "macOS", {"Ventura", {13, 7, 1}}},
-
- // MacOS Sonoma
- "23A344" = {{23, 0, 0}, "macOS", {"Sonoma", {14, 0, 0}}},
- "23B74" = {{23, 1, 0}, "macOS", {"Sonoma", {14, 1, 0}}},
- "23B81" = {{23, 1, 0}, "macOS", {"Sonoma", {14, 1, 1}}},
- "23B2082" = {{23, 1, 0}, "macOS", {"Sonoma", {14, 1, 1}}},
- "23B92" = {{23, 1, 0}, "macOS", {"Sonoma", {14, 1, 2}}},
- "23B2091" = {{23, 1, 0}, "macOS", {"Sonoma", {14, 1, 2}}},
- "23C64" = {{23, 2, 0}, "macOS", {"Sonoma", {14, 2, 0}}},
- "23C71" = {{23, 2, 0}, "macOS", {"Sonoma", {14, 2, 1}}},
- "23D56" = {{23, 3, 0}, "macOS", {"Sonoma", {14, 3, 0}}},
- "23D60" = {{23, 3, 0}, "macOS", {"Sonoma", {14, 3, 1}}},
- "23E214" = {{23, 4, 0}, "macOS", {"Sonoma", {14, 4, 0}}},
- "23E224" = {{23, 4, 0}, "macOS", {"Sonoma", {14, 4, 1}}},
- "23F79" = {{23, 5, 0}, "macOS", {"Sonoma", {14, 5, 0}}},
- "23G80" = {{23, 6, 0}, "macOS", {"Sonoma", {14, 6, 0}}},
- "23G93" = {{23, 6, 0}, "macOS", {"Sonoma", {14, 6, 1}}},
- "23H124" = {{23, 6, 0}, "macOS", {"Sonoma", {14, 7, 0}}},
- "23H222" = {{23, 6, 0}, "macOS", {"Sonoma", {14, 7, 1}}},
-
- // MacOS Sequoia
- "24A335" = {{24, 0, 0}, "macOS", {"Sequoia", {15, 0, 0}}},
- "24A348" = {{24, 0, 0}, "macOS", {"Sequoia", {15, 0, 1}}},
- "24B83" = {{24, 1, 0}, "macOS", {"Sequoia", {15, 1, 0}}},
-}
-
-@(private)
-Darwin_Match :: enum {
- Unknown,
- Exact,
- Nearest,
-}
-
-@(private)
-map_darwin_kernel_version_to_macos_release :: proc(build: string, darwin: [3]int) -> (res: Darwin_To_Release, match: Darwin_Match) {
- // Find exact release match if possible.
- if v, v_ok := macos_release_map[build]; v_ok {
- return v, .Exact
- }
-
- nearest: Darwin_To_Release
- for _, v in macos_release_map {
- // Try an exact match on XNU version first.
- if darwin == v.darwin {
- return v, .Exact
- }
-
- // Major kernel version needs to match exactly,
- // otherwise the release is considered .Unknown
- if darwin.x == v.darwin.x {
- if nearest == {} {
- nearest = v
- }
- if darwin.y >= v.darwin.y && v.darwin != nearest.darwin {
- nearest = v
- if darwin.z >= v.darwin.z && v.darwin != nearest.darwin {
- nearest = v
- }
- }
- }
- }
-
- if nearest == {} {
- return {darwin, "macOS", {"Unknown", {}}}, .Unknown
- } else {
- return nearest, .Nearest
- }
+ os_version.as_string = string(b.buf[:])
}
diff --git a/core/sys/info/platform_linux.odin b/core/sys/info/platform_linux.odin
index 45efc3329..9c342e567 100644
--- a/core/sys/info/platform_linux.odin
+++ b/core/sys/info/platform_linux.odin
@@ -16,9 +16,13 @@ init_os_version :: proc () {
b := strings.builder_from_bytes(version_string_buf[:])
// Try to parse `/etc/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS`
- {
+ pretty_parse: {
fd, errno := linux.open("/etc/os-release", {})
- assert(errno == .NONE, "Failed to read /etc/os-release")
+ if errno != .NONE {
+ strings.write_string(&b, "Unknown Linux Distro")
+ break pretty_parse
+ }
+
defer {
cerrno := linux.close(fd)
assert(cerrno == .NONE, "Failed to close the file descriptor")
@@ -26,7 +30,10 @@ init_os_version :: proc () {
os_release_buf: [2048]u8
n, read_errno := linux.read(fd, os_release_buf[:])
- assert(read_errno == .NONE, "Failed to read data from /etc/os-release")
+ if read_errno != .NONE {
+ strings.write_string(&b, "Unknown Linux Distro")
+ break pretty_parse
+ }
release := string(os_release_buf[:n])
// Search the line in the file until we find "PRETTY_NAME="
@@ -59,7 +66,7 @@ init_os_version :: proc () {
os_version.as_string = strings.to_string(b)
// Parse the Linux version out of the release string
- {
+ version_loop: {
version_num, _, version_suffix := strings.partition(release_str, "-")
os_version.version = version_suffix
@@ -72,11 +79,11 @@ init_os_version :: proc () {
case 0: dst = &os_version.major
case 1: dst = &os_version.minor
case 2: dst = &os_version.patch
- case: break
+ case: break version_loop
}
num, ok := strconv.parse_int(part)
- if !ok { break }
+ if !ok { break version_loop }
dst^ = num
}
diff --git a/core/sys/linux/bits.odin b/core/sys/linux/bits.odin
index 9ce2e206e..53660dc8f 100644
--- a/core/sys/linux/bits.odin
+++ b/core/sys/linux/bits.odin
@@ -1,5 +1,10 @@
package linux
+import "base:intrinsics"
+
+@(private)
+log2 :: intrinsics.constant_log2
+
/*
Represents an error returned by most of syscalls
@@ -152,43 +157,65 @@ Errno :: enum i32 {
RDONLY flag is not present, because it has the value of 0, i.e. it is the
default, unless WRONLY or RDWR is specified.
*/
-Open_Flags_Bits :: enum {
- WRONLY = 0,
- RDWR = 1,
- CREAT = 6,
- EXCL = 7,
- NOCTTY = 8,
- TRUNC = 9,
- APPEND = 10,
- NONBLOCK = 11,
- DSYNC = 12,
- ASYNC = 13,
- DIRECT = 14,
- LARGEFILE = 15,
- DIRECTORY = 16,
- NOFOLLOW = 17,
- NOATIME = 18,
- CLOEXEC = 19,
- PATH = 21,
+when ODIN_ARCH != .arm64 && ODIN_ARCH != .arm32 {
+ Open_Flags_Bits :: enum {
+ WRONLY = 0,
+ RDWR = 1,
+ CREAT = 6,
+ EXCL = 7,
+ NOCTTY = 8,
+ TRUNC = 9,
+ APPEND = 10,
+ NONBLOCK = 11,
+ DSYNC = 12,
+ ASYNC = 13,
+ DIRECT = 14,
+ LARGEFILE = 15,
+ DIRECTORY = 16,
+ NOFOLLOW = 17,
+ NOATIME = 18,
+ CLOEXEC = 19,
+ PATH = 21,
+ }
+ // https://github.com/torvalds/linux/blob/7367539ad4b0f8f9b396baf02110962333719a48/include/uapi/asm-generic/fcntl.h#L19
+ #assert(1 << uint(Open_Flags_Bits.WRONLY) == 0o0000000_1)
+ #assert(1 << uint(Open_Flags_Bits.RDWR) == 0o0000000_2)
+ #assert(1 << uint(Open_Flags_Bits.CREAT) == 0o00000_100)
+ #assert(1 << uint(Open_Flags_Bits.EXCL) == 0o00000_200)
+ #assert(1 << uint(Open_Flags_Bits.NOCTTY) == 0o00000_400)
+ #assert(1 << uint(Open_Flags_Bits.TRUNC) == 0o0000_1000)
+ #assert(1 << uint(Open_Flags_Bits.APPEND) == 0o0000_2000)
+ #assert(1 << uint(Open_Flags_Bits.NONBLOCK) == 0o0000_4000)
+ #assert(1 << uint(Open_Flags_Bits.DSYNC) == 0o000_10000)
+ #assert(1 << uint(Open_Flags_Bits.ASYNC) == 0o000_20000)
+ #assert(1 << uint(Open_Flags_Bits.DIRECT) == 0o000_40000)
+ #assert(1 << uint(Open_Flags_Bits.LARGEFILE) == 0o00_100000)
+ #assert(1 << uint(Open_Flags_Bits.DIRECTORY) == 0o00_200000)
+ #assert(1 << uint(Open_Flags_Bits.NOFOLLOW) == 0o00_400000)
+ #assert(1 << uint(Open_Flags_Bits.NOATIME) == 0o0_1000000)
+ #assert(1 << uint(Open_Flags_Bits.CLOEXEC) == 0o0_2000000)
+ #assert(1 << uint(Open_Flags_Bits.PATH) == 0o_10000000)
+} else {
+ Open_Flags_Bits :: enum {
+ WRONLY = 0,
+ RDWR = 1,
+ CREAT = 6,
+ EXCL = 7,
+ NOCTTY = 8,
+ TRUNC = 9,
+ APPEND = 10,
+ NONBLOCK = 11,
+ DSYNC = 12,
+ ASYNC = 13,
+ DIRECTORY = 14,
+ NOFOLLOW = 15,
+ DIRECT = 16,
+ LARGEFILE = 17,
+ NOATIME = 18,
+ CLOEXEC = 19,
+ PATH = 21,
+ }
}
-// https://github.com/torvalds/linux/blob/7367539ad4b0f8f9b396baf02110962333719a48/include/uapi/asm-generic/fcntl.h#L19
-#assert(1 << uint(Open_Flags_Bits.WRONLY) == 0o0000000_1)
-#assert(1 << uint(Open_Flags_Bits.RDWR) == 0o0000000_2)
-#assert(1 << uint(Open_Flags_Bits.CREAT) == 0o00000_100)
-#assert(1 << uint(Open_Flags_Bits.EXCL) == 0o00000_200)
-#assert(1 << uint(Open_Flags_Bits.NOCTTY) == 0o00000_400)
-#assert(1 << uint(Open_Flags_Bits.TRUNC) == 0o0000_1000)
-#assert(1 << uint(Open_Flags_Bits.APPEND) == 0o0000_2000)
-#assert(1 << uint(Open_Flags_Bits.NONBLOCK) == 0o0000_4000)
-#assert(1 << uint(Open_Flags_Bits.DSYNC) == 0o000_10000)
-#assert(1 << uint(Open_Flags_Bits.ASYNC) == 0o000_20000)
-#assert(1 << uint(Open_Flags_Bits.DIRECT) == 0o000_40000)
-#assert(1 << uint(Open_Flags_Bits.LARGEFILE) == 0o00_100000)
-#assert(1 << uint(Open_Flags_Bits.DIRECTORY) == 0o00_200000)
-#assert(1 << uint(Open_Flags_Bits.NOFOLLOW) == 0o00_400000)
-#assert(1 << uint(Open_Flags_Bits.NOATIME) == 0o0_1000000)
-#assert(1 << uint(Open_Flags_Bits.CLOEXEC) == 0o0_2000000)
-#assert(1 << uint(Open_Flags_Bits.PATH) == 0o_10000000)
/*
Bits for FD_Flags bitset
@@ -1307,6 +1334,7 @@ Socket_Option :: enum {
ACCEPTCONN = 30,
PEERSEC = 31,
PASSSEC = 34,
+ IP_ADD_MEMBERSHIP = 35,
MARK = 36,
PROTOCOL = 38,
DOMAIN = 39,
@@ -1816,22 +1844,23 @@ EPoll_Flags_Bits :: enum {
}
EPoll_Event_Kind :: enum u32 {
- IN = 0x001,
- PRI = 0x002,
- OUT = 0x004,
- RDNORM = 0x040,
- RDBAND = 0x080,
- WRNORM = 0x100,
- WRBAND = 0x200,
- MSG = 0x400,
- ERR = 0x008,
- HUP = 0x010,
- RDHUP = 0x2000,
- EXCLUSIVE = 1<<28,
- WAKEUP = 1<<29,
- ONESHOT = 1<<30,
- ET = 1<<31,
+ IN = log2(0x001),
+ PRI = log2(0x002),
+ OUT = log2(0x004),
+ RDNORM = log2(0x040),
+ RDBAND = log2(0x080),
+ WRNORM = log2(0x100),
+ WRBAND = log2(0x200),
+ MSG = log2(0x400),
+ ERR = log2(0x008),
+ HUP = log2(0x010),
+ RDHUP = log2(0x2000),
+ EXCLUSIVE = log2(1<<28),
+ WAKEUP = log2(1<<29),
+ ONESHOT = log2(1<<30),
+ ET = log2(1<<31),
}
+EPoll_Event_Set :: bit_set[EPoll_Event_Kind; u32]
EPoll_Ctl_Opcode :: enum i32 {
ADD = 1,
diff --git a/core/sys/linux/helpers.odin b/core/sys/linux/helpers.odin
index aefc1179e..9a7550d57 100644
--- a/core/sys/linux/helpers.odin
+++ b/core/sys/linux/helpers.odin
@@ -138,8 +138,8 @@ errno_unwrap :: proc {errno_unwrap2, errno_unwrap3}
when size_of(int) == 4 {
// xxx64 system calls take some parameters as pairs of ulongs rather than a single pointer
@(private)
- compat64_arg_pair :: #force_inline proc "contextless" (a: i64) -> (hi: uint, lo: uint) {
- no_sign := uint(a)
+ compat64_arg_pair :: #force_inline proc "contextless" (a: i64) -> (lo: uint, hi: uint) {
+ no_sign := u64(a)
hi = uint(no_sign >> 32)
lo = uint(no_sign & 0xffff_ffff)
return
diff --git a/core/sys/linux/sys.odin b/core/sys/linux/sys.odin
index 690902f07..985623e85 100644
--- a/core/sys/linux/sys.odin
+++ b/core/sys/linux/sys.odin
@@ -1,3 +1,4 @@
+#+build linux
#+no-instrumentation
package linux
@@ -151,7 +152,8 @@ lseek :: proc "contextless" (fd: Fd, off: i64, whence: Seek_Whence) -> (i64, Err
return errno_unwrap(ret, i64)
} else {
result: i64 = ---
- ret := syscall(SYS__llseek, fd, compat64_arg_pair(off), &result, whence)
+ lo, hi := compat64_arg_pair(off)
+ ret := syscall(SYS__llseek, fd, hi, lo, &result, whence)
return result, Errno(-ret)
}
}
@@ -211,7 +213,7 @@ rt_sigreturn :: proc "c" () -> ! {
/*
Alter an action taken by a process.
*/
-rt_sigaction :: proc "contextless" (sig: Signal, sigaction: ^Sig_Action($T), old_sigaction: ^Sig_Action) -> Errno {
+rt_sigaction :: proc "contextless" (sig: Signal, sigaction: ^Sig_Action($T), old_sigaction: ^Sig_Action($U)) -> Errno {
// NOTE(jason): It appears that the restorer is required for i386 and amd64
when ODIN_ARCH == .i386 || ODIN_ARCH == .amd64 {
sigaction.flags += {.RESTORER}
@@ -251,7 +253,11 @@ ioctl :: proc "contextless" (fd: Fd, request: u32, arg: uintptr) -> (uintptr) {
Available since Linux 2.2.
*/
pread :: proc "contextless" (fd: Fd, buf: []u8, offset: i64) -> (int, Errno) {
- ret := syscall(SYS_pread64, fd, raw_data(buf), len(buf), compat64_arg_pair(offset))
+ when ODIN_ARCH == .arm32 {
+ ret := syscall(SYS_pread64, fd, raw_data(buf), len(buf), 0, compat64_arg_pair(offset))
+ } else {
+ ret := syscall(SYS_pread64, fd, raw_data(buf), len(buf), compat64_arg_pair(offset))
+ }
return errno_unwrap(ret, int)
}
@@ -261,7 +267,11 @@ pread :: proc "contextless" (fd: Fd, buf: []u8, offset: i64) -> (int, Errno) {
Available since Linux 2.2.
*/
pwrite :: proc "contextless" (fd: Fd, buf: []u8, offset: i64) -> (int, Errno) {
- ret := syscall(SYS_pwrite64, fd, raw_data(buf), len(buf), compat64_arg_pair(offset))
+ when ODIN_ARCH == .arm32 {
+ ret := syscall(SYS_pwrite64, fd, raw_data(buf), len(buf), 0, compat64_arg_pair(offset))
+ } else {
+ ret := syscall(SYS_pwrite64, fd, raw_data(buf), len(buf), compat64_arg_pair(offset))
+ }
return errno_unwrap(ret, int)
}
@@ -1127,7 +1137,10 @@ fdatasync :: proc "contextless" (fd: Fd) -> (Errno) {
On 32-bit architectures available since Linux 2.4.
*/
truncate :: proc "contextless" (name: cstring, length: i64) -> (Errno) {
- when size_of(int) == 4 {
+ when ODIN_ARCH == .arm32 {
+ ret := syscall(SYS_truncate64, cast(rawptr) name, 0, compat64_arg_pair(length))
+ return Errno(-ret)
+ } else when size_of(int) == 4 {
ret := syscall(SYS_truncate64, cast(rawptr) name, compat64_arg_pair(length))
return Errno(-ret)
} else {
@@ -1141,7 +1154,10 @@ truncate :: proc "contextless" (name: cstring, length: i64) -> (Errno) {
On 32-bit architectures available since 2.4.
*/
ftruncate :: proc "contextless" (fd: Fd, length: i64) -> (Errno) {
- when size_of(int) == 4 {
+ when ODIN_ARCH == .arm32 {
+ ret := syscall(SYS_ftruncate64, fd, 0, compat64_arg_pair(length))
+ return Errno(-ret)
+ } else when size_of(int) == 4 {
ret := syscall(SYS_ftruncate64, fd, compat64_arg_pair(length))
return Errno(-ret)
} else {
@@ -1952,10 +1968,10 @@ sigaltstack :: proc "contextless" (stack: ^Sig_Stack, old_stack: ^Sig_Stack) ->
*/
mknod :: proc "contextless" (name: cstring, mode: Mode, dev: Dev) -> (Errno) {
when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
- ret := syscall(SYS_mknodat, AT_FDCWD, cast(rawptr) name, transmute(u32) mode, dev)
+ ret := syscall(SYS_mknodat, AT_FDCWD, cast(rawptr) name, transmute(u32) mode, cast(uint) dev)
return Errno(-ret)
} else {
- ret := syscall(SYS_mknod, cast(rawptr) name, transmute(u32) mode, dev)
+ ret := syscall(SYS_mknod, cast(rawptr) name, transmute(u32) mode, cast(uint) dev)
return Errno(-ret)
}
}
@@ -1995,10 +2011,10 @@ statfs :: proc "contextless" (path: cstring, statfs: ^Stat_FS) -> (Errno) {
*/
fstatfs :: proc "contextless" (fd: Fd, statfs: ^Stat_FS) -> (Errno) {
when size_of(int) == 8 {
- ret := syscall(SYS_statfs, fd, statfs)
+ ret := syscall(SYS_fstatfs, fd, statfs)
return Errno(-ret)
} else {
- ret := syscall(SYS_statfs64, fd, size_of(Stat_FS), statfs)
+ ret := syscall(SYS_fstatfs64, fd, size_of(Stat_FS), statfs)
return Errno(-ret)
}
}
@@ -2586,7 +2602,7 @@ mkdirat :: proc "contextless" (dirfd: Fd, name: cstring, mode: Mode) -> (Errno)
Available since Linux 2.6.16.
*/
mknodat :: proc "contextless" (dirfd: Fd, name: cstring, mode: Mode, dev: Dev) -> (Errno) {
- ret := syscall(SYS_mknodat, dirfd, cast(rawptr) name, transmute(u32) mode, dev)
+ ret := syscall(SYS_mknodat, dirfd, cast(rawptr) name, transmute(u32) mode, cast(uint) dev)
return Errno(-ret)
}
@@ -2684,13 +2700,8 @@ faccessat :: proc "contextless" (dirfd: Fd, name: cstring, mode: Mode = F_OK) ->
Available since Linux 2.6.16.
*/
ppoll :: proc "contextless" (fds: []Poll_Fd, timeout: ^Time_Spec, sigmask: ^Sig_Set) -> (i32, Errno) {
- when size_of(int) == 8 {
- ret := syscall(SYS_ppoll, raw_data(fds), len(fds), timeout, sigmask, size_of(Sig_Set))
- return errno_unwrap(ret, i32)
- } else {
- ret := syscall(SYS_ppoll_time64, raw_data(fds), len(fds), timeout, sigmask, size_of(Sig_Set))
- return errno_unwrap(ret, i32)
- }
+ ret := syscall(SYS_ppoll, raw_data(fds), len(fds), timeout, sigmask, size_of(Sig_Set))
+ return errno_unwrap(ret, i32)
}
// TODO(flysand): unshare
diff --git a/core/sys/linux/syscall_arm64.odin b/core/sys/linux/syscall_arm64.odin
index da8eb45da..6f1c93f83 100644
--- a/core/sys/linux/syscall_arm64.odin
+++ b/core/sys/linux/syscall_arm64.odin
@@ -317,5 +317,18 @@ SYS_futex_waitv :: uintptr(449)
SYS_set_mempolicy_home_node :: uintptr(450)
SYS_cachestat :: uintptr(451)
SYS_fchmodat2 :: uintptr(452)
-
+SYS_map_shadow_stack :: uintptr(453)
+SYS_futex_wake :: uintptr(454)
+SYS_futex_wait :: uintptr(455)
+SYS_futex_requeue :: uintptr(456)
+SYS_statmount :: uintptr(457)
+SYS_listmount :: uintptr(458)
+SYS_lsm_get_self_attr :: uintptr(459)
+SYS_lsm_set_self_attr :: uintptr(460)
+SYS_lsm_list_modules :: uintptr(461)
+SYS_mseal :: uintptr(462)
+SYS_setxattrat :: uintptr(463)
+SYS_getxattrat :: uintptr(464)
+SYS_listxattrat :: uintptr(465)
+SYS_removexattrat :: uintptr(466)
diff --git a/core/sys/linux/types.odin b/core/sys/linux/types.odin
index 42d5cc988..8f2284f56 100644
--- a/core/sys/linux/types.odin
+++ b/core/sys/linux/types.odin
@@ -3,7 +3,7 @@ package linux
/*
Type for storage device handle.
*/
-Dev :: distinct int
+Dev :: distinct u64
/*
Type for 32-bit User IDs.
@@ -153,6 +153,7 @@ when ODIN_ARCH == .amd64 {
uid: Uid,
gid: Gid,
rdev: Dev,
+ _: [4]u8,
size: i64,
blksize: uint,
blocks: u64,
@@ -516,79 +517,79 @@ Pid_FD_Flags :: bit_set[Pid_FD_Flags_Bits; i32]
Sig_Set :: [_SIGSET_NWORDS]uint
@private SI_MAX_SIZE :: 128
-@private SI_ARCH_PREAMBLE :: 4 * size_of(i32)
+@private SI_ARCH_PREAMBLE :: 4 * size_of(i32) when size_of(rawptr) == 8 else 3 * size_of(i32)
@private SI_PAD_SIZE :: SI_MAX_SIZE - SI_ARCH_PREAMBLE
Sig_Handler_Fn :: #type proc "c" (sig: Signal)
Sig_Restore_Fn :: #type proc "c" () -> !
-Sig_Info :: struct #packed {
- signo: Signal,
- errno: Errno,
- code: i32,
- _pad0: i32,
- using _union: struct #raw_union {
- _pad1: [SI_PAD_SIZE]u8,
- using _kill: struct {
- pid: Pid, /* sender's pid */
- uid: Uid, /* sender's uid */
- },
- using _timer: struct {
- timerid: i32, /* timer id */
- overrun: i32, /* overrun count */
- value: Sig_Val, /* timer value */
- },
- /* POSIX.1b signals */
- using _rt: struct {
- _pid0: Pid, /* sender's pid */
- _uid0: Uid, /* sender's uid */
- },
- /* SIGCHLD */
- using _sigchld: struct {
- _pid1: Pid, /* which child */
- _uid1: Uid, /* sender's uid */
- status: i32, /* exit code */
- utime: uint,
- stime: uint, //clock_t
- },
- /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
- using _sigfault: struct {
- addr: rawptr, /* faulting insn/memory ref. */
- using _: struct #raw_union {
- trapno: i32, /* Trap number that caused signal */
- addr_lsb: i16, /* LSB of the reported address */
- using _addr_bnd: struct {
- _pad2: u64,
- lower: rawptr, /* lower bound during fault */
- upper: rawptr, /* upper bound during fault */
- },
- using _addr_pkey: struct {
- _pad3: u64,
- pkey: u32, /* protection key on PTE that faulted */
- },
- using _perf: struct {
- perf_data: u64,
- perf_type: u32,
- perf_flags: u32,
+when size_of(rawptr) == 8 {
+ Sig_Info :: struct #packed {
+ signo: Signal,
+ errno: Errno,
+ code: i32,
+ _pad0: i32,
+ using _union: struct #raw_union {
+ _pad1: [SI_PAD_SIZE]u8,
+ using _kill: struct {
+ pid: Pid, /* sender's pid */
+ uid: Uid, /* sender's uid */
+ },
+ using _timer: struct {
+ timerid: i32, /* timer id */
+ overrun: i32, /* overrun count */
+ value: Sig_Val, /* timer value */
+ },
+ /* POSIX.1b signals */
+ using _rt: struct {
+ _pid0: Pid, /* sender's pid */
+ _uid0: Uid, /* sender's uid */
+ },
+ /* SIGCHLD */
+ using _sigchld: struct {
+ _pid1: Pid, /* which child */
+ _uid1: Uid, /* sender's uid */
+ status: i32, /* exit code */
+ utime: uint,
+ stime: uint, //clock_t
+ },
+ /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
+ using _sigfault: struct {
+ addr: rawptr, /* faulting insn/memory ref. */
+ using _: struct #raw_union {
+ trapno: i32, /* Trap number that caused signal */
+ addr_lsb: i16, /* LSB of the reported address */
+ using _addr_bnd: struct {
+ _pad2: u64,
+ lower: rawptr, /* lower bound during fault */
+ upper: rawptr, /* upper bound during fault */
+ },
+ using _addr_pkey: struct {
+ _pad3: u64,
+ pkey: u32, /* protection key on PTE that faulted */
+ },
+ using _perf: struct {
+ perf_data: u64,
+ perf_type: u32,
+ perf_flags: u32,
+ },
},
},
+ /* SIGPOLL */
+ using _sigpoll: struct {
+ band: int, /* POLL_IN, POLL_OUT, POLL_MSG */
+ fd: Fd,
+ },
+ /* SIGSYS */
+ using _sigsys: struct {
+ call_addr: rawptr, /* calling user insn */
+ syscall: i32, /* triggering system call number */
+ arch: u32, /* AUDIT_ARCH_* of syscall */
+ },
},
- /* SIGPOLL */
- using _sigpoll: struct {
- band: int, /* POLL_IN, POLL_OUT, POLL_MSG */
- fd: Fd,
- },
- /* SIGSYS */
- using _sigsys: struct {
- call_addr: rawptr, /* calling user insn */
- syscall: i32, /* triggering system call number */
- arch: u32, /* AUDIT_ARCH_* of syscall */
- },
- },
-}
+ }
-#assert(size_of(Sig_Info) == 128)
-when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
+ #assert(size_of(Sig_Info) == 128)
#assert(offset_of(Sig_Info, signo) == 0x00)
#assert(offset_of(Sig_Info, errno) == 0x04)
#assert(offset_of(Sig_Info, code) == 0x08)
@@ -615,7 +616,96 @@ when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
#assert(offset_of(Sig_Info, syscall) == 0x18)
#assert(offset_of(Sig_Info, arch) == 0x1C)
} else {
- // TODO
+ Sig_Info :: struct {
+ signo: Signal,
+ errno: Errno,
+ code: i32,
+ using _union: struct #raw_union {
+ _pad1: [SI_PAD_SIZE]u8,
+ using _kill: struct {
+ pid: Pid, /* sender's pid */
+ uid: Uid, /* sender's uid */
+ },
+ using _timer: struct {
+ timerid: i32, /* timer id */
+ overrun: i32, /* overrun count */
+ value: Sig_Val, /* timer value */
+ },
+ /* POSIX.1b signals */
+ using _rt: struct {
+ _pid0: Pid, /* sender's pid */
+ _uid0: Uid, /* sender's uid */
+ },
+ /* SIGCHLD */
+ using _sigchld: struct {
+ _pid1: Pid, /* which child */
+ _uid1: Uid, /* sender's uid */
+ status: i32, /* exit code */
+ utime: uint,
+ stime: uint, //clock_t
+ },
+ /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
+ using _sigfault: struct {
+ addr: rawptr, /* faulting insn/memory ref. */
+ using _: struct #raw_union {
+ trapno: i32, /* Trap number that caused signal */
+ addr_lsb: i16, /* LSB of the reported address */
+ using _addr_bnd: struct {
+ _pad2: u32,
+ lower: rawptr, /* lower bound during fault */
+ upper: rawptr, /* upper bound during fault */
+ },
+ using _addr_pkey: struct {
+ _pad3: u32,
+ pkey: u32, /* protection key on PTE that faulted */
+ },
+ using _perf: struct {
+ perf_data: u32,
+ perf_type: u32,
+ perf_flags: u32,
+ },
+ },
+ },
+ /* SIGPOLL */
+ using _sigpoll: struct {
+ band: int, /* POLL_IN, POLL_OUT, POLL_MSG */
+ fd: Fd,
+ },
+ /* SIGSYS */
+ using _sigsys: struct {
+ call_addr: rawptr, /* calling user insn */
+ syscall: i32, /* triggering system call number */
+ arch: u32, /* AUDIT_ARCH_* of syscall */
+ },
+ },
+ }
+
+ #assert(size_of(Sig_Info) == 128)
+ #assert(offset_of(Sig_Info, signo) == 0x00)
+ #assert(offset_of(Sig_Info, errno) == 0x04)
+ #assert(offset_of(Sig_Info, code) == 0x08)
+ #assert(offset_of(Sig_Info, pid) == 0x0c)
+ #assert(offset_of(Sig_Info, uid) == 0x10)
+ #assert(offset_of(Sig_Info, timerid) == 0x0c)
+ #assert(offset_of(Sig_Info, overrun) == 0x10)
+ #assert(offset_of(Sig_Info, value) == 0x14)
+ #assert(offset_of(Sig_Info, status) == 0x14)
+ #assert(offset_of(Sig_Info, utime) == 0x18)
+ #assert(offset_of(Sig_Info, stime) == 0x1c)
+ #assert(offset_of(Sig_Info, addr) == 0x0c)
+ #assert(offset_of(Sig_Info, addr_lsb) == 0x10)
+ #assert(offset_of(Sig_Info, trapno) == 0x10)
+ #assert(offset_of(Sig_Info, lower) == 0x14)
+ #assert(offset_of(Sig_Info, upper) == 0x18)
+ #assert(offset_of(Sig_Info, pkey) == 0x14)
+ #assert(offset_of(Sig_Info, perf_data) == 0x10)
+ #assert(offset_of(Sig_Info, perf_type) == 0x14)
+ #assert(offset_of(Sig_Info, perf_flags) == 0x18)
+ #assert(offset_of(Sig_Info, band) == 0x0c)
+ #assert(offset_of(Sig_Info, fd) == 0x10)
+ #assert(offset_of(Sig_Info, call_addr) == 0x0c)
+ #assert(offset_of(Sig_Info, syscall) == 0x10)
+ #assert(offset_of(Sig_Info, arch) == 0x14)
}
SIGEV_MAX_SIZE :: 64
@@ -684,6 +774,14 @@ Address_Family :: distinct Protocol_Family
*/
Socket_Msg :: bit_set[Socket_Msg_Bits; i32]
+/*
+ Struct representing a generic socket address.
+*/
+Sock_Addr :: struct #packed {
+ sa_family: Address_Family,
+ sa_data: [14]u8,
+}
+
/*
Struct representing IPv4 socket address.
*/
@@ -691,6 +789,7 @@ Sock_Addr_In :: struct #packed {
sin_family: Address_Family,
sin_port: u16be,
sin_addr: [4]u8,
+ sin_zero: [size_of(Sock_Addr) - size_of(Address_Family) - size_of(u16be) - size_of([4]u8)]u8,
}
/*
@@ -720,6 +819,7 @@ Sock_Addr_Any :: struct #raw_union {
family: Address_Family,
port: u16be,
},
+ using generic: Sock_Addr,
using ipv4: Sock_Addr_In,
using ipv6: Sock_Addr_In6,
using uds: Sock_Addr_Un,
@@ -1350,7 +1450,7 @@ EPoll_Data :: struct #raw_union {
}
EPoll_Event :: struct #packed {
- events: EPoll_Event_Kind,
+ events: EPoll_Event_Set,
data: EPoll_Data,
}
diff --git a/core/sys/linux/wrappers.odin b/core/sys/linux/wrappers.odin
index 4f6118c80..7b0dc61e2 100644
--- a/core/sys/linux/wrappers.odin
+++ b/core/sys/linux/wrappers.odin
@@ -86,22 +86,18 @@ dirent_iterate_buf :: proc "contextless" (buf: []u8, offs: ^int) -> (d: ^Dirent,
/// The lifetime of the string is bound to the lifetime of the provided dirent structure
dirent_name :: proc "contextless" (dirent: ^Dirent) -> string #no_bounds_check {
str := ([^]u8)(&dirent.name)
- // Note(flysand): The string size calculated above applies only to the ideal case
- // we subtract 1 byte from the string size, because a null terminator is guaranteed
- // to be present. But! That said, the dirents are aligned to 8 bytes and the padding
- // between the null terminator and the start of the next struct may be not initialized
- // which means we also have to scan these garbage bytes.
- str_size := int(dirent.reclen) - 1 - cast(int)offset_of(Dirent, name)
- // This skips *only* over the garbage, since if we're not garbage we're at nul terminator,
- // which skips this loop
- for str[str_size] != 0 {
- str_size -= 1
+ // Dirents are aligned to 8 bytes, so there is guaranteed to be a null
+ // terminator in the last 8 bytes.
+ str_size := int(dirent.reclen) - cast(int)offset_of(Dirent, name)
+
+ trunc := min(str_size, 8)
+ str_size -= trunc
+ for _ in 0.. u64 {
return u64(id) | (u64(op) << 8) | (u64(res) << 16)
-}
\ No newline at end of file
+}
diff --git a/core/sys/orca/macros.odin b/core/sys/orca/macros.odin
index d6a1a0f82..12adfdb91 100644
--- a/core/sys/orca/macros.odin
+++ b/core/sys/orca/macros.odin
@@ -37,12 +37,7 @@ log_info :: proc "contextless" (msg: cstring, loc := #caller_location) {
}
abort :: proc "contextless" (msg: cstring, loc := #caller_location) {
- abort_ext(
- cstring(raw_data(loc.procedure)),
- cstring(raw_data(loc.file_path)),
- loc.line,
- msg,
- )
+ abort_ext(cstring(raw_data(loc.procedure)), cstring(raw_data(loc.file_path)), loc.line, msg)
}
////////////////////////////////////////////////////////////////////////////////
@@ -55,7 +50,12 @@ list_entry :: proc "contextless" (elt: ^list_elt, $T: typeid, $member: string) -
}
// Get the next entry in a list.
-list_next_entry :: proc "contextless" (list: ^list, elt: ^list_elt, $T: typeid, $member: string) -> ^T {
+list_next_entry :: proc "contextless" (
+ list: ^list,
+ elt: ^list_elt,
+ $T: typeid,
+ $member: string,
+) -> ^T {
if elt.next != list.last {
return list_entry(elt.next, T, member)
}
@@ -64,7 +64,12 @@ list_next_entry :: proc "contextless" (list: ^list, elt: ^list_elt, $T: typeid,
}
// Get the previous entry in a list.
-list_prev_entry :: proc "contextless" (list: ^list, elt: ^list_elt, $T: typeid, $member: string) -> ^T {
+list_prev_entry :: proc "contextless" (
+ list: ^list,
+ elt: ^list_elt,
+ $T: typeid,
+ $member: string,
+) -> ^T {
if elt.prev != list.last {
return list_entry(elt.prev, T, member)
}
@@ -94,9 +99,23 @@ list_last_entry :: proc "contextless" (list: ^list, $T: typeid, $member: string)
// _elt: ^list_elt
// for elt in oc.list_for(list, &_elt, int, "elt") {
// }
-list_for :: proc "contextless" (list: ^list, elt: ^^list_elt, $T: typeid, $member: string) -> (^T, bool) {
+list_for :: proc "contextless" (
+ list: ^list,
+ elt: ^^list_elt,
+ $T: typeid,
+ $member: string,
+) -> (
+ ^T,
+ bool,
+) {
if elt == nil {
- assert_fail(#file, #procedure, #line, "elt != nil", "misuse of `list_for`, expected `elt` to not be nil")
+ assert_fail(
+ #file,
+ #procedure,
+ #line,
+ "elt != nil",
+ "misuse of `list_for`, expected `elt` to not be nil",
+ )
}
if elt^ == nil {
@@ -112,7 +131,15 @@ list_for :: proc "contextless" (list: ^list, elt: ^^list_elt, $T: typeid, $membe
list_iter :: list_for
-list_for_reverse :: proc "contextless" (list: ^list, elt: ^^list_elt, $T: typeid, $member: string) -> (^T, bool) {
+list_for_reverse :: proc "contextless" (
+ list: ^list,
+ elt: ^^list_elt,
+ $T: typeid,
+ $member: string,
+) -> (
+ ^T,
+ bool,
+) {
if elt^ == nil {
elt^ = list.last
entry := list_checked_entry(elt^, T, member)
@@ -226,27 +253,17 @@ str32_list_for :: proc "contextless" (list: ^str32_list, elt: ^^list_elt) -> (^s
return list_for(&list.list, elt, str32_elt, "listElt")
}
-@(deferred_none=ui_box_end)
-ui_container :: proc "contextless" (name: string, flags: ui_flags = {}) -> ^ui_box {
- return ui_box_begin_str8(name, flags)
+@(deferred_none = ui_box_end)
+ui_container :: proc "contextless" (name: string) -> ^ui_box {
+ return ui_box_begin_str8(name)
}
-@(deferred_none=ui_end_frame)
-ui_frame :: proc "contextless" (frame_size: [2]f32, style: ui_style, mask: ui_style_mask) {
- ui_begin_frame(frame_size, style, mask)
+@(deferred_none = ui_menu_end)
+ui_menu :: proc "contextless" (key, name: string) {
+ ui_menu_begin_str8(key, name)
}
-@(deferred_none=ui_panel_end)
-ui_panel :: proc "contextless" (name: cstring, flags: ui_flags) {
- ui_panel_begin(name, flags)
-}
-
-@(deferred_none=ui_menu_end)
-ui_menu :: proc "contextless" (name: cstring) {
- ui_menu_begin(name)
-}
-
-@(deferred_none=ui_menu_bar_end)
-ui_menu_bar :: proc "contextless" (name: cstring) {
- ui_menu_bar_begin(name)
+@(deferred_none = ui_menu_bar_end)
+ui_menu_bar :: proc "contextless" (key: string) {
+ ui_menu_bar_begin_str8(key)
}
diff --git a/core/sys/orca/odin.odin b/core/sys/orca/odin.odin
index 5c3e3e4d9..c59b990cf 100644
--- a/core/sys/orca/odin.odin
+++ b/core/sys/orca/odin.odin
@@ -8,15 +8,25 @@ create_odin_logger :: proc(lowest := runtime.Logger_Level.Debug, ident := "") ->
return runtime.Logger{odin_logger_proc, nil, lowest, {}}
}
+log_typed :: proc "contextless" (level: log_level, msg: cstring, loc := #caller_location) {
+ log_ext(
+ level,
+ cstring(raw_data(loc.procedure)),
+ cstring(raw_data(loc.file_path)),
+ loc.line,
+ msg,
+ )
+}
+
odin_logger_proc :: proc(logger_data: rawptr, level: runtime.Logger_Level, text: string, options: runtime.Logger_Options, location := #caller_location) {
cbuf := make([]byte, len(text)+1, context.temp_allocator)
copy(cbuf, text)
ctext := cstring(raw_data(cbuf))
switch level {
- case .Debug, .Info: log_info(ctext, location)
- case .Warning: log_warning(ctext, location)
+ case .Debug, .Info: log_typed(.INFO, ctext, location)
+ case .Warning: log_typed(.WARNING, ctext, location)
case: fallthrough
- case .Error, .Fatal: log_error(ctext, location)
+ case .Error, .Fatal: log_typed(.ERROR, ctext, location)
}
}
diff --git a/core/sys/orca/orca.odin b/core/sys/orca/orca.odin
index d1e7dbf66..abcf42fe2 100644
--- a/core/sys/orca/orca.odin
+++ b/core/sys/orca/orca.odin
@@ -14,27 +14,6 @@ pool :: struct {
blockSize: u64,
}
-@(link_prefix="OC_")
-foreign {
- UI_DARK_THEME: ui_theme
- UI_LIGHT_THEME: ui_theme
-
- UI_DARK_PALETTE: ui_palette
- UI_LIGHT_PALETTE: ui_palette
-}
-
-
-SYS_MAX_ERROR :: 1024
-
-sys_err_def :: struct {
- msg: [SYS_MAX_ERROR]u8 `fmt:"s,0"`,
- code: i32,
-}
-
-@(link_prefix="oc_")
-foreign {
- sys_error: sys_err_def
-}
UNICODE_BASIC_LATIN :: unicode_range { 0x0000, 127 }
UNICODE_C1_CONTROLS_AND_LATIN_1_SUPPLEMENT :: unicode_range { 0x0080, 127 }
UNICODE_LATIN_EXTENDED_A :: unicode_range { 0x0100, 127 }
@@ -178,43 +157,6 @@ file_write_slice :: proc(file: file, slice: []char) -> u64 {
file_read_slice :: proc(file: file, slice: []char) -> u64 {
return file_read(file, u64(len(slice)), raw_data(slice))
}
-
-style_enum :: enum {
- SIZE_WIDTH = 1,
- SIZE_HEIGHT,
-
- LAYOUT_AXIS,
- LAYOUT_ALIGN_X,
- LAYOUT_ALIGN_Y,
- LAYOUT_SPACING,
- LAYOUT_MARGIN_X,
- LAYOUT_MARGIN_Y,
-
- FLOAT_X,
- FLOAT_Y,
-
- COLOR,
- BG_COLOR,
- BORDER_COLOR,
- BORDER_SIZE,
- ROUNDNESS,
-
- FONT,
- FONT_SIZE,
-
- ANIMATION_TIME,
- ANIMATION_MASK,
-}
-
-ui_style_mask :: bit_set[style_enum; u64]
-
-// Masks like the C version that can be used as common combinations
-SIZE :: ui_style_mask { .SIZE_WIDTH, .SIZE_HEIGHT }
-LAYOUT_MARGINS :: ui_style_mask { .LAYOUT_MARGIN_X, .LAYOUT_MARGIN_Y }
-LAYOUT :: ui_style_mask { .LAYOUT_AXIS, .LAYOUT_ALIGN_X, .LAYOUT_ALIGN_Y, .LAYOUT_SPACING, .LAYOUT_MARGIN_X, .LAYOUT_MARGIN_Y }
-FLOAT :: ui_style_mask { .FLOAT_X, .FLOAT_Y }
-MASK_INHERITED :: ui_style_mask { .COLOR, .FONT, .FONT_SIZE, .ANIMATION_TIME, .ANIMATION_MASK }
-
////////////////////////////////////////////////////////////////////////////////
// Utility data structures and helpers used throughout the Orca API.
////////////////////////////////////////////////////////////////////////////////
@@ -578,8 +520,28 @@ foreign {
// A unicode codepoint.
utf32 :: rune
+// This enum declares the possible return status of UTF8 decoding/encoding operations.
+utf8_status :: enum u32 {
+ // The operation was successful.
+ OK = 0,
+ // The operation unexpectedly encountered the end of the utf8 sequence.
+ OUT_OF_BOUNDS = 1,
+ // A continuation byte was encountered where a leading byte was expected.
+ UNEXPECTED_CONTINUATION_BYTE = 3,
+ // A leading byte was encountered in the middle of the encoding of utf8 codepoint.
+ UNEXPECTED_LEADING_BYTE = 4,
+ // The utf8 sequence contains an invalid byte.
+ INVALID_BYTE = 5,
+ // The operation encountered an invalid utf8 codepoint.
+ INVALID_CODEPOINT = 6,
+ // The utf8 sequence contains an overlong encoding of a utf8 codepoint.
+ OVERLONG_ENCODING = 7,
+}
+
// A type representing the result of decoding of utf8-encoded codepoint.
utf8_dec :: struct {
+ // The status of the decoding operation. If not `OC_UTF8_OK`, it describes the error that was encountered during decoding.
+ status: utf8_status,
// The decoded codepoint.
codepoint: utf32,
// The size of the utf8 sequence encoding that codepoint.
@@ -1033,7 +995,7 @@ file_dialog_kind :: enum u32 {
// File dialog flags.
file_dialog_flag :: enum u32 {
// This dialog allows selecting files.
- FILES = 1,
+ FILES = 0,
// This dialog allows selecting directories.
DIRECTORIES,
// This dialog allows selecting multiple items.
@@ -1180,7 +1142,7 @@ io_req :: struct {
unused: u64,
},
using _: struct #raw_union {
- open: struct {
+ using open: struct {
// The access permissions requested on the file to open.
rights: file_access,
// The options to use when opening the file.
@@ -1390,181 +1352,333 @@ foreign {
// A 2D Vector Graphics API.
////////////////////////////////////////////////////////////////////////////////
+// An opaque handle to a graphics surface.
surface :: distinct u64
+// An opaque handle representing a rendering engine for the canvas API.
canvas_renderer :: distinct u64
+// An opaque handle to a canvas context. Canvas contexts are used to hold contextual state about drawing commands, such as the current color or the current line width, and to record drawing commands. Once commands have been recorded, they can be rendered to a surface using `oc_canvas_render()`.
canvas_context :: distinct u64
+// An opaque font handle.
font :: distinct u64
+// An opaque image handle.
image :: distinct u64
+// This enum describes possible blending modes for color gradient.
gradient_blend_space :: enum u32 {
+ // The gradient colors are interpolated in linear space.
LINEAR = 0,
+ // The gradient colors are interpolated in sRGB space.
SRGB = 1,
}
+// An enum identifying possible color spaces.
color_space :: enum u32 {
+ // A linear RGB color space.
RGB = 0,
+ // An sRGB color space.
SRGB = 1,
}
+// A struct representing a color.
color :: struct { using c: [4]f32, colorSpace: color_space }
+// Stroke joint types.
joint_type :: enum u32 {
+ // Miter joint.
MITER = 0,
+ // Bevel joint.
BEVEL = 1,
+ // Don't join path segments.
NONE = 2,
}
+// Cap types.
cap_type :: enum u32 {
+ // Don't draw caps.
NONE = 0,
+ // Square caps.
SQUARE = 1,
}
+// A struct describing the metrics of a font.
font_metrics :: struct {
+ // The ascent from the baseline to the top of the line (a positive value means the top line is above the baseline).
ascent: f32,
+ // The descent from the baseline to the bottom line (a positive value means the bottom line is below the baseline).
descent: f32,
+ // The gap between two lines of text.
lineGap: f32,
+ // The height of the lowercase character 'x'.
xHeight: f32,
+ // The height of capital letters.
capHeight: f32,
+ // The maximum character width.
width: f32,
}
+// A struct describing the metrics of a single glyph.
glyph_metrics :: struct {
ink: rect,
+ // The default amount from which to advance the cursor after drawing this glyph.
advance: vec2,
}
+// A struct describing the metrics of a run of glyphs.
text_metrics :: struct {
+ // The bounding box of the inked portion of the text.
ink: rect,
+ // The logical bounding box of the text (including ascents, descents, and line gaps).
logical: rect,
+ // The amount from which to advance the cursor after drawing the text.
advance: vec2,
}
+// An opaque struct representing a rectangle atlas. This is used to allocate rectangular regions of an image to make texture atlases.
rect_atlas :: struct {}
+// A struct describing a rectangular sub-region of an image.
image_region :: struct {
+ // The image handle.
image: image,
+ // The rectangular region of the image.
rect: rect,
}
@(default_calling_convention="c", link_prefix="oc_")
foreign {
+ // Returns a `nil` surface handle.
surface_nil :: proc() -> surface ---
+ // Check if a surface handle is `nil`.
surface_is_nil :: proc(surface: surface) -> bool ---
+ // Destroy a graphics surface.
surface_destroy :: proc(surface: surface) ---
+ /*
+ Get a surface's size.
+
+ The size is returned in device-independent "points". To get the size in pixels, multiply the size in points by the scaling factor returned by `oc_surface_contents_scaling()`.
+ */
surface_get_size :: proc(surface: surface) -> vec2 ---
+ // Get the scaling factor of a surface.
surface_contents_scaling :: proc(surface: surface) -> vec2 ---
+ // Bring a surface to the foreground, rendering it on top of other surfaces.
surface_bring_to_front :: proc(surface: surface) ---
+ // Send a surface to the background, rendering it below other surfaces.
surface_send_to_back :: proc(surface: surface) ---
+ // Checks if a surface is hidden.
surface_get_hidden :: proc(surface: surface) -> bool ---
+ // Set the hidden status of a surface.
surface_set_hidden :: proc(surface: surface, hidden: bool) ---
+ // Create a color using RGBA values.
color_rgba :: proc(r: f32, g: f32, b: f32, a: f32) -> color ---
+ // Create a current color using sRGBA values.
color_srgba :: proc(r: f32, g: f32, b: f32, a: f32) -> color ---
+ // Convert a color from one color space to another.
color_convert :: proc(_color: color, colorSpace: color_space) -> color ---
+ // Returns a `nil` canvas renderer handle.
canvas_renderer_nil :: proc() -> canvas_renderer ---
+ // Checks if a canvas renderer handle is `nil`.
canvas_renderer_is_nil :: proc(renderer: canvas_renderer) -> bool ---
+ // Create a canvas renderer.
canvas_renderer_create :: proc() -> canvas_renderer ---
+ // Destroy a canvas renderer.
canvas_renderer_destroy :: proc(renderer: canvas_renderer) ---
+ // Render canvas commands onto a surface.
canvas_render :: proc(renderer: canvas_renderer, _context: canvas_context, surface: surface) ---
+ // Present a canvas surface to the display.
canvas_present :: proc(renderer: canvas_renderer, surface: surface) ---
+ // Create a surface for rendering vector graphics.
canvas_surface_create :: proc(renderer: canvas_renderer) -> surface ---
- canvas_surface_swap_interval :: proc(surface: surface, swap: i32) ---
+ // Returns a `nil` canvas context handle.
canvas_context_nil :: proc() -> canvas_context ---
+ // Checks if a canvas context handle is `nil`.
canvas_context_is_nil :: proc(_context: canvas_context) -> bool ---
+ // Create a canvas context.
canvas_context_create :: proc() -> canvas_context ---
+ // Destroy a canvas context
canvas_context_destroy :: proc(_context: canvas_context) ---
+ // Make a canvas context current in the calling thread. Subsequent canvas commands will refer to this context until another context is made current.
canvas_context_select :: proc(_context: canvas_context) -> canvas_context ---
+ // Set the multisample anti-aliasing sample count for the commands of a context.
canvas_context_set_msaa_sample_count :: proc(_context: canvas_context, sampleCount: u32) ---
+ // Return a `nil` font handle.
font_nil :: proc() -> font ---
+ // Check if a font handle is `nil`.
font_is_nil :: proc(font: font) -> bool ---
+ // Create a font from in-memory TrueType data.
font_create_from_memory :: proc(mem: str8, rangeCount: u32, ranges: ^unicode_range) -> font ---
+ // Create a font from a TrueType font file.
font_create_from_file :: proc(file: file, rangeCount: u32, ranges: ^unicode_range) -> font ---
+ // Create a font from a TrueType font file path.
font_create_from_path :: proc(path: str8, rangeCount: u32, ranges: ^unicode_range) -> font ---
+ // Destroy a font.
font_destroy :: proc(font: font) ---
+ // Get the glyph indices of a run of unicode code points in a given font.
font_get_glyph_indices :: proc(font: font, codePoints: str32, backing: str32) -> str32 ---
+ // Get the glyph indices of a run of unicode code points in a given font and push them on an arena.
font_push_glyph_indices :: proc(arena: ^arena, font: font, codePoints: str32) -> str32 ---
+ // Get the glyp index of a single codepoint in a given font.
font_get_glyph_index :: proc(font: font, codePoint: utf32) -> u32 ---
+ // Get a font's metrics for a given font size.
font_get_metrics :: proc(font: font, emSize: f32) -> font_metrics ---
+ // Get a font's unscaled metrics.
font_get_metrics_unscaled :: proc(font: font) -> font_metrics ---
+ // Get a scale factor to apply to unscaled font metrics to obtain a given 'm' size.
font_get_scale_for_em_pixels :: proc(font: font, emSize: f32) -> f32 ---
+ // Get text metrics for a run of unicode code points.
font_text_metrics_utf32 :: proc(font: font, fontSize: f32, codepoints: str32) -> text_metrics ---
+ // Get the text metrics for a utf8 string.
font_text_metrics :: proc(font: font, fontSize: f32, text: str8) -> text_metrics ---
+ // Returns a `nil` image handle.
image_nil :: proc() -> image ---
+ // Check if an image handle is `nil`.
image_is_nil :: proc(a: image) -> bool ---
+ // Create an uninitialized image.
image_create :: proc(renderer: canvas_renderer, width: u32, height: u32) -> image ---
+ // Create an image from an array of 8 bit per channel rgba values.
image_create_from_rgba8 :: proc(renderer: canvas_renderer, width: u32, height: u32, pixels: [^]u8) -> image ---
+ // Create an image from in-memory png, jpeg or bmp data.
image_create_from_memory :: proc(renderer: canvas_renderer, mem: str8, flip: bool) -> image ---
+ // Create an image from an image file. Supported formats are: png, jpeg or bmp.
image_create_from_file :: proc(renderer: canvas_renderer, file: file, flip: bool) -> image ---
+ // Create an image from an image file path. Supported formats are: png, jpeg or bmp.
image_create_from_path :: proc(renderer: canvas_renderer, path: str8, flip: bool) -> image ---
+ // Destroy an image.
image_destroy :: proc(image: image) ---
+ // Upload pixels to an image.
image_upload_region_rgba8 :: proc(image: image, region: rect, pixels: [^]u8) ---
+ // Get the size of an image.
image_size :: proc(image: image) -> vec2 ---
+ // Create a rectangle atlas.
rect_atlas_create :: proc(arena: ^arena, width: i32, height: i32) -> ^rect_atlas ---
+ // Allocate a rectangular region from an atlas.
rect_atlas_alloc :: proc(atlas: ^rect_atlas, width: i32, height: i32) -> rect ---
+ // Recycle a rectangular region that was previously allocated from an atlas.
rect_atlas_recycle :: proc(atlas: ^rect_atlas, rect: rect) ---
+ // Allocate an image region from an atlas and upload pixels to that region.
image_atlas_alloc_from_rgba8 :: proc(atlas: ^rect_atlas, backingImage: image, width: u32, height: u32, pixels: [^]u8) -> image_region ---
+ // Allocate an image region from an atlas and upload an image to it.
image_atlas_alloc_from_memory :: proc(atlas: ^rect_atlas, backingImage: image, mem: str8, flip: bool) -> image_region ---
+ // Allocate an image region from an atlas and upload an image to it.
image_atlas_alloc_from_file :: proc(atlas: ^rect_atlas, backingImage: image, file: file, flip: bool) -> image_region ---
+ // Allocate an image region from an atlas and upload an image to it.
image_atlas_alloc_from_path :: proc(atlas: ^rect_atlas, backingImage: image, path: str8, flip: bool) -> image_region ---
+ // Recycle an image region allocated from an atlas.
image_atlas_recycle :: proc(atlas: ^rect_atlas, imageRgn: image_region) ---
+ // Push a matrix on the transform stack.
matrix_push :: proc(_matrix: mat2x3) ---
+ // Multiply a matrix with the top of the transform stack, and push the result on the top of the stack.
matrix_multiply_push :: proc(_matrix: mat2x3) ---
+ // Pop a matrix from the transform stack.
matrix_pop :: proc() ---
+ // Get the top matrix of the transform stack.
matrix_top :: proc() -> mat2x3 ---
+ // Push a clip rectangle to the clip stack.
clip_push :: proc(x: f32, y: f32, w: f32, h: f32) ---
+ // Pop from the clip stack.
clip_pop :: proc() ---
+ // Get the clip rectangle from the top of the clip stack.
clip_top :: proc() -> rect ---
+ // Set the current color.
set_color :: proc(_color: color) ---
+ // Set the current color using linear RGBA values.
set_color_rgba :: proc(r: f32, g: f32, b: f32, a: f32) ---
+ // Set the current color using sRGBA values.
set_color_srgba :: proc(r: f32, g: f32, b: f32, a: f32) ---
+ // Set the current color gradient.
set_gradient :: proc(blendSpace: gradient_blend_space, bottomLeft: color, bottomRight: color, topRight: color, topLeft: color) ---
+ // Set the current line width.
set_width :: proc(width: f32) ---
+ // Set the current tolerance for the line width. Bigger values increase performance but allow more inconsistent stroke widths along a path.
set_tolerance :: proc(tolerance: f32) ---
+ // Set the current joint style.
set_joint :: proc(joint: joint_type) ---
+ // Set the maximum joint excursion. If a joint would extend past this threshold, the renderer falls back to a bevel joint.
set_max_joint_excursion :: proc(maxJointExcursion: f32) ---
+ // Set the current cap style.
set_cap :: proc(cap: cap_type) ---
+ // The the current font.
set_font :: proc(font: font) ---
+ // Set the current font size.
set_font_size :: proc(size: f32) ---
+ // Set the current text flip value. `true` flips the y-axis of text rendering commands.
set_text_flip :: proc(flip: bool) ---
+ // Set the current source image.
set_image :: proc(image: image) ---
+ // Set the current source image region.
set_image_source_region :: proc(region: rect) ---
+ // Get the current color
get_color :: proc() -> color ---
+ // Get the current line width.
get_width :: proc() -> f32 ---
+ // Get the current line width tolerance.
get_tolerance :: proc() -> f32 ---
+ // Get the current joint style.
get_joint :: proc() -> joint_type ---
+ // Get the current max joint excursion.
get_max_joint_excursion :: proc() -> f32 ---
+ // Get the current cap style.
get_cap :: proc() -> cap_type ---
+ // Get the current font.
get_font :: proc() -> font ---
+ // Get the current font size.
get_font_size :: proc() -> f32 ---
+ // Get the current text flip value.
get_text_flip :: proc() -> bool ---
+ // Get the current source image.
get_image :: proc() -> image ---
+ // Get the current image source region.
get_image_source_region :: proc() -> rect ---
+ // Get the current cursor position.
get_position :: proc() -> vec2 ---
+ // Move the cursor to a given position.
move_to :: proc(x: f32, y: f32) ---
+ // Add a line to the path from the current position to a new one.
line_to :: proc(x: f32, y: f32) ---
+ // Add a quadratic Bézier curve to the path from the current position to a new one.
quadratic_to :: proc(x1: f32, y1: f32, x2: f32, y2: f32) ---
+ // Add a cubic Bézier curve to the path from the current position to a new one.
cubic_to :: proc(x1: f32, y1: f32, x2: f32, y2: f32, x3: f32, y3: f32) ---
+ // Close the current path with a line.
close_path :: proc() ---
+ // Add the outlines of a glyph run to the path, using glyph indices.
glyph_outlines :: proc(glyphIndices: str32) -> rect ---
+ // Add the outlines of a glyph run to the path, using unicode codepoints.
codepoints_outlines :: proc(string: str32) ---
+ // Add the outlines of a glyph run to the path, using a utf8 string.
text_outlines :: proc(string: str8) ---
+ // Clear the canvas to the current color.
clear :: proc() ---
+ // Fill the current path.
fill :: proc() ---
+ // Stroke the current path.
stroke :: proc() ---
+ // Draw a filled rectangle.
rectangle_fill :: proc(x: f32, y: f32, w: f32, h: f32) ---
+ // Draw a stroked rectangle.
rectangle_stroke :: proc(x: f32, y: f32, w: f32, h: f32) ---
+ // Draw a filled rounded rectangle.
rounded_rectangle_fill :: proc(x: f32, y: f32, w: f32, h: f32, r: f32) ---
+ // Draw a stroked rounded rectangle.
rounded_rectangle_stroke :: proc(x: f32, y: f32, w: f32, h: f32, r: f32) ---
+ // Draw a filled ellipse.
ellipse_fill :: proc(x: f32, y: f32, rx: f32, ry: f32) ---
+ // Draw a stroked ellipse.
ellipse_stroke :: proc(x: f32, y: f32, rx: f32, ry: f32) ---
+ // Draw a filled circle.
circle_fill :: proc(x: f32, y: f32, r: f32) ---
+ // Draw a stroked circle.
circle_stroke :: proc(x: f32, y: f32, r: f32) ---
+ // Add an arc to the path.
arc :: proc(x: f32, y: f32, r: f32, arcAngle: f32, startAngle: f32) ---
+ // Draw a text line.
text_fill :: proc(x: f32, y: f32, text: str8) ---
+ // Draw an image.
image_draw :: proc(image: image, rect: rect) ---
+ // Draw a sub-region of an image.
image_draw_region :: proc(image: image, srcRegion: rect, dstRegion: rect) ---
}
@@ -1574,9 +1688,11 @@ foreign {
@(default_calling_convention="c", link_prefix="oc_")
foreign {
+ // Create a graphics surface for GLES rendering.
gles_surface_create :: proc() -> surface ---
+ // Make the GL context of the surface current.
gles_surface_make_current :: proc(surface: surface) ---
- gles_surface_swap_interval :: proc(surface: surface, interval: i32) ---
+ // Swap the buffers of a GLES surface.
gles_surface_swap_buffers :: proc(surface: surface) ---
}
@@ -1638,463 +1754,6 @@ input_state :: struct {
clipboard: clipboard_state,
}
-ui_key :: struct {
- hash: u64,
-}
-
-ui_axis :: enum u32 {
- X = 0,
- Y = 1,
- COUNT = 2,
-}
-
-ui_align :: enum u32 {
- START = 0,
- END = 1,
- CENTER = 2,
-}
-
-ui_layout_align :: [2]ui_align
-
-ui_layout :: struct {
- axis: ui_axis,
- spacing: f32,
- margin: [2]f32,
- align: ui_layout_align,
-}
-
-ui_size_kind :: enum u32 {
- TEXT = 0,
- PIXELS = 1,
- CHILDREN = 2,
- PARENT = 3,
- PARENT_MINUS_PIXELS = 4,
-}
-
-ui_size :: struct {
- kind: ui_size_kind,
- value: f32,
- relax: f32,
- minSize: f32,
-}
-
-ui_box_size :: [2]ui_size
-
-ui_box_floating :: [2]bool
-
-ui_style :: struct {
- size: ui_box_size,
- layout: ui_layout,
- floating: ui_box_floating,
- floatTarget: vec2,
- _color: color,
- bgColor: color,
- borderColor: color,
- font: font,
- fontSize: f32,
- borderSize: f32,
- roundness: f32,
- animationTime: f32,
- animationMask: ui_style_mask,
-}
-
-ui_palette :: struct {
- red0: color,
- red1: color,
- red2: color,
- red3: color,
- red4: color,
- red5: color,
- red6: color,
- red7: color,
- red8: color,
- red9: color,
- orange0: color,
- orange1: color,
- orange2: color,
- orange3: color,
- orange4: color,
- orange5: color,
- orange6: color,
- orange7: color,
- orange8: color,
- orange9: color,
- amber0: color,
- amber1: color,
- amber2: color,
- amber3: color,
- amber4: color,
- amber5: color,
- amber6: color,
- amber7: color,
- amber8: color,
- amber9: color,
- yellow0: color,
- yellow1: color,
- yellow2: color,
- yellow3: color,
- yellow4: color,
- yellow5: color,
- yellow6: color,
- yellow7: color,
- yellow8: color,
- yellow9: color,
- lime0: color,
- lime1: color,
- lime2: color,
- lime3: color,
- lime4: color,
- lime5: color,
- lime6: color,
- lime7: color,
- lime8: color,
- lime9: color,
- lightGreen0: color,
- lightGreen1: color,
- lightGreen2: color,
- lightGreen3: color,
- lightGreen4: color,
- lightGreen5: color,
- lightGreen6: color,
- lightGreen7: color,
- lightGreen8: color,
- lightGreen9: color,
- green0: color,
- green1: color,
- green2: color,
- green3: color,
- green4: color,
- green5: color,
- green6: color,
- green7: color,
- green8: color,
- green9: color,
- teal0: color,
- teal1: color,
- teal2: color,
- teal3: color,
- teal4: color,
- teal5: color,
- teal6: color,
- teal7: color,
- teal8: color,
- teal9: color,
- cyan0: color,
- cyan1: color,
- cyan2: color,
- cyan3: color,
- cyan4: color,
- cyan5: color,
- cyan6: color,
- cyan7: color,
- cyan8: color,
- cyan9: color,
- lightBlue0: color,
- lightBlue1: color,
- lightBlue2: color,
- lightBlue3: color,
- lightBlue4: color,
- lightBlue5: color,
- lightBlue6: color,
- lightBlue7: color,
- lightBlue8: color,
- lightBlue9: color,
- blue0: color,
- blue1: color,
- blue2: color,
- blue3: color,
- blue4: color,
- blue5: color,
- blue6: color,
- blue7: color,
- blue8: color,
- blue9: color,
- indigo0: color,
- indigo1: color,
- indigo2: color,
- indigo3: color,
- indigo4: color,
- indigo5: color,
- indigo6: color,
- indigo7: color,
- indigo8: color,
- indigo9: color,
- violet0: color,
- violet1: color,
- violet2: color,
- violet3: color,
- violet4: color,
- violet5: color,
- violet6: color,
- violet7: color,
- violet8: color,
- violet9: color,
- purple0: color,
- purple1: color,
- purple2: color,
- purple3: color,
- purple4: color,
- purple5: color,
- purple6: color,
- purple7: color,
- purple8: color,
- purple9: color,
- pink0: color,
- pink1: color,
- pink2: color,
- pink3: color,
- pink4: color,
- pink5: color,
- pink6: color,
- pink7: color,
- pink8: color,
- pink9: color,
- grey0: color,
- grey1: color,
- grey2: color,
- grey3: color,
- grey4: color,
- grey5: color,
- grey6: color,
- grey7: color,
- grey8: color,
- grey9: color,
- black: color,
- white: color,
-}
-
-ui_theme :: struct {
- white: color,
- primary: color,
- primaryHover: color,
- primaryActive: color,
- border: color,
- fill0: color,
- fill1: color,
- fill2: color,
- bg0: color,
- bg1: color,
- bg2: color,
- bg3: color,
- bg4: color,
- text0: color,
- text1: color,
- text2: color,
- text3: color,
- sliderThumbBorder: color,
- elevatedBorder: color,
- roundnessSmall: f32,
- roundnessMedium: f32,
- roundnessLarge: f32,
- palette: ^ui_palette,
-}
-
-ui_tag :: struct {
- hash: u64,
-}
-
-ui_selector_kind :: enum u32 {
- ANY = 0,
- OWNER = 1,
- TEXT = 2,
- TAG = 3,
- STATUS = 4,
- KEY = 5,
-}
-
-ui_status_flag :: enum u8 {
- HOVER = 1,
- HOT,
- ACTIVE,
- DRAGGING,
-}
-ui_status :: bit_set[ui_status_flag; u8]
-
-ui_selector_op :: enum u32 {
- DESCENDANT = 0,
- AND = 1,
-}
-
-ui_selector :: struct {
- listElt: list_elt,
- kind: ui_selector_kind,
- op: ui_selector_op,
- using _: struct #raw_union {
- text: str8,
- key: ui_key,
- tag: ui_tag,
- status: ui_status,
- },
-}
-
-ui_pattern :: struct {
- l: list,
-}
-
-ui_box :: struct {
- listElt: list_elt,
- children: list,
- parent: ^ui_box,
- overlayElt: list_elt,
- bucketElt: list_elt,
- key: ui_key,
- frameCounter: u64,
- flags: ui_flags,
- string: str8,
- tags: list,
- drawProc: ui_box_draw_proc,
- drawData: rawptr,
- beforeRules: list,
- afterRules: list,
- targetStyle: ^ui_style,
- style: ui_style,
- z: u32,
- floatPos: vec2,
- childrenSum: [2]f32,
- spacing: [2]f32,
- minSize: [2]f32,
- rect: rect,
- sig: ^ui_sig,
- fresh: bool,
- closed: bool,
- parentClosed: bool,
- dragging: bool,
- hot: bool,
- active: bool,
- scroll: vec2,
- pressedMouse: vec2,
- hotTransition: f32,
- activeTransition: f32,
-}
-
-ui_style_rule :: struct {
- boxElt: list_elt,
- buildElt: list_elt,
- tmpElt: list_elt,
- owner: ^ui_box,
- pattern: ui_pattern,
- mask: ui_style_mask,
- style: ^ui_style,
-}
-
-ui_sig :: struct {
- box: ^ui_box,
- mouse: vec2,
- delta: vec2,
- wheel: vec2,
- pressed: bool,
- released: bool,
- clicked: bool,
- doubleClicked: bool,
- tripleClicked: bool,
- rightPressed: bool,
- dragging: bool,
- hovering: bool,
- pasted: bool,
-}
-
-ui_box_draw_proc :: proc "c" (arg0: ^ui_box, arg1: rawptr)
-
-ui_flag :: enum u32 {
- CLICKABLE = 0,
- SCROLL_WHEEL_X,
- SCROLL_WHEEL_Y,
- BLOCK_MOUSE,
- HOT_ANIMATION,
- ACTIVE_ANIMATION,
- OVERFLOW_ALLOW_X,
- OVERFLOW_ALLOW_Y,
- CLIP,
- DRAW_BACKGROUND,
- DRAW_FOREGROUND,
- DRAW_BORDER,
- DRAW_TEXT,
- DRAW_PROC,
- OVERLAY,
-}
-ui_flags :: bit_set[ui_flag; u32]
-
-MAX_INPUT_CHAR_PER_FRAME :: 64
-
-ui_input_text :: struct {
- count: u8 `fmt:"-"`,
- codePoints: [64]utf32 `fmt:"s,count"`,
-}
-
-ui_stack_elt :: struct {
- parent: ^ui_stack_elt,
- using _: struct #raw_union {
- box: ^ui_box,
- size: ui_size,
- clip: rect,
- },
-}
-
-ui_tag_elt :: struct {
- listElt: list_elt,
- tag: ui_tag,
-}
-
-BOX_MAP_BUCKET_COUNT :: 1024
-
-ui_edit_move :: enum u32 {
- NONE = 0,
- CHAR = 1,
- WORD = 2,
- LINE = 3,
-}
-
-ui_context :: struct {
- init: bool,
- input: input_state,
- frameCounter: u64,
- frameTime: f64,
- lastFrameDuration: f64,
- frameArena: arena,
- boxPool: pool,
- boxMap: [1024]list,
- root: ^ui_box,
- overlay: ^ui_box,
- overlayList: list,
- boxStack: ^ui_stack_elt,
- clipStack: ^ui_stack_elt,
- nextBoxBeforeRules: list,
- nextBoxAfterRules: list,
- nextBoxTags: list,
- z: u32,
- hovered: ^ui_box,
- focus: ^ui_box,
- editCursor: i32,
- editMark: i32,
- editFirstDisplayedChar: i32,
- editCursorBlinkStart: f64,
- editSelectionMode: ui_edit_move,
- editWordSelectionInitialCursor: i32,
- editWordSelectionInitialMark: i32,
- theme: ^ui_theme,
-}
-
-ui_text_box_result :: struct {
- changed: bool,
- accepted: bool,
- text: str8,
-}
-
-ui_select_popup_info :: struct {
- changed: bool,
- selectedIndex: i32,
- optionCount: i32 `fmt:"-"`,
- options: [^]str8 `fmt:"s,optionCount"`,
- placeholder: str8,
-}
-
-ui_radio_group_info :: struct {
- changed: bool,
- selectedIndex: i32,
- optionCount: i32 `fmt:"-"`,
- options: [^]str8 `fmt:"s,optionCount"`,
-}
-
@(default_calling_convention="c", link_prefix="oc_")
foreign {
input_process_event :: proc(arena: ^arena, state: ^input_state, event: ^event) ---
@@ -2120,59 +1779,364 @@ foreign {
clipboard_pasted :: proc(state: ^input_state) -> bool ---
clipboard_pasted_text :: proc(state: ^input_state) -> str8 ---
key_mods :: proc(state: ^input_state) -> keymod_flags ---
- ui_init :: proc(_context: ^ui_context) ---
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Graphical User Interface Core API.
+////////////////////////////////////////////////////////////////////////////////
+
+ui_axis :: enum u32 {
+ X = 0,
+ Y = 1,
+ COUNT = 2,
+}
+
+ui_align :: enum u32 {
+ START = 0,
+ END = 1,
+ CENTER = 2,
+}
+
+ui_size_kind :: enum u32 {
+ CHILDREN = 0,
+ TEXT = 1,
+ PIXELS = 2,
+ PARENT = 3,
+ PARENT_MINUS_PIXELS = 4,
+}
+
+ui_size :: struct {
+ kind: ui_size_kind,
+ value: f32,
+ relax: f32,
+ minSize: f32,
+}
+
+ui_overflow :: enum u32 {
+ CLIP = 0,
+ ALLOW = 1,
+ SCROLL = 2,
+}
+
+ui_attribute :: enum u32 {
+ WIDTH = 0,
+ HEIGHT = 1,
+ AXIS = 2,
+ MARGIN_X = 3,
+ MARGIN_Y = 4,
+ SPACING = 5,
+ ALIGN_X = 6,
+ ALIGN_Y = 7,
+ FLOATING_X = 8,
+ FLOATING_Y = 9,
+ FLOAT_TARGET_X = 10,
+ FLOAT_TARGET_Y = 11,
+ OVERFLOW_X = 12,
+ OVERFLOW_Y = 13,
+ CONSTRAIN_X = 14,
+ CONSTRAIN_Y = 15,
+ COLOR = 16,
+ BG_COLOR = 17,
+ BORDER_COLOR = 18,
+ FONT = 19,
+ TEXT_SIZE = 20,
+ BORDER_SIZE = 21,
+ ROUNDNESS = 22,
+ DRAW_MASK = 23,
+ ANIMATION_TIME = 24,
+ ANIMATION_MASK = 25,
+ CLICK_THROUGH = 26,
+ ATTRIBUTE_COUNT = 27,
+}
+
+ui_attribute_mask :: enum u32 {
+ NONE = 0,
+ SIZE_WIDTH = 1,
+ SIZE_HEIGHT = 2,
+ LAYOUT_AXIS = 4,
+ LAYOUT_ALIGN_X = 64,
+ LAYOUT_ALIGN_Y = 128,
+ LAYOUT_SPACING = 32,
+ LAYOUT_MARGIN_X = 8,
+ LAYOUT_MARGIN_Y = 16,
+ FLOATING_X = 256,
+ FLOATING_Y = 512,
+ FLOAT_TARGET_X = 1024,
+ FLOAT_TARGET_Y = 2048,
+ OVERFLOW_X = 4096,
+ OVERFLOW_Y = 8192,
+ CONSTRAIN_X = 16384,
+ CONSTRAIN_Y = 32768,
+ COLOR = 65536,
+ BG_COLOR = 131072,
+ BORDER_COLOR = 262144,
+ BORDER_SIZE = 2097152,
+ ROUNDNESS = 4194304,
+ FONT = 524288,
+ FONT_SIZE = 1048576,
+ DRAW_MASK = 8388608,
+ ANIMATION_TIME = 16777216,
+ ANIMATION_MASK = 33554432,
+ CLICK_THROUGH = 67108864,
+}
+
+ui_layout_align :: [2]ui_align
+
+ui_layout :: struct {
+ axis: ui_axis,
+ spacing: f32,
+ margin: [2]f32,
+ align: ui_layout_align,
+ overflow: [2]ui_overflow,
+ constrain: [2]bool,
+}
+
+ui_box_size :: [2]ui_size
+
+ui_box_floating :: [2]bool
+
+ui_draw_mask :: enum u32 {
+ BACKGROUND = 1,
+ BORDER = 2,
+ TEXT = 4,
+ PROC = 8,
+}
+
+ui_style :: struct {
+ size: ui_box_size,
+ layout: ui_layout,
+ floating: ui_box_floating,
+ floatTarget: vec2,
+ _color: color,
+ bgColor: color,
+ borderColor: color,
+ font: font,
+ fontSize: f32,
+ borderSize: f32,
+ roundness: f32,
+ drawMask: u32,
+ animationTime: f32,
+ animationMask: ui_attribute_mask,
+ clickThrough: bool,
+}
+
+ui_context :: struct {}
+
+ui_sig :: struct {
+ box: ^ui_box,
+ mouse: vec2,
+ delta: vec2,
+ wheel: vec2,
+ lastPressedMouse: vec2,
+ pressed: bool,
+ released: bool,
+ clicked: bool,
+ doubleClicked: bool,
+ tripleClicked: bool,
+ rightPressed: bool,
+ closed: bool,
+ active: bool,
+ hover: bool,
+ focus: bool,
+ pasted: bool,
+}
+
+ui_box_draw_proc :: proc "c" (arg0: ^ui_box, arg1: rawptr)
+
+ui_key :: struct {
+ hash: u64,
+}
+
+ui_box :: struct {
+ listElt: list_elt,
+ children: list,
+ parent: ^ui_box,
+ overlayElt: list_elt,
+ overlay: bool,
+ bucketElt: list_elt,
+ key: ui_key,
+ frameCounter: u64,
+ keyString: str8,
+ text: str8,
+ tags: list,
+ drawProc: ui_box_draw_proc,
+ drawData: rawptr,
+ rules: list,
+ targetStyle: ^ui_style,
+ style: ui_style,
+ z: u32,
+ floatPos: vec2,
+ childrenSum: [2]f32,
+ spacing: [2]f32,
+ minSize: [2]f32,
+ rect: rect,
+ styleVariables: list,
+ sig: ui_sig,
+ fresh: bool,
+ closed: bool,
+ parentClosed: bool,
+ dragging: bool,
+ hot: bool,
+ active: bool,
+ scroll: vec2,
+ pressedMouse: vec2,
+ hotTransition: f32,
+ activeTransition: f32,
+}
+
+@(default_calling_convention="c", link_prefix="oc_")
+foreign {
+ ui_context_create :: proc(defaultFont: font) -> ^ui_context ---
+ ui_context_destroy :: proc(_context: ^ui_context) ---
ui_get_context :: proc() -> ^ui_context ---
ui_set_context :: proc(_context: ^ui_context) ---
ui_process_event :: proc(event: ^event) ---
- ui_begin_frame :: proc(size: vec2, #by_ptr defaultStyle: ui_style, mask: ui_style_mask) ---
- ui_end_frame :: proc() ---
+ ui_frame_begin :: proc(size: vec2) ---
+ ui_frame_end :: proc() ---
ui_draw :: proc() ---
- ui_set_theme :: proc(theme: ^ui_theme) ---
- ui_key_make_str8 :: proc(string: str8) -> ui_key ---
- ui_key_make_path :: proc(path: str8_list) -> ui_key ---
- ui_box_make_str8 :: proc(string: str8, flags: ui_flags) -> ^ui_box ---
- ui_box_begin_str8 :: proc(string: str8, flags: ui_flags) -> ^ui_box ---
+ ui_input :: proc() -> ^input_state ---
+ ui_frame_arena :: proc() -> ^arena ---
+ ui_frame_time :: proc() -> f64 ---
+ ui_box_begin_str8 :: proc(string: str8) -> ^ui_box ---
ui_box_end :: proc() -> ^ui_box ---
- ui_box_push :: proc(box: ^ui_box) ---
- ui_box_pop :: proc() ---
- ui_box_top :: proc() -> ^ui_box ---
- ui_box_lookup_key :: proc(key: ui_key) -> ^ui_box ---
- ui_box_lookup_str8 :: proc(string: str8) -> ^ui_box ---
ui_box_set_draw_proc :: proc(box: ^ui_box, _proc: ui_box_draw_proc, data: rawptr) ---
- ui_box_closed :: proc(box: ^ui_box) -> bool ---
+ ui_box_set_text :: proc(box: ^ui_box, text: str8) ---
+ ui_box_set_overlay :: proc(box: ^ui_box, overlay: bool) ---
ui_box_set_closed :: proc(box: ^ui_box, closed: bool) ---
- ui_box_active :: proc(box: ^ui_box) -> bool ---
- ui_box_activate :: proc(box: ^ui_box) ---
- ui_box_deactivate :: proc(box: ^ui_box) ---
- ui_box_hot :: proc(box: ^ui_box) -> bool ---
- ui_box_set_hot :: proc(box: ^ui_box, hot: bool) ---
- ui_box_sig :: proc(box: ^ui_box) -> ui_sig ---
- ui_tag_make_str8 :: proc(string: str8) -> ui_tag ---
- ui_tag_box_str8 :: proc(box: ^ui_box, string: str8) ---
+ ui_box_user_data_get :: proc(box: ^ui_box) -> cstring ---
+ ui_box_user_data_push :: proc(box: ^ui_box, size: u64) -> cstring ---
+ ui_box_request_focus :: proc(box: ^ui_box) ---
+ ui_box_release_focus :: proc(box: ^ui_box) ---
+ ui_box_get_sig :: proc(box: ^ui_box) -> ui_sig ---
+ ui_set_draw_proc :: proc(_proc: ui_box_draw_proc, data: rawptr) ---
+ ui_set_text :: proc(text: str8) ---
+ ui_set_overlay :: proc(overlay: bool) ---
+ ui_set_closed :: proc(closed: bool) ---
+ ui_user_data_get :: proc() -> cstring ---
+ ui_user_data_push :: proc(size: u64) -> cstring ---
+ ui_request_focus :: proc() ---
+ ui_release_focus :: proc() ---
+ ui_get_sig :: proc() -> ui_sig ---
+ ui_box_tag_str8 :: proc(box: ^ui_box, string: str8) ---
+ ui_tag_str8 :: proc(string: str8) ---
ui_tag_next_str8 :: proc(string: str8) ---
- ui_apply_style_with_mask :: proc(dst: ^ui_style, src: ^ui_style, mask: ui_style_mask) ---
- ui_pattern_push :: proc(arena: ^arena, pattern: ^ui_pattern, selector: ui_selector) ---
- ui_pattern_all :: proc() -> ui_pattern ---
- ui_pattern_owner :: proc() -> ui_pattern ---
- ui_style_next :: proc(#by_ptr style: ui_style, mask: ui_style_mask) ---
- ui_style_match_before :: proc(pattern: ui_pattern, #by_ptr style: ui_style, mask: ui_style_mask) ---
- ui_style_match_after :: proc(pattern: ui_pattern, #by_ptr style: ui_style, mask: ui_style_mask) ---
- ui_label :: proc(label: cstring) -> ui_sig ---
- ui_label_str8 :: proc(label: str8) -> ui_sig ---
- ui_button :: proc(label: cstring) -> ui_sig ---
- ui_checkbox :: proc(name: cstring, checked: ^bool) -> ui_sig ---
- ui_slider :: proc(name: cstring, value: ^f32) -> ^ui_box ---
- ui_scrollbar :: proc(name: cstring, thumbRatio: f32, scrollValue: ^f32) -> ^ui_box ---
- ui_tooltip :: proc(label: cstring) ---
- ui_panel_begin :: proc(name: cstring, flags: ui_flags) ---
- ui_panel_end :: proc() ---
- ui_menu_bar_begin :: proc(name: cstring) ---
- ui_menu_bar_end :: proc() ---
- ui_menu_begin :: proc(label: cstring) ---
- ui_menu_end :: proc() ---
- ui_menu_button :: proc(label: cstring) -> ui_sig ---
- ui_text_box :: proc(name: cstring, arena: ^arena, text: str8) -> ui_text_box_result ---
- ui_select_popup :: proc(name: cstring, info: ^ui_select_popup_info) -> ui_select_popup_info ---
- ui_radio_group :: proc(name: cstring, info: ^ui_radio_group_info) -> ui_radio_group_info ---
+ ui_style_rule_begin :: proc(pattern: str8) ---
+ ui_style_rule_end :: proc() ---
+ ui_style_set_i32 :: proc(attr: ui_attribute, i: i32) ---
+ ui_style_set_f32 :: proc(attr: ui_attribute, f: f32) ---
+ ui_style_set_color :: proc(attr: ui_attribute, _color: color) ---
+ ui_style_set_font :: proc(attr: ui_attribute, font: font) ---
+ ui_style_set_size :: proc(attr: ui_attribute, size: ui_size) ---
+ ui_style_set_var_str8 :: proc(attr: ui_attribute, var: str8) ---
+ ui_style_set_var :: proc(attr: ui_attribute, var: cstring) ---
+ ui_var_default_i32_str8 :: proc(name: str8, i: i32) ---
+ ui_var_default_f32_str8 :: proc(name: str8, f: f32) ---
+ ui_var_default_size_str8 :: proc(name: str8, size: ui_size) ---
+ ui_var_default_color_str8 :: proc(name: str8, _color: color) ---
+ ui_var_default_font_str8 :: proc(name: str8, font: font) ---
+ ui_var_default_str8 :: proc(name: str8, src: str8) ---
+ ui_var_default_i32 :: proc(name: cstring, i: i32) ---
+ ui_var_default_f32 :: proc(name: cstring, f: f32) ---
+ ui_var_default_size :: proc(name: cstring, size: ui_size) ---
+ ui_var_default_color :: proc(name: cstring, _color: color) ---
+ ui_var_default_font :: proc(name: cstring, font: font) ---
+ ui_var_default :: proc(name: cstring, src: cstring) ---
+ ui_var_set_i32_str8 :: proc(name: str8, i: i32) ---
+ ui_var_set_f32_str8 :: proc(name: str8, f: f32) ---
+ ui_var_set_size_str8 :: proc(name: str8, size: ui_size) ---
+ ui_var_set_color_str8 :: proc(name: str8, _color: color) ---
+ ui_var_set_font_str8 :: proc(name: str8, font: font) ---
+ ui_var_set_str8 :: proc(name: str8, src: str8) ---
+ ui_var_set_i32 :: proc(name: cstring, i: i32) ---
+ ui_var_set_f32 :: proc(name: cstring, f: f32) ---
+ ui_var_set_size :: proc(name: cstring, size: ui_size) ---
+ ui_var_set_color :: proc(name: cstring, _color: color) ---
+ ui_var_set_font :: proc(name: cstring, font: font) ---
+ ui_var_set :: proc(name: cstring, src: cstring) ---
+ ui_var_get_i32_str8 :: proc(name: str8) -> i32 ---
+ ui_var_get_f32_str8 :: proc(name: str8) -> f32 ---
+ ui_var_get_size_str8 :: proc(name: str8) -> ui_size ---
+ ui_var_get_color_str8 :: proc(name: str8) -> color ---
+ ui_var_get_font_str8 :: proc(name: str8) -> font ---
+ ui_var_get_i32 :: proc(name: cstring) -> i32 ---
+ ui_var_get_f32 :: proc(name: cstring) -> f32 ---
+ ui_var_get_size :: proc(name: cstring) -> ui_size ---
+ ui_var_get_color :: proc(name: cstring) -> color ---
+ ui_var_get_font :: proc(name: cstring) -> font ---
+ ui_theme_dark :: proc() ---
+ ui_theme_light :: proc() ---
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Graphical User Interface Widgets.
+////////////////////////////////////////////////////////////////////////////////
+
+ui_text_box_result :: struct {
+ changed: bool,
+ accepted: bool,
+ text: str8,
+ box: ^ui_box,
+}
+
+ui_edit_move :: enum u32 {
+ NONE = 0,
+ CHAR = 1,
+ WORD = 2,
+ LINE = 3,
+}
+
+ui_text_box_info :: struct {
+ text: str8,
+ defaultText: str8,
+ cursor: i32,
+ mark: i32,
+ selectionMode: ui_edit_move,
+ wordSelectionInitialCursor: i32,
+ wordSelectionInitialMark: i32,
+ firstDisplayedChar: i32,
+ cursorBlinkStart: f64,
+}
+
+ui_select_popup_info :: struct {
+ changed: bool,
+ selectedIndex: i32,
+ optionCount: i32 `fmt:"-"`,
+ options: [^]str8 `fmt:"s,optionCount"`,
+ placeholder: str8,
+}
+
+ui_radio_group_info :: struct {
+ changed: bool,
+ selectedIndex: i32,
+ optionCount: i32 `fmt:"-"`,
+ options: [^]str8 `fmt:"s,optionCount"`,
+}
+
+@(default_calling_convention="c", link_prefix="oc_")
+foreign {
+ ui_label :: proc(key: cstring, label: cstring) -> ui_sig ---
+ ui_label_str8 :: proc(key: str8, label: str8) -> ui_sig ---
+ ui_button :: proc(key: cstring, text: cstring) -> ui_sig ---
+ ui_button_str8 :: proc(key: str8, text: str8) -> ui_sig ---
+ ui_checkbox :: proc(key: cstring, checked: ^bool) -> ui_sig ---
+ ui_checkbox_str8 :: proc(key: str8, checked: ^bool) -> ui_sig ---
+ ui_slider :: proc(name: cstring, value: ^f32) -> ^ui_box ---
+ ui_slider_str8 :: proc(name: str8, value: ^f32) -> ^ui_box ---
+ ui_tooltip :: proc(key: cstring, text: cstring) ---
+ ui_tooltip_str8 :: proc(key: str8, text: str8) ---
+ ui_menu_bar_begin :: proc(key: cstring) ---
+ ui_menu_bar_begin_str8 :: proc(key: str8) ---
+ ui_menu_bar_end :: proc() ---
+ ui_menu_begin :: proc(key: cstring, name: cstring) ---
+ ui_menu_begin_str8 :: proc(key: str8, name: str8) ---
+ ui_menu_end :: proc() ---
+ ui_menu_button :: proc(key: cstring, text: cstring) -> ui_sig ---
+ ui_menu_button_str8 :: proc(key: str8, text: str8) -> ui_sig ---
+ ui_text_box :: proc(key: cstring, arena: ^arena, info: ^ui_text_box_info) -> ui_text_box_result ---
+ ui_text_box_str8 :: proc(key: str8, arena: ^arena, info: ^ui_text_box_info) -> ui_text_box_result ---
+ ui_select_popup :: proc(key: cstring, info: ^ui_select_popup_info) -> ui_select_popup_info ---
+ ui_select_popup_str8 :: proc(key: str8, info: ^ui_select_popup_info) -> ui_select_popup_info ---
+ ui_radio_group :: proc(key: cstring, info: ^ui_radio_group_info) -> ui_radio_group_info ---
+ ui_radio_group_str8 :: proc(key: str8, info: ^ui_radio_group_info) -> ui_radio_group_info ---
}
diff --git a/core/sys/posix/arpa_inet.odin b/core/sys/posix/arpa_inet.odin
index d3592dd80..ac850ed49 100644
--- a/core/sys/posix/arpa_inet.odin
+++ b/core/sys/posix/arpa_inet.odin
@@ -1,10 +1,12 @@
-#+build darwin, linux, freebsd, openbsd, netbsd
+#+build darwin, linux, freebsd, openbsd, netbsd, haiku
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
+} else when ODIN_OS == .Haiku {
+ foreign import lib "system:network"
} else {
foreign import lib "system:c"
}
diff --git a/core/sys/posix/dirent.odin b/core/sys/posix/dirent.odin
index bf32be8cf..1394f6b9e 100644
--- a/core/sys/posix/dirent.odin
+++ b/core/sys/posix/dirent.odin
@@ -1,4 +1,4 @@
-#+build darwin, linux, freebsd, openbsd, netbsd
+#+build darwin, linux, freebsd, openbsd, netbsd, haiku
package posix
import "core:c"
@@ -219,12 +219,23 @@ when ODIN_OS == .Darwin {
} else when ODIN_OS == .Linux {
- dirent :: struct {
- d_ino: u64, /* [PSX] file number of entry */
- d_off: i64, /* directory offset of the next entry */
- d_reclen: u16, /* length of this record */
- d_type: D_Type, /* file type */
- d_name: [256]c.char `fmt:"s,0"`, /* [PSX] entry name */
- }
+ dirent :: struct {
+ d_ino: u64, /* [PSX] file number of entry */
+ d_off: i64, /* directory offset of the next entry */
+ d_reclen: u16, /* length of this record */
+ d_type: D_Type, /* file type */
+ d_name: [256]c.char `fmt:"s,0"`, /* [PSX] entry name */
+ }
+
+} else when ODIN_OS == .Haiku {
+
+ dirent :: struct {
+ d_dev: dev_t, /* device */
+ d_pdev: dev_t, /* parent device (only for queries) */
+ d_ino: ino_t, /* inode number */
+ d_pino: ino_t, /* parent inode (only for queries) */
+ d_reclen: c.ushort, /* length of this record, not the name */
+ d_name: [0]c.char `fmt:"s,0"`, /* name of the entry (null byte terminated) */
+ }
}
diff --git a/core/sys/posix/errno.odin b/core/sys/posix/errno.odin
index 9bc77f12e..bb4e9e045 100644
--- a/core/sys/posix/errno.odin
+++ b/core/sys/posix/errno.odin
@@ -1,4 +1,4 @@
-#+build windows, darwin, linux, freebsd, openbsd, netbsd
+#+build windows, darwin, linux, freebsd, openbsd, netbsd, haiku
package posix
import "core:c"
@@ -536,5 +536,92 @@ when ODIN_OS == .Darwin {
ETXTBSY :: 139
EWOULDBLOCK :: 140
EXDEV :: 18
+} else when ODIN_OS == .Haiku {
+ _HAIKU_USE_POSITIVE_POSIX_ERRORS :: libc._HAIKU_USE_POSITIVE_POSIX_ERRORS
+ _POSIX_ERROR_FACTOR :: libc._POSIX_ERROR_FACTOR
+
+ _GENERAL_ERROR_BASE :: min(c.int)
+ _OS_ERROR_BASE :: _GENERAL_ERROR_BASE + 0x1000
+ _STORAGE_ERROR_BASE :: _GENERAL_ERROR_BASE + 0x6000
+ _POSIX_ERROR_BASE :: _GENERAL_ERROR_BASE + 0x7000
+
+ EIO :: _POSIX_ERROR_FACTOR * (_GENERAL_ERROR_BASE + 1) // B_IO_ERROR
+ EACCES :: _POSIX_ERROR_FACTOR * (_GENERAL_ERROR_BASE + 2) // B_PERMISSION_DENIED
+ EINVAL :: _POSIX_ERROR_FACTOR * (_GENERAL_ERROR_BASE + 5) // B_BAD_VALUE
+ ETIMEDOUT :: _POSIX_ERROR_FACTOR * (_GENERAL_ERROR_BASE + 9) // B_TIMED_OUT
+ EINTR :: _POSIX_ERROR_FACTOR * (_GENERAL_ERROR_BASE + 10) // B_INTERRUPTED
+ EAGAIN :: _POSIX_ERROR_FACTOR * (_GENERAL_ERROR_BASE + 11) // B_WOULD_BLOCK /* SysV compatibility */
+ EWOULDBLOCK :: _POSIX_ERROR_FACTOR * (_GENERAL_ERROR_BASE + 11) // B_WOULD_BLOCK /* BSD compatibility */
+ EBUSY :: _POSIX_ERROR_FACTOR * (_GENERAL_ERROR_BASE + 14) // B_BUSY
+ EPERM :: _POSIX_ERROR_FACTOR * (_GENERAL_ERROR_BASE + 15) // B_NOT_ALLOWED
+ EFAULT :: _POSIX_ERROR_FACTOR * (_OS_ERROR_BASE + 0x301) // B_BAD_ADDRESS
+ ENOEXEC :: _POSIX_ERROR_FACTOR * (_OS_ERROR_BASE + 0x302) // B_NOT_AN_EXECUTABLE
+ EBADF :: _POSIX_ERROR_FACTOR * (_STORAGE_ERROR_BASE + 0) // B_FILE_ERROR
+ EEXIST :: _POSIX_ERROR_FACTOR * (_STORAGE_ERROR_BASE + 2) // B_FILE_EXISTS
+ ENOENT :: _POSIX_ERROR_FACTOR * (_STORAGE_ERROR_BASE + 3) // B_ENTRY_NOT_FOUND
+ ENAMETOOLONG :: _POSIX_ERROR_FACTOR * (_STORAGE_ERROR_BASE + 4) // B_NAME_TOO_LONG
+ ENOTDIR :: _POSIX_ERROR_FACTOR * (_STORAGE_ERROR_BASE + 5) // B_NOT_A_DIRECTORY
+ ENOTEMPTY :: _POSIX_ERROR_FACTOR * (_STORAGE_ERROR_BASE + 6) // B_DIRECTORY_NOT_EMPTY
+ ENOSPC :: _POSIX_ERROR_FACTOR * (_STORAGE_ERROR_BASE + 7) // B_DEVICE_FULL
+ EROFS :: _POSIX_ERROR_FACTOR * (_STORAGE_ERROR_BASE + 8) // B_READ_ONLY_DEVICE
+ EISDIR :: _POSIX_ERROR_FACTOR * (_STORAGE_ERROR_BASE + 9) // B_IS_A_DIRECTORY
+ EMFILE :: _POSIX_ERROR_FACTOR * (_STORAGE_ERROR_BASE + 10) // B_NO_MORE_FDS
+ EXDEV :: _POSIX_ERROR_FACTOR * (_STORAGE_ERROR_BASE + 11) // B_CROSS_DEVICE_LINK
+ ELOOP :: _POSIX_ERROR_FACTOR * (_STORAGE_ERROR_BASE + 12) // B_LINK_LIMIT
+ EPIPE :: _POSIX_ERROR_FACTOR * (_STORAGE_ERROR_BASE + 13) // B_BUSTED_PIPE
+ ENOMEM :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 0) when _HAIKU_USE_POSITIVE_POSIX_ERRORS else (_GENERAL_ERROR_BASE + 0) // B_NO_MEMORY
+ E2BIG :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 1)
+ ECHILD :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 2)
+ EDEADLK :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 3)
+ EFBIG :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 4)
+ EMLINK :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 5)
+ ENFILE :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 6)
+ ENODEV :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 7)
+ ENOLCK :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 8)
+ ENOSYS :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 9)
+ ENOTTY :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 10)
+ ENXIO :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 11)
+ ESPIPE :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 12)
+ ESRCH :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 13)
+ EPROTOTYPE :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 18)
+ EPROTONOSUPPORT :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 19)
+ EAFNOSUPPORT :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 21)
+ EADDRINUSE :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 22)
+ EADDRNOTAVAIL :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 23)
+ ENETDOWN :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 24)
+ ENETUNREACH :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 25)
+ ENETRESET :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 26)
+ ECONNABORTED :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 27)
+ ECONNRESET :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 28)
+ EISCONN :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 29)
+ ENOTCONN :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 30)
+ ECONNREFUSED :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 32)
+ EHOSTUNREACH :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 33)
+ ENOPROTOOPT :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 34)
+ ENOBUFS :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 35)
+ EINPROGRESS :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 36)
+ EALREADY :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 37)
+ ENOMSG :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 39)
+ ESTALE :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 40)
+ EOVERFLOW :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 41)
+ EMSGSIZE :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 42)
+ EOPNOTSUPP :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 43)
+ ENOTSOCK :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 44)
+ EBADMSG :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 46)
+ ECANCELED :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 47)
+ EDESTADDRREQ :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 48)
+ EDQUOT :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 49)
+ EIDRM :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 50)
+ EMULTIHOP :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 51)
+ ENODATA :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 52)
+ ENOLINK :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 53)
+ ENOSR :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 54)
+ ENOSTR :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 55)
+ ENOTSUP :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 56)
+ EPROTO :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 57)
+ ETIME :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 58)
+ ETXTBSY :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 59)
+ ENOTRECOVERABLE :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 61)
+ EOWNERDEAD :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 62)
}
diff --git a/core/sys/posix/fcntl.odin b/core/sys/posix/fcntl.odin
index d948af600..bc0b5b5ba 100644
--- a/core/sys/posix/fcntl.odin
+++ b/core/sys/posix/fcntl.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, openbsd, freebsd, netbsd
+#+build linux, darwin, openbsd, freebsd, netbsd, haiku
package posix
import "core:c"
@@ -343,6 +343,7 @@ when ODIN_OS == .Darwin {
l_type: Lock_Type, /* [PSX] type of lock */
l_whence: c.short, /* [PSX] flag (Whence) of starting offset */
}
+
} else when ODIN_OS == .OpenBSD {
off_t :: distinct c.int64_t
@@ -408,6 +409,72 @@ when ODIN_OS == .Darwin {
l_whence: c.short, /* [PSX] flag (Whence) of starting offset */
}
+} else when ODIN_OS == .Haiku {
+
+ off_t :: distinct c.int64_t
+ pid_t :: distinct c.int32_t
+
+ /* commands that can be passed to fcntl() */
+ F_DUPFD :: 0x0001 /* duplicate fd */
+ F_GETFD :: 0x0002 /* get fd flags */
+ F_SETFD :: 0x0004 /* set fd flags */
+ F_GETFL :: 0x0008 /* get file status flags and access mode */
+ F_SETFL :: 0x0010 /* set file status flags */
+ F_GETLK :: 0x0020 /* get locking information */
+ F_SETLK :: 0x0080 /* set locking information */
+ F_SETLKW :: 0x0100 /* as above, but waits if blocked */
+ F_DUPFD_CLOEXEC :: 0x0200 /* duplicate fd with close on exec set */
+ F_GETOWN :: -1 // NOTE: Not supported.
+ F_SETOWN :: -1 // NOTE: Not supported.
+
+ /* advisory locking types */
+ F_RDLCK :: 0x0040 /* read or shared lock */
+ F_UNLCK :: 0x0200 /* unlock */
+ F_WRLCK :: 0x0400 /* write or exclusive lock */
+
+ /* file descriptor flags for fcntl() */
+ FD_CLOEXEC :: 1
+
+ O_CLOEXEC :: 0x00000040
+ O_CREAT :: 0x0200
+ O_DIRECTORY :: 0x00200000
+ O_EXCL :: 0x0100
+ O_NOCTTY :: 0x1000
+ O_NOFOLLOW :: 0x00080000
+ O_TRUNC :: 0x0400
+
+ _O_TTY_INIT :: 0
+ O_TTY_INIT :: O_Flags{} // NOTE: not defined in the headers
+
+ O_APPEND :: 0x0800
+ O_DSYNC :: 0x040000
+ O_NONBLOCK :: 0x0080
+ O_SYNC :: 0x010000
+ O_RSYNC :: 0x020000
+
+ O_EXEC :: 0x04000000 // NOTE: not defined in the headers
+ O_RDONLY :: 0
+ O_RDWR :: 0x0002
+ O_WRONLY :: 0x0001
+
+ _O_SEARCH :: 0
+ O_SEARCH :: O_Flags{} // NOTE: not defined in the headers
+
+ AT_FDCWD: FD: -100
+
+ AT_EACCESS :: 0x08
+ AT_SYMLINK_NOFOLLOW :: 0x01
+ AT_SYMLINK_FOLLOW :: 0x02
+ AT_REMOVEDIR :: 0x04
+
+ flock :: struct {
+ l_type: Lock_Type, /* [PSX] type of lock */
+ l_whence: c.short, /* [PSX] flag (Whence) of starting offset */
+ l_start: off_t, /* [PSX] relative offset in bytes */
+ l_len: off_t, /* [PSX] size; if 0 then until EOF */
+ l_pid: pid_t, /* [PSX] process ID of the process holding the lock */
+ }
+
} else when ODIN_OS == .Linux {
off_t :: distinct c.int64_t
diff --git a/core/sys/posix/fnmatch.odin b/core/sys/posix/fnmatch.odin
index 2d582705c..04c3d2888 100644
--- a/core/sys/posix/fnmatch.odin
+++ b/core/sys/posix/fnmatch.odin
@@ -1,4 +1,4 @@
-#+build darwin, linux, openbsd, freebsd, netbsd
+#+build darwin, linux, openbsd, freebsd, netbsd, haiku
package posix
import "core:c"
@@ -46,7 +46,7 @@ FNM_Flag_Bits :: enum c.int {
}
FNM_Flags :: bit_set[FNM_Flag_Bits; c.int]
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku {
FNM_NOMATCH :: 1
diff --git a/core/sys/posix/glob.odin b/core/sys/posix/glob.odin
index 7c8009a59..fb90b7546 100644
--- a/core/sys/posix/glob.odin
+++ b/core/sys/posix/glob.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -109,7 +109,7 @@ when ODIN_OS == .Darwin {
GLOB_NOMATCH :: -3
GLOB_NOSPACE :: -1
-} else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD {
+} else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku {
glob_t :: struct {
gl_pathc: c.size_t, /* [PSX] count of paths matched by pattern */
@@ -134,7 +134,7 @@ when ODIN_OS == .Darwin {
GLOB_ERR :: 0x0004
GLOB_MARK :: 0x0008
GLOB_NOCHECK :: 0x0010
- GLOB_NOESCAPE :: 0x2000 when ODIN_OS == .FreeBSD else 0x0100
+ GLOB_NOESCAPE :: 0x2000 when ODIN_OS == .FreeBSD || ODIN_OS == .Haiku else 0x0100
GLOB_NOSORT :: 0x0020
GLOB_ABORTED :: -2
diff --git a/core/sys/posix/grp.odin b/core/sys/posix/grp.odin
index 956ed148b..3694308a0 100644
--- a/core/sys/posix/grp.odin
+++ b/core/sys/posix/grp.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -115,7 +115,7 @@ foreign lib {
getgrnam_r :: proc(name: cstring, grp: ^group, buffer: [^]byte, bufsize: c.size_t, result: ^^group) -> Errno ---
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku || ODIN_OS == .Linux {
gid_t :: distinct c.uint32_t
diff --git a/core/sys/posix/langinfo.odin b/core/sys/posix/langinfo.odin
index 3c001aee0..1fddfe280 100644
--- a/core/sys/posix/langinfo.odin
+++ b/core/sys/posix/langinfo.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -143,7 +143,7 @@ nl_item :: enum nl_item_t {
CRNCYSTR = CRNCYSTR,
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .Haiku {
// NOTE: declared with `_t` so we can enumerate the real `nl_info`.
nl_item_t :: distinct c.int
@@ -210,7 +210,7 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD {
YESEXPR :: 52
NOEXPR :: 53
- CRNCYSTR :: 56
+ CRNCYSTR :: 54 when ODIN_OS == .Haiku else 56
} else when ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
diff --git a/core/sys/posix/libgen.odin b/core/sys/posix/libgen.odin
index 69176a557..2354bf70d 100644
--- a/core/sys/posix/libgen.odin
+++ b/core/sys/posix/libgen.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
when ODIN_OS == .Darwin {
diff --git a/core/sys/posix/locale.odin b/core/sys/posix/locale.odin
index 5b8d7c216..bbe10e803 100644
--- a/core/sys/posix/locale.odin
+++ b/core/sys/posix/locale.odin
@@ -1,4 +1,4 @@
-#+build windows, linux, darwin, netbsd, openbsd, freebsd
+#+build windows, linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c/libc"
diff --git a/core/sys/posix/monetary.odin b/core/sys/posix/monetary.odin
index ee342e211..a444bff09 100644
--- a/core/sys/posix/monetary.odin
+++ b/core/sys/posix/monetary.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
diff --git a/core/sys/posix/netdb.odin b/core/sys/posix/netdb.odin
index 79e13a140..ff1cb9d4c 100644
--- a/core/sys/posix/netdb.odin
+++ b/core/sys/posix/netdb.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -319,7 +319,7 @@ Info_Errno :: enum c.int {
OVERFLOW = EAI_OVERFLOW,
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux || ODIN_OS == .Haiku {
hostent :: struct {
h_name: cstring, /* [PSX] official name of host */
@@ -352,15 +352,28 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
// The highest reserved port number.
IPPORT_RESERVED :: 1024
- addrinfo :: struct {
- ai_flags: Addrinfo_Flags, /* [PSX] input flags */
- ai_family: AF, /* [PSX] address family of socket */
- ai_socktype: Sock, /* [PSX] socket type */
- ai_protocol: Protocol, /* [PSX] protocol of socket */
- ai_addrlen: socklen_t, /* [PSX] length of socket address */
- ai_canonname: cstring, /* [PSX] canonical name of service location */
- ai_addr: ^sockaddr, /* [PSX] binary address */
- ai_next: ^addrinfo, /* [PSX] pointer to next in list */
+ when ODIN_OS == .Linux || ODIN_OS == .OpenBSD {
+ addrinfo :: struct {
+ ai_flags: Addrinfo_Flags, /* [PSX] input flags */
+ ai_family: AF, /* [PSX] address family of socket */
+ ai_socktype: Sock, /* [PSX] socket type */
+ ai_protocol: Protocol, /* [PSX] protocol of socket */
+ ai_addrlen: socklen_t, /* [PSX] length of socket address */
+ ai_addr: ^sockaddr, /* [PSX] binary address */
+ ai_canonname: cstring, /* [PSX] canonical name of service location */
+ ai_next: ^addrinfo, /* [PSX] pointer to next in list */
+ }
+ } else {
+ addrinfo :: struct {
+ ai_flags: Addrinfo_Flags, /* [PSX] input flags */
+ ai_family: AF, /* [PSX] address family of socket */
+ ai_socktype: Sock, /* [PSX] socket type */
+ ai_protocol: Protocol, /* [PSX] protocol of socket */
+ ai_addrlen: socklen_t, /* [PSX] length of socket address */
+ ai_canonname: cstring, /* [PSX] canonical name of service location */
+ ai_addr: ^sockaddr, /* [PSX] binary address */
+ ai_next: ^addrinfo, /* [PSX] pointer to next in list */
+ }
}
when ODIN_OS == .Darwin {
@@ -431,6 +444,23 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
NI_NUMERICSCOPE :: 0x100
NI_DGRAM :: 16
+ } else when ODIN_OS == .Haiku {
+
+ AI_PASSIVE :: 0x001
+ AI_CANONNAME :: 0x002
+ AI_NUMERICHOST :: 0x004
+ AI_NUMERICSERV :: 0x008
+ AI_V4MAPPED :: 0x800
+ AI_ALL :: 0x100
+ AI_ADDRCONFIG :: 0x400
+
+ NI_NOFQDN :: 0x01
+ NI_NUMERICHOST :: 0x02
+ NI_NAMEREQD :: 0x04
+ NI_NUMERICSERV :: 0x08
+ NI_DGRAM :: 0x10
+ NI_NUMERICSCOPE :: 0x40
+
}
when ODIN_OS == .OpenBSD {
diff --git a/core/sys/posix/netinet_in.odin b/core/sys/posix/netinet_in.odin
index a2cf904ce..ec05915de 100644
--- a/core/sys/posix/netinet_in.odin
+++ b/core/sys/posix/netinet_in.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -31,20 +31,31 @@ Protocol :: enum c.int {
UDP = IPPROTO_UDP,
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux || ODIN_OS == .Haiku {
in_addr :: struct {
s_addr: in_addr_t, /* [PSX] big endian address */
}
- in6_addr :: struct {
- using _: struct #raw_union {
- s6_addr: [16]c.uint8_t, /* [PSX] big endian address */
- __u6_addr16: [8]c.uint16_t,
- __u6_addr32: [4]c.uint32_t,
- },
+ when ODIN_OS == .Haiku {
+ in6_addr :: struct #packed {
+ using _: struct #raw_union {
+ s6_addr: [16]c.uint8_t, /* [PSX] big endian address */
+ __u6_addr16: [8]c.uint16_t,
+ __u6_addr32: [4]c.uint32_t,
+ },
+ }
+ } else {
+ in6_addr :: struct {
+ using _: struct #raw_union {
+ s6_addr: [16]c.uint8_t, /* [PSX] big endian address */
+ __u6_addr16: [8]c.uint16_t,
+ __u6_addr32: [4]c.uint32_t,
+ },
+ }
}
+
when ODIN_OS == .Linux {
sockaddr_in :: struct {
@@ -77,12 +88,20 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
} else {
+ when ODIN_OS == .Haiku {
+ @(private)
+ _SIN_ZEROSIZE :: 24
+ } else {
+ @(private)
+ _SIN_ZEROSIZE :: 8
+ }
+
sockaddr_in :: struct {
sin_len: c.uint8_t,
sin_family: sa_family_t, /* [PSX] AF_INET (but a smaller size) */
sin_port: in_port_t, /* [PSX] port number */
sin_addr: in_addr, /* [PSX] IP address */
- sin_zero: [8]c.char,
+ sin_zero: [_SIN_ZEROSIZE]c.char,
}
sockaddr_in6 :: struct {
@@ -99,13 +118,23 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
ipv6mr_interface: c.uint, /* [PSX] interface index */
}
- IPV6_JOIN_GROUP :: 12
- IPV6_LEAVE_GROUP :: 13
- IPV6_MULTICAST_HOPS :: 10
- IPV6_MULTICAST_IF :: 9
- IPV6_MULTICAST_LOOP :: 11
- IPV6_UNICAST_HOPS :: 4
- IPV6_V6ONLY :: 27
+ when ODIN_OS == .Haiku {
+ IPV6_JOIN_GROUP :: 28
+ IPV6_LEAVE_GROUP :: 29
+ IPV6_MULTICAST_HOPS :: 25
+ IPV6_MULTICAST_IF :: 24
+ IPV6_MULTICAST_LOOP :: 26
+ IPV6_UNICAST_HOPS :: 27
+ IPV6_V6ONLY :: 30
+ } else {
+ IPV6_JOIN_GROUP :: 12
+ IPV6_LEAVE_GROUP :: 13
+ IPV6_MULTICAST_HOPS :: 10
+ IPV6_MULTICAST_IF :: 9
+ IPV6_MULTICAST_LOOP :: 11
+ IPV6_UNICAST_HOPS :: 4
+ IPV6_V6ONLY :: 27
+ }
}
diff --git a/core/sys/posix/poll.odin b/core/sys/posix/poll.odin
index 9c3b8b081..44ec767a6 100644
--- a/core/sys/posix/poll.odin
+++ b/core/sys/posix/poll.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "base:intrinsics"
@@ -25,7 +25,11 @@ foreign lib {
poll :: proc(fds: [^]pollfd, nfds: nfds_t, timeout: c.int) -> c.int ---
}
-nfds_t :: c.uint
+when ODIN_OS == .Haiku {
+ nfds_t :: c.ulong
+} else {
+ nfds_t :: c.uint
+}
Poll_Event_Bits :: enum c.short {
// Data other than high-priority data may be read without blocking.
@@ -53,7 +57,7 @@ Poll_Event_Bits :: enum c.short {
}
Poll_Event :: bit_set[Poll_Event_Bits; c.short]
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku {
pollfd :: struct {
fd: FD, /* [PSX] the following descriptor being polled */
@@ -61,17 +65,36 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
revents: Poll_Event, /* [PSX] the output event flags */
}
- POLLIN :: 0x0001
- POLLRDNORM :: 0x0040
- POLLRDBAND :: 0x0080
- POLLPRI :: 0x0002
- POLLOUT :: 0x0004
- POLLWRNORM :: POLLOUT
- POLLWRBAND :: 0x0100
+ when ODIN_OS == .Haiku {
+
+ POLLIN :: 0x0001 /* any readable data available */
+ POLLOUT :: 0x0002 /* file descriptor is writeable */
+ POLLRDNORM :: POLLIN
+ POLLWRNORM :: POLLOUT
+ POLLRDBAND :: 0x0008 /* priority readable data */
+ POLLWRBAND :: 0x0010 /* priority data can be written */
+ POLLPRI :: 0x0020 /* high priority readable data */
+
+ POLLERR :: 0x0004 /* errors pending */
+ POLLHUP :: 0x0080 /* disconnected */
+ POLLNVAL :: 0x1000 /* invalid file descriptor */
+
+ } else {
+
+ POLLIN :: 0x0001
+ POLLRDNORM :: 0x0040
+ POLLRDBAND :: 0x0080
+ POLLPRI :: 0x0002
+ POLLOUT :: 0x0004
+ POLLWRNORM :: POLLOUT
+ POLLWRBAND :: 0x0100
+
+ POLLERR :: 0x0008
+ POLLHUP :: 0x0010
+ POLLNVAL :: 0x0020
+
+ }
- POLLERR :: 0x0008
- POLLHUP :: 0x0010
- POLLNVAL :: 0x0020
} else when ODIN_OS == .Linux {
diff --git a/core/sys/posix/pthread.odin b/core/sys/posix/pthread.odin
index 490064da6..36a3cd7b3 100644
--- a/core/sys/posix/pthread.odin
+++ b/core/sys/posix/pthread.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -554,6 +554,56 @@ when ODIN_OS == .Darwin {
sched_priority: c.int, /* [PSX] process or thread execution scheduling priority */
}
+} else when ODIN_OS == .Haiku {
+
+ PTHREAD_CANCEL_ASYNCHRONOUS :: 2
+ PTHREAD_CANCEL_DEFERRED :: 0
+
+ PTHREAD_CANCEL_DISABLE :: 1
+ PTHREAD_CANCEL_ENABLE :: 0
+
+ PTHREAD_CANCELED :: rawptr(uintptr(1))
+
+ PTHREAD_CREATE_DETACHED :: 0x1
+ PTHREAD_CREATE_JOINABLE :: 0
+
+ PTHREAD_EXPLICIT_SCHED :: 0
+ PTHREAD_INHERIT_SCHED :: 0x4
+
+ PTHREAD_PRIO_INHERIT :: 1
+ PTHREAD_PRIO_NONE :: 0
+ PTHREAD_PRIO_PROTECT :: 2
+
+ PTHREAD_PROCESS_SHARED :: 1
+ PTHREAD_PROCESS_PRIVATE :: 0
+
+ PTHREAD_SCOPE_PROCESS :: 0
+ PTHREAD_SCOPE_SYSTEM :: 0x2
+
+ pthread_t :: distinct rawptr
+ pthread_attr_t :: distinct rawptr
+ pthread_key_t :: distinct c.int
+
+ pthread_mutex_t :: struct {
+ flags: u32,
+ lock: i32,
+ unused: i32,
+ owner: i32,
+ owner_count: i32,
+ }
+
+ pthread_cond_t :: struct {
+ flags: u32,
+ unused: i32,
+ mutex: ^pthread_mutex_t,
+ waiter_count: i32,
+ lock: i32,
+ }
+
+ sched_param :: struct {
+ sched_priority: c.int, /* [PSX] process or thread execution scheduling priority */
+ }
+
} else when ODIN_OS == .Linux {
PTHREAD_CANCEL_DEFERRED :: 0
diff --git a/core/sys/posix/pwd.odin b/core/sys/posix/pwd.odin
index 33cbcd7c5..75d15c899 100644
--- a/core/sys/posix/pwd.odin
+++ b/core/sys/posix/pwd.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -176,4 +176,16 @@ when ODIN_OS == .Darwin || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
pw_shell: cstring, /* Shell program. */
}
+} else when ODIN_OS == .Haiku {
+
+ passwd :: struct {
+ pw_name: cstring, /* [PSX] user name */
+ pw_passwd: cstring, /* encrypted password */
+ pw_uid: uid_t, /* [PSX] user uid */
+ pw_gid: gid_t, /* [PSX] user gid */
+ pw_dir: cstring, /* Home directory. */
+ pw_shell: cstring, /* Shell program. */
+ pw_gecos: cstring, /* Real name. */
+ }
+
}
diff --git a/core/sys/posix/sched.odin b/core/sys/posix/sched.odin
index e91178b09..82b335653 100644
--- a/core/sys/posix/sched.odin
+++ b/core/sys/posix/sched.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -101,4 +101,11 @@ when ODIN_OS == .Darwin {
SCHED_FIFO :: 1
SCHED_RR :: 2
+} else when ODIN_OS == .Haiku {
+
+ SCHED_FIFO :: 1
+ SCHED_RR :: 2
+ // SCHED_SPORADIC :: 3 NOTE: not a thing on freebsd, netbsd and probably others, leaving it out
+ SCHED_OTHER :: 4
+
}
diff --git a/core/sys/posix/signal.odin b/core/sys/posix/signal.odin
index 4ba4e9943..d2f737946 100644
--- a/core/sys/posix/signal.odin
+++ b/core/sys/posix/signal.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "base:intrinsics"
@@ -565,7 +565,7 @@ when ODIN_OS == .Darwin {
SS_ONSTACK :: 0x0001
SS_DISABLE :: 0x0004
- when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm32 {
+ when ODIN_ARCH == .arm64 || ODIN_ARCH == .arm32 {
MINSIGSTKSZ :: 1024 * 4
} else when ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 {
MINSIGSTKSZ :: 512 * 4
@@ -1180,4 +1180,151 @@ when ODIN_OS == .Darwin {
SI_TIMER :: -2
SI_MESGQ :: -3
SI_ASYNCIO :: -4
+
+} else when ODIN_OS == .Haiku {
+
+ // Request that signal be held
+ SIG_HOLD :: rawptr(uintptr(3))
+
+ uid_t :: distinct c.uint32_t
+ sigset_t :: distinct u64
+
+ SIGHUP :: 1 // hangup -- tty is gone!
+ //SIGINT :: 2 // interrupt
+ SIGQUIT :: 3 // `quit' special character typed in tty
+ //SIGILL :: 4 // illegal instruction
+ SIGCHLD :: 5 // child process exited
+ //SIGABRT :: 6 // abort() called, dont' catch
+ SIGPIPE :: 7 // write to a pipe w/no readers
+ //SIGFPE :: 8 // floating point exception
+ SIGKILL :: 9 // kill a team (not catchable)
+ SIGSTOP :: 10 // suspend a thread (not catchable)
+ //SIGSEGV :: 11 // segmentation violation (read: invalid pointer)
+ SIGCONT :: 12 // continue execution if suspended
+ SIGTSTP :: 13 // `stop' special character typed in tty
+ SIGALRM :: 14 // an alarm has gone off (see alarm())
+ //SIGTERM :: 15 // termination requested
+ SIGTTIN :: 16 // read of tty from bg process
+ SIGTTOU :: 17 // write to tty from bg process
+ SIGUSR1 :: 18 // app defined signal 1
+ SIGUSR2 :: 19 // app defined signal 2
+ SIGWINCH :: 20 // tty window size changed
+ SIGKILLTHR :: 21 // be specific: kill just the thread, not team
+ SIGTRAP :: 22 // Trace/breakpoint trap
+ SIGPOLL :: 23 // Pollable event
+ SIGPROF :: 24 // Profiling timer expired
+ SIGSYS :: 25 // Bad system call
+ SIGURG :: 26 // High bandwidth data is available at socket
+ SIGVTALRM :: 27 // Virtual timer expired
+ SIGXCPU :: 28 // CPU time limit exceeded
+ SIGXFSZ :: 29 // File size limit exceeded
+ SIGBUS :: 30 // access to undefined portion of a memory object
+
+ // NOTE: this is actually defined as `sigaction`, but due to the function with the same name
+ // `_t` has been added.
+
+ sigaction_t :: struct {
+ using _: struct #raw_union {
+ sa_handler: proc "c" (Signal), /* [PSX] signal-catching function or one of the SIG_IGN or SIG_DFL */
+ sa_sigaction: proc "c" (Signal, ^siginfo_t, rawptr), /* [PSX] signal-catching function */
+ },
+ sa_mask: sigset_t, /* [PSX] set of signals to be blocked during execution of the signal handling function */
+ sa_flags: SA_Flags, /* [PSX] special flags */
+ sa_userdata: rawptr, /* will be passed to the signal handler, BeOS extension */
+ }
+
+ SIG_BLOCK :: 1
+ SIG_UNBLOCK :: 2
+ SIG_SETMASK :: 3
+
+ SA_NOCLDSTOP :: 0x01
+ SA_NOCLDWAIT :: 0x02
+ SA_RESETHAND :: 0x04
+ SA_NODEFER :: 0x08
+ SA_RESTART :: 0x10
+ SA_ONSTACK :: 0x20
+ SA_SIGINFO :: 0x40
+
+ SS_ONSTACK :: 1
+ SS_DISABLE :: 2
+
+ MINSIGSTKSZ :: 8192
+ SIGSTKSZ :: 16384
+
+ stack_t :: struct {
+ ss_sp: rawptr, /* [PSX] stack base or pointer */
+ ss_size: c.size_t, /* [PSX] stack size */
+ ss_flags: SS_Flags, /* [PSX] flags */
+ }
+
+ siginfo_t :: struct {
+ si_signo: Signal, /* [PSX] signal number */
+ si_code: struct #raw_union { /* [PSX] specific more detailed codes per signal */
+ ill: ILL_Code,
+ fpe: FPE_Code,
+ segv: SEGV_Code,
+ bus: BUS_Code,
+ trap: TRAP_Code,
+ chld: CLD_Code,
+ poll: POLL_Code,
+ any: Any_Code,
+ },
+ si_errno: Errno, /* [PSX] errno value associated with this signal */
+ si_pid: pid_t, /* sending process ID */
+ si_uid: uid_t, /* real user ID of sending process */
+ si_addr: rawptr, /* address of faulting instruction */
+ si_status: c.int, /* exit value or signal */
+ si_band: c.long, /* band event for SIGPOLL */
+ si_value: sigval, /* signal value */
+ }
+
+ /* any signal */
+ SI_USER :: 0 /* signal sent by user */
+ SI_QUEUE :: 1 /* signal sent by sigqueue() */
+ SI_TIMER :: 2 /* signal sent on timer_settime() timeout */
+ SI_ASYNCIO :: 3 /* signal sent on asynchronous I/O completion */
+ SI_MESGQ :: 4 /* signal sent on arrival of message on empty message queue */
+ /* SIGILL */
+ ILL_ILLOPC :: 10 /* illegal opcode */
+ ILL_ILLOPN :: 11 /* illegal operand */
+ ILL_ILLADR :: 12 /* illegal addressing mode */
+ ILL_ILLTRP :: 13 /* illegal trap */
+ ILL_PRVOPC :: 14 /* privileged opcode */
+ ILL_PRVREG :: 15 /* privileged register */
+ ILL_COPROC :: 16 /* coprocessor error */
+ ILL_BADSTK :: 17 /* internal stack error */
+ /* SIGFPE */
+ FPE_INTDIV :: 20 /* integer division by zero */
+ FPE_INTOVF :: 21 /* integer overflow */
+ FPE_FLTDIV :: 22 /* floating-point division by zero */
+ FPE_FLTOVF :: 23 /* floating-point overflow */
+ FPE_FLTUND :: 24 /* floating-point underflow */
+ FPE_FLTRES :: 25 /* floating-point inexact result */
+ FPE_FLTINV :: 26 /* invalid floating-point operation */
+ FPE_FLTSUB :: 27 /* subscript out of range */
+ /* SIGSEGV */
+ SEGV_MAPERR :: 30 /* address not mapped to object */
+ SEGV_ACCERR :: 31 /* invalid permissions for mapped object */
+ /* SIGBUS */
+ BUS_ADRALN :: 40 /* invalid address alignment */
+ BUS_ADRERR :: 41 /* nonexistent physical address */
+ BUS_OBJERR :: 42 /* object-specific hardware error */
+ /* SIGTRAP */
+ TRAP_BRKPT :: 50 /* process breakpoint */
+ TRAP_TRACE :: 51 /* process trace trap. */
+ /* SIGCHLD */
+ CLD_EXITED :: 60 /* child exited */
+ CLD_KILLED :: 61 /* child terminated abnormally without core dump */
+ CLD_DUMPED :: 62 /* child terminated abnormally with core dump */
+ CLD_TRAPPED :: 63 /* traced child trapped */
+ CLD_STOPPED :: 64 /* child stopped */
+ CLD_CONTINUED :: 65 /* stopped child continued */
+ /* SIGPOLL */
+ POLL_IN :: 70 /* input available */
+ POLL_OUT :: 71 /* output available */
+ POLL_MSG :: 72 /* input message available */
+ POLL_ERR :: 73 /* I/O error */
+ POLL_PRI :: 74 /* high priority input available */
+ POLL_HUP :: 75 /* device disconnected */
+
}
diff --git a/core/sys/posix/signal_libc.odin b/core/sys/posix/signal_libc.odin
index aef22da29..7a054ddd7 100644
--- a/core/sys/posix/signal_libc.odin
+++ b/core/sys/posix/signal_libc.odin
@@ -1,4 +1,4 @@
-#+build linux, windows, darwin, netbsd, openbsd, freebsd
+#+build linux, windows, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "base:intrinsics"
diff --git a/core/sys/posix/stdio_libc.odin b/core/sys/posix/stdio_libc.odin
index fbd949b2c..12706970d 100644
--- a/core/sys/posix/stdio_libc.odin
+++ b/core/sys/posix/stdio_libc.odin
@@ -1,4 +1,4 @@
-#+build linux, windows, linux, darwin, netbsd, openbsd, freebsd
+#+build linux, windows, linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
diff --git a/core/sys/posix/stdlib.odin b/core/sys/posix/stdlib.odin
index 640c70b5a..5f1ae1908 100644
--- a/core/sys/posix/stdlib.odin
+++ b/core/sys/posix/stdlib.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "base:intrinsics"
diff --git a/core/sys/posix/stdlib_libc.odin b/core/sys/posix/stdlib_libc.odin
index fa4d925b2..6574026f4 100644
--- a/core/sys/posix/stdlib_libc.odin
+++ b/core/sys/posix/stdlib_libc.odin
@@ -1,4 +1,4 @@
-#+build linux, windows, darwin, netbsd, openbsd, freebsd
+#+build linux, windows, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "base:intrinsics"
diff --git a/core/sys/posix/string.odin b/core/sys/posix/string.odin
index 96b6a9007..3f9dbb43e 100644
--- a/core/sys/posix/string.odin
+++ b/core/sys/posix/string.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
diff --git a/core/sys/posix/string_libc.odin b/core/sys/posix/string_libc.odin
index 336352cbc..72164cc4c 100644
--- a/core/sys/posix/string_libc.odin
+++ b/core/sys/posix/string_libc.odin
@@ -1,4 +1,4 @@
-#+build linux, windows, darwin, netbsd, openbsd, freebsd
+#+build linux, windows, darwin, netbsd, openbsd, freebsd, haiku
package posix
when ODIN_OS == .Windows {
diff --git a/core/sys/posix/sys_ipc.odin b/core/sys/posix/sys_ipc.odin
index 0f7ec06c5..bf5938ce1 100644
--- a/core/sys/posix/sys_ipc.odin
+++ b/core/sys/posix/sys_ipc.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -111,4 +111,27 @@ when ODIN_OS == .Darwin {
IPC_SET :: 1
IPC_STAT :: 2
+} else when ODIN_OS == .Haiku {
+
+ key_t :: distinct c.int32_t
+
+ ipc_perm :: struct {
+ key: key_t,
+ uid: uid_t, /* [PSX] owner's user ID */
+ gid: gid_t, /* [PSX] owner's group ID */
+ cuid: uid_t, /* [PSX] creator's user ID */
+ cgid: gid_t, /* [PSX] creator's group ID */
+ mode: mode_t, /* [PSX] read/write perms */
+ }
+
+ IPC_CREAT :: 0o01000
+ IPC_EXCL :: 0o02000
+ IPC_NOWAIT :: 0o04000
+
+ IPC_PRIVATE :: key_t(0)
+
+ IPC_RMID :: 0
+ IPC_SET :: 1
+ IPC_STAT :: 2
+
}
diff --git a/core/sys/posix/sys_msg.odin b/core/sys/posix/sys_msg.odin
index 0e78777f9..c578b1fc6 100644
--- a/core/sys/posix/sys_msg.odin
+++ b/core/sys/posix/sys_msg.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -171,4 +171,22 @@ when ODIN_OS == .Darwin {
__unused: [2]c.ulong,
}
+} else when ODIN_OS == .Haiku {
+
+ msgqnum_t :: distinct c.uint32_t
+ msglen_t :: distinct c.uint32_t
+
+ MSG_NOERROR :: 0o10000
+
+ msqid_ds :: struct {
+ msg_perm: ipc_perm, /* [PSX] operation permission structure */
+ msg_qnum: msgqnum_t, /* [PSX] number of messages currently on queue */
+ msg_qbytes: msglen_t, /* [PSX] maximum number of bytes allowed on queue */
+ msg_lspid: pid_t, /* [PSX] process ID of last msgsnd() */
+ msg_lrpid: pid_t, /* [PSX] process ID of last msgrcv() */
+ msg_stime: time_t, /* [PSX] time of last msgsnd() */
+ msg_rtime: time_t, /* [PSX] time of last msgrcv() */
+ msg_ctime: time_t, /* [PSX] time of last change */
+ }
+
}
diff --git a/core/sys/posix/sys_resource.odin b/core/sys/posix/sys_resource.odin
index 9af2a929b..ae478382a 100644
--- a/core/sys/posix/sys_resource.odin
+++ b/core/sys/posix/sys_resource.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -96,15 +96,26 @@ when ODIN_OS == .NetBSD {
@(private) LGETRUSAGE :: "getrusage"
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux || ODIN_OS == .Haiku {
PRIO_PROCESS :: 0
PRIO_PGRP :: 1
PRIO_USER :: 2
- rlim_t :: distinct c.uint64_t
+ when ODIN_OS == .Haiku {
+ rlim_t :: distinct c.ulong
+ } else {
+ rlim_t :: distinct c.uint64_t
+ }
- RLIM_INFINITY :: ~rlim_t(0) when ODIN_OS == .Linux else (rlim_t(1) << 63) - 1
+ when ODIN_OS == .Haiku {
+ RLIM_INFINITY :: rlim_t(0xFFFFFFFF)
+ } else when ODIN_OS == .Linux {
+ RLIM_INFINITY :: ~rlim_t(0)
+ } else {
+ RLIM_INFINITY :: (rlim_t(1) << 63) - 1
+ }
+
RLIM_SAVED_MAX :: RLIM_INFINITY
RLIM_SAVED_CUR :: RLIM_INFINITY
@@ -140,19 +151,29 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
ru_nivcsw: c.long, /* involuntary " */
}
- RLIMIT_CORE :: 4
- RLIMIT_CPU :: 0
- RLIMIT_DATA :: 2
- RLIMIT_FSIZE :: 1
- RLIMIT_NOFILE :: 7 when ODIN_OS == .Linux else 8
- RLIMIT_STACK :: 3
-
- when ODIN_OS == .Linux {
- RLIMIT_AS :: 9
- } else when ODIN_OS == .Darwin || ODIN_OS == .OpenBSD {
- RLIMIT_AS :: 5
+ when ODIN_OS == .Haiku {
+ RLIMIT_CORE :: 0
+ RLIMIT_CPU :: 1
+ RLIMIT_DATA :: 2
+ RLIMIT_FSIZE :: 3
+ RLIMIT_NOFILE :: 4
+ RLIMIT_STACK :: 5
+ RLIMIT_AS :: 6
} else {
- RLIMIT_AS :: 10
+ RLIMIT_CORE :: 4
+ RLIMIT_CPU :: 0
+ RLIMIT_DATA :: 2
+ RLIMIT_FSIZE :: 1
+ RLIMIT_NOFILE :: 7 when ODIN_OS == .Linux else 8
+ RLIMIT_STACK :: 3
+
+ when ODIN_OS == .Linux {
+ RLIMIT_AS :: 9
+ } else when ODIN_OS == .Darwin || ODIN_OS == .OpenBSD {
+ RLIMIT_AS :: 5
+ } else {
+ RLIMIT_AS :: 10
+ }
}
}
diff --git a/core/sys/posix/sys_select.odin b/core/sys/posix/sys_select.odin
index 2058ee777..a75e58de6 100644
--- a/core/sys/posix/sys_select.odin
+++ b/core/sys/posix/sys_select.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "base:intrinsics"
@@ -56,9 +56,9 @@ when ODIN_OS == .NetBSD {
LSELECT :: "select"
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux || ODIN_OS == .Haiku {
- suseconds_t :: distinct (c.int32_t when ODIN_OS == .Darwin || ODIN_OS == .NetBSD else c.long)
+ suseconds_t :: distinct (c.int32_t when ODIN_OS == .Darwin || ODIN_OS == .NetBSD || ODIN_OS == .Haiku else c.long)
timeval :: struct {
tv_sec: time_t, /* [PSX] seconds */
@@ -75,8 +75,14 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
@(private)
ALIGN :: align_of(c.long) when ODIN_OS == .FreeBSD || ODIN_OS == .Linux else align_of(c.int32_t)
- fd_set :: struct #align(ALIGN) {
- fds_bits: [(FD_SETSIZE / __NFDBITS) when (FD_SETSIZE % __NFDBITS) == 0 else (FD_SETSIZE / __NFDBITS) + 1]c.int32_t,
+ when ODIN_OS == .Haiku {
+ fd_set :: struct #align(ALIGN) {
+ fds_bits: [(FD_SETSIZE + (__NFDBITS - 1)) / __NFDBITS]c.int32_t,
+ }
+ } else {
+ fd_set :: struct #align(ALIGN) {
+ fds_bits: [(FD_SETSIZE / __NFDBITS) when (FD_SETSIZE % __NFDBITS) == 0 else (FD_SETSIZE / __NFDBITS) + 1]c.int32_t,
+ }
}
@(private)
diff --git a/core/sys/posix/sys_sem.odin b/core/sys/posix/sys_sem.odin
index 6b695e766..069315f87 100644
--- a/core/sys/posix/sys_sem.odin
+++ b/core/sys/posix/sys_sem.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -154,4 +154,30 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
sem_flg: c.short, /* [PSX] operation flags */
}
+} else when ODIN_OS == .Haiku {
+
+ SEM_UNDO :: 10 // undo the operation on exit
+
+ // Commands for `semctl'.
+ GETPID :: 3
+ GETVAL :: 4
+ GETALL :: 5
+ GETNCNT :: 6
+ GETZCNT :: 7
+ SETVAL :: 8
+ SETALL :: 9
+
+ semid_ds :: struct {
+ sem_perm: ipc_perm, // [PSX] operation permission structure
+ sem_nsems: c.ushort, // [PSX] number of semaphores in set
+ sem_otime: time_t, // [PSX] last semop()
+ sem_ctime: time_t, // [PSX] last time changed by semctl()
+ }
+
+ sembuf :: struct {
+ sem_num: c.ushort, /* [PSX] semaphore number */
+ sem_op: c.short, /* [PSX] semaphore operation */
+ sem_flg: c.short, /* [PSX] operation flags */
+ }
+
}
diff --git a/core/sys/posix/sys_socket.odin b/core/sys/posix/sys_socket.odin
index 4dd6074a3..0645893d0 100644
--- a/core/sys/posix/sys_socket.odin
+++ b/core/sys/posix/sys_socket.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -328,24 +328,32 @@ when ODIN_OS == .NetBSD {
@(private) LSOCKET :: "socket"
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux || ODIN_OS == .Haiku {
socklen_t :: distinct c.uint
+ when ODIN_OS == .Haiku {
+ @(private)
+ _SA_DATASIZE :: 30
+ } else {
+ @(private)
+ _SA_DATASIZE :: 14
+ }
+
when ODIN_OS == .Linux {
_sa_family_t :: distinct c.ushort
sockaddr :: struct {
- sa_family: sa_family_t, /* [PSX] address family */
- sa_data: [14]c.char, /* [PSX] socket address */
+ sa_family: sa_family_t, /* [PSX] address family */
+ sa_data: [_SA_DATASIZE]c.char, /* [PSX] socket address */
}
} else {
_sa_family_t :: distinct c.uint8_t
sockaddr :: struct {
- sa_len: c.uint8_t, /* total length */
- sa_family: sa_family_t, /* [PSX] address family */
- sa_data: [14]c.char, /* [PSX] socket address */
+ sa_len: c.uint8_t, /* total length */
+ sa_family: sa_family_t, /* [PSX] address family */
+ sa_data: [_SA_DATASIZE]c.char, /* [PSX] socket address */
}
}
@@ -355,6 +363,11 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
_SS_PAD1SIZE :: 6
@(private)
_SS_PAD2SIZE :: 240
+ } else when ODIN_OS == .Haiku {
+ @(private)
+ _SS_PAD1SIZE :: 6
+ @(private)
+ _SS_PAD2SIZE :: 112
} else when ODIN_OS == .Linux {
@(private)
_SS_SIZE :: 128
@@ -486,6 +499,26 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
SO_RCVTIMEO :: 66
SO_SNDTIMEO :: 67
+ } else when ODIN_OS == .Haiku {
+ SOL_SOCKET :: -1
+
+ SO_ACCEPTCONN :: 0x00000001
+ SO_BROADCAST :: 0x00000002
+ SO_DEBUG :: 0x00000004
+ SO_DONTROUTE :: 0x00000008
+ SO_ERROR :: 0x40000007
+ SO_KEEPALIVE :: 0x00000010
+ SO_OOBINLINE :: 0x00000020
+ SO_RCVBUF :: 0x40000004
+ SO_RCVLOWAT :: 0x40000005
+ SO_REUSEADDR :: 0x00000040
+ SO_SNDBUF :: 0x40000001
+ SO_SNDLOWAT :: 0x40000002
+ SO_TYPE :: 0x40000008
+
+ SO_LINGER :: 0x00000200
+ SO_RCVTIMEO :: 0x40000006
+ SO_SNDTIMEO :: 0x40000003
} else {
SOL_SOCKET :: 0xffff
@@ -523,7 +556,11 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
}
// The maximum backlog queue length for listen().
- SOMAXCONN :: 128
+ when ODIN_OS == .Haiku {
+ SOMAXCONN :: 32
+ } else {
+ SOMAXCONN :: 128
+ }
when ODIN_OS == .Linux {
MSG_CTRUNC :: 0x008
@@ -549,11 +586,18 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
MSG_NOSIGNAL :: 0x00020000
} else when ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
MSG_NOSIGNAL :: 0x0400
+ } else when ODIN_OS == .Haiku {
+ MSG_NOSIGNAL :: 0x800
}
}
- AF_INET :: 2
- AF_UNIX :: 1
+ when ODIN_OS == .Haiku {
+ AF_INET :: 1
+ AF_UNIX :: 9
+ } else {
+ AF_INET :: 2
+ AF_UNIX :: 1
+ }
when ODIN_OS == .Darwin {
AF_INET6 :: 30
@@ -563,6 +607,8 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
AF_INET6 :: 24
} else when ODIN_OS == .Linux {
AF_INET6 :: 10
+ } else when ODIN_OS == .Haiku {
+ AF_INET6 :: 5
}
SHUT_RD :: 0
diff --git a/core/sys/posix/sys_stat.odin b/core/sys/posix/sys_stat.odin
index 61b98ef35..265356e54 100644
--- a/core/sys/posix/sys_stat.odin
+++ b/core/sys/posix/sys_stat.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -428,6 +428,36 @@ when ODIN_OS == .Darwin {
UTIME_NOW :: -2
UTIME_OMIT :: -1
+} else when ODIN_OS == .Haiku {
+
+ dev_t :: distinct c.int32_t
+ nlink_t :: distinct c.int32_t
+ _mode_t :: distinct c.uint32_t
+ blkcnt_t :: distinct c.int64_t
+ blksize_t :: distinct c.int32_t
+ ino_t :: distinct c.int64_t
+
+ stat_t :: struct {
+ st_dev: dev_t, /* [PSX] ID of device containing file */
+ st_ino: ino_t, /* [PSX] file serial number */
+ st_mode: mode_t, /* [PSX] mode of file */
+ st_nlink: nlink_t, /* [PSX] number of hard links */
+ st_uid: uid_t, /* [PSX] user ID of the file */
+ st_gid: gid_t, /* [PSX] group ID of the file */
+ st_size: off_t, /* [PSX] file size, in bytes */
+ st_rdev: dev_t, /* [PSX] device ID */
+ st_blksize: blksize_t, /* [PSX] optimal blocksize for I/O */
+ st_atim: timespec, /* [PSX] time of last access */
+ st_mtim: timespec, /* [PSX] time of last data modification */
+ st_ctim: timespec, /* [PSX] time of last status change */
+ st_crtim: timespec, /* [PSX] time of last status change */
+ st_type: c.uint32_t,
+ st_blocks: blkcnt_t, /* [PSX] blocks allocated for file */
+ }
+
+ UTIME_NOW :: 1000000000
+ UTIME_OMIT :: 1000000001
+
} else when ODIN_OS == .Linux {
dev_t :: distinct u64
diff --git a/core/sys/posix/sys_time.odin b/core/sys/posix/sys_time.odin
index 3036352aa..94eafec85 100644
--- a/core/sys/posix/sys_time.odin
+++ b/core/sys/posix/sys_time.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -78,4 +78,15 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
ITIMER_VIRTUAL :: 1
ITIMER_PROF :: 2
+} else when ODIN_OS == .Haiku {
+
+ itimerval :: struct {
+ it_interval: timeval, /* [PSX] timer interval */
+ it_value: timeval, /* [PSX] current value */
+ }
+
+ ITIMER_REAL :: 1
+ ITIMER_VIRTUAL :: 2
+ ITIMER_PROF :: 3
+
}
diff --git a/core/sys/posix/sys_times.odin b/core/sys/posix/sys_times.odin
index 113e3f963..73db489a7 100644
--- a/core/sys/posix/sys_times.odin
+++ b/core/sys/posix/sys_times.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
when ODIN_OS == .Darwin {
@@ -25,7 +25,7 @@ when ODIN_OS == .NetBSD {
@(private) LTIMES :: "times"
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux || ODIN_OS == .Haiku {
tms :: struct {
tms_utime: clock_t, /* [PSX] user CPU time */
diff --git a/core/sys/posix/sys_uio.odin b/core/sys/posix/sys_uio.odin
index a0ad2934e..5770f8058 100644
--- a/core/sys/posix/sys_uio.odin
+++ b/core/sys/posix/sys_uio.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -31,7 +31,7 @@ foreign libc {
writev :: proc(fildes: FD, iov: [^]iovec, iovcnt: c.int) -> c.ssize_t ---
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux || ODIN_OS == .Haiku {
iovec :: struct {
iov_base: rawptr, /* [PSX] base address of I/O memory region */
diff --git a/core/sys/posix/sys_un.odin b/core/sys/posix/sys_un.odin
index ca5c4ee31..167bf3ce1 100644
--- a/core/sys/posix/sys_un.odin
+++ b/core/sys/posix/sys_un.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -20,4 +20,12 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
sun_path: [108]c.char, /* [PSX] socket pathname */
}
+} else when ODIN_OS == .Haiku {
+
+ sockaddr_un :: struct {
+ sun_len: c.uint8_t,
+ sun_family: sa_family_t, /* [PSX] address family */
+ sun_path: [126]c.char, /* [PSX] socket pathname */
+ }
+
}
diff --git a/core/sys/posix/sys_utsname.odin b/core/sys/posix/sys_utsname.odin
index 64930160f..5ea8807a7 100644
--- a/core/sys/posix/sys_utsname.odin
+++ b/core/sys/posix/sys_utsname.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -38,10 +38,10 @@ foreign lib {
uname :: proc(uname: ^utsname) -> c.int ---
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku {
@(private)
- _SYS_NAMELEN :: 256
+ _SYS_NAMELEN :: 32 when ODIN_OS == .Haiku else 256
utsname :: struct {
sysname: [_SYS_NAMELEN]c.char `fmt:"s,0"`, /* [PSX] name of OS */
diff --git a/core/sys/posix/sys_wait.odin b/core/sys/posix/sys_wait.odin
index 812bd8c62..d3bcdfddd 100644
--- a/core/sys/posix/sys_wait.odin
+++ b/core/sys/posix/sys_wait.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -442,4 +442,56 @@ when ODIN_OS == .Darwin {
_WIFCONTINUED :: #force_inline proc "contextless" (x: c.int) -> bool {
return x == 0xffff
}
+
+} else when ODIN_OS == .Haiku {
+
+ id_t :: distinct c.int32_t
+
+ WCONTINUED :: 0x04
+ WNOHANG :: 0x01
+ WUNTRACED :: 0x02
+
+ WEXITED :: 0x08
+ WNOWAIT :: 0x20
+ WSTOPPED :: 0x10
+
+ _P_ALL :: 0
+ _P_PID :: 1
+ _P_PGID :: 2
+
+ @(private)
+ _WIFEXITED :: #force_inline proc "contextless" (x: c.int) -> bool {
+ return (x & ~(c.int)(0xff)) == 0
+ }
+
+ @(private)
+ _WEXITSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int {
+ return x & 0xff
+ }
+
+ @(private)
+ _WIFSIGNALED :: #force_inline proc "contextless" (x: c.int) -> bool {
+ return ((x >> 8) & 0xff) != 0
+ }
+
+ @(private)
+ _WTERMSIG :: #force_inline proc "contextless" (x: c.int) -> Signal {
+ return Signal((x >> 8) & 0xff)
+ }
+
+ @(private)
+ _WIFSTOPPED :: #force_inline proc "contextless" (x: c.int) -> bool {
+ return ((x >> 16) & 0xff) != 0
+ }
+
+ @(private)
+ _WSTOPSIG :: #force_inline proc "contextless" (x: c.int) -> Signal {
+ return Signal((x >> 16) & 0xff)
+ }
+
+ @(private)
+ _WIFCONTINUED :: #force_inline proc "contextless" (x: c.int) -> bool {
+ return (x & 0x20000) != 0
+ }
+
}
diff --git a/core/sys/posix/termios.odin b/core/sys/posix/termios.odin
index 0c07eceb9..4ca884e87 100644
--- a/core/sys/posix/termios.odin
+++ b/core/sys/posix/termios.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -138,17 +138,30 @@ CLocal_Flag_Bits :: enum tcflag_t {
}
CLocal_Flags :: bit_set[CLocal_Flag_Bits; tcflag_t]
-CControl_Flag_Bits :: enum tcflag_t {
- // CS5 = log2(CS5), /* 5 bits (pseudo) (default) */
- CS6 = log2(CS6), /* 6 bits */
- CS7 = log2(CS7), /* 7 bits */
- CS8 = log2(CS8), /* 8 bits */
- CSTOPB = log2(CSTOPB), /* send 2 stop bits */
- CREAD = log2(CREAD), /* enable receiver */
- PARENB = log2(PARENB), /* parity enable */
- PARODD = log2(PARODD), /* odd parity, else even */
- HUPCL = log2(HUPCL), /* hang up on last close */
- CLOCAL = log2(CLOCAL), /* ignore modem status lines */
+when ODIN_OS == .Haiku {
+ CControl_Flag_Bits :: enum tcflag_t {
+ // CS7 = log2(CS7), /* 7 bits (default) */
+ CS8 = log2(CS8), /* 8 bits */
+ CSTOPB = log2(CSTOPB), /* send 2 stop bits */
+ CREAD = log2(CREAD), /* enable receiver */
+ PARENB = log2(PARENB), /* parity enable */
+ PARODD = log2(PARODD), /* odd parity, else even */
+ HUPCL = log2(HUPCL), /* hang up on last close */
+ CLOCAL = log2(CLOCAL), /* ignore modem status lines */
+ }
+} else {
+ CControl_Flag_Bits :: enum tcflag_t {
+ // CS5 = log2(CS5), /* 5 bits (pseudo) (default) */
+ CS6 = log2(CS6), /* 6 bits */
+ CS7 = log2(CS7), /* 7 bits */
+ CS8 = log2(CS8), /* 8 bits */
+ CSTOPB = log2(CSTOPB), /* send 2 stop bits */
+ CREAD = log2(CREAD), /* enable receiver */
+ PARENB = log2(PARENB), /* parity enable */
+ PARODD = log2(PARODD), /* odd parity, else even */
+ HUPCL = log2(HUPCL), /* hang up on last close */
+ CLOCAL = log2(CLOCAL), /* ignore modem status lines */
+ }
}
CControl_Flags :: bit_set[CControl_Flag_Bits; tcflag_t]
@@ -597,4 +610,151 @@ when ODIN_OS == .Darwin {
TCOOFF :: 0
TCOON :: 1
+} else when ODIN_OS == .Haiku {
+
+ cc_t :: distinct c.uchar
+ _speed_t :: distinct c.uint32_t
+ tcflag_t :: distinct c.uint16_t
+
+ // Same as speed_t, but 16-bit.
+ CSpeed :: enum tcflag_t {
+ B0 = B0,
+ B50 = B50,
+ B75 = B75,
+ B110 = B110,
+ B134 = B134,
+ B150 = B150,
+ B200 = B200,
+ B300 = B300,
+ B600 = B600,
+ B1200 = B1200,
+ B1800 = B1800,
+ B2400 = B2400,
+ B4800 = B4800,
+ B9600 = B9600,
+ B19200 = B19200,
+ B38400 = B38400,
+ }
+
+ termios :: struct {
+ c_iflag: CInput_Flags, /* [XBD] input flags */
+ c_ispeed: CSpeed, /* input speed */
+ c_oflag: COutput_Flags, /* [XBD] output flags */
+ c_ospeed: CSpeed, /* output speed */
+ c_cflag: CControl_Flags, /* [XBD] control flags */
+ c_ispeed_high: tcflag_t, /* high word of input baudrate */
+ c_lflag: CLocal_Flags, /* [XBD] local flag */
+ c_ospeed_high: tcflag_t, /* high word of output baudrate */
+ c_line: c.char,
+ _padding: c.uchar,
+ _padding2: c.uchar,
+ c_cc: [NCCS]cc_t,
+ }
+
+ NCCS :: 11
+
+ VINTR :: 0
+ VQUIT :: 1
+ VERASE :: 2
+ VKILL :: 3
+ VEOF :: 4
+ VEOL :: 5
+ VMIN :: 4
+ VTIME :: 5
+ VEOL2 :: 6
+ VSWTCH :: 7
+ VSTART :: 8
+ VSTOP :: 9
+ VSUSP :: 10
+
+ IGNBRK :: 0x01 /* ignore break condition */
+ BRKINT :: 0x02 /* break sends interrupt */
+ IGNPAR :: 0x04 /* ignore characters with parity errors */
+ PARMRK :: 0x08 /* mark parity errors */
+ INPCK :: 0x10 /* enable input parity checking */
+ ISTRIP :: 0x20 /* strip high bit from characters */
+ INLCR :: 0x40 /* maps newline to CR on input */
+ IGNCR :: 0x80 /* ignore carriage returns */
+ ICRNL :: 0x100 /* map CR to newline on input */
+ IXON :: 0x400 /* enable input SW flow control */
+ IXANY :: 0x800 /* any character will restart input */
+ IXOFF :: 0x1000 /* enable output SW flow control */
+
+ OPOST :: 0x01 /* enable postprocessing of output */
+ ONLCR :: 0x04 /* map NL to CR-NL on output */
+ OCRNL :: 0x08 /* map CR to NL on output */
+ ONOCR :: 0x10 /* no CR output when at column 0 */
+ ONLRET :: 0x20 /* newline performs CR function */
+ OFILL :: 0x40 /* use fill characters for delays */
+ OFDEL :: 0x80 /* Fills are DEL, otherwise NUL */
+ _NLDLY :: 0x100 /* Newline delays: */
+ NL0 :: 0x000
+ NL1 :: 0x100
+ _CRDLY :: 0x600 /* Carriage return delays: */
+ CR0 :: 0x000
+ CR1 :: 0x200
+ CR2 :: 0x400
+ CR3 :: 0x600
+ _TABDLY :: 0x1800 /* Tab delays: */
+ TAB0 :: 0x0000
+ TAB1 :: 0x0800
+ TAB3 :: 0x1800
+ _BSDLY :: 0x2000 /* Backspace delays: */
+ BS0 :: 0x0000
+ BS1 :: 0x2000
+ _VTDLY :: 0x4000 /* Vertical tab delays: */
+ VT0 :: 0x0000
+ VT1 :: 0x4000
+ _FFDLY :: 0x8000 /* Form feed delays: */
+ FF0 :: 0x0000
+ FF1 :: 0x8000
+
+ B0 :: 0x00 /* hang up */
+ B50 :: 0x01 /* 50 baud */
+ B75 :: 0x02
+ B110 :: 0x03
+ B134 :: 0x04
+ B150 :: 0x05
+ B200 :: 0x06
+ B300 :: 0x07
+ B600 :: 0x08
+ B1200 :: 0x09
+ B1800 :: 0x0A
+ B2400 :: 0x0B
+ B4800 :: 0x0C
+ B9600 :: 0x0D
+ B19200 :: 0x0E
+ B38400 :: 0x0F
+
+ _CSIZE :: 0x20 /* character size */
+ //CS5 :: 0x00 /* only 7 and 8 bits supported */
+ //CS6 :: 0x00 /* Note, it was not very wise to set all of these */
+ //CS7 :: 0x00 /* to zero, but there is not much we can do about it*/
+ CS8 :: 0x20
+ CSTOPB :: 0x40 /* send 2 stop bits, not 1 */
+ CREAD :: 0x80 /* enable receiver */
+ PARENB :: 0x100 /* parity enable */
+ PARODD :: 0x200 /* odd parity, else even */
+ HUPCL :: 0x400 /* hangs up on last close */
+ CLOCAL :: 0x800 /* indicates local line */
+
+ ISIG :: 0x01 /* enable signals */
+ ICANON :: 0x02 /* Canonical input */
+ ECHO :: 0x08 /* Enable echo */
+ ECHOE :: 0x10 /* Echo erase as bs-sp-bs */
+ ECHOK :: 0x20 /* Echo nl after kill */
+ ECHONL :: 0x40 /* Echo nl */
+ NOFLSH :: 0x80 /* Disable flush after int or quit */
+ TOSTOP :: 0x100 /* stop bg processes that write to tty */
+ IEXTEN :: 0x200 /* implementation defined extensions */
+
+ TCIFLUSH :: 1
+ TCOFLUSH :: 2
+ TCIOFLUSH :: 3
+
+ TCIOFF :: 0x04
+ TCION :: 0x08
+ TCOOFF :: 0x01
+ TCOON :: 0x02
+
}
diff --git a/core/sys/posix/time.odin b/core/sys/posix/time.odin
index f9c51c63c..88f0153f4 100644
--- a/core/sys/posix/time.odin
+++ b/core/sys/posix/time.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -230,6 +230,17 @@ when ODIN_OS == .Darwin {
getdate_err: Errno = .ENOSYS // NOTE: looks like it's not a thing on OpenBSD.
+} else when ODIN_OS == .Haiku {
+
+ clockid_t :: distinct c.int32_t
+
+ CLOCK_MONOTONIC :: 0
+ CLOCK_PROCESS_CPUTIME_ID :: -2
+ CLOCK_REALTIME :: -1
+ CLOCK_THREAD_CPUTIME_ID :: -3
+
+ getdate_err: Errno = .ENOSYS // NOTE: looks like it's not a thing on Haiku.
+
} else when ODIN_OS == .Linux {
clockid_t :: distinct c.int
diff --git a/core/sys/posix/unistd.odin b/core/sys/posix/unistd.odin
index 0526b3235..b8020317c 100644
--- a/core/sys/posix/unistd.odin
+++ b/core/sys/posix/unistd.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -899,7 +899,7 @@ CS :: enum c.int {
}
PC :: enum c.int {
- _2_SYMLINK = _PC_2_SYMLINK,
+ _2_SYMLINKS = _PC_2_SYMLINKS,
_ALLOC_SIZE_MIN = _PC_ALLOC_SIZE_MIN,
_ASYNC_IO = _PC_ASYNC_IO,
_CHOWN_RESTRICTED = _PC_CHOWN_RESTRICTED,
@@ -1099,7 +1099,7 @@ when ODIN_OS == .Darwin {
_PC_CHOWN_RESTRICTED :: 7
_PC_NO_TRUNC :: 8
_PC_VDISABLE :: 9
- _PC_2_SYMLINK :: 15
+ _PC_2_SYMLINKS :: 15
_PC_ALLOC_SIZE_MIN :: 16
_PC_ASYNC_IO :: 17
_PC_FILESIZEBITS :: 18
@@ -1280,7 +1280,7 @@ when ODIN_OS == .Darwin {
_PC_CHOWN_RESTRICTED :: 7
_PC_NO_TRUNC :: 8
_PC_VDISABLE :: 9
- _PC_2_SYMLINK :: 13 // NOTE: not in headers (freebsd)
+ _PC_2_SYMLINKS :: 13 // NOTE: not in headers (freebsd)
_PC_ALLOC_SIZE_MIN :: 10
_PC_ASYNC_IO :: 53
_PC_FILESIZEBITS :: 12
@@ -1461,7 +1461,7 @@ when ODIN_OS == .Darwin {
_PC_CHOWN_RESTRICTED :: 7
_PC_NO_TRUNC :: 8
_PC_VDISABLE :: 9
- _PC_2_SYMLINK :: 13 // NOTE: not in headers
+ _PC_2_SYMLINKS :: 13 // NOTE: not in headers
_PC_ALLOC_SIZE_MIN :: 10 // NOTE: not in headers
_PC_ASYNC_IO :: 53 // NOTE: not in headers
_PC_FILESIZEBITS :: 11
@@ -1646,7 +1646,7 @@ when ODIN_OS == .Darwin {
_PC_CHOWN_RESTRICTED :: 7
_PC_NO_TRUNC :: 8
_PC_VDISABLE :: 9
- _PC_2_SYMLINK :: 10
+ _PC_2_SYMLINKS :: 10
_PC_ALLOC_SIZE_MIN :: 11
_PC_ASYNC_IO :: 12
_PC_FILESIZEBITS :: 13
@@ -1816,180 +1816,390 @@ when ODIN_OS == .Darwin {
F_TLOCK :: 2
F_ULOCK :: 0
- _CS_PATH :: 1
- _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS :: 2
+ _CS_PATH :: 0
+ _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS :: 1
+ _CS_GNU_LIBC_VERSION :: 2
+ _CS_GNU_LIBPTHREAD_VERSION :: 3
+ _CS_POSIX_V5_WIDTH_RESTRICTED_ENVS :: 4
+ _CS_POSIX_V7_WIDTH_RESTRICTED_ENVS :: 5
- _CS_POSIX_V6_ILP32_OFF32_CFLAGS :: 1116
- _CS_POSIX_V6_ILP32_OFF32_LDFLAGS :: 1117
- _CS_POSIX_V6_ILP32_OFF32_LIBS :: 1118
- _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS :: 1120
- _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS :: 1121
- _CS_POSIX_V6_ILP32_OFFBIG_LIBS :: 1122
- _CS_POSIX_V6_LP64_OFF64_CFLAGS :: 1124
- _CS_POSIX_V6_LP64_OFF64_LDFLAGS :: 1125
- _CS_POSIX_V6_LP64_OFF64_LIBS :: 1126
- _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS :: 1128
- _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS :: 1129
- _CS_POSIX_V6_LPBIG_OFFBIG_LIBS :: 1130
+ _CS_POSIX_V6_ILP32_OFF32_CFLAGS :: 1116
+ _CS_POSIX_V6_ILP32_OFF32_LDFLAGS :: 1117
+ _CS_POSIX_V6_ILP32_OFF32_LIBS :: 1118
+ _CS_POSIX_V6_ILP32_OFF32_LINTFLAGS :: 1119
+ _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS :: 1120
+ _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS :: 1121
+ _CS_POSIX_V6_ILP32_OFFBIG_LIBS :: 1122
+ _CS_POSIX_V6_ILP32_OFFBIG_LINTFLAGS :: 1123
+ _CS_POSIX_V6_LP64_OFF64_CFLAGS :: 1124
+ _CS_POSIX_V6_LP64_OFF64_LDFLAGS :: 1125
+ _CS_POSIX_V6_LP64_OFF64_LIBS :: 1126
+ _CS_POSIX_V6_LP64_OFF64_LINTFLAGS :: 1127
+ _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS :: 1128
+ _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS :: 1129
+ _CS_POSIX_V6_LPBIG_OFFBIG_LIBS :: 1130
+ _CS_POSIX_V6_LPBIG_OFFBIG_LINTFLAGS :: 1131
+ _CS_POSIX_V7_ILP32_OFF32_CFLAGS :: 1132
+ _CS_POSIX_V7_ILP32_OFF32_LDFLAGS :: 1133
+ _CS_POSIX_V7_ILP32_OFF32_LIBS :: 1134
+ _CS_POSIX_V7_ILP32_OFF32_LINTFLAGS :: 1135
+ _CS_POSIX_V7_ILP32_OFFBIG_CFLAGS :: 1136
+ _CS_POSIX_V7_ILP32_OFFBIG_LDFLAGS :: 1137
+ _CS_POSIX_V7_ILP32_OFFBIG_LIBS :: 1138
+ _CS_POSIX_V7_ILP32_OFFBIG_LINTFLAGS :: 1139
+ _CS_POSIX_V7_LP64_OFF64_CFLAGS :: 1140
+ _CS_POSIX_V7_LP64_OFF64_LDFLAGS :: 1141
+ _CS_POSIX_V7_LP64_OFF64_LIBS :: 1142
+ _CS_POSIX_V7_LP64_OFF64_LINTFLAGS :: 1143
+ _CS_POSIX_V7_LPBIG_OFFBIG_CFLAGS :: 1144
+ _CS_POSIX_V7_LPBIG_OFFBIG_LDFLAGS :: 1145
+ _CS_POSIX_V7_LPBIG_OFFBIG_LIBS :: 1146
+ _CS_POSIX_V7_LPBIG_OFFBIG_LINTFLAGS :: 1147
+ _CS_V6_ENV :: 1148
+ _CS_V7_ENV :: 1149
+ _CS_POSIX_V7_THREADS_CFLAGS :: 1150
+ _CS_POSIX_V7_THREADS_LDFLAGS :: 1151
- _PC_LINK_MAX :: 1
- _PC_MAX_CANON :: 2
- _PC_MAX_INPUT :: 3
- _PC_NAME_MAX :: 4
- _PC_PATH_MAX :: 5
- _PC_PIPE_BUF :: 6
- _PC_CHOWN_RESTRICTED :: 7
- _PC_NO_TRUNC :: 8
- _PC_VDISABLE :: 9
- _PC_SYNC_IO :: 10
- _PC_ASYNC_IO :: 11
- _PC_PRIO_IO :: 12
- _PC_FILESIZEBITS :: 14
- _PC_REC_INCR_XFER_SIZE :: 15
- _PC_REC_MAX_XFER_SIZE :: 16
- _PC_REC_MIN_XFER_SIZE :: 17
- _PC_REC_XFER_ALIGN :: 18
- _PC_ALLOC_SIZE_MIN :: 19
- _PC_SYMLINK_MAX :: 20
- _PC_2_SYMLINK :: 21
+ _PC_LINK_MAX :: 0
+ _PC_MAX_CANON :: 1
+ _PC_MAX_INPUT :: 2
+ _PC_NAME_MAX :: 3
+ _PC_PATH_MAX :: 4
+ _PC_PIPE_BUF :: 5
+ _PC_CHOWN_RESTRICTED :: 6
+ _PC_NO_TRUNC :: 7
+ _PC_VDISABLE :: 8
+ _PC_SYNC_IO :: 9
+ _PC_ASYNC_IO :: 10
+ _PC_PRIO_IO :: 11
+ _PC_SOCK_MAXBUF :: 12
+ _PC_FILESIZEBITS :: 13
+ _PC_REC_INCR_XFER_SIZE :: 14
+ _PC_REC_MAX_XFER_SIZE :: 15
+ _PC_REC_MIN_XFER_SIZE :: 16
+ _PC_REC_XFER_ALIGN :: 17
+ _PC_ALLOC_SIZE_MIN :: 18
+ _PC_SYMLINK_MAX :: 19
+ _PC_2_SYMLINKS :: 20
- _SC_ARG_MAX :: 1
- _SC_CHILD_MAX :: 2
- _SC_CLK_TCK :: 3
- _SC_NGROUPS_MAX :: 4
- _SC_OPEN_MAX :: 5
- _SC_STREAM_MAX :: 6
- _SC_TZNAME_MAX :: 7
- _SC_JOB_CONTROL :: 8
- _SC_SAVED_IDS :: 9
- _SC_REALTIME_SIGNALS :: 10
- _SC_PRIORITY_SCHEDULING :: 11
- _SC_TIMERS :: 12
- _SC_ASYNCHRONOUS_IO :: 13
- _SC_PRIORITIZED_IO :: 14
- _SC_SYNCHRONIZED_IO :: 15
- _SC_FSYNC :: 16
- _SC_MAPPED_FILES :: 17
- _SC_MEMLOCK :: 18
- _SC_MEMLOCK_RANGE :: 19
- _SC_MEMORY_PROTECTION :: 20
- _SC_MESSAGE_PASSING :: 21
- _SC_SEMAPHORES :: 22
- _SC_SHARED_MEMORY_OBJECTS :: 23
- _SC_AIO_LISTIO_MAX :: 24
- _SC_AIO_MAX :: 25
- _SC_AIO_PRIO_DELTA_MAX :: 26
- _SC_DELAYTIMER_MAX :: 27
- _SC_MQ_OPEN_MAX :: 28
- _SC_MQ_PRIO_MAX :: 29
- _SC_VERSION :: 30
- _SC_PAGESIZE :: 31
- _SC_PAGE_SIZE :: _SC_PAGESIZE
- _SC_RTSIG_MAX :: 32
- _SC_SEM_NSEMS_MAX :: 33
- _SC_SEM_VALUE_MAX :: 34
- _SC_SIGQUEUE_MAX :: 35
- _SC_TIMER_MAX :: 36
- _SC_BC_BASE_MAX :: 37
- _SC_BC_DIM_MAX :: 38
- _SC_BC_SCALE_MAX :: 39
- _SC_BC_STRING_MAX :: 40
- _SC_COLL_WEIGHTS_MAX :: 41
- _SC_EXPR_NEST_MAX :: 43
- _SC_LINE_MAX :: 44
- _SC_RE_DUP_MAX :: 45
- _SC_2_VERSION :: 47
- _SC_2_C_BIND :: 48
- _SC_2_C_DEV :: 49
- _SC_2_FORT_DEV :: 50
- _SC_2_FORT_RUN :: 51
- _SC_2_SW_DEV :: 52
- _SC_2_LOCALEDEF :: 53
-
- _SC_IOV_MAX :: 62
- _SC_THREADS :: 69
- _SC_THREAD_SAFE_FUNCTIONS :: 70
- _SC_GETGR_R_SIZE_MAX :: 71
- _SC_GETPW_R_SIZE_MAX :: 72
- _SC_LOGIN_NAME_MAX :: 73
- _SC_TTY_NAME_MAX :: 74
- _SC_THREAD_DESTRUCTOR_ITERATIONS :: 75
- _SC_THREAD_KEYS_MAX :: 76
- _SC_THREAD_STACK_MIN :: 77
- _SC_THREAD_THREADS_MAX :: 78
- _SC_THREAD_ATTR_STACKADDR :: 79
- _SC_THREAD_ATTR_STACKSIZE :: 80
- _SC_THREAD_PRIORITY_SCHEDULING :: 81
- _SC_THREAD_PRIO_INHERIT :: 82
- _SC_THREAD_PRIO_PROTECT :: 83
- _SC_THREAD_PROCESS_SHARED :: 84
- _SC_NPROCESSORS_CONF :: 85
- _SC_NPROCESSORS_ONLN :: 86
- _SC_PHYS_PAGES :: 87
- _SC_AVPHYS_PAGES :: 88
- _SC_ATEXIT_MAX :: 89
- _SC_PASS_MAX :: 90
- _SC_XOPEN_VERSION :: 91
- _SC_XOPEN_UNIX :: 92
- _SC_XOPEN_CRYPT :: 93
- _SC_XOPEN_ENH_I18N :: 94
- _SC_XOPEN_SHM :: 95
- _SC_2_CHAR_TERM :: 96
+ _SC_ARG_MAX :: 0
+ _SC_CHILD_MAX :: 1
+ _SC_CLK_TCK :: 2
+ _SC_NGROUPS_MAX :: 3
+ _SC_OPEN_MAX :: 4
+ _SC_STREAM_MAX :: 5
+ _SC_TZNAME_MAX :: 6
+ _SC_JOB_CONTROL :: 7
+ _SC_SAVED_IDS :: 8
+ _SC_REALTIME_SIGNALS :: 9
+ _SC_PRIORITY_SCHEDULING :: 10
+ _SC_TIMERS :: 11
+ _SC_ASYNCHRONOUS_IO :: 12
+ _SC_PRIORITIZED_IO :: 13
+ _SC_SYNCHRONIZED_IO :: 14
+ _SC_FSYNC :: 15
+ _SC_MAPPED_FILES :: 16
+ _SC_MEMLOCK :: 17
+ _SC_MEMLOCK_RANGE :: 18
+ _SC_MEMORY_PROTECTION :: 19
+ _SC_MESSAGE_PASSING :: 20
+ _SC_SEMAPHORES :: 21
+ _SC_SHARED_MEMORY_OBJECTS :: 22
+ _SC_AIO_LISTIO_MAX :: 23
+ _SC_AIO_MAX :: 24
+ _SC_AIO_PRIO_DELTA_MAX :: 25
+ _SC_DELAYTIMER_MAX :: 26
+ _SC_MQ_OPEN_MAX :: 27
+ _SC_MQ_PRIO_MAX :: 28
+ _SC_VERSION :: 29
+ _SC_PAGE_SIZE :: 30
+ _SC_PAGESIZE :: _SC_PAGE_SIZE
+ _SC_RTSIG_MAX :: 31
+ _SC_SEM_NSEMS_MAX :: 32
+ _SC_SEM_VALUE_MAX :: 33
+ _SC_SIGQUEUE_MAX :: 34
+ _SC_TIMER_MAX :: 35
+ _SC_BC_BASE_MAX :: 36
+ _SC_BC_DIM_MAX :: 37
+ _SC_BC_SCALE_MAX :: 38
+ _SC_BC_STRING_MAX :: 39
+ _SC_COLL_WEIGHTS_MAX :: 40
+ _SC_EXPR_NEST_MAX :: 42
+ _SC_LINE_MAX :: 43
+ _SC_RE_DUP_MAX :: 44
+ _SC_2_VERSION :: 46
+ _SC_2_C_BIND :: 47
+ _SC_2_C_DEV :: 48
+ _SC_2_FORT_DEV :: 49
+ _SC_2_FORT_RUN :: 50
+ _SC_2_SW_DEV :: 51
+ _SC_2_LOCALEDEF :: 52
+ _SC_UIO_MAXIOV :: 60
+ _SC_IOV_MAX :: _SC_UIO_MAXIOV
+ _SC_THREADS :: 67
+ _SC_THREAD_SAFE_FUNCTIONS :: 68
+ _SC_GETGR_R_SIZE_MAX :: 69
+ _SC_GETPW_R_SIZE_MAX :: 70
+ _SC_LOGIN_NAME_MAX :: 71
+ _SC_TTY_NAME_MAX :: 72
+ _SC_THREAD_DESTRUCTOR_ITERATIONS :: 73
+ _SC_THREAD_KEYS_MAX :: 74
+ _SC_THREAD_STACK_MIN :: 75
+ _SC_THREAD_THREADS_MAX :: 76
+ _SC_THREAD_ATTR_STACKADDR :: 77
+ _SC_THREAD_ATTR_STACKSIZE :: 78
+ _SC_THREAD_PRIORITY_SCHEDULING :: 79
+ _SC_THREAD_PRIO_INHERIT :: 80
+ _SC_THREAD_PRIO_PROTECT :: 81
+ _SC_THREAD_PROCESS_SHARED :: 82
+ _SC_NPROCESSORS_CONF :: 83
+ _SC_NPROCESSORS_ONLN :: 84
+ _SC_PHYS_PAGES :: 85
+ _SC_AVPHYS_PAGES :: 86
+ _SC_ATEXIT_MAX :: 87
+ _SC_PASS_MAX :: 88
+ _SC_XOPEN_VERSION :: 89
+ _SC_XOPEN_XCU_VERSION :: 90
+ _SC_XOPEN_UNIX :: 91
+ _SC_XOPEN_CRYPT :: 92
+ _SC_XOPEN_ENH_I18N :: 93
+ _SC_XOPEN_SHM :: 94
+ _SC_2_CHAR_TERM :: 95
_SC_2_UPE :: 97
+ _SC_XOPEN_XPG2 :: 98
+ _SC_XOPEN_XPG3 :: 99
+ _SC_XOPEN_XPG4 :: 100
+ _SC_NZERO :: 109
+ _SC_XBS5_ILP32_OFF32 :: 125
+ _SC_XBS5_ILP32_OFFBIG :: 126
+ _SC_XBS5_LP64_OFF64 :: 127
+ _SC_XBS5_LPBIG_OFFBIG :: 128
+ _SC_XOPEN_LEGACY :: 129
+ _SC_XOPEN_REALTIME :: 130
+ _SC_XOPEN_REALTIME_THREADS :: 131
+ _SC_ADVISORY_INFO :: 132
+ _SC_BARRIERS :: 133
+ _SC_CLOCK_SELECTION :: 137
+ _SC_CPUTIME :: 138
+ _SC_THREAD_CPUTIME :: 139
+ _SC_MONOTONIC_CLOCK :: 149
+ _SC_READER_WRITER_LOCKS :: 153
+ _SC_SPIN_LOCKS :: 154
+ _SC_REGEXP :: 155
+ _SC_SHELL :: 157
+ _SC_SPAWN :: 159
+ _SC_SPORADIC_SERVER :: 160
+ _SC_THREAD_SPORADIC_SERVER :: 161
+ _SC_TIMEOUTS :: 164
+ _SC_TYPED_MEMORY_OBJECTS :: 165
+ _SC_2_PBS :: 168
+ _SC_2_PBS_ACCOUNTING :: 169
+ _SC_2_PBS_LOCATE :: 170
+ _SC_2_PBS_MESSAGE :: 171
+ _SC_2_PBS_TRACK :: 172
+ _SC_SYMLOOP_MAX :: 173
+ _SC_STREAMS :: 174
+ _SC_2_PBS_CHECKPOINT :: 175
+ _SC_V6_ILP32_OFF32 :: 176
+ _SC_V6_ILP32_OFFBIG :: 177
+ _SC_V6_LP64_OFF64 :: 178
+ _SC_V6_LPBIG_OFFBIG :: 179
+ _SC_HOST_NAME_MAX :: 180
+ _SC_TRACE :: 181
+ _SC_TRACE_EVENT_FILTER :: 182
+ _SC_TRACE_INHERIT :: 183
+ _SC_TRACE_LOG :: 184
- _SC_XOPEN_LEGACY :: 129
- _SC_XOPEN_REALTIME :: 130
- _SC_XOPEN_REALTIME_THREADS :: 131
- _SC_ADVISORY_INFO :: 132
- _SC_BARRIERS :: 133
- _SC_CLOCK_SELECTION :: 137
- _SC_CPUTIME :: 138
- _SC_THREAD_CPUTIME :: 139
- _SC_MONOTONIC_CLOCK :: 149
- _SC_READER_WRITER_LOCKS :: 153
- _SC_SPIN_LOCKS :: 154
- _SC_REGEXP :: 155
- _SC_SHELL :: 157
- _SC_SPAWN :: 159
- _SC_SPORADIC_SERVER :: 160
- _SC_THREAD_SPORADIC_SERVER :: 161
- _SC_TIMEOUTS :: 164
- _SC_TYPED_MEMORY_OBJECTS :: 165
- _SC_2_PBS :: 168
- _SC_2_PBS_ACCOUNTING :: 169
- _SC_2_PBS_LOCATE :: 170
- _SC_2_PBS_MESSAGE :: 171
- _SC_2_PBS_TRACK :: 172
- _SC_SYMLOOP_MAX :: 173
- _SC_2_PBS_CHECKPOINT :: 174
- _SC_V6_ILP32_OFF32 :: 175
- _SC_V6_ILP32_OFFBIG :: 176
- _SC_V6_LP64_OFF64 :: 177
- _SC_V6_LPBIG_OFFBIG :: 178
- _SC_HOST_NAME_MAX :: 179
- _SC_TRACE :: 180
- _SC_TRACE_EVENT_FILTER :: 181
- _SC_TRACE_INHERIT :: 182
- _SC_TRACE_LOG :: 183
-
- _SC_IPV6 :: 234
- _SC_RAW_SOCKETS :: 235
- _SC_V7_ILP32_OFF32 :: 236
- _SC_V7_ILP32_OFFBIG :: 237
- _SC_V7_LP64_OFF64 :: 238
- _SC_V7_LPBIG_OFFBIG :: 239
- _SC_SS_REPL_MAX :: 240
- _SC_TRACE_EVENT_NAME_MAX :: 241
- _SC_TRACE_NAME_MAX :: 242
- _SC_TRACE_SYS_MAX :: 243
- _SC_TRACE_USER_EVENT_MAX :: 244
- _SC_XOPEN_STREAMS :: 245
- _SC_THREAD_ROBUST_PRIO_INHERIT :: 246
- _SC_THREAD_ROBUST_PRIO_PROTECT :: 247
+ _SC_IPV6 :: 235
+ _SC_RAW_SOCKETS :: 236
+ _SC_V7_ILP32_OFF32 :: 237
+ _SC_V7_ILP32_OFFBIG :: 238
+ _SC_V7_LP64_OFF64 :: 239
+ _SC_V7_LPBIG_OFFBIG :: 240
+ _SC_SS_REPL_MAX :: 241
+ _SC_TRACE_EVENT_NAME_MAX :: 242
+ _SC_TRACE_NAME_MAX :: 243
+ _SC_TRACE_SYS_MAX :: 244
+ _SC_TRACE_USER_EVENT_MAX :: 245
+ _SC_XOPEN_STREAMS :: 246
+ _SC_THREAD_ROBUST_PRIO_INHERIT :: 247
+ _SC_THREAD_ROBUST_PRIO_PROTECT :: 248
+ _SC_MINSIGSTKSZ :: 249
+ _SC_SIGSTKSZ :: 250
// NOTE: Not implemented.
_SC_XOPEN_UUCP :: 0
// NOTE: Not implemented.
_POSIX_VDISABLE :: 0
+} else when ODIN_OS == .Haiku {
+
+ _F_OK :: 0
+ X_OK :: 1
+ W_OK :: 2
+ R_OK :: 4
+
+ F_LOCK :: 1
+ F_TEST :: 3
+ F_TLOCK :: 2
+ F_ULOCK :: 0
+
+ _CS_PATH :: 1
+ _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS :: 0 // Undefined.
+ _CS_POSIX_V6_ILP32_OFF32_CFLAGS :: 0 // Undefined.
+ _CS_POSIX_V6_ILP32_OFF32_LDFLAGS :: 0 // Undefined.
+ _CS_POSIX_V6_ILP32_OFF32_LIBS :: 0 // Undefined.
+ _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS :: 0 // Undefined.
+ _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS :: 0 // Undefined.
+ _CS_POSIX_V6_ILP32_OFFBIG_LIBS :: 0 // Undefined.
+ _CS_POSIX_V6_LP64_OFF64_CFLAGS :: 0 // Undefined.
+ _CS_POSIX_V6_LP64_OFF64_LDFLAGS :: 0 // Undefined.
+ _CS_POSIX_V6_LP64_OFF64_LIBS :: 0 // Undefined.
+ _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS :: 0 // Undefined.
+ _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS :: 0 // Undefined.
+ _CS_POSIX_V6_LPBIG_OFFBIG_LIBS :: 0 // Undefined.
+
+ _SC_ASYNCHRONOUS_IO :: 0 // Undefined.
+ _SC_RAW_SOCKETS :: 0 // Undefined.
+ _SC_SS_REPL_MAX :: 0 // Undefined.
+ _SC_TRACE_EVENT_NAME_MAX :: 0 // Undefined.
+ _SC_TRACE_NAME_MAX :: 0 // Undefined.
+ _SC_TRACE_SYS_MAX :: 0 // Undefined.
+ _SC_TRACE_USER_EVENT_MAX :: 0 // Undefined.
+
+ _PC_CHOWN_RESTRICTED :: 1
+ _PC_MAX_CANON :: 2
+ _PC_MAX_INPUT :: 3
+ _PC_NAME_MAX :: 4
+ _PC_NO_TRUNC :: 5
+ _PC_PATH_MAX :: 6
+ _PC_PIPE_BUF :: 7
+ _PC_VDISABLE :: 8
+ _PC_LINK_MAX :: 25
+ _PC_SYNC_IO :: 26
+ _PC_ASYNC_IO :: 27
+ _PC_PRIO_IO :: 28
+ _PC_FILESIZEBITS :: 30
+ _PC_REC_INCR_XFER_SIZE :: 31
+ _PC_REC_MAX_XFER_SIZE :: 32
+ _PC_REC_MIN_XFER_SIZE :: 33
+ _PC_REC_XFER_ALIGN :: 34
+ _PC_ALLOC_SIZE_MIN :: 35
+ _PC_SYMLINK_MAX :: 36
+ _PC_2_SYMLINKS :: 37
+
+ _SC_ARG_MAX :: 15
+ _SC_CHILD_MAX :: 16
+ _SC_CLK_TCK :: 17
+ _SC_JOB_CONTROL :: 18
+ _SC_NGROUPS_MAX :: 19
+ _SC_OPEN_MAX :: 20
+ _SC_SAVED_IDS :: 21
+ _SC_STREAM_MAX :: 22
+ _SC_TZNAME_MAX :: 23
+ _SC_VERSION :: 24
+ _SC_GETGR_R_SIZE_MAX :: 25
+ _SC_GETPW_R_SIZE_MAX :: 26
+ _SC_PAGE_SIZE :: 27
+ _SC_PAGESIZE :: _SC_PAGE_SIZE
+ _SC_SEM_NSEMS_MAX :: 28
+ _SC_SEM_VALUE_MAX :: 29
+ _SC_SEMAPHORES :: 30
+ _SC_THREADS :: 31
+ _SC_IOV_MAX :: 32
+ _SC_NPROCESSORS_CONF :: 34
+ _SC_NPROCESSORS_ONLN :: 35
+ _SC_ATEXIT_MAX :: 37
+ _SC_MAPPED_FILES :: 45
+ _SC_THREAD_PROCESS_SHARED :: 46
+ _SC_THREAD_STACK_MIN :: 47
+ _SC_THREAD_ATTR_STACKADDR :: 48
+ _SC_THREAD_ATTR_STACKSIZE :: 49
+ _SC_THREAD_PRIORITY_SCHEDULING :: 50
+ _SC_REALTIME_SIGNALS :: 51
+ _SC_MEMORY_PROTECTION :: 52
+ _SC_SIGQUEUE_MAX :: 53
+ _SC_RTSIG_MAX :: 54
+ _SC_MONOTONIC_CLOCK :: 55
+ _SC_DELAYTIMER_MAX :: 56
+ _SC_TIMER_MAX :: 57
+ _SC_TIMERS :: 58
+ _SC_CPUTIME :: 59
+ _SC_THREAD_CPUTIME :: 60
+ _SC_HOST_NAME_MAX :: 61
+ _SC_REGEXP :: 62
+ _SC_SYMLOOP_MAX :: 63
+ _SC_SHELL :: 64
+ _SC_TTY_NAME_MAX :: 65
+ _SC_ADVISORY_INFO :: 66
+ _SC_BARRIERS :: 67
+ _SC_CLOCK_SELECTION :: 68
+ _SC_FSYNC :: 69
+ _SC_IPV6 :: 70
+ _SC_MEMLOCK :: 71
+ _SC_MEMLOCK_RANGE :: 72
+ _SC_MESSAGE_PASSING :: 73
+ _SC_PRIORITIZED_IO :: 74
+ _SC_PRIORITY_SCHEDULING :: 75
+ _SC_READER_WRITER_LOCKS :: 76
+ _SC_SHARED_MEMORY_OBJECTS :: 77
+ _SC_SPAWN :: 78
+ _SC_SPIN_LOCKS :: 79
+ _SC_SPORADIC_SERVER :: 80
+ _SC_SYNCHRONIZED_IO :: 81
+ _SC_THREAD_PRIO_INHERIT :: 82
+ _SC_THREAD_PRIO_PROTECT :: 83
+ _SC_THREAD_SAFE_FUNCTIONS :: 86
+ _SC_THREAD_SPORADIC_SERVER :: 87
+ _SC_TIMEOUTS :: 88
+ _SC_TRACE :: 89
+ _SC_TRACE_EVENT_FILTER :: 90
+ _SC_TRACE_INHERIT :: 91
+ _SC_TRACE_LOG :: 92
+ _SC_TYPED_MEMORY_OBJECTS :: 93
+ _SC_V6_ILP32_OFF32 :: 94
+ _SC_V6_ILP32_OFFBIG :: 95
+ _SC_V6_LP64_OFF64 :: 96
+ _SC_V6_LPBIG_OFFBIG :: 97
+ _SC_2_C_BIND :: 102
+ _SC_2_C_DEV :: 103
+ _SC_2_CHAR_TERM :: 104
+ _SC_2_FORT_DEV :: 105
+ _SC_2_FORT_RUN :: 106
+ _SC_2_LOCALEDEF :: 107
+ _SC_2_PBS :: 108
+ _SC_2_PBS_ACCOUNTING :: 109
+ _SC_2_PBS_CHECKPOINT :: 110
+ _SC_2_PBS_LOCATE :: 111
+ _SC_2_PBS_MESSAGE :: 112
+ _SC_2_PBS_TRACK :: 113
+ _SC_2_SW_DEV :: 114
+ _SC_2_UPE :: 115
+ _SC_2_VERSION :: 116
+ _SC_XOPEN_CRYPT :: 117
+ _SC_XOPEN_ENH_I18N :: 118
+ _SC_XOPEN_REALTIME :: 119
+ _SC_XOPEN_REALTIME_THREADS :: 120
+ _SC_XOPEN_SHM :: 121
+ _SC_XOPEN_STREAMS :: 122
+ _SC_XOPEN_UNIX :: 123
+ _SC_XOPEN_VERSION :: 125
+ _SC_AIO_LISTIO_MAX :: 126
+ _SC_AIO_MAX :: 127
+ _SC_AIO_PRIO_DELTA_MAX :: 128
+ _SC_BC_BASE_MAX :: 129
+ _SC_BC_DIM_MAX :: 130
+ _SC_BC_SCALE_MAX :: 131
+ _SC_BC_STRING_MAX :: 132
+ _SC_COLL_WEIGHTS_MAX :: 133
+ _SC_EXPR_NEST_MAX :: 134
+ _SC_LINE_MAX :: 135
+ _SC_LOGIN_NAME_MAX :: 136
+ _SC_MQ_OPEN_MAX :: 137
+ _SC_MQ_PRIO_MAX :: 138
+ _SC_THREAD_DESTRUCTOR_ITERATIONS :: 139
+ _SC_THREAD_KEYS_MAX :: 140
+ _SC_THREAD_THREADS_MAX :: 141
+ _SC_RE_DUP_MAX :: 142
+
}
diff --git a/core/sys/posix/unistd_libc.odin b/core/sys/posix/unistd_libc.odin
index bbfe3d59d..74edb6862 100644
--- a/core/sys/posix/unistd_libc.odin
+++ b/core/sys/posix/unistd_libc.odin
@@ -1,4 +1,4 @@
-#+build linux, windows, darwin, netbsd, openbsd, freebsd
+#+build linux, windows, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
diff --git a/core/sys/posix/utime.odin b/core/sys/posix/utime.odin
index e884eb1a3..98c8166d6 100644
--- a/core/sys/posix/utime.odin
+++ b/core/sys/posix/utime.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
when ODIN_OS == .Darwin {
@@ -25,7 +25,7 @@ when ODIN_OS == .NetBSD {
@(private) LUTIME :: "utime"
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux || ODIN_OS == .Haiku {
utimbuf :: struct {
actime: time_t, /* [PSX] access time (seconds since epoch) */
diff --git a/core/sys/wasm/js/events.odin b/core/sys/wasm/js/events.odin
index ffa3a1202..37a6e6c35 100644
--- a/core/sys/wasm/js/events.odin
+++ b/core/sys/wasm/js/events.odin
@@ -189,7 +189,7 @@ Key_Location :: enum u8 {
KEYBOARD_MAX_KEY_SIZE :: 32
KEYBOARD_MAX_CODE_SIZE :: 32
-GAMEPAD_MAX_ID_SIZE :: 64
+GAMEPAD_MAX_ID_SIZE :: 96
GAMEPAD_MAX_MAPPING_SIZE :: 64
GAMEPAD_MAX_BUTTONS :: 64
@@ -239,6 +239,12 @@ Gamepad_State :: struct {
_mapping_buf: [GAMEPAD_MAX_MAPPING_SIZE]byte `fmt:"-"`,
}
+Pointer_Type :: enum u8 {
+ Mouse,
+ Pen,
+ Touch,
+}
+
Event :: struct {
kind: Event_Kind,
target_kind: Event_Target_Kind,
@@ -275,6 +281,8 @@ Event :: struct {
repeat: bool,
+ char: rune,
+
_key_len: int `fmt:"-"`,
_code_len: int `fmt:"-"`,
_key_buf: [KEYBOARD_MAX_KEY_SIZE]byte `fmt:"-"`,
@@ -295,6 +303,21 @@ Event :: struct {
button: i16,
buttons: bit_set[0..<16; u16],
+
+ pointer: struct {
+ altitude_angle: f64,
+ azimuth_angle: f64,
+ persistent_device_id: int,
+ pointer_id: int,
+ width: int,
+ height: int,
+ pressure: f64,
+ tangential_pressure: f64,
+ tilt: [2]f64,
+ twist: f64,
+ pointer_type: Pointer_Type,
+ is_primary: bool,
+ },
},
gamepad: Gamepad_State,
@@ -323,13 +346,13 @@ add_event_listener :: proc(id: string, kind: Event_Kind, user_data: rawptr, call
return _add_event_listener(id, event_kind_string[kind], kind, user_data, callback, use_capture)
}
-remove_event_listener :: proc(id: string, kind: Event_Kind, user_data: rawptr, callback: proc(e: Event)) -> bool {
+remove_event_listener :: proc(id: string, kind: Event_Kind, user_data: rawptr, callback: proc(e: Event), use_capture := false) -> bool {
@(default_calling_convention="contextless")
foreign dom_lib {
@(link_name="remove_event_listener")
- _remove_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc "odin" (Event)) -> bool ---
+ _remove_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc "odin" (Event), use_capture: bool) -> bool ---
}
- return _remove_event_listener(id, event_kind_string[kind], user_data, callback)
+ return _remove_event_listener(id, event_kind_string[kind], user_data, callback, use_capture)
}
add_window_event_listener :: proc(kind: Event_Kind, user_data: rawptr, callback: proc(e: Event), use_capture := false) -> bool {
@@ -341,20 +364,26 @@ add_window_event_listener :: proc(kind: Event_Kind, user_data: rawptr, callback:
return _add_window_event_listener(event_kind_string[kind], kind, user_data, callback, use_capture)
}
-remove_window_event_listener :: proc(kind: Event_Kind, user_data: rawptr, callback: proc(e: Event)) -> bool {
+remove_window_event_listener :: proc(kind: Event_Kind, user_data: rawptr, callback: proc(e: Event), use_capture := false) -> bool {
@(default_calling_convention="contextless")
foreign dom_lib {
@(link_name="remove_window_event_listener")
- _remove_window_event_listener :: proc(name: string, user_data: rawptr, callback: proc "odin" (Event)) -> bool ---
+ _remove_window_event_listener :: proc(name: string, user_data: rawptr, callback: proc "odin" (Event), use_capture: bool) -> bool ---
}
- return _remove_window_event_listener(event_kind_string[kind], user_data, callback)
+ return _remove_window_event_listener(event_kind_string[kind], user_data, callback, use_capture)
}
remove_event_listener_from_event :: proc(e: Event) -> bool {
+ from_use_capture_false: bool
+ from_use_capture_true: bool
if e.id == "" {
- return remove_window_event_listener(e.kind, e.user_data, e.callback)
+ from_use_capture_false = remove_window_event_listener(e.kind, e.user_data, e.callback, false)
+ from_use_capture_true = remove_window_event_listener(e.kind, e.user_data, e.callback, true)
+ } else {
+ from_use_capture_false = remove_event_listener(e.id, e.kind, e.user_data, e.callback, false)
+ from_use_capture_true = remove_event_listener(e.id, e.kind, e.user_data, e.callback, true)
}
- return remove_event_listener(e.id, e.kind, e.user_data, e.callback)
+ return from_use_capture_false || from_use_capture_true
}
add_custom_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc(e: Event), use_capture := false) -> bool {
@@ -365,13 +394,13 @@ add_custom_event_listener :: proc(id: string, name: string, user_data: rawptr, c
}
return _add_event_listener(id, name, .Custom, user_data, callback, use_capture)
}
-remove_custom_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc(e: Event)) -> bool {
+remove_custom_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc(e: Event), use_capture := false) -> bool {
@(default_calling_convention="contextless")
foreign dom_lib {
@(link_name="remove_event_listener")
- _remove_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc "odin" (Event)) -> bool ---
+ _remove_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc "odin" (Event), use_capture: bool) -> bool ---
}
- return _remove_event_listener(id, name, user_data, callback)
+ return _remove_event_listener(id, name, user_data, callback, use_capture)
}
get_gamepad_state :: proc "contextless" (index: int, s: ^Gamepad_State) -> bool {
@@ -384,7 +413,14 @@ get_gamepad_state :: proc "contextless" (index: int, s: ^Gamepad_State) -> bool
if s == nil {
return false
}
- return _get_gamepad_state(index, s)
+
+ if !_get_gamepad_state(index, s) {
+ return false
+ }
+
+ s.id = string(s._id_buf[:s._id_len])
+ s.mapping = string(s._mapping_buf[:s._mapping_len])
+ return true
}
@@ -415,4 +451,4 @@ do_event_callback :: proc(user_data: rawptr, callback: proc(e: Event)) {
callback(event)
}
-}
\ No newline at end of file
+}
diff --git a/core/sys/wasm/js/events_all_targets.odin b/core/sys/wasm/js/events_all_targets.odin
index b7e01ca10..6439396c5 100644
--- a/core/sys/wasm/js/events_all_targets.odin
+++ b/core/sys/wasm/js/events_all_targets.odin
@@ -263,7 +263,7 @@ add_event_listener :: proc(id: string, kind: Event_Kind, user_data: rawptr, call
panic("vendor:wasm/js not supported on non JS targets")
}
-remove_event_listener :: proc(id: string, kind: Event_Kind, user_data: rawptr, callback: proc(e: Event)) -> bool {
+remove_event_listener :: proc(id: string, kind: Event_Kind, user_data: rawptr, callback: proc(e: Event), use_capture := false) -> bool {
panic("vendor:wasm/js not supported on non JS targets")
}
@@ -271,7 +271,7 @@ add_window_event_listener :: proc(kind: Event_Kind, user_data: rawptr, callback:
panic("vendor:wasm/js not supported on non JS targets")
}
-remove_window_event_listener :: proc(kind: Event_Kind, user_data: rawptr, callback: proc(e: Event)) -> bool {
+remove_window_event_listener :: proc(kind: Event_Kind, user_data: rawptr, callback: proc(e: Event), use_capture := false) -> bool {
panic("vendor:wasm/js not supported on non JS targets")
}
@@ -282,6 +282,6 @@ remove_event_listener_from_event :: proc(e: Event) -> bool {
add_custom_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc(e: Event), use_capture := false) -> bool {
panic("vendor:wasm/js not supported on non JS targets")
}
-remove_custom_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc(e: Event)) -> bool {
+remove_custom_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc(e: Event), use_capture := false) -> bool {
panic("vendor:wasm/js not supported on non JS targets")
-}
\ No newline at end of file
+}
diff --git a/core/sys/wasm/js/general.odin b/core/sys/wasm/js/general.odin
index 4ed2ae298..22bb08e2b 100644
--- a/core/sys/wasm/js/general.odin
+++ b/core/sys/wasm/js/general.odin
@@ -9,4 +9,5 @@ foreign odin_env {
abort :: proc() -> ! ---
alert :: proc(msg: string) ---
evaluate :: proc(str: string) ---
-}
\ No newline at end of file
+ open :: proc(url: string, name := "", specs := "") ---
+}
diff --git a/core/sys/wasm/js/odin.js b/core/sys/wasm/js/odin.js
index 07a77952c..d5faa5210 100644
--- a/core/sys/wasm/js/odin.js
+++ b/core/sys/wasm/js/odin.js
@@ -17,7 +17,7 @@ class WasmMemoryInterface {
constructor() {
this.memory = null;
this.exports = null;
- this.listenerMap = {};
+ this.listenerMap = new Map();
// Size (in bytes) of the integer type, should be 4 on `js_wasm32` and 8 on `js_wasm64p32`
this.intSize = 4;
@@ -110,7 +110,10 @@ class WasmMemoryInterface {
}
loadCstring(ptr) {
- const start = this.loadPtr(ptr);
+ return this.loadCstringDirect(this.loadPtr(ptr));
+ }
+
+ loadCstringDirect(start) {
if (start == 0) {
return null;
}
@@ -399,6 +402,9 @@ class WebGLInterface {
BlendEquation: (mode) => {
this.ctx.blendEquation(mode);
},
+ BlendEquationSeparate: (modeRGB, modeAlpha) => {
+ this.ctx.blendEquationSeparate(modeRGB, modeAlpha);
+ },
BlendFunc: (sfactor, dfactor) => {
this.ctx.blendFunc(sfactor, dfactor);
},
@@ -630,6 +636,13 @@ class WebGLInterface {
GetParameter: (pname) => {
return this.ctx.getParameter(pname);
},
+ GetParameter4i: (pname, v0, v1, v2, v3) => {
+ const i4 = this.ctx.getParameter(pname);
+ this.mem.storeI32(v0, i4[0]);
+ this.mem.storeI32(v1, i4[1]);
+ this.mem.storeI32(v2, i4[2]);
+ this.mem.storeI32(v3, i4[3]);
+ },
GetProgramParameter: (program, pname) => {
return this.ctx.getProgramParameter(this.programs[program], pname)
},
@@ -1312,18 +1325,20 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory) {
} else if (!line.includes("\n")) {
currentLine[isError] = currentLine[isError].concat(line);
} else {
- let lines = line.split("\n");
+ let lines = line.trimEnd().split("\n");
let printLast = lines.length > 1 && line.endsWith("\n");
println(currentLine[isError].concat(lines[0]));
currentLine[isError] = "";
for (let i = 1; i < lines.length-1; i++) {
println(lines[i]);
}
- let last = lines[lines.length-1];
- if (printLast) {
- println(last);
- } else {
- currentLine[isError] = last;
+ if (lines.length > 1) {
+ let last = lines[lines.length-1];
+ if (printLast) {
+ println(last);
+ } else {
+ currentLine[isError] = last;
+ }
}
}
@@ -1390,6 +1405,10 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory) {
info.scrollTop = info.scrollHeight;
};
+ const listener_key = (id, name, data, callback, useCapture) => {
+ return `${id}-${name}-data:${data}-callback:${callback}-useCapture:${useCapture}`;
+ };
+
let webglContext = new WebGLInterface(wasmMemoryInterface);
const env = {};
@@ -1418,6 +1437,13 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory) {
abort: () => { Module.abort() },
evaluate: (str_ptr, str_len) => { eval.call(null, wasmMemoryInterface.loadString(str_ptr, str_len)); },
+ open: (url_ptr, url_len, name_ptr, name_len, specs_ptr, specs_len) => {
+ const url = wasmMemoryInterface.loadString(url_ptr, url_len);
+ const name = wasmMemoryInterface.loadString(name_ptr, name_len);
+ const specs = wasmMemoryInterface.loadString(specs_ptr, specs_len);
+ window.open(url, name, specs);
+ },
+
// return a bigint to be converted to i64
time_now: () => BigInt(Date.now()),
tick_now: () => performance.now(),
@@ -1530,6 +1556,29 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory) {
wmi.storeI16(off(2), e.button);
wmi.storeU16(off(2), e.buttons);
+
+ if (e instanceof PointerEvent) {
+ wmi.storeF64(off(8), e.altitudeAngle);
+ wmi.storeF64(off(8), e.azimuthAngle);
+ wmi.storeInt(off(W), e.persistentDeviceId);
+ wmi.storeInt(off(W), e.pointerId);
+ wmi.storeInt(off(W), e.width);
+ wmi.storeInt(off(W), e.height);
+ wmi.storeF64(off(8), e.pressure);
+ wmi.storeF64(off(8), e.tangentialPressure);
+ wmi.storeF64(off(8), e.tiltX);
+ wmi.storeF64(off(8), e.tiltY);
+ wmi.storeF64(off(8), e.twist);
+ if (e.pointerType == "pen") {
+ wmi.storeU8(off(1), 1);
+ } else if (e.pointerType == "touch") {
+ wmi.storeU8(off(1), 2);
+ } else {
+ wmi.storeU8(off(1), 0);
+ }
+ wmi.storeU8(off(1), !!e.isPrimary);
+ }
+
} else if (e instanceof KeyboardEvent) {
// Note: those strings are constructed
// on the native side from buffers that
@@ -1546,6 +1595,8 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory) {
wmi.storeU8(off(1), !!e.repeat);
+ wmi.storeI32(off(4), e.charCode);
+
wmi.storeInt(off(W, W), e.key.length)
wmi.storeInt(off(W, W), e.code.length)
wmi.storeString(off(32, 1), e.key);
@@ -1585,10 +1636,24 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory) {
}
}
- wmi.storeInt(off(W, W), e.gamepad.id.length)
- wmi.storeInt(off(W, W), e.gamepad.mapping.length)
- wmi.storeString(off(64, 1), e.gamepad.id);
- wmi.storeString(off(64, 1), e.gamepad.mapping);
+ let idLength = e.gamepad.id.length;
+ let id = e.gamepad.id;
+ if (idLength > 96) {
+ idLength = 96;
+ id = id.slice(0, 93) + '...';
+ }
+
+ let mappingLength = e.gamepad.mapping.length;
+ let mapping = e.gamepad.mapping;
+ if (mappingLength > 64) {
+ mappingLength = 61;
+ mapping = mapping.slice(0, 61) + '...';
+ }
+
+ wmi.storeInt(off(W, W), idLength);
+ wmi.storeInt(off(W, W), mappingLength);
+ wmi.storeString(off(96, 1), id);
+ wmi.storeString(off(64, 1), mapping);
}
},
@@ -1599,6 +1664,10 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory) {
if (element == undefined) {
return false;
}
+ let key = listener_key(id, name, data, callback, !!use_capture);
+ if (wasmMemoryInterface.listenerMap.has(key)) {
+ return false;
+ }
let listener = (e) => {
let event_data = {};
@@ -1609,7 +1678,7 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory) {
onEventReceived(event_data, data, callback);
};
- wasmMemoryInterface.listenerMap[{data: data, callback: callback}] = listener;
+ wasmMemoryInterface.listenerMap.set(key, listener);
element.addEventListener(name, listener, !!use_capture);
return true;
},
@@ -1617,6 +1686,11 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory) {
add_window_event_listener: (name_ptr, name_len, name_code, data, callback, use_capture) => {
let name = wasmMemoryInterface.loadString(name_ptr, name_len);
let element = window;
+ let key = listener_key('window', name, data, callback, !!use_capture);
+ if (wasmMemoryInterface.listenerMap.has(key)) {
+ return false;
+ }
+
let listener = (e) => {
let event_data = {};
event_data.id_ptr = 0;
@@ -1626,12 +1700,12 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory) {
onEventReceived(event_data, data, callback);
};
- wasmMemoryInterface.listenerMap[{data: data, callback: callback}] = listener;
+ wasmMemoryInterface.listenerMap.set(key, listener);
element.addEventListener(name, listener, !!use_capture);
return true;
},
- remove_event_listener: (id_ptr, id_len, name_ptr, name_len, data, callback) => {
+ remove_event_listener: (id_ptr, id_len, name_ptr, name_len, data, callback, use_capture) => {
let id = wasmMemoryInterface.loadString(id_ptr, id_len);
let name = wasmMemoryInterface.loadString(name_ptr, name_len);
let element = getElement(id);
@@ -1639,24 +1713,28 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory) {
return false;
}
- let listener = wasmMemoryInterface.listenerMap[{data: data, callback: callback}];
- if (listener == undefined) {
+ let key = listener_key(id, name, data, callback, !!use_capture);
+ let listener = wasmMemoryInterface.listenerMap.get(key);
+ if (listener === undefined) {
return false;
}
- element.removeEventListener(name, listener);
+ wasmMemoryInterface.listenerMap.delete(key);
+
+ element.removeEventListener(name, listener, !!use_capture);
return true;
},
- remove_window_event_listener: (name_ptr, name_len, data, callback) => {
+ remove_window_event_listener: (name_ptr, name_len, data, callback, use_capture) => {
let name = wasmMemoryInterface.loadString(name_ptr, name_len);
let element = window;
- let key = {data: data, callback: callback};
- let listener = wasmMemoryInterface.listenerMap[key];
- if (!listener) {
+
+ let key = listener_key('window', name, data, callback, !!use_capture);
+ let listener = wasmMemoryInterface.listenerMap.get(key);
+ if (listener === undefined) {
return false;
}
- wasmMemoryInterface.listenerMap[key] = undefined;
+ wasmMemoryInterface.listenerMap.delete(key);
- element.removeEventListener(name, listener);
+ element.removeEventListener(name, listener, !!use_capture);
return true;
},
@@ -1753,10 +1831,24 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory) {
}
}
- wmi.storeInt(off(W, W), gamepad.id.length)
- wmi.storeInt(off(W, W), gamepad.mapping.length)
- wmi.storeString(off(64, 1), gamepad.id);
- wmi.storeString(off(64, 1), gamepad.mapping);
+ let idLength = gamepad.id.length;
+ let id = gamepad.id;
+ if (idLength > 96) {
+ idLength = 96;
+ id = id.slice(0, 93) + '...';
+ }
+
+ let mappingLength = gamepad.mapping.length;
+ let mapping = gamepad.mapping;
+ if (mappingLength > 64) {
+ mappingLength = 61;
+ mapping = mapping.slice(0, 61) + '...';
+ }
+
+ wmi.storeInt(off(W, W), idLength);
+ wmi.storeInt(off(W, W), mappingLength);
+ wmi.storeString(off(96, 1), id);
+ wmi.storeString(off(64, 1), mapping);
return true;
}
diff --git a/core/sys/windows/comctl32.odin b/core/sys/windows/comctl32.odin
index 477800413..d954f952c 100644
--- a/core/sys/windows/comctl32.odin
+++ b/core/sys/windows/comctl32.odin
@@ -5,5 +5,2093 @@ foreign import "system:Comctl32.lib"
@(default_calling_convention="system")
foreign Comctl32 {
+ InitCommonControlsEx :: proc(picce: ^INITCOMMONCONTROLSEX) -> BOOL ---
LoadIconWithScaleDown :: proc(hinst: HINSTANCE, pszName: PCWSTR, cx: c_int, cy: c_int, phico: ^HICON) -> HRESULT ---
+ SetWindowSubclass :: proc(hwnd: HWND, pfnSubclass: SUBCLASSPROC, uIdSubclass: UINT_PTR, dwRefData: DWORD_PTR) ---
+}
+
+ICC_LISTVIEW_CLASSES :: 0x00000001
+ICC_TREEVIEW_CLASSES :: 0x00000002
+ICC_BAR_CLASSES :: 0x00000004
+ICC_TAB_CLASSES :: 0x00000008
+ICC_UPDOWN_CLASS :: 0x00000010
+ICC_PROGRESS_CLASS :: 0x00000020
+ICC_HOTKEY_CLASS :: 0x00000040
+ICC_ANIMATE_CLASS :: 0x00000080
+ICC_WIN95_CLASSES :: 0x000000FF
+ICC_DATE_CLASSES :: 0x00000100
+ICC_USEREX_CLASSES :: 0x00000200
+ICC_COOL_CLASSES :: 0x00000400
+ICC_INTERNET_CLASSES :: 0x00000800
+ICC_PAGESCROLLER_CLASS :: 0x00001000
+ICC_NATIVEFNTCTL_CLASS :: 0x00002000
+ICC_STANDARD_CLASSES :: 0x00004000
+ICC_LINK_CLASS :: 0x00008000
+
+INITCOMMONCONTROLSEX :: struct {
+ dwSize: DWORD,
+ dwICC: DWORD,
+}
+
+COMCTL32_VERSION :: 6
+HINST_COMMCTRL :: cast(HINSTANCE)(~uintptr(0))
+
+// Common Control Class Names
+WC_HEADER :: "SysHeader32"
+WC_LISTVIEW :: "SysListView32"
+WC_TREEVIEW :: "SysTreeView32"
+WC_COMBOBOXEX :: "ComboBoxEx32"
+WC_TABCONTROL :: "SysTabControl32"
+WC_IPADDRESS :: "SysIPAddress32"
+WC_PAGESCROLLER :: "SysPager"
+WC_NATIVEFONTCTL :: "NativeFontCtl"
+WC_BUTTON :: "Button"
+WC_STATIC :: "Static"
+WC_EDIT :: "Edit"
+WC_LISTBOX :: "ListBox"
+WC_COMBOBOX :: "ComboBox"
+WC_SCROLLBAR :: "ScrollBar"
+WC_LINK :: "SysLink"
+
+TOOLBARCLASSNAME :: "ToolbarWindow32"
+REBARCLASSNAME :: "ReBarWindow32"
+STATUSCLASSNAME :: "msctls_statusbar32"
+
+TOOLTIPS_CLASS :: "tooltips_class32"
+TRACKBAR_CLASS :: "msctls_trackbar32"
+UPDOWN_CLASS :: "msctls_updown32"
+PROGRESS_CLASS :: "msctls_progress32"
+HOTKEY_CLASS :: "msctls_hotkey32"
+ANIMATE_CLASS :: "SysAnimate32"
+MONTHCAL_CLASS :: "SysMonthCal32"
+DATETIMEPICK_CLASS :: "SysDateTimePick32"
+
+// Common Control Constants
+MSGF_COMMCTRL_BEGINDRAG :: 0x4200
+MSGF_COMMCTRL_SIZEHEADER :: 0x4201
+MSGF_COMMCTRL_DRAGSELECT :: 0x4202
+MSGF_COMMCTRL_TOOLBARCUST :: 0x4203
+
+// Custom Draw Constants
+CDRF_DODEFAULT :: 0x00
+CDRF_NEWFONT :: 0x02
+CDRF_SKIPDEFAULT :: 0x04
+CDRF_NOTIFYPOSTPAINT :: 0x10
+CDRF_NOTIFYITEMDRAW :: 0x20
+CDRF_NOTIFYSUBITEMDRAW :: 0x20
+CDRF_NOTIFYPOSTERASE :: 0x40
+
+CDDS_PREPAINT :: 0x00001
+CDDS_POSTPAINT :: 0x00002
+CDDS_PREERASE :: 0x00003
+CDDS_POSTERASE :: 0x00004
+CDDS_ITEM :: 0x10000
+CDDS_ITEMPREPAINT :: (CDDS_ITEM | CDDS_PREPAINT)
+CDDS_ITEMPOSTPAINT :: (CDDS_ITEM | CDDS_POSTPAINT)
+CDDS_ITEMPREERASE :: (CDDS_ITEM | CDDS_PREERASE)
+CDDS_ITEMPOSTERASE :: (CDDS_ITEM | CDDS_POSTERASE)
+CDDS_SUBITEM :: 0x20000
+
+CDIS_SELECTED :: 0x001
+CDIS_GRAYED :: 0x002
+CDIS_DISABLED :: 0x004
+CDIS_CHECKED :: 0x008
+CDIS_FOCUS :: 0x010
+CDIS_DEFAULT :: 0x020
+CDIS_HOT :: 0x040
+CDIS_MARKED :: 0x080
+CDIS_INDETERMINATE :: 0x100
+CDIS_SHOWKEYBOARDCUES :: 0x200
+
+// Image Lists
+CLR_NONE :: 0xFFFFFFFF
+CLR_DEFAULT :: 0xFF000000
+
+ILC_MASK :: 0x00000001
+ILC_COLOR :: 0x00000000
+ILC_COLORDDB :: 0x000000FE
+ILC_COLOR4 :: 0x00000004
+ILC_COLOR8 :: 0x00000008
+ILC_COLOR16 :: 0x00000010
+ILC_COLOR24 :: 0x00000018
+ILC_COLOR32 :: 0x00000020
+ILC_PALETTE :: 0x00000800
+ILC_MIRROR :: 0x00002000
+ILC_PERITEMMIRROR :: 0x00008000
+ILC_ORIGINALSIZE :: 0x00010000
+ILC_HIGHQUALITYSCALE :: 0x00020000
+
+ILD_NORMAL :: 0x00000000
+ILD_TRANSPARENT :: 0x00000001
+ILD_MASK :: 0x00000010
+ILD_IMAGE :: 0x00000020
+ILD_ROP :: 0x00000040
+ILD_BLEND25 :: 0x00000002
+ILD_BLEND50 :: 0x00000004
+ILD_OVERLAYMASK :: 0x00000F00
+ILD_PRESERVEALPHA :: 0x00001000
+ILD_SCALE :: 0x00002000
+ILD_DPISCALE :: 0x00004000
+ILD_ASYNC :: 0x00008000
+
+ILD_SELECTED :: ILD_BLEND50
+ILD_FOCUS :: ILD_BLEND25
+ILD_BLEND :: ILD_BLEND50
+CLR_HILIGHT :: CLR_DEFAULT
+
+ILS_NORMAL :: 0x00000000
+ILS_GLOW :: 0x00000001
+ILS_SHADOW :: 0x00000002
+ILS_SATURATE :: 0x00000004
+ILS_ALPHA :: 0x00000008
+
+ILGT_NORMAL :: 0x00000000
+ILGT_ASYNC :: 0x00000001
+
+ILCF_MOVE :: 0x00000000
+ILCF_SWAP :: 0x00000001
+
+ILP_NORMAL :: 0
+ILP_DOWNLEVEL :: 1
+
+IMAGELISTDRAWPARAMS :: struct {
+ cbSize: DWORD,
+ himl: HIMAGELIST,
+ i: i32,
+ hdcDst: HDC,
+ x: i32,
+ y: i32,
+ cx: i32,
+ cy: i32,
+ xBitmap: i32,
+ yBitmap: i32,
+ rgbBk: COLORREF,
+ rgbFg: COLORREF,
+ fStyle: UINT,
+ dwRop: DWORD,
+ fState: DWORD,
+ Frame: DWORD,
+ crEffect: COLORREF,
+}
+LPIMAGELISTDRAWPARAMS :: ^IMAGELISTDRAWPARAMS
+
+IMAGEINFO :: struct {
+ hbmImage: HBITMAP,
+ hbmMask: HBITMAP,
+ Unused1: i32,
+ Unused2: i32,
+ rcImage: RECT,
+}
+LPIMAGEINFO :: ^IMAGEINFO
+
+@(default_calling_convention="system")
+foreign Comctl32 {
+ ImageList_Create :: proc(cx, cy: i32, flags: UINT, cInitial, cGrow: i32) -> HIMAGELIST ---
+ ImageList_Destroy :: proc(himl: HIMAGELIST) -> BOOL ---
+ ImageList_GetImageCount :: proc(himl: HIMAGELIST) -> i32 ---
+ ImageList_SetImageCount :: proc(himl: HIMAGELIST, uNewCount: UINT) -> BOOL ---
+ ImageList_Add :: proc(himl: HIMAGELIST, hbmImage, hbmMask: HBITMAP) -> i32 ---
+ ImageList_ReplaceIcon :: proc(himl: HIMAGELIST, i: i32, hicon: HICON) -> i32 ---
+ ImageList_SetBkColor :: proc(himl: HIMAGELIST, clrBk: COLORREF) -> COLORREF ---
+ ImageList_GetBkColor :: proc(himl: HIMAGELIST) -> COLORREF ---
+ ImageList_SetOverlayImage :: proc(himl: HIMAGELIST, iImage: i32, iOverlay: i32) -> BOOL ---
+ ImageList_Draw :: proc(himl: HIMAGELIST, i: i32, hdcDst: HDC, x, y: i32, fStyle: UINT) -> BOOL ---
+ ImageList_Replace :: proc(himl: HIMAGELIST, i: i32, hbmImage, hbmMask: HBITMAP) -> BOOL ---
+ ImageList_AddMasked :: proc(himl: HIMAGELIST, hbmImage: HBITMAP, crMask: COLORREF) -> i32 ---
+ ImageList_DrawEx :: proc(himl: HIMAGELIST, i: i32, hdcDst: HDC, x, y, dx, dy: i32, rgbBk, rgbFg: COLORREF, fStyle: UINT) -> BOOL ---
+ ImageList_DrawIndirect :: proc(pimldp: ^IMAGELISTDRAWPARAMS) -> BOOL ---
+ ImageList_Remove :: proc(himl: HIMAGELIST, i: i32) -> BOOL ---
+ ImageList_GetIcon :: proc(himl: HIMAGELIST, i: i32, flags: UINT) -> HICON ---
+ ImageList_LoadImageW :: proc(hi: HINSTANCE, lpbmp: LPCWSTR, cx, cgrow: i32, crMask: COLORREF, uType, uFlags: UINT) -> HIMAGELIST ---
+ ImageList_Copy :: proc(himlDst: HIMAGELIST, iDst: i32, himlSrc: HIMAGELIST, iSrc: i32, uFlags: UINT) -> BOOL ---
+ ImageList_BeginDrag :: proc(himlTrack: HIMAGELIST, iTrack, dxHotspot, dyHotspot: i32) -> BOOL ---
+ ImageList_EndDrag :: proc() ---
+ ImageList_DragEnter :: proc(hwndLock: HWND, x, y: i32) -> BOOL ---
+ ImageList_DragLeave :: proc(hwndLock: HWND) -> BOOL ---
+ ImageList_DragMove :: proc(x, y: i32) -> BOOL ---
+ ImageList_SetDragCursorImage :: proc(himlDrag: HIMAGELIST, iDrag, dxHotspot, dyHotspot: i32) -> BOOL ---
+ ImageList_DragShowNolock :: proc(fShow: BOOL) -> BOOL ---
+ ImageList_GetDragImage :: proc(ppt, pptHotspot: ^POINT) -> HIMAGELIST ---
+ ImageList_Read :: proc(pstm: ^IStream) -> HIMAGELIST ---
+ ImageList_Write :: proc(himl: HIMAGELIST, pstm: ^IStream) -> BOOL ---
+ ImageList_ReadEx :: proc(dwFlags: DWORD, pstm: ^IStream, riid: REFIID, ppv: PVOID) -> HRESULT ---
+ ImageList_WriteEx :: proc(himl: HIMAGELIST, dwFlags: DWORD, pstm: ^IStream) -> HRESULT ---
+ ImageList_GetIconSize :: proc(himl: HIMAGELIST, cx, cy: ^i32) -> BOOL ---
+ ImageList_SetIconSize :: proc(himl: HIMAGELIST, cx, cy: i32) -> BOOL ---
+ ImageList_GetImageInfo :: proc(himl: HIMAGELIST, i: i32, pImageInfo: ^IMAGEINFO) -> BOOL ---
+ ImageList_Merge :: proc(himl1: HIMAGELIST, i1: i32, himl2: HIMAGELIST, i2: i32, dx, dy: i32) -> HIMAGELIST ---
+ ImageList_Duplicate :: proc(himl: HIMAGELIST) -> HIMAGELIST ---
+ HIMAGELIST_QueryInterface :: proc(himl: HIMAGELIST, riid: REFIID, ppv: rawptr) -> HRESULT ---
+}
+
+ImageList_AddIcon :: #force_inline proc "system" (himl: HIMAGELIST, hicon: HICON) -> i32 {
+ return ImageList_ReplaceIcon(himl, -1, hicon)
+}
+ImageList_RemoveAll :: #force_inline proc "system" (himl: HIMAGELIST) -> BOOL {
+ return ImageList_Remove(himl, -1)
+}
+ImageList_ExtractIcon :: #force_inline proc "system" (hi: HINSTANCE, himl: HIMAGELIST, i: i32) -> HICON {
+ return ImageList_GetIcon(himl, i, 0)
+}
+ImageList_LoadBitmap :: #force_inline proc "system" (hi: HINSTANCE, lpbmp: LPCWSTR, cx, cGrow: i32, crMask: COLORREF) -> HIMAGELIST {
+ return ImageList_LoadImageW(hi, lpbmp, cx, cGrow, crMask, IMAGE_BITMAP, 0)
+}
+
+// Status Bar Control
+SBT_NOBORDERS :: 0x0100
+SBT_POPOUT :: 0x0200
+SBT_RTLREADING :: 0x0400
+SBT_NOTABPARSING :: 0x0800
+SBT_OWNERDRAW :: 0x1000
+
+SBN_SIMPLEMODECHANGE :: SBN_FIRST - 0
+
+SB_SIMPLEID :: 0xFF
+
+@(default_calling_convention="system")
+foreign Comctl32 {
+ DrawStatusTextW :: proc(hDC: HDC, lprc: ^RECT, pszText: LPCWSTR, uFlags: UINT) ---
+ CreateStatusWindowW :: proc(style: LONG, lpszText: LPCWSTR, hwndParent: HWND, wID: UINT) -> HWND ---
+}
+
+// Menu Help
+MINSYSCOMMAND :: SC_SIZE
+
+@(default_calling_convention="system")
+foreign Comctl32 {
+ MenuHelp :: proc(uMsg: UINT, wParam: WPARAM, lParam: LPARAM, hMainMenu: HMENU, hInst: HINSTANCE, hwndStatus: HWND, lpwIDs: ^UINT) ---
+ ShowHideMenuCtl :: proc(hWnd: HWND, uFlags: UINT_PTR, lpInfo: LPINT) -> BOOL ---
+ GetEffectiveClientRect :: proc(hWnd: HWND, lprc: LPRECT, lpInfo: ^INT) ---
+}
+
+// Drag List
+DL_CURSORSET :: 0
+DL_STOPCURSOR :: 1
+DL_COPYCURSOR :: 2
+DL_MOVECURSOR :: 3
+
+DRAGLISTMSGSTRING :: "commctrl_DragListMsg"
+
+@(default_calling_convention="system")
+foreign Comctl32 {
+ MakeDragList :: proc(hLB: HWND) -> BOOL ---
+ DrawInsert :: proc(handParent: HWND, hLB: HWND, nItem: c_int) ---
+ LBItemFromPt :: proc(hLB: HWND, pt: POINT, bAutoScroll: BOOL) -> c_int ---
+}
+
+// Header Control
+HDTEXTFILTERW :: struct {
+ pszText: LPWSTR,
+ cchTextMax: INT,
+}
+HD_TEXTFILTERW :: HDTEXTFILTERW
+LPHDTEXTFILTERW :: ^HDTEXTFILTERW
+LPHD_TEXTFILTERW :: LPHDTEXTFILTERW
+
+HDITEMW :: struct {
+ mask: UINT,
+ cxy: c_int,
+ pszText: LPWSTR,
+ hbm: HBITMAP,
+ cchTextMax: c_int,
+ fmt: c_int,
+ lParam: LPARAM,
+ iImage: c_int,
+ iOrder: c_int,
+ type: UINT,
+ pvFilter: rawptr,
+}
+HD_ITEMW :: HDITEMW
+LPHDITEMW :: ^HDITEMW
+LPHD_ITEMW :: LPHDITEMW
+
+HDLAYOUT :: struct {
+ prc: ^RECT,
+ pwpos: ^WINDOWPOS,
+}
+HD_LAYOUT :: HDLAYOUT
+LPHDLAYOUT :: ^HDLAYOUT
+LPHD_LAYOUT :: LPHDLAYOUT
+
+HDHITTESTINFO :: struct {
+ pt: POINT,
+ flags: UINT,
+ iItem: c_int,
+}
+HD_HITTESTINFO :: HDHITTESTINFO
+LPHDHITTESTINFO :: ^HDHITTESTINFO
+LPHD_HITTESTINFO :: LPHDHITTESTINFO
+
+NMHEADERW :: struct {
+ hdr: NMHDR,
+ iItem: c_int,
+ iButton: c_int,
+ pitem: ^HDITEMW,
+}
+LPNMHEADERW :: ^NMHEADERW
+HD_NOTIFYW :: NMHEADERW
+LPHD_NOTIFYW :: LPNMHEADERW
+
+NMHDDISPINFOW :: struct {
+ hdr: NMHDR,
+ iItem: c_int,
+ mask: UINT,
+ pszText: LPWSTR,
+ cchTextMax: c_int,
+ iImage: c_int,
+ lParam: LPARAM,
+}
+LPNMHDDISPINFOW :: ^NMHDDISPINFOW
+
+NMHDFILTERBTNCLICK :: struct {
+ hdr: NMHDR,
+ iItem: c_int,
+ rc: RECT,
+}
+LPNMHDFILTERBTNCLICK :: ^NMHDFILTERBTNCLICK
+
+Header_GetItemCount :: #force_inline proc "system" (hwndHD: HWND) -> c_int {
+ return cast(c_int)SendMessageW(hwndHD, HDM_GETITEMCOUNT, 0, 0)
+}
+Header_InsertItem :: #force_inline proc "system" (hwndHD: HWND, i: c_int, phdi: ^HD_ITEMW) -> c_int {
+ return cast(c_int)SendMessageW(hwndHD, HDM_INSERTITEMW, cast(WPARAM)i, cast(LPARAM)uintptr(phdi))
+}
+Header_DeleteItem :: #force_inline proc "system" (hwndHD: HWND, i: c_int) -> BOOL {
+ return cast(BOOL)SendMessageW(hwndHD, HDM_DELETEITEM, cast(WPARAM)i, 0)
+}
+Header_GetItem :: #force_inline proc "system" (hwndHD: HWND, i: c_int, phdi: ^HD_ITEMW) -> BOOL {
+ return cast(BOOL)SendMessageW(hwndHD, HDM_GETITEMW, cast(WPARAM)i, cast(LPARAM)uintptr(phdi))
+}
+Header_SetItem :: #force_inline proc "system" (hwndHD: HWND, i: c_int, phdi: ^HD_ITEMW) -> BOOL {
+ return cast(BOOL)SendMessageW(hwndHD, HDM_SETITEMW, cast(WPARAM)i, cast(LPARAM)uintptr(phdi))
+}
+Header_Layout :: #force_inline proc "system" (hwndHD: HWND, playout: ^HD_LAYOUT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwndHD, HDM_LAYOUT, 0, cast(LPARAM)uintptr(playout))
+}
+
+Header_GetItemRect :: #force_inline proc "system" (hwnd: HWND, iItem: c_int, lprc: ^RECT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd,HDM_GETITEMRECT,cast(WPARAM)iItem,cast(LPARAM)uintptr(lprc))
+}
+Header_SetImageList :: #force_inline proc "system" (hwnd: HWND, himl: HIMAGELIST) -> HIMAGELIST {
+ return cast(HIMAGELIST)uintptr(SendMessageW(hwnd,HDM_SETIMAGELIST,0,cast(LPARAM)uintptr(himl)))
+}
+Header_GetImageList :: #force_inline proc "system" (hwnd: HWND) -> HIMAGELIST {
+ return cast(HIMAGELIST)uintptr(SendMessageW(hwnd,HDM_GETIMAGELIST,0,0))
+}
+Header_OrderToIndex :: #force_inline proc "system" (hwnd: HWND, i: c_int) -> c_int {
+ return cast(c_int)SendMessageW(hwnd,HDM_ORDERTOINDEX,cast(WPARAM)i,0)
+}
+Header_CreateDragImage :: #force_inline proc "system" (hwnd: HWND, i: c_int) -> HIMAGELIST {
+ return cast(HIMAGELIST)uintptr(SendMessageW(hwnd,HDM_CREATEDRAGIMAGE,cast(WPARAM)i,0))
+}
+Header_GetOrderArray :: #force_inline proc "system" (hwnd: HWND, iCount: c_int, lpi: ^c_int) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd,HDM_GETORDERARRAY,cast(WPARAM)iCount,cast(LPARAM)uintptr(lpi))
+}
+Header_SetOrderArray :: #force_inline proc "system" (hwnd: HWND, iCount: c_int, lpi: ^c_int) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd,HDM_SETORDERARRAY,cast(WPARAM)iCount,cast(LPARAM)uintptr(lpi))
+}
+Header_SetHotDivider :: #force_inline proc "system" (hwnd: HWND, fPos: BOOL, dw: DWORD) -> c_int {
+ return cast(c_int)SendMessageW(hwnd,HDM_SETHOTDIVIDER,cast(WPARAM)fPos,cast(LPARAM)dw)
+}
+Header_SetBitmapMargin :: #force_inline proc "system" (hwnd: HWND, iWidth: c_int) -> c_int {
+ return cast(c_int)SendMessageW(hwnd,HDM_SETBITMAPMARGIN,cast(WPARAM)iWidth,0)
+}
+Header_GetBitmapMargin :: #force_inline proc "system" (hwnd: HWND) -> c_int {
+ return cast(c_int)SendMessageW(hwnd,HDM_GETBITMAPMARGIN,0,0)
+}
+Header_SetUnicodeFormat :: #force_inline proc "system" (hwnd: HWND, fUnicode: BOOL) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd,HDM_SETUNICODEFORMAT,cast(WPARAM)fUnicode,0)
+}
+Header_GetUnicodeFormat :: #force_inline proc "system" (hwnd: HWND) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd,HDM_GETUNICODEFORMAT,0,0)
+}
+Header_SetFilterChangeTimeout :: #force_inline proc "system" (hwnd: HWND, i: c_int) -> c_int {
+ return cast(c_int)SendMessageW(hwnd,HDM_SETFILTERCHANGETIMEOUT,0,cast(LPARAM)i)
+}
+Header_EditFilter :: #force_inline proc "system" (hwnd: HWND, i: c_int, fDiscardChanges: BOOL) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd,HDM_EDITFILTER,cast(WPARAM)i,MAKELPARAM(fDiscardChanges,0))
+}
+Header_ClearFilter :: #force_inline proc "system" (hwnd: HWND, i: c_int) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd,HDM_CLEARFILTER,cast(WPARAM)i,0)
+}
+Header_ClearAllFilters :: #force_inline proc "system" (hwnd: HWND) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd,HDM_CLEARFILTER,~WPARAM(0),0)
+}
+
+// Toolbar Control
+COLORSCHEME :: struct {
+ dwSize: DWORD,
+ clrBtnHighlight: COLORREF,
+ clrBtnShadow: COLORREF,
+}
+LPCOLORSCHEME :: ^COLORSCHEME
+
+COLORMAP :: struct {
+ from: COLORREF,
+ to: COLORREF,
+}
+LPCOLORMAP :: ^COLORMAP
+
+TBBUTTON :: struct {
+ iBitmap: c_int,
+ idCommand: c_int,
+ fsState: BYTE,
+ fsStyle: BYTE,
+ bReserved: [size_of(uintptr) - 2]BYTE,
+ dwData: DWORD_PTR,
+ iString: INT_PTR,
+}
+PTBBUTTON :: ^TBBUTTON
+LPTBBUTTON :: ^TBBUTTON
+LPCTBBUTTON :: ^TBBUTTON
+
+TBADDBITMAP :: struct {
+ hInst: HINSTANCE,
+ nID: UINT_PTR,
+}
+LPTBADDBITMAP :: ^TBADDBITMAP
+
+TBSAVEPARAMSW :: struct {
+ hkr: HKEY,
+ pszSubKey: LPCWSTR,
+ pszValueName: LPCWSTR,
+}
+
+TBINSERTMARK :: struct {
+ iButton: c_int,
+ dwFlags: DWORD,
+}
+LPTBINSERTMARK :: ^TBINSERTMARK
+
+TBREPLACEBITMAP :: struct {
+ hInstOld: HINSTANCE,
+ nIDOld: UINT_PTR,
+ hInstNew: HINSTANCE,
+ nIDNew: UINT_PTR,
+ nButtons: c_int,
+}
+LPTBREPLACEBITMAP :: ^TBREPLACEBITMAP
+
+TBBUTTONINFOW :: struct {
+ cbSize: UINT,
+ dwMask: DWORD,
+ idCommand: c_int,
+ iImage: c_int,
+ fsState: BYTE,
+ fsStyle: BYTE,
+ cx: WORD,
+ lParam: DWORD_PTR,
+ pszText: LPWSTR,
+ cchText: c_int,
+}
+LPTBBUTTONINFOW :: ^TBBUTTONINFOW
+
+TBMETRICS :: struct {
+ cbSize: UINT,
+ dwMask: DWORD,
+ cxPad: c_int,
+ cyPad: c_int,
+ cxBarPad: c_int,
+ cyBarPad: c_int,
+ cxButtonSpacing: c_int,
+ cyButtonSpacing: c_int,
+}
+LPTBMETRICS :: ^TBMETRICS
+
+NMTTCUSTOMDRAW :: struct {
+ nmcd: NMCUSTOMDRAW,
+ uDrawFlags: UINT,
+}
+LPNMTTCUSTOMDRAW :: ^NMTTCUSTOMDRAW
+
+@(default_calling_convention="system")
+foreign Comctl32 {
+ CreateToolbarEx :: proc(hwnd: HWND, ws: DWORD, wID: UINT, nBitmaps: c_int, hBMInst: HINSTANCE, wBMID: UINT_PTR, lpButtons: LPCTBBUTTON, iNumButtons: c_int, dxButton,dyButton: c_int, dxBitmap,dyBitmap: c_int, uStructSize: UINT) -> HWND ---
+ CreateMappedBitmap :: proc(hInstance: HINSTANCE, idBitmap: INT_PTR, wFlags: UINT, lpColorMap: LPCOLORMAP, iNumMaps: c_int) -> HBITMAP ---
+}
+
+// Button Control
+BUTTON_IMAGELIST_ALIGN_LEFT :: 0
+BUTTON_IMAGELIST_ALIGN_RIGHT :: 1
+BUTTON_IMAGELIST_ALIGN_TOP :: 2
+BUTTON_IMAGELIST_ALIGN_BOTTOM :: 3
+BUTTON_IMAGELIST_ALIGN_CENTER :: 4
+
+BCSIF_GLYPH :: 0x0001
+BCSIF_IMAGE :: 0x0002
+BCSIF_STYLE :: 0x0004
+BCSIF_SIZE :: 0x0008
+
+BCSS_NOSPLIT :: 0x0001
+BCSS_STRETCH :: 0x0002
+BCSS_ALIGNLEFT :: 0x0004
+BCSS_IMAGE :: 0x0008
+
+BUTTON_IMAGELIST :: struct {
+ himl: HIMAGELIST,
+ margin: RECT,
+ uAlign: UINT,
+}
+PBUTTON_IMAGELIST :: ^BUTTON_IMAGELIST
+
+BUTTON_SPLITINFO :: struct {
+ mask: UINT,
+ himlGlyph: HIMAGELIST,
+ uSplitStyle: UINT,
+ size: SIZE,
+}
+PBUTTON_SPLITINFO :: ^BUTTON_SPLITINFO
+
+NMBCHOTITEM :: struct {
+ hdr: NMHDR,
+ dwFlags: DWORD,
+}
+LPNMBCHOTITEM :: ^NMBCHOTITEM
+
+NMBCDROPDOWN :: struct {
+ hdr: NMHDR,
+ rcButton: RECT,
+}
+LPNMBCDROPDOWN :: ^NMBCDROPDOWN
+
+// BCM_SETIMAGELIST value
+BCCL_NOGLYPH :: cast(HIMAGELIST)(~uintptr(0))
+
+Button_GetIdealSize :: #force_inline proc "system" (hwnd: HWND, psize: ^SIZE) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, BCM_GETIDEALSIZE, 0, cast(LPARAM)uintptr(psize))
+}
+Button_SetImageList :: #force_inline proc "system" (hwnd: HWND, pbuttonImagelist: PBUTTON_IMAGELIST) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, BCM_SETIMAGELIST, 0, cast(LPARAM)uintptr(pbuttonImagelist))
+}
+Button_GetImageList :: #force_inline proc "system" (hwnd: HWND, pbuttonImagelist: PBUTTON_IMAGELIST) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, BCM_GETIMAGELIST, 0, cast(LPARAM)uintptr(pbuttonImagelist))
+}
+Button_SetTextMargin :: #force_inline proc "system" (hwnd: HWND, pmargin: ^RECT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, BCM_SETTEXTMARGIN, 0, cast(LPARAM)uintptr(pmargin))
+}
+Button_GetTextMargin :: #force_inline proc "system" (hwnd: HWND, pmargin: ^RECT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, BCM_GETTEXTMARGIN, 0, cast(LPARAM)uintptr(pmargin))
+}
+Button_SetNote :: #force_inline proc "system" (hwnd: HWND, psz: LPCWSTR) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, BCM_SETNOTE, 0, cast(LPARAM)uintptr(psz))
+}
+Button_GetNote :: #force_inline proc "system" (hwnd: HWND, psz: LPCWSTR, pcc: ^c_int) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, BCM_GETNOTE, uintptr(pcc), cast(LPARAM)uintptr(psz))
+}
+Button_GetNoteLength :: #force_inline proc "system" (hwnd: HWND) -> LRESULT {
+ return SendMessageW(hwnd, BCM_GETNOTELENGTH, 0, 0)
+}
+Button_SetElevationRequiredState :: #force_inline proc "system" (hwnd: HWND, fRequired: BOOL) -> LRESULT {
+ return SendMessageW(hwnd, BCM_SETSHIELD, 0, cast(LPARAM)fRequired)
+}
+Button_SetDropDownState :: #force_inline proc "system" (hwnd: HWND, fDropDown: BOOL) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, BCM_SETDROPDOWNSTATE, cast(WPARAM)fDropDown, 0)
+}
+Button_SetSplitInfo :: #force_inline proc "system" (hwnd: HWND, psi: ^BUTTON_SPLITINFO) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, BCM_SETSPLITINFO, 0, cast(LPARAM)uintptr(psi))
+}
+Button_GetSplitInfo :: #force_inline proc "system" (hwnd: HWND, psi: ^BUTTON_SPLITINFO) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, BCM_GETSPLITINFO, 0, cast(LPARAM)uintptr(psi))
+}
+
+// Edit Control
+EDITBALLOONTIP :: struct {
+ cbStruct: DWORD,
+ pszTitle: LPCWSTR,
+ pszText: LPCWSTR,
+ ttiIcon: INT,
+}
+PEDITBALLOONTIP :: ^EDITBALLOONTIP
+
+Edit_SetCueBannerText :: #force_inline proc "system" (hwnd: HWND, lpcwText: LPCWSTR) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, EM_SETCUEBANNER, 0, cast(LPARAM)uintptr(lpcwText))
+}
+Edit_SetCueBannerTextFocused :: #force_inline proc "system" (hwnd: HWND, lpcwText: LPCWSTR, fDrawFocused: BOOL) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, EM_SETCUEBANNER, cast(WPARAM)fDrawFocused, cast(LPARAM)uintptr(lpcwText))
+}
+Edit_GetCueBannerText :: #force_inline proc "system" (hwnd: HWND, lpwText: LPWSTR, cchText: LONG) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, EM_GETCUEBANNER, uintptr(lpwText), cast(LPARAM)cchText)
+}
+Edit_ShowBalloonTip :: #force_inline proc "system" (hwnd: HWND, peditballoontip: PEDITBALLOONTIP) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, EM_SHOWBALLOONTIP, 0, cast(LPARAM)uintptr(peditballoontip))
+}
+Edit_HideBalloonTip :: #force_inline proc "system" (hwnd: HWND) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, EM_HIDEBALLOONTIP, 0, 0)
+}
+
+Edit_SetHilite :: #force_inline proc "system" (hwndCtl: HWND, ichStart: c_int, ichEnd: c_int) {
+ SendMessageW(hwndCtl, EM_SETHILITE, cast(WPARAM)ichStart, cast(LPARAM)ichEnd)
+}
+Edit_GetHilite :: #force_inline proc "system" (hwndCtl: HWND) -> DWORD {
+ return cast(DWORD)SendMessageW(hwndCtl, EM_GETHILITE, 0, 0)
+}
+
+Edit_NoSetFocus :: #force_inline proc "system" (hwndCtl: HWND) {
+ SendMessageW(hwndCtl, EM_NOSETFOCUS, 0, 0)
+}
+Edit_TakeFocus :: #force_inline proc "system" (hwndCtl: HWND) {
+ SendMessageW(hwndCtl, EM_TAKEFOCUS, 0, 0)
+}
+
+// Up Down Control
+@(default_calling_convention="system")
+foreign Comctl32 {
+ CreateUpDownControl :: proc(dwStyle: DWORD, x,y: c_int, cx,cy: c_int, hParent: HWND, nID: c_int, hInst: HINSTANCE, hBuddy: HWND, nUpper,nLower,nPos: c_int) -> HWND ---
+}
+
+// Progress Bar Control
+PBRANGE :: struct {
+ iLow: c_int,
+ iHigh: c_int,
+}
+PPBRANGE :: ^PBRANGE
+
+// Hot Key Control
+HOTKEYF_SHIFT :: 0x1
+HOTKEYF_CONTROL :: 0x2
+HOTKEYF_ALT :: 0x4
+HOTKEYF_EXT :: 0x8
+
+HKCOMB_NONE :: 0x01
+HKCOMB_S :: 0x02
+HKCOMB_C :: 0x04
+HKCOMB_A :: 0x08
+HKCOMB_SC :: 0x10
+HKCOMB_SA :: 0x20
+HKCOMB_CA :: 0x40
+HKCOMB_SCA :: 0x80
+
+// List View Control
+LVSIL_NORMAL :: 0
+LVSIL_SMALL :: 1
+LVSIL_STATE :: 2
+
+LVIF_TEXT :: 0x001
+LVIF_IMAGE :: 0x002
+LVIF_PARAM :: 0x004
+LVIF_STATE :: 0x008
+LVIF_INDENT :: 0x010
+LVIF_GROUPID :: 0x100
+LVIF_COLUMNS :: 0x200
+LVIF_NORECOMPUTE :: 0x800
+
+LVIS_FOCUSED :: 0x01
+LVIS_SELECTED :: 0x02
+LVIS_CUT :: 0x04
+LVIS_DROPHILITED :: 0x08
+LVIS_GLOW :: 0x10
+LVIS_ACTIVATING :: 0x20
+
+LVIS_OVERLAYMASK :: 0x0F00
+LVIS_STATEIMAGEMASK :: 0xF000
+
+LVNI_ALL :: 0x000
+LVNI_FOCUSED :: 0x001
+LVNI_SELECTED :: 0x002
+LVNI_CUT :: 0x004
+LVNI_DROPHILITED :: 0x008
+LVNI_ABOVE :: 0x100
+LVNI_BELOW :: 0x200
+LVNI_TOLEFT :: 0x400
+LVNI_TORIGHT :: 0x800
+
+LVFI_PARAM :: 0x01
+LVFI_STRING :: 0x02
+LVFI_PARTIAL :: 0x08
+LVFI_WRAP :: 0x20
+LVFI_NEARESTXY :: 0x40
+
+I_INDENTCALLBACK :: -1
+
+I_GROUPIDCALLBACK :: -1
+I_GROUPIDNONE :: -2
+
+LPSTR_TEXTCALLBACKW :: cast(LPWSTR)~uintptr(0)
+
+I_IMAGECALLBACK :: -1
+I_IMAGENONE :: -2
+
+I_COLUMNSCALLBACK :: ~UINT(0)
+
+LVIR_BOUNDS :: 0
+LVIR_ICON :: 1
+LVIR_LABEL :: 2
+LVIR_SELECTBOUNDS :: 3
+
+LVHT_NOWHERE :: 0x1
+LVHT_ONITEMICON :: 0x2
+LVHT_ONITEMLABEL :: 0x4
+LVHT_ONITEMSTATEICON :: 0x8
+LVHT_ONITEM :: LVHT_ONITEMICON | LVHT_ONITEMLABEL | LVHT_ONITEMSTATEICON
+
+LVHT_ABOVE :: 0x08
+LVHT_BELOW :: 0x10
+LVHT_TORIGHT :: 0x20
+LVHT_TOLEFT :: 0x40
+
+LVA_DEFAULT :: 0x0
+LVA_ALIGNLEFT :: 0x1
+LVA_ALIGNTOP :: 0x2
+LVA_SNAPTOGRID :: 0x5
+
+LVCF_FMT :: 0x001
+LVCF_WIDTH :: 0x002
+LVCF_TEXT :: 0x004
+LVCF_SUBITEM :: 0x008
+LVCF_IMAGE :: 0x010
+LVCF_ORDER :: 0x020
+LVCF_MINWIDTH :: 0x040
+LVCF_DEFAULTWIDTH :: 0x080
+LVCF_IDEALWIDTH :: 0x100
+
+LVCFMT_LEFT :: 0x0000000
+LVCFMT_RIGHT :: 0x0000001
+LVCFMT_CENTER :: 0x0000002
+LVCFMT_FIXED_WIDTH :: 0x0000100
+LVCFMT_IMAGE :: 0x0000800
+LVCFMT_BITMAP_ON_RIGHT :: 0x0001000
+LVCFMT_COL_HAS_IMAGES :: 0x0008000
+LVCFMT_NO_DPI_SCALE :: 0x0040000
+LVCFMT_FIXED_RATIO :: 0x0080000
+LVCFMT_LINE_BREAK :: 0x0100000
+LVCFMT_FILL :: 0x0200000
+LVCFMT_WRAP :: 0x0400000
+LVCFMT_NO_TITLE :: 0x0800000
+LVCFMT_SPLITBUTTON :: 0x1000000
+
+LVCFMT_JUSTIFYMASK :: 0x3
+LVCFMT_TILE_PLACEMENTMASK :: (LVCFMT_LINE_BREAK|LVCFMT_FILL)
+
+LVSCW_AUTOSIZE :: -1
+LVSCW_AUTOSIZE_USEHEADER :: -2
+
+LVSICF_NOINVALIDATEALL :: 0x1
+LVSICF_NOSCROLL :: 0x2
+
+LVS_EX_GRIDLINES :: 0x00000001
+LVS_EX_SUBITEMIMAGES :: 0x00000002
+LVS_EX_CHECKBOXES :: 0x00000004
+LVS_EX_TRACKSELECT :: 0x00000008
+LVS_EX_HEADERDRAGDROP :: 0x00000010
+LVS_EX_FULLROWSELECT :: 0x00000020
+LVS_EX_ONECLICKACTIVATE :: 0x00000040
+LVS_EX_TWOCLICKACTIVATE :: 0x00000080
+LVS_EX_FLATSB :: 0x00000100
+LVS_EX_REGIONAL :: 0x00000200
+LVS_EX_INFOTIP :: 0x00000400
+LVS_EX_UNDERLINEHOT :: 0x00000800
+LVS_EX_UNDERLINECOLD :: 0x00001000
+LVS_EX_MULTIWORKAREAS :: 0x00002000
+LVS_EX_LABELTIP :: 0x00004000
+LVS_EX_BORDERSELECT :: 0x00008000
+LVS_EX_DOUBLEBUFFER :: 0x00010000
+LVS_EX_HIDELABELS :: 0x00020000
+LVS_EX_SINGLEROW :: 0x00040000
+LVS_EX_SNAPTOGRID :: 0x00080000
+LVS_EX_SIMPLESELECT :: 0x00100000
+LVS_EX_JUSTIFYCOLUMNS :: 0x00200000
+LVS_EX_TRANSPARENTBKGND :: 0x00400000
+LVS_EX_TRANSPARENTSHADOWTEXT :: 0x00800000
+LVS_EX_AUTOAUTOARRANGE :: 0x01000000
+LVS_EX_HEADERINALLVIEWS :: 0x02000000
+LVS_EX_AUTOCHECKSELECT :: 0x08000000
+LVS_EX_AUTOSIZECOLUMNS :: 0x10000000
+LVS_EX_COLUMNSNAPPOINTS :: 0x40000000
+LVS_EX_COLUMNOVERFLOW :: 0x80000000
+
+LV_MAX_WORKAREAS :: 16
+
+LVBKIF_SOURCE_NONE :: 0x0
+LVBKIF_SOURCE_HBITMAP :: 0x1
+LVBKIF_SOURCE_URL :: 0x2
+LVBKIF_SOURCE_MASK :: 0x3
+
+LVBKIF_STYLE_NORMAL :: 0x00
+LVBKIF_STYLE_TILE :: 0x10
+LVBKIF_STYLE_MASK :: 0x10
+
+LVBKIF_FLAG_TILEOFFSET :: 0x100
+
+LVBKIF_TYPE_WATERMARK :: 0x10000000
+
+LV_VIEW_ICON :: 0x0
+LV_VIEW_DETAILS :: 0x1
+LV_VIEW_SMALLICON :: 0x2
+LV_VIEW_LIST :: 0x3
+LV_VIEW_TILE :: 0x4
+LV_VIEW_MAX :: 0x4
+
+LVGF_NONE :: 0x00
+LVGF_HEADER :: 0x01
+LVGF_FOOTER :: 0x02
+LVGF_STATE :: 0x04
+LVGF_ALIGN :: 0x08
+LVGF_GROUPID :: 0x10
+
+LVGS_NORMAL :: 0x0
+LVGS_COLLAPSED :: 0x1
+LVGS_HIDDEN :: 0x2
+
+LVGA_HEADER_LEFT :: 0x1
+LVGA_HEADER_CENTER :: 0x2
+LVGA_HEADER_RIGHT :: 0x4
+LVGA_FOOTER_LEFT :: 0x8
+LVGA_FOOTER_CENTER :: 0x10
+LVGA_FOOTER_RIGHT :: 0x20
+
+LVGMF_NONE :: 0x0
+LVGMF_BORDERSIZE :: 0x1
+LVGMF_BORDERCOLOR :: 0x2
+LVGMF_TEXTCOLOR :: 0x4
+
+LVTVIF_AUTOSIZE :: 0x0
+LVTVIF_FIXEDWIDTH :: 0x1
+LVTVIF_FIXEDHEIGHT :: 0x2
+LVTVIF_FIXEDSIZE :: 0x3
+
+LVTVIM_TILESIZE :: 0x1
+LVTVIM_COLUMNS :: 0x2
+LVTVIM_LABELMARGIN :: 0x4
+
+LVIM_AFTER :: 0x1
+
+LVKF_ALT :: 0x1
+LVKF_CONTROL :: 0x2
+LVKF_SHIFT :: 0x4
+
+LVCDI_ITEM :: 0x0
+LVCDI_GROUP :: 0x1
+
+LVCDRF_NOSELECT :: 0x10000
+LVCDRF_NOGROUPFRAME :: 0x20000
+
+LVN_ITEMCHANGING :: (LVN_FIRST-0)
+LVN_ITEMCHANGED :: (LVN_FIRST-1)
+LVN_INSERTITEM :: (LVN_FIRST-2)
+LVN_DELETEITEM :: (LVN_FIRST-3)
+LVN_DELETEALLITEMS :: (LVN_FIRST-4)
+LVN_BEGINLABELEDITA :: (LVN_FIRST-5)
+LVN_BEGINLABELEDITW :: (LVN_FIRST-75)
+LVN_ENDLABELEDITA :: (LVN_FIRST-6)
+LVN_ENDLABELEDITW :: (LVN_FIRST-76)
+LVN_COLUMNCLICK :: (LVN_FIRST-8)
+LVN_BEGINDRAG :: (LVN_FIRST-9)
+LVN_BEGINRDRAG :: (LVN_FIRST-11)
+LVN_ODCACHEHINT :: (LVN_FIRST-13)
+LVN_ODFINDITEMA :: (LVN_FIRST-52)
+LVN_ODFINDITEMW :: (LVN_FIRST-79)
+LVN_ITEMACTIVATE :: (LVN_FIRST-14)
+LVN_ODSTATECHANGED :: (LVN_FIRST-15)
+LVN_HOTTRACK :: (LVN_FIRST-21)
+LVN_GETDISPINFOA :: (LVN_FIRST-50)
+LVN_GETDISPINFOW :: (LVN_FIRST-77)
+LVN_SETDISPINFOA :: (LVN_FIRST-51)
+LVN_SETDISPINFOW :: (LVN_FIRST-78)
+LVN_KEYDOWN :: (LVN_FIRST-55)
+LVN_MARQUEEBEGIN :: (LVN_FIRST-56)
+LVN_GETINFOTIPA :: (LVN_FIRST-57)
+LVN_GETINFOTIPW :: (LVN_FIRST-58)
+LVN_BEGINSCROLL :: (LVN_FIRST-80)
+LVN_ENDSCROLL :: (LVN_FIRST-81)
+
+LVIF_DI_SETITEM :: 0x1000
+
+LVGIT_UNFOLDED :: 0x1
+
+LVITEMW :: struct {
+ mask: UINT,
+ iItem: c_int,
+ iSubItem: c_int,
+ state: UINT,
+ stateMask: UINT,
+ pszText: LPWSTR,
+ cchTextMax: c_int,
+ iImage: c_int,
+ lParam: LPARAM,
+ iIndent: c_int,
+ iGroupId: c_int,
+ cColumns: UINT,
+ puColumns: PUINT,
+}
+LV_ITEMW :: LVITEMW
+LPLVITEMW :: ^LVITEMW
+LPLV_ITEMW :: LPLVITEMW
+
+LVFINDINFOW :: struct {
+ flags: UINT,
+ psz: LPCWSTR,
+ lParam: LPARAM,
+ pt: POINT,
+ vkDirection: UINT,
+}
+LPFINDINFOW :: ^LVFINDINFOW
+LV_FINDINFOW :: LVFINDINFOW
+
+LVHITTESTINFO :: struct {
+ pt: POINT,
+ flags: UINT,
+ iItem: c_int,
+ iSubItem: c_int,
+}
+LV_HITTESTINFO :: LVHITTESTINFO
+LPLVHITTESTINFO :: ^LVHITTESTINFO
+LPLV_HITTESTINFO :: LPLVHITTESTINFO
+
+LVCOLUMNW :: struct {
+ mask: UINT,
+ fmt: c_int,
+ cx: c_int,
+ pszText: LPWSTR,
+ cchTextMax: c_int,
+ iSubItem: c_int,
+ iImage: c_int,
+ iOrder: c_int,
+ cxMin: c_int,
+ cxDefault: c_int,
+ cxIdeal: c_int,
+}
+LV_COLUMNW :: LVCOLUMNW
+LPLVCOLUMNW :: ^LVCOLUMNW
+LPLV_COLUMNW :: LPLVCOLUMNW
+
+LVBKIMAGEW :: struct {
+ ulFlags: ULONG,
+ hbm: HBITMAP,
+ pszImage: LPWSTR,
+ cchImageMax: UINT,
+ xOffsetPercent: c_int,
+ yOffsetPercent: c_int,
+}
+LV_BKIMAGEW :: LVBKIMAGEW
+LPLVBKIMAGEW :: ^LVBKIMAGEW
+LPLV_BKIMAGEW :: LPLVBKIMAGEW
+
+LVGROUP :: struct {
+ cbSize: UINT,
+ mask: UINT,
+ pszHeader: LPWSTR,
+ cchHeader: c_int,
+ pszFooter: LPWSTR,
+ cchFooter: c_int,
+ iGroupId: c_int,
+ stateMask: UINT,
+ state: UINT,
+ uAlign: UINT,
+}
+PLVGROUP :: ^LVGROUP
+
+LVGROUPMETRICS :: struct {
+ cbSize: UINT,
+ mask: UINT,
+ Left: UINT,
+ Top: UINT,
+ Right: UINT,
+ Bottom: UINT,
+ crLeft: COLORREF,
+ crTop: COLORREF,
+ crRight: COLORREF,
+ crBottom: COLORREF,
+ crHeader: COLORREF,
+ crFooter: COLORREF,
+}
+PLVGROUPMETRICS :: ^LVGROUPMETRICS
+
+LVINSERTGROUPSORTED :: struct {
+ pfnGroupCompare: PFNLVGROUPCOMPARE,
+ pvData: rawptr,
+ lvGroup: LVGROUP,
+}
+PLVINSERTGROUPSORTED :: ^LVINSERTGROUPSORTED
+
+LVTILEVIEWINFO :: struct {
+ cbSize: UINT,
+ dwMask: DWORD,
+ dwFlags: DWORD,
+ sizeTile: SIZE,
+ cLines: c_int,
+ rcLabelMargin: RECT,
+}
+PLVTILEVIEWINFO :: ^LVTILEVIEWINFO
+
+LVTILEINFO :: struct {
+ cbSize: UINT,
+ iItem: c_int,
+ cColumns: UINT,
+ puColumns: PUINT,
+}
+PLVTILEINFO :: ^LVTILEINFO
+
+LVINSERTMARK :: struct {
+ cbSize: UINT,
+ dwFlags: DWORD,
+ iItem: c_int,
+ dwReserved: DWORD,
+}
+LPLVINSERTMARK :: ^LVINSERTMARK
+
+LVSETINFOTIP :: struct {
+ cbSize: UINT,
+ dwFlags: DWORD,
+ pszText: LPWSTR,
+ iItem: c_int,
+ iSubItem: c_int,
+}
+PLVSETINFOTIP :: ^LVSETINFOTIP
+
+NMLISTVIEW :: struct {
+ hdr: NMHDR,
+ iItem: c_int,
+ iSubItem: c_int,
+ uNewState: UINT,
+ uOldState: UINT,
+ uChanged: UINT,
+ ptAction: POINT,
+ lParam: LPARAM,
+}
+NM_LISTVIEW :: NMLISTVIEW
+LPNMLISTVIEW :: ^NMLISTVIEW
+LPNM_LISTVIEW :: LPNMLISTVIEW
+
+NMITEMACTIVATE :: struct {
+ hdr: NMHDR,
+ iItem: c_int,
+ iSubItem: c_int,
+ uNewState: UINT,
+ uOldState: UINT,
+ uChanged: UINT,
+ ptAction: POINT,
+ lParam: LPARAM,
+ uKeyFlags: UINT,
+}
+NM_ITEMACTIVATE :: NMITEMACTIVATE
+LPNMITEMACTIVATE :: ^NMITEMACTIVATE
+LPNM_ITEMACTIVATE :: LPNMITEMACTIVATE
+
+NMLVCUSTOMDRAW :: struct {
+ nmcd: NMCUSTOMDRAW,
+ clrText: COLORREF,
+ clrTextBk: COLORREF,
+ iSubItem: c_int,
+ dwItemType: DWORD,
+ clrFace: COLORREF,
+ iIconEffect: c_int,
+ iIconPhase: c_int,
+ iPartId: c_int,
+ iStateId: c_int,
+ rcText: RECT,
+ uAlign: UINT,
+}
+NMLV_CUSTOMDRAW :: NMLVCUSTOMDRAW
+LPNMLVCUSTOMDRAW :: ^NMLVCUSTOMDRAW
+LPNMLV_CUSTOMDRAW :: LPNMLVCUSTOMDRAW
+
+NMLVCACHEHINT :: struct {
+ hdr: NMHDR,
+ iFrom: c_int,
+ iTo: c_int,
+}
+LPNMLVCACHEHINT :: ^NMLVCACHEHINT
+NM_CACHEHINT :: NMLVCACHEHINT
+PNM_CACHEHINT :: LPNMLVCACHEHINT
+LPNM_CACHEHINT :: LPNMLVCACHEHINT
+
+NMLVFINDITEMW :: struct {
+ hdr: NMHDR,
+ iStart: c_int,
+ lvfi: LVFINDINFOW,
+}
+LPNMLVFINDITEMW :: ^NMLVFINDITEMW
+NM_FINDITEMW :: NMLVFINDITEMW
+PNM_FINDITEMW :: LPNMLVFINDITEMW
+LPNM_FINDITEMW :: LPNMLVFINDITEMW
+
+NMLVODSTATECHANGE :: struct {
+ hdr: NMHDR,
+ iFrom: c_int,
+ iTo: c_int,
+ uNewState: UINT,
+ uOldState: UINT,
+}
+LPNMLVODSTATECHANGE :: ^NMLVODSTATECHANGE
+NM_ODSTATECHANGE :: NMLVODSTATECHANGE
+PNM_ODSTATECHANGE :: NMLVODSTATECHANGE
+LPNM_ODSTATECHANGE :: LPNMLVODSTATECHANGE
+
+LVDISPINFOW :: struct {
+ hdr: NMHDR,
+ item: LVITEMW,
+}
+LV_DISPINFO :: LVDISPINFOW
+LPNMLVDISPINFOW :: ^LVDISPINFOW
+
+NMLVKEYDOWN :: struct #packed {
+ hdr: NMHDR,
+ wVKey: WORD,
+ flags: UINT,
+}
+LV_KEYDOWN :: NMLVKEYDOWN
+LPNMLVKEYDOWN :: ^NMLVKEYDOWN
+
+NMLVGETINFOTIPW :: struct {
+ hdr: NMHDR,
+ dwFlags: DWORD,
+ pszText: LPWSTR,
+ cchTextMax: c_int,
+ iItem: c_int,
+ iSubItem: c_int,
+ lParam: LPARAM,
+}
+LPNMLVGETINFOTIPW :: ^NMLVGETINFOTIPW
+
+NMLVSCROLL :: struct {
+ hdr: NMHDR,
+ dx: c_int,
+ dy: c_int,
+}
+LPNMLVSCROLL :: ^NMLVSCROLL
+
+PFNLVCOMPARE :: #type proc "system" (lpItem1,lpItem2: LPARAM, lpUser: LPARAM) -> c_int
+PFNLVGROUPCOMPARE :: #type proc "system" (item1,item2: c_int, user: rawptr) -> c_int
+
+INDEXTOSTATEIMAGEMASK :: #force_inline proc "system" (i: UINT) -> UINT {
+ return i << 12
+}
+
+ListView_GetItem :: #force_inline proc "system" (hwnd: HWND, pitem: ^LV_ITEMW) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_GETITEMW, 0, cast(LPARAM)uintptr(pitem))
+}
+ListView_SetItem :: #force_inline proc "system" (hwnd: HWND, pitem: ^LV_ITEMW) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_SETITEMW, 0, cast(LPARAM)uintptr(pitem))
+}
+ListView_InsertItem :: #force_inline proc "system" (hwnd: HWND, pitem: ^LV_ITEMW) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_INSERTITEMW, 0, cast(LPARAM)uintptr(pitem))
+}
+ListView_DeleteItem :: #force_inline proc "system" (hwnd: HWND, i: c_int) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_DELETEITEM, cast(WPARAM)i, 0)
+}
+ListView_DeleteAllItems :: #force_inline proc "system" (hwnd: HWND) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_DELETEALLITEMS, 0, 0)
+}
+ListView_GetCallbackMask :: #force_inline proc "system" (hwnd: HWND) -> UINT {
+ return cast(UINT)SendMessageW(hwnd, LVM_GETCALLBACKMASK, 0, 0)
+}
+ListView_SetCallbackMask :: #force_inline proc "system" (hwnd: HWND, mask: UINT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_SETCALLBACKMASK, cast(WPARAM)mask, 0)
+}
+ListView_GetNextItem :: #force_inline proc "system" (hwnd: HWND, i: c_int, flags: UINT) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_GETNEXTITEM, cast(WPARAM)i, MAKELPARAM(flags,0))
+}
+ListView_FindItem :: #force_inline proc "system" (hwnd: HWND, iStart: c_int, plvfi: ^LV_FINDINFOW) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_FINDITEMW, cast(WPARAM)iStart, cast(LPARAM)uintptr(plvfi))
+}
+ListView_GetItemRect :: #force_inline proc "system" (hwnd: HWND, i: c_int, prc: ^RECT, code: c_int) -> BOOL {
+ if prc != nil {
+ prc.left = code
+ }
+ return cast(BOOL)SendMessageW(hwnd, LVM_GETITEMRECT, cast(WPARAM)i, cast(LPARAM)uintptr(prc))
+}
+ListView_SetItemPosition :: #force_inline proc "system" (hwnd: HWND, i: c_int, x,y: c_int) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_SETITEMPOSITION, cast(WPARAM)i, MAKELPARAM(x,y))
+}
+ListView_GetItemPosition :: #force_inline proc "system" (hwnd: HWND, i: c_int, ppt: ^POINT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_GETITEMPOSITION, cast(WPARAM)i, cast(LPARAM)uintptr(ppt))
+}
+ListView_GetStringWidth :: #force_inline proc "system" (hwndLV: HWND, psz: LPCWSTR) -> c_int {
+ return cast(c_int)SendMessageW(hwndLV, LVM_GETSTRINGWIDTHW, 0, cast(LPARAM)uintptr(psz))
+}
+ListView_HitTest :: #force_inline proc "system" (hwndLV: HWND, pinfo: ^LV_HITTESTINFO) -> c_int {
+ return cast(c_int)SendMessageW(hwndLV, LVM_HITTEST, 0, cast(LPARAM)uintptr(pinfo))
+}
+ListView_EnsureVisible :: #force_inline proc "system" (hwndLV: HWND, i: c_int, fPartialOK: BOOL) -> BOOL {
+ return cast(BOOL)SendMessageW(hwndLV, LVM_ENSUREVISIBLE, cast(WPARAM)i, MAKELPARAM(fPartialOK,0))
+}
+ListView_Scroll :: #force_inline proc "system" (hwndLV: HWND, dx,dy: c_int) -> BOOL {
+ return cast(BOOL)SendMessageW(hwndLV, LVM_SCROLL, cast(WPARAM)dx, cast(LPARAM)dy)
+}
+ListView_RedrawItems :: #force_inline proc "system" (hwndLV: HWND, iFirst,iLast: c_int) -> BOOL {
+ return cast(BOOL)SendMessageW(hwndLV, LVM_REDRAWITEMS, cast(WPARAM)iFirst, cast(LPARAM)iLast)
+}
+ListView_Arrange :: #force_inline proc "system" (hwndLV: HWND, code: UINT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwndLV, LVM_ARRANGE, cast(WPARAM)code, 0)
+}
+ListView_EditLabel :: #force_inline proc "system" (hwndLV: HWND, i: c_int) -> HWND {
+ return cast(HWND)uintptr(SendMessageW(hwndLV, LVM_EDITLABELW, cast(WPARAM)i, 0))
+}
+ListView_GetEditControl :: #force_inline proc "system" (hwndLV: HWND) -> HWND {
+ return cast(HWND)uintptr(SendMessageW(hwndLV, LVM_GETEDITCONTROL, 0, 0))
+}
+ListView_GetColumn :: #force_inline proc "system" (hwnd: HWND, iCol: c_int, pcol: ^LV_COLUMNW) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_GETCOLUMNW, cast(WPARAM)iCol, cast(LPARAM)uintptr(pcol))
+}
+ListView_SetColumn :: #force_inline proc "system" (hwnd: HWND, iCol: c_int, pcol: ^LV_COLUMNW) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_SETCOLUMNW, cast(WPARAM)iCol, cast(LPARAM)uintptr(pcol))
+}
+ListView_InsertColumn :: #force_inline proc "system" (hwnd: HWND, iCol: c_int, pcol: ^LV_COLUMNW) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_INSERTCOLUMNW, cast(WPARAM)iCol, cast(LPARAM)uintptr(pcol))
+}
+ListView_DeleteColumn :: #force_inline proc "system" (hwnd: HWND, iCol: c_int) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_DELETECOLUMN, cast(WPARAM)iCol, 0)
+}
+ListView_GetColumnWidth :: #force_inline proc "system" (hwnd: HWND, iCol: c_int) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_GETCOLUMNWIDTH, cast(WPARAM)iCol, 0)
+}
+ListView_SetColumnWidth :: #force_inline proc "system" (hwnd: HWND, iCol: c_int, cx: c_int) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_SETCOLUMNWIDTH, cast(WPARAM)iCol, MAKELPARAM(cx,0))
+}
+ListView_GetHeader :: #force_inline proc "system" (hwnd: HWND) -> HWND {
+ return cast(HWND)uintptr(SendMessageW(hwnd, LVM_GETHEADER, 0, 0))
+}
+ListView_CreateDragImage :: #force_inline proc "system" (hwnd: HWND, i: c_int, lpptUpLeft: LPPOINT) -> HIMAGELIST {
+ return cast(HIMAGELIST)uintptr(SendMessageW(hwnd, LVM_CREATEDRAGIMAGE, cast(WPARAM)i, cast(LPARAM)uintptr(lpptUpLeft)))
+}
+ListView_GetViewRect :: #force_inline proc "system" (hwnd: HWND, prc: ^RECT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_GETVIEWRECT, 0, cast(LPARAM)uintptr(prc))
+}
+ListView_GetTextColor :: #force_inline proc "system" (hwnd: HWND) -> COLORREF {
+ return cast(COLORREF)SendMessageW(hwnd, LVM_GETTEXTCOLOR, 0, 0)
+}
+ListView_SetTextColor :: #force_inline proc "system" (hwnd: HWND, clrText: COLORREF) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_SETTEXTCOLOR, 0, cast(LPARAM)clrText)
+}
+ListView_GetTextBkColor :: #force_inline proc "system" (hwnd: HWND) -> COLORREF {
+ return cast(COLORREF)SendMessageW(hwnd, LVM_GETTEXTBKCOLOR, 0, 0)
+}
+ListView_SetTextBkColor :: #force_inline proc "system" (hwnd: HWND, clrTextBk: COLORREF) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_SETTEXTBKCOLOR, 0, cast(LPARAM)clrTextBk)
+}
+ListView_GetTopIndex :: #force_inline proc "system" (hwndLV: HWND) -> c_int {
+ return cast(c_int)SendMessageW(hwndLV, LVM_GETTOPINDEX, 0, 0)
+}
+ListView_GetCountPerPage :: #force_inline proc "system" (hwndLV: HWND) -> c_int {
+ return cast(c_int)SendMessageW(hwndLV, LVM_GETCOUNTPERPAGE, 0, 0)
+}
+ListView_GetOrigin :: #force_inline proc "system" (hwndLV: HWND, ppt: ^POINT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwndLV, LVM_GETORIGIN, 0, cast(LPARAM)uintptr(ppt))
+}
+ListView_Update :: #force_inline proc "system" (hwndLV: HWND, i: c_int) -> BOOL {
+ return cast(BOOL)SendMessageW(hwndLV, LVM_UPDATE, cast(WPARAM)i, 0)
+}
+ListView_SetItemState :: #force_inline proc "system" (hwndLV: HWND, i: c_int, data: UINT, mask: UINT) {
+ item := LV_ITEMW {
+ stateMask = mask,
+ state = data,
+ }
+ SendMessageW(hwndLV, LVM_SETITEMSTATE, cast(WPARAM)i, cast(LPARAM)uintptr(&item))
+}
+ListView_SetCheckState :: #force_inline proc "system" (hwndLV: HWND, i: c_int, fCheck: BOOL) {
+ ListView_SetItemState(hwndLV, i, INDEXTOSTATEIMAGEMASK(2 if fCheck else 1), LVIS_STATEIMAGEMASK)
+}
+ListView_GetItemState :: #force_inline proc "system" (hwndLV: HWND, i: c_int, mask: UINT) -> UINT {
+ return cast(UINT)SendMessageW(hwndLV, LVM_GETITEMSTATE, cast(WPARAM)i, cast(LPARAM)mask)
+}
+ListView_GetCheckState :: #force_inline proc "system" (hwndLV: HWND, i: c_int) -> UINT {
+ return ((cast(UINT)SendMessageW(hwndLV, LVM_GETITEMSTATE, cast(WPARAM)i, cast(LPARAM)LVIS_STATEIMAGEMASK)) >> 12) - 1
+}
+ListView_GetItemText :: #force_inline proc "system" (hwndLV: HWND, i: c_int, iSubItem: c_int, pszText: LPWSTR, cchTextMax: c_int) {
+ item := LV_ITEMW {
+ iSubItem = iSubItem,
+ cchTextMax = cchTextMax,
+ pszText = pszText,
+ }
+ SendMessageW(hwndLV, LVM_GETITEMTEXTW, cast(WPARAM)i, cast(LPARAM)uintptr(&item))
+}
+ListView_SetItemText :: #force_inline proc "system" (hwndLV: HWND, i: c_int, iSubItem: c_int, pszText: LPWSTR) {
+ item := LV_ITEMW {
+ iSubItem = iSubItem,
+ pszText = pszText,
+ }
+ SendMessageW(hwndLV, LVM_SETITEMTEXTW, cast(WPARAM)i, cast(LPARAM)uintptr(&item))
+}
+ListView_SetItemCount :: #force_inline proc "system" (hwndLV: HWND, cItems: c_int) {
+ SendMessageW(hwndLV, LVM_SETITEMCOUNT, cast(WPARAM)cItems, 0)
+}
+ListView_SetItemCountEx :: #force_inline proc "system" (hwndLV: HWND, cItems: c_int, dwFlags: DWORD) {
+ SendMessageW(hwndLV, LVM_SETITEMCOUNT, cast(WPARAM)cItems, cast(LPARAM)dwFlags)
+}
+ListView_SortItems :: #force_inline proc "system" (hwndLV: HWND, pfnCompare: PFNLVCOMPARE, lpUser: LPARAM) -> BOOL {
+ return cast(BOOL)SendMessageW(hwndLV, LVM_SORTITEMS, cast(WPARAM)lpUser, cast(LPARAM)transmute(uintptr)(pfnCompare))
+}
+ListView_SetItemPosition32 :: #force_inline proc "system" (hwndLV: HWND, i: c_int, x0,y0: c_int) {
+ ptNewPos := POINT {
+ x = x0,
+ y = y0,
+ }
+ SendMessageW(hwndLV, LVM_SETITEMPOSITION32, cast(WPARAM)i, cast(LPARAM)uintptr(&ptNewPos))
+}
+ListView_GetSelectedCount :: #force_inline proc "system" (hwndLV: HWND) -> UINT {
+ return cast(UINT)SendMessageW(hwndLV, LVM_GETSELECTEDCOUNT, 0, 0)
+}
+ListView_GetItemSpacing :: #force_inline proc "system" (hwndLV: HWND, fSmall: BOOL) -> DWORD {
+ return cast(DWORD)SendMessageW(hwndLV, LVM_GETITEMSPACING, cast(WPARAM)fSmall, 0)
+}
+ListView_GetISearchString :: #force_inline proc "system" (hwndLV: HWND, lpsz: LPWSTR) -> BOOL {
+ return cast(BOOL)SendMessageW(hwndLV, LVM_GETISEARCHSTRINGW, 0, cast(LPARAM)uintptr(lpsz))
+}
+ListView_SetIconSpacing :: #force_inline proc "system" (hwndLV: HWND, cx,cy: c_int) -> DWORD {
+ return cast(DWORD)SendMessageW(hwndLV, LVM_SETICONSPACING, 0, cast(LPARAM)MAKELONG(cx,cy))
+}
+ListView_SetExtendedListViewStyle :: #force_inline proc "system" (hwndLV: HWND, dw: DWORD) -> DWORD {
+ return cast(DWORD)SendMessageW(hwndLV, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, cast(LPARAM)dw)
+}
+ListView_SetExtendedListViewStyleEx :: #force_inline proc "system" (hwndLV: HWND, dwMask: DWORD, dw: DWORD) -> DWORD {
+ return cast(DWORD)SendMessageW(hwndLV, LVM_SETEXTENDEDLISTVIEWSTYLE, cast(WPARAM)dwMask, cast(LPARAM)dw)
+}
+ListView_GetSubItemRect :: #force_inline proc "system" (hwnd: HWND, iItem: c_int, iSubItem: c_int, code: c_int, prc: LPRECT) -> BOOL {
+ if prc != nil {
+ prc.top = iSubItem
+ prc.left = code
+ }
+ return cast(BOOL)SendMessageW(hwnd, LVM_GETSUBITEMRECT, cast(WPARAM)iItem, cast(LPARAM)uintptr(prc))
+}
+ListView_SubItemHitTest :: #force_inline proc "system" (hwnd: HWND, plvhti: LPLVHITTESTINFO) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_SUBITEMHITTEST, 0, cast(LPARAM)uintptr(plvhti))
+}
+ListView_SetColumnOrderArray :: #force_inline proc "system" (hwnd: HWND, iCount: c_int, pi: LPINT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_SETCOLUMNORDERARRAY, cast(WPARAM)iCount, cast(LPARAM)uintptr(pi))
+}
+ListView_GetColumnOrderArray :: #force_inline proc "system" (hwnd: HWND, iCount: c_int, pi: LPINT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_GETCOLUMNORDERARRAY, cast(WPARAM)iCount, cast(LPARAM)uintptr(pi))
+}
+ListView_SetHotItem :: #force_inline proc "system" (hwnd: HWND, i: c_int) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_SETHOTITEM, cast(WPARAM)i, 0)
+}
+ListView_GetHotItem :: #force_inline proc "system" (hwnd: HWND) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_GETHOTITEM, 0, 0)
+}
+ListView_SetHotCursor :: #force_inline proc "system" (hwnd: HWND, hcur: HCURSOR) -> HCURSOR {
+ return cast(HCURSOR)uintptr(SendMessageW(hwnd, LVM_SETHOTCURSOR, 0, cast(LPARAM)uintptr(hcur)))
+}
+ListView_GetHotCursor :: #force_inline proc "system" (hwnd: HWND) -> HCURSOR {
+ return cast(HCURSOR)uintptr(SendMessageW(hwnd, LVM_GETHOTCURSOR, 0, 0))
+}
+ListView_ApproximateViewRect :: #force_inline proc "system" (hwnd: HWND, iWidth,iHeight: c_int, iCount: c_int) -> DWORD {
+ return cast(DWORD)SendMessageW(hwnd, LVM_APPROXIMATEVIEWRECT, cast(WPARAM)iCount, MAKELPARAM(iWidth,iHeight))
+}
+ListView_SetWorkAreas :: #force_inline proc "system" (hwnd: HWND, nWorkAreas: UINT, prc: ^RECT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_SETWORKAREAS, cast(WPARAM)nWorkAreas, cast(LPARAM)uintptr(prc))
+}
+ListView_GetWorkAreas :: #force_inline proc "system" (hwnd: HWND, nWorkAreas: UINT, prc: ^RECT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_GETWORKAREAS, cast(WPARAM)nWorkAreas, cast(LPARAM)uintptr(prc))
+}
+ListView_GetNumberOfWorkAreas :: #force_inline proc "system" (hwnd: HWND, pnWorkAreas: ^UINT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_GETNUMBEROFWORKAREAS, 0, cast(LPARAM)uintptr(pnWorkAreas))
+}
+ListView_GetSelectionMark :: #force_inline proc "system" (hwnd: HWND) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_GETSELECTIONMARK, 0, 0)
+}
+ListView_SetSelectionMark :: #force_inline proc "system" (hwnd: HWND, i: c_int) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_SETSELECTIONMARK, 0, cast(LPARAM)i)
+}
+ListView_SetHoverTime :: #force_inline proc "system" (hwndLV: HWND, dwHoverTimeMs: DWORD) -> DWORD {
+ return cast(DWORD)SendMessageW(hwndLV, LVM_SETHOVERTIME, 0, cast(LPARAM)dwHoverTimeMs)
+}
+ListView_GetHoverTime :: #force_inline proc "system" (hwndLV: HWND) -> DWORD {
+ return cast(DWORD)SendMessageW(hwndLV, LVM_GETHOVERTIME, 0, 0)
+}
+ListView_SetToolTips :: #force_inline proc "system" (hwndLV: HWND, hwndNewHwnd: HWND) -> HWND {
+ return cast(HWND)uintptr(SendMessageW(hwndLV, LVM_SETTOOLTIPS, cast(WPARAM)hwndNewHwnd, 0))
+}
+ListView_GetToolTips :: #force_inline proc "system" (hwndLV: HWND) -> HWND {
+ return cast(HWND)uintptr(SendMessageW(hwndLV, LVM_GETTOOLTIPS, 0, 0))
+}
+ListView_SortItemsEx :: #force_inline proc "system" (hwndLV: HWND, pfnCompare: PFNLVCOMPARE, lpUser: LPARAM) -> BOOL {
+ return cast(BOOL)SendMessageW(hwndLV, LVM_SORTITEMSEX, cast(WPARAM)lpUser, cast(LPARAM)transmute(uintptr)(pfnCompare))
+}
+ListView_SetSelectedColumn :: #force_inline proc "system" (hwnd: HWND, iCol: c_int) {
+ SendMessageW(hwnd, LVM_SETSELECTEDCOLUMN, cast(WPARAM)iCol, 0)
+}
+ListView_SetView :: #force_inline proc "system" (hwnd: HWND, iView: DWORD) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_SETVIEW, cast(WPARAM)iView, 0)
+}
+ListView_GetView :: #force_inline proc "system" (hwnd: HWND) -> DWORD {
+ return cast(DWORD)SendMessageW(hwnd, LVM_GETVIEW, 0, 0)
+}
+ListView_InsertGroup :: #force_inline proc "system" (hwnd: HWND, index: c_int, pgrp: PLVGROUP) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_INSERTGROUP, cast(WPARAM)index, cast(LPARAM)uintptr(pgrp))
+}
+ListView_SetGroupInfo :: #force_inline proc "system" (hwnd: HWND, iGroupId: c_int, pgrp: PLVGROUP) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_SETGROUPINFO, cast(WPARAM)iGroupId, cast(LPARAM)uintptr(pgrp))
+}
+ListView_GetGroupInfo :: #force_inline proc "system" (hwnd: HWND, iGroupId: c_int, pgrp: PLVGROUP) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_GETGROUPINFO, cast(WPARAM)iGroupId, cast(LPARAM)uintptr(pgrp))
+}
+ListView_RemoveGroup :: #force_inline proc "system" (hwnd: HWND, iGroupId: c_int) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_REMOVEGROUP, cast(WPARAM)iGroupId, 0)
+}
+ListView_MoveGroup :: #force_inline proc "system" (hwnd: HWND, iGroupId: c_int, toIndex: c_int) {
+ SendMessageW(hwnd, LVM_MOVEGROUP, cast(WPARAM)iGroupId, cast(LPARAM)toIndex)
+}
+ListView_MoveItemToGroup :: #force_inline proc "system" (hwnd: HWND, idItemFrom: c_int, idGroupTo: c_int) {
+ SendMessageW(hwnd, LVM_MOVEITEMTOGROUP, cast(WPARAM)idItemFrom, cast(LPARAM)idGroupTo)
+}
+ListView_SetGroupMetrics :: #force_inline proc "system" (hwnd: HWND, pGroupMetrics: PLVGROUPMETRICS) {
+ SendMessageW(hwnd, LVM_SETGROUPMETRICS, 0, cast(LPARAM)uintptr(pGroupMetrics))
+}
+ListView_GetGroupMetrics :: #force_inline proc "system" (hwnd: HWND, pGroupMetrics: PLVGROUPMETRICS) {
+ SendMessageW(hwnd, LVM_GETGROUPMETRICS, 0, cast(LPARAM)uintptr(pGroupMetrics))
+}
+ListView_EnableGroupView :: #force_inline proc "system" (hwnd: HWND, fEnable: BOOL) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_ENABLEGROUPVIEW, cast(WPARAM)fEnable, 0)
+}
+ListView_SortGroups :: #force_inline proc "system" (hwnd: HWND, pfnGroupCompare: PFNLVGROUPCOMPARE, pUser: rawptr) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_SORTGROUPS, transmute(uintptr)(pfnGroupCompare), cast(LPARAM)uintptr(pUser))
+}
+ListView_InsertGroupSorted :: #force_inline proc "system" (hwnd: HWND, structInsert: PLVINSERTGROUPSORTED) {
+ SendMessageW(hwnd, LVM_INSERTGROUPSORTED, uintptr(structInsert), 0)
+}
+ListView_RemoveAllGroups :: #force_inline proc "system" (hwnd: HWND) {
+ SendMessageW(hwnd, LVM_REMOVEALLGROUPS, 0, 0)
+}
+ListView_HasGroup :: #force_inline proc "system" (hwnd: HWND, dwGroupId: c_int) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_HASGROUP, cast(WPARAM)dwGroupId, 0)
+}
+ListView_SetTileViewInfo :: #force_inline proc "system" (hwnd: HWND, ptvi: PLVTILEVIEWINFO) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_SETTILEVIEWINFO, 0, cast(LPARAM)uintptr(ptvi))
+}
+ListView_GetTileViewInfo :: #force_inline proc "system" (hwnd: HWND, ptvi: PLVTILEVIEWINFO) {
+ SendMessageW(hwnd, LVM_GETTILEVIEWINFO, 0, cast(LPARAM)uintptr(ptvi))
+}
+ListView_SetTileInfo :: #force_inline proc "system" (hwnd: HWND, pti: PLVTILEINFO) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_SETTILEINFO, 0, cast(LPARAM)uintptr(pti))
+}
+ListView_GetTileInfo :: #force_inline proc "system" (hwnd: HWND, pti: PLVTILEINFO) {
+ SendMessageW(hwnd, LVM_GETTILEINFO, 0, cast(LPARAM)uintptr(pti))
+}
+ListView_SetInsertMark :: #force_inline proc "system" (hwnd: HWND, lvim: LPLVINSERTMARK) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_SETINSERTMARK, 0, cast(LPARAM)uintptr(lvim))
+}
+ListView_GetInsertMark :: #force_inline proc "system" (hwnd: HWND, lvim: LPLVINSERTMARK) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_GETINSERTMARK, 0, cast(LPARAM)uintptr(lvim))
+}
+ListView_InsertMarkHitTest :: #force_inline proc "system" (hwnd: HWND, point: LPPOINT, lvim: LPLVINSERTMARK) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_INSERTMARKHITTEST, uintptr(point), cast(LPARAM)uintptr(lvim))
+}
+ListView_GetInsertMarkRect :: #force_inline proc "system" (hwnd: HWND, rc: LPRECT) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_GETINSERTMARKRECT, 0, cast(LPARAM)uintptr(rc))
+}
+ListView_SetInsertMarkColor :: #force_inline proc "system" (hwnd: HWND, color: COLORREF) -> COLORREF {
+ return cast(COLORREF)SendMessageW(hwnd, LVM_SETINSERTMARKCOLOR, 0, cast(LPARAM)color)
+}
+ListView_GetInsertMarkColor :: #force_inline proc "system" (hwnd: HWND) -> COLORREF {
+ return cast(COLORREF)SendMessageW(hwnd, LVM_GETINSERTMARKCOLOR, 0, 0)
+}
+ListView_SetInfoTip :: #force_inline proc "system" (hwndLV: HWND, plvInfoTip: PLVSETINFOTIP) -> BOOL {
+ return cast(BOOL)SendMessageW(hwndLV, LVM_SETINFOTIP, 0, cast(LPARAM)uintptr(plvInfoTip))
+}
+ListView_GetSelectedColumn :: #force_inline proc "system" (hwnd: HWND) -> UINT {
+ return cast(UINT)SendMessageW(hwnd, LVM_GETSELECTEDCOLUMN, 0, 0)
+}
+ListView_IsGroupViewEnabled :: #force_inline proc "system" (hwnd: HWND) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_ISGROUPVIEWENABLED, 0, 0)
+}
+ListView_GetOutlineColor :: #force_inline proc "system" (hwnd: HWND) -> COLORREF {
+ return cast(COLORREF)SendMessageW(hwnd, LVM_GETOUTLINECOLOR, 0, 0)
+}
+ListView_SetOutlineColor :: #force_inline proc "system" (hwnd: HWND, color: COLORREF) -> COLORREF {
+ return cast(COLORREF)SendMessageW(hwnd, LVM_SETOUTLINECOLOR, 0, cast(LPARAM)color)
+}
+ListView_CancelEditLabel :: #force_inline proc "system" (hwnd: HWND) {
+ SendMessageW(hwnd, LVM_CANCELEDITLABEL, 0, 0)
+}
+ListView_MapIndexToID :: #force_inline proc "system" (hwnd: HWND, index: UINT) -> UINT {
+ return cast(UINT)SendMessageW(hwnd, LVM_MAPINDEXTOID, cast(WPARAM)index, 0)
+}
+ListView_MapIDToIndex :: #force_inline proc "system" (hwnd: HWND, id: UINT) -> UINT {
+ return cast(UINT)SendMessageW(hwnd, LVM_MAPIDTOINDEX, cast(WPARAM)id, 0)
+}
+ListView_IsItemVisible :: #force_inline proc "system" (hwnd: HWND, index: UINT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_ISITEMVISIBLE, cast(WPARAM)index, 0)
+}
+
+// Tree View Control
+HTREEITEM :: distinct rawptr
+
+TVIF_TEXT :: 0x01
+TVIF_IMAGE :: 0x02
+TVIF_PARAM :: 0x04
+TVIF_STATE :: 0x08
+TVIF_HANDLE :: 0x10
+TVIF_SELECTEDIMAGE :: 0x20
+TVIF_CHILDREN :: 0x40
+TVIF_INTEGRAL :: 0x80
+
+TVIS_SELECTED :: 0x02
+TVIS_CUT :: 0x04
+TVIS_DROPHILITED :: 0x08
+TVIS_BOLD :: 0x10
+TVIS_EXPANDED :: 0x20
+TVIS_EXPANDEDONCE :: 0x40
+TVIS_EXPANDPARTIAL :: 0x80
+
+TVIS_OVERLAYMASK :: 0x0F00
+TVIS_STATEIMAGEMASK :: 0xF000
+TVIS_USERMASK :: 0xF000
+
+I_CHILDRENCALLBACK :: (-1)
+
+TVI_ROOT :: cast(HTREEITEM)~uintptr(0x10000 - 1)
+TVI_FIRST :: cast(HTREEITEM)~uintptr(0x0FFFF - 1)
+TVI_LAST :: cast(HTREEITEM)~uintptr(0x0FFFE - 1)
+TVI_SORT :: cast(HTREEITEM)~uintptr(0x0FFFD - 1)
+
+TVN_SELCHANGINGA :: (TVN_FIRST-1)
+TVN_SELCHANGINGW :: (TVN_FIRST-50)
+TVN_SELCHANGEDA :: (TVN_FIRST-2)
+TVN_SELCHANGEDW :: (TVN_FIRST-51)
+TVN_GETDISPINFOA :: (TVN_FIRST-3)
+TVN_GETDISPINFOW :: (TVN_FIRST-52)
+TVN_SETDISPINFOA :: (TVN_FIRST-4)
+TVN_SETDISPINFOW :: (TVN_FIRST-53)
+TVN_ITEMEXPANDINGA :: (TVN_FIRST-5)
+TVN_ITEMEXPANDINGW :: (TVN_FIRST-54)
+TVN_ITEMEXPANDEDA :: (TVN_FIRST-6)
+TVN_ITEMEXPANDEDW :: (TVN_FIRST-55)
+TVN_BEGINDRAGA :: (TVN_FIRST-7)
+TVN_BEGINDRAGW :: (TVN_FIRST-56)
+TVN_BEGINRDRAGA :: (TVN_FIRST-8)
+TVN_BEGINRDRAGW :: (TVN_FIRST-57)
+TVN_DELETEITEMA :: (TVN_FIRST-9)
+TVN_DELETEITEMW :: (TVN_FIRST-58)
+TVN_BEGINLABELEDITA :: (TVN_FIRST-10)
+TVN_BEGINLABELEDITW :: (TVN_FIRST-59)
+TVN_ENDLABELEDITA :: (TVN_FIRST-11)
+TVN_ENDLABELEDITW :: (TVN_FIRST-60)
+TVN_KEYDOWN :: (TVN_FIRST-12)
+TVN_GETINFOTIPA :: (TVN_FIRST-13)
+TVN_GETINFOTIPW :: (TVN_FIRST-14)
+TVN_SINGLEEXPAND :: (TVN_FIRST-15)
+
+TVC_UNKNOWN :: 0x0
+TVC_BYMOUSE :: 0x1
+TVC_BYKEYBOARD :: 0x2
+
+TVIF_DI_SETITEM :: 0x1000
+
+TVNRET_DEFAULT :: 0
+TVNRET_SKIPOLD :: 1
+TVNRET_SKIPNEW :: 2
+
+TVCDRF_NOIMAGES :: 0x10000
+
+TVITEMW :: struct {
+ mask: UINT,
+ hItem: HTREEITEM,
+ state: UINT,
+ stateMask: UINT,
+ pszText: LPWSTR,
+ cchTextMax: c_int,
+ iImage: c_int,
+ iSelectedImage: c_int,
+ cChildren: c_int,
+ lParam: LPARAM,
+}
+TV_ITEMW :: TVITEMW
+LPTVITEMW :: ^TVITEMW
+LPTV_ITEMW :: LPTVITEMW
+
+TVITEMEXW :: struct {
+ mask: UINT,
+ hItem: HTREEITEM,
+ state: UINT,
+ stateMask: UINT,
+ pszText: LPWSTR,
+ cchTextMax: c_int,
+ iImage: c_int,
+ iSelectedImage: c_int,
+ cChildren: c_int,
+ lParam: LPARAM,
+ iIntegral: c_int,
+}
+TV_ITEMEXW :: TVITEMEXW
+LPTVITEMEXW :: ^TVITEMEXW
+LPTV_ITEMEXW :: LPTVITEMEXW
+
+TVINSERTSTRUCTW :: struct {
+ hParent: HTREEITEM,
+ hInsertAfter: HTREEITEM,
+ _: struct #raw_union {
+ itemex: TVITEMEXW,
+ item: TV_ITEMW,
+ },
+}
+TV_INSERTSTRUCTW :: TVINSERTSTRUCTW
+LPTVINSERTSTRUCTW :: ^TVINSERTSTRUCTW
+LPTV_INSERTSTRUCTW :: LPTVINSERTSTRUCTW
+
+TVHITTESTINFO :: struct {
+ pt: POINT,
+ flags: UINT,
+ hItem: HTREEITEM,
+}
+TV_HITTESTINFO :: TVHITTESTINFO
+LPTVHITTESTINFO :: ^TVHITTESTINFO
+LPTV_HITTESTINFO :: LPTVHITTESTINFO
+
+TVSORTCB :: struct {
+ hParent: HTREEITEM,
+ lpfnCompare: PFNTVCOMPARE,
+ lParam: LPARAM,
+}
+TV_SORTCB :: TVSORTCB
+LPTVSORTCB :: ^TVSORTCB
+LPTV_SORTCB :: LPTVSORTCB
+
+NMTREEVIEWW :: struct {
+ hdr: NMHDR,
+ action: UINT,
+ itemOld: TVITEMW,
+ itemNew: TVITEMW,
+ ptDrag: POINT,
+}
+NM_TREEVIEWW :: NMTREEVIEWW
+LPNMTREEVIEWW :: ^NMTREEVIEWW
+LPNM_TREEVIEWW :: LPNMTREEVIEWW
+
+NMTVDISPINFOW :: struct {
+ hdr: NMHDR,
+ item: TVITEMW,
+}
+TV_DISPINFOW :: NMTVDISPINFOW
+LPNMTVDISPINFOW :: ^NMTVDISPINFOW
+
+NMTVDISPINFOEXW :: struct {
+ hdr: NMHDR,
+ item: TVITEMEXW,
+}
+TV_DISPINFOEXW :: NMTVDISPINFOEXW
+LPNMTVDISPINFOEXW :: ^NMTVDISPINFOEXW
+
+NMTVKEYDOWN :: struct #packed {
+ hdr: NMHDR,
+ wVKey: WORD,
+ flags: UINT,
+}
+TV_KEYDOWN :: NMTVKEYDOWN
+LPNMTVKEYDOWN :: ^NMTVKEYDOWN
+
+NMTVCUSTOMDRAW :: struct {
+ nmcd: NMCUSTOMDRAW,
+ clrText: COLORREF,
+ clrTextBk: COLORREF,
+ iLevel: c_int,
+}
+LPNMTVCUSTOMDRAW :: ^NMTVCUSTOMDRAW
+
+NMTVGETINFOTIPW :: struct {
+ hdr: NMHDR,
+ pszText: LPWSTR,
+ cchTextMax: c_int,
+ hItem: HTREEITEM,
+ lParam: LPARAM,
+}
+TV_GETINFOTIPW :: NMTVGETINFOTIPW
+LPNMTVGETINFOTIPW :: ^NMTVGETINFOTIPW
+
+PFNTVCOMPARE :: #type proc "system" (lParam1,lParam2: LPARAM, lParamSort: LPARAM) -> c_int
+
+TreeView_InsertItem :: #force_inline proc "system" (hwnd: HWND, lpis: LPTV_INSERTSTRUCTW) -> HTREEITEM {
+ return cast(HTREEITEM)uintptr(SendMessageW(hwnd, TVM_INSERTITEMW, 0, cast(LPARAM)uintptr(lpis)))
+}
+TreeView_DeleteItem :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TVM_DELETEITEM, 0, cast(LPARAM)uintptr(hitem))
+}
+TreeView_DeleteAllItems :: #force_inline proc "system" (hwnd: HWND) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TVM_DELETEITEM, 0, cast(LPARAM)transmute(uintptr)(TVI_ROOT))
+}
+TreeView_Expand :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM, code: UINT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TVM_EXPAND, cast(WPARAM)code, cast(LPARAM)uintptr(hitem))
+}
+TreeView_GetItemRect :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM, prc: ^RECT, code: UINT) -> BOOL {
+ alias: struct #raw_union {
+ rc: ^RECT,
+ hitem: ^HTREEITEM,
+ }
+
+ alias.rc = prc
+ alias.hitem^ = hitem
+
+ return cast(BOOL)SendMessageW(hwnd, TVM_GETITEMRECT, cast(WPARAM)code, cast(LPARAM)uintptr(prc))
+}
+TreeView_GetCount :: #force_inline proc "system" (hwnd: HWND) -> UINT {
+ return cast(UINT)SendMessageW(hwnd, TVM_GETCOUNT, 0, 0)
+}
+TreeView_GetIndent :: #force_inline proc "system" (hwnd: HWND) -> UINT {
+ return cast(UINT)SendMessageW(hwnd, TVM_GETINDENT, 0, 0)
+}
+TreeView_SetIndent :: #force_inline proc "system" (hwnd: HWND, indent: UINT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TVM_SETINDENT, cast(WPARAM)indent, 0)
+}
+TreeView_GetImageList :: #force_inline proc "system" (hwnd: HWND, iImage: INT) -> HIMAGELIST {
+ return cast(HIMAGELIST)uintptr(SendMessageW(hwnd, TVM_GETIMAGELIST, cast(WPARAM)iImage, 0))
+}
+TreeView_SetImageList :: #force_inline proc "system" (hwnd: HWND, himl: HIMAGELIST, iImage: INT) -> HIMAGELIST {
+ return cast(HIMAGELIST)uintptr(SendMessageW(hwnd, TVM_SETIMAGELIST, cast(WPARAM)iImage, cast(LPARAM)uintptr(himl)))
+}
+TreeView_GetNextItem :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM, code: UINT) -> HTREEITEM {
+ return cast(HTREEITEM)uintptr(SendMessageW(hwnd, TVM_GETNEXTITEM, cast(WPARAM)code, cast(LPARAM)uintptr(hitem)))
+}
+TreeView_GetChild :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM) -> HTREEITEM {
+ return TreeView_GetNextItem(hwnd, hitem, TVGN_CHILD)
+}
+TreeView_GetNextSibling :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM) -> HTREEITEM {
+ return TreeView_GetNextItem(hwnd, hitem, TVGN_NEXT)
+}
+TreeView_GetPrevSibling :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM) -> HTREEITEM {
+ return TreeView_GetNextItem(hwnd, hitem, TVGN_PREVIOUS)
+}
+TreeView_GetParent :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM) -> HTREEITEM {
+ return TreeView_GetNextItem(hwnd, hitem, TVGN_PARENT)
+}
+TreeView_GetFirstVisible :: #force_inline proc "system" (hwnd: HWND) -> HTREEITEM {
+ return TreeView_GetNextItem(hwnd, nil, TVGN_FIRSTVISIBLE)
+}
+TreeView_GetNextVisible :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM) -> HTREEITEM {
+ return TreeView_GetNextItem(hwnd, hitem, TVGN_NEXTVISIBLE)
+}
+TreeView_GetPrevVisible :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM) -> HTREEITEM {
+ return TreeView_GetNextItem(hwnd, hitem, TVGN_PREVIOUSVISIBLE)
+}
+TreeView_GetSelection :: #force_inline proc "system" (hwnd: HWND) -> HTREEITEM {
+ return TreeView_GetNextItem(hwnd, nil, TVGN_CARET)
+}
+TreeView_GetDropHilight :: #force_inline proc "system" (hwnd: HWND) -> HTREEITEM {
+ return TreeView_GetNextItem(hwnd, nil, TVGN_DROPHILITE)
+}
+TreeView_GetRoot :: #force_inline proc "system" (hwnd: HWND) -> HTREEITEM {
+ return TreeView_GetNextItem(hwnd, nil, TVGN_ROOT)
+}
+TreeView_GetLastVisible :: #force_inline proc "system" (hwnd: HWND) -> HTREEITEM {
+ return TreeView_GetNextItem(hwnd, nil, TVGN_LASTVISIBLE)
+}
+TreeView_Select :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM, code: UINT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TVM_SELECTITEM, cast(WPARAM)code, cast(LPARAM)uintptr(hitem))
+}
+TreeView_SelectItem :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM) -> BOOL {
+ return TreeView_Select(hwnd, hitem, TVGN_CARET)
+}
+TreeView_SelectDropTarget :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM) -> BOOL {
+ return TreeView_Select(hwnd, hitem, TVGN_DROPHILITE)
+}
+TreeView_SelectSetFirstVisible :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM) -> BOOL {
+ return TreeView_Select(hwnd, hitem, TVGN_FIRSTVISIBLE)
+}
+TreeView_GetItem :: #force_inline proc "system" (hwnd: HWND, pitem: ^TV_ITEMW) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TVM_GETITEMW, 0, cast(LPARAM)uintptr(pitem))
+}
+TreeView_SetItem :: #force_inline proc "system" (hwnd: HWND, pitem: ^TV_ITEMW) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TVM_SETITEMW, 0, cast(LPARAM)uintptr(pitem))
+}
+TreeView_EditLabel :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM) -> HWND {
+ return cast(HWND)uintptr(SendMessageW(hwnd, TVM_EDITLABELW, 0, cast(LPARAM)uintptr(hitem)))
+}
+TreeView_GetEditControl :: #force_inline proc "system" (hwnd: HWND) -> HWND {
+ return cast(HWND)uintptr(SendMessageW(hwnd, TVM_GETEDITCONTROL, 0, 0))
+}
+TreeView_GetVisibleCount :: #force_inline proc "system" (hwnd: HWND) -> UINT {
+ return cast(UINT)SendMessageW(hwnd, TVM_GETVISIBLECOUNT, 0, 0)
+}
+TreeView_HitTest :: #force_inline proc "system" (hwnd: HWND, lpht: LPTV_HITTESTINFO) -> HTREEITEM {
+ return cast(HTREEITEM)uintptr(SendMessageW(hwnd, TVM_HITTEST, 0, cast(LPARAM)uintptr(lpht)))
+}
+TreeView_CreateDragImage :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM) -> HIMAGELIST {
+ return cast(HIMAGELIST)uintptr(SendMessageW(hwnd, TVM_CREATEDRAGIMAGE, 0, cast(LPARAM)uintptr(hitem)))
+}
+TreeView_SortChildren :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM, recurse: BOOL) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TVM_SORTCHILDREN, cast(WPARAM)recurse, cast(LPARAM)uintptr(hitem))
+}
+TreeView_EnsureVisible :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TVM_ENSUREVISIBLE, 0, cast(LPARAM)uintptr(hitem))
+}
+TreeView_SortChildrenCB :: #force_inline proc "system" (hwnd: HWND, psort: LPTVSORTCB, recurse: BOOL) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TVM_SORTCHILDRENCB, cast(WPARAM)recurse, cast(LPARAM)uintptr(psort))
+}
+TreeView_EndEditLabelNow :: #force_inline proc "system" (hwnd: HWND, fCancel: BOOL) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TVM_ENDEDITLABELNOW, cast(WPARAM)fCancel, 0)
+}
+TreeView_SetToolTips :: #force_inline proc "system" (hwnd: HWND, hwndTT: HWND) -> HWND {
+ return cast(HWND)uintptr(SendMessageW(hwnd, TVM_SETTOOLTIPS, uintptr(hwndTT), 0))
+}
+TreeView_GetToolTips :: #force_inline proc "system" (hwnd: HWND) -> HWND {
+ return cast(HWND)uintptr(SendMessageW(hwnd, TVM_GETTOOLTIPS, 0, 0))
+}
+TreeView_GetISearchString :: #force_inline proc "system" (hwnd: HWND, lpsz: LPWSTR) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TVM_GETISEARCHSTRINGW, 0, cast(LPARAM)uintptr(lpsz))
+}
+TreeView_SetInsertMark :: #force_inline proc "system" (hwnd: HWND, hItem: HTREEITEM, fAfter: BOOL) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TVM_SETINSERTMARK, cast(WPARAM)fAfter, cast(LPARAM)uintptr(hItem))
+}
+TreeView_SetUnicodeFormat :: #force_inline proc "system" (hwnd: HWND, fUnicode: BOOL) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TVM_SETUNICODEFORMAT, cast(WPARAM)fUnicode, 0)
+}
+TreeView_GetUnicodeFormat :: #force_inline proc "system" (hwnd: HWND) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TVM_GETUNICODEFORMAT, 0, 0)
+}
+TreeView_SetItemHeight :: #force_inline proc "system" (hwnd: HWND, iHeight: c_int) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, TVM_SETITEMHEIGHT, cast(WPARAM)iHeight, 0)
+}
+TreeView_GetItemHeight :: #force_inline proc "system" (hwnd: HWND) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, TVM_GETITEMHEIGHT, 0, 0)
+}
+TreeView_SetBkColor :: #force_inline proc "system" (hwnd: HWND, clr: COLORREF) -> COLORREF {
+ return cast(COLORREF)SendMessageW(hwnd, TVM_SETBKCOLOR, 0, cast(LPARAM)clr)
+}
+TreeView_SetTextColor :: #force_inline proc "system" (hwnd: HWND, clr: COLORREF) -> COLORREF {
+ return cast(COLORREF)SendMessageW(hwnd, TVM_SETTEXTCOLOR, 0, cast(LPARAM)clr)
+}
+TreeView_GetBkColor :: #force_inline proc "system" (hwnd: HWND) -> COLORREF {
+ return cast(COLORREF)SendMessageW(hwnd, TVM_GETBKCOLOR, 0, 0)
+}
+TreeView_GetTextColor :: #force_inline proc "system" (hwnd: HWND) -> COLORREF {
+ return cast(COLORREF)SendMessageW(hwnd, TVM_GETTEXTCOLOR, 0, 0)
+}
+TreeView_SetScrollTime :: #force_inline proc "system" (hwnd: HWND, uTime: UINT) -> UINT {
+ return cast(UINT)SendMessageW(hwnd, TVM_SETSCROLLTIME, cast(WPARAM)uTime, 0)
+}
+TreeView_GetScrollTime :: #force_inline proc "system" (hwnd: HWND) -> UINT {
+ return cast(UINT)SendMessageW(hwnd, TVM_GETSCROLLTIME, 0, 0)
+}
+TreeView_SetInsertMarkColor :: #force_inline proc "system" (hwnd: HWND, clr: COLORREF) -> COLORREF {
+ return cast(COLORREF)SendMessageW(hwnd, TVM_SETINSERTMARKCOLOR, 0, cast(LPARAM)clr)
+}
+TreeView_GetInsertMarkColor :: #force_inline proc "system" (hwnd: HWND) -> COLORREF {
+ return cast(COLORREF)SendMessageW(hwnd, TVM_GETINSERTMARKCOLOR, 0, 0)
+}
+TreeView_SetItemState :: #force_inline proc "system" (hwndTV: HWND, hti: HTREEITEM, data: UINT, mask: UINT) {
+ item := TVITEMW {
+ mask = TVIF_STATE,
+ hItem = hti,
+ stateMask = mask,
+ state = data,
+ }
+ SendMessageW(hwndTV, TVM_SETITEMW, 0, cast(LPARAM)uintptr(&item))
+}
+TreeView_SetCheckState :: #force_inline proc "system" (hwndTV: HWND, hti: HTREEITEM, fCheck: BOOL) {
+ TreeView_SetItemState(hwndTV, hti, INDEXTOSTATEIMAGEMASK(2 if fCheck else 1), TVIS_STATEIMAGEMASK)
+}
+TreeView_GetItemState :: #force_inline proc "system" (hwndTV: HWND, hti: HTREEITEM, mask: UINT) -> UINT {
+ return cast(UINT)SendMessageW(hwndTV, TVM_GETITEMSTATE, uintptr(hti), cast(LPARAM)mask)
+}
+TreeView_GetCheckState :: #force_inline proc "system" (hwndTV: HWND, hti: HTREEITEM) -> UINT {
+ return ((cast(UINT)SendMessageW(hwndTV, TVM_GETITEMSTATE, uintptr(hti), cast(LPARAM)TVIS_STATEIMAGEMASK)) >> 12) - 1
+}
+TreeView_SetLineColor :: #force_inline proc "system" (hwnd: HWND, clr: COLORREF) -> COLORREF {
+ return cast(COLORREF)SendMessageW(hwnd, TVM_SETLINECOLOR, 0, cast(LPARAM)clr)
+}
+TreeView_GetLineColor :: #force_inline proc "system" (hwnd: HWND) -> COLORREF {
+ return cast(COLORREF)SendMessageW(hwnd, TVM_GETLINECOLOR, 0, 0)
+}
+TreeView_MapAccIDToHTREEITEM :: #force_inline proc "system" (hwnd: HWND, id: UINT) -> HTREEITEM {
+ return cast(HTREEITEM)uintptr(SendMessageW(hwnd, TVM_MAPACCIDTOHTREEITEM, cast(WPARAM)id, 0))
+}
+TreeView_MapHTREEITEMToAccID :: #force_inline proc "system" (hwnd: HWND, htreeitem: HTREEITEM) -> UINT {
+ return cast(UINT)SendMessageW(hwnd, TVM_MAPHTREEITEMTOACCID, uintptr(htreeitem), 0)
+}
+
+// Combo Box Ex Control
+CBEIF_TEXT :: 0x01
+CBEIF_IMAGE :: 0x02
+CBEIF_SELECTEDIMAGE :: 0x04
+CBEIF_OVERLAY :: 0x08
+CBEIF_INDENT :: 0x10
+CBEIF_LPARAM :: 0x20
+
+CBEIF_DI_SETITEM :: 0x10000000
+
+CBES_EX_NOEDITIMAGE :: 0x01
+CBES_EX_NOEDITIMAGEINDENT :: 0x02
+CBES_EX_PATHWORDBREAKPROC :: 0x04
+CBES_EX_NOSIZELIMIT :: 0x08
+CBES_EX_CASESENSITIVE :: 0x10
+
+CBEN_GETDISPINFOA :: (CBEN_FIRST - 0)
+CBEN_INSERTITEM :: (CBEN_FIRST - 1)
+CBEN_DELETEITEM :: (CBEN_FIRST - 2)
+CBEN_BEGINEDIT :: (CBEN_FIRST - 4)
+CBEN_ENDEDITA :: (CBEN_FIRST - 5)
+CBEN_ENDEDITW :: (CBEN_FIRST - 6)
+CBEN_GETDISPINFOW :: (CBEN_FIRST - 7)
+CBEN_DRAGBEGINA :: (CBEN_FIRST - 8)
+CBEN_DRAGBEGINW :: (CBEN_FIRST - 9)
+
+CBENF_KILLFOCUS :: 1
+CBENF_RETURN :: 2
+CBENF_ESCAPE :: 3
+CBENF_DROPDOWN :: 4
+
+CBEMAXSTRLEN :: 260
+
+COMBOBOXEXITEMW :: struct {
+ mask: UINT,
+ iItem: INT_PTR,
+ pszText: LPWSTR,
+ cchTextMax: c_int,
+ iImage: c_int,
+ iSelectedImage: c_int,
+ iOverlay: c_int,
+ iIndent: c_int,
+ lParam: LPARAM,
+}
+PCOMBOBOXEXITEMW :: ^COMBOBOXEXITEMW
+PCCOMBOBOXEXITEMW :: ^COMBOBOXEXITEMW
+
+NMCOMBOBOXEXW :: struct {
+ hdr: NMHDR,
+ ceItem: COMBOBOXEXITEMW,
+}
+PNMCOMBOBOXEXW :: ^NMCOMBOBOXEXW
+
+NMCBEDRAGBEGINW :: struct {
+ hdr: NMHDR,
+ iItemId: c_int,
+ szText: [CBEMAXSTRLEN]WCHAR,
+}
+PNMCBEDRAGBEGINW :: ^NMCBEDRAGBEGINW
+LPNMCBEDRAGBEGINW :: PNMCBEDRAGBEGINW
+
+NMCBEENDEDITW :: struct {
+ hdr: NMHDR,
+ fChanged: BOOL,
+ iNewSelection: c_int,
+ szText: [CBEMAXSTRLEN]WCHAR,
+ iWhy: c_int,
+}
+PNMCBEENDEDITW :: ^NMCBEENDEDITW
+LPNMCBEENDEDITW :: PNMCBEENDEDITW
+
+// Tab Control
+TCS_EX_FLATSEPARATORS :: 0x1
+TCS_EX_REGISTERDROP :: 0x2
+
+TCN_KEYDOWN :: TCN_FIRST - 0
+TCN_SELCHANGE :: TCN_FIRST - 1
+TCN_SELCHANGING :: TCN_FIRST - 2
+TCN_GETOBJECT :: TCN_FIRST - 3
+TCN_FOCUSCHANGE :: TCN_FIRST - 4
+
+TCITEMHEADERW :: struct {
+ mask: UINT,
+ lpReserved1: UINT,
+ lpReserved2: UINT,
+ pszText: LPWSTR,
+ cchTextMax: c_int,
+ iImage: c_int,
+}
+TC_ITEMHEADERW :: TCITEMHEADERW
+LPTCITEMHEADERW :: ^TCITEMHEADERW
+LPTC_ITEMHEADERW :: LPTCITEMHEADERW
+
+TCITEMW :: struct {
+ mask: UINT,
+ dwState: DWORD,
+ dwStateMask: DWORD,
+ pszText: LPWSTR,
+ cchTextMax: c_int,
+ iImage: c_int,
+ lParam: LPARAM,
+}
+TC_ITEMW :: TCITEMW
+LPTCITEMW :: ^TCITEMW
+LPTC_ITEMW :: LPTCITEMW
+
+TCHITTESTINFO :: struct {
+ pt: POINT,
+ flags: UINT,
+}
+TC_HITTESTINFO :: TCHITTESTINFO
+LPTCHITTESTINFO :: ^TCHITTESTINFO
+LPTC_HITTESTINFO :: LPTCHITTESTINFO
+
+NMTCKEYDOWN :: struct #packed {
+ hdr: NMHDR,
+ wVKey: WORD,
+ flags: UINT,
+}
+TC_KEYDOWN :: NMTCKEYDOWN
+
+TabCtrl_GetImageList :: #force_inline proc "system" (hwnd: HWND) -> HIMAGELIST {
+ return cast(HIMAGELIST)uintptr(SendMessageW(hwnd, TCM_GETIMAGELIST, 0, 0))
+}
+TabCtrl_SetImageList :: #force_inline proc "system" (hwnd: HWND, himl: HIMAGELIST) -> HIMAGELIST {
+ return cast(HIMAGELIST)uintptr(SendMessageW(hwnd, TCM_SETIMAGELIST, 0, cast(LPARAM)uintptr(himl)))
+}
+TabCtrl_GetItemCount :: #force_inline proc "system" (hwnd: HWND) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, TCM_GETITEMCOUNT, 0, 0)
+}
+TabCtrl_GetItem :: #force_inline proc "system" (hwnd: HWND, iItem: c_int, pitem: ^TC_ITEMW) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TCM_GETITEMW, cast(WPARAM)iItem, cast(LPARAM)uintptr(pitem))
+}
+TabCtrl_SetItem :: #force_inline proc "system" (hwnd: HWND, iItem: c_int, pitem: ^TC_ITEMW) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TCM_SETITEMW, cast(WPARAM)iItem, cast(LPARAM)uintptr(pitem))
+}
+TabCtrl_InsertItem :: #force_inline proc "system" (hwnd: HWND, iItem: c_int, pitem: ^TC_ITEMW) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, TCM_INSERTITEMW, cast(WPARAM)iItem, cast(LPARAM)uintptr(pitem))
+}
+TabCtrl_DeleteItem :: #force_inline proc "system" (hwnd: HWND, i: c_int) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TCM_DELETEITEM, cast(WPARAM)i, 0)
+}
+TabCtrl_DeleteAllItems :: #force_inline proc "system" (hwnd: HWND) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TCM_DELETEALLITEMS, 0, 0)
+}
+TabCtrl_GetItemRect :: #force_inline proc "system" (hwnd: HWND, i: c_int, prc: ^RECT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TCM_GETITEMRECT, cast(WPARAM)i, cast(LPARAM)uintptr(prc))
+}
+TabCtrl_GetCurSel :: #force_inline proc "system" (hwnd: HWND) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, TCM_GETCURSEL, 0, 0)
+}
+TabCtrl_SetCurSel :: #force_inline proc "system" (hwnd: HWND, i: c_int) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, TCM_SETCURSEL, cast(WPARAM)i, 0)
+}
+TabCtrl_HitTest :: #force_inline proc "system" (hwndTC: HWND, pinfo: ^TC_HITTESTINFO) -> c_int {
+ return cast(c_int)SendMessageW(hwndTC, TCM_HITTEST, 0, cast(LPARAM)uintptr(pinfo))
+}
+TabCtrl_SetItemExtra :: #force_inline proc "system" (hwndTC: HWND, cb: c_int) -> BOOL {
+ return cast(BOOL)SendMessageW(hwndTC, TCM_SETITEMEXTRA, cast(WPARAM)cb, 0)
+}
+TabCtrl_AdjustRect :: #force_inline proc "system" (hwnd: HWND, bLarger: BOOL, prc: ^RECT) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, TCM_ADJUSTRECT, cast(WPARAM)bLarger, cast(LPARAM)uintptr(prc))
+}
+TabCtrl_SetItemSize :: #force_inline proc "system" (hwnd: HWND, x,y: c_int) -> DWORD {
+ return cast(DWORD)SendMessageW(hwnd, TCM_SETITEMSIZE, 0, MAKELPARAM(x,y))
+}
+TabCtrl_RemoveImage :: #force_inline proc "system" (hwnd: HWND, i: c_int) {
+ SendMessageW(hwnd, TCM_REMOVEIMAGE, cast(WPARAM)i, 0)
+}
+TabCtrl_SetPadding :: #force_inline proc "system" (hwnd: HWND, cx,cy: c_int) {
+ SendMessageW(hwnd, TCM_SETPADDING, 0, MAKELPARAM(cx,cy))
+}
+TabCtrl_GetRowCount :: #force_inline proc "system" (hwnd: HWND) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, TCM_GETROWCOUNT, 0, 0)
+}
+TabCtrl_GetToolTips :: #force_inline proc "system" (hwnd: HWND) -> HWND {
+ return cast(HWND)uintptr(SendMessageW(hwnd, TCM_GETTOOLTIPS, 0, 0))
+}
+TabCtrl_SetToolTips :: #force_inline proc "system" (hwnd: HWND, hwndTT: HWND) {
+ SendMessageW(hwnd, TCM_SETTOOLTIPS, uintptr(hwndTT), 0)
+}
+TabCtrl_GetCurFocus :: #force_inline proc "system" (hwnd: HWND) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, TCM_GETCURFOCUS, 0, 0)
+}
+TabCtrl_SetCurFocus :: #force_inline proc "system" (hwnd: HWND, i: c_int) {
+ SendMessageW(hwnd, TCM_SETCURFOCUS, cast(WPARAM)i, 0)
+}
+TabCtrl_SetMinTabWidth :: #force_inline proc "system" (hwnd: HWND, x: c_int) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, TCM_SETMINTABWIDTH, 0, cast(LPARAM)x)
+}
+TabCtrl_DeselectAll :: #force_inline proc "system" (hwnd: HWND, fExcludeFocus: BOOL) {
+ SendMessageW(hwnd, TCM_DESELECTALL, cast(WPARAM)fExcludeFocus, 0)
+}
+TabCtrl_HighlightItem :: #force_inline proc "system" (hwnd: HWND, i: c_int, fHighlight: BOOL) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TCM_HIGHLIGHTITEM, cast(WPARAM)i, cast(LPARAM)MAKELONG(fHighlight,0))
+}
+TabCtrl_SetExtendedStyle :: #force_inline proc "system" (hwnd: HWND, dw: DWORD) -> DWORD {
+ return cast(DWORD)SendMessageW(hwnd, TCM_SETEXTENDEDSTYLE, 0, cast(LPARAM)dw)
+}
+TabCtrl_GetExtendedStyle :: #force_inline proc "system" (hwnd: HWND) -> DWORD {
+ return cast(DWORD)SendMessageW(hwnd, TCM_GETEXTENDEDSTYLE, 0, 0)
+}
+TabCtrl_SetUnicodeFormat :: #force_inline proc "system" (hwnd: HWND, fUnicode: BOOL) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TCM_SETUNICODEFORMAT, cast(WPARAM)fUnicode, 0)
+}
+TabCtrl_GetUnicodeFormat :: #force_inline proc "system" (hwnd: HWND) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TCM_GETUNICODEFORMAT, 0, 0)
}
diff --git a/core/sys/windows/dbghelp.odin b/core/sys/windows/dbghelp.odin
index 336992b4a..e32b4c874 100644
--- a/core/sys/windows/dbghelp.odin
+++ b/core/sys/windows/dbghelp.odin
@@ -15,7 +15,7 @@ MINIDUMP_DIRECTORY :: struct {
Location: MINIDUMP_LOCATION_DESCRIPTOR,
}
-MINIDUMP_EXCEPTION_INFORMATION :: struct {
+MINIDUMP_EXCEPTION_INFORMATION :: struct #max_field_align(4) {
ThreadId: DWORD,
ExceptionPointers: ^EXCEPTION_POINTERS,
ClientPointers: BOOL,
diff --git a/core/sys/windows/dnsapi.odin b/core/sys/windows/dnsapi.odin
index 4fd9f7a19..728813696 100644
--- a/core/sys/windows/dnsapi.odin
+++ b/core/sys/windows/dnsapi.odin
@@ -5,6 +5,6 @@ foreign import "system:Dnsapi.lib"
@(default_calling_convention="system")
foreign Dnsapi {
- DnsQuery_UTF8 :: proc(name: cstring, type: u16, options: DWORD, extra: PVOID, results: ^^DNS_RECORD, reserved: PVOID) -> DNS_STATUS ---
+ DnsQuery_UTF8 :: proc(name: cstring, type: u16, options: DNS_QUERY_OPTIONS, extra: PVOID, results: ^^DNS_RECORD, reserved: PVOID) -> DNS_STATUS ---
DnsRecordListFree :: proc(list: ^DNS_RECORD, options: DWORD) ---
}
diff --git a/core/sys/windows/dwmapi.odin b/core/sys/windows/dwmapi.odin
index 11a46f53a..e86730660 100644
--- a/core/sys/windows/dwmapi.odin
+++ b/core/sys/windows/dwmapi.odin
@@ -38,6 +38,13 @@ DWMNCRENDERINGPOLICY :: enum {
DWMNCRP_LAST,
}
+DWM_WINDOW_CORNER_PREFERENCE :: enum c_int {
+ DEFAULT,
+ DONOTROUND,
+ ROUND,
+ ROUNDSMALL,
+}
+
@(default_calling_convention="system")
foreign dwmapi {
DwmFlush :: proc() -> HRESULT ---
diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin
index 8be50bceb..266dcdbf4 100644
--- a/core/sys/windows/kernel32.odin
+++ b/core/sys/windows/kernel32.odin
@@ -20,6 +20,15 @@ COMMON_LVB_GRID_RVERTICAL :: WORD(0x1000)
COMMON_LVB_REVERSE_VIDEO :: WORD(0x4000)
COMMON_LVB_UNDERSCORE :: WORD(0x8000)
COMMON_LVB_SBCSDBCS :: WORD(0x0300)
+EV_BREAK :: DWORD(0x0040)
+EV_CTS :: DWORD(0x0008)
+EV_DSR :: DWORD(0x0010)
+EV_ERR :: DWORD(0x0080)
+EV_RING :: DWORD(0x0100)
+EV_RLSD :: DWORD(0x0020)
+EV_RXCHAR :: DWORD(0x0001)
+EV_RXFLAG :: DWORD(0x0002)
+EV_TXEMPTY :: DWORD(0x0004)
@(default_calling_convention="system")
foreign kernel32 {
@@ -109,9 +118,12 @@ foreign kernel32 {
ClearCommError :: proc(hFile: HANDLE, lpErrors: ^Com_Error, lpStat: ^COMSTAT) -> BOOL ---
GetCommState :: proc(handle: HANDLE, dcb: ^DCB) -> BOOL ---
SetCommState :: proc(handle: HANDLE, dcb: ^DCB) -> BOOL ---
- GetCommPorts :: proc(lpPortNumbers: PULONG, uPortNumbersCount: ULONG, puPortNumbersFound: PULONG) -> ULONG ---
+ SetCommMask :: proc(handle: HANDLE, dwEvtMap: DWORD) -> BOOL ---
+ GetCommMask :: proc(handle: HANDLE, lpEvtMask: LPDWORD) -> BOOL ---
+ WaitCommEvent :: proc(handle: HANDLE, lpEvtMask: LPDWORD, lpOverlapped: LPOVERLAPPED) -> BOOL ---
GetCommandLineW :: proc() -> LPCWSTR ---
GetTempPathW :: proc(nBufferLength: DWORD, lpBuffer: LPCWSTR) -> DWORD ---
+ GetTempFileNameW :: proc(lpPathName: LPCWSTR, lpPrefixString: LPCWSTR, uUnique: c_int, lpTempFileName: LPWSTR) -> c_uint ---
GetCurrentProcess :: proc() -> HANDLE ---
GetCurrentProcessId :: proc() -> DWORD ---
GetCurrentThread :: proc() -> HANDLE ---
@@ -239,6 +251,10 @@ foreign kernel32 {
hThread: HANDLE,
lpContext: LPCONTEXT,
) -> BOOL ---
+ SetThreadContext :: proc(
+ hThread: HANDLE,
+ lpContext: LPCONTEXT,
+ ) -> BOOL ---
CreateProcessW :: proc(
lpApplicationName: LPCWSTR,
lpCommandLine: LPWSTR,
@@ -1068,6 +1084,11 @@ foreign one_core {
PageProtection: ULONG,
PreferredNode: ULONG,
) -> PVOID ---
+ GetCommPorts :: proc(
+ lpPortNumbers: PULONG,
+ uPortNumbersCount: ULONG,
+ puPortNumbersFound: PULONG,
+ ) -> ULONG ---
}
@@ -1220,3 +1241,31 @@ GHND :: (GMEM_MOVEABLE | GMEM_ZEROINIT)
GPTR :: (GMEM_FIXED | GMEM_ZEROINIT)
LPTOP_LEVEL_EXCEPTION_FILTER :: PVECTORED_EXCEPTION_HANDLER
+
+ACTCTXW :: struct {
+ Size: ULONG,
+ Flags: DWORD,
+ Source: LPCWSTR,
+ ProcessorArchitecture: USHORT,
+ LangId: LANGID,
+ AssemblyDirectory: LPCWSTR,
+ ResourceName: LPCWSTR,
+ ApplicationName: LPCWSTR,
+ Module: HMODULE,
+}
+PACTCTXW :: ^ACTCTXW
+PCACTCTXW :: ^ACTCTXW
+
+ACTCTX_FLAG_PROCESSOR_ARCHITECTURE_VALID :: 0x001
+ACTCTX_FLAG_LANGID_VALID :: 0x002
+ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID :: 0x004
+ACTCTX_FLAG_RESOURCE_NAME_VALID :: 0x008
+ACTCTX_FLAG_SET_PROCESS_DEFAULT :: 0x010
+ACTCTX_FLAG_APPLICATION_NAME_VALID :: 0x020
+ACTCTX_FLAG_HMODULE_VALID :: 0x080
+
+@(default_calling_convention="system")
+foreign kernel32 {
+ CreateActCtxW :: proc(pActCtx: ^ACTCTXW) -> HANDLE ---
+ ActivateActCtx :: proc(hActCtx: HANDLE, lpCookie: ^ULONG_PTR) -> BOOL ---
+}
diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin
index e1ace4133..f698b63d4 100644
--- a/core/sys/windows/types.odin
+++ b/core/sys/windows/types.odin
@@ -66,6 +66,7 @@ PULONG_PTR :: ^ULONG_PTR
LPULONG_PTR :: ^ULONG_PTR
DWORD_PTR :: ULONG_PTR
LONG_PTR :: int
+INT_PTR :: int
UINT_PTR :: uintptr
ULONG :: c_ulong
ULONGLONG :: c_ulonglong
@@ -143,6 +144,7 @@ LPWSAPROTOCOL_INFO :: ^WSAPROTOCOL_INFO
LPSTR :: ^CHAR
LPWSTR :: ^WCHAR
OLECHAR :: WCHAR
+BSTR :: ^OLECHAR
LPOLESTR :: ^OLECHAR
LPCOLESTR :: LPCSTR
LPFILETIME :: ^FILETIME
@@ -541,6 +543,44 @@ COLOR_3DHIGHLIGHT :: COLOR_BTNHIGHLIGHT
COLOR_3DHILIGHT :: COLOR_BTNHIGHLIGHT
COLOR_BTNHILIGHT :: COLOR_BTNHIGHLIGHT
+// Common Control Notification Code Ranges
+NM_FIRST :: 0
+NM_LAST :: ~DWORD(99 - 1)
+LVN_FIRST :: ~DWORD(100 - 1)
+LVN_LAST :: ~DWORD(199 - 1)
+HDN_FIRST :: ~DWORD(300 - 1)
+HDN_LAST :: ~DWORD(399 - 1)
+TVN_FIRST :: ~DWORD(400 - 1)
+TVN_LAST :: ~DWORD(499 - 1)
+TTN_FIRST :: ~DWORD(520 - 1)
+TTN_LAST :: ~DWORD(549 - 1)
+TCN_FIRST :: ~DWORD(550 - 1)
+TCN_LAST :: ~DWORD(580 - 1)
+CDN_FIRST :: ~DWORD(601 - 1)
+CDN_LAST :: ~DWORD(699 - 1)
+TBN_FIRST :: ~DWORD(700 - 1)
+TBN_LAST :: ~DWORD(720 - 1)
+UDN_FIRST :: ~DWORD(721 - 1)
+UDN_LAST :: ~DWORD(740 - 1)
+MCN_FIRST :: ~DWORD(750 - 1)
+MCN_LAST :: ~DWORD(759 - 1)
+DTN_FIRST :: ~DWORD(760 - 1)
+DTN_LAST :: ~DWORD(799 - 1)
+CBEN_FIRST :: ~DWORD(800 - 1)
+CBEN_LAST :: ~DWORD(830 - 1)
+RBN_FIRST :: ~DWORD(831 - 1)
+RBN_LAST :: ~DWORD(859 - 1)
+IPN_FIRST :: ~DWORD(860 - 1)
+IPN_LAST :: ~DWORD(879 - 1)
+SBN_FIRST :: ~DWORD(880 - 1)
+SBN_LAST :: ~DWORD(899 - 1)
+PGN_FIRST :: ~DWORD(900 - 1)
+PGN_LAST :: ~DWORD(950 - 1)
+WMN_FIRST :: ~DWORD(1000 - 1)
+WMN_LAST :: ~DWORD(1200 - 1)
+BCN_FIRST :: ~DWORD(1250 - 1)
+BCN_LAST :: ~DWORD(1350 - 1)
+
// Combo Box Notification Codes
CBN_ERRSPACE :: -1
CBN_SELCHANGE :: 1
@@ -623,6 +663,10 @@ BST_INDETERMINATE :: 0x0002
BST_PUSHED :: 0x0004
BST_FOCUS :: 0x0008
+// Button Control Notification Codes
+BCN_HOTITEMCHANGE :: (BCN_FIRST + 0x0001)
+BCN_DROPDOWN :: (BCN_FIRST + 0x0002)
+
// Static Control Constants
SS_LEFT :: 0x00000000
SS_CENTER :: 0x00000001
@@ -685,6 +729,416 @@ EN_VSCROLL :: 0x0602
EN_ALIGN_LTR_EC :: 0x0700
EN_ALIGN_RTL_EC :: 0x0701
+// Toolbar Styles
+TBS_AUTOTICKS :: 0x001
+TBS_VERT :: 0x002
+TBS_HORZ :: 0x000
+TBS_TOP :: 0x004
+TBS_BOTTOM :: 0x000
+TBS_LEFT :: 0x004
+TBS_RIGHT :: 0x000
+TBS_BOTH :: 0x008
+TBS_NOTICKS :: 0x010
+TBS_ENABLESELRANGE :: 0x020
+TBS_FIXEDLENGTH :: 0x040
+TBS_NOTHUMB :: 0x080
+TBS_TOOLTIPS :: 0x100
+TBS_REVERSED :: 0x200
+TBS_DOWNISLEFT :: 0x400
+
+// Toolbar Button Styles
+TBSTYLE_BUTTON :: 0x0000
+TBSTYLE_SEP :: 0x0001
+TBSTYLE_CHECK :: 0x0002
+TBSTYLE_GROUP :: 0x0004
+TBSTYLE_CHECKGROUP :: (TBSTYLE_GROUP | TBSTYLE_CHECK)
+TBSTYLE_DROPDOWN :: 0x0008
+TBSTYLE_AUTOSIZE :: 0x0010
+TBSTYLE_NOPREFIX :: 0x0020
+TBSTYLE_TOOLTIPS :: 0x0100
+TBSTYLE_WRAPABLE :: 0x0200
+TBSTYLE_ALTDRAG :: 0x0400
+TBSTYLE_FLAT :: 0x0800
+TBSTYLE_LIST :: 0x1000
+TBSTYLE_CUSTOMERASE :: 0x2000
+TBSTYLE_REGISTERDROP :: 0x4000
+TBSTYLE_TRANSPARENT :: 0x8000
+
+// Toolbar Button Styles (Aliases)
+BTNS_BUTTON :: TBSTYLE_BUTTON
+BTNS_SEP :: TBSTYLE_SEP
+BTNS_CHECK :: TBSTYLE_CHECK
+BTNS_GROUP :: TBSTYLE_GROUP
+BTNS_CHECKGROUP :: TBSTYLE_CHECKGROUP
+BTNS_DROPDOWN :: TBSTYLE_DROPDOWN
+BTNS_AUTOSIZE :: TBSTYLE_AUTOSIZE
+BTNS_NOPREFIX :: TBSTYLE_NOPREFIX
+BTNS_SHOWTEXT :: 0x40
+BTNS_WHOLEDROPDOWN :: 0x80
+
+// Toolbar Extended Styles
+TBSTYLE_EX_DRAWDDARROWS :: 0x01
+TBSTYLE_EX_MIXEDBUTTONS :: 0x08
+TBSTYLE_EX_HIDECLIPPEDBUTTONS :: 0x10
+TBSTYLE_EX_DOUBLEBUFFER :: 0x80
+
+// Toolbar Item State Codes
+TBSTATE_CHECKED :: 0x01
+TBSTATE_PRESSED :: 0x02
+TBSTATE_ENABLED :: 0x04
+TBSTATE_HIDDEN :: 0x08
+TBSTATE_INDETERMINATE :: 0x10
+TBSTATE_WRAP :: 0x20
+TBSTATE_ELLIPSES :: 0x40
+TBSTATE_MARKED :: 0x80
+
+// Toolbar Constants
+TBCDRF_NOEDGES :: 0x010000
+TBCDRF_HILITEHOTTRACK :: 0x020000
+TBCDRF_NOOFFSET :: 0x040000
+TBCDRF_NOMARK :: 0x080000
+TBCDRF_NOETCHEDEFFECT :: 0x100000
+TBCDRF_BLENDICON :: 0x200000
+TBCDRF_NOBACKGROUND :: 0x400000
+
+TBBF_LARGE :: 0x1
+
+TBIF_IMAGE :: 0x00000001
+TBIF_TEXT :: 0x00000002
+TBIF_STATE :: 0x00000004
+TBIF_STYLE :: 0x00000008
+TBIF_LPARAM :: 0x00000010
+TBIF_COMMAND :: 0x00000020
+TBIF_SIZE :: 0x00000040
+TBIF_BYINDEX :: 0x80000000
+
+TBMF_PAD :: 0x1
+TBMF_BARPAD :: 0x2
+TBMF_BUTTONSPACING :: 0x4
+
+IDB_STD_SMALL_COLOR :: 0
+IDB_STD_LARGE_COLOR :: 1
+IDB_VIEW_SMALL_COLOR :: 4
+IDB_VIEW_LARGE_COLOR :: 5
+IDB_HIST_SMALL_COLOR :: 8
+IDB_HIST_LARGE_COLOR :: 9
+
+STD_CUT :: 0
+STD_COPY :: 1
+STD_PASTE :: 2
+STD_UNDO :: 3
+STD_REDOW :: 4
+STD_DELETE :: 5
+STD_FILENEW :: 6
+STD_FILEOPEN :: 7
+STD_FILESAVE :: 8
+STD_PRINTPRE :: 9
+STD_PROPERTIES :: 10
+STD_HELP :: 11
+STD_FIND :: 12
+STD_REPLACE :: 13
+STD_PRINT :: 14
+
+VIEW_LARGEICONS :: 0
+VIEW_SMALLICONS :: 1
+VIEW_LIST :: 2
+VIEW_DETAILS :: 3
+VIEW_SORTNAME :: 4
+VIEW_SORTSIZE :: 5
+VIEW_SORTDATE :: 6
+VIEW_SORTTYPE :: 7
+VIEW_PARENTFOLDER :: 8
+VIEW_NETCONNECT :: 9
+VIEW_NETDISCONNECT :: 10
+VIEW_NEWFOLDER :: 11
+VIEW_VIEWMENU :: 12
+
+HIST_BACK :: 0
+HIST_FORWARD :: 1
+HIST_FAVORITES :: 2
+HIST_ADDTOFAVORITES :: 3
+HIST_VIEWTREE :: 4
+
+// Header Control Styles
+HDS_HORZ :: 0x000
+HDS_BUTTONS :: 0x002
+HDS_HOTTRACK :: 0x004
+HDS_HIDDEN :: 0x008
+HDS_DRAGDROP :: 0x040
+HDS_FULLDRAG :: 0x080
+HDS_FILTERBAR :: 0x100
+HDS_FLAT :: 0x200
+
+// Header Control Notifications
+HDN_ITEMCHANGINGA :: (HDN_FIRST-0)
+HDN_ITEMCHANGEDA :: (HDN_FIRST-1)
+HDN_ITEMCLICKA :: (HDN_FIRST-2)
+HDN_ITEMDBLCLICKA :: (HDN_FIRST-3)
+HDN_DIVIDERDBLCLICKA :: (HDN_FIRST-5)
+HDN_BEGINTRACKA :: (HDN_FIRST-6)
+HDN_ENDTRACKA :: (HDN_FIRST-7)
+HDN_TRACKA :: (HDN_FIRST-8)
+HDN_GETDISPINFOA :: (HDN_FIRST-9)
+HDN_BEGINDRAG :: (HDN_FIRST-10)
+HDN_ENDDRAG :: (HDN_FIRST-11)
+HDN_FILTERCHANGE :: (HDN_FIRST-12)
+HDN_FILTERBTNCLICK :: (HDN_FIRST-13)
+HDN_ITEMCHANGINGW :: (HDN_FIRST-20)
+HDN_ITEMCHANGEDW :: (HDN_FIRST-21)
+HDN_ITEMCLICKW :: (HDN_FIRST-22)
+HDN_ITEMDBLCLICKW :: (HDN_FIRST-23)
+HDN_DIVIDERDBLCLICKW :: (HDN_FIRST-25)
+HDN_BEGINTRACKW :: (HDN_FIRST-26)
+HDN_ENDTRACKW :: (HDN_FIRST-27)
+HDN_TRACKW :: (HDN_FIRST-28)
+HDN_GETDISPINFOW :: (HDN_FIRST-29)
+
+// Header Control Constants
+HDFT_ISSTRING :: 0x0000
+HDFT_ISNUMBER :: 0x0001
+HDFT_HASNOVALUE :: 0x8000
+
+HDI_WIDTH :: 0x001
+HDI_HEIGHT :: HDI_WIDTH
+HDI_TEXT :: 0x002
+HDI_FORMAT :: 0x004
+HDI_LPARAM :: 0x008
+HDI_BITMAP :: 0x010
+HDI_IMAGE :: 0x020
+HDI_DI_SETITEM :: 0x040
+HDI_ORDER :: 0x080
+HDI_FILTER :: 0x100
+
+HDF_LEFT :: 0x0000000
+HDF_RIGHT :: 0x0000001
+HDF_CENTER :: 0x0000002
+HDF_JUSTIFYMASK :: 0x0000003
+HDF_RTLREADING :: 0x0000004
+HDF_CHECKBOX :: 0x0000040
+HDF_CHECKED :: 0x0000080
+HDF_FIXEDWIDTH :: 0x0000100
+HDF_SORTDOWN :: 0x0000200
+HDF_SORTUP :: 0x0000400
+HDF_IMAGE :: 0x0000800
+HDF_BITMAP_ON_RIGHT :: 0x0001000
+HDF_BITMAP :: 0x0002000
+HDF_STRING :: 0x0004000
+HDF_OWNERDRAW :: 0x0008000
+HDF_SPLITBUTTON :: 0x1000000
+
+HHT_NOWHERE :: 0x001
+HHT_ONHEADER :: 0x002
+HHT_ONDIVIDER :: 0x004
+HHT_ONDIVOPEN :: 0x008
+HHT_ONFILTER :: 0x010
+HHT_ONFILTERBUTTON :: 0x020
+HHT_ABOVE :: 0x100
+HHT_BELOW :: 0x200
+HHT_TORIGHT :: 0x400
+HHT_TOLEFT :: 0x800
+
+// Rebar Control Styles
+RBS_TOOLTIPS :: 0x0100
+RBS_VARHEIGHT :: 0x0200
+RBS_BANDBORDERS :: 0x0400
+RBS_FIXEDORDER :: 0x0800
+RBS_REGISTERDROP :: 0x1000
+RBS_AUTOSIZE :: 0x2000
+RBS_VERTICALGRIPPER :: 0x4000
+RBS_DBLCLKTOGGLE :: 0x8000
+
+// Tooltip Control Styles
+TTS_ALWAYSTIP :: 0x01
+TTS_NOPREFIX :: 0x02
+TTS_NOANIMATE :: 0x10
+TTS_NOFADE :: 0x20
+TTS_BALLOON :: 0x40
+TTS_CLOSE :: 0x80
+
+// Statusbar Control Styles
+SBARS_SIZEGRIP :: 0x100
+SBARS_TOOLTIPS :: 0x800
+
+// Statusbar Control Constants
+SBT_TOOLTIPS :: 0x800
+
+// Up-Down Control Styles
+UDS_WRAP :: 0x001
+UDS_SETBUDDYINT :: 0x002
+UDS_ALIGNRIGHT :: 0x004
+UDS_ALIGNLEFT :: 0x008
+UDS_AUTOBUDDY :: 0x010
+UDS_ARROWKEYS :: 0x020
+UDS_HORZ :: 0x040
+UDS_NOTHOUSANDS :: 0x080
+UDS_HOTTRACK :: 0x100
+
+// Common Control Styles
+CCS_TOP :: 0x01
+CCS_NOMOVEY :: 0x02
+CCS_BOTTOM :: 0x03
+CCS_NORESIZE :: 0x04
+CCS_NOPARENTALIGN :: 0x08
+CCS_ADJUSTABLE :: 0x20
+CCS_NODIVIDER :: 0x40
+CCS_VERT :: 0x80
+CCS_LEFT :: (CCS_VERT | CCS_TOP)
+CCS_RIGHT :: (CCS_VERT | CCS_BOTTOM)
+CCS_NOMOVEX :: (CCS_VERT | CCS_NOMOVEY)
+
+// List-View Control Styles
+LVS_ICON :: 0x0000
+LVS_REPORT :: 0x0001
+LVS_SMALLICON :: 0x0002
+LVS_LIST :: 0x0003
+LVS_TYPEMASK :: 0x0003
+LVS_SINGLESEL :: 0x0004
+LVS_SHOWSELALWAYS :: 0x0008
+LVS_SORTASCENDING :: 0x0010
+LVS_SORTDESCENDING :: 0x0020
+LVS_SHAREIMAGELISTS :: 0x0040
+LVS_NOLABELWRAP :: 0x0080
+LVS_AUTOARRANGE :: 0x0100
+LVS_EDITLABELS :: 0x0200
+LVS_OWNERDATA :: 0x1000
+LVS_NOSCROLL :: 0x2000
+LVS_TYPESTYLEMASK :: 0xFC00
+LVS_ALIGNTOP :: 0x0000
+LVS_ALIGNLEFT :: 0x0800
+LVS_ALIGNMASK :: 0x0C00
+LVS_OWNERDRAWFIXED :: 0x0400
+LVS_NOCOLUMNHEADER :: 0x4000
+LVS_NOSORTHEADER :: 0x8000
+
+// Tree-View Control Styles
+TVS_HASBUTTONS :: 0x0001
+TVS_HASLINES :: 0x0002
+TVS_LINESATROOT :: 0x0004
+TVS_EDITLABELS :: 0x0008
+TVS_DISABLEDRAGDROP :: 0x0010
+TVS_SHOWSELALWAYS :: 0x0020
+TVS_RTLREADING :: 0x0040
+TVS_NOTOOLTIPS :: 0x0080
+TVS_CHECKBOXES :: 0x0100
+TVS_TRACKSELECT :: 0x0200
+TVS_SINGLEEXPAND :: 0x0400
+TVS_INFOTIP :: 0x0800
+TVS_FULLROWSELECT :: 0x1000
+TVS_NOSCROLL :: 0x2000
+TVS_NONEVENHEIGHT :: 0x4000
+TVS_NOHSCROLL :: 0x8000
+
+// Tree-View Control Constants
+TVE_COLLAPSE :: 0x0001
+TVE_EXPAND :: 0x0002
+TVE_TOGGLE :: 0x0003
+TVE_EXPANDPARTIAL :: 0x4000
+TVE_COLLAPSERESET :: 0x8000
+
+TVSIL_NORMAL :: 0
+TVSIL_STATE :: 2
+
+TVGN_ROOT :: 0x0
+TVGN_NEXT :: 0x1
+TVGN_PREVIOUS :: 0x2
+TVGN_PARENT :: 0x3
+TVGN_CHILD :: 0x4
+TVGN_FIRSTVISIBLE :: 0x5
+TVGN_NEXTVISIBLE :: 0x6
+TVGN_PREVIOUSVISIBLE :: 0x7
+TVGN_DROPHILITE :: 0x8
+TVGN_CARET :: 0x9
+TVGN_LASTVISIBLE :: 0xA
+
+TVSI_NOSINGLEEXPAND :: 0x8000
+
+TVHT_NOWHERE :: 0x001
+TVHT_ONITEMICON :: 0x002
+TVHT_ONITEMLABEL :: 0x004
+TVHT_ONITEM :: (TVHT_ONITEMICON | TVHT_ONITEMLABEL | TVHT_ONITEMSTATEICON)
+TVHT_ONITEMINDENT :: 0x008
+TVHT_ONITEMBUTTON :: 0x010
+TVHT_ONITEMRIGHT :: 0x020
+TVHT_ONITEMSTATEICON :: 0x040
+TVHT_ABOVE :: 0x100
+TVHT_BELOW :: 0x200
+TVHT_TORIGHT :: 0x400
+TVHT_TOLEFT :: 0x800
+
+// Tab Control Styles
+TCS_SCROLLOPPOSITE :: 0x0001
+TCS_BOTTOM :: 0x0002
+TCS_RIGHT :: 0x0002
+TCS_MULTISELECT :: 0x0004
+TCS_FLATBUTTONS :: 0x0008
+TCS_FORCEICONLEFT :: 0x0010
+TCS_FORCELABELLEFT :: 0x0020
+TCS_HOTTRACK :: 0x0040
+TCS_VERTICAL :: 0x0080
+TCS_TABS :: 0x0000
+TCS_BUTTONS :: 0x0100
+TCS_SINGLELINE :: 0x0000
+TCS_MULTILINE :: 0x0200
+TCS_RIGHTJUSTIFY :: 0x0000
+TCS_FIXEDWIDTH :: 0x0400
+TCS_RAGGEDRIGHT :: 0x0800
+TCS_FOCUSONBUTTONDOWN :: 0x1000
+TCS_OWNERDRAWFIXED :: 0x2000
+TCS_TOOLTIPS :: 0x4000
+TCS_FOCUSNEVER :: 0x8000
+
+// Tab Control Constants
+TCIF_TEXT :: 0x01
+TCIF_IMAGE :: 0x02
+TCIF_RTLREADING :: 0x04
+TCIF_PARAM :: 0x08
+TCIF_STATE :: 0x10
+
+TCIS_BUTTONPRESSED :: 0x1
+TCIS_HIGHLIGHTED :: 0x2
+
+TCHT_NOWHERE :: 0x1
+TCHT_ONITEMICON :: 0x2
+TCHT_ONITEMLABEL :: 0x4
+TCHT_ONITEM :: (TCHT_ONITEMICON | TCHT_ONITEMLABEL)
+
+// Animation Control Styles
+ACS_CENTER :: 0x1
+ACS_TRANSPARENT :: 0x2
+ACS_AUTOPLAY :: 0x4
+ACS_TIMER :: 0x8
+
+// Month-Calendar Control Styles
+MCS_DAYSTATE :: 0x01
+MCS_MULTISELECT :: 0x02
+MCS_WEEKNUMBERS :: 0x04
+MCS_NOTODAYCIRCLE :: 0x08
+MCS_NOTODAY :: 0x10
+
+// Date-and-Time Picker Control Styles
+DTS_UPDOWN :: 0x01
+DTS_SHOWNONE :: 0x02
+DTS_SHORTDATEFORMAT :: 0x00
+DTS_LONGDATEFORMAT :: 0x04
+DTS_SHORTDATECENTURYFORMAT :: 0x0C
+DTS_TIMEFORMAT :: 0x09
+DTS_APPCANPARSE :: 0x10
+DTS_RIGHTALIGN :: 0x20
+
+// Pager Control Styles
+PGS_VERT :: 0x0
+PGS_HORZ :: 0x1
+PGS_AUTOSCROLL :: 0x2
+PGS_DRAGNDROP :: 0x4
+
+// Native Font Control Styles
+NFS_EDIT :: 0x01
+NFS_STATIC :: 0x02
+NFS_LISTCOMBO :: 0x04
+NFS_BUTTON :: 0x08
+NFS_ALL :: 0x10
+NFS_USEFONTASSOC :: 0x20
+
// Font Weights
FW_DONTCARE :: 0
FW_THIN :: 100
@@ -795,6 +1249,8 @@ TIMERPROC :: #type proc "system" (HWND, UINT, UINT_PTR, DWORD)
WNDPROC :: #type proc "system" (HWND, UINT, WPARAM, LPARAM) -> LRESULT
+SUBCLASSPROC :: #type proc "system" (HWND, UINT, WPARAM, LPARAM, UINT_PTR, DWORD_PTR) -> LRESULT
+
HOOKPROC :: #type proc "system" (code: c_int, wParam: WPARAM, lParam: LPARAM) -> LRESULT
WINEVENTPROC :: #type proc "system" (
@@ -1203,6 +1659,16 @@ NMHDR :: struct {
code: UINT, // NM_ code
}
+NMCUSTOMDRAW :: struct {
+ hdr: NMHDR,
+ dwDrawStage: DWORD,
+ hdc: HDC,
+ rc: RECT,
+ dwItemSpec: DWORD_PTR,
+ uItemState: UINT,
+ lItemlParam: LPARAM,
+}
+
NCCALCSIZE_PARAMS :: struct {
rgrc: [3]RECT,
lppos: PWINDOWPOS,
@@ -2201,7 +2667,24 @@ DUPLICATE_SAME_ACCESS: DWORD : 0x00000002
CONDITION_VARIABLE_INIT :: CONDITION_VARIABLE{}
SRWLOCK_INIT :: SRWLOCK{}
-STARTF_USESTDHANDLES: DWORD : 0x00000100
+// Flags in STARTUPINFOW.dwFlags.
+STARTF_USESHOWWINDOW: DWORD : 0x00000001
+STARTF_USESIZE: DWORD : 0x00000002
+STARTF_USEPOSITION: DWORD : 0x00000004
+STARTF_USECOUNTCHARS: DWORD : 0x00000008
+STARTF_USEFILLATTRIBUTE: DWORD : 0x00000010
+STARTF_RUNFULLSCREEN: DWORD : 0x00000020 // ignored for non-x86 platforms
+STARTF_FORCEONFEEDBACK: DWORD : 0x00000040
+STARTF_FORCEOFFFEEDBACK: DWORD : 0x00000080
+STARTF_USESTDHANDLES: DWORD : 0x00000100
+// WINVER >= 0x400
+STARTF_USEHOTKEY: DWORD : 0x00000200
+STARTF_TITLEISLINKNAME: DWORD : 0x00000800
+STARTF_TITLEISAPPID: DWORD : 0x00001000
+STARTF_PREVENTPINNING: DWORD : 0x00002000
+// WINVER >= 0x600
+STARTF_UNTRUSTEDSOURCE: DWORD : 0x00008000
+
VOLUME_NAME_DOS: DWORD : 0x0
@@ -2694,11 +3177,23 @@ EXCEPTION_MAXIMUM_PARAMETERS :: 15
EXCEPTION_DATATYPE_MISALIGNMENT :: 0x80000002
EXCEPTION_BREAKPOINT :: 0x80000003
+EXCEPTION_SINGLE_STEP :: 0x80000004
EXCEPTION_ACCESS_VIOLATION :: 0xC0000005
+EXCEPTION_IN_PAGE_ERROR :: 0xC0000006
EXCEPTION_ILLEGAL_INSTRUCTION :: 0xC000001D
+EXCEPTION_NONCONTINUABLE_EXCEPTION :: 0xC0000025
+EXCEPTION_INVALID_DISPOSITION :: 0xC0000026
EXCEPTION_ARRAY_BOUNDS_EXCEEDED :: 0xC000008C
+EXCEPTION_FLT_DENORMAL_OPERAND :: 0xC000008D
+EXCEPTION_FLT_DIVIDE_BY_ZERO :: 0xC000008E
+EXCEPTION_FLT_INEXACT_RESULT :: 0xC000008F
+EXCEPTION_FLT_INVALID_OPERATION :: 0xC0000090
+EXCEPTION_FLT_OVERFLOW :: 0xC0000091
+EXCEPTION_FLT_STACK_CHECK :: 0xC0000092
+EXCEPTION_FLT_UNDERFLOW :: 0xC0000093
EXCEPTION_INT_DIVIDE_BY_ZERO :: 0xC0000094
EXCEPTION_INT_OVERFLOW :: 0xC0000095
+EXCEPTION_PRIV_INSTRUCTION :: 0xC0000096
EXCEPTION_STACK_OVERFLOW :: 0xC00000FD
STATUS_PRIVILEGED_INSTRUCTION :: 0xC0000096
@@ -3415,8 +3910,6 @@ TIME_ZONE_INFORMATION :: struct {
DaylightBias: LONG,
}
-
-@(private="file")
IMAGE_DOS_HEADER :: struct {
e_magic: WORD,
e_cblp: WORD,
@@ -3534,6 +4027,19 @@ IMAGE_EXPORT_DIRECTORY :: struct {
AddressOfNameOrdinals: DWORD, // RVA from base of image
}
+IMAGE_DEBUG_DIRECTORY :: struct {
+ Characteristics: DWORD,
+ TimeDateStamp: DWORD,
+ MajorVersion: WORD,
+ MinorVersion: WORD,
+ Type: DWORD,
+ SizeOfData: DWORD,
+ AddressOfRawData: DWORD,
+ PointerToRawData: DWORD,
+}
+
+IMAGE_DEBUG_TYPE_CODEVIEW :: 2
+
SICHINTF :: DWORD
SHCONTF :: DWORD
SFGAOF :: ULONG
@@ -4519,7 +5025,7 @@ DNS_RECORD :: struct { // aka DNS_RECORDA
Flags: DWORD,
dwTtl: DWORD,
_: DWORD,
- Data: struct #raw_union {
+ Data: struct #raw_union #align(4) {
CNAME: DNS_PTR_DATAA,
A: u32be, // Ipv4 Address
AAAA: u128be, // Ipv6 Address
@@ -4550,6 +5056,31 @@ DNS_SRV_DATAA :: struct {
_: WORD, // padding
}
+// See https://learn.microsoft.com/en-us/windows/win32/dns/dns-constants
+DNS_QUERY_OPTION :: enum DWORD {
+ ACCEPT_TRUNCATED_RESPONSE = 0,
+ DNS_QUERY_USE_TCP_ONLY = 1,
+ NO_RECURSION = 2,
+ BYPASS_CACHE = 3,
+ NO_WIRE_QUERY = 4,
+ NO_LOCAL_NAME = 5,
+ NO_HOSTS_FILE = 6,
+ NO_NETBT = 7,
+ WIRE_ONLY = 8,
+ RETURN_MESSAGE = 9,
+ MULTICAST_ONLY = 10,
+ NO_MULTICAST = 11,
+ TREAT_AS_FQDN = 12,
+ ADDRCONFIG = 13,
+ DUAL_ADDR = 14,
+ MULTICAST_WAIT = 17,
+ MULTICAST_VERIFY = 18,
+ DONT_RESET_TTL_VALUES = 20,
+ DISABLE_IDN_ENCODING = 21,
+ APPEND_MULTILABEL = 23,
+}
+DNS_QUERY_OPTIONS :: bit_set[DNS_QUERY_OPTION; DWORD]
+
SOCKADDR :: struct {
sa_family: ADDRESS_FAMILY,
sa_data: [14]CHAR,
diff --git a/core/sys/windows/user32.odin b/core/sys/windows/user32.odin
index 4ae33cd32..94cd57811 100644
--- a/core/sys/windows/user32.odin
+++ b/core/sys/windows/user32.odin
@@ -2,6 +2,7 @@
package sys_windows
import "base:intrinsics"
+import "core:c"
foreign import user32 "system:User32.lib"
@(default_calling_convention="system")
@@ -32,6 +33,8 @@ foreign user32 {
RegisterClassExW :: proc(^WNDCLASSEXW) -> ATOM ---
UnregisterClassW :: proc(lpClassName: LPCWSTR, hInstance: HINSTANCE) -> BOOL ---
+ RegisterHotKey :: proc(hnwd: HWND, id: c.int, fsModifiers: UINT, vk: UINT) -> BOOL ---
+
CreateWindowExW :: proc(
dwExStyle: DWORD,
lpClassName: LPCWSTR,
@@ -51,6 +54,7 @@ foreign user32 {
IsWindowVisible :: proc(hwnd: HWND) -> BOOL ---
IsWindowEnabled :: proc(hwnd: HWND) -> BOOL ---
IsIconic :: proc(hwnd: HWND) -> BOOL ---
+ IsZoomed :: proc(hwnd: HWND) -> BOOL ---
BringWindowToTop :: proc(hWnd: HWND) -> BOOL ---
GetTopWindow :: proc(hWnd: HWND) -> HWND ---
SetForegroundWindow :: proc(hWnd: HWND) -> BOOL ---
@@ -59,6 +63,8 @@ foreign user32 {
UpdateWindow :: proc(hWnd: HWND) -> BOOL ---
SetActiveWindow :: proc(hWnd: HWND) -> HWND ---
GetActiveWindow :: proc() -> HWND ---
+ SetFocus :: proc(hWnd: HWND) -> HWND ---
+ GetFocus :: proc() -> HWND ---
RedrawWindow :: proc(hwnd: HWND, lprcUpdate: LPRECT, hrgnUpdate: HRGN, flags: RedrawWindowFlags) -> BOOL ---
SetParent :: proc(hWndChild: HWND, hWndNewParent: HWND) -> HWND ---
SetPropW :: proc(hWnd: HWND, lpString: LPCWSTR, hData: HANDLE) -> BOOL ---
@@ -207,6 +213,7 @@ foreign user32 {
EnumDisplayMonitors :: proc(hdc: HDC, lprcClip: LPRECT, lpfnEnum: Monitor_Enum_Proc, dwData: LPARAM) -> BOOL ---
EnumWindows :: proc(lpEnumFunc: Window_Enum_Proc, lParam: LPARAM) -> BOOL ---
+ EnumChildWindows :: proc(hWndParent: HWND, lpEnumFunc: Window_Enum_Proc, lParam: LPARAM) -> BOOL ---
IsProcessDPIAware :: proc() -> BOOL ---
SetProcessDPIAware :: proc() -> BOOL ---
@@ -548,11 +555,11 @@ RI_KEY_TERMSRV_SHADOW :: 0x10
MOUSE_MOVE_RELATIVE :: 0x00
MOUSE_MOVE_ABSOLUTE :: 0x01
MOUSE_VIRTUAL_DESKTOP :: 0x02
-MOUSE_ATTRIUBTTES_CHANGED :: 0x04
+MOUSE_ATTRIBUTES_CHANGED :: 0x04
MOUSE_MOVE_NOCOALESCE :: 0x08
RI_MOUSE_BUTTON_1_DOWN :: 0x0001
-RI_MOUSE_LEFT_BUTTON_DOWNS :: RI_MOUSE_BUTTON_1_DOWN
+RI_MOUSE_LEFT_BUTTON_DOWN :: RI_MOUSE_BUTTON_1_DOWN
RI_MOUSE_BUTTON_1_UP :: 0x0002
RI_MOUSE_LEFT_BUTTON_UP :: RI_MOUSE_BUTTON_1_UP
RI_MOUSE_BUTTON_2_DOWN :: 0x0004
@@ -842,3 +849,23 @@ FKF_CONFIRMHOTKEY :: 0x8
FKF_HOTKEYSOUND :: 0x10
FKF_INDICATOR :: 0x20
FKF_CLICKON :: 0x40
+
+NONCLIENTMETRICSW :: struct {
+ cbSize: UINT,
+ iBorderWidth: i32,
+ iScrollWidth: i32,
+ iScrollHeight: i32,
+ iCaptionWidth: i32,
+ iCaptionHeight: i32,
+ lfCaptionFont: LOGFONTW,
+ iSmCaptionWidth: i32,
+ iSmCaptionHeight: i32,
+ lfSmCaptionFont: LOGFONTW,
+ iMenuWidth: i32,
+ iMenuHeight: i32,
+ lfMenuFont: LOGFONTW,
+ lfStatusFont: LOGFONTW,
+ lfMessageFont: LOGFONTW,
+ iPaddedBorderWidth: i32,
+}
+LPNONCLIENTMETRICSW :: ^NONCLIENTMETRICSW
diff --git a/core/sys/windows/ux_theme.odin b/core/sys/windows/ux_theme.odin
index 392cf1e18..679d9998f 100644
--- a/core/sys/windows/ux_theme.odin
+++ b/core/sys/windows/ux_theme.odin
@@ -9,4 +9,5 @@ PMARGINS :: ^MARGINS
@(default_calling_convention="system")
foreign uxtheme {
IsThemeActive :: proc() -> BOOL ---
+ SetWindowTheme :: proc(hWnd: HWND, pszSubAppName, pszSubIdList: LPCWSTR) -> HRESULT ---
}
diff --git a/core/sys/windows/window_messages.odin b/core/sys/windows/window_messages.odin
index d69771bdf..0901ee696 100644
--- a/core/sys/windows/window_messages.odin
+++ b/core/sys/windows/window_messages.odin
@@ -687,10 +687,14 @@ EM_GETAUTOURLDETECT :: 0x045c
TB_GETSTRINGA :: 0x045c
EM_SETPALETTE :: 0x045d
EM_GETTEXTEX :: 0x045e
+TB_SETHOTITEM2 :: 0x045e
EM_GETTEXTLENGTHEX :: 0x045f
EM_SHOWSCROLLBAR :: 0x0460
+TB_SETLISTGAP :: 0x0460
EM_SETTEXTEX :: 0x0461
+TB_GETIMAGELISTCOUNT :: 0x0462
TAPI_REPLY :: 0x0463
+TB_GETIDEALSIZE :: 0x0463
ACM_OPENA :: 0x0464
BFFM_SETSTATUSTEXTA :: 0x0464
CDM_FIRST :: 0x0464
@@ -704,6 +708,7 @@ CDM_GETFILEPATH :: 0x0465
EM_GETPUNCTUATION :: 0x0465
IPM_SETADDRESS :: 0x0465
PSM_SETCURSEL :: 0x0465
+TB_GETMETRICS :: 0x0465
UDM_SETRANGE :: 0x0465
WM_CHOOSEFONT_SETLOGFONT :: 0x0465
ACM_STOP :: 0x0466
@@ -712,6 +717,7 @@ CDM_GETFOLDERPATH :: 0x0466
EM_SETWORDWRAPMODE :: 0x0466
IPM_GETADDRESS :: 0x0466
PSM_REMOVEPAGE :: 0x0466
+TB_SETMETRICS :: 0x0466
UDM_GETRANGE :: 0x0466
WM_CAP_SET_CALLBACK_ERRORW :: 0x0466
WM_CHOOSEFONT_SETFLAGS :: 0x0466
@@ -721,6 +727,7 @@ CDM_GETFOLDERIDLIST :: 0x0467
EM_GETWORDWRAPMODE :: 0x0467
IPM_SETRANGE :: 0x0467
PSM_ADDPAGE :: 0x0467
+TB_GETITEMDROPDOWNRECT :: 0x0467
UDM_SETPOS :: 0x0467
WM_CAP_SET_CALLBACK_STATUSW :: 0x0467
BFFM_SETSTATUSTEXTW :: 0x0468
@@ -728,11 +735,13 @@ CDM_SETCONTROLTEXT :: 0x0468
EM_SETIMECOLOR :: 0x0468
IPM_SETFOCUS :: 0x0468
PSM_CHANGED :: 0x0468
+TB_SETPRESSEDIMAGELIST :: 0x0468
UDM_GETPOS :: 0x0468
CDM_HIDECONTROL :: 0x0469
EM_GETIMECOLOR :: 0x0469
IPM_ISBLANK :: 0x0469
PSM_RESTARTWINDOWS :: 0x0469
+TB_GETPRESSEDIMAGELIST :: 0x0469
UDM_SETBUDDY :: 0x0469
CDM_SETDEFEXT :: 0x046a
EM_SETIMEOPTIONS :: 0x046a
@@ -915,6 +924,10 @@ FM_GETDRIVEINFOW :: 0x0611
FM_GETFILESELW :: 0x0614
FM_GETFILESELLFNW :: 0x0615
WLX_WM_SAS :: 0x0659
+LM_HITTEST :: 0x0700
+LM_GETIDEALHEIGHT :: 0x0701
+LM_SETITEM :: 0x0702
+LM_GETITEM :: 0x0703
SM_GETSELCOUNT :: 0x07e8
UM_GETSELCOUNT :: 0x07e8
WM_CPL_LAUNCH :: 0x07e8
@@ -1011,6 +1024,7 @@ LVM_GETITEMW :: 0x104b
LVM_SETITEMW :: 0x104c
LVM_INSERTITEMW :: 0x104d
LVM_GETTOOLTIPS :: 0x104e
+LVM_SORTITEMSEX :: 0x1051
LVM_FINDITEMW :: 0x1053
LVM_GETSTRINGWIDTHW :: 0x1057
LVM_GETCOLUMNW :: 0x105f
@@ -1065,7 +1079,143 @@ LVM_GETFOOTERITEM :: 0x10d0
LVM_GETITEMINDEXRECT :: 0x10d1
LVM_SETITEMINDEXSTATE :: 0x10d2
LVM_GETNEXTITEMINDEX :: 0x10d3
+TV_FIRST :: 0x1100
+TVM_INSERTITEMA :: (TV_FIRST+0)
+TVM_DELETEITEM :: (TV_FIRST+1)
+TVM_EXPAND :: (TV_FIRST+2)
+TVM_GETITEMRECT :: (TV_FIRST+4)
+TVM_GETCOUNT :: (TV_FIRST+5)
+TVM_GETINDENT :: (TV_FIRST+6)
+TVM_SETINDENT :: (TV_FIRST+7)
+TVM_GETIMAGELIST :: (TV_FIRST+8)
+TVM_SETIMAGELIST :: (TV_FIRST+9)
+TVM_GETNEXTITEM :: (TV_FIRST+10)
+TVM_SELECTITEM :: (TV_FIRST+11)
+TVM_GETITEMA :: (TV_FIRST+12)
+TVM_SETITEMA :: (TV_FIRST+13)
+TVM_EDITLABELA :: (TV_FIRST+14)
+TVM_GETEDITCONTROL :: (TV_FIRST+15)
+TVM_GETVISIBLECOUNT :: (TV_FIRST+16)
+TVM_HITTEST :: (TV_FIRST+17)
+TVM_CREATEDRAGIMAGE :: (TV_FIRST+18)
+TVM_SORTCHILDREN :: (TV_FIRST+19)
+TVM_ENSUREVISIBLE :: (TV_FIRST+20)
+TVM_SORTCHILDRENCB :: (TV_FIRST+21)
+TVM_ENDEDITLABELNOW :: (TV_FIRST+22)
+TVM_GETISEARCHSTRINGA :: (TV_FIRST+23)
+TVM_SETTOOLTIPS :: (TV_FIRST+24)
+TVM_GETTOOLTIPS :: (TV_FIRST+25)
+TVM_SETINSERTMARK :: (TV_FIRST+26)
+TVM_SETUNICODEFORMAT :: CCM_SETUNICODEFORMAT
+TVM_GETUNICODEFORMAT :: CCM_GETUNICODEFORMAT
+TVM_SETITEMHEIGHT :: (TV_FIRST+27)
+TVM_GETITEMHEIGHT :: (TV_FIRST+28)
+TVM_SETBKCOLOR :: (TV_FIRST+29)
+TVM_SETTEXTCOLOR :: (TV_FIRST+30)
+TVM_GETBKCOLOR :: (TV_FIRST+31)
+TVM_GETTEXTCOLOR :: (TV_FIRST+32)
+TVM_SETSCROLLTIME :: (TV_FIRST+33)
+TVM_GETSCROLLTIME :: (TV_FIRST+34)
+TVM_SETINSERTMARKCOLOR :: (TV_FIRST+37)
+TVM_GETINSERTMARKCOLOR :: (TV_FIRST+38)
+TVM_GETITEMSTATE :: (TV_FIRST+39)
+TVM_SETLINECOLOR :: (TV_FIRST+40)
+TVM_GETLINECOLOR :: (TV_FIRST+41)
+TVM_MAPACCIDTOHTREEITEM :: (TV_FIRST+42)
+TVM_MAPHTREEITEMTOACCID :: (TV_FIRST+43)
+TVM_INSERTITEMW :: (TV_FIRST+50)
+TVM_GETITEMW :: (TV_FIRST+62)
+TVM_SETITEMW :: (TV_FIRST+63)
+TVM_GETISEARCHSTRINGW :: (TV_FIRST+64)
+TVM_EDITLABELW :: (TV_FIRST+65)
+HDM_FIRST :: 0x1200
+HDM_GETITEMCOUNT :: (HDM_FIRST+0)
+HDM_INSERTITEMA :: (HDM_FIRST+1)
+HDM_DELETEITEM :: (HDM_FIRST+2)
+HDM_GETITEMA :: (HDM_FIRST+3)
+HDM_SETITEMA :: (HDM_FIRST+4)
+HDM_LAYOUT :: (HDM_FIRST+5)
+HDM_HITTEST :: (HDM_FIRST+6)
+HDM_GETITEMRECT :: (HDM_FIRST+7)
+HDM_SETIMAGELIST :: (HDM_FIRST+8)
+HDM_GETIMAGELIST :: (HDM_FIRST+9)
+HDM_INSERTITEMW :: (HDM_FIRST+10)
+HDM_GETITEMW :: (HDM_FIRST+11)
+HDM_SETITEMW :: (HDM_FIRST+12)
+HDM_ORDERTOINDEX :: (HDM_FIRST+15)
+HDM_CREATEDRAGIMAGE :: (HDM_FIRST+16)
+HDM_GETORDERARRAY :: (HDM_FIRST+17)
+HDM_SETORDERARRAY :: (HDM_FIRST+18)
+HDM_SETHOTDIVIDER :: (HDM_FIRST+19)
+HDM_SETBITMAPMARGIN :: (HDM_FIRST+20)
+HDM_GETBITMAPMARGIN :: (HDM_FIRST+21)
+HDM_SETFILTERCHANGETIMEOUT :: (HDM_FIRST+22)
+HDM_SETUNICODEFORMAT :: CCM_SETUNICODEFORMAT
+HDM_GETUNICODEFORMAT :: CCM_GETUNICODEFORMAT
+HDM_EDITFILTER :: (HDM_FIRST+23)
+HDM_CLEARFILTER :: (HDM_FIRST+24)
+TCM_FIRST :: 0x1300
+TCM_GETIMAGELIST :: (TCM_FIRST+2)
+TCM_SETIMAGELIST :: (TCM_FIRST+3)
+TCM_GETITEMCOUNT :: (TCM_FIRST+4)
+TCM_GETITEMA :: (TCM_FIRST+5)
+TCM_SETITEMA :: (TCM_FIRST+6)
+TCM_INSERTITEMA :: (TCM_FIRST+7)
+TCM_DELETEITEM :: (TCM_FIRST+8)
+TCM_DELETEALLITEMS :: (TCM_FIRST+9)
+TCM_GETITEMRECT :: (TCM_FIRST+10)
+TCM_GETCURSEL :: (TCM_FIRST+11)
+TCM_SETCURSEL :: (TCM_FIRST+12)
+TCM_HITTEST :: (TCM_FIRST+13)
+TCM_SETITEMEXTRA :: (TCM_FIRST+14)
+TCM_ADJUSTRECT :: (TCM_FIRST+40)
+TCM_SETITEMSIZE :: (TCM_FIRST+41)
+TCM_REMOVEIMAGE :: (TCM_FIRST+42)
+TCM_SETPADDING :: (TCM_FIRST+43)
+TCM_GETROWCOUNT :: (TCM_FIRST+44)
+TCM_GETTOOLTIPS :: (TCM_FIRST+45)
+TCM_SETTOOLTIPS :: (TCM_FIRST+46)
+TCM_GETCURFOCUS :: (TCM_FIRST+47)
+TCM_SETCURFOCUS :: (TCM_FIRST+48)
+TCM_SETMINTABWIDTH :: (TCM_FIRST+49)
+TCM_DESELECTALL :: (TCM_FIRST+50)
+TCM_HIGHLIGHTITEM :: (TCM_FIRST+51)
+TCM_SETEXTENDEDSTYLE :: (TCM_FIRST+52)
+TCM_GETEXTENDEDSTYLE :: (TCM_FIRST+53)
+TCM_SETUNICODEFORMAT :: CCM_SETUNICODEFORMAT
+TCM_GETUNICODEFORMAT :: CCM_GETUNICODEFORMAT
+TCM_GETITEMW :: (TCM_FIRST+60)
+TCM_SETITEMW :: (TCM_FIRST+61)
+TCM_INSERTITEMW :: (TCM_FIRST+62)
+PGM_FIRST :: 0x1400
+PGM_SETCHILD :: (PGM_FIRST+1)
+PGM_RECALCSIZE :: (PGM_FIRST+2)
+PGM_FORWARDMOUSE :: (PGM_FIRST+3)
+PGM_SETBKCOLOR :: (PGM_FIRST+4)
+PGM_GETBKCOLOR :: (PGM_FIRST+5)
+PGM_SETBORDER :: (PGM_FIRST+6)
+PGM_GETBORDER :: (PGM_FIRST+7)
+PGM_SETPOS :: (PGM_FIRST+8)
+PGM_GETPOS :: (PGM_FIRST+9)
+PGM_SETBUTTONSIZE :: (PGM_FIRST+10)
+PGM_GETBUTTONSIZE :: (PGM_FIRST+11)
+PGM_GETBUTTONSTATE :: (PGM_FIRST+12)
+PGM_GETDROPTARGET :: CCM_GETDROPTARGET
+ECM_FIRST :: 0x1500
+EM_SETCUEBANNER :: ECM_FIRST + 0x0001
+EM_GETCUEBANNER :: ECM_FIRST + 0x0002
+EM_SHOWBALLOONTIP :: ECM_FIRST + 0x0003
+EM_HIDEBALLOONTIP :: ECM_FIRST + 0x0004
+EM_SETHILITE :: ECM_FIRST + 0x0005
+EM_GETHILITE :: ECM_FIRST + 0x0006
+EM_NOSETFOCUS :: ECM_FIRST + 0x0007
+EM_TAKEFOCUS :: ECM_FIRST + 0x0008
BCM_FIRST :: 0x1600
+BCM_GETIDEALSIZE :: BCM_FIRST + 0x0001
+BCM_SETIMAGELIST :: BCM_FIRST + 0x0002
+BCM_GETIMAGELIST :: BCM_FIRST + 0x0003
+BCM_SETTEXTMARGIN :: BCM_FIRST + 0x0004
+BCM_GETTEXTMARGIN :: BCM_FIRST + 0x0005
BCM_SETDROPDOWNSTATE :: BCM_FIRST + 0x0006
BCM_SETSPLITINFO :: BCM_FIRST + 0x0007
BCM_GETSPLITINFO :: BCM_FIRST + 0x0008
@@ -1073,9 +1223,29 @@ BCM_SETNOTE :: BCM_FIRST + 0x0009
BCM_GETNOTE :: BCM_FIRST + 0x000A
BCM_GETNOTELENGTH :: BCM_FIRST + 0x000B
BCM_SETSHIELD :: BCM_FIRST + 0x000C
+CBM_FIRST :: 0x1700
+CB_SETMINVISIBLE :: CBM_FIRST + 0x0001
+CB_GETMINVISIBLE :: CBM_FIRST + 0x0002
+CCM_FIRST :: 0x2000
+CCM_LAST :: (CCM_FIRST+0x200)
+CCM_SETBKCOLOR :: (CCM_FIRST+1)
+CCM_SETCOLORSCHEME :: (CCM_FIRST+2)
+CCM_GETCOLORSCHEME :: (CCM_FIRST+3)
+CCM_GETDROPTARGET :: (CCM_FIRST+4)
+CCM_SETUNICODEFORMAT :: (CCM_FIRST+5)
+CCM_GETUNICODEFORMAT :: (CCM_FIRST+6)
+CCM_SETVERSION :: (CCM_FIRST+7)
+CCM_GETVERSION :: (CCM_FIRST+8)
+CCM_SETNOTIFYWINDOW :: (CCM_FIRST+9)
+CCM_SETWINDOWTHEME :: (CCM_FIRST+11)
+CCM_DPISCALE :: (CCM_FIRST+12)
OCM__BASE :: 0x2000
LVM_SETUNICODEFORMAT :: 0x2005
+SB_SETUNICODEFORMAT :: 0x2005
LVM_GETUNICODEFORMAT :: 0x2006
+SB_GETUNICODEFORMAT :: 0x2006
+CBEM_SETWINDOWTHEME :: 0x200b
+TB_SETWINDOWTHEME :: 0x200b
OCM_CTLCOLOR :: 0x2019
OCM_DRAWITEM :: 0x202b
OCM_MEASUREITEM :: 0x202c
diff --git a/core/sys/windows/ws2_32.odin b/core/sys/windows/ws2_32.odin
index 5b2952495..d808bcd09 100644
--- a/core/sys/windows/ws2_32.odin
+++ b/core/sys/windows/ws2_32.odin
@@ -91,6 +91,7 @@ foreign ws2_32 {
WSACleanup :: proc() -> c_int ---
// [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsagetlasterror)
WSAGetLastError :: proc() -> c_int ---
+ WSASetLastError :: proc(err: c_int) ---
// [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsapoll)
WSAPoll :: proc(fdArray: ^WSA_POLLFD, fds: c_ulong, timeout: c_int) -> c_int ---
// [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsaduplicatesocketw)
diff --git a/core/sys/windows/xinput.odin b/core/sys/windows/xinput.odin
new file mode 100644
index 000000000..0089f88cb
--- /dev/null
+++ b/core/sys/windows/xinput.odin
@@ -0,0 +1,210 @@
+#+build windows
+package sys_windows
+
+foreign import "system:xinput.lib"
+
+// Device types available in XINPUT_CAPABILITIES
+// Correspond to XINPUT_DEVTYPE_...
+XINPUT_DEVTYPE :: enum BYTE {
+ GAMEPAD = 0x01,
+}
+
+// Device subtypes available in XINPUT_CAPABILITIES
+// Correspond to XINPUT_DEVSUBTYPE_...
+XINPUT_DEVSUBTYPE :: enum BYTE {
+ UNKNOWN = 0x00,
+ GAMEPAD = 0x01,
+ WHEEL = 0x02,
+ ARCADE_STICK = 0x03,
+ FLIGHT_STICK = 0x04,
+ DANCE_PAD = 0x05,
+ GUITAR = 0x06,
+ GUITAR_ALTERNATE = 0x07,
+ DRUM_KIT = 0x08,
+ GUITAR_BASS = 0x0B,
+ ARCADE_PAD = 0x13,
+}
+
+// Flags for XINPUT_CAPABILITIES
+// Correspond to log2(XINPUT_CAPS_...)
+XINPUT_CAP :: enum WORD {
+ FFB_SUPPORTED = 0,
+ WIRELESS = 1,
+ VOICE_SUPPORTED = 2,
+ PMD_SUPPORTED = 3,
+ NO_NAVIGATION = 4,
+}
+XINPUT_CAPS :: distinct bit_set[XINPUT_CAP;WORD]
+
+// Constants for gamepad buttons
+// Correspond to log2(XINPUT_GAMEPAD_...)
+XINPUT_GAMEPAD_BUTTON_BIT :: enum WORD {
+ DPAD_UP = 0,
+ DPAD_DOWN = 1,
+ DPAD_LEFT = 2,
+ DPAD_RIGHT = 3,
+ START = 4,
+ BACK = 5,
+ LEFT_THUMB = 6,
+ RIGHT_THUMB = 7,
+ LEFT_SHOULDER = 8,
+ RIGHT_SHOULDER = 9,
+ A = 12,
+ B = 13,
+ X = 14,
+ Y = 15,
+}
+XINPUT_GAMEPAD_BUTTON :: distinct bit_set[XINPUT_GAMEPAD_BUTTON_BIT;WORD]
+
+// Gamepad thresholds
+XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE: SHORT : 7849
+XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE: SHORT : 8689
+XINPUT_GAMEPAD_TRIGGER_THRESHOLD: SHORT : 30
+
+// Flags to pass to XInputGetCapabilities
+// Corresponds to log2(XINPUT_FLAG_...)
+XINPUT_FLAG_BIT :: enum WORD {
+ GAMEPAD = 0,
+}
+XINPUT_FLAG :: distinct bit_set[XINPUT_FLAG_BIT;DWORD]
+
+// Devices that support batteries
+// Corresponds to BATTERY_DEVTYPE_...
+BATTERY_DEVTYPE :: enum BYTE {
+ GAMEPAD = 0x00,
+ HEADSET = 0x01,
+}
+
+// Flags for battery status level
+// Correspond to BATTERY_TYPE_...
+BATTERY_TYPE :: enum BYTE {
+ DISCONNECTED = 0x00, // This device is not connected
+ WIRED = 0x01, // Wired device, no battery
+ ALKALINE = 0x02, // Alkaline battery source
+ NIMH = 0x03, // Nickel Metal Hydride battery source
+ UNKNOWN = 0xFF, // Cannot determine the battery type
+}
+
+// These are only valid for wireless, connected devices, with known battery types
+// The amount of use time remaining depends on the type of device.
+// Correspond to BATTERY_LEVEL_...
+BATTERY_LEVEL :: enum BYTE {
+ EMPTY = 0x00,
+ LOW = 0x01,
+ MEDIUM = 0x02,
+ FULL = 0x03,
+}
+
+// User index definitions
+
+// Index of the gamer associated with the device
+XUSER :: enum DWORD {
+ One = 0,
+ Two = 1,
+ Three = 2,
+ Four = 3,
+ Any = 0x000000FF, // Can be only used with XInputGetKeystroke
+}
+
+XUSER_MAX_COUNT :: 4
+
+// Codes returned for the gamepad keystroke
+// Corresponds to VK_PAD_...
+VK_PAD :: enum WORD {
+ A = 0x5800,
+ B = 0x5801,
+ X = 0x5802,
+ Y = 0x5803,
+ RSHOULDER = 0x5804,
+ LSHOULDER = 0x5805,
+ LTRIGGER = 0x5806,
+ RTRIGGER = 0x5807,
+ DPAD_UP = 0x5810,
+ DPAD_DOWN = 0x5811,
+ DPAD_LEFT = 0x5812,
+ DPAD_RIGHT = 0x5813,
+ START = 0x5814,
+ BACK = 0x5815,
+ LTHUMB_PRESS = 0x5816,
+ RTHUMB_PRESS = 0x5817,
+ LTHUMB_UP = 0x5820,
+ LTHUMB_DOWN = 0x5821,
+ LTHUMB_RIGHT = 0x5822,
+ LTHUMB_LEFT = 0x5823,
+ LTHUMB_UPLEFT = 0x5824,
+ LTHUMB_UPRIGHT = 0x5825,
+ LTHUMB_DOWNRIGHT = 0x5826,
+ LTHUMB_DOWNLEFT = 0x5827,
+ RTHUMB_UP = 0x5830,
+ RTHUMB_DOWN = 0x5831,
+ RTHUMB_RIGHT = 0x5832,
+ RTHUMB_LEFT = 0x5833,
+ RTHUMB_UPLEFT = 0x5834,
+ RTHUMB_UPRIGHT = 0x5835,
+ RTHUMB_DOWNRIGHT = 0x5836,
+ RTHUMB_DOWNLEFT = 0x5837,
+}
+
+// Flags used in XINPUT_KEYSTROKE
+// Correspond to log2(XINPUT_KEYSTROKE_...)
+XINPUT_KEYSTROKE_BIT :: enum WORD {
+ KEYDOWN = 0,
+ KEYUP = 1,
+ REPEAT = 2,
+}
+XINPUT_KEYSTROKES :: distinct bit_set[XINPUT_KEYSTROKE_BIT;WORD]
+
+// Structures used by XInput APIs
+XINPUT_GAMEPAD :: struct {
+ wButtons: XINPUT_GAMEPAD_BUTTON,
+ bLeftTrigger: BYTE,
+ bRightTrigger: BYTE,
+ sThumbLX: SHORT,
+ sThumbLY: SHORT,
+ sThumbRX: SHORT,
+ sThumbRY: SHORT,
+}
+
+XINPUT_STATE :: struct {
+ dwPacketNumber: DWORD,
+ Gamepad: XINPUT_GAMEPAD,
+}
+
+XINPUT_VIBRATION :: struct {
+ wLeftMotorSpeed: WORD,
+ wRightMotorSpeed: WORD,
+}
+
+XINPUT_CAPABILITIES :: struct {
+ Type: XINPUT_DEVTYPE,
+ SubType: XINPUT_DEVSUBTYPE,
+ Flags: XINPUT_CAPS,
+ Gamepad: XINPUT_GAMEPAD,
+ Vibration: XINPUT_VIBRATION,
+}
+
+XINPUT_BATTERY_INFORMATION :: struct {
+ BatteryType: BATTERY_TYPE,
+ BatteryLevel: BATTERY_LEVEL,
+}
+
+XINPUT_KEYSTROKE :: struct {
+ VirtualKey: VK_PAD,
+ Unicode: WCHAR,
+ Flags: XINPUT_KEYSTROKES,
+ UserIndex: XUSER,
+ HidCode: BYTE,
+}
+
+// XInput APIs
+@(default_calling_convention = "system")
+foreign xinput {
+ XInputGetState :: proc(user: XUSER, pState: ^XINPUT_STATE) -> System_Error ---
+ XInputSetState :: proc(user: XUSER, pVibration: ^XINPUT_VIBRATION) -> System_Error ---
+ XInputGetCapabilities :: proc(user: XUSER, dwFlags: XINPUT_FLAG, pCapabilities: ^XINPUT_CAPABILITIES) -> System_Error ---
+ XInputEnable :: proc(enable: BOOL) ---
+ XInputGetAudioDeviceIds :: proc(user: XUSER, pRenderDeviceId: LPWSTR, pRenderCount: ^UINT, pCaptureDeviceId: LPWSTR, pCaptureCount: ^UINT) -> System_Error ---
+ XInputGetBatteryInformation :: proc(user: XUSER, devType: BATTERY_DEVTYPE, pBatteryInformation: ^XINPUT_BATTERY_INFORMATION) -> System_Error ---
+ XInputGetKeystroke :: proc(user: XUSER, dwReserved: DWORD, pKeystroke: ^XINPUT_KEYSTROKE) -> System_Error ---
+ XInputGetDSoundAudioDeviceGuids :: proc(user: XUSER, pDSoundRenderGuid: ^GUID, pDSoundCaptureGuid: ^GUID) -> System_Error ---
+}
diff --git a/core/testing/runner.odin b/core/testing/runner.odin
index 6b9d610ed..83a5ac4e7 100644
--- a/core/testing/runner.odin
+++ b/core/testing/runner.odin
@@ -391,6 +391,7 @@ runner :: proc(internal_tests: []Internal_Test) -> bool {
fmt.assertf(alloc_error == nil, "Error allocating memory for task allocator #%i: %v", i, alloc_error)
when TRACKING_MEMORY {
mem.tracking_allocator_init(&task_memory_trackers[i], mem.rollback_stack_allocator(&task_allocators[i]))
+ task_memory_trackers[i].bad_free_callback = mem.tracking_allocator_bad_free_callback_add_to_array
}
}
diff --git a/core/testing/signal_handler_posix.odin b/core/testing/signal_handler_posix.odin
index 1bfcc875b..0efba27dc 100644
--- a/core/testing/signal_handler_posix.odin
+++ b/core/testing/signal_handler_posix.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
#+private
package testing
diff --git a/core/text/match/strlib.odin b/core/text/match/strlib.odin
index bfa696dcd..819f464c5 100644
--- a/core/text/match/strlib.odin
+++ b/core/text/match/strlib.odin
@@ -682,11 +682,14 @@ find_aux :: proc(
// iterative matching which returns the 0th/1st match
// rest has to be used from captures
+// assumes captures is zeroed on first iteration
+// resets captures to zero on last iteration
gmatch :: proc(
haystack: ^string,
pattern: string,
captures: ^[MAX_CAPTURES]Match,
) -> (res: string, ok: bool) {
+ haystack^ = haystack[captures[0].byte_end:]
if len(haystack) > 0 {
length, err := find_aux(haystack^, pattern, 0, false, captures)
@@ -695,10 +698,11 @@ gmatch :: proc(
first := length > 1 ? 1 : 0
cap := captures[first]
res = haystack[cap.byte_start:cap.byte_end]
- haystack^ = haystack[cap.byte_end:]
}
}
-
+ if !ok {
+ captures^ = {}
+ }
return
}
@@ -794,11 +798,14 @@ gsub_with :: proc(
gsub :: proc { gsub_builder, gsub_allocator }
// iterative find with zeroth capture only
+// assumes captures is zeroed on first iteration
+// resets captures to zero on last iteration
gfind :: proc(
haystack: ^string,
pattern: string,
captures: ^[MAX_CAPTURES]Match,
) -> (res: string, ok: bool) {
+ haystack^ = haystack[captures[0].byte_end:]
if len(haystack) > 0 {
length, err := find_aux(haystack^, pattern, 0, true, captures)
@@ -806,10 +813,11 @@ gfind :: proc(
ok = true
cap := captures[0]
res = haystack[cap.byte_start:cap.byte_end]
- haystack^ = haystack[cap.byte_end:]
}
}
-
+ if !ok {
+ captures^ = {}
+ }
return
}
diff --git a/core/text/regex/regex.odin b/core/text/regex/regex.odin
index 3dc26b5c6..c805740f7 100644
--- a/core/text/regex/regex.odin
+++ b/core/text/regex/regex.odin
@@ -8,6 +8,7 @@ package regex
Feoramund: Initial implementation.
*/
+import "base:runtime"
import "core:text/regex/common"
import "core:text/regex/compiler"
import "core:text/regex/optimizer"
@@ -27,6 +28,8 @@ Creation_Error :: enum {
Expected_Delimiter,
// An unknown letter was supplied to `create_by_user` after the last delimiter.
Unknown_Flag,
+ // An unsupported flag was supplied.
+ Unsupported_Flag,
}
Error :: union #shared_nil {
@@ -51,7 +54,7 @@ This struct corresponds to a set of string captures from a RegEx match.
such that `str[pos[0][0]:pos[0][1]] == groups[0]`.
*/
Capture :: struct {
- pos: [][2]int,
+ pos: [][2]int,
groups: []string,
}
@@ -59,11 +62,22 @@ Capture :: struct {
A compiled Regular Expression value, to be used with the `match_*` procedures.
*/
Regular_Expression :: struct {
- flags: Flags `fmt:"-"`,
+ flags: Flags `fmt:"-"`,
class_data: []virtual_machine.Rune_Class_Data `fmt:"-"`,
- program: []virtual_machine.Opcode `fmt:"-"`,
+ program: []virtual_machine.Opcode `fmt:"-"`,
}
+/*
+An iterator to repeatedly match a pattern against a string, to be used with `*_iterator` procedures.
+Note: Does not handle `.Multiline` properly.
+*/
+Match_Iterator :: struct {
+ regex: Regular_Expression,
+ capture: Capture,
+ vm: virtual_machine.Machine,
+ idx: int,
+ temp: runtime.Allocator,
+}
/*
Create a regular expression from a string pattern and a set of flags.
@@ -245,6 +259,45 @@ create_by_user :: proc(
return create(pattern[start:end], flags, permanent_allocator, temporary_allocator)
}
+/*
+Create a `Match_Iterator` using a string to search, a regular expression to match against it, and a set of flags.
+
+*Allocates Using Provided Allocators*
+
+Inputs:
+- str: The string to iterate over.
+- pattern: The pattern to match.
+- flags: A `bit_set` of RegEx flags.
+- permanent_allocator: The allocator to use for the compiled regular expression. (default: context.allocator)
+- temporary_allocator: The allocator to use for the intermediate compilation and iteration stages. (default: context.temp_allocator)
+
+Returns:
+- result: The `Match_Iterator`.
+- err: An error, if one occurred.
+*/
+create_iterator :: proc(
+ str: string,
+ pattern: string,
+ flags: Flags = {},
+ permanent_allocator := context.allocator,
+ temporary_allocator := context.temp_allocator,
+) -> (result: Match_Iterator, err: Error) {
+ flags := flags
+ flags += {.Global} // We're iterating over a string, so the next match could start anywhere
+
+ if .Multiline in flags {
+ return {}, .Unsupported_Flag
+ }
+
+ result.regex = create(pattern, flags, permanent_allocator, temporary_allocator) or_return
+ result.capture = preallocate_capture()
+ result.temp = temporary_allocator
+ result.vm = virtual_machine.create(result.regex.program, str)
+ result.vm.class_data = result.regex.class_data
+
+ return
+}
+
/*
Match a regular expression against a string and allocate the results into the
returned `capture` structure.
@@ -381,14 +434,78 @@ match_with_preallocated_capture :: proc(
capture.pos[n] = {a, b}
n += 1
}
+ num_groups = n
}
return
}
+/*
+Iterate over a `Match_Iterator` and return successive captures.
+Note: Does not handle `.Multiline` properly.
+
+Inputs:
+- it: Pointer to the `Match_Iterator` to iterate over.
+
+Returns:
+- result: `Capture` for this iteration.
+- ok: A bool indicating if there was a match, stopping the iteration on `false`.
+*/
+match_iterator :: proc(it: ^Match_Iterator) -> (result: Capture, index: int, ok: bool) {
+ assert(len(it.capture.groups) >= common.MAX_CAPTURE_GROUPS,
+ "Pre-allocated RegEx capture `groups` must be at least 10 elements long.")
+ assert(len(it.capture.pos) >= common.MAX_CAPTURE_GROUPS,
+ "Pre-allocated RegEx capture `pos` must be at least 10 elements long.")
+
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
+
+ saved: ^[2 * common.MAX_CAPTURE_GROUPS]int
+ {
+ context.allocator = it.temp
+ if .Unicode in it.regex.flags {
+ saved, ok = virtual_machine.run(&it.vm, true)
+ } else {
+ saved, ok = virtual_machine.run(&it.vm, false)
+ }
+ }
+
+ str := string(it.vm.memory)
+ num_groups: int
+
+ if saved != nil {
+ n := 0
+
+ #no_bounds_check for i := 0; i < len(saved); i += 2 {
+ a, b := saved[i], saved[i + 1]
+ if a == -1 || b == -1 {
+ continue
+ }
+
+ it.capture.groups[n] = str[a:b]
+ it.capture.pos[n] = {a, b}
+ n += 1
+ }
+ num_groups = n
+ }
+
+ defer if ok {
+ it.idx += 1
+ }
+
+ if num_groups > 0 {
+ result = {it.capture.pos[:num_groups], it.capture.groups[:num_groups]}
+ }
+ return result, it.idx, ok
+}
+
match :: proc {
match_and_allocate_capture,
match_with_preallocated_capture,
+ match_iterator,
+}
+
+reset :: proc(it: ^Match_Iterator) {
+ it.idx = 0
}
/*
@@ -405,7 +522,7 @@ Returns:
@require_results
preallocate_capture :: proc(allocator := context.allocator) -> (result: Capture) {
context.allocator = allocator
- result.pos = make([][2]int, common.MAX_CAPTURE_GROUPS)
+ result.pos = make([][2]int, common.MAX_CAPTURE_GROUPS)
result.groups = make([]string, common.MAX_CAPTURE_GROUPS)
return
}
@@ -435,7 +552,7 @@ Free all data allocated by the `match_and_allocate_capture` procedure.
*Frees Using Provided Allocator*
Inputs:
-- capture: A Capture.
+- capture: A `Capture`.
- allocator: (default: context.allocator)
*/
destroy_capture :: proc(capture: Capture, allocator := context.allocator) {
@@ -444,7 +561,24 @@ destroy_capture :: proc(capture: Capture, allocator := context.allocator) {
delete(capture.pos)
}
+/*
+Free all data allocated by the `create_iterator` procedure.
+
+*Frees Using Provided Allocator*
+
+Inputs:
+- it: A `Match_Iterator`
+- allocator: (default: context.allocator)
+*/
+destroy_iterator :: proc(it: Match_Iterator, allocator := context.allocator) {
+ context.allocator = allocator
+ destroy(it.regex)
+ destroy(it.capture)
+ virtual_machine.destroy(it.vm)
+}
+
destroy :: proc {
destroy_regex,
destroy_capture,
+ destroy_iterator,
}
diff --git a/core/text/regex/virtual_machine/virtual_machine.odin b/core/text/regex/virtual_machine/virtual_machine.odin
index a4fca6c4d..ab1dfbec1 100644
--- a/core/text/regex/virtual_machine/virtual_machine.odin
+++ b/core/text/regex/virtual_machine/virtual_machine.odin
@@ -627,8 +627,9 @@ opcode_count :: proc(code: Program) -> (opcodes: int) {
return
}
-create :: proc(code: Program, str: string) -> (vm: Machine) {
+create :: proc(code: Program, str: string, allocator := context.allocator) -> (vm: Machine) {
assert(len(code) > 0, "RegEx VM has no instructions.")
+ context.allocator = allocator
vm.memory = str
vm.code = code
@@ -644,3 +645,11 @@ create :: proc(code: Program, str: string) -> (vm: Machine) {
return
}
+
+destroy :: proc(vm: Machine, allocator := context.allocator) {
+ context.allocator = allocator
+
+ delete(vm.busy_map)
+ free(vm.threads)
+ free(vm.next_threads)
+}
\ No newline at end of file
diff --git a/core/thread/thread.odin b/core/thread/thread.odin
index c1cbceb42..c7fd082ac 100644
--- a/core/thread/thread.odin
+++ b/core/thread/thread.odin
@@ -264,12 +264,14 @@ flag is specified.
is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
in order to free the resources associated with the temporary allocations.
*/
-create_and_start :: proc(fn: proc(), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread {
+create_and_start :: proc(fn: proc(), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> (t: ^Thread) {
thread_proc :: proc(t: ^Thread) {
fn := cast(proc())t.data
fn()
}
- t := create(thread_proc, priority)
+ if t = create(thread_proc, priority); t == nil {
+ return
+ }
t.data = rawptr(fn)
if self_cleanup {
intrinsics.atomic_or(&t.flags, {.Self_Cleanup})
@@ -295,14 +297,16 @@ flag is specified.
is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
in order to free the resources associated with the temporary allocations.
*/
-create_and_start_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread {
+create_and_start_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> (t: ^Thread) {
thread_proc :: proc(t: ^Thread) {
fn := cast(proc(rawptr))t.data
assert(t.user_index >= 1)
data := t.user_args[0]
fn(data)
}
- t := create(thread_proc, priority)
+ if t = create(thread_proc, priority); t == nil {
+ return
+ }
t.data = rawptr(fn)
t.user_index = 1
t.user_args[0] = data
@@ -330,7 +334,7 @@ flag is specified.
is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
in order to free the resources associated with the temporary allocations.
*/
-create_and_start_with_poly_data :: proc(data: $T, fn: proc(data: T), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread
+create_and_start_with_poly_data :: proc(data: $T, fn: proc(data: T), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> (t: ^Thread)
where size_of(T) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
thread_proc :: proc(t: ^Thread) {
fn := cast(proc(T))t.data
@@ -338,7 +342,9 @@ create_and_start_with_poly_data :: proc(data: $T, fn: proc(data: T), init_contex
data := (^T)(&t.user_args[0])^
fn(data)
}
- t := create(thread_proc, priority)
+ if t = create(thread_proc, priority); t == nil {
+ return
+ }
t.data = rawptr(fn)
t.user_index = 1
@@ -371,7 +377,7 @@ flag is specified.
is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
in order to free the resources associated with the temporary allocations.
*/
-create_and_start_with_poly_data2 :: proc(arg1: $T1, arg2: $T2, fn: proc(T1, T2), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread
+create_and_start_with_poly_data2 :: proc(arg1: $T1, arg2: $T2, fn: proc(T1, T2), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> (t: ^Thread)
where size_of(T1) + size_of(T2) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
thread_proc :: proc(t: ^Thread) {
fn := cast(proc(T1, T2))t.data
@@ -383,7 +389,9 @@ create_and_start_with_poly_data2 :: proc(arg1: $T1, arg2: $T2, fn: proc(T1, T2),
fn(arg1, arg2)
}
- t := create(thread_proc, priority)
+ if t = create(thread_proc, priority); t == nil {
+ return
+ }
t.data = rawptr(fn)
t.user_index = 2
@@ -418,7 +426,7 @@ flag is specified.
is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
in order to free the resources associated with the temporary allocations.
*/
-create_and_start_with_poly_data3 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, fn: proc(arg1: T1, arg2: T2, arg3: T3), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread
+create_and_start_with_poly_data3 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, fn: proc(arg1: T1, arg2: T2, arg3: T3), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> (t: ^Thread)
where size_of(T1) + size_of(T2) + size_of(T3) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
thread_proc :: proc(t: ^Thread) {
fn := cast(proc(T1, T2, T3))t.data
@@ -431,7 +439,9 @@ create_and_start_with_poly_data3 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, fn: pr
fn(arg1, arg2, arg3)
}
- t := create(thread_proc, priority)
+ if t = create(thread_proc, priority); t == nil {
+ return
+ }
t.data = rawptr(fn)
t.user_index = 3
@@ -467,7 +477,7 @@ flag is specified.
is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
in order to free the resources associated with the temporary allocations.
*/
-create_and_start_with_poly_data4 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, arg4: $T4, fn: proc(arg1: T1, arg2: T2, arg3: T3, arg4: T4), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread
+create_and_start_with_poly_data4 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, arg4: $T4, fn: proc(arg1: T1, arg2: T2, arg3: T3, arg4: T4), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> (t: ^Thread)
where size_of(T1) + size_of(T2) + size_of(T3) + size_of(T4) <= size_of(rawptr) * MAX_USER_ARGUMENTS {
thread_proc :: proc(t: ^Thread) {
fn := cast(proc(T1, T2, T3, T4))t.data
@@ -481,7 +491,9 @@ create_and_start_with_poly_data4 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, arg4:
fn(arg1, arg2, arg3, arg4)
}
- t := create(thread_proc, priority)
+ if t = create(thread_proc, priority); t == nil {
+ return
+ }
t.data = rawptr(fn)
t.user_index = 4
@@ -513,8 +525,10 @@ _select_context_for_thread :: proc(init_context: Maybe(runtime.Context)) -> runt
Ensure that the temp allocator is thread-safe when the user provides a specific initial context to use.
Without this, the thread will use the same temp allocator state as the parent thread, and thus, bork it up.
*/
- if ctx.temp_allocator.procedure == runtime.default_temp_allocator_proc {
- ctx.temp_allocator.data = &runtime.global_default_temp_allocator_data
+ when !ODIN_DEFAULT_TO_NIL_ALLOCATOR {
+ if ctx.temp_allocator.procedure == runtime.default_temp_allocator_proc {
+ ctx.temp_allocator.data = &runtime.global_default_temp_allocator_data
+ }
}
return ctx
}
@@ -529,4 +543,4 @@ _maybe_destroy_default_temp_allocator :: proc(init_context: Maybe(runtime.Contex
if context.temp_allocator.procedure == runtime.default_temp_allocator_proc {
runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data)
}
-}
+}
\ No newline at end of file
diff --git a/core/thread/thread_pool.odin b/core/thread/thread_pool.odin
index d9166b450..59bf90620 100644
--- a/core/thread/thread_pool.odin
+++ b/core/thread/thread_pool.odin
@@ -9,6 +9,7 @@ package thread
import "base:intrinsics"
import "core:sync"
import "core:mem"
+import "core:container/queue"
Task_Proc :: #type proc(task: Task)
@@ -40,7 +41,7 @@ Pool :: struct {
threads: []^Thread,
- tasks: [dynamic]Task,
+ tasks: queue.Queue(Task),
tasks_done: [dynamic]Task,
}
@@ -69,13 +70,13 @@ pool_thread_runner :: proc(t: ^Thread) {
}
// Once initialized, the pool's memory address is not allowed to change until
-// it is destroyed.
+// it is destroyed.
//
// The thread pool requires an allocator which it either owns, or which is thread safe.
pool_init :: proc(pool: ^Pool, allocator: mem.Allocator, thread_count: int) {
context.allocator = allocator
pool.allocator = allocator
- pool.tasks = make([dynamic]Task)
+ queue.init(&pool.tasks)
pool.tasks_done = make([dynamic]Task)
pool.threads = make([]^Thread, max(thread_count, 1))
@@ -92,7 +93,7 @@ pool_init :: proc(pool: ^Pool, allocator: mem.Allocator, thread_count: int) {
}
pool_destroy :: proc(pool: ^Pool) {
- delete(pool.tasks)
+ queue.destroy(&pool.tasks)
delete(pool.tasks_done)
for &t in pool.threads {
@@ -140,11 +141,11 @@ pool_join :: proc(pool: ^Pool) {
// the thread pool. You can even add tasks from inside other tasks.
//
// Each task also needs an allocator which it either owns, or which is thread
-// safe.
+// safe.
pool_add_task :: proc(pool: ^Pool, allocator: mem.Allocator, procedure: Task_Proc, data: rawptr, user_index: int = 0) {
sync.guard(&pool.mutex)
- append(&pool.tasks, Task{
+ queue.push_back(&pool.tasks, Task{
procedure = procedure,
data = data,
user_index = user_index,
@@ -288,10 +289,10 @@ pool_is_empty :: #force_inline proc(pool: ^Pool) -> bool {
pool_pop_waiting :: proc(pool: ^Pool) -> (task: Task, got_task: bool) {
sync.guard(&pool.mutex)
- if len(pool.tasks) != 0 {
+ if queue.len(pool.tasks) != 0 {
intrinsics.atomic_sub(&pool.num_waiting, 1)
intrinsics.atomic_add(&pool.num_in_processing, 1)
- task = pop_front(&pool.tasks)
+ task = queue.pop_front(&pool.tasks)
got_task = true
}
diff --git a/core/time/datetime/constants.odin b/core/time/datetime/constants.odin
index e24709e49..b4fb4a15f 100644
--- a/core/time/datetime/constants.odin
+++ b/core/time/datetime/constants.odin
@@ -5,13 +5,11 @@ Type representing a mononotic day number corresponding to a date.
Ordinal 1 = Midnight Monday, January 1, 1 A.D. (Gregorian)
| Midnight Monday, January 3, 1 A.D. (Julian)
+
+Every other ordinal counts days forwards, starting from the above date.
*/
Ordinal :: i64
-/*
-*/
-EPOCH :: Ordinal(1)
-
/*
Minimum valid value for date.
diff --git a/core/time/datetime/datetime.odin b/core/time/datetime/datetime.odin
index 2cd90b0e7..f52bff9b6 100644
--- a/core/time/datetime/datetime.odin
+++ b/core/time/datetime/datetime.odin
@@ -98,7 +98,7 @@ This procedure takes the value of an ordinal and returns the day of week for
that ordinal.
*/
day_of_week :: proc "contextless" (ordinal: Ordinal) -> (day: Weekday) {
- return Weekday((ordinal - EPOCH + 1) %% 7)
+ return Weekday(ordinal %% 7)
}
/*
@@ -349,8 +349,7 @@ the result is unspecified.
unsafe_date_to_ordinal :: proc "contextless" (date: Date) -> (ordinal: Ordinal) {
year_minus_one := date.year - 1
- // Day before epoch
- ordinal = EPOCH - 1
+ ordinal = 0
// Add non-leap days
ordinal += 365 * year_minus_one
@@ -382,17 +381,17 @@ This procedure returns the year and the day of the year of a given ordinal.
Of the ordinal is outside of its valid range, the result is unspecified.
*/
unsafe_ordinal_to_year :: proc "contextless" (ordinal: Ordinal) -> (year: i64, day_ordinal: i64) {
- // Days after epoch
- d0 := ordinal - EPOCH
+ // Correct for leap year cycle starting at day 1.
+ d0 := ordinal - 1
// Number of 400-year cycles and remainder
- n400, d1 := divmod(d0, 146097)
+ n400, d1 := divmod(d0, 365*400 + 100 - 3)
// Number of 100-year cycles and remainder
- n100, d2 := divmod(d1, 36524)
+ n100, d2 := divmod(d1, 365*100 + 25 - 1)
// Number of 4-year cycles and remainder
- n4, d3 := divmod(d2, 1461)
+ n4, d3 := divmod(d2, 365*4 + 1)
// Number of remaining days
n1, d4 := divmod(d3, 365)
diff --git a/core/time/perf.odin b/core/time/perf.odin
index 784d7acd6..265a20edf 100644
--- a/core/time/perf.odin
+++ b/core/time/perf.odin
@@ -17,6 +17,13 @@ tick_now :: proc "contextless" () -> Tick {
return _tick_now()
}
+/*
+Add duration to a tick.
+*/
+tick_add :: proc "contextless" (t: Tick, d: Duration) -> Tick {
+ return Tick{t._nsec + i64(d)}
+}
+
/*
Obtain the difference between ticks.
*/
diff --git a/core/time/time_other.odin b/core/time/time_other.odin
index d89bcbd42..3f0f06e9b 100644
--- a/core/time/time_other.odin
+++ b/core/time/time_other.odin
@@ -9,6 +9,7 @@
#+build !wasi
#+build !windows
#+build !orca
+#+build !haiku
package time
_IS_SUPPORTED :: false
diff --git a/core/time/time_unix.odin b/core/time/time_unix.odin
index 61c4e91d3..c384d6d07 100644
--- a/core/time/time_unix.odin
+++ b/core/time/time_unix.odin
@@ -1,5 +1,5 @@
#+private
-#+build darwin, freebsd, openbsd, netbsd
+#+build darwin, freebsd, openbsd, netbsd, haiku
package time
import "core:sys/posix"
diff --git a/core/time/timezone/tz_windows.odin b/core/time/timezone/tz_windows.odin
index 238c4c933..8dc5f533c 100644
--- a/core/time/timezone/tz_windows.odin
+++ b/core/time/timezone/tz_windows.odin
@@ -11,146 +11,147 @@ TZ_Abbrev :: struct {
dst: string,
}
-tz_abbrevs := map[string]TZ_Abbrev {
- "Egypt Standard Time" = {"EET", "EEST"}, // Africa/Cairo
- "Morocco Standard Time" = {"+00", "+01"}, // Africa/Casablanca
- "South Africa Standard Time" = {"SAST", "SAST"}, // Africa/Johannesburg
- "South Sudan Standard Time" = {"CAT", "CAT"}, // Africa/Juba
- "Sudan Standard Time" = {"CAT", "CAT"}, // Africa/Khartoum
- "W. Central Africa Standard Time" = {"WAT", "WAT"}, // Africa/Lagos
- "E. Africa Standard Time" = {"EAT", "EAT"}, // Africa/Nairobi
- "Sao Tome Standard Time" = {"GMT", "GMT"}, // Africa/Sao_Tome
- "Libya Standard Time" = {"EET", "EET"}, // Africa/Tripoli
- "Namibia Standard Time" = {"CAT", "CAT"}, // Africa/Windhoek
- "Aleutian Standard Time" = {"HST", "HDT"}, // America/Adak
- "Alaskan Standard Time" = {"AKST", "AKDT"}, // America/Anchorage
- "Tocantins Standard Time" = {"-03", "-03"}, // America/Araguaina
- "Paraguay Standard Time" = {"-04", "-03"}, // America/Asuncion
- "Bahia Standard Time" = {"-03", "-03"}, // America/Bahia
- "SA Pacific Standard Time" = {"-05", "-05"}, // America/Bogota
- "Argentina Standard Time" = {"-03", "-03"}, // America/Buenos_Aires
- "Eastern Standard Time (Mexico)" = {"EST", "EST"}, // America/Cancun
- "Venezuela Standard Time" = {"-04", "-04"}, // America/Caracas
- "SA Eastern Standard Time" = {"-03", "-03"}, // America/Cayenne
- "Central Standard Time" = {"CST", "CDT"}, // America/Chicago
- "Central Brazilian Standard Time" = {"-04", "-04"}, // America/Cuiaba
- "Mountain Standard Time" = {"MST", "MDT"}, // America/Denver
- "Greenland Standard Time" = {"-03", "-02"}, // America/Godthab
- "Turks And Caicos Standard Time" = {"EST", "EDT"}, // America/Grand_Turk
- "Central America Standard Time" = {"CST", "CST"}, // America/Guatemala
- "Atlantic Standard Time" = {"AST", "ADT"}, // America/Halifax
- "Cuba Standard Time" = {"CST", "CDT"}, // America/Havana
- "US Eastern Standard Time" = {"EST", "EDT"}, // America/Indianapolis
- "SA Western Standard Time" = {"-04", "-04"}, // America/La_Paz
- "Pacific Standard Time" = {"PST", "PDT"}, // America/Los_Angeles
- "Mountain Standard Time (Mexico)" = {"MST", "MST"}, // America/Mazatlan
- "Central Standard Time (Mexico)" = {"CST", "CST"}, // America/Mexico_City
- "Saint Pierre Standard Time" = {"-03", "-02"}, // America/Miquelon
- "Montevideo Standard Time" = {"-03", "-03"}, // America/Montevideo
- "Eastern Standard Time" = {"EST", "EDT"}, // America/New_York
- "US Mountain Standard Time" = {"MST", "MST"}, // America/Phoenix
- "Haiti Standard Time" = {"EST", "EDT"}, // America/Port-au-Prince
- "Magallanes Standard Time" = {"-03", "-03"}, // America/Punta_Arenas
- "Canada Central Standard Time" = {"CST", "CST"}, // America/Regina
- "Pacific SA Standard Time" = {"-04", "-03"}, // America/Santiago
- "E. South America Standard Time" = {"-03", "-03"}, // America/Sao_Paulo
- "Newfoundland Standard Time" = {"NST", "NDT"}, // America/St_Johns
- "Pacific Standard Time (Mexico)" = {"PST", "PDT"}, // America/Tijuana
- "Yukon Standard Time" = {"MST", "MST"}, // America/Whitehorse
- "Central Asia Standard Time" = {"+06", "+06"}, // Asia/Almaty
- "Jordan Standard Time" = {"+03", "+03"}, // Asia/Amman
- "Arabic Standard Time" = {"+03", "+03"}, // Asia/Baghdad
- "Azerbaijan Standard Time" = {"+04", "+04"}, // Asia/Baku
- "SE Asia Standard Time" = {"+07", "+07"}, // Asia/Bangkok
- "Altai Standard Time" = {"+07", "+07"}, // Asia/Barnaul
- "Middle East Standard Time" = {"EET", "EEST"}, // Asia/Beirut
- "India Standard Time" = {"IST", "IST"}, // Asia/Calcutta
- "Transbaikal Standard Time" = {"+09", "+09"}, // Asia/Chita
- "Sri Lanka Standard Time" = {"+0530", "+0530"}, // Asia/Colombo
- "Syria Standard Time" = {"+03", "+03"}, // Asia/Damascus
- "Bangladesh Standard Time" = {"+06", "+06"}, // Asia/Dhaka
- "Arabian Standard Time" = {"+04", "+04"}, // Asia/Dubai
- "West Bank Standard Time" = {"EET", "EEST"}, // Asia/Hebron
- "W. Mongolia Standard Time" = {"+07", "+07"}, // Asia/Hovd
- "North Asia East Standard Time" = {"+08", "+08"}, // Asia/Irkutsk
- "Israel Standard Time" = {"IST", "IDT"}, // Asia/Jerusalem
- "Afghanistan Standard Time" = {"+0430", "+0430"}, // Asia/Kabul
- "Russia Time Zone 11" = {"+12", "+12"}, // Asia/Kamchatka
- "Pakistan Standard Time" = {"PKT", "PKT"}, // Asia/Karachi
- "Nepal Standard Time" = {"+0545", "+0545"}, // Asia/Katmandu
- "North Asia Standard Time" = {"+07", "+07"}, // Asia/Krasnoyarsk
- "Magadan Standard Time" = {"+11", "+11"}, // Asia/Magadan
- "N. Central Asia Standard Time" = {"+07", "+07"}, // Asia/Novosibirsk
- "Omsk Standard Time" = {"+06", "+06"}, // Asia/Omsk
- "North Korea Standard Time" = {"KST", "KST"}, // Asia/Pyongyang
- "Qyzylorda Standard Time" = {"+05", "+05"}, // Asia/Qyzylorda
- "Myanmar Standard Time" = {"+0630", "+0630"}, // Asia/Rangoon
- "Arab Standard Time" = {"+03", "+03"}, // Asia/Riyadh
- "Sakhalin Standard Time" = {"+11", "+11"}, // Asia/Sakhalin
- "Korea Standard Time" = {"KST", "KST"}, // Asia/Seoul
- "China Standard Time" = {"CST", "CST"}, // Asia/Shanghai
- "Singapore Standard Time" = {"+08", "+08"}, // Asia/Singapore
- "Russia Time Zone 10" = {"+11", "+11"}, // Asia/Srednekolymsk
- "Taipei Standard Time" = {"CST", "CST"}, // Asia/Taipei
- "West Asia Standard Time" = {"+05", "+05"}, // Asia/Tashkent
- "Georgian Standard Time" = {"+04", "+04"}, // Asia/Tbilisi
- "Iran Standard Time" = {"+0330", "+0330"}, // Asia/Tehran
- "Tokyo Standard Time" = {"JST", "JST"}, // Asia/Tokyo
- "Tomsk Standard Time" = {"+07", "+07"}, // Asia/Tomsk
- "Ulaanbaatar Standard Time" = {"+08", "+08"}, // Asia/Ulaanbaatar
- "Vladivostok Standard Time" = {"+10", "+10"}, // Asia/Vladivostok
- "Yakutsk Standard Time" = {"+09", "+09"}, // Asia/Yakutsk
- "Ekaterinburg Standard Time" = {"+05", "+05"}, // Asia/Yekaterinburg
- "Caucasus Standard Time" = {"+04", "+04"}, // Asia/Yerevan
- "Azores Standard Time" = {"-01", "+00"}, // Atlantic/Azores
- "Cape Verde Standard Time" = {"-01", "-01"}, // Atlantic/Cape_Verde
- "Greenwich Standard Time" = {"GMT", "GMT"}, // Atlantic/Reykjavik
- "Cen. Australia Standard Time" = {"ACST", "ACDT"}, // Australia/Adelaide
- "E. Australia Standard Time" = {"AEST", "AEST"}, // Australia/Brisbane
- "AUS Central Standard Time" = {"ACST", "ACST"}, // Australia/Darwin
- "Aus Central W. Standard Time" = {"+0845", "+0845"}, // Australia/Eucla
- "Tasmania Standard Time" = {"AEST", "AEDT"}, // Australia/Hobart
- "Lord Howe Standard Time" = {"+1030", "+11"}, // Australia/Lord_Howe
- "W. Australia Standard Time" = {"AWST", "AWST"}, // Australia/Perth
- "AUS Eastern Standard Time" = {"AEST", "AEDT"}, // Australia/Sydney
- "UTC-11" = {"-11", "-11"}, // Etc/GMT+11
- "Dateline Standard Time" = {"-12", "-12"}, // Etc/GMT+12
- "UTC-02" = {"-02", "-02"}, // Etc/GMT+2
- "UTC-08" = {"-08", "-08"}, // Etc/GMT+8
- "UTC-09" = {"-09", "-09"}, // Etc/GMT+9
- "UTC+12" = {"+12", "+12"}, // Etc/GMT-12
- "UTC+13" = {"+13", "+13"}, // Etc/GMT-13
- "UTC" = {"UTC", "UTC"}, // Etc/UTC
- "Astrakhan Standard Time" = {"+04", "+04"}, // Europe/Astrakhan
- "W. Europe Standard Time" = {"CET", "CEST"}, // Europe/Berlin
- "GTB Standard Time" = {"EET", "EEST"}, // Europe/Bucharest
- "Central Europe Standard Time" = {"CET", "CEST"}, // Europe/Budapest
- "E. Europe Standard Time" = {"EET", "EEST"}, // Europe/Chisinau
- "Turkey Standard Time" = {"+03", "+03"}, // Europe/Istanbul
- "Kaliningrad Standard Time" = {"EET", "EET"}, // Europe/Kaliningrad
- "FLE Standard Time" = {"EET", "EEST"}, // Europe/Kiev
- "GMT Standard Time" = {"GMT", "BST"}, // Europe/London
- "Belarus Standard Time" = {"+03", "+03"}, // Europe/Minsk
- "Russian Standard Time" = {"MSK", "MSK"}, // Europe/Moscow
- "Romance Standard Time" = {"CET", "CEST"}, // Europe/Paris
- "Russia Time Zone 3" = {"+04", "+04"}, // Europe/Samara
- "Saratov Standard Time" = {"+04", "+04"}, // Europe/Saratov
- "Volgograd Standard Time" = {"MSK", "MSK"}, // Europe/Volgograd
- "Central European Standard Time" = {"CET", "CEST"}, // Europe/Warsaw
- "Mauritius Standard Time" = {"+04", "+04"}, // Indian/Mauritius
- "Samoa Standard Time" = {"+13", "+13"}, // Pacific/Apia
- "New Zealand Standard Time" = {"NZST", "NZDT"}, // Pacific/Auckland
- "Bougainville Standard Time" = {"+11", "+11"}, // Pacific/Bougainville
- "Chatham Islands Standard Time" = {"+1245", "+1345"}, // Pacific/Chatham
- "Easter Island Standard Time" = {"-06", "-05"}, // Pacific/Easter
- "Fiji Standard Time" = {"+12", "+12"}, // Pacific/Fiji
- "Central Pacific Standard Time" = {"+11", "+11"}, // Pacific/Guadalcanal
- "Hawaiian Standard Time" = {"HST", "HST"}, // Pacific/Honolulu
- "Line Islands Standard Time" = {"+14", "+14"}, // Pacific/Kiritimati
- "Marquesas Standard Time" = {"-0930", "-0930"}, // Pacific/Marquesas
- "Norfolk Standard Time" = {"+11", "+12"}, // Pacific/Norfolk
- "West Pacific Standard Time" = {"+10", "+10"}, // Pacific/Port_Moresby
- "Tonga Standard Time" = {"+13", "+13"}, // Pacific/Tongatapu
+@(rodata)
+tz_abbrevs := [?]struct{key: string, value: TZ_Abbrev}{
+ {"Egypt Standard Time", {"EET", "EEST"}}, // Africa/Cairo
+ {"Morocco Standard Time", {"+00", "+01"}}, // Africa/Casablanca
+ {"South Africa Standard Time", {"SAST", "SAST"}}, // Africa/Johannesburg
+ {"South Sudan Standard Time", {"CAT", "CAT"}}, // Africa/Juba
+ {"Sudan Standard Time", {"CAT", "CAT"}}, // Africa/Khartoum
+ {"W. Central Africa Standard Time", {"WAT", "WAT"}}, // Africa/Lagos
+ {"E. Africa Standard Time", {"EAT", "EAT"}}, // Africa/Nairobi
+ {"Sao Tome Standard Time", {"GMT", "GMT"}}, // Africa/Sao_Tome
+ {"Libya Standard Time", {"EET", "EET"}}, // Africa/Tripoli
+ {"Namibia Standard Time", {"CAT", "CAT"}}, // Africa/Windhoek
+ {"Aleutian Standard Time", {"HST", "HDT"}}, // America/Adak
+ {"Alaskan Standard Time", {"AKST", "AKDT"}}, // America/Anchorage
+ {"Tocantins Standard Time", {"-03", "-03"}}, // America/Araguaina
+ {"Paraguay Standard Time", {"-04", "-03"}}, // America/Asuncion
+ {"Bahia Standard Time", {"-03", "-03"}}, // America/Bahia
+ {"SA Pacific Standard Time", {"-05", "-05"}}, // America/Bogota
+ {"Argentina Standard Time", {"-03", "-03"}}, // America/Buenos_Aires
+ {"Eastern Standard Time (Mexico)", {"EST", "EST"}}, // America/Cancun
+ {"Venezuela Standard Time", {"-04", "-04"}}, // America/Caracas
+ {"SA Eastern Standard Time", {"-03", "-03"}}, // America/Cayenne
+ {"Central Standard Time", {"CST", "CDT"}}, // America/Chicago
+ {"Central Brazilian Standard Time", {"-04", "-04"}}, // America/Cuiaba
+ {"Mountain Standard Time", {"MST", "MDT"}}, // America/Denver
+ {"Greenland Standard Time", {"-03", "-02"}}, // America/Godthab
+ {"Turks And Caicos Standard Time", {"EST", "EDT"}}, // America/Grand_Turk
+ {"Central America Standard Time", {"CST", "CST"}}, // America/Guatemala
+ {"Atlantic Standard Time", {"AST", "ADT"}}, // America/Halifax
+ {"Cuba Standard Time", {"CST", "CDT"}}, // America/Havana
+ {"US Eastern Standard Time", {"EST", "EDT"}}, // America/Indianapolis
+ {"SA Western Standard Time", {"-04", "-04"}}, // America/La_Paz
+ {"Pacific Standard Time", {"PST", "PDT"}}, // America/Los_Angeles
+ {"Mountain Standard Time (Mexico)", {"MST", "MST"}}, // America/Mazatlan
+ {"Central Standard Time (Mexico)", {"CST", "CST"}}, // America/Mexico_City
+ {"Saint Pierre Standard Time", {"-03", "-02"}}, // America/Miquelon
+ {"Montevideo Standard Time", {"-03", "-03"}}, // America/Montevideo
+ {"Eastern Standard Time", {"EST", "EDT"}}, // America/New_York
+ {"US Mountain Standard Time", {"MST", "MST"}}, // America/Phoenix
+ {"Haiti Standard Time", {"EST", "EDT"}}, // America/Port-au-Prince
+ {"Magallanes Standard Time", {"-03", "-03"}}, // America/Punta_Arenas
+ {"Canada Central Standard Time", {"CST", "CST"}}, // America/Regina
+ {"Pacific SA Standard Time", {"-04", "-03"}}, // America/Santiago
+ {"E. South America Standard Time", {"-03", "-03"}}, // America/Sao_Paulo
+ {"Newfoundland Standard Time", {"NST", "NDT"}}, // America/St_Johns
+ {"Pacific Standard Time (Mexico)", {"PST", "PDT"}}, // America/Tijuana
+ {"Yukon Standard Time", {"MST", "MST"}}, // America/Whitehorse
+ {"Central Asia Standard Time", {"+06", "+06"}}, // Asia/Almaty
+ {"Jordan Standard Time", {"+03", "+03"}}, // Asia/Amman
+ {"Arabic Standard Time", {"+03", "+03"}}, // Asia/Baghdad
+ {"Azerbaijan Standard Time", {"+04", "+04"}}, // Asia/Baku
+ {"SE Asia Standard Time", {"+07", "+07"}}, // Asia/Bangkok
+ {"Altai Standard Time", {"+07", "+07"}}, // Asia/Barnaul
+ {"Middle East Standard Time", {"EET", "EEST"}}, // Asia/Beirut
+ {"India Standard Time", {"IST", "IST"}}, // Asia/Calcutta
+ {"Transbaikal Standard Time", {"+09", "+09"}}, // Asia/Chita
+ {"Sri Lanka Standard Time", {"+0530", "+0530"}}, // Asia/Colombo
+ {"Syria Standard Time", {"+03", "+03"}}, // Asia/Damascus
+ {"Bangladesh Standard Time", {"+06", "+06"}}, // Asia/Dhaka
+ {"Arabian Standard Time", {"+04", "+04"}}, // Asia/Dubai
+ {"West Bank Standard Time", {"EET", "EEST"}}, // Asia/Hebron
+ {"W. Mongolia Standard Time", {"+07", "+07"}}, // Asia/Hovd
+ {"North Asia East Standard Time", {"+08", "+08"}}, // Asia/Irkutsk
+ {"Israel Standard Time", {"IST", "IDT"}}, // Asia/Jerusalem
+ {"Afghanistan Standard Time", {"+0430", "+0430"}}, // Asia/Kabul
+ {"Russia Time Zone 11", {"+12", "+12"}}, // Asia/Kamchatka
+ {"Pakistan Standard Time", {"PKT", "PKT"}}, // Asia/Karachi
+ {"Nepal Standard Time", {"+0545", "+0545"}}, // Asia/Katmandu
+ {"North Asia Standard Time", {"+07", "+07"}}, // Asia/Krasnoyarsk
+ {"Magadan Standard Time", {"+11", "+11"}}, // Asia/Magadan
+ {"N. Central Asia Standard Time", {"+07", "+07"}}, // Asia/Novosibirsk
+ {"Omsk Standard Time", {"+06", "+06"}}, // Asia/Omsk
+ {"North Korea Standard Time", {"KST", "KST"}}, // Asia/Pyongyang
+ {"Qyzylorda Standard Time", {"+05", "+05"}}, // Asia/Qyzylorda
+ {"Myanmar Standard Time", {"+0630", "+0630"}}, // Asia/Rangoon
+ {"Arab Standard Time", {"+03", "+03"}}, // Asia/Riyadh
+ {"Sakhalin Standard Time", {"+11", "+11"}}, // Asia/Sakhalin
+ {"Korea Standard Time", {"KST", "KST"}}, // Asia/Seoul
+ {"China Standard Time", {"CST", "CST"}}, // Asia/Shanghai
+ {"Singapore Standard Time", {"+08", "+08"}}, // Asia/Singapore
+ {"Russia Time Zone 10", {"+11", "+11"}}, // Asia/Srednekolymsk
+ {"Taipei Standard Time", {"CST", "CST"}}, // Asia/Taipei
+ {"West Asia Standard Time", {"+05", "+05"}}, // Asia/Tashkent
+ {"Georgian Standard Time", {"+04", "+04"}}, // Asia/Tbilisi
+ {"Iran Standard Time", {"+0330", "+0330"}}, // Asia/Tehran
+ {"Tokyo Standard Time", {"JST", "JST"}}, // Asia/Tokyo
+ {"Tomsk Standard Time", {"+07", "+07"}}, // Asia/Tomsk
+ {"Ulaanbaatar Standard Time", {"+08", "+08"}}, // Asia/Ulaanbaatar
+ {"Vladivostok Standard Time", {"+10", "+10"}}, // Asia/Vladivostok
+ {"Yakutsk Standard Time", {"+09", "+09"}}, // Asia/Yakutsk
+ {"Ekaterinburg Standard Time", {"+05", "+05"}}, // Asia/Yekaterinburg
+ {"Caucasus Standard Time", {"+04", "+04"}}, // Asia/Yerevan
+ {"Azores Standard Time", {"-01", "+00"}}, // Atlantic/Azores
+ {"Cape Verde Standard Time", {"-01", "-01"}}, // Atlantic/Cape_Verde
+ {"Greenwich Standard Time", {"GMT", "GMT"}}, // Atlantic/Reykjavik
+ {"Cen. Australia Standard Time", {"ACST", "ACDT"}}, // Australia/Adelaide
+ {"E. Australia Standard Time", {"AEST", "AEST"}}, // Australia/Brisbane
+ {"AUS Central Standard Time", {"ACST", "ACST"}}, // Australia/Darwin
+ {"Aus Central W. Standard Time", {"+0845", "+0845"}}, // Australia/Eucla
+ {"Tasmania Standard Time", {"AEST", "AEDT"}}, // Australia/Hobart
+ {"Lord Howe Standard Time", {"+1030", "+11"}}, // Australia/Lord_Howe
+ {"W. Australia Standard Time", {"AWST", "AWST"}}, // Australia/Perth
+ {"AUS Eastern Standard Time", {"AEST", "AEDT"}}, // Australia/Sydney
+ {"UTC-11", {"-11", "-11"}}, // Etc/GMT+11
+ {"Dateline Standard Time", {"-12", "-12"}}, // Etc/GMT+12
+ {"UTC-02", {"-02", "-02"}}, // Etc/GMT+2
+ {"UTC-08", {"-08", "-08"}}, // Etc/GMT+8
+ {"UTC-09", {"-09", "-09"}}, // Etc/GMT+9
+ {"UTC+12", {"+12", "+12"}}, // Etc/GMT-12
+ {"UTC+13", {"+13", "+13"}}, // Etc/GMT-13
+ {"UTC", {"UTC", "UTC"}}, // Etc/UTC
+ {"Astrakhan Standard Time", {"+04", "+04"}}, // Europe/Astrakhan
+ {"W. Europe Standard Time", {"CET", "CEST"}}, // Europe/Berlin
+ {"GTB Standard Time", {"EET", "EEST"}}, // Europe/Bucharest
+ {"Central Europe Standard Time", {"CET", "CEST"}}, // Europe/Budapest
+ {"E. Europe Standard Time", {"EET", "EEST"}}, // Europe/Chisinau
+ {"Turkey Standard Time", {"+03", "+03"}}, // Europe/Istanbul
+ {"Kaliningrad Standard Time", {"EET", "EET"}}, // Europe/Kaliningrad
+ {"FLE Standard Time", {"EET", "EEST"}}, // Europe/Kiev
+ {"GMT Standard Time", {"GMT", "BST"}}, // Europe/London
+ {"Belarus Standard Time", {"+03", "+03"}}, // Europe/Minsk
+ {"Russian Standard Time", {"MSK", "MSK"}}, // Europe/Moscow
+ {"Romance Standard Time", {"CET", "CEST"}}, // Europe/Paris
+ {"Russia Time Zone 3", {"+04", "+04"}}, // Europe/Samara
+ {"Saratov Standard Time", {"+04", "+04"}}, // Europe/Saratov
+ {"Volgograd Standard Time", {"MSK", "MSK"}}, // Europe/Volgograd
+ {"Central European Standard Time", {"CET", "CEST"}}, // Europe/Warsaw
+ {"Mauritius Standard Time", {"+04", "+04"}}, // Indian/Mauritius
+ {"Samoa Standard Time", {"+13", "+13"}}, // Pacific/Apia
+ {"New Zealand Standard Time", {"NZST", "NZDT"}}, // Pacific/Auckland
+ {"Bougainville Standard Time", {"+11", "+11"}}, // Pacific/Bougainville
+ {"Chatham Islands Standard Time", {"+1245", "+1345"}}, // Pacific/Chatham
+ {"Easter Island Standard Time", {"-06", "-05"}}, // Pacific/Easter
+ {"Fiji Standard Time", {"+12", "+12"}}, // Pacific/Fiji
+ {"Central Pacific Standard Time", {"+11", "+11"}}, // Pacific/Guadalcanal
+ {"Hawaiian Standard Time", {"HST", "HST"}}, // Pacific/Honolulu
+ {"Line Islands Standard Time", {"+14", "+14"}}, // Pacific/Kiritimati
+ {"Marquesas Standard Time", {"-0930", "-0930"}}, // Pacific/Marquesas
+ {"Norfolk Standard Time", {"+11", "+12"}}, // Pacific/Norfolk
+ {"West Pacific Standard Time", {"+10", "+10"}}, // Pacific/Port_Moresby
+ {"Tonga Standard Time", {"+13", "+13"}}, // Pacific/Tongatapu
}
iana_to_windows_tz :: proc(iana_name: string, allocator := context.allocator) -> (name: string, success: bool) {
@@ -269,7 +270,18 @@ _region_load :: proc(reg_str: string, allocator := context.allocator) -> (out_re
defer delete(wintz_name, allocator)
defer delete(iana_name, allocator)
- abbrevs := tz_abbrevs[wintz_name] or_return
+ abbrevs: TZ_Abbrev
+ abbrevs_ok: bool
+ for pair in tz_abbrevs {
+ if pair.key == wintz_name {
+ abbrevs = pair.value
+ abbrevs_ok = true
+ break
+ }
+ }
+ if !abbrevs_ok {
+ return
+ }
if abbrevs.std == "UTC" && abbrevs.dst == abbrevs.std {
return nil, true
}
diff --git a/core/time/timezone/tzdate.odin b/core/time/timezone/tzdate.odin
index 96df44299..8e900ec11 100644
--- a/core/time/timezone/tzdate.odin
+++ b/core/time/timezone/tzdate.odin
@@ -168,7 +168,7 @@ process_rrule :: proc(rrule: datetime.TZ_RRule, tm: time.Time) -> (out: datetime
},
}
record_sort_proc :: proc(i, j: datetime.TZ_Record) -> bool {
- return i.time > j.time
+ return i.time < j.time
}
slice.sort_by(records, record_sort_proc)
@@ -179,7 +179,7 @@ process_rrule :: proc(rrule: datetime.TZ_RRule, tm: time.Time) -> (out: datetime
}
}
- return records[len(records)-1], true
+ return records[0], true
}
datetime_to_utc :: proc(dt: datetime.DateTime) -> (out: datetime.DateTime, success: bool) #optional_ok {
@@ -224,7 +224,7 @@ datetime_to_tz :: proc(dt: datetime.DateTime, tz: ^datetime.TZ_Region) -> (out:
record := region_get_nearest(tz, tm) or_return
secs := time.time_to_unix(tm)
- adj_time := time.unix(secs + record.utc_offset, 0)
+ adj_time := time.unix(secs + record.utc_offset, i64(dt.nano))
adj_dt := time.time_to_datetime(adj_time) or_return
adj_dt.tz = tz
diff --git a/examples/all/all_experimental.odin b/examples/all/all_experimental.odin
index 1d4eb4bb9..f00098abd 100644
--- a/examples/all/all_experimental.odin
+++ b/examples/all/all_experimental.odin
@@ -1,8 +1,2 @@
#+build windows
package all
-
-import c_tokenizer "core:c/frontend/tokenizer"
-import c_preprocessor "core:c/frontend/preprocessor"
-
-_ :: c_tokenizer
-_ :: c_preprocessor
diff --git a/examples/all/all_main.odin b/examples/all/all_main.odin
index 4a8a198d3..0a17227b8 100644
--- a/examples/all/all_main.odin
+++ b/examples/all/all_main.odin
@@ -26,12 +26,14 @@ import topological_sort "core:container/topological_sort"
import crypto "core:crypto"
import aead "core:crypto/aead"
+import aegis "core:crypto/aegis"
import aes "core:crypto/aes"
import blake2b "core:crypto/blake2b"
import blake2s "core:crypto/blake2s"
import chacha20 "core:crypto/chacha20"
import chacha20poly1305 "core:crypto/chacha20poly1305"
import crypto_hash "core:crypto/hash"
+import deoxysii "core:crypto/deoxysii"
import ed25519 "core:crypto/ed25519"
import hkdf "core:crypto/hkdf"
import hmac "core:crypto/hmac"
@@ -48,6 +50,7 @@ import shake "core:crypto/shake"
import sm3 "core:crypto/sm3"
import tuplehash "core:crypto/tuplehash"
import x25519 "core:crypto/x25519"
+import x448 "core:crypto/x448"
import pe "core:debug/pe"
import trace "core:debug/trace"
@@ -115,6 +118,7 @@ import relative "core:relative"
import reflect "core:reflect"
import runtime "base:runtime"
+import sanitizer "base:sanitizer"
import simd "core:simd"
import x86 "core:simd/x86"
import slice "core:slice"
@@ -169,11 +173,13 @@ _ :: topological_sort
_ :: crypto
_ :: crypto_hash
_ :: aead
+_ :: aegis
_ :: aes
_ :: blake2b
_ :: blake2s
_ :: chacha20
_ :: chacha20poly1305
+_ :: deoxysii
_ :: ed25519
_ :: hmac
_ :: hkdf
@@ -190,6 +196,7 @@ _ :: shake
_ :: sm3
_ :: tuplehash
_ :: x25519
+_ :: x448
_ :: pe
_ :: trace
_ :: dynlib
@@ -269,3 +276,4 @@ _ :: uuid_legacy
_ :: utf8
_ :: utf8string
_ :: utf16
+_ :: sanitizer
diff --git a/examples/all/all_vendor_windows.odin b/examples/all/all_vendor_windows.odin
index 50529afc9..5087bac07 100644
--- a/examples/all/all_vendor_windows.odin
+++ b/examples/all/all_vendor_windows.odin
@@ -2,6 +2,9 @@ package all
import wgpu "vendor:wgpu"
import b2 "vendor:box2d"
+import game_input "vendor:windows/GameInput"
_ :: wgpu
-_ :: b2
\ No newline at end of file
+_ :: b2
+_ :: game_input
+
diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin
index 36d1359ca..c559d4771 100644
--- a/examples/demo/demo.odin
+++ b/examples/demo/demo.odin
@@ -1,4 +1,5 @@
#+vet !using-stmt !using-param
+#+feature dynamic-literals
package main
import "core:fmt"
@@ -2184,7 +2185,7 @@ or_break_and_or_continue_operators :: proc() {
// The concept of 'or_break' and 'or_continue' is very similar to that of 'or_return'.
// The difference is that unlike 'or_return', the value does not get returned from
// the current procedure but rather discarded if it is 'false' or not 'nil', and then
- // the specified branch (i.e. break or_continue).
+ // the specified branch (i.e. break or continue).
// The or branch expression can be labelled if a specific statement needs to be used.
Error :: enum {
@@ -2336,7 +2337,7 @@ matrix_type :: proc() {
// There is no support for booleans, quaternions, or any compound type.
// Indexing a matrix can be used with the matrix indexing syntax
- // This mirrors othe type usages: type on the left, usage on the right
+ // This mirrors other type usages: type on the left, usage on the right
elem := m[1, 2] // row 1, column 2
assert(elem == -6)
@@ -2598,6 +2599,7 @@ main :: proc() {
using_statement()
implicit_context_system()
parametric_polymorphism()
+ threading_example()
array_programming()
map_type()
implicit_selector_expression()
@@ -2613,7 +2615,6 @@ main :: proc() {
ranged_fields_for_array_compound_literals()
deprecated_attribute()
range_statements_with_multiple_return_values()
- threading_example()
soa_struct_layout()
constant_literal_expressions()
union_maybe()
diff --git a/misc/featuregen/featuregen.py b/misc/featuregen/featuregen.py
index ecc47f70c..534934b87 100644
--- a/misc/featuregen/featuregen.py
+++ b/misc/featuregen/featuregen.py
@@ -21,7 +21,7 @@ with tempfile.NamedTemporaryFile(suffix=".odin", delete=True) as temp_file:
temp_file.write(b"package main\n")
for arch, target, triple, cpus, features in archs:
- cmd = ["odin", "build", temp_file.name, "-file", "-build-mode:llvm", "-out:temp", "-target-features:\"help\"", f"-target:\"{target}\""]
+ cmd = ["odin", "build", temp_file.name, "-file", "-use-single-module", "-build-mode:asm", "-out:temp", "-target-features:\"help\"", f"-target:\"{target}\""]
process = subprocess.Popen(cmd, stderr=subprocess.PIPE, text=True)
state = SEEKING_CPUS
@@ -59,7 +59,7 @@ with tempfile.NamedTemporaryFile(suffix=".odin", delete=True) as temp_file:
print(f"odin build returned with non-zero exit code {process.returncode}")
sys.exit(1)
- os.remove("temp.ll")
+ os.remove("temp.S")
def print_default_features(triple, microarch):
cmd = ["./featuregen", triple, microarch]
diff --git a/misc/get-date.c b/misc/get-date.c
new file mode 100644
index 000000000..bf5b32738
--- /dev/null
+++ b/misc/get-date.c
@@ -0,0 +1,13 @@
+/*
+ Prints the current date as YYYYMMDD
+
+ e.g. 2024-12-25
+*/
+#include
+#include
+
+int main(int arg_count, char const **arg_ptr) {
+ time_t t = time(NULL);
+ struct tm* now = localtime(&t);
+ printf("%04d%02d%02d", now->tm_year + 1900, now->tm_mon + 1, now->tm_mday);
+}
\ No newline at end of file
diff --git a/misc/odin.rc b/misc/odin.rc
index 9e605f6dc..70d395030 100644
--- a/misc/odin.rc
+++ b/misc/odin.rc
@@ -55,12 +55,12 @@ BEGIN
BLOCK "0409FDE9"
BEGIN
VALUE "CompanyName", "https://odin-lang.org/"
- VALUE "FileDescription", "Odin general-purpose programming language." // note this is shown in the task manager
+ VALUE "FileDescription", "Odin" // note this is shown in the task manager
VALUE "FileVersion", QUOTE(VF)
VALUE "InternalName", "odin.exe"
VALUE "LegalCopyright", "Copyright (c) 2016-2024 Ginger Bill. All rights reserved."
VALUE "OriginalFilename", "odin.exe"
- VALUE "ProductName", "The Odin Programming Language"
+ VALUE "ProductName", "Odin Programming Language"
VALUE "ProductVersion", QUOTE(VP)
VALUE "Comments", QUOTE(git-sha: GIT_SHA)
// custom values
diff --git a/odin.rdi b/odin.rdi
deleted file mode 100644
index 851ee3578..000000000
Binary files a/odin.rdi and /dev/null differ
diff --git a/shell.nix b/shell.nix
index 21301b9d7..33e90018c 100644
--- a/shell.nix
+++ b/shell.nix
@@ -3,9 +3,10 @@ pkgs.mkShell {
name = "odin";
nativeBuildInputs = with pkgs; [
git
- clang_17
- llvmPackages_17.llvm
- llvmPackages_17.bintools
+ which
+ clang_20
+ llvmPackages_20.llvm
+ llvmPackages_20.bintools
];
shellHook="CXX=clang++";
}
diff --git a/src/big_int.cpp b/src/big_int.cpp
index 8e476f090..0b0a9a400 100644
--- a/src/big_int.cpp
+++ b/src/big_int.cpp
@@ -251,7 +251,10 @@ gb_internal void big_int_from_string(BigInt *dst, String const &s, bool *success
exp *= 10;
exp += v;
}
- big_int_exp_u64(dst, &b, exp, success);
+ BigInt tmp = {};
+ mp_init(&tmp);
+ big_int_exp_u64(&tmp, &b, exp, success);
+ big_int_mul_eq(dst, &tmp);
}
if (is_negative) {
diff --git a/src/bug_report.cpp b/src/bug_report.cpp
index d4517f9e3..0a617fe39 100644
--- a/src/bug_report.cpp
+++ b/src/bug_report.cpp
@@ -526,402 +526,46 @@ gb_internal void report_os_info() {
gb_printf(", %s %s\n", info->sysname, info->release);
#elif defined(GB_SYSTEM_OSX)
- struct Darwin_To_Release {
- const char* build; // 21G83
- int darwin[3]; // Darwin kernel triplet
- const char* os_name; // OS X, MacOS
- struct {
- const char* name; // Monterey, Mojave, etc.
- int version[3]; // 12.4, etc.
- } release;
- };
-
- Darwin_To_Release macos_release_map[] = {
- {"8A428", { 8, 0, 0}, "macOS", {"Tiger", {10, 4, 0}}},
- {"8A432", { 8, 0, 0}, "macOS", {"Tiger", {10, 4, 0}}},
- {"8B15", { 8, 1, 0}, "macOS", {"Tiger", {10, 4, 1}}},
- {"8B17", { 8, 1, 0}, "macOS", {"Tiger", {10, 4, 1}}},
- {"8C46", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}},
- {"8C47", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}},
- {"8E102", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}},
- {"8E45", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}},
- {"8E90", { 8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}},
- {"8F46", { 8, 3, 0}, "macOS", {"Tiger", {10, 4, 3}}},
- {"8G32", { 8, 4, 0}, "macOS", {"Tiger", {10, 4, 4}}},
- {"8G1165", { 8, 4, 0}, "macOS", {"Tiger", {10, 4, 4}}},
- {"8H14", { 8, 5, 0}, "macOS", {"Tiger", {10, 4, 5}}},
- {"8G1454", { 8, 5, 0}, "macOS", {"Tiger", {10, 4, 5}}},
- {"8I127", { 8, 6, 0}, "macOS", {"Tiger", {10, 4, 6}}},
- {"8I1119", { 8, 6, 0}, "macOS", {"Tiger", {10, 4, 6}}},
- {"8J135", { 8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}},
- {"8J2135a", { 8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}},
- {"8K1079", { 8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}},
- {"8N5107", { 8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}},
- {"8L127", { 8, 8, 0}, "macOS", {"Tiger", {10, 4, 8}}},
- {"8L2127", { 8, 8, 0}, "macOS", {"Tiger", {10, 4, 8}}},
- {"8P135", { 8, 9, 0}, "macOS", {"Tiger", {10, 4, 9}}},
- {"8P2137", { 8, 9, 0}, "macOS", {"Tiger", {10, 4, 9}}},
- {"8R218", { 8, 10, 0}, "macOS", {"Tiger", {10, 4, 10}}},
- {"8R2218", { 8, 10, 0}, "macOS", {"Tiger", {10, 4, 10}}},
- {"8R2232", { 8, 10, 0}, "macOS", {"Tiger", {10, 4, 10}}},
- {"8S165", { 8, 11, 0}, "macOS", {"Tiger", {10, 4, 11}}},
- {"8S2167", { 8, 11, 0}, "macOS", {"Tiger", {10, 4, 11}}},
- {"9A581", { 9, 0, 0}, "macOS", {"Leopard", {10, 5, 0}}},
- {"9B18", { 9, 1, 0}, "macOS", {"Leopard", {10, 5, 1}}},
- {"9B2117", { 9, 1, 1}, "macOS", {"Leopard", {10, 5, 1}}},
- {"9C31", { 9, 2, 0}, "macOS", {"Leopard", {10, 5, 2}}},
- {"9C7010", { 9, 2, 0}, "macOS", {"Leopard", {10, 5, 2}}},
- {"9D34", { 9, 3, 0}, "macOS", {"Leopard", {10, 5, 3}}},
- {"9E17", { 9, 4, 0}, "macOS", {"Leopard", {10, 5, 4}}},
- {"9F33", { 9, 5, 0}, "macOS", {"Leopard", {10, 5, 5}}},
- {"9G55", { 9, 6, 0}, "macOS", {"Leopard", {10, 5, 6}}},
- {"9G66", { 9, 6, 0}, "macOS", {"Leopard", {10, 5, 6}}},
- {"9G71", { 9, 6, 0}, "macOS", {"Leopard", {10, 5, 6}}},
- {"9J61", { 9, 7, 0}, "macOS", {"Leopard", {10, 5, 7}}},
- {"9L30", { 9, 8, 0}, "macOS", {"Leopard", {10, 5, 8}}},
- {"9L34", { 9, 8, 0}, "macOS", {"Leopard", {10, 5, 8}}},
- {"10A432", {10, 0, 0}, "macOS", {"Snow Leopard", {10, 6, 0}}},
- {"10A433", {10, 0, 0}, "macOS", {"Snow Leopard", {10, 6, 0}}},
- {"10B504", {10, 1, 0}, "macOS", {"Snow Leopard", {10, 6, 1}}},
- {"10C540", {10, 2, 0}, "macOS", {"Snow Leopard", {10, 6, 2}}},
- {"10D573", {10, 3, 0}, "macOS", {"Snow Leopard", {10, 6, 3}}},
- {"10D575", {10, 3, 0}, "macOS", {"Snow Leopard", {10, 6, 3}}},
- {"10D578", {10, 3, 0}, "macOS", {"Snow Leopard", {10, 6, 3}}},
- {"10F569", {10, 4, 0}, "macOS", {"Snow Leopard", {10, 6, 4}}},
- {"10H574", {10, 5, 0}, "macOS", {"Snow Leopard", {10, 6, 5}}},
- {"10J567", {10, 6, 0}, "macOS", {"Snow Leopard", {10, 6, 6}}},
- {"10J869", {10, 7, 0}, "macOS", {"Snow Leopard", {10, 6, 7}}},
- {"10J3250", {10, 7, 0}, "macOS", {"Snow Leopard", {10, 6, 7}}},
- {"10J4138", {10, 7, 0}, "macOS", {"Snow Leopard", {10, 6, 7}}},
- {"10K540", {10, 8, 0}, "macOS", {"Snow Leopard", {10, 6, 8}}},
- {"10K549", {10, 8, 0}, "macOS", {"Snow Leopard", {10, 6, 8}}},
- {"11A511", {11, 0, 0}, "macOS", {"Lion", {10, 7, 0}}},
- {"11A511s", {11, 0, 0}, "macOS", {"Lion", {10, 7, 0}}},
- {"11A2061", {11, 0, 2}, "macOS", {"Lion", {10, 7, 0}}},
- {"11A2063", {11, 0, 2}, "macOS", {"Lion", {10, 7, 0}}},
- {"11B26", {11, 1, 0}, "macOS", {"Lion", {10, 7, 1}}},
- {"11B2118", {11, 1, 0}, "macOS", {"Lion", {10, 7, 1}}},
- {"11C74", {11, 2, 0}, "macOS", {"Lion", {10, 7, 2}}},
- {"11D50", {11, 3, 0}, "macOS", {"Lion", {10, 7, 3}}},
- {"11E53", {11, 4, 0}, "macOS", {"Lion", {10, 7, 4}}},
- {"11G56", {11, 4, 2}, "macOS", {"Lion", {10, 7, 5}}},
- {"11G63", {11, 4, 2}, "macOS", {"Lion", {10, 7, 5}}},
- {"12A269", {12, 0, 0}, "macOS", {"Mountain Lion", {10, 8, 0}}},
- {"12B19", {12, 1, 0}, "macOS", {"Mountain Lion", {10, 8, 1}}},
- {"12C54", {12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}},
- {"12C60", {12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}},
- {"12C2034", {12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}},
- {"12C3104", {12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}},
- {"12D78", {12, 3, 0}, "macOS", {"Mountain Lion", {10, 8, 3}}},
- {"12E55", {12, 4, 0}, "macOS", {"Mountain Lion", {10, 8, 4}}},
- {"12E3067", {12, 4, 0}, "macOS", {"Mountain Lion", {10, 8, 4}}},
- {"12E4022", {12, 4, 0}, "macOS", {"Mountain Lion", {10, 8, 4}}},
- {"12F37", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
- {"12F45", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
- {"12F2501", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
- {"12F2518", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
- {"12F2542", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
- {"12F2560", {12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
- {"13A603", {13, 0, 0}, "macOS", {"Mavericks", {10, 9, 0}}},
- {"13B42", {13, 0, 0}, "macOS", {"Mavericks", {10, 9, 1}}},
- {"13C64", {13, 1, 0}, "macOS", {"Mavericks", {10, 9, 2}}},
- {"13C1021", {13, 1, 0}, "macOS", {"Mavericks", {10, 9, 2}}},
- {"13D65", {13, 2, 0}, "macOS", {"Mavericks", {10, 9, 3}}},
- {"13E28", {13, 3, 0}, "macOS", {"Mavericks", {10, 9, 4}}},
- {"13F34", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"13F1066", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"13F1077", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"13F1096", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"13F1112", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"13F1134", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"13F1507", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"13F1603", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"13F1712", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"13F1808", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"13F1911", {13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
- {"14A389", {14, 0, 0}, "macOS", {"Yosemite", {10, 10, 0}}},
- {"14B25", {14, 0, 0}, "macOS", {"Yosemite", {10, 10, 1}}},
- {"14C109", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}},
- {"14C1510", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}},
- {"14C2043", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}},
- {"14C1514", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}},
- {"14C2513", {14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}},
- {"14D131", {14, 3, 0}, "macOS", {"Yosemite", {10, 10, 3}}},
- {"14D136", {14, 3, 0}, "macOS", {"Yosemite", {10, 10, 3}}},
- {"14E46", {14, 4, 0}, "macOS", {"Yosemite", {10, 10, 4}}},
- {"14F27", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F1021", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F1505", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F1509", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F1605", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F1713", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F1808", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F1909", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F1912", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F2009", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F2109", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F2315", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F2411", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"14F2511", {14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
- {"15A284", {15, 0, 0}, "macOS", {"El Capitan", {10, 11, 0}}},
- {"15B42", {15, 0, 0}, "macOS", {"El Capitan", {10, 11, 1}}},
- {"15C50", {15, 2, 0}, "macOS", {"El Capitan", {10, 11, 2}}},
- {"15D21", {15, 3, 0}, "macOS", {"El Capitan", {10, 11, 3}}},
- {"15E65", {15, 4, 0}, "macOS", {"El Capitan", {10, 11, 4}}},
- {"15F34", {15, 5, 0}, "macOS", {"El Capitan", {10, 11, 5}}},
- {"15G31", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G1004", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G1011", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G1108", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G1212", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G1217", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G1421", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G1510", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G1611", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G17023", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G18013", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G19009", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G20015", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G21013", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"15G22010", {15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
- {"16A323", {16, 0, 0}, "macOS", {"Sierra", {10, 12, 0}}},
- {"16B2555", {16, 1, 0}, "macOS", {"Sierra", {10, 12, 1}}},
- {"16B2657", {16, 1, 0}, "macOS", {"Sierra", {10, 12, 1}}},
- {"16C67", {16, 3, 0}, "macOS", {"Sierra", {10, 12, 2}}},
- {"16C68", {16, 3, 0}, "macOS", {"Sierra", {10, 12, 2}}},
- {"16D32", {16, 4, 0}, "macOS", {"Sierra", {10, 12, 3}}},
- {"16E195", {16, 5, 0}, "macOS", {"Sierra", {10, 12, 4}}},
- {"16F73", {16, 6, 0}, "macOS", {"Sierra", {10, 12, 5}}},
- {"16F2073", {16, 6, 0}, "macOS", {"Sierra", {10, 12, 5}}},
- {"16G29", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1036", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1114", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1212", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1314", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1408", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1510", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1618", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1710", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1815", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1917", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G1918", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G2016", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G2127", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G2128", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"16G2136", {16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
- {"17A365", {17, 0, 0}, "macOS", {"High Sierra", {10, 13, 0}}},
- {"17A405", {17, 0, 0}, "macOS", {"High Sierra", {10, 13, 0}}},
- {"17B48", {17, 2, 0}, "macOS", {"High Sierra", {10, 13, 1}}},
- {"17B1002", {17, 2, 0}, "macOS", {"High Sierra", {10, 13, 1}}},
- {"17B1003", {17, 2, 0}, "macOS", {"High Sierra", {10, 13, 1}}},
- {"17C88", {17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}},
- {"17C89", {17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}},
- {"17C205", {17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}},
- {"17C2205", {17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}},
- {"17D47", {17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}},
- {"17D2047", {17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}},
- {"17D102", {17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}},
- {"17D2102", {17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}},
- {"17E199", {17, 5, 0}, "macOS", {"High Sierra", {10, 13, 4}}},
- {"17E202", {17, 5, 0}, "macOS", {"High Sierra", {10, 13, 4}}},
- {"17F77", {17, 6, 0}, "macOS", {"High Sierra", {10, 13, 5}}},
- {"17G65", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G2208", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G2307", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G3025", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G4015", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G5019", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G6029", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G6030", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G7024", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G8029", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G8030", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G8037", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G9016", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G10021", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G11023", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G12034", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G13033", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G13035", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G14019", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G14033", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"17G14042", {17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
- {"18A391", {18, 0, 0}, "macOS", {"Mojave", {10, 14, 0}}},
- {"18B75", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 1}}},
- {"18B2107", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 1}}},
- {"18B3094", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 1}}},
- {"18C54", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 2}}},
- {"18D42", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 3}}},
- {"18D43", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 3}}},
- {"18D109", {18, 2, 0}, "macOS", {"Mojave", {10, 14, 3}}},
- {"18E226", {18, 5, 0}, "macOS", {"Mojave", {10, 14, 4}}},
- {"18E227", {18, 5, 0}, "macOS", {"Mojave", {10, 14, 4}}},
- {"18F132", {18, 6, 0}, "macOS", {"Mojave", {10, 14, 5}}},
- {"18G84", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G87", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G95", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G103", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G1012", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G2022", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G3020", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G4032", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G5033", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G6020", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G6032", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G6042", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G7016", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G8012", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G8022", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G9028", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G9216", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"18G9323", {18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
- {"19A583", {19, 0, 0}, "macOS", {"Catalina", {10, 15, 0}}},
- {"19A602", {19, 0, 0}, "macOS", {"Catalina", {10, 15, 0}}},
- {"19A603", {19, 0, 0}, "macOS", {"Catalina", {10, 15, 0}}},
- {"19B88", {19, 0, 0}, "macOS", {"Catalina", {10, 15, 1}}},
- {"19C57", {19, 2, 0}, "macOS", {"Catalina", {10, 15, 2}}},
- {"19C58", {19, 2, 0}, "macOS", {"Catalina", {10, 15, 2}}},
- {"19D76", {19, 3, 0}, "macOS", {"Catalina", {10, 15, 3}}},
- {"19E266", {19, 4, 0}, "macOS", {"Catalina", {10, 15, 4}}},
- {"19E287", {19, 4, 0}, "macOS", {"Catalina", {10, 15, 4}}},
- {"19F96", {19, 5, 0}, "macOS", {"Catalina", {10, 15, 5}}},
- {"19F101", {19, 5, 0}, "macOS", {"Catalina", {10, 15, 5}}},
- {"19G73", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 6}}},
- {"19G2021", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 6}}},
- {"19H2", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H4", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H15", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H114", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H512", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H524", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1030", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1217", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1323", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1417", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1419", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1519", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1615", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1713", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1715", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1824", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H1922", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"19H2026", {19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
- {"20A2411", {20, 1, 0}, "macOS", {"Big Sur", {11, 0, 0}}},
- {"20B29", {20, 1, 0}, "macOS", {"Big Sur", {11, 0, 1}}},
- {"20B50", {20, 1, 0}, "macOS", {"Big Sur", {11, 0, 1}}},
- {"20C69", {20, 2, 0}, "macOS", {"Big Sur", {11, 1, 0}}},
- {"20D64", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 0}}},
- {"20D74", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 1}}},
- {"20D75", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 1}}},
- {"20D80", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 2}}},
- {"20D91", {20, 3, 0}, "macOS", {"Big Sur", {11, 2, 3}}},
- {"20E232", {20, 4, 0}, "macOS", {"Big Sur", {11, 3, 0}}},
- {"20E241", {20, 4, 0}, "macOS", {"Big Sur", {11, 3, 1}}},
- {"20F71", {20, 5, 0}, "macOS", {"Big Sur", {11, 4, 0}}},
- {"20G71", {20, 6, 0}, "macOS", {"Big Sur", {11, 5, 0}}},
- {"20G80", {20, 6, 0}, "macOS", {"Big Sur", {11, 5, 1}}},
- {"20G95", {20, 6, 0}, "macOS", {"Big Sur", {11, 5, 2}}},
- {"20G165", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 0}}},
- {"20G224", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 1}}},
- {"20G314", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 2}}},
- {"20G415", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 3}}},
- {"20G417", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 4}}},
- {"20G527", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 5}}},
- {"20G624", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 6}}},
- {"20G630", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 7}}},
- {"20G730", {20, 6, 0}, "macOS", {"Big Sur", {11, 6, 8}}},
- {"20G817", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 0}}},
- {"20G918", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 1}}},
- {"20G1020", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 2}}},
- {"20G1116", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 3}}},
- {"20G1120", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 4}}},
- {"20G1225", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 5}}},
- {"20G1231", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 6}}},
- {"20G1345", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 7}}},
- {"20G1351", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 8}}},
- {"20G1426", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 9}}},
- {"20G1427", {20, 6, 0}, "macOS", {"Big Sur", {11, 7, 10}}},
- {"21A344", {21, 0, 1}, "macOS", {"Monterey", {12, 0, 0}}},
- {"21A559", {21, 1, 0}, "macOS", {"Monterey", {12, 0, 1}}},
- {"21C52", {21, 2, 0}, "macOS", {"Monterey", {12, 1, 0}}},
- {"21D49", {21, 3, 0}, "macOS", {"Monterey", {12, 2, 0}}},
- {"21D62", {21, 3, 0}, "macOS", {"Monterey", {12, 2, 1}}},
- {"21E230", {21, 4, 0}, "macOS", {"Monterey", {12, 3, 0}}},
- {"21E258", {21, 4, 0}, "macOS", {"Monterey", {12, 3, 1}}},
- {"21F79", {21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}},
- {"21F2081", {21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}},
- {"21F2092", {21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}},
- {"21G72", {21, 6, 0}, "macOS", {"Monterey", {12, 5, 0}}},
- {"21G83", {21, 6, 0}, "macOS", {"Monterey", {12, 5, 1}}},
- {"21G115", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 0}}},
- {"21G217", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 1}}},
- {"21G320", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 2}}},
- {"21G419", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 3}}},
- {"21G526", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 4}}},
- {"21G531", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 5}}},
- {"21G646", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 6}}},
- {"21G651", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 7}}},
- {"21G725", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 8}}},
- {"21G726", {21, 6, 0}, "macOS", {"Monterey", {12, 6, 9}}},
- {"21G816", {21, 6, 0}, "macOS", {"Monterey", {12, 7, 0}}},
- {"21G920", {21, 6, 0}, "macOS", {"Monterey", {12, 7, 1}}},
- {"21G1974", {21, 6, 0}, "macOS", {"Monterey", {12, 7, 2}}},
- {"21H1015", {21, 6, 0}, "macOS", {"Monterey", {12, 7, 3}}},
- {"21H1123", {21, 6, 0}, "macOS", {"Monterey", {12, 7, 4}}},
- {"21H1222", {21, 6, 0}, "macOS", {"Monterey", {12, 7, 5}}},
- {"21H1320", {21, 6, 0}, "macOS", {"Monterey", {12, 7, 6}}},
- {"22A380", {13, 0, 0}, "macOS", {"Ventura", {22, 1, 0}}},
- {"22A400", {13, 0, 1}, "macOS", {"Ventura", {22, 1, 0}}},
- {"22C65", {13, 1, 0}, "macOS", {"Ventura", {22, 2, 0}}},
- {"22D49", {13, 2, 0}, "macOS", {"Ventura", {22, 3, 0}}},
- {"22D68", {13, 2, 1}, "macOS", {"Ventura", {22, 3, 0}}},
- {"22E252", {13, 3, 0}, "macOS", {"Ventura", {22, 4, 0}}},
- {"22E261", {13, 3, 1}, "macOS", {"Ventura", {22, 4, 0}}},
- {"22F66", {13, 4, 0}, "macOS", {"Ventura", {22, 5, 0}}},
- {"22F82", {13, 4, 1}, "macOS", {"Ventura", {22, 5, 0}}},
- {"22E772610a", {13, 4, 1}, "macOS", {"Ventura", {22, 5, 0}}},
- {"22F770820d", {13, 4, 1}, "macOS", {"Ventura", {22, 5, 0}}},
- {"22G74", {13, 5, 0}, "macOS", {"Ventura", {22, 6, 0}}},
- {"22G90", {13, 5, 1}, "macOS", {"Ventura", {22, 6, 0}}},
- {"22G91", {13, 5, 2}, "macOS", {"Ventura", {22, 6, 0}}},
- {"22G120", {13, 6, 0}, "macOS", {"Ventura", {22, 6, 0}}},
- {"22G313", {13, 6, 1}, "macOS", {"Ventura", {22, 6, 0}}},
- {"22G320", {13, 6, 2}, "macOS", {"Ventura", {22, 6, 0}}},
- {"22G436", {22, 6, 0}, "macOS", {"Ventura", {13, 6, 3}}},
- {"22G513", {22, 6, 0}, "macOS", {"Ventura", {13, 6, 4}}},
- {"22G621", {22, 6, 0}, "macOS", {"Ventura", {13, 6, 5}}},
- {"22G630", {22, 6, 0}, "macOS", {"Ventura", {13, 6, 6}}},
- {"22G720", {22, 6, 0}, "macOS", {"Ventura", {13, 6, 7}}},
- {"22G820", {22, 6, 0}, "macOS", {"Ventura", {13, 6, 8}}},
- {"22G830", {22, 6, 0}, "macOS", {"Ventura", {13, 6, 9}}},
- {"22H123", {22, 6, 0}, "macOS", {"Ventura", {13, 7, 0}}},
- {"22H221", {22, 6, 0}, "macOS", {"Ventura", {13, 7, 1}}},
- {"23A344", {23, 0, 0}, "macOS", {"Sonoma", {14, 0, 0}}},
- {"23B74", {23, 1, 0}, "macOS", {"Sonoma", {14, 1, 0}}},
- {"23B81", {23, 1, 0}, "macOS", {"Sonoma", {14, 1, 1}}},
- {"23B2082", {23, 1, 0}, "macOS", {"Sonoma", {14, 1, 1}}},
- {"23B92", {23, 1, 0}, "macOS", {"Sonoma", {14, 1, 2}}},
- {"23B2091", {23, 1, 0}, "macOS", {"Sonoma", {14, 1, 2}}},
- {"23C64", {23, 2, 0}, "macOS", {"Sonoma", {14, 2, 0}}},
- {"23C71", {23, 2, 0}, "macOS", {"Sonoma", {14, 2, 1}}},
- {"23D56", {23, 3, 0}, "macOS", {"Sonoma", {14, 3, 0}}},
- {"23D60", {23, 3, 0}, "macOS", {"Sonoma", {14, 3, 1}}},
- {"23E214", {23, 4, 0}, "macOS", {"Sonoma", {14, 4, 0}}},
- {"23E224", {23, 4, 0}, "macOS", {"Sonoma", {14, 4, 1}}},
- {"23F79", {23, 5, 0}, "macOS", {"Sonoma", {14, 5, 0}}},
- {"23G80", {23, 6, 0}, "macOS", {"Sonoma", {14, 6, 0}}},
- {"23G93", {23, 6, 0}, "macOS", {"Sonoma", {14, 6, 1}}},
- {"23H124", {23, 6, 0}, "macOS", {"Sonoma", {14, 7, 0}}},
- {"23H222", {23, 6, 0}, "macOS", {"Sonoma", {14, 7, 1}}},
- {"24A335", {24, 0, 0}, "macOS", {"Sequoia", {15, 0, 0}}},
- {"24A348", {24, 0, 0}, "macOS", {"Sequoia", {15, 0, 1}}},
- {"24B83", {24, 1, 0}, "macOS", {"Sequoia", {15, 1, 0}}},
+ gbString sw_vers = gb_string_make(heap_allocator(), "");
+ if (!system_exec_command_line_app_output("sw_vers -productVersion", &sw_vers)) {
+ gb_printf("macOS Unknown\n");
+ return;
+ }
+
+ uint32_t major, minor, patch = 0;
+
+ if (sscanf(cast(const char *)sw_vers, "%u.%u.%u", &major, &minor, &patch) < 1) {
+ gb_printf("macOS Unknown\n");
+ return;
+ }
+
+ switch (major) {
+ case 15: gb_printf("macOS Sequoia"); break;
+ case 14: gb_printf("macOS Sonoma"); break;
+ case 13: gb_printf("macOS Ventura"); break;
+ case 12: gb_printf("macOS Monterey"); break;
+ case 11: gb_printf("macOS Big Sur"); break;
+ case 10:
+ {
+ switch (minor) {
+ case 15: gb_printf("macOS Catalina"); break;
+ case 14: gb_printf("macOS Mojave"); break;
+ case 13: gb_printf("macOS High Sierra"); break;
+ case 12: gb_printf("macOS Sierra"); break;
+ case 11: gb_printf("OS X El Capitan"); break;
+ case 10: gb_printf("OS X Yosemite"); break;
+ default: gb_printf("macOS Unknown");
+ };
+ break;
+ }
+ default:
+ gb_printf("macOS Unknown");
};
+ gb_printf(" %d.%d.%d (build ", major, minor, patch);
b32 build_found = 1;
b32 darwin_found = 1;
- uint32_t major, minor, patch;
#define MACOS_VERSION_BUFFER_SIZE 100
char build_buffer[MACOS_VERSION_BUFFER_SIZE];
@@ -937,81 +581,25 @@ gb_internal void report_os_info() {
int darwin_mibs[] = { CTL_KERN, KERN_OSRELEASE };
if (sysctl(darwin_mibs, 2, darwin_buffer, &darwin_buffer_size, NULL, 0) == -1) {
- gb_printf("macOS Unknown\n");
- return;
+ darwin_found = 0;
} else {
if (sscanf(darwin_buffer, "%u.%u.%u", &major, &minor, &patch) != 3) {
darwin_found = 0;
}
}
- // Scan table for match on BUILD
- int macos_release_count = sizeof(macos_release_map) / sizeof(macos_release_map[0]);
- Darwin_To_Release build_match = {};
- Darwin_To_Release kernel_match = {};
-
- for (int build = 0; build < macos_release_count; build++) {
- Darwin_To_Release rel = macos_release_map[build];
-
- // Do we have an exact match on the BUILD?
- if (gb_strcmp(rel.build, (const char *)build_buffer) == 0) {
- build_match = rel;
- break;
- }
-
- // Do we have an exact Darwin match?
- if (rel.darwin[0] == major && rel.darwin[1] == minor && rel.darwin[2] == patch) {
- kernel_match = rel;
- }
-
- // Major kernel version needs to match exactly,
- if (rel.darwin[0] == major) {
- // No major version match yet.
- if (!kernel_match.os_name) {
- kernel_match = rel;
- }
- if (minor >= rel.darwin[1]) {
- kernel_match = rel;
- if (patch >= rel.darwin[2]) {
- kernel_match = rel;
- }
- }
- }
- }
-
- Darwin_To_Release match = {};
- if(!build_match.build) {
- match = kernel_match;
+ if (build_found) {
+ gb_printf("%s, kernel ", build_buffer);
} else {
- match = build_match;
+ gb_printf("Unknown, kernel ");
}
- if (match.os_name) {
- gb_printf("%s %s %d", match.os_name, match.release.name, match.release.version[0]);
- if (match.release.version[1] > 0 || match.release.version[2] > 0) {
- gb_printf(".%d", match.release.version[1]);
- }
- if (match.release.version[2] > 0) {
- gb_printf(".%d", match.release.version[2]);
- }
- if (build_found) {
- gb_printf(" (build: %s, kernel: %d.%d.%d)\n", build_buffer, match.darwin[0], match.darwin[1], match.darwin[2]);
- } else {
- gb_printf(" (build: %s?, kernel: %d.%d.%d)\n", match.build, match.darwin[0], match.darwin[1], match.darwin[2]);
- }
- return;
+ if (darwin_found) {
+ gb_printf("%s)\n", darwin_buffer);
+ } else {
+ gb_printf("Unknown)\n");
}
- if (build_found && darwin_found) {
- gb_printf("macOS Unknown (build: %s, kernel: %d.%d.%d)\n", build_buffer, major, minor, patch);
- return;
- } else if (build_found) {
- gb_printf("macOS Unknown (build: %s)\n", build_buffer);
- return;
- } else if (darwin_found) {
- gb_printf("macOS Unknown (kernel: %d.%d.%d)\n", major, minor, patch);
- return;
- }
#elif defined(GB_SYSTEM_OPENBSD) || defined(GB_SYSTEM_NETBSD)
struct utsname un;
diff --git a/src/build_settings.cpp b/src/build_settings.cpp
index 50fae93b8..c941e0f68 100644
--- a/src/build_settings.cpp
+++ b/src/build_settings.cpp
@@ -109,6 +109,12 @@ gb_global String target_arch_names[TargetArch_COUNT] = {
str_lit("riscv64"),
};
+#if defined(GB_SYSTEM_WINDOWS)
+ #include
+#else
+ #include
+#endif
+
#include "build_settings_microarch.cpp"
gb_global String target_endian_names[TargetEndian_COUNT] = {
@@ -165,6 +171,7 @@ struct TargetMetrics {
enum Subtarget : u32 {
Subtarget_Default,
Subtarget_iOS,
+ Subtarget_Android,
Subtarget_COUNT,
};
@@ -172,6 +179,7 @@ enum Subtarget : u32 {
gb_global String subtarget_strings[Subtarget_COUNT] = {
str_lit(""),
str_lit("ios"),
+ str_lit("android"),
};
@@ -198,20 +206,25 @@ enum BuildModeKind {
BuildMode_COUNT,
};
-enum CommandKind : u32 {
+enum CommandKind : u64 {
Command_run = 1<<0,
Command_build = 1<<1,
- Command_check = 1<<3,
- Command_doc = 1<<5,
- Command_version = 1<<6,
- Command_test = 1<<7,
+ Command_check = 1<<2,
+ Command_doc = 1<<3,
+ Command_version = 1<<4,
+ Command_test = 1<<5,
- Command_strip_semicolon = 1<<8,
- Command_bug_report = 1<<9,
+ Command_strip_semicolon = 1<<6,
+ Command_bug_report = 1<<7,
+
+ Command_bundle_android = 1<<8,
+ Command_bundle_macos = 1<<9,
+ Command_bundle_ios = 1<<10,
+ Command_bundle_orca = 1<<11,
Command__does_check = Command_run|Command_build|Command_check|Command_doc|Command_test|Command_strip_semicolon,
Command__does_build = Command_run|Command_build|Command_test,
- Command_all = ~(u32)0,
+ Command_all = ~(CommandKind)0,
};
gb_global char const *odin_command_strings[32] = {
@@ -222,6 +235,11 @@ gb_global char const *odin_command_strings[32] = {
"version",
"test",
"strip-semicolon",
+ "",
+ "bundle android",
+ "bundle macos",
+ "bundle ios",
+ "bundle orca",
};
@@ -324,6 +342,18 @@ u64 get_vet_flag_from_name(String const &name) {
return VetFlag_NONE;
}
+enum OptInFeatureFlags : u64 {
+ OptInFeatureFlag_NONE = 0,
+ OptInFeatureFlag_DynamicLiterals = 1u<<0,
+};
+
+u64 get_feature_flag_from_name(String const &name) {
+ if (name == "dynamic-literals") {
+ return OptInFeatureFlag_DynamicLiterals;
+ }
+ return OptInFeatureFlag_NONE;
+}
+
enum SanitizerFlags : u32 {
SanitizerFlag_NONE = 0,
@@ -429,7 +459,6 @@ struct BuildContext {
bool ignore_unknown_attributes;
bool no_bounds_check;
bool no_type_assert;
- bool no_dynamic_literals;
bool no_output_files;
bool no_crt;
bool no_rpath;
@@ -461,12 +490,14 @@ struct BuildContext {
bool ignore_microsoft_magic;
bool linker_map_file;
+ bool use_single_module;
bool use_separate_modules;
bool module_per_file;
bool cached;
BuildCacheData build_cache_data;
bool internal_no_inline;
+ bool internal_by_value;
bool no_threaded_checker;
@@ -508,6 +539,21 @@ struct BuildContext {
String minimum_os_version_string;
bool minimum_os_version_string_given;
+
+
+ int ODIN_ANDROID_API_LEVEL;
+
+ String ODIN_ANDROID_SDK;
+
+ String ODIN_ANDROID_NDK;
+ String ODIN_ANDROID_NDK_TOOLCHAIN;
+ String ODIN_ANDROID_NDK_TOOLCHAIN_LIB;
+ String ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL;
+ String ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT;
+
+ String android_keystore;
+ String android_keystore_alias;
+ String android_keystore_password;
};
gb_global BuildContext build_context = {0};
@@ -531,12 +577,6 @@ gb_internal isize MAX_ERROR_COLLECTOR_COUNT(void) {
return build_context.max_error_count;
}
-#if defined(GB_SYSTEM_WINDOWS)
- #include
-#else
- #include
-#endif
-
// NOTE: AMD64 targets had their alignment on 128 bit ints bumped from 8 to 16 (undocumented of course).
#if LLVM_VERSION_MAJOR >= 18
#define AMD64_MAX_ALIGNMENT 16
@@ -807,13 +847,39 @@ gb_global NamedTargetMetrics *selected_target_metrics;
gb_global Subtarget selected_subtarget;
-gb_internal TargetOsKind get_target_os_from_string(String str) {
+gb_internal TargetOsKind get_target_os_from_string(String str, Subtarget *subtarget_ = nullptr) {
+ String os_name = str;
+ String subtarget = {};
+ auto part = string_partition(str, str_lit(":"));
+ if (part.match.len == 1) {
+ os_name = part.head;
+ subtarget = part.tail;
+ }
+
+ TargetOsKind kind = TargetOs_Invalid;
+
for (isize i = 0; i < TargetOs_COUNT; i++) {
- if (str_eq_ignore_case(target_os_names[i], str)) {
- return cast(TargetOsKind)i;
+ if (str_eq_ignore_case(target_os_names[i], os_name)) {
+ kind = cast(TargetOsKind)i;
+ break;
}
}
- return TargetOs_Invalid;
+ if (subtarget_) *subtarget_ = Subtarget_Default;
+
+ if (subtarget.len != 0) {
+ if (str_eq_ignore_case(subtarget, "generic") || str_eq_ignore_case(subtarget, "default")) {
+ if (subtarget_) *subtarget_ = Subtarget_Default;
+ } else {
+ for (isize i = 1; i < Subtarget_COUNT; i++) {
+ if (str_eq_ignore_case(subtarget_strings[i], subtarget)) {
+ if (subtarget_) *subtarget_ = cast(Subtarget)i;
+ break;
+ }
+ }
+ }
+ }
+
+ return kind;
}
gb_internal TargetArchKind get_target_arch_from_string(String str) {
@@ -933,6 +999,14 @@ gb_internal bool is_arch_x86(void) {
gb_global String const WIN32_SEPARATOR_STRING = {cast(u8 *)"\\", 1};
gb_global String const NIX_SEPARATOR_STRING = {cast(u8 *)"/", 1};
+gb_global String const SEPARATOR_STRING =
+#if defined(GB_SYSTEM_WINDOWS)
+ WIN32_SEPARATOR_STRING;
+#else
+ NIX_SEPARATOR_STRING;
+#endif
+
+
gb_global String const WASM_MODULE_NAME_SEPARATOR = str_lit("..");
gb_internal String internal_odin_root_dir(void);
@@ -1448,6 +1522,94 @@ gb_internal bool has_ansi_terminal_colours(void) {
return build_context.has_ansi_terminal_colours && !json_errors();
}
+gb_internal void init_android_values(bool with_sdk) {
+ auto *bc = &build_context;
+ { // Android SDK/API Level
+ String default_level = str_lit("34");
+ if (!bc->minimum_os_version_string_given) {
+ bc->minimum_os_version_string = default_level;
+ }
+ BigInt level = {};
+ bool success = false;
+ big_int_from_string(&level, bc->minimum_os_version_string, &success);
+ if (!success) {
+ gb_printf_err("Warning: Invalid -minimum-os-version:%.*s for -subtarget:Android, defaulting to %.*s\n", LIT(bc->minimum_os_version_string), LIT(default_level));
+ bc->minimum_os_version_string = default_level;
+ big_int_from_string(&level, bc->minimum_os_version_string, &success);
+ GB_ASSERT(success);
+ }
+
+ i64 new_level = big_int_to_i64(&level);
+
+ if (new_level >= 21) {
+ bc->ODIN_ANDROID_API_LEVEL = cast(int)new_level;
+ } else {
+ gb_printf_err("Warning: Invalid -minimum-os-version:%.*s for -subtarget:Android, defaulting to %.*s\n", LIT(bc->minimum_os_version_string), LIT(default_level));
+ bc->ODIN_ANDROID_API_LEVEL = atoi(cast(char const *)default_level.text);
+ }
+ }
+ bc->ODIN_ANDROID_NDK = normalize_path(permanent_allocator(), make_string_c(gb_get_env("ODIN_ANDROID_NDK", permanent_allocator())), NIX_SEPARATOR_STRING);
+ bc->ODIN_ANDROID_NDK_TOOLCHAIN = normalize_path(permanent_allocator(), make_string_c(gb_get_env("ODIN_ANDROID_NDK_TOOLCHAIN", permanent_allocator())), NIX_SEPARATOR_STRING);
+ bc->ODIN_ANDROID_SDK = normalize_path(permanent_allocator(), make_string_c(gb_get_env("ODIN_ANDROID_SDK", permanent_allocator())), NIX_SEPARATOR_STRING);
+
+ #if defined(GB_SYSTEM_WINDOWS)
+ if (bc->ODIN_ANDROID_SDK.len == 0) {
+ bc->ODIN_ANDROID_SDK = normalize_path(permanent_allocator(),
+ path_to_fullpath(permanent_allocator(), str_lit("%LocalAppData%/Android/Sdk"), nullptr),
+ NIX_SEPARATOR_STRING);
+ }
+ #endif
+
+ if (bc->ODIN_ANDROID_NDK.len != 0 && bc->ODIN_ANDROID_NDK_TOOLCHAIN.len == 0) {
+ String arch = str_lit("x86_64");
+ #if defined (GB_CPU_ARM)
+ // TODO(bill): this is a complete guess
+ arch = str_lit("aarch64");
+ #endif
+ #if defined(GB_SYSTEM_WINDOWS)
+ bc->ODIN_ANDROID_NDK_TOOLCHAIN = concatenate4_strings(temporary_allocator(), bc->ODIN_ANDROID_NDK, str_lit("toolchains/llvm/prebuilt/"), str_lit("windows-"), arch);
+ #elif defined(GB_SYSTEM_OSX)
+ // TODO(bill): is this name even correct?
+ bc->ODIN_ANDROID_NDK_TOOLCHAIN = concatenate4_strings(temporary_allocator(), bc->ODIN_ANDROID_NDK, str_lit("toolchains/llvm/prebuilt/"), str_lit("darwin-"), arch);
+ #elif defined(GB_SYSTEM_LINUX)
+ bc->ODIN_ANDROID_NDK_TOOLCHAIN = concatenate4_strings(temporary_allocator(), bc->ODIN_ANDROID_NDK, str_lit("toolchains/llvm/prebuilt/"), str_lit("linux-"), arch);
+ #endif
+
+ bc->ODIN_ANDROID_NDK_TOOLCHAIN = normalize_path(permanent_allocator(), bc->ODIN_ANDROID_NDK_TOOLCHAIN, NIX_SEPARATOR_STRING);
+ }
+
+ if (bc->ODIN_ANDROID_NDK.len == 0 && !with_sdk) {
+ gb_printf_err("Error: ODIN_ANDROID_NDK not set");
+ gb_exit(1);
+
+ }
+
+ if (bc->ODIN_ANDROID_NDK_TOOLCHAIN.len == 0 && !with_sdk) {
+ gb_printf_err("Error: ODIN_ANDROID_NDK not set");
+ gb_exit(1);
+ }
+
+ bc->ODIN_ANDROID_NDK_TOOLCHAIN_LIB = concatenate_strings(permanent_allocator(), bc->ODIN_ANDROID_NDK_TOOLCHAIN, str_lit("sysroot/usr/lib/aarch64-linux-android/"));
+
+ char buf[32] = {};
+ gb_snprintf(buf, gb_size_of(buf), "%d/", bc->ODIN_ANDROID_API_LEVEL);
+ bc->ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL = concatenate_strings(permanent_allocator(), bc->ODIN_ANDROID_NDK_TOOLCHAIN_LIB, make_string_c(buf));
+
+ bc->ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT = concatenate_strings(permanent_allocator(), bc->ODIN_ANDROID_NDK_TOOLCHAIN, str_lit("sysroot/"));
+
+
+ if (with_sdk) {
+ if (bc->ODIN_ANDROID_SDK.len == 0) {
+ gb_printf_err("Error: ODIN_ANDROID_SDK not set, which is required for -build-mode:executable for -subtarget:android");
+ gb_exit(1);
+ }
+ if (bc->android_keystore.len == 0) {
+ gb_printf_err("Error: -android-keystore: has not been set\n");
+ gb_exit(1);
+ }
+ }
+}
+
gb_internal bool has_asm_extension(String const &path) {
String ext = path_extension(path);
if (ext == ".asm") {
@@ -1628,6 +1790,30 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
bc->ODIN_WINDOWS_SUBSYSTEM = windows_subsystem_names[Windows_Subsystem_CONSOLE];
}
+ if (subtarget == Subtarget_Android) {
+ switch (build_context.build_mode) {
+ case BuildMode_DynamicLibrary:
+ case BuildMode_Object:
+ case BuildMode_Assembly:
+ case BuildMode_LLVM_IR:
+ break;
+ default:
+ case BuildMode_Executable:
+ case BuildMode_StaticLibrary:
+ if ((build_context.command_kind & Command__does_build) != 0) {
+ gb_printf_err("Unsupported -build-mode for -subtarget:android\n");
+ gb_printf_err("\tCurrently only supporting: \n");
+ // gb_printf_err("\t\texe\n");
+ gb_printf_err("\t\tshared\n");
+ gb_printf_err("\t\tobject\n");
+ gb_printf_err("\t\tassembly\n");
+ gb_printf_err("\t\tllvm-ir\n");
+ gb_exit(1);
+ }
+ break;
+ }
+ }
+
if (metrics->os == TargetOs_darwin && subtarget == Subtarget_iOS) {
switch (metrics->arch) {
case TargetArch_arm64:
@@ -1639,6 +1825,15 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
default:
GB_PANIC("Unknown architecture for darwin");
}
+ } else if (metrics->os == TargetOs_linux && subtarget == Subtarget_Android) {
+ switch (metrics->arch) {
+ case TargetArch_arm64:
+ bc->metrics.target_triplet = str_lit("aarch64-none-linux-android");
+ bc->reloc_mode = RelocMode_PIC;
+ break;
+ default:
+ GB_PANIC("Unknown architecture for -subtarget:android");
+ }
}
if (bc->metrics.os == TargetOs_windows) {
@@ -1693,6 +1888,8 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
if (subtarget == Subtarget_Default) {
bc->metrics.target_triplet = concatenate_strings(permanent_allocator(), bc->metrics.target_triplet, bc->minimum_os_version_string);
}
+ } else if (selected_subtarget == Subtarget_Android) {
+ init_android_values(bc->build_mode == BuildMode_Executable && (bc->command_kind & Command__does_build) != 0);
}
if (!bc->custom_optimization_level) {
@@ -1707,13 +1904,15 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
bc->optimization_level = gb_clamp(bc->optimization_level, -1, 3);
-#if defined(GB_SYSTEM_WINDOWS)
if (bc->optimization_level <= 0) {
if (!is_arch_wasm()) {
bc->use_separate_modules = true;
}
}
-#endif
+
+ if (build_context.use_single_module) {
+ bc->use_separate_modules = false;
+ }
// TODO: Static map calls are bugged on `amd64sysv` abi.
@@ -1854,11 +2053,6 @@ gb_internal bool init_build_paths(String init_filename) {
produces_output_file = true;
}
- if (build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR ||
- build_context.ODIN_DEFAULT_TO_PANIC_ALLOCATOR) {
- bc->no_dynamic_literals = true;
- }
-
if (!produces_output_file) {
// Command doesn't produce output files. We're done.
return true;
@@ -1937,7 +2131,10 @@ gb_internal bool init_build_paths(String init_filename) {
output_extension = make_string(nullptr, 0);
String const single_file_extension = str_lit(".odin");
- if (build_context.metrics.os == TargetOs_windows) {
+ if (selected_subtarget == Subtarget_Android) {
+ // NOTE(bill): It's always shared!
+ output_extension = STR_LIT("so");
+ } else if (build_context.metrics.os == TargetOs_windows) {
output_extension = STR_LIT("exe");
} else if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
// Do nothing: we don't want the .bin extension
@@ -2053,7 +2250,7 @@ gb_internal bool init_build_paths(String init_filename) {
// Do we have an extension? We might not if the output filename was supplied.
if (bc->build_paths[BuildPath_Output].ext.len == 0) {
- if (build_context.metrics.os == TargetOs_windows || build_context.build_mode != BuildMode_Executable) {
+ if (build_context.metrics.os == TargetOs_windows || is_arch_wasm() || build_context.build_mode != BuildMode_Executable) {
bc->build_paths[BuildPath_Output].ext = copy_string(ha, output_extension);
}
}
@@ -2117,6 +2314,7 @@ gb_internal bool init_build_paths(String init_filename) {
}
}
+ bool no_crt_checks_failed = false;
if (build_context.no_crt && !build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR && !build_context.ODIN_DEFAULT_TO_PANIC_ALLOCATOR) {
switch (build_context.metrics.os) {
case TargetOs_linux:
@@ -2126,11 +2324,29 @@ gb_internal bool init_build_paths(String init_filename) {
case TargetOs_openbsd:
case TargetOs_netbsd:
case TargetOs_haiku:
- gb_printf_err("-no-crt on unix systems requires either -default-to-nil-allocator or -default-to-panic-allocator to also be present because the default allocator requires crt\n");
- return false;
+ gb_printf_err("-no-crt on Unix systems requires either -default-to-nil-allocator or -default-to-panic-allocator to also be present, because the default allocator requires CRT\n");
+ no_crt_checks_failed = true;
}
}
+ if (build_context.no_crt && !build_context.no_thread_local) {
+ switch (build_context.metrics.os) {
+ case TargetOs_linux:
+ case TargetOs_darwin:
+ case TargetOs_essence:
+ case TargetOs_freebsd:
+ case TargetOs_openbsd:
+ case TargetOs_netbsd:
+ case TargetOs_haiku:
+ gb_printf_err("-no-crt on Unix systems requires the -no-thread-local flag to also be present, because the TLS is inaccessible without CRT\n");
+ no_crt_checks_failed = true;
+ }
+ }
+
+ if (no_crt_checks_failed) {
+ return false;
+ }
+
return true;
}
diff --git a/src/build_settings_microarch.cpp b/src/build_settings_microarch.cpp
index 8f64d4026..16fb2196f 100644
--- a/src/build_settings_microarch.cpp
+++ b/src/build_settings_microarch.cpp
@@ -1,3 +1,561 @@
+#ifndef LLVM_VERSION_MAJOR
+ #error "LLVM_VERSION_MAJOR is not defined!"
+#endif
+
+#if LLVM_VERSION_MAJOR >= 20
+// Generated with the featuregen script in `misc/featuregen`
+gb_global String target_microarch_list[TargetArch_COUNT] = {
+ // TargetArch_Invalid:
+ str_lit(""),
+ // TargetArch_amd64:
+ str_lit("alderlake,amdfam10,arrowlake,arrowlake-s,arrowlake_s,athlon,athlon-4,athlon-fx,athlon-mp,athlon-tbird,athlon-xp,athlon64,athlon64-sse3,atom,atom_sse4_2,atom_sse4_2_movbe,barcelona,bdver1,bdver2,bdver3,bdver4,bonnell,broadwell,btver1,btver2,c3,c3-2,cannonlake,cascadelake,clearwaterforest,cooperlake,core-avx-i,core-avx2,core2,core_2_duo_sse4_1,core_2_duo_ssse3,core_2nd_gen_avx,core_3rd_gen_avx,core_4th_gen_avx,core_4th_gen_avx_tsx,core_5th_gen_avx,core_5th_gen_avx_tsx,core_aes_pclmulqdq,core_i7_sse4_2,corei7,corei7-avx,diamondrapids,emeraldrapids,generic,geode,goldmont,goldmont-plus,goldmont_plus,gracemont,grandridge,graniterapids,graniterapids-d,graniterapids_d,haswell,i386,i486,i586,i686,icelake-client,icelake-server,icelake_client,icelake_server,ivybridge,k6,k6-2,k6-3,k8,k8-sse3,knl,knm,lakemont,lunarlake,meteorlake,mic_avx512,nehalem,nocona,opteron,opteron-sse3,pantherlake,penryn,pentium,pentium-m,pentium-mmx,pentium2,pentium3,pentium3m,pentium4,pentium4m,pentium_4,pentium_4_sse3,pentium_ii,pentium_iii,pentium_iii_no_xmm_regs,pentium_m,pentium_mmx,pentium_pro,pentiumpro,prescott,raptorlake,rocketlake,sandybridge,sapphirerapids,sierraforest,silvermont,skx,skylake,skylake-avx512,skylake_avx512,slm,tigerlake,tremont,westmere,winchip-c6,winchip2,x86-64,x86-64-v2,x86-64-v3,x86-64-v4,yonah,znver1,znver2,znver3,znver4,znver5"),
+ // TargetArch_i386:
+ str_lit("alderlake,amdfam10,arrowlake,arrowlake-s,arrowlake_s,athlon,athlon-4,athlon-fx,athlon-mp,athlon-tbird,athlon-xp,athlon64,athlon64-sse3,atom,atom_sse4_2,atom_sse4_2_movbe,barcelona,bdver1,bdver2,bdver3,bdver4,bonnell,broadwell,btver1,btver2,c3,c3-2,cannonlake,cascadelake,clearwaterforest,cooperlake,core-avx-i,core-avx2,core2,core_2_duo_sse4_1,core_2_duo_ssse3,core_2nd_gen_avx,core_3rd_gen_avx,core_4th_gen_avx,core_4th_gen_avx_tsx,core_5th_gen_avx,core_5th_gen_avx_tsx,core_aes_pclmulqdq,core_i7_sse4_2,corei7,corei7-avx,diamondrapids,emeraldrapids,generic,geode,goldmont,goldmont-plus,goldmont_plus,gracemont,grandridge,graniterapids,graniterapids-d,graniterapids_d,haswell,i386,i486,i586,i686,icelake-client,icelake-server,icelake_client,icelake_server,ivybridge,k6,k6-2,k6-3,k8,k8-sse3,knl,knm,lakemont,lunarlake,meteorlake,mic_avx512,nehalem,nocona,opteron,opteron-sse3,pantherlake,penryn,pentium,pentium-m,pentium-mmx,pentium2,pentium3,pentium3m,pentium4,pentium4m,pentium_4,pentium_4_sse3,pentium_ii,pentium_iii,pentium_iii_no_xmm_regs,pentium_m,pentium_mmx,pentium_pro,pentiumpro,prescott,raptorlake,rocketlake,sandybridge,sapphirerapids,sierraforest,silvermont,skx,skylake,skylake-avx512,skylake_avx512,slm,tigerlake,tremont,westmere,winchip-c6,winchip2,x86-64,x86-64-v2,x86-64-v3,x86-64-v4,yonah,znver1,znver2,znver3,znver4,znver5"),
+ // TargetArch_arm32:
+ str_lit("arm1020e,arm1020t,arm1022e,arm10e,arm10tdmi,arm1136j-s,arm1136jf-s,arm1156t2-s,arm1156t2f-s,arm1176jz-s,arm1176jzf-s,arm710t,arm720t,arm7tdmi,arm7tdmi-s,arm8,arm810,arm9,arm920,arm920t,arm922t,arm926ej-s,arm940t,arm946e-s,arm966e-s,arm968e-s,arm9e,arm9tdmi,cortex-a12,cortex-a15,cortex-a17,cortex-a32,cortex-a35,cortex-a5,cortex-a510,cortex-a53,cortex-a55,cortex-a57,cortex-a7,cortex-a710,cortex-a72,cortex-a73,cortex-a75,cortex-a76,cortex-a76ae,cortex-a77,cortex-a78,cortex-a78ae,cortex-a78c,cortex-a8,cortex-a9,cortex-m0,cortex-m0plus,cortex-m1,cortex-m23,cortex-m3,cortex-m33,cortex-m35p,cortex-m4,cortex-m52,cortex-m55,cortex-m7,cortex-m85,cortex-r4,cortex-r4f,cortex-r5,cortex-r52,cortex-r52plus,cortex-r7,cortex-r8,cortex-x1,cortex-x1c,cyclone,ep9312,exynos-m3,exynos-m4,exynos-m5,generic,iwmmxt,krait,kryo,mpcore,mpcorenovfp,neoverse-n1,neoverse-n2,neoverse-v1,sc000,sc300,star-mc1,strongarm,strongarm110,strongarm1100,strongarm1110,swift,xscale"),
+ // TargetArch_arm64:
+ str_lit("a64fx,ampere1,ampere1a,ampere1b,apple-a10,apple-a11,apple-a12,apple-a13,apple-a14,apple-a15,apple-a16,apple-a17,apple-a7,apple-a8,apple-a9,apple-m1,apple-m2,apple-m3,apple-m4,apple-s4,apple-s5,carmel,cobalt-100,cortex-a34,cortex-a35,cortex-a510,cortex-a520,cortex-a520ae,cortex-a53,cortex-a55,cortex-a57,cortex-a65,cortex-a65ae,cortex-a710,cortex-a715,cortex-a72,cortex-a720,cortex-a720ae,cortex-a725,cortex-a73,cortex-a75,cortex-a76,cortex-a76ae,cortex-a77,cortex-a78,cortex-a78ae,cortex-a78c,cortex-r82,cortex-r82ae,cortex-x1,cortex-x1c,cortex-x2,cortex-x3,cortex-x4,cortex-x925,cyclone,exynos-m3,exynos-m4,exynos-m5,falkor,fujitsu-monaka,generic,grace,kryo,neoverse-512tvb,neoverse-e1,neoverse-n1,neoverse-n2,neoverse-n3,neoverse-v1,neoverse-v2,neoverse-v3,neoverse-v3ae,oryon-1,saphira,thunderx,thunderx2t99,thunderx3t110,thunderxt81,thunderxt83,thunderxt88,tsv110"),
+ // TargetArch_wasm32:
+ str_lit("bleeding-edge,generic,lime1,mvp"),
+ // TargetArch_wasm64p32:
+ str_lit("bleeding-edge,generic,lime1,mvp"),
+ // TargetArch_riscv64:
+ str_lit("generic,generic-rv32,generic-rv64,mips-p8700,rocket,rocket-rv32,rocket-rv64,rp2350-hazard3,sifive-7-series,sifive-e20,sifive-e21,sifive-e24,sifive-e31,sifive-e34,sifive-e76,sifive-p450,sifive-p470,sifive-p550,sifive-p670,sifive-s21,sifive-s51,sifive-s54,sifive-s76,sifive-u54,sifive-u74,sifive-x280,spacemit-x60,syntacore-scr1-base,syntacore-scr1-max,syntacore-scr3-rv32,syntacore-scr3-rv64,syntacore-scr4-rv32,syntacore-scr4-rv64,syntacore-scr5-rv32,syntacore-scr5-rv64,syntacore-scr7,tt-ascalon-d8,veyron-v1,xiangshan-nanhu"),
+};
+
+// Generated with the featuregen script in `misc/featuregen`
+gb_global String target_features_list[TargetArch_COUNT] = {
+ // TargetArch_Invalid:
+ str_lit(""),
+ // TargetArch_amd64:
+ str_lit("16bit-mode,32bit-mode,64bit,64bit-mode,adx,aes,allow-light-256-bit,amx-avx512,amx-bf16,amx-complex,amx-fp16,amx-fp8,amx-int8,amx-movrs,amx-tf32,amx-tile,amx-transpose,avx,avx10.1-256,avx10.1-512,avx10.2-256,avx10.2-512,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vp2intersect,avx512vpopcntdq,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,branch-hint,branchfusion,ccmp,cf,cldemote,clflushopt,clwb,clzero,cmov,cmpccxadd,crc32,cx16,cx8,egpr,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-lzcnt-tzcnt,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-popcnt,false-deps-range,fast-11bytenop,fast-15bytenop,fast-7bytenop,fast-bextr,fast-dpwssd,fast-gather,fast-hops,fast-imm16,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fast-vector-shift-masks,faster-shift-than-shuffle,fma,fma4,fsgsbase,fsrm,fxsr,gfni,harden-sls-ijmp,harden-sls-ret,hreset,idivl-to-divb,idivq-to-divl,inline-asm-use-gpr32,invpcid,kl,lea-sp,lea-uses-ag,lvi-cfi,lvi-load-hardening,lwp,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,movrs,mwaitx,ndd,nf,no-bypass-delay,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pad-short-functions,pclmul,pconfig,pku,popcnt,ppx,prefer-128-bit,prefer-256-bit,prefer-mask-registers,prefer-movmsk-over-vtest,prefer-no-gather,prefer-no-scatter,prefetchi,prfchw,ptwrite,push2pop2,raoint,rdpid,rdpru,rdrnd,rdseed,retpoline,retpoline-external-thunk,retpoline-indirect-branches,retpoline-indirect-calls,rtm,sahf,sbb-dep-breaking,serialize,seses,sgx,sha,sha512,shstk,slow-3ops-lea,slow-incdec,slow-lea,slow-pmaddwd,slow-pmulld,slow-shld,slow-two-mem-ops,slow-unaligned-mem-16,slow-unaligned-mem-32,sm3,sm4,soft-float,sse,sse-unaligned-mem,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,tagged-globals,tbm,tsxldtrk,tuning-fast-imm-vector-shift,uintr,use-glm-div-sqrt-costs,use-slm-arith-costs,usermsr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,widekl,x87,xop,xsave,xsavec,xsaveopt,xsaves,zu"),
+ // TargetArch_i386:
+ str_lit("16bit-mode,32bit-mode,64bit,64bit-mode,adx,aes,allow-light-256-bit,amx-avx512,amx-bf16,amx-complex,amx-fp16,amx-fp8,amx-int8,amx-movrs,amx-tf32,amx-tile,amx-transpose,avx,avx10.1-256,avx10.1-512,avx10.2-256,avx10.2-512,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vp2intersect,avx512vpopcntdq,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,branch-hint,branchfusion,ccmp,cf,cldemote,clflushopt,clwb,clzero,cmov,cmpccxadd,crc32,cx16,cx8,egpr,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-lzcnt-tzcnt,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-popcnt,false-deps-range,fast-11bytenop,fast-15bytenop,fast-7bytenop,fast-bextr,fast-dpwssd,fast-gather,fast-hops,fast-imm16,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fast-vector-shift-masks,faster-shift-than-shuffle,fma,fma4,fsgsbase,fsrm,fxsr,gfni,harden-sls-ijmp,harden-sls-ret,hreset,idivl-to-divb,idivq-to-divl,inline-asm-use-gpr32,invpcid,kl,lea-sp,lea-uses-ag,lvi-cfi,lvi-load-hardening,lwp,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,movrs,mwaitx,ndd,nf,no-bypass-delay,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pad-short-functions,pclmul,pconfig,pku,popcnt,ppx,prefer-128-bit,prefer-256-bit,prefer-mask-registers,prefer-movmsk-over-vtest,prefer-no-gather,prefer-no-scatter,prefetchi,prfchw,ptwrite,push2pop2,raoint,rdpid,rdpru,rdrnd,rdseed,retpoline,retpoline-external-thunk,retpoline-indirect-branches,retpoline-indirect-calls,rtm,sahf,sbb-dep-breaking,serialize,seses,sgx,sha,sha512,shstk,slow-3ops-lea,slow-incdec,slow-lea,slow-pmaddwd,slow-pmulld,slow-shld,slow-two-mem-ops,slow-unaligned-mem-16,slow-unaligned-mem-32,sm3,sm4,soft-float,sse,sse-unaligned-mem,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,tagged-globals,tbm,tsxldtrk,tuning-fast-imm-vector-shift,uintr,use-glm-div-sqrt-costs,use-slm-arith-costs,usermsr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,widekl,x87,xop,xsave,xsavec,xsaveopt,xsaves,zu"),
+ // TargetArch_arm32:
+ str_lit("32bit,8msecext,a12,a15,a17,a32,a35,a5,a53,a55,a57,a7,a72,a73,a75,a76,a77,a78c,a8,a9,aapcs-frame-chain,aclass,acquire-release,aes,armv4,armv4t,armv5t,armv5te,armv5tej,armv6,armv6-m,armv6j,armv6k,armv6kz,armv6s-m,armv6t2,armv7-a,armv7-m,armv7-r,armv7e-m,armv7k,armv7s,armv7ve,armv8-a,armv8-m.base,armv8-m.main,armv8-r,armv8.1-a,armv8.1-m.main,armv8.2-a,armv8.3-a,armv8.4-a,armv8.5-a,armv8.6-a,armv8.7-a,armv8.8-a,armv8.9-a,armv9-a,armv9.1-a,armv9.2-a,armv9.3-a,armv9.4-a,armv9.5-a,armv9.6-a,atomics-32,avoid-movs-shop,avoid-muls,avoid-partial-cpsr,bf16,big-endian-instructions,branch-align-64,cde,cdecp0,cdecp1,cdecp2,cdecp3,cdecp4,cdecp5,cdecp6,cdecp7,cheap-predicable-cpsr,clrbhb,cortex-a510,cortex-a710,cortex-a78,cortex-a78ae,cortex-x1,cortex-x1c,crc,crypto,d32,db,dfb,disable-postra-scheduler,dont-widen-vmovs,dotprod,dsp,execute-only,expand-fp-mlx,exynos,fix-cmse-cve-2021-35465,fix-cortex-a57-aes-1742098,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp16fml,fp64,fpao,fpregs,fpregs16,fpregs64,fullfp16,fuse-aes,fuse-literals,harden-sls-blr,harden-sls-nocomdat,harden-sls-retbr,hwdiv,hwdiv-arm,i8mm,iwmmxt,iwmmxt2,krait,kryo,lob,long-calls,loop-align,m3,m55,m7,m85,mclass,mp,muxed-units,mve,mve.fp,mve1beat,mve2beat,mve4beat,nacl-trap,neon,neon-fpmovs,neonfp,neoverse-v1,no-branch-predictor,no-bti-at-return-twice,no-movt,no-neg-immediates,noarm,nonpipelined-vfp,pacbti,perfmon,prefer-ishst,prefer-vmovsr,prof-unpr,r4,r5,r52,r52plus,r7,ras,rclass,read-tp-tpidrprw,read-tp-tpidruro,read-tp-tpidrurw,reserve-r9,ret-addr-stack,sb,sha2,slow-fp-brcc,slow-load-D-subreg,slow-odd-reg,slow-vdup32,slow-vgetlni32,slowfpvfmx,slowfpvmlx,soft-float,splat-vfp-neon,strict-align,swift,thumb-mode,thumb2,trustzone,use-mipipeliner,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.1m.main,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8.8a,v8.9a,v8m,v8m.main,v9.1a,v9.2a,v9.3a,v9.4a,v9.5a,v9.6a,v9a,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization,vldn-align,vmlx-forwarding,vmlx-hazards,wide-stride-vfp,xscale,zcz"),
+ // TargetArch_arm64:
+ str_lit("CONTEXTIDREL2,a35,a510,a520,a520ae,a53,a55,a57,a64fx,a65,a710,a715,a72,a720,a720ae,a73,a75,a76,a77,a78,a78ae,a78c,addr-lsl-slow-14,aes,aggressive-fma,all,alternate-sextload-cvt-f32-pattern,altnzcv,alu-lsl-fast,am,ampere1,ampere1a,ampere1b,amvs,apple-a10,apple-a11,apple-a12,apple-a13,apple-a14,apple-a15,apple-a16,apple-a17,apple-a7,apple-m4,arith-bcc-fusion,arith-cbz-fusion,ascend-store-address,avoid-ldapur,balance-fp-ops,bf16,brbe,bti,call-saved-x10,call-saved-x11,call-saved-x12,call-saved-x13,call-saved-x14,call-saved-x15,call-saved-x18,call-saved-x8,call-saved-x9,carmel,ccdp,ccidx,ccpp,chk,clrbhb,cmp-bcc-fusion,cmpbr,complxnum,cortex-a725,cortex-r82,cortex-r82ae,cortex-x1,cortex-x2,cortex-x3,cortex-x4,cortex-x925,cpa,crc,crypto,cssc,d128,disable-latency-sched-heuristic,disable-ldp,disable-stp,dit,dotprod,ecv,el2vmsa,el3,enable-select-opt,ete,exynos-cheap-as-move,exynosm3,exynosm4,f32mm,f64mm,f8f16mm,f8f32mm,falkor,faminmax,fgt,fix-cortex-a53-835769,flagm,fmv,force-32bit-jump-tables,fp-armv8,fp16fml,fp8,fp8dot2,fp8dot4,fp8fma,fpac,fprcvt,fptoint,fujitsu-monaka,fullfp16,fuse-address,fuse-addsub-2reg-const1,fuse-adrp-add,fuse-aes,fuse-arith-logic,fuse-crypto-eor,fuse-csel,fuse-literals,gcs,harden-sls-blr,harden-sls-nocomdat,harden-sls-retbr,hbc,hcx,i8mm,ite,jsconv,kryo,ldp-aligned-only,lor,ls64,lse,lse128,lse2,lsfe,lsui,lut,mec,mops,mpam,mte,neon,neoverse512tvb,neoversee1,neoversen1,neoversen2,neoversen3,neoversev1,neoversev2,neoversev3,neoversev3AE,nmi,no-bti-at-return-twice,no-neg-immediates,no-sve-fp-ld1r,no-zcz-fp,nv,occmo,oryon-1,outline-atomics,pan,pan-rwv,pauth,pauth-lr,pcdphint,perfmon,pops,predictable-select-expensive,predres,prfm-slc-target,rand,ras,rasv2,rcpc,rcpc-immo,rcpc3,rdm,reserve-lr-for-ra,reserve-x1,reserve-x10,reserve-x11,reserve-x12,reserve-x13,reserve-x14,reserve-x15,reserve-x18,reserve-x2,reserve-x20,reserve-x21,reserve-x22,reserve-x23,reserve-x24,reserve-x25,reserve-x26,reserve-x27,reserve-x28,reserve-x3,reserve-x4,reserve-x5,reserve-x6,reserve-x7,reserve-x9,rme,saphira,sb,sel2,sha2,sha3,slow-misaligned-128store,slow-paired-128,slow-strqro-store,sm4,sme,sme-b16b16,sme-f16f16,sme-f64f64,sme-f8f16,sme-f8f32,sme-fa64,sme-i16i64,sme-lutv2,sme-mop4,sme-tmop,sme2,sme2p1,sme2p2,spe,spe-eef,specres2,specrestrict,ssbs,ssve-aes,ssve-bitperm,ssve-fp8dot2,ssve-fp8dot4,ssve-fp8fma,store-pair-suppress,stp-aligned-only,strict-align,sve,sve-aes,sve-aes2,sve-b16b16,sve-bfscale,sve-bitperm,sve-f16f32mm,sve2,sve2-aes,sve2-bitperm,sve2-sha3,sve2-sm4,sve2p1,sve2p2,tagged-globals,the,thunderx,thunderx2t99,thunderx3t110,thunderxt81,thunderxt83,thunderxt88,tlb-rmi,tlbiw,tme,tpidr-el1,tpidr-el2,tpidr-el3,tpidrro-el0,tracev8.4,trbe,tsv110,uaops,use-experimental-zeroing-pseudos,use-fixed-over-scalable-if-equal-cost,use-postra-scheduler,use-reciprocal-square-root,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8.8a,v8.9a,v8a,v8r,v9.1a,v9.2a,v9.3a,v9.4a,v9.5a,v9.6a,v9a,vh,wfxt,xs,zcm,zcz,zcz-fp-workaround,zcz-gp"),
+ // TargetArch_wasm32:
+ str_lit("atomics,bulk-memory,bulk-memory-opt,call-indirect-overlong,exception-handling,extended-const,fp16,multimemory,multivalue,mutable-globals,nontrapping-fptoint,reference-types,relaxed-simd,sign-ext,simd128,tail-call,wide-arithmetic"),
+ // TargetArch_wasm64p32:
+ str_lit("atomics,bulk-memory,bulk-memory-opt,call-indirect-overlong,exception-handling,extended-const,fp16,multimemory,multivalue,mutable-globals,nontrapping-fptoint,reference-types,relaxed-simd,sign-ext,simd128,tail-call,wide-arithmetic"),
+ // TargetArch_riscv64:
+ str_lit("32bit,64bit,a,auipc-addi-fusion,b,c,conditional-cmv-fusion,d,disable-latency-sched-heuristic,dlen-factor-2,e,experimental,experimental-rvm23u32,experimental-sdext,experimental-sdtrig,experimental-smctr,experimental-ssctr,experimental-svukte,experimental-xqcia,experimental-xqciac,experimental-xqcicli,experimental-xqcicm,experimental-xqcics,experimental-xqcicsr,experimental-xqciint,experimental-xqcilo,experimental-xqcilsm,experimental-xqcisls,experimental-zalasr,experimental-zicfilp,experimental-zicfiss,experimental-zvbc32e,experimental-zvkgs,f,forced-atomics,h,i,ld-add-fusion,lui-addi-fusion,m,mips-p8700,no-default-unroll,no-rvc-hints,no-sink-splat-operands,no-trailing-seq-cst-fence,optimized-nf2-segment-load-store,optimized-nf3-segment-load-store,optimized-nf4-segment-load-store,optimized-nf5-segment-load-store,optimized-nf6-segment-load-store,optimized-nf7-segment-load-store,optimized-nf8-segment-load-store,optimized-zero-stride-load,predictable-select-expensive,prefer-w-inst,relax,reserve-x1,reserve-x10,reserve-x11,reserve-x12,reserve-x13,reserve-x14,reserve-x15,reserve-x16,reserve-x17,reserve-x18,reserve-x19,reserve-x2,reserve-x20,reserve-x21,reserve-x22,reserve-x23,reserve-x24,reserve-x25,reserve-x26,reserve-x27,reserve-x28,reserve-x29,reserve-x3,reserve-x30,reserve-x31,reserve-x4,reserve-x5,reserve-x6,reserve-x7,reserve-x8,reserve-x9,rva20s64,rva20u64,rva22s64,rva22u64,rva23s64,rva23u64,rvb23s64,rvb23u64,rvi20u32,rvi20u64,save-restore,sha,shcounterenw,shgatpa,shifted-zextw-fusion,short-forward-branch-opt,shtvala,shvsatpa,shvstvala,shvstvecd,sifive7,smaia,smcdeleg,smcsrind,smdbltrp,smepmp,smmpm,smnpm,smrnmi,smstateen,ssaia,ssccfg,ssccptr,sscofpmf,sscounterenw,sscsrind,ssdbltrp,ssnpm,sspm,ssqosid,ssstateen,ssstrict,sstc,sstvala,sstvecd,ssu64xl,supm,svade,svadu,svbare,svinval,svnapot,svpbmt,svvptc,tagged-globals,unaligned-scalar-mem,unaligned-vector-mem,use-postra-scheduler,v,ventana-veyron,vxrm-pipeline-flush,xcvalu,xcvbi,xcvbitmanip,xcvelw,xcvmac,xcvmem,xcvsimd,xmipscmove,xmipslsp,xsfcease,xsfvcp,xsfvfnrclipxfqf,xsfvfwmaccqqq,xsfvqmaccdod,xsfvqmaccqoq,xsifivecdiscarddlone,xsifivecflushdlone,xtheadba,xtheadbb,xtheadbs,xtheadcmo,xtheadcondmov,xtheadfmemidx,xtheadmac,xtheadmemidx,xtheadmempair,xtheadsync,xtheadvdot,xventanacondops,xwchc,za128rs,za64rs,zaamo,zabha,zacas,zalrsc,zama16b,zawrs,zba,zbb,zbc,zbkb,zbkc,zbkx,zbs,zca,zcb,zcd,zce,zcf,zcmop,zcmp,zcmt,zdinx,zexth-fusion,zextw-fusion,zfa,zfbfmin,zfh,zfhmin,zfinx,zhinx,zhinxmin,zic64b,zicbom,zicbop,zicboz,ziccamoa,ziccif,zicclsm,ziccrse,zicntr,zicond,zicsr,zifencei,zihintntl,zihintpause,zihpm,zimop,zk,zkn,zknd,zkne,zknh,zkr,zks,zksed,zksh,zkt,zmmul,ztso,zvbb,zvbc,zve32f,zve32x,zve64d,zve64f,zve64x,zvfbfmin,zvfbfwma,zvfh,zvfhmin,zvkb,zvkg,zvkn,zvknc,zvkned,zvkng,zvknha,zvknhb,zvks,zvksc,zvksed,zvksg,zvksh,zvkt,zvl1024b,zvl128b,zvl16384b,zvl2048b,zvl256b,zvl32768b,zvl32b,zvl4096b,zvl512b,zvl64b,zvl65536b,zvl8192b"),
+};
+
+// Generated with the featuregen script in `misc/featuregen`
+gb_global int target_microarch_counts[TargetArch_COUNT] = {
+ // TargetArch_Invalid:
+ 0,
+ // TargetArch_amd64:
+ 129,
+ // TargetArch_i386:
+ 129,
+ // TargetArch_arm32:
+ 95,
+ // TargetArch_arm64:
+ 82,
+ // TargetArch_wasm32:
+ 4,
+ // TargetArch_wasm64p32:
+ 4,
+ // TargetArch_riscv64:
+ 39,
+};
+
+// Generated with the featuregen script in `misc/featuregen`
+gb_global MicroarchFeatureList microarch_features_list[] = {
+ // TargetArch_amd64:
+ { str_lit("alderlake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("amdfam10"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,idivq-to-divl,lzcnt,nopl,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4a,vzeroupper,x87") },
+ { str_lit("arrowlake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("arrowlake-s"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,sha512,shstk,slow-3ops-lea,sm3,sm4,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("arrowlake_s"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,sha512,shstk,slow-3ops-lea,sm3,sm4,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("athlon"), str_lit("64bit-mode,cmov,cx8,mmx,nopl,prfchw,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("athlon-4"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,prfchw,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("athlon-fx"), str_lit("64bit,64bit-mode,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,prfchw,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("athlon-mp"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,prfchw,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("athlon-tbird"), str_lit("64bit-mode,cmov,cx8,mmx,nopl,prfchw,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("athlon-xp"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,prfchw,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("athlon64"), str_lit("64bit,64bit-mode,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,prfchw,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("athlon64-sse3"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,prfchw,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") },
+ { str_lit("atom"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fast-imm16,fxsr,idivl-to-divb,idivq-to-divl,lea-sp,lea-uses-ag,mmx,movbe,no-bypass-delay,nopl,pad-short-functions,sahf,slow-two-mem-ops,slow-unaligned-mem-16,sse,sse2,sse3,ssse3,vzeroupper,x87") },
+ { str_lit("atom_sse4_2"), str_lit("64bit,64bit-mode,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-7bytenop,fast-imm16,fast-movbe,fxsr,idivq-to-divl,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,rdrnd,sahf,slow-incdec,slow-lea,slow-pmulld,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-slm-arith-costs,vzeroupper,x87") },
+ { str_lit("atom_sse4_2_movbe"), str_lit("64bit,64bit-mode,aes,clflushopt,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-7bytenop,fast-imm16,fast-movbe,fsgsbase,fxsr,idivq-to-divl,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,sha,slow-incdec,slow-lea,slow-pmulld,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-slm-arith-costs,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("barcelona"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,idivq-to-divl,lzcnt,nopl,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4a,vzeroupper,x87") },
+ { str_lit("bdver1"), str_lit("64bit,64bit-mode,aes,avx,branchfusion,cmov,crc32,cx16,cx8,fast-11bytenop,fast-scalar-shift-masks,fma4,fxsr,idivq-to-divl,lwp,lzcnt,mmx,nopl,pclmul,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vzeroupper,x87,xop,xsave") },
+ { str_lit("bdver2"), str_lit("64bit,64bit-mode,aes,avx,bmi,branchfusion,cmov,crc32,cx16,cx8,f16c,fast-11bytenop,fast-bextr,fast-movbe,fast-scalar-shift-masks,fma,fma4,fxsr,idivq-to-divl,lwp,lzcnt,mmx,nopl,pclmul,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,tbm,vzeroupper,x87,xop,xsave") },
+ { str_lit("bdver3"), str_lit("64bit,64bit-mode,aes,avx,bmi,branchfusion,cmov,crc32,cx16,cx8,f16c,fast-11bytenop,fast-bextr,fast-movbe,fast-scalar-shift-masks,fma,fma4,fsgsbase,fxsr,idivq-to-divl,lwp,lzcnt,mmx,nopl,pclmul,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,tbm,vzeroupper,x87,xop,xsave,xsaveopt") },
+ { str_lit("bdver4"), str_lit("64bit,64bit-mode,aes,avx,avx2,bmi,bmi2,branchfusion,cmov,crc32,cx16,cx8,f16c,fast-11bytenop,fast-bextr,fast-movbe,fast-scalar-shift-masks,fma,fma4,fsgsbase,fxsr,idivq-to-divl,lwp,lzcnt,mmx,movbe,mwaitx,nopl,pclmul,popcnt,prfchw,rdrnd,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,tbm,vzeroupper,x87,xop,xsave,xsaveopt") },
+ { str_lit("bonnell"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fast-imm16,fxsr,idivl-to-divb,idivq-to-divl,lea-sp,lea-uses-ag,mmx,movbe,no-bypass-delay,nopl,pad-short-functions,sahf,slow-two-mem-ops,slow-unaligned-mem-16,sse,sse2,sse3,ssse3,vzeroupper,x87") },
+ { str_lit("broadwell"), str_lit("64bit,64bit-mode,adx,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("btver1"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fast-15bytenop,fast-imm16,fast-scalar-shift-masks,fast-vector-shift-masks,fxsr,idivq-to-divl,lzcnt,mmx,nopl,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4a,ssse3,vzeroupper,x87") },
+ { str_lit("btver2"), str_lit("64bit,64bit-mode,aes,avx,bmi,cmov,crc32,cx16,cx8,f16c,fast-15bytenop,fast-bextr,fast-hops,fast-imm16,fast-lzcnt,fast-movbe,fast-scalar-shift-masks,fast-vector-shift-masks,fxsr,idivq-to-divl,lzcnt,mmx,movbe,nopl,pclmul,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,x87,xsave,xsaveopt") },
+ { str_lit("c3"), str_lit("64bit-mode,mmx,prfchw,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("c3-2"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("cannonlake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vl,bmi,bmi2,clflushopt,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,sha,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("cascadelake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,avx512vnni,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,faster-shift-than-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("clearwaterforest"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prefetchi,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,sha512,shstk,slow-3ops-lea,sm3,sm4,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,uintr,usermsr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("cooperlake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bf16,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,avx512vnni,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,faster-shift-than-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("core-avx-i"), str_lit("64bit,64bit-mode,avx,cmov,crc32,cx16,cx8,f16c,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fsgsbase,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core-avx2"), str_lit("64bit,64bit-mode,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core2"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fxsr,macrofusion,mmx,nopl,sahf,slow-unaligned-mem-16,sse,sse2,sse3,ssse3,vzeroupper,x87") },
+ { str_lit("core_2_duo_sse4_1"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fxsr,macrofusion,mmx,nopl,sahf,slow-unaligned-mem-16,sse,sse2,sse3,sse4.1,ssse3,vzeroupper,x87") },
+ { str_lit("core_2_duo_ssse3"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fxsr,macrofusion,mmx,nopl,sahf,slow-unaligned-mem-16,sse,sse2,sse3,ssse3,vzeroupper,x87") },
+ { str_lit("core_2nd_gen_avx"), str_lit("64bit,64bit-mode,avx,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core_3rd_gen_avx"), str_lit("64bit,64bit-mode,avx,cmov,crc32,cx16,cx8,f16c,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fsgsbase,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core_4th_gen_avx"), str_lit("64bit,64bit-mode,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core_4th_gen_avx_tsx"), str_lit("64bit,64bit-mode,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core_5th_gen_avx"), str_lit("64bit,64bit-mode,adx,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core_5th_gen_avx_tsx"), str_lit("64bit,64bit-mode,adx,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core_aes_pclmulqdq"), str_lit("64bit,64bit-mode,cmov,crc32,cx16,cx8,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,sahf,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") },
+ { str_lit("core_i7_sse4_2"), str_lit("64bit,64bit-mode,cmov,crc32,cx16,cx8,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,popcnt,sahf,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") },
+ { str_lit("corei7"), str_lit("64bit,64bit-mode,cmov,crc32,cx16,cx8,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,popcnt,sahf,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") },
+ { str_lit("corei7-avx"), str_lit("64bit,64bit-mode,avx,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("diamondrapids"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,amx-avx512,amx-bf16,amx-complex,amx-fp16,amx-fp8,amx-int8,amx-movrs,amx-tf32,amx-tile,amx-transpose,avx,avx10.1-256,avx10.1-512,avx10.2-256,avx10.2-512,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,branch-hint,ccmp,cf,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,egpr,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,movrs,ndd,nf,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,ppx,prefer-256-bit,prefetchi,prfchw,ptwrite,push2pop2,rdpid,rdrnd,rdseed,sahf,serialize,sha,sha512,shstk,sm3,sm4,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,usermsr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves,zu") },
+ { str_lit("emeraldrapids"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,amx-bf16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("generic"), str_lit("64bit,64bit-mode,cx8,fast-15bytenop,fast-scalar-fsqrt,idivq-to-divl,macrofusion,slow-3ops-lea,sse,sse2,vzeroupper,x87") },
+ { str_lit("geode"), str_lit("64bit-mode,cx8,mmx,prfchw,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("goldmont"), str_lit("64bit,64bit-mode,aes,clflushopt,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-imm16,fast-movbe,fsgsbase,fxsr,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,sha,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-glm-div-sqrt-costs,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("goldmont-plus"), str_lit("64bit,64bit-mode,aes,clflushopt,cmov,crc32,cx16,cx8,fast-imm16,fast-movbe,fsgsbase,fxsr,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,sha,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-glm-div-sqrt-costs,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("goldmont_plus"), str_lit("64bit,64bit-mode,aes,clflushopt,cmov,crc32,cx16,cx8,fast-imm16,fast-movbe,fsgsbase,fxsr,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,sha,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-glm-div-sqrt-costs,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("gracemont"), str_lit("64bit,64bit-mode,adx,aes,avx,avx2,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,f16c,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,nopl,pclmul,pconfig,pku,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("grandridge"), str_lit("64bit,64bit-mode,adx,aes,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,nopl,pclmul,pconfig,pku,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("graniterapids"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,amx-bf16,amx-fp16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxvnni,bmi,bmi2,branch-hint,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prefetchi,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("graniterapids-d"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,amx-bf16,amx-complex,amx-fp16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxvnni,bmi,bmi2,branch-hint,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prefetchi,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("graniterapids_d"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,amx-bf16,amx-complex,amx-fp16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxvnni,bmi,bmi2,branch-hint,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prefetchi,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("haswell"), str_lit("64bit,64bit-mode,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("i386"), str_lit("64bit-mode,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("i486"), str_lit("64bit-mode,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("i586"), str_lit("64bit-mode,cx8,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("i686"), str_lit("64bit-mode,cmov,cx8,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("icelake-client"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,clflushopt,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("icelake-server"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("icelake_client"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,clflushopt,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("icelake_server"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("ivybridge"), str_lit("64bit,64bit-mode,avx,cmov,crc32,cx16,cx8,f16c,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fsgsbase,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("k6"), str_lit("64bit-mode,cx8,mmx,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("k6-2"), str_lit("64bit-mode,cx8,mmx,prfchw,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("k6-3"), str_lit("64bit-mode,cx8,mmx,prfchw,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("k8"), str_lit("64bit,64bit-mode,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,prfchw,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("k8-sse3"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,prfchw,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") },
+ { str_lit("knl"), str_lit("64bit,64bit-mode,adx,aes,avx,avx2,avx512cd,avx512f,bmi,bmi2,cmov,crc32,cx16,cx8,evex512,f16c,fast-gather,fast-imm16,fast-movbe,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,nopl,pclmul,popcnt,prefer-mask-registers,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,slow-incdec,slow-pmaddwd,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,x87,xsave,xsaveopt") },
+ { str_lit("knm"), str_lit("64bit,64bit-mode,adx,aes,avx,avx2,avx512cd,avx512f,avx512vpopcntdq,bmi,bmi2,cmov,crc32,cx16,cx8,evex512,f16c,fast-gather,fast-imm16,fast-movbe,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,nopl,pclmul,popcnt,prefer-mask-registers,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,slow-incdec,slow-pmaddwd,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,x87,xsave,xsaveopt") },
+ { str_lit("lakemont"), str_lit("64bit-mode,cx8,slow-unaligned-mem-16,sse,sse2,vzeroupper") },
+ { str_lit("lunarlake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,sha512,shstk,slow-3ops-lea,sm3,sm4,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("meteorlake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("mic_avx512"), str_lit("64bit,64bit-mode,adx,aes,avx,avx2,avx512cd,avx512f,bmi,bmi2,cmov,crc32,cx16,cx8,evex512,f16c,fast-gather,fast-imm16,fast-movbe,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,nopl,pclmul,popcnt,prefer-mask-registers,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,slow-incdec,slow-pmaddwd,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,x87,xsave,xsaveopt") },
+ { str_lit("nehalem"), str_lit("64bit,64bit-mode,cmov,crc32,cx16,cx8,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,popcnt,sahf,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") },
+ { str_lit("nocona"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") },
+ { str_lit("opteron"), str_lit("64bit,64bit-mode,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,prfchw,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("opteron-sse3"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,prfchw,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") },
+ { str_lit("pantherlake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prefetchi,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,sha512,shstk,slow-3ops-lea,sm3,sm4,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("penryn"), str_lit("64bit,64bit-mode,cmov,cx16,cx8,fxsr,macrofusion,mmx,nopl,sahf,slow-unaligned-mem-16,sse,sse2,sse3,sse4.1,ssse3,vzeroupper,x87") },
+ { str_lit("pentium"), str_lit("64bit-mode,cx8,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium-m"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium-mmx"), str_lit("64bit-mode,cx8,mmx,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium2"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium3"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium3m"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium4"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium4m"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium_4"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium_4_sse3"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") },
+ { str_lit("pentium_ii"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium_iii"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium_iii_no_xmm_regs"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium_m"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium_mmx"), str_lit("64bit-mode,cx8,mmx,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium_pro"), str_lit("64bit-mode,cmov,cx8,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentiumpro"), str_lit("64bit-mode,cmov,cx8,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("prescott"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") },
+ { str_lit("raptorlake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("rocketlake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,clflushopt,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("sandybridge"), str_lit("64bit,64bit-mode,avx,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("sapphirerapids"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,amx-bf16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("sierraforest"), str_lit("64bit,64bit-mode,adx,aes,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,nopl,pclmul,pconfig,pku,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("silvermont"), str_lit("64bit,64bit-mode,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-7bytenop,fast-imm16,fast-movbe,fxsr,idivq-to-divl,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,rdrnd,sahf,slow-incdec,slow-lea,slow-pmulld,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-slm-arith-costs,vzeroupper,x87") },
+ { str_lit("skx"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,faster-shift-than-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("skylake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,bmi,bmi2,clflushopt,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("skylake-avx512"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,faster-shift-than-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("skylake_avx512"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,faster-shift-than-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("slm"), str_lit("64bit,64bit-mode,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-7bytenop,fast-imm16,fast-movbe,fxsr,idivq-to-divl,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,rdrnd,sahf,slow-incdec,slow-lea,slow-pmulld,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-slm-arith-costs,vzeroupper,x87") },
+ { str_lit("tigerlake"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vp2intersect,avx512vpopcntdq,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("tremont"), str_lit("64bit,64bit-mode,aes,clflushopt,clwb,cmov,crc32,cx16,cx8,fast-imm16,fast-movbe,fsgsbase,fxsr,gfni,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,sha,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-glm-div-sqrt-costs,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("westmere"), str_lit("64bit,64bit-mode,cmov,crc32,cx16,cx8,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,sahf,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") },
+ { str_lit("winchip-c6"), str_lit("64bit-mode,mmx,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("winchip2"), str_lit("64bit-mode,mmx,prfchw,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("x86-64"), str_lit("64bit,64bit-mode,cmov,cx8,fxsr,idivq-to-divl,macrofusion,mmx,nopl,slow-3ops-lea,slow-incdec,sse,sse2,vzeroupper,x87") },
+ { str_lit("x86-64-v2"), str_lit("64bit,64bit-mode,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fxsr,idivq-to-divl,macrofusion,mmx,nopl,popcnt,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") },
+ { str_lit("x86-64-v3"), str_lit("64bit,64bit-mode,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fxsr,idivq-to-divl,lzcnt,macrofusion,mmx,movbe,nopl,popcnt,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave") },
+ { str_lit("x86-64-v4"), str_lit("64bit,64bit-mode,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,bmi,bmi2,cmov,crc32,cx16,cx8,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fxsr,idivq-to-divl,lzcnt,macrofusion,mmx,movbe,nopl,popcnt,prefer-256-bit,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave") },
+ { str_lit("yonah"), str_lit("64bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") },
+ { str_lit("znver1"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,bmi,bmi2,branchfusion,clflushopt,clzero,cmov,crc32,cx16,cx8,f16c,fast-15bytenop,fast-bextr,fast-imm16,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,mwaitx,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,sbb-dep-breaking,sha,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("znver2"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,bmi,bmi2,branchfusion,clflushopt,clwb,clzero,cmov,crc32,cx16,cx8,f16c,fast-15bytenop,fast-bextr,fast-imm16,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,mwaitx,nopl,pclmul,popcnt,prfchw,rdpid,rdpru,rdrnd,rdseed,sahf,sbb-dep-breaking,sha,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("znver3"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,bmi,bmi2,branchfusion,clflushopt,clwb,clzero,cmov,crc32,cx16,cx8,f16c,fast-15bytenop,fast-bextr,fast-imm16,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,mwaitx,nopl,pclmul,pku,popcnt,prfchw,rdpid,rdpru,rdrnd,rdseed,sahf,sbb-dep-breaking,sha,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vaes,vpclmulqdq,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("znver4"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,branchfusion,clflushopt,clwb,clzero,cmov,crc32,cx16,cx8,evex512,f16c,fast-15bytenop,fast-bextr,fast-dpwssd,fast-imm16,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,mwaitx,nopl,pclmul,pku,popcnt,prfchw,rdpid,rdpru,rdrnd,rdseed,sahf,sbb-dep-breaking,sha,shstk,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vaes,vpclmulqdq,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("znver5"), str_lit("64bit,64bit-mode,adx,aes,allow-light-256-bit,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vp2intersect,avx512vpopcntdq,avxvnni,bmi,bmi2,branchfusion,clflushopt,clwb,clzero,cmov,crc32,cx16,cx8,evex512,f16c,fast-15bytenop,fast-bextr,fast-dpwssd,fast-imm16,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,mwaitx,nopl,pclmul,pku,popcnt,prefetchi,prfchw,rdpid,rdpru,rdrnd,rdseed,sahf,sbb-dep-breaking,sha,shstk,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vaes,vpclmulqdq,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ // TargetArch_i386:
+ { str_lit("alderlake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("amdfam10"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,idivq-to-divl,lzcnt,nopl,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4a,vzeroupper,x87") },
+ { str_lit("arrowlake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("arrowlake-s"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,sha512,shstk,slow-3ops-lea,sm3,sm4,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("arrowlake_s"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,sha512,shstk,slow-3ops-lea,sm3,sm4,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("athlon"), str_lit("32bit-mode,cmov,cx8,mmx,nopl,prfchw,slow-shld,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("athlon-4"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,prfchw,slow-shld,slow-unaligned-mem-16,sse,vzeroupper,x87") },
+ { str_lit("athlon-fx"), str_lit("32bit-mode,64bit,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,prfchw,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("athlon-mp"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,prfchw,slow-shld,slow-unaligned-mem-16,sse,vzeroupper,x87") },
+ { str_lit("athlon-tbird"), str_lit("32bit-mode,cmov,cx8,mmx,nopl,prfchw,slow-shld,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("athlon-xp"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,prfchw,slow-shld,slow-unaligned-mem-16,sse,vzeroupper,x87") },
+ { str_lit("athlon64"), str_lit("32bit-mode,64bit,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,prfchw,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("athlon64-sse3"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,prfchw,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") },
+ { str_lit("atom"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fast-imm16,fxsr,idivl-to-divb,idivq-to-divl,lea-sp,lea-uses-ag,mmx,movbe,no-bypass-delay,nopl,pad-short-functions,sahf,slow-two-mem-ops,slow-unaligned-mem-16,sse,sse2,sse3,ssse3,vzeroupper,x87") },
+ { str_lit("atom_sse4_2"), str_lit("32bit-mode,64bit,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-7bytenop,fast-imm16,fast-movbe,fxsr,idivq-to-divl,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,rdrnd,sahf,slow-incdec,slow-lea,slow-pmulld,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-slm-arith-costs,vzeroupper,x87") },
+ { str_lit("atom_sse4_2_movbe"), str_lit("32bit-mode,64bit,aes,clflushopt,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-7bytenop,fast-imm16,fast-movbe,fsgsbase,fxsr,idivq-to-divl,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,sha,slow-incdec,slow-lea,slow-pmulld,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-slm-arith-costs,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("barcelona"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,idivq-to-divl,lzcnt,nopl,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4a,vzeroupper,x87") },
+ { str_lit("bdver1"), str_lit("32bit-mode,64bit,aes,avx,branchfusion,cmov,crc32,cx16,cx8,fast-11bytenop,fast-scalar-shift-masks,fma4,fxsr,idivq-to-divl,lwp,lzcnt,mmx,nopl,pclmul,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vzeroupper,x87,xop,xsave") },
+ { str_lit("bdver2"), str_lit("32bit-mode,64bit,aes,avx,bmi,branchfusion,cmov,crc32,cx16,cx8,f16c,fast-11bytenop,fast-bextr,fast-movbe,fast-scalar-shift-masks,fma,fma4,fxsr,idivq-to-divl,lwp,lzcnt,mmx,nopl,pclmul,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,tbm,vzeroupper,x87,xop,xsave") },
+ { str_lit("bdver3"), str_lit("32bit-mode,64bit,aes,avx,bmi,branchfusion,cmov,crc32,cx16,cx8,f16c,fast-11bytenop,fast-bextr,fast-movbe,fast-scalar-shift-masks,fma,fma4,fsgsbase,fxsr,idivq-to-divl,lwp,lzcnt,mmx,nopl,pclmul,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,tbm,vzeroupper,x87,xop,xsave,xsaveopt") },
+ { str_lit("bdver4"), str_lit("32bit-mode,64bit,aes,avx,avx2,bmi,bmi2,branchfusion,cmov,crc32,cx16,cx8,f16c,fast-11bytenop,fast-bextr,fast-movbe,fast-scalar-shift-masks,fma,fma4,fsgsbase,fxsr,idivq-to-divl,lwp,lzcnt,mmx,movbe,mwaitx,nopl,pclmul,popcnt,prfchw,rdrnd,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,tbm,vzeroupper,x87,xop,xsave,xsaveopt") },
+ { str_lit("bonnell"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fast-imm16,fxsr,idivl-to-divb,idivq-to-divl,lea-sp,lea-uses-ag,mmx,movbe,no-bypass-delay,nopl,pad-short-functions,sahf,slow-two-mem-ops,slow-unaligned-mem-16,sse,sse2,sse3,ssse3,vzeroupper,x87") },
+ { str_lit("broadwell"), str_lit("32bit-mode,64bit,adx,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("btver1"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fast-15bytenop,fast-imm16,fast-scalar-shift-masks,fast-vector-shift-masks,fxsr,idivq-to-divl,lzcnt,mmx,nopl,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4a,ssse3,vzeroupper,x87") },
+ { str_lit("btver2"), str_lit("32bit-mode,64bit,aes,avx,bmi,cmov,crc32,cx16,cx8,f16c,fast-15bytenop,fast-bextr,fast-hops,fast-imm16,fast-lzcnt,fast-movbe,fast-scalar-shift-masks,fast-vector-shift-masks,fxsr,idivq-to-divl,lzcnt,mmx,movbe,nopl,pclmul,popcnt,prfchw,sahf,sbb-dep-breaking,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,x87,xsave,xsaveopt") },
+ { str_lit("c3"), str_lit("32bit-mode,mmx,prfchw,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("c3-2"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,slow-unaligned-mem-16,sse,vzeroupper,x87") },
+ { str_lit("cannonlake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vl,bmi,bmi2,clflushopt,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,sha,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("cascadelake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,avx512vnni,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,faster-shift-than-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("clearwaterforest"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prefetchi,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,sha512,shstk,slow-3ops-lea,sm3,sm4,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,uintr,usermsr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("cooperlake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bf16,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,avx512vnni,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,faster-shift-than-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("core-avx-i"), str_lit("32bit-mode,64bit,avx,cmov,crc32,cx16,cx8,f16c,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fsgsbase,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core-avx2"), str_lit("32bit-mode,64bit,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core2"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fxsr,macrofusion,mmx,nopl,sahf,slow-unaligned-mem-16,sse,sse2,sse3,ssse3,vzeroupper,x87") },
+ { str_lit("core_2_duo_sse4_1"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fxsr,macrofusion,mmx,nopl,sahf,slow-unaligned-mem-16,sse,sse2,sse3,sse4.1,ssse3,vzeroupper,x87") },
+ { str_lit("core_2_duo_ssse3"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fxsr,macrofusion,mmx,nopl,sahf,slow-unaligned-mem-16,sse,sse2,sse3,ssse3,vzeroupper,x87") },
+ { str_lit("core_2nd_gen_avx"), str_lit("32bit-mode,64bit,avx,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core_3rd_gen_avx"), str_lit("32bit-mode,64bit,avx,cmov,crc32,cx16,cx8,f16c,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fsgsbase,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core_4th_gen_avx"), str_lit("32bit-mode,64bit,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core_4th_gen_avx_tsx"), str_lit("32bit-mode,64bit,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core_5th_gen_avx"), str_lit("32bit-mode,64bit,adx,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core_5th_gen_avx_tsx"), str_lit("32bit-mode,64bit,adx,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("core_aes_pclmulqdq"), str_lit("32bit-mode,64bit,cmov,crc32,cx16,cx8,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,sahf,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") },
+ { str_lit("core_i7_sse4_2"), str_lit("32bit-mode,64bit,cmov,crc32,cx16,cx8,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,popcnt,sahf,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") },
+ { str_lit("corei7"), str_lit("32bit-mode,64bit,cmov,crc32,cx16,cx8,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,popcnt,sahf,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") },
+ { str_lit("corei7-avx"), str_lit("32bit-mode,64bit,avx,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("diamondrapids"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,amx-avx512,amx-bf16,amx-complex,amx-fp16,amx-fp8,amx-int8,amx-movrs,amx-tf32,amx-tile,amx-transpose,avx,avx10.1-256,avx10.1-512,avx10.2-256,avx10.2-512,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,branch-hint,ccmp,cf,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,egpr,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,movrs,ndd,nf,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,ppx,prefer-256-bit,prefetchi,prfchw,ptwrite,push2pop2,rdpid,rdrnd,rdseed,sahf,serialize,sha,sha512,shstk,sm3,sm4,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,usermsr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves,zu") },
+ { str_lit("emeraldrapids"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,amx-bf16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("generic"), str_lit("32bit-mode,64bit,cx8,fast-15bytenop,fast-scalar-fsqrt,idivq-to-divl,macrofusion,slow-3ops-lea,vzeroupper,x87") },
+ { str_lit("geode"), str_lit("32bit-mode,cx8,mmx,prfchw,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("goldmont"), str_lit("32bit-mode,64bit,aes,clflushopt,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-imm16,fast-movbe,fsgsbase,fxsr,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,sha,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-glm-div-sqrt-costs,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("goldmont-plus"), str_lit("32bit-mode,64bit,aes,clflushopt,cmov,crc32,cx16,cx8,fast-imm16,fast-movbe,fsgsbase,fxsr,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,sha,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-glm-div-sqrt-costs,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("goldmont_plus"), str_lit("32bit-mode,64bit,aes,clflushopt,cmov,crc32,cx16,cx8,fast-imm16,fast-movbe,fsgsbase,fxsr,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,sha,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-glm-div-sqrt-costs,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("gracemont"), str_lit("32bit-mode,64bit,adx,aes,avx,avx2,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,f16c,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,nopl,pclmul,pconfig,pku,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("grandridge"), str_lit("32bit-mode,64bit,adx,aes,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,nopl,pclmul,pconfig,pku,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("graniterapids"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,amx-bf16,amx-fp16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxvnni,bmi,bmi2,branch-hint,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prefetchi,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("graniterapids-d"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,amx-bf16,amx-complex,amx-fp16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxvnni,bmi,bmi2,branch-hint,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prefetchi,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("graniterapids_d"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,amx-bf16,amx-complex,amx-fp16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxvnni,bmi,bmi2,branch-hint,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prefetchi,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("haswell"), str_lit("32bit-mode,64bit,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("i386"), str_lit("32bit-mode,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("i486"), str_lit("32bit-mode,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("i586"), str_lit("32bit-mode,cx8,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("i686"), str_lit("32bit-mode,cmov,cx8,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("icelake-client"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,clflushopt,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("icelake-server"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("icelake_client"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,clflushopt,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("icelake_server"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("ivybridge"), str_lit("32bit-mode,64bit,avx,cmov,crc32,cx16,cx8,f16c,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fsgsbase,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,rdrnd,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("k6"), str_lit("32bit-mode,cx8,mmx,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("k6-2"), str_lit("32bit-mode,cx8,mmx,prfchw,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("k6-3"), str_lit("32bit-mode,cx8,mmx,prfchw,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("k8"), str_lit("32bit-mode,64bit,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,prfchw,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("k8-sse3"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,prfchw,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") },
+ { str_lit("knl"), str_lit("32bit-mode,64bit,adx,aes,avx,avx2,avx512cd,avx512f,bmi,bmi2,cmov,crc32,cx16,cx8,evex512,f16c,fast-gather,fast-imm16,fast-movbe,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,nopl,pclmul,popcnt,prefer-mask-registers,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,slow-incdec,slow-pmaddwd,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,x87,xsave,xsaveopt") },
+ { str_lit("knm"), str_lit("32bit-mode,64bit,adx,aes,avx,avx2,avx512cd,avx512f,avx512vpopcntdq,bmi,bmi2,cmov,crc32,cx16,cx8,evex512,f16c,fast-gather,fast-imm16,fast-movbe,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,nopl,pclmul,popcnt,prefer-mask-registers,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,slow-incdec,slow-pmaddwd,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,x87,xsave,xsaveopt") },
+ { str_lit("lakemont"), str_lit("32bit-mode,cx8,slow-unaligned-mem-16,vzeroupper") },
+ { str_lit("lunarlake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,sha512,shstk,slow-3ops-lea,sm3,sm4,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("meteorlake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("mic_avx512"), str_lit("32bit-mode,64bit,adx,aes,avx,avx2,avx512cd,avx512f,bmi,bmi2,cmov,crc32,cx16,cx8,evex512,f16c,fast-gather,fast-imm16,fast-movbe,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,nopl,pclmul,popcnt,prefer-mask-registers,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,slow-incdec,slow-pmaddwd,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,x87,xsave,xsaveopt") },
+ { str_lit("nehalem"), str_lit("32bit-mode,64bit,cmov,crc32,cx16,cx8,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,popcnt,sahf,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") },
+ { str_lit("nocona"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") },
+ { str_lit("opteron"), str_lit("32bit-mode,64bit,cmov,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,prfchw,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("opteron-sse3"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fast-scalar-shift-masks,fxsr,mmx,nopl,prfchw,sbb-dep-breaking,slow-shld,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") },
+ { str_lit("pantherlake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint16,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prefetchi,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,sha512,shstk,slow-3ops-lea,sm3,sm4,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("penryn"), str_lit("32bit-mode,64bit,cmov,cx16,cx8,fxsr,macrofusion,mmx,nopl,sahf,slow-unaligned-mem-16,sse,sse2,sse3,sse4.1,ssse3,vzeroupper,x87") },
+ { str_lit("pentium"), str_lit("32bit-mode,cx8,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("pentium-m"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium-mmx"), str_lit("32bit-mode,cx8,mmx,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("pentium2"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("pentium3"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,vzeroupper,x87") },
+ { str_lit("pentium3m"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,vzeroupper,x87") },
+ { str_lit("pentium4"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium4m"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium_4"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium_4_sse3"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") },
+ { str_lit("pentium_ii"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("pentium_iii"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,vzeroupper,x87") },
+ { str_lit("pentium_iii_no_xmm_regs"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,vzeroupper,x87") },
+ { str_lit("pentium_m"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,vzeroupper,x87") },
+ { str_lit("pentium_mmx"), str_lit("32bit-mode,cx8,mmx,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("pentium_pro"), str_lit("32bit-mode,cmov,cx8,nopl,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("pentiumpro"), str_lit("32bit-mode,cmov,cx8,nopl,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("prescott"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") },
+ { str_lit("raptorlake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,f16c,false-deps-perm,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,idivq-to-divl,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-movmsk-over-vtest,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("rocketlake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,clflushopt,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("sandybridge"), str_lit("32bit-mode,64bit,avx,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsaveopt") },
+ { str_lit("sapphirerapids"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,amx-bf16,amx-int8,amx-tile,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512fp16,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,avxvnni,bmi,bmi2,cldemote,clflushopt,clwb,cmov,crc32,cx16,cx8,enqcmd,ermsb,evex512,f16c,false-deps-getmant,false-deps-mulc,false-deps-mullq,false-deps-perm,false-deps-range,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pconfig,pku,popcnt,prefer-256-bit,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tsxldtrk,tuning-fast-imm-vector-shift,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("sierraforest"), str_lit("32bit-mode,64bit,adx,aes,avx,avx2,avxifma,avxneconvert,avxvnni,avxvnniint8,bmi,bmi2,cldemote,clflushopt,clwb,cmov,cmpccxadd,crc32,cx16,cx8,enqcmd,f16c,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,gfni,hreset,invpcid,kl,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,nopl,pclmul,pconfig,pku,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,serialize,sha,shstk,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,uintr,vaes,vpclmulqdq,vzeroupper,waitpkg,widekl,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("silvermont"), str_lit("32bit-mode,64bit,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-7bytenop,fast-imm16,fast-movbe,fxsr,idivq-to-divl,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,rdrnd,sahf,slow-incdec,slow-lea,slow-pmulld,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-slm-arith-costs,vzeroupper,x87") },
+ { str_lit("skx"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,faster-shift-than-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("skylake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,bmi,bmi2,clflushopt,cmov,crc32,cx16,cx8,ermsb,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("skylake-avx512"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,faster-shift-than-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("skylake_avx512"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,faster-shift-than-shuffle,fma,fsgsbase,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdrnd,rdseed,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("slm"), str_lit("32bit-mode,64bit,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-7bytenop,fast-imm16,fast-movbe,fxsr,idivq-to-divl,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,rdrnd,sahf,slow-incdec,slow-lea,slow-pmulld,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-slm-arith-costs,vzeroupper,x87") },
+ { str_lit("tigerlake"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vp2intersect,avx512vpopcntdq,bmi,bmi2,clflushopt,clwb,cmov,crc32,cx16,cx8,ermsb,evex512,f16c,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,no-bypass-delay-blend,no-bypass-delay-mov,no-bypass-delay-shuffle,nopl,pclmul,pku,popcnt,prefer-256-bit,prfchw,rdpid,rdrnd,rdseed,sahf,sha,shstk,sse,sse2,sse3,sse4.1,sse4.2,ssse3,tuning-fast-imm-vector-shift,vaes,vpclmulqdq,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("tremont"), str_lit("32bit-mode,64bit,aes,clflushopt,clwb,cmov,crc32,cx16,cx8,fast-imm16,fast-movbe,fsgsbase,fxsr,gfni,mmx,movbe,no-bypass-delay,nopl,pclmul,popcnt,prfchw,ptwrite,rdpid,rdrnd,rdseed,sahf,sha,slow-incdec,slow-lea,slow-two-mem-ops,sse,sse2,sse3,sse4.1,sse4.2,ssse3,use-glm-div-sqrt-costs,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("westmere"), str_lit("32bit-mode,64bit,cmov,crc32,cx16,cx8,fxsr,idivq-to-divl,macrofusion,mmx,no-bypass-delay-mov,nopl,pclmul,popcnt,sahf,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") },
+ { str_lit("winchip-c6"), str_lit("32bit-mode,mmx,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("winchip2"), str_lit("32bit-mode,mmx,prfchw,slow-unaligned-mem-16,vzeroupper,x87") },
+ { str_lit("x86-64"), str_lit("32bit-mode,64bit,cmov,cx8,fxsr,idivq-to-divl,macrofusion,mmx,nopl,slow-3ops-lea,slow-incdec,sse,sse2,vzeroupper,x87") },
+ { str_lit("x86-64-v2"), str_lit("32bit-mode,64bit,cmov,crc32,cx16,cx8,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fxsr,idivq-to-divl,macrofusion,mmx,nopl,popcnt,sahf,slow-3ops-lea,slow-unaligned-mem-32,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87") },
+ { str_lit("x86-64-v3"), str_lit("32bit-mode,64bit,allow-light-256-bit,avx,avx2,bmi,bmi2,cmov,crc32,cx16,cx8,f16c,false-deps-lzcnt-tzcnt,false-deps-popcnt,fast-15bytenop,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fma,fxsr,idivq-to-divl,lzcnt,macrofusion,mmx,movbe,nopl,popcnt,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave") },
+ { str_lit("x86-64-v4"), str_lit("32bit-mode,64bit,allow-light-256-bit,avx,avx2,avx512bw,avx512cd,avx512dq,avx512f,avx512vl,bmi,bmi2,cmov,crc32,cx16,cx8,evex512,f16c,false-deps-popcnt,fast-15bytenop,fast-gather,fast-scalar-fsqrt,fast-shld-rotate,fast-variable-crosslane-shuffle,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fxsr,idivq-to-divl,lzcnt,macrofusion,mmx,movbe,nopl,popcnt,prefer-256-bit,sahf,slow-3ops-lea,sse,sse2,sse3,sse4.1,sse4.2,ssse3,vzeroupper,x87,xsave") },
+ { str_lit("yonah"), str_lit("32bit-mode,cmov,cx8,fxsr,mmx,nopl,slow-unaligned-mem-16,sse,sse2,sse3,vzeroupper,x87") },
+ { str_lit("znver1"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,bmi,bmi2,branchfusion,clflushopt,clzero,cmov,crc32,cx16,cx8,f16c,fast-15bytenop,fast-bextr,fast-imm16,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,mwaitx,nopl,pclmul,popcnt,prfchw,rdrnd,rdseed,sahf,sbb-dep-breaking,sha,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vzeroupper,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("znver2"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,bmi,bmi2,branchfusion,clflushopt,clwb,clzero,cmov,crc32,cx16,cx8,f16c,fast-15bytenop,fast-bextr,fast-imm16,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fxsr,idivq-to-divl,lzcnt,mmx,movbe,mwaitx,nopl,pclmul,popcnt,prfchw,rdpid,rdpru,rdrnd,rdseed,sahf,sbb-dep-breaking,sha,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("znver3"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,bmi,bmi2,branchfusion,clflushopt,clwb,clzero,cmov,crc32,cx16,cx8,f16c,fast-15bytenop,fast-bextr,fast-imm16,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,mwaitx,nopl,pclmul,pku,popcnt,prfchw,rdpid,rdpru,rdrnd,rdseed,sahf,sbb-dep-breaking,sha,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vaes,vpclmulqdq,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("znver4"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vpopcntdq,bmi,bmi2,branchfusion,clflushopt,clwb,clzero,cmov,crc32,cx16,cx8,evex512,f16c,fast-15bytenop,fast-bextr,fast-dpwssd,fast-imm16,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,mwaitx,nopl,pclmul,pku,popcnt,prfchw,rdpid,rdpru,rdrnd,rdseed,sahf,sbb-dep-breaking,sha,shstk,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vaes,vpclmulqdq,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ { str_lit("znver5"), str_lit("32bit-mode,64bit,adx,aes,allow-light-256-bit,avx,avx2,avx512bf16,avx512bitalg,avx512bw,avx512cd,avx512dq,avx512f,avx512ifma,avx512vbmi,avx512vbmi2,avx512vl,avx512vnni,avx512vp2intersect,avx512vpopcntdq,avxvnni,bmi,bmi2,branchfusion,clflushopt,clwb,clzero,cmov,crc32,cx16,cx8,evex512,f16c,fast-15bytenop,fast-bextr,fast-dpwssd,fast-imm16,fast-lzcnt,fast-movbe,fast-scalar-fsqrt,fast-scalar-shift-masks,fast-variable-perlane-shuffle,fast-vector-fsqrt,fma,fsgsbase,fsrm,fxsr,gfni,idivq-to-divl,invpcid,lzcnt,macrofusion,mmx,movbe,movdir64b,movdiri,mwaitx,nopl,pclmul,pku,popcnt,prefetchi,prfchw,rdpid,rdpru,rdrnd,rdseed,sahf,sbb-dep-breaking,sha,shstk,slow-shld,sse,sse2,sse3,sse4.1,sse4.2,sse4a,ssse3,vaes,vpclmulqdq,vzeroupper,wbnoinvd,x87,xsave,xsavec,xsaveopt,xsaves") },
+ // TargetArch_arm32:
+ { str_lit("arm1020e"), str_lit("armv5te,v4t,v5t,v5te") },
+ { str_lit("arm1020t"), str_lit("armv5t,v4t,v5t") },
+ { str_lit("arm1022e"), str_lit("armv5te,v4t,v5t,v5te") },
+ { str_lit("arm10e"), str_lit("armv5te,v4t,v5t,v5te") },
+ { str_lit("arm10tdmi"), str_lit("armv5t,v4t,v5t") },
+ { str_lit("arm1136j-s"), str_lit("armv6,dsp,v4t,v5t,v5te,v6") },
+ { str_lit("arm1136jf-s"), str_lit("armv6,dsp,fp64,fpregs,fpregs64,slowfpvmlx,v4t,v5t,v5te,v6,vfp2,vfp2sp") },
+ { str_lit("arm1156t2-s"), str_lit("armv6t2,dsp,thumb2,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v8m") },
+ { str_lit("arm1156t2f-s"), str_lit("armv6t2,dsp,fp64,fpregs,fpregs64,slowfpvmlx,thumb2,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v8m,vfp2,vfp2sp") },
+ { str_lit("arm1176jz-s"), str_lit("armv6kz,trustzone,v4t,v5t,v5te,v6,v6k") },
+ { str_lit("arm1176jzf-s"), str_lit("armv6kz,fp64,fpregs,fpregs64,slowfpvmlx,trustzone,v4t,v5t,v5te,v6,v6k,vfp2,vfp2sp") },
+ { str_lit("arm710t"), str_lit("armv4t,v4t") },
+ { str_lit("arm720t"), str_lit("armv4t,v4t") },
+ { str_lit("arm7tdmi"), str_lit("armv4t,v4t") },
+ { str_lit("arm7tdmi-s"), str_lit("armv4t,v4t") },
+ { str_lit("arm8"), str_lit("armv4") },
+ { str_lit("arm810"), str_lit("armv4") },
+ { str_lit("arm9"), str_lit("armv4t,v4t") },
+ { str_lit("arm920"), str_lit("armv4t,v4t") },
+ { str_lit("arm920t"), str_lit("armv4t,v4t") },
+ { str_lit("arm922t"), str_lit("armv4t,v4t") },
+ { str_lit("arm926ej-s"), str_lit("armv5te,v4t,v5t,v5te") },
+ { str_lit("arm940t"), str_lit("armv4t,v4t") },
+ { str_lit("arm946e-s"), str_lit("armv5te,v4t,v5t,v5te") },
+ { str_lit("arm966e-s"), str_lit("armv5te,v4t,v5t,v5te") },
+ { str_lit("arm968e-s"), str_lit("armv5te,v4t,v5t,v5te") },
+ { str_lit("arm9e"), str_lit("armv5te,v4t,v5t,v5te") },
+ { str_lit("arm9tdmi"), str_lit("armv4t,v4t") },
+ { str_lit("cortex-a12"), str_lit("a12,aclass,armv7-a,avoid-partial-cpsr,d32,db,dsp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,ret-addr-stack,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization,vmlx-forwarding") },
+ { str_lit("cortex-a15"), str_lit("a15,aclass,armv7-a,avoid-partial-cpsr,d32,db,dont-widen-vmovs,dsp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,muxed-units,neon,perfmon,ret-addr-stack,splat-vfp-neon,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization,vldn-align") },
+ { str_lit("cortex-a17"), str_lit("a17,aclass,armv7-a,avoid-partial-cpsr,d32,db,dsp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,ret-addr-stack,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization,vmlx-forwarding") },
+ { str_lit("cortex-a32"), str_lit("aclass,acquire-release,aes,armv8-a,crc,crypto,d32,db,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a35"), str_lit("a35,aclass,acquire-release,aes,armv8-a,crc,crypto,d32,db,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a5"), str_lit("a5,aclass,armv7-a,d32,db,dsp,fp16,fp64,fpregs,fpregs64,mp,neon,perfmon,ret-addr-stack,slow-fp-brcc,slowfpvfmx,slowfpvmlx,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,vmlx-forwarding") },
+ { str_lit("cortex-a510"), str_lit("aclass,acquire-release,armv9-a,bf16,cortex-a710,crc,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp16fml,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,i8mm,mp,neon,perfmon,ras,sb,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8m,v9a,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a53"), str_lit("a53,aclass,acquire-release,aes,armv8-a,crc,crypto,d32,db,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpao,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a55"), str_lit("a55,aclass,acquire-release,aes,armv8.2-a,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a57"), str_lit("a57,aclass,acquire-release,aes,armv8-a,avoid-partial-cpsr,cheap-predicable-cpsr,crc,crypto,d32,db,dsp,fix-cortex-a57-aes-1742098,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpao,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a7"), str_lit("a7,aclass,armv7-a,d32,db,dsp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,ret-addr-stack,slow-fp-brcc,slowfpvfmx,slowfpvmlx,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization,vmlx-forwarding,vmlx-hazards") },
+ { str_lit("cortex-a710"), str_lit("aclass,acquire-release,armv9-a,bf16,cortex-a710,crc,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp16fml,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,i8mm,mp,neon,perfmon,ras,sb,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8m,v9a,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a72"), str_lit("a72,aclass,acquire-release,aes,armv8-a,crc,crypto,d32,db,dsp,fix-cortex-a57-aes-1742098,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a73"), str_lit("a73,aclass,acquire-release,aes,armv8-a,crc,crypto,d32,db,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a75"), str_lit("a75,aclass,acquire-release,aes,armv8.2-a,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a76"), str_lit("a76,aclass,acquire-release,aes,armv8.2-a,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a76ae"), str_lit("a76,aclass,acquire-release,aes,armv8.2-a,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a77"), str_lit("a77,aclass,acquire-release,aes,armv8.2-a,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a78"), str_lit("aclass,acquire-release,aes,armv8.2-a,cortex-a78,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a78ae"), str_lit("aclass,acquire-release,aes,armv8.2-a,cortex-a78ae,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a78c"), str_lit("a78c,aclass,acquire-release,aes,armv8.2-a,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-a8"), str_lit("a8,aclass,armv7-a,d32,db,dsp,fp64,fpregs,fpregs64,neon,nonpipelined-vfp,perfmon,ret-addr-stack,slow-fp-brcc,slowfpvfmx,slowfpvmlx,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vmlx-forwarding,vmlx-hazards") },
+ { str_lit("cortex-a9"), str_lit("a9,aclass,armv7-a,avoid-partial-cpsr,d32,db,dsp,expand-fp-mlx,fp16,fp64,fpregs,fpregs64,mp,muxed-units,neon,neon-fpmovs,perfmon,prefer-vmovsr,ret-addr-stack,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vldn-align,vmlx-forwarding,vmlx-hazards") },
+ { str_lit("cortex-m0"), str_lit("armv6-m,db,mclass,no-branch-predictor,noarm,strict-align,thumb-mode,v4t,v5t,v5te,v6,v6m") },
+ { str_lit("cortex-m0plus"), str_lit("armv6-m,db,mclass,no-branch-predictor,noarm,strict-align,thumb-mode,v4t,v5t,v5te,v6,v6m") },
+ { str_lit("cortex-m1"), str_lit("armv6-m,db,mclass,no-branch-predictor,noarm,strict-align,thumb-mode,v4t,v5t,v5te,v6,v6m") },
+ { str_lit("cortex-m23"), str_lit("8msecext,acquire-release,armv8-m.base,db,hwdiv,mclass,no-branch-predictor,no-movt,noarm,strict-align,thumb-mode,v4t,v5t,v5te,v6,v6m,v7clrex,v8m") },
+ { str_lit("cortex-m3"), str_lit("armv7-m,db,hwdiv,loop-align,m3,mclass,no-branch-predictor,noarm,thumb-mode,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m") },
+ { str_lit("cortex-m33"), str_lit("8msecext,acquire-release,armv8-m.main,avoid-muls,db,dsp,fix-cmse-cve-2021-35465,fp-armv8d16sp,fp16,fpregs,hwdiv,loop-align,mclass,no-branch-predictor,noarm,slowfpvfmx,slowfpvmlx,thumb-mode,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,v8m.main,vfp2sp,vfp3d16sp,vfp4d16sp") },
+ { str_lit("cortex-m35p"), str_lit("8msecext,acquire-release,armv8-m.main,db,dsp,fix-cmse-cve-2021-35465,fp-armv8d16sp,fp16,fpregs,hwdiv,loop-align,mclass,no-branch-predictor,noarm,slowfpvfmx,slowfpvmlx,thumb-mode,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,v8m.main,vfp2sp,vfp3d16sp,vfp4d16sp") },
+ { str_lit("cortex-m4"), str_lit("armv7e-m,db,dsp,fp16,fpregs,hwdiv,loop-align,mclass,no-branch-predictor,noarm,slowfpvfmx,slowfpvmlx,thumb-mode,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2sp,vfp3d16sp,vfp4d16sp") },
+ { str_lit("cortex-m52"), str_lit("8msecext,acquire-release,armv8.1-m.main,db,dsp,fp-armv8d16,fp-armv8d16sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,lob,loop-align,mclass,mve,mve.fp,mve1beat,no-branch-predictor,noarm,pacbti,ras,slowfpvmlx,thumb-mode,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8.1m.main,v8m,v8m.main,vfp2,vfp2sp,vfp3d16,vfp3d16sp,vfp4d16,vfp4d16sp") },
+ { str_lit("cortex-m55"), str_lit("8msecext,acquire-release,armv8.1-m.main,db,dsp,fix-cmse-cve-2021-35465,fp-armv8d16,fp-armv8d16sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,lob,loop-align,m55,mclass,mve,mve.fp,no-branch-predictor,noarm,ras,slowfpvmlx,thumb-mode,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8.1m.main,v8m,v8m.main,vfp2,vfp2sp,vfp3d16,vfp3d16sp,vfp4d16,vfp4d16sp") },
+ { str_lit("cortex-m7"), str_lit("armv7e-m,branch-align-64,db,dsp,fp-armv8d16,fp-armv8d16sp,fp16,fp64,fpregs,fpregs64,hwdiv,m7,mclass,noarm,thumb-mode,thumb2,use-mipipeliner,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3d16,vfp3d16sp,vfp4d16,vfp4d16sp") },
+ { str_lit("cortex-m85"), str_lit("8msecext,acquire-release,armv8.1-m.main,branch-align-64,db,dsp,fp-armv8d16,fp-armv8d16sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,lob,m85,mclass,mve,mve.fp,noarm,pacbti,ras,thumb-mode,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8.1m.main,v8m,v8m.main,vfp2,vfp2sp,vfp3d16,vfp3d16sp,vfp4d16,vfp4d16sp") },
+ { str_lit("cortex-r4"), str_lit("armv7-r,avoid-partial-cpsr,db,dsp,hwdiv,perfmon,r4,rclass,ret-addr-stack,thumb2,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m") },
+ { str_lit("cortex-r4f"), str_lit("armv7-r,avoid-partial-cpsr,db,dsp,fp64,fpregs,fpregs64,hwdiv,perfmon,r4,rclass,ret-addr-stack,slow-fp-brcc,slowfpvfmx,slowfpvmlx,thumb2,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3d16,vfp3d16sp") },
+ { str_lit("cortex-r5"), str_lit("armv7-r,avoid-partial-cpsr,db,dsp,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,perfmon,r5,rclass,ret-addr-stack,slow-fp-brcc,slowfpvfmx,slowfpvmlx,thumb2,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3d16,vfp3d16sp") },
+ { str_lit("cortex-r52"), str_lit("acquire-release,armv8-r,crc,d32,db,dfb,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpao,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,r52,rclass,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-r52plus"), str_lit("acquire-release,armv8-r,crc,d32,db,dfb,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpao,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,r52plus,rclass,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-r7"), str_lit("armv7-r,avoid-partial-cpsr,db,dsp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,perfmon,r7,rclass,ret-addr-stack,slow-fp-brcc,slowfpvfmx,slowfpvmlx,thumb2,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3d16,vfp3d16sp") },
+ { str_lit("cortex-r8"), str_lit("armv7-r,avoid-partial-cpsr,db,dsp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,perfmon,rclass,ret-addr-stack,slow-fp-brcc,slowfpvfmx,slowfpvmlx,thumb2,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3d16,vfp3d16sp") },
+ { str_lit("cortex-x1"), str_lit("aclass,acquire-release,aes,armv8.2-a,cortex-x1,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cortex-x1c"), str_lit("aclass,acquire-release,aes,armv8.2-a,cortex-x1c,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("cyclone"), str_lit("aclass,acquire-release,aes,armv8-a,avoid-movs-shop,avoid-partial-cpsr,crc,crypto,d32,db,disable-postra-scheduler,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,neonfp,perfmon,ret-addr-stack,sha2,slowfpvfmx,slowfpvmlx,swift,thumb2,trustzone,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization,zcz") },
+ { str_lit("ep9312"), str_lit("armv4t,v4t") },
+ { str_lit("exynos-m3"), str_lit("aclass,acquire-release,aes,armv8-a,crc,crypto,d32,db,dont-widen-vmovs,dsp,expand-fp-mlx,exynos,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,fuse-aes,fuse-literals,hwdiv,hwdiv-arm,mp,neon,perfmon,prof-unpr,ret-addr-stack,sha2,slow-fp-brcc,slow-vdup32,slow-vgetlni32,slowfpvfmx,slowfpvmlx,splat-vfp-neon,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization,wide-stride-vfp,zcz") },
+ { str_lit("exynos-m4"), str_lit("aclass,acquire-release,aes,armv8.2-a,crc,crypto,d32,db,dont-widen-vmovs,dotprod,dsp,expand-fp-mlx,exynos,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,fuse-aes,fuse-literals,hwdiv,hwdiv-arm,mp,neon,perfmon,prof-unpr,ras,ret-addr-stack,sha2,slow-fp-brcc,slow-vdup32,slow-vgetlni32,slowfpvfmx,slowfpvmlx,splat-vfp-neon,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization,wide-stride-vfp,zcz") },
+ { str_lit("exynos-m5"), str_lit("aclass,acquire-release,aes,armv8.2-a,crc,crypto,d32,db,dont-widen-vmovs,dotprod,dsp,expand-fp-mlx,exynos,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,fuse-aes,fuse-literals,hwdiv,hwdiv-arm,mp,neon,perfmon,prof-unpr,ras,ret-addr-stack,sha2,slow-fp-brcc,slow-vdup32,slow-vgetlni32,slowfpvfmx,slowfpvmlx,splat-vfp-neon,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization,wide-stride-vfp,zcz") },
+ { str_lit("generic"), str_lit("") },
+ { str_lit("iwmmxt"), str_lit("armv5te,v4t,v5t,v5te") },
+ { str_lit("krait"), str_lit("aclass,armv7-a,avoid-partial-cpsr,d32,db,dsp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,krait,muxed-units,neon,perfmon,ret-addr-stack,thumb2,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,vldn-align,vmlx-forwarding") },
+ { str_lit("kryo"), str_lit("aclass,acquire-release,aes,armv8-a,crc,crypto,d32,db,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,kryo,mp,neon,perfmon,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("mpcore"), str_lit("armv6k,fp64,fpregs,fpregs64,slowfpvmlx,v4t,v5t,v5te,v6,v6k,vfp2,vfp2sp") },
+ { str_lit("mpcorenovfp"), str_lit("armv6k,v4t,v5t,v5te,v6,v6k") },
+ { str_lit("neoverse-n1"), str_lit("aclass,acquire-release,aes,armv8.2-a,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("neoverse-n2"), str_lit("aclass,acquire-release,armv9-a,bf16,crc,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp16fml,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,i8mm,mp,neon,perfmon,ras,sb,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8m,v9a,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("neoverse-v1"), str_lit("aclass,acquire-release,aes,armv8.4-a,bf16,crc,crypto,d32,db,dotprod,dsp,fp-armv8,fp-armv8d16,fp-armv8d16sp,fp-armv8sp,fp16,fp64,fpregs,fpregs16,fpregs64,fullfp16,hwdiv,hwdiv-arm,i8mm,mp,neon,perfmon,ras,sha2,thumb2,trustzone,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8,v8.1a,v8.2a,v8.3a,v8.4a,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,virtualization") },
+ { str_lit("sc000"), str_lit("armv6-m,db,mclass,no-branch-predictor,noarm,strict-align,thumb-mode,v4t,v5t,v5te,v6,v6m") },
+ { str_lit("sc300"), str_lit("armv7-m,db,hwdiv,m3,mclass,no-branch-predictor,noarm,thumb-mode,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m") },
+ { str_lit("star-mc1"), str_lit("8msecext,acquire-release,armv8-m.main,db,dsp,fix-cmse-cve-2021-35465,fp-armv8d16sp,fp16,fpregs,hwdiv,loop-align,mclass,no-branch-predictor,noarm,slowfpvfmx,slowfpvmlx,thumb-mode,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,v8m.main,vfp2sp,vfp3d16sp,vfp4d16sp") },
+ { str_lit("strongarm"), str_lit("armv4") },
+ { str_lit("strongarm110"), str_lit("armv4") },
+ { str_lit("strongarm1100"), str_lit("armv4") },
+ { str_lit("strongarm1110"), str_lit("armv4") },
+ { str_lit("swift"), str_lit("aclass,armv7-a,avoid-movs-shop,avoid-partial-cpsr,d32,db,disable-postra-scheduler,dsp,fp16,fp64,fpregs,fpregs64,hwdiv,hwdiv-arm,mp,neon,neonfp,perfmon,prefer-ishst,prof-unpr,ret-addr-stack,slow-load-D-subreg,slow-odd-reg,slow-vdup32,slow-vgetlni32,slowfpvfmx,slowfpvmlx,swift,thumb2,use-misched,v4t,v5t,v5te,v6,v6k,v6m,v6t2,v7,v7clrex,v8m,vfp2,vfp2sp,vfp3,vfp3d16,vfp3d16sp,vfp3sp,vfp4,vfp4d16,vfp4d16sp,vfp4sp,vmlx-hazards,wide-stride-vfp") },
+ { str_lit("xscale"), str_lit("armv5te,v4t,v5t,v5te") },
+ // TargetArch_arm64:
+ { str_lit("a64fx"), str_lit("CONTEXTIDREL2,a64fx,aes,aggressive-fma,arith-bcc-fusion,ccpp,complxnum,crc,el2vmsa,el3,fp-armv8,fullfp16,lor,lse,neon,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rdm,sha2,store-pair-suppress,sve,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("ampere1"), str_lit("CONTEXTIDREL2,aes,aggressive-fma,altnzcv,alu-lsl-fast,am,ampere1,amvs,arith-bcc-fusion,bf16,bti,ccdp,ccidx,ccpp,cmp-bcc-fusion,complxnum,crc,dit,dotprod,ecv,el2vmsa,el3,fgt,flagm,fp-armv8,fptoint,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-literals,i8mm,jsconv,ldp-aligned-only,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,predres,rand,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,specrestrict,ssbs,store-pair-suppress,stp-aligned-only,tlb-rmi,tracev8.4,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8a,vh") },
+ { str_lit("ampere1a"), str_lit("CONTEXTIDREL2,aes,aggressive-fma,altnzcv,alu-lsl-fast,am,ampere1a,amvs,arith-bcc-fusion,bf16,bti,ccdp,ccidx,ccpp,cmp-bcc-fusion,complxnum,crc,dit,dotprod,ecv,el2vmsa,el3,fgt,flagm,fp-armv8,fptoint,fullfp16,fuse-address,fuse-addsub-2reg-const1,fuse-adrp-add,fuse-aes,fuse-literals,i8mm,jsconv,ldp-aligned-only,lor,lse,lse2,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predres,rand,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,sm4,specrestrict,ssbs,store-pair-suppress,stp-aligned-only,tlb-rmi,tracev8.4,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8a,vh") },
+ { str_lit("ampere1b"), str_lit("CONTEXTIDREL2,aes,aggressive-fma,altnzcv,alu-lsl-fast,am,ampere1b,amvs,arith-bcc-fusion,bf16,bti,ccdp,ccidx,ccpp,cmp-bcc-fusion,complxnum,crc,cssc,dit,dotprod,ecv,el2vmsa,el3,enable-select-opt,fgt,flagm,fp-armv8,fptoint,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-literals,hcx,i8mm,jsconv,ldp-aligned-only,lor,lse,lse2,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,rand,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,sm4,specrestrict,ssbs,store-pair-suppress,stp-aligned-only,tlb-rmi,tracev8.4,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8a,vh,wfxt,xs") },
+ { str_lit("apple-a10"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,apple-a10,arith-bcc-fusion,arith-cbz-fusion,crc,disable-latency-sched-heuristic,el2vmsa,el3,fp-armv8,fuse-aes,fuse-crypto-eor,lor,neon,pan,perfmon,rdm,sha2,store-pair-suppress,v8a,vh,zcm,zcz,zcz-gp") },
+ { str_lit("apple-a11"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,apple-a11,arith-bcc-fusion,arith-cbz-fusion,ccpp,crc,disable-latency-sched-heuristic,el2vmsa,el3,fp-armv8,fullfp16,fuse-aes,fuse-crypto-eor,lor,lse,neon,pan,pan-rwv,perfmon,ras,rdm,sha2,store-pair-suppress,uaops,v8.1a,v8.2a,v8a,vh,zcm,zcz,zcz-gp") },
+ { str_lit("apple-a12"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,apple-a12,arith-bcc-fusion,arith-cbz-fusion,ccpp,complxnum,crc,disable-latency-sched-heuristic,el2vmsa,el3,fp-armv8,fullfp16,fuse-aes,fuse-crypto-eor,jsconv,lor,lse,neon,pan,pan-rwv,pauth,perfmon,ras,rcpc,rdm,sha2,store-pair-suppress,uaops,v8.1a,v8.2a,v8.3a,v8a,vh,zcm,zcz,zcz-gp") },
+ { str_lit("apple-a13"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,am,apple-a13,arith-bcc-fusion,arith-cbz-fusion,ccpp,complxnum,crc,disable-latency-sched-heuristic,dit,dotprod,el2vmsa,el3,flagm,fp-armv8,fp16fml,fullfp16,fuse-aes,fuse-crypto-eor,jsconv,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,ras,rcpc,rcpc-immo,rdm,sel2,sha2,sha3,store-pair-suppress,tlb-rmi,tracev8.4,uaops,v8.1a,v8.2a,v8.3a,v8.4a,v8a,vh,zcm,zcz,zcz-gp") },
+ { str_lit("apple-a14"), str_lit("CONTEXTIDREL2,aes,aggressive-fma,alternate-sextload-cvt-f32-pattern,altnzcv,am,apple-a14,arith-bcc-fusion,arith-cbz-fusion,ccdp,ccpp,complxnum,crc,disable-latency-sched-heuristic,dit,dotprod,el2vmsa,el3,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-address,fuse-aes,fuse-arith-logic,fuse-crypto-eor,fuse-csel,fuse-literals,jsconv,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,specrestrict,ssbs,store-pair-suppress,tlb-rmi,tracev8.4,uaops,v8.1a,v8.2a,v8.3a,v8.4a,v8a,vh,zcm,zcz,zcz-gp") },
+ { str_lit("apple-a15"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,altnzcv,am,amvs,apple-a15,arith-bcc-fusion,arith-cbz-fusion,bf16,bti,ccdp,ccpp,complxnum,crc,disable-latency-sched-heuristic,dit,dotprod,ecv,el2vmsa,el3,fgt,flagm,fp-armv8,fp16fml,fpac,fptoint,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-arith-logic,fuse-crypto-eor,fuse-csel,fuse-literals,i8mm,jsconv,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,specrestrict,ssbs,store-pair-suppress,tlb-rmi,tracev8.4,uaops,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8a,vh,zcm,zcz,zcz-gp") },
+ { str_lit("apple-a16"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,altnzcv,am,amvs,apple-a16,arith-bcc-fusion,arith-cbz-fusion,bf16,bti,ccdp,ccpp,complxnum,crc,disable-latency-sched-heuristic,dit,dotprod,ecv,el2vmsa,el3,fgt,flagm,fp-armv8,fp16fml,fpac,fptoint,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-arith-logic,fuse-crypto-eor,fuse-csel,fuse-literals,hcx,i8mm,jsconv,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,specrestrict,ssbs,store-pair-suppress,tlb-rmi,tracev8.4,uaops,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8a,vh,zcm,zcz,zcz-gp") },
+ { str_lit("apple-a17"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,altnzcv,am,amvs,apple-a17,arith-bcc-fusion,arith-cbz-fusion,bf16,bti,ccdp,ccpp,complxnum,crc,disable-latency-sched-heuristic,dit,dotprod,ecv,el2vmsa,el3,fgt,flagm,fp-armv8,fp16fml,fpac,fptoint,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-arith-logic,fuse-crypto-eor,fuse-csel,fuse-literals,hcx,i8mm,jsconv,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,specrestrict,ssbs,store-pair-suppress,tlb-rmi,tracev8.4,uaops,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8a,vh,zcm,zcz,zcz-gp") },
+ { str_lit("apple-a7"), str_lit("aes,alternate-sextload-cvt-f32-pattern,apple-a7,arith-bcc-fusion,arith-cbz-fusion,disable-latency-sched-heuristic,el2vmsa,el3,fp-armv8,fuse-aes,fuse-crypto-eor,neon,perfmon,sha2,store-pair-suppress,v8a,zcm,zcz,zcz-fp-workaround,zcz-gp") },
+ { str_lit("apple-a8"), str_lit("aes,alternate-sextload-cvt-f32-pattern,apple-a7,arith-bcc-fusion,arith-cbz-fusion,disable-latency-sched-heuristic,el2vmsa,el3,fp-armv8,fuse-aes,fuse-crypto-eor,neon,perfmon,sha2,store-pair-suppress,v8a,zcm,zcz,zcz-fp-workaround,zcz-gp") },
+ { str_lit("apple-a9"), str_lit("aes,alternate-sextload-cvt-f32-pattern,apple-a7,arith-bcc-fusion,arith-cbz-fusion,disable-latency-sched-heuristic,el2vmsa,el3,fp-armv8,fuse-aes,fuse-crypto-eor,neon,perfmon,sha2,store-pair-suppress,v8a,zcm,zcz,zcz-fp-workaround,zcz-gp") },
+ { str_lit("apple-m1"), str_lit("CONTEXTIDREL2,aes,aggressive-fma,alternate-sextload-cvt-f32-pattern,altnzcv,am,apple-a14,arith-bcc-fusion,arith-cbz-fusion,ccdp,ccpp,complxnum,crc,disable-latency-sched-heuristic,dit,dotprod,el2vmsa,el3,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-address,fuse-aes,fuse-arith-logic,fuse-crypto-eor,fuse-csel,fuse-literals,jsconv,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,specrestrict,ssbs,store-pair-suppress,tlb-rmi,tracev8.4,uaops,v8.1a,v8.2a,v8.3a,v8.4a,v8a,vh,zcm,zcz,zcz-gp") },
+ { str_lit("apple-m2"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,altnzcv,am,amvs,apple-a15,arith-bcc-fusion,arith-cbz-fusion,bf16,bti,ccdp,ccpp,complxnum,crc,disable-latency-sched-heuristic,dit,dotprod,ecv,el2vmsa,el3,fgt,flagm,fp-armv8,fp16fml,fpac,fptoint,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-arith-logic,fuse-crypto-eor,fuse-csel,fuse-literals,i8mm,jsconv,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,specrestrict,ssbs,store-pair-suppress,tlb-rmi,tracev8.4,uaops,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8a,vh,zcm,zcz,zcz-gp") },
+ { str_lit("apple-m3"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,altnzcv,am,amvs,apple-a16,arith-bcc-fusion,arith-cbz-fusion,bf16,bti,ccdp,ccpp,complxnum,crc,disable-latency-sched-heuristic,dit,dotprod,ecv,el2vmsa,el3,fgt,flagm,fp-armv8,fp16fml,fpac,fptoint,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-arith-logic,fuse-crypto-eor,fuse-csel,fuse-literals,hcx,i8mm,jsconv,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,specrestrict,ssbs,store-pair-suppress,tlb-rmi,tracev8.4,uaops,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8a,vh,zcm,zcz,zcz-gp") },
+ { str_lit("apple-m4"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,altnzcv,am,amvs,apple-m4,arith-bcc-fusion,arith-cbz-fusion,bf16,bti,ccdp,ccpp,complxnum,crc,disable-latency-sched-heuristic,dit,dotprod,ecv,el2vmsa,el3,fgt,flagm,fp-armv8,fp16fml,fpac,fptoint,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-arith-logic,fuse-crypto-eor,fuse-csel,fuse-literals,hcx,i8mm,jsconv,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,sme,sme-f64f64,sme-i16i64,sme2,specrestrict,tlb-rmi,tracev8.4,uaops,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8a,vh,wfxt,xs,zcm,zcz,zcz-gp") },
+ { str_lit("apple-s4"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,apple-a12,arith-bcc-fusion,arith-cbz-fusion,ccpp,complxnum,crc,disable-latency-sched-heuristic,el2vmsa,el3,fp-armv8,fullfp16,fuse-aes,fuse-crypto-eor,jsconv,lor,lse,neon,pan,pan-rwv,pauth,perfmon,ras,rcpc,rdm,sha2,store-pair-suppress,uaops,v8.1a,v8.2a,v8.3a,v8a,vh,zcm,zcz,zcz-gp") },
+ { str_lit("apple-s5"), str_lit("CONTEXTIDREL2,aes,alternate-sextload-cvt-f32-pattern,apple-a12,arith-bcc-fusion,arith-cbz-fusion,ccpp,complxnum,crc,disable-latency-sched-heuristic,el2vmsa,el3,fp-armv8,fullfp16,fuse-aes,fuse-crypto-eor,jsconv,lor,lse,neon,pan,pan-rwv,pauth,perfmon,ras,rcpc,rdm,sha2,store-pair-suppress,uaops,v8.1a,v8.2a,v8.3a,v8a,vh,zcm,zcz,zcz-gp") },
+ { str_lit("carmel"), str_lit("CONTEXTIDREL2,aes,carmel,ccpp,crc,el2vmsa,el3,fp-armv8,fullfp16,lor,lse,neon,pan,pan-rwv,ras,rdm,sha2,uaops,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("cobalt-100"), str_lit("CONTEXTIDREL2,altnzcv,alu-lsl-fast,am,bf16,bti,ccdp,ccidx,ccpp,complxnum,crc,dit,dotprod,el2vmsa,el3,enable-select-opt,ete,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,neoversen2,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") },
+ { str_lit("cortex-a34"), str_lit("a35,aes,crc,el2vmsa,el3,fp-armv8,neon,perfmon,sha2,v8a") },
+ { str_lit("cortex-a35"), str_lit("a35,aes,crc,el2vmsa,el3,fp-armv8,neon,perfmon,sha2,v8a") },
+ { str_lit("cortex-a510"), str_lit("CONTEXTIDREL2,a510,altnzcv,am,bf16,bti,ccdp,ccidx,ccpp,complxnum,crc,dit,dotprod,el2vmsa,el3,ete,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") },
+ { str_lit("cortex-a520"), str_lit("CONTEXTIDREL2,a520,altnzcv,am,amvs,bf16,bti,ccdp,ccidx,ccpp,complxnum,crc,dit,dotprod,ecv,el2vmsa,el3,ete,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,hcx,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8a,v9.1a,v9.2a,v9a,vh,wfxt,xs") },
+ { str_lit("cortex-a520ae"), str_lit("CONTEXTIDREL2,a520ae,altnzcv,am,amvs,bf16,bti,ccdp,ccidx,ccpp,complxnum,crc,dit,dotprod,ecv,el2vmsa,el3,ete,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,hcx,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8a,v9.1a,v9.2a,v9a,vh,wfxt,xs") },
+ { str_lit("cortex-a53"), str_lit("a53,aes,balance-fp-ops,crc,el2vmsa,el3,fp-armv8,fuse-adrp-add,fuse-aes,neon,perfmon,sha2,use-postra-scheduler,v8a") },
+ { str_lit("cortex-a55"), str_lit("CONTEXTIDREL2,a55,aes,ccpp,crc,dotprod,el2vmsa,el3,fp-armv8,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,lor,lse,neon,pan,pan-rwv,perfmon,ras,rcpc,rdm,sha2,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("cortex-a57"), str_lit("a57,addr-lsl-slow-14,aes,balance-fp-ops,crc,el2vmsa,el3,enable-select-opt,fp-armv8,fuse-adrp-add,fuse-aes,fuse-literals,neon,perfmon,predictable-select-expensive,sha2,use-postra-scheduler,v8a") },
+ { str_lit("cortex-a65"), str_lit("CONTEXTIDREL2,a65,aes,ccpp,crc,dotprod,el2vmsa,el3,enable-select-opt,fp-armv8,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-literals,lor,lse,neon,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,ssbs,uaops,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("cortex-a65ae"), str_lit("CONTEXTIDREL2,a65,aes,ccpp,crc,dotprod,el2vmsa,el3,enable-select-opt,fp-armv8,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-literals,lor,lse,neon,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,ssbs,uaops,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("cortex-a710"), str_lit("CONTEXTIDREL2,a710,altnzcv,alu-lsl-fast,am,bf16,bti,ccdp,ccidx,ccpp,cmp-bcc-fusion,complxnum,crc,dit,dotprod,el2vmsa,el3,enable-select-opt,ete,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") },
+ { str_lit("cortex-a715"), str_lit("CONTEXTIDREL2,a715,altnzcv,alu-lsl-fast,am,bf16,bti,ccdp,ccidx,ccpp,cmp-bcc-fusion,complxnum,crc,dit,dotprod,el2vmsa,el3,enable-select-opt,ete,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,spe,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") },
+ { str_lit("cortex-a72"), str_lit("a72,addr-lsl-slow-14,aes,crc,el2vmsa,el3,enable-select-opt,fp-armv8,fuse-adrp-add,fuse-aes,fuse-literals,neon,perfmon,predictable-select-expensive,sha2,v8a") },
+ { str_lit("cortex-a720"), str_lit("CONTEXTIDREL2,a720,altnzcv,alu-lsl-fast,am,amvs,bf16,bti,ccdp,ccidx,ccpp,cmp-bcc-fusion,complxnum,crc,dit,dotprod,ecv,el2vmsa,el3,enable-select-opt,ete,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,hcx,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,spe,spe-eef,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8a,v9.1a,v9.2a,v9a,vh,wfxt,xs") },
+ { str_lit("cortex-a720ae"), str_lit("CONTEXTIDREL2,a720ae,altnzcv,alu-lsl-fast,am,amvs,bf16,bti,ccdp,ccidx,ccpp,cmp-bcc-fusion,complxnum,crc,dit,dotprod,ecv,el2vmsa,el3,enable-select-opt,ete,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,hcx,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,spe,spe-eef,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8a,v9.1a,v9.2a,v9a,vh,wfxt,xs") },
+ { str_lit("cortex-a725"), str_lit("CONTEXTIDREL2,altnzcv,alu-lsl-fast,am,amvs,bf16,bti,ccdp,ccidx,ccpp,cmp-bcc-fusion,complxnum,cortex-a725,crc,dit,dotprod,ecv,el2vmsa,el3,enable-select-opt,ete,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,hcx,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,spe,spe-eef,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8a,v9.1a,v9.2a,v9a,vh,wfxt,xs") },
+ { str_lit("cortex-a73"), str_lit("a73,addr-lsl-slow-14,aes,crc,el2vmsa,el3,enable-select-opt,fp-armv8,fuse-adrp-add,fuse-aes,neon,perfmon,predictable-select-expensive,sha2,v8a") },
+ { str_lit("cortex-a75"), str_lit("CONTEXTIDREL2,a75,addr-lsl-slow-14,aes,ccpp,crc,dotprod,el2vmsa,el3,enable-select-opt,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,neon,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,uaops,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("cortex-a76"), str_lit("CONTEXTIDREL2,a76,addr-lsl-slow-14,aes,alu-lsl-fast,ccpp,crc,dotprod,el2vmsa,el3,enable-select-opt,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,neon,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,ssbs,uaops,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("cortex-a76ae"), str_lit("CONTEXTIDREL2,a76,addr-lsl-slow-14,aes,alu-lsl-fast,ccpp,crc,dotprod,el2vmsa,el3,enable-select-opt,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,neon,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,ssbs,uaops,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("cortex-a77"), str_lit("CONTEXTIDREL2,a77,addr-lsl-slow-14,aes,alu-lsl-fast,ccpp,cmp-bcc-fusion,crc,dotprod,el2vmsa,el3,enable-select-opt,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,neon,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,ssbs,uaops,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("cortex-a78"), str_lit("CONTEXTIDREL2,a78,addr-lsl-slow-14,aes,alu-lsl-fast,ccpp,cmp-bcc-fusion,crc,dotprod,el2vmsa,el3,enable-select-opt,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,neon,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,spe,ssbs,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("cortex-a78ae"), str_lit("CONTEXTIDREL2,a78ae,addr-lsl-slow-14,aes,alu-lsl-fast,ccpp,cmp-bcc-fusion,crc,dotprod,el2vmsa,el3,enable-select-opt,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,neon,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,spe,ssbs,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("cortex-a78c"), str_lit("CONTEXTIDREL2,a78c,addr-lsl-slow-14,aes,alu-lsl-fast,ccpp,cmp-bcc-fusion,crc,dotprod,el2vmsa,el3,enable-select-opt,flagm,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,neon,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,spe,ssbs,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("cortex-r82"), str_lit("CONTEXTIDREL2,ccdp,ccpp,complxnum,cortex-r82,crc,dit,dotprod,flagm,fp-armv8,fp16fml,fullfp16,jsconv,lse,neon,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,specrestrict,ssbs,tlb-rmi,tracev8.4,uaops,use-postra-scheduler,v8r") },
+ { str_lit("cortex-r82ae"), str_lit("CONTEXTIDREL2,ccdp,ccpp,complxnum,cortex-r82ae,crc,dit,dotprod,flagm,fp-armv8,fp16fml,fullfp16,jsconv,lse,neon,pan,pan-rwv,pauth,perfmon,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,specrestrict,ssbs,tlb-rmi,tracev8.4,uaops,use-postra-scheduler,v8r") },
+ { str_lit("cortex-x1"), str_lit("CONTEXTIDREL2,addr-lsl-slow-14,aes,alu-lsl-fast,ccpp,cmp-bcc-fusion,cortex-x1,crc,dotprod,el2vmsa,el3,enable-select-opt,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,neon,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,spe,ssbs,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("cortex-x1c"), str_lit("CONTEXTIDREL2,addr-lsl-slow-14,aes,alu-lsl-fast,ccpp,cmp-bcc-fusion,cortex-x1,crc,dotprod,el2vmsa,el3,enable-select-opt,flagm,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,lse2,neon,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,ras,rcpc,rcpc-immo,rdm,sha2,spe,ssbs,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("cortex-x2"), str_lit("CONTEXTIDREL2,altnzcv,alu-lsl-fast,am,bf16,bti,ccdp,ccidx,ccpp,cmp-bcc-fusion,complxnum,cortex-x2,crc,dit,dotprod,el2vmsa,el3,enable-select-opt,ete,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-fixed-over-scalable-if-equal-cost,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") },
+ { str_lit("cortex-x3"), str_lit("CONTEXTIDREL2,altnzcv,alu-lsl-fast,am,avoid-ldapur,bf16,bti,ccdp,ccidx,ccpp,complxnum,cortex-x3,crc,dit,dotprod,el2vmsa,el3,enable-select-opt,ete,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,spe,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-fixed-over-scalable-if-equal-cost,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") },
+ { str_lit("cortex-x4"), str_lit("CONTEXTIDREL2,altnzcv,alu-lsl-fast,am,amvs,avoid-ldapur,bf16,bti,ccdp,ccidx,ccpp,complxnum,cortex-x4,crc,dit,dotprod,ecv,el2vmsa,el3,enable-select-opt,ete,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,hcx,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,spe,spe-eef,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-fixed-over-scalable-if-equal-cost,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8a,v9.1a,v9.2a,v9a,vh,wfxt,xs") },
+ { str_lit("cortex-x925"), str_lit("CONTEXTIDREL2,altnzcv,alu-lsl-fast,am,amvs,avoid-ldapur,bf16,bti,ccdp,ccidx,ccpp,complxnum,cortex-x925,crc,dit,dotprod,ecv,el2vmsa,el3,enable-select-opt,ete,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,hcx,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,spe,spe-eef,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-fixed-over-scalable-if-equal-cost,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8a,v9.1a,v9.2a,v9a,vh,wfxt,xs") },
+ { str_lit("cyclone"), str_lit("aes,alternate-sextload-cvt-f32-pattern,apple-a7,arith-bcc-fusion,arith-cbz-fusion,disable-latency-sched-heuristic,el2vmsa,el3,fp-armv8,fuse-aes,fuse-crypto-eor,neon,perfmon,sha2,store-pair-suppress,v8a,zcm,zcz,zcz-fp-workaround,zcz-gp") },
+ { str_lit("exynos-m3"), str_lit("aes,alu-lsl-fast,crc,el2vmsa,el3,exynos-cheap-as-move,exynosm3,force-32bit-jump-tables,fp-armv8,fuse-address,fuse-adrp-add,fuse-aes,fuse-csel,fuse-literals,neon,perfmon,predictable-select-expensive,sha2,store-pair-suppress,use-postra-scheduler,v8a") },
+ { str_lit("exynos-m4"), str_lit("CONTEXTIDREL2,aes,alu-lsl-fast,arith-bcc-fusion,arith-cbz-fusion,ccpp,crc,dotprod,el2vmsa,el3,exynos-cheap-as-move,exynosm4,force-32bit-jump-tables,fp-armv8,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-arith-logic,fuse-csel,fuse-literals,lor,lse,neon,pan,pan-rwv,perfmon,ras,rdm,sha2,store-pair-suppress,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh,zcz,zcz-gp") },
+ { str_lit("exynos-m5"), str_lit("CONTEXTIDREL2,aes,alu-lsl-fast,arith-bcc-fusion,arith-cbz-fusion,ccpp,crc,dotprod,el2vmsa,el3,exynos-cheap-as-move,exynosm4,force-32bit-jump-tables,fp-armv8,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-arith-logic,fuse-csel,fuse-literals,lor,lse,neon,pan,pan-rwv,perfmon,ras,rdm,sha2,store-pair-suppress,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh,zcz,zcz-gp") },
+ { str_lit("falkor"), str_lit("aes,alu-lsl-fast,crc,el2vmsa,el3,falkor,fp-armv8,neon,perfmon,predictable-select-expensive,rdm,sha2,slow-strqro-store,store-pair-suppress,use-postra-scheduler,v8a,zcz,zcz-gp") },
+ { str_lit("fujitsu-monaka"), str_lit("CONTEXTIDREL2,aes,altnzcv,am,amvs,arith-bcc-fusion,bf16,bti,ccdp,ccidx,ccpp,clrbhb,complxnum,crc,dit,dotprod,ecv,el2vmsa,el3,enable-select-opt,ete,faminmax,fgt,flagm,fp-armv8,fp16fml,fp8,fp8dot2,fpac,fptoint,fujitsu-monaka,fullfp16,hbc,hcx,i8mm,jsconv,lor,ls64,lse,lse2,lut,mec,mops,mpam,neon,nmi,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,rand,ras,rcpc,rcpc-immo,rdm,rme,sb,sel2,sha2,sha3,sm4,specres2,specrestrict,ssbs,sve,sve-aes,sve-bitperm,sve2,sve2-sha3,sve2-sm4,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8.8a,v8a,v9.1a,v9.2a,v9.3a,v9a,vh,wfxt,xs") },
+ { str_lit("generic"), str_lit("enable-select-opt,ete,fp-armv8,fuse-adrp-add,fuse-aes,neon,trbe,use-postra-scheduler") },
+ { str_lit("grace"), str_lit("CONTEXTIDREL2,altnzcv,alu-lsl-fast,am,avoid-ldapur,bf16,bti,ccdp,ccidx,ccpp,cmp-bcc-fusion,complxnum,crc,dit,dotprod,el2vmsa,el3,enable-select-opt,ete,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,neoversev2,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,rand,ras,rcpc,rcpc-immo,rdm,sb,sel2,spe,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-fixed-over-scalable-if-equal-cost,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") },
+ { str_lit("kryo"), str_lit("aes,alu-lsl-fast,crc,el2vmsa,el3,fp-armv8,kryo,neon,perfmon,predictable-select-expensive,sha2,store-pair-suppress,use-postra-scheduler,v8a,zcz,zcz-gp") },
+ { str_lit("neoverse-512tvb"), str_lit("CONTEXTIDREL2,aes,alu-lsl-fast,am,bf16,ccdp,ccidx,ccpp,complxnum,crc,dit,dotprod,el2vmsa,el3,enable-select-opt,flagm,fp-armv8,fp16fml,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mpam,neon,neoverse512tvb,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,rand,ras,rcpc,rcpc-immo,rdm,sel2,sha2,sha3,sm4,spe,ssbs,sve,tlb-rmi,tracev8.4,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8a,vh") },
+ { str_lit("neoverse-e1"), str_lit("CONTEXTIDREL2,aes,ccpp,crc,dotprod,el2vmsa,el3,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,neon,neoversee1,pan,pan-rwv,perfmon,ras,rcpc,rdm,sha2,ssbs,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("neoverse-n1"), str_lit("CONTEXTIDREL2,addr-lsl-slow-14,aes,alu-lsl-fast,ccpp,crc,dotprod,el2vmsa,el3,enable-select-opt,fp-armv8,fullfp16,fuse-adrp-add,fuse-aes,lor,lse,neon,neoversen1,pan,pan-rwv,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,spe,ssbs,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh") },
+ { str_lit("neoverse-n2"), str_lit("CONTEXTIDREL2,altnzcv,alu-lsl-fast,am,bf16,bti,ccdp,ccidx,ccpp,complxnum,crc,dit,dotprod,el2vmsa,el3,enable-select-opt,ete,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,neoversen2,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,ras,rcpc,rcpc-immo,rdm,sb,sel2,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") },
+ { str_lit("neoverse-n3"), str_lit("CONTEXTIDREL2,altnzcv,alu-lsl-fast,am,amvs,bf16,bti,ccdp,ccidx,ccpp,complxnum,crc,dit,dotprod,ecv,el2vmsa,el3,enable-select-opt,ete,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,hcx,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,neoversen3,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,rand,ras,rcpc,rcpc-immo,rdm,sb,sel2,spe,spe-eef,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8a,v9.1a,v9.2a,v9a,vh,wfxt,xs") },
+ { str_lit("neoverse-v1"), str_lit("CONTEXTIDREL2,addr-lsl-slow-14,aes,alu-lsl-fast,am,bf16,ccdp,ccidx,ccpp,complxnum,crc,dit,dotprod,el2vmsa,el3,enable-select-opt,flagm,fp-armv8,fp16fml,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mpam,neon,neoversev1,no-sve-fp-ld1r,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,rand,ras,rcpc,rcpc-immo,rdm,sel2,sha2,sha3,sm4,spe,ssbs,sve,tlb-rmi,tracev8.4,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8a,vh") },
+ { str_lit("neoverse-v2"), str_lit("CONTEXTIDREL2,altnzcv,alu-lsl-fast,am,avoid-ldapur,bf16,bti,ccdp,ccidx,ccpp,cmp-bcc-fusion,complxnum,crc,dit,dotprod,el2vmsa,el3,enable-select-opt,ete,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,i8mm,jsconv,lor,lse,lse2,mpam,mte,neon,neoversev2,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,rand,ras,rcpc,rcpc-immo,rdm,sb,sel2,spe,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-fixed-over-scalable-if-equal-cost,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8a,v9a,vh") },
+ { str_lit("neoverse-v3"), str_lit("CONTEXTIDREL2,altnzcv,alu-lsl-fast,am,amvs,avoid-ldapur,bf16,brbe,bti,ccdp,ccidx,ccpp,complxnum,crc,dit,dotprod,ecv,el2vmsa,el3,enable-select-opt,ete,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,hcx,i8mm,jsconv,lor,ls64,lse,lse2,mpam,mte,neon,neoversev3,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,rand,ras,rcpc,rcpc-immo,rdm,rme,sb,sel2,spe,spe-eef,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8a,v9.1a,v9.2a,v9a,vh,wfxt,xs") },
+ { str_lit("neoverse-v3ae"), str_lit("CONTEXTIDREL2,altnzcv,alu-lsl-fast,am,amvs,avoid-ldapur,bf16,brbe,bti,ccdp,ccidx,ccpp,complxnum,crc,dit,dotprod,ecv,el2vmsa,el3,enable-select-opt,ete,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-adrp-add,fuse-aes,hcx,i8mm,jsconv,lor,ls64,lse,lse2,mpam,mte,neon,neoversev3AE,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,predres,rand,ras,rcpc,rcpc-immo,rdm,rme,sb,sel2,spe,spe-eef,specrestrict,ssbs,sve,sve-bitperm,sve2,tlb-rmi,tracev8.4,trbe,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8.7a,v8a,v9.1a,v9.2a,v9a,vh,wfxt,xs") },
+ { str_lit("oryon-1"), str_lit("CONTEXTIDREL2,aes,altnzcv,am,amvs,bf16,bti,ccdp,ccidx,ccpp,complxnum,crc,dit,dotprod,ecv,el2vmsa,el3,enable-select-opt,fgt,flagm,fp-armv8,fp16fml,fptoint,fullfp16,fuse-address,fuse-adrp-add,fuse-aes,fuse-crypto-eor,i8mm,jsconv,lor,lse,lse2,mpam,neon,nv,oryon-1,pan,pan-rwv,pauth,perfmon,predres,rand,ras,rcpc,rcpc-immo,rdm,sb,sel2,sha2,sha3,sm4,spe,specrestrict,ssbs,tlb-rmi,tracev8.4,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8.5a,v8.6a,v8a,vh") },
+ { str_lit("saphira"), str_lit("CONTEXTIDREL2,aes,alu-lsl-fast,am,ccidx,ccpp,complxnum,crc,dit,dotprod,el2vmsa,el3,flagm,fp-armv8,jsconv,lor,lse,lse2,mpam,neon,nv,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,ras,rcpc,rcpc-immo,rdm,saphira,sel2,sha2,spe,store-pair-suppress,tlb-rmi,tracev8.4,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8.4a,v8a,vh,zcz,zcz-gp") },
+ { str_lit("thunderx"), str_lit("aes,crc,el2vmsa,el3,fp-armv8,neon,perfmon,predictable-select-expensive,sha2,store-pair-suppress,thunderx,use-postra-scheduler,v8a") },
+ { str_lit("thunderx2t99"), str_lit("CONTEXTIDREL2,aes,aggressive-fma,arith-bcc-fusion,crc,el2vmsa,el3,fp-armv8,lor,lse,neon,pan,predictable-select-expensive,rdm,sha2,store-pair-suppress,thunderx2t99,use-postra-scheduler,v8.1a,v8a,vh") },
+ { str_lit("thunderx3t110"), str_lit("CONTEXTIDREL2,aes,aggressive-fma,arith-bcc-fusion,balance-fp-ops,ccidx,ccpp,complxnum,crc,el2vmsa,el3,fp-armv8,jsconv,lor,lse,neon,pan,pan-rwv,pauth,perfmon,predictable-select-expensive,ras,rcpc,rdm,sha2,store-pair-suppress,strict-align,thunderx3t110,uaops,use-postra-scheduler,v8.1a,v8.2a,v8.3a,v8a,vh") },
+ { str_lit("thunderxt81"), str_lit("aes,crc,el2vmsa,el3,fp-armv8,neon,perfmon,predictable-select-expensive,sha2,store-pair-suppress,thunderxt81,use-postra-scheduler,v8a") },
+ { str_lit("thunderxt83"), str_lit("aes,crc,el2vmsa,el3,fp-armv8,neon,perfmon,predictable-select-expensive,sha2,store-pair-suppress,thunderxt83,use-postra-scheduler,v8a") },
+ { str_lit("thunderxt88"), str_lit("aes,crc,el2vmsa,el3,fp-armv8,neon,perfmon,predictable-select-expensive,sha2,store-pair-suppress,thunderxt88,use-postra-scheduler,v8a") },
+ { str_lit("tsv110"), str_lit("CONTEXTIDREL2,aes,ccpp,complxnum,crc,dotprod,el2vmsa,el3,fp-armv8,fp16fml,fullfp16,fuse-aes,jsconv,lor,lse,neon,pan,pan-rwv,perfmon,ras,rdm,sha2,spe,store-pair-suppress,tsv110,uaops,use-postra-scheduler,v8.1a,v8.2a,v8a,vh") },
+ // TargetArch_wasm32:
+ { str_lit("bleeding-edge"), str_lit("atomics,bulk-memory,bulk-memory-opt,call-indirect-overlong,exception-handling,extended-const,fp16,multimemory,multivalue,mutable-globals,nontrapping-fptoint,reference-types,relaxed-simd,sign-ext,simd128,tail-call") },
+ { str_lit("generic"), str_lit("bulk-memory,bulk-memory-opt,call-indirect-overlong,multivalue,mutable-globals,nontrapping-fptoint,reference-types,sign-ext") },
+ { str_lit("lime1"), str_lit("bulk-memory-opt,call-indirect-overlong,extended-const,multivalue,mutable-globals,nontrapping-fptoint,sign-ext") },
+ { str_lit("mvp"), str_lit("") },
+ // TargetArch_wasm64p32:
+ { str_lit("bleeding-edge"), str_lit("atomics,bulk-memory,bulk-memory-opt,call-indirect-overlong,exception-handling,extended-const,fp16,multimemory,multivalue,mutable-globals,nontrapping-fptoint,reference-types,relaxed-simd,sign-ext,simd128,tail-call") },
+ { str_lit("generic"), str_lit("bulk-memory,bulk-memory-opt,call-indirect-overlong,multivalue,mutable-globals,nontrapping-fptoint,reference-types,sign-ext") },
+ { str_lit("lime1"), str_lit("bulk-memory-opt,call-indirect-overlong,extended-const,multivalue,mutable-globals,nontrapping-fptoint,sign-ext") },
+ { str_lit("mvp"), str_lit("") },
+ // TargetArch_riscv64:
+ { str_lit("generic"), str_lit("64bit,i,optimized-nf2-segment-load-store") },
+ { str_lit("generic-rv32"), str_lit("32bit,i,optimized-nf2-segment-load-store") },
+ { str_lit("generic-rv64"), str_lit("64bit,i,optimized-nf2-segment-load-store") },
+ { str_lit("mips-p8700"), str_lit("64bit,a,c,d,f,i,m,mips-p8700,xmipscmove,xmipslsp,zaamo,zalrsc,zba,zbb,zicsr,zifencei,zmmul") },
+ { str_lit("rocket"), str_lit("") },
+ { str_lit("rocket-rv32"), str_lit("32bit,i,zicsr,zifencei") },
+ { str_lit("rocket-rv64"), str_lit("64bit,i,zicsr,zifencei") },
+ { str_lit("rp2350-hazard3"), str_lit("32bit,a,c,i,m,zaamo,zalrsc,zba,zbb,zbkb,zbs,zca,zcb,zcmp,zicsr,zifencei,zmmul") },
+ { str_lit("sifive-7-series"), str_lit("no-default-unroll,short-forward-branch-opt,sifive7,use-postra-scheduler") },
+ { str_lit("sifive-e20"), str_lit("32bit,c,i,m,zicsr,zifencei,zmmul") },
+ { str_lit("sifive-e21"), str_lit("32bit,a,c,i,m,zaamo,zalrsc,zicsr,zifencei,zmmul") },
+ { str_lit("sifive-e24"), str_lit("32bit,a,c,f,i,m,zaamo,zalrsc,zicsr,zifencei,zmmul") },
+ { str_lit("sifive-e31"), str_lit("32bit,a,c,i,m,zaamo,zalrsc,zicsr,zifencei,zmmul") },
+ { str_lit("sifive-e34"), str_lit("32bit,a,c,f,i,m,zaamo,zalrsc,zicsr,zifencei,zmmul") },
+ { str_lit("sifive-e76"), str_lit("32bit,a,c,f,i,m,no-default-unroll,short-forward-branch-opt,sifive7,use-postra-scheduler,zaamo,zalrsc,zicsr,zifencei,zmmul") },
+ { str_lit("sifive-p450"), str_lit("64bit,a,auipc-addi-fusion,b,c,conditional-cmv-fusion,d,f,i,lui-addi-fusion,m,no-default-unroll,unaligned-scalar-mem,unaligned-vector-mem,use-postra-scheduler,za64rs,zaamo,zalrsc,zba,zbb,zbs,zfhmin,zic64b,zicbom,zicbop,zicboz,ziccamoa,ziccif,zicclsm,ziccrse,zicntr,zicsr,zifencei,zihintntl,zihintpause,zihpm,zkt,zmmul") },
+ { str_lit("sifive-p470"), str_lit("64bit,a,auipc-addi-fusion,b,c,conditional-cmv-fusion,d,f,i,lui-addi-fusion,m,no-default-unroll,no-sink-splat-operands,unaligned-scalar-mem,unaligned-vector-mem,use-postra-scheduler,v,vxrm-pipeline-flush,xsifivecdiscarddlone,xsifivecflushdlone,za64rs,zaamo,zalrsc,zba,zbb,zbs,zfhmin,zic64b,zicbom,zicbop,zicboz,ziccamoa,ziccif,zicclsm,ziccrse,zicntr,zicsr,zifencei,zihintntl,zihintpause,zihpm,zkt,zmmul,zvbb,zvbc,zve32f,zve32x,zve64d,zve64f,zve64x,zvkb,zvkg,zvkn,zvknc,zvkned,zvkng,zvknhb,zvks,zvksc,zvksed,zvksg,zvksh,zvkt,zvl128b,zvl32b,zvl64b") },
+ { str_lit("sifive-p550"), str_lit("64bit,a,auipc-addi-fusion,c,conditional-cmv-fusion,d,f,i,lui-addi-fusion,m,no-default-unroll,use-postra-scheduler,zaamo,zalrsc,zba,zbb,zicsr,zifencei,zmmul") },
+ { str_lit("sifive-p670"), str_lit("64bit,a,auipc-addi-fusion,b,c,conditional-cmv-fusion,d,f,i,lui-addi-fusion,m,no-default-unroll,no-sink-splat-operands,unaligned-scalar-mem,unaligned-vector-mem,use-postra-scheduler,v,vxrm-pipeline-flush,za64rs,zaamo,zalrsc,zba,zbb,zbs,zfhmin,zic64b,zicbom,zicbop,zicboz,ziccamoa,ziccif,zicclsm,ziccrse,zicntr,zicsr,zifencei,zihintntl,zihintpause,zihpm,zkt,zmmul,zvbb,zvbc,zve32f,zve32x,zve64d,zve64f,zve64x,zvkb,zvkg,zvkn,zvknc,zvkned,zvkng,zvknhb,zvks,zvksc,zvksed,zvksg,zvksh,zvkt,zvl128b,zvl32b,zvl64b") },
+ { str_lit("sifive-s21"), str_lit("64bit,a,c,i,m,zaamo,zalrsc,zicsr,zifencei,zmmul") },
+ { str_lit("sifive-s51"), str_lit("64bit,a,c,i,m,zaamo,zalrsc,zicsr,zifencei,zmmul") },
+ { str_lit("sifive-s54"), str_lit("64bit,a,c,d,f,i,m,zaamo,zalrsc,zicsr,zifencei,zmmul") },
+ { str_lit("sifive-s76"), str_lit("64bit,a,c,d,f,i,m,no-default-unroll,short-forward-branch-opt,sifive7,use-postra-scheduler,zaamo,zalrsc,zicsr,zifencei,zihintpause,zmmul") },
+ { str_lit("sifive-u54"), str_lit("64bit,a,c,d,f,i,m,zaamo,zalrsc,zicsr,zifencei,zmmul") },
+ { str_lit("sifive-u74"), str_lit("64bit,a,c,d,f,i,m,no-default-unroll,short-forward-branch-opt,sifive7,use-postra-scheduler,zaamo,zalrsc,zicsr,zifencei,zmmul") },
+ { str_lit("sifive-x280"), str_lit("64bit,a,c,d,dlen-factor-2,f,i,m,no-default-unroll,optimized-nf2-segment-load-store,optimized-zero-stride-load,short-forward-branch-opt,sifive7,use-postra-scheduler,v,zaamo,zalrsc,zba,zbb,zfh,zfhmin,zicsr,zifencei,zmmul,zve32f,zve32x,zve64d,zve64f,zve64x,zvfh,zvfhmin,zvl128b,zvl256b,zvl32b,zvl512b,zvl64b") },
+ { str_lit("spacemit-x60"), str_lit("64bit,a,b,c,d,dlen-factor-2,f,i,m,optimized-nf2-segment-load-store,optimized-nf3-segment-load-store,optimized-nf4-segment-load-store,ssccptr,sscofpmf,sscounterenw,sstc,sstvala,sstvecd,svade,svbare,svinval,svnapot,svpbmt,unaligned-scalar-mem,v,vxrm-pipeline-flush,za64rs,zaamo,zalrsc,zba,zbb,zbc,zbkc,zbs,zfh,zfhmin,zic64b,zicbom,zicbop,zicboz,ziccamoa,ziccif,zicclsm,ziccrse,zicntr,zicond,zicsr,zifencei,zihintpause,zihpm,zkt,zmmul,zve32f,zve32x,zve64d,zve64f,zve64x,zvfh,zvfhmin,zvkt,zvl128b,zvl256b,zvl32b,zvl64b") },
+ { str_lit("syntacore-scr1-base"), str_lit("32bit,c,i,no-default-unroll,zicsr,zifencei") },
+ { str_lit("syntacore-scr1-max"), str_lit("32bit,c,i,m,no-default-unroll,zicsr,zifencei,zmmul") },
+ { str_lit("syntacore-scr3-rv32"), str_lit("32bit,c,i,m,no-default-unroll,use-postra-scheduler,zicsr,zifencei,zmmul") },
+ { str_lit("syntacore-scr3-rv64"), str_lit("64bit,a,c,i,m,no-default-unroll,use-postra-scheduler,zaamo,zalrsc,zicsr,zifencei,zmmul") },
+ { str_lit("syntacore-scr4-rv32"), str_lit("32bit,c,d,f,i,m,no-default-unroll,use-postra-scheduler,zicsr,zifencei,zmmul") },
+ { str_lit("syntacore-scr4-rv64"), str_lit("64bit,a,c,d,f,i,m,no-default-unroll,use-postra-scheduler,zaamo,zalrsc,zicsr,zifencei,zmmul") },
+ { str_lit("syntacore-scr5-rv32"), str_lit("32bit,a,c,d,f,i,m,no-default-unroll,use-postra-scheduler,zaamo,zalrsc,zicsr,zifencei,zmmul") },
+ { str_lit("syntacore-scr5-rv64"), str_lit("64bit,a,c,d,f,i,m,no-default-unroll,use-postra-scheduler,zaamo,zalrsc,zicsr,zifencei,zmmul") },
+ { str_lit("syntacore-scr7"), str_lit("64bit,a,c,d,f,i,m,no-default-unroll,use-postra-scheduler,v,zaamo,zalrsc,zba,zbb,zbc,zbkb,zbkc,zbkx,zbs,zicsr,zifencei,zkn,zknd,zkne,zknh,zmmul,zve32f,zve32x,zve64d,zve64f,zve64x,zvl128b,zvl32b,zvl64b") },
+ { str_lit("tt-ascalon-d8"), str_lit("64bit,a,b,c,d,f,h,i,m,no-default-unroll,optimized-zero-stride-load,sha,shcounterenw,shgatpa,shtvala,shvsatpa,shvstvala,shvstvecd,smaia,ssaia,ssccptr,sscofpmf,sscounterenw,ssnpm,ssstateen,ssstrict,sstc,sstvala,sstvecd,ssu64xl,supm,svade,svbare,svinval,svnapot,svpbmt,unaligned-scalar-mem,unaligned-vector-mem,use-postra-scheduler,v,za64rs,zaamo,zalrsc,zawrs,zba,zbb,zbs,zca,zcb,zcmop,zfa,zfbfmin,zfh,zfhmin,zic64b,zicbom,zicbop,zicboz,ziccamoa,ziccif,zicclsm,ziccrse,zicntr,zicond,zicsr,zifencei,zihintntl,zihintpause,zihpm,zimop,zkt,zmmul,zvbb,zvbc,zve32f,zve32x,zve64d,zve64f,zve64x,zvfbfmin,zvfbfwma,zvfh,zvfhmin,zvkb,zvkg,zvkn,zvkned,zvkng,zvknhb,zvkt,zvl128b,zvl256b,zvl32b,zvl64b") },
+ { str_lit("veyron-v1"), str_lit("64bit,a,auipc-addi-fusion,c,d,f,i,ld-add-fusion,lui-addi-fusion,m,shifted-zextw-fusion,ventana-veyron,xventanacondops,zaamo,zalrsc,zba,zbb,zbc,zbs,zexth-fusion,zextw-fusion,zicbom,zicbop,zicboz,zicntr,zicsr,zifencei,zihintpause,zihpm,zmmul") },
+ { str_lit("xiangshan-nanhu"), str_lit("64bit,a,c,d,f,i,m,no-default-unroll,shifted-zextw-fusion,svinval,zaamo,zalrsc,zba,zbb,zbc,zbkb,zbkc,zbkx,zbs,zexth-fusion,zextw-fusion,zicbom,zicboz,zicsr,zifencei,zkn,zknd,zkne,zknh,zksed,zksh,zmmul") },
+};
+#else
// Generated with the featuregen script in `misc/featuregen`
gb_global String target_microarch_list[TargetArch_COUNT] = {
// TargetArch_Invalid:
@@ -514,3 +1072,4 @@ gb_global MicroarchFeatureList microarch_features_list[] = {
{ str_lit("veyron-v1"), str_lit("64bit,a,auipc-addi-fusion,c,d,f,ld-add-fusion,lui-addi-fusion,m,shifted-zextw-fusion,ventana-veyron,xventanacondops,zba,zbb,zbc,zbs,zexth-fusion,zextw-fusion,zicbom,zicbop,zicboz,zicntr,zicsr,zifencei,zihintpause,zihpm") },
{ str_lit("xiangshan-nanhu"), str_lit("64bit,a,c,d,f,m,svinval,zba,zbb,zbc,zbkb,zbkc,zbkx,zbs,zicbom,zicboz,zicsr,zifencei,zkn,zknd,zkne,zknh,zksed,zksh") },
};
+#endif
diff --git a/src/bundle_command.cpp b/src/bundle_command.cpp
new file mode 100644
index 000000000..cd0cd589f
--- /dev/null
+++ b/src/bundle_command.cpp
@@ -0,0 +1,212 @@
+i32 bundle_android(String init_directory);
+
+i32 bundle(String init_directory) {
+ switch (build_context.command_kind) {
+ case Command_bundle_android:
+ return bundle_android(init_directory);
+ }
+ gb_printf_err("Unknown odin package \n");
+ return 1;
+}
+
+
+i32 bundle_android(String original_init_directory) {
+ TEMPORARY_ALLOCATOR_GUARD();
+
+ i32 result = 0;
+ init_android_values(/*with_sdk*/true);
+
+ bool init_directory_ok = false;
+ String init_directory = path_to_fullpath(temporary_allocator(), original_init_directory, &init_directory_ok);
+ if (!init_directory_ok) {
+ gb_printf_err("Error: '%.*s' is not a valid directory", LIT(original_init_directory));
+ return 1;
+ }
+ init_directory = normalize_path(temporary_allocator(), init_directory, NIX_SEPARATOR_STRING);
+
+ int const ODIN_ANDROID_API_LEVEL = build_context.ODIN_ANDROID_API_LEVEL;
+
+ String android_sdk_build_tools = concatenate3_strings(temporary_allocator(),
+ build_context.ODIN_ANDROID_SDK, str_lit("build-tools"), NIX_SEPARATOR_STRING);
+
+ Array list = {};
+ ReadDirectoryError rd_err = read_directory(android_sdk_build_tools, &list);
+ defer (array_free(&list));
+
+ switch (rd_err) {
+ case ReadDirectory_InvalidPath:
+ gb_printf_err("Invalid path: %.*s\n", LIT(android_sdk_build_tools));
+ return 1;
+ case ReadDirectory_NotExists:
+ gb_printf_err("Path does not exist: %.*s\n", LIT(android_sdk_build_tools));
+ return 1;
+ case ReadDirectory_Permission:
+ gb_printf_err("Unknown error whilst reading path %.*s\n", LIT(android_sdk_build_tools));
+ return 1;
+ case ReadDirectory_NotDir:
+ gb_printf_err("Expected a directory for a package, got a file: %.*s\n", LIT(android_sdk_build_tools));
+ return 1;
+ case ReadDirectory_Empty:
+ gb_printf_err("Empty directory: %.*s\n", LIT(android_sdk_build_tools));
+ return 1;
+ case ReadDirectory_Unknown:
+ gb_printf_err("Unknown error whilst reading path %.*s\n", LIT(android_sdk_build_tools));
+ return 1;
+ }
+
+ auto possible_valid_dirs = array_make(heap_allocator(), 0, list.count);
+ defer (array_free(&possible_valid_dirs));
+
+
+ for (FileInfo fi : list) if (fi.is_dir) {
+ bool all_numbers = true;
+ for (isize i = 0; i < fi.name.len; i++) {
+ u8 c = fi.name[i];
+ if ('0' <= c && c <= '9') {
+ // true
+ } else if (i == 0) {
+ all_numbers = false;
+ } else if (c == '.') {
+ break;
+ } else {
+ all_numbers = false;
+ }
+ }
+
+ if (all_numbers) {
+ array_add(&possible_valid_dirs, fi);
+ }
+ }
+
+ if (possible_valid_dirs.count == 0) {
+ gb_printf_err("Unable to find any Android SDK/API Level in %.*s\n", LIT(android_sdk_build_tools));
+ return 1;
+ }
+
+ int *dir_numbers = gb_alloc_array(temporary_allocator(), int, possible_valid_dirs.count);
+
+ char buf[1024] = {};
+ for_array(i, possible_valid_dirs) {
+ FileInfo fi = possible_valid_dirs[i];
+ isize n = gb_min(gb_size_of(buf)-1, fi.name.len);
+ memcpy(buf, fi.name.text, n);
+ buf[n] = 0;
+
+ dir_numbers[i] = atoi(buf);
+ }
+
+ isize closest_number_idx = -1;
+ for (isize i = 0; i < possible_valid_dirs.count; i++) {
+ if (dir_numbers[i] >= ODIN_ANDROID_API_LEVEL) {
+ if (closest_number_idx < 0) {
+ closest_number_idx = i;
+ } else if (dir_numbers[i] < dir_numbers[closest_number_idx]) {
+ closest_number_idx = i;
+ }
+ }
+ }
+
+ if (closest_number_idx < 0) {
+ gb_printf_err("Unable to find any Android SDK/API Level in %.*s meeting the minimum API level of %d\n", LIT(android_sdk_build_tools), ODIN_ANDROID_API_LEVEL);
+ return 1;
+ }
+
+ String api_number = possible_valid_dirs[closest_number_idx].name;
+
+ android_sdk_build_tools = concatenate_strings(temporary_allocator(), android_sdk_build_tools, api_number);
+ String android_sdk_platforms = concatenate_strings(temporary_allocator(),
+ build_context.ODIN_ANDROID_SDK,
+ make_string_c(gb_bprintf("platforms/android-%d/", dir_numbers[closest_number_idx]))
+ );
+
+ android_sdk_build_tools = normalize_path(temporary_allocator(), android_sdk_build_tools, NIX_SEPARATOR_STRING);
+ android_sdk_platforms = normalize_path(temporary_allocator(), android_sdk_platforms, NIX_SEPARATOR_STRING);
+
+ gbString cmd = gb_string_make(heap_allocator(), "");
+ defer (gb_string_free(cmd));
+
+
+ String output_filename = str_lit("test");
+ String output_apk = path_remove_extension(output_filename);
+
+ TIME_SECTION("Android aapt");
+ {
+ TEMPORARY_ALLOCATOR_GUARD();
+ gb_string_clear(cmd);
+
+ String manifest = concatenate_strings(temporary_allocator(), init_directory, str_lit("AndroidManifest.xml"));
+
+ cmd = gb_string_append_length(cmd, android_sdk_build_tools.text, android_sdk_build_tools.len);
+ cmd = gb_string_appendc(cmd, "aapt");
+ cmd = gb_string_appendc(cmd, " package -f");
+ cmd = gb_string_append_fmt(cmd, " -M \"%.*s\"", LIT(manifest));
+ cmd = gb_string_append_fmt(cmd, " -I \"%.*sandroid.jar\"", LIT(android_sdk_platforms));
+ cmd = gb_string_append_fmt(cmd, " -F \"%.*s.apk-build\"", LIT(output_apk));
+
+ String resources_dir = concatenate_strings(temporary_allocator(), init_directory, str_lit("res"));
+ if (gb_file_exists((const char *)resources_dir.text)) {
+ cmd = gb_string_append_fmt(cmd, " -S \"%.*s\"", LIT(resources_dir));
+ }
+
+ String assets_dir = concatenate_strings(temporary_allocator(), init_directory, str_lit("assets"));
+ if (gb_file_exists((const char *)assets_dir.text)) {
+ cmd = gb_string_append_fmt(cmd, " -A \"%.*s\"", LIT(assets_dir));
+ }
+
+ String lib_dir = concatenate_strings(temporary_allocator(), init_directory, str_lit("lib"));
+ if (gb_file_exists((const char *)lib_dir.text)) {
+ cmd = gb_string_append_fmt(cmd, " \"%.*s\"", LIT(lib_dir));
+ }
+
+ result = system_exec_command_line_app("android-aapt", cmd);
+ if (result) {
+ return result;
+ }
+ }
+
+ TIME_SECTION("Android zipalign");
+ {
+ TEMPORARY_ALLOCATOR_GUARD();
+ gb_string_clear(cmd);
+
+ cmd = gb_string_append_length(cmd, android_sdk_build_tools.text, android_sdk_build_tools.len);
+ cmd = gb_string_appendc(cmd, "zipalign");
+ cmd = gb_string_appendc(cmd, " -f 4");
+ cmd = gb_string_append_fmt(cmd, " \"%.*s.apk-build\" \"%.*s.apk\"", LIT(output_apk), LIT(output_apk));
+
+ result = system_exec_command_line_app("android-zipalign", cmd);
+ if (result) {
+ return result;
+ }
+ }
+
+ TIME_SECTION("Android apksigner");
+ {
+ TEMPORARY_ALLOCATOR_GUARD();
+ gb_string_clear(cmd);
+
+ cmd = gb_string_append_length(cmd, android_sdk_build_tools.text, android_sdk_build_tools.len);
+ cmd = gb_string_appendc(cmd, "apksigner");
+ cmd = gb_string_appendc(cmd, " sign");
+
+ String keystore = normalize_path(temporary_allocator(), build_context.android_keystore, NIX_SEPARATOR_STRING);
+ keystore = substring(keystore, 0, keystore.len - 1);
+ cmd = gb_string_append_fmt(cmd, " --ks \"%.*s\"", LIT(keystore));
+
+ if (build_context.android_keystore_alias.len != 0) {
+ cmd = gb_string_append_fmt(cmd, " --ks-key-alias \"%.*s\"", LIT(build_context.android_keystore_alias));
+ }
+ if (build_context.android_keystore_password.len != 0) {
+ cmd = gb_string_append_fmt(cmd, " --ks-pass pass:\"%.*s\"", LIT(build_context.android_keystore_password));
+ }
+
+ cmd = gb_string_append_fmt(cmd, " \"%.*s.apk\"", LIT(output_apk));
+
+ result = system_exec_command_line_app("android-apksigner", cmd);
+ if (result) {
+ return result;
+ }
+ }
+
+ return 0;
+}
diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp
index c86503093..f66a8605c 100644
--- a/src/check_builtin.cpp
+++ b/src/check_builtin.cpp
@@ -645,6 +645,13 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan
break;
}
+ if (!are_types_identical(x.type, y.type)) {
+ gbString tx = type_to_string(x.type);
+ gbString ty = type_to_string(y.type);
+ error(call, "Mismatched types to '%.*s', '%s' vs '%s'", LIT(builtin_name), tx, ty);
+ gb_string_free(ty);
+ gb_string_free(tx);
+ }
Type *vt = base_type(x.type);
GB_ASSERT(vt->kind == Type_SimdVector);
@@ -888,6 +895,39 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan
return true;
}
+ case BuiltinProc_simd_extract_lsbs:
+ case BuiltinProc_simd_extract_msbs:
+ {
+ Operand x = {};
+ check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false;
+
+ if (!is_type_simd_vector(x.type)) {
+ gbString xs = type_to_string(x.type);
+ error(x.expr, "'%.*s' expected a simd vector type, got '%s'", LIT(builtin_name), xs);
+ gb_string_free(xs);
+ return false;
+ }
+
+ Type *elem = base_array_type(x.type);
+ if (!is_type_integer_like(elem)) {
+ gbString xs = type_to_string(x.type);
+ error(x.expr, "'%.*s' expected a #simd type with integer or boolean elements, got '%s'", LIT(builtin_name), xs);
+ gb_string_free(xs);
+ return false;
+ }
+
+ i64 num_elems = get_array_type_count(x.type);
+
+ Type *result_type = alloc_type_bit_set();
+ result_type->BitSet.elem = t_int;
+ result_type->BitSet.lower = 0;
+ result_type->BitSet.upper = num_elems - 1;
+
+ operand->mode = Addressing_Value;
+ operand->type = result_type;
+ return true;
+ }
+
case BuiltinProc_simd_shuffle:
{
@@ -1642,12 +1682,16 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o
}
if (ce->args.count > 0) {
Ast *arg = ce->args[0];
- Operand o = {};
- Entity *e = check_ident(c, &o, arg, nullptr, nullptr, true);
- if (e == nullptr || (e->flags & EntityFlag_Param) == 0) {
- error(ce->args[0], "'#caller_expression' expected a valid earlier parameter name");
+ if (arg->kind != Ast_Ident) {
+ error(arg, "'#caller_expression' expected an identifier");
+ } else {
+ Operand o = {};
+ Entity *e = check_ident(c, &o, arg, nullptr, nullptr, true);
+ if (e == nullptr || (e->flags & EntityFlag_Param) == 0) {
+ error(arg, "'#caller_expression' expected a valid earlier parameter name");
+ }
+ arg->Ident.entity = e;
}
- arg->Ident.entity = e;
}
operand->type = t_string;
@@ -2551,6 +2595,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_swizzle: {
// swizzle :: proc(v: [N]T, ..int) -> [M]T
+ if (!operand->type) {
+ return false;
+ }
+
Type *original_type = operand->type;
Type *type = base_type(original_type);
i64 max_count = 0;
@@ -2908,6 +2956,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
// imag :: proc(x: type) -> float_type
Operand *x = operand;
+ if (!x->type) {
+ return false;
+ }
+
if (is_type_untyped(x->type)) {
if (x->mode == Addressing_Constant) {
if (is_type_numeric(x->type)) {
@@ -2968,6 +3020,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
// kmag :: proc(x: type) -> float_type
Operand *x = operand;
+ if (!x->type) {
+ return false;
+ }
+
if (is_type_untyped(x->type)) {
if (x->mode == Addressing_Constant) {
if (is_type_numeric(x->type)) {
@@ -3017,6 +3073,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_conj: {
// conj :: proc(x: type) -> type
Operand *x = operand;
+ if (!x->type) {
+ return false;
+ }
+
Type *t = x->type;
Type *elem = core_array_type(t);
@@ -3057,10 +3117,14 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
case BuiltinProc_expand_values: {
+ if (!operand->type) {
+ return false;
+ }
+
Type *type = base_type(operand->type);
if (!is_type_struct(type) && !is_type_array(type)) {
gbString type_str = type_to_string(operand->type);
- error(call, "Expected a struct or array type, got '%s'", type_str);
+ error(call, "Expected a struct or array type to 'expand_values', got '%s'", type_str);
gb_string_free(type_str);
return false;
}
@@ -3096,8 +3160,13 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
check_multi_expr_or_type(c, operand, ce->args[0]);
+ if (!operand->type) {
+ return false;
+ }
+
Type *original_type = operand->type;
Type *type = base_type(operand->type);
+
if (operand->mode == Addressing_Type && is_type_enumerated_array(type)) {
// Okay
} else if (!is_type_ordered(type) || !(is_type_numeric(type) || is_type_string(type))) {
@@ -3268,6 +3337,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
check_multi_expr_or_type(c, operand, ce->args[0]);
+ if (!operand->type) {
+ return false;
+ }
+
Type *original_type = operand->type;
Type *type = base_type(operand->type);
@@ -3443,6 +3516,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_abs: {
// abs :: proc(n: numeric) -> numeric
+ if (!operand->type) {
+ return false;
+ }
+
if (!(is_type_numeric(operand->type) && !is_type_array(operand->type))) {
gbString type_str = type_to_string(operand->type);
error(call, "Expected a numeric type to 'abs', got '%s'", type_str);
@@ -3455,9 +3532,12 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case ExactValue_Integer:
mp_abs(&operand->value.value_integer, &operand->value.value_integer);
break;
- case ExactValue_Float:
- operand->value.value_float = gb_abs(operand->value.value_float);
+ case ExactValue_Float: {
+ u64 abs = bit_cast(operand->value.value_float);
+ abs &= 0x7FFFFFFFFFFFFFFF;
+ operand->value.value_float = bit_cast(abs);
break;
+ }
case ExactValue_Complex: {
f64 r = operand->value.value_complex->real;
f64 i = operand->value.value_complex->imag;
@@ -3498,6 +3578,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_clamp: {
// clamp :: proc(a, min, max: ordered) -> ordered
+ if (!operand->type) {
+ return false;
+ }
+
Type *type = operand->type;
if (!is_type_ordered(type) || !(is_type_numeric(type) || is_type_string(type))) {
gbString type_str = type_to_string(operand->type);
@@ -5466,6 +5550,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case Type_EnumeratedArray: operand->type = bt->EnumeratedArray.elem; break;
case Type_Slice: operand->type = bt->Slice.elem; break;
case Type_DynamicArray: operand->type = bt->DynamicArray.elem; break;
+ case Type_SimdVector: operand->type = bt->SimdVector.elem; break;
}
}
operand->mode = Addressing_Type;
@@ -5507,6 +5592,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
// NOTE(bill): Is this even correct?
new_type->Union.node = operand->expr;
new_type->Union.scope = bt->Union.scope;
+ if (bt->Union.kind == UnionType_no_nil) {
+ new_type->Union.kind = UnionType_no_nil;
+ }
operand->type = new_type;
}
diff --git a/src/check_decl.cpp b/src/check_decl.cpp
index 60eb030ff..ba6445ea4 100644
--- a/src/check_decl.cpp
+++ b/src/check_decl.cpp
@@ -60,7 +60,7 @@ gb_internal Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *o
error(operand->expr, "Cannot assign a type '%s' to variable '%.*s'", t, LIT(e->token.string));
}
if (e->type == nullptr) {
- error_line("\tThe type of the variable '%.*s' cannot be inferred as a type does not have a default type\n", LIT(e->token.string));
+ error_line("\tThe type of the variable '%.*s' cannot be inferred as a type and does not have a default type\n", LIT(e->token.string));
}
e->type = operand->type;
return nullptr;
@@ -94,12 +94,14 @@ gb_internal Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *o
return nullptr;
}
if (e2->state.load() != EntityState_Resolved) {
- gbString str = type_to_string(t);
- defer (gb_string_free(str));
- error(e->token, "Invalid use of a polymorphic type '%s' in %.*s", str, LIT(context_name));
- e->type = t_invalid;
+ e->type = t;
return nullptr;
}
+ gbString str = type_to_string(t);
+ defer (gb_string_free(str));
+ error(operand->expr, "Invalid use of a non-specialized polymorphic type '%s' in %.*s", str, LIT(context_name));
+ e->type = t_invalid;
+ return nullptr;
} else if (is_type_empty_union(t)) {
gbString str = type_to_string(t);
defer (gb_string_free(str));
@@ -466,6 +468,10 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr,
}
e->type = named;
+ if (!is_distinct) {
+ e->TypeName.is_type_alias = true;
+ }
+
check_type_path_push(ctx, e);
Type *bt = check_type_expr(ctx, te, named);
check_type_path_pop(ctx);
@@ -500,9 +506,9 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr,
if (!is_distinct) {
e->type = bt;
named->Named.base = bt;
- e->TypeName.is_type_alias = true;
}
+ e->TypeName.is_type_alias = !is_distinct;
if (decl->type_expr != nullptr) {
Type *t = check_type(ctx, decl->type_expr);
@@ -626,6 +632,10 @@ gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr
Operand x = {};
x.type = entity->type;
x.mode = Addressing_Variable;
+ if (entity->kind == Entity_Constant) {
+ x.mode = Addressing_Constant;
+ x.value = entity->Constant.value;
+ }
if (!check_is_assignable_to(ctx, &x, e->type)) {
gbString expr_str = expr_to_string(init);
gbString op_type_str = type_to_string(entity->type);
@@ -855,6 +865,7 @@ gb_internal Entity *init_entity_foreign_library(CheckerContext *ctx, Entity *e)
} else {
String name = ident->Ident.token.string;
Entity *found = scope_lookup(ctx->scope, name);
+
if (found == nullptr) {
if (is_blank_ident(name)) {
// NOTE(bill): link against nothing
@@ -971,6 +982,43 @@ gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeCon
}
}
+gb_internal void check_foreign_procedure(CheckerContext *ctx, Entity *e, DeclInfo *d) {
+ GB_ASSERT(e != nullptr);
+ GB_ASSERT(e->kind == Entity_Procedure);
+ String name = e->Procedure.link_name;
+
+ mutex_lock(&ctx->info->foreign_mutex);
+
+ auto *fp = &ctx->info->foreigns;
+ StringHashKey key = string_hash_string(name);
+ Entity **found = string_map_get(fp, key);
+ if (found && e != *found) {
+ Entity *f = *found;
+ TokenPos pos = f->token.pos;
+ Type *this_type = base_type(e->type);
+ Type *other_type = base_type(f->type);
+ if (is_type_proc(this_type) && is_type_proc(other_type)) {
+ if (!are_signatures_similar_enough(this_type, other_type)) {
+ error(d->proc_lit,
+ "Redeclaration of foreign procedure '%.*s' with different type signatures\n"
+ "\tat %s",
+ LIT(name), token_pos_to_string(pos));
+ }
+ } else if (!signature_parameter_similar_enough(this_type, other_type)) {
+ error(d->proc_lit,
+ "Foreign entity '%.*s' previously declared elsewhere with a different type\n"
+ "\tat %s",
+ LIT(name), token_pos_to_string(pos));
+ }
+ } else if (name == "main") {
+ error(d->proc_lit, "The link name 'main' is reserved for internal use");
+ } else {
+ string_map_set(fp, key, e);
+ }
+
+ mutex_unlock(&ctx->info->foreign_mutex);
+}
+
gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
GB_ASSERT(e->type == nullptr);
if (d->proc_lit->kind != Ast_ProcLit) {
@@ -1307,57 +1355,16 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
name = e->Procedure.link_name;
}
Entity *foreign_library = init_entity_foreign_library(ctx, e);
-
- if (is_arch_wasm() && foreign_library != nullptr) {
- String module_name = str_lit("env");
- GB_ASSERT (foreign_library->kind == Entity_LibraryName);
- if (foreign_library->LibraryName.paths.count != 1) {
- error(foreign_library->token, "'foreign import' for '%.*s' architecture may only have one path, got %td",
- LIT(target_arch_names[build_context.metrics.arch]), foreign_library->LibraryName.paths.count);
- }
-
- if (foreign_library->LibraryName.paths.count >= 1) {
- module_name = foreign_library->LibraryName.paths[0];
- }
-
- if (!string_ends_with(module_name, str_lit(".o"))) {
- name = concatenate3_strings(permanent_allocator(), module_name, WASM_MODULE_NAME_SEPARATOR, name);
- }
- }
-
e->Procedure.is_foreign = true;
e->Procedure.link_name = name;
+ e->Procedure.foreign_library = foreign_library;
- mutex_lock(&ctx->info->foreign_mutex);
-
- auto *fp = &ctx->info->foreigns;
- StringHashKey key = string_hash_string(name);
- Entity **found = string_map_get(fp, key);
- if (found && e != *found) {
- Entity *f = *found;
- TokenPos pos = f->token.pos;
- Type *this_type = base_type(e->type);
- Type *other_type = base_type(f->type);
- if (is_type_proc(this_type) && is_type_proc(other_type)) {
- if (!are_signatures_similar_enough(this_type, other_type)) {
- error(d->proc_lit,
- "Redeclaration of foreign procedure '%.*s' with different type signatures\n"
- "\tat %s",
- LIT(name), token_pos_to_string(pos));
- }
- } else if (!signature_parameter_similar_enough(this_type, other_type)) {
- error(d->proc_lit,
- "Foreign entity '%.*s' previously declared elsewhere with a different type\n"
- "\tat %s",
- LIT(name), token_pos_to_string(pos));
- }
- } else if (name == "main") {
- error(d->proc_lit, "The link name 'main' is reserved for internal use");
+ if (is_arch_wasm() && foreign_library != nullptr) {
+ // NOTE(bill): this must be delayed because the foreign import paths might not be evaluated yet until much later
+ mpsc_enqueue(&ctx->info->foreign_decls_to_check, e);
} else {
- string_map_set(fp, key, e);
+ check_foreign_procedure(ctx, e, d);
}
-
- mutex_unlock(&ctx->info->foreign_mutex);
} else {
String name = e->token.string;
if (e->Procedure.link_name.len > 0) {
@@ -1743,8 +1750,8 @@ gb_internal void add_deps_from_child_to_parent(DeclInfo *decl) {
rw_mutex_shared_lock(&decl->type_info_deps_mutex);
rw_mutex_lock(&decl->parent->type_info_deps_mutex);
- for (Type *t : decl->type_info_deps) {
- ptr_set_add(&decl->parent->type_info_deps, t);
+ for (auto const &tt : decl->type_info_deps) {
+ type_set_add(&decl->parent->type_info_deps, tt);
}
rw_mutex_unlock(&decl->parent->type_info_deps_mutex);
@@ -1785,6 +1792,10 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
ctx->curr_proc_sig = type;
ctx->curr_proc_calling_convention = type->Proc.calling_convention;
+ if (decl->parent && decl->entity && decl->parent->entity) {
+ decl->entity->parent_proc_decl = decl->parent;
+ }
+
if (ctx->pkg->name != "runtime") {
switch (type->Proc.calling_convention) {
case ProcCC_None:
@@ -1874,6 +1885,8 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
check_open_scope(ctx, body);
{
+ ctx->scope->decl_info = decl;
+
for (auto const &entry : using_entities) {
Entity *uvar = entry.uvar;
Entity *prev = scope_insert(ctx->scope, uvar);
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index cb4647f33..91d9e669f 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -345,7 +345,7 @@ gb_internal void check_scope_decls(CheckerContext *c, Slice const &nodes,
check_collect_entities(c, nodes);
for (auto const &entry : s->elements) {
- Entity *e = entry.value;
+ Entity *e = entry.value;\
switch (e->kind) {
case Entity_Constant:
case Entity_TypeName:
@@ -863,6 +863,11 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
if (are_types_identical(vt, s)) {
return 1;
}
+ if (is_type_proc(vt)) {
+ if (are_types_identical(base_type(vt), src)) {
+ return 1;
+ }
+ }
}
if (dst->Union.variants.count == 1) {
@@ -1044,7 +1049,7 @@ gb_internal AstPackage *get_package_of_type(Type *type) {
}
-// NOTE(bill): 'content_name' is for debugging and error messages
+// NOTE(bill): 'context_name' is for debugging and error messages
gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *type, String context_name) {
check_not_tuple(c, operand);
if (operand->mode == Addressing_Invalid) {
@@ -1973,10 +1978,10 @@ gb_internal bool check_binary_op(CheckerContext *c, Operand *o, Token op) {
case Token_Quo:
case Token_QuoEq:
if (is_type_matrix(main_type)) {
- error(op, "Operator '%.*s' is only allowed with matrix types", LIT(op.string));
+ error(op, "Operator '%.*s' is not allowed with matrix types", LIT(op.string));
return false;
} else if (is_type_simd_vector(main_type) && is_type_integer(type)) {
- error(op, "Operator '%.*s' is only allowed with #simd types with integer elements", LIT(op.string));
+ error(op, "Operator '%.*s' is not allowed with #simd types with integer elements", LIT(op.string));
return false;
}
/*fallthrough*/
@@ -2023,14 +2028,14 @@ gb_internal bool check_binary_op(CheckerContext *c, Operand *o, Token op) {
case Token_ModEq:
case Token_ModModEq:
if (is_type_matrix(main_type)) {
- error(op, "Operator '%.*s' is only allowed with matrix types", LIT(op.string));
+ error(op, "Operator '%.*s' is not allowed with matrix types", LIT(op.string));
return false;
}
if (!is_type_integer(type)) {
error(op, "Operator '%.*s' is only allowed with integers", LIT(op.string));
return false;
} else if (is_type_simd_vector(main_type)) {
- error(op, "Operator '%.*s' is only allowed with #simd types with integer elements", LIT(op.string));
+ error(op, "Operator '%.*s' is not allowed with #simd types with integer elements", LIT(op.string));
return false;
}
break;
@@ -2599,9 +2604,8 @@ gb_internal ExactValue exact_bit_set_all_set_mask(Type *type) {
continue;
}
- BigInt shift_amount = f->Constant.value.value_integer;
- big_int_sub_eq(&shift_amount, &b_lower_base);
-
+ BigInt shift_amount = {};
+ big_int_sub(&shift_amount, &f->Constant.value.value_integer, &b_lower_base);
BigInt value = {};
big_int_shl(&value, &one, &shift_amount);
@@ -3541,6 +3545,8 @@ gb_internal bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type
return false;
}
+ Operand src = *o;
+
Type *src_t = o->type;
Type *dst_t = t;
Type *src_bt = base_type(src_t);
@@ -3629,7 +3635,8 @@ gb_internal bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type
// identical casts that cannot be foreseen or otherwise
// forbidden, so just skip them.
if (forbid_identical && check_vet_flags(c) & VetFlag_Cast &&
- (c->curr_proc_sig == nullptr || !is_type_polymorphic(c->curr_proc_sig))) {
+ (c->curr_proc_sig == nullptr || !is_type_polymorphic(c->curr_proc_sig)) &&
+ check_is_castable_to(c, &src, dst_t)) {
if (are_types_identical(src_t, dst_t)) {
gbString oper_str = expr_to_string(o->expr);
gbString to_type = type_to_string(dst_t);
@@ -3646,7 +3653,8 @@ gb_internal bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type
gb_string_free(oper_str);
gb_string_free(to_type);
} else if (is_type_integer(src_t) && is_type_integer(dst_t) &&
- types_have_same_internal_endian(src_t, dst_t)) {
+ types_have_same_internal_endian(src_t, dst_t) &&
+ type_endian_kind_of(src_t) == type_endian_kind_of(dst_t)) {
gbString oper_type = type_to_string(src_t);
gbString to_type = type_to_string(dst_t);
error(o->expr, "Use of 'transmute' where 'cast' would be preferred since both are integers of the same endianness, from '%s' to '%s'", oper_type, to_type);
@@ -3662,6 +3670,11 @@ gb_internal bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type
}
gb_internal bool check_binary_array_expr(CheckerContext *c, Token op, Operand *x, Operand *y) {
+ if (is_type_array_like(x->type) || is_type_array_like(y->type)) {
+ if (op.kind == Token_CmpAnd || op.kind == Token_CmpOr) {
+ error(op, "Array programming is not allowed with the operator '%.*s'", LIT(op.string));
+ }
+ }
if (is_type_array(x->type) && !is_type_array(y->type)) {
if (check_is_assignable_to(c, y, x->type)) {
if (check_binary_op(c, x, op)) {
@@ -3669,6 +3682,13 @@ gb_internal bool check_binary_array_expr(CheckerContext *c, Token op, Operand *x
}
}
}
+ if (is_type_simd_vector(x->type) && !is_type_simd_vector(y->type)) {
+ if (check_is_assignable_to(c, y, x->type)) {
+ if (check_binary_op(c, x, op)) {
+ return true;
+ }
+ }
+ }
return false;
}
@@ -4451,8 +4471,8 @@ gb_internal ExactValue convert_exact_value_for_type(ExactValue v, Type *type) {
}
gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) {
- GB_ASSERT_NOT_NULL(target_type);
- if (operand->mode == Addressing_Invalid ||
+ // GB_ASSERT_NOT_NULL(target_type);
+ if (target_type == nullptr || operand->mode == Addressing_Invalid ||
operand->mode == Addressing_Type ||
is_type_typed(operand->type) ||
target_type == t_invalid) {
@@ -4493,8 +4513,7 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
} else {
switch (operand->type->Basic.kind) {
case Basic_UntypedBool:
- if (!is_type_boolean(target_type) &&
- !is_type_integer(target_type)) {
+ if (!is_type_boolean(target_type)) {
operand->mode = Addressing_Invalid;
convert_untyped_error(c, operand, target_type);
return;
@@ -4553,6 +4572,19 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
break;
}
+ case Type_SimdVector: {
+ Type *elem = base_array_type(t);
+ if (check_is_assignable_to(c, operand, elem)) {
+ operand->mode = Addressing_Value;
+ } else {
+ operand->mode = Addressing_Invalid;
+ convert_untyped_error(c, operand, target_type);
+ return;
+ }
+
+ break;
+ }
+
case Type_Matrix: {
Type *elem = base_array_type(t);
if (check_is_assignable_to(c, operand, elem)) {
@@ -4977,8 +5009,12 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v
if (success_) *success_ = true;
if (finish_) *finish_ = false;
return tav.value;
+ } else if (is_type_proc(tav.type)) {
+ if (success_) *success_ = true;
+ if (finish_) *finish_ = false;
+ return tav.value;
} else {
- GB_ASSERT(is_type_untyped_nil(tav.type));
+ GB_ASSERT_MSG(is_type_untyped_nil(tav.type), "%s", type_to_string(tav.type));
if (success_) *success_ = true;
if (finish_) *finish_ = false;
return tav.value;
@@ -5481,6 +5517,11 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
case Addressing_SwizzleVariable:
operand->mode = Addressing_SwizzleVariable;
break;
+ case Addressing_Value:
+ if (is_type_pointer(original_type)) {
+ operand->mode = Addressing_SwizzleVariable;
+ }
+ break;
}
if (array_type->kind == Type_SimdVector) {
@@ -8718,6 +8759,18 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A
error(node, "#caller_expression may only be used as a default argument parameter");
o->type = t_string;
o->mode = Addressing_Value;
+ } else if (name == "branch_location") {
+ if (!c->in_defer) {
+ error(node, "#branch_location may only be used within a 'defer' statement");
+ } else if (c->curr_proc_decl) {
+ Entity *e = c->curr_proc_decl->entity;
+ if (e != nullptr) {
+ GB_ASSERT(e->kind == Entity_Procedure);
+ e->Procedure.uses_branch_location = true;
+ }
+ }
+ o->type = t_source_code_location;
+ o->mode = Addressing_Value;
} else {
if (name == "location") {
init_core_source_code_location(c->checker);
@@ -8924,8 +8977,14 @@ gb_internal ExprKind check_or_else_expr(CheckerContext *c, Operand *o, Ast *node
o->expr = node;
return Expr_Expr;
}
+
+ Type *left_type = nullptr;
+ Type *right_type = nullptr;
+ check_or_else_split_types(c, &x, name, &left_type, &right_type);
+ add_type_and_value(c, arg, x.mode, x.type, x.value);
+
bool y_is_diverging = false;
- check_expr_base(c, &y, default_value, x.type);
+ check_expr_base(c, &y, default_value, left_type);
switch (y.mode) {
case Addressing_NoValue:
if (is_diverging_expr(y.expr)) {
@@ -8950,11 +9009,6 @@ gb_internal ExprKind check_or_else_expr(CheckerContext *c, Operand *o, Ast *node
return Expr_Expr;
}
- Type *left_type = nullptr;
- Type *right_type = nullptr;
- check_or_else_split_types(c, &x, name, &left_type, &right_type);
- add_type_and_value(c, arg, x.mode, x.type, x.value);
-
if (left_type != nullptr) {
if (!y_is_diverging) {
check_assignment(c, &y, left_type, name);
@@ -9332,6 +9386,23 @@ gb_internal bool is_expr_inferred_fixed_array(Ast *type_expr) {
return false;
}
+gb_internal bool check_for_dynamic_literals(CheckerContext *c, Ast *node, AstCompoundLit *cl) {
+ if (cl->elems.count > 0 && (check_feature_flags(c, node) & OptInFeatureFlag_DynamicLiterals) == 0) {
+ ERROR_BLOCK();
+ error(node, "Compound literals of dynamic types are disabled by default");
+ error_line("\tSuggestion: If you want to enable them for this specific file, add '#+feature dynamic-literals' at the top of the file\n");
+ error_line("\tWarning: Please understand that dynamic literals will implicitly allocate using the current 'context.allocator' in that scope\n");
+ if (build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR) {
+ error_line("\tWarning: As '-default-to-panic-allocator' has been set, the dynamic compound literal may not be initialized as expected\n");
+ } else if (build_context.ODIN_DEFAULT_TO_PANIC_ALLOCATOR) {
+ error_line("\tWarning: As '-default-to-panic-allocator' has been set, the dynamic compound literal may not be initialized as expected\n");
+ }
+ return false;
+ }
+
+ return cl->elems.count > 0;
+}
+
gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
ExprKind kind = Expr_Expr;
ast_node(cl, CompoundLit, node);
@@ -9532,11 +9603,6 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
elem_type = t->DynamicArray.elem;
context_name = str_lit("dynamic array literal");
is_constant = false;
-
- if (!build_context.no_dynamic_literals) {
- add_package_dependency(c, "runtime", "__dynamic_array_reserve");
- add_package_dependency(c, "runtime", "__dynamic_array_append");
- }
} else if (t->kind == Type_SimdVector) {
elem_type = t->SimdVector.elem;
context_name = str_lit("simd vector literal");
@@ -9711,8 +9777,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
if (t->kind == Type_DynamicArray) {
- if (build_context.no_dynamic_literals && cl->elems.count) {
- error(node, "Compound literals of dynamic types have been disabled");
+ if (check_for_dynamic_literals(c, node, cl)) {
+ add_package_dependency(c, "runtime", "__dynamic_array_reserve");
+ add_package_dependency(c, "runtime", "__dynamic_array_append");
}
}
@@ -10101,9 +10168,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
}
}
- if (build_context.no_dynamic_literals && cl->elems.count) {
- error(node, "Compound literals of dynamic types have been disabled");
- } else {
+ if (check_for_dynamic_literals(c, node, cl)) {
add_map_reserve_dependencies(c);
add_map_set_dependencies(c);
}
@@ -10325,7 +10390,7 @@ gb_internal ExprKind check_type_assertion(CheckerContext *c, Operand *o, Ast *no
add_type_info_type(c, o->type);
o->type = type_hint;
o->mode = Addressing_OptionalOk;
- return kind;
+ goto end;
}
}
@@ -10390,6 +10455,8 @@ gb_internal ExprKind check_type_assertion(CheckerContext *c, Operand *o, Ast *no
}
}
+end:;
+
if ((c->state_flags & StateFlag_no_type_assert) == 0) {
add_package_dependency(c, "runtime", "type_assertion_check");
add_package_dependency(c, "runtime", "type_assertion_check2");
diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp
index 2418fcc5c..1b44ff4d7 100644
--- a/src/check_stmt.cpp
+++ b/src/check_stmt.cpp
@@ -894,15 +894,49 @@ gb_internal void error_var_decl_identifier(Ast *name) {
}
}
-gb_internal void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
+gb_internal void check_unroll_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
ast_node(irs, UnrollRangeStmt, node);
check_open_scope(ctx, node);
+ defer (check_close_scope(ctx));
Type *val0 = nullptr;
Type *val1 = nullptr;
Entity *entities[2] = {};
isize entity_count = 0;
+ i64 unroll_count = -1;
+
+ if (irs->args.count > 0) {
+ if (irs->args.count > 1) {
+ error(irs->args[1], "#unroll only supports a single argument for the unroll per loop amount");
+ }
+ Ast *arg = irs->args[0];
+ if (arg->kind == Ast_FieldValue) {
+ error(arg, "#unroll does not yet support named arguments");
+ arg = arg->FieldValue.value;
+ }
+
+ Operand x = {};
+ check_expr(ctx, &x, arg);
+ if (x.mode != Addressing_Constant || !is_type_integer(x.type)) {
+ gbString s = expr_to_string(x.expr);
+ error(x.expr, "Expected a constant integer for #unroll, got '%s'", s);
+ gb_string_free(s);
+ } else {
+ ExactValue value = exact_value_to_integer(x.value);
+ i64 v = exact_value_to_i64(value);
+ if (v < 1) {
+ error(x.expr, "Expected a constant integer >= 1 for #unroll, got %lld", cast(long long)v);
+ } else {
+ unroll_count = v;
+ if (v > 1024) {
+ error(x.expr, "Too large of a value for #unroll, got %lld, expected <= 1024", cast(long long)v);
+ }
+ }
+
+ }
+ }
+
Ast *expr = unparen_expr(irs->expr);
ExactValue inline_for_depth = exact_value_i64(0);
@@ -946,18 +980,39 @@ gb_internal void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod
val0 = t_rune;
val1 = t_int;
inline_for_depth = exact_value_i64(operand.value.value_string.len);
+ if (unroll_count > 0) {
+ error(node, "#unroll(%lld) does not support strings", cast(long long)unroll_count);
+ }
}
break;
case Type_Array:
val0 = t->Array.elem;
val1 = t_int;
- inline_for_depth = exact_value_i64(t->Array.count);
+ inline_for_depth = unroll_count > 0 ? exact_value_i64(unroll_count) : exact_value_i64(t->Array.count);
break;
case Type_EnumeratedArray:
val0 = t->EnumeratedArray.elem;
val1 = t->EnumeratedArray.index;
+ if (unroll_count > 0) {
+ error(node, "#unroll(%lld) does not support enumerated arrays", cast(long long)unroll_count);
+ }
inline_for_depth = exact_value_i64(t->EnumeratedArray.count);
break;
+
+ case Type_Slice:
+ if (unroll_count > 0) {
+ val0 = t->Slice.elem;
+ val1 = t_int;
+ inline_for_depth = exact_value_i64(unroll_count);
+ }
+ break;
+ case Type_DynamicArray:
+ if (unroll_count > 0) {
+ val0 = t->DynamicArray.elem;
+ val1 = t_int;
+ inline_for_depth = exact_value_i64(unroll_count);
+ }
+ break;
}
}
@@ -967,7 +1022,7 @@ gb_internal void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod
error(operand.expr, "Cannot iterate over '%s' of type '%s' in an '#unroll for' statement", s, t);
gb_string_free(t);
gb_string_free(s);
- } else if (operand.mode != Addressing_Constant) {
+ } else if (operand.mode != Addressing_Constant && unroll_count <= 0) {
error(operand.expr, "An '#unroll for' expression must be known at compile time");
}
}
@@ -1050,8 +1105,6 @@ gb_internal void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod
check_stmt(ctx, irs->body, mod_flags);
-
- check_close_scope(ctx);
}
gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
@@ -1393,8 +1446,8 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_
Ast *nil_seen = nullptr;
- PtrSet seen = {};
- defer (ptr_set_destroy(&seen));
+ TypeSet seen = {};
+ defer (type_set_destroy(&seen));
for (Ast *stmt : bs->stmts) {
if (stmt->kind != Ast_CaseClause) {
@@ -1462,7 +1515,7 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_
GB_PANIC("Unknown type to type switch statement");
}
- if (type_ptr_set_update(&seen, y.type)) {
+ if (type_set_update(&seen, y.type)) {
TokenPos pos = cc->token.pos;
gbString expr_str = expr_to_string(y.expr);
error(y.expr,
@@ -1516,7 +1569,7 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_
auto unhandled = array_make(temporary_allocator(), 0, variants.count);
for (Type *t : variants) {
- if (!type_ptr_set_exists(&seen, t)) {
+ if (!type_set_exists(&seen, t)) {
array_add(&unhandled, t);
}
}
@@ -2605,6 +2658,7 @@ gb_internal void check_for_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
if (cond && cond->kind == Ast_BinaryExpr &&
cond->BinaryExpr.left && cond->BinaryExpr.right &&
cond->BinaryExpr.op.kind == Token_GtEq &&
+ type_of_expr(cond->BinaryExpr.left) != nullptr &&
is_type_unsigned(type_of_expr(cond->BinaryExpr.left)) &&
cond->BinaryExpr.right->tav.value.kind == ExactValue_Integer &&
is_exact_value_zero(cond->BinaryExpr.right->tav.value)) {
@@ -2612,6 +2666,7 @@ gb_internal void check_for_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
} else if (cond && cond->kind == Ast_BinaryExpr &&
cond->BinaryExpr.left && cond->BinaryExpr.right &&
cond->BinaryExpr.op.kind == Token_LtEq &&
+ type_of_expr(cond->BinaryExpr.right) != nullptr &&
is_type_unsigned(type_of_expr(cond->BinaryExpr.right)) &&
cond->BinaryExpr.left->tav.value.kind == ExactValue_Integer &&
is_exact_value_zero(cond->BinaryExpr.left->tav.value)) {
@@ -2677,7 +2732,7 @@ gb_internal void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags)
case_end;
case_ast_node(irs, UnrollRangeStmt, node);
- check_inline_range_stmt(ctx, node, mod_flags);
+ check_unroll_range_stmt(ctx, node, mod_flags);
case_end;
case_ast_node(ss, SwitchStmt, node);
@@ -2700,6 +2755,47 @@ gb_internal void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags)
if (ctx->decl) {
ctx->decl->defer_used += 1;
}
+
+ // NOTE(bill): Handling errors/warnings
+
+ Ast *stmt = ds->stmt;
+ Ast *original_stmt = stmt;
+
+ bool is_singular = true;
+ while (is_singular && stmt->kind == Ast_BlockStmt) {
+ Ast *inner_stmt = nullptr;
+ for (Ast *s : stmt->BlockStmt.stmts) {
+ if (s->kind == Ast_EmptyStmt) {
+ continue;
+ }
+ if (inner_stmt != nullptr) {
+ is_singular = false;
+ break;
+ }
+ inner_stmt = s;
+ }
+
+ if (inner_stmt != nullptr) {
+ stmt = inner_stmt;
+ }
+ }
+ if (!is_singular) {
+ stmt = original_stmt;
+ }
+
+ switch (stmt->kind) {
+ case_ast_node(as, AssignStmt, stmt);
+ if (as->op.kind != Token_Eq) {
+ break;
+ }
+ for (Ast *lhs : as->lhs) {
+ Entity *e = entity_of_node(lhs);
+ if (e && e->flags & EntityFlag_Result) {
+ error(lhs, "Assignments to named return values within 'defer' will not affect the value that is returned");
+ }
+ }
+ case_end;
+ }
}
case_end;
diff --git a/src/check_type.cpp b/src/check_type.cpp
index 84e7fb249..1549f477e 100644
--- a/src/check_type.cpp
+++ b/src/check_type.cpp
@@ -685,7 +685,8 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *
ST_ALIGN(min_field_align);
ST_ALIGN(max_field_align);
ST_ALIGN(align);
- if (struct_type->Struct.custom_align < struct_type->Struct.custom_min_field_align) {
+ if (struct_type->Struct.custom_align != 0 &&
+ struct_type->Struct.custom_align < struct_type->Struct.custom_min_field_align) {
error(st->align, "#align(%lld) is defined to be less than #min_field_align(%lld)",
cast(long long)struct_type->Struct.custom_align,
cast(long long)struct_type->Struct.custom_min_field_align);
@@ -1154,8 +1155,7 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type,
}
}
-
-
+ #if 0 // Reconsider at a later date
if (bit_sizes.count > 0 && is_type_integer(backing_type)) {
bool all_booleans = is_type_boolean(fields[0]->type);
bool all_ones = bit_sizes[0] == 1;
@@ -1181,7 +1181,7 @@ gb_internal void check_bit_field_type(CheckerContext *ctx, Type *bit_field_type,
}
}
}
-
+ #endif
bit_field_type->BitField.fields = slice_from_array(fields);
bit_field_type->BitField.bit_sizes = slice_from_array(bit_sizes);
@@ -2081,7 +2081,9 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
if (type != t_invalid && !check_is_assignable_to(ctx, &op, type, allow_array_programming)) {
bool ok = true;
if (p->flags&FieldFlag_any_int) {
- if ((!is_type_integer(op.type) && !is_type_enum(op.type)) || (!is_type_integer(type) && !is_type_enum(type))) {
+ if (op.type == nullptr) {
+ ok = false;
+ } else if ((!is_type_integer(op.type) && !is_type_enum(op.type)) || (!is_type_integer(type) && !is_type_enum(type))) {
ok = false;
} else if (!check_is_castable_to(ctx, &op, type)) {
ok = false;
@@ -2366,8 +2368,7 @@ gb_internal Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_res
}
gb_internal void check_procedure_param_polymorphic_type(CheckerContext *ctx, Type *type, Ast *type_expr) {
- GB_ASSERT_NOT_NULL(type_expr);
- if (type == nullptr || ctx->in_polymorphic_specialization) { return; }
+ if (type == nullptr || type_expr == nullptr || ctx->in_polymorphic_specialization) { return; }
if (!is_type_polymorphic_record_unspecialized(type)) { return; }
bool invalid_polymorpic_type_use = false;
@@ -2441,8 +2442,12 @@ gb_internal bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc
bool success = true;
isize specialization_count = 0;
Type *params = check_get_params(c, c->scope, pt->params, &variadic, &variadic_index, &success, &specialization_count, operands);
- Type *results = check_get_results(c, c->scope, pt->results);
+ bool no_poly_return = c->disallow_polymorphic_return_types;
+ c->disallow_polymorphic_return_types = c->scope == c->polymorphic_scope;
+ // NOTE(zen3ger): if the parapoly scope is the current proc's scope, then the return types shall not declare new poly vars
+ Type *results = check_get_results(c, c->scope, pt->results);
+ c->disallow_polymorphic_return_types = no_poly_return;
isize param_count = 0;
isize result_count = 0;
@@ -2769,6 +2774,21 @@ gb_internal void add_map_key_type_dependencies(CheckerContext *ctx, Type *key) {
return;
}
+ if (key->kind == Type_Basic) {
+ if (key->Basic.flags & BasicFlag_Quaternion) {
+ add_package_dependency(ctx, "runtime", "default_hasher_f64");
+ add_package_dependency(ctx, "runtime", "default_hasher_quaternion256");
+ return;
+ } else if (key->Basic.flags & BasicFlag_Complex) {
+ add_package_dependency(ctx, "runtime", "default_hasher_f64");
+ add_package_dependency(ctx, "runtime", "default_hasher_complex128");
+ return;
+ } else if (key->Basic.flags & BasicFlag_Float) {
+ add_package_dependency(ctx, "runtime", "default_hasher_f64");
+ return;
+ }
+ }
+
if (key->kind == Type_Struct) {
add_package_dependency(ctx, "runtime", "default_hasher");
for_array(i, key->Struct.fields) {
@@ -2855,15 +2875,23 @@ gb_internal void check_matrix_type(CheckerContext *ctx, Type **type, Ast *node)
}
if (generic_row == nullptr && row_count < MATRIX_ELEMENT_COUNT_MIN) {
- gbString s = expr_to_string(row.expr);
- error(row.expr, "Invalid matrix row count, expected %d+ rows, got %s", MATRIX_ELEMENT_COUNT_MIN, s);
- gb_string_free(s);
+ if (row.expr == nullptr) {
+ error(node, "Invalid matrix row count, got nothing");
+ } else {
+ gbString s = expr_to_string(row.expr);
+ error(row.expr, "Invalid matrix row count, expected %d+ rows, got %s", MATRIX_ELEMENT_COUNT_MIN, s);
+ gb_string_free(s);
+ }
}
if (generic_column == nullptr && column_count < MATRIX_ELEMENT_COUNT_MIN) {
- gbString s = expr_to_string(column.expr);
- error(column.expr, "Invalid matrix column count, expected %d+ rows, got %s", MATRIX_ELEMENT_COUNT_MIN, s);
- gb_string_free(s);
+ if (column.expr == nullptr) {
+ error(node, "Invalid matrix column count, got nothing");
+ } else {
+ gbString s = expr_to_string(column.expr);
+ error(column.expr, "Invalid matrix column count, expected %d+ rows, got %s", MATRIX_ELEMENT_COUNT_MIN, s);
+ gb_string_free(s);
+ }
}
if ((generic_row == nullptr && generic_column == nullptr) && row_count*column_count > MATRIX_ELEMENT_COUNT_MAX) {
@@ -3384,6 +3412,9 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T
}
Type *t = alloc_type_generic(ctx->scope, 0, token.string, specific);
if (ctx->allow_polymorphic_types) {
+ if (ctx->disallow_polymorphic_return_types) {
+ error(ident, "Undeclared polymorphic parameter '%.*s' in return type", LIT(token.string));
+ }
Scope *ps = ctx->polymorphic_scope;
Scope *s = ctx->scope;
Scope *entity_scope = s;
@@ -3487,6 +3518,17 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T
elem = o.type;
}
+ if (!ctx->in_polymorphic_specialization && ctx->disallow_polymorphic_return_types) {
+ Type *t = base_type(elem);
+ if (t != nullptr &&
+ unparen_expr(pt->type)->kind == Ast_Ident &&
+ is_type_polymorphic_record_unspecialized(t)) {
+ gbString err_str = expr_to_string(e);
+ error(e, "Invalid use of a non-specialized polymorphic type '%s'", err_str);
+ gb_string_free(err_str);
+ }
+ }
+
if (pt->tag != nullptr) {
GB_ASSERT(pt->tag->kind == Ast_BasicDirective);
diff --git a/src/checker.cpp b/src/checker.cpp
index b7cf343f8..5a5ec9706 100644
--- a/src/checker.cpp
+++ b/src/checker.cpp
@@ -3,7 +3,10 @@
#include "entity.cpp"
#include "types.cpp"
-String get_final_microarchitecture();
+
+gb_internal u64 type_hash_canonical_type(Type *type);
+
+gb_internal String get_final_microarchitecture();
gb_internal void check_expr(CheckerContext *c, Operand *operand, Ast *expression);
gb_internal void check_expr_or_type(CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint=nullptr);
@@ -170,7 +173,7 @@ gb_internal void init_decl_info(DeclInfo *d, Scope *scope, DeclInfo *parent) {
d->parent = parent;
d->scope = scope;
ptr_set_init(&d->deps, 0);
- ptr_set_init(&d->type_info_deps, 0);
+ type_set_init(&d->type_info_deps, 0);
d->labels.allocator = heap_allocator();
d->variadic_reuses.allocator = heap_allocator();
d->variadic_reuse_max_bytes = 0;
@@ -355,6 +358,10 @@ gb_internal void check_open_scope(CheckerContext *c, Ast *node) {
scope->flags |= ScopeFlag_Type;
break;
}
+ if (c->decl && c->decl->proc_lit) {
+ // Number the scopes within a procedure body depth-first
+ scope->index = c->decl->scope_index++;
+ }
c->scope = scope;
c->state_flags |= StateFlag_bounds_check;
}
@@ -542,6 +549,23 @@ gb_internal u64 check_vet_flags(Ast *node) {
return ast_file_vet_flags(file);
}
+gb_internal u64 check_feature_flags(CheckerContext *c, Ast *node) {
+ AstFile *file = c->file;
+ if (file == nullptr &&
+ c->curr_proc_decl &&
+ c->curr_proc_decl->proc_lit) {
+ file = c->curr_proc_decl->proc_lit->file();
+ }
+ if (file == nullptr) {
+ file = node->file();
+ }
+ if (file != nullptr && file->feature_flags_set) {
+ return file->feature_flags;
+ }
+ return 0;
+}
+
+
enum VettedEntityKind {
VettedEntity_Invalid,
@@ -727,14 +751,20 @@ gb_internal void check_scope_usage_internal(Checker *c, Scope *scope, u64 vet_fl
array_add(&vetted_entities, ve_unused);
} else if (is_shadowed) {
array_add(&vetted_entities, ve_shadowed);
- } else if (e->kind == Entity_Variable && (e->flags & (EntityFlag_Param|EntityFlag_Using|EntityFlag_Static)) == 0 && !e->Variable.is_global) {
+ } else if (e->kind == Entity_Variable && (e->flags & (EntityFlag_Param|EntityFlag_Using|EntityFlag_Static|EntityFlag_Field)) == 0 && !e->Variable.is_global) {
i64 sz = type_size_of(e->type);
// TODO(bill): When is a good size warn?
// Is >256 KiB good enough?
if (sz > 1ll<<18) {
- gbString type_str = type_to_string(e->type);
- warning(e->token, "Declaration of '%.*s' may cause a stack overflow due to its type '%s' having a size of %lld bytes", LIT(e->token.string), type_str, cast(long long)sz);
- gb_string_free(type_str);
+ bool is_ref = false;
+ if((e->flags & EntityFlag_ForValue) != 0) {
+ is_ref = type_deref(e->Variable.for_loop_parent_type) != NULL;
+ }
+ if(!is_ref) {
+ gbString type_str = type_to_string(e->type);
+ warning(e->token, "Declaration of '%.*s' may cause a stack overflow due to its type '%s' having a size of %lld bytes", LIT(e->token.string), type_str, cast(long long)sz);
+ gb_string_free(type_str);
+ }
}
}
}
@@ -802,11 +832,17 @@ gb_internal void add_dependency(CheckerInfo *info, DeclInfo *d, Entity *e) {
rw_mutex_unlock(&d->deps_mutex);
}
gb_internal void add_type_info_dependency(CheckerInfo *info, DeclInfo *d, Type *type) {
- if (d == nullptr) {
+ if (d == nullptr || type == nullptr) {
return;
}
+ if (type->kind == Type_Named) {
+ Entity *e = type->Named.type_name;
+ if (e->TypeName.is_type_alias) {
+ type = type->Named.base;
+ }
+ }
rw_mutex_lock(&d->type_info_deps_mutex);
- ptr_set_add(&d->type_info_deps, type);
+ type_set_add(&d->type_info_deps, type);
rw_mutex_unlock(&d->type_info_deps_mutex);
}
@@ -1113,6 +1149,7 @@ gb_internal void init_universal(void) {
GlobalEnumValue values[Subtarget_COUNT] = {
{"Default", Subtarget_Default},
{"iOS", Subtarget_iOS},
+ {"Android", Subtarget_Android},
};
auto fields = add_global_enum_type(str_lit("Odin_Platform_Subtarget_Type"), values, gb_count_of(values));
@@ -1164,7 +1201,6 @@ gb_internal void init_universal(void) {
add_global_bool_constant("ODIN_NO_BOUNDS_CHECK", build_context.no_bounds_check);
add_global_bool_constant("ODIN_NO_TYPE_ASSERT", build_context.no_type_assert);
add_global_bool_constant("ODIN_DEFAULT_TO_PANIC_ALLOCATOR", bc->ODIN_DEFAULT_TO_PANIC_ALLOCATOR);
- add_global_bool_constant("ODIN_NO_DYNAMIC_LITERALS", bc->no_dynamic_literals);
add_global_bool_constant("ODIN_NO_CRT", bc->no_crt);
add_global_bool_constant("ODIN_USE_SEPARATE_MODULES", bc->use_separate_modules);
add_global_bool_constant("ODIN_TEST", bc->command_kind == Command_test);
@@ -1336,8 +1372,11 @@ gb_internal void init_checker_info(CheckerInfo *i) {
string_map_init(&i->foreigns);
// map_init(&i->gen_procs);
map_init(&i->gen_types);
- array_init(&i->type_info_types, a);
- map_init(&i->type_info_map);
+
+ type_set_init(&i->min_dep_type_info_set);
+ map_init(&i->min_dep_type_info_index_map);
+
+ // map_init(&i->type_info_map);
string_map_init(&i->files);
string_map_init(&i->packages);
array_init(&i->variable_init_order, a);
@@ -1356,6 +1395,7 @@ gb_internal void init_checker_info(CheckerInfo *i) {
mpsc_init(&i->required_global_variable_queue, a); // 1<<10);
mpsc_init(&i->required_foreign_imports_through_force_queue, a); // 1<<10);
mpsc_init(&i->foreign_imports_to_check_fullpaths, a); // 1<<10);
+ mpsc_init(&i->foreign_decls_to_check, a); // 1<<10);
mpsc_init(&i->intrinsics_entry_point_usage, a); // 1<<10); // just waste some memory here, even if it probably never used
string_map_init(&i->load_directory_cache);
@@ -1369,8 +1409,10 @@ gb_internal void destroy_checker_info(CheckerInfo *i) {
string_map_destroy(&i->foreigns);
// map_destroy(&i->gen_procs);
map_destroy(&i->gen_types);
- array_free(&i->type_info_types);
- map_destroy(&i->type_info_map);
+
+ type_set_destroy(&i->min_dep_type_info_set);
+ map_destroy(&i->min_dep_type_info_index_map);
+
string_map_destroy(&i->files);
string_map_destroy(&i->packages);
array_free(&i->variable_init_order);
@@ -1382,6 +1424,7 @@ gb_internal void destroy_checker_info(CheckerInfo *i) {
mpsc_destroy(&i->required_global_variable_queue);
mpsc_destroy(&i->required_foreign_imports_through_force_queue);
mpsc_destroy(&i->foreign_imports_to_check_fullpaths);
+ mpsc_destroy(&i->foreign_decls_to_check);
map_destroy(&i->objc_msgSend_types);
string_map_destroy(&i->load_file_cache);
@@ -1603,6 +1646,23 @@ gb_internal void check_remove_expr_info(CheckerContext *c, Ast *e) {
}
}
+gb_internal isize type_info_index(CheckerInfo *info, TypeInfoPair pair, bool error_on_failure) {
+ mutex_lock(&info->minimum_dependency_type_info_mutex);
+
+ isize entry_index = -1;
+ u64 hash = pair.hash;
+ isize *found_entry_index = map_get(&info->min_dep_type_info_index_map, hash);
+ if (found_entry_index) {
+ entry_index = *found_entry_index;
+ }
+ mutex_unlock(&info->minimum_dependency_type_info_mutex);
+
+ if (error_on_failure && entry_index < 0) {
+ compiler_error("Type_Info for '%s' could not be found", type_to_string(pair.type));
+ }
+ return entry_index;
+}
+
gb_internal isize type_info_index(CheckerInfo *info, Type *type, bool error_on_failure) {
type = default_type(type);
@@ -1610,34 +1670,12 @@ gb_internal isize type_info_index(CheckerInfo *info, Type *type, bool error_on_f
type = t_bool;
}
- mutex_lock(&info->type_info_mutex);
-
- isize entry_index = -1;
- isize *found_entry_index = map_get(&info->type_info_map, type);
- if (found_entry_index) {
- entry_index = *found_entry_index;
- }
- if (entry_index < 0) {
- // NOTE(bill): Do manual linear search
- for (auto const &e : info->type_info_map) {
- if (are_types_identical_unique_tuples(e.key, type)) {
- entry_index = e.value;
- // NOTE(bill): Add it to the search map
- map_set(&info->type_info_map, type, entry_index);
- break;
- }
- }
- }
-
- mutex_unlock(&info->type_info_mutex);
-
- if (error_on_failure && entry_index < 0) {
- compiler_error("Type_Info for '%s' could not be found", type_to_string(type));
- }
- return entry_index;
+ u64 hash = type_hash_canonical_type(type);
+ return type_info_index(info, {type, hash}, error_on_failure);
}
+
gb_internal void add_untyped(CheckerContext *c, Ast *expr, AddressingMode mode, Type *type, ExactValue const &value) {
if (expr == nullptr) {
return;
@@ -1989,8 +2027,12 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
}
add_type_info_dependency(c->info, c->decl, t);
-
+#if 0
MUTEX_GUARD_BLOCK(&c->info->type_info_mutex) {
+ if (type_set_update(&c->info->type_info_set, t)) {
+ // return;
+ }
+
auto found = map_get(&c->info->type_info_map, t);
if (found != nullptr) {
// Types have already been added
@@ -2013,7 +2055,8 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
// Unique entry
// NOTE(bill): map entries grow linearly and in order
ti_index = c->info->type_info_types.count;
- array_add(&c->info->type_info_types, t);
+ TypeInfoPair tt = {t, type_hash_canonical_type(t)};
+ array_add(&c->info->type_info_types, tt);
}
map_set(&c->checker->info.type_info_map, t, ti_index);
@@ -2208,6 +2251,7 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
GB_PANIC("Unhandled type: %*.s %d", LIT(type_strings[bt->kind]), bt->kind);
break;
}
+#endif
}
@@ -2265,19 +2309,7 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) {
return;
}
- auto *set = &c->info.minimum_dependency_type_info_set;
-
- isize ti_index = type_info_index(&c->info, t, false);
- if (ti_index < 0) {
- add_type_info_type(&c->builtin_ctx, t); // Missing the type information
- ti_index = type_info_index(&c->info, t, false);
- }
- GB_ASSERT(ti_index >= 0);
- // IMPORTANT NOTE(bill): this must be copied as `map_set` takes a const ref
- // and effectively assigns the `+1` of the value
- isize const count = set->count;
- if (map_set_if_not_previously_exists(set, ti_index+1, count)) {
- // Type already exists;
+ if (type_set_update(&c->info.min_dep_type_info_set, t)) {
return;
}
@@ -2477,8 +2509,8 @@ gb_internal void add_dependency_to_set(Checker *c, Entity *entity) {
if (decl == nullptr) {
return;
}
- for (Type *t : decl->type_info_deps) {
- add_min_dep_type_info(c, t);
+ for (TypeInfoPair const tt : decl->type_info_deps) {
+ add_min_dep_type_info(c, tt.type);
}
for (Entity *e : decl->deps) {
@@ -2614,6 +2646,10 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st
is_init = false;
}
+ if (is_blank_ident(e->token)) {
+ error(e->token, "An @(init) procedure must not use a blank identifier as its name");
+ }
+
if (is_init) {
add_dependency_to_set(c, e);
array_add(&c->info.init_procedures, e);
@@ -2636,6 +2672,10 @@ gb_internal void generate_minimum_dependency_set_internal(Checker *c, Entity *st
is_fini = false;
}
+ if (is_blank_ident(e->token)) {
+ error(e->token, "An @(fini) procedure must not use a blank identifier as its name");
+ }
+
if (is_fini) {
add_dependency_to_set(c, e);
array_add(&c->info.fini_procedures, e);
@@ -2678,7 +2718,6 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) {
isize min_dep_set_cap = next_pow2_isize(entity_count*4); // empirically determined factor
ptr_set_init(&c->info.minimum_dependency_set, min_dep_set_cap);
- map_init(&c->info.minimum_dependency_type_info_set);
#define FORCE_ADD_RUNTIME_ENTITIES(condition, ...) do { \
if (condition) { \
@@ -3870,6 +3909,7 @@ gb_internal DECL_ATTRIBUTE_PROC(type_decl_attribute) {
#include "check_expr.cpp"
#include "check_builtin.cpp"
#include "check_type.cpp"
+#include "name_canonicalization.cpp"
#include "check_decl.cpp"
#include "check_stmt.cpp"
@@ -4992,6 +5032,9 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_import_decl_attribute) {
error(elem, "Expected a string value for '%.*s'", LIT(name));
}
return true;
+ } else if (name == "export") {
+ ac->is_export = true;
+ return true;
} else if (name == "force" || name == "require") {
if (value != nullptr) {
error(elem, "Expected no parameter for '%.*s'", LIT(name));
@@ -5016,6 +5059,12 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_import_decl_attribute) {
ac->extra_linker_flags = ev.value_string;
}
return true;
+ } else if (name == "ignore_duplicates") {
+ if (value != nullptr) {
+ error(elem, "Expected no parameter for '%.*s'", LIT(name));
+ }
+ ac->ignore_duplicates = true;
+ return true;
}
return false;
}
@@ -5094,6 +5143,38 @@ gb_internal void check_foreign_import_fullpaths(Checker *c) {
e->LibraryName.paths = fl->fullpaths;
}
+
+ for (Entity *e = nullptr; mpsc_dequeue(&c->info.foreign_decls_to_check, &e); /**/) {
+ GB_ASSERT(e != nullptr);
+ if (e->kind != Entity_Procedure) {
+ continue;
+ }
+ if (!is_arch_wasm()) {
+ continue;
+ }
+ Entity *foreign_library = e->Procedure.foreign_library;
+ GB_ASSERT(foreign_library != nullptr);
+
+ String name = e->Procedure.link_name;
+
+ String module_name = str_lit("env");
+ GB_ASSERT (foreign_library->kind == Entity_LibraryName);
+ if (foreign_library->LibraryName.paths.count != 1) {
+ error(foreign_library->token, "'foreign import' for '%.*s' architecture may only have one path, got %td",
+ LIT(target_arch_names[build_context.metrics.arch]), foreign_library->LibraryName.paths.count);
+ }
+
+ if (foreign_library->LibraryName.paths.count >= 1) {
+ module_name = foreign_library->LibraryName.paths[0];
+ }
+
+ if (!string_ends_with(module_name, str_lit(".o"))) {
+ name = concatenate3_strings(permanent_allocator(), module_name, WASM_MODULE_NAME_SEPARATOR, name);
+ }
+ e->Procedure.link_name = name;
+
+ check_foreign_procedure(&ctx, e, e->decl_info);
+ }
}
gb_internal void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) {
@@ -5119,14 +5200,21 @@ gb_internal void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) {
GB_ASSERT(fl->library_name.pos.line != 0);
fl->library_name.string = library_name;
+ AttributeContext ac = {};
+ check_decl_attributes(ctx, fl->attributes, foreign_import_decl_attribute, &ac);
+
+ Scope *scope = parent_scope;
+ if (ac.is_export) {
+ scope = parent_scope->parent;
+ }
+
Entity *e = alloc_entity_library_name(parent_scope, fl->library_name, t_invalid,
fl->fullpaths, library_name);
e->LibraryName.decl = decl;
add_entity_flags_from_file(ctx, e, parent_scope);
- add_entity(ctx, parent_scope, nullptr, e);
+ add_entity(ctx, scope, nullptr, e);
+
- AttributeContext ac = {};
- check_decl_attributes(ctx, fl->attributes, foreign_import_decl_attribute, &ac);
if (ac.require_declaration) {
mpsc_enqueue(&ctx->info->required_foreign_imports_through_force_queue, e);
add_entity_use(ctx, nullptr, e);
@@ -5134,6 +5222,9 @@ gb_internal void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) {
if (ac.foreign_import_priority_index != 0) {
e->LibraryName.priority_index = ac.foreign_import_priority_index;
}
+ if (ac.ignore_duplicates) {
+ e->LibraryName.ignore_duplicates = true;
+ }
String extra_linker_flags = string_trim_whitespace(ac.extra_linker_flags);
if (extra_linker_flags.len != 0) {
e->LibraryName.extra_linker_flags = extra_linker_flags;
@@ -6244,6 +6335,10 @@ gb_internal void check_deferred_procedures(Checker *c) {
continue;
}
+ if (dst_params == nullptr) {
+ error(src->token, "Deferred procedure must have parameters for %s", attribute);
+ continue;
+ }
GB_ASSERT(dst_params->kind == Type_Tuple);
Type *tsrc = alloc_type_tuple();
@@ -6645,6 +6740,58 @@ gb_internal void check_parsed_files(Checker *c) {
add_type_and_value(&c->builtin_ctx, u.expr, u.info->mode, u.info->type, u.info->value);
}
+ TIME_SECTION("initialize and check for collisions in type info array");
+ {
+ Array type_info_types; // sorted after filled
+ array_init(&type_info_types, heap_allocator());
+ defer (array_free(&type_info_types));
+
+ for (auto const &tt : c->info.min_dep_type_info_set) {
+ array_add(&type_info_types, tt);
+ }
+ array_sort(type_info_types, type_info_pair_cmp);
+
+ array_init(&c->info.type_info_types_hash_map, heap_allocator(), type_info_types.count*2 + 1);
+ map_reserve(&c->info.min_dep_type_info_index_map, type_info_types.count);
+
+ isize hash_map_len = c->info.type_info_types_hash_map.count;
+ for (auto const &tt : type_info_types) {
+ isize index = tt.hash % hash_map_len;
+ // NOTE(bill): no need for a sanity check since there
+ // will always be enough space for the entries
+ for (;;) {
+ if (index == 0 || c->info.type_info_types_hash_map[index].hash != 0) {
+ index = (index+1) % hash_map_len;
+ continue;
+ }
+ break;
+ }
+ c->info.type_info_types_hash_map[index] = tt;
+
+ bool exists = map_set_if_not_previously_exists(&c->info.min_dep_type_info_index_map, tt.hash, index);
+ if (exists) {
+ for (auto const &entry : c->info.min_dep_type_info_index_map) {
+ if (entry.key != tt.hash) {
+ continue;
+ }
+ auto const &other = type_info_types[entry.value];
+ if (are_types_identical_unique_tuples(tt.type, other.type)) {
+ continue;
+ }
+ gbString t = temp_canonical_string(tt.type);
+ gbString o = temp_canonical_string(other.type);
+ GB_PANIC("%s (%s) %llu vs %s (%s) %llu",
+ type_to_string(tt.type, false), t, cast(unsigned long long)tt.hash,
+ type_to_string(other.type, false), o, cast(unsigned long long)other.hash);
+ }
+ }
+ }
+
+
+ GB_ASSERT(c->info.min_dep_type_info_index_map.count <= type_info_types.count);
+ }
+
+
TIME_SECTION("sort init and fini procedures");
check_sort_init_and_fini_procedures(c);
diff --git a/src/checker.hpp b/src/checker.hpp
index 438156f18..d3b2d7d89 100644
--- a/src/checker.hpp
+++ b/src/checker.hpp
@@ -140,6 +140,7 @@ struct AttributeContext {
bool instrumentation_enter : 1;
bool instrumentation_exit : 1;
bool rodata : 1;
+ bool ignore_duplicates : 1;
u32 optimization_mode; // ProcedureOptimizationMode
i64 foreign_import_priority_index;
String extra_linker_flags;
@@ -166,6 +167,7 @@ typedef DECL_ATTRIBUTE_PROC(DeclAttributeProc);
gb_internal void check_decl_attributes(CheckerContext *c, Array const &attributes, DeclAttributeProc *proc, AttributeContext *ac);
+#include "name_canonicalization.hpp"
enum ProcCheckedState : u8 {
ProcCheckedState_Unchecked,
@@ -220,13 +222,15 @@ struct DeclInfo {
RwMutex deps_mutex;
PtrSet deps;
- RwMutex type_info_deps_mutex;
- PtrSet type_info_deps;
+ RwMutex type_info_deps_mutex;
+ TypeSet type_info_deps;
BlockingMutex type_and_value_mutex;
Array labels;
+ i32 scope_index;
+
Array variadic_reuses;
i64 variadic_reuse_max_bytes;
i64 variadic_reuse_max_align;
@@ -271,10 +275,14 @@ struct Scope {
std::atomic next;
std::atomic head_child;
+ i32 index; // within a procedure
+
RwMutex mutex;
StringMap elements;
PtrSet imported;
+ DeclInfo *decl_info;
+
i32 flags; // ScopeFlag
union {
AstPackage *pkg;
@@ -420,8 +428,10 @@ struct CheckerInfo {
Scope * init_scope;
Entity * entry_point;
PtrSet minimum_dependency_set;
- PtrMap*type info index*/isize, /*min dep index*/isize> minimum_dependency_type_info_set;
-
+ BlockingMutex minimum_dependency_type_info_mutex;
+ PtrMap*type info hash*/u64, /*min dep index*/isize> min_dep_type_info_index_map;
+ TypeSet min_dep_type_info_set;
+ Array type_info_types_hash_map; // 2 * type_info_types.count
Array testing_procedures;
@@ -449,9 +459,10 @@ struct CheckerInfo {
BlockingMutex gen_types_mutex;
PtrMap gen_types;
- BlockingMutex type_info_mutex; // NOT recursive
- Array type_info_types;
- PtrMap type_info_map;
+ // BlockingMutex type_info_mutex; // NOT recursive
+ // Array type_info_types;
+ // PtrMap type_info_map;
+ // TypeSet type_info_set;
BlockingMutex foreign_mutex; // NOT recursive
StringMap foreigns;
@@ -461,6 +472,7 @@ struct CheckerInfo {
MPSCQueue required_global_variable_queue;
MPSCQueue required_foreign_imports_through_force_queue;
MPSCQueue foreign_imports_to_check_fullpaths;
+ MPSCQueue foreign_decls_to_check;
MPSCQueue intrinsics_entry_point_usage;
@@ -521,6 +533,7 @@ struct CheckerContext {
bool in_enum_type;
bool collect_delayed_decls;
bool allow_polymorphic_types;
+ bool disallow_polymorphic_return_types; // NOTE(zen3ger): no poly type decl in return types
bool no_polymorphic_errors;
bool hide_polymorphic_errors;
bool in_polymorphic_specialization;
@@ -568,6 +581,7 @@ gb_internal DeclInfo * decl_info_of_entity (Entity * e);
gb_internal AstFile * ast_file_of_filename (CheckerInfo *i, String filename);
// IMPORTANT: Only to use once checking is done
gb_internal isize type_info_index (CheckerInfo *i, Type *type, bool error_on_failure);
+gb_internal isize type_info_index (CheckerInfo *info, TypeInfoPair pair, bool error_on_failure);
// Will return nullptr if not found
gb_internal Entity *entity_of_node(Ast *expr);
diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp
index 2dfd570e4..40dde8240 100644
--- a/src/checker_builtin_procs.hpp
+++ b/src/checker_builtin_procs.hpp
@@ -181,6 +181,9 @@ BuiltinProc__simd_begin,
BuiltinProc_simd_reduce_any,
BuiltinProc_simd_reduce_all,
+ BuiltinProc_simd_extract_lsbs,
+ BuiltinProc_simd_extract_msbs,
+
BuiltinProc_simd_shuffle,
BuiltinProc_simd_select,
@@ -523,6 +526,9 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("simd_reduce_any"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_reduce_all"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_extract_lsbs"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("simd_extract_msbs"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
{STR_LIT("simd_shuffle"), 2, true, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("simd_select"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
diff --git a/src/common.cpp b/src/common.cpp
index 0ef39bd10..ad1e5a851 100644
--- a/src/common.cpp
+++ b/src/common.cpp
@@ -134,9 +134,9 @@ gb_internal u32 fnv32a(void const *data, isize len) {
return h;
}
-gb_internal u64 fnv64a(void const *data, isize len) {
+gb_internal u64 fnv64a(void const *data, isize len, u64 seed=0xcbf29ce484222325ull) {
u8 const *bytes = cast(u8 const *)data;
- u64 h = 0xcbf29ce484222325ull;
+ u64 h = seed;
for (; len >= 8; len -= 8, bytes += 8) {
h = (h ^ bytes[0]) * 0x100000001b3ull;
diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp
index 341b3fa6b..a92ffc7ad 100644
--- a/src/docs_writer.cpp
+++ b/src/docs_writer.cpp
@@ -16,6 +16,8 @@ gb_global char const* OdinDocWriterState_strings[] {
"writing ",
};
+gb_global std::atomic g_in_doc_writer;
+
struct OdinDocWriter {
CheckerInfo *info;
OdinDocWriterState state;
@@ -26,11 +28,10 @@ struct OdinDocWriter {
StringMap string_cache;
- OrderedInsertPtrMap file_cache;
- OrderedInsertPtrMap pkg_cache;
- OrderedInsertPtrMap entity_cache;
- OrderedInsertPtrMap type_cache;
- OrderedInsertPtrMap stable_type_cache;
+ OrderedInsertPtrMap file_cache;
+ OrderedInsertPtrMap pkg_cache;
+ OrderedInsertPtrMap entity_cache;
+ OrderedInsertPtrMap type_cache;
OdinDocWriterItemTracker files;
OdinDocWriterItemTracker pkgs;
@@ -61,7 +62,6 @@ gb_internal void odin_doc_writer_prepare(OdinDocWriter *w) {
map_init(&w->pkg_cache, 1<<10);
map_init(&w->entity_cache, 1<<18);
map_init(&w->type_cache, 1<<18);
- map_init(&w->stable_type_cache, 1<<18);
odin_doc_writer_item_tracker_init(&w->files, 1);
odin_doc_writer_item_tracker_init(&w->pkgs, 1);
@@ -81,7 +81,6 @@ gb_internal void odin_doc_writer_destroy(OdinDocWriter *w) {
map_destroy(&w->pkg_cache);
map_destroy(&w->entity_cache);
map_destroy(&w->type_cache);
- map_destroy(&w->stable_type_cache);
}
@@ -492,55 +491,18 @@ gb_internal OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
}
}
- // Type **mapped_type = map_get(&w->stable_type_cache, type); // may map to itself
- // if (mapped_type && *mapped_type) {
- // type = *mapped_type;
- // }
-
- OdinDocTypeIndex *found = map_get(&w->type_cache, type);
+ u64 type_hash = type_hash_canonical_type(type);
+ OdinDocTypeIndex *found = map_get(&w->type_cache, type_hash);
if (found) {
return *found;
}
- for (auto const &entry : w->type_cache) {
- // NOTE(bill): THIS IS SLOW
- Type *x = type;
- Type *y = entry.key;
-
- if (x == y) {
- goto do_set;
- }
-
- if (!x | !y) {
- continue;
- }
- if (y->kind == Type_Named) {
- Entity *e = y->Named.type_name;
- if (e->TypeName.is_type_alias) {
- y = y->Named.base;
- }
- }
- if (x->kind != y->kind) {
- continue;
- }
-
- if (!are_types_identical_internal(x, y, true)) {
- continue;
- }
-
- do_set:
- OdinDocTypeIndex index = entry.value;
- map_set(&w->type_cache, type, index);
- map_set(&w->stable_type_cache, type, entry.key);
- return index;
- }
OdinDocType *dst = nullptr;
OdinDocType doc_type = {};
OdinDocTypeIndex type_index = 0;
type_index = odin_doc_write_item(w, &w->types, &doc_type, &dst);
- map_set(&w->type_cache, type, type_index);
- map_set(&w->stable_type_cache, type, type);
+ map_set(&w->type_cache, type_hash, type_index);
switch (type->kind) {
case Type_Basic:
@@ -1177,6 +1139,8 @@ gb_internal void odin_doc_write_to_file(OdinDocWriter *w, char const *filename)
}
gb_internal void odin_doc_write(CheckerInfo *info, char const *filename) {
+ g_in_doc_writer.store(true);
+
OdinDocWriter w_ = {};
OdinDocWriter *w = &w_;
defer (odin_doc_writer_destroy(w));
@@ -1192,4 +1156,11 @@ gb_internal void odin_doc_write(CheckerInfo *info, char const *filename) {
odin_doc_writer_end_writing(w);
odin_doc_write_to_file(w, filename);
+
+ g_in_doc_writer.store(false);
}
+
+
+gb_internal bool is_in_doc_writer(void) {
+ return g_in_doc_writer.load();
+}
\ No newline at end of file
diff --git a/src/entity.cpp b/src/entity.cpp
index 0c4a20df4..b2148aa7b 100644
--- a/src/entity.cpp
+++ b/src/entity.cpp
@@ -256,6 +256,8 @@ struct Entity {
bool entry_point_only : 1;
bool has_instrumentation : 1;
bool is_memcpy_like : 1;
+ bool uses_branch_location : 1;
+ bool is_anonymous : 1;
} Procedure;
struct {
Array entities;
@@ -273,6 +275,7 @@ struct Entity {
Slice paths;
String name;
i64 priority_index;
+ bool ignore_duplicates;
String extra_linker_flags;
} LibraryName;
i32 Nil;
diff --git a/src/exact_value.cpp b/src/exact_value.cpp
index 5d6016ecc..37751c8f1 100644
--- a/src/exact_value.cpp
+++ b/src/exact_value.cpp
@@ -370,7 +370,11 @@ gb_internal ExactValue exact_value_from_basic_literal(TokenKind kind, String con
}
case Token_Rune: {
Rune r = GB_RUNE_INVALID;
- utf8_decode(string.text, string.len, &r);
+ if (string.len == 1) {
+ r = cast(Rune)string.text[0];
+ } else {
+ utf8_decode(string.text, string.len, &r);
+ }
return exact_value_i64(r);
}
}
@@ -950,6 +954,10 @@ gb_internal bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y)
case ExactValue_Float: {
f64 a = x.value_float;
f64 b = y.value_float;
+ if (isnan(a) || isnan(b)) {
+ return op == Token_NotEq;
+ }
+
switch (op) {
case Token_CmpEq: return cmp_f64(a, b) == 0;
case Token_NotEq: return cmp_f64(a, b) != 0;
diff --git a/src/gb/gb.h b/src/gb/gb.h
index f74026c7d..a1b659637 100644
--- a/src/gb/gb.h
+++ b/src/gb/gb.h
@@ -5837,15 +5837,33 @@ gb_inline isize gb_printf_err_va(char const *fmt, va_list va) {
}
gb_inline isize gb_fprintf_va(struct gbFile *f, char const *fmt, va_list va) {
- gb_local_persist char buf[4096];
- isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va);
- gb_file_write(f, buf, len-1); // NOTE(bill): prevent extra whitespace
+ char buf[4096];
+ va_list va_save;
+ va_copy(va_save, va);
+ isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va_save);
+ va_end(va_save);
+ char *new_buf = NULL;
+ isize n = gb_size_of(buf);
+ while (len < 0) {
+ va_copy(va_save, va);
+ defer (va_end(va_save));
+ n <<= 1;
+ gb_free(gb_heap_allocator(), new_buf);
+ new_buf = gb_alloc_array(gb_heap_allocator(), char, n);;
+ len = gb_snprintf_va(new_buf, n, fmt, va_save);
+ }
+ if (new_buf != NULL) {
+ gb_file_write(f, new_buf, len-1); // NOTE(bill): prevent extra whitespace
+ gb_free(gb_heap_allocator(), new_buf);
+ } else {
+ gb_file_write(f, buf, len-1); // NOTE(bill): prevent extra whitespace
+ }
return len;
}
gb_inline char *gb_bprintf_va(char const *fmt, va_list va) {
- gb_local_persist char buffer[4096];
+ gb_thread_local gb_local_persist char buffer[4096];
gb_snprintf_va(buffer, gb_size_of(buffer), fmt, va);
return buffer;
}
@@ -5901,7 +5919,7 @@ gb_internal isize gb__print_string(char *text, isize max_len, gbprivFmtInfo *inf
len = info->precision < len ? info->precision : len;
}
- res += gb_strlcpy(text, str, len);
+ res += gb_strlcpy(text, str, gb_min(len, remaining));
if (info->width > res) {
isize padding = info->width - len;
@@ -5919,7 +5937,7 @@ gb_internal isize gb__print_string(char *text, isize max_len, gbprivFmtInfo *inf
}
}
- res += gb_strlcpy(text, str, len);
+ res += gb_strlcpy(text, str, gb_min(len, remaining));
}
@@ -6055,15 +6073,16 @@ gb_internal isize gb__print_f64(char *text, isize max_len, gbprivFmtInfo *info,
gb_no_inline isize gb_snprintf_va(char *text, isize max_len, char const *fmt, va_list va) {
char const *text_begin = text;
- isize remaining = max_len, res;
+ isize remaining = max_len - 1, res;
- while (*fmt) {
+ while (*fmt && remaining > 0) {
gbprivFmtInfo info = {0};
isize len = 0;
info.precision = -1;
- while (*fmt && *fmt != '%' && remaining) {
+ while (remaining > 0 && *fmt && *fmt != '%') {
*text++ = *fmt++;
+ remaining--;
}
if (*fmt == '%') {
@@ -6229,7 +6248,7 @@ gb_no_inline isize gb_snprintf_va(char *text, isize max_len, char const *fmt, va
text += len;
if (len >= remaining) {
- remaining = gb_min(remaining, 1);
+ remaining = 0;
} else {
remaining -= len;
}
diff --git a/src/linker.cpp b/src/linker.cpp
index 261d6e7a4..ec165ef7d 100644
--- a/src/linker.cpp
+++ b/src/linker.cpp
@@ -7,19 +7,15 @@ struct LinkerData {
Array output_temp_paths;
String output_base;
String output_name;
-#if defined(GB_SYSTEM_OSX)
- b8 needs_system_library_linked;
-#endif
+ bool needs_system_library_linked;
};
gb_internal i32 system_exec_command_line_app(char const *name, char const *fmt, ...);
gb_internal bool system_exec_command_line_app_output(char const *command, gbString *output);
-#if defined(GB_SYSTEM_OSX)
gb_internal void linker_enable_system_library_linking(LinkerData *ld) {
- ld->needs_system_library_linked = 1;
+ ld->needs_system_library_linked = true;
}
-#endif
gb_internal void linker_data_init(LinkerData *ld, CheckerInfo *info, String const &init_fullpath) {
gbAllocator ha = heap_allocator();
@@ -28,9 +24,7 @@ gb_internal void linker_data_init(LinkerData *ld, CheckerInfo *info, String cons
array_init(&ld->foreign_libraries, ha, 0, 1024);
ptr_set_init(&ld->foreign_libraries_set, 1024);
-#if defined(GB_SYSTEM_OSX)
- ld->needs_system_library_linked = 0;
-#endif
+ ld->needs_system_library_linked = false;
if (build_context.out_filepath.len == 0) {
ld->output_name = remove_directory_from_path(init_fullpath);
@@ -136,6 +130,9 @@ gb_internal i32 linker_stage(LinkerData *gen) {
return result;
}
+ bool is_cross_linking = false;
+ bool is_android = false;
+
if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
#if defined(GB_SYSTEM_UNIX)
result = system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
@@ -146,23 +143,30 @@ gb_internal i32 linker_stage(LinkerData *gen) {
LIT(target_arch_names[build_context.metrics.arch])
);
#endif
- } else if (build_context.cross_compiling && build_context.different_os) {
- gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n",
- LIT(target_os_names[build_context.metrics.os]),
- LIT(target_arch_names[build_context.metrics.arch])
- );
- build_context.keep_object_files = true;
+ } else if (build_context.cross_compiling && (build_context.different_os || selected_subtarget != Subtarget_Default)) {
+ switch (selected_subtarget) {
+ case Subtarget_Android:
+ is_cross_linking = true;
+ is_android = true;
+ goto try_cross_linking;
+ default:
+ gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n",
+ LIT(target_os_names[build_context.metrics.os]),
+ LIT(target_arch_names[build_context.metrics.arch])
+ );
+ build_context.keep_object_files = true;
+ break;
+ }
} else {
+try_cross_linking:;
+
#if defined(GB_SYSTEM_WINDOWS)
- bool is_windows = true;
+ bool is_windows = build_context.metrics.os == TargetOs_windows;
#else
bool is_windows = false;
#endif
- #if defined(GB_SYSTEM_OSX)
- bool is_osx = true;
- #else
- bool is_osx = false;
- #endif
+
+ bool is_osx = build_context.metrics.os == TargetOs_darwin;
if (is_windows) {
@@ -414,23 +418,27 @@ gb_internal i32 linker_stage(LinkerData *gen) {
} else {
timings_start_section(timings, str_lit("ld-link"));
+ int const ODIN_ANDROID_API_LEVEL = build_context.ODIN_ANDROID_API_LEVEL;
+
+ String ODIN_ANDROID_NDK = build_context.ODIN_ANDROID_NDK;
+ String ODIN_ANDROID_NDK_TOOLCHAIN = build_context.ODIN_ANDROID_NDK_TOOLCHAIN;
+ String ODIN_ANDROID_NDK_TOOLCHAIN_LIB = build_context.ODIN_ANDROID_NDK_TOOLCHAIN_LIB;
+ String ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL = build_context.ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL;
+ String ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT = build_context.ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT;
+
// Link using `clang`, unless overridden by `ODIN_CLANG_PATH` environment variable.
const char* clang_path = gb_get_env("ODIN_CLANG_PATH", permanent_allocator());
if (clang_path == NULL) {
clang_path = "clang";
}
- // NOTE(vassvik): get cwd, for used for local shared libs linking, since those have to be relative to the exe
- char cwd[256];
- #if !defined(GB_SYSTEM_WINDOWS)
- getcwd(&cwd[0], 256);
- #endif
- //printf("%s\n", cwd);
-
// NOTE(vassvik): needs to add the root to the library search paths, so that the full filenames of the library
// files can be passed with -l:
- gbString lib_str = gb_string_make(heap_allocator(), "-L/");
+ gbString lib_str = gb_string_make(heap_allocator(), "");
defer (gb_string_free(lib_str));
+ #if !defined(GB_SYSTEM_WINDOWS)
+ lib_str = gb_string_appendc(lib_str, "-L/ ");
+ #endif
StringSet asm_files = {};
string_set_init(&asm_files, 64);
@@ -449,6 +457,26 @@ gb_internal i32 linker_stage(LinkerData *gen) {
if (extra_linker_flags.len != 0) {
lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(extra_linker_flags));
}
+
+ if (build_context.metrics.os == TargetOs_darwin) {
+ // Print frameworks first
+ for (String lib : e->LibraryName.paths) {
+ lib = string_trim_whitespace(lib);
+ if (lib.len == 0) {
+ continue;
+ }
+ if (string_ends_with(lib, str_lit(".framework"))) {
+ if (string_set_update(&min_libs_set, lib)) {
+ continue;
+ }
+
+ String lib_name = lib;
+ lib_name = remove_extension_from_path(lib_name);
+ lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name));
+ }
+ }
+ }
+
for (String lib : e->LibraryName.paths) {
lib = string_trim_whitespace(lib);
if (lib.len == 0) {
@@ -476,19 +504,20 @@ gb_internal i32 linker_stage(LinkerData *gen) {
}
String obj_format;
- #if defined(GB_ARCH_64_BIT)
- if (is_osx) {
- obj_format = str_lit("macho64");
+ if (build_context.metrics.ptr_size == 8) {
+ if (is_osx) {
+ obj_format = str_lit("macho64");
+ } else {
+ obj_format = str_lit("elf64");
+ }
} else {
- obj_format = str_lit("elf64");
+ GB_ASSERT(build_context.metrics.ptr_size == 4);
+ if (is_osx) {
+ obj_format = str_lit("macho32");
+ } else {
+ obj_format = str_lit("elf32");
+ }
}
- #elif defined(GB_ARCH_32_BIT)
- if (is_osx) {
- obj_format = str_lit("macho32");
- } else {
- obj_format = str_lit("elf32");
- }
- #endif // GB_ARCH_*_BIT
if (build_context.metrics.arch == TargetArch_riscv64) {
result = system_exec_command_line_app("clang",
@@ -536,7 +565,18 @@ gb_internal i32 linker_stage(LinkerData *gen) {
}
array_add(&gen->output_object_paths, obj_file);
} else {
- if (string_set_update(&min_libs_set, lib) && build_context.min_link_libs) {
+ bool short_circuit = false;
+ if (string_ends_with(lib, str_lit(".framework"))) {
+ short_circuit = true;
+ } else if (string_ends_with(lib, str_lit(".dylib"))) {
+ short_circuit = true;
+ } else if (string_ends_with(lib, str_lit(".so"))) {
+ short_circuit = true;
+ } else if (e->LibraryName.ignore_duplicates) {
+ short_circuit = true;
+ }
+
+ if (string_set_update(&min_libs_set, lib) && (build_context.min_link_libs || short_circuit)) {
continue;
}
@@ -563,7 +603,7 @@ gb_internal i32 linker_stage(LinkerData *gen) {
// object
// dynamic lib
// static libs, absolute full path relative to the file in which the lib was imported from
- lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib));
+ lib_str = gb_string_append_fmt(lib_str, " \"%.*s\" ", LIT(lib));
} else {
// dynamic or static system lib, just link regularly searching system library paths
lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
@@ -587,6 +627,84 @@ gb_internal i32 linker_stage(LinkerData *gen) {
gbString object_files = gb_string_make(heap_allocator(), "");
defer (gb_string_free(object_files));
+
+
+ if (is_android) { // NOTE(bill): glue code needed for Android
+ TIME_SECTION("Android Native App Glue Compile");
+
+ String android_glue_object = {};
+ String android_glue_static_lib = {};
+
+ char hash_buf[64] = {};
+ gb_snprintf(hash_buf, gb_size_of(hash_buf), "%p", &hash_buf);
+ String hash = make_string_c(hash_buf);
+
+ String temp_dir = normalize_path(temporary_allocator(), temporary_directory(temporary_allocator()), NIX_SEPARATOR_STRING);
+ android_glue_object = concatenate4_strings(temporary_allocator(), temp_dir, str_lit("android_native_app_glue-"), hash, str_lit(".o"));
+ android_glue_static_lib = concatenate4_strings(permanent_allocator(), temp_dir, str_lit("libandroid_native_app_glue-"), hash, str_lit(".a"));
+
+ gbString glue = gb_string_make_length(heap_allocator(), ODIN_ANDROID_NDK_TOOLCHAIN.text, ODIN_ANDROID_NDK_TOOLCHAIN.len);
+ defer (gb_string_free(glue));
+
+ glue = gb_string_append_fmt(glue, "bin/clang");
+ glue = gb_string_append_fmt(glue, " --target=aarch64-linux-android%d ", ODIN_ANDROID_API_LEVEL);
+ glue = gb_string_appendc(glue, "-c \"");
+ glue = gb_string_append_length(glue, ODIN_ANDROID_NDK.text, ODIN_ANDROID_NDK.len);
+ glue = gb_string_appendc(glue, "sources/android/native_app_glue/android_native_app_glue.c");
+ glue = gb_string_appendc(glue, "\" ");
+ glue = gb_string_appendc(glue, "-o \"");
+ glue = gb_string_append_length(glue, android_glue_object.text, android_glue_object.len);
+ glue = gb_string_appendc(glue, "\" ");
+
+ glue = gb_string_appendc(glue, "--sysroot \"");
+ glue = gb_string_append_length(glue, ODIN_ANDROID_NDK_TOOLCHAIN.text, ODIN_ANDROID_NDK_TOOLCHAIN.len);
+ glue = gb_string_appendc(glue, "sysroot");
+ glue = gb_string_appendc(glue, "\" ");
+
+ glue = gb_string_appendc(glue, "\"-I");
+ glue = gb_string_append_length(glue, ODIN_ANDROID_NDK_TOOLCHAIN.text, ODIN_ANDROID_NDK_TOOLCHAIN.len);
+ glue = gb_string_appendc(glue, "sysroot/usr/include/");
+ glue = gb_string_appendc(glue, "\" ");
+
+ glue = gb_string_appendc(glue, "\"-I");
+ glue = gb_string_append_length(glue, ODIN_ANDROID_NDK_TOOLCHAIN.text, ODIN_ANDROID_NDK_TOOLCHAIN.len);
+ glue = gb_string_appendc(glue, "sysroot/usr/include/aarch64-linux-android/");
+ glue = gb_string_appendc(glue, "\" ");
+
+
+ glue = gb_string_appendc(glue, "-Wno-macro-redefined ");
+
+ result = system_exec_command_line_app("android-native-app-glue-compile", glue);
+ if (result) {
+ return result;
+ }
+
+ TIME_SECTION("Android Native App Glue ar");
+
+ gbString ar = gb_string_make_length(heap_allocator(), ODIN_ANDROID_NDK_TOOLCHAIN.text, ODIN_ANDROID_NDK_TOOLCHAIN.len);
+ defer (gb_string_free(ar));
+
+ ar = gb_string_appendc(ar, "bin/llvm-ar");
+
+ ar = gb_string_appendc(ar, " rcs ");
+
+ ar = gb_string_appendc(ar, "\"");
+ ar = gb_string_append_length(ar, android_glue_static_lib.text, android_glue_static_lib.len);
+ ar = gb_string_appendc(ar, "\" ");
+
+ ar = gb_string_appendc(ar, "\"");
+ ar = gb_string_append_length(ar, android_glue_object.text, android_glue_object.len);
+ ar = gb_string_appendc(ar, "\" ");
+
+ result = system_exec_command_line_app("android-native-app-glue-ar", ar);
+ if (result) {
+ return result;
+ }
+
+ object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(android_glue_static_lib));
+ }
+
+
for (String object_path : gen->output_object_paths) {
object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
}
@@ -630,7 +748,9 @@ gb_internal i32 linker_stage(LinkerData *gen) {
link_settings = gb_string_appendc(link_settings, "-Wl,-init,'_odin_entry_point' ");
link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' ");
}
-
+ } else if (is_android) {
+ // Always shared even in android!
+ link_settings = gb_string_appendc(link_settings, "-shared ");
}
if (build_context.build_mode == BuildMode_Executable && build_context.reloc_mode == RelocMode_PIC) {
@@ -639,6 +759,7 @@ gb_internal i32 linker_stage(LinkerData *gen) {
if (build_context.metrics.os != TargetOs_openbsd
&& build_context.metrics.os != TargetOs_haiku
&& build_context.metrics.arch != TargetArch_riscv64
+ && !is_android
) {
// OpenBSD and Haiku default to PIE executable. do not pass -no-pie for it.
link_settings = gb_string_appendc(link_settings, "-no-pie ");
@@ -670,6 +791,32 @@ gb_internal i32 linker_stage(LinkerData *gen) {
// This points the linker to where the entry point is
link_settings = gb_string_appendc(link_settings, "-e _main ");
}
+ } else if (build_context.metrics.os == TargetOs_openbsd) {
+ // OpenBSD ports install shared libraries in /usr/local/lib. Also, we must explicitly link libpthread.
+ platform_lib_str = gb_string_appendc(platform_lib_str, "-lpthread -Wl,-L/usr/local/lib ");
+ // Until the LLVM back-end can be adapted to emit endbr64 instructions on amd64, we
+ // need to pass -z nobtcfi in order to allow the resulting program to run under
+ // OpenBSD 7.4 and newer. Once support is added at compile time, this can be dropped.
+ platform_lib_str = gb_string_appendc(platform_lib_str, "-Wl,-z,nobtcfi ");
+ }
+
+ if (is_android) {
+ GB_ASSERT(ODIN_ANDROID_NDK_TOOLCHAIN_LIB.len != 0);
+ GB_ASSERT(ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL.len != 0);
+ GB_ASSERT(ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT.len != 0);
+
+ platform_lib_str = gb_string_appendc(platform_lib_str, "\"-L");
+ platform_lib_str = gb_string_append_length(platform_lib_str, ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL.text, ODIN_ANDROID_NDK_TOOLCHAIN_LIB_LEVEL.len);
+ platform_lib_str = gb_string_appendc(platform_lib_str, "\" ");
+
+ platform_lib_str = gb_string_appendc(platform_lib_str, "-landroid ");
+ platform_lib_str = gb_string_appendc(platform_lib_str, "-llog ");
+
+ platform_lib_str = gb_string_appendc(platform_lib_str, "\"--sysroot=");
+ platform_lib_str = gb_string_append_length(platform_lib_str, ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT.text, ODIN_ANDROID_NDK_TOOLCHAIN_SYSROOT.len);
+ platform_lib_str = gb_string_appendc(platform_lib_str, "\" ");
+
+ link_settings = gb_string_appendc(link_settings, "-u ANativeActivity_onCreate ");
}
if (!build_context.no_rpath) {
@@ -678,24 +825,36 @@ gb_internal i32 linker_stage(LinkerData *gen) {
if (build_context.metrics.os == TargetOs_darwin) {
link_settings = gb_string_appendc(link_settings, "-Wl,-rpath,@loader_path ");
} else {
- link_settings = gb_string_appendc(link_settings, "-Wl,-rpath,\\$ORIGIN ");
+ if (is_android) {
+ // ignore
+ } else {
+ link_settings = gb_string_appendc(link_settings, "-Wl,-rpath,\\$ORIGIN ");
+ }
}
}
if (!build_context.no_crt) {
- platform_lib_str = gb_string_appendc(platform_lib_str, "-lm ");
+ lib_str = gb_string_appendc(lib_str, "-lm ");
if (build_context.metrics.os == TargetOs_darwin) {
// NOTE: adding this causes a warning about duplicate libraries, I think it is
// automatically assumed/added by clang when you don't do `-nostdlib`.
- // platform_lib_str = gb_string_appendc(platform_lib_str, "-lSystem ");
+ // lib_str = gb_string_appendc(lib_str, "-lSystem ");
} else {
- platform_lib_str = gb_string_appendc(platform_lib_str, "-lc ");
+ lib_str = gb_string_appendc(lib_str, "-lc ");
}
}
- gbString link_command_line = gb_string_make(heap_allocator(), clang_path);
+ gbString link_command_line = gb_string_make(heap_allocator(), "");
defer (gb_string_free(link_command_line));
+ if (is_android) {
+ gbString ndk_bin_directory = gb_string_make_length(temporary_allocator(), ODIN_ANDROID_NDK_TOOLCHAIN.text, ODIN_ANDROID_NDK_TOOLCHAIN.len);
+ link_command_line = gb_string_appendc(link_command_line, ndk_bin_directory);
+ link_command_line = gb_string_appendc(link_command_line, "bin/clang");
+ link_command_line = gb_string_append_fmt(link_command_line, " --target=aarch64-linux-android%d ", ODIN_ANDROID_API_LEVEL);
+ } else {
+ link_command_line = gb_string_appendc(link_command_line, clang_path);
+ }
link_command_line = gb_string_appendc(link_command_line, " -Wno-unused-command-line-argument ");
link_command_line = gb_string_appendc(link_command_line, object_files);
link_command_line = gb_string_append_fmt(link_command_line, " -o \"%.*s\" ", LIT(output_filename));
@@ -705,6 +864,11 @@ gb_internal i32 linker_stage(LinkerData *gen) {
link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.extra_linker_flags));
link_command_line = gb_string_append_fmt(link_command_line, " %s ", link_settings);
+
+ if (is_android) {
+ TIME_SECTION("Linking");
+ }
+
if (build_context.linker_choice == Linker_lld) {
link_command_line = gb_string_append_fmt(link_command_line, " -fuse-ld=lld");
result = system_exec_command_line_app("lld-link", link_command_line);
diff --git a/src/llvm-c/Analysis.h b/src/llvm-c/Analysis.h
index 6b93b5c3d..270b145a4 100644
--- a/src/llvm-c/Analysis.h
+++ b/src/llvm-c/Analysis.h
@@ -19,8 +19,8 @@
#ifndef LLVM_C_ANALYSIS_H
#define LLVM_C_ANALYSIS_H
-#include "ExternC.h"
-#include "Types.h"
+#include "llvm-c/ExternC.h"
+#include "llvm-c/Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/BitReader.h b/src/llvm-c/BitReader.h
index 725f3fa84..088107468 100644
--- a/src/llvm-c/BitReader.h
+++ b/src/llvm-c/BitReader.h
@@ -19,8 +19,8 @@
#ifndef LLVM_C_BITREADER_H
#define LLVM_C_BITREADER_H
-#include "ExternC.h"
-#include "Types.h"
+#include "llvm-c/ExternC.h"
+#include "llvm-c/Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/BitWriter.h b/src/llvm-c/BitWriter.h
index ba4a61afc..ea84b6593 100644
--- a/src/llvm-c/BitWriter.h
+++ b/src/llvm-c/BitWriter.h
@@ -19,8 +19,8 @@
#ifndef LLVM_C_BITWRITER_H
#define LLVM_C_BITWRITER_H
-#include "ExternC.h"
-#include "Types.h"
+#include "llvm-c/ExternC.h"
+#include "llvm-c/Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/Comdat.h b/src/llvm-c/Comdat.h
index 30df20799..8002bc058 100644
--- a/src/llvm-c/Comdat.h
+++ b/src/llvm-c/Comdat.h
@@ -14,8 +14,8 @@
#ifndef LLVM_C_COMDAT_H
#define LLVM_C_COMDAT_H
-#include "ExternC.h"
-#include "Types.h"
+#include "llvm-c/ExternC.h"
+#include "llvm-c/Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/Config/llvm-config.h b/src/llvm-c/Config/llvm-config.h
index e4edb83c5..8b77597c9 100644
--- a/src/llvm-c/Config/llvm-config.h
+++ b/src/llvm-c/Config/llvm-config.h
@@ -142,16 +142,16 @@
#define LLVM_USE_PERF 0
/* Major version of the LLVM API */
-#define LLVM_VERSION_MAJOR 18
+#define LLVM_VERSION_MAJOR 20
/* Minor version of the LLVM API */
#define LLVM_VERSION_MINOR 1
/* Patch version of the LLVM API */
-#define LLVM_VERSION_PATCH 8
+#define LLVM_VERSION_PATCH 0
/* LLVM version string */
-#define LLVM_VERSION_STRING "18.1.8"
+#define LLVM_VERSION_STRING "20.1.0"
/* Whether LLVM records statistics for use with GetStatistics(),
* PrintStatistics() or PrintStatisticsJSON()
diff --git a/src/llvm-c/Core.h b/src/llvm-c/Core.h
index 25b8248fd..dc8ecf4fb 100644
--- a/src/llvm-c/Core.h
+++ b/src/llvm-c/Core.h
@@ -15,11 +15,11 @@
#ifndef LLVM_C_CORE_H
#define LLVM_C_CORE_H
-#include "Deprecated.h"
-#include "ErrorHandling.h"
-#include "ExternC.h"
+#include "llvm-c/Deprecated.h"
+#include "llvm-c/ErrorHandling.h"
+#include "llvm-c/ExternC.h"
-#include "Types.h"
+#include "llvm-c/Types.h"
LLVM_C_EXTERN_C_BEGIN
@@ -146,27 +146,27 @@ typedef enum {
} LLVMOpcode;
typedef enum {
- LLVMVoidTypeKind, /**< type with no size */
- LLVMHalfTypeKind, /**< 16 bit floating point type */
- LLVMFloatTypeKind, /**< 32 bit floating point type */
- LLVMDoubleTypeKind, /**< 64 bit floating point type */
- LLVMX86_FP80TypeKind, /**< 80 bit floating point type (X87) */
- LLVMFP128TypeKind, /**< 128 bit floating point type (112-bit mantissa)*/
- LLVMPPC_FP128TypeKind, /**< 128 bit floating point type (two 64-bits) */
- LLVMLabelTypeKind, /**< Labels */
- LLVMIntegerTypeKind, /**< Arbitrary bit width integers */
- LLVMFunctionTypeKind, /**< Functions */
- LLVMStructTypeKind, /**< Structures */
- LLVMArrayTypeKind, /**< Arrays */
- LLVMPointerTypeKind, /**< Pointers */
- LLVMVectorTypeKind, /**< Fixed width SIMD vector type */
- LLVMMetadataTypeKind, /**< Metadata */
- LLVMX86_MMXTypeKind, /**< X86 MMX */
- LLVMTokenTypeKind, /**< Tokens */
- LLVMScalableVectorTypeKind, /**< Scalable SIMD vector type */
- LLVMBFloatTypeKind, /**< 16 bit brain floating point type */
- LLVMX86_AMXTypeKind, /**< X86 AMX */
- LLVMTargetExtTypeKind, /**< Target extension type */
+ LLVMVoidTypeKind = 0, /**< type with no size */
+ LLVMHalfTypeKind = 1, /**< 16 bit floating point type */
+ LLVMFloatTypeKind = 2, /**< 32 bit floating point type */
+ LLVMDoubleTypeKind = 3, /**< 64 bit floating point type */
+ LLVMX86_FP80TypeKind = 4, /**< 80 bit floating point type (X87) */
+ LLVMFP128TypeKind = 5, /**< 128 bit floating point type (112-bit mantissa)*/
+ LLVMPPC_FP128TypeKind = 6, /**< 128 bit floating point type (two 64-bits) */
+ LLVMLabelTypeKind = 7, /**< Labels */
+ LLVMIntegerTypeKind = 8, /**< Arbitrary bit width integers */
+ LLVMFunctionTypeKind = 9, /**< Functions */
+ LLVMStructTypeKind = 10, /**< Structures */
+ LLVMArrayTypeKind = 11, /**< Arrays */
+ LLVMPointerTypeKind = 12, /**< Pointers */
+ LLVMVectorTypeKind = 13, /**< Fixed width SIMD vector type */
+ LLVMMetadataTypeKind = 14, /**< Metadata */
+ /* 15 previously used by LLVMX86_MMXTypeKind */
+ LLVMTokenTypeKind = 16, /**< Tokens */
+ LLVMScalableVectorTypeKind = 17, /**< Scalable SIMD vector type */
+ LLVMBFloatTypeKind = 18, /**< 16 bit brain floating point type */
+ LLVMX86_AMXTypeKind = 19, /**< X86 AMX */
+ LLVMTargetExtTypeKind = 20, /**< Target extension type */
} LLVMTypeKind;
typedef enum {
@@ -286,6 +286,7 @@ typedef enum {
LLVMInstructionValueKind,
LLVMPoisonValueValueKind,
LLVMConstantTargetNoneValueKind,
+ LLVMConstantPtrAuthValueKind,
} LLVMValueKind;
typedef enum {
@@ -361,35 +362,42 @@ typedef enum {
} LLVMAtomicOrdering;
typedef enum {
- LLVMAtomicRMWBinOpXchg, /**< Set the new value and return the one old */
- LLVMAtomicRMWBinOpAdd, /**< Add a value and return the old one */
- LLVMAtomicRMWBinOpSub, /**< Subtract a value and return the old one */
- LLVMAtomicRMWBinOpAnd, /**< And a value and return the old one */
- LLVMAtomicRMWBinOpNand, /**< Not-And a value and return the old one */
- LLVMAtomicRMWBinOpOr, /**< OR a value and return the old one */
- LLVMAtomicRMWBinOpXor, /**< Xor a value and return the old one */
- LLVMAtomicRMWBinOpMax, /**< Sets the value if it's greater than the
- original using a signed comparison and return
- the old one */
- LLVMAtomicRMWBinOpMin, /**< Sets the value if it's Smaller than the
- original using a signed comparison and return
- the old one */
- LLVMAtomicRMWBinOpUMax, /**< Sets the value if it's greater than the
- original using an unsigned comparison and return
- the old one */
- LLVMAtomicRMWBinOpUMin, /**< Sets the value if it's greater than the
- original using an unsigned comparison and return
- the old one */
- LLVMAtomicRMWBinOpFAdd, /**< Add a floating point value and return the
- old one */
- LLVMAtomicRMWBinOpFSub, /**< Subtract a floating point value and return the
+ LLVMAtomicRMWBinOpXchg, /**< Set the new value and return the one old */
+ LLVMAtomicRMWBinOpAdd, /**< Add a value and return the old one */
+ LLVMAtomicRMWBinOpSub, /**< Subtract a value and return the old one */
+ LLVMAtomicRMWBinOpAnd, /**< And a value and return the old one */
+ LLVMAtomicRMWBinOpNand, /**< Not-And a value and return the old one */
+ LLVMAtomicRMWBinOpOr, /**< OR a value and return the old one */
+ LLVMAtomicRMWBinOpXor, /**< Xor a value and return the old one */
+ LLVMAtomicRMWBinOpMax, /**< Sets the value if it's greater than the
+ original using a signed comparison and return
+ the old one */
+ LLVMAtomicRMWBinOpMin, /**< Sets the value if it's Smaller than the
+ original using a signed comparison and return
+ the old one */
+ LLVMAtomicRMWBinOpUMax, /**< Sets the value if it's greater than the
+ original using an unsigned comparison and return
+ the old one */
+ LLVMAtomicRMWBinOpUMin, /**< Sets the value if it's greater than the
+ original using an unsigned comparison and return
+ the old one */
+ LLVMAtomicRMWBinOpFAdd, /**< Add a floating point value and return the
old one */
- LLVMAtomicRMWBinOpFMax, /**< Sets the value if it's greater than the
- original using an floating point comparison and
- return the old one */
- LLVMAtomicRMWBinOpFMin, /**< Sets the value if it's smaller than the
- original using an floating point comparison and
- return the old one */
+ LLVMAtomicRMWBinOpFSub, /**< Subtract a floating point value and return the
+ old one */
+ LLVMAtomicRMWBinOpFMax, /**< Sets the value if it's greater than the
+ original using an floating point comparison and
+ return the old one */
+ LLVMAtomicRMWBinOpFMin, /**< Sets the value if it's smaller than the
+ original using an floating point comparison and
+ return the old one */
+ LLVMAtomicRMWBinOpUIncWrap, /**< Increments the value, wrapping back to zero
+ when incremented above input value */
+ LLVMAtomicRMWBinOpUDecWrap, /**< Decrements the value, wrapping back to
+ the input value when decremented below zero */
+ LLVMAtomicRMWBinOpUSubCond, /**
#else
diff --git a/src/llvm-c/Error.h b/src/llvm-c/Error.h
index 00746c701..874bbcfe8 100644
--- a/src/llvm-c/Error.h
+++ b/src/llvm-c/Error.h
@@ -14,7 +14,7 @@
#ifndef LLVM_C_ERROR_H
#define LLVM_C_ERROR_H
-#include "ExternC.h"
+#include "llvm-c/ExternC.h"
LLVM_C_EXTERN_C_BEGIN
@@ -51,6 +51,14 @@ LLVMErrorTypeId LLVMGetErrorTypeId(LLVMErrorRef Err);
*/
void LLVMConsumeError(LLVMErrorRef Err);
+/**
+ * Report a fatal error if Err is a failure value.
+ *
+ * This function can be used to wrap calls to fallible functions ONLY when it is
+ * known that the Error will always be a success value.
+ */
+void LLVMCantFail(LLVMErrorRef Err);
+
/**
* Returns the given string's error message. This operation consumes the error,
* and the given LLVMErrorRef value is not usable once this call returns.
diff --git a/src/llvm-c/ErrorHandling.h b/src/llvm-c/ErrorHandling.h
index 7f9b50a95..d9b9f2275 100644
--- a/src/llvm-c/ErrorHandling.h
+++ b/src/llvm-c/ErrorHandling.h
@@ -14,7 +14,7 @@
#ifndef LLVM_C_ERRORHANDLING_H
#define LLVM_C_ERRORHANDLING_H
-#include "ExternC.h"
+#include "llvm-c/ExternC.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/ExecutionEngine.h b/src/llvm-c/ExecutionEngine.h
index 8e72faefd..c5fc9bdb4 100644
--- a/src/llvm-c/ExecutionEngine.h
+++ b/src/llvm-c/ExecutionEngine.h
@@ -19,10 +19,10 @@
#ifndef LLVM_C_EXECUTIONENGINE_H
#define LLVM_C_EXECUTIONENGINE_H
-#include "ExternC.h"
-#include "Target.h"
-#include "TargetMachine.h"
-#include "Types.h"
+#include "llvm-c/ExternC.h"
+#include "llvm-c/Target.h"
+#include "llvm-c/TargetMachine.h"
+#include "llvm-c/Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/IRReader.h b/src/llvm-c/IRReader.h
index ec1110c7a..905b84fa5 100644
--- a/src/llvm-c/IRReader.h
+++ b/src/llvm-c/IRReader.h
@@ -14,8 +14,8 @@
#ifndef LLVM_C_IRREADER_H
#define LLVM_C_IRREADER_H
-#include "ExternC.h"
-#include "Types.h"
+#include "llvm-c/ExternC.h"
+#include "llvm-c/Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/LLJIT.h b/src/llvm-c/LLJIT.h
index ee207e10e..a58c3b8bb 100644
--- a/src/llvm-c/LLJIT.h
+++ b/src/llvm-c/LLJIT.h
@@ -24,10 +24,10 @@
#ifndef LLVM_C_LLJIT_H
#define LLVM_C_LLJIT_H
-#include "Error.h"
-#include "Orc.h"
-#include "TargetMachine.h"
-#include "Types.h"
+#include "llvm-c/Error.h"
+#include "llvm-c/Orc.h"
+#include "llvm-c/TargetMachine.h"
+#include "llvm-c/Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/LLJITUtils.h b/src/llvm-c/LLJITUtils.h
index 57ffedff8..940097432 100644
--- a/src/llvm-c/LLJITUtils.h
+++ b/src/llvm-c/LLJITUtils.h
@@ -26,7 +26,7 @@
#ifndef LLVM_C_LLJITUTILS_H
#define LLVM_C_LLJITUTILS_H
-#include "LLJIT.h"
+#include "llvm-c/LLJIT.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/Linker.h b/src/llvm-c/Linker.h
index 463a2cff9..acff5d5e2 100644
--- a/src/llvm-c/Linker.h
+++ b/src/llvm-c/Linker.h
@@ -14,8 +14,8 @@
#ifndef LLVM_C_LINKER_H
#define LLVM_C_LINKER_H
-#include "ExternC.h"
-#include "Types.h"
+#include "llvm-c/ExternC.h"
+#include "llvm-c/Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/Object.h b/src/llvm-c/Object.h
index 1948c3c34..2f39179c1 100644
--- a/src/llvm-c/Object.h
+++ b/src/llvm-c/Object.h
@@ -19,9 +19,9 @@
#ifndef LLVM_C_OBJECT_H
#define LLVM_C_OBJECT_H
-#include "ExternC.h"
-#include "Types.h"
-#include "Config/llvm-config.h"
+#include "llvm-c/ExternC.h"
+#include "llvm-c/Types.h"
+#include "llvm-c/Config/llvm-config.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/Orc.h b/src/llvm-c/Orc.h
index ecd110b4d..8609a8a6d 100644
--- a/src/llvm-c/Orc.h
+++ b/src/llvm-c/Orc.h
@@ -27,9 +27,9 @@
#ifndef LLVM_C_ORC_H
#define LLVM_C_ORC_H
-#include "Error.h"
-#include "TargetMachine.h"
-#include "Types.h"
+#include "llvm-c/Error.h"
+#include "llvm-c/TargetMachine.h"
+#include "llvm-c/Types.h"
LLVM_C_EXTERN_C_BEGIN
@@ -181,6 +181,15 @@ typedef struct {
*/
typedef LLVMOrcCDependenceMapPair *LLVMOrcCDependenceMapPairs;
+/**
+ * A set of symbols that share dependencies.
+ */
+typedef struct {
+ LLVMOrcCSymbolsList Symbols;
+ LLVMOrcCDependenceMapPairs Dependencies;
+ size_t NumDependencies;
+} LLVMOrcCSymbolDependenceGroup;
+
/**
* Lookup kind. This can be used by definition generators when deciding whether
* to produce a definition for a requested symbol.
@@ -808,6 +817,19 @@ LLVMErrorRef LLVMOrcMaterializationResponsibilityNotifyResolved(
* that all symbols covered by this MaterializationResponsibility instance
* have been emitted.
*
+ * This function takes ownership of the symbols in the Dependencies struct.
+ * This allows the following pattern...
+ *
+ * LLVMOrcSymbolStringPoolEntryRef Names[] = {...};
+ * LLVMOrcCDependenceMapPair Dependence = {JD, {Names, sizeof(Names)}}
+ * LLVMOrcMaterializationResponsibilityAddDependencies(JD, Name, &Dependence,
+ * 1);
+ *
+ * ... without requiring cleanup of the elements of the Names array afterwards.
+ *
+ * The client is still responsible for deleting the Dependencies.Names arrays,
+ * and the Dependencies array itself.
+ *
* This method will return an error if any symbols being resolved have been
* moved to the error state due to the failure of a dependency. If this
* method returns an error then clients should log it and call
@@ -817,7 +839,8 @@ LLVMErrorRef LLVMOrcMaterializationResponsibilityNotifyResolved(
* LLVMErrorSuccess.
*/
LLVMErrorRef LLVMOrcMaterializationResponsibilityNotifyEmitted(
- LLVMOrcMaterializationResponsibilityRef MR);
+ LLVMOrcMaterializationResponsibilityRef MR,
+ LLVMOrcCSymbolDependenceGroup *SymbolDepGroups, size_t NumSymbolDepGroups);
/**
* Attempt to claim responsibility for new definitions. This method can be
@@ -870,38 +893,6 @@ LLVMErrorRef LLVMOrcMaterializationResponsibilityDelegate(
LLVMOrcSymbolStringPoolEntryRef *Symbols, size_t NumSymbols,
LLVMOrcMaterializationResponsibilityRef *Result);
-/**
- * Adds dependencies to a symbol that the MaterializationResponsibility is
- * responsible for.
- *
- * This function takes ownership of Dependencies struct. The Names
- * array have been retained for this function. This allows the following
- * pattern...
- *
- * LLVMOrcSymbolStringPoolEntryRef Names[] = {...};
- * LLVMOrcCDependenceMapPair Dependence = {JD, {Names, sizeof(Names)}}
- * LLVMOrcMaterializationResponsibilityAddDependencies(JD, Name, &Dependence,
- * 1);
- *
- * ... without requiring cleanup of the elements of the Names array afterwards.
- *
- * The client is still responsible for deleting the Dependencies.Names array
- * itself.
- */
-void LLVMOrcMaterializationResponsibilityAddDependencies(
- LLVMOrcMaterializationResponsibilityRef MR,
- LLVMOrcSymbolStringPoolEntryRef Name,
- LLVMOrcCDependenceMapPairs Dependencies, size_t NumPairs);
-
-/**
- * Adds dependencies to all symbols that the MaterializationResponsibility is
- * responsible for. See LLVMOrcMaterializationResponsibilityAddDependencies for
- * notes about memory responsibility.
- */
-void LLVMOrcMaterializationResponsibilityAddDependenciesForAll(
- LLVMOrcMaterializationResponsibilityRef MR,
- LLVMOrcCDependenceMapPairs Dependencies, size_t NumPairs);
-
/**
* Create a "bare" JITDylib.
*
diff --git a/src/llvm-c/OrcEE.h b/src/llvm-c/OrcEE.h
index aef24c7aa..d451187aa 100644
--- a/src/llvm-c/OrcEE.h
+++ b/src/llvm-c/OrcEE.h
@@ -24,11 +24,11 @@
#ifndef LLVM_C_ORCEE_H
#define LLVM_C_ORCEE_H
-#include "Error.h"
-#include "ExecutionEngine.h"
-#include "Orc.h"
-#include "TargetMachine.h"
-#include "Types.h"
+#include "llvm-c/Error.h"
+#include "llvm-c/ExecutionEngine.h"
+#include "llvm-c/Orc.h"
+#include "llvm-c/TargetMachine.h"
+#include "llvm-c/Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/Remarks.h b/src/llvm-c/Remarks.h
index 548a4041a..ffe647a65 100644
--- a/src/llvm-c/Remarks.h
+++ b/src/llvm-c/Remarks.h
@@ -15,8 +15,8 @@
#ifndef LLVM_C_REMARKS_H
#define LLVM_C_REMARKS_H
-#include "ExternC.h"
-#include "Types.h"
+#include "llvm-c/ExternC.h"
+#include "llvm-c/Types.h"
#ifdef __cplusplus
#include
#else
diff --git a/src/llvm-c/Support.h b/src/llvm-c/Support.h
index 31a75354c..17657861b 100644
--- a/src/llvm-c/Support.h
+++ b/src/llvm-c/Support.h
@@ -14,9 +14,9 @@
#ifndef LLVM_C_SUPPORT_H
#define LLVM_C_SUPPORT_H
-#include "DataTypes.h"
-#include "ExternC.h"
-#include "Types.h"
+#include "llvm-c/DataTypes.h"
+#include "llvm-c/ExternC.h"
+#include "llvm-c/Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/Target.h b/src/llvm-c/Target.h
index 4d03741c4..54367a41b 100644
--- a/src/llvm-c/Target.h
+++ b/src/llvm-c/Target.h
@@ -19,9 +19,9 @@
#ifndef LLVM_C_TARGET_H
#define LLVM_C_TARGET_H
-#include "ExternC.h"
-#include "Types.h"
-#include "Config/llvm-config.h"
+#include "llvm-c/ExternC.h"
+#include "llvm-c/Types.h"
+#include "llvm-c/Config/llvm-config.h"
LLVM_C_EXTERN_C_BEGIN
@@ -40,34 +40,34 @@ typedef struct LLVMOpaqueTargetLibraryInfotData *LLVMTargetLibraryInfoRef;
/* Declare all of the target-initialization functions that are available. */
#define LLVM_TARGET(TargetName) \
void LLVMInitialize##TargetName##TargetInfo(void);
-#include "Config/Targets.def"
+#include "llvm-c/Config/Targets.def"
#undef LLVM_TARGET /* Explicit undef to make SWIG happier */
#define LLVM_TARGET(TargetName) void LLVMInitialize##TargetName##Target(void);
-#include "Config/Targets.def"
+#include "llvm-c/Config/Targets.def"
#undef LLVM_TARGET /* Explicit undef to make SWIG happier */
#define LLVM_TARGET(TargetName) \
void LLVMInitialize##TargetName##TargetMC(void);
-#include "Config/Targets.def"
+#include "llvm-c/Config/Targets.def"
#undef LLVM_TARGET /* Explicit undef to make SWIG happier */
/* Declare all of the available assembly printer initialization functions. */
#define LLVM_ASM_PRINTER(TargetName) \
void LLVMInitialize##TargetName##AsmPrinter(void);
-#include "Config/AsmPrinters.def"
+#include "llvm-c/Config/AsmPrinters.def"
#undef LLVM_ASM_PRINTER /* Explicit undef to make SWIG happier */
/* Declare all of the available assembly parser initialization functions. */
#define LLVM_ASM_PARSER(TargetName) \
void LLVMInitialize##TargetName##AsmParser(void);
-#include "Config/AsmParsers.def"
+#include "llvm-c/Config/AsmParsers.def"
#undef LLVM_ASM_PARSER /* Explicit undef to make SWIG happier */
/* Declare all of the available disassembler initialization functions. */
#define LLVM_DISASSEMBLER(TargetName) \
void LLVMInitialize##TargetName##Disassembler(void);
-#include "Config/Disassemblers.def"
+#include "llvm-c/Config/Disassemblers.def"
#undef LLVM_DISASSEMBLER /* Explicit undef to make SWIG happier */
/** LLVMInitializeAllTargetInfos - The main program should call this function if
@@ -75,7 +75,7 @@ typedef struct LLVMOpaqueTargetLibraryInfotData *LLVMTargetLibraryInfoRef;
support. */
static inline void LLVMInitializeAllTargetInfos(void) {
#define LLVM_TARGET(TargetName) LLVMInitialize##TargetName##TargetInfo();
-#include "Config/Targets.def"
+#include "llvm-c/Config/Targets.def"
#undef LLVM_TARGET /* Explicit undef to make SWIG happier */
}
@@ -84,7 +84,7 @@ static inline void LLVMInitializeAllTargetInfos(void) {
support. */
static inline void LLVMInitializeAllTargets(void) {
#define LLVM_TARGET(TargetName) LLVMInitialize##TargetName##Target();
-#include "Config/Targets.def"
+#include "llvm-c/Config/Targets.def"
#undef LLVM_TARGET /* Explicit undef to make SWIG happier */
}
@@ -93,7 +93,7 @@ static inline void LLVMInitializeAllTargets(void) {
support. */
static inline void LLVMInitializeAllTargetMCs(void) {
#define LLVM_TARGET(TargetName) LLVMInitialize##TargetName##TargetMC();
-#include "Config/Targets.def"
+#include "llvm-c/Config/Targets.def"
#undef LLVM_TARGET /* Explicit undef to make SWIG happier */
}
@@ -102,7 +102,7 @@ static inline void LLVMInitializeAllTargetMCs(void) {
available via the TargetRegistry. */
static inline void LLVMInitializeAllAsmPrinters(void) {
#define LLVM_ASM_PRINTER(TargetName) LLVMInitialize##TargetName##AsmPrinter();
-#include "Config/AsmPrinters.def"
+#include "llvm-c/Config/AsmPrinters.def"
#undef LLVM_ASM_PRINTER /* Explicit undef to make SWIG happier */
}
@@ -111,7 +111,7 @@ static inline void LLVMInitializeAllAsmPrinters(void) {
available via the TargetRegistry. */
static inline void LLVMInitializeAllAsmParsers(void) {
#define LLVM_ASM_PARSER(TargetName) LLVMInitialize##TargetName##AsmParser();
-#include "Config/AsmParsers.def"
+#include "llvm-c/Config/AsmParsers.def"
#undef LLVM_ASM_PARSER /* Explicit undef to make SWIG happier */
}
@@ -121,7 +121,7 @@ static inline void LLVMInitializeAllAsmParsers(void) {
static inline void LLVMInitializeAllDisassemblers(void) {
#define LLVM_DISASSEMBLER(TargetName) \
LLVMInitialize##TargetName##Disassembler();
-#include "Config/Disassemblers.def"
+#include "llvm-c/Config/Disassemblers.def"
#undef LLVM_DISASSEMBLER /* Explicit undef to make SWIG happier */
}
@@ -244,7 +244,7 @@ LLVMTypeRef LLVMIntPtrTypeInContext(LLVMContextRef C, LLVMTargetDataRef TD);
LLVMTypeRef LLVMIntPtrTypeForASInContext(LLVMContextRef C, LLVMTargetDataRef TD,
unsigned AS);
-/** Computes the size of a type in bytes for a target.
+/** Computes the size of a type in bits for a target.
See the method llvm::DataLayout::getTypeSizeInBits. */
unsigned long long LLVMSizeOfTypeInBits(LLVMTargetDataRef TD, LLVMTypeRef Ty);
diff --git a/src/llvm-c/TargetMachine.h b/src/llvm-c/TargetMachine.h
index aa628e216..cbe891380 100644
--- a/src/llvm-c/TargetMachine.h
+++ b/src/llvm-c/TargetMachine.h
@@ -19,9 +19,9 @@
#ifndef LLVM_C_TARGETMACHINE_H
#define LLVM_C_TARGETMACHINE_H
-#include "ExternC.h"
-#include "Target.h"
-#include "Types.h"
+#include "llvm-c/ExternC.h"
+#include "llvm-c/Target.h"
+#include "llvm-c/Types.h"
LLVM_C_EXTERN_C_BEGIN
diff --git a/src/llvm-c/Transforms/PassBuilder.h b/src/llvm-c/Transforms/PassBuilder.h
index 8ad2a9982..d297b57ca 100644
--- a/src/llvm-c/Transforms/PassBuilder.h
+++ b/src/llvm-c/Transforms/PassBuilder.h
@@ -14,9 +14,9 @@
#ifndef LLVM_C_TRANSFORMS_PASSBUILDER_H
#define LLVM_C_TRANSFORMS_PASSBUILDER_H
-#include "../Error.h"
-#include "../TargetMachine.h"
-#include "../Types.h"
+#include "llvm-c/Error.h"
+#include "llvm-c/TargetMachine.h"
+#include "llvm-c/Types.h"
/**
* @defgroup LLVMCCoreNewPM New Pass Manager
@@ -50,6 +50,16 @@ LLVMErrorRef LLVMRunPasses(LLVMModuleRef M, const char *Passes,
LLVMTargetMachineRef TM,
LLVMPassBuilderOptionsRef Options);
+/**
+ * Construct and run a set of passes over a function.
+ *
+ * This function behaves the same as LLVMRunPasses, but operates on a single
+ * function instead of an entire module.
+ */
+LLVMErrorRef LLVMRunPassesOnFunction(LLVMValueRef F, const char *Passes,
+ LLVMTargetMachineRef TM,
+ LLVMPassBuilderOptionsRef Options);
+
/**
* Create a new set of options for a PassBuilder
*
@@ -72,6 +82,14 @@ void LLVMPassBuilderOptionsSetVerifyEach(LLVMPassBuilderOptionsRef Options,
void LLVMPassBuilderOptionsSetDebugLogging(LLVMPassBuilderOptionsRef Options,
LLVMBool DebugLogging);
+/**
+ * Specify a custom alias analysis pipeline for the PassBuilder to be used
+ * instead of the default one. The string argument is not copied; the caller
+ * is responsible for ensuring it outlives the PassBuilderOptions instance.
+ */
+void LLVMPassBuilderOptionsSetAAPipeline(LLVMPassBuilderOptionsRef Options,
+ const char *AAPipeline);
+
void LLVMPassBuilderOptionsSetLoopInterleaving(
LLVMPassBuilderOptionsRef Options, LLVMBool LoopInterleaving);
diff --git a/src/llvm-c/Types.h b/src/llvm-c/Types.h
index 77aa7c9b4..4681500ef 100644
--- a/src/llvm-c/Types.h
+++ b/src/llvm-c/Types.h
@@ -14,8 +14,8 @@
#ifndef LLVM_C_TYPES_H
#define LLVM_C_TYPES_H
-#include "DataTypes.h"
-#include "ExternC.h"
+#include "llvm-c/DataTypes.h"
+#include "llvm-c/ExternC.h"
LLVM_C_EXTERN_C_BEGIN
@@ -169,6 +169,11 @@ typedef struct LLVMOpaqueJITEventListener *LLVMJITEventListenerRef;
*/
typedef struct LLVMOpaqueBinary *LLVMBinaryRef;
+/**
+ * @see llvm::DbgRecord
+ */
+typedef struct LLVMOpaqueDbgRecord *LLVMDbgRecordRef;
+
/**
* @}
*/
diff --git a/src/llvm-c/lto.h b/src/llvm-c/lto.h
index 89f76c695..5ceb02224 100644
--- a/src/llvm-c/lto.h
+++ b/src/llvm-c/lto.h
@@ -16,7 +16,7 @@
#ifndef LLVM_C_LTO_H
#define LLVM_C_LTO_H
-#include "ExternC.h"
+#include "llvm-c/ExternC.h"
#ifdef __cplusplus
#include
diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp
index 0b2bb7956..c8e1ca764 100644
--- a/src/llvm_abi.cpp
+++ b/src/llvm_abi.cpp
@@ -256,8 +256,10 @@ gb_internal i64 lb_sizeof(LLVMTypeRef type) {
}
break;
+#if LLVM_VERSION_MAJOR < 20
case LLVMX86_MMXTypeKind:
return 8;
+#endif
case LLVMVectorTypeKind:
{
LLVMTypeRef elem = OdinLLVMGetVectorElementType(type);
@@ -310,8 +312,10 @@ gb_internal i64 lb_alignof(LLVMTypeRef type) {
case LLVMArrayTypeKind:
return lb_alignof(OdinLLVMGetArrayElementType(type));
+#if LLVM_VERSION_MAJOR < 20
case LLVMX86_MMXTypeKind:
return 8;
+#endif
case LLVMVectorTypeKind:
{
// TODO(bill): This appears to be correct but LLVM isn't necessarily "great" with regards to documentation
@@ -569,7 +573,9 @@ namespace lbAbiAmd64SysV {
gb_internal void classify_with(LLVMTypeRef t, Array *cls, i64 ix, i64 off);
gb_internal void fixup(LLVMTypeRef t, Array *cls);
- gb_internal lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention);
+ gb_internal lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention,
+ bool is_arg,
+ i32 *int_regs, i32 *sse_regs);
gb_internal Array classify(LLVMTypeRef t);
gb_internal LLVMTypeRef llreg(LLVMContextRef c, Array const ®_classes, LLVMTypeRef type);
@@ -579,7 +585,9 @@ namespace lbAbiAmd64SysV {
}
LB_ABI_MODIFY_RETURN_IF_TUPLE_MACRO();
- return amd64_type(c, return_type, Amd64TypeAttribute_StructRect, ft->calling_convention);
+ return amd64_type(c, return_type, Amd64TypeAttribute_StructRect, ft->calling_convention,
+ false,
+ nullptr, nullptr);
}
gb_internal LB_ABI_INFO(abi_info) {
@@ -588,10 +596,16 @@ namespace lbAbiAmd64SysV {
ft->ctx = c;
ft->calling_convention = calling_convention;
+ i32 int_regs = 6; // rdi, rsi, rdx, rcx, r8, r9
+ i32 sse_regs = 8; // xmm0-xmm7
+
ft->args = array_make(lb_function_type_args_allocator(), arg_count);
for (unsigned i = 0; i < arg_count; i++) {
- ft->args[i] = amd64_type(c, arg_types[i], Amd64TypeAttribute_ByVal, calling_convention);
+ ft->args[i] = amd64_type(c, arg_types[i], Amd64TypeAttribute_ByVal, calling_convention,
+ true,
+ &int_regs, &sse_regs);
}
+
ft->ret = compute_return_type(ft, c, return_type, return_is_defined, return_is_tuple);
return ft;
@@ -650,17 +664,79 @@ namespace lbAbiAmd64SysV {
}
- gb_internal lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention) {
+
+ gb_internal bool is_aggregate(LLVMTypeRef type) {
+ LLVMTypeKind kind = LLVMGetTypeKind(type);
+ switch (kind) {
+ case LLVMStructTypeKind:
+ if (LLVMCountStructElementTypes(type) == 1) {
+ return is_aggregate(LLVMStructGetTypeAtIndex(type, 0));
+ }
+ return true;
+ case LLVMArrayTypeKind:
+ if (LLVMGetArrayLength(type) == 1) {
+ return is_aggregate(LLVMGetElementType(type));
+ }
+ return true;
+ }
+ return false;
+ };
+
+ gb_internal lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention,
+ bool is_arg,
+ i32 *int_regs, i32 *sse_regs) {
+ auto cls = classify(type);
+ i32 needed_int = 0;
+ i32 needed_sse = 0;
+ for (auto c : cls) {
+ switch (c) {
+ case RegClass_Int:
+ needed_int += 1;
+ break;
+ case RegClass_SSEFs:
+ case RegClass_SSEFv:
+ case RegClass_SSEDs:
+ case RegClass_SSEDv:
+ case RegClass_SSEInt8:
+ case RegClass_SSEInt16:
+ case RegClass_SSEInt32:
+ case RegClass_SSEInt64:
+ case RegClass_SSEInt128:
+ case RegClass_SSEUp:
+ needed_sse += 1;
+ break;
+ }
+ }
+
+ bool ran_out_of_regs = false;
+ if (int_regs && sse_regs) {
+ *int_regs -= needed_int;
+ *sse_regs -= needed_sse;
+ bool int_ok = *int_regs >= 0;
+ bool sse_ok = *sse_regs >= 0;
+
+ *int_regs = gb_max(*int_regs, 0);
+ *sse_regs = gb_max(*sse_regs, 0);
+
+ if ((!int_ok || !sse_ok) && is_aggregate(type)) {
+ ran_out_of_regs = true;
+ }
+ }
+
if (is_register(type)) {
LLVMAttributeRef attribute = nullptr;
if (type == LLVMInt1TypeInContext(c)) {
attribute = lb_create_enum_attribute(c, "zeroext");
}
return lb_arg_type_direct(type, nullptr, nullptr, attribute);
- }
-
- auto cls = classify(type);
- if (is_mem_cls(cls, attribute_kind)) {
+ } else if (ran_out_of_regs) {
+ if (is_arg) {
+ return lb_arg_type_indirect_byval(c, type);
+ } else {
+ LLVMAttributeRef attribute = lb_create_enum_attribute_with_type(c, "sret", type);
+ return lb_arg_type_indirect(type, attribute);
+ }
+ } else if (is_mem_cls(cls, attribute_kind)) {
LLVMAttributeRef attribute = nullptr;
if (attribute_kind == Amd64TypeAttribute_ByVal) {
if (is_calling_convention_odin(calling_convention)) {
@@ -1814,7 +1890,8 @@ gb_internal LB_ABI_INFO(lb_get_abi_info) {
return_type, return_is_defined,
ALLOW_SPLIT_MULTI_RETURNS && return_is_tuple && is_calling_convention_odin(calling_convention),
calling_convention,
- base_type(original_type));
+ base_type(original_type)
+ );
// NOTE(bill): this is handled here rather than when developing the type in `lb_type_internal_for_procedures_raw`
diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp
index 01ded321e..2f861573a 100644
--- a/src/llvm_backend.cpp
+++ b/src/llvm_backend.cpp
@@ -24,7 +24,7 @@
#include "llvm_backend_stmt.cpp"
#include "llvm_backend_proc.cpp"
-String get_default_microarchitecture() {
+gb_internal String get_default_microarchitecture() {
String default_march = str_lit("generic");
if (build_context.metrics.arch == TargetArch_amd64) {
// NOTE(bill): x86-64-v2 is more than enough for everyone
@@ -47,7 +47,7 @@ String get_default_microarchitecture() {
return default_march;
}
-String get_final_microarchitecture() {
+gb_internal String get_final_microarchitecture() {
BuildContext *bc = &build_context;
String microarch = bc->microarch;
@@ -169,11 +169,17 @@ gb_internal void lb_correct_entity_linkage(lbGenerator *gen) {
other_global = LLVMGetNamedGlobal(ec.other_module->mod, ec.cname);
if (other_global) {
LLVMSetLinkage(other_global, LLVMWeakAnyLinkage);
+ if (!ec.e->Variable.is_export && !ec.e->Variable.is_foreign) {
+ LLVMSetVisibility(other_global, LLVMHiddenVisibility);
+ }
}
} else if (ec.e->kind == Entity_Procedure) {
other_global = LLVMGetNamedFunction(ec.other_module->mod, ec.cname);
if (other_global) {
LLVMSetLinkage(other_global, LLVMWeakAnyLinkage);
+ if (!ec.e->Procedure.is_export && !ec.e->Procedure.is_foreign) {
+ LLVMSetVisibility(other_global, LLVMHiddenVisibility);
+ }
}
}
}
@@ -227,6 +233,16 @@ gb_internal lbContextData *lb_push_context_onto_stack(lbProcedure *p, lbAddr ctx
}
+gb_internal String lb_internal_gen_name_from_type(char const *prefix, Type *type) {
+ gbString str = gb_string_make(permanent_allocator(), prefix);
+ u64 hash = type_hash_canonical_type(type);
+ str = gb_string_appendc(str, "-");
+ str = gb_string_append_fmt(str, "%llu", cast(unsigned long long)hash);
+ String proc_name = make_string(cast(u8 const *)str, gb_string_length(str));
+ return proc_name;
+}
+
+
gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type) {
type = base_type(type);
GB_ASSERT(is_type_comparable(type));
@@ -234,7 +250,8 @@ gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type) {
Type *pt = alloc_type_pointer(type);
LLVMTypeRef ptr_type = lb_type(m, pt);
- lbProcedure **found = map_get(&m->equal_procs, type);
+ String proc_name = lb_internal_gen_name_from_type("__$equal", type);
+ lbProcedure **found = string_map_get(&m->gen_procs, proc_name);
lbProcedure *compare_proc = nullptr;
if (found) {
compare_proc = *found;
@@ -242,17 +259,12 @@ gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type) {
return {compare_proc->value, compare_proc->type};
}
- static std::atomic proc_index;
-
- char buf[32] = {};
- isize n = gb_snprintf(buf, 32, "__$equal%u", 1+proc_index.fetch_add(1));
- char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
- String proc_name = make_string_c(str);
lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_equal_proc);
- map_set(&m->equal_procs, type, p);
+ string_map_set(&m->gen_procs, proc_name, p);
lb_begin_procedure_body(p);
+ LLVMSetLinkage(p->value, LLVMInternalLinkage);
// lb_add_attribute_to_proc(m, p->value, "readonly");
lb_add_attribute_to_proc(m, p->value, "nounwind");
@@ -404,24 +416,19 @@ gb_internal lbValue lb_hasher_proc_for_type(lbModule *m, Type *type) {
Type *pt = alloc_type_pointer(type);
- lbProcedure **found = map_get(&m->hasher_procs, type);
+ String proc_name = lb_internal_gen_name_from_type("__$hasher", type);
+ lbProcedure **found = string_map_get(&m->gen_procs, proc_name);
if (found) {
GB_ASSERT(*found != nullptr);
return {(*found)->value, (*found)->type};
}
- static std::atomic proc_index;
-
- char buf[32] = {};
- isize n = gb_snprintf(buf, 32, "__$hasher%u", 1+proc_index.fetch_add(1));
- char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
- String proc_name = make_string_c(str);
-
lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_hasher_proc);
- map_set(&m->hasher_procs, type, p);
+ string_map_set(&m->gen_procs, proc_name, p);
lb_begin_procedure_body(p);
defer (lb_end_procedure_body(p));
+ LLVMSetLinkage(p->value, LLVMInternalLinkage);
// lb_add_attribute_to_proc(m, p->value, "readonly");
lb_add_attribute_to_proc(m, p->value, "nounwind");
@@ -556,6 +563,53 @@ gb_internal lbValue lb_hasher_proc_for_type(lbModule *m, Type *type) {
lbValue res = lb_emit_runtime_call(p, "default_hasher_string", args);
lb_add_callsite_force_inline(p, res);
LLVMBuildRet(p->builder, res.value);
+ } else if (is_type_float(type)) {
+ lbValue ptr = lb_emit_conv(p, data, pt);
+ lbValue v = lb_emit_load(p, ptr);
+ v = lb_emit_conv(p, v, t_f64);
+
+ auto args = array_make(temporary_allocator(), 2);
+ args[0] = v;
+ args[1] = seed;
+ lbValue res = lb_emit_runtime_call(p, "default_hasher_f64", args);
+ lb_add_callsite_force_inline(p, res);
+ LLVMBuildRet(p->builder, res.value);
+ } else if (is_type_complex(type)) {
+ lbValue ptr = lb_emit_conv(p, data, pt);
+ lbValue xp = lb_emit_struct_ep(p, ptr, 0);
+ lbValue yp = lb_emit_struct_ep(p, ptr, 1);
+
+ lbValue x = lb_emit_conv(p, lb_emit_load(p, xp), t_f64);
+ lbValue y = lb_emit_conv(p, lb_emit_load(p, yp), t_f64);
+
+ auto args = array_make(temporary_allocator(), 3);
+ args[0] = x;
+ args[1] = y;
+ args[2] = seed;
+ lbValue res = lb_emit_runtime_call(p, "default_hasher_complex128", args);
+ lb_add_callsite_force_inline(p, res);
+ LLVMBuildRet(p->builder, res.value);
+ } else if (is_type_quaternion(type)) {
+ lbValue ptr = lb_emit_conv(p, data, pt);
+ lbValue xp = lb_emit_struct_ep(p, ptr, 0);
+ lbValue yp = lb_emit_struct_ep(p, ptr, 1);
+ lbValue zp = lb_emit_struct_ep(p, ptr, 2);
+ lbValue wp = lb_emit_struct_ep(p, ptr, 3);
+
+ lbValue x = lb_emit_conv(p, lb_emit_load(p, xp), t_f64);
+ lbValue y = lb_emit_conv(p, lb_emit_load(p, yp), t_f64);
+ lbValue z = lb_emit_conv(p, lb_emit_load(p, zp), t_f64);
+ lbValue w = lb_emit_conv(p, lb_emit_load(p, wp), t_f64);
+
+ auto args = array_make(temporary_allocator(), 5);
+ args[0] = x;
+ args[1] = y;
+ args[2] = z;
+ args[3] = w;
+ args[4] = seed;
+ lbValue res = lb_emit_runtime_call(p, "default_hasher_quaternion256", args);
+ lb_add_callsite_force_inline(p, res);
+ LLVMBuildRet(p->builder, res.value);
} else {
GB_PANIC("Unhandled type for hasher: %s", type_to_string(type));
}
@@ -571,21 +625,15 @@ gb_internal lbValue lb_map_get_proc_for_type(lbModule *m, Type *type) {
type = base_type(type);
GB_ASSERT(type->kind == Type_Map);
-
- lbProcedure **found = map_get(&m->map_get_procs, type);
+ String proc_name = lb_internal_gen_name_from_type("__$map_get", type);
+ lbProcedure **found = string_map_get(&m->gen_procs, proc_name);
if (found) {
GB_ASSERT(*found != nullptr);
return {(*found)->value, (*found)->type};
}
- static std::atomic proc_index;
-
- char buf[32] = {};
- isize n = gb_snprintf(buf, 32, "__$map_get-%u", 1+proc_index.fetch_add(1));
- char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
- String proc_name = make_string_c(str);
lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_map_get_proc);
- map_set(&m->map_get_procs, type, p);
+ string_map_set(&m->gen_procs, proc_name, p);
lb_begin_procedure_body(p);
defer (lb_end_procedure_body(p));
@@ -752,21 +800,15 @@ gb_internal lbValue lb_map_set_proc_for_type(lbModule *m, Type *type) {
type = base_type(type);
GB_ASSERT(type->kind == Type_Map);
-
- lbProcedure **found = map_get(&m->map_set_procs, type);
+ String proc_name = lb_internal_gen_name_from_type("__$map_set", type);
+ lbProcedure **found = string_map_get(&m->gen_procs, proc_name);
if (found) {
GB_ASSERT(*found != nullptr);
return {(*found)->value, (*found)->type};
}
- static std::atomic proc_index;
-
- char buf[32] = {};
- isize n = gb_snprintf(buf, 32, "__$map_set-%u", 1+proc_index.fetch_add(1));
- char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
- String proc_name = make_string_c(str);
lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_map_set_proc);
- map_set(&m->map_set_procs, type, p);
+ string_map_set(&m->gen_procs, proc_name, p);
lb_begin_procedure_body(p);
defer (lb_end_procedure_body(p));
@@ -911,7 +953,7 @@ gb_internal lbValue lb_gen_map_cell_info_ptr(lbModule *m, Type *type) {
LLVMValueRef llvm_res = llvm_const_named_struct(m, t_map_cell_info, const_values, gb_count_of(const_values));
lbValue res = {llvm_res, t_map_cell_info};
- lbAddr addr = lb_add_global_generated(m, t_map_cell_info, res, nullptr);
+ lbAddr addr = lb_add_global_generated_with_name(m, t_map_cell_info, res, lb_internal_gen_name_from_type("ggv$map_cell_info", type));
lb_make_global_private_const(addr);
map_set(&m->map_cell_info_map, type, addr);
@@ -942,7 +984,7 @@ gb_internal lbValue lb_gen_map_info_ptr(lbModule *m, Type *map_type) {
LLVMValueRef llvm_res = llvm_const_named_struct(m, t_map_info, const_values, gb_count_of(const_values));
lbValue res = {llvm_res, t_map_info};
- lbAddr addr = lb_add_global_generated(m, t_map_info, res, nullptr);
+ lbAddr addr = lb_add_global_generated_with_name(m, t_map_info, res, lb_internal_gen_name_from_type("ggv$map_info", map_type));
lb_make_global_private_const(addr);
map_set(&m->map_info_map, map_type, addr);
@@ -1096,8 +1138,6 @@ gb_internal void lb_internal_dynamic_map_set(lbProcedure *p, lbValue const &map_
}
gb_internal lbValue lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_ptr, isize const capacity, TokenPos const &pos) {
- GB_ASSERT(!build_context.no_dynamic_literals);
-
TEMPORARY_ALLOCATOR_GUARD();
String proc_name = {};
@@ -1133,30 +1173,51 @@ gb_internal lbProcedure *lb_create_objc_names(lbModule *main_module) {
return p;
}
-gb_internal void lb_finalize_objc_names(lbProcedure *p) {
+gb_internal void lb_finalize_objc_names(lbGenerator *gen, lbProcedure *p) {
if (p == nullptr) {
return;
}
lbModule *m = p->module;
+ GB_ASSERT(m == &p->module->gen->default_module);
TEMPORARY_ALLOCATOR_GUARD();
+ StringSet handled = {};
+ string_set_init(&handled);
+ defer (string_set_destroy(&handled));
+
auto args = array_make(temporary_allocator(), 1);
LLVMSetLinkage(p->value, LLVMInternalLinkage);
lb_begin_procedure_body(p);
- for (auto const &entry : m->objc_classes) {
- String name = entry.key;
- args[0] = lb_const_value(m, t_cstring, exact_value_string(name));
- lbValue ptr = lb_emit_runtime_call(p, "objc_lookUpClass", args);
- lb_addr_store(p, entry.value, ptr);
+
+ auto register_thing = [&handled, &m, &args](lbProcedure *p, lbObjCGlobal const &g, char const *call) {
+ if (!string_set_update(&handled, g.name)) {
+ lbAddr addr = {};
+ lbValue *found = string_map_get(&m->members, g.global_name);
+ if (found) {
+ addr = lb_addr(*found);
+ } else {
+ lbValue v = {};
+ LLVMTypeRef t = lb_type(m, g.type);
+ v.value = LLVMAddGlobal(m->mod, t, g.global_name);
+ v.type = alloc_type_pointer(g.type);
+ addr = lb_addr(v);
+ LLVMSetInitializer(v.value, LLVMConstNull(t));
+ }
+
+ args[0] = lb_const_value(m, t_cstring, exact_value_string(g.name));
+ lbValue ptr = lb_emit_runtime_call(p, call, args);
+ lb_addr_store(p, addr, ptr);
+ }
+ };
+
+ for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_classes, &g); /**/) {
+ register_thing(p, g, "objc_lookUpClass");
}
- for (auto const &entry : m->objc_selectors) {
- String name = entry.key;
- args[0] = lb_const_value(m, t_cstring, exact_value_string(name));
- lbValue ptr = lb_emit_runtime_call(p, "sel_registerName", args);
- lb_addr_store(p, entry.value, ptr);
+ for (lbObjCGlobal g = {}; mpsc_dequeue(&gen->objc_selectors, &g); /**/) {
+ register_thing(p, g, "sel_registerName");
}
lb_end_procedure_body(p);
@@ -1280,7 +1341,10 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
if (is_type_any(t)) {
// NOTE(bill): Edge case for 'any' type
Type *var_type = default_type(var.init.type);
- lbAddr g = lb_add_global_generated(main_module, var_type, var.init);
+ gbString var_name = gb_string_make(permanent_allocator(), "__$global_any::");
+ gbString e_str = string_canonical_entity_name(temporary_allocator(), e);
+ var_name = gb_string_append_length(var_name, e_str, gb_strlen(e_str));
+ lbAddr g = lb_add_global_generated_with_name(main_module, var_type, var.init, make_string_c(var_name));
lb_addr_store(p, g, var.init);
lbValue gp = lb_addr_get_ptr(p, g);
@@ -1559,21 +1623,13 @@ gb_internal WORKER_TASK_PROC(lb_llvm_function_pass_per_module) {
}
}
- for (auto const &entry : m->equal_procs) {
+ for (auto const &entry : m->gen_procs) {
lbProcedure *p = entry.value;
- lb_llvm_function_pass_per_function_internal(m, p);
- }
- for (auto const &entry : m->hasher_procs) {
- lbProcedure *p = entry.value;
- lb_llvm_function_pass_per_function_internal(m, p);
- }
- for (auto const &entry : m->map_get_procs) {
- lbProcedure *p = entry.value;
- lb_llvm_function_pass_per_function_internal(m, p, lbFunctionPassManager_none);
- }
- for (auto const &entry : m->map_set_procs) {
- lbProcedure *p = entry.value;
- lb_llvm_function_pass_per_function_internal(m, p, lbFunctionPassManager_none);
+ if (string_starts_with(p->name, str_lit("__$map"))) {
+ lb_llvm_function_pass_per_function_internal(m, p, lbFunctionPassManager_none);
+ } else {
+ lb_llvm_function_pass_per_function_internal(m, p);
+ }
}
return 0;
@@ -1603,797 +1659,7 @@ gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) {
LLVMPassBuilderOptionsRef pb_options = LLVMCreatePassBuilderOptions();
defer (LLVMDisposePassBuilderOptions(pb_options));
- switch (build_context.optimization_level) {
- case -1:
- array_add(&passes, "function(annotation-remarks)");
- break;
- case 0:
- array_add(&passes, "always-inline");
- array_add(&passes, "function(annotation-remarks)");
- break;
- case 1:
-// default
-// Passes removed: coro, openmp, sroa
-#if LLVM_VERSION_MAJOR == 17
- array_add(&passes, u8R"(
-annotation2metadata,
-forceattrs,
-inferattrs,
-function(
- lower-expect,
- simplifycfg,
- early-cse<>
-),
-ipsccp,
-called-value-propagation,
-globalopt,
-function(
- mem2reg,
- instcombine,
- simplifycfg
-),
-require,
-function(
- invalidate
-),
-require,
-cgscc(
- devirt<4>(
- inline,
- inline,
- function-attrs,
- function(
- early-cse,
- speculative-execution,
- jump-threading,
- correlated-propagation,
- simplifycfg,
- instcombine,
- aggressive-instcombine,
- constraint-elimination,
- tailcallelim,
- simplifycfg,
- reassociate,
- loop-mssa(
- loop-instsimplify,
- loop-simplifycfg,
- licm,
- loop-rotate,
- licm,
- simple-loop-unswitch
- ),
- simplifycfg,
- instcombine,
- loop(
- loop-idiom,
- indvars,
- loop-deletion,
- loop-unroll-full
- ),
- vector-combine,
- mldst-motion,
- gvn<>,
- sccp,
- bdce,
- instcombine,
- jump-threading,
- correlated-propagation,
- adce,
- memcpyopt,
- dse,
- move-auto-init,
- loop-mssa(
- licm
- ),
- simplifycfg,
- instcombine
- ),
- function-attrs,
- function(
- require
- )
- )
-),
-deadargelim,
-globalopt,
-globaldce,
-elim-avail-extern,
-rpo-function-attrs,
-recompute-globalsaa,
-function(
- float2int,
- lower-constant-intrinsics,
- loop(
- loop-rotate,
- loop-deletion
- ),
- loop-distribute,
- inject-tli-mappings,
- loop-vectorize,
- loop-load-elim,
- instcombine,
- simplifycfg,
- slp-vectorizer,
- vector-combine,
- instcombine,
- loop-unroll,
- transform-warning,
- instcombine,
- loop-mssa(
- licm
- ),
- alignment-from-assumptions,
- loop-sink,
- instsimplify,
- div-rem-pairs,
- tailcallelim,
- simplifycfg
-),
-globaldce,
-constmerge,
-cg-profile,
-rel-lookup-table-converter,
-function(
- annotation-remarks
-),
-verify
-)");
-#else
- array_add(&passes, u8R"(
-annotation2metadata,
-forceattrs,
-inferattrs,
-function(
- lower-expect,
- simplifycfg,
- sroa,
- early-cse<>
-),
-ipsccp,
-called-value-propagation,
-globalopt,
-function(
- mem2reg,
- instcombine,
- simplifycfg
-),
-always-inline,
-require,
-function(
- invalidate
-),
-require,
-cgscc(
- devirt<4>(
- inline,
- function-attrs,
- function(
- sroa,
- early-cse,
- speculative-execution,
- jump-threading,
- correlated-propagation,
- simplifycfg,
- instcombine,
- aggressive-instcombine,
- tailcallelim,
- simplifycfg,
- reassociate,
- constraint-elimination,
- loop-mssa(
- loop-instsimplify,
- loop-simplifycfg,
- licm,
- loop-rotate,
- licm,
- simple-loop-unswitch
- ),
- simplifycfg,
- instcombine,
- loop(
- loop-idiom,
- indvars,
- loop-deletion,
- loop-unroll-full
- ),
- sroa,
- vector-combine,
- mldst-motion,
- gvn<>,
- sccp,
- bdce,
- instcombine,
- jump-threading,
- correlated-propagation,
- adce,
- memcpyopt,
- dse,
- move-auto-init,
- loop-mssa(
- licm
- ),
- simplifycfg,
- instcombine
- ),
- function-attrs,
- function(
- require
- )
- )
-),
-deadargelim,
-globalopt,
-globaldce,
-elim-avail-extern,
-rpo-function-attrs,
-recompute-globalsaa,
-function(
- float2int,
- lower-constant-intrinsics,
- loop(
- loop-rotate,
- loop-deletion
- ),
- loop-distribute,
- inject-tli-mappings,
- loop-vectorize,
- infer-alignment,
- loop-load-elim,
- instcombine,
- simplifycfg,
- slp-vectorizer,
- vector-combine,
- instcombine,
- loop-unroll,
- transform-warning,
- sroa,
- infer-alignment,
- instcombine,
- loop-mssa(
- licm
- ),
- alignment-from-assumptions,
- loop-sink,
- instsimplify,
- div-rem-pairs,
- tailcallelim,
- simplifycfg
-),
-globaldce,
-constmerge,
-cg-profile,
-rel-lookup-table-converter,
-function(
- annotation-remarks
-),
-verify
-)");
-#endif
- break;
-// default
-// Passes removed: coro, openmp, sroa
- case 2:
-#if LLVM_VERSION_MAJOR == 17
- array_add(&passes, u8R"(
-annotation2metadata,
-forceattrs,
-inferattrs,
-function(
- lower-expect,
- simplifycfg,
- early-cse<>
-),
-ipsccp,
-called-value-propagation,
-globalopt,
-function(
- mem2reg,
- instcombine,
- simplifycfg
-),
-require,
-function(
- invalidate
-),
-require,
-cgscc(
- devirt<4>(
- inline,
- inline,
- function-attrs,
- function(
- early-cse,
- speculative-execution,
- jump-threading,
- correlated-propagation,
- simplifycfg,
- instcombine,
- aggressive-instcombine,
- constraint-elimination,
- libcalls-shrinkwrap,
- tailcallelim,
- simplifycfg,
- reassociate,
- loop-mssa(
- loop-instsimplify,
- loop-simplifycfg,
- licm,
- loop-rotate,
- licm,
- simple-loop-unswitch
- ),
- simplifycfg,
- instcombine,
- loop(
- loop-idiom,
- indvars,
- loop-deletion,
- loop-unroll-full
- ),
- vector-combine,
- mldst-motion,
- gvn<>,
- sccp,
- bdce,
- instcombine,
- jump-threading,
- correlated-propagation,
- adce,
- memcpyopt,
- dse,
- move-auto-init,
- loop-mssa(
- licm
- ),
- simplifycfg,
- instcombine
- ),
- function-attrs,
- function(
- require
- )
- )
-),
-deadargelim,
-globalopt,
-globaldce,
-elim-avail-extern,
-rpo-function-attrs,
-recompute-globalsaa,
-function(
- float2int,
- lower-constant-intrinsics,
- loop(
- loop-rotate,
- loop-deletion
- ),
- loop-distribute,
- inject-tli-mappings,
- loop-vectorize,
- loop-load-elim,
- instcombine,
- simplifycfg,
- slp-vectorizer,
- vector-combine,
- instcombine,
- loop-unroll,
- transform-warning,
- instcombine,
- loop-mssa(
- licm
- ),
- alignment-from-assumptions,
- loop-sink,
- instsimplify,
- div-rem-pairs,
- tailcallelim,
- simplifycfg
-),
-globaldce,
-constmerge,
-cg-profile,
-rel-lookup-table-converter,
-function(
- annotation-remarks
-),
-verify
-)");
-#else
- array_add(&passes, u8R"(
-annotation2metadata,
-forceattrs,
-inferattrs,
-function(
- lower-expect,
- simplifycfg,
- sroa,
- early-cse<>
-),
-ipsccp,
-called-value-propagation,
-globalopt,
-function(
- mem2reg,
- instcombine,
- simplifycfg
-),
-always-inline,
-require,
-function(
- invalidate
-),
-require,
-cgscc(
- devirt<4>(
- inline,
- function-attrs,
- function(
- sroa,
- early-cse,
- speculative-execution,
- jump-threading,
- correlated-propagation,
- simplifycfg,
- instcombine,
- aggressive-instcombine,
- libcalls-shrinkwrap,
- tailcallelim,
- simplifycfg,
- reassociate,
- constraint-elimination,
- loop-mssa(
- loop-instsimplify,
- loop-simplifycfg,
- licm,
- loop-rotate,
- licm,
- simple-loop-unswitch
- ),
- simplifycfg,
- instcombine,
- loop(
- loop-idiom,
- indvars,
- loop-deletion,
- loop-unroll-full
- ),
- sroa,
- vector-combine,
- mldst-motion,
- gvn<>,
- sccp,
- bdce,
- instcombine