mirror of
https://github.com/odin-lang/Odin.git
synced 2026-08-04 13:18:31 +00:00
Merge branch 'master' into master
This commit is contained in:
10
.gitattributes
vendored
10
.gitattributes
vendored
@@ -1,6 +1,12 @@
|
||||
*.odin linguist-language=Odin
|
||||
* text=auto
|
||||
|
||||
# These files must always have *nix line-endings
|
||||
Makefile text eol=lf
|
||||
*.sh text eol=lf
|
||||
*.sh text eol=lf
|
||||
vendor/box2d/lib/box2d_windows_amd64_avx2.lib filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/box2d/lib/box2d_windows_amd64_sse2.lib filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/miniaudio/lib/miniaudio.lib filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/sdl3/SDL3.dll filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/sdl3/SDL3.lib filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/sdl3/mixer/*.dll filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/sdl3/mixer/*.lib filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
64
.github/workflows/ci.yml
vendored
64
.github/workflows/ci.yml
vendored
@@ -6,7 +6,7 @@ jobs:
|
||||
name: NetBSD Build, Check, and Test
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
PKGSRC_BRANCH: 2025Q2
|
||||
PKGSRC_BRANCH: 2025Q4
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build, Check, and Test
|
||||
@@ -93,8 +93,8 @@ jobs:
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: |
|
||||
brew update
|
||||
brew install llvm@20 wasmtime lua@5.4 lld
|
||||
echo "$(brew --prefix llvm@20)/bin" >> $GITHUB_PATH
|
||||
brew install llvm@22 wasmtime lua@5.4 lld
|
||||
echo "$(brew --prefix llvm@22)/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Download LLVM (Ubuntu)
|
||||
if: matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm'
|
||||
@@ -139,8 +139,19 @@ jobs:
|
||||
run: ./odin test tests/core/normal.odin -file -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -sanitize:address
|
||||
- name: Optimized Core library tests
|
||||
run: ./odin test tests/core/speed.odin -o:speed -file -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -sanitize:address
|
||||
- name: Wycheproof tests
|
||||
|
||||
run: ./odin test tests/core/crypto/wycheproof -vet -vet-tabs -strict-style -vet-style -vet-cast -warnings-as-errors -disallow-do -o:speed
|
||||
- name: Vendor library tests
|
||||
run: ./odin test tests/vendor -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -sanitize:address
|
||||
if: matrix.os != 'macos-15-intel' && matrix.os != 'macos-latest'
|
||||
- name: Vendor library tests (MacOS ARM)
|
||||
run: ./odin test tests/vendor -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -sanitize:address -extra-linker-flags:"-L/opt/homebrew/opt/lua@5.4/lib"
|
||||
if: matrix.os == 'macos-latest'
|
||||
- name: Vendor library tests (MacOS Intel)
|
||||
run: ./odin test tests/vendor -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -sanitize:address -extra-linker-flags:"-L/usr/local/opt/lua@5.4/lib"
|
||||
if: matrix.os == 'macos-15-intel'
|
||||
|
||||
- name: Internals tests
|
||||
run: ./odin test tests/internal -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -sanitize:address
|
||||
- name: GitHub Issue tests
|
||||
@@ -156,18 +167,14 @@ jobs:
|
||||
|
||||
- name: Check benchmarks
|
||||
run: ./odin check tests/benchmark -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -no-entry-point
|
||||
|
||||
- name: Odin check examples/all for Linux i386
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:linux_i386
|
||||
- name: Odin check examples/all for Linux arm64
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:linux_arm64
|
||||
- name: Odin check examples/all for FreeBSD amd64
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freebsd_amd64
|
||||
- name: Odin check examples/all for OpenBSD amd64
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:openbsd_amd64
|
||||
|
||||
- name: Odin check examples/all for js_wasm32
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -no-entry-point -target:js_wasm32
|
||||
@@ -178,12 +185,6 @@ jobs:
|
||||
- name: Odin check examples/all/sdl3 for Linux i386
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all/sdl3 -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -no-entry-point -target:linux_i386
|
||||
- name: Odin check examples/all/sdl3 for Linux arm64
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all/sdl3 -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -no-entry-point -target:linux_arm64
|
||||
- name: Odin check examples/all/sdl3 for FreeBSD amd64
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all/sdl3 -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -no-entry-point -target:freebsd_amd64
|
||||
- name: Odin check examples/all/sdl3 for OpenBSD amd64
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all/sdl3 -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -no-entry-point -target:openbsd_amd64
|
||||
@@ -197,7 +198,7 @@ jobs:
|
||||
- name: build Odin
|
||||
shell: cmd
|
||||
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"
|
||||
./build.bat 1
|
||||
- name: Odin version
|
||||
run: ./odin version
|
||||
@@ -206,70 +207,75 @@ jobs:
|
||||
- name: Odin check
|
||||
shell: cmd
|
||||
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"
|
||||
odin check examples/demo -vet
|
||||
- name: Odin run
|
||||
shell: cmd
|
||||
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"
|
||||
odin run examples/demo
|
||||
- name: Odin run -debug
|
||||
shell: cmd
|
||||
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"
|
||||
odin run examples/demo -debug -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do
|
||||
- name: Odin check examples/all
|
||||
shell: cmd
|
||||
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"
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do
|
||||
- name: Odin check examples/all/sdl3
|
||||
shell: cmd
|
||||
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"
|
||||
odin check examples/all/sdl3 -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -no-entry-point
|
||||
- name: Core library tests
|
||||
shell: cmd
|
||||
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"
|
||||
odin test tests/core/normal.odin -file -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -sanitize:address
|
||||
- name: Optimized core library tests
|
||||
shell: cmd
|
||||
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"
|
||||
odin test tests/core/speed.odin -o:speed -file -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -sanitize:address
|
||||
- name: Wycheproof tests
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
|
||||
odin test tests/core/crypto/wycheproof -vet -vet-tabs -strict-style -vet-style -vet-cast -warnings-as-errors -disallow-do -o:speed -microarch:native
|
||||
- name: Vendor library tests
|
||||
shell: cmd
|
||||
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 -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -sanitize:address
|
||||
- name: Odin internals tests
|
||||
shell: cmd
|
||||
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"
|
||||
odin test tests/internal -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -sanitize:address
|
||||
- name: Check issues
|
||||
shell: cmd
|
||||
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"
|
||||
cd tests/issues
|
||||
call run.bat
|
||||
- name: Check benchmarks
|
||||
shell: cmd
|
||||
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"
|
||||
odin check tests/benchmark -vet -strict-style -no-entry-point
|
||||
- name: Odin documentation tests
|
||||
shell: cmd
|
||||
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"
|
||||
cd tests\documentation
|
||||
call build.bat
|
||||
- name: Odin check examples/all for Windows 32bits
|
||||
shell: cmd
|
||||
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"
|
||||
odin check examples/all -strict-style -target:windows_i386
|
||||
|
||||
build_linux_riscv64:
|
||||
|
||||
10
.github/workflows/nightly.yml
vendored
10
.github/workflows/nightly.yml
vendored
@@ -12,6 +12,8 @@ jobs:
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
lfs: true
|
||||
- name: build Odin
|
||||
shell: cmd
|
||||
run: |
|
||||
@@ -47,6 +49,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
lfs: true
|
||||
- name: (Linux) Download LLVM and Build Odin
|
||||
run: |
|
||||
docker run --rm -v "$PWD:/src" -w /src alpine sh -c '
|
||||
@@ -87,6 +91,8 @@ jobs:
|
||||
runs-on: ubuntu-24.04-arm
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
lfs: true
|
||||
- name: (Linux ARM) Download LLVM and Build Odin
|
||||
run: |
|
||||
docker run --rm -v "$PWD:/src" -w /src arm64v8/alpine sh -c '
|
||||
@@ -127,6 +133,8 @@ jobs:
|
||||
runs-on: macos-15-intel
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
lfs: true
|
||||
- name: Download LLVM and setup PATH
|
||||
run: |
|
||||
brew update
|
||||
@@ -166,6 +174,8 @@ jobs:
|
||||
runs-on: macos-latest # ARM machine
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
lfs: true
|
||||
- name: Download LLVM and setup PATH
|
||||
run: |
|
||||
brew update
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -279,6 +279,9 @@ demo.bin
|
||||
libLLVM*.so*
|
||||
*.a
|
||||
|
||||
# WASM
|
||||
*.wasm
|
||||
|
||||
# shared collection
|
||||
shared/
|
||||
|
||||
|
||||
@@ -38,6 +38,8 @@ count_ones :: proc(x: $T) -> T where type_is_integer(T) || type_is_sim
|
||||
count_zeros :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
|
||||
count_trailing_zeros :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
|
||||
count_leading_zeros :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
|
||||
count_trailing_ones :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
|
||||
count_leading_ones :: 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) ---
|
||||
|
||||
@@ -75,7 +77,9 @@ prefetch_write_instruction :: proc(address: rawptr, #const locality: i32 /* 0..=
|
||||
prefetch_write_data :: proc(address: rawptr, #const locality: i32 /* 0..=3 */) ---
|
||||
|
||||
// Compiler Hints
|
||||
expect :: proc(val, expected_val: $T) -> T ---
|
||||
expect :: proc(val, expected_val: $T) -> T ---
|
||||
likely :: proc(val: $T) -> T where type_is_boolean(T) ---
|
||||
unlikely :: proc(val: $T) -> T where type_is_boolean(T) ---
|
||||
|
||||
// Linux and Darwin Only
|
||||
syscall :: proc(id: uintptr, args: ..uintptr) -> uintptr ---
|
||||
@@ -178,6 +182,7 @@ type_is_bit_set :: proc($T: typeid) -> bool ---
|
||||
type_is_bit_field :: proc($T: typeid) -> bool ---
|
||||
type_is_simd_vector :: proc($T: typeid) -> bool ---
|
||||
type_is_matrix :: proc($T: typeid) -> bool ---
|
||||
type_is_fixed_capacity_dynamic_array :: proc($T: typeid) -> bool ---
|
||||
|
||||
type_has_nil :: proc($T: typeid) -> bool ---
|
||||
|
||||
@@ -220,6 +225,8 @@ type_is_superset_of :: proc($Super, $Sub: typeid) -> bool ---
|
||||
|
||||
type_field_index_of :: proc($T: typeid, $name: string) -> uintptr ---
|
||||
|
||||
type_fixed_capacity_dynamic_array_len_offset :: proc($T: typeid/[dynamic; $N]$E) -> uintptr ---
|
||||
|
||||
// "Contiguous" means that the set of enum constants, when sorted, have a difference of either 0 or 1 between consecutive values.
|
||||
// This is the exact opposite of "sparse".
|
||||
type_enum_is_contiguous :: proc($T: typeid) -> bool where type_is_enum(T) ---
|
||||
@@ -355,7 +362,7 @@ has_target_feature :: proc($test: $T) -> bool where type_is_string(T) || type_is
|
||||
|
||||
|
||||
// Utility Calls
|
||||
concatentate :: proc(x, y: $T, z: ..T) -> T where type_is_array(T) || type_is_slice(T) ---
|
||||
concatenate :: proc(x, y: $T, z: ..T) -> T where type_is_array(T) || type_is_slice(T) ---
|
||||
|
||||
// Returns the value of the procedure where `x` must be a call expression
|
||||
procedure_of :: proc(x: $T) -> T where type_is_proc(T) ---
|
||||
|
||||
@@ -39,6 +39,10 @@ Calling_Convention :: enum u8 {
|
||||
|
||||
Win64 = 9,
|
||||
SysV = 10,
|
||||
|
||||
Preserve_None = 11,
|
||||
Preserve_Most = 12,
|
||||
Preserve_All = 13,
|
||||
}
|
||||
|
||||
Type_Info_Enum_Value :: distinct i64
|
||||
@@ -122,6 +126,7 @@ Type_Info_Struct_Flag :: enum u8 {
|
||||
raw_union = 1,
|
||||
all_or_none = 2,
|
||||
align = 3,
|
||||
simple = 4,
|
||||
}
|
||||
|
||||
Type_Info_Struct :: struct {
|
||||
@@ -136,7 +141,7 @@ Type_Info_Struct :: struct {
|
||||
|
||||
flags: Type_Info_Struct_Flags,
|
||||
|
||||
// These are only set iff this structure is an SOA structure
|
||||
// These are only set if and only if (⟺) this structure is an SOA structure
|
||||
soa_kind: Type_Info_Struct_Soa_Kind,
|
||||
soa_len: i32,
|
||||
soa_base_type: ^Type_Info,
|
||||
@@ -165,10 +170,11 @@ Type_Info_Map :: struct {
|
||||
map_info: ^Map_Info,
|
||||
}
|
||||
Type_Info_Bit_Set :: struct {
|
||||
elem: ^Type_Info,
|
||||
underlying: ^Type_Info, // Possibly nil
|
||||
lower: i64,
|
||||
upper: i64,
|
||||
elem: ^Type_Info,
|
||||
underlying: ^Type_Info,
|
||||
explicit_underlying: bool, // false = bit_set[T], true = bit_set[T, U]
|
||||
lower: i64,
|
||||
upper: i64,
|
||||
}
|
||||
Type_Info_Simd_Vector :: struct {
|
||||
elem: ^Type_Info,
|
||||
@@ -200,6 +206,14 @@ Type_Info_Bit_Field :: struct {
|
||||
field_count: int,
|
||||
}
|
||||
|
||||
Type_Info_Fixed_Capacity_Dynamic_Array :: struct {
|
||||
elem: ^Type_Info,
|
||||
elem_size: int,
|
||||
capacity: int,
|
||||
len_offset: uintptr,
|
||||
}
|
||||
|
||||
|
||||
Type_Info_Flag :: enum u8 {
|
||||
Comparable = 0,
|
||||
Simple_Compare = 1,
|
||||
@@ -240,6 +254,7 @@ Type_Info :: struct {
|
||||
Type_Info_Matrix,
|
||||
Type_Info_Soa_Pointer,
|
||||
Type_Info_Bit_Field,
|
||||
Type_Info_Fixed_Capacity_Dynamic_Array,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -419,6 +434,11 @@ Raw_Dynamic_Array :: struct {
|
||||
allocator: Allocator,
|
||||
}
|
||||
|
||||
Raw_Fixed_Capacity_Dynamic_Array :: struct($Capacity: uint, $T: typeid) {
|
||||
data: [Capacity]T,
|
||||
len: int,
|
||||
}
|
||||
|
||||
// The raw, type-erased representation of a map.
|
||||
//
|
||||
// 32-bytes on 64-bit
|
||||
@@ -653,9 +673,8 @@ type_info_base :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
|
||||
return base
|
||||
}
|
||||
|
||||
|
||||
// type_info_core returns the core-type of a `^Type_Info` stripping the `distinct`ness from the first level AND/OR
|
||||
// returns the backing integer type of an enum or bit_set `^Type_Info`.
|
||||
// returns the backing integer type of an enum `^Type_Info`.
|
||||
// This is also aliased as `type_info_base_without_enum`
|
||||
@(require_results)
|
||||
type_info_core :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
|
||||
@@ -675,11 +694,35 @@ type_info_core :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
|
||||
return base
|
||||
}
|
||||
|
||||
// type_info_base_without_enum returns the core-type of a `^Type_Info` stripping the `distinct`ness from the first level AND/OR
|
||||
|
||||
|
||||
// type_info_underlying returns the underlying (backing) type of a `^Type_Info` stripping the `distinct`ness from the first level AND/OR
|
||||
// returns the backing integer type of an enum `^Type_Info` AND/OR the underlying integer type of a bit_set or bit_field.
|
||||
@(require_results)
|
||||
type_info_underlying :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
|
||||
if info == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
base := info
|
||||
loop: for {
|
||||
#partial switch i in base.variant {
|
||||
case Type_Info_Named: base = i.base
|
||||
case Type_Info_Enum: base = i.base
|
||||
case Type_Info_Bit_Set: base = i.underlying
|
||||
case Type_Info_Bit_Field: base = i.backing_type
|
||||
case: break loop
|
||||
}
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
// `type_info_base_without_enum` returns the core-type of a `^Type_Info` stripping the `distinct`ness from the first level AND/OR
|
||||
// returns the backing integer type of an enum or bit_set `^Type_Info`.
|
||||
// This is also aliased as `type_info_core`
|
||||
type_info_base_without_enum :: type_info_core
|
||||
|
||||
@(require_results)
|
||||
__type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info #no_bounds_check {
|
||||
n := u64(len(type_table))
|
||||
i := transmute(u64)id % n
|
||||
@@ -695,14 +738,16 @@ __type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info #no_bounds_check
|
||||
|
||||
when !ODIN_NO_RTTI {
|
||||
// typeid_base returns the base-type of a `typeid` stripping the `distinct`ness from the first level
|
||||
@(require_results)
|
||||
typeid_base :: proc "contextless" (id: typeid) -> typeid {
|
||||
ti := type_info_of(id)
|
||||
ti = type_info_base(ti)
|
||||
return ti.id
|
||||
}
|
||||
// typeid_core returns the core-type of a `typeid` stripping the `distinct`ness from the first level AND/OR
|
||||
// returns the backing integer type of an enum or bit_set `typeid`.
|
||||
// returns the backing integer type of an enum `typeid`.
|
||||
// This is also aliased as `typeid_base_without_enum`
|
||||
@(require_results)
|
||||
typeid_core :: proc "contextless" (id: typeid) -> typeid {
|
||||
ti := type_info_core(type_info_of(id))
|
||||
return ti.id
|
||||
@@ -712,6 +757,12 @@ when !ODIN_NO_RTTI {
|
||||
// returns the backing integer type of an enum or bit_set `typeid`.
|
||||
// This is also aliased as `typeid_core`
|
||||
typeid_base_without_enum :: typeid_core
|
||||
|
||||
@(require_results)
|
||||
typeid_underlying :: proc "contextless" (id: typeid) -> typeid {
|
||||
ti := type_info_underlying(type_info_of(id))
|
||||
return ti.id
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -119,14 +119,14 @@ copy :: proc{copy_slice, copy_from_string, copy_from_string16}
|
||||
|
||||
|
||||
|
||||
// `unordered_remove` removed the element at the specified `index`. It does so by replacing the current end value
|
||||
// `unordered_remove_dynamic_array` removed the element at the specified `index`. It does so by replacing the current end value
|
||||
// with the old value, and reducing the length of the dynamic array by 1.
|
||||
//
|
||||
// Note: This is an O(1) operation.
|
||||
// Note: If you want the elements to remain in their order, use `ordered_remove`.
|
||||
// Note: If the index is out of bounds, this procedure will panic.
|
||||
@builtin
|
||||
unordered_remove :: proc(array: ^$D/[dynamic]$T, #any_int index: int, loc := #caller_location) #no_bounds_check {
|
||||
unordered_remove_dynamic_array :: proc(array: ^$D/[dynamic]$T, #any_int index: int, loc := #caller_location) #no_bounds_check {
|
||||
bounds_check_error_loc(loc, index, len(array))
|
||||
n := len(array)-1
|
||||
if index != n {
|
||||
@@ -134,13 +134,13 @@ unordered_remove :: proc(array: ^$D/[dynamic]$T, #any_int index: int, loc := #ca
|
||||
}
|
||||
(^Raw_Dynamic_Array)(array).len -= 1
|
||||
}
|
||||
// `ordered_remove` removed the element at the specified `index` whilst keeping the order of the other elements.
|
||||
// `ordered_remove_dynamic_array` removed the element at the specified `index` whilst keeping the order of the other elements.
|
||||
//
|
||||
// Note: This is an O(N) operation.
|
||||
// Note: If the elements do not have to remain in their order, prefer `unordered_remove`.
|
||||
// Note: If the index is out of bounds, this procedure will panic.
|
||||
@builtin
|
||||
ordered_remove :: proc(array: ^$D/[dynamic]$T, #any_int index: int, loc := #caller_location) #no_bounds_check {
|
||||
ordered_remove_dynamic_array :: proc(array: ^$D/[dynamic]$T, #any_int index: int, loc := #caller_location) #no_bounds_check {
|
||||
bounds_check_error_loc(loc, index, len(array))
|
||||
if index+1 < len(array) {
|
||||
copy(array[index:], array[index+1:])
|
||||
@@ -148,12 +148,12 @@ ordered_remove :: proc(array: ^$D/[dynamic]$T, #any_int index: int, loc := #call
|
||||
(^Raw_Dynamic_Array)(array).len -= 1
|
||||
}
|
||||
|
||||
// `remove_range` removes a range of elements specified by the range `lo` and `hi`, whilst keeping the order of the other elements.
|
||||
// `remove_range_dynamic_array` removes a range of elements specified by the range `lo` and `hi`, whilst keeping the order of the other elements.
|
||||
//
|
||||
// Note: This is an O(N) operation.
|
||||
// Note: If the range is out of bounds, this procedure will panic.
|
||||
@builtin
|
||||
remove_range :: proc(array: ^$D/[dynamic]$T, #any_int lo, hi: int, loc := #caller_location) #no_bounds_check {
|
||||
remove_range_dynamic_array :: proc(array: ^$D/[dynamic]$T, #any_int lo, hi: int, loc := #caller_location) #no_bounds_check {
|
||||
slice_expr_error_lo_hi_loc(loc, lo, hi, len(array))
|
||||
n := max(hi-lo, 0)
|
||||
if n > 0 {
|
||||
@@ -164,29 +164,117 @@ remove_range :: proc(array: ^$D/[dynamic]$T, #any_int lo, hi: int, loc := #calle
|
||||
}
|
||||
}
|
||||
|
||||
// `unordered_remove_fixed_capacity_dynamic_array` removed the element at the specified `index`. It does so by replacing the current end value
|
||||
// with the old value, and reducing the length of the dynamic array by 1.
|
||||
//
|
||||
// Note: This is an O(1) operation.
|
||||
// Note: If you want the elements to remain in their order, use `ordered_remove`.
|
||||
// Note: If the index is out of bounds, this procedure will panic.
|
||||
@builtin
|
||||
unordered_remove_fixed_capacity_dynamic_array :: proc(array: ^$D/[dynamic; $N]$E, #any_int index: int, loc := #caller_location) #no_bounds_check {
|
||||
bounds_check_error_loc(loc, index, len(array))
|
||||
n := len(array)-1
|
||||
if index != n {
|
||||
array[index] = array[n]
|
||||
}
|
||||
(^Raw_Fixed_Capacity_Dynamic_Array(N, E))(array).len -= 1
|
||||
}
|
||||
// `ordered_remove_fixed_capacity_dynamic_array` removed the element at the specified `index` whilst keeping the order of the other elements.
|
||||
//
|
||||
// Note: This is an O(N) operation.
|
||||
// Note: If the elements do not have to remain in their order, prefer `unordered_remove`.
|
||||
// Note: If the index is out of bounds, this procedure will panic.
|
||||
@builtin
|
||||
ordered_remove_fixed_capacity_dynamic_array :: proc(array: ^$D/[dynamic; $N]$E, #any_int index: int, loc := #caller_location) #no_bounds_check {
|
||||
bounds_check_error_loc(loc, index, len(array))
|
||||
if index+1 < len(array) {
|
||||
copy(array[index:], array[index+1:])
|
||||
}
|
||||
(^Raw_Fixed_Capacity_Dynamic_Array(N, E))(array).len -= 1
|
||||
}
|
||||
|
||||
// `pop` will remove and return the end value of dynamic array `array` and reduces the length of `array` by 1.
|
||||
// `remove_range_fixed_capacity_dynamic_array` removes a range of elements specified by the range `lo` and `hi`, whilst keeping the order of the other elements.
|
||||
//
|
||||
// Note: This is an O(N) operation.
|
||||
// Note: If the range is out of bounds, this procedure will panic.
|
||||
@builtin
|
||||
remove_range_fixed_capacity_dynamic_array :: proc(array: ^$D/[dynamic; $N]$E, #any_int lo, hi: int, loc := #caller_location) #no_bounds_check {
|
||||
slice_expr_error_lo_hi_loc(loc, lo, hi, len(array))
|
||||
n := max(hi-lo, 0)
|
||||
if n > 0 {
|
||||
if hi != len(array) {
|
||||
copy(array[lo:], array[hi:])
|
||||
}
|
||||
(^Raw_Fixed_Capacity_Dynamic_Array(N, E))(array).len -= n
|
||||
}
|
||||
}
|
||||
|
||||
@builtin
|
||||
unordered_remove :: proc{
|
||||
unordered_remove_dynamic_array,
|
||||
unordered_remove_fixed_capacity_dynamic_array,
|
||||
}
|
||||
|
||||
|
||||
@builtin
|
||||
ordered_remove :: proc{
|
||||
ordered_remove_dynamic_array,
|
||||
ordered_remove_fixed_capacity_dynamic_array,
|
||||
}
|
||||
|
||||
@builtin
|
||||
remove_range :: proc{
|
||||
remove_range_dynamic_array,
|
||||
remove_range_fixed_capacity_dynamic_array,
|
||||
}
|
||||
|
||||
|
||||
|
||||
// `pop_dynamic_array` will remove and return the end value of dynamic array `array` and reduces the length of `array` by 1.
|
||||
//
|
||||
// Note: If the dynamic array has no elements (`len(array) == 0`), this procedure will panic.
|
||||
@builtin
|
||||
pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
|
||||
pop_dynamic_array :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
|
||||
assert(len(array) > 0, loc=loc)
|
||||
_pop_type_erased(&res, (^Raw_Dynamic_Array)(array), size_of(E))
|
||||
_pop_dynamic_array_type_erased(&res, (^Raw_Dynamic_Array)(array), size_of(E))
|
||||
return res
|
||||
}
|
||||
|
||||
_pop_type_erased :: proc(res: rawptr, array: ^Raw_Dynamic_Array, elem_size: int, loc := #caller_location) {
|
||||
_pop_dynamic_array_type_erased :: proc(res: rawptr, array: ^Raw_Dynamic_Array, elem_size: int) {
|
||||
end := rawptr(uintptr(array.data) + uintptr(elem_size*(array.len-1)))
|
||||
intrinsics.mem_copy_non_overlapping(res, end, elem_size)
|
||||
array.len -= 1
|
||||
}
|
||||
|
||||
|
||||
// `pop_fixed_capacity_dynamic_array` will remove and return the end value of fixed capacity dynamic array `array` and reduces the length of `array` by 1.
|
||||
//
|
||||
// Note: If the fixed capacity dynamic array has no elements (`len(array) == 0`), this procedure will panic.
|
||||
@builtin
|
||||
pop_fixed_capacity_dynamic_array :: proc(array: ^$T/[dynamic; $N]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
|
||||
assert(len(array) > 0, loc=loc)
|
||||
|
||||
// `pop_safe` trys to remove and return the end value of dynamic array `array` and reduces the length of `array` by 1.
|
||||
elem_size :: size_of(E)
|
||||
end := rawptr(uintptr(array) + uintptr(elem_size*(len(array)-1)))
|
||||
intrinsics.mem_copy_non_overlapping(&res, end, elem_size)
|
||||
(^Raw_Fixed_Capacity_Dynamic_Array(N, E))(array).len -= 1
|
||||
return res
|
||||
}
|
||||
|
||||
|
||||
// `pop` will remove and return the end value of dynamic array `array` and reduces the length of `array` by 1.
|
||||
//
|
||||
// Note: If the dynamic array has no elements (`len(array) == 0`), this procedure will panic.
|
||||
@builtin
|
||||
pop :: proc{
|
||||
pop_dynamic_array,
|
||||
pop_fixed_capacity_dynamic_array,
|
||||
}
|
||||
|
||||
// `pop_safe_dynamic_array` trys to remove and return the end value of dynamic array `array` and reduces the length of `array` by 1.
|
||||
// If the operation is not possible, it will return false.
|
||||
@builtin
|
||||
pop_safe :: proc "contextless" (array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
|
||||
pop_safe_dynamic_array :: proc "contextless" (array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
|
||||
if len(array) == 0 {
|
||||
return
|
||||
}
|
||||
@@ -195,11 +283,32 @@ pop_safe :: proc "contextless" (array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #n
|
||||
return
|
||||
}
|
||||
|
||||
// `pop_front` will remove and return the first value of dynamic array `array` and reduces the length of `array` by 1.
|
||||
// `pop_safe_fixed_capacity_dynamic_array` trys to remove and return the end value of dynamic array `array` and reduces the length of `array` by 1.
|
||||
// If the operation is not possible, it will return false.
|
||||
@builtin
|
||||
pop_safe_fixed_capacity_dynamic_array :: proc "contextless" (array: ^$T/[dynamic; $N]$E) -> (res: E, ok: bool) #no_bounds_check {
|
||||
if len(array) == 0 {
|
||||
return
|
||||
}
|
||||
res, ok = array[len(array)-1], true
|
||||
(^Raw_Fixed_Capacity_Dynamic_Array(N, E))(array).len -= 1
|
||||
return
|
||||
}
|
||||
|
||||
// `pop_safe` trys to remove and return the end value of dynamic array `array` and reduces the length of `array` by 1.
|
||||
// If the operation is not possible, it will return false.
|
||||
@builtin
|
||||
pop_safe :: proc{
|
||||
pop_safe_dynamic_array,
|
||||
pop_safe_fixed_capacity_dynamic_array,
|
||||
}
|
||||
|
||||
|
||||
// `pop_front_dynamic_array` will remove and return the first value of dynamic array `array` and reduces the length of `array` by 1.
|
||||
//
|
||||
// Note: If the dynamic array as no elements (`len(array) == 0`), this procedure will panic.
|
||||
@builtin
|
||||
pop_front :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
|
||||
pop_front_dynamic_array :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
|
||||
assert(len(array) > 0, loc=loc)
|
||||
res = array[0]
|
||||
if len(array) > 1 {
|
||||
@@ -209,10 +318,35 @@ pop_front :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #
|
||||
return res
|
||||
}
|
||||
|
||||
// `pop_front_safe` trys to return and remove the first value of dynamic array `array` and reduces the length of `array` by 1.
|
||||
// `pop_front_fixed_capacity_dynamic_array` will remove and return the first value of fixed capacity dynamic array `array` and reduces the length of `array` by 1.
|
||||
//
|
||||
// Note: If the fixed capacity dynamic array as no elements (`len(array) == 0`), this procedure will panic.
|
||||
@builtin
|
||||
pop_front_fixed_capacity_dynamic_array :: proc(array: ^$T/[dynamic; $N]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
|
||||
assert(len(array) > 0, loc=loc)
|
||||
res = array[0]
|
||||
if len(array) > 1 {
|
||||
copy(array[0:], array[1:])
|
||||
}
|
||||
(^Raw_Fixed_Capacity_Dynamic_Array(N, E))(array).len -= 1
|
||||
return res
|
||||
}
|
||||
|
||||
|
||||
// `pop_front` will remove and return the first value of dynamic array `array` and reduces the length of `array` by 1.
|
||||
//
|
||||
// Note: If the dynamic array as no elements (`len(array) == 0`), this procedure will panic.
|
||||
@builtin
|
||||
pop_front :: proc{
|
||||
pop_front_dynamic_array,
|
||||
pop_front_fixed_capacity_dynamic_array,
|
||||
}
|
||||
|
||||
|
||||
// `pop_front_safe_dynamic_array` trys to return and remove the first value of dynamic array `array` and reduces the length of `array` by 1.
|
||||
// If the operation is not possible, it will return false.
|
||||
@builtin
|
||||
pop_front_safe :: proc "contextless" (array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
|
||||
pop_front_safe_dynamic_array :: proc "contextless" (array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
|
||||
if len(array) == 0 {
|
||||
return
|
||||
}
|
||||
@@ -224,12 +358,37 @@ pop_front_safe :: proc "contextless" (array: ^$T/[dynamic]$E) -> (res: E, ok: bo
|
||||
return
|
||||
}
|
||||
|
||||
// `pop_front_safe_fixed_capacity_dynamic_array` trys to return and remove the first value of dynamic array `array` and reduces the length of `array` by 1.
|
||||
// If the operation is not possible, it will return false.
|
||||
@builtin
|
||||
pop_front_safe_fixed_capacity_dynamic_array :: proc "contextless" (array: ^$T/[dynamic; $N]$E) -> (res: E, ok: bool) #no_bounds_check {
|
||||
if len(array) == 0 {
|
||||
return
|
||||
}
|
||||
res, ok = array[0], true
|
||||
if len(array) > 1 {
|
||||
copy(array[0:], array[1:])
|
||||
}
|
||||
(^Raw_Fixed_Capacity_Dynamic_Array(N, E))(array).len -= 1
|
||||
return
|
||||
}
|
||||
|
||||
// `pop_front_safe` trys to return and remove the first value of dynamic array `array` and reduces the length of `array` by 1.
|
||||
// If the operation is not possible, it will return false.
|
||||
@builtin
|
||||
pop_front_safe :: proc {
|
||||
pop_front_safe_dynamic_array,
|
||||
pop_front_safe_fixed_capacity_dynamic_array,
|
||||
}
|
||||
|
||||
|
||||
|
||||
// `clear` will set the length of a passed dynamic array or map to `0`
|
||||
@builtin
|
||||
clear :: proc{
|
||||
clear_dynamic_array,
|
||||
clear_map,
|
||||
clear_fixed_capacity_dynamic_array,
|
||||
|
||||
clear_soa_dynamic_array,
|
||||
}
|
||||
@@ -254,6 +413,7 @@ non_zero_reserve :: proc{
|
||||
@builtin
|
||||
resize :: proc{
|
||||
resize_dynamic_array,
|
||||
resize_fixed_capacity_dynamic_array,
|
||||
|
||||
resize_soa,
|
||||
}
|
||||
@@ -261,13 +421,17 @@ resize :: proc{
|
||||
@builtin
|
||||
non_zero_resize :: proc{
|
||||
non_zero_resize_dynamic_array,
|
||||
non_zero_resize_fixed_capacity_dynamic_array,
|
||||
|
||||
non_zero_resize_soa,
|
||||
}
|
||||
|
||||
// Shrinks the capacity of a dynamic array or map down to the current length, or the given capacity.
|
||||
@builtin
|
||||
shrink :: proc{shrink_dynamic_array, shrink_map}
|
||||
shrink :: proc{
|
||||
shrink_dynamic_array,
|
||||
shrink_map,
|
||||
}
|
||||
|
||||
// `free` will try to free the passed pointer, with the given `allocator` if the allocator supports this operation.
|
||||
@builtin
|
||||
@@ -357,7 +521,7 @@ new_aligned :: proc($T: typeid, alignment: int, allocator := context.allocator,
|
||||
|
||||
@(builtin, require_results)
|
||||
new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_location) -> (t: ^T, err: Allocator_Error) #optional_allocator_error {
|
||||
t = (^T)(raw_data(mem_alloc_bytes(size_of(T), align_of(T), allocator, loc) or_return))
|
||||
t = (^T)(raw_data(mem_alloc_non_zeroed(size_of(T), align_of(T), allocator, loc) or_return))
|
||||
if t != nil {
|
||||
t^ = data
|
||||
}
|
||||
@@ -430,7 +594,6 @@ _make_dynamic_array_len_cap :: proc(array: ^Raw_Dynamic_Array, size_of_elem, ali
|
||||
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
|
||||
}
|
||||
|
||||
@@ -667,6 +830,15 @@ non_zero_append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, l
|
||||
return _append_elem_string(array, arg, false, loc)
|
||||
}
|
||||
|
||||
// `non_zero_append_elem_fixed_capacity_string` appends a string to the end of a dynamic array of bytes, without zeroing any reserved memory
|
||||
//
|
||||
// Note: Prefer using the procedure group `non_zero_append`.
|
||||
@builtin
|
||||
non_zero_append_elem_fixed_capacity_string :: proc "contextless" (array: ^$T/[dynamic; $N]$E/u8, arg: $A/string) -> (n: int) {
|
||||
return append_fixed_capacity_elem(array, transmute([]byte)arg)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// The append_string built-in procedure appends multiple strings to the end of a [dynamic]u8 like type
|
||||
//
|
||||
@@ -684,6 +856,57 @@ append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// `append_fixed_capacity_elem` appends an element to the end of a fixed capacity dynamic array. Returns 0 on failure
|
||||
@builtin
|
||||
append_fixed_capacity_elem :: proc "contextless" (array: ^$T/[dynamic; $N]$E, #no_broadcast arg: E) -> (n: int) {
|
||||
Raw :: Raw_Fixed_Capacity_Dynamic_Array(N, E)
|
||||
|
||||
if (^Raw)(array).len >= N {
|
||||
return 0
|
||||
}
|
||||
|
||||
when size_of(E) != 0 {
|
||||
#no_bounds_check (^Raw)(array).data[(^Raw)(array).len] = arg
|
||||
}
|
||||
(^Raw)(array).len += 1
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
// `append_fixed_capacity_elem` appends an element to the end of a fixed capacity dynamic array. Returns 0 on failure
|
||||
@builtin
|
||||
append_fixed_capacity_elems :: proc "contextless" (array: ^$T/[dynamic; $N]$E, #no_broadcast args: ..E) -> (n: int) {
|
||||
Raw :: Raw_Fixed_Capacity_Dynamic_Array(N, E)
|
||||
raw := (^Raw)(array)
|
||||
|
||||
n = min(N - len(array), len(args))
|
||||
|
||||
#no_bounds_check when size_of(E) != 0 {
|
||||
intrinsics.mem_copy(&raw.data[raw.len], raw_data(args), n*size_of(E))
|
||||
}
|
||||
|
||||
raw.len += n
|
||||
return n
|
||||
}
|
||||
|
||||
// The append_fixed_capacity_string built-in procedure appends multiple strings to the end of a [dynamic]u8 like type
|
||||
//
|
||||
// Note: Prefer using the procedure group `append`.
|
||||
@builtin
|
||||
append_fixed_capacity_string :: proc "contextless" (array: ^$T/[dynamic; $N]$E/u8, args: ..string) -> (n: int) {
|
||||
n_arg: int
|
||||
for arg in args {
|
||||
n_arg = append_fixed_capacity_elems(array, ..transmute([]E)(arg))
|
||||
n += n_arg
|
||||
if n_arg < len(arg) {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// The append built-in procedure appends elements to the end of a dynamic array
|
||||
@builtin
|
||||
append :: proc{
|
||||
@@ -691,6 +914,10 @@ append :: proc{
|
||||
append_elems,
|
||||
append_elem_string,
|
||||
|
||||
append_fixed_capacity_elem,
|
||||
append_fixed_capacity_elems,
|
||||
append_fixed_capacity_string,
|
||||
|
||||
append_soa_elem,
|
||||
append_soa_elems,
|
||||
}
|
||||
@@ -701,6 +928,10 @@ non_zero_append :: proc{
|
||||
non_zero_append_elems,
|
||||
non_zero_append_elem_string,
|
||||
|
||||
append_fixed_capacity_elem,
|
||||
append_fixed_capacity_elems,
|
||||
non_zero_append_elem_fixed_capacity_string,
|
||||
|
||||
non_zero_append_soa_elem,
|
||||
non_zero_append_soa_elems,
|
||||
}
|
||||
@@ -709,7 +940,7 @@ non_zero_append :: proc{
|
||||
// `append_nothing` appends an empty value to a dynamic array. It returns `1, nil` if successful, and `0, err` when it was not possible,
|
||||
// whatever `err` happens to be.
|
||||
@builtin
|
||||
append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
append_nothing_dynamic_array :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
if array == nil {
|
||||
return 0, nil
|
||||
}
|
||||
@@ -718,6 +949,27 @@ append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (n: i
|
||||
return len(array)-prev_len, nil
|
||||
}
|
||||
|
||||
// `append_nothing` appends an empty value to a dynamic array. It returns `1, nil` if successful, and `0, err` when it was not possible,
|
||||
// whatever `err` happens to be.
|
||||
@builtin
|
||||
append_nothing_fixed_capacity_dynamic_array :: proc "contextless" (array: ^$T/[dynamic; $N]$E) -> (n: int, ok: bool) {
|
||||
if array == nil {
|
||||
return 0, true
|
||||
}
|
||||
prev_len := len(array)
|
||||
resize_fixed_capacity_dynamic_array(array, len(array)+1) or_return
|
||||
return len(array)-prev_len, true
|
||||
}
|
||||
|
||||
|
||||
// `append_nothing` appends an empty value to a dynamic array. It returns `1, nil` if successful, and `0, err` when it was not possible,
|
||||
// whatever `err` happens to be.
|
||||
@builtin
|
||||
append_nothing :: proc{
|
||||
append_nothing_dynamic_array,
|
||||
append_nothing_fixed_capacity_dynamic_array,
|
||||
}
|
||||
|
||||
|
||||
// `inject_at_elem` injects an element in a dynamic array at a specified index and moves the previous elements after that index "across"
|
||||
@builtin
|
||||
@@ -793,12 +1045,92 @@ inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, #any_int index: int, ar
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// `inject_at_elem_fixed_capacity_dynamic_array` injects an element in a dynamic array at a specified index and moves the previous elements after that index "across"
|
||||
@builtin
|
||||
inject_at_elem_fixed_capacity_dynamic_array :: proc(array: ^$T/[dynamic; $N]$E, #any_int index: int, #no_broadcast arg: E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
|
||||
when !ODIN_NO_BOUNDS_CHECK {
|
||||
ensure(index >= 0, "Index must be positive.", loc)
|
||||
}
|
||||
if array == nil {
|
||||
return false
|
||||
}
|
||||
n := max(len(array), index)
|
||||
m :: 1
|
||||
new_size := n + m
|
||||
|
||||
resize(array, new_size) or_return
|
||||
when size_of(E) != 0 {
|
||||
copy(array[index + m:], array[index:])
|
||||
array[index] = arg
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// `inject_at_elems_fixed_capacity_dynamic_array` injects multiple elements in a dynamic array at a specified index and moves the previous elements after that index "across"
|
||||
@builtin
|
||||
inject_at_elems_fixed_capacity_dynamic_array :: proc(array: ^$T/[dynamic; $N]$E, #any_int index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
|
||||
when !ODIN_NO_BOUNDS_CHECK {
|
||||
ensure(index >= 0, "Index must be positive.", loc)
|
||||
}
|
||||
if array == nil {
|
||||
return false
|
||||
}
|
||||
if len(args) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
n := max(len(array), index)
|
||||
m := len(args)
|
||||
new_size := n + m
|
||||
|
||||
resize(array, new_size) or_return
|
||||
when size_of(E) != 0 {
|
||||
copy(array[index + m:], array[index:])
|
||||
copy(array[index:], args)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// `inject_at_elem_string_fixed_capacity_dynamic_array` injects a string into a dynamic array at a specified index and moves the previous elements after that index "across"
|
||||
@builtin
|
||||
inject_at_elem_string_fixed_capacity_dynamic_array :: proc(array: ^$T/[dynamic; $N]$E/u8, #any_int index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check {
|
||||
when !ODIN_NO_BOUNDS_CHECK {
|
||||
ensure(index >= 0, "Index must be positive.", loc)
|
||||
}
|
||||
if array == nil {
|
||||
return false
|
||||
}
|
||||
if len(arg) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
n := max(len(array), index)
|
||||
m := len(arg)
|
||||
new_size := n + m
|
||||
|
||||
resize(array, new_size) or_return
|
||||
copy(array[index+m:], array[index:])
|
||||
copy(array[index:], arg)
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
// `inject_at` injects something into a dynamic array at a specified index and moves the previous elements after that index "across"
|
||||
@builtin inject_at :: proc{inject_at_elem, inject_at_elems, inject_at_elem_string}
|
||||
@builtin
|
||||
inject_at :: proc{
|
||||
inject_at_elem,
|
||||
inject_at_elems,
|
||||
inject_at_elem_string,
|
||||
|
||||
inject_at_elem_fixed_capacity_dynamic_array,
|
||||
inject_at_elems_fixed_capacity_dynamic_array,
|
||||
inject_at_elem_string_fixed_capacity_dynamic_array,
|
||||
}
|
||||
|
||||
|
||||
|
||||
// `assign_at_elem` assigns a value at a given index. If the requested index is smaller than the current
|
||||
// `assign_at_elem` assigns a value at a given index. If the requested index is past the end of the current
|
||||
// size of the dynamic array, it will attempt to `resize` the a new length of `index+1` and then assign as `index`.
|
||||
@builtin
|
||||
assign_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||
@@ -814,7 +1146,7 @@ assign_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, arg: E, loc
|
||||
}
|
||||
|
||||
|
||||
// `assign_at_elems` assigns a values at a given index. If the requested index is smaller than the current
|
||||
// `assign_at_elems` assigns a values at a given index. If the requested index is past the end of the current
|
||||
// size of the dynamic array, it will attempt to `resize` the a new length of `index+len(args)` and then assign as `index`.
|
||||
@builtin
|
||||
assign_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||
@@ -832,7 +1164,7 @@ assign_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadca
|
||||
return
|
||||
}
|
||||
|
||||
// `assign_at_elem_string` assigns a string at a given index. If the requested index is smaller than the current
|
||||
// `assign_at_elem_string` assigns a string at a given index. If the requested index is past the end of the current
|
||||
// size of the dynamic array, it will attempt to `resize` the a new length of `index+len(arg)` and then assign as `index`.
|
||||
@builtin
|
||||
assign_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, #any_int index: int, arg: string, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||
@@ -850,15 +1182,72 @@ assign_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, #any_int index: int, ar
|
||||
return
|
||||
}
|
||||
|
||||
// `assign_at` assigns a value at a given index. If the requested index is smaller than the current
|
||||
|
||||
// `assign_at_elem_fixed_capacity_dynamic_array` assigns a value at a given index. If the requested index is past the end of the current
|
||||
// size of the dynamic array, it will attempt to `resize` the a new length of `index+1` and then assign as `index`.
|
||||
@builtin
|
||||
assign_at_elem_fixed_capacity_dynamic_array :: proc "contextless" (array: ^$T/[dynamic; $N]$E, #any_int index: int, arg: E) -> (ok: bool) #no_bounds_check {
|
||||
if index < len(array) {
|
||||
array[index] = arg
|
||||
ok = true
|
||||
} else {
|
||||
resize(array, index+1, loc) or_return
|
||||
array[index] = arg
|
||||
ok = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// `assign_at_elems_fixed_capacity_dynamic_array` assigns a values at a given index. If the requested index is past the end of the current
|
||||
// size of the dynamic array, it will attempt to `resize` the a new length of `index+len(args)` and then assign as `index`.
|
||||
@builtin
|
||||
assign_at_elems_fixed_capacity_dynamic_array :: proc "contextless" (array: ^$T/[dynamic; $N]$E, #any_int index: int, #no_broadcast args: ..E) -> (ok: bool) #no_bounds_check {
|
||||
new_size := index + len(args)
|
||||
if len(args) == 0 {
|
||||
ok = true
|
||||
} else if new_size < len(array) {
|
||||
copy(array[index:], args)
|
||||
ok = true
|
||||
} else {
|
||||
resize(array, new_size, loc) or_return
|
||||
copy(array[index:], args)
|
||||
ok = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// `assign_at_elem_string_fixed_capacity_dynamic_array` assigns a string at a given index. If the requested index is past the end of the current
|
||||
// size of the dynamic array, it will attempt to `resize` the a new length of `index+len(arg)` and then assign as `index`.
|
||||
@builtin
|
||||
assign_at_elem_string_fixed_capacity_dynamic_array :: proc "contextless" (array: ^$T/[dynamic; $N]$E/u8, #any_int index: int, arg: string) -> (ok: bool) #no_bounds_check {
|
||||
new_size := index + len(arg)
|
||||
if len(arg) == 0 {
|
||||
ok = true
|
||||
} else if new_size < len(array) {
|
||||
copy(array[index:], arg)
|
||||
ok = true
|
||||
} else {
|
||||
resize(array, new_size, loc) or_return
|
||||
copy(array[index:], arg)
|
||||
ok = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// `assign_at` assigns a value at a given index. If the requested index is past the end of the current
|
||||
// size of the dynamic array, it will attempt to `resize` the a new length of `index+size_needed` and then assign as `index`.
|
||||
@builtin
|
||||
assign_at :: proc{
|
||||
assign_at_elem,
|
||||
assign_at_elems,
|
||||
assign_at_elem_string,
|
||||
}
|
||||
|
||||
assign_at_elem_fixed_capacity_dynamic_array,
|
||||
assign_at_elems_fixed_capacity_dynamic_array,
|
||||
assign_at_elem_string_fixed_capacity_dynamic_array,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -872,6 +1261,16 @@ clear_dynamic_array :: proc "contextless" (array: ^$T/[dynamic]$E) {
|
||||
}
|
||||
}
|
||||
|
||||
// `clear_fixed_capacity_dynamic_array` will set the length of a passed dynamic array to `0`
|
||||
//
|
||||
// Note: Prefer the procedure group `clear`.
|
||||
@builtin
|
||||
clear_fixed_capacity_dynamic_array :: proc "contextless" (array: ^$T/[dynamic; $N]$E) {
|
||||
if array != nil {
|
||||
(^Raw_Fixed_Capacity_Dynamic_Array(N, E))(array).len = 0
|
||||
}
|
||||
}
|
||||
|
||||
// `reserve_dynamic_array` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`).
|
||||
//
|
||||
// When a memory resize allocation is required, the memory will be asked to be zeroed (i.e. it calls `mem_resize`).
|
||||
@@ -888,8 +1287,8 @@ _reserve_dynamic_array :: #force_no_inline proc(a: ^Raw_Dynamic_Array, size_of_e
|
||||
|
||||
if a.allocator.procedure == nil {
|
||||
a.allocator = context.allocator
|
||||
assert(a.allocator.procedure != nil)
|
||||
}
|
||||
assert(a.allocator.procedure != nil)
|
||||
|
||||
old_size := a.cap * size_of_elem
|
||||
new_size := capacity * size_of_elem
|
||||
@@ -948,8 +1347,8 @@ _resize_dynamic_array :: #force_no_inline proc(a: ^Raw_Dynamic_Array, size_of_el
|
||||
|
||||
if a.allocator.procedure == nil {
|
||||
a.allocator = context.allocator
|
||||
assert(a.allocator.procedure != nil)
|
||||
}
|
||||
assert(a.allocator.procedure != nil)
|
||||
|
||||
old_size := a.cap * size_of_elem
|
||||
new_size := length * size_of_elem
|
||||
@@ -991,6 +1390,43 @@ non_zero_resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: i
|
||||
return _resize_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), length, false, loc=loc)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// `resize_fixed_capacity_dynamic_array` will try to resize memory of a passed fixed capacity dynamic array or map to the requested element count (setting the `len`, and possibly `cap`).
|
||||
//
|
||||
// Note: Prefer the procedure group `resize`
|
||||
@builtin
|
||||
resize_fixed_capacity_dynamic_array :: proc "contextless" (array: ^$T/[dynamic; $N]$E, #any_int length: int) -> bool {
|
||||
if array == nil {
|
||||
return false
|
||||
}
|
||||
if len(array) < length {
|
||||
size_of_elem :: size_of(E)
|
||||
|
||||
num_reused := min(N, length) - len(array)
|
||||
intrinsics.mem_zero(([^]byte)(array)[len(array)*size_of_elem:], num_reused*size_of_elem)
|
||||
}
|
||||
|
||||
raw := (^Raw_Fixed_Capacity_Dynamic_Array(N, E))(array)
|
||||
new_length := clamp(length, 0, N)
|
||||
raw.len = new_length
|
||||
return true
|
||||
}
|
||||
|
||||
// `non_zero_resize_fixed_capacity_dynamic_array` will try to resize memory of a passed fixed capacity dynamic array or map to the requested element count (setting the `len`, and possibly `cap`).
|
||||
//
|
||||
// Note: Prefer the procedure group `resize`
|
||||
@builtin
|
||||
non_zero_resize_fixed_capacity_dynamic_array :: proc "contextless" (array: ^$T/[dynamic; $N]$E, #any_int length: int) -> bool {
|
||||
if array == nil {
|
||||
return false
|
||||
}
|
||||
raw := (^Raw_Fixed_Capacity_Dynamic_Array(N, E))(array)
|
||||
new_length := clamp(length, 0, N)
|
||||
raw.len = new_length
|
||||
return true
|
||||
}
|
||||
|
||||
// Shrinks the capacity of a dynamic array down to the current length, or the given capacity.
|
||||
//
|
||||
// If `new_cap` is negative, then `len(array)` is used.
|
||||
@@ -1000,6 +1436,7 @@ non_zero_resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: i
|
||||
// If `len(array) < new_cap`, then `len(array)` will be left unchanged.
|
||||
//
|
||||
// Note: Prefer the procedure group `shrink`
|
||||
@builtin
|
||||
shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int new_cap := -1, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) {
|
||||
return _shrink_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), new_cap, loc)
|
||||
}
|
||||
@@ -1017,8 +1454,8 @@ _shrink_dynamic_array :: proc(a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem
|
||||
|
||||
if a.allocator.procedure == nil {
|
||||
a.allocator = context.allocator
|
||||
assert(a.allocator.procedure != nil)
|
||||
}
|
||||
assert(a.allocator.procedure != nil)
|
||||
|
||||
old_size := a.cap * size_of_elem
|
||||
new_size := new_cap * size_of_elem
|
||||
@@ -1088,7 +1525,7 @@ card :: proc "contextless" (s: $S/bit_set[$E; $U]) -> int {
|
||||
|
||||
|
||||
|
||||
// Evaluates the condition and panics the program iff the condition is false.
|
||||
// Evaluates the condition and panics the program if and only if (⟺) the condition is false.
|
||||
// This uses the `context.assertion_failure_procedure` to assert.
|
||||
//
|
||||
// This routine will be ignored when `ODIN_DISABLE_ASSERT` is true.
|
||||
@@ -1112,7 +1549,7 @@ assert :: proc(condition: bool, message := #caller_expression(condition), loc :=
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluates the condition and panics the program iff the condition is false.
|
||||
// Evaluates the condition and panics the program if and only if (⟺) the condition is false.
|
||||
// This uses the `context.assertion_failure_procedure` to assert.
|
||||
// This routine ignores `ODIN_DISABLE_ASSERT`, and will always execute.
|
||||
@builtin
|
||||
@@ -1152,7 +1589,7 @@ unimplemented :: proc(message := "", loc := #caller_location) -> ! {
|
||||
p("not yet implemented", message, loc)
|
||||
}
|
||||
|
||||
// Evaluates the condition and panics the program iff the condition is false.
|
||||
// Evaluates the condition and panics the program if and only if (⟺) the condition is false.
|
||||
// This uses the `default_assertion_contextless_failure_proc` to assert.
|
||||
//
|
||||
// This routine will be ignored when `ODIN_DISABLE_ASSERT` is true.
|
||||
@@ -1172,7 +1609,7 @@ assert_contextless :: proc "contextless" (condition: bool, message := #caller_ex
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluates the condition and panics the program iff the condition is false.
|
||||
// Evaluates the condition and panics the program if and only if (⟺) the condition is false.
|
||||
// This uses the `default_assertion_contextless_failure_proc` to assert.
|
||||
@builtin
|
||||
ensure_contextless :: proc "contextless" (condition: bool, message := #caller_expression(condition), loc := #caller_location) {
|
||||
|
||||
@@ -105,8 +105,8 @@ make_soa_aligned :: proc($T: typeid/#soa[]$E, #any_int length, alignment: int, a
|
||||
allocator := allocator
|
||||
if allocator.procedure == nil {
|
||||
allocator = context.allocator
|
||||
assert(allocator.procedure != nil)
|
||||
}
|
||||
assert(allocator.procedure != nil)
|
||||
|
||||
new_bytes: []byte
|
||||
new_bytes, err = allocator.procedure(
|
||||
@@ -240,8 +240,8 @@ _reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, zero_memory: boo
|
||||
|
||||
if array.allocator.procedure == nil {
|
||||
array.allocator = context.allocator
|
||||
assert(array.allocator.procedure != nil)
|
||||
}
|
||||
assert(array.allocator.procedure != nil)
|
||||
|
||||
footer := raw_soa_footer(array)
|
||||
if size_of(E) == 0 {
|
||||
@@ -615,7 +615,7 @@ inject_at_elems_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int index: int, #no
|
||||
// `inject_at_soa` injects something into a dynamic SOA array at a specified index and moves the previous elements after that index "across"
|
||||
@builtin inject_at_soa :: proc{inject_at_elem_soa, inject_at_elems_soa}
|
||||
|
||||
|
||||
@builtin
|
||||
delete_soa_slice :: proc(array: $T/#soa[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
field_count :: len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)
|
||||
when field_count != 0 {
|
||||
@@ -626,6 +626,7 @@ delete_soa_slice :: proc(array: $T/#soa[]$E, allocator := context.allocator, loc
|
||||
return nil
|
||||
}
|
||||
|
||||
@builtin
|
||||
delete_soa_dynamic_array :: proc(array: $T/#soa[dynamic]$E, loc := #caller_location) -> Allocator_Error {
|
||||
field_count :: len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)
|
||||
when field_count != 0 {
|
||||
@@ -644,7 +645,7 @@ delete_soa :: proc{
|
||||
delete_soa_dynamic_array,
|
||||
}
|
||||
|
||||
|
||||
@builtin
|
||||
clear_soa_dynamic_array :: proc(array: ^$T/#soa[dynamic]$E) {
|
||||
field_count :: len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)
|
||||
when field_count != 0 {
|
||||
|
||||
@@ -235,6 +235,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
if start < old_end && old_end == block.used && new_end <= block.capacity {
|
||||
// grow data in-place, adjusting next allocation
|
||||
block.used = uint(new_end)
|
||||
arena.total_used = uint(new_end)
|
||||
data = block.base[start:new_end]
|
||||
// sanitizer.address_unpoison(data)
|
||||
return
|
||||
|
||||
@@ -18,8 +18,8 @@ __dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap:
|
||||
// assuming that appending/reserving will set the allocator, if it is not already set.
|
||||
if array.allocator.procedure == nil {
|
||||
array.allocator = context.allocator
|
||||
assert(array.allocator.procedure != nil)
|
||||
}
|
||||
assert(array.allocator.procedure != nil)
|
||||
|
||||
if cap <= array.cap {
|
||||
return true
|
||||
@@ -52,8 +52,8 @@ __dynamic_array_shrink :: proc(array_: rawptr, elem_size, elem_align: int, new_c
|
||||
// assuming that appending/reserving will set the allocator, if it is not already set.
|
||||
if array.allocator.procedure == nil {
|
||||
array.allocator = context.allocator
|
||||
assert(array.allocator.procedure != nil)
|
||||
}
|
||||
assert(array.allocator.procedure != nil)
|
||||
|
||||
if new_cap > array.cap {
|
||||
return
|
||||
|
||||
@@ -283,7 +283,9 @@ map_desired_position :: #force_inline proc "contextless" (m: Raw_Map, hash: Map_
|
||||
map_probe_distance :: #force_inline proc "contextless" (m: Raw_Map, hash: Map_Hash, slot: uintptr) -> uintptr {
|
||||
// We do not use map_cap since we know the capacity will not be zero here.
|
||||
capacity := uintptr(1) << map_log2_cap(m)
|
||||
return (slot + capacity - map_desired_position(m, hash)) & (capacity - 1)
|
||||
|
||||
// return (slot + capacity - map_desired_position(m, hash)) & (capacity - 1)
|
||||
return (slot - uintptr(hash)) & (capacity - 1) // NOTE(bill): this is equivalent to the above, but less operations
|
||||
}
|
||||
|
||||
// When working with the type-erased structure at runtime we need information
|
||||
|
||||
@@ -12,7 +12,7 @@ bounds_trap :: proc "contextless" () -> ! {
|
||||
}
|
||||
|
||||
@(no_instrumentation)
|
||||
type_assertion_trap :: proc "contextless" () -> ! {
|
||||
type_assertion_trap_contextless :: proc "contextless" () -> ! {
|
||||
when ODIN_OS == .Windows {
|
||||
windows_trap_type_assertion()
|
||||
} else when ODIN_OS == .Orca {
|
||||
@@ -137,20 +137,22 @@ matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32
|
||||
|
||||
|
||||
when ODIN_NO_RTTI {
|
||||
type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: i32) {
|
||||
type_assertion_check_with_context :: proc "odin" (ok: bool, file: string, line, column: i32) {
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
@(cold, no_instrumentation)
|
||||
handle_error :: proc "contextless" (file: string, line, column: i32) -> ! {
|
||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||
print_string(" Invalid type assertion\n")
|
||||
type_assertion_trap()
|
||||
handle_error :: proc "odin" (file: string, line, column: i32) -> ! {
|
||||
p := context.assertion_failure_proc
|
||||
if p == nil {
|
||||
p = default_assertion_failure_proc
|
||||
}
|
||||
p("type assertion", "Invalid type assertion", Source_Code_Location{file, line, column, ""})
|
||||
}
|
||||
handle_error(file, line, column)
|
||||
}
|
||||
|
||||
type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: i32) {
|
||||
type_assertion_check_contextless :: proc "contextless" (ok: bool, file: string, line, column: i32) {
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
@@ -158,12 +160,71 @@ when ODIN_NO_RTTI {
|
||||
handle_error :: proc "contextless" (file: string, line, column: i32) -> ! {
|
||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||
print_string(" Invalid type assertion\n")
|
||||
type_assertion_trap()
|
||||
type_assertion_trap_contextless()
|
||||
}
|
||||
handle_error(file, line, column)
|
||||
}
|
||||
|
||||
type_assertion_check2_with_context :: proc "odin" (ok: bool, file: string, line, column: i32) {
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
@(cold, no_instrumentation)
|
||||
handle_error :: proc "odin" (file: string, line, column: i32) -> ! {
|
||||
p := context.assertion_failure_proc
|
||||
if p == nil {
|
||||
p = default_assertion_failure_proc
|
||||
}
|
||||
p("type assertion", "Invalid type assertion", Source_Code_Location{file, line, column, ""})
|
||||
}
|
||||
|
||||
handle_error(file, line, column)
|
||||
}
|
||||
|
||||
type_assertion_check2_contextless :: proc "contextless" (ok: bool, file: string, line, column: i32) {
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
@(cold, no_instrumentation)
|
||||
handle_error :: proc "contextless" (file: string, line, column: i32) -> ! {
|
||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||
print_string(" Invalid type assertion\n")
|
||||
type_assertion_trap_contextless()
|
||||
}
|
||||
handle_error(file, line, column)
|
||||
}
|
||||
} else {
|
||||
type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid) {
|
||||
@(private="file")
|
||||
TYPE_ASSERTION_BUFFER_SIZE :: 1024
|
||||
|
||||
type_assertion_check_with_context :: proc "odin" (ok: bool, file: string, line, column: i32, from, to: typeid) {
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
@(cold, no_instrumentation)
|
||||
handle_error :: proc "odin" (file: string, line, column: i32, from, to: typeid) -> ! {
|
||||
do_msg :: proc "contextless" (i: ^int, buf: []byte, file: string, line, column: i32, from, to: typeid) -> bool {
|
||||
write_string(i, buf, "Invalid type assertion from ") or_return
|
||||
write_typeid(i, buf, from) or_return
|
||||
write_string(i, buf, " to ") or_return
|
||||
write_typeid(i, buf, to) or_return
|
||||
return true
|
||||
}
|
||||
|
||||
buf: [TYPE_ASSERTION_BUFFER_SIZE]byte
|
||||
i := 0
|
||||
_ = do_msg(&i, buf[:], file, line, column, from, to)
|
||||
|
||||
p := context.assertion_failure_proc
|
||||
if p == nil {
|
||||
p = default_assertion_failure_proc
|
||||
}
|
||||
p("type assertion", string(buf[:i]), Source_Code_Location{file, line, column, ""})
|
||||
}
|
||||
handle_error(file, line, column, from, to)
|
||||
}
|
||||
|
||||
type_assertion_check_contextless :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid) {
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
@@ -175,47 +236,90 @@ when ODIN_NO_RTTI {
|
||||
print_string(" to ")
|
||||
print_typeid(to)
|
||||
print_byte('\n')
|
||||
type_assertion_trap()
|
||||
type_assertion_trap_contextless()
|
||||
}
|
||||
handle_error(file, line, column, from, to)
|
||||
}
|
||||
|
||||
type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid, from_data: rawptr) {
|
||||
@(private="file")
|
||||
type_assertion_variant_type :: proc "contextless" (id: typeid, data: rawptr) -> typeid {
|
||||
if id == nil || data == nil {
|
||||
return id
|
||||
}
|
||||
ti := type_info_base(type_info_of(id))
|
||||
#partial switch v in ti.variant {
|
||||
case Type_Info_Any:
|
||||
return (^any)(data).id
|
||||
case Type_Info_Union:
|
||||
if v.tag_type == nil {
|
||||
if (^rawptr)(data)^ == nil {
|
||||
return nil
|
||||
}
|
||||
return v.variants[0].id
|
||||
|
||||
}
|
||||
|
||||
tag_ptr := uintptr(data) + v.tag_offset
|
||||
idx := 0
|
||||
switch v.tag_type.size {
|
||||
case 1: idx = int( (^u8)(tag_ptr)^); if !v.no_nil { idx -= 1 }
|
||||
case 2: idx = int( (^u16)(tag_ptr)^); if !v.no_nil { idx -= 1 }
|
||||
case 4: idx = int( (^u32)(tag_ptr)^); if !v.no_nil { idx -= 1 }
|
||||
case 8: idx = int( (^u64)(tag_ptr)^); if !v.no_nil { idx -= 1 }
|
||||
case 16: idx = int((^u128)(tag_ptr)^); if !v.no_nil { idx -= 1 }
|
||||
}
|
||||
if idx < 0 {
|
||||
return nil
|
||||
} else if idx < len(v.variants) {
|
||||
return v.variants[idx].id
|
||||
}
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
type_assertion_check2_with_context :: proc "odin" (ok: bool, file: string, line, column: i32, from, to: typeid, from_data: rawptr) {
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
|
||||
variant_type :: proc "contextless" (id: typeid, data: rawptr) -> typeid {
|
||||
if id == nil || data == nil {
|
||||
return id
|
||||
}
|
||||
ti := type_info_base(type_info_of(id))
|
||||
#partial switch v in ti.variant {
|
||||
case Type_Info_Any:
|
||||
return (^any)(data).id
|
||||
case Type_Info_Union:
|
||||
tag_ptr := uintptr(data) + v.tag_offset
|
||||
idx := 0
|
||||
switch v.tag_type.size {
|
||||
case 1: idx = int((^u8)(tag_ptr)^) - 1
|
||||
case 2: idx = int((^u16)(tag_ptr)^) - 1
|
||||
case 4: idx = int((^u32)(tag_ptr)^) - 1
|
||||
case 8: idx = int((^u64)(tag_ptr)^) - 1
|
||||
case 16: idx = int((^u128)(tag_ptr)^) - 1
|
||||
}
|
||||
if idx < 0 {
|
||||
return nil
|
||||
} else if idx < len(v.variants) {
|
||||
return v.variants[idx].id
|
||||
@(cold, no_instrumentation)
|
||||
handle_error :: proc "odin" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) -> ! {
|
||||
do_msg :: proc "contextless" (i: ^int, buf: []byte, file: string, line, column: i32, from, to, actual: typeid) -> bool {
|
||||
write_string(i, buf, "Invalid type assertion from ") or_return
|
||||
write_typeid(i, buf, from) or_return
|
||||
write_string(i, buf, " to ") or_return
|
||||
write_typeid(i, buf, to) or_return
|
||||
if actual != from {
|
||||
write_string(i, buf, ", actual type: ") or_return
|
||||
write_typeid(i, buf, actual) or_return
|
||||
}
|
||||
return true
|
||||
}
|
||||
return id
|
||||
|
||||
actual := type_assertion_variant_type(from, from_data)
|
||||
|
||||
buf: [TYPE_ASSERTION_BUFFER_SIZE]byte
|
||||
i := 0
|
||||
_ = do_msg(&i, buf[:], file, line, column, from, to, actual)
|
||||
|
||||
p := context.assertion_failure_proc
|
||||
if p == nil {
|
||||
p = default_assertion_failure_proc
|
||||
}
|
||||
p("type assertion", string(buf[:i]), Source_Code_Location{file, line, column, ""})
|
||||
}
|
||||
handle_error(file, line, column, from, to, from_data)
|
||||
}
|
||||
|
||||
type_assertion_check2_contextless :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid, from_data: rawptr) {
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
|
||||
@(cold, no_instrumentation)
|
||||
handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) -> ! {
|
||||
|
||||
actual := variant_type(from, from_data)
|
||||
actual := type_assertion_variant_type(from, from_data)
|
||||
|
||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||
print_string(" Invalid type assertion from ")
|
||||
@@ -227,7 +331,7 @@ when ODIN_NO_RTTI {
|
||||
print_typeid(actual)
|
||||
}
|
||||
print_byte('\n')
|
||||
type_assertion_trap()
|
||||
type_assertion_trap_contextless()
|
||||
}
|
||||
handle_error(file, line, column, from, to, from_data)
|
||||
}
|
||||
|
||||
@@ -7,10 +7,11 @@ import "base:intrinsics"
|
||||
IS_WASM :: ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32
|
||||
|
||||
@(private)
|
||||
RUNTIME_LINKAGE :: "strong" when (
|
||||
ODIN_USE_SEPARATE_MODULES ||
|
||||
ODIN_BUILD_MODE == .Dynamic ||
|
||||
!ODIN_NO_CRT) else "internal"
|
||||
RUNTIME_LINKAGE :: "strong" when ODIN_USE_SEPARATE_MODULES else
|
||||
"internal" when ODIN_NO_ENTRY_POINT && (ODIN_BUILD_MODE == .Static || ODIN_BUILD_MODE == .Dynamic || ODIN_BUILD_MODE == .Object) else
|
||||
"strong" when ODIN_BUILD_MODE == .Dynamic else
|
||||
"strong" when !ODIN_NO_CRT else
|
||||
"internal"
|
||||
RUNTIME_REQUIRE :: false // !ODIN_TILDE
|
||||
|
||||
@(private)
|
||||
@@ -147,6 +148,7 @@ mem_alloc_non_zeroed :: #force_no_inline proc(size: int, alignment: int = DEFAUL
|
||||
return allocator.procedure(allocator.data, .Alloc_Non_Zeroed, size, alignment, nil, 0, loc)
|
||||
}
|
||||
|
||||
@builtin
|
||||
mem_free :: #force_no_inline proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if ptr == nil || allocator.procedure == nil {
|
||||
return nil
|
||||
@@ -171,7 +173,7 @@ mem_free_bytes :: #force_no_inline proc(bytes: []byte, allocator := context.allo
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@builtin
|
||||
mem_free_all :: #force_no_inline proc(allocator := context.allocator, loc := #caller_location) -> (err: Allocator_Error) {
|
||||
if allocator.procedure != nil {
|
||||
_, err = allocator.procedure(allocator.data, .Free_All, 0, 0, nil, 0, loc)
|
||||
@@ -340,7 +342,7 @@ memory_compare :: proc "contextless" (x, y: rawptr, n: int) -> int #no_bounds_ch
|
||||
case y == nil: return +1
|
||||
}
|
||||
a, b := cast([^]byte)x, cast([^]byte)y
|
||||
|
||||
|
||||
n := uint(n)
|
||||
i := uint(0)
|
||||
m := uint(0)
|
||||
@@ -716,7 +718,7 @@ quaternion256_eq :: #force_inline proc "contextless" (a, b: quaternion256) -> bo
|
||||
quaternion256_ne :: #force_inline proc "contextless" (a, b: quaternion256) -> bool { return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b) }
|
||||
|
||||
|
||||
string_decode_rune :: proc "contextless" (s: string) -> (rune, int) {
|
||||
string_decode_rune :: proc "contextless" (s: string) -> (rune, int) #no_bounds_check {
|
||||
// NOTE(bill): Duplicated here to remove dependency on package unicode/utf8
|
||||
|
||||
@(static, rodata) accept_sizes := [256]u8{
|
||||
@@ -795,7 +797,7 @@ string_decode_rune :: proc "contextless" (s: string) -> (rune, int) {
|
||||
return rune(s0&MASK4)<<18 | rune(b1&MASKX)<<12 | rune(b2&MASKX)<<6 | rune(b3&MASKX), 4
|
||||
}
|
||||
|
||||
string_decode_last_rune :: proc "contextless" (s: string) -> (rune, int) {
|
||||
string_decode_last_rune :: proc "contextless" (s: string) -> (rune, int) #no_bounds_check {
|
||||
RUNE_ERROR :: '\ufffd'
|
||||
RUNE_SELF :: 0x80
|
||||
UTF_MAX :: 4
|
||||
@@ -831,7 +833,7 @@ string_decode_last_rune :: proc "contextless" (s: string) -> (rune, int) {
|
||||
}
|
||||
|
||||
|
||||
string16_decode_rune :: proc "contextless" (s: string16) -> (rune, int) {
|
||||
string16_decode_rune :: proc "contextless" (s: string16) -> (rune, int) #no_bounds_check {
|
||||
REPLACEMENT_CHAR :: '\ufffd'
|
||||
_surr1 :: 0xd800
|
||||
_surr2 :: 0xdc00
|
||||
@@ -859,7 +861,7 @@ string16_decode_rune :: proc "contextless" (s: string16) -> (rune, int) {
|
||||
return r, w
|
||||
}
|
||||
|
||||
string16_decode_last_rune :: proc "contextless" (s: string16) -> (rune, int) {
|
||||
string16_decode_last_rune :: proc "contextless" (s: string16) -> (rune, int) #no_bounds_check {
|
||||
REPLACEMENT_CHAR :: '\ufffd'
|
||||
_surr1 :: 0xd800
|
||||
_surr2 :: 0xdc00
|
||||
@@ -1408,4 +1410,3 @@ when .Address in ODIN_SANITIZER_FLAGS {
|
||||
__asan_unpoison_memory_region :: proc "system" (address: rawptr, size: uint) ---
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
14
base/runtime/os_specific_essence.odin
Normal file
14
base/runtime/os_specific_essence.odin
Normal file
@@ -0,0 +1,14 @@
|
||||
#+build essence
|
||||
#+private
|
||||
package runtime
|
||||
|
||||
_HAS_RAND_BYTES :: false
|
||||
|
||||
// TODO(bill): reimplement `os.write`
|
||||
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
return 0, -1
|
||||
}
|
||||
|
||||
_exit :: proc "contextless" (code: int) -> ! {
|
||||
trap()
|
||||
}
|
||||
@@ -184,10 +184,11 @@ print_rune :: #force_no_inline proc "contextless" (r: rune) -> int #no_bounds_ch
|
||||
|
||||
|
||||
print_u64 :: #force_no_inline proc "contextless" (x: u64) #no_bounds_check {
|
||||
b :: u64(10)
|
||||
u := x
|
||||
|
||||
a: [129]byte
|
||||
i := len(a)
|
||||
b := u64(10)
|
||||
u := x
|
||||
for u >= b {
|
||||
i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
|
||||
u /= b
|
||||
@@ -199,11 +200,9 @@ print_u64 :: #force_no_inline proc "contextless" (x: u64) #no_bounds_check {
|
||||
|
||||
|
||||
print_i64 :: #force_no_inline proc "contextless" (x: i64) #no_bounds_check {
|
||||
b :: i64(10)
|
||||
|
||||
u := x
|
||||
neg := u < 0
|
||||
u = abs(u)
|
||||
b :: u64(10)
|
||||
u := u64(abs(x))
|
||||
neg := x < 0
|
||||
|
||||
a: [129]byte
|
||||
i := len(a)
|
||||
@@ -281,11 +280,22 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
|
||||
print_byte('i' if info.signed else 'u')
|
||||
print_u64(u64(8*ti.size))
|
||||
}
|
||||
switch info.endianness {
|
||||
case .Platform: // nothing
|
||||
case .Little: print_string("le")
|
||||
case .Big: print_string("be")
|
||||
}
|
||||
|
||||
case Type_Info_Rune:
|
||||
print_string("rune")
|
||||
case Type_Info_Float:
|
||||
print_byte('f')
|
||||
print_u64(u64(8*ti.size))
|
||||
switch info.endianness {
|
||||
case .Platform: // nothing
|
||||
case .Little: print_string("le")
|
||||
case .Big: print_string("be")
|
||||
}
|
||||
case Type_Info_Complex:
|
||||
print_string("complex")
|
||||
print_u64(u64(8*ti.size))
|
||||
@@ -382,6 +392,12 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
|
||||
print_string("[]")
|
||||
print_type(info.elem)
|
||||
|
||||
case Type_Info_Fixed_Capacity_Dynamic_Array:
|
||||
print_string("[dynamic; ")
|
||||
print_u64(u64(info.capacity))
|
||||
print_string("]")
|
||||
print_type(info.elem)
|
||||
|
||||
case Type_Info_Map:
|
||||
print_string("map[")
|
||||
print_type(info.key)
|
||||
@@ -411,6 +427,7 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
|
||||
if .packed in info.flags { print_string("#packed ") }
|
||||
if .raw_union in info.flags { print_string("#raw_union ") }
|
||||
if .all_or_none in info.flags { print_string("#all_or_none ") }
|
||||
if .simple in info.flags { print_string("#simple ") }
|
||||
if .align in info.flags {
|
||||
print_string("#align(")
|
||||
print_u64(u64(ti.align))
|
||||
@@ -467,7 +484,7 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
|
||||
print_string("..")
|
||||
print_i64(info.upper)
|
||||
}
|
||||
if info.underlying != nil {
|
||||
if info.explicit_underlying {
|
||||
print_string("; ")
|
||||
print_type(info.underlying)
|
||||
}
|
||||
@@ -495,6 +512,9 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
|
||||
print_type(info.elem)
|
||||
|
||||
case Type_Info_Matrix:
|
||||
if info.layout == .Row_Major {
|
||||
print_string("#row_major ")
|
||||
}
|
||||
print_string("matrix[")
|
||||
print_u64(u64(info.row_count))
|
||||
print_string(", ")
|
||||
@@ -503,3 +523,425 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
|
||||
print_type(info.elem)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
write_string :: proc "contextless" (i: ^int, dst: []byte, src: string) -> bool {
|
||||
if i^ < len(dst) {
|
||||
i^ += copy(dst[i^:], src)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
write_byte :: proc "contextless" (i: ^int, dst: []byte, src: byte) -> bool {
|
||||
if i^ < len(dst) {
|
||||
dst[i^] = src
|
||||
i^ += 1
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
write_u64 :: proc "contextless" (j: ^int, dst: []byte, x: u64) -> bool {
|
||||
if j^ < len(dst) {
|
||||
b :: u64(10)
|
||||
u := x
|
||||
|
||||
a: [129]byte
|
||||
i := len(a)
|
||||
for u >= b {
|
||||
i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
|
||||
u /= b
|
||||
}
|
||||
i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
|
||||
|
||||
return write_string(j, dst, string(a[i:]))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
write_i64 :: proc "contextless" (j: ^int, dst: []byte, x: i64) -> bool {
|
||||
if j^ < len(dst) {
|
||||
b :: u64(10)
|
||||
u := u64(abs(x))
|
||||
neg := x < 0
|
||||
|
||||
a: [129]byte
|
||||
i := len(a)
|
||||
for u >= b {
|
||||
i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
|
||||
u /= b
|
||||
}
|
||||
i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
|
||||
if neg {
|
||||
i -= 1; a[i] = '-'
|
||||
}
|
||||
|
||||
return write_string(j, dst, string(a[i:]))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
write_caller_location :: #force_no_inline proc "contextless" (i: ^int, buf: []byte, loc: Source_Code_Location) -> bool {
|
||||
write_string(i, buf, loc.file_path) or_return
|
||||
|
||||
when ODIN_ERROR_POS_STYLE == .Default {
|
||||
write_byte(i, buf, '(') or_return
|
||||
write_u64 (i, buf, u64(loc.line)) or_return
|
||||
if loc.column != 0 {
|
||||
write_byte(i, buf, ':') or_return
|
||||
write_u64 (i, buf, u64(loc.column)) or_return
|
||||
}
|
||||
write_byte(i, buf, ')') or_return
|
||||
return true
|
||||
} else when ODIN_ERROR_POS_STYLE == .Unix {
|
||||
write_byte(i, buf, ':') or_return
|
||||
write_u64 (i, buf, u64(loc.line)) or_return
|
||||
if loc.column != 0 {
|
||||
write_byte(i, buf, ':') or_return
|
||||
write_u64 (i, buf, u64(loc.column)) or_return
|
||||
}
|
||||
write_byte(i, buf, ':') or_return
|
||||
return true
|
||||
} else {
|
||||
#panic("unhandled ODIN_ERROR_POS_STYLE")
|
||||
}
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
write_typeid :: #force_no_inline proc "contextless" (i: ^int, buf: []byte, id: typeid) -> bool {
|
||||
when ODIN_NO_RTTI {
|
||||
if id == nil {
|
||||
write_string(i, buf, "nil") or_return
|
||||
} else {
|
||||
write_string(i, buf, "<unknown type>") or_return
|
||||
}
|
||||
} else {
|
||||
if id == nil {
|
||||
write_string(i, buf, "nil") or_return
|
||||
} else {
|
||||
ti := type_info_of(id)
|
||||
write_write_type(i, buf, ti) or_return
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
write_rune :: #force_no_inline proc "contextless" (i: ^int, buf: []byte, r: rune) -> (written: int, ok: bool) #no_bounds_check {
|
||||
RUNE_SELF :: 0x80
|
||||
|
||||
if r < RUNE_SELF {
|
||||
write_byte(i, buf,byte(r)) or_return
|
||||
return 1, true
|
||||
}
|
||||
|
||||
b, n := encode_rune(r)
|
||||
prev := i^
|
||||
write_string(i, buf, string(b[:n])) or_return
|
||||
return i^ - prev, true
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
write_encoded_rune :: #force_no_inline proc "contextless" (i: ^int, buf: []byte, r: rune) -> bool {
|
||||
write_byte(i, buf, '\'') or_return
|
||||
|
||||
switch r {
|
||||
case '\a': write_string(i, buf, "\\a") or_return
|
||||
case '\b': write_string(i, buf, "\\b") or_return
|
||||
case '\e': write_string(i, buf, "\\e") or_return
|
||||
case '\f': write_string(i, buf, "\\f") or_return
|
||||
case '\n': write_string(i, buf, "\\n") or_return
|
||||
case '\r': write_string(i, buf, "\\r") or_return
|
||||
case '\t': write_string(i, buf, "\\t") or_return
|
||||
case '\v': write_string(i, buf, "\\v") or_return
|
||||
case:
|
||||
if r <= 0 {
|
||||
write_string(i, buf, "\\x00") or_return
|
||||
} else if r < 32 {
|
||||
n0, n1 := u8(r) >> 4, u8(r) & 0xf
|
||||
write_string(i, buf, "\\x") or_return
|
||||
write_byte (i, buf, _INTEGER_DIGITS_VAR[n0]) or_return
|
||||
write_byte (i, buf, _INTEGER_DIGITS_VAR[n1]) or_return
|
||||
} else {
|
||||
_ = write_rune(i, buf, r) or_return
|
||||
}
|
||||
}
|
||||
|
||||
write_byte(i, buf, '\'') or_return
|
||||
return true
|
||||
}
|
||||
|
||||
@(optimization_mode="favor_size")
|
||||
write_write_type :: #force_no_inline proc "contextless" (i: ^int, buf: []byte, ti: ^Type_Info) -> bool {
|
||||
if ti == nil {
|
||||
write_string(i, buf, "nil") or_return
|
||||
return true
|
||||
}
|
||||
|
||||
switch info in ti.variant {
|
||||
case Type_Info_Named:
|
||||
write_string(i, buf, info.name) or_return
|
||||
case Type_Info_Integer:
|
||||
switch ti.id {
|
||||
case int: write_string(i, buf, "int") or_return
|
||||
case uint: write_string(i, buf, "uint") or_return
|
||||
case uintptr: write_string(i, buf, "uintptr") or_return
|
||||
case:
|
||||
write_byte(i, buf, 'i' if info.signed else 'u') or_return
|
||||
write_u64 (i, buf, u64(8*ti.size)) or_return
|
||||
}
|
||||
switch info.endianness {
|
||||
case .Platform: // nothing
|
||||
case .Little: write_string(i, buf, "le") or_return
|
||||
case .Big: write_string(i, buf, "be") or_return
|
||||
}
|
||||
|
||||
case Type_Info_Rune:
|
||||
write_string(i, buf, "rune") or_return
|
||||
case Type_Info_Float:
|
||||
write_byte(i, buf, 'f') or_return
|
||||
write_u64(i, buf, u64(8*ti.size)) or_return
|
||||
switch info.endianness {
|
||||
case .Platform: // nothing
|
||||
case .Little: write_string(i, buf, "le") or_return
|
||||
case .Big: write_string(i, buf, "be") or_return
|
||||
}
|
||||
|
||||
case Type_Info_Complex:
|
||||
write_string(i, buf, "complex") or_return
|
||||
write_u64 (i, buf, u64(8*ti.size)) or_return
|
||||
case Type_Info_Quaternion:
|
||||
write_string(i, buf, "quaternion") or_return
|
||||
write_u64 (i, buf, u64(8*ti.size)) or_return
|
||||
case Type_Info_String:
|
||||
if info.is_cstring {
|
||||
write_byte(i, buf, 'c') or_return
|
||||
}
|
||||
write_string(i, buf, "string") or_return
|
||||
switch info.encoding {
|
||||
case .UTF_8: /**/
|
||||
case .UTF_16: write_string(i, buf, "16") or_return
|
||||
}
|
||||
case Type_Info_Boolean:
|
||||
switch ti.id {
|
||||
case bool: write_string(i, buf, "bool") or_return
|
||||
case:
|
||||
write_byte(i, buf, 'b') or_return
|
||||
write_u64 (i, buf, u64(8*ti.size)) or_return
|
||||
}
|
||||
case Type_Info_Any:
|
||||
write_string(i, buf, "any") or_return
|
||||
case Type_Info_Type_Id:
|
||||
write_string(i, buf, "typeid") or_return
|
||||
|
||||
case Type_Info_Pointer:
|
||||
if info.elem == nil {
|
||||
write_string(i, buf, "rawptr") or_return
|
||||
} else {
|
||||
write_string (i, buf, "^") or_return
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
}
|
||||
case Type_Info_Multi_Pointer:
|
||||
write_string (i, buf, "[^]") or_return
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
case Type_Info_Soa_Pointer:
|
||||
write_string (i, buf, "#soa ^") or_return
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
case Type_Info_Procedure:
|
||||
write_string(i, buf, "proc") or_return
|
||||
if info.params == nil {
|
||||
write_string(i, buf, "()") or_return
|
||||
} else {
|
||||
t := info.params.variant.(Type_Info_Parameters)
|
||||
write_byte(i, buf, '(') or_return
|
||||
for t, j in t.types {
|
||||
if j > 0 { write_string(i, buf, ", ") or_return }
|
||||
write_write_type(i, buf, t) or_return
|
||||
}
|
||||
write_string(i, buf, ")") or_return
|
||||
}
|
||||
if info.results != nil {
|
||||
write_string (i, buf, " -> ") or_return
|
||||
write_write_type(i, buf, info.results) or_return
|
||||
}
|
||||
case Type_Info_Parameters:
|
||||
count := len(info.names)
|
||||
if count != 1 { write_byte(i, buf, '(') or_return }
|
||||
for name, j in info.names {
|
||||
if j > 0 { write_string(i, buf, ", ") or_return }
|
||||
|
||||
t := info.types[j]
|
||||
|
||||
if len(name) > 0 {
|
||||
write_string(i, buf, name) or_return
|
||||
write_string(i, buf, ": ") or_return
|
||||
}
|
||||
write_write_type(i, buf, t) or_return
|
||||
}
|
||||
if count != 1 { write_string(i, buf, ")") or_return }
|
||||
|
||||
case Type_Info_Array:
|
||||
write_byte (i, buf, '[') or_return
|
||||
write_u64 (i, buf, u64(info.count)) or_return
|
||||
write_byte (i, buf, ']') or_return
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
|
||||
case Type_Info_Enumerated_Array:
|
||||
if info.is_sparse {
|
||||
write_string(i, buf, "#sparse") or_return
|
||||
}
|
||||
write_byte (i, buf, '[') or_return
|
||||
write_write_type(i, buf, info.index) or_return
|
||||
write_byte (i, buf, ']') or_return
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
|
||||
|
||||
case Type_Info_Dynamic_Array:
|
||||
write_string (i, buf, "[dynamic]") or_return
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
case Type_Info_Slice:
|
||||
write_string (i, buf, "[]") or_return
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
|
||||
case Type_Info_Fixed_Capacity_Dynamic_Array:
|
||||
write_string (i, buf, "[dynamic; ") or_return
|
||||
write_u64 (i, buf, u64(info.capacity)) or_return
|
||||
write_string (i, buf, "]") or_return
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
|
||||
case Type_Info_Map:
|
||||
write_string (i, buf, "map[") or_return
|
||||
write_write_type(i, buf, info.key) or_return
|
||||
write_byte (i, buf, ']') or_return
|
||||
write_write_type(i, buf, info.value) or_return
|
||||
|
||||
case Type_Info_Struct:
|
||||
switch info.soa_kind {
|
||||
case .None: // Ignore
|
||||
case .Fixed:
|
||||
write_string (i, buf, "#soa[") or_return
|
||||
write_u64 (i, buf, u64(info.soa_len)) or_return
|
||||
write_byte (i, buf, ']') or_return
|
||||
write_write_type(i, buf, info.soa_base_type) or_return
|
||||
return true
|
||||
case .Slice:
|
||||
write_string (i, buf, "#soa[]") or_return
|
||||
write_write_type(i, buf, info.soa_base_type) or_return
|
||||
return true
|
||||
case .Dynamic:
|
||||
write_string (i, buf, "#soa[dynamic]") or_return
|
||||
write_write_type(i, buf, info.soa_base_type) or_return
|
||||
return true
|
||||
}
|
||||
|
||||
write_string(i, buf, "struct ") or_return
|
||||
if .packed in info.flags { write_string(i, buf, "#packed ") or_return }
|
||||
if .raw_union in info.flags { write_string(i, buf, "#raw_union ") or_return }
|
||||
if .all_or_none in info.flags { write_string(i, buf, "#all_or_none ") or_return }
|
||||
if .simple in info.flags { write_string(i, buf, "#simple ") or_return }
|
||||
if .align in info.flags {
|
||||
write_string(i, buf, "#align(") or_return
|
||||
write_u64(i, buf, u64(ti.align)) or_return
|
||||
write_string(i, buf, ") ") or_return
|
||||
}
|
||||
write_byte(i, buf, '{') or_return
|
||||
for name, j in info.names[:info.field_count] {
|
||||
if j > 0 { write_string(i, buf, ", ") or_return }
|
||||
write_string (i, buf, name) or_return
|
||||
write_string (i, buf, ": ") or_return
|
||||
write_write_type(i, buf, info.types[j]) or_return
|
||||
}
|
||||
write_byte(i, buf, '}') or_return
|
||||
|
||||
case Type_Info_Union:
|
||||
write_string(i, buf, "union ") or_return
|
||||
if info.custom_align {
|
||||
write_string(i, buf, "#align(") or_return
|
||||
write_u64 (i, buf, u64(ti.align)) or_return
|
||||
write_string(i, buf, ") ") or_return
|
||||
}
|
||||
if info.no_nil {
|
||||
write_string(i, buf, "#no_nil ") or_return
|
||||
}
|
||||
write_byte(i, buf, '{') or_return
|
||||
for variant, j in info.variants {
|
||||
if j > 0 { write_string(i, buf, ", ") or_return }
|
||||
write_write_type(i, buf, variant) or_return
|
||||
}
|
||||
write_string(i, buf, "}") or_return
|
||||
|
||||
case Type_Info_Enum:
|
||||
write_string (i, buf, "enum ") or_return
|
||||
write_write_type(i, buf, info.base) or_return
|
||||
write_string (i, buf, " {") or_return
|
||||
for name, j in info.names {
|
||||
if j > 0 { write_string(i, buf, ", ") or_return }
|
||||
write_string(i, buf, name) or_return
|
||||
}
|
||||
write_string(i, buf, "}") or_return
|
||||
|
||||
case Type_Info_Bit_Set:
|
||||
write_string(i, buf, "bit_set[") or_return
|
||||
|
||||
#partial switch elem in type_info_base(info.elem).variant {
|
||||
case Type_Info_Enum:
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
case Type_Info_Rune:
|
||||
write_encoded_rune(i, buf, rune(info.lower)) or_return
|
||||
write_string (i, buf, "..") or_return
|
||||
write_encoded_rune(i, buf, rune(info.upper)) or_return
|
||||
case:
|
||||
write_i64 (i, buf, info.lower) or_return
|
||||
write_string(i, buf, "..") or_return
|
||||
write_i64 (i, buf, info.upper) or_return
|
||||
}
|
||||
if info.explicit_underlying {
|
||||
write_string (i, buf, "; ") or_return
|
||||
write_write_type(i, buf, info.underlying) or_return
|
||||
}
|
||||
write_byte(i, buf, ']') or_return
|
||||
|
||||
case Type_Info_Bit_Field:
|
||||
write_string (i, buf, "bit_field ") or_return
|
||||
write_write_type(i, buf, info.backing_type) or_return
|
||||
write_string (i, buf, " {") or_return
|
||||
for name, j in info.names[:info.field_count] {
|
||||
if j > 0 { write_string(i, buf, ", ") or_return }
|
||||
write_string (i, buf, name) or_return
|
||||
write_string (i, buf, ": ") or_return
|
||||
write_write_type(i, buf, info.types[j]) or_return
|
||||
write_string (i, buf, " | ") or_return
|
||||
write_u64 (i, buf, u64(info.bit_sizes[j])) or_return
|
||||
}
|
||||
write_byte(i, buf, '}') or_return
|
||||
|
||||
|
||||
case Type_Info_Simd_Vector:
|
||||
write_string (i, buf, "#simd[") or_return
|
||||
write_u64 (i, buf, u64(info.count)) or_return
|
||||
write_byte (i, buf, ']') or_return
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
|
||||
case Type_Info_Matrix:
|
||||
if info.layout == .Row_Major {
|
||||
write_string(i, buf, "#row_major ") or_return
|
||||
}
|
||||
write_string (i, buf, "matrix[") or_return
|
||||
write_u64 (i, buf, u64(info.row_count)) or_return
|
||||
write_string (i, buf, ", ") or_return
|
||||
write_u64 (i, buf, u64(info.column_count)) or_return
|
||||
write_string (i, buf, "]") or_return
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -136,7 +136,7 @@ chacha8rand_refill_simd256 :: proc(r: ^Default_Random_State) {
|
||||
//
|
||||
// LLVM appears not to consider "this instruction is totally
|
||||
// awful on the given microarchitcture", which leads to
|
||||
// `VPCOMPRESSED` being generated iff AVX512 support is
|
||||
// `VPCOMPRESSED` being generated if and only if (⟺) AVX512 support is
|
||||
// enabled for `intrinsics.simd_masked_compress_store`.
|
||||
// On Zen 4, this leads to a 50% performance regression vs
|
||||
// the 128-bit SIMD code.
|
||||
|
||||
BIN
bin/radlink.exe
BIN
bin/radlink.exe
Binary file not shown.
@@ -94,6 +94,7 @@ if %release_mode% EQU 0 ( rem Debug
|
||||
set compiler_warnings= ^
|
||||
-W4 -WX ^
|
||||
-wd4100 -wd4101 -wd4127 -wd4146 ^
|
||||
-wd4324 ^
|
||||
-wd4505 ^
|
||||
-wd4456 -wd4457
|
||||
|
||||
|
||||
@@ -26,36 +26,42 @@ error() {
|
||||
exit 1
|
||||
}
|
||||
|
||||
SUPPORTED_LLVM_VERSIONS="22 21 20 19 18 17 14"
|
||||
|
||||
# Brew advises people not to add llvm to their $PATH, so try and use brew to find it.
|
||||
if [ -z "$LLVM_CONFIG" ] && [ -n "$(command -v brew)" ]; then
|
||||
if [ -n "$(command -v $(brew --prefix llvm@21)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@21)/bin/llvm-config"
|
||||
elif [ -n "$(command -v $(brew --prefix llvm@20)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@20)/bin/llvm-config"
|
||||
elif [ -n "$(command -v $(brew --prefix llvm@19)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@19)/bin/llvm-config"
|
||||
elif [ -n "$(command -v $(brew --prefix llvm@18)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@18)/bin/llvm-config"
|
||||
elif [ -n "$(command -v $(brew --prefix llvm@17)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@17)/bin/llvm-config"
|
||||
elif [ -n "$(command -v $(brew --prefix llvm@14)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@14)/bin/llvm-config"
|
||||
fi
|
||||
for V in $SUPPORTED_LLVM_VERSIONS; do
|
||||
if [ -n "$(command -v $(brew --prefix llvm@$V)/bin/llvm-config)" ]; then
|
||||
LLVM_CONFIG="$(brew --prefix llvm@$V)/bin/llvm-config"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [ -z "$LLVM_CONFIG" ]; then
|
||||
# darwin, linux, openbsd
|
||||
if [ -n "$(command -v llvm-config-21)" ]; then LLVM_CONFIG="llvm-config-21"
|
||||
elif [ -n "$(command -v llvm-config-20)" ]; then LLVM_CONFIG="llvm-config-20"
|
||||
elif [ -n "$(command -v llvm-config-19)" ]; then LLVM_CONFIG="llvm-config-19"
|
||||
elif [ -n "$(command -v llvm-config-18)" ]; then LLVM_CONFIG="llvm-config-18"
|
||||
elif [ -n "$(command -v llvm-config-17)" ]; then LLVM_CONFIG="llvm-config-17"
|
||||
elif [ -n "$(command -v llvm-config-14)" ]; then LLVM_CONFIG="llvm-config-14"
|
||||
# freebsd
|
||||
elif [ -n "$(command -v llvm-config21)" ]; then LLVM_CONFIG="llvm-config21"
|
||||
elif [ -n "$(command -v llvm-config20)" ]; then LLVM_CONFIG="llvm-config20"
|
||||
elif [ -n "$(command -v llvm-config19)" ]; then LLVM_CONFIG="llvm-config19"
|
||||
elif [ -n "$(command -v llvm-config18)" ]; then LLVM_CONFIG="llvm-config18"
|
||||
elif [ -n "$(command -v llvm-config17)" ]; then LLVM_CONFIG="llvm-config17"
|
||||
elif [ -n "$(command -v llvm-config14)" ]; then LLVM_CONFIG="llvm-config14"
|
||||
# fallback
|
||||
elif [ -n "$(command -v llvm-config)" ]; then LLVM_CONFIG="llvm-config"
|
||||
else
|
||||
error "No llvm-config command found. Set LLVM_CONFIG to proceed."
|
||||
DEFAULT_VERSION=""
|
||||
|
||||
if [ -n "$(command -v llvm-config)" ]; then
|
||||
DEFAULT_VERSION=$(llvm-config --version | awk -F. '{print $1}')
|
||||
fi
|
||||
|
||||
for V in $SUPPORTED_LLVM_VERSIONS; do
|
||||
if [ "$DEFAULT_VERSION" = "$V" ]; then
|
||||
LLVM_CONFIG="llvm-config"
|
||||
break
|
||||
# darwin, linux, openbsd
|
||||
elif [ -n "$(command -v "llvm-config-$V")" ]; then
|
||||
LLVM_CONFIG="llvm-config-$V"
|
||||
break
|
||||
# freebsd
|
||||
elif [ -n "$(command -v "llvm-config$V")" ]; then
|
||||
LLVM_CONFIG="llvm-config$V"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$LLVM_CONFIG" ]; then
|
||||
error "No supported llvm-config command found. Set LLVM_CONFIG to proceed."
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -72,8 +78,8 @@ LLVM_VERSION_MAJOR="$(echo $LLVM_VERSION | awk -F. '{print $1}')"
|
||||
LLVM_VERSION_MINOR="$(echo $LLVM_VERSION | awk -F. '{print $2}')"
|
||||
LLVM_VERSION_PATCH="$(echo $LLVM_VERSION | awk -F. '{print $3}')"
|
||||
|
||||
if [ $LLVM_VERSION_MAJOR -lt 14 ] || ([ $LLVM_VERSION_MAJOR -gt 14 ] && [ $LLVM_VERSION_MAJOR -lt 17 ]) || [ $LLVM_VERSION_MAJOR -gt 21 ]; then
|
||||
error "Invalid LLVM version $LLVM_VERSION: must be 14, 17, 18, 19, 20, or 21"
|
||||
if [ $LLVM_VERSION_MAJOR -lt 14 ] || ([ $LLVM_VERSION_MAJOR -gt 14 ] && [ $LLVM_VERSION_MAJOR -lt 17 ]) || [ $LLVM_VERSION_MAJOR -gt 22 ]; then
|
||||
error "Invalid LLVM version $LLVM_VERSION: must be 14, 17, 18, 19, 20, 21 or 22"
|
||||
fi
|
||||
|
||||
case "$OS_NAME" in
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package bufio
|
||||
|
||||
import "base:runtime"
|
||||
import "core:io"
|
||||
import "core:mem"
|
||||
import "core:unicode/utf8"
|
||||
import "core:bytes"
|
||||
|
||||
// Reader is a buffered wrapper for an io.Reader
|
||||
Reader :: struct {
|
||||
buf: []byte,
|
||||
buf_allocator: mem.Allocator,
|
||||
buf_allocator: runtime.Allocator,
|
||||
|
||||
rd: io.Reader, // reader
|
||||
r, w: int, // read and write positions for buf
|
||||
@@ -45,7 +45,7 @@ reader_init_with_buf :: proc(b: ^Reader, rd: io.Reader, buf: []byte) {
|
||||
b.buf = buf
|
||||
}
|
||||
|
||||
// reader_destroy destroys the underlying buffer with its associated allocator IFF that allocator has been set
|
||||
// reader_destroy destroys the underlying buffer with its associated allocator if and only if (⟺) that allocator has been set
|
||||
reader_destroy :: proc(b: ^Reader) {
|
||||
delete(b.buf, b.buf_allocator)
|
||||
b^ = {}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package bufio
|
||||
|
||||
import "core:bytes"
|
||||
import "core:io"
|
||||
import "core:mem"
|
||||
import "core:unicode/utf8"
|
||||
import "base:runtime"
|
||||
import "base:intrinsics"
|
||||
import "core:io"
|
||||
import "core:bytes"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
// Extra errors returns by scanning procedures
|
||||
Scanner_Extra_Error :: enum i32 {
|
||||
@@ -60,7 +60,15 @@ scanner_init_with_buffer :: proc(s: ^Scanner, r: io.Reader, buf: []byte) -> ^Sca
|
||||
s.r = r
|
||||
s.split = scan_lines
|
||||
s.max_token_size = DEFAULT_MAX_SCAN_TOKEN_SIZE
|
||||
s.buf = mem.buffer_from_slice(buf)
|
||||
s.buf = transmute([dynamic]byte)runtime.Raw_Dynamic_Array{
|
||||
data = raw_data(buf),
|
||||
len = 0,
|
||||
cap = len(buf),
|
||||
allocator = runtime.Allocator{
|
||||
procedure = runtime.nil_allocator_proc,
|
||||
data = nil,
|
||||
},
|
||||
}
|
||||
resize(&s.buf, cap(s.buf))
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package bufio
|
||||
|
||||
import "base:runtime"
|
||||
import "core:io"
|
||||
import "core:mem"
|
||||
import "core:unicode/utf8"
|
||||
// import "core:bytes"
|
||||
|
||||
// Writer is a buffered wrapper for an io.Writer
|
||||
Writer :: struct {
|
||||
buf: []byte,
|
||||
buf_allocator: mem.Allocator,
|
||||
buf_allocator: runtime.Allocator,
|
||||
|
||||
wr: io.Writer,
|
||||
n: int,
|
||||
@@ -35,7 +35,7 @@ writer_init_with_buf :: proc(b: ^Writer, wr: io.Writer, buf: []byte) {
|
||||
b.buf = buf
|
||||
}
|
||||
|
||||
// writer_destroy destroys the underlying buffer with its associated allocator IFF that allocator has been set
|
||||
// writer_destroy destroys the underlying buffer with its associated allocator if and only if (⟺) that allocator has been set
|
||||
writer_destroy :: proc(b: ^Writer) {
|
||||
delete(b.buf, b.buf_allocator)
|
||||
b^ = {}
|
||||
|
||||
@@ -176,6 +176,11 @@ buffer_write_ptr :: proc(b: ^Buffer, ptr: rawptr, size: int, loc := #caller_loca
|
||||
return buffer_write(b, ([^]byte)(ptr)[:size], loc=loc)
|
||||
}
|
||||
|
||||
buffer_write_slice :: proc(b: ^Buffer, slice: $S/[]$T, loc := #caller_location) -> (n: int, err: io.Error) {
|
||||
size := len(slice)*size_of(T)
|
||||
return buffer_write(b, ([^]byte)(raw_data(slice))[:size], loc=loc)
|
||||
}
|
||||
|
||||
buffer_write_string :: proc(b: ^Buffer, s: string, loc := #caller_location) -> (n: int, err: io.Error) {
|
||||
b.last_read = .Invalid
|
||||
m, ok := _buffer_try_grow(b, len(s), loc=loc)
|
||||
@@ -248,6 +253,12 @@ buffer_read_ptr :: proc(b: ^Buffer, ptr: rawptr, size: int) -> (n: int, err: io.
|
||||
return buffer_read(b, ([^]byte)(ptr)[:size])
|
||||
}
|
||||
|
||||
buffer_read_slice :: proc(b: ^Buffer, slice: $S/[]$T) -> (n: int, err: io.Error) {
|
||||
size := len(slice)*size_of(T)
|
||||
return buffer_read(b, ([^]byte)(raw_data(slice))[:size])
|
||||
}
|
||||
|
||||
|
||||
buffer_read_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) {
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Procedures for manipulation of `[]byte` slices.
|
||||
package bytes
|
||||
|
||||
import "base:runtime"
|
||||
import "base:intrinsics"
|
||||
import "core:mem"
|
||||
import "core:simd"
|
||||
import "core:unicode"
|
||||
import "core:unicode/utf8"
|
||||
@@ -35,20 +35,20 @@ SCANNER_SENTINEL_MIN_128: simd.u8x16 : u8(0xff)
|
||||
SIMD_REG_SIZE_128 :: 16
|
||||
|
||||
clone :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> []byte {
|
||||
c := make([]byte, len(s), allocator, loc)
|
||||
c := make([]byte, len(s), allocator, loc=loc)
|
||||
copy(c, s)
|
||||
return c[:len(s)]
|
||||
}
|
||||
|
||||
clone_safe :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: mem.Allocator_Error) {
|
||||
c := make([]byte, len(s), allocator, loc) or_return
|
||||
clone_safe :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: runtime.Allocator_Error) {
|
||||
c := make([]byte, len(s), allocator, loc=loc) or_return
|
||||
copy(c, s)
|
||||
return c[:len(s)], nil
|
||||
}
|
||||
|
||||
ptr_from_slice :: ptr_from_bytes
|
||||
ptr_from_bytes :: proc(str: []byte) -> ^byte {
|
||||
d := transmute(mem.Raw_String)str
|
||||
d := transmute(runtime.Raw_String)str
|
||||
return d.data
|
||||
}
|
||||
|
||||
@@ -67,10 +67,16 @@ truncate_to_rune :: proc(str: []byte, r: rune) -> []byte {
|
||||
return str[:n]
|
||||
}
|
||||
|
||||
// Compares two strings, returning a value representing which one comes first lexiographically.
|
||||
// -1 for `a`; 1 for `b`, or 0 if they are equal.
|
||||
// Compares two []byte, returning a value representing which one comes first lexiographically.
|
||||
// Returns: -1 for `lhs`, 1 for `rhs`, or 0 if they are equal.
|
||||
compare :: proc(lhs, rhs: []byte) -> int {
|
||||
return mem.compare(lhs, rhs)
|
||||
res := runtime.memory_compare(raw_data(lhs), raw_data(rhs), min(len(lhs), len(rhs)))
|
||||
if res == 0 && len(lhs) != len(rhs) {
|
||||
return len(lhs) <= len(rhs) ? -1 : +1
|
||||
} else if len(lhs) == 0 && len(rhs) == 0 {
|
||||
return 0
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
contains_rune :: proc(s: []byte, r: rune) -> int {
|
||||
@@ -157,7 +163,7 @@ has_suffix :: proc(s, suffix: []byte) -> bool {
|
||||
}
|
||||
|
||||
|
||||
join :: proc(a: [][]byte, sep: []byte, allocator := context.allocator) -> []byte {
|
||||
join :: proc(a: [][]byte, sep: []byte, allocator := context.allocator, loc := #caller_location) -> []byte {
|
||||
if len(a) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -167,7 +173,7 @@ join :: proc(a: [][]byte, sep: []byte, allocator := context.allocator) -> []byte
|
||||
n += len(s)
|
||||
}
|
||||
|
||||
b := make([]byte, n, allocator)
|
||||
b := make([]byte, n, allocator, loc=loc)
|
||||
i := copy(b, a[0])
|
||||
for s in a[1:] {
|
||||
i += copy(b[i:], sep)
|
||||
@@ -176,7 +182,7 @@ join :: proc(a: [][]byte, sep: []byte, allocator := context.allocator) -> []byte
|
||||
return b
|
||||
}
|
||||
|
||||
join_safe :: proc(a: [][]byte, sep: []byte, allocator := context.allocator) -> (data: []byte, err: mem.Allocator_Error) {
|
||||
join_safe :: proc(a: [][]byte, sep: []byte, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: runtime.Allocator_Error) {
|
||||
if len(a) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -186,7 +192,7 @@ join_safe :: proc(a: [][]byte, sep: []byte, allocator := context.allocator) -> (
|
||||
n += len(s)
|
||||
}
|
||||
|
||||
b := make([]byte, n, allocator) or_return
|
||||
b := make([]byte, n, allocator, loc=loc) or_return
|
||||
i := copy(b, a[0])
|
||||
for s in a[1:] {
|
||||
i += copy(b[i:], sep)
|
||||
@@ -195,7 +201,7 @@ join_safe :: proc(a: [][]byte, sep: []byte, allocator := context.allocator) -> (
|
||||
return b, nil
|
||||
}
|
||||
|
||||
concatenate :: proc(a: [][]byte, allocator := context.allocator) -> []byte {
|
||||
concatenate :: proc(a: [][]byte, allocator := context.allocator, loc := #caller_location) -> []byte {
|
||||
if len(a) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -204,7 +210,7 @@ concatenate :: proc(a: [][]byte, allocator := context.allocator) -> []byte {
|
||||
for s in a {
|
||||
n += len(s)
|
||||
}
|
||||
b := make([]byte, n, allocator)
|
||||
b := make([]byte, n, allocator, loc=loc)
|
||||
i := 0
|
||||
for s in a {
|
||||
i += copy(b[i:], s)
|
||||
@@ -212,7 +218,7 @@ concatenate :: proc(a: [][]byte, allocator := context.allocator) -> []byte {
|
||||
return b
|
||||
}
|
||||
|
||||
concatenate_safe :: proc(a: [][]byte, allocator := context.allocator) -> (data: []byte, err: mem.Allocator_Error) {
|
||||
concatenate_safe :: proc(a: [][]byte, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: runtime.Allocator_Error) {
|
||||
if len(a) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -221,7 +227,7 @@ concatenate_safe :: proc(a: [][]byte, allocator := context.allocator) -> (data:
|
||||
for s in a {
|
||||
n += len(s)
|
||||
}
|
||||
b := make([]byte, n, allocator) or_return
|
||||
b := make([]byte, n, allocator, loc=loc) or_return
|
||||
i := 0
|
||||
for s in a {
|
||||
i += copy(b[i:], s)
|
||||
@@ -231,7 +237,7 @@ concatenate_safe :: proc(a: [][]byte, allocator := context.allocator) -> (data:
|
||||
|
||||
|
||||
@private
|
||||
_split :: proc(s, sep: []byte, sep_save, n: int, allocator := context.allocator) -> [][]byte {
|
||||
_split :: proc(s, sep: []byte, sep_save, n: int, allocator := context.allocator, loc := #caller_location) -> [][]byte {
|
||||
s, n := s, n
|
||||
|
||||
if n == 0 {
|
||||
@@ -244,7 +250,7 @@ _split :: proc(s, sep: []byte, sep_save, n: int, allocator := context.allocator)
|
||||
n = l
|
||||
}
|
||||
|
||||
res := make([dynamic][]byte, n, allocator)
|
||||
res := make([dynamic][]byte, n, allocator, loc=loc)
|
||||
for i := 0; i < n-1; i += 1 {
|
||||
_, w := utf8.decode_rune(s)
|
||||
res[i] = s[:w]
|
||||
@@ -260,7 +266,7 @@ _split :: proc(s, sep: []byte, sep_save, n: int, allocator := context.allocator)
|
||||
n = count(s, sep) + 1
|
||||
}
|
||||
|
||||
res := make([dynamic][]byte, n, allocator)
|
||||
res := make([dynamic][]byte, n, allocator, loc=loc)
|
||||
|
||||
n -= 1
|
||||
|
||||
@@ -278,20 +284,20 @@ _split :: proc(s, sep: []byte, sep_save, n: int, allocator := context.allocator)
|
||||
return res[:i+1]
|
||||
}
|
||||
|
||||
split :: proc(s, sep: []byte, allocator := context.allocator) -> [][]byte {
|
||||
return _split(s, sep, 0, -1, allocator)
|
||||
split :: proc(s, sep: []byte, allocator := context.allocator, loc := #caller_location) -> [][]byte {
|
||||
return _split(s, sep, 0, -1, allocator, loc)
|
||||
}
|
||||
|
||||
split_n :: proc(s, sep: []byte, n: int, allocator := context.allocator) -> [][]byte {
|
||||
return _split(s, sep, 0, n, allocator)
|
||||
split_n :: proc(s, sep: []byte, n: int, allocator := context.allocator, loc := #caller_location) -> [][]byte {
|
||||
return _split(s, sep, 0, n, allocator, loc)
|
||||
}
|
||||
|
||||
split_after :: proc(s, sep: []byte, allocator := context.allocator) -> [][]byte {
|
||||
return _split(s, sep, len(sep), -1, allocator)
|
||||
split_after :: proc(s, sep: []byte, allocator := context.allocator, loc := #caller_location) -> [][]byte {
|
||||
return _split(s, sep, len(sep), -1, allocator, loc)
|
||||
}
|
||||
|
||||
split_after_n :: proc(s, sep: []byte, n: int, allocator := context.allocator) -> [][]byte {
|
||||
return _split(s, sep, len(sep), n, allocator)
|
||||
split_after_n :: proc(s, sep: []byte, n: int, allocator := context.allocator, loc := #caller_location) -> [][]byte {
|
||||
return _split(s, sep, len(sep), n, allocator, loc)
|
||||
}
|
||||
|
||||
|
||||
@@ -768,14 +774,14 @@ count :: proc(s, substr: []byte) -> int {
|
||||
}
|
||||
|
||||
|
||||
repeat :: proc(s: []byte, count: int, allocator := context.allocator) -> []byte {
|
||||
repeat :: proc(s: []byte, count: int, allocator := context.allocator, loc := #caller_location) -> []byte {
|
||||
if count < 0 {
|
||||
panic("bytes: negative repeat count")
|
||||
} else if count > 0 && (len(s)*count)/count != len(s) {
|
||||
panic("bytes: repeat count will cause an overflow")
|
||||
}
|
||||
|
||||
b := make([]byte, len(s)*count, allocator)
|
||||
b := make([]byte, len(s)*count, allocator, loc=loc)
|
||||
i := copy(b, s)
|
||||
for i < len(b) { // 2^N trick to reduce the need to copy
|
||||
copy(b[i:], b[:i])
|
||||
@@ -784,12 +790,12 @@ repeat :: proc(s: []byte, count: int, allocator := context.allocator) -> []byte
|
||||
return b
|
||||
}
|
||||
|
||||
replace_all :: proc(s, old, new: []byte, allocator := context.allocator) -> (output: []byte, was_allocation: bool) {
|
||||
return replace(s, old, new, -1, allocator)
|
||||
replace_all :: proc(s, old, new: []byte, allocator := context.allocator, loc := #caller_location) -> (output: []byte, was_allocation: bool) {
|
||||
return replace(s, old, new, -1, allocator, loc)
|
||||
}
|
||||
|
||||
// if n < 0, no limit on the number of replacements
|
||||
replace :: proc(s, old, new: []byte, n: int, allocator := context.allocator) -> (output: []byte, was_allocation: bool) {
|
||||
replace :: proc(s, old, new: []byte, n: int, allocator := context.allocator, loc := #caller_location) -> (output: []byte, was_allocation: bool) {
|
||||
if string(old) == string(new) || n == 0 {
|
||||
was_allocation = false
|
||||
output = s
|
||||
@@ -805,7 +811,7 @@ replace :: proc(s, old, new: []byte, n: int, allocator := context.allocator) ->
|
||||
}
|
||||
|
||||
|
||||
t := make([]byte, len(s) + byte_count*(len(new) - len(old)), allocator)
|
||||
t := make([]byte, len(s) + byte_count*(len(new) - len(old)), allocator, loc=loc)
|
||||
was_allocation = true
|
||||
|
||||
w := 0
|
||||
@@ -829,12 +835,12 @@ replace :: proc(s, old, new: []byte, n: int, allocator := context.allocator) ->
|
||||
return
|
||||
}
|
||||
|
||||
remove :: proc(s, key: []byte, n: int, allocator := context.allocator) -> (output: []byte, was_allocation: bool) {
|
||||
return replace(s, key, {}, n, allocator)
|
||||
remove :: proc(s, key: []byte, n: int, allocator := context.allocator, loc := #caller_location) -> (output: []byte, was_allocation: bool) {
|
||||
return replace(s, key, {}, n, allocator, loc)
|
||||
}
|
||||
|
||||
remove_all :: proc(s, key: []byte, allocator := context.allocator) -> (output: []byte, was_allocation: bool) {
|
||||
return remove(s, key, -1, allocator)
|
||||
remove_all :: proc(s, key: []byte, allocator := context.allocator, loc := #caller_location) -> (output: []byte, was_allocation: bool) {
|
||||
return remove(s, key, -1, allocator, loc)
|
||||
}
|
||||
|
||||
@(private) _ascii_space := [256]u8{'\t' = 1, '\n' = 1, '\v' = 1, '\f' = 1, '\r' = 1, ' ' = 1}
|
||||
@@ -991,16 +997,18 @@ trim_left :: proc(s: []byte, cutset: []byte) -> []byte {
|
||||
if s == nil || cutset == nil {
|
||||
return s
|
||||
}
|
||||
state := cutset
|
||||
return trim_left_proc_with_state(s, is_in_cutset, &state)
|
||||
begin := 0; end := len(s)
|
||||
for ; begin < end && index_byte(cutset, s[begin]) >= 0; begin += 1 {}
|
||||
return s[begin:]
|
||||
}
|
||||
|
||||
trim_right :: proc(s: []byte, cutset: []byte) -> []byte {
|
||||
if s == nil || cutset == nil {
|
||||
return s
|
||||
}
|
||||
state := cutset
|
||||
return trim_right_proc_with_state(s, is_in_cutset, &state)
|
||||
begin := 0; end := len(s)
|
||||
for ; end > begin && index_byte(cutset, s[end - 1]) >= 0; end -= 1 {}
|
||||
return s[:end]
|
||||
}
|
||||
|
||||
trim :: proc(s: []byte, cutset: []byte) -> []byte {
|
||||
@@ -1046,7 +1054,7 @@ trim_suffix :: proc(s, suffix: []byte) -> []byte {
|
||||
return s
|
||||
}
|
||||
|
||||
split_multi :: proc(s: []byte, substrs: [][]byte, skip_empty := false, allocator := context.allocator) -> [][]byte #no_bounds_check {
|
||||
split_multi :: proc(s: []byte, substrs: [][]byte, skip_empty := false, allocator := context.allocator, loc := #caller_location) -> [][]byte #no_bounds_check {
|
||||
if s == nil || len(substrs) <= 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -1094,7 +1102,7 @@ split_multi :: proc(s: []byte, substrs: [][]byte, skip_empty := false, allocator
|
||||
return nil
|
||||
}
|
||||
|
||||
buf := make([][]byte, n, allocator)
|
||||
buf := make([][]byte, n, allocator, loc=loc)
|
||||
|
||||
n, i, l = 0, 0, 0
|
||||
|
||||
@@ -1181,10 +1189,10 @@ split_multi_iterator :: proc(s: ^[]byte, substrs: [][]byte, skip_empty := false)
|
||||
|
||||
// Scrubs invalid utf-8 characters and replaces them with the replacement string
|
||||
// Adjacent invalid bytes are only replaced once
|
||||
scrub :: proc(s: []byte, replacement: []byte, allocator := context.allocator) -> []byte {
|
||||
scrub :: proc(s: []byte, replacement: []byte, allocator := context.allocator, loc := #caller_location) -> []byte {
|
||||
str := s
|
||||
b: Buffer
|
||||
buffer_init_allocator(&b, 0, len(s), allocator)
|
||||
buffer_init_allocator(&b, 0, len(s), allocator, loc)
|
||||
|
||||
has_error := false
|
||||
cursor := 0
|
||||
@@ -1196,11 +1204,11 @@ scrub :: proc(s: []byte, replacement: []byte, allocator := context.allocator) ->
|
||||
if r == utf8.RUNE_ERROR {
|
||||
if !has_error {
|
||||
has_error = true
|
||||
buffer_write(&b, origin[:cursor])
|
||||
buffer_write(&b, origin[:cursor], loc)
|
||||
}
|
||||
} else if has_error {
|
||||
has_error = false
|
||||
buffer_write(&b, replacement)
|
||||
buffer_write(&b, replacement, loc)
|
||||
|
||||
origin = origin[cursor:]
|
||||
cursor = 0
|
||||
@@ -1214,10 +1222,10 @@ scrub :: proc(s: []byte, replacement: []byte, allocator := context.allocator) ->
|
||||
}
|
||||
|
||||
|
||||
reverse :: proc(s: []byte, allocator := context.allocator) -> []byte {
|
||||
reverse :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> []byte {
|
||||
str := s
|
||||
n := len(str)
|
||||
buf := make([]byte, n)
|
||||
buf := make([]byte, n, loc=loc)
|
||||
i := n
|
||||
|
||||
for len(str) > 0 {
|
||||
@@ -1229,7 +1237,7 @@ reverse :: proc(s: []byte, allocator := context.allocator) -> []byte {
|
||||
return buf
|
||||
}
|
||||
|
||||
expand_tabs :: proc(s: []byte, tab_size: int, allocator := context.allocator) -> []byte {
|
||||
expand_tabs :: proc(s: []byte, tab_size: int, allocator := context.allocator, loc := #caller_location) -> []byte {
|
||||
if tab_size <= 0 {
|
||||
panic("tab size must be positive")
|
||||
}
|
||||
@@ -1240,7 +1248,7 @@ expand_tabs :: proc(s: []byte, tab_size: int, allocator := context.allocator) ->
|
||||
}
|
||||
|
||||
b: Buffer
|
||||
buffer_init_allocator(&b, 0, len(s), allocator)
|
||||
buffer_init_allocator(&b, 0, len(s), allocator, loc=loc)
|
||||
|
||||
str := s
|
||||
column: int
|
||||
@@ -1252,7 +1260,7 @@ expand_tabs :: proc(s: []byte, tab_size: int, allocator := context.allocator) ->
|
||||
expand := tab_size - column%tab_size
|
||||
|
||||
for i := 0; i < expand; i += 1 {
|
||||
buffer_write_byte(&b, ' ')
|
||||
buffer_write_byte(&b, ' ', loc)
|
||||
}
|
||||
|
||||
column += expand
|
||||
@@ -1263,7 +1271,7 @@ expand_tabs :: proc(s: []byte, tab_size: int, allocator := context.allocator) ->
|
||||
column += w
|
||||
}
|
||||
|
||||
buffer_write_rune(&b, r)
|
||||
buffer_write_rune(&b, r, loc)
|
||||
}
|
||||
|
||||
str = str[w:]
|
||||
@@ -1288,59 +1296,59 @@ partition :: proc(str, sep: []byte) -> (head, match, tail: []byte) {
|
||||
center_justify :: centre_justify // NOTE(bill): Because Americans exist
|
||||
|
||||
// centre_justify returns a byte slice with a pad byte slice at boths sides if the str's rune length is smaller than length
|
||||
centre_justify :: proc(str: []byte, length: int, pad: []byte, allocator := context.allocator) -> []byte {
|
||||
centre_justify :: proc(str: []byte, length: int, pad: []byte, allocator := context.allocator, loc := #caller_location) -> []byte {
|
||||
n := rune_count(str)
|
||||
if n >= length || pad == nil {
|
||||
return clone(str, allocator)
|
||||
return clone(str, allocator, loc)
|
||||
}
|
||||
|
||||
remains := length-1
|
||||
pad_len := rune_count(pad)
|
||||
|
||||
b: Buffer
|
||||
buffer_init_allocator(&b, 0, len(str) + (remains/pad_len + 1)*len(pad), allocator)
|
||||
buffer_init_allocator(&b, 0, len(str) + (remains/pad_len + 1)*len(pad), allocator, loc)
|
||||
|
||||
write_pad_string(&b, pad, pad_len, remains/2)
|
||||
buffer_write(&b, str)
|
||||
buffer_write(&b, str, loc)
|
||||
write_pad_string(&b, pad, pad_len, (remains+1)/2)
|
||||
|
||||
return buffer_to_bytes(&b)
|
||||
}
|
||||
|
||||
// left_justify returns a byte slice with a pad byte slice at left side if the str's rune length is smaller than length
|
||||
left_justify :: proc(str: []byte, length: int, pad: []byte, allocator := context.allocator) -> []byte {
|
||||
left_justify :: proc(str: []byte, length: int, pad: []byte, allocator := context.allocator, loc := #caller_location) -> []byte {
|
||||
n := rune_count(str)
|
||||
if n >= length || pad == nil {
|
||||
return clone(str, allocator)
|
||||
return clone(str, allocator, loc)
|
||||
}
|
||||
|
||||
remains := length-1
|
||||
pad_len := rune_count(pad)
|
||||
|
||||
b: Buffer
|
||||
buffer_init_allocator(&b, 0, len(str) + (remains/pad_len + 1)*len(pad), allocator)
|
||||
buffer_init_allocator(&b, 0, len(str) + (remains/pad_len + 1)*len(pad), allocator, loc)
|
||||
|
||||
buffer_write(&b, str)
|
||||
buffer_write(&b, str, loc)
|
||||
write_pad_string(&b, pad, pad_len, remains)
|
||||
|
||||
return buffer_to_bytes(&b)
|
||||
}
|
||||
|
||||
// right_justify returns a byte slice with a pad byte slice at right side if the str's rune length is smaller than length
|
||||
right_justify :: proc(str: []byte, length: int, pad: []byte, allocator := context.allocator) -> []byte {
|
||||
right_justify :: proc(str: []byte, length: int, pad: []byte, allocator := context.allocator, loc := #caller_location) -> []byte {
|
||||
n := rune_count(str)
|
||||
if n >= length || pad == nil {
|
||||
return clone(str, allocator)
|
||||
return clone(str, allocator, loc)
|
||||
}
|
||||
|
||||
remains := length-1
|
||||
pad_len := rune_count(pad)
|
||||
|
||||
b: Buffer
|
||||
buffer_init_allocator(&b, 0, len(str) + (remains/pad_len + 1)*len(pad), allocator)
|
||||
buffer_init_allocator(&b, 0, len(str) + (remains/pad_len + 1)*len(pad), allocator, loc)
|
||||
|
||||
write_pad_string(&b, pad, pad_len, remains)
|
||||
buffer_write(&b, str)
|
||||
buffer_write(&b, str, loc)
|
||||
|
||||
return buffer_to_bytes(&b)
|
||||
}
|
||||
@@ -1349,11 +1357,11 @@ right_justify :: proc(str: []byte, length: int, pad: []byte, allocator := contex
|
||||
|
||||
|
||||
@private
|
||||
write_pad_string :: proc(b: ^Buffer, pad: []byte, pad_len, remains: int) {
|
||||
write_pad_string :: proc(b: ^Buffer, pad: []byte, pad_len, remains: int, loc := #caller_location) {
|
||||
repeats := remains / pad_len
|
||||
|
||||
for i := 0; i < repeats; i += 1 {
|
||||
buffer_write(b, pad)
|
||||
buffer_write(b, pad, loc)
|
||||
}
|
||||
|
||||
n := remains % pad_len
|
||||
@@ -1361,7 +1369,7 @@ write_pad_string :: proc(b: ^Buffer, pad: []byte, pad_len, remains: int) {
|
||||
|
||||
for i := 0; i < n; i += 1 {
|
||||
r, width := utf8.decode_rune(p)
|
||||
buffer_write_rune(b, r)
|
||||
buffer_write_rune(b, r, loc)
|
||||
p = p[width:]
|
||||
}
|
||||
}
|
||||
@@ -1369,7 +1377,7 @@ write_pad_string :: proc(b: ^Buffer, pad: []byte, pad_len, remains: int) {
|
||||
|
||||
// fields splits the byte slice s around each instance of one or more consecutive white space character, defined by unicode.is_space
|
||||
// returning a slice of subslices of s or an empty slice if s only contains white space
|
||||
fields :: proc(s: []byte, allocator := context.allocator) -> [][]byte #no_bounds_check {
|
||||
fields :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> [][]byte #no_bounds_check {
|
||||
n := 0
|
||||
was_space := 1
|
||||
set_bits := u8(0)
|
||||
@@ -1384,14 +1392,14 @@ fields :: proc(s: []byte, allocator := context.allocator) -> [][]byte #no_bounds
|
||||
}
|
||||
|
||||
if set_bits >= utf8.RUNE_SELF {
|
||||
return fields_proc(s, unicode.is_space, allocator)
|
||||
return fields_proc(s, unicode.is_space, allocator, loc)
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
a := make([][]byte, n, allocator)
|
||||
a := make([][]byte, n, allocator, loc=loc)
|
||||
na := 0
|
||||
field_start := 0
|
||||
i := 0
|
||||
@@ -1425,15 +1433,15 @@ fields :: proc(s: []byte, allocator := context.allocator) -> [][]byte #no_bounds
|
||||
//
|
||||
// fields_proc makes no guarantee about the order in which it calls f(ch)
|
||||
// it assumes that `f` always returns the same value for a given ch
|
||||
fields_proc :: proc(s: []byte, f: proc(rune) -> bool, allocator := context.allocator) -> [][]byte #no_bounds_check {
|
||||
subslices := make([dynamic][]byte, 0, 32, allocator)
|
||||
fields_proc :: proc(s: []byte, f: proc(rune) -> bool, allocator := context.allocator, loc := #caller_location) -> [][]byte #no_bounds_check {
|
||||
subslices := make([dynamic][]byte, 0, 32, allocator, loc=loc)
|
||||
|
||||
start, end := -1, -1
|
||||
for r, offset in string(s) {
|
||||
end = offset
|
||||
if f(r) {
|
||||
if start >= 0 {
|
||||
append(&subslices, s[start : end])
|
||||
append(&subslices, s[start : end], loc=loc)
|
||||
// -1 could be used, but just speed it up through bitwise not
|
||||
// gotta love 2's complement
|
||||
start = ~start
|
||||
@@ -1446,13 +1454,13 @@ fields_proc :: proc(s: []byte, f: proc(rune) -> bool, allocator := context.alloc
|
||||
}
|
||||
|
||||
if start >= 0 {
|
||||
append(&subslices, s[start : len(s)])
|
||||
append(&subslices, s[start : len(s)], loc=loc)
|
||||
}
|
||||
|
||||
return subslices[:]
|
||||
}
|
||||
|
||||
// alias returns true iff a and b have a non-zero length, and any part of
|
||||
// alias returns true if and only if (⟺) 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)
|
||||
@@ -1466,7 +1474,7 @@ alias :: proc "contextless" (a, b: []byte) -> bool {
|
||||
return a_start <= b_end && b_start <= a_end
|
||||
}
|
||||
|
||||
// alias_inexactly returns true iff a and b have a non-zero length,
|
||||
// alias_inexactly returns true if and only if (⟺) 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).
|
||||
|
||||
@@ -61,6 +61,27 @@ reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Erro
|
||||
}
|
||||
return
|
||||
}
|
||||
reader_read_slice :: proc(r: ^Reader, slice: $T/[]$S) -> (n: int, err: io.Error) {
|
||||
b := ([^]byte)(raw_data(slice))[:len(slice)*size_of(S)]
|
||||
return reader_read(r, b)
|
||||
}
|
||||
|
||||
reader_read_slice_at :: proc(r: ^Reader, slice: $T/[]$S, off: i64) -> (n: int, err: io.Error) {
|
||||
b := ([^]byte)(raw_data(slice))[:len(slice)*size_of(S)]
|
||||
return reader_read_at(r, b, off)
|
||||
}
|
||||
|
||||
reader_read_ptr :: proc(r: ^Reader, data: rawptr, len: int) -> (n: int, err: io.Error) {
|
||||
b := ([^]byte)(data)[:len]
|
||||
return reader_read(r, b)
|
||||
}
|
||||
|
||||
reader_read_ptr_at :: proc(r: ^Reader, data: rawptr, len: int, off: i64) -> (n: int, err: io.Error) {
|
||||
b := ([^]byte)(data)[:len]
|
||||
return reader_read_at(r, b, off)
|
||||
}
|
||||
|
||||
|
||||
reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) {
|
||||
r.prev_rune = -1
|
||||
if r.i >= i64(len(r.s)) {
|
||||
|
||||
@@ -154,12 +154,12 @@ _nan_bit_pattern := ~u64(0)
|
||||
|
||||
// On amd64 Windows and Linux, float_t and double_t are respectively both
|
||||
// their usual types. On x86 it's not possible to define these types correctly
|
||||
// since they would be long double which Odin does have support for.
|
||||
// since they would be long double which Odin does NOT have support for.
|
||||
float_t :: float
|
||||
double_t :: double
|
||||
|
||||
NAN := transmute(double)(_nan_bit_pattern)
|
||||
INFINITY :: 1e5000
|
||||
INFINITY :: 0h7ff00000_00000000 // +Inf
|
||||
|
||||
HUGE_VALF :: INFINITY
|
||||
HUGE_VAL :: double(INFINITY)
|
||||
|
||||
@@ -368,8 +368,6 @@ refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width :=
|
||||
if len(z.input_data) != 0 {
|
||||
b = u64(z.input_data[0])
|
||||
z.input_data = z.input_data[1:]
|
||||
} else {
|
||||
b = 0
|
||||
}
|
||||
|
||||
z.code_buffer |= b << u8(z.num_bits)
|
||||
|
||||
@@ -5,6 +5,7 @@ Example:
|
||||
import "core:bytes"
|
||||
import "core:os"
|
||||
import "core:compress"
|
||||
import "core:compress/gzip"
|
||||
import "core:fmt"
|
||||
|
||||
// Small GZIP file with fextra, fname and fcomment present.
|
||||
@@ -22,7 +23,8 @@ Example:
|
||||
|
||||
main :: proc() {
|
||||
// Set up output buffer.
|
||||
buf := bytes.Buffer{}
|
||||
buf: bytes.Buffer
|
||||
defer bytes.buffer_destroy(&buf)
|
||||
|
||||
stdout :: proc(s: string) {
|
||||
os.write_string(os.stdout, s)
|
||||
@@ -31,15 +33,13 @@ Example:
|
||||
os.write_string(os.stderr, s)
|
||||
}
|
||||
|
||||
args := os.args
|
||||
|
||||
if len(args) < 2 {
|
||||
if len(os.args) < 2 {
|
||||
stderr("No input file specified.\n")
|
||||
err := load(data=TEST, buf=&buf, known_gzip_size=len(TEST))
|
||||
err := gzip.load(data=TEST, buf=&buf, known_gzip_size=len(TEST))
|
||||
if err == nil {
|
||||
stdout("Displaying test vector: ")
|
||||
stdout("Displaying test vector: \"")
|
||||
stdout(bytes.buffer_to_string(&buf))
|
||||
stdout("\n")
|
||||
stdout("\"\n")
|
||||
} else {
|
||||
fmt.printf("gzip.load returned %v\n", err)
|
||||
}
|
||||
@@ -47,35 +47,31 @@ Example:
|
||||
os.exit(0)
|
||||
}
|
||||
|
||||
// The rest are all files.
|
||||
args = args[1:]
|
||||
err: Error
|
||||
for file in os.args[1:] {
|
||||
err: gzip.Error
|
||||
|
||||
for file in args {
|
||||
if file == "-" {
|
||||
// Read from stdin
|
||||
s := os.stream_from_handle(os.stdin)
|
||||
ctx := &compress.Context_Stream_Input{
|
||||
input = s,
|
||||
input = os.stdin.stream,
|
||||
}
|
||||
err = load(ctx, &buf)
|
||||
err = gzip.load(ctx, &buf)
|
||||
} else {
|
||||
err = load(file, &buf)
|
||||
err = gzip.load(file, &buf)
|
||||
}
|
||||
if err != nil {
|
||||
if err != E_General.File_Not_Found {
|
||||
stderr("File not found: ")
|
||||
stderr(file)
|
||||
stderr("\n")
|
||||
os.exit(1)
|
||||
}
|
||||
switch err {
|
||||
case nil:
|
||||
stdout(bytes.buffer_to_string(&buf))
|
||||
case gzip.E_General.File_Not_Found:
|
||||
stderr("File not found: ")
|
||||
stderr(file)
|
||||
stderr("\n")
|
||||
os.exit(1)
|
||||
case:
|
||||
stderr("GZIP returned an error.\n")
|
||||
bytes.buffer_destroy(&buf)
|
||||
os.exit(2)
|
||||
}
|
||||
stdout(bytes.buffer_to_string(&buf))
|
||||
}
|
||||
bytes.buffer_destroy(&buf)
|
||||
}
|
||||
*/
|
||||
package compress_gzip
|
||||
|
||||
@@ -107,14 +107,10 @@ load :: proc{load_from_bytes, load_from_file, load_from_context}
|
||||
load_from_file :: proc(filename: string, buf: ^bytes.Buffer, expected_output_size := -1, allocator := context.allocator) -> (err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
data, ok := os.read_entire_file(filename)
|
||||
defer delete(data)
|
||||
file_data, file_err := os.read_entire_file(filename, allocator)
|
||||
defer delete(file_data)
|
||||
|
||||
err = E_General.File_Not_Found
|
||||
if ok {
|
||||
err = load_from_bytes(data, buf, len(data), expected_output_size)
|
||||
}
|
||||
return
|
||||
return load_from_bytes(file_data, buf, len(file_data), expected_output_size) if file_err == nil else E_General.File_Not_Found
|
||||
}
|
||||
|
||||
load_from_bytes :: proc(data: []byte, buf: ^bytes.Buffer, known_gzip_size := -1, expected_output_size := -1, allocator := context.allocator) -> (err: Error) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#+vet !using-param
|
||||
#+feature using-stmt
|
||||
package compress_zlib
|
||||
|
||||
/*
|
||||
@@ -326,7 +326,7 @@ decode_huffman :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bo
|
||||
return 0, .Code_Buffer_Malformed
|
||||
}
|
||||
compress.refill_lsb(z)
|
||||
if z.num_bits > 63 {
|
||||
if z.code_buffer == 0 {
|
||||
return 0, .Stream_Too_Short
|
||||
}
|
||||
}
|
||||
@@ -491,7 +491,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
|
||||
*/
|
||||
expected_output_size = max(max(expected_output_size, compress.COMPRESS_OUTPUT_ALLOCATE_MIN), 512)
|
||||
|
||||
// fmt.printf("\nZLIB: Expected Payload Size: %v\n\n", expected_output_size);
|
||||
// fmt.printfln("ZLIB: Expected Payload Size: %v", expected_output_size)
|
||||
|
||||
if expected_output_size > 0 && expected_output_size <= compress.COMPRESS_OUTPUT_ALLOCATE_MAX {
|
||||
/*
|
||||
@@ -522,11 +522,16 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
|
||||
final := u32(0)
|
||||
type := u32(0)
|
||||
|
||||
defer if int(z.bytes_written) != len(z.output.buf) {
|
||||
resize(&z.output.buf, int(z.bytes_written))
|
||||
}
|
||||
|
||||
for {
|
||||
final = compress.read_bits_lsb(z, 1)
|
||||
type = compress.read_bits_lsb(z, 2)
|
||||
|
||||
// fmt.printf("Final: %v | Type: %v\n", final, type)
|
||||
// fmt.printfln("len(z): %v", len(z.input_data))
|
||||
// fmt.printfln("Final: %v | Type: %v", final, type)
|
||||
|
||||
switch type {
|
||||
case 0:
|
||||
@@ -561,7 +566,6 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
|
||||
case 3:
|
||||
return .BType_3
|
||||
case:
|
||||
// fmt.printf("Err: %v | Final: %v | Type: %v\n", err, final, type)
|
||||
if type == 1 {
|
||||
// Use fixed code lengths.
|
||||
build_huffman(z_repeat, Z_FIXED_LENGTH[:]) or_return
|
||||
@@ -590,7 +594,6 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
|
||||
|
||||
for n < ntot {
|
||||
c = decode_huffman(z, codelength_ht) or_return
|
||||
|
||||
if c < 0 || c >= 19 {
|
||||
return .Huffman_Bad_Code_Lengths
|
||||
}
|
||||
@@ -635,15 +638,12 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
|
||||
}
|
||||
parse_huffman_block(z, z_repeat, z_offset) or_return
|
||||
}
|
||||
|
||||
if final == 1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if int(z.bytes_written) != len(z.output.buf) {
|
||||
resize(&z.output.buf, int(z.bytes_written)) or_return
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -100,20 +100,20 @@ len :: proc "contextless" (t: ^$T/Tree($Value)) -> int {
|
||||
return t._size
|
||||
}
|
||||
|
||||
// first returns the first node in the tree (in-order) or nil iff
|
||||
// first returns the first node in the tree (in-order) or nil if and only if (⟺)
|
||||
// the tree is empty.
|
||||
first :: proc "contextless" (t: ^$T/Tree($Value)) -> ^Node(Value) {
|
||||
return tree_first_or_last_in_order(t, Direction.Backward)
|
||||
}
|
||||
|
||||
// last returns the last element in the tree (in-order) or nil iff
|
||||
// last returns the last element in the tree (in-order) or nil if and only if (⟺)
|
||||
// the tree is empty.
|
||||
last :: proc "contextless" (t: ^$T/Tree($Value)) -> ^Node(Value) {
|
||||
return tree_first_or_last_in_order(t, Direction.Forward)
|
||||
}
|
||||
|
||||
// find finds the value in the tree, and returns the corresponding
|
||||
// node or nil iff the value is not present.
|
||||
// node or nil if and only if (⟺) the value is not present.
|
||||
find :: proc(t: ^$T/Tree($Value), value: Value) -> ^Node(Value) {
|
||||
cur := t._root
|
||||
descend_loop: for cur != nil {
|
||||
@@ -168,7 +168,7 @@ find_or_insert :: proc(
|
||||
return
|
||||
}
|
||||
|
||||
// remove removes a node or value from the tree, and returns true iff the
|
||||
// remove removes a node or value from the tree, and returns true if and only if (⟺) the
|
||||
// removal was successful. While the node's value will be left intact,
|
||||
// the node itself will be freed via the tree's node allocator.
|
||||
remove :: proc {
|
||||
@@ -176,7 +176,7 @@ remove :: proc {
|
||||
remove_node,
|
||||
}
|
||||
|
||||
// remove_value removes a value from the tree, and returns true iff the
|
||||
// remove_value removes a value from the tree, and returns true if and only if (⟺) the
|
||||
// removal was successful. While the node's value will be left intact,
|
||||
// the node itself will be freed via the tree's node allocator.
|
||||
remove_value :: proc(t: ^$T/Tree($Value), value: Value, call_on_remove: bool = true) -> bool {
|
||||
@@ -187,7 +187,7 @@ remove_value :: proc(t: ^$T/Tree($Value), value: Value, call_on_remove: bool = t
|
||||
return remove_node(t, n, call_on_remove)
|
||||
}
|
||||
|
||||
// remove_node removes a node from the tree, and returns true iff the
|
||||
// remove_node removes a node from the tree, and returns true if and only if (⟺) the
|
||||
// removal was successful. While the node's value will be left intact,
|
||||
// the node itself will be freed via the tree's node allocator.
|
||||
remove_node :: proc(t: ^$T/Tree($Value), node: ^Node(Value), call_on_remove: bool = true) -> bool {
|
||||
@@ -281,14 +281,14 @@ iterator_from_pos :: proc "contextless" (
|
||||
}
|
||||
|
||||
// iterator_get returns the node currently pointed to by the iterator,
|
||||
// or nil iff the node has been removed, the tree is empty, or the end
|
||||
// or nil if and only if (⟺) the node has been removed, the tree is empty, or the end
|
||||
// of the tree has been reached.
|
||||
iterator_get :: proc "contextless" (it: ^$I/Iterator($Value)) -> ^Node(Value) {
|
||||
return it._cur
|
||||
}
|
||||
|
||||
// iterator_remove removes the node currently pointed to by the iterator,
|
||||
// and returns true iff the removal was successful. Semantics are the
|
||||
// and returns true if and only if (⟺) the removal was successful. Semantics are the
|
||||
// same as the Tree remove.
|
||||
iterator_remove :: proc(it: ^$I/Iterator($Value), call_on_remove: bool = true) -> bool {
|
||||
if it._cur == nil {
|
||||
@@ -304,7 +304,7 @@ iterator_remove :: proc(it: ^$I/Iterator($Value), call_on_remove: bool = true) -
|
||||
}
|
||||
|
||||
// iterator_next advances the iterator and returns the (node, true) or
|
||||
// or (nil, false) iff the end of the tree has been reached.
|
||||
// or (nil, false) if and only if (⟺) the end of the tree has been reached.
|
||||
//
|
||||
// Note: The first call to iterator_next will return the first node instead
|
||||
// of advancing the iterator.
|
||||
|
||||
@@ -2,7 +2,6 @@ package container_dynamic_bit_array
|
||||
|
||||
import "base:builtin"
|
||||
import "base:intrinsics"
|
||||
import "core:mem"
|
||||
|
||||
/*
|
||||
Note that these constants are dependent on the backing being a u64.
|
||||
@@ -329,7 +328,7 @@ Inputs:
|
||||
*/
|
||||
clear :: proc(ba: ^Bit_Array) {
|
||||
if ba == nil { return }
|
||||
mem.zero_slice(ba.bits[:])
|
||||
intrinsics.mem_zero(raw_data(ba.bits), builtin.len(ba.bits) * NUM_BITS / 8)
|
||||
}
|
||||
/*
|
||||
Gets the length of set and unset valid bits in the Bit_Array.
|
||||
|
||||
60
core/container/handle_map/doc.odin
Normal file
60
core/container/handle_map/doc.odin
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
Handle-based map using either fixed-length arrays, or exponential arrays from "core:container/xar".
|
||||
|
||||
Example:
|
||||
import hm "core:container/handle_map"
|
||||
|
||||
Handle :: hm.Handle32
|
||||
|
||||
Entity :: struct {
|
||||
handle: Handle,
|
||||
pos: [2]f32,
|
||||
}
|
||||
|
||||
{ // static map
|
||||
entities: hm.Static_Handle_Map(1024, Entity, Handle)
|
||||
|
||||
h1 := hm.add(&entities, Entity{pos = {1, 4}})
|
||||
h2 := hm.add(&entities, Entity{pos = {9, 16}})
|
||||
|
||||
if e, ok := hm.get(&entities, h2); ok {
|
||||
e.pos.x += 32
|
||||
}
|
||||
|
||||
hm.remove(&entities, h1)
|
||||
|
||||
h3 := hm.add(&entities, Entity{pos = {6, 7}})
|
||||
assert(hm.is_valid(entities, h3))
|
||||
|
||||
it := hm.iterator_make(&entities)
|
||||
for e, h in hm.iterate(&it) {
|
||||
assert(hm.is_valid(entities, h))
|
||||
e.pos += {1, 2}
|
||||
}
|
||||
}
|
||||
|
||||
{ // dynamic map
|
||||
entities: hm.Dynamic_Handle_Map(Entity, Handle)
|
||||
hm.dynamic_init(&entities, context.allocator)
|
||||
defer hm.dynamic_destroy(&entities)
|
||||
|
||||
h1 := hm.add(&entities, Entity{pos = {1, 4}})
|
||||
h2 := hm.add(&entities, Entity{pos = {9, 16}})
|
||||
|
||||
if e, ok := hm.get(&entities, h2); ok {
|
||||
e.pos.x += 32
|
||||
}
|
||||
|
||||
hm.remove(&entities, h1)
|
||||
|
||||
h3 := hm.add(&entities, Entity{pos = {6, 7}})
|
||||
assert(hm.is_valid(entities, h3))
|
||||
|
||||
it := hm.iterator_make(&entities)
|
||||
for e, h in hm.iterate(&it) {
|
||||
assert(hm.is_valid(entities, h))
|
||||
e.pos += {1, 2}
|
||||
}
|
||||
}
|
||||
*/
|
||||
package container_handle_map
|
||||
142
core/container/handle_map/dynamic_handle_map.odin
Normal file
142
core/container/handle_map/dynamic_handle_map.odin
Normal file
@@ -0,0 +1,142 @@
|
||||
package container_handle_map
|
||||
|
||||
import "base:runtime"
|
||||
import "base:builtin"
|
||||
import "base:intrinsics"
|
||||
@(require) import "core:container/xar"
|
||||
|
||||
Dynamic_Handle_Map :: struct($T: typeid, $Handle_Type: typeid)
|
||||
where
|
||||
intrinsics.type_has_field(Handle_Type, "idx"),
|
||||
intrinsics.type_has_field(Handle_Type, "gen"),
|
||||
intrinsics.type_is_unsigned(intrinsics.type_field_type(Handle_Type, "idx")),
|
||||
intrinsics.type_is_unsigned(intrinsics.type_field_type(Handle_Type, "gen")),
|
||||
intrinsics.type_field_type(Handle_Type, "idx") == intrinsics.type_field_type(Handle_Type, "gen"),
|
||||
|
||||
intrinsics.type_has_field (T, "handle"),
|
||||
intrinsics.type_field_type(T, "handle") == Handle_Type {
|
||||
|
||||
items: xar.Array(T, 4),
|
||||
unused_items: xar.Array(u32, 4),
|
||||
}
|
||||
|
||||
dynamic_init :: proc(m: ^$D/Dynamic_Handle_Map($T, $Handle_Type), allocator: runtime.Allocator) {
|
||||
xar.init(&m.items, allocator)
|
||||
xar.init(&m.unused_items, allocator)
|
||||
}
|
||||
|
||||
dynamic_destroy :: proc(m: ^$D/Dynamic_Handle_Map($T, $Handle_Type)) {
|
||||
xar.destroy(&m.unused_items)
|
||||
xar.destroy(&m.items)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
dynamic_add :: proc(m: ^$D/Dynamic_Handle_Map($T, $Handle_Type), item: T, loc := #caller_location) -> (handle: Handle_Type, err: runtime.Allocator_Error) #optional_allocator_error {
|
||||
if xar.len(m.unused_items) > 0 {
|
||||
i := xar.pop(&m.unused_items)
|
||||
ptr := xar.get_ptr_unsafe(&m.items, i)
|
||||
prev_gen := ptr.handle.gen
|
||||
ptr^ = item
|
||||
|
||||
ptr.handle.idx = auto_cast i
|
||||
ptr.handle.gen = auto_cast (prev_gen + 1)
|
||||
return ptr.handle, nil
|
||||
}
|
||||
|
||||
if xar.len(m.items) == 0 {
|
||||
// initialize the zero-value sentinel
|
||||
xar.append(&m.items, T{}, loc) or_return
|
||||
}
|
||||
|
||||
_ = xar.append(&m.items, item, loc) or_return
|
||||
i := xar.len(m.items)-1
|
||||
|
||||
ptr := xar.get_ptr_unsafe(&m.items, i)
|
||||
|
||||
ptr.handle.idx = auto_cast i
|
||||
ptr.handle.gen = 1
|
||||
return ptr.handle, nil
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
dynamic_get :: proc "contextless" (m: ^$D/Dynamic_Handle_Map($T, $Handle_Type), h: Handle_Type) -> (^T, bool) #optional_ok {
|
||||
if h.idx <= 0 || int(u32(h.idx)) >= xar.len(m.items) {
|
||||
return nil, false
|
||||
}
|
||||
if e := xar.get_ptr_unsafe(&m.items, h.idx); e.handle == h {
|
||||
return e, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
dynamic_remove :: proc(m: ^$D/Dynamic_Handle_Map($T, $Handle_Type), h: Handle_Type, loc := #caller_location) -> (found: bool, err: runtime.Allocator_Error) {
|
||||
if h.idx <= 0 || int(u32(h.idx)) >= xar.len(m.items) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if item := xar.get_ptr(&m.items, h.idx); item.handle == h {
|
||||
xar.append(&m.unused_items, u32(h.idx), loc) or_return
|
||||
item.handle.idx = 0
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
dynamic_is_valid :: proc "contextless" (m: ^$D/Dynamic_Handle_Map($T, $Handle_Type), h: Handle_Type) -> bool {
|
||||
return h.idx > 0 && int(u32(h.idx)) < xar.len(m.items) && xar.get_ptr_unsafe(&m.items, h.idx).handle == h
|
||||
}
|
||||
|
||||
// Returns the number of possibly valid items in the handle map.
|
||||
@(require_results)
|
||||
dynamic_len :: proc "contextless" (m: $D/Dynamic_Handle_Map($T, $Handle_Type)) -> uint {
|
||||
n := xar.len(m.items) - xar.len(m.unused_items)
|
||||
return uint(n-1 if n > 0 else 0)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
dynamic_cap :: proc "contextless" (m: $D/Dynamic_Handle_Map($T, $Handle_Type)) -> uint {
|
||||
n := xar.cap(m.items)
|
||||
return uint(n-1 if n > 0 else 0)
|
||||
}
|
||||
|
||||
dynamic_clear :: proc "contextless" (m: ^$D/Dynamic_Handle_Map($T, $Handle_Type)) {
|
||||
xar.clear(&m.items)
|
||||
xar.clear(&m.unused_items)
|
||||
}
|
||||
|
||||
|
||||
// An iterator for a handle map.
|
||||
Dynamic_Handle_Map_Iterator :: struct($D: typeid) {
|
||||
m: ^D,
|
||||
index: int,
|
||||
}
|
||||
|
||||
// Makes an iterator from a handle map.
|
||||
@(require_results)
|
||||
dynamic_iterator_make :: proc "contextless" (m: ^$D/Dynamic_Handle_Map($T, $Handle_Type)) -> Dynamic_Handle_Map_Iterator(D) {
|
||||
return {m, 1}
|
||||
}
|
||||
|
||||
/*
|
||||
Iterate over a handle map. It will skip over unused item slots (e.g. handle.idx == 0).
|
||||
Usage:
|
||||
it := hm.dynamic_iterator_make(&the_dynamic_handle_map)
|
||||
for item, handle in hm.iterate(&it) {
|
||||
...
|
||||
}
|
||||
*/
|
||||
@(require_results)
|
||||
dynamic_iterate :: proc "contextless" (it: ^$DHI/Dynamic_Handle_Map_Iterator($D/Dynamic_Handle_Map($T, $Handle_Type))) -> (val: ^T, h: Handle_Type, ok: bool) {
|
||||
for _ in it.index..<xar.len(it.m.items) {
|
||||
e := xar.get_ptr_unsafe(&it.m.items, it.index)
|
||||
it.index += 1
|
||||
|
||||
if e.handle.idx != 0 {
|
||||
return e, e.handle, true
|
||||
}
|
||||
}
|
||||
it.index = 0
|
||||
return
|
||||
}
|
||||
222
core/container/handle_map/static_handle_map.odin
Normal file
222
core/container/handle_map/static_handle_map.odin
Normal file
@@ -0,0 +1,222 @@
|
||||
package container_handle_map
|
||||
|
||||
import "base:builtin"
|
||||
import "base:intrinsics"
|
||||
|
||||
// Default 16-bit Handle type which can be used for handle maps which only need a maximum of 254 (1<<8 - 2) items
|
||||
Handle16 :: struct {
|
||||
idx: u8,
|
||||
gen: u8,
|
||||
}
|
||||
|
||||
// Default 32-bit Handle type which can be used for handle maps which only need a maximum of 65534 (1<<16 - 2) items
|
||||
Handle32 :: struct {
|
||||
idx: u16,
|
||||
gen: u16,
|
||||
}
|
||||
|
||||
// Default 64-bit Handle type which can be used for handle maps which only need a maximum of 4294967294 (1<<32 - 2) items
|
||||
Handle64 :: struct {
|
||||
idx: u32,
|
||||
gen: u32,
|
||||
}
|
||||
|
||||
Static_Handle_Map :: struct($N: uint, $T: typeid, $Handle_Type: typeid)
|
||||
where
|
||||
0 < N, N < uint(1<<31 - 1),
|
||||
|
||||
intrinsics.type_has_field(Handle_Type, "idx"),
|
||||
intrinsics.type_has_field(Handle_Type, "gen"),
|
||||
intrinsics.type_is_unsigned(intrinsics.type_field_type(Handle_Type, "idx")),
|
||||
intrinsics.type_is_unsigned(intrinsics.type_field_type(Handle_Type, "gen")),
|
||||
intrinsics.type_field_type(Handle_Type, "idx") == intrinsics.type_field_type(Handle_Type, "gen"),
|
||||
|
||||
N < uint(max(intrinsics.type_field_type(Handle_Type, "idx"))),
|
||||
|
||||
intrinsics.type_has_field (T, "handle"),
|
||||
intrinsics.type_field_type(T, "handle") == Handle_Type {
|
||||
|
||||
// The zero element represent a zero-value sentinel (dummy value), allowing for `idx == 0` to mean a no-handle.
|
||||
// This means the capacity is actually N-1 items.
|
||||
items: [N]T,
|
||||
|
||||
used_len: u32, // How many of the items are in use
|
||||
unused_len: u32, // Use to calculate the number of valid items
|
||||
unused_items: [N]u32,
|
||||
next_unused: u32,
|
||||
}
|
||||
|
||||
|
||||
// `add` a value of type `T` to the handle map. This will return a pointer to the item and an optional boolean to check for validity.
|
||||
@(require_results)
|
||||
static_add :: proc "contextless" (m: ^$H/Static_Handle_Map($N, $T, $Handle_Type), item: T) -> (handle: Handle_Type, ok: bool) #optional_ok {
|
||||
if i := m.next_unused; i != 0 {
|
||||
ptr := &m.items[i]
|
||||
|
||||
m.next_unused = m.unused_items[i]
|
||||
m.unused_items[i] = 0
|
||||
|
||||
prev_gen := ptr.handle.gen
|
||||
ptr^ = item
|
||||
|
||||
ptr.handle.idx = auto_cast i
|
||||
ptr.handle.gen = auto_cast (prev_gen + 1)
|
||||
m.unused_len -= 1
|
||||
return ptr.handle, true
|
||||
}
|
||||
|
||||
if m.used_len == 0 {
|
||||
// initialize the zero-value sentinel
|
||||
m.items[0] = {}
|
||||
m.used_len += 1
|
||||
}
|
||||
|
||||
if m.used_len == builtin.len(m.items) {
|
||||
return {}, false
|
||||
}
|
||||
|
||||
ptr := &m.items[m.used_len]
|
||||
ptr^ = item
|
||||
|
||||
ptr.handle.idx = auto_cast m.used_len
|
||||
ptr.handle.gen = 1
|
||||
m.used_len += 1
|
||||
return ptr.handle, true
|
||||
}
|
||||
|
||||
// `get` a stable pointer of type `^T` by resolving the handle `h`. If the handle is not valid, then `nil, false` is returned.
|
||||
@(require_results)
|
||||
static_get :: proc "contextless" (m: ^$H/Static_Handle_Map($N, $T, $Handle_Type), h: Handle_Type) -> (^T, bool) #optional_ok {
|
||||
if h.idx <= 0 || u32(h.idx) >= m.used_len {
|
||||
return nil, false
|
||||
}
|
||||
if e := &m.items[h.idx]; e.handle == h {
|
||||
return e, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// `remove` an item from the handle map from the handle `h`.
|
||||
static_remove :: proc "contextless" (m: ^$H/Static_Handle_Map($N, $T, $Handle_Type), h: Handle_Type) -> bool {
|
||||
if h.idx <= 0 || u32(h.idx) >= m.used_len {
|
||||
return false
|
||||
}
|
||||
|
||||
if item := &m.items[h.idx]; item.handle == h {
|
||||
m.unused_items[h.idx] = m.next_unused
|
||||
m.next_unused = u32(h.idx)
|
||||
m.unused_len += 1
|
||||
item.handle.idx = 0
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Returns true when the handle `h` is valid relating to the handle map.
|
||||
@(require_results)
|
||||
static_is_valid :: proc "contextless" (m: $H/Static_Handle_Map($N, $T, $Handle_Type), h: Handle_Type) -> bool {
|
||||
return h.idx > 0 && u32(h.idx) < m.used_len && m.items[h.idx].handle == h
|
||||
}
|
||||
|
||||
// Returns the number of possibly valid items in the handle map.
|
||||
@(require_results)
|
||||
static_len :: proc "contextless" (m: $H/Static_Handle_Map($N, $T, $Handle_Type)) -> uint {
|
||||
n := uint(m.used_len) - uint(m.unused_len)
|
||||
return n-1 if n > 0 else 0
|
||||
}
|
||||
|
||||
// Returns the capacity of the items in a handle map.
|
||||
// This is equivalent to `N-1` as the zero value is reserved for the zero-value sentinel.
|
||||
@(require_results)
|
||||
static_cap :: proc "contextless" (m: $H/Static_Handle_Map($N, $T, $Handle_Type)) -> uint {
|
||||
// We could just return `N` but I am doing this for clarity
|
||||
return builtin.len(m.items)-1
|
||||
}
|
||||
|
||||
// `clear` the handle map by zeroing all of the memory.
|
||||
// Internally this does not do `m^ = {}` but rather uses `intrinsics.mem_zero` explicitly improve performance.
|
||||
static_clear :: proc "contextless" (m: ^$H/Static_Handle_Map($N, $T, $Handle_Type)) {
|
||||
intrinsics.mem_zero(m, size_of(m^))
|
||||
}
|
||||
|
||||
// An iterator for a handle map.
|
||||
Static_Handle_Map_Iterator :: struct($H: typeid) {
|
||||
m: ^H,
|
||||
index: u32,
|
||||
}
|
||||
|
||||
// Makes an iterator from a handle map.
|
||||
@(require_results)
|
||||
static_iterator_make :: proc "contextless" (m: ^$H/Static_Handle_Map($N, $T, $Handle_Type)) -> Static_Handle_Map_Iterator(H) {
|
||||
return {m, 1}
|
||||
}
|
||||
|
||||
/*
|
||||
Iterate over a handle map. It will skip over unused item slots (e.g. handle.idx == 0).
|
||||
Usage:
|
||||
it := hm.iterator_make(&the_handle_map)
|
||||
for item, handle in hm.iterate(&it) {
|
||||
...
|
||||
}
|
||||
*/
|
||||
@(require_results)
|
||||
static_iterate :: proc "contextless" (it: ^$HI/Static_Handle_Map_Iterator($H/Static_Handle_Map($N, $T, $Handle_Type))) -> (val: ^T, h: Handle_Type, ok: bool) {
|
||||
for _ in it.index..<it.m.used_len {
|
||||
e := &it.m.items[it.index]
|
||||
it.index += 1
|
||||
|
||||
if e.handle.idx != 0 {
|
||||
return e, e.handle, true
|
||||
}
|
||||
}
|
||||
it.index = 0
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
add :: proc{
|
||||
static_add,
|
||||
dynamic_add,
|
||||
}
|
||||
|
||||
get :: proc{
|
||||
static_get,
|
||||
dynamic_get,
|
||||
}
|
||||
|
||||
remove :: proc{
|
||||
static_remove,
|
||||
dynamic_remove,
|
||||
}
|
||||
|
||||
is_valid :: proc{
|
||||
static_is_valid,
|
||||
dynamic_is_valid,
|
||||
}
|
||||
|
||||
len :: proc{
|
||||
static_len,
|
||||
dynamic_len,
|
||||
}
|
||||
|
||||
cap :: proc{
|
||||
static_cap,
|
||||
dynamic_cap,
|
||||
}
|
||||
|
||||
clear :: proc{
|
||||
static_clear,
|
||||
dynamic_clear,
|
||||
}
|
||||
|
||||
iterator_make :: proc{
|
||||
static_iterator_make,
|
||||
dynamic_iterator_make,
|
||||
}
|
||||
|
||||
iterate :: proc{
|
||||
static_iterate,
|
||||
dynamic_iterate,
|
||||
}
|
||||
142
core/container/pool/pool.odin
Normal file
142
core/container/pool/pool.odin
Normal file
@@ -0,0 +1,142 @@
|
||||
package container_pool
|
||||
|
||||
import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
import "base:sanitizer"
|
||||
import "core:sync"
|
||||
|
||||
_ :: sanitizer
|
||||
_ :: sync
|
||||
|
||||
DEFAULT_BLOCK_SIZE :: _DEFAULT_BLOCK_SIZE
|
||||
|
||||
Pool_Arena :: _Pool_Arena
|
||||
|
||||
/*
|
||||
A thread-safe (between init and destroy) object pool backed by virtual growing arena returning stable pointers.
|
||||
The element type requires an intrusive link node.
|
||||
|
||||
Example:
|
||||
Elem :: struct {
|
||||
link: ^Elem,
|
||||
}
|
||||
|
||||
p: pool.Pool(Elem)
|
||||
pool.init(&p, "link")
|
||||
*/
|
||||
Pool :: struct($T: typeid) {
|
||||
arena: Pool_Arena,
|
||||
num_outstanding: int,
|
||||
num_ready: int,
|
||||
link_off: uintptr,
|
||||
free_list: ^T,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
init :: proc(p: ^Pool($T), $link_field: string, block_size: uint = DEFAULT_BLOCK_SIZE) -> (err: runtime.Allocator_Error)
|
||||
where intrinsics.type_has_field(T, link_field),
|
||||
intrinsics.type_field_type(T, link_field) == ^T {
|
||||
p.link_off = offset_of_by_string(T, link_field)
|
||||
return _pool_arena_init(&p.arena, block_size)
|
||||
}
|
||||
|
||||
destroy :: proc(p: ^Pool($T)) {
|
||||
elem := sync.atomic_exchange_explicit(&p.free_list, nil, .Acquire)
|
||||
|
||||
sync.atomic_store_explicit(&p.num_ready, 0, .Relaxed)
|
||||
|
||||
when .Address in ODIN_SANITIZER_FLAGS {
|
||||
for ; elem != nil; elem = _get_next(p, elem) {
|
||||
_unpoison_elem(p, elem)
|
||||
}
|
||||
} else {
|
||||
_ = elem
|
||||
}
|
||||
|
||||
_pool_arena_destroy(&p.arena)
|
||||
p.arena = {}
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get :: proc(p: ^Pool($T)) -> (elem: ^T, err: runtime.Allocator_Error) #optional_allocator_error {
|
||||
defer sync.atomic_add_explicit(&p.num_outstanding, 1, .Relaxed)
|
||||
|
||||
for {
|
||||
elem = sync.atomic_load_explicit(&p.free_list, .Acquire)
|
||||
if elem == nil {
|
||||
// NOTE: pool arena has an internal lock.
|
||||
return new(T, _pool_arena_allocator(&p.arena))
|
||||
}
|
||||
|
||||
if _, ok := sync.atomic_compare_exchange_weak_explicit(&p.free_list, elem, _get_next(p, elem), .Acquire, .Relaxed); ok {
|
||||
_set_next(p, elem, nil)
|
||||
_unpoison_elem(p, elem)
|
||||
sync.atomic_sub_explicit(&p.num_ready, 1, .Relaxed)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
put :: proc(p: ^Pool($T), elem: ^T) {
|
||||
intrinsics.mem_zero(elem, size_of(T))
|
||||
_poison_elem(p, elem)
|
||||
|
||||
defer sync.atomic_sub_explicit(&p.num_outstanding, 1, .Relaxed)
|
||||
defer sync.atomic_add_explicit(&p.num_ready, 1, .Relaxed)
|
||||
|
||||
for {
|
||||
head := sync.atomic_load_explicit(&p.free_list, .Relaxed)
|
||||
_set_next(p, elem, head)
|
||||
if _, ok := sync.atomic_compare_exchange_weak_explicit(&p.free_list, head, elem, .Release, .Relaxed); ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
num_outstanding :: proc(p: ^Pool($T)) -> int {
|
||||
return sync.atomic_load(&p.num_outstanding)
|
||||
}
|
||||
|
||||
num_ready :: proc(p: ^Pool($T)) -> int {
|
||||
return sync.atomic_load(&p.num_ready)
|
||||
}
|
||||
|
||||
cap :: proc(p: ^Pool($T)) -> int {
|
||||
return sync.atomic_load(&p.num_ready) + sync.atomic_load(&p.num_outstanding)
|
||||
}
|
||||
|
||||
_get_next :: proc(p: ^Pool($T), elem: ^T) -> ^T {
|
||||
return (^^T)(uintptr(elem) + p.link_off)^
|
||||
}
|
||||
|
||||
_set_next :: proc(p: ^Pool($T), elem: ^T, next: ^T) {
|
||||
(^^T)(uintptr(elem) + p.link_off)^ = next
|
||||
}
|
||||
|
||||
_poison_elem :: proc(p: ^Pool($T), elem: ^T) {
|
||||
when .Address in ODIN_SANITIZER_FLAGS {
|
||||
if p.link_off > 0 {
|
||||
sanitizer.address_poison_rawptr(elem, int(p.link_off))
|
||||
}
|
||||
|
||||
len := size_of(T) - p.link_off - size_of(rawptr)
|
||||
if len > 0 {
|
||||
ptr := rawptr(uintptr(elem) + p.link_off + size_of(rawptr))
|
||||
sanitizer.address_poison_rawptr(ptr, int(len))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_unpoison_elem :: proc(p: ^Pool($T), elem: ^T) {
|
||||
when .Address in ODIN_SANITIZER_FLAGS {
|
||||
if p.link_off > 0 {
|
||||
sanitizer.address_unpoison_rawptr(elem, int(p.link_off))
|
||||
}
|
||||
|
||||
len := size_of(T) - p.link_off - size_of(rawptr)
|
||||
if len > 0 {
|
||||
ptr := rawptr(uintptr(elem) + p.link_off + size_of(rawptr))
|
||||
sanitizer.address_unpoison_rawptr(ptr, int(len))
|
||||
}
|
||||
}
|
||||
}
|
||||
27
core/container/pool/pool_arena_others.odin
Normal file
27
core/container/pool/pool_arena_others.odin
Normal file
@@ -0,0 +1,27 @@
|
||||
#+build !darwin
|
||||
#+build !freebsd
|
||||
#+build !openbsd
|
||||
#+build !netbsd
|
||||
#+build !linux
|
||||
#+build !windows
|
||||
#+private
|
||||
package container_pool
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
_Pool_Arena :: runtime.Arena
|
||||
|
||||
_DEFAULT_BLOCK_SIZE :: runtime.Megabyte
|
||||
|
||||
_pool_arena_init :: proc(arena: ^Pool_Arena, block_size: uint = DEFAULT_BLOCK_SIZE) -> (err: runtime.Allocator_Error) {
|
||||
runtime.arena_init(arena, block_size, runtime.default_allocator()) or_return
|
||||
return
|
||||
}
|
||||
|
||||
_pool_arena_allocator :: proc(arena: ^Pool_Arena) -> runtime.Allocator {
|
||||
return runtime.arena_allocator(arena)
|
||||
}
|
||||
|
||||
_pool_arena_destroy :: proc(arena: ^Pool_Arena) {
|
||||
runtime.arena_destroy(arena)
|
||||
}
|
||||
22
core/container/pool/pool_arena_virtual.odin
Normal file
22
core/container/pool/pool_arena_virtual.odin
Normal file
@@ -0,0 +1,22 @@
|
||||
#+build darwin, freebsd, openbsd, netbsd, linux, windows
|
||||
package container_pool
|
||||
|
||||
import "base:runtime"
|
||||
import "core:mem/virtual"
|
||||
|
||||
_Pool_Arena :: virtual.Arena
|
||||
|
||||
_DEFAULT_BLOCK_SIZE :: runtime.Gigabyte
|
||||
|
||||
_pool_arena_init :: proc(arena: ^Pool_Arena, block_size: uint = DEFAULT_BLOCK_SIZE) -> (err: runtime.Allocator_Error) {
|
||||
virtual.arena_init_growing(arena, block_size) or_return
|
||||
return
|
||||
}
|
||||
|
||||
_pool_arena_allocator :: proc(arena: ^Pool_Arena) -> runtime.Allocator {
|
||||
return virtual.arena_allocator(arena)
|
||||
}
|
||||
|
||||
_pool_arena_destroy :: proc(arena: ^Pool_Arena) {
|
||||
virtual.arena_destroy(arena)
|
||||
}
|
||||
53
core/container/priority_queue/doc.odin
Normal file
53
core/container/priority_queue/doc.odin
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
A priority queue data structure.
|
||||
|
||||
Important: It needs to be initialized with `less` and `swap` procedures, see `init` and `init_from_dynamic_array`.
|
||||
|
||||
Example:
|
||||
import "base:runtime"
|
||||
import pq "core:container/priority_queue"
|
||||
|
||||
main :: proc() {
|
||||
Printer_Job :: struct {
|
||||
user_id: u64,
|
||||
weight: enum u8 {Highest, High, Normal, Low, Idle},
|
||||
}
|
||||
|
||||
q: pq.Priority_Queue(Printer_Job)
|
||||
pq.init(
|
||||
pq = &q,
|
||||
less = proc(a, b: Printer_Job) -> bool {
|
||||
// Jobs will be sorted in order of increasing weight
|
||||
return a.weight < b.weight
|
||||
},
|
||||
swap = pq.default_swap_proc(Printer_Job),
|
||||
)
|
||||
defer pq.destroy(&q)
|
||||
|
||||
// Add jobs with random weights
|
||||
for _ in 0..<100 {
|
||||
job: Printer_Job = ---
|
||||
assert(runtime.random_generator_read_ptr(context.random_generator, &job, size_of(job)))
|
||||
pq.push(&q, job)
|
||||
}
|
||||
|
||||
// Drain jobs in order of importance
|
||||
last: Printer_Job
|
||||
for pq.len(q) > 0 {
|
||||
v := pq.pop(&q)
|
||||
assert(v.weight >= last.weight)
|
||||
last = v
|
||||
}
|
||||
|
||||
// Queue empty?
|
||||
assert(pq.len(q) == 0)
|
||||
|
||||
// Add one more job
|
||||
pq.push(&q, Printer_Job{user_id = 42, weight = .Idle})
|
||||
|
||||
// Cancel all jobs
|
||||
pq.clear(&q)
|
||||
assert(pq.len(q) == 0)
|
||||
}
|
||||
*/
|
||||
package container_priority_queue
|
||||
@@ -1,9 +1,12 @@
|
||||
// A priority queue data structure.
|
||||
package container_priority_queue
|
||||
|
||||
import "base:builtin"
|
||||
import "base:runtime"
|
||||
|
||||
// Priority Queue.
|
||||
//
|
||||
// Important: It needs to be initialized with `less` and `swap` procedures, see `init` and `init_from_dynamic_array`.
|
||||
// See `doc.odin` for an example.
|
||||
Priority_Queue :: struct($T: typeid) {
|
||||
queue: [dynamic]T,
|
||||
|
||||
|
||||
@@ -91,24 +91,24 @@ destroy :: proc(t: ^$T/Tree($Key, $Value), call_on_remove: bool = true) {
|
||||
}
|
||||
}
|
||||
|
||||
len :: proc "contextless" (t: ^$T/Tree($Key, $Value)) -> (node_count: int) {
|
||||
len :: proc "contextless" (t: $T/Tree($Key, $Value)) -> (node_count: int) {
|
||||
return t._size
|
||||
}
|
||||
|
||||
// first returns the first node in the tree (in-order) or nil iff
|
||||
// first returns the first node in the tree (in-order) or nil if and only if (⟺)
|
||||
// the tree is empty.
|
||||
first :: proc "contextless" (t: ^$T/Tree($Key, $Value)) -> ^Node(Key, Value) {
|
||||
return tree_first_or_last_in_order(t, Direction.Backward)
|
||||
}
|
||||
|
||||
// last returns the last element in the tree (in-order) or nil iff
|
||||
// last returns the last element in the tree (in-order) or nil if and only if (⟺)
|
||||
// the tree is empty.
|
||||
last :: proc "contextless" (t: ^$T/Tree($Key, $Value)) -> ^Node(Key, Value) {
|
||||
return tree_first_or_last_in_order(t, Direction.Forward)
|
||||
}
|
||||
|
||||
// find finds the key in the tree, and returns the corresponding node, or nil iff the value is not present.
|
||||
find :: proc(t: ^$T/Tree($Key, $Value), key: Key) -> (node: ^Node(Key, Value)) {
|
||||
// find finds the key in the tree, and returns the corresponding node, or nil if and only if (⟺) the value is not present.
|
||||
find :: proc(t: $T/Tree($Key, $Value), key: Key) -> (node: ^Node(Key, Value)) {
|
||||
node = t._root
|
||||
for node != nil {
|
||||
switch t._cmp_fn(key, node.key) {
|
||||
@@ -120,8 +120,8 @@ find :: proc(t: ^$T/Tree($Key, $Value), key: Key) -> (node: ^Node(Key, Value)) {
|
||||
return node
|
||||
}
|
||||
|
||||
// find_value finds the key in the tree, and returns the corresponding value, or nil iff the value is not present.
|
||||
find_value :: proc(t: ^$T/Tree($Key, $Value), key: Key) -> (value: Value, ok: bool) #optional_ok {
|
||||
// find_value finds the key in the tree, and returns the corresponding value, or nil if and only if (⟺) the value is not present.
|
||||
find_value :: proc(t: $T/Tree($Key, $Value), key: Key) -> (value: Value, ok: bool) #optional_ok {
|
||||
if n := find(t, key); n != nil {
|
||||
return n.value, true
|
||||
}
|
||||
@@ -154,7 +154,7 @@ find_or_insert :: proc(t: ^$T/Tree($Key, $Value), key: Key, value: Value) -> (n:
|
||||
return n, true, nil
|
||||
}
|
||||
|
||||
// remove removes a node or value from the tree, and returns true iff the
|
||||
// remove removes a node or value from the tree, and returns true if and only if (⟺) the
|
||||
// removal was successful. While the node's value will be left intact,
|
||||
// the node itself will be freed via the tree's node allocator.
|
||||
remove :: proc {
|
||||
@@ -162,18 +162,18 @@ remove :: proc {
|
||||
remove_node,
|
||||
}
|
||||
|
||||
// remove_value removes a value from the tree, and returns true iff the
|
||||
// remove_value removes a value from the tree, and returns true if and only if (⟺) the
|
||||
// removal was successful. While the node's key + value will be left intact,
|
||||
// the node itself will be freed via the tree's node allocator.
|
||||
remove_key :: proc(t: ^$T/Tree($Key, $Value), key: Key, call_on_remove := true) -> bool {
|
||||
n := find(t, key)
|
||||
n := find(t^, key)
|
||||
if n == nil {
|
||||
return false // Key not found, nothing to do
|
||||
}
|
||||
return remove_node(t, n, call_on_remove)
|
||||
}
|
||||
|
||||
// remove_node removes a node from the tree, and returns true iff the
|
||||
// remove_node removes a node from the tree, and returns true if and only if (⟺) the
|
||||
// removal was successful. While the node's key + value will be left intact,
|
||||
// the node itself will be freed via the tree's node allocator.
|
||||
remove_node :: proc(t: ^$T/Tree($Key, $Value), node: ^$N/Node(Key, Value), call_on_remove := true) -> (found: bool) {
|
||||
@@ -235,14 +235,14 @@ iterator_from_pos :: proc "contextless" (t: ^$T/Tree($Key, $Value), pos: ^Node(K
|
||||
}
|
||||
|
||||
// iterator_get returns the node currently pointed to by the iterator,
|
||||
// or nil iff the node has been removed, the tree is empty, or the end
|
||||
// or nil if and only if (⟺) the node has been removed, the tree is empty, or the end
|
||||
// of the tree has been reached.
|
||||
iterator_get :: proc "contextless" (it: ^$I/Iterator($Key, $Value)) -> ^Node(Key, Value) {
|
||||
return it._cur
|
||||
}
|
||||
|
||||
// iterator_remove removes the node currently pointed to by the iterator,
|
||||
// and returns true iff the removal was successful. Semantics are the
|
||||
// and returns true if and only if (⟺) the removal was successful. Semantics are the
|
||||
// same as the Tree remove.
|
||||
iterator_remove :: proc(it: ^$I/Iterator($Key, $Value), call_on_remove: bool = true) -> bool {
|
||||
if it._cur == nil {
|
||||
@@ -258,7 +258,7 @@ iterator_remove :: proc(it: ^$I/Iterator($Key, $Value), call_on_remove: bool = t
|
||||
}
|
||||
|
||||
// iterator_next advances the iterator and returns the (node, true) or
|
||||
// or (nil, false) iff the end of the tree has been reached.
|
||||
// or (nil, false) if and only if (⟺) the end of the tree has been reached.
|
||||
//
|
||||
// Note: The first call to iterator_next will return the first node instead
|
||||
// of advancing the iterator.
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/*
|
||||
Deprecation Notice: Prefer using `[dynamic; N]T` (fixed capacity dynamic arrays).
|
||||
|
||||
A dynamic array-like interface on a stack-allocated, fixed-size array.
|
||||
|
||||
The `Small_Array` type is optimal for scenarios where you need
|
||||
|
||||
@@ -21,7 +21,7 @@ Example:
|
||||
}
|
||||
*/
|
||||
Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
|
||||
data: [N]T,
|
||||
data: [N]T `fmt:",len"`,
|
||||
len: int,
|
||||
}
|
||||
|
||||
@@ -386,7 +386,7 @@ Attempts to add the given element at the beginning.
|
||||
This operation assumes that the small-array is not empty.
|
||||
|
||||
Note: Performing this operation will cause pointers obtained
|
||||
through get_ptr(_save) to reference incorrect elements.
|
||||
through get_ptr(_safe) to reference incorrect elements.
|
||||
|
||||
**Inputs**
|
||||
- `a`: A pointer to the small-array
|
||||
@@ -466,7 +466,7 @@ Removes and returns the first element of the small-array.
|
||||
This operation assumes that the small-array is not empty.
|
||||
|
||||
Note: Performing this operation will cause pointers obtained
|
||||
through get_ptr(_save) to reference incorrect elements.
|
||||
through get_ptr(_safe) to reference incorrect elements.
|
||||
|
||||
**Inputs**
|
||||
- `a`: A pointer to the small-array
|
||||
@@ -542,7 +542,7 @@ Attempts to remove and return the first element of the small array.
|
||||
Unlike `pop_front`, it does not assume that the array is non-empty.
|
||||
|
||||
Note: Performing this operation will cause pointers obtained
|
||||
through get_ptr(_save) to reference incorrect elements.
|
||||
through get_ptr(_safe) to reference incorrect elements.
|
||||
|
||||
**Inputs**
|
||||
- `a`: A pointer to the small-array
|
||||
@@ -616,7 +616,7 @@ consume :: proc "odin" (a: ^$A/Small_Array($N, $T), count: int, loc := #caller_l
|
||||
Removes the element at the specified index while retaining order.
|
||||
|
||||
Note: Performing this operation will cause pointers obtained
|
||||
through get_ptr(_save) to reference incorrect elements.
|
||||
through get_ptr(_safe) to reference incorrect elements.
|
||||
|
||||
**Inputs**
|
||||
- `a`: A pointer to the small-array
|
||||
@@ -754,7 +754,7 @@ push_back_elems :: proc "contextless" (a: ^$A/Small_Array($N, $T), items: ..T) -
|
||||
Tries to insert an element at the specified position.
|
||||
|
||||
Note: Performing this operation will cause pointers obtained
|
||||
through get_ptr(_save) to reference incorrect elements.
|
||||
through get_ptr(_safe) to reference incorrect elements.
|
||||
|
||||
**Inputs**
|
||||
- `a`: A pointer to the small-array
|
||||
|
||||
157
core/container/xar/freelist.odin
Normal file
157
core/container/xar/freelist.odin
Normal file
@@ -0,0 +1,157 @@
|
||||
package container_xar
|
||||
|
||||
@(require) import "base:runtime"
|
||||
|
||||
Freelist_Array :: struct($T: typeid, $SHIFT: uint) where
|
||||
0 < SHIFT,
|
||||
SHIFT <= MAX_SHIFT,
|
||||
size_of(T) >= size_of(^T) {
|
||||
array: Array(T, SHIFT),
|
||||
freelist: ^T,
|
||||
}
|
||||
|
||||
freelist_init :: proc(x: ^$X/Freelist_Array($T, $SHIFT), allocator := context.allocator) {
|
||||
init(&x.array, allocator)
|
||||
x.freelist = nil
|
||||
}
|
||||
|
||||
freelist_destroy :: proc(x: ^$X/Freelist_Array($T, $SHIFT)) {
|
||||
destroy(&x.array)
|
||||
x.freelist = nil
|
||||
}
|
||||
|
||||
freelist_clear :: proc(x: ^$X/Freelist_Array($T, $SHIFT)) {
|
||||
clear(&x.array)
|
||||
x.freelist = nil
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
freelist_push_with_index :: proc(x: ^$X/Freelist_Array($T, $SHIFT), value: T, loc := #caller_location) -> (ptr: ^T, index: int, err: runtime.Allocator_Error) {
|
||||
if x.freelist != nil {
|
||||
slot := x.freelist
|
||||
idx, found := freelist_linear_search(x, slot)
|
||||
assert(found)
|
||||
x.freelist = (^^T)(slot)^
|
||||
slot^ = value
|
||||
return slot, idx, nil
|
||||
}
|
||||
idx := x.array.len
|
||||
ptr = array_push_back_elem_and_get_ptr(&x.array, value, loc) or_return
|
||||
return ptr, idx, nil
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
freelist_push :: proc(x: ^$X/Freelist_Array($T, $SHIFT), value: T, loc := #caller_location) -> (ptr: ^T, err: runtime.Allocator_Error) {
|
||||
ptr, _, err = freelist_push_with_index(x, value, loc)
|
||||
return
|
||||
}
|
||||
|
||||
freelist_pop :: proc(x: ^$X/Freelist_Array($T, $SHIFT), #any_int index: int, loc := #caller_location) -> T {
|
||||
item := array_get_ptr(&x.array, index, loc)
|
||||
result := item^
|
||||
(^^T)(item)^ = x.freelist
|
||||
x.freelist = item
|
||||
return result
|
||||
}
|
||||
|
||||
freelist_release :: proc(x: ^$X/Freelist_Array($T, $SHIFT), #any_int index: int, loc := #caller_location) {
|
||||
item := array_get_ptr(&x.array, index, loc)
|
||||
(^^T)(item)^ = x.freelist
|
||||
x.freelist = item
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
freelist_linear_search :: proc(x: ^$X/Freelist_Array($T, $SHIFT), ptr: ^T) -> (index: int, found: bool) {
|
||||
base := 0
|
||||
for chunk, c in x.array.chunks {
|
||||
if chunk == nil {
|
||||
break
|
||||
}
|
||||
chunk_cap := 1 << (SHIFT + uint(c if c > 0 else 1) - 1)
|
||||
ptr_addr := uintptr(ptr)
|
||||
chunk_start_addr := uintptr(chunk)
|
||||
chunk_end_addr := chunk_start_addr + uintptr(chunk_cap * size_of(T))
|
||||
if chunk_start_addr <= ptr_addr && ptr_addr < chunk_end_addr {
|
||||
offset := int(ptr_addr - chunk_start_addr) / size_of(T)
|
||||
return base + offset, true
|
||||
}
|
||||
base += chunk_cap
|
||||
}
|
||||
return -1, false
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
freelist_get :: proc(x: ^$X/Freelist_Array($T, $SHIFT), #any_int index: int, loc := #caller_location) -> T {
|
||||
return array_get(&x.array, index, loc)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
freelist_get_ptr :: proc(x: ^$X/Freelist_Array($T, $SHIFT), #any_int index: int, loc := #caller_location) -> ^T {
|
||||
return array_get_ptr(&x.array, index, loc)
|
||||
}
|
||||
|
||||
freelist_set :: proc(x: ^$X/Freelist_Array($T, $SHIFT), #any_int index: int, value: T, loc := #caller_location) {
|
||||
array_set(&x.array, index, value, loc)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
freelist_len :: proc(x: $X/Freelist_Array($T, $SHIFT)) -> int {
|
||||
return x.array.len
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
freelist_cap :: proc(x: $X/Freelist_Array($T, $SHIFT)) -> int {
|
||||
return array_cap(x.array)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
freelist_is_freed :: proc(x: ^$X/Freelist_Array($T, $SHIFT), #any_int index: int) -> bool {
|
||||
ptr := array_get_ptr(&x.array, index)
|
||||
current := x.freelist
|
||||
for current != nil {
|
||||
if current == ptr {
|
||||
return true
|
||||
}
|
||||
current = (^^T)(current)^
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
Freelist_Iterator :: struct($T: typeid, $SHIFT: uint) {
|
||||
freelist_array: ^Freelist_Array(T, SHIFT),
|
||||
idx: int,
|
||||
}
|
||||
|
||||
freelist_iterator :: proc(x: ^$X/Freelist_Array($T, $SHIFT)) -> Freelist_Iterator(T, SHIFT) {
|
||||
return {freelist_array = x, idx = 0}
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
freelist_iterate_by_val :: proc(it: ^Freelist_Iterator($T, $SHIFT)) -> (val: T, idx: int, ok: bool) {
|
||||
for it.idx < it.freelist_array.array.len {
|
||||
if !freelist_is_freed(it.freelist_array, it.idx) {
|
||||
val = array_get(&it.freelist_array.array, it.idx)
|
||||
idx = it.idx
|
||||
it.idx += 1
|
||||
return val, idx, true
|
||||
}
|
||||
it.idx += 1
|
||||
}
|
||||
it.idx = 0
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
freelist_iterate_by_ptr :: proc(it: ^Freelist_Iterator($T, $SHIFT)) -> (val: ^T, idx: int, ok: bool) {
|
||||
for it.idx < it.freelist_array.array.len {
|
||||
if !freelist_is_freed(it.freelist_array, it.idx) {
|
||||
val = array_get_ptr(&it.freelist_array.array, it.idx)
|
||||
idx = it.idx
|
||||
it.idx += 1
|
||||
return val, idx, true
|
||||
}
|
||||
it.idx += 1
|
||||
}
|
||||
it.idx = 0
|
||||
return
|
||||
}
|
||||
538
core/container/xar/xar.odin
Normal file
538
core/container/xar/xar.odin
Normal file
@@ -0,0 +1,538 @@
|
||||
/*
|
||||
Exponential Array (Xar).
|
||||
|
||||
A dynamically growing array using exponentially-sized chunks, providing stable
|
||||
memory addresses for all elements. Unlike `[dynamic]T`, elements are never
|
||||
moved once allocated, making it safe to hold pointers to elements.
|
||||
|
||||
For more information: https://azmr.uk/dyn/#exponential-arrayxar
|
||||
|
||||
Example:
|
||||
|
||||
import "core:container/xar"
|
||||
|
||||
example :: proc() {
|
||||
x: xar.Array(int, 4)
|
||||
defer xar.destroy(&x)
|
||||
|
||||
xar.push_back(&x, 10)
|
||||
xar.push_back(&x, 20)
|
||||
xar.push_back(&x, 30)
|
||||
|
||||
ptr := xar.get_ptr(&x, 1) // ptr remains valid after more push_backs
|
||||
xar.push_back(&x, 40)
|
||||
fmt.println(ptr^) // prints 20
|
||||
}
|
||||
*/
|
||||
package container_xar
|
||||
|
||||
@(require) import "base:intrinsics"
|
||||
@(require) import "base:runtime"
|
||||
|
||||
PLATFORM_BITS :: 8*size_of(uint)
|
||||
_LOG2_PLATFORM_BITS :: intrinsics.constant_log2(PLATFORM_BITS)
|
||||
|
||||
MAX_SHIFT :: PLATFORM_BITS>>1
|
||||
|
||||
/*
|
||||
An Exponential Array with stable element addresses.
|
||||
|
||||
Unlike `[dynamic]T` which reallocates and moves elements when growing, `Array`
|
||||
allocates separate chunks of exponentially increasing size. This guarantees
|
||||
that pointers to elements remain valid for the lifetime of the container.
|
||||
|
||||
Fields:
|
||||
- `chunks`: Fixed array of multi-pointers to allocated chunks
|
||||
- `len`: Number of elements currently stored
|
||||
- `allocator`: Allocator used for chunk allocations
|
||||
|
||||
Type Parameters:
|
||||
- `T`: The element type
|
||||
- `SHIFT`: Controls initial chunk size (1 << SHIFT). Must be in range (0, MAX_SHIFT].
|
||||
Larger values mean fewer, bigger chunks. Recommended: 4-8.
|
||||
|
||||
Chunk sizes grow as:
|
||||
- `chunks[0]`: 1 << SHIFT elements
|
||||
- `chunks[1]`: 1 << SHIFT elements
|
||||
- `chunks[2]`: 1 << (SHIFT + 1) elements
|
||||
- `chunks[3]`: 1 << (SHIFT + 2) elements
|
||||
- `chunks[4]`: 1 << (SHIFT + 3) elements
|
||||
- ...and so on
|
||||
|
||||
Example:
|
||||
|
||||
import "core:container/xar"
|
||||
|
||||
example :: proc() {
|
||||
// Xar with initial chunk size of 16 (1 << 4)
|
||||
x: xar.Array(My_Struct, 4)
|
||||
defer xar.destroy(&x)
|
||||
}
|
||||
*/
|
||||
Array :: struct($T: typeid, $SHIFT: uint) where 0 < SHIFT, SHIFT <= MAX_SHIFT {
|
||||
chunks: [(1 << (_LOG2_PLATFORM_BITS - intrinsics.constant_log2(SHIFT))) + 1][^]T,
|
||||
len: int,
|
||||
allocator: runtime.Allocator,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Initializes an exponential array with the given allocator.
|
||||
|
||||
**Inputs**
|
||||
- `x`: Pointer to the exponential array to initialize
|
||||
- `allocator`: Allocator to use for chunk allocations (defaults to context.allocator)
|
||||
*/
|
||||
array_init :: proc(x: ^$X/Array($T, $SHIFT), allocator := context.allocator) {
|
||||
x^ = {allocator = allocator}
|
||||
}
|
||||
|
||||
/*
|
||||
Frees all allocated chunks and resets the exponential array.
|
||||
|
||||
**Inputs**
|
||||
- `x`: Pointer to the exponential array to destroy
|
||||
*/
|
||||
array_destroy :: proc(x: ^$X/Array($T, $SHIFT)) {
|
||||
#reverse for c, i in x.chunks {
|
||||
if c != nil {
|
||||
n := 1 << (SHIFT + uint(i if i > 0 else 1) - 1)
|
||||
size_in_bytes := n * size_of(T)
|
||||
runtime.mem_free_with_size(c, size_in_bytes, x.allocator)
|
||||
}
|
||||
}
|
||||
x^ = {}
|
||||
}
|
||||
|
||||
/*
|
||||
Resets the array's length to zero without freeing memory.
|
||||
Allocated chunks are retained for reuse.
|
||||
*/
|
||||
array_clear :: proc "contextless" (x: ^$X/Array($T, $SHIFT)) {
|
||||
x.len = 0
|
||||
}
|
||||
|
||||
// Returns the length of the exponential-array
|
||||
@(require_results)
|
||||
array_len :: proc "contextless" (x: $X/Array($T, $SHIFT)) -> int {
|
||||
return x.len
|
||||
}
|
||||
|
||||
// Returns the number of allocated elements
|
||||
@(require_results)
|
||||
array_cap :: proc "contextless" (x: $X/Array($T, $SHIFT)) -> int {
|
||||
#reverse for c, i in x.chunks {
|
||||
if c != nil {
|
||||
return 1 << (SHIFT + uint(i if i > 0 else 1))
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Internal: computes chunk index, element index within chunk, and chunk capacity for a given index.
|
||||
@(require_results)
|
||||
_meta_get :: #force_inline proc "contextless" ($SHIFT: uint, index: uint) -> (chunk_idx, elem_idx, chunk_cap: uint) {
|
||||
elem_idx = index
|
||||
chunk_cap = uint(1) << SHIFT
|
||||
chunk_idx = 0
|
||||
|
||||
index_shift := index >> SHIFT
|
||||
if index_shift > 0 {
|
||||
N :: 8*size_of(uint)-1
|
||||
CLZ :: intrinsics.count_leading_zeros
|
||||
chunk_idx = N-CLZ(index_shift) // MSB(index_shift)
|
||||
|
||||
chunk_cap = 1 << (chunk_idx + SHIFT)
|
||||
elem_idx -= chunk_cap
|
||||
chunk_idx += 1
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
/*
|
||||
Get a copy of the element at the specified index.
|
||||
|
||||
**Inputs**
|
||||
- `x`: Pointer to the exponential array
|
||||
- `index`: Position of the element (0-indexed)
|
||||
|
||||
**Returns**
|
||||
- a copy of the element
|
||||
*/
|
||||
@(require_results)
|
||||
array_get :: proc(x: ^$X/Array($T, $SHIFT), #any_int index: int, loc := #caller_location) -> (val: T) #no_bounds_check {
|
||||
runtime.bounds_check_error_loc(loc, index, x.len)
|
||||
chunk_idx, elem_idx, _ := _meta_get(SHIFT, uint(index))
|
||||
return x.chunks[chunk_idx][elem_idx]
|
||||
}
|
||||
|
||||
/*
|
||||
Get a pointer to the element at the specified index.
|
||||
|
||||
The returned pointer remains valid even after additional elements are added,
|
||||
as long as the element is not removed and the array is not destroyed.
|
||||
|
||||
**Inputs**
|
||||
- `x`: Pointer to the exponential array
|
||||
- `index`: Position of the element (0-indexed)
|
||||
|
||||
**Returns**
|
||||
- a stable pointer to the element
|
||||
|
||||
Example:
|
||||
|
||||
import "core:container/xar"
|
||||
|
||||
get_ptr_example :: proc() {
|
||||
x: xar.Array(int, 4)
|
||||
defer xar.destroy(&x)
|
||||
|
||||
xar.push_back(&x, 100)
|
||||
ptr := xar.get_ptr(&x, 0)
|
||||
|
||||
// Pointer remains valid after growing
|
||||
for i in 0..<1000 {
|
||||
xar.push_back(&x, i)
|
||||
}
|
||||
|
||||
fmt.println(ptr^) // Still prints 100
|
||||
}
|
||||
*/
|
||||
@(require_results)
|
||||
array_get_ptr :: proc(x: ^$X/Array($T, $SHIFT), #any_int index: int, loc := #caller_location) -> (val: ^T) #no_bounds_check {
|
||||
runtime.bounds_check_error_loc(loc, index, x.len)
|
||||
chunk_idx, elem_idx, _ := _meta_get(SHIFT, uint(index))
|
||||
return &x.chunks[chunk_idx][elem_idx]
|
||||
}
|
||||
|
||||
// No bounds checking
|
||||
@(require_results)
|
||||
array_get_ptr_unsafe :: proc "contextless" (x: ^$X/Array($T, $SHIFT), #any_int index: int) -> (val: ^T) #no_bounds_check {
|
||||
chunk_idx, elem_idx, _ := _meta_get(SHIFT, uint(index))
|
||||
return &x.chunks[chunk_idx][elem_idx]
|
||||
}
|
||||
|
||||
/*
|
||||
Set the element at the specified index to the given value.
|
||||
|
||||
**Inputs**
|
||||
- `x`: Pointer to the exponential array
|
||||
- `index`: Position of the element (0-indexed)
|
||||
- `value`: The value to set
|
||||
*/
|
||||
array_set :: proc(x: ^$X/Array($T, $SHIFT), #any_int index: int, value: T, loc := #caller_location) #no_bounds_check {
|
||||
runtime.bounds_check_error_loc(loc, index, x.len)
|
||||
chunk_idx, elem_idx, _ := _meta_get(SHIFT, uint(index))
|
||||
x.chunks[chunk_idx][elem_idx] = value
|
||||
}
|
||||
|
||||
array_append :: proc{array_push_back_elem, array_push_back_elems}
|
||||
array_push_back :: proc{array_push_back_elem, array_push_back_elems}
|
||||
|
||||
|
||||
/*
|
||||
Append an element to the end of the exponential array.
|
||||
Allocates a new chunk if necessary. Existing elements aren't moved, and their pointers remain stable.
|
||||
|
||||
**Inputs**
|
||||
- `x`: Pointer to the exponential array
|
||||
- `value`: The element to append
|
||||
|
||||
**Returns**
|
||||
- number of elements added (always 1 on success)
|
||||
- allocation error if chunk allocation failed
|
||||
|
||||
Example:
|
||||
|
||||
import "core:container/xar"
|
||||
|
||||
push_back_example :: proc() {
|
||||
x: xar.Array(string, 4)
|
||||
defer xar.destroy(&x)
|
||||
|
||||
xar.push_back(&x, "hello")
|
||||
xar.push_back(&x, "world")
|
||||
|
||||
fmt.println(xar.get(&x, 0)) // hello
|
||||
fmt.println(xar.get(&x, 1)) // world
|
||||
}
|
||||
*/
|
||||
array_push_back_elem :: proc(x: ^$X/Array($T, $SHIFT), value: T, loc := #caller_location) -> (n: int, err: runtime.Allocator_Error) {
|
||||
if x.allocator.procedure == nil {
|
||||
// to minic `[dynamic]T` behaviour
|
||||
x.allocator = context.allocator
|
||||
}
|
||||
|
||||
chunk_idx, elem_idx, chunk_cap := _meta_get(SHIFT, uint(x.len))
|
||||
if x.chunks[chunk_idx] == nil {
|
||||
x.chunks[chunk_idx] = make([^]T, chunk_cap, x.allocator) or_return
|
||||
}
|
||||
x.chunks[chunk_idx][elem_idx] = value
|
||||
x.len += 1
|
||||
n = 1
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Append multiple elements to the end of the exponential array.
|
||||
|
||||
**Inputs**
|
||||
- `x`: Pointer to the exponential array
|
||||
- `values`: The elements to append
|
||||
|
||||
**Returns**
|
||||
- number of elements successfully added
|
||||
- allocation error if chunk allocation failed (partial append possible)
|
||||
*/
|
||||
array_push_back_elems :: proc(x: ^$X/Array($T, $SHIFT), values: ..T, loc := #caller_location) -> (n: int, err: runtime.Allocator_Error) {
|
||||
for value in values {
|
||||
n += array_push_back_elem(x, value, loc) or_return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
array_append_and_get_ptr :: array_push_back_elem_and_get_ptr
|
||||
append_and_get_ptr :: array_push_back_elem_and_get_ptr
|
||||
|
||||
/*
|
||||
Append an element and return a stable pointer to it.
|
||||
This is useful when you need to initialize a complex struct in-place or
|
||||
retain a reference to the newly added element.
|
||||
|
||||
**Inputs**
|
||||
- `x`: Pointer to the exponential array
|
||||
- `value`: The element to append
|
||||
|
||||
**Returns**
|
||||
- a stable pointer to the newly added element
|
||||
- allocation error if chunk allocation failed
|
||||
|
||||
Example:
|
||||
|
||||
import "core:container/xar"
|
||||
|
||||
push_back_and_get_ptr_example :: proc() {
|
||||
x: xar.Array(My_Struct, 4)
|
||||
defer xar.destroy(&x)
|
||||
|
||||
ptr := xar.push_back_elem_and_get_ptr(&x, My_Struct{}) or_else panic("alloc failed")
|
||||
ptr.field = 42 // Initialize in-place
|
||||
}
|
||||
*/
|
||||
@(require_results)
|
||||
array_push_back_elem_and_get_ptr :: proc(x: ^$X/Array($T, $SHIFT), value: T, loc := #caller_location) -> (ptr: ^T, err: runtime.Allocator_Error) {
|
||||
if x.allocator.procedure == nil {
|
||||
// to minic `[dynamic]T` behaviour
|
||||
x.allocator = context.allocator
|
||||
}
|
||||
|
||||
chunk_idx, elem_idx, chunk_cap := _meta_get(SHIFT, uint(x.len))
|
||||
if x.chunks[chunk_idx] == nil {
|
||||
x.chunks[chunk_idx] = make([^]T, chunk_cap, x.allocator) or_return
|
||||
}
|
||||
x.chunks[chunk_idx][elem_idx] = value
|
||||
x.len += 1
|
||||
ptr = &x.chunks[chunk_idx][elem_idx]
|
||||
return
|
||||
}
|
||||
|
||||
// `pop` will remove and return the end value of an exponential array `x` and reduces the length of the array by 1.
|
||||
//
|
||||
// Note: If the exponential array has no elements (`xar.len(x) == 0`), this procedure will panic.
|
||||
array_pop :: proc(x: ^$X/Array($T, $SHIFT), loc := #caller_location) -> (val: T) {
|
||||
assert(x.len > 0, loc=loc)
|
||||
index := uint(x.len-1)
|
||||
chunk_idx, elem_idx, _ := _meta_get(SHIFT, index)
|
||||
x.len -= 1
|
||||
return x.chunks[chunk_idx][elem_idx]
|
||||
}
|
||||
|
||||
// `pop_safe` trys to remove and return the end value of dynamic array `x` and reduces the length of the array by 1.
|
||||
// If the operation is not possible, it will return false.
|
||||
@(require_results)
|
||||
array_pop_safe :: proc(x: ^$X/Array($T, $SHIFT)) -> (val: T, ok: bool) {
|
||||
if x.len == 0 {
|
||||
return
|
||||
}
|
||||
index := uint(x.len-1)
|
||||
chunk_idx, elem_idx, _ := _meta_get(SHIFT, index)
|
||||
x.len -= 1
|
||||
|
||||
val = x.chunks[chunk_idx][elem_idx]
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
`unordered_remove` removed the element at the specified `index`. It does so by replacing the current end value
|
||||
with the old value, and reducing the length of the exponential array by 1.
|
||||
|
||||
Note: This is an O(1) operation.
|
||||
Note: This is currently no procedure that is the equivalent of an "ordered_remove"
|
||||
Note: If the index is out of bounds, this procedure will panic.
|
||||
|
||||
Note: Pointers to the last element become invalid (it gets moved). Pointers to other elements remain valid.
|
||||
|
||||
Example:
|
||||
|
||||
import "core:encoding/xar"
|
||||
|
||||
unordered_remove_example :: proc() {
|
||||
x: xar.Array(int, 4)
|
||||
defer xar.destroy(&x)
|
||||
|
||||
xar.push_back(&x, 10)
|
||||
xar.push_back(&x, 20)
|
||||
xar.push_back(&x, 30)
|
||||
|
||||
xar.unordered_remove(&x, 0) // Removes 10, replaces with 30
|
||||
|
||||
// Array now contains [30, 20]
|
||||
fmt.println(xar.get(&x, 0)) // 30
|
||||
fmt.println(xar.get(&x, 1)) // 20
|
||||
}
|
||||
*/
|
||||
array_unordered_remove :: proc(x: ^$X/Array($T, $SHIFT), #any_int index: int, loc := #caller_location) {
|
||||
runtime.bounds_check_error_loc(loc, index, x.len)
|
||||
n := x.len-1
|
||||
if index != n {
|
||||
end := array_get(x, n)
|
||||
array_set(x, index, end)
|
||||
}
|
||||
x.len -= 1
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
array_linear_search :: proc(x: ^$X/Array($T, $SHIFT), elem: T) -> (index: int, found: bool) where intrinsics.type_is_comparable(T) {
|
||||
it := array_iterator(x)
|
||||
for val, i in array_iterate_by_val(it) {
|
||||
if val == elem {
|
||||
return i, true
|
||||
}
|
||||
}
|
||||
return -1, false
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Iterator state for traversing a `Xar`.
|
||||
|
||||
Fields:
|
||||
- `xar`: Pointer to the exponential array being iterated
|
||||
- `idx`: Current iteration index
|
||||
*/
|
||||
Array_Iterator :: struct($T: typeid, $SHIFT: uint) {
|
||||
xar: ^Array(T, SHIFT),
|
||||
idx: int,
|
||||
}
|
||||
|
||||
/*
|
||||
Create an iterator for traversing the exponential array.
|
||||
|
||||
**Inputs**
|
||||
- `xar`: Pointer to the exponential array
|
||||
|
||||
**Returns**
|
||||
- an iterator positioned at the start
|
||||
|
||||
Example:
|
||||
|
||||
import "core:container/xar"
|
||||
import "core:fmt"
|
||||
|
||||
iterator_example :: proc() {
|
||||
x: xar.Array(int, 4)
|
||||
defer xar.destroy(&x)
|
||||
|
||||
xar.push_back(&x, 10)
|
||||
xar.push_back(&x, 20)
|
||||
xar.push_back(&x, 30)
|
||||
|
||||
it := xar.iterator(&x)
|
||||
for val in xar.iterate_by_ptr(&it) {
|
||||
fmt.println(val^)
|
||||
}
|
||||
}
|
||||
|
||||
Output:
|
||||
|
||||
10
|
||||
20
|
||||
30
|
||||
*/
|
||||
array_iterator :: proc(xar: ^$X/Array($T, $SHIFT)) -> Array_Iterator(T, SHIFT) {
|
||||
return {xar = auto_cast xar, idx = 0}
|
||||
}
|
||||
|
||||
/*
|
||||
Advance the iterator and returns the next element.
|
||||
|
||||
**Inputs**
|
||||
- `it`: Pointer to the iterator
|
||||
|
||||
**Returns**
|
||||
- current element
|
||||
- `true` if an element was returned, `false` if iteration is complete
|
||||
*/
|
||||
array_iterate_by_val :: proc(it: ^Array_Iterator($T, $SHIFT)) -> (val: T, idx: int, ok: bool) {
|
||||
if it.idx >= it.xar.len {
|
||||
it.idx = 0
|
||||
return
|
||||
}
|
||||
val = array_get(it.xar, it.idx)
|
||||
idx = it.idx
|
||||
it.idx += 1
|
||||
return val, idx, true
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Advance the iterator and returns a pointer to the next element.
|
||||
|
||||
**Inputs**
|
||||
- `it`: Pointer to the iterator
|
||||
|
||||
**Returns**
|
||||
- pointer to the current element
|
||||
- `true` if an element was returned, `false` if iteration is complete
|
||||
*/
|
||||
array_iterate_by_ptr :: proc(it: ^Array_Iterator($T, $SHIFT)) -> (val: ^T, idx: int, ok: bool) {
|
||||
if it.idx >= it.xar.len {
|
||||
it.idx = 0
|
||||
return
|
||||
}
|
||||
val = array_get_ptr(it.xar, it.idx)
|
||||
idx = it.idx
|
||||
it.idx += 1
|
||||
return val, idx, true
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
init :: proc{array_init, freelist_init}
|
||||
destroy :: proc{array_destroy, freelist_destroy}
|
||||
clear :: proc{array_clear, freelist_clear}
|
||||
len :: proc{array_len, freelist_len}
|
||||
cap :: proc{array_cap, freelist_cap}
|
||||
get :: proc{array_get, freelist_get}
|
||||
get_ptr_unsafe :: proc{array_get_ptr_unsafe}
|
||||
get_ptr :: proc{array_get_ptr, freelist_get_ptr}
|
||||
set :: proc{array_set, freelist_set}
|
||||
append :: proc{array_push_back_elem, array_push_back_elems}
|
||||
push_back :: proc{array_push_back_elem, array_push_back_elems}
|
||||
push_back_elem :: proc{array_push_back_elem}
|
||||
push_back_elems :: proc{array_push_back_elems}
|
||||
push_back_elem_and_get_ptr:: proc{array_push_back_elem_and_get_ptr}
|
||||
pop :: proc{array_pop, freelist_pop}
|
||||
pop_safe :: proc{array_pop_safe}
|
||||
unordered_remove :: proc{array_unordered_remove}
|
||||
iterator :: proc{array_iterator, freelist_iterator}
|
||||
iterate_by_val :: proc{array_iterate_by_val, freelist_iterate_by_val}
|
||||
iterate_by_ptr :: proc{array_iterate_by_ptr, freelist_iterate_by_ptr}
|
||||
|
||||
push_with_index :: proc{freelist_push_with_index}
|
||||
push :: proc{freelist_push}
|
||||
release :: proc{freelist_release}
|
||||
linear_search :: proc{array_linear_search, freelist_linear_search}
|
||||
is_freed :: proc{freelist_is_freed}
|
||||
@@ -1,7 +1,6 @@
|
||||
package aes_ct64
|
||||
|
||||
import "base:intrinsics"
|
||||
import "core:mem"
|
||||
import "core:crypto"
|
||||
|
||||
STRIDE :: 4
|
||||
|
||||
@@ -82,5 +81,5 @@ decrypt_blocks :: proc(ctx: ^Context, dst, src: [][]byte) {
|
||||
// reset sanitizes the Context. The Context must be re-initialized to
|
||||
// be used again.
|
||||
reset :: proc(ctx: ^Context) {
|
||||
mem.zero_explicit(ctx, size_of(ctx))
|
||||
}
|
||||
crypto.zero_explicit(ctx, size_of(ctx))
|
||||
}
|
||||
@@ -22,8 +22,6 @@
|
||||
|
||||
package aes_ct64
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
inv_sub_bytes :: proc "contextless" (q: ^[8]u64) {
|
||||
// AES S-box is:
|
||||
// S(x) = A(I(x)) ^ 0x63
|
||||
|
||||
@@ -22,9 +22,9 @@
|
||||
|
||||
package aes_ct64
|
||||
|
||||
import "core:crypto"
|
||||
import "core:crypto/_aes"
|
||||
import "core:encoding/endian"
|
||||
import "core:mem"
|
||||
|
||||
@(private, require_results)
|
||||
sub_word :: proc "contextless" (x: u32) -> u32 {
|
||||
@@ -35,7 +35,7 @@ sub_word :: proc "contextless" (x: u32) -> u32 {
|
||||
orthogonalize(&q)
|
||||
ret := u32(q[0])
|
||||
|
||||
mem.zero_explicit(&q[0], size_of(u64))
|
||||
crypto.zero_explicit(&q[0], size_of(u64))
|
||||
|
||||
return ret
|
||||
}
|
||||
@@ -97,8 +97,8 @@ keysched :: proc "contextless" (comp_skey: []u64, key: []byte) -> int {
|
||||
(q[7] & 0x8888888888888888)
|
||||
}
|
||||
|
||||
mem.zero_explicit(&skey, size_of(skey))
|
||||
mem.zero_explicit(&q, size_of(q))
|
||||
crypto.zero_explicit(&skey, size_of(skey))
|
||||
crypto.zero_explicit(&q, size_of(q))
|
||||
|
||||
return num_rounds
|
||||
}
|
||||
|
||||
69
core/crypto/_aes/hw/api.odin
Normal file
69
core/crypto/_aes/hw/api.odin
Normal file
@@ -0,0 +1,69 @@
|
||||
package aes_hw
|
||||
|
||||
@(require) import "core:sys/info"
|
||||
|
||||
// is_supported returns true if and only if (⟺) hardware accelerated AES
|
||||
// is supported.
|
||||
is_supported :: proc "contextless" () -> bool {
|
||||
when ODIN_ARCH == .amd64 {
|
||||
// Note: Everything with AES-NI has support for
|
||||
// the required SSE extxtensions.
|
||||
req_features :: info.CPU_Features{
|
||||
.sse2,
|
||||
.ssse3,
|
||||
.sse41,
|
||||
.aes,
|
||||
}
|
||||
return info.cpu_features() >= req_features
|
||||
} else when ODIN_ARCH == .arm64 || ODIN_ARCH == .arm32 {
|
||||
req_features :: info.CPU_Features{
|
||||
.asimd,
|
||||
.aes,
|
||||
}
|
||||
return info.cpu_features() >= req_features
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// is_ghash_supported returns true if and only if (⟺) hardware accelerated
|
||||
// GHASH is supported.
|
||||
is_ghash_supported :: proc "contextless" () -> bool {
|
||||
// Just having hardware GHASH is silly.
|
||||
if !is_supported() {
|
||||
return false
|
||||
}
|
||||
|
||||
when ODIN_ARCH == .amd64 {
|
||||
return info.cpu_features() >= info.CPU_Features{
|
||||
.pclmulqdq,
|
||||
}
|
||||
} else when ODIN_ARCH == .arm64 || ODIN_ARCH == .arm32{
|
||||
// Once we can actually use this, we can re-enable this.
|
||||
//
|
||||
// return info.cpu_features() >= info.CPU_Features{
|
||||
// .pmull,
|
||||
// }
|
||||
return false
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 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 `u8x16`, however that implies alignment (or using AVX).
|
||||
//
|
||||
// All the people using e-waste processors that don't support an
|
||||
// instruction 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)
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#+build amd64
|
||||
package aes_hw_intel
|
||||
package aes_hw
|
||||
|
||||
import "base:intrinsics"
|
||||
import "core:crypto/_aes"
|
||||
115
core/crypto/_aes/hw/intrinsics_arm.odin
Normal file
115
core/crypto/_aes/hw/intrinsics_arm.odin
Normal file
@@ -0,0 +1,115 @@
|
||||
#+build arm64,arm32
|
||||
package aes_hw
|
||||
|
||||
import "core:simd"
|
||||
import "core:simd/arm"
|
||||
|
||||
// https://blog.michaelbrase.com/2018/05/08/emulating-x86-aes-intrinsics-on-armv8-a/
|
||||
|
||||
TARGET_FEATURES :: "neon,aes"
|
||||
HAS_GHASH :: false // Temporary
|
||||
|
||||
@(require_results, enable_target_feature = "aes")
|
||||
aesdec :: #force_inline proc "c" (data, key: simd.u8x16) -> simd.u8x16 {
|
||||
return simd.bit_xor(arm.vaesimcq_u8(arm.vaesdq_u8(data, simd.u8x16{})), key)
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature = "aes")
|
||||
aesdeclast :: #force_inline proc "c" (data, key: simd.u8x16) -> simd.u8x16 {
|
||||
return simd.bit_xor(arm.vaesdq_u8(data, simd.u8x16{}), key)
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature = "aes")
|
||||
aesenc :: #force_inline proc "c" (data, key: simd.u8x16) -> simd.u8x16 {
|
||||
return simd.bit_xor(arm.vaesmcq_u8(arm.vaeseq_u8(data, simd.u8x16{})), key)
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature = "aes")
|
||||
aesenclast :: #force_inline proc "c" (data, key: simd.u8x16) -> simd.u8x16 {
|
||||
return simd.bit_xor(arm.vaeseq_u8(data, simd.u8x16{}), key)
|
||||
}
|
||||
|
||||
aesimc :: arm.vaesimcq_u8
|
||||
|
||||
@(require_results, enable_target_feature = "aes")
|
||||
aeskeygenassist :: #force_inline proc "c" (data: simd.u8x16, $IMM8: u8) -> simd.u8x16 {
|
||||
a := arm.vaeseq_u8(data, simd.u8x16{}) // AESE does ShiftRows and SubBytes on A
|
||||
|
||||
// Undo ShiftRows step from AESE and extract X1 and X3
|
||||
dest := simd.swizzle(
|
||||
a,
|
||||
0x04, 0x01, 0x0e, 0x0b, // SubBytes(X1)
|
||||
0x01, 0x0e, 0x0b, 0x04, // ROT(SubBytes(X1))
|
||||
0x0c, 0x09, 0x06, 0x03, // SubBytes(X3)
|
||||
0x09, 0x06, 0x03, 0x0c, // ROT(SubBytes(X3))
|
||||
)
|
||||
|
||||
rcons := simd.u8x16{
|
||||
0, 0, 0, 0,
|
||||
IMM8, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
IMM8, 0, 0, 0,
|
||||
}
|
||||
|
||||
return simd.bit_xor(dest, rcons)
|
||||
}
|
||||
|
||||
// The keyschedule implementation is easier to read with some extra
|
||||
// Intel intrinsics that are emulated by built-in LLVM ops anyway.
|
||||
|
||||
@(private, require_results, enable_target_feature = TARGET_FEATURES)
|
||||
_mm_slli_si128 :: #force_inline proc "c" (a: simd.u8x16, $IMM8: u32) -> simd.u8x16 {
|
||||
shift :: IMM8 & 0xff
|
||||
|
||||
// This needs to emit behavior identical to PSLLDQ which is as follows:
|
||||
//
|
||||
// TEMP := COUNT
|
||||
// IF (TEMP > 15) THEN TEMP := 16; FI
|
||||
// DEST := DEST << (TEMP * 8)
|
||||
// DEST[MAXVL-1:128] (Unmodified)
|
||||
|
||||
return simd.shuffle(
|
||||
simd.u8x16{},
|
||||
a,
|
||||
0 when shift > 15 else (16 - shift + 0),
|
||||
1 when shift > 15 else (16 - shift + 1),
|
||||
2 when shift > 15 else (16 - shift + 2),
|
||||
3 when shift > 15 else (16 - shift + 3),
|
||||
4 when shift > 15 else (16 - shift + 4),
|
||||
5 when shift > 15 else (16 - shift + 5),
|
||||
6 when shift > 15 else (16 - shift + 6),
|
||||
7 when shift > 15 else (16 - shift + 7),
|
||||
8 when shift > 15 else (16 - shift + 8),
|
||||
9 when shift > 15 else (16 - shift + 9),
|
||||
10 when shift > 15 else (16 - shift + 10),
|
||||
11 when shift > 15 else (16 - shift + 11),
|
||||
12 when shift > 15 else (16 - shift + 12),
|
||||
13 when shift > 15 else (16 - shift + 13),
|
||||
14 when shift > 15 else (16 - shift + 14),
|
||||
15 when shift > 15 else (16 - shift + 15),
|
||||
)
|
||||
}
|
||||
|
||||
@(private, require_results, enable_target_feature = TARGET_FEATURES)
|
||||
_mm_shuffle_epi32 :: #force_inline proc "c" (a: simd.u8x16, $IMM8: u32) -> simd.u8x16 {
|
||||
v := transmute(simd.i32x4)a
|
||||
return transmute(simd.u8x16)simd.shuffle(
|
||||
v,
|
||||
v,
|
||||
IMM8 & 0b11,
|
||||
(IMM8 >> 2) & 0b11,
|
||||
(IMM8 >> 4) & 0b11,
|
||||
(IMM8 >> 6) & 0b11,
|
||||
)
|
||||
}
|
||||
|
||||
@(private, require_results, enable_target_feature = TARGET_FEATURES)
|
||||
_mm_shuffle_ps :: #force_inline proc "c" (a, b: simd.u8x16, $MASK: u32) -> simd.u8x16 {
|
||||
return transmute(simd.u8x16)simd.shuffle(
|
||||
transmute(simd.u32x4)(a),
|
||||
transmute(simd.u32x4)(b),
|
||||
u32(MASK) & 0b11,
|
||||
(u32(MASK)>>2) & 0b11,
|
||||
((u32(MASK)>>4) & 0b11)+4,
|
||||
((u32(MASK)>>6) & 0b11)+4)
|
||||
}
|
||||
55
core/crypto/_aes/hw/intrinsics_intel.odin
Normal file
55
core/crypto/_aes/hw/intrinsics_intel.odin
Normal file
@@ -0,0 +1,55 @@
|
||||
#+build amd64
|
||||
package aes_hw
|
||||
|
||||
import "core:simd"
|
||||
import "core:simd/x86"
|
||||
|
||||
// Intel/RISC-V semantics.
|
||||
|
||||
TARGET_FEATURES :: "sse,sse2,ssse3,sse4.1,aes"
|
||||
HAS_GHASH :: true
|
||||
|
||||
@(require_results, enable_target_feature = "aes")
|
||||
aesdec :: #force_inline proc "c" (data, key: simd.u8x16) -> simd.u8x16 {
|
||||
return transmute(simd.u8x16)(x86._mm_aesdec_si128(transmute(x86.__m128i)(data), transmute(x86.__m128i)(key)))
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature = "aes")
|
||||
aesdeclast :: #force_inline proc "c" (data, key: simd.u8x16) -> simd.u8x16 {
|
||||
return transmute(simd.u8x16)(x86._mm_aesdeclast_si128(transmute(x86.__m128i)(data), transmute(x86.__m128i)(key)))
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature = "aes")
|
||||
aesenc :: #force_inline proc "c" (data, key: simd.u8x16) -> simd.u8x16 {
|
||||
return transmute(simd.u8x16)(x86._mm_aesenc_si128(transmute(x86.__m128i)(data), transmute(x86.__m128i)(key)))
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature = "aes")
|
||||
aesenclast :: #force_inline proc "c" (data, key: simd.u8x16) -> simd.u8x16 {
|
||||
return transmute(simd.u8x16)(x86._mm_aesenclast_si128(transmute(x86.__m128i)(data), transmute(x86.__m128i)(key)))
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature = "aes")
|
||||
aesimc :: #force_inline proc "c" (data: simd.u8x16) -> simd.u8x16 {
|
||||
return transmute(simd.u8x16)(x86._mm_aesimc_si128(transmute(x86.__m128i)(data)))
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature = "aes")
|
||||
aeskeygenassist :: #force_inline proc "c" (data: simd.u8x16, $IMM8: u8) -> simd.u8x16 {
|
||||
return transmute(simd.u8x16)(x86._mm_aeskeygenassist_si128(transmute(x86.__m128i)(data), IMM8))
|
||||
}
|
||||
|
||||
@(private, require_results, enable_target_feature = TARGET_FEATURES)
|
||||
_mm_slli_si128 :: #force_inline proc "c" (a: simd.u8x16, $IMM8: u32) -> simd.u8x16 {
|
||||
return transmute(simd.u8x16)(x86._mm_slli_si128(transmute(x86.__m128i)(a), IMM8))
|
||||
}
|
||||
|
||||
@(private, require_results, enable_target_feature = TARGET_FEATURES)
|
||||
_mm_shuffle_epi32 :: #force_inline proc "c" (a: simd.u8x16, $IMM8: u32) -> simd.u8x16 {
|
||||
return transmute(simd.u8x16)(x86._mm_shuffle_epi32(transmute(x86.__m128i)(a), IMM8))
|
||||
}
|
||||
|
||||
@(private, require_results, enable_target_feature = TARGET_FEATURES)
|
||||
_mm_shuffle_ps :: #force_inline proc "c" (a, b: simd.u8x16, $MASK: u32) -> simd.u8x16 {
|
||||
return transmute(simd.u8x16)(x86._mm_shuffle_ps(transmute(x86.__m128)(a), transmute(x86.__m128)(b), MASK))
|
||||
}
|
||||
181
core/crypto/_aes/hw/keysched_hw.odin
Normal file
181
core/crypto/_aes/hw/keysched_hw.odin
Normal file
@@ -0,0 +1,181 @@
|
||||
// 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,arm32
|
||||
package aes_hw
|
||||
|
||||
import "base:intrinsics"
|
||||
import "core:crypto"
|
||||
import "core:crypto/_aes"
|
||||
import "core:simd"
|
||||
|
||||
// Inspiration taken from BearSSL's AES-NI implementation.
|
||||
//
|
||||
// Note: This assumes that the SROA optimization pass is enabled to be
|
||||
// anything resembling performant 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 = TARGET_FEATURES)
|
||||
expand_step128 :: #force_inline proc(k1, k2: simd.u8x16) -> simd.u8x16 {
|
||||
k1, k2 := k1, k2
|
||||
|
||||
k2 = _mm_shuffle_epi32(k2, 0xff)
|
||||
k1 = simd.bit_xor(k1, _mm_slli_si128(k1, 0x04))
|
||||
k1 = simd.bit_xor(k1, _mm_slli_si128(k1, 0x04))
|
||||
k1 = simd.bit_xor(k1, _mm_slli_si128(k1, 0x04))
|
||||
return simd.bit_xor(k1, k2)
|
||||
}
|
||||
|
||||
@(private = "file", require_results, enable_target_feature = TARGET_FEATURES)
|
||||
expand_step192a :: #force_inline proc (k1_, k2_: ^simd.u8x16, k3: simd.u8x16) -> (simd.u8x16, simd.u8x16) {
|
||||
k1, k2, k3 := k1_^, k2_^, k3
|
||||
|
||||
k3 = _mm_shuffle_epi32(k3, 0x55)
|
||||
k1 = simd.bit_xor(k1, _mm_slli_si128(k1, 0x04))
|
||||
k1 = simd.bit_xor(k1, _mm_slli_si128(k1, 0x04))
|
||||
k1 = simd.bit_xor(k1, _mm_slli_si128(k1, 0x04))
|
||||
k1 = simd.bit_xor(k1, k3)
|
||||
|
||||
tmp := k2
|
||||
k2 = simd.bit_xor(k2, _mm_slli_si128(k2, 0x04))
|
||||
k2 = simd.bit_xor(k2, _mm_shuffle_epi32(k1, 0xff))
|
||||
|
||||
k1_, k2_ := k1_, k2_
|
||||
k1_^, k2_^ = k1, k2
|
||||
|
||||
r1 := _mm_shuffle_ps(tmp, k1, 0x44)
|
||||
r2 := _mm_shuffle_ps(k1, k2, 0x4e)
|
||||
|
||||
return r1, r2
|
||||
}
|
||||
|
||||
@(private = "file", require_results, enable_target_feature = TARGET_FEATURES)
|
||||
expand_step192b :: #force_inline proc (k1_, k2_: ^simd.u8x16, k3: simd.u8x16) -> simd.u8x16 {
|
||||
k1, k2, k3 := k1_^, k2_^, k3
|
||||
|
||||
k3 = _mm_shuffle_epi32(k3, 0x55)
|
||||
k1 = simd.bit_xor(k1, _mm_slli_si128(k1, 0x04))
|
||||
k1 = simd.bit_xor(k1, _mm_slli_si128(k1, 0x04))
|
||||
k1 = simd.bit_xor(k1, _mm_slli_si128(k1, 0x04))
|
||||
k1 = simd.bit_xor(k1, k3)
|
||||
|
||||
k2 = simd.bit_xor(k2, _mm_slli_si128(k2, 0x04))
|
||||
k2 = simd.bit_xor(k2, _mm_shuffle_epi32(k1, 0xff))
|
||||
|
||||
k1_, k2_ := k1_, k2_
|
||||
k1_^, k2_^ = k1, k2
|
||||
|
||||
return k1
|
||||
}
|
||||
|
||||
@(private = "file", require_results, enable_target_feature = TARGET_FEATURES)
|
||||
expand_step256b :: #force_inline proc(k1, k2: simd.u8x16) -> simd.u8x16 {
|
||||
k1, k2 := k1, k2
|
||||
|
||||
k2 = _mm_shuffle_epi32(k2, 0xaa)
|
||||
k1 = simd.bit_xor(k1, _mm_slli_si128(k1, 0x04))
|
||||
k1 = simd.bit_xor(k1, _mm_slli_si128(k1, 0x04))
|
||||
k1 = simd.bit_xor(k1, _mm_slli_si128(k1, 0x04))
|
||||
return simd.bit_xor(k1, k2)
|
||||
}
|
||||
|
||||
@(private = "file", enable_target_feature = TARGET_FEATURES)
|
||||
derive_dec_keys :: proc(ctx: ^Context, sks: ^[15]simd.u8x16, num_rounds: int) {
|
||||
intrinsics.unaligned_store((^simd.u8x16)(&ctx._sk_exp_dec[0]), sks[num_rounds])
|
||||
for i in 1 ..< num_rounds {
|
||||
tmp := aesimc(sks[i])
|
||||
intrinsics.unaligned_store((^simd.u8x16)(&ctx._sk_exp_dec[num_rounds - i]), tmp)
|
||||
}
|
||||
intrinsics.unaligned_store((^simd.u8x16)(&ctx._sk_exp_dec[num_rounds]), sks[0])
|
||||
}
|
||||
|
||||
@(private, enable_target_feature = TARGET_FEATURES)
|
||||
keysched :: proc(ctx: ^Context, key: []byte) {
|
||||
sks: [15]simd.u8x16 = ---
|
||||
|
||||
// Compute the encryption keys.
|
||||
num_rounds, key_len := 0, len(key)
|
||||
switch key_len {
|
||||
case _aes.KEY_SIZE_128:
|
||||
sks[0] = intrinsics.unaligned_load((^simd.u8x16)(raw_data(key)))
|
||||
sks[1] = expand_step128(sks[0], aeskeygenassist(sks[0], 0x01))
|
||||
sks[2] = expand_step128(sks[1], aeskeygenassist(sks[1], 0x02))
|
||||
sks[3] = expand_step128(sks[2], aeskeygenassist(sks[2], 0x04))
|
||||
sks[4] = expand_step128(sks[3], aeskeygenassist(sks[3], 0x08))
|
||||
sks[5] = expand_step128(sks[4], aeskeygenassist(sks[4], 0x10))
|
||||
sks[6] = expand_step128(sks[5], aeskeygenassist(sks[5], 0x20))
|
||||
sks[7] = expand_step128(sks[6], aeskeygenassist(sks[6], 0x40))
|
||||
sks[8] = expand_step128(sks[7], aeskeygenassist(sks[7], 0x80))
|
||||
sks[9] = expand_step128(sks[8], aeskeygenassist(sks[8], 0x1b))
|
||||
sks[10] = expand_step128(sks[9], aeskeygenassist(sks[9], 0x36))
|
||||
num_rounds = _aes.ROUNDS_128
|
||||
case _aes.KEY_SIZE_192:
|
||||
k0 := intrinsics.unaligned_load((^simd.u8x16)(raw_data(key)))
|
||||
|
||||
k1_tmp: [16]byte
|
||||
copy(k1_tmp[:], key[16:24])
|
||||
k1 := intrinsics.unaligned_load((^simd.u8x16)(&k1_tmp))
|
||||
crypto.zero_explicit(&k1_tmp, size_of(k1_tmp))
|
||||
|
||||
sks[0] = k0
|
||||
sks[1], sks[2] = expand_step192a(&k0, &k1, aeskeygenassist(k1, 0x01))
|
||||
sks[3] = expand_step192b(&k0, &k1, aeskeygenassist(k1, 0x02))
|
||||
sks[4], sks[5] = expand_step192a(&k0, &k1, aeskeygenassist(k1, 0x04))
|
||||
sks[6] = expand_step192b(&k0, &k1, aeskeygenassist(k1, 0x08))
|
||||
sks[7], sks[8] = expand_step192a(&k0, &k1, aeskeygenassist(k1, 0x10))
|
||||
sks[9] = expand_step192b(&k0, &k1, aeskeygenassist(k1, 0x20))
|
||||
sks[10], sks[11] = expand_step192a(&k0, &k1, aeskeygenassist(k1, 0x40))
|
||||
sks[12] = expand_step192b(&k0, &k1, aeskeygenassist(k1, 0x80))
|
||||
num_rounds = _aes.ROUNDS_192
|
||||
|
||||
case _aes.KEY_SIZE_256:
|
||||
sks[0] = intrinsics.unaligned_load((^simd.u8x16)(raw_data(key)))
|
||||
sks[1] = intrinsics.unaligned_load((^simd.u8x16)(raw_data(key[16:])))
|
||||
sks[2] = expand_step128(sks[0], aeskeygenassist(sks[1], 0x01))
|
||||
sks[3] = expand_step256b(sks[1], aeskeygenassist(sks[2], 0x01))
|
||||
sks[4] = expand_step128(sks[2], aeskeygenassist(sks[3], 0x02))
|
||||
sks[5] = expand_step256b(sks[3], aeskeygenassist(sks[4], 0x02))
|
||||
sks[6] = expand_step128(sks[4], aeskeygenassist(sks[5], 0x04))
|
||||
sks[7] = expand_step256b(sks[5], aeskeygenassist(sks[6], 0x04))
|
||||
sks[8] = expand_step128(sks[6], aeskeygenassist(sks[7], 0x08))
|
||||
sks[9] = expand_step256b(sks[7], aeskeygenassist(sks[8], 0x08))
|
||||
sks[10] = expand_step128(sks[8], aeskeygenassist(sks[9], 0x10))
|
||||
sks[11] = expand_step256b(sks[9], aeskeygenassist(sks[10], 0x10))
|
||||
sks[12] = expand_step128(sks[10], aeskeygenassist(sks[11], 0x20))
|
||||
sks[13] = expand_step256b(sks[11], aeskeygenassist(sks[12], 0x20))
|
||||
sks[14] = expand_step128(sks[12], aeskeygenassist(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((^simd.u8x16)(&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
|
||||
|
||||
crypto.zero_explicit(&sks, size_of(sks))
|
||||
}
|
||||
11
core/crypto/_aes/hw/unsupported.odin
Normal file
11
core/crypto/_aes/hw/unsupported.odin
Normal file
@@ -0,0 +1,11 @@
|
||||
#+build !amd64
|
||||
#+build !arm64
|
||||
#+build !arm32
|
||||
package aes_hw
|
||||
|
||||
HAS_GHASH :: false
|
||||
|
||||
@(private)
|
||||
keysched :: proc(ctx: ^Context, key: []byte) {
|
||||
panic("crypto/aes: hardware implementation unsupported")
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
#+build amd64
|
||||
package aes_hw_intel
|
||||
|
||||
import "core:sys/info"
|
||||
|
||||
// is_supported 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)
|
||||
}
|
||||
|
||||
@@ -1,178 +0,0 @@
|
||||
// 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))
|
||||
}
|
||||
@@ -10,25 +10,21 @@ package _blake2
|
||||
Implementation of the BLAKE2 hashing algorithm, as defined in <https://datatracker.ietf.org/doc/html/rfc7693> and <https://www.blake2.net/>
|
||||
*/
|
||||
|
||||
import "base:intrinsics"
|
||||
import "core:crypto"
|
||||
import "core:encoding/endian"
|
||||
import "core:mem"
|
||||
|
||||
BLAKE2S_BLOCK_SIZE :: 64
|
||||
BLAKE2S_SIZE :: 32
|
||||
BLAKE2B_BLOCK_SIZE :: 128
|
||||
BLAKE2B_SIZE :: 64
|
||||
|
||||
MAX_SIZE :: 255
|
||||
|
||||
Blake2s_Context :: struct {
|
||||
h: [8]u32,
|
||||
t: [2]u32,
|
||||
f: [2]u32,
|
||||
x: [BLAKE2S_BLOCK_SIZE]byte,
|
||||
nx: int,
|
||||
ih: [8]u32,
|
||||
padded_key: [BLAKE2S_BLOCK_SIZE]byte,
|
||||
is_keyed: bool,
|
||||
size: byte,
|
||||
is_last_node: bool,
|
||||
|
||||
@@ -41,9 +37,6 @@ Blake2b_Context :: struct {
|
||||
f: [2]u64,
|
||||
x: [BLAKE2B_BLOCK_SIZE]byte,
|
||||
nx: int,
|
||||
ih: [8]u64,
|
||||
padded_key: [BLAKE2B_BLOCK_SIZE]byte,
|
||||
is_keyed: bool,
|
||||
size: byte,
|
||||
is_last_node: bool,
|
||||
|
||||
@@ -86,11 +79,12 @@ BLAKE2B_IV := [8]u64 {
|
||||
|
||||
init :: proc "contextless" (ctx: ^$T, cfg: ^Blake2_Config) {
|
||||
when T == Blake2s_Context {
|
||||
max_size :: BLAKE2S_SIZE
|
||||
MAX_SIZE :: BLAKE2S_SIZE
|
||||
} else when T == Blake2b_Context {
|
||||
max_size :: BLAKE2B_SIZE
|
||||
MAX_SIZE :: BLAKE2B_SIZE
|
||||
}
|
||||
ensure_contextless(cfg.size <= max_size, "blake2: requested output size exceeeds algorithm max")
|
||||
ensure_contextless(cfg.size <= MAX_SIZE, "blake2: requested output size exceeeds algorithm max")
|
||||
ensure_contextless(len(cfg.key) <= MAX_SIZE, "blake2: requested key size exceeeds algorithm max")
|
||||
|
||||
// To save having to allocate a scratch buffer, use the internal
|
||||
// data buffer (`ctx.x`), as it is exactly the correct size.
|
||||
@@ -133,7 +127,7 @@ init :: proc "contextless" (ctx: ^$T, cfg: ^Blake2_Config) {
|
||||
p[17] = cfg.tree.(Blake2_Tree).inner_hash_size
|
||||
}
|
||||
} else {
|
||||
p[2], p[3] = 1, 1
|
||||
p[2], p[3], p[4], p[5], p[6], p[7] = 1, 1, 0, 0, 0, 0
|
||||
}
|
||||
ctx.size = cfg.size
|
||||
for i := 0; i < 8; i += 1 {
|
||||
@@ -145,23 +139,17 @@ init :: proc "contextless" (ctx: ^$T, cfg: ^Blake2_Config) {
|
||||
}
|
||||
}
|
||||
|
||||
mem.zero(&ctx.x, size_of(ctx.x)) // Done with the scratch space, no barrier.
|
||||
intrinsics.mem_zero(&ctx.x, size_of(ctx.x)) // Done with the scratch space, no barrier.
|
||||
|
||||
if cfg.tree != nil && cfg.tree.(Blake2_Tree).is_last_node {
|
||||
ctx.is_last_node = true
|
||||
}
|
||||
if len(cfg.key) > 0 {
|
||||
copy(ctx.padded_key[:], cfg.key)
|
||||
update(ctx, ctx.padded_key[:])
|
||||
ctx.is_keyed = true
|
||||
copy(ctx.x[:], cfg.key)
|
||||
ctx.nx = len(ctx.x)
|
||||
} else {
|
||||
ctx.nx = 0
|
||||
}
|
||||
copy(ctx.ih[:], ctx.h[:])
|
||||
copy(ctx.h[:], ctx.ih[:])
|
||||
if ctx.is_keyed {
|
||||
update(ctx, ctx.padded_key[:])
|
||||
}
|
||||
|
||||
ctx.nx = 0
|
||||
|
||||
ctx.is_initialized = true
|
||||
}
|
||||
@@ -171,22 +159,22 @@ update :: proc "contextless" (ctx: ^$T, p: []byte) {
|
||||
|
||||
p := p
|
||||
when T == Blake2s_Context {
|
||||
block_size :: BLAKE2S_BLOCK_SIZE
|
||||
BLOCK_SIZE :: BLAKE2S_BLOCK_SIZE
|
||||
} else when T == Blake2b_Context {
|
||||
block_size :: BLAKE2B_BLOCK_SIZE
|
||||
BLOCK_SIZE :: BLAKE2B_BLOCK_SIZE
|
||||
}
|
||||
|
||||
left := block_size - ctx.nx
|
||||
left := BLOCK_SIZE - ctx.nx
|
||||
if len(p) > left {
|
||||
copy(ctx.x[ctx.nx:], p[:left])
|
||||
p = p[left:]
|
||||
blocks(ctx, ctx.x[:])
|
||||
ctx.nx = 0
|
||||
}
|
||||
if len(p) > block_size {
|
||||
n := len(p) &~ (block_size - 1)
|
||||
if len(p) > BLOCK_SIZE {
|
||||
n := len(p) &~ (BLOCK_SIZE - 1)
|
||||
if n == len(p) {
|
||||
n -= block_size
|
||||
n -= BLOCK_SIZE
|
||||
}
|
||||
blocks(ctx, p[:n])
|
||||
p = p[n:]
|
||||
@@ -222,17 +210,11 @@ reset :: proc "contextless" (ctx: ^$T) {
|
||||
return
|
||||
}
|
||||
|
||||
mem.zero_explicit(ctx, size_of(ctx^))
|
||||
crypto.zero_explicit(ctx, size_of(ctx^))
|
||||
}
|
||||
|
||||
@(private)
|
||||
blake2s_final :: proc "contextless" (ctx: ^Blake2s_Context, hash: []byte) {
|
||||
if ctx.is_keyed {
|
||||
for i := 0; i < len(ctx.padded_key); i += 1 {
|
||||
ctx.padded_key[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
dec := BLAKE2S_BLOCK_SIZE - u32(ctx.nx)
|
||||
if ctx.t[0] < dec {
|
||||
ctx.t[1] -= 1
|
||||
@@ -244,23 +226,20 @@ blake2s_final :: proc "contextless" (ctx: ^Blake2s_Context, hash: []byte) {
|
||||
ctx.f[1] = 0xffffffff
|
||||
}
|
||||
|
||||
for i := ctx.nx; i < BLAKE2S_BLOCK_SIZE; i+= 1 {
|
||||
ctx.x[i] = 0
|
||||
}
|
||||
blocks(ctx, ctx.x[:])
|
||||
|
||||
dst: [BLAKE2S_SIZE]byte
|
||||
for i := 0; i < BLAKE2S_SIZE / 4; i += 1 {
|
||||
endian.unchecked_put_u32le(dst[i * 4:], ctx.h[i])
|
||||
}
|
||||
copy(hash, dst[:])
|
||||
copy(hash, dst[:ctx.size])
|
||||
}
|
||||
|
||||
@(private)
|
||||
blake2b_final :: proc "contextless" (ctx: ^Blake2b_Context, hash: []byte) {
|
||||
if ctx.is_keyed {
|
||||
for i := 0; i < len(ctx.padded_key); i += 1 {
|
||||
ctx.padded_key[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
dec := BLAKE2B_BLOCK_SIZE - u64(ctx.nx)
|
||||
if ctx.t[0] < dec {
|
||||
ctx.t[1] -= 1
|
||||
@@ -272,6 +251,9 @@ blake2b_final :: proc "contextless" (ctx: ^Blake2b_Context, hash: []byte) {
|
||||
ctx.f[1] = 0xffffffffffffffff
|
||||
}
|
||||
|
||||
for i := ctx.nx; i < BLAKE2B_BLOCK_SIZE; i+= 1 {
|
||||
ctx.x[i] = 0
|
||||
}
|
||||
blocks(ctx, ctx.x[:])
|
||||
|
||||
dst: [BLAKE2B_SIZE]byte
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package _chacha20
|
||||
|
||||
import "core:crypto"
|
||||
import "core:encoding/endian"
|
||||
import "core:math/bits"
|
||||
import "core:mem"
|
||||
|
||||
// KEY_SIZE is the (X)ChaCha20 key size in bytes.
|
||||
KEY_SIZE :: 32
|
||||
@@ -88,8 +88,8 @@ seek :: proc(ctx: ^Context, block_nr: u64) {
|
||||
// reset sanitizes the Context. The Context must be re-initialized to
|
||||
// be used again.
|
||||
reset :: proc(ctx: ^Context) {
|
||||
mem.zero_explicit(&ctx._s, size_of(ctx._s))
|
||||
mem.zero_explicit(&ctx._buffer, size_of(ctx._buffer))
|
||||
crypto.zero_explicit(&ctx._s, size_of(ctx._s))
|
||||
crypto.zero_explicit(&ctx._buffer, size_of(ctx._buffer))
|
||||
|
||||
ctx._is_initialized = false
|
||||
}
|
||||
@@ -116,4 +116,4 @@ check_counter_limit :: proc(ctx: ^Context, nr_blocks: int) {
|
||||
}
|
||||
|
||||
ensure(ctr_ok, "crypto/chacha20: maximum (X)ChaCha20 keystream per IV reached")
|
||||
}
|
||||
}
|
||||
@@ -4,133 +4,68 @@ import "core:crypto/_chacha20"
|
||||
import "core:encoding/endian"
|
||||
import "core:math/bits"
|
||||
|
||||
// At least with LLVM21 force_inline produces identical perf to
|
||||
// manual inlining, yay.
|
||||
@(private)
|
||||
quarter_round :: #force_inline proc "contextless" (a, b, c, d: u32) -> (u32, u32, u32, u32) {
|
||||
a, b, c, d := a, b, c, d
|
||||
|
||||
a += b
|
||||
d ~= a
|
||||
d = bits.rotate_left32(d, 16)
|
||||
|
||||
c += d
|
||||
b ~= c
|
||||
b = bits.rotate_left32(b, 12)
|
||||
|
||||
a += b
|
||||
d ~= a
|
||||
d = bits.rotate_left32(d, 8)
|
||||
|
||||
c += d
|
||||
b ~= c
|
||||
b = bits.rotate_left32(b, 7)
|
||||
|
||||
return a, b, c, d
|
||||
}
|
||||
|
||||
stream_blocks :: proc(ctx: ^_chacha20.Context, dst, src: []byte, nr_blocks: int) {
|
||||
// Enforce the maximum consumed keystream per IV.
|
||||
_chacha20.check_counter_limit(ctx, nr_blocks)
|
||||
|
||||
dst, src := dst, src
|
||||
x := &ctx._s
|
||||
|
||||
|
||||
// Filippo Valsorda made an observation that only one of the column
|
||||
// round depends on the counter (s12), so it is worth precomputing
|
||||
// and reusing across multiple blocks. As far as I know, only Go's
|
||||
// chacha implementation does this.
|
||||
|
||||
p1, p5, p9, p13 := quarter_round(_chacha20.SIGMA_1, x[5], x[9], x[13])
|
||||
p2, p6, p10, p14 := quarter_round(_chacha20.SIGMA_2, x[6], x[10], x[14])
|
||||
p3, p7, p11, p15 := quarter_round(_chacha20.SIGMA_3, x[7], x[11], x[15])
|
||||
|
||||
for n := 0; n < nr_blocks; n = n + 1 {
|
||||
x0, x1, x2, x3 :=
|
||||
_chacha20.SIGMA_0, _chacha20.SIGMA_1, _chacha20.SIGMA_2, _chacha20.SIGMA_3
|
||||
x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15 :=
|
||||
x[4], x[5], x[6], x[7], x[8], x[9], x[10], x[11], x[12], x[13], x[14], x[15]
|
||||
// First column round that depends on the counter
|
||||
p0, p4, p8, p12 := quarter_round(_chacha20.SIGMA_0, x[4], x[8], x[12])
|
||||
|
||||
for i := _chacha20.ROUNDS; i > 0; i = i - 2 {
|
||||
// Even when forcing inlining manually inlining all of
|
||||
// these is decently faster.
|
||||
// First diagonal round
|
||||
x0, x5, x10, x15 := quarter_round(p0, p5, p10, p15)
|
||||
x1, x6, x11, x12 := quarter_round(p1, p6, p11, p12)
|
||||
x2, x7, x8, x13 := quarter_round(p2, p7, p8, p13)
|
||||
x3, x4, x9, x14 := quarter_round(p3, p4, p9, p14)
|
||||
|
||||
// quarterround(x, 0, 4, 8, 12)
|
||||
x0 += x4
|
||||
x12 ~= x0
|
||||
x12 = bits.rotate_left32(x12, 16)
|
||||
x8 += x12
|
||||
x4 ~= x8
|
||||
x4 = bits.rotate_left32(x4, 12)
|
||||
x0 += x4
|
||||
x12 ~= x0
|
||||
x12 = bits.rotate_left32(x12, 8)
|
||||
x8 += x12
|
||||
x4 ~= x8
|
||||
x4 = bits.rotate_left32(x4, 7)
|
||||
for i := _chacha20.ROUNDS - 2; i > 0; i = i - 2 {
|
||||
x0, x4, x8, x12 = quarter_round(x0, x4, x8, x12)
|
||||
x1, x5, x9, x13 = quarter_round(x1, x5, x9, x13)
|
||||
x2, x6, x10, x14 = quarter_round(x2, x6, x10, x14)
|
||||
x3, x7, x11, x15 = quarter_round(x3, x7, x11, x15)
|
||||
|
||||
// quarterround(x, 1, 5, 9, 13)
|
||||
x1 += x5
|
||||
x13 ~= x1
|
||||
x13 = bits.rotate_left32(x13, 16)
|
||||
x9 += x13
|
||||
x5 ~= x9
|
||||
x5 = bits.rotate_left32(x5, 12)
|
||||
x1 += x5
|
||||
x13 ~= x1
|
||||
x13 = bits.rotate_left32(x13, 8)
|
||||
x9 += x13
|
||||
x5 ~= x9
|
||||
x5 = bits.rotate_left32(x5, 7)
|
||||
|
||||
// quarterround(x, 2, 6, 10, 14)
|
||||
x2 += x6
|
||||
x14 ~= x2
|
||||
x14 = bits.rotate_left32(x14, 16)
|
||||
x10 += x14
|
||||
x6 ~= x10
|
||||
x6 = bits.rotate_left32(x6, 12)
|
||||
x2 += x6
|
||||
x14 ~= x2
|
||||
x14 = bits.rotate_left32(x14, 8)
|
||||
x10 += x14
|
||||
x6 ~= x10
|
||||
x6 = bits.rotate_left32(x6, 7)
|
||||
|
||||
// quarterround(x, 3, 7, 11, 15)
|
||||
x3 += x7
|
||||
x15 ~= x3
|
||||
x15 = bits.rotate_left32(x15, 16)
|
||||
x11 += x15
|
||||
x7 ~= x11
|
||||
x7 = bits.rotate_left32(x7, 12)
|
||||
x3 += x7
|
||||
x15 ~= x3
|
||||
x15 = bits.rotate_left32(x15, 8)
|
||||
x11 += x15
|
||||
x7 ~= x11
|
||||
x7 = bits.rotate_left32(x7, 7)
|
||||
|
||||
// quarterround(x, 0, 5, 10, 15)
|
||||
x0 += x5
|
||||
x15 ~= x0
|
||||
x15 = bits.rotate_left32(x15, 16)
|
||||
x10 += x15
|
||||
x5 ~= x10
|
||||
x5 = bits.rotate_left32(x5, 12)
|
||||
x0 += x5
|
||||
x15 ~= x0
|
||||
x15 = bits.rotate_left32(x15, 8)
|
||||
x10 += x15
|
||||
x5 ~= x10
|
||||
x5 = bits.rotate_left32(x5, 7)
|
||||
|
||||
// quarterround(x, 1, 6, 11, 12)
|
||||
x1 += x6
|
||||
x12 ~= x1
|
||||
x12 = bits.rotate_left32(x12, 16)
|
||||
x11 += x12
|
||||
x6 ~= x11
|
||||
x6 = bits.rotate_left32(x6, 12)
|
||||
x1 += x6
|
||||
x12 ~= x1
|
||||
x12 = bits.rotate_left32(x12, 8)
|
||||
x11 += x12
|
||||
x6 ~= x11
|
||||
x6 = bits.rotate_left32(x6, 7)
|
||||
|
||||
// quarterround(x, 2, 7, 8, 13)
|
||||
x2 += x7
|
||||
x13 ~= x2
|
||||
x13 = bits.rotate_left32(x13, 16)
|
||||
x8 += x13
|
||||
x7 ~= x8
|
||||
x7 = bits.rotate_left32(x7, 12)
|
||||
x2 += x7
|
||||
x13 ~= x2
|
||||
x13 = bits.rotate_left32(x13, 8)
|
||||
x8 += x13
|
||||
x7 ~= x8
|
||||
x7 = bits.rotate_left32(x7, 7)
|
||||
|
||||
// quarterround(x, 3, 4, 9, 14)
|
||||
x3 += x4
|
||||
x14 ~= x3
|
||||
x14 = bits.rotate_left32(x14, 16)
|
||||
x9 += x14
|
||||
x4 ~= x9
|
||||
x4 = bits.rotate_left32(x4, 12)
|
||||
x3 += x4
|
||||
x14 ~= x3
|
||||
x14 = bits.rotate_left32(x14, 8)
|
||||
x9 += x14
|
||||
x4 ~= x9
|
||||
x4 = bits.rotate_left32(x4, 7)
|
||||
x0, x5, x10, x15 = quarter_round(x0, x5, x10, x15)
|
||||
x1, x6, x11, x12 = quarter_round(x1, x6, x11, x12)
|
||||
x2, x7, x8, x13 = quarter_round(x2, x7, x8, x13)
|
||||
x3, x4, x9, x14 = quarter_round(x3, x4, x9, x14)
|
||||
}
|
||||
|
||||
x0 += _chacha20.SIGMA_0
|
||||
@@ -236,117 +171,15 @@ hchacha20 :: proc "contextless" (dst, key, iv: []byte) {
|
||||
x15 := endian.unchecked_get_u32le(iv[12:16])
|
||||
|
||||
for i := _chacha20.ROUNDS; i > 0; i = i - 2 {
|
||||
// quarterround(x, 0, 4, 8, 12)
|
||||
x0 += x4
|
||||
x12 ~= x0
|
||||
x12 = bits.rotate_left32(x12, 16)
|
||||
x8 += x12
|
||||
x4 ~= x8
|
||||
x4 = bits.rotate_left32(x4, 12)
|
||||
x0 += x4
|
||||
x12 ~= x0
|
||||
x12 = bits.rotate_left32(x12, 8)
|
||||
x8 += x12
|
||||
x4 ~= x8
|
||||
x4 = bits.rotate_left32(x4, 7)
|
||||
x0, x4, x8, x12 = quarter_round(x0, x4, x8, x12)
|
||||
x1, x5, x9, x13 = quarter_round(x1, x5, x9, x13)
|
||||
x2, x6, x10, x14 = quarter_round(x2, x6, x10, x14)
|
||||
x3, x7, x11, x15 = quarter_round(x3, x7, x11, x15)
|
||||
|
||||
// quarterround(x, 1, 5, 9, 13)
|
||||
x1 += x5
|
||||
x13 ~= x1
|
||||
x13 = bits.rotate_left32(x13, 16)
|
||||
x9 += x13
|
||||
x5 ~= x9
|
||||
x5 = bits.rotate_left32(x5, 12)
|
||||
x1 += x5
|
||||
x13 ~= x1
|
||||
x13 = bits.rotate_left32(x13, 8)
|
||||
x9 += x13
|
||||
x5 ~= x9
|
||||
x5 = bits.rotate_left32(x5, 7)
|
||||
|
||||
// quarterround(x, 2, 6, 10, 14)
|
||||
x2 += x6
|
||||
x14 ~= x2
|
||||
x14 = bits.rotate_left32(x14, 16)
|
||||
x10 += x14
|
||||
x6 ~= x10
|
||||
x6 = bits.rotate_left32(x6, 12)
|
||||
x2 += x6
|
||||
x14 ~= x2
|
||||
x14 = bits.rotate_left32(x14, 8)
|
||||
x10 += x14
|
||||
x6 ~= x10
|
||||
x6 = bits.rotate_left32(x6, 7)
|
||||
|
||||
// quarterround(x, 3, 7, 11, 15)
|
||||
x3 += x7
|
||||
x15 ~= x3
|
||||
x15 = bits.rotate_left32(x15, 16)
|
||||
x11 += x15
|
||||
x7 ~= x11
|
||||
x7 = bits.rotate_left32(x7, 12)
|
||||
x3 += x7
|
||||
x15 ~= x3
|
||||
x15 = bits.rotate_left32(x15, 8)
|
||||
x11 += x15
|
||||
x7 ~= x11
|
||||
x7 = bits.rotate_left32(x7, 7)
|
||||
|
||||
// quarterround(x, 0, 5, 10, 15)
|
||||
x0 += x5
|
||||
x15 ~= x0
|
||||
x15 = bits.rotate_left32(x15, 16)
|
||||
x10 += x15
|
||||
x5 ~= x10
|
||||
x5 = bits.rotate_left32(x5, 12)
|
||||
x0 += x5
|
||||
x15 ~= x0
|
||||
x15 = bits.rotate_left32(x15, 8)
|
||||
x10 += x15
|
||||
x5 ~= x10
|
||||
x5 = bits.rotate_left32(x5, 7)
|
||||
|
||||
// quarterround(x, 1, 6, 11, 12)
|
||||
x1 += x6
|
||||
x12 ~= x1
|
||||
x12 = bits.rotate_left32(x12, 16)
|
||||
x11 += x12
|
||||
x6 ~= x11
|
||||
x6 = bits.rotate_left32(x6, 12)
|
||||
x1 += x6
|
||||
x12 ~= x1
|
||||
x12 = bits.rotate_left32(x12, 8)
|
||||
x11 += x12
|
||||
x6 ~= x11
|
||||
x6 = bits.rotate_left32(x6, 7)
|
||||
|
||||
// quarterround(x, 2, 7, 8, 13)
|
||||
x2 += x7
|
||||
x13 ~= x2
|
||||
x13 = bits.rotate_left32(x13, 16)
|
||||
x8 += x13
|
||||
x7 ~= x8
|
||||
x7 = bits.rotate_left32(x7, 12)
|
||||
x2 += x7
|
||||
x13 ~= x2
|
||||
x13 = bits.rotate_left32(x13, 8)
|
||||
x8 += x13
|
||||
x7 ~= x8
|
||||
x7 = bits.rotate_left32(x7, 7)
|
||||
|
||||
// quarterround(x, 3, 4, 9, 14)
|
||||
x3 += x4
|
||||
x14 ~= x3
|
||||
x14 = bits.rotate_left32(x14, 16)
|
||||
x9 += x14
|
||||
x4 ~= x9
|
||||
x4 = bits.rotate_left32(x4, 12)
|
||||
x3 += x4
|
||||
x14 ~= x3
|
||||
x14 = bits.rotate_left32(x14, 8)
|
||||
x9 += x14
|
||||
x4 ~= x9
|
||||
x4 = bits.rotate_left32(x4, 7)
|
||||
x0, x5, x10, x15 = quarter_round(x0, x5, x10, x15)
|
||||
x1, x6, x11, x12 = quarter_round(x1, x6, x11, x12)
|
||||
x2, x7, x8, x13 = quarter_round(x2, x7, x8, x13)
|
||||
x3, x4, x9, x14 = quarter_round(x3, x4, x9, x14)
|
||||
}
|
||||
|
||||
endian.unchecked_put_u32le(dst[0:4], x0)
|
||||
|
||||
@@ -215,7 +215,7 @@ _store_simd128 :: #force_inline proc "contextless" (
|
||||
intrinsics.unaligned_store((^simd.u32x4)(dst[3:]), v3)
|
||||
}
|
||||
|
||||
// is_performant returns true iff the target and current host both support
|
||||
// is_performant returns true if and only if (⟺) the target and current host both support
|
||||
// "enough" 128-bit SIMD to make this implementation performant.
|
||||
is_performant :: proc "contextless" () -> bool {
|
||||
when ODIN_ARCH == .arm64 || ODIN_ARCH == .arm32 || ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 || ODIN_ARCH == .riscv64 {
|
||||
@@ -227,12 +227,7 @@ is_performant :: proc "contextless" () -> bool {
|
||||
req_features :: info.CPU_Features{.V}
|
||||
}
|
||||
|
||||
features, ok := info.cpu.features.?
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return features >= req_features
|
||||
return info.cpu_features() >= req_features
|
||||
} else when ODIN_ARCH == .wasm64p32 || ODIN_ARCH == .wasm32 {
|
||||
return intrinsics.has_target_feature("simd128")
|
||||
} else {
|
||||
|
||||
@@ -36,17 +36,12 @@ _VEC_ZERO_ONE: simd.u64x4 : {0, 0, 1, 0}
|
||||
@(private = "file")
|
||||
_VEC_TWO: simd.u64x4 : {2, 0, 2, 0}
|
||||
|
||||
// is_performant returns true iff the target and current host both support
|
||||
// is_performant returns true if and only if (⟺) the target and current host both support
|
||||
// "enough" SIMD to make this implementation performant.
|
||||
is_performant :: proc "contextless" () -> bool {
|
||||
req_features :: info.CPU_Features{.avx, .avx2}
|
||||
|
||||
features, ok := info.cpu.features.?
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return features >= req_features
|
||||
return info.cpu_features() >= req_features
|
||||
}
|
||||
|
||||
@(private = "file")
|
||||
|
||||
@@ -13,7 +13,8 @@ See:
|
||||
|
||||
import "core:crypto"
|
||||
import field "core:crypto/_fiat/field_curve25519"
|
||||
import "core:mem"
|
||||
|
||||
zero_explicit :: crypto.zero_explicit
|
||||
|
||||
// Group_Element is an edwards25519 group element, as extended homogenous
|
||||
// coordinates, which represents the affine point `(x, y)` as `(X, Y, Z, T)`,
|
||||
@@ -96,7 +97,7 @@ Group_Element :: struct {
|
||||
}
|
||||
|
||||
ge_clear :: proc "contextless" (ge: ^Group_Element) {
|
||||
mem.zero_explicit(ge, size_of(Group_Element))
|
||||
zero_explicit(ge, size_of(Group_Element))
|
||||
}
|
||||
|
||||
ge_set :: proc "contextless" (ge, a: ^Group_Element) {
|
||||
@@ -159,7 +160,7 @@ ge_set_bytes :: proc "contextless" (ge: ^Group_Element, b: []byte) -> bool {
|
||||
|
||||
ge_cond_assign(ge, &tmp, is_canonical)
|
||||
|
||||
mem.zero_explicit(&buf, size_of(buf))
|
||||
zero_explicit(&buf, size_of(buf))
|
||||
|
||||
return is_canonical == 1
|
||||
}
|
||||
@@ -195,7 +196,6 @@ ge_generator :: proc "contextless" (ge: ^Group_Element) {
|
||||
ge_set(ge, &GE_BASEPOINT)
|
||||
}
|
||||
|
||||
@(private)
|
||||
Addend_Group_Element :: struct {
|
||||
y2_minus_x2: field.Loose_Field_Element, // t1
|
||||
y2_plus_x2: field.Loose_Field_Element, // t3
|
||||
@@ -203,7 +203,6 @@ Addend_Group_Element :: struct {
|
||||
two_times_z2: field.Loose_Field_Element, // t5
|
||||
}
|
||||
|
||||
@(private)
|
||||
ge_addend_set :: proc "contextless" (ge_a: ^Addend_Group_Element, ge: ^Group_Element) {
|
||||
field.fe_sub(&ge_a.y2_minus_x2, &ge.y, &ge.x)
|
||||
field.fe_add(&ge_a.y2_plus_x2, &ge.y, &ge.x)
|
||||
@@ -233,8 +232,8 @@ ge_add :: proc "contextless" (ge, a, b: ^Group_Element) {
|
||||
scratch: Add_Scratch = ---
|
||||
ge_add_addend(ge, a, &b_, &scratch)
|
||||
|
||||
mem.zero_explicit(&b_, size_of(Addend_Group_Element))
|
||||
mem.zero_explicit(&scratch, size_of(Add_Scratch))
|
||||
zero_explicit(&b_, size_of(Addend_Group_Element))
|
||||
zero_explicit(&scratch, size_of(Add_Scratch))
|
||||
}
|
||||
|
||||
@(private)
|
||||
@@ -354,7 +353,7 @@ ge_double :: proc "contextless" (ge, a: ^Group_Element, scratch: ^Double_Scratch
|
||||
field.fe_carry_mul(&ge.z, F, G_)
|
||||
|
||||
if sanitize {
|
||||
mem.zero_explicit(scratch, size_of(Double_Scratch))
|
||||
zero_explicit(scratch, size_of(Double_Scratch))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,6 +419,6 @@ ge_in_prime_order_subgroup_vartime :: proc "contextless" (ge: ^Group_Element) ->
|
||||
// that is a ~50% speedup, and a lot of added complexity for something
|
||||
// that is better solved by "just use ristretto255".
|
||||
tmp: Group_Element = ---
|
||||
_ge_scalarmult(&tmp, ge, &SC_ELL, true)
|
||||
ge_scalarmult_raw(&tmp, ge, &SC_ELL, true)
|
||||
return ge_equal(&tmp, &GE_IDENTITY) == 1
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package _edwards25519
|
||||
|
||||
import field "core:crypto/_fiat/field_scalar25519"
|
||||
import "core:mem"
|
||||
|
||||
Scalar :: field.Montgomery_Domain_Field_Element
|
||||
|
||||
@@ -19,7 +18,7 @@ sc_set_u64 :: proc "contextless" (sc: ^Scalar, i: u64) {
|
||||
tmp := field.Non_Montgomery_Domain_Field_Element{i, 0, 0, 0}
|
||||
field.fe_to_montgomery(sc, &tmp)
|
||||
|
||||
mem.zero_explicit(&tmp, size_of(tmp))
|
||||
zero_explicit(&tmp, size_of(tmp))
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
@@ -36,7 +35,7 @@ sc_set_bytes_rfc8032 :: proc "contextless" (sc: ^Scalar, b: []byte) {
|
||||
}
|
||||
|
||||
sc_clear :: proc "contextless" (sc: ^Scalar) {
|
||||
mem.zero_explicit(sc, size_of(Scalar))
|
||||
zero_explicit(sc, size_of(Scalar))
|
||||
}
|
||||
|
||||
sc_set :: field.fe_set
|
||||
|
||||
@@ -1,130 +1,23 @@
|
||||
package _edwards25519
|
||||
|
||||
import "core:crypto"
|
||||
import field "core:crypto/_fiat/field_scalar25519"
|
||||
import "core:math/bits"
|
||||
import "core:mem"
|
||||
|
||||
// GE_BASEPOINT_TABLE is 1 * G, ... 15 * G, in precomputed format.
|
||||
//
|
||||
// Note: When generating, the values were reduced to Tight_Field_Element
|
||||
// ranges, even though that is not required.
|
||||
@(private)
|
||||
GE_BASEPOINT_TABLE := Multiply_Table {
|
||||
{
|
||||
{62697248952638, 204681361388450, 631292143396476, 338455783676468, 1213667448819585},
|
||||
{1288382639258501, 245678601348599, 269427782077623, 1462984067271730, 137412439391563},
|
||||
{301289933810280, 1259582250014073, 1422107436869536, 796239922652654, 1953934009299142},
|
||||
{2, 0, 0, 0, 0},
|
||||
},
|
||||
{
|
||||
{1519297034332653, 1098796920435767, 1823476547744119, 808144629470969, 2110930855619772},
|
||||
{338005982828284, 1667856962156925, 100399270107451, 1604566703601691, 1950338038771369},
|
||||
{1920505767731247, 1443759578976892, 1659852098357048, 1484431291070208, 275018744912646},
|
||||
{763163817085987, 2195095074806923, 2167883174351839, 1868059999999762, 911071066608705},
|
||||
},
|
||||
{
|
||||
{960627541894068, 1314966688943942, 1126875971034044, 2059608312958945, 605975666152586},
|
||||
{1714478358025626, 2209607666607510, 1600912834284834, 496072478982142, 481970031861896},
|
||||
{851735079403194, 1088965826757164, 141569479297499, 602804610059257, 2004026468601520},
|
||||
{197585529552380, 324719066578543, 564481854250498, 1173818332764578, 35452976395676},
|
||||
},
|
||||
{
|
||||
{1152980410747203, 2196804280851952, 25745194962557, 1915167295473129, 1266299690309224},
|
||||
{809905889679060, 979732230071345, 1509972345538142, 188492426534402, 818965583123815},
|
||||
{997685409185036, 1451818320876327, 2126681166774509, 2000509606057528, 235432372486854},
|
||||
{887734189279642, 1460338685162044, 877378220074262, 102436391401299, 153369156847490},
|
||||
},
|
||||
{
|
||||
{2056621900836770, 1821657694132497, 1627986892909426, 1163363868678833, 1108873376459226},
|
||||
{1187697490593623, 1066539945237335, 885654531892000, 1357534489491782, 359370291392448},
|
||||
{1509033452137525, 1305318174298508, 613642471748944, 1987256352550234, 1044283663101541},
|
||||
{220105720697037, 387661783287620, 328296827867762, 360035589590664, 795213236824054},
|
||||
},
|
||||
{
|
||||
{1820794733038396, 1612235121681074, 757405923441402, 1094031020892801, 231025333128907},
|
||||
{1639067873254194, 1484176557946322, 300800382144789, 1329915446659183, 1211704578730455},
|
||||
{641900794791527, 1711751746971612, 179044712319955, 576455585963824, 1852617592509865},
|
||||
{743549047192397, 685091042550147, 1952415336873496, 1965124675654685, 513364998442917},
|
||||
},
|
||||
{
|
||||
{1004557076870448, 1762911374844520, 1330807633622723, 384072910939787, 953849032243810},
|
||||
{2178275058221458, 257933183722891, 376684351537894, 2010189102001786, 1981824297484148},
|
||||
{1332915663881114, 1286540505502549, 1741691283561518, 977214932156314, 1764059494778091},
|
||||
{429702949064027, 1368332611650677, 2019867176450999, 2212258376161746, 526160996742554},
|
||||
},
|
||||
{
|
||||
{2098932988258576, 2203688382075948, 2120400160059479, 1748488020948146, 1203264167282624},
|
||||
{677131386735829, 1850249298025188, 672782146532031, 2144145693078904, 2088656272813787},
|
||||
{1065622343976192, 1573853211848116, 223560413590068, 333846833073379, 27832122205830},
|
||||
{1781008836504573, 917619542051793, 544322748939913, 882577394308384, 1720521246471195},
|
||||
},
|
||||
{
|
||||
{660120928379860, 2081944024858618, 1878411111349191, 424587356517195, 2111317439894005},
|
||||
{1834193977811532, 1864164086863319, 797334633289424, 150410812403062, 2085177078466389},
|
||||
{1438117271371866, 783915531014482, 388731514584658, 292113935417795, 1945855002546714},
|
||||
{1678140823166658, 679103239148744, 614102761596238, 1052962498997885, 1863983323810390},
|
||||
},
|
||||
{
|
||||
{1690309392496233, 1116333140326275, 1377242323631039, 717196888780674, 82724646713353},
|
||||
{1722370213432106, 74265192976253, 264239578448472, 1714909985012994, 2216984958602173},
|
||||
{2010482366920922, 1294036471886319, 566466395005815, 1631955803657320, 1751698647538458},
|
||||
{1073230604155753, 1159087041338551, 1664057985455483, 127472702826203, 1339591128522371},
|
||||
},
|
||||
{
|
||||
{478053307175577, 2179515791720985, 21146535423512, 1831683844029536, 462805561553981},
|
||||
{1945267486565588, 1298536818409655, 2214511796262989, 1904981051429012, 252904800782086},
|
||||
{268945954671210, 222740425595395, 1208025911856230, 1080418823003555, 75929831922483},
|
||||
{1884784014268948, 643868448202966, 978736549726821, 46385971089796, 1296884812292320},
|
||||
},
|
||||
{
|
||||
{1861159462859103, 7077532564710, 963010365896826, 1938780006785270, 766241051941647},
|
||||
{1778966986051906, 1713995999765361, 1394565822271816, 1366699246468722, 1213407027149475},
|
||||
{1978989286560907, 2135084162045594, 1951565508865477, 671788336314416, 293123929458176},
|
||||
{902608944504080, 2167765718046481, 1285718473078022, 1222562171329269, 492109027844479},
|
||||
},
|
||||
{
|
||||
{1820807832746213, 1029220580458586, 1101997555432203, 1039081975563572, 202477981158221},
|
||||
{1866134980680205, 2222325502763386, 1830284629571201, 1046966214478970, 418381946936795},
|
||||
{1783460633291322, 1719505443254998, 1810489639976220, 877049370713018, 2187801198742619},
|
||||
{197118243000763, 305493867565736, 518814410156522, 1656246186645170, 901894734874934},
|
||||
},
|
||||
{
|
||||
{225454942125915, 478410476654509, 600524586037746, 643450007230715, 1018615928259319},
|
||||
{1733330584845708, 881092297970296, 507039890129464, 496397090721598, 2230888519577628},
|
||||
{690155664737246, 1010454785646677, 753170144375012, 1651277613844874, 1622648796364156},
|
||||
{1321310321891618, 1089655277873603, 235891750867089, 815878279563688, 1709264240047556},
|
||||
},
|
||||
{
|
||||
{805027036551342, 1387174275567452, 1156538511461704, 1465897486692171, 1208567094120903},
|
||||
{2228417017817483, 202885584970535, 2182114782271881, 2077405042592934, 1029684358182774},
|
||||
{460447547653983, 627817697755692, 524899434670834, 1228019344939427, 740684787777653},
|
||||
{849757462467675, 447476306919899, 422618957298818, 302134659227815, 675831828440895},
|
||||
},
|
||||
}
|
||||
import subtle "core:crypto/_subtle"
|
||||
|
||||
ge_scalarmult :: proc "contextless" (ge, p: ^Group_Element, sc: ^Scalar) {
|
||||
tmp: field.Non_Montgomery_Domain_Field_Element
|
||||
field.fe_from_montgomery(&tmp, sc)
|
||||
|
||||
_ge_scalarmult(ge, p, &tmp)
|
||||
ge_scalarmult_raw(ge, p, &tmp)
|
||||
|
||||
mem.zero_explicit(&tmp, size_of(tmp))
|
||||
}
|
||||
|
||||
ge_scalarmult_basepoint :: proc "contextless" (ge: ^Group_Element, sc: ^Scalar) {
|
||||
// Something like the comb method from "Fast and compact elliptic-curve
|
||||
// cryptography" Section 3.3, would be more performant, but more
|
||||
// complex.
|
||||
//
|
||||
// - https://eprint.iacr.org/2012/309
|
||||
ge_scalarmult(ge, &GE_BASEPOINT, sc)
|
||||
zero_explicit(&tmp, size_of(tmp))
|
||||
}
|
||||
|
||||
ge_scalarmult_vartime :: proc "contextless" (ge, p: ^Group_Element, sc: ^Scalar) {
|
||||
tmp: field.Non_Montgomery_Domain_Field_Element
|
||||
field.fe_from_montgomery(&tmp, sc)
|
||||
|
||||
_ge_scalarmult(ge, p, &tmp, true)
|
||||
ge_scalarmult_raw(ge, p, &tmp, true)
|
||||
}
|
||||
|
||||
ge_double_scalarmult_basepoint_vartime :: proc "contextless" (
|
||||
@@ -147,6 +40,12 @@ ge_double_scalarmult_basepoint_vartime :: proc "contextless" (
|
||||
|
||||
A_tbl: Multiply_Table = ---
|
||||
mul_tbl_set(&A_tbl, A, &tmp_add)
|
||||
when crypto.COMPACT_IMPLS == true {
|
||||
G_tbl: Multiply_Table = ---
|
||||
mul_tbl_set(&G_tbl, &GE_BASEPOINT, &tmp_add)
|
||||
} else {
|
||||
tmp_bp_addend: Basepoint_Addend_Group_Element = ---
|
||||
}
|
||||
|
||||
sc_a, sc_b: field.Non_Montgomery_Domain_Field_Element
|
||||
field.fe_from_montgomery(&sc_a, a)
|
||||
@@ -170,21 +69,28 @@ ge_double_scalarmult_basepoint_vartime :: proc "contextless" (
|
||||
ge_double(&tmp, &tmp, &tmp_dbl)
|
||||
}
|
||||
mul_tbl_add(&tmp, &A_tbl, hi_a, &tmp_add, &tmp_addend, true)
|
||||
mul_tbl_add(&tmp, &GE_BASEPOINT_TABLE, hi_b, &tmp_add, &tmp_addend, true)
|
||||
when crypto.COMPACT_IMPLS == true {
|
||||
mul_tbl_add(&tmp, &G_tbl, hi_b, &tmp_add, &tmp_addend, true)
|
||||
} else {
|
||||
mul_bp_tbl_add(&tmp, GE_BASEPOINT_TABLE, hi_b, &tmp_add, &tmp_bp_addend, true)
|
||||
}
|
||||
|
||||
ge_double(&tmp, &tmp, &tmp_dbl)
|
||||
ge_double(&tmp, &tmp, &tmp_dbl)
|
||||
ge_double(&tmp, &tmp, &tmp_dbl)
|
||||
ge_double(&tmp, &tmp, &tmp_dbl)
|
||||
mul_tbl_add(&tmp, &A_tbl, lo_a, &tmp_add, &tmp_addend, true)
|
||||
mul_tbl_add(&tmp, &GE_BASEPOINT_TABLE, lo_b, &tmp_add, &tmp_addend, true)
|
||||
when crypto.COMPACT_IMPLS == true {
|
||||
mul_tbl_add(&tmp, &G_tbl, lo_b, &tmp_add, &tmp_addend, true)
|
||||
} else {
|
||||
mul_bp_tbl_add(&tmp, GE_BASEPOINT_TABLE, lo_b, &tmp_add, &tmp_bp_addend, true)
|
||||
}
|
||||
}
|
||||
|
||||
ge_set(ge, &tmp)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_ge_scalarmult :: proc "contextless" (
|
||||
ge_scalarmult_raw :: proc "contextless" (
|
||||
ge, p: ^Group_Element,
|
||||
sc: ^field.Non_Montgomery_Domain_Field_Element,
|
||||
unsafe_is_vartime := false,
|
||||
@@ -227,9 +133,9 @@ _ge_scalarmult :: proc "contextless" (
|
||||
|
||||
if !unsafe_is_vartime {
|
||||
ge_clear(&tmp)
|
||||
mem.zero_explicit(&tmp_add, size_of(Add_Scratch))
|
||||
mem.zero_explicit(&tmp_addend, size_of(Addend_Group_Element))
|
||||
mem.zero_explicit(&tmp_dbl, size_of(Double_Scratch))
|
||||
zero_explicit(&tmp_add, size_of(Add_Scratch))
|
||||
zero_explicit(&tmp_addend, size_of(Addend_Group_Element))
|
||||
zero_explicit(&tmp_dbl, size_of(Double_Scratch))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,8 +187,8 @@ mul_tbl_add :: proc "contextless" (
|
||||
{2, 0, 0, 0, 0}, // z * 2
|
||||
}
|
||||
for i := u64(1); i < 16; i = i + 1 {
|
||||
_, ctrl := bits.sub_u64(0, (i ~ idx), 0)
|
||||
ge_addend_conditional_assign(tmp_addend, &tbl[i - 1], int(~ctrl) & 1)
|
||||
ctrl := subtle.eq(i, idx)
|
||||
ge_addend_conditional_assign(tmp_addend, &tbl[i - 1], int(ctrl))
|
||||
}
|
||||
ge_add_addend(ge, ge, tmp_addend, tmp_add)
|
||||
}
|
||||
|
||||
146
core/crypto/_edwards25519/edwards25519_scalar_mul_base.odin
Normal file
146
core/crypto/_edwards25519/edwards25519_scalar_mul_base.odin
Normal file
@@ -0,0 +1,146 @@
|
||||
package _edwards25519
|
||||
|
||||
import "core:crypto"
|
||||
import field "core:crypto/_fiat/field_curve25519"
|
||||
import scalar "core:crypto/_fiat/field_scalar25519"
|
||||
import subtle "core:crypto/_subtle"
|
||||
|
||||
ge_scalarmult_basepoint :: proc "contextless" (ge: ^Group_Element, sc: ^Scalar) {
|
||||
when crypto.COMPACT_IMPLS == true {
|
||||
ge_scalarmult(ge, &GE_BASEPOINT, sc)
|
||||
} else {
|
||||
tmp_sc: scalar.Non_Montgomery_Domain_Field_Element
|
||||
scalar.fe_from_montgomery(&tmp_sc, sc)
|
||||
|
||||
tmp_add: Add_Scratch = ---
|
||||
tmp_addend: Basepoint_Addend_Group_Element = ---
|
||||
|
||||
ge_identity(ge)
|
||||
for i in 0..<32 {
|
||||
limb := i / 8
|
||||
shift := uint(i & 7) * 8
|
||||
limb_byte := tmp_sc[limb] >> shift
|
||||
|
||||
hi, lo := (limb_byte >> 4) & 0x0f, limb_byte & 0x0f
|
||||
mul_bp_tbl_add(ge, &Gen_Multiply_Table_edwards25519_lo[i], lo, &tmp_add, &tmp_addend, false)
|
||||
mul_bp_tbl_add(ge, &Gen_Multiply_Table_edwards25519_hi[i], hi, &tmp_add, &tmp_addend, false)
|
||||
}
|
||||
|
||||
zero_explicit(&tmp_sc, size_of(tmp_sc))
|
||||
zero_explicit(&tmp_add, size_of(Add_Scratch))
|
||||
zero_explicit(&tmp_addend, size_of(Basepoint_Addend_Group_Element))
|
||||
}
|
||||
}
|
||||
|
||||
when crypto.COMPACT_IMPLS == false {
|
||||
@(private="file",rodata)
|
||||
TWO_TIMES_Z2 := field.Loose_Field_Element{2, 0, 0, 0, 0}
|
||||
|
||||
@(private)
|
||||
Basepoint_Addend_Group_Element :: struct {
|
||||
y2_minus_x2: field.Loose_Field_Element, // t1
|
||||
y2_plus_x2: field.Loose_Field_Element, // t3
|
||||
k_times_t2: field.Tight_Field_Element, // t4
|
||||
}
|
||||
|
||||
@(private)
|
||||
Basepoint_Multiply_Table :: [15]Basepoint_Addend_Group_Element
|
||||
|
||||
@(private)
|
||||
ge_bp_addend_conditional_assign :: proc "contextless" (ge_a, a: ^Basepoint_Addend_Group_Element, ctrl: int) {
|
||||
field.fe_cond_select(&ge_a.y2_minus_x2, &ge_a.y2_minus_x2, &a.y2_minus_x2, ctrl)
|
||||
field.fe_cond_select(&ge_a.y2_plus_x2, &ge_a.y2_plus_x2, &a.y2_plus_x2, ctrl)
|
||||
field.fe_cond_select(&ge_a.k_times_t2, &ge_a.k_times_t2, &a.k_times_t2, ctrl)
|
||||
}
|
||||
|
||||
@(private)
|
||||
ge_add_bp_addend :: proc "contextless" (
|
||||
ge, a: ^Group_Element,
|
||||
b: ^Basepoint_Addend_Group_Element,
|
||||
scratch: ^Add_Scratch,
|
||||
) {
|
||||
// https://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-3
|
||||
// Assumptions: k=2*d, z = 1 (precomputation ftw)
|
||||
//
|
||||
// t0 = Y1-X1
|
||||
// t1 = Y2-X2
|
||||
// A = t0*t1
|
||||
// t2 = Y1+X1
|
||||
// t3 = Y2+X2
|
||||
// B = t2*t3
|
||||
// t4 = k*T2
|
||||
// C = T1*t4
|
||||
// t5 = 2*Z2
|
||||
// D = Z1*t5
|
||||
// E = B-A
|
||||
// F = D-C
|
||||
// G = D+C
|
||||
// H = B+A
|
||||
// X3 = E*F
|
||||
// Y3 = G*H
|
||||
// T3 = E*H
|
||||
// Z3 = F*G
|
||||
//
|
||||
// In order to make the scalar multiply faster, the addend is provided
|
||||
// as a `Addend_Group_Element` with t1, t3, t4, and t5 precomputed, as
|
||||
// it is trivially obvious that those are the only values used by the
|
||||
// formula that are directly dependent on `b`, and are only dependent
|
||||
// on `b` and constants. This saves 1 sub, 2 adds, and 1 multiply,
|
||||
// each time the intermediate representation can be reused.
|
||||
|
||||
A, B, C, D := &scratch.A, &scratch.B, &scratch.C, &scratch.D
|
||||
E, F, G, H := &scratch.E, &scratch.F, &scratch.G, &scratch.H
|
||||
t0, t2 := &scratch.t0, &scratch.t2
|
||||
|
||||
field.fe_sub(t0, &a.y, &a.x)
|
||||
t1 := &b.y2_minus_x2
|
||||
field.fe_carry_mul(A, t0, t1)
|
||||
field.fe_add(t2, &a.y, &a.x)
|
||||
t3 := &b.y2_plus_x2
|
||||
field.fe_carry_mul(B, t2, t3)
|
||||
t4 := &b.k_times_t2
|
||||
field.fe_carry_mul(C, field.fe_relax_cast(&a.t), field.fe_relax_cast(t4))
|
||||
field.fe_carry_mul(D, field.fe_relax_cast(&a.z), &TWO_TIMES_Z2)
|
||||
field.fe_sub(E, B, A)
|
||||
field.fe_sub(F, D, C)
|
||||
field.fe_add(G, D, C)
|
||||
field.fe_add(H, B, A)
|
||||
field.fe_carry_mul(&ge.x, E, F)
|
||||
field.fe_carry_mul(&ge.y, G, H)
|
||||
field.fe_carry_mul(&ge.t, E, H)
|
||||
field.fe_carry_mul(&ge.z, F, G)
|
||||
}
|
||||
|
||||
@(private)
|
||||
mul_bp_tbl_add :: proc "contextless" (
|
||||
ge: ^Group_Element,
|
||||
tbl: ^Basepoint_Multiply_Table,
|
||||
idx: u64,
|
||||
tmp_add: ^Add_Scratch,
|
||||
tmp_addend: ^Basepoint_Addend_Group_Element,
|
||||
unsafe_is_vartime: bool,
|
||||
) {
|
||||
// Variable time lookup, with the addition omitted entirely if idx == 0.
|
||||
if unsafe_is_vartime {
|
||||
// Skip adding the point at infinity.
|
||||
if idx != 0 {
|
||||
ge_add_bp_addend(ge, ge, &tbl[idx-1], tmp_add)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Constant time lookup.
|
||||
tmp_addend^ = {
|
||||
// Point at infinity (0, 1, 1, 0) in precomputed form, note
|
||||
// that the precomputed tables rescale so that `Z = 1`.
|
||||
{1, 0, 0, 0, 0}, // y - x
|
||||
{1, 0, 0, 0, 0}, // y + x
|
||||
{0, 0, 0, 0, 0}, // t * 2d
|
||||
}
|
||||
for i := u64(1); i < 16; i = i + 1 {
|
||||
ctrl := subtle.eq(i, idx)
|
||||
ge_bp_addend_conditional_assign(tmp_addend, &tbl[i - 1], int(ctrl))
|
||||
}
|
||||
ge_add_bp_addend(ge, ge, tmp_addend, tmp_add)
|
||||
}
|
||||
}
|
||||
4947
core/crypto/_edwards25519/edwards25519_table.odin
Normal file
4947
core/crypto/_edwards25519/edwards25519_table.odin
Normal file
File diff suppressed because it is too large
Load Diff
138
core/crypto/_edwards25519/tools/edwards_gen_tables.odin
Normal file
138
core/crypto/_edwards25519/tools/edwards_gen_tables.odin
Normal file
@@ -0,0 +1,138 @@
|
||||
package edwards_tools
|
||||
|
||||
import ed "core:crypto/_edwards25519"
|
||||
import field "core:crypto/_fiat/field_curve25519"
|
||||
import scalar "core:crypto/_fiat/field_scalar25519"
|
||||
import "core:encoding/endian"
|
||||
import "core:fmt"
|
||||
import path "core:path/filepath"
|
||||
import "core:os"
|
||||
import "core:strings"
|
||||
|
||||
// Yes this leaks memory, fite me IRL.
|
||||
|
||||
GENERATED :: `/*
|
||||
------ GENERATED ------ DO NOT EDIT ------ GENERATED ------ DO NOT EDIT ------ GENERATED ------
|
||||
*/`
|
||||
|
||||
@(private, rodata)
|
||||
FE_D2 := field.Tight_Field_Element {
|
||||
1859910466990425,
|
||||
932731440258426,
|
||||
1072319116312658,
|
||||
1815898335770999,
|
||||
633789495995903,
|
||||
}
|
||||
|
||||
main :: proc() {
|
||||
Basepoint_Addend_Group_Element :: struct {
|
||||
y2_minus_x2: field.Loose_Field_Element, // t1
|
||||
y2_plus_x2: field.Loose_Field_Element, // t3
|
||||
k_times_t2: field.Tight_Field_Element, // t4
|
||||
}
|
||||
Basepoint_Multiply_Table :: [15]Basepoint_Addend_Group_Element
|
||||
|
||||
ge_bp_addend_set := proc(ge_a: ^Basepoint_Addend_Group_Element, ge: ^ed.Group_Element) {
|
||||
// We rescale so Z == 1, so T = X * Y
|
||||
x_, y_, z_inv: field.Tight_Field_Element
|
||||
field.fe_carry_inv(&z_inv, field.fe_relax_cast(&ge.z))
|
||||
field.fe_carry_mul(&x_, field.fe_relax_cast(&ge.x), field.fe_relax_cast(&z_inv))
|
||||
field.fe_carry_mul(&y_, field.fe_relax_cast(&ge.y), field.fe_relax_cast(&z_inv))
|
||||
|
||||
field.fe_sub(&ge_a.y2_minus_x2, &y_, &x_)
|
||||
field.fe_add(&ge_a.y2_plus_x2, &y_, &x_)
|
||||
field.fe_carry_mul(&ge_a.k_times_t2, field.fe_relax_cast(&x_), field.fe_relax_cast(&y_))
|
||||
field.fe_carry_mul(&ge_a.k_times_t2, field.fe_relax_cast(&ge_a.k_times_t2), field.fe_relax_cast(&FE_D2))
|
||||
}
|
||||
|
||||
Multiply_Table_hi: [32]Basepoint_Multiply_Table
|
||||
Multiply_Table_lo: [32]Basepoint_Multiply_Table
|
||||
|
||||
sc_set_unchecked := proc(sc: ^scalar.Non_Montgomery_Domain_Field_Element, b: []byte) {
|
||||
sc[0] = endian.unchecked_get_u64le(b[0:])
|
||||
sc[1] = endian.unchecked_get_u64le(b[8:])
|
||||
sc[2] = endian.unchecked_get_u64le(b[16:])
|
||||
sc[3] = endian.unchecked_get_u64le(b[24:])
|
||||
}
|
||||
|
||||
g, p: ed.Group_Element
|
||||
ed.ge_generator(&g)
|
||||
|
||||
sc: scalar.Non_Montgomery_Domain_Field_Element
|
||||
|
||||
// Precompute ([1,15] << n) * G multiples of G, LSB->MSB
|
||||
for i in 0..<32 {
|
||||
b: [32]byte
|
||||
for j in 1..<16 {
|
||||
b[i] = u8(j)
|
||||
sc_set_unchecked(&sc, b[:])
|
||||
ed.ge_scalarmult_raw(&p, &g, &sc, true)
|
||||
ge_bp_addend_set(&Multiply_Table_lo[i][j-1], &p)
|
||||
|
||||
b[i] = u8(j) << 4
|
||||
sc_set_unchecked(&sc, b[:])
|
||||
ed.ge_scalarmult_raw(&p, &g, &sc, true)
|
||||
ge_bp_addend_set(&Multiply_Table_hi[i][j-1], &p)
|
||||
|
||||
b[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
fn, err := path.join({ODIN_ROOT, "core", "crypto", "_edwards25519", "edwards25519_table.odin"}, context.allocator)
|
||||
if err != .None {
|
||||
fmt.eprintfln("Join path error for edwards25519_table.odin: %v", err);
|
||||
os.exit(1);
|
||||
}
|
||||
bld: strings.Builder
|
||||
w := strings.to_writer(&bld)
|
||||
|
||||
fmt.wprintln(w, "package _edwards25519")
|
||||
fmt.wprintln(w, "")
|
||||
fmt.wprintln(w, GENERATED)
|
||||
fmt.wprintln(w, "")
|
||||
fmt.wprintln(w, "import \"core:crypto\"")
|
||||
fmt.wprintln(w, "")
|
||||
fmt.wprintln(w, "when crypto.COMPACT_IMPLS == false {")
|
||||
|
||||
fmt.wprintln(w, "\t@(private,rodata)")
|
||||
fmt.wprintln(w, "\tGen_Multiply_Table_edwards25519_lo := [32]Basepoint_Multiply_Table {")
|
||||
for &v in Multiply_Table_lo {
|
||||
fmt.wprintln(w, "\t\t{")
|
||||
for &ap in v {
|
||||
fmt.wprintln(w, "\t\t\t{")
|
||||
|
||||
t1, t3, t4 := &ap.y2_minus_x2, &ap.y2_plus_x2, &ap.k_times_t2
|
||||
fmt.wprintf(w, "\t\t\t\t{{%d, %d, %d, %d, %d},\n", t1[0], t1[1], t1[2], t1[3], t1[4])
|
||||
fmt.wprintf(w, "\t\t\t\t{{%d, %d, %d, %d, %d},\n", t3[0], t3[1], t3[2], t3[3], t3[4])
|
||||
fmt.wprintf(w, "\t\t\t\t{{%d, %d, %d, %d, %d},\n", t4[0], t4[1], t4[2], t4[3], t4[4])
|
||||
|
||||
fmt.wprintln(w, "\t\t\t},")
|
||||
}
|
||||
fmt.wprintln(w, "\t\t},")
|
||||
}
|
||||
fmt.wprintln(w, "\t}\n")
|
||||
|
||||
fmt.wprintln(w, "\t@(private,rodata)")
|
||||
fmt.wprintln(w, "\tGen_Multiply_Table_edwards25519_hi := [32]Basepoint_Multiply_Table {")
|
||||
for &v in Multiply_Table_hi {
|
||||
fmt.wprintln(w, "\t\t{")
|
||||
for &ap in v {
|
||||
fmt.wprintln(w, "\t\t\t{")
|
||||
|
||||
t1, t3, t4 := &ap.y2_minus_x2, &ap.y2_plus_x2, &ap.k_times_t2
|
||||
fmt.wprintf(w, "\t\t\t\t{{%d, %d, %d, %d, %d},\n", t1[0], t1[1], t1[2], t1[3], t1[4])
|
||||
fmt.wprintf(w, "\t\t\t\t{{%d, %d, %d, %d, %d},\n", t3[0], t3[1], t3[2], t3[3], t3[4])
|
||||
fmt.wprintf(w, "\t\t\t\t{{%d, %d, %d, %d, %d},\n", t4[0], t4[1], t4[2], t4[3], t4[4])
|
||||
|
||||
fmt.wprintln(w, "\t\t\t},")
|
||||
}
|
||||
fmt.wprintln(w, "\t\t},")
|
||||
}
|
||||
fmt.wprintln(w, "\t}\n")
|
||||
|
||||
fmt.wprintln(w, "\tGE_BASEPOINT_TABLE := &Gen_Multiply_Table_edwards25519_lo[0]")
|
||||
|
||||
fmt.wprintln(w, "}")
|
||||
|
||||
_ = os.write_entire_file(fn, transmute([]byte)(strings.to_string(bld)))
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
package field_curve25519
|
||||
|
||||
import "core:crypto"
|
||||
import "core:mem"
|
||||
|
||||
zero_explicit :: crypto.zero_explicit
|
||||
|
||||
fe_relax_cast :: #force_inline proc "contextless" (
|
||||
arg1: ^Tight_Field_Element,
|
||||
@@ -18,7 +19,7 @@ fe_tighten_cast :: #force_inline proc "contextless" (
|
||||
fe_clear :: proc "contextless" (
|
||||
arg1: $T,
|
||||
) where T == ^Tight_Field_Element || T == ^Loose_Field_Element {
|
||||
mem.zero_explicit(arg1, size_of(arg1^))
|
||||
zero_explicit(arg1, size_of(arg1^))
|
||||
}
|
||||
|
||||
fe_clear_vec :: proc "contextless" (
|
||||
@@ -38,7 +39,7 @@ fe_from_bytes :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^[32]byte
|
||||
|
||||
_fe_from_bytes(out1, &tmp1)
|
||||
|
||||
mem.zero_explicit(&tmp1, size_of(tmp1))
|
||||
zero_explicit(&tmp1, size_of(tmp1))
|
||||
}
|
||||
|
||||
fe_is_negative :: proc "contextless" (arg1: ^Tight_Field_Element) -> int {
|
||||
@@ -47,7 +48,7 @@ fe_is_negative :: proc "contextless" (arg1: ^Tight_Field_Element) -> int {
|
||||
fe_to_bytes(&tmp1, arg1)
|
||||
ret := tmp1[0] & 1
|
||||
|
||||
mem.zero_explicit(&tmp1, size_of(tmp1))
|
||||
zero_explicit(&tmp1, size_of(tmp1))
|
||||
|
||||
return int(ret)
|
||||
}
|
||||
@@ -59,8 +60,8 @@ fe_equal :: proc "contextless" (arg1, arg2: ^Tight_Field_Element) -> int {
|
||||
fe_to_bytes(&tmp2, arg2)
|
||||
ret := crypto.compare_constant_time(tmp1[:], tmp2[:])
|
||||
|
||||
mem.zero_explicit(&tmp1, size_of(tmp1))
|
||||
mem.zero_explicit(&tmp2, size_of(tmp2))
|
||||
zero_explicit(&tmp1, size_of(tmp1))
|
||||
zero_explicit(&tmp2, size_of(tmp2))
|
||||
|
||||
return ret
|
||||
}
|
||||
@@ -72,7 +73,7 @@ fe_equal_bytes :: proc "contextless" (arg1: ^Tight_Field_Element, arg2: ^[32]byt
|
||||
|
||||
ret := crypto.compare_constant_time(tmp1[:], arg2[:])
|
||||
|
||||
mem.zero_explicit(&tmp1, size_of(tmp1))
|
||||
zero_explicit(&tmp1, size_of(tmp1))
|
||||
|
||||
return ret
|
||||
}
|
||||
@@ -175,7 +176,7 @@ fe_carry_sqrt_ratio_m1 :: proc "contextless" (
|
||||
fe_carry_abs(out1, r)
|
||||
|
||||
fe_clear_vec([]^Tight_Field_Element{&w, &tmp1, &tmp2, &tmp3})
|
||||
mem.zero_explicit(&b, size_of(b))
|
||||
zero_explicit(&b, size_of(b))
|
||||
|
||||
return correct_sign_sqrt | flipped_sign_sqrt
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package field_curve448
|
||||
|
||||
import "core:mem"
|
||||
import "core:crypto"
|
||||
|
||||
fe_relax_cast :: #force_inline proc "contextless" (
|
||||
arg1: ^Tight_Field_Element,
|
||||
@@ -17,7 +17,7 @@ fe_tighten_cast :: #force_inline proc "contextless" (
|
||||
fe_clear :: proc "contextless" (
|
||||
arg1: $T,
|
||||
) where T == ^Tight_Field_Element || T == ^Loose_Field_Element {
|
||||
mem.zero_explicit(arg1, size_of(arg1^))
|
||||
crypto.zero_explicit(arg1, size_of(arg1^))
|
||||
}
|
||||
|
||||
fe_clear_vec :: proc "contextless" (
|
||||
|
||||
345
core/crypto/_fiat/field_p256r1/field.odin
Normal file
345
core/crypto/_fiat/field_p256r1/field.odin
Normal file
@@ -0,0 +1,345 @@
|
||||
package field_p256r1
|
||||
|
||||
import "core:crypto"
|
||||
import subtle "core:crypto/_subtle"
|
||||
import "core:encoding/endian"
|
||||
import "core:math/bits"
|
||||
|
||||
fe_clear :: proc "contextless" (arg1: ^Montgomery_Domain_Field_Element) {
|
||||
crypto.zero_explicit(arg1, size_of(Montgomery_Domain_Field_Element))
|
||||
}
|
||||
|
||||
fe_clear_vec :: proc "contextless" (
|
||||
arg1: []^Montgomery_Domain_Field_Element,
|
||||
) {
|
||||
for fe in arg1 {
|
||||
fe_clear(fe)
|
||||
}
|
||||
}
|
||||
|
||||
fe_from_bytes :: proc "contextless" (
|
||||
out1: ^Montgomery_Domain_Field_Element,
|
||||
arg1: []byte,
|
||||
unsafe_assume_canonical := false,
|
||||
) -> bool {
|
||||
ensure_contextless(len(arg1) == 32, "p256r1: invalid fe input buffer")
|
||||
|
||||
// Note: We assume the input is in big-endian.
|
||||
tmp := Non_Montgomery_Domain_Field_Element {
|
||||
endian.unchecked_get_u64be(arg1[24:]),
|
||||
endian.unchecked_get_u64be(arg1[16:]),
|
||||
endian.unchecked_get_u64be(arg1[8:]),
|
||||
endian.unchecked_get_u64be(arg1[0:]),
|
||||
}
|
||||
defer crypto.zero_explicit(&tmp, size_of(tmp))
|
||||
|
||||
// Check that tmp is in the the range [0, ELL).
|
||||
if !unsafe_assume_canonical {
|
||||
_, borrow := bits.sub_u64(ELL[0] - 1, tmp[0], 0)
|
||||
_, borrow = bits.sub_u64(ELL[1], tmp[1], borrow)
|
||||
_, borrow = bits.sub_u64(ELL[2], tmp[2], borrow)
|
||||
_, borrow = bits.sub_u64(ELL[3], tmp[3], borrow)
|
||||
if borrow != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
fe_to_montgomery(out1, &tmp)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fe_to_bytes :: proc "contextless" (out1: []byte, arg1: ^Montgomery_Domain_Field_Element) {
|
||||
ensure_contextless(len(out1) == 32, "p256r1: invalid fe output buffer")
|
||||
|
||||
tmp: Non_Montgomery_Domain_Field_Element = ---
|
||||
fe_from_montgomery(&tmp, arg1)
|
||||
|
||||
// Note: Likewise, output in big-endian.
|
||||
endian.unchecked_put_u64be(out1[24:], tmp[0])
|
||||
endian.unchecked_put_u64be(out1[16:], tmp[1])
|
||||
endian.unchecked_put_u64be(out1[8:], tmp[2])
|
||||
endian.unchecked_put_u64be(out1[0:], tmp[3])
|
||||
|
||||
crypto.zero_explicit(&tmp, size_of(tmp))
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
fe_equal :: proc "contextless" (arg1, arg2: ^Montgomery_Domain_Field_Element) -> int {
|
||||
tmp: Montgomery_Domain_Field_Element = ---
|
||||
fe_sub(&tmp, arg1, arg2)
|
||||
|
||||
// This will only underflow if and only if (⟺) arg1 == arg2, and we return the borrow,
|
||||
// which will be 1.
|
||||
is_eq := subtle.u64_is_zero(fe_non_zero(&tmp))
|
||||
|
||||
fe_clear(&tmp)
|
||||
|
||||
return int(is_eq)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
fe_is_odd :: proc "contextless" (arg1: ^Montgomery_Domain_Field_Element) -> int {
|
||||
tmp: Non_Montgomery_Domain_Field_Element = ---
|
||||
defer crypto.zero_explicit(&tmp, size_of(tmp))
|
||||
|
||||
fe_from_montgomery(&tmp, arg1)
|
||||
return int(tmp[0] & 1)
|
||||
}
|
||||
|
||||
fe_pow2k :: proc "contextless" (
|
||||
out1: ^Montgomery_Domain_Field_Element,
|
||||
arg1: ^Montgomery_Domain_Field_Element,
|
||||
arg2: uint,
|
||||
) {
|
||||
// Special case: `arg1^(2 * 0) = 1`, though this should never happen.
|
||||
if arg2 == 0 {
|
||||
fe_one(out1)
|
||||
return
|
||||
}
|
||||
|
||||
fe_square(out1, arg1)
|
||||
for _ in 1 ..< arg2 {
|
||||
fe_square(out1, out1)
|
||||
}
|
||||
}
|
||||
|
||||
fe_inv :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
|
||||
// Inversion computation is derived from the addition chain:
|
||||
//
|
||||
// _10 = 2*1
|
||||
// _11 = 1 + _10
|
||||
// _110 = 2*_11
|
||||
// _111 = 1 + _110
|
||||
// _111000 = _111 << 3
|
||||
// _111111 = _111 + _111000
|
||||
// x12 = _111111 << 6 + _111111
|
||||
// x15 = x12 << 3 + _111
|
||||
// x16 = 2*x15 + 1
|
||||
// x32 = x16 << 16 + x16
|
||||
// i53 = x32 << 15
|
||||
// x47 = x15 + i53
|
||||
// i263 = ((i53 << 17 + 1) << 143 + x47) << 47
|
||||
// return (x47 + i263) << 2
|
||||
//
|
||||
// Operations: 255 squares 11 multiplies
|
||||
//
|
||||
// Generated by github.com/mmcloughlin/addchain v0.4.0.
|
||||
|
||||
// Note: Need to stash `arg1` (`xx`) in the case that `out1`/`arg1` alias,
|
||||
// as `arg1` is used after `out1` has been altered.
|
||||
t0, t1, xx: Montgomery_Domain_Field_Element = ---, ---, arg1^
|
||||
|
||||
// Step 1: z = x^0x2
|
||||
fe_square(out1, arg1)
|
||||
|
||||
// Step 2: z = x^0x3
|
||||
fe_mul(out1, &xx, out1)
|
||||
|
||||
// Step 3: z = x^0x6
|
||||
fe_square(out1, out1)
|
||||
|
||||
// Step 4: z = x^0x7
|
||||
fe_mul(out1, &xx, out1)
|
||||
|
||||
// Step 7: t0 = x^0x38
|
||||
fe_pow2k(&t0, out1, 3)
|
||||
|
||||
// Step 8: t0 = x^0x3f
|
||||
fe_mul(&t0, out1, &t0)
|
||||
|
||||
// Step 14: t1 = x^0xfc0
|
||||
fe_pow2k(&t1, &t0, 6)
|
||||
|
||||
// Step 15: t0 = x^0xfff
|
||||
fe_mul(&t0, &t0, &t1)
|
||||
|
||||
// Step 18: t0 = x^0x7ff8
|
||||
fe_pow2k(&t0, &t0, 3)
|
||||
|
||||
// Step 19: z = x^0x7fff
|
||||
fe_mul(out1, out1, &t0)
|
||||
|
||||
// Step 20: t0 = x^0xfffe
|
||||
fe_square(&t0, out1)
|
||||
|
||||
// Step 21: t0 = x^0xffff
|
||||
fe_mul(&t0, &xx, &t0)
|
||||
|
||||
// Step 37: t1 = x^0xffff0000
|
||||
fe_pow2k(&t1, &t0, 16)
|
||||
|
||||
// Step 38: t0 = x^0xffffffff
|
||||
fe_mul(&t0, &t0, &t1)
|
||||
|
||||
// Step 53: t0 = x^0x7fffffff8000
|
||||
fe_pow2k(&t0, &t0, 15)
|
||||
|
||||
// Step 54: z = x^0x7fffffffffff
|
||||
fe_mul(out1, out1, &t0)
|
||||
|
||||
// Step 71: t0 = x^0xffffffff00000000
|
||||
fe_pow2k(&t0, &t0, 17)
|
||||
|
||||
// Step 72: t0 = x^0xffffffff00000001
|
||||
fe_mul(&t0, &xx, &t0)
|
||||
|
||||
// Step 215: t0 = x^0x7fffffff80000000800000000000000000000000000000000000
|
||||
fe_pow2k(&t0, &t0, 143)
|
||||
|
||||
// Step 216: t0 = x^0x7fffffff800000008000000000000000000000007fffffffffff
|
||||
fe_mul(&t0, out1, &t0)
|
||||
|
||||
// Step 263: t0 = x^0x3fffffffc00000004000000000000000000000003fffffffffff800000000000
|
||||
fe_pow2k(&t0, &t0, 47)
|
||||
|
||||
// Step 264: z = x^0x3fffffffc00000004000000000000000000000003fffffffffffffffffffffff
|
||||
fe_mul(out1, out1, &t0)
|
||||
|
||||
// Step 266: z = x^0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc
|
||||
fe_pow2k(out1, out1, 2)
|
||||
|
||||
fe_mul(out1, out1, &xx)
|
||||
|
||||
fe_clear_vec([]^Montgomery_Domain_Field_Element{&t0, &t1, &xx})
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
fe_sqrt :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) -> int {
|
||||
// Square root candidate can be derived via exponentiation by `(p + 1) / 4`
|
||||
// From sage: 28948022302589062190674361737351893382521535853822578548883407827216774463488
|
||||
//
|
||||
// Inversion computation is derived from the addition chain:
|
||||
//
|
||||
// _10 = 2*1
|
||||
// _11 = 1 + _10
|
||||
// _1100 = _11 << 2
|
||||
// _1111 = _11 + _1100
|
||||
// _11110000 = _1111 << 4
|
||||
// _11111111 = _1111 + _11110000
|
||||
// x16 = _11111111 << 8 + _11111111
|
||||
// x32 = x16 << 16 + x16
|
||||
// return ((x32 << 32 + 1) << 96 + 1) << 94
|
||||
//
|
||||
// Operations: 253 squares 7 multiplies
|
||||
//
|
||||
// Generated by github.com/mmcloughlin/addchain v0.4.0.
|
||||
|
||||
// Likewise this tramples over arg1, so stash another copy.
|
||||
t0, xx: Montgomery_Domain_Field_Element = ---, arg1^
|
||||
|
||||
// Step 1: z = x^0x2
|
||||
fe_square(out1, arg1)
|
||||
|
||||
// Step 2: z = x^0x3
|
||||
fe_mul(out1, &xx, out1)
|
||||
|
||||
// Step 4: t0 = x^0xc
|
||||
fe_pow2k(&t0, &xx, 2)
|
||||
|
||||
// Step 5: z = x^0xf
|
||||
fe_mul(out1, out1, &t0)
|
||||
|
||||
// Step 9: t0 = x^0xf0
|
||||
fe_pow2k(&t0, out1, 4)
|
||||
|
||||
// Step 10: z = x^0xff
|
||||
fe_mul(out1, out1, &t0)
|
||||
|
||||
// Step 18: t0 = x^0xff00
|
||||
fe_pow2k(&t0, out1, 8)
|
||||
|
||||
// Step 19: z = x^0xffff
|
||||
fe_mul(out1, out1, &t0)
|
||||
|
||||
// Step 35: t0 = x^0xffff0000
|
||||
fe_pow2k(&t0, out1, 16)
|
||||
|
||||
// Step 36: z = x^0xffffffff
|
||||
fe_mul(out1, out1, &t0)
|
||||
|
||||
// Step 68: z = x^0xffffffff00000000
|
||||
fe_pow2k(out1, out1, 32)
|
||||
|
||||
// Step 69: z = x^0xffffffff00000001
|
||||
fe_mul(out1, &xx, out1)
|
||||
|
||||
// Step 165: z = x^0xffffffff00000001000000000000000000000000
|
||||
fe_pow2k(out1, out1, 96)
|
||||
|
||||
// Step 166: z = x^0xffffffff00000001000000000000000000000001
|
||||
fe_mul(out1, &xx, out1)
|
||||
|
||||
// Step 260: z = x^0x3fffffffc0000000400000000000000000000000400000000000000000000000
|
||||
fe_pow2k(out1, out1, 94)
|
||||
|
||||
// Ensure that our candidate is actually the square root.
|
||||
check, zero: Montgomery_Domain_Field_Element
|
||||
fe_square(&check, out1)
|
||||
|
||||
is_valid := fe_equal(&check, &xx)
|
||||
fe_cond_select(out1, &zero, out1, is_valid)
|
||||
|
||||
fe_clear_vec([]^Montgomery_Domain_Field_Element{&t0, &xx, &check})
|
||||
|
||||
return is_valid
|
||||
}
|
||||
|
||||
fe_zero :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element) {
|
||||
out1[0] = 0
|
||||
out1[1] = 0
|
||||
out1[2] = 0
|
||||
out1[3] = 0
|
||||
}
|
||||
|
||||
fe_set :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
|
||||
x1 := arg1[0]
|
||||
x2 := arg1[1]
|
||||
x3 := arg1[2]
|
||||
x4 := arg1[3]
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
}
|
||||
|
||||
@(optimization_mode = "none")
|
||||
fe_cond_swap :: #force_no_inline proc "contextless" (out1, out2: ^Montgomery_Domain_Field_Element, arg1: int) {
|
||||
mask := (u64(arg1) * 0xffffffffffffffff)
|
||||
x := (out1[0] ~ out2[0]) & mask
|
||||
x1, y1 := out1[0] ~ x, out2[0] ~ x
|
||||
x = (out1[1] ~ out2[1]) & mask
|
||||
x2, y2 := out1[1] ~ x, out2[1] ~ x
|
||||
x = (out1[2] ~ out2[2]) & mask
|
||||
x3, y3 := out1[2] ~ x, out2[2] ~ x
|
||||
x = (out1[3] ~ out2[3]) & mask
|
||||
x4, y4 := out1[3] ~ x, out2[3] ~ x
|
||||
out1[0], out2[0] = x1, y1
|
||||
out1[1], out2[1] = x2, y2
|
||||
out1[2], out2[2] = x3, y3
|
||||
out1[3], out2[3] = x4, y4
|
||||
}
|
||||
|
||||
@(optimization_mode = "none")
|
||||
fe_cond_select :: #force_no_inline proc "contextless" (
|
||||
out1, arg1, arg2: ^Montgomery_Domain_Field_Element,
|
||||
arg3: int,
|
||||
) {
|
||||
mask := (u64(arg3) * 0xffffffffffffffff)
|
||||
x1 := ((mask & arg2[0]) | ((~mask) & arg1[0]))
|
||||
x2 := ((mask & arg2[1]) | ((~mask) & arg1[1]))
|
||||
x3 := ((mask & arg2[2]) | ((~mask) & arg1[2]))
|
||||
x4 := ((mask & arg2[3]) | ((~mask) & arg1[3]))
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
}
|
||||
|
||||
fe_cond_negate :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element, ctrl: int) {
|
||||
tmp1: Montgomery_Domain_Field_Element = ---
|
||||
fe_opp(&tmp1, arg1)
|
||||
fe_cond_select(out1, arg1, &tmp1, ctrl)
|
||||
|
||||
fe_clear(&tmp1)
|
||||
}
|
||||
501
core/crypto/_fiat/field_p256r1/field64.odin
Normal file
501
core/crypto/_fiat/field_p256r1/field64.odin
Normal file
@@ -0,0 +1,501 @@
|
||||
// The BSD 1-Clause License (BSD-1-Clause)
|
||||
//
|
||||
// Copyright (c) 2015-2020 the fiat-crypto authors (see the AUTHORS file)
|
||||
// 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 fiat-crypto 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 Berkeley Software Design,
|
||||
// Inc. 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.
|
||||
|
||||
package field_p256r1
|
||||
|
||||
// The file provides arithmetic on the field Z/(2^256 - 2^224 + 2^192 + 2^96 - 1)
|
||||
// using a 64-bit Montgomery form internal representation. It is derived
|
||||
// primarily from the machine generated Golang output from the fiat-crypto
|
||||
// project.
|
||||
//
|
||||
// While the base implementation is provably correct, this implementation
|
||||
// makes no such claims as the port and optimizations were done by hand.
|
||||
//
|
||||
// WARNING: While big-endian is the common representation used for this
|
||||
// curve, the fiat output uses least-significant-limb first.
|
||||
|
||||
import fiat "core:crypto/_fiat"
|
||||
import "core:math/bits"
|
||||
|
||||
// ELL is the saturated representation of the field order, least-significant
|
||||
// limb first.
|
||||
ELL :: [4]u64{0xffffffffffffffff, 0xffffffff, 0x0, 0xffffffff00000001}
|
||||
|
||||
Montgomery_Domain_Field_Element :: distinct [4]u64
|
||||
Non_Montgomery_Domain_Field_Element :: distinct [4]u64
|
||||
|
||||
fe_mul :: proc "contextless" (out1, arg1, arg2: ^Montgomery_Domain_Field_Element) {
|
||||
x1 := arg1[1]
|
||||
x2 := arg1[2]
|
||||
x3 := arg1[3]
|
||||
x4 := arg1[0]
|
||||
x6, x5 := bits.mul_u64(x4, arg2[3])
|
||||
x8, x7 := bits.mul_u64(x4, arg2[2])
|
||||
x10, x9 := bits.mul_u64(x4, arg2[1])
|
||||
x12, x11 := bits.mul_u64(x4, arg2[0])
|
||||
x13, x14 := bits.add_u64(x12, x9, u64(0x0))
|
||||
x15, x16 := bits.add_u64(x10, x7, u64(fiat.u1(x14)))
|
||||
x17, x18 := bits.add_u64(x8, x5, u64(fiat.u1(x16)))
|
||||
x19 := (u64(fiat.u1(x18)) + x6)
|
||||
x21, x20 := bits.mul_u64(x11, 0xffffffff00000001)
|
||||
x23, x22 := bits.mul_u64(x11, 0xffffffff)
|
||||
x25, x24 := bits.mul_u64(x11, 0xffffffffffffffff)
|
||||
x26, x27 := bits.add_u64(x25, x22, u64(0x0))
|
||||
x28 := (u64(fiat.u1(x27)) + x23)
|
||||
_, x30 := bits.add_u64(x11, x24, u64(0x0))
|
||||
x31, x32 := bits.add_u64(x13, x26, u64(fiat.u1(x30)))
|
||||
x33, x34 := bits.add_u64(x15, x28, u64(fiat.u1(x32)))
|
||||
x35, x36 := bits.add_u64(x17, x20, u64(fiat.u1(x34)))
|
||||
x37, x38 := bits.add_u64(x19, x21, u64(fiat.u1(x36)))
|
||||
x40, x39 := bits.mul_u64(x1, arg2[3])
|
||||
x42, x41 := bits.mul_u64(x1, arg2[2])
|
||||
x44, x43 := bits.mul_u64(x1, arg2[1])
|
||||
x46, x45 := bits.mul_u64(x1, arg2[0])
|
||||
x47, x48 := bits.add_u64(x46, x43, u64(0x0))
|
||||
x49, x50 := bits.add_u64(x44, x41, u64(fiat.u1(x48)))
|
||||
x51, x52 := bits.add_u64(x42, x39, u64(fiat.u1(x50)))
|
||||
x53 := (u64(fiat.u1(x52)) + x40)
|
||||
x54, x55 := bits.add_u64(x31, x45, u64(0x0))
|
||||
x56, x57 := bits.add_u64(x33, x47, u64(fiat.u1(x55)))
|
||||
x58, x59 := bits.add_u64(x35, x49, u64(fiat.u1(x57)))
|
||||
x60, x61 := bits.add_u64(x37, x51, u64(fiat.u1(x59)))
|
||||
x62, x63 := bits.add_u64(u64(fiat.u1(x38)), x53, u64(fiat.u1(x61)))
|
||||
x65, x64 := bits.mul_u64(x54, 0xffffffff00000001)
|
||||
x67, x66 := bits.mul_u64(x54, 0xffffffff)
|
||||
x69, x68 := bits.mul_u64(x54, 0xffffffffffffffff)
|
||||
x70, x71 := bits.add_u64(x69, x66, u64(0x0))
|
||||
x72 := (u64(fiat.u1(x71)) + x67)
|
||||
_, x74 := bits.add_u64(x54, x68, u64(0x0))
|
||||
x75, x76 := bits.add_u64(x56, x70, u64(fiat.u1(x74)))
|
||||
x77, x78 := bits.add_u64(x58, x72, u64(fiat.u1(x76)))
|
||||
x79, x80 := bits.add_u64(x60, x64, u64(fiat.u1(x78)))
|
||||
x81, x82 := bits.add_u64(x62, x65, u64(fiat.u1(x80)))
|
||||
x83 := (u64(fiat.u1(x82)) + u64(fiat.u1(x63)))
|
||||
x85, x84 := bits.mul_u64(x2, arg2[3])
|
||||
x87, x86 := bits.mul_u64(x2, arg2[2])
|
||||
x89, x88 := bits.mul_u64(x2, arg2[1])
|
||||
x91, x90 := bits.mul_u64(x2, arg2[0])
|
||||
x92, x93 := bits.add_u64(x91, x88, u64(0x0))
|
||||
x94, x95 := bits.add_u64(x89, x86, u64(fiat.u1(x93)))
|
||||
x96, x97 := bits.add_u64(x87, x84, u64(fiat.u1(x95)))
|
||||
x98 := (u64(fiat.u1(x97)) + x85)
|
||||
x99, x100 := bits.add_u64(x75, x90, u64(0x0))
|
||||
x101, x102 := bits.add_u64(x77, x92, u64(fiat.u1(x100)))
|
||||
x103, x104 := bits.add_u64(x79, x94, u64(fiat.u1(x102)))
|
||||
x105, x106 := bits.add_u64(x81, x96, u64(fiat.u1(x104)))
|
||||
x107, x108 := bits.add_u64(x83, x98, u64(fiat.u1(x106)))
|
||||
x110, x109 := bits.mul_u64(x99, 0xffffffff00000001)
|
||||
x112, x111 := bits.mul_u64(x99, 0xffffffff)
|
||||
x114, x113 := bits.mul_u64(x99, 0xffffffffffffffff)
|
||||
x115, x116 := bits.add_u64(x114, x111, u64(0x0))
|
||||
x117 := (u64(fiat.u1(x116)) + x112)
|
||||
_, x119 := bits.add_u64(x99, x113, u64(0x0))
|
||||
x120, x121 := bits.add_u64(x101, x115, u64(fiat.u1(x119)))
|
||||
x122, x123 := bits.add_u64(x103, x117, u64(fiat.u1(x121)))
|
||||
x124, x125 := bits.add_u64(x105, x109, u64(fiat.u1(x123)))
|
||||
x126, x127 := bits.add_u64(x107, x110, u64(fiat.u1(x125)))
|
||||
x128 := (u64(fiat.u1(x127)) + u64(fiat.u1(x108)))
|
||||
x130, x129 := bits.mul_u64(x3, arg2[3])
|
||||
x132, x131 := bits.mul_u64(x3, arg2[2])
|
||||
x134, x133 := bits.mul_u64(x3, arg2[1])
|
||||
x136, x135 := bits.mul_u64(x3, arg2[0])
|
||||
x137, x138 := bits.add_u64(x136, x133, u64(0x0))
|
||||
x139, x140 := bits.add_u64(x134, x131, u64(fiat.u1(x138)))
|
||||
x141, x142 := bits.add_u64(x132, x129, u64(fiat.u1(x140)))
|
||||
x143 := (u64(fiat.u1(x142)) + x130)
|
||||
x144, x145 := bits.add_u64(x120, x135, u64(0x0))
|
||||
x146, x147 := bits.add_u64(x122, x137, u64(fiat.u1(x145)))
|
||||
x148, x149 := bits.add_u64(x124, x139, u64(fiat.u1(x147)))
|
||||
x150, x151 := bits.add_u64(x126, x141, u64(fiat.u1(x149)))
|
||||
x152, x153 := bits.add_u64(x128, x143, u64(fiat.u1(x151)))
|
||||
x155, x154 := bits.mul_u64(x144, 0xffffffff00000001)
|
||||
x157, x156 := bits.mul_u64(x144, 0xffffffff)
|
||||
x159, x158 := bits.mul_u64(x144, 0xffffffffffffffff)
|
||||
x160, x161 := bits.add_u64(x159, x156, u64(0x0))
|
||||
x162 := (u64(fiat.u1(x161)) + x157)
|
||||
_, x164 := bits.add_u64(x144, x158, u64(0x0))
|
||||
x165, x166 := bits.add_u64(x146, x160, u64(fiat.u1(x164)))
|
||||
x167, x168 := bits.add_u64(x148, x162, u64(fiat.u1(x166)))
|
||||
x169, x170 := bits.add_u64(x150, x154, u64(fiat.u1(x168)))
|
||||
x171, x172 := bits.add_u64(x152, x155, u64(fiat.u1(x170)))
|
||||
x173 := (u64(fiat.u1(x172)) + u64(fiat.u1(x153)))
|
||||
x174, x175 := bits.sub_u64(x165, 0xffffffffffffffff, u64(0x0))
|
||||
x176, x177 := bits.sub_u64(x167, 0xffffffff, u64(fiat.u1(x175)))
|
||||
x178, x179 := bits.sub_u64(x169, u64(0x0), u64(fiat.u1(x177)))
|
||||
x180, x181 := bits.sub_u64(x171, 0xffffffff00000001, u64(fiat.u1(x179)))
|
||||
_, x183 := bits.sub_u64(x173, u64(0x0), u64(fiat.u1(x181)))
|
||||
x184 := fiat.cmovznz_u64(fiat.u1(x183), x174, x165)
|
||||
x185 := fiat.cmovznz_u64(fiat.u1(x183), x176, x167)
|
||||
x186 := fiat.cmovznz_u64(fiat.u1(x183), x178, x169)
|
||||
x187 := fiat.cmovznz_u64(fiat.u1(x183), x180, x171)
|
||||
out1[0] = x184
|
||||
out1[1] = x185
|
||||
out1[2] = x186
|
||||
out1[3] = x187
|
||||
}
|
||||
|
||||
fe_square :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
|
||||
x1 := arg1[1]
|
||||
x2 := arg1[2]
|
||||
x3 := arg1[3]
|
||||
x4 := arg1[0]
|
||||
x6, x5 := bits.mul_u64(x4, arg1[3])
|
||||
x8, x7 := bits.mul_u64(x4, arg1[2])
|
||||
x10, x9 := bits.mul_u64(x4, arg1[1])
|
||||
x12, x11 := bits.mul_u64(x4, arg1[0])
|
||||
x13, x14 := bits.add_u64(x12, x9, u64(0x0))
|
||||
x15, x16 := bits.add_u64(x10, x7, u64(fiat.u1(x14)))
|
||||
x17, x18 := bits.add_u64(x8, x5, u64(fiat.u1(x16)))
|
||||
x19 := (u64(fiat.u1(x18)) + x6)
|
||||
x21, x20 := bits.mul_u64(x11, 0xffffffff00000001)
|
||||
x23, x22 := bits.mul_u64(x11, 0xffffffff)
|
||||
x25, x24 := bits.mul_u64(x11, 0xffffffffffffffff)
|
||||
x26, x27 := bits.add_u64(x25, x22, u64(0x0))
|
||||
x28 := (u64(fiat.u1(x27)) + x23)
|
||||
_, x30 := bits.add_u64(x11, x24, u64(0x0))
|
||||
x31, x32 := bits.add_u64(x13, x26, u64(fiat.u1(x30)))
|
||||
x33, x34 := bits.add_u64(x15, x28, u64(fiat.u1(x32)))
|
||||
x35, x36 := bits.add_u64(x17, x20, u64(fiat.u1(x34)))
|
||||
x37, x38 := bits.add_u64(x19, x21, u64(fiat.u1(x36)))
|
||||
x40, x39 := bits.mul_u64(x1, arg1[3])
|
||||
x42, x41 := bits.mul_u64(x1, arg1[2])
|
||||
x44, x43 := bits.mul_u64(x1, arg1[1])
|
||||
x46, x45 := bits.mul_u64(x1, arg1[0])
|
||||
x47, x48 := bits.add_u64(x46, x43, u64(0x0))
|
||||
x49, x50 := bits.add_u64(x44, x41, u64(fiat.u1(x48)))
|
||||
x51, x52 := bits.add_u64(x42, x39, u64(fiat.u1(x50)))
|
||||
x53 := (u64(fiat.u1(x52)) + x40)
|
||||
x54, x55 := bits.add_u64(x31, x45, u64(0x0))
|
||||
x56, x57 := bits.add_u64(x33, x47, u64(fiat.u1(x55)))
|
||||
x58, x59 := bits.add_u64(x35, x49, u64(fiat.u1(x57)))
|
||||
x60, x61 := bits.add_u64(x37, x51, u64(fiat.u1(x59)))
|
||||
x62, x63 := bits.add_u64(u64(fiat.u1(x38)), x53, u64(fiat.u1(x61)))
|
||||
x65, x64 := bits.mul_u64(x54, 0xffffffff00000001)
|
||||
x67, x66 := bits.mul_u64(x54, 0xffffffff)
|
||||
x69, x68 := bits.mul_u64(x54, 0xffffffffffffffff)
|
||||
x70, x71 := bits.add_u64(x69, x66, u64(0x0))
|
||||
x72 := (u64(fiat.u1(x71)) + x67)
|
||||
_, x74 := bits.add_u64(x54, x68, u64(0x0))
|
||||
x75, x76 := bits.add_u64(x56, x70, u64(fiat.u1(x74)))
|
||||
x77, x78 := bits.add_u64(x58, x72, u64(fiat.u1(x76)))
|
||||
x79, x80 := bits.add_u64(x60, x64, u64(fiat.u1(x78)))
|
||||
x81, x82 := bits.add_u64(x62, x65, u64(fiat.u1(x80)))
|
||||
x83 := (u64(fiat.u1(x82)) + u64(fiat.u1(x63)))
|
||||
x85, x84 := bits.mul_u64(x2, arg1[3])
|
||||
x87, x86 := bits.mul_u64(x2, arg1[2])
|
||||
x89, x88 := bits.mul_u64(x2, arg1[1])
|
||||
x91, x90 := bits.mul_u64(x2, arg1[0])
|
||||
x92, x93 := bits.add_u64(x91, x88, u64(0x0))
|
||||
x94, x95 := bits.add_u64(x89, x86, u64(fiat.u1(x93)))
|
||||
x96, x97 := bits.add_u64(x87, x84, u64(fiat.u1(x95)))
|
||||
x98 := (u64(fiat.u1(x97)) + x85)
|
||||
x99, x100 := bits.add_u64(x75, x90, u64(0x0))
|
||||
x101, x102 := bits.add_u64(x77, x92, u64(fiat.u1(x100)))
|
||||
x103, x104 := bits.add_u64(x79, x94, u64(fiat.u1(x102)))
|
||||
x105, x106 := bits.add_u64(x81, x96, u64(fiat.u1(x104)))
|
||||
x107, x108 := bits.add_u64(x83, x98, u64(fiat.u1(x106)))
|
||||
x110, x109 := bits.mul_u64(x99, 0xffffffff00000001)
|
||||
x112, x111 := bits.mul_u64(x99, 0xffffffff)
|
||||
x114, x113 := bits.mul_u64(x99, 0xffffffffffffffff)
|
||||
x115, x116 := bits.add_u64(x114, x111, u64(0x0))
|
||||
x117 := (u64(fiat.u1(x116)) + x112)
|
||||
_, x119 := bits.add_u64(x99, x113, u64(0x0))
|
||||
x120, x121 := bits.add_u64(x101, x115, u64(fiat.u1(x119)))
|
||||
x122, x123 := bits.add_u64(x103, x117, u64(fiat.u1(x121)))
|
||||
x124, x125 := bits.add_u64(x105, x109, u64(fiat.u1(x123)))
|
||||
x126, x127 := bits.add_u64(x107, x110, u64(fiat.u1(x125)))
|
||||
x128 := (u64(fiat.u1(x127)) + u64(fiat.u1(x108)))
|
||||
x130, x129 := bits.mul_u64(x3, arg1[3])
|
||||
x132, x131 := bits.mul_u64(x3, arg1[2])
|
||||
x134, x133 := bits.mul_u64(x3, arg1[1])
|
||||
x136, x135 := bits.mul_u64(x3, arg1[0])
|
||||
x137, x138 := bits.add_u64(x136, x133, u64(0x0))
|
||||
x139, x140 := bits.add_u64(x134, x131, u64(fiat.u1(x138)))
|
||||
x141, x142 := bits.add_u64(x132, x129, u64(fiat.u1(x140)))
|
||||
x143 := (u64(fiat.u1(x142)) + x130)
|
||||
x144, x145 := bits.add_u64(x120, x135, u64(0x0))
|
||||
x146, x147 := bits.add_u64(x122, x137, u64(fiat.u1(x145)))
|
||||
x148, x149 := bits.add_u64(x124, x139, u64(fiat.u1(x147)))
|
||||
x150, x151 := bits.add_u64(x126, x141, u64(fiat.u1(x149)))
|
||||
x152, x153 := bits.add_u64(x128, x143, u64(fiat.u1(x151)))
|
||||
x155, x154 := bits.mul_u64(x144, 0xffffffff00000001)
|
||||
x157, x156 := bits.mul_u64(x144, 0xffffffff)
|
||||
x159, x158 := bits.mul_u64(x144, 0xffffffffffffffff)
|
||||
x160, x161 := bits.add_u64(x159, x156, u64(0x0))
|
||||
x162 := (u64(fiat.u1(x161)) + x157)
|
||||
_, x164 := bits.add_u64(x144, x158, u64(0x0))
|
||||
x165, x166 := bits.add_u64(x146, x160, u64(fiat.u1(x164)))
|
||||
x167, x168 := bits.add_u64(x148, x162, u64(fiat.u1(x166)))
|
||||
x169, x170 := bits.add_u64(x150, x154, u64(fiat.u1(x168)))
|
||||
x171, x172 := bits.add_u64(x152, x155, u64(fiat.u1(x170)))
|
||||
x173 := (u64(fiat.u1(x172)) + u64(fiat.u1(x153)))
|
||||
x174, x175 := bits.sub_u64(x165, 0xffffffffffffffff, u64(0x0))
|
||||
x176, x177 := bits.sub_u64(x167, 0xffffffff, u64(fiat.u1(x175)))
|
||||
x178, x179 := bits.sub_u64(x169, u64(0x0), u64(fiat.u1(x177)))
|
||||
x180, x181 := bits.sub_u64(x171, 0xffffffff00000001, u64(fiat.u1(x179)))
|
||||
_, x183 := bits.sub_u64(x173, u64(0x0), u64(fiat.u1(x181)))
|
||||
x184 := fiat.cmovznz_u64(fiat.u1(x183), x174, x165)
|
||||
x185 := fiat.cmovznz_u64(fiat.u1(x183), x176, x167)
|
||||
x186 := fiat.cmovznz_u64(fiat.u1(x183), x178, x169)
|
||||
x187 := fiat.cmovznz_u64(fiat.u1(x183), x180, x171)
|
||||
out1[0] = x184
|
||||
out1[1] = x185
|
||||
out1[2] = x186
|
||||
out1[3] = x187
|
||||
}
|
||||
|
||||
fe_add :: proc "contextless" (out1, arg1, arg2: ^Montgomery_Domain_Field_Element) {
|
||||
x1, x2 := bits.add_u64(arg1[0], arg2[0], u64(0x0))
|
||||
x3, x4 := bits.add_u64(arg1[1], arg2[1], u64(fiat.u1(x2)))
|
||||
x5, x6 := bits.add_u64(arg1[2], arg2[2], u64(fiat.u1(x4)))
|
||||
x7, x8 := bits.add_u64(arg1[3], arg2[3], u64(fiat.u1(x6)))
|
||||
x9, x10 := bits.sub_u64(x1, 0xffffffffffffffff, u64(0x0))
|
||||
x11, x12 := bits.sub_u64(x3, 0xffffffff, u64(fiat.u1(x10)))
|
||||
x13, x14 := bits.sub_u64(x5, u64(0x0), u64(fiat.u1(x12)))
|
||||
x15, x16 := bits.sub_u64(x7, 0xffffffff00000001, u64(fiat.u1(x14)))
|
||||
_, x18 := bits.sub_u64(u64(fiat.u1(x8)), u64(0x0), u64(fiat.u1(x16)))
|
||||
x19 := fiat.cmovznz_u64(fiat.u1(x18), x9, x1)
|
||||
x20 := fiat.cmovznz_u64(fiat.u1(x18), x11, x3)
|
||||
x21 := fiat.cmovznz_u64(fiat.u1(x18), x13, x5)
|
||||
x22 := fiat.cmovznz_u64(fiat.u1(x18), x15, x7)
|
||||
out1[0] = x19
|
||||
out1[1] = x20
|
||||
out1[2] = x21
|
||||
out1[3] = x22
|
||||
}
|
||||
|
||||
fe_sub :: proc "contextless" (out1, arg1, arg2: ^Montgomery_Domain_Field_Element) {
|
||||
x1, x2 := bits.sub_u64(arg1[0], arg2[0], u64(0x0))
|
||||
x3, x4 := bits.sub_u64(arg1[1], arg2[1], u64(fiat.u1(x2)))
|
||||
x5, x6 := bits.sub_u64(arg1[2], arg2[2], u64(fiat.u1(x4)))
|
||||
x7, x8 := bits.sub_u64(arg1[3], arg2[3], u64(fiat.u1(x6)))
|
||||
x9 := fiat.cmovznz_u64(fiat.u1(x8), u64(0x0), 0xffffffffffffffff)
|
||||
x10, x11 := bits.add_u64(x1, x9, u64(0x0))
|
||||
x12, x13 := bits.add_u64(x3, (x9 & 0xffffffff), u64(fiat.u1(x11)))
|
||||
x14, x15 := bits.add_u64(x5, u64(0x0), u64(fiat.u1(x13)))
|
||||
x16, _ := bits.add_u64(x7, (x9 & 0xffffffff00000001), u64(fiat.u1(x15)))
|
||||
out1[0] = x10
|
||||
out1[1] = x12
|
||||
out1[2] = x14
|
||||
out1[3] = x16
|
||||
}
|
||||
|
||||
fe_opp :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
|
||||
x1, x2 := bits.sub_u64(u64(0x0), arg1[0], u64(0x0))
|
||||
x3, x4 := bits.sub_u64(u64(0x0), arg1[1], u64(fiat.u1(x2)))
|
||||
x5, x6 := bits.sub_u64(u64(0x0), arg1[2], u64(fiat.u1(x4)))
|
||||
x7, x8 := bits.sub_u64(u64(0x0), arg1[3], u64(fiat.u1(x6)))
|
||||
x9 := fiat.cmovznz_u64(fiat.u1(x8), u64(0x0), 0xffffffffffffffff)
|
||||
x10, x11 := bits.add_u64(x1, x9, u64(0x0))
|
||||
x12, x13 := bits.add_u64(x3, (x9 & 0xffffffff), u64(fiat.u1(x11)))
|
||||
x14, x15 := bits.add_u64(x5, u64(0x0), u64(fiat.u1(x13)))
|
||||
x16, _ := bits.add_u64(x7, (x9 & 0xffffffff00000001), u64(fiat.u1(x15)))
|
||||
out1[0] = x10
|
||||
out1[1] = x12
|
||||
out1[2] = x14
|
||||
out1[3] = x16
|
||||
}
|
||||
|
||||
fe_one :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element) {
|
||||
out1[0] = 0x1
|
||||
out1[1] = 0xffffffff00000000
|
||||
out1[2] = 0xffffffffffffffff
|
||||
out1[3] = 0xfffffffe
|
||||
}
|
||||
|
||||
fe_non_zero :: proc "contextless" (arg1: ^Montgomery_Domain_Field_Element) -> u64 {
|
||||
return arg1[0] | (arg1[1] | (arg1[2] | arg1[3]))
|
||||
}
|
||||
|
||||
@(optimization_mode = "none")
|
||||
fe_cond_assign :: #force_no_inline proc "contextless" (
|
||||
out1, arg1: ^Montgomery_Domain_Field_Element,
|
||||
arg2: int,
|
||||
) {
|
||||
x1 := fiat.cmovznz_u64(fiat.u1(arg2), out1[0], arg1[0])
|
||||
x2 := fiat.cmovznz_u64(fiat.u1(arg2), out1[1], arg1[1])
|
||||
x3 := fiat.cmovznz_u64(fiat.u1(arg2), out1[2], arg1[2])
|
||||
x4 := fiat.cmovznz_u64(fiat.u1(arg2), out1[3], arg1[3])
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
}
|
||||
|
||||
fe_from_montgomery :: proc "contextless" (
|
||||
out1: ^Non_Montgomery_Domain_Field_Element,
|
||||
arg1: ^Montgomery_Domain_Field_Element,
|
||||
) {
|
||||
x1 := arg1[0]
|
||||
x3, x2 := bits.mul_u64(x1, 0xffffffff00000001)
|
||||
x5, x4 := bits.mul_u64(x1, 0xffffffff)
|
||||
x7, x6 := bits.mul_u64(x1, 0xffffffffffffffff)
|
||||
x8, x9 := bits.add_u64(x7, x4, u64(0x0))
|
||||
_, x11 := bits.add_u64(x1, x6, u64(0x0))
|
||||
x12, x13 := bits.add_u64(u64(0x0), x8, u64(fiat.u1(x11)))
|
||||
x14, x15 := bits.add_u64(x12, arg1[1], u64(0x0))
|
||||
x17, x16 := bits.mul_u64(x14, 0xffffffff00000001)
|
||||
x19, x18 := bits.mul_u64(x14, 0xffffffff)
|
||||
x21, x20 := bits.mul_u64(x14, 0xffffffffffffffff)
|
||||
x22, x23 := bits.add_u64(x21, x18, u64(0x0))
|
||||
_, x25 := bits.add_u64(x14, x20, u64(0x0))
|
||||
x26, x27 := bits.add_u64((u64(fiat.u1(x15)) + (u64(fiat.u1(x13)) + (u64(fiat.u1(x9)) + x5))), x22, u64(fiat.u1(x25)))
|
||||
x28, x29 := bits.add_u64(x2, (u64(fiat.u1(x23)) + x19), u64(fiat.u1(x27)))
|
||||
x30, x31 := bits.add_u64(x3, x16, u64(fiat.u1(x29)))
|
||||
x32, x33 := bits.add_u64(x26, arg1[2], u64(0x0))
|
||||
x34, x35 := bits.add_u64(x28, u64(0x0), u64(fiat.u1(x33)))
|
||||
x36, x37 := bits.add_u64(x30, u64(0x0), u64(fiat.u1(x35)))
|
||||
x39, x38 := bits.mul_u64(x32, 0xffffffff00000001)
|
||||
x41, x40 := bits.mul_u64(x32, 0xffffffff)
|
||||
x43, x42 := bits.mul_u64(x32, 0xffffffffffffffff)
|
||||
x44, x45 := bits.add_u64(x43, x40, u64(0x0))
|
||||
_, x47 := bits.add_u64(x32, x42, u64(0x0))
|
||||
x48, x49 := bits.add_u64(x34, x44, u64(fiat.u1(x47)))
|
||||
x50, x51 := bits.add_u64(x36, (u64(fiat.u1(x45)) + x41), u64(fiat.u1(x49)))
|
||||
x52, x53 := bits.add_u64((u64(fiat.u1(x37)) + (u64(fiat.u1(x31)) + x17)), x38, u64(fiat.u1(x51)))
|
||||
x54, x55 := bits.add_u64(x48, arg1[3], u64(0x0))
|
||||
x56, x57 := bits.add_u64(x50, u64(0x0), u64(fiat.u1(x55)))
|
||||
x58, x59 := bits.add_u64(x52, u64(0x0), u64(fiat.u1(x57)))
|
||||
x61, x60 := bits.mul_u64(x54, 0xffffffff00000001)
|
||||
x63, x62 := bits.mul_u64(x54, 0xffffffff)
|
||||
x65, x64 := bits.mul_u64(x54, 0xffffffffffffffff)
|
||||
x66, x67 := bits.add_u64(x65, x62, u64(0x0))
|
||||
_, x69 := bits.add_u64(x54, x64, u64(0x0))
|
||||
x70, x71 := bits.add_u64(x56, x66, u64(fiat.u1(x69)))
|
||||
x72, x73 := bits.add_u64(x58, (u64(fiat.u1(x67)) + x63), u64(fiat.u1(x71)))
|
||||
x74, x75 := bits.add_u64((u64(fiat.u1(x59)) + (u64(fiat.u1(x53)) + x39)), x60, u64(fiat.u1(x73)))
|
||||
x76 := (u64(fiat.u1(x75)) + x61)
|
||||
x77, x78 := bits.sub_u64(x70, 0xffffffffffffffff, u64(0x0))
|
||||
x79, x80 := bits.sub_u64(x72, 0xffffffff, u64(fiat.u1(x78)))
|
||||
x81, x82 := bits.sub_u64(x74, u64(0x0), u64(fiat.u1(x80)))
|
||||
x83, x84 := bits.sub_u64(x76, 0xffffffff00000001, u64(fiat.u1(x82)))
|
||||
_, x86 := bits.sub_u64(u64(0x0), u64(0x0), u64(fiat.u1(x84)))
|
||||
x87 := fiat.cmovznz_u64(fiat.u1(x86), x77, x70)
|
||||
x88 := fiat.cmovznz_u64(fiat.u1(x86), x79, x72)
|
||||
x89 := fiat.cmovznz_u64(fiat.u1(x86), x81, x74)
|
||||
x90 := fiat.cmovznz_u64(fiat.u1(x86), x83, x76)
|
||||
out1[0] = x87
|
||||
out1[1] = x88
|
||||
out1[2] = x89
|
||||
out1[3] = x90
|
||||
}
|
||||
|
||||
fe_to_montgomery :: proc "contextless" (
|
||||
out1: ^Montgomery_Domain_Field_Element,
|
||||
arg1: ^Non_Montgomery_Domain_Field_Element,
|
||||
) {
|
||||
x1 := arg1[1]
|
||||
x2 := arg1[2]
|
||||
x3 := arg1[3]
|
||||
x4 := arg1[0]
|
||||
x6, x5 := bits.mul_u64(x4, 0x4fffffffd)
|
||||
x8, x7 := bits.mul_u64(x4, 0xfffffffffffffffe)
|
||||
x10, x9 := bits.mul_u64(x4, 0xfffffffbffffffff)
|
||||
x12, x11 := bits.mul_u64(x4, 0x3)
|
||||
x13, x14 := bits.add_u64(x12, x9, u64(0x0))
|
||||
x15, x16 := bits.add_u64(x10, x7, u64(fiat.u1(x14)))
|
||||
x17, x18 := bits.add_u64(x8, x5, u64(fiat.u1(x16)))
|
||||
x20, x19 := bits.mul_u64(x11, 0xffffffff00000001)
|
||||
x22, x21 := bits.mul_u64(x11, 0xffffffff)
|
||||
x24, x23 := bits.mul_u64(x11, 0xffffffffffffffff)
|
||||
x25, x26 := bits.add_u64(x24, x21, u64(0x0))
|
||||
_, x28 := bits.add_u64(x11, x23, u64(0x0))
|
||||
x29, x30 := bits.add_u64(x13, x25, u64(fiat.u1(x28)))
|
||||
x31, x32 := bits.add_u64(x15, (u64(fiat.u1(x26)) + x22), u64(fiat.u1(x30)))
|
||||
x33, x34 := bits.add_u64(x17, x19, u64(fiat.u1(x32)))
|
||||
x35, x36 := bits.add_u64((u64(fiat.u1(x18)) + x6), x20, u64(fiat.u1(x34)))
|
||||
x38, x37 := bits.mul_u64(x1, 0x4fffffffd)
|
||||
x40, x39 := bits.mul_u64(x1, 0xfffffffffffffffe)
|
||||
x42, x41 := bits.mul_u64(x1, 0xfffffffbffffffff)
|
||||
x44, x43 := bits.mul_u64(x1, 0x3)
|
||||
x45, x46 := bits.add_u64(x44, x41, u64(0x0))
|
||||
x47, x48 := bits.add_u64(x42, x39, u64(fiat.u1(x46)))
|
||||
x49, x50 := bits.add_u64(x40, x37, u64(fiat.u1(x48)))
|
||||
x51, x52 := bits.add_u64(x29, x43, u64(0x0))
|
||||
x53, x54 := bits.add_u64(x31, x45, u64(fiat.u1(x52)))
|
||||
x55, x56 := bits.add_u64(x33, x47, u64(fiat.u1(x54)))
|
||||
x57, x58 := bits.add_u64(x35, x49, u64(fiat.u1(x56)))
|
||||
x60, x59 := bits.mul_u64(x51, 0xffffffff00000001)
|
||||
x62, x61 := bits.mul_u64(x51, 0xffffffff)
|
||||
x64, x63 := bits.mul_u64(x51, 0xffffffffffffffff)
|
||||
x65, x66 := bits.add_u64(x64, x61, u64(0x0))
|
||||
_, x68 := bits.add_u64(x51, x63, u64(0x0))
|
||||
x69, x70 := bits.add_u64(x53, x65, u64(fiat.u1(x68)))
|
||||
x71, x72 := bits.add_u64(x55, (u64(fiat.u1(x66)) + x62), u64(fiat.u1(x70)))
|
||||
x73, x74 := bits.add_u64(x57, x59, u64(fiat.u1(x72)))
|
||||
x75, x76 := bits.add_u64(((u64(fiat.u1(x58)) + u64(fiat.u1(x36))) + (u64(fiat.u1(x50)) + x38)), x60, u64(fiat.u1(x74)))
|
||||
x78, x77 := bits.mul_u64(x2, 0x4fffffffd)
|
||||
x80, x79 := bits.mul_u64(x2, 0xfffffffffffffffe)
|
||||
x82, x81 := bits.mul_u64(x2, 0xfffffffbffffffff)
|
||||
x84, x83 := bits.mul_u64(x2, 0x3)
|
||||
x85, x86 := bits.add_u64(x84, x81, u64(0x0))
|
||||
x87, x88 := bits.add_u64(x82, x79, u64(fiat.u1(x86)))
|
||||
x89, x90 := bits.add_u64(x80, x77, u64(fiat.u1(x88)))
|
||||
x91, x92 := bits.add_u64(x69, x83, u64(0x0))
|
||||
x93, x94 := bits.add_u64(x71, x85, u64(fiat.u1(x92)))
|
||||
x95, x96 := bits.add_u64(x73, x87, u64(fiat.u1(x94)))
|
||||
x97, x98 := bits.add_u64(x75, x89, u64(fiat.u1(x96)))
|
||||
x100, x99 := bits.mul_u64(x91, 0xffffffff00000001)
|
||||
x102, x101 := bits.mul_u64(x91, 0xffffffff)
|
||||
x104, x103 := bits.mul_u64(x91, 0xffffffffffffffff)
|
||||
x105, x106 := bits.add_u64(x104, x101, u64(0x0))
|
||||
_, x108 := bits.add_u64(x91, x103, u64(0x0))
|
||||
x109, x110 := bits.add_u64(x93, x105, u64(fiat.u1(x108)))
|
||||
x111, x112 := bits.add_u64(x95, (u64(fiat.u1(x106)) + x102), u64(fiat.u1(x110)))
|
||||
x113, x114 := bits.add_u64(x97, x99, u64(fiat.u1(x112)))
|
||||
x115, x116 := bits.add_u64(((u64(fiat.u1(x98)) + u64(fiat.u1(x76))) + (u64(fiat.u1(x90)) + x78)), x100, u64(fiat.u1(x114)))
|
||||
x118, x117 := bits.mul_u64(x3, 0x4fffffffd)
|
||||
x120, x119 := bits.mul_u64(x3, 0xfffffffffffffffe)
|
||||
x122, x121 := bits.mul_u64(x3, 0xfffffffbffffffff)
|
||||
x124, x123 := bits.mul_u64(x3, 0x3)
|
||||
x125, x126 := bits.add_u64(x124, x121, u64(0x0))
|
||||
x127, x128 := bits.add_u64(x122, x119, u64(fiat.u1(x126)))
|
||||
x129, x130 := bits.add_u64(x120, x117, u64(fiat.u1(x128)))
|
||||
x131, x132 := bits.add_u64(x109, x123, u64(0x0))
|
||||
x133, x134 := bits.add_u64(x111, x125, u64(fiat.u1(x132)))
|
||||
x135, x136 := bits.add_u64(x113, x127, u64(fiat.u1(x134)))
|
||||
x137, x138 := bits.add_u64(x115, x129, u64(fiat.u1(x136)))
|
||||
x140, x139 := bits.mul_u64(x131, 0xffffffff00000001)
|
||||
x142, x141 := bits.mul_u64(x131, 0xffffffff)
|
||||
x144, x143 := bits.mul_u64(x131, 0xffffffffffffffff)
|
||||
x145, x146 := bits.add_u64(x144, x141, u64(0x0))
|
||||
_, x148 := bits.add_u64(x131, x143, u64(0x0))
|
||||
x149, x150 := bits.add_u64(x133, x145, u64(fiat.u1(x148)))
|
||||
x151, x152 := bits.add_u64(x135, (u64(fiat.u1(x146)) + x142), u64(fiat.u1(x150)))
|
||||
x153, x154 := bits.add_u64(x137, x139, u64(fiat.u1(x152)))
|
||||
x155, x156 := bits.add_u64(((u64(fiat.u1(x138)) + u64(fiat.u1(x116))) + (u64(fiat.u1(x130)) + x118)), x140, u64(fiat.u1(x154)))
|
||||
x157, x158 := bits.sub_u64(x149, 0xffffffffffffffff, u64(0x0))
|
||||
x159, x160 := bits.sub_u64(x151, 0xffffffff, u64(fiat.u1(x158)))
|
||||
x161, x162 := bits.sub_u64(x153, u64(0x0), u64(fiat.u1(x160)))
|
||||
x163, x164 := bits.sub_u64(x155, 0xffffffff00000001, u64(fiat.u1(x162)))
|
||||
_, x166 := bits.sub_u64(u64(fiat.u1(x156)), u64(0x0), u64(fiat.u1(x164)))
|
||||
x167 := fiat.cmovznz_u64(fiat.u1(x166), x157, x149)
|
||||
x168 := fiat.cmovznz_u64(fiat.u1(x166), x159, x151)
|
||||
x169 := fiat.cmovznz_u64(fiat.u1(x166), x161, x153)
|
||||
x170 := fiat.cmovznz_u64(fiat.u1(x166), x163, x155)
|
||||
out1[0] = x167
|
||||
out1[1] = x168
|
||||
out1[2] = x169
|
||||
out1[3] = x170
|
||||
}
|
||||
436
core/crypto/_fiat/field_p384r1/field.odin
Normal file
436
core/crypto/_fiat/field_p384r1/field.odin
Normal file
@@ -0,0 +1,436 @@
|
||||
package field_p384r1
|
||||
|
||||
import "core:crypto"
|
||||
import subtle "core:crypto/_subtle"
|
||||
import "core:encoding/endian"
|
||||
import "core:math/bits"
|
||||
|
||||
fe_clear :: proc "contextless" (arg1: ^Montgomery_Domain_Field_Element) {
|
||||
crypto.zero_explicit(arg1, size_of(Montgomery_Domain_Field_Element))
|
||||
}
|
||||
|
||||
fe_clear_vec :: proc "contextless" (
|
||||
arg1: []^Montgomery_Domain_Field_Element,
|
||||
) {
|
||||
for fe in arg1 {
|
||||
fe_clear(fe)
|
||||
}
|
||||
}
|
||||
|
||||
fe_from_bytes :: proc "contextless" (
|
||||
out1: ^Montgomery_Domain_Field_Element,
|
||||
arg1: []byte,
|
||||
unsafe_assume_canonical := false,
|
||||
) -> bool {
|
||||
ensure_contextless(len(arg1) == 48, "p384r1: invalid fe input buffer")
|
||||
|
||||
// Note: We assume the input is in big-endian.
|
||||
tmp := Non_Montgomery_Domain_Field_Element {
|
||||
endian.unchecked_get_u64be(arg1[40:]),
|
||||
endian.unchecked_get_u64be(arg1[32:]),
|
||||
endian.unchecked_get_u64be(arg1[24:]),
|
||||
endian.unchecked_get_u64be(arg1[16:]),
|
||||
endian.unchecked_get_u64be(arg1[8:]),
|
||||
endian.unchecked_get_u64be(arg1[0:]),
|
||||
}
|
||||
defer crypto.zero_explicit(&tmp, size_of(tmp))
|
||||
|
||||
// Check that tmp is in the the range [0, ELL).
|
||||
if !unsafe_assume_canonical {
|
||||
_, borrow := bits.sub_u64(ELL[0] - 1, tmp[0], 0)
|
||||
_, borrow = bits.sub_u64(ELL[1], tmp[1], borrow)
|
||||
_, borrow = bits.sub_u64(ELL[2], tmp[2], borrow)
|
||||
_, borrow = bits.sub_u64(ELL[3], tmp[3], borrow)
|
||||
_, borrow = bits.sub_u64(ELL[4], tmp[4], borrow)
|
||||
_, borrow = bits.sub_u64(ELL[5], tmp[5], borrow)
|
||||
if borrow != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
fe_to_montgomery(out1, &tmp)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fe_to_bytes :: proc "contextless" (out1: []byte, arg1: ^Montgomery_Domain_Field_Element) {
|
||||
ensure_contextless(len(out1) == 48, "p384r1: invalid fe output buffer")
|
||||
|
||||
tmp: Non_Montgomery_Domain_Field_Element = ---
|
||||
fe_from_montgomery(&tmp, arg1)
|
||||
|
||||
// Note: Likewise, output in big-endian.
|
||||
endian.unchecked_put_u64be(out1[40:], tmp[0])
|
||||
endian.unchecked_put_u64be(out1[32:], tmp[1])
|
||||
endian.unchecked_put_u64be(out1[24:], tmp[2])
|
||||
endian.unchecked_put_u64be(out1[16:], tmp[3])
|
||||
endian.unchecked_put_u64be(out1[8:], tmp[4])
|
||||
endian.unchecked_put_u64be(out1[0:], tmp[5])
|
||||
|
||||
crypto.zero_explicit(&tmp, size_of(tmp))
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
fe_equal :: proc "contextless" (arg1, arg2: ^Montgomery_Domain_Field_Element) -> int {
|
||||
tmp: Montgomery_Domain_Field_Element = ---
|
||||
fe_sub(&tmp, arg1, arg2)
|
||||
|
||||
// This will only underflow if and only if (⟺) arg1 == arg2, and we return the borrow,
|
||||
// which will be 1.
|
||||
is_eq := subtle.u64_is_zero(fe_non_zero(&tmp))
|
||||
|
||||
fe_clear(&tmp)
|
||||
|
||||
return int(is_eq)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
fe_is_odd :: proc "contextless" (arg1: ^Montgomery_Domain_Field_Element) -> int {
|
||||
tmp: Non_Montgomery_Domain_Field_Element = ---
|
||||
defer crypto.zero_explicit(&tmp, size_of(tmp))
|
||||
|
||||
fe_from_montgomery(&tmp, arg1)
|
||||
return int(tmp[0] & 1)
|
||||
}
|
||||
|
||||
fe_pow2k :: proc "contextless" (
|
||||
out1: ^Montgomery_Domain_Field_Element,
|
||||
arg1: ^Montgomery_Domain_Field_Element,
|
||||
arg2: uint,
|
||||
) {
|
||||
// Special case: `arg1^(2 * 0) = 1`, though this should never happen.
|
||||
if arg2 == 0 {
|
||||
fe_one(out1)
|
||||
return
|
||||
}
|
||||
|
||||
fe_square(out1, arg1)
|
||||
for _ in 1 ..< arg2 {
|
||||
fe_square(out1, out1)
|
||||
}
|
||||
}
|
||||
|
||||
fe_inv :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
|
||||
// Inversion computation is derived from the addition chain:
|
||||
//
|
||||
// _10 = 2*1
|
||||
// _11 = 1 + _10
|
||||
// _110 = 2*_11
|
||||
// _111 = 1 + _110
|
||||
// _111000 = _111 << 3
|
||||
// _111111 = _111 + _111000
|
||||
// x12 = _111111 << 6 + _111111
|
||||
// x24 = x12 << 12 + x12
|
||||
// x30 = x24 << 6 + _111111
|
||||
// x31 = 2*x30 + 1
|
||||
// x32 = 2*x31 + 1
|
||||
// x63 = x32 << 31 + x31
|
||||
// x126 = x63 << 63 + x63
|
||||
// x252 = x126 << 126 + x126
|
||||
// x255 = x252 << 3 + _111
|
||||
// return ((x255 << 33 + x32) << 94 + x30) << 2
|
||||
//
|
||||
// Operations: 383 squares 14 multiplies
|
||||
//
|
||||
// Generated by github.com/mmcloughlin/addchain v0.4.0.
|
||||
|
||||
// Note: Need to stash `arg1` (`xx`) in the case that `out1`/`arg1` alias,
|
||||
// as `arg1` is used after `out1` has been altered.
|
||||
t0, t1, t2, t3, xx: Montgomery_Domain_Field_Element = ---, ---, ---, ---, arg1^
|
||||
|
||||
// Step 1: z = x^0x2
|
||||
fe_square(out1, arg1)
|
||||
|
||||
// Step 2: z = x^0x3
|
||||
fe_mul(out1, &xx, out1)
|
||||
|
||||
// Step 3: z = x^0x6
|
||||
fe_square(out1, out1)
|
||||
|
||||
// Step 4: t1 = x^0x7
|
||||
fe_mul(&t1, &xx, out1)
|
||||
|
||||
// Step 7: z = x^0x38
|
||||
fe_pow2k(out1, &t1, 3)
|
||||
|
||||
// Step 8: z = x^0x3f
|
||||
fe_mul(out1, &t1, out1)
|
||||
|
||||
// Step 14: t0 = x^0xfc0
|
||||
fe_pow2k(&t0, out1, 6)
|
||||
|
||||
// Step 15: t0 = x^0xfff
|
||||
fe_mul(&t0, out1, &t0)
|
||||
|
||||
// Step 27: t2 = x^0xfff000
|
||||
fe_pow2k(&t2, &t0, 12)
|
||||
|
||||
// Step 28: t0 = x^0xffffff
|
||||
fe_mul(&t0, &t0, &t2)
|
||||
|
||||
// Step 34: t0 = x^0x3fffffc0
|
||||
fe_pow2k(&t0, &t0, 6)
|
||||
|
||||
// Step 35: z = x^0x3fffffff
|
||||
fe_mul(out1, out1, &t0)
|
||||
|
||||
// Step 36: t0 = x^0x7ffffffe
|
||||
fe_square(&t0, out1)
|
||||
|
||||
// Step 37: t2 = x^0x7fffffff
|
||||
fe_mul(&t2, &xx, &t0)
|
||||
|
||||
// Step 38: t0 = x^0xfffffffe
|
||||
fe_square(&t0, &t2)
|
||||
|
||||
// Step 39: t0 = x^0xffffffff
|
||||
fe_mul(&t0, &xx, &t0)
|
||||
|
||||
// Step 70: t3 = x^0x7fffffff80000000
|
||||
fe_pow2k(&t3, &t0, 31)
|
||||
|
||||
// Step 71: t2 = x^0x7fffffffffffffff
|
||||
fe_mul(&t2, &t2, &t3)
|
||||
|
||||
// Step 134: t3 = x^0x3fffffffffffffff8000000000000000
|
||||
fe_pow2k(&t3, &t2, 63)
|
||||
|
||||
// Step 135: t2 = x^0x3fffffffffffffffffffffffffffffff
|
||||
fe_mul(&t2, &t2, &t3)
|
||||
|
||||
// Step 261: t3 = x^0xfffffffffffffffffffffffffffffffc0000000000000000000000000000000
|
||||
fe_pow2k(&t3, &t2, 126)
|
||||
|
||||
// Step 262: t2 = x^0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
fe_mul(&t2, &t2, &t3)
|
||||
|
||||
// Step 265: t2 = x^0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8
|
||||
fe_pow2k(&t2, &t2, 3)
|
||||
|
||||
// Step 266: t1 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
fe_mul(&t1, &t1, &t2)
|
||||
|
||||
// Step 299: t1 = x^0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00000000
|
||||
fe_pow2k(&t1, &t1, 33)
|
||||
|
||||
// Step 300: t0 = x^0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff
|
||||
fe_mul(&t0, &t0, &t1)
|
||||
|
||||
// Step 394: t0 = x^0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffffffc00000000000000000000000
|
||||
fe_pow2k(&t0, &t0, 94)
|
||||
|
||||
// Step 395: z = x^0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffffffc0000000000000003fffffff
|
||||
fe_mul(out1, out1, &t0)
|
||||
|
||||
// Step 397: z = x^0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffffc
|
||||
fe_pow2k(out1, out1, 2)
|
||||
|
||||
fe_mul(out1, out1, &xx)
|
||||
|
||||
fe_clear_vec([]^Montgomery_Domain_Field_Element{&t0, &t1, &t2, &t3, &xx})
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
fe_sqrt :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) -> int {
|
||||
// Square root candidate can be derived via exponentiation by `(p + 1) / 4`
|
||||
// From sage: 9850501549098619803069760025035903451269934817616361666987073351061430442874217582261816522064734500465401743278080
|
||||
//
|
||||
// Inversion computation is derived from the addition chain:
|
||||
//
|
||||
// _10 = 2*1
|
||||
// _11 = 1 + _10
|
||||
// _110 = 2*_11
|
||||
// _111 = 1 + _110
|
||||
// _111000 = _111 << 3
|
||||
// _111111 = _111 + _111000
|
||||
// x12 = _111111 << 6 + _111111
|
||||
// x24 = x12 << 12 + x12
|
||||
// x30 = x24 << 6 + _111111
|
||||
// x31 = 2*x30 + 1
|
||||
// x32 = 2*x31 + 1
|
||||
// x63 = x32 << 31 + x31
|
||||
// x126 = x63 << 63 + x63
|
||||
// x252 = x126 << 126 + x126
|
||||
// x255 = x252 << 3 + _111
|
||||
// return ((x255 << 33 + x32) << 94 + x30) << 2
|
||||
//
|
||||
// Operations: 383 squares 14 multiplies
|
||||
//
|
||||
// Generated by github.com/mmcloughlin/addchain v0.4.0.
|
||||
|
||||
// Likewise this tramples over arg1, so stash another copy.
|
||||
t0, t1, t2, t3, xx: Montgomery_Domain_Field_Element = ---, ---, ---, ---, arg1^
|
||||
|
||||
// Step 1: z = x^0x2
|
||||
fe_square(out1, arg1)
|
||||
|
||||
// Step 2: z = x^0x3
|
||||
fe_mul(out1, &xx, out1)
|
||||
|
||||
// Step 3: z = x^0x6
|
||||
fe_square(out1, out1)
|
||||
|
||||
// Step 4: t1 = x^0x7
|
||||
fe_mul(&t1, &xx, out1)
|
||||
|
||||
// Step 7: z = x^0x38
|
||||
fe_pow2k(out1, &t1, 3)
|
||||
|
||||
// Step 8: z = x^0x3f
|
||||
fe_mul(out1, &t1, out1)
|
||||
|
||||
// Step 14: t0 = x^0xfc0
|
||||
fe_pow2k(&t0, out1, 6)
|
||||
|
||||
// Step 15: t0 = x^0xfff
|
||||
fe_mul(&t0, out1, &t0)
|
||||
|
||||
// Step 27: t2 = x^0xfff000
|
||||
fe_pow2k(&t2, &t0, 12)
|
||||
|
||||
// Step 28: t0 = x^0xffffff
|
||||
fe_mul(&t0, &t0, &t2)
|
||||
|
||||
// Step 34: t0 = x^0x3fffffc0
|
||||
fe_pow2k(&t0, &t0, 6)
|
||||
|
||||
// Step 35: z = x^0x3fffffff
|
||||
fe_mul(out1, out1, &t0)
|
||||
|
||||
// Step 36: t0 = x^0x7ffffffe
|
||||
fe_square(&t0, out1)
|
||||
|
||||
// Step 37: t2 = x^0x7fffffff
|
||||
fe_mul(&t2, &xx, &t0)
|
||||
|
||||
// Step 38: t0 = x^0xfffffffe
|
||||
fe_square(&t0, &t2)
|
||||
|
||||
// Step 39: t0 = x^0xffffffff
|
||||
fe_mul(&t0, &xx, &t0)
|
||||
|
||||
// Step 70: t3 = x^0x7fffffff80000000
|
||||
fe_pow2k(&t3, &t0, 31)
|
||||
|
||||
// Step 71: t2 = x^0x7fffffffffffffff
|
||||
fe_mul(&t2, &t2, &t3)
|
||||
|
||||
// Step 134: t3 = x^0x3fffffffffffffff8000000000000000
|
||||
fe_pow2k(&t3, &t2, 63)
|
||||
|
||||
// Step 135: t2 = x^0x3fffffffffffffffffffffffffffffff
|
||||
fe_mul(&t2, &t2, &t3)
|
||||
|
||||
// Step 261: t3 = x^0xfffffffffffffffffffffffffffffffc0000000000000000000000000000000
|
||||
fe_pow2k(&t3, &t2, 126)
|
||||
|
||||
// Step 262: t2 = x^0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
fe_mul(&t2, &t2, &t3)
|
||||
|
||||
// Step 265: t2 = x^0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8
|
||||
fe_pow2k(&t2, &t2, 3)
|
||||
|
||||
// Step 266: t1 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
fe_mul(&t1, &t1, &t2)
|
||||
|
||||
// Step 299: t1 = x^0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00000000
|
||||
fe_pow2k(&t1, &t1, 33)
|
||||
|
||||
// Step 300: t0 = x^0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff
|
||||
fe_mul(&t0, &t0, &t1)
|
||||
|
||||
// Step 394: t0 = x^0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffffffc00000000000000000000000
|
||||
fe_pow2k(&t0, &t0, 94)
|
||||
|
||||
// Step 395: z = x^0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffffffc0000000000000003fffffff
|
||||
fe_mul(out1, out1, &t0)
|
||||
|
||||
// Step 397: z = x^0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffffc
|
||||
fe_pow2k(out1, out1, 2)
|
||||
|
||||
// Ensure that our candidate is actually the square root.
|
||||
check, zero: Montgomery_Domain_Field_Element
|
||||
fe_square(&check, out1)
|
||||
|
||||
is_valid := fe_equal(&check, &xx)
|
||||
fe_cond_select(out1, &zero, out1, is_valid)
|
||||
|
||||
fe_clear_vec([]^Montgomery_Domain_Field_Element{&t0, &t1, &t2, &t3, &xx, &check})
|
||||
|
||||
return is_valid
|
||||
}
|
||||
|
||||
fe_zero :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element) {
|
||||
out1[0] = 0
|
||||
out1[1] = 0
|
||||
out1[2] = 0
|
||||
out1[3] = 0
|
||||
out1[4] = 0
|
||||
out1[5] = 0
|
||||
}
|
||||
|
||||
fe_set :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
|
||||
x1 := arg1[0]
|
||||
x2 := arg1[1]
|
||||
x3 := arg1[2]
|
||||
x4 := arg1[3]
|
||||
x5 := arg1[4]
|
||||
x6 := arg1[5]
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
out1[4] = x5
|
||||
out1[5] = x6
|
||||
}
|
||||
|
||||
@(optimization_mode = "none")
|
||||
fe_cond_swap :: #force_no_inline proc "contextless" (out1, out2: ^Montgomery_Domain_Field_Element, arg1: int) {
|
||||
mask := (u64(arg1) * 0xffffffffffffffff)
|
||||
x := (out1[0] ~ out2[0]) & mask
|
||||
x1, y1 := out1[0] ~ x, out2[0] ~ x
|
||||
x = (out1[1] ~ out2[1]) & mask
|
||||
x2, y2 := out1[1] ~ x, out2[1] ~ x
|
||||
x = (out1[2] ~ out2[2]) & mask
|
||||
x3, y3 := out1[2] ~ x, out2[2] ~ x
|
||||
x = (out1[3] ~ out2[3]) & mask
|
||||
x4, y4 := out1[3] ~ x, out2[3] ~ x
|
||||
x = (out1[4] ~ out2[4]) & mask
|
||||
x5, y5 := out1[4] ~ x, out2[4] ~ x
|
||||
x = (out1[5] ~ out2[5]) & mask
|
||||
x6, y6 := out1[5] ~ x, out2[5] ~ x
|
||||
out1[0], out2[0] = x1, y1
|
||||
out1[1], out2[1] = x2, y2
|
||||
out1[2], out2[2] = x3, y3
|
||||
out1[3], out2[3] = x4, y4
|
||||
out1[4], out2[4] = x5, y5
|
||||
out1[5], out2[5] = x6, y6
|
||||
}
|
||||
|
||||
@(optimization_mode = "none")
|
||||
fe_cond_select :: #force_no_inline proc "contextless" (
|
||||
out1, arg1, arg2: ^Montgomery_Domain_Field_Element,
|
||||
arg3: int,
|
||||
) {
|
||||
mask := (u64(arg3) * 0xffffffffffffffff)
|
||||
x1 := ((mask & arg2[0]) | ((~mask) & arg1[0]))
|
||||
x2 := ((mask & arg2[1]) | ((~mask) & arg1[1]))
|
||||
x3 := ((mask & arg2[2]) | ((~mask) & arg1[2]))
|
||||
x4 := ((mask & arg2[3]) | ((~mask) & arg1[3]))
|
||||
x5 := ((mask & arg2[4]) | ((~mask) & arg1[4]))
|
||||
x6 := ((mask & arg2[5]) | ((~mask) & arg1[5]))
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
out1[4] = x5
|
||||
out1[5] = x6
|
||||
}
|
||||
|
||||
fe_cond_negate :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element, ctrl: int) {
|
||||
tmp1: Montgomery_Domain_Field_Element = ---
|
||||
fe_opp(&tmp1, arg1)
|
||||
fe_cond_select(out1, arg1, &tmp1, ctrl)
|
||||
|
||||
fe_clear(&tmp1)
|
||||
}
|
||||
1063
core/crypto/_fiat/field_p384r1/field64.odin
Normal file
1063
core/crypto/_fiat/field_p384r1/field64.odin
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
package field_poly1305
|
||||
|
||||
import "core:crypto"
|
||||
import "core:encoding/endian"
|
||||
import "core:mem"
|
||||
|
||||
fe_relax_cast :: #force_inline proc "contextless" (
|
||||
arg1: ^Tight_Field_Element,
|
||||
@@ -57,7 +57,7 @@ fe_from_u64s :: proc "contextless" (out1: ^Tight_Field_Element, lo, hi: u64) {
|
||||
_fe_from_bytes(out1, &tmp)
|
||||
|
||||
// This routine is only used to deserialize `r` which is confidential.
|
||||
mem.zero_explicit(&tmp, size_of(tmp))
|
||||
crypto.zero_explicit(&tmp, size_of(tmp))
|
||||
}
|
||||
|
||||
fe_zero :: proc "contextless" (out1: ^Tight_Field_Element) {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package field_scalar25519
|
||||
|
||||
import "core:crypto"
|
||||
import subtle "core:crypto/_subtle"
|
||||
import "core:encoding/endian"
|
||||
import "core:math/bits"
|
||||
import "core:mem"
|
||||
|
||||
@(private, rodata)
|
||||
_TWO_168 := Montgomery_Domain_Field_Element {
|
||||
@@ -20,7 +21,7 @@ _TWO_336 := Montgomery_Domain_Field_Element {
|
||||
}
|
||||
|
||||
fe_clear :: proc "contextless" (arg1: ^Montgomery_Domain_Field_Element) {
|
||||
mem.zero_explicit(arg1, size_of(Montgomery_Domain_Field_Element))
|
||||
crypto.zero_explicit(arg1, size_of(Montgomery_Domain_Field_Element))
|
||||
}
|
||||
|
||||
fe_from_bytes :: proc "contextless" (
|
||||
@@ -34,7 +35,7 @@ fe_from_bytes :: proc "contextless" (
|
||||
endian.unchecked_get_u64le(arg1[16:]),
|
||||
endian.unchecked_get_u64le(arg1[24:]),
|
||||
}
|
||||
defer mem.zero_explicit(&tmp, size_of(tmp))
|
||||
defer crypto.zero_explicit(&tmp, size_of(tmp))
|
||||
|
||||
// Check that tmp is in the the range [0, ELL).
|
||||
if !unsafe_assume_canonical {
|
||||
@@ -66,7 +67,7 @@ fe_from_bytes_rfc8032 :: proc "contextless" (
|
||||
|
||||
fe_from_bytes_wide(out1, &tmp)
|
||||
|
||||
mem.zero_explicit(&tmp, size_of(tmp))
|
||||
crypto.zero_explicit(&tmp, size_of(tmp))
|
||||
}
|
||||
|
||||
fe_from_bytes_wide :: proc "contextless" (
|
||||
@@ -100,7 +101,7 @@ _fe_from_bytes_short :: proc "contextless" (out1: ^Montgomery_Domain_Field_Eleme
|
||||
copy(tmp[:], arg1)
|
||||
|
||||
_ = fe_from_bytes(out1, &tmp, true)
|
||||
mem.zero_explicit(&tmp, size_of(tmp))
|
||||
crypto.zero_explicit(&tmp, size_of(tmp))
|
||||
}
|
||||
|
||||
fe_to_bytes :: proc "contextless" (out1: []byte, arg1: ^Montgomery_Domain_Field_Element) {
|
||||
@@ -114,20 +115,18 @@ fe_to_bytes :: proc "contextless" (out1: []byte, arg1: ^Montgomery_Domain_Field_
|
||||
endian.unchecked_put_u64le(out1[16:], tmp[2])
|
||||
endian.unchecked_put_u64le(out1[24:], tmp[3])
|
||||
|
||||
mem.zero_explicit(&tmp, size_of(tmp))
|
||||
crypto.zero_explicit(&tmp, size_of(tmp))
|
||||
}
|
||||
|
||||
fe_equal :: proc "contextless" (arg1, arg2: ^Montgomery_Domain_Field_Element) -> int {
|
||||
tmp: Montgomery_Domain_Field_Element
|
||||
fe_sub(&tmp, arg1, arg2)
|
||||
|
||||
// This will only underflow iff arg1 == arg2, and we return the borrow,
|
||||
// which will be 1.
|
||||
_, borrow := bits.sub_u64(fe_non_zero(&tmp), 1, 0)
|
||||
is_eq := subtle.eq(fe_non_zero(&tmp), 0)
|
||||
|
||||
fe_clear(&tmp)
|
||||
|
||||
return int(borrow)
|
||||
return int(is_eq)
|
||||
}
|
||||
|
||||
fe_zero :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element) {
|
||||
|
||||
478
core/crypto/_fiat/field_scalarp256r1/field.odin
Normal file
478
core/crypto/_fiat/field_scalarp256r1/field.odin
Normal file
@@ -0,0 +1,478 @@
|
||||
package field_scalarp256r1
|
||||
|
||||
import "core:crypto"
|
||||
import subtle "core:crypto/_subtle"
|
||||
import "core:encoding/endian"
|
||||
import "core:math/bits"
|
||||
|
||||
@(private, rodata)
|
||||
TWO_192 := Montgomery_Domain_Field_Element{
|
||||
2482910415990817935,
|
||||
2879494685571067143,
|
||||
8732918506673730078,
|
||||
85565669603516024,
|
||||
}
|
||||
@(private, rodata)
|
||||
TWO_384 := Montgomery_Domain_Field_Element{
|
||||
2127524300190691059,
|
||||
17014302137236182484,
|
||||
16604910261202196099,
|
||||
3621421107472562910,
|
||||
}
|
||||
// 2^384 % p (From sage)
|
||||
// 0x431905529c0166ce652e96b7ccca0a99679b73e19ad16947f01cf013fc632551
|
||||
|
||||
fe_clear :: proc "contextless" (arg1: ^Montgomery_Domain_Field_Element) {
|
||||
crypto.zero_explicit(arg1, size_of(Montgomery_Domain_Field_Element))
|
||||
}
|
||||
|
||||
fe_clear_vec :: proc "contextless" (
|
||||
arg1: []^Montgomery_Domain_Field_Element,
|
||||
) {
|
||||
for fe in arg1 {
|
||||
fe_clear(fe)
|
||||
}
|
||||
}
|
||||
|
||||
fe_from_bytes :: proc "contextless" (
|
||||
out1: ^Montgomery_Domain_Field_Element,
|
||||
arg1: []byte,
|
||||
) -> bool {
|
||||
ensure_contextless(len(out1) <= 64, "p256r1: invalid scalar input buffer")
|
||||
|
||||
is_canonical := false
|
||||
s_len := len(arg1)
|
||||
switch {
|
||||
case s_len < 32:
|
||||
// No way this can be greater than the order.
|
||||
fe_unchecked_set(out1, arg1)
|
||||
is_canonical = true
|
||||
case s_len == 32:
|
||||
// It is quite likely that a reduction mod p is required,
|
||||
// as the order of the curve is sufficiently smaller than
|
||||
// 2^256-1, so just check if we actually needed to reduced
|
||||
// and do the reduction anyway, so that things that require
|
||||
// canonical scalars can reject non-canonical encodings.
|
||||
is_canonical = fe_is_canonical(arg1)
|
||||
fallthrough
|
||||
case:
|
||||
// Use Frank Denis' trick, as documented by Filippo Valsorda
|
||||
// at https://words.filippo.io/dispatches/wide-reduction/
|
||||
//
|
||||
// "I represent the value as a+b*2^192+c*2^384"
|
||||
//
|
||||
// Note: Omitting the `c` computation is fine as, reduction
|
||||
// being length dependent provides no useful timing information.
|
||||
|
||||
// Zero extend to 512-bits.
|
||||
src_512: [64]byte
|
||||
copy(src_512[64-s_len:], arg1)
|
||||
defer crypto.zero_explicit(&src_512, size_of(src_512))
|
||||
|
||||
fe_unchecked_set(out1, src_512[40:]) // a
|
||||
b: Montgomery_Domain_Field_Element
|
||||
fe_unchecked_set(&b, src_512[16:40]) // b
|
||||
|
||||
fe_mul(&b, &b, &TWO_192)
|
||||
fe_add(out1, out1, &b)
|
||||
if s_len >= 48 {
|
||||
c: Montgomery_Domain_Field_Element
|
||||
fe_unchecked_set(&c, src_512[:16]) // c
|
||||
fe_mul(&c, &c, &TWO_384)
|
||||
fe_add(out1, out1, &c)
|
||||
|
||||
fe_clear(&c)
|
||||
}
|
||||
|
||||
fe_clear(&b)
|
||||
}
|
||||
|
||||
return !is_canonical
|
||||
}
|
||||
|
||||
@(private)
|
||||
fe_is_canonical :: proc "contextless" (arg1: []byte) -> bool {
|
||||
_, borrow := bits.sub_u64(ELL[0] - 1, endian.unchecked_get_u64be(arg1[24:]), 0)
|
||||
_, borrow = bits.sub_u64(ELL[1], endian.unchecked_get_u64be(arg1[16:]), borrow)
|
||||
_, borrow = bits.sub_u64(ELL[2], endian.unchecked_get_u64be(arg1[8:]), borrow)
|
||||
_, borrow = bits.sub_u64(ELL[3], endian.unchecked_get_u64be(arg1[0:]), borrow)
|
||||
return borrow == 0
|
||||
}
|
||||
|
||||
@(private)
|
||||
fe_unchecked_set :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element, arg1: []byte) {
|
||||
arg1_256: [32]byte
|
||||
defer crypto.zero_explicit(&arg1_256, size_of(arg1_256))
|
||||
copy(arg1_256[32-len(arg1):], arg1)
|
||||
|
||||
tmp := Non_Montgomery_Domain_Field_Element {
|
||||
endian.unchecked_get_u64be(arg1_256[24:]),
|
||||
endian.unchecked_get_u64be(arg1_256[16:]),
|
||||
endian.unchecked_get_u64be(arg1_256[8:]),
|
||||
endian.unchecked_get_u64be(arg1_256[0:]),
|
||||
}
|
||||
defer crypto.zero_explicit(&tmp, size_of(tmp))
|
||||
|
||||
fe_to_montgomery(out1, &tmp)
|
||||
}
|
||||
|
||||
fe_to_bytes :: proc "contextless" (out1: []byte, arg1: ^Montgomery_Domain_Field_Element) {
|
||||
ensure_contextless(len(out1) == 32, "p256r1: invalid scalar output buffer")
|
||||
|
||||
tmp: Non_Montgomery_Domain_Field_Element = ---
|
||||
fe_from_montgomery(&tmp, arg1)
|
||||
|
||||
// Note: Likewise, output in big-endian.
|
||||
endian.unchecked_put_u64be(out1[24:], tmp[0])
|
||||
endian.unchecked_put_u64be(out1[16:], tmp[1])
|
||||
endian.unchecked_put_u64be(out1[8:], tmp[2])
|
||||
endian.unchecked_put_u64be(out1[0:], tmp[3])
|
||||
|
||||
crypto.zero_explicit(&tmp, size_of(tmp))
|
||||
}
|
||||
|
||||
fe_equal :: proc "contextless" (arg1, arg2: ^Montgomery_Domain_Field_Element) -> int {
|
||||
tmp: Montgomery_Domain_Field_Element = ---
|
||||
fe_sub(&tmp, arg1, arg2)
|
||||
|
||||
is_eq := subtle.eq(fe_non_zero(&tmp), 0)
|
||||
|
||||
fe_clear(&tmp)
|
||||
|
||||
return int(is_eq)
|
||||
}
|
||||
|
||||
fe_is_odd :: proc "contextless" (arg1: ^Montgomery_Domain_Field_Element) -> int {
|
||||
tmp: Non_Montgomery_Domain_Field_Element = ---
|
||||
defer crypto.zero_explicit(&tmp, size_of(tmp))
|
||||
|
||||
fe_from_montgomery(&tmp, arg1)
|
||||
return int(tmp[0] & 1)
|
||||
}
|
||||
|
||||
fe_zero :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element) {
|
||||
out1[0] = 0
|
||||
out1[1] = 0
|
||||
out1[2] = 0
|
||||
out1[3] = 0
|
||||
}
|
||||
|
||||
fe_set :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
|
||||
x1 := arg1[0]
|
||||
x2 := arg1[1]
|
||||
x3 := arg1[2]
|
||||
x4 := arg1[3]
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
}
|
||||
|
||||
@(optimization_mode = "none")
|
||||
fe_cond_swap :: #force_no_inline proc "contextless" (out1, out2: ^Montgomery_Domain_Field_Element, arg1: int) {
|
||||
mask := (u64(arg1) * 0xffffffffffffffff)
|
||||
x := (out1[0] ~ out2[0]) & mask
|
||||
x1, y1 := out1[0] ~ x, out2[0] ~ x
|
||||
x = (out1[1] ~ out2[1]) & mask
|
||||
x2, y2 := out1[1] ~ x, out2[1] ~ x
|
||||
x = (out1[2] ~ out2[2]) & mask
|
||||
x3, y3 := out1[2] ~ x, out2[2] ~ x
|
||||
x = (out1[3] ~ out2[3]) & mask
|
||||
x4, y4 := out1[3] ~ x, out2[3] ~ x
|
||||
out1[0], out2[0] = x1, y1
|
||||
out1[1], out2[1] = x2, y2
|
||||
out1[2], out2[2] = x3, y3
|
||||
out1[3], out2[3] = x4, y4
|
||||
}
|
||||
|
||||
@(optimization_mode = "none")
|
||||
fe_cond_select :: #force_no_inline proc "contextless" (
|
||||
out1, arg1, arg2: ^Montgomery_Domain_Field_Element,
|
||||
arg3: int,
|
||||
) {
|
||||
mask := (u64(arg3) * 0xffffffffffffffff)
|
||||
x1 := ((mask & arg2[0]) | ((~mask) & arg1[0]))
|
||||
x2 := ((mask & arg2[1]) | ((~mask) & arg1[1]))
|
||||
x3 := ((mask & arg2[2]) | ((~mask) & arg1[2]))
|
||||
x4 := ((mask & arg2[3]) | ((~mask) & arg1[3]))
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
}
|
||||
|
||||
fe_cond_negate :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element, ctrl: int) {
|
||||
tmp1: Montgomery_Domain_Field_Element = ---
|
||||
fe_opp(&tmp1, arg1)
|
||||
fe_cond_select(out1, arg1, &tmp1, ctrl)
|
||||
|
||||
fe_clear(&tmp1)
|
||||
}
|
||||
|
||||
fe_pow2k :: proc "contextless" (
|
||||
out1: ^Montgomery_Domain_Field_Element,
|
||||
arg1: ^Montgomery_Domain_Field_Element,
|
||||
arg2: uint,
|
||||
) {
|
||||
// Special case: `arg1^(2 * 0) = 1`, though this should never happen.
|
||||
if arg2 == 0 {
|
||||
fe_one(out1)
|
||||
return
|
||||
}
|
||||
|
||||
fe_square(out1, arg1)
|
||||
for _ in 1 ..< arg2 {
|
||||
fe_square(out1, out1)
|
||||
}
|
||||
}
|
||||
|
||||
fe_inv :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
|
||||
// Inversion computation is derived from the addition chain:
|
||||
//
|
||||
// _10 = 2*1
|
||||
// _100 = 2*_10
|
||||
// _101 = 1 + _100
|
||||
// _110 = 1 + _101
|
||||
// _1001 = _100 + _101
|
||||
// _1111 = _110 + _1001
|
||||
// _10010 = 2*_1001
|
||||
// _10101 = _110 + _1111
|
||||
// _11000 = _110 + _10010
|
||||
// _11010 = _10 + _11000
|
||||
// _101111 = _10101 + _11010
|
||||
// _111000 = _1001 + _101111
|
||||
// _111101 = _101 + _111000
|
||||
// _111111 = _10 + _111101
|
||||
// _1001111 = _10010 + _111101
|
||||
// _1100001 = _10010 + _1001111
|
||||
// _1100011 = _10 + _1100001
|
||||
// _1110011 = _10010 + _1100001
|
||||
// _1110111 = _100 + _1110011
|
||||
// _1111101 = _110 + _1110111
|
||||
// _10010101 = _11000 + _1111101
|
||||
// _10100111 = _10010 + _10010101
|
||||
// _10101101 = _110 + _10100111
|
||||
// _11100101 = _111000 + _10101101
|
||||
// _11111111 = _11010 + _11100101
|
||||
// x16 = _11111111 << 8 + _11111111
|
||||
// x32 = x16 << 16 + x16
|
||||
// i133 = ((x32 << 48 + x16) << 16 + x16) << 16
|
||||
// i158 = ((x16 + i133) << 16 + x16) << 6 + _101111
|
||||
// i186 = ((i158 << 9 + _1110011) << 8 + _1111101) << 9
|
||||
// i206 = ((_10101101 + i186) << 8 + _10100111) << 9 + _101111
|
||||
// i236 = ((i206 << 8 + _111101) << 11 + _1001111) << 9
|
||||
// i257 = ((_1110111 + i236) << 10 + _11100101) << 8 + _1100001
|
||||
// i286 = ((i257 << 7 + _111111) << 10 + _1100011) << 10
|
||||
// return (_10010101 + i286) << 6 + _1111
|
||||
//
|
||||
// Operations: 251 squares 43 multiplies
|
||||
//
|
||||
// Generated by github.com/mmcloughlin/addchain v0.4.0.
|
||||
|
||||
// Note: Need to stash `arg1` (`xx`) in the case that `out1`/`arg1` alias,
|
||||
// as `arg1` is used after `out1` has been altered.
|
||||
t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, xx: Montgomery_Domain_Field_Element = ---, ---, ---, ---, ---, ---, ---, ---, ---, ---, ---, ---, ---, ---, ---, arg1^
|
||||
|
||||
// Step 1: t1 = x^0x2
|
||||
fe_square(&t1, arg1)
|
||||
|
||||
// Step 2: t5 = x^0x4
|
||||
fe_square(&t5, &t1)
|
||||
|
||||
// Step 3: t2 = x^0x5
|
||||
fe_mul(&t2, arg1, &t5)
|
||||
|
||||
// Step 4: t10 = x^0x6
|
||||
fe_mul(&t10, arg1, &t2)
|
||||
|
||||
// Step 5: t3 = x^0x9
|
||||
fe_mul(&t3, &t5, &t2)
|
||||
|
||||
// Step 6: z = x^0xf
|
||||
fe_mul(out1, &t10, &t3)
|
||||
|
||||
// Step 7: t9 = x^0x12
|
||||
fe_square(&t9, &t3)
|
||||
|
||||
// Step 8: t4 = x^0x15
|
||||
fe_mul(&t4, &t10, out1)
|
||||
|
||||
// Step 9: t0 = x^0x18
|
||||
fe_mul(&t0, &t10, &t9)
|
||||
|
||||
// Step 10: t13 = x^0x1a
|
||||
fe_mul(&t13, &t1, &t0)
|
||||
|
||||
// Step 11: t8 = x^0x2f
|
||||
fe_mul(&t8, &t4, &t13)
|
||||
|
||||
// Step 12: t4 = x^0x38
|
||||
fe_mul(&t4, &t3, &t8)
|
||||
|
||||
// Step 13: t7 = x^0x3d
|
||||
fe_mul(&t7, &t2, &t4)
|
||||
|
||||
// Step 14: t2 = x^0x3f
|
||||
fe_mul(&t2, &t1, &t7)
|
||||
|
||||
// Step 15: t6 = x^0x4f
|
||||
fe_mul(&t6, &t9, &t7)
|
||||
|
||||
// Step 16: t3 = x^0x61
|
||||
fe_mul(&t3, &t9, &t6)
|
||||
|
||||
// Step 17: t1 = x^0x63
|
||||
fe_mul(&t1, &t1, &t3)
|
||||
|
||||
// Step 18: t12 = x^0x73
|
||||
fe_mul(&t12, &t9, &t3)
|
||||
|
||||
// Step 19: t5 = x^0x77
|
||||
fe_mul(&t5, &t5, &t12)
|
||||
|
||||
// Step 20: t11 = x^0x7d
|
||||
fe_mul(&t11, &t10, &t5)
|
||||
|
||||
// Step 21: t0 = x^0x95
|
||||
fe_mul(&t0, &t0, &t11)
|
||||
|
||||
// Step 22: t9 = x^0xa7
|
||||
fe_mul(&t9, &t9, &t0)
|
||||
|
||||
// Step 23: t10 = x^0xad
|
||||
fe_mul(&t10, &t10, &t9)
|
||||
|
||||
// Step 24: t4 = x^0xe5
|
||||
fe_mul(&t4, &t4, &t10)
|
||||
|
||||
// Step 25: t13 = x^0xff
|
||||
fe_mul(&t13, &t13, &t4)
|
||||
|
||||
// Step 33: t14 = x^0xff00
|
||||
fe_pow2k(&t14, &t13, 8)
|
||||
|
||||
// Step 34: t13 = x^0xffff
|
||||
fe_mul(&t13, &t13, &t14)
|
||||
|
||||
// Step 50: t14 = x^0xffff0000
|
||||
fe_pow2k(&t14, &t13, 16)
|
||||
|
||||
// Step 51: t14 = x^0xffffffff
|
||||
fe_mul(&t14, &t13, &t14)
|
||||
|
||||
// Step 99: t14 = x^0xffffffff000000000000
|
||||
fe_pow2k(&t14, &t14, 48)
|
||||
|
||||
// Step 100: t14 = x^0xffffffff00000000ffff
|
||||
fe_mul(&t14, &t13, &t14)
|
||||
|
||||
// Step 116: t14 = x^0xffffffff00000000ffff0000
|
||||
fe_pow2k(&t14, &t14, 16)
|
||||
|
||||
// Step 117: t14 = x^0xffffffff00000000ffffffff
|
||||
fe_mul(&t14, &t13, &t14)
|
||||
|
||||
// Step 133: t14 = x^0xffffffff00000000ffffffff0000
|
||||
fe_pow2k(&t14, &t14, 16)
|
||||
|
||||
// Step 134: t14 = x^0xffffffff00000000ffffffffffff
|
||||
fe_mul(&t14, &t13, &t14)
|
||||
|
||||
// Step 150: t14 = x^0xffffffff00000000ffffffffffff0000
|
||||
fe_pow2k(&t14, &t14, 16)
|
||||
|
||||
// Step 151: t13 = x^0xffffffff00000000ffffffffffffffff
|
||||
fe_mul(&t13, &t13, &t14)
|
||||
|
||||
// Step 157: t13 = x^0x3fffffffc00000003fffffffffffffffc0
|
||||
fe_pow2k(&t13, &t13, 6)
|
||||
|
||||
// Step 158: t13 = x^0x3fffffffc00000003fffffffffffffffef
|
||||
fe_mul(&t13, &t8, &t13)
|
||||
|
||||
// Step 167: t13 = x^0x7fffffff800000007fffffffffffffffde00
|
||||
fe_pow2k(&t13, &t13, 9)
|
||||
|
||||
// Step 168: t12 = x^0x7fffffff800000007fffffffffffffffde73
|
||||
fe_mul(&t12, &t12, &t13)
|
||||
|
||||
// Step 176: t12 = x^0x7fffffff800000007fffffffffffffffde7300
|
||||
fe_pow2k(&t12, &t12, 8)
|
||||
|
||||
// Step 177: t11 = x^0x7fffffff800000007fffffffffffffffde737d
|
||||
fe_mul(&t11, &t11, &t12)
|
||||
|
||||
// Step 186: t11 = x^0xffffffff00000000ffffffffffffffffbce6fa00
|
||||
fe_pow2k(&t11, &t11, 9)
|
||||
|
||||
// Step 187: t10 = x^0xffffffff00000000ffffffffffffffffbce6faad
|
||||
fe_mul(&t10, &t10, &t11)
|
||||
|
||||
// Step 195: t10 = x^0xffffffff00000000ffffffffffffffffbce6faad00
|
||||
fe_pow2k(&t10, &t10, 8)
|
||||
|
||||
// Step 196: t9 = x^0xffffffff00000000ffffffffffffffffbce6faada7
|
||||
fe_mul(&t9, &t9, &t10)
|
||||
|
||||
// Step 205: t9 = x^0x1fffffffe00000001ffffffffffffffff79cdf55b4e00
|
||||
fe_pow2k(&t9, &t9, 9)
|
||||
|
||||
// Step 206: t8 = x^0x1fffffffe00000001ffffffffffffffff79cdf55b4e2f
|
||||
fe_mul(&t8, &t8, &t9)
|
||||
|
||||
// Step 214: t8 = x^0x1fffffffe00000001ffffffffffffffff79cdf55b4e2f00
|
||||
fe_pow2k(&t8, &t8, 8)
|
||||
|
||||
// Step 215: t7 = x^0x1fffffffe00000001ffffffffffffffff79cdf55b4e2f3d
|
||||
fe_mul(&t7, &t7, &t8)
|
||||
|
||||
// Step 226: t7 = x^0xffffffff00000000ffffffffffffffffbce6faada7179e800
|
||||
fe_pow2k(&t7, &t7, 11)
|
||||
|
||||
// Step 227: t6 = x^0xffffffff00000000ffffffffffffffffbce6faada7179e84f
|
||||
fe_mul(&t6, &t6, &t7)
|
||||
|
||||
// Step 236: t6 = x^0x1fffffffe00000001ffffffffffffffff79cdf55b4e2f3d09e00
|
||||
fe_pow2k(&t6, &t6, 9)
|
||||
|
||||
// Step 237: t5 = x^0x1fffffffe00000001ffffffffffffffff79cdf55b4e2f3d09e77
|
||||
fe_mul(&t5, &t5, &t6)
|
||||
|
||||
// Step 247: t5 = x^0x7fffffff800000007fffffffffffffffde737d56d38bcf4279dc00
|
||||
fe_pow2k(&t5, &t5, 10)
|
||||
|
||||
// Step 248: t4 = x^0x7fffffff800000007fffffffffffffffde737d56d38bcf4279dce5
|
||||
fe_mul(&t4, &t4, &t5)
|
||||
|
||||
// Step 256: t4 = x^0x7fffffff800000007fffffffffffffffde737d56d38bcf4279dce500
|
||||
fe_pow2k(&t4, &t4, 8)
|
||||
|
||||
// Step 257: t3 = x^0x7fffffff800000007fffffffffffffffde737d56d38bcf4279dce561
|
||||
fe_mul(&t3, &t3, &t4)
|
||||
|
||||
// Step 264: t3 = x^0x3fffffffc00000003fffffffffffffffef39beab69c5e7a13cee72b080
|
||||
fe_pow2k(&t3, &t3, 7)
|
||||
|
||||
// Step 265: t2 = x^0x3fffffffc00000003fffffffffffffffef39beab69c5e7a13cee72b0bf
|
||||
fe_mul(&t2, &t2, &t3)
|
||||
|
||||
// Step 275: t2 = x^0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc00
|
||||
fe_pow2k(&t2, &t2, 10)
|
||||
|
||||
// Step 276: t1 = x^0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63
|
||||
fe_mul(&t1, &t1, &t2)
|
||||
|
||||
// Step 286: t1 = x^0x3fffffffc00000003fffffffffffffffef39beab69c5e7a13cee72b0bf18c00
|
||||
fe_pow2k(&t1, &t1, 10)
|
||||
|
||||
// Step 287: t0 = x^0x3fffffffc00000003fffffffffffffffef39beab69c5e7a13cee72b0bf18c95
|
||||
fe_mul(&t0, &t0, &t1)
|
||||
|
||||
// Step 293: t0 = x^0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632540
|
||||
fe_pow2k(&t0, &t0, 6)
|
||||
|
||||
// Step 294: z = x^0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63254f
|
||||
fe_mul(out1, out1, &t0)
|
||||
|
||||
fe_clear_vec([]^Montgomery_Domain_Field_Element{&t0, &t1, &t2, &t3, &t4, &t5, &t6, &t7, &t8, &t9, &t10, &t11, &t12, &t13, &t14, &xx})
|
||||
}
|
||||
569
core/crypto/_fiat/field_scalarp256r1/field64.odin
Normal file
569
core/crypto/_fiat/field_scalarp256r1/field64.odin
Normal file
@@ -0,0 +1,569 @@
|
||||
// The BSD 1-Clause License (BSD-1-Clause)
|
||||
//
|
||||
// Copyright (c) 2015-2020 the fiat-crypto authors (see the AUTHORS file)
|
||||
// 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 fiat-crypto 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 Berkeley Software Design,
|
||||
// Inc. 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.
|
||||
|
||||
package field_scalarp256r1
|
||||
|
||||
// The file provides arithmetic on the field Z/(2^256 - 2^224 + 2^192 -
|
||||
// 89188191075325690597107910205041859247) using a 64-bit Montgomery form
|
||||
// internal representation. It is derived primarily from the machine
|
||||
// generated Golang output from the fiat-crypto project.
|
||||
//
|
||||
// While the base implementation is provably correct, this implementation
|
||||
// makes no such claims as the port and optimizations were done by hand.
|
||||
//
|
||||
// WARNING: While big-endian is the common representation used for this
|
||||
// curve, the fiat output uses least-significant-limb first.
|
||||
|
||||
import fiat "core:crypto/_fiat"
|
||||
import "core:math/bits"
|
||||
|
||||
// ELL is the saturated representation of the field order, least-significant
|
||||
// limb first.
|
||||
ELL :: [4]u64{0xf3b9cac2fc632551, 0xbce6faada7179e84, 0xffffffffffffffff, 0xffffffff00000000}
|
||||
|
||||
Montgomery_Domain_Field_Element :: distinct [4]u64
|
||||
Non_Montgomery_Domain_Field_Element :: distinct [4]u64
|
||||
|
||||
fe_mul :: proc "contextless" (out1, arg1, arg2: ^Montgomery_Domain_Field_Element) {
|
||||
x1 := arg1[1]
|
||||
x2 := arg1[2]
|
||||
x3 := arg1[3]
|
||||
x4 := arg1[0]
|
||||
x6, x5 := bits.mul_u64(x4, arg2[3])
|
||||
x8, x7 := bits.mul_u64(x4, arg2[2])
|
||||
x10, x9 := bits.mul_u64(x4, arg2[1])
|
||||
x12, x11 := bits.mul_u64(x4, arg2[0])
|
||||
x13, x14 := bits.add_u64(x12, x9, u64(0x0))
|
||||
x15, x16 := bits.add_u64(x10, x7, u64(fiat.u1(x14)))
|
||||
x17, x18 := bits.add_u64(x8, x5, u64(fiat.u1(x16)))
|
||||
x19 := (u64(fiat.u1(x18)) + x6)
|
||||
_, x20 := bits.mul_u64(x11, 0xccd1c8aaee00bc4f)
|
||||
x23, x22 := bits.mul_u64(x20, 0xffffffff00000000)
|
||||
x25, x24 := bits.mul_u64(x20, 0xffffffffffffffff)
|
||||
x27, x26 := bits.mul_u64(x20, 0xbce6faada7179e84)
|
||||
x29, x28 := bits.mul_u64(x20, 0xf3b9cac2fc632551)
|
||||
x30, x31 := bits.add_u64(x29, x26, u64(0x0))
|
||||
x32, x33 := bits.add_u64(x27, x24, u64(fiat.u1(x31)))
|
||||
x34, x35 := bits.add_u64(x25, x22, u64(fiat.u1(x33)))
|
||||
x36 := (u64(fiat.u1(x35)) + x23)
|
||||
_, x38 := bits.add_u64(x11, x28, u64(0x0))
|
||||
x39, x40 := bits.add_u64(x13, x30, u64(fiat.u1(x38)))
|
||||
x41, x42 := bits.add_u64(x15, x32, u64(fiat.u1(x40)))
|
||||
x43, x44 := bits.add_u64(x17, x34, u64(fiat.u1(x42)))
|
||||
x45, x46 := bits.add_u64(x19, x36, u64(fiat.u1(x44)))
|
||||
x48, x47 := bits.mul_u64(x1, arg2[3])
|
||||
x50, x49 := bits.mul_u64(x1, arg2[2])
|
||||
x52, x51 := bits.mul_u64(x1, arg2[1])
|
||||
x54, x53 := bits.mul_u64(x1, arg2[0])
|
||||
x55, x56 := bits.add_u64(x54, x51, u64(0x0))
|
||||
x57, x58 := bits.add_u64(x52, x49, u64(fiat.u1(x56)))
|
||||
x59, x60 := bits.add_u64(x50, x47, u64(fiat.u1(x58)))
|
||||
x61 := (u64(fiat.u1(x60)) + x48)
|
||||
x62, x63 := bits.add_u64(x39, x53, u64(0x0))
|
||||
x64, x65 := bits.add_u64(x41, x55, u64(fiat.u1(x63)))
|
||||
x66, x67 := bits.add_u64(x43, x57, u64(fiat.u1(x65)))
|
||||
x68, x69 := bits.add_u64(x45, x59, u64(fiat.u1(x67)))
|
||||
x70, x71 := bits.add_u64(u64(fiat.u1(x46)), x61, u64(fiat.u1(x69)))
|
||||
_, x72 := bits.mul_u64(x62, 0xccd1c8aaee00bc4f)
|
||||
x75, x74 := bits.mul_u64(x72, 0xffffffff00000000)
|
||||
x77, x76 := bits.mul_u64(x72, 0xffffffffffffffff)
|
||||
x79, x78 := bits.mul_u64(x72, 0xbce6faada7179e84)
|
||||
x81, x80 := bits.mul_u64(x72, 0xf3b9cac2fc632551)
|
||||
x82, x83 := bits.add_u64(x81, x78, u64(0x0))
|
||||
x84, x85 := bits.add_u64(x79, x76, u64(fiat.u1(x83)))
|
||||
x86, x87 := bits.add_u64(x77, x74, u64(fiat.u1(x85)))
|
||||
x88 := (u64(fiat.u1(x87)) + x75)
|
||||
_, x90 := bits.add_u64(x62, x80, u64(0x0))
|
||||
x91, x92 := bits.add_u64(x64, x82, u64(fiat.u1(x90)))
|
||||
x93, x94 := bits.add_u64(x66, x84, u64(fiat.u1(x92)))
|
||||
x95, x96 := bits.add_u64(x68, x86, u64(fiat.u1(x94)))
|
||||
x97, x98 := bits.add_u64(x70, x88, u64(fiat.u1(x96)))
|
||||
x99 := (u64(fiat.u1(x98)) + u64(fiat.u1(x71)))
|
||||
x101, x100 := bits.mul_u64(x2, arg2[3])
|
||||
x103, x102 := bits.mul_u64(x2, arg2[2])
|
||||
x105, x104 := bits.mul_u64(x2, arg2[1])
|
||||
x107, x106 := bits.mul_u64(x2, arg2[0])
|
||||
x108, x109 := bits.add_u64(x107, x104, u64(0x0))
|
||||
x110, x111 := bits.add_u64(x105, x102, u64(fiat.u1(x109)))
|
||||
x112, x113 := bits.add_u64(x103, x100, u64(fiat.u1(x111)))
|
||||
x114 := (u64(fiat.u1(x113)) + x101)
|
||||
x115, x116 := bits.add_u64(x91, x106, u64(0x0))
|
||||
x117, x118 := bits.add_u64(x93, x108, u64(fiat.u1(x116)))
|
||||
x119, x120 := bits.add_u64(x95, x110, u64(fiat.u1(x118)))
|
||||
x121, x122 := bits.add_u64(x97, x112, u64(fiat.u1(x120)))
|
||||
x123, x124 := bits.add_u64(x99, x114, u64(fiat.u1(x122)))
|
||||
_, x125 := bits.mul_u64(x115, 0xccd1c8aaee00bc4f)
|
||||
x128, x127 := bits.mul_u64(x125, 0xffffffff00000000)
|
||||
x130, x129 := bits.mul_u64(x125, 0xffffffffffffffff)
|
||||
x132, x131 := bits.mul_u64(x125, 0xbce6faada7179e84)
|
||||
x134, x133 := bits.mul_u64(x125, 0xf3b9cac2fc632551)
|
||||
x135, x136 := bits.add_u64(x134, x131, u64(0x0))
|
||||
x137, x138 := bits.add_u64(x132, x129, u64(fiat.u1(x136)))
|
||||
x139, x140 := bits.add_u64(x130, x127, u64(fiat.u1(x138)))
|
||||
x141 := (u64(fiat.u1(x140)) + x128)
|
||||
_, x143 := bits.add_u64(x115, x133, u64(0x0))
|
||||
x144, x145 := bits.add_u64(x117, x135, u64(fiat.u1(x143)))
|
||||
x146, x147 := bits.add_u64(x119, x137, u64(fiat.u1(x145)))
|
||||
x148, x149 := bits.add_u64(x121, x139, u64(fiat.u1(x147)))
|
||||
x150, x151 := bits.add_u64(x123, x141, u64(fiat.u1(x149)))
|
||||
x152 := (u64(fiat.u1(x151)) + u64(fiat.u1(x124)))
|
||||
x154, x153 := bits.mul_u64(x3, arg2[3])
|
||||
x156, x155 := bits.mul_u64(x3, arg2[2])
|
||||
x158, x157 := bits.mul_u64(x3, arg2[1])
|
||||
x160, x159 := bits.mul_u64(x3, arg2[0])
|
||||
x161, x162 := bits.add_u64(x160, x157, u64(0x0))
|
||||
x163, x164 := bits.add_u64(x158, x155, u64(fiat.u1(x162)))
|
||||
x165, x166 := bits.add_u64(x156, x153, u64(fiat.u1(x164)))
|
||||
x167 := (u64(fiat.u1(x166)) + x154)
|
||||
x168, x169 := bits.add_u64(x144, x159, u64(0x0))
|
||||
x170, x171 := bits.add_u64(x146, x161, u64(fiat.u1(x169)))
|
||||
x172, x173 := bits.add_u64(x148, x163, u64(fiat.u1(x171)))
|
||||
x174, x175 := bits.add_u64(x150, x165, u64(fiat.u1(x173)))
|
||||
x176, x177 := bits.add_u64(x152, x167, u64(fiat.u1(x175)))
|
||||
_, x178 := bits.mul_u64(x168, 0xccd1c8aaee00bc4f)
|
||||
x181, x180 := bits.mul_u64(x178, 0xffffffff00000000)
|
||||
x183, x182 := bits.mul_u64(x178, 0xffffffffffffffff)
|
||||
x185, x184 := bits.mul_u64(x178, 0xbce6faada7179e84)
|
||||
x187, x186 := bits.mul_u64(x178, 0xf3b9cac2fc632551)
|
||||
x188, x189 := bits.add_u64(x187, x184, u64(0x0))
|
||||
x190, x191 := bits.add_u64(x185, x182, u64(fiat.u1(x189)))
|
||||
x192, x193 := bits.add_u64(x183, x180, u64(fiat.u1(x191)))
|
||||
x194 := (u64(fiat.u1(x193)) + x181)
|
||||
_, x196 := bits.add_u64(x168, x186, u64(0x0))
|
||||
x197, x198 := bits.add_u64(x170, x188, u64(fiat.u1(x196)))
|
||||
x199, x200 := bits.add_u64(x172, x190, u64(fiat.u1(x198)))
|
||||
x201, x202 := bits.add_u64(x174, x192, u64(fiat.u1(x200)))
|
||||
x203, x204 := bits.add_u64(x176, x194, u64(fiat.u1(x202)))
|
||||
x205 := (u64(fiat.u1(x204)) + u64(fiat.u1(x177)))
|
||||
x206, x207 := bits.sub_u64(x197, 0xf3b9cac2fc632551, u64(0x0))
|
||||
x208, x209 := bits.sub_u64(x199, 0xbce6faada7179e84, u64(fiat.u1(x207)))
|
||||
x210, x211 := bits.sub_u64(x201, 0xffffffffffffffff, u64(fiat.u1(x209)))
|
||||
x212, x213 := bits.sub_u64(x203, 0xffffffff00000000, u64(fiat.u1(x211)))
|
||||
_, x215 := bits.sub_u64(x205, u64(0x0), u64(fiat.u1(x213)))
|
||||
x216 := fiat.cmovznz_u64(fiat.u1(x215), x206, x197)
|
||||
x217 := fiat.cmovznz_u64(fiat.u1(x215), x208, x199)
|
||||
x218 := fiat.cmovznz_u64(fiat.u1(x215), x210, x201)
|
||||
x219 := fiat.cmovznz_u64(fiat.u1(x215), x212, x203)
|
||||
out1[0] = x216
|
||||
out1[1] = x217
|
||||
out1[2] = x218
|
||||
out1[3] = x219
|
||||
}
|
||||
|
||||
fe_square :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
|
||||
x1 := arg1[1]
|
||||
x2 := arg1[2]
|
||||
x3 := arg1[3]
|
||||
x4 := arg1[0]
|
||||
x6, x5 := bits.mul_u64(x4, arg1[3])
|
||||
x8, x7 := bits.mul_u64(x4, arg1[2])
|
||||
x10, x9 := bits.mul_u64(x4, arg1[1])
|
||||
x12, x11 := bits.mul_u64(x4, arg1[0])
|
||||
x13, x14 := bits.add_u64(x12, x9, u64(0x0))
|
||||
x15, x16 := bits.add_u64(x10, x7, u64(fiat.u1(x14)))
|
||||
x17, x18 := bits.add_u64(x8, x5, u64(fiat.u1(x16)))
|
||||
x19 := (u64(fiat.u1(x18)) + x6)
|
||||
_, x20 := bits.mul_u64(x11, 0xccd1c8aaee00bc4f)
|
||||
x23, x22 := bits.mul_u64(x20, 0xffffffff00000000)
|
||||
x25, x24 := bits.mul_u64(x20, 0xffffffffffffffff)
|
||||
x27, x26 := bits.mul_u64(x20, 0xbce6faada7179e84)
|
||||
x29, x28 := bits.mul_u64(x20, 0xf3b9cac2fc632551)
|
||||
x30, x31 := bits.add_u64(x29, x26, u64(0x0))
|
||||
x32, x33 := bits.add_u64(x27, x24, u64(fiat.u1(x31)))
|
||||
x34, x35 := bits.add_u64(x25, x22, u64(fiat.u1(x33)))
|
||||
x36 := (u64(fiat.u1(x35)) + x23)
|
||||
_, x38 := bits.add_u64(x11, x28, u64(0x0))
|
||||
x39, x40 := bits.add_u64(x13, x30, u64(fiat.u1(x38)))
|
||||
x41, x42 := bits.add_u64(x15, x32, u64(fiat.u1(x40)))
|
||||
x43, x44 := bits.add_u64(x17, x34, u64(fiat.u1(x42)))
|
||||
x45, x46 := bits.add_u64(x19, x36, u64(fiat.u1(x44)))
|
||||
x48, x47 := bits.mul_u64(x1, arg1[3])
|
||||
x50, x49 := bits.mul_u64(x1, arg1[2])
|
||||
x52, x51 := bits.mul_u64(x1, arg1[1])
|
||||
x54, x53 := bits.mul_u64(x1, arg1[0])
|
||||
x55, x56 := bits.add_u64(x54, x51, u64(0x0))
|
||||
x57, x58 := bits.add_u64(x52, x49, u64(fiat.u1(x56)))
|
||||
x59, x60 := bits.add_u64(x50, x47, u64(fiat.u1(x58)))
|
||||
x61 := (u64(fiat.u1(x60)) + x48)
|
||||
x62, x63 := bits.add_u64(x39, x53, u64(0x0))
|
||||
x64, x65 := bits.add_u64(x41, x55, u64(fiat.u1(x63)))
|
||||
x66, x67 := bits.add_u64(x43, x57, u64(fiat.u1(x65)))
|
||||
x68, x69 := bits.add_u64(x45, x59, u64(fiat.u1(x67)))
|
||||
x70, x71 := bits.add_u64(u64(fiat.u1(x46)), x61, u64(fiat.u1(x69)))
|
||||
_, x72 := bits.mul_u64(x62, 0xccd1c8aaee00bc4f)
|
||||
x75, x74 := bits.mul_u64(x72, 0xffffffff00000000)
|
||||
x77, x76 := bits.mul_u64(x72, 0xffffffffffffffff)
|
||||
x79, x78 := bits.mul_u64(x72, 0xbce6faada7179e84)
|
||||
x81, x80 := bits.mul_u64(x72, 0xf3b9cac2fc632551)
|
||||
x82, x83 := bits.add_u64(x81, x78, u64(0x0))
|
||||
x84, x85 := bits.add_u64(x79, x76, u64(fiat.u1(x83)))
|
||||
x86, x87 := bits.add_u64(x77, x74, u64(fiat.u1(x85)))
|
||||
x88 := (u64(fiat.u1(x87)) + x75)
|
||||
_, x90 := bits.add_u64(x62, x80, u64(0x0))
|
||||
x91, x92 := bits.add_u64(x64, x82, u64(fiat.u1(x90)))
|
||||
x93, x94 := bits.add_u64(x66, x84, u64(fiat.u1(x92)))
|
||||
x95, x96 := bits.add_u64(x68, x86, u64(fiat.u1(x94)))
|
||||
x97, x98 := bits.add_u64(x70, x88, u64(fiat.u1(x96)))
|
||||
x99 := (u64(fiat.u1(x98)) + u64(fiat.u1(x71)))
|
||||
x101, x100 := bits.mul_u64(x2, arg1[3])
|
||||
x103, x102 := bits.mul_u64(x2, arg1[2])
|
||||
x105, x104 := bits.mul_u64(x2, arg1[1])
|
||||
x107, x106 := bits.mul_u64(x2, arg1[0])
|
||||
x108, x109 := bits.add_u64(x107, x104, u64(0x0))
|
||||
x110, x111 := bits.add_u64(x105, x102, u64(fiat.u1(x109)))
|
||||
x112, x113 := bits.add_u64(x103, x100, u64(fiat.u1(x111)))
|
||||
x114 := (u64(fiat.u1(x113)) + x101)
|
||||
x115, x116 := bits.add_u64(x91, x106, u64(0x0))
|
||||
x117, x118 := bits.add_u64(x93, x108, u64(fiat.u1(x116)))
|
||||
x119, x120 := bits.add_u64(x95, x110, u64(fiat.u1(x118)))
|
||||
x121, x122 := bits.add_u64(x97, x112, u64(fiat.u1(x120)))
|
||||
x123, x124 := bits.add_u64(x99, x114, u64(fiat.u1(x122)))
|
||||
_, x125 := bits.mul_u64(x115, 0xccd1c8aaee00bc4f)
|
||||
x128, x127 := bits.mul_u64(x125, 0xffffffff00000000)
|
||||
x130, x129 := bits.mul_u64(x125, 0xffffffffffffffff)
|
||||
x132, x131 := bits.mul_u64(x125, 0xbce6faada7179e84)
|
||||
x134, x133 := bits.mul_u64(x125, 0xf3b9cac2fc632551)
|
||||
x135, x136 := bits.add_u64(x134, x131, u64(0x0))
|
||||
x137, x138 := bits.add_u64(x132, x129, u64(fiat.u1(x136)))
|
||||
x139, x140 := bits.add_u64(x130, x127, u64(fiat.u1(x138)))
|
||||
x141 := (u64(fiat.u1(x140)) + x128)
|
||||
_, x143 := bits.add_u64(x115, x133, u64(0x0))
|
||||
x144, x145 := bits.add_u64(x117, x135, u64(fiat.u1(x143)))
|
||||
x146, x147 := bits.add_u64(x119, x137, u64(fiat.u1(x145)))
|
||||
x148, x149 := bits.add_u64(x121, x139, u64(fiat.u1(x147)))
|
||||
x150, x151 := bits.add_u64(x123, x141, u64(fiat.u1(x149)))
|
||||
x152 := (u64(fiat.u1(x151)) + u64(fiat.u1(x124)))
|
||||
x154, x153 := bits.mul_u64(x3, arg1[3])
|
||||
x156, x155 := bits.mul_u64(x3, arg1[2])
|
||||
x158, x157 := bits.mul_u64(x3, arg1[1])
|
||||
x160, x159 := bits.mul_u64(x3, arg1[0])
|
||||
x161, x162 := bits.add_u64(x160, x157, u64(0x0))
|
||||
x163, x164 := bits.add_u64(x158, x155, u64(fiat.u1(x162)))
|
||||
x165, x166 := bits.add_u64(x156, x153, u64(fiat.u1(x164)))
|
||||
x167 := (u64(fiat.u1(x166)) + x154)
|
||||
x168, x169 := bits.add_u64(x144, x159, u64(0x0))
|
||||
x170, x171 := bits.add_u64(x146, x161, u64(fiat.u1(x169)))
|
||||
x172, x173 := bits.add_u64(x148, x163, u64(fiat.u1(x171)))
|
||||
x174, x175 := bits.add_u64(x150, x165, u64(fiat.u1(x173)))
|
||||
x176, x177 := bits.add_u64(x152, x167, u64(fiat.u1(x175)))
|
||||
_, x178 := bits.mul_u64(x168, 0xccd1c8aaee00bc4f)
|
||||
x181, x180 := bits.mul_u64(x178, 0xffffffff00000000)
|
||||
x183, x182 := bits.mul_u64(x178, 0xffffffffffffffff)
|
||||
x185, x184 := bits.mul_u64(x178, 0xbce6faada7179e84)
|
||||
x187, x186 := bits.mul_u64(x178, 0xf3b9cac2fc632551)
|
||||
x188, x189 := bits.add_u64(x187, x184, u64(0x0))
|
||||
x190, x191 := bits.add_u64(x185, x182, u64(fiat.u1(x189)))
|
||||
x192, x193 := bits.add_u64(x183, x180, u64(fiat.u1(x191)))
|
||||
x194 := (u64(fiat.u1(x193)) + x181)
|
||||
_, x196 := bits.add_u64(x168, x186, u64(0x0))
|
||||
x197, x198 := bits.add_u64(x170, x188, u64(fiat.u1(x196)))
|
||||
x199, x200 := bits.add_u64(x172, x190, u64(fiat.u1(x198)))
|
||||
x201, x202 := bits.add_u64(x174, x192, u64(fiat.u1(x200)))
|
||||
x203, x204 := bits.add_u64(x176, x194, u64(fiat.u1(x202)))
|
||||
x205 := (u64(fiat.u1(x204)) + u64(fiat.u1(x177)))
|
||||
x206, x207 := bits.sub_u64(x197, 0xf3b9cac2fc632551, u64(0x0))
|
||||
x208, x209 := bits.sub_u64(x199, 0xbce6faada7179e84, u64(fiat.u1(x207)))
|
||||
x210, x211 := bits.sub_u64(x201, 0xffffffffffffffff, u64(fiat.u1(x209)))
|
||||
x212, x213 := bits.sub_u64(x203, 0xffffffff00000000, u64(fiat.u1(x211)))
|
||||
_, x215 := bits.sub_u64(x205, u64(0x0), u64(fiat.u1(x213)))
|
||||
x216 := fiat.cmovznz_u64(fiat.u1(x215), x206, x197)
|
||||
x217 := fiat.cmovznz_u64(fiat.u1(x215), x208, x199)
|
||||
x218 := fiat.cmovznz_u64(fiat.u1(x215), x210, x201)
|
||||
x219 := fiat.cmovznz_u64(fiat.u1(x215), x212, x203)
|
||||
out1[0] = x216
|
||||
out1[1] = x217
|
||||
out1[2] = x218
|
||||
out1[3] = x219
|
||||
}
|
||||
|
||||
fe_add :: proc "contextless" (out1, arg1, arg2: ^Montgomery_Domain_Field_Element) {
|
||||
x1, x2 := bits.add_u64(arg1[0], arg2[0], u64(0x0))
|
||||
x3, x4 := bits.add_u64(arg1[1], arg2[1], u64(fiat.u1(x2)))
|
||||
x5, x6 := bits.add_u64(arg1[2], arg2[2], u64(fiat.u1(x4)))
|
||||
x7, x8 := bits.add_u64(arg1[3], arg2[3], u64(fiat.u1(x6)))
|
||||
x9, x10 := bits.sub_u64(x1, 0xf3b9cac2fc632551, u64(0x0))
|
||||
x11, x12 := bits.sub_u64(x3, 0xbce6faada7179e84, u64(fiat.u1(x10)))
|
||||
x13, x14 := bits.sub_u64(x5, 0xffffffffffffffff, u64(fiat.u1(x12)))
|
||||
x15, x16 := bits.sub_u64(x7, 0xffffffff00000000, u64(fiat.u1(x14)))
|
||||
_, x18 := bits.sub_u64(u64(fiat.u1(x8)), u64(0x0), u64(fiat.u1(x16)))
|
||||
x19 := fiat.cmovznz_u64(fiat.u1(x18), x9, x1)
|
||||
x20 := fiat.cmovznz_u64(fiat.u1(x18), x11, x3)
|
||||
x21 := fiat.cmovznz_u64(fiat.u1(x18), x13, x5)
|
||||
x22 := fiat.cmovznz_u64(fiat.u1(x18), x15, x7)
|
||||
out1[0] = x19
|
||||
out1[1] = x20
|
||||
out1[2] = x21
|
||||
out1[3] = x22
|
||||
}
|
||||
|
||||
fe_sub :: proc "contextless" (out1, arg1, arg2: ^Montgomery_Domain_Field_Element) {
|
||||
x1, x2 := bits.sub_u64(arg1[0], arg2[0], u64(0x0))
|
||||
x3, x4 := bits.sub_u64(arg1[1], arg2[1], u64(fiat.u1(x2)))
|
||||
x5, x6 := bits.sub_u64(arg1[2], arg2[2], u64(fiat.u1(x4)))
|
||||
x7, x8 := bits.sub_u64(arg1[3], arg2[3], u64(fiat.u1(x6)))
|
||||
x9 := fiat.cmovznz_u64(fiat.u1(x8), u64(0x0), 0xffffffffffffffff)
|
||||
x10, x11 := bits.add_u64(x1, (x9 & 0xf3b9cac2fc632551), u64(0x0))
|
||||
x12, x13 := bits.add_u64(x3, (x9 & 0xbce6faada7179e84), u64(fiat.u1(x11)))
|
||||
x14, x15 := bits.add_u64(x5, x9, u64(fiat.u1(x13)))
|
||||
x16, _ := bits.add_u64(x7, (x9 & 0xffffffff00000000), u64(fiat.u1(x15)))
|
||||
out1[0] = x10
|
||||
out1[1] = x12
|
||||
out1[2] = x14
|
||||
out1[3] = x16
|
||||
}
|
||||
|
||||
fe_opp :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
|
||||
x1, x2 := bits.sub_u64(u64(0x0), arg1[0], u64(0x0))
|
||||
x3, x4 := bits.sub_u64(u64(0x0), arg1[1], u64(fiat.u1(x2)))
|
||||
x5, x6 := bits.sub_u64(u64(0x0), arg1[2], u64(fiat.u1(x4)))
|
||||
x7, x8 := bits.sub_u64(u64(0x0), arg1[3], u64(fiat.u1(x6)))
|
||||
x9 := fiat.cmovznz_u64(fiat.u1(x8), u64(0x0), 0xffffffffffffffff)
|
||||
x10, x11 := bits.add_u64(x1, (x9 & 0xf3b9cac2fc632551), u64(0x0))
|
||||
x12, x13 := bits.add_u64(x3, (x9 & 0xbce6faada7179e84), u64(fiat.u1(x11)))
|
||||
x14, x15 := bits.add_u64(x5, x9, u64(fiat.u1(x13)))
|
||||
x16, _ := bits.add_u64(x7, (x9 & 0xffffffff00000000), u64(fiat.u1(x15)))
|
||||
out1[0] = x10
|
||||
out1[1] = x12
|
||||
out1[2] = x14
|
||||
out1[3] = x16
|
||||
}
|
||||
|
||||
fe_one :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element) {
|
||||
out1[0] = 0xc46353d039cdaaf
|
||||
out1[1] = 0x4319055258e8617b
|
||||
out1[2] = u64(0x0)
|
||||
out1[3] = 0xffffffff
|
||||
}
|
||||
|
||||
fe_non_zero :: proc "contextless" (arg1: ^Montgomery_Domain_Field_Element) -> u64 {
|
||||
return arg1[0] | (arg1[1] | (arg1[2] | arg1[3]))
|
||||
}
|
||||
|
||||
@(optimization_mode = "none")
|
||||
fe_cond_assign :: #force_no_inline proc "contextless" (
|
||||
out1, arg1: ^Montgomery_Domain_Field_Element,
|
||||
arg2: int,
|
||||
) {
|
||||
x1 := fiat.cmovznz_u64(fiat.u1(arg2), out1[0], arg1[0])
|
||||
x2 := fiat.cmovznz_u64(fiat.u1(arg2), out1[1], arg1[1])
|
||||
x3 := fiat.cmovznz_u64(fiat.u1(arg2), out1[2], arg1[2])
|
||||
x4 := fiat.cmovznz_u64(fiat.u1(arg2), out1[3], arg1[3])
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
}
|
||||
|
||||
fe_from_montgomery :: proc "contextless" (
|
||||
out1: ^Non_Montgomery_Domain_Field_Element,
|
||||
arg1: ^Montgomery_Domain_Field_Element,
|
||||
) {
|
||||
x1 := arg1[0]
|
||||
_, x2 := bits.mul_u64(x1, 0xccd1c8aaee00bc4f)
|
||||
x5, x4 := bits.mul_u64(x2, 0xffffffff00000000)
|
||||
x7, x6 := bits.mul_u64(x2, 0xffffffffffffffff)
|
||||
x9, x8 := bits.mul_u64(x2, 0xbce6faada7179e84)
|
||||
x11, x10 := bits.mul_u64(x2, 0xf3b9cac2fc632551)
|
||||
x12, x13 := bits.add_u64(x11, x8, u64(0x0))
|
||||
x14, x15 := bits.add_u64(x9, x6, u64(fiat.u1(x13)))
|
||||
x16, x17 := bits.add_u64(x7, x4, u64(fiat.u1(x15)))
|
||||
_, x19 := bits.add_u64(x1, x10, u64(0x0))
|
||||
x20, x21 := bits.add_u64(u64(0x0), x12, u64(fiat.u1(x19)))
|
||||
x22, x23 := bits.add_u64(u64(0x0), x14, u64(fiat.u1(x21)))
|
||||
x24, x25 := bits.add_u64(u64(0x0), x16, u64(fiat.u1(x23)))
|
||||
x26, x27 := bits.add_u64(x20, arg1[1], u64(0x0))
|
||||
x28, x29 := bits.add_u64(x22, u64(0x0), u64(fiat.u1(x27)))
|
||||
x30, x31 := bits.add_u64(x24, u64(0x0), u64(fiat.u1(x29)))
|
||||
_, x32 := bits.mul_u64(x26, 0xccd1c8aaee00bc4f)
|
||||
x35, x34 := bits.mul_u64(x32, 0xffffffff00000000)
|
||||
x37, x36 := bits.mul_u64(x32, 0xffffffffffffffff)
|
||||
x39, x38 := bits.mul_u64(x32, 0xbce6faada7179e84)
|
||||
x41, x40 := bits.mul_u64(x32, 0xf3b9cac2fc632551)
|
||||
x42, x43 := bits.add_u64(x41, x38, u64(0x0))
|
||||
x44, x45 := bits.add_u64(x39, x36, u64(fiat.u1(x43)))
|
||||
x46, x47 := bits.add_u64(x37, x34, u64(fiat.u1(x45)))
|
||||
_, x49 := bits.add_u64(x26, x40, u64(0x0))
|
||||
x50, x51 := bits.add_u64(x28, x42, u64(fiat.u1(x49)))
|
||||
x52, x53 := bits.add_u64(x30, x44, u64(fiat.u1(x51)))
|
||||
x54, x55 := bits.add_u64((u64(fiat.u1(x31)) + (u64(fiat.u1(x25)) + (u64(fiat.u1(x17)) + x5))), x46, u64(fiat.u1(x53)))
|
||||
x56, x57 := bits.add_u64(x50, arg1[2], u64(0x0))
|
||||
x58, x59 := bits.add_u64(x52, u64(0x0), u64(fiat.u1(x57)))
|
||||
x60, x61 := bits.add_u64(x54, u64(0x0), u64(fiat.u1(x59)))
|
||||
_, x62 := bits.mul_u64(x56, 0xccd1c8aaee00bc4f)
|
||||
x65, x64 := bits.mul_u64(x62, 0xffffffff00000000)
|
||||
x67, x66 := bits.mul_u64(x62, 0xffffffffffffffff)
|
||||
x69, x68 := bits.mul_u64(x62, 0xbce6faada7179e84)
|
||||
x71, x70 := bits.mul_u64(x62, 0xf3b9cac2fc632551)
|
||||
x72, x73 := bits.add_u64(x71, x68, u64(0x0))
|
||||
x74, x75 := bits.add_u64(x69, x66, u64(fiat.u1(x73)))
|
||||
x76, x77 := bits.add_u64(x67, x64, u64(fiat.u1(x75)))
|
||||
_, x79 := bits.add_u64(x56, x70, u64(0x0))
|
||||
x80, x81 := bits.add_u64(x58, x72, u64(fiat.u1(x79)))
|
||||
x82, x83 := bits.add_u64(x60, x74, u64(fiat.u1(x81)))
|
||||
x84, x85 := bits.add_u64((u64(fiat.u1(x61)) + (u64(fiat.u1(x55)) + (u64(fiat.u1(x47)) + x35))), x76, u64(fiat.u1(x83)))
|
||||
x86, x87 := bits.add_u64(x80, arg1[3], u64(0x0))
|
||||
x88, x89 := bits.add_u64(x82, u64(0x0), u64(fiat.u1(x87)))
|
||||
x90, x91 := bits.add_u64(x84, u64(0x0), u64(fiat.u1(x89)))
|
||||
_, x92 := bits.mul_u64(x86, 0xccd1c8aaee00bc4f)
|
||||
x95, x94 := bits.mul_u64(x92, 0xffffffff00000000)
|
||||
x97, x96 := bits.mul_u64(x92, 0xffffffffffffffff)
|
||||
x99, x98 := bits.mul_u64(x92, 0xbce6faada7179e84)
|
||||
x101, x100 := bits.mul_u64(x92, 0xf3b9cac2fc632551)
|
||||
x102, x103 := bits.add_u64(x101, x98, u64(0x0))
|
||||
x104, x105 := bits.add_u64(x99, x96, u64(fiat.u1(x103)))
|
||||
x106, x107 := bits.add_u64(x97, x94, u64(fiat.u1(x105)))
|
||||
_, x109 := bits.add_u64(x86, x100, u64(0x0))
|
||||
x110, x111 := bits.add_u64(x88, x102, u64(fiat.u1(x109)))
|
||||
x112, x113 := bits.add_u64(x90, x104, u64(fiat.u1(x111)))
|
||||
x114, x115 := bits.add_u64((u64(fiat.u1(x91)) + (u64(fiat.u1(x85)) + (u64(fiat.u1(x77)) + x65))), x106, u64(fiat.u1(x113)))
|
||||
x116 := (u64(fiat.u1(x115)) + (u64(fiat.u1(x107)) + x95))
|
||||
x117, x118 := bits.sub_u64(x110, 0xf3b9cac2fc632551, u64(0x0))
|
||||
x119, x120 := bits.sub_u64(x112, 0xbce6faada7179e84, u64(fiat.u1(x118)))
|
||||
x121, x122 := bits.sub_u64(x114, 0xffffffffffffffff, u64(fiat.u1(x120)))
|
||||
x123, x124 := bits.sub_u64(x116, 0xffffffff00000000, u64(fiat.u1(x122)))
|
||||
_, x126 := bits.sub_u64(u64(0x0), u64(0x0), u64(fiat.u1(x124)))
|
||||
x127 := fiat.cmovznz_u64(fiat.u1(x126), x117, x110)
|
||||
x128 := fiat.cmovznz_u64(fiat.u1(x126), x119, x112)
|
||||
x129 := fiat.cmovznz_u64(fiat.u1(x126), x121, x114)
|
||||
x130 := fiat.cmovznz_u64(fiat.u1(x126), x123, x116)
|
||||
out1[0] = x127
|
||||
out1[1] = x128
|
||||
out1[2] = x129
|
||||
out1[3] = x130
|
||||
}
|
||||
|
||||
fe_to_montgomery :: proc "contextless" (
|
||||
out1: ^Montgomery_Domain_Field_Element,
|
||||
arg1: ^Non_Montgomery_Domain_Field_Element,
|
||||
) {
|
||||
x1 := arg1[1]
|
||||
x2 := arg1[2]
|
||||
x3 := arg1[3]
|
||||
x4 := arg1[0]
|
||||
x6, x5 := bits.mul_u64(x4, 0x66e12d94f3d95620)
|
||||
x8, x7 := bits.mul_u64(x4, 0x2845b2392b6bec59)
|
||||
x10, x9 := bits.mul_u64(x4, 0x4699799c49bd6fa6)
|
||||
x12, x11 := bits.mul_u64(x4, 0x83244c95be79eea2)
|
||||
x13, x14 := bits.add_u64(x12, x9, u64(0x0))
|
||||
x15, x16 := bits.add_u64(x10, x7, u64(fiat.u1(x14)))
|
||||
x17, x18 := bits.add_u64(x8, x5, u64(fiat.u1(x16)))
|
||||
_, x19 := bits.mul_u64(x11, 0xccd1c8aaee00bc4f)
|
||||
x22, x21 := bits.mul_u64(x19, 0xffffffff00000000)
|
||||
x24, x23 := bits.mul_u64(x19, 0xffffffffffffffff)
|
||||
x26, x25 := bits.mul_u64(x19, 0xbce6faada7179e84)
|
||||
x28, x27 := bits.mul_u64(x19, 0xf3b9cac2fc632551)
|
||||
x29, x30 := bits.add_u64(x28, x25, u64(0x0))
|
||||
x31, x32 := bits.add_u64(x26, x23, u64(fiat.u1(x30)))
|
||||
x33, x34 := bits.add_u64(x24, x21, u64(fiat.u1(x32)))
|
||||
_, x36 := bits.add_u64(x11, x27, u64(0x0))
|
||||
x37, x38 := bits.add_u64(x13, x29, u64(fiat.u1(x36)))
|
||||
x39, x40 := bits.add_u64(x15, x31, u64(fiat.u1(x38)))
|
||||
x41, x42 := bits.add_u64(x17, x33, u64(fiat.u1(x40)))
|
||||
x43, x44 := bits.add_u64((u64(fiat.u1(x18)) + x6), (u64(fiat.u1(x34)) + x22), u64(fiat.u1(x42)))
|
||||
x46, x45 := bits.mul_u64(x1, 0x66e12d94f3d95620)
|
||||
x48, x47 := bits.mul_u64(x1, 0x2845b2392b6bec59)
|
||||
x50, x49 := bits.mul_u64(x1, 0x4699799c49bd6fa6)
|
||||
x52, x51 := bits.mul_u64(x1, 0x83244c95be79eea2)
|
||||
x53, x54 := bits.add_u64(x52, x49, u64(0x0))
|
||||
x55, x56 := bits.add_u64(x50, x47, u64(fiat.u1(x54)))
|
||||
x57, x58 := bits.add_u64(x48, x45, u64(fiat.u1(x56)))
|
||||
x59, x60 := bits.add_u64(x37, x51, u64(0x0))
|
||||
x61, x62 := bits.add_u64(x39, x53, u64(fiat.u1(x60)))
|
||||
x63, x64 := bits.add_u64(x41, x55, u64(fiat.u1(x62)))
|
||||
x65, x66 := bits.add_u64(x43, x57, u64(fiat.u1(x64)))
|
||||
_, x67 := bits.mul_u64(x59, 0xccd1c8aaee00bc4f)
|
||||
x70, x69 := bits.mul_u64(x67, 0xffffffff00000000)
|
||||
x72, x71 := bits.mul_u64(x67, 0xffffffffffffffff)
|
||||
x74, x73 := bits.mul_u64(x67, 0xbce6faada7179e84)
|
||||
x76, x75 := bits.mul_u64(x67, 0xf3b9cac2fc632551)
|
||||
x77, x78 := bits.add_u64(x76, x73, u64(0x0))
|
||||
x79, x80 := bits.add_u64(x74, x71, u64(fiat.u1(x78)))
|
||||
x81, x82 := bits.add_u64(x72, x69, u64(fiat.u1(x80)))
|
||||
_, x84 := bits.add_u64(x59, x75, u64(0x0))
|
||||
x85, x86 := bits.add_u64(x61, x77, u64(fiat.u1(x84)))
|
||||
x87, x88 := bits.add_u64(x63, x79, u64(fiat.u1(x86)))
|
||||
x89, x90 := bits.add_u64(x65, x81, u64(fiat.u1(x88)))
|
||||
x91, x92 := bits.add_u64(((u64(fiat.u1(x66)) + u64(fiat.u1(x44))) + (u64(fiat.u1(x58)) + x46)), (u64(fiat.u1(x82)) + x70), u64(fiat.u1(x90)))
|
||||
x94, x93 := bits.mul_u64(x2, 0x66e12d94f3d95620)
|
||||
x96, x95 := bits.mul_u64(x2, 0x2845b2392b6bec59)
|
||||
x98, x97 := bits.mul_u64(x2, 0x4699799c49bd6fa6)
|
||||
x100, x99 := bits.mul_u64(x2, 0x83244c95be79eea2)
|
||||
x101, x102 := bits.add_u64(x100, x97, u64(0x0))
|
||||
x103, x104 := bits.add_u64(x98, x95, u64(fiat.u1(x102)))
|
||||
x105, x106 := bits.add_u64(x96, x93, u64(fiat.u1(x104)))
|
||||
x107, x108 := bits.add_u64(x85, x99, u64(0x0))
|
||||
x109, x110 := bits.add_u64(x87, x101, u64(fiat.u1(x108)))
|
||||
x111, x112 := bits.add_u64(x89, x103, u64(fiat.u1(x110)))
|
||||
x113, x114 := bits.add_u64(x91, x105, u64(fiat.u1(x112)))
|
||||
_, x115 := bits.mul_u64(x107, 0xccd1c8aaee00bc4f)
|
||||
x118, x117 := bits.mul_u64(x115, 0xffffffff00000000)
|
||||
x120, x119 := bits.mul_u64(x115, 0xffffffffffffffff)
|
||||
x122, x121 := bits.mul_u64(x115, 0xbce6faada7179e84)
|
||||
x124, x123 := bits.mul_u64(x115, 0xf3b9cac2fc632551)
|
||||
x125, x126 := bits.add_u64(x124, x121, u64(0x0))
|
||||
x127, x128 := bits.add_u64(x122, x119, u64(fiat.u1(x126)))
|
||||
x129, x130 := bits.add_u64(x120, x117, u64(fiat.u1(x128)))
|
||||
_, x132 := bits.add_u64(x107, x123, u64(0x0))
|
||||
x133, x134 := bits.add_u64(x109, x125, u64(fiat.u1(x132)))
|
||||
x135, x136 := bits.add_u64(x111, x127, u64(fiat.u1(x134)))
|
||||
x137, x138 := bits.add_u64(x113, x129, u64(fiat.u1(x136)))
|
||||
x139, x140 := bits.add_u64(((u64(fiat.u1(x114)) + u64(fiat.u1(x92))) + (u64(fiat.u1(x106)) + x94)), (u64(fiat.u1(x130)) + x118), u64(fiat.u1(x138)))
|
||||
x142, x141 := bits.mul_u64(x3, 0x66e12d94f3d95620)
|
||||
x144, x143 := bits.mul_u64(x3, 0x2845b2392b6bec59)
|
||||
x146, x145 := bits.mul_u64(x3, 0x4699799c49bd6fa6)
|
||||
x148, x147 := bits.mul_u64(x3, 0x83244c95be79eea2)
|
||||
x149, x150 := bits.add_u64(x148, x145, u64(0x0))
|
||||
x151, x152 := bits.add_u64(x146, x143, u64(fiat.u1(x150)))
|
||||
x153, x154 := bits.add_u64(x144, x141, u64(fiat.u1(x152)))
|
||||
x155, x156 := bits.add_u64(x133, x147, u64(0x0))
|
||||
x157, x158 := bits.add_u64(x135, x149, u64(fiat.u1(x156)))
|
||||
x159, x160 := bits.add_u64(x137, x151, u64(fiat.u1(x158)))
|
||||
x161, x162 := bits.add_u64(x139, x153, u64(fiat.u1(x160)))
|
||||
_, x163 := bits.mul_u64(x155, 0xccd1c8aaee00bc4f)
|
||||
x166, x165 := bits.mul_u64(x163, 0xffffffff00000000)
|
||||
x168, x167 := bits.mul_u64(x163, 0xffffffffffffffff)
|
||||
x170, x169 := bits.mul_u64(x163, 0xbce6faada7179e84)
|
||||
x172, x171 := bits.mul_u64(x163, 0xf3b9cac2fc632551)
|
||||
x173, x174 := bits.add_u64(x172, x169, u64(0x0))
|
||||
x175, x176 := bits.add_u64(x170, x167, u64(fiat.u1(x174)))
|
||||
x177, x178 := bits.add_u64(x168, x165, u64(fiat.u1(x176)))
|
||||
_, x180 := bits.add_u64(x155, x171, u64(0x0))
|
||||
x181, x182 := bits.add_u64(x157, x173, u64(fiat.u1(x180)))
|
||||
x183, x184 := bits.add_u64(x159, x175, u64(fiat.u1(x182)))
|
||||
x185, x186 := bits.add_u64(x161, x177, u64(fiat.u1(x184)))
|
||||
x187, x188 := bits.add_u64(((u64(fiat.u1(x162)) + u64(fiat.u1(x140))) + (u64(fiat.u1(x154)) + x142)), (u64(fiat.u1(x178)) + x166), u64(fiat.u1(x186)))
|
||||
x189, x190 := bits.sub_u64(x181, 0xf3b9cac2fc632551, u64(0x0))
|
||||
x191, x192 := bits.sub_u64(x183, 0xbce6faada7179e84, u64(fiat.u1(x190)))
|
||||
x193, x194 := bits.sub_u64(x185, 0xffffffffffffffff, u64(fiat.u1(x192)))
|
||||
x195, x196 := bits.sub_u64(x187, 0xffffffff00000000, u64(fiat.u1(x194)))
|
||||
_, x198 := bits.sub_u64(u64(fiat.u1(x188)), u64(0x0), u64(fiat.u1(x196)))
|
||||
x199 := fiat.cmovznz_u64(fiat.u1(x198), x189, x181)
|
||||
x200 := fiat.cmovznz_u64(fiat.u1(x198), x191, x183)
|
||||
x201 := fiat.cmovznz_u64(fiat.u1(x198), x193, x185)
|
||||
x202 := fiat.cmovznz_u64(fiat.u1(x198), x195, x187)
|
||||
out1[0] = x199
|
||||
out1[1] = x200
|
||||
out1[2] = x201
|
||||
out1[3] = x202
|
||||
}
|
||||
607
core/crypto/_fiat/field_scalarp384r1/field.odin
Normal file
607
core/crypto/_fiat/field_scalarp384r1/field.odin
Normal file
@@ -0,0 +1,607 @@
|
||||
package field_scalarp384r1
|
||||
|
||||
import "core:crypto"
|
||||
import subtle "core:crypto/_subtle"
|
||||
import "core:encoding/endian"
|
||||
import "core:math/bits"
|
||||
|
||||
@(private, rodata)
|
||||
TWO_256 := Montgomery_Domain_Field_Element{
|
||||
17975668497346362272,
|
||||
12895982994901192340,
|
||||
1913828944324294218,
|
||||
902107514168524577,
|
||||
1374695839762142861,
|
||||
12098342389602539653,
|
||||
}
|
||||
|
||||
fe_clear :: proc "contextless" (arg1: ^Montgomery_Domain_Field_Element) {
|
||||
crypto.zero_explicit(arg1, size_of(Montgomery_Domain_Field_Element))
|
||||
}
|
||||
|
||||
fe_clear_vec :: proc "contextless" (
|
||||
arg1: []^Montgomery_Domain_Field_Element,
|
||||
) {
|
||||
for fe in arg1 {
|
||||
fe_clear(fe)
|
||||
}
|
||||
}
|
||||
|
||||
fe_from_bytes :: proc "contextless" (
|
||||
out1: ^Montgomery_Domain_Field_Element,
|
||||
arg1: []byte,
|
||||
) -> bool {
|
||||
ensure_contextless(len(out1) <= 64, "p384r1: invalid scalar input buffer")
|
||||
|
||||
is_canonical := false
|
||||
s_len := len(arg1)
|
||||
switch {
|
||||
case s_len < 48:
|
||||
// No way this can be greater than the order.
|
||||
fe_unchecked_set(out1, arg1)
|
||||
is_canonical = true
|
||||
case s_len == 48:
|
||||
// There is no way for any 384-bit value to be >= 2n, so
|
||||
// the reduction can be done by `src - n` and a conditional
|
||||
// select based on the underflow.
|
||||
//
|
||||
// It is *extremely* unlikely that the reduction is actually
|
||||
// needed.
|
||||
tmp: Non_Montgomery_Domain_Field_Element = ---
|
||||
fe_unchecked_set_saturated(&tmp, arg1)
|
||||
reduced := tmp
|
||||
defer crypto.zero_explicit(&tmp, size_of(tmp))
|
||||
defer crypto.zero_explicit(&reduced, size_of(reduced))
|
||||
|
||||
borrow: u64
|
||||
reduced[0], borrow = bits.sub_u64(tmp[0], ELL[0], borrow)
|
||||
reduced[1], borrow = bits.sub_u64(tmp[1], ELL[1], borrow)
|
||||
reduced[2], borrow = bits.sub_u64(tmp[2], ELL[2], borrow)
|
||||
reduced[3], borrow = bits.sub_u64(tmp[3], ELL[3], borrow)
|
||||
reduced[4], borrow = bits.sub_u64(tmp[4], ELL[4], borrow)
|
||||
reduced[5], borrow = bits.sub_u64(tmp[5], ELL[5], borrow)
|
||||
need_reduced := subtle.u64_is_zero(borrow)
|
||||
|
||||
fe_cond_select(&tmp, &tmp, &reduced, int(need_reduced))
|
||||
fe_to_montgomery(out1, &tmp)
|
||||
|
||||
is_canonical = need_reduced == 0
|
||||
case:
|
||||
// Use Frank Denis' trick, as documented by Filippo Valsorda
|
||||
// at https://words.filippo.io/dispatches/wide-reduction/
|
||||
//
|
||||
// "I represent the value as a+b*2^192+c*2^384"
|
||||
//
|
||||
// Since digests beyond 512-bits are unrealistic, we do
|
||||
// "a+b*2^256"
|
||||
|
||||
// Zero extend to 512-bits.
|
||||
src_512: [64]byte
|
||||
copy(src_512[64-s_len:], arg1)
|
||||
defer crypto.zero_explicit(&src_512, size_of(src_512))
|
||||
|
||||
fe_unchecked_set(out1, src_512[32:]) // a
|
||||
b: Montgomery_Domain_Field_Element
|
||||
fe_unchecked_set(&b, src_512[:32]) // b
|
||||
|
||||
fe_mul(&b, &b, &TWO_256)
|
||||
fe_add(out1, out1, &b)
|
||||
|
||||
fe_clear(&b)
|
||||
}
|
||||
|
||||
return !is_canonical
|
||||
}
|
||||
|
||||
@(private)
|
||||
fe_is_canonical :: proc "contextless" (arg1: []byte) -> bool {
|
||||
_, borrow := bits.sub_u64(ELL[0] - 1, endian.unchecked_get_u64be(arg1[40:]), 0)
|
||||
_, borrow = bits.sub_u64(ELL[1], endian.unchecked_get_u64be(arg1[32:]), borrow)
|
||||
_, borrow = bits.sub_u64(ELL[2], endian.unchecked_get_u64be(arg1[24:]), borrow)
|
||||
_, borrow = bits.sub_u64(ELL[3], endian.unchecked_get_u64be(arg1[16:]), borrow)
|
||||
_, borrow = bits.sub_u64(ELL[4], endian.unchecked_get_u64be(arg1[8:]), borrow)
|
||||
_, borrow = bits.sub_u64(ELL[5], endian.unchecked_get_u64be(arg1[0:]), borrow)
|
||||
return borrow == 0
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
fe_unchecked_set_saturated :: proc "contextless" (out1: ^Non_Montgomery_Domain_Field_Element, arg1: []byte) {
|
||||
out1[0] = endian.unchecked_get_u64be(arg1[40:])
|
||||
out1[1] = endian.unchecked_get_u64be(arg1[32:])
|
||||
out1[2] = endian.unchecked_get_u64be(arg1[24:])
|
||||
out1[3] = endian.unchecked_get_u64be(arg1[16:])
|
||||
out1[4] = endian.unchecked_get_u64be(arg1[8:])
|
||||
out1[5] = endian.unchecked_get_u64be(arg1[0:])
|
||||
}
|
||||
|
||||
@(private)
|
||||
fe_unchecked_set :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element, arg1: []byte) {
|
||||
arg1_384: [48]byte
|
||||
defer crypto.zero_explicit(&arg1_384, size_of(arg1_384))
|
||||
copy(arg1_384[48-len(arg1):], arg1)
|
||||
|
||||
tmp: Non_Montgomery_Domain_Field_Element = ---
|
||||
fe_unchecked_set_saturated(&tmp, arg1_384[:])
|
||||
defer crypto.zero_explicit(&tmp, size_of(tmp))
|
||||
|
||||
fe_to_montgomery(out1, &tmp)
|
||||
}
|
||||
|
||||
fe_to_bytes :: proc "contextless" (out1: []byte, arg1: ^Montgomery_Domain_Field_Element) {
|
||||
ensure_contextless(len(out1) == 48, "p384r1: invalid scalar output buffer")
|
||||
|
||||
tmp: Non_Montgomery_Domain_Field_Element = ---
|
||||
fe_from_montgomery(&tmp, arg1)
|
||||
|
||||
// Note: Likewise, output in big-endian.
|
||||
endian.unchecked_put_u64be(out1[40:], tmp[0])
|
||||
endian.unchecked_put_u64be(out1[32:], tmp[1])
|
||||
endian.unchecked_put_u64be(out1[24:], tmp[2])
|
||||
endian.unchecked_put_u64be(out1[16:], tmp[3])
|
||||
endian.unchecked_put_u64be(out1[8:], tmp[4])
|
||||
endian.unchecked_put_u64be(out1[0:], tmp[5])
|
||||
|
||||
crypto.zero_explicit(&tmp, size_of(tmp))
|
||||
}
|
||||
|
||||
fe_equal :: proc "contextless" (arg1, arg2: ^Montgomery_Domain_Field_Element) -> int {
|
||||
tmp: Montgomery_Domain_Field_Element = ---
|
||||
fe_sub(&tmp, arg1, arg2)
|
||||
|
||||
is_eq := subtle.eq(fe_non_zero(&tmp), 0)
|
||||
|
||||
fe_clear(&tmp)
|
||||
|
||||
return int(is_eq)
|
||||
}
|
||||
|
||||
fe_is_odd :: proc "contextless" (arg1: ^Montgomery_Domain_Field_Element) -> int {
|
||||
tmp: Non_Montgomery_Domain_Field_Element = ---
|
||||
defer crypto.zero_explicit(&tmp, size_of(tmp))
|
||||
|
||||
fe_from_montgomery(&tmp, arg1)
|
||||
return int(tmp[0] & 1)
|
||||
}
|
||||
|
||||
fe_zero :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element) {
|
||||
out1[0] = 0
|
||||
out1[1] = 0
|
||||
out1[2] = 0
|
||||
out1[3] = 0
|
||||
out1[4] = 0
|
||||
out1[5] = 0
|
||||
}
|
||||
|
||||
fe_set :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
|
||||
x1 := arg1[0]
|
||||
x2 := arg1[1]
|
||||
x3 := arg1[2]
|
||||
x4 := arg1[3]
|
||||
x5 := arg1[4]
|
||||
x6 := arg1[5]
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
out1[4] = x5
|
||||
out1[5] = x6
|
||||
}
|
||||
|
||||
@(optimization_mode = "none")
|
||||
fe_cond_swap :: #force_no_inline proc "contextless" (out1, out2: ^Montgomery_Domain_Field_Element, arg1: int) {
|
||||
mask := (u64(arg1) * 0xffffffffffffffff)
|
||||
x := (out1[0] ~ out2[0]) & mask
|
||||
x1, y1 := out1[0] ~ x, out2[0] ~ x
|
||||
x = (out1[1] ~ out2[1]) & mask
|
||||
x2, y2 := out1[1] ~ x, out2[1] ~ x
|
||||
x = (out1[2] ~ out2[2]) & mask
|
||||
x3, y3 := out1[2] ~ x, out2[2] ~ x
|
||||
x = (out1[3] ~ out2[3]) & mask
|
||||
x4, y4 := out1[3] ~ x, out2[3] ~ x
|
||||
x = (out1[4] ~ out2[4]) & mask
|
||||
x5, y5 := out1[4] ~ x, out2[4] ~ x
|
||||
x = (out1[5] ~ out2[5]) & mask
|
||||
x6, y6 := out1[5] ~ x, out2[5] ~ x
|
||||
out1[0], out2[0] = x1, y1
|
||||
out1[1], out2[1] = x2, y2
|
||||
out1[2], out2[2] = x3, y3
|
||||
out1[3], out2[3] = x4, y4
|
||||
out1[4], out2[4] = x5, y5
|
||||
out1[5], out2[5] = x6, y6
|
||||
}
|
||||
|
||||
@(optimization_mode = "none")
|
||||
fe_cond_select :: #force_no_inline proc "contextless" (
|
||||
out1, arg1, arg2: ^$T,
|
||||
arg3: int,
|
||||
) where T == Montgomery_Domain_Field_Element || T == Non_Montgomery_Domain_Field_Element {
|
||||
mask := (u64(arg3) * 0xffffffffffffffff)
|
||||
x1 := ((mask & arg2[0]) | ((~mask) & arg1[0]))
|
||||
x2 := ((mask & arg2[1]) | ((~mask) & arg1[1]))
|
||||
x3 := ((mask & arg2[2]) | ((~mask) & arg1[2]))
|
||||
x4 := ((mask & arg2[3]) | ((~mask) & arg1[3]))
|
||||
x5 := ((mask & arg2[4]) | ((~mask) & arg1[4]))
|
||||
x6 := ((mask & arg2[5]) | ((~mask) & arg1[5]))
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
out1[4] = x5
|
||||
out1[5] = x6
|
||||
}
|
||||
|
||||
fe_cond_negate :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element, ctrl: int) {
|
||||
tmp1: Montgomery_Domain_Field_Element = ---
|
||||
fe_opp(&tmp1, arg1)
|
||||
fe_cond_select(out1, arg1, &tmp1, ctrl)
|
||||
|
||||
fe_clear(&tmp1)
|
||||
}
|
||||
|
||||
fe_pow2k :: proc "contextless" (
|
||||
out1: ^Montgomery_Domain_Field_Element,
|
||||
arg1: ^Montgomery_Domain_Field_Element,
|
||||
arg2: uint,
|
||||
) {
|
||||
// Special case: `arg1^(2 * 0) = 1`, though this should never happen.
|
||||
if arg2 == 0 {
|
||||
fe_one(out1)
|
||||
return
|
||||
}
|
||||
|
||||
fe_square(out1, arg1)
|
||||
for _ in 1 ..< arg2 {
|
||||
fe_square(out1, out1)
|
||||
}
|
||||
}
|
||||
|
||||
fe_inv :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
|
||||
// Inversion computation is derived from the addition chain:
|
||||
//
|
||||
// _10 = 2*1
|
||||
// _11 = 1 + _10
|
||||
// _101 = _10 + _11
|
||||
// _111 = _10 + _101
|
||||
// _1001 = _10 + _111
|
||||
// _1011 = _10 + _1001
|
||||
// _1101 = _10 + _1011
|
||||
// _1111 = _10 + _1101
|
||||
// _11110 = 2*_1111
|
||||
// _11111 = 1 + _11110
|
||||
// _1111100 = _11111 << 2
|
||||
// i14 = _1111100 << 2
|
||||
// i26 = (i14 << 3 + _1111100) << 7 + i14
|
||||
// i42 = i26 << 15 + i26
|
||||
// x64 = i42 << 30 + i42 + _1111
|
||||
// x128 = x64 << 64 + x64
|
||||
// x192 = x128 << 64 + x64
|
||||
// x194 = x192 << 2 + _11
|
||||
// i225 = ((x194 << 6 + _111) << 3 + _11) << 7
|
||||
// i235 = 2*((_1101 + i225) << 6 + _1101) + 1
|
||||
// i258 = ((i235 << 11 + _11111) << 2 + 1) << 8
|
||||
// i269 = ((_1101 + i258) << 2 + _11) << 6 + _1011
|
||||
// i286 = ((i269 << 4 + _111) << 6 + _11111) << 5
|
||||
// i308 = ((_1011 + i286) << 10 + _1101) << 9 + _1101
|
||||
// i323 = ((i308 << 4 + _1011) << 6 + _1001) << 3
|
||||
// i340 = ((1 + i323) << 7 + _1011) << 7 + _101
|
||||
// i357 = ((i340 << 5 + _111) << 5 + _1111) << 5
|
||||
// i369 = ((_1011 + i357) << 4 + _1011) << 5 + _111
|
||||
// i387 = ((i369 << 3 + _11) << 7 + _11) << 6
|
||||
// i397 = ((_1011 + i387) << 4 + _101) << 3 + _11
|
||||
// i413 = ((i397 << 4 + _11) << 4 + _11) << 6
|
||||
// i427 = ((_101 + i413) << 5 + _101) << 6 + _1011
|
||||
// return (2*i427 + 1) << 4 + 1
|
||||
//
|
||||
// Operations: 381 squares 53 multiplies
|
||||
//
|
||||
// Generated by github.com/mmcloughlin/addchain v0.4.0.
|
||||
|
||||
// Note: Need to stash `arg1` (`xx`) in the case that `out1`/`arg1` alias,
|
||||
// as `arg1` is used after `out1` has been altered.
|
||||
t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, xx: Montgomery_Domain_Field_Element = ---, ---, ---, ---, ---, ---, ---, ---, ---, ---, arg1^
|
||||
|
||||
// Step 1: t3 = x^0x2
|
||||
fe_square(&t3, arg1)
|
||||
|
||||
// Step 2: t1 = x^0x3
|
||||
fe_mul(&t1, arg1, &t3)
|
||||
|
||||
// Step 3: t0 = x^0x5
|
||||
fe_mul(&t0, &t3, &t1)
|
||||
|
||||
// Step 4: t2 = x^0x7
|
||||
fe_mul(&t2, &t3, &t0)
|
||||
|
||||
// Step 5: t4 = x^0x9
|
||||
fe_mul(&t4, &t3, &t2)
|
||||
|
||||
// Step 6: z = x^0xb
|
||||
fe_mul(out1, &t3, &t4)
|
||||
|
||||
// Step 7: t5 = x^0xd
|
||||
fe_mul(&t5, &t3, out1)
|
||||
|
||||
// Step 8: t3 = x^0xf
|
||||
fe_mul(&t3, &t3, &t5)
|
||||
|
||||
// Step 9: t6 = x^0x1e
|
||||
fe_square(&t6, &t3)
|
||||
|
||||
// Step 10: t6 = x^0x1f
|
||||
fe_mul(&t6, &xx, &t6)
|
||||
|
||||
// Step 12: t8 = x^0x7c
|
||||
fe_pow2k(&t8, &t6, 2)
|
||||
|
||||
// Step 14: t7 = x^0x1f0
|
||||
fe_pow2k(&t7, &t8, 2)
|
||||
|
||||
// Step 17: t9 = x^0xf80
|
||||
fe_pow2k(&t9, &t7, 3)
|
||||
|
||||
// Step 18: t8 = x^0xffc
|
||||
fe_mul(&t8, &t8, &t9)
|
||||
|
||||
// Step 25: t8 = x^0x7fe00
|
||||
fe_pow2k(&t8, &t8, 7)
|
||||
|
||||
// Step 26: t7 = x^0x7fff0
|
||||
fe_mul(&t7, &t7, &t8)
|
||||
|
||||
// Step 41: t8 = x^0x3fff80000
|
||||
fe_pow2k(&t8, &t7, 15)
|
||||
|
||||
// Step 42: t7 = x^0x3fffffff0
|
||||
fe_mul(&t7, &t7, &t8)
|
||||
|
||||
// Step 72: t8 = x^0xfffffffc00000000
|
||||
fe_pow2k(&t8, &t7, 30)
|
||||
|
||||
// Step 73: t7 = x^0xfffffffffffffff0
|
||||
fe_mul(&t7, &t7, &t8)
|
||||
|
||||
// Step 74: t7 = x^0xffffffffffffffff
|
||||
fe_mul(&t7, &t3, &t7)
|
||||
|
||||
// Step 138: t8 = x^0xffffffffffffffff0000000000000000
|
||||
fe_pow2k(&t8, &t7, 64)
|
||||
|
||||
// Step 139: t8 = x^0xffffffffffffffffffffffffffffffff
|
||||
fe_mul(&t8, &t7, &t8)
|
||||
|
||||
// Step 203: t8 = x^0xffffffffffffffffffffffffffffffff0000000000000000
|
||||
fe_pow2k(&t8, &t8, 64)
|
||||
|
||||
// Step 204: t7 = x^0xffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
fe_mul(&t7, &t7, &t8)
|
||||
|
||||
// Step 206: t7 = x^0x3fffffffffffffffffffffffffffffffffffffffffffffffc
|
||||
fe_pow2k(&t7, &t7, 2)
|
||||
|
||||
// Step 207: t7 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
fe_mul(&t7, &t1, &t7)
|
||||
|
||||
// Step 213: t7 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc0
|
||||
fe_pow2k(&t7, &t7, 6)
|
||||
|
||||
// Step 214: t7 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7
|
||||
fe_mul(&t7, &t2, &t7)
|
||||
|
||||
// Step 217: t7 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffe38
|
||||
fe_pow2k(&t7, &t7, 3)
|
||||
|
||||
// Step 218: t7 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffe3b
|
||||
fe_mul(&t7, &t1, &t7)
|
||||
|
||||
// Step 225: t7 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d80
|
||||
fe_pow2k(&t7, &t7, 7)
|
||||
|
||||
// Step 226: t7 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d
|
||||
fe_mul(&t7, &t5, &t7)
|
||||
|
||||
// Step 232: t7 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc76340
|
||||
fe_pow2k(&t7, &t7, 6)
|
||||
|
||||
// Step 233: t7 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d
|
||||
fe_mul(&t7, &t5, &t7)
|
||||
|
||||
// Step 234: t7 = x^0x1ffffffffffffffffffffffffffffffffffffffffffffffff8ec69a
|
||||
fe_square(&t7, &t7)
|
||||
|
||||
// Step 235: t7 = x^0x1ffffffffffffffffffffffffffffffffffffffffffffffff8ec69b
|
||||
fe_mul(&t7, &xx, &t7)
|
||||
|
||||
// Step 246: t7 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d800
|
||||
fe_pow2k(&t7, &t7, 11)
|
||||
|
||||
// Step 247: t7 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f
|
||||
fe_mul(&t7, &t6, &t7)
|
||||
|
||||
// Step 249: t7 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607c
|
||||
fe_pow2k(&t7, &t7, 2)
|
||||
|
||||
// Step 250: t7 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d
|
||||
fe_mul(&t7, &xx, &t7)
|
||||
|
||||
// Step 258: t7 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d00
|
||||
fe_pow2k(&t7, &t7, 8)
|
||||
|
||||
// Step 259: t7 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0d
|
||||
fe_mul(&t7, &t5, &t7)
|
||||
|
||||
// Step 261: t7 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f434
|
||||
fe_pow2k(&t7, &t7, 2)
|
||||
|
||||
// Step 262: t7 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f437
|
||||
fe_mul(&t7, &t1, &t7)
|
||||
|
||||
// Step 268: t7 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dc0
|
||||
fe_pow2k(&t7, &t7, 6)
|
||||
|
||||
// Step 269: t7 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb
|
||||
fe_mul(&t7, out1, &t7)
|
||||
|
||||
// Step 273: t7 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb0
|
||||
fe_pow2k(&t7, &t7, 4)
|
||||
|
||||
// Step 274: t7 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb7
|
||||
fe_mul(&t7, &t2, &t7)
|
||||
|
||||
// Step 280: t7 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372dc0
|
||||
fe_pow2k(&t7, &t7, 6)
|
||||
|
||||
// Step 281: t6 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf
|
||||
fe_mul(&t6, &t6, &t7)
|
||||
|
||||
// Step 286: t6 = x^0x1ffffffffffffffffffffffffffffffffffffffffffffffff8ec69b03e86e5bbe0
|
||||
fe_pow2k(&t6, &t6, 5)
|
||||
|
||||
// Step 287: t6 = x^0x1ffffffffffffffffffffffffffffffffffffffffffffffff8ec69b03e86e5bbeb
|
||||
fe_mul(&t6, out1, &t6)
|
||||
|
||||
// Step 297: t6 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffe3b1a6c0fa1b96efac00
|
||||
fe_pow2k(&t6, &t6, 10)
|
||||
|
||||
// Step 298: t6 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffe3b1a6c0fa1b96efac0d
|
||||
fe_mul(&t6, &t5, &t6)
|
||||
|
||||
// Step 307: t6 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a00
|
||||
fe_pow2k(&t6, &t6, 9)
|
||||
|
||||
// Step 308: t5 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0d
|
||||
fe_mul(&t5, &t5, &t6)
|
||||
|
||||
// Step 312: t5 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0d0
|
||||
fe_pow2k(&t5, &t5, 4)
|
||||
|
||||
// Step 313: t5 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db
|
||||
fe_mul(&t5, out1, &t5)
|
||||
|
||||
// Step 319: t5 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb77d606836c0
|
||||
fe_pow2k(&t5, &t5, 6)
|
||||
|
||||
// Step 320: t4 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb77d606836c9
|
||||
fe_mul(&t4, &t4, &t5)
|
||||
|
||||
// Step 323: t4 = x^0x1ffffffffffffffffffffffffffffffffffffffffffffffff8ec69b03e86e5bbeb0341b648
|
||||
fe_pow2k(&t4, &t4, 3)
|
||||
|
||||
// Step 324: t4 = x^0x1ffffffffffffffffffffffffffffffffffffffffffffffff8ec69b03e86e5bbeb0341b649
|
||||
fe_mul(&t4, &xx, &t4)
|
||||
|
||||
// Step 331: t4 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db2480
|
||||
fe_pow2k(&t4, &t4, 7)
|
||||
|
||||
// Step 332: t4 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b
|
||||
fe_mul(&t4, out1, &t4)
|
||||
|
||||
// Step 339: t4 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffe3b1a6c0fa1b96efac0d06d924580
|
||||
fe_pow2k(&t4, &t4, 7)
|
||||
|
||||
// Step 340: t4 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffe3b1a6c0fa1b96efac0d06d924585
|
||||
fe_mul(&t4, &t0, &t4)
|
||||
|
||||
// Step 345: t4 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a0
|
||||
fe_pow2k(&t4, &t4, 5)
|
||||
|
||||
// Step 346: t4 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a7
|
||||
fe_mul(&t4, &t2, &t4)
|
||||
|
||||
// Step 351: t4 = x^0x1ffffffffffffffffffffffffffffffffffffffffffffffff8ec69b03e86e5bbeb0341b6491614e0
|
||||
fe_pow2k(&t4, &t4, 5)
|
||||
|
||||
// Step 352: t3 = x^0x1ffffffffffffffffffffffffffffffffffffffffffffffff8ec69b03e86e5bbeb0341b6491614ef
|
||||
fe_mul(&t3, &t3, &t4)
|
||||
|
||||
// Step 357: t3 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb77d606836c922c29de0
|
||||
fe_pow2k(&t3, &t3, 5)
|
||||
|
||||
// Step 358: t3 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb77d606836c922c29deb
|
||||
fe_mul(&t3, out1, &t3)
|
||||
|
||||
// Step 362: t3 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb77d606836c922c29deb0
|
||||
fe_pow2k(&t3, &t3, 4)
|
||||
|
||||
// Step 363: t3 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb77d606836c922c29debb
|
||||
fe_mul(&t3, out1, &t3)
|
||||
|
||||
// Step 368: t3 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffe3b1a6c0fa1b96efac0d06d9245853bd760
|
||||
fe_pow2k(&t3, &t3, 5)
|
||||
|
||||
// Step 369: t2 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffe3b1a6c0fa1b96efac0d06d9245853bd767
|
||||
fe_mul(&t2, &t2, &t3)
|
||||
|
||||
// Step 372: t2 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb77d606836c922c29debb38
|
||||
fe_pow2k(&t2, &t2, 3)
|
||||
|
||||
// Step 373: t2 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb77d606836c922c29debb3b
|
||||
fe_mul(&t2, &t1, &t2)
|
||||
|
||||
// Step 380: t2 = x^0x1ffffffffffffffffffffffffffffffffffffffffffffffff8ec69b03e86e5bbeb0341b6491614ef5d9d80
|
||||
fe_pow2k(&t2, &t2, 7)
|
||||
|
||||
// Step 381: t2 = x^0x1ffffffffffffffffffffffffffffffffffffffffffffffff8ec69b03e86e5bbeb0341b6491614ef5d9d83
|
||||
fe_mul(&t2, &t1, &t2)
|
||||
|
||||
// Step 387: t2 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffe3b1a6c0fa1b96efac0d06d9245853bd76760c0
|
||||
fe_pow2k(&t2, &t2, 6)
|
||||
|
||||
// Step 388: t2 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffe3b1a6c0fa1b96efac0d06d9245853bd76760cb
|
||||
fe_mul(&t2, out1, &t2)
|
||||
|
||||
// Step 392: t2 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffe3b1a6c0fa1b96efac0d06d9245853bd76760cb0
|
||||
fe_pow2k(&t2, &t2, 4)
|
||||
|
||||
// Step 393: t2 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffe3b1a6c0fa1b96efac0d06d9245853bd76760cb5
|
||||
fe_mul(&t2, &t0, &t2)
|
||||
|
||||
// Step 396: t2 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb77d606836c922c29debb3b065a8
|
||||
fe_pow2k(&t2, &t2, 3)
|
||||
|
||||
// Step 397: t2 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb77d606836c922c29debb3b065ab
|
||||
fe_mul(&t2, &t1, &t2)
|
||||
|
||||
// Step 401: t2 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb77d606836c922c29debb3b065ab0
|
||||
fe_pow2k(&t2, &t2, 4)
|
||||
|
||||
// Step 402: t2 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb77d606836c922c29debb3b065ab3
|
||||
fe_mul(&t2, &t1, &t2)
|
||||
|
||||
// Step 406: t2 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb77d606836c922c29debb3b065ab30
|
||||
fe_pow2k(&t2, &t2, 4)
|
||||
|
||||
// Step 407: t1 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb77d606836c922c29debb3b065ab33
|
||||
fe_mul(&t1, &t1, &t2)
|
||||
|
||||
// Step 413: t1 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc0
|
||||
fe_pow2k(&t1, &t1, 6)
|
||||
|
||||
// Step 414: t1 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc5
|
||||
fe_mul(&t1, &t0, &t1)
|
||||
|
||||
// Step 419: t1 = x^0x1ffffffffffffffffffffffffffffffffffffffffffffffff8ec69b03e86e5bbeb0341b6491614ef5d9d832d5998a0
|
||||
fe_pow2k(&t1, &t1, 5)
|
||||
|
||||
// Step 420: t0 = x^0x1ffffffffffffffffffffffffffffffffffffffffffffffff8ec69b03e86e5bbeb0341b6491614ef5d9d832d5998a5
|
||||
fe_mul(&t0, &t0, &t1)
|
||||
|
||||
// Step 426: t0 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffe3b1a6c0fa1b96efac0d06d9245853bd76760cb56662940
|
||||
fe_pow2k(&t0, &t0, 6)
|
||||
|
||||
// Step 427: z = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffe3b1a6c0fa1b96efac0d06d9245853bd76760cb5666294b
|
||||
fe_mul(out1, out1, &t0)
|
||||
|
||||
// Step 428: z = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc5296
|
||||
fe_square(out1, out1)
|
||||
|
||||
// Step 429: z = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc5297
|
||||
fe_mul(out1, &xx, out1)
|
||||
|
||||
// Step 433: z = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52970
|
||||
fe_pow2k(out1, out1, 4)
|
||||
|
||||
// Step 434: z = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52971
|
||||
fe_mul(out1, &xx, out1)
|
||||
|
||||
fe_clear_vec([]^Montgomery_Domain_Field_Element{&t0, &t1, &t2, &t3, &t4, &t5, &t6, &t7, &t8, &t9, &xx})
|
||||
}
|
||||
1081
core/crypto/_fiat/field_scalarp384r1/field64.odin
Normal file
1081
core/crypto/_fiat/field_scalarp384r1/field64.odin
Normal file
File diff suppressed because it is too large
Load Diff
@@ -16,7 +16,7 @@ package _sha3
|
||||
*/
|
||||
|
||||
import "core:math/bits"
|
||||
import "core:mem"
|
||||
import "core:crypto"
|
||||
|
||||
ROUNDS :: 24
|
||||
|
||||
@@ -179,7 +179,7 @@ reset :: proc "contextless" (ctx: ^Context) {
|
||||
return
|
||||
}
|
||||
|
||||
mem.zero_explicit(ctx, size_of(ctx^))
|
||||
crypto.zero_explicit(ctx, size_of(ctx^))
|
||||
}
|
||||
|
||||
shake_xof :: proc "contextless" (ctx: ^Context) {
|
||||
|
||||
42
core/crypto/_subtle/subtle.odin
Normal file
42
core/crypto/_subtle/subtle.odin
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
Various useful bit operations in constant time.
|
||||
*/
|
||||
package _subtle
|
||||
|
||||
import "core:math/bits"
|
||||
|
||||
// byte_eq returns 1 if and only if (⟺) a == b, 0 otherwise.
|
||||
@(optimization_mode="none")
|
||||
byte_eq :: proc "contextless" (a, b: byte) -> int {
|
||||
v := a ~ b
|
||||
|
||||
// v == 0 if and only if (⟺) a == b. The subtraction will underflow, setting the
|
||||
// sign bit, which will get returned.
|
||||
return int((u32(v)-1) >> 31)
|
||||
}
|
||||
|
||||
// u64_eq returns 1 if and only if (⟺) a == b, 0 otherwise.
|
||||
@(optimization_mode="none")
|
||||
u64_eq :: proc "contextless" (a, b: u64) -> u64 {
|
||||
_, borrow := bits.sub_u64(0, a ~ b, 0)
|
||||
return (~borrow) & 1
|
||||
}
|
||||
|
||||
eq :: proc {
|
||||
byte_eq,
|
||||
u64_eq,
|
||||
}
|
||||
|
||||
// u64_is_zero returns 1 if and only if (⟺) a == 0, 0 otherwise.
|
||||
@(optimization_mode="none")
|
||||
u64_is_zero :: proc "contextless" (a: u64) -> u64 {
|
||||
_, borrow := bits.sub_u64(a, 1, 0)
|
||||
return borrow
|
||||
}
|
||||
|
||||
// u64_is_non_zero returns 1 if and only if (⟺) a != 0, 0 otherwise.
|
||||
@(optimization_mode="none")
|
||||
u64_is_non_zero :: proc "contextless" (a: u64) -> u64 {
|
||||
is_zero := u64_is_zero(a)
|
||||
return (~is_zero) & 1
|
||||
}
|
||||
205
core/crypto/_weierstrass/fe.odin
Normal file
205
core/crypto/_weierstrass/fe.odin
Normal file
@@ -0,0 +1,205 @@
|
||||
package _weierstrass
|
||||
|
||||
import p256r1 "core:crypto/_fiat/field_p256r1"
|
||||
import p384r1 "core:crypto/_fiat/field_p384r1"
|
||||
import subtle "core:crypto/_subtle"
|
||||
|
||||
Field_Element_p256r1 :: p256r1.Montgomery_Domain_Field_Element
|
||||
Field_Element_p384r1 :: p384r1.Montgomery_Domain_Field_Element
|
||||
|
||||
FE_SIZE_P256R1 :: 32
|
||||
FE_SIZE_P384R1 :: 48
|
||||
|
||||
fe_clear :: proc {
|
||||
p256r1.fe_clear,
|
||||
p384r1.fe_clear,
|
||||
}
|
||||
|
||||
fe_clear_vec :: proc {
|
||||
p256r1.fe_clear_vec,
|
||||
p384r1.fe_clear_vec,
|
||||
}
|
||||
|
||||
fe_set_bytes :: proc {
|
||||
p256r1.fe_from_bytes,
|
||||
p384r1.fe_from_bytes,
|
||||
}
|
||||
|
||||
fe_bytes :: proc {
|
||||
p256r1.fe_to_bytes,
|
||||
p384r1.fe_to_bytes,
|
||||
}
|
||||
|
||||
fe_set :: proc {
|
||||
p256r1.fe_set,
|
||||
p384r1.fe_set,
|
||||
}
|
||||
|
||||
fe_zero :: proc {
|
||||
p256r1.fe_zero,
|
||||
p384r1.fe_zero,
|
||||
}
|
||||
|
||||
fe_a :: proc {
|
||||
fe_a_p256r1,
|
||||
fe_a_p384r1,
|
||||
}
|
||||
|
||||
fe_b :: proc {
|
||||
fe_b_p256r1,
|
||||
fe_b_p384r1,
|
||||
}
|
||||
|
||||
fe_gen_x :: proc {
|
||||
fe_gen_x_p256r1,
|
||||
fe_gen_x_p384r1,
|
||||
}
|
||||
|
||||
fe_gen_y :: proc {
|
||||
fe_gen_y_p256r1,
|
||||
fe_gen_y_p384r1,
|
||||
}
|
||||
|
||||
fe_one :: proc {
|
||||
p256r1.fe_one,
|
||||
p384r1.fe_one,
|
||||
}
|
||||
|
||||
fe_add :: proc {
|
||||
p256r1.fe_add,
|
||||
p384r1.fe_add,
|
||||
}
|
||||
|
||||
fe_sub :: proc {
|
||||
p256r1.fe_sub,
|
||||
p384r1.fe_sub,
|
||||
}
|
||||
|
||||
fe_negate :: proc {
|
||||
p256r1.fe_opp,
|
||||
p384r1.fe_opp,
|
||||
}
|
||||
|
||||
fe_mul :: proc {
|
||||
p256r1.fe_mul,
|
||||
p384r1.fe_mul,
|
||||
}
|
||||
|
||||
fe_square :: proc {
|
||||
p256r1.fe_square,
|
||||
p384r1.fe_square,
|
||||
}
|
||||
|
||||
fe_inv :: proc {
|
||||
p256r1.fe_inv,
|
||||
p384r1.fe_inv,
|
||||
}
|
||||
|
||||
fe_sqrt :: proc {
|
||||
p256r1.fe_sqrt,
|
||||
p384r1.fe_sqrt,
|
||||
}
|
||||
|
||||
fe_equal :: proc {
|
||||
p256r1.fe_equal,
|
||||
p384r1.fe_equal,
|
||||
}
|
||||
|
||||
fe_is_odd :: proc {
|
||||
p256r1.fe_is_odd,
|
||||
p384r1.fe_is_odd,
|
||||
}
|
||||
|
||||
fe_is_zero :: proc {
|
||||
fe_is_zero_p256r1,
|
||||
fe_is_zero_p384r1,
|
||||
}
|
||||
|
||||
fe_cond_select :: proc {
|
||||
p256r1.fe_cond_select,
|
||||
p384r1.fe_cond_select,
|
||||
}
|
||||
|
||||
fe_a_p256r1 :: proc "contextless" (fe: ^Field_Element_p256r1) {
|
||||
// a = 0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc
|
||||
// = -3 mod p
|
||||
fe[0] = 18446744073709551612
|
||||
fe[1] = 17179869183
|
||||
fe[2] = 0
|
||||
fe[3] = 18446744056529682436
|
||||
}
|
||||
|
||||
fe_b_p256r1 :: proc "contextless" (fe: ^Field_Element_p256r1) {
|
||||
// b = 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b
|
||||
fe[0] = 15608596021259845087
|
||||
fe[1] = 12461466548982526096
|
||||
fe[2] = 16546823903870267094
|
||||
fe[3] = 15866188208926050356
|
||||
}
|
||||
|
||||
fe_gen_x_p256r1 :: proc "contextless" (fe: ^Field_Element_p256r1) {
|
||||
// G_x = 0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296
|
||||
fe[0] = 8784043285714375740
|
||||
fe[1] = 8483257759279461889
|
||||
fe[2] = 8789745728267363600
|
||||
fe[3] = 1770019616739251654
|
||||
}
|
||||
|
||||
fe_gen_y_p256r1 :: proc "contextless" (fe: ^Field_Element_p256r1) {
|
||||
// G_y = 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5
|
||||
fe[0] = 15992936863339206154
|
||||
fe[1] = 10037038012062884956
|
||||
fe[2] = 15197544864945402661
|
||||
fe[3] = 9615747158586711429
|
||||
}
|
||||
|
||||
fe_a_p384r1 :: proc "contextless" (fe: ^Field_Element_p384r1) {
|
||||
// a = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffffc
|
||||
// = -3 mod p
|
||||
fe[0] = 17179869180
|
||||
fe[1] = 18446744056529682432
|
||||
fe[2] = 18446744073709551611
|
||||
fe[3] = 18446744073709551615
|
||||
fe[4] = 18446744073709551615
|
||||
fe[5] = 18446744073709551615
|
||||
}
|
||||
|
||||
fe_b_p384r1 :: proc "contextless" (fe: ^Field_Element_p384r1) {
|
||||
// b = 0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef
|
||||
fe[0] = 581395848458481100
|
||||
fe[1] = 17809957346689692396
|
||||
fe[2] = 8643006485390950958
|
||||
fe[3] = 16372638458395724514
|
||||
fe[4] = 13126622871277412500
|
||||
fe[5] = 14774077593024970745
|
||||
}
|
||||
|
||||
fe_gen_x_p384r1 :: proc "contextless" (fe: ^Field_Element_p384r1) {
|
||||
// G_x = 0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7
|
||||
fe[0] = 4454189113653900584
|
||||
fe[1] = 2369870743683386936
|
||||
fe[2] = 9771750146904378734
|
||||
fe[3] = 7229551204834152191
|
||||
fe[4] = 9308930686126579243
|
||||
fe[5] = 5564951339003155731
|
||||
}
|
||||
|
||||
fe_gen_y_p384r1 :: proc "contextless" (fe: ^Field_Element_p384r1) {
|
||||
// G_y = 0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f
|
||||
fe[0] = 2523209505731486974
|
||||
fe[1] = 11655219901025790380
|
||||
fe[2] = 10064955099576512592
|
||||
fe[3] = 14322381509056856025
|
||||
fe[4] = 15960759442596276288
|
||||
fe[5] = 3132442392059561449
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
fe_is_zero_p256r1 :: proc "contextless" (fe: ^Field_Element_p256r1) -> int {
|
||||
return int(subtle.u64_is_zero(p256r1.fe_non_zero(fe)))
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
fe_is_zero_p384r1 :: proc "contextless" (fe: ^Field_Element_p384r1) -> int {
|
||||
return int(subtle.u64_is_zero(p384r1.fe_non_zero(fe)))
|
||||
}
|
||||
580
core/crypto/_weierstrass/point.odin
Normal file
580
core/crypto/_weierstrass/point.odin
Normal file
@@ -0,0 +1,580 @@
|
||||
package _weierstrass
|
||||
|
||||
/*
|
||||
This implements prime order short Weierstrass curves defined over a field
|
||||
k with char(k) != 2, 3 (`y^2 = x^3 + ax + b`). for the purpose of
|
||||
implementing ECDH and ECDSA. Use of this package for other purposes is
|
||||
NOT RECOMMENDED.
|
||||
|
||||
As an explicit simplicity/performance tradeoff, projective representation
|
||||
was chosen so that it is possible to use the complete addition
|
||||
formulas.
|
||||
|
||||
See:
|
||||
- https://eprint.iacr.org/2015/1060.pdf
|
||||
- https://hyperelliptic.org/EFD/g1p/auto-shortw-projective.html
|
||||
|
||||
WARNING: The point addition and doubling formulas are specialized for
|
||||
`a = -3`, which covers secp256r1, secp384r1, secp521r1, FRP256v1, SM2,
|
||||
and GOST 34.10. The brainpool curves and secp256k1 are NOT SUPPORTED
|
||||
and would require slightly different formulas.
|
||||
*/
|
||||
|
||||
Point_p256r1 :: struct {
|
||||
x: Field_Element_p256r1,
|
||||
y: Field_Element_p256r1,
|
||||
z: Field_Element_p256r1,
|
||||
}
|
||||
|
||||
Point_p384r1 :: struct {
|
||||
x: Field_Element_p384r1,
|
||||
y: Field_Element_p384r1,
|
||||
z: Field_Element_p384r1,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
pt_set_xy_bytes :: proc "contextless" (p: ^$T, x_raw, y_raw: []byte) -> bool {
|
||||
when T == Point_p256r1 {
|
||||
FE_SZ :: FE_SIZE_P256R1
|
||||
x, y: Field_Element_p256r1
|
||||
defer fe_clear_vec([]^Field_Element_p256r1{&x, &y})
|
||||
} else when T == Point_p384r1 {
|
||||
FE_SZ :: FE_SIZE_P384R1
|
||||
x, y: Field_Element_p384r1
|
||||
defer fe_clear_vec([]^Field_Element_p384r1{&x, &y})
|
||||
} else {
|
||||
#panic("weierstrass: invalid curve")
|
||||
}
|
||||
|
||||
if len(x_raw) != FE_SZ || len(y_raw) != FE_SZ {
|
||||
return false
|
||||
}
|
||||
|
||||
if !fe_set_bytes(&x, x_raw) {
|
||||
return false
|
||||
}
|
||||
if !fe_set_bytes(&y, y_raw) {
|
||||
return false
|
||||
}
|
||||
if !is_on_curve(&x, &y) {
|
||||
return false
|
||||
}
|
||||
|
||||
fe_set(&p.x, &x)
|
||||
fe_set(&p.y, &y)
|
||||
fe_one(&p.z)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
pt_set_x_bytes :: proc "contextless" (p: ^$T, x_raw: []byte, y_is_odd: int) -> bool {
|
||||
when T == Point_p256r1 {
|
||||
FE_SZ :: FE_SIZE_P256R1
|
||||
x, y, yy, y_neg: Field_Element_p256r1
|
||||
defer fe_clear_vec([]^Field_Element_p256r1{&x, &y, &yy, &y_neg})
|
||||
} else when T == Point_p384r1 {
|
||||
FE_SZ :: FE_SIZE_P384R1
|
||||
x, y, yy, y_neg: Field_Element_p384r1
|
||||
defer fe_clear_vec([]^Field_Element_p384r1{&x, &y, &yy, &y_neg})
|
||||
} else {
|
||||
#panic("weierstrass: invalid curve")
|
||||
}
|
||||
|
||||
if len(x_raw) != FE_SZ {
|
||||
return false
|
||||
}
|
||||
|
||||
if !fe_set_bytes(&x, x_raw) {
|
||||
return false
|
||||
}
|
||||
set_yy_candidate(&yy, &x)
|
||||
if fe_sqrt(&y, &yy) != 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Pick the correct y-coordinate.
|
||||
fe_negate(&y_neg, &y)
|
||||
parity_neq := (y_is_odd ~ fe_is_odd(&y)) & 1
|
||||
|
||||
fe_set(&p.x, &x)
|
||||
fe_cond_select(&p.y, &y, &y_neg, parity_neq)
|
||||
fe_one(&p.z)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
pt_bytes :: proc "contextless" (x, y: []byte, p: ^$T) -> bool {
|
||||
when T == Point_p256r1 {
|
||||
FE_SZ :: FE_SIZE_P256R1
|
||||
} else when T == Point_p384r1 {
|
||||
FE_SZ :: FE_SIZE_P384R1
|
||||
} else {
|
||||
#panic("weierstrass: invalid curve")
|
||||
}
|
||||
|
||||
if pt_is_identity(p) == 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Convert to affine coordinates.
|
||||
pt_rescale(p, p)
|
||||
|
||||
switch len(x) {
|
||||
case 0:
|
||||
case FE_SZ:
|
||||
fe_bytes(x, &p.x)
|
||||
case:
|
||||
panic_contextless("weierstrass: invalid x buffer")
|
||||
}
|
||||
switch len(y) {
|
||||
case 0:
|
||||
case FE_SZ:
|
||||
fe_bytes(y, &p.y)
|
||||
case:
|
||||
panic_contextless("weierstrass: invalid y buffer")
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
pt_set :: proc "contextless" (p, q: ^$T) {
|
||||
fe_set(&p.x, &q.x)
|
||||
fe_set(&p.y, &q.y)
|
||||
fe_set(&p.z, &q.z)
|
||||
}
|
||||
|
||||
pt_identity :: proc "contextless" (p: ^$T) {
|
||||
fe_zero(&p.x)
|
||||
fe_one(&p.y)
|
||||
fe_zero(&p.z)
|
||||
}
|
||||
|
||||
pt_generator :: proc "contextless" (p: ^$T) {
|
||||
fe_gen_x(&p.x)
|
||||
fe_gen_y(&p.y)
|
||||
fe_one(&p.z)
|
||||
}
|
||||
|
||||
pt_clear :: proc "contextless" (p: ^$T) {
|
||||
fe_clear(&p.x)
|
||||
fe_clear(&p.y)
|
||||
fe_clear(&p.z)
|
||||
}
|
||||
|
||||
pt_clear_vec :: proc "contextless" (arg: []^$T) {
|
||||
for p in arg {
|
||||
pt_clear(p)
|
||||
}
|
||||
}
|
||||
|
||||
pt_add :: proc "contextless" (p, a, b: ^$T) {
|
||||
// Algorithm 4 from "Complete addition formulas for prime
|
||||
// order elliptic curves" by Renes, Costello, and Batina.
|
||||
//
|
||||
// The formula is complete in that it is valid for all a and b,
|
||||
// without exceptions or extra assumptions about the inputs.
|
||||
//
|
||||
// The operation costs are `12M + 2mb + 29a`.
|
||||
|
||||
when T == Point_p256r1 {
|
||||
t0, t1, t2, t3, t4, b_fe: Field_Element_p256r1
|
||||
x3, y3, z3: Field_Element_p256r1
|
||||
defer fe_clear_vec([]^Field_Element_p256r1{&t0, &t1, &t2, &t3, &t4, &x3, &y3, &z3})
|
||||
} else when T == Point_p384r1 {
|
||||
t0, t1, t2, t3, t4, b_fe: Field_Element_p384r1
|
||||
x3, y3, z3: Field_Element_p384r1
|
||||
defer fe_clear_vec([]^Field_Element_p384r1{&t0, &t1, &t2, &t3, &t4, &x3, &y3, &z3})
|
||||
} else {
|
||||
#panic("weierstrass: invalid curve")
|
||||
}
|
||||
|
||||
x1, y1, z1 := &a.x, &a.y, &a.z
|
||||
x2, y2, z2 := &b.x, &b.y, &b.z
|
||||
|
||||
fe_b(&b_fe)
|
||||
|
||||
// t0 := X1 * X2 ; t1 := Y1 * Y2 ; t2 := Z1 * Z2 ;
|
||||
fe_mul(&t0, x1, x2)
|
||||
fe_mul(&t1, y1, y2)
|
||||
fe_mul(&t2, z1, z2)
|
||||
|
||||
// t3 := X1 + Y1 ; t4 := X2 + Y2 ; t3 := t3 * t4 ;
|
||||
fe_add(&t3, x1, y1)
|
||||
fe_add(&t4, x2, y2)
|
||||
fe_mul(&t3, &t3, &t4)
|
||||
|
||||
// t4 := t0 + t1 ; t3 := t3 - t4 ; t4 := Y1 + Z1 ;
|
||||
fe_add(&t4, &t0, &t1)
|
||||
fe_sub(&t3, &t3, &t4)
|
||||
fe_add(&t4, y1, z1)
|
||||
|
||||
// X3 := Y2 + Z2 ; t4 := t4 * X3 ; X3 := t1 + t2 ;
|
||||
fe_add(&x3, y2, z2)
|
||||
fe_mul(&t4, &t4, &x3)
|
||||
fe_add(&x3, &t1, &t2)
|
||||
|
||||
// t4 := t4 - X3 ; X3 := X1 + Z1 ; Y3 := X2 + Z2 ;
|
||||
fe_sub(&t4, &t4, &x3)
|
||||
fe_add(&x3, x1, z1)
|
||||
fe_add(&y3, x2, z2)
|
||||
|
||||
// X3 := X3 * Y3 ; Y3 := t0 + t2 ; Y3 := X3 - Y3 ;
|
||||
fe_mul(&x3, &x3, &y3)
|
||||
fe_add(&y3, &t0, &t2)
|
||||
fe_sub(&y3, &x3, &y3)
|
||||
|
||||
// Z3 := b * t2 ; X3 := Y3 - Z3 ; Z3 := X3 + X3 ;
|
||||
fe_mul(&z3, &b_fe, &t2)
|
||||
fe_sub(&x3, &y3, &z3)
|
||||
fe_add(&z3, &x3, &x3)
|
||||
|
||||
// X3 := X3 + Z3 ; Z3 := t1 - X3 ; X3 := t1 + X3 ;
|
||||
fe_add(&x3, &x3, &z3)
|
||||
fe_sub(&z3, &t1, &x3)
|
||||
fe_add(&x3, &t1, &x3)
|
||||
|
||||
// Y3 := b * Y3 ; t1 := t2 + t2 ; t2 := t1 + t2 ;
|
||||
fe_mul(&y3, &b_fe, &y3)
|
||||
fe_add(&t1, &t2, &t2)
|
||||
fe_add(&t2, &t1, &t2)
|
||||
|
||||
// Y3 := Y3 - t2 ; Y3 := Y3 - t0 ; t1 := Y3 + Y3 ;
|
||||
fe_sub(&y3, &y3, &t2)
|
||||
fe_sub(&y3, &y3, &t0)
|
||||
fe_add(&t1, &y3, &y3)
|
||||
|
||||
// Y3 := t1 + Y3 ; t1 := t0 + t0 ; t0 := t1 + t0 ;
|
||||
fe_add(&y3, &t1, &y3)
|
||||
fe_add(&t1, &t0, &t0)
|
||||
fe_add(&t0, &t1, &t0)
|
||||
|
||||
// t0 := t0 - t2 ; t1 := t4 * Y3 ; t2 := t0 * Y3 ;
|
||||
fe_sub(&t0, &t0, &t2)
|
||||
fe_mul(&t1, &t4, &y3)
|
||||
fe_mul(&t2, &t0, &y3)
|
||||
|
||||
// Y3 := X3 * Z3 ; Y3 := Y3 + t2 ; X3 := t3 * X3 ;
|
||||
fe_mul(&y3, &x3, &z3)
|
||||
fe_add(&y3, &y3, &t2)
|
||||
fe_mul(&x3, &t3, &x3)
|
||||
|
||||
// X3 := X3 - t1 ; Z3 := t4 * Z3 ; t1 := t3 * t0 ;
|
||||
fe_sub(&x3, &x3, &t1)
|
||||
fe_mul(&z3, &t4, &z3)
|
||||
fe_mul(&t1, &t3, &t0)
|
||||
|
||||
// Z3 := Z3 + t1 ;
|
||||
fe_add(&z3, &z3, &t1)
|
||||
|
||||
// return X3 , Y3 , Z3 ;
|
||||
fe_set(&p.x, &x3)
|
||||
fe_set(&p.y, &y3)
|
||||
fe_set(&p.z, &z3)
|
||||
}
|
||||
|
||||
@(private)
|
||||
pt_add_mixed :: proc "contextless" (p, a: ^$T, x2, y2: ^$U) {
|
||||
// Algorithm 5 from "Complete addition formulas for prime
|
||||
// order elliptic curves" by Renes, Costello, and Batina.
|
||||
//
|
||||
// The formula is mixed in that it assumes the z-coordinate
|
||||
// of the addend (`Z2`) is `1`, meaning that it CAN NOT
|
||||
// handle the addend being the point at infinity.
|
||||
//
|
||||
// The operation costs are `11M + 2mb + 23a` saving
|
||||
// `1M + 6a` over `pt_add`.
|
||||
|
||||
when T == Point_p256r1 && U == Field_Element_p256r1 {
|
||||
t0, t1, t2, t3, t4, b_fe: Field_Element_p256r1
|
||||
x3, y3, z3: Field_Element_p256r1
|
||||
defer fe_clear_vec([]^Field_Element_p256r1{&t0, &t1, &t2, &t3, &t4, &x3, &y3, &z3})
|
||||
} else when T == Point_p384r1 {
|
||||
t0, t1, t2, t3, t4, b_fe: Field_Element_p384r1
|
||||
x3, y3, z3: Field_Element_p384r1
|
||||
defer fe_clear_vec([]^Field_Element_p384r1{&t0, &t1, &t2, &t3, &t4, &x3, &y3, &z3})
|
||||
} else {
|
||||
#panic("weierstrass: invalid curve")
|
||||
}
|
||||
|
||||
x1, y1, z1 := &a.x, &a.y, &a.z
|
||||
|
||||
fe_b(&b_fe)
|
||||
|
||||
// t0 := X1 * X2 ; t1 := Y1 * Y2 ; t3 := X2 + Y2 ;
|
||||
fe_mul(&t0, x1, x2)
|
||||
fe_mul(&t1, y1, y2)
|
||||
fe_add(&t3, x2, y2)
|
||||
|
||||
// t4 := X1 + Y1 ; t3 := t3 * t4 ; t4 := t0 + t1 ;
|
||||
fe_add(&t4, x1, y1)
|
||||
fe_mul(&t3, &t3, &t4)
|
||||
fe_add(&t4, &t0, &t1)
|
||||
|
||||
// t3 := t3 − t4 ; t4 := Y2 * Z1 ; t4 := t4 + Y1 ;
|
||||
fe_sub(&t3, &t3, &t4)
|
||||
fe_mul(&t4, y2, z1)
|
||||
fe_add(&t4, &t4, y1)
|
||||
|
||||
// Y3 := X2 * Z1 ; Y3 := Y3 + X1 ; Z3 := b * Z1 ;
|
||||
fe_mul(&y3, x2, z1)
|
||||
fe_add(&y3, &y3, x1)
|
||||
fe_mul(&z3, &b_fe, z1)
|
||||
|
||||
// X3 := Y3 − Z3 ; Z3 := X3 + X3 ; X3 := X3 + Z3 ;
|
||||
fe_sub(&x3, &y3, &z3)
|
||||
fe_add(&z3, &x3, &x3)
|
||||
fe_add(&x3, &x3, &z3)
|
||||
|
||||
// Z3 := t1 − X3 ; X3 := t1 + X3 ;. Y3 := b * Y3 ;
|
||||
fe_sub(&z3, &t1, &x3)
|
||||
fe_add(&x3, &t1, &x3)
|
||||
fe_mul(&y3, &b_fe, &y3)
|
||||
|
||||
// t1 := Z1 + Z1 ; t2 := t1 + Z1 ; Y3 := Y3 − t2 ;
|
||||
fe_add(&t1, z1, z1)
|
||||
fe_add(&t2, &t1, z1)
|
||||
fe_sub(&y3, &y3, &t2)
|
||||
|
||||
// Y3 := Y3 − t0 ; t1 := Y3 + Y3 ; Y3 := t1 + Y3 ;
|
||||
fe_sub(&y3, &y3, &t0)
|
||||
fe_add(&t1, &y3, &y3)
|
||||
fe_add(&y3, &t1, &y3)
|
||||
|
||||
// t1 := t0 + t0 ; t0 := t1 + t0 ; t0 := t0 − t2 ;
|
||||
fe_add(&t1, &t0, &t0)
|
||||
fe_add(&t0, &t1, &t0)
|
||||
fe_sub(&t0, &t0, &t2)
|
||||
|
||||
// t1 := t4 * Y3 ; t2 := t0 * Y3 ; Y3 := X3 * Z3 ;
|
||||
fe_mul(&t1, &t4, &y3)
|
||||
fe_mul(&t2, &t0, &y3)
|
||||
fe_mul(&y3, &x3, &z3)
|
||||
|
||||
// Y3 := Y3 + t2 ; X3 := t3 * X3 ; X3 := X3 − t1 ;
|
||||
fe_add(&y3, &y3, &t2)
|
||||
fe_mul(&x3, &t3, &x3)
|
||||
fe_sub(&x3, &x3, &t1)
|
||||
|
||||
// Z3 := t4 * Z3 ; t1 := t3 * t0 ; Z3 := Z3 + t1 ;
|
||||
fe_mul(&z3, &t4, &z3)
|
||||
fe_mul(&t1, &t3, &t0)
|
||||
fe_add(&z3, &z3, &t1)
|
||||
|
||||
// return X3 , Y3 , Z3 ;
|
||||
fe_set(&p.x, &x3)
|
||||
fe_set(&p.y, &y3)
|
||||
fe_set(&p.z, &z3)
|
||||
}
|
||||
|
||||
pt_double :: proc "contextless" (p, a: ^$T) {
|
||||
// Algorithm 6 from "Complete addition formulas for prime
|
||||
// order elliptic curves" by Renes, Costello, and Batina.
|
||||
//
|
||||
// The formula is complete in that it is valid for all a,
|
||||
// without exceptions or extra assumptions about the inputs.
|
||||
//
|
||||
// The operation costs are `8M + 3S + 2mb + 21a`.
|
||||
|
||||
when T == Point_p256r1 {
|
||||
t0, t1, t2, t3, b_fe: Field_Element_p256r1
|
||||
x3, y3, z3: Field_Element_p256r1
|
||||
defer fe_clear_vec([]^Field_Element_p256r1{&t0, &t1, &t2, &t3, &x3, &y3, &z3})
|
||||
} else when T == Point_p384r1 {
|
||||
t0, t1, t2, t3, b_fe: Field_Element_p384r1
|
||||
x3, y3, z3: Field_Element_p384r1
|
||||
defer fe_clear_vec([]^Field_Element_p384r1{&t0, &t1, &t2, &t3, &x3, &y3, &z3})
|
||||
} else {
|
||||
#panic("weierstrass: invalid curve")
|
||||
}
|
||||
|
||||
x, y, z := &a.x, &a.y, &a.z
|
||||
|
||||
fe_b(&b_fe)
|
||||
|
||||
// t0 := X ^2; t1 := Y ^2; t2 := Z ^2;
|
||||
fe_square(&t0, x)
|
||||
fe_square(&t1, y)
|
||||
fe_square(&t2, z)
|
||||
|
||||
// t3 := X * Y ; t3 := t3 + t3 ; Z3 := X * Z ;
|
||||
fe_mul(&t3, x, y)
|
||||
fe_add(&t3, &t3, &t3)
|
||||
fe_mul(&z3, x, z)
|
||||
|
||||
// Z3 := Z3 + Z3 ; Y3 := b * t2 ; Y3 := Y3 - Z3 ;
|
||||
fe_add(&z3, &z3, &z3)
|
||||
fe_mul(&y3, &b_fe, &t2)
|
||||
fe_sub(&y3, &y3, &z3)
|
||||
|
||||
// X3 := Y3 + Y3 ; Y3 := X3 + Y3 ; X3 := t1 - Y3 ;
|
||||
fe_add(&x3, &y3, &y3)
|
||||
fe_add(&y3, &x3, &y3)
|
||||
fe_sub(&x3, &t1, &y3)
|
||||
|
||||
// Y3 := t1 + Y3 ; Y3 := X3 * Y3 ; X3 := X3 * t3 ;
|
||||
fe_add(&y3, &t1, &y3)
|
||||
fe_mul(&y3, &x3, &y3)
|
||||
fe_mul(&x3, &x3, &t3)
|
||||
|
||||
// t3 := t2 + t2 ; t2 := t2 + t3 ; Z3 := b * Z3 ;
|
||||
fe_add(&t3, &t2, &t2)
|
||||
fe_add(&t2, &t2, &t3)
|
||||
fe_mul(&z3, &b_fe, &z3)
|
||||
|
||||
// Z3 := Z3 - t2 ; Z3 := Z3 - t0 ; t3 := Z3 + Z3 ;
|
||||
fe_sub(&z3, &z3, &t2)
|
||||
fe_sub(&z3, &z3, &t0)
|
||||
fe_add(&t3, &z3, &z3)
|
||||
|
||||
// Z3 := Z3 + t3 ; t3 := t0 + t0 ; t0 := t3 + t0 ;
|
||||
fe_add(&z3, &z3, &t3)
|
||||
fe_add(&t3, &t0, &t0)
|
||||
fe_add(&t0, &t3, &t0)
|
||||
|
||||
// t0 := t0 - t2 ; t0 := t0 * Z3 ; Y3 := Y3 + t0 ;
|
||||
fe_sub(&t0, &t0, &t2)
|
||||
fe_mul(&t0, &t0, &z3)
|
||||
fe_add(&y3, &y3, &t0)
|
||||
|
||||
// t0 := Y * Z ; t0 := t0 + t0 ; Z3 := t0 * Z3 ;
|
||||
fe_mul(&t0, y, z)
|
||||
fe_add(&t0, &t0, &t0)
|
||||
fe_mul(&z3, &t0, &z3)
|
||||
|
||||
// X3 := X3 - Z3 ; Z3 := t0 * t1 ; Z3 := Z3 + Z3 ;
|
||||
fe_sub(&x3, &x3, &z3)
|
||||
fe_mul(&z3, &t0, &t1)
|
||||
fe_add(&z3, &z3, &z3)
|
||||
|
||||
// Z3 := Z3 + Z3 ;
|
||||
fe_add(&z3, &z3, &z3)
|
||||
|
||||
// return X3 , Y3 , Z3 ;
|
||||
fe_set(&p.x, &x3)
|
||||
fe_set(&p.y, &y3)
|
||||
fe_set(&p.z, &z3)
|
||||
}
|
||||
|
||||
pt_sub :: proc "contextless" (p, a, b: ^$T) {
|
||||
b_neg: T
|
||||
pt_negate(&b_neg, b)
|
||||
pt_add(p, a, &b_neg)
|
||||
|
||||
fe_clear(&b_neg)
|
||||
}
|
||||
|
||||
pt_negate :: proc "contextless" (p, a: ^$T) {
|
||||
fe_set(&p.x, &a.x)
|
||||
fe_negate(&p.y, &a.y)
|
||||
fe_set(&p.z, &a.z)
|
||||
}
|
||||
|
||||
pt_rescale :: proc "contextless" (p, a: ^$T) {
|
||||
// A = 1/Z1
|
||||
// X3 = A*X1
|
||||
// Y3 = A*Y1
|
||||
// Z3 = 1
|
||||
//
|
||||
// As per "From A to Z: Projective coordinates leakage in the wild"
|
||||
// leaking the Z-coordinate is bad. The modular inversion algorithm
|
||||
// used in this library is based on Fermat's Little Theorem.
|
||||
//
|
||||
// See: https://eprint.iacr.org/2020/432.pdf
|
||||
|
||||
was_identity := pt_is_identity(a)
|
||||
|
||||
when T == Point_p256r1 {
|
||||
z_inv: Field_Element_p256r1
|
||||
} else when T == Point_p384r1 {
|
||||
z_inv: Field_Element_p384r1
|
||||
} else {
|
||||
#panic("weierstrass: invalid curve")
|
||||
}
|
||||
|
||||
ident: T
|
||||
fe_inv(&z_inv, &a.z)
|
||||
fe_mul(&p.x, &a.x, &z_inv)
|
||||
fe_mul(&p.y, &a.y, &z_inv)
|
||||
fe_one(&p.z)
|
||||
|
||||
pt_identity(&ident)
|
||||
pt_cond_select(p, p, &ident, was_identity)
|
||||
|
||||
fe_clear(&z_inv)
|
||||
}
|
||||
|
||||
pt_cond_select :: proc "contextless" (p, a, b: ^$T, ctrl: int) {
|
||||
fe_cond_select(&p.x, &a.x, &b.x, ctrl)
|
||||
fe_cond_select(&p.y, &a.y, &b.y, ctrl)
|
||||
fe_cond_select(&p.z, &a.z, &b.z, ctrl)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
pt_equal :: proc "contextless" (a, b: ^$T) -> int {
|
||||
when T == Point_p256r1 {
|
||||
x1z2, x2z1, y1z2, y2z1: Field_Element_p256r1
|
||||
} else when T == Point_p384r1 {
|
||||
x1z2, x2z1, y1z2, y2z1: Field_Element_p384r1
|
||||
} else {
|
||||
#panic("weierstrass: invalid curve")
|
||||
}
|
||||
|
||||
// Check X1Z2 == X2Z1 && Y1Z2 == Y2Z1
|
||||
fe_mul(&x1z2, &a.x, &b.z)
|
||||
fe_mul(&x2z1, &b.x, &a.z)
|
||||
|
||||
fe_mul(&y1z2, &a.y, &b.z)
|
||||
fe_mul(&y2z1, &b.y, &a.z)
|
||||
|
||||
return fe_equal(&x1z2, &x2z1) & fe_equal(&y1z2, &y2z1)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
pt_is_identity :: proc "contextless" (p: ^$T) -> int {
|
||||
return fe_is_zero(&p.z)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
pt_is_y_odd :: proc "contextless" (p: ^$T) -> int {
|
||||
tmp: T
|
||||
defer pt_clear(&tmp)
|
||||
|
||||
fe_set(&tmp, p)
|
||||
pt_rescale(&tmp)
|
||||
|
||||
return fe_is_odd(&tmp.y)
|
||||
}
|
||||
|
||||
@(private)
|
||||
is_on_curve :: proc "contextless" (x, y: ^$T) -> bool {
|
||||
maybe_yy, yy: T
|
||||
defer fe_clear_vec([]^T{&maybe_yy, &yy})
|
||||
|
||||
// RHS: x^3 + ax + b
|
||||
set_yy_candidate(&maybe_yy, x)
|
||||
|
||||
// LHS: y^2
|
||||
fe_square(&yy, y)
|
||||
|
||||
return fe_equal(&maybe_yy, &yy) == 1
|
||||
}
|
||||
|
||||
@(private)
|
||||
set_yy_candidate :: proc "contextless" (maybe_yy, x: ^$T) {
|
||||
// RHS: x^3 + ax + b
|
||||
rhs, tmp: T
|
||||
|
||||
fe_square(&tmp, x)
|
||||
fe_mul(&rhs, &tmp, x)
|
||||
|
||||
fe_a(&tmp)
|
||||
fe_mul(&tmp, &tmp, x)
|
||||
fe_add(&rhs, &rhs, &tmp)
|
||||
|
||||
fe_b(&tmp)
|
||||
fe_add(maybe_yy, &rhs, &tmp)
|
||||
|
||||
fe_clear(&rhs)
|
||||
}
|
||||
99
core/crypto/_weierstrass/point_s11n_sec.odin
Normal file
99
core/crypto/_weierstrass/point_s11n_sec.odin
Normal file
@@ -0,0 +1,99 @@
|
||||
package _weierstrass
|
||||
|
||||
@(require) import "core:crypto"
|
||||
|
||||
@(private)
|
||||
SEC_PREFIX_IDENTITY :: 0x00
|
||||
@(private)
|
||||
SEC_PREFIX_COMPRESSED_EVEN :: 0x02
|
||||
@(private)
|
||||
SEC_PREFIX_COMPRESSED_ODD :: 0x03
|
||||
SEC_PREFIX_UNCOMPRESSED :: 0x04
|
||||
|
||||
@(require_results)
|
||||
pt_set_sec_bytes :: proc "contextless" (p: ^$T, b: []byte) -> bool {
|
||||
when T == Point_p256r1 {
|
||||
FE_SZ :: FE_SIZE_P256R1
|
||||
} else when T == Point_p384r1 {
|
||||
FE_SZ :: FE_SIZE_P384R1
|
||||
} else {
|
||||
#panic("weierstrass: invalid curve")
|
||||
}
|
||||
|
||||
b_len := len(b)
|
||||
if b_len < 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
switch b[0] {
|
||||
case SEC_PREFIX_IDENTITY:
|
||||
if b_len != 1 {
|
||||
return false
|
||||
}
|
||||
pt_identity(p)
|
||||
return true
|
||||
case SEC_PREFIX_COMPRESSED_EVEN, SEC_PREFIX_COMPRESSED_ODD:
|
||||
if b_len != 1 + FE_SZ {
|
||||
return false
|
||||
}
|
||||
y_is_odd := b[0] - SEC_PREFIX_COMPRESSED_EVEN
|
||||
return pt_set_x_bytes(p, b[1:], int(y_is_odd))
|
||||
case SEC_PREFIX_UNCOMPRESSED:
|
||||
if b_len != 1 + 2 * FE_SZ {
|
||||
return false
|
||||
}
|
||||
x, y := b[1:1+FE_SZ], b[1+FE_SZ:]
|
||||
return pt_set_xy_bytes(p, x, y)
|
||||
case:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
pt_sec_bytes :: proc "contextless" (b: []byte, p: ^$T, compressed: bool) -> bool {
|
||||
when T == Point_p256r1 {
|
||||
FE_SZ :: FE_SIZE_P256R1
|
||||
} else when T == Point_p384r1 {
|
||||
FE_SZ :: FE_SIZE_P384R1
|
||||
} else {
|
||||
#panic("weierstrass: invalid curve")
|
||||
}
|
||||
|
||||
b_len := len(b)
|
||||
if pt_is_identity(p) == 1 {
|
||||
if b_len != 1 {
|
||||
return false
|
||||
}
|
||||
b[0] = SEC_PREFIX_IDENTITY
|
||||
return true
|
||||
}
|
||||
|
||||
x, y: []byte
|
||||
y_: [FE_SZ]byte
|
||||
switch compressed {
|
||||
case true:
|
||||
if b_len != 1 + FE_SZ {
|
||||
return false
|
||||
}
|
||||
x, y = b[1:], y_[:]
|
||||
case false:
|
||||
if b_len != 1 + 2 * FE_SZ {
|
||||
return false
|
||||
}
|
||||
b[0]= SEC_PREFIX_UNCOMPRESSED
|
||||
x, y = b[1:1+FE_SZ], b[1+FE_SZ:]
|
||||
}
|
||||
if !pt_bytes(x, y, p) {
|
||||
return false
|
||||
}
|
||||
if compressed {
|
||||
// Instead of calling pt_is_y_odd, just serializing
|
||||
// y into a temp buffer and checking the parity saves
|
||||
// 1 redundant rescale call.
|
||||
y_is_odd := byte(y[FE_SZ-1] & 1)
|
||||
b[0] = SEC_PREFIX_COMPRESSED_EVEN + y_is_odd
|
||||
crypto.zero_explicit(&y_, size_of(y_))
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
142
core/crypto/_weierstrass/sc.odin
Normal file
142
core/crypto/_weierstrass/sc.odin
Normal file
@@ -0,0 +1,142 @@
|
||||
package _weierstrass
|
||||
|
||||
@(require) import "core:crypto"
|
||||
import p256r1 "core:crypto/_fiat/field_scalarp256r1"
|
||||
import p384r1 "core:crypto/_fiat/field_scalarp384r1"
|
||||
import subtle "core:crypto/_subtle"
|
||||
|
||||
Scalar_p256r1 :: p256r1.Montgomery_Domain_Field_Element
|
||||
Scalar_p384r1 :: p384r1.Montgomery_Domain_Field_Element
|
||||
|
||||
SC_SIZE_P256R1 :: 32
|
||||
SC_SIZE_P384R1 :: 48
|
||||
SC_SIZE_MAX :: SC_SIZE_P384R1
|
||||
|
||||
sc_size :: proc "contextless" (sc: ^$T) -> int where T == Scalar_p256r1 || T == Scalar_p384r1 {
|
||||
when T == Scalar_p256r1 {
|
||||
return SC_SIZE_P256R1
|
||||
} else when T == Scalar_p384r1 {
|
||||
return SC_SIZE_P384R1
|
||||
}
|
||||
}
|
||||
|
||||
sc_set_random :: proc(sc: ^$T) where T == Scalar_p256r1 || T == Scalar_p384r1 {
|
||||
ensure(crypto.HAS_RAND_BYTES, "weierstrass: entropy source required")
|
||||
|
||||
b: [48]byte = ---
|
||||
defer crypto.zero_explicit(&b, size_of(b))
|
||||
|
||||
when T == Scalar_p256r1 {
|
||||
// 384-bits reduced makes the modulo bias insignificant
|
||||
for {
|
||||
crypto.rand_bytes(b[:])
|
||||
_ = sc_set_bytes(sc, b[:])
|
||||
if sc_is_zero(sc) == 0 { // Likely
|
||||
break
|
||||
}
|
||||
}
|
||||
} else when T == Scalar_p384r1 {
|
||||
for {
|
||||
crypto.rand_bytes(b[:])
|
||||
did_reduce := sc_set_bytes(sc, b[:])
|
||||
if !did_reduce && sc_is_zero(sc) == 0 { // Likely
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sc_clear :: proc {
|
||||
p256r1.fe_clear,
|
||||
p384r1.fe_clear,
|
||||
}
|
||||
|
||||
sc_clear_vec :: proc {
|
||||
p256r1.fe_clear_vec,
|
||||
p384r1.fe_clear_vec,
|
||||
}
|
||||
|
||||
sc_set_bytes :: proc {
|
||||
p256r1.fe_from_bytes,
|
||||
p384r1.fe_from_bytes,
|
||||
}
|
||||
|
||||
sc_bytes :: proc {
|
||||
p256r1.fe_to_bytes,
|
||||
p384r1.fe_to_bytes,
|
||||
}
|
||||
|
||||
sc_set :: proc {
|
||||
p256r1.fe_set,
|
||||
p384r1.fe_set,
|
||||
}
|
||||
|
||||
sc_zero :: proc {
|
||||
p256r1.fe_zero,
|
||||
p384r1.fe_zero,
|
||||
}
|
||||
|
||||
sc_one :: proc {
|
||||
p256r1.fe_one,
|
||||
p384r1.fe_one,
|
||||
}
|
||||
|
||||
sc_add :: proc {
|
||||
p256r1.fe_add,
|
||||
p384r1.fe_add,
|
||||
}
|
||||
|
||||
sc_sub :: proc {
|
||||
p256r1.fe_sub,
|
||||
p384r1.fe_sub,
|
||||
}
|
||||
|
||||
sc_negate :: proc {
|
||||
p256r1.fe_opp,
|
||||
p384r1.fe_opp,
|
||||
}
|
||||
|
||||
sc_mul :: proc {
|
||||
p256r1.fe_mul,
|
||||
p384r1.fe_mul,
|
||||
}
|
||||
|
||||
sc_square :: proc {
|
||||
p256r1.fe_square,
|
||||
p384r1.fe_square,
|
||||
}
|
||||
|
||||
sc_inv :: proc {
|
||||
p256r1.fe_inv,
|
||||
p384r1.fe_inv,
|
||||
}
|
||||
|
||||
sc_cond_assign :: proc {
|
||||
p256r1.fe_cond_assign,
|
||||
p384r1.fe_cond_assign,
|
||||
}
|
||||
|
||||
sc_equal :: proc {
|
||||
p256r1.fe_equal,
|
||||
p384r1.fe_equal,
|
||||
}
|
||||
|
||||
sc_is_odd :: proc {
|
||||
p256r1.fe_is_odd,
|
||||
p384r1.fe_is_odd,
|
||||
}
|
||||
|
||||
sc_is_zero :: proc {
|
||||
sc_is_zero_p256r1,
|
||||
sc_is_zero_p384r1,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
sc_is_zero_p256r1 :: proc "contextless" (fe: ^Scalar_p256r1) -> int {
|
||||
return int(subtle.u64_is_zero(p256r1.fe_non_zero(fe)))
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
sc_is_zero_p384r1 :: proc "contextless" (fe: ^Scalar_p384r1) -> int {
|
||||
return int(subtle.u64_is_zero(p384r1.fe_non_zero(fe)))
|
||||
}
|
||||
299
core/crypto/_weierstrass/scalar_mul.odin
Normal file
299
core/crypto/_weierstrass/scalar_mul.odin
Normal file
@@ -0,0 +1,299 @@
|
||||
package _weierstrass
|
||||
|
||||
import "core:crypto"
|
||||
@(require) import subtle "core:crypto/_subtle"
|
||||
|
||||
pt_scalar_mul :: proc "contextless" (
|
||||
p, a: ^$T,
|
||||
sc: ^$S,
|
||||
unsafe_is_vartime: bool = false,
|
||||
) {
|
||||
when T == Point_p256r1 && S == Scalar_p256r1 {
|
||||
SC_SZ :: SC_SIZE_P256R1
|
||||
} else when T == Point_p384r1 && S == Scalar_p384r1 {
|
||||
SC_SZ :: SC_SIZE_P384R1
|
||||
} else {
|
||||
#panic("weierstrass: invalid curve")
|
||||
}
|
||||
|
||||
b: [SC_SZ]byte = ---
|
||||
sc_bytes(b[:], sc)
|
||||
|
||||
pt_scalar_mul_bytes(p, a, b[:], unsafe_is_vartime)
|
||||
|
||||
if !unsafe_is_vartime {
|
||||
crypto.zero_explicit(&b, size_of(b))
|
||||
}
|
||||
}
|
||||
|
||||
pt_scalar_mul_bytes :: proc "contextless" (
|
||||
p, a: ^$T,
|
||||
sc: []byte,
|
||||
unsafe_is_vartime: bool = false,
|
||||
) {
|
||||
when T == Point_p256r1 {
|
||||
p_tbl: Multiply_Table_p256r1 = ---
|
||||
q, tmp: Point_p256r1 = ---, ---
|
||||
SC_SZ :: SC_SIZE_P256R1
|
||||
} else when T == Point_p384r1 {
|
||||
p_tbl: Multiply_Table_p384r1 = ---
|
||||
q, tmp: Point_p384r1 = ---, ---
|
||||
SC_SZ :: SC_SIZE_P384R1
|
||||
} else {
|
||||
#panic("weierstrass: invalid curve")
|
||||
}
|
||||
|
||||
assert_contextless(len(sc) == SC_SZ, "weierstrass: invalid scalar size")
|
||||
mul_tbl_set(&p_tbl, a, unsafe_is_vartime)
|
||||
|
||||
pt_identity(&q)
|
||||
for limb_byte, i in sc {
|
||||
hi, lo := (limb_byte >> 4) & 0x0f, limb_byte & 0x0f
|
||||
|
||||
if i != 0 {
|
||||
pt_double(&q, &q)
|
||||
pt_double(&q, &q)
|
||||
pt_double(&q, &q)
|
||||
pt_double(&q, &q)
|
||||
}
|
||||
mul_tbl_lookup_add(&q, &tmp, &p_tbl, u64(hi), unsafe_is_vartime)
|
||||
|
||||
pt_double(&q, &q)
|
||||
pt_double(&q, &q)
|
||||
pt_double(&q, &q)
|
||||
pt_double(&q, &q)
|
||||
mul_tbl_lookup_add(&q, &tmp, &p_tbl, u64(lo), unsafe_is_vartime)
|
||||
}
|
||||
|
||||
pt_set(p, &q)
|
||||
|
||||
if !unsafe_is_vartime {
|
||||
crypto.zero_explicit(&p_tbl, size_of(p_tbl))
|
||||
pt_clear_vec([]^T{&q, &tmp})
|
||||
}
|
||||
}
|
||||
|
||||
pt_double_scalar_mul_generator_vartime :: proc "contextless" (
|
||||
p, q: ^$T,
|
||||
sc_g, sc_q: ^$Q,
|
||||
) {
|
||||
// Strauss-Shamir, commonly referred to as the "Shamir trick",
|
||||
// saves half the doublings, relative to doing this the naive way.
|
||||
//
|
||||
// Note: In the unlikely event where we support curves with an
|
||||
// efficent endomorphism (secp256k1), scalarmul + GLV is faster.
|
||||
when T == Point_p256r1 && Q == Scalar_p256r1 {
|
||||
q_tbl: Multiply_Table_p256r1 = ---
|
||||
SC_SZ :: SC_SIZE_P256R1
|
||||
} else when T == Point_p384r1 && Q == Scalar_p384r1 {
|
||||
q_tbl: Multiply_Table_p384r1 = ---
|
||||
SC_SZ :: SC_SIZE_P384R1
|
||||
} else {
|
||||
#panic("weierstrass: invalid curve")
|
||||
}
|
||||
|
||||
sc_q_bytes, sc_g_bytes: [SC_SZ]byte = ---, ---
|
||||
sc_bytes(sc_q_bytes[:], sc_q)
|
||||
sc_bytes(sc_g_bytes[:], sc_g)
|
||||
|
||||
r, tmp: T = ---, ---
|
||||
when crypto.COMPACT_IMPLS == true {
|
||||
pt_generator(&r)
|
||||
when T == Point_p256r1 {
|
||||
g_tbl: Multiply_Table_p256r1 = ---
|
||||
} else when T == Point_p384r1 {
|
||||
g_tbl: Multiply_Table_p384r1 = ---
|
||||
}
|
||||
mul_tbl_set(&g_tbl, &r, true)
|
||||
} else {
|
||||
when T == Point_p256r1 {
|
||||
g_tbl := &Gen_Multiply_Table_p256r1_lo[31]
|
||||
} else when T == Point_p384r1 {
|
||||
g_tbl := &Gen_Multiply_Table_p384r1_lo[47]
|
||||
}
|
||||
}
|
||||
mul_tbl_set(&q_tbl, q, true)
|
||||
|
||||
pt_identity(&r)
|
||||
for i in 0..<SC_SZ {
|
||||
limb_byte_q, limb_byte_g := sc_q_bytes[i], sc_g_bytes[i]
|
||||
hi_q, lo_q := (limb_byte_q >> 4) & 0x0f, limb_byte_q & 0x0f
|
||||
hi_g, lo_g := (limb_byte_g >> 4) & 0x0f, limb_byte_g & 0x0f
|
||||
|
||||
if i != 0 {
|
||||
pt_double(&r, &r)
|
||||
pt_double(&r, &r)
|
||||
pt_double(&r, &r)
|
||||
pt_double(&r, &r)
|
||||
}
|
||||
mul_tbl_lookup_add(&r, &tmp, &q_tbl, u64(hi_q), true)
|
||||
when crypto.COMPACT_IMPLS == true {
|
||||
mul_tbl_lookup_add(&r, &tmp, &g_tbl, u64(hi_g), true)
|
||||
} else {
|
||||
mul_affine_tbl_lookup_add(&r, &tmp, g_tbl, u64(hi_g), true)
|
||||
}
|
||||
|
||||
pt_double(&r, &r)
|
||||
pt_double(&r, &r)
|
||||
pt_double(&r, &r)
|
||||
pt_double(&r, &r)
|
||||
mul_tbl_lookup_add(&r, &tmp, &q_tbl, u64(lo_q), true)
|
||||
when crypto.COMPACT_IMPLS == true {
|
||||
mul_tbl_lookup_add(&r, &tmp, &g_tbl, u64(lo_g), true)
|
||||
} else {
|
||||
mul_affine_tbl_lookup_add(&r, &tmp, g_tbl, u64(lo_g), true)
|
||||
}
|
||||
}
|
||||
|
||||
pt_set(p, &r)
|
||||
}
|
||||
|
||||
when crypto.COMPACT_IMPLS == true {
|
||||
pt_scalar_mul_generator :: proc "contextless" (
|
||||
p: ^$T,
|
||||
sc: ^$S,
|
||||
unsafe_is_vartime: bool = false,
|
||||
) {
|
||||
g: T
|
||||
pt_generator(&g)
|
||||
|
||||
pt_scalar_mul(p, &g, sc, unsafe_is_vartime)
|
||||
}
|
||||
} else {
|
||||
pt_scalar_mul_generator :: proc "contextless" (
|
||||
p: ^$T,
|
||||
sc: ^$S,
|
||||
unsafe_is_vartime: bool = false,
|
||||
) {
|
||||
when T == Point_p256r1 && S == Scalar_p256r1 {
|
||||
p_tbl_hi := &Gen_Multiply_Table_p256r1_hi
|
||||
p_tbl_lo := &Gen_Multiply_Table_p256r1_lo
|
||||
tmp: Point_p256r1 = ---
|
||||
SC_SZ :: SC_SIZE_P256R1
|
||||
} else when T == Point_p384r1 && S == Scalar_p384r1 {
|
||||
p_tbl_hi := &Gen_Multiply_Table_p384r1_hi
|
||||
p_tbl_lo := &Gen_Multiply_Table_p384r1_lo
|
||||
tmp: Point_p384r1 = ---
|
||||
SC_SZ :: SC_SIZE_P384R1
|
||||
} else {
|
||||
#panic("weierstrass: invalid curve")
|
||||
}
|
||||
|
||||
b: [SC_SZ]byte
|
||||
sc_bytes(b[:], sc)
|
||||
|
||||
pt_identity(p)
|
||||
for limb_byte, i in b {
|
||||
hi, lo := (limb_byte >> 4) & 0x0f, limb_byte & 0x0f
|
||||
mul_affine_tbl_lookup_add(p, &tmp, &p_tbl_hi[i], u64(hi), unsafe_is_vartime)
|
||||
mul_affine_tbl_lookup_add(p, &tmp, &p_tbl_lo[i], u64(lo), unsafe_is_vartime)
|
||||
}
|
||||
|
||||
if !unsafe_is_vartime {
|
||||
crypto.zero_explicit(&b, size_of(b))
|
||||
pt_clear(&tmp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
Multiply_Table_p256r1 :: [15]Point_p256r1
|
||||
@(private="file")
|
||||
Multiply_Table_p384r1 :: [15]Point_p384r1
|
||||
|
||||
@(private="file")
|
||||
mul_tbl_set :: proc "contextless"(
|
||||
tbl: ^$T,
|
||||
point: ^$U,
|
||||
unsafe_is_vartime: bool,
|
||||
) {
|
||||
when T == Multiply_Table_p256r1 && U == Point_p256r1{
|
||||
tmp: Point_p256r1
|
||||
} else when T == Multiply_Table_p384r1 && U == Point_p384r1{
|
||||
tmp: Point_p384r1
|
||||
} else {
|
||||
#panic("weierstrass: invalid curve")
|
||||
}
|
||||
|
||||
pt_set(&tmp, point)
|
||||
pt_set(&tbl[0], &tmp)
|
||||
for i in 1 ..<15 {
|
||||
pt_add(&tmp, &tmp, point)
|
||||
pt_set(&tbl[i], &tmp)
|
||||
}
|
||||
|
||||
if !unsafe_is_vartime {
|
||||
pt_clear(&tmp)
|
||||
}
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
mul_tbl_lookup_add :: proc "contextless" (
|
||||
point, tmp: ^$T,
|
||||
tbl: ^$U,
|
||||
idx: u64,
|
||||
unsafe_is_vartime: bool,
|
||||
) {
|
||||
if unsafe_is_vartime {
|
||||
switch idx {
|
||||
case 0:
|
||||
case:
|
||||
pt_add(point, point, &tbl[idx - 1])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
pt_identity(tmp)
|
||||
for i in u64(1)..<16 {
|
||||
ctrl := subtle.eq(i, idx)
|
||||
pt_cond_select(tmp, tmp, &tbl[i - 1], int(ctrl))
|
||||
}
|
||||
|
||||
pt_add(point, point, tmp)
|
||||
}
|
||||
|
||||
when crypto.COMPACT_IMPLS == false {
|
||||
@(private)
|
||||
Affine_Point_p256r1 :: struct {
|
||||
x: Field_Element_p256r1,
|
||||
y: Field_Element_p256r1,
|
||||
}
|
||||
|
||||
@(private)
|
||||
Affine_Point_p384r1 :: struct {
|
||||
x: Field_Element_p384r1,
|
||||
y: Field_Element_p384r1,
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
mul_affine_tbl_lookup_add :: proc "contextless" (
|
||||
point, tmp: ^$T,
|
||||
tbl: ^$U,
|
||||
idx: u64,
|
||||
unsafe_is_vartime: bool,
|
||||
) {
|
||||
if unsafe_is_vartime {
|
||||
switch idx {
|
||||
case 0:
|
||||
case:
|
||||
pt_add_mixed(point, point, &tbl[idx - 1].x, &tbl[idx - 1].y)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
pt_identity(tmp)
|
||||
for i in u64(1)..<16 {
|
||||
ctrl := int(subtle.eq(i, idx))
|
||||
fe_cond_select(&tmp.x, &tmp.x, &tbl[i - 1].x, ctrl)
|
||||
fe_cond_select(&tmp.y, &tmp.y, &tbl[i - 1].y, ctrl)
|
||||
}
|
||||
|
||||
// The mixed addition formula assumes that the addend is not
|
||||
// the neutral element. Do the addition regardless, and then
|
||||
// conditionally select the right result.
|
||||
pt_add_mixed(tmp, point, &tmp.x, &tmp.y)
|
||||
|
||||
ctrl := subtle.u64_is_non_zero(idx)
|
||||
pt_cond_select(point, point, tmp, int(ctrl))
|
||||
}
|
||||
}
|
||||
3985
core/crypto/_weierstrass/secp256r1_table.odin
Normal file
3985
core/crypto/_weierstrass/secp256r1_table.odin
Normal file
File diff suppressed because it is too large
Load Diff
5969
core/crypto/_weierstrass/secp384r1_table.odin
Normal file
5969
core/crypto/_weierstrass/secp384r1_table.odin
Normal file
File diff suppressed because it is too large
Load Diff
134
core/crypto/_weierstrass/tools/ecc_gen_tables.odin
Normal file
134
core/crypto/_weierstrass/tools/ecc_gen_tables.odin
Normal file
@@ -0,0 +1,134 @@
|
||||
package weierstrass_tools
|
||||
|
||||
import secec "core:crypto/_weierstrass"
|
||||
import "core:fmt"
|
||||
import path "core:path/filepath"
|
||||
import "core:os"
|
||||
import "core:strings"
|
||||
|
||||
// Yes this leaks memory, fite me IRL.
|
||||
|
||||
GENERATED :: `/*
|
||||
------ GENERATED ------ DO NOT EDIT ------ GENERATED ------ DO NOT EDIT ------ GENERATED ------
|
||||
*/`
|
||||
|
||||
main :: proc() {
|
||||
gen_tables("p256r1")
|
||||
gen_tables("p384r1")
|
||||
}
|
||||
|
||||
gen_tables :: proc($CURVE: string) {
|
||||
when CURVE == "p256r1" {
|
||||
Affine_Point_p256r1 :: struct {
|
||||
x: secec.Field_Element_p256r1,
|
||||
y: secec.Field_Element_p256r1,
|
||||
}
|
||||
|
||||
Multiply_Table_hi: [32][15]Affine_Point_p256r1
|
||||
Multiply_Table_lo: [32][15]Affine_Point_p256r1
|
||||
|
||||
SC_LEN :: 32
|
||||
|
||||
g, p: secec.Point_p256r1
|
||||
} else when CURVE == "p384r1" {
|
||||
Affine_Point_p384r1 :: struct {
|
||||
x: secec.Field_Element_p384r1,
|
||||
y: secec.Field_Element_p384r1,
|
||||
}
|
||||
Multiply_Table_hi: [48][15]Affine_Point_p384r1
|
||||
Multiply_Table_lo: [48][15]Affine_Point_p384r1
|
||||
|
||||
SC_LEN :: 48
|
||||
|
||||
g, p: secec.Point_p384r1
|
||||
} else {
|
||||
#panic("weistrass/tools: invalid curve")
|
||||
}
|
||||
|
||||
secec.pt_generator(&g)
|
||||
|
||||
// Precompute ([1,15] << n) * G multiples of G, MSB->LSB
|
||||
for i in 0..<SC_LEN {
|
||||
b: [SC_LEN]byte
|
||||
for j in 1..<16 {
|
||||
b[i] = u8(j) << 4
|
||||
secec.pt_scalar_mul_bytes(&p, &g, b[:], true)
|
||||
secec.pt_rescale(&p, &p)
|
||||
secec.fe_set(&Multiply_Table_hi[i][j-1].x, &p.x)
|
||||
secec.fe_set(&Multiply_Table_hi[i][j-1].y, &p.y)
|
||||
|
||||
b[i] = u8(j)
|
||||
secec.pt_scalar_mul_bytes(&p, &g, b[:], true)
|
||||
secec.pt_rescale(&p, &p)
|
||||
secec.fe_set(&Multiply_Table_lo[i][j-1].x, &p.x)
|
||||
secec.fe_set(&Multiply_Table_lo[i][j-1].y, &p.y)
|
||||
|
||||
b[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
fn_ := "sec" + CURVE + "_table.odin"
|
||||
fn, err := path.join({ODIN_ROOT, "core", "crypto", "_weierstrass", fn_}, context.allocator)
|
||||
if err != .None {
|
||||
fmt.eprintfln("Join path error for %s: %v", fn_, err);
|
||||
os.exit(1);
|
||||
}
|
||||
bld: strings.Builder
|
||||
w := strings.to_writer(&bld)
|
||||
|
||||
fmt.wprintln(w, "package _weierstrass")
|
||||
fmt.wprintln(w, "")
|
||||
fmt.wprintln(w, GENERATED)
|
||||
fmt.wprintln(w, "")
|
||||
fmt.wprintln(w, "import \"core:crypto\"")
|
||||
fmt.wprintln(w, "")
|
||||
fmt.wprintln(w, "when crypto.COMPACT_IMPLS == false {")
|
||||
|
||||
fmt.wprintln(w, "\t@(private,rodata)")
|
||||
fmt.wprintf(w, "\tGen_Multiply_Table_%s_hi := [%d][15]Affine_Point_%s {{\n", CURVE, SC_LEN, CURVE)
|
||||
for &v, i in Multiply_Table_hi {
|
||||
fmt.wprintln(w, "\t\t{")
|
||||
for &ap, j in v {
|
||||
fmt.wprintln(w, "\t\t\t{")
|
||||
|
||||
x, y := &ap.x, &ap.y
|
||||
when CURVE == "p256r1" {
|
||||
fmt.wprintf(w, "\t\t\t\t{{%d, %d, %d, %d},\n", x[0], x[1], x[2], x[3])
|
||||
fmt.wprintf(w, "\t\t\t\t{{%d, %d, %d, %d},\n", y[0], y[1], y[2], y[3])
|
||||
} else when CURVE == "p384r1" {
|
||||
fmt.wprintf(w, "\t\t\t\t{{%d, %d, %d, %d, %d, %d},\n", x[0], x[1], x[2], x[3], x[4], x[5])
|
||||
fmt.wprintf(w, "\t\t\t\t{{%d, %d, %d, %d, %d, %d},\n", y[0], y[1], y[2], y[3], y[4], y[5])
|
||||
}
|
||||
|
||||
fmt.wprintln(w, "\t\t\t},")
|
||||
}
|
||||
fmt.wprintln(w, "\t\t},")
|
||||
}
|
||||
fmt.wprintln(w, "\t}\n")
|
||||
|
||||
fmt.wprintln(w, "\t@(private,rodata)")
|
||||
fmt.wprintf(w, "\tGen_Multiply_Table_%s_lo := [%d][15]Affine_Point_%s {{\n", CURVE, SC_LEN, CURVE)
|
||||
for &v, i in Multiply_Table_lo {
|
||||
fmt.wprintln(w, "\t\t{")
|
||||
for &ap, j in v {
|
||||
fmt.wprintln(w, "\t\t\t{")
|
||||
|
||||
x, y := &ap.x, &ap.y
|
||||
when CURVE == "p256r1" {
|
||||
fmt.wprintf(w, "\t\t\t\t{{%d, %d, %d, %d},\n", x[0], x[1], x[2], x[3])
|
||||
fmt.wprintf(w, "\t\t\t\t{{%d, %d, %d, %d},\n", y[0], y[1], y[2], y[3])
|
||||
} else when CURVE == "p384r1" {
|
||||
fmt.wprintf(w, "\t\t\t\t{{%d, %d, %d, %d, %d, %d},\n", x[0], x[1], x[2], x[3], x[4], x[5])
|
||||
fmt.wprintf(w, "\t\t\t\t{{%d, %d, %d, %d, %d, %d},\n", y[0], y[1], y[2], y[3], y[4], y[5])
|
||||
}
|
||||
|
||||
fmt.wprintln(w, "\t\t\t},")
|
||||
}
|
||||
fmt.wprintln(w, "\t\t},")
|
||||
}
|
||||
fmt.wprintln(w, "\t}")
|
||||
|
||||
fmt.wprintln(w, "}")
|
||||
|
||||
_ = os.write_entire_file(fn, transmute([]byte)(strings.to_string(bld)))
|
||||
}
|
||||
@@ -13,7 +13,7 @@ seal_oneshot :: proc(algo: Algorithm, dst, tag, key, iv, aad, plaintext: []byte,
|
||||
|
||||
// open authenticates the aad and ciphertext, and decrypts the ciphertext,
|
||||
// with the provided algorithm, key, iv, and tag, and stores the output in dst,
|
||||
// returning true iff the authentication was successful. If authentication
|
||||
// returning true if and only if (⟺) the authentication was successful. If authentication
|
||||
// fails, the destination buffer will be zeroed.
|
||||
//
|
||||
// dst and ciphertext MUST alias exactly or not at all.
|
||||
|
||||
@@ -183,7 +183,7 @@ seal_ctx :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
|
||||
|
||||
// open_ctx authenticates the aad and ciphertext, and decrypts the ciphertext,
|
||||
// with the provided Context, iv, and tag, and stores the output in dst,
|
||||
// returning true iff the authentication was successful. If authentication
|
||||
// returning true if and only if (⟺) the authentication was successful. If authentication
|
||||
// fails, the destination buffer will be zeroed.
|
||||
//
|
||||
// dst and plaintext MUST alias exactly or not at all.
|
||||
|
||||
@@ -11,7 +11,6 @@ package aegis
|
||||
import "core:bytes"
|
||||
import "core:crypto"
|
||||
import "core:crypto/aes"
|
||||
import "core:mem"
|
||||
|
||||
// KEY_SIZE_128L is the AEGIS-128L key size in bytes.
|
||||
KEY_SIZE_128L :: 16
|
||||
@@ -145,7 +144,7 @@ seal :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
|
||||
|
||||
// open authenticates the aad and ciphertext, and decrypts the ciphertext,
|
||||
// with the provided Context, iv, and tag, and stores the output in dst,
|
||||
// returning true iff the authentication was successful. If authentication
|
||||
// returning true if and only if (⟺) the authentication was successful. If authentication
|
||||
// fails, the destination buffer will be zeroed.
|
||||
//
|
||||
// dst and plaintext MUST alias exactly or not at all.
|
||||
@@ -197,8 +196,8 @@ open :: proc(ctx: ^Context, dst, iv, aad, ciphertext, tag: []byte) -> bool {
|
||||
}
|
||||
|
||||
if crypto.compare_constant_time(tag, derived_tag) != 1 {
|
||||
mem.zero_explicit(raw_data(derived_tag), len(derived_tag))
|
||||
mem.zero_explicit(raw_data(dst), ct_len)
|
||||
crypto.zero_explicit(raw_data(derived_tag), len(derived_tag))
|
||||
crypto.zero_explicit(raw_data(dst), ct_len)
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -208,7 +207,7 @@ open :: proc(ctx: ^Context, dst, iv, aad, ciphertext, tag: []byte) -> bool {
|
||||
// reset sanitizes the Context. The Context must be
|
||||
// re-initialized to be used again.
|
||||
reset :: proc "contextless" (ctx: ^Context) {
|
||||
mem.zero_explicit(&ctx._key, len(ctx._key))
|
||||
crypto.zero_explicit(&ctx._key, len(ctx._key))
|
||||
ctx._key_len = 0
|
||||
ctx._is_initialized = false
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
package aegis
|
||||
|
||||
import "core:crypto"
|
||||
import aes "core:crypto/_aes/ct64"
|
||||
import "core:encoding/endian"
|
||||
import "core:mem"
|
||||
|
||||
// This uses the bitlsiced 64-bit general purpose register SWAR AES
|
||||
// round function. The intermediate state is stored in interleaved
|
||||
@@ -324,7 +324,7 @@ dec_sw_256 :: #force_inline proc "contextless" (st: ^State_SW, xi, ci: []byte) #
|
||||
@(private = "file")
|
||||
dec_partial_sw_128l :: proc "contextless" (st: ^State_SW, xn, cn: []byte) #no_bounds_check {
|
||||
tmp: [_RATE_128L]byte
|
||||
defer mem.zero_explicit(&tmp, size_of(tmp))
|
||||
defer crypto.zero_explicit(&tmp, size_of(tmp))
|
||||
|
||||
z0_0, z0_1, z1_0, z1_1 := z_sw_128l(st)
|
||||
copy(tmp[:], cn)
|
||||
@@ -349,7 +349,7 @@ dec_partial_sw_128l :: proc "contextless" (st: ^State_SW, xn, cn: []byte) #no_bo
|
||||
@(private = "file")
|
||||
dec_partial_sw_256 :: proc "contextless" (st: ^State_SW, xn, cn: []byte) #no_bounds_check {
|
||||
tmp: [_RATE_256]byte
|
||||
defer mem.zero_explicit(&tmp, size_of(tmp))
|
||||
defer crypto.zero_explicit(&tmp, size_of(tmp))
|
||||
|
||||
z_0, z_1 := z_sw_256(st)
|
||||
copy(tmp[:], cn)
|
||||
@@ -448,5 +448,5 @@ finalize_sw :: proc "contextless" (st: ^State_SW, tag: []byte, ad_len, msg_len:
|
||||
|
||||
@(private)
|
||||
reset_state_sw :: proc "contextless" (st: ^State_SW) {
|
||||
mem.zero_explicit(st, size_of(st^))
|
||||
crypto.zero_explicit(st, size_of(st^))
|
||||
}
|
||||
|
||||
397
core/crypto/aegis/aegis_impl_hw.odin
Normal file
397
core/crypto/aegis/aegis_impl_hw.odin
Normal file
@@ -0,0 +1,397 @@
|
||||
#+build amd64,arm32
|
||||
package aegis
|
||||
|
||||
import "base:intrinsics"
|
||||
import "core:crypto"
|
||||
import aes_hw "core:crypto/_aes/hw"
|
||||
import "core:encoding/endian"
|
||||
import "core:simd"
|
||||
|
||||
@(private)
|
||||
State_HW :: struct {
|
||||
s0: simd.u8x16,
|
||||
s1: simd.u8x16,
|
||||
s2: simd.u8x16,
|
||||
s3: simd.u8x16,
|
||||
s4: simd.u8x16,
|
||||
s5: simd.u8x16,
|
||||
s6: simd.u8x16,
|
||||
s7: simd.u8x16,
|
||||
rate: int,
|
||||
}
|
||||
|
||||
when ODIN_ARCH == .amd64 {
|
||||
@(private="file")
|
||||
TARGET_FEATURES :: "sse2,aes"
|
||||
} else when ODIN_ARCH == .arm64 || ODIN_ARCH == .arm32 {
|
||||
@(private="file")
|
||||
TARGET_FEATURES :: "neon,aes"
|
||||
}
|
||||
|
||||
// is_hardware_accelerated returns true if and only if (⟺) hardware
|
||||
// accelerated AEGIS is supported.
|
||||
is_hardware_accelerated :: proc "contextless" () -> bool {
|
||||
return aes_hw.is_supported()
|
||||
}
|
||||
|
||||
@(private, enable_target_feature = TARGET_FEATURES)
|
||||
init_hw :: proc "contextless" (ctx: ^Context, st: ^State_HW, iv: []byte) {
|
||||
switch ctx._key_len {
|
||||
case KEY_SIZE_128L:
|
||||
key := intrinsics.unaligned_load((^simd.u8x16)(&ctx._key[0]))
|
||||
iv := intrinsics.unaligned_load((^simd.u8x16)(raw_data(iv)))
|
||||
|
||||
st.s0 = simd.bit_xor(key, iv)
|
||||
st.s1 = intrinsics.unaligned_load((^simd.u8x16)(&_C1[0]))
|
||||
st.s2 = intrinsics.unaligned_load((^simd.u8x16)(&_C0[0]))
|
||||
st.s3 = st.s1
|
||||
st.s4 = st.s0
|
||||
st.s5 = simd.bit_xor(key, st.s2) // key ^ C0
|
||||
st.s6 = simd.bit_xor(key, st.s1) // key ^ C1
|
||||
st.s7 = st.s5
|
||||
st.rate = _RATE_128L
|
||||
|
||||
for _ in 0 ..< 10 {
|
||||
update_hw_128l(st, iv, key)
|
||||
}
|
||||
case KEY_SIZE_256:
|
||||
k0 := intrinsics.unaligned_load((^simd.u8x16)(&ctx._key[0]))
|
||||
k1 := intrinsics.unaligned_load((^simd.u8x16)(&ctx._key[16]))
|
||||
n0 := intrinsics.unaligned_load((^simd.u8x16)(&iv[0]))
|
||||
n1 := intrinsics.unaligned_load((^simd.u8x16)(&iv[16]))
|
||||
|
||||
st.s0 = simd.bit_xor(k0, n0)
|
||||
st.s1 = simd.bit_xor(k1, n1)
|
||||
st.s2 = intrinsics.unaligned_load((^simd.u8x16)(&_C1[0]))
|
||||
st.s3 = intrinsics.unaligned_load((^simd.u8x16)(&_C0[0]))
|
||||
st.s4 = simd.bit_xor(k0, st.s3) // k0 ^ C0
|
||||
st.s5 = simd.bit_xor(k1, st.s2) // k1 ^ C1
|
||||
st.rate = _RATE_256
|
||||
|
||||
u0, u1 := st.s0, st.s1
|
||||
for _ in 0 ..< 4 {
|
||||
update_hw_256(st, k0)
|
||||
update_hw_256(st, k1)
|
||||
update_hw_256(st, u0)
|
||||
update_hw_256(st, u1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@(private = "file", enable_target_feature = TARGET_FEATURES)
|
||||
update_hw_128l :: #force_inline proc "contextless" (st: ^State_HW, m0, m1: simd.u8x16) {
|
||||
s0_ := aes_hw.aesenc(st.s7, simd.bit_xor(st.s0, m0))
|
||||
s1_ := aes_hw.aesenc(st.s0, st.s1)
|
||||
s2_ := aes_hw.aesenc(st.s1, st.s2)
|
||||
s3_ := aes_hw.aesenc(st.s2, st.s3)
|
||||
s4_ := aes_hw.aesenc(st.s3, simd.bit_xor(st.s4, m1))
|
||||
s5_ := aes_hw.aesenc(st.s4, st.s5)
|
||||
s6_ := aes_hw.aesenc(st.s5, st.s6)
|
||||
s7_ := aes_hw.aesenc(st.s6, st.s7)
|
||||
st.s0, st.s1, st.s2, st.s3, st.s4, st.s5, st.s6, st.s7 = s0_, s1_, s2_, s3_, s4_, s5_, s6_, s7_
|
||||
}
|
||||
|
||||
@(private = "file", enable_target_feature = TARGET_FEATURES)
|
||||
update_hw_256 :: #force_inline proc "contextless" (st: ^State_HW, m: simd.u8x16) {
|
||||
s0_ := aes_hw.aesenc(st.s5, simd.bit_xor(st.s0, m))
|
||||
s1_ := aes_hw.aesenc(st.s0, st.s1)
|
||||
s2_ := aes_hw.aesenc(st.s1, st.s2)
|
||||
s3_ := aes_hw.aesenc(st.s2, st.s3)
|
||||
s4_ := aes_hw.aesenc(st.s3, st.s4)
|
||||
s5_ := aes_hw.aesenc(st.s4, st.s5)
|
||||
st.s0, st.s1, st.s2, st.s3, st.s4, st.s5 = s0_, s1_, s2_, s3_, s4_, s5_
|
||||
}
|
||||
|
||||
@(private = "file", enable_target_feature = TARGET_FEATURES)
|
||||
absorb_hw_128l :: #force_inline proc "contextless" (st: ^State_HW, ai: []byte) {
|
||||
t0 := intrinsics.unaligned_load((^simd.u8x16)(&ai[0]))
|
||||
t1 := intrinsics.unaligned_load((^simd.u8x16)(&ai[16]))
|
||||
update_hw_128l(st, t0, t1)
|
||||
}
|
||||
|
||||
@(private = "file", enable_target_feature = TARGET_FEATURES)
|
||||
absorb_hw_256 :: #force_inline proc "contextless" (st: ^State_HW, ai: []byte) {
|
||||
m := intrinsics.unaligned_load((^simd.u8x16)(&ai[0]))
|
||||
update_hw_256(st, m)
|
||||
}
|
||||
|
||||
@(private, enable_target_feature = TARGET_FEATURES)
|
||||
absorb_hw :: proc "contextless" (st: ^State_HW, aad: []byte) #no_bounds_check {
|
||||
ai, l := aad, len(aad)
|
||||
|
||||
switch st.rate {
|
||||
case _RATE_128L:
|
||||
for l >= _RATE_128L {
|
||||
absorb_hw_128l(st, ai)
|
||||
ai = ai[_RATE_128L:]
|
||||
l -= _RATE_128L
|
||||
}
|
||||
case _RATE_256:
|
||||
for l >= _RATE_256 {
|
||||
absorb_hw_256(st, ai)
|
||||
|
||||
ai = ai[_RATE_256:]
|
||||
l -= _RATE_256
|
||||
}
|
||||
}
|
||||
|
||||
// Pad out the remainder with `0`s till it is rate sized.
|
||||
if l > 0 {
|
||||
tmp: [_RATE_MAX]byte // AAD is not confidential.
|
||||
copy(tmp[:], ai)
|
||||
switch st.rate {
|
||||
case _RATE_128L:
|
||||
absorb_hw_128l(st, tmp[:])
|
||||
case _RATE_256:
|
||||
absorb_hw_256(st, tmp[:])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@(private = "file", enable_target_feature = TARGET_FEATURES, require_results)
|
||||
z_hw_128l :: #force_inline proc "contextless" (st: ^State_HW) -> (simd.u8x16, simd.u8x16) {
|
||||
z0 := simd.bit_xor(
|
||||
st.s6,
|
||||
simd.bit_xor(
|
||||
st.s1,
|
||||
simd.bit_and(st.s2, st.s3),
|
||||
),
|
||||
)
|
||||
z1 := simd.bit_xor(
|
||||
st.s2,
|
||||
simd.bit_xor(
|
||||
st.s5,
|
||||
simd.bit_and(st.s6, st.s7),
|
||||
),
|
||||
)
|
||||
return z0, z1
|
||||
}
|
||||
|
||||
@(private = "file", enable_target_feature = TARGET_FEATURES, require_results)
|
||||
z_hw_256 :: #force_inline proc "contextless" (st: ^State_HW) -> simd.u8x16 {
|
||||
return simd.bit_xor(
|
||||
st.s1,
|
||||
simd.bit_xor(
|
||||
st.s4,
|
||||
simd.bit_xor(
|
||||
st.s5,
|
||||
simd.bit_and(st.s2, st.s3),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@(private = "file", enable_target_feature = TARGET_FEATURES)
|
||||
enc_hw_128l :: #force_inline proc "contextless" (st: ^State_HW, ci, xi: []byte) #no_bounds_check {
|
||||
z0, z1 := z_hw_128l(st)
|
||||
|
||||
t0 := intrinsics.unaligned_load((^simd.u8x16)(&xi[0]))
|
||||
t1 := intrinsics.unaligned_load((^simd.u8x16)(&xi[16]))
|
||||
update_hw_128l(st, t0, t1)
|
||||
|
||||
out0 := simd.bit_xor(t0, z0)
|
||||
out1 := simd.bit_xor(t1, z1)
|
||||
intrinsics.unaligned_store((^simd.u8x16)(&ci[0]), out0)
|
||||
intrinsics.unaligned_store((^simd.u8x16)(&ci[16]), out1)
|
||||
}
|
||||
|
||||
@(private = "file", enable_target_feature = TARGET_FEATURES)
|
||||
enc_hw_256 :: #force_inline proc "contextless" (st: ^State_HW, ci, xi: []byte) #no_bounds_check {
|
||||
z := z_hw_256(st)
|
||||
|
||||
xi_ := intrinsics.unaligned_load((^simd.u8x16)(raw_data(xi)))
|
||||
update_hw_256(st, xi_)
|
||||
|
||||
ci_ := simd.bit_xor(xi_, z)
|
||||
intrinsics.unaligned_store((^simd.u8x16)(raw_data(ci)), ci_)
|
||||
}
|
||||
|
||||
@(private, enable_target_feature = TARGET_FEATURES)
|
||||
enc_hw :: proc "contextless" (st: ^State_HW, dst, src: []byte) #no_bounds_check {
|
||||
ci, xi, l := dst, src, len(src)
|
||||
|
||||
switch st.rate {
|
||||
case _RATE_128L:
|
||||
for l >= _RATE_128L {
|
||||
enc_hw_128l(st, ci, xi)
|
||||
ci = ci[_RATE_128L:]
|
||||
xi = xi[_RATE_128L:]
|
||||
l -= _RATE_128L
|
||||
}
|
||||
case _RATE_256:
|
||||
for l >= _RATE_256 {
|
||||
enc_hw_256(st, ci, xi)
|
||||
ci = ci[_RATE_256:]
|
||||
xi = xi[_RATE_256:]
|
||||
l -= _RATE_256
|
||||
}
|
||||
}
|
||||
|
||||
// Pad out the remainder with `0`s till it is rate sized.
|
||||
if l > 0 {
|
||||
tmp: [_RATE_MAX]byte // Ciphertext is not confidential.
|
||||
copy(tmp[:], xi)
|
||||
switch st.rate {
|
||||
case _RATE_128L:
|
||||
enc_hw_128l(st, tmp[:], tmp[:])
|
||||
case _RATE_256:
|
||||
enc_hw_256(st, tmp[:], tmp[:])
|
||||
}
|
||||
copy(ci, tmp[:l])
|
||||
}
|
||||
}
|
||||
|
||||
@(private = "file", enable_target_feature = TARGET_FEATURES)
|
||||
dec_hw_128l :: #force_inline proc "contextless" (st: ^State_HW, xi, ci: []byte) #no_bounds_check {
|
||||
z0, z1 := z_hw_128l(st)
|
||||
|
||||
t0 := intrinsics.unaligned_load((^simd.u8x16)(&ci[0]))
|
||||
t1 := intrinsics.unaligned_load((^simd.u8x16)(&ci[16]))
|
||||
out0 := simd.bit_xor(t0, z0)
|
||||
out1 := simd.bit_xor(t1, z1)
|
||||
|
||||
update_hw_128l(st, out0, out1)
|
||||
intrinsics.unaligned_store((^simd.u8x16)(&xi[0]), out0)
|
||||
intrinsics.unaligned_store((^simd.u8x16)(&xi[16]), out1)
|
||||
}
|
||||
|
||||
@(private = "file", enable_target_feature = TARGET_FEATURES)
|
||||
dec_hw_256 :: #force_inline proc "contextless" (st: ^State_HW, xi, ci: []byte) #no_bounds_check {
|
||||
z := z_hw_256(st)
|
||||
|
||||
ci_ := intrinsics.unaligned_load((^simd.u8x16)(raw_data(ci)))
|
||||
xi_ := simd.bit_xor(ci_, z)
|
||||
|
||||
update_hw_256(st, xi_)
|
||||
intrinsics.unaligned_store((^simd.u8x16)(raw_data(xi)), xi_)
|
||||
}
|
||||
|
||||
@(private = "file", enable_target_feature = TARGET_FEATURES)
|
||||
dec_partial_hw_128l :: #force_inline proc "contextless" (st: ^State_HW, xn, cn: []byte) #no_bounds_check {
|
||||
tmp: [_RATE_128L]byte
|
||||
defer crypto.zero_explicit(&tmp, size_of(tmp))
|
||||
|
||||
z0, z1 := z_hw_128l(st)
|
||||
copy(tmp[:], cn)
|
||||
|
||||
t0 := intrinsics.unaligned_load((^simd.u8x16)(&tmp[0]))
|
||||
t1 := intrinsics.unaligned_load((^simd.u8x16)(&tmp[16]))
|
||||
out0 := simd.bit_xor(t0, z0)
|
||||
out1 := simd.bit_xor(t1, z1)
|
||||
|
||||
intrinsics.unaligned_store((^simd.u8x16)(&tmp[0]), out0)
|
||||
intrinsics.unaligned_store((^simd.u8x16)(&tmp[16]), out1)
|
||||
copy(xn, tmp[:])
|
||||
|
||||
for off := len(xn); off < _RATE_128L; off += 1 {
|
||||
tmp[off] = 0
|
||||
}
|
||||
out0 = intrinsics.unaligned_load((^simd.u8x16)(&tmp[0])) // v0
|
||||
out1 = intrinsics.unaligned_load((^simd.u8x16)(&tmp[16])) // v1
|
||||
update_hw_128l(st, out0, out1)
|
||||
}
|
||||
|
||||
@(private = "file", enable_target_feature = TARGET_FEATURES)
|
||||
dec_partial_hw_256 :: #force_inline proc "contextless" (st: ^State_HW, xn, cn: []byte) #no_bounds_check {
|
||||
tmp: [_RATE_256]byte
|
||||
defer crypto.zero_explicit(&tmp, size_of(tmp))
|
||||
|
||||
z := z_hw_256(st)
|
||||
copy(tmp[:], cn)
|
||||
|
||||
cn_ := intrinsics.unaligned_load((^simd.u8x16)(&tmp[0]))
|
||||
xn_ := simd.bit_xor(cn_, z)
|
||||
|
||||
intrinsics.unaligned_store((^simd.u8x16)(&tmp[0]), xn_)
|
||||
copy(xn, tmp[:])
|
||||
|
||||
for off := len(xn); off < _RATE_256; off += 1 {
|
||||
tmp[off] = 0
|
||||
}
|
||||
xn_ = intrinsics.unaligned_load((^simd.u8x16)(&tmp[0]))
|
||||
update_hw_256(st, xn_)
|
||||
}
|
||||
|
||||
@(private, enable_target_feature = TARGET_FEATURES)
|
||||
dec_hw :: proc "contextless" (st: ^State_HW, dst, src: []byte) #no_bounds_check {
|
||||
xi, ci, l := dst, src, len(src)
|
||||
|
||||
switch st.rate {
|
||||
case _RATE_128L:
|
||||
for l >= _RATE_128L {
|
||||
dec_hw_128l(st, xi, ci)
|
||||
xi = xi[_RATE_128L:]
|
||||
ci = ci[_RATE_128L:]
|
||||
l -= _RATE_128L
|
||||
}
|
||||
case _RATE_256:
|
||||
for l >= _RATE_256 {
|
||||
dec_hw_256(st, xi, ci)
|
||||
xi = xi[_RATE_256:]
|
||||
ci = ci[_RATE_256:]
|
||||
l -= _RATE_256
|
||||
}
|
||||
}
|
||||
|
||||
// Process the remainder.
|
||||
if l > 0 {
|
||||
switch st.rate {
|
||||
case _RATE_128L:
|
||||
dec_partial_hw_128l(st, xi, ci)
|
||||
case _RATE_256:
|
||||
dec_partial_hw_256(st, xi, ci)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@(private, enable_target_feature = TARGET_FEATURES)
|
||||
finalize_hw :: proc "contextless" (st: ^State_HW, tag: []byte, ad_len, msg_len: int) {
|
||||
tmp: [16]byte
|
||||
endian.unchecked_put_u64le(tmp[0:], u64(ad_len) * 8)
|
||||
endian.unchecked_put_u64le(tmp[8:], u64(msg_len) * 8)
|
||||
|
||||
t := intrinsics.unaligned_load((^simd.u8x16)(&tmp[0]))
|
||||
|
||||
t0, t1: simd.u8x16 = ---, ---
|
||||
switch st.rate {
|
||||
case _RATE_128L:
|
||||
t = simd.bit_xor(st.s2, t)
|
||||
for _ in 0 ..< 7 {
|
||||
update_hw_128l(st, t, t)
|
||||
}
|
||||
|
||||
t0 = simd.bit_xor(st.s0, st.s1)
|
||||
t0 = simd.bit_xor(t0, st.s2)
|
||||
t0 = simd.bit_xor(t0, st.s3)
|
||||
|
||||
t1 = simd.bit_xor(st.s4, st.s5)
|
||||
t1 = simd.bit_xor(t1, st.s6)
|
||||
if len(tag) == TAG_SIZE_256 {
|
||||
t1 = simd.bit_xor(t1, st.s7)
|
||||
}
|
||||
case _RATE_256:
|
||||
t = simd.bit_xor(st.s3, t)
|
||||
for _ in 0 ..< 7 {
|
||||
update_hw_256(st, t)
|
||||
}
|
||||
|
||||
t0 = simd.bit_xor(st.s0, st.s1)
|
||||
t0 = simd.bit_xor(t0, st.s2)
|
||||
|
||||
t1 = simd.bit_xor(st.s3, st.s4)
|
||||
t1 = simd.bit_xor(t1, st.s5)
|
||||
}
|
||||
switch len(tag) {
|
||||
case TAG_SIZE_128:
|
||||
t0 = simd.bit_xor(t0, t1)
|
||||
intrinsics.unaligned_store((^simd.u8x16)(&tag[0]), t0)
|
||||
case TAG_SIZE_256:
|
||||
intrinsics.unaligned_store((^simd.u8x16)(&tag[0]), t0)
|
||||
intrinsics.unaligned_store((^simd.u8x16)(&tag[16]), t1)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
reset_state_hw :: proc "contextless" (st: ^State_HW) {
|
||||
crypto.zero_explicit(st, size_of(st^))
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
#+build !amd64
|
||||
#+build !arm64
|
||||
#+build !arm32
|
||||
package aegis
|
||||
|
||||
@(private = "file")
|
||||
@@ -7,7 +9,7 @@ ERR_HW_NOT_SUPPORTED :: "crypto/aegis: hardware implementation unsupported"
|
||||
@(private)
|
||||
State_HW :: struct {}
|
||||
|
||||
// is_hardware_accelerated returns true iff hardware accelerated AEGIS
|
||||
// is_hardware_accelerated returns true if and only if (⟺) hardware accelerated AEGIS
|
||||
// is supported.
|
||||
is_hardware_accelerated :: proc "contextless" () -> bool {
|
||||
return false
|
||||
|
||||
@@ -1,389 +0,0 @@
|
||||
#+build amd64
|
||||
package aegis
|
||||
|
||||
import "base:intrinsics"
|
||||
import "core:crypto/aes"
|
||||
import "core:encoding/endian"
|
||||
import "core:mem"
|
||||
import "core:simd/x86"
|
||||
|
||||
@(private)
|
||||
State_HW :: struct {
|
||||
s0: x86.__m128i,
|
||||
s1: x86.__m128i,
|
||||
s2: x86.__m128i,
|
||||
s3: x86.__m128i,
|
||||
s4: x86.__m128i,
|
||||
s5: x86.__m128i,
|
||||
s6: x86.__m128i,
|
||||
s7: x86.__m128i,
|
||||
rate: int,
|
||||
}
|
||||
|
||||
// is_hardware_accelerated returns true iff hardware accelerated AEGIS
|
||||
// is supported.
|
||||
is_hardware_accelerated :: proc "contextless" () -> bool {
|
||||
return aes.is_hardware_accelerated()
|
||||
}
|
||||
|
||||
@(private, enable_target_feature = "sse2,aes")
|
||||
init_hw :: proc "contextless" (ctx: ^Context, st: ^State_HW, iv: []byte) {
|
||||
switch ctx._key_len {
|
||||
case KEY_SIZE_128L:
|
||||
key := intrinsics.unaligned_load((^x86.__m128i)(&ctx._key[0]))
|
||||
iv := intrinsics.unaligned_load((^x86.__m128i)(raw_data(iv)))
|
||||
|
||||
st.s0 = x86._mm_xor_si128(key, iv)
|
||||
st.s1 = intrinsics.unaligned_load((^x86.__m128i)(&_C1[0]))
|
||||
st.s2 = intrinsics.unaligned_load((^x86.__m128i)(&_C0[0]))
|
||||
st.s3 = st.s1
|
||||
st.s4 = st.s0
|
||||
st.s5 = x86._mm_xor_si128(key, st.s2) // key ^ C0
|
||||
st.s6 = x86._mm_xor_si128(key, st.s1) // key ^ C1
|
||||
st.s7 = st.s5
|
||||
st.rate = _RATE_128L
|
||||
|
||||
for _ in 0 ..< 10 {
|
||||
update_hw_128l(st, iv, key)
|
||||
}
|
||||
case KEY_SIZE_256:
|
||||
k0 := intrinsics.unaligned_load((^x86.__m128i)(&ctx._key[0]))
|
||||
k1 := intrinsics.unaligned_load((^x86.__m128i)(&ctx._key[16]))
|
||||
n0 := intrinsics.unaligned_load((^x86.__m128i)(&iv[0]))
|
||||
n1 := intrinsics.unaligned_load((^x86.__m128i)(&iv[16]))
|
||||
|
||||
st.s0 = x86._mm_xor_si128(k0, n0)
|
||||
st.s1 = x86._mm_xor_si128(k1, n1)
|
||||
st.s2 = intrinsics.unaligned_load((^x86.__m128i)(&_C1[0]))
|
||||
st.s3 = intrinsics.unaligned_load((^x86.__m128i)(&_C0[0]))
|
||||
st.s4 = x86._mm_xor_si128(k0, st.s3) // k0 ^ C0
|
||||
st.s5 = x86._mm_xor_si128(k1, st.s2) // k1 ^ C1
|
||||
st.rate = _RATE_256
|
||||
|
||||
u0, u1 := st.s0, st.s1
|
||||
for _ in 0 ..< 4 {
|
||||
update_hw_256(st, k0)
|
||||
update_hw_256(st, k1)
|
||||
update_hw_256(st, u0)
|
||||
update_hw_256(st, u1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@(private = "file", enable_target_feature = "sse2,aes")
|
||||
update_hw_128l :: #force_inline proc "contextless" (st: ^State_HW, m0, m1: x86.__m128i) {
|
||||
s0_ := x86._mm_aesenc_si128(st.s7, x86._mm_xor_si128(st.s0, m0))
|
||||
s1_ := x86._mm_aesenc_si128(st.s0, st.s1)
|
||||
s2_ := x86._mm_aesenc_si128(st.s1, st.s2)
|
||||
s3_ := x86._mm_aesenc_si128(st.s2, st.s3)
|
||||
s4_ := x86._mm_aesenc_si128(st.s3, x86._mm_xor_si128(st.s4, m1))
|
||||
s5_ := x86._mm_aesenc_si128(st.s4, st.s5)
|
||||
s6_ := x86._mm_aesenc_si128(st.s5, st.s6)
|
||||
s7_ := x86._mm_aesenc_si128(st.s6, st.s7)
|
||||
st.s0, st.s1, st.s2, st.s3, st.s4, st.s5, st.s6, st.s7 = s0_, s1_, s2_, s3_, s4_, s5_, s6_, s7_
|
||||
}
|
||||
|
||||
@(private = "file", enable_target_feature = "sse2,aes")
|
||||
update_hw_256 :: #force_inline proc "contextless" (st: ^State_HW, m: x86.__m128i) {
|
||||
s0_ := x86._mm_aesenc_si128(st.s5, x86._mm_xor_si128(st.s0, m))
|
||||
s1_ := x86._mm_aesenc_si128(st.s0, st.s1)
|
||||
s2_ := x86._mm_aesenc_si128(st.s1, st.s2)
|
||||
s3_ := x86._mm_aesenc_si128(st.s2, st.s3)
|
||||
s4_ := x86._mm_aesenc_si128(st.s3, st.s4)
|
||||
s5_ := x86._mm_aesenc_si128(st.s4, st.s5)
|
||||
st.s0, st.s1, st.s2, st.s3, st.s4, st.s5 = s0_, s1_, s2_, s3_, s4_, s5_
|
||||
}
|
||||
|
||||
@(private = "file", enable_target_feature = "sse2,aes")
|
||||
absorb_hw_128l :: #force_inline proc "contextless" (st: ^State_HW, ai: []byte) {
|
||||
t0 := intrinsics.unaligned_load((^x86.__m128i)(&ai[0]))
|
||||
t1 := intrinsics.unaligned_load((^x86.__m128i)(&ai[16]))
|
||||
update_hw_128l(st, t0, t1)
|
||||
}
|
||||
|
||||
@(private = "file", enable_target_feature = "sse2,aes")
|
||||
absorb_hw_256 :: #force_inline proc "contextless" (st: ^State_HW, ai: []byte) {
|
||||
m := intrinsics.unaligned_load((^x86.__m128i)(&ai[0]))
|
||||
update_hw_256(st, m)
|
||||
}
|
||||
|
||||
@(private, enable_target_feature = "sse2,aes")
|
||||
absorb_hw :: proc "contextless" (st: ^State_HW, aad: []byte) #no_bounds_check {
|
||||
ai, l := aad, len(aad)
|
||||
|
||||
switch st.rate {
|
||||
case _RATE_128L:
|
||||
for l >= _RATE_128L {
|
||||
absorb_hw_128l(st, ai)
|
||||
ai = ai[_RATE_128L:]
|
||||
l -= _RATE_128L
|
||||
}
|
||||
case _RATE_256:
|
||||
for l >= _RATE_256 {
|
||||
absorb_hw_256(st, ai)
|
||||
|
||||
ai = ai[_RATE_256:]
|
||||
l -= _RATE_256
|
||||
}
|
||||
}
|
||||
|
||||
// Pad out the remainder with `0`s till it is rate sized.
|
||||
if l > 0 {
|
||||
tmp: [_RATE_MAX]byte // AAD is not confidential.
|
||||
copy(tmp[:], ai)
|
||||
switch st.rate {
|
||||
case _RATE_128L:
|
||||
absorb_hw_128l(st, tmp[:])
|
||||
case _RATE_256:
|
||||
absorb_hw_256(st, tmp[:])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@(private = "file", enable_target_feature = "sse2", require_results)
|
||||
z_hw_128l :: #force_inline proc "contextless" (st: ^State_HW) -> (x86.__m128i, x86.__m128i) {
|
||||
z0 := x86._mm_xor_si128(
|
||||
st.s6,
|
||||
x86._mm_xor_si128(
|
||||
st.s1,
|
||||
x86._mm_and_si128(st.s2, st.s3),
|
||||
),
|
||||
)
|
||||
z1 := x86._mm_xor_si128(
|
||||
st.s2,
|
||||
x86._mm_xor_si128(
|
||||
st.s5,
|
||||
x86._mm_and_si128(st.s6, st.s7),
|
||||
),
|
||||
)
|
||||
return z0, z1
|
||||
}
|
||||
|
||||
@(private = "file", enable_target_feature = "sse2", require_results)
|
||||
z_hw_256 :: #force_inline proc "contextless" (st: ^State_HW) -> x86.__m128i {
|
||||
return x86._mm_xor_si128(
|
||||
st.s1,
|
||||
x86._mm_xor_si128(
|
||||
st.s4,
|
||||
x86._mm_xor_si128(
|
||||
st.s5,
|
||||
x86._mm_and_si128(st.s2, st.s3),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@(private = "file", enable_target_feature = "sse2,aes")
|
||||
enc_hw_128l :: #force_inline proc "contextless" (st: ^State_HW, ci, xi: []byte) #no_bounds_check {
|
||||
z0, z1 := z_hw_128l(st)
|
||||
|
||||
t0 := intrinsics.unaligned_load((^x86.__m128i)(&xi[0]))
|
||||
t1 := intrinsics.unaligned_load((^x86.__m128i)(&xi[16]))
|
||||
update_hw_128l(st, t0, t1)
|
||||
|
||||
out0 := x86._mm_xor_si128(t0, z0)
|
||||
out1 := x86._mm_xor_si128(t1, z1)
|
||||
intrinsics.unaligned_store((^x86.__m128i)(&ci[0]), out0)
|
||||
intrinsics.unaligned_store((^x86.__m128i)(&ci[16]), out1)
|
||||
}
|
||||
|
||||
@(private = "file", enable_target_feature = "sse2,aes")
|
||||
enc_hw_256 :: #force_inline proc "contextless" (st: ^State_HW, ci, xi: []byte) #no_bounds_check {
|
||||
z := z_hw_256(st)
|
||||
|
||||
xi_ := intrinsics.unaligned_load((^x86.__m128i)(raw_data(xi)))
|
||||
update_hw_256(st, xi_)
|
||||
|
||||
ci_ := x86._mm_xor_si128(xi_, z)
|
||||
intrinsics.unaligned_store((^x86.__m128i)(raw_data(ci)), ci_)
|
||||
}
|
||||
|
||||
@(private, enable_target_feature = "sse2,aes")
|
||||
enc_hw :: proc "contextless" (st: ^State_HW, dst, src: []byte) #no_bounds_check {
|
||||
ci, xi, l := dst, src, len(src)
|
||||
|
||||
switch st.rate {
|
||||
case _RATE_128L:
|
||||
for l >= _RATE_128L {
|
||||
enc_hw_128l(st, ci, xi)
|
||||
ci = ci[_RATE_128L:]
|
||||
xi = xi[_RATE_128L:]
|
||||
l -= _RATE_128L
|
||||
}
|
||||
case _RATE_256:
|
||||
for l >= _RATE_256 {
|
||||
enc_hw_256(st, ci, xi)
|
||||
ci = ci[_RATE_256:]
|
||||
xi = xi[_RATE_256:]
|
||||
l -= _RATE_256
|
||||
}
|
||||
}
|
||||
|
||||
// Pad out the remainder with `0`s till it is rate sized.
|
||||
if l > 0 {
|
||||
tmp: [_RATE_MAX]byte // Ciphertext is not confidential.
|
||||
copy(tmp[:], xi)
|
||||
switch st.rate {
|
||||
case _RATE_128L:
|
||||
enc_hw_128l(st, tmp[:], tmp[:])
|
||||
case _RATE_256:
|
||||
enc_hw_256(st, tmp[:], tmp[:])
|
||||
}
|
||||
copy(ci, tmp[:l])
|
||||
}
|
||||
}
|
||||
|
||||
@(private = "file", enable_target_feature = "sse2,aes")
|
||||
dec_hw_128l :: #force_inline proc "contextless" (st: ^State_HW, xi, ci: []byte) #no_bounds_check {
|
||||
z0, z1 := z_hw_128l(st)
|
||||
|
||||
t0 := intrinsics.unaligned_load((^x86.__m128i)(&ci[0]))
|
||||
t1 := intrinsics.unaligned_load((^x86.__m128i)(&ci[16]))
|
||||
out0 := x86._mm_xor_si128(t0, z0)
|
||||
out1 := x86._mm_xor_si128(t1, z1)
|
||||
|
||||
update_hw_128l(st, out0, out1)
|
||||
intrinsics.unaligned_store((^x86.__m128i)(&xi[0]), out0)
|
||||
intrinsics.unaligned_store((^x86.__m128i)(&xi[16]), out1)
|
||||
}
|
||||
|
||||
@(private = "file", enable_target_feature = "sse2,aes")
|
||||
dec_hw_256 :: #force_inline proc "contextless" (st: ^State_HW, xi, ci: []byte) #no_bounds_check {
|
||||
z := z_hw_256(st)
|
||||
|
||||
ci_ := intrinsics.unaligned_load((^x86.__m128i)(raw_data(ci)))
|
||||
xi_ := x86._mm_xor_si128(ci_, z)
|
||||
|
||||
update_hw_256(st, xi_)
|
||||
intrinsics.unaligned_store((^x86.__m128i)(raw_data(xi)), xi_)
|
||||
}
|
||||
|
||||
@(private = "file", enable_target_feature = "sse2,aes")
|
||||
dec_partial_hw_128l :: #force_inline proc "contextless" (st: ^State_HW, xn, cn: []byte) #no_bounds_check {
|
||||
tmp: [_RATE_128L]byte
|
||||
defer mem.zero_explicit(&tmp, size_of(tmp))
|
||||
|
||||
z0, z1 := z_hw_128l(st)
|
||||
copy(tmp[:], cn)
|
||||
|
||||
t0 := intrinsics.unaligned_load((^x86.__m128i)(&tmp[0]))
|
||||
t1 := intrinsics.unaligned_load((^x86.__m128i)(&tmp[16]))
|
||||
out0 := x86._mm_xor_si128(t0, z0)
|
||||
out1 := x86._mm_xor_si128(t1, z1)
|
||||
|
||||
intrinsics.unaligned_store((^x86.__m128i)(&tmp[0]), out0)
|
||||
intrinsics.unaligned_store((^x86.__m128i)(&tmp[16]), out1)
|
||||
copy(xn, tmp[:])
|
||||
|
||||
for off := len(xn); off < _RATE_128L; off += 1 {
|
||||
tmp[off] = 0
|
||||
}
|
||||
out0 = intrinsics.unaligned_load((^x86.__m128i)(&tmp[0])) // v0
|
||||
out1 = intrinsics.unaligned_load((^x86.__m128i)(&tmp[16])) // v1
|
||||
update_hw_128l(st, out0, out1)
|
||||
}
|
||||
|
||||
@(private = "file", enable_target_feature = "sse2,aes")
|
||||
dec_partial_hw_256 :: #force_inline proc "contextless" (st: ^State_HW, xn, cn: []byte) #no_bounds_check {
|
||||
tmp: [_RATE_256]byte
|
||||
defer mem.zero_explicit(&tmp, size_of(tmp))
|
||||
|
||||
z := z_hw_256(st)
|
||||
copy(tmp[:], cn)
|
||||
|
||||
cn_ := intrinsics.unaligned_load((^x86.__m128i)(&tmp[0]))
|
||||
xn_ := x86._mm_xor_si128(cn_, z)
|
||||
|
||||
intrinsics.unaligned_store((^x86.__m128i)(&tmp[0]), xn_)
|
||||
copy(xn, tmp[:])
|
||||
|
||||
for off := len(xn); off < _RATE_256; off += 1 {
|
||||
tmp[off] = 0
|
||||
}
|
||||
xn_ = intrinsics.unaligned_load((^x86.__m128i)(&tmp[0]))
|
||||
update_hw_256(st, xn_)
|
||||
}
|
||||
|
||||
@(private, enable_target_feature = "sse2,aes")
|
||||
dec_hw :: proc "contextless" (st: ^State_HW, dst, src: []byte) #no_bounds_check {
|
||||
xi, ci, l := dst, src, len(src)
|
||||
|
||||
switch st.rate {
|
||||
case _RATE_128L:
|
||||
for l >= _RATE_128L {
|
||||
dec_hw_128l(st, xi, ci)
|
||||
xi = xi[_RATE_128L:]
|
||||
ci = ci[_RATE_128L:]
|
||||
l -= _RATE_128L
|
||||
}
|
||||
case _RATE_256:
|
||||
for l >= _RATE_256 {
|
||||
dec_hw_256(st, xi, ci)
|
||||
xi = xi[_RATE_256:]
|
||||
ci = ci[_RATE_256:]
|
||||
l -= _RATE_256
|
||||
}
|
||||
}
|
||||
|
||||
// Process the remainder.
|
||||
if l > 0 {
|
||||
switch st.rate {
|
||||
case _RATE_128L:
|
||||
dec_partial_hw_128l(st, xi, ci)
|
||||
case _RATE_256:
|
||||
dec_partial_hw_256(st, xi, ci)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@(private, enable_target_feature = "sse2,aes")
|
||||
finalize_hw :: proc "contextless" (st: ^State_HW, tag: []byte, ad_len, msg_len: int) {
|
||||
tmp: [16]byte
|
||||
endian.unchecked_put_u64le(tmp[0:], u64(ad_len) * 8)
|
||||
endian.unchecked_put_u64le(tmp[8:], u64(msg_len) * 8)
|
||||
|
||||
t := intrinsics.unaligned_load((^x86.__m128i)(&tmp[0]))
|
||||
|
||||
t0, t1: x86.__m128i = ---, ---
|
||||
switch st.rate {
|
||||
case _RATE_128L:
|
||||
t = x86._mm_xor_si128(st.s2, t)
|
||||
for _ in 0 ..< 7 {
|
||||
update_hw_128l(st, t, t)
|
||||
}
|
||||
|
||||
t0 = x86._mm_xor_si128(st.s0, st.s1)
|
||||
t0 = x86._mm_xor_si128(t0, st.s2)
|
||||
t0 = x86._mm_xor_si128(t0, st.s3)
|
||||
|
||||
t1 = x86._mm_xor_si128(st.s4, st.s5)
|
||||
t1 = x86._mm_xor_si128(t1, st.s6)
|
||||
if len(tag) == TAG_SIZE_256 {
|
||||
t1 = x86._mm_xor_si128(t1, st.s7)
|
||||
}
|
||||
case _RATE_256:
|
||||
t = x86._mm_xor_si128(st.s3, t)
|
||||
for _ in 0 ..< 7 {
|
||||
update_hw_256(st, t)
|
||||
}
|
||||
|
||||
t0 = x86._mm_xor_si128(st.s0, st.s1)
|
||||
t0 = x86._mm_xor_si128(t0, st.s2)
|
||||
|
||||
t1 = x86._mm_xor_si128(st.s3, st.s4)
|
||||
t1 = x86._mm_xor_si128(t1, st.s5)
|
||||
}
|
||||
switch len(tag) {
|
||||
case TAG_SIZE_128:
|
||||
t0 = x86._mm_xor_si128(t0, t1)
|
||||
intrinsics.unaligned_store((^x86.__m128i)(&tag[0]), t0)
|
||||
case TAG_SIZE_256:
|
||||
intrinsics.unaligned_store((^x86.__m128i)(&tag[0]), t0)
|
||||
intrinsics.unaligned_store((^x86.__m128i)(&tag[16]), t1)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
reset_state_hw :: proc "contextless" (st: ^State_HW) {
|
||||
mem.zero_explicit(st, size_of(st^))
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import "core:bytes"
|
||||
import "core:crypto/_aes/ct64"
|
||||
import "core:encoding/endian"
|
||||
import "core:math/bits"
|
||||
import "core:mem"
|
||||
|
||||
// CTR_IV_SIZE is the size of the CTR mode IV in bytes.
|
||||
CTR_IV_SIZE :: 16
|
||||
@@ -117,7 +116,7 @@ reset_ctr :: proc "contextless" (ctx: ^Context_CTR) {
|
||||
ctx._off = 0
|
||||
ctx._ctr_hi = 0
|
||||
ctx._ctr_lo = 0
|
||||
mem.zero_explicit(&ctx._buffer, size_of(ctx._buffer))
|
||||
zero_explicit(&ctx._buffer, size_of(ctx._buffer))
|
||||
ctx._is_initialized = false
|
||||
}
|
||||
|
||||
@@ -172,7 +171,7 @@ ctr_blocks :: proc(ctx: ^Context_CTR, dst, src: []byte, nr_blocks: int) #no_boun
|
||||
// Write back the counter.
|
||||
ctx._ctr_hi, ctx._ctr_lo = ctr_hi, ctr_lo
|
||||
|
||||
mem.zero_explicit(&tmp, size_of(tmp))
|
||||
zero_explicit(&tmp, size_of(tmp))
|
||||
}
|
||||
|
||||
@(private)
|
||||
|
||||
@@ -1,31 +1,32 @@
|
||||
#+build amd64
|
||||
#+build amd64,arm32
|
||||
package aes
|
||||
|
||||
import "base:intrinsics"
|
||||
import "core:crypto/_aes"
|
||||
import aes_hw "core:crypto/_aes/hw"
|
||||
import "core:encoding/endian"
|
||||
import "core:math/bits"
|
||||
import "core:mem"
|
||||
import "core:simd/x86"
|
||||
import "core:simd"
|
||||
|
||||
@(private)
|
||||
CTR_STRIDE_HW :: 4
|
||||
@(private)
|
||||
CTR_STRIDE_BYTES_HW :: CTR_STRIDE_HW * BLOCK_SIZE
|
||||
|
||||
@(private, enable_target_feature = "sse2,aes")
|
||||
@(private, enable_target_feature = aes_hw.TARGET_FEATURES)
|
||||
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 = ---
|
||||
sks: [15]simd.u8x16 = ---
|
||||
for i in 0 ..= hw_ctx._num_rounds {
|
||||
sks[i] = intrinsics.unaligned_load((^x86.__m128i)(&hw_ctx._sk_exp_enc[i]))
|
||||
sks[i] = intrinsics.unaligned_load((^simd.u8x16)(&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)),
|
||||
}
|
||||
hw_inc_ctr := #force_inline proc "contextless" (hi, lo: u64) -> (simd.u8x16, u64, u64) {
|
||||
buf: [BLOCK_SIZE]byte = ---
|
||||
endian.unchecked_put_u64be(buf[0:], hi)
|
||||
endian.unchecked_put_u64be(buf[8:], lo)
|
||||
ret := intrinsics.unaligned_load((^simd.u8x16)(&buf))
|
||||
|
||||
hi, lo := hi, lo
|
||||
carry: u64
|
||||
@@ -47,42 +48,42 @@ ctr_blocks_hw :: proc(ctx: ^Context_CTR, dst, src: []byte, nr_blocks: int) #no_b
|
||||
nr_blocks := nr_blocks
|
||||
ctr_hi, ctr_lo := ctx._ctr_hi, ctx._ctr_lo
|
||||
|
||||
blks: [CTR_STRIDE_HW]x86.__m128i = ---
|
||||
blks: [CTR_STRIDE_HW]simd.u8x16 = ---
|
||||
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])
|
||||
blks[i] = simd.bit_xor(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])
|
||||
blks[j] = aes_hw.aesenc(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])
|
||||
blks[i] = aes_hw.aesenclast(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])
|
||||
blks[j] = aes_hw.aesenc(blks[j], sks[i])
|
||||
}
|
||||
}
|
||||
#unroll for i in 0 ..< CTR_STRIDE_HW {
|
||||
blks[i] = x86._mm_aesenclast_si128(blks[i], sks[12])
|
||||
blks[i] = aes_hw.aesenclast(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])
|
||||
blks[j] = aes_hw.aesenc(blks[j], sks[i])
|
||||
}
|
||||
}
|
||||
#unroll for i in 0 ..< CTR_STRIDE_HW {
|
||||
blks[i] = x86._mm_aesenclast_si128(blks[i], sks[14])
|
||||
blks[i] = aes_hw.aesenclast(blks[i], sks[14])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,23 +100,23 @@ ctr_blocks_hw :: proc(ctx: ^Context_CTR, dst, src: []byte, nr_blocks: int) #no_b
|
||||
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])
|
||||
blks[0] = simd.bit_xor(blks[0], sks[0])
|
||||
#unroll for i in 1 ..= 9 {
|
||||
blks[0] = x86._mm_aesenc_si128(blks[0], sks[i])
|
||||
blks[0] = aes_hw.aesenc(blks[0], sks[i])
|
||||
}
|
||||
switch hw_ctx._num_rounds {
|
||||
case _aes.ROUNDS_128:
|
||||
blks[0] = x86._mm_aesenclast_si128(blks[0], sks[10])
|
||||
blks[0] = aes_hw.aesenclast(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] = aes_hw.aesenc(blks[0], sks[i])
|
||||
}
|
||||
blks[0] = x86._mm_aesenclast_si128(blks[0], sks[12])
|
||||
blks[0] = aes_hw.aesenclast(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] = aes_hw.aesenc(blks[0], sks[i])
|
||||
}
|
||||
blks[0] = x86._mm_aesenclast_si128(blks[0], sks[14])
|
||||
blks[0] = aes_hw.aesenclast(blks[0], sks[14])
|
||||
}
|
||||
|
||||
xor_blocks_hw(dst, src, blks[:1])
|
||||
@@ -130,22 +131,22 @@ ctr_blocks_hw :: proc(ctx: ^Context_CTR, dst, src: []byte, nr_blocks: int) #no_b
|
||||
// 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))
|
||||
zero_explicit(&blks, size_of(blks))
|
||||
zero_explicit(&sks, size_of(sks))
|
||||
}
|
||||
|
||||
@(private, enable_target_feature = "sse2")
|
||||
xor_blocks_hw :: proc(dst, src: []byte, blocks: []x86.__m128i) {
|
||||
@(private, enable_target_feature = aes_hw.TARGET_FEATURES)
|
||||
xor_blocks_hw :: proc(dst, src: []byte, blocks: []simd.u8x16) {
|
||||
#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)
|
||||
tmp := intrinsics.unaligned_load((^simd.u8x16)(raw_data(src[off:])))
|
||||
blocks[i] = simd.bit_xor(blocks[i], tmp)
|
||||
}
|
||||
}
|
||||
for i in 0 ..< len(blocks) {
|
||||
intrinsics.unaligned_store((^x86.__m128i)(raw_data(dst[i * BLOCK_SIZE:])), blocks[i])
|
||||
intrinsics.unaligned_store((^simd.u8x16)(raw_data(dst[i * BLOCK_SIZE:])), blocks[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
59
core/crypto/aes/aes_ecb_hw.odin
Normal file
59
core/crypto/aes/aes_ecb_hw.odin
Normal file
@@ -0,0 +1,59 @@
|
||||
#+build amd64,arm32
|
||||
package aes
|
||||
|
||||
import "base:intrinsics"
|
||||
import "core:crypto/_aes"
|
||||
import aes_hw "core:crypto/_aes/hw"
|
||||
import "core:simd"
|
||||
|
||||
@(private, enable_target_feature = aes_hw.TARGET_FEATURES)
|
||||
encrypt_block_hw :: proc(ctx: ^Context_Impl_Hardware, dst, src: []byte) {
|
||||
blk := intrinsics.unaligned_load((^simd.u8x16)(raw_data(src)))
|
||||
|
||||
blk = simd.bit_xor(blk, intrinsics.unaligned_load((^simd.u8x16)(&ctx._sk_exp_enc[0])))
|
||||
#unroll for i in 1 ..= 9 {
|
||||
blk = aes_hw.aesenc(blk, intrinsics.unaligned_load((^simd.u8x16)(&ctx._sk_exp_enc[i])))
|
||||
}
|
||||
switch ctx._num_rounds {
|
||||
case _aes.ROUNDS_128:
|
||||
blk = aes_hw.aesenclast(blk, intrinsics.unaligned_load((^simd.u8x16)(&ctx._sk_exp_enc[10])))
|
||||
case _aes.ROUNDS_192:
|
||||
#unroll for i in 10 ..= 11 {
|
||||
blk = aes_hw.aesenc(blk, intrinsics.unaligned_load((^simd.u8x16)(&ctx._sk_exp_enc[i])))
|
||||
}
|
||||
blk = aes_hw.aesenclast(blk, intrinsics.unaligned_load((^simd.u8x16)(&ctx._sk_exp_enc[12])))
|
||||
case _aes.ROUNDS_256:
|
||||
#unroll for i in 10 ..= 13 {
|
||||
blk = aes_hw.aesenc(blk, intrinsics.unaligned_load((^simd.u8x16)(&ctx._sk_exp_enc[i])))
|
||||
}
|
||||
blk = aes_hw.aesenclast(blk, intrinsics.unaligned_load((^simd.u8x16)(&ctx._sk_exp_enc[14])))
|
||||
}
|
||||
|
||||
intrinsics.unaligned_store((^simd.u8x16)(raw_data(dst)), blk)
|
||||
}
|
||||
|
||||
@(private, enable_target_feature = aes_hw.TARGET_FEATURES)
|
||||
decrypt_block_hw :: proc(ctx: ^Context_Impl_Hardware, dst, src: []byte) {
|
||||
blk := intrinsics.unaligned_load((^simd.u8x16)(raw_data(src)))
|
||||
|
||||
blk = simd.bit_xor(blk, intrinsics.unaligned_load((^simd.u8x16)(&ctx._sk_exp_dec[0])))
|
||||
#unroll for i in 1 ..= 9 {
|
||||
blk = aes_hw.aesdec(blk, intrinsics.unaligned_load((^simd.u8x16)(&ctx._sk_exp_dec[i])))
|
||||
}
|
||||
switch ctx._num_rounds {
|
||||
case _aes.ROUNDS_128:
|
||||
blk = aes_hw.aesdeclast(blk, intrinsics.unaligned_load((^simd.u8x16)(&ctx._sk_exp_dec[10])))
|
||||
case _aes.ROUNDS_192:
|
||||
#unroll for i in 10 ..= 11 {
|
||||
blk = aes_hw.aesdec(blk, intrinsics.unaligned_load((^simd.u8x16)(&ctx._sk_exp_dec[i])))
|
||||
}
|
||||
blk = aes_hw.aesdeclast(blk, intrinsics.unaligned_load((^simd.u8x16)(&ctx._sk_exp_dec[12])))
|
||||
case _aes.ROUNDS_256:
|
||||
#unroll for i in 10 ..= 13 {
|
||||
blk = aes_hw.aesdec(blk, intrinsics.unaligned_load((^simd.u8x16)(&ctx._sk_exp_dec[i])))
|
||||
}
|
||||
blk = aes_hw.aesdeclast(blk, intrinsics.unaligned_load((^simd.u8x16)(&ctx._sk_exp_dec[14])))
|
||||
}
|
||||
|
||||
intrinsics.unaligned_store((^simd.u8x16)(raw_data(dst)), blk)
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
#+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)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user