Merge tag 'dev-2024-07'

This commit is contained in:
ed
2024-07-01 13:32:04 -04:00
160 changed files with 13729 additions and 7588 deletions
+14 -8
View File
@@ -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
+2 -1
View File
@@ -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 ---
+56 -14
View File
@@ -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
+64 -50
View File
@@ -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 {
+5
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+47 -3
View File
@@ -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)
}
}
+3 -3
View File
@@ -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) {
+3 -3
View File
@@ -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
} }
+3 -3
View File
@@ -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
View File
@@ -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))
+3 -3
View File
@@ -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))
} }
+4 -7
View File
@@ -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
} }
+6 -3
View File
@@ -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).
+1 -2
View File
@@ -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
} }
+2 -2
View File
@@ -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))
+1 -1
View File
@@ -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,
} }
+4 -4
View File
@@ -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))
} }
+2 -2
View File
@@ -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" (
+2 -2
View File
@@ -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" (
+5 -5
View File
@@ -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))
} }
+5 -5
View File
@@ -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))
} }
+2 -2
View File
@@ -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)
} }
} }
+3 -3
View File
@@ -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_)
} }
+5 -5
View File
@@ -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))
} }
+8 -8
View File
@@ -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))
} }
+7 -7
View File
@@ -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)
+1 -1
View File
@@ -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
+4 -2
View File
@@ -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
View File
@@ -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
} }
+16 -16
View File
@@ -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.
+1 -1
View File
@@ -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 {
-1
View File
@@ -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)
+7 -7
View File
@@ -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 {
File diff suppressed because it is too large Load Diff
+16 -14
View File
@@ -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 {
@@ -602,7 +604,7 @@ opt_write_iteration :: proc(w: io.Writer, opt: ^Marshal_Options, iteration: int)
opt_write_indentation(w, opt) or_return opt_write_indentation(w, opt) or_return
case .MJSON: case .MJSON:
if iteration > 0 { if !first_iteration {
// on pretty no commas necessary // on pretty no commas necessary
if opt.pretty { if opt.pretty {
io.write_byte(w, '\n') or_return io.write_byte(w, '\n') or_return
+28
View File
@@ -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.
+67
View File
@@ -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,
}
+46
View File
@@ -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
+333
View File
@@ -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,
}
+146
View File
@@ -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,
}
+242
View File
@@ -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)
}
+89
View File
@@ -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,
}
+131
View File
@@ -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,
}
+5 -5
View File
@@ -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
View File
@@ -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 {
+13 -16
View File
@@ -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 {
@@ -1230,9 +1230,9 @@ _fmt_memory :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, units: st
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)
+11 -10
View File
@@ -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)
}
} }
/* /*
+3 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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:
+5 -7
View File
@@ -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)
} }
+1 -1
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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
+5 -7
View File
@@ -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
+1 -1
View File
@@ -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)) {
+8 -3
View File
@@ -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
+2 -2
View File
@@ -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
+3 -3
View File
@@ -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
View File
@@ -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, ":")
+1
View File
@@ -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,
+13 -16
View File
@@ -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
} }
+78 -78
View File
@@ -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[:], {}
} }
+2 -3
View File
@@ -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)
} }
+4 -4
View File
@@ -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 {
+8 -6
View File
@@ -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
+1
View File
@@ -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,
+17 -9
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
+16
View File
@@ -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
View File
@@ -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"
} }
-3
View File
@@ -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
View File
@@ -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, &times, 0)) return _get_platform_error(linux.utimensat(linux.AT_FDCWD, name_cstr, &times[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, &times, 0)) return _get_platform_error(linux.utimensat(f.impl.fd, nil, &times[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
View File
@@ -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
View File
@@ -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
} }
+11 -1
View File
@@ -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
View File
@@ -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)
} }
+4 -4
View File
@@ -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) {
+8 -4
View File
@@ -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
+1 -2
View File
@@ -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
} }
+7 -4
View File
@@ -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
+1 -2
View File
@@ -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
} }
+12
View File
@@ -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})
+2 -2
View File
@@ -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 {
+1 -1
View File
@@ -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]) {
+2 -2
View File
@@ -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]
+2 -3
View File
@@ -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
} }
+8 -8
View File
@@ -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
+1 -1
View File
@@ -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`.
+1 -1
View File
@@ -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))
+1 -1
View File
@@ -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)
} }
} }
+1 -1
View File
@@ -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