Merge branch 'master' into master

This commit is contained in:
WP. Yingamphol
2026-03-21 21:18:12 +07:00
committed by GitHub
893 changed files with 146325 additions and 29772 deletions

10
.gitattributes vendored
View File

@@ -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

View File

@@ -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:

View File

@@ -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
View File

@@ -279,6 +279,9 @@ demo.bin
libLLVM*.so*
*.a
# WASM
*.wasm
# shared collection
shared/

View File

@@ -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) ---

View File

@@ -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
}
}

View File

@@ -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) {

View File

@@ -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 {

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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) ---
}
}

View 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()
}

View File

@@ -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
}

View File

@@ -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.

Binary file not shown.

View File

@@ -94,6 +94,7 @@ if %release_mode% EQU 0 ( rem Debug
set compiler_warnings= ^
-W4 -WX ^
-wd4100 -wd4101 -wd4127 -wd4146 ^
-wd4324 ^
-wd4505 ^
-wd4456 -wd4457

View File

@@ -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

View File

@@ -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^ = {}

View File

@@ -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
}

View File

@@ -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^ = {}

View File

@@ -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

View File

@@ -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).

View File

@@ -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)) {

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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) {

View File

@@ -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
}

View File

@@ -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.

View File

@@ -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.

View 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

View 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
}

View 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,
}

View 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))
}
}
}

View 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)
}

View 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)
}

View 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

View File

@@ -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,

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View 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
View 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}

View File

@@ -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))
}

View File

@@ -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

View File

@@ -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
}

View 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)
}

View File

@@ -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"

View 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)
}

View 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))
}

View 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))
}

View 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")
}

View File

@@ -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)
}

View File

@@ -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))
}

View File

@@ -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

View File

@@ -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")
}
}

View File

@@ -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)

View File

@@ -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 {

View File

@@ -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")

View 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
}
}

View File

@@ -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

View File

@@ -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)
}

View 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)
}
}

File diff suppressed because it is too large Load Diff

View 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)))
}

View File

@@ -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
}

View File

@@ -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" (

View 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)
}

View 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
}

View 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)
}

File diff suppressed because it is too large Load Diff

View File

@@ -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) {

View File

@@ -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) {

View 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})
}

View 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
}

View 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})
}

File diff suppressed because it is too large Load Diff

View File

@@ -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) {

View 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
}

View 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)))
}

View 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)
}

View 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
}

View 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)))
}

View 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))
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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)))
}

View File

@@ -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.

View File

@@ -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.

View File

@@ -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
}
}

View File

@@ -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^))
}

View 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^))
}

View File

@@ -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

View File

@@ -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^))
}

View File

@@ -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)

View File

@@ -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])
}
}
}

View 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)
}

View File

@@ -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