Merge remote-tracking branch 'offical/master'

# Conflicts:
#	core/os/os_darwin.odin
#	core/os/os_freebsd.odin
#	core/os/os_js.odin
#	core/os/os_linux.odin
#	core/os/os_openbsd.odin
#	core/os/os_windows.odin
This commit is contained in:
ed
2024-08-04 18:49:08 -04:00
354 changed files with 22202 additions and 4803 deletions
+7 -8
View File
@@ -6,7 +6,7 @@ jobs:
name: NetBSD Build, Check, and Test name: NetBSD Build, Check, and Test
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: env:
PKGSRC_BRANCH: 2024Q1 PKGSRC_BRANCH: 2024Q2
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Build, Check, and Test - name: Build, Check, and Test
@@ -18,13 +18,11 @@ jobs:
usesh: true usesh: true
copyback: false copyback: false
prepare: | prepare: |
PKG_PATH="https://cdn.NetBSD.org/pub/pkgsrc/packages/NetBSD/$(uname -p)/$(uname -r | cut -d_ -f1)_${PKGSRC_BRANCH}/All" /usr/sbin/pkg_add pkgin PKG_PATH="https://cdn.NetBSD.org/pub/pkgsrc/packages/NetBSD/$(uname -p)/10.0_2024Q2/All" /usr/sbin/pkg_add pkgin
pkgin -y in gmake git bash python311 pkgin -y in gmake git bash python311 llvm clang
pkgin -y in libxml2 perl zstd
/usr/sbin/pkg_add https://github.com/andreas-jonsson/llvm17-netbsd-bin/releases/download/pkgsrc-current/llvm-17.0.6.tgz
/usr/sbin/pkg_add https://github.com/andreas-jonsson/llvm17-netbsd-bin/releases/download/pkgsrc-current/clang-17.0.6.tgz
ln -s /usr/pkg/bin/python3.11 /usr/bin/python3 ln -s /usr/pkg/bin/python3.11 /usr/bin/python3
run: | run: |
set -e -x
git config --global --add safe.directory $(pwd) git config --global --add safe.directory $(pwd)
gmake release gmake release
./odin version ./odin version
@@ -91,13 +89,13 @@ jobs:
- name: Download LLVM (MacOS Intel) - name: Download LLVM (MacOS Intel)
if: matrix.os == 'macos-13' if: matrix.os == 'macos-13'
run: | run: |
brew install llvm@17 brew install llvm@17 lua@5.4
echo "/usr/local/opt/llvm@17/bin" >> $GITHUB_PATH echo "/usr/local/opt/llvm@17/bin" >> $GITHUB_PATH
- name: Download LLVM (MacOS ARM) - name: Download LLVM (MacOS ARM)
if: matrix.os == 'macos-14' if: matrix.os == 'macos-14'
run: | run: |
brew install llvm@17 wasmtime brew install llvm@17 wasmtime lua@5.4
echo "/opt/homebrew/opt/llvm@17/bin" >> $GITHUB_PATH echo "/opt/homebrew/opt/llvm@17/bin" >> $GITHUB_PATH
- name: Build Odin - name: Build Odin
@@ -207,6 +205,7 @@ jobs:
shell: cmd shell: cmd
run: | run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
copy vendor\lua\5.4\windows\*.dll .
odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false
- name: Odin internals tests - name: Odin internals tests
shell: cmd shell: cmd
+6 -6
View File
@@ -50,8 +50,8 @@ jobs:
run: | run: |
wget https://apt.llvm.org/llvm.sh wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh chmod +x llvm.sh
sudo ./llvm.sh 17 sudo ./llvm.sh 18
echo "/usr/lib/llvm-17/bin" >> $GITHUB_PATH echo "/usr/lib/llvm-18/bin" >> $GITHUB_PATH
- name: build odin - name: build odin
run: make nightly run: make nightly
- name: Odin run - name: Odin run
@@ -82,8 +82,8 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Download LLVM and setup PATH - name: Download LLVM and setup PATH
run: | run: |
brew install llvm@17 dylibbundler brew install llvm@18 dylibbundler
echo "/usr/local/opt/llvm@17/bin" >> $GITHUB_PATH echo "/usr/local/opt/llvm@18/bin" >> $GITHUB_PATH
- name: build odin - name: build odin
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to # These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
# not link with libunwind bundled with LLVM but link with libunwind on the system. # not link with libunwind bundled with LLVM but link with libunwind on the system.
@@ -116,8 +116,8 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Download LLVM and setup PATH - name: Download LLVM and setup PATH
run: | run: |
brew install llvm@17 dylibbundler brew install llvm@18 dylibbundler
echo "/opt/homebrew/opt/llvm@17/bin" >> $GITHUB_PATH echo "/opt/homebrew/opt/llvm@18/bin" >> $GITHUB_PATH
- name: build odin - name: build odin
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to # These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
# not link with libunwind bundled with LLVM but link with libunwind on the system. # not link with libunwind bundled with LLVM but link with libunwind on the system.
+1 -32
View File
@@ -24,38 +24,6 @@ bld/
![Cc]ore/[Ll]og/ ![Cc]ore/[Ll]og/
tests/documentation/verify/ tests/documentation/verify/
tests/documentation/all.odin-doc tests/documentation/all.odin-doc
tests/internal/test_map
tests/internal/test_pow
tests/internal/test_rtti
tests/core/test_base64
tests/core/test_cbor
tests/core/test_core_compress
tests/core/test_core_container
tests/core/test_core_filepath
tests/core/test_core_fmt
tests/core/test_core_i18n
tests/core/test_core_image
tests/core/test_core_libc
tests/core/test_core_match
tests/core/test_core_math
tests/core/test_core_net
tests/core/test_core_os_exit
tests/core/test_core_reflect
tests/core/test_core_strings
tests/core/test_core_time
tests/core/test_crypto
tests/core/test_hash
tests/core/test_hex
tests/core/test_hxa
tests/core/test_json
tests/core/test_linalg_glsl_math
tests/core/test_noise
tests/core/test_varint
tests/core/test_xml
tests/core/test_core_slice
tests/core/test_core_thread
tests/core/test_core_runtime
tests/vendor/vendor_botan
# Visual Studio 2015 cache/options directory # Visual Studio 2015 cache/options directory
.vs/ .vs/
# Visual Studio Code options directory # Visual Studio Code options directory
@@ -63,6 +31,7 @@ tests/vendor/vendor_botan
# Uncomment if you have tasks that create the project's static files in wwwroot # Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/ #wwwroot/
demo demo
benchmark
# MSTest test Results # MSTest test Results
[Tt]est[Rr]esult*/ [Tt]est[Rr]esult*/
BIN
View File
Binary file not shown.
+6 -3
View File
@@ -38,9 +38,12 @@ count_leading_zeros :: proc(x: $T) -> T where type_is_integer(T) || type_is_sim
reverse_bits :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) --- reverse_bits :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
byte_swap :: proc(x: $T) -> T where type_is_integer(T) || type_is_float(T) --- byte_swap :: proc(x: $T) -> T where type_is_integer(T) || type_is_float(T) ---
overflow_add :: proc(lhs, rhs: $T) -> (T, bool) --- overflow_add :: proc(lhs, rhs: $T) -> (T, bool) where type_is_integer(T) #optional_ok ---
overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) --- overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) where type_is_integer(T) #optional_ok ---
overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) --- overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) where type_is_integer(T) #optional_ok ---
add_sat :: proc(lhs, rhs: $T) -> T where type_is_integer(T) ---
sub_sat :: proc(lhs, rhs: $T) -> T where type_is_integer(T) ---
sqrt :: proc(x: $T) -> T where type_is_float(T) || (type_is_simd_vector(T) && type_is_float(type_elem_type(T))) --- sqrt :: proc(x: $T) -> T where type_is_float(T) || (type_is_simd_vector(T) && type_is_float(type_elem_type(T))) ---
+36 -23
View File
@@ -66,7 +66,7 @@ Type_Info_Named :: struct {
name: string, name: string,
base: ^Type_Info, base: ^Type_Info,
pkg: string, pkg: string,
loc: Source_Code_Location, loc: ^Source_Code_Location,
} }
Type_Info_Integer :: struct {signed: bool, endianness: Platform_Endianness} Type_Info_Integer :: struct {signed: bool, endianness: Platform_Endianness}
Type_Info_Rune :: struct {} Type_Info_Rune :: struct {}
@@ -112,23 +112,32 @@ Type_Info_Parameters :: struct { // Only used for procedures parameters and resu
} }
Type_Info_Tuple :: Type_Info_Parameters // Will be removed eventually Type_Info_Tuple :: Type_Info_Parameters // Will be removed eventually
Type_Info_Struct :: struct { Type_Info_Struct_Flags :: distinct bit_set[Type_Info_Struct_Flag; u8]
types: []^Type_Info, Type_Info_Struct_Flag :: enum u8 {
names: []string, packed = 0,
offsets: []uintptr, raw_union = 1,
usings: []bool, no_copy = 2,
tags: []string, align = 3,
is_packed: bool, }
is_raw_union: bool,
is_no_copy: bool,
custom_align: bool,
equal: Equal_Proc, // set only when the struct has .Comparable set but does not have .Simple_Compare set Type_Info_Struct :: struct {
// Slice these with `field_count`
types: [^]^Type_Info `fmt:"v,field_count"`,
names: [^]string `fmt:"v,field_count"`,
offsets: [^]uintptr `fmt:"v,field_count"`,
usings: [^]bool `fmt:"v,field_count"`,
tags: [^]string `fmt:"v,field_count"`,
field_count: i32,
flags: Type_Info_Struct_Flags,
// These are only set iff this structure is an SOA structure // These are only set iff this structure is an SOA structure
soa_kind: Type_Info_Struct_Soa_Kind, soa_kind: Type_Info_Struct_Soa_Kind,
soa_len: i32,
soa_base_type: ^Type_Info, soa_base_type: ^Type_Info,
soa_len: int,
equal: Equal_Proc, // set only when the struct has .Comparable set but does not have .Simple_Compare set
} }
Type_Info_Union :: struct { Type_Info_Union :: struct {
variants: []^Type_Info, variants: []^Type_Info,
@@ -142,9 +151,9 @@ Type_Info_Union :: struct {
shared_nil: bool, shared_nil: bool,
} }
Type_Info_Enum :: struct { Type_Info_Enum :: struct {
base: ^Type_Info, base: ^Type_Info,
names: []string, names: []string,
values: []Type_Info_Enum_Value, values: []Type_Info_Enum_Value,
} }
Type_Info_Map :: struct { Type_Info_Map :: struct {
key: ^Type_Info, key: ^Type_Info,
@@ -187,11 +196,12 @@ Type_Info_Soa_Pointer :: struct {
} }
Type_Info_Bit_Field :: struct { Type_Info_Bit_Field :: struct {
backing_type: ^Type_Info, backing_type: ^Type_Info,
names: []string, names: [^]string `fmt:"v,field_count"`,
types: []^Type_Info, types: [^]^Type_Info `fmt:"v,field_count"`,
bit_sizes: []uintptr, bit_sizes: [^]uintptr `fmt:"v,field_count"`,
bit_offsets: []uintptr, bit_offsets: [^]uintptr `fmt:"v,field_count"`,
tags: []string, tags: [^]string `fmt:"v,field_count"`,
field_count: int,
} }
Type_Info_Flag :: enum u8 { Type_Info_Flag :: enum u8 {
@@ -299,6 +309,8 @@ when ODIN_OS == .Windows {
Thread_Detach = 3, Thread_Detach = 3,
} }
dll_forward_reason: DLL_Forward_Reason dll_forward_reason: DLL_Forward_Reason
dll_instance: rawptr
} }
// IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it) // IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it)
@@ -513,11 +525,12 @@ Raw_Quaternion256_Vector_Scalar :: struct {vector: [3]f64, scalar: f64}
Linux, Linux,
Essence, Essence,
FreeBSD, FreeBSD,
Haiku,
OpenBSD, OpenBSD,
NetBSD, NetBSD,
Haiku,
WASI, WASI,
JS, JS,
Orca,
Freestanding, Freestanding,
} }
*/ */
@@ -577,7 +590,7 @@ Odin_Platform_Subtarget_Type :: type_of(ODIN_PLATFORM_SUBTARGET)
Memory = 1, Memory = 1,
Thread = 2, Thread = 2,
} }
Odin_Sanitizer_Flags :: distinct bitset[Odin_Sanitizer_Flag; u32] Odin_Sanitizer_Flags :: distinct bit_set[Odin_Sanitizer_Flag; u32]
ODIN_SANITIZER_FLAGS // is a constant ODIN_SANITIZER_FLAGS // is a constant
*/ */
+106 -101
View File
@@ -333,16 +333,23 @@ make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, alloca
// Note: Prefer using the procedure group `make`. // Note: Prefer using the procedure group `make`.
@(builtin, require_results) @(builtin, require_results)
make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error { make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
make_dynamic_array_error_loc(loc, len, cap) err = _make_dynamic_array_len_cap((^Raw_Dynamic_Array)(&array), size_of(E), align_of(E), len, cap, allocator, loc)
array.allocator = allocator // initialize allocator before just in case it fails to allocate any memory
data := mem_alloc_bytes(size_of(E)*cap, align_of(E), allocator, loc) or_return
s := Raw_Dynamic_Array{raw_data(data), len, cap, allocator}
if data == nil && size_of(E) != 0 {
s.len, s.cap = 0, 0
}
array = transmute(T)s
return return
} }
@(require_results)
_make_dynamic_array_len_cap :: proc(array: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (err: Allocator_Error) {
make_dynamic_array_error_loc(loc, len, cap)
array.allocator = allocator // initialize allocator before just in case it fails to allocate any memory
data := mem_alloc_bytes(size_of_elem*cap, align_of_elem, allocator, loc) or_return
use_zero := data == nil && size_of_elem != 0
array.data = raw_data(data)
array.len = 0 if use_zero else len
array.cap = 0 if use_zero else cap
array.allocator = allocator
return
}
// `make_map` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value. // `make_map` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value.
// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it. // Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
// //
@@ -440,107 +447,103 @@ delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value:
return return
} }
_append_elem :: #force_inline proc(array: ^$T/[dynamic]$E, arg: E, should_zero: bool, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { _append_elem :: #force_inline proc(array: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, arg_ptr: rawptr, should_zero: bool, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
if array == nil { if array == nil {
return 0, nil return
} }
when size_of(E) == 0 {
array := (^Raw_Dynamic_Array)(array)
array.len += 1
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 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 array.cap < array.len+1 {
if should_zero { // Same behavior as _append_elems but there's only one arg, so we always just add DEFAULT_DYNAMIC_ARRAY_CAPACITY.
err = reserve(array, cap, loc) cap := 2 * array.cap + DEFAULT_DYNAMIC_ARRAY_CAPACITY
} else {
err = non_zero_reserve(array, cap, loc) // do not 'or_return' here as it could be a partial success
} err = _reserve_dynamic_array(array, size_of_elem, align_of_elem, cap, should_zero, loc)
}
if cap(array)-len(array) > 0 {
a := (^Raw_Dynamic_Array)(array)
when size_of(E) != 0 {
data := ([^]E)(a.data)
assert(data != nil, loc=loc)
data[a.len] = arg
}
a.len += 1
return 1, err
}
return 0, err
} }
if array.cap-array.len > 0 {
data := ([^]byte)(array.data)
assert(data != nil, loc=loc)
data = data[array.len*size_of_elem:]
intrinsics.mem_copy_non_overlapping(data, arg_ptr, size_of_elem)
array.len += 1
n = 1
}
return
} }
@builtin @builtin
append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
return _append_elem(array, arg, true, loc=loc) when size_of(E) == 0 {
(^Raw_Dynamic_Array)(array).len += 1
return 1, nil
} else {
arg := arg
return _append_elem((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), &arg, true, loc=loc)
}
} }
@builtin @builtin
non_zero_append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { non_zero_append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
return _append_elem(array, arg, false, loc=loc) when size_of(E) == 0 {
(^Raw_Dynamic_Array)(array).len += 1
return 1, nil
} else {
arg := arg
return _append_elem((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), &arg, false, loc=loc)
}
} }
_append_elems :: #force_inline proc(array: ^$T/[dynamic]$E, should_zero: bool, loc := #caller_location, args: ..E) -> (n: int, err: Allocator_Error) #optional_allocator_error { _append_elems :: #force_inline proc(array: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, should_zero: bool, loc := #caller_location, args: rawptr, arg_len: int) -> (n: int, err: Allocator_Error) #optional_allocator_error {
if array == nil { if array == nil {
return 0, nil return 0, nil
} }
arg_len := len(args)
if arg_len <= 0 { if arg_len <= 0 {
return 0, nil return 0, nil
} }
when size_of(E) == 0 { if array.cap < array.len+arg_len {
array := (^Raw_Dynamic_Array)(array) cap := 2 * array.cap + max(DEFAULT_DYNAMIC_ARRAY_CAPACITY, arg_len)
array.len += arg_len
return arg_len, nil
} else {
if cap(array) < len(array)+arg_len {
cap := 2 * cap(array) + max(DEFAULT_DYNAMIC_ARRAY_CAPACITY, arg_len)
// do not 'or_return' here as it could be a partial success // do not 'or_return' here as it could be a partial success
if should_zero { err = _reserve_dynamic_array(array, size_of_elem, align_of_elem, cap, should_zero, loc)
err = reserve(array, cap, loc)
} else {
err = non_zero_reserve(array, cap, loc)
}
}
arg_len = min(cap(array)-len(array), arg_len)
if arg_len > 0 {
a := (^Raw_Dynamic_Array)(array)
when size_of(E) != 0 {
data := ([^]E)(a.data)
assert(data != nil, loc=loc)
intrinsics.mem_copy(&data[a.len], raw_data(args), size_of(E) * arg_len)
}
a.len += arg_len
}
return arg_len, err
} }
arg_len := arg_len
arg_len = min(array.cap-array.len, arg_len)
if arg_len > 0 {
data := ([^]byte)(array.data)
assert(data != nil, loc=loc)
data = data[array.len*size_of_elem:]
intrinsics.mem_copy(data, args, size_of_elem * arg_len) // must be mem_copy (overlapping)
array.len += arg_len
}
return arg_len, err
} }
@builtin @builtin
append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
return _append_elems(array, true, loc, ..args) when size_of(E) == 0 {
a := (^Raw_Dynamic_Array)(array)
a.len += len(args)
return len(args), nil
} else {
return _append_elems((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), true, loc, raw_data(args), len(args))
}
} }
@builtin @builtin
non_zero_append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { non_zero_append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
return _append_elems(array, false, loc, ..args) when size_of(E) == 0 {
a := (^Raw_Dynamic_Array)(array)
a.len += len(args)
return len(args), nil
} else {
return _append_elems((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), false, loc, raw_data(args), len(args))
}
} }
// The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type // The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type
_append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, should_zero: bool, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error { _append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, should_zero: bool, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
args := transmute([]E)arg return _append_elems((^Raw_Dynamic_Array)(array), 1, 1, should_zero, loc, raw_data(arg), len(arg))
if should_zero {
return append_elems(array, ..args, loc=loc)
} else {
return non_zero_append_elems(array, ..args, loc=loc)
}
} }
@builtin @builtin
@@ -679,7 +682,7 @@ assign_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #calle
@builtin @builtin
assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
new_size := index + len(args) new_size := index + len(args)
if len(args) == 0 { if len(args) == 0 {
ok = true ok = true
@@ -729,11 +732,10 @@ clear_dynamic_array :: proc "contextless" (array: ^$T/[dynamic]$E) {
// `reserve_dynamic_array` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`). // `reserve_dynamic_array` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`).
// //
// Note: Prefer the procedure group `reserve`. // Note: Prefer the procedure group `reserve`.
_reserve_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, capacity: int, should_zero: bool, loc := #caller_location) -> Allocator_Error { _reserve_dynamic_array :: #force_inline proc(a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, capacity: int, should_zero: bool, loc := #caller_location) -> Allocator_Error {
if array == nil { if a == nil {
return nil return nil
} }
a := (^Raw_Dynamic_Array)(array)
if capacity <= a.cap { if capacity <= a.cap {
return nil return nil
@@ -744,15 +746,15 @@ _reserve_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, capacity: i
} }
assert(a.allocator.procedure != nil) assert(a.allocator.procedure != nil)
old_size := a.cap * size_of(E) old_size := a.cap * size_of_elem
new_size := capacity * size_of(E) new_size := capacity * size_of_elem
allocator := a.allocator allocator := a.allocator
new_data: []byte new_data: []byte
if should_zero { if should_zero {
new_data = mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return new_data = mem_resize(a.data, old_size, new_size, align_of_elem, allocator, loc) or_return
} else { } else {
new_data = non_zero_mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return new_data = non_zero_mem_resize(a.data, old_size, new_size, align_of_elem, allocator, loc) or_return
} }
if new_data == nil && new_size > 0 { if new_data == nil && new_size > 0 {
return .Out_Of_Memory return .Out_Of_Memory
@@ -765,26 +767,23 @@ _reserve_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, capacity: i
@builtin @builtin
reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error { reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error {
return _reserve_dynamic_array(array, capacity, true, loc) return _reserve_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), capacity, true, loc)
} }
@builtin @builtin
non_zero_reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error { non_zero_reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error {
return _reserve_dynamic_array(array, capacity, false, loc) return _reserve_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), capacity, false, loc)
} }
// `resize_dynamic_array` will try to resize memory of a passed dynamic array or map to the requested element count (setting the `len`, and possibly `cap`).
// _resize_dynamic_array :: #force_inline proc(a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, length: int, should_zero: bool, loc := #caller_location) -> Allocator_Error {
// Note: Prefer the procedure group `resize` if a == nil {
_resize_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, length: int, should_zero: bool, loc := #caller_location) -> Allocator_Error {
if array == nil {
return nil return nil
} }
a := (^Raw_Dynamic_Array)(array)
if length <= a.cap { if length <= a.cap {
if should_zero && a.len < length { if should_zero && a.len < length {
intrinsics.mem_zero(([^]E)(a.data)[a.len:], (length-a.len)*size_of(E)) intrinsics.mem_zero(([^]byte)(a.data)[a.len*size_of_elem:], (length-a.len)*size_of_elem)
} }
a.len = max(length, 0) a.len = max(length, 0)
return nil return nil
@@ -795,15 +794,15 @@ _resize_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, length: int,
} }
assert(a.allocator.procedure != nil) assert(a.allocator.procedure != nil)
old_size := a.cap * size_of(E) old_size := a.cap * size_of_elem
new_size := length * size_of(E) new_size := length * size_of_elem
allocator := a.allocator allocator := a.allocator
new_data : []byte new_data : []byte
if should_zero { if should_zero {
new_data = mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return new_data = mem_resize(a.data, old_size, new_size, align_of_elem, allocator, loc) or_return
} else { } else {
new_data = non_zero_mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return new_data = non_zero_mem_resize(a.data, old_size, new_size, align_of_elem, allocator, loc) or_return
} }
if new_data == nil && new_size > 0 { if new_data == nil && new_size > 0 {
return .Out_Of_Memory return .Out_Of_Memory
@@ -815,14 +814,17 @@ _resize_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, length: int,
return nil return nil
} }
// `resize_dynamic_array` will try to resize memory of a passed dynamic array or map to the requested element count (setting the `len`, and possibly `cap`).
//
// Note: Prefer the procedure group `resize`
@builtin @builtin
resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error { resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error {
return _resize_dynamic_array(array, length, true, loc=loc) return _resize_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), length, true, loc=loc)
} }
@builtin @builtin
non_zero_resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error { non_zero_resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error {
return _resize_dynamic_array(array, length, false, loc=loc) return _resize_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), length, false, loc=loc)
} }
/* /*
@@ -837,10 +839,13 @@ non_zero_resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: i
Note: Prefer the procedure group `shrink` Note: Prefer the procedure group `shrink`
*/ */
shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) { shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) {
if array == nil { return _shrink_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), new_cap, loc)
}
_shrink_dynamic_array :: proc(a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, new_cap := -1, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) {
if a == nil {
return return
} }
a := (^Raw_Dynamic_Array)(array)
new_cap := new_cap if new_cap >= 0 else a.len new_cap := new_cap if new_cap >= 0 else a.len
@@ -853,10 +858,10 @@ shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #call
} }
assert(a.allocator.procedure != nil) assert(a.allocator.procedure != nil)
old_size := a.cap * size_of(E) old_size := a.cap * size_of_elem
new_size := new_cap * size_of(E) new_size := new_cap * size_of_elem
new_data := mem_resize(a.data, old_size, new_size, align_of(E), a.allocator, loc) or_return new_data := mem_resize(a.data, old_size, new_size, align_of_elem, a.allocator, loc) or_return
a.data = raw_data(new_data) a.data = raw_data(new_data)
a.len = min(new_cap, a.len) a.len = min(new_cap, a.len)
+1 -1
View File
@@ -352,7 +352,7 @@ non_zero_append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, #no_broadcast args
} }
_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 { _append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, zero_memory: bool, #no_broadcast args: []E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
if array == nil { if array == nil {
return return
} }
+6 -6
View File
@@ -577,7 +577,7 @@ map_grow_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Inf
@(require_results) @(require_results)
map_reserve_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, new_capacity: uintptr, loc := #caller_location) -> Allocator_Error { map_reserve_dynamic :: #force_no_inline proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, new_capacity: uintptr, loc := #caller_location) -> Allocator_Error {
@(require_results) @(require_results)
ceil_log2 :: #force_inline proc "contextless" (x: uintptr) -> uintptr { ceil_log2 :: #force_inline proc "contextless" (x: uintptr) -> uintptr {
z := intrinsics.count_leading_zeros(x) z := intrinsics.count_leading_zeros(x)
@@ -641,7 +641,7 @@ map_reserve_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_
@(require_results) @(require_results)
map_shrink_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) { map_shrink_dynamic :: #force_no_inline proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) {
if m.allocator.procedure == nil { if m.allocator.procedure == nil {
m.allocator = context.allocator m.allocator = context.allocator
} }
@@ -688,7 +688,7 @@ map_shrink_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_I
} }
@(require_results) @(require_results)
map_free_dynamic :: proc "odin" (m: Raw_Map, info: ^Map_Info, loc := #caller_location) -> Allocator_Error { map_free_dynamic :: #force_no_inline proc "odin" (m: Raw_Map, info: ^Map_Info, loc := #caller_location) -> Allocator_Error {
ptr := rawptr(map_data(m)) ptr := rawptr(map_data(m))
size := int(map_total_allocation_size(uintptr(map_cap(m)), info)) size := int(map_total_allocation_size(uintptr(map_cap(m)), info))
err := mem_free_with_size(ptr, size, m.allocator, loc) err := mem_free_with_size(ptr, size, m.allocator, loc)
@@ -700,7 +700,7 @@ map_free_dynamic :: proc "odin" (m: Raw_Map, info: ^Map_Info, loc := #caller_loc
} }
@(require_results) @(require_results)
map_lookup_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info, k: uintptr) -> (index: uintptr, ok: bool) { map_lookup_dynamic :: #force_no_inline proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info, k: uintptr) -> (index: uintptr, ok: bool) {
if map_len(m) == 0 { if map_len(m) == 0 {
return 0, false return 0, false
} }
@@ -723,7 +723,7 @@ map_lookup_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info,
} }
} }
@(require_results) @(require_results)
map_exists_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info, k: uintptr) -> (ok: bool) { map_exists_dynamic :: #force_no_inline proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info, k: uintptr) -> (ok: bool) {
if map_len(m) == 0 { if map_len(m) == 0 {
return false return false
} }
@@ -749,7 +749,7 @@ map_exists_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info,
@(require_results) @(require_results)
map_erase_dynamic :: #force_inline proc "contextless" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, k: uintptr) -> (old_k, old_v: uintptr, ok: bool) { map_erase_dynamic :: #force_no_inline proc "contextless" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, k: uintptr) -> (old_k, old_v: uintptr, ok: bool) {
index := map_lookup_dynamic(m^, info, k) or_return index := map_lookup_dynamic(m^, info, k) or_return
ks, vs, hs, _, _ := map_kvh_data_dynamic(m^, info) ks, vs, hs, _, _ := map_kvh_data_dynamic(m^, info)
hs[index] |= TOMBSTONE_MASK hs[index] |= TOMBSTONE_MASK
+2 -1
View File
@@ -10,8 +10,9 @@ when ODIN_BUILD_MODE == .Dynamic {
DllMain :: proc "system" (hinstDLL: rawptr, fdwReason: u32, lpReserved: rawptr) -> b32 { DllMain :: proc "system" (hinstDLL: rawptr, fdwReason: u32, lpReserved: rawptr) -> b32 {
context = default_context() context = default_context()
// Populate Windows DLL-specific global // Populate Windows DLL-specific globals
dll_forward_reason = DLL_Forward_Reason(fdwReason) dll_forward_reason = DLL_Forward_Reason(fdwReason)
dll_instance = hinstDLL
switch dll_forward_reason { switch dll_forward_reason {
case .Process_Attach: case .Process_Attach:
+18 -5
View File
@@ -5,11 +5,24 @@ package runtime
import "base:intrinsics" import "base:intrinsics"
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) { _stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
WRITE :: 0x2000004
STDERR :: 2 STDERR :: 2
ret := intrinsics.syscall(WRITE, STDERR, uintptr(raw_data(data)), uintptr(len(data))) when ODIN_NO_CRT {
if ret < 0 { WRITE :: 0x2000004
return 0, _OS_Errno(-ret) ret := intrinsics.syscall(WRITE, STDERR, uintptr(raw_data(data)), uintptr(len(data)))
if ret < 0 {
return 0, _OS_Errno(-ret)
}
return int(ret), 0
} else {
foreign {
write :: proc(handle: i32, buffer: [^]byte, count: uint) -> int ---
__error :: proc() -> ^i32 ---
}
if ret := write(STDERR, raw_data(data), len(data)); ret >= 0 {
return int(ret), 0
}
return 0, _OS_Errno(__error()^)
} }
return int(ret), 0
} }
+7 -6
View File
@@ -262,7 +262,7 @@ print_typeid :: #force_no_inline proc "contextless" (id: typeid) {
} }
} }
@(optimization_mode="size") @(optimization_mode="favor_size")
print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) { print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
if ti == nil { if ti == nil {
print_string("nil") print_string("nil")
@@ -401,15 +401,16 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
} }
print_string("struct ") print_string("struct ")
if info.is_packed { print_string("#packed ") } if .packed in info.flags { print_string("#packed ") }
if info.is_raw_union { print_string("#raw_union ") } if .raw_union in info.flags { print_string("#raw_union ") }
if info.custom_align { if .no_copy in info.flags { print_string("#no_copy ") }
if .align in info.flags {
print_string("#align(") print_string("#align(")
print_u64(u64(ti.align)) print_u64(u64(ti.align))
print_string(") ") print_string(") ")
} }
print_byte('{') print_byte('{')
for name, i in info.names { for name, i in info.names[:info.field_count] {
if i > 0 { print_string(", ") } if i > 0 { print_string(", ") }
print_string(name) print_string(name)
print_string(": ") print_string(": ")
@@ -469,7 +470,7 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
print_string("bit_field ") print_string("bit_field ")
print_type(info.backing_type) print_type(info.backing_type)
print_string(" {") print_string(" {")
for name, i in info.names { for name, i in info.names[:info.field_count] {
if i > 0 { print_string(", ") } if i > 0 { print_string(", ") }
print_string(name) print_string(name)
print_string(": ") print_string(": ")
+2 -1
View File
@@ -297,7 +297,8 @@ lock :: proc(a: ^WASM_Allocator) {
return return
} }
assert(intrinsics.wasm_memory_atomic_wait32((^u32)(&a.mu), u32(new_state), -1) != 0) ret := intrinsics.wasm_memory_atomic_wait32((^u32)(&a.mu), u32(new_state), -1)
assert(ret != 0)
intrinsics.cpu_relax() intrinsics.cpu_relax()
} }
} }
Binary file not shown.
Binary file not shown.
+4 -1
View File
@@ -48,6 +48,9 @@ if "%2" == "1" (
set odin_version_raw="dev-%curr_year%-%curr_month%" set odin_version_raw="dev-%curr_year%-%curr_month%"
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF
rem Parse source code as utf-8 even on shift-jis and other codepages
rem See https://learn.microsoft.com/en-us/cpp/build/reference/utf-8-set-source-and-executable-character-sets-to-utf-8?view=msvc-170
set compiler_flags= %compiler_flags% /utf-8
set compiler_defines= -DODIN_VERSION_RAW=\"%odin_version_raw%\" set compiler_defines= -DODIN_VERSION_RAW=\"%odin_version_raw%\"
if not exist .git\ goto skip_git_hash if not exist .git\ goto skip_git_hash
@@ -111,7 +114,7 @@ call build_vendor.bat
if %errorlevel% neq 0 goto end_of_build if %errorlevel% neq 0 goto end_of_build
rem If the demo doesn't run for you and your CPU is more than a decade old, try -microarch:native rem If the demo doesn't run for you and your CPU is more than a decade old, try -microarch:native
if %release_mode% EQU 0 odin run examples/demo -- Hellope World if %release_mode% EQU 0 odin run examples/demo -vet -strict-style -- Hellope World
del *.obj > NUL 2> NUL del *.obj > NUL 2> NUL
+1 -1
View File
@@ -144,7 +144,7 @@ build_odin() {
} }
run_demo() { run_demo() {
./odin run examples/demo/demo.odin -file -- Hellope World ./odin run examples/demo -vet -strict-style -- Hellope World
} }
if [ $# -eq 0 ]; then if [ $# -eq 0 ]; then
+25
View File
@@ -1167,3 +1167,28 @@ fields_proc :: proc(s: []byte, f: proc(rune) -> bool, allocator := context.alloc
return subslices[:] return subslices[:]
} }
// alias returns true iff a and b have a non-zero length, and any part of
// a overlaps with b.
alias :: proc "contextless" (a, b: []byte) -> bool {
a_len, b_len := len(a), len(b)
if a_len == 0 || b_len == 0 {
return false
}
a_start, b_start := uintptr(raw_data(a)), uintptr(raw_data(b))
a_end, b_end := a_start + uintptr(a_len-1), b_start + uintptr(b_len-1)
return a_start <= b_end && b_start <= a_end
}
// alias_inexactly returns true iff a and b have a non-zero length,
// the base pointer of a and b are NOT equal, and any part of a overlaps
// with b (ie: `alias(a, b)` with an exception that returns false for
// `a == b`, `b = a[:len(a)-69]` and similar conditions).
alias_inexactly :: proc "contextless" (a, b: []byte) -> bool {
if raw_data(a) == raw_data(b) {
return false
}
return alias(a, b)
}
+25 -25
View File
@@ -186,7 +186,7 @@ input_size_from_stream :: proc(z: ^Context_Stream_Input) -> (res: i64, err: Erro
input_size :: proc{input_size_from_memory, input_size_from_stream} input_size :: proc{input_size_from_memory, input_size_from_stream}
@(optimization_mode="speed") @(optimization_mode="favor_size")
read_slice_from_memory :: #force_inline proc(z: ^Context_Memory_Input, size: int) -> (res: []u8, err: io.Error) { read_slice_from_memory :: #force_inline proc(z: ^Context_Memory_Input, size: int) -> (res: []u8, err: io.Error) {
#no_bounds_check { #no_bounds_check {
if len(z.input_data) >= size { if len(z.input_data) >= size {
@@ -203,7 +203,7 @@ read_slice_from_memory :: #force_inline proc(z: ^Context_Memory_Input, size: int
} }
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
read_slice_from_stream :: #force_inline proc(z: ^Context_Stream_Input, size: int) -> (res: []u8, err: io.Error) { read_slice_from_stream :: #force_inline proc(z: ^Context_Stream_Input, size: int) -> (res: []u8, err: io.Error) {
// TODO: REMOVE ALL USE OF context.temp_allocator here // TODO: REMOVE ALL USE OF context.temp_allocator here
// there is literally no need for it // there is literally no need for it
@@ -214,13 +214,13 @@ read_slice_from_stream :: #force_inline proc(z: ^Context_Stream_Input, size: int
read_slice :: proc{read_slice_from_memory, read_slice_from_stream} read_slice :: proc{read_slice_from_memory, read_slice_from_stream}
@(optimization_mode="speed") @(optimization_mode="favor_size")
read_data :: #force_inline proc(z: ^$C, $T: typeid) -> (res: T, err: io.Error) { read_data :: #force_inline proc(z: ^$C, $T: typeid) -> (res: T, err: io.Error) {
b := read_slice(z, size_of(T)) or_return b := read_slice(z, size_of(T)) or_return
return (^T)(&b[0])^, nil return (^T)(&b[0])^, nil
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
read_u8_from_memory :: #force_inline proc(z: ^Context_Memory_Input) -> (res: u8, err: io.Error) { read_u8_from_memory :: #force_inline proc(z: ^Context_Memory_Input) -> (res: u8, err: io.Error) {
#no_bounds_check { #no_bounds_check {
if len(z.input_data) >= 1 { if len(z.input_data) >= 1 {
@@ -232,7 +232,7 @@ read_u8_from_memory :: #force_inline proc(z: ^Context_Memory_Input) -> (res: u8,
return 0, .EOF return 0, .EOF
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
read_u8_from_stream :: #force_inline proc(z: ^Context_Stream_Input) -> (res: u8, err: io.Error) { read_u8_from_stream :: #force_inline proc(z: ^Context_Stream_Input) -> (res: u8, err: io.Error) {
b := read_slice_from_stream(z, 1) or_return b := read_slice_from_stream(z, 1) or_return
return b[0], nil return b[0], nil
@@ -242,7 +242,7 @@ read_u8 :: proc{read_u8_from_memory, read_u8_from_stream}
// You would typically only use this at the end of Inflate, to drain bits from the code buffer // You would typically only use this at the end of Inflate, to drain bits from the code buffer
// preferentially. // preferentially.
@(optimization_mode="speed") @(optimization_mode="favor_size")
read_u8_prefer_code_buffer_lsb :: #force_inline proc(z: ^$C) -> (res: u8, err: io.Error) { read_u8_prefer_code_buffer_lsb :: #force_inline proc(z: ^$C) -> (res: u8, err: io.Error) {
if z.num_bits >= 8 { if z.num_bits >= 8 {
res = u8(read_bits_no_refill_lsb(z, 8)) res = u8(read_bits_no_refill_lsb(z, 8))
@@ -257,7 +257,7 @@ read_u8_prefer_code_buffer_lsb :: #force_inline proc(z: ^$C) -> (res: u8, err: i
return return
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
peek_data_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid) -> (res: T, err: io.Error) { peek_data_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid) -> (res: T, err: io.Error) {
size :: size_of(T) size :: size_of(T)
@@ -275,7 +275,7 @@ peek_data_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid
} }
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
peek_data_at_offset_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid, #any_int offset: int) -> (res: T, err: io.Error) { peek_data_at_offset_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid, #any_int offset: int) -> (res: T, err: io.Error) {
size :: size_of(T) size :: size_of(T)
@@ -293,7 +293,7 @@ peek_data_at_offset_from_memory :: #force_inline proc(z: ^Context_Memory_Input,
} }
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid) -> (res: T, err: io.Error) { peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid) -> (res: T, err: io.Error) {
size :: size_of(T) size :: size_of(T)
@@ -317,7 +317,7 @@ peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid
return res, .None return res, .None
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
peek_data_at_offset_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid, #any_int offset: int) -> (res: T, err: io.Error) { peek_data_at_offset_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid, #any_int offset: int) -> (res: T, err: io.Error) {
size :: size_of(T) size :: size_of(T)
@@ -352,14 +352,14 @@ peek_data :: proc{peek_data_from_memory, peek_data_from_stream, peek_data_at_off
// Sliding window read back // Sliding window read back
@(optimization_mode="speed") @(optimization_mode="favor_size")
peek_back_byte :: #force_inline proc(z: ^$C, offset: i64) -> (res: u8, err: io.Error) { peek_back_byte :: #force_inline proc(z: ^$C, offset: i64) -> (res: u8, err: io.Error) {
// Look back into the sliding window. // Look back into the sliding window.
return z.output.buf[z.bytes_written - offset], .None return z.output.buf[z.bytes_written - offset], .None
} }
// Generalized bit reader LSB // Generalized bit reader LSB
@(optimization_mode="speed") @(optimization_mode="favor_size")
refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width := i8(48)) { refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width := i8(48)) {
refill := u64(width) refill := u64(width)
b := u64(0) b := u64(0)
@@ -385,7 +385,7 @@ refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width :=
} }
// Generalized bit reader LSB // Generalized bit reader LSB
@(optimization_mode="speed") @(optimization_mode="favor_size")
refill_lsb_from_stream :: proc(z: ^Context_Stream_Input, width := i8(24)) { refill_lsb_from_stream :: proc(z: ^Context_Stream_Input, width := i8(24)) {
refill := u64(width) refill := u64(width)
@@ -414,13 +414,13 @@ refill_lsb_from_stream :: proc(z: ^Context_Stream_Input, width := i8(24)) {
refill_lsb :: proc{refill_lsb_from_memory, refill_lsb_from_stream} refill_lsb :: proc{refill_lsb_from_memory, refill_lsb_from_stream}
@(optimization_mode="speed") @(optimization_mode="favor_size")
consume_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) { consume_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) {
z.code_buffer >>= width z.code_buffer >>= width
z.num_bits -= u64(width) z.num_bits -= u64(width)
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
consume_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) { consume_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) {
z.code_buffer >>= width z.code_buffer >>= width
z.num_bits -= u64(width) z.num_bits -= u64(width)
@@ -428,7 +428,7 @@ consume_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, wid
consume_bits_lsb :: proc{consume_bits_lsb_from_memory, consume_bits_lsb_from_stream} consume_bits_lsb :: proc{consume_bits_lsb_from_memory, consume_bits_lsb_from_stream}
@(optimization_mode="speed") @(optimization_mode="favor_size")
peek_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 { peek_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
if z.num_bits < u64(width) { if z.num_bits < u64(width) {
refill_lsb(z) refill_lsb(z)
@@ -436,7 +436,7 @@ peek_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width:
return u32(z.code_buffer &~ (~u64(0) << width)) return u32(z.code_buffer &~ (~u64(0) << width))
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
peek_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 { peek_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
if z.num_bits < u64(width) { if z.num_bits < u64(width) {
refill_lsb(z) refill_lsb(z)
@@ -446,13 +446,13 @@ peek_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width:
peek_bits_lsb :: proc{peek_bits_lsb_from_memory, peek_bits_lsb_from_stream} peek_bits_lsb :: proc{peek_bits_lsb_from_memory, peek_bits_lsb_from_stream}
@(optimization_mode="speed") @(optimization_mode="favor_size")
peek_bits_no_refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 { peek_bits_no_refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
assert(z.num_bits >= u64(width)) assert(z.num_bits >= u64(width))
return u32(z.code_buffer &~ (~u64(0) << width)) return u32(z.code_buffer &~ (~u64(0) << width))
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
peek_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 { peek_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
assert(z.num_bits >= u64(width)) assert(z.num_bits >= u64(width))
return u32(z.code_buffer &~ (~u64(0) << width)) return u32(z.code_buffer &~ (~u64(0) << width))
@@ -460,14 +460,14 @@ peek_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Inp
peek_bits_no_refill_lsb :: proc{peek_bits_no_refill_lsb_from_memory, peek_bits_no_refill_lsb_from_stream} peek_bits_no_refill_lsb :: proc{peek_bits_no_refill_lsb_from_memory, peek_bits_no_refill_lsb_from_stream}
@(optimization_mode="speed") @(optimization_mode="favor_size")
read_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 { read_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
k := #force_inline peek_bits_lsb(z, width) k := #force_inline peek_bits_lsb(z, width)
#force_inline consume_bits_lsb(z, width) #force_inline consume_bits_lsb(z, width)
return k return k
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
read_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 { read_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
k := peek_bits_lsb(z, width) k := peek_bits_lsb(z, width)
consume_bits_lsb(z, width) consume_bits_lsb(z, width)
@@ -476,14 +476,14 @@ read_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width:
read_bits_lsb :: proc{read_bits_lsb_from_memory, read_bits_lsb_from_stream} read_bits_lsb :: proc{read_bits_lsb_from_memory, read_bits_lsb_from_stream}
@(optimization_mode="speed") @(optimization_mode="favor_size")
read_bits_no_refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 { read_bits_no_refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
k := #force_inline peek_bits_no_refill_lsb(z, width) k := #force_inline peek_bits_no_refill_lsb(z, width)
#force_inline consume_bits_lsb(z, width) #force_inline consume_bits_lsb(z, width)
return k return k
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
read_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 { read_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
k := peek_bits_no_refill_lsb(z, width) k := peek_bits_no_refill_lsb(z, width)
consume_bits_lsb(z, width) consume_bits_lsb(z, width)
@@ -493,14 +493,14 @@ read_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Inp
read_bits_no_refill_lsb :: proc{read_bits_no_refill_lsb_from_memory, read_bits_no_refill_lsb_from_stream} read_bits_no_refill_lsb :: proc{read_bits_no_refill_lsb_from_memory, read_bits_no_refill_lsb_from_stream}
@(optimization_mode="speed") @(optimization_mode="favor_size")
discard_to_next_byte_lsb_from_memory :: proc(z: ^Context_Memory_Input) { discard_to_next_byte_lsb_from_memory :: proc(z: ^Context_Memory_Input) {
discard := u8(z.num_bits & 7) discard := u8(z.num_bits & 7)
#force_inline consume_bits_lsb(z, discard) #force_inline consume_bits_lsb(z, discard)
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
discard_to_next_byte_lsb_from_stream :: proc(z: ^Context_Stream_Input) { discard_to_next_byte_lsb_from_stream :: proc(z: ^Context_Stream_Input) {
discard := u8(z.num_bits & 7) discard := u8(z.num_bits & 7)
consume_bits_lsb(z, discard) consume_bits_lsb(z, discard)
+13 -13
View File
@@ -120,7 +120,7 @@ Huffman_Table :: struct {
} }
// Implementation starts here // Implementation starts here
@(optimization_mode="speed") @(optimization_mode="favor_size")
z_bit_reverse :: #force_inline proc(n: u16, bits: u8) -> (r: u16) { z_bit_reverse :: #force_inline proc(n: u16, bits: u8) -> (r: u16) {
assert(bits <= 16) assert(bits <= 16)
// NOTE: Can optimize with llvm.bitreverse.i64 or some bit twiddling // NOTE: Can optimize with llvm.bitreverse.i64 or some bit twiddling
@@ -136,7 +136,7 @@ z_bit_reverse :: #force_inline proc(n: u16, bits: u8) -> (r: u16) {
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
grow_buffer :: proc(buf: ^[dynamic]u8) -> (err: compress.Error) { grow_buffer :: proc(buf: ^[dynamic]u8) -> (err: compress.Error) {
/* /*
That we get here at all means that we didn't pass an expected output size, That we get here at all means that we didn't pass an expected output size,
@@ -154,7 +154,7 @@ grow_buffer :: proc(buf: ^[dynamic]u8) -> (err: compress.Error) {
TODO: Make these return compress.Error. TODO: Make these return compress.Error.
*/ */
@(optimization_mode="speed") @(optimization_mode="favor_size")
write_byte :: #force_inline proc(z: ^$C, c: u8) -> (err: io.Error) #no_bounds_check { write_byte :: #force_inline proc(z: ^$C, c: u8) -> (err: io.Error) #no_bounds_check {
/* /*
Resize if needed. Resize if needed.
@@ -173,7 +173,7 @@ write_byte :: #force_inline proc(z: ^$C, c: u8) -> (err: io.Error) #no_bounds_ch
return .None return .None
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
repl_byte :: proc(z: ^$C, count: u16, c: u8) -> (err: io.Error) #no_bounds_check { repl_byte :: proc(z: ^$C, count: u16, c: u8) -> (err: io.Error) #no_bounds_check {
/* /*
TODO(Jeroen): Once we have a magic ring buffer, we can just peek/write into it TODO(Jeroen): Once we have a magic ring buffer, we can just peek/write into it
@@ -201,7 +201,7 @@ repl_byte :: proc(z: ^$C, count: u16, c: u8) -> (err: io.Error) #no_bounds_check
return .None return .None
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
repl_bytes :: proc(z: ^$C, count: u16, distance: u16) -> (err: io.Error) { repl_bytes :: proc(z: ^$C, count: u16, distance: u16) -> (err: io.Error) {
/* /*
TODO(Jeroen): Once we have a magic ring buffer, we can just peek/write into it TODO(Jeroen): Once we have a magic ring buffer, we can just peek/write into it
@@ -234,8 +234,8 @@ allocate_huffman_table :: proc(allocator := context.allocator) -> (z: ^Huffman_T
return new(Huffman_Table, allocator), nil return new(Huffman_Table, allocator), nil
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) { build_huffman :: #force_no_inline proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
sizes: [HUFFMAN_MAX_BITS+1]int sizes: [HUFFMAN_MAX_BITS+1]int
next_code: [HUFFMAN_MAX_BITS+1]int next_code: [HUFFMAN_MAX_BITS+1]int
@@ -293,7 +293,7 @@ build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
return nil return nil
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
decode_huffman_slowpath :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check { decode_huffman_slowpath :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
code := u16(compress.peek_bits_lsb(z,16)) code := u16(compress.peek_bits_lsb(z,16))
@@ -324,7 +324,7 @@ decode_huffman_slowpath :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Erro
return r, nil return r, nil
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
decode_huffman :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check { decode_huffman :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
if z.num_bits < 16 { if z.num_bits < 16 {
if z.num_bits > 63 { if z.num_bits > 63 {
@@ -344,7 +344,7 @@ decode_huffman :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bo
return decode_huffman_slowpath(z, t) return decode_huffman_slowpath(z, t)
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err: Error) #no_bounds_check { parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err: Error) #no_bounds_check {
#no_bounds_check for { #no_bounds_check for {
value, e := decode_huffman(z, z_repeat) value, e := decode_huffman(z, z_repeat)
@@ -413,7 +413,7 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err:
} }
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := false, expected_output_size := -1, allocator := context.allocator) -> (err: Error) #no_bounds_check { inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := false, expected_output_size := -1, allocator := context.allocator) -> (err: Error) #no_bounds_check {
/* /*
ctx.output must be a bytes.Buffer for now. We'll add a separate implementation that writes to a stream. ctx.output must be a bytes.Buffer for now. We'll add a separate implementation that writes to a stream.
@@ -486,7 +486,7 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f
// TODO: Check alignment of reserve/resize. // TODO: Check alignment of reserve/resize.
@(optimization_mode="speed") @(optimization_mode="favor_size")
inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.allocator) -> (err: Error) #no_bounds_check { inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.allocator) -> (err: Error) #no_bounds_check {
context.allocator = allocator context.allocator = allocator
expected_output_size := expected_output_size expected_output_size := expected_output_size
@@ -670,4 +670,4 @@ inflate_from_byte_array_raw :: proc(input: []u8, buf: ^bytes.Buffer, raw := fals
return inflate_raw(&ctx, expected_output_size=expected_output_size) return inflate_raw(&ctx, expected_output_size=expected_output_size)
} }
inflate :: proc{inflate_from_context, inflate_from_byte_array} inflate :: proc{inflate_from_context, inflate_from_byte_array}
+46
View File
@@ -0,0 +1,46 @@
/*
Package list implements an intrusive doubly-linked list.
An intrusive container requires a `Node` to be embedded in your own structure, like this:
My_String :: struct {
node: list.Node,
value: string,
}
Embedding the members of a `list.Node` in your structure with the `using` keyword is also allowed:
My_String :: struct {
using node: list.Node,
value: string,
}
Here is a full example:
package test
import "core:fmt"
import "core:container/intrusive/list"
main :: proc() {
l: list.List
one := My_String{value="Hello"}
two := My_String{value="World"}
list.push_back(&l, &one.node)
list.push_back(&l, &two.node)
iter := list.iterator_head(l, My_String, "node")
for s in list.iterate_next(&iter) {
fmt.println(s.value)
}
}
My_String :: struct {
node: list.Node,
value: string,
}
*/
package container_intrusive_list
@@ -18,11 +18,18 @@ List :: struct {
tail: ^Node, tail: ^Node,
} }
// The list link you must include in your own structure.
Node :: struct { Node :: struct {
prev, next: ^Node, prev, next: ^Node,
} }
/*
Inserts a new element at the front of the list with O(1) time complexity.
**Inputs**
- list: The container list
- node: The node member of the user-defined element structure
*/
push_front :: proc "contextless" (list: ^List, node: ^Node) { push_front :: proc "contextless" (list: ^List, node: ^Node) {
if list.head != nil { if list.head != nil {
list.head.prev = node list.head.prev = node
@@ -33,7 +40,13 @@ push_front :: proc "contextless" (list: ^List, node: ^Node) {
node.prev, node.next = nil, nil node.prev, node.next = nil, nil
} }
} }
/*
Inserts a new element at the back of the list with O(1) time complexity.
**Inputs**
- list: The container list
- node: The node member of the user-defined element structure
*/
push_back :: proc "contextless" (list: ^List, node: ^Node) { push_back :: proc "contextless" (list: ^List, node: ^Node) {
if list.tail != nil { if list.tail != nil {
list.tail.next = node list.tail.next = node
@@ -45,6 +58,13 @@ push_back :: proc "contextless" (list: ^List, node: ^Node) {
} }
} }
/*
Removes an element from a list with O(1) time complexity.
**Inputs**
- list: The container list
- node: The node member of the user-defined element structure to be removed
*/
remove :: proc "contextless" (list: ^List, node: ^Node) { remove :: proc "contextless" (list: ^List, node: ^Node) {
if node != nil { if node != nil {
if node.next != nil { if node.next != nil {
@@ -61,7 +81,13 @@ remove :: proc "contextless" (list: ^List, node: ^Node) {
} }
} }
} }
/*
Removes from the given list all elements that satisfy a condition with O(N) time complexity.
**Inputs**
- list: The container list
- to_erase: The condition procedure. It should return `true` if a node should be removed, `false` otherwise
*/
remove_by_proc :: proc(list: ^List, to_erase: proc(^Node) -> bool) { remove_by_proc :: proc(list: ^List, to_erase: proc(^Node) -> bool) {
for node := list.head; node != nil; { for node := list.head; node != nil; {
next := node.next next := node.next
@@ -82,7 +108,13 @@ remove_by_proc :: proc(list: ^List, to_erase: proc(^Node) -> bool) {
node = next node = next
} }
} }
/*
Removes from the given list all elements that satisfy a condition with O(N) time complexity.
**Inputs**
- list: The container list
- to_erase: The _contextless_ condition procedure. It should return `true` if a node should be removed, `false` otherwise
*/
remove_by_proc_contextless :: proc(list: ^List, to_erase: proc "contextless" (^Node) -> bool) { remove_by_proc_contextless :: proc(list: ^List, to_erase: proc "contextless" (^Node) -> bool) {
for node := list.head; node != nil; { for node := list.head; node != nil; {
next := node.next next := node.next
@@ -104,12 +136,26 @@ remove_by_proc_contextless :: proc(list: ^List, to_erase: proc "contextless" (^N
} }
} }
/*
Checks whether the given list does not contain any element.
**Inputs**
- list: The container list
**Returns** `true` if `list` is empty, `false` otherwise
*/
is_empty :: proc "contextless" (list: ^List) -> bool { is_empty :: proc "contextless" (list: ^List) -> bool {
return list.head == nil return list.head == nil
} }
/*
Removes and returns the element at the front of the list with O(1) time complexity.
**Inputs**
- list: The container list
**Returns** The node member of the user-defined element structure, or `nil` if the list is empty
*/
pop_front :: proc "contextless" (list: ^List) -> ^Node { pop_front :: proc "contextless" (list: ^List) -> ^Node {
link := list.head link := list.head
if link == nil { if link == nil {
@@ -130,6 +176,14 @@ pop_front :: proc "contextless" (list: ^List) -> ^Node {
return link return link
} }
/*
Removes and returns the element at the back of the list with O(1) time complexity.
**Inputs**
- list: The container list
**Returns** The node member of the user-defined element structure, or `nil` if the list is empty
*/
pop_back :: proc "contextless" (list: ^List) -> ^Node { pop_back :: proc "contextless" (list: ^List) -> ^Node {
link := list.tail link := list.tail
if link == nil { if link == nil {
@@ -151,29 +205,102 @@ pop_back :: proc "contextless" (list: ^List) -> ^Node {
} }
Iterator :: struct($T: typeid) { Iterator :: struct($T: typeid) {
curr: ^Node, curr: ^Node,
offset: uintptr, offset: uintptr,
} }
/*
Creates an iterator pointing at the head of the given list. For an example, see `iterate_next`.
**Inputs**
- list: The container list
- T: The type of the list's elements
- field_name: The name of the node field in the `T` structure
**Returns** An iterator pointing at the head of `list`
*/
iterator_head :: proc "contextless" (list: List, $T: typeid, $field_name: string) -> Iterator(T) iterator_head :: proc "contextless" (list: List, $T: typeid, $field_name: string) -> Iterator(T)
where intrinsics.type_has_field(T, field_name), where intrinsics.type_has_field(T, field_name),
intrinsics.type_field_type(T, field_name) == Node { intrinsics.type_field_type(T, field_name) == Node {
return {list.head, offset_of_by_string(T, field_name)} return {list.head, offset_of_by_string(T, field_name)}
} }
/*
Creates an iterator pointing at the tail of the given list. For an example, see `iterate_prev`.
**Inputs**
- list: The container list
- T: The type of the list's elements
- field_name: The name of the node field in the `T` structure
**Returns** An iterator pointing at the tail of `list`
*/
iterator_tail :: proc "contextless" (list: List, $T: typeid, $field_name: string) -> Iterator(T) iterator_tail :: proc "contextless" (list: List, $T: typeid, $field_name: string) -> Iterator(T)
where intrinsics.type_has_field(T, field_name), where intrinsics.type_has_field(T, field_name),
intrinsics.type_field_type(T, field_name) == Node { intrinsics.type_field_type(T, field_name) == Node {
return {list.tail, offset_of_by_string(T, field_name)} return {list.tail, offset_of_by_string(T, field_name)}
} }
/*
Creates an iterator pointing at the specified node of a list.
**Inputs**
- node: a list node
- T: The type of the list's elements
- field_name: The name of the node field in the `T` structure
**Returns** An iterator pointing at `node`
*/
iterator_from_node :: proc "contextless" (node: ^Node, $T: typeid, $field_name: string) -> Iterator(T) iterator_from_node :: proc "contextless" (node: ^Node, $T: typeid, $field_name: string) -> Iterator(T)
where intrinsics.type_has_field(T, field_name), where intrinsics.type_has_field(T, field_name),
intrinsics.type_field_type(T, field_name) == Node { intrinsics.type_field_type(T, field_name) == Node {
return {node, offset_of_by_string(T, field_name)} return {node, offset_of_by_string(T, field_name)}
} }
/*
Retrieves the next element in a list and advances the iterator.
**Inputs**
- it: The iterator
**Returns**
- ptr: The next list element
- ok: `true` if the element is valid (the iterator could advance), `false` otherwise
Example:
import "core:fmt"
import "core:container/intrusive/list"
iterate_next_example :: proc() {
l: list.List
one := My_Struct{value=1}
two := My_Struct{value=2}
list.push_back(&l, &one.node)
list.push_back(&l, &two.node)
it := list.iterator_head(l, My_Struct, "node")
for num in list.iterate_next(&it) {
fmt.println(num.value)
}
}
My_Struct :: struct {
node : list.Node,
value: int,
}
Output:
1
2
*/
iterate_next :: proc "contextless" (it: ^Iterator($T)) -> (ptr: ^T, ok: bool) { iterate_next :: proc "contextless" (it: ^Iterator($T)) -> (ptr: ^T, ok: bool) {
node := it.curr node := it.curr
if node == nil { if node == nil {
@@ -183,7 +310,47 @@ iterate_next :: proc "contextless" (it: ^Iterator($T)) -> (ptr: ^T, ok: bool) {
return (^T)(uintptr(node) - it.offset), true return (^T)(uintptr(node) - it.offset), true
} }
/*
Retrieves the previous element in a list and recede the iterator.
**Inputs**
- it: The iterator
**Returns**
- ptr: The previous list element
- ok: `true` if the element is valid (the iterator could recede), `false` otherwise
Example:
import "core:fmt"
import "core:container/intrusive/list"
iterate_next_example :: proc() {
l: list.List
one := My_Struct{value=1}
two := My_Struct{value=2}
list.push_back(&l, &one.node)
list.push_back(&l, &two.node)
it := list.iterator_tail(l, My_Struct, "node")
for num in list.iterate_prev(&it) {
fmt.println(num.value)
}
}
My_Struct :: struct {
node : list.Node,
value: int,
}
Output:
2
1
*/
iterate_prev :: proc "contextless" (it: ^Iterator($T)) -> (ptr: ^T, ok: bool) { iterate_prev :: proc "contextless" (it: ^Iterator($T)) -> (ptr: ^T, ok: bool) {
node := it.curr node := it.curr
if node == nil { if node == nil {
@@ -192,4 +359,4 @@ iterate_prev :: proc "contextless" (it: ^Iterator($T)) -> (ptr: ^T, ok: bool) {
it.curr = node.prev it.curr = node.prev
return (^T)(uintptr(node) - it.offset), true return (^T)(uintptr(node) - it.offset), true
} }
+2 -2
View File
@@ -95,11 +95,11 @@ front_ptr :: proc(q: ^$Q/Queue($T)) -> ^T {
} }
back :: proc(q: ^$Q/Queue($T)) -> T { back :: proc(q: ^$Q/Queue($T)) -> T {
idx := (q.offset+uint(q.len))%builtin.len(q.data) idx := (q.offset+uint(q.len - 1))%builtin.len(q.data)
return q.data[idx] return q.data[idx]
} }
back_ptr :: proc(q: ^$Q/Queue($T)) -> ^T { back_ptr :: proc(q: ^$Q/Queue($T)) -> ^T {
idx := (q.offset+uint(q.len))%builtin.len(q.data) idx := (q.offset+uint(q.len - 1))%builtin.len(q.data)
return &q.data[idx] return &q.data[idx]
} }
+2 -12
View File
@@ -7,9 +7,8 @@ STRIDE :: 4
// Context is a keyed AES (ECB) instance. // Context is a keyed AES (ECB) instance.
Context :: struct { Context :: struct {
_sk_exp: [120]u64, _sk_exp: [120]u64,
_num_rounds: int, _num_rounds: int,
_is_initialized: bool,
} }
// init initializes a context for AES with the provided key. // init initializes a context for AES with the provided key.
@@ -18,13 +17,10 @@ init :: proc(ctx: ^Context, key: []byte) {
ctx._num_rounds = keysched(skey[:], key) ctx._num_rounds = keysched(skey[:], key)
skey_expand(ctx._sk_exp[:], skey[:], ctx._num_rounds) skey_expand(ctx._sk_exp[:], skey[:], ctx._num_rounds)
ctx._is_initialized = true
} }
// encrypt_block sets `dst` to `AES-ECB-Encrypt(src)`. // encrypt_block sets `dst` to `AES-ECB-Encrypt(src)`.
encrypt_block :: proc(ctx: ^Context, dst, src: []byte) { encrypt_block :: proc(ctx: ^Context, dst, src: []byte) {
assert(ctx._is_initialized)
q: [8]u64 q: [8]u64
load_blockx1(&q, src) load_blockx1(&q, src)
_encrypt(&q, ctx._sk_exp[:], ctx._num_rounds) _encrypt(&q, ctx._sk_exp[:], ctx._num_rounds)
@@ -33,8 +29,6 @@ encrypt_block :: proc(ctx: ^Context, dst, src: []byte) {
// encrypt_block sets `dst` to `AES-ECB-Decrypt(src)`. // encrypt_block sets `dst` to `AES-ECB-Decrypt(src)`.
decrypt_block :: proc(ctx: ^Context, dst, src: []byte) { decrypt_block :: proc(ctx: ^Context, dst, src: []byte) {
assert(ctx._is_initialized)
q: [8]u64 q: [8]u64
load_blockx1(&q, src) load_blockx1(&q, src)
_decrypt(&q, ctx._sk_exp[:], ctx._num_rounds) _decrypt(&q, ctx._sk_exp[:], ctx._num_rounds)
@@ -43,8 +37,6 @@ decrypt_block :: proc(ctx: ^Context, dst, src: []byte) {
// encrypt_blocks sets `dst` to `AES-ECB-Encrypt(src[0], .. src[n])`. // encrypt_blocks sets `dst` to `AES-ECB-Encrypt(src[0], .. src[n])`.
encrypt_blocks :: proc(ctx: ^Context, dst, src: [][]byte) { encrypt_blocks :: proc(ctx: ^Context, dst, src: [][]byte) {
assert(ctx._is_initialized)
q: [8]u64 = --- q: [8]u64 = ---
src, dst := src, dst src, dst := src, dst
@@ -67,8 +59,6 @@ encrypt_blocks :: proc(ctx: ^Context, dst, src: [][]byte) {
// decrypt_blocks sets dst to `AES-ECB-Decrypt(src[0], .. src[n])`. // decrypt_blocks sets dst to `AES-ECB-Decrypt(src[0], .. src[n])`.
decrypt_blocks :: proc(ctx: ^Context, dst, src: [][]byte) { decrypt_blocks :: proc(ctx: ^Context, dst, src: [][]byte) {
assert(ctx._is_initialized)
q: [8]u64 = --- q: [8]u64 = ---
src, dst := src, dst src, dst := src, dst
+43
View File
@@ -0,0 +1,43 @@
//+build amd64
package aes_hw_intel
import "core:sys/info"
// is_supporte returns true iff hardware accelerated AES
// is supported.
is_supported :: proc "contextless" () -> bool {
features, ok := info.cpu_features.?
if !ok {
return false
}
// Note: Everything with AES-NI and PCLMULQDQ has support for
// the required SSE extxtensions.
req_features :: info.CPU_Features{
.sse2,
.ssse3,
.sse41,
.aes,
.pclmulqdq,
}
return features >= req_features
}
// Context is a keyed AES (ECB) instance.
Context :: struct {
// Note: The ideal thing to do is for the expanded round keys to be
// arrays of `__m128i`, however that implies alignment (or using AVX).
//
// All the people using e-waste processors that don't support an
// insturction set that has been around for over 10 years are why
// we can't have nice things.
_sk_exp_enc: [15][16]byte,
_sk_exp_dec: [15][16]byte,
_num_rounds: int,
}
// init initializes a context for AES with the provided key.
init :: proc(ctx: ^Context, key: []byte) {
keysched(ctx, key)
}
+281
View File
@@ -0,0 +1,281 @@
// Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS 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.
//+build amd64
package aes_hw_intel
import "base:intrinsics"
import "core:crypto/_aes"
import "core:simd"
import "core:simd/x86"
@(private = "file")
GHASH_STRIDE_HW :: 4
@(private = "file")
GHASH_STRIDE_BYTES_HW :: GHASH_STRIDE_HW * _aes.GHASH_BLOCK_SIZE
// GHASH is defined over elements of GF(2^128) with "full little-endian"
// representation: leftmost byte is least significant, and, within each
// byte, leftmost _bit_ is least significant. The natural ordering in
// x86 is "mixed little-endian": bytes are ordered from least to most
// significant, but bits within a byte are in most-to-least significant
// order. Going to full little-endian representation would require
// reversing bits within each byte, which is doable but expensive.
//
// Instead, we go to full big-endian representation, by swapping bytes
// around, which is done with a single _mm_shuffle_epi8() opcode (it
// comes with SSSE3; all CPU that offer pclmulqdq also have SSSE3). We
// can use a full big-endian representation because in a carryless
// multiplication, we have a nice bit reversal property:
//
// rev_128(x) * rev_128(y) = rev_255(x * y)
//
// So by using full big-endian, we still get the right result, except
// that it is right-shifted by 1 bit. The left-shift is relatively
// inexpensive, and it can be mutualised.
//
// Since SSE2 opcodes do not have facilities for shitfting full 128-bit
// values with bit precision, we have to break down values into 64-bit
// chunks. We number chunks from 0 to 3 in left to right order.
@(private = "file")
byteswap_index := transmute(x86.__m128i)simd.i8x16{
// Note: simd.i8x16 is reverse order from x86._mm_set_epi8.
15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
}
@(private = "file", require_results, enable_target_feature = "sse2,ssse3")
byteswap :: #force_inline proc "contextless" (x: x86.__m128i) -> x86.__m128i {
return x86._mm_shuffle_epi8(x, byteswap_index)
}
// From a 128-bit value kw, compute kx as the XOR of the two 64-bit
// halves of kw (into the right half of kx; left half is unspecified),
// and return kx.
@(private = "file", require_results, enable_target_feature = "sse2")
bk :: #force_inline proc "contextless" (kw: x86.__m128i) -> x86.__m128i {
return x86._mm_xor_si128(kw, x86._mm_shuffle_epi32(kw, 0x0e))
}
// Combine two 64-bit values (k0:k1) into a 128-bit (kw) value and
// the XOR of the two values (kx), and return (kw, kx).
@(private = "file", enable_target_feature = "sse2")
pbk :: #force_inline proc "contextless" (k0, k1: x86.__m128i) -> (x86.__m128i, x86.__m128i) {
kw := x86._mm_unpacklo_epi64(k1, k0)
kx := x86._mm_xor_si128(k0, k1)
return kw, kx
}
// Left-shift by 1 bit a 256-bit value (in four 64-bit words).
@(private = "file", require_results, enable_target_feature = "sse2")
sl_256 :: #force_inline proc "contextless" (x0, x1, x2, x3: x86.__m128i) -> (x86.__m128i, x86.__m128i, x86.__m128i, x86.__m128i) {
x0, x1, x2, x3 := x0, x1, x2, x3
x0 = x86._mm_or_si128(x86._mm_slli_epi64(x0, 1), x86._mm_srli_epi64(x1, 63))
x1 = x86._mm_or_si128(x86._mm_slli_epi64(x1, 1), x86._mm_srli_epi64(x2, 63))
x2 = x86._mm_or_si128(x86._mm_slli_epi64(x2, 1), x86._mm_srli_epi64(x3, 63))
x3 = x86._mm_slli_epi64(x3, 1)
return x0, x1, x2, x3
}
// Perform reduction in GF(2^128).
@(private = "file", require_results, enable_target_feature = "sse2")
reduce_f128 :: #force_inline proc "contextless" (x0, x1, x2, x3: x86.__m128i) -> (x86.__m128i, x86.__m128i) {
x0, x1, x2 := x0, x1, x2
x1 = x86._mm_xor_si128(
x1,
x86._mm_xor_si128(
x86._mm_xor_si128(
x3,
x86._mm_srli_epi64(x3, 1)),
x86._mm_xor_si128(
x86._mm_srli_epi64(x3, 2),
x86._mm_srli_epi64(x3, 7))))
x2 = x86._mm_xor_si128(
x86._mm_xor_si128(
x2,
x86._mm_slli_epi64(x3, 63)),
x86._mm_xor_si128(
x86._mm_slli_epi64(x3, 62),
x86._mm_slli_epi64(x3, 57)))
x0 = x86._mm_xor_si128(
x0,
x86._mm_xor_si128(
x86._mm_xor_si128(
x2,
x86._mm_srli_epi64(x2, 1)),
x86._mm_xor_si128(
x86._mm_srli_epi64(x2, 2),
x86._mm_srli_epi64(x2, 7))))
x1 = x86._mm_xor_si128(
x86._mm_xor_si128(
x1,
x86._mm_slli_epi64(x2, 63)),
x86._mm_xor_si128(
x86._mm_slli_epi64(x2, 62),
x86._mm_slli_epi64(x2, 57)))
return x0, x1
}
// Square value kw in GF(2^128) into (dw,dx).
@(private = "file", require_results, enable_target_feature = "sse2,pclmul")
square_f128 :: #force_inline proc "contextless" (kw: x86.__m128i) -> (x86.__m128i, x86.__m128i) {
z1 := x86._mm_clmulepi64_si128(kw, kw, 0x11)
z3 := x86._mm_clmulepi64_si128(kw, kw, 0x00)
z0 := x86._mm_shuffle_epi32(z1, 0x0E)
z2 := x86._mm_shuffle_epi32(z3, 0x0E)
z0, z1, z2, z3 = sl_256(z0, z1, z2, z3)
z0, z1 = reduce_f128(z0, z1, z2, z3)
return pbk(z0, z1)
}
// ghash calculates the GHASH of data, with the key `key`, and input `dst`
// and `data`, and stores the resulting digest in `dst`.
//
// Note: `dst` is both an input and an output, to support easy implementation
// of GCM.
@(enable_target_feature = "sse2,ssse3,pclmul")
ghash :: proc "contextless" (dst, key, data: []byte) #no_bounds_check {
if len(dst) != _aes.GHASH_BLOCK_SIZE || len(key) != _aes.GHASH_BLOCK_SIZE {
intrinsics.trap()
}
// Note: BearSSL opts to copy the remainder into a zero-filled
// 64-byte buffer. We do something slightly more simple.
// Load key and dst (h and y).
yw := intrinsics.unaligned_load((^x86.__m128i)(raw_data(dst)))
h1w := intrinsics.unaligned_load((^x86.__m128i)(raw_data(key)))
yw = byteswap(yw)
h1w = byteswap(h1w)
h1x := bk(h1w)
// Process 4 blocks at a time
buf := data
l := len(buf)
if l >= GHASH_STRIDE_BYTES_HW {
// Compute h2 = h^2
h2w, h2x := square_f128(h1w)
// Compute h3 = h^3 = h*(h^2)
t1 := x86._mm_clmulepi64_si128(h1w, h2w, 0x11)
t3 := x86._mm_clmulepi64_si128(h1w, h2w, 0x00)
t2 := x86._mm_xor_si128(
x86._mm_clmulepi64_si128(h1x, h2x, 0x00),
x86._mm_xor_si128(t1, t3))
t0 := x86._mm_shuffle_epi32(t1, 0x0E)
t1 = x86._mm_xor_si128(t1, x86._mm_shuffle_epi32(t2, 0x0E))
t2 = x86._mm_xor_si128(t2, x86._mm_shuffle_epi32(t3, 0x0E))
t0, t1, t2, t3 = sl_256(t0, t1, t2, t3)
t0, t1 = reduce_f128(t0, t1, t2, t3)
h3w, h3x := pbk(t0, t1)
// Compute h4 = h^4 = (h^2)^2
h4w, h4x := square_f128(h2w)
for l >= GHASH_STRIDE_BYTES_HW {
aw0 := intrinsics.unaligned_load((^x86.__m128i)(raw_data(buf)))
aw1 := intrinsics.unaligned_load((^x86.__m128i)(raw_data(buf[16:])))
aw2 := intrinsics.unaligned_load((^x86.__m128i)(raw_data(buf[32:])))
aw3 := intrinsics.unaligned_load((^x86.__m128i)(raw_data(buf[48:])))
aw0 = byteswap(aw0)
aw1 = byteswap(aw1)
aw2 = byteswap(aw2)
aw3 = byteswap(aw3)
buf, l = buf[GHASH_STRIDE_BYTES_HW:], l - GHASH_STRIDE_BYTES_HW
aw0 = x86._mm_xor_si128(aw0, yw)
ax1 := bk(aw1)
ax2 := bk(aw2)
ax3 := bk(aw3)
ax0 := bk(aw0)
t1 = x86._mm_xor_si128(
x86._mm_xor_si128(
x86._mm_clmulepi64_si128(aw0, h4w, 0x11),
x86._mm_clmulepi64_si128(aw1, h3w, 0x11)),
x86._mm_xor_si128(
x86._mm_clmulepi64_si128(aw2, h2w, 0x11),
x86._mm_clmulepi64_si128(aw3, h1w, 0x11)))
t3 = x86._mm_xor_si128(
x86._mm_xor_si128(
x86._mm_clmulepi64_si128(aw0, h4w, 0x00),
x86._mm_clmulepi64_si128(aw1, h3w, 0x00)),
x86._mm_xor_si128(
x86._mm_clmulepi64_si128(aw2, h2w, 0x00),
x86._mm_clmulepi64_si128(aw3, h1w, 0x00)))
t2 = x86._mm_xor_si128(
x86._mm_xor_si128(
x86._mm_clmulepi64_si128(ax0, h4x, 0x00),
x86._mm_clmulepi64_si128(ax1, h3x, 0x00)),
x86._mm_xor_si128(
x86._mm_clmulepi64_si128(ax2, h2x, 0x00),
x86._mm_clmulepi64_si128(ax3, h1x, 0x00)))
t2 = x86._mm_xor_si128(t2, x86._mm_xor_si128(t1, t3))
t0 = x86._mm_shuffle_epi32(t1, 0x0E)
t1 = x86._mm_xor_si128(t1, x86._mm_shuffle_epi32(t2, 0x0E))
t2 = x86._mm_xor_si128(t2, x86._mm_shuffle_epi32(t3, 0x0E))
t0, t1, t2, t3 = sl_256(t0, t1, t2, t3)
t0, t1 = reduce_f128(t0, t1, t2, t3)
yw = x86._mm_unpacklo_epi64(t1, t0)
}
}
// Process 1 block at a time
src: []byte
for l > 0 {
if l >= _aes.GHASH_BLOCK_SIZE {
src = buf
buf = buf[_aes.GHASH_BLOCK_SIZE:]
l -= _aes.GHASH_BLOCK_SIZE
} else {
tmp: [_aes.GHASH_BLOCK_SIZE]byte
copy(tmp[:], buf)
src = tmp[:]
l = 0
}
aw := intrinsics.unaligned_load((^x86.__m128i)(raw_data(src)))
aw = byteswap(aw)
aw = x86._mm_xor_si128(aw, yw)
ax := bk(aw)
t1 := x86._mm_clmulepi64_si128(aw, h1w, 0x11)
t3 := x86._mm_clmulepi64_si128(aw, h1w, 0x00)
t2 := x86._mm_clmulepi64_si128(ax, h1x, 0x00)
t2 = x86._mm_xor_si128(t2, x86._mm_xor_si128(t1, t3))
t0 := x86._mm_shuffle_epi32(t1, 0x0E)
t1 = x86._mm_xor_si128(t1, x86._mm_shuffle_epi32(t2, 0x0E))
t2 = x86._mm_xor_si128(t2, x86._mm_shuffle_epi32(t3, 0x0E))
t0, t1, t2, t3 = sl_256(t0, t1, t2, t3)
t0, t1 = reduce_f128(t0, t1, t2, t3)
yw = x86._mm_unpacklo_epi64(t1, t0)
}
// Write back the hash (dst, aka y)
yw = byteswap(yw)
intrinsics.unaligned_store((^x86.__m128i)(raw_data(dst)), yw)
}
@@ -0,0 +1,178 @@
// Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS “AS IS” AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS 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.
//+build amd64
package aes_hw_intel
import "base:intrinsics"
import "core:crypto/_aes"
import "core:mem"
import "core:simd/x86"
// Intel AES-NI based implementation. Inspiration taken from BearSSL.
//
// Note: This assumes that the SROA optimization pass is enabled to be
// anything resembling performat otherwise, LLVM will not elide a massive
// number of redundant loads/stores it generates for every intrinsic call.
@(private = "file", require_results, enable_target_feature = "sse2")
expand_step128 :: #force_inline proc(k1, k2: x86.__m128i) -> x86.__m128i {
k1, k2 := k1, k2
k2 = x86._mm_shuffle_epi32(k2, 0xff)
k1 = x86._mm_xor_si128(k1, x86._mm_slli_si128(k1, 0x04))
k1 = x86._mm_xor_si128(k1, x86._mm_slli_si128(k1, 0x04))
k1 = x86._mm_xor_si128(k1, x86._mm_slli_si128(k1, 0x04))
return x86._mm_xor_si128(k1, k2)
}
@(private = "file", require_results, enable_target_feature = "sse,sse2")
expand_step192a :: #force_inline proc (k1_, k2_: ^x86.__m128i, k3: x86.__m128i) -> (x86.__m128i, x86.__m128i) {
k1, k2, k3 := k1_^, k2_^, k3
k3 = x86._mm_shuffle_epi32(k3, 0x55)
k1 = x86._mm_xor_si128(k1, x86._mm_slli_si128(k1, 0x04))
k1 = x86._mm_xor_si128(k1, x86._mm_slli_si128(k1, 0x04))
k1 = x86._mm_xor_si128(k1, x86._mm_slli_si128(k1, 0x04))
k1 = x86._mm_xor_si128(k1, k3)
tmp := k2
k2 = x86._mm_xor_si128(k2, x86._mm_slli_si128(k2, 0x04))
k2 = x86._mm_xor_si128(k2, x86._mm_shuffle_epi32(k1, 0xff))
k1_, k2_ := k1_, k2_
k1_^, k2_^ = k1, k2
r1 := transmute(x86.__m128i)(x86._mm_shuffle_ps(transmute(x86.__m128)(tmp), transmute(x86.__m128)(k1), 0x44))
r2 := transmute(x86.__m128i)(x86._mm_shuffle_ps(transmute(x86.__m128)(k1), transmute(x86.__m128)(k2), 0x4e))
return r1, r2
}
@(private = "file", require_results, enable_target_feature = "sse2")
expand_step192b :: #force_inline proc (k1_, k2_: ^x86.__m128i, k3: x86.__m128i) -> x86.__m128i {
k1, k2, k3 := k1_^, k2_^, k3
k3 = x86._mm_shuffle_epi32(k3, 0x55)
k1 = x86._mm_xor_si128(k1, x86._mm_slli_si128(k1, 0x04))
k1 = x86._mm_xor_si128(k1, x86._mm_slli_si128(k1, 0x04))
k1 = x86._mm_xor_si128(k1, x86._mm_slli_si128(k1, 0x04))
k1 = x86._mm_xor_si128(k1, k3)
k2 = x86._mm_xor_si128(k2, x86._mm_slli_si128(k2, 0x04))
k2 = x86._mm_xor_si128(k2, x86._mm_shuffle_epi32(k1, 0xff))
k1_, k2_ := k1_, k2_
k1_^, k2_^ = k1, k2
return k1
}
@(private = "file", require_results, enable_target_feature = "sse2")
expand_step256b :: #force_inline proc(k1, k2: x86.__m128i) -> x86.__m128i {
k1, k2 := k1, k2
k2 = x86._mm_shuffle_epi32(k2, 0xaa)
k1 = x86._mm_xor_si128(k1, x86._mm_slli_si128(k1, 0x04))
k1 = x86._mm_xor_si128(k1, x86._mm_slli_si128(k1, 0x04))
k1 = x86._mm_xor_si128(k1, x86._mm_slli_si128(k1, 0x04))
return x86._mm_xor_si128(k1, k2)
}
@(private = "file", enable_target_feature = "aes")
derive_dec_keys :: proc(ctx: ^Context, sks: ^[15]x86.__m128i, num_rounds: int) {
intrinsics.unaligned_store((^x86.__m128i)(&ctx._sk_exp_dec[0]), sks[num_rounds])
for i in 1 ..< num_rounds {
tmp := x86._mm_aesimc_si128(sks[i])
intrinsics.unaligned_store((^x86.__m128i)(&ctx._sk_exp_dec[num_rounds - i]), tmp)
}
intrinsics.unaligned_store((^x86.__m128i)(&ctx._sk_exp_dec[num_rounds]), sks[0])
}
@(private, enable_target_feature = "sse,sse2,aes")
keysched :: proc(ctx: ^Context, key: []byte) {
sks: [15]x86.__m128i = ---
// Compute the encryption keys.
num_rounds, key_len := 0, len(key)
switch key_len {
case _aes.KEY_SIZE_128:
sks[0] = intrinsics.unaligned_load((^x86.__m128i)(raw_data(key)))
sks[1] = expand_step128(sks[0], x86._mm_aeskeygenassist_si128(sks[0], 0x01))
sks[2] = expand_step128(sks[1], x86._mm_aeskeygenassist_si128(sks[1], 0x02))
sks[3] = expand_step128(sks[2], x86._mm_aeskeygenassist_si128(sks[2], 0x04))
sks[4] = expand_step128(sks[3], x86._mm_aeskeygenassist_si128(sks[3], 0x08))
sks[5] = expand_step128(sks[4], x86._mm_aeskeygenassist_si128(sks[4], 0x10))
sks[6] = expand_step128(sks[5], x86._mm_aeskeygenassist_si128(sks[5], 0x20))
sks[7] = expand_step128(sks[6], x86._mm_aeskeygenassist_si128(sks[6], 0x40))
sks[8] = expand_step128(sks[7], x86._mm_aeskeygenassist_si128(sks[7], 0x80))
sks[9] = expand_step128(sks[8], x86._mm_aeskeygenassist_si128(sks[8], 0x1b))
sks[10] = expand_step128(sks[9], x86._mm_aeskeygenassist_si128(sks[9], 0x36))
num_rounds = _aes.ROUNDS_128
case _aes.KEY_SIZE_192:
k0 := intrinsics.unaligned_load((^x86.__m128i)(raw_data(key)))
k1 := x86.__m128i{
intrinsics.unaligned_load((^i64)(raw_data(key[16:]))),
0,
}
sks[0] = k0
sks[1], sks[2] = expand_step192a(&k0, &k1, x86._mm_aeskeygenassist_si128(k1, 0x01))
sks[3] = expand_step192b(&k0, &k1, x86._mm_aeskeygenassist_si128(k1, 0x02))
sks[4], sks[5] = expand_step192a(&k0, &k1, x86._mm_aeskeygenassist_si128(k1, 0x04))
sks[6] = expand_step192b(&k0, &k1, x86._mm_aeskeygenassist_si128(k1, 0x08))
sks[7], sks[8] = expand_step192a(&k0, &k1, x86._mm_aeskeygenassist_si128(k1, 0x10))
sks[9] = expand_step192b(&k0, &k1, x86._mm_aeskeygenassist_si128(k1, 0x20))
sks[10], sks[11] = expand_step192a(&k0, &k1, x86._mm_aeskeygenassist_si128(k1, 0x40))
sks[12] = expand_step192b(&k0, &k1, x86._mm_aeskeygenassist_si128(k1, 0x80))
num_rounds = _aes.ROUNDS_192
case _aes.KEY_SIZE_256:
sks[0] = intrinsics.unaligned_load((^x86.__m128i)(raw_data(key)))
sks[1] = intrinsics.unaligned_load((^x86.__m128i)(raw_data(key[16:])))
sks[2] = expand_step128(sks[0], x86._mm_aeskeygenassist_si128(sks[1], 0x01))
sks[3] = expand_step256b(sks[1], x86._mm_aeskeygenassist_si128(sks[2], 0x01))
sks[4] = expand_step128(sks[2], x86._mm_aeskeygenassist_si128(sks[3], 0x02))
sks[5] = expand_step256b(sks[3], x86._mm_aeskeygenassist_si128(sks[4], 0x02))
sks[6] = expand_step128(sks[4], x86._mm_aeskeygenassist_si128(sks[5], 0x04))
sks[7] = expand_step256b(sks[5], x86._mm_aeskeygenassist_si128(sks[6], 0x04))
sks[8] = expand_step128(sks[6], x86._mm_aeskeygenassist_si128(sks[7], 0x08))
sks[9] = expand_step256b(sks[7], x86._mm_aeskeygenassist_si128(sks[8], 0x08))
sks[10] = expand_step128(sks[8], x86._mm_aeskeygenassist_si128(sks[9], 0x10))
sks[11] = expand_step256b(sks[9], x86._mm_aeskeygenassist_si128(sks[10], 0x10))
sks[12] = expand_step128(sks[10], x86._mm_aeskeygenassist_si128(sks[11], 0x20))
sks[13] = expand_step256b(sks[11], x86._mm_aeskeygenassist_si128(sks[12], 0x20))
sks[14] = expand_step128(sks[12], x86._mm_aeskeygenassist_si128(sks[13], 0x40))
num_rounds = _aes.ROUNDS_256
case:
panic("crypto/aes: invalid AES key size")
}
for i in 0 ..= num_rounds {
intrinsics.unaligned_store((^x86.__m128i)(&ctx._sk_exp_enc[i]), sks[i])
}
// Compute the decryption keys. GCM and CTR do not need this, however
// ECB, CBC, OCB3, etc do.
derive_dec_keys(ctx, &sks, num_rounds)
ctx._num_rounds = num_rounds
mem.zero_explicit(&sks, size_of(sks))
}
-1
View File
@@ -6,7 +6,6 @@ See:
- https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf - https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf
- https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf - https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
*/ */
package aes package aes
import "core:crypto/_aes" import "core:crypto/_aes"
+17 -15
View File
@@ -1,5 +1,6 @@
package aes package aes
import "core:bytes"
import "core:crypto/_aes/ct64" import "core:crypto/_aes/ct64"
import "core:encoding/endian" import "core:encoding/endian"
import "core:math/bits" import "core:math/bits"
@@ -37,14 +38,15 @@ init_ctr :: proc(ctx: ^Context_CTR, key, iv: []byte, impl := Implementation.Hard
xor_bytes_ctr :: proc(ctx: ^Context_CTR, dst, src: []byte) { xor_bytes_ctr :: proc(ctx: ^Context_CTR, dst, src: []byte) {
assert(ctx._is_initialized) assert(ctx._is_initialized)
// TODO: Enforcing that dst and src alias exactly or not at all
// is a good idea, though odd aliasing should be extremely uncommon.
src, dst := src, dst src, dst := src, dst
if dst_len := len(dst); dst_len < len(src) { if dst_len := len(dst); dst_len < len(src) {
src = src[:dst_len] src = src[:dst_len]
} }
if bytes.alias_inexactly(dst, src) {
panic("crypto/aes: dst and src alias inexactly")
}
for remaining := len(src); remaining > 0; { for remaining := len(src); remaining > 0; {
// Process multiple blocks at once // Process multiple blocks at once
if ctx._off == BLOCK_SIZE { if ctx._off == BLOCK_SIZE {
@@ -123,8 +125,8 @@ reset_ctr :: proc "contextless" (ctx: ^Context_CTR) {
ctx._is_initialized = false ctx._is_initialized = false
} }
@(private) @(private = "file")
ctr_blocks :: proc(ctx: ^Context_CTR, dst, src: []byte, nr_blocks: int) { ctr_blocks :: proc(ctx: ^Context_CTR, dst, src: []byte, nr_blocks: int) #no_bounds_check {
// Use the optimized hardware implementation if available. // Use the optimized hardware implementation if available.
if _, is_hw := ctx._impl.(Context_Impl_Hardware); is_hw { if _, is_hw := ctx._impl.(Context_Impl_Hardware); is_hw {
ctr_blocks_hw(ctx, dst, src, nr_blocks) ctr_blocks_hw(ctx, dst, src, nr_blocks)
@@ -183,17 +185,17 @@ xor_blocks :: #force_inline proc "contextless" (dst, src: []byte, blocks: [][]by
// performance of this implementation matters to where that // performance of this implementation matters to where that
// optimization would be worth it, use chacha20poly1305, or a // optimization would be worth it, use chacha20poly1305, or a
// CPU that isn't e-waste. // CPU that isn't e-waste.
if src != nil { #no_bounds_check {
#no_bounds_check { if src != nil {
for i in 0 ..< len(blocks) { for i in 0 ..< len(blocks) {
off := i * BLOCK_SIZE off := i * BLOCK_SIZE
for j in 0 ..< BLOCK_SIZE { for j in 0 ..< BLOCK_SIZE {
blocks[i][j] ~= src[off + j] blocks[i][j] ~= src[off + j]
}
} }
} }
for i in 0 ..< len(blocks) {
copy(dst[i * BLOCK_SIZE:], blocks[i])
} }
} }
for i in 0 ..< len(blocks) {
copy(dst[i * BLOCK_SIZE:], blocks[i])
}
} }
+151
View File
@@ -0,0 +1,151 @@
//+build amd64
package aes
import "base:intrinsics"
import "core:crypto/_aes"
import "core:math/bits"
import "core:mem"
import "core:simd/x86"
@(private)
CTR_STRIDE_HW :: 4
@(private)
CTR_STRIDE_BYTES_HW :: CTR_STRIDE_HW * BLOCK_SIZE
@(private, enable_target_feature = "sse2,aes")
ctr_blocks_hw :: proc(ctx: ^Context_CTR, dst, src: []byte, nr_blocks: int) #no_bounds_check {
hw_ctx := ctx._impl.(Context_Impl_Hardware)
sks: [15]x86.__m128i = ---
for i in 0 ..= hw_ctx._num_rounds {
sks[i] = intrinsics.unaligned_load((^x86.__m128i)(&hw_ctx._sk_exp_enc[i]))
}
hw_inc_ctr := #force_inline proc "contextless" (hi, lo: u64) -> (x86.__m128i, u64, u64) {
ret := x86.__m128i{
i64(intrinsics.byte_swap(hi)),
i64(intrinsics.byte_swap(lo)),
}
hi, lo := hi, lo
carry: u64
lo, carry = bits.add_u64(lo, 1, 0)
hi, _ = bits.add_u64(hi, 0, carry)
return ret, hi, lo
}
// The latency of AESENC depends on mfg and microarchitecture:
// - 7 -> up to Broadwell
// - 4 -> AMD and Skylake - Cascade Lake
// - 3 -> Ice Lake and newer
//
// This implementation does 4 blocks at once, since performance
// should be "adequate" across most CPUs.
src, dst := src, dst
nr_blocks := nr_blocks
ctr_hi, ctr_lo := ctx._ctr_hi, ctx._ctr_lo
blks: [CTR_STRIDE_HW]x86.__m128i = ---
for nr_blocks >= CTR_STRIDE_HW {
#unroll for i in 0..< CTR_STRIDE_HW {
blks[i], ctr_hi, ctr_lo = hw_inc_ctr(ctr_hi, ctr_lo)
}
#unroll for i in 0 ..< CTR_STRIDE_HW {
blks[i] = x86._mm_xor_si128(blks[i], sks[0])
}
#unroll for i in 1 ..= 9 {
#unroll for j in 0 ..< CTR_STRIDE_HW {
blks[j] = x86._mm_aesenc_si128(blks[j], sks[i])
}
}
switch hw_ctx._num_rounds {
case _aes.ROUNDS_128:
#unroll for i in 0 ..< CTR_STRIDE_HW {
blks[i] = x86._mm_aesenclast_si128(blks[i], sks[10])
}
case _aes.ROUNDS_192:
#unroll for i in 10 ..= 11 {
#unroll for j in 0 ..< CTR_STRIDE_HW {
blks[j] = x86._mm_aesenc_si128(blks[j], sks[i])
}
}
#unroll for i in 0 ..< CTR_STRIDE_HW {
blks[i] = x86._mm_aesenclast_si128(blks[i], sks[12])
}
case _aes.ROUNDS_256:
#unroll for i in 10 ..= 13 {
#unroll for j in 0 ..< CTR_STRIDE_HW {
blks[j] = x86._mm_aesenc_si128(blks[j], sks[i])
}
}
#unroll for i in 0 ..< CTR_STRIDE_HW {
blks[i] = x86._mm_aesenclast_si128(blks[i], sks[14])
}
}
xor_blocks_hw(dst, src, blks[:])
if src != nil {
src = src[CTR_STRIDE_BYTES_HW:]
}
dst = dst[CTR_STRIDE_BYTES_HW:]
nr_blocks -= CTR_STRIDE_HW
}
// Handle the remainder.
for nr_blocks > 0 {
blks[0], ctr_hi, ctr_lo = hw_inc_ctr(ctr_hi, ctr_lo)
blks[0] = x86._mm_xor_si128(blks[0], sks[0])
#unroll for i in 1 ..= 9 {
blks[0] = x86._mm_aesenc_si128(blks[0], sks[i])
}
switch hw_ctx._num_rounds {
case _aes.ROUNDS_128:
blks[0] = x86._mm_aesenclast_si128(blks[0], sks[10])
case _aes.ROUNDS_192:
#unroll for i in 10 ..= 11 {
blks[0] = x86._mm_aesenc_si128(blks[0], sks[i])
}
blks[0] = x86._mm_aesenclast_si128(blks[0], sks[12])
case _aes.ROUNDS_256:
#unroll for i in 10 ..= 13 {
blks[0] = x86._mm_aesenc_si128(blks[0], sks[i])
}
blks[0] = x86._mm_aesenclast_si128(blks[0], sks[14])
}
xor_blocks_hw(dst, src, blks[:1])
if src != nil {
src = src[BLOCK_SIZE:]
}
dst = dst[BLOCK_SIZE:]
nr_blocks -= 1
}
// Write back the counter.
ctx._ctr_hi, ctx._ctr_lo = ctr_hi, ctr_lo
mem.zero_explicit(&blks, size_of(blks))
mem.zero_explicit(&sks, size_of(sks))
}
@(private, enable_target_feature = "sse2")
xor_blocks_hw :: proc(dst, src: []byte, blocks: []x86.__m128i) {
#no_bounds_check {
if src != nil {
for i in 0 ..< len(blocks) {
off := i * BLOCK_SIZE
tmp := intrinsics.unaligned_load((^x86.__m128i)(raw_data(src[off:])))
blocks[i] = x86._mm_xor_si128(blocks[i], tmp)
}
}
for i in 0 ..< len(blocks) {
intrinsics.unaligned_store((^x86.__m128i)(raw_data(dst[i * BLOCK_SIZE:])), blocks[i])
}
}
}
+58
View File
@@ -0,0 +1,58 @@
//+build amd64
package aes
import "base:intrinsics"
import "core:crypto/_aes"
import "core:simd/x86"
@(private, enable_target_feature = "sse2,aes")
encrypt_block_hw :: proc(ctx: ^Context_Impl_Hardware, dst, src: []byte) {
blk := intrinsics.unaligned_load((^x86.__m128i)(raw_data(src)))
blk = x86._mm_xor_si128(blk, intrinsics.unaligned_load((^x86.__m128i)(&ctx._sk_exp_enc[0])))
#unroll for i in 1 ..= 9 {
blk = x86._mm_aesenc_si128(blk, intrinsics.unaligned_load((^x86.__m128i)(&ctx._sk_exp_enc[i])))
}
switch ctx._num_rounds {
case _aes.ROUNDS_128:
blk = x86._mm_aesenclast_si128(blk, intrinsics.unaligned_load((^x86.__m128i)(&ctx._sk_exp_enc[10])))
case _aes.ROUNDS_192:
#unroll for i in 10 ..= 11 {
blk = x86._mm_aesenc_si128(blk, intrinsics.unaligned_load((^x86.__m128i)(&ctx._sk_exp_enc[i])))
}
blk = x86._mm_aesenclast_si128(blk, intrinsics.unaligned_load((^x86.__m128i)(&ctx._sk_exp_enc[12])))
case _aes.ROUNDS_256:
#unroll for i in 10 ..= 13 {
blk = x86._mm_aesenc_si128(blk, intrinsics.unaligned_load((^x86.__m128i)(&ctx._sk_exp_enc[i])))
}
blk = x86._mm_aesenclast_si128(blk, intrinsics.unaligned_load((^x86.__m128i)(&ctx._sk_exp_enc[14])))
}
intrinsics.unaligned_store((^x86.__m128i)(raw_data(dst)), blk)
}
@(private, enable_target_feature = "sse2,aes")
decrypt_block_hw :: proc(ctx: ^Context_Impl_Hardware, dst, src: []byte) {
blk := intrinsics.unaligned_load((^x86.__m128i)(raw_data(src)))
blk = x86._mm_xor_si128(blk, intrinsics.unaligned_load((^x86.__m128i)(&ctx._sk_exp_dec[0])))
#unroll for i in 1 ..= 9 {
blk = x86._mm_aesdec_si128(blk, intrinsics.unaligned_load((^x86.__m128i)(&ctx._sk_exp_dec[i])))
}
switch ctx._num_rounds {
case _aes.ROUNDS_128:
blk = x86._mm_aesdeclast_si128(blk, intrinsics.unaligned_load((^x86.__m128i)(&ctx._sk_exp_dec[10])))
case _aes.ROUNDS_192:
#unroll for i in 10 ..= 11 {
blk = x86._mm_aesdec_si128(blk, intrinsics.unaligned_load((^x86.__m128i)(&ctx._sk_exp_dec[i])))
}
blk = x86._mm_aesdeclast_si128(blk, intrinsics.unaligned_load((^x86.__m128i)(&ctx._sk_exp_dec[12])))
case _aes.ROUNDS_256:
#unroll for i in 10 ..= 13 {
blk = x86._mm_aesdec_si128(blk, intrinsics.unaligned_load((^x86.__m128i)(&ctx._sk_exp_dec[i])))
}
blk = x86._mm_aesdeclast_si128(blk, intrinsics.unaligned_load((^x86.__m128i)(&ctx._sk_exp_dec[14])))
}
intrinsics.unaligned_store((^x86.__m128i)(raw_data(dst)), blk)
}
+45 -29
View File
@@ -1,13 +1,16 @@
package aes package aes
import "core:bytes"
import "core:crypto" import "core:crypto"
import "core:crypto/_aes" import "core:crypto/_aes"
import "core:crypto/_aes/ct64" import "core:crypto/_aes/ct64"
import "core:encoding/endian" import "core:encoding/endian"
import "core:mem" import "core:mem"
// GCM_NONCE_SIZE is the size of the GCM nonce in bytes. // GCM_NONCE_SIZE is the default size of the GCM nonce in bytes.
GCM_NONCE_SIZE :: 12 GCM_NONCE_SIZE :: 12
// GCM_NONCE_SIZE_MAX is the maximum size of the GCM nonce in bytes.
GCM_NONCE_SIZE_MAX :: 0x2000000000000000 // floor((2^64 - 1) / 8) bits
// GCM_TAG_SIZE is the size of a GCM tag in bytes. // GCM_TAG_SIZE is the size of a GCM tag in bytes.
GCM_TAG_SIZE :: _aes.GHASH_TAG_SIZE GCM_TAG_SIZE :: _aes.GHASH_TAG_SIZE
@@ -39,6 +42,9 @@ seal_gcm :: proc(ctx: ^Context_GCM, dst, tag, nonce, aad, plaintext: []byte) {
if len(dst) != len(plaintext) { if len(dst) != len(plaintext) {
panic("crypto/aes: invalid destination ciphertext size") panic("crypto/aes: invalid destination ciphertext size")
} }
if bytes.alias_inexactly(dst, plaintext) {
panic("crypto/aes: dst and plaintext alias inexactly")
}
if impl, is_hw := ctx._impl.(Context_Impl_Hardware); is_hw { if impl, is_hw := ctx._impl.(Context_Impl_Hardware); is_hw {
gcm_seal_hw(&impl, dst, tag, nonce, aad, plaintext) gcm_seal_hw(&impl, dst, tag, nonce, aad, plaintext)
@@ -47,17 +53,19 @@ seal_gcm :: proc(ctx: ^Context_GCM, dst, tag, nonce, aad, plaintext: []byte) {
h: [_aes.GHASH_KEY_SIZE]byte h: [_aes.GHASH_KEY_SIZE]byte
j0: [_aes.GHASH_BLOCK_SIZE]byte j0: [_aes.GHASH_BLOCK_SIZE]byte
j0_enc: [_aes.GHASH_BLOCK_SIZE]byte
s: [_aes.GHASH_TAG_SIZE]byte s: [_aes.GHASH_TAG_SIZE]byte
init_ghash_ct64(ctx, &h, &j0, nonce) init_ghash_ct64(ctx, &h, &j0, &j0_enc, nonce)
// Note: Our GHASH implementation handles appending padding. // Note: Our GHASH implementation handles appending padding.
ct64.ghash(s[:], h[:], aad) ct64.ghash(s[:], h[:], aad)
gctr_ct64(ctx, dst, &s, plaintext, &h, nonce, true) gctr_ct64(ctx, dst, &s, plaintext, &h, &j0, true)
final_ghash_ct64(&s, &h, &j0, len(aad), len(plaintext)) final_ghash_ct64(&s, &h, &j0_enc, len(aad), len(plaintext))
copy(tag, s[:]) copy(tag, s[:])
mem.zero_explicit(&h, len(h)) mem.zero_explicit(&h, len(h))
mem.zero_explicit(&j0, len(j0)) mem.zero_explicit(&j0, len(j0))
mem.zero_explicit(&j0_enc, len(j0_enc))
} }
// open_gcm authenticates the aad and ciphertext, and decrypts the ciphertext, // open_gcm authenticates the aad and ciphertext, and decrypts the ciphertext,
@@ -73,6 +81,9 @@ open_gcm :: proc(ctx: ^Context_GCM, dst, nonce, aad, ciphertext, tag: []byte) ->
if len(dst) != len(ciphertext) { if len(dst) != len(ciphertext) {
panic("crypto/aes: invalid destination plaintext size") panic("crypto/aes: invalid destination plaintext size")
} }
if bytes.alias_inexactly(dst, ciphertext) {
panic("crypto/aes: dst and ciphertext alias inexactly")
}
if impl, is_hw := ctx._impl.(Context_Impl_Hardware); is_hw { if impl, is_hw := ctx._impl.(Context_Impl_Hardware); is_hw {
return gcm_open_hw(&impl, dst, nonce, aad, ciphertext, tag) return gcm_open_hw(&impl, dst, nonce, aad, ciphertext, tag)
@@ -80,12 +91,13 @@ open_gcm :: proc(ctx: ^Context_GCM, dst, nonce, aad, ciphertext, tag: []byte) ->
h: [_aes.GHASH_KEY_SIZE]byte h: [_aes.GHASH_KEY_SIZE]byte
j0: [_aes.GHASH_BLOCK_SIZE]byte j0: [_aes.GHASH_BLOCK_SIZE]byte
j0_enc: [_aes.GHASH_BLOCK_SIZE]byte
s: [_aes.GHASH_TAG_SIZE]byte s: [_aes.GHASH_TAG_SIZE]byte
init_ghash_ct64(ctx, &h, &j0, nonce) init_ghash_ct64(ctx, &h, &j0, &j0_enc, nonce)
ct64.ghash(s[:], h[:], aad) ct64.ghash(s[:], h[:], aad)
gctr_ct64(ctx, dst, &s, ciphertext, &h, nonce, false) gctr_ct64(ctx, dst, &s, ciphertext, &h, &j0, false)
final_ghash_ct64(&s, &h, &j0, len(aad), len(ciphertext)) final_ghash_ct64(&s, &h, &j0_enc, len(aad), len(ciphertext))
ok := crypto.compare_constant_time(s[:], tag) == 1 ok := crypto.compare_constant_time(s[:], tag) == 1
if !ok { if !ok {
@@ -94,6 +106,7 @@ open_gcm :: proc(ctx: ^Context_GCM, dst, nonce, aad, ciphertext, tag: []byte) ->
mem.zero_explicit(&h, len(h)) mem.zero_explicit(&h, len(h))
mem.zero_explicit(&j0, len(j0)) mem.zero_explicit(&j0, len(j0))
mem.zero_explicit(&j0_enc, len(j0_enc))
mem.zero_explicit(&s, len(s)) mem.zero_explicit(&s, len(s))
return ok return ok
@@ -106,19 +119,14 @@ reset_gcm :: proc "contextless" (ctx: ^Context_GCM) {
ctx._is_initialized = false ctx._is_initialized = false
} }
@(private) @(private = "file")
gcm_validate_common_slice_sizes :: proc(tag, nonce, aad, text: []byte) { gcm_validate_common_slice_sizes :: proc(tag, nonce, aad, text: []byte) {
if len(tag) != GCM_TAG_SIZE { if len(tag) != GCM_TAG_SIZE {
panic("crypto/aes: invalid GCM tag size") panic("crypto/aes: invalid GCM tag size")
} }
// The specification supports nonces in the range [1, 2^64) bits // The specification supports nonces in the range [1, 2^64) bits.
// however per NIST SP 800-38D 5.2.1.1: if l := len(nonce); l == 0 || u64(l) >= GCM_NONCE_SIZE_MAX {
//
// > For IVs, it is recommended that implementations restrict support
// > to the length of 96 bits, to promote interoperability, efficiency,
// > and simplicity of design.
if len(nonce) != GCM_NONCE_SIZE {
panic("crypto/aes: invalid GCM nonce size") panic("crypto/aes: invalid GCM nonce size")
} }
@@ -135,6 +143,7 @@ init_ghash_ct64 :: proc(
ctx: ^Context_GCM, ctx: ^Context_GCM,
h: ^[_aes.GHASH_KEY_SIZE]byte, h: ^[_aes.GHASH_KEY_SIZE]byte,
j0: ^[_aes.GHASH_BLOCK_SIZE]byte, j0: ^[_aes.GHASH_BLOCK_SIZE]byte,
j0_enc: ^[_aes.GHASH_BLOCK_SIZE]byte,
nonce: []byte, nonce: []byte,
) { ) {
impl := &ctx._impl.(ct64.Context) impl := &ctx._impl.(ct64.Context)
@@ -142,12 +151,25 @@ init_ghash_ct64 :: proc(
// 1. Let H = CIPH(k, 0^128) // 1. Let H = CIPH(k, 0^128)
ct64.encrypt_block(impl, h[:], h[:]) ct64.encrypt_block(impl, h[:], h[:])
// Define a block, J0, as follows:
if l := len(nonce); l == GCM_NONCE_SIZE {
// if len(IV) = 96, then let J0 = IV || 0^31 || 1
copy(j0[:], nonce)
j0[_aes.GHASH_BLOCK_SIZE - 1] = 1
} else {
// If len(IV) != 96, then let s = 128 ceil(len(IV)/128) - len(IV),
// and let J0 = GHASHH(IV || 0^(s+64) || ceil(len(IV))^64).
ct64.ghash(j0[:], h[:], nonce)
tmp: [_aes.GHASH_BLOCK_SIZE]byte
endian.unchecked_put_u64be(tmp[8:], u64(l) * 8)
ct64.ghash(j0[:], h[:], tmp[:])
}
// ECB encrypt j0, so that we can just XOR with the tag. In theory // ECB encrypt j0, so that we can just XOR with the tag. In theory
// this could be processed along with the final GCTR block, to // this could be processed along with the final GCTR block, to
// potentially save a call to AES-ECB, but... just use AES-NI. // potentially save a call to AES-ECB, but... just use AES-NI.
copy(j0[:], nonce) ct64.encrypt_block(impl, j0_enc[:], j0[:])
j0[_aes.GHASH_BLOCK_SIZE - 1] = 1
ct64.encrypt_block(impl, j0[:], j0[:])
} }
@(private = "file") @(private = "file")
@@ -175,33 +197,27 @@ gctr_ct64 :: proc(
s: ^[_aes.GHASH_BLOCK_SIZE]byte, s: ^[_aes.GHASH_BLOCK_SIZE]byte,
src: []byte, src: []byte,
h: ^[_aes.GHASH_KEY_SIZE]byte, h: ^[_aes.GHASH_KEY_SIZE]byte,
nonce: []byte, nonce: ^[_aes.GHASH_BLOCK_SIZE]byte,
is_seal: bool, is_seal: bool,
) { ) #no_bounds_check {
ct64_inc_ctr32 := #force_inline proc "contextless" (dst: []byte, ctr: u32) -> u32 { ct64_inc_ctr32 := #force_inline proc "contextless" (dst: []byte, ctr: u32) -> u32 {
endian.unchecked_put_u32be(dst[12:], ctr) endian.unchecked_put_u32be(dst[12:], ctr)
return ctr + 1 return ctr + 1
} }
// 2. Define a block J_0 as follows: // Setup the counter blocks.
// if len(IV) = 96, then let J0 = IV || 0^31 || 1
//
// Note: We only support 96 bit IVs.
tmp, tmp2: [ct64.STRIDE][BLOCK_SIZE]byte = ---, --- tmp, tmp2: [ct64.STRIDE][BLOCK_SIZE]byte = ---, ---
ctrs, blks: [ct64.STRIDE][]byte = ---, --- ctrs, blks: [ct64.STRIDE][]byte = ---, ---
ctr: u32 = 2 ctr := endian.unchecked_get_u32be(nonce[GCM_NONCE_SIZE:]) + 1
for i in 0 ..< ct64.STRIDE { for i in 0 ..< ct64.STRIDE {
// Setup scratch space for the keystream. // Setup scratch space for the keystream.
blks[i] = tmp2[i][:] blks[i] = tmp2[i][:]
// Pre-copy the IV to all the counter blocks. // Pre-copy the IV to all the counter blocks.
ctrs[i] = tmp[i][:] ctrs[i] = tmp[i][:]
copy(ctrs[i], nonce) copy(ctrs[i], nonce[:GCM_NONCE_SIZE])
} }
// We stitch the GCTR and GHASH operations together, so that only
// one pass over the ciphertext is required.
impl := &ctx._impl.(ct64.Context) impl := &ctx._impl.(ct64.Context)
src, dst := src, dst src, dst := src, dst
+243
View File
@@ -0,0 +1,243 @@
//+build amd64
package aes
import "base:intrinsics"
import "core:crypto"
import "core:crypto/_aes"
import "core:crypto/_aes/hw_intel"
import "core:encoding/endian"
import "core:mem"
import "core:simd/x86"
@(private)
gcm_seal_hw :: proc(ctx: ^Context_Impl_Hardware, dst, tag, nonce, aad, plaintext: []byte) {
h: [_aes.GHASH_KEY_SIZE]byte
j0: [_aes.GHASH_BLOCK_SIZE]byte
j0_enc: [_aes.GHASH_BLOCK_SIZE]byte
s: [_aes.GHASH_TAG_SIZE]byte
init_ghash_hw(ctx, &h, &j0, &j0_enc, nonce)
// Note: Our GHASH implementation handles appending padding.
hw_intel.ghash(s[:], h[:], aad)
gctr_hw(ctx, dst, &s, plaintext, &h, &j0, true)
final_ghash_hw(&s, &h, &j0_enc, len(aad), len(plaintext))
copy(tag, s[:])
mem.zero_explicit(&h, len(h))
mem.zero_explicit(&j0, len(j0))
mem.zero_explicit(&j0_enc, len(j0_enc))
}
@(private)
gcm_open_hw :: proc(ctx: ^Context_Impl_Hardware, dst, nonce, aad, ciphertext, tag: []byte) -> bool {
h: [_aes.GHASH_KEY_SIZE]byte
j0: [_aes.GHASH_BLOCK_SIZE]byte
j0_enc: [_aes.GHASH_BLOCK_SIZE]byte
s: [_aes.GHASH_TAG_SIZE]byte
init_ghash_hw(ctx, &h, &j0, &j0_enc, nonce)
hw_intel.ghash(s[:], h[:], aad)
gctr_hw(ctx, dst, &s, ciphertext, &h, &j0, false)
final_ghash_hw(&s, &h, &j0_enc, len(aad), len(ciphertext))
ok := crypto.compare_constant_time(s[:], tag) == 1
if !ok {
mem.zero_explicit(raw_data(dst), len(dst))
}
mem.zero_explicit(&h, len(h))
mem.zero_explicit(&j0, len(j0))
mem.zero_explicit(&j0_enc, len(j0_enc))
mem.zero_explicit(&s, len(s))
return ok
}
@(private = "file")
init_ghash_hw :: proc(
ctx: ^Context_Impl_Hardware,
h: ^[_aes.GHASH_KEY_SIZE]byte,
j0: ^[_aes.GHASH_BLOCK_SIZE]byte,
j0_enc: ^[_aes.GHASH_BLOCK_SIZE]byte,
nonce: []byte,
) {
// 1. Let H = CIPH(k, 0^128)
encrypt_block_hw(ctx, h[:], h[:])
// Define a block, J0, as follows:
if l := len(nonce); l == GCM_NONCE_SIZE {
// if len(IV) = 96, then let J0 = IV || 0^31 || 1
copy(j0[:], nonce)
j0[_aes.GHASH_BLOCK_SIZE - 1] = 1
} else {
// If len(IV) != 96, then let s = 128 ceil(len(IV)/128) - len(IV),
// and let J0 = GHASHH(IV || 0^(s+64) || ceil(len(IV))^64).
hw_intel.ghash(j0[:], h[:], nonce)
tmp: [_aes.GHASH_BLOCK_SIZE]byte
endian.unchecked_put_u64be(tmp[8:], u64(l) * 8)
hw_intel.ghash(j0[:], h[:], tmp[:])
}
// ECB encrypt j0, so that we can just XOR with the tag.
encrypt_block_hw(ctx, j0_enc[:], j0[:])
}
@(private = "file", enable_target_feature = "sse2")
final_ghash_hw :: proc(
s: ^[_aes.GHASH_BLOCK_SIZE]byte,
h: ^[_aes.GHASH_KEY_SIZE]byte,
j0: ^[_aes.GHASH_BLOCK_SIZE]byte,
a_len: int,
t_len: int,
) {
blk: [_aes.GHASH_BLOCK_SIZE]byte
endian.unchecked_put_u64be(blk[0:], u64(a_len) * 8)
endian.unchecked_put_u64be(blk[8:], u64(t_len) * 8)
hw_intel.ghash(s[:], h[:], blk[:])
j0_vec := intrinsics.unaligned_load((^x86.__m128i)(j0))
s_vec := intrinsics.unaligned_load((^x86.__m128i)(s))
s_vec = x86._mm_xor_si128(s_vec, j0_vec)
intrinsics.unaligned_store((^x86.__m128i)(s), s_vec)
}
@(private = "file", enable_target_feature = "sse2,sse4.1,aes")
gctr_hw :: proc(
ctx: ^Context_Impl_Hardware,
dst: []byte,
s: ^[_aes.GHASH_BLOCK_SIZE]byte,
src: []byte,
h: ^[_aes.GHASH_KEY_SIZE]byte,
nonce: ^[_aes.GHASH_BLOCK_SIZE]byte,
is_seal: bool,
) #no_bounds_check {
sks: [15]x86.__m128i = ---
for i in 0 ..= ctx._num_rounds {
sks[i] = intrinsics.unaligned_load((^x86.__m128i)(&ctx._sk_exp_enc[i]))
}
// Setup the counter block
ctr_blk := intrinsics.unaligned_load((^x86.__m128i)(nonce))
ctr := endian.unchecked_get_u32be(nonce[GCM_NONCE_SIZE:]) + 1
src, dst := src, dst
// Note: Instead of doing GHASH and CTR separately, it is more
// performant to interleave (stitch) the two operations together.
// This results in an unreadable mess, so we opt for simplicity
// as performance is adequate.
blks: [CTR_STRIDE_HW]x86.__m128i = ---
nr_blocks := len(src) / BLOCK_SIZE
for nr_blocks >= CTR_STRIDE_HW {
if !is_seal {
hw_intel.ghash(s[:], h[:], src[:CTR_STRIDE_BYTES_HW])
}
#unroll for i in 0 ..< CTR_STRIDE_HW {
blks[i], ctr = hw_inc_ctr32(&ctr_blk, ctr)
}
#unroll for i in 0 ..< CTR_STRIDE_HW {
blks[i] = x86._mm_xor_si128(blks[i], sks[0])
}
#unroll for i in 1 ..= 9 {
#unroll for j in 0 ..< CTR_STRIDE_HW {
blks[j] = x86._mm_aesenc_si128(blks[j], sks[i])
}
}
switch ctx._num_rounds {
case _aes.ROUNDS_128:
#unroll for i in 0 ..< CTR_STRIDE_HW {
blks[i] = x86._mm_aesenclast_si128(blks[i], sks[10])
}
case _aes.ROUNDS_192:
#unroll for i in 10 ..= 11 {
#unroll for j in 0 ..< CTR_STRIDE_HW {
blks[j] = x86._mm_aesenc_si128(blks[j], sks[i])
}
}
#unroll for i in 0 ..< CTR_STRIDE_HW {
blks[i] = x86._mm_aesenclast_si128(blks[i], sks[12])
}
case _aes.ROUNDS_256:
#unroll for i in 10 ..= 13 {
#unroll for j in 0 ..< CTR_STRIDE_HW {
blks[j] = x86._mm_aesenc_si128(blks[j], sks[i])
}
}
#unroll for i in 0 ..< CTR_STRIDE_HW {
blks[i] = x86._mm_aesenclast_si128(blks[i], sks[14])
}
}
xor_blocks_hw(dst, src, blks[:])
if is_seal {
hw_intel.ghash(s[:], h[:], dst[:CTR_STRIDE_BYTES_HW])
}
src = src[CTR_STRIDE_BYTES_HW:]
dst = dst[CTR_STRIDE_BYTES_HW:]
nr_blocks -= CTR_STRIDE_HW
}
// Handle the remainder.
for n := len(src); n > 0; {
l := min(n, BLOCK_SIZE)
if !is_seal {
hw_intel.ghash(s[:], h[:], src[:l])
}
blks[0], ctr = hw_inc_ctr32(&ctr_blk, ctr)
blks[0] = x86._mm_xor_si128(blks[0], sks[0])
#unroll for i in 1 ..= 9 {
blks[0] = x86._mm_aesenc_si128(blks[0], sks[i])
}
switch ctx._num_rounds {
case _aes.ROUNDS_128:
blks[0] = x86._mm_aesenclast_si128(blks[0], sks[10])
case _aes.ROUNDS_192:
#unroll for i in 10 ..= 11 {
blks[0] = x86._mm_aesenc_si128(blks[0], sks[i])
}
blks[0] = x86._mm_aesenclast_si128(blks[0], sks[12])
case _aes.ROUNDS_256:
#unroll for i in 10 ..= 13 {
blks[0] = x86._mm_aesenc_si128(blks[0], sks[i])
}
blks[0] = x86._mm_aesenclast_si128(blks[0], sks[14])
}
if l == BLOCK_SIZE {
xor_blocks_hw(dst, src, blks[:1])
} else {
blk: [BLOCK_SIZE]byte
copy(blk[:], src)
xor_blocks_hw(blk[:], blk[:], blks[:1])
copy(dst, blk[:l])
}
if is_seal {
hw_intel.ghash(s[:], h[:], dst[:l])
}
dst = dst[l:]
src = src[l:]
n -= l
}
mem.zero_explicit(&blks, size_of(blks))
mem.zero_explicit(&sks, size_of(sks))
}
// BUG: Sticking this in gctr_hw (like the other implementations) crashes
// the compiler.
//
// src/check_expr.cpp(7892): Assertion Failure: `c->curr_proc_decl->entity`
@(private = "file", enable_target_feature = "sse4.1")
hw_inc_ctr32 :: #force_inline proc "contextless" (src: ^x86.__m128i, ctr: u32) -> (x86.__m128i, u32) {
ret := x86._mm_insert_epi32(src^, i32(intrinsics.byte_swap(ctr)), 3)
return ret, ctr + 1
}
+1
View File
@@ -1,3 +1,4 @@
//+build !amd64
package aes package aes
@(private = "file") @(private = "file")
+18
View File
@@ -0,0 +1,18 @@
//+build amd64
package aes
import "core:crypto/_aes/hw_intel"
// is_hardware_accelerated returns true iff hardware accelerated AES
// is supported.
is_hardware_accelerated :: proc "contextless" () -> bool {
return hw_intel.is_supported()
}
@(private)
Context_Impl_Hardware :: hw_intel.Context
@(private, enable_target_feature = "sse2,aes")
init_impl_hw :: proc(ctx: ^Context_Impl_Hardware, key: []byte) {
hw_intel.init(ctx, key)
}
+5 -3
View File
@@ -7,6 +7,7 @@ See:
*/ */
package chacha20 package chacha20
import "core:bytes"
import "core:encoding/endian" import "core:encoding/endian"
import "core:math/bits" import "core:math/bits"
import "core:mem" import "core:mem"
@@ -121,14 +122,15 @@ seek :: proc(ctx: ^Context, block_nr: u64) {
xor_bytes :: proc(ctx: ^Context, dst, src: []byte) { xor_bytes :: proc(ctx: ^Context, dst, src: []byte) {
assert(ctx._is_initialized) assert(ctx._is_initialized)
// TODO: Enforcing that dst and src alias exactly or not at all
// is a good idea, though odd aliasing should be extremely uncommon.
src, dst := src, dst src, dst := src, dst
if dst_len := len(dst); dst_len < len(src) { if dst_len := len(dst); dst_len < len(src) {
src = src[:dst_len] src = src[:dst_len]
} }
if bytes.alias_inexactly(dst, src) {
panic("crypto/chacha20: dst and src alias inexactly")
}
for remaining := len(src); remaining > 0; { for remaining := len(src); remaining > 0; {
// Process multiple blocks at once // Process multiple blocks at once
if ctx._off == _BLOCK_SIZE { if ctx._off == _BLOCK_SIZE {
+6 -2
View File
@@ -60,7 +60,11 @@ rand_bytes :: proc (dst: []byte) {
_rand_bytes(dst) _rand_bytes(dst)
} }
// random_generator returns a `runtime.Random_Generator` backed by the
// system entropy source.
//
// Support for the system entropy source can be checked with the
// `HAS_RAND_BYTES` boolean constant.
random_generator :: proc() -> runtime.Random_Generator { random_generator :: proc() -> runtime.Random_Generator {
return { return {
procedure = proc(data: rawptr, mode: runtime.Random_Generator_Mode, p: []byte) { procedure = proc(data: rawptr, mode: runtime.Random_Generator_Mode, p: []byte) {
@@ -79,4 +83,4 @@ random_generator :: proc() -> runtime.Random_Generator {
}, },
data = nil, data = nil,
} }
} }
+3 -3
View File
@@ -8,9 +8,9 @@ HAS_RAND_BYTES :: true
@(private) @(private)
_rand_bytes :: proc(dst: []byte) { _rand_bytes :: proc(dst: []byte) {
ret := (os.Errno)(win32.BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG)) ret := os.Platform_Error(win32.BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG))
if ret != os.ERROR_NONE { if ret != nil {
switch ret { #partial switch ret {
case os.ERROR_INVALID_HANDLE: case os.ERROR_INVALID_HANDLE:
// The handle to the first parameter is invalid. // The handle to the first parameter is invalid.
// This should not happen here, since we explicitly pass nil to it // This should not happen here, since we explicitly pass nil to it
+5 -13
View File
@@ -16,15 +16,12 @@ Library :: distinct rawptr
Loads a dynamic library from the filesystem. The paramater `global_symbols` makes the symbols in the loaded Loads a dynamic library from the filesystem. The paramater `global_symbols` makes the symbols in the loaded
library available to resolve references in subsequently loaded libraries. library available to resolve references in subsequently loaded libraries.
The paramater `global_symbols` is only used for the platforms `linux`, `darwin`, `freebsd` and `openbsd`. The parameter `global_symbols` is only used for the platforms `linux`, `darwin`, `freebsd` and `openbsd`.
On `windows` this paramater is ignored. On `windows` this paramater is ignored.
The underlying behaviour is platform specific. The underlying behaviour is platform specific.
On `linux`, `darwin`, `freebsd` and `openbsd` refer to `dlopen`. On `linux`, `darwin`, `freebsd` and `openbsd` refer to `dlopen`.
On `windows` refer to `LoadLibraryW`. On `windows` refer to `LoadLibraryW`. Also temporarily needs an allocator to convert a string.
**Implicit Allocators**
`context.temp_allocator`
Example: Example:
import "core:dynlib" import "core:dynlib"
@@ -79,10 +76,7 @@ Loads the address of a procedure/variable from a dynamic library.
The underlying behaviour is platform specific. The underlying behaviour is platform specific.
On `linux`, `darwin`, `freebsd` and `openbsd` refer to `dlsym`. On `linux`, `darwin`, `freebsd` and `openbsd` refer to `dlsym`.
On `windows` refer to `GetProcAddress`. On `windows` refer to `GetProcAddress`. Also temporarily needs an allocator to convert a string.
**Implicit Allocators**
`context.temp_allocator`
Example: Example:
import "core:dynlib" import "core:dynlib"
@@ -177,9 +171,7 @@ initialize_symbols :: proc(
return count, count > 0 return count, count > 0
} }
/* // Returns an error message for the last failed procedure call.
Returns an error message for the last failed procedure call.
*/
last_error :: proc() -> string { last_error :: proc() -> string {
return _last_error() return _last_error()
} }
+1 -1
View File
@@ -16,4 +16,4 @@ _symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found
_last_error :: proc() -> string { _last_error :: proc() -> string {
return "" return ""
} }
+1 -1
View File
@@ -26,4 +26,4 @@ _symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found
_last_error :: proc() -> string { _last_error :: proc() -> string {
err := os.dlerror() err := os.dlerror()
return "unknown" if err == "" else err return "unknown" if err == "" else err
} }
+7 -9
View File
@@ -4,14 +4,12 @@ package dynlib
import win32 "core:sys/windows" import win32 "core:sys/windows"
import "core:strings" import "core:strings"
import "base:runtime"
import "core:reflect" import "core:reflect"
_load_library :: proc(path: string, global_symbols := false) -> (Library, bool) { _load_library :: proc(path: string, global_symbols := false, allocator := context.temp_allocator) -> (Library, bool) {
// NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL // NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL
wide_path := win32.utf8_to_wstring(path, allocator)
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() defer free(wide_path, allocator)
wide_path := win32.utf8_to_wstring(path, context.temp_allocator)
handle := cast(Library)win32.LoadLibraryW(wide_path) handle := cast(Library)win32.LoadLibraryW(wide_path)
return handle, handle != nil return handle, handle != nil
} }
@@ -21,9 +19,9 @@ _unload_library :: proc(library: Library) -> bool {
return bool(ok) return bool(ok)
} }
_symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) { _symbol_address :: proc(library: Library, symbol: string, allocator := context.temp_allocator) -> (ptr: rawptr, found: bool) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() c_str := strings.clone_to_cstring(symbol, allocator)
c_str := strings.clone_to_cstring(symbol, context.temp_allocator) defer delete(c_str, allocator)
ptr = win32.GetProcAddress(cast(win32.HMODULE)library, c_str) ptr = win32.GetProcAddress(cast(win32.HMODULE)library, c_str)
found = ptr != nil found = ptr != nil
return return
@@ -33,4 +31,4 @@ _last_error :: proc() -> string {
err := win32.System_Error(win32.GetLastError()) err := win32.System_Error(win32.GetLastError())
err_msg := reflect.enum_string(err) err_msg := reflect.enum_string(err)
return "unknown" if err_msg == "" else err_msg return "unknown" if err_msg == "" else err_msg
} }
+33 -5
View File
@@ -77,8 +77,11 @@ You can look at the default tags provided for pointers on how these implementati
Example: Example:
package main package main
import "base:intrinsics"
import "core:encoding/cbor" import "core:encoding/cbor"
import "core:fmt" import "core:fmt"
import "core:reflect"
import "core:time" import "core:time"
Possibilities :: union { Possibilities :: union {
@@ -93,9 +96,32 @@ Example:
ignore_this: ^Data `cbor:"-"`, // Ignored by implementation. ignore_this: ^Data `cbor:"-"`, // Ignored by implementation.
renamed: f32 `cbor:"renamed :)"`, // Renamed when encoded. renamed: f32 `cbor:"renamed :)"`, // Renamed when encoded.
my_union: Possibilities, // Union support. my_union: Possibilities, // Union support.
my_raw: [8]u32 `cbor_tag:"raw"`, // Custom tag that just writes the value as bytes.
} }
main :: proc() { main :: proc() {
// Example custom tag implementation that instead of breaking down all parts,
// just writes the value as a big byte blob. This is an advanced feature but very powerful.
RAW_TAG_NR :: 200
cbor.tag_register_number({
marshal = proc(_: ^cbor.Tag_Implementation, e: cbor.Encoder, v: any) -> cbor.Marshal_Error {
cbor._encode_u8(e.writer, RAW_TAG_NR, .Tag) or_return
return cbor.err_conv(cbor._encode_bytes(e, reflect.as_bytes(v)))
},
unmarshal = proc(_: ^cbor.Tag_Implementation, d: cbor.Decoder, _: cbor.Tag_Number, v: any) -> (cbor.Unmarshal_Error) {
hdr := cbor._decode_header(d.reader) or_return
maj, add := cbor._header_split(hdr)
if maj != .Bytes {
return .Bad_Tag_Value
}
bytes := cbor.err_conv(cbor._decode_bytes(d, add, maj)) or_return
intrinsics.mem_copy_non_overlapping(v.data, raw_data(bytes), len(bytes))
return nil
},
}, RAW_TAG_NR, "raw")
now := time.Time{_nsec = 1701117968 * 1e9} now := time.Time{_nsec = 1701117968 * 1e9}
data := Data{ data := Data{
@@ -105,21 +131,22 @@ Example:
ignore_this = &Data{}, ignore_this = &Data{},
renamed = 123123.125, renamed = 123123.125,
my_union = 3, my_union = 3,
my_raw = {1=1, 2=2, 3=3},
} }
// Marshal the struct into binary CBOR. // Marshal the struct into binary CBOR.
binary, err := cbor.marshal(data, cbor.ENCODE_FULLY_DETERMINISTIC) binary, err := cbor.marshal(data, cbor.ENCODE_FULLY_DETERMINISTIC)
assert(err == nil) fmt.assertf(err == nil, "marshal error: %v", err)
defer delete(binary) defer delete(binary)
// Decode the binary data into a `cbor.Value`. // Decode the binary data into a `cbor.Value`.
decoded, derr := cbor.decode(string(binary)) decoded, derr := cbor.decode(string(binary))
assert(derr == nil) fmt.assertf(derr == nil, "decode error: %v", derr)
defer cbor.destroy(decoded) defer cbor.destroy(decoded)
// Turn the CBOR into a human readable representation defined as the diagnostic format in [[RFC 8949 Section 8;https://www.rfc-editor.org/rfc/rfc8949.html#name-diagnostic-notation]]. // Turn the CBOR into a human readable representation defined as the diagnostic format in [[RFC 8949 Section 8;https://www.rfc-editor.org/rfc/rfc8949.html#name-diagnostic-notation]].
diagnosis, eerr := cbor.to_diagnostic_format(decoded) diagnosis, eerr := cbor.to_diagnostic_format(decoded)
assert(eerr == nil) fmt.assertf(eerr == nil, "to diagnostic error: %v", eerr)
defer delete(diagnosis) defer delete(diagnosis)
fmt.println(diagnosis) fmt.println(diagnosis)
@@ -127,6 +154,7 @@ Example:
Output: Output:
{ {
"my_raw": 200(h'00001000200030000000000000000000'),
"my_union": 1010([ "my_union": 1010([
"int", "int",
3 3
+61 -20
View File
@@ -54,7 +54,7 @@ marshal_into_bytes :: proc(v: any, flags := ENCODE_SMALL, allocator := context.a
defer if err != nil { strings.builder_destroy(&b) } defer if err != nil { strings.builder_destroy(&b) }
if err = marshal_into_builder(&b, v, flags, temp_allocator, loc=loc); err != nil { if err = marshal_into_builder(&b, v, flags, temp_allocator); err != nil {
return return
} }
@@ -63,20 +63,20 @@ marshal_into_bytes :: proc(v: any, flags := ENCODE_SMALL, allocator := context.a
// Marshals the given value into a CBOR byte stream written to the given builder. // Marshals the given value into a CBOR byte stream written to the given builder.
// See docs on the `marshal_into` proc group for more info. // See docs on the `marshal_into` proc group for more info.
marshal_into_builder :: proc(b: ^strings.Builder, v: any, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator, loc := #caller_location) -> Marshal_Error { marshal_into_builder :: proc(b: ^strings.Builder, v: any, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator) -> Marshal_Error {
return marshal_into_writer(strings.to_writer(b), v, flags, temp_allocator, loc=loc) return marshal_into_writer(strings.to_writer(b), v, flags, temp_allocator)
} }
// Marshals the given value into a CBOR byte stream written to the given writer. // Marshals the given value into a CBOR byte stream written to the given writer.
// See docs on the `marshal_into` proc group for more info. // See docs on the `marshal_into` proc group for more info.
marshal_into_writer :: proc(w: io.Writer, v: any, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator, loc := #caller_location) -> Marshal_Error { marshal_into_writer :: proc(w: io.Writer, v: any, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator) -> Marshal_Error {
encoder := Encoder{flags, w, temp_allocator} encoder := Encoder{flags, w, temp_allocator}
return marshal_into_encoder(encoder, v, loc=loc) return marshal_into_encoder(encoder, v)
} }
// Marshals the given value into a CBOR byte stream written to the given encoder. // Marshals the given value into a CBOR byte stream written to the given encoder.
// See docs on the `marshal_into` proc group for more info. // See docs on the `marshal_into` proc group for more info.
marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (err: Marshal_Error) { marshal_into_encoder :: proc(e: Encoder, v: any) -> (err: Marshal_Error) {
e := e e := e
if e.temp_allocator.procedure == nil { if e.temp_allocator.procedure == nil {
@@ -97,11 +97,14 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e
return impl->marshal(e, v) return impl->marshal(e, v)
} }
ti := runtime.type_info_base(type_info_of(v.id)) ti := runtime.type_info_core(type_info_of(v.id))
a := any{v.data, ti.id} return _marshal_into_encoder(e, v, ti)
}
_marshal_into_encoder :: proc(e: Encoder, v: any, ti: ^runtime.Type_Info) -> (err: Marshal_Error) {
a := any{v.data, ti.id}
#partial switch info in ti.variant { #partial switch info in ti.variant {
case runtime.Type_Info_Named: case runtime.Type_Info_Named, runtime.Type_Info_Enum, runtime.Type_Info_Bit_Field:
unreachable() unreachable()
case runtime.Type_Info_Pointer: case runtime.Type_Info_Pointer:
@@ -223,18 +226,38 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e
} }
err_conv(_encode_u64(e, u64(info.count), .Array)) or_return err_conv(_encode_u64(e, u64(info.count), .Array)) or_return
if impl, ok := _tag_implementations_type[info.elem.id]; ok {
for i in 0..<info.count {
data := uintptr(v.data) + uintptr(i*info.elem_size)
impl->marshal(e, any{rawptr(data), info.elem.id}) or_return
}
return
}
elem_ti := runtime.type_info_core(type_info_of(info.elem.id))
for i in 0..<info.count { for i in 0..<info.count {
data := uintptr(v.data) + uintptr(i*info.elem_size) data := uintptr(v.data) + uintptr(i*info.elem_size)
marshal_into(e, any{rawptr(data), info.elem.id}) or_return _marshal_into_encoder(e, any{rawptr(data), info.elem.id}, elem_ti) or_return
} }
return return
case runtime.Type_Info_Enumerated_Array: case runtime.Type_Info_Enumerated_Array:
// index := runtime.type_info_base(info.index).variant.(runtime.Type_Info_Enum) // index := runtime.type_info_base(info.index).variant.(runtime.Type_Info_Enum)
err_conv(_encode_u64(e, u64(info.count), .Array)) or_return err_conv(_encode_u64(e, u64(info.count), .Array)) or_return
if impl, ok := _tag_implementations_type[info.elem.id]; ok {
for i in 0..<info.count {
data := uintptr(v.data) + uintptr(i*info.elem_size)
impl->marshal(e, any{rawptr(data), info.elem.id}) or_return
}
return
}
elem_ti := runtime.type_info_core(type_info_of(info.elem.id))
for i in 0..<info.count { for i in 0..<info.count {
data := uintptr(v.data) + uintptr(i*info.elem_size) data := uintptr(v.data) + uintptr(i*info.elem_size)
marshal_into(e, any{rawptr(data), info.elem.id}) or_return _marshal_into_encoder(e, any{rawptr(data), info.elem.id}, elem_ti) or_return
} }
return return
@@ -246,9 +269,19 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e
array := (^mem.Raw_Dynamic_Array)(v.data) array := (^mem.Raw_Dynamic_Array)(v.data)
err_conv(_encode_u64(e, u64(array.len), .Array)) or_return err_conv(_encode_u64(e, u64(array.len), .Array)) or_return
if impl, ok := _tag_implementations_type[info.elem.id]; ok {
for i in 0..<array.len {
data := uintptr(array.data) + uintptr(i*info.elem_size)
impl->marshal(e, any{rawptr(data), info.elem.id}) or_return
}
return
}
elem_ti := runtime.type_info_core(type_info_of(info.elem.id))
for i in 0..<array.len { for i in 0..<array.len {
data := uintptr(array.data) + uintptr(i*info.elem_size) data := uintptr(array.data) + uintptr(i*info.elem_size)
marshal_into(e, any{rawptr(data), info.elem.id}) or_return _marshal_into_encoder(e, any{rawptr(data), info.elem.id}, elem_ti) or_return
} }
return return
@@ -260,9 +293,19 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e
array := (^mem.Raw_Slice)(v.data) array := (^mem.Raw_Slice)(v.data)
err_conv(_encode_u64(e, u64(array.len), .Array)) or_return err_conv(_encode_u64(e, u64(array.len), .Array)) or_return
if impl, ok := _tag_implementations_type[info.elem.id]; ok {
for i in 0..<array.len {
data := uintptr(array.data) + uintptr(i*info.elem_size)
impl->marshal(e, any{rawptr(data), info.elem.id}) or_return
}
return
}
elem_ti := runtime.type_info_core(type_info_of(info.elem.id))
for i in 0..<array.len { for i in 0..<array.len {
data := uintptr(array.data) + uintptr(i*info.elem_size) data := uintptr(array.data) + uintptr(i*info.elem_size)
marshal_into(e, any{rawptr(data), info.elem.id}) or_return _marshal_into_encoder(e, any{rawptr(data), info.elem.id}, elem_ti) or_return
} }
return return
@@ -308,7 +351,8 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e
builder := strings.builder_from_slice(res[:]) builder := strings.builder_from_slice(res[:])
e.writer = strings.to_stream(&builder) e.writer = strings.to_stream(&builder)
assert(_encode_u64(e, u64(len(str)), .Text) == nil) err := _encode_u64(e, u64(len(str)), .Text)
assert(err == nil)
res[9] = u8(len(builder.buf)) res[9] = u8(len(builder.buf))
assert(res[9] < 10) assert(res[9] < 10)
return return
@@ -463,7 +507,7 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e
} }
n: u64; { n: u64; {
for _, i in info.names { for _, i in info.names[:info.field_count] {
if field_name(info, i) != "-" { if field_name(info, i) != "-" {
n += 1 n += 1
} }
@@ -479,7 +523,7 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e
entries := make([dynamic]Name, 0, n, e.temp_allocator) or_return entries := make([dynamic]Name, 0, n, e.temp_allocator) or_return
defer delete(entries) defer delete(entries)
for _, i in info.names { for _, i in info.names[:info.field_count] {
fname := field_name(info, i) fname := field_name(info, i)
if fname == "-" { if fname == "-" {
continue continue
@@ -497,7 +541,7 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e
marshal_entry(e, info, v, entry.name, entry.field) or_return marshal_entry(e, info, v, entry.name, entry.field) or_return
} }
} else { } else {
for _, i in info.names { for _, i in info.names[:info.field_count] {
fname := field_name(info, i) fname := field_name(info, i)
if fname == "-" { if fname == "-" {
continue continue
@@ -542,9 +586,6 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e
return marshal_into(e, any{v.data, vti.id}) return marshal_into(e, any{v.data, vti.id})
case runtime.Type_Info_Enum:
return marshal_into(e, any{v.data, info.base.id})
case runtime.Type_Info_Bit_Set: case runtime.Type_Info_Bit_Set:
// Store bit_set as big endian just like the protocol. // Store bit_set as big endian just like the protocol.
do_byte_swap := !reflect.bit_set_is_big_endian(v) do_byte_swap := !reflect.bit_set_is_big_endian(v)
+9 -16
View File
@@ -96,7 +96,8 @@ _unmarshal_value :: proc(d: Decoder, v: any, hdr: Header, allocator := context.a
ti = reflect.type_info_base(variant) ti = reflect.type_info_base(variant)
if !reflect.is_pointer_internally(variant) { if !reflect.is_pointer_internally(variant) {
tag := any{rawptr(uintptr(v.data) + u.tag_offset), u.tag_type.id} tag := any{rawptr(uintptr(v.data) + u.tag_offset), u.tag_type.id}
assert(_assign_int(tag, 1)) assigned := _assign_int(tag, 1)
assert(assigned)
} }
} }
} }
@@ -520,9 +521,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
return return
case reflect.Type_Info_Array: case reflect.Type_Info_Array:
_, scap := err_conv(_decode_len_container(d, add)) or_return length, _ := err_conv(_decode_len_container(d, add)) or_return
length := min(scap, t.count)
if length > t.count { if length > t.count {
return _unsupported(v, hdr) return _unsupported(v, hdr)
} }
@@ -534,9 +533,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
return return
case reflect.Type_Info_Enumerated_Array: case reflect.Type_Info_Enumerated_Array:
_, scap := err_conv(_decode_len_container(d, add)) or_return length, _ := err_conv(_decode_len_container(d, add)) or_return
length := min(scap, t.count)
if length > t.count { if length > t.count {
return _unsupported(v, hdr) return _unsupported(v, hdr)
} }
@@ -548,9 +545,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
return return
case reflect.Type_Info_Complex: case reflect.Type_Info_Complex:
_, scap := err_conv(_decode_len_container(d, add)) or_return length, _ := err_conv(_decode_len_container(d, add)) or_return
length := min(scap, 2)
if length > 2 { if length > 2 {
return _unsupported(v, hdr) return _unsupported(v, hdr)
} }
@@ -570,9 +565,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
return return
case reflect.Type_Info_Quaternion: case reflect.Type_Info_Quaternion:
_, scap := err_conv(_decode_len_container(d, add)) or_return length, _ := err_conv(_decode_len_container(d, add)) or_return
length := min(scap, 4)
if length > 4 { if length > 4 {
return _unsupported(v, hdr) return _unsupported(v, hdr)
} }
@@ -626,14 +619,14 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header,
#partial switch t in ti.variant { #partial switch t in ti.variant {
case reflect.Type_Info_Struct: case reflect.Type_Info_Struct:
if t.is_raw_union { if .raw_union in t.flags {
return _unsupported(v, hdr) return _unsupported(v, hdr)
} }
length, _ := err_conv(_decode_len_container(d, add)) or_return length, _ := err_conv(_decode_len_container(d, add)) or_return
unknown := length == -1 unknown := length == -1
fields := reflect.struct_fields_zipped(ti.id) fields := reflect.struct_fields_zipped(ti.id)
for idx := 0; idx < len(fields) && (unknown || idx < length); idx += 1 { for idx := 0; idx < len(fields) && (unknown || idx < length); idx += 1 {
// Decode key, keys can only be strings. // Decode key, keys can only be strings.
key: string key: string
@@ -646,7 +639,7 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header,
key = keyv key = keyv
} }
defer delete(key, context.temp_allocator) defer delete(key, context.temp_allocator)
// Find matching field. // Find matching field.
use_field_idx := -1 use_field_idx := -1
{ {
+3 -3
View File
@@ -38,9 +38,9 @@ iterate_csv_from_stream :: proc(filename: string) {
r.reuse_record_buffer = true // Without it you have to each of the fields within it r.reuse_record_buffer = true // Without it you have to each of the fields within it
defer csv.reader_destroy(&r) defer csv.reader_destroy(&r)
handle, errno := os.open(filename) handle, err := os.open(filename)
if errno != os.ERROR_NONE { if err != nil {
fmt.printfln("Error opening file: %v", filename) fmt.eprintfln("Error opening file: %v", filename)
return return
} }
defer os.close(handle) defer os.close(handle)
+12 -9
View File
@@ -82,15 +82,17 @@ Map :: distinct map[string]map[string]string
load_map_from_string :: proc(src: string, allocator: runtime.Allocator, options := DEFAULT_OPTIONS) -> (m: Map, err: runtime.Allocator_Error) { load_map_from_string :: proc(src: string, allocator: runtime.Allocator, options := DEFAULT_OPTIONS) -> (m: Map, err: runtime.Allocator_Error) {
unquote :: proc(val: string) -> (string, runtime.Allocator_Error) { unquote :: proc(val: string) -> (string, runtime.Allocator_Error) {
v, allocated, ok := strconv.unquote_string(val) if len(val) > 0 && (val[0] == '"' || val[0] == '\'') {
if !ok { v, allocated, ok := strconv.unquote_string(val)
return strings.clone(val) if !ok {
return strings.clone(val)
}
if allocated {
return v, nil
}
return strings.clone(v), nil
} }
if allocated { return strings.clone(val)
return v, nil
}
return strings.clone(v)
} }
context.allocator = allocator context.allocator = allocator
@@ -121,7 +123,7 @@ load_map_from_path :: proc(path: string, allocator: runtime.Allocator, options :
data := os.read_entire_file(path, allocator) or_return data := os.read_entire_file(path, allocator) or_return
defer delete(data, allocator) defer delete(data, allocator)
m, err = load_map_from_string(string(data), allocator, options) m, err = load_map_from_string(string(data), allocator, options)
ok = err != nil ok = err == nil
defer if !ok { defer if !ok {
delete_map(m) delete_map(m)
} }
@@ -142,6 +144,7 @@ delete_map :: proc(m: Map) {
delete(value, allocator) delete(value, allocator)
} }
delete(section) delete(section)
delete(pairs)
} }
delete(m) delete(m)
} }
+51 -34
View File
@@ -100,38 +100,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
case runtime.Type_Info_Integer: case runtime.Type_Info_Integer:
buf: [40]byte buf: [40]byte
u: u128 u := cast_any_int_to_u128(a)
switch i in a {
case i8: u = u128(i)
case i16: u = u128(i)
case i32: u = u128(i)
case i64: u = u128(i)
case i128: u = u128(i)
case int: u = u128(i)
case u8: u = u128(i)
case u16: u = u128(i)
case u32: u = u128(i)
case u64: u = u128(i)
case u128: u = u128(i)
case uint: u = u128(i)
case uintptr: u = u128(i)
case i16le: u = u128(i)
case i32le: u = u128(i)
case i64le: u = u128(i)
case u16le: u = u128(i)
case u32le: u = u128(i)
case u64le: u = u128(i)
case u128le: u = u128(i)
case i16be: u = u128(i)
case i32be: u = u128(i)
case i64be: u = u128(i)
case u16be: u = u128(i)
case u32be: u = u128(i)
case u64be: u = u128(i)
case u128be: u = u128(i)
}
s: string s: string
@@ -310,7 +279,12 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
case cstring: name = string(s) case cstring: name = string(s)
} }
opt_write_key(w, opt, name) or_return opt_write_key(w, opt, name) or_return
case runtime.Type_Info_Integer:
buf: [40]byte
u := cast_any_int_to_u128(ka)
name = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*kti.size, "0123456789", nil)
opt_write_key(w, opt, name) or_return
case: return .Unsupported_Type case: return .Unsupported_Type
} }
} }
@@ -406,10 +380,15 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
ti := runtime.type_info_base(type_info_of(v.id)) ti := runtime.type_info_base(type_info_of(v.id))
info := ti.variant.(runtime.Type_Info_Struct) info := ti.variant.(runtime.Type_Info_Struct)
first_iteration := true first_iteration := true
for name, i in info.names { for name, i in info.names[:info.field_count] {
omitempty := false omitempty := false
json_name, extra := json_name_from_tag_value(reflect.struct_tag_get(reflect.Struct_Tag(info.tags[i]), "json")) json_name, extra := json_name_from_tag_value(reflect.struct_tag_get(reflect.Struct_Tag(info.tags[i]), "json"))
if json_name == "-" {
continue
}
for flag in strings.split_iterator(&extra, ",") { for flag in strings.split_iterator(&extra, ",") {
switch flag { switch flag {
case "omitempty": case "omitempty":
@@ -657,3 +636,41 @@ opt_write_indentation :: proc(w: io.Writer, opt: ^Marshal_Options) -> (err: io.E
return return
} }
@(private)
cast_any_int_to_u128 :: proc(any_int_value: any) -> u128 {
u: u128 = 0
switch i in any_int_value {
case i8: u = u128(i)
case i16: u = u128(i)
case i32: u = u128(i)
case i64: u = u128(i)
case i128: u = u128(i)
case int: u = u128(i)
case u8: u = u128(i)
case u16: u = u128(i)
case u32: u = u128(i)
case u64: u = u128(i)
case u128: u = u128(i)
case uint: u = u128(i)
case uintptr: u = u128(i)
case i16le: u = u128(i)
case i32le: u = u128(i)
case i64le: u = u128(i)
case u16le: u = u128(i)
case u32le: u = u128(i)
case u64le: u = u128(i)
case u128le: u = u128(i)
case i16be: u = u128(i)
case i32be: u = u128(i)
case i64be: u = u128(i)
case u16be: u = u128(i)
case u32be: u = u128(i)
case u64be: u = u128(i)
case u128be: u = u128(i)
}
return u
}
+24 -11
View File
@@ -363,12 +363,11 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
} }
v := v v := v
v = reflect.any_base(v) ti := reflect.type_info_base(type_info_of(v.id))
ti := type_info_of(v.id)
#partial switch t in ti.variant { #partial switch t in ti.variant {
case reflect.Type_Info_Struct: case reflect.Type_Info_Struct:
if t.is_raw_union { if .raw_union in t.flags {
return UNSUPPORTED_TYPE return UNSUPPORTED_TYPE
} }
@@ -475,7 +474,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
} }
case reflect.Type_Info_Map: case reflect.Type_Info_Map:
if !reflect.is_string(t.key) { if !reflect.is_string(t.key) && !reflect.is_integer(t.key) {
return UNSUPPORTED_TYPE return UNSUPPORTED_TYPE
} }
raw_map := (^mem.Raw_Map)(v.data) raw_map := (^mem.Raw_Map)(v.data)
@@ -492,25 +491,39 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
key, _ := parse_object_key(p, p.allocator) key, _ := parse_object_key(p, p.allocator)
unmarshal_expect_token(p, .Colon) unmarshal_expect_token(p, .Colon)
mem.zero_slice(elem_backing) mem.zero_slice(elem_backing)
if uerr := unmarshal_value(p, map_backing_value); uerr != nil { if uerr := unmarshal_value(p, map_backing_value); uerr != nil {
delete(key, p.allocator) delete(key, p.allocator)
return uerr return uerr
} }
key_ptr := rawptr(&key) key_ptr: rawptr
key_cstr: cstring #partial switch tk in t.key.variant {
if reflect.is_cstring(t.key) { case runtime.Type_Info_String:
key_cstr = cstring(raw_data(key)) key_ptr = rawptr(&key)
key_ptr = &key_cstr key_cstr: cstring
if reflect.is_cstring(t.key) {
key_cstr = cstring(raw_data(key))
key_ptr = &key_cstr
}
case runtime.Type_Info_Integer:
i, ok := strconv.parse_i128(key)
if !ok { return UNSUPPORTED_TYPE }
key_ptr = rawptr(&i)
case: return UNSUPPORTED_TYPE
} }
set_ptr := runtime.__dynamic_map_set_without_hash(raw_map, t.map_info, key_ptr, map_backing_value.data) set_ptr := runtime.__dynamic_map_set_without_hash(raw_map, t.map_info, key_ptr, map_backing_value.data)
if set_ptr == nil { if set_ptr == nil {
delete(key, p.allocator) delete(key, p.allocator)
} }
// there's no need to keep string value on the heap, since it was copied into map
if reflect.is_integer(t.key) {
delete(key, p.allocator)
}
if parse_comma(p) { if parse_comma(p) {
break map_loop break map_loop
+5 -5
View File
@@ -126,7 +126,7 @@ error :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
t.error_count += 1 t.error_count += 1
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
advance_rune :: proc(t: ^Tokenizer) { advance_rune :: proc(t: ^Tokenizer) {
#no_bounds_check { #no_bounds_check {
/* /*
@@ -170,7 +170,7 @@ peek_byte :: proc(t: ^Tokenizer, offset := 0) -> byte {
return 0 return 0
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
skip_whitespace :: proc(t: ^Tokenizer) { skip_whitespace :: proc(t: ^Tokenizer) {
for { for {
switch t.ch { switch t.ch {
@@ -182,7 +182,7 @@ skip_whitespace :: proc(t: ^Tokenizer) {
} }
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
is_letter :: proc(r: rune) -> bool { is_letter :: proc(r: rune) -> bool {
if r < utf8.RUNE_SELF { if r < utf8.RUNE_SELF {
switch r { switch r {
@@ -296,7 +296,7 @@ skip_cdata :: proc(t: ^Tokenizer) -> (err: Error) {
return return
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
scan_string :: proc(t: ^Tokenizer, offset: int, close: rune = '<', consume_close := false, multiline := true) -> (value: string, err: Error) { scan_string :: proc(t: ^Tokenizer, offset: int, close: rune = '<', consume_close := false, multiline := true) -> (value: string, err: Error) {
err = .None err = .None
@@ -414,4 +414,4 @@ scan :: proc(t: ^Tokenizer, multiline_string := false) -> Token {
lit = string(t.src[offset : t.offset]) lit = string(t.src[offset : t.offset])
} }
return Token{kind, lit, pos} return Token{kind, lit, pos}
} }
+1 -1
View File
@@ -28,7 +28,7 @@ Parse_Error :: struct {
// Provides more granular information than what just a string could hold. // Provides more granular information than what just a string could hold.
Open_File_Error :: struct { Open_File_Error :: struct {
filename: string, filename: string,
errno: os.Errno, errno: os.Error,
mode: int, mode: int,
perms: int, perms: int,
} }
+5 -5
View File
@@ -12,7 +12,7 @@ import "core:reflect"
// Push a positional argument onto a data struct, checking for specified // Push a positional argument onto a data struct, checking for specified
// positionals first before adding it to a fallback field. // positionals first before adding it to a fallback field.
@(optimization_mode="size") @(optimization_mode="favor_size")
push_positional :: #force_no_inline proc (model: ^$T, parser: ^Parser, arg: string) -> (error: Error) { push_positional :: #force_no_inline proc (model: ^$T, parser: ^Parser, arg: string) -> (error: Error) {
if bit_array.get(&parser.filled_pos, parser.filled_pos.max_index) { if bit_array.get(&parser.filled_pos, parser.filled_pos.max_index) {
// The max index is set, which means we're out of space. // The max index is set, which means we're out of space.
@@ -74,7 +74,7 @@ register_field :: proc(parser: ^Parser, field: reflect.Struct_Field, index: int)
} }
// Set a `-flag` argument, Odin-style. // Set a `-flag` argument, Odin-style.
@(optimization_mode="size") @(optimization_mode="favor_size")
set_odin_flag :: proc(model: ^$T, parser: ^Parser, name: string) -> (error: Error) { set_odin_flag :: proc(model: ^$T, parser: ^Parser, name: string) -> (error: Error) {
// We make a special case for help requests. // We make a special case for help requests.
switch name { switch name {
@@ -100,7 +100,7 @@ set_odin_flag :: proc(model: ^$T, parser: ^Parser, name: string) -> (error: Erro
} }
// Set a `-flag` argument, UNIX-style. // Set a `-flag` argument, UNIX-style.
@(optimization_mode="size") @(optimization_mode="favor_size")
set_unix_flag :: proc(model: ^$T, parser: ^Parser, name: string) -> (future_args: int, error: Error) { set_unix_flag :: proc(model: ^$T, parser: ^Parser, name: string) -> (future_args: int, error: Error) {
// We make a special case for help requests. // We make a special case for help requests.
switch name { switch name {
@@ -137,7 +137,7 @@ set_unix_flag :: proc(model: ^$T, parser: ^Parser, name: string) -> (future_args
} }
// Set a `-flag:option` argument. // Set a `-flag:option` argument.
@(optimization_mode="size") @(optimization_mode="favor_size")
set_option :: proc(model: ^$T, parser: ^Parser, name, option: string) -> (error: Error) { set_option :: proc(model: ^$T, parser: ^Parser, name, option: string) -> (error: Error) {
field, index := get_field_by_name(model, name) or_return field, index := get_field_by_name(model, name) or_return
@@ -176,7 +176,7 @@ set_option :: proc(model: ^$T, parser: ^Parser, name, option: string) -> (error:
} }
// Set a `-map:key=value` argument. // Set a `-map:key=value` argument.
@(optimization_mode="size") @(optimization_mode="favor_size")
set_key_value :: proc(model: ^$T, parser: ^Parser, name, key, value: string) -> (error: Error) { set_key_value :: proc(model: ^$T, parser: ^Parser, name, key, value: string) -> (error: Error) {
field, index := get_field_by_name(model, name) or_return field, index := get_field_by_name(model, name) or_return
+6 -6
View File
@@ -13,7 +13,7 @@ import "core:strings"
@require import "core:time/datetime" @require import "core:time/datetime"
import "core:unicode/utf8" import "core:unicode/utf8"
@(optimization_mode="size") @(optimization_mode="favor_size")
parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info: ^runtime.Type_Info) -> bool { parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info: ^runtime.Type_Info) -> bool {
bounded_int :: proc(value, min, max: i128) -> (result: i128, ok: bool) { bounded_int :: proc(value, min, max: i128) -> (result: i128, ok: bool) {
return value, min <= value && value <= max return value, min <= value && value <= max
@@ -202,7 +202,7 @@ parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info:
// especially with files. // especially with files.
// //
// We want to provide as informative as an error as we can. // We want to provide as informative as an error as we can.
@(optimization_mode="size", disabled=NO_CORE_NAMED_TYPES) @(optimization_mode="favor_size", disabled=NO_CORE_NAMED_TYPES)
parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type: typeid, arg_tag: string, out_error: ^Error) { parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type: typeid, arg_tag: string, out_error: ^Error) {
// Core types currently supported: // Core types currently supported:
// //
@@ -254,8 +254,8 @@ parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type:
} }
handle, errno := os.open(str, mode, perms) handle, errno := os.open(str, mode, perms)
if errno != 0 { if errno != nil {
// NOTE(Feoramund): os.Errno is system-dependent, and there's // NOTE(Feoramund): os.Error is system-dependent, and there's
// currently no good way to translate them all into strings. // currently no good way to translate them all into strings.
// //
// The upcoming `os2` package will hopefully solve this. // The upcoming `os2` package will hopefully solve this.
@@ -320,7 +320,7 @@ parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type:
} }
} }
@(optimization_mode="size") @(optimization_mode="favor_size")
set_unbounded_integer_by_type :: proc(ptr: rawptr, value: $T, data_type: typeid) where intrinsics.type_is_integer(T) { set_unbounded_integer_by_type :: proc(ptr: rawptr, value: $T, data_type: typeid) where intrinsics.type_is_integer(T) {
switch data_type { switch data_type {
case i8: (^i8) (ptr)^ = cast(i8) value case i8: (^i8) (ptr)^ = cast(i8) value
@@ -367,7 +367,7 @@ set_unbounded_integer_by_type :: proc(ptr: rawptr, value: $T, data_type: typeid)
} }
} }
@(optimization_mode="size") @(optimization_mode="favor_size")
parse_and_set_pointer_by_type :: proc(ptr: rawptr, str: string, type_info: ^runtime.Type_Info, arg_tag: string) -> (error: Error) { parse_and_set_pointer_by_type :: proc(ptr: rawptr, str: string, type_info: ^runtime.Type_Info, arg_tag: string) -> (error: Error) {
#partial switch specific_type_info in type_info.variant { #partial switch specific_type_info in type_info.variant {
case runtime.Type_Info_Named: case runtime.Type_Info_Named:
+2 -2
View File
@@ -11,7 +11,7 @@ package flags
@require import "core:strings" @require import "core:strings"
// This proc is used to assert that `T` meets the expectations of the library. // This proc is used to assert that `T` meets the expectations of the library.
@(optimization_mode="size", disabled=ODIN_DISABLE_ASSERT) @(optimization_mode="favor_size", disabled=ODIN_DISABLE_ASSERT)
validate_structure :: proc(model_type: $T, style: Parsing_Style, loc := #caller_location) { validate_structure :: proc(model_type: $T, style: Parsing_Style, loc := #caller_location) {
positionals_assigned_so_far: bit_array.Bit_Array positionals_assigned_so_far: bit_array.Bit_Array
defer bit_array.destroy(&positionals_assigned_so_far) defer bit_array.destroy(&positionals_assigned_so_far)
@@ -162,7 +162,7 @@ validate_structure :: proc(model_type: $T, style: Parsing_Style, loc := #caller_
// Validate that all the required arguments are set and that the set arguments // Validate that all the required arguments are set and that the set arguments
// are up to the program's expectations. // are up to the program's expectations.
@(optimization_mode="size") @(optimization_mode="favor_size")
validate_arguments :: proc(model: ^$T, parser: ^Parser) -> Error { validate_arguments :: proc(model: ^$T, parser: ^Parser) -> Error {
check_fields: for field, index in reflect.struct_fields_zipped(T) { check_fields: for field, index in reflect.struct_fields_zipped(T) {
was_set := bit_array.get(&parser.fields_set, index) was_set := bit_array.get(&parser.fields_set, index)
+1 -1
View File
@@ -32,7 +32,7 @@ Inputs:
Returns: Returns:
- error: A union of errors; parsing, file open, a help request, or validation. - error: A union of errors; parsing, file open, a help request, or validation.
*/ */
@(optimization_mode="size") @(optimization_mode="favor_size")
parse :: proc( parse :: proc(
model: ^$T, model: ^$T,
args: []string, args: []string,
+1 -1
View File
@@ -17,7 +17,7 @@ Inputs:
- program: The name of the program, usually the first argument to `os.args`. - program: The name of the program, usually the first argument to `os.args`.
- style: The argument parsing style, required to show flags in the proper style. - style: The argument parsing style, required to show flags in the proper style.
*/ */
@(optimization_mode="size") @(optimization_mode="favor_size")
write_usage :: proc(out: io.Writer, data_type: typeid, program: string = "", style: Parsing_Style = .Odin) { write_usage :: proc(out: io.Writer, data_type: typeid, program: string = "", style: Parsing_Style = .Odin) {
// All flags get their tags parsed so they can be reasoned about later. // All flags get their tags parsed so they can be reasoned about later.
Flag :: struct { Flag :: struct {
+2 -2
View File
@@ -19,7 +19,7 @@ Inputs:
- allocator: (default: context.allocator) - allocator: (default: context.allocator)
- loc: The caller location for debugging purposes (default: #caller_location) - loc: The caller location for debugging purposes (default: #caller_location)
*/ */
@(optimization_mode="size") @(optimization_mode="favor_size")
parse_or_exit :: proc( parse_or_exit :: proc(
model: ^$T, model: ^$T,
program_args: []string, program_args: []string,
@@ -63,7 +63,7 @@ Inputs:
- error: The error returned from `parse`. - error: The error returned from `parse`.
- style: The argument parsing style, required to show flags in the proper style, when usage is shown. - style: The argument parsing style, required to show flags in the proper style, when usage is shown.
*/ */
@(optimization_mode="size") @(optimization_mode="favor_size")
print_errors :: proc(data_type: typeid, error: Error, program: string, style: Parsing_Style = .Odin) { print_errors :: proc(data_type: typeid, error: Error, program: string, style: Parsing_Style = .Odin) {
stderr := os.stream_from_handle(os.stderr) stderr := os.stream_from_handle(os.stderr)
stdout := os.stream_from_handle(os.stdout) stdout := os.stream_from_handle(os.stdout)
+47 -28
View File
@@ -334,6 +334,27 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
message := tprintf(fmt, ..args) message := tprintf(fmt, ..args)
p("Panic", message, loc) p("Panic", message, loc)
} }
// Creates a formatted C string
//
// *Allocates Using Context's Allocator*
//
// Inputs:
// - args: A variadic list of arguments to be formatted.
// - sep: An optional separator string (default is a single space).
//
// Returns: A formatted C string.
//
@(require_results)
caprint :: proc(args: ..any, sep := " ", allocator := context.allocator) -> cstring {
str: strings.Builder
strings.builder_init(&str, allocator)
sbprint(&str, ..args, sep=sep)
strings.write_byte(&str, 0)
s := strings.to_string(str)
return cstring(raw_data(s))
}
// Creates a formatted C string // Creates a formatted C string
// //
// *Allocates Using Context's Allocator* // *Allocates Using Context's Allocator*
@@ -346,9 +367,9 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
// Returns: A formatted C string // Returns: A formatted C string
// //
@(require_results) @(require_results)
caprintf :: proc(format: string, args: ..any, newline := false) -> cstring { caprintf :: proc(format: string, args: ..any, allocator := context.allocator, newline := false) -> cstring {
str: strings.Builder str: strings.Builder
strings.builder_init(&str) strings.builder_init(&str, allocator)
sbprintf(&str, format, ..args, newline=newline) sbprintf(&str, format, ..args, newline=newline)
strings.write_byte(&str, 0) strings.write_byte(&str, 0)
s := strings.to_string(str) s := strings.to_string(str)
@@ -365,8 +386,8 @@ caprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
// Returns: A formatted C string // Returns: A formatted C string
// //
@(require_results) @(require_results)
caprintfln :: proc(format: string, args: ..any) -> cstring { caprintfln :: proc(format: string, args: ..any, allocator := context.allocator) -> cstring {
return caprintf(format, ..args, newline=true) return caprintf(format, ..args, allocator=allocator, newline=true)
} }
// Creates a formatted C string // Creates a formatted C string
// //
@@ -380,12 +401,7 @@ caprintfln :: proc(format: string, args: ..any) -> cstring {
// //
@(require_results) @(require_results)
ctprint :: proc(args: ..any, sep := " ") -> cstring { ctprint :: proc(args: ..any, sep := " ") -> cstring {
str: strings.Builder return caprint(args=args, sep=sep, allocator=context.temp_allocator)
strings.builder_init(&str, context.temp_allocator)
sbprint(&str, ..args, sep=sep)
strings.write_byte(&str, 0)
s := strings.to_string(str)
return cstring(raw_data(s))
} }
// Creates a formatted C string // Creates a formatted C string
// //
@@ -400,12 +416,7 @@ ctprint :: proc(args: ..any, sep := " ") -> cstring {
// //
@(require_results) @(require_results)
ctprintf :: proc(format: string, args: ..any, newline := false) -> cstring { ctprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
str: strings.Builder return caprintf(format=format, args=args, allocator=context.temp_allocator, newline=newline)
strings.builder_init(&str, context.temp_allocator)
sbprintf(&str, format, ..args, newline=newline)
strings.write_byte(&str, 0)
s := strings.to_string(str)
return cstring(raw_data(s))
} }
// Creates a formatted C string, followed by a newline. // Creates a formatted C string, followed by a newline.
// //
@@ -419,7 +430,7 @@ ctprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
// //
@(require_results) @(require_results)
ctprintfln :: proc(format: string, args: ..any) -> cstring { ctprintfln :: proc(format: string, args: ..any) -> cstring {
return ctprintf(format, ..args, newline=true) return caprintf(format=format, args=args, allocator=context.temp_allocator, newline=true)
} }
// Formats using the default print settings and writes to the given strings.Builder // Formats using the default print settings and writes to the given strings.Builder
// //
@@ -951,10 +962,10 @@ fmt_bad_verb :: proc(fi: ^Info, verb: rune) {
io.write_string(fi.writer, "%!", &fi.n) io.write_string(fi.writer, "%!", &fi.n)
io.write_rune(fi.writer, verb, &fi.n) io.write_rune(fi.writer, verb, &fi.n)
io.write_byte(fi.writer, '(', &fi.n) io.write_byte(fi.writer, '(', &fi.n)
if fi.arg.id != nil { if arg := fi.arg; arg != nil {
reflect.write_typeid(fi.writer, fi.arg.id, &fi.n) reflect.write_typeid(fi.writer, arg.id, &fi.n)
io.write_byte(fi.writer, '=', &fi.n) io.write_byte(fi.writer, '=', &fi.n)
fmt_value(fi, fi.arg, 'v') fmt_value(fi, arg, 'v')
} else { } else {
io.write_string(fi.writer, "<nil>", &fi.n) io.write_string(fi.writer, "<nil>", &fi.n)
} }
@@ -1861,7 +1872,7 @@ handle_tag :: proc(state: ^Info_State, data: rawptr, info: reflect.Type_Info_Str
if optional_len == nil { if optional_len == nil {
return return
} }
for f, i in info.names { for f, i in info.names[:info.field_count] {
if f != field_name { if f != field_name {
continue continue
} }
@@ -1965,7 +1976,7 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St
fmt_bad_verb(fi, the_verb) fmt_bad_verb(fi, the_verb)
return return
} }
if info.is_raw_union { if .raw_union in info.flags {
if type_name == "" { if type_name == "" {
io.write_string(fi.writer, "(raw union)", &fi.n) io.write_string(fi.writer, "(raw union)", &fi.n)
} else { } else {
@@ -1989,7 +2000,7 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St
// fi.hash = false; // fi.hash = false;
fi.indent += 1 fi.indent += 1
is_empty := len(info.names) == 0 is_empty := info.field_count == 0
if !is_soa && hash && !is_empty { if !is_soa && hash && !is_empty {
io.write_byte(fi.writer, '\n', &fi.n) io.write_byte(fi.writer, '\n', &fi.n)
@@ -2010,17 +2021,17 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St
base_type_name = v.name base_type_name = v.name
} }
actual_field_count := len(info.names) actual_field_count := info.field_count
n := uintptr(info.soa_len) n := uintptr(info.soa_len)
if info.soa_kind == .Slice { if info.soa_kind == .Slice {
actual_field_count = len(info.names)-1 // len actual_field_count = info.field_count-1 // len
n = uintptr((^int)(uintptr(v.data) + info.offsets[actual_field_count])^) n = uintptr((^int)(uintptr(v.data) + info.offsets[actual_field_count])^)
} else if info.soa_kind == .Dynamic { } else if info.soa_kind == .Dynamic {
actual_field_count = len(info.names)-3 // len, cap, allocator actual_field_count = info.field_count-3 // len, cap, allocator
n = uintptr((^int)(uintptr(v.data) + info.offsets[actual_field_count])^) n = uintptr((^int)(uintptr(v.data) + info.offsets[actual_field_count])^)
} }
@@ -2099,7 +2110,7 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St
} }
} else { } else {
field_count := -1 field_count := -1
for name, i in info.names { for name, i in info.names[:info.field_count] {
optional_len: int = -1 optional_len: int = -1
use_nul_termination: bool = false use_nul_termination: bool = false
verb := the_verb if the_verb == 'w' else 'v' verb := the_verb if the_verb == 'w' else 'v'
@@ -2605,7 +2616,7 @@ fmt_bit_field :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Bit
field_count := -1 field_count := -1
for name, i in info.names { for name, i in info.names[:info.field_count] {
field_verb := verb field_verb := verb
if handle_bit_field_tag(v.data, info, i, &field_verb) { if handle_bit_field_tag(v.data, info, i, &field_verb) {
continue continue
@@ -2751,9 +2762,11 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
elem := runtime.type_info_base(info.elem) elem := runtime.type_info_base(info.elem)
if elem != nil { if elem != nil {
if n, ok := fi.optional_len.?; ok { if n, ok := fi.optional_len.?; ok {
fi.optional_len = nil
fmt_array(fi, ptr, n, elem.size, elem, verb) fmt_array(fi, ptr, n, elem.size, elem, verb)
return return
} else if fi.use_nul_termination { } else if fi.use_nul_termination {
fi.use_nul_termination = false
fmt_array_nul_terminated(fi, ptr, -1, elem.size, elem, verb) fmt_array_nul_terminated(fi, ptr, -1, elem.size, elem, verb)
return return
} }
@@ -2855,8 +2868,10 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
n := info.count n := info.count
ptr := v.data ptr := v.data
if ol, ok := fi.optional_len.?; ok { if ol, ok := fi.optional_len.?; ok {
fi.optional_len = nil
n = min(n, ol) n = min(n, ol)
} else if fi.use_nul_termination { } else if fi.use_nul_termination {
fi.use_nul_termination = false
fmt_array_nul_terminated(fi, ptr, n, info.elem_size, info.elem, verb) fmt_array_nul_terminated(fi, ptr, n, info.elem_size, info.elem, verb)
return return
} }
@@ -2867,8 +2882,10 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
n := slice.len n := slice.len
ptr := slice.data ptr := slice.data
if ol, ok := fi.optional_len.?; ok { if ol, ok := fi.optional_len.?; ok {
fi.optional_len = nil
n = min(n, ol) n = min(n, ol)
} else if fi.use_nul_termination { } else if fi.use_nul_termination {
fi.use_nul_termination = false
fmt_array_nul_terminated(fi, ptr, n, info.elem_size, info.elem, verb) fmt_array_nul_terminated(fi, ptr, n, info.elem_size, info.elem, verb)
return return
} }
@@ -2879,8 +2896,10 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
n := array.len n := array.len
ptr := array.data ptr := array.data
if ol, ok := fi.optional_len.?; ok { if ol, ok := fi.optional_len.?; ok {
fi.optional_len = nil
n = min(n, ol) n = min(n, ol)
} else if fi.use_nul_termination { } else if fi.use_nul_termination {
fi.use_nul_termination = false
fmt_array_nul_terminated(fi, ptr, n, info.elem_size, info.elem, verb) fmt_array_nul_terminated(fi, ptr, n, info.elem_size, info.elem, verb)
return return
} }
+4 -4
View File
@@ -1,6 +1,6 @@
package hash package hash
@(optimization_mode="speed") @(optimization_mode="favor_size")
crc64_ecma_182 :: proc "contextless" (data: []byte, seed := u64(0)) -> (result: u64) #no_bounds_check { crc64_ecma_182 :: proc "contextless" (data: []byte, seed := u64(0)) -> (result: u64) #no_bounds_check {
result = seed result = seed
#no_bounds_check for b in data { #no_bounds_check for b in data {
@@ -14,7 +14,7 @@ crc64_ecma_182 :: proc "contextless" (data: []byte, seed := u64(0)) -> (result:
bit-reversed, with one's complement pre and post processing. bit-reversed, with one's complement pre and post processing.
Based on Mark Adler's v1.4 implementation in C under the ZLIB license. Based on Mark Adler's v1.4 implementation in C under the ZLIB license.
*/ */
@(optimization_mode="speed") @(optimization_mode="favor_size")
crc64_xz :: proc "contextless" (data: []byte, seed := u64(0)) -> u64 #no_bounds_check { crc64_xz :: proc "contextless" (data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
data := data data := data
result := ~u64le(seed) result := ~u64le(seed)
@@ -52,7 +52,7 @@ crc64_xz :: proc "contextless" (data: []byte, seed := u64(0)) -> u64 #no_bounds_
/* /*
Generator polynomial: x^64 + x^4 + x^3 + x + 1 Generator polynomial: x^64 + x^4 + x^3 + x + 1
*/ */
@(optimization_mode="speed") @(optimization_mode="favor_size")
crc64_iso_3306 :: proc "contextless" (data: []byte, seed := u64(0)) -> u64 #no_bounds_check { crc64_iso_3306 :: proc "contextless" (data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
result := seed result := seed
@@ -738,4 +738,4 @@ crc64_iso_3306_inverse :: proc "contextless" (data: []byte, seed := u64(0)) -> u
0x9fc0, 0x9e70, 0x9ca0, 0x9d10, 0x9fc0, 0x9e70, 0x9ca0, 0x9d10,
0x9480, 0x9530, 0x97e0, 0x9650, 0x9480, 0x9530, 0x97e0, 0x9650,
0x9240, 0x93f0, 0x9120, 0x9090, 0x9240, 0x93f0, 0x9120, 0x9090,
} }
+1 -1
View File
@@ -2,7 +2,7 @@ package hash
import "base:intrinsics" import "base:intrinsics"
@(optimization_mode="speed") @(optimization_mode="favor_size")
crc32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 #no_bounds_check { crc32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
crc := ~seed crc := ~seed
buffer := raw_data(data) buffer := raw_data(data)
+11 -11
View File
@@ -3,7 +3,7 @@ package hash
import "core:mem" import "core:mem"
import "base:intrinsics" import "base:intrinsics"
@(optimization_mode="speed") @(optimization_mode="favor_size")
adler32 :: proc "contextless" (data: []byte, seed := u32(1)) -> u32 #no_bounds_check { adler32 :: proc "contextless" (data: []byte, seed := u32(1)) -> u32 #no_bounds_check {
ADLER_CONST :: 65521 ADLER_CONST :: 65521
@@ -46,7 +46,7 @@ adler32 :: proc "contextless" (data: []byte, seed := u32(1)) -> u32 #no_bounds_c
return (u32(b) << 16) | u32(a) return (u32(b) << 16) | u32(a)
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
djb2 :: proc "contextless" (data: []byte, seed := u32(5381)) -> u32 { djb2 :: proc "contextless" (data: []byte, seed := u32(5381)) -> u32 {
hash: u32 = seed hash: u32 = seed
for b in data { for b in data {
@@ -73,7 +73,7 @@ djbx33a :: proc "contextless" (data: []byte, seed := u32(5381)) -> (result: [16]
} }
// If you have a choice, prefer fnv32a // If you have a choice, prefer fnv32a
@(optimization_mode="speed") @(optimization_mode="favor_size")
fnv32_no_a :: proc "contextless" (data: []byte, seed := u32(0x811c9dc5)) -> u32 { fnv32_no_a :: proc "contextless" (data: []byte, seed := u32(0x811c9dc5)) -> u32 {
h: u32 = seed h: u32 = seed
for b in data { for b in data {
@@ -86,7 +86,7 @@ fnv32 :: fnv32_no_a // NOTE(bill): Not a fan of these aliases but seems necessar
fnv64 :: fnv64_no_a // NOTE(bill): Not a fan of these aliases but seems necessary fnv64 :: fnv64_no_a // NOTE(bill): Not a fan of these aliases but seems necessary
// If you have a choice, prefer fnv64a // If you have a choice, prefer fnv64a
@(optimization_mode="speed") @(optimization_mode="favor_size")
fnv64_no_a :: proc "contextless" (data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 { fnv64_no_a :: proc "contextless" (data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
h: u64 = seed h: u64 = seed
for b in data { for b in data {
@@ -94,7 +94,7 @@ fnv64_no_a :: proc "contextless" (data: []byte, seed := u64(0xcbf29ce484222325))
} }
return h return h
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
fnv32a :: proc "contextless" (data: []byte, seed := u32(0x811c9dc5)) -> u32 { fnv32a :: proc "contextless" (data: []byte, seed := u32(0x811c9dc5)) -> u32 {
h: u32 = seed h: u32 = seed
for b in data { for b in data {
@@ -103,7 +103,7 @@ fnv32a :: proc "contextless" (data: []byte, seed := u32(0x811c9dc5)) -> u32 {
return h return h
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
fnv64a :: proc "contextless" (data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 { fnv64a :: proc "contextless" (data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
h: u64 = seed h: u64 = seed
for b in data { for b in data {
@@ -112,7 +112,7 @@ fnv64a :: proc "contextless" (data: []byte, seed := u64(0xcbf29ce484222325)) ->
return h return h
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
jenkins :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 { jenkins :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
hash: u32 = seed hash: u32 = seed
for b in data { for b in data {
@@ -126,7 +126,7 @@ jenkins :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
return hash return hash
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
murmur32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 { murmur32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
c1_32: u32 : 0xcc9e2d51 c1_32: u32 : 0xcc9e2d51
c2_32: u32 : 0x1b873593 c2_32: u32 : 0x1b873593
@@ -177,7 +177,7 @@ murmur32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
} }
// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L96 // See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L96
@(optimization_mode="speed") @(optimization_mode="favor_size")
murmur64a :: proc "contextless" (data: []byte, seed := u64(0x9747b28c)) -> u64 { murmur64a :: proc "contextless" (data: []byte, seed := u64(0x9747b28c)) -> u64 {
m :: 0xc6a4a7935bd1e995 m :: 0xc6a4a7935bd1e995
r :: 47 r :: 47
@@ -218,7 +218,7 @@ murmur64a :: proc "contextless" (data: []byte, seed := u64(0x9747b28c)) -> u64 {
} }
// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L140 // See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L140
@(optimization_mode="speed") @(optimization_mode="favor_size")
murmur64b :: proc "contextless" (data: []byte, seed := u64(0x9747b28c)) -> u64 { murmur64b :: proc "contextless" (data: []byte, seed := u64(0x9747b28c)) -> u64 {
m :: 0x5bd1e995 m :: 0x5bd1e995
r :: 24 r :: 24
@@ -286,7 +286,7 @@ murmur64b :: proc "contextless" (data: []byte, seed := u64(0x9747b28c)) -> u64 {
return u64(h1)<<32 | u64(h2) return u64(h1)<<32 | u64(h2)
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
sdbm :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 { sdbm :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
hash: u32 = seed hash: u32 = seed
for b in data { for b in data {
+5 -5
View File
@@ -67,17 +67,17 @@ when !XXH_DISABLE_PREFETCH {
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH_rotl32 :: #force_inline proc(x, r: u32) -> (res: u32) { XXH_rotl32 :: #force_inline proc(x, r: u32) -> (res: u32) {
return ((x << r) | (x >> (32 - r))) return ((x << r) | (x >> (32 - r)))
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH_rotl64 :: #force_inline proc(x, r: u64) -> (res: u64) { XXH_rotl64 :: #force_inline proc(x, r: u64) -> (res: u64) {
return ((x << r) | (x >> (64 - r))) return ((x << r) | (x >> (64 - r)))
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH32_read32 :: #force_inline proc(buf: []u8, alignment := Alignment.Unaligned) -> (res: u32) { XXH32_read32 :: #force_inline proc(buf: []u8, alignment := Alignment.Unaligned) -> (res: u32) {
if XXH_FORCE_MEMORY_ACCESS == 2 || alignment == .Aligned { if XXH_FORCE_MEMORY_ACCESS == 2 || alignment == .Aligned {
#no_bounds_check b := (^u32le)(&buf[0])^ #no_bounds_check b := (^u32le)(&buf[0])^
@@ -89,7 +89,7 @@ XXH32_read32 :: #force_inline proc(buf: []u8, alignment := Alignment.Unaligned)
} }
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH64_read64 :: #force_inline proc(buf: []u8, alignment := Alignment.Unaligned) -> (res: u64) { XXH64_read64 :: #force_inline proc(buf: []u8, alignment := Alignment.Unaligned) -> (res: u64) {
if XXH_FORCE_MEMORY_ACCESS == 2 || alignment == .Aligned { if XXH_FORCE_MEMORY_ACCESS == 2 || alignment == .Aligned {
#no_bounds_check b := (^u64le)(&buf[0])^ #no_bounds_check b := (^u64le)(&buf[0])^
@@ -99,4 +99,4 @@ XXH64_read64 :: #force_inline proc(buf: []u8, alignment := Alignment.Unaligned)
mem_copy(&b, raw_data(buf[:]), 8) mem_copy(&b, raw_data(buf[:]), 8)
return u64(b) return u64(b)
} }
} }
+45 -45
View File
@@ -111,13 +111,13 @@ XXH128_canonical :: struct {
@param lhs, rhs The 64-bit integers to multiply @param lhs, rhs The 64-bit integers to multiply
@return The low 64 bits of the product XOR'd by the high 64 bits. @return The low 64 bits of the product XOR'd by the high 64 bits.
*/ */
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH_mul_64_to_128_fold_64 :: #force_inline proc(lhs, rhs: xxh_u64) -> (res: xxh_u64) { XXH_mul_64_to_128_fold_64 :: #force_inline proc(lhs, rhs: xxh_u64) -> (res: xxh_u64) {
t := u128(lhs) * u128(rhs) t := u128(lhs) * u128(rhs)
return u64(t & 0xFFFFFFFFFFFFFFFF) ~ u64(t >> 64) return u64(t & 0xFFFFFFFFFFFFFFFF) ~ u64(t >> 64)
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH_xorshift_64 :: #force_inline proc(v: xxh_u64, #any_int shift: uint) -> (res: xxh_u64) { XXH_xorshift_64 :: #force_inline proc(v: xxh_u64, #any_int shift: uint) -> (res: xxh_u64) {
return v ~ (v >> shift) return v ~ (v >> shift)
} }
@@ -125,7 +125,7 @@ XXH_xorshift_64 :: #force_inline proc(v: xxh_u64, #any_int shift: uint) -> (res:
/* /*
This is a fast avalanche stage, suitable when input bits are already partially mixed This is a fast avalanche stage, suitable when input bits are already partially mixed
*/ */
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_avalanche :: #force_inline proc(h64: xxh_u64) -> (res: xxh_u64) { XXH3_avalanche :: #force_inline proc(h64: xxh_u64) -> (res: xxh_u64) {
res = XXH_xorshift_64(h64, 37) res = XXH_xorshift_64(h64, 37)
res *= 0x165667919E3779F9 res *= 0x165667919E3779F9
@@ -137,7 +137,7 @@ XXH3_avalanche :: #force_inline proc(h64: xxh_u64) -> (res: xxh_u64) {
This is a stronger avalanche, inspired by Pelle Evensen's rrmxmx This is a stronger avalanche, inspired by Pelle Evensen's rrmxmx
preferable when input has not been previously mixed preferable when input has not been previously mixed
*/ */
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_rrmxmx :: #force_inline proc(h64, length: xxh_u64) -> (res: xxh_u64) { XXH3_rrmxmx :: #force_inline proc(h64, length: xxh_u64) -> (res: xxh_u64) {
/* this mix is inspired by Pelle Evensen's rrmxmx */ /* this mix is inspired by Pelle Evensen's rrmxmx */
res = h64 res = h64
@@ -166,7 +166,7 @@ XXH3_rrmxmx :: #force_inline proc(h64, length: xxh_u64) -> (res: xxh_u64) {
fast for a _128-bit_ hash on 32-bit (it usually clears XXH64). fast for a _128-bit_ hash on 32-bit (it usually clears XXH64).
*/ */
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_len_1to3_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) { XXH3_len_1to3_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
/* A doubled version of 1to3_64b with different constants. */ /* A doubled version of 1to3_64b with different constants. */
length := len(input) length := len(input)
@@ -190,7 +190,7 @@ XXH3_len_1to3_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u6
} }
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_len_4to8_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) { XXH3_len_4to8_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
length := len(input) length := len(input)
seed := seed seed := seed
@@ -219,7 +219,7 @@ XXH3_len_4to8_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u6
} }
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_len_9to16_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) { XXH3_len_9to16_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
length := len(input) length := len(input)
@@ -261,7 +261,7 @@ XXH3_len_9to16_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u
/* /*
Assumption: `secret` size is >= XXH3_SECRET_SIZE_MIN Assumption: `secret` size is >= XXH3_SECRET_SIZE_MIN
*/ */
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_len_0to16_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) { XXH3_len_0to16_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
length := len(input) length := len(input)
@@ -279,7 +279,7 @@ XXH3_len_0to16_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u
/* /*
A bit slower than XXH3_mix16B, but handles multiply by zero better. A bit slower than XXH3_mix16B, but handles multiply by zero better.
*/ */
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH128_mix32B :: #force_inline proc(acc: xxh_u128, input_1: []u8, input_2: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) { XXH128_mix32B :: #force_inline proc(acc: xxh_u128, input_1: []u8, input_2: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
acc128 := XXH128_hash_t{ acc128 := XXH128_hash_t{
h = acc, h = acc,
@@ -293,7 +293,7 @@ XXH128_mix32B :: #force_inline proc(acc: xxh_u128, input_1: []u8, input_2: []u8,
} }
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_len_17to128_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) { XXH3_len_17to128_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
length := len(input) length := len(input)
@@ -323,7 +323,7 @@ XXH3_len_17to128_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh
unreachable() unreachable()
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_len_129to240_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) { XXH3_len_129to240_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
length := len(input) length := len(input)
@@ -379,7 +379,7 @@ XXH3_INIT_ACC :: [XXH_ACC_NB]xxh_u64{
XXH_SECRET_MERGEACCS_START :: 11 XXH_SECRET_MERGEACCS_START :: 11
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_hashLong_128b_internal :: #force_inline proc( XXH3_hashLong_128b_internal :: #force_inline proc(
input: []u8, input: []u8,
secret: []u8, secret: []u8,
@@ -407,7 +407,7 @@ XXH3_hashLong_128b_internal :: #force_inline proc(
/* /*
* It's important for performance that XXH3_hashLong is not inlined. * It's important for performance that XXH3_hashLong is not inlined.
*/ */
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_hashLong_128b_default :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: XXH3_128_hash) { XXH3_hashLong_128b_default :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: XXH3_128_hash) {
return XXH3_hashLong_128b_internal(input, XXH3_kSecret[:], XXH3_accumulate_512, XXH3_scramble_accumulator) return XXH3_hashLong_128b_internal(input, XXH3_kSecret[:], XXH3_accumulate_512, XXH3_scramble_accumulator)
} }
@@ -415,12 +415,12 @@ XXH3_hashLong_128b_default :: #force_no_inline proc(input: []u8, seed: xxh_u64,
/* /*
* It's important for performance that XXH3_hashLong is not inlined. * It's important for performance that XXH3_hashLong is not inlined.
*/ */
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_hashLong_128b_withSecret :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: XXH3_128_hash) { XXH3_hashLong_128b_withSecret :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: XXH3_128_hash) {
return XXH3_hashLong_128b_internal(input, secret, XXH3_accumulate_512, XXH3_scramble_accumulator) return XXH3_hashLong_128b_internal(input, secret, XXH3_accumulate_512, XXH3_scramble_accumulator)
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_hashLong_128b_withSeed_internal :: #force_inline proc( XXH3_hashLong_128b_withSeed_internal :: #force_inline proc(
input: []u8, seed: xxh_u64, secret: []u8, input: []u8, seed: xxh_u64, secret: []u8,
f_acc512: XXH3_accumulate_512_f, f_acc512: XXH3_accumulate_512_f,
@@ -441,14 +441,14 @@ XXH3_hashLong_128b_withSeed_internal :: #force_inline proc(
/* /*
* It's important for performance that XXH3_hashLong is not inlined. * It's important for performance that XXH3_hashLong is not inlined.
*/ */
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_hashLong_128b_withSeed :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: XXH3_128_hash) { XXH3_hashLong_128b_withSeed :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: XXH3_128_hash) {
return XXH3_hashLong_128b_withSeed_internal(input, seed, secret, XXH3_accumulate_512, XXH3_scramble_accumulator , XXH3_init_custom_secret) return XXH3_hashLong_128b_withSeed_internal(input, seed, secret, XXH3_accumulate_512, XXH3_scramble_accumulator , XXH3_init_custom_secret)
} }
XXH3_hashLong128_f :: #type proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: XXH3_128_hash) XXH3_hashLong128_f :: #type proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: XXH3_128_hash)
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_128bits_internal :: #force_inline proc( XXH3_128bits_internal :: #force_inline proc(
input: []u8, seed: xxh_u64, secret: []u8, f_hl128: XXH3_hashLong128_f) -> (res: XXH3_128_hash) { input: []u8, seed: xxh_u64, secret: []u8, f_hl128: XXH3_hashLong128_f) -> (res: XXH3_128_hash) {
@@ -474,17 +474,17 @@ XXH3_128bits_internal :: #force_inline proc(
} }
/* === Public XXH128 API === */ /* === Public XXH128 API === */
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_128_default :: proc(input: []u8) -> (hash: XXH3_128_hash) { XXH3_128_default :: proc(input: []u8) -> (hash: XXH3_128_hash) {
return XXH3_128bits_internal(input, 0, XXH3_kSecret[:], XXH3_hashLong_128b_withSeed) return XXH3_128bits_internal(input, 0, XXH3_kSecret[:], XXH3_hashLong_128b_withSeed)
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_128_with_seed :: proc(input: []u8, seed: xxh_u64) -> (hash: XXH3_128_hash) { XXH3_128_with_seed :: proc(input: []u8, seed: xxh_u64) -> (hash: XXH3_128_hash) {
return XXH3_128bits_internal(input, seed, XXH3_kSecret[:], XXH3_hashLong_128b_withSeed) return XXH3_128bits_internal(input, seed, XXH3_kSecret[:], XXH3_hashLong_128b_withSeed)
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_128_with_secret :: proc(input: []u8, secret: []u8) -> (hash: XXH3_128_hash) { XXH3_128_with_secret :: proc(input: []u8, secret: []u8) -> (hash: XXH3_128_hash) {
return XXH3_128bits_internal(input, 0, secret, XXH3_hashLong_128b_withSecret) return XXH3_128bits_internal(input, 0, secret, XXH3_hashLong_128b_withSecret)
} }
@@ -519,7 +519,7 @@ XXH3_128 :: proc { XXH3_128_default, XXH3_128_with_seed, XXH3_128_with_secret }
The XOR mixing hides individual parts of the secret and increases entropy. The XOR mixing hides individual parts of the secret and increases entropy.
This adds an extra layer of strength for custom secrets. This adds an extra layer of strength for custom secrets.
*/ */
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_len_1to3_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) { XXH3_len_1to3_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
length := u32(len(input)) length := u32(len(input))
assert(input != nil) assert(input != nil)
@@ -542,7 +542,7 @@ XXH3_len_1to3_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64
} }
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_len_4to8_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) { XXH3_len_4to8_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
length := u32(len(input)) length := u32(len(input))
assert(input != nil) assert(input != nil)
@@ -562,7 +562,7 @@ XXH3_len_4to8_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64
} }
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_len_9to16_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) { XXH3_len_9to16_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
length := u64(len(input)) length := u64(len(input))
assert(input != nil) assert(input != nil)
@@ -579,7 +579,7 @@ XXH3_len_9to16_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u6
} }
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_len_0to16_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) { XXH3_len_0to16_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
length := u64(len(input)) length := u64(len(input))
assert(input != nil) assert(input != nil)
@@ -621,7 +621,7 @@ XXH3_len_0to16_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u6
by this, although it is always a good idea to use a proper seed if you care by this, although it is always a good idea to use a proper seed if you care
about strength. about strength.
*/ */
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_mix16B :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) { XXH3_mix16B :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
input_lo := XXH64_read64(input[0:]) input_lo := XXH64_read64(input[0:])
input_hi := XXH64_read64(input[8:]) input_hi := XXH64_read64(input[8:])
@@ -632,7 +632,7 @@ XXH3_mix16B :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (
} }
/* For mid range keys, XXH3 uses a Mum-hash variant. */ /* For mid range keys, XXH3 uses a Mum-hash variant. */
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_len_17to128_64b :: proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) { XXH3_len_17to128_64b :: proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
assert(len(secret) >= XXH3_SECRET_SIZE_MIN) assert(len(secret) >= XXH3_SECRET_SIZE_MIN)
length := len(input) length := len(input)
@@ -665,7 +665,7 @@ XXH3_MIDSIZE_MAX :: 240
XXH3_MIDSIZE_STARTOFFSET :: 3 XXH3_MIDSIZE_STARTOFFSET :: 3
XXH3_MIDSIZE_LASTOFFSET :: 17 XXH3_MIDSIZE_LASTOFFSET :: 17
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_len_129to240_64b :: proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) { XXH3_len_129to240_64b :: proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
assert(len(secret) >= XXH3_SECRET_SIZE_MIN) assert(len(secret) >= XXH3_SECRET_SIZE_MIN)
length := len(input) length := len(input)
@@ -699,7 +699,7 @@ XXH_SECRET_CONSUME_RATE :: 8 /* nb of secret bytes consumed at each accumulatio
XXH_ACC_NB :: (XXH_STRIPE_LEN / size_of(xxh_u64)) XXH_ACC_NB :: (XXH_STRIPE_LEN / size_of(xxh_u64))
XXH_SECRET_LASTACC_START :: 7 /* not aligned on 8, last secret is different from acc & scrambler */ XXH_SECRET_LASTACC_START :: 7 /* not aligned on 8, last secret is different from acc & scrambler */
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH_writeLE64 :: #force_inline proc(dst: []u8, v64: u64le) { XXH_writeLE64 :: #force_inline proc(dst: []u8, v64: u64le) {
v := v64 v := v64
mem_copy(raw_data(dst), &v, size_of(v64)) mem_copy(raw_data(dst), &v, size_of(v64))
@@ -737,7 +737,7 @@ XXH3_scramble_accumulator : XXH3_scramble_accumulator_f = XXH3_scramble_accumula
XXH3_init_custom_secret : XXH3_init_custom_secret_f = XXH3_init_custom_secret_scalar XXH3_init_custom_secret : XXH3_init_custom_secret_f = XXH3_init_custom_secret_scalar
/* scalar variants - universal */ /* scalar variants - universal */
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_accumulate_512_scalar :: #force_inline proc(acc: []xxh_u64, input: []u8, secret: []u8) { XXH3_accumulate_512_scalar :: #force_inline proc(acc: []xxh_u64, input: []u8, secret: []u8) {
xacc := acc /* presumed aligned */ xacc := acc /* presumed aligned */
xinput := input /* no alignment restriction */ xinput := input /* no alignment restriction */
@@ -754,7 +754,7 @@ XXH3_accumulate_512_scalar :: #force_inline proc(acc: []xxh_u64, input: []u8, se
} }
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_scramble_accumulator_scalar :: #force_inline proc(acc: []xxh_u64, secret: []u8) { XXH3_scramble_accumulator_scalar :: #force_inline proc(acc: []xxh_u64, secret: []u8) {
xacc := acc /* presumed aligned */ xacc := acc /* presumed aligned */
xsecret := secret /* no alignment restriction */ xsecret := secret /* no alignment restriction */
@@ -771,7 +771,7 @@ XXH3_scramble_accumulator_scalar :: #force_inline proc(acc: []xxh_u64, secret: [
} }
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_init_custom_secret_scalar :: #force_inline proc(custom_secret: []u8, seed64: xxh_u64) { XXH3_init_custom_secret_scalar :: #force_inline proc(custom_secret: []u8, seed64: xxh_u64) {
#assert((XXH_SECRET_DEFAULT_SIZE & 15) == 0) #assert((XXH_SECRET_DEFAULT_SIZE & 15) == 0)
@@ -791,7 +791,7 @@ XXH_PREFETCH_DIST :: 320
* Loops over XXH3_accumulate_512(). * Loops over XXH3_accumulate_512().
* Assumption: nbStripes will not overflow the secret size * Assumption: nbStripes will not overflow the secret size
*/ */
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_accumulate :: #force_inline proc( XXH3_accumulate :: #force_inline proc(
acc: []xxh_u64, input: []u8, secret: []u8, nbStripes: uint, f_acc512: XXH3_accumulate_512_f) { acc: []xxh_u64, input: []u8, secret: []u8, nbStripes: uint, f_acc512: XXH3_accumulate_512_f) {
@@ -804,7 +804,7 @@ XXH3_accumulate :: #force_inline proc(
} }
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_hashLong_internal_loop :: #force_inline proc(acc: []xxh_u64, input: []u8, secret: []u8, XXH3_hashLong_internal_loop :: #force_inline proc(acc: []xxh_u64, input: []u8, secret: []u8,
f_acc512: XXH3_accumulate_512_f, f_scramble: XXH3_scramble_accumulator_f) { f_acc512: XXH3_accumulate_512_f, f_scramble: XXH3_scramble_accumulator_f) {
@@ -833,14 +833,14 @@ XXH3_hashLong_internal_loop :: #force_inline proc(acc: []xxh_u64, input: []u8, s
} }
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_mix2Accs :: #force_inline proc(acc: []xxh_u64, secret: []u8) -> (res: xxh_u64) { XXH3_mix2Accs :: #force_inline proc(acc: []xxh_u64, secret: []u8) -> (res: xxh_u64) {
return XXH_mul_64_to_128_fold_64( return XXH_mul_64_to_128_fold_64(
acc[0] ~ XXH64_read64(secret), acc[0] ~ XXH64_read64(secret),
acc[1] ~ XXH64_read64(secret[8:])) acc[1] ~ XXH64_read64(secret[8:]))
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_mergeAccs :: #force_inline proc(acc: []xxh_u64, secret: []u8, start: xxh_u64) -> (res: xxh_u64) { XXH3_mergeAccs :: #force_inline proc(acc: []xxh_u64, secret: []u8, start: xxh_u64) -> (res: xxh_u64) {
result64 := start result64 := start
#no_bounds_check for i := 0; i < 4; i += 1 { #no_bounds_check for i := 0; i < 4; i += 1 {
@@ -849,7 +849,7 @@ XXH3_mergeAccs :: #force_inline proc(acc: []xxh_u64, secret: []u8, start: xxh_u6
return XXH3_avalanche(result64) return XXH3_avalanche(result64)
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_hashLong_64b_internal :: #force_inline proc(input: []u8, secret: []u8, XXH3_hashLong_64b_internal :: #force_inline proc(input: []u8, secret: []u8,
f_acc512: XXH3_accumulate_512_f, f_scramble: XXH3_scramble_accumulator_f) -> (hash: xxh_u64) { f_acc512: XXH3_accumulate_512_f, f_scramble: XXH3_scramble_accumulator_f) -> (hash: xxh_u64) {
@@ -868,7 +868,7 @@ XXH3_hashLong_64b_internal :: #force_inline proc(input: []u8, secret: []u8,
/* /*
It's important for performance that XXH3_hashLong is not inlined. It's important for performance that XXH3_hashLong is not inlined.
*/ */
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_hashLong_64b_withSecret :: #force_no_inline proc(input: []u8, seed64: xxh_u64, secret: []u8) -> (hash: xxh_u64) { XXH3_hashLong_64b_withSecret :: #force_no_inline proc(input: []u8, seed64: xxh_u64, secret: []u8) -> (hash: xxh_u64) {
return XXH3_hashLong_64b_internal(input, secret, XXH3_accumulate_512, XXH3_scramble_accumulator) return XXH3_hashLong_64b_internal(input, secret, XXH3_accumulate_512, XXH3_scramble_accumulator)
} }
@@ -880,7 +880,7 @@ XXH3_hashLong_64b_withSecret :: #force_no_inline proc(input: []u8, seed64: xxh_u
This variant enforces that the compiler can detect that, This variant enforces that the compiler can detect that,
and uses this opportunity to streamline the generated code for better performance. and uses this opportunity to streamline the generated code for better performance.
*/ */
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_hashLong_64b_default :: #force_no_inline proc(input: []u8, seed64: xxh_u64, secret: []u8) -> (hash: xxh_u64) { XXH3_hashLong_64b_default :: #force_no_inline proc(input: []u8, seed64: xxh_u64, secret: []u8) -> (hash: xxh_u64) {
return XXH3_hashLong_64b_internal(input, XXH3_kSecret[:], XXH3_accumulate_512, XXH3_scramble_accumulator) return XXH3_hashLong_64b_internal(input, XXH3_kSecret[:], XXH3_accumulate_512, XXH3_scramble_accumulator)
} }
@@ -896,7 +896,7 @@ XXH3_hashLong_64b_default :: #force_no_inline proc(input: []u8, seed64: xxh_u64,
It's important for performance that XXH3_hashLong is not inlined. Not sure It's important for performance that XXH3_hashLong is not inlined. Not sure
why (uop cache maybe?), but the difference is large and easily measurable. why (uop cache maybe?), but the difference is large and easily measurable.
*/ */
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_hashLong_64b_withSeed_internal :: #force_no_inline proc( XXH3_hashLong_64b_withSeed_internal :: #force_no_inline proc(
input: []u8, input: []u8,
seed: xxh_u64, seed: xxh_u64,
@@ -916,7 +916,7 @@ XXH3_hashLong_64b_withSeed_internal :: #force_no_inline proc(
/* /*
It's important for performance that XXH3_hashLong is not inlined. It's important for performance that XXH3_hashLong is not inlined.
*/ */
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_hashLong_64b_withSeed :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (hash: xxh_u64) { XXH3_hashLong_64b_withSeed :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (hash: xxh_u64) {
return XXH3_hashLong_64b_withSeed_internal(input, seed, XXH3_accumulate_512, XXH3_scramble_accumulator, XXH3_init_custom_secret) return XXH3_hashLong_64b_withSeed_internal(input, seed, XXH3_accumulate_512, XXH3_scramble_accumulator, XXH3_init_custom_secret)
} }
@@ -924,7 +924,7 @@ XXH3_hashLong_64b_withSeed :: #force_no_inline proc(input: []u8, seed: xxh_u64,
XXH3_hashLong64_f :: #type proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: xxh_u64) XXH3_hashLong64_f :: #type proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: xxh_u64)
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_64bits_internal :: proc(input: []u8, seed: xxh_u64, secret: []u8, f_hashLong: XXH3_hashLong64_f) -> (hash: xxh_u64) { XXH3_64bits_internal :: proc(input: []u8, seed: xxh_u64, secret: []u8, f_hashLong: XXH3_hashLong64_f) -> (hash: xxh_u64) {
assert(len(secret) >= XXH3_SECRET_SIZE_MIN) assert(len(secret) >= XXH3_SECRET_SIZE_MIN)
/* /*
@@ -944,19 +944,19 @@ XXH3_64bits_internal :: proc(input: []u8, seed: xxh_u64, secret: []u8, f_hashLon
} }
/* === Public entry point === */ /* === Public entry point === */
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_64_default :: proc(input: []u8) -> (hash: xxh_u64) { XXH3_64_default :: proc(input: []u8) -> (hash: xxh_u64) {
return XXH3_64bits_internal(input, 0, XXH3_kSecret[:], XXH3_hashLong_64b_default) return XXH3_64bits_internal(input, 0, XXH3_kSecret[:], XXH3_hashLong_64b_default)
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_64_with_seed :: proc(input: []u8, seed: xxh_u64) -> (hash: xxh_u64) { XXH3_64_with_seed :: proc(input: []u8, seed: xxh_u64) -> (hash: xxh_u64) {
return XXH3_64bits_internal(input, seed, XXH3_kSecret[:], XXH3_hashLong_64b_withSeed) return XXH3_64bits_internal(input, seed, XXH3_kSecret[:], XXH3_hashLong_64b_withSeed)
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH3_64_with_secret :: proc(input, secret: []u8) -> (hash: xxh_u64) { XXH3_64_with_secret :: proc(input, secret: []u8) -> (hash: xxh_u64) {
return XXH3_64bits_internal(input, 0, secret, XXH3_hashLong_64b_withSecret) return XXH3_64bits_internal(input, 0, secret, XXH3_hashLong_64b_withSecret)
} }
XXH3_64 :: proc { XXH3_64_default, XXH3_64_with_seed, XXH3_64_with_secret } XXH3_64 :: proc { XXH3_64_default, XXH3_64_with_seed, XXH3_64_with_secret }
+5 -5
View File
@@ -40,7 +40,7 @@ XXH_PRIME32_3 :: 0xC2B2AE3D /*!< 0b11000010101100101010111000111101 */
XXH_PRIME32_4 :: 0x27D4EB2F /*!< 0b00100111110101001110101100101111 */ XXH_PRIME32_4 :: 0x27D4EB2F /*!< 0b00100111110101001110101100101111 */
XXH_PRIME32_5 :: 0x165667B1 /*!< 0b00010110010101100110011110110001 */ XXH_PRIME32_5 :: 0x165667B1 /*!< 0b00010110010101100110011110110001 */
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH32_round :: #force_inline proc(seed, input: XXH32_hash) -> (res: XXH32_hash) { XXH32_round :: #force_inline proc(seed, input: XXH32_hash) -> (res: XXH32_hash) {
seed := seed seed := seed
@@ -53,7 +53,7 @@ XXH32_round :: #force_inline proc(seed, input: XXH32_hash) -> (res: XXH32_hash)
/* /*
Mix all bits Mix all bits
*/ */
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH32_avalanche :: #force_inline proc(h32: u32) -> (res: u32) { XXH32_avalanche :: #force_inline proc(h32: u32) -> (res: u32) {
h32 := h32 h32 := h32
@@ -65,7 +65,7 @@ XXH32_avalanche :: #force_inline proc(h32: u32) -> (res: u32) {
return h32 return h32
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH32_finalize :: #force_inline proc(h32: u32, buf: []u8, alignment: Alignment) -> (res: u32) { XXH32_finalize :: #force_inline proc(h32: u32, buf: []u8, alignment: Alignment) -> (res: u32) {
process_1 :: #force_inline proc(h32: u32, buf: []u8) -> (h32_res: u32, buf_res: []u8) { process_1 :: #force_inline proc(h32: u32, buf: []u8) -> (h32_res: u32, buf_res: []u8) {
#no_bounds_check b := u32(buf[0]) #no_bounds_check b := u32(buf[0])
@@ -143,7 +143,7 @@ XXH32_finalize :: #force_inline proc(h32: u32, buf: []u8, alignment: Alignment)
unreachable() unreachable()
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH32_endian_align :: #force_inline proc(input: []u8, seed := XXH32_DEFAULT_SEED, alignment: Alignment) -> (res: XXH32_hash) { XXH32_endian_align :: #force_inline proc(input: []u8, seed := XXH32_DEFAULT_SEED, alignment: Alignment) -> (res: XXH32_hash) {
buf := input buf := input
length := len(input) length := len(input)
@@ -318,4 +318,4 @@ XXH32_canonical_from_hash :: proc(hash: XXH32_hash) -> (canonical: XXH32_canonic
XXH32_hash_from_canonical :: proc(canonical: ^XXH32_canonical) -> (hash: XXH32_hash) { XXH32_hash_from_canonical :: proc(canonical: ^XXH32_canonical) -> (hash: XXH32_hash) {
h := (^u32be)(&canonical.digest)^ h := (^u32be)(&canonical.digest)^
return XXH32_hash(h) return XXH32_hash(h)
} }
+8 -8
View File
@@ -40,7 +40,7 @@ XXH_PRIME64_3 :: 0x165667B19E3779F9 /*!< 0b0001011001010110011001111011000110011
XXH_PRIME64_4 :: 0x85EBCA77C2B2AE63 /*!< 0b1000010111101011110010100111011111000010101100101010111001100011 */ XXH_PRIME64_4 :: 0x85EBCA77C2B2AE63 /*!< 0b1000010111101011110010100111011111000010101100101010111001100011 */
XXH_PRIME64_5 :: 0x27D4EB2F165667C5 /*!< 0b0010011111010100111010110010111100010110010101100110011111000101 */ XXH_PRIME64_5 :: 0x27D4EB2F165667C5 /*!< 0b0010011111010100111010110010111100010110010101100110011111000101 */
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH64_round :: proc(acc, input: xxh_u64) -> (res: xxh_u64) { XXH64_round :: proc(acc, input: xxh_u64) -> (res: xxh_u64) {
acc := acc acc := acc
@@ -50,14 +50,14 @@ XXH64_round :: proc(acc, input: xxh_u64) -> (res: xxh_u64) {
return acc return acc
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH64_mergeRound :: proc(acc, val: xxh_u64) -> (res: xxh_u64) { XXH64_mergeRound :: proc(acc, val: xxh_u64) -> (res: xxh_u64) {
res = acc ~ XXH64_round(0, val) res = acc ~ XXH64_round(0, val)
res = res * XXH_PRIME64_1 + XXH_PRIME64_4 res = res * XXH_PRIME64_1 + XXH_PRIME64_4
return res return res
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH64_avalanche :: proc(h64: xxh_u64) -> (res: xxh_u64) { XXH64_avalanche :: proc(h64: xxh_u64) -> (res: xxh_u64) {
res = h64 res = h64
res ~= res >> 33 res ~= res >> 33
@@ -68,7 +68,7 @@ XXH64_avalanche :: proc(h64: xxh_u64) -> (res: xxh_u64) {
return res return res
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH64_finalize :: proc(h64: xxh_u64, buf: []u8, alignment: Alignment) -> (res: xxh_u64) { XXH64_finalize :: proc(h64: xxh_u64, buf: []u8, alignment: Alignment) -> (res: xxh_u64) {
buf := buf buf := buf
length := len(buf) & 31 length := len(buf) & 31
@@ -100,7 +100,7 @@ XXH64_finalize :: proc(h64: xxh_u64, buf: []u8, alignment: Alignment) -> (res: x
return XXH64_avalanche(res) return XXH64_avalanche(res)
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH64_endian_align :: proc(input: []u8, seed := XXH64_DEFAULT_SEED, alignment := Alignment.Unaligned) -> (res: xxh_u64) { XXH64_endian_align :: proc(input: []u8, seed := XXH64_DEFAULT_SEED, alignment := Alignment.Unaligned) -> (res: xxh_u64) {
buf := input buf := input
length := len(buf) length := len(buf)
@@ -191,7 +191,7 @@ XXH64_reset_state :: proc(state_ptr: ^XXH64_state, seed := XXH64_DEFAULT_SEED) -
return .None return .None
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH64_update :: proc(state: ^XXH64_state, input: []u8) -> (err: Error) { XXH64_update :: proc(state: ^XXH64_state, input: []u8) -> (err: Error) {
buf := input buf := input
length := len(buf) length := len(buf)
@@ -245,7 +245,7 @@ XXH64_update :: proc(state: ^XXH64_state, input: []u8) -> (err: Error) {
return .None return .None
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
XXH64_digest :: proc(state: ^XXH64_state) -> (res: XXH64_hash) { XXH64_digest :: proc(state: ^XXH64_state) -> (res: XXH64_hash) {
if state.total_len >= 32 { if state.total_len >= 32 {
v1 := state.v1 v1 := state.v1
@@ -292,4 +292,4 @@ XXH64_canonical_from_hash :: proc(hash: XXH64_hash) -> (canonical: XXH64_canonic
XXH64_hash_from_canonical :: proc(canonical: ^XXH64_canonical) -> (hash: XXH64_hash) { XXH64_hash_from_canonical :: proc(canonical: ^XXH64_canonical) -> (hash: XXH64_hash) {
h := (^u64be)(&canonical.digest)^ h := (^u64be)(&canonical.digest)^
return XXH64_hash(h) return XXH64_hash(h)
} }
+2 -2
View File
@@ -122,7 +122,7 @@ load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context
return img, err return img, err
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
context.allocator = allocator context.allocator = allocator
options := options options := options
@@ -743,4 +743,4 @@ destroy :: proc(img: ^Image) {
@(init, private) @(init, private)
_register :: proc() { _register :: proc() {
image.register(.BMP, load_from_bytes, destroy) image.register(.BMP, load_from_bytes, destroy)
} }
+67 -4
View File
@@ -112,7 +112,8 @@ Image_Option:
`.alpha_drop_if_present` `.alpha_drop_if_present`
If the image has an alpha channel, drop it. If the image has an alpha channel, drop it.
You may want to use `.alpha_premultiply` in this case. You may want to use `.alpha_
tiply` in this case.
NOTE: For PNG, this also skips handling of the tRNS chunk, if present, NOTE: For PNG, this also skips handling of the tRNS chunk, if present,
unless you select `alpha_premultiply`. unless you select `alpha_premultiply`.
@@ -587,6 +588,32 @@ Channel :: enum u8 {
A = 4, A = 4,
} }
// Take a slice of pixels (`[]RGBA_Pixel`, etc), and return an `Image`
// Don't call `destroy` on the resulting `Image`. Instead, delete the original `pixels` slice.
pixels_to_image :: proc(pixels: [][$N]$E, width: int, height: int) -> (img: Image, ok: bool) where E == u8 || E == u16, N >= 1 && N <= 4 {
if len(pixels) != width * height {
return {}, false
}
img.height = height
img.width = width
img.depth = 8 when E == u8 else 16
img.channels = N
s := transmute(runtime.Raw_Slice)pixels
d := runtime.Raw_Dynamic_Array{
data = s.data,
len = s.len * size_of(E) * N,
cap = s.len * size_of(E) * N,
allocator = runtime.nil_allocator(),
}
img.pixels = bytes.Buffer{
buf = transmute([dynamic]u8)d,
}
return img, true
}
// When you have an RGB(A) image, but want a particular channel. // When you have an RGB(A) image, but want a particular channel.
return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok: bool) { return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok: bool) {
// Were we actually given a valid image? // Were we actually given a valid image?
@@ -1293,6 +1320,42 @@ blend_pixel :: #force_inline proc(fg: [$N]$T, alpha: T, bg: [N]T) -> (res: [N]T)
} }
blend :: proc{blend_single_channel, blend_pixel} blend :: proc{blend_single_channel, blend_pixel}
// For all pixels of the image, multiplies R, G and B by Alpha. This is useful mainly for games rendering anti-aliased transparent sprites.
// Grayscale with alpha images are supported as well.
// Note that some image formats like QOI explicitly do NOT support premultiplied alpha, so you will end up with a non-standard file.
premultiply_alpha :: proc(img: ^Image) -> (ok: bool) {
switch {
case img.channels == 2 && img.depth == 8:
pixels := mem.slice_data_cast([]GA_Pixel, img.pixels.buf[:])
for &pixel in pixels {
pixel.r = u8(u32(pixel.r) * u32(pixel.g) / 0xFF)
}
return true
case img.channels == 2 && img.depth == 16:
pixels := mem.slice_data_cast([]GA_Pixel_16, img.pixels.buf[:])
for &pixel in pixels {
pixel.r = u16(u32(pixel.r) * u32(pixel.g) / 0xFFFF)
}
return true
case img.channels == 4 && img.depth == 8:
pixels := mem.slice_data_cast([]RGBA_Pixel, img.pixels.buf[:])
for &pixel in pixels {
pixel.r = u8(u32(pixel.r) * u32(pixel.a) / 0xFF)
pixel.g = u8(u32(pixel.g) * u32(pixel.a) / 0xFF)
pixel.b = u8(u32(pixel.b) * u32(pixel.a) / 0xFF)
}
return true
case img.channels == 4 && img.depth == 16:
pixels := mem.slice_data_cast([]RGBA_Pixel_16, img.pixels.buf[:])
for &pixel in pixels {
pixel.r = u16(u32(pixel.r) * u32(pixel.a) / 0xFFFF)
pixel.g = u16(u32(pixel.g) * u32(pixel.a) / 0xFFFF)
pixel.b = u16(u32(pixel.b) * u32(pixel.a) / 0xFFFF)
}
return true
case: return false
}
}
// Replicates grayscale values into RGB(A) 8- or 16-bit images as appropriate. // Replicates grayscale values into RGB(A) 8- or 16-bit images as appropriate.
// Returns early with `false` if already an RGB(A) image. // Returns early with `false` if already an RGB(A) image.
@@ -1376,7 +1439,7 @@ expand_grayscale :: proc(img: ^Image, allocator := context.allocator) -> (ok: bo
/* /*
Helper functions to read and write data from/to a Context, etc. Helper functions to read and write data from/to a Context, etc.
*/ */
@(optimization_mode="speed") @(optimization_mode="favor_size")
read_data :: proc(z: $C, $T: typeid) -> (res: T, err: compress.General_Error) { read_data :: proc(z: $C, $T: typeid) -> (res: T, err: compress.General_Error) {
if r, e := compress.read_data(z, T); e != .None { if r, e := compress.read_data(z, T); e != .None {
return {}, .Stream_Too_Short return {}, .Stream_Too_Short
@@ -1385,7 +1448,7 @@ read_data :: proc(z: $C, $T: typeid) -> (res: T, err: compress.General_Error) {
} }
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
read_u8 :: proc(z: $C) -> (res: u8, err: compress.General_Error) { read_u8 :: proc(z: $C) -> (res: u8, err: compress.General_Error) {
if r, e := compress.read_u8(z); e != .None { if r, e := compress.read_u8(z); e != .None {
return {}, .Stream_Too_Short return {}, .Stream_Too_Short
@@ -1405,4 +1468,4 @@ write_bytes :: proc(buf: ^bytes.Buffer, data: []u8) -> (err: compress.General_Er
return .Resize_Failed return .Resize_Failed
} }
return nil return nil
} }
+1 -1
View File
@@ -27,7 +27,7 @@ which :: proc{
which_file :: proc(path: string) -> Which_File_Type { which_file :: proc(path: string) -> Which_File_Type {
f, err := os.open(path) f, err := os.open(path)
if err != 0 { if err != nil {
return .Unknown return .Unknown
} }
header: [128]byte header: [128]byte
+1 -1
View File
@@ -213,7 +213,7 @@ write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: b
} }
fd, err := open(filename, flags, mode) fd, err := open(filename, flags, mode)
if err != 0 { if err != nil {
return false return false
} }
defer close(fd) defer close(fd)
+1 -1
View File
@@ -450,7 +450,7 @@ when false {
} }
fd, fderr := open(filename, flags, mode) fd, fderr := open(filename, flags, mode)
if fderr != 0 { if fderr != nil {
return .Cannot_Open_File return .Cannot_Open_File
} }
defer close(fd) defer close(fd)
+2 -2
View File
@@ -170,7 +170,7 @@ load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context
return img, err return img, err
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
context.allocator = allocator context.allocator = allocator
options := options options := options
@@ -373,4 +373,4 @@ qoi_hash :: #force_inline proc(pixel: RGBA_Pixel) -> (index: u8) {
@(init, private) @(init, private)
_register :: proc() { _register :: proc() {
image.register(.QOI, load_from_bytes, destroy) image.register(.QOI, load_from_bytes, destroy)
} }
-4
View File
@@ -375,10 +375,6 @@ write_at_least :: proc(w: Writer, buf: []byte, min: int) -> (n: int, err: Error)
nn, err = write(w, buf[n:]) nn, err = write(w, buf[n:])
n += nn n += nn
} }
if err == nil && n < min {
err = .Short_Write
}
return return
} }
+6 -56
View File
@@ -229,7 +229,7 @@ sqrt_complex128 :: proc "contextless" (x: complex128) -> complex128 {
} }
ln_complex32 :: proc "contextless" (x: complex32) -> complex32 { ln_complex32 :: proc "contextless" (x: complex32) -> complex32 {
return complex(math.ln(abs(x)), phase(x)) return complex32(ln_complex64(complex64(x)))
} }
ln_complex64 :: proc "contextless" (x: complex64) -> complex64 { ln_complex64 :: proc "contextless" (x: complex64) -> complex64 {
return complex(math.ln(abs(x)), phase(x)) return complex(math.ln(abs(x)), phase(x))
@@ -240,26 +240,7 @@ ln_complex128 :: proc "contextless" (x: complex128) -> complex128 {
exp_complex32 :: proc "contextless" (x: complex32) -> complex32 { exp_complex32 :: proc "contextless" (x: complex32) -> complex32 {
switch re, im := real(x), imag(x); { return complex32(exp_complex64(complex64(x)))
case math.is_inf(re, 0):
switch {
case re > 0 && im == 0:
return x
case math.is_inf(im, 0) || math.is_nan(im):
if re < 0 {
return complex(0, math.copy_sign(0, im))
} else {
return complex(math.inf_f64(1.0), math.nan_f64())
}
}
case math.is_nan(re):
if im == 0 {
return complex(math.nan_f16(), im)
}
}
r := math.exp(real(x))
s, c := math.sincos(imag(x))
return complex(r*c, r*s)
} }
exp_complex64 :: proc "contextless" (x: complex64) -> complex64 { exp_complex64 :: proc "contextless" (x: complex64) -> complex64 {
switch re, im := real(x), imag(x); { switch re, im := real(x), imag(x); {
@@ -308,37 +289,7 @@ exp_complex128 :: proc "contextless" (x: complex128) -> complex128 {
pow_complex32 :: proc "contextless" (x, y: complex32) -> complex32 { pow_complex32 :: proc "contextless" (x, y: complex32) -> complex32 {
if x == 0 { // Guaranteed also true for x == -0. return complex32(pow_complex64(complex64(x), complex64(y)))
if is_nan(y) {
return nan_complex32()
}
r, i := real(y), imag(y)
switch {
case r == 0:
return 1
case r < 0:
if i == 0 {
return complex(math.inf_f16(1), 0)
}
return inf_complex32()
case r > 0:
return 0
}
unreachable()
}
modulus := abs(x)
if modulus == 0 {
return complex(0, 0)
}
r := math.pow(modulus, real(y))
arg := phase(x)
theta := real(y) * arg
if imag(y) != 0 {
r *= math.exp(-imag(y) * arg)
theta += imag(y) * math.ln(modulus)
}
s, c := math.sincos(theta)
return complex(r*c, r*s)
} }
pow_complex64 :: proc "contextless" (x, y: complex64) -> complex64 { pow_complex64 :: proc "contextless" (x, y: complex64) -> complex64 {
if x == 0 { // Guaranteed also true for x == -0. if x == 0 { // Guaranteed also true for x == -0.
@@ -410,7 +361,7 @@ pow_complex128 :: proc "contextless" (x, y: complex128) -> complex128 {
log10_complex32 :: proc "contextless" (x: complex32) -> complex32 { log10_complex32 :: proc "contextless" (x: complex32) -> complex32 {
return math.LN10*ln(x) return complex32(log10_complex64(complex64(x)))
} }
log10_complex64 :: proc "contextless" (x: complex64) -> complex64 { log10_complex64 :: proc "contextless" (x: complex64) -> complex64 {
return math.LN10*ln(x) return math.LN10*ln(x)
@@ -421,7 +372,7 @@ log10_complex128 :: proc "contextless" (x: complex128) -> complex128 {
phase_complex32 :: proc "contextless" (x: complex32) -> f16 { phase_complex32 :: proc "contextless" (x: complex32) -> f16 {
return math.atan2(imag(x), real(x)) return f16(phase_complex64(complex64(x)))
} }
phase_complex64 :: proc "contextless" (x: complex64) -> f32 { phase_complex64 :: proc "contextless" (x: complex64) -> f32 {
return math.atan2(imag(x), real(x)) return math.atan2(imag(x), real(x))
@@ -432,8 +383,7 @@ phase_complex128 :: proc "contextless" (x: complex128) -> f64 {
rect_complex32 :: proc "contextless" (r, θ: f16) -> complex32 { rect_complex32 :: proc "contextless" (r, θ: f16) -> complex32 {
s, c := math.sincos(θ) return complex32(rect_complex64(f32(r), f32(θ)))
return complex(r*c, r*s)
} }
rect_complex64 :: proc "contextless" (r, θ: f32) -> complex64 { rect_complex64 :: proc "contextless" (r, θ: f32) -> complex64 {
s, c := math.sincos(θ) s, c := math.sincos(θ)
+3 -13
View File
@@ -61,8 +61,7 @@ atanh :: proc{
acos_complex32 :: proc "contextless" (x: complex32) -> complex32 { acos_complex32 :: proc "contextless" (x: complex32) -> complex32 {
w := asin(x) return complex32(acos_complex64(complex64(x)))
return complex(math.PI/2 - real(w), -imag(w))
} }
acos_complex64 :: proc "contextless" (x: complex64) -> complex64 { acos_complex64 :: proc "contextless" (x: complex64) -> complex64 {
w := asin(x) w := asin(x)
@@ -75,14 +74,7 @@ acos_complex128 :: proc "contextless" (x: complex128) -> complex128 {
acosh_complex32 :: proc "contextless" (x: complex32) -> complex32 { acosh_complex32 :: proc "contextless" (x: complex32) -> complex32 {
if x == 0 { return complex32(acosh_complex64(complex64(x)))
return complex(0, math.copy_sign(math.PI/2, imag(x)))
}
w := acos(x)
if imag(w) <= 0 {
return complex(-imag(w), real(w))
}
return complex(imag(w), -real(w))
} }
acosh_complex64 :: proc "contextless" (x: complex64) -> complex64 { acosh_complex64 :: proc "contextless" (x: complex64) -> complex64 {
if x == 0 { if x == 0 {
@@ -257,9 +249,7 @@ atan_complex128 :: proc "contextless" (x: complex128) -> complex128 {
} }
atanh_complex32 :: proc "contextless" (x: complex32) -> complex32 { atanh_complex32 :: proc "contextless" (x: complex32) -> complex32 {
z := complex(-imag(x), real(x)) // z = i * x return complex32(atanh_complex64(complex64(x)))
z = atan(z)
return complex(imag(z), -real(z)) // z = -i * z
} }
atanh_complex64 :: proc "contextless" (x: complex64) -> complex64 { atanh_complex64 :: proc "contextless" (x: complex64) -> complex64 {
z := complex(-imag(x), real(x)) // z = i * x z := complex(-imag(x), real(x)) // z = i * x
+2 -1
View File
@@ -275,7 +275,8 @@ to_ptr :: proc{vector_to_ptr, matrix_to_ptr}
vector_angle_between :: proc "contextless" (a, b: $V/[$N]$E) -> E { vector_angle_between :: proc "contextless" (a, b: $V/[$N]$E) -> E {
a0 := normalize0(a) a0 := normalize0(a)
b0 := normalize0(b) b0 := normalize0(b)
return math.acos(dot(a0, b0)) d := clamp(dot(a0, b0), -1, +1)
return math.acos(d)
} }
quaternion64_angle_between :: proc "contextless" (a, b: $Q/quaternion64) -> f16 { quaternion64_angle_between :: proc "contextless" (a, b: $Q/quaternion64) -> f16 {
c := normalize0(conj(a) * b) c := normalize0(conj(a) * b)
+6 -6
View File
@@ -688,7 +688,7 @@ _internal_noise_4d_unskewed_base :: proc(seed: i64, coord: Vec4) -> (value: f32)
/* /*
Utility functions Utility functions
*/ */
@(optimization_mode="speed") @(optimization_mode="favor_size")
grad_2d :: proc(seed: i64, svp: [2]i64, delta: [2]f32) -> (value: f32) { grad_2d :: proc(seed: i64, svp: [2]i64, delta: [2]f32) -> (value: f32) {
hash := seed ~ svp.x ~ svp.y hash := seed ~ svp.x ~ svp.y
hash *= HASH_MULTIPLIER hash *= HASH_MULTIPLIER
@@ -698,7 +698,7 @@ grad_2d :: proc(seed: i64, svp: [2]i64, delta: [2]f32) -> (value: f32) {
return GRADIENTS_2D[gi] * delta.x + GRADIENTS_2D[gi | 1] * delta.y return GRADIENTS_2D[gi] * delta.x + GRADIENTS_2D[gi | 1] * delta.y
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
grad_3d :: proc(seed: i64, rvp: [3]i64, delta: [3]f32) -> (value: f32) { grad_3d :: proc(seed: i64, rvp: [3]i64, delta: [3]f32) -> (value: f32) {
hash := (seed ~ rvp.x) ~ (rvp.y ~ rvp.z) hash := (seed ~ rvp.x) ~ (rvp.y ~ rvp.z)
hash *= HASH_MULTIPLIER hash *= HASH_MULTIPLIER
@@ -708,7 +708,7 @@ grad_3d :: proc(seed: i64, rvp: [3]i64, delta: [3]f32) -> (value: f32) {
return GRADIENTS_3D[gi] * delta.x + GRADIENTS_3D[gi | 1] * delta.y + GRADIENTS_3D[gi | 2] * delta.z return GRADIENTS_3D[gi] * delta.x + GRADIENTS_3D[gi | 1] * delta.y + GRADIENTS_3D[gi | 2] * delta.z
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
grad_4d :: proc(seed: i64, svp: [4]i64, delta: [4]f32) -> (value: f32) { grad_4d :: proc(seed: i64, svp: [4]i64, delta: [4]f32) -> (value: f32) {
hash := seed ~ (svp.x ~ svp.y) ~ (svp.z ~ svp.w) hash := seed ~ (svp.x ~ svp.y) ~ (svp.z ~ svp.w)
hash *= HASH_MULTIPLIER hash *= HASH_MULTIPLIER
@@ -720,13 +720,13 @@ grad_4d :: proc(seed: i64, svp: [4]i64, delta: [4]f32) -> (value: f32) {
grad :: proc {grad_2d, grad_3d, grad_4d} grad :: proc {grad_2d, grad_3d, grad_4d}
@(optimization_mode="speed") @(optimization_mode="favor_size")
fast_floor :: proc(x: f64) -> (floored: i64) { fast_floor :: proc(x: f64) -> (floored: i64) {
xi := i64(x) xi := i64(x)
return x < f64(xi) ? xi - 1 : xi return x < f64(xi) ? xi - 1 : xi
} }
@(optimization_mode="speed") @(optimization_mode="favor_size")
fast_round :: proc(x: f64) -> (rounded: i64) { fast_round :: proc(x: f64) -> (rounded: i64) {
return x < 0 ? i64(x - 0.5) : i64(x + 0.5) return x < 0 ? i64(x - 0.5) : i64(x + 0.5)
} }
+60 -60
View File
@@ -8,12 +8,12 @@ float32_uniform :: float32_range
// Triangular Distribution // Triangular Distribution
// See: http://wikipedia.org/wiki/Triangular_distribution // See: http://wikipedia.org/wiki/Triangular_distribution
@(require_results) @(require_results)
float64_triangular :: proc(lo, hi: f64, mode: Maybe(f64)) -> f64 { float64_triangular :: proc(lo, hi: f64, mode: Maybe(f64), gen := context.random_generator) -> f64 {
if hi-lo == 0 { if hi-lo == 0 {
return lo return lo
} }
lo, hi := lo, hi lo, hi := lo, hi
u := float64() u := float64(gen)
c := f64(0.5) if mode == nil else clamp((mode.?-lo) / (hi-lo), 0, 1) c := f64(0.5) if mode == nil else clamp((mode.?-lo) / (hi-lo), 0, 1)
if u > c { if u > c {
u = 1-u u = 1-u
@@ -26,12 +26,12 @@ float64_triangular :: proc(lo, hi: f64, mode: Maybe(f64)) -> f64 {
// Triangular Distribution // Triangular Distribution
// See: http://wikipedia.org/wiki/Triangular_distribution // See: http://wikipedia.org/wiki/Triangular_distribution
@(require_results) @(require_results)
float32_triangular :: proc(lo, hi: f32, mode: Maybe(f32)) -> f32 { float32_triangular :: proc(lo, hi: f32, mode: Maybe(f32), gen := context.random_generator) -> f32 {
if hi-lo == 0 { if hi-lo == 0 {
return lo return lo
} }
lo, hi := lo, hi lo, hi := lo, hi
u := float32() u := float32(gen)
c := f32(0.5) if mode == nil else clamp((mode.?-lo) / (hi-lo), 0, 1) c := f32(0.5) if mode == nil else clamp((mode.?-lo) / (hi-lo), 0, 1)
if u > c { if u > c {
u = 1-u u = 1-u
@@ -44,25 +44,25 @@ float32_triangular :: proc(lo, hi: f32, mode: Maybe(f32)) -> f32 {
// Normal/Gaussian Distribution // Normal/Gaussian Distribution
@(require_results) @(require_results)
float64_normal :: proc(mean, stddev: f64) -> f64 { float64_normal :: proc(mean, stddev: f64, gen := context.random_generator) -> f64 {
return norm_float64() * stddev + mean return norm_float64(gen) * stddev + mean
} }
// Normal/Gaussian Distribution // Normal/Gaussian Distribution
@(require_results) @(require_results)
float32_normal :: proc(mean, stddev: f32) -> f32 { float32_normal :: proc(mean, stddev: f32, gen := context.random_generator) -> f32 {
return f32(float64_normal(f64(mean), f64(stddev))) return f32(float64_normal(f64(mean), f64(stddev), gen))
} }
// Log Normal Distribution // Log Normal Distribution
@(require_results) @(require_results)
float64_log_normal :: proc(mean, stddev: f64) -> f64 { float64_log_normal :: proc(mean, stddev: f64, gen := context.random_generator) -> f64 {
return math.exp(float64_normal(mean, stddev)) return math.exp(float64_normal(mean, stddev, gen))
} }
// Log Normal Distribution // Log Normal Distribution
@(require_results) @(require_results)
float32_log_normal :: proc(mean, stddev: f32) -> f32 { float32_log_normal :: proc(mean, stddev: f32, gen := context.random_generator) -> f32 {
return f32(float64_log_normal(f64(mean), f64(stddev))) return f32(float64_log_normal(f64(mean), f64(stddev), gen))
} }
@@ -72,8 +72,8 @@ float32_log_normal :: proc(mean, stddev: f32) -> f32 {
// 0 to positive infinity if lambda > 0 // 0 to positive infinity if lambda > 0
// negative infinity to 0 if lambda <= 0 // negative infinity to 0 if lambda <= 0
@(require_results) @(require_results)
float64_exponential :: proc(lambda: f64) -> f64 { float64_exponential :: proc(lambda: f64, gen := context.random_generator) -> f64 {
return - math.ln(1 - float64()) / lambda return - math.ln(1 - float64(gen)) / lambda
} }
// Exponential Distribution // Exponential Distribution
// `lambda` is 1.0/(desired mean). It should be non-zero. // `lambda` is 1.0/(desired mean). It should be non-zero.
@@ -81,8 +81,8 @@ float64_exponential :: proc(lambda: f64) -> f64 {
// 0 to positive infinity if lambda > 0 // 0 to positive infinity if lambda > 0
// negative infinity to 0 if lambda <= 0 // negative infinity to 0 if lambda <= 0
@(require_results) @(require_results)
float32_exponential :: proc(lambda: f32) -> f32 { float32_exponential :: proc(lambda: f32, gen := context.random_generator) -> f32 {
return f32(float64_exponential(f64(lambda))) return f32(float64_exponential(f64(lambda), gen))
} }
@@ -96,7 +96,7 @@ float32_exponential :: proc(lambda: f32) -> f32 {
// //
// mean is alpha*beta, variance is math.pow(alpha*beta, 2) // mean is alpha*beta, variance is math.pow(alpha*beta, 2)
@(require_results) @(require_results)
float64_gamma :: proc(alpha, beta: f64) -> f64 { float64_gamma :: proc(alpha, beta: f64, gen := context.random_generator) -> f64 {
if alpha <= 0 || beta <= 0 { if alpha <= 0 || beta <= 0 {
panic(#procedure + ": alpha and beta must be > 0.0") panic(#procedure + ": alpha and beta must be > 0.0")
} }
@@ -112,11 +112,11 @@ float64_gamma :: proc(alpha, beta: f64) -> f64 {
bbb := alpha - LOG4 bbb := alpha - LOG4
ccc := alpha + ainv ccc := alpha + ainv
for { for {
u1 := float64() u1 := float64(gen)
if !(1e-7 < u1 && u1 < 0.9999999) { if !(1e-7 < u1 && u1 < 0.9999999) {
continue continue
} }
u2 := 1 - float64() u2 := 1 - float64(gen)
v := math.ln(u1 / (1 - u1)) / ainv v := math.ln(u1 / (1 - u1)) / ainv
x := alpha * math.exp(v) x := alpha * math.exp(v)
z := u1 * u1 * u2 z := u1 * u1 * u2
@@ -127,12 +127,12 @@ float64_gamma :: proc(alpha, beta: f64) -> f64 {
} }
case alpha == 1: case alpha == 1:
// float64_exponential(1/beta) // float64_exponential(1/beta)
return -math.ln(1 - float64()) * beta return -math.ln(1 - float64(gen)) * beta
case: case:
// ALGORITHM GS of Statistical Computing - Kennedy & Gentle // ALGORITHM GS of Statistical Computing - Kennedy & Gentle
x: f64 x: f64
for { for {
u := float64() u := float64(gen)
b := (math.e + alpha) / math.e b := (math.e + alpha) / math.e
p := b * u p := b * u
if p <= 1 { if p <= 1 {
@@ -140,7 +140,7 @@ float64_gamma :: proc(alpha, beta: f64) -> f64 {
} else { } else {
x = -math.ln((b - p) / alpha) x = -math.ln((b - p) / alpha)
} }
u1 := float64() u1 := float64(gen)
if p > 1 { if p > 1 {
if u1 <= math.pow(x, alpha-1) { if u1 <= math.pow(x, alpha-1) {
break break
@@ -162,8 +162,8 @@ float64_gamma :: proc(alpha, beta: f64) -> f64 {
// //
// mean is alpha*beta, variance is math.pow(alpha*beta, 2) // mean is alpha*beta, variance is math.pow(alpha*beta, 2)
@(require_results) @(require_results)
float32_gamma :: proc(alpha, beta: f32) -> f32 { float32_gamma :: proc(alpha, beta: f32, gen := context.random_generator) -> f32 {
return f32(float64_gamma(f64(alpha), f64(beta))) return f32(float64_gamma(f64(alpha), f64(beta), gen))
} }
@@ -173,14 +173,14 @@ float32_gamma :: proc(alpha, beta: f32) -> f32 {
// //
// Return values range between 0 and 1 // Return values range between 0 and 1
@(require_results) @(require_results)
float64_beta :: proc(alpha, beta: f64) -> f64 { float64_beta :: proc(alpha, beta: f64, gen := context.random_generator) -> f64 {
if alpha <= 0 || beta <= 0 { if alpha <= 0 || beta <= 0 {
panic(#procedure + ": alpha and beta must be > 0.0") panic(#procedure + ": alpha and beta must be > 0.0")
} }
// Knuth Vol 2 Ed 3 pg 134 "the beta distribution" // Knuth Vol 2 Ed 3 pg 134 "the beta distribution"
y := float64_gamma(alpha, 1.0) y := float64_gamma(alpha, 1.0, gen)
if y != 0 { if y != 0 {
return y / (y + float64_gamma(beta, 1.0)) return y / (y + float64_gamma(beta, 1.0, gen))
} }
return 0 return 0
} }
@@ -190,35 +190,35 @@ float64_beta :: proc(alpha, beta: f64) -> f64 {
// //
// Return values range between 0 and 1 // Return values range between 0 and 1
@(require_results) @(require_results)
float32_beta :: proc(alpha, beta: f32) -> f32 { float32_beta :: proc(alpha, beta: f32, gen := context.random_generator) -> f32 {
return f32(float64_beta(f64(alpha), f64(beta))) return f32(float64_beta(f64(alpha), f64(beta), gen))
} }
// Pareto distribution, `alpha` is the shape parameter. // Pareto distribution, `alpha` is the shape parameter.
// https://wikipedia.org/wiki/Pareto_distribution // https://wikipedia.org/wiki/Pareto_distribution
@(require_results) @(require_results)
float64_pareto :: proc(alpha: f64) -> f64 { float64_pareto :: proc(alpha: f64, gen := context.random_generator) -> f64 {
return math.pow(1 - float64(), -1.0 / alpha) return math.pow(1 - float64(gen), -1.0 / alpha)
} }
// Pareto distribution, `alpha` is the shape parameter. // Pareto distribution, `alpha` is the shape parameter.
// https://wikipedia.org/wiki/Pareto_distribution // https://wikipedia.org/wiki/Pareto_distribution
@(require_results) @(require_results)
float32_pareto :: proc(alpha, beta: f32) -> f32 { float32_pareto :: proc(alpha, beta: f32, gen := context.random_generator) -> f32 {
return f32(float64_pareto(f64(alpha))) return f32(float64_pareto(f64(alpha), gen))
} }
// Weibull distribution, `alpha` is the scale parameter, `beta` is the shape parameter. // Weibull distribution, `alpha` is the scale parameter, `beta` is the shape parameter.
@(require_results) @(require_results)
float64_weibull :: proc(alpha, beta: f64) -> f64 { float64_weibull :: proc(alpha, beta: f64, gen := context.random_generator) -> f64 {
u := 1 - float64() u := 1 - float64(gen)
return alpha * math.pow(-math.ln(u), 1.0/beta) return alpha * math.pow(-math.ln(u), 1.0/beta)
} }
// Weibull distribution, `alpha` is the scale parameter, `beta` is the shape parameter. // Weibull distribution, `alpha` is the scale parameter, `beta` is the shape parameter.
@(require_results) @(require_results)
float32_weibull :: proc(alpha, beta: f32) -> f32 { float32_weibull :: proc(alpha, beta: f32, gen := context.random_generator) -> f32 {
return f32(float64_weibull(f64(alpha), f64(beta))) return f32(float64_weibull(f64(alpha), f64(beta), gen))
} }
@@ -227,23 +227,23 @@ float32_weibull :: proc(alpha, beta: f32) -> f32 {
// `kappa` is the concentration parameter which must be >= 0 // `kappa` is the concentration parameter which must be >= 0
// When `kappa` is zero, the Distribution is a uniform Distribution over the range 0 to 2pi // When `kappa` is zero, the Distribution is a uniform Distribution over the range 0 to 2pi
@(require_results) @(require_results)
float64_von_mises :: proc(mean_angle, kappa: f64) -> f64 { float64_von_mises :: proc(mean_angle, kappa: f64, gen := context.random_generator) -> f64 {
// Fisher, N.I., "Statistical Analysis of Circular Data", Cambridge University Press, 1993. // Fisher, N.I., "Statistical Analysis of Circular Data", Cambridge University Press, 1993.
mu := mean_angle mu := mean_angle
if kappa <= 1e-6 { if kappa <= 1e-6 {
return math.TAU * float64() return math.TAU * float64(gen)
} }
s := 0.5 / kappa s := 0.5 / kappa
t := s + math.sqrt(1 + s*s) t := s + math.sqrt(1 + s*s)
z: f64 z: f64
for { for {
u1 := float64() u1 := float64(gen)
z = math.cos(math.TAU * 0.5 * u1) z = math.cos(math.TAU * 0.5 * u1)
d := z / (t + z) d := z / (t + z)
u2 := float64() u2 := float64(gen)
if u2 < 1 - d*d || u2 <= (1-d)*math.exp(d) { if u2 < 1 - d*d || u2 <= (1-d)*math.exp(d) {
break break
} }
@@ -251,7 +251,7 @@ float64_von_mises :: proc(mean_angle, kappa: f64) -> f64 {
q := 1.0 / t q := 1.0 / t
f := (q + z) / (1 + q*z) f := (q + z) / (1 + q*z)
u3 := float64() u3 := float64(gen)
if u3 > 0.5 { if u3 > 0.5 {
return math.mod(mu + math.acos(f), math.TAU) return math.mod(mu + math.acos(f), math.TAU)
} else { } else {
@@ -263,57 +263,57 @@ float64_von_mises :: proc(mean_angle, kappa: f64) -> f64 {
// `kappa` is the concentration parameter which must be >= 0 // `kappa` is the concentration parameter which must be >= 0
// When `kappa` is zero, the Distribution is a uniform Distribution over the range 0 to 2pi // When `kappa` is zero, the Distribution is a uniform Distribution over the range 0 to 2pi
@(require_results) @(require_results)
float32_von_mises :: proc(mean_angle, kappa: f32) -> f32 { float32_von_mises :: proc(mean_angle, kappa: f32, gen := context.random_generator) -> f32 {
return f32(float64_von_mises(f64(mean_angle), f64(kappa))) return f32(float64_von_mises(f64(mean_angle), f64(kappa), gen))
} }
// Cauchy-Lorentz Distribution // Cauchy-Lorentz Distribution
// `x_0` is the location, `gamma` is the scale where `gamma` > 0 // `x_0` is the location, `gamma` is the scale where `gamma` > 0
@(require_results) @(require_results)
float64_cauchy_lorentz :: proc(x_0, gamma: f64) -> f64 { float64_cauchy_lorentz :: proc(x_0, gamma: f64, gen := context.random_generator) -> f64 {
assert(gamma > 0) assert(gamma > 0)
// Calculated from the inverse CDF // Calculated from the inverse CDF
return math.tan(math.PI * (float64() - 0.5))*gamma + x_0 return math.tan(math.PI * (float64(gen) - 0.5))*gamma + x_0
} }
// Cauchy-Lorentz Distribution // Cauchy-Lorentz Distribution
// `x_0` is the location, `gamma` is the scale where `gamma` > 0 // `x_0` is the location, `gamma` is the scale where `gamma` > 0
@(require_results) @(require_results)
float32_cauchy_lorentz :: proc(x_0, gamma: f32) -> f32 { float32_cauchy_lorentz :: proc(x_0, gamma: f32, gen := context.random_generator) -> f32 {
return f32(float64_cauchy_lorentz(f64(x_0), f64(gamma))) return f32(float64_cauchy_lorentz(f64(x_0), f64(gamma), gen))
} }
// Log Cauchy-Lorentz Distribution // Log Cauchy-Lorentz Distribution
// `x_0` is the location, `gamma` is the scale where `gamma` > 0 // `x_0` is the location, `gamma` is the scale where `gamma` > 0
@(require_results) @(require_results)
float64_log_cauchy_lorentz :: proc(x_0, gamma: f64) -> f64 { float64_log_cauchy_lorentz :: proc(x_0, gamma: f64, gen := context.random_generator) -> f64 {
assert(gamma > 0) assert(gamma > 0)
return math.exp(math.tan(math.PI * (float64() - 0.5))*gamma + x_0) return math.exp(math.tan(math.PI * (float64(gen) - 0.5))*gamma + x_0)
} }
// Log Cauchy-Lorentz Distribution // Log Cauchy-Lorentz Distribution
// `x_0` is the location, `gamma` is the scale where `gamma` > 0 // `x_0` is the location, `gamma` is the scale where `gamma` > 0
@(require_results) @(require_results)
float32_log_cauchy_lorentz :: proc(x_0, gamma: f32) -> f32 { float32_log_cauchy_lorentz :: proc(x_0, gamma: f32, gen := context.random_generator) -> f32 {
return f32(float64_log_cauchy_lorentz(f64(x_0), f64(gamma))) return f32(float64_log_cauchy_lorentz(f64(x_0), f64(gamma), gen))
} }
// Laplace Distribution // Laplace Distribution
// `b` is the scale where `b` > 0 // `b` is the scale where `b` > 0
@(require_results) @(require_results)
float64_laplace :: proc(mean, b: f64) -> f64 { float64_laplace :: proc(mean, b: f64, gen := context.random_generator) -> f64 {
assert(b > 0) assert(b > 0)
p := float64()-0.5 p := float64(gen)-0.5
return -math.sign(p)*math.ln(1 - 2*abs(p))*b + mean return -math.sign(p)*math.ln(1 - 2*abs(p))*b + mean
} }
// Laplace Distribution // Laplace Distribution
// `b` is the scale where `b` > 0 // `b` is the scale where `b` > 0
@(require_results) @(require_results)
float32_laplace :: proc(mean, b: f32) -> f32 { float32_laplace :: proc(mean, b: f32, gen := context.random_generator) -> f32 {
return f32(float64_laplace(f64(mean), f64(b))) return f32(float64_laplace(f64(mean), f64(b), gen))
} }
@@ -321,18 +321,18 @@ float32_laplace :: proc(mean, b: f32) -> f32 {
// `eta` is the shape, `b` is the scale // `eta` is the shape, `b` is the scale
// Both `eta` and `b` must be > 0 // Both `eta` and `b` must be > 0
@(require_results) @(require_results)
float64_gompertz :: proc(eta, b: f64) -> f64 { float64_gompertz :: proc(eta, b: f64, gen := context.random_generator) -> f64 {
if eta <= 0 || b <= 0 { if eta <= 0 || b <= 0 {
panic(#procedure + ": eta and b must be > 0.0") panic(#procedure + ": eta and b must be > 0.0")
} }
p := float64() p := float64(gen)
return math.ln(1 - math.ln(1 - p)/eta)/b return math.ln(1 - math.ln(1 - p)/eta)/b
} }
// Gompertz Distribution // Gompertz Distribution
// `eta` is the shape, `b` is the scale // `eta` is the shape, `b` is the scale
// Both `eta` and `b` must be > 0 // Both `eta` and `b` must be > 0
@(require_results) @(require_results)
float32_gompertz :: proc(eta, b: f32) -> f32 { float32_gompertz :: proc(eta, b: f32, gen := context.random_generator) -> f32 {
return f32(float64_gompertz(f64(eta), f64(b))) return f32(float64_gompertz(f64(eta), f64(b), gen))
} }
+4 -4
View File
@@ -16,7 +16,7 @@ import "core:math"
// https://www.jstatsoft.org/article/view/v005i08 [web page] // https://www.jstatsoft.org/article/view/v005i08 [web page]
// //
@(require_results) @(require_results)
exp_float64 :: proc() -> f64 { exp_float64 :: proc(gen := context.random_generator) -> f64 {
re :: 7.69711747013104972 re :: 7.69711747013104972
@(static, rodata) @(static, rodata)
@@ -199,16 +199,16 @@ exp_float64 :: proc() -> f64 {
} }
for { for {
j := uint32() j := uint32(gen)
i := j & 0xFF i := j & 0xFF
x := f64(j) * f64(we[i]) x := f64(j) * f64(we[i])
if j < ke[i] { if j < ke[i] {
return x return x
} }
if i == 0 { if i == 0 {
return re - math.ln(float64()) return re - math.ln(float64(gen))
} }
if fe[i]+f32(float64())*(fe[i-1]-fe[i]) < f32(math.exp(-x)) { if fe[i]+f32(float64(gen))*(fe[i-1]-fe[i]) < f32(math.exp(-x)) {
return x return x
} }
} }
+5 -5
View File
@@ -18,7 +18,7 @@ import "core:math"
// https://www.jstatsoft.org/article/view/v005i08 [web page] // https://www.jstatsoft.org/article/view/v005i08 [web page]
// //
@(require_results) @(require_results)
norm_float64 :: proc() -> f64 { norm_float64 :: proc(gen := context.random_generator) -> f64 {
rn :: 3.442619855899 rn :: 3.442619855899
@(static, rodata) @(static, rodata)
@@ -116,7 +116,7 @@ norm_float64 :: proc() -> f64 {
} }
for { for {
j := i32(uint32()) j := i32(uint32(gen))
i := j & 0x7f i := j & 0x7f
x := f64(j) * f64(wn[i]) x := f64(j) * f64(wn[i])
if u32(abs(j)) < kn[i] { if u32(abs(j)) < kn[i] {
@@ -126,15 +126,15 @@ norm_float64 :: proc() -> f64 {
if i == 0 { if i == 0 {
for { for {
x = -math.ln(float64()) * (1.0/ rn) x = -math.ln(float64(gen)) * (1.0/ rn)
y := -math.ln(float64()) y := -math.ln(float64(gen))
if y+y >= x*x { if y+y >= x*x {
break break
} }
} }
return j > 0 ? rn + x : -rn - x return j > 0 ? rn + x : -rn - x
} }
if fn[i]+f32(float64())*(fn[i-1]-fn[i]) < f32(math.exp(-0.5*x*x)) { if fn[i]+f32(float64(gen))*(fn[i-1]-fn[i]) < f32(math.exp(-0.5*x*x)) {
return x return x
} }
} }
+66 -47
View File
@@ -9,6 +9,10 @@ import "base:runtime"
import "core:math" import "core:math"
import "core:mem" import "core:mem"
Generator :: runtime.Random_Generator
Generator_Query_Info :: runtime.Random_Generator_Query_Info
Default_Random_State :: runtime.Default_Random_State Default_Random_State :: runtime.Default_Random_State
default_random_generator :: runtime.default_random_generator default_random_generator :: runtime.default_random_generator
@@ -54,7 +58,7 @@ Example:
import "core:fmt" import "core:fmt"
set_global_seed_example :: proc() { set_global_seed_example :: proc() {
rand.set_global_seed(1) rand.reset(1)
fmt.println(rand.uint64()) fmt.println(rand.uint64())
} }
@@ -62,15 +66,24 @@ Possible Output:
10 10
*/ */
reset :: proc(seed: u64) { reset :: proc(seed: u64, gen := context.random_generator) {
runtime.random_generator_reset_u64(context.random_generator, seed) runtime.random_generator_reset_u64(gen, seed)
}
reset_bytes :: proc(bytes: []byte, gen := context.random_generator) {
runtime.random_generator_reset_bytes(gen, bytes)
}
query_info :: proc(gen := context.random_generator) -> Generator_Query_Info {
return runtime.random_generator_query_info(gen)
} }
@(private) @(private)
_random_u64 :: proc() -> (res: u64) { _random_u64 :: proc(gen := context.random_generator) -> (res: u64) {
ok := runtime.random_generator_read_ptr(context.random_generator, &res, size_of(res)) ok := runtime.random_generator_read_ptr(gen, &res, size_of(res))
assert(ok, "uninitialized context.random_generator") assert(ok, "uninitialized gen/context.random_generator")
return return
} }
@@ -95,7 +108,7 @@ Possible Output:
*/ */
@(require_results) @(require_results)
uint32 :: proc() -> (val: u32) { return u32(_random_u64()) } uint32 :: proc(gen := context.random_generator) -> (val: u32) { return u32(_random_u64(gen)) }
/* /*
Generates a random 64 bit value using the provided random number generator. If no generator is provided the global random number generator will be used. Generates a random 64 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
@@ -118,7 +131,7 @@ Possible Output:
*/ */
@(require_results) @(require_results)
uint64 :: proc() -> (val: u64) { return _random_u64() } uint64 :: proc(gen := context.random_generator) -> (val: u64) { return _random_u64(gen) }
/* /*
Generates a random 128 bit value using the provided random number generator. If no generator is provided the global random number generator will be used. Generates a random 128 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
@@ -141,9 +154,9 @@ Possible Output:
*/ */
@(require_results) @(require_results)
uint128 :: proc() -> (val: u128) { uint128 :: proc(gen := context.random_generator) -> (val: u128) {
a := u128(_random_u64()) a := u128(_random_u64(gen))
b := u128(_random_u64()) b := u128(_random_u64(gen))
return (a<<64) | b return (a<<64) | b
} }
@@ -168,7 +181,7 @@ Possible Output:
389 389
*/ */
@(require_results) int31 :: proc() -> (val: i32) { return i32(uint32() << 1 >> 1) } @(require_results) int31 :: proc(gen := context.random_generator) -> (val: i32) { return i32(uint32(gen) << 1 >> 1) }
/* /*
Generates a random 63 bit value using the provided random number generator. If no generator is provided the global random number generator will be used. Generates a random 63 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
@@ -191,7 +204,7 @@ Possible Output:
389 389
*/ */
@(require_results) int63 :: proc() -> (val: i64) { return i64(uint64() << 1 >> 1) } @(require_results) int63 :: proc(gen := context.random_generator) -> (val: i64) { return i64(uint64(gen) << 1 >> 1) }
/* /*
Generates a random 127 bit value using the provided random number generator. If no generator is provided the global random number generator will be used. Generates a random 127 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
@@ -214,7 +227,7 @@ Possible Output:
389 389
*/ */
@(require_results) int127 :: proc() -> (val: i128) { return i128(uint128() << 1 >> 1) } @(require_results) int127 :: proc(gen := context.random_generator) -> (val: i128) { return i128(uint128(gen) << 1 >> 1) }
/* /*
Generates a random 31 bit value in the range `[0, n)` using the provided random number generator. If no generator is provided the global random number generator will be used. Generates a random 31 bit value in the range `[0, n)` using the provided random number generator. If no generator is provided the global random number generator will be used.
@@ -242,17 +255,17 @@ Possible Output:
*/ */
@(require_results) @(require_results)
int31_max :: proc(n: i32) -> (val: i32) { int31_max :: proc(n: i32, gen := context.random_generator) -> (val: i32) {
if n <= 0 { if n <= 0 {
panic("Invalid argument to int31_max") panic("Invalid argument to int31_max")
} }
if n&(n-1) == 0 { if n&(n-1) == 0 {
return int31() & (n-1) return int31(gen) & (n-1)
} }
max := i32((1<<31) - 1 - (1<<31)%u32(n)) max := i32((1<<31) - 1 - (1<<31)%u32(n))
v := int31() v := int31(gen)
for v > max { for v > max {
v = int31() v = int31(gen)
} }
return v % n return v % n
} }
@@ -283,17 +296,17 @@ Possible Output:
*/ */
@(require_results) @(require_results)
int63_max :: proc(n: i64) -> (val: i64) { int63_max :: proc(n: i64, gen := context.random_generator) -> (val: i64) {
if n <= 0 { if n <= 0 {
panic("Invalid argument to int63_max") panic("Invalid argument to int63_max")
} }
if n&(n-1) == 0 { if n&(n-1) == 0 {
return int63() & (n-1) return int63(gen) & (n-1)
} }
max := i64((1<<63) - 1 - (1<<63)%u64(n)) max := i64((1<<63) - 1 - (1<<63)%u64(n))
v := int63() v := int63(gen)
for v > max { for v > max {
v = int63() v = int63(gen)
} }
return v % n return v % n
} }
@@ -324,17 +337,17 @@ Possible Output:
*/ */
@(require_results) @(require_results)
int127_max :: proc(n: i128) -> (val: i128) { int127_max :: proc(n: i128, gen := context.random_generator) -> (val: i128) {
if n <= 0 { if n <= 0 {
panic("Invalid argument to int127_max") panic("Invalid argument to int127_max")
} }
if n&(n-1) == 0 { if n&(n-1) == 0 {
return int127() & (n-1) return int127(gen) & (n-1)
} }
max := i128((1<<127) - 1 - (1<<127)%u128(n)) max := i128((1<<127) - 1 - (1<<127)%u128(n))
v := int127() v := int127(gen)
for v > max { for v > max {
v = int127() v = int127(gen)
} }
return v % n return v % n
} }
@@ -365,14 +378,14 @@ Possible Output:
*/ */
@(require_results) @(require_results)
int_max :: proc(n: int) -> (val: int) { int_max :: proc(n: int, gen := context.random_generator) -> (val: int) {
if n <= 0 { if n <= 0 {
panic("Invalid argument to int_max") panic("Invalid argument to int_max")
} }
when size_of(int) == 4 { when size_of(int) == 4 {
return int(int31_max(i32(n))) return int(int31_max(i32(n), gen))
} else { } else {
return int(int63_max(i64(n))) return int(int63_max(i64(n), gen))
} }
} }
@@ -396,7 +409,7 @@ Possible Output:
0.511 0.511
*/ */
@(require_results) float64 :: proc() -> (val: f64) { return f64(int63_max(1<<53)) / (1 << 53) } @(require_results) float64 :: proc(gen := context.random_generator) -> (val: f64) { return f64(int63_max(1<<53, gen)) / (1 << 53) }
/* /*
Generates a random single floating point value in the range `[0, 1)` using the provided random number generator. If no generator is provided the global random number generator will be used. Generates a random single floating point value in the range `[0, 1)` using the provided random number generator. If no generator is provided the global random number generator will be used.
@@ -418,7 +431,7 @@ Possible Output:
0.511 0.511
*/ */
@(require_results) float32 :: proc() -> (val: f32) { return f32(int31_max(1<<24)) / (1 << 24) } @(require_results) float32 :: proc(gen := context.random_generator) -> (val: f32) { return f32(int31_max(1<<24, gen)) / (1 << 24) }
/* /*
Generates a random double floating point value in the range `[low, high)` using the provided random number generator. If no generator is provided the global random number generator will be used. Generates a random double floating point value in the range `[low, high)` using the provided random number generator. If no generator is provided the global random number generator will be used.
@@ -446,9 +459,9 @@ Possible Output:
673.130 673.130
*/ */
@(require_results) float64_range :: proc(low, high: f64) -> (val: f64) { @(require_results) float64_range :: proc(low, high: f64, gen := context.random_generator) -> (val: f64) {
assert(low <= high, "low must be lower than or equal to high") assert(low <= high, "low must be lower than or equal to high")
val = (high-low)*float64() + low val = (high-low)*float64(gen) + low
if val >= high { if val >= high {
val = max(low, high * (1 - math.F64_EPSILON)) val = max(low, high * (1 - math.F64_EPSILON))
} }
@@ -481,9 +494,9 @@ Possible Output:
673.130 673.130
*/ */
@(require_results) float32_range :: proc(low, high: f32) -> (val: f32) { @(require_results) float32_range :: proc(low, high: f32, gen := context.random_generator) -> (val: f32) {
assert(low <= high, "low must be lower than or equal to high") assert(low <= high, "low must be lower than or equal to high")
val = (high-low)*float32() + low val = (high-low)*float32(gen) + low
if val >= high { if val >= high {
val = max(low, high * (1 - math.F32_EPSILON)) val = max(low, high * (1 - math.F32_EPSILON))
} }
@@ -518,12 +531,12 @@ Possible Output:
*/ */
@(require_results) @(require_results)
read :: proc(p: []byte) -> (n: int) { read :: proc(p: []byte, gen := context.random_generator) -> (n: int) {
pos := i8(0) pos := i8(0)
val := i64(0) val := i64(0)
for n = 0; n < len(p); n += 1 { for n = 0; n < len(p); n += 1 {
if pos == 0 { if pos == 0 {
val = int63() val = int63(gen)
pos = 7 pos = 7
} }
p[n] = byte(val) p[n] = byte(val)
@@ -566,10 +579,10 @@ Possible Output:
*/ */
@(require_results) @(require_results)
perm :: proc(n: int, allocator := context.allocator) -> (res: []int, err: mem.Allocator_Error) #optional_allocator_error { perm :: proc(n: int, allocator := context.allocator, gen := context.random_generator) -> (res: []int, err: mem.Allocator_Error) #optional_allocator_error {
m := make([]int, n, allocator) or_return m := make([]int, n, allocator) or_return
for i := 0; i < n; i += 1 { for i := 0; i < n; i += 1 {
j := int_max(i+1) j := int_max(i+1, gen)
m[i] = m[j] m[i] = m[j]
m[j] = i m[j] = i
} }
@@ -599,14 +612,20 @@ Possible Output:
[2, 4, 3, 1] [2, 4, 3, 1]
*/ */
shuffle :: proc(array: $T/[]$E) { shuffle :: proc(array: $T/[]$E, gen := context.random_generator) {
n := i64(len(array)) n := i64(len(array))
if n < 2 { if n < 2 {
return return
} }
for i := i64(n - 1); i > 0; i -= 1 { i := n - 1
j := int63_max(i + 1) for ; i > (1<<31 - 2); i -= 1 {
j := int63_max(i + 1, gen)
array[i], array[j] = array[j], array[i]
}
for ; i > 0; i -= 1 {
j := int31_max(i32(i + 1), gen)
array[i], array[j] = array[j], array[i] array[i], array[j] = array[j], array[i]
} }
} }
@@ -641,17 +660,17 @@ Possible Output:
*/ */
@(require_results) @(require_results)
choice :: proc(array: $T/[]$E) -> (res: E) { choice :: proc(array: $T/[]$E, gen := context.random_generator) -> (res: E) {
n := i64(len(array)) n := i64(len(array))
if n < 1 { if n < 1 {
return E{} return E{}
} }
return array[int63_max(n)] return array[int63_max(n, gen)]
} }
@(require_results) @(require_results)
choice_enum :: proc($T: typeid) -> T choice_enum :: proc($T: typeid, gen := context.random_generator) -> T
where where
intrinsics.type_is_enum(T), intrinsics.type_is_enum(T),
size_of(T) <= 8, size_of(T) <= 8,
@@ -659,11 +678,11 @@ choice_enum :: proc($T: typeid) -> T
{ {
when intrinsics.type_is_unsigned(intrinsics.type_core_type(T)) && when intrinsics.type_is_unsigned(intrinsics.type_core_type(T)) &&
u64(max(T)) > u64(max(i64)) { u64(max(T)) > u64(max(i64)) {
i := uint64() % u64(len(T)) i := uint64(gen) % u64(len(T))
i += u64(min(T)) i += u64(min(T))
return T(i) return T(i)
} else { } else {
i := int63_max(i64(len(T))) i := int63_max(i64(len(T)), gen)
i += i64(min(T)) i += i64(min(T))
return T(i) return T(i)
} }
+1 -3
View File
@@ -748,9 +748,7 @@ dynamic_pool_alloc_bytes :: proc(p: ^Dynamic_Pool, bytes: int) -> ([]byte, Alloc
return return
} }
n := bytes n := align_formula(bytes, p.alignment)
extra := p.alignment - (n % p.alignment)
n += extra
if n > p.block_size { if n > p.block_size {
return nil, .Invalid_Argument return nil, .Invalid_Argument
} }
+3 -3
View File
@@ -284,7 +284,7 @@ adjust_request_size_with_err :: proc(size, align: uint) -> (adjusted: uint, err:
// TLSF utility functions. In most cases these are direct translations of // TLSF utility functions. In most cases these are direct translations of
// the documentation in the research paper. // the documentation in the research paper.
@(optimization_mode="speed", require_results) @(optimization_mode="favor_size", require_results)
mapping_insert :: proc(size: uint) -> (fl, sl: i32) { mapping_insert :: proc(size: uint) -> (fl, sl: i32) {
if size < SMALL_BLOCK_SIZE { if size < SMALL_BLOCK_SIZE {
// Store small blocks in first list. // Store small blocks in first list.
@@ -297,7 +297,7 @@ mapping_insert :: proc(size: uint) -> (fl, sl: i32) {
return return
} }
@(optimization_mode="speed", require_results) @(optimization_mode="favor_size", require_results)
mapping_round :: #force_inline proc(size: uint) -> (rounded: uint) { mapping_round :: #force_inline proc(size: uint) -> (rounded: uint) {
rounded = size rounded = size
if size >= SMALL_BLOCK_SIZE { if size >= SMALL_BLOCK_SIZE {
@@ -308,7 +308,7 @@ mapping_round :: #force_inline proc(size: uint) -> (rounded: uint) {
} }
// This version rounds up to the next block size (for allocations) // This version rounds up to the next block size (for allocations)
@(optimization_mode="speed", require_results) @(optimization_mode="favor_size", require_results)
mapping_search :: proc(size: uint) -> (fl, sl: i32) { mapping_search :: proc(size: uint) -> (fl, sl: i32) {
return mapping_insert(mapping_round(size)) return mapping_insert(mapping_round(size))
} }
+2 -2
View File
@@ -24,7 +24,7 @@ map_file :: proc{
map_file_from_path :: proc(filename: string, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) { map_file_from_path :: proc(filename: string, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) {
fd, err := os.open(filename, os.O_RDWR) fd, err := os.open(filename, os.O_RDWR)
if err != 0 { if err != nil {
return nil, .Open_Failure return nil, .Open_Failure
} }
defer os.close(fd) defer os.close(fd)
@@ -34,7 +34,7 @@ map_file_from_path :: proc(filename: string, flags: Map_File_Flags) -> (data: []
map_file_from_file_descriptor :: proc(fd: uintptr, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) { map_file_from_file_descriptor :: proc(fd: uintptr, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) {
size, os_err := os.file_size(os.Handle(fd)) size, os_err := os.file_size(os.Handle(fd))
if os_err != 0 { if os_err != nil {
return nil, .Stat_Failure return nil, .Stat_Failure
} }
if size < 0 { if size < 0 {
@@ -1,5 +1,7 @@
//+build freebsd, openbsd, netbsd
//+private //+private
//+build !darwin
//+build !linux
//+build !windows
package mem_virtual package mem_virtual
_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) { _reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
+32 -32
View File
@@ -53,9 +53,9 @@ _create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (so
unreachable() unreachable()
} }
sock, ok := os.socket(c_family, c_type, c_protocol) sock, sock_err := os.socket(c_family, c_type, c_protocol)
if ok != os.ERROR_NONE { if sock_err != nil {
err = Create_Socket_Error(ok) err = Create_Socket_Error(os.is_platform_error(sock_err) or_else -1)
return return
} }
@@ -84,8 +84,8 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
sockaddr := _endpoint_to_sockaddr(endpoint) sockaddr := _endpoint_to_sockaddr(endpoint)
res := os.connect(os.Socket(skt), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len)) res := os.connect(os.Socket(skt), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len))
if res != os.ERROR_NONE { if res != nil {
err = Dial_Error(res) err = Dial_Error(os.is_platform_error(res) or_else -1)
return return
} }
@@ -100,11 +100,11 @@ _bind :: proc(skt: Any_Socket, ep: Endpoint) -> (err: Network_Error) {
sockaddr := _endpoint_to_sockaddr(ep) sockaddr := _endpoint_to_sockaddr(ep)
s := any_socket_to_socket(skt) s := any_socket_to_socket(skt)
res := os.bind(os.Socket(s), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len)) res := os.bind(os.Socket(s), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len))
if res != os.ERROR_NONE { if res != nil {
if res == os.EACCES && ep.port <= MAX_PRIVILEGED_PORT { if res == os.EACCES && ep.port <= MAX_PRIVILEGED_PORT {
err = .Privileged_Port_Without_Root err = .Privileged_Port_Without_Root
} else { } else {
err = Bind_Error(res) err = Bind_Error(os.is_platform_error(res) or_else -1)
} }
} }
return return
@@ -128,8 +128,8 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (skt: TCP_
bind(sock, interface_endpoint) or_return bind(sock, interface_endpoint) or_return
res := os.listen(os.Socket(skt), backlog) res := os.listen(os.Socket(skt), backlog)
if res != os.ERROR_NONE { if res != nil {
err = Listen_Error(res) err = Listen_Error(os.is_platform_error(res) or_else -1)
return return
} }
@@ -141,9 +141,9 @@ _accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (client
sockaddr: os.SOCKADDR_STORAGE_LH sockaddr: os.SOCKADDR_STORAGE_LH
sockaddrlen := c.int(size_of(sockaddr)) sockaddrlen := c.int(size_of(sockaddr))
client_sock, ok := os.accept(os.Socket(sock), cast(^os.SOCKADDR) &sockaddr, &sockaddrlen) client_sock, client_sock_err := os.accept(os.Socket(sock), cast(^os.SOCKADDR) &sockaddr, &sockaddrlen)
if ok != os.ERROR_NONE { if client_sock_err != nil {
err = Accept_Error(ok) err = Accept_Error(os.is_platform_error(client_sock_err) or_else -1)
return return
} }
client = TCP_Socket(client_sock) client = TCP_Socket(client_sock)
@@ -162,9 +162,9 @@ _recv_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_read: int, err: Networ
if len(buf) <= 0 { if len(buf) <= 0 {
return return
} }
res, ok := os.recv(os.Socket(skt), buf, 0) res, res_err := os.recv(os.Socket(skt), buf, 0)
if ok != os.ERROR_NONE { if res_err != nil {
err = TCP_Recv_Error(ok) err = TCP_Recv_Error(os.is_platform_error(res_err) or_else -1)
return return
} }
return int(res), nil return int(res), nil
@@ -178,9 +178,9 @@ _recv_udp :: proc(skt: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endp
from: os.SOCKADDR_STORAGE_LH from: os.SOCKADDR_STORAGE_LH
fromsize := c.int(size_of(from)) fromsize := c.int(size_of(from))
res, ok := os.recvfrom(os.Socket(skt), buf, 0, cast(^os.SOCKADDR) &from, &fromsize) res, res_err := os.recvfrom(os.Socket(skt), buf, 0, cast(^os.SOCKADDR) &from, &fromsize)
if ok != os.ERROR_NONE { if res_err != nil {
err = UDP_Recv_Error(ok) err = UDP_Recv_Error(os.is_platform_error(res_err) or_else -1)
return return
} }
@@ -194,9 +194,9 @@ _send_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_written: int, err: Net
for bytes_written < len(buf) { for bytes_written < len(buf) {
limit := min(int(max(i32)), len(buf) - bytes_written) limit := min(int(max(i32)), len(buf) - bytes_written)
remaining := buf[bytes_written:][:limit] remaining := buf[bytes_written:][:limit]
res, ok := os.send(os.Socket(skt), remaining, 0) res, res_err := os.send(os.Socket(skt), remaining, 0)
if ok != os.ERROR_NONE { if res_err != nil {
err = TCP_Send_Error(ok) err = TCP_Send_Error(os.is_platform_error(res_err) or_else -1)
return return
} }
bytes_written += int(res) bytes_written += int(res)
@@ -210,9 +210,9 @@ _send_udp :: proc(skt: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written:
for bytes_written < len(buf) { for bytes_written < len(buf) {
limit := min(1<<31, len(buf) - bytes_written) limit := min(1<<31, len(buf) - bytes_written)
remaining := buf[bytes_written:][:limit] remaining := buf[bytes_written:][:limit]
res, ok := os.sendto(os.Socket(skt), remaining, 0, cast(^os.SOCKADDR)&toaddr, i32(toaddr.len)) res, res_err := os.sendto(os.Socket(skt), remaining, 0, cast(^os.SOCKADDR)&toaddr, i32(toaddr.len))
if ok != os.ERROR_NONE { if res_err != nil {
err = UDP_Send_Error(ok) err = UDP_Send_Error(os.is_platform_error(res_err) or_else -1)
return return
} }
bytes_written += int(res) bytes_written += int(res)
@@ -224,8 +224,8 @@ _send_udp :: proc(skt: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written:
_shutdown :: proc(skt: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Error) { _shutdown :: proc(skt: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Error) {
s := any_socket_to_socket(skt) s := any_socket_to_socket(skt)
res := os.shutdown(os.Socket(s), int(manner)) res := os.shutdown(os.Socket(s), int(manner))
if res != os.ERROR_NONE { if res != nil {
return Shutdown_Error(res) return Shutdown_Error(os.is_platform_error(res) or_else -1)
} }
return return
} }
@@ -302,8 +302,8 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca
skt := any_socket_to_socket(s) skt := any_socket_to_socket(s)
res := os.setsockopt(os.Socket(skt), int(level), int(option), ptr, len) res := os.setsockopt(os.Socket(skt), int(level), int(option), ptr, len)
if res != os.ERROR_NONE { if res != nil {
return Socket_Option_Error(res) return Socket_Option_Error(os.is_platform_error(res) or_else -1)
} }
return nil return nil
@@ -314,8 +314,8 @@ _set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_E
socket := any_socket_to_socket(socket) socket := any_socket_to_socket(socket)
flags, getfl_err := os.fcntl(int(socket), os.F_GETFL, 0) flags, getfl_err := os.fcntl(int(socket), os.F_GETFL, 0)
if getfl_err != os.ERROR_NONE { if getfl_err != nil {
return Set_Blocking_Error(getfl_err) return Set_Blocking_Error(os.is_platform_error(getfl_err) or_else -1)
} }
if should_block { if should_block {
@@ -325,8 +325,8 @@ _set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_E
} }
_, setfl_err := os.fcntl(int(socket), os.F_SETFL, flags) _, setfl_err := os.fcntl(int(socket), os.F_SETFL, flags)
if setfl_err != os.ERROR_NONE { if setfl_err != nil {
return Set_Blocking_Error(setfl_err) return Set_Blocking_Error(os.is_platform_error(setfl_err) or_else -1)
} }
return nil return nil
+3 -3
View File
@@ -117,7 +117,7 @@ _wrap_os_addr :: proc "contextless" (addr: linux.Sock_Addr_Any)->(Endpoint) {
_create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (Any_Socket, Network_Error) { _create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (Any_Socket, Network_Error) {
family := _unwrap_os_family(family) family := _unwrap_os_family(family)
proto, socktype := _unwrap_os_proto_socktype(protocol) proto, socktype := _unwrap_os_proto_socktype(protocol)
sock, errno := linux.socket(family, socktype, {}, proto) sock, errno := linux.socket(family, socktype, {.CLOEXEC}, proto)
if errno != .NONE { if errno != .NONE {
return {}, Create_Socket_Error(errno) return {}, Create_Socket_Error(errno)
} }
@@ -132,7 +132,7 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
} }
// Create new TCP socket // Create new TCP socket
os_sock: linux.Fd os_sock: linux.Fd
os_sock, errno = linux.socket(_unwrap_os_family(family_from_endpoint(endpoint)), .STREAM, {}, .TCP) os_sock, errno = linux.socket(_unwrap_os_family(family_from_endpoint(endpoint)), .STREAM, {.CLOEXEC}, .TCP)
if errno != .NONE { if errno != .NONE {
// TODO(flysand): should return invalid file descriptor here casted as TCP_Socket // TODO(flysand): should return invalid file descriptor here casted as TCP_Socket
return {}, Create_Socket_Error(errno) return {}, Create_Socket_Error(errno)
@@ -172,7 +172,7 @@ _listen_tcp :: proc(endpoint: Endpoint, backlog := 1000) -> (TCP_Socket, Network
ep_address := _unwrap_os_addr(endpoint) ep_address := _unwrap_os_addr(endpoint)
// Create TCP socket // Create TCP socket
os_sock: linux.Fd os_sock: linux.Fd
os_sock, errno = linux.socket(ep_family, .STREAM, {}, .TCP) os_sock, errno = linux.socket(ep_family, .STREAM, {.CLOEXEC}, .TCP)
if errno != .NONE { if errno != .NONE {
// TODO(flysand): should return invalid file descriptor here casted as TCP_Socket // TODO(flysand): should return invalid file descriptor here casted as TCP_Socket
return {}, Create_Socket_Error(errno) return {}, Create_Socket_Error(errno)
+4 -1
View File
@@ -599,6 +599,7 @@ Field_Flag :: enum {
Subtype, Subtype,
By_Ptr, By_Ptr,
No_Broadcast, No_Broadcast,
No_Capture,
Results, Results,
Tags, Tags,
@@ -619,6 +620,7 @@ field_flag_strings := [Field_Flag]string{
.Subtype = "#subtype", .Subtype = "#subtype",
.By_Ptr = "#by_ptr", .By_Ptr = "#by_ptr",
.No_Broadcast = "#no_broadcast", .No_Broadcast = "#no_broadcast",
.No_Capture = "#no_capture",
.Results = "results", .Results = "results",
.Tags = "field tag", .Tags = "field tag",
@@ -634,6 +636,7 @@ field_hash_flag_strings := []struct{key: string, flag: Field_Flag}{
{"subtype", .Subtype}, {"subtype", .Subtype},
{"by_ptr", .By_Ptr}, {"by_ptr", .By_Ptr},
{"no_broadcast", .No_Broadcast}, {"no_broadcast", .No_Broadcast},
{"no_capture", .No_Capture},
} }
@@ -754,7 +757,7 @@ Array_Type :: struct {
using node: Expr, using node: Expr,
open: tokenizer.Pos, open: tokenizer.Pos,
tag: ^Expr, tag: ^Expr,
len: ^Expr, // Ellipsis node for [?]T array types, nil for slice types len: ^Expr, // Unary_Expr node for [?]T array types, nil for slice types
close: tokenizer.Pos, close: tokenizer.Pos,
elem: ^Expr, elem: ^Expr,
} }
+21 -17
View File
@@ -1778,6 +1778,7 @@ parse_var_type :: proc(p: ^Parser, flags: ast.Field_Flags) -> ^ast.Expr {
type = ast.new(ast.Bad_Expr, tok.pos, end_pos(tok)) type = ast.new(ast.Bad_Expr, tok.pos, end_pos(tok))
} }
e := ast.new(ast.Ellipsis, type.pos, type) e := ast.new(ast.Ellipsis, type.pos, type)
e.tok = tok.kind
e.expr = type e.expr = type
return e return e
} }
@@ -2179,22 +2180,25 @@ parse_inlining_operand :: proc(p: ^Parser, lhs: bool, tok: tokenizer.Token) -> ^
} }
} }
#partial switch e in ast.strip_or_return_expr(expr).derived_expr { if expr != nil {
case ^ast.Proc_Lit: #partial switch e in ast.strip_or_return_expr(expr).derived_expr {
if e.inlining != .None && e.inlining != pi { case ^ast.Proc_Lit:
error(p, expr.pos, "both 'inline' and 'no_inline' cannot be applied to a procedure literal") if e.inlining != .None && e.inlining != pi {
error(p, expr.pos, "both 'inline' and 'no_inline' cannot be applied to a procedure literal")
}
e.inlining = pi
return expr
case ^ast.Call_Expr:
if e.inlining != .None && e.inlining != pi {
error(p, expr.pos, "both 'inline' and 'no_inline' cannot be applied to a procedure call")
}
e.inlining = pi
return expr
} }
e.inlining = pi
case ^ast.Call_Expr:
if e.inlining != .None && e.inlining != pi {
error(p, expr.pos, "both 'inline' and 'no_inline' cannot be applied to a procedure call")
}
e.inlining = pi
case:
error(p, tok.pos, "'%s' must be followed by a procedure literal or call", tok.text)
return ast.new(ast.Bad_Expr, tok.pos, expr)
} }
return expr
error(p, tok.pos, "'%s' must be followed by a procedure literal or call", tok.text)
return ast.new(ast.Bad_Expr, tok.pos, expr)
} }
parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
@@ -2258,18 +2262,18 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
hp.type = type hp.type = type
return hp return hp
case "file", "line", "procedure", "caller_location": case "file", "directory", "line", "procedure", "caller_location":
bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name)) bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
bd.tok = tok bd.tok = tok
bd.name = name.text bd.name = name.text
return bd return bd
case "location", "load", "assert", "defined", "config":
case "location", "exists", "load", "load_directory", "load_hash", "hash", "assert", "panic", "defined", "config":
bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name)) bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
bd.tok = tok bd.tok = tok
bd.name = name.text bd.name = name.text
return parse_call_expr(p, bd) return parse_call_expr(p, bd)
case "soa": case "soa":
bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name)) bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
bd.tok = tok bd.tok = tok
-73
View File
@@ -1,73 +0,0 @@
//+build freebsd, netbsd
package os
import "core:mem"
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
dirp: Dir
dirp, err = _fdopendir(fd)
if err != ERROR_NONE {
return
}
defer _closedir(dirp)
dirpath: string
dirpath, err = absolute_path_from_handle(fd)
if err != ERROR_NONE {
return
}
defer delete(dirpath)
n := n
size := n
if n <= 0 {
n = -1
size = 100
}
dfi := make([dynamic]File_Info, 0, size, allocator)
for {
entry: Dirent
end_of_stream: bool
entry, err, end_of_stream = _readdir(dirp)
if err != ERROR_NONE {
for fi_ in dfi {
file_info_delete(fi_, allocator)
}
delete(dfi)
return
} else if end_of_stream {
break
}
fi_: File_Info
filename := cast(string)(transmute(cstring)mem.Raw_Cstring{ data = &entry.name[0] })
if filename == "." || filename == ".." {
continue
}
fullpath := make([]byte, len(dirpath)+1+len(filename), context.temp_allocator)
copy(fullpath, dirpath)
copy(fullpath[len(dirpath):], "/")
copy(fullpath[len(dirpath)+1:], filename)
defer delete(fullpath, context.temp_allocator)
fi_, err = stat(string(fullpath), allocator)
if err != ERROR_NONE {
for fi__ in dfi {
file_info_delete(fi__, allocator)
}
delete(dfi)
return
}
append(&dfi, fi_)
}
return dfi[:], ERROR_NONE
}
-69
View File
@@ -1,69 +0,0 @@
package os
import "core:strings"
import "core:mem"
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
dirp: Dir
dirp, err = _fdopendir(fd)
if err != ERROR_NONE {
return
}
defer _closedir(dirp)
dirpath: string
dirpath, err = absolute_path_from_handle(fd)
if err != ERROR_NONE {
return
}
defer delete(dirpath)
n := n
size := n
if n <= 0 {
n = -1
size = 100
}
dfi := make([dynamic]File_Info, 0, size, allocator)
for {
entry: Dirent
end_of_stream: bool
entry, err, end_of_stream = _readdir(dirp)
if err != ERROR_NONE {
for fi_ in dfi {
file_info_delete(fi_, allocator)
}
delete(dfi)
return
} else if end_of_stream {
break
}
fi_: File_Info
filename := cast(string)(transmute(cstring)mem.Raw_Cstring{ data = &entry.name[0] })
if filename == "." || filename == ".." {
continue
}
fullpath := strings.join( []string{ dirpath, filename }, "/", context.temp_allocator)
defer delete(fullpath, context.temp_allocator)
fi_, err = stat(fullpath, allocator)
if err != ERROR_NONE {
for fi__ in dfi {
file_info_delete(fi__, allocator)
}
delete(dfi)
return
}
append(&dfi, fi_)
}
return dfi[:], ERROR_NONE
}
-72
View File
@@ -1,72 +0,0 @@
package os
import "core:strings"
import "core:mem"
import "base:runtime"
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
dirp: Dir
dirp, err = _fdopendir(fd)
if err != ERROR_NONE {
return
}
defer _closedir(dirp)
dirpath: string
dirpath, err = absolute_path_from_handle(fd)
if err != ERROR_NONE {
return
}
defer delete(dirpath)
n := n
size := n
if n <= 0 {
n = -1
size = 100
}
dfi := make([dynamic]File_Info, 0, size, allocator)
for {
entry: Dirent
end_of_stream: bool
entry, err, end_of_stream = _readdir(dirp)
if err != ERROR_NONE {
for fi_ in dfi {
file_info_delete(fi_, allocator)
}
delete(dfi)
return
} else if end_of_stream {
break
}
fi_: File_Info
filename := cast(string)(transmute(cstring)mem.Raw_Cstring{ data = &entry.name[0] })
if filename == "." || filename == ".." {
continue
}
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
fullpath := strings.join( []string{ dirpath, filename }, "/", context.temp_allocator)
defer delete(fullpath, context.temp_allocator)
fi_, err = stat(fullpath, allocator)
if err != ERROR_NONE {
for fi__ in dfi {
file_info_delete(fi__, allocator)
}
delete(dfi)
return
}
append(&dfi, fi_)
}
return dfi[:], ERROR_NONE
}
-71
View File
@@ -1,71 +0,0 @@
package os
import "core:strings"
import "core:mem"
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
dirp: Dir
dirp, err = _fdopendir(fd)
if err != ERROR_NONE {
return
}
defer _closedir(dirp)
// XXX OpenBSD
dirpath: string
dirpath, err = absolute_path_from_handle(fd)
if err != ERROR_NONE {
return
}
defer delete(dirpath)
n := n
size := n
if n <= 0 {
n = -1
size = 100
}
dfi := make([dynamic]File_Info, 0, size, allocator)
for {
entry: Dirent
end_of_stream: bool
entry, err, end_of_stream = _readdir(dirp)
if err != ERROR_NONE {
for fi_ in dfi {
file_info_delete(fi_, allocator)
}
delete(dfi)
return
} else if end_of_stream {
break
}
fi_: File_Info
filename := cast(string)(transmute(cstring)mem.Raw_Cstring{ data = &entry.name[0] })
if filename == "." || filename == ".." {
continue
}
fullpath := strings.join( []string{ dirpath, filename }, "/", context.temp_allocator)
defer delete(fullpath, context.temp_allocator)
fi_, err = stat(fullpath, allocator)
if err != ERROR_NONE {
for fi__ in dfi {
file_info_delete(fi__, allocator)
}
delete(dfi)
return
}
append(&dfi, fi_)
}
return dfi[:], ERROR_NONE
}
+62
View File
@@ -0,0 +1,62 @@
//+build darwin, linux, netbsd, freebsd, openbsd
package os
import "core:strings"
@(require_results)
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Error) {
dirp := _fdopendir(fd) or_return
defer _closedir(dirp)
dirpath := absolute_path_from_handle(fd) or_return
defer delete(dirpath)
n := n
size := n
if n <= 0 {
n = -1
size = 100
}
dfi := make([dynamic]File_Info, 0, size, allocator) or_return
defer if err != nil {
for fi_ in dfi {
file_info_delete(fi_, allocator)
}
delete(dfi)
}
for {
entry: Dirent
end_of_stream: bool
entry, err, end_of_stream = _readdir(dirp)
if err != nil {
return
} else if end_of_stream {
break
}
fi_: File_Info
filename := string(cstring(&entry.name[0]))
if filename == "." || filename == ".." {
continue
}
fullpath := strings.join({ dirpath, filename }, "/", allocator)
s: OS_Stat
s, err = _lstat(fullpath)
if err != nil {
delete(fullpath, allocator)
return
}
_fill_file_info_from_stat(&fi_, s)
fi_.fullpath = fullpath
fi_.name = path_base(fi_.fullpath)
append(&dfi, fi_)
}
return dfi[:], nil
}
+11 -10
View File
@@ -4,7 +4,9 @@ import win32 "core:sys/windows"
import "core:strings" import "core:strings"
import "base:runtime" import "base:runtime"
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) { @(require_results)
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Error) {
@(require_results)
find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW) -> (fi: File_Info) { find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW) -> (fi: File_Info) {
// Ignore "." and ".." // Ignore "." and ".."
if d.cFileName[0] == '.' && d.cFileName[1] == 0 { if d.cFileName[0] == '.' && d.cFileName[1] == 0 {
@@ -57,7 +59,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
dir_fi, _ := file_info_from_get_file_information_by_handle("", h) dir_fi, _ := file_info_from_get_file_information_by_handle("", h)
if !dir_fi.is_dir { if !dir_fi.is_dir {
return nil, ERROR_FILE_IS_NOT_DIR return nil, .Not_Dir
} }
n := n n := n
@@ -68,15 +70,14 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
} }
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
wpath: []u16 wpath := cleanpath_from_handle_u16(fd, context.temp_allocator) or_return
wpath, err = cleanpath_from_handle_u16(fd, context.temp_allocator) if len(wpath) == 0 {
if len(wpath) == 0 || err != ERROR_NONE {
return return
} }
dfi := make([dynamic]File_Info, 0, size) dfi := make([dynamic]File_Info, 0, size) or_return
wpath_search := make([]u16, len(wpath)+3, context.temp_allocator) wpath_search := make([]u16, len(wpath)+3, context.temp_allocator) or_return
copy(wpath_search, wpath) copy(wpath_search, wpath)
wpath_search[len(wpath)+0] = '\\' wpath_search[len(wpath)+0] = '\\'
wpath_search[len(wpath)+1] = '*' wpath_search[len(wpath)+1] = '*'
@@ -88,7 +89,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
find_data := &win32.WIN32_FIND_DATAW{} find_data := &win32.WIN32_FIND_DATAW{}
find_handle := win32.FindFirstFileW(raw_data(wpath_search), find_data) find_handle := win32.FindFirstFileW(raw_data(wpath_search), find_data)
if find_handle == win32.INVALID_HANDLE_VALUE { if find_handle == win32.INVALID_HANDLE_VALUE {
err = Errno(win32.GetLastError()) err = get_last_error()
return dfi[:], err return dfi[:], err
} }
defer win32.FindClose(find_handle) defer win32.FindClose(find_handle)
@@ -101,7 +102,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
} }
if !win32.FindNextFileW(find_handle, find_data) { if !win32.FindNextFileW(find_handle, find_data) {
e := Errno(win32.GetLastError()) e := get_last_error()
if e == ERROR_NO_MORE_FILES { if e == ERROR_NO_MORE_FILES {
break break
} }
@@ -109,5 +110,5 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
} }
} }
return dfi[:], ERROR_NONE return dfi[:], nil
} }
+19 -19
View File
@@ -7,27 +7,22 @@ import "base:runtime"
// If the variable is found in the environment the value (which can be empty) is returned and the boolean is true // If the variable is found in the environment the value (which can be empty) is returned and the boolean is true
// Otherwise the returned value will be empty and the boolean will be false // Otherwise the returned value will be empty and the boolean will be false
// NOTE: the value will be allocated with the supplied allocator // NOTE: the value will be allocated with the supplied allocator
@(require_results)
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
if key == "" { if key == "" {
return return
} }
wkey := win32.utf8_to_wstring(key) wkey := win32.utf8_to_wstring(key)
n := win32.GetEnvironmentVariableW(wkey, nil, 0) n := win32.GetEnvironmentVariableW(wkey, nil, 0)
if n == 0 { if n == 0 && get_last_error() == ERROR_ENVVAR_NOT_FOUND {
err := win32.GetLastError() return "", false
if err == u32(ERROR_ENVVAR_NOT_FOUND) {
return "", false
}
} }
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
b := make([dynamic]u16, n, context.temp_allocator) b, _ := make([dynamic]u16, n, context.temp_allocator)
n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b))) n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)))
if n == 0 { if n == 0 && get_last_error() == ERROR_ENVVAR_NOT_FOUND {
err := win32.GetLastError() return "", false
if err == u32(ERROR_ENVVAR_NOT_FOUND) {
return "", false
}
} }
value, _ = win32.utf16_to_utf8(b[:n], allocator) value, _ = win32.utf16_to_utf8(b[:n], allocator)
found = true found = true
@@ -39,41 +34,46 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
// It returns the value, which will be empty if the variable is not present // It returns the value, which will be empty if the variable is not present
// To distinguish between an empty value and an unset value, use lookup_env // To distinguish between an empty value and an unset value, use lookup_env
// NOTE: the value will be allocated with the supplied allocator // NOTE: the value will be allocated with the supplied allocator
@(require_results)
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) { get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
value, _ = lookup_env(key, allocator) value, _ = lookup_env(key, allocator)
return return
} }
// set_env sets the value of the environment variable named by the key // set_env sets the value of the environment variable named by the key
set_env :: proc(key, value: string) -> Errno { set_env :: proc(key, value: string) -> Error {
k := win32.utf8_to_wstring(key) k := win32.utf8_to_wstring(key)
v := win32.utf8_to_wstring(value) v := win32.utf8_to_wstring(value)
if !win32.SetEnvironmentVariableW(k, v) { if !win32.SetEnvironmentVariableW(k, v) {
return Errno(win32.GetLastError()) return get_last_error()
} }
return 0 return nil
} }
// unset_env unsets a single environment variable // unset_env unsets a single environment variable
unset_env :: proc(key: string) -> Errno { unset_env :: proc(key: string) -> Error {
k := win32.utf8_to_wstring(key) k := win32.utf8_to_wstring(key)
if !win32.SetEnvironmentVariableW(k, nil) { if !win32.SetEnvironmentVariableW(k, nil) {
return Errno(win32.GetLastError()) return get_last_error()
} }
return 0 return nil
} }
// environ returns a copy of strings representing the environment, in the form "key=value" // environ returns a copy of strings representing the environment, in the form "key=value"
// NOTE: the slice of strings and the strings with be allocated using the supplied allocator // NOTE: the slice of strings and the strings with be allocated using the supplied allocator
@(require_results)
environ :: proc(allocator := context.allocator) -> []string { environ :: proc(allocator := context.allocator) -> []string {
envs := cast([^]win32.WCHAR)(win32.GetEnvironmentStringsW()) envs := ([^]win32.WCHAR)(win32.GetEnvironmentStringsW())
if envs == nil { if envs == nil {
return nil return nil
} }
defer win32.FreeEnvironmentStringsW(envs) defer win32.FreeEnvironmentStringsW(envs)
r := make([dynamic]string, 0, 50, allocator) r, err := make([dynamic]string, 0, 50, allocator)
if err != nil {
return nil
}
for from, i := 0, 0; true; i += 1 { for from, i := 0, 0; true; i += 1 {
if c := envs[i]; c == 0 { if c := envs[i]; c == 0 {
if i <= from { if i <= from {
+322
View File
@@ -0,0 +1,322 @@
package os
import "base:intrinsics"
import "base:runtime"
import "core:io"
Platform_Error :: _Platform_Error
#assert(size_of(Platform_Error) <= 4)
#assert(intrinsics.type_has_nil(Platform_Error))
General_Error :: enum u32 {
None,
Permission_Denied,
Exist,
Not_Exist,
Closed,
Timeout,
Broken_Pipe,
// Indicates that an attempt to retrieve a file's size was made, but the
// file doesn't have a size.
No_Size,
Invalid_File,
Invalid_Dir,
Invalid_Path,
Invalid_Callback,
Pattern_Has_Separator,
Unsupported,
File_Is_Pipe,
Not_Dir,
}
Errno :: Error // alias for legacy use
Error :: union #shared_nil {
General_Error,
io.Error,
runtime.Allocator_Error,
Platform_Error,
}
#assert(size_of(Error) == 8)
ERROR_NONE :: Error{}
ERROR_EOF :: io.Error.EOF
@(require_results)
is_platform_error :: proc "contextless" (ferr: Error) -> (err: i32, ok: bool) {
v := ferr.(Platform_Error) or_else {}
return i32(v), i32(v) != 0
}
@(require_results)
error_string :: proc "contextless" (ferr: Error) -> string {
if ferr == nil {
return ""
}
switch e in ferr {
case General_Error:
switch e {
case .None: return ""
case .Permission_Denied: return "permission denied"
case .Exist: return "file already exists"
case .Not_Exist: return "file does not exist"
case .Closed: return "file already closed"
case .Timeout: return "i/o timeout"
case .Broken_Pipe: return "Broken pipe"
case .No_Size: return "file has no definite size"
case .Invalid_File: return "invalid file"
case .Invalid_Dir: return "invalid directory"
case .Invalid_Path: return "invalid path"
case .Invalid_Callback: return "invalid callback"
case .Unsupported: return "unsupported"
case .Pattern_Has_Separator: return "pattern has separator"
case .File_Is_Pipe: return "file is pipe"
case .Not_Dir: return "file is not directory"
}
case io.Error:
switch e {
case .None: return ""
case .EOF: return "eof"
case .Unexpected_EOF: return "unexpected eof"
case .Short_Write: return "short write"
case .Invalid_Write: return "invalid write result"
case .Short_Buffer: return "short buffer"
case .No_Progress: return "multiple read calls return no data or error"
case .Invalid_Whence: return "invalid whence"
case .Invalid_Offset: return "invalid offset"
case .Invalid_Unread: return "invalid unread"
case .Negative_Read: return "negative read"
case .Negative_Write: return "negative write"
case .Negative_Count: return "negative count"
case .Buffer_Full: return "buffer full"
case .Unknown, .Empty: //
}
case runtime.Allocator_Error:
switch e {
case .None: return ""
case .Out_Of_Memory: return "out of memory"
case .Invalid_Pointer: return "invalid allocator pointer"
case .Invalid_Argument: return "invalid allocator argument"
case .Mode_Not_Implemented: return "allocator mode not implemented"
}
case Platform_Error:
return _error_string(e)
}
return "unknown error"
}
print_error :: proc(f: Handle, ferr: Error, msg: string) -> (n: int, err: Error) {
err_str := error_string(ferr)
// msg + ": " + err_str + '\n'
length := len(msg) + 2 + len(err_str) + 1
buf_ := intrinsics.alloca(length, 1)
buf := buf_[:length]
copy(buf, msg)
buf[len(msg)] = ':'
buf[len(msg) + 1] = ' '
copy(buf[len(msg) + 2:], err_str)
buf[length - 1] = '\n'
return write(f, buf)
}
@(require_results, private)
_error_string :: proc "contextless" (e: Platform_Error) -> string where intrinsics.type_is_enum(Platform_Error) {
if e == nil {
return ""
}
when ODIN_OS == .Darwin {
if s := string(_darwin_string_error(i32(e))); s != "" {
return s
}
}
when ODIN_OS != .Linux {
@(require_results)
binary_search :: proc "contextless" (array: $A/[]$T, key: T) -> (index: int, found: bool) #no_bounds_check {
n := len(array)
left, right := 0, n
for left < right {
mid := int(uint(left+right) >> 1)
if array[mid] < key {
left = mid+1
} else {
// equal or greater
right = mid
}
}
return left, left < n && array[left] == key
}
err := runtime.Type_Info_Enum_Value(e)
ti := &runtime.type_info_base(type_info_of(Platform_Error)).variant.(runtime.Type_Info_Enum)
if idx, ok := binary_search(ti.values, err); ok {
return ti.names[idx]
}
} else {
@(rodata, static)
pe_strings := [Platform_Error]string{
.NONE = "",
.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",
}
if Platform_Error.NONE <= e && e <= max(Platform_Error) {
return pe_strings[e]
}
}
return "<unknown platform error>"
}
@(private, require_results)
error_to_io_error :: proc(ferr: Error) -> io.Error {
if ferr == nil {
return .None
}
return ferr.(io.Error) or_else .Unknown
}
+84 -94
View File
@@ -5,13 +5,15 @@ import "base:intrinsics"
import "base:runtime" import "base:runtime"
import "core:unicode/utf16" import "core:unicode/utf16"
@(require_results)
is_path_separator :: proc(c: byte) -> bool { is_path_separator :: proc(c: byte) -> bool {
return c == '/' || c == '\\' return c == '/' || c == '\\'
} }
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) { @(require_results)
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Error) {
if len(path) == 0 { if len(path) == 0 {
return INVALID_HANDLE, ERROR_FILE_NOT_FOUND return INVALID_HANDLE, General_Error.Not_Exist
} }
access: u32 access: u32
@@ -52,32 +54,31 @@ open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errn
wide_path := win32.utf8_to_wstring(path) wide_path := win32.utf8_to_wstring(path)
handle := Handle(win32.CreateFileW(wide_path, access, share_mode, sa, create_mode, win32.FILE_ATTRIBUTE_NORMAL|win32.FILE_FLAG_BACKUP_SEMANTICS, nil)) handle := Handle(win32.CreateFileW(wide_path, access, share_mode, sa, create_mode, win32.FILE_ATTRIBUTE_NORMAL|win32.FILE_FLAG_BACKUP_SEMANTICS, nil))
if handle != INVALID_HANDLE { if handle != INVALID_HANDLE {
return handle, ERROR_NONE return handle, nil
} }
err := Errno(win32.GetLastError()) return INVALID_HANDLE, get_last_error()
return INVALID_HANDLE, err
} }
close :: proc(fd: Handle) -> Errno { close :: proc(fd: Handle) -> Error {
if !win32.CloseHandle(win32.HANDLE(fd)) { if !win32.CloseHandle(win32.HANDLE(fd)) {
return Errno(win32.GetLastError()) return get_last_error()
} }
return ERROR_NONE return nil
} }
flush :: proc(fd: Handle) -> (err: Errno) { flush :: proc(fd: Handle) -> (err: Error) {
if !win32.FlushFileBuffers(win32.HANDLE(fd)) { if !win32.FlushFileBuffers(win32.HANDLE(fd)) {
err = Errno(win32.GetLastError()) err = get_last_error()
} }
return return
} }
write :: proc(fd: Handle, data: []byte) -> (int, Errno) { write :: proc(fd: Handle, data: []byte) -> (int, Error) {
if len(data) == 0 { if len(data) == 0 {
return 0, ERROR_NONE return 0, nil
} }
single_write_length: win32.DWORD single_write_length: win32.DWORD
@@ -90,25 +91,24 @@ write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
e := win32.WriteFile(win32.HANDLE(fd), &data[total_write], to_write, &single_write_length, nil) e := win32.WriteFile(win32.HANDLE(fd), &data[total_write], to_write, &single_write_length, nil)
if single_write_length <= 0 || !e { if single_write_length <= 0 || !e {
err := Errno(win32.GetLastError()) return int(total_write), get_last_error()
return int(total_write), err
} }
total_write += i64(single_write_length) total_write += i64(single_write_length)
} }
return int(total_write), ERROR_NONE return int(total_write), nil
} }
@(private="file") @(private="file", require_results)
read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) { read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Error) {
if len(b) == 0 { if len(b) == 0 {
return 0, 0 return 0, nil
} }
BUF_SIZE :: 386 BUF_SIZE :: 386
buf16: [BUF_SIZE]u16 buf16: [BUF_SIZE]u16
buf8: [4*BUF_SIZE]u8 buf8: [4*BUF_SIZE]u8
for n < len(b) && err == 0 { for n < len(b) && err == nil {
min_read := max(len(b)/4, 1 if len(b) > 0 else 0) min_read := max(len(b)/4, 1 if len(b) > 0 else 0)
max_read := u32(min(BUF_SIZE, min_read)) max_read := u32(min(BUF_SIZE, min_read))
if max_read == 0 { if max_read == 0 {
@@ -118,14 +118,14 @@ read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) {
single_read_length: u32 single_read_length: u32
ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil) ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil)
if !ok { if !ok {
err = Errno(win32.GetLastError()) err = get_last_error()
} }
buf8_len := utf16.decode_to_utf8(buf8[:], buf16[:single_read_length]) buf8_len := utf16.decode_to_utf8(buf8[:], buf16[:single_read_length])
src := buf8[:buf8_len] src := buf8[:buf8_len]
ctrl_z := false ctrl_z := false
for i := 0; i < len(src) && n+i < len(b); i += 1 { for i := 0; i < len(src) && n < len(b); i += 1 {
x := src[i] x := src[i]
if x == 0x1a { // ctrl-z if x == 0x1a { // ctrl-z
ctrl_z = true ctrl_z = true
@@ -149,9 +149,9 @@ read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) {
return return
} }
read :: proc(fd: Handle, data: []byte) -> (total_read: int, err: Errno) { read :: proc(fd: Handle, data: []byte) -> (total_read: int, err: Error) {
if len(data) == 0 { if len(data) == 0 {
return 0, ERROR_NONE return 0, nil
} }
handle := win32.HANDLE(fd) handle := win32.HANDLE(fd)
@@ -165,7 +165,7 @@ read :: proc(fd: Handle, data: []byte) -> (total_read: int, err: Errno) {
if is_console { if is_console {
total_read, err = read_console(handle, data[total_read:][:to_read]) total_read, err = read_console(handle, data[total_read:][:to_read])
if err != 0 { if err != nil {
return total_read, err return total_read, err
} }
} else { } else {
@@ -175,18 +175,18 @@ read :: proc(fd: Handle, data: []byte) -> (total_read: int, err: Errno) {
// Successful read can mean two things, including EOF, see: // Successful read can mean two things, including EOF, see:
// https://learn.microsoft.com/en-us/windows/win32/fileio/testing-for-the-end-of-a-file // https://learn.microsoft.com/en-us/windows/win32/fileio/testing-for-the-end-of-a-file
if bytes_read == 0 { if bytes_read == 0 {
return 0, ERROR_HANDLE_EOF return 0, .EOF
} else { } else {
return int(bytes_read), ERROR_NONE return int(bytes_read), nil
} }
} else { } else {
return 0, Errno(win32.GetLastError()) return 0, get_last_error()
} }
} }
return total_read, ERROR_NONE return total_read, nil
} }
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
w: u32 w: u32
switch whence { switch whence {
case 0: w = win32.FILE_BEGIN case 0: w = win32.FILE_BEGIN
@@ -197,22 +197,23 @@ seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
lo := i32(offset) lo := i32(offset)
ft := win32.GetFileType(win32.HANDLE(fd)) ft := win32.GetFileType(win32.HANDLE(fd))
if ft == win32.FILE_TYPE_PIPE { if ft == win32.FILE_TYPE_PIPE {
return 0, ERROR_FILE_IS_PIPE return 0, .File_Is_Pipe
} }
dw_ptr := win32.SetFilePointer(win32.HANDLE(fd), lo, &hi, w) dw_ptr := win32.SetFilePointer(win32.HANDLE(fd), lo, &hi, w)
if dw_ptr == win32.INVALID_SET_FILE_POINTER { if dw_ptr == win32.INVALID_SET_FILE_POINTER {
err := Errno(win32.GetLastError()) err := get_last_error()
return 0, err return 0, err
} }
return i64(hi)<<32 + i64(dw_ptr), ERROR_NONE return i64(hi)<<32 + i64(dw_ptr), nil
} }
file_size :: proc(fd: Handle) -> (i64, Errno) { @(require_results)
file_size :: proc(fd: Handle) -> (i64, Error) {
length: win32.LARGE_INTEGER length: win32.LARGE_INTEGER
err: Errno err: Error
if !win32.GetFileSizeEx(win32.HANDLE(fd), &length) { if !win32.GetFileSizeEx(win32.HANDLE(fd), &length) {
err = Errno(win32.GetLastError()) err = get_last_error()
} }
return i64(length), err return i64(length), err
} }
@@ -220,10 +221,9 @@ file_size :: proc(fd: Handle) -> (i64, Errno) {
@(private) @(private)
MAX_RW :: 1<<30 MAX_RW :: 1<<30
ERROR_EOF :: 38
@(private) @(private)
pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) {
buf := data buf := data
if len(buf) > MAX_RW { if len(buf) > MAX_RW {
buf = buf[:MAX_RW] buf = buf[:MAX_RW]
@@ -239,15 +239,15 @@ pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
h := win32.HANDLE(fd) h := win32.HANDLE(fd)
done: win32.DWORD done: win32.DWORD
e: Errno e: Error
if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) { if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
e = Errno(win32.GetLastError()) e = get_last_error()
done = 0 done = 0
} }
return int(done), e return int(done), e
} }
@(private) @(private)
pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) {
buf := data buf := data
if len(buf) > MAX_RW { if len(buf) > MAX_RW {
buf = buf[:MAX_RW] buf = buf[:MAX_RW]
@@ -261,9 +261,9 @@ pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
h := win32.HANDLE(fd) h := win32.HANDLE(fd)
done: win32.DWORD done: win32.DWORD
e: Errno e: Error
if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) { if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
e = Errno(win32.GetLastError()) e = get_last_error()
done = 0 done = 0
} }
return int(done), e return int(done), e
@@ -279,19 +279,19 @@ on Windows, read_at changes the position of the file cursor, on *nix, it does no
will read from the location twice on *nix, and from two different locations on Windows will read from the location twice on *nix, and from two different locations on Windows
*/ */
read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno) { read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
if offset < 0 { if offset < 0 {
return 0, ERROR_NEGATIVE_OFFSET return 0, .Invalid_Offset
} }
b, offset := data, offset b, offset := data, offset
for len(b) > 0 { for len(b) > 0 {
m, e := pread(fd, b, offset) m, e := pread(fd, b, offset)
if e == ERROR_EOF { if e == ERROR_EOF {
err = 0 err = nil
break break
} }
if e != 0 { if e != nil {
err = e err = e
break break
} }
@@ -311,18 +311,14 @@ on Windows, write_at changes the position of the file cursor, on *nix, it does n
will write to the location twice on *nix, and to two different locations on Windows will write to the location twice on *nix, and to two different locations on Windows
*/ */
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno) { write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
if offset < 0 { if offset < 0 {
return 0, ERROR_NEGATIVE_OFFSET return 0, .Invalid_Offset
} }
b, offset := data, offset b, offset := data, offset
for len(b) > 0 { for len(b) > 0 {
m, e := pwrite(fd, b, offset) m := pwrite(fd, b, offset) or_return
if e != 0 {
err = e
break
}
n += m n += m
b = b[m:] b = b[m:]
offset += i64(m) offset += i64(m)
@@ -338,6 +334,7 @@ stdout := get_std_handle(uint(win32.STD_OUTPUT_HANDLE))
stderr := get_std_handle(uint(win32.STD_ERROR_HANDLE)) stderr := get_std_handle(uint(win32.STD_ERROR_HANDLE))
@(require_results)
get_std_handle :: proc "contextless" (h: uint) -> Handle { get_std_handle :: proc "contextless" (h: uint) -> Handle {
fd := win32.GetStdHandle(win32.DWORD(h)) fd := win32.GetStdHandle(win32.DWORD(h))
return Handle(fd) return Handle(fd)
@@ -352,6 +349,7 @@ exists :: proc(path: string) -> bool {
return attribs != win32.INVALID_FILE_ATTRIBUTES return attribs != win32.INVALID_FILE_ATTRIBUTES
} }
@(require_results)
is_file :: proc(path: string) -> bool { is_file :: proc(path: string) -> bool {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wpath := win32.utf8_to_wstring(path, context.temp_allocator) wpath := win32.utf8_to_wstring(path, context.temp_allocator)
@@ -363,6 +361,7 @@ is_file :: proc(path: string) -> bool {
return false return false
} }
@(require_results)
is_dir :: proc(path: string) -> bool { is_dir :: proc(path: string) -> bool {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wpath := win32.utf8_to_wstring(path, context.temp_allocator) wpath := win32.utf8_to_wstring(path, context.temp_allocator)
@@ -377,13 +376,14 @@ is_dir :: proc(path: string) -> bool {
// NOTE(tetra): GetCurrentDirectory is not thread safe with SetCurrentDirectory and GetFullPathName // NOTE(tetra): GetCurrentDirectory is not thread safe with SetCurrentDirectory and GetFullPathName
@private cwd_lock := win32.SRWLOCK{} // zero is initialized @private cwd_lock := win32.SRWLOCK{} // zero is initialized
@(require_results)
get_current_directory :: proc(allocator := context.allocator) -> string { get_current_directory :: proc(allocator := context.allocator) -> string {
win32.AcquireSRWLockExclusive(&cwd_lock) win32.AcquireSRWLockExclusive(&cwd_lock)
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
sz_utf16 := win32.GetCurrentDirectoryW(0, nil) sz_utf16 := win32.GetCurrentDirectoryW(0, nil)
dir_buf_wstr := make([]u16, sz_utf16, context.temp_allocator) // the first time, it _includes_ the NUL. dir_buf_wstr, _ := make([]u16, sz_utf16, context.temp_allocator) // the first time, it _includes_ the NUL.
sz_utf16 = win32.GetCurrentDirectoryW(win32.DWORD(len(dir_buf_wstr)), raw_data(dir_buf_wstr)) sz_utf16 = win32.GetCurrentDirectoryW(win32.DWORD(len(dir_buf_wstr)), raw_data(dir_buf_wstr))
assert(int(sz_utf16)+1 == len(dir_buf_wstr)) // the second time, it _excludes_ the NUL. assert(int(sz_utf16)+1 == len(dir_buf_wstr)) // the second time, it _excludes_ the NUL.
@@ -393,14 +393,14 @@ get_current_directory :: proc(allocator := context.allocator) -> string {
return win32.utf16_to_utf8(dir_buf_wstr, allocator) or_else "" return win32.utf16_to_utf8(dir_buf_wstr, allocator) or_else ""
} }
set_current_directory :: proc(path: string) -> (err: Errno) { set_current_directory :: proc(path: string) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wstr := win32.utf8_to_wstring(path, context.temp_allocator) wstr := win32.utf8_to_wstring(path, context.temp_allocator)
win32.AcquireSRWLockExclusive(&cwd_lock) win32.AcquireSRWLockExclusive(&cwd_lock)
if !win32.SetCurrentDirectoryW(wstr) { if !win32.SetCurrentDirectoryW(wstr) {
err = Errno(win32.GetLastError()) err = get_last_error()
} }
win32.ReleaseSRWLockExclusive(&cwd_lock) win32.ReleaseSRWLockExclusive(&cwd_lock)
@@ -409,31 +409,31 @@ set_current_directory :: proc(path: string) -> (err: Errno) {
} }
change_directory :: set_current_directory change_directory :: set_current_directory
make_directory :: proc(path: string, mode: u32 = 0) -> (err: Errno) { make_directory :: proc(path: string, mode: u32 = 0) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
// Mode is unused on Windows, but is needed on *nix // Mode is unused on Windows, but is needed on *nix
wpath := win32.utf8_to_wstring(path, context.temp_allocator) wpath := win32.utf8_to_wstring(path, context.temp_allocator)
if !win32.CreateDirectoryW(wpath, nil) { if !win32.CreateDirectoryW(wpath, nil) {
err = Errno(win32.GetLastError()) err = get_last_error()
} }
return return
} }
remove_directory :: proc(path: string) -> (err: Errno) { remove_directory :: proc(path: string) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wpath := win32.utf8_to_wstring(path, context.temp_allocator) wpath := win32.utf8_to_wstring(path, context.temp_allocator)
if !win32.RemoveDirectoryW(wpath) { if !win32.RemoveDirectoryW(wpath) {
err = Errno(win32.GetLastError()) err = get_last_error()
} }
return return
} }
@(private) @(private, require_results)
is_abs :: proc(path: string) -> bool { is_abs :: proc(path: string) -> bool {
if len(path) > 0 && path[0] == '/' { if len(path) > 0 && path[0] == '/' {
return true return true
@@ -449,7 +449,7 @@ is_abs :: proc(path: string) -> bool {
return false return false
} }
@(private) @(private, require_results)
fix_long_path :: proc(path: string) -> string { fix_long_path :: proc(path: string) -> string {
if len(path) < 248 { if len(path) < 248 {
return path return path
@@ -464,7 +464,7 @@ fix_long_path :: proc(path: string) -> string {
prefix :: `\\?` prefix :: `\\?`
path_buf := make([]byte, len(prefix)+len(path)+len(`\`), context.temp_allocator) path_buf, _ := make([]byte, len(prefix)+len(path)+len(`\`), context.temp_allocator)
copy(path_buf, prefix) copy(path_buf, prefix)
n := len(path) n := len(path)
r, w := 0, len(prefix) r, w := 0, len(prefix)
@@ -494,80 +494,69 @@ fix_long_path :: proc(path: string) -> string {
} }
link :: proc(old_name, new_name: string) -> (err: Errno) { link :: proc(old_name, new_name: string) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
n := win32.utf8_to_wstring(fix_long_path(new_name)) n := win32.utf8_to_wstring(fix_long_path(new_name))
o := win32.utf8_to_wstring(fix_long_path(old_name)) o := win32.utf8_to_wstring(fix_long_path(old_name))
return Errno(win32.CreateHardLinkW(n, o, nil)) return Platform_Error(win32.CreateHardLinkW(n, o, nil))
} }
unlink :: proc(path: string) -> (err: Errno) { unlink :: proc(path: string) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wpath := win32.utf8_to_wstring(path, context.temp_allocator) wpath := win32.utf8_to_wstring(path, context.temp_allocator)
if !win32.DeleteFileW(wpath) { if !win32.DeleteFileW(wpath) {
err = Errno(win32.GetLastError()) err = get_last_error()
} }
return return
} }
rename :: proc(old_path, new_path: string) -> (err: Errno) { rename :: proc(old_path, new_path: string) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
from := win32.utf8_to_wstring(old_path, context.temp_allocator) from := win32.utf8_to_wstring(old_path, context.temp_allocator)
to := win32.utf8_to_wstring(new_path, context.temp_allocator) to := win32.utf8_to_wstring(new_path, context.temp_allocator)
if !win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) { if !win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) {
err = Errno(win32.GetLastError()) err = get_last_error()
} }
return return
} }
ftruncate :: proc(fd: Handle, length: i64) -> (err: Errno) { ftruncate :: proc(fd: Handle, length: i64) -> (err: Error) {
curr_off, e := seek(fd, 0, 1) curr_off := seek(fd, 0, 1) or_return
if e != 0 {
return e
}
defer seek(fd, curr_off, 0) defer seek(fd, curr_off, 0)
_, e = seek(fd, length, 0) _= seek(fd, length, 0) or_return
if e != 0 {
return e
}
ok := win32.SetEndOfFile(win32.HANDLE(fd)) ok := win32.SetEndOfFile(win32.HANDLE(fd))
if !ok { if !ok {
return Errno(win32.GetLastError()) return get_last_error()
} }
return ERROR_NONE return nil
} }
truncate :: proc(path: string, length: i64) -> (err: Errno) { truncate :: proc(path: string, length: i64) -> (err: Error) {
fd: Handle fd := open(path, O_WRONLY|O_CREATE, 0o666) or_return
fd, err = open(path, O_WRONLY|O_CREATE, 0o666)
if err != 0 {
return
}
defer close(fd) defer close(fd)
err = ftruncate(fd, length) return ftruncate(fd, length)
return
} }
remove :: proc(name: string) -> Errno { remove :: proc(name: string) -> Error {
p := win32.utf8_to_wstring(fix_long_path(name)) p := win32.utf8_to_wstring(fix_long_path(name))
err, err1: win32.DWORD err, err1: win32.DWORD
if !win32.DeleteFileW(p) { if !win32.DeleteFileW(p) {
err = win32.GetLastError() err = win32.GetLastError()
} }
if err == 0 { if err == 0 {
return 0 return nil
} }
if !win32.RemoveDirectoryW(p) { if !win32.RemoveDirectoryW(p) {
err1 = win32.GetLastError() err1 = win32.GetLastError()
} }
if err1 == 0 { if err1 == 0 {
return 0 return nil
} }
if err != err1 { if err != err1 {
@@ -588,16 +577,17 @@ remove :: proc(name: string) -> Errno {
} }
} }
return Errno(err) return Platform_Error(err)
} }
pipe :: proc() -> (r, w: Handle, err: Errno) { @(require_results)
pipe :: proc() -> (r, w: Handle, err: Error) {
sa: win32.SECURITY_ATTRIBUTES sa: win32.SECURITY_ATTRIBUTES
sa.nLength = size_of(win32.SECURITY_ATTRIBUTES) sa.nLength = size_of(win32.SECURITY_ATTRIBUTES)
sa.bInheritHandle = true sa.bInheritHandle = true
if !win32.CreatePipe((^win32.HANDLE)(&r), (^win32.HANDLE)(&w), &sa, 0) { if !win32.CreatePipe((^win32.HANDLE)(&r), (^win32.HANDLE)(&w), &sa, 0) {
err = Errno(win32.GetLastError()) err = get_last_error()
} }
return return
} }
+98 -66
View File
@@ -1,6 +1,8 @@
package os package os
import "base:intrinsics"
import "base:runtime" import "base:runtime"
import "core:io"
import "core:strconv" import "core:strconv"
import "core:unicode/utf8" import "core:unicode/utf8"
@@ -13,15 +15,15 @@ SEEK_SET :: 0
SEEK_CUR :: 1 SEEK_CUR :: 1
SEEK_END :: 2 SEEK_END :: 2
write_string :: proc(fd: Handle, str: string) -> (int, Errno) { write_string :: proc(fd: Handle, str: string) -> (int, Error) {
return write(fd, transmute([]byte)str) return write(fd, transmute([]byte)str)
} }
write_byte :: proc(fd: Handle, b: byte) -> (int, Errno) { write_byte :: proc(fd: Handle, b: byte) -> (int, Error) {
return write(fd, []byte{b}) return write(fd, []byte{b})
} }
write_rune :: proc(fd: Handle, r: rune) -> (int, Errno) { write_rune :: proc(fd: Handle, r: rune) -> (int, Error) {
if r < utf8.RUNE_SELF { if r < utf8.RUNE_SELF {
return write_byte(fd, byte(r)) return write_byte(fd, byte(r))
} }
@@ -30,105 +32,94 @@ write_rune :: proc(fd: Handle, r: rune) -> (int, Errno) {
return write(fd, b[:n]) return write(fd, b[:n])
} }
write_encoded_rune :: proc(fd: Handle, r: rune) { write_encoded_rune :: proc(f: Handle, r: rune) -> (n: int, err: Error) {
write_byte(fd, '\'') wrap :: proc(m: int, merr: Error, n: ^int, err: ^Error) -> bool {
n^ += m
if merr != nil {
err^ = merr
return true
}
return false
}
if wrap(write_byte(f, '\''), &n, &err) { return }
switch r { switch r {
case '\a': write_string(fd, "\\a") case '\a': if wrap(write_string(f, "\\a"), &n, &err) { return }
case '\b': write_string(fd, "\\b") case '\b': if wrap(write_string(f, "\\b"), &n, &err) { return }
case '\e': write_string(fd, "\\e") case '\e': if wrap(write_string(f, "\\e"), &n, &err) { return }
case '\f': write_string(fd, "\\f") case '\f': if wrap(write_string(f, "\\f"), &n, &err) { return }
case '\n': write_string(fd, "\\n") case '\n': if wrap(write_string(f, "\\n"), &n, &err) { return }
case '\r': write_string(fd, "\\r") case '\r': if wrap(write_string(f, "\\r"), &n, &err) { return }
case '\t': write_string(fd, "\\t") case '\t': if wrap(write_string(f, "\\t"), &n, &err) { return }
case '\v': write_string(fd, "\\v") case '\v': if wrap(write_string(f, "\\v"), &n, &err) { return }
case: case:
if r < 32 { if r < 32 {
write_string(fd, "\\x") if wrap(write_string(f, "\\x"), &n, &err) { return }
b: [2]byte b: [2]byte
s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil) s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil)
switch len(s) { switch len(s) {
case 0: write_string(fd, "00") case 0: if wrap(write_string(f, "00"), &n, &err) { return }
case 1: write_rune(fd, '0') case 1: if wrap(write_rune(f, '0'), &n, &err) { return }
case 2: write_string(fd, s) case 2: if wrap(write_string(f, s), &n, &err) { return }
} }
} else { } else {
write_rune(fd, r) if wrap(write_rune(f, r), &n, &err) { return }
} }
} }
write_byte(fd, '\'') _ = wrap(write_byte(f, '\''), &n, &err)
return
} }
read_at_least :: proc(fd: Handle, buf: []byte, min: int) -> (n: int, err: Errno) { read_at_least :: proc(fd: Handle, buf: []byte, min: int) -> (n: int, err: Error) {
if len(buf) < min { if len(buf) < min {
return 0, -1 return 0, io.Error.Short_Buffer
} }
nn := max(int) nn := max(int)
for nn > 0 && n < min && err == 0 { for nn > 0 && n < min && err == nil {
nn, err = read(fd, buf[n:]) nn, err = read(fd, buf[n:])
n += nn n += nn
} }
if n >= min { if n >= min {
err = 0 err = nil
} }
return return
} }
read_full :: proc(fd: Handle, buf: []byte) -> (n: int, err: Errno) { read_full :: proc(fd: Handle, buf: []byte) -> (n: int, err: Error) {
return read_at_least(fd, buf, len(buf)) return read_at_least(fd, buf, len(buf))
} }
@(require_results)
file_size_from_path :: proc(path: string) -> i64 { file_size_from_path :: proc(path: string) -> i64 {
fd, err := open(path, O_RDONLY, 0) fd, err := open(path, O_RDONLY, 0)
if err != 0 { if err != nil {
return -1 return -1
} }
defer close(fd) defer close(fd)
length: i64 length: i64
if length, err = file_size(fd); err != 0 { if length, err = file_size(fd); err != nil {
return -1 return -1
} }
return length return length
} }
@(require_results)
read_entire_file_from_filename :: proc(name: string, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) { read_entire_file_from_filename :: proc(name: string, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) {
context.allocator = allocator err: Error
data, err = read_entire_file_from_filename_or_err(name, allocator, loc)
fd, err := open(name, O_RDONLY, 0) success = err == nil
if err != 0 { return
return nil, false
}
defer close(fd)
return read_entire_file_from_handle(fd, allocator, loc)
} }
@(require_results)
read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) { read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) {
context.allocator = allocator err: Error
data, err = read_entire_file_from_handle_or_err(fd, allocator, loc)
length: i64 success = err == nil
err: Errno return
if length, err = file_size(fd); err != 0 {
return nil, false
}
if length <= 0 {
return nil, true
}
data = make([]byte, int(length), allocator, loc)
if data == nil {
return nil, false
}
bytes_read, read_err := read_full(fd, data)
if read_err != ERROR_NONE {
delete(data)
return nil, false
}
return data[:bytes_read], true
} }
read_entire_file :: proc { read_entire_file :: proc {
@@ -136,7 +127,50 @@ read_entire_file :: proc {
read_entire_file_from_handle, read_entire_file_from_handle,
} }
@(require_results)
read_entire_file_from_filename_or_err :: proc(name: string, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Error) {
context.allocator = allocator
fd := open(name, O_RDONLY, 0) or_return
defer close(fd)
return read_entire_file_from_handle_or_err(fd, allocator, loc)
}
@(require_results)
read_entire_file_from_handle_or_err :: proc(fd: Handle, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Error) {
context.allocator = allocator
length := file_size(fd) or_return
if length <= 0 {
return nil, nil
}
data = make([]byte, int(length), allocator, loc) or_return
if data == nil {
return nil, nil
}
defer if err != nil {
delete(data, allocator)
}
bytes_read := read_full(fd, data) or_return
data = data[:bytes_read]
return
}
read_entire_file_or_err :: proc {
read_entire_file_from_filename_or_err,
read_entire_file_from_handle_or_err,
}
write_entire_file :: proc(name: string, data: []byte, truncate := true) -> (success: bool) { write_entire_file :: proc(name: string, data: []byte, truncate := true) -> (success: bool) {
return write_entire_file_or_err(name, data, truncate) == nil
}
@(require_results)
write_entire_file_or_err :: proc(name: string, data: []byte, truncate := true) -> Error {
flags: int = O_WRONLY|O_CREATE flags: int = O_WRONLY|O_CREATE
if truncate { if truncate {
flags |= O_TRUNC flags |= O_TRUNC
@@ -148,21 +182,18 @@ write_entire_file :: proc(name: string, data: []byte, truncate := true) -> (succ
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
} }
fd, err := open(name, flags, mode) fd := open(name, flags, mode) or_return
if err != 0 {
return false
}
defer close(fd) defer close(fd)
_, write_err := write(fd, data) _ = write(fd, data) or_return
return write_err == 0 return nil
} }
write_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) { write_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Error) {
return write(fd, ([^]byte)(data)[:len]) return write(fd, ([^]byte)(data)[:len])
} }
read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) { read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Error) {
return read(fd, ([^]byte)(data)[:len]) return read(fd, ([^]byte)(data)[:len])
} }
@@ -173,6 +204,7 @@ heap_alloc :: runtime.heap_alloc
heap_resize :: runtime.heap_resize heap_resize :: runtime.heap_resize
heap_free :: runtime.heap_free heap_free :: runtime.heap_free
@(require_results)
processor_core_count :: proc() -> int { processor_core_count :: proc() -> int {
return _processor_core_count() return _processor_core_count()
} }
+27 -11
View File
@@ -10,20 +10,29 @@ file_allocator :: proc() -> runtime.Allocator {
temp_allocator_proc :: runtime.arena_allocator_proc temp_allocator_proc :: runtime.arena_allocator_proc
@(private="file")
MAX_TEMP_ARENA_COUNT :: 2
@(private="file", thread_local) @(private="file", thread_local)
global_default_temp_allocator_arena: runtime.Arena global_default_temp_allocator_arenas: [MAX_TEMP_ARENA_COUNT]runtime.Arena
@(private="file", thread_local)
global_default_temp_allocator_index: uint
@(require_results) @(require_results)
temp_allocator :: proc() -> runtime.Allocator { temp_allocator :: proc() -> runtime.Allocator {
return runtime.Allocator{ return runtime.Allocator{
procedure = temp_allocator_proc, procedure = temp_allocator_proc,
data = &global_default_temp_allocator_arena, data = &global_default_temp_allocator_arenas[global_default_temp_allocator_index],
} }
} }
@(require_results) @(require_results)
temp_allocator_temp_begin :: proc(loc := #caller_location) -> (temp: runtime.Arena_Temp) { temp_allocator_temp_begin :: proc(loc := #caller_location) -> (temp: runtime.Arena_Temp) {
temp = runtime.arena_temp_begin(&global_default_temp_allocator_arena, loc) temp = runtime.arena_temp_begin(&global_default_temp_allocator_arenas[global_default_temp_allocator_index], loc)
return return
} }
@@ -33,16 +42,23 @@ temp_allocator_temp_end :: proc(temp: runtime.Arena_Temp, loc := #caller_locatio
@(fini, private) @(fini, private)
temp_allocator_fini :: proc() { temp_allocator_fini :: proc() {
runtime.arena_destroy(&global_default_temp_allocator_arena) for &arena in global_default_temp_allocator_arenas {
global_default_temp_allocator_arena = {} runtime.arena_destroy(&arena)
}
global_default_temp_allocator_arenas = {}
} }
@(deferred_out=temp_allocator_temp_end) TEMP_ALLOCATOR_GUARD_END :: proc(temp: runtime.Arena_Temp, loc := #caller_location) {
TEMP_ALLOCATOR_GUARD :: #force_inline proc(ignore := false, loc := #caller_location) -> (runtime.Arena_Temp, runtime.Source_Code_Location) { runtime.arena_temp_end(temp, loc)
if ignore { if temp.arena != nil {
return {}, loc global_default_temp_allocator_index = (global_default_temp_allocator_index-1)%MAX_TEMP_ARENA_COUNT
} else {
return temp_allocator_temp_begin(loc), loc
} }
} }
@(deferred_out=TEMP_ALLOCATOR_GUARD_END)
TEMP_ALLOCATOR_GUARD :: #force_inline proc(loc := #caller_location) -> (runtime.Arena_Temp, runtime.Source_Code_Location) {
tmp := temp_allocator_temp_begin(loc)
global_default_temp_allocator_index = (global_default_temp_allocator_index+1)%MAX_TEMP_ARENA_COUNT
return tmp, loc
}
+82
View File
@@ -0,0 +1,82 @@
package os2
import "base:runtime"
import "core:slice"
read_dir :: read_directory
@(require_results)
read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (files: []File_Info, err: Error) {
if f == nil {
return nil, .Invalid_File
}
n := n
size := n
if n <= 0 {
n = -1
size = 100
}
TEMP_ALLOCATOR_GUARD()
it := read_directory_iterator_create(f) or_return
defer _read_directory_iterator_destroy(&it)
dfi := make([dynamic]File_Info, 0, size, temp_allocator())
defer if err != nil {
for fi in dfi {
file_info_delete(fi, allocator)
}
}
for fi, index in read_directory_iterator(&it) {
if n > 0 && index == n {
break
}
append(&dfi, file_info_clone(fi, allocator) or_return)
}
return slice.clone(dfi[:], allocator)
}
@(require_results)
read_all_directory :: proc(f: ^File, allocator: runtime.Allocator) -> (fi: []File_Info, err: Error) {
return read_directory(f, -1, allocator)
}
@(require_results)
read_directory_by_path :: proc(path: string, n: int, allocator: runtime.Allocator) -> (fi: []File_Info, err: Error) {
f := open(path) or_return
defer close(f)
return read_directory(f, n, allocator)
}
@(require_results)
read_all_directory_by_path :: proc(path: string, allocator: runtime.Allocator) -> (fi: []File_Info, err: Error) {
return read_directory_by_path(path, -1, allocator)
}
Read_Directory_Iterator :: struct {
f: ^File,
impl: Read_Directory_Iterator_Impl,
}
@(require_results)
read_directory_iterator_create :: proc(f: ^File) -> (Read_Directory_Iterator, Error) {
return _read_directory_iterator_create(f)
}
read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
_read_directory_iterator_destroy(it)
}
// NOTE(bill): `File_Info` does not need to deleted on each iteration. Any copies must be manually copied with `file_info_clone`
@(require_results)
read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
return _read_directory_iterator(it)
}

Some files were not shown because too many files have changed in this diff Show More