mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-07 16:18:52 +00:00
Merge remote-tracking branch 'upstream/master' into sys-windows-2
# Conflicts: # core/sys/windows/kernel32.odin # core/sys/windows/types.odin # core/sys/windows/user32.odin # core/sys/windows/winerror.odin
This commit is contained in:
@@ -32,8 +32,8 @@ jobs:
|
|||||||
gmake -C vendor/stb/src
|
gmake -C vendor/stb/src
|
||||||
gmake -C vendor/cgltf/src
|
gmake -C vendor/cgltf/src
|
||||||
gmake -C vendor/miniaudio/src
|
gmake -C vendor/miniaudio/src
|
||||||
./odin check examples/all -vet -strict-style -target:netbsd_amd64
|
./odin check examples/all -vet -strict-style -disallow-do -target:netbsd_amd64
|
||||||
./odin check examples/all -vet -strict-style -target:netbsd_arm64
|
./odin check examples/all -vet -strict-style -disallow-do -target:netbsd_arm64
|
||||||
./odin test tests/core/normal.odin -file -all-packages -define:ODIN_TEST_FANCY=false
|
./odin test tests/core/normal.odin -file -all-packages -define:ODIN_TEST_FANCY=false
|
||||||
./odin test tests/core/speed.odin -file -all-packages -o:speed -define:ODIN_TEST_FANCY=false
|
./odin test tests/core/speed.odin -file -all-packages -o:speed -define:ODIN_TEST_FANCY=false
|
||||||
./odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false
|
./odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false
|
||||||
@@ -62,7 +62,7 @@ jobs:
|
|||||||
gmake -C vendor/stb/src
|
gmake -C vendor/stb/src
|
||||||
gmake -C vendor/cgltf/src
|
gmake -C vendor/cgltf/src
|
||||||
gmake -C vendor/miniaudio/src
|
gmake -C vendor/miniaudio/src
|
||||||
./odin check examples/all -vet -strict-style -target:freebsd_amd64
|
./odin check examples/all -vet -strict-style -disallow-do -target:freebsd_amd64
|
||||||
./odin test tests/core/normal.odin -file -all-packages -define:ODIN_TEST_FANCY=false
|
./odin test tests/core/normal.odin -file -all-packages -define:ODIN_TEST_FANCY=false
|
||||||
./odin test tests/core/speed.odin -file -all-packages -o:speed -define:ODIN_TEST_FANCY=false
|
./odin test tests/core/speed.odin -file -all-packages -o:speed -define:ODIN_TEST_FANCY=false
|
||||||
./odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false
|
./odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false
|
||||||
@@ -97,7 +97,7 @@ jobs:
|
|||||||
- name: Download LLVM (MacOS ARM)
|
- name: Download LLVM (MacOS ARM)
|
||||||
if: matrix.os == 'macos-14'
|
if: matrix.os == 'macos-14'
|
||||||
run: |
|
run: |
|
||||||
brew install llvm@17
|
brew install llvm@17 wasmtime
|
||||||
echo "/opt/homebrew/opt/llvm@17/bin" >> $GITHUB_PATH
|
echo "/opt/homebrew/opt/llvm@17/bin" >> $GITHUB_PATH
|
||||||
|
|
||||||
- name: Build Odin
|
- name: Build Odin
|
||||||
@@ -135,18 +135,24 @@ jobs:
|
|||||||
./run.sh
|
./run.sh
|
||||||
|
|
||||||
- name: Odin check examples/all for Linux i386
|
- name: Odin check examples/all for Linux i386
|
||||||
run: ./odin check examples/all -vet -strict-style -target:linux_i386
|
run: ./odin check examples/all -vet -strict-style -disallow-do -target:linux_i386
|
||||||
if: matrix.os == 'ubuntu-latest'
|
if: matrix.os == 'ubuntu-latest'
|
||||||
- name: Odin check examples/all for Linux arm64
|
- name: Odin check examples/all for Linux arm64
|
||||||
run: ./odin check examples/all -vet -strict-style -target:linux_arm64
|
run: ./odin check examples/all -vet -strict-style -disallow-do -target:linux_arm64
|
||||||
if: matrix.os == 'ubuntu-latest'
|
if: matrix.os == 'ubuntu-latest'
|
||||||
- name: Odin check examples/all for FreeBSD amd64
|
- name: Odin check examples/all for FreeBSD amd64
|
||||||
run: ./odin check examples/all -vet -strict-style -target:freebsd_amd64
|
run: ./odin check examples/all -vet -strict-style -disallow-do -target:freebsd_amd64
|
||||||
if: matrix.os == 'ubuntu-latest'
|
if: matrix.os == 'ubuntu-latest'
|
||||||
- name: Odin check examples/all for OpenBSD amd64
|
- name: Odin check examples/all for OpenBSD amd64
|
||||||
run: ./odin check examples/all -vet -strict-style -target:openbsd_amd64
|
run: ./odin check examples/all -vet -strict-style -disallow-do -target:openbsd_amd64
|
||||||
if: matrix.os == 'ubuntu-latest'
|
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
|
||||||
|
wasmtime ./demo.wasm
|
||||||
|
if: matrix.os == 'macos-14'
|
||||||
|
|
||||||
build_windows:
|
build_windows:
|
||||||
name: Windows Build, Check, and Test
|
name: Windows Build, Check, and Test
|
||||||
runs-on: windows-2022
|
runs-on: windows-2022
|
||||||
|
|||||||
@@ -50,8 +50,8 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
wget https://apt.llvm.org/llvm.sh
|
wget https://apt.llvm.org/llvm.sh
|
||||||
chmod +x llvm.sh
|
chmod +x llvm.sh
|
||||||
sudo ./llvm.sh 17
|
sudo ./llvm.sh 18
|
||||||
echo "/usr/lib/llvm-17/bin" >> $GITHUB_PATH
|
echo "/usr/lib/llvm-18/bin" >> $GITHUB_PATH
|
||||||
- name: build odin
|
- name: build odin
|
||||||
run: make nightly
|
run: make nightly
|
||||||
- name: Odin run
|
- name: Odin run
|
||||||
@@ -82,8 +82,8 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Download LLVM and setup PATH
|
- name: Download LLVM and setup PATH
|
||||||
run: |
|
run: |
|
||||||
brew install llvm@17 dylibbundler
|
brew install llvm@18 dylibbundler
|
||||||
echo "/usr/local/opt/llvm@17/bin" >> $GITHUB_PATH
|
echo "/usr/local/opt/llvm@18/bin" >> $GITHUB_PATH
|
||||||
- name: build odin
|
- name: build odin
|
||||||
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
|
# 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.
|
# not link with libunwind bundled with LLVM but link with libunwind on the system.
|
||||||
@@ -116,8 +116,8 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Download LLVM and setup PATH
|
- name: Download LLVM and setup PATH
|
||||||
run: |
|
run: |
|
||||||
brew install llvm@17 dylibbundler
|
brew install llvm@18 dylibbundler
|
||||||
echo "/opt/homebrew/opt/llvm@17/bin" >> $GITHUB_PATH
|
echo "/opt/homebrew/opt/llvm@18/bin" >> $GITHUB_PATH
|
||||||
- name: build odin
|
- name: build odin
|
||||||
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
|
# 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.
|
# not link with libunwind bundled with LLVM but link with libunwind on the system.
|
||||||
|
|||||||
+1
-1
@@ -303,7 +303,7 @@ bin/
|
|||||||
# - Linux/MacOS
|
# - Linux/MacOS
|
||||||
odin
|
odin
|
||||||
!odin/
|
!odin/
|
||||||
odin.dSYM
|
**/*.dSYM
|
||||||
*.bin
|
*.bin
|
||||||
demo.bin
|
demo.bin
|
||||||
libLLVM*.so*
|
libLLVM*.so*
|
||||||
|
|||||||
BIN
Binary file not shown.
@@ -73,6 +73,8 @@ expect :: proc(val, expected_val: T) -> T ---
|
|||||||
|
|
||||||
// Linux and Darwin Only
|
// Linux and Darwin Only
|
||||||
syscall :: proc(id: uintptr, args: ..uintptr) -> uintptr ---
|
syscall :: proc(id: uintptr, args: ..uintptr) -> uintptr ---
|
||||||
|
// FreeBSD, NetBSD, et cetera
|
||||||
|
syscall_bsd :: proc(id: uintptr, args: ..uintptr) -> (uintptr, bool) ---
|
||||||
|
|
||||||
|
|
||||||
// Atomics
|
// Atomics
|
||||||
@@ -193,6 +195,7 @@ type_proc_parameter_type :: proc($T: typeid, index: int) -> typeid where type_i
|
|||||||
type_proc_return_type :: proc($T: typeid, index: int) -> typeid where type_is_proc(T) ---
|
type_proc_return_type :: proc($T: typeid, index: int) -> typeid where type_is_proc(T) ---
|
||||||
|
|
||||||
type_struct_field_count :: proc($T: typeid) -> int where type_is_struct(T) ---
|
type_struct_field_count :: proc($T: typeid) -> int where type_is_struct(T) ---
|
||||||
|
type_struct_has_implicit_padding :: proc($T: typeid) -> bool where type_is_struct(T) ---
|
||||||
|
|
||||||
type_polymorphic_record_parameter_count :: proc($T: typeid) -> typeid ---
|
type_polymorphic_record_parameter_count :: proc($T: typeid) -> typeid ---
|
||||||
type_polymorphic_record_parameter_value :: proc($T: typeid, index: int) -> $V ---
|
type_polymorphic_record_parameter_value :: proc($T: typeid, index: int) -> $V ---
|
||||||
|
|||||||
@@ -299,6 +299,8 @@ when ODIN_OS == .Windows {
|
|||||||
Thread_Detach = 3,
|
Thread_Detach = 3,
|
||||||
}
|
}
|
||||||
dll_forward_reason: DLL_Forward_Reason
|
dll_forward_reason: DLL_Forward_Reason
|
||||||
|
|
||||||
|
dll_instance: rawptr
|
||||||
}
|
}
|
||||||
|
|
||||||
// IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it)
|
// IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it)
|
||||||
@@ -397,11 +399,34 @@ Logger :: struct {
|
|||||||
options: Logger_Options,
|
options: Logger_Options,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Random_Generator_Mode :: enum {
|
||||||
|
Read,
|
||||||
|
Reset,
|
||||||
|
Query_Info,
|
||||||
|
}
|
||||||
|
|
||||||
|
Random_Generator_Query_Info_Flag :: enum u32 {
|
||||||
|
Cryptographic,
|
||||||
|
Uniform,
|
||||||
|
External_Entropy,
|
||||||
|
Resettable,
|
||||||
|
}
|
||||||
|
Random_Generator_Query_Info :: distinct bit_set[Random_Generator_Query_Info_Flag; u32]
|
||||||
|
|
||||||
|
Random_Generator_Proc :: #type proc(data: rawptr, mode: Random_Generator_Mode, p: []byte)
|
||||||
|
|
||||||
|
Random_Generator :: struct {
|
||||||
|
procedure: Random_Generator_Proc,
|
||||||
|
data: rawptr,
|
||||||
|
}
|
||||||
|
|
||||||
Context :: struct {
|
Context :: struct {
|
||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
temp_allocator: Allocator,
|
temp_allocator: Allocator,
|
||||||
assertion_failure_proc: Assertion_Failure_Proc,
|
assertion_failure_proc: Assertion_Failure_Proc,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
|
random_generator: Random_Generator,
|
||||||
|
|
||||||
user_ptr: rawptr,
|
user_ptr: rawptr,
|
||||||
user_index: int,
|
user_index: int,
|
||||||
@@ -708,6 +733,9 @@ __init_context :: proc "contextless" (c: ^Context) {
|
|||||||
|
|
||||||
c.logger.procedure = default_logger_proc
|
c.logger.procedure = default_logger_proc
|
||||||
c.logger.data = nil
|
c.logger.data = nil
|
||||||
|
|
||||||
|
c.random_generator.procedure = default_random_generator_proc
|
||||||
|
c.random_generator.data = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code_Location) -> ! {
|
default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code_Location) -> ! {
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ copy :: proc{copy_slice, copy_from_string}
|
|||||||
// with the old value, and reducing the length of the dynamic array by 1.
|
// with the old value, and reducing the length of the dynamic array by 1.
|
||||||
//
|
//
|
||||||
// Note: This is an O(1) operation.
|
// Note: This is an O(1) operation.
|
||||||
// Note: If you the elements to remain in their order, use `ordered_remove`.
|
// Note: If you want the elements to remain in their order, use `ordered_remove`.
|
||||||
// Note: If the index is out of bounds, this procedure will panic.
|
// Note: If the index is out of bounds, this procedure will panic.
|
||||||
@builtin
|
@builtin
|
||||||
unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) #no_bounds_check {
|
unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) #no_bounds_check {
|
||||||
@@ -79,7 +79,7 @@ unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_loca
|
|||||||
// `ordered_remove` removed the element at the specified `index` whilst keeping the order of the other elements.
|
// `ordered_remove` removed the element at the specified `index` whilst keeping the order of the other elements.
|
||||||
//
|
//
|
||||||
// Note: This is an O(N) operation.
|
// Note: This is an O(N) operation.
|
||||||
// Note: If you the elements do not have to remain in their order, prefer `unordered_remove`.
|
// Note: If the elements do not have to remain in their order, prefer `unordered_remove`.
|
||||||
// Note: If the index is out of bounds, this procedure will panic.
|
// Note: If the index is out of bounds, this procedure will panic.
|
||||||
@builtin
|
@builtin
|
||||||
ordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) #no_bounds_check {
|
ordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) #no_bounds_check {
|
||||||
@@ -163,21 +163,43 @@ pop_front_safe :: proc "contextless" (array: ^$T/[dynamic]$E) -> (res: E, ok: bo
|
|||||||
|
|
||||||
// `clear` will set the length of a passed dynamic array or map to `0`
|
// `clear` will set the length of a passed dynamic array or map to `0`
|
||||||
@builtin
|
@builtin
|
||||||
clear :: proc{clear_dynamic_array, clear_map}
|
clear :: proc{
|
||||||
|
clear_dynamic_array,
|
||||||
|
clear_map,
|
||||||
|
|
||||||
|
clear_soa_dynamic_array,
|
||||||
|
}
|
||||||
|
|
||||||
// `reserve` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`).
|
// `reserve` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`).
|
||||||
@builtin
|
@builtin
|
||||||
reserve :: proc{reserve_dynamic_array, reserve_map}
|
reserve :: proc{
|
||||||
|
reserve_dynamic_array,
|
||||||
|
reserve_map,
|
||||||
|
|
||||||
|
reserve_soa,
|
||||||
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
non_zero_reserve :: proc{non_zero_reserve_dynamic_array}
|
non_zero_reserve :: proc{
|
||||||
|
non_zero_reserve_dynamic_array,
|
||||||
|
|
||||||
|
non_zero_reserve_soa,
|
||||||
|
}
|
||||||
|
|
||||||
// `resize` will try to resize memory of a passed dynamic array to the requested element count (setting the `len`, and possibly `cap`).
|
// `resize` will try to resize memory of a passed dynamic array to the requested element count (setting the `len`, and possibly `cap`).
|
||||||
@builtin
|
@builtin
|
||||||
resize :: proc{resize_dynamic_array}
|
resize :: proc{
|
||||||
|
resize_dynamic_array,
|
||||||
|
|
||||||
|
resize_soa,
|
||||||
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
non_zero_resize :: proc{non_zero_resize_dynamic_array}
|
non_zero_resize :: proc{
|
||||||
|
non_zero_resize_dynamic_array,
|
||||||
|
|
||||||
|
non_zero_resize_soa,
|
||||||
|
}
|
||||||
|
|
||||||
// Shrinks the capacity of a dynamic array or map down to the current length, or the given capacity.
|
// Shrinks the capacity of a dynamic array or map down to the current length, or the given capacity.
|
||||||
@builtin
|
@builtin
|
||||||
@@ -268,7 +290,7 @@ new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_locat
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFAULT_RESERVE_CAPACITY :: 16
|
DEFAULT_DYNAMIC_ARRAY_CAPACITY :: 8
|
||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_allocator_error {
|
make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_allocator_error {
|
||||||
@@ -295,7 +317,7 @@ make_slice :: proc($T: typeid/[]$E, #any_int len: int, allocator := context.allo
|
|||||||
// Note: Prefer using the procedure group `make`.
|
// Note: Prefer using the procedure group `make`.
|
||||||
@(builtin, require_results)
|
@(builtin, require_results)
|
||||||
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_allocator_error {
|
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_allocator_error {
|
||||||
return make_dynamic_array_len_cap(T, 0, DEFAULT_RESERVE_CAPACITY, allocator, loc)
|
return make_dynamic_array_len_cap(T, 0, 0, allocator, loc)
|
||||||
}
|
}
|
||||||
// `make_dynamic_array_len` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value.
|
// `make_dynamic_array_len` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value.
|
||||||
// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
|
// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
|
||||||
@@ -364,6 +386,11 @@ make :: proc{
|
|||||||
make_dynamic_array_len_cap,
|
make_dynamic_array_len_cap,
|
||||||
make_map,
|
make_map,
|
||||||
make_multi_pointer,
|
make_multi_pointer,
|
||||||
|
|
||||||
|
make_soa_slice,
|
||||||
|
make_soa_dynamic_array,
|
||||||
|
make_soa_dynamic_array_len,
|
||||||
|
make_soa_dynamic_array_len_cap,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -423,7 +450,8 @@ _append_elem :: #force_inline proc(array: ^$T/[dynamic]$E, arg: E, should_zero:
|
|||||||
return 1, nil
|
return 1, nil
|
||||||
} else {
|
} else {
|
||||||
if cap(array) < len(array)+1 {
|
if cap(array) < len(array)+1 {
|
||||||
cap := 2 * cap(array) + max(8, 1)
|
// Same behavior as _append_elems but there's only one arg, so we always just add DEFAULT_DYNAMIC_ARRAY_CAPACITY.
|
||||||
|
cap := 2 * cap(array) + DEFAULT_DYNAMIC_ARRAY_CAPACITY
|
||||||
|
|
||||||
// do not 'or_return' here as it could be a partial success
|
// do not 'or_return' here as it could be a partial success
|
||||||
if should_zero {
|
if should_zero {
|
||||||
@@ -472,7 +500,7 @@ _append_elems :: #force_inline proc(array: ^$T/[dynamic]$E, should_zero: bool, l
|
|||||||
return arg_len, nil
|
return arg_len, nil
|
||||||
} else {
|
} else {
|
||||||
if cap(array) < len(array)+arg_len {
|
if cap(array) < len(array)+arg_len {
|
||||||
cap := 2 * cap(array) + max(8, arg_len)
|
cap := 2 * cap(array) + max(DEFAULT_DYNAMIC_ARRAY_CAPACITY, arg_len)
|
||||||
|
|
||||||
// do not 'or_return' here as it could be a partial success
|
// do not 'or_return' here as it could be a partial success
|
||||||
if should_zero {
|
if should_zero {
|
||||||
@@ -540,8 +568,23 @@ append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The append built-in procedure appends elements to the end of a dynamic array
|
// The append built-in procedure appends elements to the end of a dynamic array
|
||||||
@builtin append :: proc{append_elem, append_elems, append_elem_string}
|
@builtin append :: proc{
|
||||||
@builtin non_zero_append :: proc{non_zero_append_elem, non_zero_append_elems, non_zero_append_elem_string}
|
append_elem,
|
||||||
|
append_elems,
|
||||||
|
append_elem_string,
|
||||||
|
|
||||||
|
append_soa_elem,
|
||||||
|
append_soa_elems,
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin non_zero_append :: proc{
|
||||||
|
non_zero_append_elem,
|
||||||
|
non_zero_append_elems,
|
||||||
|
non_zero_append_elem_string,
|
||||||
|
|
||||||
|
non_zero_append_soa_elem,
|
||||||
|
non_zero_append_soa_elems,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ raw_soa_footer_slice :: proc(array: ^$T/#soa[]$E) -> (footer: ^Raw_SOA_Footer_Sl
|
|||||||
if array == nil {
|
if array == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
field_count := uintptr(intrinsics.type_struct_field_count(E))
|
field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E))
|
||||||
footer = (^Raw_SOA_Footer_Slice)(uintptr(array) + field_count*size_of(rawptr))
|
footer = (^Raw_SOA_Footer_Slice)(uintptr(array) + field_count*size_of(rawptr))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -64,12 +64,7 @@ raw_soa_footer_dynamic_array :: proc(array: ^$T/#soa[dynamic]$E) -> (footer: ^Ra
|
|||||||
if array == nil {
|
if array == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
field_count: uintptr
|
field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E))
|
||||||
when intrinsics.type_is_array(E) {
|
|
||||||
field_count = len(E)
|
|
||||||
} else {
|
|
||||||
field_count = uintptr(intrinsics.type_struct_field_count(E))
|
|
||||||
}
|
|
||||||
footer = (^Raw_SOA_Footer_Dynamic_Array)(uintptr(array) + field_count*size_of(rawptr))
|
footer = (^Raw_SOA_Footer_Dynamic_Array)(uintptr(array) + field_count*size_of(rawptr))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -98,7 +93,7 @@ make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, alloc
|
|||||||
ti = type_info_base(ti)
|
ti = type_info_base(ti)
|
||||||
si := &ti.variant.(Type_Info_Struct)
|
si := &ti.variant.(Type_Info_Struct)
|
||||||
|
|
||||||
field_count := uintptr(intrinsics.type_struct_field_count(E))
|
field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E))
|
||||||
|
|
||||||
total_size := 0
|
total_size := 0
|
||||||
for i in 0..<field_count {
|
for i in 0..<field_count {
|
||||||
@@ -147,7 +142,7 @@ make_soa_slice :: proc($T: typeid/#soa[]$E, length: int, allocator := context.al
|
|||||||
@(builtin, require_results)
|
@(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 {
|
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
|
context.allocator = allocator
|
||||||
reserve_soa(&array, DEFAULT_RESERVE_CAPACITY, loc) or_return
|
reserve_soa(&array, 0, loc) or_return
|
||||||
return array, nil
|
return array, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,8 +182,28 @@ resize_soa :: proc(array: ^$T/#soa[dynamic]$E, length: int, loc := #caller_locat
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
non_zero_resize_soa :: proc(array: ^$T/#soa[dynamic]$E, length: int, loc := #caller_location) -> Allocator_Error {
|
||||||
|
if array == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
non_zero_reserve_soa(array, length, loc) or_return
|
||||||
|
footer := raw_soa_footer(array)
|
||||||
|
footer.len = length
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error {
|
reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error {
|
||||||
|
return _reserve_soa(array, capacity, true, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
non_zero_reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error {
|
||||||
|
return _reserve_soa(array, capacity, false, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
_reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, zero_memory: bool, loc := #caller_location) -> Allocator_Error {
|
||||||
if array == nil {
|
if array == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -213,12 +228,7 @@ reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_lo
|
|||||||
ti = type_info_base(ti)
|
ti = type_info_base(ti)
|
||||||
si := &ti.variant.(Type_Info_Struct)
|
si := &ti.variant.(Type_Info_Struct)
|
||||||
|
|
||||||
field_count: uintptr
|
field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E))
|
||||||
when intrinsics.type_is_array(E) {
|
|
||||||
field_count = len(E)
|
|
||||||
} else {
|
|
||||||
field_count = uintptr(intrinsics.type_struct_field_count(E))
|
|
||||||
}
|
|
||||||
assert(footer.cap == old_cap)
|
assert(footer.cap == old_cap)
|
||||||
|
|
||||||
old_size := 0
|
old_size := 0
|
||||||
@@ -238,7 +248,7 @@ reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_lo
|
|||||||
old_data := (^rawptr)(array)^
|
old_data := (^rawptr)(array)^
|
||||||
|
|
||||||
new_bytes := array.allocator.procedure(
|
new_bytes := array.allocator.procedure(
|
||||||
array.allocator.data, .Alloc, new_size, max_align,
|
array.allocator.data, .Alloc if zero_memory else .Alloc_Non_Zeroed, new_size, max_align,
|
||||||
nil, old_size, loc,
|
nil, old_size, loc,
|
||||||
) or_return
|
) or_return
|
||||||
new_data := raw_data(new_bytes)
|
new_data := raw_data(new_bytes)
|
||||||
@@ -273,15 +283,26 @@ reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_lo
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
return _append_soa_elem(array, true, arg, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
non_zero_append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
return _append_soa_elem(array, false, arg, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
_append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, zero_memory: bool, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
if array == nil {
|
if array == nil {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if cap(array) <= len(array) + 1 {
|
if cap(array) <= len(array) + 1 {
|
||||||
cap := 2 * cap(array) + 8
|
// Same behavior as append_soa_elems but there's only one arg, so we always just add DEFAULT_DYNAMIC_ARRAY_CAPACITY.
|
||||||
err = reserve_soa(array, cap, loc) // do not 'or_return' here as it could be a partial success
|
cap := 2 * cap(array) + DEFAULT_DYNAMIC_ARRAY_CAPACITY
|
||||||
|
err = _reserve_soa(array, cap, zero_memory, loc) // do not 'or_return' here as it could be a partial success
|
||||||
}
|
}
|
||||||
|
|
||||||
footer := raw_soa_footer(array)
|
footer := raw_soa_footer(array)
|
||||||
@@ -290,12 +311,7 @@ append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, arg: E, loc := #caller_locat
|
|||||||
ti := type_info_of(T)
|
ti := type_info_of(T)
|
||||||
ti = type_info_base(ti)
|
ti = type_info_base(ti)
|
||||||
si := &ti.variant.(Type_Info_Struct)
|
si := &ti.variant.(Type_Info_Struct)
|
||||||
field_count: uintptr
|
field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E))
|
||||||
when intrinsics.type_is_array(E) {
|
|
||||||
field_count = len(E)
|
|
||||||
} else {
|
|
||||||
field_count = uintptr(intrinsics.type_struct_field_count(E))
|
|
||||||
}
|
|
||||||
|
|
||||||
data := (^rawptr)(array)^
|
data := (^rawptr)(array)^
|
||||||
|
|
||||||
@@ -326,7 +342,17 @@ append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, arg: E, loc := #caller_locat
|
|||||||
}
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
return _append_soa_elems(array, true, args=args, loc=loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
non_zero_append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
return _append_soa_elems(array, false, args=args, loc=loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, zero_memory: bool, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
if array == nil {
|
if array == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -337,8 +363,8 @@ append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_l
|
|||||||
}
|
}
|
||||||
|
|
||||||
if cap(array) <= len(array)+arg_len {
|
if cap(array) <= len(array)+arg_len {
|
||||||
cap := 2 * cap(array) + max(8, arg_len)
|
cap := 2 * cap(array) + max(DEFAULT_DYNAMIC_ARRAY_CAPACITY, arg_len)
|
||||||
err = reserve_soa(array, cap, loc) // do not 'or_return' here as it could be a partial success
|
err = _reserve_soa(array, cap, zero_memory, loc) // do not 'or_return' here as it could be a partial success
|
||||||
}
|
}
|
||||||
arg_len = min(cap(array)-len(array), arg_len)
|
arg_len = min(cap(array)-len(array), arg_len)
|
||||||
|
|
||||||
@@ -347,7 +373,7 @@ append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_l
|
|||||||
ti := type_info_of(typeid_of(T))
|
ti := type_info_of(typeid_of(T))
|
||||||
ti = type_info_base(ti)
|
ti = type_info_base(ti)
|
||||||
si := &ti.variant.(Type_Info_Struct)
|
si := &ti.variant.(Type_Info_Struct)
|
||||||
field_count := uintptr(intrinsics.type_struct_field_count(E))
|
field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E))
|
||||||
|
|
||||||
data := (^rawptr)(array)^
|
data := (^rawptr)(array)^
|
||||||
|
|
||||||
@@ -389,7 +415,8 @@ append_soa :: proc{
|
|||||||
|
|
||||||
|
|
||||||
delete_soa_slice :: proc(array: $T/#soa[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
delete_soa_slice :: proc(array: $T/#soa[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||||
when intrinsics.type_struct_field_count(E) != 0 {
|
field_count :: len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)
|
||||||
|
when field_count != 0 {
|
||||||
array := array
|
array := array
|
||||||
ptr := (^rawptr)(&array)^
|
ptr := (^rawptr)(&array)^
|
||||||
free(ptr, allocator, loc) or_return
|
free(ptr, allocator, loc) or_return
|
||||||
@@ -398,7 +425,8 @@ delete_soa_slice :: proc(array: $T/#soa[]$E, allocator := context.allocator, loc
|
|||||||
}
|
}
|
||||||
|
|
||||||
delete_soa_dynamic_array :: proc(array: $T/#soa[dynamic]$E, loc := #caller_location) -> Allocator_Error {
|
delete_soa_dynamic_array :: proc(array: $T/#soa[dynamic]$E, loc := #caller_location) -> Allocator_Error {
|
||||||
when intrinsics.type_struct_field_count(E) != 0 {
|
field_count :: len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)
|
||||||
|
when field_count != 0 {
|
||||||
array := array
|
array := array
|
||||||
ptr := (^rawptr)(&array)^
|
ptr := (^rawptr)(&array)^
|
||||||
footer := raw_soa_footer(&array)
|
footer := raw_soa_footer(&array)
|
||||||
@@ -416,7 +444,8 @@ delete_soa :: proc{
|
|||||||
|
|
||||||
|
|
||||||
clear_soa_dynamic_array :: proc(array: ^$T/#soa[dynamic]$E) {
|
clear_soa_dynamic_array :: proc(array: ^$T/#soa[dynamic]$E) {
|
||||||
when intrinsics.type_struct_field_count(E) != 0 {
|
field_count :: len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)
|
||||||
|
when field_count != 0 {
|
||||||
footer := raw_soa_footer(array)
|
footer := raw_soa_footer(array)
|
||||||
footer.len = 0
|
footer.len = 0
|
||||||
}
|
}
|
||||||
@@ -438,12 +467,7 @@ into_dynamic_soa :: proc(array: $T/#soa[]$E) -> #soa[dynamic]E {
|
|||||||
allocator = nil_allocator(),
|
allocator = nil_allocator(),
|
||||||
}
|
}
|
||||||
|
|
||||||
field_count: uintptr
|
field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E))
|
||||||
when intrinsics.type_is_array(E) {
|
|
||||||
field_count = len(E)
|
|
||||||
} else {
|
|
||||||
field_count = uintptr(intrinsics.type_struct_field_count(E))
|
|
||||||
}
|
|
||||||
|
|
||||||
array := array
|
array := array
|
||||||
dynamic_data := ([^]rawptr)(&d)[:field_count]
|
dynamic_data := ([^]rawptr)(&d)[:field_count]
|
||||||
@@ -467,12 +491,7 @@ unordered_remove_soa :: proc(array: ^$T/#soa[dynamic]$E, index: int, loc := #cal
|
|||||||
ti = type_info_base(ti)
|
ti = type_info_base(ti)
|
||||||
si := &ti.variant.(Type_Info_Struct)
|
si := &ti.variant.(Type_Info_Struct)
|
||||||
|
|
||||||
field_count: uintptr
|
field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E))
|
||||||
when intrinsics.type_is_array(E) {
|
|
||||||
field_count = len(E)
|
|
||||||
} else {
|
|
||||||
field_count = uintptr(intrinsics.type_struct_field_count(E))
|
|
||||||
}
|
|
||||||
|
|
||||||
data := uintptr(array)
|
data := uintptr(array)
|
||||||
for i in 0..<field_count {
|
for i in 0..<field_count {
|
||||||
@@ -500,12 +519,7 @@ ordered_remove_soa :: proc(array: ^$T/#soa[dynamic]$E, index: int, loc := #calle
|
|||||||
ti = type_info_base(ti)
|
ti = type_info_base(ti)
|
||||||
si := &ti.variant.(Type_Info_Struct)
|
si := &ti.variant.(Type_Info_Struct)
|
||||||
|
|
||||||
field_count: uintptr
|
field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E))
|
||||||
when intrinsics.type_is_array(E) {
|
|
||||||
field_count = len(E)
|
|
||||||
} else {
|
|
||||||
field_count = uintptr(intrinsics.type_struct_field_count(E))
|
|
||||||
}
|
|
||||||
|
|
||||||
data := uintptr(array)
|
data := uintptr(array)
|
||||||
for i in 0..<field_count {
|
for i in 0..<field_count {
|
||||||
|
|||||||
+2
-1
@@ -12,7 +12,8 @@ Memory_Block :: struct {
|
|||||||
capacity: uint,
|
capacity: uint,
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: This is for internal use, prefer `Arena` from `core:mem/virtual` if necessary
|
// NOTE: This is a growing arena that is only used for the default temp allocator.
|
||||||
|
// For your own growing arena needs, prefer `Arena` from `core:mem/virtual`.
|
||||||
Arena :: struct {
|
Arena :: struct {
|
||||||
backing_allocator: Allocator,
|
backing_allocator: Allocator,
|
||||||
curr_block: ^Memory_Block,
|
curr_block: ^Memory_Block,
|
||||||
@@ -22,6 +22,11 @@ when !ODIN_TEST && !ODIN_NO_ENTRY_POINT {
|
|||||||
@(link_name="_start", linkage="strong", require, export)
|
@(link_name="_start", linkage="strong", require, export)
|
||||||
_start :: proc "c" () {
|
_start :: proc "c" () {
|
||||||
context = default_context()
|
context = default_context()
|
||||||
|
|
||||||
|
when ODIN_OS == .WASI {
|
||||||
|
_wasi_setup_args()
|
||||||
|
}
|
||||||
|
|
||||||
#force_no_inline _startup_runtime()
|
#force_no_inline _startup_runtime()
|
||||||
intrinsics.__entry_point()
|
intrinsics.__entry_point()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ when ODIN_BUILD_MODE == .Dynamic {
|
|||||||
DllMain :: proc "system" (hinstDLL: rawptr, fdwReason: u32, lpReserved: rawptr) -> b32 {
|
DllMain :: proc "system" (hinstDLL: rawptr, fdwReason: u32, lpReserved: rawptr) -> b32 {
|
||||||
context = default_context()
|
context = default_context()
|
||||||
|
|
||||||
// Populate Windows DLL-specific global
|
// Populate Windows DLL-specific globals
|
||||||
dll_forward_reason = DLL_Forward_Reason(fdwReason)
|
dll_forward_reason = DLL_Forward_Reason(fdwReason)
|
||||||
|
dll_instance = hinstDLL
|
||||||
|
|
||||||
switch dll_forward_reason {
|
switch dll_forward_reason {
|
||||||
case .Process_Attach:
|
case .Process_Attach:
|
||||||
|
|||||||
@@ -97,14 +97,14 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
heap_alloc :: proc "contextless" (size: int, zero_memory := true) -> rawptr {
|
||||||
return _heap_alloc(size, zero_memory)
|
return _heap_alloc(size, zero_memory)
|
||||||
}
|
}
|
||||||
|
|
||||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
heap_resize :: proc "contextless" (ptr: rawptr, new_size: int) -> rawptr {
|
||||||
return _heap_resize(ptr, new_size)
|
return _heap_resize(ptr, new_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
heap_free :: proc(ptr: rawptr) {
|
heap_free :: proc "contextless" (ptr: rawptr) {
|
||||||
_heap_free(ptr)
|
_heap_free(ptr)
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ foreign {
|
|||||||
@(link_name="realloc") _orca_realloc :: proc "c" (ptr: rawptr, size: int) -> rawptr ---
|
@(link_name="realloc") _orca_realloc :: proc "c" (ptr: rawptr, size: int) -> rawptr ---
|
||||||
}
|
}
|
||||||
|
|
||||||
_heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
_heap_alloc :: proc "contextless" (size: int, zero_memory := true) -> rawptr {
|
||||||
if size <= 0 {
|
if size <= 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -20,10 +20,10 @@ _heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
_heap_resize :: proc "contextless" (ptr: rawptr, new_size: int) -> rawptr {
|
||||||
return _orca_realloc(ptr, new_size)
|
return _orca_realloc(ptr, new_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
_heap_free :: proc(ptr: rawptr) {
|
_heap_free :: proc "contextless" (ptr: rawptr) {
|
||||||
_orca_free(ptr)
|
_orca_free(ptr)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,17 @@
|
|||||||
//+private
|
//+private
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
_heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
_heap_alloc :: proc "contextless" (size: int, zero_memory := true) -> rawptr {
|
||||||
|
context = default_context()
|
||||||
unimplemented("base:runtime 'heap_alloc' procedure is not supported on this platform")
|
unimplemented("base:runtime 'heap_alloc' procedure is not supported on this platform")
|
||||||
}
|
}
|
||||||
|
|
||||||
_heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
_heap_resize :: proc "contextless" (ptr: rawptr, new_size: int) -> rawptr {
|
||||||
|
context = default_context()
|
||||||
unimplemented("base:runtime 'heap_resize' procedure is not supported on this platform")
|
unimplemented("base:runtime 'heap_resize' procedure is not supported on this platform")
|
||||||
}
|
}
|
||||||
|
|
||||||
_heap_free :: proc(ptr: rawptr) {
|
_heap_free :: proc "contextless" (ptr: rawptr) {
|
||||||
|
context = default_context()
|
||||||
unimplemented("base:runtime 'heap_free' procedure is not supported on this platform")
|
unimplemented("base:runtime 'heap_free' procedure is not supported on this platform")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ foreign libc {
|
|||||||
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr ---
|
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr ---
|
||||||
}
|
}
|
||||||
|
|
||||||
_heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
_heap_alloc :: proc "contextless" (size: int, zero_memory := true) -> rawptr {
|
||||||
if size <= 0 {
|
if size <= 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -27,12 +27,12 @@ _heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
_heap_resize :: proc "contextless" (ptr: rawptr, new_size: int) -> rawptr {
|
||||||
// NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on
|
// NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on
|
||||||
// POSIX platforms. Ensure your caller takes this into account.
|
// POSIX platforms. Ensure your caller takes this into account.
|
||||||
return _unix_realloc(ptr, new_size)
|
return _unix_realloc(ptr, new_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
_heap_free :: proc(ptr: rawptr) {
|
_heap_free :: proc "contextless" (ptr: rawptr) {
|
||||||
_unix_free(ptr)
|
_unix_free(ptr)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,11 +14,11 @@ foreign kernel32 {
|
|||||||
HeapFree :: proc(hHeap: rawptr, dwFlags: u32, lpMem: rawptr) -> b32 ---
|
HeapFree :: proc(hHeap: rawptr, dwFlags: u32, lpMem: rawptr) -> b32 ---
|
||||||
}
|
}
|
||||||
|
|
||||||
_heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
_heap_alloc :: proc "contextless" (size: int, zero_memory := true) -> rawptr {
|
||||||
HEAP_ZERO_MEMORY :: 0x00000008
|
HEAP_ZERO_MEMORY :: 0x00000008
|
||||||
return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY if zero_memory else 0, uint(size))
|
return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY if zero_memory else 0, uint(size))
|
||||||
}
|
}
|
||||||
_heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
_heap_resize :: proc "contextless" (ptr: rawptr, new_size: int) -> rawptr {
|
||||||
if new_size == 0 {
|
if new_size == 0 {
|
||||||
_heap_free(ptr)
|
_heap_free(ptr)
|
||||||
return nil
|
return nil
|
||||||
@@ -30,7 +30,7 @@ _heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
|||||||
HEAP_ZERO_MEMORY :: 0x00000008
|
HEAP_ZERO_MEMORY :: 0x00000008
|
||||||
return HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ptr, uint(new_size))
|
return HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ptr, uint(new_size))
|
||||||
}
|
}
|
||||||
_heap_free :: proc(ptr: rawptr) {
|
_heap_free :: proc "contextless" (ptr: rawptr) {
|
||||||
if ptr == nil {
|
if ptr == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-27
@@ -1,3 +1,4 @@
|
|||||||
|
//+vet !cast
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
@@ -29,7 +30,7 @@ is_power_of_two_int :: #force_inline proc "contextless" (x: int) -> bool {
|
|||||||
return (x & (x-1)) == 0
|
return (x & (x-1)) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
align_forward_int :: #force_inline proc(ptr, align: int) -> int {
|
align_forward_int :: #force_inline proc "odin" (ptr, align: int) -> int {
|
||||||
assert(is_power_of_two_int(align))
|
assert(is_power_of_two_int(align))
|
||||||
|
|
||||||
p := ptr
|
p := ptr
|
||||||
@@ -47,7 +48,7 @@ is_power_of_two_uint :: #force_inline proc "contextless" (x: uint) -> bool {
|
|||||||
return (x & (x-1)) == 0
|
return (x & (x-1)) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
align_forward_uint :: #force_inline proc(ptr, align: uint) -> uint {
|
align_forward_uint :: #force_inline proc "odin" (ptr, align: uint) -> uint {
|
||||||
assert(is_power_of_two_uint(align))
|
assert(is_power_of_two_uint(align))
|
||||||
|
|
||||||
p := ptr
|
p := ptr
|
||||||
@@ -65,7 +66,7 @@ is_power_of_two_uintptr :: #force_inline proc "contextless" (x: uintptr) -> bool
|
|||||||
return (x & (x-1)) == 0
|
return (x & (x-1)) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
align_forward_uintptr :: #force_inline proc(ptr, align: uintptr) -> uintptr {
|
align_forward_uintptr :: #force_inline proc "odin" (ptr, align: uintptr) -> uintptr {
|
||||||
assert(is_power_of_two_uintptr(align))
|
assert(is_power_of_two_uintptr(align))
|
||||||
|
|
||||||
p := ptr
|
p := ptr
|
||||||
@@ -642,21 +643,24 @@ abs_quaternion256 :: #force_inline proc "contextless" (x: quaternion256) -> f64
|
|||||||
|
|
||||||
|
|
||||||
quo_complex32 :: proc "contextless" (n, m: complex32) -> complex32 {
|
quo_complex32 :: proc "contextless" (n, m: complex32) -> complex32 {
|
||||||
e, f: f16
|
nr, ni := f32(real(n)), f32(imag(n))
|
||||||
|
mr, mi := f32(real(m)), f32(imag(m))
|
||||||
|
|
||||||
if abs(real(m)) >= abs(imag(m)) {
|
e, f: f32
|
||||||
ratio := imag(m) / real(m)
|
|
||||||
denom := real(m) + ratio*imag(m)
|
if abs(mr) >= abs(mi) {
|
||||||
e = (real(n) + imag(n)*ratio) / denom
|
ratio := mi / mr
|
||||||
f = (imag(n) - real(n)*ratio) / denom
|
denom := mr + ratio*mi
|
||||||
|
e = (nr + ni*ratio) / denom
|
||||||
|
f = (ni - nr*ratio) / denom
|
||||||
} else {
|
} else {
|
||||||
ratio := real(m) / imag(m)
|
ratio := mr / mi
|
||||||
denom := imag(m) + ratio*real(m)
|
denom := mi + ratio*mr
|
||||||
e = (real(n)*ratio + imag(n)) / denom
|
e = (nr*ratio + ni) / denom
|
||||||
f = (imag(n)*ratio - real(n)) / denom
|
f = (ni*ratio - nr) / denom
|
||||||
}
|
}
|
||||||
|
|
||||||
return complex(e, f)
|
return complex(f16(e), f16(f))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -697,15 +701,15 @@ quo_complex128 :: proc "contextless" (n, m: complex128) -> complex128 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mul_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
|
mul_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
|
||||||
q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q)
|
q0, q1, q2, q3 := f32(real(q)), f32(imag(q)), f32(jmag(q)), f32(kmag(q))
|
||||||
r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r)
|
r0, r1, r2, r3 := f32(real(r)), f32(imag(r)), f32(jmag(r)), f32(kmag(r))
|
||||||
|
|
||||||
t0 := r0*q0 - r1*q1 - r2*q2 - r3*q3
|
t0 := r0*q0 - r1*q1 - r2*q2 - r3*q3
|
||||||
t1 := r0*q1 + r1*q0 - r2*q3 + r3*q2
|
t1 := r0*q1 + r1*q0 - r2*q3 + r3*q2
|
||||||
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
|
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
|
||||||
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0
|
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0
|
||||||
|
|
||||||
return quaternion(w=t0, x=t1, y=t2, z=t3)
|
return quaternion(w=f16(t0), x=f16(t1), y=f16(t2), z=f16(t3))
|
||||||
}
|
}
|
||||||
|
|
||||||
mul_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
|
mul_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
|
||||||
@@ -733,8 +737,8 @@ mul_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
quo_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
|
quo_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
|
||||||
q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q)
|
q0, q1, q2, q3 := f32(real(q)), f32(imag(q)), f32(jmag(q)), f32(kmag(q))
|
||||||
r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r)
|
r0, r1, r2, r3 := f32(real(r)), f32(imag(r)), f32(jmag(r)), f32(kmag(r))
|
||||||
|
|
||||||
invmag2 := 1.0 / (r0*r0 + r1*r1 + r2*r2 + r3*r3)
|
invmag2 := 1.0 / (r0*r0 + r1*r1 + r2*r2 + r3*r3)
|
||||||
|
|
||||||
@@ -743,7 +747,7 @@ quo_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
|
|||||||
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2
|
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2
|
||||||
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2
|
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2
|
||||||
|
|
||||||
return quaternion(w=t0, x=t1, y=t2, z=t3)
|
return quaternion(w=f16(t0), x=f16(t1), y=f16(t2), z=f16(t3))
|
||||||
}
|
}
|
||||||
|
|
||||||
quo_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
|
quo_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
|
||||||
@@ -1012,26 +1016,26 @@ modti3 :: proc "c" (a, b: i128) -> i128 {
|
|||||||
bn := (b ~ s_b) - s_b
|
bn := (b ~ s_b) - s_b
|
||||||
|
|
||||||
r: u128 = ---
|
r: u128 = ---
|
||||||
_ = udivmod128(transmute(u128)an, transmute(u128)bn, &r)
|
_ = udivmod128(u128(an), u128(bn), &r)
|
||||||
return (transmute(i128)r ~ s_a) - s_a
|
return (i128(r) ~ s_a) - s_a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@(link_name="__divmodti4", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
@(link_name="__divmodti4", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||||
divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 {
|
divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 {
|
||||||
u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem)
|
u := udivmod128(u128(a), u128(b), (^u128)(rem))
|
||||||
return transmute(i128)u
|
return i128(u)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(link_name="__divti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
@(link_name="__divti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||||
divti3 :: proc "c" (a, b: i128) -> i128 {
|
divti3 :: proc "c" (a, b: i128) -> i128 {
|
||||||
u := udivmodti4(transmute(u128)a, transmute(u128)b, nil)
|
u := udivmodti4(u128(a), u128(b), nil)
|
||||||
return transmute(i128)u
|
return i128(u)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@(link_name="__fixdfti", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
@(link_name="__fixdfti", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||||
fixdfti :: proc(a: u64) -> i128 {
|
fixdfti :: proc "c" (a: u64) -> i128 {
|
||||||
significandBits :: 52
|
significandBits :: 52
|
||||||
typeWidth :: (size_of(u64)*8)
|
typeWidth :: (size_of(u64)*8)
|
||||||
exponentBits :: (typeWidth - significandBits - 1)
|
exponentBits :: (typeWidth - significandBits - 1)
|
||||||
|
|||||||
@@ -5,11 +5,24 @@ package runtime
|
|||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||||
WRITE :: 0x2000004
|
|
||||||
STDERR :: 2
|
STDERR :: 2
|
||||||
|
when ODIN_NO_CRT {
|
||||||
|
WRITE :: 0x2000004
|
||||||
ret := intrinsics.syscall(WRITE, STDERR, uintptr(raw_data(data)), uintptr(len(data)))
|
ret := intrinsics.syscall(WRITE, STDERR, uintptr(raw_data(data)), uintptr(len(data)))
|
||||||
if ret < 0 {
|
if ret < 0 {
|
||||||
return 0, _OS_Errno(-ret)
|
return 0, _OS_Errno(-ret)
|
||||||
}
|
}
|
||||||
return int(ret), 0
|
return int(ret), 0
|
||||||
|
} else {
|
||||||
|
foreign {
|
||||||
|
write :: proc(handle: i32, buffer: [^]byte, count: uint) -> int ---
|
||||||
|
__error :: proc() -> ^i32 ---
|
||||||
|
}
|
||||||
|
|
||||||
|
if ret := write(STDERR, raw_data(data), len(data)); ret >= 0 {
|
||||||
|
return int(ret), 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, _OS_Errno(__error()^)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,54 @@
|
|||||||
//+private
|
//+private
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:sys/wasm/wasi"
|
foreign import wasi "wasi_snapshot_preview1"
|
||||||
|
|
||||||
|
@(default_calling_convention="contextless")
|
||||||
|
foreign wasi {
|
||||||
|
fd_write :: proc(
|
||||||
|
fd: i32,
|
||||||
|
iovs: [][]byte,
|
||||||
|
n: ^uint,
|
||||||
|
) -> u16 ---
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
args_sizes_get :: proc(
|
||||||
|
num_of_args: ^uint,
|
||||||
|
size_of_args: ^uint,
|
||||||
|
) -> u16 ---
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
args_get :: proc(
|
||||||
|
argv: [^]cstring,
|
||||||
|
argv_buf: [^]byte,
|
||||||
|
) -> u16 ---
|
||||||
|
}
|
||||||
|
|
||||||
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||||
data_iovec := (wasi.ciovec_t)(data)
|
n: uint
|
||||||
n, err := wasi.fd_write(1, {data_iovec})
|
err := fd_write(1, {data}, &n)
|
||||||
return int(n), _OS_Errno(err)
|
return int(n), _OS_Errno(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_wasi_setup_args :: proc() {
|
||||||
|
num_of_args, size_of_args: uint
|
||||||
|
if errno := args_sizes_get(&num_of_args, &size_of_args); errno != 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err: Allocator_Error
|
||||||
|
if args__, err = make([]cstring, num_of_args); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
args_buf: []byte
|
||||||
|
if args_buf, err = make([]byte, size_of_args); err != nil {
|
||||||
|
delete(args__)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if errno := args_get(raw_data(args__), raw_data(args_buf)); errno != 0 {
|
||||||
|
delete(args__)
|
||||||
|
delete(args_buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -262,7 +262,7 @@ print_typeid :: #force_no_inline proc "contextless" (id: typeid) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="size")
|
@(optimization_mode="favor_size")
|
||||||
print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
|
print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
|
||||||
if ti == nil {
|
if ti == nil {
|
||||||
print_string("nil")
|
print_string("nil")
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
|
random_generator_read_bytes :: proc(rg: Random_Generator, p: []byte) -> bool {
|
||||||
|
if rg.procedure != nil {
|
||||||
|
rg.procedure(rg.data, .Read, p)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
|
random_generator_read_ptr :: proc(rg: Random_Generator, p: rawptr, len: uint) -> bool {
|
||||||
|
if rg.procedure != nil {
|
||||||
|
rg.procedure(rg.data, .Read, ([^]byte)(p)[:len])
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
|
random_generator_query_info :: proc(rg: Random_Generator) -> (info: Random_Generator_Query_Info) {
|
||||||
|
if rg.procedure != nil {
|
||||||
|
rg.procedure(rg.data, .Query_Info, ([^]byte)(&info)[:size_of(info)])
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
random_generator_reset_bytes :: proc(rg: Random_Generator, p: []byte) {
|
||||||
|
if rg.procedure != nil {
|
||||||
|
rg.procedure(rg.data, .Reset, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
random_generator_reset_u64 :: proc(rg: Random_Generator, p: u64) {
|
||||||
|
if rg.procedure != nil {
|
||||||
|
p := p
|
||||||
|
rg.procedure(rg.data, .Reset, ([^]byte)(&p)[:size_of(p)])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Default_Random_State :: struct {
|
||||||
|
state: u64,
|
||||||
|
inc: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
default_random_generator_proc :: proc(data: rawptr, mode: Random_Generator_Mode, p: []byte) {
|
||||||
|
@(require_results)
|
||||||
|
read_u64 :: proc "contextless" (r: ^Default_Random_State) -> u64 {
|
||||||
|
old_state := r.state
|
||||||
|
r.state = old_state * 6364136223846793005 + (r.inc|1)
|
||||||
|
xor_shifted := (((old_state >> 59) + 5) ~ old_state) * 12605985483714917081
|
||||||
|
rot := (old_state >> 59)
|
||||||
|
return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 63))
|
||||||
|
}
|
||||||
|
|
||||||
|
@(thread_local)
|
||||||
|
global_rand_seed: Default_Random_State
|
||||||
|
|
||||||
|
init :: proc "contextless" (r: ^Default_Random_State, seed: u64) {
|
||||||
|
seed := seed
|
||||||
|
if seed == 0 {
|
||||||
|
seed = u64(intrinsics.read_cycle_counter())
|
||||||
|
}
|
||||||
|
r.state = 0
|
||||||
|
r.inc = (seed << 1) | 1
|
||||||
|
_ = read_u64(r)
|
||||||
|
r.state += seed
|
||||||
|
_ = read_u64(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
r: ^Default_Random_State = ---
|
||||||
|
if data == nil {
|
||||||
|
r = &global_rand_seed
|
||||||
|
} else {
|
||||||
|
r = cast(^Default_Random_State)data
|
||||||
|
}
|
||||||
|
|
||||||
|
switch mode {
|
||||||
|
case .Read:
|
||||||
|
if r.state == 0 && r.inc == 0 {
|
||||||
|
init(r, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch len(p) {
|
||||||
|
case size_of(u64):
|
||||||
|
// Fast path for a 64-bit destination.
|
||||||
|
intrinsics.unaligned_store((^u64)(raw_data(p)), read_u64(r))
|
||||||
|
case:
|
||||||
|
// All other cases.
|
||||||
|
pos := i8(0)
|
||||||
|
val := u64(0)
|
||||||
|
for &v in p {
|
||||||
|
if pos == 0 {
|
||||||
|
val = read_u64(r)
|
||||||
|
pos = 7
|
||||||
|
}
|
||||||
|
v = byte(val)
|
||||||
|
val >>= 8
|
||||||
|
pos -= 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case .Reset:
|
||||||
|
seed: u64
|
||||||
|
mem_copy_non_overlapping(&seed, raw_data(p), min(size_of(seed), len(p)))
|
||||||
|
init(r, seed)
|
||||||
|
|
||||||
|
case .Query_Info:
|
||||||
|
if len(p) != size_of(Random_Generator_Query_Info) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
info := (^Random_Generator_Query_Info)(raw_data(p))
|
||||||
|
info^ += {.Uniform, .Resettable}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default_random_generator :: proc "contextless" (state: ^Default_Random_State = nil) -> Random_Generator {
|
||||||
|
return {
|
||||||
|
procedure = default_random_generator_proc,
|
||||||
|
data = state,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -58,7 +58,7 @@ udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
|
|||||||
return u128(n[high] >> _ctz(d[high]))
|
return u128(n[high] >> _ctz(d[high]))
|
||||||
}
|
}
|
||||||
|
|
||||||
sr = transmute(u32)(i32(_clz(d[high])) - i32(_clz(n[high])))
|
sr = u32(i32(_clz(d[high])) - i32(_clz(n[high])))
|
||||||
if sr > U64_BITS - 2 {
|
if sr > U64_BITS - 2 {
|
||||||
if rem != nil {
|
if rem != nil {
|
||||||
rem^ = a
|
rem^ = a
|
||||||
@@ -107,7 +107,7 @@ udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
|
|||||||
r[low] = n[high] >> (sr - U64_BITS)
|
r[low] = n[high] >> (sr - U64_BITS)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sr = transmute(u32)(i32(_clz(d[high])) - i32(_clz(n[high])))
|
sr = u32(i32(_clz(d[high])) - i32(_clz(n[high])))
|
||||||
|
|
||||||
if sr > U64_BITS - 1 {
|
if sr > U64_BITS - 1 {
|
||||||
if rem != nil {
|
if rem != nil {
|
||||||
@@ -143,7 +143,7 @@ udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
|
|||||||
r_all = transmute(u128)r
|
r_all = transmute(u128)r
|
||||||
s := i128(b - r_all - 1) >> (U128_BITS - 1)
|
s := i128(b - r_all - 1) >> (U128_BITS - 1)
|
||||||
carry = u32(s & 1)
|
carry = u32(s & 1)
|
||||||
r_all -= b & transmute(u128)s
|
r_all -= b & u128(s)
|
||||||
r = transmute([2]u64)r_all
|
r = transmute([2]u64)r_all
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -495,7 +495,7 @@ claim_more_memory :: proc(a: ^WASM_Allocator, num_bytes: uint) -> bool {
|
|||||||
// we can just extend the spill.
|
// we can just extend the spill.
|
||||||
spill_end := uintptr(raw_data(a.spill)) + uintptr(len(a.spill))
|
spill_end := uintptr(raw_data(a.spill)) + uintptr(len(a.spill))
|
||||||
if spill_end == uintptr(raw_data(allocated)) {
|
if spill_end == uintptr(raw_data(allocated)) {
|
||||||
raw_spill := transmute(^Raw_Slice)(&a.spill)
|
raw_spill := (^Raw_Slice)(&a.spill)
|
||||||
raw_spill.len += len(allocated)
|
raw_spill.len += len(allocated)
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, we have to "waste" the previous spill.
|
// Otherwise, we have to "waste" the previous spill.
|
||||||
@@ -679,7 +679,7 @@ allocate_memory :: proc(a: ^WASM_Allocator, alignment: uint, size: uint, loc :=
|
|||||||
// but we just had a stale bit set to mark a populated bucket.
|
// but we just had a stale bit set to mark a populated bucket.
|
||||||
// Reset the bit to update latest status so that we do not
|
// Reset the bit to update latest status so that we do not
|
||||||
// redundantly look at this bucket again.
|
// redundantly look at this bucket again.
|
||||||
a.free_region_buckets_used &= ~(BUCKET_BITMASK_T(1) << bucket_index)
|
a.free_region_buckets_used &~= BUCKET_BITMASK_T(1) << bucket_index
|
||||||
bucket_mask ~= 1
|
bucket_mask ~= 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -760,7 +760,7 @@ free :: proc(a: ^WASM_Allocator, ptr: rawptr, loc := #caller_location) {
|
|||||||
defer unlock(a)
|
defer unlock(a)
|
||||||
|
|
||||||
size := region.size
|
size := region.size
|
||||||
assert(region_is_in_use(region), "double free", loc=loc)
|
assert(region_is_in_use(region), "double free or corrupt region", loc=loc)
|
||||||
|
|
||||||
prev_region_size_field := ([^]uint)(region)[-1]
|
prev_region_size_field := ([^]uint)(region)[-1]
|
||||||
prev_region_size := prev_region_size_field & ~uint(FREE_REGION_FLAG)
|
prev_region_size := prev_region_size_field & ~uint(FREE_REGION_FLAG)
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -48,6 +48,9 @@ if "%2" == "1" (
|
|||||||
set odin_version_raw="dev-%curr_year%-%curr_month%"
|
set odin_version_raw="dev-%curr_year%-%curr_month%"
|
||||||
|
|
||||||
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF
|
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
|
||||||
|
set compiler_flags= %compiler_flags% /utf-8
|
||||||
set compiler_defines= -DODIN_VERSION_RAW=\"%odin_version_raw%\"
|
set compiler_defines= -DODIN_VERSION_RAW=\"%odin_version_raw%\"
|
||||||
|
|
||||||
if not exist .git\ goto skip_git_hash
|
if not exist .git\ goto skip_git_hash
|
||||||
@@ -111,7 +114,7 @@ call build_vendor.bat
|
|||||||
if %errorlevel% neq 0 goto end_of_build
|
if %errorlevel% neq 0 goto end_of_build
|
||||||
|
|
||||||
rem If the demo doesn't run for you and your CPU is more than a decade old, try -microarch:native
|
rem If the demo doesn't run for you and your CPU is more than a decade old, try -microarch:native
|
||||||
if %release_mode% EQU 0 odin run examples/demo -- Hellope World
|
if %release_mode% EQU 0 odin run examples/demo -vet -strict-style -- Hellope World
|
||||||
|
|
||||||
del *.obj > NUL 2> NUL
|
del *.obj > NUL 2> NUL
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -144,7 +144,7 @@ build_odin() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
run_demo() {
|
run_demo() {
|
||||||
./odin run examples/demo/demo.odin -file -- Hellope World
|
./odin run examples/demo -vet -strict-style -- Hellope World
|
||||||
}
|
}
|
||||||
|
|
||||||
if [ $# -eq 0 ]; then
|
if [ $# -eq 0 ]; then
|
||||||
|
|||||||
+1
-14
@@ -34,20 +34,7 @@ when ODIN_OS == .Windows {
|
|||||||
SIGTERM :: 15
|
SIGTERM :: 15
|
||||||
}
|
}
|
||||||
|
|
||||||
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Haiku || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
|
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Haiku || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Darwin {
|
||||||
SIG_ERR :: rawptr(~uintptr(0))
|
|
||||||
SIG_DFL :: rawptr(uintptr(0))
|
|
||||||
SIG_IGN :: rawptr(uintptr(1))
|
|
||||||
|
|
||||||
SIGABRT :: 6
|
|
||||||
SIGFPE :: 8
|
|
||||||
SIGILL :: 4
|
|
||||||
SIGINT :: 2
|
|
||||||
SIGSEGV :: 11
|
|
||||||
SIGTERM :: 15
|
|
||||||
}
|
|
||||||
|
|
||||||
when ODIN_OS == .Darwin {
|
|
||||||
SIG_ERR :: rawptr(~uintptr(0))
|
SIG_ERR :: rawptr(~uintptr(0))
|
||||||
SIG_DFL :: rawptr(uintptr(0))
|
SIG_DFL :: rawptr(uintptr(0))
|
||||||
SIG_IGN :: rawptr(uintptr(1))
|
SIG_IGN :: rawptr(uintptr(1))
|
||||||
|
|||||||
+25
-25
@@ -186,7 +186,7 @@ input_size_from_stream :: proc(z: ^Context_Stream_Input) -> (res: i64, err: Erro
|
|||||||
|
|
||||||
input_size :: proc{input_size_from_memory, input_size_from_stream}
|
input_size :: proc{input_size_from_memory, input_size_from_stream}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_slice_from_memory :: #force_inline proc(z: ^Context_Memory_Input, size: int) -> (res: []u8, err: io.Error) {
|
read_slice_from_memory :: #force_inline proc(z: ^Context_Memory_Input, size: int) -> (res: []u8, err: io.Error) {
|
||||||
#no_bounds_check {
|
#no_bounds_check {
|
||||||
if len(z.input_data) >= size {
|
if len(z.input_data) >= size {
|
||||||
@@ -203,7 +203,7 @@ read_slice_from_memory :: #force_inline proc(z: ^Context_Memory_Input, size: int
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_slice_from_stream :: #force_inline proc(z: ^Context_Stream_Input, size: int) -> (res: []u8, err: io.Error) {
|
read_slice_from_stream :: #force_inline proc(z: ^Context_Stream_Input, size: int) -> (res: []u8, err: io.Error) {
|
||||||
// TODO: REMOVE ALL USE OF context.temp_allocator here
|
// TODO: REMOVE ALL USE OF context.temp_allocator here
|
||||||
// there is literally no need for it
|
// there is literally no need for it
|
||||||
@@ -214,13 +214,13 @@ read_slice_from_stream :: #force_inline proc(z: ^Context_Stream_Input, size: int
|
|||||||
|
|
||||||
read_slice :: proc{read_slice_from_memory, read_slice_from_stream}
|
read_slice :: proc{read_slice_from_memory, read_slice_from_stream}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_data :: #force_inline proc(z: ^$C, $T: typeid) -> (res: T, err: io.Error) {
|
read_data :: #force_inline proc(z: ^$C, $T: typeid) -> (res: T, err: io.Error) {
|
||||||
b := read_slice(z, size_of(T)) or_return
|
b := read_slice(z, size_of(T)) or_return
|
||||||
return (^T)(&b[0])^, nil
|
return (^T)(&b[0])^, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_u8_from_memory :: #force_inline proc(z: ^Context_Memory_Input) -> (res: u8, err: io.Error) {
|
read_u8_from_memory :: #force_inline proc(z: ^Context_Memory_Input) -> (res: u8, err: io.Error) {
|
||||||
#no_bounds_check {
|
#no_bounds_check {
|
||||||
if len(z.input_data) >= 1 {
|
if len(z.input_data) >= 1 {
|
||||||
@@ -232,7 +232,7 @@ read_u8_from_memory :: #force_inline proc(z: ^Context_Memory_Input) -> (res: u8,
|
|||||||
return 0, .EOF
|
return 0, .EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_u8_from_stream :: #force_inline proc(z: ^Context_Stream_Input) -> (res: u8, err: io.Error) {
|
read_u8_from_stream :: #force_inline proc(z: ^Context_Stream_Input) -> (res: u8, err: io.Error) {
|
||||||
b := read_slice_from_stream(z, 1) or_return
|
b := read_slice_from_stream(z, 1) or_return
|
||||||
return b[0], nil
|
return b[0], nil
|
||||||
@@ -242,7 +242,7 @@ read_u8 :: proc{read_u8_from_memory, read_u8_from_stream}
|
|||||||
|
|
||||||
// You would typically only use this at the end of Inflate, to drain bits from the code buffer
|
// You would typically only use this at the end of Inflate, to drain bits from the code buffer
|
||||||
// preferentially.
|
// preferentially.
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_u8_prefer_code_buffer_lsb :: #force_inline proc(z: ^$C) -> (res: u8, err: io.Error) {
|
read_u8_prefer_code_buffer_lsb :: #force_inline proc(z: ^$C) -> (res: u8, err: io.Error) {
|
||||||
if z.num_bits >= 8 {
|
if z.num_bits >= 8 {
|
||||||
res = u8(read_bits_no_refill_lsb(z, 8))
|
res = u8(read_bits_no_refill_lsb(z, 8))
|
||||||
@@ -257,7 +257,7 @@ read_u8_prefer_code_buffer_lsb :: #force_inline proc(z: ^$C) -> (res: u8, err: i
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
peek_data_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid) -> (res: T, err: io.Error) {
|
peek_data_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid) -> (res: T, err: io.Error) {
|
||||||
size :: size_of(T)
|
size :: size_of(T)
|
||||||
|
|
||||||
@@ -275,7 +275,7 @@ peek_data_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
peek_data_at_offset_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid, #any_int offset: int) -> (res: T, err: io.Error) {
|
peek_data_at_offset_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid, #any_int offset: int) -> (res: T, err: io.Error) {
|
||||||
size :: size_of(T)
|
size :: size_of(T)
|
||||||
|
|
||||||
@@ -293,7 +293,7 @@ peek_data_at_offset_from_memory :: #force_inline proc(z: ^Context_Memory_Input,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid) -> (res: T, err: io.Error) {
|
peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid) -> (res: T, err: io.Error) {
|
||||||
size :: size_of(T)
|
size :: size_of(T)
|
||||||
|
|
||||||
@@ -317,7 +317,7 @@ peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid
|
|||||||
return res, .None
|
return res, .None
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
peek_data_at_offset_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid, #any_int offset: int) -> (res: T, err: io.Error) {
|
peek_data_at_offset_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid, #any_int offset: int) -> (res: T, err: io.Error) {
|
||||||
size :: size_of(T)
|
size :: size_of(T)
|
||||||
|
|
||||||
@@ -352,14 +352,14 @@ peek_data :: proc{peek_data_from_memory, peek_data_from_stream, peek_data_at_off
|
|||||||
|
|
||||||
|
|
||||||
// Sliding window read back
|
// Sliding window read back
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
peek_back_byte :: #force_inline proc(z: ^$C, offset: i64) -> (res: u8, err: io.Error) {
|
peek_back_byte :: #force_inline proc(z: ^$C, offset: i64) -> (res: u8, err: io.Error) {
|
||||||
// Look back into the sliding window.
|
// Look back into the sliding window.
|
||||||
return z.output.buf[z.bytes_written - offset], .None
|
return z.output.buf[z.bytes_written - offset], .None
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generalized bit reader LSB
|
// Generalized bit reader LSB
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width := i8(48)) {
|
refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width := i8(48)) {
|
||||||
refill := u64(width)
|
refill := u64(width)
|
||||||
b := u64(0)
|
b := u64(0)
|
||||||
@@ -385,7 +385,7 @@ refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width :=
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generalized bit reader LSB
|
// Generalized bit reader LSB
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
refill_lsb_from_stream :: proc(z: ^Context_Stream_Input, width := i8(24)) {
|
refill_lsb_from_stream :: proc(z: ^Context_Stream_Input, width := i8(24)) {
|
||||||
refill := u64(width)
|
refill := u64(width)
|
||||||
|
|
||||||
@@ -414,13 +414,13 @@ refill_lsb_from_stream :: proc(z: ^Context_Stream_Input, width := i8(24)) {
|
|||||||
refill_lsb :: proc{refill_lsb_from_memory, refill_lsb_from_stream}
|
refill_lsb :: proc{refill_lsb_from_memory, refill_lsb_from_stream}
|
||||||
|
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
consume_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) {
|
consume_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) {
|
||||||
z.code_buffer >>= width
|
z.code_buffer >>= width
|
||||||
z.num_bits -= u64(width)
|
z.num_bits -= u64(width)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
consume_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) {
|
consume_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) {
|
||||||
z.code_buffer >>= width
|
z.code_buffer >>= width
|
||||||
z.num_bits -= u64(width)
|
z.num_bits -= u64(width)
|
||||||
@@ -428,7 +428,7 @@ consume_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, wid
|
|||||||
|
|
||||||
consume_bits_lsb :: proc{consume_bits_lsb_from_memory, consume_bits_lsb_from_stream}
|
consume_bits_lsb :: proc{consume_bits_lsb_from_memory, consume_bits_lsb_from_stream}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
peek_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
peek_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
||||||
if z.num_bits < u64(width) {
|
if z.num_bits < u64(width) {
|
||||||
refill_lsb(z)
|
refill_lsb(z)
|
||||||
@@ -436,7 +436,7 @@ peek_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width:
|
|||||||
return u32(z.code_buffer &~ (~u64(0) << width))
|
return u32(z.code_buffer &~ (~u64(0) << width))
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
peek_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
peek_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
||||||
if z.num_bits < u64(width) {
|
if z.num_bits < u64(width) {
|
||||||
refill_lsb(z)
|
refill_lsb(z)
|
||||||
@@ -446,13 +446,13 @@ peek_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width:
|
|||||||
|
|
||||||
peek_bits_lsb :: proc{peek_bits_lsb_from_memory, peek_bits_lsb_from_stream}
|
peek_bits_lsb :: proc{peek_bits_lsb_from_memory, peek_bits_lsb_from_stream}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
peek_bits_no_refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
peek_bits_no_refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
||||||
assert(z.num_bits >= u64(width))
|
assert(z.num_bits >= u64(width))
|
||||||
return u32(z.code_buffer &~ (~u64(0) << width))
|
return u32(z.code_buffer &~ (~u64(0) << width))
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
peek_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
peek_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
||||||
assert(z.num_bits >= u64(width))
|
assert(z.num_bits >= u64(width))
|
||||||
return u32(z.code_buffer &~ (~u64(0) << width))
|
return u32(z.code_buffer &~ (~u64(0) << width))
|
||||||
@@ -460,14 +460,14 @@ peek_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Inp
|
|||||||
|
|
||||||
peek_bits_no_refill_lsb :: proc{peek_bits_no_refill_lsb_from_memory, peek_bits_no_refill_lsb_from_stream}
|
peek_bits_no_refill_lsb :: proc{peek_bits_no_refill_lsb_from_memory, peek_bits_no_refill_lsb_from_stream}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
read_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
||||||
k := #force_inline peek_bits_lsb(z, width)
|
k := #force_inline peek_bits_lsb(z, width)
|
||||||
#force_inline consume_bits_lsb(z, width)
|
#force_inline consume_bits_lsb(z, width)
|
||||||
return k
|
return k
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
read_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
||||||
k := peek_bits_lsb(z, width)
|
k := peek_bits_lsb(z, width)
|
||||||
consume_bits_lsb(z, width)
|
consume_bits_lsb(z, width)
|
||||||
@@ -476,14 +476,14 @@ read_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width:
|
|||||||
|
|
||||||
read_bits_lsb :: proc{read_bits_lsb_from_memory, read_bits_lsb_from_stream}
|
read_bits_lsb :: proc{read_bits_lsb_from_memory, read_bits_lsb_from_stream}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_bits_no_refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
read_bits_no_refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
||||||
k := #force_inline peek_bits_no_refill_lsb(z, width)
|
k := #force_inline peek_bits_no_refill_lsb(z, width)
|
||||||
#force_inline consume_bits_lsb(z, width)
|
#force_inline consume_bits_lsb(z, width)
|
||||||
return k
|
return k
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
read_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
||||||
k := peek_bits_no_refill_lsb(z, width)
|
k := peek_bits_no_refill_lsb(z, width)
|
||||||
consume_bits_lsb(z, width)
|
consume_bits_lsb(z, width)
|
||||||
@@ -493,14 +493,14 @@ read_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Inp
|
|||||||
read_bits_no_refill_lsb :: proc{read_bits_no_refill_lsb_from_memory, read_bits_no_refill_lsb_from_stream}
|
read_bits_no_refill_lsb :: proc{read_bits_no_refill_lsb_from_memory, read_bits_no_refill_lsb_from_stream}
|
||||||
|
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
discard_to_next_byte_lsb_from_memory :: proc(z: ^Context_Memory_Input) {
|
discard_to_next_byte_lsb_from_memory :: proc(z: ^Context_Memory_Input) {
|
||||||
discard := u8(z.num_bits & 7)
|
discard := u8(z.num_bits & 7)
|
||||||
#force_inline consume_bits_lsb(z, discard)
|
#force_inline consume_bits_lsb(z, discard)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
discard_to_next_byte_lsb_from_stream :: proc(z: ^Context_Stream_Input) {
|
discard_to_next_byte_lsb_from_stream :: proc(z: ^Context_Stream_Input) {
|
||||||
discard := u8(z.num_bits & 7)
|
discard := u8(z.num_bits & 7)
|
||||||
consume_bits_lsb(z, discard)
|
consume_bits_lsb(z, discard)
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ decompress_slice_to_output_buffer :: proc(input: []u8, output: []u8, model := DE
|
|||||||
validate_model(model) or_return
|
validate_model(model) or_return
|
||||||
|
|
||||||
for inp < inp_end {
|
for inp < inp_end {
|
||||||
val := transmute(i8)input[inp]
|
val := i8(input[inp])
|
||||||
mark := int(-1)
|
mark := int(-1)
|
||||||
|
|
||||||
for val < 0 {
|
for val < 0 {
|
||||||
@@ -274,12 +274,9 @@ compress_string_to_buffer :: proc(input: string, output: []u8, model := DEFAULT_
|
|||||||
out_ptr := raw_data(output[out:])
|
out_ptr := raw_data(output[out:])
|
||||||
|
|
||||||
switch pack.bytes_packed {
|
switch pack.bytes_packed {
|
||||||
case 4:
|
case 4: intrinsics.unaligned_store((^u32)(out_ptr), code)
|
||||||
intrinsics.unaligned_store(transmute(^u32)out_ptr, code)
|
case 2: intrinsics.unaligned_store((^u16)(out_ptr), u16(code))
|
||||||
case 2:
|
case 1: intrinsics.unaligned_store( (^u8)(out_ptr), u8(code))
|
||||||
intrinsics.unaligned_store(transmute(^u16)out_ptr, u16(code))
|
|
||||||
case 1:
|
|
||||||
intrinsics.unaligned_store(transmute(^u8)out_ptr, u8(code))
|
|
||||||
case:
|
case:
|
||||||
return out, .Unknown_Compression_Method
|
return out, .Unknown_Compression_Method
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ Huffman_Table :: struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Implementation starts here
|
// Implementation starts here
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
z_bit_reverse :: #force_inline proc(n: u16, bits: u8) -> (r: u16) {
|
z_bit_reverse :: #force_inline proc(n: u16, bits: u8) -> (r: u16) {
|
||||||
assert(bits <= 16)
|
assert(bits <= 16)
|
||||||
// NOTE: Can optimize with llvm.bitreverse.i64 or some bit twiddling
|
// NOTE: Can optimize with llvm.bitreverse.i64 or some bit twiddling
|
||||||
@@ -136,7 +136,7 @@ z_bit_reverse :: #force_inline proc(n: u16, bits: u8) -> (r: u16) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
grow_buffer :: proc(buf: ^[dynamic]u8) -> (err: compress.Error) {
|
grow_buffer :: proc(buf: ^[dynamic]u8) -> (err: compress.Error) {
|
||||||
/*
|
/*
|
||||||
That we get here at all means that we didn't pass an expected output size,
|
That we get here at all means that we didn't pass an expected output size,
|
||||||
@@ -154,7 +154,7 @@ grow_buffer :: proc(buf: ^[dynamic]u8) -> (err: compress.Error) {
|
|||||||
TODO: Make these return compress.Error.
|
TODO: Make these return compress.Error.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
write_byte :: #force_inline proc(z: ^$C, c: u8) -> (err: io.Error) #no_bounds_check {
|
write_byte :: #force_inline proc(z: ^$C, c: u8) -> (err: io.Error) #no_bounds_check {
|
||||||
/*
|
/*
|
||||||
Resize if needed.
|
Resize if needed.
|
||||||
@@ -173,7 +173,7 @@ write_byte :: #force_inline proc(z: ^$C, c: u8) -> (err: io.Error) #no_bounds_ch
|
|||||||
return .None
|
return .None
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
repl_byte :: proc(z: ^$C, count: u16, c: u8) -> (err: io.Error) #no_bounds_check {
|
repl_byte :: proc(z: ^$C, count: u16, c: u8) -> (err: io.Error) #no_bounds_check {
|
||||||
/*
|
/*
|
||||||
TODO(Jeroen): Once we have a magic ring buffer, we can just peek/write into it
|
TODO(Jeroen): Once we have a magic ring buffer, we can just peek/write into it
|
||||||
@@ -201,7 +201,7 @@ repl_byte :: proc(z: ^$C, count: u16, c: u8) -> (err: io.Error) #no_bounds_check
|
|||||||
return .None
|
return .None
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
repl_bytes :: proc(z: ^$C, count: u16, distance: u16) -> (err: io.Error) {
|
repl_bytes :: proc(z: ^$C, count: u16, distance: u16) -> (err: io.Error) {
|
||||||
/*
|
/*
|
||||||
TODO(Jeroen): Once we have a magic ring buffer, we can just peek/write into it
|
TODO(Jeroen): Once we have a magic ring buffer, we can just peek/write into it
|
||||||
@@ -234,7 +234,7 @@ allocate_huffman_table :: proc(allocator := context.allocator) -> (z: ^Huffman_T
|
|||||||
return new(Huffman_Table, allocator), nil
|
return new(Huffman_Table, allocator), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
|
build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
|
||||||
sizes: [HUFFMAN_MAX_BITS+1]int
|
sizes: [HUFFMAN_MAX_BITS+1]int
|
||||||
next_code: [HUFFMAN_MAX_BITS+1]int
|
next_code: [HUFFMAN_MAX_BITS+1]int
|
||||||
@@ -293,7 +293,7 @@ build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
decode_huffman_slowpath :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
|
decode_huffman_slowpath :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
|
||||||
code := u16(compress.peek_bits_lsb(z,16))
|
code := u16(compress.peek_bits_lsb(z,16))
|
||||||
|
|
||||||
@@ -324,7 +324,7 @@ decode_huffman_slowpath :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Erro
|
|||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
decode_huffman :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
|
decode_huffman :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
|
||||||
if z.num_bits < 16 {
|
if z.num_bits < 16 {
|
||||||
if z.num_bits > 63 {
|
if z.num_bits > 63 {
|
||||||
@@ -344,7 +344,7 @@ decode_huffman :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bo
|
|||||||
return decode_huffman_slowpath(z, t)
|
return decode_huffman_slowpath(z, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err: Error) #no_bounds_check {
|
parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err: Error) #no_bounds_check {
|
||||||
#no_bounds_check for {
|
#no_bounds_check for {
|
||||||
value, e := decode_huffman(z, z_repeat)
|
value, e := decode_huffman(z, z_repeat)
|
||||||
@@ -413,7 +413,7 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := false, expected_output_size := -1, allocator := context.allocator) -> (err: Error) #no_bounds_check {
|
inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := false, expected_output_size := -1, allocator := context.allocator) -> (err: Error) #no_bounds_check {
|
||||||
/*
|
/*
|
||||||
ctx.output must be a bytes.Buffer for now. We'll add a separate implementation that writes to a stream.
|
ctx.output must be a bytes.Buffer for now. We'll add a separate implementation that writes to a stream.
|
||||||
@@ -486,7 +486,7 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f
|
|||||||
|
|
||||||
// TODO: Check alignment of reserve/resize.
|
// TODO: Check alignment of reserve/resize.
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.allocator) -> (err: Error) #no_bounds_check {
|
inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.allocator) -> (err: Error) #no_bounds_check {
|
||||||
context.allocator = allocator
|
context.allocator = allocator
|
||||||
expected_output_size := expected_output_size
|
expected_output_size := expected_output_size
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ init_cmp :: proc(
|
|||||||
init_ordered :: proc(
|
init_ordered :: proc(
|
||||||
t: ^$T/Tree($Value),
|
t: ^$T/Tree($Value),
|
||||||
node_allocator := context.allocator,
|
node_allocator := context.allocator,
|
||||||
) where intrinsics.type_is_ordered_numeric(Value) {
|
) where intrinsics.type_is_ordered(Value) {
|
||||||
init_cmp(t, slice.cmp_proc(Value), node_allocator)
|
init_cmp(t, slice.cmp_proc(Value), node_allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -210,8 +210,11 @@ set :: proc(ba: ^Bit_Array, #any_int index: uint, set_to: bool = true, allocator
|
|||||||
|
|
||||||
ba.max_index = max(idx, ba.max_index)
|
ba.max_index = max(idx, ba.max_index)
|
||||||
|
|
||||||
if set_to{ ba.bits[leg_index] |= 1 << uint(bit_index) }
|
if set_to {
|
||||||
else { ba.bits[leg_index] &= ~(1 << uint(bit_index)) }
|
ba.bits[leg_index] |= 1 << uint(bit_index)
|
||||||
|
} else {
|
||||||
|
ba.bits[leg_index] &~= 1 << uint(bit_index)
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -253,7 +256,7 @@ Inputs:
|
|||||||
- index: Which bit in the array
|
- index: Which bit in the array
|
||||||
*/
|
*/
|
||||||
unsafe_unset :: proc(b: ^Bit_Array, bit: int) #no_bounds_check {
|
unsafe_unset :: proc(b: ^Bit_Array, bit: int) #no_bounds_check {
|
||||||
b.bits[bit >> INDEX_SHIFT] &= ~(1 << uint(bit & INDEX_MASK))
|
b.bits[bit >> INDEX_SHIFT] &~= 1 << uint(bit & INDEX_MASK)
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
|
A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
|
||||||
|
|||||||
@@ -70,8 +70,7 @@ set :: proc(c: ^$C/Cache($Key, $Value), key: Key, value: Value) -> runtime.Alloc
|
|||||||
if c.count == c.capacity {
|
if c.count == c.capacity {
|
||||||
e = c.tail
|
e = c.tail
|
||||||
_remove_node(c, e)
|
_remove_node(c, e)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
c.count += 1
|
c.count += 1
|
||||||
e = new(Node(Key, Value), c.node_allocator) or_return
|
e = new(Node(Key, Value), c.node_allocator) or_return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ pop_front_safe :: proc(q: ^$Q/Queue($T)) -> (elem: T, ok: bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push multiple elements to the front of the queue
|
// Push multiple elements to the back of the queue
|
||||||
push_back_elems :: proc(q: ^$Q/Queue($T), elems: ..T) -> (ok: bool, err: runtime.Allocator_Error) {
|
push_back_elems :: proc(q: ^$Q/Queue($T), elems: ..T) -> (ok: bool, err: runtime.Allocator_Error) {
|
||||||
n := uint(builtin.len(elems))
|
n := uint(builtin.len(elems))
|
||||||
if space(q^) < int(n) {
|
if space(q^) < int(n) {
|
||||||
@@ -241,7 +241,7 @@ clear :: proc(q: ^$Q/Queue($T)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Internal growinh procedure
|
// Internal growing procedure
|
||||||
_grow :: proc(q: ^$Q/Queue($T), min_capacity: uint = 0) -> runtime.Allocator_Error {
|
_grow :: proc(q: ^$Q/Queue($T), min_capacity: uint = 0) -> runtime.Allocator_Error {
|
||||||
new_capacity := max(min_capacity, uint(8), uint(builtin.len(q.data))*2)
|
new_capacity := max(min_capacity, uint(8), uint(builtin.len(q.data))*2)
|
||||||
n := uint(builtin.len(q.data))
|
n := uint(builtin.len(q.data))
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ init_cmp :: proc(t: ^$T/Tree($Key, $Value), cmp_fn: proc(a, b: Key) -> Ordering,
|
|||||||
|
|
||||||
// init_ordered initializes a tree containing ordered keys, with
|
// init_ordered initializes a tree containing ordered keys, with
|
||||||
// a comparison function that results in an ascending order sort.
|
// a comparison function that results in an ascending order sort.
|
||||||
init_ordered :: proc(t: ^$T/Tree($Key, $Value), node_allocator := context.allocator) where intrinsics.type_is_ordered_numeric(Key) {
|
init_ordered :: proc(t: ^$T/Tree($Key, $Value), node_allocator := context.allocator) where intrinsics.type_is_ordered(Key) {
|
||||||
init_cmp(t, slice.cmp_proc(Key), node_allocator)
|
init_cmp(t, slice.cmp_proc(Key), node_allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ ge_set_bytes :: proc "contextless" (ge: ^Group_Element, b: []byte) -> bool {
|
|||||||
if len(b) != 32 {
|
if len(b) != 32 {
|
||||||
intrinsics.trap()
|
intrinsics.trap()
|
||||||
}
|
}
|
||||||
b_ := transmute(^[32]byte)(raw_data(b))
|
b_ := (^[32]byte)(raw_data(b))
|
||||||
|
|
||||||
// Do the work in a scratch element, so that ge is unchanged on
|
// Do the work in a scratch element, so that ge is unchanged on
|
||||||
// failure.
|
// failure.
|
||||||
@@ -169,7 +169,7 @@ ge_bytes :: proc "contextless" (ge: ^Group_Element, dst: []byte) {
|
|||||||
if len(dst) != 32 {
|
if len(dst) != 32 {
|
||||||
intrinsics.trap()
|
intrinsics.trap()
|
||||||
}
|
}
|
||||||
dst_ := transmute(^[32]byte)(raw_data(dst))
|
dst_ := (^[32]byte)(raw_data(dst))
|
||||||
|
|
||||||
// Convert the element to affine (x, y) representation.
|
// Convert the element to affine (x, y) representation.
|
||||||
x, y, z_inv: field.Tight_Field_Element = ---, ---, ---
|
x, y, z_inv: field.Tight_Field_Element = ---, ---, ---
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ sc_set_bytes :: proc "contextless" (sc: ^Scalar, b: []byte) -> bool {
|
|||||||
if len(b) != 32 {
|
if len(b) != 32 {
|
||||||
intrinsics.trap()
|
intrinsics.trap()
|
||||||
}
|
}
|
||||||
b_ := transmute(^[32]byte)(raw_data(b))
|
b_ := (^[32]byte)(raw_data(b))
|
||||||
return field.fe_from_bytes(sc, b_)
|
return field.fe_from_bytes(sc, b_)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ sc_set_bytes_rfc8032 :: proc "contextless" (sc: ^Scalar, b: []byte) {
|
|||||||
if len(b) != 32 {
|
if len(b) != 32 {
|
||||||
intrinsics.trap()
|
intrinsics.trap()
|
||||||
}
|
}
|
||||||
b_ := transmute(^[32]byte)(raw_data(b))
|
b_ := (^[32]byte)(raw_data(b))
|
||||||
field.fe_from_bytes_rfc8032(sc, b_)
|
field.fe_from_bytes_rfc8032(sc, b_)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ import "core:mem"
|
|||||||
fe_relax_cast :: #force_inline proc "contextless" (
|
fe_relax_cast :: #force_inline proc "contextless" (
|
||||||
arg1: ^Tight_Field_Element,
|
arg1: ^Tight_Field_Element,
|
||||||
) -> ^Loose_Field_Element {
|
) -> ^Loose_Field_Element {
|
||||||
return transmute(^Loose_Field_Element)(arg1)
|
return (^Loose_Field_Element)(arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fe_tighten_cast :: #force_inline proc "contextless" (
|
fe_tighten_cast :: #force_inline proc "contextless" (
|
||||||
arg1: ^Loose_Field_Element,
|
arg1: ^Loose_Field_Element,
|
||||||
) -> ^Tight_Field_Element {
|
) -> ^Tight_Field_Element {
|
||||||
return transmute(^Tight_Field_Element)(arg1)
|
return (^Tight_Field_Element)(arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fe_clear :: proc "contextless" (
|
fe_clear :: proc "contextless" (
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ import "core:mem"
|
|||||||
fe_relax_cast :: #force_inline proc "contextless" (
|
fe_relax_cast :: #force_inline proc "contextless" (
|
||||||
arg1: ^Tight_Field_Element,
|
arg1: ^Tight_Field_Element,
|
||||||
) -> ^Loose_Field_Element {
|
) -> ^Loose_Field_Element {
|
||||||
return transmute(^Loose_Field_Element)(arg1)
|
return (^Loose_Field_Element)(arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fe_tighten_cast :: #force_inline proc "contextless" (
|
fe_tighten_cast :: #force_inline proc "contextless" (
|
||||||
arg1: ^Loose_Field_Element,
|
arg1: ^Loose_Field_Element,
|
||||||
) -> ^Tight_Field_Element {
|
) -> ^Tight_Field_Element {
|
||||||
return transmute(^Tight_Field_Element)(arg1)
|
return (^Tight_Field_Element)(arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fe_from_bytes :: #force_inline proc "contextless" (
|
fe_from_bytes :: #force_inline proc "contextless" (
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ helper routines.
|
|||||||
*/
|
*/
|
||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
|
import "base:runtime"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
|
||||||
// compare_constant_time returns 1 iff a and b are equal, 0 otherwise.
|
// compare_constant_time returns 1 iff a and b are equal, 0 otherwise.
|
||||||
@@ -58,3 +59,24 @@ rand_bytes :: proc (dst: []byte) {
|
|||||||
|
|
||||||
_rand_bytes(dst)
|
_rand_bytes(dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
random_generator :: proc() -> runtime.Random_Generator {
|
||||||
|
return {
|
||||||
|
procedure = proc(data: rawptr, mode: runtime.Random_Generator_Mode, p: []byte) {
|
||||||
|
switch mode {
|
||||||
|
case .Read:
|
||||||
|
rand_bytes(p)
|
||||||
|
case .Reset:
|
||||||
|
// do nothing
|
||||||
|
case .Query_Info:
|
||||||
|
if len(p) != size_of(runtime.Random_Generator_Query_Info) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
info := (^runtime.Random_Generator_Query_Info)(raw_data(p))
|
||||||
|
info^ += {.Uniform, .Cryptographic, .External_Entropy}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data = nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ init_256 :: proc(ctx: ^Context, key, domain_sep: []byte) {
|
|||||||
update :: proc(ctx: ^Context, data: []byte) {
|
update :: proc(ctx: ^Context, data: []byte) {
|
||||||
assert(ctx.is_initialized)
|
assert(ctx.is_initialized)
|
||||||
|
|
||||||
shake.write(transmute(^shake.Context)(ctx), data)
|
shake.write((^shake.Context)(ctx), data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// final finalizes the Context, writes the tag to dst, and calls reset
|
// final finalizes the Context, writes the tag to dst, and calls reset
|
||||||
@@ -75,7 +75,7 @@ final :: proc(ctx: ^Context, dst: []byte) {
|
|||||||
panic("crypto/kmac: invalid KMAC tag_size, too short")
|
panic("crypto/kmac: invalid KMAC tag_size, too short")
|
||||||
}
|
}
|
||||||
|
|
||||||
_sha3.final_cshake(transmute(^_sha3.Context)(ctx), dst)
|
_sha3.final_cshake((^_sha3.Context)(ctx), dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
// clone clones the Context other into ctx.
|
// clone clones the Context other into ctx.
|
||||||
@@ -84,7 +84,7 @@ clone :: proc(ctx, other: ^Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
shake.clone(transmute(^shake.Context)(ctx), transmute(^shake.Context)(other))
|
shake.clone((^shake.Context)(ctx), (^shake.Context)(other))
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset sanitizes the Context. The Context must be re-initialized to
|
// reset sanitizes the Context. The Context must be re-initialized to
|
||||||
@@ -94,7 +94,7 @@ reset :: proc(ctx: ^Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
shake.reset(transmute(^shake.Context)(ctx))
|
shake.reset((^shake.Context)(ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
@@ -107,7 +107,7 @@ _init_kmac :: proc(ctx: ^Context, key, s: []byte, sec_strength: int) {
|
|||||||
panic("crypto/kmac: invalid KMAC key, too short")
|
panic("crypto/kmac: invalid KMAC key, too short")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx_ := transmute(^_sha3.Context)(ctx)
|
ctx_ := (^_sha3.Context)(ctx)
|
||||||
_sha3.init_cshake(ctx_, N_KMAC, s, sec_strength)
|
_sha3.init_cshake(ctx_, N_KMAC, s, sec_strength)
|
||||||
_sha3.bytepad(ctx_, [][]byte{key}, _sha3.rate_cshake(sec_strength))
|
_sha3.bytepad(ctx_, [][]byte{key}, _sha3.rate_cshake(sec_strength))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,12 +66,12 @@ init_512 :: proc(ctx: ^Context) {
|
|||||||
@(private)
|
@(private)
|
||||||
_init :: proc(ctx: ^Context) {
|
_init :: proc(ctx: ^Context) {
|
||||||
ctx.dsbyte = _sha3.DS_KECCAK
|
ctx.dsbyte = _sha3.DS_KECCAK
|
||||||
_sha3.init(transmute(^_sha3.Context)(ctx))
|
_sha3.init((^_sha3.Context)(ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
// update adds more data to the Context.
|
// update adds more data to the Context.
|
||||||
update :: proc(ctx: ^Context, data: []byte) {
|
update :: proc(ctx: ^Context, data: []byte) {
|
||||||
_sha3.update(transmute(^_sha3.Context)(ctx), data)
|
_sha3.update((^_sha3.Context)(ctx), data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// final finalizes the Context, writes the digest to hash, and calls
|
// final finalizes the Context, writes the digest to hash, and calls
|
||||||
@@ -80,16 +80,16 @@ update :: proc(ctx: ^Context, data: []byte) {
|
|||||||
// Iff finalize_clone is set, final will work on a copy of the Context,
|
// Iff finalize_clone is set, final will work on a copy of the Context,
|
||||||
// which is useful for for calculating rolling digests.
|
// which is useful for for calculating rolling digests.
|
||||||
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
||||||
_sha3.final(transmute(^_sha3.Context)(ctx), hash, finalize_clone)
|
_sha3.final((^_sha3.Context)(ctx), hash, finalize_clone)
|
||||||
}
|
}
|
||||||
|
|
||||||
// clone clones the Context other into ctx.
|
// clone clones the Context other into ctx.
|
||||||
clone :: proc(ctx, other: ^Context) {
|
clone :: proc(ctx, other: ^Context) {
|
||||||
_sha3.clone(transmute(^_sha3.Context)(ctx), transmute(^_sha3.Context)(other))
|
_sha3.clone((^_sha3.Context)(ctx), (^_sha3.Context)(other))
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset sanitizes the Context. The Context must be re-initialized to
|
// reset sanitizes the Context. The Context must be re-initialized to
|
||||||
// be used again.
|
// be used again.
|
||||||
reset :: proc(ctx: ^Context) {
|
reset :: proc(ctx: ^Context) {
|
||||||
_sha3.reset(transmute(^_sha3.Context)(ctx))
|
_sha3.reset((^_sha3.Context)(ctx))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ ge_set_bytes :: proc "contextless" (ge: ^Group_Element, b: []byte) -> bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
b_ := transmute(^[32]byte)(raw_data(b))
|
b_ := (^[32]byte)(raw_data(b))
|
||||||
|
|
||||||
s: field.Tight_Field_Element = ---
|
s: field.Tight_Field_Element = ---
|
||||||
defer field.fe_clear(&s)
|
defer field.fe_clear(&s)
|
||||||
@@ -297,7 +297,7 @@ ge_bytes :: proc(ge: ^Group_Element, dst: []byte) {
|
|||||||
// 2. Return the 32-byte little-endian encoding of s. More
|
// 2. Return the 32-byte little-endian encoding of s. More
|
||||||
// specifically, this is the encoding of the canonical
|
// specifically, this is the encoding of the canonical
|
||||||
// representation of s as an integer between 0 and p-1, inclusive.
|
// representation of s as an integer between 0 and p-1, inclusive.
|
||||||
dst_ := transmute(^[32]byte)(raw_data(dst))
|
dst_ := (^[32]byte)(raw_data(dst))
|
||||||
field.fe_to_bytes(dst_, &tmp)
|
field.fe_to_bytes(dst_, &tmp)
|
||||||
|
|
||||||
field.fe_clear_vec([]^field.Tight_Field_Element{&u1, &u2, &tmp, &z_inv, &ix0, &iy0, &x, &y})
|
field.fe_clear_vec([]^field.Tight_Field_Element{&u1, &u2, &tmp, &z_inv, &ix0, &iy0, &x, &y})
|
||||||
@@ -417,7 +417,7 @@ ge_is_identity :: proc(ge: ^Group_Element) -> int {
|
|||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
ge_map :: proc "contextless" (ge: ^Group_Element, b: []byte) {
|
ge_map :: proc "contextless" (ge: ^Group_Element, b: []byte) {
|
||||||
b_ := transmute(^[32]byte)(raw_data(b))
|
b_ := (^[32]byte)(raw_data(b))
|
||||||
|
|
||||||
// The MAP function is defined on 32-byte strings as:
|
// The MAP function is defined on 32-byte strings as:
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ sc_set_bytes_wide :: proc(sc: ^Scalar, b: []byte) {
|
|||||||
panic("crypto/ristretto255: invalid wide input size")
|
panic("crypto/ristretto255: invalid wide input size")
|
||||||
}
|
}
|
||||||
|
|
||||||
b_ := transmute(^[WIDE_SCALAR_SIZE]byte)(raw_data(b))
|
b_ := (^[WIDE_SCALAR_SIZE]byte)(raw_data(b))
|
||||||
grp.sc_set_bytes_wide(sc, b_)
|
grp.sc_set_bytes_wide(sc, b_)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,12 +68,12 @@ init_512 :: proc(ctx: ^Context) {
|
|||||||
@(private)
|
@(private)
|
||||||
_init :: proc(ctx: ^Context) {
|
_init :: proc(ctx: ^Context) {
|
||||||
ctx.dsbyte = _sha3.DS_SHA3
|
ctx.dsbyte = _sha3.DS_SHA3
|
||||||
_sha3.init(transmute(^_sha3.Context)(ctx))
|
_sha3.init((^_sha3.Context)(ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
// update adds more data to the Context.
|
// update adds more data to the Context.
|
||||||
update :: proc(ctx: ^Context, data: []byte) {
|
update :: proc(ctx: ^Context, data: []byte) {
|
||||||
_sha3.update(transmute(^_sha3.Context)(ctx), data)
|
_sha3.update((^_sha3.Context)(ctx), data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// final finalizes the Context, writes the digest to hash, and calls
|
// final finalizes the Context, writes the digest to hash, and calls
|
||||||
@@ -82,16 +82,16 @@ update :: proc(ctx: ^Context, data: []byte) {
|
|||||||
// Iff finalize_clone is set, final will work on a copy of the Context,
|
// Iff finalize_clone is set, final will work on a copy of the Context,
|
||||||
// which is useful for for calculating rolling digests.
|
// which is useful for for calculating rolling digests.
|
||||||
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
||||||
_sha3.final(transmute(^_sha3.Context)(ctx), hash, finalize_clone)
|
_sha3.final((^_sha3.Context)(ctx), hash, finalize_clone)
|
||||||
}
|
}
|
||||||
|
|
||||||
// clone clones the Context other into ctx.
|
// clone clones the Context other into ctx.
|
||||||
clone :: proc(ctx, other: ^Context) {
|
clone :: proc(ctx, other: ^Context) {
|
||||||
_sha3.clone(transmute(^_sha3.Context)(ctx), transmute(^_sha3.Context)(other))
|
_sha3.clone((^_sha3.Context)(ctx), (^_sha3.Context)(other))
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset sanitizes the Context. The Context must be re-initialized to
|
// reset sanitizes the Context. The Context must be re-initialized to
|
||||||
// be used again.
|
// be used again.
|
||||||
reset :: proc(ctx: ^Context) {
|
reset :: proc(ctx: ^Context) {
|
||||||
_sha3.reset(transmute(^_sha3.Context)(ctx))
|
_sha3.reset((^_sha3.Context)(ctx))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,35 +24,35 @@ Context :: distinct _sha3.Context
|
|||||||
|
|
||||||
// init_128 initializes a Context for SHAKE128.
|
// init_128 initializes a Context for SHAKE128.
|
||||||
init_128 :: proc(ctx: ^Context) {
|
init_128 :: proc(ctx: ^Context) {
|
||||||
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), nil, nil, 128)
|
_sha3.init_cshake((^_sha3.Context)(ctx), nil, nil, 128)
|
||||||
}
|
}
|
||||||
|
|
||||||
// init_256 initializes a Context for SHAKE256.
|
// init_256 initializes a Context for SHAKE256.
|
||||||
init_256 :: proc(ctx: ^Context) {
|
init_256 :: proc(ctx: ^Context) {
|
||||||
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), nil, nil, 256)
|
_sha3.init_cshake((^_sha3.Context)(ctx), nil, nil, 256)
|
||||||
}
|
}
|
||||||
|
|
||||||
// init_cshake_128 initializes a Context for cSHAKE128.
|
// init_cshake_128 initializes a Context for cSHAKE128.
|
||||||
init_cshake_128 :: proc(ctx: ^Context, domain_sep: []byte) {
|
init_cshake_128 :: proc(ctx: ^Context, domain_sep: []byte) {
|
||||||
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), nil, domain_sep, 128)
|
_sha3.init_cshake((^_sha3.Context)(ctx), nil, domain_sep, 128)
|
||||||
}
|
}
|
||||||
|
|
||||||
// init_cshake_256 initializes a Context for cSHAKE256.
|
// init_cshake_256 initializes a Context for cSHAKE256.
|
||||||
init_cshake_256 :: proc(ctx: ^Context, domain_sep: []byte) {
|
init_cshake_256 :: proc(ctx: ^Context, domain_sep: []byte) {
|
||||||
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), nil, domain_sep, 256)
|
_sha3.init_cshake((^_sha3.Context)(ctx), nil, domain_sep, 256)
|
||||||
}
|
}
|
||||||
|
|
||||||
// write writes more data into the SHAKE instance. This MUST not be called
|
// write writes more data into the SHAKE instance. This MUST not be called
|
||||||
// after any reads have been done, and attempts to do so will panic.
|
// after any reads have been done, and attempts to do so will panic.
|
||||||
write :: proc(ctx: ^Context, data: []byte) {
|
write :: proc(ctx: ^Context, data: []byte) {
|
||||||
_sha3.update(transmute(^_sha3.Context)(ctx), data)
|
_sha3.update((^_sha3.Context)(ctx), data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// read reads output from the SHAKE instance. There is no practical upper
|
// read reads output from the SHAKE instance. There is no practical upper
|
||||||
// limit to the amount of data that can be read from SHAKE. After read has
|
// limit to the amount of data that can be read from SHAKE. After read has
|
||||||
// been called one or more times, further calls to write will panic.
|
// been called one or more times, further calls to write will panic.
|
||||||
read :: proc(ctx: ^Context, dst: []byte) {
|
read :: proc(ctx: ^Context, dst: []byte) {
|
||||||
ctx_ := transmute(^_sha3.Context)(ctx)
|
ctx_ := (^_sha3.Context)(ctx)
|
||||||
if !ctx.is_finalized {
|
if !ctx.is_finalized {
|
||||||
_sha3.shake_xof(ctx_)
|
_sha3.shake_xof(ctx_)
|
||||||
}
|
}
|
||||||
@@ -62,11 +62,11 @@ read :: proc(ctx: ^Context, dst: []byte) {
|
|||||||
|
|
||||||
// clone clones the Context other into ctx.
|
// clone clones the Context other into ctx.
|
||||||
clone :: proc(ctx, other: ^Context) {
|
clone :: proc(ctx, other: ^Context) {
|
||||||
_sha3.clone(transmute(^_sha3.Context)(ctx), transmute(^_sha3.Context)(other))
|
_sha3.clone((^_sha3.Context)(ctx), (^_sha3.Context)(other))
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset sanitizes the Context. The Context must be re-initialized to
|
// reset sanitizes the Context. The Context must be re-initialized to
|
||||||
// be used again.
|
// be used again.
|
||||||
reset :: proc(ctx: ^Context) {
|
reset :: proc(ctx: ^Context) {
|
||||||
_sha3.reset(transmute(^_sha3.Context)(ctx))
|
_sha3.reset((^_sha3.Context)(ctx))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,19 +13,19 @@ Context :: distinct _sha3.Context
|
|||||||
|
|
||||||
// init_128 initializes a Context for TupleHash128 or TupleHashXOF128.
|
// init_128 initializes a Context for TupleHash128 or TupleHashXOF128.
|
||||||
init_128 :: proc(ctx: ^Context, domain_sep: []byte) {
|
init_128 :: proc(ctx: ^Context, domain_sep: []byte) {
|
||||||
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), N_TUPLEHASH, domain_sep, 128)
|
_sha3.init_cshake((^_sha3.Context)(ctx), N_TUPLEHASH, domain_sep, 128)
|
||||||
}
|
}
|
||||||
|
|
||||||
// init_256 initializes a Context for TupleHash256 or TupleHashXOF256.
|
// init_256 initializes a Context for TupleHash256 or TupleHashXOF256.
|
||||||
init_256 :: proc(ctx: ^Context, domain_sep: []byte) {
|
init_256 :: proc(ctx: ^Context, domain_sep: []byte) {
|
||||||
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), N_TUPLEHASH, domain_sep, 256)
|
_sha3.init_cshake((^_sha3.Context)(ctx), N_TUPLEHASH, domain_sep, 256)
|
||||||
}
|
}
|
||||||
|
|
||||||
// write_element writes a tuple element into the TupleHash or TupleHashXOF
|
// write_element writes a tuple element into the TupleHash or TupleHashXOF
|
||||||
// instance. This MUST not be called after any reads have been done, and
|
// instance. This MUST not be called after any reads have been done, and
|
||||||
// any attempts to do so will panic.
|
// any attempts to do so will panic.
|
||||||
write_element :: proc(ctx: ^Context, data: []byte) {
|
write_element :: proc(ctx: ^Context, data: []byte) {
|
||||||
_, _ = _sha3.encode_string(transmute(^_sha3.Context)(ctx), data)
|
_, _ = _sha3.encode_string((^_sha3.Context)(ctx), data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// final finalizes the Context, writes the digest to hash, and calls
|
// final finalizes the Context, writes the digest to hash, and calls
|
||||||
@@ -34,7 +34,7 @@ write_element :: proc(ctx: ^Context, data: []byte) {
|
|||||||
// Iff finalize_clone is set, final will work on a copy of the Context,
|
// Iff finalize_clone is set, final will work on a copy of the Context,
|
||||||
// which is useful for for calculating rolling digests.
|
// which is useful for for calculating rolling digests.
|
||||||
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
||||||
_sha3.final_cshake(transmute(^_sha3.Context)(ctx), hash, finalize_clone)
|
_sha3.final_cshake((^_sha3.Context)(ctx), hash, finalize_clone)
|
||||||
}
|
}
|
||||||
|
|
||||||
// read reads output from the TupleHashXOF instance. There is no practical
|
// read reads output from the TupleHashXOF instance. There is no practical
|
||||||
@@ -42,7 +42,7 @@ final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
|||||||
// After read has been called one or more times, further calls to
|
// After read has been called one or more times, further calls to
|
||||||
// write_element will panic.
|
// write_element will panic.
|
||||||
read :: proc(ctx: ^Context, dst: []byte) {
|
read :: proc(ctx: ^Context, dst: []byte) {
|
||||||
ctx_ := transmute(^_sha3.Context)(ctx)
|
ctx_ := (^_sha3.Context)(ctx)
|
||||||
if !ctx.is_finalized {
|
if !ctx.is_finalized {
|
||||||
_sha3.encode_byte_len(ctx_, 0, false) // right_encode
|
_sha3.encode_byte_len(ctx_, 0, false) // right_encode
|
||||||
_sha3.shake_xof(ctx_)
|
_sha3.shake_xof(ctx_)
|
||||||
@@ -53,13 +53,13 @@ read :: proc(ctx: ^Context, dst: []byte) {
|
|||||||
|
|
||||||
// clone clones the Context other into ctx.
|
// clone clones the Context other into ctx.
|
||||||
clone :: proc(ctx, other: ^Context) {
|
clone :: proc(ctx, other: ^Context) {
|
||||||
_sha3.clone(transmute(^_sha3.Context)(ctx), transmute(^_sha3.Context)(other))
|
_sha3.clone((^_sha3.Context)(ctx), (^_sha3.Context)(other))
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset sanitizes the Context. The Context must be re-initialized to
|
// reset sanitizes the Context. The Context must be re-initialized to
|
||||||
// be used again.
|
// be used again.
|
||||||
reset :: proc(ctx: ^Context) {
|
reset :: proc(ctx: ^Context) {
|
||||||
_sha3.reset(transmute(^_sha3.Context)(ctx))
|
_sha3.reset((^_sha3.Context)(ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ _Context :: struct {
|
|||||||
|
|
||||||
@(private="package")
|
@(private="package")
|
||||||
_init :: proc(ctx: ^Context) -> (ok: bool) {
|
_init :: proc(ctx: ^Context) -> (ok: bool) {
|
||||||
defer if !ok do destroy(ctx)
|
defer if !ok { destroy(ctx) }
|
||||||
|
|
||||||
ctx.impl.state = backtrace_create_state("odin-debug-trace", 1, nil, ctx)
|
ctx.impl.state = backtrace_create_state("odin-debug-trace", 1, nil, ctx)
|
||||||
return ctx.impl.state != nil
|
return ctx.impl.state != nil
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
//+build !windows !linux !darwin
|
//+build !windows !linux !darwin
|
||||||
package debug_trace
|
package debug_trace
|
||||||
|
|
||||||
|
import "base:runtime"
|
||||||
|
|
||||||
_Context :: struct {
|
_Context :: struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10,9 +12,9 @@ _init :: proc(ctx: ^Context) -> (ok: bool) {
|
|||||||
_destroy :: proc(ctx: ^Context) -> bool {
|
_destroy :: proc(ctx: ^Context) -> bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
_frames :: proc(ctx: ^Context, skip: uint, allocator: runtime.Allocator) -> []Frame {
|
_frames :: proc(ctx: ^Context, skip: uint, frames_buffer: []Frame) -> []Frame {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
_resolve :: proc(ctx: ^Context, frame: Frame, allocator: runtime.Allocator) -> (result: runtime.Source_Code_Location) {
|
_resolve :: proc(ctx: ^Context, frame: Frame, allocator: runtime.Allocator) -> (result: Frame_Location) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-12
@@ -16,15 +16,12 @@ Library :: distinct rawptr
|
|||||||
Loads a dynamic library from the filesystem. The paramater `global_symbols` makes the symbols in the loaded
|
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.
|
library available to resolve references in subsequently loaded libraries.
|
||||||
|
|
||||||
The paramater `global_symbols` is only used for the platforms `linux`, `darwin`, `freebsd` and `openbsd`.
|
The parameter `global_symbols` is only used for the platforms `linux`, `darwin`, `freebsd` and `openbsd`.
|
||||||
On `windows` this paramater is ignored.
|
On `windows` this paramater is ignored.
|
||||||
|
|
||||||
The underlying behaviour is platform specific.
|
The underlying behaviour is platform specific.
|
||||||
On `linux`, `darwin`, `freebsd` and `openbsd` refer to `dlopen`.
|
On `linux`, `darwin`, `freebsd` and `openbsd` refer to `dlopen`.
|
||||||
On `windows` refer to `LoadLibraryW`.
|
On `windows` refer to `LoadLibraryW`. Also temporarily needs an allocator to convert a string.
|
||||||
|
|
||||||
**Implicit Allocators**
|
|
||||||
`context.temp_allocator`
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
import "core:dynlib"
|
import "core:dynlib"
|
||||||
@@ -79,10 +76,7 @@ Loads the address of a procedure/variable from a dynamic library.
|
|||||||
|
|
||||||
The underlying behaviour is platform specific.
|
The underlying behaviour is platform specific.
|
||||||
On `linux`, `darwin`, `freebsd` and `openbsd` refer to `dlsym`.
|
On `linux`, `darwin`, `freebsd` and `openbsd` refer to `dlsym`.
|
||||||
On `windows` refer to `GetProcAddress`.
|
On `windows` refer to `GetProcAddress`. Also temporarily needs an allocator to convert a string.
|
||||||
|
|
||||||
**Implicit Allocators**
|
|
||||||
`context.temp_allocator`
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
import "core:dynlib"
|
import "core:dynlib"
|
||||||
@@ -177,9 +171,7 @@ initialize_symbols :: proc(
|
|||||||
return count, count > 0
|
return count, count > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Returns an error message for the last failed procedure call.
|
||||||
Returns an error message for the last failed procedure call.
|
|
||||||
*/
|
|
||||||
last_error :: proc() -> string {
|
last_error :: proc() -> string {
|
||||||
return _last_error()
|
return _last_error()
|
||||||
}
|
}
|
||||||
@@ -4,14 +4,12 @@ package dynlib
|
|||||||
|
|
||||||
import win32 "core:sys/windows"
|
import win32 "core:sys/windows"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
import "base:runtime"
|
|
||||||
import "core:reflect"
|
import "core:reflect"
|
||||||
|
|
||||||
_load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
_load_library :: proc(path: string, global_symbols := false, allocator := context.temp_allocator) -> (Library, bool) {
|
||||||
// NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL
|
// NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL
|
||||||
|
wide_path := win32.utf8_to_wstring(path, allocator)
|
||||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
defer free(wide_path, allocator)
|
||||||
wide_path := win32.utf8_to_wstring(path, context.temp_allocator)
|
|
||||||
handle := cast(Library)win32.LoadLibraryW(wide_path)
|
handle := cast(Library)win32.LoadLibraryW(wide_path)
|
||||||
return handle, handle != nil
|
return handle, handle != nil
|
||||||
}
|
}
|
||||||
@@ -21,9 +19,9 @@ _unload_library :: proc(library: Library) -> bool {
|
|||||||
return bool(ok)
|
return bool(ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
_symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
|
_symbol_address :: proc(library: Library, symbol: string, allocator := context.temp_allocator) -> (ptr: rawptr, found: bool) {
|
||||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
c_str := strings.clone_to_cstring(symbol, allocator)
|
||||||
c_str := strings.clone_to_cstring(symbol, context.temp_allocator)
|
defer delete(c_str, allocator)
|
||||||
ptr = win32.GetProcAddress(cast(win32.HMODULE)library, c_str)
|
ptr = win32.GetProcAddress(cast(win32.HMODULE)library, c_str)
|
||||||
found = ptr != nil
|
found = ptr != nil
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -233,7 +233,7 @@ encode_into_encoder :: proc(e: Encoder, v: Value, loc := #caller_location) -> En
|
|||||||
|
|
||||||
if .Self_Described_CBOR in e.flags {
|
if .Self_Described_CBOR in e.flags {
|
||||||
_encode_u64(e, TAG_SELF_DESCRIBED_CBOR, .Tag) or_return
|
_encode_u64(e, TAG_SELF_DESCRIBED_CBOR, .Tag) or_return
|
||||||
e.flags &~= { .Self_Described_CBOR }
|
e.flags -= { .Self_Described_CBOR }
|
||||||
}
|
}
|
||||||
|
|
||||||
switch v_spec in v {
|
switch v_spec in v {
|
||||||
|
|||||||
@@ -77,8 +77,11 @@ You can look at the default tags provided for pointers on how these implementati
|
|||||||
Example:
|
Example:
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
|
||||||
import "core:encoding/cbor"
|
import "core:encoding/cbor"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
import "core:reflect"
|
||||||
import "core:time"
|
import "core:time"
|
||||||
|
|
||||||
Possibilities :: union {
|
Possibilities :: union {
|
||||||
@@ -93,9 +96,32 @@ Example:
|
|||||||
ignore_this: ^Data `cbor:"-"`, // Ignored by implementation.
|
ignore_this: ^Data `cbor:"-"`, // Ignored by implementation.
|
||||||
renamed: f32 `cbor:"renamed :)"`, // Renamed when encoded.
|
renamed: f32 `cbor:"renamed :)"`, // Renamed when encoded.
|
||||||
my_union: Possibilities, // Union support.
|
my_union: Possibilities, // Union support.
|
||||||
|
|
||||||
|
my_raw: [8]u32 `cbor_tag:"raw"`, // Custom tag that just writes the value as bytes.
|
||||||
}
|
}
|
||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
|
// Example custom tag implementation that instead of breaking down all parts,
|
||||||
|
// just writes the value as a big byte blob. This is an advanced feature but very powerful.
|
||||||
|
RAW_TAG_NR :: 200
|
||||||
|
cbor.tag_register_number({
|
||||||
|
marshal = proc(_: ^cbor.Tag_Implementation, e: cbor.Encoder, v: any) -> cbor.Marshal_Error {
|
||||||
|
cbor._encode_u8(e.writer, RAW_TAG_NR, .Tag) or_return
|
||||||
|
return cbor.err_conv(cbor._encode_bytes(e, reflect.as_bytes(v)))
|
||||||
|
},
|
||||||
|
unmarshal = proc(_: ^cbor.Tag_Implementation, d: cbor.Decoder, _: cbor.Tag_Number, v: any) -> (cbor.Unmarshal_Error) {
|
||||||
|
hdr := cbor._decode_header(d.reader) or_return
|
||||||
|
maj, add := cbor._header_split(hdr)
|
||||||
|
if maj != .Bytes {
|
||||||
|
return .Bad_Tag_Value
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes := cbor.err_conv(cbor._decode_bytes(d, add, maj)) or_return
|
||||||
|
intrinsics.mem_copy_non_overlapping(v.data, raw_data(bytes), len(bytes))
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}, RAW_TAG_NR, "raw")
|
||||||
|
|
||||||
now := time.Time{_nsec = 1701117968 * 1e9}
|
now := time.Time{_nsec = 1701117968 * 1e9}
|
||||||
|
|
||||||
data := Data{
|
data := Data{
|
||||||
@@ -105,21 +131,22 @@ Example:
|
|||||||
ignore_this = &Data{},
|
ignore_this = &Data{},
|
||||||
renamed = 123123.125,
|
renamed = 123123.125,
|
||||||
my_union = 3,
|
my_union = 3,
|
||||||
|
my_raw = {1=1, 2=2, 3=3},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marshal the struct into binary CBOR.
|
// Marshal the struct into binary CBOR.
|
||||||
binary, err := cbor.marshal(data, cbor.ENCODE_FULLY_DETERMINISTIC)
|
binary, err := cbor.marshal(data, cbor.ENCODE_FULLY_DETERMINISTIC)
|
||||||
assert(err == nil)
|
fmt.assertf(err == nil, "marshal error: %v", err)
|
||||||
defer delete(binary)
|
defer delete(binary)
|
||||||
|
|
||||||
// Decode the binary data into a `cbor.Value`.
|
// Decode the binary data into a `cbor.Value`.
|
||||||
decoded, derr := cbor.decode(string(binary))
|
decoded, derr := cbor.decode(string(binary))
|
||||||
assert(derr == nil)
|
fmt.assertf(derr == nil, "decode error: %v", derr)
|
||||||
defer cbor.destroy(decoded)
|
defer cbor.destroy(decoded)
|
||||||
|
|
||||||
// Turn the CBOR into a human readable representation defined as the diagnostic format in [[RFC 8949 Section 8;https://www.rfc-editor.org/rfc/rfc8949.html#name-diagnostic-notation]].
|
// Turn the CBOR into a human readable representation defined as the diagnostic format in [[RFC 8949 Section 8;https://www.rfc-editor.org/rfc/rfc8949.html#name-diagnostic-notation]].
|
||||||
diagnosis, eerr := cbor.to_diagnostic_format(decoded)
|
diagnosis, eerr := cbor.to_diagnostic_format(decoded)
|
||||||
assert(eerr == nil)
|
fmt.assertf(eerr == nil, "to diagnostic error: %v", eerr)
|
||||||
defer delete(diagnosis)
|
defer delete(diagnosis)
|
||||||
|
|
||||||
fmt.println(diagnosis)
|
fmt.println(diagnosis)
|
||||||
@@ -127,6 +154,7 @@ Example:
|
|||||||
|
|
||||||
Output:
|
Output:
|
||||||
{
|
{
|
||||||
|
"my_raw": 200(h'00001000200030000000000000000000'),
|
||||||
"my_union": 1010([
|
"my_union": 1010([
|
||||||
"int",
|
"int",
|
||||||
3
|
3
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ marshal_into_bytes :: proc(v: any, flags := ENCODE_SMALL, allocator := context.a
|
|||||||
|
|
||||||
defer if err != nil { strings.builder_destroy(&b) }
|
defer if err != nil { strings.builder_destroy(&b) }
|
||||||
|
|
||||||
if err = marshal_into_builder(&b, v, flags, temp_allocator, loc=loc); err != nil {
|
if err = marshal_into_builder(&b, v, flags, temp_allocator); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,20 +63,20 @@ marshal_into_bytes :: proc(v: any, flags := ENCODE_SMALL, allocator := context.a
|
|||||||
|
|
||||||
// Marshals the given value into a CBOR byte stream written to the given builder.
|
// Marshals the given value into a CBOR byte stream written to the given builder.
|
||||||
// See docs on the `marshal_into` proc group for more info.
|
// See docs on the `marshal_into` proc group for more info.
|
||||||
marshal_into_builder :: proc(b: ^strings.Builder, v: any, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator, loc := #caller_location) -> Marshal_Error {
|
marshal_into_builder :: proc(b: ^strings.Builder, v: any, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator) -> Marshal_Error {
|
||||||
return marshal_into_writer(strings.to_writer(b), v, flags, temp_allocator, loc=loc)
|
return marshal_into_writer(strings.to_writer(b), v, flags, temp_allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marshals the given value into a CBOR byte stream written to the given writer.
|
// Marshals the given value into a CBOR byte stream written to the given writer.
|
||||||
// See docs on the `marshal_into` proc group for more info.
|
// See docs on the `marshal_into` proc group for more info.
|
||||||
marshal_into_writer :: proc(w: io.Writer, v: any, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator, loc := #caller_location) -> Marshal_Error {
|
marshal_into_writer :: proc(w: io.Writer, v: any, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator) -> Marshal_Error {
|
||||||
encoder := Encoder{flags, w, temp_allocator}
|
encoder := Encoder{flags, w, temp_allocator}
|
||||||
return marshal_into_encoder(encoder, v, loc=loc)
|
return marshal_into_encoder(encoder, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marshals the given value into a CBOR byte stream written to the given encoder.
|
// Marshals the given value into a CBOR byte stream written to the given encoder.
|
||||||
// See docs on the `marshal_into` proc group for more info.
|
// See docs on the `marshal_into` proc group for more info.
|
||||||
marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (err: Marshal_Error) {
|
marshal_into_encoder :: proc(e: Encoder, v: any) -> (err: Marshal_Error) {
|
||||||
e := e
|
e := e
|
||||||
|
|
||||||
if e.temp_allocator.procedure == nil {
|
if e.temp_allocator.procedure == nil {
|
||||||
@@ -85,7 +85,7 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e
|
|||||||
|
|
||||||
if .Self_Described_CBOR in e.flags {
|
if .Self_Described_CBOR in e.flags {
|
||||||
err_conv(_encode_u64(e, TAG_SELF_DESCRIBED_CBOR, .Tag)) or_return
|
err_conv(_encode_u64(e, TAG_SELF_DESCRIBED_CBOR, .Tag)) or_return
|
||||||
e.flags &~= { .Self_Described_CBOR }
|
e.flags -= { .Self_Described_CBOR }
|
||||||
}
|
}
|
||||||
|
|
||||||
if v == nil {
|
if v == nil {
|
||||||
@@ -97,11 +97,14 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e
|
|||||||
return impl->marshal(e, v)
|
return impl->marshal(e, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
ti := runtime.type_info_base(type_info_of(v.id))
|
ti := runtime.type_info_core(type_info_of(v.id))
|
||||||
a := any{v.data, ti.id}
|
return _marshal_into_encoder(e, v, ti)
|
||||||
|
}
|
||||||
|
|
||||||
|
_marshal_into_encoder :: proc(e: Encoder, v: any, ti: ^runtime.Type_Info) -> (err: Marshal_Error) {
|
||||||
|
a := any{v.data, ti.id}
|
||||||
#partial switch info in ti.variant {
|
#partial switch info in ti.variant {
|
||||||
case runtime.Type_Info_Named:
|
case runtime.Type_Info_Named, runtime.Type_Info_Enum, runtime.Type_Info_Bit_Field:
|
||||||
unreachable()
|
unreachable()
|
||||||
|
|
||||||
case runtime.Type_Info_Pointer:
|
case runtime.Type_Info_Pointer:
|
||||||
@@ -223,18 +226,38 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e
|
|||||||
}
|
}
|
||||||
|
|
||||||
err_conv(_encode_u64(e, u64(info.count), .Array)) or_return
|
err_conv(_encode_u64(e, u64(info.count), .Array)) or_return
|
||||||
|
|
||||||
|
if impl, ok := _tag_implementations_type[info.elem.id]; ok {
|
||||||
for i in 0..<info.count {
|
for i in 0..<info.count {
|
||||||
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
||||||
marshal_into(e, any{rawptr(data), info.elem.id}) or_return
|
impl->marshal(e, any{rawptr(data), info.elem.id}) or_return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
elem_ti := runtime.type_info_core(type_info_of(info.elem.id))
|
||||||
|
for i in 0..<info.count {
|
||||||
|
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
||||||
|
_marshal_into_encoder(e, any{rawptr(data), info.elem.id}, elem_ti) or_return
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
||||||
case runtime.Type_Info_Enumerated_Array:
|
case runtime.Type_Info_Enumerated_Array:
|
||||||
// index := runtime.type_info_base(info.index).variant.(runtime.Type_Info_Enum)
|
// index := runtime.type_info_base(info.index).variant.(runtime.Type_Info_Enum)
|
||||||
err_conv(_encode_u64(e, u64(info.count), .Array)) or_return
|
err_conv(_encode_u64(e, u64(info.count), .Array)) or_return
|
||||||
|
|
||||||
|
if impl, ok := _tag_implementations_type[info.elem.id]; ok {
|
||||||
for i in 0..<info.count {
|
for i in 0..<info.count {
|
||||||
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
||||||
marshal_into(e, any{rawptr(data), info.elem.id}) or_return
|
impl->marshal(e, any{rawptr(data), info.elem.id}) or_return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
elem_ti := runtime.type_info_core(type_info_of(info.elem.id))
|
||||||
|
for i in 0..<info.count {
|
||||||
|
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
||||||
|
_marshal_into_encoder(e, any{rawptr(data), info.elem.id}, elem_ti) or_return
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -246,9 +269,19 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e
|
|||||||
|
|
||||||
array := (^mem.Raw_Dynamic_Array)(v.data)
|
array := (^mem.Raw_Dynamic_Array)(v.data)
|
||||||
err_conv(_encode_u64(e, u64(array.len), .Array)) or_return
|
err_conv(_encode_u64(e, u64(array.len), .Array)) or_return
|
||||||
|
|
||||||
|
if impl, ok := _tag_implementations_type[info.elem.id]; ok {
|
||||||
for i in 0..<array.len {
|
for i in 0..<array.len {
|
||||||
data := uintptr(array.data) + uintptr(i*info.elem_size)
|
data := uintptr(array.data) + uintptr(i*info.elem_size)
|
||||||
marshal_into(e, any{rawptr(data), info.elem.id}) or_return
|
impl->marshal(e, any{rawptr(data), info.elem.id}) or_return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
elem_ti := runtime.type_info_core(type_info_of(info.elem.id))
|
||||||
|
for i in 0..<array.len {
|
||||||
|
data := uintptr(array.data) + uintptr(i*info.elem_size)
|
||||||
|
_marshal_into_encoder(e, any{rawptr(data), info.elem.id}, elem_ti) or_return
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -260,9 +293,19 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e
|
|||||||
|
|
||||||
array := (^mem.Raw_Slice)(v.data)
|
array := (^mem.Raw_Slice)(v.data)
|
||||||
err_conv(_encode_u64(e, u64(array.len), .Array)) or_return
|
err_conv(_encode_u64(e, u64(array.len), .Array)) or_return
|
||||||
|
|
||||||
|
if impl, ok := _tag_implementations_type[info.elem.id]; ok {
|
||||||
for i in 0..<array.len {
|
for i in 0..<array.len {
|
||||||
data := uintptr(array.data) + uintptr(i*info.elem_size)
|
data := uintptr(array.data) + uintptr(i*info.elem_size)
|
||||||
marshal_into(e, any{rawptr(data), info.elem.id}) or_return
|
impl->marshal(e, any{rawptr(data), info.elem.id}) or_return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
elem_ti := runtime.type_info_core(type_info_of(info.elem.id))
|
||||||
|
for i in 0..<array.len {
|
||||||
|
data := uintptr(array.data) + uintptr(i*info.elem_size)
|
||||||
|
_marshal_into_encoder(e, any{rawptr(data), info.elem.id}, elem_ti) or_return
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -542,9 +585,6 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e
|
|||||||
|
|
||||||
return marshal_into(e, any{v.data, vti.id})
|
return marshal_into(e, any{v.data, vti.id})
|
||||||
|
|
||||||
case runtime.Type_Info_Enum:
|
|
||||||
return marshal_into(e, any{v.data, info.base.id})
|
|
||||||
|
|
||||||
case runtime.Type_Info_Bit_Set:
|
case runtime.Type_Info_Bit_Set:
|
||||||
// Store bit_set as big endian just like the protocol.
|
// Store bit_set as big endian just like the protocol.
|
||||||
do_byte_swap := !reflect.bit_set_is_big_endian(v)
|
do_byte_swap := !reflect.bit_set_is_big_endian(v)
|
||||||
|
|||||||
@@ -95,7 +95,6 @@ tag_register_number :: proc(impl: Tag_Implementation, nr: Tag_Number, id: string
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Controls initialization of default tag implementations.
|
// Controls initialization of default tag implementations.
|
||||||
// JS and WASI default to a panic allocator so we don't want to do it on those.
|
|
||||||
INITIALIZE_DEFAULT_TAGS :: #config(CBOR_INITIALIZE_DEFAULT_TAGS, !ODIN_DEFAULT_TO_PANIC_ALLOCATOR && !ODIN_DEFAULT_TO_NIL_ALLOCATOR)
|
INITIALIZE_DEFAULT_TAGS :: #config(CBOR_INITIALIZE_DEFAULT_TAGS, !ODIN_DEFAULT_TO_PANIC_ALLOCATOR && !ODIN_DEFAULT_TO_NIL_ALLOCATOR)
|
||||||
|
|
||||||
@(private, init, disabled=!INITIALIZE_DEFAULT_TAGS)
|
@(private, init, disabled=!INITIALIZE_DEFAULT_TAGS)
|
||||||
|
|||||||
@@ -520,9 +520,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
|||||||
return
|
return
|
||||||
|
|
||||||
case reflect.Type_Info_Array:
|
case reflect.Type_Info_Array:
|
||||||
_, scap := err_conv(_decode_len_container(d, add)) or_return
|
length, _ := err_conv(_decode_len_container(d, add)) or_return
|
||||||
length := min(scap, t.count)
|
|
||||||
|
|
||||||
if length > t.count {
|
if length > t.count {
|
||||||
return _unsupported(v, hdr)
|
return _unsupported(v, hdr)
|
||||||
}
|
}
|
||||||
@@ -534,9 +532,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
|||||||
return
|
return
|
||||||
|
|
||||||
case reflect.Type_Info_Enumerated_Array:
|
case reflect.Type_Info_Enumerated_Array:
|
||||||
_, scap := err_conv(_decode_len_container(d, add)) or_return
|
length, _ := err_conv(_decode_len_container(d, add)) or_return
|
||||||
length := min(scap, t.count)
|
|
||||||
|
|
||||||
if length > t.count {
|
if length > t.count {
|
||||||
return _unsupported(v, hdr)
|
return _unsupported(v, hdr)
|
||||||
}
|
}
|
||||||
@@ -548,9 +544,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
|||||||
return
|
return
|
||||||
|
|
||||||
case reflect.Type_Info_Complex:
|
case reflect.Type_Info_Complex:
|
||||||
_, scap := err_conv(_decode_len_container(d, add)) or_return
|
length, _ := err_conv(_decode_len_container(d, add)) or_return
|
||||||
length := min(scap, 2)
|
|
||||||
|
|
||||||
if length > 2 {
|
if length > 2 {
|
||||||
return _unsupported(v, hdr)
|
return _unsupported(v, hdr)
|
||||||
}
|
}
|
||||||
@@ -570,9 +564,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
|||||||
return
|
return
|
||||||
|
|
||||||
case reflect.Type_Info_Quaternion:
|
case reflect.Type_Info_Quaternion:
|
||||||
_, scap := err_conv(_decode_len_container(d, add)) or_return
|
length, _ := err_conv(_decode_len_container(d, add)) or_return
|
||||||
length := min(scap, 4)
|
|
||||||
|
|
||||||
if length > 4 {
|
if length > 4 {
|
||||||
return _unsupported(v, hdr)
|
return _unsupported(v, hdr)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -138,7 +138,9 @@ iterator_next :: proc(r: ^Reader) -> (record: []string, idx: int, err: Error, mo
|
|||||||
return record, r.line_count - 1, r.last_iterator_error, r.last_iterator_error == nil
|
return record, r.line_count - 1, r.last_iterator_error, r.last_iterator_error == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get last error if we the iterator
|
// Get last CSV parse error if we ignored it in the iterator loop
|
||||||
|
//
|
||||||
|
// for record, row_idx in csv.iterator_next(&r) { ... }
|
||||||
iterator_last_error :: proc(r: Reader) -> (err: Error) {
|
iterator_last_error :: proc(r: Reader) -> (err: Error) {
|
||||||
return r.last_iterator_error
|
return r.last_iterator_error
|
||||||
}
|
}
|
||||||
@@ -169,7 +171,7 @@ is_io_error :: proc(err: Error, io_err: io.Error) -> bool {
|
|||||||
|
|
||||||
// read_all reads all the remaining records from r.
|
// read_all reads all the remaining records from r.
|
||||||
// Each record is a slice of fields.
|
// Each record is a slice of fields.
|
||||||
// read_all is defined to read until an EOF, and does not treat, and does not treat EOF as an error
|
// read_all is defined to read until an EOF, and does not treat EOF as an error
|
||||||
@(require_results)
|
@(require_results)
|
||||||
read_all :: proc(r: ^Reader, allocator := context.allocator) -> ([][]string, Error) {
|
read_all :: proc(r: ^Reader, allocator := context.allocator) -> ([][]string, Error) {
|
||||||
context.allocator = allocator
|
context.allocator = allocator
|
||||||
|
|||||||
@@ -239,7 +239,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
|||||||
case runtime.Type_Info_Array:
|
case runtime.Type_Info_Array:
|
||||||
opt_write_start(w, opt, '[') or_return
|
opt_write_start(w, opt, '[') or_return
|
||||||
for i in 0..<info.count {
|
for i in 0..<info.count {
|
||||||
opt_write_iteration(w, opt, i) or_return
|
opt_write_iteration(w, opt, i == 0) or_return
|
||||||
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
||||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
||||||
}
|
}
|
||||||
@@ -248,7 +248,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
|||||||
case runtime.Type_Info_Enumerated_Array:
|
case runtime.Type_Info_Enumerated_Array:
|
||||||
opt_write_start(w, opt, '[') or_return
|
opt_write_start(w, opt, '[') or_return
|
||||||
for i in 0..<info.count {
|
for i in 0..<info.count {
|
||||||
opt_write_iteration(w, opt, i) or_return
|
opt_write_iteration(w, opt, i == 0) or_return
|
||||||
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
||||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
||||||
}
|
}
|
||||||
@@ -258,7 +258,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
|||||||
opt_write_start(w, opt, '[') or_return
|
opt_write_start(w, opt, '[') or_return
|
||||||
array := cast(^mem.Raw_Dynamic_Array)v.data
|
array := cast(^mem.Raw_Dynamic_Array)v.data
|
||||||
for i in 0..<array.len {
|
for i in 0..<array.len {
|
||||||
opt_write_iteration(w, opt, i) or_return
|
opt_write_iteration(w, opt, i == 0) or_return
|
||||||
data := uintptr(array.data) + uintptr(i*info.elem_size)
|
data := uintptr(array.data) + uintptr(i*info.elem_size)
|
||||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
||||||
}
|
}
|
||||||
@@ -268,7 +268,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
|||||||
opt_write_start(w, opt, '[') or_return
|
opt_write_start(w, opt, '[') or_return
|
||||||
slice := cast(^mem.Raw_Slice)v.data
|
slice := cast(^mem.Raw_Slice)v.data
|
||||||
for i in 0..<slice.len {
|
for i in 0..<slice.len {
|
||||||
opt_write_iteration(w, opt, i) or_return
|
opt_write_iteration(w, opt, i == 0) or_return
|
||||||
data := uintptr(slice.data) + uintptr(i*info.elem_size)
|
data := uintptr(slice.data) + uintptr(i*info.elem_size)
|
||||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
||||||
}
|
}
|
||||||
@@ -290,7 +290,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
|||||||
for bucket_index in 0..<map_cap {
|
for bucket_index in 0..<map_cap {
|
||||||
runtime.map_hash_is_valid(hs[bucket_index]) or_continue
|
runtime.map_hash_is_valid(hs[bucket_index]) or_continue
|
||||||
|
|
||||||
opt_write_iteration(w, opt, i) or_return
|
opt_write_iteration(w, opt, i == 0) or_return
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
key := rawptr(runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index))
|
key := rawptr(runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index))
|
||||||
@@ -356,7 +356,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
|||||||
slice.sort_by(sorted[:], proc(i, j: Entry) -> bool { return i.key < j.key })
|
slice.sort_by(sorted[:], proc(i, j: Entry) -> bool { return i.key < j.key })
|
||||||
|
|
||||||
for s, i in sorted {
|
for s, i in sorted {
|
||||||
opt_write_iteration(w, opt, i) or_return
|
opt_write_iteration(w, opt, i == 0) or_return
|
||||||
opt_write_key(w, opt, s.key) or_return
|
opt_write_key(w, opt, s.key) or_return
|
||||||
marshal_to_writer(w, s.value, opt) or_return
|
marshal_to_writer(w, s.value, opt) or_return
|
||||||
}
|
}
|
||||||
@@ -405,6 +405,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
|||||||
marshal_struct_fields :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: Marshal_Error) {
|
marshal_struct_fields :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: Marshal_Error) {
|
||||||
ti := runtime.type_info_base(type_info_of(v.id))
|
ti := runtime.type_info_base(type_info_of(v.id))
|
||||||
info := ti.variant.(runtime.Type_Info_Struct)
|
info := ti.variant.(runtime.Type_Info_Struct)
|
||||||
|
first_iteration := true
|
||||||
for name, i in info.names {
|
for name, i in info.names {
|
||||||
omitempty := false
|
omitempty := false
|
||||||
|
|
||||||
@@ -424,7 +425,8 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
opt_write_iteration(w, opt, i) or_return
|
opt_write_iteration(w, opt, first_iteration) or_return
|
||||||
|
first_iteration = false
|
||||||
if json_name != "" {
|
if json_name != "" {
|
||||||
opt_write_key(w, opt, json_name) or_return
|
opt_write_key(w, opt, json_name) or_return
|
||||||
} else {
|
} else {
|
||||||
@@ -588,10 +590,10 @@ opt_write_start :: proc(w: io.Writer, opt: ^Marshal_Options, c: byte) -> (err: i
|
|||||||
}
|
}
|
||||||
|
|
||||||
// insert comma separation and write indentations
|
// insert comma separation and write indentations
|
||||||
opt_write_iteration :: proc(w: io.Writer, opt: ^Marshal_Options, iteration: int) -> (err: io.Error) {
|
opt_write_iteration :: proc(w: io.Writer, opt: ^Marshal_Options, first_iteration: bool) -> (err: io.Error) {
|
||||||
switch opt.spec {
|
switch opt.spec {
|
||||||
case .JSON, .JSON5:
|
case .JSON, .JSON5:
|
||||||
if iteration > 0 {
|
if !first_iteration {
|
||||||
io.write_byte(w, ',') or_return
|
io.write_byte(w, ',') or_return
|
||||||
|
|
||||||
if opt.pretty {
|
if opt.pretty {
|
||||||
@@ -602,7 +604,7 @@ opt_write_iteration :: proc(w: io.Writer, opt: ^Marshal_Options, iteration: int)
|
|||||||
opt_write_indentation(w, opt) or_return
|
opt_write_indentation(w, opt) or_return
|
||||||
|
|
||||||
case .MJSON:
|
case .MJSON:
|
||||||
if iteration > 0 {
|
if !first_iteration {
|
||||||
// on pretty no commas necessary
|
// on pretty no commas necessary
|
||||||
if opt.pretty {
|
if opt.pretty {
|
||||||
io.write_byte(w, '\n') or_return
|
io.write_byte(w, '\n') or_return
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2024, Feoramund
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package uuid
|
||||||
|
|
||||||
|
// A RFC 4122 Universally Unique Identifier
|
||||||
|
Identifier :: distinct [16]u8
|
||||||
|
|
||||||
|
EXPECTED_LENGTH :: 8 + 4 + 4 + 4 + 12 + 4
|
||||||
|
|
||||||
|
VERSION_BYTE_INDEX :: 6
|
||||||
|
VARIANT_BYTE_INDEX :: 8
|
||||||
|
|
||||||
|
// The number of 100-nanosecond intervals between 1582-10-15 and 1970-01-01.
|
||||||
|
HNS_INTERVALS_BETWEEN_GREG_AND_UNIX :: 141427 * 24 * 60 * 60 * 1000 * 1000 * 10
|
||||||
|
|
||||||
|
VERSION_7_TIME_MASK :: 0xffffffff_ffff0000_00000000_00000000
|
||||||
|
VERSION_7_TIME_SHIFT :: 80
|
||||||
|
VERSION_7_COUNTER_MASK :: 0x00000000_00000fff_00000000_00000000
|
||||||
|
VERSION_7_COUNTER_SHIFT :: 64
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
NO_CSPRNG_ERROR :: "The context random generator is not cryptographic. See the documentation for an example of how to set one up."
|
||||||
|
@(private)
|
||||||
|
BIG_CLOCK_ERROR :: "The clock sequence can only hold 14 bits of data, therefore no number greater than 16,383 (0x3FFF)."
|
||||||
|
@(private)
|
||||||
|
VERSION_7_BIG_COUNTER_ERROR :: "This implementation of the version 7 UUID counter can only hold 12 bits of data, therefore no number greater than 4,095 (0xFFF)."
|
||||||
|
|
||||||
|
Read_Error :: enum {
|
||||||
|
None,
|
||||||
|
Invalid_Length,
|
||||||
|
Invalid_Hexadecimal,
|
||||||
|
Invalid_Separator,
|
||||||
|
}
|
||||||
|
|
||||||
|
Variant_Type :: enum {
|
||||||
|
Unknown,
|
||||||
|
Reserved_Apollo_NCS, // 0b0xx
|
||||||
|
RFC_4122, // 0b10x
|
||||||
|
Reserved_Microsoft_COM, // 0b110
|
||||||
|
Reserved_Future, // 0b111
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name string is a fully-qualified domain name.
|
||||||
|
@(rodata)
|
||||||
|
Namespace_DNS := Identifier {
|
||||||
|
0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1,
|
||||||
|
0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name string is a URL.
|
||||||
|
@(rodata)
|
||||||
|
Namespace_URL := Identifier {
|
||||||
|
0x6b, 0xa7, 0xb8, 0x11, 0x9d, 0xad, 0x11, 0xd1,
|
||||||
|
0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name string is an ISO OID.
|
||||||
|
@(rodata)
|
||||||
|
Namespace_OID := Identifier {
|
||||||
|
0x6b, 0xa7, 0xb8, 0x12, 0x9d, 0xad, 0x11, 0xd1,
|
||||||
|
0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name string is an X.500 DN (in DER or a text output format).
|
||||||
|
@(rodata)
|
||||||
|
Namespace_X500 := Identifier {
|
||||||
|
0x6b, 0xa7, 0xb8, 0x14, 0x9d, 0xad, 0x11, 0xd1,
|
||||||
|
0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
package uuid implements Universally Unique Identifiers according to the
|
||||||
|
standard originally outlined in RFC 4122 with additions from RFC 9562.
|
||||||
|
|
||||||
|
The UUIDs are textually represented and read in the following string format:
|
||||||
|
`00000000-0000-v000-V000-000000000000`
|
||||||
|
|
||||||
|
`v` is where the version bits reside, and `V` is where the variant bits reside.
|
||||||
|
The meaning of the other bits is version-dependent.
|
||||||
|
|
||||||
|
Outside of string representations, UUIDs are represented in memory by a 128-bit
|
||||||
|
structure organized as an array of 16 bytes.
|
||||||
|
|
||||||
|
|
||||||
|
Of the UUID versions which may make use of random number generation, a
|
||||||
|
requirement is placed upon them that the underlying generator be
|
||||||
|
cryptographically-secure, per RFC 9562's suggestion.
|
||||||
|
|
||||||
|
- Version 1 without a node argument.
|
||||||
|
- Version 4 in all cases.
|
||||||
|
- Version 6 without either a clock or node argument.
|
||||||
|
- Version 7 in all cases.
|
||||||
|
|
||||||
|
Here's an example of how to set up one:
|
||||||
|
|
||||||
|
import "core:crypto"
|
||||||
|
import "core:encoding/uuid"
|
||||||
|
|
||||||
|
main :: proc() {
|
||||||
|
my_uuid: uuid.Identifier
|
||||||
|
|
||||||
|
{
|
||||||
|
// This scope will have a CSPRNG.
|
||||||
|
context.random_generator = crypto.random_generator()
|
||||||
|
my_uuid = uuid.generate_v7()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Back to the default random number generator.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
For more information on the specifications, see here:
|
||||||
|
- https://www.rfc-editor.org/rfc/rfc4122.html
|
||||||
|
- https://www.rfc-editor.org/rfc/rfc9562.html
|
||||||
|
*/
|
||||||
|
package uuid
|
||||||
@@ -0,0 +1,333 @@
|
|||||||
|
package uuid
|
||||||
|
|
||||||
|
import "base:runtime"
|
||||||
|
import "core:crypto/hash"
|
||||||
|
import "core:math/rand"
|
||||||
|
import "core:time"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Generate a version 1 UUID.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- clock_seq: The clock sequence, a number which must be initialized to a random number once in the lifetime of a system.
|
||||||
|
- node: An optional 48-bit spatially unique identifier, specified to be the IEEE 802 address of the system.
|
||||||
|
If one is not provided or available, 48 bits of random state will take its place.
|
||||||
|
- timestamp: A timestamp from the `core:time` package, or `nil` to use the current time.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- result: The generated UUID.
|
||||||
|
*/
|
||||||
|
generate_v1 :: proc(clock_seq: u16, node: Maybe([6]u8) = nil, timestamp: Maybe(time.Time) = nil) -> (result: Identifier) {
|
||||||
|
assert(clock_seq <= 0x3FFF, BIG_CLOCK_ERROR)
|
||||||
|
unix_time_in_hns_intervals := time.to_unix_nanoseconds(timestamp.? or_else time.now()) / 100
|
||||||
|
|
||||||
|
uuid_timestamp := cast(u64le)(HNS_INTERVALS_BETWEEN_GREG_AND_UNIX + unix_time_in_hns_intervals)
|
||||||
|
uuid_timestamp_octets := transmute([8]u8)uuid_timestamp
|
||||||
|
|
||||||
|
result[0] = uuid_timestamp_octets[0]
|
||||||
|
result[1] = uuid_timestamp_octets[1]
|
||||||
|
result[2] = uuid_timestamp_octets[2]
|
||||||
|
result[3] = uuid_timestamp_octets[3]
|
||||||
|
result[4] = uuid_timestamp_octets[4]
|
||||||
|
result[5] = uuid_timestamp_octets[5]
|
||||||
|
|
||||||
|
result[6] = uuid_timestamp_octets[6] >> 4
|
||||||
|
result[7] = uuid_timestamp_octets[6] << 4 | uuid_timestamp_octets[7]
|
||||||
|
|
||||||
|
if realized_node, ok := node.?; ok {
|
||||||
|
mutable_node := realized_node
|
||||||
|
runtime.mem_copy_non_overlapping(&result[10], &mutable_node[0], 6)
|
||||||
|
} else {
|
||||||
|
assert(.Cryptographic in runtime.random_generator_query_info(context.random_generator), NO_CSPRNG_ERROR)
|
||||||
|
bytes_generated := rand.read(result[10:])
|
||||||
|
assert(bytes_generated == 6, "RNG failed to generate 6 bytes for UUID v1.")
|
||||||
|
}
|
||||||
|
|
||||||
|
result[VERSION_BYTE_INDEX] |= 0x10
|
||||||
|
result[VARIANT_BYTE_INDEX] |= 0x80
|
||||||
|
|
||||||
|
result[8] |= cast(u8)(clock_seq & 0x3F00 >> 8)
|
||||||
|
result[9] = cast(u8)clock_seq
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Generate a version 4 UUID.
|
||||||
|
|
||||||
|
This UUID will be pseudorandom, save for 6 pre-determined version and variant bits.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- result: The generated UUID.
|
||||||
|
*/
|
||||||
|
generate_v4 :: proc() -> (result: Identifier) {
|
||||||
|
assert(.Cryptographic in runtime.random_generator_query_info(context.random_generator), NO_CSPRNG_ERROR)
|
||||||
|
bytes_generated := rand.read(result[:])
|
||||||
|
assert(bytes_generated == 16, "RNG failed to generate 16 bytes for UUID v4.")
|
||||||
|
|
||||||
|
result[VERSION_BYTE_INDEX] &= 0x0F
|
||||||
|
result[VERSION_BYTE_INDEX] |= 0x40
|
||||||
|
|
||||||
|
result[VARIANT_BYTE_INDEX] &= 0x3F
|
||||||
|
result[VARIANT_BYTE_INDEX] |= 0x80
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Generate a version 6 UUID.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- clock_seq: The clock sequence from version 1, now made optional.
|
||||||
|
If unspecified, it will be replaced with random bits.
|
||||||
|
- node: An optional 48-bit spatially unique identifier, specified to be the IEEE 802 address of the system.
|
||||||
|
If one is not provided or available, 48 bits of random state will take its place.
|
||||||
|
- timestamp: A timestamp from the `core:time` package, or `nil` to use the current time.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- result: The generated UUID.
|
||||||
|
*/
|
||||||
|
generate_v6 :: proc(clock_seq: Maybe(u16) = nil, node: Maybe([6]u8) = nil, timestamp: Maybe(time.Time) = nil) -> (result: Identifier) {
|
||||||
|
unix_time_in_hns_intervals := time.to_unix_nanoseconds(timestamp.? or_else time.now()) / 100
|
||||||
|
|
||||||
|
uuid_timestamp := cast(u128be)(HNS_INTERVALS_BETWEEN_GREG_AND_UNIX + unix_time_in_hns_intervals)
|
||||||
|
|
||||||
|
result = transmute(Identifier)(
|
||||||
|
uuid_timestamp & 0x0FFFFFFF_FFFFF000 << 68 |
|
||||||
|
uuid_timestamp & 0x00000000_00000FFF << 64
|
||||||
|
)
|
||||||
|
|
||||||
|
if realized_clock_seq, ok := clock_seq.?; ok {
|
||||||
|
assert(realized_clock_seq <= 0x3FFF, BIG_CLOCK_ERROR)
|
||||||
|
result[8] |= cast(u8)(realized_clock_seq & 0x3F00 >> 8)
|
||||||
|
result[9] = cast(u8)realized_clock_seq
|
||||||
|
} else {
|
||||||
|
assert(.Cryptographic in runtime.random_generator_query_info(context.random_generator), NO_CSPRNG_ERROR)
|
||||||
|
temporary: [2]u8
|
||||||
|
bytes_generated := rand.read(temporary[:])
|
||||||
|
assert(bytes_generated == 2, "RNG failed to generate 2 bytes for UUID v1.")
|
||||||
|
result[8] |= temporary[0] & 0x3F
|
||||||
|
result[9] = temporary[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
if realized_node, ok := node.?; ok {
|
||||||
|
mutable_node := realized_node
|
||||||
|
runtime.mem_copy_non_overlapping(&result[10], &mutable_node[0], 6)
|
||||||
|
} else {
|
||||||
|
assert(.Cryptographic in runtime.random_generator_query_info(context.random_generator), NO_CSPRNG_ERROR)
|
||||||
|
bytes_generated := rand.read(result[10:])
|
||||||
|
assert(bytes_generated == 6, "RNG failed to generate 6 bytes for UUID v1.")
|
||||||
|
}
|
||||||
|
|
||||||
|
result[VERSION_BYTE_INDEX] |= 0x60
|
||||||
|
result[VARIANT_BYTE_INDEX] |= 0x80
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Generate a version 7 UUID.
|
||||||
|
|
||||||
|
This UUID will be pseudorandom, save for 6 pre-determined version and variant
|
||||||
|
bits and a 48-bit timestamp.
|
||||||
|
|
||||||
|
It is designed with time-based sorting in mind, such as for database usage, as
|
||||||
|
the highest bits are allocated from the timestamp of when it is created.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- timestamp: A timestamp from the `core:time` package, or `nil` to use the current time.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- result: The generated UUID.
|
||||||
|
*/
|
||||||
|
generate_v7_basic :: proc(timestamp: Maybe(time.Time) = nil) -> (result: Identifier) {
|
||||||
|
assert(.Cryptographic in runtime.random_generator_query_info(context.random_generator), NO_CSPRNG_ERROR)
|
||||||
|
unix_time_in_milliseconds := time.to_unix_nanoseconds(timestamp.? or_else time.now()) / 1e6
|
||||||
|
|
||||||
|
result = transmute(Identifier)(cast(u128be)unix_time_in_milliseconds << VERSION_7_TIME_SHIFT)
|
||||||
|
|
||||||
|
bytes_generated := rand.read(result[6:])
|
||||||
|
assert(bytes_generated == 10, "RNG failed to generate 10 bytes for UUID v7.")
|
||||||
|
|
||||||
|
result[VERSION_BYTE_INDEX] &= 0x0F
|
||||||
|
result[VERSION_BYTE_INDEX] |= 0x70
|
||||||
|
|
||||||
|
result[VARIANT_BYTE_INDEX] &= 0x3F
|
||||||
|
result[VARIANT_BYTE_INDEX] |= 0x80
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Generate a version 7 UUID that has an incremented counter.
|
||||||
|
|
||||||
|
This UUID will be pseudorandom, save for 6 pre-determined version and variant
|
||||||
|
bits, a 48-bit timestamp, and 12 bits of counter state.
|
||||||
|
|
||||||
|
It is designed with time-based sorting in mind, such as for database usage, as
|
||||||
|
the highest bits are allocated from the timestamp of when it is created.
|
||||||
|
|
||||||
|
This procedure is preferable if you are generating hundreds or thousands of
|
||||||
|
UUIDs as a batch within the span of a millisecond. Do note that the counter
|
||||||
|
only has 12 bits of state, thus `counter` cannot exceed the number 4,095.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
import "core:uuid"
|
||||||
|
|
||||||
|
// Create a batch of UUIDs all at once.
|
||||||
|
batch: [dynamic]uuid.Identifier
|
||||||
|
|
||||||
|
for i: u16 = 0; i < 1000; i += 1 {
|
||||||
|
my_uuid := uuid.generate_v7_counter(i)
|
||||||
|
append(&batch, my_uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- counter: A 12-bit value which should be incremented each time a UUID is generated in a batch.
|
||||||
|
- timestamp: A timestamp from the `core:time` package, or `nil` to use the current time.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- result: The generated UUID.
|
||||||
|
*/
|
||||||
|
generate_v7_with_counter :: proc(counter: u16, timestamp: Maybe(time.Time) = nil) -> (result: Identifier) {
|
||||||
|
assert(.Cryptographic in runtime.random_generator_query_info(context.random_generator), NO_CSPRNG_ERROR)
|
||||||
|
assert(counter <= 0x0fff, VERSION_7_BIG_COUNTER_ERROR)
|
||||||
|
unix_time_in_milliseconds := time.to_unix_nanoseconds(timestamp.? or_else time.now()) / 1e6
|
||||||
|
|
||||||
|
result = transmute(Identifier)(
|
||||||
|
cast(u128be)unix_time_in_milliseconds << VERSION_7_TIME_SHIFT |
|
||||||
|
cast(u128be)counter << VERSION_7_COUNTER_SHIFT
|
||||||
|
)
|
||||||
|
|
||||||
|
bytes_generated := rand.read(result[8:])
|
||||||
|
assert(bytes_generated == 8, "RNG failed to generate 8 bytes for UUID v7.")
|
||||||
|
|
||||||
|
result[VERSION_BYTE_INDEX] &= 0x0F
|
||||||
|
result[VERSION_BYTE_INDEX] |= 0x70
|
||||||
|
|
||||||
|
result[VARIANT_BYTE_INDEX] &= 0x3F
|
||||||
|
result[VARIANT_BYTE_INDEX] |= 0x80
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_v7 :: proc {
|
||||||
|
generate_v7_basic,
|
||||||
|
generate_v7_with_counter,
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Generate a version 8 UUID using a specific hashing algorithm.
|
||||||
|
|
||||||
|
This UUID is generated by hashing a name with a namespace.
|
||||||
|
|
||||||
|
Note that all version 8 UUIDs are for experimental or vendor-specific use
|
||||||
|
cases, per the specification. This use case in particular is for offering a
|
||||||
|
non-legacy alternative to UUID versions 3 and 5.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- namespace: An `Identifier` that is used to represent the underlying namespace.
|
||||||
|
This can be any one of the `Namespace_*` values provided in this package.
|
||||||
|
- name: The byte slice which will be hashed with the namespace.
|
||||||
|
- algorithm: A hashing algorithm from `core:crypto/hash`.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- result: The generated UUID.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
import "core:crypto/hash"
|
||||||
|
import "core:encoding/uuid"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
main :: 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
3730f688-4bff-8dce-9cbf-74a3960c5703
|
||||||
|
|
||||||
|
*/
|
||||||
|
generate_v8_hash_bytes :: proc(
|
||||||
|
namespace: Identifier,
|
||||||
|
name: []byte,
|
||||||
|
algorithm: hash.Algorithm,
|
||||||
|
) -> (
|
||||||
|
result: Identifier,
|
||||||
|
) {
|
||||||
|
// 128 bytes should be enough for the foreseeable future.
|
||||||
|
digest: [128]byte
|
||||||
|
|
||||||
|
assert(hash.DIGEST_SIZES[algorithm] >= 16, "Per RFC 9562, the hashing algorithm used must generate a digest of 128 bits or larger.")
|
||||||
|
assert(hash.DIGEST_SIZES[algorithm] < len(digest), "Digest size is too small for this algorithm. The buffer must be increased.")
|
||||||
|
|
||||||
|
hash_context: hash.Context
|
||||||
|
hash.init(&hash_context, algorithm)
|
||||||
|
|
||||||
|
mutable_namespace := namespace
|
||||||
|
hash.update(&hash_context, mutable_namespace[:])
|
||||||
|
hash.update(&hash_context, name[:])
|
||||||
|
hash.final(&hash_context, digest[:])
|
||||||
|
|
||||||
|
runtime.mem_copy_non_overlapping(&result, &digest, 16)
|
||||||
|
|
||||||
|
result[VERSION_BYTE_INDEX] &= 0x0F
|
||||||
|
result[VERSION_BYTE_INDEX] |= 0x80
|
||||||
|
|
||||||
|
result[VARIANT_BYTE_INDEX] &= 0x3F
|
||||||
|
result[VARIANT_BYTE_INDEX] |= 0x80
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Generate a version 8 UUID using a specific hashing algorithm.
|
||||||
|
|
||||||
|
This UUID is generated by hashing a name with a namespace.
|
||||||
|
|
||||||
|
Note that all version 8 UUIDs are for experimental or vendor-specific use
|
||||||
|
cases, per the specification. This use case in particular is for offering a
|
||||||
|
non-legacy alternative to UUID versions 3 and 5.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- namespace: An `Identifier` that is used to represent the underlying namespace.
|
||||||
|
This can be any one of the `Namespace_*` values provided in this package.
|
||||||
|
- name: The string which will be hashed with the namespace.
|
||||||
|
- algorithm: A hashing algorithm from `core:crypto/hash`.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- result: The generated UUID.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
import "core:crypto/hash"
|
||||||
|
import "core:encoding/uuid"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
main :: 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
3730f688-4bff-8dce-9cbf-74a3960c5703
|
||||||
|
|
||||||
|
*/
|
||||||
|
generate_v8_hash_string :: proc(
|
||||||
|
namespace: Identifier,
|
||||||
|
name: string,
|
||||||
|
algorithm: hash.Algorithm,
|
||||||
|
) -> (
|
||||||
|
result: Identifier,
|
||||||
|
) {
|
||||||
|
return generate_v8_hash_bytes(namespace, transmute([]byte)name, algorithm)
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_v8_hash :: proc {
|
||||||
|
generate_v8_hash_bytes,
|
||||||
|
generate_v8_hash_string,
|
||||||
|
}
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
/*
|
||||||
|
package uuid/legacy implements versions 3 and 5 of UUID generation, both of
|
||||||
|
which are using hashing algorithms (MD5 and SHA1, respectively) that are known
|
||||||
|
these days to no longer be secure.
|
||||||
|
*/
|
||||||
|
package uuid_legacy
|
||||||
|
|
||||||
|
import "base:runtime"
|
||||||
|
import "core:crypto/legacy/md5"
|
||||||
|
import "core:crypto/legacy/sha1"
|
||||||
|
import "core:encoding/uuid"
|
||||||
|
|
||||||
|
Identifier :: uuid.Identifier
|
||||||
|
VERSION_BYTE_INDEX :: uuid.VERSION_BYTE_INDEX
|
||||||
|
VARIANT_BYTE_INDEX :: uuid.VARIANT_BYTE_INDEX
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Generate a version 3 UUID.
|
||||||
|
|
||||||
|
This UUID is generated with a MD5 hash of a name and a namespace.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- namespace: An `Identifier` that is used to represent the underlying namespace.
|
||||||
|
This can be any one of the `Namespace_*` values provided in the `uuid` package.
|
||||||
|
- name: The byte slice which will be hashed with the namespace.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- result: The generated UUID.
|
||||||
|
*/
|
||||||
|
generate_v3_bytes :: proc(
|
||||||
|
namespace: Identifier,
|
||||||
|
name: []byte,
|
||||||
|
) -> (
|
||||||
|
result: Identifier,
|
||||||
|
) {
|
||||||
|
namespace := namespace
|
||||||
|
|
||||||
|
ctx: md5.Context
|
||||||
|
md5.init(&ctx)
|
||||||
|
md5.update(&ctx, namespace[:])
|
||||||
|
md5.update(&ctx, name)
|
||||||
|
md5.final(&ctx, result[:])
|
||||||
|
|
||||||
|
result[VERSION_BYTE_INDEX] &= 0x0F
|
||||||
|
result[VERSION_BYTE_INDEX] |= 0x30
|
||||||
|
|
||||||
|
result[VARIANT_BYTE_INDEX] &= 0x3F
|
||||||
|
result[VARIANT_BYTE_INDEX] |= 0x80
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Generate a version 3 UUID.
|
||||||
|
|
||||||
|
This UUID is generated with a MD5 hash of a name and a namespace.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- namespace: An `Identifier` that is used to represent the underlying namespace.
|
||||||
|
This can be any one of the `Namespace_*` values provided in the `uuid` package.
|
||||||
|
- name: The string which will be hashed with the namespace.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- result: The generated UUID.
|
||||||
|
*/
|
||||||
|
generate_v3_string :: proc(
|
||||||
|
namespace: Identifier,
|
||||||
|
name: string,
|
||||||
|
) -> (
|
||||||
|
result: Identifier,
|
||||||
|
) {
|
||||||
|
return generate_v3_bytes(namespace, transmute([]byte)name)
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_v3 :: proc {
|
||||||
|
generate_v3_bytes,
|
||||||
|
generate_v3_string,
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Generate a version 5 UUID.
|
||||||
|
|
||||||
|
This UUID is generated with a SHA1 hash of a name and a namespace.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- namespace: An `Identifier` that is used to represent the underlying namespace.
|
||||||
|
This can be any one of the `Namespace_*` values provided in the `uuid` package.
|
||||||
|
- name: The byte slice which will be hashed with the namespace.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- result: The generated UUID.
|
||||||
|
*/
|
||||||
|
generate_v5_bytes :: proc(
|
||||||
|
namespace: Identifier,
|
||||||
|
name: []byte,
|
||||||
|
) -> (
|
||||||
|
result: Identifier,
|
||||||
|
) {
|
||||||
|
namespace := namespace
|
||||||
|
digest: [sha1.DIGEST_SIZE]byte
|
||||||
|
|
||||||
|
ctx: sha1.Context
|
||||||
|
sha1.init(&ctx)
|
||||||
|
sha1.update(&ctx, namespace[:])
|
||||||
|
sha1.update(&ctx, name)
|
||||||
|
sha1.final(&ctx, digest[:])
|
||||||
|
|
||||||
|
runtime.mem_copy_non_overlapping(&result, &digest, 16)
|
||||||
|
|
||||||
|
result[VERSION_BYTE_INDEX] &= 0x0F
|
||||||
|
result[VERSION_BYTE_INDEX] |= 0x50
|
||||||
|
|
||||||
|
result[VARIANT_BYTE_INDEX] &= 0x3F
|
||||||
|
result[VARIANT_BYTE_INDEX] |= 0x80
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Generate a version 5 UUID.
|
||||||
|
|
||||||
|
This UUID is generated with a SHA1 hash of a name and a namespace.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- namespace: An `Identifier` that is used to represent the underlying namespace.
|
||||||
|
This can be any one of the `Namespace_*` values provided in the `uuid` package.
|
||||||
|
- name: The string which will be hashed with the namespace.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- result: The generated UUID.
|
||||||
|
*/
|
||||||
|
generate_v5_string :: proc(
|
||||||
|
namespace: Identifier,
|
||||||
|
name: string,
|
||||||
|
) -> (
|
||||||
|
result: Identifier,
|
||||||
|
) {
|
||||||
|
return generate_v5_bytes(namespace, transmute([]byte)name)
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_v5 :: proc {
|
||||||
|
generate_v5_bytes,
|
||||||
|
generate_v5_string,
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,242 @@
|
|||||||
|
package uuid
|
||||||
|
|
||||||
|
import "base:runtime"
|
||||||
|
import "core:time"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Convert a string to a UUID.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- str: A string in the 8-4-4-4-12 format.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- id: The converted identifier, or `nil` if there is an error.
|
||||||
|
- error: A description of the error, or `nil` if successful.
|
||||||
|
*/
|
||||||
|
read :: proc "contextless" (str: string) -> (id: Identifier, error: Read_Error) #no_bounds_check {
|
||||||
|
// Only exact-length strings are acceptable.
|
||||||
|
if len(str) != EXPECTED_LENGTH {
|
||||||
|
return {}, .Invalid_Length
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check ahead to see if the separators are in the right places.
|
||||||
|
if str[8] != '-' || str[13] != '-' || str[18] != '-' || str[23] != '-' {
|
||||||
|
return {}, .Invalid_Separator
|
||||||
|
}
|
||||||
|
|
||||||
|
read_nibble :: proc "contextless" (nibble: u8) -> u8 {
|
||||||
|
switch nibble {
|
||||||
|
case '0' ..= '9':
|
||||||
|
return nibble - '0'
|
||||||
|
case 'A' ..= 'F':
|
||||||
|
return nibble - 'A' + 10
|
||||||
|
case 'a' ..= 'f':
|
||||||
|
return nibble - 'a' + 10
|
||||||
|
case:
|
||||||
|
// Return an error value.
|
||||||
|
return 0xFF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
index := 0
|
||||||
|
octet_index := 0
|
||||||
|
|
||||||
|
CHUNKS :: [5]int{8, 4, 4, 4, 12}
|
||||||
|
|
||||||
|
for chunk in CHUNKS {
|
||||||
|
for i := index; i < index + chunk; i += 2 {
|
||||||
|
high := read_nibble(str[i])
|
||||||
|
low := read_nibble(str[i + 1])
|
||||||
|
|
||||||
|
if high | low > 0xF {
|
||||||
|
return {}, .Invalid_Hexadecimal
|
||||||
|
}
|
||||||
|
|
||||||
|
id[octet_index] = low | high << 4
|
||||||
|
octet_index += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
index += chunk + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get the version of a UUID.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- id: The identifier.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- number: The version number.
|
||||||
|
*/
|
||||||
|
version :: proc "contextless" (id: Identifier) -> (number: int) #no_bounds_check {
|
||||||
|
return cast(int)(id[VERSION_BYTE_INDEX] & 0xF0 >> 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get the variant of a UUID.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- id: The identifier.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- variant: The variant type.
|
||||||
|
*/
|
||||||
|
variant :: proc "contextless" (id: Identifier) -> (variant: Variant_Type) #no_bounds_check {
|
||||||
|
switch {
|
||||||
|
case id[VARIANT_BYTE_INDEX] & 0x80 == 0:
|
||||||
|
return .Reserved_Apollo_NCS
|
||||||
|
case id[VARIANT_BYTE_INDEX] & 0xC0 == 0x80:
|
||||||
|
return .RFC_4122
|
||||||
|
case id[VARIANT_BYTE_INDEX] & 0xE0 == 0xC0:
|
||||||
|
return .Reserved_Microsoft_COM
|
||||||
|
case id[VARIANT_BYTE_INDEX] & 0xF0 == 0xE0:
|
||||||
|
return .Reserved_Future
|
||||||
|
case:
|
||||||
|
return .Unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get the clock sequence of a version 1 or version 6 UUID.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- id: The identifier.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- clock_seq: The 14-bit clock sequence field.
|
||||||
|
*/
|
||||||
|
clock_seq :: proc "contextless" (id: Identifier) -> (clock_seq: u16) {
|
||||||
|
return cast(u16)id[9] | cast(u16)id[8] & 0x3F << 8
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get the node of a version 1 or version 6 UUID.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- id: The identifier.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- node: The 48-bit spatially unique identifier.
|
||||||
|
*/
|
||||||
|
node :: proc "contextless" (id: Identifier) -> (node: [6]u8) {
|
||||||
|
mutable_id := id
|
||||||
|
runtime.mem_copy_non_overlapping(&node, &mutable_id[10], 6)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get the raw timestamp of a version 1 UUID.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- id: The identifier.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- timestamp: The timestamp, in 100-nanosecond intervals since 1582-10-15.
|
||||||
|
*/
|
||||||
|
raw_time_v1 :: proc "contextless" (id: Identifier) -> (timestamp: u64) {
|
||||||
|
timestamp_octets: [8]u8
|
||||||
|
|
||||||
|
timestamp_octets[0] = id[0]
|
||||||
|
timestamp_octets[1] = id[1]
|
||||||
|
timestamp_octets[2] = id[2]
|
||||||
|
timestamp_octets[3] = id[3]
|
||||||
|
timestamp_octets[4] = id[4]
|
||||||
|
timestamp_octets[5] = id[5]
|
||||||
|
|
||||||
|
timestamp_octets[6] = id[6] << 4 | id[7] >> 4
|
||||||
|
timestamp_octets[7] = id[7] & 0xF
|
||||||
|
|
||||||
|
return cast(u64)transmute(u64le)timestamp_octets
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get the timestamp of a version 1 UUID.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- id: The identifier.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- timestamp: The timestamp of the UUID.
|
||||||
|
*/
|
||||||
|
time_v1 :: proc "contextless" (id: Identifier) -> (timestamp: time.Time) {
|
||||||
|
return time.from_nanoseconds(cast(i64)(raw_time_v1(id) - HNS_INTERVALS_BETWEEN_GREG_AND_UNIX) * 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get the raw timestamp of a version 6 UUID.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- id: The identifier.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- timestamp: The timestamp, in 100-nanosecond intervals since 1582-10-15.
|
||||||
|
*/
|
||||||
|
raw_time_v6 :: proc "contextless" (id: Identifier) -> (timestamp: u64) {
|
||||||
|
temporary := transmute(u128be)id
|
||||||
|
|
||||||
|
timestamp |= cast(u64)(temporary & 0xFFFFFFFF_FFFF0000_00000000_00000000 >> 68)
|
||||||
|
timestamp |= cast(u64)(temporary & 0x00000000_00000FFF_00000000_00000000 >> 64)
|
||||||
|
|
||||||
|
return timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get the timestamp of a version 6 UUID.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- id: The identifier.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- timestamp: The timestamp, in 100-nanosecond intervals since 1582-10-15.
|
||||||
|
*/
|
||||||
|
time_v6 :: proc "contextless" (id: Identifier) -> (timestamp: time.Time) {
|
||||||
|
return time.from_nanoseconds(cast(i64)(raw_time_v6(id) - HNS_INTERVALS_BETWEEN_GREG_AND_UNIX) * 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get the raw timestamp of a version 7 UUID.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- id: The identifier.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- timestamp: The timestamp, in milliseconds since the UNIX epoch.
|
||||||
|
*/
|
||||||
|
raw_time_v7 :: proc "contextless" (id: Identifier) -> (timestamp: u64) {
|
||||||
|
time_bits := transmute(u128be)id & VERSION_7_TIME_MASK
|
||||||
|
return cast(u64)(time_bits >> VERSION_7_TIME_SHIFT)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get the timestamp of a version 7 UUID.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- id: The identifier.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- timestamp: The timestamp, in milliseconds since the UNIX epoch.
|
||||||
|
*/
|
||||||
|
time_v7 :: proc "contextless" (id: Identifier) -> (timestamp: time.Time) {
|
||||||
|
return time.from_nanoseconds(cast(i64)raw_time_v7(id) * 1e6)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get the 12-bit counter value of a version 7 UUID.
|
||||||
|
|
||||||
|
The UUID must have been generated with a counter, otherwise this procedure will
|
||||||
|
return random bits.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- id: The identifier.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- counter: The 12-bit counter value.
|
||||||
|
*/
|
||||||
|
counter_v7 :: proc "contextless" (id: Identifier) -> (counter: u16) {
|
||||||
|
counter_bits := transmute(u128be)id & VERSION_7_COUNTER_MASK
|
||||||
|
return cast(u16)(counter_bits >> VERSION_7_COUNTER_SHIFT)
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package uuid
|
||||||
|
|
||||||
|
import "base:runtime"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Stamp a 128-bit integer as being a valid version 8 UUID.
|
||||||
|
|
||||||
|
Per the specification, all version 8 UUIDs are either for experimental or
|
||||||
|
vendor-specific purposes. This procedure allows for converting arbitrary data
|
||||||
|
into custom UUIDs.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- integer: Any integer type.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- result: A valid version 8 UUID.
|
||||||
|
*/
|
||||||
|
stamp_v8_int :: proc(#any_int integer: u128) -> (result: Identifier) {
|
||||||
|
result = transmute(Identifier)cast(u128be)integer
|
||||||
|
|
||||||
|
result[VERSION_BYTE_INDEX] &= 0x0F
|
||||||
|
result[VERSION_BYTE_INDEX] |= 0x80
|
||||||
|
|
||||||
|
result[VARIANT_BYTE_INDEX] &= 0x3F
|
||||||
|
result[VARIANT_BYTE_INDEX] |= 0x80
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Stamp an array of 16 bytes as being a valid version 8 UUID.
|
||||||
|
|
||||||
|
Per the specification, all version 8 UUIDs are either for experimental or
|
||||||
|
vendor-specific purposes. This procedure allows for converting arbitrary data
|
||||||
|
into custom UUIDs.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- array: An array of 16 bytes.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- result: A valid version 8 UUID.
|
||||||
|
*/
|
||||||
|
stamp_v8_array :: proc(array: [16]u8) -> (result: Identifier) {
|
||||||
|
result = Identifier(array)
|
||||||
|
|
||||||
|
result[VERSION_BYTE_INDEX] &= 0x0F
|
||||||
|
result[VERSION_BYTE_INDEX] |= 0x80
|
||||||
|
|
||||||
|
result[VARIANT_BYTE_INDEX] &= 0x3F
|
||||||
|
result[VARIANT_BYTE_INDEX] |= 0x80
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Stamp a slice of bytes as being a valid version 8 UUID.
|
||||||
|
|
||||||
|
If the slice is less than 16 bytes long, the data available will be used.
|
||||||
|
If it is longer than 16 bytes, only the first 16 will be used.
|
||||||
|
|
||||||
|
This procedure does not modify the underlying slice.
|
||||||
|
|
||||||
|
Per the specification, all version 8 UUIDs are either for experimental or
|
||||||
|
vendor-specific purposes. This procedure allows for converting arbitrary data
|
||||||
|
into custom UUIDs.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- slice: A slice of bytes.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- result: A valid version 8 UUID.
|
||||||
|
*/
|
||||||
|
stamp_v8_slice :: proc(slice: []u8) -> (result: Identifier) {
|
||||||
|
runtime.mem_copy_non_overlapping(&result, &slice[0], min(16, len(slice)))
|
||||||
|
|
||||||
|
result[VERSION_BYTE_INDEX] &= 0x0F
|
||||||
|
result[VERSION_BYTE_INDEX] |= 0x80
|
||||||
|
|
||||||
|
result[VARIANT_BYTE_INDEX] &= 0x3F
|
||||||
|
result[VARIANT_BYTE_INDEX] |= 0x80
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
stamp_v8 :: proc {
|
||||||
|
stamp_v8_int,
|
||||||
|
stamp_v8_array,
|
||||||
|
stamp_v8_slice,
|
||||||
|
}
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
package uuid
|
||||||
|
|
||||||
|
import "base:runtime"
|
||||||
|
import "core:io"
|
||||||
|
import "core:strconv"
|
||||||
|
import "core:strings"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Write a UUID in the 8-4-4-4-12 format.
|
||||||
|
|
||||||
|
This procedure performs error checking with every byte written.
|
||||||
|
|
||||||
|
If you can guarantee beforehand that your stream has enough space to hold the
|
||||||
|
UUID (32 bytes), then it is better to use `unsafe_write` instead as that will
|
||||||
|
be faster.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- w: A writable stream.
|
||||||
|
- id: The identifier to convert.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- error: An `io` error, if one occurred, otherwise `nil`.
|
||||||
|
*/
|
||||||
|
write :: proc(w: io.Writer, id: Identifier) -> (error: io.Error) #no_bounds_check {
|
||||||
|
write_octet :: proc (w: io.Writer, octet: u8) -> io.Error #no_bounds_check {
|
||||||
|
high_nibble := octet >> 4
|
||||||
|
low_nibble := octet & 0xF
|
||||||
|
|
||||||
|
io.write_byte(w, strconv.digits[high_nibble]) or_return
|
||||||
|
io.write_byte(w, strconv.digits[low_nibble]) or_return
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for index in 0 ..< 4 { write_octet(w, id[index]) or_return }
|
||||||
|
io.write_byte(w, '-') or_return
|
||||||
|
for index in 4 ..< 6 { write_octet(w, id[index]) or_return }
|
||||||
|
io.write_byte(w, '-') or_return
|
||||||
|
for index in 6 ..< 8 { write_octet(w, id[index]) or_return }
|
||||||
|
io.write_byte(w, '-') or_return
|
||||||
|
for index in 8 ..< 10 { write_octet(w, id[index]) or_return }
|
||||||
|
io.write_byte(w, '-') or_return
|
||||||
|
for index in 10 ..< 16 { write_octet(w, id[index]) or_return }
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Write a UUID in the 8-4-4-4-12 format.
|
||||||
|
|
||||||
|
This procedure performs no error checking on the underlying stream.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- w: A writable stream.
|
||||||
|
- id: The identifier to convert.
|
||||||
|
*/
|
||||||
|
unsafe_write :: proc(w: io.Writer, id: Identifier) #no_bounds_check {
|
||||||
|
write_octet :: proc (w: io.Writer, octet: u8) #no_bounds_check {
|
||||||
|
high_nibble := octet >> 4
|
||||||
|
low_nibble := octet & 0xF
|
||||||
|
|
||||||
|
io.write_byte(w, strconv.digits[high_nibble])
|
||||||
|
io.write_byte(w, strconv.digits[low_nibble])
|
||||||
|
}
|
||||||
|
|
||||||
|
for index in 0 ..< 4 { write_octet(w, id[index]) }
|
||||||
|
io.write_byte(w, '-')
|
||||||
|
for index in 4 ..< 6 { write_octet(w, id[index]) }
|
||||||
|
io.write_byte(w, '-')
|
||||||
|
for index in 6 ..< 8 { write_octet(w, id[index]) }
|
||||||
|
io.write_byte(w, '-')
|
||||||
|
for index in 8 ..< 10 { write_octet(w, id[index]) }
|
||||||
|
io.write_byte(w, '-')
|
||||||
|
for index in 10 ..< 16 { write_octet(w, id[index]) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Convert a UUID to a string in the 8-4-4-4-12 format.
|
||||||
|
|
||||||
|
*Allocates Using Provided Allocator*
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- id: The identifier to convert.
|
||||||
|
- allocator: (default: context.allocator)
|
||||||
|
- loc: The caller location for debugging purposes (default: #caller_location)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- str: The allocated and converted string.
|
||||||
|
- error: An optional allocator error if one occured, `nil` otherwise.
|
||||||
|
*/
|
||||||
|
to_string_allocated :: proc(
|
||||||
|
id: Identifier,
|
||||||
|
allocator := context.allocator,
|
||||||
|
loc := #caller_location,
|
||||||
|
) -> (
|
||||||
|
str: string,
|
||||||
|
error: runtime.Allocator_Error,
|
||||||
|
) #optional_allocator_error {
|
||||||
|
buf := make([]byte, EXPECTED_LENGTH, allocator, loc) or_return
|
||||||
|
builder := strings.builder_from_bytes(buf[:])
|
||||||
|
unsafe_write(strings.to_writer(&builder), id)
|
||||||
|
return strings.to_string(builder), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Convert a UUID to a string in the 8-4-4-4-12 format.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- id: The identifier to convert.
|
||||||
|
- buffer: A byte buffer to store the result. Must be at least 32 bytes large.
|
||||||
|
- loc: The caller location for debugging purposes (default: #caller_location)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- str: The converted string which will be stored in `buffer`.
|
||||||
|
*/
|
||||||
|
to_string_buffer :: proc(
|
||||||
|
id: Identifier,
|
||||||
|
buffer: []byte,
|
||||||
|
loc := #caller_location,
|
||||||
|
) -> (
|
||||||
|
str: string,
|
||||||
|
) {
|
||||||
|
assert(len(buffer) >= EXPECTED_LENGTH, "The buffer provided is not at least 32 bytes large.", loc)
|
||||||
|
builder := strings.builder_from_bytes(buffer)
|
||||||
|
unsafe_write(strings.to_writer(&builder), id)
|
||||||
|
return strings.to_string(builder)
|
||||||
|
}
|
||||||
|
|
||||||
|
to_string :: proc {
|
||||||
|
to_string_allocated,
|
||||||
|
to_string_buffer,
|
||||||
|
}
|
||||||
@@ -126,7 +126,7 @@ error :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
|
|||||||
t.error_count += 1
|
t.error_count += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
advance_rune :: proc(t: ^Tokenizer) {
|
advance_rune :: proc(t: ^Tokenizer) {
|
||||||
#no_bounds_check {
|
#no_bounds_check {
|
||||||
/*
|
/*
|
||||||
@@ -170,7 +170,7 @@ peek_byte :: proc(t: ^Tokenizer, offset := 0) -> byte {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
skip_whitespace :: proc(t: ^Tokenizer) {
|
skip_whitespace :: proc(t: ^Tokenizer) {
|
||||||
for {
|
for {
|
||||||
switch t.ch {
|
switch t.ch {
|
||||||
@@ -182,7 +182,7 @@ skip_whitespace :: proc(t: ^Tokenizer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
is_letter :: proc(r: rune) -> bool {
|
is_letter :: proc(r: rune) -> bool {
|
||||||
if r < utf8.RUNE_SELF {
|
if r < utf8.RUNE_SELF {
|
||||||
switch r {
|
switch r {
|
||||||
@@ -296,7 +296,7 @@ skip_cdata :: proc(t: ^Tokenizer) -> (err: Error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
scan_string :: proc(t: ^Tokenizer, offset: int, close: rune = '<', consume_close := false, multiline := true) -> (value: string, err: Error) {
|
scan_string :: proc(t: ^Tokenizer, offset: int, close: rune = '<', consume_close := false, multiline := true) -> (value: string, err: Error) {
|
||||||
err = .None
|
err = .None
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
BSD 3-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2024, Feoramund
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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.
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package flags
|
||||||
|
|
||||||
|
import "core:time"
|
||||||
|
|
||||||
|
// Set to true to compile with support for core named types disabled, as a
|
||||||
|
// fallback in the event your platform does not support one of the types, or
|
||||||
|
// you have no need for them and want a smaller binary.
|
||||||
|
NO_CORE_NAMED_TYPES :: #config(ODIN_CORE_FLAGS_NO_CORE_NAMED_TYPES, false)
|
||||||
|
|
||||||
|
// Override support for parsing `time` types.
|
||||||
|
IMPORTING_TIME :: #config(ODIN_CORE_FLAGS_USE_TIME, time.IS_SUPPORTED)
|
||||||
|
|
||||||
|
// Override support for parsing `net` types.
|
||||||
|
// TODO: Update this when the BSDs are supported.
|
||||||
|
IMPORTING_NET :: #config(ODIN_CORE_FLAGS_USE_NET, ODIN_OS == .Windows || ODIN_OS == .Linux || ODIN_OS == .Darwin)
|
||||||
|
|
||||||
|
TAG_ARGS :: "args"
|
||||||
|
SUBTAG_NAME :: "name"
|
||||||
|
SUBTAG_POS :: "pos"
|
||||||
|
SUBTAG_REQUIRED :: "required"
|
||||||
|
SUBTAG_HIDDEN :: "hidden"
|
||||||
|
SUBTAG_VARIADIC :: "variadic"
|
||||||
|
SUBTAG_FILE :: "file"
|
||||||
|
SUBTAG_PERMS :: "perms"
|
||||||
|
SUBTAG_INDISTINCT :: "indistinct"
|
||||||
|
|
||||||
|
TAG_USAGE :: "usage"
|
||||||
|
|
||||||
|
UNDOCUMENTED_FLAG :: "<This flag has not been documented yet.>"
|
||||||
|
|
||||||
|
INTERNAL_VARIADIC_FLAG :: "varg"
|
||||||
|
|
||||||
|
RESERVED_HELP_FLAG :: "help"
|
||||||
|
RESERVED_HELP_FLAG_SHORT :: "h"
|
||||||
|
|
||||||
|
// If there are more than this number of flags in total, only the required and
|
||||||
|
// positional flags will be shown in the one-line usage summary.
|
||||||
|
ONE_LINE_FLAG_CUTOFF_COUNT :: 16
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
/*
|
||||||
|
package flags implements a command-line argument parser.
|
||||||
|
|
||||||
|
It works by using Odin's run-time type information to determine where and how
|
||||||
|
to store data on a struct provided by the program. Type conversion is handled
|
||||||
|
automatically and errors are reported with useful messages.
|
||||||
|
|
||||||
|
|
||||||
|
Command-Line Syntax:
|
||||||
|
|
||||||
|
Arguments are treated differently depending on how they're formatted.
|
||||||
|
The format is similar to the Odin binary's way of handling compiler flags.
|
||||||
|
|
||||||
|
```
|
||||||
|
type handling
|
||||||
|
------------ ------------------------
|
||||||
|
<positional> depends on struct layout
|
||||||
|
-<flag> set a bool true
|
||||||
|
-<flag:option> set flag to option
|
||||||
|
-<flag=option> set flag to option, alternative syntax
|
||||||
|
-<map>:<key>=<value> set map[key] to value
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
Struct Tags:
|
||||||
|
|
||||||
|
Users of the `core:encoding/json` package may be familiar with using tags to
|
||||||
|
annotate struct metadata. The same technique is used here to annotate where
|
||||||
|
arguments should go and which are required.
|
||||||
|
|
||||||
|
Under the `args` tag, there are the following subtags:
|
||||||
|
|
||||||
|
- `name=S`: set `S` as the flag's name.
|
||||||
|
- `pos=N`: place positional argument `N` into this flag.
|
||||||
|
- `hidden`: hide this flag from the usage documentation.
|
||||||
|
- `required`: cause verification to fail if this argument is not set.
|
||||||
|
- `variadic`: take all remaining arguments when set, UNIX-style only.
|
||||||
|
- `file`: for `os.Handle` types, file open mode.
|
||||||
|
- `perms`: for `os.Handle` types, file open permissions.
|
||||||
|
- `indistinct`: allow the setting of distinct types by their base type.
|
||||||
|
|
||||||
|
`required` may be given a range specifier in the following formats:
|
||||||
|
```
|
||||||
|
min
|
||||||
|
<max
|
||||||
|
min<max
|
||||||
|
```
|
||||||
|
|
||||||
|
`max` is not inclusive in this range, as noted by the less-than `<` sign, so if
|
||||||
|
you want to require 3 and only 3 arguments in a dynamic array, you would
|
||||||
|
specify `required=3<4`.
|
||||||
|
|
||||||
|
|
||||||
|
`variadic` may be given a number (`variadic=N`) above 1 to limit how many extra
|
||||||
|
arguments it consumes.
|
||||||
|
|
||||||
|
|
||||||
|
`file` determines the file open mode for an `os.Handle`.
|
||||||
|
It accepts a string of flags that can be mixed together:
|
||||||
|
- r: read
|
||||||
|
- w: write
|
||||||
|
- c: create, create the file if it doesn't exist
|
||||||
|
- a: append, add any new writes to the end of the file
|
||||||
|
- t: truncate, erase the file on open
|
||||||
|
|
||||||
|
|
||||||
|
`perms` determines the file open permissions for an `os.Handle`.
|
||||||
|
|
||||||
|
The permissions are represented by three numbers in octal format. The first
|
||||||
|
number is the owner, the second is the group, and the third is other. Read is
|
||||||
|
represented by 4, write by 2, and execute by 1.
|
||||||
|
|
||||||
|
These numbers are added together to get combined permissions. For example, 644
|
||||||
|
represents read/write for the owner, read for the group, and read for other.
|
||||||
|
|
||||||
|
Note that this may only have effect on UNIX-like platforms. By default, `perms`
|
||||||
|
is set to 444 when only reading and 644 when writing.
|
||||||
|
|
||||||
|
|
||||||
|
`indistinct` tells the parser that it's okay to treat distinct types as their
|
||||||
|
underlying base type. Normally, the parser will hand those types off to the
|
||||||
|
custom type setter (more about that later) if one is available, if it doesn't
|
||||||
|
know how to handle the type.
|
||||||
|
|
||||||
|
|
||||||
|
Usage Tag:
|
||||||
|
|
||||||
|
There is also the `usage` tag, which is a plain string to be printed alongside
|
||||||
|
the flag in the usage output. If `usage` contains a newline, it will be
|
||||||
|
properly aligned when printed.
|
||||||
|
|
||||||
|
All surrounding whitespace is trimmed when formatting with multiple lines.
|
||||||
|
|
||||||
|
|
||||||
|
Supported Flag Data Types:
|
||||||
|
|
||||||
|
- all booleans
|
||||||
|
- all integers
|
||||||
|
- all floats
|
||||||
|
- all enums
|
||||||
|
- all complex numbers
|
||||||
|
- all quaternions
|
||||||
|
- all bit_sets
|
||||||
|
- `string` and `cstring`
|
||||||
|
- `rune`
|
||||||
|
- `os.Handle`
|
||||||
|
- `time.Time`
|
||||||
|
- `datetime.DateTime`
|
||||||
|
- `net.Host_Or_Endpoint`,
|
||||||
|
- additional custom types, see Custom Types below
|
||||||
|
- `dynamic` arrays with element types of the above
|
||||||
|
- `map[string]`s or `map[cstring]`s with value types of the above
|
||||||
|
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
|
||||||
|
The parser will ensure `required` arguments are set, if no errors occurred
|
||||||
|
during parsing. This is on by default.
|
||||||
|
|
||||||
|
Additionally, you may call `register_flag_checker` to set your own argument
|
||||||
|
validation procedure that will be called after the default checker.
|
||||||
|
|
||||||
|
|
||||||
|
Strict:
|
||||||
|
|
||||||
|
The parser will return on the first error and stop parsing. This is on by
|
||||||
|
default. Otherwise, all arguments that can be parsed, will be, and only the
|
||||||
|
last error is returned.
|
||||||
|
|
||||||
|
|
||||||
|
Error Messages:
|
||||||
|
|
||||||
|
All error message strings are allocated using the context's `temp_allocator`,
|
||||||
|
so if you need them to persist, make sure to clone the underlying `message`.
|
||||||
|
|
||||||
|
|
||||||
|
Help:
|
||||||
|
|
||||||
|
By default, `-h` and `-help` are reserved flags which raise their own error
|
||||||
|
type when set, allowing the program to handle the request differently from
|
||||||
|
other errors.
|
||||||
|
|
||||||
|
|
||||||
|
Custom Types:
|
||||||
|
|
||||||
|
You may specify your own type setter for program-specific structs and other
|
||||||
|
named types. Call `register_type_setter` with an appropriate proc before
|
||||||
|
calling any of the parsing procs.
|
||||||
|
|
||||||
|
A compliant `Custom_Type_Setter` must return three values:
|
||||||
|
- an error message if one occurred,
|
||||||
|
- a boolean indicating if the proc handles the type, and
|
||||||
|
- an `Allocator_Error` if any occurred.
|
||||||
|
|
||||||
|
If the setter does not handle the type, simply return without setting any of
|
||||||
|
the values.
|
||||||
|
|
||||||
|
|
||||||
|
UNIX-style:
|
||||||
|
|
||||||
|
This package also supports parsing arguments in a limited flavor of UNIX.
|
||||||
|
Odin and UNIX style are mutually exclusive, and which one to be used is chosen
|
||||||
|
at parse time.
|
||||||
|
|
||||||
|
```
|
||||||
|
--flag
|
||||||
|
--flag=argument
|
||||||
|
--flag argument
|
||||||
|
--flag argument repeating-argument
|
||||||
|
```
|
||||||
|
|
||||||
|
`-flag` may also be substituted for `--flag`.
|
||||||
|
|
||||||
|
Do note that map flags are not currently supported in this parsing style.
|
||||||
|
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
A complete example is given in the `example` subdirectory.
|
||||||
|
*/
|
||||||
|
package flags
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package flags
|
||||||
|
|
||||||
|
import "core:os"
|
||||||
|
|
||||||
|
Parse_Error_Reason :: enum {
|
||||||
|
None,
|
||||||
|
// An extra positional argument was given, and there is no `varg` field.
|
||||||
|
Extra_Positional,
|
||||||
|
// The underlying type does not support the string value it is being set to.
|
||||||
|
Bad_Value,
|
||||||
|
// No flag was given by the user.
|
||||||
|
No_Flag,
|
||||||
|
// No value was given by the user.
|
||||||
|
No_Value,
|
||||||
|
// The flag on the struct is missing.
|
||||||
|
Missing_Flag,
|
||||||
|
// The type itself isn't supported.
|
||||||
|
Unsupported_Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Raised during parsing, naturally.
|
||||||
|
Parse_Error :: struct {
|
||||||
|
reason: Unified_Parse_Error_Reason,
|
||||||
|
message: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Raised during parsing.
|
||||||
|
// Provides more granular information than what just a string could hold.
|
||||||
|
Open_File_Error :: struct {
|
||||||
|
filename: string,
|
||||||
|
errno: os.Errno,
|
||||||
|
mode: int,
|
||||||
|
perms: int,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Raised during parsing.
|
||||||
|
Help_Request :: distinct bool
|
||||||
|
|
||||||
|
|
||||||
|
// Raised after parsing, during validation.
|
||||||
|
Validation_Error :: struct {
|
||||||
|
message: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
Error :: union {
|
||||||
|
Parse_Error,
|
||||||
|
Open_File_Error,
|
||||||
|
Help_Request,
|
||||||
|
Validation_Error,
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
//+build freebsd, netbsd, openbsd
|
||||||
|
package flags
|
||||||
|
|
||||||
|
import "base:runtime"
|
||||||
|
|
||||||
|
Unified_Parse_Error_Reason :: union #shared_nil {
|
||||||
|
Parse_Error_Reason,
|
||||||
|
runtime.Allocator_Error,
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
//+build !freebsd !netbsd !openbsd
|
||||||
|
package flags
|
||||||
|
|
||||||
|
import "base:runtime"
|
||||||
|
import "core:net"
|
||||||
|
|
||||||
|
Unified_Parse_Error_Reason :: union #shared_nil {
|
||||||
|
Parse_Error_Reason,
|
||||||
|
runtime.Allocator_Error,
|
||||||
|
net.Parse_Endpoint_Error,
|
||||||
|
}
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
package core_flags_example
|
||||||
|
|
||||||
|
import "base:runtime"
|
||||||
|
import "core:flags"
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:net"
|
||||||
|
import "core:os"
|
||||||
|
import "core:time/datetime"
|
||||||
|
|
||||||
|
|
||||||
|
Fixed_Point1_1 :: struct {
|
||||||
|
integer: u8,
|
||||||
|
fractional: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
Optimization_Level :: enum {
|
||||||
|
Slow,
|
||||||
|
Fast,
|
||||||
|
Warp_Speed,
|
||||||
|
Ludicrous_Speed,
|
||||||
|
}
|
||||||
|
|
||||||
|
// It's simple but powerful.
|
||||||
|
my_custom_type_setter :: proc(
|
||||||
|
data: rawptr,
|
||||||
|
data_type: typeid,
|
||||||
|
unparsed_value: string,
|
||||||
|
args_tag: string,
|
||||||
|
) -> (
|
||||||
|
error: string,
|
||||||
|
handled: bool,
|
||||||
|
alloc_error: runtime.Allocator_Error,
|
||||||
|
) {
|
||||||
|
if data_type == Fixed_Point1_1 {
|
||||||
|
handled = true
|
||||||
|
ptr := cast(^Fixed_Point1_1)data
|
||||||
|
|
||||||
|
// precision := flags.get_subtag(args_tag, "precision")
|
||||||
|
|
||||||
|
if len(unparsed_value) == 3 {
|
||||||
|
ptr.integer = unparsed_value[0] - '0'
|
||||||
|
ptr.fractional = unparsed_value[2] - '0'
|
||||||
|
} else {
|
||||||
|
error = "Incorrect format. Must be in the form of `i.f`."
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform sanity checking here in the type parsing phase.
|
||||||
|
//
|
||||||
|
// The validation phase is flag-specific.
|
||||||
|
if !(0 <= ptr.integer && ptr.integer < 10) || !(0 <= ptr.fractional && ptr.fractional < 10) {
|
||||||
|
error = "Incorrect format. Must be between `0.0` and `9.9`."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
my_custom_flag_checker :: proc(
|
||||||
|
model: rawptr,
|
||||||
|
name: string,
|
||||||
|
value: any,
|
||||||
|
args_tag: string,
|
||||||
|
) -> (error: string) {
|
||||||
|
if name == "iterations" {
|
||||||
|
v := value.(int)
|
||||||
|
if !(1 <= v && v < 5) {
|
||||||
|
error = "Iterations only supports 1 ..< 5."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Distinct_Int :: distinct int
|
||||||
|
|
||||||
|
main :: proc() {
|
||||||
|
Options :: struct {
|
||||||
|
|
||||||
|
file: os.Handle `args:"pos=0,required,file=r" usage:"Input file."`,
|
||||||
|
output: os.Handle `args:"pos=1,file=cw" usage:"Output file."`,
|
||||||
|
|
||||||
|
hub: net.Host_Or_Endpoint `usage:"Internet address to contact for updates."`,
|
||||||
|
schedule: datetime.DateTime `usage:"Launch tasks at this time."`,
|
||||||
|
|
||||||
|
opt: Optimization_Level `usage:"Optimization level."`,
|
||||||
|
todo: [dynamic]string `usage:"Todo items."`,
|
||||||
|
|
||||||
|
accuracy: Fixed_Point1_1 `args:"required" usage:"Lenience in FLOP calculations."`,
|
||||||
|
iterations: int `usage:"Run this many times."`,
|
||||||
|
|
||||||
|
// Note how the parser will transform this flag's name into `special-int`.
|
||||||
|
special_int: Distinct_Int `args:"indistinct" usage:"Able to set distinct types."`,
|
||||||
|
|
||||||
|
quat: quaternion256,
|
||||||
|
|
||||||
|
bits: bit_set[0..<8],
|
||||||
|
|
||||||
|
// Many different requirement styles:
|
||||||
|
|
||||||
|
// gadgets: [dynamic]string `args:"required=1" usage:"gadgets"`,
|
||||||
|
// widgets: [dynamic]string `args:"required=<3" usage:"widgets"`,
|
||||||
|
// foos: [dynamic]string `args:"required=2<4"`,
|
||||||
|
// bars: [dynamic]string `args:"required=3<4"`,
|
||||||
|
// bots: [dynamic]string `args:"required"`,
|
||||||
|
|
||||||
|
// (Maps) Only available in Odin style:
|
||||||
|
|
||||||
|
// assignments: map[string]u8 `args:"name=assign" usage:"Number of jobs per worker."`,
|
||||||
|
|
||||||
|
// (Variadic) Only available in UNIX style:
|
||||||
|
|
||||||
|
// bots: [dynamic]string `args:"variadic=2,required"`,
|
||||||
|
|
||||||
|
verbose: bool `usage:"Show verbose output."`,
|
||||||
|
debug: bool `args:"hidden" usage:"print debug info"`,
|
||||||
|
|
||||||
|
varg: [dynamic]string `usage:"Any extra arguments go here."`,
|
||||||
|
}
|
||||||
|
|
||||||
|
opt: Options
|
||||||
|
style : flags.Parsing_Style = .Odin
|
||||||
|
|
||||||
|
flags.register_type_setter(my_custom_type_setter)
|
||||||
|
flags.register_flag_checker(my_custom_flag_checker)
|
||||||
|
flags.parse_or_exit(&opt, os.args, style)
|
||||||
|
|
||||||
|
fmt.printfln("%#v", opt)
|
||||||
|
|
||||||
|
if opt.output != 0 {
|
||||||
|
os.write_string(opt.output, "Hellope!\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,262 @@
|
|||||||
|
//+private
|
||||||
|
package flags
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
@require import "base:runtime"
|
||||||
|
import "core:container/bit_array"
|
||||||
|
@require import "core:fmt"
|
||||||
|
@require import "core:mem"
|
||||||
|
import "core:reflect"
|
||||||
|
@require import "core:strconv"
|
||||||
|
@require import "core:strings"
|
||||||
|
|
||||||
|
// Push a positional argument onto a data struct, checking for specified
|
||||||
|
// positionals first before adding it to a fallback field.
|
||||||
|
@(optimization_mode="favor_size")
|
||||||
|
push_positional :: #force_no_inline proc (model: ^$T, parser: ^Parser, arg: string) -> (error: Error) {
|
||||||
|
if bit_array.get(&parser.filled_pos, parser.filled_pos.max_index) {
|
||||||
|
// The max index is set, which means we're out of space.
|
||||||
|
// Add one free bit by setting the index above to false.
|
||||||
|
bit_array.set(&parser.filled_pos, 1 + parser.filled_pos.max_index, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pos: int = ---
|
||||||
|
{
|
||||||
|
iter := bit_array.make_iterator(&parser.filled_pos)
|
||||||
|
ok: bool
|
||||||
|
pos, ok = bit_array.iterate_by_unset(&iter)
|
||||||
|
|
||||||
|
// This may be an allocator error.
|
||||||
|
assert(ok, "Unable to find a free spot in the positional bit_array.")
|
||||||
|
}
|
||||||
|
|
||||||
|
field, index, has_pos_assigned := get_field_by_pos(model, pos)
|
||||||
|
|
||||||
|
if !has_pos_assigned {
|
||||||
|
when intrinsics.type_has_field(T, INTERNAL_VARIADIC_FLAG) {
|
||||||
|
// Add it to the fallback array.
|
||||||
|
field = reflect.struct_field_by_name(T, INTERNAL_VARIADIC_FLAG)
|
||||||
|
} else {
|
||||||
|
return Parse_Error {
|
||||||
|
.Extra_Positional,
|
||||||
|
fmt.tprintf("Got extra positional argument `%s` with nowhere to store it.", arg),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr := cast(rawptr)(cast(uintptr)model + field.offset)
|
||||||
|
args_tag, _ := reflect.struct_tag_lookup(field.tag, TAG_ARGS)
|
||||||
|
field_name := get_field_name(field)
|
||||||
|
error = parse_and_set_pointer_by_type(ptr, arg, field.type, args_tag)
|
||||||
|
#partial switch &specific_error in error {
|
||||||
|
case Parse_Error:
|
||||||
|
specific_error.message = fmt.tprintf("Unable to set positional #%i (%s) of type %v to `%s`.%s%s",
|
||||||
|
pos,
|
||||||
|
field_name,
|
||||||
|
field.type,
|
||||||
|
arg,
|
||||||
|
" " if len(specific_error.message) > 0 else "",
|
||||||
|
specific_error.message)
|
||||||
|
case nil:
|
||||||
|
bit_array.set(&parser.filled_pos, pos)
|
||||||
|
bit_array.set(&parser.fields_set, index)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
register_field :: proc(parser: ^Parser, field: reflect.Struct_Field, index: int) {
|
||||||
|
if pos, ok := get_field_pos(field); ok {
|
||||||
|
bit_array.set(&parser.filled_pos, pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
bit_array.set(&parser.fields_set, index)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a `-flag` argument, Odin-style.
|
||||||
|
@(optimization_mode="favor_size")
|
||||||
|
set_odin_flag :: proc(model: ^$T, parser: ^Parser, name: string) -> (error: Error) {
|
||||||
|
// We make a special case for help requests.
|
||||||
|
switch name {
|
||||||
|
case RESERVED_HELP_FLAG, RESERVED_HELP_FLAG_SHORT:
|
||||||
|
return Help_Request{}
|
||||||
|
}
|
||||||
|
|
||||||
|
field, index := get_field_by_name(model, name) or_return
|
||||||
|
|
||||||
|
#partial switch specific_type_info in field.type.variant {
|
||||||
|
case runtime.Type_Info_Boolean:
|
||||||
|
ptr := cast(^bool)(cast(uintptr)model + field.offset)
|
||||||
|
ptr^ = true
|
||||||
|
case:
|
||||||
|
return Parse_Error {
|
||||||
|
.Bad_Value,
|
||||||
|
fmt.tprintf("Unable to set `%s` of type %v to true.", name, field.type),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
register_field(parser, field, index)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a `-flag` argument, UNIX-style.
|
||||||
|
@(optimization_mode="favor_size")
|
||||||
|
set_unix_flag :: proc(model: ^$T, parser: ^Parser, name: string) -> (future_args: int, error: Error) {
|
||||||
|
// We make a special case for help requests.
|
||||||
|
switch name {
|
||||||
|
case RESERVED_HELP_FLAG, RESERVED_HELP_FLAG_SHORT:
|
||||||
|
return 0, Help_Request{}
|
||||||
|
}
|
||||||
|
|
||||||
|
field, index := get_field_by_name(model, name) or_return
|
||||||
|
|
||||||
|
#partial switch specific_type_info in field.type.variant {
|
||||||
|
case runtime.Type_Info_Boolean:
|
||||||
|
ptr := cast(^bool)(cast(uintptr)model + field.offset)
|
||||||
|
ptr^ = true
|
||||||
|
case runtime.Type_Info_Dynamic_Array:
|
||||||
|
future_args = 1
|
||||||
|
if tag, ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS); ok {
|
||||||
|
if length, is_variadic := get_struct_subtag(tag, SUBTAG_VARIADIC); is_variadic {
|
||||||
|
// Variadic arrays may specify how many arguments they consume at once.
|
||||||
|
// Otherwise, they take everything that's left.
|
||||||
|
if value, value_ok := strconv.parse_u64_of_base(length, 10); value_ok {
|
||||||
|
future_args = cast(int)value
|
||||||
|
} else {
|
||||||
|
future_args = max(int)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case:
|
||||||
|
// `--flag`, waiting on its value.
|
||||||
|
future_args = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
register_field(parser, field, index)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a `-flag:option` argument.
|
||||||
|
@(optimization_mode="favor_size")
|
||||||
|
set_option :: proc(model: ^$T, parser: ^Parser, name, option: string) -> (error: Error) {
|
||||||
|
field, index := get_field_by_name(model, name) or_return
|
||||||
|
|
||||||
|
if len(option) == 0 {
|
||||||
|
return Parse_Error {
|
||||||
|
.No_Value,
|
||||||
|
fmt.tprintf("Setting `%s` to an empty value is meaningless.", name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Guard against incorrect syntax.
|
||||||
|
#partial switch specific_type_info in field.type.variant {
|
||||||
|
case runtime.Type_Info_Map:
|
||||||
|
return Parse_Error {
|
||||||
|
.No_Value,
|
||||||
|
fmt.tprintf("Unable to set `%s` of type %v to `%s`. Are you missing an `=`? The correct format is `map:key=value`.", name, field.type, option),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr := cast(rawptr)(cast(uintptr)model + field.offset)
|
||||||
|
args_tag := reflect.struct_tag_get(field.tag, TAG_ARGS)
|
||||||
|
error = parse_and_set_pointer_by_type(ptr, option, field.type, args_tag)
|
||||||
|
#partial switch &specific_error in error {
|
||||||
|
case Parse_Error:
|
||||||
|
specific_error.message = fmt.tprintf("Unable to set `%s` of type %v to `%s`.%s%s",
|
||||||
|
name,
|
||||||
|
field.type,
|
||||||
|
option,
|
||||||
|
" " if len(specific_error.message) > 0 else "",
|
||||||
|
specific_error.message)
|
||||||
|
case nil:
|
||||||
|
register_field(parser, field, index)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a `-map:key=value` argument.
|
||||||
|
@(optimization_mode="favor_size")
|
||||||
|
set_key_value :: proc(model: ^$T, parser: ^Parser, name, key, value: string) -> (error: Error) {
|
||||||
|
field, index := get_field_by_name(model, name) or_return
|
||||||
|
|
||||||
|
#partial switch specific_type_info in field.type.variant {
|
||||||
|
case runtime.Type_Info_Map:
|
||||||
|
key := key
|
||||||
|
key_ptr := cast(rawptr)&key
|
||||||
|
key_cstr: cstring
|
||||||
|
if reflect.is_cstring(specific_type_info.key) {
|
||||||
|
// We clone the key here, because it's liable to be a slice of an
|
||||||
|
// Odin string, and we need to put a NUL terminator in it.
|
||||||
|
key_cstr = strings.clone_to_cstring(key)
|
||||||
|
key_ptr = &key_cstr
|
||||||
|
}
|
||||||
|
defer if key_cstr != nil {
|
||||||
|
delete(key_cstr)
|
||||||
|
}
|
||||||
|
|
||||||
|
raw_map := (^runtime.Raw_Map)(cast(uintptr)model + field.offset)
|
||||||
|
|
||||||
|
hash := specific_type_info.map_info.key_hasher(key_ptr, runtime.map_seed(raw_map^))
|
||||||
|
|
||||||
|
backing_alloc := false
|
||||||
|
elem_backing: []byte
|
||||||
|
value_ptr: rawptr
|
||||||
|
|
||||||
|
if raw_map.allocator.procedure == nil {
|
||||||
|
raw_map.allocator = context.allocator
|
||||||
|
} else {
|
||||||
|
value_ptr = runtime.__dynamic_map_get(raw_map,
|
||||||
|
specific_type_info.map_info,
|
||||||
|
hash,
|
||||||
|
key_ptr,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if value_ptr == nil {
|
||||||
|
alloc_error: runtime.Allocator_Error = ---
|
||||||
|
elem_backing, alloc_error = mem.alloc_bytes(specific_type_info.value.size, specific_type_info.value.align)
|
||||||
|
if elem_backing == nil {
|
||||||
|
return Parse_Error {
|
||||||
|
alloc_error,
|
||||||
|
"Failed to allocate element backing for map value.",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
backing_alloc = true
|
||||||
|
value_ptr = raw_data(elem_backing)
|
||||||
|
}
|
||||||
|
|
||||||
|
args_tag, _ := reflect.struct_tag_lookup(field.tag, TAG_ARGS)
|
||||||
|
error = parse_and_set_pointer_by_type(value_ptr, value, specific_type_info.value, args_tag)
|
||||||
|
#partial switch &specific_error in error {
|
||||||
|
case Parse_Error:
|
||||||
|
specific_error.message = fmt.tprintf("Unable to set `%s` of type %v with key=value: `%s`=`%s`.%s%s",
|
||||||
|
name,
|
||||||
|
field.type,
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
" " if len(specific_error.message) > 0 else "",
|
||||||
|
specific_error.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
if backing_alloc {
|
||||||
|
runtime.__dynamic_map_set(raw_map,
|
||||||
|
specific_type_info.map_info,
|
||||||
|
hash,
|
||||||
|
key_ptr,
|
||||||
|
value_ptr,
|
||||||
|
)
|
||||||
|
|
||||||
|
delete(elem_backing)
|
||||||
|
}
|
||||||
|
|
||||||
|
register_field(parser, field, index)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return Parse_Error {
|
||||||
|
.Bad_Value,
|
||||||
|
fmt.tprintf("Unable to set `%s` of type %v with key=value: `%s`=`%s`.", name, field.type, key, value),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
//+private
|
||||||
|
package flags
|
||||||
|
|
||||||
|
import "core:container/bit_array"
|
||||||
|
import "core:strconv"
|
||||||
|
import "core:strings"
|
||||||
|
|
||||||
|
// Used to group state together.
|
||||||
|
Parser :: struct {
|
||||||
|
// `fields_set` tracks which arguments have been set.
|
||||||
|
// It uses their struct field index.
|
||||||
|
fields_set: bit_array.Bit_Array,
|
||||||
|
|
||||||
|
// `filled_pos` tracks which arguments have been filled into positional
|
||||||
|
// spots, much like how `fmt` treats them.
|
||||||
|
filled_pos: bit_array.Bit_Array,
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_one_odin_arg :: proc(model: ^$T, parser: ^Parser, arg: string) -> (error: Error) {
|
||||||
|
arg := arg
|
||||||
|
|
||||||
|
if strings.has_prefix(arg, "-") {
|
||||||
|
arg = arg[1:]
|
||||||
|
|
||||||
|
flag: string
|
||||||
|
assignment_rune: rune
|
||||||
|
find_assignment: for r, i in arg {
|
||||||
|
switch r {
|
||||||
|
case ':', '=':
|
||||||
|
assignment_rune = r
|
||||||
|
flag = arg[:i]
|
||||||
|
arg = arg[1 + i:]
|
||||||
|
break find_assignment
|
||||||
|
case:
|
||||||
|
continue find_assignment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if assignment_rune == 0 {
|
||||||
|
if len(arg) == 0 {
|
||||||
|
return Parse_Error {
|
||||||
|
.No_Flag,
|
||||||
|
"No flag was given.",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -flag
|
||||||
|
set_odin_flag(model, parser, arg) or_return
|
||||||
|
|
||||||
|
} else if assignment_rune == ':' {
|
||||||
|
// -flag:option <OR> -map:key=value
|
||||||
|
error = set_option(model, parser, flag, arg)
|
||||||
|
|
||||||
|
if error != nil {
|
||||||
|
// -flag:option did not work, so this may be a -map:key=value set.
|
||||||
|
find_equals: for r, i in arg {
|
||||||
|
if r == '=' {
|
||||||
|
key := arg[:i]
|
||||||
|
arg = arg[1 + i:]
|
||||||
|
error = set_key_value(model, parser, flag, key, arg)
|
||||||
|
break find_equals
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// -flag=option, alternative syntax
|
||||||
|
set_option(model, parser, flag, arg) or_return
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// positional
|
||||||
|
error = push_positional(model, parser, arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_one_unix_arg :: proc(model: ^$T, parser: ^Parser, arg: string) -> (
|
||||||
|
future_args: int,
|
||||||
|
current_flag: string,
|
||||||
|
error: Error,
|
||||||
|
) {
|
||||||
|
arg := arg
|
||||||
|
|
||||||
|
if strings.has_prefix(arg, "-") {
|
||||||
|
// -flag
|
||||||
|
arg = arg[1:]
|
||||||
|
|
||||||
|
if strings.has_prefix(arg, "-") {
|
||||||
|
// Allow `--` to function as `-`.
|
||||||
|
arg = arg[1:]
|
||||||
|
|
||||||
|
if len(arg) == 0 {
|
||||||
|
// `--`, and only `--`.
|
||||||
|
// Everything from now on will be treated as an argument.
|
||||||
|
future_args = max(int)
|
||||||
|
current_flag = INTERNAL_VARIADIC_FLAG
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flag: string
|
||||||
|
find_assignment: for r, i in arg {
|
||||||
|
if r == '=' {
|
||||||
|
// --flag=option
|
||||||
|
flag = arg[:i]
|
||||||
|
arg = arg[1 + i:]
|
||||||
|
error = set_option(model, parser, flag, arg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --flag option, potentially
|
||||||
|
future_args = set_unix_flag(model, parser, arg) or_return
|
||||||
|
current_flag = arg
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// positional
|
||||||
|
error = push_positional(model, parser, arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse a number of requirements specifier.
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
//
|
||||||
|
// `min`
|
||||||
|
// `<max`
|
||||||
|
// `min<max`
|
||||||
|
parse_requirements :: proc(str: string) -> (minimum, maximum: int, ok: bool) {
|
||||||
|
if len(str) == 0 {
|
||||||
|
return 1, max(int), true
|
||||||
|
}
|
||||||
|
|
||||||
|
if less_than := strings.index_byte(str, '<'); less_than != -1 {
|
||||||
|
if len(str) == 1 {
|
||||||
|
return 0, 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
#no_bounds_check left := str[:less_than]
|
||||||
|
#no_bounds_check right := str[1 + less_than:]
|
||||||
|
|
||||||
|
if left_value, parse_ok := strconv.parse_u64_of_base(left, 10); parse_ok {
|
||||||
|
minimum = cast(int)left_value
|
||||||
|
} else if len(left) > 0 {
|
||||||
|
return 0, 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
if right_value, parse_ok := strconv.parse_u64_of_base(right, 10); parse_ok {
|
||||||
|
maximum = cast(int)right_value
|
||||||
|
} else if len(right) > 0 {
|
||||||
|
return 0, 0, false
|
||||||
|
} else {
|
||||||
|
maximum = max(int)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if value, parse_ok := strconv.parse_u64_of_base(str, 10); parse_ok {
|
||||||
|
minimum = cast(int)value
|
||||||
|
maximum = max(int)
|
||||||
|
} else {
|
||||||
|
return 0, 0, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = true
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,529 @@
|
|||||||
|
//+private
|
||||||
|
package flags
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
import "base:runtime"
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:mem"
|
||||||
|
import "core:os"
|
||||||
|
import "core:reflect"
|
||||||
|
import "core:strconv"
|
||||||
|
import "core:strings"
|
||||||
|
@require import "core:time"
|
||||||
|
@require import "core:time/datetime"
|
||||||
|
import "core:unicode/utf8"
|
||||||
|
|
||||||
|
@(optimization_mode="favor_size")
|
||||||
|
parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info: ^runtime.Type_Info) -> bool {
|
||||||
|
bounded_int :: proc(value, min, max: i128) -> (result: i128, ok: bool) {
|
||||||
|
return value, min <= value && value <= max
|
||||||
|
}
|
||||||
|
|
||||||
|
bounded_uint :: proc(value, max: u128) -> (result: u128, ok: bool) {
|
||||||
|
return value, value <= max
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(Feoramund): This procedure has been written with the goal in mind
|
||||||
|
// of generating the least amount of assembly, given that this library is
|
||||||
|
// likely to be called once and forgotten.
|
||||||
|
//
|
||||||
|
// I've rewritten the switch tables below in 3 different ways, and the
|
||||||
|
// current one generates the least amount of code for me on Linux AMD64.
|
||||||
|
//
|
||||||
|
// The other two ways were:
|
||||||
|
//
|
||||||
|
// - the original implementation: use of parametric polymorphism which led
|
||||||
|
// to dozens of functions generated, one for each type.
|
||||||
|
//
|
||||||
|
// - a `value, ok` assignment statement with the `or_return` done at the
|
||||||
|
// end of the switch, instead of inline.
|
||||||
|
//
|
||||||
|
// This seems to be the smallest way for now.
|
||||||
|
|
||||||
|
#partial switch specific_type_info in type_info.variant {
|
||||||
|
case runtime.Type_Info_Integer:
|
||||||
|
if specific_type_info.signed {
|
||||||
|
value := strconv.parse_i128(str) or_return
|
||||||
|
switch type_info.id {
|
||||||
|
case i8: (^i8) (ptr)^ = cast(i8) bounded_int(value, cast(i128)min(i8), cast(i128)max(i8) ) or_return
|
||||||
|
case i16: (^i16) (ptr)^ = cast(i16) bounded_int(value, cast(i128)min(i16), cast(i128)max(i16) ) or_return
|
||||||
|
case i32: (^i32) (ptr)^ = cast(i32) bounded_int(value, cast(i128)min(i32), cast(i128)max(i32) ) or_return
|
||||||
|
case i64: (^i64) (ptr)^ = cast(i64) bounded_int(value, cast(i128)min(i64), cast(i128)max(i64) ) or_return
|
||||||
|
case i128: (^i128) (ptr)^ = value
|
||||||
|
|
||||||
|
case int: (^int) (ptr)^ = cast(int) bounded_int(value, cast(i128)min(int), cast(i128)max(int) ) or_return
|
||||||
|
|
||||||
|
case i16le: (^i16le) (ptr)^ = cast(i16le) bounded_int(value, cast(i128)min(i16le), cast(i128)max(i16le) ) or_return
|
||||||
|
case i32le: (^i32le) (ptr)^ = cast(i32le) bounded_int(value, cast(i128)min(i32le), cast(i128)max(i32le) ) or_return
|
||||||
|
case i64le: (^i64le) (ptr)^ = cast(i64le) bounded_int(value, cast(i128)min(i64le), cast(i128)max(i64le) ) or_return
|
||||||
|
case i128le: (^i128le)(ptr)^ = cast(i128le) bounded_int(value, cast(i128)min(i128le), cast(i128)max(i128le)) or_return
|
||||||
|
|
||||||
|
case i16be: (^i16be) (ptr)^ = cast(i16be) bounded_int(value, cast(i128)min(i16be), cast(i128)max(i16be) ) or_return
|
||||||
|
case i32be: (^i32be) (ptr)^ = cast(i32be) bounded_int(value, cast(i128)min(i32be), cast(i128)max(i32be) ) or_return
|
||||||
|
case i64be: (^i64be) (ptr)^ = cast(i64be) bounded_int(value, cast(i128)min(i64be), cast(i128)max(i64be) ) or_return
|
||||||
|
case i128be: (^i128be)(ptr)^ = cast(i128be) bounded_int(value, cast(i128)min(i128be), cast(i128)max(i128be)) or_return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value := strconv.parse_u128(str) or_return
|
||||||
|
switch type_info.id {
|
||||||
|
case u8: (^u8) (ptr)^ = cast(u8) bounded_uint(value, cast(u128)max(u8) ) or_return
|
||||||
|
case u16: (^u16) (ptr)^ = cast(u16) bounded_uint(value, cast(u128)max(u16) ) or_return
|
||||||
|
case u32: (^u32) (ptr)^ = cast(u32) bounded_uint(value, cast(u128)max(u32) ) or_return
|
||||||
|
case u64: (^u64) (ptr)^ = cast(u64) bounded_uint(value, cast(u128)max(u64) ) or_return
|
||||||
|
case u128: (^u128) (ptr)^ = value
|
||||||
|
|
||||||
|
case uint: (^uint) (ptr)^ = cast(uint) bounded_uint(value, cast(u128)max(uint) ) or_return
|
||||||
|
case uintptr: (^uintptr)(ptr)^ = cast(uintptr) bounded_uint(value, cast(u128)max(uintptr)) or_return
|
||||||
|
|
||||||
|
case u16le: (^u16le) (ptr)^ = cast(u16le) bounded_uint(value, cast(u128)max(u16le) ) or_return
|
||||||
|
case u32le: (^u32le) (ptr)^ = cast(u32le) bounded_uint(value, cast(u128)max(u32le) ) or_return
|
||||||
|
case u64le: (^u64le) (ptr)^ = cast(u64le) bounded_uint(value, cast(u128)max(u64le) ) or_return
|
||||||
|
case u128le: (^u128le) (ptr)^ = cast(u128le) bounded_uint(value, cast(u128)max(u128le) ) or_return
|
||||||
|
|
||||||
|
case u16be: (^u16be) (ptr)^ = cast(u16be) bounded_uint(value, cast(u128)max(u16be) ) or_return
|
||||||
|
case u32be: (^u32be) (ptr)^ = cast(u32be) bounded_uint(value, cast(u128)max(u32be) ) or_return
|
||||||
|
case u64be: (^u64be) (ptr)^ = cast(u64be) bounded_uint(value, cast(u128)max(u64be) ) or_return
|
||||||
|
case u128be: (^u128be) (ptr)^ = cast(u128be) bounded_uint(value, cast(u128)max(u128be) ) or_return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case runtime.Type_Info_Rune:
|
||||||
|
if utf8.rune_count_in_string(str) != 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
(^rune)(ptr)^ = utf8.rune_at_pos(str, 0)
|
||||||
|
|
||||||
|
case runtime.Type_Info_Float:
|
||||||
|
value := strconv.parse_f64(str) or_return
|
||||||
|
switch type_info.id {
|
||||||
|
case f16: (^f16) (ptr)^ = cast(f16) value
|
||||||
|
case f32: (^f32) (ptr)^ = cast(f32) value
|
||||||
|
case f64: (^f64) (ptr)^ = value
|
||||||
|
|
||||||
|
case f16le: (^f16le)(ptr)^ = cast(f16le) value
|
||||||
|
case f32le: (^f32le)(ptr)^ = cast(f32le) value
|
||||||
|
case f64le: (^f64le)(ptr)^ = cast(f64le) value
|
||||||
|
|
||||||
|
case f16be: (^f16be)(ptr)^ = cast(f16be) value
|
||||||
|
case f32be: (^f32be)(ptr)^ = cast(f32be) value
|
||||||
|
case f64be: (^f64be)(ptr)^ = cast(f64be) value
|
||||||
|
}
|
||||||
|
|
||||||
|
case runtime.Type_Info_Complex:
|
||||||
|
value := strconv.parse_complex128(str) or_return
|
||||||
|
switch type_info.id {
|
||||||
|
case complex32: (^complex32) (ptr)^ = (complex32)(value)
|
||||||
|
case complex64: (^complex64) (ptr)^ = (complex64)(value)
|
||||||
|
case complex128: (^complex128)(ptr)^ = value
|
||||||
|
}
|
||||||
|
|
||||||
|
case runtime.Type_Info_Quaternion:
|
||||||
|
value := strconv.parse_quaternion256(str) or_return
|
||||||
|
switch type_info.id {
|
||||||
|
case quaternion64: (^quaternion64) (ptr)^ = (quaternion64)(value)
|
||||||
|
case quaternion128: (^quaternion128)(ptr)^ = (quaternion128)(value)
|
||||||
|
case quaternion256: (^quaternion256)(ptr)^ = value
|
||||||
|
}
|
||||||
|
|
||||||
|
case runtime.Type_Info_String:
|
||||||
|
if specific_type_info.is_cstring {
|
||||||
|
cstr_ptr := (^cstring)(ptr)
|
||||||
|
if cstr_ptr != nil {
|
||||||
|
// Prevent memory leaks from us setting this value multiple times.
|
||||||
|
delete(cstr_ptr^)
|
||||||
|
}
|
||||||
|
cstr_ptr^ = strings.clone_to_cstring(str)
|
||||||
|
} else {
|
||||||
|
(^string)(ptr)^ = str
|
||||||
|
}
|
||||||
|
|
||||||
|
case runtime.Type_Info_Boolean:
|
||||||
|
value := strconv.parse_bool(str) or_return
|
||||||
|
switch type_info.id {
|
||||||
|
case bool: (^bool)(ptr)^ = value
|
||||||
|
case b8: (^b8) (ptr)^ = b8(value)
|
||||||
|
case b16: (^b16) (ptr)^ = b16(value)
|
||||||
|
case b32: (^b32) (ptr)^ = b32(value)
|
||||||
|
case b64: (^b64) (ptr)^ = b64(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
case runtime.Type_Info_Bit_Set:
|
||||||
|
// Parse a string of 1's and 0's, from left to right,
|
||||||
|
// least significant bit to most significant bit.
|
||||||
|
value: u128
|
||||||
|
|
||||||
|
// NOTE: `upper` is inclusive, i.e: `0..=31`
|
||||||
|
max_bit_index := u128(1 + specific_type_info.upper - specific_type_info.lower)
|
||||||
|
bit_index := u128(0)
|
||||||
|
#no_bounds_check for string_index in 0..<uint(len(str)) {
|
||||||
|
if bit_index == max_bit_index {
|
||||||
|
// The string's too long for this bit_set.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch str[string_index] {
|
||||||
|
case '1':
|
||||||
|
value |= 1 << bit_index
|
||||||
|
bit_index += 1
|
||||||
|
case '0':
|
||||||
|
bit_index += 1
|
||||||
|
continue
|
||||||
|
case '_':
|
||||||
|
continue
|
||||||
|
case:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if specific_type_info.underlying != nil {
|
||||||
|
set_unbounded_integer_by_type(ptr, value, specific_type_info.underlying.id)
|
||||||
|
} else {
|
||||||
|
switch 8*type_info.size {
|
||||||
|
case 8: (^u8) (ptr)^ = cast(u8) value
|
||||||
|
case 16: (^u16) (ptr)^ = cast(u16) value
|
||||||
|
case 32: (^u32) (ptr)^ = cast(u32) value
|
||||||
|
case 64: (^u64) (ptr)^ = cast(u64) value
|
||||||
|
case 128: (^u128)(ptr)^ = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case:
|
||||||
|
fmt.panicf("Unsupported base data type: %v", specific_type_info)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// This proc exists to make error handling easier, since everything in the base
|
||||||
|
// type one above works on booleans. It's a simple parsing error if it's false.
|
||||||
|
//
|
||||||
|
// However, here we have to be more careful about how we handle errors,
|
||||||
|
// especially with files.
|
||||||
|
//
|
||||||
|
// We want to provide as informative as an error as we can.
|
||||||
|
@(optimization_mode="favor_size", disabled=NO_CORE_NAMED_TYPES)
|
||||||
|
parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type: typeid, arg_tag: string, out_error: ^Error) {
|
||||||
|
// Core types currently supported:
|
||||||
|
//
|
||||||
|
// - os.Handle
|
||||||
|
// - time.Time
|
||||||
|
// - datetime.DateTime
|
||||||
|
// - net.Host_Or_Endpoint
|
||||||
|
|
||||||
|
GENERIC_RFC_3339_ERROR :: "Invalid RFC 3339 string. Try this format: `yyyy-mm-ddThh:mm:ssZ`, for example `2024-02-29T16:30:00Z`."
|
||||||
|
|
||||||
|
out_error^ = nil
|
||||||
|
|
||||||
|
if data_type == os.Handle {
|
||||||
|
// NOTE: `os` is hopefully available everywhere, even if it might panic on some calls.
|
||||||
|
wants_read := false
|
||||||
|
wants_write := false
|
||||||
|
mode: int
|
||||||
|
|
||||||
|
if file, ok := get_struct_subtag(arg_tag, SUBTAG_FILE); ok {
|
||||||
|
for i in 0..<len(file) {
|
||||||
|
#no_bounds_check switch file[i] {
|
||||||
|
case 'r': wants_read = true
|
||||||
|
case 'w': wants_write = true
|
||||||
|
case 'c': mode |= os.O_CREATE
|
||||||
|
case 'a': mode |= os.O_APPEND
|
||||||
|
case 't': mode |= os.O_TRUNC
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sane default.
|
||||||
|
// owner/group/other: r--r--r--
|
||||||
|
perms: int = 0o444
|
||||||
|
|
||||||
|
if wants_read && wants_write {
|
||||||
|
mode |= os.O_RDWR
|
||||||
|
perms |= 0o200
|
||||||
|
} else if wants_write {
|
||||||
|
mode |= os.O_WRONLY
|
||||||
|
perms |= 0o200
|
||||||
|
} else {
|
||||||
|
mode |= os.O_RDONLY
|
||||||
|
}
|
||||||
|
|
||||||
|
if permstr, ok := get_struct_subtag(arg_tag, SUBTAG_PERMS); ok {
|
||||||
|
if value, parse_ok := strconv.parse_u64_of_base(permstr, 8); parse_ok {
|
||||||
|
perms = int(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handle, errno := os.open(str, mode, perms)
|
||||||
|
if errno != 0 {
|
||||||
|
// NOTE(Feoramund): os.Errno is system-dependent, and there's
|
||||||
|
// currently no good way to translate them all into strings.
|
||||||
|
//
|
||||||
|
// The upcoming `os2` package will hopefully solve this.
|
||||||
|
//
|
||||||
|
// We can at least provide the number for now, so the user can look
|
||||||
|
// it up.
|
||||||
|
out_error^ = Open_File_Error {
|
||||||
|
str,
|
||||||
|
errno,
|
||||||
|
mode,
|
||||||
|
perms,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
(^os.Handle)(ptr)^ = handle
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
when IMPORTING_TIME {
|
||||||
|
if data_type == time.Time {
|
||||||
|
// NOTE: The leap second data is discarded.
|
||||||
|
res, consumed := time.rfc3339_to_time_utc(str)
|
||||||
|
if consumed == 0 {
|
||||||
|
// The RFC 3339 parsing facilities provide no indication as to what
|
||||||
|
// went wrong, so just treat it as a regular parsing error.
|
||||||
|
out_error^ = Parse_Error {
|
||||||
|
.Bad_Value,
|
||||||
|
GENERIC_RFC_3339_ERROR,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
(^time.Time)(ptr)^ = res
|
||||||
|
return
|
||||||
|
} else if data_type == datetime.DateTime {
|
||||||
|
// NOTE: The UTC offset and leap second data are discarded.
|
||||||
|
res, _, _, consumed := time.rfc3339_to_components(str)
|
||||||
|
if consumed == 0 {
|
||||||
|
out_error^ = Parse_Error {
|
||||||
|
.Bad_Value,
|
||||||
|
GENERIC_RFC_3339_ERROR,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
(^datetime.DateTime)(ptr)^ = res
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when IMPORTING_NET {
|
||||||
|
if try_net_parse_workaround(data_type, str, ptr, out_error) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out_error ^= Parse_Error {
|
||||||
|
// The caller will add more details.
|
||||||
|
.Unsupported_Type,
|
||||||
|
"",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(optimization_mode="favor_size")
|
||||||
|
set_unbounded_integer_by_type :: proc(ptr: rawptr, value: $T, data_type: typeid) where intrinsics.type_is_integer(T) {
|
||||||
|
switch data_type {
|
||||||
|
case i8: (^i8) (ptr)^ = cast(i8) value
|
||||||
|
case i16: (^i16) (ptr)^ = cast(i16) value
|
||||||
|
case i32: (^i32) (ptr)^ = cast(i32) value
|
||||||
|
case i64: (^i64) (ptr)^ = cast(i64) value
|
||||||
|
case i128: (^i128) (ptr)^ = cast(i128) value
|
||||||
|
|
||||||
|
case int: (^int) (ptr)^ = cast(int) value
|
||||||
|
|
||||||
|
case i16le: (^i16le) (ptr)^ = cast(i16le) value
|
||||||
|
case i32le: (^i32le) (ptr)^ = cast(i32le) value
|
||||||
|
case i64le: (^i64le) (ptr)^ = cast(i64le) value
|
||||||
|
case i128le: (^i128le) (ptr)^ = cast(i128le) value
|
||||||
|
|
||||||
|
case i16be: (^i16be) (ptr)^ = cast(i16be) value
|
||||||
|
case i32be: (^i32be) (ptr)^ = cast(i32be) value
|
||||||
|
case i64be: (^i64be) (ptr)^ = cast(i64be) value
|
||||||
|
case i128be: (^i128be) (ptr)^ = cast(i128be) value
|
||||||
|
|
||||||
|
case u8: (^u8) (ptr)^ = cast(u8) value
|
||||||
|
case u16: (^u16) (ptr)^ = cast(u16) value
|
||||||
|
case u32: (^u32) (ptr)^ = cast(u32) value
|
||||||
|
case u64: (^u64) (ptr)^ = cast(u64) value
|
||||||
|
case u128: (^u128) (ptr)^ = cast(u128) value
|
||||||
|
|
||||||
|
case uint: (^uint) (ptr)^ = cast(uint) value
|
||||||
|
case uintptr: (^uintptr)(ptr)^ = cast(uintptr) value
|
||||||
|
|
||||||
|
case u16le: (^u16le) (ptr)^ = cast(u16le) value
|
||||||
|
case u32le: (^u32le) (ptr)^ = cast(u32le) value
|
||||||
|
case u64le: (^u64le) (ptr)^ = cast(u64le) value
|
||||||
|
case u128le: (^u128le) (ptr)^ = cast(u128le) value
|
||||||
|
|
||||||
|
case u16be: (^u16be) (ptr)^ = cast(u16be) value
|
||||||
|
case u32be: (^u32be) (ptr)^ = cast(u32be) value
|
||||||
|
case u64be: (^u64be) (ptr)^ = cast(u64be) value
|
||||||
|
case u128be: (^u128be) (ptr)^ = cast(u128be) value
|
||||||
|
|
||||||
|
case rune: (^rune) (ptr)^ = cast(rune) value
|
||||||
|
|
||||||
|
case:
|
||||||
|
fmt.panicf("Unsupported integer backing type: %v", data_type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(optimization_mode="favor_size")
|
||||||
|
parse_and_set_pointer_by_type :: proc(ptr: rawptr, str: string, type_info: ^runtime.Type_Info, arg_tag: string) -> (error: Error) {
|
||||||
|
#partial switch specific_type_info in type_info.variant {
|
||||||
|
case runtime.Type_Info_Named:
|
||||||
|
if global_custom_type_setter != nil {
|
||||||
|
// The program gets to go first.
|
||||||
|
error_message, handled, alloc_error := global_custom_type_setter(ptr, type_info.id, str, arg_tag)
|
||||||
|
|
||||||
|
if alloc_error != nil {
|
||||||
|
// There was an allocation error. Bail out.
|
||||||
|
return Parse_Error {
|
||||||
|
alloc_error,
|
||||||
|
"Custom type setter encountered allocation error.",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if handled {
|
||||||
|
// The program handled the type.
|
||||||
|
|
||||||
|
if len(error_message) != 0 {
|
||||||
|
// However, there was an error. Pass it along.
|
||||||
|
error = Parse_Error {
|
||||||
|
.Bad_Value,
|
||||||
|
error_message,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Might be a named enum. Need to check here first, since we handle all enums.
|
||||||
|
if enum_type_info, is_enum := specific_type_info.base.variant.(runtime.Type_Info_Enum); is_enum {
|
||||||
|
if value, ok := reflect.enum_from_name_any(type_info.id, str); ok {
|
||||||
|
set_unbounded_integer_by_type(ptr, value, enum_type_info.base.id)
|
||||||
|
} else {
|
||||||
|
return Parse_Error {
|
||||||
|
.Bad_Value,
|
||||||
|
fmt.tprintf("Invalid value name. Valid names are: %s", enum_type_info.names),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parse_and_set_pointer_by_named_type(ptr, str, type_info.id, arg_tag, &error)
|
||||||
|
|
||||||
|
if error != nil {
|
||||||
|
// So far, it's none of the types that we recognize.
|
||||||
|
// Check to see if we can set it by base type, if allowed.
|
||||||
|
if _, is_indistinct := get_struct_subtag(arg_tag, SUBTAG_INDISTINCT); is_indistinct {
|
||||||
|
return parse_and_set_pointer_by_type(ptr, str, specific_type_info.base, arg_tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case runtime.Type_Info_Dynamic_Array:
|
||||||
|
ptr := cast(^runtime.Raw_Dynamic_Array)ptr
|
||||||
|
|
||||||
|
// Try to convert the value first.
|
||||||
|
elem_backing, alloc_error := mem.alloc_bytes(specific_type_info.elem.size, specific_type_info.elem.align)
|
||||||
|
if alloc_error != nil {
|
||||||
|
return Parse_Error {
|
||||||
|
alloc_error,
|
||||||
|
"Failed to allocate element backing for dynamic array.",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defer delete(elem_backing)
|
||||||
|
parse_and_set_pointer_by_type(raw_data(elem_backing), str, specific_type_info.elem, arg_tag) or_return
|
||||||
|
|
||||||
|
if !runtime.__dynamic_array_resize(ptr, specific_type_info.elem.size, specific_type_info.elem.align, ptr.len + 1) {
|
||||||
|
// NOTE: This is purely an assumption that it's OOM.
|
||||||
|
// Regardless, the resize failed.
|
||||||
|
return Parse_Error {
|
||||||
|
runtime.Allocator_Error.Out_Of_Memory,
|
||||||
|
"Failed to resize dynamic array.",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
subptr := rawptr(
|
||||||
|
uintptr(ptr.data) +
|
||||||
|
uintptr((ptr.len - 1) * specific_type_info.elem.size))
|
||||||
|
mem.copy(subptr, raw_data(elem_backing), len(elem_backing))
|
||||||
|
|
||||||
|
case runtime.Type_Info_Enum:
|
||||||
|
// This is a nameless enum.
|
||||||
|
// The code here is virtually the same as above for named enums.
|
||||||
|
if value, ok := reflect.enum_from_name_any(type_info.id, str); ok {
|
||||||
|
set_unbounded_integer_by_type(ptr, value, specific_type_info.base.id)
|
||||||
|
} else {
|
||||||
|
return Parse_Error {
|
||||||
|
.Bad_Value,
|
||||||
|
fmt.tprintf("Invalid value name. Valid names are: %s", specific_type_info.names),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case:
|
||||||
|
if !parse_and_set_pointer_by_base_type(ptr, str, type_info) {
|
||||||
|
return Parse_Error {
|
||||||
|
// The caller will add more details.
|
||||||
|
.Bad_Value,
|
||||||
|
"",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
get_struct_subtag :: get_subtag
|
||||||
|
|
||||||
|
get_field_name :: proc(field: reflect.Struct_Field) -> string {
|
||||||
|
if args_tag, ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS); ok {
|
||||||
|
if name_subtag, name_ok := get_struct_subtag(args_tag, SUBTAG_NAME); name_ok {
|
||||||
|
return name_subtag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
name, _ := strings.replace_all(field.name, "_", "-", context.temp_allocator)
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
get_field_pos :: proc(field: reflect.Struct_Field) -> (int, bool) {
|
||||||
|
if args_tag, ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS); ok {
|
||||||
|
if pos_subtag, pos_ok := get_struct_subtag(args_tag, SUBTAG_POS); pos_ok {
|
||||||
|
if value, parse_ok := strconv.parse_u64_of_base(pos_subtag, 10); parse_ok {
|
||||||
|
return int(value), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a struct field by its field name or `name` subtag.
|
||||||
|
get_field_by_name :: proc(model: ^$T, name: string) -> (result: reflect.Struct_Field, index: int, error: Error) {
|
||||||
|
for field, i in reflect.struct_fields_zipped(T) {
|
||||||
|
if get_field_name(field) == name {
|
||||||
|
return field, i, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
error = Parse_Error {
|
||||||
|
.Missing_Flag,
|
||||||
|
fmt.tprintf("Unable to find any flag named `%s`.", name),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a struct field by its `pos` subtag.
|
||||||
|
get_field_by_pos :: proc(model: ^$T, pos: int) -> (result: reflect.Struct_Field, index: int, ok: bool) {
|
||||||
|
for field, i in reflect.struct_fields_zipped(T) {
|
||||||
|
args_tag := reflect.struct_tag_lookup(field.tag, TAG_ARGS) or_continue
|
||||||
|
pos_subtag := get_struct_subtag(args_tag, SUBTAG_POS) or_continue
|
||||||
|
|
||||||
|
value, parse_ok := strconv.parse_u64_of_base(pos_subtag, 10)
|
||||||
|
if parse_ok && cast(int)value == pos {
|
||||||
|
return field, i, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
//+private
|
||||||
|
//+build !freebsd !netbsd !openbsd
|
||||||
|
package flags
|
||||||
|
|
||||||
|
import "core:net"
|
||||||
|
|
||||||
|
// This proc exists purely as a workaround for import restrictions.
|
||||||
|
// Returns true if caller should return early.
|
||||||
|
try_net_parse_workaround :: #force_inline proc (
|
||||||
|
data_type: typeid,
|
||||||
|
str: string,
|
||||||
|
ptr: rawptr,
|
||||||
|
out_error: ^Error,
|
||||||
|
) -> bool {
|
||||||
|
if data_type == net.Host_Or_Endpoint {
|
||||||
|
addr, net_error := net.parse_hostname_or_endpoint(str)
|
||||||
|
if net_error != nil {
|
||||||
|
// We pass along `net.Error` here.
|
||||||
|
out_error^ = Parse_Error {
|
||||||
|
net_error,
|
||||||
|
"Invalid Host/Endpoint.",
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
(cast(^net.Host_Or_Endpoint)ptr)^ = addr
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -0,0 +1,244 @@
|
|||||||
|
//+private
|
||||||
|
package flags
|
||||||
|
|
||||||
|
@require import "base:runtime"
|
||||||
|
@require import "core:container/bit_array"
|
||||||
|
@require import "core:fmt"
|
||||||
|
@require import "core:mem"
|
||||||
|
@require import "core:os"
|
||||||
|
@require import "core:reflect"
|
||||||
|
@require import "core:strconv"
|
||||||
|
@require import "core:strings"
|
||||||
|
|
||||||
|
// This proc is used to assert that `T` meets the expectations of the library.
|
||||||
|
@(optimization_mode="favor_size", disabled=ODIN_DISABLE_ASSERT)
|
||||||
|
validate_structure :: proc(model_type: $T, style: Parsing_Style, loc := #caller_location) {
|
||||||
|
positionals_assigned_so_far: bit_array.Bit_Array
|
||||||
|
defer bit_array.destroy(&positionals_assigned_so_far)
|
||||||
|
|
||||||
|
check_fields: for field in reflect.struct_fields_zipped(T) {
|
||||||
|
if style == .Unix {
|
||||||
|
#partial switch specific_type_info in field.type.variant {
|
||||||
|
case runtime.Type_Info_Map:
|
||||||
|
fmt.panicf("%T.%s is a map type, and these are not supported in UNIX-style parsing mode.",
|
||||||
|
model_type, field.name, loc = loc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
name_is_safe := true
|
||||||
|
defer {
|
||||||
|
fmt.assertf(name_is_safe, "%T.%s is using a reserved name.",
|
||||||
|
model_type, field.name, loc = loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch field.name {
|
||||||
|
case RESERVED_HELP_FLAG, RESERVED_HELP_FLAG_SHORT:
|
||||||
|
name_is_safe = false
|
||||||
|
}
|
||||||
|
|
||||||
|
args_tag, ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS)
|
||||||
|
if !ok {
|
||||||
|
// If it has no args tag, then we've checked all we need to.
|
||||||
|
// Most of this proc is validating that the subtags are sane.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if name, has_name := get_struct_subtag(args_tag, SUBTAG_NAME); has_name {
|
||||||
|
fmt.assertf(len(name) > 0, "%T.%s has a zero-length `%s`.",
|
||||||
|
model_type, field.name, SUBTAG_NAME, loc = loc)
|
||||||
|
|
||||||
|
fmt.assertf(strings.index(name, " ") == -1, "%T.%s has a `%s` with spaces in it.",
|
||||||
|
model_type, field.name, SUBTAG_NAME, loc = loc)
|
||||||
|
|
||||||
|
switch name {
|
||||||
|
case RESERVED_HELP_FLAG, RESERVED_HELP_FLAG_SHORT:
|
||||||
|
name_is_safe = false
|
||||||
|
continue check_fields
|
||||||
|
case:
|
||||||
|
name_is_safe = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if pos_str, has_pos := get_struct_subtag(args_tag, SUBTAG_POS); has_pos {
|
||||||
|
#partial switch specific_type_info in field.type.variant {
|
||||||
|
case runtime.Type_Info_Map:
|
||||||
|
fmt.panicf("%T.%s has `%s` defined, and this does not make sense on a map type.",
|
||||||
|
model_type, field.name, SUBTAG_POS, loc = loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
pos_value, pos_ok := strconv.parse_u64_of_base(pos_str, 10)
|
||||||
|
fmt.assertf(pos_ok, "%T.%s has `%s` defined as %q but cannot be parsed a base-10 integer >= 0.",
|
||||||
|
model_type, field.name, SUBTAG_POS, pos_str, loc = loc)
|
||||||
|
fmt.assertf(!bit_array.get(&positionals_assigned_so_far, pos_value), "%T.%s has `%s` set to #%i, but that position has already been assigned to another flag.",
|
||||||
|
model_type, field.name, SUBTAG_POS, pos_value, loc = loc)
|
||||||
|
bit_array.set(&positionals_assigned_so_far, pos_value)
|
||||||
|
}
|
||||||
|
|
||||||
|
required_min, required_max: int
|
||||||
|
if requirement, is_required := get_struct_subtag(args_tag, SUBTAG_REQUIRED); is_required {
|
||||||
|
fmt.assertf(!reflect.is_boolean(field.type), "%T.%s is a required boolean. This is disallowed.",
|
||||||
|
model_type, field.name, loc = loc)
|
||||||
|
|
||||||
|
fmt.assertf(field.name != INTERNAL_VARIADIC_FLAG, "%T.%s is defined as required. This is disallowed.",
|
||||||
|
model_type, field.name, loc = loc)
|
||||||
|
|
||||||
|
if len(requirement) > 0 {
|
||||||
|
if required_min, required_max, ok = parse_requirements(requirement); ok {
|
||||||
|
#partial switch specific_type_info in field.type.variant {
|
||||||
|
case runtime.Type_Info_Dynamic_Array:
|
||||||
|
fmt.assertf(required_min != required_max, "%T.%s has `%s` defined as %q, but the minimum and maximum are the same. Increase the maximum by 1 for an exact number of arguments: (%i<%i)",
|
||||||
|
model_type,
|
||||||
|
field.name,
|
||||||
|
SUBTAG_REQUIRED,
|
||||||
|
requirement,
|
||||||
|
required_min,
|
||||||
|
1 + required_max,
|
||||||
|
loc = loc)
|
||||||
|
|
||||||
|
fmt.assertf(required_min < required_max, "%T.%s has `%s` defined as %q, but the minimum and maximum are swapped.",
|
||||||
|
model_type, field.name, SUBTAG_REQUIRED, requirement, loc = loc)
|
||||||
|
|
||||||
|
case:
|
||||||
|
fmt.panicf("%T.%s has `%s` defined as %q, but ranges are only supported on dynamic arrays.",
|
||||||
|
model_type, field.name, SUBTAG_REQUIRED, requirement, loc = loc)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.panicf("%T.%s has `%s` defined as %q, but it cannot be parsed as a valid range.",
|
||||||
|
model_type, field.name, SUBTAG_REQUIRED, requirement, loc = loc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if length, is_variadic := get_struct_subtag(args_tag, SUBTAG_VARIADIC); is_variadic {
|
||||||
|
if value, parse_ok := strconv.parse_u64_of_base(length, 10); parse_ok {
|
||||||
|
fmt.assertf(value > 0,
|
||||||
|
"%T.%s has `%s` set to %i. It must be greater than zero.",
|
||||||
|
model_type, field.name, value, SUBTAG_VARIADIC, loc = loc)
|
||||||
|
fmt.assertf(value != 1,
|
||||||
|
"%T.%s has `%s` set to 1. This has no effect.",
|
||||||
|
model_type, field.name, SUBTAG_VARIADIC, loc = loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
#partial switch specific_type_info in field.type.variant {
|
||||||
|
case runtime.Type_Info_Dynamic_Array:
|
||||||
|
fmt.assertf(style != .Odin,
|
||||||
|
"%T.%s has `%s` defined, but this only makes sense in UNIX-style parsing mode.",
|
||||||
|
model_type, field.name, SUBTAG_VARIADIC, loc = loc)
|
||||||
|
case:
|
||||||
|
fmt.panicf("%T.%s has `%s` defined, but this only makes sense on dynamic arrays.",
|
||||||
|
model_type, field.name, SUBTAG_VARIADIC, loc = loc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allowed_to_define_file_perms: bool = ---
|
||||||
|
#partial switch specific_type_info in field.type.variant {
|
||||||
|
case runtime.Type_Info_Map:
|
||||||
|
allowed_to_define_file_perms = specific_type_info.value.id == os.Handle
|
||||||
|
case runtime.Type_Info_Dynamic_Array:
|
||||||
|
allowed_to_define_file_perms = specific_type_info.elem.id == os.Handle
|
||||||
|
case:
|
||||||
|
allowed_to_define_file_perms = field.type.id == os.Handle
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, has_file := get_struct_subtag(args_tag, SUBTAG_FILE); has_file {
|
||||||
|
fmt.assertf(allowed_to_define_file_perms, "%T.%s has `%s` defined, but it is not nor does it contain an `os.Handle` type.",
|
||||||
|
model_type, field.name, SUBTAG_FILE, loc = loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, has_perms := get_struct_subtag(args_tag, SUBTAG_PERMS); has_perms {
|
||||||
|
fmt.assertf(allowed_to_define_file_perms, "%T.%s has `%s` defined, but it is not nor does it contain an `os.Handle` type.",
|
||||||
|
model_type, field.name, SUBTAG_PERMS, loc = loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
#partial switch specific_type_info in field.type.variant {
|
||||||
|
case runtime.Type_Info_Map:
|
||||||
|
fmt.assertf(reflect.is_string(specific_type_info.key), "%T.%s is defined as a map[%T]. Only string types are currently supported as map keys.",
|
||||||
|
model_type,
|
||||||
|
field.name,
|
||||||
|
specific_type_info.key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate that all the required arguments are set and that the set arguments
|
||||||
|
// are up to the program's expectations.
|
||||||
|
@(optimization_mode="favor_size")
|
||||||
|
validate_arguments :: proc(model: ^$T, parser: ^Parser) -> Error {
|
||||||
|
check_fields: for field, index in reflect.struct_fields_zipped(T) {
|
||||||
|
was_set := bit_array.get(&parser.fields_set, index)
|
||||||
|
|
||||||
|
field_name := get_field_name(field)
|
||||||
|
args_tag := reflect.struct_tag_get(field.tag, TAG_ARGS)
|
||||||
|
requirement, is_required := get_struct_subtag(args_tag, SUBTAG_REQUIRED)
|
||||||
|
|
||||||
|
required_min, required_max: int
|
||||||
|
has_requirements: bool
|
||||||
|
if is_required {
|
||||||
|
required_min, required_max, has_requirements = parse_requirements(requirement)
|
||||||
|
}
|
||||||
|
|
||||||
|
if has_requirements && required_min == 0 {
|
||||||
|
// Allow `0<n` or `<n` to bypass the required condition.
|
||||||
|
is_required = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, is_array := field.type.variant.(runtime.Type_Info_Dynamic_Array); is_array && has_requirements {
|
||||||
|
// If it's an array, make sure it meets the required number of arguments.
|
||||||
|
ptr := cast(^runtime.Raw_Dynamic_Array)(cast(uintptr)model + field.offset)
|
||||||
|
if required_min == required_max - 1 && ptr.len != required_min {
|
||||||
|
return Validation_Error {
|
||||||
|
fmt.tprintf("The flag `%s` had %i option%s set, but it requires exactly %i.",
|
||||||
|
field_name,
|
||||||
|
ptr.len,
|
||||||
|
"" if ptr.len == 1 else "s",
|
||||||
|
required_min),
|
||||||
|
}
|
||||||
|
} else if required_min > ptr.len || ptr.len >= required_max {
|
||||||
|
if required_max == max(int) {
|
||||||
|
return Validation_Error {
|
||||||
|
fmt.tprintf("The flag `%s` had %i option%s set, but it requires at least %i.",
|
||||||
|
field_name,
|
||||||
|
ptr.len,
|
||||||
|
"" if ptr.len == 1 else "s",
|
||||||
|
required_min),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Validation_Error {
|
||||||
|
fmt.tprintf("The flag `%s` had %i option%s set, but it requires at least %i and at most %i.",
|
||||||
|
field_name,
|
||||||
|
ptr.len,
|
||||||
|
"" if ptr.len == 1 else "s",
|
||||||
|
required_min,
|
||||||
|
required_max - 1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if !was_set {
|
||||||
|
if is_required {
|
||||||
|
return Validation_Error {
|
||||||
|
fmt.tprintf("The required flag `%s` was not set.", field_name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not set, not required; moving on.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// All default checks have passed. The program gets a look at it now.
|
||||||
|
|
||||||
|
if global_custom_flag_checker != nil {
|
||||||
|
ptr := cast(rawptr)(cast(uintptr)model + field.offset)
|
||||||
|
error := global_custom_flag_checker(model,
|
||||||
|
field.name,
|
||||||
|
mem.make_any(ptr, field.type.id),
|
||||||
|
args_tag)
|
||||||
|
|
||||||
|
if len(error) > 0 {
|
||||||
|
// The program reported an error message.
|
||||||
|
return Validation_Error { error }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
package flags
|
||||||
|
|
||||||
|
@require import "core:container/bit_array"
|
||||||
|
@require import "core:fmt"
|
||||||
|
|
||||||
|
Parsing_Style :: enum {
|
||||||
|
// Odin-style: `-flag`, `-flag:option`, `-map:key=value`
|
||||||
|
Odin,
|
||||||
|
// UNIX-style: `-flag` or `--flag`, `--flag=argument`, `--flag argument repeating-argument`
|
||||||
|
Unix,
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Parse a slice of command-line arguments into an annotated struct.
|
||||||
|
|
||||||
|
*Allocates Using Provided Allocator*
|
||||||
|
|
||||||
|
By default, this proc will only allocate memory outside of its lifetime if it
|
||||||
|
has to append to a dynamic array, set a map value, or set a cstring.
|
||||||
|
|
||||||
|
The program is expected to free any allocations on `model` as a result of parsing.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- model: A pointer to an annotated struct with flag definitions.
|
||||||
|
- args: A slice of strings, usually `os.args[1:]`.
|
||||||
|
- style: The argument parsing style.
|
||||||
|
- validate_args: If `true`, will ensure that all required arguments are set if no errors occurred.
|
||||||
|
- strict: If `true`, will return on first error. Otherwise, parsing continues.
|
||||||
|
- allocator: (default: context.allocator)
|
||||||
|
- loc: The caller location for debugging purposes (default: #caller_location)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- error: A union of errors; parsing, file open, a help request, or validation.
|
||||||
|
*/
|
||||||
|
@(optimization_mode="favor_size")
|
||||||
|
parse :: proc(
|
||||||
|
model: ^$T,
|
||||||
|
args: []string,
|
||||||
|
style: Parsing_Style = .Odin,
|
||||||
|
validate_args: bool = true,
|
||||||
|
strict: bool = true,
|
||||||
|
allocator := context.allocator,
|
||||||
|
loc := #caller_location,
|
||||||
|
) -> (error: Error) {
|
||||||
|
context.allocator = allocator
|
||||||
|
validate_structure(model^, style, loc)
|
||||||
|
|
||||||
|
parser: Parser
|
||||||
|
defer {
|
||||||
|
bit_array.destroy(&parser.filled_pos)
|
||||||
|
bit_array.destroy(&parser.fields_set)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch style {
|
||||||
|
case .Odin:
|
||||||
|
for arg in args {
|
||||||
|
error = parse_one_odin_arg(model, &parser, arg)
|
||||||
|
if strict && error != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case .Unix:
|
||||||
|
// Support for `-flag argument (repeating-argument ...)`
|
||||||
|
future_args: int
|
||||||
|
current_flag: string
|
||||||
|
|
||||||
|
for i := 0; i < len(args); i += 1 {
|
||||||
|
#no_bounds_check arg := args[i]
|
||||||
|
future_args, current_flag, error = parse_one_unix_arg(model, &parser, arg)
|
||||||
|
if strict && error != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for starting_future_args := future_args; future_args > 0; future_args -= 1 {
|
||||||
|
i += 1
|
||||||
|
if i == len(args) {
|
||||||
|
if future_args == starting_future_args {
|
||||||
|
return Parse_Error {
|
||||||
|
.No_Value,
|
||||||
|
fmt.tprintf("Expected a value for `%s` but none was given.", current_flag),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
#no_bounds_check arg = args[i]
|
||||||
|
|
||||||
|
error = set_option(model, &parser, current_flag, arg)
|
||||||
|
if strict && error != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if error == nil && validate_args {
|
||||||
|
return validate_arguments(model, &parser)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package flags
|
||||||
|
|
||||||
|
import "base:runtime"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Handle setting custom data types.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- data: A raw pointer to the field where the data will go.
|
||||||
|
- data_type: Type information on the underlying field.
|
||||||
|
- unparsed_value: The unparsed string that the flag is being set to.
|
||||||
|
- args_tag: The `args` tag from the struct's field.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- error: An error message, or an empty string if no error occurred.
|
||||||
|
- handled: A boolean indicating if the setter handles this type.
|
||||||
|
- alloc_error: If an allocation error occurred, return it here.
|
||||||
|
*/
|
||||||
|
Custom_Type_Setter :: #type proc(
|
||||||
|
data: rawptr,
|
||||||
|
data_type: typeid,
|
||||||
|
unparsed_value: string,
|
||||||
|
args_tag: string,
|
||||||
|
) -> (
|
||||||
|
error: string,
|
||||||
|
handled: bool,
|
||||||
|
alloc_error: runtime.Allocator_Error,
|
||||||
|
)
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
global_custom_type_setter: Custom_Type_Setter
|
||||||
|
|
||||||
|
/*
|
||||||
|
Set the global custom type setter.
|
||||||
|
|
||||||
|
Note that only one can be active at a time.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- setter: The type setter. Pass `nil` to disable any previously set setter.
|
||||||
|
*/
|
||||||
|
register_type_setter :: proc(setter: Custom_Type_Setter) {
|
||||||
|
global_custom_type_setter = setter
|
||||||
|
}
|
||||||
@@ -0,0 +1,293 @@
|
|||||||
|
package flags
|
||||||
|
|
||||||
|
import "base:runtime"
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:io"
|
||||||
|
import "core:reflect"
|
||||||
|
import "core:slice"
|
||||||
|
import "core:strconv"
|
||||||
|
import "core:strings"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Write out the documentation for the command-line arguments to a stream.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- out: The stream to write to.
|
||||||
|
- data_type: The typeid of the data structure to describe.
|
||||||
|
- program: The name of the program, usually the first argument to `os.args`.
|
||||||
|
- style: The argument parsing style, required to show flags in the proper style.
|
||||||
|
*/
|
||||||
|
@(optimization_mode="favor_size")
|
||||||
|
write_usage :: proc(out: io.Writer, data_type: typeid, program: string = "", style: Parsing_Style = .Odin) {
|
||||||
|
// All flags get their tags parsed so they can be reasoned about later.
|
||||||
|
Flag :: struct {
|
||||||
|
name: string,
|
||||||
|
usage: string,
|
||||||
|
type_description: string,
|
||||||
|
full_length: int,
|
||||||
|
pos: int,
|
||||||
|
required_min, required_max: int,
|
||||||
|
is_positional: bool,
|
||||||
|
is_required: bool,
|
||||||
|
is_boolean: bool,
|
||||||
|
is_variadic: bool,
|
||||||
|
variadic_length: int,
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// POSITIONAL+REQUIRED, POSITIONAL, REQUIRED, NON_REQUIRED+NON_POSITIONAL, ...
|
||||||
|
//
|
||||||
|
sort_flags :: proc(i, j: Flag) -> slice.Ordering {
|
||||||
|
// `varg` goes to the end.
|
||||||
|
if i.name == INTERNAL_VARIADIC_FLAG {
|
||||||
|
return .Greater
|
||||||
|
} else if j.name == INTERNAL_VARIADIC_FLAG {
|
||||||
|
return .Less
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle positionals.
|
||||||
|
if i.is_positional {
|
||||||
|
if j.is_positional {
|
||||||
|
return slice.cmp(i.pos, j.pos)
|
||||||
|
} else {
|
||||||
|
return .Less
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if j.is_positional {
|
||||||
|
return .Greater
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then required flags.
|
||||||
|
if i.is_required {
|
||||||
|
if !j.is_required {
|
||||||
|
return .Less
|
||||||
|
}
|
||||||
|
} else if j.is_required {
|
||||||
|
return .Greater
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, sort by name.
|
||||||
|
return slice.cmp(i.name, j.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
describe_array_requirements :: proc(flag: Flag) -> (spec: string) {
|
||||||
|
if flag.is_required {
|
||||||
|
if flag.required_min == flag.required_max - 1 {
|
||||||
|
spec = fmt.tprintf(", exactly %i", flag.required_min)
|
||||||
|
} else if flag.required_min > 0 && flag.required_max == max(int) {
|
||||||
|
spec = fmt.tprintf(", at least %i", flag.required_min)
|
||||||
|
} else if flag.required_min == 0 && flag.required_max > 1 {
|
||||||
|
spec = fmt.tprintf(", at most %i", flag.required_max - 1)
|
||||||
|
} else if flag.required_min > 0 && flag.required_max > 1 {
|
||||||
|
spec = fmt.tprintf(", between %i and %i", flag.required_min, flag.required_max - 1)
|
||||||
|
} else {
|
||||||
|
spec = ", required"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
builder := strings.builder_make()
|
||||||
|
defer strings.builder_destroy(&builder)
|
||||||
|
|
||||||
|
flag_prefix, flag_assignment: string = ---, ---
|
||||||
|
switch style {
|
||||||
|
case .Odin: flag_prefix = "-"; flag_assignment = ":"
|
||||||
|
case .Unix: flag_prefix = "--"; flag_assignment = " "
|
||||||
|
}
|
||||||
|
|
||||||
|
visible_flags: [dynamic]Flag
|
||||||
|
defer delete(visible_flags)
|
||||||
|
|
||||||
|
longest_flag_length: int
|
||||||
|
|
||||||
|
for field in reflect.struct_fields_zipped(data_type) {
|
||||||
|
flag: Flag
|
||||||
|
|
||||||
|
if args_tag, ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS); ok {
|
||||||
|
if _, is_hidden := get_struct_subtag(args_tag, SUBTAG_HIDDEN); is_hidden {
|
||||||
|
// Hidden flags stay hidden.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if pos_str, is_pos := get_struct_subtag(args_tag, SUBTAG_POS); is_pos {
|
||||||
|
flag.is_positional = true
|
||||||
|
if pos, parse_ok := strconv.parse_u64_of_base(pos_str, 10); parse_ok {
|
||||||
|
flag.pos = cast(int)pos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if requirement, is_required := get_struct_subtag(args_tag, SUBTAG_REQUIRED); is_required {
|
||||||
|
flag.is_required = true
|
||||||
|
flag.required_min, flag.required_max, _ = parse_requirements(requirement)
|
||||||
|
}
|
||||||
|
if length_str, is_variadic := get_struct_subtag(args_tag, SUBTAG_VARIADIC); is_variadic {
|
||||||
|
flag.is_variadic = true
|
||||||
|
if length, parse_ok := strconv.parse_u64_of_base(length_str, 10); parse_ok {
|
||||||
|
flag.variadic_length = cast(int)length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flag.name = get_field_name(field)
|
||||||
|
flag.is_boolean = reflect.is_boolean(field.type)
|
||||||
|
|
||||||
|
if usage, ok := reflect.struct_tag_lookup(field.tag, TAG_USAGE); ok {
|
||||||
|
flag.usage = usage
|
||||||
|
} else {
|
||||||
|
flag.usage = UNDOCUMENTED_FLAG
|
||||||
|
}
|
||||||
|
|
||||||
|
#partial switch specific_type_info in field.type.variant {
|
||||||
|
case runtime.Type_Info_Map:
|
||||||
|
flag.type_description = fmt.tprintf("<%v>=<%v>%s",
|
||||||
|
specific_type_info.key.id,
|
||||||
|
specific_type_info.value.id,
|
||||||
|
", required" if flag.is_required else "")
|
||||||
|
|
||||||
|
case runtime.Type_Info_Dynamic_Array:
|
||||||
|
requirement_spec := describe_array_requirements(flag)
|
||||||
|
|
||||||
|
if flag.is_variadic || flag.name == INTERNAL_VARIADIC_FLAG {
|
||||||
|
if flag.variadic_length == 0 {
|
||||||
|
flag.type_description = fmt.tprintf("<%v, ...>%s",
|
||||||
|
specific_type_info.elem.id,
|
||||||
|
requirement_spec)
|
||||||
|
} else {
|
||||||
|
flag.type_description = fmt.tprintf("<%v, %i at once>%s",
|
||||||
|
specific_type_info.elem.id,
|
||||||
|
flag.variadic_length,
|
||||||
|
requirement_spec)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
flag.type_description = fmt.tprintf("<%v>%s", specific_type_info.elem.id,
|
||||||
|
requirement_spec if len(requirement_spec) > 0 else ", multiple")
|
||||||
|
}
|
||||||
|
|
||||||
|
case:
|
||||||
|
if flag.is_boolean {
|
||||||
|
/*
|
||||||
|
if flag.is_required {
|
||||||
|
flag.type_description = ", required"
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
} else {
|
||||||
|
flag.type_description = fmt.tprintf("<%v>%s",
|
||||||
|
field.type.id,
|
||||||
|
", required" if flag.is_required else "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if flag.name == INTERNAL_VARIADIC_FLAG {
|
||||||
|
flag.full_length = len(flag.type_description)
|
||||||
|
} else if flag.is_boolean {
|
||||||
|
flag.full_length = len(flag_prefix) + len(flag.name) + len(flag.type_description)
|
||||||
|
} else {
|
||||||
|
flag.full_length = len(flag_prefix) + len(flag.name) + len(flag_assignment) + len(flag.type_description)
|
||||||
|
}
|
||||||
|
|
||||||
|
longest_flag_length = max(longest_flag_length, flag.full_length)
|
||||||
|
|
||||||
|
append(&visible_flags, flag)
|
||||||
|
}
|
||||||
|
|
||||||
|
slice.sort_by_cmp(visible_flags[:], sort_flags)
|
||||||
|
|
||||||
|
// All the flags have been figured out now.
|
||||||
|
|
||||||
|
if len(program) > 0 {
|
||||||
|
keep_it_short := len(visible_flags) >= ONE_LINE_FLAG_CUTOFF_COUNT
|
||||||
|
|
||||||
|
strings.write_string(&builder, "Usage:\n\t")
|
||||||
|
strings.write_string(&builder, program)
|
||||||
|
|
||||||
|
for flag in visible_flags {
|
||||||
|
if keep_it_short && !(flag.is_required || flag.is_positional || flag.name == INTERNAL_VARIADIC_FLAG) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
strings.write_byte(&builder, ' ')
|
||||||
|
|
||||||
|
if flag.name == INTERNAL_VARIADIC_FLAG {
|
||||||
|
strings.write_string(&builder, "...")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !flag.is_required { strings.write_byte(&builder, '[') }
|
||||||
|
if !flag.is_positional { strings.write_string(&builder, flag_prefix) }
|
||||||
|
strings.write_string(&builder, flag.name)
|
||||||
|
if !flag.is_required { strings.write_byte(&builder, ']') }
|
||||||
|
}
|
||||||
|
|
||||||
|
strings.write_byte(&builder, '\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(visible_flags) == 0 {
|
||||||
|
// No visible flags. An unusual situation, but prevent any extra work.
|
||||||
|
fmt.wprint(out, strings.to_string(builder))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
strings.write_string(&builder, "Flags:\n")
|
||||||
|
|
||||||
|
// Divide the positional/required arguments and the non-required arguments.
|
||||||
|
divider_index := -1
|
||||||
|
for flag, i in visible_flags {
|
||||||
|
if !flag.is_positional && !flag.is_required {
|
||||||
|
divider_index = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if divider_index == 0 {
|
||||||
|
divider_index = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
for flag, i in visible_flags {
|
||||||
|
if i == divider_index {
|
||||||
|
SPACING :: 2 // Number of spaces before the '|' from below.
|
||||||
|
strings.write_byte(&builder, '\t')
|
||||||
|
spacing := strings.repeat(" ", SPACING + longest_flag_length, context.temp_allocator)
|
||||||
|
strings.write_string(&builder, spacing)
|
||||||
|
strings.write_string(&builder, "|\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
strings.write_byte(&builder, '\t')
|
||||||
|
|
||||||
|
if flag.name == INTERNAL_VARIADIC_FLAG {
|
||||||
|
strings.write_string(&builder, flag.type_description)
|
||||||
|
} else {
|
||||||
|
strings.write_string(&builder, flag_prefix)
|
||||||
|
strings.write_string(&builder, flag.name)
|
||||||
|
if !flag.is_boolean {
|
||||||
|
strings.write_string(&builder, flag_assignment)
|
||||||
|
}
|
||||||
|
strings.write_string(&builder, flag.type_description)
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.contains_rune(flag.usage, '\n') {
|
||||||
|
// Multi-line usage documentation. Let's make it look nice.
|
||||||
|
usage_builder := strings.builder_make(context.temp_allocator)
|
||||||
|
|
||||||
|
strings.write_byte(&usage_builder, '\n')
|
||||||
|
iter := strings.trim_space(flag.usage)
|
||||||
|
for line in strings.split_lines_iterator(&iter) {
|
||||||
|
strings.write_string(&usage_builder, "\t\t")
|
||||||
|
strings.write_string(&usage_builder, strings.trim_left_space(line))
|
||||||
|
strings.write_byte(&usage_builder, '\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
strings.write_string(&builder, strings.to_string(usage_builder))
|
||||||
|
} else {
|
||||||
|
// Single-line usage documentation.
|
||||||
|
spacing := strings.repeat(" ",
|
||||||
|
(longest_flag_length) - flag.full_length,
|
||||||
|
context.temp_allocator)
|
||||||
|
|
||||||
|
strings.write_string(&builder, spacing)
|
||||||
|
strings.write_string(&builder, " | ")
|
||||||
|
strings.write_string(&builder, flag.usage)
|
||||||
|
strings.write_byte(&builder, '\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.wprint(out, strings.to_string(builder))
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
package flags
|
||||||
|
|
||||||
|
import "core:fmt"
|
||||||
|
@require import "core:os"
|
||||||
|
@require import "core:path/filepath"
|
||||||
|
import "core:strings"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Parse any arguments into an annotated struct or exit if there was an error.
|
||||||
|
|
||||||
|
*Allocates Using Provided Allocator*
|
||||||
|
|
||||||
|
This is a convenience wrapper over `parse` and `print_errors`.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- model: A pointer to an annotated struct.
|
||||||
|
- program_args: A slice of strings, usually `os.args`.
|
||||||
|
- style: The argument parsing style.
|
||||||
|
- allocator: (default: context.allocator)
|
||||||
|
- loc: The caller location for debugging purposes (default: #caller_location)
|
||||||
|
*/
|
||||||
|
@(optimization_mode="favor_size")
|
||||||
|
parse_or_exit :: proc(
|
||||||
|
model: ^$T,
|
||||||
|
program_args: []string,
|
||||||
|
style: Parsing_Style = .Odin,
|
||||||
|
allocator := context.allocator,
|
||||||
|
loc := #caller_location,
|
||||||
|
) {
|
||||||
|
assert(len(program_args) > 0, "Program arguments slice is empty.", loc)
|
||||||
|
|
||||||
|
program := filepath.base(program_args[0])
|
||||||
|
args: []string
|
||||||
|
|
||||||
|
if len(program_args) > 1 {
|
||||||
|
args = program_args[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
error := parse(model, args, style)
|
||||||
|
if error != nil {
|
||||||
|
stderr := os.stream_from_handle(os.stderr)
|
||||||
|
|
||||||
|
if len(args) == 0 {
|
||||||
|
// No arguments entered, and there was an error; show the usage,
|
||||||
|
// specifically on STDERR.
|
||||||
|
write_usage(stderr, T, program, style)
|
||||||
|
fmt.wprintln(stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
print_errors(T, error, program, style)
|
||||||
|
|
||||||
|
_, was_help_request := error.(Help_Request)
|
||||||
|
os.exit(0 if was_help_request else 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
Print out any errors that may have resulted from parsing.
|
||||||
|
|
||||||
|
All error messages print to STDERR, while usage goes to STDOUT, if requested.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- data_type: The typeid of the data structure to describe, if usage is requested.
|
||||||
|
- error: The error returned from `parse`.
|
||||||
|
- style: The argument parsing style, required to show flags in the proper style, when usage is shown.
|
||||||
|
*/
|
||||||
|
@(optimization_mode="favor_size")
|
||||||
|
print_errors :: proc(data_type: typeid, error: Error, program: string, style: Parsing_Style = .Odin) {
|
||||||
|
stderr := os.stream_from_handle(os.stderr)
|
||||||
|
stdout := os.stream_from_handle(os.stdout)
|
||||||
|
|
||||||
|
switch specific_error in error {
|
||||||
|
case Parse_Error:
|
||||||
|
fmt.wprintfln(stderr, "[%T.%v] %s", specific_error, specific_error.reason, specific_error.message)
|
||||||
|
case Open_File_Error:
|
||||||
|
fmt.wprintfln(stderr, "[%T#%i] Unable to open file with perms 0o%o in mode 0x%x: %s",
|
||||||
|
specific_error,
|
||||||
|
specific_error.errno,
|
||||||
|
specific_error.perms,
|
||||||
|
specific_error.mode,
|
||||||
|
specific_error.filename)
|
||||||
|
case Validation_Error:
|
||||||
|
fmt.wprintfln(stderr, "[%T] %s", specific_error, specific_error.message)
|
||||||
|
case Help_Request:
|
||||||
|
write_usage(stdout, data_type, program, style)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
Get the value for a subtag.
|
||||||
|
|
||||||
|
This is useful if you need to parse through the `args` tag for a struct field
|
||||||
|
on a custom type setter or custom flag checker.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
import "core:flags"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
subtag_example :: proc() {
|
||||||
|
args_tag := "precision=3,signed"
|
||||||
|
|
||||||
|
precision, has_precision := flags.get_subtag(args_tag, "precision")
|
||||||
|
signed, is_signed := flags.get_subtag(args_tag, "signed")
|
||||||
|
|
||||||
|
fmt.printfln("precision = %q, %t", precision, has_precision)
|
||||||
|
fmt.printfln("signed = %q, %t", signed, is_signed)
|
||||||
|
}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
precision = "3", true
|
||||||
|
signed = "", true
|
||||||
|
|
||||||
|
*/
|
||||||
|
get_subtag :: proc(tag, id: string) -> (value: string, ok: bool) {
|
||||||
|
// This proc was initially private in `internal_rtti.odin`, but given how
|
||||||
|
// useful it would be to custom type setters and flag checkers, it lives
|
||||||
|
// here now.
|
||||||
|
|
||||||
|
tag := tag
|
||||||
|
|
||||||
|
for subtag in strings.split_iterator(&tag, ",") {
|
||||||
|
if equals := strings.index_byte(subtag, '='); equals != -1 && id == subtag[:equals] {
|
||||||
|
return subtag[1 + equals:], true
|
||||||
|
} else if id == subtag {
|
||||||
|
return "", true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package flags
|
||||||
|
|
||||||
|
/*
|
||||||
|
Check a flag after parsing, during the validation stage.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- model: A raw pointer to the data structure provided to `parse`.
|
||||||
|
- name: The name of the flag being checked.
|
||||||
|
- value: An `any` type that contains the value to be checked.
|
||||||
|
- args_tag: The `args` tag from within the struct.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- error: An error message, or an empty string if no error occurred.
|
||||||
|
*/
|
||||||
|
Custom_Flag_Checker :: #type proc(
|
||||||
|
model: rawptr,
|
||||||
|
name: string,
|
||||||
|
value: any,
|
||||||
|
args_tag: string,
|
||||||
|
) -> (
|
||||||
|
error: string,
|
||||||
|
)
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
global_custom_flag_checker: Custom_Flag_Checker
|
||||||
|
|
||||||
|
/*
|
||||||
|
Set the global custom flag checker.
|
||||||
|
|
||||||
|
Note that only one can be active at a time.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- checker: The flag checker. Pass `nil` to disable any previously set checker.
|
||||||
|
*/
|
||||||
|
register_flag_checker :: proc(checker: Custom_Flag_Checker) {
|
||||||
|
global_custom_flag_checker = checker
|
||||||
|
}
|
||||||
+38
-16
@@ -373,6 +373,25 @@ caprintfln :: proc(format: string, args: ..any) -> cstring {
|
|||||||
// *Allocates Using Context's Temporary Allocator*
|
// *Allocates Using Context's Temporary Allocator*
|
||||||
//
|
//
|
||||||
// Inputs:
|
// Inputs:
|
||||||
|
// - args: A variadic list of arguments to be formatted.
|
||||||
|
// - sep: An optional separator string (default is a single space).
|
||||||
|
//
|
||||||
|
// Returns: A formatted C string.
|
||||||
|
//
|
||||||
|
@(require_results)
|
||||||
|
ctprint :: proc(args: ..any, sep := " ") -> cstring {
|
||||||
|
str: strings.Builder
|
||||||
|
strings.builder_init(&str, context.temp_allocator)
|
||||||
|
sbprint(&str, ..args, sep=sep)
|
||||||
|
strings.write_byte(&str, 0)
|
||||||
|
s := strings.to_string(str)
|
||||||
|
return cstring(raw_data(s))
|
||||||
|
}
|
||||||
|
// Creates a formatted C string
|
||||||
|
//
|
||||||
|
// *Allocates Using Context's Temporary Allocator*
|
||||||
|
//
|
||||||
|
// Inputs:
|
||||||
// - format: A format string with placeholders for the provided arguments
|
// - format: A format string with placeholders for the provided arguments
|
||||||
// - args: A variadic list of arguments to be formatted
|
// - args: A variadic list of arguments to be formatted
|
||||||
// - newline: Whether the string should end with a newline. (See `ctprintfln`.)
|
// - newline: Whether the string should end with a newline. (See `ctprintfln`.)
|
||||||
@@ -932,10 +951,10 @@ fmt_bad_verb :: proc(fi: ^Info, verb: rune) {
|
|||||||
io.write_string(fi.writer, "%!", &fi.n)
|
io.write_string(fi.writer, "%!", &fi.n)
|
||||||
io.write_rune(fi.writer, verb, &fi.n)
|
io.write_rune(fi.writer, verb, &fi.n)
|
||||||
io.write_byte(fi.writer, '(', &fi.n)
|
io.write_byte(fi.writer, '(', &fi.n)
|
||||||
if fi.arg.id != nil {
|
if arg := fi.arg; arg != nil {
|
||||||
reflect.write_typeid(fi.writer, fi.arg.id, &fi.n)
|
reflect.write_typeid(fi.writer, arg.id, &fi.n)
|
||||||
io.write_byte(fi.writer, '=', &fi.n)
|
io.write_byte(fi.writer, '=', &fi.n)
|
||||||
fmt_value(fi, fi.arg, 'v')
|
fmt_value(fi, arg, 'v')
|
||||||
} else {
|
} else {
|
||||||
io.write_string(fi.writer, "<nil>", &fi.n)
|
io.write_string(fi.writer, "<nil>", &fi.n)
|
||||||
}
|
}
|
||||||
@@ -1053,8 +1072,8 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d
|
|||||||
}
|
}
|
||||||
|
|
||||||
flags: strconv.Int_Flags
|
flags: strconv.Int_Flags
|
||||||
if fi.hash && !fi.zero && start == 0 { flags |= {.Prefix} }
|
if fi.hash && !fi.zero && start == 0 { flags += {.Prefix} }
|
||||||
if fi.plus { flags |= {.Plus} }
|
if fi.plus { flags += {.Plus} }
|
||||||
s := strconv.append_bits(buf[start:], u, base, is_signed, bit_size, digits, flags)
|
s := strconv.append_bits(buf[start:], u, base, is_signed, bit_size, digits, flags)
|
||||||
prev_zero := fi.zero
|
prev_zero := fi.zero
|
||||||
defer fi.zero = prev_zero
|
defer fi.zero = prev_zero
|
||||||
@@ -1138,8 +1157,8 @@ _fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: i
|
|||||||
}
|
}
|
||||||
|
|
||||||
flags: strconv.Int_Flags
|
flags: strconv.Int_Flags
|
||||||
if fi.hash && !fi.zero && start == 0 { flags |= {.Prefix} }
|
if fi.hash && !fi.zero && start == 0 { flags += {.Prefix} }
|
||||||
if fi.plus { flags |= {.Plus} }
|
if fi.plus { flags += {.Plus} }
|
||||||
s := strconv.append_bits_128(buf[start:], u, base, is_signed, bit_size, digits, flags)
|
s := strconv.append_bits_128(buf[start:], u, base, is_signed, bit_size, digits, flags)
|
||||||
|
|
||||||
if fi.hash && fi.zero && fi.indent == 0 {
|
if fi.hash && fi.zero && fi.indent == 0 {
|
||||||
@@ -1441,13 +1460,10 @@ fmt_string :: proc(fi: ^Info, s: string, verb: rune) {
|
|||||||
if !fi.minus {
|
if !fi.minus {
|
||||||
io.write_string(fi.writer, s, &fi.n)
|
io.write_string(fi.writer, s, &fi.n)
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
io.write_string(fi.writer, s, &fi.n)
|
io.write_string(fi.writer, s, &fi.n)
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
io.write_string(fi.writer, s, &fi.n)
|
io.write_string(fi.writer, s, &fi.n)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1973,11 +1989,13 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St
|
|||||||
// fi.hash = false;
|
// fi.hash = false;
|
||||||
fi.indent += 1
|
fi.indent += 1
|
||||||
|
|
||||||
if !is_soa && hash {
|
is_empty := len(info.names) == 0
|
||||||
|
|
||||||
|
if !is_soa && hash && !is_empty {
|
||||||
io.write_byte(fi.writer, '\n', &fi.n)
|
io.write_byte(fi.writer, '\n', &fi.n)
|
||||||
}
|
}
|
||||||
defer {
|
defer {
|
||||||
if hash {
|
if !is_soa && hash && !is_empty {
|
||||||
for _ in 0..<indent { io.write_byte(fi.writer, '\t', &fi.n) }
|
for _ in 0..<indent { io.write_byte(fi.writer, '\t', &fi.n) }
|
||||||
}
|
}
|
||||||
io.write_byte(fi.writer, ']' if is_soa && the_verb == 'v' else '}', &fi.n)
|
io.write_byte(fi.writer, ']' if is_soa && the_verb == 'v' else '}', &fi.n)
|
||||||
@@ -2025,9 +2043,9 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St
|
|||||||
}
|
}
|
||||||
io.write_string(fi.writer, base_type_name, &fi.n)
|
io.write_string(fi.writer, base_type_name, &fi.n)
|
||||||
io.write_byte(fi.writer, '{', &fi.n)
|
io.write_byte(fi.writer, '{', &fi.n)
|
||||||
if hash { io.write_byte(fi.writer, '\n', &fi.n) }
|
if hash && !is_empty { io.write_byte(fi.writer, '\n', &fi.n) }
|
||||||
defer {
|
defer {
|
||||||
if hash {
|
if hash && !is_empty {
|
||||||
fi.indent -= 1
|
fi.indent -= 1
|
||||||
fmt_write_indent(fi)
|
fmt_write_indent(fi)
|
||||||
fi.indent += 1
|
fi.indent += 1
|
||||||
@@ -2075,6 +2093,10 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St
|
|||||||
if hash { io.write_string(fi.writer, ",\n", &fi.n) }
|
if hash { io.write_string(fi.writer, ",\n", &fi.n) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if hash && n > 0 {
|
||||||
|
for _ in 0..<indent { io.write_byte(fi.writer, '\t', &fi.n) }
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
field_count := -1
|
field_count := -1
|
||||||
for name, i in info.names {
|
for name, i in info.names {
|
||||||
|
|||||||
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
package hash
|
package hash
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
crc64_ecma_182 :: proc "contextless" (data: []byte, seed := u64(0)) -> (result: u64) #no_bounds_check {
|
crc64_ecma_182 :: proc "contextless" (data: []byte, seed := u64(0)) -> (result: u64) #no_bounds_check {
|
||||||
result = seed
|
result = seed
|
||||||
#no_bounds_check for b in data {
|
#no_bounds_check for b in data {
|
||||||
@@ -14,7 +14,7 @@ crc64_ecma_182 :: proc "contextless" (data: []byte, seed := u64(0)) -> (result:
|
|||||||
bit-reversed, with one's complement pre and post processing.
|
bit-reversed, with one's complement pre and post processing.
|
||||||
Based on Mark Adler's v1.4 implementation in C under the ZLIB license.
|
Based on Mark Adler's v1.4 implementation in C under the ZLIB license.
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
crc64_xz :: proc "contextless" (data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
|
crc64_xz :: proc "contextless" (data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
|
||||||
data := data
|
data := data
|
||||||
result := ~u64le(seed)
|
result := ~u64le(seed)
|
||||||
@@ -52,7 +52,7 @@ crc64_xz :: proc "contextless" (data: []byte, seed := u64(0)) -> u64 #no_bounds_
|
|||||||
/*
|
/*
|
||||||
Generator polynomial: x^64 + x^4 + x^3 + x + 1
|
Generator polynomial: x^64 + x^4 + x^3 + x + 1
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
crc64_iso_3306 :: proc "contextless" (data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
|
crc64_iso_3306 :: proc "contextless" (data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
|
||||||
|
|
||||||
result := seed
|
result := seed
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package hash
|
|||||||
|
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
crc32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
|
crc32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
|
||||||
crc := ~seed
|
crc := ~seed
|
||||||
buffer := raw_data(data)
|
buffer := raw_data(data)
|
||||||
|
|||||||
+11
-11
@@ -3,7 +3,7 @@ package hash
|
|||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
adler32 :: proc "contextless" (data: []byte, seed := u32(1)) -> u32 #no_bounds_check {
|
adler32 :: proc "contextless" (data: []byte, seed := u32(1)) -> u32 #no_bounds_check {
|
||||||
|
|
||||||
ADLER_CONST :: 65521
|
ADLER_CONST :: 65521
|
||||||
@@ -46,7 +46,7 @@ adler32 :: proc "contextless" (data: []byte, seed := u32(1)) -> u32 #no_bounds_c
|
|||||||
return (u32(b) << 16) | u32(a)
|
return (u32(b) << 16) | u32(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
djb2 :: proc "contextless" (data: []byte, seed := u32(5381)) -> u32 {
|
djb2 :: proc "contextless" (data: []byte, seed := u32(5381)) -> u32 {
|
||||||
hash: u32 = seed
|
hash: u32 = seed
|
||||||
for b in data {
|
for b in data {
|
||||||
@@ -73,7 +73,7 @@ djbx33a :: proc "contextless" (data: []byte, seed := u32(5381)) -> (result: [16]
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If you have a choice, prefer fnv32a
|
// If you have a choice, prefer fnv32a
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
fnv32_no_a :: proc "contextless" (data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
fnv32_no_a :: proc "contextless" (data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||||
h: u32 = seed
|
h: u32 = seed
|
||||||
for b in data {
|
for b in data {
|
||||||
@@ -86,7 +86,7 @@ fnv32 :: fnv32_no_a // NOTE(bill): Not a fan of these aliases but seems necessar
|
|||||||
fnv64 :: fnv64_no_a // NOTE(bill): Not a fan of these aliases but seems necessary
|
fnv64 :: fnv64_no_a // NOTE(bill): Not a fan of these aliases but seems necessary
|
||||||
|
|
||||||
// If you have a choice, prefer fnv64a
|
// If you have a choice, prefer fnv64a
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
fnv64_no_a :: proc "contextless" (data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
fnv64_no_a :: proc "contextless" (data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
||||||
h: u64 = seed
|
h: u64 = seed
|
||||||
for b in data {
|
for b in data {
|
||||||
@@ -94,7 +94,7 @@ fnv64_no_a :: proc "contextless" (data: []byte, seed := u64(0xcbf29ce484222325))
|
|||||||
}
|
}
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
fnv32a :: proc "contextless" (data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
fnv32a :: proc "contextless" (data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||||
h: u32 = seed
|
h: u32 = seed
|
||||||
for b in data {
|
for b in data {
|
||||||
@@ -103,7 +103,7 @@ fnv32a :: proc "contextless" (data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
|||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
fnv64a :: proc "contextless" (data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
fnv64a :: proc "contextless" (data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
||||||
h: u64 = seed
|
h: u64 = seed
|
||||||
for b in data {
|
for b in data {
|
||||||
@@ -112,7 +112,7 @@ fnv64a :: proc "contextless" (data: []byte, seed := u64(0xcbf29ce484222325)) ->
|
|||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
jenkins :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
|
jenkins :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
|
||||||
hash: u32 = seed
|
hash: u32 = seed
|
||||||
for b in data {
|
for b in data {
|
||||||
@@ -126,7 +126,7 @@ jenkins :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
|
|||||||
return hash
|
return hash
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
murmur32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
|
murmur32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
|
||||||
c1_32: u32 : 0xcc9e2d51
|
c1_32: u32 : 0xcc9e2d51
|
||||||
c2_32: u32 : 0x1b873593
|
c2_32: u32 : 0x1b873593
|
||||||
@@ -177,7 +177,7 @@ murmur32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L96
|
// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L96
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
murmur64a :: proc "contextless" (data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
murmur64a :: proc "contextless" (data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
||||||
m :: 0xc6a4a7935bd1e995
|
m :: 0xc6a4a7935bd1e995
|
||||||
r :: 47
|
r :: 47
|
||||||
@@ -218,7 +218,7 @@ murmur64a :: proc "contextless" (data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L140
|
// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L140
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
murmur64b :: proc "contextless" (data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
murmur64b :: proc "contextless" (data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
||||||
m :: 0x5bd1e995
|
m :: 0x5bd1e995
|
||||||
r :: 24
|
r :: 24
|
||||||
@@ -286,7 +286,7 @@ murmur64b :: proc "contextless" (data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
|||||||
return u64(h1)<<32 | u64(h2)
|
return u64(h1)<<32 | u64(h2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
sdbm :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
|
sdbm :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
|
||||||
hash: u32 = seed
|
hash: u32 = seed
|
||||||
for b in data {
|
for b in data {
|
||||||
|
|||||||
@@ -67,17 +67,17 @@ when !XXH_DISABLE_PREFETCH {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH_rotl32 :: #force_inline proc(x, r: u32) -> (res: u32) {
|
XXH_rotl32 :: #force_inline proc(x, r: u32) -> (res: u32) {
|
||||||
return ((x << r) | (x >> (32 - r)))
|
return ((x << r) | (x >> (32 - r)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH_rotl64 :: #force_inline proc(x, r: u64) -> (res: u64) {
|
XXH_rotl64 :: #force_inline proc(x, r: u64) -> (res: u64) {
|
||||||
return ((x << r) | (x >> (64 - r)))
|
return ((x << r) | (x >> (64 - r)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH32_read32 :: #force_inline proc(buf: []u8, alignment := Alignment.Unaligned) -> (res: u32) {
|
XXH32_read32 :: #force_inline proc(buf: []u8, alignment := Alignment.Unaligned) -> (res: u32) {
|
||||||
if XXH_FORCE_MEMORY_ACCESS == 2 || alignment == .Aligned {
|
if XXH_FORCE_MEMORY_ACCESS == 2 || alignment == .Aligned {
|
||||||
#no_bounds_check b := (^u32le)(&buf[0])^
|
#no_bounds_check b := (^u32le)(&buf[0])^
|
||||||
@@ -89,7 +89,7 @@ XXH32_read32 :: #force_inline proc(buf: []u8, alignment := Alignment.Unaligned)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH64_read64 :: #force_inline proc(buf: []u8, alignment := Alignment.Unaligned) -> (res: u64) {
|
XXH64_read64 :: #force_inline proc(buf: []u8, alignment := Alignment.Unaligned) -> (res: u64) {
|
||||||
if XXH_FORCE_MEMORY_ACCESS == 2 || alignment == .Aligned {
|
if XXH_FORCE_MEMORY_ACCESS == 2 || alignment == .Aligned {
|
||||||
#no_bounds_check b := (^u64le)(&buf[0])^
|
#no_bounds_check b := (^u64le)(&buf[0])^
|
||||||
|
|||||||
@@ -111,13 +111,13 @@ XXH128_canonical :: struct {
|
|||||||
@param lhs, rhs The 64-bit integers to multiply
|
@param lhs, rhs The 64-bit integers to multiply
|
||||||
@return The low 64 bits of the product XOR'd by the high 64 bits.
|
@return The low 64 bits of the product XOR'd by the high 64 bits.
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH_mul_64_to_128_fold_64 :: #force_inline proc(lhs, rhs: xxh_u64) -> (res: xxh_u64) {
|
XXH_mul_64_to_128_fold_64 :: #force_inline proc(lhs, rhs: xxh_u64) -> (res: xxh_u64) {
|
||||||
t := u128(lhs) * u128(rhs)
|
t := u128(lhs) * u128(rhs)
|
||||||
return u64(t & 0xFFFFFFFFFFFFFFFF) ~ u64(t >> 64)
|
return u64(t & 0xFFFFFFFFFFFFFFFF) ~ u64(t >> 64)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH_xorshift_64 :: #force_inline proc(v: xxh_u64, #any_int shift: uint) -> (res: xxh_u64) {
|
XXH_xorshift_64 :: #force_inline proc(v: xxh_u64, #any_int shift: uint) -> (res: xxh_u64) {
|
||||||
return v ~ (v >> shift)
|
return v ~ (v >> shift)
|
||||||
}
|
}
|
||||||
@@ -125,7 +125,7 @@ XXH_xorshift_64 :: #force_inline proc(v: xxh_u64, #any_int shift: uint) -> (res:
|
|||||||
/*
|
/*
|
||||||
This is a fast avalanche stage, suitable when input bits are already partially mixed
|
This is a fast avalanche stage, suitable when input bits are already partially mixed
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_avalanche :: #force_inline proc(h64: xxh_u64) -> (res: xxh_u64) {
|
XXH3_avalanche :: #force_inline proc(h64: xxh_u64) -> (res: xxh_u64) {
|
||||||
res = XXH_xorshift_64(h64, 37)
|
res = XXH_xorshift_64(h64, 37)
|
||||||
res *= 0x165667919E3779F9
|
res *= 0x165667919E3779F9
|
||||||
@@ -137,7 +137,7 @@ XXH3_avalanche :: #force_inline proc(h64: xxh_u64) -> (res: xxh_u64) {
|
|||||||
This is a stronger avalanche, inspired by Pelle Evensen's rrmxmx
|
This is a stronger avalanche, inspired by Pelle Evensen's rrmxmx
|
||||||
preferable when input has not been previously mixed
|
preferable when input has not been previously mixed
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_rrmxmx :: #force_inline proc(h64, length: xxh_u64) -> (res: xxh_u64) {
|
XXH3_rrmxmx :: #force_inline proc(h64, length: xxh_u64) -> (res: xxh_u64) {
|
||||||
/* this mix is inspired by Pelle Evensen's rrmxmx */
|
/* this mix is inspired by Pelle Evensen's rrmxmx */
|
||||||
res = h64
|
res = h64
|
||||||
@@ -166,7 +166,7 @@ XXH3_rrmxmx :: #force_inline proc(h64, length: xxh_u64) -> (res: xxh_u64) {
|
|||||||
fast for a _128-bit_ hash on 32-bit (it usually clears XXH64).
|
fast for a _128-bit_ hash on 32-bit (it usually clears XXH64).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_len_1to3_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
|
XXH3_len_1to3_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
|
||||||
/* A doubled version of 1to3_64b with different constants. */
|
/* A doubled version of 1to3_64b with different constants. */
|
||||||
length := len(input)
|
length := len(input)
|
||||||
@@ -190,7 +190,7 @@ XXH3_len_1to3_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u6
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_len_4to8_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
|
XXH3_len_4to8_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
|
||||||
length := len(input)
|
length := len(input)
|
||||||
seed := seed
|
seed := seed
|
||||||
@@ -219,7 +219,7 @@ XXH3_len_4to8_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u6
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_len_9to16_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
|
XXH3_len_9to16_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
|
||||||
length := len(input)
|
length := len(input)
|
||||||
|
|
||||||
@@ -261,7 +261,7 @@ XXH3_len_9to16_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u
|
|||||||
/*
|
/*
|
||||||
Assumption: `secret` size is >= XXH3_SECRET_SIZE_MIN
|
Assumption: `secret` size is >= XXH3_SECRET_SIZE_MIN
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_len_0to16_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
|
XXH3_len_0to16_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
|
||||||
length := len(input)
|
length := len(input)
|
||||||
|
|
||||||
@@ -279,7 +279,7 @@ XXH3_len_0to16_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u
|
|||||||
/*
|
/*
|
||||||
A bit slower than XXH3_mix16B, but handles multiply by zero better.
|
A bit slower than XXH3_mix16B, but handles multiply by zero better.
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH128_mix32B :: #force_inline proc(acc: xxh_u128, input_1: []u8, input_2: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
|
XXH128_mix32B :: #force_inline proc(acc: xxh_u128, input_1: []u8, input_2: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
|
||||||
acc128 := XXH128_hash_t{
|
acc128 := XXH128_hash_t{
|
||||||
h = acc,
|
h = acc,
|
||||||
@@ -293,7 +293,7 @@ XXH128_mix32B :: #force_inline proc(acc: xxh_u128, input_1: []u8, input_2: []u8,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_len_17to128_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
|
XXH3_len_17to128_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
|
||||||
length := len(input)
|
length := len(input)
|
||||||
|
|
||||||
@@ -323,7 +323,7 @@ XXH3_len_17to128_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh
|
|||||||
unreachable()
|
unreachable()
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_len_129to240_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
|
XXH3_len_129to240_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
|
||||||
length := len(input)
|
length := len(input)
|
||||||
|
|
||||||
@@ -379,7 +379,7 @@ XXH3_INIT_ACC :: [XXH_ACC_NB]xxh_u64{
|
|||||||
|
|
||||||
XXH_SECRET_MERGEACCS_START :: 11
|
XXH_SECRET_MERGEACCS_START :: 11
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_hashLong_128b_internal :: #force_inline proc(
|
XXH3_hashLong_128b_internal :: #force_inline proc(
|
||||||
input: []u8,
|
input: []u8,
|
||||||
secret: []u8,
|
secret: []u8,
|
||||||
@@ -407,7 +407,7 @@ XXH3_hashLong_128b_internal :: #force_inline proc(
|
|||||||
/*
|
/*
|
||||||
* It's important for performance that XXH3_hashLong is not inlined.
|
* It's important for performance that XXH3_hashLong is not inlined.
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_hashLong_128b_default :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: XXH3_128_hash) {
|
XXH3_hashLong_128b_default :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: XXH3_128_hash) {
|
||||||
return XXH3_hashLong_128b_internal(input, XXH3_kSecret[:], XXH3_accumulate_512, XXH3_scramble_accumulator)
|
return XXH3_hashLong_128b_internal(input, XXH3_kSecret[:], XXH3_accumulate_512, XXH3_scramble_accumulator)
|
||||||
}
|
}
|
||||||
@@ -415,12 +415,12 @@ XXH3_hashLong_128b_default :: #force_no_inline proc(input: []u8, seed: xxh_u64,
|
|||||||
/*
|
/*
|
||||||
* It's important for performance that XXH3_hashLong is not inlined.
|
* It's important for performance that XXH3_hashLong is not inlined.
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_hashLong_128b_withSecret :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: XXH3_128_hash) {
|
XXH3_hashLong_128b_withSecret :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: XXH3_128_hash) {
|
||||||
return XXH3_hashLong_128b_internal(input, secret, XXH3_accumulate_512, XXH3_scramble_accumulator)
|
return XXH3_hashLong_128b_internal(input, secret, XXH3_accumulate_512, XXH3_scramble_accumulator)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_hashLong_128b_withSeed_internal :: #force_inline proc(
|
XXH3_hashLong_128b_withSeed_internal :: #force_inline proc(
|
||||||
input: []u8, seed: xxh_u64, secret: []u8,
|
input: []u8, seed: xxh_u64, secret: []u8,
|
||||||
f_acc512: XXH3_accumulate_512_f,
|
f_acc512: XXH3_accumulate_512_f,
|
||||||
@@ -441,14 +441,14 @@ XXH3_hashLong_128b_withSeed_internal :: #force_inline proc(
|
|||||||
/*
|
/*
|
||||||
* It's important for performance that XXH3_hashLong is not inlined.
|
* It's important for performance that XXH3_hashLong is not inlined.
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_hashLong_128b_withSeed :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: XXH3_128_hash) {
|
XXH3_hashLong_128b_withSeed :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: XXH3_128_hash) {
|
||||||
return XXH3_hashLong_128b_withSeed_internal(input, seed, secret, XXH3_accumulate_512, XXH3_scramble_accumulator , XXH3_init_custom_secret)
|
return XXH3_hashLong_128b_withSeed_internal(input, seed, secret, XXH3_accumulate_512, XXH3_scramble_accumulator , XXH3_init_custom_secret)
|
||||||
}
|
}
|
||||||
|
|
||||||
XXH3_hashLong128_f :: #type proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: XXH3_128_hash)
|
XXH3_hashLong128_f :: #type proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: XXH3_128_hash)
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_128bits_internal :: #force_inline proc(
|
XXH3_128bits_internal :: #force_inline proc(
|
||||||
input: []u8, seed: xxh_u64, secret: []u8, f_hl128: XXH3_hashLong128_f) -> (res: XXH3_128_hash) {
|
input: []u8, seed: xxh_u64, secret: []u8, f_hl128: XXH3_hashLong128_f) -> (res: XXH3_128_hash) {
|
||||||
|
|
||||||
@@ -474,17 +474,17 @@ XXH3_128bits_internal :: #force_inline proc(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* === Public XXH128 API === */
|
/* === Public XXH128 API === */
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_128_default :: proc(input: []u8) -> (hash: XXH3_128_hash) {
|
XXH3_128_default :: proc(input: []u8) -> (hash: XXH3_128_hash) {
|
||||||
return XXH3_128bits_internal(input, 0, XXH3_kSecret[:], XXH3_hashLong_128b_withSeed)
|
return XXH3_128bits_internal(input, 0, XXH3_kSecret[:], XXH3_hashLong_128b_withSeed)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_128_with_seed :: proc(input: []u8, seed: xxh_u64) -> (hash: XXH3_128_hash) {
|
XXH3_128_with_seed :: proc(input: []u8, seed: xxh_u64) -> (hash: XXH3_128_hash) {
|
||||||
return XXH3_128bits_internal(input, seed, XXH3_kSecret[:], XXH3_hashLong_128b_withSeed)
|
return XXH3_128bits_internal(input, seed, XXH3_kSecret[:], XXH3_hashLong_128b_withSeed)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_128_with_secret :: proc(input: []u8, secret: []u8) -> (hash: XXH3_128_hash) {
|
XXH3_128_with_secret :: proc(input: []u8, secret: []u8) -> (hash: XXH3_128_hash) {
|
||||||
return XXH3_128bits_internal(input, 0, secret, XXH3_hashLong_128b_withSecret)
|
return XXH3_128bits_internal(input, 0, secret, XXH3_hashLong_128b_withSecret)
|
||||||
}
|
}
|
||||||
@@ -519,7 +519,7 @@ XXH3_128 :: proc { XXH3_128_default, XXH3_128_with_seed, XXH3_128_with_secret }
|
|||||||
The XOR mixing hides individual parts of the secret and increases entropy.
|
The XOR mixing hides individual parts of the secret and increases entropy.
|
||||||
This adds an extra layer of strength for custom secrets.
|
This adds an extra layer of strength for custom secrets.
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_len_1to3_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
|
XXH3_len_1to3_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
|
||||||
length := u32(len(input))
|
length := u32(len(input))
|
||||||
assert(input != nil)
|
assert(input != nil)
|
||||||
@@ -542,7 +542,7 @@ XXH3_len_1to3_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_len_4to8_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
|
XXH3_len_4to8_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
|
||||||
length := u32(len(input))
|
length := u32(len(input))
|
||||||
assert(input != nil)
|
assert(input != nil)
|
||||||
@@ -562,7 +562,7 @@ XXH3_len_4to8_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_len_9to16_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
|
XXH3_len_9to16_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
|
||||||
length := u64(len(input))
|
length := u64(len(input))
|
||||||
assert(input != nil)
|
assert(input != nil)
|
||||||
@@ -579,7 +579,7 @@ XXH3_len_9to16_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u6
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_len_0to16_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
|
XXH3_len_0to16_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
|
||||||
length := u64(len(input))
|
length := u64(len(input))
|
||||||
assert(input != nil)
|
assert(input != nil)
|
||||||
@@ -621,7 +621,7 @@ XXH3_len_0to16_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u6
|
|||||||
by this, although it is always a good idea to use a proper seed if you care
|
by this, although it is always a good idea to use a proper seed if you care
|
||||||
about strength.
|
about strength.
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_mix16B :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
|
XXH3_mix16B :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
|
||||||
input_lo := XXH64_read64(input[0:])
|
input_lo := XXH64_read64(input[0:])
|
||||||
input_hi := XXH64_read64(input[8:])
|
input_hi := XXH64_read64(input[8:])
|
||||||
@@ -632,7 +632,7 @@ XXH3_mix16B :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* For mid range keys, XXH3 uses a Mum-hash variant. */
|
/* For mid range keys, XXH3 uses a Mum-hash variant. */
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_len_17to128_64b :: proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
|
XXH3_len_17to128_64b :: proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
|
||||||
assert(len(secret) >= XXH3_SECRET_SIZE_MIN)
|
assert(len(secret) >= XXH3_SECRET_SIZE_MIN)
|
||||||
length := len(input)
|
length := len(input)
|
||||||
@@ -665,7 +665,7 @@ XXH3_MIDSIZE_MAX :: 240
|
|||||||
XXH3_MIDSIZE_STARTOFFSET :: 3
|
XXH3_MIDSIZE_STARTOFFSET :: 3
|
||||||
XXH3_MIDSIZE_LASTOFFSET :: 17
|
XXH3_MIDSIZE_LASTOFFSET :: 17
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_len_129to240_64b :: proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
|
XXH3_len_129to240_64b :: proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
|
||||||
assert(len(secret) >= XXH3_SECRET_SIZE_MIN)
|
assert(len(secret) >= XXH3_SECRET_SIZE_MIN)
|
||||||
length := len(input)
|
length := len(input)
|
||||||
@@ -699,7 +699,7 @@ XXH_SECRET_CONSUME_RATE :: 8 /* nb of secret bytes consumed at each accumulatio
|
|||||||
XXH_ACC_NB :: (XXH_STRIPE_LEN / size_of(xxh_u64))
|
XXH_ACC_NB :: (XXH_STRIPE_LEN / size_of(xxh_u64))
|
||||||
XXH_SECRET_LASTACC_START :: 7 /* not aligned on 8, last secret is different from acc & scrambler */
|
XXH_SECRET_LASTACC_START :: 7 /* not aligned on 8, last secret is different from acc & scrambler */
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH_writeLE64 :: #force_inline proc(dst: []u8, v64: u64le) {
|
XXH_writeLE64 :: #force_inline proc(dst: []u8, v64: u64le) {
|
||||||
v := v64
|
v := v64
|
||||||
mem_copy(raw_data(dst), &v, size_of(v64))
|
mem_copy(raw_data(dst), &v, size_of(v64))
|
||||||
@@ -737,7 +737,7 @@ XXH3_scramble_accumulator : XXH3_scramble_accumulator_f = XXH3_scramble_accumula
|
|||||||
XXH3_init_custom_secret : XXH3_init_custom_secret_f = XXH3_init_custom_secret_scalar
|
XXH3_init_custom_secret : XXH3_init_custom_secret_f = XXH3_init_custom_secret_scalar
|
||||||
|
|
||||||
/* scalar variants - universal */
|
/* scalar variants - universal */
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_accumulate_512_scalar :: #force_inline proc(acc: []xxh_u64, input: []u8, secret: []u8) {
|
XXH3_accumulate_512_scalar :: #force_inline proc(acc: []xxh_u64, input: []u8, secret: []u8) {
|
||||||
xacc := acc /* presumed aligned */
|
xacc := acc /* presumed aligned */
|
||||||
xinput := input /* no alignment restriction */
|
xinput := input /* no alignment restriction */
|
||||||
@@ -754,7 +754,7 @@ XXH3_accumulate_512_scalar :: #force_inline proc(acc: []xxh_u64, input: []u8, se
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_scramble_accumulator_scalar :: #force_inline proc(acc: []xxh_u64, secret: []u8) {
|
XXH3_scramble_accumulator_scalar :: #force_inline proc(acc: []xxh_u64, secret: []u8) {
|
||||||
xacc := acc /* presumed aligned */
|
xacc := acc /* presumed aligned */
|
||||||
xsecret := secret /* no alignment restriction */
|
xsecret := secret /* no alignment restriction */
|
||||||
@@ -771,7 +771,7 @@ XXH3_scramble_accumulator_scalar :: #force_inline proc(acc: []xxh_u64, secret: [
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_init_custom_secret_scalar :: #force_inline proc(custom_secret: []u8, seed64: xxh_u64) {
|
XXH3_init_custom_secret_scalar :: #force_inline proc(custom_secret: []u8, seed64: xxh_u64) {
|
||||||
#assert((XXH_SECRET_DEFAULT_SIZE & 15) == 0)
|
#assert((XXH_SECRET_DEFAULT_SIZE & 15) == 0)
|
||||||
|
|
||||||
@@ -791,7 +791,7 @@ XXH_PREFETCH_DIST :: 320
|
|||||||
* Loops over XXH3_accumulate_512().
|
* Loops over XXH3_accumulate_512().
|
||||||
* Assumption: nbStripes will not overflow the secret size
|
* Assumption: nbStripes will not overflow the secret size
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_accumulate :: #force_inline proc(
|
XXH3_accumulate :: #force_inline proc(
|
||||||
acc: []xxh_u64, input: []u8, secret: []u8, nbStripes: uint, f_acc512: XXH3_accumulate_512_f) {
|
acc: []xxh_u64, input: []u8, secret: []u8, nbStripes: uint, f_acc512: XXH3_accumulate_512_f) {
|
||||||
|
|
||||||
@@ -804,7 +804,7 @@ XXH3_accumulate :: #force_inline proc(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_hashLong_internal_loop :: #force_inline proc(acc: []xxh_u64, input: []u8, secret: []u8,
|
XXH3_hashLong_internal_loop :: #force_inline proc(acc: []xxh_u64, input: []u8, secret: []u8,
|
||||||
f_acc512: XXH3_accumulate_512_f, f_scramble: XXH3_scramble_accumulator_f) {
|
f_acc512: XXH3_accumulate_512_f, f_scramble: XXH3_scramble_accumulator_f) {
|
||||||
|
|
||||||
@@ -833,14 +833,14 @@ XXH3_hashLong_internal_loop :: #force_inline proc(acc: []xxh_u64, input: []u8, s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_mix2Accs :: #force_inline proc(acc: []xxh_u64, secret: []u8) -> (res: xxh_u64) {
|
XXH3_mix2Accs :: #force_inline proc(acc: []xxh_u64, secret: []u8) -> (res: xxh_u64) {
|
||||||
return XXH_mul_64_to_128_fold_64(
|
return XXH_mul_64_to_128_fold_64(
|
||||||
acc[0] ~ XXH64_read64(secret),
|
acc[0] ~ XXH64_read64(secret),
|
||||||
acc[1] ~ XXH64_read64(secret[8:]))
|
acc[1] ~ XXH64_read64(secret[8:]))
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_mergeAccs :: #force_inline proc(acc: []xxh_u64, secret: []u8, start: xxh_u64) -> (res: xxh_u64) {
|
XXH3_mergeAccs :: #force_inline proc(acc: []xxh_u64, secret: []u8, start: xxh_u64) -> (res: xxh_u64) {
|
||||||
result64 := start
|
result64 := start
|
||||||
#no_bounds_check for i := 0; i < 4; i += 1 {
|
#no_bounds_check for i := 0; i < 4; i += 1 {
|
||||||
@@ -849,7 +849,7 @@ XXH3_mergeAccs :: #force_inline proc(acc: []xxh_u64, secret: []u8, start: xxh_u6
|
|||||||
return XXH3_avalanche(result64)
|
return XXH3_avalanche(result64)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_hashLong_64b_internal :: #force_inline proc(input: []u8, secret: []u8,
|
XXH3_hashLong_64b_internal :: #force_inline proc(input: []u8, secret: []u8,
|
||||||
f_acc512: XXH3_accumulate_512_f, f_scramble: XXH3_scramble_accumulator_f) -> (hash: xxh_u64) {
|
f_acc512: XXH3_accumulate_512_f, f_scramble: XXH3_scramble_accumulator_f) -> (hash: xxh_u64) {
|
||||||
|
|
||||||
@@ -868,7 +868,7 @@ XXH3_hashLong_64b_internal :: #force_inline proc(input: []u8, secret: []u8,
|
|||||||
/*
|
/*
|
||||||
It's important for performance that XXH3_hashLong is not inlined.
|
It's important for performance that XXH3_hashLong is not inlined.
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_hashLong_64b_withSecret :: #force_no_inline proc(input: []u8, seed64: xxh_u64, secret: []u8) -> (hash: xxh_u64) {
|
XXH3_hashLong_64b_withSecret :: #force_no_inline proc(input: []u8, seed64: xxh_u64, secret: []u8) -> (hash: xxh_u64) {
|
||||||
return XXH3_hashLong_64b_internal(input, secret, XXH3_accumulate_512, XXH3_scramble_accumulator)
|
return XXH3_hashLong_64b_internal(input, secret, XXH3_accumulate_512, XXH3_scramble_accumulator)
|
||||||
}
|
}
|
||||||
@@ -880,7 +880,7 @@ XXH3_hashLong_64b_withSecret :: #force_no_inline proc(input: []u8, seed64: xxh_u
|
|||||||
This variant enforces that the compiler can detect that,
|
This variant enforces that the compiler can detect that,
|
||||||
and uses this opportunity to streamline the generated code for better performance.
|
and uses this opportunity to streamline the generated code for better performance.
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_hashLong_64b_default :: #force_no_inline proc(input: []u8, seed64: xxh_u64, secret: []u8) -> (hash: xxh_u64) {
|
XXH3_hashLong_64b_default :: #force_no_inline proc(input: []u8, seed64: xxh_u64, secret: []u8) -> (hash: xxh_u64) {
|
||||||
return XXH3_hashLong_64b_internal(input, XXH3_kSecret[:], XXH3_accumulate_512, XXH3_scramble_accumulator)
|
return XXH3_hashLong_64b_internal(input, XXH3_kSecret[:], XXH3_accumulate_512, XXH3_scramble_accumulator)
|
||||||
}
|
}
|
||||||
@@ -896,26 +896,27 @@ XXH3_hashLong_64b_default :: #force_no_inline proc(input: []u8, seed64: xxh_u64,
|
|||||||
It's important for performance that XXH3_hashLong is not inlined. Not sure
|
It's important for performance that XXH3_hashLong is not inlined. Not sure
|
||||||
why (uop cache maybe?), but the difference is large and easily measurable.
|
why (uop cache maybe?), but the difference is large and easily measurable.
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_hashLong_64b_withSeed_internal :: #force_no_inline proc(input: []u8,
|
XXH3_hashLong_64b_withSeed_internal :: #force_no_inline proc(
|
||||||
|
input: []u8,
|
||||||
seed: xxh_u64,
|
seed: xxh_u64,
|
||||||
f_acc512: XXH3_accumulate_512_f,
|
f_acc512: XXH3_accumulate_512_f,
|
||||||
f_scramble: XXH3_scramble_accumulator_f,
|
f_scramble: XXH3_scramble_accumulator_f,
|
||||||
f_init_sec: XXH3_init_custom_secret_f) -> (hash: xxh_u64) {
|
f_init_sec: XXH3_init_custom_secret_f,
|
||||||
|
) -> (hash: xxh_u64) {
|
||||||
if seed == 0 {
|
if seed == 0 {
|
||||||
return XXH3_hashLong_64b_internal(input, XXH3_kSecret[:], f_acc512, f_scramble)
|
return XXH3_hashLong_64b_internal(input, XXH3_kSecret[:], f_acc512, f_scramble)
|
||||||
}
|
}
|
||||||
{
|
|
||||||
secret: [XXH_SECRET_DEFAULT_SIZE]u8
|
secret: [XXH_SECRET_DEFAULT_SIZE]u8
|
||||||
f_init_sec(secret[:], seed)
|
f_init_sec(secret[:], seed)
|
||||||
return XXH3_hashLong_64b_internal(input, secret[:], f_acc512, f_scramble)
|
return XXH3_hashLong_64b_internal(input, secret[:], f_acc512, f_scramble)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
It's important for performance that XXH3_hashLong is not inlined.
|
It's important for performance that XXH3_hashLong is not inlined.
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_hashLong_64b_withSeed :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (hash: xxh_u64) {
|
XXH3_hashLong_64b_withSeed :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (hash: xxh_u64) {
|
||||||
return XXH3_hashLong_64b_withSeed_internal(input, seed, XXH3_accumulate_512, XXH3_scramble_accumulator, XXH3_init_custom_secret)
|
return XXH3_hashLong_64b_withSeed_internal(input, seed, XXH3_accumulate_512, XXH3_scramble_accumulator, XXH3_init_custom_secret)
|
||||||
}
|
}
|
||||||
@@ -923,7 +924,7 @@ XXH3_hashLong_64b_withSeed :: #force_no_inline proc(input: []u8, seed: xxh_u64,
|
|||||||
|
|
||||||
XXH3_hashLong64_f :: #type proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: xxh_u64)
|
XXH3_hashLong64_f :: #type proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: xxh_u64)
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_64bits_internal :: proc(input: []u8, seed: xxh_u64, secret: []u8, f_hashLong: XXH3_hashLong64_f) -> (hash: xxh_u64) {
|
XXH3_64bits_internal :: proc(input: []u8, seed: xxh_u64, secret: []u8, f_hashLong: XXH3_hashLong64_f) -> (hash: xxh_u64) {
|
||||||
assert(len(secret) >= XXH3_SECRET_SIZE_MIN)
|
assert(len(secret) >= XXH3_SECRET_SIZE_MIN)
|
||||||
/*
|
/*
|
||||||
@@ -943,17 +944,17 @@ XXH3_64bits_internal :: proc(input: []u8, seed: xxh_u64, secret: []u8, f_hashLon
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* === Public entry point === */
|
/* === Public entry point === */
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_64_default :: proc(input: []u8) -> (hash: xxh_u64) {
|
XXH3_64_default :: proc(input: []u8) -> (hash: xxh_u64) {
|
||||||
return XXH3_64bits_internal(input, 0, XXH3_kSecret[:], XXH3_hashLong_64b_default)
|
return XXH3_64bits_internal(input, 0, XXH3_kSecret[:], XXH3_hashLong_64b_default)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_64_with_seed :: proc(input: []u8, seed: xxh_u64) -> (hash: xxh_u64) {
|
XXH3_64_with_seed :: proc(input: []u8, seed: xxh_u64) -> (hash: xxh_u64) {
|
||||||
return XXH3_64bits_internal(input, seed, XXH3_kSecret[:], XXH3_hashLong_64b_withSeed)
|
return XXH3_64bits_internal(input, seed, XXH3_kSecret[:], XXH3_hashLong_64b_withSeed)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_64_with_secret :: proc(input, secret: []u8) -> (hash: xxh_u64) {
|
XXH3_64_with_secret :: proc(input, secret: []u8) -> (hash: xxh_u64) {
|
||||||
return XXH3_64bits_internal(input, 0, secret, XXH3_hashLong_64b_withSecret)
|
return XXH3_64bits_internal(input, 0, secret, XXH3_hashLong_64b_withSecret)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ XXH_PRIME32_3 :: 0xC2B2AE3D /*!< 0b11000010101100101010111000111101 */
|
|||||||
XXH_PRIME32_4 :: 0x27D4EB2F /*!< 0b00100111110101001110101100101111 */
|
XXH_PRIME32_4 :: 0x27D4EB2F /*!< 0b00100111110101001110101100101111 */
|
||||||
XXH_PRIME32_5 :: 0x165667B1 /*!< 0b00010110010101100110011110110001 */
|
XXH_PRIME32_5 :: 0x165667B1 /*!< 0b00010110010101100110011110110001 */
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH32_round :: #force_inline proc(seed, input: XXH32_hash) -> (res: XXH32_hash) {
|
XXH32_round :: #force_inline proc(seed, input: XXH32_hash) -> (res: XXH32_hash) {
|
||||||
seed := seed
|
seed := seed
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ XXH32_round :: #force_inline proc(seed, input: XXH32_hash) -> (res: XXH32_hash)
|
|||||||
/*
|
/*
|
||||||
Mix all bits
|
Mix all bits
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH32_avalanche :: #force_inline proc(h32: u32) -> (res: u32) {
|
XXH32_avalanche :: #force_inline proc(h32: u32) -> (res: u32) {
|
||||||
h32 := h32
|
h32 := h32
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ XXH32_avalanche :: #force_inline proc(h32: u32) -> (res: u32) {
|
|||||||
return h32
|
return h32
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH32_finalize :: #force_inline proc(h32: u32, buf: []u8, alignment: Alignment) -> (res: u32) {
|
XXH32_finalize :: #force_inline proc(h32: u32, buf: []u8, alignment: Alignment) -> (res: u32) {
|
||||||
process_1 :: #force_inline proc(h32: u32, buf: []u8) -> (h32_res: u32, buf_res: []u8) {
|
process_1 :: #force_inline proc(h32: u32, buf: []u8) -> (h32_res: u32, buf_res: []u8) {
|
||||||
#no_bounds_check b := u32(buf[0])
|
#no_bounds_check b := u32(buf[0])
|
||||||
@@ -143,7 +143,7 @@ XXH32_finalize :: #force_inline proc(h32: u32, buf: []u8, alignment: Alignment)
|
|||||||
unreachable()
|
unreachable()
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH32_endian_align :: #force_inline proc(input: []u8, seed := XXH32_DEFAULT_SEED, alignment: Alignment) -> (res: XXH32_hash) {
|
XXH32_endian_align :: #force_inline proc(input: []u8, seed := XXH32_DEFAULT_SEED, alignment: Alignment) -> (res: XXH32_hash) {
|
||||||
buf := input
|
buf := input
|
||||||
length := len(input)
|
length := len(input)
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ XXH_PRIME64_3 :: 0x165667B19E3779F9 /*!< 0b0001011001010110011001111011000110011
|
|||||||
XXH_PRIME64_4 :: 0x85EBCA77C2B2AE63 /*!< 0b1000010111101011110010100111011111000010101100101010111001100011 */
|
XXH_PRIME64_4 :: 0x85EBCA77C2B2AE63 /*!< 0b1000010111101011110010100111011111000010101100101010111001100011 */
|
||||||
XXH_PRIME64_5 :: 0x27D4EB2F165667C5 /*!< 0b0010011111010100111010110010111100010110010101100110011111000101 */
|
XXH_PRIME64_5 :: 0x27D4EB2F165667C5 /*!< 0b0010011111010100111010110010111100010110010101100110011111000101 */
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH64_round :: proc(acc, input: xxh_u64) -> (res: xxh_u64) {
|
XXH64_round :: proc(acc, input: xxh_u64) -> (res: xxh_u64) {
|
||||||
acc := acc
|
acc := acc
|
||||||
|
|
||||||
@@ -50,14 +50,14 @@ XXH64_round :: proc(acc, input: xxh_u64) -> (res: xxh_u64) {
|
|||||||
return acc
|
return acc
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH64_mergeRound :: proc(acc, val: xxh_u64) -> (res: xxh_u64) {
|
XXH64_mergeRound :: proc(acc, val: xxh_u64) -> (res: xxh_u64) {
|
||||||
res = acc ~ XXH64_round(0, val)
|
res = acc ~ XXH64_round(0, val)
|
||||||
res = res * XXH_PRIME64_1 + XXH_PRIME64_4
|
res = res * XXH_PRIME64_1 + XXH_PRIME64_4
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH64_avalanche :: proc(h64: xxh_u64) -> (res: xxh_u64) {
|
XXH64_avalanche :: proc(h64: xxh_u64) -> (res: xxh_u64) {
|
||||||
res = h64
|
res = h64
|
||||||
res ~= res >> 33
|
res ~= res >> 33
|
||||||
@@ -68,7 +68,7 @@ XXH64_avalanche :: proc(h64: xxh_u64) -> (res: xxh_u64) {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH64_finalize :: proc(h64: xxh_u64, buf: []u8, alignment: Alignment) -> (res: xxh_u64) {
|
XXH64_finalize :: proc(h64: xxh_u64, buf: []u8, alignment: Alignment) -> (res: xxh_u64) {
|
||||||
buf := buf
|
buf := buf
|
||||||
length := len(buf) & 31
|
length := len(buf) & 31
|
||||||
@@ -100,7 +100,7 @@ XXH64_finalize :: proc(h64: xxh_u64, buf: []u8, alignment: Alignment) -> (res: x
|
|||||||
return XXH64_avalanche(res)
|
return XXH64_avalanche(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH64_endian_align :: proc(input: []u8, seed := XXH64_DEFAULT_SEED, alignment := Alignment.Unaligned) -> (res: xxh_u64) {
|
XXH64_endian_align :: proc(input: []u8, seed := XXH64_DEFAULT_SEED, alignment := Alignment.Unaligned) -> (res: xxh_u64) {
|
||||||
buf := input
|
buf := input
|
||||||
length := len(buf)
|
length := len(buf)
|
||||||
@@ -191,7 +191,7 @@ XXH64_reset_state :: proc(state_ptr: ^XXH64_state, seed := XXH64_DEFAULT_SEED) -
|
|||||||
return .None
|
return .None
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH64_update :: proc(state: ^XXH64_state, input: []u8) -> (err: Error) {
|
XXH64_update :: proc(state: ^XXH64_state, input: []u8) -> (err: Error) {
|
||||||
buf := input
|
buf := input
|
||||||
length := len(buf)
|
length := len(buf)
|
||||||
@@ -245,7 +245,7 @@ XXH64_update :: proc(state: ^XXH64_state, input: []u8) -> (err: Error) {
|
|||||||
return .None
|
return .None
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH64_digest :: proc(state: ^XXH64_state) -> (res: XXH64_hash) {
|
XXH64_digest :: proc(state: ^XXH64_state) -> (res: XXH64_hash) {
|
||||||
if state.total_len >= 32 {
|
if state.total_len >= 32 {
|
||||||
v1 := state.v1
|
v1 := state.v1
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context
|
|||||||
return img, err
|
return img, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||||
context.allocator = allocator
|
context.allocator = allocator
|
||||||
options := options
|
options := options
|
||||||
@@ -131,7 +131,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
|||||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||||
|
|
||||||
if .info in options {
|
if .info in options {
|
||||||
options |= {.return_metadata, .do_not_decompress_image}
|
options += {.return_metadata, .do_not_decompress_image}
|
||||||
options -= {.info}
|
options -= {.info}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1376,7 +1376,7 @@ expand_grayscale :: proc(img: ^Image, allocator := context.allocator) -> (ok: bo
|
|||||||
/*
|
/*
|
||||||
Helper functions to read and write data from/to a Context, etc.
|
Helper functions to read and write data from/to a Context, etc.
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_data :: proc(z: $C, $T: typeid) -> (res: T, err: compress.General_Error) {
|
read_data :: proc(z: $C, $T: typeid) -> (res: T, err: compress.General_Error) {
|
||||||
if r, e := compress.read_data(z, T); e != .None {
|
if r, e := compress.read_data(z, T); e != .None {
|
||||||
return {}, .Stream_Too_Short
|
return {}, .Stream_Too_Short
|
||||||
@@ -1385,7 +1385,7 @@ read_data :: proc(z: $C, $T: typeid) -> (res: T, err: compress.General_Error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_u8 :: proc(z: $C) -> (res: u8, err: compress.General_Error) {
|
read_u8 :: proc(z: $C) -> (res: u8, err: compress.General_Error) {
|
||||||
if r, e := compress.read_u8(z); e != .None {
|
if r, e := compress.read_u8(z); e != .None {
|
||||||
return {}, .Stream_Too_Short
|
return {}, .Stream_Too_Short
|
||||||
|
|||||||
@@ -341,7 +341,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
|||||||
options := options
|
options := options
|
||||||
|
|
||||||
if .info in options {
|
if .info in options {
|
||||||
options |= {.return_metadata, .do_not_decompress_image}
|
options += {.return_metadata, .do_not_decompress_image}
|
||||||
options -= {.info}
|
options -= {.info}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -354,7 +354,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
|||||||
}
|
}
|
||||||
|
|
||||||
if .do_not_expand_channels in options {
|
if .do_not_expand_channels in options {
|
||||||
options |= {.do_not_expand_grayscale, .do_not_expand_indexed}
|
options += {.do_not_expand_grayscale, .do_not_expand_indexed}
|
||||||
}
|
}
|
||||||
|
|
||||||
if img == nil {
|
if img == nil {
|
||||||
|
|||||||
@@ -139,15 +139,13 @@ save_to_buffer :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}
|
|||||||
} else {
|
} else {
|
||||||
// Write RGB literal
|
// Write RGB literal
|
||||||
output.buf[written] = u8(QOI_Opcode_Tag.RGB)
|
output.buf[written] = u8(QOI_Opcode_Tag.RGB)
|
||||||
pix_bytes := transmute([4]u8)pix
|
copy(output.buf[written + 1:], pix[:3])
|
||||||
copy(output.buf[written + 1:], pix_bytes[:3])
|
|
||||||
written += 4
|
written += 4
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Write RGBA literal
|
// Write RGBA literal
|
||||||
output.buf[written] = u8(QOI_Opcode_Tag.RGBA)
|
output.buf[written] = u8(QOI_Opcode_Tag.RGBA)
|
||||||
pix_bytes := transmute([4]u8)pix
|
copy(output.buf[written + 1:], pix[:])
|
||||||
copy(output.buf[written + 1:], pix_bytes[:])
|
|
||||||
written += 5
|
written += 5
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,13 +170,13 @@ load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context
|
|||||||
return img, err
|
return img, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||||
context.allocator = allocator
|
context.allocator = allocator
|
||||||
options := options
|
options := options
|
||||||
|
|
||||||
if .info in options {
|
if .info in options {
|
||||||
options |= {.return_metadata, .do_not_decompress_image}
|
options += {.return_metadata, .do_not_decompress_image}
|
||||||
options -= {.info}
|
options -= {.info}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
|||||||
}
|
}
|
||||||
|
|
||||||
if .info in options {
|
if .info in options {
|
||||||
options |= {.return_metadata, .do_not_decompress_image}
|
options += {.return_metadata, .do_not_decompress_image}
|
||||||
options -= {.info}
|
options -= {.info}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -375,10 +375,6 @@ write_at_least :: proc(w: Writer, buf: []byte, min: int) -> (n: int, err: Error)
|
|||||||
nn, err = write(w, buf[n:])
|
nn, err = write(w, buf[n:])
|
||||||
n += nn
|
n += nn
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil && n < min {
|
|
||||||
err = .Short_Write
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -362,11 +362,11 @@ platform_count_lsb :: #force_inline proc(a: $T) -> (count: int)
|
|||||||
|
|
||||||
count_lsb :: proc { int_count_lsb, platform_count_lsb, }
|
count_lsb :: proc { int_count_lsb, platform_count_lsb, }
|
||||||
|
|
||||||
int_random_digit :: proc(r: ^rnd.Rand = nil) -> (res: DIGIT) {
|
int_random_digit :: proc() -> (res: DIGIT) {
|
||||||
when _DIGIT_BITS == 60 { // DIGIT = u64
|
when _DIGIT_BITS == 60 { // DIGIT = u64
|
||||||
return DIGIT(rnd.uint64(r)) & _MASK
|
return DIGIT(rnd.uint64()) & _MASK
|
||||||
} else when _DIGIT_BITS == 28 { // DIGIT = u32
|
} else when _DIGIT_BITS == 28 { // DIGIT = u32
|
||||||
return DIGIT(rnd.uint32(r)) & _MASK
|
return DIGIT(rnd.uint32()) & _MASK
|
||||||
} else {
|
} else {
|
||||||
panic("Unsupported DIGIT size.")
|
panic("Unsupported DIGIT size.")
|
||||||
}
|
}
|
||||||
@@ -374,12 +374,12 @@ int_random_digit :: proc(r: ^rnd.Rand = nil) -> (res: DIGIT) {
|
|||||||
return 0 // We shouldn't get here.
|
return 0 // We shouldn't get here.
|
||||||
}
|
}
|
||||||
|
|
||||||
int_random :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil, allocator := context.allocator) -> (err: Error) {
|
int_random :: proc(dest: ^Int, bits: int, allocator := context.allocator) -> (err: Error) {
|
||||||
/*
|
/*
|
||||||
Check that `a` is usable.
|
Check that `a` is usable.
|
||||||
*/
|
*/
|
||||||
assert_if_nil(dest)
|
assert_if_nil(dest)
|
||||||
return #force_inline internal_int_random(dest, bits, r, allocator)
|
return #force_inline internal_int_random(dest, bits, allocator)
|
||||||
|
|
||||||
}
|
}
|
||||||
random :: proc { int_random, }
|
random :: proc { int_random, }
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user