mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-25 23:14:59 -07:00
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:
@@ -6,7 +6,7 @@ jobs:
|
||||
name: NetBSD Build, Check, and Test
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
PKGSRC_BRANCH: 2024Q1
|
||||
PKGSRC_BRANCH: 2024Q2
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build, Check, and Test
|
||||
@@ -18,13 +18,11 @@ jobs:
|
||||
usesh: true
|
||||
copyback: false
|
||||
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
|
||||
pkgin -y in gmake git bash python311
|
||||
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
|
||||
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 llvm clang
|
||||
ln -s /usr/pkg/bin/python3.11 /usr/bin/python3
|
||||
run: |
|
||||
set -e -x
|
||||
git config --global --add safe.directory $(pwd)
|
||||
gmake release
|
||||
./odin version
|
||||
@@ -91,13 +89,13 @@ jobs:
|
||||
- name: Download LLVM (MacOS Intel)
|
||||
if: matrix.os == 'macos-13'
|
||||
run: |
|
||||
brew install llvm@17
|
||||
brew install llvm@17 lua@5.4
|
||||
echo "/usr/local/opt/llvm@17/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Download LLVM (MacOS ARM)
|
||||
if: matrix.os == 'macos-14'
|
||||
run: |
|
||||
brew install llvm@17 wasmtime
|
||||
brew install llvm@17 wasmtime lua@5.4
|
||||
echo "/opt/homebrew/opt/llvm@17/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Build Odin
|
||||
@@ -207,6 +205,7 @@ jobs:
|
||||
shell: cmd
|
||||
run: |
|
||||
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
|
||||
- name: Odin internals tests
|
||||
shell: cmd
|
||||
|
||||
@@ -50,8 +50,8 @@ jobs:
|
||||
run: |
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh 17
|
||||
echo "/usr/lib/llvm-17/bin" >> $GITHUB_PATH
|
||||
sudo ./llvm.sh 18
|
||||
echo "/usr/lib/llvm-18/bin" >> $GITHUB_PATH
|
||||
- name: build odin
|
||||
run: make nightly
|
||||
- name: Odin run
|
||||
@@ -82,8 +82,8 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Download LLVM and setup PATH
|
||||
run: |
|
||||
brew install llvm@17 dylibbundler
|
||||
echo "/usr/local/opt/llvm@17/bin" >> $GITHUB_PATH
|
||||
brew install llvm@18 dylibbundler
|
||||
echo "/usr/local/opt/llvm@18/bin" >> $GITHUB_PATH
|
||||
- name: build odin
|
||||
# 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.
|
||||
@@ -116,8 +116,8 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Download LLVM and setup PATH
|
||||
run: |
|
||||
brew install llvm@17 dylibbundler
|
||||
echo "/opt/homebrew/opt/llvm@17/bin" >> $GITHUB_PATH
|
||||
brew install llvm@18 dylibbundler
|
||||
echo "/opt/homebrew/opt/llvm@18/bin" >> $GITHUB_PATH
|
||||
- name: build odin
|
||||
# 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.
|
||||
|
||||
+1
-32
@@ -24,38 +24,6 @@ bld/
|
||||
![Cc]ore/[Ll]og/
|
||||
tests/documentation/verify/
|
||||
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
|
||||
.vs/
|
||||
# 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
|
||||
#wwwroot/
|
||||
demo
|
||||
benchmark
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
|
||||
BIN
Binary file not shown.
@@ -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) ---
|
||||
byte_swap :: proc(x: $T) -> T where type_is_integer(T) || type_is_float(T) ---
|
||||
|
||||
overflow_add :: proc(lhs, rhs: $T) -> (T, bool) ---
|
||||
overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) ---
|
||||
overflow_mul :: 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) where type_is_integer(T) #optional_ok ---
|
||||
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))) ---
|
||||
|
||||
|
||||
+36
-23
@@ -66,7 +66,7 @@ Type_Info_Named :: struct {
|
||||
name: string,
|
||||
base: ^Type_Info,
|
||||
pkg: string,
|
||||
loc: Source_Code_Location,
|
||||
loc: ^Source_Code_Location,
|
||||
}
|
||||
Type_Info_Integer :: struct {signed: bool, endianness: Platform_Endianness}
|
||||
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_Struct :: struct {
|
||||
types: []^Type_Info,
|
||||
names: []string,
|
||||
offsets: []uintptr,
|
||||
usings: []bool,
|
||||
tags: []string,
|
||||
is_packed: bool,
|
||||
is_raw_union: bool,
|
||||
is_no_copy: bool,
|
||||
custom_align: bool,
|
||||
Type_Info_Struct_Flags :: distinct bit_set[Type_Info_Struct_Flag; u8]
|
||||
Type_Info_Struct_Flag :: enum u8 {
|
||||
packed = 0,
|
||||
raw_union = 1,
|
||||
no_copy = 2,
|
||||
align = 3,
|
||||
}
|
||||
|
||||
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
|
||||
soa_kind: Type_Info_Struct_Soa_Kind,
|
||||
soa_len: i32,
|
||||
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 {
|
||||
variants: []^Type_Info,
|
||||
@@ -142,9 +151,9 @@ Type_Info_Union :: struct {
|
||||
shared_nil: bool,
|
||||
}
|
||||
Type_Info_Enum :: struct {
|
||||
base: ^Type_Info,
|
||||
names: []string,
|
||||
values: []Type_Info_Enum_Value,
|
||||
base: ^Type_Info,
|
||||
names: []string,
|
||||
values: []Type_Info_Enum_Value,
|
||||
}
|
||||
Type_Info_Map :: struct {
|
||||
key: ^Type_Info,
|
||||
@@ -187,11 +196,12 @@ Type_Info_Soa_Pointer :: struct {
|
||||
}
|
||||
Type_Info_Bit_Field :: struct {
|
||||
backing_type: ^Type_Info,
|
||||
names: []string,
|
||||
types: []^Type_Info,
|
||||
bit_sizes: []uintptr,
|
||||
bit_offsets: []uintptr,
|
||||
tags: []string,
|
||||
names: [^]string `fmt:"v,field_count"`,
|
||||
types: [^]^Type_Info `fmt:"v,field_count"`,
|
||||
bit_sizes: [^]uintptr `fmt:"v,field_count"`,
|
||||
bit_offsets: [^]uintptr `fmt:"v,field_count"`,
|
||||
tags: [^]string `fmt:"v,field_count"`,
|
||||
field_count: int,
|
||||
}
|
||||
|
||||
Type_Info_Flag :: enum u8 {
|
||||
@@ -299,6 +309,8 @@ when ODIN_OS == .Windows {
|
||||
Thread_Detach = 3,
|
||||
}
|
||||
dll_forward_reason: DLL_Forward_Reason
|
||||
|
||||
dll_instance: rawptr
|
||||
}
|
||||
|
||||
// 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,
|
||||
Essence,
|
||||
FreeBSD,
|
||||
Haiku,
|
||||
OpenBSD,
|
||||
NetBSD,
|
||||
Haiku,
|
||||
WASI,
|
||||
JS,
|
||||
Orca,
|
||||
Freestanding,
|
||||
}
|
||||
*/
|
||||
@@ -577,7 +590,7 @@ Odin_Platform_Subtarget_Type :: type_of(ODIN_PLATFORM_SUBTARGET)
|
||||
Memory = 1,
|
||||
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
|
||||
*/
|
||||
|
||||
+106
-101
@@ -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`.
|
||||
@(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_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(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
|
||||
err = _make_dynamic_array_len_cap((^Raw_Dynamic_Array)(&array), size_of(E), align_of(E), len, cap, allocator, loc)
|
||||
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.
|
||||
// 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
|
||||
}
|
||||
|
||||
_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 {
|
||||
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 should_zero {
|
||||
err = reserve(array, cap, loc)
|
||||
} else {
|
||||
err = non_zero_reserve(array, cap, 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+1 {
|
||||
// Same behavior as _append_elems but there's only one arg, so we always just add DEFAULT_DYNAMIC_ARRAY_CAPACITY.
|
||||
cap := 2 * array.cap + DEFAULT_DYNAMIC_ARRAY_CAPACITY
|
||||
|
||||
// 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 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
|
||||
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
|
||||
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 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
arg_len := len(args)
|
||||
if arg_len <= 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
when size_of(E) == 0 {
|
||||
array := (^Raw_Dynamic_Array)(array)
|
||||
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)
|
||||
if array.cap < array.len+arg_len {
|
||||
cap := 2 * array.cap + max(DEFAULT_DYNAMIC_ARRAY_CAPACITY, arg_len)
|
||||
|
||||
// do not 'or_return' here as it could be a partial success
|
||||
if should_zero {
|
||||
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
|
||||
// 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)
|
||||
}
|
||||
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
|
||||
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
|
||||
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
|
||||
_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
|
||||
if should_zero {
|
||||
return append_elems(array, ..args, loc=loc)
|
||||
} else {
|
||||
return non_zero_append_elems(array, ..args, loc=loc)
|
||||
}
|
||||
return _append_elems((^Raw_Dynamic_Array)(array), 1, 1, should_zero, loc, raw_data(arg), len(arg))
|
||||
}
|
||||
|
||||
@builtin
|
||||
@@ -679,7 +682,7 @@ assign_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #calle
|
||||
|
||||
|
||||
@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)
|
||||
if len(args) == 0 {
|
||||
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`).
|
||||
//
|
||||
// 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 {
|
||||
if array == nil {
|
||||
_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 a == nil {
|
||||
return nil
|
||||
}
|
||||
a := (^Raw_Dynamic_Array)(array)
|
||||
|
||||
if capacity <= a.cap {
|
||||
return nil
|
||||
@@ -744,15 +746,15 @@ _reserve_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, capacity: i
|
||||
}
|
||||
assert(a.allocator.procedure != nil)
|
||||
|
||||
old_size := a.cap * size_of(E)
|
||||
new_size := capacity * size_of(E)
|
||||
old_size := a.cap * size_of_elem
|
||||
new_size := capacity * size_of_elem
|
||||
allocator := a.allocator
|
||||
|
||||
new_data: []byte
|
||||
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 {
|
||||
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 {
|
||||
return .Out_Of_Memory
|
||||
@@ -765,26 +767,23 @@ _reserve_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, capacity: i
|
||||
|
||||
@builtin
|
||||
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
|
||||
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`).
|
||||
//
|
||||
// Note: Prefer the procedure group `resize`
|
||||
_resize_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, length: int, should_zero: bool, loc := #caller_location) -> Allocator_Error {
|
||||
if array == nil {
|
||||
|
||||
_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 {
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
a := (^Raw_Dynamic_Array)(array)
|
||||
|
||||
if length <= a.cap {
|
||||
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)
|
||||
return nil
|
||||
@@ -795,15 +794,15 @@ _resize_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, length: int,
|
||||
}
|
||||
assert(a.allocator.procedure != nil)
|
||||
|
||||
old_size := a.cap * size_of(E)
|
||||
new_size := length * size_of(E)
|
||||
old_size := a.cap * size_of_elem
|
||||
new_size := length * size_of_elem
|
||||
allocator := a.allocator
|
||||
|
||||
new_data : []byte
|
||||
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 {
|
||||
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 {
|
||||
return .Out_Of_Memory
|
||||
@@ -815,14 +814,17 @@ _resize_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, length: int,
|
||||
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
|
||||
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
|
||||
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`
|
||||
*/
|
||||
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
|
||||
}
|
||||
a := (^Raw_Dynamic_Array)(array)
|
||||
|
||||
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)
|
||||
|
||||
old_size := a.cap * size_of(E)
|
||||
new_size := new_cap * size_of(E)
|
||||
old_size := a.cap * size_of_elem
|
||||
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.len = min(new_cap, a.len)
|
||||
|
||||
@@ -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 {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -577,7 +577,7 @@ map_grow_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Inf
|
||||
|
||||
|
||||
@(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)
|
||||
ceil_log2 :: #force_inline proc "contextless" (x: uintptr) -> uintptr {
|
||||
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)
|
||||
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 {
|
||||
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)
|
||||
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))
|
||||
size := int(map_total_allocation_size(uintptr(map_cap(m)), info))
|
||||
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)
|
||||
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 {
|
||||
return 0, false
|
||||
}
|
||||
@@ -723,7 +723,7 @@ map_lookup_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info,
|
||||
}
|
||||
}
|
||||
@(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 {
|
||||
return false
|
||||
}
|
||||
@@ -749,7 +749,7 @@ map_exists_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info,
|
||||
|
||||
|
||||
@(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
|
||||
ks, vs, hs, _, _ := map_kvh_data_dynamic(m^, info)
|
||||
hs[index] |= TOMBSTONE_MASK
|
||||
|
||||
@@ -10,8 +10,9 @@ when ODIN_BUILD_MODE == .Dynamic {
|
||||
DllMain :: proc "system" (hinstDLL: rawptr, fdwReason: u32, lpReserved: rawptr) -> b32 {
|
||||
context = default_context()
|
||||
|
||||
// Populate Windows DLL-specific global
|
||||
// Populate Windows DLL-specific globals
|
||||
dll_forward_reason = DLL_Forward_Reason(fdwReason)
|
||||
dll_instance = hinstDLL
|
||||
|
||||
switch dll_forward_reason {
|
||||
case .Process_Attach:
|
||||
|
||||
@@ -5,11 +5,24 @@ package runtime
|
||||
import "base:intrinsics"
|
||||
|
||||
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
WRITE :: 0x2000004
|
||||
STDERR :: 2
|
||||
ret := intrinsics.syscall(WRITE, STDERR, uintptr(raw_data(data)), uintptr(len(data)))
|
||||
if ret < 0 {
|
||||
return 0, _OS_Errno(-ret)
|
||||
when ODIN_NO_CRT {
|
||||
WRITE :: 0x2000004
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
if ti == nil {
|
||||
print_string("nil")
|
||||
@@ -401,15 +401,16 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
|
||||
}
|
||||
|
||||
print_string("struct ")
|
||||
if info.is_packed { print_string("#packed ") }
|
||||
if info.is_raw_union { print_string("#raw_union ") }
|
||||
if info.custom_align {
|
||||
if .packed in info.flags { print_string("#packed ") }
|
||||
if .raw_union in info.flags { print_string("#raw_union ") }
|
||||
if .no_copy in info.flags { print_string("#no_copy ") }
|
||||
if .align in info.flags {
|
||||
print_string("#align(")
|
||||
print_u64(u64(ti.align))
|
||||
print_string(") ")
|
||||
}
|
||||
print_byte('{')
|
||||
for name, i in info.names {
|
||||
for name, i in info.names[:info.field_count] {
|
||||
if i > 0 { print_string(", ") }
|
||||
print_string(name)
|
||||
print_string(": ")
|
||||
@@ -469,7 +470,7 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
|
||||
print_string("bit_field ")
|
||||
print_type(info.backing_type)
|
||||
print_string(" {")
|
||||
for name, i in info.names {
|
||||
for name, i in info.names[:info.field_count] {
|
||||
if i > 0 { print_string(", ") }
|
||||
print_string(name)
|
||||
print_string(": ")
|
||||
|
||||
@@ -297,7 +297,8 @@ lock :: proc(a: ^WASM_Allocator) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -48,6 +48,9 @@ if "%2" == "1" (
|
||||
set odin_version_raw="dev-%curr_year%-%curr_month%"
|
||||
|
||||
set 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%\"
|
||||
|
||||
if not exist .git\ goto skip_git_hash
|
||||
@@ -111,7 +114,7 @@ call build_vendor.bat
|
||||
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
|
||||
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
|
||||
|
||||
|
||||
+1
-1
@@ -144,7 +144,7 @@ build_odin() {
|
||||
}
|
||||
|
||||
run_demo() {
|
||||
./odin run examples/demo/demo.odin -file -- Hellope World
|
||||
./odin run examples/demo -vet -strict-style -- Hellope World
|
||||
}
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
|
||||
@@ -1167,3 +1167,28 @@ fields_proc :: proc(s: []byte, f: proc(rune) -> bool, allocator := context.alloc
|
||||
|
||||
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
@@ -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}
|
||||
|
||||
@(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) {
|
||||
#no_bounds_check {
|
||||
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) {
|
||||
// TODO: REMOVE ALL USE OF context.temp_allocator here
|
||||
// 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}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
read_data :: #force_inline proc(z: ^$C, $T: typeid) -> (res: T, err: io.Error) {
|
||||
b := read_slice(z, size_of(T)) or_return
|
||||
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) {
|
||||
#no_bounds_check {
|
||||
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
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
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
|
||||
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
|
||||
// preferentially.
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
read_u8_prefer_code_buffer_lsb :: #force_inline proc(z: ^$C) -> (res: u8, err: io.Error) {
|
||||
if z.num_bits >= 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
|
||||
}
|
||||
|
||||
@(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) {
|
||||
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) {
|
||||
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) {
|
||||
size :: size_of(T)
|
||||
|
||||
@@ -317,7 +317,7 @@ peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid
|
||||
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) {
|
||||
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
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
peek_back_byte :: #force_inline proc(z: ^$C, offset: i64) -> (res: u8, err: io.Error) {
|
||||
// Look back into the sliding window.
|
||||
return z.output.buf[z.bytes_written - offset], .None
|
||||
}
|
||||
|
||||
// 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 := u64(width)
|
||||
b := u64(0)
|
||||
@@ -385,7 +385,7 @@ refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width :=
|
||||
}
|
||||
|
||||
// Generalized bit reader LSB
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
refill_lsb_from_stream :: proc(z: ^Context_Stream_Input, width := i8(24)) {
|
||||
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}
|
||||
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
consume_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) {
|
||||
z.code_buffer >>= 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) {
|
||||
z.code_buffer >>= 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}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
peek_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
||||
if z.num_bits < u64(width) {
|
||||
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))
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
peek_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
||||
if z.num_bits < u64(width) {
|
||||
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}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
peek_bits_no_refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
||||
assert(z.num_bits >= u64(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 {
|
||||
assert(z.num_bits >= u64(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}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
read_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
||||
k := #force_inline peek_bits_lsb(z, width)
|
||||
#force_inline consume_bits_lsb(z, width)
|
||||
return k
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
read_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
||||
k := peek_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}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
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)
|
||||
#force_inline consume_bits_lsb(z, width)
|
||||
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 {
|
||||
k := peek_bits_no_refill_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}
|
||||
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
discard_to_next_byte_lsb_from_memory :: proc(z: ^Context_Memory_Input) {
|
||||
discard := u8(z.num_bits & 7)
|
||||
#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 := u8(z.num_bits & 7)
|
||||
consume_bits_lsb(z, discard)
|
||||
|
||||
@@ -120,7 +120,7 @@ Huffman_Table :: struct {
|
||||
}
|
||||
|
||||
// Implementation starts here
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
z_bit_reverse :: #force_inline proc(n: u16, bits: u8) -> (r: u16) {
|
||||
assert(bits <= 16)
|
||||
// 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) {
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
write_byte :: #force_inline proc(z: ^$C, c: u8) -> (err: io.Error) #no_bounds_check {
|
||||
/*
|
||||
Resize if needed.
|
||||
@@ -173,7 +173,7 @@ write_byte :: #force_inline proc(z: ^$C, c: u8) -> (err: io.Error) #no_bounds_ch
|
||||
return .None
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
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
|
||||
@@ -201,7 +201,7 @@ repl_byte :: proc(z: ^$C, count: u16, c: u8) -> (err: io.Error) #no_bounds_check
|
||||
return .None
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
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
|
||||
@@ -234,8 +234,8 @@ allocate_huffman_table :: proc(allocator := context.allocator) -> (z: ^Huffman_T
|
||||
return new(Huffman_Table, allocator), nil
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
|
||||
@(optimization_mode="favor_size")
|
||||
build_huffman :: #force_no_inline proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
|
||||
sizes: [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
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
decode_huffman_slowpath :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
|
||||
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
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
decode_huffman :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
|
||||
if z.num_bits < 16 {
|
||||
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)
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err: Error) #no_bounds_check {
|
||||
#no_bounds_check for {
|
||||
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 {
|
||||
/*
|
||||
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.
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.allocator) -> (err: Error) #no_bounds_check {
|
||||
context.allocator = allocator
|
||||
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)
|
||||
}
|
||||
|
||||
inflate :: proc{inflate_from_context, inflate_from_byte_array}
|
||||
inflate :: proc{inflate_from_context, inflate_from_byte_array}
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
// The list link you must include in your own structure.
|
||||
Node :: struct {
|
||||
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) {
|
||||
if list.head != nil {
|
||||
list.head.prev = node
|
||||
@@ -33,7 +40,13 @@ push_front :: proc "contextless" (list: ^List, node: ^Node) {
|
||||
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) {
|
||||
if list.tail != nil {
|
||||
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) {
|
||||
if node != 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) {
|
||||
for node := list.head; node != nil; {
|
||||
next := node.next
|
||||
@@ -82,7 +108,13 @@ remove_by_proc :: proc(list: ^List, to_erase: proc(^Node) -> bool) {
|
||||
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) {
|
||||
for node := list.head; node != nil; {
|
||||
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 {
|
||||
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 {
|
||||
link := list.head
|
||||
if link == nil {
|
||||
@@ -130,6 +176,14 @@ pop_front :: proc "contextless" (list: ^List) -> ^Node {
|
||||
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 {
|
||||
link := list.tail
|
||||
if link == nil {
|
||||
@@ -151,29 +205,102 @@ pop_back :: proc "contextless" (list: ^List) -> ^Node {
|
||||
}
|
||||
|
||||
|
||||
|
||||
Iterator :: struct($T: typeid) {
|
||||
curr: ^Node,
|
||||
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)
|
||||
where intrinsics.type_has_field(T, field_name),
|
||||
intrinsics.type_field_type(T, field_name) == Node {
|
||||
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)
|
||||
where intrinsics.type_has_field(T, field_name),
|
||||
intrinsics.type_field_type(T, field_name) == Node {
|
||||
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)
|
||||
where intrinsics.type_has_field(T, field_name),
|
||||
intrinsics.type_field_type(T, field_name) == Node {
|
||||
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) {
|
||||
node := it.curr
|
||||
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
|
||||
}
|
||||
/*
|
||||
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) {
|
||||
node := it.curr
|
||||
if node == nil {
|
||||
@@ -192,4 +359,4 @@ iterate_prev :: proc "contextless" (it: ^Iterator($T)) -> (ptr: ^T, ok: bool) {
|
||||
it.curr = node.prev
|
||||
|
||||
return (^T)(uintptr(node) - it.offset), true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,11 +95,11 @@ front_ptr :: 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]
|
||||
}
|
||||
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]
|
||||
}
|
||||
|
||||
|
||||
@@ -7,9 +7,8 @@ STRIDE :: 4
|
||||
|
||||
// Context is a keyed AES (ECB) instance.
|
||||
Context :: struct {
|
||||
_sk_exp: [120]u64,
|
||||
_num_rounds: int,
|
||||
_is_initialized: bool,
|
||||
_sk_exp: [120]u64,
|
||||
_num_rounds: int,
|
||||
}
|
||||
|
||||
// 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)
|
||||
skey_expand(ctx._sk_exp[:], skey[:], ctx._num_rounds)
|
||||
ctx._is_initialized = true
|
||||
}
|
||||
|
||||
// encrypt_block sets `dst` to `AES-ECB-Encrypt(src)`.
|
||||
encrypt_block :: proc(ctx: ^Context, dst, src: []byte) {
|
||||
assert(ctx._is_initialized)
|
||||
|
||||
q: [8]u64
|
||||
load_blockx1(&q, src)
|
||||
_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)`.
|
||||
decrypt_block :: proc(ctx: ^Context, dst, src: []byte) {
|
||||
assert(ctx._is_initialized)
|
||||
|
||||
q: [8]u64
|
||||
load_blockx1(&q, src)
|
||||
_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 :: proc(ctx: ^Context, dst, src: [][]byte) {
|
||||
assert(ctx._is_initialized)
|
||||
|
||||
q: [8]u64 = ---
|
||||
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 :: proc(ctx: ^Context, dst, src: [][]byte) {
|
||||
assert(ctx._is_initialized)
|
||||
|
||||
q: [8]u64 = ---
|
||||
src, dst := src, dst
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
@@ -6,7 +6,6 @@ See:
|
||||
- https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf
|
||||
- https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
|
||||
*/
|
||||
|
||||
package aes
|
||||
|
||||
import "core:crypto/_aes"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package aes
|
||||
|
||||
import "core:bytes"
|
||||
import "core:crypto/_aes/ct64"
|
||||
import "core:encoding/endian"
|
||||
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) {
|
||||
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
|
||||
if dst_len := len(dst); dst_len < len(src) {
|
||||
src = src[:dst_len]
|
||||
}
|
||||
|
||||
if bytes.alias_inexactly(dst, src) {
|
||||
panic("crypto/aes: dst and src alias inexactly")
|
||||
}
|
||||
|
||||
for remaining := len(src); remaining > 0; {
|
||||
// Process multiple blocks at once
|
||||
if ctx._off == BLOCK_SIZE {
|
||||
@@ -123,8 +125,8 @@ reset_ctr :: proc "contextless" (ctx: ^Context_CTR) {
|
||||
ctx._is_initialized = false
|
||||
}
|
||||
|
||||
@(private)
|
||||
ctr_blocks :: proc(ctx: ^Context_CTR, dst, src: []byte, nr_blocks: int) {
|
||||
@(private = "file")
|
||||
ctr_blocks :: proc(ctx: ^Context_CTR, dst, src: []byte, nr_blocks: int) #no_bounds_check {
|
||||
// Use the optimized hardware implementation if available.
|
||||
if _, is_hw := ctx._impl.(Context_Impl_Hardware); is_hw {
|
||||
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
|
||||
// optimization would be worth it, use chacha20poly1305, or a
|
||||
// CPU that isn't e-waste.
|
||||
if src != nil {
|
||||
#no_bounds_check {
|
||||
for i in 0 ..< len(blocks) {
|
||||
off := i * BLOCK_SIZE
|
||||
for j in 0 ..< BLOCK_SIZE {
|
||||
blocks[i][j] ~= src[off + j]
|
||||
#no_bounds_check {
|
||||
if src != nil {
|
||||
for i in 0 ..< len(blocks) {
|
||||
off := i * BLOCK_SIZE
|
||||
for j in 0 ..< BLOCK_SIZE {
|
||||
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])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -1,13 +1,16 @@
|
||||
package aes
|
||||
|
||||
import "core:bytes"
|
||||
import "core:crypto"
|
||||
import "core:crypto/_aes"
|
||||
import "core:crypto/_aes/ct64"
|
||||
import "core:encoding/endian"
|
||||
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_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 :: _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) {
|
||||
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 {
|
||||
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
|
||||
j0: [_aes.GHASH_BLOCK_SIZE]byte
|
||||
j0_enc: [_aes.GHASH_BLOCK_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.
|
||||
ct64.ghash(s[:], h[:], aad)
|
||||
gctr_ct64(ctx, dst, &s, plaintext, &h, nonce, true)
|
||||
final_ghash_ct64(&s, &h, &j0, len(aad), len(plaintext))
|
||||
gctr_ct64(ctx, dst, &s, plaintext, &h, &j0, true)
|
||||
final_ghash_ct64(&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))
|
||||
}
|
||||
|
||||
// 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) {
|
||||
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 {
|
||||
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
|
||||
j0: [_aes.GHASH_BLOCK_SIZE]byte
|
||||
j0_enc: [_aes.GHASH_BLOCK_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)
|
||||
gctr_ct64(ctx, dst, &s, ciphertext, &h, nonce, false)
|
||||
final_ghash_ct64(&s, &h, &j0, len(aad), len(ciphertext))
|
||||
gctr_ct64(ctx, dst, &s, ciphertext, &h, &j0, false)
|
||||
final_ghash_ct64(&s, &h, &j0_enc, len(aad), len(ciphertext))
|
||||
|
||||
ok := crypto.compare_constant_time(s[:], tag) == 1
|
||||
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(&j0, len(j0))
|
||||
mem.zero_explicit(&j0_enc, len(j0_enc))
|
||||
mem.zero_explicit(&s, len(s))
|
||||
|
||||
return ok
|
||||
@@ -106,19 +119,14 @@ reset_gcm :: proc "contextless" (ctx: ^Context_GCM) {
|
||||
ctx._is_initialized = false
|
||||
}
|
||||
|
||||
@(private)
|
||||
@(private = "file")
|
||||
gcm_validate_common_slice_sizes :: proc(tag, nonce, aad, text: []byte) {
|
||||
if len(tag) != GCM_TAG_SIZE {
|
||||
panic("crypto/aes: invalid GCM tag size")
|
||||
}
|
||||
|
||||
// The specification supports nonces in the range [1, 2^64) bits
|
||||
// however per NIST SP 800-38D 5.2.1.1:
|
||||
//
|
||||
// > 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 {
|
||||
// The specification supports nonces in the range [1, 2^64) bits.
|
||||
if l := len(nonce); l == 0 || u64(l) >= GCM_NONCE_SIZE_MAX {
|
||||
panic("crypto/aes: invalid GCM nonce size")
|
||||
}
|
||||
|
||||
@@ -135,6 +143,7 @@ init_ghash_ct64 :: proc(
|
||||
ctx: ^Context_GCM,
|
||||
h: ^[_aes.GHASH_KEY_SIZE]byte,
|
||||
j0: ^[_aes.GHASH_BLOCK_SIZE]byte,
|
||||
j0_enc: ^[_aes.GHASH_BLOCK_SIZE]byte,
|
||||
nonce: []byte,
|
||||
) {
|
||||
impl := &ctx._impl.(ct64.Context)
|
||||
@@ -142,12 +151,25 @@ init_ghash_ct64 :: proc(
|
||||
// 1. Let H = CIPH(k, 0^128)
|
||||
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
|
||||
// this could be processed along with the final GCTR block, to
|
||||
// potentially save a call to AES-ECB, but... just use AES-NI.
|
||||
copy(j0[:], nonce)
|
||||
j0[_aes.GHASH_BLOCK_SIZE - 1] = 1
|
||||
ct64.encrypt_block(impl, j0[:], j0[:])
|
||||
ct64.encrypt_block(impl, j0_enc[:], j0[:])
|
||||
}
|
||||
|
||||
@(private = "file")
|
||||
@@ -175,33 +197,27 @@ gctr_ct64 :: proc(
|
||||
s: ^[_aes.GHASH_BLOCK_SIZE]byte,
|
||||
src: []byte,
|
||||
h: ^[_aes.GHASH_KEY_SIZE]byte,
|
||||
nonce: []byte,
|
||||
nonce: ^[_aes.GHASH_BLOCK_SIZE]byte,
|
||||
is_seal: bool,
|
||||
) {
|
||||
) #no_bounds_check {
|
||||
ct64_inc_ctr32 := #force_inline proc "contextless" (dst: []byte, ctr: u32) -> u32 {
|
||||
endian.unchecked_put_u32be(dst[12:], ctr)
|
||||
return ctr + 1
|
||||
}
|
||||
|
||||
// 2. Define a block J_0 as follows:
|
||||
// if len(IV) = 96, then let J0 = IV || 0^31 || 1
|
||||
//
|
||||
// Note: We only support 96 bit IVs.
|
||||
// Setup the counter blocks.
|
||||
tmp, tmp2: [ct64.STRIDE][BLOCK_SIZE]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 {
|
||||
// Setup scratch space for the keystream.
|
||||
blks[i] = tmp2[i][:]
|
||||
|
||||
// Pre-copy the IV to all the counter blocks.
|
||||
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)
|
||||
src, dst := src, dst
|
||||
|
||||
|
||||
@@ -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,3 +1,4 @@
|
||||
//+build !amd64
|
||||
package aes
|
||||
|
||||
@(private = "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)
|
||||
}
|
||||
@@ -7,6 +7,7 @@ See:
|
||||
*/
|
||||
package chacha20
|
||||
|
||||
import "core:bytes"
|
||||
import "core:encoding/endian"
|
||||
import "core:math/bits"
|
||||
import "core:mem"
|
||||
@@ -121,14 +122,15 @@ seek :: proc(ctx: ^Context, block_nr: u64) {
|
||||
xor_bytes :: proc(ctx: ^Context, dst, src: []byte) {
|
||||
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
|
||||
if dst_len := len(dst); dst_len < len(src) {
|
||||
src = src[:dst_len]
|
||||
}
|
||||
|
||||
if bytes.alias_inexactly(dst, src) {
|
||||
panic("crypto/chacha20: dst and src alias inexactly")
|
||||
}
|
||||
|
||||
for remaining := len(src); remaining > 0; {
|
||||
// Process multiple blocks at once
|
||||
if ctx._off == _BLOCK_SIZE {
|
||||
|
||||
@@ -60,7 +60,11 @@ rand_bytes :: proc (dst: []byte) {
|
||||
_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 {
|
||||
return {
|
||||
procedure = proc(data: rawptr, mode: runtime.Random_Generator_Mode, p: []byte) {
|
||||
@@ -79,4 +83,4 @@ random_generator :: proc() -> runtime.Random_Generator {
|
||||
},
|
||||
data = nil,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@ HAS_RAND_BYTES :: true
|
||||
|
||||
@(private)
|
||||
_rand_bytes :: proc(dst: []byte) {
|
||||
ret := (os.Errno)(win32.BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG))
|
||||
if ret != os.ERROR_NONE {
|
||||
switch ret {
|
||||
ret := os.Platform_Error(win32.BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG))
|
||||
if ret != nil {
|
||||
#partial switch ret {
|
||||
case os.ERROR_INVALID_HANDLE:
|
||||
// The handle to the first parameter is invalid.
|
||||
// This should not happen here, since we explicitly pass nil to it
|
||||
|
||||
+5
-13
@@ -16,15 +16,12 @@ Library :: distinct rawptr
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
The underlying behaviour is platform specific.
|
||||
On `linux`, `darwin`, `freebsd` and `openbsd` refer to `dlopen`.
|
||||
On `windows` refer to `LoadLibraryW`.
|
||||
|
||||
**Implicit Allocators**
|
||||
`context.temp_allocator`
|
||||
On `windows` refer to `LoadLibraryW`. Also temporarily needs an allocator to convert a string.
|
||||
|
||||
Example:
|
||||
import "core:dynlib"
|
||||
@@ -79,10 +76,7 @@ Loads the address of a procedure/variable from a dynamic library.
|
||||
|
||||
The underlying behaviour is platform specific.
|
||||
On `linux`, `darwin`, `freebsd` and `openbsd` refer to `dlsym`.
|
||||
On `windows` refer to `GetProcAddress`.
|
||||
|
||||
**Implicit Allocators**
|
||||
`context.temp_allocator`
|
||||
On `windows` refer to `GetProcAddress`. Also temporarily needs an allocator to convert a string.
|
||||
|
||||
Example:
|
||||
import "core:dynlib"
|
||||
@@ -177,9 +171,7 @@ initialize_symbols :: proc(
|
||||
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 {
|
||||
return _last_error()
|
||||
}
|
||||
}
|
||||
@@ -16,4 +16,4 @@ _symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found
|
||||
|
||||
_last_error :: proc() -> string {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
@@ -26,4 +26,4 @@ _symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found
|
||||
_last_error :: proc() -> string {
|
||||
err := os.dlerror()
|
||||
return "unknown" if err == "" else err
|
||||
}
|
||||
}
|
||||
@@ -4,14 +4,12 @@ package dynlib
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
import "core:strings"
|
||||
import "base:runtime"
|
||||
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
|
||||
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
wide_path := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
wide_path := win32.utf8_to_wstring(path, allocator)
|
||||
defer free(wide_path, allocator)
|
||||
handle := cast(Library)win32.LoadLibraryW(wide_path)
|
||||
return handle, handle != nil
|
||||
}
|
||||
@@ -21,9 +19,9 @@ _unload_library :: proc(library: Library) -> bool {
|
||||
return bool(ok)
|
||||
}
|
||||
|
||||
_symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
c_str := strings.clone_to_cstring(symbol, context.temp_allocator)
|
||||
_symbol_address :: proc(library: Library, symbol: string, allocator := context.temp_allocator) -> (ptr: rawptr, found: bool) {
|
||||
c_str := strings.clone_to_cstring(symbol, allocator)
|
||||
defer delete(c_str, allocator)
|
||||
ptr = win32.GetProcAddress(cast(win32.HMODULE)library, c_str)
|
||||
found = ptr != nil
|
||||
return
|
||||
@@ -33,4 +31,4 @@ _last_error :: proc() -> string {
|
||||
err := win32.System_Error(win32.GetLastError())
|
||||
err_msg := reflect.enum_string(err)
|
||||
return "unknown" if err_msg == "" else err_msg
|
||||
}
|
||||
}
|
||||
@@ -77,8 +77,11 @@ You can look at the default tags provided for pointers on how these implementati
|
||||
Example:
|
||||
package main
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
import "core:encoding/cbor"
|
||||
import "core:fmt"
|
||||
import "core:reflect"
|
||||
import "core:time"
|
||||
|
||||
Possibilities :: union {
|
||||
@@ -93,9 +96,32 @@ Example:
|
||||
ignore_this: ^Data `cbor:"-"`, // Ignored by implementation.
|
||||
renamed: f32 `cbor:"renamed :)"`, // Renamed when encoded.
|
||||
my_union: Possibilities, // Union support.
|
||||
|
||||
my_raw: [8]u32 `cbor_tag:"raw"`, // Custom tag that just writes the value as bytes.
|
||||
}
|
||||
|
||||
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}
|
||||
|
||||
data := Data{
|
||||
@@ -105,21 +131,22 @@ Example:
|
||||
ignore_this = &Data{},
|
||||
renamed = 123123.125,
|
||||
my_union = 3,
|
||||
my_raw = {1=1, 2=2, 3=3},
|
||||
}
|
||||
|
||||
|
||||
// Marshal the struct into binary CBOR.
|
||||
binary, err := cbor.marshal(data, cbor.ENCODE_FULLY_DETERMINISTIC)
|
||||
assert(err == nil)
|
||||
fmt.assertf(err == nil, "marshal error: %v", err)
|
||||
defer delete(binary)
|
||||
|
||||
|
||||
// Decode the binary data into a `cbor.Value`.
|
||||
decoded, derr := cbor.decode(string(binary))
|
||||
assert(derr == nil)
|
||||
fmt.assertf(derr == nil, "decode error: %v", derr)
|
||||
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]].
|
||||
diagnosis, eerr := cbor.to_diagnostic_format(decoded)
|
||||
assert(eerr == nil)
|
||||
fmt.assertf(eerr == nil, "to diagnostic error: %v", eerr)
|
||||
defer delete(diagnosis)
|
||||
|
||||
fmt.println(diagnosis)
|
||||
@@ -127,6 +154,7 @@ Example:
|
||||
|
||||
Output:
|
||||
{
|
||||
"my_raw": 200(h'00001000200030000000000000000000'),
|
||||
"my_union": 1010([
|
||||
"int",
|
||||
3
|
||||
|
||||
@@ -54,7 +54,7 @@ marshal_into_bytes :: proc(v: any, flags := ENCODE_SMALL, allocator := context.a
|
||||
|
||||
defer if err != nil { strings.builder_destroy(&b) }
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
// 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 {
|
||||
return marshal_into_writer(strings.to_writer(b), v, flags, temp_allocator, loc=loc)
|
||||
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)
|
||||
}
|
||||
|
||||
// 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.
|
||||
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}
|
||||
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.
|
||||
// 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
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
ti := runtime.type_info_base(type_info_of(v.id))
|
||||
a := any{v.data, ti.id}
|
||||
ti := runtime.type_info_core(type_info_of(v.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 {
|
||||
case runtime.Type_Info_Named:
|
||||
case runtime.Type_Info_Named, runtime.Type_Info_Enum, runtime.Type_Info_Bit_Field:
|
||||
unreachable()
|
||||
|
||||
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
|
||||
|
||||
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 {
|
||||
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
|
||||
|
||||
case runtime.Type_Info_Enumerated_Array:
|
||||
// index := runtime.type_info_base(info.index).variant.(runtime.Type_Info_Enum)
|
||||
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 {
|
||||
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
|
||||
|
||||
@@ -246,9 +269,19 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e
|
||||
|
||||
array := (^mem.Raw_Dynamic_Array)(v.data)
|
||||
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 {
|
||||
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
|
||||
|
||||
@@ -260,9 +293,19 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e
|
||||
|
||||
array := (^mem.Raw_Slice)(v.data)
|
||||
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 {
|
||||
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
|
||||
|
||||
@@ -308,7 +351,8 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e
|
||||
builder := strings.builder_from_slice(res[:])
|
||||
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))
|
||||
assert(res[9] < 10)
|
||||
return
|
||||
@@ -463,7 +507,7 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e
|
||||
}
|
||||
|
||||
n: u64; {
|
||||
for _, i in info.names {
|
||||
for _, i in info.names[:info.field_count] {
|
||||
if field_name(info, i) != "-" {
|
||||
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
|
||||
defer delete(entries)
|
||||
|
||||
for _, i in info.names {
|
||||
for _, i in info.names[:info.field_count] {
|
||||
fname := field_name(info, i)
|
||||
if fname == "-" {
|
||||
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
|
||||
}
|
||||
} else {
|
||||
for _, i in info.names {
|
||||
for _, i in info.names[:info.field_count] {
|
||||
fname := field_name(info, i)
|
||||
if fname == "-" {
|
||||
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})
|
||||
|
||||
case runtime.Type_Info_Enum:
|
||||
return marshal_into(e, any{v.data, info.base.id})
|
||||
|
||||
case runtime.Type_Info_Bit_Set:
|
||||
// Store bit_set as big endian just like the protocol.
|
||||
do_byte_swap := !reflect.bit_set_is_big_endian(v)
|
||||
|
||||
@@ -96,7 +96,8 @@ _unmarshal_value :: proc(d: Decoder, v: any, hdr: Header, allocator := context.a
|
||||
ti = reflect.type_info_base(variant)
|
||||
if !reflect.is_pointer_internally(variant) {
|
||||
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
|
||||
|
||||
case reflect.Type_Info_Array:
|
||||
_, scap := err_conv(_decode_len_container(d, add)) or_return
|
||||
length := min(scap, t.count)
|
||||
|
||||
length, _ := err_conv(_decode_len_container(d, add)) or_return
|
||||
if length > t.count {
|
||||
return _unsupported(v, hdr)
|
||||
}
|
||||
@@ -534,9 +533,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
||||
return
|
||||
|
||||
case reflect.Type_Info_Enumerated_Array:
|
||||
_, scap := err_conv(_decode_len_container(d, add)) or_return
|
||||
length := min(scap, t.count)
|
||||
|
||||
length, _ := err_conv(_decode_len_container(d, add)) or_return
|
||||
if length > t.count {
|
||||
return _unsupported(v, hdr)
|
||||
}
|
||||
@@ -548,9 +545,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
||||
return
|
||||
|
||||
case reflect.Type_Info_Complex:
|
||||
_, scap := err_conv(_decode_len_container(d, add)) or_return
|
||||
length := min(scap, 2)
|
||||
|
||||
length, _ := err_conv(_decode_len_container(d, add)) or_return
|
||||
if length > 2 {
|
||||
return _unsupported(v, hdr)
|
||||
}
|
||||
@@ -570,9 +565,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
||||
return
|
||||
|
||||
case reflect.Type_Info_Quaternion:
|
||||
_, scap := err_conv(_decode_len_container(d, add)) or_return
|
||||
length := min(scap, 4)
|
||||
|
||||
length, _ := err_conv(_decode_len_container(d, add)) or_return
|
||||
if length > 4 {
|
||||
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 {
|
||||
case reflect.Type_Info_Struct:
|
||||
if t.is_raw_union {
|
||||
if .raw_union in t.flags {
|
||||
return _unsupported(v, hdr)
|
||||
}
|
||||
|
||||
length, _ := err_conv(_decode_len_container(d, add)) or_return
|
||||
unknown := length == -1
|
||||
fields := reflect.struct_fields_zipped(ti.id)
|
||||
|
||||
|
||||
for idx := 0; idx < len(fields) && (unknown || idx < length); idx += 1 {
|
||||
// Decode key, keys can only be strings.
|
||||
key: string
|
||||
@@ -646,7 +639,7 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header,
|
||||
key = keyv
|
||||
}
|
||||
defer delete(key, context.temp_allocator)
|
||||
|
||||
|
||||
// Find matching field.
|
||||
use_field_idx := -1
|
||||
{
|
||||
|
||||
@@ -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
|
||||
defer csv.reader_destroy(&r)
|
||||
|
||||
handle, errno := os.open(filename)
|
||||
if errno != os.ERROR_NONE {
|
||||
fmt.printfln("Error opening file: %v", filename)
|
||||
handle, err := os.open(filename)
|
||||
if err != nil {
|
||||
fmt.eprintfln("Error opening file: %v", filename)
|
||||
return
|
||||
}
|
||||
defer os.close(handle)
|
||||
|
||||
@@ -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) {
|
||||
unquote :: proc(val: string) -> (string, runtime.Allocator_Error) {
|
||||
v, allocated, ok := strconv.unquote_string(val)
|
||||
if !ok {
|
||||
return strings.clone(val)
|
||||
if len(val) > 0 && (val[0] == '"' || val[0] == '\'') {
|
||||
v, allocated, ok := strconv.unquote_string(val)
|
||||
if !ok {
|
||||
return strings.clone(val)
|
||||
}
|
||||
if allocated {
|
||||
return v, nil
|
||||
}
|
||||
return strings.clone(v), nil
|
||||
}
|
||||
if allocated {
|
||||
return v, nil
|
||||
}
|
||||
return strings.clone(v)
|
||||
|
||||
return strings.clone(val)
|
||||
}
|
||||
|
||||
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
|
||||
defer delete(data, allocator)
|
||||
m, err = load_map_from_string(string(data), allocator, options)
|
||||
ok = err != nil
|
||||
ok = err == nil
|
||||
defer if !ok {
|
||||
delete_map(m)
|
||||
}
|
||||
@@ -142,6 +144,7 @@ delete_map :: proc(m: Map) {
|
||||
delete(value, allocator)
|
||||
}
|
||||
delete(section)
|
||||
delete(pairs)
|
||||
}
|
||||
delete(m)
|
||||
}
|
||||
|
||||
@@ -100,38 +100,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
|
||||
case runtime.Type_Info_Integer:
|
||||
buf: [40]byte
|
||||
u: u128
|
||||
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)
|
||||
}
|
||||
u := cast_any_int_to_u128(a)
|
||||
|
||||
s: string
|
||||
|
||||
@@ -310,7 +279,12 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
case cstring: name = string(s)
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
info := ti.variant.(runtime.Type_Info_Struct)
|
||||
first_iteration := true
|
||||
for name, i in info.names {
|
||||
for name, i in info.names[:info.field_count] {
|
||||
omitempty := false
|
||||
|
||||
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, ",") {
|
||||
switch flag {
|
||||
case "omitempty":
|
||||
@@ -657,3 +636,41 @@ opt_write_indentation :: proc(w: io.Writer, opt: ^Marshal_Options) -> (err: io.E
|
||||
|
||||
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
|
||||
}
|
||||
@@ -363,12 +363,11 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
||||
}
|
||||
|
||||
v := v
|
||||
v = reflect.any_base(v)
|
||||
ti := type_info_of(v.id)
|
||||
ti := reflect.type_info_base(type_info_of(v.id))
|
||||
|
||||
#partial switch t in ti.variant {
|
||||
case reflect.Type_Info_Struct:
|
||||
if t.is_raw_union {
|
||||
if .raw_union in t.flags {
|
||||
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:
|
||||
if !reflect.is_string(t.key) {
|
||||
if !reflect.is_string(t.key) && !reflect.is_integer(t.key) {
|
||||
return UNSUPPORTED_TYPE
|
||||
}
|
||||
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)
|
||||
unmarshal_expect_token(p, .Colon)
|
||||
|
||||
|
||||
|
||||
mem.zero_slice(elem_backing)
|
||||
if uerr := unmarshal_value(p, map_backing_value); uerr != nil {
|
||||
delete(key, p.allocator)
|
||||
return uerr
|
||||
}
|
||||
|
||||
key_ptr := rawptr(&key)
|
||||
key_ptr: rawptr
|
||||
|
||||
key_cstr: cstring
|
||||
if reflect.is_cstring(t.key) {
|
||||
key_cstr = cstring(raw_data(key))
|
||||
key_ptr = &key_cstr
|
||||
#partial switch tk in t.key.variant {
|
||||
case runtime.Type_Info_String:
|
||||
key_ptr = rawptr(&key)
|
||||
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)
|
||||
if set_ptr == nil {
|
||||
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) {
|
||||
break map_loop
|
||||
|
||||
@@ -126,7 +126,7 @@ error :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
|
||||
t.error_count += 1
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
advance_rune :: proc(t: ^Tokenizer) {
|
||||
#no_bounds_check {
|
||||
/*
|
||||
@@ -170,7 +170,7 @@ peek_byte :: proc(t: ^Tokenizer, offset := 0) -> byte {
|
||||
return 0
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
skip_whitespace :: proc(t: ^Tokenizer) {
|
||||
for {
|
||||
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 {
|
||||
if r < utf8.RUNE_SELF {
|
||||
switch r {
|
||||
@@ -296,7 +296,7 @@ skip_cdata :: proc(t: ^Tokenizer) -> (err: Error) {
|
||||
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) {
|
||||
err = .None
|
||||
|
||||
@@ -414,4 +414,4 @@ scan :: proc(t: ^Tokenizer, multiline_string := false) -> Token {
|
||||
lit = string(t.src[offset : t.offset])
|
||||
}
|
||||
return Token{kind, lit, pos}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ Parse_Error :: struct {
|
||||
// Provides more granular information than what just a string could hold.
|
||||
Open_File_Error :: struct {
|
||||
filename: string,
|
||||
errno: os.Errno,
|
||||
errno: os.Error,
|
||||
mode: int,
|
||||
perms: int,
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import "core:reflect"
|
||||
|
||||
// Push a positional argument onto a data struct, checking for specified
|
||||
// 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) {
|
||||
if bit_array.get(&parser.filled_pos, parser.filled_pos.max_index) {
|
||||
// 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.
|
||||
@(optimization_mode="size")
|
||||
@(optimization_mode="favor_size")
|
||||
set_odin_flag :: proc(model: ^$T, parser: ^Parser, name: string) -> (error: Error) {
|
||||
// We make a special case for help requests.
|
||||
switch name {
|
||||
@@ -100,7 +100,7 @@ set_odin_flag :: proc(model: ^$T, parser: ^Parser, name: string) -> (error: Erro
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// We make a special case for help requests.
|
||||
switch name {
|
||||
@@ -137,7 +137,7 @@ set_unix_flag :: proc(model: ^$T, parser: ^Parser, name: string) -> (future_args
|
||||
}
|
||||
|
||||
// Set a `-flag:option` argument.
|
||||
@(optimization_mode="size")
|
||||
@(optimization_mode="favor_size")
|
||||
set_option :: proc(model: ^$T, parser: ^Parser, name, option: string) -> (error: Error) {
|
||||
field, index := get_field_by_name(model, name) or_return
|
||||
|
||||
@@ -176,7 +176,7 @@ set_option :: proc(model: ^$T, parser: ^Parser, name, option: string) -> (error:
|
||||
}
|
||||
|
||||
// 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) {
|
||||
field, index := get_field_by_name(model, name) or_return
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import "core:strings"
|
||||
@require import "core:time/datetime"
|
||||
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 {
|
||||
bounded_int :: proc(value, min, max: i128) -> (result: i128, ok: bool) {
|
||||
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.
|
||||
//
|
||||
// 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) {
|
||||
// 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)
|
||||
if errno != 0 {
|
||||
// NOTE(Feoramund): os.Errno is system-dependent, and there's
|
||||
if errno != nil {
|
||||
// NOTE(Feoramund): os.Error is system-dependent, and there's
|
||||
// currently no good way to translate them all into strings.
|
||||
//
|
||||
// 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) {
|
||||
switch data_type {
|
||||
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) {
|
||||
#partial switch specific_type_info in type_info.variant {
|
||||
case runtime.Type_Info_Named:
|
||||
|
||||
@@ -11,7 +11,7 @@ package flags
|
||||
@require import "core:strings"
|
||||
|
||||
// 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) {
|
||||
positionals_assigned_so_far: bit_array.Bit_Array
|
||||
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
|
||||
// are up to the program's expectations.
|
||||
@(optimization_mode="size")
|
||||
@(optimization_mode="favor_size")
|
||||
validate_arguments :: proc(model: ^$T, parser: ^Parser) -> Error {
|
||||
check_fields: for field, index in reflect.struct_fields_zipped(T) {
|
||||
was_set := bit_array.get(&parser.fields_set, index)
|
||||
|
||||
@@ -32,7 +32,7 @@ Inputs:
|
||||
Returns:
|
||||
- error: A union of errors; parsing, file open, a help request, or validation.
|
||||
*/
|
||||
@(optimization_mode="size")
|
||||
@(optimization_mode="favor_size")
|
||||
parse :: proc(
|
||||
model: ^$T,
|
||||
args: []string,
|
||||
|
||||
@@ -17,7 +17,7 @@ Inputs:
|
||||
- program: The name of the program, usually the first argument to `os.args`.
|
||||
- style: The argument parsing style, required to show flags in the proper style.
|
||||
*/
|
||||
@(optimization_mode="size")
|
||||
@(optimization_mode="favor_size")
|
||||
write_usage :: proc(out: io.Writer, data_type: typeid, program: string = "", style: Parsing_Style = .Odin) {
|
||||
// All flags get their tags parsed so they can be reasoned about later.
|
||||
Flag :: struct {
|
||||
|
||||
@@ -19,7 +19,7 @@ Inputs:
|
||||
- allocator: (default: context.allocator)
|
||||
- loc: The caller location for debugging purposes (default: #caller_location)
|
||||
*/
|
||||
@(optimization_mode="size")
|
||||
@(optimization_mode="favor_size")
|
||||
parse_or_exit :: proc(
|
||||
model: ^$T,
|
||||
program_args: []string,
|
||||
@@ -63,7 +63,7 @@ Inputs:
|
||||
- error: The error returned from `parse`.
|
||||
- style: The argument parsing style, required to show flags in the proper style, when usage is shown.
|
||||
*/
|
||||
@(optimization_mode="size")
|
||||
@(optimization_mode="favor_size")
|
||||
print_errors :: proc(data_type: typeid, error: Error, program: string, style: Parsing_Style = .Odin) {
|
||||
stderr := os.stream_from_handle(os.stderr)
|
||||
stdout := os.stream_from_handle(os.stdout)
|
||||
|
||||
+47
-28
@@ -334,6 +334,27 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
|
||||
message := tprintf(fmt, ..args)
|
||||
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
|
||||
//
|
||||
// *Allocates Using Context's Allocator*
|
||||
@@ -346,9 +367,9 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
|
||||
// Returns: A formatted C string
|
||||
//
|
||||
@(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
|
||||
strings.builder_init(&str)
|
||||
strings.builder_init(&str, allocator)
|
||||
sbprintf(&str, format, ..args, newline=newline)
|
||||
strings.write_byte(&str, 0)
|
||||
s := strings.to_string(str)
|
||||
@@ -365,8 +386,8 @@ caprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
|
||||
// Returns: A formatted C string
|
||||
//
|
||||
@(require_results)
|
||||
caprintfln :: proc(format: string, args: ..any) -> cstring {
|
||||
return caprintf(format, ..args, newline=true)
|
||||
caprintfln :: proc(format: string, args: ..any, allocator := context.allocator) -> cstring {
|
||||
return caprintf(format, ..args, allocator=allocator, newline=true)
|
||||
}
|
||||
// Creates a formatted C string
|
||||
//
|
||||
@@ -380,12 +401,7 @@ caprintfln :: proc(format: string, args: ..any) -> cstring {
|
||||
//
|
||||
@(require_results)
|
||||
ctprint :: proc(args: ..any, sep := " ") -> cstring {
|
||||
str: strings.Builder
|
||||
strings.builder_init(&str, context.temp_allocator)
|
||||
sbprint(&str, ..args, sep=sep)
|
||||
strings.write_byte(&str, 0)
|
||||
s := strings.to_string(str)
|
||||
return cstring(raw_data(s))
|
||||
return caprint(args=args, sep=sep, allocator=context.temp_allocator)
|
||||
}
|
||||
// Creates a formatted C string
|
||||
//
|
||||
@@ -400,12 +416,7 @@ ctprint :: proc(args: ..any, sep := " ") -> cstring {
|
||||
//
|
||||
@(require_results)
|
||||
ctprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
|
||||
str: strings.Builder
|
||||
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))
|
||||
return caprintf(format=format, args=args, allocator=context.temp_allocator, newline=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)
|
||||
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
|
||||
//
|
||||
@@ -951,10 +962,10 @@ fmt_bad_verb :: proc(fi: ^Info, verb: rune) {
|
||||
io.write_string(fi.writer, "%!", &fi.n)
|
||||
io.write_rune(fi.writer, verb, &fi.n)
|
||||
io.write_byte(fi.writer, '(', &fi.n)
|
||||
if fi.arg.id != nil {
|
||||
reflect.write_typeid(fi.writer, fi.arg.id, &fi.n)
|
||||
if arg := fi.arg; arg != nil {
|
||||
reflect.write_typeid(fi.writer, arg.id, &fi.n)
|
||||
io.write_byte(fi.writer, '=', &fi.n)
|
||||
fmt_value(fi, fi.arg, 'v')
|
||||
fmt_value(fi, arg, 'v')
|
||||
} else {
|
||||
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 {
|
||||
return
|
||||
}
|
||||
for f, i in info.names {
|
||||
for f, i in info.names[:info.field_count] {
|
||||
if f != field_name {
|
||||
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)
|
||||
return
|
||||
}
|
||||
if info.is_raw_union {
|
||||
if .raw_union in info.flags {
|
||||
if type_name == "" {
|
||||
io.write_string(fi.writer, "(raw union)", &fi.n)
|
||||
} else {
|
||||
@@ -1989,7 +2000,7 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St
|
||||
// fi.hash = false;
|
||||
fi.indent += 1
|
||||
|
||||
is_empty := len(info.names) == 0
|
||||
is_empty := info.field_count == 0
|
||||
|
||||
if !is_soa && hash && !is_empty {
|
||||
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
|
||||
}
|
||||
|
||||
actual_field_count := len(info.names)
|
||||
actual_field_count := info.field_count
|
||||
|
||||
n := uintptr(info.soa_len)
|
||||
|
||||
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])^)
|
||||
|
||||
} 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])^)
|
||||
}
|
||||
@@ -2099,7 +2110,7 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St
|
||||
}
|
||||
} else {
|
||||
field_count := -1
|
||||
for name, i in info.names {
|
||||
for name, i in info.names[:info.field_count] {
|
||||
optional_len: int = -1
|
||||
use_nul_termination: bool = false
|
||||
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
|
||||
for name, i in info.names {
|
||||
for name, i in info.names[:info.field_count] {
|
||||
field_verb := verb
|
||||
if handle_bit_field_tag(v.data, info, i, &field_verb) {
|
||||
continue
|
||||
@@ -2751,9 +2762,11 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
elem := runtime.type_info_base(info.elem)
|
||||
if elem != nil {
|
||||
if n, ok := fi.optional_len.?; ok {
|
||||
fi.optional_len = nil
|
||||
fmt_array(fi, ptr, n, elem.size, elem, verb)
|
||||
return
|
||||
} else if fi.use_nul_termination {
|
||||
fi.use_nul_termination = false
|
||||
fmt_array_nul_terminated(fi, ptr, -1, elem.size, elem, verb)
|
||||
return
|
||||
}
|
||||
@@ -2855,8 +2868,10 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
n := info.count
|
||||
ptr := v.data
|
||||
if ol, ok := fi.optional_len.?; ok {
|
||||
fi.optional_len = nil
|
||||
n = min(n, ol)
|
||||
} else if fi.use_nul_termination {
|
||||
fi.use_nul_termination = false
|
||||
fmt_array_nul_terminated(fi, ptr, n, info.elem_size, info.elem, verb)
|
||||
return
|
||||
}
|
||||
@@ -2867,8 +2882,10 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
n := slice.len
|
||||
ptr := slice.data
|
||||
if ol, ok := fi.optional_len.?; ok {
|
||||
fi.optional_len = nil
|
||||
n = min(n, ol)
|
||||
} else if fi.use_nul_termination {
|
||||
fi.use_nul_termination = false
|
||||
fmt_array_nul_terminated(fi, ptr, n, info.elem_size, info.elem, verb)
|
||||
return
|
||||
}
|
||||
@@ -2879,8 +2896,10 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
n := array.len
|
||||
ptr := array.data
|
||||
if ol, ok := fi.optional_len.?; ok {
|
||||
fi.optional_len = nil
|
||||
n = min(n, ol)
|
||||
} else if fi.use_nul_termination {
|
||||
fi.use_nul_termination = false
|
||||
fmt_array_nul_terminated(fi, ptr, n, info.elem_size, info.elem, verb)
|
||||
return
|
||||
}
|
||||
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
package hash
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
crc64_ecma_182 :: proc "contextless" (data: []byte, seed := u64(0)) -> (result: u64) #no_bounds_check {
|
||||
result = seed
|
||||
#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.
|
||||
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 {
|
||||
data := data
|
||||
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
|
||||
*/
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
crc64_iso_3306 :: proc "contextless" (data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
|
||||
|
||||
result := seed
|
||||
@@ -738,4 +738,4 @@ crc64_iso_3306_inverse :: proc "contextless" (data: []byte, seed := u64(0)) -> u
|
||||
0x9fc0, 0x9e70, 0x9ca0, 0x9d10,
|
||||
0x9480, 0x9530, 0x97e0, 0x9650,
|
||||
0x9240, 0x93f0, 0x9120, 0x9090,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package hash
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
crc32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
|
||||
crc := ~seed
|
||||
buffer := raw_data(data)
|
||||
|
||||
+11
-11
@@ -3,7 +3,7 @@ package hash
|
||||
import "core:mem"
|
||||
import "base:intrinsics"
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
adler32 :: proc "contextless" (data: []byte, seed := u32(1)) -> u32 #no_bounds_check {
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
djb2 :: proc "contextless" (data: []byte, seed := u32(5381)) -> u32 {
|
||||
hash: u32 = seed
|
||||
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
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
fnv32_no_a :: proc "contextless" (data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
h: u32 = seed
|
||||
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
|
||||
|
||||
// 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 {
|
||||
h: u64 = seed
|
||||
for b in data {
|
||||
@@ -94,7 +94,7 @@ fnv64_no_a :: proc "contextless" (data: []byte, seed := u64(0xcbf29ce484222325))
|
||||
}
|
||||
return h
|
||||
}
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
fnv32a :: proc "contextless" (data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
h: u32 = seed
|
||||
for b in data {
|
||||
@@ -103,7 +103,7 @@ fnv32a :: proc "contextless" (data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
return h
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
fnv64a :: proc "contextless" (data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
||||
h: u64 = seed
|
||||
for b in data {
|
||||
@@ -112,7 +112,7 @@ fnv64a :: proc "contextless" (data: []byte, seed := u64(0xcbf29ce484222325)) ->
|
||||
return h
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
jenkins :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
|
||||
hash: u32 = seed
|
||||
for b in data {
|
||||
@@ -126,7 +126,7 @@ jenkins :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
|
||||
return hash
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
murmur32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
|
||||
c1_32: u32 : 0xcc9e2d51
|
||||
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
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
murmur64a :: proc "contextless" (data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
||||
m :: 0xc6a4a7935bd1e995
|
||||
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
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
murmur64b :: proc "contextless" (data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
||||
m :: 0x5bd1e995
|
||||
r :: 24
|
||||
@@ -286,7 +286,7 @@ murmur64b :: proc "contextless" (data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
||||
return u64(h1)<<32 | u64(h2)
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
sdbm :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
|
||||
hash: u32 = seed
|
||||
for b in data {
|
||||
|
||||
@@ -67,17 +67,17 @@ when !XXH_DISABLE_PREFETCH {
|
||||
}
|
||||
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
XXH_rotl32 :: #force_inline proc(x, r: u32) -> (res: u32) {
|
||||
return ((x << r) | (x >> (32 - r)))
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
XXH_rotl64 :: #force_inline proc(x, r: u64) -> (res: u64) {
|
||||
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) {
|
||||
if XXH_FORCE_MEMORY_ACCESS == 2 || alignment == .Aligned {
|
||||
#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) {
|
||||
if XXH_FORCE_MEMORY_ACCESS == 2 || alignment == .Aligned {
|
||||
#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)
|
||||
return u64(b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,13 +111,13 @@ XXH128_canonical :: struct {
|
||||
@param lhs, rhs The 64-bit integers to multiply
|
||||
@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) {
|
||||
t := u128(lhs) * u128(rhs)
|
||||
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) {
|
||||
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
|
||||
*/
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
XXH3_avalanche :: #force_inline proc(h64: xxh_u64) -> (res: xxh_u64) {
|
||||
res = XXH_xorshift_64(h64, 37)
|
||||
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
|
||||
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) {
|
||||
/* this mix is inspired by Pelle Evensen's rrmxmx */
|
||||
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).
|
||||
*/
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
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. */
|
||||
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) {
|
||||
length := len(input)
|
||||
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) {
|
||||
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
|
||||
*/
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
XXH3_len_0to16_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
|
||||
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.
|
||||
*/
|
||||
@(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) {
|
||||
acc128 := XXH128_hash_t{
|
||||
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) {
|
||||
length := len(input)
|
||||
|
||||
@@ -323,7 +323,7 @@ XXH3_len_17to128_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh
|
||||
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) {
|
||||
length := len(input)
|
||||
|
||||
@@ -379,7 +379,7 @@ XXH3_INIT_ACC :: [XXH_ACC_NB]xxh_u64{
|
||||
|
||||
XXH_SECRET_MERGEACCS_START :: 11
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
XXH3_hashLong_128b_internal :: #force_inline proc(
|
||||
input: []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.
|
||||
*/
|
||||
@(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) {
|
||||
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.
|
||||
*/
|
||||
@(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) {
|
||||
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(
|
||||
input: []u8, seed: xxh_u64, secret: []u8,
|
||||
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.
|
||||
*/
|
||||
@(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) {
|
||||
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)
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
XXH3_128bits_internal :: #force_inline proc(
|
||||
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 === */
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
XXH3_128_default :: proc(input: []u8) -> (hash: XXH3_128_hash) {
|
||||
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) {
|
||||
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) {
|
||||
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.
|
||||
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) {
|
||||
length := u32(len(input))
|
||||
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) {
|
||||
length := u32(len(input))
|
||||
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) {
|
||||
length := u64(len(input))
|
||||
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) {
|
||||
length := u64(len(input))
|
||||
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
|
||||
about strength.
|
||||
*/
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
XXH3_mix16B :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
|
||||
input_lo := XXH64_read64(input[0:])
|
||||
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. */
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
XXH3_len_17to128_64b :: proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
|
||||
assert(len(secret) >= XXH3_SECRET_SIZE_MIN)
|
||||
length := len(input)
|
||||
@@ -665,7 +665,7 @@ XXH3_MIDSIZE_MAX :: 240
|
||||
XXH3_MIDSIZE_STARTOFFSET :: 3
|
||||
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) {
|
||||
assert(len(secret) >= XXH3_SECRET_SIZE_MIN)
|
||||
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_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) {
|
||||
v := 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
|
||||
|
||||
/* scalar variants - universal */
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
XXH3_accumulate_512_scalar :: #force_inline proc(acc: []xxh_u64, input: []u8, secret: []u8) {
|
||||
xacc := acc /* presumed aligned */
|
||||
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) {
|
||||
xacc := acc /* presumed aligned */
|
||||
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) {
|
||||
#assert((XXH_SECRET_DEFAULT_SIZE & 15) == 0)
|
||||
|
||||
@@ -791,7 +791,7 @@ XXH_PREFETCH_DIST :: 320
|
||||
* Loops over XXH3_accumulate_512().
|
||||
* Assumption: nbStripes will not overflow the secret size
|
||||
*/
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
XXH3_accumulate :: #force_inline proc(
|
||||
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,
|
||||
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) {
|
||||
return XXH_mul_64_to_128_fold_64(
|
||||
acc[0] ~ XXH64_read64(secret),
|
||||
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) {
|
||||
result64 := start
|
||||
#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)
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
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) {
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
@(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) {
|
||||
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,
|
||||
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) {
|
||||
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
|
||||
why (uop cache maybe?), but the difference is large and easily measurable.
|
||||
*/
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
XXH3_hashLong_64b_withSeed_internal :: #force_no_inline proc(
|
||||
input: []u8,
|
||||
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.
|
||||
*/
|
||||
@(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) {
|
||||
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)
|
||||
|
||||
@(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) {
|
||||
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 === */
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
XXH3_64_default :: proc(input: []u8) -> (hash: xxh_u64) {
|
||||
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) {
|
||||
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) {
|
||||
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 }
|
||||
|
||||
@@ -40,7 +40,7 @@ XXH_PRIME32_3 :: 0xC2B2AE3D /*!< 0b11000010101100101010111000111101 */
|
||||
XXH_PRIME32_4 :: 0x27D4EB2F /*!< 0b00100111110101001110101100101111 */
|
||||
XXH_PRIME32_5 :: 0x165667B1 /*!< 0b00010110010101100110011110110001 */
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
XXH32_round :: #force_inline proc(seed, input: XXH32_hash) -> (res: XXH32_hash) {
|
||||
seed := seed
|
||||
|
||||
@@ -53,7 +53,7 @@ XXH32_round :: #force_inline proc(seed, input: XXH32_hash) -> (res: XXH32_hash)
|
||||
/*
|
||||
Mix all bits
|
||||
*/
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
XXH32_avalanche :: #force_inline proc(h32: u32) -> (res: u32) {
|
||||
h32 := h32
|
||||
|
||||
@@ -65,7 +65,7 @@ XXH32_avalanche :: #force_inline proc(h32: u32) -> (res: u32) {
|
||||
return h32
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
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) {
|
||||
#no_bounds_check b := u32(buf[0])
|
||||
@@ -143,7 +143,7 @@ XXH32_finalize :: #force_inline proc(h32: u32, buf: []u8, alignment: Alignment)
|
||||
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) {
|
||||
buf := 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) {
|
||||
h := (^u32be)(&canonical.digest)^
|
||||
return XXH32_hash(h)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ XXH_PRIME64_3 :: 0x165667B19E3779F9 /*!< 0b0001011001010110011001111011000110011
|
||||
XXH_PRIME64_4 :: 0x85EBCA77C2B2AE63 /*!< 0b1000010111101011110010100111011111000010101100101010111001100011 */
|
||||
XXH_PRIME64_5 :: 0x27D4EB2F165667C5 /*!< 0b0010011111010100111010110010111100010110010101100110011111000101 */
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
XXH64_round :: proc(acc, input: xxh_u64) -> (res: xxh_u64) {
|
||||
acc := acc
|
||||
|
||||
@@ -50,14 +50,14 @@ XXH64_round :: proc(acc, input: xxh_u64) -> (res: xxh_u64) {
|
||||
return acc
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
XXH64_mergeRound :: proc(acc, val: xxh_u64) -> (res: xxh_u64) {
|
||||
res = acc ~ XXH64_round(0, val)
|
||||
res = res * XXH_PRIME64_1 + XXH_PRIME64_4
|
||||
return res
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
XXH64_avalanche :: proc(h64: xxh_u64) -> (res: xxh_u64) {
|
||||
res = h64
|
||||
res ~= res >> 33
|
||||
@@ -68,7 +68,7 @@ XXH64_avalanche :: proc(h64: xxh_u64) -> (res: xxh_u64) {
|
||||
return res
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
XXH64_finalize :: proc(h64: xxh_u64, buf: []u8, alignment: Alignment) -> (res: xxh_u64) {
|
||||
buf := buf
|
||||
length := len(buf) & 31
|
||||
@@ -100,7 +100,7 @@ XXH64_finalize :: proc(h64: xxh_u64, buf: []u8, alignment: Alignment) -> (res: x
|
||||
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) {
|
||||
buf := input
|
||||
length := len(buf)
|
||||
@@ -191,7 +191,7 @@ XXH64_reset_state :: proc(state_ptr: ^XXH64_state, seed := XXH64_DEFAULT_SEED) -
|
||||
return .None
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
XXH64_update :: proc(state: ^XXH64_state, input: []u8) -> (err: Error) {
|
||||
buf := input
|
||||
length := len(buf)
|
||||
@@ -245,7 +245,7 @@ XXH64_update :: proc(state: ^XXH64_state, input: []u8) -> (err: Error) {
|
||||
return .None
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
XXH64_digest :: proc(state: ^XXH64_state) -> (res: XXH64_hash) {
|
||||
if state.total_len >= 32 {
|
||||
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) {
|
||||
h := (^u64be)(&canonical.digest)^
|
||||
return XXH64_hash(h)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context
|
||||
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) {
|
||||
context.allocator = allocator
|
||||
options := options
|
||||
@@ -743,4 +743,4 @@ destroy :: proc(img: ^Image) {
|
||||
@(init, private)
|
||||
_register :: proc() {
|
||||
image.register(.BMP, load_from_bytes, destroy)
|
||||
}
|
||||
}
|
||||
|
||||
+67
-4
@@ -112,7 +112,8 @@ Image_Option:
|
||||
|
||||
`.alpha_drop_if_present`
|
||||
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,
|
||||
unless you select `alpha_premultiply`.
|
||||
@@ -587,6 +588,32 @@ Channel :: enum u8 {
|
||||
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.
|
||||
return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok: bool) {
|
||||
// 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}
|
||||
|
||||
// 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.
|
||||
// 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.
|
||||
*/
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
read_data :: proc(z: $C, $T: typeid) -> (res: T, err: compress.General_Error) {
|
||||
if r, e := compress.read_data(z, T); e != .None {
|
||||
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) {
|
||||
if r, e := compress.read_u8(z); e != .None {
|
||||
return {}, .Stream_Too_Short
|
||||
@@ -1405,4 +1468,4 @@ write_bytes :: proc(buf: ^bytes.Buffer, data: []u8) -> (err: compress.General_Er
|
||||
return .Resize_Failed
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ which :: proc{
|
||||
|
||||
which_file :: proc(path: string) -> Which_File_Type {
|
||||
f, err := os.open(path)
|
||||
if err != 0 {
|
||||
if err != nil {
|
||||
return .Unknown
|
||||
}
|
||||
header: [128]byte
|
||||
|
||||
@@ -213,7 +213,7 @@ write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: b
|
||||
}
|
||||
|
||||
fd, err := open(filename, flags, mode)
|
||||
if err != 0 {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer close(fd)
|
||||
|
||||
@@ -450,7 +450,7 @@ when false {
|
||||
}
|
||||
|
||||
fd, fderr := open(filename, flags, mode)
|
||||
if fderr != 0 {
|
||||
if fderr != nil {
|
||||
return .Cannot_Open_File
|
||||
}
|
||||
defer close(fd)
|
||||
|
||||
@@ -170,7 +170,7 @@ load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context
|
||||
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) {
|
||||
context.allocator = allocator
|
||||
options := options
|
||||
@@ -373,4 +373,4 @@ qoi_hash :: #force_inline proc(pixel: RGBA_Pixel) -> (index: u8) {
|
||||
@(init, private)
|
||||
_register :: proc() {
|
||||
image.register(.QOI, load_from_bytes, destroy)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,10 +375,6 @@ write_at_least :: proc(w: Writer, buf: []byte, min: int) -> (n: int, err: Error)
|
||||
nn, err = write(w, buf[n:])
|
||||
n += nn
|
||||
}
|
||||
|
||||
if err == nil && n < min {
|
||||
err = .Short_Write
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -229,7 +229,7 @@ sqrt_complex128 :: proc "contextless" (x: complex128) -> complex128 {
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
switch re, im := real(x), imag(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)
|
||||
return complex32(exp_complex64(complex64(x)))
|
||||
}
|
||||
exp_complex64 :: proc "contextless" (x: complex64) -> complex64 {
|
||||
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 {
|
||||
if x == 0 { // Guaranteed also true for x == -0.
|
||||
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)
|
||||
return complex32(pow_complex64(complex64(x), complex64(y)))
|
||||
}
|
||||
pow_complex64 :: proc "contextless" (x, y: complex64) -> complex64 {
|
||||
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 {
|
||||
return math.LN10*ln(x)
|
||||
return complex32(log10_complex64(complex64(x)))
|
||||
}
|
||||
log10_complex64 :: proc "contextless" (x: complex64) -> complex64 {
|
||||
return math.LN10*ln(x)
|
||||
@@ -421,7 +372,7 @@ log10_complex128 :: proc "contextless" (x: complex128) -> complex128 {
|
||||
|
||||
|
||||
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 {
|
||||
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 {
|
||||
s, c := math.sincos(θ)
|
||||
return complex(r*c, r*s)
|
||||
return complex32(rect_complex64(f32(r), f32(θ)))
|
||||
}
|
||||
rect_complex64 :: proc "contextless" (r, θ: f32) -> complex64 {
|
||||
s, c := math.sincos(θ)
|
||||
|
||||
@@ -61,8 +61,7 @@ atanh :: proc{
|
||||
|
||||
|
||||
acos_complex32 :: proc "contextless" (x: complex32) -> complex32 {
|
||||
w := asin(x)
|
||||
return complex(math.PI/2 - real(w), -imag(w))
|
||||
return complex32(acos_complex64(complex64(x)))
|
||||
}
|
||||
acos_complex64 :: proc "contextless" (x: complex64) -> complex64 {
|
||||
w := asin(x)
|
||||
@@ -75,14 +74,7 @@ acos_complex128 :: proc "contextless" (x: complex128) -> complex128 {
|
||||
|
||||
|
||||
acosh_complex32 :: proc "contextless" (x: complex32) -> complex32 {
|
||||
if x == 0 {
|
||||
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))
|
||||
return complex32(acosh_complex64(complex64(x)))
|
||||
}
|
||||
acosh_complex64 :: proc "contextless" (x: complex64) -> complex64 {
|
||||
if x == 0 {
|
||||
@@ -257,9 +249,7 @@ atan_complex128 :: proc "contextless" (x: complex128) -> complex128 {
|
||||
}
|
||||
|
||||
atanh_complex32 :: proc "contextless" (x: complex32) -> complex32 {
|
||||
z := complex(-imag(x), real(x)) // z = i * x
|
||||
z = atan(z)
|
||||
return complex(imag(z), -real(z)) // z = -i * z
|
||||
return complex32(atanh_complex64(complex64(x)))
|
||||
}
|
||||
atanh_complex64 :: proc "contextless" (x: complex64) -> complex64 {
|
||||
z := complex(-imag(x), real(x)) // z = i * x
|
||||
|
||||
@@ -275,7 +275,8 @@ to_ptr :: proc{vector_to_ptr, matrix_to_ptr}
|
||||
vector_angle_between :: proc "contextless" (a, b: $V/[$N]$E) -> E {
|
||||
a0 := normalize0(a)
|
||||
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 {
|
||||
c := normalize0(conj(a) * b)
|
||||
|
||||
@@ -688,7 +688,7 @@ _internal_noise_4d_unskewed_base :: proc(seed: i64, coord: Vec4) -> (value: f32)
|
||||
/*
|
||||
Utility functions
|
||||
*/
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
grad_2d :: proc(seed: i64, svp: [2]i64, delta: [2]f32) -> (value: f32) {
|
||||
hash := seed ~ svp.x ~ svp.y
|
||||
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
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
grad_3d :: proc(seed: i64, rvp: [3]i64, delta: [3]f32) -> (value: f32) {
|
||||
hash := (seed ~ rvp.x) ~ (rvp.y ~ rvp.z)
|
||||
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
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
grad_4d :: proc(seed: i64, svp: [4]i64, delta: [4]f32) -> (value: f32) {
|
||||
hash := seed ~ (svp.x ~ svp.y) ~ (svp.z ~ svp.w)
|
||||
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}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
fast_floor :: proc(x: f64) -> (floored: i64) {
|
||||
xi := i64(x)
|
||||
return x < f64(xi) ? xi - 1 : xi
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
@(optimization_mode="favor_size")
|
||||
fast_round :: proc(x: f64) -> (rounded: i64) {
|
||||
return x < 0 ? i64(x - 0.5) : i64(x + 0.5)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,12 +8,12 @@ float32_uniform :: float32_range
|
||||
// Triangular Distribution
|
||||
// See: http://wikipedia.org/wiki/Triangular_distribution
|
||||
@(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 {
|
||||
return lo
|
||||
}
|
||||
lo, hi := lo, hi
|
||||
u := float64()
|
||||
u := float64(gen)
|
||||
c := f64(0.5) if mode == nil else clamp((mode.?-lo) / (hi-lo), 0, 1)
|
||||
if u > c {
|
||||
u = 1-u
|
||||
@@ -26,12 +26,12 @@ float64_triangular :: proc(lo, hi: f64, mode: Maybe(f64)) -> f64 {
|
||||
// Triangular Distribution
|
||||
// See: http://wikipedia.org/wiki/Triangular_distribution
|
||||
@(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 {
|
||||
return lo
|
||||
}
|
||||
lo, hi := lo, hi
|
||||
u := float32()
|
||||
u := float32(gen)
|
||||
c := f32(0.5) if mode == nil else clamp((mode.?-lo) / (hi-lo), 0, 1)
|
||||
if u > c {
|
||||
u = 1-u
|
||||
@@ -44,25 +44,25 @@ float32_triangular :: proc(lo, hi: f32, mode: Maybe(f32)) -> f32 {
|
||||
|
||||
// Normal/Gaussian Distribution
|
||||
@(require_results)
|
||||
float64_normal :: proc(mean, stddev: f64) -> f64 {
|
||||
return norm_float64() * stddev + mean
|
||||
float64_normal :: proc(mean, stddev: f64, gen := context.random_generator) -> f64 {
|
||||
return norm_float64(gen) * stddev + mean
|
||||
}
|
||||
// Normal/Gaussian Distribution
|
||||
@(require_results)
|
||||
float32_normal :: proc(mean, stddev: f32) -> f32 {
|
||||
return f32(float64_normal(f64(mean), f64(stddev)))
|
||||
float32_normal :: proc(mean, stddev: f32, gen := context.random_generator) -> f32 {
|
||||
return f32(float64_normal(f64(mean), f64(stddev), gen))
|
||||
}
|
||||
|
||||
|
||||
// Log Normal Distribution
|
||||
@(require_results)
|
||||
float64_log_normal :: proc(mean, stddev: f64) -> f64 {
|
||||
return math.exp(float64_normal(mean, stddev))
|
||||
float64_log_normal :: proc(mean, stddev: f64, gen := context.random_generator) -> f64 {
|
||||
return math.exp(float64_normal(mean, stddev, gen))
|
||||
}
|
||||
// Log Normal Distribution
|
||||
@(require_results)
|
||||
float32_log_normal :: proc(mean, stddev: f32) -> f32 {
|
||||
return f32(float64_log_normal(f64(mean), f64(stddev)))
|
||||
float32_log_normal :: proc(mean, stddev: f32, gen := context.random_generator) -> f32 {
|
||||
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
|
||||
// negative infinity to 0 if lambda <= 0
|
||||
@(require_results)
|
||||
float64_exponential :: proc(lambda: f64) -> f64 {
|
||||
return - math.ln(1 - float64()) / lambda
|
||||
float64_exponential :: proc(lambda: f64, gen := context.random_generator) -> f64 {
|
||||
return - math.ln(1 - float64(gen)) / lambda
|
||||
}
|
||||
// Exponential Distribution
|
||||
// `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
|
||||
// negative infinity to 0 if lambda <= 0
|
||||
@(require_results)
|
||||
float32_exponential :: proc(lambda: f32) -> f32 {
|
||||
return f32(float64_exponential(f64(lambda)))
|
||||
float32_exponential :: proc(lambda: f32, gen := context.random_generator) -> f32 {
|
||||
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)
|
||||
@(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 {
|
||||
panic(#procedure + ": alpha and beta must be > 0.0")
|
||||
}
|
||||
@@ -112,11 +112,11 @@ float64_gamma :: proc(alpha, beta: f64) -> f64 {
|
||||
bbb := alpha - LOG4
|
||||
ccc := alpha + ainv
|
||||
for {
|
||||
u1 := float64()
|
||||
u1 := float64(gen)
|
||||
if !(1e-7 < u1 && u1 < 0.9999999) {
|
||||
continue
|
||||
}
|
||||
u2 := 1 - float64()
|
||||
u2 := 1 - float64(gen)
|
||||
v := math.ln(u1 / (1 - u1)) / ainv
|
||||
x := alpha * math.exp(v)
|
||||
z := u1 * u1 * u2
|
||||
@@ -127,12 +127,12 @@ float64_gamma :: proc(alpha, beta: f64) -> f64 {
|
||||
}
|
||||
case alpha == 1:
|
||||
// float64_exponential(1/beta)
|
||||
return -math.ln(1 - float64()) * beta
|
||||
return -math.ln(1 - float64(gen)) * beta
|
||||
case:
|
||||
// ALGORITHM GS of Statistical Computing - Kennedy & Gentle
|
||||
x: f64
|
||||
for {
|
||||
u := float64()
|
||||
u := float64(gen)
|
||||
b := (math.e + alpha) / math.e
|
||||
p := b * u
|
||||
if p <= 1 {
|
||||
@@ -140,7 +140,7 @@ float64_gamma :: proc(alpha, beta: f64) -> f64 {
|
||||
} else {
|
||||
x = -math.ln((b - p) / alpha)
|
||||
}
|
||||
u1 := float64()
|
||||
u1 := float64(gen)
|
||||
if p > 1 {
|
||||
if u1 <= math.pow(x, alpha-1) {
|
||||
break
|
||||
@@ -162,8 +162,8 @@ float64_gamma :: proc(alpha, beta: f64) -> f64 {
|
||||
//
|
||||
// mean is alpha*beta, variance is math.pow(alpha*beta, 2)
|
||||
@(require_results)
|
||||
float32_gamma :: proc(alpha, beta: f32) -> f32 {
|
||||
return f32(float64_gamma(f64(alpha), f64(beta)))
|
||||
float32_gamma :: proc(alpha, beta: f32, gen := context.random_generator) -> f32 {
|
||||
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
|
||||
@(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 {
|
||||
panic(#procedure + ": alpha and beta must be > 0.0")
|
||||
}
|
||||
// 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 {
|
||||
return y / (y + float64_gamma(beta, 1.0))
|
||||
return y / (y + float64_gamma(beta, 1.0, gen))
|
||||
}
|
||||
return 0
|
||||
}
|
||||
@@ -190,35 +190,35 @@ float64_beta :: proc(alpha, beta: f64) -> f64 {
|
||||
//
|
||||
// Return values range between 0 and 1
|
||||
@(require_results)
|
||||
float32_beta :: proc(alpha, beta: f32) -> f32 {
|
||||
return f32(float64_beta(f64(alpha), f64(beta)))
|
||||
float32_beta :: proc(alpha, beta: f32, gen := context.random_generator) -> f32 {
|
||||
return f32(float64_beta(f64(alpha), f64(beta), gen))
|
||||
}
|
||||
|
||||
|
||||
// Pareto distribution, `alpha` is the shape parameter.
|
||||
// https://wikipedia.org/wiki/Pareto_distribution
|
||||
@(require_results)
|
||||
float64_pareto :: proc(alpha: f64) -> f64 {
|
||||
return math.pow(1 - float64(), -1.0 / alpha)
|
||||
float64_pareto :: proc(alpha: f64, gen := context.random_generator) -> f64 {
|
||||
return math.pow(1 - float64(gen), -1.0 / alpha)
|
||||
}
|
||||
// Pareto distribution, `alpha` is the shape parameter.
|
||||
// https://wikipedia.org/wiki/Pareto_distribution
|
||||
@(require_results)
|
||||
float32_pareto :: proc(alpha, beta: f32) -> f32 {
|
||||
return f32(float64_pareto(f64(alpha)))
|
||||
float32_pareto :: proc(alpha, beta: f32, gen := context.random_generator) -> f32 {
|
||||
return f32(float64_pareto(f64(alpha), gen))
|
||||
}
|
||||
|
||||
|
||||
// Weibull distribution, `alpha` is the scale parameter, `beta` is the shape parameter.
|
||||
@(require_results)
|
||||
float64_weibull :: proc(alpha, beta: f64) -> f64 {
|
||||
u := 1 - float64()
|
||||
float64_weibull :: proc(alpha, beta: f64, gen := context.random_generator) -> f64 {
|
||||
u := 1 - float64(gen)
|
||||
return alpha * math.pow(-math.ln(u), 1.0/beta)
|
||||
}
|
||||
// Weibull distribution, `alpha` is the scale parameter, `beta` is the shape parameter.
|
||||
@(require_results)
|
||||
float32_weibull :: proc(alpha, beta: f32) -> f32 {
|
||||
return f32(float64_weibull(f64(alpha), f64(beta)))
|
||||
float32_weibull :: proc(alpha, beta: f32, gen := context.random_generator) -> f32 {
|
||||
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
|
||||
// When `kappa` is zero, the Distribution is a uniform Distribution over the range 0 to 2pi
|
||||
@(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.
|
||||
|
||||
mu := mean_angle
|
||||
if kappa <= 1e-6 {
|
||||
return math.TAU * float64()
|
||||
return math.TAU * float64(gen)
|
||||
}
|
||||
|
||||
s := 0.5 / kappa
|
||||
t := s + math.sqrt(1 + s*s)
|
||||
z: f64
|
||||
for {
|
||||
u1 := float64()
|
||||
u1 := float64(gen)
|
||||
z = math.cos(math.TAU * 0.5 * u1)
|
||||
|
||||
d := z / (t + z)
|
||||
u2 := float64()
|
||||
u2 := float64(gen)
|
||||
if u2 < 1 - d*d || u2 <= (1-d)*math.exp(d) {
|
||||
break
|
||||
}
|
||||
@@ -251,7 +251,7 @@ float64_von_mises :: proc(mean_angle, kappa: f64) -> f64 {
|
||||
|
||||
q := 1.0 / t
|
||||
f := (q + z) / (1 + q*z)
|
||||
u3 := float64()
|
||||
u3 := float64(gen)
|
||||
if u3 > 0.5 {
|
||||
return math.mod(mu + math.acos(f), math.TAU)
|
||||
} else {
|
||||
@@ -263,57 +263,57 @@ float64_von_mises :: proc(mean_angle, kappa: f64) -> f64 {
|
||||
// `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
|
||||
@(require_results)
|
||||
float32_von_mises :: proc(mean_angle, kappa: f32) -> f32 {
|
||||
return f32(float64_von_mises(f64(mean_angle), f64(kappa)))
|
||||
float32_von_mises :: proc(mean_angle, kappa: f32, gen := context.random_generator) -> f32 {
|
||||
return f32(float64_von_mises(f64(mean_angle), f64(kappa), gen))
|
||||
}
|
||||
|
||||
|
||||
// Cauchy-Lorentz Distribution
|
||||
// `x_0` is the location, `gamma` is the scale where `gamma` > 0
|
||||
@(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)
|
||||
|
||||
// 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
|
||||
// `x_0` is the location, `gamma` is the scale where `gamma` > 0
|
||||
@(require_results)
|
||||
float32_cauchy_lorentz :: proc(x_0, gamma: f32) -> f32 {
|
||||
return f32(float64_cauchy_lorentz(f64(x_0), f64(gamma)))
|
||||
float32_cauchy_lorentz :: proc(x_0, gamma: f32, gen := context.random_generator) -> f32 {
|
||||
return f32(float64_cauchy_lorentz(f64(x_0), f64(gamma), gen))
|
||||
}
|
||||
|
||||
|
||||
// Log Cauchy-Lorentz Distribution
|
||||
// `x_0` is the location, `gamma` is the scale where `gamma` > 0
|
||||
@(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)
|
||||
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
|
||||
// `x_0` is the location, `gamma` is the scale where `gamma` > 0
|
||||
@(require_results)
|
||||
float32_log_cauchy_lorentz :: proc(x_0, gamma: f32) -> f32 {
|
||||
return f32(float64_log_cauchy_lorentz(f64(x_0), f64(gamma)))
|
||||
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), gen))
|
||||
}
|
||||
|
||||
|
||||
// Laplace Distribution
|
||||
// `b` is the scale where `b` > 0
|
||||
@(require_results)
|
||||
float64_laplace :: proc(mean, b: f64) -> f64 {
|
||||
float64_laplace :: proc(mean, b: f64, gen := context.random_generator) -> f64 {
|
||||
assert(b > 0)
|
||||
p := float64()-0.5
|
||||
p := float64(gen)-0.5
|
||||
return -math.sign(p)*math.ln(1 - 2*abs(p))*b + mean
|
||||
}
|
||||
// Laplace Distribution
|
||||
// `b` is the scale where `b` > 0
|
||||
@(require_results)
|
||||
float32_laplace :: proc(mean, b: f32) -> f32 {
|
||||
return f32(float64_laplace(f64(mean), f64(b)))
|
||||
float32_laplace :: proc(mean, b: f32, gen := context.random_generator) -> f32 {
|
||||
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
|
||||
// Both `eta` and `b` must be > 0
|
||||
@(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 {
|
||||
panic(#procedure + ": eta and b must be > 0.0")
|
||||
}
|
||||
|
||||
p := float64()
|
||||
p := float64(gen)
|
||||
return math.ln(1 - math.ln(1 - p)/eta)/b
|
||||
}
|
||||
// Gompertz Distribution
|
||||
// `eta` is the shape, `b` is the scale
|
||||
// Both `eta` and `b` must be > 0
|
||||
@(require_results)
|
||||
float32_gompertz :: proc(eta, b: f32) -> f32 {
|
||||
return f32(float64_gompertz(f64(eta), f64(b)))
|
||||
float32_gompertz :: proc(eta, b: f32, gen := context.random_generator) -> f32 {
|
||||
return f32(float64_gompertz(f64(eta), f64(b), gen))
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import "core:math"
|
||||
// https://www.jstatsoft.org/article/view/v005i08 [web page]
|
||||
//
|
||||
@(require_results)
|
||||
exp_float64 :: proc() -> f64 {
|
||||
exp_float64 :: proc(gen := context.random_generator) -> f64 {
|
||||
re :: 7.69711747013104972
|
||||
|
||||
@(static, rodata)
|
||||
@@ -199,16 +199,16 @@ exp_float64 :: proc() -> f64 {
|
||||
}
|
||||
|
||||
for {
|
||||
j := uint32()
|
||||
j := uint32(gen)
|
||||
i := j & 0xFF
|
||||
x := f64(j) * f64(we[i])
|
||||
if j < ke[i] {
|
||||
return x
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import "core:math"
|
||||
// https://www.jstatsoft.org/article/view/v005i08 [web page]
|
||||
//
|
||||
@(require_results)
|
||||
norm_float64 :: proc() -> f64 {
|
||||
norm_float64 :: proc(gen := context.random_generator) -> f64 {
|
||||
rn :: 3.442619855899
|
||||
|
||||
@(static, rodata)
|
||||
@@ -116,7 +116,7 @@ norm_float64 :: proc() -> f64 {
|
||||
}
|
||||
|
||||
for {
|
||||
j := i32(uint32())
|
||||
j := i32(uint32(gen))
|
||||
i := j & 0x7f
|
||||
x := f64(j) * f64(wn[i])
|
||||
if u32(abs(j)) < kn[i] {
|
||||
@@ -126,15 +126,15 @@ norm_float64 :: proc() -> f64 {
|
||||
|
||||
if i == 0 {
|
||||
for {
|
||||
x = -math.ln(float64()) * (1.0/ rn)
|
||||
y := -math.ln(float64())
|
||||
x = -math.ln(float64(gen)) * (1.0/ rn)
|
||||
y := -math.ln(float64(gen))
|
||||
if y+y >= x*x {
|
||||
break
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
+66
-47
@@ -9,6 +9,10 @@ import "base:runtime"
|
||||
import "core:math"
|
||||
import "core:mem"
|
||||
|
||||
Generator :: runtime.Random_Generator
|
||||
|
||||
Generator_Query_Info :: runtime.Random_Generator_Query_Info
|
||||
|
||||
Default_Random_State :: runtime.Default_Random_State
|
||||
default_random_generator :: runtime.default_random_generator
|
||||
|
||||
@@ -54,7 +58,7 @@ Example:
|
||||
import "core:fmt"
|
||||
|
||||
set_global_seed_example :: proc() {
|
||||
rand.set_global_seed(1)
|
||||
rand.reset(1)
|
||||
fmt.println(rand.uint64())
|
||||
}
|
||||
|
||||
@@ -62,15 +66,24 @@ Possible Output:
|
||||
|
||||
10
|
||||
*/
|
||||
reset :: proc(seed: u64) {
|
||||
runtime.random_generator_reset_u64(context.random_generator, seed)
|
||||
reset :: proc(seed: u64, gen := context.random_generator) {
|
||||
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)
|
||||
_random_u64 :: proc() -> (res: u64) {
|
||||
ok := runtime.random_generator_read_ptr(context.random_generator, &res, size_of(res))
|
||||
assert(ok, "uninitialized context.random_generator")
|
||||
_random_u64 :: proc(gen := context.random_generator) -> (res: u64) {
|
||||
ok := runtime.random_generator_read_ptr(gen, &res, size_of(res))
|
||||
assert(ok, "uninitialized gen/context.random_generator")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -95,7 +108,7 @@ Possible Output:
|
||||
|
||||
*/
|
||||
@(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.
|
||||
@@ -118,7 +131,7 @@ Possible Output:
|
||||
|
||||
*/
|
||||
@(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.
|
||||
@@ -141,9 +154,9 @@ Possible Output:
|
||||
|
||||
*/
|
||||
@(require_results)
|
||||
uint128 :: proc() -> (val: u128) {
|
||||
a := u128(_random_u64())
|
||||
b := u128(_random_u64())
|
||||
uint128 :: proc(gen := context.random_generator) -> (val: u128) {
|
||||
a := u128(_random_u64(gen))
|
||||
b := u128(_random_u64(gen))
|
||||
return (a<<64) | b
|
||||
}
|
||||
|
||||
@@ -168,7 +181,7 @@ Possible Output:
|
||||
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.
|
||||
@@ -191,7 +204,7 @@ Possible Output:
|
||||
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.
|
||||
@@ -214,7 +227,7 @@ Possible Output:
|
||||
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.
|
||||
@@ -242,17 +255,17 @@ Possible Output:
|
||||
|
||||
*/
|
||||
@(require_results)
|
||||
int31_max :: proc(n: i32) -> (val: i32) {
|
||||
int31_max :: proc(n: i32, gen := context.random_generator) -> (val: i32) {
|
||||
if n <= 0 {
|
||||
panic("Invalid argument to int31_max")
|
||||
}
|
||||
if n&(n-1) == 0 {
|
||||
return int31() & (n-1)
|
||||
return int31(gen) & (n-1)
|
||||
}
|
||||
max := i32((1<<31) - 1 - (1<<31)%u32(n))
|
||||
v := int31()
|
||||
v := int31(gen)
|
||||
for v > max {
|
||||
v = int31()
|
||||
v = int31(gen)
|
||||
}
|
||||
return v % n
|
||||
}
|
||||
@@ -283,17 +296,17 @@ Possible Output:
|
||||
|
||||
*/
|
||||
@(require_results)
|
||||
int63_max :: proc(n: i64) -> (val: i64) {
|
||||
int63_max :: proc(n: i64, gen := context.random_generator) -> (val: i64) {
|
||||
if n <= 0 {
|
||||
panic("Invalid argument to int63_max")
|
||||
}
|
||||
if n&(n-1) == 0 {
|
||||
return int63() & (n-1)
|
||||
return int63(gen) & (n-1)
|
||||
}
|
||||
max := i64((1<<63) - 1 - (1<<63)%u64(n))
|
||||
v := int63()
|
||||
v := int63(gen)
|
||||
for v > max {
|
||||
v = int63()
|
||||
v = int63(gen)
|
||||
}
|
||||
return v % n
|
||||
}
|
||||
@@ -324,17 +337,17 @@ Possible Output:
|
||||
|
||||
*/
|
||||
@(require_results)
|
||||
int127_max :: proc(n: i128) -> (val: i128) {
|
||||
int127_max :: proc(n: i128, gen := context.random_generator) -> (val: i128) {
|
||||
if n <= 0 {
|
||||
panic("Invalid argument to int127_max")
|
||||
}
|
||||
if n&(n-1) == 0 {
|
||||
return int127() & (n-1)
|
||||
return int127(gen) & (n-1)
|
||||
}
|
||||
max := i128((1<<127) - 1 - (1<<127)%u128(n))
|
||||
v := int127()
|
||||
v := int127(gen)
|
||||
for v > max {
|
||||
v = int127()
|
||||
v = int127(gen)
|
||||
}
|
||||
return v % n
|
||||
}
|
||||
@@ -365,14 +378,14 @@ Possible Output:
|
||||
|
||||
*/
|
||||
@(require_results)
|
||||
int_max :: proc(n: int) -> (val: int) {
|
||||
int_max :: proc(n: int, gen := context.random_generator) -> (val: int) {
|
||||
if n <= 0 {
|
||||
panic("Invalid argument to int_max")
|
||||
}
|
||||
when size_of(int) == 4 {
|
||||
return int(int31_max(i32(n)))
|
||||
return int(int31_max(i32(n), gen))
|
||||
} else {
|
||||
return int(int63_max(i64(n)))
|
||||
return int(int63_max(i64(n), gen))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,7 +409,7 @@ Possible Output:
|
||||
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.
|
||||
@@ -418,7 +431,7 @@ Possible Output:
|
||||
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.
|
||||
@@ -446,9 +459,9 @@ Possible Output:
|
||||
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")
|
||||
val = (high-low)*float64() + low
|
||||
val = (high-low)*float64(gen) + low
|
||||
if val >= high {
|
||||
val = max(low, high * (1 - math.F64_EPSILON))
|
||||
}
|
||||
@@ -481,9 +494,9 @@ Possible Output:
|
||||
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")
|
||||
val = (high-low)*float32() + low
|
||||
val = (high-low)*float32(gen) + low
|
||||
if val >= high {
|
||||
val = max(low, high * (1 - math.F32_EPSILON))
|
||||
}
|
||||
@@ -518,12 +531,12 @@ Possible Output:
|
||||
|
||||
*/
|
||||
@(require_results)
|
||||
read :: proc(p: []byte) -> (n: int) {
|
||||
read :: proc(p: []byte, gen := context.random_generator) -> (n: int) {
|
||||
pos := i8(0)
|
||||
val := i64(0)
|
||||
for n = 0; n < len(p); n += 1 {
|
||||
if pos == 0 {
|
||||
val = int63()
|
||||
val = int63(gen)
|
||||
pos = 7
|
||||
}
|
||||
p[n] = byte(val)
|
||||
@@ -566,10 +579,10 @@ Possible Output:
|
||||
|
||||
*/
|
||||
@(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
|
||||
for i := 0; i < n; i += 1 {
|
||||
j := int_max(i+1)
|
||||
j := int_max(i+1, gen)
|
||||
m[i] = m[j]
|
||||
m[j] = i
|
||||
}
|
||||
@@ -599,14 +612,20 @@ Possible Output:
|
||||
[2, 4, 3, 1]
|
||||
|
||||
*/
|
||||
shuffle :: proc(array: $T/[]$E) {
|
||||
shuffle :: proc(array: $T/[]$E, gen := context.random_generator) {
|
||||
n := i64(len(array))
|
||||
if n < 2 {
|
||||
return
|
||||
}
|
||||
|
||||
for i := i64(n - 1); i > 0; i -= 1 {
|
||||
j := int63_max(i + 1)
|
||||
i := n - 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]
|
||||
}
|
||||
}
|
||||
@@ -641,17 +660,17 @@ Possible Output:
|
||||
|
||||
*/
|
||||
@(require_results)
|
||||
choice :: proc(array: $T/[]$E) -> (res: E) {
|
||||
choice :: proc(array: $T/[]$E, gen := context.random_generator) -> (res: E) {
|
||||
n := i64(len(array))
|
||||
if n < 1 {
|
||||
return E{}
|
||||
}
|
||||
return array[int63_max(n)]
|
||||
return array[int63_max(n, gen)]
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
choice_enum :: proc($T: typeid) -> T
|
||||
choice_enum :: proc($T: typeid, gen := context.random_generator) -> T
|
||||
where
|
||||
intrinsics.type_is_enum(T),
|
||||
size_of(T) <= 8,
|
||||
@@ -659,11 +678,11 @@ choice_enum :: proc($T: typeid) -> T
|
||||
{
|
||||
when intrinsics.type_is_unsigned(intrinsics.type_core_type(T)) &&
|
||||
u64(max(T)) > u64(max(i64)) {
|
||||
i := uint64() % u64(len(T))
|
||||
i := uint64(gen) % u64(len(T))
|
||||
i += u64(min(T))
|
||||
return T(i)
|
||||
} else {
|
||||
i := int63_max(i64(len(T)))
|
||||
i := int63_max(i64(len(T)), gen)
|
||||
i += i64(min(T))
|
||||
return T(i)
|
||||
}
|
||||
|
||||
@@ -748,9 +748,7 @@ dynamic_pool_alloc_bytes :: proc(p: ^Dynamic_Pool, bytes: int) -> ([]byte, Alloc
|
||||
return
|
||||
}
|
||||
|
||||
n := bytes
|
||||
extra := p.alignment - (n % p.alignment)
|
||||
n += extra
|
||||
n := align_formula(bytes, p.alignment)
|
||||
if n > p.block_size {
|
||||
return nil, .Invalid_Argument
|
||||
}
|
||||
|
||||
@@ -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
|
||||
// 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) {
|
||||
if size < SMALL_BLOCK_SIZE {
|
||||
// Store small blocks in first list.
|
||||
@@ -297,7 +297,7 @@ mapping_insert :: proc(size: uint) -> (fl, sl: i32) {
|
||||
return
|
||||
}
|
||||
|
||||
@(optimization_mode="speed", require_results)
|
||||
@(optimization_mode="favor_size", require_results)
|
||||
mapping_round :: #force_inline proc(size: uint) -> (rounded: uint) {
|
||||
rounded = 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)
|
||||
@(optimization_mode="speed", require_results)
|
||||
@(optimization_mode="favor_size", require_results)
|
||||
mapping_search :: proc(size: uint) -> (fl, sl: i32) {
|
||||
return mapping_insert(mapping_round(size))
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ map_file :: proc{
|
||||
|
||||
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)
|
||||
if err != 0 {
|
||||
if err != nil {
|
||||
return nil, .Open_Failure
|
||||
}
|
||||
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) {
|
||||
size, os_err := os.file_size(os.Handle(fd))
|
||||
if os_err != 0 {
|
||||
if os_err != nil {
|
||||
return nil, .Stat_Failure
|
||||
}
|
||||
if size < 0 {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
//+build freebsd, openbsd, netbsd
|
||||
//+private
|
||||
//+build !darwin
|
||||
//+build !linux
|
||||
//+build !windows
|
||||
package mem_virtual
|
||||
|
||||
_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
|
||||
+32
-32
@@ -53,9 +53,9 @@ _create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (so
|
||||
unreachable()
|
||||
}
|
||||
|
||||
sock, ok := os.socket(c_family, c_type, c_protocol)
|
||||
if ok != os.ERROR_NONE {
|
||||
err = Create_Socket_Error(ok)
|
||||
sock, sock_err := os.socket(c_family, c_type, c_protocol)
|
||||
if sock_err != nil {
|
||||
err = Create_Socket_Error(os.is_platform_error(sock_err) or_else -1)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -84,8 +84,8 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
|
||||
|
||||
sockaddr := _endpoint_to_sockaddr(endpoint)
|
||||
res := os.connect(os.Socket(skt), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len))
|
||||
if res != os.ERROR_NONE {
|
||||
err = Dial_Error(res)
|
||||
if res != nil {
|
||||
err = Dial_Error(os.is_platform_error(res) or_else -1)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -100,11 +100,11 @@ _bind :: proc(skt: Any_Socket, ep: Endpoint) -> (err: Network_Error) {
|
||||
sockaddr := _endpoint_to_sockaddr(ep)
|
||||
s := any_socket_to_socket(skt)
|
||||
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 {
|
||||
err = .Privileged_Port_Without_Root
|
||||
} else {
|
||||
err = Bind_Error(res)
|
||||
err = Bind_Error(os.is_platform_error(res) or_else -1)
|
||||
}
|
||||
}
|
||||
return
|
||||
@@ -128,8 +128,8 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (skt: TCP_
|
||||
bind(sock, interface_endpoint) or_return
|
||||
|
||||
res := os.listen(os.Socket(skt), backlog)
|
||||
if res != os.ERROR_NONE {
|
||||
err = Listen_Error(res)
|
||||
if res != nil {
|
||||
err = Listen_Error(os.is_platform_error(res) or_else -1)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -141,9 +141,9 @@ _accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (client
|
||||
sockaddr: os.SOCKADDR_STORAGE_LH
|
||||
sockaddrlen := c.int(size_of(sockaddr))
|
||||
|
||||
client_sock, ok := os.accept(os.Socket(sock), cast(^os.SOCKADDR) &sockaddr, &sockaddrlen)
|
||||
if ok != os.ERROR_NONE {
|
||||
err = Accept_Error(ok)
|
||||
client_sock, client_sock_err := os.accept(os.Socket(sock), cast(^os.SOCKADDR) &sockaddr, &sockaddrlen)
|
||||
if client_sock_err != nil {
|
||||
err = Accept_Error(os.is_platform_error(client_sock_err) or_else -1)
|
||||
return
|
||||
}
|
||||
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 {
|
||||
return
|
||||
}
|
||||
res, ok := os.recv(os.Socket(skt), buf, 0)
|
||||
if ok != os.ERROR_NONE {
|
||||
err = TCP_Recv_Error(ok)
|
||||
res, res_err := os.recv(os.Socket(skt), buf, 0)
|
||||
if res_err != nil {
|
||||
err = TCP_Recv_Error(os.is_platform_error(res_err) or_else -1)
|
||||
return
|
||||
}
|
||||
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
|
||||
fromsize := c.int(size_of(from))
|
||||
res, ok := os.recvfrom(os.Socket(skt), buf, 0, cast(^os.SOCKADDR) &from, &fromsize)
|
||||
if ok != os.ERROR_NONE {
|
||||
err = UDP_Recv_Error(ok)
|
||||
res, res_err := os.recvfrom(os.Socket(skt), buf, 0, cast(^os.SOCKADDR) &from, &fromsize)
|
||||
if res_err != nil {
|
||||
err = UDP_Recv_Error(os.is_platform_error(res_err) or_else -1)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -194,9 +194,9 @@ _send_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_written: int, err: Net
|
||||
for bytes_written < len(buf) {
|
||||
limit := min(int(max(i32)), len(buf) - bytes_written)
|
||||
remaining := buf[bytes_written:][:limit]
|
||||
res, ok := os.send(os.Socket(skt), remaining, 0)
|
||||
if ok != os.ERROR_NONE {
|
||||
err = TCP_Send_Error(ok)
|
||||
res, res_err := os.send(os.Socket(skt), remaining, 0)
|
||||
if res_err != nil {
|
||||
err = TCP_Send_Error(os.is_platform_error(res_err) or_else -1)
|
||||
return
|
||||
}
|
||||
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) {
|
||||
limit := min(1<<31, len(buf) - bytes_written)
|
||||
remaining := buf[bytes_written:][:limit]
|
||||
res, ok := os.sendto(os.Socket(skt), remaining, 0, cast(^os.SOCKADDR)&toaddr, i32(toaddr.len))
|
||||
if ok != os.ERROR_NONE {
|
||||
err = UDP_Send_Error(ok)
|
||||
res, res_err := os.sendto(os.Socket(skt), remaining, 0, cast(^os.SOCKADDR)&toaddr, i32(toaddr.len))
|
||||
if res_err != nil {
|
||||
err = UDP_Send_Error(os.is_platform_error(res_err) or_else -1)
|
||||
return
|
||||
}
|
||||
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) {
|
||||
s := any_socket_to_socket(skt)
|
||||
res := os.shutdown(os.Socket(s), int(manner))
|
||||
if res != os.ERROR_NONE {
|
||||
return Shutdown_Error(res)
|
||||
if res != nil {
|
||||
return Shutdown_Error(os.is_platform_error(res) or_else -1)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -302,8 +302,8 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca
|
||||
|
||||
skt := any_socket_to_socket(s)
|
||||
res := os.setsockopt(os.Socket(skt), int(level), int(option), ptr, len)
|
||||
if res != os.ERROR_NONE {
|
||||
return Socket_Option_Error(res)
|
||||
if res != nil {
|
||||
return Socket_Option_Error(os.is_platform_error(res) or_else -1)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -314,8 +314,8 @@ _set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_E
|
||||
socket := any_socket_to_socket(socket)
|
||||
|
||||
flags, getfl_err := os.fcntl(int(socket), os.F_GETFL, 0)
|
||||
if getfl_err != os.ERROR_NONE {
|
||||
return Set_Blocking_Error(getfl_err)
|
||||
if getfl_err != nil {
|
||||
return Set_Blocking_Error(os.is_platform_error(getfl_err) or_else -1)
|
||||
}
|
||||
|
||||
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)
|
||||
if setfl_err != os.ERROR_NONE {
|
||||
return Set_Blocking_Error(setfl_err)
|
||||
if setfl_err != nil {
|
||||
return Set_Blocking_Error(os.is_platform_error(setfl_err) or_else -1)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -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) {
|
||||
family := _unwrap_os_family(family)
|
||||
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 {
|
||||
return {}, Create_Socket_Error(errno)
|
||||
}
|
||||
@@ -132,7 +132,7 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
|
||||
}
|
||||
// Create new TCP socket
|
||||
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 {
|
||||
// TODO(flysand): should return invalid file descriptor here casted as TCP_Socket
|
||||
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)
|
||||
// Create TCP socket
|
||||
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 {
|
||||
// TODO(flysand): should return invalid file descriptor here casted as TCP_Socket
|
||||
return {}, Create_Socket_Error(errno)
|
||||
|
||||
@@ -599,6 +599,7 @@ Field_Flag :: enum {
|
||||
Subtype,
|
||||
By_Ptr,
|
||||
No_Broadcast,
|
||||
No_Capture,
|
||||
|
||||
Results,
|
||||
Tags,
|
||||
@@ -619,6 +620,7 @@ field_flag_strings := [Field_Flag]string{
|
||||
.Subtype = "#subtype",
|
||||
.By_Ptr = "#by_ptr",
|
||||
.No_Broadcast = "#no_broadcast",
|
||||
.No_Capture = "#no_capture",
|
||||
|
||||
.Results = "results",
|
||||
.Tags = "field tag",
|
||||
@@ -634,6 +636,7 @@ field_hash_flag_strings := []struct{key: string, flag: Field_Flag}{
|
||||
{"subtype", .Subtype},
|
||||
{"by_ptr", .By_Ptr},
|
||||
{"no_broadcast", .No_Broadcast},
|
||||
{"no_capture", .No_Capture},
|
||||
}
|
||||
|
||||
|
||||
@@ -754,7 +757,7 @@ Array_Type :: struct {
|
||||
using node: Expr,
|
||||
open: tokenizer.Pos,
|
||||
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,
|
||||
elem: ^Expr,
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
e := ast.new(ast.Ellipsis, type.pos, type)
|
||||
e.tok = tok.kind
|
||||
e.expr = type
|
||||
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 {
|
||||
case ^ast.Proc_Lit:
|
||||
if e.inlining != .None && e.inlining != pi {
|
||||
error(p, expr.pos, "both 'inline' and 'no_inline' cannot be applied to a procedure literal")
|
||||
if expr != nil {
|
||||
#partial switch e in ast.strip_or_return_expr(expr).derived_expr {
|
||||
case ^ast.Proc_Lit:
|
||||
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 {
|
||||
@@ -2258,18 +2262,18 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
hp.type = type
|
||||
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.tok = tok
|
||||
bd.name = name.text
|
||||
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.tok = tok
|
||||
bd.name = name.text
|
||||
return parse_call_expr(p, bd)
|
||||
|
||||
|
||||
case "soa":
|
||||
bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
|
||||
bd.tok = tok
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
@@ -4,7 +4,9 @@ import win32 "core:sys/windows"
|
||||
import "core:strings"
|
||||
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) {
|
||||
// Ignore "." and ".."
|
||||
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)
|
||||
if !dir_fi.is_dir {
|
||||
return nil, ERROR_FILE_IS_NOT_DIR
|
||||
return nil, .Not_Dir
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
wpath: []u16
|
||||
wpath, err = cleanpath_from_handle_u16(fd, context.temp_allocator)
|
||||
if len(wpath) == 0 || err != ERROR_NONE {
|
||||
wpath := cleanpath_from_handle_u16(fd, context.temp_allocator) or_return
|
||||
if len(wpath) == 0 {
|
||||
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)
|
||||
wpath_search[len(wpath)+0] = '\\'
|
||||
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_handle := win32.FindFirstFileW(raw_data(wpath_search), find_data)
|
||||
if find_handle == win32.INVALID_HANDLE_VALUE {
|
||||
err = Errno(win32.GetLastError())
|
||||
err = get_last_error()
|
||||
return dfi[:], err
|
||||
}
|
||||
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) {
|
||||
e := Errno(win32.GetLastError())
|
||||
e := get_last_error()
|
||||
if e == ERROR_NO_MORE_FILES {
|
||||
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
@@ -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
|
||||
// Otherwise the returned value will be empty and the boolean will be false
|
||||
// NOTE: the value will be allocated with the supplied allocator
|
||||
@(require_results)
|
||||
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
if key == "" {
|
||||
return
|
||||
}
|
||||
wkey := win32.utf8_to_wstring(key)
|
||||
n := win32.GetEnvironmentVariableW(wkey, nil, 0)
|
||||
if n == 0 {
|
||||
err := win32.GetLastError()
|
||||
if err == u32(ERROR_ENVVAR_NOT_FOUND) {
|
||||
return "", false
|
||||
}
|
||||
if n == 0 && get_last_error() == ERROR_ENVVAR_NOT_FOUND {
|
||||
return "", false
|
||||
}
|
||||
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)))
|
||||
if n == 0 {
|
||||
err := win32.GetLastError()
|
||||
if err == u32(ERROR_ENVVAR_NOT_FOUND) {
|
||||
return "", false
|
||||
}
|
||||
if n == 0 && get_last_error() == ERROR_ENVVAR_NOT_FOUND {
|
||||
return "", false
|
||||
}
|
||||
value, _ = win32.utf16_to_utf8(b[:n], allocator)
|
||||
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
|
||||
// To distinguish between an empty value and an unset value, use lookup_env
|
||||
// NOTE: the value will be allocated with the supplied allocator
|
||||
@(require_results)
|
||||
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
value, _ = lookup_env(key, allocator)
|
||||
return
|
||||
}
|
||||
|
||||
// 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)
|
||||
v := win32.utf8_to_wstring(value)
|
||||
|
||||
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 :: proc(key: string) -> Errno {
|
||||
unset_env :: proc(key: string) -> Error {
|
||||
k := win32.utf8_to_wstring(key)
|
||||
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"
|
||||
// NOTE: the slice of strings and the strings with be allocated using the supplied allocator
|
||||
@(require_results)
|
||||
environ :: proc(allocator := context.allocator) -> []string {
|
||||
envs := cast([^]win32.WCHAR)(win32.GetEnvironmentStringsW())
|
||||
envs := ([^]win32.WCHAR)(win32.GetEnvironmentStringsW())
|
||||
if envs == nil {
|
||||
return nil
|
||||
}
|
||||
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 {
|
||||
if c := envs[i]; c == 0 {
|
||||
if i <= from {
|
||||
|
||||
@@ -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
@@ -5,13 +5,15 @@ import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
import "core:unicode/utf16"
|
||||
|
||||
@(require_results)
|
||||
is_path_separator :: proc(c: byte) -> bool {
|
||||
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 {
|
||||
return INVALID_HANDLE, ERROR_FILE_NOT_FOUND
|
||||
return INVALID_HANDLE, General_Error.Not_Exist
|
||||
}
|
||||
|
||||
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)
|
||||
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 {
|
||||
return handle, ERROR_NONE
|
||||
return handle, nil
|
||||
}
|
||||
|
||||
err := Errno(win32.GetLastError())
|
||||
return INVALID_HANDLE, err
|
||||
return INVALID_HANDLE, get_last_error()
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) -> Errno {
|
||||
close :: proc(fd: Handle) -> Error {
|
||||
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)) {
|
||||
err = Errno(win32.GetLastError())
|
||||
err = get_last_error()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
write :: proc(fd: Handle, data: []byte) -> (int, Error) {
|
||||
if len(data) == 0 {
|
||||
return 0, ERROR_NONE
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
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)
|
||||
if single_write_length <= 0 || !e {
|
||||
err := Errno(win32.GetLastError())
|
||||
return int(total_write), err
|
||||
return int(total_write), get_last_error()
|
||||
}
|
||||
total_write += i64(single_write_length)
|
||||
}
|
||||
return int(total_write), ERROR_NONE
|
||||
return int(total_write), nil
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) {
|
||||
@(private="file", require_results)
|
||||
read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Error) {
|
||||
if len(b) == 0 {
|
||||
return 0, 0
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
BUF_SIZE :: 386
|
||||
buf16: [BUF_SIZE]u16
|
||||
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)
|
||||
max_read := u32(min(BUF_SIZE, min_read))
|
||||
if max_read == 0 {
|
||||
@@ -118,14 +118,14 @@ read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) {
|
||||
single_read_length: u32
|
||||
ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil)
|
||||
if !ok {
|
||||
err = Errno(win32.GetLastError())
|
||||
err = get_last_error()
|
||||
}
|
||||
|
||||
buf8_len := utf16.decode_to_utf8(buf8[:], buf16[:single_read_length])
|
||||
src := buf8[:buf8_len]
|
||||
|
||||
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]
|
||||
if x == 0x1a { // ctrl-z
|
||||
ctrl_z = true
|
||||
@@ -149,9 +149,9 @@ read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) {
|
||||
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 {
|
||||
return 0, ERROR_NONE
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
handle := win32.HANDLE(fd)
|
||||
@@ -165,7 +165,7 @@ read :: proc(fd: Handle, data: []byte) -> (total_read: int, err: Errno) {
|
||||
|
||||
if is_console {
|
||||
total_read, err = read_console(handle, data[total_read:][:to_read])
|
||||
if err != 0 {
|
||||
if err != nil {
|
||||
return total_read, err
|
||||
}
|
||||
} 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:
|
||||
// https://learn.microsoft.com/en-us/windows/win32/fileio/testing-for-the-end-of-a-file
|
||||
if bytes_read == 0 {
|
||||
return 0, ERROR_HANDLE_EOF
|
||||
return 0, .EOF
|
||||
} else {
|
||||
return int(bytes_read), ERROR_NONE
|
||||
return int(bytes_read), nil
|
||||
}
|
||||
} 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
|
||||
switch whence {
|
||||
case 0: w = win32.FILE_BEGIN
|
||||
@@ -197,22 +197,23 @@ seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
||||
lo := i32(offset)
|
||||
ft := win32.GetFileType(win32.HANDLE(fd))
|
||||
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)
|
||||
if dw_ptr == win32.INVALID_SET_FILE_POINTER {
|
||||
err := Errno(win32.GetLastError())
|
||||
err := get_last_error()
|
||||
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
|
||||
err: Errno
|
||||
err: Error
|
||||
if !win32.GetFileSizeEx(win32.HANDLE(fd), &length) {
|
||||
err = Errno(win32.GetLastError())
|
||||
err = get_last_error()
|
||||
}
|
||||
return i64(length), err
|
||||
}
|
||||
@@ -220,10 +221,9 @@ file_size :: proc(fd: Handle) -> (i64, Errno) {
|
||||
|
||||
@(private)
|
||||
MAX_RW :: 1<<30
|
||||
ERROR_EOF :: 38
|
||||
|
||||
@(private)
|
||||
pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
|
||||
pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) {
|
||||
buf := data
|
||||
if len(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)
|
||||
done: win32.DWORD
|
||||
e: Errno
|
||||
e: Error
|
||||
if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
|
||||
e = Errno(win32.GetLastError())
|
||||
e = get_last_error()
|
||||
done = 0
|
||||
}
|
||||
return int(done), e
|
||||
}
|
||||
@(private)
|
||||
pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
|
||||
pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) {
|
||||
buf := data
|
||||
if len(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)
|
||||
done: win32.DWORD
|
||||
e: Errno
|
||||
e: Error
|
||||
if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
|
||||
e = Errno(win32.GetLastError())
|
||||
e = get_last_error()
|
||||
done = 0
|
||||
}
|
||||
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
|
||||
*/
|
||||
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 {
|
||||
return 0, ERROR_NEGATIVE_OFFSET
|
||||
return 0, .Invalid_Offset
|
||||
}
|
||||
|
||||
b, offset := data, offset
|
||||
for len(b) > 0 {
|
||||
m, e := pread(fd, b, offset)
|
||||
if e == ERROR_EOF {
|
||||
err = 0
|
||||
err = nil
|
||||
break
|
||||
}
|
||||
if e != 0 {
|
||||
if e != nil {
|
||||
err = e
|
||||
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
|
||||
*/
|
||||
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 {
|
||||
return 0, ERROR_NEGATIVE_OFFSET
|
||||
return 0, .Invalid_Offset
|
||||
}
|
||||
|
||||
b, offset := data, offset
|
||||
for len(b) > 0 {
|
||||
m, e := pwrite(fd, b, offset)
|
||||
if e != 0 {
|
||||
err = e
|
||||
break
|
||||
}
|
||||
m := pwrite(fd, b, offset) or_return
|
||||
n += m
|
||||
b = b[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))
|
||||
|
||||
|
||||
@(require_results)
|
||||
get_std_handle :: proc "contextless" (h: uint) -> Handle {
|
||||
fd := win32.GetStdHandle(win32.DWORD(h))
|
||||
return Handle(fd)
|
||||
@@ -352,6 +349,7 @@ exists :: proc(path: string) -> bool {
|
||||
return attribs != win32.INVALID_FILE_ATTRIBUTES
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
is_file :: proc(path: string) -> bool {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
@@ -363,6 +361,7 @@ is_file :: proc(path: string) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
is_dir :: proc(path: string) -> bool {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
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
|
||||
@private cwd_lock := win32.SRWLOCK{} // zero is initialized
|
||||
|
||||
@(require_results)
|
||||
get_current_directory :: proc(allocator := context.allocator) -> string {
|
||||
win32.AcquireSRWLockExclusive(&cwd_lock)
|
||||
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
|
||||
|
||||
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))
|
||||
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 ""
|
||||
}
|
||||
|
||||
set_current_directory :: proc(path: string) -> (err: Errno) {
|
||||
set_current_directory :: proc(path: string) -> (err: Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
wstr := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
|
||||
win32.AcquireSRWLockExclusive(&cwd_lock)
|
||||
|
||||
if !win32.SetCurrentDirectoryW(wstr) {
|
||||
err = Errno(win32.GetLastError())
|
||||
err = get_last_error()
|
||||
}
|
||||
|
||||
win32.ReleaseSRWLockExclusive(&cwd_lock)
|
||||
@@ -409,31 +409,31 @@ set_current_directory :: proc(path: string) -> (err: Errno) {
|
||||
}
|
||||
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()
|
||||
// Mode is unused on Windows, but is needed on *nix
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
|
||||
if !win32.CreateDirectoryW(wpath, nil) {
|
||||
err = Errno(win32.GetLastError())
|
||||
err = get_last_error()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
remove_directory :: proc(path: string) -> (err: Errno) {
|
||||
remove_directory :: proc(path: string) -> (err: Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
|
||||
if !win32.RemoveDirectoryW(wpath) {
|
||||
err = Errno(win32.GetLastError())
|
||||
err = get_last_error()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
@(private)
|
||||
@(private, require_results)
|
||||
is_abs :: proc(path: string) -> bool {
|
||||
if len(path) > 0 && path[0] == '/' {
|
||||
return true
|
||||
@@ -449,7 +449,7 @@ is_abs :: proc(path: string) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@(private)
|
||||
@(private, require_results)
|
||||
fix_long_path :: proc(path: string) -> string {
|
||||
if len(path) < 248 {
|
||||
return path
|
||||
@@ -464,7 +464,7 @@ fix_long_path :: proc(path: string) -> string {
|
||||
|
||||
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)
|
||||
n := len(path)
|
||||
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()
|
||||
n := win32.utf8_to_wstring(fix_long_path(new_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()
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
|
||||
if !win32.DeleteFileW(wpath) {
|
||||
err = Errno(win32.GetLastError())
|
||||
err = get_last_error()
|
||||
}
|
||||
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()
|
||||
from := win32.utf8_to_wstring(old_path, context.temp_allocator)
|
||||
to := win32.utf8_to_wstring(new_path, context.temp_allocator)
|
||||
|
||||
if !win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) {
|
||||
err = Errno(win32.GetLastError())
|
||||
err = get_last_error()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
ftruncate :: proc(fd: Handle, length: i64) -> (err: Errno) {
|
||||
curr_off, e := seek(fd, 0, 1)
|
||||
if e != 0 {
|
||||
return e
|
||||
}
|
||||
ftruncate :: proc(fd: Handle, length: i64) -> (err: Error) {
|
||||
curr_off := seek(fd, 0, 1) or_return
|
||||
defer seek(fd, curr_off, 0)
|
||||
_, e = seek(fd, length, 0)
|
||||
if e != 0 {
|
||||
return e
|
||||
}
|
||||
_= seek(fd, length, 0) or_return
|
||||
ok := win32.SetEndOfFile(win32.HANDLE(fd))
|
||||
if !ok {
|
||||
return Errno(win32.GetLastError())
|
||||
return get_last_error()
|
||||
}
|
||||
return ERROR_NONE
|
||||
return nil
|
||||
}
|
||||
|
||||
truncate :: proc(path: string, length: i64) -> (err: Errno) {
|
||||
fd: Handle
|
||||
fd, err = open(path, O_WRONLY|O_CREATE, 0o666)
|
||||
if err != 0 {
|
||||
return
|
||||
}
|
||||
truncate :: proc(path: string, length: i64) -> (err: Error) {
|
||||
fd := open(path, O_WRONLY|O_CREATE, 0o666) or_return
|
||||
defer close(fd)
|
||||
err = ftruncate(fd, length)
|
||||
return
|
||||
return ftruncate(fd, length)
|
||||
}
|
||||
|
||||
|
||||
remove :: proc(name: string) -> Errno {
|
||||
remove :: proc(name: string) -> Error {
|
||||
p := win32.utf8_to_wstring(fix_long_path(name))
|
||||
err, err1: win32.DWORD
|
||||
if !win32.DeleteFileW(p) {
|
||||
err = win32.GetLastError()
|
||||
}
|
||||
if err == 0 {
|
||||
return 0
|
||||
return nil
|
||||
}
|
||||
if !win32.RemoveDirectoryW(p) {
|
||||
err1 = win32.GetLastError()
|
||||
}
|
||||
if err1 == 0 {
|
||||
return 0
|
||||
return nil
|
||||
}
|
||||
|
||||
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.nLength = size_of(win32.SECURITY_ATTRIBUTES)
|
||||
sa.bInheritHandle = true
|
||||
if !win32.CreatePipe((^win32.HANDLE)(&r), (^win32.HANDLE)(&w), &sa, 0) {
|
||||
err = Errno(win32.GetLastError())
|
||||
err = get_last_error()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
+98
-66
@@ -1,6 +1,8 @@
|
||||
package os
|
||||
|
||||
import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
import "core:io"
|
||||
import "core:strconv"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
@@ -13,15 +15,15 @@ SEEK_SET :: 0
|
||||
SEEK_CUR :: 1
|
||||
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)
|
||||
}
|
||||
|
||||
write_byte :: proc(fd: Handle, b: byte) -> (int, Errno) {
|
||||
write_byte :: proc(fd: Handle, b: byte) -> (int, Error) {
|
||||
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 {
|
||||
return write_byte(fd, byte(r))
|
||||
}
|
||||
@@ -30,105 +32,94 @@ write_rune :: proc(fd: Handle, r: rune) -> (int, Errno) {
|
||||
return write(fd, b[:n])
|
||||
}
|
||||
|
||||
write_encoded_rune :: proc(fd: Handle, r: rune) {
|
||||
write_byte(fd, '\'')
|
||||
write_encoded_rune :: proc(f: Handle, r: rune) -> (n: int, err: Error) {
|
||||
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 {
|
||||
case '\a': write_string(fd, "\\a")
|
||||
case '\b': write_string(fd, "\\b")
|
||||
case '\e': write_string(fd, "\\e")
|
||||
case '\f': write_string(fd, "\\f")
|
||||
case '\n': write_string(fd, "\\n")
|
||||
case '\r': write_string(fd, "\\r")
|
||||
case '\t': write_string(fd, "\\t")
|
||||
case '\v': write_string(fd, "\\v")
|
||||
case '\a': if wrap(write_string(f, "\\a"), &n, &err) { return }
|
||||
case '\b': if wrap(write_string(f, "\\b"), &n, &err) { return }
|
||||
case '\e': if wrap(write_string(f, "\\e"), &n, &err) { return }
|
||||
case '\f': if wrap(write_string(f, "\\f"), &n, &err) { return }
|
||||
case '\n': if wrap(write_string(f, "\\n"), &n, &err) { return }
|
||||
case '\r': if wrap(write_string(f, "\\r"), &n, &err) { return }
|
||||
case '\t': if wrap(write_string(f, "\\t"), &n, &err) { return }
|
||||
case '\v': if wrap(write_string(f, "\\v"), &n, &err) { return }
|
||||
case:
|
||||
if r < 32 {
|
||||
write_string(fd, "\\x")
|
||||
if wrap(write_string(f, "\\x"), &n, &err) { return }
|
||||
b: [2]byte
|
||||
s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil)
|
||||
switch len(s) {
|
||||
case 0: write_string(fd, "00")
|
||||
case 1: write_rune(fd, '0')
|
||||
case 2: write_string(fd, s)
|
||||
case 0: if wrap(write_string(f, "00"), &n, &err) { return }
|
||||
case 1: if wrap(write_rune(f, '0'), &n, &err) { return }
|
||||
case 2: if wrap(write_string(f, s), &n, &err) { return }
|
||||
}
|
||||
} 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 {
|
||||
return 0, -1
|
||||
return 0, io.Error.Short_Buffer
|
||||
}
|
||||
nn := max(int)
|
||||
for nn > 0 && n < min && err == 0 {
|
||||
for nn > 0 && n < min && err == nil {
|
||||
nn, err = read(fd, buf[n:])
|
||||
n += nn
|
||||
}
|
||||
if n >= min {
|
||||
err = 0
|
||||
err = nil
|
||||
}
|
||||
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))
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
file_size_from_path :: proc(path: string) -> i64 {
|
||||
fd, err := open(path, O_RDONLY, 0)
|
||||
if err != 0 {
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
defer close(fd)
|
||||
|
||||
length: i64
|
||||
if length, err = file_size(fd); err != 0 {
|
||||
if length, err = file_size(fd); err != nil {
|
||||
return -1
|
||||
}
|
||||
return length
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
read_entire_file_from_filename :: proc(name: string, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) {
|
||||
context.allocator = allocator
|
||||
|
||||
fd, err := open(name, O_RDONLY, 0)
|
||||
if err != 0 {
|
||||
return nil, false
|
||||
}
|
||||
defer close(fd)
|
||||
|
||||
return read_entire_file_from_handle(fd, allocator, loc)
|
||||
err: Error
|
||||
data, err = read_entire_file_from_filename_or_err(name, allocator, loc)
|
||||
success = err == nil
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) {
|
||||
context.allocator = allocator
|
||||
|
||||
length: i64
|
||||
err: Errno
|
||||
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
|
||||
err: Error
|
||||
data, err = read_entire_file_from_handle_or_err(fd, allocator, loc)
|
||||
success = err == nil
|
||||
return
|
||||
}
|
||||
|
||||
read_entire_file :: proc {
|
||||
@@ -136,7 +127,50 @@ read_entire_file :: proc {
|
||||
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) {
|
||||
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
|
||||
if truncate {
|
||||
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
|
||||
}
|
||||
|
||||
fd, err := open(name, flags, mode)
|
||||
if err != 0 {
|
||||
return false
|
||||
}
|
||||
fd := open(name, flags, mode) or_return
|
||||
defer close(fd)
|
||||
|
||||
_, write_err := write(fd, data)
|
||||
return write_err == 0
|
||||
_ = write(fd, data) or_return
|
||||
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])
|
||||
}
|
||||
|
||||
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])
|
||||
}
|
||||
|
||||
@@ -173,6 +204,7 @@ heap_alloc :: runtime.heap_alloc
|
||||
heap_resize :: runtime.heap_resize
|
||||
heap_free :: runtime.heap_free
|
||||
|
||||
@(require_results)
|
||||
processor_core_count :: proc() -> int {
|
||||
return _processor_core_count()
|
||||
}
|
||||
|
||||
+27
-11
@@ -10,20 +10,29 @@ file_allocator :: proc() -> runtime.Allocator {
|
||||
|
||||
temp_allocator_proc :: runtime.arena_allocator_proc
|
||||
|
||||
@(private="file")
|
||||
MAX_TEMP_ARENA_COUNT :: 2
|
||||
|
||||
@(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)
|
||||
temp_allocator :: proc() -> runtime.Allocator {
|
||||
return runtime.Allocator{
|
||||
procedure = temp_allocator_proc,
|
||||
data = &global_default_temp_allocator_arena,
|
||||
data = &global_default_temp_allocator_arenas[global_default_temp_allocator_index],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@(require_results)
|
||||
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
|
||||
}
|
||||
|
||||
@@ -33,16 +42,23 @@ temp_allocator_temp_end :: proc(temp: runtime.Arena_Temp, loc := #caller_locatio
|
||||
|
||||
@(fini, private)
|
||||
temp_allocator_fini :: proc() {
|
||||
runtime.arena_destroy(&global_default_temp_allocator_arena)
|
||||
global_default_temp_allocator_arena = {}
|
||||
for &arena in global_default_temp_allocator_arenas {
|
||||
runtime.arena_destroy(&arena)
|
||||
}
|
||||
global_default_temp_allocator_arenas = {}
|
||||
}
|
||||
|
||||
@(deferred_out=temp_allocator_temp_end)
|
||||
TEMP_ALLOCATOR_GUARD :: #force_inline proc(ignore := false, loc := #caller_location) -> (runtime.Arena_Temp, runtime.Source_Code_Location) {
|
||||
if ignore {
|
||||
return {}, loc
|
||||
} else {
|
||||
return temp_allocator_temp_begin(loc), loc
|
||||
TEMP_ALLOCATOR_GUARD_END :: proc(temp: runtime.Arena_Temp, loc := #caller_location) {
|
||||
runtime.arena_temp_end(temp, loc)
|
||||
if temp.arena != nil {
|
||||
global_default_temp_allocator_index = (global_default_temp_allocator_index-1)%MAX_TEMP_ARENA_COUNT
|
||||
}
|
||||
}
|
||||
|
||||
@(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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user