mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 15:48:51 +00: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
|
name: NetBSD Build, Check, and Test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
PKGSRC_BRANCH: 2024Q1
|
PKGSRC_BRANCH: 2024Q2
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Build, Check, and Test
|
- name: Build, Check, and Test
|
||||||
@@ -18,13 +18,11 @@ jobs:
|
|||||||
usesh: true
|
usesh: true
|
||||||
copyback: false
|
copyback: false
|
||||||
prepare: |
|
prepare: |
|
||||||
PKG_PATH="https://cdn.NetBSD.org/pub/pkgsrc/packages/NetBSD/$(uname -p)/$(uname -r | cut -d_ -f1)_${PKGSRC_BRANCH}/All" /usr/sbin/pkg_add pkgin
|
PKG_PATH="https://cdn.NetBSD.org/pub/pkgsrc/packages/NetBSD/$(uname -p)/10.0_2024Q2/All" /usr/sbin/pkg_add pkgin
|
||||||
pkgin -y in gmake git bash python311
|
pkgin -y in gmake git bash python311 llvm clang
|
||||||
pkgin -y in libxml2 perl zstd
|
|
||||||
/usr/sbin/pkg_add https://github.com/andreas-jonsson/llvm17-netbsd-bin/releases/download/pkgsrc-current/llvm-17.0.6.tgz
|
|
||||||
/usr/sbin/pkg_add https://github.com/andreas-jonsson/llvm17-netbsd-bin/releases/download/pkgsrc-current/clang-17.0.6.tgz
|
|
||||||
ln -s /usr/pkg/bin/python3.11 /usr/bin/python3
|
ln -s /usr/pkg/bin/python3.11 /usr/bin/python3
|
||||||
run: |
|
run: |
|
||||||
|
set -e -x
|
||||||
git config --global --add safe.directory $(pwd)
|
git config --global --add safe.directory $(pwd)
|
||||||
gmake release
|
gmake release
|
||||||
./odin version
|
./odin version
|
||||||
@@ -91,13 +89,13 @@ jobs:
|
|||||||
- name: Download LLVM (MacOS Intel)
|
- name: Download LLVM (MacOS Intel)
|
||||||
if: matrix.os == 'macos-13'
|
if: matrix.os == 'macos-13'
|
||||||
run: |
|
run: |
|
||||||
brew install llvm@17
|
brew install llvm@17 lua@5.4
|
||||||
echo "/usr/local/opt/llvm@17/bin" >> $GITHUB_PATH
|
echo "/usr/local/opt/llvm@17/bin" >> $GITHUB_PATH
|
||||||
|
|
||||||
- name: Download LLVM (MacOS ARM)
|
- name: Download LLVM (MacOS ARM)
|
||||||
if: matrix.os == 'macos-14'
|
if: matrix.os == 'macos-14'
|
||||||
run: |
|
run: |
|
||||||
brew install llvm@17 wasmtime
|
brew install llvm@17 wasmtime lua@5.4
|
||||||
echo "/opt/homebrew/opt/llvm@17/bin" >> $GITHUB_PATH
|
echo "/opt/homebrew/opt/llvm@17/bin" >> $GITHUB_PATH
|
||||||
|
|
||||||
- name: Build Odin
|
- name: Build Odin
|
||||||
@@ -207,6 +205,7 @@ jobs:
|
|||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||||
|
copy vendor\lua\5.4\windows\*.dll .
|
||||||
odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false
|
odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false
|
||||||
- name: Odin internals tests
|
- name: Odin internals tests
|
||||||
shell: cmd
|
shell: cmd
|
||||||
|
|||||||
@@ -50,8 +50,8 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
wget https://apt.llvm.org/llvm.sh
|
wget https://apt.llvm.org/llvm.sh
|
||||||
chmod +x llvm.sh
|
chmod +x llvm.sh
|
||||||
sudo ./llvm.sh 17
|
sudo ./llvm.sh 18
|
||||||
echo "/usr/lib/llvm-17/bin" >> $GITHUB_PATH
|
echo "/usr/lib/llvm-18/bin" >> $GITHUB_PATH
|
||||||
- name: build odin
|
- name: build odin
|
||||||
run: make nightly
|
run: make nightly
|
||||||
- name: Odin run
|
- name: Odin run
|
||||||
@@ -82,8 +82,8 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Download LLVM and setup PATH
|
- name: Download LLVM and setup PATH
|
||||||
run: |
|
run: |
|
||||||
brew install llvm@17 dylibbundler
|
brew install llvm@18 dylibbundler
|
||||||
echo "/usr/local/opt/llvm@17/bin" >> $GITHUB_PATH
|
echo "/usr/local/opt/llvm@18/bin" >> $GITHUB_PATH
|
||||||
- name: build odin
|
- name: build odin
|
||||||
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
|
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
|
||||||
# not link with libunwind bundled with LLVM but link with libunwind on the system.
|
# not link with libunwind bundled with LLVM but link with libunwind on the system.
|
||||||
@@ -116,8 +116,8 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Download LLVM and setup PATH
|
- name: Download LLVM and setup PATH
|
||||||
run: |
|
run: |
|
||||||
brew install llvm@17 dylibbundler
|
brew install llvm@18 dylibbundler
|
||||||
echo "/opt/homebrew/opt/llvm@17/bin" >> $GITHUB_PATH
|
echo "/opt/homebrew/opt/llvm@18/bin" >> $GITHUB_PATH
|
||||||
- name: build odin
|
- name: build odin
|
||||||
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
|
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
|
||||||
# not link with libunwind bundled with LLVM but link with libunwind on the system.
|
# not link with libunwind bundled with LLVM but link with libunwind on the system.
|
||||||
|
|||||||
+1
-32
@@ -24,38 +24,6 @@ bld/
|
|||||||
![Cc]ore/[Ll]og/
|
![Cc]ore/[Ll]og/
|
||||||
tests/documentation/verify/
|
tests/documentation/verify/
|
||||||
tests/documentation/all.odin-doc
|
tests/documentation/all.odin-doc
|
||||||
tests/internal/test_map
|
|
||||||
tests/internal/test_pow
|
|
||||||
tests/internal/test_rtti
|
|
||||||
tests/core/test_base64
|
|
||||||
tests/core/test_cbor
|
|
||||||
tests/core/test_core_compress
|
|
||||||
tests/core/test_core_container
|
|
||||||
tests/core/test_core_filepath
|
|
||||||
tests/core/test_core_fmt
|
|
||||||
tests/core/test_core_i18n
|
|
||||||
tests/core/test_core_image
|
|
||||||
tests/core/test_core_libc
|
|
||||||
tests/core/test_core_match
|
|
||||||
tests/core/test_core_math
|
|
||||||
tests/core/test_core_net
|
|
||||||
tests/core/test_core_os_exit
|
|
||||||
tests/core/test_core_reflect
|
|
||||||
tests/core/test_core_strings
|
|
||||||
tests/core/test_core_time
|
|
||||||
tests/core/test_crypto
|
|
||||||
tests/core/test_hash
|
|
||||||
tests/core/test_hex
|
|
||||||
tests/core/test_hxa
|
|
||||||
tests/core/test_json
|
|
||||||
tests/core/test_linalg_glsl_math
|
|
||||||
tests/core/test_noise
|
|
||||||
tests/core/test_varint
|
|
||||||
tests/core/test_xml
|
|
||||||
tests/core/test_core_slice
|
|
||||||
tests/core/test_core_thread
|
|
||||||
tests/core/test_core_runtime
|
|
||||||
tests/vendor/vendor_botan
|
|
||||||
# Visual Studio 2015 cache/options directory
|
# Visual Studio 2015 cache/options directory
|
||||||
.vs/
|
.vs/
|
||||||
# Visual Studio Code options directory
|
# Visual Studio Code options directory
|
||||||
@@ -63,6 +31,7 @@ tests/vendor/vendor_botan
|
|||||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
#wwwroot/
|
#wwwroot/
|
||||||
demo
|
demo
|
||||||
|
benchmark
|
||||||
|
|
||||||
# MSTest test Results
|
# MSTest test Results
|
||||||
[Tt]est[Rr]esult*/
|
[Tt]est[Rr]esult*/
|
||||||
|
|||||||
BIN
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) ---
|
reverse_bits :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
|
||||||
byte_swap :: proc(x: $T) -> T where type_is_integer(T) || type_is_float(T) ---
|
byte_swap :: proc(x: $T) -> T where type_is_integer(T) || type_is_float(T) ---
|
||||||
|
|
||||||
overflow_add :: proc(lhs, rhs: $T) -> (T, bool) ---
|
overflow_add :: proc(lhs, rhs: $T) -> (T, bool) where type_is_integer(T) #optional_ok ---
|
||||||
overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) ---
|
overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) where type_is_integer(T) #optional_ok ---
|
||||||
overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) ---
|
overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) where type_is_integer(T) #optional_ok ---
|
||||||
|
|
||||||
|
add_sat :: proc(lhs, rhs: $T) -> T where type_is_integer(T) ---
|
||||||
|
sub_sat :: proc(lhs, rhs: $T) -> T where type_is_integer(T) ---
|
||||||
|
|
||||||
sqrt :: proc(x: $T) -> T where type_is_float(T) || (type_is_simd_vector(T) && type_is_float(type_elem_type(T))) ---
|
sqrt :: proc(x: $T) -> T where type_is_float(T) || (type_is_simd_vector(T) && type_is_float(type_elem_type(T))) ---
|
||||||
|
|
||||||
|
|||||||
+36
-23
@@ -66,7 +66,7 @@ Type_Info_Named :: struct {
|
|||||||
name: string,
|
name: string,
|
||||||
base: ^Type_Info,
|
base: ^Type_Info,
|
||||||
pkg: string,
|
pkg: string,
|
||||||
loc: Source_Code_Location,
|
loc: ^Source_Code_Location,
|
||||||
}
|
}
|
||||||
Type_Info_Integer :: struct {signed: bool, endianness: Platform_Endianness}
|
Type_Info_Integer :: struct {signed: bool, endianness: Platform_Endianness}
|
||||||
Type_Info_Rune :: struct {}
|
Type_Info_Rune :: struct {}
|
||||||
@@ -112,23 +112,32 @@ Type_Info_Parameters :: struct { // Only used for procedures parameters and resu
|
|||||||
}
|
}
|
||||||
Type_Info_Tuple :: Type_Info_Parameters // Will be removed eventually
|
Type_Info_Tuple :: Type_Info_Parameters // Will be removed eventually
|
||||||
|
|
||||||
Type_Info_Struct :: struct {
|
Type_Info_Struct_Flags :: distinct bit_set[Type_Info_Struct_Flag; u8]
|
||||||
types: []^Type_Info,
|
Type_Info_Struct_Flag :: enum u8 {
|
||||||
names: []string,
|
packed = 0,
|
||||||
offsets: []uintptr,
|
raw_union = 1,
|
||||||
usings: []bool,
|
no_copy = 2,
|
||||||
tags: []string,
|
align = 3,
|
||||||
is_packed: bool,
|
}
|
||||||
is_raw_union: bool,
|
|
||||||
is_no_copy: bool,
|
|
||||||
custom_align: bool,
|
|
||||||
|
|
||||||
equal: Equal_Proc, // set only when the struct has .Comparable set but does not have .Simple_Compare set
|
Type_Info_Struct :: struct {
|
||||||
|
// Slice these with `field_count`
|
||||||
|
types: [^]^Type_Info `fmt:"v,field_count"`,
|
||||||
|
names: [^]string `fmt:"v,field_count"`,
|
||||||
|
offsets: [^]uintptr `fmt:"v,field_count"`,
|
||||||
|
usings: [^]bool `fmt:"v,field_count"`,
|
||||||
|
tags: [^]string `fmt:"v,field_count"`,
|
||||||
|
|
||||||
|
field_count: i32,
|
||||||
|
|
||||||
|
flags: Type_Info_Struct_Flags,
|
||||||
|
|
||||||
// These are only set iff this structure is an SOA structure
|
// These are only set iff this structure is an SOA structure
|
||||||
soa_kind: Type_Info_Struct_Soa_Kind,
|
soa_kind: Type_Info_Struct_Soa_Kind,
|
||||||
|
soa_len: i32,
|
||||||
soa_base_type: ^Type_Info,
|
soa_base_type: ^Type_Info,
|
||||||
soa_len: int,
|
|
||||||
|
equal: Equal_Proc, // set only when the struct has .Comparable set but does not have .Simple_Compare set
|
||||||
}
|
}
|
||||||
Type_Info_Union :: struct {
|
Type_Info_Union :: struct {
|
||||||
variants: []^Type_Info,
|
variants: []^Type_Info,
|
||||||
@@ -142,9 +151,9 @@ Type_Info_Union :: struct {
|
|||||||
shared_nil: bool,
|
shared_nil: bool,
|
||||||
}
|
}
|
||||||
Type_Info_Enum :: struct {
|
Type_Info_Enum :: struct {
|
||||||
base: ^Type_Info,
|
base: ^Type_Info,
|
||||||
names: []string,
|
names: []string,
|
||||||
values: []Type_Info_Enum_Value,
|
values: []Type_Info_Enum_Value,
|
||||||
}
|
}
|
||||||
Type_Info_Map :: struct {
|
Type_Info_Map :: struct {
|
||||||
key: ^Type_Info,
|
key: ^Type_Info,
|
||||||
@@ -187,11 +196,12 @@ Type_Info_Soa_Pointer :: struct {
|
|||||||
}
|
}
|
||||||
Type_Info_Bit_Field :: struct {
|
Type_Info_Bit_Field :: struct {
|
||||||
backing_type: ^Type_Info,
|
backing_type: ^Type_Info,
|
||||||
names: []string,
|
names: [^]string `fmt:"v,field_count"`,
|
||||||
types: []^Type_Info,
|
types: [^]^Type_Info `fmt:"v,field_count"`,
|
||||||
bit_sizes: []uintptr,
|
bit_sizes: [^]uintptr `fmt:"v,field_count"`,
|
||||||
bit_offsets: []uintptr,
|
bit_offsets: [^]uintptr `fmt:"v,field_count"`,
|
||||||
tags: []string,
|
tags: [^]string `fmt:"v,field_count"`,
|
||||||
|
field_count: int,
|
||||||
}
|
}
|
||||||
|
|
||||||
Type_Info_Flag :: enum u8 {
|
Type_Info_Flag :: enum u8 {
|
||||||
@@ -299,6 +309,8 @@ when ODIN_OS == .Windows {
|
|||||||
Thread_Detach = 3,
|
Thread_Detach = 3,
|
||||||
}
|
}
|
||||||
dll_forward_reason: DLL_Forward_Reason
|
dll_forward_reason: DLL_Forward_Reason
|
||||||
|
|
||||||
|
dll_instance: rawptr
|
||||||
}
|
}
|
||||||
|
|
||||||
// IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it)
|
// IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it)
|
||||||
@@ -513,11 +525,12 @@ Raw_Quaternion256_Vector_Scalar :: struct {vector: [3]f64, scalar: f64}
|
|||||||
Linux,
|
Linux,
|
||||||
Essence,
|
Essence,
|
||||||
FreeBSD,
|
FreeBSD,
|
||||||
Haiku,
|
|
||||||
OpenBSD,
|
OpenBSD,
|
||||||
NetBSD,
|
NetBSD,
|
||||||
|
Haiku,
|
||||||
WASI,
|
WASI,
|
||||||
JS,
|
JS,
|
||||||
|
Orca,
|
||||||
Freestanding,
|
Freestanding,
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
@@ -577,7 +590,7 @@ Odin_Platform_Subtarget_Type :: type_of(ODIN_PLATFORM_SUBTARGET)
|
|||||||
Memory = 1,
|
Memory = 1,
|
||||||
Thread = 2,
|
Thread = 2,
|
||||||
}
|
}
|
||||||
Odin_Sanitizer_Flags :: distinct bitset[Odin_Sanitizer_Flag; u32]
|
Odin_Sanitizer_Flags :: distinct bit_set[Odin_Sanitizer_Flag; u32]
|
||||||
|
|
||||||
ODIN_SANITIZER_FLAGS // is a constant
|
ODIN_SANITIZER_FLAGS // is a constant
|
||||||
*/
|
*/
|
||||||
|
|||||||
+106
-101
@@ -333,16 +333,23 @@ make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, alloca
|
|||||||
// Note: Prefer using the procedure group `make`.
|
// Note: Prefer using the procedure group `make`.
|
||||||
@(builtin, require_results)
|
@(builtin, require_results)
|
||||||
make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
||||||
make_dynamic_array_error_loc(loc, len, cap)
|
err = _make_dynamic_array_len_cap((^Raw_Dynamic_Array)(&array), size_of(E), align_of(E), len, cap, allocator, loc)
|
||||||
array.allocator = allocator // initialize allocator before just in case it fails to allocate any memory
|
|
||||||
data := mem_alloc_bytes(size_of(E)*cap, align_of(E), allocator, loc) or_return
|
|
||||||
s := Raw_Dynamic_Array{raw_data(data), len, cap, allocator}
|
|
||||||
if data == nil && size_of(E) != 0 {
|
|
||||||
s.len, s.cap = 0, 0
|
|
||||||
}
|
|
||||||
array = transmute(T)s
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
|
_make_dynamic_array_len_cap :: proc(array: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (err: Allocator_Error) {
|
||||||
|
make_dynamic_array_error_loc(loc, len, cap)
|
||||||
|
array.allocator = allocator // initialize allocator before just in case it fails to allocate any memory
|
||||||
|
data := mem_alloc_bytes(size_of_elem*cap, align_of_elem, allocator, loc) or_return
|
||||||
|
use_zero := data == nil && size_of_elem != 0
|
||||||
|
array.data = raw_data(data)
|
||||||
|
array.len = 0 if use_zero else len
|
||||||
|
array.cap = 0 if use_zero else cap
|
||||||
|
array.allocator = allocator
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// `make_map` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value.
|
// `make_map` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value.
|
||||||
// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
|
// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
|
||||||
//
|
//
|
||||||
@@ -440,107 +447,103 @@ delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_append_elem :: #force_inline proc(array: ^$T/[dynamic]$E, arg: E, should_zero: bool, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
_append_elem :: #force_inline proc(array: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, arg_ptr: rawptr, should_zero: bool, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
if array == nil {
|
if array == nil {
|
||||||
return 0, nil
|
return
|
||||||
}
|
}
|
||||||
when size_of(E) == 0 {
|
|
||||||
array := (^Raw_Dynamic_Array)(array)
|
|
||||||
array.len += 1
|
|
||||||
return 1, nil
|
|
||||||
} else {
|
|
||||||
if cap(array) < len(array)+1 {
|
|
||||||
// Same behavior as _append_elems but there's only one arg, so we always just add DEFAULT_DYNAMIC_ARRAY_CAPACITY.
|
|
||||||
cap := 2 * cap(array) + DEFAULT_DYNAMIC_ARRAY_CAPACITY
|
|
||||||
|
|
||||||
// do not 'or_return' here as it could be a partial success
|
if array.cap < array.len+1 {
|
||||||
if should_zero {
|
// Same behavior as _append_elems but there's only one arg, so we always just add DEFAULT_DYNAMIC_ARRAY_CAPACITY.
|
||||||
err = reserve(array, cap, loc)
|
cap := 2 * array.cap + DEFAULT_DYNAMIC_ARRAY_CAPACITY
|
||||||
} else {
|
|
||||||
err = non_zero_reserve(array, cap, loc)
|
// do not 'or_return' here as it could be a partial success
|
||||||
}
|
err = _reserve_dynamic_array(array, size_of_elem, align_of_elem, cap, should_zero, loc)
|
||||||
}
|
|
||||||
if cap(array)-len(array) > 0 {
|
|
||||||
a := (^Raw_Dynamic_Array)(array)
|
|
||||||
when size_of(E) != 0 {
|
|
||||||
data := ([^]E)(a.data)
|
|
||||||
assert(data != nil, loc=loc)
|
|
||||||
data[a.len] = arg
|
|
||||||
}
|
|
||||||
a.len += 1
|
|
||||||
return 1, err
|
|
||||||
}
|
|
||||||
return 0, err
|
|
||||||
}
|
}
|
||||||
|
if array.cap-array.len > 0 {
|
||||||
|
data := ([^]byte)(array.data)
|
||||||
|
assert(data != nil, loc=loc)
|
||||||
|
data = data[array.len*size_of_elem:]
|
||||||
|
intrinsics.mem_copy_non_overlapping(data, arg_ptr, size_of_elem)
|
||||||
|
array.len += 1
|
||||||
|
n = 1
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
return _append_elem(array, arg, true, loc=loc)
|
when size_of(E) == 0 {
|
||||||
|
(^Raw_Dynamic_Array)(array).len += 1
|
||||||
|
return 1, nil
|
||||||
|
} else {
|
||||||
|
arg := arg
|
||||||
|
return _append_elem((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), &arg, true, loc=loc)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
non_zero_append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
non_zero_append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
return _append_elem(array, arg, false, loc=loc)
|
when size_of(E) == 0 {
|
||||||
|
(^Raw_Dynamic_Array)(array).len += 1
|
||||||
|
return 1, nil
|
||||||
|
} else {
|
||||||
|
arg := arg
|
||||||
|
return _append_elem((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), &arg, false, loc=loc)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_append_elems :: #force_inline proc(array: ^$T/[dynamic]$E, should_zero: bool, loc := #caller_location, args: ..E) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
_append_elems :: #force_inline proc(array: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, should_zero: bool, loc := #caller_location, args: rawptr, arg_len: int) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
if array == nil {
|
if array == nil {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
arg_len := len(args)
|
|
||||||
if arg_len <= 0 {
|
if arg_len <= 0 {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
when size_of(E) == 0 {
|
if array.cap < array.len+arg_len {
|
||||||
array := (^Raw_Dynamic_Array)(array)
|
cap := 2 * array.cap + max(DEFAULT_DYNAMIC_ARRAY_CAPACITY, arg_len)
|
||||||
array.len += arg_len
|
|
||||||
return arg_len, nil
|
|
||||||
} else {
|
|
||||||
if cap(array) < len(array)+arg_len {
|
|
||||||
cap := 2 * cap(array) + max(DEFAULT_DYNAMIC_ARRAY_CAPACITY, arg_len)
|
|
||||||
|
|
||||||
// do not 'or_return' here as it could be a partial success
|
// do not 'or_return' here as it could be a partial success
|
||||||
if should_zero {
|
err = _reserve_dynamic_array(array, size_of_elem, align_of_elem, cap, should_zero, loc)
|
||||||
err = reserve(array, cap, loc)
|
|
||||||
} else {
|
|
||||||
err = non_zero_reserve(array, cap, loc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
arg_len = min(cap(array)-len(array), arg_len)
|
|
||||||
if arg_len > 0 {
|
|
||||||
a := (^Raw_Dynamic_Array)(array)
|
|
||||||
when size_of(E) != 0 {
|
|
||||||
data := ([^]E)(a.data)
|
|
||||||
assert(data != nil, loc=loc)
|
|
||||||
intrinsics.mem_copy(&data[a.len], raw_data(args), size_of(E) * arg_len)
|
|
||||||
}
|
|
||||||
a.len += arg_len
|
|
||||||
}
|
|
||||||
return arg_len, err
|
|
||||||
}
|
}
|
||||||
|
arg_len := arg_len
|
||||||
|
arg_len = min(array.cap-array.len, arg_len)
|
||||||
|
if arg_len > 0 {
|
||||||
|
data := ([^]byte)(array.data)
|
||||||
|
assert(data != nil, loc=loc)
|
||||||
|
data = data[array.len*size_of_elem:]
|
||||||
|
intrinsics.mem_copy(data, args, size_of_elem * arg_len) // must be mem_copy (overlapping)
|
||||||
|
array.len += arg_len
|
||||||
|
}
|
||||||
|
return arg_len, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
return _append_elems(array, true, loc, ..args)
|
when size_of(E) == 0 {
|
||||||
|
a := (^Raw_Dynamic_Array)(array)
|
||||||
|
a.len += len(args)
|
||||||
|
return len(args), nil
|
||||||
|
} else {
|
||||||
|
return _append_elems((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), true, loc, raw_data(args), len(args))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
non_zero_append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
non_zero_append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
return _append_elems(array, false, loc, ..args)
|
when size_of(E) == 0 {
|
||||||
|
a := (^Raw_Dynamic_Array)(array)
|
||||||
|
a.len += len(args)
|
||||||
|
return len(args), nil
|
||||||
|
} else {
|
||||||
|
return _append_elems((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), false, loc, raw_data(args), len(args))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type
|
// The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type
|
||||||
_append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, should_zero: bool, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
_append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, should_zero: bool, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
args := transmute([]E)arg
|
return _append_elems((^Raw_Dynamic_Array)(array), 1, 1, should_zero, loc, raw_data(arg), len(arg))
|
||||||
if should_zero {
|
|
||||||
return append_elems(array, ..args, loc=loc)
|
|
||||||
} else {
|
|
||||||
return non_zero_append_elems(array, ..args, loc=loc)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
@@ -679,7 +682,7 @@ assign_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #calle
|
|||||||
|
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||||
new_size := index + len(args)
|
new_size := index + len(args)
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
ok = true
|
ok = true
|
||||||
@@ -729,11 +732,10 @@ clear_dynamic_array :: proc "contextless" (array: ^$T/[dynamic]$E) {
|
|||||||
// `reserve_dynamic_array` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`).
|
// `reserve_dynamic_array` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`).
|
||||||
//
|
//
|
||||||
// Note: Prefer the procedure group `reserve`.
|
// Note: Prefer the procedure group `reserve`.
|
||||||
_reserve_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, capacity: int, should_zero: bool, loc := #caller_location) -> Allocator_Error {
|
_reserve_dynamic_array :: #force_inline proc(a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, capacity: int, should_zero: bool, loc := #caller_location) -> Allocator_Error {
|
||||||
if array == nil {
|
if a == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
a := (^Raw_Dynamic_Array)(array)
|
|
||||||
|
|
||||||
if capacity <= a.cap {
|
if capacity <= a.cap {
|
||||||
return nil
|
return nil
|
||||||
@@ -744,15 +746,15 @@ _reserve_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, capacity: i
|
|||||||
}
|
}
|
||||||
assert(a.allocator.procedure != nil)
|
assert(a.allocator.procedure != nil)
|
||||||
|
|
||||||
old_size := a.cap * size_of(E)
|
old_size := a.cap * size_of_elem
|
||||||
new_size := capacity * size_of(E)
|
new_size := capacity * size_of_elem
|
||||||
allocator := a.allocator
|
allocator := a.allocator
|
||||||
|
|
||||||
new_data: []byte
|
new_data: []byte
|
||||||
if should_zero {
|
if should_zero {
|
||||||
new_data = mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
new_data = mem_resize(a.data, old_size, new_size, align_of_elem, allocator, loc) or_return
|
||||||
} else {
|
} else {
|
||||||
new_data = non_zero_mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
new_data = non_zero_mem_resize(a.data, old_size, new_size, align_of_elem, allocator, loc) or_return
|
||||||
}
|
}
|
||||||
if new_data == nil && new_size > 0 {
|
if new_data == nil && new_size > 0 {
|
||||||
return .Out_Of_Memory
|
return .Out_Of_Memory
|
||||||
@@ -765,26 +767,23 @@ _reserve_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, capacity: i
|
|||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error {
|
reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error {
|
||||||
return _reserve_dynamic_array(array, capacity, true, loc)
|
return _reserve_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), capacity, true, loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
non_zero_reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error {
|
non_zero_reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error {
|
||||||
return _reserve_dynamic_array(array, capacity, false, loc)
|
return _reserve_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), capacity, false, loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// `resize_dynamic_array` will try to resize memory of a passed dynamic array or map to the requested element count (setting the `len`, and possibly `cap`).
|
|
||||||
//
|
_resize_dynamic_array :: #force_inline proc(a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, length: int, should_zero: bool, loc := #caller_location) -> Allocator_Error {
|
||||||
// Note: Prefer the procedure group `resize`
|
if a == nil {
|
||||||
_resize_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, length: int, should_zero: bool, loc := #caller_location) -> Allocator_Error {
|
|
||||||
if array == nil {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
a := (^Raw_Dynamic_Array)(array)
|
|
||||||
|
|
||||||
if length <= a.cap {
|
if length <= a.cap {
|
||||||
if should_zero && a.len < length {
|
if should_zero && a.len < length {
|
||||||
intrinsics.mem_zero(([^]E)(a.data)[a.len:], (length-a.len)*size_of(E))
|
intrinsics.mem_zero(([^]byte)(a.data)[a.len*size_of_elem:], (length-a.len)*size_of_elem)
|
||||||
}
|
}
|
||||||
a.len = max(length, 0)
|
a.len = max(length, 0)
|
||||||
return nil
|
return nil
|
||||||
@@ -795,15 +794,15 @@ _resize_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, length: int,
|
|||||||
}
|
}
|
||||||
assert(a.allocator.procedure != nil)
|
assert(a.allocator.procedure != nil)
|
||||||
|
|
||||||
old_size := a.cap * size_of(E)
|
old_size := a.cap * size_of_elem
|
||||||
new_size := length * size_of(E)
|
new_size := length * size_of_elem
|
||||||
allocator := a.allocator
|
allocator := a.allocator
|
||||||
|
|
||||||
new_data : []byte
|
new_data : []byte
|
||||||
if should_zero {
|
if should_zero {
|
||||||
new_data = mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
new_data = mem_resize(a.data, old_size, new_size, align_of_elem, allocator, loc) or_return
|
||||||
} else {
|
} else {
|
||||||
new_data = non_zero_mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
new_data = non_zero_mem_resize(a.data, old_size, new_size, align_of_elem, allocator, loc) or_return
|
||||||
}
|
}
|
||||||
if new_data == nil && new_size > 0 {
|
if new_data == nil && new_size > 0 {
|
||||||
return .Out_Of_Memory
|
return .Out_Of_Memory
|
||||||
@@ -815,14 +814,17 @@ _resize_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, length: int,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `resize_dynamic_array` will try to resize memory of a passed dynamic array or map to the requested element count (setting the `len`, and possibly `cap`).
|
||||||
|
//
|
||||||
|
// Note: Prefer the procedure group `resize`
|
||||||
@builtin
|
@builtin
|
||||||
resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error {
|
resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error {
|
||||||
return _resize_dynamic_array(array, length, true, loc=loc)
|
return _resize_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), length, true, loc=loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
non_zero_resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error {
|
non_zero_resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error {
|
||||||
return _resize_dynamic_array(array, length, false, loc=loc)
|
return _resize_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), length, false, loc=loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -837,10 +839,13 @@ non_zero_resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: i
|
|||||||
Note: Prefer the procedure group `shrink`
|
Note: Prefer the procedure group `shrink`
|
||||||
*/
|
*/
|
||||||
shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) {
|
shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) {
|
||||||
if array == nil {
|
return _shrink_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), new_cap, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
_shrink_dynamic_array :: proc(a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, new_cap := -1, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) {
|
||||||
|
if a == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
a := (^Raw_Dynamic_Array)(array)
|
|
||||||
|
|
||||||
new_cap := new_cap if new_cap >= 0 else a.len
|
new_cap := new_cap if new_cap >= 0 else a.len
|
||||||
|
|
||||||
@@ -853,10 +858,10 @@ shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #call
|
|||||||
}
|
}
|
||||||
assert(a.allocator.procedure != nil)
|
assert(a.allocator.procedure != nil)
|
||||||
|
|
||||||
old_size := a.cap * size_of(E)
|
old_size := a.cap * size_of_elem
|
||||||
new_size := new_cap * size_of(E)
|
new_size := new_cap * size_of_elem
|
||||||
|
|
||||||
new_data := mem_resize(a.data, old_size, new_size, align_of(E), a.allocator, loc) or_return
|
new_data := mem_resize(a.data, old_size, new_size, align_of_elem, a.allocator, loc) or_return
|
||||||
|
|
||||||
a.data = raw_data(new_data)
|
a.data = raw_data(new_data)
|
||||||
a.len = min(new_cap, a.len)
|
a.len = min(new_cap, a.len)
|
||||||
|
|||||||
@@ -352,7 +352,7 @@ non_zero_append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, #no_broadcast args
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, zero_memory: bool, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
_append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, zero_memory: bool, #no_broadcast args: []E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
if array == nil {
|
if array == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -577,7 +577,7 @@ map_grow_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Inf
|
|||||||
|
|
||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
map_reserve_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, new_capacity: uintptr, loc := #caller_location) -> Allocator_Error {
|
map_reserve_dynamic :: #force_no_inline proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, new_capacity: uintptr, loc := #caller_location) -> Allocator_Error {
|
||||||
@(require_results)
|
@(require_results)
|
||||||
ceil_log2 :: #force_inline proc "contextless" (x: uintptr) -> uintptr {
|
ceil_log2 :: #force_inline proc "contextless" (x: uintptr) -> uintptr {
|
||||||
z := intrinsics.count_leading_zeros(x)
|
z := intrinsics.count_leading_zeros(x)
|
||||||
@@ -641,7 +641,7 @@ map_reserve_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_
|
|||||||
|
|
||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
map_shrink_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) {
|
map_shrink_dynamic :: #force_no_inline proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) {
|
||||||
if m.allocator.procedure == nil {
|
if m.allocator.procedure == nil {
|
||||||
m.allocator = context.allocator
|
m.allocator = context.allocator
|
||||||
}
|
}
|
||||||
@@ -688,7 +688,7 @@ map_shrink_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_I
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
map_free_dynamic :: proc "odin" (m: Raw_Map, info: ^Map_Info, loc := #caller_location) -> Allocator_Error {
|
map_free_dynamic :: #force_no_inline proc "odin" (m: Raw_Map, info: ^Map_Info, loc := #caller_location) -> Allocator_Error {
|
||||||
ptr := rawptr(map_data(m))
|
ptr := rawptr(map_data(m))
|
||||||
size := int(map_total_allocation_size(uintptr(map_cap(m)), info))
|
size := int(map_total_allocation_size(uintptr(map_cap(m)), info))
|
||||||
err := mem_free_with_size(ptr, size, m.allocator, loc)
|
err := mem_free_with_size(ptr, size, m.allocator, loc)
|
||||||
@@ -700,7 +700,7 @@ map_free_dynamic :: proc "odin" (m: Raw_Map, info: ^Map_Info, loc := #caller_loc
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
map_lookup_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info, k: uintptr) -> (index: uintptr, ok: bool) {
|
map_lookup_dynamic :: #force_no_inline proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info, k: uintptr) -> (index: uintptr, ok: bool) {
|
||||||
if map_len(m) == 0 {
|
if map_len(m) == 0 {
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
@@ -723,7 +723,7 @@ map_lookup_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
@(require_results)
|
@(require_results)
|
||||||
map_exists_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info, k: uintptr) -> (ok: bool) {
|
map_exists_dynamic :: #force_no_inline proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info, k: uintptr) -> (ok: bool) {
|
||||||
if map_len(m) == 0 {
|
if map_len(m) == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -749,7 +749,7 @@ map_exists_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info,
|
|||||||
|
|
||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
map_erase_dynamic :: #force_inline proc "contextless" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, k: uintptr) -> (old_k, old_v: uintptr, ok: bool) {
|
map_erase_dynamic :: #force_no_inline proc "contextless" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, k: uintptr) -> (old_k, old_v: uintptr, ok: bool) {
|
||||||
index := map_lookup_dynamic(m^, info, k) or_return
|
index := map_lookup_dynamic(m^, info, k) or_return
|
||||||
ks, vs, hs, _, _ := map_kvh_data_dynamic(m^, info)
|
ks, vs, hs, _, _ := map_kvh_data_dynamic(m^, info)
|
||||||
hs[index] |= TOMBSTONE_MASK
|
hs[index] |= TOMBSTONE_MASK
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ when ODIN_BUILD_MODE == .Dynamic {
|
|||||||
DllMain :: proc "system" (hinstDLL: rawptr, fdwReason: u32, lpReserved: rawptr) -> b32 {
|
DllMain :: proc "system" (hinstDLL: rawptr, fdwReason: u32, lpReserved: rawptr) -> b32 {
|
||||||
context = default_context()
|
context = default_context()
|
||||||
|
|
||||||
// Populate Windows DLL-specific global
|
// Populate Windows DLL-specific globals
|
||||||
dll_forward_reason = DLL_Forward_Reason(fdwReason)
|
dll_forward_reason = DLL_Forward_Reason(fdwReason)
|
||||||
|
dll_instance = hinstDLL
|
||||||
|
|
||||||
switch dll_forward_reason {
|
switch dll_forward_reason {
|
||||||
case .Process_Attach:
|
case .Process_Attach:
|
||||||
|
|||||||
@@ -5,11 +5,24 @@ package runtime
|
|||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||||
WRITE :: 0x2000004
|
|
||||||
STDERR :: 2
|
STDERR :: 2
|
||||||
ret := intrinsics.syscall(WRITE, STDERR, uintptr(raw_data(data)), uintptr(len(data)))
|
when ODIN_NO_CRT {
|
||||||
if ret < 0 {
|
WRITE :: 0x2000004
|
||||||
return 0, _OS_Errno(-ret)
|
ret := intrinsics.syscall(WRITE, STDERR, uintptr(raw_data(data)), uintptr(len(data)))
|
||||||
|
if ret < 0 {
|
||||||
|
return 0, _OS_Errno(-ret)
|
||||||
|
}
|
||||||
|
return int(ret), 0
|
||||||
|
} else {
|
||||||
|
foreign {
|
||||||
|
write :: proc(handle: i32, buffer: [^]byte, count: uint) -> int ---
|
||||||
|
__error :: proc() -> ^i32 ---
|
||||||
|
}
|
||||||
|
|
||||||
|
if ret := write(STDERR, raw_data(data), len(data)); ret >= 0 {
|
||||||
|
return int(ret), 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, _OS_Errno(__error()^)
|
||||||
}
|
}
|
||||||
return int(ret), 0
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -262,7 +262,7 @@ print_typeid :: #force_no_inline proc "contextless" (id: typeid) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="size")
|
@(optimization_mode="favor_size")
|
||||||
print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
|
print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
|
||||||
if ti == nil {
|
if ti == nil {
|
||||||
print_string("nil")
|
print_string("nil")
|
||||||
@@ -401,15 +401,16 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
print_string("struct ")
|
print_string("struct ")
|
||||||
if info.is_packed { print_string("#packed ") }
|
if .packed in info.flags { print_string("#packed ") }
|
||||||
if info.is_raw_union { print_string("#raw_union ") }
|
if .raw_union in info.flags { print_string("#raw_union ") }
|
||||||
if info.custom_align {
|
if .no_copy in info.flags { print_string("#no_copy ") }
|
||||||
|
if .align in info.flags {
|
||||||
print_string("#align(")
|
print_string("#align(")
|
||||||
print_u64(u64(ti.align))
|
print_u64(u64(ti.align))
|
||||||
print_string(") ")
|
print_string(") ")
|
||||||
}
|
}
|
||||||
print_byte('{')
|
print_byte('{')
|
||||||
for name, i in info.names {
|
for name, i in info.names[:info.field_count] {
|
||||||
if i > 0 { print_string(", ") }
|
if i > 0 { print_string(", ") }
|
||||||
print_string(name)
|
print_string(name)
|
||||||
print_string(": ")
|
print_string(": ")
|
||||||
@@ -469,7 +470,7 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
|
|||||||
print_string("bit_field ")
|
print_string("bit_field ")
|
||||||
print_type(info.backing_type)
|
print_type(info.backing_type)
|
||||||
print_string(" {")
|
print_string(" {")
|
||||||
for name, i in info.names {
|
for name, i in info.names[:info.field_count] {
|
||||||
if i > 0 { print_string(", ") }
|
if i > 0 { print_string(", ") }
|
||||||
print_string(name)
|
print_string(name)
|
||||||
print_string(": ")
|
print_string(": ")
|
||||||
|
|||||||
@@ -297,7 +297,8 @@ lock :: proc(a: ^WASM_Allocator) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(intrinsics.wasm_memory_atomic_wait32((^u32)(&a.mu), u32(new_state), -1) != 0)
|
ret := intrinsics.wasm_memory_atomic_wait32((^u32)(&a.mu), u32(new_state), -1)
|
||||||
|
assert(ret != 0)
|
||||||
intrinsics.cpu_relax()
|
intrinsics.cpu_relax()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -48,6 +48,9 @@ if "%2" == "1" (
|
|||||||
set odin_version_raw="dev-%curr_year%-%curr_month%"
|
set odin_version_raw="dev-%curr_year%-%curr_month%"
|
||||||
|
|
||||||
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF
|
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF
|
||||||
|
rem Parse source code as utf-8 even on shift-jis and other codepages
|
||||||
|
rem See https://learn.microsoft.com/en-us/cpp/build/reference/utf-8-set-source-and-executable-character-sets-to-utf-8?view=msvc-170
|
||||||
|
set compiler_flags= %compiler_flags% /utf-8
|
||||||
set compiler_defines= -DODIN_VERSION_RAW=\"%odin_version_raw%\"
|
set compiler_defines= -DODIN_VERSION_RAW=\"%odin_version_raw%\"
|
||||||
|
|
||||||
if not exist .git\ goto skip_git_hash
|
if not exist .git\ goto skip_git_hash
|
||||||
@@ -111,7 +114,7 @@ call build_vendor.bat
|
|||||||
if %errorlevel% neq 0 goto end_of_build
|
if %errorlevel% neq 0 goto end_of_build
|
||||||
|
|
||||||
rem If the demo doesn't run for you and your CPU is more than a decade old, try -microarch:native
|
rem If the demo doesn't run for you and your CPU is more than a decade old, try -microarch:native
|
||||||
if %release_mode% EQU 0 odin run examples/demo -- Hellope World
|
if %release_mode% EQU 0 odin run examples/demo -vet -strict-style -- Hellope World
|
||||||
|
|
||||||
del *.obj > NUL 2> NUL
|
del *.obj > NUL 2> NUL
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -144,7 +144,7 @@ build_odin() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
run_demo() {
|
run_demo() {
|
||||||
./odin run examples/demo/demo.odin -file -- Hellope World
|
./odin run examples/demo -vet -strict-style -- Hellope World
|
||||||
}
|
}
|
||||||
|
|
||||||
if [ $# -eq 0 ]; then
|
if [ $# -eq 0 ]; then
|
||||||
|
|||||||
@@ -1167,3 +1167,28 @@ fields_proc :: proc(s: []byte, f: proc(rune) -> bool, allocator := context.alloc
|
|||||||
|
|
||||||
return subslices[:]
|
return subslices[:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// alias returns true iff a and b have a non-zero length, and any part of
|
||||||
|
// a overlaps with b.
|
||||||
|
alias :: proc "contextless" (a, b: []byte) -> bool {
|
||||||
|
a_len, b_len := len(a), len(b)
|
||||||
|
if a_len == 0 || b_len == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
a_start, b_start := uintptr(raw_data(a)), uintptr(raw_data(b))
|
||||||
|
a_end, b_end := a_start + uintptr(a_len-1), b_start + uintptr(b_len-1)
|
||||||
|
|
||||||
|
return a_start <= b_end && b_start <= a_end
|
||||||
|
}
|
||||||
|
|
||||||
|
// alias_inexactly returns true iff a and b have a non-zero length,
|
||||||
|
// the base pointer of a and b are NOT equal, and any part of a overlaps
|
||||||
|
// with b (ie: `alias(a, b)` with an exception that returns false for
|
||||||
|
// `a == b`, `b = a[:len(a)-69]` and similar conditions).
|
||||||
|
alias_inexactly :: proc "contextless" (a, b: []byte) -> bool {
|
||||||
|
if raw_data(a) == raw_data(b) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return alias(a, b)
|
||||||
|
}
|
||||||
|
|||||||
+25
-25
@@ -186,7 +186,7 @@ input_size_from_stream :: proc(z: ^Context_Stream_Input) -> (res: i64, err: Erro
|
|||||||
|
|
||||||
input_size :: proc{input_size_from_memory, input_size_from_stream}
|
input_size :: proc{input_size_from_memory, input_size_from_stream}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_slice_from_memory :: #force_inline proc(z: ^Context_Memory_Input, size: int) -> (res: []u8, err: io.Error) {
|
read_slice_from_memory :: #force_inline proc(z: ^Context_Memory_Input, size: int) -> (res: []u8, err: io.Error) {
|
||||||
#no_bounds_check {
|
#no_bounds_check {
|
||||||
if len(z.input_data) >= size {
|
if len(z.input_data) >= size {
|
||||||
@@ -203,7 +203,7 @@ read_slice_from_memory :: #force_inline proc(z: ^Context_Memory_Input, size: int
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_slice_from_stream :: #force_inline proc(z: ^Context_Stream_Input, size: int) -> (res: []u8, err: io.Error) {
|
read_slice_from_stream :: #force_inline proc(z: ^Context_Stream_Input, size: int) -> (res: []u8, err: io.Error) {
|
||||||
// TODO: REMOVE ALL USE OF context.temp_allocator here
|
// TODO: REMOVE ALL USE OF context.temp_allocator here
|
||||||
// there is literally no need for it
|
// there is literally no need for it
|
||||||
@@ -214,13 +214,13 @@ read_slice_from_stream :: #force_inline proc(z: ^Context_Stream_Input, size: int
|
|||||||
|
|
||||||
read_slice :: proc{read_slice_from_memory, read_slice_from_stream}
|
read_slice :: proc{read_slice_from_memory, read_slice_from_stream}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_data :: #force_inline proc(z: ^$C, $T: typeid) -> (res: T, err: io.Error) {
|
read_data :: #force_inline proc(z: ^$C, $T: typeid) -> (res: T, err: io.Error) {
|
||||||
b := read_slice(z, size_of(T)) or_return
|
b := read_slice(z, size_of(T)) or_return
|
||||||
return (^T)(&b[0])^, nil
|
return (^T)(&b[0])^, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_u8_from_memory :: #force_inline proc(z: ^Context_Memory_Input) -> (res: u8, err: io.Error) {
|
read_u8_from_memory :: #force_inline proc(z: ^Context_Memory_Input) -> (res: u8, err: io.Error) {
|
||||||
#no_bounds_check {
|
#no_bounds_check {
|
||||||
if len(z.input_data) >= 1 {
|
if len(z.input_data) >= 1 {
|
||||||
@@ -232,7 +232,7 @@ read_u8_from_memory :: #force_inline proc(z: ^Context_Memory_Input) -> (res: u8,
|
|||||||
return 0, .EOF
|
return 0, .EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_u8_from_stream :: #force_inline proc(z: ^Context_Stream_Input) -> (res: u8, err: io.Error) {
|
read_u8_from_stream :: #force_inline proc(z: ^Context_Stream_Input) -> (res: u8, err: io.Error) {
|
||||||
b := read_slice_from_stream(z, 1) or_return
|
b := read_slice_from_stream(z, 1) or_return
|
||||||
return b[0], nil
|
return b[0], nil
|
||||||
@@ -242,7 +242,7 @@ read_u8 :: proc{read_u8_from_memory, read_u8_from_stream}
|
|||||||
|
|
||||||
// You would typically only use this at the end of Inflate, to drain bits from the code buffer
|
// You would typically only use this at the end of Inflate, to drain bits from the code buffer
|
||||||
// preferentially.
|
// preferentially.
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_u8_prefer_code_buffer_lsb :: #force_inline proc(z: ^$C) -> (res: u8, err: io.Error) {
|
read_u8_prefer_code_buffer_lsb :: #force_inline proc(z: ^$C) -> (res: u8, err: io.Error) {
|
||||||
if z.num_bits >= 8 {
|
if z.num_bits >= 8 {
|
||||||
res = u8(read_bits_no_refill_lsb(z, 8))
|
res = u8(read_bits_no_refill_lsb(z, 8))
|
||||||
@@ -257,7 +257,7 @@ read_u8_prefer_code_buffer_lsb :: #force_inline proc(z: ^$C) -> (res: u8, err: i
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
peek_data_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid) -> (res: T, err: io.Error) {
|
peek_data_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid) -> (res: T, err: io.Error) {
|
||||||
size :: size_of(T)
|
size :: size_of(T)
|
||||||
|
|
||||||
@@ -275,7 +275,7 @@ peek_data_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
peek_data_at_offset_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid, #any_int offset: int) -> (res: T, err: io.Error) {
|
peek_data_at_offset_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid, #any_int offset: int) -> (res: T, err: io.Error) {
|
||||||
size :: size_of(T)
|
size :: size_of(T)
|
||||||
|
|
||||||
@@ -293,7 +293,7 @@ peek_data_at_offset_from_memory :: #force_inline proc(z: ^Context_Memory_Input,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid) -> (res: T, err: io.Error) {
|
peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid) -> (res: T, err: io.Error) {
|
||||||
size :: size_of(T)
|
size :: size_of(T)
|
||||||
|
|
||||||
@@ -317,7 +317,7 @@ peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid
|
|||||||
return res, .None
|
return res, .None
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
peek_data_at_offset_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid, #any_int offset: int) -> (res: T, err: io.Error) {
|
peek_data_at_offset_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid, #any_int offset: int) -> (res: T, err: io.Error) {
|
||||||
size :: size_of(T)
|
size :: size_of(T)
|
||||||
|
|
||||||
@@ -352,14 +352,14 @@ peek_data :: proc{peek_data_from_memory, peek_data_from_stream, peek_data_at_off
|
|||||||
|
|
||||||
|
|
||||||
// Sliding window read back
|
// Sliding window read back
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
peek_back_byte :: #force_inline proc(z: ^$C, offset: i64) -> (res: u8, err: io.Error) {
|
peek_back_byte :: #force_inline proc(z: ^$C, offset: i64) -> (res: u8, err: io.Error) {
|
||||||
// Look back into the sliding window.
|
// Look back into the sliding window.
|
||||||
return z.output.buf[z.bytes_written - offset], .None
|
return z.output.buf[z.bytes_written - offset], .None
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generalized bit reader LSB
|
// Generalized bit reader LSB
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width := i8(48)) {
|
refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width := i8(48)) {
|
||||||
refill := u64(width)
|
refill := u64(width)
|
||||||
b := u64(0)
|
b := u64(0)
|
||||||
@@ -385,7 +385,7 @@ refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width :=
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generalized bit reader LSB
|
// Generalized bit reader LSB
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
refill_lsb_from_stream :: proc(z: ^Context_Stream_Input, width := i8(24)) {
|
refill_lsb_from_stream :: proc(z: ^Context_Stream_Input, width := i8(24)) {
|
||||||
refill := u64(width)
|
refill := u64(width)
|
||||||
|
|
||||||
@@ -414,13 +414,13 @@ refill_lsb_from_stream :: proc(z: ^Context_Stream_Input, width := i8(24)) {
|
|||||||
refill_lsb :: proc{refill_lsb_from_memory, refill_lsb_from_stream}
|
refill_lsb :: proc{refill_lsb_from_memory, refill_lsb_from_stream}
|
||||||
|
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
consume_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) {
|
consume_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) {
|
||||||
z.code_buffer >>= width
|
z.code_buffer >>= width
|
||||||
z.num_bits -= u64(width)
|
z.num_bits -= u64(width)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
consume_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) {
|
consume_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) {
|
||||||
z.code_buffer >>= width
|
z.code_buffer >>= width
|
||||||
z.num_bits -= u64(width)
|
z.num_bits -= u64(width)
|
||||||
@@ -428,7 +428,7 @@ consume_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, wid
|
|||||||
|
|
||||||
consume_bits_lsb :: proc{consume_bits_lsb_from_memory, consume_bits_lsb_from_stream}
|
consume_bits_lsb :: proc{consume_bits_lsb_from_memory, consume_bits_lsb_from_stream}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
peek_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
peek_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
||||||
if z.num_bits < u64(width) {
|
if z.num_bits < u64(width) {
|
||||||
refill_lsb(z)
|
refill_lsb(z)
|
||||||
@@ -436,7 +436,7 @@ peek_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width:
|
|||||||
return u32(z.code_buffer &~ (~u64(0) << width))
|
return u32(z.code_buffer &~ (~u64(0) << width))
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
peek_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
peek_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
||||||
if z.num_bits < u64(width) {
|
if z.num_bits < u64(width) {
|
||||||
refill_lsb(z)
|
refill_lsb(z)
|
||||||
@@ -446,13 +446,13 @@ peek_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width:
|
|||||||
|
|
||||||
peek_bits_lsb :: proc{peek_bits_lsb_from_memory, peek_bits_lsb_from_stream}
|
peek_bits_lsb :: proc{peek_bits_lsb_from_memory, peek_bits_lsb_from_stream}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
peek_bits_no_refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
peek_bits_no_refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
||||||
assert(z.num_bits >= u64(width))
|
assert(z.num_bits >= u64(width))
|
||||||
return u32(z.code_buffer &~ (~u64(0) << width))
|
return u32(z.code_buffer &~ (~u64(0) << width))
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
peek_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
peek_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
||||||
assert(z.num_bits >= u64(width))
|
assert(z.num_bits >= u64(width))
|
||||||
return u32(z.code_buffer &~ (~u64(0) << width))
|
return u32(z.code_buffer &~ (~u64(0) << width))
|
||||||
@@ -460,14 +460,14 @@ peek_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Inp
|
|||||||
|
|
||||||
peek_bits_no_refill_lsb :: proc{peek_bits_no_refill_lsb_from_memory, peek_bits_no_refill_lsb_from_stream}
|
peek_bits_no_refill_lsb :: proc{peek_bits_no_refill_lsb_from_memory, peek_bits_no_refill_lsb_from_stream}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
read_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
||||||
k := #force_inline peek_bits_lsb(z, width)
|
k := #force_inline peek_bits_lsb(z, width)
|
||||||
#force_inline consume_bits_lsb(z, width)
|
#force_inline consume_bits_lsb(z, width)
|
||||||
return k
|
return k
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
read_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
||||||
k := peek_bits_lsb(z, width)
|
k := peek_bits_lsb(z, width)
|
||||||
consume_bits_lsb(z, width)
|
consume_bits_lsb(z, width)
|
||||||
@@ -476,14 +476,14 @@ read_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width:
|
|||||||
|
|
||||||
read_bits_lsb :: proc{read_bits_lsb_from_memory, read_bits_lsb_from_stream}
|
read_bits_lsb :: proc{read_bits_lsb_from_memory, read_bits_lsb_from_stream}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_bits_no_refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
read_bits_no_refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
||||||
k := #force_inline peek_bits_no_refill_lsb(z, width)
|
k := #force_inline peek_bits_no_refill_lsb(z, width)
|
||||||
#force_inline consume_bits_lsb(z, width)
|
#force_inline consume_bits_lsb(z, width)
|
||||||
return k
|
return k
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
read_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
||||||
k := peek_bits_no_refill_lsb(z, width)
|
k := peek_bits_no_refill_lsb(z, width)
|
||||||
consume_bits_lsb(z, width)
|
consume_bits_lsb(z, width)
|
||||||
@@ -493,14 +493,14 @@ read_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Inp
|
|||||||
read_bits_no_refill_lsb :: proc{read_bits_no_refill_lsb_from_memory, read_bits_no_refill_lsb_from_stream}
|
read_bits_no_refill_lsb :: proc{read_bits_no_refill_lsb_from_memory, read_bits_no_refill_lsb_from_stream}
|
||||||
|
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
discard_to_next_byte_lsb_from_memory :: proc(z: ^Context_Memory_Input) {
|
discard_to_next_byte_lsb_from_memory :: proc(z: ^Context_Memory_Input) {
|
||||||
discard := u8(z.num_bits & 7)
|
discard := u8(z.num_bits & 7)
|
||||||
#force_inline consume_bits_lsb(z, discard)
|
#force_inline consume_bits_lsb(z, discard)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
discard_to_next_byte_lsb_from_stream :: proc(z: ^Context_Stream_Input) {
|
discard_to_next_byte_lsb_from_stream :: proc(z: ^Context_Stream_Input) {
|
||||||
discard := u8(z.num_bits & 7)
|
discard := u8(z.num_bits & 7)
|
||||||
consume_bits_lsb(z, discard)
|
consume_bits_lsb(z, discard)
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ Huffman_Table :: struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Implementation starts here
|
// Implementation starts here
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
z_bit_reverse :: #force_inline proc(n: u16, bits: u8) -> (r: u16) {
|
z_bit_reverse :: #force_inline proc(n: u16, bits: u8) -> (r: u16) {
|
||||||
assert(bits <= 16)
|
assert(bits <= 16)
|
||||||
// NOTE: Can optimize with llvm.bitreverse.i64 or some bit twiddling
|
// NOTE: Can optimize with llvm.bitreverse.i64 or some bit twiddling
|
||||||
@@ -136,7 +136,7 @@ z_bit_reverse :: #force_inline proc(n: u16, bits: u8) -> (r: u16) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
grow_buffer :: proc(buf: ^[dynamic]u8) -> (err: compress.Error) {
|
grow_buffer :: proc(buf: ^[dynamic]u8) -> (err: compress.Error) {
|
||||||
/*
|
/*
|
||||||
That we get here at all means that we didn't pass an expected output size,
|
That we get here at all means that we didn't pass an expected output size,
|
||||||
@@ -154,7 +154,7 @@ grow_buffer :: proc(buf: ^[dynamic]u8) -> (err: compress.Error) {
|
|||||||
TODO: Make these return compress.Error.
|
TODO: Make these return compress.Error.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
write_byte :: #force_inline proc(z: ^$C, c: u8) -> (err: io.Error) #no_bounds_check {
|
write_byte :: #force_inline proc(z: ^$C, c: u8) -> (err: io.Error) #no_bounds_check {
|
||||||
/*
|
/*
|
||||||
Resize if needed.
|
Resize if needed.
|
||||||
@@ -173,7 +173,7 @@ write_byte :: #force_inline proc(z: ^$C, c: u8) -> (err: io.Error) #no_bounds_ch
|
|||||||
return .None
|
return .None
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
repl_byte :: proc(z: ^$C, count: u16, c: u8) -> (err: io.Error) #no_bounds_check {
|
repl_byte :: proc(z: ^$C, count: u16, c: u8) -> (err: io.Error) #no_bounds_check {
|
||||||
/*
|
/*
|
||||||
TODO(Jeroen): Once we have a magic ring buffer, we can just peek/write into it
|
TODO(Jeroen): Once we have a magic ring buffer, we can just peek/write into it
|
||||||
@@ -201,7 +201,7 @@ repl_byte :: proc(z: ^$C, count: u16, c: u8) -> (err: io.Error) #no_bounds_check
|
|||||||
return .None
|
return .None
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
repl_bytes :: proc(z: ^$C, count: u16, distance: u16) -> (err: io.Error) {
|
repl_bytes :: proc(z: ^$C, count: u16, distance: u16) -> (err: io.Error) {
|
||||||
/*
|
/*
|
||||||
TODO(Jeroen): Once we have a magic ring buffer, we can just peek/write into it
|
TODO(Jeroen): Once we have a magic ring buffer, we can just peek/write into it
|
||||||
@@ -234,8 +234,8 @@ allocate_huffman_table :: proc(allocator := context.allocator) -> (z: ^Huffman_T
|
|||||||
return new(Huffman_Table, allocator), nil
|
return new(Huffman_Table, allocator), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
|
build_huffman :: #force_no_inline proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
|
||||||
sizes: [HUFFMAN_MAX_BITS+1]int
|
sizes: [HUFFMAN_MAX_BITS+1]int
|
||||||
next_code: [HUFFMAN_MAX_BITS+1]int
|
next_code: [HUFFMAN_MAX_BITS+1]int
|
||||||
|
|
||||||
@@ -293,7 +293,7 @@ build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
decode_huffman_slowpath :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
|
decode_huffman_slowpath :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
|
||||||
code := u16(compress.peek_bits_lsb(z,16))
|
code := u16(compress.peek_bits_lsb(z,16))
|
||||||
|
|
||||||
@@ -324,7 +324,7 @@ decode_huffman_slowpath :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Erro
|
|||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
decode_huffman :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
|
decode_huffman :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
|
||||||
if z.num_bits < 16 {
|
if z.num_bits < 16 {
|
||||||
if z.num_bits > 63 {
|
if z.num_bits > 63 {
|
||||||
@@ -344,7 +344,7 @@ decode_huffman :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bo
|
|||||||
return decode_huffman_slowpath(z, t)
|
return decode_huffman_slowpath(z, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err: Error) #no_bounds_check {
|
parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err: Error) #no_bounds_check {
|
||||||
#no_bounds_check for {
|
#no_bounds_check for {
|
||||||
value, e := decode_huffman(z, z_repeat)
|
value, e := decode_huffman(z, z_repeat)
|
||||||
@@ -413,7 +413,7 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := false, expected_output_size := -1, allocator := context.allocator) -> (err: Error) #no_bounds_check {
|
inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := false, expected_output_size := -1, allocator := context.allocator) -> (err: Error) #no_bounds_check {
|
||||||
/*
|
/*
|
||||||
ctx.output must be a bytes.Buffer for now. We'll add a separate implementation that writes to a stream.
|
ctx.output must be a bytes.Buffer for now. We'll add a separate implementation that writes to a stream.
|
||||||
@@ -486,7 +486,7 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f
|
|||||||
|
|
||||||
// TODO: Check alignment of reserve/resize.
|
// TODO: Check alignment of reserve/resize.
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.allocator) -> (err: Error) #no_bounds_check {
|
inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.allocator) -> (err: Error) #no_bounds_check {
|
||||||
context.allocator = allocator
|
context.allocator = allocator
|
||||||
expected_output_size := expected_output_size
|
expected_output_size := expected_output_size
|
||||||
@@ -670,4 +670,4 @@ inflate_from_byte_array_raw :: proc(input: []u8, buf: ^bytes.Buffer, raw := fals
|
|||||||
return inflate_raw(&ctx, expected_output_size=expected_output_size)
|
return inflate_raw(&ctx, expected_output_size=expected_output_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
inflate :: proc{inflate_from_context, inflate_from_byte_array}
|
inflate :: proc{inflate_from_context, inflate_from_byte_array}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
Package list implements an intrusive doubly-linked list.
|
||||||
|
|
||||||
|
An intrusive container requires a `Node` to be embedded in your own structure, like this:
|
||||||
|
|
||||||
|
My_String :: struct {
|
||||||
|
node: list.Node,
|
||||||
|
value: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
Embedding the members of a `list.Node` in your structure with the `using` keyword is also allowed:
|
||||||
|
|
||||||
|
My_String :: struct {
|
||||||
|
using node: list.Node,
|
||||||
|
value: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
Here is a full example:
|
||||||
|
|
||||||
|
package test
|
||||||
|
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:container/intrusive/list"
|
||||||
|
|
||||||
|
main :: proc() {
|
||||||
|
l: list.List
|
||||||
|
|
||||||
|
one := My_String{value="Hello"}
|
||||||
|
two := My_String{value="World"}
|
||||||
|
|
||||||
|
list.push_back(&l, &one.node)
|
||||||
|
list.push_back(&l, &two.node)
|
||||||
|
|
||||||
|
iter := list.iterator_head(l, My_String, "node")
|
||||||
|
for s in list.iterate_next(&iter) {
|
||||||
|
fmt.println(s.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
My_String :: struct {
|
||||||
|
node: list.Node,
|
||||||
|
value: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
package container_intrusive_list
|
||||||
@@ -18,11 +18,18 @@ List :: struct {
|
|||||||
tail: ^Node,
|
tail: ^Node,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The list link you must include in your own structure.
|
||||||
Node :: struct {
|
Node :: struct {
|
||||||
prev, next: ^Node,
|
prev, next: ^Node,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Inserts a new element at the front of the list with O(1) time complexity.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- list: The container list
|
||||||
|
- node: The node member of the user-defined element structure
|
||||||
|
*/
|
||||||
push_front :: proc "contextless" (list: ^List, node: ^Node) {
|
push_front :: proc "contextless" (list: ^List, node: ^Node) {
|
||||||
if list.head != nil {
|
if list.head != nil {
|
||||||
list.head.prev = node
|
list.head.prev = node
|
||||||
@@ -33,7 +40,13 @@ push_front :: proc "contextless" (list: ^List, node: ^Node) {
|
|||||||
node.prev, node.next = nil, nil
|
node.prev, node.next = nil, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
Inserts a new element at the back of the list with O(1) time complexity.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- list: The container list
|
||||||
|
- node: The node member of the user-defined element structure
|
||||||
|
*/
|
||||||
push_back :: proc "contextless" (list: ^List, node: ^Node) {
|
push_back :: proc "contextless" (list: ^List, node: ^Node) {
|
||||||
if list.tail != nil {
|
if list.tail != nil {
|
||||||
list.tail.next = node
|
list.tail.next = node
|
||||||
@@ -45,6 +58,13 @@ push_back :: proc "contextless" (list: ^List, node: ^Node) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Removes an element from a list with O(1) time complexity.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- list: The container list
|
||||||
|
- node: The node member of the user-defined element structure to be removed
|
||||||
|
*/
|
||||||
remove :: proc "contextless" (list: ^List, node: ^Node) {
|
remove :: proc "contextless" (list: ^List, node: ^Node) {
|
||||||
if node != nil {
|
if node != nil {
|
||||||
if node.next != nil {
|
if node.next != nil {
|
||||||
@@ -61,7 +81,13 @@ remove :: proc "contextless" (list: ^List, node: ^Node) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
Removes from the given list all elements that satisfy a condition with O(N) time complexity.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- list: The container list
|
||||||
|
- to_erase: The condition procedure. It should return `true` if a node should be removed, `false` otherwise
|
||||||
|
*/
|
||||||
remove_by_proc :: proc(list: ^List, to_erase: proc(^Node) -> bool) {
|
remove_by_proc :: proc(list: ^List, to_erase: proc(^Node) -> bool) {
|
||||||
for node := list.head; node != nil; {
|
for node := list.head; node != nil; {
|
||||||
next := node.next
|
next := node.next
|
||||||
@@ -82,7 +108,13 @@ remove_by_proc :: proc(list: ^List, to_erase: proc(^Node) -> bool) {
|
|||||||
node = next
|
node = next
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
Removes from the given list all elements that satisfy a condition with O(N) time complexity.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- list: The container list
|
||||||
|
- to_erase: The _contextless_ condition procedure. It should return `true` if a node should be removed, `false` otherwise
|
||||||
|
*/
|
||||||
remove_by_proc_contextless :: proc(list: ^List, to_erase: proc "contextless" (^Node) -> bool) {
|
remove_by_proc_contextless :: proc(list: ^List, to_erase: proc "contextless" (^Node) -> bool) {
|
||||||
for node := list.head; node != nil; {
|
for node := list.head; node != nil; {
|
||||||
next := node.next
|
next := node.next
|
||||||
@@ -104,12 +136,26 @@ remove_by_proc_contextless :: proc(list: ^List, to_erase: proc "contextless" (^N
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Checks whether the given list does not contain any element.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- list: The container list
|
||||||
|
|
||||||
|
**Returns** `true` if `list` is empty, `false` otherwise
|
||||||
|
*/
|
||||||
is_empty :: proc "contextless" (list: ^List) -> bool {
|
is_empty :: proc "contextless" (list: ^List) -> bool {
|
||||||
return list.head == nil
|
return list.head == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Removes and returns the element at the front of the list with O(1) time complexity.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- list: The container list
|
||||||
|
|
||||||
|
**Returns** The node member of the user-defined element structure, or `nil` if the list is empty
|
||||||
|
*/
|
||||||
pop_front :: proc "contextless" (list: ^List) -> ^Node {
|
pop_front :: proc "contextless" (list: ^List) -> ^Node {
|
||||||
link := list.head
|
link := list.head
|
||||||
if link == nil {
|
if link == nil {
|
||||||
@@ -130,6 +176,14 @@ pop_front :: proc "contextless" (list: ^List) -> ^Node {
|
|||||||
return link
|
return link
|
||||||
|
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
Removes and returns the element at the back of the list with O(1) time complexity.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- list: The container list
|
||||||
|
|
||||||
|
**Returns** The node member of the user-defined element structure, or `nil` if the list is empty
|
||||||
|
*/
|
||||||
pop_back :: proc "contextless" (list: ^List) -> ^Node {
|
pop_back :: proc "contextless" (list: ^List) -> ^Node {
|
||||||
link := list.tail
|
link := list.tail
|
||||||
if link == nil {
|
if link == nil {
|
||||||
@@ -151,29 +205,102 @@ pop_back :: proc "contextless" (list: ^List) -> ^Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Iterator :: struct($T: typeid) {
|
Iterator :: struct($T: typeid) {
|
||||||
curr: ^Node,
|
curr: ^Node,
|
||||||
offset: uintptr,
|
offset: uintptr,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Creates an iterator pointing at the head of the given list. For an example, see `iterate_next`.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- list: The container list
|
||||||
|
- T: The type of the list's elements
|
||||||
|
- field_name: The name of the node field in the `T` structure
|
||||||
|
|
||||||
|
**Returns** An iterator pointing at the head of `list`
|
||||||
|
|
||||||
|
*/
|
||||||
iterator_head :: proc "contextless" (list: List, $T: typeid, $field_name: string) -> Iterator(T)
|
iterator_head :: proc "contextless" (list: List, $T: typeid, $field_name: string) -> Iterator(T)
|
||||||
where intrinsics.type_has_field(T, field_name),
|
where intrinsics.type_has_field(T, field_name),
|
||||||
intrinsics.type_field_type(T, field_name) == Node {
|
intrinsics.type_field_type(T, field_name) == Node {
|
||||||
return {list.head, offset_of_by_string(T, field_name)}
|
return {list.head, offset_of_by_string(T, field_name)}
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
Creates an iterator pointing at the tail of the given list. For an example, see `iterate_prev`.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- list: The container list
|
||||||
|
- T: The type of the list's elements
|
||||||
|
- field_name: The name of the node field in the `T` structure
|
||||||
|
|
||||||
|
**Returns** An iterator pointing at the tail of `list`
|
||||||
|
|
||||||
|
*/
|
||||||
iterator_tail :: proc "contextless" (list: List, $T: typeid, $field_name: string) -> Iterator(T)
|
iterator_tail :: proc "contextless" (list: List, $T: typeid, $field_name: string) -> Iterator(T)
|
||||||
where intrinsics.type_has_field(T, field_name),
|
where intrinsics.type_has_field(T, field_name),
|
||||||
intrinsics.type_field_type(T, field_name) == Node {
|
intrinsics.type_field_type(T, field_name) == Node {
|
||||||
return {list.tail, offset_of_by_string(T, field_name)}
|
return {list.tail, offset_of_by_string(T, field_name)}
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
Creates an iterator pointing at the specified node of a list.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- node: a list node
|
||||||
|
- T: The type of the list's elements
|
||||||
|
- field_name: The name of the node field in the `T` structure
|
||||||
|
|
||||||
|
**Returns** An iterator pointing at `node`
|
||||||
|
|
||||||
|
*/
|
||||||
iterator_from_node :: proc "contextless" (node: ^Node, $T: typeid, $field_name: string) -> Iterator(T)
|
iterator_from_node :: proc "contextless" (node: ^Node, $T: typeid, $field_name: string) -> Iterator(T)
|
||||||
where intrinsics.type_has_field(T, field_name),
|
where intrinsics.type_has_field(T, field_name),
|
||||||
intrinsics.type_field_type(T, field_name) == Node {
|
intrinsics.type_field_type(T, field_name) == Node {
|
||||||
return {node, offset_of_by_string(T, field_name)}
|
return {node, offset_of_by_string(T, field_name)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Retrieves the next element in a list and advances the iterator.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- it: The iterator
|
||||||
|
|
||||||
|
**Returns**
|
||||||
|
- ptr: The next list element
|
||||||
|
- ok: `true` if the element is valid (the iterator could advance), `false` otherwise
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:container/intrusive/list"
|
||||||
|
|
||||||
|
iterate_next_example :: proc() {
|
||||||
|
l: list.List
|
||||||
|
|
||||||
|
one := My_Struct{value=1}
|
||||||
|
two := My_Struct{value=2}
|
||||||
|
|
||||||
|
list.push_back(&l, &one.node)
|
||||||
|
list.push_back(&l, &two.node)
|
||||||
|
|
||||||
|
it := list.iterator_head(l, My_Struct, "node")
|
||||||
|
for num in list.iterate_next(&it) {
|
||||||
|
fmt.println(num.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
My_Struct :: struct {
|
||||||
|
node : list.Node,
|
||||||
|
value: int,
|
||||||
|
}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
1
|
||||||
|
2
|
||||||
|
|
||||||
|
*/
|
||||||
iterate_next :: proc "contextless" (it: ^Iterator($T)) -> (ptr: ^T, ok: bool) {
|
iterate_next :: proc "contextless" (it: ^Iterator($T)) -> (ptr: ^T, ok: bool) {
|
||||||
node := it.curr
|
node := it.curr
|
||||||
if node == nil {
|
if node == nil {
|
||||||
@@ -183,7 +310,47 @@ iterate_next :: proc "contextless" (it: ^Iterator($T)) -> (ptr: ^T, ok: bool) {
|
|||||||
|
|
||||||
return (^T)(uintptr(node) - it.offset), true
|
return (^T)(uintptr(node) - it.offset), true
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
Retrieves the previous element in a list and recede the iterator.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- it: The iterator
|
||||||
|
|
||||||
|
**Returns**
|
||||||
|
- ptr: The previous list element
|
||||||
|
- ok: `true` if the element is valid (the iterator could recede), `false` otherwise
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:container/intrusive/list"
|
||||||
|
|
||||||
|
iterate_next_example :: proc() {
|
||||||
|
l: list.List
|
||||||
|
|
||||||
|
one := My_Struct{value=1}
|
||||||
|
two := My_Struct{value=2}
|
||||||
|
|
||||||
|
list.push_back(&l, &one.node)
|
||||||
|
list.push_back(&l, &two.node)
|
||||||
|
|
||||||
|
it := list.iterator_tail(l, My_Struct, "node")
|
||||||
|
for num in list.iterate_prev(&it) {
|
||||||
|
fmt.println(num.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
My_Struct :: struct {
|
||||||
|
node : list.Node,
|
||||||
|
value: int,
|
||||||
|
}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
2
|
||||||
|
1
|
||||||
|
|
||||||
|
*/
|
||||||
iterate_prev :: proc "contextless" (it: ^Iterator($T)) -> (ptr: ^T, ok: bool) {
|
iterate_prev :: proc "contextless" (it: ^Iterator($T)) -> (ptr: ^T, ok: bool) {
|
||||||
node := it.curr
|
node := it.curr
|
||||||
if node == nil {
|
if node == nil {
|
||||||
@@ -192,4 +359,4 @@ iterate_prev :: proc "contextless" (it: ^Iterator($T)) -> (ptr: ^T, ok: bool) {
|
|||||||
it.curr = node.prev
|
it.curr = node.prev
|
||||||
|
|
||||||
return (^T)(uintptr(node) - it.offset), true
|
return (^T)(uintptr(node) - it.offset), true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,11 +95,11 @@ front_ptr :: proc(q: ^$Q/Queue($T)) -> ^T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
back :: proc(q: ^$Q/Queue($T)) -> T {
|
back :: proc(q: ^$Q/Queue($T)) -> T {
|
||||||
idx := (q.offset+uint(q.len))%builtin.len(q.data)
|
idx := (q.offset+uint(q.len - 1))%builtin.len(q.data)
|
||||||
return q.data[idx]
|
return q.data[idx]
|
||||||
}
|
}
|
||||||
back_ptr :: proc(q: ^$Q/Queue($T)) -> ^T {
|
back_ptr :: proc(q: ^$Q/Queue($T)) -> ^T {
|
||||||
idx := (q.offset+uint(q.len))%builtin.len(q.data)
|
idx := (q.offset+uint(q.len - 1))%builtin.len(q.data)
|
||||||
return &q.data[idx]
|
return &q.data[idx]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,8 @@ STRIDE :: 4
|
|||||||
|
|
||||||
// Context is a keyed AES (ECB) instance.
|
// Context is a keyed AES (ECB) instance.
|
||||||
Context :: struct {
|
Context :: struct {
|
||||||
_sk_exp: [120]u64,
|
_sk_exp: [120]u64,
|
||||||
_num_rounds: int,
|
_num_rounds: int,
|
||||||
_is_initialized: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// init initializes a context for AES with the provided key.
|
// init initializes a context for AES with the provided key.
|
||||||
@@ -18,13 +17,10 @@ init :: proc(ctx: ^Context, key: []byte) {
|
|||||||
|
|
||||||
ctx._num_rounds = keysched(skey[:], key)
|
ctx._num_rounds = keysched(skey[:], key)
|
||||||
skey_expand(ctx._sk_exp[:], skey[:], ctx._num_rounds)
|
skey_expand(ctx._sk_exp[:], skey[:], ctx._num_rounds)
|
||||||
ctx._is_initialized = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// encrypt_block sets `dst` to `AES-ECB-Encrypt(src)`.
|
// encrypt_block sets `dst` to `AES-ECB-Encrypt(src)`.
|
||||||
encrypt_block :: proc(ctx: ^Context, dst, src: []byte) {
|
encrypt_block :: proc(ctx: ^Context, dst, src: []byte) {
|
||||||
assert(ctx._is_initialized)
|
|
||||||
|
|
||||||
q: [8]u64
|
q: [8]u64
|
||||||
load_blockx1(&q, src)
|
load_blockx1(&q, src)
|
||||||
_encrypt(&q, ctx._sk_exp[:], ctx._num_rounds)
|
_encrypt(&q, ctx._sk_exp[:], ctx._num_rounds)
|
||||||
@@ -33,8 +29,6 @@ encrypt_block :: proc(ctx: ^Context, dst, src: []byte) {
|
|||||||
|
|
||||||
// encrypt_block sets `dst` to `AES-ECB-Decrypt(src)`.
|
// encrypt_block sets `dst` to `AES-ECB-Decrypt(src)`.
|
||||||
decrypt_block :: proc(ctx: ^Context, dst, src: []byte) {
|
decrypt_block :: proc(ctx: ^Context, dst, src: []byte) {
|
||||||
assert(ctx._is_initialized)
|
|
||||||
|
|
||||||
q: [8]u64
|
q: [8]u64
|
||||||
load_blockx1(&q, src)
|
load_blockx1(&q, src)
|
||||||
_decrypt(&q, ctx._sk_exp[:], ctx._num_rounds)
|
_decrypt(&q, ctx._sk_exp[:], ctx._num_rounds)
|
||||||
@@ -43,8 +37,6 @@ decrypt_block :: proc(ctx: ^Context, dst, src: []byte) {
|
|||||||
|
|
||||||
// encrypt_blocks sets `dst` to `AES-ECB-Encrypt(src[0], .. src[n])`.
|
// encrypt_blocks sets `dst` to `AES-ECB-Encrypt(src[0], .. src[n])`.
|
||||||
encrypt_blocks :: proc(ctx: ^Context, dst, src: [][]byte) {
|
encrypt_blocks :: proc(ctx: ^Context, dst, src: [][]byte) {
|
||||||
assert(ctx._is_initialized)
|
|
||||||
|
|
||||||
q: [8]u64 = ---
|
q: [8]u64 = ---
|
||||||
src, dst := src, dst
|
src, dst := src, dst
|
||||||
|
|
||||||
@@ -67,8 +59,6 @@ encrypt_blocks :: proc(ctx: ^Context, dst, src: [][]byte) {
|
|||||||
|
|
||||||
// decrypt_blocks sets dst to `AES-ECB-Decrypt(src[0], .. src[n])`.
|
// decrypt_blocks sets dst to `AES-ECB-Decrypt(src[0], .. src[n])`.
|
||||||
decrypt_blocks :: proc(ctx: ^Context, dst, src: [][]byte) {
|
decrypt_blocks :: proc(ctx: ^Context, dst, src: [][]byte) {
|
||||||
assert(ctx._is_initialized)
|
|
||||||
|
|
||||||
q: [8]u64 = ---
|
q: [8]u64 = ---
|
||||||
src, dst := src, dst
|
src, dst := src, dst
|
||||||
|
|
||||||
|
|||||||
@@ -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-38a.pdf
|
||||||
- https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
|
- https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package aes
|
package aes
|
||||||
|
|
||||||
import "core:crypto/_aes"
|
import "core:crypto/_aes"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package aes
|
package aes
|
||||||
|
|
||||||
|
import "core:bytes"
|
||||||
import "core:crypto/_aes/ct64"
|
import "core:crypto/_aes/ct64"
|
||||||
import "core:encoding/endian"
|
import "core:encoding/endian"
|
||||||
import "core:math/bits"
|
import "core:math/bits"
|
||||||
@@ -37,14 +38,15 @@ init_ctr :: proc(ctx: ^Context_CTR, key, iv: []byte, impl := Implementation.Hard
|
|||||||
xor_bytes_ctr :: proc(ctx: ^Context_CTR, dst, src: []byte) {
|
xor_bytes_ctr :: proc(ctx: ^Context_CTR, dst, src: []byte) {
|
||||||
assert(ctx._is_initialized)
|
assert(ctx._is_initialized)
|
||||||
|
|
||||||
// TODO: Enforcing that dst and src alias exactly or not at all
|
|
||||||
// is a good idea, though odd aliasing should be extremely uncommon.
|
|
||||||
|
|
||||||
src, dst := src, dst
|
src, dst := src, dst
|
||||||
if dst_len := len(dst); dst_len < len(src) {
|
if dst_len := len(dst); dst_len < len(src) {
|
||||||
src = src[:dst_len]
|
src = src[:dst_len]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if bytes.alias_inexactly(dst, src) {
|
||||||
|
panic("crypto/aes: dst and src alias inexactly")
|
||||||
|
}
|
||||||
|
|
||||||
for remaining := len(src); remaining > 0; {
|
for remaining := len(src); remaining > 0; {
|
||||||
// Process multiple blocks at once
|
// Process multiple blocks at once
|
||||||
if ctx._off == BLOCK_SIZE {
|
if ctx._off == BLOCK_SIZE {
|
||||||
@@ -123,8 +125,8 @@ reset_ctr :: proc "contextless" (ctx: ^Context_CTR) {
|
|||||||
ctx._is_initialized = false
|
ctx._is_initialized = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private = "file")
|
||||||
ctr_blocks :: proc(ctx: ^Context_CTR, dst, src: []byte, nr_blocks: int) {
|
ctr_blocks :: proc(ctx: ^Context_CTR, dst, src: []byte, nr_blocks: int) #no_bounds_check {
|
||||||
// Use the optimized hardware implementation if available.
|
// Use the optimized hardware implementation if available.
|
||||||
if _, is_hw := ctx._impl.(Context_Impl_Hardware); is_hw {
|
if _, is_hw := ctx._impl.(Context_Impl_Hardware); is_hw {
|
||||||
ctr_blocks_hw(ctx, dst, src, nr_blocks)
|
ctr_blocks_hw(ctx, dst, src, nr_blocks)
|
||||||
@@ -183,17 +185,17 @@ xor_blocks :: #force_inline proc "contextless" (dst, src: []byte, blocks: [][]by
|
|||||||
// performance of this implementation matters to where that
|
// performance of this implementation matters to where that
|
||||||
// optimization would be worth it, use chacha20poly1305, or a
|
// optimization would be worth it, use chacha20poly1305, or a
|
||||||
// CPU that isn't e-waste.
|
// CPU that isn't e-waste.
|
||||||
if src != nil {
|
#no_bounds_check {
|
||||||
#no_bounds_check {
|
if src != nil {
|
||||||
for i in 0 ..< len(blocks) {
|
for i in 0 ..< len(blocks) {
|
||||||
off := i * BLOCK_SIZE
|
off := i * BLOCK_SIZE
|
||||||
for j in 0 ..< BLOCK_SIZE {
|
for j in 0 ..< BLOCK_SIZE {
|
||||||
blocks[i][j] ~= src[off + j]
|
blocks[i][j] ~= src[off + j]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for i in 0 ..< len(blocks) {
|
||||||
|
copy(dst[i * BLOCK_SIZE:], blocks[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i in 0 ..< len(blocks) {
|
|
||||||
copy(dst[i * BLOCK_SIZE:], blocks[i])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
package aes
|
||||||
|
|
||||||
|
import "core:bytes"
|
||||||
import "core:crypto"
|
import "core:crypto"
|
||||||
import "core:crypto/_aes"
|
import "core:crypto/_aes"
|
||||||
import "core:crypto/_aes/ct64"
|
import "core:crypto/_aes/ct64"
|
||||||
import "core:encoding/endian"
|
import "core:encoding/endian"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
|
||||||
// GCM_NONCE_SIZE is the size of the GCM nonce in bytes.
|
// GCM_NONCE_SIZE is the default size of the GCM nonce in bytes.
|
||||||
GCM_NONCE_SIZE :: 12
|
GCM_NONCE_SIZE :: 12
|
||||||
|
// GCM_NONCE_SIZE_MAX is the maximum size of the GCM nonce in bytes.
|
||||||
|
GCM_NONCE_SIZE_MAX :: 0x2000000000000000 // floor((2^64 - 1) / 8) bits
|
||||||
// GCM_TAG_SIZE is the size of a GCM tag in bytes.
|
// GCM_TAG_SIZE is the size of a GCM tag in bytes.
|
||||||
GCM_TAG_SIZE :: _aes.GHASH_TAG_SIZE
|
GCM_TAG_SIZE :: _aes.GHASH_TAG_SIZE
|
||||||
|
|
||||||
@@ -39,6 +42,9 @@ seal_gcm :: proc(ctx: ^Context_GCM, dst, tag, nonce, aad, plaintext: []byte) {
|
|||||||
if len(dst) != len(plaintext) {
|
if len(dst) != len(plaintext) {
|
||||||
panic("crypto/aes: invalid destination ciphertext size")
|
panic("crypto/aes: invalid destination ciphertext size")
|
||||||
}
|
}
|
||||||
|
if bytes.alias_inexactly(dst, plaintext) {
|
||||||
|
panic("crypto/aes: dst and plaintext alias inexactly")
|
||||||
|
}
|
||||||
|
|
||||||
if impl, is_hw := ctx._impl.(Context_Impl_Hardware); is_hw {
|
if impl, is_hw := ctx._impl.(Context_Impl_Hardware); is_hw {
|
||||||
gcm_seal_hw(&impl, dst, tag, nonce, aad, plaintext)
|
gcm_seal_hw(&impl, dst, tag, nonce, aad, plaintext)
|
||||||
@@ -47,17 +53,19 @@ seal_gcm :: proc(ctx: ^Context_GCM, dst, tag, nonce, aad, plaintext: []byte) {
|
|||||||
|
|
||||||
h: [_aes.GHASH_KEY_SIZE]byte
|
h: [_aes.GHASH_KEY_SIZE]byte
|
||||||
j0: [_aes.GHASH_BLOCK_SIZE]byte
|
j0: [_aes.GHASH_BLOCK_SIZE]byte
|
||||||
|
j0_enc: [_aes.GHASH_BLOCK_SIZE]byte
|
||||||
s: [_aes.GHASH_TAG_SIZE]byte
|
s: [_aes.GHASH_TAG_SIZE]byte
|
||||||
init_ghash_ct64(ctx, &h, &j0, nonce)
|
init_ghash_ct64(ctx, &h, &j0, &j0_enc, nonce)
|
||||||
|
|
||||||
// Note: Our GHASH implementation handles appending padding.
|
// Note: Our GHASH implementation handles appending padding.
|
||||||
ct64.ghash(s[:], h[:], aad)
|
ct64.ghash(s[:], h[:], aad)
|
||||||
gctr_ct64(ctx, dst, &s, plaintext, &h, nonce, true)
|
gctr_ct64(ctx, dst, &s, plaintext, &h, &j0, true)
|
||||||
final_ghash_ct64(&s, &h, &j0, len(aad), len(plaintext))
|
final_ghash_ct64(&s, &h, &j0_enc, len(aad), len(plaintext))
|
||||||
copy(tag, s[:])
|
copy(tag, s[:])
|
||||||
|
|
||||||
mem.zero_explicit(&h, len(h))
|
mem.zero_explicit(&h, len(h))
|
||||||
mem.zero_explicit(&j0, len(j0))
|
mem.zero_explicit(&j0, len(j0))
|
||||||
|
mem.zero_explicit(&j0_enc, len(j0_enc))
|
||||||
}
|
}
|
||||||
|
|
||||||
// open_gcm authenticates the aad and ciphertext, and decrypts the ciphertext,
|
// open_gcm authenticates the aad and ciphertext, and decrypts the ciphertext,
|
||||||
@@ -73,6 +81,9 @@ open_gcm :: proc(ctx: ^Context_GCM, dst, nonce, aad, ciphertext, tag: []byte) ->
|
|||||||
if len(dst) != len(ciphertext) {
|
if len(dst) != len(ciphertext) {
|
||||||
panic("crypto/aes: invalid destination plaintext size")
|
panic("crypto/aes: invalid destination plaintext size")
|
||||||
}
|
}
|
||||||
|
if bytes.alias_inexactly(dst, ciphertext) {
|
||||||
|
panic("crypto/aes: dst and ciphertext alias inexactly")
|
||||||
|
}
|
||||||
|
|
||||||
if impl, is_hw := ctx._impl.(Context_Impl_Hardware); is_hw {
|
if impl, is_hw := ctx._impl.(Context_Impl_Hardware); is_hw {
|
||||||
return gcm_open_hw(&impl, dst, nonce, aad, ciphertext, tag)
|
return gcm_open_hw(&impl, dst, nonce, aad, ciphertext, tag)
|
||||||
@@ -80,12 +91,13 @@ open_gcm :: proc(ctx: ^Context_GCM, dst, nonce, aad, ciphertext, tag: []byte) ->
|
|||||||
|
|
||||||
h: [_aes.GHASH_KEY_SIZE]byte
|
h: [_aes.GHASH_KEY_SIZE]byte
|
||||||
j0: [_aes.GHASH_BLOCK_SIZE]byte
|
j0: [_aes.GHASH_BLOCK_SIZE]byte
|
||||||
|
j0_enc: [_aes.GHASH_BLOCK_SIZE]byte
|
||||||
s: [_aes.GHASH_TAG_SIZE]byte
|
s: [_aes.GHASH_TAG_SIZE]byte
|
||||||
init_ghash_ct64(ctx, &h, &j0, nonce)
|
init_ghash_ct64(ctx, &h, &j0, &j0_enc, nonce)
|
||||||
|
|
||||||
ct64.ghash(s[:], h[:], aad)
|
ct64.ghash(s[:], h[:], aad)
|
||||||
gctr_ct64(ctx, dst, &s, ciphertext, &h, nonce, false)
|
gctr_ct64(ctx, dst, &s, ciphertext, &h, &j0, false)
|
||||||
final_ghash_ct64(&s, &h, &j0, len(aad), len(ciphertext))
|
final_ghash_ct64(&s, &h, &j0_enc, len(aad), len(ciphertext))
|
||||||
|
|
||||||
ok := crypto.compare_constant_time(s[:], tag) == 1
|
ok := crypto.compare_constant_time(s[:], tag) == 1
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -94,6 +106,7 @@ open_gcm :: proc(ctx: ^Context_GCM, dst, nonce, aad, ciphertext, tag: []byte) ->
|
|||||||
|
|
||||||
mem.zero_explicit(&h, len(h))
|
mem.zero_explicit(&h, len(h))
|
||||||
mem.zero_explicit(&j0, len(j0))
|
mem.zero_explicit(&j0, len(j0))
|
||||||
|
mem.zero_explicit(&j0_enc, len(j0_enc))
|
||||||
mem.zero_explicit(&s, len(s))
|
mem.zero_explicit(&s, len(s))
|
||||||
|
|
||||||
return ok
|
return ok
|
||||||
@@ -106,19 +119,14 @@ reset_gcm :: proc "contextless" (ctx: ^Context_GCM) {
|
|||||||
ctx._is_initialized = false
|
ctx._is_initialized = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private = "file")
|
||||||
gcm_validate_common_slice_sizes :: proc(tag, nonce, aad, text: []byte) {
|
gcm_validate_common_slice_sizes :: proc(tag, nonce, aad, text: []byte) {
|
||||||
if len(tag) != GCM_TAG_SIZE {
|
if len(tag) != GCM_TAG_SIZE {
|
||||||
panic("crypto/aes: invalid GCM tag size")
|
panic("crypto/aes: invalid GCM tag size")
|
||||||
}
|
}
|
||||||
|
|
||||||
// The specification supports nonces in the range [1, 2^64) bits
|
// The specification supports nonces in the range [1, 2^64) bits.
|
||||||
// however per NIST SP 800-38D 5.2.1.1:
|
if l := len(nonce); l == 0 || u64(l) >= GCM_NONCE_SIZE_MAX {
|
||||||
//
|
|
||||||
// > For IVs, it is recommended that implementations restrict support
|
|
||||||
// > to the length of 96 bits, to promote interoperability, efficiency,
|
|
||||||
// > and simplicity of design.
|
|
||||||
if len(nonce) != GCM_NONCE_SIZE {
|
|
||||||
panic("crypto/aes: invalid GCM nonce size")
|
panic("crypto/aes: invalid GCM nonce size")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,6 +143,7 @@ init_ghash_ct64 :: proc(
|
|||||||
ctx: ^Context_GCM,
|
ctx: ^Context_GCM,
|
||||||
h: ^[_aes.GHASH_KEY_SIZE]byte,
|
h: ^[_aes.GHASH_KEY_SIZE]byte,
|
||||||
j0: ^[_aes.GHASH_BLOCK_SIZE]byte,
|
j0: ^[_aes.GHASH_BLOCK_SIZE]byte,
|
||||||
|
j0_enc: ^[_aes.GHASH_BLOCK_SIZE]byte,
|
||||||
nonce: []byte,
|
nonce: []byte,
|
||||||
) {
|
) {
|
||||||
impl := &ctx._impl.(ct64.Context)
|
impl := &ctx._impl.(ct64.Context)
|
||||||
@@ -142,12 +151,25 @@ init_ghash_ct64 :: proc(
|
|||||||
// 1. Let H = CIPH(k, 0^128)
|
// 1. Let H = CIPH(k, 0^128)
|
||||||
ct64.encrypt_block(impl, h[:], h[:])
|
ct64.encrypt_block(impl, h[:], h[:])
|
||||||
|
|
||||||
|
// Define a block, J0, as follows:
|
||||||
|
if l := len(nonce); l == GCM_NONCE_SIZE {
|
||||||
|
// if len(IV) = 96, then let J0 = IV || 0^31 || 1
|
||||||
|
copy(j0[:], nonce)
|
||||||
|
j0[_aes.GHASH_BLOCK_SIZE - 1] = 1
|
||||||
|
} else {
|
||||||
|
// If len(IV) != 96, then let s = 128 ceil(len(IV)/128) - len(IV),
|
||||||
|
// and let J0 = GHASHH(IV || 0^(s+64) || ceil(len(IV))^64).
|
||||||
|
ct64.ghash(j0[:], h[:], nonce)
|
||||||
|
|
||||||
|
tmp: [_aes.GHASH_BLOCK_SIZE]byte
|
||||||
|
endian.unchecked_put_u64be(tmp[8:], u64(l) * 8)
|
||||||
|
ct64.ghash(j0[:], h[:], tmp[:])
|
||||||
|
}
|
||||||
|
|
||||||
// ECB encrypt j0, so that we can just XOR with the tag. In theory
|
// ECB encrypt j0, so that we can just XOR with the tag. In theory
|
||||||
// this could be processed along with the final GCTR block, to
|
// this could be processed along with the final GCTR block, to
|
||||||
// potentially save a call to AES-ECB, but... just use AES-NI.
|
// potentially save a call to AES-ECB, but... just use AES-NI.
|
||||||
copy(j0[:], nonce)
|
ct64.encrypt_block(impl, j0_enc[:], j0[:])
|
||||||
j0[_aes.GHASH_BLOCK_SIZE - 1] = 1
|
|
||||||
ct64.encrypt_block(impl, j0[:], j0[:])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private = "file")
|
@(private = "file")
|
||||||
@@ -175,33 +197,27 @@ gctr_ct64 :: proc(
|
|||||||
s: ^[_aes.GHASH_BLOCK_SIZE]byte,
|
s: ^[_aes.GHASH_BLOCK_SIZE]byte,
|
||||||
src: []byte,
|
src: []byte,
|
||||||
h: ^[_aes.GHASH_KEY_SIZE]byte,
|
h: ^[_aes.GHASH_KEY_SIZE]byte,
|
||||||
nonce: []byte,
|
nonce: ^[_aes.GHASH_BLOCK_SIZE]byte,
|
||||||
is_seal: bool,
|
is_seal: bool,
|
||||||
) {
|
) #no_bounds_check {
|
||||||
ct64_inc_ctr32 := #force_inline proc "contextless" (dst: []byte, ctr: u32) -> u32 {
|
ct64_inc_ctr32 := #force_inline proc "contextless" (dst: []byte, ctr: u32) -> u32 {
|
||||||
endian.unchecked_put_u32be(dst[12:], ctr)
|
endian.unchecked_put_u32be(dst[12:], ctr)
|
||||||
return ctr + 1
|
return ctr + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Define a block J_0 as follows:
|
// Setup the counter blocks.
|
||||||
// if len(IV) = 96, then let J0 = IV || 0^31 || 1
|
|
||||||
//
|
|
||||||
// Note: We only support 96 bit IVs.
|
|
||||||
tmp, tmp2: [ct64.STRIDE][BLOCK_SIZE]byte = ---, ---
|
tmp, tmp2: [ct64.STRIDE][BLOCK_SIZE]byte = ---, ---
|
||||||
ctrs, blks: [ct64.STRIDE][]byte = ---, ---
|
ctrs, blks: [ct64.STRIDE][]byte = ---, ---
|
||||||
ctr: u32 = 2
|
ctr := endian.unchecked_get_u32be(nonce[GCM_NONCE_SIZE:]) + 1
|
||||||
for i in 0 ..< ct64.STRIDE {
|
for i in 0 ..< ct64.STRIDE {
|
||||||
// Setup scratch space for the keystream.
|
// Setup scratch space for the keystream.
|
||||||
blks[i] = tmp2[i][:]
|
blks[i] = tmp2[i][:]
|
||||||
|
|
||||||
// Pre-copy the IV to all the counter blocks.
|
// Pre-copy the IV to all the counter blocks.
|
||||||
ctrs[i] = tmp[i][:]
|
ctrs[i] = tmp[i][:]
|
||||||
copy(ctrs[i], nonce)
|
copy(ctrs[i], nonce[:GCM_NONCE_SIZE])
|
||||||
}
|
}
|
||||||
|
|
||||||
// We stitch the GCTR and GHASH operations together, so that only
|
|
||||||
// one pass over the ciphertext is required.
|
|
||||||
|
|
||||||
impl := &ctx._impl.(ct64.Context)
|
impl := &ctx._impl.(ct64.Context)
|
||||||
src, dst := src, dst
|
src, dst := src, dst
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
package aes
|
||||||
|
|
||||||
@(private = "file")
|
@(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
|
package chacha20
|
||||||
|
|
||||||
|
import "core:bytes"
|
||||||
import "core:encoding/endian"
|
import "core:encoding/endian"
|
||||||
import "core:math/bits"
|
import "core:math/bits"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
@@ -121,14 +122,15 @@ seek :: proc(ctx: ^Context, block_nr: u64) {
|
|||||||
xor_bytes :: proc(ctx: ^Context, dst, src: []byte) {
|
xor_bytes :: proc(ctx: ^Context, dst, src: []byte) {
|
||||||
assert(ctx._is_initialized)
|
assert(ctx._is_initialized)
|
||||||
|
|
||||||
// TODO: Enforcing that dst and src alias exactly or not at all
|
|
||||||
// is a good idea, though odd aliasing should be extremely uncommon.
|
|
||||||
|
|
||||||
src, dst := src, dst
|
src, dst := src, dst
|
||||||
if dst_len := len(dst); dst_len < len(src) {
|
if dst_len := len(dst); dst_len < len(src) {
|
||||||
src = src[:dst_len]
|
src = src[:dst_len]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if bytes.alias_inexactly(dst, src) {
|
||||||
|
panic("crypto/chacha20: dst and src alias inexactly")
|
||||||
|
}
|
||||||
|
|
||||||
for remaining := len(src); remaining > 0; {
|
for remaining := len(src); remaining > 0; {
|
||||||
// Process multiple blocks at once
|
// Process multiple blocks at once
|
||||||
if ctx._off == _BLOCK_SIZE {
|
if ctx._off == _BLOCK_SIZE {
|
||||||
|
|||||||
@@ -60,7 +60,11 @@ rand_bytes :: proc (dst: []byte) {
|
|||||||
_rand_bytes(dst)
|
_rand_bytes(dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// random_generator returns a `runtime.Random_Generator` backed by the
|
||||||
|
// system entropy source.
|
||||||
|
//
|
||||||
|
// Support for the system entropy source can be checked with the
|
||||||
|
// `HAS_RAND_BYTES` boolean constant.
|
||||||
random_generator :: proc() -> runtime.Random_Generator {
|
random_generator :: proc() -> runtime.Random_Generator {
|
||||||
return {
|
return {
|
||||||
procedure = proc(data: rawptr, mode: runtime.Random_Generator_Mode, p: []byte) {
|
procedure = proc(data: rawptr, mode: runtime.Random_Generator_Mode, p: []byte) {
|
||||||
@@ -79,4 +83,4 @@ random_generator :: proc() -> runtime.Random_Generator {
|
|||||||
},
|
},
|
||||||
data = nil,
|
data = nil,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ HAS_RAND_BYTES :: true
|
|||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
_rand_bytes :: proc(dst: []byte) {
|
_rand_bytes :: proc(dst: []byte) {
|
||||||
ret := (os.Errno)(win32.BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG))
|
ret := os.Platform_Error(win32.BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG))
|
||||||
if ret != os.ERROR_NONE {
|
if ret != nil {
|
||||||
switch ret {
|
#partial switch ret {
|
||||||
case os.ERROR_INVALID_HANDLE:
|
case os.ERROR_INVALID_HANDLE:
|
||||||
// The handle to the first parameter is invalid.
|
// The handle to the first parameter is invalid.
|
||||||
// This should not happen here, since we explicitly pass nil to it
|
// This should not happen here, since we explicitly pass nil to it
|
||||||
|
|||||||
+5
-13
@@ -16,15 +16,12 @@ Library :: distinct rawptr
|
|||||||
Loads a dynamic library from the filesystem. The paramater `global_symbols` makes the symbols in the loaded
|
Loads a dynamic library from the filesystem. The paramater `global_symbols` makes the symbols in the loaded
|
||||||
library available to resolve references in subsequently loaded libraries.
|
library available to resolve references in subsequently loaded libraries.
|
||||||
|
|
||||||
The paramater `global_symbols` is only used for the platforms `linux`, `darwin`, `freebsd` and `openbsd`.
|
The parameter `global_symbols` is only used for the platforms `linux`, `darwin`, `freebsd` and `openbsd`.
|
||||||
On `windows` this paramater is ignored.
|
On `windows` this paramater is ignored.
|
||||||
|
|
||||||
The underlying behaviour is platform specific.
|
The underlying behaviour is platform specific.
|
||||||
On `linux`, `darwin`, `freebsd` and `openbsd` refer to `dlopen`.
|
On `linux`, `darwin`, `freebsd` and `openbsd` refer to `dlopen`.
|
||||||
On `windows` refer to `LoadLibraryW`.
|
On `windows` refer to `LoadLibraryW`. Also temporarily needs an allocator to convert a string.
|
||||||
|
|
||||||
**Implicit Allocators**
|
|
||||||
`context.temp_allocator`
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
import "core:dynlib"
|
import "core:dynlib"
|
||||||
@@ -79,10 +76,7 @@ Loads the address of a procedure/variable from a dynamic library.
|
|||||||
|
|
||||||
The underlying behaviour is platform specific.
|
The underlying behaviour is platform specific.
|
||||||
On `linux`, `darwin`, `freebsd` and `openbsd` refer to `dlsym`.
|
On `linux`, `darwin`, `freebsd` and `openbsd` refer to `dlsym`.
|
||||||
On `windows` refer to `GetProcAddress`.
|
On `windows` refer to `GetProcAddress`. Also temporarily needs an allocator to convert a string.
|
||||||
|
|
||||||
**Implicit Allocators**
|
|
||||||
`context.temp_allocator`
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
import "core:dynlib"
|
import "core:dynlib"
|
||||||
@@ -177,9 +171,7 @@ initialize_symbols :: proc(
|
|||||||
return count, count > 0
|
return count, count > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Returns an error message for the last failed procedure call.
|
||||||
Returns an error message for the last failed procedure call.
|
|
||||||
*/
|
|
||||||
last_error :: proc() -> string {
|
last_error :: proc() -> string {
|
||||||
return _last_error()
|
return _last_error()
|
||||||
}
|
}
|
||||||
@@ -16,4 +16,4 @@ _symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found
|
|||||||
|
|
||||||
_last_error :: proc() -> string {
|
_last_error :: proc() -> string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@@ -26,4 +26,4 @@ _symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found
|
|||||||
_last_error :: proc() -> string {
|
_last_error :: proc() -> string {
|
||||||
err := os.dlerror()
|
err := os.dlerror()
|
||||||
return "unknown" if err == "" else err
|
return "unknown" if err == "" else err
|
||||||
}
|
}
|
||||||
@@ -4,14 +4,12 @@ package dynlib
|
|||||||
|
|
||||||
import win32 "core:sys/windows"
|
import win32 "core:sys/windows"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
import "base:runtime"
|
|
||||||
import "core:reflect"
|
import "core:reflect"
|
||||||
|
|
||||||
_load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
_load_library :: proc(path: string, global_symbols := false, allocator := context.temp_allocator) -> (Library, bool) {
|
||||||
// NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL
|
// NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL
|
||||||
|
wide_path := win32.utf8_to_wstring(path, allocator)
|
||||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
defer free(wide_path, allocator)
|
||||||
wide_path := win32.utf8_to_wstring(path, context.temp_allocator)
|
|
||||||
handle := cast(Library)win32.LoadLibraryW(wide_path)
|
handle := cast(Library)win32.LoadLibraryW(wide_path)
|
||||||
return handle, handle != nil
|
return handle, handle != nil
|
||||||
}
|
}
|
||||||
@@ -21,9 +19,9 @@ _unload_library :: proc(library: Library) -> bool {
|
|||||||
return bool(ok)
|
return bool(ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
_symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
|
_symbol_address :: proc(library: Library, symbol: string, allocator := context.temp_allocator) -> (ptr: rawptr, found: bool) {
|
||||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
c_str := strings.clone_to_cstring(symbol, allocator)
|
||||||
c_str := strings.clone_to_cstring(symbol, context.temp_allocator)
|
defer delete(c_str, allocator)
|
||||||
ptr = win32.GetProcAddress(cast(win32.HMODULE)library, c_str)
|
ptr = win32.GetProcAddress(cast(win32.HMODULE)library, c_str)
|
||||||
found = ptr != nil
|
found = ptr != nil
|
||||||
return
|
return
|
||||||
@@ -33,4 +31,4 @@ _last_error :: proc() -> string {
|
|||||||
err := win32.System_Error(win32.GetLastError())
|
err := win32.System_Error(win32.GetLastError())
|
||||||
err_msg := reflect.enum_string(err)
|
err_msg := reflect.enum_string(err)
|
||||||
return "unknown" if err_msg == "" else err_msg
|
return "unknown" if err_msg == "" else err_msg
|
||||||
}
|
}
|
||||||
@@ -77,8 +77,11 @@ You can look at the default tags provided for pointers on how these implementati
|
|||||||
Example:
|
Example:
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
|
||||||
import "core:encoding/cbor"
|
import "core:encoding/cbor"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
import "core:reflect"
|
||||||
import "core:time"
|
import "core:time"
|
||||||
|
|
||||||
Possibilities :: union {
|
Possibilities :: union {
|
||||||
@@ -93,9 +96,32 @@ Example:
|
|||||||
ignore_this: ^Data `cbor:"-"`, // Ignored by implementation.
|
ignore_this: ^Data `cbor:"-"`, // Ignored by implementation.
|
||||||
renamed: f32 `cbor:"renamed :)"`, // Renamed when encoded.
|
renamed: f32 `cbor:"renamed :)"`, // Renamed when encoded.
|
||||||
my_union: Possibilities, // Union support.
|
my_union: Possibilities, // Union support.
|
||||||
|
|
||||||
|
my_raw: [8]u32 `cbor_tag:"raw"`, // Custom tag that just writes the value as bytes.
|
||||||
}
|
}
|
||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
|
// Example custom tag implementation that instead of breaking down all parts,
|
||||||
|
// just writes the value as a big byte blob. This is an advanced feature but very powerful.
|
||||||
|
RAW_TAG_NR :: 200
|
||||||
|
cbor.tag_register_number({
|
||||||
|
marshal = proc(_: ^cbor.Tag_Implementation, e: cbor.Encoder, v: any) -> cbor.Marshal_Error {
|
||||||
|
cbor._encode_u8(e.writer, RAW_TAG_NR, .Tag) or_return
|
||||||
|
return cbor.err_conv(cbor._encode_bytes(e, reflect.as_bytes(v)))
|
||||||
|
},
|
||||||
|
unmarshal = proc(_: ^cbor.Tag_Implementation, d: cbor.Decoder, _: cbor.Tag_Number, v: any) -> (cbor.Unmarshal_Error) {
|
||||||
|
hdr := cbor._decode_header(d.reader) or_return
|
||||||
|
maj, add := cbor._header_split(hdr)
|
||||||
|
if maj != .Bytes {
|
||||||
|
return .Bad_Tag_Value
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes := cbor.err_conv(cbor._decode_bytes(d, add, maj)) or_return
|
||||||
|
intrinsics.mem_copy_non_overlapping(v.data, raw_data(bytes), len(bytes))
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}, RAW_TAG_NR, "raw")
|
||||||
|
|
||||||
now := time.Time{_nsec = 1701117968 * 1e9}
|
now := time.Time{_nsec = 1701117968 * 1e9}
|
||||||
|
|
||||||
data := Data{
|
data := Data{
|
||||||
@@ -105,21 +131,22 @@ Example:
|
|||||||
ignore_this = &Data{},
|
ignore_this = &Data{},
|
||||||
renamed = 123123.125,
|
renamed = 123123.125,
|
||||||
my_union = 3,
|
my_union = 3,
|
||||||
|
my_raw = {1=1, 2=2, 3=3},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marshal the struct into binary CBOR.
|
// Marshal the struct into binary CBOR.
|
||||||
binary, err := cbor.marshal(data, cbor.ENCODE_FULLY_DETERMINISTIC)
|
binary, err := cbor.marshal(data, cbor.ENCODE_FULLY_DETERMINISTIC)
|
||||||
assert(err == nil)
|
fmt.assertf(err == nil, "marshal error: %v", err)
|
||||||
defer delete(binary)
|
defer delete(binary)
|
||||||
|
|
||||||
// Decode the binary data into a `cbor.Value`.
|
// Decode the binary data into a `cbor.Value`.
|
||||||
decoded, derr := cbor.decode(string(binary))
|
decoded, derr := cbor.decode(string(binary))
|
||||||
assert(derr == nil)
|
fmt.assertf(derr == nil, "decode error: %v", derr)
|
||||||
defer cbor.destroy(decoded)
|
defer cbor.destroy(decoded)
|
||||||
|
|
||||||
// Turn the CBOR into a human readable representation defined as the diagnostic format in [[RFC 8949 Section 8;https://www.rfc-editor.org/rfc/rfc8949.html#name-diagnostic-notation]].
|
// Turn the CBOR into a human readable representation defined as the diagnostic format in [[RFC 8949 Section 8;https://www.rfc-editor.org/rfc/rfc8949.html#name-diagnostic-notation]].
|
||||||
diagnosis, eerr := cbor.to_diagnostic_format(decoded)
|
diagnosis, eerr := cbor.to_diagnostic_format(decoded)
|
||||||
assert(eerr == nil)
|
fmt.assertf(eerr == nil, "to diagnostic error: %v", eerr)
|
||||||
defer delete(diagnosis)
|
defer delete(diagnosis)
|
||||||
|
|
||||||
fmt.println(diagnosis)
|
fmt.println(diagnosis)
|
||||||
@@ -127,6 +154,7 @@ Example:
|
|||||||
|
|
||||||
Output:
|
Output:
|
||||||
{
|
{
|
||||||
|
"my_raw": 200(h'00001000200030000000000000000000'),
|
||||||
"my_union": 1010([
|
"my_union": 1010([
|
||||||
"int",
|
"int",
|
||||||
3
|
3
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ marshal_into_bytes :: proc(v: any, flags := ENCODE_SMALL, allocator := context.a
|
|||||||
|
|
||||||
defer if err != nil { strings.builder_destroy(&b) }
|
defer if err != nil { strings.builder_destroy(&b) }
|
||||||
|
|
||||||
if err = marshal_into_builder(&b, v, flags, temp_allocator, loc=loc); err != nil {
|
if err = marshal_into_builder(&b, v, flags, temp_allocator); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,20 +63,20 @@ marshal_into_bytes :: proc(v: any, flags := ENCODE_SMALL, allocator := context.a
|
|||||||
|
|
||||||
// Marshals the given value into a CBOR byte stream written to the given builder.
|
// Marshals the given value into a CBOR byte stream written to the given builder.
|
||||||
// See docs on the `marshal_into` proc group for more info.
|
// See docs on the `marshal_into` proc group for more info.
|
||||||
marshal_into_builder :: proc(b: ^strings.Builder, v: any, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator, loc := #caller_location) -> Marshal_Error {
|
marshal_into_builder :: proc(b: ^strings.Builder, v: any, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator) -> Marshal_Error {
|
||||||
return marshal_into_writer(strings.to_writer(b), v, flags, temp_allocator, loc=loc)
|
return marshal_into_writer(strings.to_writer(b), v, flags, temp_allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marshals the given value into a CBOR byte stream written to the given writer.
|
// Marshals the given value into a CBOR byte stream written to the given writer.
|
||||||
// See docs on the `marshal_into` proc group for more info.
|
// See docs on the `marshal_into` proc group for more info.
|
||||||
marshal_into_writer :: proc(w: io.Writer, v: any, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator, loc := #caller_location) -> Marshal_Error {
|
marshal_into_writer :: proc(w: io.Writer, v: any, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator) -> Marshal_Error {
|
||||||
encoder := Encoder{flags, w, temp_allocator}
|
encoder := Encoder{flags, w, temp_allocator}
|
||||||
return marshal_into_encoder(encoder, v, loc=loc)
|
return marshal_into_encoder(encoder, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marshals the given value into a CBOR byte stream written to the given encoder.
|
// Marshals the given value into a CBOR byte stream written to the given encoder.
|
||||||
// See docs on the `marshal_into` proc group for more info.
|
// See docs on the `marshal_into` proc group for more info.
|
||||||
marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (err: Marshal_Error) {
|
marshal_into_encoder :: proc(e: Encoder, v: any) -> (err: Marshal_Error) {
|
||||||
e := e
|
e := e
|
||||||
|
|
||||||
if e.temp_allocator.procedure == nil {
|
if e.temp_allocator.procedure == nil {
|
||||||
@@ -97,11 +97,14 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e
|
|||||||
return impl->marshal(e, v)
|
return impl->marshal(e, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
ti := runtime.type_info_base(type_info_of(v.id))
|
ti := runtime.type_info_core(type_info_of(v.id))
|
||||||
a := any{v.data, ti.id}
|
return _marshal_into_encoder(e, v, ti)
|
||||||
|
}
|
||||||
|
|
||||||
|
_marshal_into_encoder :: proc(e: Encoder, v: any, ti: ^runtime.Type_Info) -> (err: Marshal_Error) {
|
||||||
|
a := any{v.data, ti.id}
|
||||||
#partial switch info in ti.variant {
|
#partial switch info in ti.variant {
|
||||||
case runtime.Type_Info_Named:
|
case runtime.Type_Info_Named, runtime.Type_Info_Enum, runtime.Type_Info_Bit_Field:
|
||||||
unreachable()
|
unreachable()
|
||||||
|
|
||||||
case runtime.Type_Info_Pointer:
|
case runtime.Type_Info_Pointer:
|
||||||
@@ -223,18 +226,38 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e
|
|||||||
}
|
}
|
||||||
|
|
||||||
err_conv(_encode_u64(e, u64(info.count), .Array)) or_return
|
err_conv(_encode_u64(e, u64(info.count), .Array)) or_return
|
||||||
|
|
||||||
|
if impl, ok := _tag_implementations_type[info.elem.id]; ok {
|
||||||
|
for i in 0..<info.count {
|
||||||
|
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
||||||
|
impl->marshal(e, any{rawptr(data), info.elem.id}) or_return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
elem_ti := runtime.type_info_core(type_info_of(info.elem.id))
|
||||||
for i in 0..<info.count {
|
for i in 0..<info.count {
|
||||||
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
||||||
marshal_into(e, any{rawptr(data), info.elem.id}) or_return
|
_marshal_into_encoder(e, any{rawptr(data), info.elem.id}, elem_ti) or_return
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
||||||
case runtime.Type_Info_Enumerated_Array:
|
case runtime.Type_Info_Enumerated_Array:
|
||||||
// index := runtime.type_info_base(info.index).variant.(runtime.Type_Info_Enum)
|
// index := runtime.type_info_base(info.index).variant.(runtime.Type_Info_Enum)
|
||||||
err_conv(_encode_u64(e, u64(info.count), .Array)) or_return
|
err_conv(_encode_u64(e, u64(info.count), .Array)) or_return
|
||||||
|
|
||||||
|
if impl, ok := _tag_implementations_type[info.elem.id]; ok {
|
||||||
|
for i in 0..<info.count {
|
||||||
|
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
||||||
|
impl->marshal(e, any{rawptr(data), info.elem.id}) or_return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
elem_ti := runtime.type_info_core(type_info_of(info.elem.id))
|
||||||
for i in 0..<info.count {
|
for i in 0..<info.count {
|
||||||
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
||||||
marshal_into(e, any{rawptr(data), info.elem.id}) or_return
|
_marshal_into_encoder(e, any{rawptr(data), info.elem.id}, elem_ti) or_return
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -246,9 +269,19 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e
|
|||||||
|
|
||||||
array := (^mem.Raw_Dynamic_Array)(v.data)
|
array := (^mem.Raw_Dynamic_Array)(v.data)
|
||||||
err_conv(_encode_u64(e, u64(array.len), .Array)) or_return
|
err_conv(_encode_u64(e, u64(array.len), .Array)) or_return
|
||||||
|
|
||||||
|
if impl, ok := _tag_implementations_type[info.elem.id]; ok {
|
||||||
|
for i in 0..<array.len {
|
||||||
|
data := uintptr(array.data) + uintptr(i*info.elem_size)
|
||||||
|
impl->marshal(e, any{rawptr(data), info.elem.id}) or_return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
elem_ti := runtime.type_info_core(type_info_of(info.elem.id))
|
||||||
for i in 0..<array.len {
|
for i in 0..<array.len {
|
||||||
data := uintptr(array.data) + uintptr(i*info.elem_size)
|
data := uintptr(array.data) + uintptr(i*info.elem_size)
|
||||||
marshal_into(e, any{rawptr(data), info.elem.id}) or_return
|
_marshal_into_encoder(e, any{rawptr(data), info.elem.id}, elem_ti) or_return
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -260,9 +293,19 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e
|
|||||||
|
|
||||||
array := (^mem.Raw_Slice)(v.data)
|
array := (^mem.Raw_Slice)(v.data)
|
||||||
err_conv(_encode_u64(e, u64(array.len), .Array)) or_return
|
err_conv(_encode_u64(e, u64(array.len), .Array)) or_return
|
||||||
|
|
||||||
|
if impl, ok := _tag_implementations_type[info.elem.id]; ok {
|
||||||
|
for i in 0..<array.len {
|
||||||
|
data := uintptr(array.data) + uintptr(i*info.elem_size)
|
||||||
|
impl->marshal(e, any{rawptr(data), info.elem.id}) or_return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
elem_ti := runtime.type_info_core(type_info_of(info.elem.id))
|
||||||
for i in 0..<array.len {
|
for i in 0..<array.len {
|
||||||
data := uintptr(array.data) + uintptr(i*info.elem_size)
|
data := uintptr(array.data) + uintptr(i*info.elem_size)
|
||||||
marshal_into(e, any{rawptr(data), info.elem.id}) or_return
|
_marshal_into_encoder(e, any{rawptr(data), info.elem.id}, elem_ti) or_return
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -308,7 +351,8 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e
|
|||||||
builder := strings.builder_from_slice(res[:])
|
builder := strings.builder_from_slice(res[:])
|
||||||
e.writer = strings.to_stream(&builder)
|
e.writer = strings.to_stream(&builder)
|
||||||
|
|
||||||
assert(_encode_u64(e, u64(len(str)), .Text) == nil)
|
err := _encode_u64(e, u64(len(str)), .Text)
|
||||||
|
assert(err == nil)
|
||||||
res[9] = u8(len(builder.buf))
|
res[9] = u8(len(builder.buf))
|
||||||
assert(res[9] < 10)
|
assert(res[9] < 10)
|
||||||
return
|
return
|
||||||
@@ -463,7 +507,7 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e
|
|||||||
}
|
}
|
||||||
|
|
||||||
n: u64; {
|
n: u64; {
|
||||||
for _, i in info.names {
|
for _, i in info.names[:info.field_count] {
|
||||||
if field_name(info, i) != "-" {
|
if field_name(info, i) != "-" {
|
||||||
n += 1
|
n += 1
|
||||||
}
|
}
|
||||||
@@ -479,7 +523,7 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e
|
|||||||
entries := make([dynamic]Name, 0, n, e.temp_allocator) or_return
|
entries := make([dynamic]Name, 0, n, e.temp_allocator) or_return
|
||||||
defer delete(entries)
|
defer delete(entries)
|
||||||
|
|
||||||
for _, i in info.names {
|
for _, i in info.names[:info.field_count] {
|
||||||
fname := field_name(info, i)
|
fname := field_name(info, i)
|
||||||
if fname == "-" {
|
if fname == "-" {
|
||||||
continue
|
continue
|
||||||
@@ -497,7 +541,7 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e
|
|||||||
marshal_entry(e, info, v, entry.name, entry.field) or_return
|
marshal_entry(e, info, v, entry.name, entry.field) or_return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for _, i in info.names {
|
for _, i in info.names[:info.field_count] {
|
||||||
fname := field_name(info, i)
|
fname := field_name(info, i)
|
||||||
if fname == "-" {
|
if fname == "-" {
|
||||||
continue
|
continue
|
||||||
@@ -542,9 +586,6 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e
|
|||||||
|
|
||||||
return marshal_into(e, any{v.data, vti.id})
|
return marshal_into(e, any{v.data, vti.id})
|
||||||
|
|
||||||
case runtime.Type_Info_Enum:
|
|
||||||
return marshal_into(e, any{v.data, info.base.id})
|
|
||||||
|
|
||||||
case runtime.Type_Info_Bit_Set:
|
case runtime.Type_Info_Bit_Set:
|
||||||
// Store bit_set as big endian just like the protocol.
|
// Store bit_set as big endian just like the protocol.
|
||||||
do_byte_swap := !reflect.bit_set_is_big_endian(v)
|
do_byte_swap := !reflect.bit_set_is_big_endian(v)
|
||||||
|
|||||||
@@ -96,7 +96,8 @@ _unmarshal_value :: proc(d: Decoder, v: any, hdr: Header, allocator := context.a
|
|||||||
ti = reflect.type_info_base(variant)
|
ti = reflect.type_info_base(variant)
|
||||||
if !reflect.is_pointer_internally(variant) {
|
if !reflect.is_pointer_internally(variant) {
|
||||||
tag := any{rawptr(uintptr(v.data) + u.tag_offset), u.tag_type.id}
|
tag := any{rawptr(uintptr(v.data) + u.tag_offset), u.tag_type.id}
|
||||||
assert(_assign_int(tag, 1))
|
assigned := _assign_int(tag, 1)
|
||||||
|
assert(assigned)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -520,9 +521,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
|||||||
return
|
return
|
||||||
|
|
||||||
case reflect.Type_Info_Array:
|
case reflect.Type_Info_Array:
|
||||||
_, scap := err_conv(_decode_len_container(d, add)) or_return
|
length, _ := err_conv(_decode_len_container(d, add)) or_return
|
||||||
length := min(scap, t.count)
|
|
||||||
|
|
||||||
if length > t.count {
|
if length > t.count {
|
||||||
return _unsupported(v, hdr)
|
return _unsupported(v, hdr)
|
||||||
}
|
}
|
||||||
@@ -534,9 +533,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
|||||||
return
|
return
|
||||||
|
|
||||||
case reflect.Type_Info_Enumerated_Array:
|
case reflect.Type_Info_Enumerated_Array:
|
||||||
_, scap := err_conv(_decode_len_container(d, add)) or_return
|
length, _ := err_conv(_decode_len_container(d, add)) or_return
|
||||||
length := min(scap, t.count)
|
|
||||||
|
|
||||||
if length > t.count {
|
if length > t.count {
|
||||||
return _unsupported(v, hdr)
|
return _unsupported(v, hdr)
|
||||||
}
|
}
|
||||||
@@ -548,9 +545,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
|||||||
return
|
return
|
||||||
|
|
||||||
case reflect.Type_Info_Complex:
|
case reflect.Type_Info_Complex:
|
||||||
_, scap := err_conv(_decode_len_container(d, add)) or_return
|
length, _ := err_conv(_decode_len_container(d, add)) or_return
|
||||||
length := min(scap, 2)
|
|
||||||
|
|
||||||
if length > 2 {
|
if length > 2 {
|
||||||
return _unsupported(v, hdr)
|
return _unsupported(v, hdr)
|
||||||
}
|
}
|
||||||
@@ -570,9 +565,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
|||||||
return
|
return
|
||||||
|
|
||||||
case reflect.Type_Info_Quaternion:
|
case reflect.Type_Info_Quaternion:
|
||||||
_, scap := err_conv(_decode_len_container(d, add)) or_return
|
length, _ := err_conv(_decode_len_container(d, add)) or_return
|
||||||
length := min(scap, 4)
|
|
||||||
|
|
||||||
if length > 4 {
|
if length > 4 {
|
||||||
return _unsupported(v, hdr)
|
return _unsupported(v, hdr)
|
||||||
}
|
}
|
||||||
@@ -626,14 +619,14 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header,
|
|||||||
|
|
||||||
#partial switch t in ti.variant {
|
#partial switch t in ti.variant {
|
||||||
case reflect.Type_Info_Struct:
|
case reflect.Type_Info_Struct:
|
||||||
if t.is_raw_union {
|
if .raw_union in t.flags {
|
||||||
return _unsupported(v, hdr)
|
return _unsupported(v, hdr)
|
||||||
}
|
}
|
||||||
|
|
||||||
length, _ := err_conv(_decode_len_container(d, add)) or_return
|
length, _ := err_conv(_decode_len_container(d, add)) or_return
|
||||||
unknown := length == -1
|
unknown := length == -1
|
||||||
fields := reflect.struct_fields_zipped(ti.id)
|
fields := reflect.struct_fields_zipped(ti.id)
|
||||||
|
|
||||||
for idx := 0; idx < len(fields) && (unknown || idx < length); idx += 1 {
|
for idx := 0; idx < len(fields) && (unknown || idx < length); idx += 1 {
|
||||||
// Decode key, keys can only be strings.
|
// Decode key, keys can only be strings.
|
||||||
key: string
|
key: string
|
||||||
@@ -646,7 +639,7 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header,
|
|||||||
key = keyv
|
key = keyv
|
||||||
}
|
}
|
||||||
defer delete(key, context.temp_allocator)
|
defer delete(key, context.temp_allocator)
|
||||||
|
|
||||||
// Find matching field.
|
// Find matching field.
|
||||||
use_field_idx := -1
|
use_field_idx := -1
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -38,9 +38,9 @@ iterate_csv_from_stream :: proc(filename: string) {
|
|||||||
r.reuse_record_buffer = true // Without it you have to each of the fields within it
|
r.reuse_record_buffer = true // Without it you have to each of the fields within it
|
||||||
defer csv.reader_destroy(&r)
|
defer csv.reader_destroy(&r)
|
||||||
|
|
||||||
handle, errno := os.open(filename)
|
handle, err := os.open(filename)
|
||||||
if errno != os.ERROR_NONE {
|
if err != nil {
|
||||||
fmt.printfln("Error opening file: %v", filename)
|
fmt.eprintfln("Error opening file: %v", filename)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer os.close(handle)
|
defer os.close(handle)
|
||||||
|
|||||||
@@ -82,15 +82,17 @@ Map :: distinct map[string]map[string]string
|
|||||||
|
|
||||||
load_map_from_string :: proc(src: string, allocator: runtime.Allocator, options := DEFAULT_OPTIONS) -> (m: Map, err: runtime.Allocator_Error) {
|
load_map_from_string :: proc(src: string, allocator: runtime.Allocator, options := DEFAULT_OPTIONS) -> (m: Map, err: runtime.Allocator_Error) {
|
||||||
unquote :: proc(val: string) -> (string, runtime.Allocator_Error) {
|
unquote :: proc(val: string) -> (string, runtime.Allocator_Error) {
|
||||||
v, allocated, ok := strconv.unquote_string(val)
|
if len(val) > 0 && (val[0] == '"' || val[0] == '\'') {
|
||||||
if !ok {
|
v, allocated, ok := strconv.unquote_string(val)
|
||||||
return strings.clone(val)
|
if !ok {
|
||||||
|
return strings.clone(val)
|
||||||
|
}
|
||||||
|
if allocated {
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
return strings.clone(v), nil
|
||||||
}
|
}
|
||||||
if allocated {
|
return strings.clone(val)
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
return strings.clone(v)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
context.allocator = allocator
|
context.allocator = allocator
|
||||||
@@ -121,7 +123,7 @@ load_map_from_path :: proc(path: string, allocator: runtime.Allocator, options :
|
|||||||
data := os.read_entire_file(path, allocator) or_return
|
data := os.read_entire_file(path, allocator) or_return
|
||||||
defer delete(data, allocator)
|
defer delete(data, allocator)
|
||||||
m, err = load_map_from_string(string(data), allocator, options)
|
m, err = load_map_from_string(string(data), allocator, options)
|
||||||
ok = err != nil
|
ok = err == nil
|
||||||
defer if !ok {
|
defer if !ok {
|
||||||
delete_map(m)
|
delete_map(m)
|
||||||
}
|
}
|
||||||
@@ -142,6 +144,7 @@ delete_map :: proc(m: Map) {
|
|||||||
delete(value, allocator)
|
delete(value, allocator)
|
||||||
}
|
}
|
||||||
delete(section)
|
delete(section)
|
||||||
|
delete(pairs)
|
||||||
}
|
}
|
||||||
delete(m)
|
delete(m)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,38 +100,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
|||||||
|
|
||||||
case runtime.Type_Info_Integer:
|
case runtime.Type_Info_Integer:
|
||||||
buf: [40]byte
|
buf: [40]byte
|
||||||
u: u128
|
u := cast_any_int_to_u128(a)
|
||||||
switch i in a {
|
|
||||||
case i8: u = u128(i)
|
|
||||||
case i16: u = u128(i)
|
|
||||||
case i32: u = u128(i)
|
|
||||||
case i64: u = u128(i)
|
|
||||||
case i128: u = u128(i)
|
|
||||||
case int: u = u128(i)
|
|
||||||
case u8: u = u128(i)
|
|
||||||
case u16: u = u128(i)
|
|
||||||
case u32: u = u128(i)
|
|
||||||
case u64: u = u128(i)
|
|
||||||
case u128: u = u128(i)
|
|
||||||
case uint: u = u128(i)
|
|
||||||
case uintptr: u = u128(i)
|
|
||||||
|
|
||||||
case i16le: u = u128(i)
|
|
||||||
case i32le: u = u128(i)
|
|
||||||
case i64le: u = u128(i)
|
|
||||||
case u16le: u = u128(i)
|
|
||||||
case u32le: u = u128(i)
|
|
||||||
case u64le: u = u128(i)
|
|
||||||
case u128le: u = u128(i)
|
|
||||||
|
|
||||||
case i16be: u = u128(i)
|
|
||||||
case i32be: u = u128(i)
|
|
||||||
case i64be: u = u128(i)
|
|
||||||
case u16be: u = u128(i)
|
|
||||||
case u32be: u = u128(i)
|
|
||||||
case u64be: u = u128(i)
|
|
||||||
case u128be: u = u128(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
s: string
|
s: string
|
||||||
|
|
||||||
@@ -310,7 +279,12 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
|||||||
case cstring: name = string(s)
|
case cstring: name = string(s)
|
||||||
}
|
}
|
||||||
opt_write_key(w, opt, name) or_return
|
opt_write_key(w, opt, name) or_return
|
||||||
|
case runtime.Type_Info_Integer:
|
||||||
|
buf: [40]byte
|
||||||
|
u := cast_any_int_to_u128(ka)
|
||||||
|
name = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*kti.size, "0123456789", nil)
|
||||||
|
|
||||||
|
opt_write_key(w, opt, name) or_return
|
||||||
case: return .Unsupported_Type
|
case: return .Unsupported_Type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -406,10 +380,15 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
|||||||
ti := runtime.type_info_base(type_info_of(v.id))
|
ti := runtime.type_info_base(type_info_of(v.id))
|
||||||
info := ti.variant.(runtime.Type_Info_Struct)
|
info := ti.variant.(runtime.Type_Info_Struct)
|
||||||
first_iteration := true
|
first_iteration := true
|
||||||
for name, i in info.names {
|
for name, i in info.names[:info.field_count] {
|
||||||
omitempty := false
|
omitempty := false
|
||||||
|
|
||||||
json_name, extra := json_name_from_tag_value(reflect.struct_tag_get(reflect.Struct_Tag(info.tags[i]), "json"))
|
json_name, extra := json_name_from_tag_value(reflect.struct_tag_get(reflect.Struct_Tag(info.tags[i]), "json"))
|
||||||
|
|
||||||
|
if json_name == "-" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
for flag in strings.split_iterator(&extra, ",") {
|
for flag in strings.split_iterator(&extra, ",") {
|
||||||
switch flag {
|
switch flag {
|
||||||
case "omitempty":
|
case "omitempty":
|
||||||
@@ -657,3 +636,41 @@ opt_write_indentation :: proc(w: io.Writer, opt: ^Marshal_Options) -> (err: io.E
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
cast_any_int_to_u128 :: proc(any_int_value: any) -> u128 {
|
||||||
|
u: u128 = 0
|
||||||
|
switch i in any_int_value {
|
||||||
|
case i8: u = u128(i)
|
||||||
|
case i16: u = u128(i)
|
||||||
|
case i32: u = u128(i)
|
||||||
|
case i64: u = u128(i)
|
||||||
|
case i128: u = u128(i)
|
||||||
|
case int: u = u128(i)
|
||||||
|
case u8: u = u128(i)
|
||||||
|
case u16: u = u128(i)
|
||||||
|
case u32: u = u128(i)
|
||||||
|
case u64: u = u128(i)
|
||||||
|
case u128: u = u128(i)
|
||||||
|
case uint: u = u128(i)
|
||||||
|
case uintptr: u = u128(i)
|
||||||
|
|
||||||
|
case i16le: u = u128(i)
|
||||||
|
case i32le: u = u128(i)
|
||||||
|
case i64le: u = u128(i)
|
||||||
|
case u16le: u = u128(i)
|
||||||
|
case u32le: u = u128(i)
|
||||||
|
case u64le: u = u128(i)
|
||||||
|
case u128le: u = u128(i)
|
||||||
|
|
||||||
|
case i16be: u = u128(i)
|
||||||
|
case i32be: u = u128(i)
|
||||||
|
case i64be: u = u128(i)
|
||||||
|
case u16be: u = u128(i)
|
||||||
|
case u32be: u = u128(i)
|
||||||
|
case u64be: u = u128(i)
|
||||||
|
case u128be: u = u128(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return u
|
||||||
|
}
|
||||||
@@ -363,12 +363,11 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
|||||||
}
|
}
|
||||||
|
|
||||||
v := v
|
v := v
|
||||||
v = reflect.any_base(v)
|
ti := reflect.type_info_base(type_info_of(v.id))
|
||||||
ti := type_info_of(v.id)
|
|
||||||
|
|
||||||
#partial switch t in ti.variant {
|
#partial switch t in ti.variant {
|
||||||
case reflect.Type_Info_Struct:
|
case reflect.Type_Info_Struct:
|
||||||
if t.is_raw_union {
|
if .raw_union in t.flags {
|
||||||
return UNSUPPORTED_TYPE
|
return UNSUPPORTED_TYPE
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -475,7 +474,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
|||||||
}
|
}
|
||||||
|
|
||||||
case reflect.Type_Info_Map:
|
case reflect.Type_Info_Map:
|
||||||
if !reflect.is_string(t.key) {
|
if !reflect.is_string(t.key) && !reflect.is_integer(t.key) {
|
||||||
return UNSUPPORTED_TYPE
|
return UNSUPPORTED_TYPE
|
||||||
}
|
}
|
||||||
raw_map := (^mem.Raw_Map)(v.data)
|
raw_map := (^mem.Raw_Map)(v.data)
|
||||||
@@ -492,25 +491,39 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
|||||||
key, _ := parse_object_key(p, p.allocator)
|
key, _ := parse_object_key(p, p.allocator)
|
||||||
unmarshal_expect_token(p, .Colon)
|
unmarshal_expect_token(p, .Colon)
|
||||||
|
|
||||||
|
|
||||||
mem.zero_slice(elem_backing)
|
mem.zero_slice(elem_backing)
|
||||||
if uerr := unmarshal_value(p, map_backing_value); uerr != nil {
|
if uerr := unmarshal_value(p, map_backing_value); uerr != nil {
|
||||||
delete(key, p.allocator)
|
delete(key, p.allocator)
|
||||||
return uerr
|
return uerr
|
||||||
}
|
}
|
||||||
|
|
||||||
key_ptr := rawptr(&key)
|
key_ptr: rawptr
|
||||||
|
|
||||||
key_cstr: cstring
|
#partial switch tk in t.key.variant {
|
||||||
if reflect.is_cstring(t.key) {
|
case runtime.Type_Info_String:
|
||||||
key_cstr = cstring(raw_data(key))
|
key_ptr = rawptr(&key)
|
||||||
key_ptr = &key_cstr
|
key_cstr: cstring
|
||||||
|
if reflect.is_cstring(t.key) {
|
||||||
|
key_cstr = cstring(raw_data(key))
|
||||||
|
key_ptr = &key_cstr
|
||||||
|
}
|
||||||
|
case runtime.Type_Info_Integer:
|
||||||
|
i, ok := strconv.parse_i128(key)
|
||||||
|
if !ok { return UNSUPPORTED_TYPE }
|
||||||
|
key_ptr = rawptr(&i)
|
||||||
|
case: return UNSUPPORTED_TYPE
|
||||||
}
|
}
|
||||||
|
|
||||||
set_ptr := runtime.__dynamic_map_set_without_hash(raw_map, t.map_info, key_ptr, map_backing_value.data)
|
set_ptr := runtime.__dynamic_map_set_without_hash(raw_map, t.map_info, key_ptr, map_backing_value.data)
|
||||||
if set_ptr == nil {
|
if set_ptr == nil {
|
||||||
delete(key, p.allocator)
|
delete(key, p.allocator)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// there's no need to keep string value on the heap, since it was copied into map
|
||||||
|
if reflect.is_integer(t.key) {
|
||||||
|
delete(key, p.allocator)
|
||||||
|
}
|
||||||
|
|
||||||
if parse_comma(p) {
|
if parse_comma(p) {
|
||||||
break map_loop
|
break map_loop
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ error :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
|
|||||||
t.error_count += 1
|
t.error_count += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
advance_rune :: proc(t: ^Tokenizer) {
|
advance_rune :: proc(t: ^Tokenizer) {
|
||||||
#no_bounds_check {
|
#no_bounds_check {
|
||||||
/*
|
/*
|
||||||
@@ -170,7 +170,7 @@ peek_byte :: proc(t: ^Tokenizer, offset := 0) -> byte {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
skip_whitespace :: proc(t: ^Tokenizer) {
|
skip_whitespace :: proc(t: ^Tokenizer) {
|
||||||
for {
|
for {
|
||||||
switch t.ch {
|
switch t.ch {
|
||||||
@@ -182,7 +182,7 @@ skip_whitespace :: proc(t: ^Tokenizer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
is_letter :: proc(r: rune) -> bool {
|
is_letter :: proc(r: rune) -> bool {
|
||||||
if r < utf8.RUNE_SELF {
|
if r < utf8.RUNE_SELF {
|
||||||
switch r {
|
switch r {
|
||||||
@@ -296,7 +296,7 @@ skip_cdata :: proc(t: ^Tokenizer) -> (err: Error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
scan_string :: proc(t: ^Tokenizer, offset: int, close: rune = '<', consume_close := false, multiline := true) -> (value: string, err: Error) {
|
scan_string :: proc(t: ^Tokenizer, offset: int, close: rune = '<', consume_close := false, multiline := true) -> (value: string, err: Error) {
|
||||||
err = .None
|
err = .None
|
||||||
|
|
||||||
@@ -414,4 +414,4 @@ scan :: proc(t: ^Tokenizer, multiline_string := false) -> Token {
|
|||||||
lit = string(t.src[offset : t.offset])
|
lit = string(t.src[offset : t.offset])
|
||||||
}
|
}
|
||||||
return Token{kind, lit, pos}
|
return Token{kind, lit, pos}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ Parse_Error :: struct {
|
|||||||
// Provides more granular information than what just a string could hold.
|
// Provides more granular information than what just a string could hold.
|
||||||
Open_File_Error :: struct {
|
Open_File_Error :: struct {
|
||||||
filename: string,
|
filename: string,
|
||||||
errno: os.Errno,
|
errno: os.Error,
|
||||||
mode: int,
|
mode: int,
|
||||||
perms: int,
|
perms: int,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import "core:reflect"
|
|||||||
|
|
||||||
// Push a positional argument onto a data struct, checking for specified
|
// Push a positional argument onto a data struct, checking for specified
|
||||||
// positionals first before adding it to a fallback field.
|
// positionals first before adding it to a fallback field.
|
||||||
@(optimization_mode="size")
|
@(optimization_mode="favor_size")
|
||||||
push_positional :: #force_no_inline proc (model: ^$T, parser: ^Parser, arg: string) -> (error: Error) {
|
push_positional :: #force_no_inline proc (model: ^$T, parser: ^Parser, arg: string) -> (error: Error) {
|
||||||
if bit_array.get(&parser.filled_pos, parser.filled_pos.max_index) {
|
if bit_array.get(&parser.filled_pos, parser.filled_pos.max_index) {
|
||||||
// The max index is set, which means we're out of space.
|
// The max index is set, which means we're out of space.
|
||||||
@@ -74,7 +74,7 @@ register_field :: proc(parser: ^Parser, field: reflect.Struct_Field, index: int)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set a `-flag` argument, Odin-style.
|
// Set a `-flag` argument, Odin-style.
|
||||||
@(optimization_mode="size")
|
@(optimization_mode="favor_size")
|
||||||
set_odin_flag :: proc(model: ^$T, parser: ^Parser, name: string) -> (error: Error) {
|
set_odin_flag :: proc(model: ^$T, parser: ^Parser, name: string) -> (error: Error) {
|
||||||
// We make a special case for help requests.
|
// We make a special case for help requests.
|
||||||
switch name {
|
switch name {
|
||||||
@@ -100,7 +100,7 @@ set_odin_flag :: proc(model: ^$T, parser: ^Parser, name: string) -> (error: Erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set a `-flag` argument, UNIX-style.
|
// Set a `-flag` argument, UNIX-style.
|
||||||
@(optimization_mode="size")
|
@(optimization_mode="favor_size")
|
||||||
set_unix_flag :: proc(model: ^$T, parser: ^Parser, name: string) -> (future_args: int, error: Error) {
|
set_unix_flag :: proc(model: ^$T, parser: ^Parser, name: string) -> (future_args: int, error: Error) {
|
||||||
// We make a special case for help requests.
|
// We make a special case for help requests.
|
||||||
switch name {
|
switch name {
|
||||||
@@ -137,7 +137,7 @@ set_unix_flag :: proc(model: ^$T, parser: ^Parser, name: string) -> (future_args
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set a `-flag:option` argument.
|
// Set a `-flag:option` argument.
|
||||||
@(optimization_mode="size")
|
@(optimization_mode="favor_size")
|
||||||
set_option :: proc(model: ^$T, parser: ^Parser, name, option: string) -> (error: Error) {
|
set_option :: proc(model: ^$T, parser: ^Parser, name, option: string) -> (error: Error) {
|
||||||
field, index := get_field_by_name(model, name) or_return
|
field, index := get_field_by_name(model, name) or_return
|
||||||
|
|
||||||
@@ -176,7 +176,7 @@ set_option :: proc(model: ^$T, parser: ^Parser, name, option: string) -> (error:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set a `-map:key=value` argument.
|
// Set a `-map:key=value` argument.
|
||||||
@(optimization_mode="size")
|
@(optimization_mode="favor_size")
|
||||||
set_key_value :: proc(model: ^$T, parser: ^Parser, name, key, value: string) -> (error: Error) {
|
set_key_value :: proc(model: ^$T, parser: ^Parser, name, key, value: string) -> (error: Error) {
|
||||||
field, index := get_field_by_name(model, name) or_return
|
field, index := get_field_by_name(model, name) or_return
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import "core:strings"
|
|||||||
@require import "core:time/datetime"
|
@require import "core:time/datetime"
|
||||||
import "core:unicode/utf8"
|
import "core:unicode/utf8"
|
||||||
|
|
||||||
@(optimization_mode="size")
|
@(optimization_mode="favor_size")
|
||||||
parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info: ^runtime.Type_Info) -> bool {
|
parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info: ^runtime.Type_Info) -> bool {
|
||||||
bounded_int :: proc(value, min, max: i128) -> (result: i128, ok: bool) {
|
bounded_int :: proc(value, min, max: i128) -> (result: i128, ok: bool) {
|
||||||
return value, min <= value && value <= max
|
return value, min <= value && value <= max
|
||||||
@@ -202,7 +202,7 @@ parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info:
|
|||||||
// especially with files.
|
// especially with files.
|
||||||
//
|
//
|
||||||
// We want to provide as informative as an error as we can.
|
// We want to provide as informative as an error as we can.
|
||||||
@(optimization_mode="size", disabled=NO_CORE_NAMED_TYPES)
|
@(optimization_mode="favor_size", disabled=NO_CORE_NAMED_TYPES)
|
||||||
parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type: typeid, arg_tag: string, out_error: ^Error) {
|
parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type: typeid, arg_tag: string, out_error: ^Error) {
|
||||||
// Core types currently supported:
|
// Core types currently supported:
|
||||||
//
|
//
|
||||||
@@ -254,8 +254,8 @@ parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type:
|
|||||||
}
|
}
|
||||||
|
|
||||||
handle, errno := os.open(str, mode, perms)
|
handle, errno := os.open(str, mode, perms)
|
||||||
if errno != 0 {
|
if errno != nil {
|
||||||
// NOTE(Feoramund): os.Errno is system-dependent, and there's
|
// NOTE(Feoramund): os.Error is system-dependent, and there's
|
||||||
// currently no good way to translate them all into strings.
|
// currently no good way to translate them all into strings.
|
||||||
//
|
//
|
||||||
// The upcoming `os2` package will hopefully solve this.
|
// The upcoming `os2` package will hopefully solve this.
|
||||||
@@ -320,7 +320,7 @@ parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="size")
|
@(optimization_mode="favor_size")
|
||||||
set_unbounded_integer_by_type :: proc(ptr: rawptr, value: $T, data_type: typeid) where intrinsics.type_is_integer(T) {
|
set_unbounded_integer_by_type :: proc(ptr: rawptr, value: $T, data_type: typeid) where intrinsics.type_is_integer(T) {
|
||||||
switch data_type {
|
switch data_type {
|
||||||
case i8: (^i8) (ptr)^ = cast(i8) value
|
case i8: (^i8) (ptr)^ = cast(i8) value
|
||||||
@@ -367,7 +367,7 @@ set_unbounded_integer_by_type :: proc(ptr: rawptr, value: $T, data_type: typeid)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="size")
|
@(optimization_mode="favor_size")
|
||||||
parse_and_set_pointer_by_type :: proc(ptr: rawptr, str: string, type_info: ^runtime.Type_Info, arg_tag: string) -> (error: Error) {
|
parse_and_set_pointer_by_type :: proc(ptr: rawptr, str: string, type_info: ^runtime.Type_Info, arg_tag: string) -> (error: Error) {
|
||||||
#partial switch specific_type_info in type_info.variant {
|
#partial switch specific_type_info in type_info.variant {
|
||||||
case runtime.Type_Info_Named:
|
case runtime.Type_Info_Named:
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ package flags
|
|||||||
@require import "core:strings"
|
@require import "core:strings"
|
||||||
|
|
||||||
// This proc is used to assert that `T` meets the expectations of the library.
|
// This proc is used to assert that `T` meets the expectations of the library.
|
||||||
@(optimization_mode="size", disabled=ODIN_DISABLE_ASSERT)
|
@(optimization_mode="favor_size", disabled=ODIN_DISABLE_ASSERT)
|
||||||
validate_structure :: proc(model_type: $T, style: Parsing_Style, loc := #caller_location) {
|
validate_structure :: proc(model_type: $T, style: Parsing_Style, loc := #caller_location) {
|
||||||
positionals_assigned_so_far: bit_array.Bit_Array
|
positionals_assigned_so_far: bit_array.Bit_Array
|
||||||
defer bit_array.destroy(&positionals_assigned_so_far)
|
defer bit_array.destroy(&positionals_assigned_so_far)
|
||||||
@@ -162,7 +162,7 @@ validate_structure :: proc(model_type: $T, style: Parsing_Style, loc := #caller_
|
|||||||
|
|
||||||
// Validate that all the required arguments are set and that the set arguments
|
// Validate that all the required arguments are set and that the set arguments
|
||||||
// are up to the program's expectations.
|
// are up to the program's expectations.
|
||||||
@(optimization_mode="size")
|
@(optimization_mode="favor_size")
|
||||||
validate_arguments :: proc(model: ^$T, parser: ^Parser) -> Error {
|
validate_arguments :: proc(model: ^$T, parser: ^Parser) -> Error {
|
||||||
check_fields: for field, index in reflect.struct_fields_zipped(T) {
|
check_fields: for field, index in reflect.struct_fields_zipped(T) {
|
||||||
was_set := bit_array.get(&parser.fields_set, index)
|
was_set := bit_array.get(&parser.fields_set, index)
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ Inputs:
|
|||||||
Returns:
|
Returns:
|
||||||
- error: A union of errors; parsing, file open, a help request, or validation.
|
- error: A union of errors; parsing, file open, a help request, or validation.
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="size")
|
@(optimization_mode="favor_size")
|
||||||
parse :: proc(
|
parse :: proc(
|
||||||
model: ^$T,
|
model: ^$T,
|
||||||
args: []string,
|
args: []string,
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ Inputs:
|
|||||||
- program: The name of the program, usually the first argument to `os.args`.
|
- program: The name of the program, usually the first argument to `os.args`.
|
||||||
- style: The argument parsing style, required to show flags in the proper style.
|
- style: The argument parsing style, required to show flags in the proper style.
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="size")
|
@(optimization_mode="favor_size")
|
||||||
write_usage :: proc(out: io.Writer, data_type: typeid, program: string = "", style: Parsing_Style = .Odin) {
|
write_usage :: proc(out: io.Writer, data_type: typeid, program: string = "", style: Parsing_Style = .Odin) {
|
||||||
// All flags get their tags parsed so they can be reasoned about later.
|
// All flags get their tags parsed so they can be reasoned about later.
|
||||||
Flag :: struct {
|
Flag :: struct {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ Inputs:
|
|||||||
- allocator: (default: context.allocator)
|
- allocator: (default: context.allocator)
|
||||||
- loc: The caller location for debugging purposes (default: #caller_location)
|
- loc: The caller location for debugging purposes (default: #caller_location)
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="size")
|
@(optimization_mode="favor_size")
|
||||||
parse_or_exit :: proc(
|
parse_or_exit :: proc(
|
||||||
model: ^$T,
|
model: ^$T,
|
||||||
program_args: []string,
|
program_args: []string,
|
||||||
@@ -63,7 +63,7 @@ Inputs:
|
|||||||
- error: The error returned from `parse`.
|
- error: The error returned from `parse`.
|
||||||
- style: The argument parsing style, required to show flags in the proper style, when usage is shown.
|
- style: The argument parsing style, required to show flags in the proper style, when usage is shown.
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="size")
|
@(optimization_mode="favor_size")
|
||||||
print_errors :: proc(data_type: typeid, error: Error, program: string, style: Parsing_Style = .Odin) {
|
print_errors :: proc(data_type: typeid, error: Error, program: string, style: Parsing_Style = .Odin) {
|
||||||
stderr := os.stream_from_handle(os.stderr)
|
stderr := os.stream_from_handle(os.stderr)
|
||||||
stdout := os.stream_from_handle(os.stdout)
|
stdout := os.stream_from_handle(os.stdout)
|
||||||
|
|||||||
+47
-28
@@ -334,6 +334,27 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
|
|||||||
message := tprintf(fmt, ..args)
|
message := tprintf(fmt, ..args)
|
||||||
p("Panic", message, loc)
|
p("Panic", message, loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Creates a formatted C string
|
||||||
|
//
|
||||||
|
// *Allocates Using Context's Allocator*
|
||||||
|
//
|
||||||
|
// Inputs:
|
||||||
|
// - args: A variadic list of arguments to be formatted.
|
||||||
|
// - sep: An optional separator string (default is a single space).
|
||||||
|
//
|
||||||
|
// Returns: A formatted C string.
|
||||||
|
//
|
||||||
|
@(require_results)
|
||||||
|
caprint :: proc(args: ..any, sep := " ", allocator := context.allocator) -> cstring {
|
||||||
|
str: strings.Builder
|
||||||
|
strings.builder_init(&str, allocator)
|
||||||
|
sbprint(&str, ..args, sep=sep)
|
||||||
|
strings.write_byte(&str, 0)
|
||||||
|
s := strings.to_string(str)
|
||||||
|
return cstring(raw_data(s))
|
||||||
|
}
|
||||||
|
|
||||||
// Creates a formatted C string
|
// Creates a formatted C string
|
||||||
//
|
//
|
||||||
// *Allocates Using Context's Allocator*
|
// *Allocates Using Context's Allocator*
|
||||||
@@ -346,9 +367,9 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
|
|||||||
// Returns: A formatted C string
|
// Returns: A formatted C string
|
||||||
//
|
//
|
||||||
@(require_results)
|
@(require_results)
|
||||||
caprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
|
caprintf :: proc(format: string, args: ..any, allocator := context.allocator, newline := false) -> cstring {
|
||||||
str: strings.Builder
|
str: strings.Builder
|
||||||
strings.builder_init(&str)
|
strings.builder_init(&str, allocator)
|
||||||
sbprintf(&str, format, ..args, newline=newline)
|
sbprintf(&str, format, ..args, newline=newline)
|
||||||
strings.write_byte(&str, 0)
|
strings.write_byte(&str, 0)
|
||||||
s := strings.to_string(str)
|
s := strings.to_string(str)
|
||||||
@@ -365,8 +386,8 @@ caprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
|
|||||||
// Returns: A formatted C string
|
// Returns: A formatted C string
|
||||||
//
|
//
|
||||||
@(require_results)
|
@(require_results)
|
||||||
caprintfln :: proc(format: string, args: ..any) -> cstring {
|
caprintfln :: proc(format: string, args: ..any, allocator := context.allocator) -> cstring {
|
||||||
return caprintf(format, ..args, newline=true)
|
return caprintf(format, ..args, allocator=allocator, newline=true)
|
||||||
}
|
}
|
||||||
// Creates a formatted C string
|
// Creates a formatted C string
|
||||||
//
|
//
|
||||||
@@ -380,12 +401,7 @@ caprintfln :: proc(format: string, args: ..any) -> cstring {
|
|||||||
//
|
//
|
||||||
@(require_results)
|
@(require_results)
|
||||||
ctprint :: proc(args: ..any, sep := " ") -> cstring {
|
ctprint :: proc(args: ..any, sep := " ") -> cstring {
|
||||||
str: strings.Builder
|
return caprint(args=args, sep=sep, allocator=context.temp_allocator)
|
||||||
strings.builder_init(&str, context.temp_allocator)
|
|
||||||
sbprint(&str, ..args, sep=sep)
|
|
||||||
strings.write_byte(&str, 0)
|
|
||||||
s := strings.to_string(str)
|
|
||||||
return cstring(raw_data(s))
|
|
||||||
}
|
}
|
||||||
// Creates a formatted C string
|
// Creates a formatted C string
|
||||||
//
|
//
|
||||||
@@ -400,12 +416,7 @@ ctprint :: proc(args: ..any, sep := " ") -> cstring {
|
|||||||
//
|
//
|
||||||
@(require_results)
|
@(require_results)
|
||||||
ctprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
|
ctprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
|
||||||
str: strings.Builder
|
return caprintf(format=format, args=args, allocator=context.temp_allocator, newline=newline)
|
||||||
strings.builder_init(&str, context.temp_allocator)
|
|
||||||
sbprintf(&str, format, ..args, newline=newline)
|
|
||||||
strings.write_byte(&str, 0)
|
|
||||||
s := strings.to_string(str)
|
|
||||||
return cstring(raw_data(s))
|
|
||||||
}
|
}
|
||||||
// Creates a formatted C string, followed by a newline.
|
// Creates a formatted C string, followed by a newline.
|
||||||
//
|
//
|
||||||
@@ -419,7 +430,7 @@ ctprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
|
|||||||
//
|
//
|
||||||
@(require_results)
|
@(require_results)
|
||||||
ctprintfln :: proc(format: string, args: ..any) -> cstring {
|
ctprintfln :: proc(format: string, args: ..any) -> cstring {
|
||||||
return ctprintf(format, ..args, newline=true)
|
return caprintf(format=format, args=args, allocator=context.temp_allocator, newline=true)
|
||||||
}
|
}
|
||||||
// Formats using the default print settings and writes to the given strings.Builder
|
// Formats using the default print settings and writes to the given strings.Builder
|
||||||
//
|
//
|
||||||
@@ -951,10 +962,10 @@ fmt_bad_verb :: proc(fi: ^Info, verb: rune) {
|
|||||||
io.write_string(fi.writer, "%!", &fi.n)
|
io.write_string(fi.writer, "%!", &fi.n)
|
||||||
io.write_rune(fi.writer, verb, &fi.n)
|
io.write_rune(fi.writer, verb, &fi.n)
|
||||||
io.write_byte(fi.writer, '(', &fi.n)
|
io.write_byte(fi.writer, '(', &fi.n)
|
||||||
if fi.arg.id != nil {
|
if arg := fi.arg; arg != nil {
|
||||||
reflect.write_typeid(fi.writer, fi.arg.id, &fi.n)
|
reflect.write_typeid(fi.writer, arg.id, &fi.n)
|
||||||
io.write_byte(fi.writer, '=', &fi.n)
|
io.write_byte(fi.writer, '=', &fi.n)
|
||||||
fmt_value(fi, fi.arg, 'v')
|
fmt_value(fi, arg, 'v')
|
||||||
} else {
|
} else {
|
||||||
io.write_string(fi.writer, "<nil>", &fi.n)
|
io.write_string(fi.writer, "<nil>", &fi.n)
|
||||||
}
|
}
|
||||||
@@ -1861,7 +1872,7 @@ handle_tag :: proc(state: ^Info_State, data: rawptr, info: reflect.Type_Info_Str
|
|||||||
if optional_len == nil {
|
if optional_len == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for f, i in info.names {
|
for f, i in info.names[:info.field_count] {
|
||||||
if f != field_name {
|
if f != field_name {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -1965,7 +1976,7 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St
|
|||||||
fmt_bad_verb(fi, the_verb)
|
fmt_bad_verb(fi, the_verb)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if info.is_raw_union {
|
if .raw_union in info.flags {
|
||||||
if type_name == "" {
|
if type_name == "" {
|
||||||
io.write_string(fi.writer, "(raw union)", &fi.n)
|
io.write_string(fi.writer, "(raw union)", &fi.n)
|
||||||
} else {
|
} else {
|
||||||
@@ -1989,7 +2000,7 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St
|
|||||||
// fi.hash = false;
|
// fi.hash = false;
|
||||||
fi.indent += 1
|
fi.indent += 1
|
||||||
|
|
||||||
is_empty := len(info.names) == 0
|
is_empty := info.field_count == 0
|
||||||
|
|
||||||
if !is_soa && hash && !is_empty {
|
if !is_soa && hash && !is_empty {
|
||||||
io.write_byte(fi.writer, '\n', &fi.n)
|
io.write_byte(fi.writer, '\n', &fi.n)
|
||||||
@@ -2010,17 +2021,17 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St
|
|||||||
base_type_name = v.name
|
base_type_name = v.name
|
||||||
}
|
}
|
||||||
|
|
||||||
actual_field_count := len(info.names)
|
actual_field_count := info.field_count
|
||||||
|
|
||||||
n := uintptr(info.soa_len)
|
n := uintptr(info.soa_len)
|
||||||
|
|
||||||
if info.soa_kind == .Slice {
|
if info.soa_kind == .Slice {
|
||||||
actual_field_count = len(info.names)-1 // len
|
actual_field_count = info.field_count-1 // len
|
||||||
|
|
||||||
n = uintptr((^int)(uintptr(v.data) + info.offsets[actual_field_count])^)
|
n = uintptr((^int)(uintptr(v.data) + info.offsets[actual_field_count])^)
|
||||||
|
|
||||||
} else if info.soa_kind == .Dynamic {
|
} else if info.soa_kind == .Dynamic {
|
||||||
actual_field_count = len(info.names)-3 // len, cap, allocator
|
actual_field_count = info.field_count-3 // len, cap, allocator
|
||||||
|
|
||||||
n = uintptr((^int)(uintptr(v.data) + info.offsets[actual_field_count])^)
|
n = uintptr((^int)(uintptr(v.data) + info.offsets[actual_field_count])^)
|
||||||
}
|
}
|
||||||
@@ -2099,7 +2110,7 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
field_count := -1
|
field_count := -1
|
||||||
for name, i in info.names {
|
for name, i in info.names[:info.field_count] {
|
||||||
optional_len: int = -1
|
optional_len: int = -1
|
||||||
use_nul_termination: bool = false
|
use_nul_termination: bool = false
|
||||||
verb := the_verb if the_verb == 'w' else 'v'
|
verb := the_verb if the_verb == 'w' else 'v'
|
||||||
@@ -2605,7 +2616,7 @@ fmt_bit_field :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Bit
|
|||||||
|
|
||||||
|
|
||||||
field_count := -1
|
field_count := -1
|
||||||
for name, i in info.names {
|
for name, i in info.names[:info.field_count] {
|
||||||
field_verb := verb
|
field_verb := verb
|
||||||
if handle_bit_field_tag(v.data, info, i, &field_verb) {
|
if handle_bit_field_tag(v.data, info, i, &field_verb) {
|
||||||
continue
|
continue
|
||||||
@@ -2751,9 +2762,11 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
|||||||
elem := runtime.type_info_base(info.elem)
|
elem := runtime.type_info_base(info.elem)
|
||||||
if elem != nil {
|
if elem != nil {
|
||||||
if n, ok := fi.optional_len.?; ok {
|
if n, ok := fi.optional_len.?; ok {
|
||||||
|
fi.optional_len = nil
|
||||||
fmt_array(fi, ptr, n, elem.size, elem, verb)
|
fmt_array(fi, ptr, n, elem.size, elem, verb)
|
||||||
return
|
return
|
||||||
} else if fi.use_nul_termination {
|
} else if fi.use_nul_termination {
|
||||||
|
fi.use_nul_termination = false
|
||||||
fmt_array_nul_terminated(fi, ptr, -1, elem.size, elem, verb)
|
fmt_array_nul_terminated(fi, ptr, -1, elem.size, elem, verb)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -2855,8 +2868,10 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
|||||||
n := info.count
|
n := info.count
|
||||||
ptr := v.data
|
ptr := v.data
|
||||||
if ol, ok := fi.optional_len.?; ok {
|
if ol, ok := fi.optional_len.?; ok {
|
||||||
|
fi.optional_len = nil
|
||||||
n = min(n, ol)
|
n = min(n, ol)
|
||||||
} else if fi.use_nul_termination {
|
} else if fi.use_nul_termination {
|
||||||
|
fi.use_nul_termination = false
|
||||||
fmt_array_nul_terminated(fi, ptr, n, info.elem_size, info.elem, verb)
|
fmt_array_nul_terminated(fi, ptr, n, info.elem_size, info.elem, verb)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -2867,8 +2882,10 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
|||||||
n := slice.len
|
n := slice.len
|
||||||
ptr := slice.data
|
ptr := slice.data
|
||||||
if ol, ok := fi.optional_len.?; ok {
|
if ol, ok := fi.optional_len.?; ok {
|
||||||
|
fi.optional_len = nil
|
||||||
n = min(n, ol)
|
n = min(n, ol)
|
||||||
} else if fi.use_nul_termination {
|
} else if fi.use_nul_termination {
|
||||||
|
fi.use_nul_termination = false
|
||||||
fmt_array_nul_terminated(fi, ptr, n, info.elem_size, info.elem, verb)
|
fmt_array_nul_terminated(fi, ptr, n, info.elem_size, info.elem, verb)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -2879,8 +2896,10 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
|||||||
n := array.len
|
n := array.len
|
||||||
ptr := array.data
|
ptr := array.data
|
||||||
if ol, ok := fi.optional_len.?; ok {
|
if ol, ok := fi.optional_len.?; ok {
|
||||||
|
fi.optional_len = nil
|
||||||
n = min(n, ol)
|
n = min(n, ol)
|
||||||
} else if fi.use_nul_termination {
|
} else if fi.use_nul_termination {
|
||||||
|
fi.use_nul_termination = false
|
||||||
fmt_array_nul_terminated(fi, ptr, n, info.elem_size, info.elem, verb)
|
fmt_array_nul_terminated(fi, ptr, n, info.elem_size, info.elem, verb)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -1,6 +1,6 @@
|
|||||||
package hash
|
package hash
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
crc64_ecma_182 :: proc "contextless" (data: []byte, seed := u64(0)) -> (result: u64) #no_bounds_check {
|
crc64_ecma_182 :: proc "contextless" (data: []byte, seed := u64(0)) -> (result: u64) #no_bounds_check {
|
||||||
result = seed
|
result = seed
|
||||||
#no_bounds_check for b in data {
|
#no_bounds_check for b in data {
|
||||||
@@ -14,7 +14,7 @@ crc64_ecma_182 :: proc "contextless" (data: []byte, seed := u64(0)) -> (result:
|
|||||||
bit-reversed, with one's complement pre and post processing.
|
bit-reversed, with one's complement pre and post processing.
|
||||||
Based on Mark Adler's v1.4 implementation in C under the ZLIB license.
|
Based on Mark Adler's v1.4 implementation in C under the ZLIB license.
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
crc64_xz :: proc "contextless" (data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
|
crc64_xz :: proc "contextless" (data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
|
||||||
data := data
|
data := data
|
||||||
result := ~u64le(seed)
|
result := ~u64le(seed)
|
||||||
@@ -52,7 +52,7 @@ crc64_xz :: proc "contextless" (data: []byte, seed := u64(0)) -> u64 #no_bounds_
|
|||||||
/*
|
/*
|
||||||
Generator polynomial: x^64 + x^4 + x^3 + x + 1
|
Generator polynomial: x^64 + x^4 + x^3 + x + 1
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
crc64_iso_3306 :: proc "contextless" (data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
|
crc64_iso_3306 :: proc "contextless" (data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
|
||||||
|
|
||||||
result := seed
|
result := seed
|
||||||
@@ -738,4 +738,4 @@ crc64_iso_3306_inverse :: proc "contextless" (data: []byte, seed := u64(0)) -> u
|
|||||||
0x9fc0, 0x9e70, 0x9ca0, 0x9d10,
|
0x9fc0, 0x9e70, 0x9ca0, 0x9d10,
|
||||||
0x9480, 0x9530, 0x97e0, 0x9650,
|
0x9480, 0x9530, 0x97e0, 0x9650,
|
||||||
0x9240, 0x93f0, 0x9120, 0x9090,
|
0x9240, 0x93f0, 0x9120, 0x9090,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package hash
|
|||||||
|
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
crc32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
|
crc32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
|
||||||
crc := ~seed
|
crc := ~seed
|
||||||
buffer := raw_data(data)
|
buffer := raw_data(data)
|
||||||
|
|||||||
+11
-11
@@ -3,7 +3,7 @@ package hash
|
|||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
adler32 :: proc "contextless" (data: []byte, seed := u32(1)) -> u32 #no_bounds_check {
|
adler32 :: proc "contextless" (data: []byte, seed := u32(1)) -> u32 #no_bounds_check {
|
||||||
|
|
||||||
ADLER_CONST :: 65521
|
ADLER_CONST :: 65521
|
||||||
@@ -46,7 +46,7 @@ adler32 :: proc "contextless" (data: []byte, seed := u32(1)) -> u32 #no_bounds_c
|
|||||||
return (u32(b) << 16) | u32(a)
|
return (u32(b) << 16) | u32(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
djb2 :: proc "contextless" (data: []byte, seed := u32(5381)) -> u32 {
|
djb2 :: proc "contextless" (data: []byte, seed := u32(5381)) -> u32 {
|
||||||
hash: u32 = seed
|
hash: u32 = seed
|
||||||
for b in data {
|
for b in data {
|
||||||
@@ -73,7 +73,7 @@ djbx33a :: proc "contextless" (data: []byte, seed := u32(5381)) -> (result: [16]
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If you have a choice, prefer fnv32a
|
// If you have a choice, prefer fnv32a
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
fnv32_no_a :: proc "contextless" (data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
fnv32_no_a :: proc "contextless" (data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||||
h: u32 = seed
|
h: u32 = seed
|
||||||
for b in data {
|
for b in data {
|
||||||
@@ -86,7 +86,7 @@ fnv32 :: fnv32_no_a // NOTE(bill): Not a fan of these aliases but seems necessar
|
|||||||
fnv64 :: fnv64_no_a // NOTE(bill): Not a fan of these aliases but seems necessary
|
fnv64 :: fnv64_no_a // NOTE(bill): Not a fan of these aliases but seems necessary
|
||||||
|
|
||||||
// If you have a choice, prefer fnv64a
|
// If you have a choice, prefer fnv64a
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
fnv64_no_a :: proc "contextless" (data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
fnv64_no_a :: proc "contextless" (data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
||||||
h: u64 = seed
|
h: u64 = seed
|
||||||
for b in data {
|
for b in data {
|
||||||
@@ -94,7 +94,7 @@ fnv64_no_a :: proc "contextless" (data: []byte, seed := u64(0xcbf29ce484222325))
|
|||||||
}
|
}
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
fnv32a :: proc "contextless" (data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
fnv32a :: proc "contextless" (data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||||
h: u32 = seed
|
h: u32 = seed
|
||||||
for b in data {
|
for b in data {
|
||||||
@@ -103,7 +103,7 @@ fnv32a :: proc "contextless" (data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
|||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
fnv64a :: proc "contextless" (data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
fnv64a :: proc "contextless" (data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
||||||
h: u64 = seed
|
h: u64 = seed
|
||||||
for b in data {
|
for b in data {
|
||||||
@@ -112,7 +112,7 @@ fnv64a :: proc "contextless" (data: []byte, seed := u64(0xcbf29ce484222325)) ->
|
|||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
jenkins :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
|
jenkins :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
|
||||||
hash: u32 = seed
|
hash: u32 = seed
|
||||||
for b in data {
|
for b in data {
|
||||||
@@ -126,7 +126,7 @@ jenkins :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
|
|||||||
return hash
|
return hash
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
murmur32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
|
murmur32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
|
||||||
c1_32: u32 : 0xcc9e2d51
|
c1_32: u32 : 0xcc9e2d51
|
||||||
c2_32: u32 : 0x1b873593
|
c2_32: u32 : 0x1b873593
|
||||||
@@ -177,7 +177,7 @@ murmur32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L96
|
// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L96
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
murmur64a :: proc "contextless" (data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
murmur64a :: proc "contextless" (data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
||||||
m :: 0xc6a4a7935bd1e995
|
m :: 0xc6a4a7935bd1e995
|
||||||
r :: 47
|
r :: 47
|
||||||
@@ -218,7 +218,7 @@ murmur64a :: proc "contextless" (data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L140
|
// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L140
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
murmur64b :: proc "contextless" (data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
murmur64b :: proc "contextless" (data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
||||||
m :: 0x5bd1e995
|
m :: 0x5bd1e995
|
||||||
r :: 24
|
r :: 24
|
||||||
@@ -286,7 +286,7 @@ murmur64b :: proc "contextless" (data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
|||||||
return u64(h1)<<32 | u64(h2)
|
return u64(h1)<<32 | u64(h2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
sdbm :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
|
sdbm :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
|
||||||
hash: u32 = seed
|
hash: u32 = seed
|
||||||
for b in data {
|
for b in data {
|
||||||
|
|||||||
@@ -67,17 +67,17 @@ when !XXH_DISABLE_PREFETCH {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH_rotl32 :: #force_inline proc(x, r: u32) -> (res: u32) {
|
XXH_rotl32 :: #force_inline proc(x, r: u32) -> (res: u32) {
|
||||||
return ((x << r) | (x >> (32 - r)))
|
return ((x << r) | (x >> (32 - r)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH_rotl64 :: #force_inline proc(x, r: u64) -> (res: u64) {
|
XXH_rotl64 :: #force_inline proc(x, r: u64) -> (res: u64) {
|
||||||
return ((x << r) | (x >> (64 - r)))
|
return ((x << r) | (x >> (64 - r)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH32_read32 :: #force_inline proc(buf: []u8, alignment := Alignment.Unaligned) -> (res: u32) {
|
XXH32_read32 :: #force_inline proc(buf: []u8, alignment := Alignment.Unaligned) -> (res: u32) {
|
||||||
if XXH_FORCE_MEMORY_ACCESS == 2 || alignment == .Aligned {
|
if XXH_FORCE_MEMORY_ACCESS == 2 || alignment == .Aligned {
|
||||||
#no_bounds_check b := (^u32le)(&buf[0])^
|
#no_bounds_check b := (^u32le)(&buf[0])^
|
||||||
@@ -89,7 +89,7 @@ XXH32_read32 :: #force_inline proc(buf: []u8, alignment := Alignment.Unaligned)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH64_read64 :: #force_inline proc(buf: []u8, alignment := Alignment.Unaligned) -> (res: u64) {
|
XXH64_read64 :: #force_inline proc(buf: []u8, alignment := Alignment.Unaligned) -> (res: u64) {
|
||||||
if XXH_FORCE_MEMORY_ACCESS == 2 || alignment == .Aligned {
|
if XXH_FORCE_MEMORY_ACCESS == 2 || alignment == .Aligned {
|
||||||
#no_bounds_check b := (^u64le)(&buf[0])^
|
#no_bounds_check b := (^u64le)(&buf[0])^
|
||||||
@@ -99,4 +99,4 @@ XXH64_read64 :: #force_inline proc(buf: []u8, alignment := Alignment.Unaligned)
|
|||||||
mem_copy(&b, raw_data(buf[:]), 8)
|
mem_copy(&b, raw_data(buf[:]), 8)
|
||||||
return u64(b)
|
return u64(b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,13 +111,13 @@ XXH128_canonical :: struct {
|
|||||||
@param lhs, rhs The 64-bit integers to multiply
|
@param lhs, rhs The 64-bit integers to multiply
|
||||||
@return The low 64 bits of the product XOR'd by the high 64 bits.
|
@return The low 64 bits of the product XOR'd by the high 64 bits.
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH_mul_64_to_128_fold_64 :: #force_inline proc(lhs, rhs: xxh_u64) -> (res: xxh_u64) {
|
XXH_mul_64_to_128_fold_64 :: #force_inline proc(lhs, rhs: xxh_u64) -> (res: xxh_u64) {
|
||||||
t := u128(lhs) * u128(rhs)
|
t := u128(lhs) * u128(rhs)
|
||||||
return u64(t & 0xFFFFFFFFFFFFFFFF) ~ u64(t >> 64)
|
return u64(t & 0xFFFFFFFFFFFFFFFF) ~ u64(t >> 64)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH_xorshift_64 :: #force_inline proc(v: xxh_u64, #any_int shift: uint) -> (res: xxh_u64) {
|
XXH_xorshift_64 :: #force_inline proc(v: xxh_u64, #any_int shift: uint) -> (res: xxh_u64) {
|
||||||
return v ~ (v >> shift)
|
return v ~ (v >> shift)
|
||||||
}
|
}
|
||||||
@@ -125,7 +125,7 @@ XXH_xorshift_64 :: #force_inline proc(v: xxh_u64, #any_int shift: uint) -> (res:
|
|||||||
/*
|
/*
|
||||||
This is a fast avalanche stage, suitable when input bits are already partially mixed
|
This is a fast avalanche stage, suitable when input bits are already partially mixed
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_avalanche :: #force_inline proc(h64: xxh_u64) -> (res: xxh_u64) {
|
XXH3_avalanche :: #force_inline proc(h64: xxh_u64) -> (res: xxh_u64) {
|
||||||
res = XXH_xorshift_64(h64, 37)
|
res = XXH_xorshift_64(h64, 37)
|
||||||
res *= 0x165667919E3779F9
|
res *= 0x165667919E3779F9
|
||||||
@@ -137,7 +137,7 @@ XXH3_avalanche :: #force_inline proc(h64: xxh_u64) -> (res: xxh_u64) {
|
|||||||
This is a stronger avalanche, inspired by Pelle Evensen's rrmxmx
|
This is a stronger avalanche, inspired by Pelle Evensen's rrmxmx
|
||||||
preferable when input has not been previously mixed
|
preferable when input has not been previously mixed
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_rrmxmx :: #force_inline proc(h64, length: xxh_u64) -> (res: xxh_u64) {
|
XXH3_rrmxmx :: #force_inline proc(h64, length: xxh_u64) -> (res: xxh_u64) {
|
||||||
/* this mix is inspired by Pelle Evensen's rrmxmx */
|
/* this mix is inspired by Pelle Evensen's rrmxmx */
|
||||||
res = h64
|
res = h64
|
||||||
@@ -166,7 +166,7 @@ XXH3_rrmxmx :: #force_inline proc(h64, length: xxh_u64) -> (res: xxh_u64) {
|
|||||||
fast for a _128-bit_ hash on 32-bit (it usually clears XXH64).
|
fast for a _128-bit_ hash on 32-bit (it usually clears XXH64).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_len_1to3_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
|
XXH3_len_1to3_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
|
||||||
/* A doubled version of 1to3_64b with different constants. */
|
/* A doubled version of 1to3_64b with different constants. */
|
||||||
length := len(input)
|
length := len(input)
|
||||||
@@ -190,7 +190,7 @@ XXH3_len_1to3_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u6
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_len_4to8_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
|
XXH3_len_4to8_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
|
||||||
length := len(input)
|
length := len(input)
|
||||||
seed := seed
|
seed := seed
|
||||||
@@ -219,7 +219,7 @@ XXH3_len_4to8_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u6
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_len_9to16_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
|
XXH3_len_9to16_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
|
||||||
length := len(input)
|
length := len(input)
|
||||||
|
|
||||||
@@ -261,7 +261,7 @@ XXH3_len_9to16_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u
|
|||||||
/*
|
/*
|
||||||
Assumption: `secret` size is >= XXH3_SECRET_SIZE_MIN
|
Assumption: `secret` size is >= XXH3_SECRET_SIZE_MIN
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_len_0to16_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
|
XXH3_len_0to16_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
|
||||||
length := len(input)
|
length := len(input)
|
||||||
|
|
||||||
@@ -279,7 +279,7 @@ XXH3_len_0to16_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u
|
|||||||
/*
|
/*
|
||||||
A bit slower than XXH3_mix16B, but handles multiply by zero better.
|
A bit slower than XXH3_mix16B, but handles multiply by zero better.
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH128_mix32B :: #force_inline proc(acc: xxh_u128, input_1: []u8, input_2: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
|
XXH128_mix32B :: #force_inline proc(acc: xxh_u128, input_1: []u8, input_2: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
|
||||||
acc128 := XXH128_hash_t{
|
acc128 := XXH128_hash_t{
|
||||||
h = acc,
|
h = acc,
|
||||||
@@ -293,7 +293,7 @@ XXH128_mix32B :: #force_inline proc(acc: xxh_u128, input_1: []u8, input_2: []u8,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_len_17to128_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
|
XXH3_len_17to128_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
|
||||||
length := len(input)
|
length := len(input)
|
||||||
|
|
||||||
@@ -323,7 +323,7 @@ XXH3_len_17to128_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh
|
|||||||
unreachable()
|
unreachable()
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_len_129to240_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
|
XXH3_len_129to240_128b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u128) {
|
||||||
length := len(input)
|
length := len(input)
|
||||||
|
|
||||||
@@ -379,7 +379,7 @@ XXH3_INIT_ACC :: [XXH_ACC_NB]xxh_u64{
|
|||||||
|
|
||||||
XXH_SECRET_MERGEACCS_START :: 11
|
XXH_SECRET_MERGEACCS_START :: 11
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_hashLong_128b_internal :: #force_inline proc(
|
XXH3_hashLong_128b_internal :: #force_inline proc(
|
||||||
input: []u8,
|
input: []u8,
|
||||||
secret: []u8,
|
secret: []u8,
|
||||||
@@ -407,7 +407,7 @@ XXH3_hashLong_128b_internal :: #force_inline proc(
|
|||||||
/*
|
/*
|
||||||
* It's important for performance that XXH3_hashLong is not inlined.
|
* It's important for performance that XXH3_hashLong is not inlined.
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_hashLong_128b_default :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: XXH3_128_hash) {
|
XXH3_hashLong_128b_default :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: XXH3_128_hash) {
|
||||||
return XXH3_hashLong_128b_internal(input, XXH3_kSecret[:], XXH3_accumulate_512, XXH3_scramble_accumulator)
|
return XXH3_hashLong_128b_internal(input, XXH3_kSecret[:], XXH3_accumulate_512, XXH3_scramble_accumulator)
|
||||||
}
|
}
|
||||||
@@ -415,12 +415,12 @@ XXH3_hashLong_128b_default :: #force_no_inline proc(input: []u8, seed: xxh_u64,
|
|||||||
/*
|
/*
|
||||||
* It's important for performance that XXH3_hashLong is not inlined.
|
* It's important for performance that XXH3_hashLong is not inlined.
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_hashLong_128b_withSecret :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: XXH3_128_hash) {
|
XXH3_hashLong_128b_withSecret :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: XXH3_128_hash) {
|
||||||
return XXH3_hashLong_128b_internal(input, secret, XXH3_accumulate_512, XXH3_scramble_accumulator)
|
return XXH3_hashLong_128b_internal(input, secret, XXH3_accumulate_512, XXH3_scramble_accumulator)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_hashLong_128b_withSeed_internal :: #force_inline proc(
|
XXH3_hashLong_128b_withSeed_internal :: #force_inline proc(
|
||||||
input: []u8, seed: xxh_u64, secret: []u8,
|
input: []u8, seed: xxh_u64, secret: []u8,
|
||||||
f_acc512: XXH3_accumulate_512_f,
|
f_acc512: XXH3_accumulate_512_f,
|
||||||
@@ -441,14 +441,14 @@ XXH3_hashLong_128b_withSeed_internal :: #force_inline proc(
|
|||||||
/*
|
/*
|
||||||
* It's important for performance that XXH3_hashLong is not inlined.
|
* It's important for performance that XXH3_hashLong is not inlined.
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_hashLong_128b_withSeed :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: XXH3_128_hash) {
|
XXH3_hashLong_128b_withSeed :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: XXH3_128_hash) {
|
||||||
return XXH3_hashLong_128b_withSeed_internal(input, seed, secret, XXH3_accumulate_512, XXH3_scramble_accumulator , XXH3_init_custom_secret)
|
return XXH3_hashLong_128b_withSeed_internal(input, seed, secret, XXH3_accumulate_512, XXH3_scramble_accumulator , XXH3_init_custom_secret)
|
||||||
}
|
}
|
||||||
|
|
||||||
XXH3_hashLong128_f :: #type proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: XXH3_128_hash)
|
XXH3_hashLong128_f :: #type proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: XXH3_128_hash)
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_128bits_internal :: #force_inline proc(
|
XXH3_128bits_internal :: #force_inline proc(
|
||||||
input: []u8, seed: xxh_u64, secret: []u8, f_hl128: XXH3_hashLong128_f) -> (res: XXH3_128_hash) {
|
input: []u8, seed: xxh_u64, secret: []u8, f_hl128: XXH3_hashLong128_f) -> (res: XXH3_128_hash) {
|
||||||
|
|
||||||
@@ -474,17 +474,17 @@ XXH3_128bits_internal :: #force_inline proc(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* === Public XXH128 API === */
|
/* === Public XXH128 API === */
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_128_default :: proc(input: []u8) -> (hash: XXH3_128_hash) {
|
XXH3_128_default :: proc(input: []u8) -> (hash: XXH3_128_hash) {
|
||||||
return XXH3_128bits_internal(input, 0, XXH3_kSecret[:], XXH3_hashLong_128b_withSeed)
|
return XXH3_128bits_internal(input, 0, XXH3_kSecret[:], XXH3_hashLong_128b_withSeed)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_128_with_seed :: proc(input: []u8, seed: xxh_u64) -> (hash: XXH3_128_hash) {
|
XXH3_128_with_seed :: proc(input: []u8, seed: xxh_u64) -> (hash: XXH3_128_hash) {
|
||||||
return XXH3_128bits_internal(input, seed, XXH3_kSecret[:], XXH3_hashLong_128b_withSeed)
|
return XXH3_128bits_internal(input, seed, XXH3_kSecret[:], XXH3_hashLong_128b_withSeed)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_128_with_secret :: proc(input: []u8, secret: []u8) -> (hash: XXH3_128_hash) {
|
XXH3_128_with_secret :: proc(input: []u8, secret: []u8) -> (hash: XXH3_128_hash) {
|
||||||
return XXH3_128bits_internal(input, 0, secret, XXH3_hashLong_128b_withSecret)
|
return XXH3_128bits_internal(input, 0, secret, XXH3_hashLong_128b_withSecret)
|
||||||
}
|
}
|
||||||
@@ -519,7 +519,7 @@ XXH3_128 :: proc { XXH3_128_default, XXH3_128_with_seed, XXH3_128_with_secret }
|
|||||||
The XOR mixing hides individual parts of the secret and increases entropy.
|
The XOR mixing hides individual parts of the secret and increases entropy.
|
||||||
This adds an extra layer of strength for custom secrets.
|
This adds an extra layer of strength for custom secrets.
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_len_1to3_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
|
XXH3_len_1to3_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
|
||||||
length := u32(len(input))
|
length := u32(len(input))
|
||||||
assert(input != nil)
|
assert(input != nil)
|
||||||
@@ -542,7 +542,7 @@ XXH3_len_1to3_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_len_4to8_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
|
XXH3_len_4to8_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
|
||||||
length := u32(len(input))
|
length := u32(len(input))
|
||||||
assert(input != nil)
|
assert(input != nil)
|
||||||
@@ -562,7 +562,7 @@ XXH3_len_4to8_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_len_9to16_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
|
XXH3_len_9to16_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
|
||||||
length := u64(len(input))
|
length := u64(len(input))
|
||||||
assert(input != nil)
|
assert(input != nil)
|
||||||
@@ -579,7 +579,7 @@ XXH3_len_9to16_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u6
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_len_0to16_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
|
XXH3_len_0to16_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
|
||||||
length := u64(len(input))
|
length := u64(len(input))
|
||||||
assert(input != nil)
|
assert(input != nil)
|
||||||
@@ -621,7 +621,7 @@ XXH3_len_0to16_64b :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u6
|
|||||||
by this, although it is always a good idea to use a proper seed if you care
|
by this, although it is always a good idea to use a proper seed if you care
|
||||||
about strength.
|
about strength.
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_mix16B :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
|
XXH3_mix16B :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
|
||||||
input_lo := XXH64_read64(input[0:])
|
input_lo := XXH64_read64(input[0:])
|
||||||
input_hi := XXH64_read64(input[8:])
|
input_hi := XXH64_read64(input[8:])
|
||||||
@@ -632,7 +632,7 @@ XXH3_mix16B :: #force_inline proc(input: []u8, secret: []u8, seed: xxh_u64) -> (
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* For mid range keys, XXH3 uses a Mum-hash variant. */
|
/* For mid range keys, XXH3 uses a Mum-hash variant. */
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_len_17to128_64b :: proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
|
XXH3_len_17to128_64b :: proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
|
||||||
assert(len(secret) >= XXH3_SECRET_SIZE_MIN)
|
assert(len(secret) >= XXH3_SECRET_SIZE_MIN)
|
||||||
length := len(input)
|
length := len(input)
|
||||||
@@ -665,7 +665,7 @@ XXH3_MIDSIZE_MAX :: 240
|
|||||||
XXH3_MIDSIZE_STARTOFFSET :: 3
|
XXH3_MIDSIZE_STARTOFFSET :: 3
|
||||||
XXH3_MIDSIZE_LASTOFFSET :: 17
|
XXH3_MIDSIZE_LASTOFFSET :: 17
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_len_129to240_64b :: proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
|
XXH3_len_129to240_64b :: proc(input: []u8, secret: []u8, seed: xxh_u64) -> (res: xxh_u64) {
|
||||||
assert(len(secret) >= XXH3_SECRET_SIZE_MIN)
|
assert(len(secret) >= XXH3_SECRET_SIZE_MIN)
|
||||||
length := len(input)
|
length := len(input)
|
||||||
@@ -699,7 +699,7 @@ XXH_SECRET_CONSUME_RATE :: 8 /* nb of secret bytes consumed at each accumulatio
|
|||||||
XXH_ACC_NB :: (XXH_STRIPE_LEN / size_of(xxh_u64))
|
XXH_ACC_NB :: (XXH_STRIPE_LEN / size_of(xxh_u64))
|
||||||
XXH_SECRET_LASTACC_START :: 7 /* not aligned on 8, last secret is different from acc & scrambler */
|
XXH_SECRET_LASTACC_START :: 7 /* not aligned on 8, last secret is different from acc & scrambler */
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH_writeLE64 :: #force_inline proc(dst: []u8, v64: u64le) {
|
XXH_writeLE64 :: #force_inline proc(dst: []u8, v64: u64le) {
|
||||||
v := v64
|
v := v64
|
||||||
mem_copy(raw_data(dst), &v, size_of(v64))
|
mem_copy(raw_data(dst), &v, size_of(v64))
|
||||||
@@ -737,7 +737,7 @@ XXH3_scramble_accumulator : XXH3_scramble_accumulator_f = XXH3_scramble_accumula
|
|||||||
XXH3_init_custom_secret : XXH3_init_custom_secret_f = XXH3_init_custom_secret_scalar
|
XXH3_init_custom_secret : XXH3_init_custom_secret_f = XXH3_init_custom_secret_scalar
|
||||||
|
|
||||||
/* scalar variants - universal */
|
/* scalar variants - universal */
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_accumulate_512_scalar :: #force_inline proc(acc: []xxh_u64, input: []u8, secret: []u8) {
|
XXH3_accumulate_512_scalar :: #force_inline proc(acc: []xxh_u64, input: []u8, secret: []u8) {
|
||||||
xacc := acc /* presumed aligned */
|
xacc := acc /* presumed aligned */
|
||||||
xinput := input /* no alignment restriction */
|
xinput := input /* no alignment restriction */
|
||||||
@@ -754,7 +754,7 @@ XXH3_accumulate_512_scalar :: #force_inline proc(acc: []xxh_u64, input: []u8, se
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_scramble_accumulator_scalar :: #force_inline proc(acc: []xxh_u64, secret: []u8) {
|
XXH3_scramble_accumulator_scalar :: #force_inline proc(acc: []xxh_u64, secret: []u8) {
|
||||||
xacc := acc /* presumed aligned */
|
xacc := acc /* presumed aligned */
|
||||||
xsecret := secret /* no alignment restriction */
|
xsecret := secret /* no alignment restriction */
|
||||||
@@ -771,7 +771,7 @@ XXH3_scramble_accumulator_scalar :: #force_inline proc(acc: []xxh_u64, secret: [
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_init_custom_secret_scalar :: #force_inline proc(custom_secret: []u8, seed64: xxh_u64) {
|
XXH3_init_custom_secret_scalar :: #force_inline proc(custom_secret: []u8, seed64: xxh_u64) {
|
||||||
#assert((XXH_SECRET_DEFAULT_SIZE & 15) == 0)
|
#assert((XXH_SECRET_DEFAULT_SIZE & 15) == 0)
|
||||||
|
|
||||||
@@ -791,7 +791,7 @@ XXH_PREFETCH_DIST :: 320
|
|||||||
* Loops over XXH3_accumulate_512().
|
* Loops over XXH3_accumulate_512().
|
||||||
* Assumption: nbStripes will not overflow the secret size
|
* Assumption: nbStripes will not overflow the secret size
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_accumulate :: #force_inline proc(
|
XXH3_accumulate :: #force_inline proc(
|
||||||
acc: []xxh_u64, input: []u8, secret: []u8, nbStripes: uint, f_acc512: XXH3_accumulate_512_f) {
|
acc: []xxh_u64, input: []u8, secret: []u8, nbStripes: uint, f_acc512: XXH3_accumulate_512_f) {
|
||||||
|
|
||||||
@@ -804,7 +804,7 @@ XXH3_accumulate :: #force_inline proc(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_hashLong_internal_loop :: #force_inline proc(acc: []xxh_u64, input: []u8, secret: []u8,
|
XXH3_hashLong_internal_loop :: #force_inline proc(acc: []xxh_u64, input: []u8, secret: []u8,
|
||||||
f_acc512: XXH3_accumulate_512_f, f_scramble: XXH3_scramble_accumulator_f) {
|
f_acc512: XXH3_accumulate_512_f, f_scramble: XXH3_scramble_accumulator_f) {
|
||||||
|
|
||||||
@@ -833,14 +833,14 @@ XXH3_hashLong_internal_loop :: #force_inline proc(acc: []xxh_u64, input: []u8, s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_mix2Accs :: #force_inline proc(acc: []xxh_u64, secret: []u8) -> (res: xxh_u64) {
|
XXH3_mix2Accs :: #force_inline proc(acc: []xxh_u64, secret: []u8) -> (res: xxh_u64) {
|
||||||
return XXH_mul_64_to_128_fold_64(
|
return XXH_mul_64_to_128_fold_64(
|
||||||
acc[0] ~ XXH64_read64(secret),
|
acc[0] ~ XXH64_read64(secret),
|
||||||
acc[1] ~ XXH64_read64(secret[8:]))
|
acc[1] ~ XXH64_read64(secret[8:]))
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_mergeAccs :: #force_inline proc(acc: []xxh_u64, secret: []u8, start: xxh_u64) -> (res: xxh_u64) {
|
XXH3_mergeAccs :: #force_inline proc(acc: []xxh_u64, secret: []u8, start: xxh_u64) -> (res: xxh_u64) {
|
||||||
result64 := start
|
result64 := start
|
||||||
#no_bounds_check for i := 0; i < 4; i += 1 {
|
#no_bounds_check for i := 0; i < 4; i += 1 {
|
||||||
@@ -849,7 +849,7 @@ XXH3_mergeAccs :: #force_inline proc(acc: []xxh_u64, secret: []u8, start: xxh_u6
|
|||||||
return XXH3_avalanche(result64)
|
return XXH3_avalanche(result64)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_hashLong_64b_internal :: #force_inline proc(input: []u8, secret: []u8,
|
XXH3_hashLong_64b_internal :: #force_inline proc(input: []u8, secret: []u8,
|
||||||
f_acc512: XXH3_accumulate_512_f, f_scramble: XXH3_scramble_accumulator_f) -> (hash: xxh_u64) {
|
f_acc512: XXH3_accumulate_512_f, f_scramble: XXH3_scramble_accumulator_f) -> (hash: xxh_u64) {
|
||||||
|
|
||||||
@@ -868,7 +868,7 @@ XXH3_hashLong_64b_internal :: #force_inline proc(input: []u8, secret: []u8,
|
|||||||
/*
|
/*
|
||||||
It's important for performance that XXH3_hashLong is not inlined.
|
It's important for performance that XXH3_hashLong is not inlined.
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_hashLong_64b_withSecret :: #force_no_inline proc(input: []u8, seed64: xxh_u64, secret: []u8) -> (hash: xxh_u64) {
|
XXH3_hashLong_64b_withSecret :: #force_no_inline proc(input: []u8, seed64: xxh_u64, secret: []u8) -> (hash: xxh_u64) {
|
||||||
return XXH3_hashLong_64b_internal(input, secret, XXH3_accumulate_512, XXH3_scramble_accumulator)
|
return XXH3_hashLong_64b_internal(input, secret, XXH3_accumulate_512, XXH3_scramble_accumulator)
|
||||||
}
|
}
|
||||||
@@ -880,7 +880,7 @@ XXH3_hashLong_64b_withSecret :: #force_no_inline proc(input: []u8, seed64: xxh_u
|
|||||||
This variant enforces that the compiler can detect that,
|
This variant enforces that the compiler can detect that,
|
||||||
and uses this opportunity to streamline the generated code for better performance.
|
and uses this opportunity to streamline the generated code for better performance.
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_hashLong_64b_default :: #force_no_inline proc(input: []u8, seed64: xxh_u64, secret: []u8) -> (hash: xxh_u64) {
|
XXH3_hashLong_64b_default :: #force_no_inline proc(input: []u8, seed64: xxh_u64, secret: []u8) -> (hash: xxh_u64) {
|
||||||
return XXH3_hashLong_64b_internal(input, XXH3_kSecret[:], XXH3_accumulate_512, XXH3_scramble_accumulator)
|
return XXH3_hashLong_64b_internal(input, XXH3_kSecret[:], XXH3_accumulate_512, XXH3_scramble_accumulator)
|
||||||
}
|
}
|
||||||
@@ -896,7 +896,7 @@ XXH3_hashLong_64b_default :: #force_no_inline proc(input: []u8, seed64: xxh_u64,
|
|||||||
It's important for performance that XXH3_hashLong is not inlined. Not sure
|
It's important for performance that XXH3_hashLong is not inlined. Not sure
|
||||||
why (uop cache maybe?), but the difference is large and easily measurable.
|
why (uop cache maybe?), but the difference is large and easily measurable.
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_hashLong_64b_withSeed_internal :: #force_no_inline proc(
|
XXH3_hashLong_64b_withSeed_internal :: #force_no_inline proc(
|
||||||
input: []u8,
|
input: []u8,
|
||||||
seed: xxh_u64,
|
seed: xxh_u64,
|
||||||
@@ -916,7 +916,7 @@ XXH3_hashLong_64b_withSeed_internal :: #force_no_inline proc(
|
|||||||
/*
|
/*
|
||||||
It's important for performance that XXH3_hashLong is not inlined.
|
It's important for performance that XXH3_hashLong is not inlined.
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_hashLong_64b_withSeed :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (hash: xxh_u64) {
|
XXH3_hashLong_64b_withSeed :: #force_no_inline proc(input: []u8, seed: xxh_u64, secret: []u8) -> (hash: xxh_u64) {
|
||||||
return XXH3_hashLong_64b_withSeed_internal(input, seed, XXH3_accumulate_512, XXH3_scramble_accumulator, XXH3_init_custom_secret)
|
return XXH3_hashLong_64b_withSeed_internal(input, seed, XXH3_accumulate_512, XXH3_scramble_accumulator, XXH3_init_custom_secret)
|
||||||
}
|
}
|
||||||
@@ -924,7 +924,7 @@ XXH3_hashLong_64b_withSeed :: #force_no_inline proc(input: []u8, seed: xxh_u64,
|
|||||||
|
|
||||||
XXH3_hashLong64_f :: #type proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: xxh_u64)
|
XXH3_hashLong64_f :: #type proc(input: []u8, seed: xxh_u64, secret: []u8) -> (res: xxh_u64)
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_64bits_internal :: proc(input: []u8, seed: xxh_u64, secret: []u8, f_hashLong: XXH3_hashLong64_f) -> (hash: xxh_u64) {
|
XXH3_64bits_internal :: proc(input: []u8, seed: xxh_u64, secret: []u8, f_hashLong: XXH3_hashLong64_f) -> (hash: xxh_u64) {
|
||||||
assert(len(secret) >= XXH3_SECRET_SIZE_MIN)
|
assert(len(secret) >= XXH3_SECRET_SIZE_MIN)
|
||||||
/*
|
/*
|
||||||
@@ -944,19 +944,19 @@ XXH3_64bits_internal :: proc(input: []u8, seed: xxh_u64, secret: []u8, f_hashLon
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* === Public entry point === */
|
/* === Public entry point === */
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_64_default :: proc(input: []u8) -> (hash: xxh_u64) {
|
XXH3_64_default :: proc(input: []u8) -> (hash: xxh_u64) {
|
||||||
return XXH3_64bits_internal(input, 0, XXH3_kSecret[:], XXH3_hashLong_64b_default)
|
return XXH3_64bits_internal(input, 0, XXH3_kSecret[:], XXH3_hashLong_64b_default)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_64_with_seed :: proc(input: []u8, seed: xxh_u64) -> (hash: xxh_u64) {
|
XXH3_64_with_seed :: proc(input: []u8, seed: xxh_u64) -> (hash: xxh_u64) {
|
||||||
return XXH3_64bits_internal(input, seed, XXH3_kSecret[:], XXH3_hashLong_64b_withSeed)
|
return XXH3_64bits_internal(input, seed, XXH3_kSecret[:], XXH3_hashLong_64b_withSeed)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH3_64_with_secret :: proc(input, secret: []u8) -> (hash: xxh_u64) {
|
XXH3_64_with_secret :: proc(input, secret: []u8) -> (hash: xxh_u64) {
|
||||||
return XXH3_64bits_internal(input, 0, secret, XXH3_hashLong_64b_withSecret)
|
return XXH3_64bits_internal(input, 0, secret, XXH3_hashLong_64b_withSecret)
|
||||||
}
|
}
|
||||||
|
|
||||||
XXH3_64 :: proc { XXH3_64_default, XXH3_64_with_seed, XXH3_64_with_secret }
|
XXH3_64 :: proc { XXH3_64_default, XXH3_64_with_seed, XXH3_64_with_secret }
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ XXH_PRIME32_3 :: 0xC2B2AE3D /*!< 0b11000010101100101010111000111101 */
|
|||||||
XXH_PRIME32_4 :: 0x27D4EB2F /*!< 0b00100111110101001110101100101111 */
|
XXH_PRIME32_4 :: 0x27D4EB2F /*!< 0b00100111110101001110101100101111 */
|
||||||
XXH_PRIME32_5 :: 0x165667B1 /*!< 0b00010110010101100110011110110001 */
|
XXH_PRIME32_5 :: 0x165667B1 /*!< 0b00010110010101100110011110110001 */
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH32_round :: #force_inline proc(seed, input: XXH32_hash) -> (res: XXH32_hash) {
|
XXH32_round :: #force_inline proc(seed, input: XXH32_hash) -> (res: XXH32_hash) {
|
||||||
seed := seed
|
seed := seed
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ XXH32_round :: #force_inline proc(seed, input: XXH32_hash) -> (res: XXH32_hash)
|
|||||||
/*
|
/*
|
||||||
Mix all bits
|
Mix all bits
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH32_avalanche :: #force_inline proc(h32: u32) -> (res: u32) {
|
XXH32_avalanche :: #force_inline proc(h32: u32) -> (res: u32) {
|
||||||
h32 := h32
|
h32 := h32
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ XXH32_avalanche :: #force_inline proc(h32: u32) -> (res: u32) {
|
|||||||
return h32
|
return h32
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH32_finalize :: #force_inline proc(h32: u32, buf: []u8, alignment: Alignment) -> (res: u32) {
|
XXH32_finalize :: #force_inline proc(h32: u32, buf: []u8, alignment: Alignment) -> (res: u32) {
|
||||||
process_1 :: #force_inline proc(h32: u32, buf: []u8) -> (h32_res: u32, buf_res: []u8) {
|
process_1 :: #force_inline proc(h32: u32, buf: []u8) -> (h32_res: u32, buf_res: []u8) {
|
||||||
#no_bounds_check b := u32(buf[0])
|
#no_bounds_check b := u32(buf[0])
|
||||||
@@ -143,7 +143,7 @@ XXH32_finalize :: #force_inline proc(h32: u32, buf: []u8, alignment: Alignment)
|
|||||||
unreachable()
|
unreachable()
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH32_endian_align :: #force_inline proc(input: []u8, seed := XXH32_DEFAULT_SEED, alignment: Alignment) -> (res: XXH32_hash) {
|
XXH32_endian_align :: #force_inline proc(input: []u8, seed := XXH32_DEFAULT_SEED, alignment: Alignment) -> (res: XXH32_hash) {
|
||||||
buf := input
|
buf := input
|
||||||
length := len(input)
|
length := len(input)
|
||||||
@@ -318,4 +318,4 @@ XXH32_canonical_from_hash :: proc(hash: XXH32_hash) -> (canonical: XXH32_canonic
|
|||||||
XXH32_hash_from_canonical :: proc(canonical: ^XXH32_canonical) -> (hash: XXH32_hash) {
|
XXH32_hash_from_canonical :: proc(canonical: ^XXH32_canonical) -> (hash: XXH32_hash) {
|
||||||
h := (^u32be)(&canonical.digest)^
|
h := (^u32be)(&canonical.digest)^
|
||||||
return XXH32_hash(h)
|
return XXH32_hash(h)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ XXH_PRIME64_3 :: 0x165667B19E3779F9 /*!< 0b0001011001010110011001111011000110011
|
|||||||
XXH_PRIME64_4 :: 0x85EBCA77C2B2AE63 /*!< 0b1000010111101011110010100111011111000010101100101010111001100011 */
|
XXH_PRIME64_4 :: 0x85EBCA77C2B2AE63 /*!< 0b1000010111101011110010100111011111000010101100101010111001100011 */
|
||||||
XXH_PRIME64_5 :: 0x27D4EB2F165667C5 /*!< 0b0010011111010100111010110010111100010110010101100110011111000101 */
|
XXH_PRIME64_5 :: 0x27D4EB2F165667C5 /*!< 0b0010011111010100111010110010111100010110010101100110011111000101 */
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH64_round :: proc(acc, input: xxh_u64) -> (res: xxh_u64) {
|
XXH64_round :: proc(acc, input: xxh_u64) -> (res: xxh_u64) {
|
||||||
acc := acc
|
acc := acc
|
||||||
|
|
||||||
@@ -50,14 +50,14 @@ XXH64_round :: proc(acc, input: xxh_u64) -> (res: xxh_u64) {
|
|||||||
return acc
|
return acc
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH64_mergeRound :: proc(acc, val: xxh_u64) -> (res: xxh_u64) {
|
XXH64_mergeRound :: proc(acc, val: xxh_u64) -> (res: xxh_u64) {
|
||||||
res = acc ~ XXH64_round(0, val)
|
res = acc ~ XXH64_round(0, val)
|
||||||
res = res * XXH_PRIME64_1 + XXH_PRIME64_4
|
res = res * XXH_PRIME64_1 + XXH_PRIME64_4
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH64_avalanche :: proc(h64: xxh_u64) -> (res: xxh_u64) {
|
XXH64_avalanche :: proc(h64: xxh_u64) -> (res: xxh_u64) {
|
||||||
res = h64
|
res = h64
|
||||||
res ~= res >> 33
|
res ~= res >> 33
|
||||||
@@ -68,7 +68,7 @@ XXH64_avalanche :: proc(h64: xxh_u64) -> (res: xxh_u64) {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH64_finalize :: proc(h64: xxh_u64, buf: []u8, alignment: Alignment) -> (res: xxh_u64) {
|
XXH64_finalize :: proc(h64: xxh_u64, buf: []u8, alignment: Alignment) -> (res: xxh_u64) {
|
||||||
buf := buf
|
buf := buf
|
||||||
length := len(buf) & 31
|
length := len(buf) & 31
|
||||||
@@ -100,7 +100,7 @@ XXH64_finalize :: proc(h64: xxh_u64, buf: []u8, alignment: Alignment) -> (res: x
|
|||||||
return XXH64_avalanche(res)
|
return XXH64_avalanche(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH64_endian_align :: proc(input: []u8, seed := XXH64_DEFAULT_SEED, alignment := Alignment.Unaligned) -> (res: xxh_u64) {
|
XXH64_endian_align :: proc(input: []u8, seed := XXH64_DEFAULT_SEED, alignment := Alignment.Unaligned) -> (res: xxh_u64) {
|
||||||
buf := input
|
buf := input
|
||||||
length := len(buf)
|
length := len(buf)
|
||||||
@@ -191,7 +191,7 @@ XXH64_reset_state :: proc(state_ptr: ^XXH64_state, seed := XXH64_DEFAULT_SEED) -
|
|||||||
return .None
|
return .None
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH64_update :: proc(state: ^XXH64_state, input: []u8) -> (err: Error) {
|
XXH64_update :: proc(state: ^XXH64_state, input: []u8) -> (err: Error) {
|
||||||
buf := input
|
buf := input
|
||||||
length := len(buf)
|
length := len(buf)
|
||||||
@@ -245,7 +245,7 @@ XXH64_update :: proc(state: ^XXH64_state, input: []u8) -> (err: Error) {
|
|||||||
return .None
|
return .None
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
XXH64_digest :: proc(state: ^XXH64_state) -> (res: XXH64_hash) {
|
XXH64_digest :: proc(state: ^XXH64_state) -> (res: XXH64_hash) {
|
||||||
if state.total_len >= 32 {
|
if state.total_len >= 32 {
|
||||||
v1 := state.v1
|
v1 := state.v1
|
||||||
@@ -292,4 +292,4 @@ XXH64_canonical_from_hash :: proc(hash: XXH64_hash) -> (canonical: XXH64_canonic
|
|||||||
XXH64_hash_from_canonical :: proc(canonical: ^XXH64_canonical) -> (hash: XXH64_hash) {
|
XXH64_hash_from_canonical :: proc(canonical: ^XXH64_canonical) -> (hash: XXH64_hash) {
|
||||||
h := (^u64be)(&canonical.digest)^
|
h := (^u64be)(&canonical.digest)^
|
||||||
return XXH64_hash(h)
|
return XXH64_hash(h)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context
|
|||||||
return img, err
|
return img, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||||
context.allocator = allocator
|
context.allocator = allocator
|
||||||
options := options
|
options := options
|
||||||
@@ -743,4 +743,4 @@ destroy :: proc(img: ^Image) {
|
|||||||
@(init, private)
|
@(init, private)
|
||||||
_register :: proc() {
|
_register :: proc() {
|
||||||
image.register(.BMP, load_from_bytes, destroy)
|
image.register(.BMP, load_from_bytes, destroy)
|
||||||
}
|
}
|
||||||
|
|||||||
+67
-4
@@ -112,7 +112,8 @@ Image_Option:
|
|||||||
|
|
||||||
`.alpha_drop_if_present`
|
`.alpha_drop_if_present`
|
||||||
If the image has an alpha channel, drop it.
|
If the image has an alpha channel, drop it.
|
||||||
You may want to use `.alpha_premultiply` in this case.
|
You may want to use `.alpha_
|
||||||
|
tiply` in this case.
|
||||||
|
|
||||||
NOTE: For PNG, this also skips handling of the tRNS chunk, if present,
|
NOTE: For PNG, this also skips handling of the tRNS chunk, if present,
|
||||||
unless you select `alpha_premultiply`.
|
unless you select `alpha_premultiply`.
|
||||||
@@ -587,6 +588,32 @@ Channel :: enum u8 {
|
|||||||
A = 4,
|
A = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Take a slice of pixels (`[]RGBA_Pixel`, etc), and return an `Image`
|
||||||
|
// Don't call `destroy` on the resulting `Image`. Instead, delete the original `pixels` slice.
|
||||||
|
pixels_to_image :: proc(pixels: [][$N]$E, width: int, height: int) -> (img: Image, ok: bool) where E == u8 || E == u16, N >= 1 && N <= 4 {
|
||||||
|
if len(pixels) != width * height {
|
||||||
|
return {}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
img.height = height
|
||||||
|
img.width = width
|
||||||
|
img.depth = 8 when E == u8 else 16
|
||||||
|
img.channels = N
|
||||||
|
|
||||||
|
s := transmute(runtime.Raw_Slice)pixels
|
||||||
|
d := runtime.Raw_Dynamic_Array{
|
||||||
|
data = s.data,
|
||||||
|
len = s.len * size_of(E) * N,
|
||||||
|
cap = s.len * size_of(E) * N,
|
||||||
|
allocator = runtime.nil_allocator(),
|
||||||
|
}
|
||||||
|
img.pixels = bytes.Buffer{
|
||||||
|
buf = transmute([dynamic]u8)d,
|
||||||
|
}
|
||||||
|
|
||||||
|
return img, true
|
||||||
|
}
|
||||||
|
|
||||||
// When you have an RGB(A) image, but want a particular channel.
|
// When you have an RGB(A) image, but want a particular channel.
|
||||||
return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok: bool) {
|
return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok: bool) {
|
||||||
// Were we actually given a valid image?
|
// Were we actually given a valid image?
|
||||||
@@ -1293,6 +1320,42 @@ blend_pixel :: #force_inline proc(fg: [$N]$T, alpha: T, bg: [N]T) -> (res: [N]T)
|
|||||||
}
|
}
|
||||||
blend :: proc{blend_single_channel, blend_pixel}
|
blend :: proc{blend_single_channel, blend_pixel}
|
||||||
|
|
||||||
|
// For all pixels of the image, multiplies R, G and B by Alpha. This is useful mainly for games rendering anti-aliased transparent sprites.
|
||||||
|
// Grayscale with alpha images are supported as well.
|
||||||
|
// Note that some image formats like QOI explicitly do NOT support premultiplied alpha, so you will end up with a non-standard file.
|
||||||
|
premultiply_alpha :: proc(img: ^Image) -> (ok: bool) {
|
||||||
|
switch {
|
||||||
|
case img.channels == 2 && img.depth == 8:
|
||||||
|
pixels := mem.slice_data_cast([]GA_Pixel, img.pixels.buf[:])
|
||||||
|
for &pixel in pixels {
|
||||||
|
pixel.r = u8(u32(pixel.r) * u32(pixel.g) / 0xFF)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case img.channels == 2 && img.depth == 16:
|
||||||
|
pixels := mem.slice_data_cast([]GA_Pixel_16, img.pixels.buf[:])
|
||||||
|
for &pixel in pixels {
|
||||||
|
pixel.r = u16(u32(pixel.r) * u32(pixel.g) / 0xFFFF)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case img.channels == 4 && img.depth == 8:
|
||||||
|
pixels := mem.slice_data_cast([]RGBA_Pixel, img.pixels.buf[:])
|
||||||
|
for &pixel in pixels {
|
||||||
|
pixel.r = u8(u32(pixel.r) * u32(pixel.a) / 0xFF)
|
||||||
|
pixel.g = u8(u32(pixel.g) * u32(pixel.a) / 0xFF)
|
||||||
|
pixel.b = u8(u32(pixel.b) * u32(pixel.a) / 0xFF)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case img.channels == 4 && img.depth == 16:
|
||||||
|
pixels := mem.slice_data_cast([]RGBA_Pixel_16, img.pixels.buf[:])
|
||||||
|
for &pixel in pixels {
|
||||||
|
pixel.r = u16(u32(pixel.r) * u32(pixel.a) / 0xFFFF)
|
||||||
|
pixel.g = u16(u32(pixel.g) * u32(pixel.a) / 0xFFFF)
|
||||||
|
pixel.b = u16(u32(pixel.b) * u32(pixel.a) / 0xFFFF)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
case: return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Replicates grayscale values into RGB(A) 8- or 16-bit images as appropriate.
|
// Replicates grayscale values into RGB(A) 8- or 16-bit images as appropriate.
|
||||||
// Returns early with `false` if already an RGB(A) image.
|
// Returns early with `false` if already an RGB(A) image.
|
||||||
@@ -1376,7 +1439,7 @@ expand_grayscale :: proc(img: ^Image, allocator := context.allocator) -> (ok: bo
|
|||||||
/*
|
/*
|
||||||
Helper functions to read and write data from/to a Context, etc.
|
Helper functions to read and write data from/to a Context, etc.
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_data :: proc(z: $C, $T: typeid) -> (res: T, err: compress.General_Error) {
|
read_data :: proc(z: $C, $T: typeid) -> (res: T, err: compress.General_Error) {
|
||||||
if r, e := compress.read_data(z, T); e != .None {
|
if r, e := compress.read_data(z, T); e != .None {
|
||||||
return {}, .Stream_Too_Short
|
return {}, .Stream_Too_Short
|
||||||
@@ -1385,7 +1448,7 @@ read_data :: proc(z: $C, $T: typeid) -> (res: T, err: compress.General_Error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_u8 :: proc(z: $C) -> (res: u8, err: compress.General_Error) {
|
read_u8 :: proc(z: $C) -> (res: u8, err: compress.General_Error) {
|
||||||
if r, e := compress.read_u8(z); e != .None {
|
if r, e := compress.read_u8(z); e != .None {
|
||||||
return {}, .Stream_Too_Short
|
return {}, .Stream_Too_Short
|
||||||
@@ -1405,4 +1468,4 @@ write_bytes :: proc(buf: ^bytes.Buffer, data: []u8) -> (err: compress.General_Er
|
|||||||
return .Resize_Failed
|
return .Resize_Failed
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ which :: proc{
|
|||||||
|
|
||||||
which_file :: proc(path: string) -> Which_File_Type {
|
which_file :: proc(path: string) -> Which_File_Type {
|
||||||
f, err := os.open(path)
|
f, err := os.open(path)
|
||||||
if err != 0 {
|
if err != nil {
|
||||||
return .Unknown
|
return .Unknown
|
||||||
}
|
}
|
||||||
header: [128]byte
|
header: [128]byte
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: b
|
|||||||
}
|
}
|
||||||
|
|
||||||
fd, err := open(filename, flags, mode)
|
fd, err := open(filename, flags, mode)
|
||||||
if err != 0 {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
defer close(fd)
|
defer close(fd)
|
||||||
|
|||||||
@@ -450,7 +450,7 @@ when false {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fd, fderr := open(filename, flags, mode)
|
fd, fderr := open(filename, flags, mode)
|
||||||
if fderr != 0 {
|
if fderr != nil {
|
||||||
return .Cannot_Open_File
|
return .Cannot_Open_File
|
||||||
}
|
}
|
||||||
defer close(fd)
|
defer close(fd)
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context
|
|||||||
return img, err
|
return img, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||||
context.allocator = allocator
|
context.allocator = allocator
|
||||||
options := options
|
options := options
|
||||||
@@ -373,4 +373,4 @@ qoi_hash :: #force_inline proc(pixel: RGBA_Pixel) -> (index: u8) {
|
|||||||
@(init, private)
|
@(init, private)
|
||||||
_register :: proc() {
|
_register :: proc() {
|
||||||
image.register(.QOI, load_from_bytes, destroy)
|
image.register(.QOI, load_from_bytes, destroy)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -375,10 +375,6 @@ write_at_least :: proc(w: Writer, buf: []byte, min: int) -> (n: int, err: Error)
|
|||||||
nn, err = write(w, buf[n:])
|
nn, err = write(w, buf[n:])
|
||||||
n += nn
|
n += nn
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil && n < min {
|
|
||||||
err = .Short_Write
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -229,7 +229,7 @@ sqrt_complex128 :: proc "contextless" (x: complex128) -> complex128 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ln_complex32 :: proc "contextless" (x: complex32) -> complex32 {
|
ln_complex32 :: proc "contextless" (x: complex32) -> complex32 {
|
||||||
return complex(math.ln(abs(x)), phase(x))
|
return complex32(ln_complex64(complex64(x)))
|
||||||
}
|
}
|
||||||
ln_complex64 :: proc "contextless" (x: complex64) -> complex64 {
|
ln_complex64 :: proc "contextless" (x: complex64) -> complex64 {
|
||||||
return complex(math.ln(abs(x)), phase(x))
|
return complex(math.ln(abs(x)), phase(x))
|
||||||
@@ -240,26 +240,7 @@ ln_complex128 :: proc "contextless" (x: complex128) -> complex128 {
|
|||||||
|
|
||||||
|
|
||||||
exp_complex32 :: proc "contextless" (x: complex32) -> complex32 {
|
exp_complex32 :: proc "contextless" (x: complex32) -> complex32 {
|
||||||
switch re, im := real(x), imag(x); {
|
return complex32(exp_complex64(complex64(x)))
|
||||||
case math.is_inf(re, 0):
|
|
||||||
switch {
|
|
||||||
case re > 0 && im == 0:
|
|
||||||
return x
|
|
||||||
case math.is_inf(im, 0) || math.is_nan(im):
|
|
||||||
if re < 0 {
|
|
||||||
return complex(0, math.copy_sign(0, im))
|
|
||||||
} else {
|
|
||||||
return complex(math.inf_f64(1.0), math.nan_f64())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case math.is_nan(re):
|
|
||||||
if im == 0 {
|
|
||||||
return complex(math.nan_f16(), im)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
r := math.exp(real(x))
|
|
||||||
s, c := math.sincos(imag(x))
|
|
||||||
return complex(r*c, r*s)
|
|
||||||
}
|
}
|
||||||
exp_complex64 :: proc "contextless" (x: complex64) -> complex64 {
|
exp_complex64 :: proc "contextless" (x: complex64) -> complex64 {
|
||||||
switch re, im := real(x), imag(x); {
|
switch re, im := real(x), imag(x); {
|
||||||
@@ -308,37 +289,7 @@ exp_complex128 :: proc "contextless" (x: complex128) -> complex128 {
|
|||||||
|
|
||||||
|
|
||||||
pow_complex32 :: proc "contextless" (x, y: complex32) -> complex32 {
|
pow_complex32 :: proc "contextless" (x, y: complex32) -> complex32 {
|
||||||
if x == 0 { // Guaranteed also true for x == -0.
|
return complex32(pow_complex64(complex64(x), complex64(y)))
|
||||||
if is_nan(y) {
|
|
||||||
return nan_complex32()
|
|
||||||
}
|
|
||||||
r, i := real(y), imag(y)
|
|
||||||
switch {
|
|
||||||
case r == 0:
|
|
||||||
return 1
|
|
||||||
case r < 0:
|
|
||||||
if i == 0 {
|
|
||||||
return complex(math.inf_f16(1), 0)
|
|
||||||
}
|
|
||||||
return inf_complex32()
|
|
||||||
case r > 0:
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
unreachable()
|
|
||||||
}
|
|
||||||
modulus := abs(x)
|
|
||||||
if modulus == 0 {
|
|
||||||
return complex(0, 0)
|
|
||||||
}
|
|
||||||
r := math.pow(modulus, real(y))
|
|
||||||
arg := phase(x)
|
|
||||||
theta := real(y) * arg
|
|
||||||
if imag(y) != 0 {
|
|
||||||
r *= math.exp(-imag(y) * arg)
|
|
||||||
theta += imag(y) * math.ln(modulus)
|
|
||||||
}
|
|
||||||
s, c := math.sincos(theta)
|
|
||||||
return complex(r*c, r*s)
|
|
||||||
}
|
}
|
||||||
pow_complex64 :: proc "contextless" (x, y: complex64) -> complex64 {
|
pow_complex64 :: proc "contextless" (x, y: complex64) -> complex64 {
|
||||||
if x == 0 { // Guaranteed also true for x == -0.
|
if x == 0 { // Guaranteed also true for x == -0.
|
||||||
@@ -410,7 +361,7 @@ pow_complex128 :: proc "contextless" (x, y: complex128) -> complex128 {
|
|||||||
|
|
||||||
|
|
||||||
log10_complex32 :: proc "contextless" (x: complex32) -> complex32 {
|
log10_complex32 :: proc "contextless" (x: complex32) -> complex32 {
|
||||||
return math.LN10*ln(x)
|
return complex32(log10_complex64(complex64(x)))
|
||||||
}
|
}
|
||||||
log10_complex64 :: proc "contextless" (x: complex64) -> complex64 {
|
log10_complex64 :: proc "contextless" (x: complex64) -> complex64 {
|
||||||
return math.LN10*ln(x)
|
return math.LN10*ln(x)
|
||||||
@@ -421,7 +372,7 @@ log10_complex128 :: proc "contextless" (x: complex128) -> complex128 {
|
|||||||
|
|
||||||
|
|
||||||
phase_complex32 :: proc "contextless" (x: complex32) -> f16 {
|
phase_complex32 :: proc "contextless" (x: complex32) -> f16 {
|
||||||
return math.atan2(imag(x), real(x))
|
return f16(phase_complex64(complex64(x)))
|
||||||
}
|
}
|
||||||
phase_complex64 :: proc "contextless" (x: complex64) -> f32 {
|
phase_complex64 :: proc "contextless" (x: complex64) -> f32 {
|
||||||
return math.atan2(imag(x), real(x))
|
return math.atan2(imag(x), real(x))
|
||||||
@@ -432,8 +383,7 @@ phase_complex128 :: proc "contextless" (x: complex128) -> f64 {
|
|||||||
|
|
||||||
|
|
||||||
rect_complex32 :: proc "contextless" (r, θ: f16) -> complex32 {
|
rect_complex32 :: proc "contextless" (r, θ: f16) -> complex32 {
|
||||||
s, c := math.sincos(θ)
|
return complex32(rect_complex64(f32(r), f32(θ)))
|
||||||
return complex(r*c, r*s)
|
|
||||||
}
|
}
|
||||||
rect_complex64 :: proc "contextless" (r, θ: f32) -> complex64 {
|
rect_complex64 :: proc "contextless" (r, θ: f32) -> complex64 {
|
||||||
s, c := math.sincos(θ)
|
s, c := math.sincos(θ)
|
||||||
|
|||||||
@@ -61,8 +61,7 @@ atanh :: proc{
|
|||||||
|
|
||||||
|
|
||||||
acos_complex32 :: proc "contextless" (x: complex32) -> complex32 {
|
acos_complex32 :: proc "contextless" (x: complex32) -> complex32 {
|
||||||
w := asin(x)
|
return complex32(acos_complex64(complex64(x)))
|
||||||
return complex(math.PI/2 - real(w), -imag(w))
|
|
||||||
}
|
}
|
||||||
acos_complex64 :: proc "contextless" (x: complex64) -> complex64 {
|
acos_complex64 :: proc "contextless" (x: complex64) -> complex64 {
|
||||||
w := asin(x)
|
w := asin(x)
|
||||||
@@ -75,14 +74,7 @@ acos_complex128 :: proc "contextless" (x: complex128) -> complex128 {
|
|||||||
|
|
||||||
|
|
||||||
acosh_complex32 :: proc "contextless" (x: complex32) -> complex32 {
|
acosh_complex32 :: proc "contextless" (x: complex32) -> complex32 {
|
||||||
if x == 0 {
|
return complex32(acosh_complex64(complex64(x)))
|
||||||
return complex(0, math.copy_sign(math.PI/2, imag(x)))
|
|
||||||
}
|
|
||||||
w := acos(x)
|
|
||||||
if imag(w) <= 0 {
|
|
||||||
return complex(-imag(w), real(w))
|
|
||||||
}
|
|
||||||
return complex(imag(w), -real(w))
|
|
||||||
}
|
}
|
||||||
acosh_complex64 :: proc "contextless" (x: complex64) -> complex64 {
|
acosh_complex64 :: proc "contextless" (x: complex64) -> complex64 {
|
||||||
if x == 0 {
|
if x == 0 {
|
||||||
@@ -257,9 +249,7 @@ atan_complex128 :: proc "contextless" (x: complex128) -> complex128 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
atanh_complex32 :: proc "contextless" (x: complex32) -> complex32 {
|
atanh_complex32 :: proc "contextless" (x: complex32) -> complex32 {
|
||||||
z := complex(-imag(x), real(x)) // z = i * x
|
return complex32(atanh_complex64(complex64(x)))
|
||||||
z = atan(z)
|
|
||||||
return complex(imag(z), -real(z)) // z = -i * z
|
|
||||||
}
|
}
|
||||||
atanh_complex64 :: proc "contextless" (x: complex64) -> complex64 {
|
atanh_complex64 :: proc "contextless" (x: complex64) -> complex64 {
|
||||||
z := complex(-imag(x), real(x)) // z = i * x
|
z := complex(-imag(x), real(x)) // z = i * x
|
||||||
|
|||||||
@@ -275,7 +275,8 @@ to_ptr :: proc{vector_to_ptr, matrix_to_ptr}
|
|||||||
vector_angle_between :: proc "contextless" (a, b: $V/[$N]$E) -> E {
|
vector_angle_between :: proc "contextless" (a, b: $V/[$N]$E) -> E {
|
||||||
a0 := normalize0(a)
|
a0 := normalize0(a)
|
||||||
b0 := normalize0(b)
|
b0 := normalize0(b)
|
||||||
return math.acos(dot(a0, b0))
|
d := clamp(dot(a0, b0), -1, +1)
|
||||||
|
return math.acos(d)
|
||||||
}
|
}
|
||||||
quaternion64_angle_between :: proc "contextless" (a, b: $Q/quaternion64) -> f16 {
|
quaternion64_angle_between :: proc "contextless" (a, b: $Q/quaternion64) -> f16 {
|
||||||
c := normalize0(conj(a) * b)
|
c := normalize0(conj(a) * b)
|
||||||
|
|||||||
@@ -688,7 +688,7 @@ _internal_noise_4d_unskewed_base :: proc(seed: i64, coord: Vec4) -> (value: f32)
|
|||||||
/*
|
/*
|
||||||
Utility functions
|
Utility functions
|
||||||
*/
|
*/
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
grad_2d :: proc(seed: i64, svp: [2]i64, delta: [2]f32) -> (value: f32) {
|
grad_2d :: proc(seed: i64, svp: [2]i64, delta: [2]f32) -> (value: f32) {
|
||||||
hash := seed ~ svp.x ~ svp.y
|
hash := seed ~ svp.x ~ svp.y
|
||||||
hash *= HASH_MULTIPLIER
|
hash *= HASH_MULTIPLIER
|
||||||
@@ -698,7 +698,7 @@ grad_2d :: proc(seed: i64, svp: [2]i64, delta: [2]f32) -> (value: f32) {
|
|||||||
return GRADIENTS_2D[gi] * delta.x + GRADIENTS_2D[gi | 1] * delta.y
|
return GRADIENTS_2D[gi] * delta.x + GRADIENTS_2D[gi | 1] * delta.y
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
grad_3d :: proc(seed: i64, rvp: [3]i64, delta: [3]f32) -> (value: f32) {
|
grad_3d :: proc(seed: i64, rvp: [3]i64, delta: [3]f32) -> (value: f32) {
|
||||||
hash := (seed ~ rvp.x) ~ (rvp.y ~ rvp.z)
|
hash := (seed ~ rvp.x) ~ (rvp.y ~ rvp.z)
|
||||||
hash *= HASH_MULTIPLIER
|
hash *= HASH_MULTIPLIER
|
||||||
@@ -708,7 +708,7 @@ grad_3d :: proc(seed: i64, rvp: [3]i64, delta: [3]f32) -> (value: f32) {
|
|||||||
return GRADIENTS_3D[gi] * delta.x + GRADIENTS_3D[gi | 1] * delta.y + GRADIENTS_3D[gi | 2] * delta.z
|
return GRADIENTS_3D[gi] * delta.x + GRADIENTS_3D[gi | 1] * delta.y + GRADIENTS_3D[gi | 2] * delta.z
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
grad_4d :: proc(seed: i64, svp: [4]i64, delta: [4]f32) -> (value: f32) {
|
grad_4d :: proc(seed: i64, svp: [4]i64, delta: [4]f32) -> (value: f32) {
|
||||||
hash := seed ~ (svp.x ~ svp.y) ~ (svp.z ~ svp.w)
|
hash := seed ~ (svp.x ~ svp.y) ~ (svp.z ~ svp.w)
|
||||||
hash *= HASH_MULTIPLIER
|
hash *= HASH_MULTIPLIER
|
||||||
@@ -720,13 +720,13 @@ grad_4d :: proc(seed: i64, svp: [4]i64, delta: [4]f32) -> (value: f32) {
|
|||||||
|
|
||||||
grad :: proc {grad_2d, grad_3d, grad_4d}
|
grad :: proc {grad_2d, grad_3d, grad_4d}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
fast_floor :: proc(x: f64) -> (floored: i64) {
|
fast_floor :: proc(x: f64) -> (floored: i64) {
|
||||||
xi := i64(x)
|
xi := i64(x)
|
||||||
return x < f64(xi) ? xi - 1 : xi
|
return x < f64(xi) ? xi - 1 : xi
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
fast_round :: proc(x: f64) -> (rounded: i64) {
|
fast_round :: proc(x: f64) -> (rounded: i64) {
|
||||||
return x < 0 ? i64(x - 0.5) : i64(x + 0.5)
|
return x < 0 ? i64(x - 0.5) : i64(x + 0.5)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ float32_uniform :: float32_range
|
|||||||
// Triangular Distribution
|
// Triangular Distribution
|
||||||
// See: http://wikipedia.org/wiki/Triangular_distribution
|
// See: http://wikipedia.org/wiki/Triangular_distribution
|
||||||
@(require_results)
|
@(require_results)
|
||||||
float64_triangular :: proc(lo, hi: f64, mode: Maybe(f64)) -> f64 {
|
float64_triangular :: proc(lo, hi: f64, mode: Maybe(f64), gen := context.random_generator) -> f64 {
|
||||||
if hi-lo == 0 {
|
if hi-lo == 0 {
|
||||||
return lo
|
return lo
|
||||||
}
|
}
|
||||||
lo, hi := lo, hi
|
lo, hi := lo, hi
|
||||||
u := float64()
|
u := float64(gen)
|
||||||
c := f64(0.5) if mode == nil else clamp((mode.?-lo) / (hi-lo), 0, 1)
|
c := f64(0.5) if mode == nil else clamp((mode.?-lo) / (hi-lo), 0, 1)
|
||||||
if u > c {
|
if u > c {
|
||||||
u = 1-u
|
u = 1-u
|
||||||
@@ -26,12 +26,12 @@ float64_triangular :: proc(lo, hi: f64, mode: Maybe(f64)) -> f64 {
|
|||||||
// Triangular Distribution
|
// Triangular Distribution
|
||||||
// See: http://wikipedia.org/wiki/Triangular_distribution
|
// See: http://wikipedia.org/wiki/Triangular_distribution
|
||||||
@(require_results)
|
@(require_results)
|
||||||
float32_triangular :: proc(lo, hi: f32, mode: Maybe(f32)) -> f32 {
|
float32_triangular :: proc(lo, hi: f32, mode: Maybe(f32), gen := context.random_generator) -> f32 {
|
||||||
if hi-lo == 0 {
|
if hi-lo == 0 {
|
||||||
return lo
|
return lo
|
||||||
}
|
}
|
||||||
lo, hi := lo, hi
|
lo, hi := lo, hi
|
||||||
u := float32()
|
u := float32(gen)
|
||||||
c := f32(0.5) if mode == nil else clamp((mode.?-lo) / (hi-lo), 0, 1)
|
c := f32(0.5) if mode == nil else clamp((mode.?-lo) / (hi-lo), 0, 1)
|
||||||
if u > c {
|
if u > c {
|
||||||
u = 1-u
|
u = 1-u
|
||||||
@@ -44,25 +44,25 @@ float32_triangular :: proc(lo, hi: f32, mode: Maybe(f32)) -> f32 {
|
|||||||
|
|
||||||
// Normal/Gaussian Distribution
|
// Normal/Gaussian Distribution
|
||||||
@(require_results)
|
@(require_results)
|
||||||
float64_normal :: proc(mean, stddev: f64) -> f64 {
|
float64_normal :: proc(mean, stddev: f64, gen := context.random_generator) -> f64 {
|
||||||
return norm_float64() * stddev + mean
|
return norm_float64(gen) * stddev + mean
|
||||||
}
|
}
|
||||||
// Normal/Gaussian Distribution
|
// Normal/Gaussian Distribution
|
||||||
@(require_results)
|
@(require_results)
|
||||||
float32_normal :: proc(mean, stddev: f32) -> f32 {
|
float32_normal :: proc(mean, stddev: f32, gen := context.random_generator) -> f32 {
|
||||||
return f32(float64_normal(f64(mean), f64(stddev)))
|
return f32(float64_normal(f64(mean), f64(stddev), gen))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Log Normal Distribution
|
// Log Normal Distribution
|
||||||
@(require_results)
|
@(require_results)
|
||||||
float64_log_normal :: proc(mean, stddev: f64) -> f64 {
|
float64_log_normal :: proc(mean, stddev: f64, gen := context.random_generator) -> f64 {
|
||||||
return math.exp(float64_normal(mean, stddev))
|
return math.exp(float64_normal(mean, stddev, gen))
|
||||||
}
|
}
|
||||||
// Log Normal Distribution
|
// Log Normal Distribution
|
||||||
@(require_results)
|
@(require_results)
|
||||||
float32_log_normal :: proc(mean, stddev: f32) -> f32 {
|
float32_log_normal :: proc(mean, stddev: f32, gen := context.random_generator) -> f32 {
|
||||||
return f32(float64_log_normal(f64(mean), f64(stddev)))
|
return f32(float64_log_normal(f64(mean), f64(stddev), gen))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -72,8 +72,8 @@ float32_log_normal :: proc(mean, stddev: f32) -> f32 {
|
|||||||
// 0 to positive infinity if lambda > 0
|
// 0 to positive infinity if lambda > 0
|
||||||
// negative infinity to 0 if lambda <= 0
|
// negative infinity to 0 if lambda <= 0
|
||||||
@(require_results)
|
@(require_results)
|
||||||
float64_exponential :: proc(lambda: f64) -> f64 {
|
float64_exponential :: proc(lambda: f64, gen := context.random_generator) -> f64 {
|
||||||
return - math.ln(1 - float64()) / lambda
|
return - math.ln(1 - float64(gen)) / lambda
|
||||||
}
|
}
|
||||||
// Exponential Distribution
|
// Exponential Distribution
|
||||||
// `lambda` is 1.0/(desired mean). It should be non-zero.
|
// `lambda` is 1.0/(desired mean). It should be non-zero.
|
||||||
@@ -81,8 +81,8 @@ float64_exponential :: proc(lambda: f64) -> f64 {
|
|||||||
// 0 to positive infinity if lambda > 0
|
// 0 to positive infinity if lambda > 0
|
||||||
// negative infinity to 0 if lambda <= 0
|
// negative infinity to 0 if lambda <= 0
|
||||||
@(require_results)
|
@(require_results)
|
||||||
float32_exponential :: proc(lambda: f32) -> f32 {
|
float32_exponential :: proc(lambda: f32, gen := context.random_generator) -> f32 {
|
||||||
return f32(float64_exponential(f64(lambda)))
|
return f32(float64_exponential(f64(lambda), gen))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ float32_exponential :: proc(lambda: f32) -> f32 {
|
|||||||
//
|
//
|
||||||
// mean is alpha*beta, variance is math.pow(alpha*beta, 2)
|
// mean is alpha*beta, variance is math.pow(alpha*beta, 2)
|
||||||
@(require_results)
|
@(require_results)
|
||||||
float64_gamma :: proc(alpha, beta: f64) -> f64 {
|
float64_gamma :: proc(alpha, beta: f64, gen := context.random_generator) -> f64 {
|
||||||
if alpha <= 0 || beta <= 0 {
|
if alpha <= 0 || beta <= 0 {
|
||||||
panic(#procedure + ": alpha and beta must be > 0.0")
|
panic(#procedure + ": alpha and beta must be > 0.0")
|
||||||
}
|
}
|
||||||
@@ -112,11 +112,11 @@ float64_gamma :: proc(alpha, beta: f64) -> f64 {
|
|||||||
bbb := alpha - LOG4
|
bbb := alpha - LOG4
|
||||||
ccc := alpha + ainv
|
ccc := alpha + ainv
|
||||||
for {
|
for {
|
||||||
u1 := float64()
|
u1 := float64(gen)
|
||||||
if !(1e-7 < u1 && u1 < 0.9999999) {
|
if !(1e-7 < u1 && u1 < 0.9999999) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
u2 := 1 - float64()
|
u2 := 1 - float64(gen)
|
||||||
v := math.ln(u1 / (1 - u1)) / ainv
|
v := math.ln(u1 / (1 - u1)) / ainv
|
||||||
x := alpha * math.exp(v)
|
x := alpha * math.exp(v)
|
||||||
z := u1 * u1 * u2
|
z := u1 * u1 * u2
|
||||||
@@ -127,12 +127,12 @@ float64_gamma :: proc(alpha, beta: f64) -> f64 {
|
|||||||
}
|
}
|
||||||
case alpha == 1:
|
case alpha == 1:
|
||||||
// float64_exponential(1/beta)
|
// float64_exponential(1/beta)
|
||||||
return -math.ln(1 - float64()) * beta
|
return -math.ln(1 - float64(gen)) * beta
|
||||||
case:
|
case:
|
||||||
// ALGORITHM GS of Statistical Computing - Kennedy & Gentle
|
// ALGORITHM GS of Statistical Computing - Kennedy & Gentle
|
||||||
x: f64
|
x: f64
|
||||||
for {
|
for {
|
||||||
u := float64()
|
u := float64(gen)
|
||||||
b := (math.e + alpha) / math.e
|
b := (math.e + alpha) / math.e
|
||||||
p := b * u
|
p := b * u
|
||||||
if p <= 1 {
|
if p <= 1 {
|
||||||
@@ -140,7 +140,7 @@ float64_gamma :: proc(alpha, beta: f64) -> f64 {
|
|||||||
} else {
|
} else {
|
||||||
x = -math.ln((b - p) / alpha)
|
x = -math.ln((b - p) / alpha)
|
||||||
}
|
}
|
||||||
u1 := float64()
|
u1 := float64(gen)
|
||||||
if p > 1 {
|
if p > 1 {
|
||||||
if u1 <= math.pow(x, alpha-1) {
|
if u1 <= math.pow(x, alpha-1) {
|
||||||
break
|
break
|
||||||
@@ -162,8 +162,8 @@ float64_gamma :: proc(alpha, beta: f64) -> f64 {
|
|||||||
//
|
//
|
||||||
// mean is alpha*beta, variance is math.pow(alpha*beta, 2)
|
// mean is alpha*beta, variance is math.pow(alpha*beta, 2)
|
||||||
@(require_results)
|
@(require_results)
|
||||||
float32_gamma :: proc(alpha, beta: f32) -> f32 {
|
float32_gamma :: proc(alpha, beta: f32, gen := context.random_generator) -> f32 {
|
||||||
return f32(float64_gamma(f64(alpha), f64(beta)))
|
return f32(float64_gamma(f64(alpha), f64(beta), gen))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -173,14 +173,14 @@ float32_gamma :: proc(alpha, beta: f32) -> f32 {
|
|||||||
//
|
//
|
||||||
// Return values range between 0 and 1
|
// Return values range between 0 and 1
|
||||||
@(require_results)
|
@(require_results)
|
||||||
float64_beta :: proc(alpha, beta: f64) -> f64 {
|
float64_beta :: proc(alpha, beta: f64, gen := context.random_generator) -> f64 {
|
||||||
if alpha <= 0 || beta <= 0 {
|
if alpha <= 0 || beta <= 0 {
|
||||||
panic(#procedure + ": alpha and beta must be > 0.0")
|
panic(#procedure + ": alpha and beta must be > 0.0")
|
||||||
}
|
}
|
||||||
// Knuth Vol 2 Ed 3 pg 134 "the beta distribution"
|
// Knuth Vol 2 Ed 3 pg 134 "the beta distribution"
|
||||||
y := float64_gamma(alpha, 1.0)
|
y := float64_gamma(alpha, 1.0, gen)
|
||||||
if y != 0 {
|
if y != 0 {
|
||||||
return y / (y + float64_gamma(beta, 1.0))
|
return y / (y + float64_gamma(beta, 1.0, gen))
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -190,35 +190,35 @@ float64_beta :: proc(alpha, beta: f64) -> f64 {
|
|||||||
//
|
//
|
||||||
// Return values range between 0 and 1
|
// Return values range between 0 and 1
|
||||||
@(require_results)
|
@(require_results)
|
||||||
float32_beta :: proc(alpha, beta: f32) -> f32 {
|
float32_beta :: proc(alpha, beta: f32, gen := context.random_generator) -> f32 {
|
||||||
return f32(float64_beta(f64(alpha), f64(beta)))
|
return f32(float64_beta(f64(alpha), f64(beta), gen))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Pareto distribution, `alpha` is the shape parameter.
|
// Pareto distribution, `alpha` is the shape parameter.
|
||||||
// https://wikipedia.org/wiki/Pareto_distribution
|
// https://wikipedia.org/wiki/Pareto_distribution
|
||||||
@(require_results)
|
@(require_results)
|
||||||
float64_pareto :: proc(alpha: f64) -> f64 {
|
float64_pareto :: proc(alpha: f64, gen := context.random_generator) -> f64 {
|
||||||
return math.pow(1 - float64(), -1.0 / alpha)
|
return math.pow(1 - float64(gen), -1.0 / alpha)
|
||||||
}
|
}
|
||||||
// Pareto distribution, `alpha` is the shape parameter.
|
// Pareto distribution, `alpha` is the shape parameter.
|
||||||
// https://wikipedia.org/wiki/Pareto_distribution
|
// https://wikipedia.org/wiki/Pareto_distribution
|
||||||
@(require_results)
|
@(require_results)
|
||||||
float32_pareto :: proc(alpha, beta: f32) -> f32 {
|
float32_pareto :: proc(alpha, beta: f32, gen := context.random_generator) -> f32 {
|
||||||
return f32(float64_pareto(f64(alpha)))
|
return f32(float64_pareto(f64(alpha), gen))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Weibull distribution, `alpha` is the scale parameter, `beta` is the shape parameter.
|
// Weibull distribution, `alpha` is the scale parameter, `beta` is the shape parameter.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
float64_weibull :: proc(alpha, beta: f64) -> f64 {
|
float64_weibull :: proc(alpha, beta: f64, gen := context.random_generator) -> f64 {
|
||||||
u := 1 - float64()
|
u := 1 - float64(gen)
|
||||||
return alpha * math.pow(-math.ln(u), 1.0/beta)
|
return alpha * math.pow(-math.ln(u), 1.0/beta)
|
||||||
}
|
}
|
||||||
// Weibull distribution, `alpha` is the scale parameter, `beta` is the shape parameter.
|
// Weibull distribution, `alpha` is the scale parameter, `beta` is the shape parameter.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
float32_weibull :: proc(alpha, beta: f32) -> f32 {
|
float32_weibull :: proc(alpha, beta: f32, gen := context.random_generator) -> f32 {
|
||||||
return f32(float64_weibull(f64(alpha), f64(beta)))
|
return f32(float64_weibull(f64(alpha), f64(beta), gen))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -227,23 +227,23 @@ float32_weibull :: proc(alpha, beta: f32) -> f32 {
|
|||||||
// `kappa` is the concentration parameter which must be >= 0
|
// `kappa` is the concentration parameter which must be >= 0
|
||||||
// When `kappa` is zero, the Distribution is a uniform Distribution over the range 0 to 2pi
|
// When `kappa` is zero, the Distribution is a uniform Distribution over the range 0 to 2pi
|
||||||
@(require_results)
|
@(require_results)
|
||||||
float64_von_mises :: proc(mean_angle, kappa: f64) -> f64 {
|
float64_von_mises :: proc(mean_angle, kappa: f64, gen := context.random_generator) -> f64 {
|
||||||
// Fisher, N.I., "Statistical Analysis of Circular Data", Cambridge University Press, 1993.
|
// Fisher, N.I., "Statistical Analysis of Circular Data", Cambridge University Press, 1993.
|
||||||
|
|
||||||
mu := mean_angle
|
mu := mean_angle
|
||||||
if kappa <= 1e-6 {
|
if kappa <= 1e-6 {
|
||||||
return math.TAU * float64()
|
return math.TAU * float64(gen)
|
||||||
}
|
}
|
||||||
|
|
||||||
s := 0.5 / kappa
|
s := 0.5 / kappa
|
||||||
t := s + math.sqrt(1 + s*s)
|
t := s + math.sqrt(1 + s*s)
|
||||||
z: f64
|
z: f64
|
||||||
for {
|
for {
|
||||||
u1 := float64()
|
u1 := float64(gen)
|
||||||
z = math.cos(math.TAU * 0.5 * u1)
|
z = math.cos(math.TAU * 0.5 * u1)
|
||||||
|
|
||||||
d := z / (t + z)
|
d := z / (t + z)
|
||||||
u2 := float64()
|
u2 := float64(gen)
|
||||||
if u2 < 1 - d*d || u2 <= (1-d)*math.exp(d) {
|
if u2 < 1 - d*d || u2 <= (1-d)*math.exp(d) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -251,7 +251,7 @@ float64_von_mises :: proc(mean_angle, kappa: f64) -> f64 {
|
|||||||
|
|
||||||
q := 1.0 / t
|
q := 1.0 / t
|
||||||
f := (q + z) / (1 + q*z)
|
f := (q + z) / (1 + q*z)
|
||||||
u3 := float64()
|
u3 := float64(gen)
|
||||||
if u3 > 0.5 {
|
if u3 > 0.5 {
|
||||||
return math.mod(mu + math.acos(f), math.TAU)
|
return math.mod(mu + math.acos(f), math.TAU)
|
||||||
} else {
|
} else {
|
||||||
@@ -263,57 +263,57 @@ float64_von_mises :: proc(mean_angle, kappa: f64) -> f64 {
|
|||||||
// `kappa` is the concentration parameter which must be >= 0
|
// `kappa` is the concentration parameter which must be >= 0
|
||||||
// When `kappa` is zero, the Distribution is a uniform Distribution over the range 0 to 2pi
|
// When `kappa` is zero, the Distribution is a uniform Distribution over the range 0 to 2pi
|
||||||
@(require_results)
|
@(require_results)
|
||||||
float32_von_mises :: proc(mean_angle, kappa: f32) -> f32 {
|
float32_von_mises :: proc(mean_angle, kappa: f32, gen := context.random_generator) -> f32 {
|
||||||
return f32(float64_von_mises(f64(mean_angle), f64(kappa)))
|
return f32(float64_von_mises(f64(mean_angle), f64(kappa), gen))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Cauchy-Lorentz Distribution
|
// Cauchy-Lorentz Distribution
|
||||||
// `x_0` is the location, `gamma` is the scale where `gamma` > 0
|
// `x_0` is the location, `gamma` is the scale where `gamma` > 0
|
||||||
@(require_results)
|
@(require_results)
|
||||||
float64_cauchy_lorentz :: proc(x_0, gamma: f64) -> f64 {
|
float64_cauchy_lorentz :: proc(x_0, gamma: f64, gen := context.random_generator) -> f64 {
|
||||||
assert(gamma > 0)
|
assert(gamma > 0)
|
||||||
|
|
||||||
// Calculated from the inverse CDF
|
// Calculated from the inverse CDF
|
||||||
|
|
||||||
return math.tan(math.PI * (float64() - 0.5))*gamma + x_0
|
return math.tan(math.PI * (float64(gen) - 0.5))*gamma + x_0
|
||||||
}
|
}
|
||||||
// Cauchy-Lorentz Distribution
|
// Cauchy-Lorentz Distribution
|
||||||
// `x_0` is the location, `gamma` is the scale where `gamma` > 0
|
// `x_0` is the location, `gamma` is the scale where `gamma` > 0
|
||||||
@(require_results)
|
@(require_results)
|
||||||
float32_cauchy_lorentz :: proc(x_0, gamma: f32) -> f32 {
|
float32_cauchy_lorentz :: proc(x_0, gamma: f32, gen := context.random_generator) -> f32 {
|
||||||
return f32(float64_cauchy_lorentz(f64(x_0), f64(gamma)))
|
return f32(float64_cauchy_lorentz(f64(x_0), f64(gamma), gen))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Log Cauchy-Lorentz Distribution
|
// Log Cauchy-Lorentz Distribution
|
||||||
// `x_0` is the location, `gamma` is the scale where `gamma` > 0
|
// `x_0` is the location, `gamma` is the scale where `gamma` > 0
|
||||||
@(require_results)
|
@(require_results)
|
||||||
float64_log_cauchy_lorentz :: proc(x_0, gamma: f64) -> f64 {
|
float64_log_cauchy_lorentz :: proc(x_0, gamma: f64, gen := context.random_generator) -> f64 {
|
||||||
assert(gamma > 0)
|
assert(gamma > 0)
|
||||||
return math.exp(math.tan(math.PI * (float64() - 0.5))*gamma + x_0)
|
return math.exp(math.tan(math.PI * (float64(gen) - 0.5))*gamma + x_0)
|
||||||
}
|
}
|
||||||
// Log Cauchy-Lorentz Distribution
|
// Log Cauchy-Lorentz Distribution
|
||||||
// `x_0` is the location, `gamma` is the scale where `gamma` > 0
|
// `x_0` is the location, `gamma` is the scale where `gamma` > 0
|
||||||
@(require_results)
|
@(require_results)
|
||||||
float32_log_cauchy_lorentz :: proc(x_0, gamma: f32) -> f32 {
|
float32_log_cauchy_lorentz :: proc(x_0, gamma: f32, gen := context.random_generator) -> f32 {
|
||||||
return f32(float64_log_cauchy_lorentz(f64(x_0), f64(gamma)))
|
return f32(float64_log_cauchy_lorentz(f64(x_0), f64(gamma), gen))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Laplace Distribution
|
// Laplace Distribution
|
||||||
// `b` is the scale where `b` > 0
|
// `b` is the scale where `b` > 0
|
||||||
@(require_results)
|
@(require_results)
|
||||||
float64_laplace :: proc(mean, b: f64) -> f64 {
|
float64_laplace :: proc(mean, b: f64, gen := context.random_generator) -> f64 {
|
||||||
assert(b > 0)
|
assert(b > 0)
|
||||||
p := float64()-0.5
|
p := float64(gen)-0.5
|
||||||
return -math.sign(p)*math.ln(1 - 2*abs(p))*b + mean
|
return -math.sign(p)*math.ln(1 - 2*abs(p))*b + mean
|
||||||
}
|
}
|
||||||
// Laplace Distribution
|
// Laplace Distribution
|
||||||
// `b` is the scale where `b` > 0
|
// `b` is the scale where `b` > 0
|
||||||
@(require_results)
|
@(require_results)
|
||||||
float32_laplace :: proc(mean, b: f32) -> f32 {
|
float32_laplace :: proc(mean, b: f32, gen := context.random_generator) -> f32 {
|
||||||
return f32(float64_laplace(f64(mean), f64(b)))
|
return f32(float64_laplace(f64(mean), f64(b), gen))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -321,18 +321,18 @@ float32_laplace :: proc(mean, b: f32) -> f32 {
|
|||||||
// `eta` is the shape, `b` is the scale
|
// `eta` is the shape, `b` is the scale
|
||||||
// Both `eta` and `b` must be > 0
|
// Both `eta` and `b` must be > 0
|
||||||
@(require_results)
|
@(require_results)
|
||||||
float64_gompertz :: proc(eta, b: f64) -> f64 {
|
float64_gompertz :: proc(eta, b: f64, gen := context.random_generator) -> f64 {
|
||||||
if eta <= 0 || b <= 0 {
|
if eta <= 0 || b <= 0 {
|
||||||
panic(#procedure + ": eta and b must be > 0.0")
|
panic(#procedure + ": eta and b must be > 0.0")
|
||||||
}
|
}
|
||||||
|
|
||||||
p := float64()
|
p := float64(gen)
|
||||||
return math.ln(1 - math.ln(1 - p)/eta)/b
|
return math.ln(1 - math.ln(1 - p)/eta)/b
|
||||||
}
|
}
|
||||||
// Gompertz Distribution
|
// Gompertz Distribution
|
||||||
// `eta` is the shape, `b` is the scale
|
// `eta` is the shape, `b` is the scale
|
||||||
// Both `eta` and `b` must be > 0
|
// Both `eta` and `b` must be > 0
|
||||||
@(require_results)
|
@(require_results)
|
||||||
float32_gompertz :: proc(eta, b: f32) -> f32 {
|
float32_gompertz :: proc(eta, b: f32, gen := context.random_generator) -> f32 {
|
||||||
return f32(float64_gompertz(f64(eta), f64(b)))
|
return f32(float64_gompertz(f64(eta), f64(b), gen))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import "core:math"
|
|||||||
// https://www.jstatsoft.org/article/view/v005i08 [web page]
|
// https://www.jstatsoft.org/article/view/v005i08 [web page]
|
||||||
//
|
//
|
||||||
@(require_results)
|
@(require_results)
|
||||||
exp_float64 :: proc() -> f64 {
|
exp_float64 :: proc(gen := context.random_generator) -> f64 {
|
||||||
re :: 7.69711747013104972
|
re :: 7.69711747013104972
|
||||||
|
|
||||||
@(static, rodata)
|
@(static, rodata)
|
||||||
@@ -199,16 +199,16 @@ exp_float64 :: proc() -> f64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
j := uint32()
|
j := uint32(gen)
|
||||||
i := j & 0xFF
|
i := j & 0xFF
|
||||||
x := f64(j) * f64(we[i])
|
x := f64(j) * f64(we[i])
|
||||||
if j < ke[i] {
|
if j < ke[i] {
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
return re - math.ln(float64())
|
return re - math.ln(float64(gen))
|
||||||
}
|
}
|
||||||
if fe[i]+f32(float64())*(fe[i-1]-fe[i]) < f32(math.exp(-x)) {
|
if fe[i]+f32(float64(gen))*(fe[i-1]-fe[i]) < f32(math.exp(-x)) {
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import "core:math"
|
|||||||
// https://www.jstatsoft.org/article/view/v005i08 [web page]
|
// https://www.jstatsoft.org/article/view/v005i08 [web page]
|
||||||
//
|
//
|
||||||
@(require_results)
|
@(require_results)
|
||||||
norm_float64 :: proc() -> f64 {
|
norm_float64 :: proc(gen := context.random_generator) -> f64 {
|
||||||
rn :: 3.442619855899
|
rn :: 3.442619855899
|
||||||
|
|
||||||
@(static, rodata)
|
@(static, rodata)
|
||||||
@@ -116,7 +116,7 @@ norm_float64 :: proc() -> f64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
j := i32(uint32())
|
j := i32(uint32(gen))
|
||||||
i := j & 0x7f
|
i := j & 0x7f
|
||||||
x := f64(j) * f64(wn[i])
|
x := f64(j) * f64(wn[i])
|
||||||
if u32(abs(j)) < kn[i] {
|
if u32(abs(j)) < kn[i] {
|
||||||
@@ -126,15 +126,15 @@ norm_float64 :: proc() -> f64 {
|
|||||||
|
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
for {
|
for {
|
||||||
x = -math.ln(float64()) * (1.0/ rn)
|
x = -math.ln(float64(gen)) * (1.0/ rn)
|
||||||
y := -math.ln(float64())
|
y := -math.ln(float64(gen))
|
||||||
if y+y >= x*x {
|
if y+y >= x*x {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return j > 0 ? rn + x : -rn - x
|
return j > 0 ? rn + x : -rn - x
|
||||||
}
|
}
|
||||||
if fn[i]+f32(float64())*(fn[i-1]-fn[i]) < f32(math.exp(-0.5*x*x)) {
|
if fn[i]+f32(float64(gen))*(fn[i-1]-fn[i]) < f32(math.exp(-0.5*x*x)) {
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+66
-47
@@ -9,6 +9,10 @@ import "base:runtime"
|
|||||||
import "core:math"
|
import "core:math"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
|
||||||
|
Generator :: runtime.Random_Generator
|
||||||
|
|
||||||
|
Generator_Query_Info :: runtime.Random_Generator_Query_Info
|
||||||
|
|
||||||
Default_Random_State :: runtime.Default_Random_State
|
Default_Random_State :: runtime.Default_Random_State
|
||||||
default_random_generator :: runtime.default_random_generator
|
default_random_generator :: runtime.default_random_generator
|
||||||
|
|
||||||
@@ -54,7 +58,7 @@ Example:
|
|||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
|
||||||
set_global_seed_example :: proc() {
|
set_global_seed_example :: proc() {
|
||||||
rand.set_global_seed(1)
|
rand.reset(1)
|
||||||
fmt.println(rand.uint64())
|
fmt.println(rand.uint64())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,15 +66,24 @@ Possible Output:
|
|||||||
|
|
||||||
10
|
10
|
||||||
*/
|
*/
|
||||||
reset :: proc(seed: u64) {
|
reset :: proc(seed: u64, gen := context.random_generator) {
|
||||||
runtime.random_generator_reset_u64(context.random_generator, seed)
|
runtime.random_generator_reset_u64(gen, seed)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
reset_bytes :: proc(bytes: []byte, gen := context.random_generator) {
|
||||||
|
runtime.random_generator_reset_bytes(gen, bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
query_info :: proc(gen := context.random_generator) -> Generator_Query_Info {
|
||||||
|
return runtime.random_generator_query_info(gen)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
_random_u64 :: proc() -> (res: u64) {
|
_random_u64 :: proc(gen := context.random_generator) -> (res: u64) {
|
||||||
ok := runtime.random_generator_read_ptr(context.random_generator, &res, size_of(res))
|
ok := runtime.random_generator_read_ptr(gen, &res, size_of(res))
|
||||||
assert(ok, "uninitialized context.random_generator")
|
assert(ok, "uninitialized gen/context.random_generator")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +108,7 @@ Possible Output:
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
@(require_results)
|
@(require_results)
|
||||||
uint32 :: proc() -> (val: u32) { return u32(_random_u64()) }
|
uint32 :: proc(gen := context.random_generator) -> (val: u32) { return u32(_random_u64(gen)) }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Generates a random 64 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
|
Generates a random 64 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
|
||||||
@@ -118,7 +131,7 @@ Possible Output:
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
@(require_results)
|
@(require_results)
|
||||||
uint64 :: proc() -> (val: u64) { return _random_u64() }
|
uint64 :: proc(gen := context.random_generator) -> (val: u64) { return _random_u64(gen) }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Generates a random 128 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
|
Generates a random 128 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
|
||||||
@@ -141,9 +154,9 @@ Possible Output:
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
@(require_results)
|
@(require_results)
|
||||||
uint128 :: proc() -> (val: u128) {
|
uint128 :: proc(gen := context.random_generator) -> (val: u128) {
|
||||||
a := u128(_random_u64())
|
a := u128(_random_u64(gen))
|
||||||
b := u128(_random_u64())
|
b := u128(_random_u64(gen))
|
||||||
return (a<<64) | b
|
return (a<<64) | b
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,7 +181,7 @@ Possible Output:
|
|||||||
389
|
389
|
||||||
|
|
||||||
*/
|
*/
|
||||||
@(require_results) int31 :: proc() -> (val: i32) { return i32(uint32() << 1 >> 1) }
|
@(require_results) int31 :: proc(gen := context.random_generator) -> (val: i32) { return i32(uint32(gen) << 1 >> 1) }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Generates a random 63 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
|
Generates a random 63 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
|
||||||
@@ -191,7 +204,7 @@ Possible Output:
|
|||||||
389
|
389
|
||||||
|
|
||||||
*/
|
*/
|
||||||
@(require_results) int63 :: proc() -> (val: i64) { return i64(uint64() << 1 >> 1) }
|
@(require_results) int63 :: proc(gen := context.random_generator) -> (val: i64) { return i64(uint64(gen) << 1 >> 1) }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Generates a random 127 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
|
Generates a random 127 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
|
||||||
@@ -214,7 +227,7 @@ Possible Output:
|
|||||||
389
|
389
|
||||||
|
|
||||||
*/
|
*/
|
||||||
@(require_results) int127 :: proc() -> (val: i128) { return i128(uint128() << 1 >> 1) }
|
@(require_results) int127 :: proc(gen := context.random_generator) -> (val: i128) { return i128(uint128(gen) << 1 >> 1) }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Generates a random 31 bit value in the range `[0, n)` using the provided random number generator. If no generator is provided the global random number generator will be used.
|
Generates a random 31 bit value in the range `[0, n)` using the provided random number generator. If no generator is provided the global random number generator will be used.
|
||||||
@@ -242,17 +255,17 @@ Possible Output:
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
@(require_results)
|
@(require_results)
|
||||||
int31_max :: proc(n: i32) -> (val: i32) {
|
int31_max :: proc(n: i32, gen := context.random_generator) -> (val: i32) {
|
||||||
if n <= 0 {
|
if n <= 0 {
|
||||||
panic("Invalid argument to int31_max")
|
panic("Invalid argument to int31_max")
|
||||||
}
|
}
|
||||||
if n&(n-1) == 0 {
|
if n&(n-1) == 0 {
|
||||||
return int31() & (n-1)
|
return int31(gen) & (n-1)
|
||||||
}
|
}
|
||||||
max := i32((1<<31) - 1 - (1<<31)%u32(n))
|
max := i32((1<<31) - 1 - (1<<31)%u32(n))
|
||||||
v := int31()
|
v := int31(gen)
|
||||||
for v > max {
|
for v > max {
|
||||||
v = int31()
|
v = int31(gen)
|
||||||
}
|
}
|
||||||
return v % n
|
return v % n
|
||||||
}
|
}
|
||||||
@@ -283,17 +296,17 @@ Possible Output:
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
@(require_results)
|
@(require_results)
|
||||||
int63_max :: proc(n: i64) -> (val: i64) {
|
int63_max :: proc(n: i64, gen := context.random_generator) -> (val: i64) {
|
||||||
if n <= 0 {
|
if n <= 0 {
|
||||||
panic("Invalid argument to int63_max")
|
panic("Invalid argument to int63_max")
|
||||||
}
|
}
|
||||||
if n&(n-1) == 0 {
|
if n&(n-1) == 0 {
|
||||||
return int63() & (n-1)
|
return int63(gen) & (n-1)
|
||||||
}
|
}
|
||||||
max := i64((1<<63) - 1 - (1<<63)%u64(n))
|
max := i64((1<<63) - 1 - (1<<63)%u64(n))
|
||||||
v := int63()
|
v := int63(gen)
|
||||||
for v > max {
|
for v > max {
|
||||||
v = int63()
|
v = int63(gen)
|
||||||
}
|
}
|
||||||
return v % n
|
return v % n
|
||||||
}
|
}
|
||||||
@@ -324,17 +337,17 @@ Possible Output:
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
@(require_results)
|
@(require_results)
|
||||||
int127_max :: proc(n: i128) -> (val: i128) {
|
int127_max :: proc(n: i128, gen := context.random_generator) -> (val: i128) {
|
||||||
if n <= 0 {
|
if n <= 0 {
|
||||||
panic("Invalid argument to int127_max")
|
panic("Invalid argument to int127_max")
|
||||||
}
|
}
|
||||||
if n&(n-1) == 0 {
|
if n&(n-1) == 0 {
|
||||||
return int127() & (n-1)
|
return int127(gen) & (n-1)
|
||||||
}
|
}
|
||||||
max := i128((1<<127) - 1 - (1<<127)%u128(n))
|
max := i128((1<<127) - 1 - (1<<127)%u128(n))
|
||||||
v := int127()
|
v := int127(gen)
|
||||||
for v > max {
|
for v > max {
|
||||||
v = int127()
|
v = int127(gen)
|
||||||
}
|
}
|
||||||
return v % n
|
return v % n
|
||||||
}
|
}
|
||||||
@@ -365,14 +378,14 @@ Possible Output:
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
@(require_results)
|
@(require_results)
|
||||||
int_max :: proc(n: int) -> (val: int) {
|
int_max :: proc(n: int, gen := context.random_generator) -> (val: int) {
|
||||||
if n <= 0 {
|
if n <= 0 {
|
||||||
panic("Invalid argument to int_max")
|
panic("Invalid argument to int_max")
|
||||||
}
|
}
|
||||||
when size_of(int) == 4 {
|
when size_of(int) == 4 {
|
||||||
return int(int31_max(i32(n)))
|
return int(int31_max(i32(n), gen))
|
||||||
} else {
|
} else {
|
||||||
return int(int63_max(i64(n)))
|
return int(int63_max(i64(n), gen))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,7 +409,7 @@ Possible Output:
|
|||||||
0.511
|
0.511
|
||||||
|
|
||||||
*/
|
*/
|
||||||
@(require_results) float64 :: proc() -> (val: f64) { return f64(int63_max(1<<53)) / (1 << 53) }
|
@(require_results) float64 :: proc(gen := context.random_generator) -> (val: f64) { return f64(int63_max(1<<53, gen)) / (1 << 53) }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Generates a random single floating point value in the range `[0, 1)` using the provided random number generator. If no generator is provided the global random number generator will be used.
|
Generates a random single floating point value in the range `[0, 1)` using the provided random number generator. If no generator is provided the global random number generator will be used.
|
||||||
@@ -418,7 +431,7 @@ Possible Output:
|
|||||||
0.511
|
0.511
|
||||||
|
|
||||||
*/
|
*/
|
||||||
@(require_results) float32 :: proc() -> (val: f32) { return f32(int31_max(1<<24)) / (1 << 24) }
|
@(require_results) float32 :: proc(gen := context.random_generator) -> (val: f32) { return f32(int31_max(1<<24, gen)) / (1 << 24) }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Generates a random double floating point value in the range `[low, high)` using the provided random number generator. If no generator is provided the global random number generator will be used.
|
Generates a random double floating point value in the range `[low, high)` using the provided random number generator. If no generator is provided the global random number generator will be used.
|
||||||
@@ -446,9 +459,9 @@ Possible Output:
|
|||||||
673.130
|
673.130
|
||||||
|
|
||||||
*/
|
*/
|
||||||
@(require_results) float64_range :: proc(low, high: f64) -> (val: f64) {
|
@(require_results) float64_range :: proc(low, high: f64, gen := context.random_generator) -> (val: f64) {
|
||||||
assert(low <= high, "low must be lower than or equal to high")
|
assert(low <= high, "low must be lower than or equal to high")
|
||||||
val = (high-low)*float64() + low
|
val = (high-low)*float64(gen) + low
|
||||||
if val >= high {
|
if val >= high {
|
||||||
val = max(low, high * (1 - math.F64_EPSILON))
|
val = max(low, high * (1 - math.F64_EPSILON))
|
||||||
}
|
}
|
||||||
@@ -481,9 +494,9 @@ Possible Output:
|
|||||||
673.130
|
673.130
|
||||||
|
|
||||||
*/
|
*/
|
||||||
@(require_results) float32_range :: proc(low, high: f32) -> (val: f32) {
|
@(require_results) float32_range :: proc(low, high: f32, gen := context.random_generator) -> (val: f32) {
|
||||||
assert(low <= high, "low must be lower than or equal to high")
|
assert(low <= high, "low must be lower than or equal to high")
|
||||||
val = (high-low)*float32() + low
|
val = (high-low)*float32(gen) + low
|
||||||
if val >= high {
|
if val >= high {
|
||||||
val = max(low, high * (1 - math.F32_EPSILON))
|
val = max(low, high * (1 - math.F32_EPSILON))
|
||||||
}
|
}
|
||||||
@@ -518,12 +531,12 @@ Possible Output:
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
@(require_results)
|
@(require_results)
|
||||||
read :: proc(p: []byte) -> (n: int) {
|
read :: proc(p: []byte, gen := context.random_generator) -> (n: int) {
|
||||||
pos := i8(0)
|
pos := i8(0)
|
||||||
val := i64(0)
|
val := i64(0)
|
||||||
for n = 0; n < len(p); n += 1 {
|
for n = 0; n < len(p); n += 1 {
|
||||||
if pos == 0 {
|
if pos == 0 {
|
||||||
val = int63()
|
val = int63(gen)
|
||||||
pos = 7
|
pos = 7
|
||||||
}
|
}
|
||||||
p[n] = byte(val)
|
p[n] = byte(val)
|
||||||
@@ -566,10 +579,10 @@ Possible Output:
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
@(require_results)
|
@(require_results)
|
||||||
perm :: proc(n: int, allocator := context.allocator) -> (res: []int, err: mem.Allocator_Error) #optional_allocator_error {
|
perm :: proc(n: int, allocator := context.allocator, gen := context.random_generator) -> (res: []int, err: mem.Allocator_Error) #optional_allocator_error {
|
||||||
m := make([]int, n, allocator) or_return
|
m := make([]int, n, allocator) or_return
|
||||||
for i := 0; i < n; i += 1 {
|
for i := 0; i < n; i += 1 {
|
||||||
j := int_max(i+1)
|
j := int_max(i+1, gen)
|
||||||
m[i] = m[j]
|
m[i] = m[j]
|
||||||
m[j] = i
|
m[j] = i
|
||||||
}
|
}
|
||||||
@@ -599,14 +612,20 @@ Possible Output:
|
|||||||
[2, 4, 3, 1]
|
[2, 4, 3, 1]
|
||||||
|
|
||||||
*/
|
*/
|
||||||
shuffle :: proc(array: $T/[]$E) {
|
shuffle :: proc(array: $T/[]$E, gen := context.random_generator) {
|
||||||
n := i64(len(array))
|
n := i64(len(array))
|
||||||
if n < 2 {
|
if n < 2 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := i64(n - 1); i > 0; i -= 1 {
|
i := n - 1
|
||||||
j := int63_max(i + 1)
|
for ; i > (1<<31 - 2); i -= 1 {
|
||||||
|
j := int63_max(i + 1, gen)
|
||||||
|
array[i], array[j] = array[j], array[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
for ; i > 0; i -= 1 {
|
||||||
|
j := int31_max(i32(i + 1), gen)
|
||||||
array[i], array[j] = array[j], array[i]
|
array[i], array[j] = array[j], array[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -641,17 +660,17 @@ Possible Output:
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
@(require_results)
|
@(require_results)
|
||||||
choice :: proc(array: $T/[]$E) -> (res: E) {
|
choice :: proc(array: $T/[]$E, gen := context.random_generator) -> (res: E) {
|
||||||
n := i64(len(array))
|
n := i64(len(array))
|
||||||
if n < 1 {
|
if n < 1 {
|
||||||
return E{}
|
return E{}
|
||||||
}
|
}
|
||||||
return array[int63_max(n)]
|
return array[int63_max(n, gen)]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
choice_enum :: proc($T: typeid) -> T
|
choice_enum :: proc($T: typeid, gen := context.random_generator) -> T
|
||||||
where
|
where
|
||||||
intrinsics.type_is_enum(T),
|
intrinsics.type_is_enum(T),
|
||||||
size_of(T) <= 8,
|
size_of(T) <= 8,
|
||||||
@@ -659,11 +678,11 @@ choice_enum :: proc($T: typeid) -> T
|
|||||||
{
|
{
|
||||||
when intrinsics.type_is_unsigned(intrinsics.type_core_type(T)) &&
|
when intrinsics.type_is_unsigned(intrinsics.type_core_type(T)) &&
|
||||||
u64(max(T)) > u64(max(i64)) {
|
u64(max(T)) > u64(max(i64)) {
|
||||||
i := uint64() % u64(len(T))
|
i := uint64(gen) % u64(len(T))
|
||||||
i += u64(min(T))
|
i += u64(min(T))
|
||||||
return T(i)
|
return T(i)
|
||||||
} else {
|
} else {
|
||||||
i := int63_max(i64(len(T)))
|
i := int63_max(i64(len(T)), gen)
|
||||||
i += i64(min(T))
|
i += i64(min(T))
|
||||||
return T(i)
|
return T(i)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -748,9 +748,7 @@ dynamic_pool_alloc_bytes :: proc(p: ^Dynamic_Pool, bytes: int) -> ([]byte, Alloc
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
n := bytes
|
n := align_formula(bytes, p.alignment)
|
||||||
extra := p.alignment - (n % p.alignment)
|
|
||||||
n += extra
|
|
||||||
if n > p.block_size {
|
if n > p.block_size {
|
||||||
return nil, .Invalid_Argument
|
return nil, .Invalid_Argument
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -284,7 +284,7 @@ adjust_request_size_with_err :: proc(size, align: uint) -> (adjusted: uint, err:
|
|||||||
// TLSF utility functions. In most cases these are direct translations of
|
// TLSF utility functions. In most cases these are direct translations of
|
||||||
// the documentation in the research paper.
|
// the documentation in the research paper.
|
||||||
|
|
||||||
@(optimization_mode="speed", require_results)
|
@(optimization_mode="favor_size", require_results)
|
||||||
mapping_insert :: proc(size: uint) -> (fl, sl: i32) {
|
mapping_insert :: proc(size: uint) -> (fl, sl: i32) {
|
||||||
if size < SMALL_BLOCK_SIZE {
|
if size < SMALL_BLOCK_SIZE {
|
||||||
// Store small blocks in first list.
|
// Store small blocks in first list.
|
||||||
@@ -297,7 +297,7 @@ mapping_insert :: proc(size: uint) -> (fl, sl: i32) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed", require_results)
|
@(optimization_mode="favor_size", require_results)
|
||||||
mapping_round :: #force_inline proc(size: uint) -> (rounded: uint) {
|
mapping_round :: #force_inline proc(size: uint) -> (rounded: uint) {
|
||||||
rounded = size
|
rounded = size
|
||||||
if size >= SMALL_BLOCK_SIZE {
|
if size >= SMALL_BLOCK_SIZE {
|
||||||
@@ -308,7 +308,7 @@ mapping_round :: #force_inline proc(size: uint) -> (rounded: uint) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This version rounds up to the next block size (for allocations)
|
// This version rounds up to the next block size (for allocations)
|
||||||
@(optimization_mode="speed", require_results)
|
@(optimization_mode="favor_size", require_results)
|
||||||
mapping_search :: proc(size: uint) -> (fl, sl: i32) {
|
mapping_search :: proc(size: uint) -> (fl, sl: i32) {
|
||||||
return mapping_insert(mapping_round(size))
|
return mapping_insert(mapping_round(size))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ map_file :: proc{
|
|||||||
|
|
||||||
map_file_from_path :: proc(filename: string, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) {
|
map_file_from_path :: proc(filename: string, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) {
|
||||||
fd, err := os.open(filename, os.O_RDWR)
|
fd, err := os.open(filename, os.O_RDWR)
|
||||||
if err != 0 {
|
if err != nil {
|
||||||
return nil, .Open_Failure
|
return nil, .Open_Failure
|
||||||
}
|
}
|
||||||
defer os.close(fd)
|
defer os.close(fd)
|
||||||
@@ -34,7 +34,7 @@ map_file_from_path :: proc(filename: string, flags: Map_File_Flags) -> (data: []
|
|||||||
|
|
||||||
map_file_from_file_descriptor :: proc(fd: uintptr, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) {
|
map_file_from_file_descriptor :: proc(fd: uintptr, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) {
|
||||||
size, os_err := os.file_size(os.Handle(fd))
|
size, os_err := os.file_size(os.Handle(fd))
|
||||||
if os_err != 0 {
|
if os_err != nil {
|
||||||
return nil, .Stat_Failure
|
return nil, .Stat_Failure
|
||||||
}
|
}
|
||||||
if size < 0 {
|
if size < 0 {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
//+build freebsd, openbsd, netbsd
|
|
||||||
//+private
|
//+private
|
||||||
|
//+build !darwin
|
||||||
|
//+build !linux
|
||||||
|
//+build !windows
|
||||||
package mem_virtual
|
package mem_virtual
|
||||||
|
|
||||||
_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
|
_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
|
||||||
+32
-32
@@ -53,9 +53,9 @@ _create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (so
|
|||||||
unreachable()
|
unreachable()
|
||||||
}
|
}
|
||||||
|
|
||||||
sock, ok := os.socket(c_family, c_type, c_protocol)
|
sock, sock_err := os.socket(c_family, c_type, c_protocol)
|
||||||
if ok != os.ERROR_NONE {
|
if sock_err != nil {
|
||||||
err = Create_Socket_Error(ok)
|
err = Create_Socket_Error(os.is_platform_error(sock_err) or_else -1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,8 +84,8 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
|
|||||||
|
|
||||||
sockaddr := _endpoint_to_sockaddr(endpoint)
|
sockaddr := _endpoint_to_sockaddr(endpoint)
|
||||||
res := os.connect(os.Socket(skt), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len))
|
res := os.connect(os.Socket(skt), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len))
|
||||||
if res != os.ERROR_NONE {
|
if res != nil {
|
||||||
err = Dial_Error(res)
|
err = Dial_Error(os.is_platform_error(res) or_else -1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,11 +100,11 @@ _bind :: proc(skt: Any_Socket, ep: Endpoint) -> (err: Network_Error) {
|
|||||||
sockaddr := _endpoint_to_sockaddr(ep)
|
sockaddr := _endpoint_to_sockaddr(ep)
|
||||||
s := any_socket_to_socket(skt)
|
s := any_socket_to_socket(skt)
|
||||||
res := os.bind(os.Socket(s), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len))
|
res := os.bind(os.Socket(s), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len))
|
||||||
if res != os.ERROR_NONE {
|
if res != nil {
|
||||||
if res == os.EACCES && ep.port <= MAX_PRIVILEGED_PORT {
|
if res == os.EACCES && ep.port <= MAX_PRIVILEGED_PORT {
|
||||||
err = .Privileged_Port_Without_Root
|
err = .Privileged_Port_Without_Root
|
||||||
} else {
|
} else {
|
||||||
err = Bind_Error(res)
|
err = Bind_Error(os.is_platform_error(res) or_else -1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -128,8 +128,8 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (skt: TCP_
|
|||||||
bind(sock, interface_endpoint) or_return
|
bind(sock, interface_endpoint) or_return
|
||||||
|
|
||||||
res := os.listen(os.Socket(skt), backlog)
|
res := os.listen(os.Socket(skt), backlog)
|
||||||
if res != os.ERROR_NONE {
|
if res != nil {
|
||||||
err = Listen_Error(res)
|
err = Listen_Error(os.is_platform_error(res) or_else -1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,9 +141,9 @@ _accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (client
|
|||||||
sockaddr: os.SOCKADDR_STORAGE_LH
|
sockaddr: os.SOCKADDR_STORAGE_LH
|
||||||
sockaddrlen := c.int(size_of(sockaddr))
|
sockaddrlen := c.int(size_of(sockaddr))
|
||||||
|
|
||||||
client_sock, ok := os.accept(os.Socket(sock), cast(^os.SOCKADDR) &sockaddr, &sockaddrlen)
|
client_sock, client_sock_err := os.accept(os.Socket(sock), cast(^os.SOCKADDR) &sockaddr, &sockaddrlen)
|
||||||
if ok != os.ERROR_NONE {
|
if client_sock_err != nil {
|
||||||
err = Accept_Error(ok)
|
err = Accept_Error(os.is_platform_error(client_sock_err) or_else -1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
client = TCP_Socket(client_sock)
|
client = TCP_Socket(client_sock)
|
||||||
@@ -162,9 +162,9 @@ _recv_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_read: int, err: Networ
|
|||||||
if len(buf) <= 0 {
|
if len(buf) <= 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
res, ok := os.recv(os.Socket(skt), buf, 0)
|
res, res_err := os.recv(os.Socket(skt), buf, 0)
|
||||||
if ok != os.ERROR_NONE {
|
if res_err != nil {
|
||||||
err = TCP_Recv_Error(ok)
|
err = TCP_Recv_Error(os.is_platform_error(res_err) or_else -1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return int(res), nil
|
return int(res), nil
|
||||||
@@ -178,9 +178,9 @@ _recv_udp :: proc(skt: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endp
|
|||||||
|
|
||||||
from: os.SOCKADDR_STORAGE_LH
|
from: os.SOCKADDR_STORAGE_LH
|
||||||
fromsize := c.int(size_of(from))
|
fromsize := c.int(size_of(from))
|
||||||
res, ok := os.recvfrom(os.Socket(skt), buf, 0, cast(^os.SOCKADDR) &from, &fromsize)
|
res, res_err := os.recvfrom(os.Socket(skt), buf, 0, cast(^os.SOCKADDR) &from, &fromsize)
|
||||||
if ok != os.ERROR_NONE {
|
if res_err != nil {
|
||||||
err = UDP_Recv_Error(ok)
|
err = UDP_Recv_Error(os.is_platform_error(res_err) or_else -1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,9 +194,9 @@ _send_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_written: int, err: Net
|
|||||||
for bytes_written < len(buf) {
|
for bytes_written < len(buf) {
|
||||||
limit := min(int(max(i32)), len(buf) - bytes_written)
|
limit := min(int(max(i32)), len(buf) - bytes_written)
|
||||||
remaining := buf[bytes_written:][:limit]
|
remaining := buf[bytes_written:][:limit]
|
||||||
res, ok := os.send(os.Socket(skt), remaining, 0)
|
res, res_err := os.send(os.Socket(skt), remaining, 0)
|
||||||
if ok != os.ERROR_NONE {
|
if res_err != nil {
|
||||||
err = TCP_Send_Error(ok)
|
err = TCP_Send_Error(os.is_platform_error(res_err) or_else -1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
bytes_written += int(res)
|
bytes_written += int(res)
|
||||||
@@ -210,9 +210,9 @@ _send_udp :: proc(skt: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written:
|
|||||||
for bytes_written < len(buf) {
|
for bytes_written < len(buf) {
|
||||||
limit := min(1<<31, len(buf) - bytes_written)
|
limit := min(1<<31, len(buf) - bytes_written)
|
||||||
remaining := buf[bytes_written:][:limit]
|
remaining := buf[bytes_written:][:limit]
|
||||||
res, ok := os.sendto(os.Socket(skt), remaining, 0, cast(^os.SOCKADDR)&toaddr, i32(toaddr.len))
|
res, res_err := os.sendto(os.Socket(skt), remaining, 0, cast(^os.SOCKADDR)&toaddr, i32(toaddr.len))
|
||||||
if ok != os.ERROR_NONE {
|
if res_err != nil {
|
||||||
err = UDP_Send_Error(ok)
|
err = UDP_Send_Error(os.is_platform_error(res_err) or_else -1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
bytes_written += int(res)
|
bytes_written += int(res)
|
||||||
@@ -224,8 +224,8 @@ _send_udp :: proc(skt: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written:
|
|||||||
_shutdown :: proc(skt: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Error) {
|
_shutdown :: proc(skt: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Error) {
|
||||||
s := any_socket_to_socket(skt)
|
s := any_socket_to_socket(skt)
|
||||||
res := os.shutdown(os.Socket(s), int(manner))
|
res := os.shutdown(os.Socket(s), int(manner))
|
||||||
if res != os.ERROR_NONE {
|
if res != nil {
|
||||||
return Shutdown_Error(res)
|
return Shutdown_Error(os.is_platform_error(res) or_else -1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -302,8 +302,8 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca
|
|||||||
|
|
||||||
skt := any_socket_to_socket(s)
|
skt := any_socket_to_socket(s)
|
||||||
res := os.setsockopt(os.Socket(skt), int(level), int(option), ptr, len)
|
res := os.setsockopt(os.Socket(skt), int(level), int(option), ptr, len)
|
||||||
if res != os.ERROR_NONE {
|
if res != nil {
|
||||||
return Socket_Option_Error(res)
|
return Socket_Option_Error(os.is_platform_error(res) or_else -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -314,8 +314,8 @@ _set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_E
|
|||||||
socket := any_socket_to_socket(socket)
|
socket := any_socket_to_socket(socket)
|
||||||
|
|
||||||
flags, getfl_err := os.fcntl(int(socket), os.F_GETFL, 0)
|
flags, getfl_err := os.fcntl(int(socket), os.F_GETFL, 0)
|
||||||
if getfl_err != os.ERROR_NONE {
|
if getfl_err != nil {
|
||||||
return Set_Blocking_Error(getfl_err)
|
return Set_Blocking_Error(os.is_platform_error(getfl_err) or_else -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if should_block {
|
if should_block {
|
||||||
@@ -325,8 +325,8 @@ _set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_E
|
|||||||
}
|
}
|
||||||
|
|
||||||
_, setfl_err := os.fcntl(int(socket), os.F_SETFL, flags)
|
_, setfl_err := os.fcntl(int(socket), os.F_SETFL, flags)
|
||||||
if setfl_err != os.ERROR_NONE {
|
if setfl_err != nil {
|
||||||
return Set_Blocking_Error(setfl_err)
|
return Set_Blocking_Error(os.is_platform_error(setfl_err) or_else -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ _wrap_os_addr :: proc "contextless" (addr: linux.Sock_Addr_Any)->(Endpoint) {
|
|||||||
_create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (Any_Socket, Network_Error) {
|
_create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (Any_Socket, Network_Error) {
|
||||||
family := _unwrap_os_family(family)
|
family := _unwrap_os_family(family)
|
||||||
proto, socktype := _unwrap_os_proto_socktype(protocol)
|
proto, socktype := _unwrap_os_proto_socktype(protocol)
|
||||||
sock, errno := linux.socket(family, socktype, {}, proto)
|
sock, errno := linux.socket(family, socktype, {.CLOEXEC}, proto)
|
||||||
if errno != .NONE {
|
if errno != .NONE {
|
||||||
return {}, Create_Socket_Error(errno)
|
return {}, Create_Socket_Error(errno)
|
||||||
}
|
}
|
||||||
@@ -132,7 +132,7 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
|
|||||||
}
|
}
|
||||||
// Create new TCP socket
|
// Create new TCP socket
|
||||||
os_sock: linux.Fd
|
os_sock: linux.Fd
|
||||||
os_sock, errno = linux.socket(_unwrap_os_family(family_from_endpoint(endpoint)), .STREAM, {}, .TCP)
|
os_sock, errno = linux.socket(_unwrap_os_family(family_from_endpoint(endpoint)), .STREAM, {.CLOEXEC}, .TCP)
|
||||||
if errno != .NONE {
|
if errno != .NONE {
|
||||||
// TODO(flysand): should return invalid file descriptor here casted as TCP_Socket
|
// TODO(flysand): should return invalid file descriptor here casted as TCP_Socket
|
||||||
return {}, Create_Socket_Error(errno)
|
return {}, Create_Socket_Error(errno)
|
||||||
@@ -172,7 +172,7 @@ _listen_tcp :: proc(endpoint: Endpoint, backlog := 1000) -> (TCP_Socket, Network
|
|||||||
ep_address := _unwrap_os_addr(endpoint)
|
ep_address := _unwrap_os_addr(endpoint)
|
||||||
// Create TCP socket
|
// Create TCP socket
|
||||||
os_sock: linux.Fd
|
os_sock: linux.Fd
|
||||||
os_sock, errno = linux.socket(ep_family, .STREAM, {}, .TCP)
|
os_sock, errno = linux.socket(ep_family, .STREAM, {.CLOEXEC}, .TCP)
|
||||||
if errno != .NONE {
|
if errno != .NONE {
|
||||||
// TODO(flysand): should return invalid file descriptor here casted as TCP_Socket
|
// TODO(flysand): should return invalid file descriptor here casted as TCP_Socket
|
||||||
return {}, Create_Socket_Error(errno)
|
return {}, Create_Socket_Error(errno)
|
||||||
|
|||||||
@@ -599,6 +599,7 @@ Field_Flag :: enum {
|
|||||||
Subtype,
|
Subtype,
|
||||||
By_Ptr,
|
By_Ptr,
|
||||||
No_Broadcast,
|
No_Broadcast,
|
||||||
|
No_Capture,
|
||||||
|
|
||||||
Results,
|
Results,
|
||||||
Tags,
|
Tags,
|
||||||
@@ -619,6 +620,7 @@ field_flag_strings := [Field_Flag]string{
|
|||||||
.Subtype = "#subtype",
|
.Subtype = "#subtype",
|
||||||
.By_Ptr = "#by_ptr",
|
.By_Ptr = "#by_ptr",
|
||||||
.No_Broadcast = "#no_broadcast",
|
.No_Broadcast = "#no_broadcast",
|
||||||
|
.No_Capture = "#no_capture",
|
||||||
|
|
||||||
.Results = "results",
|
.Results = "results",
|
||||||
.Tags = "field tag",
|
.Tags = "field tag",
|
||||||
@@ -634,6 +636,7 @@ field_hash_flag_strings := []struct{key: string, flag: Field_Flag}{
|
|||||||
{"subtype", .Subtype},
|
{"subtype", .Subtype},
|
||||||
{"by_ptr", .By_Ptr},
|
{"by_ptr", .By_Ptr},
|
||||||
{"no_broadcast", .No_Broadcast},
|
{"no_broadcast", .No_Broadcast},
|
||||||
|
{"no_capture", .No_Capture},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -754,7 +757,7 @@ Array_Type :: struct {
|
|||||||
using node: Expr,
|
using node: Expr,
|
||||||
open: tokenizer.Pos,
|
open: tokenizer.Pos,
|
||||||
tag: ^Expr,
|
tag: ^Expr,
|
||||||
len: ^Expr, // Ellipsis node for [?]T array types, nil for slice types
|
len: ^Expr, // Unary_Expr node for [?]T array types, nil for slice types
|
||||||
close: tokenizer.Pos,
|
close: tokenizer.Pos,
|
||||||
elem: ^Expr,
|
elem: ^Expr,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1778,6 +1778,7 @@ parse_var_type :: proc(p: ^Parser, flags: ast.Field_Flags) -> ^ast.Expr {
|
|||||||
type = ast.new(ast.Bad_Expr, tok.pos, end_pos(tok))
|
type = ast.new(ast.Bad_Expr, tok.pos, end_pos(tok))
|
||||||
}
|
}
|
||||||
e := ast.new(ast.Ellipsis, type.pos, type)
|
e := ast.new(ast.Ellipsis, type.pos, type)
|
||||||
|
e.tok = tok.kind
|
||||||
e.expr = type
|
e.expr = type
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
@@ -2179,22 +2180,25 @@ parse_inlining_operand :: proc(p: ^Parser, lhs: bool, tok: tokenizer.Token) -> ^
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#partial switch e in ast.strip_or_return_expr(expr).derived_expr {
|
if expr != nil {
|
||||||
case ^ast.Proc_Lit:
|
#partial switch e in ast.strip_or_return_expr(expr).derived_expr {
|
||||||
if e.inlining != .None && e.inlining != pi {
|
case ^ast.Proc_Lit:
|
||||||
error(p, expr.pos, "both 'inline' and 'no_inline' cannot be applied to a procedure literal")
|
if e.inlining != .None && e.inlining != pi {
|
||||||
|
error(p, expr.pos, "both 'inline' and 'no_inline' cannot be applied to a procedure literal")
|
||||||
|
}
|
||||||
|
e.inlining = pi
|
||||||
|
return expr
|
||||||
|
case ^ast.Call_Expr:
|
||||||
|
if e.inlining != .None && e.inlining != pi {
|
||||||
|
error(p, expr.pos, "both 'inline' and 'no_inline' cannot be applied to a procedure call")
|
||||||
|
}
|
||||||
|
e.inlining = pi
|
||||||
|
return expr
|
||||||
}
|
}
|
||||||
e.inlining = pi
|
|
||||||
case ^ast.Call_Expr:
|
|
||||||
if e.inlining != .None && e.inlining != pi {
|
|
||||||
error(p, expr.pos, "both 'inline' and 'no_inline' cannot be applied to a procedure call")
|
|
||||||
}
|
|
||||||
e.inlining = pi
|
|
||||||
case:
|
|
||||||
error(p, tok.pos, "'%s' must be followed by a procedure literal or call", tok.text)
|
|
||||||
return ast.new(ast.Bad_Expr, tok.pos, expr)
|
|
||||||
}
|
}
|
||||||
return expr
|
|
||||||
|
error(p, tok.pos, "'%s' must be followed by a procedure literal or call", tok.text)
|
||||||
|
return ast.new(ast.Bad_Expr, tok.pos, expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||||
@@ -2258,18 +2262,18 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
|||||||
hp.type = type
|
hp.type = type
|
||||||
return hp
|
return hp
|
||||||
|
|
||||||
case "file", "line", "procedure", "caller_location":
|
case "file", "directory", "line", "procedure", "caller_location":
|
||||||
bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
|
bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
|
||||||
bd.tok = tok
|
bd.tok = tok
|
||||||
bd.name = name.text
|
bd.name = name.text
|
||||||
return bd
|
return bd
|
||||||
case "location", "load", "assert", "defined", "config":
|
|
||||||
|
case "location", "exists", "load", "load_directory", "load_hash", "hash", "assert", "panic", "defined", "config":
|
||||||
bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
|
bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
|
||||||
bd.tok = tok
|
bd.tok = tok
|
||||||
bd.name = name.text
|
bd.name = name.text
|
||||||
return parse_call_expr(p, bd)
|
return parse_call_expr(p, bd)
|
||||||
|
|
||||||
|
|
||||||
case "soa":
|
case "soa":
|
||||||
bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
|
bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
|
||||||
bd.tok = tok
|
bd.tok = tok
|
||||||
|
|||||||
@@ -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 "core:strings"
|
||||||
import "base:runtime"
|
import "base:runtime"
|
||||||
|
|
||||||
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
|
@(require_results)
|
||||||
|
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Error) {
|
||||||
|
@(require_results)
|
||||||
find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW) -> (fi: File_Info) {
|
find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW) -> (fi: File_Info) {
|
||||||
// Ignore "." and ".."
|
// Ignore "." and ".."
|
||||||
if d.cFileName[0] == '.' && d.cFileName[1] == 0 {
|
if d.cFileName[0] == '.' && d.cFileName[1] == 0 {
|
||||||
@@ -57,7 +59,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
|
|||||||
|
|
||||||
dir_fi, _ := file_info_from_get_file_information_by_handle("", h)
|
dir_fi, _ := file_info_from_get_file_information_by_handle("", h)
|
||||||
if !dir_fi.is_dir {
|
if !dir_fi.is_dir {
|
||||||
return nil, ERROR_FILE_IS_NOT_DIR
|
return nil, .Not_Dir
|
||||||
}
|
}
|
||||||
|
|
||||||
n := n
|
n := n
|
||||||
@@ -68,15 +70,14 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
|
|||||||
}
|
}
|
||||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
|
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
|
||||||
|
|
||||||
wpath: []u16
|
wpath := cleanpath_from_handle_u16(fd, context.temp_allocator) or_return
|
||||||
wpath, err = cleanpath_from_handle_u16(fd, context.temp_allocator)
|
if len(wpath) == 0 {
|
||||||
if len(wpath) == 0 || err != ERROR_NONE {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
dfi := make([dynamic]File_Info, 0, size)
|
dfi := make([dynamic]File_Info, 0, size) or_return
|
||||||
|
|
||||||
wpath_search := make([]u16, len(wpath)+3, context.temp_allocator)
|
wpath_search := make([]u16, len(wpath)+3, context.temp_allocator) or_return
|
||||||
copy(wpath_search, wpath)
|
copy(wpath_search, wpath)
|
||||||
wpath_search[len(wpath)+0] = '\\'
|
wpath_search[len(wpath)+0] = '\\'
|
||||||
wpath_search[len(wpath)+1] = '*'
|
wpath_search[len(wpath)+1] = '*'
|
||||||
@@ -88,7 +89,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
|
|||||||
find_data := &win32.WIN32_FIND_DATAW{}
|
find_data := &win32.WIN32_FIND_DATAW{}
|
||||||
find_handle := win32.FindFirstFileW(raw_data(wpath_search), find_data)
|
find_handle := win32.FindFirstFileW(raw_data(wpath_search), find_data)
|
||||||
if find_handle == win32.INVALID_HANDLE_VALUE {
|
if find_handle == win32.INVALID_HANDLE_VALUE {
|
||||||
err = Errno(win32.GetLastError())
|
err = get_last_error()
|
||||||
return dfi[:], err
|
return dfi[:], err
|
||||||
}
|
}
|
||||||
defer win32.FindClose(find_handle)
|
defer win32.FindClose(find_handle)
|
||||||
@@ -101,7 +102,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !win32.FindNextFileW(find_handle, find_data) {
|
if !win32.FindNextFileW(find_handle, find_data) {
|
||||||
e := Errno(win32.GetLastError())
|
e := get_last_error()
|
||||||
if e == ERROR_NO_MORE_FILES {
|
if e == ERROR_NO_MORE_FILES {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -109,5 +110,5 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return dfi[:], ERROR_NONE
|
return dfi[:], nil
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-19
@@ -7,27 +7,22 @@ import "base:runtime"
|
|||||||
// If the variable is found in the environment the value (which can be empty) is returned and the boolean is true
|
// If the variable is found in the environment the value (which can be empty) is returned and the boolean is true
|
||||||
// Otherwise the returned value will be empty and the boolean will be false
|
// Otherwise the returned value will be empty and the boolean will be false
|
||||||
// NOTE: the value will be allocated with the supplied allocator
|
// NOTE: the value will be allocated with the supplied allocator
|
||||||
|
@(require_results)
|
||||||
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||||
if key == "" {
|
if key == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
wkey := win32.utf8_to_wstring(key)
|
wkey := win32.utf8_to_wstring(key)
|
||||||
n := win32.GetEnvironmentVariableW(wkey, nil, 0)
|
n := win32.GetEnvironmentVariableW(wkey, nil, 0)
|
||||||
if n == 0 {
|
if n == 0 && get_last_error() == ERROR_ENVVAR_NOT_FOUND {
|
||||||
err := win32.GetLastError()
|
return "", false
|
||||||
if err == u32(ERROR_ENVVAR_NOT_FOUND) {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
|
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
|
||||||
|
|
||||||
b := make([dynamic]u16, n, context.temp_allocator)
|
b, _ := make([dynamic]u16, n, context.temp_allocator)
|
||||||
n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)))
|
n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)))
|
||||||
if n == 0 {
|
if n == 0 && get_last_error() == ERROR_ENVVAR_NOT_FOUND {
|
||||||
err := win32.GetLastError()
|
return "", false
|
||||||
if err == u32(ERROR_ENVVAR_NOT_FOUND) {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
value, _ = win32.utf16_to_utf8(b[:n], allocator)
|
value, _ = win32.utf16_to_utf8(b[:n], allocator)
|
||||||
found = true
|
found = true
|
||||||
@@ -39,41 +34,46 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
|
|||||||
// It returns the value, which will be empty if the variable is not present
|
// It returns the value, which will be empty if the variable is not present
|
||||||
// To distinguish between an empty value and an unset value, use lookup_env
|
// To distinguish between an empty value and an unset value, use lookup_env
|
||||||
// NOTE: the value will be allocated with the supplied allocator
|
// NOTE: the value will be allocated with the supplied allocator
|
||||||
|
@(require_results)
|
||||||
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||||
value, _ = lookup_env(key, allocator)
|
value, _ = lookup_env(key, allocator)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// set_env sets the value of the environment variable named by the key
|
// set_env sets the value of the environment variable named by the key
|
||||||
set_env :: proc(key, value: string) -> Errno {
|
set_env :: proc(key, value: string) -> Error {
|
||||||
k := win32.utf8_to_wstring(key)
|
k := win32.utf8_to_wstring(key)
|
||||||
v := win32.utf8_to_wstring(value)
|
v := win32.utf8_to_wstring(value)
|
||||||
|
|
||||||
if !win32.SetEnvironmentVariableW(k, v) {
|
if !win32.SetEnvironmentVariableW(k, v) {
|
||||||
return Errno(win32.GetLastError())
|
return get_last_error()
|
||||||
}
|
}
|
||||||
return 0
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// unset_env unsets a single environment variable
|
// unset_env unsets a single environment variable
|
||||||
unset_env :: proc(key: string) -> Errno {
|
unset_env :: proc(key: string) -> Error {
|
||||||
k := win32.utf8_to_wstring(key)
|
k := win32.utf8_to_wstring(key)
|
||||||
if !win32.SetEnvironmentVariableW(k, nil) {
|
if !win32.SetEnvironmentVariableW(k, nil) {
|
||||||
return Errno(win32.GetLastError())
|
return get_last_error()
|
||||||
}
|
}
|
||||||
return 0
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// environ returns a copy of strings representing the environment, in the form "key=value"
|
// environ returns a copy of strings representing the environment, in the form "key=value"
|
||||||
// NOTE: the slice of strings and the strings with be allocated using the supplied allocator
|
// NOTE: the slice of strings and the strings with be allocated using the supplied allocator
|
||||||
|
@(require_results)
|
||||||
environ :: proc(allocator := context.allocator) -> []string {
|
environ :: proc(allocator := context.allocator) -> []string {
|
||||||
envs := cast([^]win32.WCHAR)(win32.GetEnvironmentStringsW())
|
envs := ([^]win32.WCHAR)(win32.GetEnvironmentStringsW())
|
||||||
if envs == nil {
|
if envs == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
defer win32.FreeEnvironmentStringsW(envs)
|
defer win32.FreeEnvironmentStringsW(envs)
|
||||||
|
|
||||||
r := make([dynamic]string, 0, 50, allocator)
|
r, err := make([dynamic]string, 0, 50, allocator)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
for from, i := 0, 0; true; i += 1 {
|
for from, i := 0, 0; true; i += 1 {
|
||||||
if c := envs[i]; c == 0 {
|
if c := envs[i]; c == 0 {
|
||||||
if i <= from {
|
if i <= from {
|
||||||
|
|||||||
@@ -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 "base:runtime"
|
||||||
import "core:unicode/utf16"
|
import "core:unicode/utf16"
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
is_path_separator :: proc(c: byte) -> bool {
|
is_path_separator :: proc(c: byte) -> bool {
|
||||||
return c == '/' || c == '\\'
|
return c == '/' || c == '\\'
|
||||||
}
|
}
|
||||||
|
|
||||||
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) {
|
@(require_results)
|
||||||
|
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Error) {
|
||||||
if len(path) == 0 {
|
if len(path) == 0 {
|
||||||
return INVALID_HANDLE, ERROR_FILE_NOT_FOUND
|
return INVALID_HANDLE, General_Error.Not_Exist
|
||||||
}
|
}
|
||||||
|
|
||||||
access: u32
|
access: u32
|
||||||
@@ -52,32 +54,31 @@ open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errn
|
|||||||
wide_path := win32.utf8_to_wstring(path)
|
wide_path := win32.utf8_to_wstring(path)
|
||||||
handle := Handle(win32.CreateFileW(wide_path, access, share_mode, sa, create_mode, win32.FILE_ATTRIBUTE_NORMAL|win32.FILE_FLAG_BACKUP_SEMANTICS, nil))
|
handle := Handle(win32.CreateFileW(wide_path, access, share_mode, sa, create_mode, win32.FILE_ATTRIBUTE_NORMAL|win32.FILE_FLAG_BACKUP_SEMANTICS, nil))
|
||||||
if handle != INVALID_HANDLE {
|
if handle != INVALID_HANDLE {
|
||||||
return handle, ERROR_NONE
|
return handle, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err := Errno(win32.GetLastError())
|
return INVALID_HANDLE, get_last_error()
|
||||||
return INVALID_HANDLE, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
close :: proc(fd: Handle) -> Errno {
|
close :: proc(fd: Handle) -> Error {
|
||||||
if !win32.CloseHandle(win32.HANDLE(fd)) {
|
if !win32.CloseHandle(win32.HANDLE(fd)) {
|
||||||
return Errno(win32.GetLastError())
|
return get_last_error()
|
||||||
}
|
}
|
||||||
return ERROR_NONE
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
flush :: proc(fd: Handle) -> (err: Errno) {
|
flush :: proc(fd: Handle) -> (err: Error) {
|
||||||
if !win32.FlushFileBuffers(win32.HANDLE(fd)) {
|
if !win32.FlushFileBuffers(win32.HANDLE(fd)) {
|
||||||
err = Errno(win32.GetLastError())
|
err = get_last_error()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
write :: proc(fd: Handle, data: []byte) -> (int, Error) {
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
return 0, ERROR_NONE
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
single_write_length: win32.DWORD
|
single_write_length: win32.DWORD
|
||||||
@@ -90,25 +91,24 @@ write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
|||||||
|
|
||||||
e := win32.WriteFile(win32.HANDLE(fd), &data[total_write], to_write, &single_write_length, nil)
|
e := win32.WriteFile(win32.HANDLE(fd), &data[total_write], to_write, &single_write_length, nil)
|
||||||
if single_write_length <= 0 || !e {
|
if single_write_length <= 0 || !e {
|
||||||
err := Errno(win32.GetLastError())
|
return int(total_write), get_last_error()
|
||||||
return int(total_write), err
|
|
||||||
}
|
}
|
||||||
total_write += i64(single_write_length)
|
total_write += i64(single_write_length)
|
||||||
}
|
}
|
||||||
return int(total_write), ERROR_NONE
|
return int(total_write), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private="file")
|
@(private="file", require_results)
|
||||||
read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) {
|
read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Error) {
|
||||||
if len(b) == 0 {
|
if len(b) == 0 {
|
||||||
return 0, 0
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
BUF_SIZE :: 386
|
BUF_SIZE :: 386
|
||||||
buf16: [BUF_SIZE]u16
|
buf16: [BUF_SIZE]u16
|
||||||
buf8: [4*BUF_SIZE]u8
|
buf8: [4*BUF_SIZE]u8
|
||||||
|
|
||||||
for n < len(b) && err == 0 {
|
for n < len(b) && err == nil {
|
||||||
min_read := max(len(b)/4, 1 if len(b) > 0 else 0)
|
min_read := max(len(b)/4, 1 if len(b) > 0 else 0)
|
||||||
max_read := u32(min(BUF_SIZE, min_read))
|
max_read := u32(min(BUF_SIZE, min_read))
|
||||||
if max_read == 0 {
|
if max_read == 0 {
|
||||||
@@ -118,14 +118,14 @@ read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) {
|
|||||||
single_read_length: u32
|
single_read_length: u32
|
||||||
ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil)
|
ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil)
|
||||||
if !ok {
|
if !ok {
|
||||||
err = Errno(win32.GetLastError())
|
err = get_last_error()
|
||||||
}
|
}
|
||||||
|
|
||||||
buf8_len := utf16.decode_to_utf8(buf8[:], buf16[:single_read_length])
|
buf8_len := utf16.decode_to_utf8(buf8[:], buf16[:single_read_length])
|
||||||
src := buf8[:buf8_len]
|
src := buf8[:buf8_len]
|
||||||
|
|
||||||
ctrl_z := false
|
ctrl_z := false
|
||||||
for i := 0; i < len(src) && n+i < len(b); i += 1 {
|
for i := 0; i < len(src) && n < len(b); i += 1 {
|
||||||
x := src[i]
|
x := src[i]
|
||||||
if x == 0x1a { // ctrl-z
|
if x == 0x1a { // ctrl-z
|
||||||
ctrl_z = true
|
ctrl_z = true
|
||||||
@@ -149,9 +149,9 @@ read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
read :: proc(fd: Handle, data: []byte) -> (total_read: int, err: Errno) {
|
read :: proc(fd: Handle, data: []byte) -> (total_read: int, err: Error) {
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
return 0, ERROR_NONE
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
handle := win32.HANDLE(fd)
|
handle := win32.HANDLE(fd)
|
||||||
@@ -165,7 +165,7 @@ read :: proc(fd: Handle, data: []byte) -> (total_read: int, err: Errno) {
|
|||||||
|
|
||||||
if is_console {
|
if is_console {
|
||||||
total_read, err = read_console(handle, data[total_read:][:to_read])
|
total_read, err = read_console(handle, data[total_read:][:to_read])
|
||||||
if err != 0 {
|
if err != nil {
|
||||||
return total_read, err
|
return total_read, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -175,18 +175,18 @@ read :: proc(fd: Handle, data: []byte) -> (total_read: int, err: Errno) {
|
|||||||
// Successful read can mean two things, including EOF, see:
|
// Successful read can mean two things, including EOF, see:
|
||||||
// https://learn.microsoft.com/en-us/windows/win32/fileio/testing-for-the-end-of-a-file
|
// https://learn.microsoft.com/en-us/windows/win32/fileio/testing-for-the-end-of-a-file
|
||||||
if bytes_read == 0 {
|
if bytes_read == 0 {
|
||||||
return 0, ERROR_HANDLE_EOF
|
return 0, .EOF
|
||||||
} else {
|
} else {
|
||||||
return int(bytes_read), ERROR_NONE
|
return int(bytes_read), nil
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return 0, Errno(win32.GetLastError())
|
return 0, get_last_error()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return total_read, ERROR_NONE
|
return total_read, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
|
||||||
w: u32
|
w: u32
|
||||||
switch whence {
|
switch whence {
|
||||||
case 0: w = win32.FILE_BEGIN
|
case 0: w = win32.FILE_BEGIN
|
||||||
@@ -197,22 +197,23 @@ seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
|||||||
lo := i32(offset)
|
lo := i32(offset)
|
||||||
ft := win32.GetFileType(win32.HANDLE(fd))
|
ft := win32.GetFileType(win32.HANDLE(fd))
|
||||||
if ft == win32.FILE_TYPE_PIPE {
|
if ft == win32.FILE_TYPE_PIPE {
|
||||||
return 0, ERROR_FILE_IS_PIPE
|
return 0, .File_Is_Pipe
|
||||||
}
|
}
|
||||||
|
|
||||||
dw_ptr := win32.SetFilePointer(win32.HANDLE(fd), lo, &hi, w)
|
dw_ptr := win32.SetFilePointer(win32.HANDLE(fd), lo, &hi, w)
|
||||||
if dw_ptr == win32.INVALID_SET_FILE_POINTER {
|
if dw_ptr == win32.INVALID_SET_FILE_POINTER {
|
||||||
err := Errno(win32.GetLastError())
|
err := get_last_error()
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
return i64(hi)<<32 + i64(dw_ptr), ERROR_NONE
|
return i64(hi)<<32 + i64(dw_ptr), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
file_size :: proc(fd: Handle) -> (i64, Errno) {
|
@(require_results)
|
||||||
|
file_size :: proc(fd: Handle) -> (i64, Error) {
|
||||||
length: win32.LARGE_INTEGER
|
length: win32.LARGE_INTEGER
|
||||||
err: Errno
|
err: Error
|
||||||
if !win32.GetFileSizeEx(win32.HANDLE(fd), &length) {
|
if !win32.GetFileSizeEx(win32.HANDLE(fd), &length) {
|
||||||
err = Errno(win32.GetLastError())
|
err = get_last_error()
|
||||||
}
|
}
|
||||||
return i64(length), err
|
return i64(length), err
|
||||||
}
|
}
|
||||||
@@ -220,10 +221,9 @@ file_size :: proc(fd: Handle) -> (i64, Errno) {
|
|||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
MAX_RW :: 1<<30
|
MAX_RW :: 1<<30
|
||||||
ERROR_EOF :: 38
|
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
|
pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) {
|
||||||
buf := data
|
buf := data
|
||||||
if len(buf) > MAX_RW {
|
if len(buf) > MAX_RW {
|
||||||
buf = buf[:MAX_RW]
|
buf = buf[:MAX_RW]
|
||||||
@@ -239,15 +239,15 @@ pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
|
|||||||
|
|
||||||
h := win32.HANDLE(fd)
|
h := win32.HANDLE(fd)
|
||||||
done: win32.DWORD
|
done: win32.DWORD
|
||||||
e: Errno
|
e: Error
|
||||||
if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
|
if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
|
||||||
e = Errno(win32.GetLastError())
|
e = get_last_error()
|
||||||
done = 0
|
done = 0
|
||||||
}
|
}
|
||||||
return int(done), e
|
return int(done), e
|
||||||
}
|
}
|
||||||
@(private)
|
@(private)
|
||||||
pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
|
pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) {
|
||||||
buf := data
|
buf := data
|
||||||
if len(buf) > MAX_RW {
|
if len(buf) > MAX_RW {
|
||||||
buf = buf[:MAX_RW]
|
buf = buf[:MAX_RW]
|
||||||
@@ -261,9 +261,9 @@ pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
|
|||||||
|
|
||||||
h := win32.HANDLE(fd)
|
h := win32.HANDLE(fd)
|
||||||
done: win32.DWORD
|
done: win32.DWORD
|
||||||
e: Errno
|
e: Error
|
||||||
if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
|
if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
|
||||||
e = Errno(win32.GetLastError())
|
e = get_last_error()
|
||||||
done = 0
|
done = 0
|
||||||
}
|
}
|
||||||
return int(done), e
|
return int(done), e
|
||||||
@@ -279,19 +279,19 @@ on Windows, read_at changes the position of the file cursor, on *nix, it does no
|
|||||||
|
|
||||||
will read from the location twice on *nix, and from two different locations on Windows
|
will read from the location twice on *nix, and from two different locations on Windows
|
||||||
*/
|
*/
|
||||||
read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno) {
|
read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
|
||||||
if offset < 0 {
|
if offset < 0 {
|
||||||
return 0, ERROR_NEGATIVE_OFFSET
|
return 0, .Invalid_Offset
|
||||||
}
|
}
|
||||||
|
|
||||||
b, offset := data, offset
|
b, offset := data, offset
|
||||||
for len(b) > 0 {
|
for len(b) > 0 {
|
||||||
m, e := pread(fd, b, offset)
|
m, e := pread(fd, b, offset)
|
||||||
if e == ERROR_EOF {
|
if e == ERROR_EOF {
|
||||||
err = 0
|
err = nil
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if e != 0 {
|
if e != nil {
|
||||||
err = e
|
err = e
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -311,18 +311,14 @@ on Windows, write_at changes the position of the file cursor, on *nix, it does n
|
|||||||
|
|
||||||
will write to the location twice on *nix, and to two different locations on Windows
|
will write to the location twice on *nix, and to two different locations on Windows
|
||||||
*/
|
*/
|
||||||
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno) {
|
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
|
||||||
if offset < 0 {
|
if offset < 0 {
|
||||||
return 0, ERROR_NEGATIVE_OFFSET
|
return 0, .Invalid_Offset
|
||||||
}
|
}
|
||||||
|
|
||||||
b, offset := data, offset
|
b, offset := data, offset
|
||||||
for len(b) > 0 {
|
for len(b) > 0 {
|
||||||
m, e := pwrite(fd, b, offset)
|
m := pwrite(fd, b, offset) or_return
|
||||||
if e != 0 {
|
|
||||||
err = e
|
|
||||||
break
|
|
||||||
}
|
|
||||||
n += m
|
n += m
|
||||||
b = b[m:]
|
b = b[m:]
|
||||||
offset += i64(m)
|
offset += i64(m)
|
||||||
@@ -338,6 +334,7 @@ stdout := get_std_handle(uint(win32.STD_OUTPUT_HANDLE))
|
|||||||
stderr := get_std_handle(uint(win32.STD_ERROR_HANDLE))
|
stderr := get_std_handle(uint(win32.STD_ERROR_HANDLE))
|
||||||
|
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
get_std_handle :: proc "contextless" (h: uint) -> Handle {
|
get_std_handle :: proc "contextless" (h: uint) -> Handle {
|
||||||
fd := win32.GetStdHandle(win32.DWORD(h))
|
fd := win32.GetStdHandle(win32.DWORD(h))
|
||||||
return Handle(fd)
|
return Handle(fd)
|
||||||
@@ -352,6 +349,7 @@ exists :: proc(path: string) -> bool {
|
|||||||
return attribs != win32.INVALID_FILE_ATTRIBUTES
|
return attribs != win32.INVALID_FILE_ATTRIBUTES
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
is_file :: proc(path: string) -> bool {
|
is_file :: proc(path: string) -> bool {
|
||||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||||
@@ -363,6 +361,7 @@ is_file :: proc(path: string) -> bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
is_dir :: proc(path: string) -> bool {
|
is_dir :: proc(path: string) -> bool {
|
||||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||||
@@ -377,13 +376,14 @@ is_dir :: proc(path: string) -> bool {
|
|||||||
// NOTE(tetra): GetCurrentDirectory is not thread safe with SetCurrentDirectory and GetFullPathName
|
// NOTE(tetra): GetCurrentDirectory is not thread safe with SetCurrentDirectory and GetFullPathName
|
||||||
@private cwd_lock := win32.SRWLOCK{} // zero is initialized
|
@private cwd_lock := win32.SRWLOCK{} // zero is initialized
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
get_current_directory :: proc(allocator := context.allocator) -> string {
|
get_current_directory :: proc(allocator := context.allocator) -> string {
|
||||||
win32.AcquireSRWLockExclusive(&cwd_lock)
|
win32.AcquireSRWLockExclusive(&cwd_lock)
|
||||||
|
|
||||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
|
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
|
||||||
|
|
||||||
sz_utf16 := win32.GetCurrentDirectoryW(0, nil)
|
sz_utf16 := win32.GetCurrentDirectoryW(0, nil)
|
||||||
dir_buf_wstr := make([]u16, sz_utf16, context.temp_allocator) // the first time, it _includes_ the NUL.
|
dir_buf_wstr, _ := make([]u16, sz_utf16, context.temp_allocator) // the first time, it _includes_ the NUL.
|
||||||
|
|
||||||
sz_utf16 = win32.GetCurrentDirectoryW(win32.DWORD(len(dir_buf_wstr)), raw_data(dir_buf_wstr))
|
sz_utf16 = win32.GetCurrentDirectoryW(win32.DWORD(len(dir_buf_wstr)), raw_data(dir_buf_wstr))
|
||||||
assert(int(sz_utf16)+1 == len(dir_buf_wstr)) // the second time, it _excludes_ the NUL.
|
assert(int(sz_utf16)+1 == len(dir_buf_wstr)) // the second time, it _excludes_ the NUL.
|
||||||
@@ -393,14 +393,14 @@ get_current_directory :: proc(allocator := context.allocator) -> string {
|
|||||||
return win32.utf16_to_utf8(dir_buf_wstr, allocator) or_else ""
|
return win32.utf16_to_utf8(dir_buf_wstr, allocator) or_else ""
|
||||||
}
|
}
|
||||||
|
|
||||||
set_current_directory :: proc(path: string) -> (err: Errno) {
|
set_current_directory :: proc(path: string) -> (err: Error) {
|
||||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||||
wstr := win32.utf8_to_wstring(path, context.temp_allocator)
|
wstr := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||||
|
|
||||||
win32.AcquireSRWLockExclusive(&cwd_lock)
|
win32.AcquireSRWLockExclusive(&cwd_lock)
|
||||||
|
|
||||||
if !win32.SetCurrentDirectoryW(wstr) {
|
if !win32.SetCurrentDirectoryW(wstr) {
|
||||||
err = Errno(win32.GetLastError())
|
err = get_last_error()
|
||||||
}
|
}
|
||||||
|
|
||||||
win32.ReleaseSRWLockExclusive(&cwd_lock)
|
win32.ReleaseSRWLockExclusive(&cwd_lock)
|
||||||
@@ -409,31 +409,31 @@ set_current_directory :: proc(path: string) -> (err: Errno) {
|
|||||||
}
|
}
|
||||||
change_directory :: set_current_directory
|
change_directory :: set_current_directory
|
||||||
|
|
||||||
make_directory :: proc(path: string, mode: u32 = 0) -> (err: Errno) {
|
make_directory :: proc(path: string, mode: u32 = 0) -> (err: Error) {
|
||||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||||
// Mode is unused on Windows, but is needed on *nix
|
// Mode is unused on Windows, but is needed on *nix
|
||||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||||
|
|
||||||
if !win32.CreateDirectoryW(wpath, nil) {
|
if !win32.CreateDirectoryW(wpath, nil) {
|
||||||
err = Errno(win32.GetLastError())
|
err = get_last_error()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
remove_directory :: proc(path: string) -> (err: Errno) {
|
remove_directory :: proc(path: string) -> (err: Error) {
|
||||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||||
|
|
||||||
if !win32.RemoveDirectoryW(wpath) {
|
if !win32.RemoveDirectoryW(wpath) {
|
||||||
err = Errno(win32.GetLastError())
|
err = get_last_error()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@(private)
|
@(private, require_results)
|
||||||
is_abs :: proc(path: string) -> bool {
|
is_abs :: proc(path: string) -> bool {
|
||||||
if len(path) > 0 && path[0] == '/' {
|
if len(path) > 0 && path[0] == '/' {
|
||||||
return true
|
return true
|
||||||
@@ -449,7 +449,7 @@ is_abs :: proc(path: string) -> bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private, require_results)
|
||||||
fix_long_path :: proc(path: string) -> string {
|
fix_long_path :: proc(path: string) -> string {
|
||||||
if len(path) < 248 {
|
if len(path) < 248 {
|
||||||
return path
|
return path
|
||||||
@@ -464,7 +464,7 @@ fix_long_path :: proc(path: string) -> string {
|
|||||||
|
|
||||||
prefix :: `\\?`
|
prefix :: `\\?`
|
||||||
|
|
||||||
path_buf := make([]byte, len(prefix)+len(path)+len(`\`), context.temp_allocator)
|
path_buf, _ := make([]byte, len(prefix)+len(path)+len(`\`), context.temp_allocator)
|
||||||
copy(path_buf, prefix)
|
copy(path_buf, prefix)
|
||||||
n := len(path)
|
n := len(path)
|
||||||
r, w := 0, len(prefix)
|
r, w := 0, len(prefix)
|
||||||
@@ -494,80 +494,69 @@ fix_long_path :: proc(path: string) -> string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
link :: proc(old_name, new_name: string) -> (err: Errno) {
|
link :: proc(old_name, new_name: string) -> (err: Error) {
|
||||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||||
n := win32.utf8_to_wstring(fix_long_path(new_name))
|
n := win32.utf8_to_wstring(fix_long_path(new_name))
|
||||||
o := win32.utf8_to_wstring(fix_long_path(old_name))
|
o := win32.utf8_to_wstring(fix_long_path(old_name))
|
||||||
return Errno(win32.CreateHardLinkW(n, o, nil))
|
return Platform_Error(win32.CreateHardLinkW(n, o, nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
unlink :: proc(path: string) -> (err: Errno) {
|
unlink :: proc(path: string) -> (err: Error) {
|
||||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||||
|
|
||||||
if !win32.DeleteFileW(wpath) {
|
if !win32.DeleteFileW(wpath) {
|
||||||
err = Errno(win32.GetLastError())
|
err = get_last_error()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
rename :: proc(old_path, new_path: string) -> (err: Errno) {
|
rename :: proc(old_path, new_path: string) -> (err: Error) {
|
||||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||||
from := win32.utf8_to_wstring(old_path, context.temp_allocator)
|
from := win32.utf8_to_wstring(old_path, context.temp_allocator)
|
||||||
to := win32.utf8_to_wstring(new_path, context.temp_allocator)
|
to := win32.utf8_to_wstring(new_path, context.temp_allocator)
|
||||||
|
|
||||||
if !win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) {
|
if !win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) {
|
||||||
err = Errno(win32.GetLastError())
|
err = get_last_error()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ftruncate :: proc(fd: Handle, length: i64) -> (err: Errno) {
|
ftruncate :: proc(fd: Handle, length: i64) -> (err: Error) {
|
||||||
curr_off, e := seek(fd, 0, 1)
|
curr_off := seek(fd, 0, 1) or_return
|
||||||
if e != 0 {
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
defer seek(fd, curr_off, 0)
|
defer seek(fd, curr_off, 0)
|
||||||
_, e = seek(fd, length, 0)
|
_= seek(fd, length, 0) or_return
|
||||||
if e != 0 {
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
ok := win32.SetEndOfFile(win32.HANDLE(fd))
|
ok := win32.SetEndOfFile(win32.HANDLE(fd))
|
||||||
if !ok {
|
if !ok {
|
||||||
return Errno(win32.GetLastError())
|
return get_last_error()
|
||||||
}
|
}
|
||||||
return ERROR_NONE
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
truncate :: proc(path: string, length: i64) -> (err: Errno) {
|
truncate :: proc(path: string, length: i64) -> (err: Error) {
|
||||||
fd: Handle
|
fd := open(path, O_WRONLY|O_CREATE, 0o666) or_return
|
||||||
fd, err = open(path, O_WRONLY|O_CREATE, 0o666)
|
|
||||||
if err != 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer close(fd)
|
defer close(fd)
|
||||||
err = ftruncate(fd, length)
|
return ftruncate(fd, length)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
remove :: proc(name: string) -> Errno {
|
remove :: proc(name: string) -> Error {
|
||||||
p := win32.utf8_to_wstring(fix_long_path(name))
|
p := win32.utf8_to_wstring(fix_long_path(name))
|
||||||
err, err1: win32.DWORD
|
err, err1: win32.DWORD
|
||||||
if !win32.DeleteFileW(p) {
|
if !win32.DeleteFileW(p) {
|
||||||
err = win32.GetLastError()
|
err = win32.GetLastError()
|
||||||
}
|
}
|
||||||
if err == 0 {
|
if err == 0 {
|
||||||
return 0
|
return nil
|
||||||
}
|
}
|
||||||
if !win32.RemoveDirectoryW(p) {
|
if !win32.RemoveDirectoryW(p) {
|
||||||
err1 = win32.GetLastError()
|
err1 = win32.GetLastError()
|
||||||
}
|
}
|
||||||
if err1 == 0 {
|
if err1 == 0 {
|
||||||
return 0
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != err1 {
|
if err != err1 {
|
||||||
@@ -588,16 +577,17 @@ remove :: proc(name: string) -> Errno {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Errno(err)
|
return Platform_Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pipe :: proc() -> (r, w: Handle, err: Errno) {
|
@(require_results)
|
||||||
|
pipe :: proc() -> (r, w: Handle, err: Error) {
|
||||||
sa: win32.SECURITY_ATTRIBUTES
|
sa: win32.SECURITY_ATTRIBUTES
|
||||||
sa.nLength = size_of(win32.SECURITY_ATTRIBUTES)
|
sa.nLength = size_of(win32.SECURITY_ATTRIBUTES)
|
||||||
sa.bInheritHandle = true
|
sa.bInheritHandle = true
|
||||||
if !win32.CreatePipe((^win32.HANDLE)(&r), (^win32.HANDLE)(&w), &sa, 0) {
|
if !win32.CreatePipe((^win32.HANDLE)(&r), (^win32.HANDLE)(&w), &sa, 0) {
|
||||||
err = Errno(win32.GetLastError())
|
err = get_last_error()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+98
-66
@@ -1,6 +1,8 @@
|
|||||||
package os
|
package os
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
import "base:runtime"
|
import "base:runtime"
|
||||||
|
import "core:io"
|
||||||
import "core:strconv"
|
import "core:strconv"
|
||||||
import "core:unicode/utf8"
|
import "core:unicode/utf8"
|
||||||
|
|
||||||
@@ -13,15 +15,15 @@ SEEK_SET :: 0
|
|||||||
SEEK_CUR :: 1
|
SEEK_CUR :: 1
|
||||||
SEEK_END :: 2
|
SEEK_END :: 2
|
||||||
|
|
||||||
write_string :: proc(fd: Handle, str: string) -> (int, Errno) {
|
write_string :: proc(fd: Handle, str: string) -> (int, Error) {
|
||||||
return write(fd, transmute([]byte)str)
|
return write(fd, transmute([]byte)str)
|
||||||
}
|
}
|
||||||
|
|
||||||
write_byte :: proc(fd: Handle, b: byte) -> (int, Errno) {
|
write_byte :: proc(fd: Handle, b: byte) -> (int, Error) {
|
||||||
return write(fd, []byte{b})
|
return write(fd, []byte{b})
|
||||||
}
|
}
|
||||||
|
|
||||||
write_rune :: proc(fd: Handle, r: rune) -> (int, Errno) {
|
write_rune :: proc(fd: Handle, r: rune) -> (int, Error) {
|
||||||
if r < utf8.RUNE_SELF {
|
if r < utf8.RUNE_SELF {
|
||||||
return write_byte(fd, byte(r))
|
return write_byte(fd, byte(r))
|
||||||
}
|
}
|
||||||
@@ -30,105 +32,94 @@ write_rune :: proc(fd: Handle, r: rune) -> (int, Errno) {
|
|||||||
return write(fd, b[:n])
|
return write(fd, b[:n])
|
||||||
}
|
}
|
||||||
|
|
||||||
write_encoded_rune :: proc(fd: Handle, r: rune) {
|
write_encoded_rune :: proc(f: Handle, r: rune) -> (n: int, err: Error) {
|
||||||
write_byte(fd, '\'')
|
wrap :: proc(m: int, merr: Error, n: ^int, err: ^Error) -> bool {
|
||||||
|
n^ += m
|
||||||
|
if merr != nil {
|
||||||
|
err^ = merr
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if wrap(write_byte(f, '\''), &n, &err) { return }
|
||||||
|
|
||||||
switch r {
|
switch r {
|
||||||
case '\a': write_string(fd, "\\a")
|
case '\a': if wrap(write_string(f, "\\a"), &n, &err) { return }
|
||||||
case '\b': write_string(fd, "\\b")
|
case '\b': if wrap(write_string(f, "\\b"), &n, &err) { return }
|
||||||
case '\e': write_string(fd, "\\e")
|
case '\e': if wrap(write_string(f, "\\e"), &n, &err) { return }
|
||||||
case '\f': write_string(fd, "\\f")
|
case '\f': if wrap(write_string(f, "\\f"), &n, &err) { return }
|
||||||
case '\n': write_string(fd, "\\n")
|
case '\n': if wrap(write_string(f, "\\n"), &n, &err) { return }
|
||||||
case '\r': write_string(fd, "\\r")
|
case '\r': if wrap(write_string(f, "\\r"), &n, &err) { return }
|
||||||
case '\t': write_string(fd, "\\t")
|
case '\t': if wrap(write_string(f, "\\t"), &n, &err) { return }
|
||||||
case '\v': write_string(fd, "\\v")
|
case '\v': if wrap(write_string(f, "\\v"), &n, &err) { return }
|
||||||
case:
|
case:
|
||||||
if r < 32 {
|
if r < 32 {
|
||||||
write_string(fd, "\\x")
|
if wrap(write_string(f, "\\x"), &n, &err) { return }
|
||||||
b: [2]byte
|
b: [2]byte
|
||||||
s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil)
|
s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil)
|
||||||
switch len(s) {
|
switch len(s) {
|
||||||
case 0: write_string(fd, "00")
|
case 0: if wrap(write_string(f, "00"), &n, &err) { return }
|
||||||
case 1: write_rune(fd, '0')
|
case 1: if wrap(write_rune(f, '0'), &n, &err) { return }
|
||||||
case 2: write_string(fd, s)
|
case 2: if wrap(write_string(f, s), &n, &err) { return }
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
write_rune(fd, r)
|
if wrap(write_rune(f, r), &n, &err) { return }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
write_byte(fd, '\'')
|
_ = wrap(write_byte(f, '\''), &n, &err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
read_at_least :: proc(fd: Handle, buf: []byte, min: int) -> (n: int, err: Errno) {
|
read_at_least :: proc(fd: Handle, buf: []byte, min: int) -> (n: int, err: Error) {
|
||||||
if len(buf) < min {
|
if len(buf) < min {
|
||||||
return 0, -1
|
return 0, io.Error.Short_Buffer
|
||||||
}
|
}
|
||||||
nn := max(int)
|
nn := max(int)
|
||||||
for nn > 0 && n < min && err == 0 {
|
for nn > 0 && n < min && err == nil {
|
||||||
nn, err = read(fd, buf[n:])
|
nn, err = read(fd, buf[n:])
|
||||||
n += nn
|
n += nn
|
||||||
}
|
}
|
||||||
if n >= min {
|
if n >= min {
|
||||||
err = 0
|
err = nil
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
read_full :: proc(fd: Handle, buf: []byte) -> (n: int, err: Errno) {
|
read_full :: proc(fd: Handle, buf: []byte) -> (n: int, err: Error) {
|
||||||
return read_at_least(fd, buf, len(buf))
|
return read_at_least(fd, buf, len(buf))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
file_size_from_path :: proc(path: string) -> i64 {
|
file_size_from_path :: proc(path: string) -> i64 {
|
||||||
fd, err := open(path, O_RDONLY, 0)
|
fd, err := open(path, O_RDONLY, 0)
|
||||||
if err != 0 {
|
if err != nil {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
defer close(fd)
|
defer close(fd)
|
||||||
|
|
||||||
length: i64
|
length: i64
|
||||||
if length, err = file_size(fd); err != 0 {
|
if length, err = file_size(fd); err != nil {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
return length
|
return length
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
read_entire_file_from_filename :: proc(name: string, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) {
|
read_entire_file_from_filename :: proc(name: string, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) {
|
||||||
context.allocator = allocator
|
err: Error
|
||||||
|
data, err = read_entire_file_from_filename_or_err(name, allocator, loc)
|
||||||
fd, err := open(name, O_RDONLY, 0)
|
success = err == nil
|
||||||
if err != 0 {
|
return
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
defer close(fd)
|
|
||||||
|
|
||||||
return read_entire_file_from_handle(fd, allocator, loc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) {
|
read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) {
|
||||||
context.allocator = allocator
|
err: Error
|
||||||
|
data, err = read_entire_file_from_handle_or_err(fd, allocator, loc)
|
||||||
length: i64
|
success = err == nil
|
||||||
err: Errno
|
return
|
||||||
if length, err = file_size(fd); err != 0 {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
if length <= 0 {
|
|
||||||
return nil, true
|
|
||||||
}
|
|
||||||
|
|
||||||
data = make([]byte, int(length), allocator, loc)
|
|
||||||
if data == nil {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes_read, read_err := read_full(fd, data)
|
|
||||||
if read_err != ERROR_NONE {
|
|
||||||
delete(data)
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
return data[:bytes_read], true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
read_entire_file :: proc {
|
read_entire_file :: proc {
|
||||||
@@ -136,7 +127,50 @@ read_entire_file :: proc {
|
|||||||
read_entire_file_from_handle,
|
read_entire_file_from_handle,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
|
read_entire_file_from_filename_or_err :: proc(name: string, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Error) {
|
||||||
|
context.allocator = allocator
|
||||||
|
|
||||||
|
fd := open(name, O_RDONLY, 0) or_return
|
||||||
|
defer close(fd)
|
||||||
|
|
||||||
|
return read_entire_file_from_handle_or_err(fd, allocator, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
|
read_entire_file_from_handle_or_err :: proc(fd: Handle, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Error) {
|
||||||
|
context.allocator = allocator
|
||||||
|
|
||||||
|
length := file_size(fd) or_return
|
||||||
|
if length <= 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
data = make([]byte, int(length), allocator, loc) or_return
|
||||||
|
if data == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
defer if err != nil {
|
||||||
|
delete(data, allocator)
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes_read := read_full(fd, data) or_return
|
||||||
|
data = data[:bytes_read]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
read_entire_file_or_err :: proc {
|
||||||
|
read_entire_file_from_filename_or_err,
|
||||||
|
read_entire_file_from_handle_or_err,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
write_entire_file :: proc(name: string, data: []byte, truncate := true) -> (success: bool) {
|
write_entire_file :: proc(name: string, data: []byte, truncate := true) -> (success: bool) {
|
||||||
|
return write_entire_file_or_err(name, data, truncate) == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
|
write_entire_file_or_err :: proc(name: string, data: []byte, truncate := true) -> Error {
|
||||||
flags: int = O_WRONLY|O_CREATE
|
flags: int = O_WRONLY|O_CREATE
|
||||||
if truncate {
|
if truncate {
|
||||||
flags |= O_TRUNC
|
flags |= O_TRUNC
|
||||||
@@ -148,21 +182,18 @@ write_entire_file :: proc(name: string, data: []byte, truncate := true) -> (succ
|
|||||||
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
|
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
|
||||||
}
|
}
|
||||||
|
|
||||||
fd, err := open(name, flags, mode)
|
fd := open(name, flags, mode) or_return
|
||||||
if err != 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
defer close(fd)
|
defer close(fd)
|
||||||
|
|
||||||
_, write_err := write(fd, data)
|
_ = write(fd, data) or_return
|
||||||
return write_err == 0
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
write_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) {
|
write_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Error) {
|
||||||
return write(fd, ([^]byte)(data)[:len])
|
return write(fd, ([^]byte)(data)[:len])
|
||||||
}
|
}
|
||||||
|
|
||||||
read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) {
|
read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Error) {
|
||||||
return read(fd, ([^]byte)(data)[:len])
|
return read(fd, ([^]byte)(data)[:len])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,6 +204,7 @@ heap_alloc :: runtime.heap_alloc
|
|||||||
heap_resize :: runtime.heap_resize
|
heap_resize :: runtime.heap_resize
|
||||||
heap_free :: runtime.heap_free
|
heap_free :: runtime.heap_free
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
processor_core_count :: proc() -> int {
|
processor_core_count :: proc() -> int {
|
||||||
return _processor_core_count()
|
return _processor_core_count()
|
||||||
}
|
}
|
||||||
|
|||||||
+27
-11
@@ -10,20 +10,29 @@ file_allocator :: proc() -> runtime.Allocator {
|
|||||||
|
|
||||||
temp_allocator_proc :: runtime.arena_allocator_proc
|
temp_allocator_proc :: runtime.arena_allocator_proc
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
MAX_TEMP_ARENA_COUNT :: 2
|
||||||
|
|
||||||
@(private="file", thread_local)
|
@(private="file", thread_local)
|
||||||
global_default_temp_allocator_arena: runtime.Arena
|
global_default_temp_allocator_arenas: [MAX_TEMP_ARENA_COUNT]runtime.Arena
|
||||||
|
|
||||||
|
@(private="file", thread_local)
|
||||||
|
global_default_temp_allocator_index: uint
|
||||||
|
|
||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
temp_allocator :: proc() -> runtime.Allocator {
|
temp_allocator :: proc() -> runtime.Allocator {
|
||||||
return runtime.Allocator{
|
return runtime.Allocator{
|
||||||
procedure = temp_allocator_proc,
|
procedure = temp_allocator_proc,
|
||||||
data = &global_default_temp_allocator_arena,
|
data = &global_default_temp_allocator_arenas[global_default_temp_allocator_index],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
temp_allocator_temp_begin :: proc(loc := #caller_location) -> (temp: runtime.Arena_Temp) {
|
temp_allocator_temp_begin :: proc(loc := #caller_location) -> (temp: runtime.Arena_Temp) {
|
||||||
temp = runtime.arena_temp_begin(&global_default_temp_allocator_arena, loc)
|
temp = runtime.arena_temp_begin(&global_default_temp_allocator_arenas[global_default_temp_allocator_index], loc)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,16 +42,23 @@ temp_allocator_temp_end :: proc(temp: runtime.Arena_Temp, loc := #caller_locatio
|
|||||||
|
|
||||||
@(fini, private)
|
@(fini, private)
|
||||||
temp_allocator_fini :: proc() {
|
temp_allocator_fini :: proc() {
|
||||||
runtime.arena_destroy(&global_default_temp_allocator_arena)
|
for &arena in global_default_temp_allocator_arenas {
|
||||||
global_default_temp_allocator_arena = {}
|
runtime.arena_destroy(&arena)
|
||||||
|
}
|
||||||
|
global_default_temp_allocator_arenas = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(deferred_out=temp_allocator_temp_end)
|
TEMP_ALLOCATOR_GUARD_END :: proc(temp: runtime.Arena_Temp, loc := #caller_location) {
|
||||||
TEMP_ALLOCATOR_GUARD :: #force_inline proc(ignore := false, loc := #caller_location) -> (runtime.Arena_Temp, runtime.Source_Code_Location) {
|
runtime.arena_temp_end(temp, loc)
|
||||||
if ignore {
|
if temp.arena != nil {
|
||||||
return {}, loc
|
global_default_temp_allocator_index = (global_default_temp_allocator_index-1)%MAX_TEMP_ARENA_COUNT
|
||||||
} else {
|
|
||||||
return temp_allocator_temp_begin(loc), loc
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(deferred_out=TEMP_ALLOCATOR_GUARD_END)
|
||||||
|
TEMP_ALLOCATOR_GUARD :: #force_inline proc(loc := #caller_location) -> (runtime.Arena_Temp, runtime.Source_Code_Location) {
|
||||||
|
tmp := temp_allocator_temp_begin(loc)
|
||||||
|
global_default_temp_allocator_index = (global_default_temp_allocator_index+1)%MAX_TEMP_ARENA_COUNT
|
||||||
|
return tmp, loc
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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