Merge tag 'dev-2024-07'

This commit is contained in:
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/cgltf/src
gmake -C vendor/miniaudio/src
./odin check examples/all -vet -strict-style -target:netbsd_amd64
./odin check examples/all -vet -strict-style -target:netbsd_arm64
./odin check examples/all -vet -strict-style -disallow-do -target:netbsd_amd64
./odin check examples/all -vet -strict-style -disallow-do -target:netbsd_arm64
./odin 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/vendor -all-packages -define:ODIN_TEST_FANCY=false
@@ -62,7 +62,7 @@ jobs:
gmake -C vendor/stb/src
gmake -C vendor/cgltf/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/speed.odin -file -all-packages -o:speed -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)
if: matrix.os == 'macos-14'
run: |
brew install llvm@17
brew install llvm@17 wasmtime
echo "/opt/homebrew/opt/llvm@17/bin" >> $GITHUB_PATH
- name: Build Odin
@@ -135,18 +135,24 @@ jobs:
./run.sh
- 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'
- 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'
- 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'
- 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'
- 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:
name: Windows Build, Check, and Test
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_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_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.
//
// 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.
@builtin
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.
//
// 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.
@builtin
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`
@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`).
@builtin
reserve :: proc{reserve_dynamic_array, reserve_map}
reserve :: proc{
reserve_dynamic_array,
reserve_map,
reserve_soa,
}
@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`).
@builtin
resize :: proc{resize_dynamic_array}
resize :: proc{
resize_dynamic_array,
resize_soa,
}
@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.
@builtin
@@ -268,7 +290,7 @@ new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_locat
return
}
DEFAULT_RESERVE_CAPACITY :: 16
DEFAULT_DYNAMIC_ARRAY_CAPACITY :: 8
@(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 {
@@ -295,7 +317,7 @@ make_slice :: proc($T: typeid/[]$E, #any_int len: int, allocator := context.allo
// Note: Prefer using the procedure group `make`.
@(builtin, require_results)
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.
// 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_map,
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
} else {
if cap(array) < len(array)+1 {
// Same behavior as _append_elems but there's only one arg, so we always just add 8.
cap := 2 * cap(array) + 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) + DEFAULT_DYNAMIC_ARRAY_CAPACITY
// do not 'or_return' here as it could be a partial success
if should_zero {
@@ -473,7 +500,7 @@ _append_elems :: #force_inline proc(array: ^$T/[dynamic]$E, should_zero: bool, l
return arg_len, nil
} else {
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
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
@builtin append :: proc{append_elem, append_elems, append_elem_string}
@builtin non_zero_append :: proc{non_zero_append_elem, non_zero_append_elems, non_zero_append_elem_string}
@builtin append :: proc{
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
+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 {
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))
return
}
@@ -64,12 +64,7 @@ raw_soa_footer_dynamic_array :: proc(array: ^$T/#soa[dynamic]$E) -> (footer: ^Ra
if array == nil {
return nil
}
field_count: uintptr
when intrinsics.type_is_array(E) {
field_count = len(E)
} else {
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_Dynamic_Array)(uintptr(array) + field_count*size_of(rawptr))
return
}
@@ -98,7 +93,7 @@ make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, alloc
ti = type_info_base(ti)
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
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)
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
reserve_soa(&array, DEFAULT_RESERVE_CAPACITY, loc) or_return
reserve_soa(&array, 0, loc) or_return
return array, nil
}
@@ -187,8 +182,28 @@ resize_soa :: proc(array: ^$T/#soa[dynamic]$E, length: int, loc := #caller_locat
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
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 {
return nil
}
@@ -213,12 +228,7 @@ reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_lo
ti = type_info_base(ti)
si := &ti.variant.(Type_Info_Struct)
field_count: uintptr
when intrinsics.type_is_array(E) {
field_count = len(E)
} else {
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))
assert(footer.cap == old_cap)
old_size := 0
@@ -238,7 +248,7 @@ reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_lo
old_data := (^rawptr)(array)^
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,
) or_return
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
}
@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 {
return 0, nil
}
if cap(array) <= len(array) + 1 {
cap := 2 * cap(array) + 8
err = reserve_soa(array, cap, loc) // do not 'or_return' here as it could be a partial success
// Same behavior as append_soa_elems but there's only one arg, so we always just add DEFAULT_DYNAMIC_ARRAY_CAPACITY.
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)
@@ -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_base(ti)
si := &ti.variant.(Type_Info_Struct)
field_count: uintptr
when intrinsics.type_is_array(E) {
field_count = len(E)
} else {
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)^
@@ -326,7 +342,17 @@ append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, arg: E, loc := #caller_locat
}
@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 {
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 {
cap := 2 * cap(array) + max(8, arg_len)
err = reserve_soa(array, cap, loc) // do not 'or_return' here as it could be a partial success
cap := 2 * cap(array) + max(DEFAULT_DYNAMIC_ARRAY_CAPACITY, arg_len)
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)
@@ -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_base(ti)
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)^
@@ -389,7 +415,8 @@ append_soa :: proc{
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
ptr := (^rawptr)(&array)^
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 {
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
ptr := (^rawptr)(&array)^
footer := raw_soa_footer(&array)
@@ -416,7 +444,8 @@ delete_soa :: proc{
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.len = 0
}
@@ -438,12 +467,7 @@ into_dynamic_soa :: proc(array: $T/#soa[]$E) -> #soa[dynamic]E {
allocator = nil_allocator(),
}
field_count: uintptr
when intrinsics.type_is_array(E) {
field_count = len(E)
} else {
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))
array := array
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)
si := &ti.variant.(Type_Info_Struct)
field_count: uintptr
when intrinsics.type_is_array(E) {
field_count = len(E)
} else {
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 := uintptr(array)
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)
si := &ti.variant.(Type_Info_Struct)
field_count: uintptr
when intrinsics.type_is_array(E) {
field_count = len(E)
} else {
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 := uintptr(array)
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)
_start :: proc "c" () {
context = default_context()
when ODIN_OS == .WASI {
_wasi_setup_args()
}
#force_no_inline _startup_runtime()
intrinsics.__entry_point()
}
+3
View File
@@ -3,13 +3,16 @@
package runtime
_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")
}
_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")
}
_heap_free :: proc "contextless" (ptr: rawptr) {
context = default_context()
unimplemented("base:runtime 'heap_free' procedure is not supported on this platform")
}
+31 -27
View File
@@ -1,3 +1,4 @@
//+vet !cast
package runtime
import "base:intrinsics"
@@ -29,7 +30,7 @@ is_power_of_two_int :: #force_inline proc "contextless" (x: int) -> bool {
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))
p := ptr
@@ -47,7 +48,7 @@ is_power_of_two_uint :: #force_inline proc "contextless" (x: uint) -> bool {
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))
p := ptr
@@ -65,7 +66,7 @@ is_power_of_two_uintptr :: #force_inline proc "contextless" (x: uintptr) -> bool
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))
p := ptr
@@ -642,21 +643,24 @@ abs_quaternion256 :: #force_inline proc "contextless" (x: quaternion256) -> f64
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)) {
ratio := imag(m) / real(m)
denom := real(m) + ratio*imag(m)
e = (real(n) + imag(n)*ratio) / denom
f = (imag(n) - real(n)*ratio) / denom
e, f: f32
if abs(mr) >= abs(mi) {
ratio := mi / mr
denom := mr + ratio*mi
e = (nr + ni*ratio) / denom
f = (ni - nr*ratio) / denom
} else {
ratio := real(m) / imag(m)
denom := imag(m) + ratio*real(m)
e = (real(n)*ratio + imag(n)) / denom
f = (imag(n)*ratio - real(n)) / denom
ratio := mr / mi
denom := mi + ratio*mr
e = (nr*ratio + ni) / 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 {
q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q)
r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r)
q0, q1, q2, q3 := f32(real(q)), f32(imag(q)), f32(jmag(q)), f32(kmag(q))
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
t1 := r0*q1 + r1*q0 - r2*q3 + r3*q2
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
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 {
@@ -733,8 +737,8 @@ mul_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
}
quo_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q)
r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r)
q0, q1, q2, q3 := f32(real(q)), f32(imag(q)), f32(jmag(q)), f32(kmag(q))
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)
@@ -743,7 +747,7 @@ quo_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * 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 {
@@ -1012,26 +1016,26 @@ modti3 :: proc "c" (a, b: i128) -> i128 {
bn := (b ~ s_b) - s_b
r: u128 = ---
_ = udivmod128(transmute(u128)an, transmute(u128)bn, &r)
return (transmute(i128)r ~ s_a) - s_a
_ = udivmod128(u128(an), u128(bn), &r)
return (i128(r) ~ s_a) - s_a
}
@(link_name="__divmodti4", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 {
u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem)
return transmute(i128)u
u := udivmod128(u128(a), u128(b), (^u128)(rem))
return i128(u)
}
@(link_name="__divti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
divti3 :: proc "c" (a, b: i128) -> i128 {
u := udivmodti4(transmute(u128)a, transmute(u128)b, nil)
return transmute(i128)u
u := udivmodti4(u128(a), u128(b), nil)
return i128(u)
}
@(link_name="__fixdfti", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
fixdfti :: proc(a: u64) -> i128 {
fixdfti :: proc "c" (a: u64) -> i128 {
significandBits :: 52
typeWidth :: (size_of(u64)*8)
exponentBits :: (typeWidth - significandBits - 1)
+47 -3
View File
@@ -2,10 +2,54 @@
//+private
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) {
data_iovec := (wasi.ciovec_t)(data)
n, err := wasi.fd_write(1, {data_iovec})
n: uint
err := fd_write(1, {data}, &n)
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 {
case .Read:
if r.state == 0 && r.inc == 0 {
init(r, 0)
init(r, 0)
}
switch len(p) {
case size_of(u64):
// 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:
// All other cases.
pos := i8(0)
@@ -108,7 +108,7 @@ default_random_generator_proc :: proc(data: rawptr, mode: Random_Generator_Mode,
case .Reset:
seed: u64
mem_copy_non_overlapping(&seed, raw_data(p), min(size_of(seed), len(p)))
init(r, seed)
init(r, seed)
case .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]))
}
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 rem != nil {
rem^ = a
@@ -107,7 +107,7 @@ udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
r[low] = n[high] >> (sr - U64_BITS)
}
} 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 rem != nil {
@@ -143,7 +143,7 @@ udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
r_all = transmute(u128)r
s := i128(b - r_all - 1) >> (U128_BITS - 1)
carry = u32(s & 1)
r_all -= b & transmute(u128)s
r_all -= b & u128(s)
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.
spill_end := uintptr(raw_data(a.spill)) + uintptr(len(a.spill))
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)
} else {
// 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.
// Reset the bit to update latest status so that we do not
// 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
}
@@ -760,7 +760,7 @@ free :: proc(a: ^WASM_Allocator, ptr: rawptr, loc := #caller_location) {
defer unlock(a)
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 := prev_region_size_field & ~uint(FREE_REGION_FLAG)
+1 -14
View File
@@ -34,20 +34,7 @@ when ODIN_OS == .Windows {
SIGTERM :: 15
}
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Haiku || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
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 {
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))
+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 {
// For 64-bit platforms, we set the default max buffer size to 4 GiB,
// which is GZIP and PKZIP's max payload size.
// For 64-bit platforms, we set the default max buffer size to 4 GiB,
// which is GZIP and PKZIP's max payload size.
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 32))
} else {
// 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
for inp < inp_end {
val := transmute(i8)input[inp]
val := i8(input[inp])
mark := int(-1)
for val < 0 {
@@ -274,12 +274,9 @@ compress_string_to_buffer :: proc(input: string, output: []u8, model := DEFAULT_
out_ptr := raw_data(output[out:])
switch pack.bytes_packed {
case 4:
intrinsics.unaligned_store(transmute(^u32)out_ptr, code)
case 2:
intrinsics.unaligned_store(transmute(^u16)out_ptr, u16(code))
case 1:
intrinsics.unaligned_store(transmute(^u8)out_ptr, u8(code))
case 4: intrinsics.unaligned_store((^u32)(out_ptr), code)
case 2: intrinsics.unaligned_store((^u16)(out_ptr), u16(code))
case 1: intrinsics.unaligned_store( (^u8)(out_ptr), u8(code))
case:
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)
if set_to{ ba.bits[leg_index] |= 1 << uint(bit_index) }
else { ba.bits[leg_index] &= ~(1 << uint(bit_index)) }
if set_to {
ba.bits[leg_index] |= 1 << uint(bit_index)
} else {
ba.bits[leg_index] &~= 1 << uint(bit_index)
}
return true
}
@@ -253,7 +256,7 @@ Inputs:
- index: Which bit in the array
*/
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).
+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 {
e = c.tail
_remove_node(c, e)
}
else {
} else {
c.count += 1
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
}
// 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) {
n := uint(builtin.len(elems))
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 {
new_capacity := max(min_capacity, uint(8), uint(builtin.len(q.data))*2)
n := uint(builtin.len(q.data))
+1 -1
View File
@@ -29,7 +29,7 @@ Tree :: struct($Key: typeid, $Value: typeid) {
_root: ^Node(Key, Value),
_node_allocator: runtime.Allocator,
_cmp_fn: proc(Key, Key) -> Ordering,
_cmp_fn: proc(Key, Key) -> Ordering,
_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 {
runtime.bounds_check_error_loc(loc, index, a.len)
if index+1 < a.len {
runtime.bounds_check_error_loc(loc, index, a.len)
if index+1 < a.len {
copy(a.data[index:], a.data[index+1:])
}
a.len -= 1
}
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
if index != n {
a.data[index] = a.data[n]
}
a.len -= 1
a.len -= 1
}
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 = &sorter.relations[key]
find = &sorter.relations[key]
if find == nil {
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 {
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
// failure.
@@ -169,7 +169,7 @@ ge_bytes :: proc "contextless" (ge: ^Group_Element, dst: []byte) {
if len(dst) != 32 {
intrinsics.trap()
}
dst_ := transmute(^[32]byte)(raw_data(dst))
dst_ := (^[32]byte)(raw_data(dst))
// Convert the element to affine (x, y) representation.
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 {
intrinsics.trap()
}
b_ := transmute(^[32]byte)(raw_data(b))
b_ := (^[32]byte)(raw_data(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 {
intrinsics.trap()
}
b_ := transmute(^[32]byte)(raw_data(b))
b_ := (^[32]byte)(raw_data(b))
field.fe_from_bytes_rfc8032(sc, b_)
}
@@ -6,13 +6,13 @@ import "core:mem"
fe_relax_cast :: #force_inline proc "contextless" (
arg1: ^Tight_Field_Element,
) -> ^Loose_Field_Element {
return transmute(^Loose_Field_Element)(arg1)
return (^Loose_Field_Element)(arg1)
}
fe_tighten_cast :: #force_inline proc "contextless" (
arg1: ^Loose_Field_Element,
) -> ^Tight_Field_Element {
return transmute(^Tight_Field_Element)(arg1)
return (^Tight_Field_Element)(arg1)
}
fe_clear :: proc "contextless" (
+2 -2
View File
@@ -7,13 +7,13 @@ import "core:mem"
fe_relax_cast :: #force_inline proc "contextless" (
arg1: ^Tight_Field_Element,
) -> ^Loose_Field_Element {
return transmute(^Loose_Field_Element)(arg1)
return (^Loose_Field_Element)(arg1)
}
fe_tighten_cast :: #force_inline proc "contextless" (
arg1: ^Loose_Field_Element,
) -> ^Tight_Field_Element {
return transmute(^Tight_Field_Element)(arg1)
return (^Tight_Field_Element)(arg1)
}
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) {
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
@@ -75,7 +75,7 @@ final :: proc(ctx: ^Context, dst: []byte) {
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.
@@ -84,7 +84,7 @@ clone :: proc(ctx, other: ^Context) {
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
@@ -94,7 +94,7 @@ reset :: proc(ctx: ^Context) {
return
}
shake.reset(transmute(^shake.Context)(ctx))
shake.reset((^shake.Context)(ctx))
}
@(private)
@@ -107,7 +107,7 @@ _init_kmac :: proc(ctx: ^Context, key, s: []byte, sec_strength: int) {
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.bytepad(ctx_, [][]byte{key}, _sha3.rate_cshake(sec_strength))
}
+5 -5
View File
@@ -66,12 +66,12 @@ init_512 :: proc(ctx: ^Context) {
@(private)
_init :: proc(ctx: ^Context) {
ctx.dsbyte = _sha3.DS_KECCAK
_sha3.init(transmute(^_sha3.Context)(ctx))
_sha3.init((^_sha3.Context)(ctx))
}
// update adds more data to the Context.
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
@@ -80,16 +80,16 @@ update :: proc(ctx: ^Context, data: []byte) {
// Iff finalize_clone is set, final will work on a copy of the Context,
// which is useful for for calculating rolling digests.
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
_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 :: 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
// be used again.
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) {
err := Sec.RandomCopyBytes(count=len(dst), bytes=raw_data(dst))
if err != .Success {
msg := CF.StringCopyToOdinString(Sec.CopyErrorMessageString(err))
fmt.panicf("crypto/rand_bytes: SecRandomCopyBytes returned non-zero result: %v %s", err, msg)
msg := CF.StringCopyToOdinString(Sec.CopyErrorMessageString(err))
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
}
b_ := transmute(^[32]byte)(raw_data(b))
b_ := (^[32]byte)(raw_data(b))
s: field.Tight_Field_Element = ---
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
// specifically, this is the encoding of the canonical
// 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_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)
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:
//
@@ -46,7 +46,7 @@ sc_set_bytes_wide :: proc(sc: ^Scalar, b: []byte) {
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_)
}
+5 -5
View File
@@ -68,12 +68,12 @@ init_512 :: proc(ctx: ^Context) {
@(private)
_init :: proc(ctx: ^Context) {
ctx.dsbyte = _sha3.DS_SHA3
_sha3.init(transmute(^_sha3.Context)(ctx))
_sha3.init((^_sha3.Context)(ctx))
}
// update adds more data to the Context.
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
@@ -82,16 +82,16 @@ update :: proc(ctx: ^Context, data: []byte) {
// Iff finalize_clone is set, final will work on a copy of the Context,
// which is useful for for calculating rolling digests.
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
_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 :: 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
// be used again.
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 :: 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 :: 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 :: 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 :: 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
// after any reads have been done, and attempts to do so will panic.
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
// 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.
read :: proc(ctx: ^Context, dst: []byte) {
ctx_ := transmute(^_sha3.Context)(ctx)
ctx_ := (^_sha3.Context)(ctx)
if !ctx.is_finalized {
_sha3.shake_xof(ctx_)
}
@@ -62,11 +62,11 @@ read :: proc(ctx: ^Context, dst: []byte) {
// clone clones the Context other into ctx.
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
// be used again.
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 :: 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 :: 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
// instance. This MUST not be called after any reads have been done, and
// any attempts to do so will panic.
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
@@ -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,
// which is useful for for calculating rolling digests.
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
@@ -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
// write_element will panic.
read :: proc(ctx: ^Context, dst: []byte) {
ctx_ := transmute(^_sha3.Context)(ctx)
ctx_ := (^_sha3.Context)(ctx)
if !ctx.is_finalized {
_sha3.encode_byte_len(ctx_, 0, false) // right_encode
_sha3.shake_xof(ctx_)
@@ -53,13 +53,13 @@ read :: proc(ctx: ^Context, dst: []byte) {
// clone clones the Context other into ctx.
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
// be used again.
reset :: proc(ctx: ^Context) {
_sha3.reset(transmute(^_sha3.Context)(ctx))
_sha3.reset((^_sha3.Context)(ctx))
}
@(private)
+1 -1
View File
@@ -78,7 +78,7 @@ _Context :: struct {
@(private="package")
_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)
return ctx.impl.state != nil
+4 -2
View File
@@ -1,6 +1,8 @@
//+build !windows !linux !darwin
package debug_trace
import "base:runtime"
_Context :: struct {
}
@@ -10,9 +12,9 @@ _init :: proc(ctx: ^Context) -> (ok: bool) {
_destroy :: proc(ctx: ^Context) -> bool {
return true
}
_frames :: proc(ctx: ^Context, skip: uint, allocator: runtime.Allocator) -> []Frame {
_frames :: proc(ctx: ^Context, skip: uint, frames_buffer: []Frame) -> []Frame {
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
}
+116 -116
View File
@@ -8,141 +8,141 @@ package encoding_base32
// truncate it from the encoded output.
ENC_TABLE := [32]byte {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', '2', '3', '4', '5', '6', '7',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', '2', '3', '4', '5', '6', '7',
}
PADDING :: '='
DEC_TABLE := [?]u8 {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 26, 27, 28, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 26, 27, 28, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
}
encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string {
out_length := (len(data) + 4) / 5 * 8
out := make([]byte, out_length)
_encode(out, data)
return string(out)
out_length := (len(data) + 4) / 5 * 8
out := make([]byte, out_length)
_encode(out, data)
return string(out)
}
@private
_encode :: proc(out, data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) {
out := out
data := data
out := out
data := data
for len(data) > 0 {
carry: byte
switch len(data) {
case:
out[7] = ENC_TABLE[data[4] & 0x1f]
carry = data[4] >> 5
fallthrough
case 4:
out[6] = ENC_TABLE[carry | (data[3] << 3) & 0x1f]
out[5] = ENC_TABLE[(data[3] >> 2) & 0x1f]
carry = data[3] >> 7
fallthrough
case 3:
out[4] = ENC_TABLE[carry | (data[2] << 1) & 0x1f]
carry = (data[2] >> 4) & 0x1f
fallthrough
case 2:
out[3] = ENC_TABLE[carry | (data[1] << 4) & 0x1f]
out[2] = ENC_TABLE[(data[1] >> 1) & 0x1f]
carry = (data[1] >> 6) & 0x1f
fallthrough
case 1:
out[1] = ENC_TABLE[carry | (data[0] << 2) & 0x1f]
out[0] = ENC_TABLE[data[0] >> 3]
}
for len(data) > 0 {
carry: byte
switch len(data) {
case:
out[7] = ENC_TABLE[data[4] & 0x1f]
carry = data[4] >> 5
fallthrough
case 4:
out[6] = ENC_TABLE[carry | (data[3] << 3) & 0x1f]
out[5] = ENC_TABLE[(data[3] >> 2) & 0x1f]
carry = data[3] >> 7
fallthrough
case 3:
out[4] = ENC_TABLE[carry | (data[2] << 1) & 0x1f]
carry = (data[2] >> 4) & 0x1f
fallthrough
case 2:
out[3] = ENC_TABLE[carry | (data[1] << 4) & 0x1f]
out[2] = ENC_TABLE[(data[1] >> 1) & 0x1f]
carry = (data[1] >> 6) & 0x1f
fallthrough
case 1:
out[1] = ENC_TABLE[carry | (data[0] << 2) & 0x1f]
out[0] = ENC_TABLE[data[0] >> 3]
}
if len(data) < 5 {
out[7] = byte(PADDING)
if len(data) < 4 {
out[6] = byte(PADDING)
out[5] = byte(PADDING)
if len(data) < 3 {
out[4] = byte(PADDING)
if len(data) < 2 {
out[3] = byte(PADDING)
out[2] = byte(PADDING)
}
}
}
break
}
data = data[5:]
out = out[8:]
}
if len(data) < 5 {
out[7] = byte(PADDING)
if len(data) < 4 {
out[6] = byte(PADDING)
out[5] = byte(PADDING)
if len(data) < 3 {
out[4] = byte(PADDING)
if len(data) < 2 {
out[3] = byte(PADDING)
out[2] = byte(PADDING)
}
}
}
break
}
data = data[5:]
out = out[8:]
}
}
decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check{
if len(data) == 0 {
return nil
}
if len(data) == 0 {
return nil
}
outi := 0
data := data
outi := 0
data := data
out := make([]byte, len(data) / 8 * 5, allocator)
end := false
for len(data) > 0 && !end {
dbuf : [8]byte
dlen := 8
out := make([]byte, len(data) / 8 * 5, allocator)
end := false
for len(data) > 0 && !end {
dbuf : [8]byte
dlen := 8
for j := 0; j < 8; {
if len(data) == 0 {
dlen, end = j, true
break
}
input := data[0]
data = data[1:]
if input == byte(PADDING) && j >= 2 && len(data) < 8 {
assert(!(len(data) + j < 8 - 1), "Corrupted input")
for k := 0; k < 8-1-j; k +=1 {
assert(len(data) < k || data[k] == byte(PADDING), "Corrupted input")
}
dlen, end = j, true
assert(dlen != 1 && dlen != 3 && dlen != 6, "Corrupted input")
break
}
dbuf[j] = DEC_TABLE[input]
assert(dbuf[j] != 0xff, "Corrupted input")
j += 1
}
for j := 0; j < 8; {
if len(data) == 0 {
dlen, end = j, true
break
}
input := data[0]
data = data[1:]
if input == byte(PADDING) && j >= 2 && len(data) < 8 {
assert(!(len(data) + j < 8 - 1), "Corrupted input")
for k := 0; k < 8-1-j; k +=1 {
assert(len(data) < k || data[k] == byte(PADDING), "Corrupted input")
}
dlen, end = j, true
assert(dlen != 1 && dlen != 3 && dlen != 6, "Corrupted input")
break
}
dbuf[j] = DEC_TABLE[input]
assert(dbuf[j] != 0xff, "Corrupted input")
j += 1
}
switch dlen {
case 8:
out[outi + 4] = dbuf[6] << 5 | dbuf[7]
fallthrough
case 7:
out[outi + 3] = dbuf[4] << 7 | dbuf[5] << 2 | dbuf[6] >> 3
fallthrough
case 5:
out[outi + 2] = dbuf[3] << 4 | dbuf[4] >> 1
fallthrough
case 4:
out[outi + 1] = dbuf[1] << 6 | dbuf[2] << 1 | dbuf[3] >> 4
fallthrough
case 2:
out[outi + 0] = dbuf[0] << 3 | dbuf[1] >> 2
}
outi += 5
}
return out
switch dlen {
case 8:
out[outi + 4] = dbuf[6] << 5 | dbuf[7]
fallthrough
case 7:
out[outi + 3] = dbuf[4] << 7 | dbuf[5] << 2 | dbuf[6] >> 3
fallthrough
case 5:
out[outi + 2] = dbuf[3] << 4 | dbuf[4] >> 1
fallthrough
case 4:
out[outi + 1] = dbuf[1] << 6 | dbuf[2] << 1 | dbuf[3] >> 4
fallthrough
case 2:
out[outi + 0] = dbuf[0] << 3 | dbuf[1] >> 2
}
outi += 5
}
return out
}
+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 {
_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 {
@@ -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) {
assert(len(val) >= 0)
_encode_u64(e, u64(len(val)), major) or_return
_, err = io.write_full(e.writer, val[:])
_, err = io.write_full(e.writer, val[:])
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 {
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) {
@@ -480,10 +480,10 @@ _decode_array :: proc(d: Decoder, add: Add, allocator := context.allocator, loc
_encode_array :: proc(e: Encoder, arr: Array) -> Encode_Error {
assert(len(arr) >= 0)
_encode_u64(e, u64(len(arr)), .Array)
for val in arr {
encode(e, val) or_return
}
return nil
for val in arr {
encode(e, val) or_return
}
return nil
}
_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
}
return nil
return nil
}
_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_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) {
@@ -739,16 +739,16 @@ _encode_nil :: proc(w: io.Writer) -> io.Error {
// Streaming
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)
_, err = io.write_full(w, {header})
header := (u8(major) << 5) | u8(Add.Length_Unknown)
_, err = io.write_full(w, {header})
return
}
encode_stream_end :: proc(w: io.Writer) -> io.Error {
header := (u8(Major.Other) << 5) | u8(Add.Break)
_, err := io.write_full(w, {header})
header := (u8(Major.Other) << 5) | u8(Add.Break)
_, err := io.write_full(w, {header})
return err
}
@@ -757,8 +757,8 @@ encode_stream_text :: _encode_text
encode_stream_array_item :: encode
encode_stream_map_entry :: proc(e: Encoder, key: Value, val: Value) -> Encode_Error {
encode(e, key) or_return
return encode(e, val)
encode(e, key) or_return
return encode(e, val)
}
// 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 {
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 {
-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.
// 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)
@(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,
// We only put the value of it in fields that are explicitly of type `Simple`.
switch &dst in v {
case Simple:
dst = decoded
return
case:
return _unsupported(v, hdr, add)
}
switch &dst in v {
case Simple:
dst = decoded
return
case:
return _unsupported(v, hdr, add)
}
case .Tag:
switch &dst in v {
File diff suppressed because it is too large Load Diff
+21 -19
View File
@@ -25,7 +25,7 @@ Marshal_Options :: struct {
spec: Specification,
// Use line breaks & tabs/spaces
pretty: bool,
pretty: bool,
// Use spaces for indentation instead of tabs
use_spaces: bool,
@@ -34,7 +34,7 @@ Marshal_Options :: struct {
spaces: int,
// Output uint as hex in JSON5 & MJSON
write_uint_as_hex: bool,
write_uint_as_hex: bool,
// If spec is MJSON and this is true, then keys will be quoted.
//
@@ -138,7 +138,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
// allow uints to be printed as hex
if opt.write_uint_as_hex && (opt.spec == .JSON5 || opt.spec == .MJSON) {
switch i in a {
case u8, u16, u32, u64, u128:
case u8, u16, u32, u64, u128:
s = strconv.append_bits_128(buf[:], u, 16, info.signed, 8*ti.size, "0123456789abcdef", { .Prefix })
case:
@@ -239,7 +239,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
case runtime.Type_Info_Array:
opt_write_start(w, opt, '[') or_return
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)
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:
opt_write_start(w, opt, '[') or_return
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)
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
array := cast(^mem.Raw_Dynamic_Array)v.data
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)
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
slice := cast(^mem.Raw_Slice)v.data
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)
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 {
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
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 })
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
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,
runtime.Type_Info_Multi_Pointer,
runtime.Type_Info_Procedure:
return (^rawptr)(v.data)^ == nil
return (^rawptr)(v.data)^ == nil
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:
return (^runtime.Raw_Slice)(v.data).len == 0
return (^runtime.Raw_Slice)(v.data).len == 0
case runtime.Type_Info_Union,
runtime.Type_Info_Bit_Set,
runtime.Type_Info_Soa_Pointer:
return reflect.is_nil(v)
case runtime.Type_Info_Map:
return (^runtime.Raw_Map)(v.data).len == 0
return (^runtime.Raw_Map)(v.data).len == 0
}
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) {
ti := runtime.type_info_base(type_info_of(v.id))
info := ti.variant.(runtime.Type_Info_Struct)
first_iteration := true
for name, i in info.names {
omitempty := false
@@ -424,7 +425,8 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
continue
}
opt_write_iteration(w, opt, i) or_return
opt_write_iteration(w, opt, first_iteration) or_return
first_iteration = false
if json_name != "" {
opt_write_key(w, opt, json_name) or_return
} 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
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 {
case .JSON, .JSON5:
if iteration > 0 {
case .JSON, .JSON5:
if !first_iteration {
io.write_byte(w, ',') or_return
if opt.pretty {
@@ -601,8 +603,8 @@ opt_write_iteration :: proc(w: io.Writer, opt: ^Marshal_Options, iteration: int)
opt_write_indentation(w, opt) or_return
case .MJSON:
if iteration > 0 {
case .MJSON:
if !first_iteration {
// on pretty no commas necessary
if opt.pretty {
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)
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 {
fmt.wprintln(writer, " --- ")
print_element(writer, doc, 0)
fmt.wprintln(writer, " --- ")
}
fmt.wprintln(writer, " --- ")
print_element(writer, doc, 0)
fmt.wprintln(writer, " --- ")
}
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 {
value := strconv.parse_i128(str) or_return
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 i16: (cast(^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 i64: (cast(^i64) ptr)^ = cast(i64) bounded_int(value, cast(i128)min(i64), cast(i128)max(i64) ) or_return
case i128: (cast(^i128) ptr)^ = value
case i8: (^i8) (ptr)^ = cast(i8) bounded_int(value, cast(i128)min(i8), cast(i128)max(i8) ) or_return
case i16: (^i16) (ptr)^ = cast(i16) bounded_int(value, cast(i128)min(i16), cast(i128)max(i16) ) or_return
case i32: (^i32) (ptr)^ = cast(i32) bounded_int(value, cast(i128)min(i32), cast(i128)max(i32) ) or_return
case i64: (^i64) (ptr)^ = cast(i64) bounded_int(value, cast(i128)min(i64), cast(i128)max(i64) ) or_return
case i128: (^i128) (ptr)^ = value
case int: (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 i32le: (cast(^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 i128le: (cast(^i128le)ptr)^ = cast(i128le) bounded_int(value, cast(i128)min(i128le), cast(i128)max(i128le)) or_return
case i16le: (^i16le) (ptr)^ = cast(i16le) bounded_int(value, cast(i128)min(i16le), cast(i128)max(i16le) ) or_return
case i32le: (^i32le) (ptr)^ = cast(i32le) bounded_int(value, cast(i128)min(i32le), cast(i128)max(i32le) ) or_return
case i64le: (^i64le) (ptr)^ = cast(i64le) bounded_int(value, cast(i128)min(i64le), cast(i128)max(i64le) ) or_return
case i128le: (^i128le)(ptr)^ = cast(i128le) bounded_int(value, cast(i128)min(i128le), cast(i128)max(i128le)) or_return
case i16be: (cast(^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 i64be: (cast(^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 i16be: (^i16be) (ptr)^ = cast(i16be) bounded_int(value, cast(i128)min(i16be), cast(i128)max(i16be) ) or_return
case i32be: (^i32be) (ptr)^ = cast(i32be) bounded_int(value, cast(i128)min(i32be), cast(i128)max(i32be) ) or_return
case i64be: (^i64be) (ptr)^ = cast(i64be) bounded_int(value, cast(i128)min(i64be), cast(i128)max(i64be) ) or_return
case i128be: (^i128be)(ptr)^ = cast(i128be) bounded_int(value, cast(i128)min(i128be), cast(i128)max(i128be)) or_return
}
} else {
value := strconv.parse_u128(str) or_return
switch type_info.id {
case u8: (cast(^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 u32: (cast(^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 u128: (cast(^u128) ptr)^ = value
case u8: (^u8) (ptr)^ = cast(u8) bounded_uint(value, cast(u128)max(u8) ) or_return
case u16: (^u16) (ptr)^ = cast(u16) bounded_uint(value, cast(u128)max(u16) ) or_return
case u32: (^u32) (ptr)^ = cast(u32) bounded_uint(value, cast(u128)max(u32) ) or_return
case u64: (^u64) (ptr)^ = cast(u64) bounded_uint(value, cast(u128)max(u64) ) or_return
case u128: (^u128) (ptr)^ = value
case uint: (cast(^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 uint: (^uint) (ptr)^ = cast(uint) bounded_uint(value, cast(u128)max(uint) ) or_return
case uintptr: (^uintptr)(ptr)^ = cast(uintptr) bounded_uint(value, cast(u128)max(uintptr)) or_return
case u16le: (cast(^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 u64le: (cast(^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 u16le: (^u16le) (ptr)^ = cast(u16le) bounded_uint(value, cast(u128)max(u16le) ) or_return
case u32le: (^u32le) (ptr)^ = cast(u32le) bounded_uint(value, cast(u128)max(u32le) ) or_return
case u64le: (^u64le) (ptr)^ = cast(u64le) bounded_uint(value, cast(u128)max(u64le) ) or_return
case u128le: (^u128le) (ptr)^ = cast(u128le) bounded_uint(value, cast(u128)max(u128le) ) or_return
case u16be: (cast(^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 u64be: (cast(^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 u16be: (^u16be) (ptr)^ = cast(u16be) bounded_uint(value, cast(u128)max(u16be) ) or_return
case u32be: (^u32be) (ptr)^ = cast(u32be) bounded_uint(value, cast(u128)max(u32be) ) or_return
case u64be: (^u64be) (ptr)^ = cast(u64be) bounded_uint(value, cast(u128)max(u64be) ) or_return
case u128be: (^u128be) (ptr)^ = cast(u128be) bounded_uint(value, cast(u128)max(u128be) ) or_return
}
}
@@ -92,60 +92,60 @@ parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info:
return false
}
(cast(^rune)ptr)^ = utf8.rune_at_pos(str, 0)
(^rune)(ptr)^ = utf8.rune_at_pos(str, 0)
case runtime.Type_Info_Float:
value := strconv.parse_f64(str) or_return
switch type_info.id {
case f16: (cast(^f16) ptr)^ = cast(f16) value
case f32: (cast(^f32) ptr)^ = cast(f32) value
case f64: (cast(^f64) ptr)^ = value
case f16: (^f16) (ptr)^ = cast(f16) value
case f32: (^f32) (ptr)^ = cast(f32) value
case f64: (^f64) (ptr)^ = value
case f16le: (cast(^f16le)ptr)^ = cast(f16le) value
case f32le: (cast(^f32le)ptr)^ = cast(f32le) value
case f64le: (cast(^f64le)ptr)^ = cast(f64le) value
case f16le: (^f16le)(ptr)^ = cast(f16le) value
case f32le: (^f32le)(ptr)^ = cast(f32le) value
case f64le: (^f64le)(ptr)^ = cast(f64le) value
case f16be: (cast(^f16be)ptr)^ = cast(f16be) value
case f32be: (cast(^f32be)ptr)^ = cast(f32be) value
case f64be: (cast(^f64be)ptr)^ = cast(f64be) value
case f16be: (^f16be)(ptr)^ = cast(f16be) value
case f32be: (^f32be)(ptr)^ = cast(f32be) value
case f64be: (^f64be)(ptr)^ = cast(f64be) value
}
case runtime.Type_Info_Complex:
value := strconv.parse_complex128(str) or_return
switch type_info.id {
case complex128: (cast(^complex128)ptr)^ = value
case complex64: (cast(^complex64) ptr)^ = cast(complex64)value
case complex32: (cast(^complex32) ptr)^ = cast(complex32)value
case complex32: (^complex32) (ptr)^ = (complex32)(value)
case complex64: (^complex64) (ptr)^ = (complex64)(value)
case complex128: (^complex128)(ptr)^ = value
}
case runtime.Type_Info_Quaternion:
value := strconv.parse_quaternion256(str) or_return
switch type_info.id {
case quaternion256: (cast(^quaternion256)ptr)^ = value
case quaternion128: (cast(^quaternion128)ptr)^ = cast(quaternion128)value
case quaternion64: (cast(^quaternion64) ptr)^ = cast(quaternion64)value
case quaternion64: (^quaternion64) (ptr)^ = (quaternion64)(value)
case quaternion128: (^quaternion128)(ptr)^ = (quaternion128)(value)
case quaternion256: (^quaternion256)(ptr)^ = value
}
case runtime.Type_Info_String:
if specific_type_info.is_cstring {
cstr_ptr := cast(^cstring)ptr
cstr_ptr := (^cstring)(ptr)
if cstr_ptr != nil {
// Prevent memory leaks from us setting this value multiple times.
delete(cstr_ptr^)
}
cstr_ptr^ = strings.clone_to_cstring(str)
} else {
(cast(^string)ptr)^ = str
(^string)(ptr)^ = str
}
case runtime.Type_Info_Boolean:
value := strconv.parse_bool(str) or_return
switch type_info.id {
case bool: (cast(^bool) ptr)^ = value
case b8: (cast(^b8) ptr)^ = cast(b8) value
case b16: (cast(^b16) ptr)^ = cast(b16) value
case b32: (cast(^b32) ptr)^ = cast(b32) value
case b64: (cast(^b64) ptr)^ = cast(b64) value
case bool: (^bool)(ptr)^ = value
case b8: (^b8) (ptr)^ = b8(value)
case b16: (^b16) (ptr)^ = b16(value)
case b32: (^b32) (ptr)^ = b32(value)
case b64: (^b64) (ptr)^ = b64(value)
}
case runtime.Type_Info_Bit_Set:
@@ -154,9 +154,9 @@ parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info:
value: u128
// NOTE: `upper` is inclusive, i.e: `0..=31`
max_bit_index := cast(u128)(1 + specific_type_info.upper - specific_type_info.lower)
bit_index : u128 = 0
#no_bounds_check for string_index : uint = 0; string_index < len(str); string_index += 1 {
max_bit_index := u128(1 + specific_type_info.upper - specific_type_info.lower)
bit_index := u128(0)
#no_bounds_check for string_index in 0..<uint(len(str)) {
if bit_index == max_bit_index {
// The string's too long for this bit_set.
return false
@@ -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)
} else {
switch 8*type_info.size {
case 8: (cast(^u8) ptr)^ = cast(u8) value
case 16: (cast(^u16) ptr)^ = cast(u16) value
case 32: (cast(^u32) ptr)^ = cast(u32) value
case 64: (cast(^u64) ptr)^ = cast(u64) value
case 128: (cast(^u128) ptr)^ = cast(u128) value
case 8: (^u8) (ptr)^ = cast(u8) value
case 16: (^u16) (ptr)^ = cast(u16) value
case 32: (^u32) (ptr)^ = cast(u32) value
case 64: (^u64) (ptr)^ = cast(u64) value
case 128: (^u128)(ptr)^ = value
}
}
@@ -222,7 +222,7 @@ parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type:
mode: int
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] {
case 'r': wants_read = 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 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
}
(cast(^os.Handle)ptr)^ = handle
(^os.Handle)(ptr)^ = handle
return
}
@@ -289,7 +289,7 @@ parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type:
return
}
(cast(^time.Time)ptr)^ = res
(^time.Time)(ptr)^ = res
return
} else if data_type == datetime.DateTime {
// 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
}
(cast(^datetime.DateTime)ptr)^ = res
(^datetime.DateTime)(ptr)^ = res
return
}
}
@@ -323,44 +323,44 @@ parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type:
@(optimization_mode="size")
set_unbounded_integer_by_type :: proc(ptr: rawptr, value: $T, data_type: typeid) where intrinsics.type_is_integer(T) {
switch data_type {
case i8: (cast(^i8) ptr)^ = cast(i8) value
case i16: (cast(^i16) ptr)^ = cast(i16) value
case i32: (cast(^i32) ptr)^ = cast(i32) value
case i64: (cast(^i64) ptr)^ = cast(i64) value
case i128: (cast(^i128) ptr)^ = cast(i128) value
case i8: (^i8) (ptr)^ = cast(i8) value
case i16: (^i16) (ptr)^ = cast(i16) value
case i32: (^i32) (ptr)^ = cast(i32) value
case i64: (^i64) (ptr)^ = cast(i64) value
case i128: (^i128) (ptr)^ = cast(i128) value
case int: (cast(^int) ptr)^ = cast(int) value
case int: (^int) (ptr)^ = cast(int) value
case i16le: (cast(^i16le) ptr)^ = cast(i16le) value
case i32le: (cast(^i32le) ptr)^ = cast(i32le) value
case i64le: (cast(^i64le) ptr)^ = cast(i64le) value
case i128le: (cast(^i128le) ptr)^ = cast(i128le) value
case i16le: (^i16le) (ptr)^ = cast(i16le) value
case i32le: (^i32le) (ptr)^ = cast(i32le) value
case i64le: (^i64le) (ptr)^ = cast(i64le) value
case i128le: (^i128le) (ptr)^ = cast(i128le) value
case i16be: (cast(^i16be) ptr)^ = cast(i16be) value
case i32be: (cast(^i32be) ptr)^ = cast(i32be) value
case i64be: (cast(^i64be) ptr)^ = cast(i64be) value
case i128be: (cast(^i128be) ptr)^ = cast(i128be) value
case i16be: (^i16be) (ptr)^ = cast(i16be) value
case i32be: (^i32be) (ptr)^ = cast(i32be) value
case i64be: (^i64be) (ptr)^ = cast(i64be) value
case i128be: (^i128be) (ptr)^ = cast(i128be) value
case u8: (cast(^u8) ptr)^ = cast(u8) value
case u16: (cast(^u16) ptr)^ = cast(u16) value
case u32: (cast(^u32) ptr)^ = cast(u32) value
case u64: (cast(^u64) ptr)^ = cast(u64) value
case u128: (cast(^u128) ptr)^ = cast(u128) value
case u8: (^u8) (ptr)^ = cast(u8) value
case u16: (^u16) (ptr)^ = cast(u16) value
case u32: (^u32) (ptr)^ = cast(u32) value
case u64: (^u64) (ptr)^ = cast(u64) value
case u128: (^u128) (ptr)^ = cast(u128) value
case uint: (cast(^uint) ptr)^ = cast(uint) value
case uintptr: (cast(^uintptr)ptr)^ = cast(uintptr) value
case uint: (^uint) (ptr)^ = cast(uint) value
case uintptr: (^uintptr)(ptr)^ = cast(uintptr) value
case u16le: (cast(^u16le) ptr)^ = cast(u16le) value
case u32le: (cast(^u32le) ptr)^ = cast(u32le) value
case u64le: (cast(^u64le) ptr)^ = cast(u64le) value
case u128le: (cast(^u128le) ptr)^ = cast(u128le) value
case u16le: (^u16le) (ptr)^ = cast(u16le) value
case u32le: (^u32le) (ptr)^ = cast(u32le) value
case u64le: (^u64le) (ptr)^ = cast(u64le) value
case u128le: (^u128le) (ptr)^ = cast(u128le) value
case u16be: (cast(^u16be) ptr)^ = cast(u16be) value
case u32be: (cast(^u32be) ptr)^ = cast(u32be) value
case u64be: (cast(^u64be) ptr)^ = cast(u64be) value
case u128be: (cast(^u128be) ptr)^ = cast(u128be) value
case u16be: (^u16be) (ptr)^ = cast(u16be) value
case u32be: (^u32be) (ptr)^ = cast(u32be) value
case u64be: (^u64be) (ptr)^ = cast(u64be) value
case u128be: (^u128be) (ptr)^ = cast(u128be) value
case rune: (cast(^rune) ptr)^ = cast(rune) value
case rune: (^rune) (ptr)^ = cast(rune) value
case:
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)(
cast(uintptr)ptr.data +
cast(uintptr)((ptr.len - 1) * specific_type_info.elem.size))
subptr := rawptr(
uintptr(ptr.data) +
uintptr((ptr.len - 1) * specific_type_info.elem.size))
mem.copy(subptr, raw_data(elem_backing), len(elem_backing))
case runtime.Type_Info_Enum:
@@ -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 pos_subtag, pos_ok := get_struct_subtag(args_tag, SUBTAG_POS); pos_ok {
if value, parse_ok := strconv.parse_u64_of_base(pos_subtag, 10); parse_ok {
return 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_field_by_pos :: proc(model: ^$T, pos: int) -> (result: reflect.Struct_Field, index: int, ok: bool) {
for field, i in reflect.struct_fields_zipped(T) {
args_tag, tag_ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS)
if !tag_ok {
continue
}
pos_subtag, pos_ok := get_struct_subtag(args_tag, SUBTAG_POS)
if !pos_ok {
continue
}
args_tag := reflect.struct_tag_lookup(field.tag, TAG_ARGS) or_continue
pos_subtag := get_struct_subtag(args_tag, SUBTAG_POS) or_continue
value, parse_ok := strconv.parse_u64_of_base(pos_subtag, 10)
if parse_ok && cast(int)value == pos {
+14 -17
View File
@@ -92,7 +92,7 @@ _user_formatters: ^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.")
_user_formatters = m
_user_formatters = m
}
// 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
if fi.hash && !fi.zero && start == 0 { flags |= {.Prefix} }
if fi.plus { flags |= {.Plus} }
if fi.hash && !fi.zero && start == 0 { flags += {.Prefix} }
if fi.plus { flags += {.Plus} }
s := strconv.append_bits(buf[start:], u, base, is_signed, bit_size, digits, flags)
prev_zero := fi.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
if fi.hash && !fi.zero && start == 0 { flags |= {.Prefix} }
if fi.plus { flags |= {.Plus} }
if fi.hash && !fi.zero && start == 0 { flags += {.Prefix} }
if fi.plus { flags += {.Plus} }
s := strconv.append_bits_128(buf[start:], u, base, is_signed, bit_size, digits, flags)
if fi.hash && fi.zero && fi.indent == 0 {
@@ -1229,10 +1229,10 @@ _fmt_memory :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, units: st
// Add the unit at the end.
copy(buf[len(str):], units[off:off+unit_len])
str = string(buf[:len(str)+unit_len])
if !fi.plus {
// Strip sign from "+<value>" but not "+Inf".
if str[0] == '+' && str[1] != 'I' {
if !fi.plus {
// Strip sign from "+<value>" but not "+Inf".
if str[0] == '+' && str[1] != 'I' {
str = str[1:]
}
}
@@ -1460,13 +1460,10 @@ fmt_string :: proc(fi: ^Info, s: string, verb: rune) {
if !fi.minus {
io.write_string(fi.writer, s, &fi.n)
}
}
else {
} else {
io.write_string(fi.writer, s, &fi.n)
}
}
else
{
} else {
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 {
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
}
for ev, evi in e.values {
@@ -2712,7 +2709,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
return
}
if fi.indirection_level < 1 {
fi.indirection_level += 1
fi.indirection_level += 1
defer fi.indirection_level -= 1
io.write_byte(fi.writer, '&')
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_Map:
if fi.indirection_level < 1 {
fi.indirection_level += 1
fi.indirection_level += 1
defer fi.indirection_level -= 1
io.write_byte(fi.writer, '&', &fi.n)
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.
*/
@(optimization_mode="speed")
XXH3_hashLong_64b_withSeed_internal :: #force_no_inline proc(input: []u8,
seed: xxh_u64,
f_acc512: XXH3_accumulate_512_f,
f_scramble: XXH3_scramble_accumulator_f,
f_init_sec: XXH3_init_custom_secret_f) -> (hash: xxh_u64) {
XXH3_hashLong_64b_withSeed_internal :: #force_no_inline proc(
input: []u8,
seed: xxh_u64,
f_acc512: XXH3_accumulate_512_f,
f_scramble: XXH3_scramble_accumulator_f,
f_init_sec: XXH3_init_custom_secret_f,
) -> (hash: xxh_u64) {
if seed == 0 {
return XXH3_hashLong_64b_internal(input, XXH3_kSecret[:], f_acc512, f_scramble)
}
{
secret: [XXH_SECRET_DEFAULT_SIZE]u8
f_init_sec(secret[:], seed)
return XXH3_hashLong_64b_internal(input, secret[:], f_acc512, f_scramble)
}
secret: [XXH_SECRET_DEFAULT_SIZE]u8
f_init_sec(secret[:], seed)
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
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
@@ -131,7 +131,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
if .info in options {
options |= {.return_metadata, .do_not_decompress_image}
options += {.return_metadata, .do_not_decompress_image}
options -= {.info}
}
@@ -735,7 +735,7 @@ destroy :: proc(img: ^Image) {
bytes.buffer_destroy(&img.pixels)
if v, ok := img.metadata.(^image.BMP_Info); ok {
free(v)
free(v)
}
free(img)
}
+41 -41
View File
@@ -1313,55 +1313,55 @@ expand_grayscale :: proc(img: ^Image, allocator := context.allocator) -> (ok: bo
}
switch img.depth {
case 8:
switch img.channels {
case 1: // Turn Gray into RGB
out := mem.slice_data_cast([]RGB_Pixel, buf.buf[:])
case 8:
switch img.channels {
case 1: // Turn Gray into RGB
out := mem.slice_data_cast([]RGB_Pixel, buf.buf[:])
for p in img.pixels.buf {
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, 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()
for p in img.pixels.buf {
out[0] = p // Broadcast gray value into RGB components.
out = out[1:]
}
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[:])
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] = 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()
for p in inp {
out[0].rgb = p.r // Gray component.
out[0].a = p.g // Alpha component.
}
case:
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
if .info in options {
options |= {.return_metadata, .do_not_decompress_image}
options += {.return_metadata, .do_not_decompress_image}
options -= {.info}
}
@@ -354,7 +354,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
}
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 {
@@ -535,28 +535,28 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
ct := transmute(u8)info.header.color_type
switch ct {
case 3: // Indexed color
if c.header.length != 1 {
return {}, .BKGD_Invalid_Length
}
col := _plte.entries[c.data[0]]
img.background = [3]u16{
u16(col[0]) << 8 | u16(col[0]),
u16(col[1]) << 8 | u16(col[1]),
u16(col[2]) << 8 | u16(col[2]),
}
case 0, 4: // Grayscale, with and without Alpha
if c.header.length != 2 {
return {}, .BKGD_Invalid_Length
}
col := u16(mem.slice_data_cast([]u16be, c.data[:])[0])
img.background = [3]u16{col, col, col}
case 2, 6: // Color, with and without Alpha
if c.header.length != 6 {
return {}, .BKGD_Invalid_Length
}
col := mem.slice_data_cast([]u16be, c.data[:])
img.background = [3]u16{u16(col[0]), u16(col[1]), u16(col[2])}
case 3: // Indexed color
if c.header.length != 1 {
return {}, .BKGD_Invalid_Length
}
col := _plte.entries[c.data[0]]
img.background = [3]u16{
u16(col[0]) << 8 | u16(col[0]),
u16(col[1]) << 8 | u16(col[1]),
u16(col[2]) << 8 | u16(col[2]),
}
case 0, 4: // Grayscale, with and without Alpha
if c.header.length != 2 {
return {}, .BKGD_Invalid_Length
}
col := u16(mem.slice_data_cast([]u16be, c.data[:])[0])
img.background = [3]u16{col, col, col}
case 2, 6: // Color, with and without Alpha
if c.header.length != 6 {
return {}, .BKGD_Invalid_Length
}
col := mem.slice_data_cast([]u16be, c.data[:])
img.background = [3]u16{u16(col[0]), u16(col[1]), u16(col[2])}
}
case .tRNS:
+5 -7
View File
@@ -139,15 +139,13 @@ save_to_buffer :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}
} else {
// Write RGB literal
output.buf[written] = u8(QOI_Opcode_Tag.RGB)
pix_bytes := transmute([4]u8)pix
copy(output.buf[written + 1:], pix_bytes[:3])
copy(output.buf[written + 1:], pix[:3])
written += 4
}
} else {
// Write RGBA literal
output.buf[written] = u8(QOI_Opcode_Tag.RGBA)
pix_bytes := transmute([4]u8)pix
copy(output.buf[written + 1:], pix_bytes[:])
copy(output.buf[written + 1:], pix[:])
written += 5
}
}
@@ -178,7 +176,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
options := options
if .info in options {
options |= {.return_metadata, .do_not_decompress_image}
options += {.return_metadata, .do_not_decompress_image}
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)
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)
if v, ok := img.metadata.(^image.QOI_Info); ok {
free(v)
free(v)
}
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 {
options |= {.return_metadata, .do_not_decompress_image}
options += {.return_metadata, .do_not_decompress_image}
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.
See comments of `_private_int_mul_karatsuba` for details.
It is essentially the same algorithm but merely tuned to perform recursive squarings.
See comments of `_private_int_mul_karatsuba` for details.
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) {
context.allocator = allocator
@@ -967,7 +967,7 @@ _private_int_div_3 :: proc(quotient, numerator: ^Int, allocator := context.alloc
/*
b = 2^_DIGIT_BITS / 3
*/
b := _WORD(1) << _WORD(_DIGIT_BITS) / _WORD(3)
b := _WORD(1) << _WORD(_DIGIT_BITS) / _WORD(3)
q := &Int{}
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
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])
if w >= 3 {
/*
@@ -1007,8 +1007,8 @@ _private_int_div_3 :: proc(quotient, numerator: ^Int, allocator := context.alloc
*/
if quotient != nil {
err = clamp(q)
internal_swap(q, quotient)
}
internal_swap(q, quotient)
}
internal_destroy(q)
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`.
Get copies of `a` and `b` we can modify.
*/
Get copies of `a` and `b` we can modify.
*/
u, v := &Int{}, &Int{}
defer internal_destroy(u, v)
internal_copy(u, a) 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
/*
B1. Find the common power of two for `u` and `v`.
*/
u_lsb, _ := internal_count_lsb(u)
v_lsb, _ := internal_count_lsb(v)
k := min(u_lsb, v_lsb)
/*
B1. Find the common power of two for `u` and `v`.
*/
u_lsb, _ := internal_count_lsb(u)
v_lsb, _ := internal_count_lsb(v)
k := min(u_lsb, v_lsb)
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
}
/*
Multiply by 2**k which we divided out at the beginning.
*/
internal_shl(temp_gcd_res, u, k) or_return
temp_gcd_res.sign = .Zero_or_Positive
/*
Multiply by 2**k which we divided out at the beginning.
*/
internal_shl(temp_gcd_res, u, k) or_return
temp_gcd_res.sign = .Zero_or_Positive
/*
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`
This is computed in place and on the fly. The multiplication
by b**i is handled by offseting which columns the results
are added to.
by b**i is handled by offseting which columns the results
are added to.
Note the comba method normally doesn't handle carries in the
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 "base:builtin"
import "base:intrinsics"
import "base:runtime"
@require import "base:runtime"
// 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.
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
si.x -= 1
ssi -= UNSKEW_4D
}
else if si.y > si.x && si.y >= si.z && si.y >= si.w && si.y >= score {
case si.y > si.x && si.y >= si.z && si.y >= si.w && si.y >= score:
svp.y += PRIME_Y
si.y -= 1
ssi -= UNSKEW_4D
}
else if si.z > si.x && si.z > si.y && si.z >= si.w && si.z >= score {
case si.z > si.x && si.z > si.y && si.z >= si.w && si.z >= score:
svp.z += PRIME_Z
si.z -= 1
ssi -= UNSKEW_4D
}
else if si.w > si.x && si.w > si.y && si.w > si.z && si.w >= score {
case si.w > si.x && si.w > si.y && si.w > si.z && si.w >= score:
svp.w += PRIME_W
si.w -= 1
ssi -= UNSKEW_4D
+1 -1
View File
@@ -655,7 +655,7 @@ choice_enum :: proc($T: typeid) -> T
where
intrinsics.type_is_enum(T),
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)) &&
u64(max(T)) > u64(max(i64)) {
+9 -4
View File
@@ -97,6 +97,10 @@ init :: proc{init_from_buffer, init_from_allocator}
destroy :: proc(control: ^Allocator) {
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.
// 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
@@ -132,8 +136,9 @@ allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
return nil, nil
case .Free_All:
clear(control)
return nil, nil
// NOTE: this doesn't work right at the moment, Jeroen has it on his to-do list :)
// clear(control)
return nil, .Mode_Not_Implemented
case .Resize:
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:
set := (^runtime.Allocator_Mode_Set)(old_memory)
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
@@ -153,4 +158,4 @@ allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
}
return nil, nil
}
}
+3 -3
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) {
next = block_next(block)
next.prev_phys_block = block
return
return
}
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)
alloc_bytes :: proc(control: ^Allocator, size: uint, align: uint) -> (res: []byte, err: runtime.Allocator_Error) {
res, err = alloc_bytes_non_zeroed(control, size, align)
if err != nil {
if err == nil {
intrinsics.mem_zero(raw_data(res), len(res))
}
return
@@ -735,4 +735,4 @@ resize_non_zeroed :: proc(control: ^Allocator, ptr: rawptr, old_size, new_size:
block_trim_used(control, block, adjust)
res = ([^]byte)(ptr)[:new_size]
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 {
pflags: linux.Mem_Protection
pflags = {}
if .Read in flags { pflags |= {.READ} }
if .Write in flags { pflags |= {.WRITE} }
if .Execute in flags { pflags |= {.EXEC} }
if .Read in flags { pflags += {.READ} }
if .Write in flags { pflags += {.WRITE} }
if .Execute in flags { pflags += {.EXEC} }
errno := linux.mprotect(data, size, pflags)
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])
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}
best := Zero_Run{-1, -1}
addr := transmute([8]u16be)v
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.
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
}
for val, i in addr {
for val, i in v {
if best.start == i || best.end == i {
// For the left and right side of the best zero run, print a `:`.
fmt.sbprint(&b, ":")
+1
View File
@@ -135,6 +135,7 @@ TCP_Send_Error :: enum c.int {
No_Buffer_Space_Available = win.WSAENOBUFS,
Network_Subsystem_Failure = win.WSAENETDOWN,
Host_Unreachable = win.WSAEHOSTUNREACH,
Would_Block = win.WSAEWOULDBLOCK,
// TODO: verify possible, as not mentioned in docs
Offline = win.WSAENETUNREACH,
+13 -16
View File
@@ -59,24 +59,21 @@ _enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []
switch int(ifaddr.address.family) {
case os.AF_INET, os.AF_INET6:
address = _sockaddr_basic_to_endpoint(ifaddr.address).address
case:
}
}
if ifaddr.netmask != nil {
switch int(ifaddr.netmask.family) {
case os.AF_INET, os.AF_INET6:
netmask = Netmask(_sockaddr_basic_to_endpoint(ifaddr.netmask).address)
case:
netmask = Netmask(_sockaddr_basic_to_endpoint(ifaddr.netmask).address)
}
}
if ifaddr.broadcast_or_dest != nil && .BROADCAST in ifaddr.flags {
switch int(ifaddr.broadcast_or_dest.family) {
case os.AF_INET, os.AF_INET6:
broadcast := _sockaddr_basic_to_endpoint(ifaddr.broadcast_or_dest).address
append(&iface.multicast, broadcast)
case:
broadcast := _sockaddr_basic_to_endpoint(ifaddr.broadcast_or_dest).address
append(&iface.multicast, broadcast)
}
}
@@ -91,19 +88,19 @@ _enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []
/*
TODO: Refine this based on the type of adapter.
*/
state := Link_State{}
state := Link_State{}
if .UP in ifaddr.flags {
state |= {.Up}
}
if .UP in ifaddr.flags {
state += {.Up}
}
/*if .DORMANT in ifaddr.flags {
state |= {.Dormant}
}*/
/*if .DORMANT in ifaddr.flags {
state |= {.Dormant}
}*/
if .LOOPBACK in ifaddr.flags {
state |= {.Loopback}
}
if .LOOPBACK in ifaddr.flags {
state += {.Loopback}
}
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) {
context.allocator = allocator
buf: []u8
defer delete(buf)
buf: []u8
defer delete(buf)
buf_size: u32
res: u32
buf_size: u32
res: u32
gaa: for _ in 1..=MAX_INTERFACE_ENUMERATION_TRIES {
res = sys.get_adapters_addresses(
.Unspecified, // Return both IPv4 and IPv6 adapters.
gaa: for _ in 1..=MAX_INTERFACE_ENUMERATION_TRIES {
res = sys.get_adapters_addresses(
.Unspecified, // Return both IPv4 and IPv6 adapters.
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_Gateways, // (Vista+) Return the addresses of default gateways.
.Include_Tunnel_Binding_Order, // (Vista+) Return the adapter addresses sorted in tunnel binding order.
},
nil, // Reserved
(^sys.IP_Adapter_Addresses)(raw_data(buf)),
&buf_size,
)
nil, // Reserved
(^sys.IP_Adapter_Addresses)(raw_data(buf)),
&buf_size,
)
switch res {
case 111: // ERROR_BUFFER_OVERFLOW:
delete(buf)
buf = make([]u8, buf_size)
case 0:
break gaa
case:
return {}, Platform_Error(res)
}
}
switch res {
case 111: // ERROR_BUFFER_OVERFLOW:
delete(buf)
buf = make([]u8, buf_size)
case 0:
break gaa
case:
return {}, Platform_Error(res)
}
}
if res != 0 {
return {}, .Unable_To_Enumerate_Network_Interfaces
}
if res != 0 {
return {}, .Unable_To_Enumerate_Network_Interfaces
}
_interfaces := make([dynamic]Network_Interface, 0, allocator)
for adapter := (^sys.IP_Adapter_Addresses)(raw_data(buf)); adapter != nil; adapter = adapter.Next {
_interfaces := make([dynamic]Network_Interface, 0, allocator)
for adapter := (^sys.IP_Adapter_Addresses)(raw_data(buf)); adapter != nil; adapter = adapter.Next {
friendly_name, err1 := sys.wstring_to_utf8(sys.wstring(adapter.FriendlyName), 256, allocator)
if err1 != nil { return {}, Platform_Error(err1) }
@@ -71,74 +71,74 @@ _enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []
interface := Network_Interface{
adapter_name = strings.clone(string(adapter.AdapterName)),
friendly_name = friendly_name,
description = description,
dns_suffix = dns_suffix,
friendly_name = friendly_name,
description = description,
dns_suffix = dns_suffix,
mtu = adapter.MTU,
mtu = adapter.MTU,
link = {
link = {
transmit_speed = adapter.TransmitLinkSpeed,
receive_speed = adapter.ReceiveLinkSpeed,
},
}
},
}
if adapter.PhysicalAddressLength > 0 && adapter.PhysicalAddressLength <= len(adapter.PhysicalAddress) {
interface.physical_address = physical_address_to_string(adapter.PhysicalAddress[:adapter.PhysicalAddressLength])
}
if adapter.PhysicalAddressLength > 0 && adapter.PhysicalAddressLength <= len(adapter.PhysicalAddress) {
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 {
win_addr := parse_socket_address(u_addr.Address)
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)
lease := Lease{
address = win_addr.address,
origin = {
prefix = Prefix_Origin(u_addr.PrefixOrigin),
suffix = Suffix_Origin(u_addr.SuffixOrigin),
},
lifetime = {
valid = u_addr.ValidLifetime,
preferred = u_addr.PreferredLifetime,
lease = u_addr.LeaseLifetime,
},
address_duplication = Address_Duplication(u_addr.DadState),
}
append(&interface.unicast, lease)
}
lease := Lease{
address = win_addr.address,
origin = {
prefix = Prefix_Origin(u_addr.PrefixOrigin),
suffix = Suffix_Origin(u_addr.SuffixOrigin),
},
lifetime = {
valid = u_addr.ValidLifetime,
preferred = u_addr.PreferredLifetime,
lease = u_addr.LeaseLifetime,
},
address_duplication = Address_Duplication(u_addr.DadState),
}
append(&interface.unicast, lease)
}
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)
append(&interface.anycast, addr.address)
}
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)
append(&interface.anycast, addr.address)
}
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)
append(&interface.multicast, addr.address)
}
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)
append(&interface.multicast, addr.address)
}
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)
append(&interface.gateways, addr.address)
}
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)
append(&interface.gateways, addr.address)
}
interface.dhcp_v4 = parse_socket_address(adapter.Dhcpv4Server).address
interface.dhcp_v6 = parse_socket_address(adapter.Dhcpv6Server).address
switch adapter.OperStatus {
case .Up: interface.link.state = {.Up}
case .Down: interface.link.state = {.Down}
case .Testing: interface.link.state = {.Testing}
case .Dormant: interface.link.state = {.Dormant}
case .NotPresent: interface.link.state = {.Not_Present}
case .LowerLayerDown: interface.link.state = {.Lower_Layer_Down}
case .Unknown: fallthrough
case: interface.link.state = {}
}
switch adapter.OperStatus {
case .Up: interface.link.state = {.Up}
case .Down: interface.link.state = {.Down}
case .Testing: interface.link.state = {.Testing}
case .Dormant: interface.link.state = {.Dormant}
case .NotPresent: interface.link.state = {.Not_Present}
case .LowerLayerDown: interface.link.state = {.Lower_Layer_Down}
case .Unknown: fallthrough
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[:], {}
}
+2 -3
View File
@@ -274,8 +274,7 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca
.Linger,
.Send_Timeout,
.Receive_Timeout:
t, ok := value.(time.Duration)
if !ok do panic("set_option() value must be a time.Duration here", loc)
t := value.(time.Duration) or_else panic("set_option() value must be a time.Duration here", loc)
micros := i64(time.duration_microseconds(t))
timeval_value.microseconds = int(micros % 1e6)
@@ -320,7 +319,7 @@ _set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_E
}
if should_block {
flags &= ~int(os.O_NONBLOCK)
flags &~= int(os.O_NONBLOCK)
} else {
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 = {
sin_family = .INET,
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:
return {
ipv6 = {
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,
},
}
@@ -377,9 +377,9 @@ _set_blocking :: proc(sock: Any_Socket, should_block: bool) -> (err: Network_Err
return Set_Blocking_Error(errno)
}
if should_block {
flags &= ~{.NONBLOCK}
flags -= {.NONBLOCK}
} else {
flags |= {.NONBLOCK}
flags += {.NONBLOCK}
}
errno = linux.fcntl(os_sock, linux.F_SETFL, flags)
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
len = size_of(bool_value)
case .Linger:
t, ok := value.(time.Duration)
if !ok do panic("set_option() value must be a time.Duration here", loc)
t := value.(time.Duration) or_else panic("set_option() value must be a time.Duration here", loc)
num_secs := i64(time.duration_seconds(t))
if time.Duration(num_secs * 1e9) != t do return .Linger_Only_Supports_Whole_Seconds
if num_secs > i64(max(u16)) do return .Value_Out_Of_Range
if time.Duration(num_secs * 1e9) != t {
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_linger = c.ushort(num_secs)
@@ -277,8 +280,7 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca
case
.Receive_Timeout,
.Send_Timeout:
t, ok := value.(time.Duration)
if !ok do panic("set_option() value must be a time.Duration here", loc)
t := value.(time.Duration) or_else panic("set_option() value must be a time.Duration here", loc)
int_value = i32(time.duration_milliseconds(t))
ptr = &int_value
+1
View File
@@ -513,6 +513,7 @@ Package_Decl :: struct {
Import_Decl :: struct {
using node: Decl,
docs: ^Comment_Group,
attributes: [dynamic]^Attribute, // dynamic as parsing will add to them lazily
is_using: bool,
import_tok: 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
for p.curr_tok.kind == .Comment &&
p.curr_tok.pos.line <= end_line+n {
comment: tokenizer.Token
comment: tokenizer.Token
comment, end_line = consume_comment(p)
append(&list, comment)
}
@@ -689,7 +689,12 @@ parse_when_stmt :: proc(p: ^Parser) -> ^ast.When_Stmt {
prev_level := p.expr_level
p.expr_level = -1
prev_allow_in_expr := p.allow_in_expr
p.allow_in_expr = true
cond = parse_expr(p, false)
p.allow_in_expr = prev_allow_in_expr
p.expr_level = prev_level
if cond == nil {
@@ -1103,6 +1108,9 @@ parse_attribute :: proc(p: ^Parser, tok: tokenizer.Token, open_kind, close_kind:
case ^ast.Foreign_Import_Decl:
if d.docs == nil { d.docs = docs }
append(&d.attributes, attribute)
case ^ast.Import_Decl:
if d.docs == nil { d.docs = docs }
append(&d.attributes, attribute)
case:
error(p, decl.pos, "expected a value or foreign declaration after an attribute")
free(attribute)
@@ -1307,8 +1315,8 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
// Unary Expressions
.Add, .Sub, .Xor, .Not, .And:
s := parse_simple_stmt(p, {Stmt_Allow_Flag.Label})
expect_semicolon(p, s)
s := parse_simple_stmt(p, {Stmt_Allow_Flag.Label})
expect_semicolon(p, 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 ||
p.curr_tok.kind == .EOF {
break
break
}
advance_token(p)
}
@@ -2208,10 +2216,10 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
case .Integer, .Float, .Imag,
.Rune, .String:
tok := advance_token(p)
bl := ast.new(ast.Basic_Lit, tok.pos, end_pos(tok))
bl.tok = tok
return bl
tok := advance_token(p)
bl := ast.new(ast.Basic_Lit, tok.pos, end_pos(tok))
bl.tok = tok
return bl
case .Open_Brace:
if !lhs {
@@ -2999,7 +3007,7 @@ parse_literal_value :: proc(p: ^Parser, type: ^ast.Expr) -> ^ast.Comp_Lit {
}
p.expr_level -= 1
skip_possible_newline(p)
skip_possible_newline(p)
close := expect_closing_brace_of_field_list(p)
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
err: Errno
if length, err = file_size(fd); err != 0 {
return nil, false
return nil, false
}
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)
if data == nil {
return nil, false
return nil, false
}
bytes_read, read_err := read_full(fd, data)
+210 -9
View File
@@ -2,29 +2,230 @@
package os2
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) {
//TODO
if _org_env_begin == 0 {
_build_env()
}
if v, idx := _lookup(key); idx != -1 {
found = true
value, _ = clone_string(v, allocator)
}
return
}
_set_env :: proc(key, value: string) -> bool {
//TODO
return false
_set_env :: proc(key, v_new: string) -> bool {
if _org_env_begin == 0 {
_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 {
//TODO
return false
if _org_env_begin == 0 {
_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() {
//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 {
//TODO
return nil
if _org_env_begin == 0 {
_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"
}
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
package os2
import "core:sys/unix"
import "core:sys/linux"
EPERM :: 1
ENOENT :: 2
ESRCH :: 3
EINTR :: 4
EIO :: 5
ENXIO :: 6
EBADF :: 9
EAGAIN :: 11
ENOMEM :: 12
EACCES :: 13
EFAULT :: 14
EEXIST :: 17
ENODEV :: 19
ENOTDIR :: 20
EISDIR :: 21
EINVAL :: 22
ENFILE :: 23
EMFILE :: 24
ETXTBSY :: 26
EFBIG :: 27
ENOSPC :: 28
ESPIPE :: 29
EROFS :: 30
EPIPE :: 32
ERANGE :: 34 /* Result too large */
EDEADLK :: 35 /* Resource deadlock would occur */
ENAMETOOLONG :: 36 /* File name too long */
ENOLCK :: 37 /* No record locks available */
ENOSYS :: 38 /* Invalid system call number */
ENOTEMPTY :: 39 /* Directory not empty */
ELOOP :: 40 /* Too many symbolic links encountered */
EWOULDBLOCK :: EAGAIN /* Operation would block */
ENOMSG :: 42 /* No message of desired type */
EIDRM :: 43 /* Identifier removed */
ECHRNG :: 44 /* Channel number out of range */
EL2NSYNC :: 45 /* Level 2 not synchronized */
EL3HLT :: 46 /* Level 3 halted */
EL3RST :: 47 /* Level 3 reset */
ELNRNG :: 48 /* Link number out of range */
EUNATCH :: 49 /* Protocol driver not attached */
ENOCSI :: 50 /* No CSI structure available */
EL2HLT :: 51 /* Level 2 halted */
EBADE :: 52 /* Invalid exchange */
EBADR :: 53 /* Invalid request descriptor */
EXFULL :: 54 /* Exchange full */
ENOANO :: 55 /* No anode */
EBADRQC :: 56 /* Invalid request code */
EBADSLT :: 57 /* Invalid slot */
EDEADLOCK :: EDEADLK
EBFONT :: 59 /* Bad font file format */
ENOSTR :: 60 /* Device not a stream */
ENODATA :: 61 /* No data available */
ETIME :: 62 /* Timer expired */
ENOSR :: 63 /* Out of streams resources */
ENONET :: 64 /* Machine is not on the network */
ENOPKG :: 65 /* Package not installed */
EREMOTE :: 66 /* Object is remote */
ENOLINK :: 67 /* Link has been severed */
EADV :: 68 /* Advertise error */
ESRMNT :: 69 /* Srmount error */
ECOMM :: 70 /* Communication error on send */
EPROTO :: 71 /* Protocol error */
EMULTIHOP :: 72 /* Multihop attempted */
EDOTDOT :: 73 /* RFS specific error */
EBADMSG :: 74 /* Not a data message */
EOVERFLOW :: 75 /* Value too large for defined data type */
ENOTUNIQ :: 76 /* Name not unique on network */
EBADFD :: 77 /* File descriptor in bad state */
EREMCHG :: 78 /* Remote address changed */
ELIBACC :: 79 /* Can not access a needed shared library */
ELIBBAD :: 80 /* Accessing a corrupted shared library */
ELIBSCN :: 81 /* .lib section in a.out corrupted */
ELIBMAX :: 82 /* Attempting to link in too many shared libraries */
ELIBEXEC :: 83 /* Cannot exec a shared library directly */
EILSEQ :: 84 /* Illegal byte sequence */
ERESTART :: 85 /* Interrupted system call should be restarted */
ESTRPIPE :: 86 /* Streams pipe error */
EUSERS :: 87 /* Too many users */
ENOTSOCK :: 88 /* Socket operation on non-socket */
EDESTADDRREQ :: 89 /* Destination address required */
EMSGSIZE :: 90 /* Message too long */
EPROTOTYPE :: 91 /* Protocol wrong type for socket */
ENOPROTOOPT :: 92 /* Protocol not available */
EPROTONOSUPPORT:: 93 /* Protocol not supported */
ESOCKTNOSUPPORT:: 94 /* Socket type not supported */
EOPNOTSUPP :: 95 /* Operation not supported on transport endpoint */
EPFNOSUPPORT :: 96 /* Protocol family not supported */
EAFNOSUPPORT :: 97 /* Address family not supported by protocol */
EADDRINUSE :: 98 /* Address already in use */
EADDRNOTAVAIL :: 99 /* Cannot assign requested address */
ENETDOWN :: 100 /* Network is down */
ENETUNREACH :: 101 /* Network is unreachable */
ENETRESET :: 102 /* Network dropped connection because of reset */
ECONNABORTED :: 103 /* Software caused connection abort */
ECONNRESET :: 104 /* Connection reset by peer */
ENOBUFS :: 105 /* No buffer space available */
EISCONN :: 106 /* Transport endpoint is already connected */
ENOTCONN :: 107 /* Transport endpoint is not connected */
ESHUTDOWN :: 108 /* Cannot send after transport endpoint shutdown */
ETOOMANYREFS :: 109 /* Too many references: cannot splice */
ETIMEDOUT :: 110 /* Connection timed out */
ECONNREFUSED :: 111 /* Connection refused */
EHOSTDOWN :: 112 /* Host is down */
EHOSTUNREACH :: 113 /* No route to host */
EALREADY :: 114 /* Operation already in progress */
EINPROGRESS :: 115 /* Operation now in progress */
ESTALE :: 116 /* Stale file handle */
EUCLEAN :: 117 /* Structure needs cleaning */
ENOTNAM :: 118 /* Not a XENIX named type file */
ENAVAIL :: 119 /* No XENIX semaphores available */
EISNAM :: 120 /* Is a named type file */
EREMOTEIO :: 121 /* Remote I/O error */
EDQUOT :: 122 /* Quota exceeded */
ENOMEDIUM :: 123 /* No medium found */
EMEDIUMTYPE :: 124 /* Wrong medium type */
ECANCELED :: 125 /* Operation Canceled */
ENOKEY :: 126 /* Required key not available */
EKEYEXPIRED :: 127 /* Key has expired */
EKEYREVOKED :: 128 /* Key has been revoked */
EKEYREJECTED :: 129 /* Key was rejected by service */
EOWNERDEAD :: 130 /* Owner died */
ENOTRECOVERABLE:: 131 /* State not recoverable */
ERFKILL :: 132 /* Operation not possible due to RF-kill */
EHWPOISON :: 133 /* Memory page has hardware error */
@(rodata)
_errno_strings : [linux.Errno]string = {
.NONE = "Success",
.EPERM = "Operation not permitted",
.ENOENT = "No such file or directory",
.ESRCH = "No such process",
.EINTR = "Interrupted system call",
.EIO = "Input/output error",
.ENXIO = "No such device or address",
.E2BIG = "Argument list too long",
.ENOEXEC = "Exec format error",
.EBADF = "Bad file descriptor",
.ECHILD = "No child processes",
.EAGAIN = "Resource temporarily unavailable",
.ENOMEM = "Cannot allocate memory",
.EACCES = "Permission denied",
.EFAULT = "Bad address",
.ENOTBLK = "Block device required",
.EBUSY = "Device or resource busy",
.EEXIST = "File exists",
.EXDEV = "Invalid cross-device link",
.ENODEV = "No such device",
.ENOTDIR = "Not a directory",
.EISDIR = "Is a directory",
.EINVAL = "Invalid argument",
.ENFILE = "Too many open files in system",
.EMFILE = "Too many open files",
.ENOTTY = "Inappropriate ioctl for device",
.ETXTBSY = "Text file busy",
.EFBIG = "File too large",
.ENOSPC = "No space left on device",
.ESPIPE = "Illegal seek",
.EROFS = "Read-only file system",
.EMLINK = "Too many links",
.EPIPE = "Broken pipe",
.EDOM = "Numerical argument out of domain",
.ERANGE = "Numerical result out of range",
.EDEADLK = "Resource deadlock avoided",
.ENAMETOOLONG = "File name too long",
.ENOLCK = "No locks available",
.ENOSYS = "Function not implemented",
.ENOTEMPTY = "Directory not empty",
.ELOOP = "Too many levels of symbolic links",
.EUNKNOWN_41 = "Unknown Error (41)",
.ENOMSG = "No message of desired type",
.EIDRM = "Identifier removed",
.ECHRNG = "Channel number out of range",
.EL2NSYNC = "Level 2 not synchronized",
.EL3HLT = "Level 3 halted",
.EL3RST = "Level 3 reset",
.ELNRNG = "Link number out of range",
.EUNATCH = "Protocol driver not attached",
.ENOCSI = "No CSI structure available",
.EL2HLT = "Level 2 halted",
.EBADE = "Invalid exchange",
.EBADR = "Invalid request descriptor",
.EXFULL = "Exchange full",
.ENOANO = "No anode",
.EBADRQC = "Invalid request code",
.EBADSLT = "Invalid slot",
.EUNKNOWN_58 = "Unknown Error (58)",
.EBFONT = "Bad font file format",
.ENOSTR = "Device not a stream",
.ENODATA = "No data available",
.ETIME = "Timer expired",
.ENOSR = "Out of streams resources",
.ENONET = "Machine is not on the network",
.ENOPKG = "Package not installed",
.EREMOTE = "Object is remote",
.ENOLINK = "Link has been severed",
.EADV = "Advertise error",
.ESRMNT = "Srmount error",
.ECOMM = "Communication error on send",
.EPROTO = "Protocol error",
.EMULTIHOP = "Multihop attempted",
.EDOTDOT = "RFS specific error",
.EBADMSG = "Bad message",
.EOVERFLOW = "Value too large for defined data type",
.ENOTUNIQ = "Name not unique on network",
.EBADFD = "File descriptor in bad state",
.EREMCHG = "Remote address changed",
.ELIBACC = "Can not access a needed shared library",
.ELIBBAD = "Accessing a corrupted shared library",
.ELIBSCN = ".lib section in a.out corrupted",
.ELIBMAX = "Attempting to link in too many shared libraries",
.ELIBEXEC = "Cannot exec a shared library directly",
.EILSEQ = "Invalid or incomplete multibyte or wide character",
.ERESTART = "Interrupted system call should be restarted",
.ESTRPIPE = "Streams pipe error",
.EUSERS = "Too many users",
.ENOTSOCK = "Socket operation on non-socket",
.EDESTADDRREQ = "Destination address required",
.EMSGSIZE = "Message too long",
.EPROTOTYPE = "Protocol wrong type for socket",
.ENOPROTOOPT = "Protocol not available",
.EPROTONOSUPPORT = "Protocol not supported",
.ESOCKTNOSUPPORT = "Socket type not supported",
.EOPNOTSUPP = "Operation not supported",
.EPFNOSUPPORT = "Protocol family not supported",
.EAFNOSUPPORT = "Address family not supported by protocol",
.EADDRINUSE = "Address already in use",
.EADDRNOTAVAIL = "Cannot assign requested address",
.ENETDOWN = "Network is down",
.ENETUNREACH = "Network is unreachable",
.ENETRESET = "Network dropped connection on reset",
.ECONNABORTED = "Software caused connection abort",
.ECONNRESET = "Connection reset by peer",
.ENOBUFS = "No buffer space available",
.EISCONN = "Transport endpoint is already connected",
.ENOTCONN = "Transport endpoint is not connected",
.ESHUTDOWN = "Cannot send after transport endpoint shutdown",
.ETOOMANYREFS = "Too many references: cannot splice",
.ETIMEDOUT = "Connection timed out",
.ECONNREFUSED = "Connection refused",
.EHOSTDOWN = "Host is down",
.EHOSTUNREACH = "No route to host",
.EALREADY = "Operation already in progress",
.EINPROGRESS = "Operation now in progress",
.ESTALE = "Stale file handle",
.EUCLEAN = "Structure needs cleaning",
.ENOTNAM = "Not a XENIX named type file",
.ENAVAIL = "No XENIX semaphores available",
.EISNAM = "Is a named type file",
.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))
}
_ok_or_error :: proc(res: int) -> Error {
return res >= 0 ? nil : _get_platform_error(res)
}
_error_string :: proc(errno: i32) -> string {
if errno == 0 {
return ""
if errno >= 0 && errno <= i32(max(linux.Errno)) {
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_CLOEXEC :: File_Flags{.Close_On_Exec}
stdin: ^File = nil // OS-Specific
stdout: ^File = nil // OS-Specific
stderr: ^File = nil // OS-Specific
@(require_results)
create :: proc(name: string) -> (^File, Error) {
return open(name, {.Read, .Write, .Create}, File_Mode(0o777))
+231 -161
View File
@@ -1,39 +1,64 @@
//+private
package os2
import "base:runtime"
import "core:io"
import "core:time"
import "core:sys/unix"
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
import "base:runtime"
import "core:sys/linux"
_File :: struct {
name: string,
fd: int,
fd: linux.Fd,
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) {
TEMP_ALLOCATOR_GUARD()
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
// terminal would be incredibly rare. This has no effect on files while
// 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 {
case O_RDONLY: flags_i = _O_RDONLY
case O_WRONLY: flags_i = _O_WRONLY
case O_RDWR: flags_i = _O_RDWR
case O_RDONLY:
case O_WRONLY: sys_flags += {.WRONLY}
case O_RDWR: sys_flags += {.RDWR}
}
if .Append in flags { flags_i |= _O_APPEND }
if .Create in flags { flags_i |= _O_CREAT }
if .Excl in flags { flags_i |= _O_EXCL }
if .Sync in flags { flags_i |= _O_SYNC }
if .Trunc in flags { flags_i |= _O_TRUNC }
if .Close_On_Exec in flags { flags_i |= _O_CLOEXEC }
if .Append in flags { sys_flags += {.APPEND} }
if .Create in flags { sys_flags += {.CREAT} }
if .Excl in flags { sys_flags += {.EXCL} }
if .Sync in flags { sys_flags += {.DSYNC} }
if .Trunc in flags { sys_flags += {.TRUNC} }
if .Close_On_Exec in flags { sys_flags += {.CLOEXEC} }
fd := unix.sys_open(name_cstr, flags_i, uint(perm))
if fd < 0 {
return nil, _get_platform_error(fd)
fd, errno := linux.open(name_cstr, sys_flags, transmute(linux.Mode)(u32(perm)))
if errno != .NONE {
return nil, _get_platform_error(errno)
}
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.impl.fd = int(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,
}
_construct_file(file, fd, "")
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 {
if f == nil {
return nil
@@ -86,12 +119,15 @@ _destroy :: proc(f: ^File) -> Error {
_close :: proc(f: ^File) -> Error {
if f != nil {
res := unix.sys_close(f.impl.fd)
_destroy(f)
return _ok_or_error(res)
if f == nil {
return nil
}
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 {
@@ -106,20 +142,32 @@ _name :: proc(f: ^File) -> string {
}
_seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) {
res := unix.sys_lseek(f.impl.fd, offset, int(whence))
if res < 0 {
return -1, _get_platform_error(int(res))
n, errno := linux.lseek(f.impl.fd, offset, linux.Seek_Whence(whence))
if errno != .NONE {
return -1, _get_platform_error(errno)
}
return res, nil
return n, nil
}
_read :: proc(f: ^File, p: []byte) -> (i64, Error) {
if len(p) == 0 {
return 0, nil
}
n := unix.sys_read(f.impl.fd, &p[0], len(p))
if n < 0 {
return -1, _get_platform_error(n)
n, errno := linux.read(f.impl.fd, p[:])
if errno != .NONE {
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 {
return 0, .EOF
@@ -127,91 +175,67 @@ _read :: proc(f: ^File, p: []byte) -> (i64, Error) {
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) {
if len(p) == 0 {
return 0, nil
}
n := unix.sys_write(f.impl.fd, &p[0], uint(len(p)))
if n < 0 {
return -1, _get_platform_error(n)
n, errno := linux.write(f.impl.fd, p[:])
if errno != .NONE {
return -1, _get_platform_error(errno)
}
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 {
return 0, .Invalid_Offset
}
b, offset := p, offset
for len(b) > 0 {
m := unix.sys_pwrite(f.impl.fd, &b[0], len(b), offset)
if m < 0 {
return -1, _get_platform_error(m)
}
n += i64(m)
b = b[m:]
offset += i64(m)
n, errno := linux.pwrite(f.impl.fd, p[:], offset)
if errno != .NONE {
return -1, _get_platform_error(errno)
}
return
return i64(n), nil
}
_file_size :: proc(f: ^File) -> (n: i64, err: Error) {
s: _Stat = ---
res := unix.sys_fstat(f.impl.fd, &s)
if res < 0 {
return -1, _get_platform_error(res)
s: linux.Stat = ---
errno := linux.fstat(f.impl.fd, &s)
if errno != .NONE {
return -1, _get_platform_error(errno)
}
return s.size, nil
return i64(s.size), nil
}
_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 {
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 {
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 {
TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return
fd := unix.sys_open(name_cstr, int(File_Flags.Read))
if fd < 0 {
return _get_platform_error(fd)
fd, errno := linux.open(name_cstr, {.NOFOLLOW})
#partial switch (errno) {
case .ELOOP: /* symlink */
case .NONE:
defer linux.close(fd)
if _is_dir_fd(fd) {
return _get_platform_error(linux.rmdir(name_cstr))
}
case:
return _get_platform_error(errno)
}
defer unix.sys_close(fd)
if _is_dir_fd(fd) {
return _ok_or_error(unix.sys_rmdir(name_cstr))
}
return _ok_or_error(unix.sys_unlink(name_cstr))
return _get_platform_error(linux.unlink(name_cstr))
}
_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
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 {
@@ -227,148 +251,194 @@ _link :: proc(old_name, new_name: string) -> Error {
old_name_cstr := temp_cstring(old_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 {
TEMP_ALLOCATOR_GUARD()
old_name_cstr := temp_cstring(old_name) or_return
new_name_cstr := temp_cstring(new_name) or_return
return _ok_or_error(unix.sys_symlink(old_name_cstr, new_name_cstr))
return _get_platform_error(linux.symlink(old_name_cstr, new_name_cstr))
}
_read_link_cstr :: proc(name_cstr: cstring, allocator: runtime.Allocator) -> (string, Error) {
bufsz : uint = 256
buf := make([]byte, bufsz, allocator)
for {
rc := unix.sys_readlink(name_cstr, &buf[0], bufsz)
if rc < 0 {
delete(buf)
return "", _get_platform_error(rc)
} else if rc == int(bufsz) {
sz, errno := linux.readlink(name_cstr, buf[:])
if errno != .NONE {
delete(buf, allocator)
return "", _get_platform_error(errno)
} else if sz == int(bufsz) {
bufsz *= 2
delete(buf)
delete(buf, allocator)
buf = make([]byte, bufsz, allocator)
} 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()
name_cstr := temp_cstring(name) or_return
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 {
TEMP_ALLOCATOR_GUARD()
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 {
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 {
TEMP_ALLOCATOR_GUARD()
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 {
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
_chown :: proc(name: string, uid, gid: int) -> Error {
TEMP_ALLOCATOR_GUARD()
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
_lchown :: proc(name: string, uid, gid: int) -> Error {
TEMP_ALLOCATOR_GUARD()
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
_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 {
TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return
times := [2]Unix_File_Time {
{ atime._nsec, 0 },
{ mtime._nsec, 0 },
times := [2]linux.Time_Spec {
{
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 {
times := [2]Unix_File_Time {
{ atime._nsec, 0 },
{ mtime._nsec, 0 },
times := [2]linux.Time_Spec {
{
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 {
TEMP_ALLOCATOR_GUARD()
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 {
TEMP_ALLOCATOR_GUARD()
name_cstr, _ := temp_cstring(name)
s: _Stat
res := unix.sys_stat(name_cstr, &s)
if res < 0 {
s: linux.Stat
if linux.stat(name_cstr, &s) != .NONE {
return false
}
return S_ISREG(s.mode)
return linux.S_ISREG(s.mode)
}
_is_file_fd :: proc(fd: int) -> bool {
s: _Stat
res := unix.sys_fstat(fd, &s)
if res < 0 { // error
_is_file_fd :: proc(fd: linux.Fd) -> bool {
s: linux.Stat
if linux.fstat(fd, &s) != .NONE {
return false
}
return S_ISREG(s.mode)
return linux.S_ISREG(s.mode)
}
_is_dir :: proc(name: string) -> bool {
TEMP_ALLOCATOR_GUARD()
name_cstr, _ := temp_cstring(name)
s: _Stat
res := unix.sys_stat(name_cstr, &s)
if res < 0 {
s: linux.Stat
if linux.stat(name_cstr, &s) != .NONE {
return false
}
return S_ISDIR(s.mode)
return linux.S_ISDIR(s.mode)
}
_is_dir_fd :: proc(fd: int) -> bool {
s: _Stat
res := unix.sys_fstat(fd, &s)
if res < 0 { // error
_is_dir_fd :: proc(fd: linux.Fd) -> bool {
s: linux.Stat
if linux.fstat(fd, &s) != .NONE {
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")
+11 -17
View File
@@ -1,7 +1,7 @@
//+private
package os2
import "core:sys/unix"
import "core:sys/linux"
import "core:sync"
import "core:mem"
@@ -97,9 +97,8 @@ CURRENTLY_ACTIVE :: (^^Region)(~uintptr(0))
FREE_LIST_ENTRIES_PER_BLOCK :: BLOCK_SIZE / size_of(u16)
MMAP_FLAGS :: unix.MAP_ANONYMOUS | unix.MAP_PRIVATE
MMAP_PROT :: unix.PROT_READ | unix.PROT_WRITE
MMAP_FLAGS : linux.Map_Flags : {.ANONYMOUS, .PRIVATE}
MMAP_PROT : linux.Mem_Protection : {.READ, .WRITE}
@thread_local _local_region: ^Region
global_regions: ^Region
@@ -324,11 +323,11 @@ heap_free :: proc(memory: rawptr) {
// Regions
//
_new_region :: proc() -> ^Region #no_bounds_check {
res := unix.sys_mmap(nil, uint(SIZE_OF_REGION), MMAP_PROT, MMAP_FLAGS, -1, 0)
if res < 0 {
ptr, errno := linux.mmap(0, uint(SIZE_OF_REGION), MMAP_PROT, MMAP_FLAGS, -1, 0)
if errno != .NONE {
return nil
}
new_region := (^Region)(uintptr(res))
new_region := (^Region)(ptr)
new_region.hdr.local_addr = CURRENTLY_ACTIVE
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 {
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)
if new_allocation < 0 && new_allocation > -4096 {
new_allocation, errno := linux.mmap(0, uint(mmap_size), MMAP_PROT, MMAP_FLAGS, -1, 0)
if errno != .NONE {
return nil
}
@@ -655,13 +654,8 @@ _direct_mmap_resize :: proc(alloc: ^Allocation_Header, new_size: int) -> rawptr
return mem.ptr_offset(alloc, 1)
}
new_allocation := unix.sys_mremap(
alloc,
uint(old_mmap_size),
uint(new_mmap_size),
unix.MREMAP_MAYMOVE,
)
if new_allocation < 0 && new_allocation > -4096 {
new_allocation, errno := linux.mremap(alloc, uint(old_mmap_size), uint(new_mmap_size), {.MAYMOVE})
if errno != .NONE {
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) {
requested := int(alloc.requested & REQUESTED_MASK)
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 "base:runtime"
import "core:sys/unix"
import "core:sys/linux"
_Path_Separator :: '/'
_Path_Separator_String :: "/"
_Path_List_Separator :: ':'
_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
_OPENDIR_FLAGS :: _O_RDONLY|_O_NONBLOCK|_O_DIRECTORY|_O_LARGEFILE|_O_CLOEXEC
_OPENDIR_FLAGS : linux.Open_Flags : {.NONBLOCK, .DIRECTORY, .LARGEFILE, .CLOEXEC}
_is_path_separator :: proc(c: byte) -> bool {
return c == '/'
}
_mkdir :: proc(path: string, perm: File_Mode) -> Error {
// NOTE: These modes would require sys_mknod, however, that would require
// additional arguments to this function.
// TODO: These modes would require mknod, however, that would also
// require additional arguments to this function..
if perm & (File_Mode_Named_Pipe | File_Mode_Device | File_Mode_Char_Device | File_Mode_Sym_Link) != 0 {
return .Invalid_Argument
}
TEMP_ALLOCATOR_GUARD()
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 {
_mkdirat :: proc(dfd: int, path: []u8, perm: int, has_created: ^bool) -> Error {
if len(path) == 0 {
return _ok_or_error(unix.sys_close(dfd))
}
mkdirat :: proc(dfd: linux.Fd, path: []u8, perm: int, has_created: ^bool) -> Error {
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
new_dfd := unix.sys_openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS)
switch new_dfd {
case -ENOENT:
if res := unix.sys_mkdirat(dfd, cstring(&path[0]), uint(perm)); res < 0 {
return _get_platform_error(res)
new_dfd, errno := linux.openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS)
#partial switch errno {
case .ENOENT:
if errno = linux.mkdirat(dfd, cstring(&path[0]), transmute(linux.Mode)(u32(perm))); errno != .NONE {
return _get_platform_error(errno)
}
has_created^ = true
if new_dfd = unix.sys_openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS); new_dfd < 0 {
return _get_platform_error(new_dfd)
if new_dfd, errno = linux.openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS); errno != .NONE {
return _get_platform_error(errno)
}
fallthrough
case 0:
if res := unix.sys_close(dfd); res < 0 {
return _get_platform_error(res)
case .NONE:
if errno = linux.close(dfd); errno != .NONE {
return _get_platform_error(errno)
}
// skip consecutive '/'
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:
return _get_platform_error(new_dfd)
return _get_platform_error(errno)
}
unreachable()
}
// TODO
if perm & (File_Mode_Named_Pipe | File_Mode_Device | File_Mode_Char_Device | File_Mode_Sym_Link) != 0 {
return .Invalid_Argument
}
TEMP_ALLOCATOR_GUARD()
// need something we can edit, and use to generate cstrings
allocated: bool
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())
}
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)
path_bytes[len(path)] = 0
dfd: int
dfd: linux.Fd
errno: linux.Errno
if path_bytes[0] == '/' {
dfd = unix.sys_open("/", _OPENDIR_FLAGS)
dfd, errno = linux.open("/", _OPENDIR_FLAGS)
path_bytes = path_bytes[1:]
} else {
dfd = unix.sys_open(".", _OPENDIR_FLAGS)
dfd, errno = linux.open(".", _OPENDIR_FLAGS)
}
if dfd < 0 {
return _get_platform_error(dfd)
if errno != .NONE {
return _get_platform_error(errno)
}
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 {
return nil
}
@@ -119,28 +104,28 @@ dirent64 :: struct {
_remove_all :: proc(path: string) -> Error {
DT_DIR :: 4
_remove_all_dir :: proc(dfd: int) -> Error {
remove_all_dir :: proc(dfd: linux.Fd) -> Error {
n := 64
buf := make([]u8, n)
defer delete(buf)
loop: for {
getdents_res := unix.sys_getdents64(dfd, &buf[0], n)
switch getdents_res {
case -EINVAL:
buflen, errno := linux.getdents(dfd, buf[:])
#partial switch errno {
case .EINVAL:
delete(buf)
n *= 2
buf = make([]u8, n)
continue loop
case -4096..<0:
return _get_platform_error(getdents_res)
case 0:
break loop
case .NONE:
if buflen == 0 { break loop }
case:
return _get_platform_error(errno)
}
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_name_cstr := cstring(&d.d_name[0])
@@ -156,23 +141,22 @@ _remove_all :: proc(path: string) -> Error {
continue
}
unlink_res: int
switch d.d_type {
case DT_DIR:
new_dfd := unix.sys_openat(dfd, d_name_cstr, _OPENDIR_FLAGS)
if new_dfd < 0 {
return _get_platform_error(new_dfd)
new_dfd: linux.Fd
new_dfd, errno = linux.openat(dfd, d_name_cstr, _OPENDIR_FLAGS)
if errno != .NONE {
return _get_platform_error(errno)
}
defer unix.sys_close(new_dfd)
_remove_all_dir(new_dfd) or_return
unlink_res = unix.sys_unlinkat(dfd, d_name_cstr, int(unix.AT_REMOVEDIR))
defer linux.close(new_dfd)
remove_all_dir(new_dfd) or_return
errno = linux.unlinkat(dfd, d_name_cstr, {.REMOVEDIR})
case:
unlink_res = unix.sys_unlinkat(dfd, d_name_cstr)
errno = linux.unlinkat(dfd, d_name_cstr, nil)
}
if unlink_res < 0 {
return _get_platform_error(unlink_res)
if errno != .NONE {
return _get_platform_error(errno)
}
}
}
@@ -182,17 +166,19 @@ _remove_all :: proc(path: string) -> Error {
TEMP_ALLOCATOR_GUARD()
path_cstr := temp_cstring(path) or_return
fd := unix.sys_open(path_cstr, _OPENDIR_FLAGS)
switch fd {
case -ENOTDIR:
return _ok_or_error(unix.sys_unlink(path_cstr))
case -4096..<0:
return _get_platform_error(fd)
fd, errno := linux.open(path_cstr, _OPENDIR_FLAGS)
#partial switch errno {
case .NONE:
break
case .ENOTDIR:
return _get_platform_error(linux.unlink(path_cstr))
case:
return _get_platform_error(errno)
}
defer unix.sys_close(fd)
_remove_all_dir(fd) or_return
return _ok_or_error(unix.sys_rmdir(path_cstr))
defer linux.close(fd)
remove_all_dir(fd) or_return
return _get_platform_error(linux.rmdir(path_cstr))
}
_getwd :: proc(allocator: runtime.Allocator) -> (string, Error) {
@@ -203,13 +189,12 @@ _getwd :: proc(allocator: runtime.Allocator) -> (string, Error) {
PATH_MAX :: 4096
buf := make([dynamic]u8, PATH_MAX, allocator)
for {
#no_bounds_check res := unix.sys_getcwd(&buf[0], uint(len(buf)))
if res >= 0 {
return string_from_null_terminated_bytes(buf[:]), nil
#no_bounds_check n, errno := linux.getcwd(buf[:])
if errno == .NONE {
return string(buf[:n-1]), nil
}
if res != -ERANGE {
return "", _get_platform_error(res)
if errno != .ERANGE {
return "", _get_platform_error(errno)
}
resize(&buf, len(buf)+PATH_MAX)
}
@@ -218,16 +203,16 @@ _getwd :: proc(allocator: runtime.Allocator) -> (string, Error) {
_setwd :: proc(dir: string) -> Error {
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/"
buf: [32]u8
copy(buf[:], PROC_FD_PATH)
strconv.itoa(buf[len(PROC_FD_PATH):], fd)
strconv.itoa(buf[len(PROC_FD_PATH):], int(fd))
fullpath: string
err: Error
@@ -236,4 +221,3 @@ _get_full_path :: proc(fd: int, allocator: runtime.Allocator) -> string {
}
return fullpath
}
+11 -1
View File
@@ -1,7 +1,17 @@
//+private
package os2
import "core:sys/linux"
_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 "base:runtime"
import "core:sys/unix"
import "core:sys/linux"
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) {
return _fstat_internal(f.impl.fd, allocator)
}
_fstat_internal :: proc(fd: int, allocator: runtime.Allocator) -> (File_Info, Error) {
s: _Stat
result := unix.sys_fstat(fd, &s)
if result < 0 {
return {}, _get_platform_error(result)
_fstat_internal :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> (File_Info, Error) {
s: linux.Stat
errno := linux.fstat(fd, &s)
if errno != .NONE {
return {}, _get_platform_error(errno)
}
// TODO: As of Linux 4.11, the new statx syscall can retrieve creation_time
fi := File_Info {
fullpath = _get_full_path(fd, allocator),
name = "",
size = s.size,
size = i64(s.size),
mode = 0,
is_directory = S_ISDIR(s.mode),
modification_time = time.Time {s.modified.seconds},
access_time = time.Time {s.last_access.seconds},
creation_time = time.Time{0}, // regular stat does not provide this
is_directory = linux.S_ISDIR(s.mode),
modification_time = time.Time {i64(s.mtime.time_sec) * i64(time.Second) + i64(s.mtime.time_nsec)},
access_time = time.Time {i64(s.atime.time_sec) * i64(time.Second) + i64(s.atime.time_nsec)},
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)
return fi, nil
@@ -115,11 +39,11 @@ _stat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err
TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return
fd := unix.sys_open(name_cstr, _O_RDONLY)
if fd < 0 {
return {}, _get_platform_error(fd)
fd, errno := linux.open(name_cstr, {})
if errno != .NONE {
return {}, _get_platform_error(errno)
}
defer unix.sys_close(fd)
defer linux.close(fd)
return _fstat_internal(fd, allocator)
}
@@ -127,11 +51,11 @@ _lstat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, er
TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return
fd := unix.sys_open(name_cstr, _O_RDONLY | _O_PATH | _O_NOFOLLOW)
if fd < 0 {
return {}, _get_platform_error(fd)
fd, errno := linux.open(name_cstr, {.PATH, .NOFOLLOW})
if errno != .NONE {
return {}, _get_platform_error(errno)
}
defer unix.sys_close(fd)
defer linux.close(fd)
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)
if err != 0 {
_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)
path_cstr := transmute(cstring)path_ptr
path = strings.clone( string(path_cstr) )
path_cstr := cast(cstring)path_ptr
path = strings.clone(string(path_cstr))
return path, ERROR_NONE
}
@@ -896,7 +896,7 @@ access :: proc(path: string, mask: int) -> bool {
}
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) {
+8 -4
View File
@@ -648,8 +648,8 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
}
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
}
@@ -705,7 +705,9 @@ set_current_directory :: proc(path: string) -> (err: Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_chdir(cstr)
if res == -1 do return Errno(get_last_error())
if res == -1 {
return Errno(get_last_error())
}
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
// if we don't have to.
@static page_size := -1
if page_size != -1 do return page_size
if page_size != -1 {
return page_size
}
page_size = int(_unix_getpagesize())
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)
path_cstr := transmute(cstring)path_ptr
path = strings.clone( string(path_cstr) )
path = strings.clone(string(cstring(path_ptr)))
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)
path_cstr := transmute(cstring)path_ptr
path = strings.clone( string(path_cstr) )
path = strings.clone(string(cstring(path_ptr)))
return path, ERROR_NONE
}
@@ -715,7 +714,9 @@ set_current_directory :: proc(path: string) -> (err: Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_chdir(cstr)
if res == -1 do return Errno(get_last_error())
if res == -1 {
return Errno(get_last_error())
}
return ERROR_NONE
}
@@ -756,7 +757,9 @@ get_page_size :: proc() -> int {
// NOTE(tetra): The page size never changes, so why do anything complicated
// if we don't have to.
@static page_size := -1
if page_size != -1 do return page_size
if page_size != -1 {
return page_size
}
page_size = int(_unix_getpagesize())
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)
path_cstr := transmute(cstring)path_ptr
path = strings.clone( string(path_cstr) )
path = strings.clone(string(cstring(path_ptr)))
return path, ERROR_NONE
}
+12
View File
@@ -6,6 +6,8 @@ import "base:runtime"
Handle :: distinct i32
Errno :: distinct i32
INVALID_HANDLE :: -1
ERROR_NONE :: Errno(wasi.errno_t.SUCCESS)
O_RDONLY :: 0x00000
@@ -26,6 +28,16 @@ stdout: Handle = 1
stderr: Handle = 2
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) {
iovs := wasi.ciovec_t(data)
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 {
osvi : win32.OSVERSIONINFOEXW
osvi.dwOSVersionInfoSize = size_of(win32.OSVERSIONINFOEXW)
win32.RtlGetVersion(&osvi)
return osvi
win32.RtlGetVersion(&osvi)
return osvi
}
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.
*/
dir :: proc(path: string, allocator := context.allocator) -> string {
context.allocator = allocator
context.allocator = allocator
vol := volume_name(path)
i := len(path) - 1
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:
y := b.variant.(Type_Info_Struct) or_return
switch {
switch {
case len(x.types) != len(y.types),
x.is_packed != y.is_packed,
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_base_type != y.soa_base_type,
x.soa_len != y.soa_len:
return false
return false
}
for _, i in x.types {
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)
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))
}
@@ -712,7 +711,7 @@ enumerated_array :: proc(ptr: ^$T) -> []intrinsics.type_elem_type(T)
@(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 {
for v in enums {
bits |= {v}
bits += {v}
}
return
}
+8 -8
View File
@@ -24,7 +24,7 @@ Builder :: struct {
buf: [dynamic]byte,
}
/*
Produces a Builder with a default length of 0 and cap of 16
Produces an empty Builder
*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
}
/*
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*
@@ -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
}
/*
Produces a Builder with a specified length and cap
Produces a Builder with specified length `len` and capacity `cap`.
*Allocates Using Provided Allocator*
@@ -103,7 +103,7 @@ builder_make :: proc{
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`
*Allocates Using Provided Allocator*
@@ -121,7 +121,7 @@ builder_init_none :: proc(b: ^Builder, allocator := context.allocator, loc := #c
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`
*Allocates Using Provided Allocator*
@@ -140,7 +140,7 @@ builder_init_len :: proc(b: ^Builder, len: int, allocator := context.allocator,
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`
Inputs:
@@ -723,11 +723,11 @@ write_f32 :: proc(b: ^Builder, f: f32, fmt: byte, always_signed := false) -> (n:
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:
- b: A pointer to the Builder
- f: The f32 value to be appended
- f: The f64 value to be appended
- fmt: The format byte
- 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) {
m.allocator = allocator
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`.
+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) ||
sync.atomic_load(&c.w_waiting) == 0 {
return false
return false
}
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) &&
(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))
buf, err := make([]byte, max, allocator)
if err != nil do return
if err != nil { return }
raw_str := runtime.Raw_String {
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