mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 07:38:48 +00:00
Merge tag 'dev-2024-07'
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
|
||||||
|
|||||||
@@ -194,7 +194,8 @@ type_proc_return_count :: proc($T: typeid) -> int where type_is_proc(T) ---
|
|||||||
type_proc_parameter_type :: proc($T: typeid, index: int) -> typeid where type_is_proc(T) ---
|
type_proc_parameter_type :: proc($T: typeid, index: int) -> typeid where type_is_proc(T) ---
|
||||||
type_proc_return_type :: proc($T: typeid, index: int) -> typeid where type_is_proc(T) ---
|
type_proc_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 ---
|
||||||
|
|||||||
@@ -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,8 +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 {
|
||||||
// Same behavior as _append_elems but there's only one arg, so we always just add 8.
|
// Same behavior as _append_elems but there's only one arg, so we always just add DEFAULT_DYNAMIC_ARRAY_CAPACITY.
|
||||||
cap := 2 * cap(array) + 8
|
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 {
|
||||||
@@ -473,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 {
|
||||||
@@ -541,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 {
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,16 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
_heap_alloc :: proc "contextless" (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 "contextless" (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 "contextless" (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")
|
||||||
}
|
}
|
||||||
|
|||||||
+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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -83,13 +83,13 @@ default_random_generator_proc :: proc(data: rawptr, mode: Random_Generator_Mode,
|
|||||||
switch mode {
|
switch mode {
|
||||||
case .Read:
|
case .Read:
|
||||||
if r.state == 0 && r.inc == 0 {
|
if r.state == 0 && r.inc == 0 {
|
||||||
init(r, 0)
|
init(r, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch len(p) {
|
switch len(p) {
|
||||||
case size_of(u64):
|
case size_of(u64):
|
||||||
// Fast path for a 64-bit destination.
|
// Fast path for a 64-bit destination.
|
||||||
intrinsics.unaligned_store(transmute(^u64)raw_data(p), read_u64(r))
|
intrinsics.unaligned_store((^u64)(raw_data(p)), read_u64(r))
|
||||||
case:
|
case:
|
||||||
// All other cases.
|
// All other cases.
|
||||||
pos := i8(0)
|
pos := i8(0)
|
||||||
@@ -108,7 +108,7 @@ default_random_generator_proc :: proc(data: rawptr, mode: Random_Generator_Mode,
|
|||||||
case .Reset:
|
case .Reset:
|
||||||
seed: u64
|
seed: u64
|
||||||
mem_copy_non_overlapping(&seed, raw_data(p), min(size_of(seed), len(p)))
|
mem_copy_non_overlapping(&seed, raw_data(p), min(size_of(seed), len(p)))
|
||||||
init(r, seed)
|
init(r, seed)
|
||||||
|
|
||||||
case .Query_Info:
|
case .Query_Info:
|
||||||
if len(p) != size_of(Random_Generator_Query_Info) {
|
if len(p) != size_of(Random_Generator_Query_Info) {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
+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))
|
||||||
|
|||||||
@@ -34,13 +34,13 @@ COMPRESS_OUTPUT_ALLOCATE_MIN :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MIN, 1 << 2
|
|||||||
*/
|
*/
|
||||||
when size_of(uintptr) == 8 {
|
when size_of(uintptr) == 8 {
|
||||||
|
|
||||||
// For 64-bit platforms, we set the default max buffer size to 4 GiB,
|
// For 64-bit platforms, we set the default max buffer size to 4 GiB,
|
||||||
// which is GZIP and PKZIP's max payload size.
|
// which is GZIP and PKZIP's max payload size.
|
||||||
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 32))
|
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 32))
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// For 32-bit platforms, we set the default max buffer size to 512 MiB.
|
// For 32-bit platforms, we set the default max buffer size to 512 MiB.
|
||||||
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 29))
|
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 29))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ Tree :: struct($Key: typeid, $Value: typeid) {
|
|||||||
|
|
||||||
_root: ^Node(Key, Value),
|
_root: ^Node(Key, Value),
|
||||||
_node_allocator: runtime.Allocator,
|
_node_allocator: runtime.Allocator,
|
||||||
_cmp_fn: proc(Key, Key) -> Ordering,
|
_cmp_fn: proc(Key, Key) -> Ordering,
|
||||||
_size: int,
|
_size: int,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -119,20 +119,20 @@ consume :: proc "odin" (a: ^$A/Small_Array($N, $T), count: int, loc := #caller_l
|
|||||||
}
|
}
|
||||||
|
|
||||||
ordered_remove :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, loc := #caller_location) #no_bounds_check {
|
ordered_remove :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, loc := #caller_location) #no_bounds_check {
|
||||||
runtime.bounds_check_error_loc(loc, index, a.len)
|
runtime.bounds_check_error_loc(loc, index, a.len)
|
||||||
if index+1 < a.len {
|
if index+1 < a.len {
|
||||||
copy(a.data[index:], a.data[index+1:])
|
copy(a.data[index:], a.data[index+1:])
|
||||||
}
|
}
|
||||||
a.len -= 1
|
a.len -= 1
|
||||||
}
|
}
|
||||||
|
|
||||||
unordered_remove :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, loc := #caller_location) #no_bounds_check {
|
unordered_remove :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, loc := #caller_location) #no_bounds_check {
|
||||||
runtime.bounds_check_error_loc(loc, index, a.len)
|
runtime.bounds_check_error_loc(loc, index, a.len)
|
||||||
n := a.len-1
|
n := a.len-1
|
||||||
if index != n {
|
if index != n {
|
||||||
a.data[index] = a.data[n]
|
a.data[index] = a.data[n]
|
||||||
}
|
}
|
||||||
a.len -= 1
|
a.len -= 1
|
||||||
}
|
}
|
||||||
|
|
||||||
clear :: proc "contextless" (a: ^$A/Small_Array($N, $T)) {
|
clear :: proc "contextless" (a: ^$A/Small_Array($N, $T)) {
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ add_dependency :: proc(sorter: ^$S/Sorter($K), key, dependency: K) -> bool {
|
|||||||
}
|
}
|
||||||
find.dependents[key] = true
|
find.dependents[key] = true
|
||||||
|
|
||||||
find = &sorter.relations[key]
|
find = &sorter.relations[key]
|
||||||
if find == nil {
|
if find == nil {
|
||||||
find = map_insert(&sorter.relations, key, make_relations(sorter))
|
find = map_insert(&sorter.relations, key, make_relations(sorter))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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" (
|
||||||
|
|||||||
@@ -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))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ HAS_RAND_BYTES :: true
|
|||||||
_rand_bytes :: proc(dst: []byte) {
|
_rand_bytes :: proc(dst: []byte) {
|
||||||
err := Sec.RandomCopyBytes(count=len(dst), bytes=raw_data(dst))
|
err := Sec.RandomCopyBytes(count=len(dst), bytes=raw_data(dst))
|
||||||
if err != .Success {
|
if err != .Success {
|
||||||
msg := CF.StringCopyToOdinString(Sec.CopyErrorMessageString(err))
|
msg := CF.StringCopyToOdinString(Sec.CopyErrorMessageString(err))
|
||||||
fmt.panicf("crypto/rand_bytes: SecRandomCopyBytes returned non-zero result: %v %s", err, msg)
|
fmt.panicf("crypto/rand_bytes: SecRandomCopyBytes returned non-zero result: %v %s", err, msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
+116
-116
@@ -8,141 +8,141 @@ package encoding_base32
|
|||||||
// truncate it from the encoded output.
|
// truncate it from the encoded output.
|
||||||
|
|
||||||
ENC_TABLE := [32]byte {
|
ENC_TABLE := [32]byte {
|
||||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||||
'Y', 'Z', '2', '3', '4', '5', '6', '7',
|
'Y', 'Z', '2', '3', '4', '5', '6', '7',
|
||||||
}
|
}
|
||||||
|
|
||||||
PADDING :: '='
|
PADDING :: '='
|
||||||
|
|
||||||
DEC_TABLE := [?]u8 {
|
DEC_TABLE := [?]u8 {
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 26, 27, 28, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 26, 27, 28, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
|
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
|
||||||
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
|
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string {
|
encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string {
|
||||||
out_length := (len(data) + 4) / 5 * 8
|
out_length := (len(data) + 4) / 5 * 8
|
||||||
out := make([]byte, out_length)
|
out := make([]byte, out_length)
|
||||||
_encode(out, data)
|
_encode(out, data)
|
||||||
return string(out)
|
return string(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
@private
|
@private
|
||||||
_encode :: proc(out, data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) {
|
_encode :: proc(out, data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) {
|
||||||
out := out
|
out := out
|
||||||
data := data
|
data := data
|
||||||
|
|
||||||
for len(data) > 0 {
|
for len(data) > 0 {
|
||||||
carry: byte
|
carry: byte
|
||||||
switch len(data) {
|
switch len(data) {
|
||||||
case:
|
case:
|
||||||
out[7] = ENC_TABLE[data[4] & 0x1f]
|
out[7] = ENC_TABLE[data[4] & 0x1f]
|
||||||
carry = data[4] >> 5
|
carry = data[4] >> 5
|
||||||
fallthrough
|
fallthrough
|
||||||
case 4:
|
case 4:
|
||||||
out[6] = ENC_TABLE[carry | (data[3] << 3) & 0x1f]
|
out[6] = ENC_TABLE[carry | (data[3] << 3) & 0x1f]
|
||||||
out[5] = ENC_TABLE[(data[3] >> 2) & 0x1f]
|
out[5] = ENC_TABLE[(data[3] >> 2) & 0x1f]
|
||||||
carry = data[3] >> 7
|
carry = data[3] >> 7
|
||||||
fallthrough
|
fallthrough
|
||||||
case 3:
|
case 3:
|
||||||
out[4] = ENC_TABLE[carry | (data[2] << 1) & 0x1f]
|
out[4] = ENC_TABLE[carry | (data[2] << 1) & 0x1f]
|
||||||
carry = (data[2] >> 4) & 0x1f
|
carry = (data[2] >> 4) & 0x1f
|
||||||
fallthrough
|
fallthrough
|
||||||
case 2:
|
case 2:
|
||||||
out[3] = ENC_TABLE[carry | (data[1] << 4) & 0x1f]
|
out[3] = ENC_TABLE[carry | (data[1] << 4) & 0x1f]
|
||||||
out[2] = ENC_TABLE[(data[1] >> 1) & 0x1f]
|
out[2] = ENC_TABLE[(data[1] >> 1) & 0x1f]
|
||||||
carry = (data[1] >> 6) & 0x1f
|
carry = (data[1] >> 6) & 0x1f
|
||||||
fallthrough
|
fallthrough
|
||||||
case 1:
|
case 1:
|
||||||
out[1] = ENC_TABLE[carry | (data[0] << 2) & 0x1f]
|
out[1] = ENC_TABLE[carry | (data[0] << 2) & 0x1f]
|
||||||
out[0] = ENC_TABLE[data[0] >> 3]
|
out[0] = ENC_TABLE[data[0] >> 3]
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(data) < 5 {
|
if len(data) < 5 {
|
||||||
out[7] = byte(PADDING)
|
out[7] = byte(PADDING)
|
||||||
if len(data) < 4 {
|
if len(data) < 4 {
|
||||||
out[6] = byte(PADDING)
|
out[6] = byte(PADDING)
|
||||||
out[5] = byte(PADDING)
|
out[5] = byte(PADDING)
|
||||||
if len(data) < 3 {
|
if len(data) < 3 {
|
||||||
out[4] = byte(PADDING)
|
out[4] = byte(PADDING)
|
||||||
if len(data) < 2 {
|
if len(data) < 2 {
|
||||||
out[3] = byte(PADDING)
|
out[3] = byte(PADDING)
|
||||||
out[2] = byte(PADDING)
|
out[2] = byte(PADDING)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
data = data[5:]
|
data = data[5:]
|
||||||
out = out[8:]
|
out = out[8:]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check{
|
decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check{
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
outi := 0
|
outi := 0
|
||||||
data := data
|
data := data
|
||||||
|
|
||||||
out := make([]byte, len(data) / 8 * 5, allocator)
|
out := make([]byte, len(data) / 8 * 5, allocator)
|
||||||
end := false
|
end := false
|
||||||
for len(data) > 0 && !end {
|
for len(data) > 0 && !end {
|
||||||
dbuf : [8]byte
|
dbuf : [8]byte
|
||||||
dlen := 8
|
dlen := 8
|
||||||
|
|
||||||
for j := 0; j < 8; {
|
for j := 0; j < 8; {
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
dlen, end = j, true
|
dlen, end = j, true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
input := data[0]
|
input := data[0]
|
||||||
data = data[1:]
|
data = data[1:]
|
||||||
if input == byte(PADDING) && j >= 2 && len(data) < 8 {
|
if input == byte(PADDING) && j >= 2 && len(data) < 8 {
|
||||||
assert(!(len(data) + j < 8 - 1), "Corrupted input")
|
assert(!(len(data) + j < 8 - 1), "Corrupted input")
|
||||||
for k := 0; k < 8-1-j; k +=1 {
|
for k := 0; k < 8-1-j; k +=1 {
|
||||||
assert(len(data) < k || data[k] == byte(PADDING), "Corrupted input")
|
assert(len(data) < k || data[k] == byte(PADDING), "Corrupted input")
|
||||||
}
|
}
|
||||||
dlen, end = j, true
|
dlen, end = j, true
|
||||||
assert(dlen != 1 && dlen != 3 && dlen != 6, "Corrupted input")
|
assert(dlen != 1 && dlen != 3 && dlen != 6, "Corrupted input")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
dbuf[j] = DEC_TABLE[input]
|
dbuf[j] = DEC_TABLE[input]
|
||||||
assert(dbuf[j] != 0xff, "Corrupted input")
|
assert(dbuf[j] != 0xff, "Corrupted input")
|
||||||
j += 1
|
j += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
switch dlen {
|
switch dlen {
|
||||||
case 8:
|
case 8:
|
||||||
out[outi + 4] = dbuf[6] << 5 | dbuf[7]
|
out[outi + 4] = dbuf[6] << 5 | dbuf[7]
|
||||||
fallthrough
|
fallthrough
|
||||||
case 7:
|
case 7:
|
||||||
out[outi + 3] = dbuf[4] << 7 | dbuf[5] << 2 | dbuf[6] >> 3
|
out[outi + 3] = dbuf[4] << 7 | dbuf[5] << 2 | dbuf[6] >> 3
|
||||||
fallthrough
|
fallthrough
|
||||||
case 5:
|
case 5:
|
||||||
out[outi + 2] = dbuf[3] << 4 | dbuf[4] >> 1
|
out[outi + 2] = dbuf[3] << 4 | dbuf[4] >> 1
|
||||||
fallthrough
|
fallthrough
|
||||||
case 4:
|
case 4:
|
||||||
out[outi + 1] = dbuf[1] << 6 | dbuf[2] << 1 | dbuf[3] >> 4
|
out[outi + 1] = dbuf[1] << 6 | dbuf[2] << 1 | dbuf[3] >> 4
|
||||||
fallthrough
|
fallthrough
|
||||||
case 2:
|
case 2:
|
||||||
out[outi + 0] = dbuf[0] << 3 | dbuf[1] >> 2
|
out[outi + 0] = dbuf[0] << 3 | dbuf[1] >> 2
|
||||||
}
|
}
|
||||||
outi += 5
|
outi += 5
|
||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
@@ -423,7 +423,7 @@ _decode_bytes :: proc(d: Decoder, add: Add, type: Major = .Bytes, allocator := c
|
|||||||
_encode_bytes :: proc(e: Encoder, val: Bytes, major: Major = .Bytes) -> (err: Encode_Error) {
|
_encode_bytes :: proc(e: Encoder, val: Bytes, major: Major = .Bytes) -> (err: Encode_Error) {
|
||||||
assert(len(val) >= 0)
|
assert(len(val) >= 0)
|
||||||
_encode_u64(e, u64(len(val)), major) or_return
|
_encode_u64(e, u64(len(val)), major) or_return
|
||||||
_, err = io.write_full(e.writer, val[:])
|
_, err = io.write_full(e.writer, val[:])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -440,7 +440,7 @@ _decode_text :: proc(d: Decoder, add: Add, allocator := context.allocator, loc :
|
|||||||
}
|
}
|
||||||
|
|
||||||
_encode_text :: proc(e: Encoder, val: Text) -> Encode_Error {
|
_encode_text :: proc(e: Encoder, val: Text) -> Encode_Error {
|
||||||
return _encode_bytes(e, transmute([]byte)val, .Text)
|
return _encode_bytes(e, transmute([]byte)val, .Text)
|
||||||
}
|
}
|
||||||
|
|
||||||
_decode_array_ptr :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: ^Array, err: Decode_Error) {
|
_decode_array_ptr :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: ^Array, err: Decode_Error) {
|
||||||
@@ -480,10 +480,10 @@ _decode_array :: proc(d: Decoder, add: Add, allocator := context.allocator, loc
|
|||||||
_encode_array :: proc(e: Encoder, arr: Array) -> Encode_Error {
|
_encode_array :: proc(e: Encoder, arr: Array) -> Encode_Error {
|
||||||
assert(len(arr) >= 0)
|
assert(len(arr) >= 0)
|
||||||
_encode_u64(e, u64(len(arr)), .Array)
|
_encode_u64(e, u64(len(arr)), .Array)
|
||||||
for val in arr {
|
for val in arr {
|
||||||
encode(e, val) or_return
|
encode(e, val) or_return
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_decode_map_ptr :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: ^Map, err: Decode_Error) {
|
_decode_map_ptr :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: ^Map, err: Decode_Error) {
|
||||||
@@ -576,7 +576,7 @@ _encode_map :: proc(e: Encoder, m: Map) -> (err: Encode_Error) {
|
|||||||
encode(e, entry.entry.value) or_return
|
encode(e, entry.entry.value) or_return
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_decode_tag_ptr :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: Value, err: Decode_Error) {
|
_decode_tag_ptr :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: Value, err: Decode_Error) {
|
||||||
@@ -626,7 +626,7 @@ _decode_uint_as_u64 :: proc(r: io.Reader, add: Add) -> (nr: u64, err: Decode_Err
|
|||||||
|
|
||||||
_encode_tag :: proc(e: Encoder, val: Tag) -> Encode_Error {
|
_encode_tag :: proc(e: Encoder, val: Tag) -> Encode_Error {
|
||||||
_encode_u64(e, val.number, .Tag) or_return
|
_encode_u64(e, val.number, .Tag) or_return
|
||||||
return encode(e, val.value)
|
return encode(e, val.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
_decode_simple :: proc(r: io.Reader) -> (v: Simple, err: io.Error) {
|
_decode_simple :: proc(r: io.Reader) -> (v: Simple, err: io.Error) {
|
||||||
@@ -739,16 +739,16 @@ _encode_nil :: proc(w: io.Writer) -> io.Error {
|
|||||||
// Streaming
|
// Streaming
|
||||||
|
|
||||||
encode_stream_begin :: proc(w: io.Writer, major: Major) -> (err: io.Error) {
|
encode_stream_begin :: proc(w: io.Writer, major: Major) -> (err: io.Error) {
|
||||||
assert(major >= Major(.Bytes) && major <= Major(.Map), "illegal stream type")
|
assert(major >= Major(.Bytes) && major <= Major(.Map), "illegal stream type")
|
||||||
|
|
||||||
header := (u8(major) << 5) | u8(Add.Length_Unknown)
|
header := (u8(major) << 5) | u8(Add.Length_Unknown)
|
||||||
_, err = io.write_full(w, {header})
|
_, err = io.write_full(w, {header})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
encode_stream_end :: proc(w: io.Writer) -> io.Error {
|
encode_stream_end :: proc(w: io.Writer) -> io.Error {
|
||||||
header := (u8(Major.Other) << 5) | u8(Add.Break)
|
header := (u8(Major.Other) << 5) | u8(Add.Break)
|
||||||
_, err := io.write_full(w, {header})
|
_, err := io.write_full(w, {header})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -757,8 +757,8 @@ encode_stream_text :: _encode_text
|
|||||||
encode_stream_array_item :: encode
|
encode_stream_array_item :: encode
|
||||||
|
|
||||||
encode_stream_map_entry :: proc(e: Encoder, key: Value, val: Value) -> Encode_Error {
|
encode_stream_map_entry :: proc(e: Encoder, key: Value, val: Value) -> Encode_Error {
|
||||||
encode(e, key) or_return
|
encode(e, key) or_return
|
||||||
return encode(e, val)
|
return encode(e, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
// For `Bytes` and `Text` strings: Decodes the number of items the header says follows.
|
// For `Bytes` and `Text` strings: Decodes the number of items the header says follows.
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -273,13 +273,13 @@ _unmarshal_value :: proc(d: Decoder, v: any, hdr: Header, allocator := context.a
|
|||||||
|
|
||||||
// NOTE: Because this is a special type and not to be treated as a general integer,
|
// NOTE: Because this is a special type and not to be treated as a general integer,
|
||||||
// We only put the value of it in fields that are explicitly of type `Simple`.
|
// We only put the value of it in fields that are explicitly of type `Simple`.
|
||||||
switch &dst in v {
|
switch &dst in v {
|
||||||
case Simple:
|
case Simple:
|
||||||
dst = decoded
|
dst = decoded
|
||||||
return
|
return
|
||||||
case:
|
case:
|
||||||
return _unsupported(v, hdr, add)
|
return _unsupported(v, hdr, add)
|
||||||
}
|
}
|
||||||
|
|
||||||
case .Tag:
|
case .Tag:
|
||||||
switch &dst in v {
|
switch &dst in v {
|
||||||
|
|||||||
+4816
-4816
File diff suppressed because it is too large
Load Diff
@@ -25,7 +25,7 @@ Marshal_Options :: struct {
|
|||||||
spec: Specification,
|
spec: Specification,
|
||||||
|
|
||||||
// Use line breaks & tabs/spaces
|
// Use line breaks & tabs/spaces
|
||||||
pretty: bool,
|
pretty: bool,
|
||||||
|
|
||||||
// Use spaces for indentation instead of tabs
|
// Use spaces for indentation instead of tabs
|
||||||
use_spaces: bool,
|
use_spaces: bool,
|
||||||
@@ -34,7 +34,7 @@ Marshal_Options :: struct {
|
|||||||
spaces: int,
|
spaces: int,
|
||||||
|
|
||||||
// Output uint as hex in JSON5 & MJSON
|
// Output uint as hex in JSON5 & MJSON
|
||||||
write_uint_as_hex: bool,
|
write_uint_as_hex: bool,
|
||||||
|
|
||||||
// If spec is MJSON and this is true, then keys will be quoted.
|
// If spec is MJSON and this is true, then keys will be quoted.
|
||||||
//
|
//
|
||||||
@@ -138,7 +138,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
|||||||
// allow uints to be printed as hex
|
// allow uints to be printed as hex
|
||||||
if opt.write_uint_as_hex && (opt.spec == .JSON5 || opt.spec == .MJSON) {
|
if opt.write_uint_as_hex && (opt.spec == .JSON5 || opt.spec == .MJSON) {
|
||||||
switch i in a {
|
switch i in a {
|
||||||
case u8, u16, u32, u64, u128:
|
case u8, u16, u32, u64, u128:
|
||||||
s = strconv.append_bits_128(buf[:], u, 16, info.signed, 8*ti.size, "0123456789abcdef", { .Prefix })
|
s = strconv.append_bits_128(buf[:], u, 16, info.signed, 8*ti.size, "0123456789abcdef", { .Prefix })
|
||||||
|
|
||||||
case:
|
case:
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
@@ -387,17 +387,17 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
|||||||
case runtime.Type_Info_Pointer,
|
case runtime.Type_Info_Pointer,
|
||||||
runtime.Type_Info_Multi_Pointer,
|
runtime.Type_Info_Multi_Pointer,
|
||||||
runtime.Type_Info_Procedure:
|
runtime.Type_Info_Procedure:
|
||||||
return (^rawptr)(v.data)^ == nil
|
return (^rawptr)(v.data)^ == nil
|
||||||
case runtime.Type_Info_Dynamic_Array:
|
case runtime.Type_Info_Dynamic_Array:
|
||||||
return (^runtime.Raw_Dynamic_Array)(v.data).len == 0
|
return (^runtime.Raw_Dynamic_Array)(v.data).len == 0
|
||||||
case runtime.Type_Info_Slice:
|
case runtime.Type_Info_Slice:
|
||||||
return (^runtime.Raw_Slice)(v.data).len == 0
|
return (^runtime.Raw_Slice)(v.data).len == 0
|
||||||
case runtime.Type_Info_Union,
|
case runtime.Type_Info_Union,
|
||||||
runtime.Type_Info_Bit_Set,
|
runtime.Type_Info_Bit_Set,
|
||||||
runtime.Type_Info_Soa_Pointer:
|
runtime.Type_Info_Soa_Pointer:
|
||||||
return reflect.is_nil(v)
|
return reflect.is_nil(v)
|
||||||
case runtime.Type_Info_Map:
|
case runtime.Type_Info_Map:
|
||||||
return (^runtime.Raw_Map)(v.data).len == 0
|
return (^runtime.Raw_Map)(v.data).len == 0
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -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 {
|
||||||
@@ -601,8 +603,8 @@ 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,
|
||||||
|
}
|
||||||
@@ -33,7 +33,7 @@ print :: proc(writer: io.Writer, doc: ^Document) -> (written: int, err: io.Error
|
|||||||
written += fmt.wprintf(writer, "[DOCTYPE] %v\n", doc.doctype.ident)
|
written += fmt.wprintf(writer, "[DOCTYPE] %v\n", doc.doctype.ident)
|
||||||
|
|
||||||
if len(doc.doctype.rest) > 0 {
|
if len(doc.doctype.rest) > 0 {
|
||||||
fmt.wprintf(writer, "\t%v\n", doc.doctype.rest)
|
fmt.wprintf(writer, "\t%v\n", doc.doctype.rest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,10 +42,10 @@ print :: proc(writer: io.Writer, doc: ^Document) -> (written: int, err: io.Error
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(doc.elements) > 0 {
|
if len(doc.elements) > 0 {
|
||||||
fmt.wprintln(writer, " --- ")
|
fmt.wprintln(writer, " --- ")
|
||||||
print_element(writer, doc, 0)
|
print_element(writer, doc, 0)
|
||||||
fmt.wprintln(writer, " --- ")
|
fmt.wprintln(writer, " --- ")
|
||||||
}
|
}
|
||||||
|
|
||||||
return written, .None
|
return written, .None
|
||||||
}
|
}
|
||||||
|
|||||||
+101
-108
@@ -45,45 +45,45 @@ parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info:
|
|||||||
if specific_type_info.signed {
|
if specific_type_info.signed {
|
||||||
value := strconv.parse_i128(str) or_return
|
value := strconv.parse_i128(str) or_return
|
||||||
switch type_info.id {
|
switch type_info.id {
|
||||||
case i8: (cast(^i8) ptr)^ = cast(i8) bounded_int(value, cast(i128)min(i8), cast(i128)max(i8) ) or_return
|
case i8: (^i8) (ptr)^ = cast(i8) bounded_int(value, cast(i128)min(i8), cast(i128)max(i8) ) or_return
|
||||||
case i16: (cast(^i16) ptr)^ = cast(i16) bounded_int(value, cast(i128)min(i16), cast(i128)max(i16) ) or_return
|
case i16: (^i16) (ptr)^ = cast(i16) bounded_int(value, cast(i128)min(i16), cast(i128)max(i16) ) or_return
|
||||||
case i32: (cast(^i32) ptr)^ = cast(i32) bounded_int(value, cast(i128)min(i32), cast(i128)max(i32) ) or_return
|
case i32: (^i32) (ptr)^ = cast(i32) bounded_int(value, cast(i128)min(i32), cast(i128)max(i32) ) or_return
|
||||||
case i64: (cast(^i64) ptr)^ = cast(i64) bounded_int(value, cast(i128)min(i64), cast(i128)max(i64) ) or_return
|
case i64: (^i64) (ptr)^ = cast(i64) bounded_int(value, cast(i128)min(i64), cast(i128)max(i64) ) or_return
|
||||||
case i128: (cast(^i128) ptr)^ = value
|
case i128: (^i128) (ptr)^ = value
|
||||||
|
|
||||||
case int: (cast(^int) ptr)^ = cast(int) bounded_int(value, cast(i128)min(int), cast(i128)max(int) ) or_return
|
case int: (^int) (ptr)^ = cast(int) bounded_int(value, cast(i128)min(int), cast(i128)max(int) ) or_return
|
||||||
|
|
||||||
case i16le: (cast(^i16le) ptr)^ = cast(i16le) bounded_int(value, cast(i128)min(i16le), cast(i128)max(i16le) ) or_return
|
case i16le: (^i16le) (ptr)^ = cast(i16le) bounded_int(value, cast(i128)min(i16le), cast(i128)max(i16le) ) or_return
|
||||||
case i32le: (cast(^i32le) ptr)^ = cast(i32le) bounded_int(value, cast(i128)min(i32le), cast(i128)max(i32le) ) or_return
|
case i32le: (^i32le) (ptr)^ = cast(i32le) bounded_int(value, cast(i128)min(i32le), cast(i128)max(i32le) ) or_return
|
||||||
case i64le: (cast(^i64le) ptr)^ = cast(i64le) bounded_int(value, cast(i128)min(i64le), cast(i128)max(i64le) ) or_return
|
case i64le: (^i64le) (ptr)^ = cast(i64le) bounded_int(value, cast(i128)min(i64le), cast(i128)max(i64le) ) or_return
|
||||||
case i128le: (cast(^i128le)ptr)^ = cast(i128le) bounded_int(value, cast(i128)min(i128le), cast(i128)max(i128le)) or_return
|
case i128le: (^i128le)(ptr)^ = cast(i128le) bounded_int(value, cast(i128)min(i128le), cast(i128)max(i128le)) or_return
|
||||||
|
|
||||||
case i16be: (cast(^i16be) ptr)^ = cast(i16be) bounded_int(value, cast(i128)min(i16be), cast(i128)max(i16be) ) or_return
|
case i16be: (^i16be) (ptr)^ = cast(i16be) bounded_int(value, cast(i128)min(i16be), cast(i128)max(i16be) ) or_return
|
||||||
case i32be: (cast(^i32be) ptr)^ = cast(i32be) bounded_int(value, cast(i128)min(i32be), cast(i128)max(i32be) ) or_return
|
case i32be: (^i32be) (ptr)^ = cast(i32be) bounded_int(value, cast(i128)min(i32be), cast(i128)max(i32be) ) or_return
|
||||||
case i64be: (cast(^i64be) ptr)^ = cast(i64be) bounded_int(value, cast(i128)min(i64be), cast(i128)max(i64be) ) or_return
|
case i64be: (^i64be) (ptr)^ = cast(i64be) bounded_int(value, cast(i128)min(i64be), cast(i128)max(i64be) ) or_return
|
||||||
case i128be: (cast(^i128be)ptr)^ = cast(i128be) bounded_int(value, cast(i128)min(i128be), cast(i128)max(i128be)) or_return
|
case i128be: (^i128be)(ptr)^ = cast(i128be) bounded_int(value, cast(i128)min(i128be), cast(i128)max(i128be)) or_return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
value := strconv.parse_u128(str) or_return
|
value := strconv.parse_u128(str) or_return
|
||||||
switch type_info.id {
|
switch type_info.id {
|
||||||
case u8: (cast(^u8) ptr)^ = cast(u8) bounded_uint(value, cast(u128)max(u8) ) or_return
|
case u8: (^u8) (ptr)^ = cast(u8) bounded_uint(value, cast(u128)max(u8) ) or_return
|
||||||
case u16: (cast(^u16) ptr)^ = cast(u16) bounded_uint(value, cast(u128)max(u16) ) or_return
|
case u16: (^u16) (ptr)^ = cast(u16) bounded_uint(value, cast(u128)max(u16) ) or_return
|
||||||
case u32: (cast(^u32) ptr)^ = cast(u32) bounded_uint(value, cast(u128)max(u32) ) or_return
|
case u32: (^u32) (ptr)^ = cast(u32) bounded_uint(value, cast(u128)max(u32) ) or_return
|
||||||
case u64: (cast(^u64) ptr)^ = cast(u64) bounded_uint(value, cast(u128)max(u64) ) or_return
|
case u64: (^u64) (ptr)^ = cast(u64) bounded_uint(value, cast(u128)max(u64) ) or_return
|
||||||
case u128: (cast(^u128) ptr)^ = value
|
case u128: (^u128) (ptr)^ = value
|
||||||
|
|
||||||
case uint: (cast(^uint) ptr)^ = cast(uint) bounded_uint(value, cast(u128)max(uint) ) or_return
|
case uint: (^uint) (ptr)^ = cast(uint) bounded_uint(value, cast(u128)max(uint) ) or_return
|
||||||
case uintptr: (cast(^uintptr)ptr)^ = cast(uintptr) bounded_uint(value, cast(u128)max(uintptr)) or_return
|
case uintptr: (^uintptr)(ptr)^ = cast(uintptr) bounded_uint(value, cast(u128)max(uintptr)) or_return
|
||||||
|
|
||||||
case u16le: (cast(^u16le) ptr)^ = cast(u16le) bounded_uint(value, cast(u128)max(u16le) ) or_return
|
case u16le: (^u16le) (ptr)^ = cast(u16le) bounded_uint(value, cast(u128)max(u16le) ) or_return
|
||||||
case u32le: (cast(^u32le) ptr)^ = cast(u32le) bounded_uint(value, cast(u128)max(u32le) ) or_return
|
case u32le: (^u32le) (ptr)^ = cast(u32le) bounded_uint(value, cast(u128)max(u32le) ) or_return
|
||||||
case u64le: (cast(^u64le) ptr)^ = cast(u64le) bounded_uint(value, cast(u128)max(u64le) ) or_return
|
case u64le: (^u64le) (ptr)^ = cast(u64le) bounded_uint(value, cast(u128)max(u64le) ) or_return
|
||||||
case u128le: (cast(^u128le) ptr)^ = cast(u128le) bounded_uint(value, cast(u128)max(u128le) ) or_return
|
case u128le: (^u128le) (ptr)^ = cast(u128le) bounded_uint(value, cast(u128)max(u128le) ) or_return
|
||||||
|
|
||||||
case u16be: (cast(^u16be) ptr)^ = cast(u16be) bounded_uint(value, cast(u128)max(u16be) ) or_return
|
case u16be: (^u16be) (ptr)^ = cast(u16be) bounded_uint(value, cast(u128)max(u16be) ) or_return
|
||||||
case u32be: (cast(^u32be) ptr)^ = cast(u32be) bounded_uint(value, cast(u128)max(u32be) ) or_return
|
case u32be: (^u32be) (ptr)^ = cast(u32be) bounded_uint(value, cast(u128)max(u32be) ) or_return
|
||||||
case u64be: (cast(^u64be) ptr)^ = cast(u64be) bounded_uint(value, cast(u128)max(u64be) ) or_return
|
case u64be: (^u64be) (ptr)^ = cast(u64be) bounded_uint(value, cast(u128)max(u64be) ) or_return
|
||||||
case u128be: (cast(^u128be) ptr)^ = cast(u128be) bounded_uint(value, cast(u128)max(u128be) ) or_return
|
case u128be: (^u128be) (ptr)^ = cast(u128be) bounded_uint(value, cast(u128)max(u128be) ) or_return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,60 +92,60 @@ parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info:
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
(cast(^rune)ptr)^ = utf8.rune_at_pos(str, 0)
|
(^rune)(ptr)^ = utf8.rune_at_pos(str, 0)
|
||||||
|
|
||||||
case runtime.Type_Info_Float:
|
case runtime.Type_Info_Float:
|
||||||
value := strconv.parse_f64(str) or_return
|
value := strconv.parse_f64(str) or_return
|
||||||
switch type_info.id {
|
switch type_info.id {
|
||||||
case f16: (cast(^f16) ptr)^ = cast(f16) value
|
case f16: (^f16) (ptr)^ = cast(f16) value
|
||||||
case f32: (cast(^f32) ptr)^ = cast(f32) value
|
case f32: (^f32) (ptr)^ = cast(f32) value
|
||||||
case f64: (cast(^f64) ptr)^ = value
|
case f64: (^f64) (ptr)^ = value
|
||||||
|
|
||||||
case f16le: (cast(^f16le)ptr)^ = cast(f16le) value
|
case f16le: (^f16le)(ptr)^ = cast(f16le) value
|
||||||
case f32le: (cast(^f32le)ptr)^ = cast(f32le) value
|
case f32le: (^f32le)(ptr)^ = cast(f32le) value
|
||||||
case f64le: (cast(^f64le)ptr)^ = cast(f64le) value
|
case f64le: (^f64le)(ptr)^ = cast(f64le) value
|
||||||
|
|
||||||
case f16be: (cast(^f16be)ptr)^ = cast(f16be) value
|
case f16be: (^f16be)(ptr)^ = cast(f16be) value
|
||||||
case f32be: (cast(^f32be)ptr)^ = cast(f32be) value
|
case f32be: (^f32be)(ptr)^ = cast(f32be) value
|
||||||
case f64be: (cast(^f64be)ptr)^ = cast(f64be) value
|
case f64be: (^f64be)(ptr)^ = cast(f64be) value
|
||||||
}
|
}
|
||||||
|
|
||||||
case runtime.Type_Info_Complex:
|
case runtime.Type_Info_Complex:
|
||||||
value := strconv.parse_complex128(str) or_return
|
value := strconv.parse_complex128(str) or_return
|
||||||
switch type_info.id {
|
switch type_info.id {
|
||||||
case complex128: (cast(^complex128)ptr)^ = value
|
case complex32: (^complex32) (ptr)^ = (complex32)(value)
|
||||||
case complex64: (cast(^complex64) ptr)^ = cast(complex64)value
|
case complex64: (^complex64) (ptr)^ = (complex64)(value)
|
||||||
case complex32: (cast(^complex32) ptr)^ = cast(complex32)value
|
case complex128: (^complex128)(ptr)^ = value
|
||||||
}
|
}
|
||||||
|
|
||||||
case runtime.Type_Info_Quaternion:
|
case runtime.Type_Info_Quaternion:
|
||||||
value := strconv.parse_quaternion256(str) or_return
|
value := strconv.parse_quaternion256(str) or_return
|
||||||
switch type_info.id {
|
switch type_info.id {
|
||||||
case quaternion256: (cast(^quaternion256)ptr)^ = value
|
case quaternion64: (^quaternion64) (ptr)^ = (quaternion64)(value)
|
||||||
case quaternion128: (cast(^quaternion128)ptr)^ = cast(quaternion128)value
|
case quaternion128: (^quaternion128)(ptr)^ = (quaternion128)(value)
|
||||||
case quaternion64: (cast(^quaternion64) ptr)^ = cast(quaternion64)value
|
case quaternion256: (^quaternion256)(ptr)^ = value
|
||||||
}
|
}
|
||||||
|
|
||||||
case runtime.Type_Info_String:
|
case runtime.Type_Info_String:
|
||||||
if specific_type_info.is_cstring {
|
if specific_type_info.is_cstring {
|
||||||
cstr_ptr := cast(^cstring)ptr
|
cstr_ptr := (^cstring)(ptr)
|
||||||
if cstr_ptr != nil {
|
if cstr_ptr != nil {
|
||||||
// Prevent memory leaks from us setting this value multiple times.
|
// Prevent memory leaks from us setting this value multiple times.
|
||||||
delete(cstr_ptr^)
|
delete(cstr_ptr^)
|
||||||
}
|
}
|
||||||
cstr_ptr^ = strings.clone_to_cstring(str)
|
cstr_ptr^ = strings.clone_to_cstring(str)
|
||||||
} else {
|
} else {
|
||||||
(cast(^string)ptr)^ = str
|
(^string)(ptr)^ = str
|
||||||
}
|
}
|
||||||
|
|
||||||
case runtime.Type_Info_Boolean:
|
case runtime.Type_Info_Boolean:
|
||||||
value := strconv.parse_bool(str) or_return
|
value := strconv.parse_bool(str) or_return
|
||||||
switch type_info.id {
|
switch type_info.id {
|
||||||
case bool: (cast(^bool) ptr)^ = value
|
case bool: (^bool)(ptr)^ = value
|
||||||
case b8: (cast(^b8) ptr)^ = cast(b8) value
|
case b8: (^b8) (ptr)^ = b8(value)
|
||||||
case b16: (cast(^b16) ptr)^ = cast(b16) value
|
case b16: (^b16) (ptr)^ = b16(value)
|
||||||
case b32: (cast(^b32) ptr)^ = cast(b32) value
|
case b32: (^b32) (ptr)^ = b32(value)
|
||||||
case b64: (cast(^b64) ptr)^ = cast(b64) value
|
case b64: (^b64) (ptr)^ = b64(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
case runtime.Type_Info_Bit_Set:
|
case runtime.Type_Info_Bit_Set:
|
||||||
@@ -154,9 +154,9 @@ parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info:
|
|||||||
value: u128
|
value: u128
|
||||||
|
|
||||||
// NOTE: `upper` is inclusive, i.e: `0..=31`
|
// NOTE: `upper` is inclusive, i.e: `0..=31`
|
||||||
max_bit_index := cast(u128)(1 + specific_type_info.upper - specific_type_info.lower)
|
max_bit_index := u128(1 + specific_type_info.upper - specific_type_info.lower)
|
||||||
bit_index : u128 = 0
|
bit_index := u128(0)
|
||||||
#no_bounds_check for string_index : uint = 0; string_index < len(str); string_index += 1 {
|
#no_bounds_check for string_index in 0..<uint(len(str)) {
|
||||||
if bit_index == max_bit_index {
|
if bit_index == max_bit_index {
|
||||||
// The string's too long for this bit_set.
|
// The string's too long for this bit_set.
|
||||||
return false
|
return false
|
||||||
@@ -180,11 +180,11 @@ parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info:
|
|||||||
set_unbounded_integer_by_type(ptr, value, specific_type_info.underlying.id)
|
set_unbounded_integer_by_type(ptr, value, specific_type_info.underlying.id)
|
||||||
} else {
|
} else {
|
||||||
switch 8*type_info.size {
|
switch 8*type_info.size {
|
||||||
case 8: (cast(^u8) ptr)^ = cast(u8) value
|
case 8: (^u8) (ptr)^ = cast(u8) value
|
||||||
case 16: (cast(^u16) ptr)^ = cast(u16) value
|
case 16: (^u16) (ptr)^ = cast(u16) value
|
||||||
case 32: (cast(^u32) ptr)^ = cast(u32) value
|
case 32: (^u32) (ptr)^ = cast(u32) value
|
||||||
case 64: (cast(^u64) ptr)^ = cast(u64) value
|
case 64: (^u64) (ptr)^ = cast(u64) value
|
||||||
case 128: (cast(^u128) ptr)^ = cast(u128) value
|
case 128: (^u128)(ptr)^ = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,7 +222,7 @@ parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type:
|
|||||||
mode: int
|
mode: int
|
||||||
|
|
||||||
if file, ok := get_struct_subtag(arg_tag, SUBTAG_FILE); ok {
|
if file, ok := get_struct_subtag(arg_tag, SUBTAG_FILE); ok {
|
||||||
for i := 0; i < len(file); i += 1 {
|
for i in 0..<len(file) {
|
||||||
#no_bounds_check switch file[i] {
|
#no_bounds_check switch file[i] {
|
||||||
case 'r': wants_read = true
|
case 'r': wants_read = true
|
||||||
case 'w': wants_write = true
|
case 'w': wants_write = true
|
||||||
@@ -249,7 +249,7 @@ parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type:
|
|||||||
|
|
||||||
if permstr, ok := get_struct_subtag(arg_tag, SUBTAG_PERMS); ok {
|
if permstr, ok := get_struct_subtag(arg_tag, SUBTAG_PERMS); ok {
|
||||||
if value, parse_ok := strconv.parse_u64_of_base(permstr, 8); parse_ok {
|
if value, parse_ok := strconv.parse_u64_of_base(permstr, 8); parse_ok {
|
||||||
perms = cast(int)value
|
perms = int(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,7 +271,7 @@ parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
(cast(^os.Handle)ptr)^ = handle
|
(^os.Handle)(ptr)^ = handle
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,7 +289,7 @@ parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
(cast(^time.Time)ptr)^ = res
|
(^time.Time)(ptr)^ = res
|
||||||
return
|
return
|
||||||
} else if data_type == datetime.DateTime {
|
} else if data_type == datetime.DateTime {
|
||||||
// NOTE: The UTC offset and leap second data are discarded.
|
// NOTE: The UTC offset and leap second data are discarded.
|
||||||
@@ -302,7 +302,7 @@ parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
(cast(^datetime.DateTime)ptr)^ = res
|
(^datetime.DateTime)(ptr)^ = res
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -323,44 +323,44 @@ parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type:
|
|||||||
@(optimization_mode="size")
|
@(optimization_mode="size")
|
||||||
set_unbounded_integer_by_type :: proc(ptr: rawptr, value: $T, data_type: typeid) where intrinsics.type_is_integer(T) {
|
set_unbounded_integer_by_type :: proc(ptr: rawptr, value: $T, data_type: typeid) where intrinsics.type_is_integer(T) {
|
||||||
switch data_type {
|
switch data_type {
|
||||||
case i8: (cast(^i8) ptr)^ = cast(i8) value
|
case i8: (^i8) (ptr)^ = cast(i8) value
|
||||||
case i16: (cast(^i16) ptr)^ = cast(i16) value
|
case i16: (^i16) (ptr)^ = cast(i16) value
|
||||||
case i32: (cast(^i32) ptr)^ = cast(i32) value
|
case i32: (^i32) (ptr)^ = cast(i32) value
|
||||||
case i64: (cast(^i64) ptr)^ = cast(i64) value
|
case i64: (^i64) (ptr)^ = cast(i64) value
|
||||||
case i128: (cast(^i128) ptr)^ = cast(i128) value
|
case i128: (^i128) (ptr)^ = cast(i128) value
|
||||||
|
|
||||||
case int: (cast(^int) ptr)^ = cast(int) value
|
case int: (^int) (ptr)^ = cast(int) value
|
||||||
|
|
||||||
case i16le: (cast(^i16le) ptr)^ = cast(i16le) value
|
case i16le: (^i16le) (ptr)^ = cast(i16le) value
|
||||||
case i32le: (cast(^i32le) ptr)^ = cast(i32le) value
|
case i32le: (^i32le) (ptr)^ = cast(i32le) value
|
||||||
case i64le: (cast(^i64le) ptr)^ = cast(i64le) value
|
case i64le: (^i64le) (ptr)^ = cast(i64le) value
|
||||||
case i128le: (cast(^i128le) ptr)^ = cast(i128le) value
|
case i128le: (^i128le) (ptr)^ = cast(i128le) value
|
||||||
|
|
||||||
case i16be: (cast(^i16be) ptr)^ = cast(i16be) value
|
case i16be: (^i16be) (ptr)^ = cast(i16be) value
|
||||||
case i32be: (cast(^i32be) ptr)^ = cast(i32be) value
|
case i32be: (^i32be) (ptr)^ = cast(i32be) value
|
||||||
case i64be: (cast(^i64be) ptr)^ = cast(i64be) value
|
case i64be: (^i64be) (ptr)^ = cast(i64be) value
|
||||||
case i128be: (cast(^i128be) ptr)^ = cast(i128be) value
|
case i128be: (^i128be) (ptr)^ = cast(i128be) value
|
||||||
|
|
||||||
case u8: (cast(^u8) ptr)^ = cast(u8) value
|
case u8: (^u8) (ptr)^ = cast(u8) value
|
||||||
case u16: (cast(^u16) ptr)^ = cast(u16) value
|
case u16: (^u16) (ptr)^ = cast(u16) value
|
||||||
case u32: (cast(^u32) ptr)^ = cast(u32) value
|
case u32: (^u32) (ptr)^ = cast(u32) value
|
||||||
case u64: (cast(^u64) ptr)^ = cast(u64) value
|
case u64: (^u64) (ptr)^ = cast(u64) value
|
||||||
case u128: (cast(^u128) ptr)^ = cast(u128) value
|
case u128: (^u128) (ptr)^ = cast(u128) value
|
||||||
|
|
||||||
case uint: (cast(^uint) ptr)^ = cast(uint) value
|
case uint: (^uint) (ptr)^ = cast(uint) value
|
||||||
case uintptr: (cast(^uintptr)ptr)^ = cast(uintptr) value
|
case uintptr: (^uintptr)(ptr)^ = cast(uintptr) value
|
||||||
|
|
||||||
case u16le: (cast(^u16le) ptr)^ = cast(u16le) value
|
case u16le: (^u16le) (ptr)^ = cast(u16le) value
|
||||||
case u32le: (cast(^u32le) ptr)^ = cast(u32le) value
|
case u32le: (^u32le) (ptr)^ = cast(u32le) value
|
||||||
case u64le: (cast(^u64le) ptr)^ = cast(u64le) value
|
case u64le: (^u64le) (ptr)^ = cast(u64le) value
|
||||||
case u128le: (cast(^u128le) ptr)^ = cast(u128le) value
|
case u128le: (^u128le) (ptr)^ = cast(u128le) value
|
||||||
|
|
||||||
case u16be: (cast(^u16be) ptr)^ = cast(u16be) value
|
case u16be: (^u16be) (ptr)^ = cast(u16be) value
|
||||||
case u32be: (cast(^u32be) ptr)^ = cast(u32be) value
|
case u32be: (^u32be) (ptr)^ = cast(u32be) value
|
||||||
case u64be: (cast(^u64be) ptr)^ = cast(u64be) value
|
case u64be: (^u64be) (ptr)^ = cast(u64be) value
|
||||||
case u128be: (cast(^u128be) ptr)^ = cast(u128be) value
|
case u128be: (^u128be) (ptr)^ = cast(u128be) value
|
||||||
|
|
||||||
case rune: (cast(^rune) ptr)^ = cast(rune) value
|
case rune: (^rune) (ptr)^ = cast(rune) value
|
||||||
|
|
||||||
case:
|
case:
|
||||||
fmt.panicf("Unsupported integer backing type: %v", data_type)
|
fmt.panicf("Unsupported integer backing type: %v", data_type)
|
||||||
@@ -443,9 +443,9 @@ parse_and_set_pointer_by_type :: proc(ptr: rawptr, str: string, type_info: ^runt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
subptr := cast(rawptr)(
|
subptr := rawptr(
|
||||||
cast(uintptr)ptr.data +
|
uintptr(ptr.data) +
|
||||||
cast(uintptr)((ptr.len - 1) * specific_type_info.elem.size))
|
uintptr((ptr.len - 1) * specific_type_info.elem.size))
|
||||||
mem.copy(subptr, raw_data(elem_backing), len(elem_backing))
|
mem.copy(subptr, raw_data(elem_backing), len(elem_backing))
|
||||||
|
|
||||||
case runtime.Type_Info_Enum:
|
case runtime.Type_Info_Enum:
|
||||||
@@ -490,7 +490,7 @@ get_field_pos :: proc(field: reflect.Struct_Field) -> (int, bool) {
|
|||||||
if args_tag, ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS); ok {
|
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 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 {
|
if value, parse_ok := strconv.parse_u64_of_base(pos_subtag, 10); parse_ok {
|
||||||
return cast(int)value, true
|
return int(value), true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -516,15 +516,8 @@ get_field_by_name :: proc(model: ^$T, name: string) -> (result: reflect.Struct_F
|
|||||||
// Get a struct field by its `pos` subtag.
|
// 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) {
|
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) {
|
for field, i in reflect.struct_fields_zipped(T) {
|
||||||
args_tag, tag_ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS)
|
args_tag := reflect.struct_tag_lookup(field.tag, TAG_ARGS) or_continue
|
||||||
if !tag_ok {
|
pos_subtag := get_struct_subtag(args_tag, SUBTAG_POS) or_continue
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
pos_subtag, pos_ok := get_struct_subtag(args_tag, SUBTAG_POS)
|
|
||||||
if !pos_ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
value, parse_ok := strconv.parse_u64_of_base(pos_subtag, 10)
|
value, parse_ok := strconv.parse_u64_of_base(pos_subtag, 10)
|
||||||
if parse_ok && cast(int)value == pos {
|
if parse_ok && cast(int)value == pos {
|
||||||
|
|||||||
+14
-17
@@ -92,7 +92,7 @@ _user_formatters: ^map[typeid]User_Formatter
|
|||||||
//
|
//
|
||||||
set_user_formatters :: proc(m: ^map[typeid]User_Formatter) {
|
set_user_formatters :: proc(m: ^map[typeid]User_Formatter) {
|
||||||
assert(_user_formatters == nil, "set_user_formatters must not be called more than once.")
|
assert(_user_formatters == nil, "set_user_formatters must not be called more than once.")
|
||||||
_user_formatters = m
|
_user_formatters = m
|
||||||
}
|
}
|
||||||
// Registers a user-defined formatter for a specific typeid
|
// Registers a user-defined formatter for a specific typeid
|
||||||
//
|
//
|
||||||
@@ -1072,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
|
||||||
@@ -1157,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 {
|
||||||
@@ -1229,10 +1229,10 @@ _fmt_memory :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, units: st
|
|||||||
// Add the unit at the end.
|
// Add the unit at the end.
|
||||||
copy(buf[len(str):], units[off:off+unit_len])
|
copy(buf[len(str):], units[off:off+unit_len])
|
||||||
str = string(buf[:len(str)+unit_len])
|
str = string(buf[:len(str)+unit_len])
|
||||||
|
|
||||||
if !fi.plus {
|
if !fi.plus {
|
||||||
// Strip sign from "+<value>" but not "+Inf".
|
// Strip sign from "+<value>" but not "+Inf".
|
||||||
if str[0] == '+' && str[1] != 'I' {
|
if str[0] == '+' && str[1] != 'I' {
|
||||||
str = str[1:]
|
str = str[1:]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1460,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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1768,7 +1765,7 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "", verb: rune = 'v') {
|
|||||||
|
|
||||||
if is_enum {
|
if is_enum {
|
||||||
enum_name: string
|
enum_name: string
|
||||||
if ti_named, is_named := info.elem.variant.(runtime.Type_Info_Named); is_named {
|
if ti_named, is_named := info.elem.variant.(runtime.Type_Info_Named); is_named {
|
||||||
enum_name = ti_named.name
|
enum_name = ti_named.name
|
||||||
}
|
}
|
||||||
for ev, evi in e.values {
|
for ev, evi in e.values {
|
||||||
@@ -2712,7 +2709,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if fi.indirection_level < 1 {
|
if fi.indirection_level < 1 {
|
||||||
fi.indirection_level += 1
|
fi.indirection_level += 1
|
||||||
defer fi.indirection_level -= 1
|
defer fi.indirection_level -= 1
|
||||||
io.write_byte(fi.writer, '&')
|
io.write_byte(fi.writer, '&')
|
||||||
fmt_value(fi, a, verb)
|
fmt_value(fi, a, verb)
|
||||||
@@ -2781,7 +2778,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
|||||||
runtime.Type_Info_Dynamic_Array,
|
runtime.Type_Info_Dynamic_Array,
|
||||||
runtime.Type_Info_Map:
|
runtime.Type_Info_Map:
|
||||||
if fi.indirection_level < 1 {
|
if fi.indirection_level < 1 {
|
||||||
fi.indirection_level += 1
|
fi.indirection_level += 1
|
||||||
defer fi.indirection_level -= 1
|
defer fi.indirection_level -= 1
|
||||||
io.write_byte(fi.writer, '&', &fi.n)
|
io.write_byte(fi.writer, '&', &fi.n)
|
||||||
fmt_value(fi, a, verb)
|
fmt_value(fi, a, verb)
|
||||||
|
|||||||
@@ -897,19 +897,20 @@ XXH3_hashLong_64b_default :: #force_no_inline proc(input: []u8, seed64: xxh_u64,
|
|||||||
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="speed")
|
||||||
XXH3_hashLong_64b_withSeed_internal :: #force_no_inline proc(input: []u8,
|
XXH3_hashLong_64b_withSeed_internal :: #force_no_inline proc(
|
||||||
seed: xxh_u64,
|
input: []u8,
|
||||||
f_acc512: XXH3_accumulate_512_f,
|
seed: xxh_u64,
|
||||||
f_scramble: XXH3_scramble_accumulator_f,
|
f_acc512: XXH3_accumulate_512_f,
|
||||||
f_init_sec: XXH3_init_custom_secret_f) -> (hash: xxh_u64) {
|
f_scramble: XXH3_scramble_accumulator_f,
|
||||||
|
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)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ save_to_buffer :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}
|
|||||||
written := 0
|
written := 0
|
||||||
|
|
||||||
if resize(&output.buf, int(header.size)) != nil {
|
if resize(&output.buf, int(header.size)) != nil {
|
||||||
return .Unable_To_Allocate_Or_Resize
|
return .Unable_To_Allocate_Or_Resize
|
||||||
}
|
}
|
||||||
|
|
||||||
header_bytes := transmute([size_of(image.BMP_Header)]u8)header
|
header_bytes := transmute([size_of(image.BMP_Header)]u8)header
|
||||||
@@ -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}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -735,7 +735,7 @@ destroy :: proc(img: ^Image) {
|
|||||||
|
|
||||||
bytes.buffer_destroy(&img.pixels)
|
bytes.buffer_destroy(&img.pixels)
|
||||||
if v, ok := img.metadata.(^image.BMP_Info); ok {
|
if v, ok := img.metadata.(^image.BMP_Info); ok {
|
||||||
free(v)
|
free(v)
|
||||||
}
|
}
|
||||||
free(img)
|
free(img)
|
||||||
}
|
}
|
||||||
|
|||||||
+41
-41
@@ -1313,55 +1313,55 @@ expand_grayscale :: proc(img: ^Image, allocator := context.allocator) -> (ok: bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch img.depth {
|
switch img.depth {
|
||||||
case 8:
|
case 8:
|
||||||
switch img.channels {
|
switch img.channels {
|
||||||
case 1: // Turn Gray into RGB
|
case 1: // Turn Gray into RGB
|
||||||
out := mem.slice_data_cast([]RGB_Pixel, buf.buf[:])
|
out := mem.slice_data_cast([]RGB_Pixel, buf.buf[:])
|
||||||
|
|
||||||
for p in img.pixels.buf {
|
for p in img.pixels.buf {
|
||||||
out[0] = p // Broadcast gray value into RGB components.
|
out[0] = p // Broadcast gray value into RGB components.
|
||||||
out = out[1:]
|
out = out[1:]
|
||||||
}
|
|
||||||
|
|
||||||
case 2: // Turn Gray + Alpha into RGBA
|
|
||||||
inp := mem.slice_data_cast([]GA_Pixel, img.pixels.buf[:])
|
|
||||||
out := mem.slice_data_cast([]RGBA_Pixel, buf.buf[:])
|
|
||||||
|
|
||||||
for p in inp {
|
|
||||||
out[0].rgb = p.r // Gray component.
|
|
||||||
out[0].a = p.g // Alpha component.
|
|
||||||
}
|
|
||||||
|
|
||||||
case:
|
|
||||||
unreachable()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case 16:
|
case 2: // Turn Gray + Alpha into RGBA
|
||||||
switch img.channels {
|
inp := mem.slice_data_cast([]GA_Pixel, img.pixels.buf[:])
|
||||||
case 1: // Turn Gray into RGB
|
out := mem.slice_data_cast([]RGBA_Pixel, buf.buf[:])
|
||||||
inp := mem.slice_data_cast([]u16, img.pixels.buf[:])
|
|
||||||
out := mem.slice_data_cast([]RGB_Pixel_16, buf.buf[:])
|
|
||||||
|
|
||||||
for p in inp {
|
for p in inp {
|
||||||
out[0] = p // Broadcast gray value into RGB components.
|
out[0].rgb = p.r // Gray component.
|
||||||
out = out[1:]
|
out[0].a = p.g // Alpha component.
|
||||||
}
|
|
||||||
|
|
||||||
case 2: // Turn Gray + Alpha into RGBA
|
|
||||||
inp := mem.slice_data_cast([]GA_Pixel_16, img.pixels.buf[:])
|
|
||||||
out := mem.slice_data_cast([]RGBA_Pixel_16, buf.buf[:])
|
|
||||||
|
|
||||||
for p in inp {
|
|
||||||
out[0].rgb = p.r // Gray component.
|
|
||||||
out[0].a = p.g // Alpha component.
|
|
||||||
}
|
|
||||||
|
|
||||||
case:
|
|
||||||
unreachable()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case:
|
case:
|
||||||
unreachable()
|
unreachable()
|
||||||
|
}
|
||||||
|
|
||||||
|
case 16:
|
||||||
|
switch img.channels {
|
||||||
|
case 1: // Turn Gray into RGB
|
||||||
|
inp := mem.slice_data_cast([]u16, img.pixels.buf[:])
|
||||||
|
out := mem.slice_data_cast([]RGB_Pixel_16, buf.buf[:])
|
||||||
|
|
||||||
|
for p in inp {
|
||||||
|
out[0] = p // Broadcast gray value into RGB components.
|
||||||
|
out = out[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
case 2: // Turn Gray + Alpha into RGBA
|
||||||
|
inp := mem.slice_data_cast([]GA_Pixel_16, img.pixels.buf[:])
|
||||||
|
out := mem.slice_data_cast([]RGBA_Pixel_16, buf.buf[:])
|
||||||
|
|
||||||
|
for p in inp {
|
||||||
|
out[0].rgb = p.r // Gray component.
|
||||||
|
out[0].a = p.g // Alpha component.
|
||||||
|
}
|
||||||
|
|
||||||
|
case:
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
|
||||||
|
case:
|
||||||
|
unreachable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+24
-24
@@ -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 {
|
||||||
@@ -535,28 +535,28 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
|||||||
|
|
||||||
ct := transmute(u8)info.header.color_type
|
ct := transmute(u8)info.header.color_type
|
||||||
switch ct {
|
switch ct {
|
||||||
case 3: // Indexed color
|
case 3: // Indexed color
|
||||||
if c.header.length != 1 {
|
if c.header.length != 1 {
|
||||||
return {}, .BKGD_Invalid_Length
|
return {}, .BKGD_Invalid_Length
|
||||||
}
|
}
|
||||||
col := _plte.entries[c.data[0]]
|
col := _plte.entries[c.data[0]]
|
||||||
img.background = [3]u16{
|
img.background = [3]u16{
|
||||||
u16(col[0]) << 8 | u16(col[0]),
|
u16(col[0]) << 8 | u16(col[0]),
|
||||||
u16(col[1]) << 8 | u16(col[1]),
|
u16(col[1]) << 8 | u16(col[1]),
|
||||||
u16(col[2]) << 8 | u16(col[2]),
|
u16(col[2]) << 8 | u16(col[2]),
|
||||||
}
|
}
|
||||||
case 0, 4: // Grayscale, with and without Alpha
|
case 0, 4: // Grayscale, with and without Alpha
|
||||||
if c.header.length != 2 {
|
if c.header.length != 2 {
|
||||||
return {}, .BKGD_Invalid_Length
|
return {}, .BKGD_Invalid_Length
|
||||||
}
|
}
|
||||||
col := u16(mem.slice_data_cast([]u16be, c.data[:])[0])
|
col := u16(mem.slice_data_cast([]u16be, c.data[:])[0])
|
||||||
img.background = [3]u16{col, col, col}
|
img.background = [3]u16{col, col, col}
|
||||||
case 2, 6: // Color, with and without Alpha
|
case 2, 6: // Color, with and without Alpha
|
||||||
if c.header.length != 6 {
|
if c.header.length != 6 {
|
||||||
return {}, .BKGD_Invalid_Length
|
return {}, .BKGD_Invalid_Length
|
||||||
}
|
}
|
||||||
col := mem.slice_data_cast([]u16be, c.data[:])
|
col := mem.slice_data_cast([]u16be, c.data[:])
|
||||||
img.background = [3]u16{u16(col[0]), u16(col[1]), u16(col[2])}
|
img.background = [3]u16{u16(col[0]), u16(col[1]), u16(col[2])}
|
||||||
}
|
}
|
||||||
|
|
||||||
case .tRNS:
|
case .tRNS:
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -178,7 +176,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}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,7 +230,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
|||||||
bytes_needed := image.compute_buffer_size(int(header.width), int(header.height), img.channels, 8)
|
bytes_needed := image.compute_buffer_size(int(header.width), int(header.height), img.channels, 8)
|
||||||
|
|
||||||
if resize(&img.pixels.buf, bytes_needed) != nil {
|
if resize(&img.pixels.buf, bytes_needed) != nil {
|
||||||
return img, .Unable_To_Allocate_Or_Resize
|
return img, .Unable_To_Allocate_Or_Resize
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -343,7 +341,7 @@ destroy :: proc(img: ^Image) {
|
|||||||
bytes.buffer_destroy(&img.pixels)
|
bytes.buffer_destroy(&img.pixels)
|
||||||
|
|
||||||
if v, ok := img.metadata.(^image.QOI_Info); ok {
|
if v, ok := img.metadata.(^image.QOI_Info); ok {
|
||||||
free(v)
|
free(v)
|
||||||
}
|
}
|
||||||
free(img)
|
free(img)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+24
-24
@@ -787,8 +787,8 @@ _private_int_sqr_comba :: proc(dest, src: ^Int, allocator := context.allocator)
|
|||||||
/*
|
/*
|
||||||
Karatsuba squaring, computes `dest` = `src` * `src` using three half-size squarings.
|
Karatsuba squaring, computes `dest` = `src` * `src` using three half-size squarings.
|
||||||
|
|
||||||
See comments of `_private_int_mul_karatsuba` for details.
|
See comments of `_private_int_mul_karatsuba` for details.
|
||||||
It is essentially the same algorithm but merely tuned to perform recursive squarings.
|
It is essentially the same algorithm but merely tuned to perform recursive squarings.
|
||||||
*/
|
*/
|
||||||
_private_int_sqr_karatsuba :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) {
|
_private_int_sqr_karatsuba :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) {
|
||||||
context.allocator = allocator
|
context.allocator = allocator
|
||||||
@@ -967,7 +967,7 @@ _private_int_div_3 :: proc(quotient, numerator: ^Int, allocator := context.alloc
|
|||||||
/*
|
/*
|
||||||
b = 2^_DIGIT_BITS / 3
|
b = 2^_DIGIT_BITS / 3
|
||||||
*/
|
*/
|
||||||
b := _WORD(1) << _WORD(_DIGIT_BITS) / _WORD(3)
|
b := _WORD(1) << _WORD(_DIGIT_BITS) / _WORD(3)
|
||||||
|
|
||||||
q := &Int{}
|
q := &Int{}
|
||||||
internal_grow(q, numerator.used) or_return
|
internal_grow(q, numerator.used) or_return
|
||||||
@@ -975,7 +975,7 @@ _private_int_div_3 :: proc(quotient, numerator: ^Int, allocator := context.alloc
|
|||||||
q.sign = numerator.sign
|
q.sign = numerator.sign
|
||||||
|
|
||||||
w, t: _WORD
|
w, t: _WORD
|
||||||
#no_bounds_check for ix := numerator.used; ix >= 0; ix -= 1 {
|
#no_bounds_check for ix := numerator.used - 1; ix >= 0; ix -= 1 {
|
||||||
w = (w << _WORD(_DIGIT_BITS)) | _WORD(numerator.digit[ix])
|
w = (w << _WORD(_DIGIT_BITS)) | _WORD(numerator.digit[ix])
|
||||||
if w >= 3 {
|
if w >= 3 {
|
||||||
/*
|
/*
|
||||||
@@ -1007,8 +1007,8 @@ _private_int_div_3 :: proc(quotient, numerator: ^Int, allocator := context.alloc
|
|||||||
*/
|
*/
|
||||||
if quotient != nil {
|
if quotient != nil {
|
||||||
err = clamp(q)
|
err = clamp(q)
|
||||||
internal_swap(q, quotient)
|
internal_swap(q, quotient)
|
||||||
}
|
}
|
||||||
internal_destroy(q)
|
internal_destroy(q)
|
||||||
return remainder, nil
|
return remainder, nil
|
||||||
}
|
}
|
||||||
@@ -1555,24 +1555,24 @@ _private_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int, allocator := context.
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
If neither `a` or `b` was zero, we need to compute `gcd`.
|
If neither `a` or `b` was zero, we need to compute `gcd`.
|
||||||
Get copies of `a` and `b` we can modify.
|
Get copies of `a` and `b` we can modify.
|
||||||
*/
|
*/
|
||||||
u, v := &Int{}, &Int{}
|
u, v := &Int{}, &Int{}
|
||||||
defer internal_destroy(u, v)
|
defer internal_destroy(u, v)
|
||||||
internal_copy(u, a) or_return
|
internal_copy(u, a) or_return
|
||||||
internal_copy(v, b) or_return
|
internal_copy(v, b) or_return
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Must be positive for the remainder of the algorithm.
|
Must be positive for the remainder of the algorithm.
|
||||||
*/
|
*/
|
||||||
u.sign = .Zero_or_Positive; v.sign = .Zero_or_Positive
|
u.sign = .Zero_or_Positive; v.sign = .Zero_or_Positive
|
||||||
|
|
||||||
/*
|
/*
|
||||||
B1. Find the common power of two for `u` and `v`.
|
B1. Find the common power of two for `u` and `v`.
|
||||||
*/
|
*/
|
||||||
u_lsb, _ := internal_count_lsb(u)
|
u_lsb, _ := internal_count_lsb(u)
|
||||||
v_lsb, _ := internal_count_lsb(v)
|
v_lsb, _ := internal_count_lsb(v)
|
||||||
k := min(u_lsb, v_lsb)
|
k := min(u_lsb, v_lsb)
|
||||||
|
|
||||||
if k > 0 {
|
if k > 0 {
|
||||||
/*
|
/*
|
||||||
@@ -1615,11 +1615,11 @@ _private_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int, allocator := context.
|
|||||||
internal_shr(v, v, b) or_return
|
internal_shr(v, v, b) or_return
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Multiply by 2**k which we divided out at the beginning.
|
Multiply by 2**k which we divided out at the beginning.
|
||||||
*/
|
*/
|
||||||
internal_shl(temp_gcd_res, u, k) or_return
|
internal_shl(temp_gcd_res, u, k) or_return
|
||||||
temp_gcd_res.sign = .Zero_or_Positive
|
temp_gcd_res.sign = .Zero_or_Positive
|
||||||
|
|
||||||
/*
|
/*
|
||||||
We've computed `gcd`, either the long way, or because one of the inputs was zero.
|
We've computed `gcd`, either the long way, or because one of the inputs was zero.
|
||||||
@@ -1786,8 +1786,8 @@ _private_montgomery_reduce_comba :: proc(x, n: ^Int, rho: DIGIT, allocator := co
|
|||||||
`a = a + mu * m * b**i`
|
`a = a + mu * m * b**i`
|
||||||
|
|
||||||
This is computed in place and on the fly. The multiplication
|
This is computed in place and on the fly. The multiplication
|
||||||
by b**i is handled by offseting which columns the results
|
by b**i is handled by offseting which columns the results
|
||||||
are added to.
|
are added to.
|
||||||
|
|
||||||
Note the comba method normally doesn't handle carries in the
|
Note the comba method normally doesn't handle carries in the
|
||||||
inner loop In this case we fix the carry from the previous
|
inner loop In this case we fix the carry from the previous
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package linalg
|
|||||||
import "core:math"
|
import "core:math"
|
||||||
import "base:builtin"
|
import "base:builtin"
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
import "base:runtime"
|
@require import "base:runtime"
|
||||||
|
|
||||||
// Generic
|
// Generic
|
||||||
|
|
||||||
|
|||||||
@@ -637,22 +637,20 @@ _internal_noise_4d_unskewed_base :: proc(seed: i64, coord: Vec4) -> (value: f32)
|
|||||||
|
|
||||||
// Next point is the closest vertex on the 4-simplex whose base vertex is the aforementioned vertex.
|
// Next point is the closest vertex on the 4-simplex whose base vertex is the aforementioned vertex.
|
||||||
score := 1.0 + ssi * (-1.0 / UNSKEW_4D) // Seems slightly faster than 1.0-xsi-ysi-zsi-wsi
|
score := 1.0 + ssi * (-1.0 / UNSKEW_4D) // Seems slightly faster than 1.0-xsi-ysi-zsi-wsi
|
||||||
if si.x >= si.x && si.x >= si.z && si.x >= si.w && si.x >= score {
|
switch {
|
||||||
|
case si.x >= si.x && si.x >= si.z && si.x >= si.w && si.x >= score:
|
||||||
svp.x += PRIME_X
|
svp.x += PRIME_X
|
||||||
si.x -= 1
|
si.x -= 1
|
||||||
ssi -= UNSKEW_4D
|
ssi -= UNSKEW_4D
|
||||||
}
|
case si.y > si.x && si.y >= si.z && si.y >= si.w && si.y >= score:
|
||||||
else if si.y > si.x && si.y >= si.z && si.y >= si.w && si.y >= score {
|
|
||||||
svp.y += PRIME_Y
|
svp.y += PRIME_Y
|
||||||
si.y -= 1
|
si.y -= 1
|
||||||
ssi -= UNSKEW_4D
|
ssi -= UNSKEW_4D
|
||||||
}
|
case si.z > si.x && si.z > si.y && si.z >= si.w && si.z >= score:
|
||||||
else if si.z > si.x && si.z > si.y && si.z >= si.w && si.z >= score {
|
|
||||||
svp.z += PRIME_Z
|
svp.z += PRIME_Z
|
||||||
si.z -= 1
|
si.z -= 1
|
||||||
ssi -= UNSKEW_4D
|
ssi -= UNSKEW_4D
|
||||||
}
|
case si.w > si.x && si.w > si.y && si.w > si.z && si.w >= score:
|
||||||
else if si.w > si.x && si.w > si.y && si.w > si.z && si.w >= score {
|
|
||||||
svp.w += PRIME_W
|
svp.w += PRIME_W
|
||||||
si.w -= 1
|
si.w -= 1
|
||||||
ssi -= UNSKEW_4D
|
ssi -= UNSKEW_4D
|
||||||
|
|||||||
@@ -655,7 +655,7 @@ choice_enum :: proc($T: typeid) -> T
|
|||||||
where
|
where
|
||||||
intrinsics.type_is_enum(T),
|
intrinsics.type_is_enum(T),
|
||||||
size_of(T) <= 8,
|
size_of(T) <= 8,
|
||||||
len(T) == cap(T) /* Only allow contiguous enum types */
|
len(T) == cap(T) /* Only allow contiguous enum types */ \
|
||||||
{
|
{
|
||||||
when intrinsics.type_is_unsigned(intrinsics.type_core_type(T)) &&
|
when intrinsics.type_is_unsigned(intrinsics.type_core_type(T)) &&
|
||||||
u64(max(T)) > u64(max(i64)) {
|
u64(max(T)) > u64(max(i64)) {
|
||||||
|
|||||||
@@ -97,6 +97,10 @@ init :: proc{init_from_buffer, init_from_allocator}
|
|||||||
destroy :: proc(control: ^Allocator) {
|
destroy :: proc(control: ^Allocator) {
|
||||||
if control == nil { return }
|
if control == nil { return }
|
||||||
|
|
||||||
|
if control.pool.allocator.procedure != nil {
|
||||||
|
runtime.delete(control.pool.data, control.pool.allocator)
|
||||||
|
}
|
||||||
|
|
||||||
// No need to call `pool_remove` or anything, as they're they're embedded in the backing memory.
|
// No need to call `pool_remove` or anything, as they're they're embedded in the backing memory.
|
||||||
// We do however need to free the `Pool` tracking entities and the backing memory itself.
|
// We do however need to free the `Pool` tracking entities and the backing memory itself.
|
||||||
// As `Allocator` is embedded in the first backing slice, the `control` pointer will be
|
// As `Allocator` is embedded in the first backing slice, the `control` pointer will be
|
||||||
@@ -132,8 +136,9 @@ allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
||||||
case .Free_All:
|
case .Free_All:
|
||||||
clear(control)
|
// NOTE: this doesn't work right at the moment, Jeroen has it on his to-do list :)
|
||||||
return nil, nil
|
// clear(control)
|
||||||
|
return nil, .Mode_Not_Implemented
|
||||||
|
|
||||||
case .Resize:
|
case .Resize:
|
||||||
return resize(control, old_memory, uint(old_size), uint(size), uint(alignment))
|
return resize(control, old_memory, uint(old_size), uint(size), uint(alignment))
|
||||||
@@ -144,7 +149,7 @@ allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
|
|||||||
case .Query_Features:
|
case .Query_Features:
|
||||||
set := (^runtime.Allocator_Mode_Set)(old_memory)
|
set := (^runtime.Allocator_Mode_Set)(old_memory)
|
||||||
if set != nil {
|
if set != nil {
|
||||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Resize_Non_Zeroed, .Query_Features}
|
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, /* .Free_All, */ .Resize, .Resize_Non_Zeroed, .Query_Features}
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
||||||
@@ -153,4 +158,4 @@ allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
|
|||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ block_next :: proc(block: ^Block_Header) -> (next: ^Block_Header) {
|
|||||||
block_link_next :: proc(block: ^Block_Header) -> (next: ^Block_Header) {
|
block_link_next :: proc(block: ^Block_Header) -> (next: ^Block_Header) {
|
||||||
next = block_next(block)
|
next = block_next(block)
|
||||||
next.prev_phys_block = block
|
next.prev_phys_block = block
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
block_mark_as_free :: proc(block: ^Block_Header) {
|
block_mark_as_free :: proc(block: ^Block_Header) {
|
||||||
@@ -630,7 +630,7 @@ alloc_bytes_non_zeroed :: proc(control: ^Allocator, size: uint, align: uint) ->
|
|||||||
@(require_results)
|
@(require_results)
|
||||||
alloc_bytes :: proc(control: ^Allocator, size: uint, align: uint) -> (res: []byte, err: runtime.Allocator_Error) {
|
alloc_bytes :: proc(control: ^Allocator, size: uint, align: uint) -> (res: []byte, err: runtime.Allocator_Error) {
|
||||||
res, err = alloc_bytes_non_zeroed(control, size, align)
|
res, err = alloc_bytes_non_zeroed(control, size, align)
|
||||||
if err != nil {
|
if err == nil {
|
||||||
intrinsics.mem_zero(raw_data(res), len(res))
|
intrinsics.mem_zero(raw_data(res), len(res))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -735,4 +735,4 @@ resize_non_zeroed :: proc(control: ^Allocator, ptr: rawptr, old_size, new_size:
|
|||||||
block_trim_used(control, block, adjust)
|
block_trim_used(control, block, adjust)
|
||||||
res = ([^]byte)(ptr)[:new_size]
|
res = ([^]byte)(ptr)[:new_size]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,9 +36,9 @@ _release :: proc "contextless" (data: rawptr, size: uint) {
|
|||||||
_protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
|
_protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
|
||||||
pflags: linux.Mem_Protection
|
pflags: linux.Mem_Protection
|
||||||
pflags = {}
|
pflags = {}
|
||||||
if .Read in flags { pflags |= {.READ} }
|
if .Read in flags { pflags += {.READ} }
|
||||||
if .Write in flags { pflags |= {.WRITE} }
|
if .Write in flags { pflags += {.WRITE} }
|
||||||
if .Execute in flags { pflags |= {.EXEC} }
|
if .Execute in flags { pflags += {.EXEC} }
|
||||||
errno := linux.mprotect(data, size, pflags)
|
errno := linux.mprotect(data, size, pflags)
|
||||||
return errno == .NONE
|
return errno == .NONE
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-4
@@ -370,7 +370,7 @@ parse_ip6_address :: proc(address_and_maybe_port: string) -> (addr: IP6_Address,
|
|||||||
val |= u16(ipv4[3])
|
val |= u16(ipv4[3])
|
||||||
piece_values[7] = u16be(val)
|
piece_values[7] = u16be(val)
|
||||||
}
|
}
|
||||||
return transmute(IP6_Address)piece_values, true
|
return IP6_Address(piece_values), true
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -522,10 +522,9 @@ address_to_string :: proc(addr: Address, allocator := context.temp_allocator) ->
|
|||||||
run := Zero_Run{-1, -1}
|
run := Zero_Run{-1, -1}
|
||||||
best := Zero_Run{-1, -1}
|
best := Zero_Run{-1, -1}
|
||||||
|
|
||||||
addr := transmute([8]u16be)v
|
|
||||||
|
|
||||||
last := u16be(1)
|
last := u16be(1)
|
||||||
for val, i in addr {
|
for val, i in v {
|
||||||
/*
|
/*
|
||||||
If we encounter adjacent zeroes, then start a new run if not already in one.
|
If we encounter adjacent zeroes, then start a new run if not already in one.
|
||||||
Also remember the rightmost index regardless, because it'll be the new
|
Also remember the rightmost index regardless, because it'll be the new
|
||||||
@@ -559,7 +558,7 @@ address_to_string :: proc(addr: Address, allocator := context.temp_allocator) ->
|
|||||||
last = val
|
last = val
|
||||||
}
|
}
|
||||||
|
|
||||||
for val, i in addr {
|
for val, i in v {
|
||||||
if best.start == i || best.end == i {
|
if best.start == i || best.end == i {
|
||||||
// For the left and right side of the best zero run, print a `:`.
|
// For the left and right side of the best zero run, print a `:`.
|
||||||
fmt.sbprint(&b, ":")
|
fmt.sbprint(&b, ":")
|
||||||
|
|||||||
@@ -135,6 +135,7 @@ TCP_Send_Error :: enum c.int {
|
|||||||
No_Buffer_Space_Available = win.WSAENOBUFS,
|
No_Buffer_Space_Available = win.WSAENOBUFS,
|
||||||
Network_Subsystem_Failure = win.WSAENETDOWN,
|
Network_Subsystem_Failure = win.WSAENETDOWN,
|
||||||
Host_Unreachable = win.WSAEHOSTUNREACH,
|
Host_Unreachable = win.WSAEHOSTUNREACH,
|
||||||
|
Would_Block = win.WSAEWOULDBLOCK,
|
||||||
|
|
||||||
// TODO: verify possible, as not mentioned in docs
|
// TODO: verify possible, as not mentioned in docs
|
||||||
Offline = win.WSAENETUNREACH,
|
Offline = win.WSAENETUNREACH,
|
||||||
|
|||||||
@@ -59,24 +59,21 @@ _enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []
|
|||||||
switch int(ifaddr.address.family) {
|
switch int(ifaddr.address.family) {
|
||||||
case os.AF_INET, os.AF_INET6:
|
case os.AF_INET, os.AF_INET6:
|
||||||
address = _sockaddr_basic_to_endpoint(ifaddr.address).address
|
address = _sockaddr_basic_to_endpoint(ifaddr.address).address
|
||||||
case:
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ifaddr.netmask != nil {
|
if ifaddr.netmask != nil {
|
||||||
switch int(ifaddr.netmask.family) {
|
switch int(ifaddr.netmask.family) {
|
||||||
case os.AF_INET, os.AF_INET6:
|
case os.AF_INET, os.AF_INET6:
|
||||||
netmask = Netmask(_sockaddr_basic_to_endpoint(ifaddr.netmask).address)
|
netmask = Netmask(_sockaddr_basic_to_endpoint(ifaddr.netmask).address)
|
||||||
case:
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ifaddr.broadcast_or_dest != nil && .BROADCAST in ifaddr.flags {
|
if ifaddr.broadcast_or_dest != nil && .BROADCAST in ifaddr.flags {
|
||||||
switch int(ifaddr.broadcast_or_dest.family) {
|
switch int(ifaddr.broadcast_or_dest.family) {
|
||||||
case os.AF_INET, os.AF_INET6:
|
case os.AF_INET, os.AF_INET6:
|
||||||
broadcast := _sockaddr_basic_to_endpoint(ifaddr.broadcast_or_dest).address
|
broadcast := _sockaddr_basic_to_endpoint(ifaddr.broadcast_or_dest).address
|
||||||
append(&iface.multicast, broadcast)
|
append(&iface.multicast, broadcast)
|
||||||
case:
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,19 +88,19 @@ _enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []
|
|||||||
/*
|
/*
|
||||||
TODO: Refine this based on the type of adapter.
|
TODO: Refine this based on the type of adapter.
|
||||||
*/
|
*/
|
||||||
state := Link_State{}
|
state := Link_State{}
|
||||||
|
|
||||||
if .UP in ifaddr.flags {
|
if .UP in ifaddr.flags {
|
||||||
state |= {.Up}
|
state += {.Up}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*if .DORMANT in ifaddr.flags {
|
/*if .DORMANT in ifaddr.flags {
|
||||||
state |= {.Dormant}
|
state |= {.Dormant}
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
if .LOOPBACK in ifaddr.flags {
|
if .LOOPBACK in ifaddr.flags {
|
||||||
state |= {.Loopback}
|
state += {.Loopback}
|
||||||
}
|
}
|
||||||
iface.link.state = state
|
iface.link.state = state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,42 +24,42 @@ import strings "core:strings"
|
|||||||
_enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Network_Error) {
|
_enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Network_Error) {
|
||||||
context.allocator = allocator
|
context.allocator = allocator
|
||||||
|
|
||||||
buf: []u8
|
buf: []u8
|
||||||
defer delete(buf)
|
defer delete(buf)
|
||||||
|
|
||||||
buf_size: u32
|
buf_size: u32
|
||||||
res: u32
|
res: u32
|
||||||
|
|
||||||
gaa: for _ in 1..=MAX_INTERFACE_ENUMERATION_TRIES {
|
gaa: for _ in 1..=MAX_INTERFACE_ENUMERATION_TRIES {
|
||||||
res = sys.get_adapters_addresses(
|
res = sys.get_adapters_addresses(
|
||||||
.Unspecified, // Return both IPv4 and IPv6 adapters.
|
.Unspecified, // Return both IPv4 and IPv6 adapters.
|
||||||
sys.GAA_Flags{
|
sys.GAA_Flags{
|
||||||
.Include_Prefix, // (XP SP1+) Return a list of IP address prefixes on this adapter. When this flag is set, IP address prefixes are returned for both IPv6 and IPv4 addresses.
|
.Include_Prefix, // (XP SP1+) Return a list of IP address prefixes on this adapter. When this flag is set, IP address prefixes are returned for both IPv6 and IPv4 addresses.
|
||||||
.Include_Gateways, // (Vista+) Return the addresses of default gateways.
|
.Include_Gateways, // (Vista+) Return the addresses of default gateways.
|
||||||
.Include_Tunnel_Binding_Order, // (Vista+) Return the adapter addresses sorted in tunnel binding order.
|
.Include_Tunnel_Binding_Order, // (Vista+) Return the adapter addresses sorted in tunnel binding order.
|
||||||
},
|
},
|
||||||
nil, // Reserved
|
nil, // Reserved
|
||||||
(^sys.IP_Adapter_Addresses)(raw_data(buf)),
|
(^sys.IP_Adapter_Addresses)(raw_data(buf)),
|
||||||
&buf_size,
|
&buf_size,
|
||||||
)
|
)
|
||||||
|
|
||||||
switch res {
|
switch res {
|
||||||
case 111: // ERROR_BUFFER_OVERFLOW:
|
case 111: // ERROR_BUFFER_OVERFLOW:
|
||||||
delete(buf)
|
delete(buf)
|
||||||
buf = make([]u8, buf_size)
|
buf = make([]u8, buf_size)
|
||||||
case 0:
|
case 0:
|
||||||
break gaa
|
break gaa
|
||||||
case:
|
case:
|
||||||
return {}, Platform_Error(res)
|
return {}, Platform_Error(res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if res != 0 {
|
if res != 0 {
|
||||||
return {}, .Unable_To_Enumerate_Network_Interfaces
|
return {}, .Unable_To_Enumerate_Network_Interfaces
|
||||||
}
|
}
|
||||||
|
|
||||||
_interfaces := make([dynamic]Network_Interface, 0, allocator)
|
_interfaces := make([dynamic]Network_Interface, 0, allocator)
|
||||||
for adapter := (^sys.IP_Adapter_Addresses)(raw_data(buf)); adapter != nil; adapter = adapter.Next {
|
for adapter := (^sys.IP_Adapter_Addresses)(raw_data(buf)); adapter != nil; adapter = adapter.Next {
|
||||||
friendly_name, err1 := sys.wstring_to_utf8(sys.wstring(adapter.FriendlyName), 256, allocator)
|
friendly_name, err1 := sys.wstring_to_utf8(sys.wstring(adapter.FriendlyName), 256, allocator)
|
||||||
if err1 != nil { return {}, Platform_Error(err1) }
|
if err1 != nil { return {}, Platform_Error(err1) }
|
||||||
|
|
||||||
@@ -71,74 +71,74 @@ _enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []
|
|||||||
|
|
||||||
interface := Network_Interface{
|
interface := Network_Interface{
|
||||||
adapter_name = strings.clone(string(adapter.AdapterName)),
|
adapter_name = strings.clone(string(adapter.AdapterName)),
|
||||||
friendly_name = friendly_name,
|
friendly_name = friendly_name,
|
||||||
description = description,
|
description = description,
|
||||||
dns_suffix = dns_suffix,
|
dns_suffix = dns_suffix,
|
||||||
|
|
||||||
mtu = adapter.MTU,
|
mtu = adapter.MTU,
|
||||||
|
|
||||||
link = {
|
link = {
|
||||||
transmit_speed = adapter.TransmitLinkSpeed,
|
transmit_speed = adapter.TransmitLinkSpeed,
|
||||||
receive_speed = adapter.ReceiveLinkSpeed,
|
receive_speed = adapter.ReceiveLinkSpeed,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if adapter.PhysicalAddressLength > 0 && adapter.PhysicalAddressLength <= len(adapter.PhysicalAddress) {
|
if adapter.PhysicalAddressLength > 0 && adapter.PhysicalAddressLength <= len(adapter.PhysicalAddress) {
|
||||||
interface.physical_address = physical_address_to_string(adapter.PhysicalAddress[:adapter.PhysicalAddressLength])
|
interface.physical_address = physical_address_to_string(adapter.PhysicalAddress[:adapter.PhysicalAddressLength])
|
||||||
}
|
}
|
||||||
|
|
||||||
for u_addr := (^sys.IP_ADAPTER_UNICAST_ADDRESS_LH)(adapter.FirstUnicastAddress); u_addr != nil; u_addr = u_addr.Next {
|
for u_addr := (^sys.IP_ADAPTER_UNICAST_ADDRESS_LH)(adapter.FirstUnicastAddress); u_addr != nil; u_addr = u_addr.Next {
|
||||||
win_addr := parse_socket_address(u_addr.Address)
|
win_addr := parse_socket_address(u_addr.Address)
|
||||||
|
|
||||||
lease := Lease{
|
lease := Lease{
|
||||||
address = win_addr.address,
|
address = win_addr.address,
|
||||||
origin = {
|
origin = {
|
||||||
prefix = Prefix_Origin(u_addr.PrefixOrigin),
|
prefix = Prefix_Origin(u_addr.PrefixOrigin),
|
||||||
suffix = Suffix_Origin(u_addr.SuffixOrigin),
|
suffix = Suffix_Origin(u_addr.SuffixOrigin),
|
||||||
},
|
},
|
||||||
lifetime = {
|
lifetime = {
|
||||||
valid = u_addr.ValidLifetime,
|
valid = u_addr.ValidLifetime,
|
||||||
preferred = u_addr.PreferredLifetime,
|
preferred = u_addr.PreferredLifetime,
|
||||||
lease = u_addr.LeaseLifetime,
|
lease = u_addr.LeaseLifetime,
|
||||||
},
|
},
|
||||||
address_duplication = Address_Duplication(u_addr.DadState),
|
address_duplication = Address_Duplication(u_addr.DadState),
|
||||||
}
|
}
|
||||||
append(&interface.unicast, lease)
|
append(&interface.unicast, lease)
|
||||||
}
|
}
|
||||||
|
|
||||||
for a_addr := (^sys.IP_ADAPTER_ANYCAST_ADDRESS_XP)(adapter.FirstAnycastAddress); a_addr != nil; a_addr = a_addr.Next {
|
for a_addr := (^sys.IP_ADAPTER_ANYCAST_ADDRESS_XP)(adapter.FirstAnycastAddress); a_addr != nil; a_addr = a_addr.Next {
|
||||||
addr := parse_socket_address(a_addr.Address)
|
addr := parse_socket_address(a_addr.Address)
|
||||||
append(&interface.anycast, addr.address)
|
append(&interface.anycast, addr.address)
|
||||||
}
|
}
|
||||||
|
|
||||||
for m_addr := (^sys.IP_ADAPTER_MULTICAST_ADDRESS_XP)(adapter.FirstMulticastAddress); m_addr != nil; m_addr = m_addr.Next {
|
for m_addr := (^sys.IP_ADAPTER_MULTICAST_ADDRESS_XP)(adapter.FirstMulticastAddress); m_addr != nil; m_addr = m_addr.Next {
|
||||||
addr := parse_socket_address(m_addr.Address)
|
addr := parse_socket_address(m_addr.Address)
|
||||||
append(&interface.multicast, addr.address)
|
append(&interface.multicast, addr.address)
|
||||||
}
|
}
|
||||||
|
|
||||||
for g_addr := (^sys.IP_ADAPTER_GATEWAY_ADDRESS_LH)(adapter.FirstGatewayAddress); g_addr != nil; g_addr = g_addr.Next {
|
for g_addr := (^sys.IP_ADAPTER_GATEWAY_ADDRESS_LH)(adapter.FirstGatewayAddress); g_addr != nil; g_addr = g_addr.Next {
|
||||||
addr := parse_socket_address(g_addr.Address)
|
addr := parse_socket_address(g_addr.Address)
|
||||||
append(&interface.gateways, addr.address)
|
append(&interface.gateways, addr.address)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface.dhcp_v4 = parse_socket_address(adapter.Dhcpv4Server).address
|
interface.dhcp_v4 = parse_socket_address(adapter.Dhcpv4Server).address
|
||||||
interface.dhcp_v6 = parse_socket_address(adapter.Dhcpv6Server).address
|
interface.dhcp_v6 = parse_socket_address(adapter.Dhcpv6Server).address
|
||||||
|
|
||||||
switch adapter.OperStatus {
|
switch adapter.OperStatus {
|
||||||
case .Up: interface.link.state = {.Up}
|
case .Up: interface.link.state = {.Up}
|
||||||
case .Down: interface.link.state = {.Down}
|
case .Down: interface.link.state = {.Down}
|
||||||
case .Testing: interface.link.state = {.Testing}
|
case .Testing: interface.link.state = {.Testing}
|
||||||
case .Dormant: interface.link.state = {.Dormant}
|
case .Dormant: interface.link.state = {.Dormant}
|
||||||
case .NotPresent: interface.link.state = {.Not_Present}
|
case .NotPresent: interface.link.state = {.Not_Present}
|
||||||
case .LowerLayerDown: interface.link.state = {.Lower_Layer_Down}
|
case .LowerLayerDown: interface.link.state = {.Lower_Layer_Down}
|
||||||
case .Unknown: fallthrough
|
case .Unknown: fallthrough
|
||||||
case: interface.link.state = {}
|
case: interface.link.state = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface.tunnel_type = Tunnel_Type(adapter.TunnelType)
|
interface.tunnel_type = Tunnel_Type(adapter.TunnelType)
|
||||||
|
|
||||||
append(&_interfaces, interface)
|
append(&_interfaces, interface)
|
||||||
}
|
}
|
||||||
|
|
||||||
return _interfaces[:], {}
|
return _interfaces[:], {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -274,8 +274,7 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca
|
|||||||
.Linger,
|
.Linger,
|
||||||
.Send_Timeout,
|
.Send_Timeout,
|
||||||
.Receive_Timeout:
|
.Receive_Timeout:
|
||||||
t, ok := value.(time.Duration)
|
t := value.(time.Duration) or_else panic("set_option() value must be a time.Duration here", loc)
|
||||||
if !ok do panic("set_option() value must be a time.Duration here", loc)
|
|
||||||
|
|
||||||
micros := i64(time.duration_microseconds(t))
|
micros := i64(time.duration_microseconds(t))
|
||||||
timeval_value.microseconds = int(micros % 1e6)
|
timeval_value.microseconds = int(micros % 1e6)
|
||||||
@@ -320,7 +319,7 @@ _set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_E
|
|||||||
}
|
}
|
||||||
|
|
||||||
if should_block {
|
if should_block {
|
||||||
flags &= ~int(os.O_NONBLOCK)
|
flags &~= int(os.O_NONBLOCK)
|
||||||
} else {
|
} else {
|
||||||
flags |= int(os.O_NONBLOCK)
|
flags |= int(os.O_NONBLOCK)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,14 +80,14 @@ _unwrap_os_addr :: proc "contextless" (endpoint: Endpoint)->(linux.Sock_Addr_Any
|
|||||||
ipv4 = {
|
ipv4 = {
|
||||||
sin_family = .INET,
|
sin_family = .INET,
|
||||||
sin_port = u16be(endpoint.port),
|
sin_port = u16be(endpoint.port),
|
||||||
sin_addr = transmute([4]u8) endpoint.address.(IP4_Address),
|
sin_addr = ([4]u8)(endpoint.address.(IP4_Address)),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
case IP6_Address:
|
case IP6_Address:
|
||||||
return {
|
return {
|
||||||
ipv6 = {
|
ipv6 = {
|
||||||
sin6_port = u16be(endpoint.port),
|
sin6_port = u16be(endpoint.port),
|
||||||
sin6_addr = transmute([16]u8) endpoint.address.(IP6_Address),
|
sin6_addr = transmute([16]u8)endpoint.address.(IP6_Address),
|
||||||
sin6_family = .INET6,
|
sin6_family = .INET6,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -377,9 +377,9 @@ _set_blocking :: proc(sock: Any_Socket, should_block: bool) -> (err: Network_Err
|
|||||||
return Set_Blocking_Error(errno)
|
return Set_Blocking_Error(errno)
|
||||||
}
|
}
|
||||||
if should_block {
|
if should_block {
|
||||||
flags &= ~{.NONBLOCK}
|
flags -= {.NONBLOCK}
|
||||||
} else {
|
} else {
|
||||||
flags |= {.NONBLOCK}
|
flags += {.NONBLOCK}
|
||||||
}
|
}
|
||||||
errno = linux.fcntl(os_sock, linux.F_SETFL, flags)
|
errno = linux.fcntl(os_sock, linux.F_SETFL, flags)
|
||||||
if errno != .NONE {
|
if errno != .NONE {
|
||||||
|
|||||||
@@ -263,12 +263,15 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca
|
|||||||
ptr = &bool_value
|
ptr = &bool_value
|
||||||
len = size_of(bool_value)
|
len = size_of(bool_value)
|
||||||
case .Linger:
|
case .Linger:
|
||||||
t, ok := value.(time.Duration)
|
t := value.(time.Duration) or_else panic("set_option() value must be a time.Duration here", loc)
|
||||||
if !ok do panic("set_option() value must be a time.Duration here", loc)
|
|
||||||
|
|
||||||
num_secs := i64(time.duration_seconds(t))
|
num_secs := i64(time.duration_seconds(t))
|
||||||
if time.Duration(num_secs * 1e9) != t do return .Linger_Only_Supports_Whole_Seconds
|
if time.Duration(num_secs * 1e9) != t {
|
||||||
if num_secs > i64(max(u16)) do return .Value_Out_Of_Range
|
return .Linger_Only_Supports_Whole_Seconds
|
||||||
|
}
|
||||||
|
if num_secs > i64(max(u16)) {
|
||||||
|
return .Value_Out_Of_Range
|
||||||
|
}
|
||||||
linger_value.l_onoff = 1
|
linger_value.l_onoff = 1
|
||||||
linger_value.l_linger = c.ushort(num_secs)
|
linger_value.l_linger = c.ushort(num_secs)
|
||||||
|
|
||||||
@@ -277,8 +280,7 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca
|
|||||||
case
|
case
|
||||||
.Receive_Timeout,
|
.Receive_Timeout,
|
||||||
.Send_Timeout:
|
.Send_Timeout:
|
||||||
t, ok := value.(time.Duration)
|
t := value.(time.Duration) or_else panic("set_option() value must be a time.Duration here", loc)
|
||||||
if !ok do panic("set_option() value must be a time.Duration here", loc)
|
|
||||||
|
|
||||||
int_value = i32(time.duration_milliseconds(t))
|
int_value = i32(time.duration_milliseconds(t))
|
||||||
ptr = &int_value
|
ptr = &int_value
|
||||||
|
|||||||
@@ -513,6 +513,7 @@ Package_Decl :: struct {
|
|||||||
Import_Decl :: struct {
|
Import_Decl :: struct {
|
||||||
using node: Decl,
|
using node: Decl,
|
||||||
docs: ^Comment_Group,
|
docs: ^Comment_Group,
|
||||||
|
attributes: [dynamic]^Attribute, // dynamic as parsing will add to them lazily
|
||||||
is_using: bool,
|
is_using: bool,
|
||||||
import_tok: tokenizer.Token,
|
import_tok: tokenizer.Token,
|
||||||
name: tokenizer.Token,
|
name: tokenizer.Token,
|
||||||
|
|||||||
@@ -308,7 +308,7 @@ consume_comment_group :: proc(p: ^Parser, n: int) -> (comments: ^ast.Comment_Gro
|
|||||||
end_line = p.curr_tok.pos.line
|
end_line = p.curr_tok.pos.line
|
||||||
for p.curr_tok.kind == .Comment &&
|
for p.curr_tok.kind == .Comment &&
|
||||||
p.curr_tok.pos.line <= end_line+n {
|
p.curr_tok.pos.line <= end_line+n {
|
||||||
comment: tokenizer.Token
|
comment: tokenizer.Token
|
||||||
comment, end_line = consume_comment(p)
|
comment, end_line = consume_comment(p)
|
||||||
append(&list, comment)
|
append(&list, comment)
|
||||||
}
|
}
|
||||||
@@ -689,7 +689,12 @@ parse_when_stmt :: proc(p: ^Parser) -> ^ast.When_Stmt {
|
|||||||
|
|
||||||
prev_level := p.expr_level
|
prev_level := p.expr_level
|
||||||
p.expr_level = -1
|
p.expr_level = -1
|
||||||
|
prev_allow_in_expr := p.allow_in_expr
|
||||||
|
p.allow_in_expr = true
|
||||||
|
|
||||||
cond = parse_expr(p, false)
|
cond = parse_expr(p, false)
|
||||||
|
|
||||||
|
p.allow_in_expr = prev_allow_in_expr
|
||||||
p.expr_level = prev_level
|
p.expr_level = prev_level
|
||||||
|
|
||||||
if cond == nil {
|
if cond == nil {
|
||||||
@@ -1103,6 +1108,9 @@ parse_attribute :: proc(p: ^Parser, tok: tokenizer.Token, open_kind, close_kind:
|
|||||||
case ^ast.Foreign_Import_Decl:
|
case ^ast.Foreign_Import_Decl:
|
||||||
if d.docs == nil { d.docs = docs }
|
if d.docs == nil { d.docs = docs }
|
||||||
append(&d.attributes, attribute)
|
append(&d.attributes, attribute)
|
||||||
|
case ^ast.Import_Decl:
|
||||||
|
if d.docs == nil { d.docs = docs }
|
||||||
|
append(&d.attributes, attribute)
|
||||||
case:
|
case:
|
||||||
error(p, decl.pos, "expected a value or foreign declaration after an attribute")
|
error(p, decl.pos, "expected a value or foreign declaration after an attribute")
|
||||||
free(attribute)
|
free(attribute)
|
||||||
@@ -1307,8 +1315,8 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
|
|||||||
// Unary Expressions
|
// Unary Expressions
|
||||||
.Add, .Sub, .Xor, .Not, .And:
|
.Add, .Sub, .Xor, .Not, .And:
|
||||||
|
|
||||||
s := parse_simple_stmt(p, {Stmt_Allow_Flag.Label})
|
s := parse_simple_stmt(p, {Stmt_Allow_Flag.Label})
|
||||||
expect_semicolon(p, s)
|
expect_semicolon(p, s)
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
@@ -1845,7 +1853,7 @@ parse_ident_list :: proc(p: ^Parser, allow_poly_names: bool) -> []^ast.Expr {
|
|||||||
}
|
}
|
||||||
if p.curr_tok.kind != .Comma ||
|
if p.curr_tok.kind != .Comma ||
|
||||||
p.curr_tok.kind == .EOF {
|
p.curr_tok.kind == .EOF {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
advance_token(p)
|
advance_token(p)
|
||||||
}
|
}
|
||||||
@@ -2208,10 +2216,10 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
|||||||
|
|
||||||
case .Integer, .Float, .Imag,
|
case .Integer, .Float, .Imag,
|
||||||
.Rune, .String:
|
.Rune, .String:
|
||||||
tok := advance_token(p)
|
tok := advance_token(p)
|
||||||
bl := ast.new(ast.Basic_Lit, tok.pos, end_pos(tok))
|
bl := ast.new(ast.Basic_Lit, tok.pos, end_pos(tok))
|
||||||
bl.tok = tok
|
bl.tok = tok
|
||||||
return bl
|
return bl
|
||||||
|
|
||||||
case .Open_Brace:
|
case .Open_Brace:
|
||||||
if !lhs {
|
if !lhs {
|
||||||
@@ -2999,7 +3007,7 @@ parse_literal_value :: proc(p: ^Parser, type: ^ast.Expr) -> ^ast.Comp_Lit {
|
|||||||
}
|
}
|
||||||
p.expr_level -= 1
|
p.expr_level -= 1
|
||||||
|
|
||||||
skip_possible_newline(p)
|
skip_possible_newline(p)
|
||||||
close := expect_closing_brace_of_field_list(p)
|
close := expect_closing_brace_of_field_list(p)
|
||||||
|
|
||||||
pos := type.pos if type != nil else open.pos
|
pos := type.pos if type != nil else open.pos
|
||||||
|
|||||||
+2
-2
@@ -111,7 +111,7 @@ read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator,
|
|||||||
length: i64
|
length: i64
|
||||||
err: Errno
|
err: Errno
|
||||||
if length, err = file_size(fd); err != 0 {
|
if length, err = file_size(fd); err != 0 {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
if length <= 0 {
|
if length <= 0 {
|
||||||
@@ -120,7 +120,7 @@ read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator,
|
|||||||
|
|
||||||
data = make([]byte, int(length), allocator, loc)
|
data = make([]byte, int(length), allocator, loc)
|
||||||
if data == nil {
|
if data == nil {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes_read, read_err := read_full(fd, data)
|
bytes_read, read_err := read_full(fd, data)
|
||||||
|
|||||||
+210
-9
@@ -2,29 +2,230 @@
|
|||||||
package os2
|
package os2
|
||||||
|
|
||||||
import "base:runtime"
|
import "base:runtime"
|
||||||
|
import "base:intrinsics"
|
||||||
|
|
||||||
|
import "core:sync"
|
||||||
|
import "core:slice"
|
||||||
|
import "core:strings"
|
||||||
|
|
||||||
|
// TODO: IF NO_CRT:
|
||||||
|
// Override the libc environment functions' weak linkage to
|
||||||
|
// allow us to interact with 3rd party code that DOES link
|
||||||
|
// to libc. Otherwise, our environment can be out of sync.
|
||||||
|
// ELSE:
|
||||||
|
// Just use the libc.
|
||||||
|
|
||||||
|
NOT_FOUND :: -1
|
||||||
|
|
||||||
|
// the environment is a 0 delimited list of <key>=<value> strings
|
||||||
|
_env: [dynamic]string
|
||||||
|
|
||||||
|
_env_mutex: sync.Mutex
|
||||||
|
|
||||||
|
// We need to be able to figure out if the environment variable
|
||||||
|
// is contained in the original environment or not. This also
|
||||||
|
// serves as a flag to determine if we have built _env.
|
||||||
|
_org_env_begin: uintptr
|
||||||
|
_org_env_end: uintptr
|
||||||
|
|
||||||
|
// Returns value + index location into _env
|
||||||
|
// or -1 if not found
|
||||||
|
_lookup :: proc(key: string) -> (value: string, idx: int) {
|
||||||
|
sync.mutex_lock(&_env_mutex)
|
||||||
|
defer sync.mutex_unlock(&_env_mutex)
|
||||||
|
|
||||||
|
for entry, i in _env {
|
||||||
|
if k, v := _kv_from_entry(entry); k == key {
|
||||||
|
return v, i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", -1
|
||||||
|
}
|
||||||
|
|
||||||
_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
|
_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
|
||||||
//TODO
|
if _org_env_begin == 0 {
|
||||||
|
_build_env()
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, idx := _lookup(key); idx != -1 {
|
||||||
|
found = true
|
||||||
|
value, _ = clone_string(v, allocator)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_set_env :: proc(key, value: string) -> bool {
|
_set_env :: proc(key, v_new: string) -> bool {
|
||||||
//TODO
|
if _org_env_begin == 0 {
|
||||||
return false
|
_build_env()
|
||||||
|
}
|
||||||
|
|
||||||
|
// all key values are stored as "key=value\x00"
|
||||||
|
kv_size := len(key) + len(v_new) + 2
|
||||||
|
if v_curr, idx := _lookup(key); idx != NOT_FOUND {
|
||||||
|
if v_curr == v_new {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
sync.mutex_lock(&_env_mutex)
|
||||||
|
defer sync.mutex_unlock(&_env_mutex)
|
||||||
|
|
||||||
|
unordered_remove(&_env, idx)
|
||||||
|
|
||||||
|
if !_is_in_org_env(v_curr) {
|
||||||
|
// We allocated this key-value. Possibly resize and
|
||||||
|
// overwrite the value only. Otherwise, treat as if it
|
||||||
|
// wasn't in the environment in the first place.
|
||||||
|
k_addr, v_addr := _kv_addr_from_val(v_curr, key)
|
||||||
|
if len(v_new) > len(v_curr) {
|
||||||
|
k_addr = ([^]u8)(heap_resize(k_addr, kv_size))
|
||||||
|
if k_addr == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
v_addr = &k_addr[len(key) + 1]
|
||||||
|
}
|
||||||
|
intrinsics.mem_copy_non_overlapping(v_addr, raw_data(v_new), len(v_new))
|
||||||
|
v_addr[len(v_new)] = 0
|
||||||
|
|
||||||
|
append(&_env, string(k_addr[:kv_size]))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
k_addr := ([^]u8)(heap_alloc(kv_size))
|
||||||
|
if k_addr == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
intrinsics.mem_copy_non_overlapping(k_addr, raw_data(key), len(key))
|
||||||
|
k_addr[len(key)] = '='
|
||||||
|
|
||||||
|
val_slice := k_addr[len(key) + 1:]
|
||||||
|
intrinsics.mem_copy_non_overlapping(&val_slice[0], raw_data(v_new), len(v_new))
|
||||||
|
val_slice[len(v_new)] = 0
|
||||||
|
|
||||||
|
sync.mutex_lock(&_env_mutex)
|
||||||
|
append(&_env, string(k_addr[:kv_size - 1]))
|
||||||
|
sync.mutex_unlock(&_env_mutex)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
_unset_env :: proc(key: string) -> bool {
|
_unset_env :: proc(key: string) -> bool {
|
||||||
//TODO
|
if _org_env_begin == 0 {
|
||||||
return false
|
_build_env()
|
||||||
|
}
|
||||||
|
|
||||||
|
v: string
|
||||||
|
i: int
|
||||||
|
if v, i = _lookup(key); i == -1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
sync.mutex_lock(&_env_mutex)
|
||||||
|
unordered_remove(&_env, i)
|
||||||
|
sync.mutex_unlock(&_env_mutex)
|
||||||
|
|
||||||
|
if _is_in_org_env(v) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we got this far, the envrionment variable
|
||||||
|
// existed AND was allocated by us.
|
||||||
|
k_addr, _ := _kv_addr_from_val(v, key)
|
||||||
|
heap_free(k_addr)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
_clear_env :: proc() {
|
_clear_env :: proc() {
|
||||||
//TODO
|
sync.mutex_lock(&_env_mutex)
|
||||||
|
defer sync.mutex_unlock(&_env_mutex)
|
||||||
|
|
||||||
|
for kv in _env {
|
||||||
|
if !_is_in_org_env(kv) {
|
||||||
|
heap_free(raw_data(kv))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clear(&_env)
|
||||||
|
|
||||||
|
// nothing resides in the original environment either
|
||||||
|
_org_env_begin = ~uintptr(0)
|
||||||
|
_org_env_end = ~uintptr(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
_environ :: proc(allocator: runtime.Allocator) -> []string {
|
_environ :: proc(allocator: runtime.Allocator) -> []string {
|
||||||
//TODO
|
if _org_env_begin == 0 {
|
||||||
return nil
|
_build_env()
|
||||||
|
}
|
||||||
|
env := make([]string, len(_env), allocator)
|
||||||
|
|
||||||
|
sync.mutex_lock(&_env_mutex)
|
||||||
|
defer sync.mutex_unlock(&_env_mutex)
|
||||||
|
for entry, i in _env {
|
||||||
|
env[i], _ = clone_string(entry, allocator)
|
||||||
|
}
|
||||||
|
return env
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The entire environment is stored as 0 terminated strings,
|
||||||
|
// so there is no need to clone/free individual variables
|
||||||
|
export_cstring_environment :: proc(allocator: runtime.Allocator) -> []cstring {
|
||||||
|
if _org_env_begin == 0 {
|
||||||
|
// The environment has not been modified, so we can just
|
||||||
|
// send the original environment
|
||||||
|
org_env := _get_original_env()
|
||||||
|
n: int
|
||||||
|
for ; org_env[n] != nil; n += 1 {}
|
||||||
|
return slice.clone(org_env[:n + 1], allocator)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: already terminated by nil pointer via + 1
|
||||||
|
env := make([]cstring, len(_env) + 1, allocator)
|
||||||
|
|
||||||
|
sync.mutex_lock(&_env_mutex)
|
||||||
|
defer sync.mutex_unlock(&_env_mutex)
|
||||||
|
for entry, i in _env {
|
||||||
|
env[i] = cstring(raw_data(entry))
|
||||||
|
}
|
||||||
|
return env
|
||||||
|
}
|
||||||
|
|
||||||
|
_build_env :: proc() {
|
||||||
|
sync.mutex_lock(&_env_mutex)
|
||||||
|
defer sync.mutex_unlock(&_env_mutex)
|
||||||
|
if _org_env_begin != 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_env = make(type_of(_env), heap_allocator())
|
||||||
|
cstring_env := _get_original_env()
|
||||||
|
_org_env_begin = uintptr(rawptr(cstring_env[0]))
|
||||||
|
for i := 0; cstring_env[i] != nil; i += 1 {
|
||||||
|
bytes := ([^]u8)(cstring_env[i])
|
||||||
|
n := len(cstring_env[i])
|
||||||
|
_org_env_end = uintptr(&bytes[n])
|
||||||
|
append(&_env, string(bytes[:n]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_original_env :: #force_inline proc() -> [^]cstring {
|
||||||
|
// essentially &argv[argc] which should be a nil pointer!
|
||||||
|
#no_bounds_check env: [^]cstring = &runtime.args__[len(runtime.args__)]
|
||||||
|
assert(env[0] == nil)
|
||||||
|
return &env[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
_kv_from_entry :: #force_inline proc(entry: string) -> (k, v: string) {
|
||||||
|
eq_idx := strings.index_byte(entry, '=')
|
||||||
|
if eq_idx == -1 {
|
||||||
|
return entry, ""
|
||||||
|
}
|
||||||
|
return entry[:eq_idx], entry[eq_idx + 1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
_kv_addr_from_val :: #force_inline proc(val: string, key: string) -> ([^]u8, [^]u8) {
|
||||||
|
v_addr := raw_data(val)
|
||||||
|
k_addr := ([^]u8)(&v_addr[-(len(key) + 1)])
|
||||||
|
return k_addr, v_addr
|
||||||
|
}
|
||||||
|
|
||||||
|
_is_in_org_env :: #force_inline proc(env_data: string) -> bool {
|
||||||
|
addr := uintptr(raw_data(env_data))
|
||||||
|
return addr >= _org_env_begin && addr < _org_env_end
|
||||||
|
}
|
||||||
|
|||||||
@@ -99,3 +99,19 @@ error_string :: proc(ferr: Error) -> string {
|
|||||||
|
|
||||||
return "unknown error"
|
return "unknown error"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
print_error :: proc(f: ^File, ferr: Error, msg: string) {
|
||||||
|
TEMP_ALLOCATOR_GUARD()
|
||||||
|
err_str := error_string(ferr)
|
||||||
|
|
||||||
|
// msg + ": " + err_str + '\n'
|
||||||
|
length := len(msg) + 2 + len(err_str) + 1
|
||||||
|
buf := make([]u8, length, temp_allocator())
|
||||||
|
|
||||||
|
copy(buf, msg)
|
||||||
|
buf[len(msg)] = ':'
|
||||||
|
buf[len(msg) + 1] = ' '
|
||||||
|
copy(buf[len(msg) + 2:], err_str)
|
||||||
|
buf[length - 1] = '\n'
|
||||||
|
write(f, buf)
|
||||||
|
}
|
||||||
|
|||||||
+154
-134
@@ -1,145 +1,165 @@
|
|||||||
//+private
|
//+private
|
||||||
package os2
|
package os2
|
||||||
|
|
||||||
import "core:sys/unix"
|
import "core:sys/linux"
|
||||||
|
|
||||||
EPERM :: 1
|
@(rodata)
|
||||||
ENOENT :: 2
|
_errno_strings : [linux.Errno]string = {
|
||||||
ESRCH :: 3
|
.NONE = "Success",
|
||||||
EINTR :: 4
|
.EPERM = "Operation not permitted",
|
||||||
EIO :: 5
|
.ENOENT = "No such file or directory",
|
||||||
ENXIO :: 6
|
.ESRCH = "No such process",
|
||||||
EBADF :: 9
|
.EINTR = "Interrupted system call",
|
||||||
EAGAIN :: 11
|
.EIO = "Input/output error",
|
||||||
ENOMEM :: 12
|
.ENXIO = "No such device or address",
|
||||||
EACCES :: 13
|
.E2BIG = "Argument list too long",
|
||||||
EFAULT :: 14
|
.ENOEXEC = "Exec format error",
|
||||||
EEXIST :: 17
|
.EBADF = "Bad file descriptor",
|
||||||
ENODEV :: 19
|
.ECHILD = "No child processes",
|
||||||
ENOTDIR :: 20
|
.EAGAIN = "Resource temporarily unavailable",
|
||||||
EISDIR :: 21
|
.ENOMEM = "Cannot allocate memory",
|
||||||
EINVAL :: 22
|
.EACCES = "Permission denied",
|
||||||
ENFILE :: 23
|
.EFAULT = "Bad address",
|
||||||
EMFILE :: 24
|
.ENOTBLK = "Block device required",
|
||||||
ETXTBSY :: 26
|
.EBUSY = "Device or resource busy",
|
||||||
EFBIG :: 27
|
.EEXIST = "File exists",
|
||||||
ENOSPC :: 28
|
.EXDEV = "Invalid cross-device link",
|
||||||
ESPIPE :: 29
|
.ENODEV = "No such device",
|
||||||
EROFS :: 30
|
.ENOTDIR = "Not a directory",
|
||||||
EPIPE :: 32
|
.EISDIR = "Is a directory",
|
||||||
ERANGE :: 34 /* Result too large */
|
.EINVAL = "Invalid argument",
|
||||||
EDEADLK :: 35 /* Resource deadlock would occur */
|
.ENFILE = "Too many open files in system",
|
||||||
ENAMETOOLONG :: 36 /* File name too long */
|
.EMFILE = "Too many open files",
|
||||||
ENOLCK :: 37 /* No record locks available */
|
.ENOTTY = "Inappropriate ioctl for device",
|
||||||
ENOSYS :: 38 /* Invalid system call number */
|
.ETXTBSY = "Text file busy",
|
||||||
ENOTEMPTY :: 39 /* Directory not empty */
|
.EFBIG = "File too large",
|
||||||
ELOOP :: 40 /* Too many symbolic links encountered */
|
.ENOSPC = "No space left on device",
|
||||||
EWOULDBLOCK :: EAGAIN /* Operation would block */
|
.ESPIPE = "Illegal seek",
|
||||||
ENOMSG :: 42 /* No message of desired type */
|
.EROFS = "Read-only file system",
|
||||||
EIDRM :: 43 /* Identifier removed */
|
.EMLINK = "Too many links",
|
||||||
ECHRNG :: 44 /* Channel number out of range */
|
.EPIPE = "Broken pipe",
|
||||||
EL2NSYNC :: 45 /* Level 2 not synchronized */
|
.EDOM = "Numerical argument out of domain",
|
||||||
EL3HLT :: 46 /* Level 3 halted */
|
.ERANGE = "Numerical result out of range",
|
||||||
EL3RST :: 47 /* Level 3 reset */
|
.EDEADLK = "Resource deadlock avoided",
|
||||||
ELNRNG :: 48 /* Link number out of range */
|
.ENAMETOOLONG = "File name too long",
|
||||||
EUNATCH :: 49 /* Protocol driver not attached */
|
.ENOLCK = "No locks available",
|
||||||
ENOCSI :: 50 /* No CSI structure available */
|
.ENOSYS = "Function not implemented",
|
||||||
EL2HLT :: 51 /* Level 2 halted */
|
.ENOTEMPTY = "Directory not empty",
|
||||||
EBADE :: 52 /* Invalid exchange */
|
.ELOOP = "Too many levels of symbolic links",
|
||||||
EBADR :: 53 /* Invalid request descriptor */
|
.EUNKNOWN_41 = "Unknown Error (41)",
|
||||||
EXFULL :: 54 /* Exchange full */
|
.ENOMSG = "No message of desired type",
|
||||||
ENOANO :: 55 /* No anode */
|
.EIDRM = "Identifier removed",
|
||||||
EBADRQC :: 56 /* Invalid request code */
|
.ECHRNG = "Channel number out of range",
|
||||||
EBADSLT :: 57 /* Invalid slot */
|
.EL2NSYNC = "Level 2 not synchronized",
|
||||||
EDEADLOCK :: EDEADLK
|
.EL3HLT = "Level 3 halted",
|
||||||
EBFONT :: 59 /* Bad font file format */
|
.EL3RST = "Level 3 reset",
|
||||||
ENOSTR :: 60 /* Device not a stream */
|
.ELNRNG = "Link number out of range",
|
||||||
ENODATA :: 61 /* No data available */
|
.EUNATCH = "Protocol driver not attached",
|
||||||
ETIME :: 62 /* Timer expired */
|
.ENOCSI = "No CSI structure available",
|
||||||
ENOSR :: 63 /* Out of streams resources */
|
.EL2HLT = "Level 2 halted",
|
||||||
ENONET :: 64 /* Machine is not on the network */
|
.EBADE = "Invalid exchange",
|
||||||
ENOPKG :: 65 /* Package not installed */
|
.EBADR = "Invalid request descriptor",
|
||||||
EREMOTE :: 66 /* Object is remote */
|
.EXFULL = "Exchange full",
|
||||||
ENOLINK :: 67 /* Link has been severed */
|
.ENOANO = "No anode",
|
||||||
EADV :: 68 /* Advertise error */
|
.EBADRQC = "Invalid request code",
|
||||||
ESRMNT :: 69 /* Srmount error */
|
.EBADSLT = "Invalid slot",
|
||||||
ECOMM :: 70 /* Communication error on send */
|
.EUNKNOWN_58 = "Unknown Error (58)",
|
||||||
EPROTO :: 71 /* Protocol error */
|
.EBFONT = "Bad font file format",
|
||||||
EMULTIHOP :: 72 /* Multihop attempted */
|
.ENOSTR = "Device not a stream",
|
||||||
EDOTDOT :: 73 /* RFS specific error */
|
.ENODATA = "No data available",
|
||||||
EBADMSG :: 74 /* Not a data message */
|
.ETIME = "Timer expired",
|
||||||
EOVERFLOW :: 75 /* Value too large for defined data type */
|
.ENOSR = "Out of streams resources",
|
||||||
ENOTUNIQ :: 76 /* Name not unique on network */
|
.ENONET = "Machine is not on the network",
|
||||||
EBADFD :: 77 /* File descriptor in bad state */
|
.ENOPKG = "Package not installed",
|
||||||
EREMCHG :: 78 /* Remote address changed */
|
.EREMOTE = "Object is remote",
|
||||||
ELIBACC :: 79 /* Can not access a needed shared library */
|
.ENOLINK = "Link has been severed",
|
||||||
ELIBBAD :: 80 /* Accessing a corrupted shared library */
|
.EADV = "Advertise error",
|
||||||
ELIBSCN :: 81 /* .lib section in a.out corrupted */
|
.ESRMNT = "Srmount error",
|
||||||
ELIBMAX :: 82 /* Attempting to link in too many shared libraries */
|
.ECOMM = "Communication error on send",
|
||||||
ELIBEXEC :: 83 /* Cannot exec a shared library directly */
|
.EPROTO = "Protocol error",
|
||||||
EILSEQ :: 84 /* Illegal byte sequence */
|
.EMULTIHOP = "Multihop attempted",
|
||||||
ERESTART :: 85 /* Interrupted system call should be restarted */
|
.EDOTDOT = "RFS specific error",
|
||||||
ESTRPIPE :: 86 /* Streams pipe error */
|
.EBADMSG = "Bad message",
|
||||||
EUSERS :: 87 /* Too many users */
|
.EOVERFLOW = "Value too large for defined data type",
|
||||||
ENOTSOCK :: 88 /* Socket operation on non-socket */
|
.ENOTUNIQ = "Name not unique on network",
|
||||||
EDESTADDRREQ :: 89 /* Destination address required */
|
.EBADFD = "File descriptor in bad state",
|
||||||
EMSGSIZE :: 90 /* Message too long */
|
.EREMCHG = "Remote address changed",
|
||||||
EPROTOTYPE :: 91 /* Protocol wrong type for socket */
|
.ELIBACC = "Can not access a needed shared library",
|
||||||
ENOPROTOOPT :: 92 /* Protocol not available */
|
.ELIBBAD = "Accessing a corrupted shared library",
|
||||||
EPROTONOSUPPORT:: 93 /* Protocol not supported */
|
.ELIBSCN = ".lib section in a.out corrupted",
|
||||||
ESOCKTNOSUPPORT:: 94 /* Socket type not supported */
|
.ELIBMAX = "Attempting to link in too many shared libraries",
|
||||||
EOPNOTSUPP :: 95 /* Operation not supported on transport endpoint */
|
.ELIBEXEC = "Cannot exec a shared library directly",
|
||||||
EPFNOSUPPORT :: 96 /* Protocol family not supported */
|
.EILSEQ = "Invalid or incomplete multibyte or wide character",
|
||||||
EAFNOSUPPORT :: 97 /* Address family not supported by protocol */
|
.ERESTART = "Interrupted system call should be restarted",
|
||||||
EADDRINUSE :: 98 /* Address already in use */
|
.ESTRPIPE = "Streams pipe error",
|
||||||
EADDRNOTAVAIL :: 99 /* Cannot assign requested address */
|
.EUSERS = "Too many users",
|
||||||
ENETDOWN :: 100 /* Network is down */
|
.ENOTSOCK = "Socket operation on non-socket",
|
||||||
ENETUNREACH :: 101 /* Network is unreachable */
|
.EDESTADDRREQ = "Destination address required",
|
||||||
ENETRESET :: 102 /* Network dropped connection because of reset */
|
.EMSGSIZE = "Message too long",
|
||||||
ECONNABORTED :: 103 /* Software caused connection abort */
|
.EPROTOTYPE = "Protocol wrong type for socket",
|
||||||
ECONNRESET :: 104 /* Connection reset by peer */
|
.ENOPROTOOPT = "Protocol not available",
|
||||||
ENOBUFS :: 105 /* No buffer space available */
|
.EPROTONOSUPPORT = "Protocol not supported",
|
||||||
EISCONN :: 106 /* Transport endpoint is already connected */
|
.ESOCKTNOSUPPORT = "Socket type not supported",
|
||||||
ENOTCONN :: 107 /* Transport endpoint is not connected */
|
.EOPNOTSUPP = "Operation not supported",
|
||||||
ESHUTDOWN :: 108 /* Cannot send after transport endpoint shutdown */
|
.EPFNOSUPPORT = "Protocol family not supported",
|
||||||
ETOOMANYREFS :: 109 /* Too many references: cannot splice */
|
.EAFNOSUPPORT = "Address family not supported by protocol",
|
||||||
ETIMEDOUT :: 110 /* Connection timed out */
|
.EADDRINUSE = "Address already in use",
|
||||||
ECONNREFUSED :: 111 /* Connection refused */
|
.EADDRNOTAVAIL = "Cannot assign requested address",
|
||||||
EHOSTDOWN :: 112 /* Host is down */
|
.ENETDOWN = "Network is down",
|
||||||
EHOSTUNREACH :: 113 /* No route to host */
|
.ENETUNREACH = "Network is unreachable",
|
||||||
EALREADY :: 114 /* Operation already in progress */
|
.ENETRESET = "Network dropped connection on reset",
|
||||||
EINPROGRESS :: 115 /* Operation now in progress */
|
.ECONNABORTED = "Software caused connection abort",
|
||||||
ESTALE :: 116 /* Stale file handle */
|
.ECONNRESET = "Connection reset by peer",
|
||||||
EUCLEAN :: 117 /* Structure needs cleaning */
|
.ENOBUFS = "No buffer space available",
|
||||||
ENOTNAM :: 118 /* Not a XENIX named type file */
|
.EISCONN = "Transport endpoint is already connected",
|
||||||
ENAVAIL :: 119 /* No XENIX semaphores available */
|
.ENOTCONN = "Transport endpoint is not connected",
|
||||||
EISNAM :: 120 /* Is a named type file */
|
.ESHUTDOWN = "Cannot send after transport endpoint shutdown",
|
||||||
EREMOTEIO :: 121 /* Remote I/O error */
|
.ETOOMANYREFS = "Too many references: cannot splice",
|
||||||
EDQUOT :: 122 /* Quota exceeded */
|
.ETIMEDOUT = "Connection timed out",
|
||||||
ENOMEDIUM :: 123 /* No medium found */
|
.ECONNREFUSED = "Connection refused",
|
||||||
EMEDIUMTYPE :: 124 /* Wrong medium type */
|
.EHOSTDOWN = "Host is down",
|
||||||
ECANCELED :: 125 /* Operation Canceled */
|
.EHOSTUNREACH = "No route to host",
|
||||||
ENOKEY :: 126 /* Required key not available */
|
.EALREADY = "Operation already in progress",
|
||||||
EKEYEXPIRED :: 127 /* Key has expired */
|
.EINPROGRESS = "Operation now in progress",
|
||||||
EKEYREVOKED :: 128 /* Key has been revoked */
|
.ESTALE = "Stale file handle",
|
||||||
EKEYREJECTED :: 129 /* Key was rejected by service */
|
.EUCLEAN = "Structure needs cleaning",
|
||||||
EOWNERDEAD :: 130 /* Owner died */
|
.ENOTNAM = "Not a XENIX named type file",
|
||||||
ENOTRECOVERABLE:: 131 /* State not recoverable */
|
.ENAVAIL = "No XENIX semaphores available",
|
||||||
ERFKILL :: 132 /* Operation not possible due to RF-kill */
|
.EISNAM = "Is a named type file",
|
||||||
EHWPOISON :: 133 /* Memory page has hardware error */
|
.EREMOTEIO = "Remote I/O error",
|
||||||
|
.EDQUOT = "Disk quota exceeded",
|
||||||
|
.ENOMEDIUM = "No medium found",
|
||||||
|
.EMEDIUMTYPE = "Wrong medium type",
|
||||||
|
.ECANCELED = "Operation canceled",
|
||||||
|
.ENOKEY = "Required key not available",
|
||||||
|
.EKEYEXPIRED = "Key has expired",
|
||||||
|
.EKEYREVOKED = "Key has been revoked",
|
||||||
|
.EKEYREJECTED = "Key was rejected by service",
|
||||||
|
.EOWNERDEAD = "Owner died",
|
||||||
|
.ENOTRECOVERABLE = "State not recoverable",
|
||||||
|
.ERFKILL = "Operation not possible due to RF-kill",
|
||||||
|
.EHWPOISON = "Memory page has hardware error",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_get_platform_error :: proc(errno: linux.Errno) -> Error {
|
||||||
|
#partial switch errno {
|
||||||
|
case .NONE:
|
||||||
|
return nil
|
||||||
|
case .EPERM:
|
||||||
|
return .Permission_Denied
|
||||||
|
case .EEXIST:
|
||||||
|
return .Exist
|
||||||
|
case .ENOENT:
|
||||||
|
return .Not_Exist
|
||||||
|
}
|
||||||
|
|
||||||
_get_platform_error :: proc(res: int) -> Error {
|
|
||||||
errno := unix.get_errno(res)
|
|
||||||
return Platform_Error(i32(errno))
|
return Platform_Error(i32(errno))
|
||||||
}
|
}
|
||||||
|
|
||||||
_ok_or_error :: proc(res: int) -> Error {
|
|
||||||
return res >= 0 ? nil : _get_platform_error(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
_error_string :: proc(errno: i32) -> string {
|
_error_string :: proc(errno: i32) -> string {
|
||||||
if errno == 0 {
|
if errno >= 0 && errno <= i32(max(linux.Errno)) {
|
||||||
return ""
|
return _errno_strings[linux.Errno(errno)]
|
||||||
}
|
}
|
||||||
return "Error"
|
return "Unknown Error"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,13 +45,10 @@ O_TRUNC :: File_Flags{.Trunc}
|
|||||||
O_SPARSE :: File_Flags{.Sparse}
|
O_SPARSE :: File_Flags{.Sparse}
|
||||||
O_CLOEXEC :: File_Flags{.Close_On_Exec}
|
O_CLOEXEC :: File_Flags{.Close_On_Exec}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
stdin: ^File = nil // OS-Specific
|
stdin: ^File = nil // OS-Specific
|
||||||
stdout: ^File = nil // OS-Specific
|
stdout: ^File = nil // OS-Specific
|
||||||
stderr: ^File = nil // OS-Specific
|
stderr: ^File = nil // OS-Specific
|
||||||
|
|
||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
create :: proc(name: string) -> (^File, Error) {
|
create :: proc(name: string) -> (^File, Error) {
|
||||||
return open(name, {.Read, .Write, .Create}, File_Mode(0o777))
|
return open(name, {.Read, .Write, .Create}, File_Mode(0o777))
|
||||||
|
|||||||
+231
-161
@@ -1,39 +1,64 @@
|
|||||||
//+private
|
//+private
|
||||||
package os2
|
package os2
|
||||||
|
|
||||||
import "base:runtime"
|
|
||||||
import "core:io"
|
import "core:io"
|
||||||
import "core:time"
|
import "core:time"
|
||||||
import "core:sys/unix"
|
import "base:runtime"
|
||||||
|
import "core:sys/linux"
|
||||||
INVALID_HANDLE :: -1
|
|
||||||
|
|
||||||
_O_RDONLY :: 0o00000000
|
|
||||||
_O_WRONLY :: 0o00000001
|
|
||||||
_O_RDWR :: 0o00000002
|
|
||||||
_O_CREAT :: 0o00000100
|
|
||||||
_O_EXCL :: 0o00000200
|
|
||||||
_O_NOCTTY :: 0o00000400
|
|
||||||
_O_TRUNC :: 0o00001000
|
|
||||||
_O_APPEND :: 0o00002000
|
|
||||||
_O_NONBLOCK :: 0o00004000
|
|
||||||
_O_LARGEFILE :: 0o00100000
|
|
||||||
_O_DIRECTORY :: 0o00200000
|
|
||||||
_O_NOFOLLOW :: 0o00400000
|
|
||||||
_O_SYNC :: 0o04010000
|
|
||||||
_O_CLOEXEC :: 0o02000000
|
|
||||||
_O_PATH :: 0o10000000
|
|
||||||
|
|
||||||
_AT_FDCWD :: -100
|
|
||||||
|
|
||||||
_CSTRING_NAME_HEAP_THRESHOLD :: 512
|
|
||||||
|
|
||||||
_File :: struct {
|
_File :: struct {
|
||||||
name: string,
|
name: string,
|
||||||
fd: int,
|
fd: linux.Fd,
|
||||||
allocator: runtime.Allocator,
|
allocator: runtime.Allocator,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_stdin : File = {
|
||||||
|
impl = {
|
||||||
|
name = "/proc/self/fd/0",
|
||||||
|
fd = 0,
|
||||||
|
allocator = _file_allocator(),
|
||||||
|
},
|
||||||
|
stream = {
|
||||||
|
procedure = _file_stream_proc,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_stdout : File = {
|
||||||
|
impl = {
|
||||||
|
name = "/proc/self/fd/1",
|
||||||
|
fd = 1,
|
||||||
|
allocator = _file_allocator(),
|
||||||
|
},
|
||||||
|
stream = {
|
||||||
|
procedure = _file_stream_proc,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_stderr : File = {
|
||||||
|
impl = {
|
||||||
|
name = "/proc/self/fd/2",
|
||||||
|
fd = 2,
|
||||||
|
allocator = _file_allocator(),
|
||||||
|
},
|
||||||
|
stream = {
|
||||||
|
procedure = _file_stream_proc,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
@init
|
||||||
|
_standard_stream_init :: proc() {
|
||||||
|
// cannot define these manually because cyclic reference
|
||||||
|
_stdin.stream.data = &_stdin
|
||||||
|
_stdout.stream.data = &_stdout
|
||||||
|
_stderr.stream.data = &_stderr
|
||||||
|
|
||||||
|
stdin = &_stdin
|
||||||
|
stdout = &_stdout
|
||||||
|
stderr = &_stderr
|
||||||
|
}
|
||||||
|
|
||||||
|
_file_allocator :: proc() -> runtime.Allocator {
|
||||||
|
return heap_allocator()
|
||||||
|
}
|
||||||
|
|
||||||
_open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (f: ^File, err: Error) {
|
_open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (f: ^File, err: Error) {
|
||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
name_cstr := temp_cstring(name) or_return
|
name_cstr := temp_cstring(name) or_return
|
||||||
@@ -41,40 +66,48 @@ _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (f: ^File, er
|
|||||||
// Just default to using O_NOCTTY because needing to open a controlling
|
// Just default to using O_NOCTTY because needing to open a controlling
|
||||||
// terminal would be incredibly rare. This has no effect on files while
|
// terminal would be incredibly rare. This has no effect on files while
|
||||||
// allowing us to open serial devices.
|
// allowing us to open serial devices.
|
||||||
flags_i: int = _O_NOCTTY
|
sys_flags: linux.Open_Flags = {.NOCTTY}
|
||||||
switch flags & O_RDONLY|O_WRONLY|O_RDWR {
|
switch flags & O_RDONLY|O_WRONLY|O_RDWR {
|
||||||
case O_RDONLY: flags_i = _O_RDONLY
|
case O_RDONLY:
|
||||||
case O_WRONLY: flags_i = _O_WRONLY
|
case O_WRONLY: sys_flags += {.WRONLY}
|
||||||
case O_RDWR: flags_i = _O_RDWR
|
case O_RDWR: sys_flags += {.RDWR}
|
||||||
}
|
}
|
||||||
|
|
||||||
if .Append in flags { flags_i |= _O_APPEND }
|
if .Append in flags { sys_flags += {.APPEND} }
|
||||||
if .Create in flags { flags_i |= _O_CREAT }
|
if .Create in flags { sys_flags += {.CREAT} }
|
||||||
if .Excl in flags { flags_i |= _O_EXCL }
|
if .Excl in flags { sys_flags += {.EXCL} }
|
||||||
if .Sync in flags { flags_i |= _O_SYNC }
|
if .Sync in flags { sys_flags += {.DSYNC} }
|
||||||
if .Trunc in flags { flags_i |= _O_TRUNC }
|
if .Trunc in flags { sys_flags += {.TRUNC} }
|
||||||
if .Close_On_Exec in flags { flags_i |= _O_CLOEXEC }
|
if .Close_On_Exec in flags { sys_flags += {.CLOEXEC} }
|
||||||
|
|
||||||
fd := unix.sys_open(name_cstr, flags_i, uint(perm))
|
fd, errno := linux.open(name_cstr, sys_flags, transmute(linux.Mode)(u32(perm)))
|
||||||
if fd < 0 {
|
if errno != .NONE {
|
||||||
return nil, _get_platform_error(fd)
|
return nil, _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
|
|
||||||
return _new_file(uintptr(fd), name), nil
|
return _new_file(uintptr(fd), name), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_new_file :: proc(fd: uintptr, _: string) -> ^File {
|
_new_file :: proc(fd: uintptr, _: string = "") -> ^File {
|
||||||
file := new(File, file_allocator())
|
file := new(File, file_allocator())
|
||||||
file.impl.fd = int(fd)
|
_construct_file(file, fd, "")
|
||||||
file.impl.allocator = file_allocator()
|
|
||||||
file.impl.name = _get_full_path(file.impl.fd, file.impl.allocator)
|
|
||||||
file.stream = {
|
|
||||||
data = file,
|
|
||||||
procedure = _file_stream_proc,
|
|
||||||
}
|
|
||||||
return file
|
return file
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_construct_file :: proc(file: ^File, fd: uintptr, _: string = "") {
|
||||||
|
file^ = {
|
||||||
|
impl = {
|
||||||
|
fd = linux.Fd(fd),
|
||||||
|
allocator = file_allocator(),
|
||||||
|
name = _get_full_path(file.impl.fd, file.impl.allocator),
|
||||||
|
},
|
||||||
|
stream = {
|
||||||
|
data = file,
|
||||||
|
procedure = _file_stream_proc,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_destroy :: proc(f: ^File) -> Error {
|
_destroy :: proc(f: ^File) -> Error {
|
||||||
if f == nil {
|
if f == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -86,12 +119,15 @@ _destroy :: proc(f: ^File) -> Error {
|
|||||||
|
|
||||||
|
|
||||||
_close :: proc(f: ^File) -> Error {
|
_close :: proc(f: ^File) -> Error {
|
||||||
if f != nil {
|
if f == nil {
|
||||||
res := unix.sys_close(f.impl.fd)
|
return nil
|
||||||
_destroy(f)
|
|
||||||
return _ok_or_error(res)
|
|
||||||
}
|
}
|
||||||
return nil
|
errno := linux.close(f.impl.fd)
|
||||||
|
if errno == .EBADF { // avoid possible double free
|
||||||
|
return _get_platform_error(errno)
|
||||||
|
}
|
||||||
|
_destroy(f)
|
||||||
|
return _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
|
|
||||||
_fd :: proc(f: ^File) -> uintptr {
|
_fd :: proc(f: ^File) -> uintptr {
|
||||||
@@ -106,20 +142,32 @@ _name :: proc(f: ^File) -> string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) {
|
_seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) {
|
||||||
res := unix.sys_lseek(f.impl.fd, offset, int(whence))
|
n, errno := linux.lseek(f.impl.fd, offset, linux.Seek_Whence(whence))
|
||||||
if res < 0 {
|
if errno != .NONE {
|
||||||
return -1, _get_platform_error(int(res))
|
return -1, _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
return res, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_read :: proc(f: ^File, p: []byte) -> (i64, Error) {
|
_read :: proc(f: ^File, p: []byte) -> (i64, Error) {
|
||||||
if len(p) == 0 {
|
if len(p) == 0 {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
n := unix.sys_read(f.impl.fd, &p[0], len(p))
|
n, errno := linux.read(f.impl.fd, p[:])
|
||||||
if n < 0 {
|
if errno != .NONE {
|
||||||
return -1, _get_platform_error(n)
|
return -1, _get_platform_error(errno)
|
||||||
|
}
|
||||||
|
return i64(n), n == 0 ? io.Error.EOF : nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (i64, Error) {
|
||||||
|
if offset < 0 {
|
||||||
|
return 0, .Invalid_Offset
|
||||||
|
}
|
||||||
|
|
||||||
|
n, errno := linux.pread(f.impl.fd, p[:], offset)
|
||||||
|
if errno != .NONE {
|
||||||
|
return -1, _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
return 0, .EOF
|
return 0, .EOF
|
||||||
@@ -127,91 +175,67 @@ _read :: proc(f: ^File, p: []byte) -> (i64, Error) {
|
|||||||
return i64(n), nil
|
return i64(n), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) {
|
|
||||||
if offset < 0 {
|
|
||||||
return 0, .Invalid_Offset
|
|
||||||
}
|
|
||||||
|
|
||||||
b, offset := p, offset
|
|
||||||
for len(b) > 0 {
|
|
||||||
m := unix.sys_pread(f.impl.fd, &b[0], len(b), offset)
|
|
||||||
if m < 0 {
|
|
||||||
return -1, _get_platform_error(m)
|
|
||||||
}
|
|
||||||
if m == 0 {
|
|
||||||
return 0, .EOF
|
|
||||||
}
|
|
||||||
n += i64(m)
|
|
||||||
b = b[m:]
|
|
||||||
offset += i64(m)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_write :: proc(f: ^File, p: []byte) -> (i64, Error) {
|
_write :: proc(f: ^File, p: []byte) -> (i64, Error) {
|
||||||
if len(p) == 0 {
|
if len(p) == 0 {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
n := unix.sys_write(f.impl.fd, &p[0], uint(len(p)))
|
n, errno := linux.write(f.impl.fd, p[:])
|
||||||
if n < 0 {
|
if errno != .NONE {
|
||||||
return -1, _get_platform_error(n)
|
return -1, _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
return i64(n), nil
|
return i64(n), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) {
|
_write_at :: proc(f: ^File, p: []byte, offset: i64) -> (i64, Error) {
|
||||||
if offset < 0 {
|
if offset < 0 {
|
||||||
return 0, .Invalid_Offset
|
return 0, .Invalid_Offset
|
||||||
}
|
}
|
||||||
|
|
||||||
b, offset := p, offset
|
n, errno := linux.pwrite(f.impl.fd, p[:], offset)
|
||||||
for len(b) > 0 {
|
if errno != .NONE {
|
||||||
m := unix.sys_pwrite(f.impl.fd, &b[0], len(b), offset)
|
return -1, _get_platform_error(errno)
|
||||||
if m < 0 {
|
|
||||||
return -1, _get_platform_error(m)
|
|
||||||
}
|
|
||||||
n += i64(m)
|
|
||||||
b = b[m:]
|
|
||||||
offset += i64(m)
|
|
||||||
}
|
}
|
||||||
return
|
return i64(n), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_file_size :: proc(f: ^File) -> (n: i64, err: Error) {
|
_file_size :: proc(f: ^File) -> (n: i64, err: Error) {
|
||||||
s: _Stat = ---
|
s: linux.Stat = ---
|
||||||
res := unix.sys_fstat(f.impl.fd, &s)
|
errno := linux.fstat(f.impl.fd, &s)
|
||||||
if res < 0 {
|
if errno != .NONE {
|
||||||
return -1, _get_platform_error(res)
|
return -1, _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
return s.size, nil
|
return i64(s.size), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_sync :: proc(f: ^File) -> Error {
|
_sync :: proc(f: ^File) -> Error {
|
||||||
return _ok_or_error(unix.sys_fsync(f.impl.fd))
|
return _get_platform_error(linux.fsync(f.impl.fd))
|
||||||
}
|
}
|
||||||
|
|
||||||
_flush :: proc(f: ^File) -> Error {
|
_flush :: proc(f: ^File) -> Error {
|
||||||
return _ok_or_error(unix.sys_fsync(f.impl.fd))
|
return _get_platform_error(linux.fsync(f.impl.fd))
|
||||||
}
|
}
|
||||||
|
|
||||||
_truncate :: proc(f: ^File, size: i64) -> Error {
|
_truncate :: proc(f: ^File, size: i64) -> Error {
|
||||||
return _ok_or_error(unix.sys_ftruncate(f.impl.fd, size))
|
return _get_platform_error(linux.ftruncate(f.impl.fd, size))
|
||||||
}
|
}
|
||||||
|
|
||||||
_remove :: proc(name: string) -> Error {
|
_remove :: proc(name: string) -> Error {
|
||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
name_cstr := temp_cstring(name) or_return
|
name_cstr := temp_cstring(name) or_return
|
||||||
|
|
||||||
fd := unix.sys_open(name_cstr, int(File_Flags.Read))
|
fd, errno := linux.open(name_cstr, {.NOFOLLOW})
|
||||||
if fd < 0 {
|
#partial switch (errno) {
|
||||||
return _get_platform_error(fd)
|
case .ELOOP: /* symlink */
|
||||||
|
case .NONE:
|
||||||
|
defer linux.close(fd)
|
||||||
|
if _is_dir_fd(fd) {
|
||||||
|
return _get_platform_error(linux.rmdir(name_cstr))
|
||||||
|
}
|
||||||
|
case:
|
||||||
|
return _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
defer unix.sys_close(fd)
|
|
||||||
|
|
||||||
if _is_dir_fd(fd) {
|
return _get_platform_error(linux.unlink(name_cstr))
|
||||||
return _ok_or_error(unix.sys_rmdir(name_cstr))
|
|
||||||
}
|
|
||||||
return _ok_or_error(unix.sys_unlink(name_cstr))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_rename :: proc(old_name, new_name: string) -> Error {
|
_rename :: proc(old_name, new_name: string) -> Error {
|
||||||
@@ -219,7 +243,7 @@ _rename :: proc(old_name, new_name: string) -> Error {
|
|||||||
old_name_cstr := temp_cstring(old_name) or_return
|
old_name_cstr := temp_cstring(old_name) or_return
|
||||||
new_name_cstr := temp_cstring(new_name) or_return
|
new_name_cstr := temp_cstring(new_name) or_return
|
||||||
|
|
||||||
return _ok_or_error(unix.sys_rename(old_name_cstr, new_name_cstr))
|
return _get_platform_error(linux.rename(old_name_cstr, new_name_cstr))
|
||||||
}
|
}
|
||||||
|
|
||||||
_link :: proc(old_name, new_name: string) -> Error {
|
_link :: proc(old_name, new_name: string) -> Error {
|
||||||
@@ -227,148 +251,194 @@ _link :: proc(old_name, new_name: string) -> Error {
|
|||||||
old_name_cstr := temp_cstring(old_name) or_return
|
old_name_cstr := temp_cstring(old_name) or_return
|
||||||
new_name_cstr := temp_cstring(new_name) or_return
|
new_name_cstr := temp_cstring(new_name) or_return
|
||||||
|
|
||||||
return _ok_or_error(unix.sys_link(old_name_cstr, new_name_cstr))
|
return _get_platform_error(linux.link(old_name_cstr, new_name_cstr))
|
||||||
}
|
}
|
||||||
|
|
||||||
_symlink :: proc(old_name, new_name: string) -> Error {
|
_symlink :: proc(old_name, new_name: string) -> Error {
|
||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
old_name_cstr := temp_cstring(old_name) or_return
|
old_name_cstr := temp_cstring(old_name) or_return
|
||||||
new_name_cstr := temp_cstring(new_name) or_return
|
new_name_cstr := temp_cstring(new_name) or_return
|
||||||
|
return _get_platform_error(linux.symlink(old_name_cstr, new_name_cstr))
|
||||||
return _ok_or_error(unix.sys_symlink(old_name_cstr, new_name_cstr))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_read_link_cstr :: proc(name_cstr: cstring, allocator: runtime.Allocator) -> (string, Error) {
|
_read_link_cstr :: proc(name_cstr: cstring, allocator: runtime.Allocator) -> (string, Error) {
|
||||||
bufsz : uint = 256
|
bufsz : uint = 256
|
||||||
buf := make([]byte, bufsz, allocator)
|
buf := make([]byte, bufsz, allocator)
|
||||||
for {
|
for {
|
||||||
rc := unix.sys_readlink(name_cstr, &buf[0], bufsz)
|
sz, errno := linux.readlink(name_cstr, buf[:])
|
||||||
if rc < 0 {
|
if errno != .NONE {
|
||||||
delete(buf)
|
delete(buf, allocator)
|
||||||
return "", _get_platform_error(rc)
|
return "", _get_platform_error(errno)
|
||||||
} else if rc == int(bufsz) {
|
} else if sz == int(bufsz) {
|
||||||
bufsz *= 2
|
bufsz *= 2
|
||||||
delete(buf)
|
delete(buf, allocator)
|
||||||
buf = make([]byte, bufsz, allocator)
|
buf = make([]byte, bufsz, allocator)
|
||||||
} else {
|
} else {
|
||||||
return string(buf[:rc]), nil
|
return string(buf[:sz]), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_read_link :: proc(name: string, allocator: runtime.Allocator) -> (path: string, err: Error) {
|
_read_link :: proc(name: string, allocator: runtime.Allocator) -> (s: string, e: Error) {
|
||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
name_cstr := temp_cstring(name) or_return
|
name_cstr := temp_cstring(name) or_return
|
||||||
return _read_link_cstr(name_cstr, allocator)
|
return _read_link_cstr(name_cstr, allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
_unlink :: proc(name: string) -> Error {
|
|
||||||
TEMP_ALLOCATOR_GUARD()
|
|
||||||
name_cstr := temp_cstring(name) or_return
|
|
||||||
return _ok_or_error(unix.sys_unlink(name_cstr))
|
|
||||||
}
|
|
||||||
|
|
||||||
_chdir :: proc(name: string) -> Error {
|
_chdir :: proc(name: string) -> Error {
|
||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
name_cstr := temp_cstring(name) or_return
|
name_cstr := temp_cstring(name) or_return
|
||||||
return _ok_or_error(unix.sys_chdir(name_cstr))
|
return _get_platform_error(linux.chdir(name_cstr))
|
||||||
}
|
}
|
||||||
|
|
||||||
_fchdir :: proc(f: ^File) -> Error {
|
_fchdir :: proc(f: ^File) -> Error {
|
||||||
return _ok_or_error(unix.sys_fchdir(f.impl.fd))
|
return _get_platform_error(linux.fchdir(f.impl.fd))
|
||||||
}
|
}
|
||||||
|
|
||||||
_chmod :: proc(name: string, mode: File_Mode) -> Error {
|
_chmod :: proc(name: string, mode: File_Mode) -> Error {
|
||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
name_cstr := temp_cstring(name) or_return
|
name_cstr := temp_cstring(name) or_return
|
||||||
return _ok_or_error(unix.sys_chmod(name_cstr, uint(mode)))
|
return _get_platform_error(linux.chmod(name_cstr, transmute(linux.Mode)(u32(mode))))
|
||||||
}
|
}
|
||||||
|
|
||||||
_fchmod :: proc(f: ^File, mode: File_Mode) -> Error {
|
_fchmod :: proc(f: ^File, mode: File_Mode) -> Error {
|
||||||
return _ok_or_error(unix.sys_fchmod(f.impl.fd, uint(mode)))
|
return _get_platform_error(linux.fchmod(f.impl.fd, transmute(linux.Mode)(u32(mode))))
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: will throw error without super user priviledges
|
// NOTE: will throw error without super user priviledges
|
||||||
_chown :: proc(name: string, uid, gid: int) -> Error {
|
_chown :: proc(name: string, uid, gid: int) -> Error {
|
||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
name_cstr := temp_cstring(name) or_return
|
name_cstr := temp_cstring(name) or_return
|
||||||
return _ok_or_error(unix.sys_chown(name_cstr, uid, gid))
|
return _get_platform_error(linux.chown(name_cstr, linux.Uid(uid), linux.Gid(gid)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: will throw error without super user priviledges
|
// NOTE: will throw error without super user priviledges
|
||||||
_lchown :: proc(name: string, uid, gid: int) -> Error {
|
_lchown :: proc(name: string, uid, gid: int) -> Error {
|
||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
name_cstr := temp_cstring(name) or_return
|
name_cstr := temp_cstring(name) or_return
|
||||||
return _ok_or_error(unix.sys_lchown(name_cstr, uid, gid))
|
return _get_platform_error(linux.lchown(name_cstr, linux.Uid(uid), linux.Gid(gid)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: will throw error without super user priviledges
|
// NOTE: will throw error without super user priviledges
|
||||||
_fchown :: proc(f: ^File, uid, gid: int) -> Error {
|
_fchown :: proc(f: ^File, uid, gid: int) -> Error {
|
||||||
return _ok_or_error(unix.sys_fchown(f.impl.fd, uid, gid))
|
return _get_platform_error(linux.fchown(f.impl.fd, linux.Uid(uid), linux.Gid(gid)))
|
||||||
}
|
}
|
||||||
|
|
||||||
_chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
|
_chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
|
||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
name_cstr := temp_cstring(name) or_return
|
name_cstr := temp_cstring(name) or_return
|
||||||
times := [2]Unix_File_Time {
|
times := [2]linux.Time_Spec {
|
||||||
{ atime._nsec, 0 },
|
{
|
||||||
{ mtime._nsec, 0 },
|
uint(atime._nsec) / uint(time.Second),
|
||||||
|
uint(atime._nsec) % uint(time.Second),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uint(mtime._nsec) / uint(time.Second),
|
||||||
|
uint(mtime._nsec) % uint(time.Second),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
return _ok_or_error(unix.sys_utimensat(_AT_FDCWD, name_cstr, ×, 0))
|
return _get_platform_error(linux.utimensat(linux.AT_FDCWD, name_cstr, ×[0], nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
_fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
|
_fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
|
||||||
times := [2]Unix_File_Time {
|
times := [2]linux.Time_Spec {
|
||||||
{ atime._nsec, 0 },
|
{
|
||||||
{ mtime._nsec, 0 },
|
uint(atime._nsec) / uint(time.Second),
|
||||||
|
uint(atime._nsec) % uint(time.Second),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uint(mtime._nsec) / uint(time.Second),
|
||||||
|
uint(mtime._nsec) % uint(time.Second),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
return _ok_or_error(unix.sys_utimensat(f.impl.fd, nil, ×, 0))
|
return _get_platform_error(linux.utimensat(f.impl.fd, nil, ×[0], nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
_exists :: proc(name: string) -> bool {
|
_exists :: proc(name: string) -> bool {
|
||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
name_cstr, _ := temp_cstring(name)
|
name_cstr, _ := temp_cstring(name)
|
||||||
return unix.sys_access(name_cstr, F_OK) == 0
|
res, errno := linux.access(name_cstr, linux.F_OK)
|
||||||
|
return !res && errno == .NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
_is_file :: proc(name: string) -> bool {
|
_is_file :: proc(name: string) -> bool {
|
||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
name_cstr, _ := temp_cstring(name)
|
name_cstr, _ := temp_cstring(name)
|
||||||
s: _Stat
|
s: linux.Stat
|
||||||
res := unix.sys_stat(name_cstr, &s)
|
if linux.stat(name_cstr, &s) != .NONE {
|
||||||
if res < 0 {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return S_ISREG(s.mode)
|
return linux.S_ISREG(s.mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
_is_file_fd :: proc(fd: int) -> bool {
|
_is_file_fd :: proc(fd: linux.Fd) -> bool {
|
||||||
s: _Stat
|
s: linux.Stat
|
||||||
res := unix.sys_fstat(fd, &s)
|
if linux.fstat(fd, &s) != .NONE {
|
||||||
if res < 0 { // error
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return S_ISREG(s.mode)
|
return linux.S_ISREG(s.mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
_is_dir :: proc(name: string) -> bool {
|
_is_dir :: proc(name: string) -> bool {
|
||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
name_cstr, _ := temp_cstring(name)
|
name_cstr, _ := temp_cstring(name)
|
||||||
s: _Stat
|
s: linux.Stat
|
||||||
res := unix.sys_stat(name_cstr, &s)
|
if linux.stat(name_cstr, &s) != .NONE {
|
||||||
if res < 0 {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return S_ISDIR(s.mode)
|
return linux.S_ISDIR(s.mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
_is_dir_fd :: proc(fd: int) -> bool {
|
_is_dir_fd :: proc(fd: linux.Fd) -> bool {
|
||||||
s: _Stat
|
s: linux.Stat
|
||||||
res := unix.sys_fstat(fd, &s)
|
if linux.fstat(fd, &s) != .NONE {
|
||||||
if res < 0 { // error
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return S_ISDIR(s.mode)
|
return linux.S_ISDIR(s.mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Certain files in the Linux file system are not actual
|
||||||
|
* files (e.g. everything in /proc/). Therefore, the
|
||||||
|
* read_entire_file procs fail to actually read anything
|
||||||
|
* since these "files" stat to a size of 0. Here, we just
|
||||||
|
* read until there is nothing left.
|
||||||
|
*/
|
||||||
|
_read_entire_pseudo_file :: proc { _read_entire_pseudo_file_string, _read_entire_pseudo_file_cstring }
|
||||||
|
|
||||||
|
_read_entire_pseudo_file_string :: proc(name: string, allocator: runtime.Allocator) -> (b: []u8, e: Error) {
|
||||||
|
name_cstr := clone_to_cstring(name, allocator) or_return
|
||||||
|
defer delete(name, allocator)
|
||||||
|
return _read_entire_pseudo_file_cstring(name_cstr, allocator)
|
||||||
|
}
|
||||||
|
|
||||||
|
_read_entire_pseudo_file_cstring :: proc(name: cstring, allocator: runtime.Allocator) -> ([]u8, Error) {
|
||||||
|
fd, errno := linux.open(name, {})
|
||||||
|
if errno != .NONE {
|
||||||
|
return nil, _get_platform_error(errno)
|
||||||
|
}
|
||||||
|
defer linux.close(fd)
|
||||||
|
|
||||||
|
BUF_SIZE_STEP :: 128
|
||||||
|
contents := make([dynamic]u8, 0, BUF_SIZE_STEP, allocator)
|
||||||
|
|
||||||
|
n: int
|
||||||
|
i: int
|
||||||
|
for {
|
||||||
|
resize(&contents, i + BUF_SIZE_STEP)
|
||||||
|
n, errno = linux.read(fd, contents[i:i+BUF_SIZE_STEP])
|
||||||
|
if errno != .NONE {
|
||||||
|
delete(contents)
|
||||||
|
return nil, _get_platform_error(errno)
|
||||||
|
}
|
||||||
|
if n < BUF_SIZE_STEP {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i += BUF_SIZE_STEP
|
||||||
|
}
|
||||||
|
|
||||||
|
resize(&contents, i + n)
|
||||||
|
|
||||||
|
return contents[:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private="package")
|
@(private="package")
|
||||||
|
|||||||
+11
-17
@@ -1,7 +1,7 @@
|
|||||||
//+private
|
//+private
|
||||||
package os2
|
package os2
|
||||||
|
|
||||||
import "core:sys/unix"
|
import "core:sys/linux"
|
||||||
import "core:sync"
|
import "core:sync"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
|
||||||
@@ -97,9 +97,8 @@ CURRENTLY_ACTIVE :: (^^Region)(~uintptr(0))
|
|||||||
|
|
||||||
FREE_LIST_ENTRIES_PER_BLOCK :: BLOCK_SIZE / size_of(u16)
|
FREE_LIST_ENTRIES_PER_BLOCK :: BLOCK_SIZE / size_of(u16)
|
||||||
|
|
||||||
MMAP_FLAGS :: unix.MAP_ANONYMOUS | unix.MAP_PRIVATE
|
MMAP_FLAGS : linux.Map_Flags : {.ANONYMOUS, .PRIVATE}
|
||||||
MMAP_PROT :: unix.PROT_READ | unix.PROT_WRITE
|
MMAP_PROT : linux.Mem_Protection : {.READ, .WRITE}
|
||||||
|
|
||||||
|
|
||||||
@thread_local _local_region: ^Region
|
@thread_local _local_region: ^Region
|
||||||
global_regions: ^Region
|
global_regions: ^Region
|
||||||
@@ -324,11 +323,11 @@ heap_free :: proc(memory: rawptr) {
|
|||||||
// Regions
|
// Regions
|
||||||
//
|
//
|
||||||
_new_region :: proc() -> ^Region #no_bounds_check {
|
_new_region :: proc() -> ^Region #no_bounds_check {
|
||||||
res := unix.sys_mmap(nil, uint(SIZE_OF_REGION), MMAP_PROT, MMAP_FLAGS, -1, 0)
|
ptr, errno := linux.mmap(0, uint(SIZE_OF_REGION), MMAP_PROT, MMAP_FLAGS, -1, 0)
|
||||||
if res < 0 {
|
if errno != .NONE {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
new_region := (^Region)(uintptr(res))
|
new_region := (^Region)(ptr)
|
||||||
|
|
||||||
new_region.hdr.local_addr = CURRENTLY_ACTIVE
|
new_region.hdr.local_addr = CURRENTLY_ACTIVE
|
||||||
new_region.hdr.reset_addr = &_local_region
|
new_region.hdr.reset_addr = &_local_region
|
||||||
@@ -634,8 +633,8 @@ _region_free_list_remove :: proc(region: ^Region, free_idx: u16) #no_bounds_chec
|
|||||||
//
|
//
|
||||||
_direct_mmap_alloc :: proc(size: int) -> rawptr {
|
_direct_mmap_alloc :: proc(size: int) -> rawptr {
|
||||||
mmap_size := _round_up_to_nearest(size + BLOCK_SIZE, PAGE_SIZE)
|
mmap_size := _round_up_to_nearest(size + BLOCK_SIZE, PAGE_SIZE)
|
||||||
new_allocation := unix.sys_mmap(nil, uint(mmap_size), MMAP_PROT, MMAP_FLAGS, -1, 0)
|
new_allocation, errno := linux.mmap(0, uint(mmap_size), MMAP_PROT, MMAP_FLAGS, -1, 0)
|
||||||
if new_allocation < 0 && new_allocation > -4096 {
|
if errno != .NONE {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -655,13 +654,8 @@ _direct_mmap_resize :: proc(alloc: ^Allocation_Header, new_size: int) -> rawptr
|
|||||||
return mem.ptr_offset(alloc, 1)
|
return mem.ptr_offset(alloc, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
new_allocation := unix.sys_mremap(
|
new_allocation, errno := linux.mremap(alloc, uint(old_mmap_size), uint(new_mmap_size), {.MAYMOVE})
|
||||||
alloc,
|
if errno != .NONE {
|
||||||
uint(old_mmap_size),
|
|
||||||
uint(new_mmap_size),
|
|
||||||
unix.MREMAP_MAYMOVE,
|
|
||||||
)
|
|
||||||
if new_allocation < 0 && new_allocation > -4096 {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -702,7 +696,7 @@ _direct_mmap_to_region :: proc(alloc: ^Allocation_Header, new_size: int) -> rawp
|
|||||||
_direct_mmap_free :: proc(alloc: ^Allocation_Header) {
|
_direct_mmap_free :: proc(alloc: ^Allocation_Header) {
|
||||||
requested := int(alloc.requested & REQUESTED_MASK)
|
requested := int(alloc.requested & REQUESTED_MASK)
|
||||||
mmap_size := _round_up_to_nearest(requested + BLOCK_SIZE, PAGE_SIZE)
|
mmap_size := _round_up_to_nearest(requested + BLOCK_SIZE, PAGE_SIZE)
|
||||||
unix.sys_munmap(alloc, uint(mmap_size))
|
linux.munmap(alloc, uint(mmap_size))
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
+70
-86
@@ -3,104 +3,89 @@ package os2
|
|||||||
|
|
||||||
import "core:strconv"
|
import "core:strconv"
|
||||||
import "base:runtime"
|
import "base:runtime"
|
||||||
import "core:sys/unix"
|
import "core:sys/linux"
|
||||||
|
|
||||||
_Path_Separator :: '/'
|
_Path_Separator :: '/'
|
||||||
_Path_Separator_String :: "/"
|
_Path_Separator_String :: "/"
|
||||||
_Path_List_Separator :: ':'
|
_Path_List_Separator :: ':'
|
||||||
|
|
||||||
_S_IFMT :: 0o170000 // Type of file mask
|
_OPENDIR_FLAGS : linux.Open_Flags : {.NONBLOCK, .DIRECTORY, .LARGEFILE, .CLOEXEC}
|
||||||
_S_IFIFO :: 0o010000 // Named pipe (fifo)
|
|
||||||
_S_IFCHR :: 0o020000 // Character special
|
|
||||||
_S_IFDIR :: 0o040000 // Directory
|
|
||||||
_S_IFBLK :: 0o060000 // Block special
|
|
||||||
_S_IFREG :: 0o100000 // Regular
|
|
||||||
_S_IFLNK :: 0o120000 // Symbolic link
|
|
||||||
_S_IFSOCK :: 0o140000 // Socket
|
|
||||||
|
|
||||||
_OPENDIR_FLAGS :: _O_RDONLY|_O_NONBLOCK|_O_DIRECTORY|_O_LARGEFILE|_O_CLOEXEC
|
|
||||||
|
|
||||||
_is_path_separator :: proc(c: byte) -> bool {
|
_is_path_separator :: proc(c: byte) -> bool {
|
||||||
return c == '/'
|
return c == '/'
|
||||||
}
|
}
|
||||||
|
|
||||||
_mkdir :: proc(path: string, perm: File_Mode) -> Error {
|
_mkdir :: proc(path: string, perm: File_Mode) -> Error {
|
||||||
// NOTE: These modes would require sys_mknod, however, that would require
|
// TODO: These modes would require mknod, however, that would also
|
||||||
// additional arguments to this function.
|
// require additional arguments to this function..
|
||||||
if perm & (File_Mode_Named_Pipe | File_Mode_Device | File_Mode_Char_Device | File_Mode_Sym_Link) != 0 {
|
if perm & (File_Mode_Named_Pipe | File_Mode_Device | File_Mode_Char_Device | File_Mode_Sym_Link) != 0 {
|
||||||
return .Invalid_Argument
|
return .Invalid_Argument
|
||||||
}
|
}
|
||||||
|
|
||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
path_cstr := temp_cstring(path) or_return
|
path_cstr := temp_cstring(path) or_return
|
||||||
return _ok_or_error(unix.sys_mkdir(path_cstr, uint(perm & 0o777)))
|
return _get_platform_error(linux.mkdir(path_cstr, transmute(linux.Mode)(u32(perm) & 0o777)))
|
||||||
}
|
}
|
||||||
|
|
||||||
_mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
|
_mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
|
||||||
_mkdirat :: proc(dfd: int, path: []u8, perm: int, has_created: ^bool) -> Error {
|
mkdirat :: proc(dfd: linux.Fd, path: []u8, perm: int, has_created: ^bool) -> Error {
|
||||||
if len(path) == 0 {
|
|
||||||
return _ok_or_error(unix.sys_close(dfd))
|
|
||||||
}
|
|
||||||
i: int
|
i: int
|
||||||
for /**/; i < len(path) - 1 && path[i] != '/'; i += 1 {}
|
for ; i < len(path) - 1 && path[i] != '/'; i += 1 {}
|
||||||
|
if i == 0 {
|
||||||
|
return _get_platform_error(linux.close(dfd))
|
||||||
|
}
|
||||||
path[i] = 0
|
path[i] = 0
|
||||||
new_dfd := unix.sys_openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS)
|
new_dfd, errno := linux.openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS)
|
||||||
switch new_dfd {
|
#partial switch errno {
|
||||||
case -ENOENT:
|
case .ENOENT:
|
||||||
if res := unix.sys_mkdirat(dfd, cstring(&path[0]), uint(perm)); res < 0 {
|
if errno = linux.mkdirat(dfd, cstring(&path[0]), transmute(linux.Mode)(u32(perm))); errno != .NONE {
|
||||||
return _get_platform_error(res)
|
return _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
has_created^ = true
|
has_created^ = true
|
||||||
if new_dfd = unix.sys_openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS); new_dfd < 0 {
|
if new_dfd, errno = linux.openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS); errno != .NONE {
|
||||||
return _get_platform_error(new_dfd)
|
return _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
fallthrough
|
fallthrough
|
||||||
case 0:
|
case .NONE:
|
||||||
if res := unix.sys_close(dfd); res < 0 {
|
if errno = linux.close(dfd); errno != .NONE {
|
||||||
return _get_platform_error(res)
|
return _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
// skip consecutive '/'
|
// skip consecutive '/'
|
||||||
for i += 1; i < len(path) && path[i] == '/'; i += 1 {}
|
for i += 1; i < len(path) && path[i] == '/'; i += 1 {}
|
||||||
return _mkdirat(new_dfd, path[i:], perm, has_created)
|
return mkdirat(new_dfd, path[i:], perm, has_created)
|
||||||
case:
|
case:
|
||||||
return _get_platform_error(new_dfd)
|
return _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
unreachable()
|
unreachable()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
if perm & (File_Mode_Named_Pipe | File_Mode_Device | File_Mode_Char_Device | File_Mode_Sym_Link) != 0 {
|
if perm & (File_Mode_Named_Pipe | File_Mode_Device | File_Mode_Char_Device | File_Mode_Sym_Link) != 0 {
|
||||||
return .Invalid_Argument
|
return .Invalid_Argument
|
||||||
}
|
}
|
||||||
|
|
||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
|
|
||||||
// need something we can edit, and use to generate cstrings
|
// need something we can edit, and use to generate cstrings
|
||||||
allocated: bool
|
path_bytes := make([]u8, len(path) + 1, temp_allocator())
|
||||||
path_bytes: []u8
|
|
||||||
if len(path) > _CSTRING_NAME_HEAP_THRESHOLD {
|
|
||||||
allocated = true
|
|
||||||
path_bytes = make([]u8, len(path) + 1)
|
|
||||||
} else {
|
|
||||||
path_bytes = make([]u8, len(path) + 1, temp_allocator())
|
|
||||||
}
|
|
||||||
|
|
||||||
// NULL terminate the byte slice to make it a valid cstring
|
// zero terminate the byte slice to make it a valid cstring
|
||||||
copy(path_bytes, path)
|
copy(path_bytes, path)
|
||||||
path_bytes[len(path)] = 0
|
path_bytes[len(path)] = 0
|
||||||
|
|
||||||
dfd: int
|
dfd: linux.Fd
|
||||||
|
errno: linux.Errno
|
||||||
if path_bytes[0] == '/' {
|
if path_bytes[0] == '/' {
|
||||||
dfd = unix.sys_open("/", _OPENDIR_FLAGS)
|
dfd, errno = linux.open("/", _OPENDIR_FLAGS)
|
||||||
path_bytes = path_bytes[1:]
|
path_bytes = path_bytes[1:]
|
||||||
} else {
|
} else {
|
||||||
dfd = unix.sys_open(".", _OPENDIR_FLAGS)
|
dfd, errno = linux.open(".", _OPENDIR_FLAGS)
|
||||||
}
|
}
|
||||||
if dfd < 0 {
|
if errno != .NONE {
|
||||||
return _get_platform_error(dfd)
|
return _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
|
|
||||||
has_created: bool
|
has_created: bool
|
||||||
_mkdirat(dfd, path_bytes, int(perm & 0o777), &has_created) or_return
|
mkdirat(dfd, path_bytes, int(perm & 0o777), &has_created) or_return
|
||||||
if has_created {
|
if has_created {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -119,28 +104,28 @@ dirent64 :: struct {
|
|||||||
_remove_all :: proc(path: string) -> Error {
|
_remove_all :: proc(path: string) -> Error {
|
||||||
DT_DIR :: 4
|
DT_DIR :: 4
|
||||||
|
|
||||||
_remove_all_dir :: proc(dfd: int) -> Error {
|
remove_all_dir :: proc(dfd: linux.Fd) -> Error {
|
||||||
n := 64
|
n := 64
|
||||||
buf := make([]u8, n)
|
buf := make([]u8, n)
|
||||||
defer delete(buf)
|
defer delete(buf)
|
||||||
|
|
||||||
loop: for {
|
loop: for {
|
||||||
getdents_res := unix.sys_getdents64(dfd, &buf[0], n)
|
buflen, errno := linux.getdents(dfd, buf[:])
|
||||||
switch getdents_res {
|
#partial switch errno {
|
||||||
case -EINVAL:
|
case .EINVAL:
|
||||||
delete(buf)
|
delete(buf)
|
||||||
n *= 2
|
n *= 2
|
||||||
buf = make([]u8, n)
|
buf = make([]u8, n)
|
||||||
continue loop
|
continue loop
|
||||||
case -4096..<0:
|
case .NONE:
|
||||||
return _get_platform_error(getdents_res)
|
if buflen == 0 { break loop }
|
||||||
case 0:
|
case:
|
||||||
break loop
|
return _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
|
|
||||||
d: ^dirent64
|
d: ^dirent64
|
||||||
|
|
||||||
for i := 0; i < getdents_res; i += int(d.d_reclen) {
|
for i := 0; i < buflen; i += int(d.d_reclen) {
|
||||||
d = (^dirent64)(rawptr(&buf[i]))
|
d = (^dirent64)(rawptr(&buf[i]))
|
||||||
d_name_cstr := cstring(&d.d_name[0])
|
d_name_cstr := cstring(&d.d_name[0])
|
||||||
|
|
||||||
@@ -156,23 +141,22 @@ _remove_all :: proc(path: string) -> Error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
unlink_res: int
|
|
||||||
|
|
||||||
switch d.d_type {
|
switch d.d_type {
|
||||||
case DT_DIR:
|
case DT_DIR:
|
||||||
new_dfd := unix.sys_openat(dfd, d_name_cstr, _OPENDIR_FLAGS)
|
new_dfd: linux.Fd
|
||||||
if new_dfd < 0 {
|
new_dfd, errno = linux.openat(dfd, d_name_cstr, _OPENDIR_FLAGS)
|
||||||
return _get_platform_error(new_dfd)
|
if errno != .NONE {
|
||||||
|
return _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
defer unix.sys_close(new_dfd)
|
defer linux.close(new_dfd)
|
||||||
_remove_all_dir(new_dfd) or_return
|
remove_all_dir(new_dfd) or_return
|
||||||
unlink_res = unix.sys_unlinkat(dfd, d_name_cstr, int(unix.AT_REMOVEDIR))
|
errno = linux.unlinkat(dfd, d_name_cstr, {.REMOVEDIR})
|
||||||
case:
|
case:
|
||||||
unlink_res = unix.sys_unlinkat(dfd, d_name_cstr)
|
errno = linux.unlinkat(dfd, d_name_cstr, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
if unlink_res < 0 {
|
if errno != .NONE {
|
||||||
return _get_platform_error(unlink_res)
|
return _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -182,17 +166,19 @@ _remove_all :: proc(path: string) -> Error {
|
|||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
path_cstr := temp_cstring(path) or_return
|
path_cstr := temp_cstring(path) or_return
|
||||||
|
|
||||||
fd := unix.sys_open(path_cstr, _OPENDIR_FLAGS)
|
fd, errno := linux.open(path_cstr, _OPENDIR_FLAGS)
|
||||||
switch fd {
|
#partial switch errno {
|
||||||
case -ENOTDIR:
|
case .NONE:
|
||||||
return _ok_or_error(unix.sys_unlink(path_cstr))
|
break
|
||||||
case -4096..<0:
|
case .ENOTDIR:
|
||||||
return _get_platform_error(fd)
|
return _get_platform_error(linux.unlink(path_cstr))
|
||||||
|
case:
|
||||||
|
return _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer unix.sys_close(fd)
|
defer linux.close(fd)
|
||||||
_remove_all_dir(fd) or_return
|
remove_all_dir(fd) or_return
|
||||||
return _ok_or_error(unix.sys_rmdir(path_cstr))
|
return _get_platform_error(linux.rmdir(path_cstr))
|
||||||
}
|
}
|
||||||
|
|
||||||
_getwd :: proc(allocator: runtime.Allocator) -> (string, Error) {
|
_getwd :: proc(allocator: runtime.Allocator) -> (string, Error) {
|
||||||
@@ -203,13 +189,12 @@ _getwd :: proc(allocator: runtime.Allocator) -> (string, Error) {
|
|||||||
PATH_MAX :: 4096
|
PATH_MAX :: 4096
|
||||||
buf := make([dynamic]u8, PATH_MAX, allocator)
|
buf := make([dynamic]u8, PATH_MAX, allocator)
|
||||||
for {
|
for {
|
||||||
#no_bounds_check res := unix.sys_getcwd(&buf[0], uint(len(buf)))
|
#no_bounds_check n, errno := linux.getcwd(buf[:])
|
||||||
|
if errno == .NONE {
|
||||||
if res >= 0 {
|
return string(buf[:n-1]), nil
|
||||||
return string_from_null_terminated_bytes(buf[:]), nil
|
|
||||||
}
|
}
|
||||||
if res != -ERANGE {
|
if errno != .ERANGE {
|
||||||
return "", _get_platform_error(res)
|
return "", _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
resize(&buf, len(buf)+PATH_MAX)
|
resize(&buf, len(buf)+PATH_MAX)
|
||||||
}
|
}
|
||||||
@@ -218,16 +203,16 @@ _getwd :: proc(allocator: runtime.Allocator) -> (string, Error) {
|
|||||||
|
|
||||||
_setwd :: proc(dir: string) -> Error {
|
_setwd :: proc(dir: string) -> Error {
|
||||||
dir_cstr := temp_cstring(dir) or_return
|
dir_cstr := temp_cstring(dir) or_return
|
||||||
return _ok_or_error(unix.sys_chdir(dir_cstr))
|
return _get_platform_error(linux.chdir(dir_cstr))
|
||||||
}
|
}
|
||||||
|
|
||||||
_get_full_path :: proc(fd: int, allocator: runtime.Allocator) -> string {
|
_get_full_path :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> string {
|
||||||
PROC_FD_PATH :: "/proc/self/fd/"
|
PROC_FD_PATH :: "/proc/self/fd/"
|
||||||
|
|
||||||
buf: [32]u8
|
buf: [32]u8
|
||||||
copy(buf[:], PROC_FD_PATH)
|
copy(buf[:], PROC_FD_PATH)
|
||||||
|
|
||||||
strconv.itoa(buf[len(PROC_FD_PATH):], fd)
|
strconv.itoa(buf[len(PROC_FD_PATH):], int(fd))
|
||||||
|
|
||||||
fullpath: string
|
fullpath: string
|
||||||
err: Error
|
err: Error
|
||||||
@@ -236,4 +221,3 @@ _get_full_path :: proc(fd: int, allocator: runtime.Allocator) -> string {
|
|||||||
}
|
}
|
||||||
return fullpath
|
return fullpath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,17 @@
|
|||||||
//+private
|
//+private
|
||||||
package os2
|
package os2
|
||||||
|
|
||||||
|
import "core:sys/linux"
|
||||||
|
|
||||||
_pipe :: proc() -> (r, w: ^File, err: Error) {
|
_pipe :: proc() -> (r, w: ^File, err: Error) {
|
||||||
return nil, nil, nil
|
fds: [2]linux.Fd
|
||||||
|
errno := linux.pipe2(&fds, {.CLOEXEC})
|
||||||
|
if errno != .NONE {
|
||||||
|
return nil, nil,_get_platform_error(errno)
|
||||||
|
}
|
||||||
|
|
||||||
|
r = _new_file(uintptr(fds[0]))
|
||||||
|
w = _new_file(uintptr(fds[1]))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+20
-96
@@ -3,108 +3,32 @@ package os2
|
|||||||
|
|
||||||
import "core:time"
|
import "core:time"
|
||||||
import "base:runtime"
|
import "base:runtime"
|
||||||
import "core:sys/unix"
|
import "core:sys/linux"
|
||||||
import "core:path/filepath"
|
import "core:path/filepath"
|
||||||
|
|
||||||
// File type
|
|
||||||
S_IFMT :: 0o170000 // Type of file mask
|
|
||||||
S_IFIFO :: 0o010000 // Named pipe (fifo)
|
|
||||||
S_IFCHR :: 0o020000 // Character special
|
|
||||||
S_IFDIR :: 0o040000 // Directory
|
|
||||||
S_IFBLK :: 0o060000 // Block special
|
|
||||||
S_IFREG :: 0o100000 // Regular
|
|
||||||
S_IFLNK :: 0o120000 // Symbolic link
|
|
||||||
S_IFSOCK :: 0o140000 // Socket
|
|
||||||
|
|
||||||
// File mode
|
|
||||||
// Read, write, execute/search by owner
|
|
||||||
S_IRWXU :: 0o0700 // RWX mask for owner
|
|
||||||
S_IRUSR :: 0o0400 // R for owner
|
|
||||||
S_IWUSR :: 0o0200 // W for owner
|
|
||||||
S_IXUSR :: 0o0100 // X for owner
|
|
||||||
|
|
||||||
// Read, write, execute/search by group
|
|
||||||
S_IRWXG :: 0o0070 // RWX mask for group
|
|
||||||
S_IRGRP :: 0o0040 // R for group
|
|
||||||
S_IWGRP :: 0o0020 // W for group
|
|
||||||
S_IXGRP :: 0o0010 // X for group
|
|
||||||
|
|
||||||
// Read, write, execute/search by others
|
|
||||||
S_IRWXO :: 0o0007 // RWX mask for other
|
|
||||||
S_IROTH :: 0o0004 // R for other
|
|
||||||
S_IWOTH :: 0o0002 // W for other
|
|
||||||
S_IXOTH :: 0o0001 // X for other
|
|
||||||
|
|
||||||
S_ISUID :: 0o4000 // Set user id on execution
|
|
||||||
S_ISGID :: 0o2000 // Set group id on execution
|
|
||||||
S_ISVTX :: 0o1000 // Directory restrcted delete
|
|
||||||
|
|
||||||
|
|
||||||
S_ISLNK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK }
|
|
||||||
S_ISREG :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG }
|
|
||||||
S_ISDIR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR }
|
|
||||||
S_ISCHR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR }
|
|
||||||
S_ISBLK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK }
|
|
||||||
S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO }
|
|
||||||
S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK }
|
|
||||||
|
|
||||||
F_OK :: 0 // Test for file existance
|
|
||||||
X_OK :: 1 // Test for execute permission
|
|
||||||
W_OK :: 2 // Test for write permission
|
|
||||||
R_OK :: 4 // Test for read permission
|
|
||||||
|
|
||||||
@private
|
|
||||||
Unix_File_Time :: struct {
|
|
||||||
seconds: i64,
|
|
||||||
nanoseconds: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
@private
|
|
||||||
_Stat :: struct {
|
|
||||||
device_id: u64, // ID of device containing file
|
|
||||||
serial: u64, // File serial number
|
|
||||||
nlink: u64, // Number of hard links
|
|
||||||
mode: u32, // Mode of the file
|
|
||||||
uid: u32, // User ID of the file's owner
|
|
||||||
gid: u32, // Group ID of the file's group
|
|
||||||
_padding: i32, // 32 bits of padding
|
|
||||||
rdev: u64, // Device ID, if device
|
|
||||||
size: i64, // Size of the file, in bytes
|
|
||||||
block_size: i64, // Optimal bllocksize for I/O
|
|
||||||
blocks: i64, // Number of 512-byte blocks allocated
|
|
||||||
|
|
||||||
last_access: Unix_File_Time, // Time of last access
|
|
||||||
modified: Unix_File_Time, // Time of last modification
|
|
||||||
status_change: Unix_File_Time, // Time of last status change
|
|
||||||
|
|
||||||
_reserve1,
|
|
||||||
_reserve2,
|
|
||||||
_reserve3: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
_fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) {
|
_fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) {
|
||||||
return _fstat_internal(f.impl.fd, allocator)
|
return _fstat_internal(f.impl.fd, allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
_fstat_internal :: proc(fd: int, allocator: runtime.Allocator) -> (File_Info, Error) {
|
_fstat_internal :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> (File_Info, Error) {
|
||||||
s: _Stat
|
s: linux.Stat
|
||||||
result := unix.sys_fstat(fd, &s)
|
errno := linux.fstat(fd, &s)
|
||||||
if result < 0 {
|
if errno != .NONE {
|
||||||
return {}, _get_platform_error(result)
|
return {}, _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: As of Linux 4.11, the new statx syscall can retrieve creation_time
|
// TODO: As of Linux 4.11, the new statx syscall can retrieve creation_time
|
||||||
fi := File_Info {
|
fi := File_Info {
|
||||||
fullpath = _get_full_path(fd, allocator),
|
fullpath = _get_full_path(fd, allocator),
|
||||||
name = "",
|
name = "",
|
||||||
size = s.size,
|
size = i64(s.size),
|
||||||
mode = 0,
|
mode = 0,
|
||||||
is_directory = S_ISDIR(s.mode),
|
is_directory = linux.S_ISDIR(s.mode),
|
||||||
modification_time = time.Time {s.modified.seconds},
|
modification_time = time.Time {i64(s.mtime.time_sec) * i64(time.Second) + i64(s.mtime.time_nsec)},
|
||||||
access_time = time.Time {s.last_access.seconds},
|
access_time = time.Time {i64(s.atime.time_sec) * i64(time.Second) + i64(s.atime.time_nsec)},
|
||||||
creation_time = time.Time{0}, // regular stat does not provide this
|
creation_time = time.Time{i64(s.ctime.time_sec) * i64(time.Second) + i64(s.ctime.time_nsec)}, // regular stat does not provide this
|
||||||
}
|
}
|
||||||
|
fi.creation_time = fi.modification_time
|
||||||
|
|
||||||
fi.name = filepath.base(fi.fullpath)
|
fi.name = filepath.base(fi.fullpath)
|
||||||
return fi, nil
|
return fi, nil
|
||||||
@@ -115,11 +39,11 @@ _stat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err
|
|||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
name_cstr := temp_cstring(name) or_return
|
name_cstr := temp_cstring(name) or_return
|
||||||
|
|
||||||
fd := unix.sys_open(name_cstr, _O_RDONLY)
|
fd, errno := linux.open(name_cstr, {})
|
||||||
if fd < 0 {
|
if errno != .NONE {
|
||||||
return {}, _get_platform_error(fd)
|
return {}, _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
defer unix.sys_close(fd)
|
defer linux.close(fd)
|
||||||
return _fstat_internal(fd, allocator)
|
return _fstat_internal(fd, allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,11 +51,11 @@ _lstat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, er
|
|||||||
TEMP_ALLOCATOR_GUARD()
|
TEMP_ALLOCATOR_GUARD()
|
||||||
name_cstr := temp_cstring(name) or_return
|
name_cstr := temp_cstring(name) or_return
|
||||||
|
|
||||||
fd := unix.sys_open(name_cstr, _O_RDONLY | _O_PATH | _O_NOFOLLOW)
|
fd, errno := linux.open(name_cstr, {.PATH, .NOFOLLOW})
|
||||||
if fd < 0 {
|
if errno != .NONE {
|
||||||
return {}, _get_platform_error(fd)
|
return {}, _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
defer unix.sys_close(fd)
|
defer linux.close(fd)
|
||||||
return _fstat_internal(fd, allocator)
|
return _fstat_internal(fd, allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -555,7 +555,7 @@ open :: proc(path: string, flags: int = O_RDWR, mode: int = 0) -> (Handle, Errno
|
|||||||
err := fchmod(handle, cast(u16)mode)
|
err := fchmod(handle, cast(u16)mode)
|
||||||
if err != 0 {
|
if err != 0 {
|
||||||
_unix_close(handle)
|
_unix_close(handle)
|
||||||
return INVALID_HANDLE, cast(Errno)err
|
return INVALID_HANDLE, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -883,8 +883,8 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
|
|||||||
}
|
}
|
||||||
defer _unix_free(path_ptr)
|
defer _unix_free(path_ptr)
|
||||||
|
|
||||||
path_cstr := transmute(cstring)path_ptr
|
path_cstr := cast(cstring)path_ptr
|
||||||
path = strings.clone( string(path_cstr) )
|
path = strings.clone(string(path_cstr))
|
||||||
|
|
||||||
return path, ERROR_NONE
|
return path, ERROR_NONE
|
||||||
}
|
}
|
||||||
@@ -896,7 +896,7 @@ access :: proc(path: string, mask: int) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
flush :: proc(fd: Handle) -> Errno {
|
flush :: proc(fd: Handle) -> Errno {
|
||||||
return cast(Errno)_unix_fsync(fd)
|
return cast(Errno)_unix_fsync(fd)
|
||||||
}
|
}
|
||||||
|
|
||||||
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||||
|
|||||||
@@ -648,8 +648,8 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
|
|||||||
}
|
}
|
||||||
defer _unix_free(path_ptr)
|
defer _unix_free(path_ptr)
|
||||||
|
|
||||||
path_cstr := transmute(cstring)path_ptr
|
|
||||||
path = strings.clone( string(path_cstr) )
|
path = strings.clone(string(cstring(path_ptr)))
|
||||||
|
|
||||||
return path, ERROR_NONE
|
return path, ERROR_NONE
|
||||||
}
|
}
|
||||||
@@ -705,7 +705,9 @@ set_current_directory :: proc(path: string) -> (err: Errno) {
|
|||||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||||
res := _unix_chdir(cstr)
|
res := _unix_chdir(cstr)
|
||||||
if res == -1 do return Errno(get_last_error())
|
if res == -1 {
|
||||||
|
return Errno(get_last_error())
|
||||||
|
}
|
||||||
return ERROR_NONE
|
return ERROR_NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -743,7 +745,9 @@ get_page_size :: proc "contextless" () -> int {
|
|||||||
// NOTE(tetra): The page size never changes, so why do anything complicated
|
// NOTE(tetra): The page size never changes, so why do anything complicated
|
||||||
// if we don't have to.
|
// if we don't have to.
|
||||||
@static page_size := -1
|
@static page_size := -1
|
||||||
if page_size != -1 do return page_size
|
if page_size != -1 {
|
||||||
|
return page_size
|
||||||
|
}
|
||||||
|
|
||||||
page_size = int(_unix_getpagesize())
|
page_size = int(_unix_getpagesize())
|
||||||
return page_size
|
return page_size
|
||||||
|
|||||||
@@ -890,8 +890,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
|
|||||||
}
|
}
|
||||||
defer _unix_free(path_ptr)
|
defer _unix_free(path_ptr)
|
||||||
|
|
||||||
path_cstr := transmute(cstring)path_ptr
|
path = strings.clone(string(cstring(path_ptr)))
|
||||||
path = strings.clone( string(path_cstr) )
|
|
||||||
|
|
||||||
return path, ERROR_NONE
|
return path, ERROR_NONE
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -658,8 +658,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
|
|||||||
}
|
}
|
||||||
defer _unix_free(path_ptr)
|
defer _unix_free(path_ptr)
|
||||||
|
|
||||||
path_cstr := transmute(cstring)path_ptr
|
path = strings.clone(string(cstring(path_ptr)))
|
||||||
path = strings.clone( string(path_cstr) )
|
|
||||||
|
|
||||||
return path, ERROR_NONE
|
return path, ERROR_NONE
|
||||||
}
|
}
|
||||||
@@ -715,7 +714,9 @@ set_current_directory :: proc(path: string) -> (err: Errno) {
|
|||||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||||
res := _unix_chdir(cstr)
|
res := _unix_chdir(cstr)
|
||||||
if res == -1 do return Errno(get_last_error())
|
if res == -1 {
|
||||||
|
return Errno(get_last_error())
|
||||||
|
}
|
||||||
return ERROR_NONE
|
return ERROR_NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -756,7 +757,9 @@ get_page_size :: proc() -> int {
|
|||||||
// NOTE(tetra): The page size never changes, so why do anything complicated
|
// NOTE(tetra): The page size never changes, so why do anything complicated
|
||||||
// if we don't have to.
|
// if we don't have to.
|
||||||
@static page_size := -1
|
@static page_size := -1
|
||||||
if page_size != -1 do return page_size
|
if page_size != -1 {
|
||||||
|
return page_size
|
||||||
|
}
|
||||||
|
|
||||||
page_size = int(_unix_getpagesize())
|
page_size = int(_unix_getpagesize())
|
||||||
return page_size
|
return page_size
|
||||||
|
|||||||
@@ -610,8 +610,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
|
|||||||
}
|
}
|
||||||
defer _unix_free(path_ptr)
|
defer _unix_free(path_ptr)
|
||||||
|
|
||||||
path_cstr := transmute(cstring)path_ptr
|
path = strings.clone(string(cstring(path_ptr)))
|
||||||
path = strings.clone( string(path_cstr) )
|
|
||||||
|
|
||||||
return path, ERROR_NONE
|
return path, ERROR_NONE
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import "base:runtime"
|
|||||||
Handle :: distinct i32
|
Handle :: distinct i32
|
||||||
Errno :: distinct i32
|
Errno :: distinct i32
|
||||||
|
|
||||||
|
INVALID_HANDLE :: -1
|
||||||
|
|
||||||
ERROR_NONE :: Errno(wasi.errno_t.SUCCESS)
|
ERROR_NONE :: Errno(wasi.errno_t.SUCCESS)
|
||||||
|
|
||||||
O_RDONLY :: 0x00000
|
O_RDONLY :: 0x00000
|
||||||
@@ -26,6 +28,16 @@ stdout: Handle = 1
|
|||||||
stderr: Handle = 2
|
stderr: Handle = 2
|
||||||
current_dir: Handle = 3
|
current_dir: Handle = 3
|
||||||
|
|
||||||
|
args := _alloc_command_line_arguments()
|
||||||
|
|
||||||
|
_alloc_command_line_arguments :: proc() -> (args: []string) {
|
||||||
|
args = make([]string, len(runtime.args__))
|
||||||
|
for &arg, i in args {
|
||||||
|
arg = string(runtime.args__[i])
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||||
iovs := wasi.ciovec_t(data)
|
iovs := wasi.ciovec_t(data)
|
||||||
n, err := wasi.fd_write(wasi.fd_t(fd), {iovs})
|
n, err := wasi.fd_write(wasi.fd_t(fd), {iovs})
|
||||||
|
|||||||
@@ -178,8 +178,8 @@ WINDOWS_11_BUILD_CUTOFF :: 22_000
|
|||||||
get_windows_version_w :: proc() -> win32.OSVERSIONINFOEXW {
|
get_windows_version_w :: proc() -> win32.OSVERSIONINFOEXW {
|
||||||
osvi : win32.OSVERSIONINFOEXW
|
osvi : win32.OSVERSIONINFOEXW
|
||||||
osvi.dwOSVersionInfoSize = size_of(win32.OSVERSIONINFOEXW)
|
osvi.dwOSVersionInfoSize = size_of(win32.OSVERSIONINFOEXW)
|
||||||
win32.RtlGetVersion(&osvi)
|
win32.RtlGetVersion(&osvi)
|
||||||
return osvi
|
return osvi
|
||||||
}
|
}
|
||||||
|
|
||||||
is_windows_xp :: proc() -> bool {
|
is_windows_xp :: proc() -> bool {
|
||||||
|
|||||||
@@ -432,7 +432,7 @@ rel :: proc(base_path, target_path: string, allocator := context.allocator) -> (
|
|||||||
then `"."` is returned.
|
then `"."` is returned.
|
||||||
*/
|
*/
|
||||||
dir :: proc(path: string, allocator := context.allocator) -> string {
|
dir :: proc(path: string, allocator := context.allocator) -> string {
|
||||||
context.allocator = allocator
|
context.allocator = allocator
|
||||||
vol := volume_name(path)
|
vol := volume_name(path)
|
||||||
i := len(path) - 1
|
i := len(path) - 1
|
||||||
for i >= len(vol) && !is_separator(path[i]) {
|
for i >= len(vol) && !is_separator(path[i]) {
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
|
|||||||
|
|
||||||
case Type_Info_Struct:
|
case Type_Info_Struct:
|
||||||
y := b.variant.(Type_Info_Struct) or_return
|
y := b.variant.(Type_Info_Struct) or_return
|
||||||
switch {
|
switch {
|
||||||
case len(x.types) != len(y.types),
|
case len(x.types) != len(y.types),
|
||||||
x.is_packed != y.is_packed,
|
x.is_packed != y.is_packed,
|
||||||
x.is_raw_union != y.is_raw_union,
|
x.is_raw_union != y.is_raw_union,
|
||||||
@@ -122,7 +122,7 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
|
|||||||
x.soa_kind != y.soa_kind,
|
x.soa_kind != y.soa_kind,
|
||||||
x.soa_base_type != y.soa_base_type,
|
x.soa_base_type != y.soa_base_type,
|
||||||
x.soa_len != y.soa_len:
|
x.soa_len != y.soa_len:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for _, i in x.types {
|
for _, i in x.types {
|
||||||
xn, yn := x.names[i], y.names[i]
|
xn, yn := x.names[i], y.names[i]
|
||||||
|
|||||||
@@ -156,8 +156,7 @@ linear_search_proc :: proc(array: $A/[]$T, f: proc(T) -> bool) -> (index: int, f
|
|||||||
*/
|
*/
|
||||||
@(require_results)
|
@(require_results)
|
||||||
binary_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool)
|
binary_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool)
|
||||||
where intrinsics.type_is_ordered(T) #no_bounds_check
|
where intrinsics.type_is_ordered(T) #no_bounds_check {
|
||||||
{
|
|
||||||
return binary_search_by(array, key, cmp_proc(T))
|
return binary_search_by(array, key, cmp_proc(T))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -712,7 +711,7 @@ enumerated_array :: proc(ptr: ^$T) -> []intrinsics.type_elem_type(T)
|
|||||||
@(require_results)
|
@(require_results)
|
||||||
enum_slice_to_bitset :: proc(enums: []$E, $T: typeid/bit_set[E]) -> (bits: T) where intrinsics.type_is_enum(E), intrinsics.type_bit_set_elem_type(T) == E {
|
enum_slice_to_bitset :: proc(enums: []$E, $T: typeid/bit_set[E]) -> (bits: T) where intrinsics.type_is_enum(E), intrinsics.type_bit_set_elem_type(T) == E {
|
||||||
for v in enums {
|
for v in enums {
|
||||||
bits |= {v}
|
bits += {v}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ Builder :: struct {
|
|||||||
buf: [dynamic]byte,
|
buf: [dynamic]byte,
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
Produces a Builder with a default length of 0 and cap of 16
|
Produces an empty Builder
|
||||||
|
|
||||||
*Allocates Using Provided Allocator*
|
*Allocates Using Provided Allocator*
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ builder_make_none :: proc(allocator := context.allocator, loc := #caller_locatio
|
|||||||
return Builder{buf=make([dynamic]byte, allocator, loc) or_return }, nil
|
return Builder{buf=make([dynamic]byte, allocator, loc) or_return }, nil
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
Produces a Builder with a specified length and cap of max(16,len) byte buffer
|
Produces a Builder with specified length and capacity `len`.
|
||||||
|
|
||||||
*Allocates Using Provided Allocator*
|
*Allocates Using Provided Allocator*
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ builder_make_len :: proc(len: int, allocator := context.allocator, loc := #calle
|
|||||||
return Builder{buf=make([dynamic]byte, len, allocator, loc) or_return }, nil
|
return Builder{buf=make([dynamic]byte, len, allocator, loc) or_return }, nil
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
Produces a Builder with a specified length and cap
|
Produces a Builder with specified length `len` and capacity `cap`.
|
||||||
|
|
||||||
*Allocates Using Provided Allocator*
|
*Allocates Using Provided Allocator*
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ builder_make :: proc{
|
|||||||
builder_make_len_cap,
|
builder_make_len_cap,
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
Initializes a Builder with a length of 0 and cap of 16
|
Initializes an empty Builder
|
||||||
It replaces the existing `buf`
|
It replaces the existing `buf`
|
||||||
|
|
||||||
*Allocates Using Provided Allocator*
|
*Allocates Using Provided Allocator*
|
||||||
@@ -121,7 +121,7 @@ builder_init_none :: proc(b: ^Builder, allocator := context.allocator, loc := #c
|
|||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
Initializes a Builder with a specified length and cap, which is max(len,16)
|
Initializes a Builder with specified length and capacity `len`.
|
||||||
It replaces the existing `buf`
|
It replaces the existing `buf`
|
||||||
|
|
||||||
*Allocates Using Provided Allocator*
|
*Allocates Using Provided Allocator*
|
||||||
@@ -140,7 +140,7 @@ builder_init_len :: proc(b: ^Builder, len: int, allocator := context.allocator,
|
|||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
Initializes a Builder with a specified length and cap
|
Initializes a Builder with specified length `len` and capacity `cap`.
|
||||||
It replaces the existing `buf`
|
It replaces the existing `buf`
|
||||||
|
|
||||||
Inputs:
|
Inputs:
|
||||||
@@ -723,11 +723,11 @@ write_f32 :: proc(b: ^Builder, f: f32, fmt: byte, always_signed := false) -> (n:
|
|||||||
return write_string(b, s)
|
return write_string(b, s)
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
Writes a f32 value to the Builder and returns the number of characters written
|
Writes a f64 value to the Builder and returns the number of characters written
|
||||||
|
|
||||||
Inputs:
|
Inputs:
|
||||||
- b: A pointer to the Builder
|
- b: A pointer to the Builder
|
||||||
- f: The f32 value to be appended
|
- f: The f64 value to be appended
|
||||||
- fmt: The format byte
|
- fmt: The format byte
|
||||||
- always_signed: Optional boolean flag to always include the sign
|
- always_signed: Optional boolean flag to always include the sign
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ Returns:
|
|||||||
intern_init :: proc(m: ^Intern, allocator := context.allocator, map_allocator := context.allocator, loc := #caller_location) -> (err: mem.Allocator_Error) {
|
intern_init :: proc(m: ^Intern, allocator := context.allocator, map_allocator := context.allocator, loc := #caller_location) -> (err: mem.Allocator_Error) {
|
||||||
m.allocator = allocator
|
m.allocator = allocator
|
||||||
m.entries = make(map[string]^Intern_Entry, 16, map_allocator, loc) or_return
|
m.entries = make(map[string]^Intern_Entry, 16, map_allocator, loc) or_return
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
Frees the map and all its content allocated using the `.allocator`.
|
Frees the map and all its content allocated using the `.allocator`.
|
||||||
|
|||||||
@@ -304,7 +304,7 @@ try_recv_raw :: proc "contextless" (c: ^Raw_Chan, msg_out: rawptr) -> bool {
|
|||||||
|
|
||||||
if sync.atomic_load(&c.closed) ||
|
if sync.atomic_load(&c.closed) ||
|
||||||
sync.atomic_load(&c.w_waiting) == 0 {
|
sync.atomic_load(&c.w_waiting) == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
mem.copy(msg_out, c.unbuffered_data, int(c.msg_size))
|
mem.copy(msg_out, c.unbuffered_data, int(c.msg_size))
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ atomic_rw_mutex_shared_unlock :: proc "contextless" (rw: ^Atomic_RW_Mutex) {
|
|||||||
|
|
||||||
if (state & Atomic_RW_Mutex_State_Reader_Mask == Atomic_RW_Mutex_State_Reader) &&
|
if (state & Atomic_RW_Mutex_State_Reader_Mask == Atomic_RW_Mutex_State_Reader) &&
|
||||||
(state & Atomic_RW_Mutex_State_Is_Writing != 0) {
|
(state & Atomic_RW_Mutex_State_Is_Writing != 0) {
|
||||||
atomic_sema_post(&rw.sema)
|
atomic_sema_post(&rw.sema)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ StringCopyToOdinString :: proc(
|
|||||||
max := StringGetMaximumSizeForEncoding(length, StringEncoding(StringBuiltInEncodings.UTF8))
|
max := StringGetMaximumSizeForEncoding(length, StringEncoding(StringBuiltInEncodings.UTF8))
|
||||||
|
|
||||||
buf, err := make([]byte, max, allocator)
|
buf, err := make([]byte, max, allocator)
|
||||||
if err != nil do return
|
if err != nil { return }
|
||||||
|
|
||||||
raw_str := runtime.Raw_String {
|
raw_str := runtime.Raw_String {
|
||||||
data = raw_data(buf),
|
data = raw_data(buf),
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user