mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-11 10:08:15 +00:00
Merge branch 'master' into macharena
This commit is contained in:
+44
-11
@@ -6,7 +6,7 @@ jobs:
|
|||||||
name: NetBSD Build, Check, and Test
|
name: NetBSD Build, Check, and Test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
PKGSRC_BRANCH: 2024Q2
|
PKGSRC_BRANCH: 2024Q3
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Build, Check, and Test
|
- name: Build, Check, and Test
|
||||||
@@ -32,6 +32,8 @@ jobs:
|
|||||||
gmake -C vendor/miniaudio/src
|
gmake -C vendor/miniaudio/src
|
||||||
./odin check examples/all -vet -strict-style -disallow-do -target:netbsd_amd64
|
./odin check examples/all -vet -strict-style -disallow-do -target:netbsd_amd64
|
||||||
./odin check examples/all -vet -strict-style -disallow-do -target:netbsd_arm64
|
./odin check examples/all -vet -strict-style -disallow-do -target:netbsd_arm64
|
||||||
|
./odin check vendor/sdl3 -vet -strict-style -disallow-do -target:netbsd_amd64 -no-entry-point
|
||||||
|
./odin check vendor/sdl3 -vet -strict-style -disallow-do -target:netbsd_arm64 -no-entry-point
|
||||||
./odin test tests/core/normal.odin -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
./odin test tests/core/normal.odin -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||||
./odin test tests/core/speed.odin -file -all-packages -vet -strict-style -disallow-do -o:speed -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
./odin test tests/core/speed.odin -file -all-packages -vet -strict-style -disallow-do -o:speed -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||||
./odin test tests/vendor -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
./odin test tests/vendor -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||||
@@ -50,7 +52,7 @@ jobs:
|
|||||||
usesh: true
|
usesh: true
|
||||||
copyback: false
|
copyback: false
|
||||||
prepare: |
|
prepare: |
|
||||||
pkg install -y gmake git bash python3 libxml2 llvm17
|
pkg install -y gmake git bash python3 libxml2 llvm18
|
||||||
run: |
|
run: |
|
||||||
# `set -e` is needed for test failures to register. https://github.com/vmactions/freebsd-vm/issues/72
|
# `set -e` is needed for test failures to register. https://github.com/vmactions/freebsd-vm/issues/72
|
||||||
set -e -x
|
set -e -x
|
||||||
@@ -62,6 +64,7 @@ jobs:
|
|||||||
gmake -C vendor/cgltf/src
|
gmake -C vendor/cgltf/src
|
||||||
gmake -C vendor/miniaudio/src
|
gmake -C vendor/miniaudio/src
|
||||||
./odin check examples/all -vet -strict-style -disallow-do -target:freebsd_amd64
|
./odin check examples/all -vet -strict-style -disallow-do -target:freebsd_amd64
|
||||||
|
./odin check vendor/sdl3 -vet -strict-style -disallow-do -target:freebsd_amd64 -no-entry-point
|
||||||
./odin test tests/core/normal.odin -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
./odin test tests/core/normal.odin -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||||
./odin test tests/core/speed.odin -file -all-packages -vet -strict-style -disallow-do -o:speed -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
./odin test tests/core/speed.odin -file -all-packages -vet -strict-style -disallow-do -o:speed -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||||
./odin test tests/vendor -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
./odin test tests/vendor -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||||
@@ -84,20 +87,20 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
wget https://apt.llvm.org/llvm.sh
|
wget https://apt.llvm.org/llvm.sh
|
||||||
chmod +x llvm.sh
|
chmod +x llvm.sh
|
||||||
sudo ./llvm.sh 17
|
sudo ./llvm.sh 20
|
||||||
echo "/usr/lib/llvm-17/bin" >> $GITHUB_PATH
|
echo "/usr/lib/llvm-20/bin" >> $GITHUB_PATH
|
||||||
|
|
||||||
- name: Download LLVM (MacOS Intel)
|
- name: Download LLVM (MacOS Intel)
|
||||||
if: matrix.os == 'macos-13'
|
if: matrix.os == 'macos-13'
|
||||||
run: |
|
run: |
|
||||||
brew install llvm@17 lua@5.4
|
brew update
|
||||||
echo "/usr/local/opt/llvm@17/bin" >> $GITHUB_PATH
|
brew install llvm@20 lua@5.4 lld
|
||||||
|
|
||||||
- name: Download LLVM (MacOS ARM)
|
- name: Download LLVM (MacOS ARM)
|
||||||
if: matrix.os == 'macos-14'
|
if: matrix.os == 'macos-14'
|
||||||
run: |
|
run: |
|
||||||
brew install llvm@17 wasmtime lua@5.4
|
brew update
|
||||||
echo "/opt/homebrew/opt/llvm@17/bin" >> $GITHUB_PATH
|
brew install llvm@20 wasmtime lua@5.4 lld
|
||||||
|
|
||||||
- name: Build Odin
|
- name: Build Odin
|
||||||
run: ./build_odin.sh release
|
run: ./build_odin.sh release
|
||||||
@@ -117,7 +120,9 @@ jobs:
|
|||||||
- name: Odin run -debug
|
- name: Odin run -debug
|
||||||
run: ./odin run examples/demo -debug
|
run: ./odin run examples/demo -debug
|
||||||
- name: Odin check examples/all
|
- name: Odin check examples/all
|
||||||
run: ./odin check examples/all -strict-style
|
run: ./odin check examples/all -strict-style -vet -disallow-do
|
||||||
|
- name: Odin check vendor/sdl3
|
||||||
|
run: ./odin check vendor/sdl3 -strict-style -vet -disallow-do -no-entry-point
|
||||||
- name: Normal Core library tests
|
- name: Normal Core library tests
|
||||||
run: ./odin test tests/core/normal.odin -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
run: ./odin test tests/core/normal.odin -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||||
- name: Optimized Core library tests
|
- name: Optimized Core library tests
|
||||||
@@ -146,9 +151,23 @@ jobs:
|
|||||||
run: ./odin check examples/all -vet -strict-style -disallow-do -target:openbsd_amd64
|
run: ./odin check examples/all -vet -strict-style -disallow-do -target:openbsd_amd64
|
||||||
if: matrix.os == 'ubuntu-latest'
|
if: matrix.os == 'ubuntu-latest'
|
||||||
|
|
||||||
|
- name: Odin check vendor/sdl3 for Linux i386
|
||||||
|
run: ./odin check vendor/sdl3 -vet -strict-style -disallow-do -no-entry-point -target:linux_i386
|
||||||
|
if: matrix.os == 'ubuntu-latest'
|
||||||
|
- name: Odin check vendor/sdl3 for Linux arm64
|
||||||
|
run: ./odin check vendor/sdl3 -vet -strict-style -disallow-do -no-entry-point -target:linux_arm64
|
||||||
|
if: matrix.os == 'ubuntu-latest'
|
||||||
|
- name: Odin check vendor/sdl3 for FreeBSD amd64
|
||||||
|
run: ./odin check vendor/sdl3 -vet -strict-style -disallow-do -no-entry-point -target:freebsd_amd64
|
||||||
|
if: matrix.os == 'ubuntu-latest'
|
||||||
|
- name: Odin check vendor/sdl3 for OpenBSD amd64
|
||||||
|
run: ./odin check vendor/sdl3 -vet -strict-style -disallow-do -no-entry-point -target:openbsd_amd64
|
||||||
|
if: matrix.os == 'ubuntu-latest'
|
||||||
|
|
||||||
|
|
||||||
- name: Run demo on WASI WASM32
|
- name: Run demo on WASI WASM32
|
||||||
run: |
|
run: |
|
||||||
./odin build examples/demo -target:wasi_wasm32 -vet -strict-style -disallow-do -out:demo.wasm
|
./odin build examples/demo -target:wasi_wasm32 -vet -strict-style -disallow-do -out:demo
|
||||||
wasmtime ./demo.wasm
|
wasmtime ./demo.wasm
|
||||||
if: matrix.os == 'macos-14'
|
if: matrix.os == 'macos-14'
|
||||||
|
|
||||||
@@ -187,6 +206,11 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||||
odin check examples/all -vet -strict-style -disallow-do
|
odin check examples/all -vet -strict-style -disallow-do
|
||||||
|
- name: Odin check vendor/sdl3
|
||||||
|
shell: cmd
|
||||||
|
run: |
|
||||||
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||||
|
odin check vendor/sdl3 -vet -strict-style -disallow-do -no-entry-point
|
||||||
- name: Core library tests
|
- name: Core library tests
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
@@ -208,6 +232,12 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||||
odin test tests/internal -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
odin test tests/internal -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||||
|
- name: Check issues
|
||||||
|
shell: cmd
|
||||||
|
run: |
|
||||||
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||||
|
cd tests/issues
|
||||||
|
call run.bat
|
||||||
- name: Check benchmarks
|
- name: Check benchmarks
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
@@ -260,9 +290,12 @@ jobs:
|
|||||||
make -C vendor/cgltf/src
|
make -C vendor/cgltf/src
|
||||||
make -C vendor/miniaudio/src
|
make -C vendor/miniaudio/src
|
||||||
|
|
||||||
- name: Odin check
|
- name: Odin check examples/all
|
||||||
run: ./odin check examples/all -target:linux_riscv64 -vet -strict-style -disallow-do
|
run: ./odin check examples/all -target:linux_riscv64 -vet -strict-style -disallow-do
|
||||||
|
|
||||||
|
- name: Odin check vendor/sdl3
|
||||||
|
run: ./odin check vendor/sdl3 -target:linux_riscv64 -vet -strict-style -disallow-do -no-entry-point
|
||||||
|
|
||||||
- name: Install riscv64 toolchain and qemu
|
- name: Install riscv64 toolchain and qemu
|
||||||
run: sudo apt-get install -y qemu-user qemu-user-static gcc-12-riscv64-linux-gnu libc6-riscv64-cross
|
run: sudo apt-get install -y qemu-user qemu-user-static gcc-12-riscv64-linux-gnu libc6-riscv64-cross
|
||||||
|
|
||||||
|
|||||||
@@ -49,12 +49,12 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: jirutka/setup-alpine@v1
|
- uses: jirutka/setup-alpine@v1
|
||||||
with:
|
with:
|
||||||
branch: v3.20
|
branch: edge
|
||||||
- name: (Linux) Download LLVM
|
- name: (Linux) Download LLVM
|
||||||
run: |
|
run: |
|
||||||
apk add --no-cache \
|
apk add --no-cache \
|
||||||
musl-dev llvm18-dev clang18 git mold lz4 \
|
musl-dev llvm20-dev clang20 git mold lz4 \
|
||||||
libxml2-static llvm18-static zlib-static zstd-static \
|
libxml2-static llvm20-static zlib-static zstd-static \
|
||||||
make
|
make
|
||||||
shell: alpine.sh --root {0}
|
shell: alpine.sh --root {0}
|
||||||
- name: build odin
|
- name: build odin
|
||||||
@@ -93,8 +93,9 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Download LLVM and setup PATH
|
- name: Download LLVM and setup PATH
|
||||||
run: |
|
run: |
|
||||||
brew install llvm@18 dylibbundler
|
brew update
|
||||||
echo "/usr/local/opt/llvm@18/bin" >> $GITHUB_PATH
|
brew install llvm@20 dylibbundler lld
|
||||||
|
|
||||||
- name: build odin
|
- name: build odin
|
||||||
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
|
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
|
||||||
# not link with libunwind bundled with LLVM but link with libunwind on the system.
|
# not link with libunwind bundled with LLVM but link with libunwind on the system.
|
||||||
@@ -130,8 +131,9 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Download LLVM and setup PATH
|
- name: Download LLVM and setup PATH
|
||||||
run: |
|
run: |
|
||||||
brew install llvm@18 dylibbundler
|
brew update
|
||||||
echo "/opt/homebrew/opt/llvm@18/bin" >> $GITHUB_PATH
|
brew install llvm@20 dylibbundler lld
|
||||||
|
|
||||||
- name: build odin
|
- name: build odin
|
||||||
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
|
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
|
||||||
# not link with libunwind bundled with LLVM but link with libunwind on the system.
|
# not link with libunwind bundled with LLVM but link with libunwind on the system.
|
||||||
|
|||||||
BIN
Binary file not shown.
@@ -15,7 +15,7 @@
|
|||||||
<img src="https://img.shields.io/discord/568138951836172421?logo=discord">
|
<img src="https://img.shields.io/discord/568138951836172421?logo=discord">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/odin-lang/odin/actions">
|
<a href="https://github.com/odin-lang/odin/actions">
|
||||||
<img src="https://github.com/odin-lang/odin/workflows/CI/badge.svg?branch=master&event=push">
|
<img src="https://github.com/odin-lang/odin/actions/workflows/ci.yml/badge.svg?branch=master&event=push">
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -76,6 +76,10 @@ Answers to common questions about Odin.
|
|||||||
|
|
||||||
Documentation for all the official packages part of the [core](https://pkg.odin-lang.org/core/) and [vendor](https://pkg.odin-lang.org/vendor/) library collections.
|
Documentation for all the official packages part of the [core](https://pkg.odin-lang.org/core/) and [vendor](https://pkg.odin-lang.org/vendor/) library collections.
|
||||||
|
|
||||||
|
#### [Examples](https://github.com/odin-lang/examples)
|
||||||
|
|
||||||
|
Examples on how to write idiomatic Odin code. Shows how to accomplish specific tasks in Odin, as well as how to use packages from `core` and `vendor`.
|
||||||
|
|
||||||
#### [Odin Documentation](https://odin-lang.org/docs/)
|
#### [Odin Documentation](https://odin-lang.org/docs/)
|
||||||
|
|
||||||
Documentation for the Odin language itself.
|
Documentation for the Odin language itself.
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
// This is purely for documentation
|
// This is purely for documentation
|
||||||
package builtin
|
package builtin
|
||||||
|
|
||||||
|
import "base:runtime"
|
||||||
|
|
||||||
nil :: nil
|
nil :: nil
|
||||||
false :: 0!=0
|
false :: 0!=0
|
||||||
true :: 0==0
|
true :: 0==0
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
#+build ignore
|
#+build ignore
|
||||||
package intrinsics
|
package intrinsics
|
||||||
|
|
||||||
|
import "base:runtime"
|
||||||
|
|
||||||
// Package-Related
|
// Package-Related
|
||||||
is_package_imported :: proc(package_name: string) -> bool ---
|
is_package_imported :: proc(package_name: string) -> bool ---
|
||||||
|
|
||||||
@@ -72,7 +74,7 @@ prefetch_write_instruction :: proc(address: rawptr, #const locality: i32 /* 0..=
|
|||||||
prefetch_write_data :: proc(address: rawptr, #const locality: i32 /* 0..=3 */) ---
|
prefetch_write_data :: proc(address: rawptr, #const locality: i32 /* 0..=3 */) ---
|
||||||
|
|
||||||
// Compiler Hints
|
// Compiler Hints
|
||||||
expect :: proc(val, expected_val: T) -> T ---
|
expect :: proc(val, expected_val: $T) -> T ---
|
||||||
|
|
||||||
// Linux and Darwin Only
|
// Linux and Darwin Only
|
||||||
syscall :: proc(id: uintptr, args: ..uintptr) -> uintptr ---
|
syscall :: proc(id: uintptr, args: ..uintptr) -> uintptr ---
|
||||||
@@ -219,7 +221,7 @@ type_map_cell_info :: proc($T: typeid) -> ^runtime.Map_Cell_Info ---
|
|||||||
type_convert_variants_to_pointers :: proc($T: typeid) -> typeid where type_is_union(T) ---
|
type_convert_variants_to_pointers :: proc($T: typeid) -> typeid where type_is_union(T) ---
|
||||||
type_merge :: proc($U, $V: typeid) -> typeid where type_is_union(U), type_is_union(V) ---
|
type_merge :: proc($U, $V: typeid) -> typeid where type_is_union(U), type_is_union(V) ---
|
||||||
|
|
||||||
type_has_shared_fields :: proc($U, $V: typeid) -> bool typeid where type_is_struct(U), type_is_struct(V) ---
|
type_has_shared_fields :: proc($U, $V: typeid) -> bool where type_is_struct(U), type_is_struct(V) ---
|
||||||
|
|
||||||
constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
|
constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
|
||||||
|
|
||||||
@@ -283,6 +285,9 @@ simd_reduce_xor :: proc(a: #simd[N]T) -> T where type_is_integer(T) || t
|
|||||||
simd_reduce_any :: proc(a: #simd[N]T) -> T where type_is_boolean(T) ---
|
simd_reduce_any :: proc(a: #simd[N]T) -> T where type_is_boolean(T) ---
|
||||||
simd_reduce_all :: proc(a: #simd[N]T) -> T where type_is_boolean(T) ---
|
simd_reduce_all :: proc(a: #simd[N]T) -> T where type_is_boolean(T) ---
|
||||||
|
|
||||||
|
simd_extract_lsbs :: proc(a: #simd[N]T) -> bit_set[0..<N] where type_is_integer(T) || type_is_boolean(T) ---
|
||||||
|
simd_extract_msbs :: proc(a: #simd[N]T) -> bit_set[0..<N] where type_is_integer(T) || type_is_boolean(T) ---
|
||||||
|
|
||||||
|
|
||||||
simd_gather :: proc(ptr: #simd[N]rawptr, val: #simd[N]T, mask: #simd[N]U) -> #simd[N]T where type_is_integer(U) || type_is_boolean(U) ---
|
simd_gather :: proc(ptr: #simd[N]rawptr, val: #simd[N]T, mask: #simd[N]U) -> #simd[N]T where type_is_integer(U) || type_is_boolean(U) ---
|
||||||
simd_scatter :: proc(ptr: #simd[N]rawptr, val: #simd[N]T, mask: #simd[N]U) where type_is_integer(U) || type_is_boolean(U) ---
|
simd_scatter :: proc(ptr: #simd[N]rawptr, val: #simd[N]T, mask: #simd[N]U) where type_is_integer(U) || type_is_boolean(U) ---
|
||||||
|
|||||||
+17
-48
@@ -110,7 +110,6 @@ Type_Info_Parameters :: struct { // Only used for procedures parameters and resu
|
|||||||
types: []^Type_Info,
|
types: []^Type_Info,
|
||||||
names: []string,
|
names: []string,
|
||||||
}
|
}
|
||||||
Type_Info_Tuple :: Type_Info_Parameters // Will be removed eventually
|
|
||||||
|
|
||||||
Type_Info_Struct_Flags :: distinct bit_set[Type_Info_Struct_Flag; u8]
|
Type_Info_Struct_Flags :: distinct bit_set[Type_Info_Struct_Flag; u8]
|
||||||
Type_Info_Struct_Flag :: enum u8 {
|
Type_Info_Struct_Flag :: enum u8 {
|
||||||
@@ -239,47 +238,6 @@ Type_Info :: struct {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE(bill): This must match the compiler's
|
|
||||||
Typeid_Kind :: enum u8 {
|
|
||||||
Invalid,
|
|
||||||
Integer,
|
|
||||||
Rune,
|
|
||||||
Float,
|
|
||||||
Complex,
|
|
||||||
Quaternion,
|
|
||||||
String,
|
|
||||||
Boolean,
|
|
||||||
Any,
|
|
||||||
Type_Id,
|
|
||||||
Pointer,
|
|
||||||
Multi_Pointer,
|
|
||||||
Procedure,
|
|
||||||
Array,
|
|
||||||
Enumerated_Array,
|
|
||||||
Dynamic_Array,
|
|
||||||
Slice,
|
|
||||||
Tuple,
|
|
||||||
Struct,
|
|
||||||
Union,
|
|
||||||
Enum,
|
|
||||||
Map,
|
|
||||||
Bit_Set,
|
|
||||||
Simd_Vector,
|
|
||||||
Matrix,
|
|
||||||
Soa_Pointer,
|
|
||||||
Bit_Field,
|
|
||||||
}
|
|
||||||
#assert(len(Typeid_Kind) < 32)
|
|
||||||
|
|
||||||
Typeid_Bit_Field :: bit_field uintptr {
|
|
||||||
index: uintptr | 8*size_of(uintptr) - 8,
|
|
||||||
kind: Typeid_Kind | 5, // Typeid_Kind
|
|
||||||
named: bool | 1,
|
|
||||||
special: bool | 1, // signed, cstring, etc
|
|
||||||
reserved: bool | 1,
|
|
||||||
}
|
|
||||||
#assert(size_of(Typeid_Bit_Field) == size_of(uintptr))
|
|
||||||
|
|
||||||
// NOTE(bill): only the ones that are needed (not all types)
|
// NOTE(bill): only the ones that are needed (not all types)
|
||||||
// This will be set by the compiler
|
// This will be set by the compiler
|
||||||
type_table: []^Type_Info
|
type_table: []^Type_Info
|
||||||
@@ -483,10 +441,14 @@ Raw_Any :: struct {
|
|||||||
data: rawptr,
|
data: rawptr,
|
||||||
id: typeid,
|
id: typeid,
|
||||||
}
|
}
|
||||||
|
when !ODIN_NO_RTTI {
|
||||||
|
#assert(size_of(Raw_Any) == size_of(any))
|
||||||
|
}
|
||||||
|
|
||||||
Raw_Cstring :: struct {
|
Raw_Cstring :: struct {
|
||||||
data: [^]byte,
|
data: [^]byte,
|
||||||
}
|
}
|
||||||
|
#assert(size_of(Raw_Cstring) == size_of(cstring))
|
||||||
|
|
||||||
Raw_Soa_Pointer :: struct {
|
Raw_Soa_Pointer :: struct {
|
||||||
data: rawptr,
|
data: rawptr,
|
||||||
@@ -596,10 +558,14 @@ ALL_ODIN_OS_TYPES :: Odin_OS_Types{
|
|||||||
Odin_Platform_Subtarget_Type :: enum int {
|
Odin_Platform_Subtarget_Type :: enum int {
|
||||||
Default,
|
Default,
|
||||||
iOS,
|
iOS,
|
||||||
|
Android,
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
Odin_Platform_Subtarget_Type :: type_of(ODIN_PLATFORM_SUBTARGET)
|
Odin_Platform_Subtarget_Type :: type_of(ODIN_PLATFORM_SUBTARGET)
|
||||||
|
|
||||||
|
Odin_Platform_Subtarget_Types :: bit_set[Odin_Platform_Subtarget_Type]
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// Defined internally by the compiler
|
// Defined internally by the compiler
|
||||||
Odin_Sanitizer_Flag :: enum u32 {
|
Odin_Sanitizer_Flag :: enum u32 {
|
||||||
@@ -686,13 +652,16 @@ type_info_core :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
|
|||||||
type_info_base_without_enum :: type_info_core
|
type_info_base_without_enum :: type_info_core
|
||||||
|
|
||||||
__type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info #no_bounds_check {
|
__type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info #no_bounds_check {
|
||||||
MASK :: 1<<(8*size_of(typeid) - 8) - 1
|
n := u64(len(type_table))
|
||||||
data := transmute(uintptr)id
|
i := transmute(u64)id % n
|
||||||
n := int(data & MASK)
|
for _ in 0..<n {
|
||||||
if n < 0 || n >= len(type_table) {
|
ptr := type_table[i]
|
||||||
n = 0
|
if ptr != nil && ptr.id == id {
|
||||||
|
return ptr
|
||||||
|
}
|
||||||
|
i = i+1 if i+1 < n else 0
|
||||||
}
|
}
|
||||||
return type_table[n]
|
return type_table[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
when !ODIN_NO_RTTI {
|
when !ODIN_NO_RTTI {
|
||||||
|
|||||||
@@ -826,10 +826,12 @@ _resize_dynamic_array :: #force_inline proc(a: ^Raw_Dynamic_Array, size_of_elem,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if should_zero && a.len < length {
|
||||||
|
num_reused := min(a.cap, length) - a.len
|
||||||
|
intrinsics.mem_zero(([^]byte)(a.data)[a.len*size_of_elem:], num_reused*size_of_elem)
|
||||||
|
}
|
||||||
|
|
||||||
if length <= a.cap {
|
if length <= a.cap {
|
||||||
if should_zero && a.len < length {
|
|
||||||
intrinsics.mem_zero(([^]byte)(a.data)[a.len*size_of_elem:], (length-a.len)*size_of_elem)
|
|
||||||
}
|
|
||||||
a.len = max(length, 0)
|
a.len = max(length, 0)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -936,6 +938,32 @@ map_upsert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Retrieves a pointer to the key and value for a possibly just inserted entry into the map.
|
||||||
|
|
||||||
|
If the `key` was not in the map `m`, an entry is inserted with the zero value and `just_inserted` will be `true`.
|
||||||
|
Otherwise the existing entry is left untouched and pointers to its key and value are returned.
|
||||||
|
|
||||||
|
If the map has to grow in order to insert the entry and the allocation fails, `err` is set and returned.
|
||||||
|
|
||||||
|
If `err` is `nil`, `key_ptr` and `value_ptr` are valid pointers and will not be `nil`.
|
||||||
|
|
||||||
|
WARN: User modification of the key pointed at by `key_ptr` should only be done if the new key is equal to (in hash) the old key.
|
||||||
|
If that is not the case you will corrupt the map.
|
||||||
|
*/
|
||||||
|
@(builtin, require_results)
|
||||||
|
map_entry :: proc(m: ^$T/map[$K]$V, key: K, loc := #caller_location) -> (key_ptr: ^K, value_ptr: ^V, just_inserted: bool, err: Allocator_Error) {
|
||||||
|
key := key
|
||||||
|
zero: V
|
||||||
|
|
||||||
|
_key_ptr, _value_ptr: rawptr
|
||||||
|
_key_ptr, _value_ptr, just_inserted, err = __dynamic_map_entry((^Raw_Map)(m), map_info(T), &key, &zero, loc)
|
||||||
|
|
||||||
|
key_ptr = (^K)(_key_ptr)
|
||||||
|
value_ptr = (^V)(_value_ptr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
card :: proc "contextless" (s: $S/bit_set[$E; $U]) -> int {
|
card :: proc "contextless" (s: $S/bit_set[$E; $U]) -> int {
|
||||||
@@ -964,6 +992,24 @@ assert :: proc(condition: bool, message := #caller_expression(condition), loc :=
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Evaluates the condition and aborts the program iff the condition is
|
||||||
|
// false. This routine ignores `ODIN_DISABLE_ASSERT`, and will always
|
||||||
|
// execute.
|
||||||
|
@builtin
|
||||||
|
ensure :: proc(condition: bool, message := #caller_expression(condition), loc := #caller_location) {
|
||||||
|
if !condition {
|
||||||
|
@(cold)
|
||||||
|
internal :: proc(message: string, loc: Source_Code_Location) {
|
||||||
|
p := context.assertion_failure_proc
|
||||||
|
if p == nil {
|
||||||
|
p = default_assertion_failure_proc
|
||||||
|
}
|
||||||
|
p("unsatisfied ensure", message, loc)
|
||||||
|
}
|
||||||
|
internal(message, loc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
panic :: proc(message: string, loc := #caller_location) -> ! {
|
panic :: proc(message: string, loc := #caller_location) -> ! {
|
||||||
p := context.assertion_failure_proc
|
p := context.assertion_failure_proc
|
||||||
@@ -999,6 +1045,17 @@ assert_contextless :: proc "contextless" (condition: bool, message := #caller_ex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
ensure_contextless :: proc "contextless" (condition: bool, message := #caller_expression(condition), loc := #caller_location) {
|
||||||
|
if !condition {
|
||||||
|
@(cold)
|
||||||
|
internal :: proc "contextless" (message: string, loc: Source_Code_Location) {
|
||||||
|
default_assertion_contextless_failure_proc("unsatisfied ensure", message, loc)
|
||||||
|
}
|
||||||
|
internal(message, loc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
panic_contextless :: proc "contextless" (message: string, loc := #caller_location) -> ! {
|
panic_contextless :: proc "contextless" (message: string, loc := #caller_location) -> ! {
|
||||||
default_assertion_contextless_failure_proc("panic", message, loc)
|
default_assertion_contextless_failure_proc("panic", message, loc)
|
||||||
|
|||||||
@@ -142,6 +142,7 @@ make_soa_slice :: proc($T: typeid/#soa[]$E, #any_int length: int, allocator := c
|
|||||||
@(builtin, require_results)
|
@(builtin, require_results)
|
||||||
make_soa_dynamic_array :: proc($T: typeid/#soa[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
make_soa_dynamic_array :: proc($T: typeid/#soa[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
||||||
context.allocator = allocator
|
context.allocator = allocator
|
||||||
|
array.allocator = allocator
|
||||||
reserve_soa(&array, 0, loc) or_return
|
reserve_soa(&array, 0, loc) or_return
|
||||||
return array, nil
|
return array, nil
|
||||||
}
|
}
|
||||||
@@ -149,6 +150,7 @@ make_soa_dynamic_array :: proc($T: typeid/#soa[dynamic]$E, allocator := context.
|
|||||||
@(builtin, require_results)
|
@(builtin, require_results)
|
||||||
make_soa_dynamic_array_len :: proc($T: typeid/#soa[dynamic]$E, #any_int length: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
make_soa_dynamic_array_len :: proc($T: typeid/#soa[dynamic]$E, #any_int length: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
||||||
context.allocator = allocator
|
context.allocator = allocator
|
||||||
|
array.allocator = allocator
|
||||||
resize_soa(&array, length, loc) or_return
|
resize_soa(&array, length, loc) or_return
|
||||||
return array, nil
|
return array, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,12 +105,14 @@ arena_alloc :: proc(arena: ^Arena, size, alignment: uint, loc := #caller_locatio
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
needed := align_forward_uint(size, alignment)
|
prev_used := 0 if arena.curr_block == nil else arena.curr_block.used
|
||||||
if arena.curr_block == nil || (safe_add(arena.curr_block.used, needed) or_else 0) > arena.curr_block.capacity {
|
data, err = alloc_from_memory_block(arena.curr_block, size, alignment)
|
||||||
|
if err == .Out_Of_Memory {
|
||||||
if arena.minimum_block_size == 0 {
|
if arena.minimum_block_size == 0 {
|
||||||
arena.minimum_block_size = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE
|
arena.minimum_block_size = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
needed := align_forward_uint(size, alignment)
|
||||||
block_size := max(needed, arena.minimum_block_size)
|
block_size := max(needed, arena.minimum_block_size)
|
||||||
|
|
||||||
if arena.backing_allocator.procedure == nil {
|
if arena.backing_allocator.procedure == nil {
|
||||||
@@ -121,10 +123,9 @@ arena_alloc :: proc(arena: ^Arena, size, alignment: uint, loc := #caller_locatio
|
|||||||
new_block.prev = arena.curr_block
|
new_block.prev = arena.curr_block
|
||||||
arena.curr_block = new_block
|
arena.curr_block = new_block
|
||||||
arena.total_capacity += new_block.capacity
|
arena.total_capacity += new_block.capacity
|
||||||
|
prev_used = 0
|
||||||
|
data, err = alloc_from_memory_block(arena.curr_block, size, alignment)
|
||||||
}
|
}
|
||||||
|
|
||||||
prev_used := arena.curr_block.used
|
|
||||||
data, err = alloc_from_memory_block(arena.curr_block, size, alignment)
|
|
||||||
arena.total_used += arena.curr_block.used - prev_used
|
arena.total_used += arena.curr_block.used - prev_used
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -210,10 +211,24 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
case size == 0:
|
case size == 0:
|
||||||
err = .Mode_Not_Implemented
|
err = .Mode_Not_Implemented
|
||||||
return
|
return
|
||||||
case (uintptr(old_data) & uintptr(alignment-1) == 0) && size < old_size:
|
case uintptr(old_data) & uintptr(alignment-1) == 0:
|
||||||
// shrink data in-place
|
if size < old_size {
|
||||||
data = old_data[:size]
|
// shrink data in-place
|
||||||
return
|
data = old_data[:size]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if block := arena.curr_block; block != nil {
|
||||||
|
start := uint(uintptr(old_memory)) - uint(uintptr(block.base))
|
||||||
|
old_end := start + old_size
|
||||||
|
new_end := start + size
|
||||||
|
if start < old_end && old_end == block.used && new_end <= block.capacity {
|
||||||
|
// grow data in-place, adjusting next allocation
|
||||||
|
block.used = uint(new_end)
|
||||||
|
data = block.base[start:new_end]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
new_memory := arena_alloc(arena, size, alignment, location) or_return
|
new_memory := arena_alloc(arena, size, alignment, location) or_return
|
||||||
@@ -282,9 +297,10 @@ arena_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) {
|
|||||||
|
|
||||||
if block := arena.curr_block; block != nil {
|
if block := arena.curr_block; block != nil {
|
||||||
assert(block.used >= temp.used, "out of order use of arena_temp_end", loc)
|
assert(block.used >= temp.used, "out of order use of arena_temp_end", loc)
|
||||||
amount_to_zero := min(block.used-temp.used, block.capacity-block.used)
|
amount_to_zero := block.used-temp.used
|
||||||
intrinsics.mem_zero(block.base[temp.used:], amount_to_zero)
|
intrinsics.mem_zero(block.base[temp.used:], amount_to_zero)
|
||||||
block.used = temp.used
|
block.used = temp.used
|
||||||
|
arena.total_used -= amount_to_zero
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -158,21 +158,21 @@ map_cell_index_static :: #force_inline proc "contextless" (cells: [^]Map_Cell($T
|
|||||||
} else when (N & (N - 1)) == 0 && N <= 8*size_of(uintptr) {
|
} else when (N & (N - 1)) == 0 && N <= 8*size_of(uintptr) {
|
||||||
// Likely case, N is a power of two because T is a power of two.
|
// Likely case, N is a power of two because T is a power of two.
|
||||||
|
|
||||||
|
// Unique case, no need to index data here since only one element.
|
||||||
|
when N == 1 {
|
||||||
|
return &cells[index].data[0]
|
||||||
|
}
|
||||||
|
|
||||||
// Compute the integer log 2 of N, this is the shift amount to index the
|
// Compute the integer log 2 of N, this is the shift amount to index the
|
||||||
// correct cell. Odin's intrinsics.count_leading_zeros does not produce a
|
// correct cell. Odin's intrinsics.count_leading_zeros does not produce a
|
||||||
// constant, hence this approach. We only need to check up to N = 64.
|
// constant, hence this approach. We only need to check up to N = 64.
|
||||||
SHIFT :: 1 when N < 2 else
|
SHIFT :: 1 when N == 2 else
|
||||||
2 when N < 4 else
|
2 when N == 4 else
|
||||||
3 when N < 8 else
|
3 when N == 8 else
|
||||||
4 when N < 16 else
|
4 when N == 16 else
|
||||||
5 when N < 32 else 6
|
5 when N == 32 else 6
|
||||||
#assert(SHIFT <= MAP_CACHE_LINE_LOG2)
|
#assert(SHIFT <= MAP_CACHE_LINE_LOG2)
|
||||||
// Unique case, no need to index data here since only one element.
|
return &cells[index >> SHIFT].data[index & (N - 1)]
|
||||||
when N == 1 {
|
|
||||||
return &cells[index >> SHIFT].data[0]
|
|
||||||
} else {
|
|
||||||
return &cells[index >> SHIFT].data[index & (N - 1)]
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Least likely (and worst case), we pay for a division operation but we
|
// Least likely (and worst case), we pay for a division operation but we
|
||||||
// assume the compiler does not actually generate a division. N will be in the
|
// assume the compiler does not actually generate a division. N will be in the
|
||||||
@@ -400,7 +400,7 @@ map_alloc_dynamic :: proc "odin" (info: ^Map_Info, log2_capacity: uintptr, alloc
|
|||||||
// This procedure returns the address of the just inserted value, and will
|
// This procedure returns the address of the just inserted value, and will
|
||||||
// return 'nil' if there was no room to insert the entry
|
// return 'nil' if there was no room to insert the entry
|
||||||
@(require_results)
|
@(require_results)
|
||||||
map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, h: Map_Hash, ik: uintptr, iv: uintptr) -> (result: uintptr) {
|
map_insert_hash_dynamic_with_key :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, h: Map_Hash, ik: uintptr, iv: uintptr) -> (key: uintptr, result: uintptr) {
|
||||||
h := h
|
h := h
|
||||||
pos := map_desired_position(m^, h)
|
pos := map_desired_position(m^, h)
|
||||||
distance := uintptr(0)
|
distance := uintptr(0)
|
||||||
@@ -436,7 +436,11 @@ map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^
|
|||||||
intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v), size_of_v)
|
intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v), size_of_v)
|
||||||
hs[pos] = h
|
hs[pos] = h
|
||||||
|
|
||||||
return result if result != 0 else v_dst
|
if result == 0 {
|
||||||
|
key = k_dst
|
||||||
|
result = v_dst
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if map_hash_is_deleted(element_hash) {
|
if map_hash_is_deleted(element_hash) {
|
||||||
@@ -444,13 +448,14 @@ map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^
|
|||||||
}
|
}
|
||||||
|
|
||||||
if probe_distance := map_probe_distance(m^, element_hash, pos); distance > probe_distance {
|
if probe_distance := map_probe_distance(m^, element_hash, pos); distance > probe_distance {
|
||||||
if result == 0 {
|
|
||||||
result = map_cell_index_dynamic(vs, info.vs, pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
kp := map_cell_index_dynamic(ks, info.ks, pos)
|
kp := map_cell_index_dynamic(ks, info.ks, pos)
|
||||||
vp := map_cell_index_dynamic(vs, info.vs, pos)
|
vp := map_cell_index_dynamic(vs, info.vs, pos)
|
||||||
|
|
||||||
|
if result == 0 {
|
||||||
|
key = kp
|
||||||
|
result = vp
|
||||||
|
}
|
||||||
|
|
||||||
intrinsics.mem_copy_non_overlapping(rawptr(tk), rawptr(k), size_of_k)
|
intrinsics.mem_copy_non_overlapping(rawptr(tk), rawptr(k), size_of_k)
|
||||||
intrinsics.mem_copy_non_overlapping(rawptr(k), rawptr(kp), size_of_k)
|
intrinsics.mem_copy_non_overlapping(rawptr(k), rawptr(kp), size_of_k)
|
||||||
intrinsics.mem_copy_non_overlapping(rawptr(kp), rawptr(tk), size_of_k)
|
intrinsics.mem_copy_non_overlapping(rawptr(kp), rawptr(tk), size_of_k)
|
||||||
@@ -491,7 +496,11 @@ map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^
|
|||||||
intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v), size_of_v)
|
intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v), size_of_v)
|
||||||
hs[pos] = h
|
hs[pos] = h
|
||||||
|
|
||||||
return result if result != 0 else v_dst
|
if result == 0 {
|
||||||
|
key = k_dst
|
||||||
|
result = v_dst
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
k_src := map_cell_index_dynamic(ks, info.ks, la_pos)
|
k_src := map_cell_index_dynamic(ks, info.ks, la_pos)
|
||||||
@@ -501,6 +510,7 @@ map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^
|
|||||||
if probe_distance < look_ahead {
|
if probe_distance < look_ahead {
|
||||||
// probed can be made ideal while placing saved (ending condition)
|
// probed can be made ideal while placing saved (ending condition)
|
||||||
if result == 0 {
|
if result == 0 {
|
||||||
|
key = k_dst
|
||||||
result = v_dst
|
result = v_dst
|
||||||
}
|
}
|
||||||
intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k), size_of_k)
|
intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k), size_of_k)
|
||||||
@@ -550,6 +560,7 @@ map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^
|
|||||||
} else {
|
} else {
|
||||||
// place saved, save probed
|
// place saved, save probed
|
||||||
if result == 0 {
|
if result == 0 {
|
||||||
|
key = k_dst
|
||||||
result = v_dst
|
result = v_dst
|
||||||
}
|
}
|
||||||
intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k), size_of_k)
|
intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k), size_of_k)
|
||||||
@@ -568,6 +579,12 @@ map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
|
map_insert_hash_dynamic :: #force_inline proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, h: Map_Hash, ik: uintptr, iv: uintptr) -> (result: uintptr) {
|
||||||
|
_, result = map_insert_hash_dynamic_with_key(m, info, h, ik, iv)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
map_grow_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, loc := #caller_location) -> Allocator_Error {
|
map_grow_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, loc := #caller_location) -> Allocator_Error {
|
||||||
log2_capacity := map_log2_cap(m^)
|
log2_capacity := map_log2_cap(m^)
|
||||||
@@ -941,6 +958,29 @@ __dynamic_map_set_extra :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^
|
|||||||
return nil, rawptr(result)
|
return nil, rawptr(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__dynamic_map_entry :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, key: rawptr, zero: rawptr, loc := #caller_location) -> (key_ptr: rawptr, value_ptr: rawptr, just_inserted: bool, err: Allocator_Error) {
|
||||||
|
hash := info.key_hasher(key, map_seed(m^))
|
||||||
|
|
||||||
|
if key_ptr, value_ptr = __dynamic_map_get_key_and_value(m, info, hash, key); value_ptr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
has_grown: bool
|
||||||
|
if err, has_grown = __dynamic_map_check_grow(m, info, loc); err != nil {
|
||||||
|
return
|
||||||
|
} else if has_grown {
|
||||||
|
hash = info.key_hasher(key, map_seed(m^))
|
||||||
|
}
|
||||||
|
|
||||||
|
kp, vp := map_insert_hash_dynamic_with_key(m, info, hash, uintptr(key), uintptr(zero))
|
||||||
|
key_ptr = rawptr(kp)
|
||||||
|
value_ptr = rawptr(vp)
|
||||||
|
|
||||||
|
m.len += 1
|
||||||
|
just_inserted = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// IMPORTANT: USED WITHIN THE COMPILER
|
// IMPORTANT: USED WITHIN THE COMPILER
|
||||||
@(private)
|
@(private)
|
||||||
@@ -989,3 +1029,32 @@ default_hasher_cstring :: proc "contextless" (data: rawptr, seed: uintptr) -> ui
|
|||||||
h &= HASH_MASK
|
h &= HASH_MASK
|
||||||
return uintptr(h) | uintptr(uintptr(h) == 0)
|
return uintptr(h) | uintptr(uintptr(h) == 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default_hasher_f64 :: proc "contextless" (f: f64, seed: uintptr) -> uintptr {
|
||||||
|
f := f
|
||||||
|
buf: [size_of(f)]u8
|
||||||
|
if f == 0 {
|
||||||
|
return default_hasher(&buf, seed, size_of(buf))
|
||||||
|
}
|
||||||
|
if f != f {
|
||||||
|
// TODO(bill): What should the logic be for NaNs?
|
||||||
|
return default_hasher(&f, seed, size_of(f))
|
||||||
|
}
|
||||||
|
return default_hasher(&f, seed, size_of(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
default_hasher_complex128 :: proc "contextless" (x, y: f64, seed: uintptr) -> uintptr {
|
||||||
|
seed := seed
|
||||||
|
seed = default_hasher_f64(x, seed)
|
||||||
|
seed = default_hasher_f64(y, seed)
|
||||||
|
return seed
|
||||||
|
}
|
||||||
|
|
||||||
|
default_hasher_quaternion256 :: proc "contextless" (x, y, z, w: f64, seed: uintptr) -> uintptr {
|
||||||
|
seed := seed
|
||||||
|
seed = default_hasher_f64(x, seed)
|
||||||
|
seed = default_hasher_f64(y, seed)
|
||||||
|
seed = default_hasher_f64(z, seed)
|
||||||
|
seed = default_hasher_f64(w, seed)
|
||||||
|
return seed
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ foreign libc {
|
|||||||
@(link_name="write")
|
@(link_name="write")
|
||||||
_unix_write :: proc(fd: i32, buf: rawptr, size: int) -> int ---
|
_unix_write :: proc(fd: i32, buf: rawptr, size: int) -> int ---
|
||||||
|
|
||||||
when ODIN_OS == .NetBSD {
|
when ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
|
||||||
@(link_name="__errno") __error :: proc() -> ^i32 ---
|
@(link_name="__errno") __error :: proc() -> ^i32 ---
|
||||||
} else {
|
} else {
|
||||||
__error :: proc() -> ^i32 ---
|
__error :: proc() -> ^i32 ---
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#+private
|
#+private
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
|
@(priority_index=-1e6)
|
||||||
foreign import "system:Foundation.framework"
|
foreign import "system:Foundation.framework"
|
||||||
|
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ default_random_generator_proc :: proc(data: rawptr, mode: Random_Generator_Mode,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
default_random_generator :: proc "contextless" (state: ^Default_Random_State = nil) -> Random_Generator {
|
default_random_generator :: proc "contextless" (state: ^Default_Random_State = nil) -> Random_Generator {
|
||||||
return {
|
return {
|
||||||
procedure = default_random_generator_proc,
|
procedure = default_random_generator_proc,
|
||||||
|
|||||||
@@ -0,0 +1,299 @@
|
|||||||
|
#+no-instrumentation
|
||||||
|
package sanitizer
|
||||||
|
|
||||||
|
Address_Death_Callback :: #type proc "c" (pc: rawptr, bp: rawptr, sp: rawptr, addr: rawptr, is_write: i32, access_size: uint)
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
ASAN_ENABLED :: .Address in ODIN_SANITIZER_FLAGS
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
@(default_calling_convention="system")
|
||||||
|
foreign {
|
||||||
|
__asan_poison_memory_region :: proc(address: rawptr, size: uint) ---
|
||||||
|
__asan_unpoison_memory_region :: proc(address: rawptr, size: uint) ---
|
||||||
|
__sanitizer_set_death_callback :: proc(callback: Address_Death_Callback) ---
|
||||||
|
__asan_region_is_poisoned :: proc(begin: rawptr, size: uint) -> rawptr ---
|
||||||
|
__asan_address_is_poisoned :: proc(addr: rawptr) -> i32 ---
|
||||||
|
__asan_describe_address :: proc(addr: rawptr) ---
|
||||||
|
__asan_report_present :: proc() -> i32 ---
|
||||||
|
__asan_get_report_pc :: proc() -> rawptr ---
|
||||||
|
__asan_get_report_bp :: proc() -> rawptr ---
|
||||||
|
__asan_get_report_sp :: proc() -> rawptr ---
|
||||||
|
__asan_get_report_address :: proc() -> rawptr ---
|
||||||
|
__asan_get_report_access_type :: proc() -> i32 ---
|
||||||
|
__asan_get_report_access_size :: proc() -> uint ---
|
||||||
|
__asan_get_report_description :: proc() -> cstring ---
|
||||||
|
__asan_locate_address :: proc(addr: rawptr, name: rawptr, name_size: uint, region_address: ^rawptr, region_size: ^uint) -> cstring ---
|
||||||
|
__asan_get_alloc_stack :: proc(addr: rawptr, trace: rawptr, size: uint, thread_id: ^i32) -> uint ---
|
||||||
|
__asan_get_free_stack :: proc(addr: rawptr, trace: rawptr, size: uint, thread_id: ^i32) -> uint ---
|
||||||
|
__asan_get_shadow_mapping :: proc(shadow_scale: ^uint, shadow_offset: ^uint) ---
|
||||||
|
__asan_print_accumulated_stats :: proc() ---
|
||||||
|
__asan_get_current_fake_stack :: proc() -> rawptr ---
|
||||||
|
__asan_addr_is_in_fake_stack :: proc(fake_stack: rawptr, addr: rawptr, beg: ^rawptr, end: ^rawptr) -> rawptr ---
|
||||||
|
__asan_handle_no_return :: proc() ---
|
||||||
|
__asan_update_allocation_context :: proc(addr: rawptr) -> i32 ---
|
||||||
|
}
|
||||||
|
|
||||||
|
Address_Access_Type :: enum {
|
||||||
|
none,
|
||||||
|
read,
|
||||||
|
write,
|
||||||
|
}
|
||||||
|
|
||||||
|
Address_Located_Address_String :: struct {
|
||||||
|
category: string,
|
||||||
|
name: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
Address_Shadow_Mapping :: struct {
|
||||||
|
scale: uint,
|
||||||
|
offset: uint,
|
||||||
|
}
|
||||||
|
|
||||||
|
address_poison_slice :: proc "contextless" (region: $T/[]$E) {
|
||||||
|
when ASAN_ENABLED {
|
||||||
|
__asan_poison_memory_region(raw_data(region), size_of(E) * len(region))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address_unpoison_slice :: proc "contextless" (region: $T/[]$E) {
|
||||||
|
when ASAN_ENABLED {
|
||||||
|
__asan_unpoison_memory_region(raw_data(region), size_of(E) * len(region))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address_poison_ptr :: proc "contextless" (ptr: ^$T) {
|
||||||
|
when ASAN_ENABLED {
|
||||||
|
__asan_poison_memory_region(ptr, size_of(T))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address_unpoison_ptr :: proc "contextless" (ptr: ^$T) {
|
||||||
|
when ASAN_ENABLED {
|
||||||
|
__asan_unpoison_memory_region(ptr, size_of(T))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address_poison_rawptr :: proc "contextless" (ptr: rawptr, len: int) {
|
||||||
|
when ASAN_ENABLED {
|
||||||
|
assert_contextless(len >= 0)
|
||||||
|
__asan_poison_memory_region(ptr, uint(len))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address_unpoison_rawptr :: proc "contextless" (ptr: rawptr, len: int) {
|
||||||
|
when ASAN_ENABLED {
|
||||||
|
assert_contextless(len >= 0)
|
||||||
|
__asan_unpoison_memory_region(ptr, uint(len))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address_poison :: proc {
|
||||||
|
address_poison_slice,
|
||||||
|
address_poison_ptr,
|
||||||
|
address_poison_rawptr,
|
||||||
|
}
|
||||||
|
|
||||||
|
address_unpoison :: proc {
|
||||||
|
address_unpoison_slice,
|
||||||
|
address_unpoison_ptr,
|
||||||
|
address_unpoison_rawptr,
|
||||||
|
}
|
||||||
|
|
||||||
|
address_set_death_callback :: proc "contextless" (callback: Address_Death_Callback) {
|
||||||
|
when ASAN_ENABLED {
|
||||||
|
__sanitizer_set_death_callback(callback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address_region_is_poisoned_slice :: proc "contextless" (region: []$T/$E) -> rawptr {
|
||||||
|
when ASAN_ENABLED {
|
||||||
|
return __asan_region_is_poisoned(raw_data(region), size_of(E) * len(region))
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address_region_is_poisoned_ptr :: proc "contextless" (ptr: ^$T) -> rawptr {
|
||||||
|
when ASAN_ENABLED {
|
||||||
|
return __asan_region_is_poisoned(ptr, size_of(T))
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address_region_is_poisoned_rawptr :: proc "contextless" (region: rawptr, len: int) -> rawptr {
|
||||||
|
when ASAN_ENABLED {
|
||||||
|
assert_contextless(len >= 0)
|
||||||
|
return __asan_region_is_poisoned(region, uint(len))
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address_region_is_poisoned :: proc {
|
||||||
|
address_region_is_poisoned_slice,
|
||||||
|
address_region_is_poisoned_ptr,
|
||||||
|
address_region_is_poisoned_rawptr,
|
||||||
|
}
|
||||||
|
|
||||||
|
address_address_is_poisoned :: proc "contextless" (address: rawptr) -> bool {
|
||||||
|
when ASAN_ENABLED {
|
||||||
|
return __asan_address_is_poisoned(address) != 0
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address_describe_address :: proc "contextless" (address: rawptr) {
|
||||||
|
when ASAN_ENABLED {
|
||||||
|
__asan_describe_address(address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address_report_present :: proc "contextless" () -> bool {
|
||||||
|
when ASAN_ENABLED {
|
||||||
|
return __asan_report_present() != 0
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address_get_report_pc :: proc "contextless" () -> rawptr {
|
||||||
|
when ASAN_ENABLED {
|
||||||
|
return __asan_get_report_pc()
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address_get_report_bp :: proc "contextless" () -> rawptr {
|
||||||
|
when ASAN_ENABLED {
|
||||||
|
return __asan_get_report_bp()
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address_get_report_sp :: proc "contextless" () -> rawptr {
|
||||||
|
when ASAN_ENABLED {
|
||||||
|
return __asan_get_report_sp()
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address_get_report_address :: proc "contextless" () -> rawptr {
|
||||||
|
when ASAN_ENABLED {
|
||||||
|
return __asan_get_report_address()
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address_get_report_access_type :: proc "contextless" () -> Address_Access_Type {
|
||||||
|
when ASAN_ENABLED {
|
||||||
|
return __asan_get_report_access_type() == 0 ? .read : .write
|
||||||
|
} else {
|
||||||
|
return .none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address_get_report_access_size :: proc "contextless" () -> uint {
|
||||||
|
when ASAN_ENABLED {
|
||||||
|
return __asan_get_report_access_size()
|
||||||
|
} else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address_get_report_description :: proc "contextless" () -> string {
|
||||||
|
when ASAN_ENABLED {
|
||||||
|
return string(__asan_get_report_description())
|
||||||
|
} else {
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address_locate_address :: proc "contextless" (addr: rawptr, data: []byte) -> (Address_Located_Address_String, []byte) {
|
||||||
|
when ASAN_ENABLED {
|
||||||
|
out_addr: rawptr
|
||||||
|
out_size: uint
|
||||||
|
str := __asan_locate_address(addr, raw_data(data), len(data), &out_addr, &out_size)
|
||||||
|
return { string(str), string(cstring(raw_data(data))) }, (cast([^]byte)out_addr)[:out_size]
|
||||||
|
} else {
|
||||||
|
return { "", "" }, {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address_get_alloc_stack_trace :: proc "contextless" (addr: rawptr, data: []rawptr) -> ([]rawptr, int) {
|
||||||
|
when ASAN_ENABLED {
|
||||||
|
out_thread: i32
|
||||||
|
__asan_get_alloc_stack(addr, raw_data(data), len(data), &out_thread)
|
||||||
|
return data, int(out_thread)
|
||||||
|
} else {
|
||||||
|
return {}, 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address_get_free_stack_trace :: proc "contextless" (addr: rawptr, data: []rawptr) -> ([]rawptr, int) {
|
||||||
|
when ASAN_ENABLED {
|
||||||
|
out_thread: i32
|
||||||
|
__asan_get_free_stack(addr, raw_data(data), len(data), &out_thread)
|
||||||
|
return data, int(out_thread)
|
||||||
|
} else {
|
||||||
|
return {}, 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address_get_shadow_mapping :: proc "contextless" () -> Address_Shadow_Mapping {
|
||||||
|
when ASAN_ENABLED {
|
||||||
|
result: Address_Shadow_Mapping
|
||||||
|
__asan_get_shadow_mapping(&result.scale, &result.offset)
|
||||||
|
return result
|
||||||
|
} else {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address_print_accumulated_stats :: proc "contextless" () {
|
||||||
|
when ASAN_ENABLED {
|
||||||
|
__asan_print_accumulated_stats()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address_get_current_fake_stack :: proc "contextless" () -> rawptr {
|
||||||
|
when ASAN_ENABLED {
|
||||||
|
return __asan_get_current_fake_stack()
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address_is_in_fake_stack :: proc "contextless" (fake_stack: rawptr, addr: rawptr) -> ([]byte, bool) {
|
||||||
|
when ASAN_ENABLED {
|
||||||
|
begin: rawptr
|
||||||
|
end: rawptr
|
||||||
|
if __asan_addr_is_in_fake_stack(fake_stack, addr, &begin, &end) == nil {
|
||||||
|
return {}, false
|
||||||
|
}
|
||||||
|
return ((cast([^]byte)begin)[:uintptr(end)-uintptr(begin)]), true
|
||||||
|
} else {
|
||||||
|
return {}, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address_handle_no_return :: proc "contextless" () {
|
||||||
|
when ASAN_ENABLED {
|
||||||
|
__asan_handle_no_return()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
address_update_allocation_context :: proc "contextless" (addr: rawptr) -> bool {
|
||||||
|
when ASAN_ENABLED {
|
||||||
|
return __asan_update_allocation_context(addr) != 0
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -4,12 +4,12 @@ setlocal EnableDelayedExpansion
|
|||||||
|
|
||||||
where /Q cl.exe || (
|
where /Q cl.exe || (
|
||||||
set __VSCMD_ARG_NO_LOGO=1
|
set __VSCMD_ARG_NO_LOGO=1
|
||||||
for /f "tokens=*" %%i in ('"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -requires Microsoft.VisualStudio.Workload.NativeDesktop -property installationPath') do set VS=%%i
|
for /f "tokens=*" %%i in ('"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath') do set VS=%%i
|
||||||
if "!VS!" equ "" (
|
if "!VS!" equ "" (
|
||||||
echo ERROR: Visual Studio installation not found
|
echo ERROR: MSVC installation not found
|
||||||
exit /b 1
|
exit /b 1
|
||||||
)
|
)
|
||||||
call "!VS!\VC\Auxiliary\Build\vcvarsall.bat" amd64 || exit /b 1
|
call "!VS!\Common7\Tools\vsdevcmd.bat" -arch=x64 -host_arch=x64 || exit /b 1
|
||||||
)
|
)
|
||||||
|
|
||||||
if "%VSCMD_ARG_TGT_ARCH%" neq "x64" (
|
if "%VSCMD_ARG_TGT_ARCH%" neq "x64" (
|
||||||
@@ -19,7 +19,11 @@ if "%VSCMD_ARG_TGT_ARCH%" neq "x64" (
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
for /f %%i in ('powershell get-date -format "{yyyyMMdd}"') do (
|
pushd misc
|
||||||
|
cl /nologo get-date.c
|
||||||
|
popd
|
||||||
|
|
||||||
|
for /f %%i in ('misc\get-date') do (
|
||||||
set CURR_DATE_TIME=%%i
|
set CURR_DATE_TIME=%%i
|
||||||
)
|
)
|
||||||
set curr_year=%CURR_DATE_TIME:~0,4%
|
set curr_year=%CURR_DATE_TIME:~0,4%
|
||||||
@@ -58,7 +62,6 @@ set V4=0
|
|||||||
set odin_version_full="%V1%.%V2%.%V3%.%V4%"
|
set odin_version_full="%V1%.%V2%.%V3%.%V4%"
|
||||||
set odin_version_raw="dev-%V1%-%V2%"
|
set odin_version_raw="dev-%V1%-%V2%"
|
||||||
|
|
||||||
|
|
||||||
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF
|
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF
|
||||||
rem Parse source code as utf-8 even on shift-jis and other codepages
|
rem Parse source code as utf-8 even on shift-jis and other codepages
|
||||||
rem See https://learn.microsoft.com/en-us/cpp/build/reference/utf-8-set-source-and-executable-character-sets-to-utf-8?view=msvc-170
|
rem See https://learn.microsoft.com/en-us/cpp/build/reference/utf-8-set-source-and-executable-character-sets-to-utf-8?view=msvc-170
|
||||||
@@ -135,6 +138,7 @@ del *.ilk > NUL 2> NUL
|
|||||||
|
|
||||||
rc %rc_flags% %odin_rc%
|
rc %rc_flags% %odin_rc%
|
||||||
cl %compiler_settings% "src\main.cpp" "src\libtommath.cpp" /link %linker_settings% -OUT:%exe_name%
|
cl %compiler_settings% "src\main.cpp" "src\libtommath.cpp" /link %linker_settings% -OUT:%exe_name%
|
||||||
|
if %errorlevel% neq 0 goto end_of_build
|
||||||
mt -nologo -inputresource:%exe_name%;#1 -manifest misc\odin.manifest -outputresource:%exe_name%;#1 -validate_manifest -identity:"odin, processorArchitecture=amd64, version=%odin_version_full%, type=win32"
|
mt -nologo -inputresource:%exe_name%;#1 -manifest misc\odin.manifest -outputresource:%exe_name%;#1 -validate_manifest -identity:"odin, processorArchitecture=amd64, version=%odin_version_full%, type=win32"
|
||||||
if %errorlevel% neq 0 goto end_of_build
|
if %errorlevel% neq 0 goto end_of_build
|
||||||
|
|
||||||
|
|||||||
+19
-13
@@ -9,7 +9,7 @@ set -eu
|
|||||||
CPPFLAGS="$CPPFLAGS -DODIN_VERSION_RAW=\"dev-$(date +"%Y-%m")\""
|
CPPFLAGS="$CPPFLAGS -DODIN_VERSION_RAW=\"dev-$(date +"%Y-%m")\""
|
||||||
CXXFLAGS="$CXXFLAGS -std=c++14"
|
CXXFLAGS="$CXXFLAGS -std=c++14"
|
||||||
DISABLED_WARNINGS="-Wno-switch -Wno-macro-redefined -Wno-unused-value"
|
DISABLED_WARNINGS="-Wno-switch -Wno-macro-redefined -Wno-unused-value"
|
||||||
LDFLAGS="$LDFLAGS -pthread -lm -lstdc++"
|
LDFLAGS="$LDFLAGS -pthread -lm"
|
||||||
OS_ARCH="$(uname -m)"
|
OS_ARCH="$(uname -m)"
|
||||||
OS_NAME="$(uname -s)"
|
OS_NAME="$(uname -s)"
|
||||||
|
|
||||||
@@ -25,7 +25,9 @@ error() {
|
|||||||
|
|
||||||
# Brew advises people not to add llvm to their $PATH, so try and use brew to find it.
|
# 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 [ -z "$LLVM_CONFIG" ] && [ -n "$(command -v brew)" ]; then
|
||||||
if [ -n "$(command -v $(brew --prefix llvm@18)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@18)/bin/llvm-config"
|
if [ -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@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"
|
elif [ -n "$(command -v $(brew --prefix llvm@14)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@14)/bin/llvm-config"
|
||||||
fi
|
fi
|
||||||
@@ -33,13 +35,17 @@ fi
|
|||||||
|
|
||||||
if [ -z "$LLVM_CONFIG" ]; then
|
if [ -z "$LLVM_CONFIG" ]; then
|
||||||
# darwin, linux, openbsd
|
# darwin, linux, openbsd
|
||||||
if [ -n "$(command -v llvm-config-18)" ]; then LLVM_CONFIG="llvm-config-18"
|
if [ -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-17)" ]; then LLVM_CONFIG="llvm-config-17"
|
||||||
elif [ -n "$(command -v llvm-config-14)" ]; then LLVM_CONFIG="llvm-config-14"
|
elif [ -n "$(command -v llvm-config-14)" ]; then LLVM_CONFIG="llvm-config-14"
|
||||||
elif [ -n "$(command -v llvm-config-13)" ]; then LLVM_CONFIG="llvm-config-13"
|
elif [ -n "$(command -v llvm-config-13)" ]; then LLVM_CONFIG="llvm-config-13"
|
||||||
elif [ -n "$(command -v llvm-config-12)" ]; then LLVM_CONFIG="llvm-config-12"
|
elif [ -n "$(command -v llvm-config-12)" ]; then LLVM_CONFIG="llvm-config-12"
|
||||||
elif [ -n "$(command -v llvm-config-11)" ]; then LLVM_CONFIG="llvm-config-11"
|
elif [ -n "$(command -v llvm-config-11)" ]; then LLVM_CONFIG="llvm-config-11"
|
||||||
# freebsd
|
# freebsd
|
||||||
|
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-config18)" ]; then LLVM_CONFIG="llvm-config18"
|
||||||
elif [ -n "$(command -v llvm-config17)" ]; then LLVM_CONFIG="llvm-config17"
|
elif [ -n "$(command -v llvm-config17)" ]; then LLVM_CONFIG="llvm-config17"
|
||||||
elif [ -n "$(command -v llvm-config14)" ]; then LLVM_CONFIG="llvm-config14"
|
elif [ -n "$(command -v llvm-config14)" ]; then LLVM_CONFIG="llvm-config14"
|
||||||
@@ -66,15 +72,15 @@ LLVM_VERSION_MAJOR="$(echo $LLVM_VERSION | awk -F. '{print $1}')"
|
|||||||
LLVM_VERSION_MINOR="$(echo $LLVM_VERSION | awk -F. '{print $2}')"
|
LLVM_VERSION_MINOR="$(echo $LLVM_VERSION | awk -F. '{print $2}')"
|
||||||
LLVM_VERSION_PATCH="$(echo $LLVM_VERSION | awk -F. '{print $3}')"
|
LLVM_VERSION_PATCH="$(echo $LLVM_VERSION | awk -F. '{print $3}')"
|
||||||
|
|
||||||
if [ $LLVM_VERSION_MAJOR -lt 11 ] || ([ $LLVM_VERSION_MAJOR -gt 14 ] && [ $LLVM_VERSION_MAJOR -lt 17 ]) || [ $LLVM_VERSION_MAJOR -gt 18 ]; then
|
if [ $LLVM_VERSION_MAJOR -lt 11 ] || ([ $LLVM_VERSION_MAJOR -gt 14 ] && [ $LLVM_VERSION_MAJOR -lt 17 ]) || [ $LLVM_VERSION_MAJOR -gt 20 ]; then
|
||||||
error "Invalid LLVM version $LLVM_VERSION: must be 11, 12, 13, 14, 17 or 18"
|
error "Invalid LLVM version $LLVM_VERSION: must be 11, 12, 13, 14, 17, 18, 19 or 20"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
case "$OS_NAME" in
|
case "$OS_NAME" in
|
||||||
Darwin)
|
Darwin)
|
||||||
if [ "$OS_ARCH" = "arm64" ]; then
|
if [ "$OS_ARCH" = "arm64" ]; then
|
||||||
if [ $LLVM_VERSION_MAJOR -lt 13 ]; then
|
if [ $LLVM_VERSION_MAJOR -lt 13 ]; then
|
||||||
error "Invalid LLVM version $LLVM_VERSION: Darwin Arm64 requires LLVM 13, 14, 17 or 18"
|
error "Invalid LLVM version $LLVM_VERSION: Darwin Arm64 requires LLVM 13, 14, 17, 18, 19 or 20"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -92,28 +98,28 @@ Darwin)
|
|||||||
;;
|
;;
|
||||||
FreeBSD)
|
FreeBSD)
|
||||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||||
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
LDFLAGS="$LDFLAGS -lstdc++ $($LLVM_CONFIG --libs core native --system-libs)"
|
||||||
;;
|
;;
|
||||||
NetBSD)
|
NetBSD)
|
||||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||||
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
LDFLAGS="$LDFLAGS -lstdc++ $($LLVM_CONFIG --libs core native --system-libs)"
|
||||||
;;
|
;;
|
||||||
Linux)
|
Linux)
|
||||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||||
LDFLAGS="$LDFLAGS -ldl $($LLVM_CONFIG --libs core native --system-libs --libfiles)"
|
LDFLAGS="$LDFLAGS -lstdc++ -ldl $($LLVM_CONFIG --libs core native --system-libs --libfiles)"
|
||||||
# Copy libLLVM*.so into current directory for linking
|
# Copy libLLVM*.so into current directory for linking
|
||||||
# NOTE: This is needed by the Linux release pipeline!
|
# NOTE: This is needed by the Linux release pipeline!
|
||||||
# cp $(readlink -f $($LLVM_CONFIG --libfiles)) ./
|
# cp $(readlink -f $($LLVM_CONFIG --libfiles)) ./
|
||||||
LDFLAGS="$LDFLAGS -Wl,-rpath=\$ORIGIN"
|
LDFLAGS="$LDFLAGS -Wl,-rpath=\$ORIGIN"
|
||||||
;;
|
;;
|
||||||
OpenBSD)
|
OpenBSD)
|
||||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
CXXFLAGS="$CXXFLAGS -I/usr/local/include $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||||
LDFLAGS="$LDFLAGS -liconv"
|
LDFLAGS="$LDFLAGS -lstdc++ -L/usr/local/lib -liconv"
|
||||||
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
||||||
;;
|
;;
|
||||||
Haiku)
|
Haiku)
|
||||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags) -I/system/develop/headers/private/shared -I/system/develop/headers/private/kernel"
|
CXXFLAGS="$CXXFLAGS -D_GNU_SOURCE $($LLVM_CONFIG --cxxflags --ldflags) -I/system/develop/headers/private/shared -I/system/develop/headers/private/kernel"
|
||||||
LDFLAGS="$LDFLAGS -liconv"
|
LDFLAGS="$LDFLAGS -lstdc++ -liconv"
|
||||||
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
# Intended for use in Alpine containers, see the "nightly" Github action for a list of dependencies
|
# Intended for use in Alpine containers, see the "nightly" Github action for a list of dependencies
|
||||||
|
|
||||||
CXX="clang++-18"
|
CXX="clang++-20"
|
||||||
LLVM_CONFIG="llvm-config-18"
|
LLVM_CONFIG="llvm-config-20"
|
||||||
|
|
||||||
DISABLED_WARNINGS="-Wno-switch -Wno-macro-redefined -Wno-unused-value"
|
DISABLED_WARNINGS="-Wno-switch -Wno-macro-redefined -Wno-unused-value"
|
||||||
|
|
||||||
|
|||||||
@@ -114,3 +114,5 @@ CHAR_BIT :: 8
|
|||||||
va_list :: struct #align(16) {
|
va_list :: struct #align(16) {
|
||||||
_: [4096]u8,
|
_: [4096]u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FILE :: struct {}
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
package c_frontend_preprocess
|
|
||||||
|
|
||||||
import "core:c/frontend/tokenizer"
|
|
||||||
|
|
||||||
const_expr :: proc(rest: ^^Token, tok: ^Token) -> i64 {
|
|
||||||
// TODO(bill): Handle const_expr correctly
|
|
||||||
// This is effectively a mini-parser
|
|
||||||
|
|
||||||
assert(rest != nil)
|
|
||||||
assert(tok != nil)
|
|
||||||
rest^ = tokenizer.new_eof(tok)
|
|
||||||
switch v in tok.val {
|
|
||||||
case i64:
|
|
||||||
return v
|
|
||||||
case f64:
|
|
||||||
return i64(v)
|
|
||||||
case string:
|
|
||||||
return 0
|
|
||||||
case []u16:
|
|
||||||
// TODO
|
|
||||||
case []u32:
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,154 +0,0 @@
|
|||||||
package c_frontend_preprocess
|
|
||||||
|
|
||||||
import "core:unicode/utf8"
|
|
||||||
|
|
||||||
unquote_char :: proc(str: string, quote: byte) -> (r: rune, multiple_bytes: bool, tail_string: string, success: bool) {
|
|
||||||
hex_to_int :: proc(c: byte) -> int {
|
|
||||||
switch c {
|
|
||||||
case '0'..='9': return int(c-'0')
|
|
||||||
case 'a'..='f': return int(c-'a')+10
|
|
||||||
case 'A'..='F': return int(c-'A')+10
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
w: int
|
|
||||||
|
|
||||||
if str[0] == quote && quote == '"' {
|
|
||||||
return
|
|
||||||
} else if str[0] >= 0x80 {
|
|
||||||
r, w = utf8.decode_rune_in_string(str)
|
|
||||||
return r, true, str[w:], true
|
|
||||||
} else if str[0] != '\\' {
|
|
||||||
return rune(str[0]), false, str[1:], true
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(str) <= 1 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s := str
|
|
||||||
c := s[1]
|
|
||||||
s = s[2:]
|
|
||||||
|
|
||||||
switch c {
|
|
||||||
case: r = rune(c)
|
|
||||||
|
|
||||||
case 'a': r = '\a'
|
|
||||||
case 'b': r = '\b'
|
|
||||||
case 'e': r = '\e'
|
|
||||||
case 'f': r = '\f'
|
|
||||||
case 'n': r = '\n'
|
|
||||||
case 'r': r = '\r'
|
|
||||||
case 't': r = '\t'
|
|
||||||
case 'v': r = '\v'
|
|
||||||
case '\\': r = '\\'
|
|
||||||
|
|
||||||
case '"': r = '"'
|
|
||||||
case '\'': r = '\''
|
|
||||||
|
|
||||||
case '0'..='7':
|
|
||||||
v := int(c-'0')
|
|
||||||
if len(s) < 2 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for i in 0..<len(s) {
|
|
||||||
d := int(s[i]-'0')
|
|
||||||
if d < 0 || d > 7 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
v = (v<<3) | d
|
|
||||||
}
|
|
||||||
s = s[2:]
|
|
||||||
if v > 0xff {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r = rune(v)
|
|
||||||
|
|
||||||
case 'x', 'u', 'U':
|
|
||||||
count: int
|
|
||||||
switch c {
|
|
||||||
case 'x': count = 2
|
|
||||||
case 'u': count = 4
|
|
||||||
case 'U': count = 8
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(s) < count {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for i in 0..<count {
|
|
||||||
d := hex_to_int(s[i])
|
|
||||||
if d < 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r = (r<<4) | rune(d)
|
|
||||||
}
|
|
||||||
s = s[count:]
|
|
||||||
if c == 'x' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if r > utf8.MAX_RUNE {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
multiple_bytes = true
|
|
||||||
}
|
|
||||||
|
|
||||||
success = true
|
|
||||||
tail_string = s
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
unquote_string :: proc(lit: string, allocator := context.allocator) -> (res: string, allocated, success: bool) {
|
|
||||||
contains_rune :: proc(s: string, r: rune) -> int {
|
|
||||||
for c, offset in s {
|
|
||||||
if c == r {
|
|
||||||
return offset
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(len(lit) >= 2)
|
|
||||||
|
|
||||||
s := lit
|
|
||||||
quote := '"'
|
|
||||||
|
|
||||||
if s == `""` {
|
|
||||||
return "", false, true
|
|
||||||
}
|
|
||||||
|
|
||||||
if contains_rune(s, '\n') >= 0 {
|
|
||||||
return s, false, false
|
|
||||||
}
|
|
||||||
|
|
||||||
if contains_rune(s, '\\') < 0 && contains_rune(s, quote) < 0 {
|
|
||||||
if quote == '"' {
|
|
||||||
return s, false, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s = s[1:len(s)-1]
|
|
||||||
|
|
||||||
|
|
||||||
buf_len := 3*len(s) / 2
|
|
||||||
buf := make([]byte, buf_len, allocator)
|
|
||||||
offset := 0
|
|
||||||
for len(s) > 0 {
|
|
||||||
r, multiple_bytes, tail_string, ok := unquote_char(s, byte(quote))
|
|
||||||
if !ok {
|
|
||||||
delete(buf)
|
|
||||||
return s, false, false
|
|
||||||
}
|
|
||||||
s = tail_string
|
|
||||||
if r < 0x80 || !multiple_bytes {
|
|
||||||
buf[offset] = byte(r)
|
|
||||||
offset += 1
|
|
||||||
} else {
|
|
||||||
b, w := utf8.encode_rune(r)
|
|
||||||
copy(buf[offset:], b[:w])
|
|
||||||
offset += w
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
new_string := string(buf[:offset])
|
|
||||||
|
|
||||||
return new_string, true, true
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
/*
|
|
||||||
Example:
|
|
||||||
package demo
|
|
||||||
|
|
||||||
import tokenizer "core:c/frontend/tokenizer"
|
|
||||||
import preprocessor "core:c/frontend/preprocessor"
|
|
||||||
import "core:fmt"
|
|
||||||
|
|
||||||
main :: proc() {
|
|
||||||
t := &tokenizer.Tokenizer{};
|
|
||||||
tokenizer.init_defaults(t);
|
|
||||||
|
|
||||||
cpp := &preprocessor.Preprocessor{};
|
|
||||||
cpp.warn, cpp.err = t.warn, t.err;
|
|
||||||
preprocessor.init_lookup_tables(cpp);
|
|
||||||
preprocessor.init_default_macros(cpp);
|
|
||||||
cpp.include_paths = {"my/path/to/include"};
|
|
||||||
|
|
||||||
tok := tokenizer.tokenize_file(t, "the/source/file.c", 1);
|
|
||||||
|
|
||||||
tok = preprocessor.preprocess(cpp, tok);
|
|
||||||
if tok != nil {
|
|
||||||
for t := tok; t.kind != .EOF; t = t.next {
|
|
||||||
fmt.println(t.lit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.println("[Done]");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
package c_frontend_tokenizer
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
package c_frontend_tokenizer
|
|
||||||
|
|
||||||
// NOTE(bill): This is a really dumb approach for a hide set,
|
|
||||||
// but it's really simple and probably fast enough in practice
|
|
||||||
|
|
||||||
|
|
||||||
Hide_Set :: struct {
|
|
||||||
next: ^Hide_Set,
|
|
||||||
name: string,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
new_hide_set :: proc(name: string) -> ^Hide_Set {
|
|
||||||
hs := new(Hide_Set)
|
|
||||||
hs.name = name
|
|
||||||
return hs
|
|
||||||
}
|
|
||||||
|
|
||||||
hide_set_contains :: proc(hs: ^Hide_Set, name: string) -> bool {
|
|
||||||
for h := hs; h != nil; h = h.next {
|
|
||||||
if h.name == name {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
hide_set_union :: proc(a, b: ^Hide_Set) -> ^Hide_Set {
|
|
||||||
head: Hide_Set
|
|
||||||
curr := &head
|
|
||||||
|
|
||||||
for h := a; h != nil; h = h.next {
|
|
||||||
curr.next = new_hide_set(h.name)
|
|
||||||
curr = curr.next
|
|
||||||
}
|
|
||||||
curr.next = b
|
|
||||||
return head.next
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
hide_set_intersection :: proc(a, b: ^Hide_Set) -> ^Hide_Set {
|
|
||||||
head: Hide_Set
|
|
||||||
curr := &head
|
|
||||||
|
|
||||||
for h := a; h != nil; h = h.next {
|
|
||||||
if hide_set_contains(b, h.name) {
|
|
||||||
curr.next = new_hide_set(h.name)
|
|
||||||
curr = curr.next
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return head.next
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
add_hide_set :: proc(tok: ^Token, hs: ^Hide_Set) -> ^Token {
|
|
||||||
head: Token
|
|
||||||
curr := &head
|
|
||||||
|
|
||||||
tok := tok
|
|
||||||
for ; tok != nil; tok = tok.next {
|
|
||||||
t := copy_token(tok)
|
|
||||||
t.hide_set = hide_set_union(t.hide_set, hs)
|
|
||||||
curr.next = t
|
|
||||||
curr = curr.next
|
|
||||||
}
|
|
||||||
return head.next
|
|
||||||
}
|
|
||||||
@@ -1,169 +0,0 @@
|
|||||||
package c_frontend_tokenizer
|
|
||||||
|
|
||||||
|
|
||||||
Pos :: struct {
|
|
||||||
file: string,
|
|
||||||
line: int,
|
|
||||||
column: int,
|
|
||||||
offset: int,
|
|
||||||
}
|
|
||||||
|
|
||||||
Token_Kind :: enum {
|
|
||||||
Invalid,
|
|
||||||
Ident,
|
|
||||||
Punct,
|
|
||||||
Keyword,
|
|
||||||
Char,
|
|
||||||
String,
|
|
||||||
Number,
|
|
||||||
PP_Number,
|
|
||||||
Comment,
|
|
||||||
EOF,
|
|
||||||
}
|
|
||||||
|
|
||||||
File :: struct {
|
|
||||||
name: string,
|
|
||||||
id: int,
|
|
||||||
src: []byte,
|
|
||||||
|
|
||||||
display_name: string,
|
|
||||||
line_delta: int,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Token_Type_Hint :: enum u8 {
|
|
||||||
None,
|
|
||||||
|
|
||||||
Int,
|
|
||||||
Long,
|
|
||||||
Long_Long,
|
|
||||||
|
|
||||||
Unsigned_Int,
|
|
||||||
Unsigned_Long,
|
|
||||||
Unsigned_Long_Long,
|
|
||||||
|
|
||||||
Float,
|
|
||||||
Double,
|
|
||||||
Long_Double,
|
|
||||||
|
|
||||||
UTF_8,
|
|
||||||
UTF_16,
|
|
||||||
UTF_32,
|
|
||||||
UTF_Wide,
|
|
||||||
}
|
|
||||||
|
|
||||||
Token_Value :: union {
|
|
||||||
i64,
|
|
||||||
f64,
|
|
||||||
string,
|
|
||||||
[]u16,
|
|
||||||
[]u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
Token :: struct {
|
|
||||||
kind: Token_Kind,
|
|
||||||
next: ^Token,
|
|
||||||
lit: string,
|
|
||||||
|
|
||||||
pos: Pos,
|
|
||||||
file: ^File,
|
|
||||||
line_delta: int,
|
|
||||||
at_bol: bool,
|
|
||||||
has_space: bool,
|
|
||||||
|
|
||||||
type_hint: Token_Type_Hint,
|
|
||||||
val: Token_Value,
|
|
||||||
prefix: string,
|
|
||||||
|
|
||||||
// Preprocessor values
|
|
||||||
hide_set: ^Hide_Set,
|
|
||||||
origin: ^Token,
|
|
||||||
}
|
|
||||||
|
|
||||||
Is_Keyword_Proc :: #type proc(tok: ^Token) -> bool
|
|
||||||
|
|
||||||
copy_token :: proc(tok: ^Token) -> ^Token {
|
|
||||||
t, _ := new_clone(tok^)
|
|
||||||
t.next = nil
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
new_eof :: proc(tok: ^Token) -> ^Token {
|
|
||||||
t, _ := new_clone(tok^)
|
|
||||||
t.kind = .EOF
|
|
||||||
t.lit = ""
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
default_is_keyword :: proc(tok: ^Token) -> bool {
|
|
||||||
if tok.kind == .Keyword {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if len(tok.lit) > 0 {
|
|
||||||
return default_keyword_set[tok.lit]
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
token_name := [Token_Kind]string {
|
|
||||||
.Invalid = "invalid",
|
|
||||||
.Ident = "ident",
|
|
||||||
.Punct = "punct",
|
|
||||||
.Keyword = "keyword",
|
|
||||||
.Char = "char",
|
|
||||||
.String = "string",
|
|
||||||
.Number = "number",
|
|
||||||
.PP_Number = "preprocessor number",
|
|
||||||
.Comment = "comment",
|
|
||||||
.EOF = "eof",
|
|
||||||
}
|
|
||||||
|
|
||||||
default_keyword_set := map[string]bool{
|
|
||||||
"auto" = true,
|
|
||||||
"break" = true,
|
|
||||||
"case" = true,
|
|
||||||
"char" = true,
|
|
||||||
"const" = true,
|
|
||||||
"continue" = true,
|
|
||||||
"default" = true,
|
|
||||||
"do" = true,
|
|
||||||
"double" = true,
|
|
||||||
"else" = true,
|
|
||||||
"enum" = true,
|
|
||||||
"extern" = true,
|
|
||||||
"float" = true,
|
|
||||||
"for" = true,
|
|
||||||
"goto" = true,
|
|
||||||
"if" = true,
|
|
||||||
"int" = true,
|
|
||||||
"long" = true,
|
|
||||||
"register" = true,
|
|
||||||
"restrict" = true,
|
|
||||||
"return" = true,
|
|
||||||
"short" = true,
|
|
||||||
"signed" = true,
|
|
||||||
"sizeof" = true,
|
|
||||||
"static" = true,
|
|
||||||
"struct" = true,
|
|
||||||
"switch" = true,
|
|
||||||
"typedef" = true,
|
|
||||||
"union" = true,
|
|
||||||
"unsigned" = true,
|
|
||||||
"void" = true,
|
|
||||||
"volatile" = true,
|
|
||||||
"while" = true,
|
|
||||||
"_Alignas" = true,
|
|
||||||
"_Alignof" = true,
|
|
||||||
"_Atomic" = true,
|
|
||||||
"_Bool" = true,
|
|
||||||
"_Generic" = true,
|
|
||||||
"_Noreturn" = true,
|
|
||||||
"_Thread_local" = true,
|
|
||||||
"__restrict" = true,
|
|
||||||
"typeof" = true,
|
|
||||||
"asm" = true,
|
|
||||||
"__restrict__" = true,
|
|
||||||
"__thread" = true,
|
|
||||||
"__attribute__" = true,
|
|
||||||
}
|
|
||||||
@@ -1,667 +0,0 @@
|
|||||||
package c_frontend_tokenizer
|
|
||||||
|
|
||||||
import "core:fmt"
|
|
||||||
import "core:os"
|
|
||||||
import "core:strings"
|
|
||||||
import "core:unicode/utf8"
|
|
||||||
|
|
||||||
|
|
||||||
Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any)
|
|
||||||
|
|
||||||
|
|
||||||
Tokenizer :: struct {
|
|
||||||
// Immutable data
|
|
||||||
path: string,
|
|
||||||
src: []byte,
|
|
||||||
|
|
||||||
|
|
||||||
// Tokenizing state
|
|
||||||
ch: rune,
|
|
||||||
offset: int,
|
|
||||||
read_offset: int,
|
|
||||||
line_offset: int,
|
|
||||||
line_count: int,
|
|
||||||
|
|
||||||
// Extra information for tokens
|
|
||||||
at_bol: bool,
|
|
||||||
has_space: bool,
|
|
||||||
|
|
||||||
// Mutable data
|
|
||||||
err: Error_Handler,
|
|
||||||
warn: Error_Handler,
|
|
||||||
error_count: int,
|
|
||||||
warning_count: int,
|
|
||||||
}
|
|
||||||
|
|
||||||
init_defaults :: proc(t: ^Tokenizer, err: Error_Handler = default_error_handler, warn: Error_Handler = default_warn_handler) {
|
|
||||||
t.err = err
|
|
||||||
t.warn = warn
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@(private)
|
|
||||||
offset_to_pos :: proc(t: ^Tokenizer, offset: int) -> (pos: Pos) {
|
|
||||||
pos.file = t.path
|
|
||||||
pos.offset = offset
|
|
||||||
pos.line = t.line_count
|
|
||||||
pos.column = offset - t.line_offset + 1
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
default_error_handler :: proc(pos: Pos, msg: string, args: ..any) {
|
|
||||||
fmt.eprintf("%s(%d:%d) ", pos.file, pos.line, pos.column)
|
|
||||||
fmt.eprintf(msg, ..args)
|
|
||||||
fmt.eprintf("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
default_warn_handler :: proc(pos: Pos, msg: string, args: ..any) {
|
|
||||||
fmt.eprintf("%s(%d:%d) warning: ", pos.file, pos.line, pos.column)
|
|
||||||
fmt.eprintf(msg, ..args)
|
|
||||||
fmt.eprintf("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
error_offset :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
|
|
||||||
pos := offset_to_pos(t, offset)
|
|
||||||
if t.err != nil {
|
|
||||||
t.err(pos, msg, ..args)
|
|
||||||
}
|
|
||||||
t.error_count += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
warn_offset :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
|
|
||||||
pos := offset_to_pos(t, offset)
|
|
||||||
if t.warn != nil {
|
|
||||||
t.warn(pos, msg, ..args)
|
|
||||||
}
|
|
||||||
t.warning_count += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
error :: proc(t: ^Tokenizer, tok: ^Token, msg: string, args: ..any) {
|
|
||||||
pos := tok.pos
|
|
||||||
if t.err != nil {
|
|
||||||
t.err(pos, msg, ..args)
|
|
||||||
}
|
|
||||||
t.error_count += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
warn :: proc(t: ^Tokenizer, tok: ^Token, msg: string, args: ..any) {
|
|
||||||
pos := tok.pos
|
|
||||||
if t.warn != nil {
|
|
||||||
t.warn(pos, msg, ..args)
|
|
||||||
}
|
|
||||||
t.warning_count += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
advance_rune :: proc(t: ^Tokenizer) {
|
|
||||||
if t.read_offset < len(t.src) {
|
|
||||||
t.offset = t.read_offset
|
|
||||||
if t.ch == '\n' {
|
|
||||||
t.at_bol = true
|
|
||||||
t.line_offset = t.offset
|
|
||||||
t.line_count += 1
|
|
||||||
}
|
|
||||||
r, w := rune(t.src[t.read_offset]), 1
|
|
||||||
switch {
|
|
||||||
case r == 0:
|
|
||||||
error_offset(t, t.offset, "illegal character NUL")
|
|
||||||
case r >= utf8.RUNE_SELF:
|
|
||||||
r, w = utf8.decode_rune(t.src[t.read_offset:])
|
|
||||||
if r == utf8.RUNE_ERROR && w == 1 {
|
|
||||||
error_offset(t, t.offset, "illegal UTF-8 encoding")
|
|
||||||
} else if r == utf8.RUNE_BOM && t.offset > 0 {
|
|
||||||
error_offset(t, t.offset, "illegal byte order mark")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.read_offset += w
|
|
||||||
t.ch = r
|
|
||||||
} else {
|
|
||||||
t.offset = len(t.src)
|
|
||||||
if t.ch == '\n' {
|
|
||||||
t.at_bol = true
|
|
||||||
t.line_offset = t.offset
|
|
||||||
t.line_count += 1
|
|
||||||
}
|
|
||||||
t.ch = -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
advance_rune_n :: proc(t: ^Tokenizer, n: int) {
|
|
||||||
for _ in 0..<n {
|
|
||||||
advance_rune(t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is_digit :: proc(r: rune) -> bool {
|
|
||||||
return '0' <= r && r <= '9'
|
|
||||||
}
|
|
||||||
|
|
||||||
skip_whitespace :: proc(t: ^Tokenizer) {
|
|
||||||
for {
|
|
||||||
switch t.ch {
|
|
||||||
case ' ', '\t', '\r', '\v', '\f', '\n':
|
|
||||||
t.has_space = true
|
|
||||||
advance_rune(t)
|
|
||||||
case:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scan_comment :: proc(t: ^Tokenizer) -> string {
|
|
||||||
offset := t.offset-1
|
|
||||||
next := -1
|
|
||||||
general: {
|
|
||||||
if t.ch == '/'{ // line comments
|
|
||||||
advance_rune(t)
|
|
||||||
for t.ch != '\n' && t.ch >= 0 {
|
|
||||||
advance_rune(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
next = t.offset
|
|
||||||
if t.ch == '\n' {
|
|
||||||
next += 1
|
|
||||||
}
|
|
||||||
break general
|
|
||||||
}
|
|
||||||
|
|
||||||
/* style comment */
|
|
||||||
advance_rune(t)
|
|
||||||
for t.ch >= 0 {
|
|
||||||
ch := t.ch
|
|
||||||
advance_rune(t)
|
|
||||||
if ch == '*' && t.ch == '/' {
|
|
||||||
advance_rune(t)
|
|
||||||
next = t.offset
|
|
||||||
break general
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
error_offset(t, offset, "comment not terminated")
|
|
||||||
}
|
|
||||||
|
|
||||||
lit := t.src[offset : t.offset]
|
|
||||||
|
|
||||||
// NOTE(bill): Strip CR for line comments
|
|
||||||
for len(lit) > 2 && lit[1] == '/' && lit[len(lit)-1] == '\r' {
|
|
||||||
lit = lit[:len(lit)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return string(lit)
|
|
||||||
}
|
|
||||||
|
|
||||||
scan_identifier :: proc(t: ^Tokenizer) -> string {
|
|
||||||
offset := t.offset
|
|
||||||
|
|
||||||
for is_ident1(t.ch) {
|
|
||||||
advance_rune(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(t.src[offset : t.offset])
|
|
||||||
}
|
|
||||||
|
|
||||||
scan_string :: proc(t: ^Tokenizer) -> string {
|
|
||||||
offset := t.offset-1
|
|
||||||
|
|
||||||
for {
|
|
||||||
ch := t.ch
|
|
||||||
if ch == '\n' || ch < 0 {
|
|
||||||
error_offset(t, offset, "string literal was not terminated")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
advance_rune(t)
|
|
||||||
if ch == '"' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if ch == '\\' {
|
|
||||||
scan_escape(t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(t.src[offset : t.offset])
|
|
||||||
}
|
|
||||||
|
|
||||||
digit_val :: proc(r: rune) -> int {
|
|
||||||
switch r {
|
|
||||||
case '0'..='9':
|
|
||||||
return int(r-'0')
|
|
||||||
case 'A'..='F':
|
|
||||||
return int(r-'A' + 10)
|
|
||||||
case 'a'..='f':
|
|
||||||
return int(r-'a' + 10)
|
|
||||||
}
|
|
||||||
return 16
|
|
||||||
}
|
|
||||||
|
|
||||||
scan_escape :: proc(t: ^Tokenizer) -> bool {
|
|
||||||
offset := t.offset
|
|
||||||
|
|
||||||
esc := t.ch
|
|
||||||
n: int
|
|
||||||
base, max: u32
|
|
||||||
switch esc {
|
|
||||||
case 'a', 'b', 'e', 'f', 'n', 't', 'v', 'r', '\\', '\'', '"':
|
|
||||||
advance_rune(t)
|
|
||||||
return true
|
|
||||||
|
|
||||||
case '0'..='7':
|
|
||||||
for digit_val(t.ch) < 8 {
|
|
||||||
advance_rune(t)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
case 'x':
|
|
||||||
advance_rune(t)
|
|
||||||
for digit_val(t.ch) < 16 {
|
|
||||||
advance_rune(t)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
case 'u':
|
|
||||||
advance_rune(t)
|
|
||||||
n, base, max = 4, 16, utf8.MAX_RUNE
|
|
||||||
case 'U':
|
|
||||||
advance_rune(t)
|
|
||||||
n, base, max = 8, 16, utf8.MAX_RUNE
|
|
||||||
case:
|
|
||||||
if t.ch < 0 {
|
|
||||||
error_offset(t, offset, "escape sequence was not terminated")
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
x: u32
|
|
||||||
main_loop: for n > 0 {
|
|
||||||
d := u32(digit_val(t.ch))
|
|
||||||
if d >= base {
|
|
||||||
if t.ch == '"' || t.ch == '\'' {
|
|
||||||
break main_loop
|
|
||||||
}
|
|
||||||
if t.ch < 0 {
|
|
||||||
error_offset(t, t.offset, "escape sequence was not terminated")
|
|
||||||
} else {
|
|
||||||
error_offset(t, t.offset, "illegal character '%r' : %d in escape sequence", t.ch, t.ch)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
x = x*base + d
|
|
||||||
advance_rune(t)
|
|
||||||
n -= 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if x > max || 0xd800 <= x && x <= 0xdfff {
|
|
||||||
error_offset(t, offset, "escape sequence is an invalid Unicode code point")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
scan_rune :: proc(t: ^Tokenizer) -> string {
|
|
||||||
offset := t.offset-1
|
|
||||||
valid := true
|
|
||||||
n := 0
|
|
||||||
for {
|
|
||||||
ch := t.ch
|
|
||||||
if ch == '\n' || ch < 0 {
|
|
||||||
if valid {
|
|
||||||
error_offset(t, offset, "rune literal not terminated")
|
|
||||||
valid = false
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
advance_rune(t)
|
|
||||||
if ch == '\'' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
n += 1
|
|
||||||
if ch == '\\' {
|
|
||||||
if !scan_escape(t) {
|
|
||||||
valid = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if valid && n != 1 {
|
|
||||||
error_offset(t, offset, "illegal rune literal")
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(t.src[offset : t.offset])
|
|
||||||
}
|
|
||||||
|
|
||||||
scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Token_Kind, string) {
|
|
||||||
scan_mantissa :: proc(t: ^Tokenizer, base: int) {
|
|
||||||
for digit_val(t.ch) < base {
|
|
||||||
advance_rune(t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scan_exponent :: proc(t: ^Tokenizer) {
|
|
||||||
if t.ch == 'e' || t.ch == 'E' || t.ch == 'p' || t.ch == 'P' {
|
|
||||||
advance_rune(t)
|
|
||||||
if t.ch == '-' || t.ch == '+' {
|
|
||||||
advance_rune(t)
|
|
||||||
}
|
|
||||||
if digit_val(t.ch) < 10 {
|
|
||||||
scan_mantissa(t, 10)
|
|
||||||
} else {
|
|
||||||
error_offset(t, t.offset, "illegal floating-point exponent")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scan_fraction :: proc(t: ^Tokenizer) -> (early_exit: bool) {
|
|
||||||
if t.ch == '.' && peek(t) == '.' {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if t.ch == '.' {
|
|
||||||
advance_rune(t)
|
|
||||||
scan_mantissa(t, 10)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
check_end := true
|
|
||||||
|
|
||||||
|
|
||||||
offset := t.offset
|
|
||||||
seen_point := seen_decimal_point
|
|
||||||
|
|
||||||
if seen_point {
|
|
||||||
offset -= 1
|
|
||||||
scan_mantissa(t, 10)
|
|
||||||
scan_exponent(t)
|
|
||||||
} else {
|
|
||||||
if t.ch == '0' {
|
|
||||||
int_base :: proc(t: ^Tokenizer, base: int, msg: string) {
|
|
||||||
prev := t.offset
|
|
||||||
advance_rune(t)
|
|
||||||
scan_mantissa(t, base)
|
|
||||||
if t.offset - prev <= 1 {
|
|
||||||
error_offset(t, t.offset, msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
advance_rune(t)
|
|
||||||
switch t.ch {
|
|
||||||
case 'b', 'B':
|
|
||||||
int_base(t, 2, "illegal binary integer")
|
|
||||||
case 'x', 'X':
|
|
||||||
int_base(t, 16, "illegal hexadecimal integer")
|
|
||||||
case:
|
|
||||||
seen_point = false
|
|
||||||
scan_mantissa(t, 10)
|
|
||||||
if t.ch == '.' {
|
|
||||||
seen_point = true
|
|
||||||
if scan_fraction(t) {
|
|
||||||
check_end = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if check_end {
|
|
||||||
scan_exponent(t)
|
|
||||||
check_end = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if check_end {
|
|
||||||
scan_mantissa(t, 10)
|
|
||||||
|
|
||||||
if !scan_fraction(t) {
|
|
||||||
scan_exponent(t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return .Number, string(t.src[offset : t.offset])
|
|
||||||
}
|
|
||||||
|
|
||||||
scan_punct :: proc(t: ^Tokenizer, ch: rune) -> (kind: Token_Kind) {
|
|
||||||
kind = .Punct
|
|
||||||
switch ch {
|
|
||||||
case:
|
|
||||||
kind = .Invalid
|
|
||||||
|
|
||||||
case '<', '>':
|
|
||||||
if t.ch == ch {
|
|
||||||
advance_rune(t)
|
|
||||||
}
|
|
||||||
if t.ch == '=' {
|
|
||||||
advance_rune(t)
|
|
||||||
}
|
|
||||||
case '!', '+', '-', '*', '/', '%', '^', '=':
|
|
||||||
if t.ch == '=' {
|
|
||||||
advance_rune(t)
|
|
||||||
}
|
|
||||||
case '#':
|
|
||||||
if t.ch == '#' {
|
|
||||||
advance_rune(t)
|
|
||||||
}
|
|
||||||
case '&':
|
|
||||||
if t.ch == '=' || t.ch == '&' {
|
|
||||||
advance_rune(t)
|
|
||||||
}
|
|
||||||
case '|':
|
|
||||||
if t.ch == '=' || t.ch == '|' {
|
|
||||||
advance_rune(t)
|
|
||||||
}
|
|
||||||
case '(', ')', '[', ']', '{', '}':
|
|
||||||
// okay
|
|
||||||
case '~', ',', ':', ';', '?':
|
|
||||||
// okay
|
|
||||||
case '`':
|
|
||||||
// okay
|
|
||||||
case '.':
|
|
||||||
if t.ch == '.' && peek(t) == '.' {
|
|
||||||
advance_rune(t)
|
|
||||||
advance_rune(t) // consume last '.'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
peek :: proc(t: ^Tokenizer) -> byte {
|
|
||||||
if t.read_offset < len(t.src) {
|
|
||||||
return t.src[t.read_offset]
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
peek_str :: proc(t: ^Tokenizer, str: string) -> bool {
|
|
||||||
if t.read_offset < len(t.src) {
|
|
||||||
return strings.has_prefix(string(t.src[t.offset:]), str)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
scan_literal_prefix :: proc(t: ^Tokenizer, str: string, prefix: ^string) -> bool {
|
|
||||||
if peek_str(t, str) {
|
|
||||||
offset := t.offset
|
|
||||||
for _ in str {
|
|
||||||
advance_rune(t)
|
|
||||||
}
|
|
||||||
prefix^ = string(t.src[offset:][:len(str)-1])
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
allow_next_to_be_newline :: proc(t: ^Tokenizer) -> bool {
|
|
||||||
if t.ch == '\n' {
|
|
||||||
advance_rune(t)
|
|
||||||
return true
|
|
||||||
} else if t.ch == '\r' && peek(t) == '\n' { // allow for MS-DOS style line endings
|
|
||||||
advance_rune(t) // \r
|
|
||||||
advance_rune(t) // \n
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
scan :: proc(t: ^Tokenizer, f: ^File) -> ^Token {
|
|
||||||
skip_whitespace(t)
|
|
||||||
|
|
||||||
offset := t.offset
|
|
||||||
|
|
||||||
kind: Token_Kind
|
|
||||||
lit: string
|
|
||||||
prefix: string
|
|
||||||
|
|
||||||
switch ch := t.ch; {
|
|
||||||
case scan_literal_prefix(t, `u8"`, &prefix):
|
|
||||||
kind = .String
|
|
||||||
lit = scan_string(t)
|
|
||||||
case scan_literal_prefix(t, `u"`, &prefix):
|
|
||||||
kind = .String
|
|
||||||
lit = scan_string(t)
|
|
||||||
case scan_literal_prefix(t, `L"`, &prefix):
|
|
||||||
kind = .String
|
|
||||||
lit = scan_string(t)
|
|
||||||
case scan_literal_prefix(t, `U"`, &prefix):
|
|
||||||
kind = .String
|
|
||||||
lit = scan_string(t)
|
|
||||||
case scan_literal_prefix(t, `u'`, &prefix):
|
|
||||||
kind = .Char
|
|
||||||
lit = scan_rune(t)
|
|
||||||
case scan_literal_prefix(t, `L'`, &prefix):
|
|
||||||
kind = .Char
|
|
||||||
lit = scan_rune(t)
|
|
||||||
case scan_literal_prefix(t, `U'`, &prefix):
|
|
||||||
kind = .Char
|
|
||||||
lit = scan_rune(t)
|
|
||||||
|
|
||||||
case is_ident0(ch):
|
|
||||||
lit = scan_identifier(t)
|
|
||||||
kind = .Ident
|
|
||||||
case '0' <= ch && ch <= '9':
|
|
||||||
kind, lit = scan_number(t, false)
|
|
||||||
case:
|
|
||||||
advance_rune(t)
|
|
||||||
switch ch {
|
|
||||||
case -1:
|
|
||||||
kind = .EOF
|
|
||||||
case '\\':
|
|
||||||
kind = .Punct
|
|
||||||
if allow_next_to_be_newline(t) {
|
|
||||||
t.at_bol = true
|
|
||||||
t.has_space = false
|
|
||||||
return scan(t, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
case '.':
|
|
||||||
if is_digit(t.ch) {
|
|
||||||
kind, lit = scan_number(t, true)
|
|
||||||
} else {
|
|
||||||
kind = scan_punct(t, ch)
|
|
||||||
}
|
|
||||||
case '"':
|
|
||||||
kind = .String
|
|
||||||
lit = scan_string(t)
|
|
||||||
case '\'':
|
|
||||||
kind = .Char
|
|
||||||
lit = scan_rune(t)
|
|
||||||
case '/':
|
|
||||||
if t.ch == '/' || t.ch == '*' {
|
|
||||||
kind = .Comment
|
|
||||||
lit = scan_comment(t)
|
|
||||||
t.has_space = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
fallthrough
|
|
||||||
case:
|
|
||||||
kind = scan_punct(t, ch)
|
|
||||||
if kind == .Invalid && ch != utf8.RUNE_BOM {
|
|
||||||
error_offset(t, t.offset, "illegal character '%r': %d", ch, ch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if lit == "" {
|
|
||||||
lit = string(t.src[offset : t.offset])
|
|
||||||
}
|
|
||||||
|
|
||||||
if kind == .Comment {
|
|
||||||
return scan(t, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
tok := new(Token)
|
|
||||||
tok.kind = kind
|
|
||||||
tok.lit = lit
|
|
||||||
tok.pos = offset_to_pos(t, offset)
|
|
||||||
tok.file = f
|
|
||||||
tok.prefix = prefix
|
|
||||||
tok.at_bol = t.at_bol
|
|
||||||
tok.has_space = t.has_space
|
|
||||||
|
|
||||||
t.at_bol, t.has_space = false, false
|
|
||||||
|
|
||||||
return tok
|
|
||||||
}
|
|
||||||
|
|
||||||
tokenize :: proc(t: ^Tokenizer, f: ^File) -> ^Token {
|
|
||||||
setup_tokenizer: {
|
|
||||||
t.src = f.src
|
|
||||||
t.ch = ' '
|
|
||||||
t.offset = 0
|
|
||||||
t.read_offset = 0
|
|
||||||
t.line_offset = 0
|
|
||||||
t.line_count = len(t.src) > 0 ? 1 : 0
|
|
||||||
t.error_count = 0
|
|
||||||
t.path = f.name
|
|
||||||
|
|
||||||
|
|
||||||
advance_rune(t)
|
|
||||||
if t.ch == utf8.RUNE_BOM {
|
|
||||||
advance_rune(t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
t.at_bol = true
|
|
||||||
t.has_space = false
|
|
||||||
|
|
||||||
head: Token
|
|
||||||
curr := &head
|
|
||||||
for {
|
|
||||||
tok := scan(t, f)
|
|
||||||
if tok == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
curr.next = tok
|
|
||||||
curr = curr.next
|
|
||||||
if tok.kind == .EOF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return head.next
|
|
||||||
}
|
|
||||||
|
|
||||||
add_new_file :: proc(t: ^Tokenizer, name: string, src: []byte, id: int) -> ^File {
|
|
||||||
file := new(File)
|
|
||||||
file.id = id
|
|
||||||
file.src = src
|
|
||||||
file.name = name
|
|
||||||
file.display_name = name
|
|
||||||
return file
|
|
||||||
}
|
|
||||||
|
|
||||||
tokenize_file :: proc(t: ^Tokenizer, path: string, id: int, loc := #caller_location) -> ^Token {
|
|
||||||
src, ok := os.read_entire_file(path)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return tokenize(t, add_new_file(t, path, src, id))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline_tokenize :: proc(t: ^Tokenizer, tok: ^Token, src: []byte) -> ^Token {
|
|
||||||
file := new(File)
|
|
||||||
file.src = src
|
|
||||||
if tok.file != nil {
|
|
||||||
file.id = tok.file.id
|
|
||||||
file.name = tok.file.name
|
|
||||||
file.display_name = tok.file.name
|
|
||||||
}
|
|
||||||
|
|
||||||
return tokenize(t, file)
|
|
||||||
}
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
package c_frontend_tokenizer
|
|
||||||
|
|
||||||
|
|
||||||
in_range :: proc(range: []rune, c: rune) -> bool #no_bounds_check {
|
|
||||||
for i := 0; range[i] != -1; i += 2 {
|
|
||||||
if range[i] <= c && c <= range[i+1] {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// [https://www.sigbus.info/n1570#D] C11 allows ASCII and some multibyte characters in certan Unicode ranges to be used in an identifier.
|
|
||||||
//
|
|
||||||
// is_ident0 returns true if a given character is acceptable as the first character of an identifier.
|
|
||||||
is_ident0 :: proc(c: rune) -> bool {
|
|
||||||
return in_range(_range_ident0, c)
|
|
||||||
}
|
|
||||||
// is_ident0 returns true if a given character is acceptable as a non-first character of an identifier.
|
|
||||||
is_ident1 :: proc(c: rune) -> bool {
|
|
||||||
return is_ident0(c) || in_range(_range_ident1, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the number of columns needed to display a given character in a fixed-width font.
|
|
||||||
// Based on https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
|
|
||||||
char_width :: proc(c: rune) -> int {
|
|
||||||
switch {
|
|
||||||
case in_range(_range_width0, c):
|
|
||||||
return 0
|
|
||||||
case in_range(_range_width2, c):
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
display_width :: proc(str: string) -> (w: int) {
|
|
||||||
for c in str {
|
|
||||||
w += char_width(c)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_range_ident0 := []rune{
|
|
||||||
'_', '_', 'a', 'z', 'A', 'Z', '$', '$',
|
|
||||||
0x00A8, 0x00A8, 0x00AA, 0x00AA, 0x00AD, 0x00AD, 0x00AF, 0x00AF,
|
|
||||||
0x00B2, 0x00B5, 0x00B7, 0x00BA, 0x00BC, 0x00BE, 0x00C0, 0x00D6,
|
|
||||||
0x00D8, 0x00F6, 0x00F8, 0x00FF, 0x0100, 0x02FF, 0x0370, 0x167F,
|
|
||||||
0x1681, 0x180D, 0x180F, 0x1DBF, 0x1E00, 0x1FFF, 0x200B, 0x200D,
|
|
||||||
0x202A, 0x202E, 0x203F, 0x2040, 0x2054, 0x2054, 0x2060, 0x206F,
|
|
||||||
0x2070, 0x20CF, 0x2100, 0x218F, 0x2460, 0x24FF, 0x2776, 0x2793,
|
|
||||||
0x2C00, 0x2DFF, 0x2E80, 0x2FFF, 0x3004, 0x3007, 0x3021, 0x302F,
|
|
||||||
0x3031, 0x303F, 0x3040, 0xD7FF, 0xF900, 0xFD3D, 0xFD40, 0xFDCF,
|
|
||||||
0xFDF0, 0xFE1F, 0xFE30, 0xFE44, 0xFE47, 0xFFFD,
|
|
||||||
0x10000, 0x1FFFD, 0x20000, 0x2FFFD, 0x30000, 0x3FFFD, 0x40000, 0x4FFFD,
|
|
||||||
0x50000, 0x5FFFD, 0x60000, 0x6FFFD, 0x70000, 0x7FFFD, 0x80000, 0x8FFFD,
|
|
||||||
0x90000, 0x9FFFD, 0xA0000, 0xAFFFD, 0xB0000, 0xBFFFD, 0xC0000, 0xCFFFD,
|
|
||||||
0xD0000, 0xDFFFD, 0xE0000, 0xEFFFD,
|
|
||||||
-1,
|
|
||||||
}
|
|
||||||
|
|
||||||
_range_ident1 := []rune{
|
|
||||||
'0', '9', '$', '$', 0x0300, 0x036F, 0x1DC0, 0x1DFF, 0x20D0, 0x20FF, 0xFE20, 0xFE2F,
|
|
||||||
-1,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
_range_width0 := []rune{
|
|
||||||
0x0000, 0x001F, 0x007f, 0x00a0, 0x0300, 0x036F, 0x0483, 0x0486,
|
|
||||||
0x0488, 0x0489, 0x0591, 0x05BD, 0x05BF, 0x05BF, 0x05C1, 0x05C2,
|
|
||||||
0x05C4, 0x05C5, 0x05C7, 0x05C7, 0x0600, 0x0603, 0x0610, 0x0615,
|
|
||||||
0x064B, 0x065E, 0x0670, 0x0670, 0x06D6, 0x06E4, 0x06E7, 0x06E8,
|
|
||||||
0x06EA, 0x06ED, 0x070F, 0x070F, 0x0711, 0x0711, 0x0730, 0x074A,
|
|
||||||
0x07A6, 0x07B0, 0x07EB, 0x07F3, 0x0901, 0x0902, 0x093C, 0x093C,
|
|
||||||
0x0941, 0x0948, 0x094D, 0x094D, 0x0951, 0x0954, 0x0962, 0x0963,
|
|
||||||
0x0981, 0x0981, 0x09BC, 0x09BC, 0x09C1, 0x09C4, 0x09CD, 0x09CD,
|
|
||||||
0x09E2, 0x09E3, 0x0A01, 0x0A02, 0x0A3C, 0x0A3C, 0x0A41, 0x0A42,
|
|
||||||
0x0A47, 0x0A48, 0x0A4B, 0x0A4D, 0x0A70, 0x0A71, 0x0A81, 0x0A82,
|
|
||||||
0x0ABC, 0x0ABC, 0x0AC1, 0x0AC5, 0x0AC7, 0x0AC8, 0x0ACD, 0x0ACD,
|
|
||||||
0x0AE2, 0x0AE3, 0x0B01, 0x0B01, 0x0B3C, 0x0B3C, 0x0B3F, 0x0B3F,
|
|
||||||
0x0B41, 0x0B43, 0x0B4D, 0x0B4D, 0x0B56, 0x0B56, 0x0B82, 0x0B82,
|
|
||||||
0x0BC0, 0x0BC0, 0x0BCD, 0x0BCD, 0x0C3E, 0x0C40, 0x0C46, 0x0C48,
|
|
||||||
0x0C4A, 0x0C4D, 0x0C55, 0x0C56, 0x0CBC, 0x0CBC, 0x0CBF, 0x0CBF,
|
|
||||||
0x0CC6, 0x0CC6, 0x0CCC, 0x0CCD, 0x0CE2, 0x0CE3, 0x0D41, 0x0D43,
|
|
||||||
0x0D4D, 0x0D4D, 0x0DCA, 0x0DCA, 0x0DD2, 0x0DD4, 0x0DD6, 0x0DD6,
|
|
||||||
0x0E31, 0x0E31, 0x0E34, 0x0E3A, 0x0E47, 0x0E4E, 0x0EB1, 0x0EB1,
|
|
||||||
0x0EB4, 0x0EB9, 0x0EBB, 0x0EBC, 0x0EC8, 0x0ECD, 0x0F18, 0x0F19,
|
|
||||||
0x0F35, 0x0F35, 0x0F37, 0x0F37, 0x0F39, 0x0F39, 0x0F71, 0x0F7E,
|
|
||||||
0x0F80, 0x0F84, 0x0F86, 0x0F87, 0x0F90, 0x0F97, 0x0F99, 0x0FBC,
|
|
||||||
0x0FC6, 0x0FC6, 0x102D, 0x1030, 0x1032, 0x1032, 0x1036, 0x1037,
|
|
||||||
0x1039, 0x1039, 0x1058, 0x1059, 0x1160, 0x11FF, 0x135F, 0x135F,
|
|
||||||
0x1712, 0x1714, 0x1732, 0x1734, 0x1752, 0x1753, 0x1772, 0x1773,
|
|
||||||
0x17B4, 0x17B5, 0x17B7, 0x17BD, 0x17C6, 0x17C6, 0x17C9, 0x17D3,
|
|
||||||
0x17DD, 0x17DD, 0x180B, 0x180D, 0x18A9, 0x18A9, 0x1920, 0x1922,
|
|
||||||
0x1927, 0x1928, 0x1932, 0x1932, 0x1939, 0x193B, 0x1A17, 0x1A18,
|
|
||||||
0x1B00, 0x1B03, 0x1B34, 0x1B34, 0x1B36, 0x1B3A, 0x1B3C, 0x1B3C,
|
|
||||||
0x1B42, 0x1B42, 0x1B6B, 0x1B73, 0x1DC0, 0x1DCA, 0x1DFE, 0x1DFF,
|
|
||||||
0x200B, 0x200F, 0x202A, 0x202E, 0x2060, 0x2063, 0x206A, 0x206F,
|
|
||||||
0x20D0, 0x20EF, 0x302A, 0x302F, 0x3099, 0x309A, 0xA806, 0xA806,
|
|
||||||
0xA80B, 0xA80B, 0xA825, 0xA826, 0xFB1E, 0xFB1E, 0xFE00, 0xFE0F,
|
|
||||||
0xFE20, 0xFE23, 0xFEFF, 0xFEFF, 0xFFF9, 0xFFFB, 0x10A01, 0x10A03,
|
|
||||||
0x10A05, 0x10A06, 0x10A0C, 0x10A0F, 0x10A38, 0x10A3A, 0x10A3F, 0x10A3F,
|
|
||||||
0x1D167, 0x1D169, 0x1D173, 0x1D182, 0x1D185, 0x1D18B, 0x1D1AA, 0x1D1AD,
|
|
||||||
0x1D242, 0x1D244, 0xE0001, 0xE0001, 0xE0020, 0xE007F, 0xE0100, 0xE01EF,
|
|
||||||
-1,
|
|
||||||
}
|
|
||||||
|
|
||||||
_range_width2 := []rune{
|
|
||||||
0x1100, 0x115F, 0x2329, 0x2329, 0x232A, 0x232A, 0x2E80, 0x303E,
|
|
||||||
0x3040, 0xA4CF, 0xAC00, 0xD7A3, 0xF900, 0xFAFF, 0xFE10, 0xFE19,
|
|
||||||
0xFE30, 0xFE6F, 0xFF00, 0xFF60, 0xFFE0, 0xFFE6, 0x1F000, 0x1F644,
|
|
||||||
0x20000, 0x2FFFD, 0x30000, 0x3FFFD,
|
|
||||||
-1,
|
|
||||||
}
|
|
||||||
@@ -88,14 +88,15 @@ when ODIN_OS == .Haiku {
|
|||||||
_get_errno :: proc() -> ^int ---
|
_get_errno :: proc() -> ^int ---
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private="file")
|
_HAIKU_USE_POSITIVE_POSIX_ERRORS :: #config(HAIKU_USE_POSITIVE_POSIX_ERRORS, false)
|
||||||
B_GENERAL_ERROR_BASE :: min(i32)
|
_POSIX_ERROR_FACTOR :: -1 when _HAIKU_USE_POSITIVE_POSIX_ERRORS else 1
|
||||||
@(private="file")
|
|
||||||
B_POSIX_ERROR_BASE :: B_GENERAL_ERROR_BASE + 0x7000
|
|
||||||
|
|
||||||
EDOM :: B_POSIX_ERROR_BASE + 16
|
@(private="file") _GENERAL_ERROR_BASE :: min(int)
|
||||||
EILSEQ :: B_POSIX_ERROR_BASE + 38
|
@(private="file") _POSIX_ERROR_BASE :: _GENERAL_ERROR_BASE + 0x7000
|
||||||
ERANGE :: B_POSIX_ERROR_BASE + 17
|
|
||||||
|
EDOM :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 16)
|
||||||
|
EILSEQ :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 38)
|
||||||
|
ERANGE :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 17)
|
||||||
}
|
}
|
||||||
|
|
||||||
when ODIN_OS == .JS {
|
when ODIN_OS == .JS {
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ when ODIN_OS == .Windows {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Windows {
|
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku || ODIN_OS == .Windows {
|
||||||
|
|
||||||
LC_ALL :: 0
|
LC_ALL :: 0
|
||||||
LC_COLLATE :: 1
|
LC_COLLATE :: 1
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package libc
|
package libc
|
||||||
|
|
||||||
|
import "core:c"
|
||||||
import "core:io"
|
import "core:io"
|
||||||
|
|
||||||
when ODIN_OS == .Windows {
|
when ODIN_OS == .Windows {
|
||||||
@@ -15,7 +16,7 @@ when ODIN_OS == .Windows {
|
|||||||
|
|
||||||
// 7.21 Input/output
|
// 7.21 Input/output
|
||||||
|
|
||||||
FILE :: struct {}
|
FILE :: c.FILE
|
||||||
|
|
||||||
Whence :: enum int {
|
Whence :: enum int {
|
||||||
SET = SEEK_SET,
|
SET = SEEK_SET,
|
||||||
|
|||||||
@@ -42,6 +42,21 @@ when ODIN_OS == .Linux {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
when ODIN_OS == .Haiku {
|
||||||
|
RAND_MAX :: 0x7fffffff
|
||||||
|
|
||||||
|
// GLIBC and MUSL only
|
||||||
|
@(private="file")
|
||||||
|
@(default_calling_convention="c")
|
||||||
|
foreign libc {
|
||||||
|
__ctype_get_mb_cur_max :: proc() -> ushort ---
|
||||||
|
}
|
||||||
|
|
||||||
|
MB_CUR_MAX :: #force_inline proc() -> size_t {
|
||||||
|
return size_t(__ctype_get_mb_cur_max())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD {
|
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD {
|
||||||
RAND_MAX :: 0x7fffffff
|
RAND_MAX :: 0x7fffffff
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS =
|
|||||||
|
|
||||||
time_t :: distinct i64
|
time_t :: distinct i64
|
||||||
|
|
||||||
when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD {
|
when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku {
|
||||||
clock_t :: distinct int32_t
|
clock_t :: distinct int32_t
|
||||||
} else {
|
} else {
|
||||||
clock_t :: distinct long
|
clock_t :: distinct long
|
||||||
|
|||||||
@@ -278,19 +278,19 @@ Example:
|
|||||||
iterate_next_example :: proc() {
|
iterate_next_example :: proc() {
|
||||||
l: list.List
|
l: list.List
|
||||||
|
|
||||||
one := My_Struct{value=1}
|
one := My_Next_Struct{value=1}
|
||||||
two := My_Struct{value=2}
|
two := My_Next_Struct{value=2}
|
||||||
|
|
||||||
list.push_back(&l, &one.node)
|
list.push_back(&l, &one.node)
|
||||||
list.push_back(&l, &two.node)
|
list.push_back(&l, &two.node)
|
||||||
|
|
||||||
it := list.iterator_head(l, My_Struct, "node")
|
it := list.iterator_head(l, My_Next_Struct, "node")
|
||||||
for num in list.iterate_next(&it) {
|
for num in list.iterate_next(&it) {
|
||||||
fmt.println(num.value)
|
fmt.println(num.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
My_Struct :: struct {
|
My_Next_Struct :: struct {
|
||||||
node : list.Node,
|
node : list.Node,
|
||||||
value: int,
|
value: int,
|
||||||
}
|
}
|
||||||
@@ -325,22 +325,22 @@ Example:
|
|||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:container/intrusive/list"
|
import "core:container/intrusive/list"
|
||||||
|
|
||||||
iterate_next_example :: proc() {
|
iterate_prev_example :: proc() {
|
||||||
l: list.List
|
l: list.List
|
||||||
|
|
||||||
one := My_Struct{value=1}
|
one := My_Prev_Struct{value=1}
|
||||||
two := My_Struct{value=2}
|
two := My_Prev_Struct{value=2}
|
||||||
|
|
||||||
list.push_back(&l, &one.node)
|
list.push_back(&l, &one.node)
|
||||||
list.push_back(&l, &two.node)
|
list.push_back(&l, &two.node)
|
||||||
|
|
||||||
it := list.iterator_tail(l, My_Struct, "node")
|
it := list.iterator_tail(l, My_Prev_Struct, "node")
|
||||||
for num in list.iterate_prev(&it) {
|
for num in list.iterate_prev(&it) {
|
||||||
fmt.println(num.value)
|
fmt.println(num.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
My_Struct :: struct {
|
My_Prev_Struct :: struct {
|
||||||
node : list.Node,
|
node : list.Node,
|
||||||
value: int,
|
value: int,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package container_priority_queue
|
package container_priority_queue
|
||||||
|
|
||||||
import "base:builtin"
|
import "base:builtin"
|
||||||
|
import "base:runtime"
|
||||||
|
|
||||||
Priority_Queue :: struct($T: typeid) {
|
Priority_Queue :: struct($T: typeid) {
|
||||||
queue: [dynamic]T,
|
queue: [dynamic]T,
|
||||||
@@ -17,13 +18,14 @@ default_swap_proc :: proc($T: typeid) -> proc(q: []T, i, j: int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init :: proc(pq: ^$Q/Priority_Queue($T), less: proc(a, b: T) -> bool, swap: proc(q: []T, i, j: int), capacity := DEFAULT_CAPACITY, allocator := context.allocator) {
|
init :: proc(pq: ^$Q/Priority_Queue($T), less: proc(a, b: T) -> bool, swap: proc(q: []T, i, j: int), capacity := DEFAULT_CAPACITY, allocator := context.allocator) -> (err: runtime.Allocator_Error) {
|
||||||
if pq.queue.allocator.procedure == nil {
|
if pq.queue.allocator.procedure == nil {
|
||||||
pq.queue.allocator = allocator
|
pq.queue.allocator = allocator
|
||||||
}
|
}
|
||||||
reserve(pq, capacity)
|
reserve(pq, capacity) or_return
|
||||||
pq.less = less
|
pq.less = less
|
||||||
pq.swap = swap
|
pq.swap = swap
|
||||||
|
return .None
|
||||||
}
|
}
|
||||||
|
|
||||||
init_from_dynamic_array :: proc(pq: ^$Q/Priority_Queue($T), queue: [dynamic]T, less: proc(a, b: T) -> bool, swap: proc(q: []T, i, j: int)) {
|
init_from_dynamic_array :: proc(pq: ^$Q/Priority_Queue($T), queue: [dynamic]T, less: proc(a, b: T) -> bool, swap: proc(q: []T, i, j: int)) {
|
||||||
@@ -41,8 +43,8 @@ destroy :: proc(pq: ^$Q/Priority_Queue($T)) {
|
|||||||
delete(pq.queue)
|
delete(pq.queue)
|
||||||
}
|
}
|
||||||
|
|
||||||
reserve :: proc(pq: ^$Q/Priority_Queue($T), capacity: int) {
|
reserve :: proc(pq: ^$Q/Priority_Queue($T), capacity: int) -> (err: runtime.Allocator_Error) {
|
||||||
builtin.reserve(&pq.queue, capacity)
|
return builtin.reserve(&pq.queue, capacity)
|
||||||
}
|
}
|
||||||
clear :: proc(pq: ^$Q/Priority_Queue($T)) {
|
clear :: proc(pq: ^$Q/Priority_Queue($T)) {
|
||||||
builtin.clear(&pq.queue)
|
builtin.clear(&pq.queue)
|
||||||
@@ -103,9 +105,10 @@ fix :: proc(pq: ^$Q/Priority_Queue($T), i: int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
push :: proc(pq: ^$Q/Priority_Queue($T), value: T) {
|
push :: proc(pq: ^$Q/Priority_Queue($T), value: T) -> (err: runtime.Allocator_Error) {
|
||||||
append(&pq.queue, value)
|
append(&pq.queue, value) or_return
|
||||||
_shift_up(pq, builtin.len(pq.queue)-1)
|
_shift_up(pq, builtin.len(pq.queue)-1)
|
||||||
|
return .None
|
||||||
}
|
}
|
||||||
|
|
||||||
pop :: proc(pq: ^$Q/Priority_Queue($T), loc := #caller_location) -> (value: T) {
|
pop :: proc(pq: ^$Q/Priority_Queue($T), loc := #caller_location) -> (value: T) {
|
||||||
|
|||||||
@@ -46,8 +46,7 @@ init_with_contents :: proc(q: ^$Q/Queue($T), backing: []T) -> bool {
|
|||||||
cap = builtin.len(backing),
|
cap = builtin.len(backing),
|
||||||
allocator = {procedure=runtime.nil_allocator_proc, data=nil},
|
allocator = {procedure=runtime.nil_allocator_proc, data=nil},
|
||||||
}
|
}
|
||||||
q.len = len(backing)
|
q.len = builtin.len(backing)
|
||||||
q.offset = len(backing)
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
Package small_array implements a dynamic array like
|
||||||
|
interface on a stack-allocated, fixed-size array.
|
||||||
|
|
||||||
|
The Small_Array type is optimal for scenarios where you need
|
||||||
|
a container for a fixed number of elements of a specific type,
|
||||||
|
with the total number known at compile time but the exact
|
||||||
|
number to be used determined at runtime.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:container/small_array"
|
||||||
|
|
||||||
|
create :: proc() -> (result: small_array.Small_Array(10, rune)) {
|
||||||
|
// appending single elements
|
||||||
|
small_array.push(&result, 'e')
|
||||||
|
// pushing a bunch of elements at once
|
||||||
|
small_array.push(&result, 'l', 'i', 'x', '-', 'e')
|
||||||
|
// pre-pending
|
||||||
|
small_array.push_front(&result, 'H')
|
||||||
|
// removing elements
|
||||||
|
small_array.ordered_remove(&result, 4)
|
||||||
|
// resizing to the desired length (the capacity will stay unchanged)
|
||||||
|
small_array.resize(&result, 7)
|
||||||
|
// inserting elements
|
||||||
|
small_array.inject_at(&result, 'p', 5)
|
||||||
|
// updating elements
|
||||||
|
small_array.set(&result, 3, 'l')
|
||||||
|
// getting pointers to elements
|
||||||
|
o := small_array.get_ptr(&result, 4)
|
||||||
|
o^ = 'o'
|
||||||
|
// and much more ....
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// the Small_Array can be an ordinary parameter 'generic' over
|
||||||
|
// the actual length to be usable with different sizes
|
||||||
|
print_elements :: proc(arr: ^small_array.Small_Array($N, rune)) {
|
||||||
|
for r in small_array.slice(arr) {
|
||||||
|
fmt.print(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main :: proc() {
|
||||||
|
arr := create()
|
||||||
|
// ...
|
||||||
|
print_elements(&arr)
|
||||||
|
}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
Hellope
|
||||||
|
|
||||||
|
*/
|
||||||
|
package container_small_array
|
||||||
@@ -4,36 +4,171 @@ import "base:builtin"
|
|||||||
import "base:runtime"
|
import "base:runtime"
|
||||||
_ :: runtime
|
_ :: runtime
|
||||||
|
|
||||||
|
/*
|
||||||
|
A fixed-size stack-allocated array operated on in a dynamic fashion.
|
||||||
|
|
||||||
|
Fields:
|
||||||
|
- `data`: The underlying array
|
||||||
|
- `len`: Amount of items that the `Small_Array` currently holds
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
import "core:container/small_array"
|
||||||
|
|
||||||
|
example :: proc() {
|
||||||
|
a: small_array.Small_Array(100, int)
|
||||||
|
small_array.push_back(&a, 10)
|
||||||
|
}
|
||||||
|
*/
|
||||||
Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
|
Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
|
||||||
data: [N]T,
|
data: [N]T,
|
||||||
len: int,
|
len: int,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Returns the amount of items in the small-array.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- `a`: The small-array
|
||||||
|
|
||||||
|
**Returns**
|
||||||
|
- the amount of items in the array
|
||||||
|
*/
|
||||||
len :: proc "contextless" (a: $A/Small_Array) -> int {
|
len :: proc "contextless" (a: $A/Small_Array) -> int {
|
||||||
return a.len
|
return a.len
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Returns the capacity of the small-array.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- `a`: The small-array
|
||||||
|
|
||||||
|
**Returns** the capacity
|
||||||
|
*/
|
||||||
cap :: proc "contextless" (a: $A/Small_Array) -> int {
|
cap :: proc "contextless" (a: $A/Small_Array) -> int {
|
||||||
return builtin.len(a.data)
|
return builtin.len(a.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Returns how many more items the small-array could fit.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- `a`: The small-array
|
||||||
|
|
||||||
|
**Returns**
|
||||||
|
- the number of unused slots
|
||||||
|
*/
|
||||||
space :: proc "contextless" (a: $A/Small_Array) -> int {
|
space :: proc "contextless" (a: $A/Small_Array) -> int {
|
||||||
return builtin.len(a.data) - a.len
|
return builtin.len(a.data) - a.len
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Returns a slice of the data.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- `a`: The pointer to the small-array
|
||||||
|
|
||||||
|
**Returns**
|
||||||
|
- the slice
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
import "core:container/small_array"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
slice_example :: proc() {
|
||||||
|
print :: proc(a: ^small_array.Small_Array($N, int)) {
|
||||||
|
for item in small_array.slice(a) {
|
||||||
|
fmt.println(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a: small_array.Small_Array(5, int)
|
||||||
|
small_array.push_back(&a, 1)
|
||||||
|
small_array.push_back(&a, 2)
|
||||||
|
print(&a)
|
||||||
|
}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
1
|
||||||
|
2
|
||||||
|
*/
|
||||||
slice :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> []T {
|
slice :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> []T {
|
||||||
return a.data[:a.len]
|
return a.data[:a.len]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get a copy of the item at the specified position.
|
||||||
|
This operation assumes that the small-array is large enough.
|
||||||
|
|
||||||
|
This will result in:
|
||||||
|
- the value if 0 <= index < len
|
||||||
|
- the zero value of the type if len < index < capacity
|
||||||
|
- 'crash' if capacity < index or index < 0
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- `a`: The small-array
|
||||||
|
- `index`: The position of the item to get
|
||||||
|
|
||||||
|
**Returns**
|
||||||
|
- the element at the specified position
|
||||||
|
*/
|
||||||
get :: proc "contextless" (a: $A/Small_Array($N, $T), index: int) -> T {
|
get :: proc "contextless" (a: $A/Small_Array($N, $T), index: int) -> T {
|
||||||
return a.data[index]
|
return a.data[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get a pointer to the item at the specified position.
|
||||||
|
This operation assumes that the small-array is large enough.
|
||||||
|
|
||||||
|
This will result in:
|
||||||
|
- the pointer if 0 <= index < len
|
||||||
|
- the pointer to the zero value if len < index < capacity
|
||||||
|
- 'crash' if capacity < index or index < 0
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- `a`: A pointer to the small-array
|
||||||
|
- `index`: The position of the item to get
|
||||||
|
|
||||||
|
**Returns**
|
||||||
|
- the pointer to the element at the specified position
|
||||||
|
*/
|
||||||
get_ptr :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int) -> ^T {
|
get_ptr :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int) -> ^T {
|
||||||
return &a.data[index]
|
return &a.data[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Attempt to get a copy of the item at the specified position.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- `a`: The small-array
|
||||||
|
- `index`: The position of the item to get
|
||||||
|
|
||||||
|
**Returns**
|
||||||
|
- the element at the specified position
|
||||||
|
- true if element exists, false otherwise
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
import "core:container/small_array"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
get_safe_example :: proc() {
|
||||||
|
a: small_array.Small_Array(5, rune)
|
||||||
|
small_array.push_back(&a, 'A')
|
||||||
|
|
||||||
|
fmt.println(small_array.get_safe(a, 0) or_else 'x')
|
||||||
|
fmt.println(small_array.get_safe(a, 1) or_else 'x')
|
||||||
|
}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
A
|
||||||
|
x
|
||||||
|
|
||||||
|
*/
|
||||||
get_safe :: proc(a: $A/Small_Array($N, $T), index: int) -> (T, bool) #no_bounds_check {
|
get_safe :: proc(a: $A/Small_Array($N, $T), index: int) -> (T, bool) #no_bounds_check {
|
||||||
if index < 0 || index >= a.len {
|
if index < 0 || index >= a.len {
|
||||||
return {}, false
|
return {}, false
|
||||||
@@ -41,6 +176,17 @@ get_safe :: proc(a: $A/Small_Array($N, $T), index: int) -> (T, bool) #no_bounds_
|
|||||||
return a.data[index], true
|
return a.data[index], true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get a pointer to the item at the specified position.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- `a`: A pointer to the small-array
|
||||||
|
- `index`: The position of the item to get
|
||||||
|
|
||||||
|
**Returns**
|
||||||
|
- the pointer to the element at the specified position
|
||||||
|
- true if element exists, false otherwise
|
||||||
|
*/
|
||||||
get_ptr_safe :: proc(a: ^$A/Small_Array($N, $T), index: int) -> (^T, bool) #no_bounds_check {
|
get_ptr_safe :: proc(a: ^$A/Small_Array($N, $T), index: int) -> (^T, bool) #no_bounds_check {
|
||||||
if index < 0 || index >= a.len {
|
if index < 0 || index >= a.len {
|
||||||
return {}, false
|
return {}, false
|
||||||
@@ -48,15 +194,128 @@ get_ptr_safe :: proc(a: ^$A/Small_Array($N, $T), index: int) -> (^T, bool) #no_b
|
|||||||
return &a.data[index], true
|
return &a.data[index], true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Set the element at the specified position to the given value.
|
||||||
|
This operation assumes that the small-array is large enough.
|
||||||
|
|
||||||
|
This will result in:
|
||||||
|
- the value being set if 0 <= index < capacity
|
||||||
|
- 'crash' otherwise
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- `a`: A pointer to the small-array
|
||||||
|
- `index`: The position of the item to set
|
||||||
|
- `value`: The value to set the element to
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
import "core:container/small_array"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
set_example :: proc() {
|
||||||
|
a: small_array.Small_Array(5, rune)
|
||||||
|
small_array.push_back(&a, 'A')
|
||||||
|
small_array.push_back(&a, 'B')
|
||||||
|
fmt.println(small_array.slice(&a))
|
||||||
|
|
||||||
|
// updates index 0
|
||||||
|
small_array.set(&a, 0, 'Z')
|
||||||
|
fmt.println(small_array.slice(&a))
|
||||||
|
|
||||||
|
// updates to a position x, where
|
||||||
|
// len <= x < cap are not visible since
|
||||||
|
// the length of the small-array remains unchanged
|
||||||
|
small_array.set(&a, 2, 'X')
|
||||||
|
small_array.set(&a, 3, 'Y')
|
||||||
|
small_array.set(&a, 4, 'Z')
|
||||||
|
fmt.println(small_array.slice(&a))
|
||||||
|
|
||||||
|
// resizing makes the change visible
|
||||||
|
small_array.resize(&a, 100)
|
||||||
|
fmt.println(small_array.slice(&a))
|
||||||
|
}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
[A, B]
|
||||||
|
[Z, B]
|
||||||
|
[Z, B]
|
||||||
|
[Z, B, X, Y, Z]
|
||||||
|
|
||||||
|
*/
|
||||||
set :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, item: T) {
|
set :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, item: T) {
|
||||||
a.data[index] = item
|
a.data[index] = item
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Tries to resize the small-array to the specified length.
|
||||||
|
|
||||||
|
The new length will be:
|
||||||
|
- `length` if `length` <= capacity
|
||||||
|
- capacity if length > capacity
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- `a`: A pointer to the small-array
|
||||||
|
- `length`: The new desired length
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
import "core:container/small_array"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
resize_example :: proc() {
|
||||||
|
a: small_array.Small_Array(5, int)
|
||||||
|
|
||||||
|
small_array.push_back(&a, 1)
|
||||||
|
small_array.push_back(&a, 2)
|
||||||
|
fmt.println(small_array.slice(&a))
|
||||||
|
|
||||||
|
small_array.resize(&a, 1)
|
||||||
|
fmt.println(small_array.slice(&a))
|
||||||
|
|
||||||
|
small_array.resize(&a, 100)
|
||||||
|
fmt.println(small_array.slice(&a))
|
||||||
|
}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
[1, 2]
|
||||||
|
[1]
|
||||||
|
[1, 2, 0, 0, 0]
|
||||||
|
*/
|
||||||
resize :: proc "contextless" (a: ^$A/Small_Array, length: int) {
|
resize :: proc "contextless" (a: ^$A/Small_Array, length: int) {
|
||||||
a.len = min(length, builtin.len(a.data))
|
a.len = min(length, builtin.len(a.data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Attempts to add the given element to the end.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- `a`: A pointer to the small-array
|
||||||
|
- `item`: The item to append
|
||||||
|
|
||||||
|
**Returns**
|
||||||
|
- true if there was enough space to fit the element, false otherwise
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
import "core:container/small_array"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
push_back_example :: proc() {
|
||||||
|
a: small_array.Small_Array(2, int)
|
||||||
|
|
||||||
|
assert(small_array.push_back(&a, 1), "this should fit")
|
||||||
|
assert(small_array.push_back(&a, 2), "this should fit")
|
||||||
|
assert(!small_array.push_back(&a, 3), "this should not fit")
|
||||||
|
|
||||||
|
fmt.println(small_array.slice(&a))
|
||||||
|
}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
[1, 2]
|
||||||
|
*/
|
||||||
push_back :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
push_back :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||||
if a.len < cap(a^) {
|
if a.len < cap(a^) {
|
||||||
a.data[a.len] = item
|
a.data[a.len] = item
|
||||||
@@ -66,6 +325,39 @@ push_back :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- `a`: A pointer to the small-array
|
||||||
|
- `item`: The item to append
|
||||||
|
|
||||||
|
**Returns**
|
||||||
|
- true if there was enough space to fit the element, false otherwise
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
import "core:container/small_array"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
push_front_example :: proc() {
|
||||||
|
a: small_array.Small_Array(2, int)
|
||||||
|
|
||||||
|
assert(small_array.push_front(&a, 2), "this should fit")
|
||||||
|
assert(small_array.push_front(&a, 1), "this should fit")
|
||||||
|
assert(!small_array.push_back(&a, 0), "this should not fit")
|
||||||
|
|
||||||
|
fmt.println(small_array.slice(&a))
|
||||||
|
}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
[1, 2]
|
||||||
|
*/
|
||||||
push_front :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
push_front :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||||
if a.len < cap(a^) {
|
if a.len < cap(a^) {
|
||||||
a.len += 1
|
a.len += 1
|
||||||
@@ -77,6 +369,35 @@ push_front :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Removes and returns the last element of the small-array.
|
||||||
|
This operation assumes that the small-array is not empty.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- `a`: A pointer to the small-array
|
||||||
|
|
||||||
|
**Returns**
|
||||||
|
- a copy of the element removed from the end of the small-array
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
import "core:container/small_array"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
pop_back_example :: proc() {
|
||||||
|
a: small_array.Small_Array(5, int)
|
||||||
|
small_array.push(&a, 0, 1, 2)
|
||||||
|
|
||||||
|
fmt.println("BEFORE:", small_array.slice(&a))
|
||||||
|
small_array.pop_back(&a)
|
||||||
|
fmt.println("AFTER: ", small_array.slice(&a))
|
||||||
|
}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
BEFORE: [0, 1, 2]
|
||||||
|
AFTER: [0, 1]
|
||||||
|
*/
|
||||||
pop_back :: proc "odin" (a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
pop_back :: proc "odin" (a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||||
assert(condition=(N > 0 && a.len > 0), loc=loc)
|
assert(condition=(N > 0 && a.len > 0), loc=loc)
|
||||||
item := a.data[a.len-1]
|
item := a.data[a.len-1]
|
||||||
@@ -84,6 +405,38 @@ pop_back :: proc "odin" (a: ^$A/Small_Array($N, $T), loc := #caller_location) ->
|
|||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- `a`: A pointer to the small-array
|
||||||
|
|
||||||
|
**Returns**
|
||||||
|
- a copy of the element removed from the beginning of the small-array
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
import "core:container/small_array"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
pop_front_example :: proc() {
|
||||||
|
a: small_array.Small_Array(5, int)
|
||||||
|
small_array.push(&a, 0, 1, 2)
|
||||||
|
|
||||||
|
fmt.println("BEFORE:", small_array.slice(&a))
|
||||||
|
small_array.pop_front(&a)
|
||||||
|
fmt.println("AFTER: ", small_array.slice(&a))
|
||||||
|
}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
BEFORE: [0, 1, 2]
|
||||||
|
AFTER: [1, 2]
|
||||||
|
*/
|
||||||
pop_front :: proc "odin" (a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
pop_front :: proc "odin" (a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||||
assert(condition=(N > 0 && a.len > 0), loc=loc)
|
assert(condition=(N > 0 && a.len > 0), loc=loc)
|
||||||
item := a.data[0]
|
item := a.data[0]
|
||||||
@@ -93,6 +446,32 @@ pop_front :: proc "odin" (a: ^$A/Small_Array($N, $T), loc := #caller_location) -
|
|||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Attempts to remove and return the last element of the small array.
|
||||||
|
Unlike `pop_back`, it does not assume that the array is non-empty.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- `a`: A pointer to the small-array
|
||||||
|
|
||||||
|
**Returns**
|
||||||
|
- a copy of the element removed from the end of the small-array
|
||||||
|
- true if the small-array was not empty, false otherwise
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
import "core:container/small_array"
|
||||||
|
|
||||||
|
pop_back_safe_example :: proc() {
|
||||||
|
a: small_array.Small_Array(3, int)
|
||||||
|
small_array.push(&a, 1)
|
||||||
|
|
||||||
|
el, ok := small_array.pop_back_safe(&a)
|
||||||
|
assert(ok, "there was an element in the array")
|
||||||
|
|
||||||
|
el, ok = small_array.pop_back_safe(&a)
|
||||||
|
assert(!ok, "there was NO element in the array")
|
||||||
|
}
|
||||||
|
*/
|
||||||
pop_back_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
pop_back_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
||||||
if N > 0 && a.len > 0 {
|
if N > 0 && a.len > 0 {
|
||||||
item = a.data[a.len-1]
|
item = a.data[a.len-1]
|
||||||
@@ -102,6 +481,35 @@ pop_back_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> (item: T, ok
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- `a`: A pointer to the small-array
|
||||||
|
|
||||||
|
**Returns**
|
||||||
|
- a copy of the element removed from the beginning of the small-array
|
||||||
|
- true if the small-array was not empty, false otherwise
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
import "core:container/small_array"
|
||||||
|
|
||||||
|
pop_front_safe_example :: proc() {
|
||||||
|
a: small_array.Small_Array(3, int)
|
||||||
|
small_array.push(&a, 1)
|
||||||
|
|
||||||
|
el, ok := small_array.pop_front_safe(&a)
|
||||||
|
assert(ok, "there was an element in the array")
|
||||||
|
|
||||||
|
el, ok = small_array.pop_front_(&a)
|
||||||
|
assert(!ok, "there was NO element in the array")
|
||||||
|
}
|
||||||
|
*/
|
||||||
pop_front_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
pop_front_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
||||||
if N > 0 && a.len > 0 {
|
if N > 0 && a.len > 0 {
|
||||||
item = a.data[0]
|
item = a.data[0]
|
||||||
@@ -113,11 +521,70 @@ pop_front_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> (item: T, o
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Decreases the length of the small-array by the given amount.
|
||||||
|
The elements are therefore not really removed and can be
|
||||||
|
recovered by calling `resize`.
|
||||||
|
|
||||||
|
Note: This procedure assumes that the array has a sufficient length.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- `a`: A pointer to the small-array
|
||||||
|
- `count`: The amount the length should be reduced by
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
import "core:container/small_array"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
consume_example :: proc() {
|
||||||
|
a: small_array.Small_Array(3, int)
|
||||||
|
small_array.push(&a, 0, 1, 2)
|
||||||
|
|
||||||
|
fmt.println("BEFORE:", small_array.slice(&a))
|
||||||
|
small_array.consume(&a, 2)
|
||||||
|
fmt.println("AFTER :", small_array.slice(&a))
|
||||||
|
}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
BEFORE: [0, 1, 2]
|
||||||
|
AFTER : [0]
|
||||||
|
*/
|
||||||
consume :: proc "odin" (a: ^$A/Small_Array($N, $T), count: int, loc := #caller_location) {
|
consume :: proc "odin" (a: ^$A/Small_Array($N, $T), count: int, loc := #caller_location) {
|
||||||
assert(condition=a.len >= count, loc=loc)
|
assert(condition=a.len >= count, loc=loc)
|
||||||
a.len -= count
|
a.len -= count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- `a`: A pointer to the small-array
|
||||||
|
- `index`: The position of the element to remove
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
import "core:container/small_array"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
ordered_remove_example :: proc() {
|
||||||
|
a: small_array.Small_Array(4, int)
|
||||||
|
small_array.push(&a, 0, 1, 2, 3)
|
||||||
|
|
||||||
|
fmt.println("BEFORE:", small_array.slice(&a))
|
||||||
|
small_array.ordered_remove(&a, 1)
|
||||||
|
fmt.println("AFTER :", small_array.slice(&a))
|
||||||
|
}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
BEFORE: [0, 1, 2, 3]
|
||||||
|
AFTER : [0, 2, 3]
|
||||||
|
*/
|
||||||
ordered_remove :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, loc := #caller_location) #no_bounds_check {
|
ordered_remove :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, loc := #caller_location) #no_bounds_check {
|
||||||
runtime.bounds_check_error_loc(loc, index, a.len)
|
runtime.bounds_check_error_loc(loc, index, a.len)
|
||||||
if index+1 < a.len {
|
if index+1 < a.len {
|
||||||
@@ -126,6 +593,32 @@ ordered_remove :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, lo
|
|||||||
a.len -= 1
|
a.len -= 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Removes the element at the specified index without retaining order.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- `a`: A pointer to the small-array
|
||||||
|
- `index`: The position of the element to remove
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
import "core:container/small_array"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
unordered_remove_example :: proc() {
|
||||||
|
a: small_array.Small_Array(4, int)
|
||||||
|
small_array.push(&a, 0, 1, 2, 3)
|
||||||
|
|
||||||
|
fmt.println("BEFORE:", small_array.slice(&a))
|
||||||
|
small_array.unordered_remove(&a, 1)
|
||||||
|
fmt.println("AFTER :", small_array.slice(&a))
|
||||||
|
}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
BEFORE: [0, 1, 2, 3]
|
||||||
|
AFTER : [0, 3, 2]
|
||||||
|
*/
|
||||||
unordered_remove :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, loc := #caller_location) #no_bounds_check {
|
unordered_remove :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, loc := #caller_location) #no_bounds_check {
|
||||||
runtime.bounds_check_error_loc(loc, index, a.len)
|
runtime.bounds_check_error_loc(loc, index, a.len)
|
||||||
n := a.len-1
|
n := a.len-1
|
||||||
@@ -135,10 +628,63 @@ unordered_remove :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int,
|
|||||||
a.len -= 1
|
a.len -= 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Sets the length of the small-array to 0.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- `a`: A pointer to the small-array
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
import "core:container/small_array"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
clear_example :: proc() {
|
||||||
|
a: small_array.Small_Array(4, int)
|
||||||
|
small_array.push(&a, 0, 1, 2, 3)
|
||||||
|
|
||||||
|
fmt.println("BEFORE:", small_array.slice(&a))
|
||||||
|
small_array.clear(&a)
|
||||||
|
fmt.println("AFTER :", small_array.slice(&a))
|
||||||
|
}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
BEFORE: [0, 1, 2, 3]
|
||||||
|
AFTER : []
|
||||||
|
|
||||||
|
*/
|
||||||
clear :: proc "contextless" (a: ^$A/Small_Array($N, $T)) {
|
clear :: proc "contextless" (a: ^$A/Small_Array($N, $T)) {
|
||||||
resize(a, 0)
|
resize(a, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Attempts to append all elements to the small-array returning
|
||||||
|
false if there is not enough space to fit all of them.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- `a`: A pointer to the small-array
|
||||||
|
- `item`: The item to append
|
||||||
|
- ..:
|
||||||
|
|
||||||
|
**Returns**
|
||||||
|
- true if there was enough space to fit the element, false otherwise
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
import "core:container/small_array"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
push_back_elems_example :: proc() {
|
||||||
|
a: small_array.Small_Array(100, int)
|
||||||
|
small_array.push_back_elems(&a, 0, 1, 2, 3, 4)
|
||||||
|
fmt.println(small_array.slice(&a))
|
||||||
|
}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
[0, 1, 2, 3, 4]
|
||||||
|
*/
|
||||||
push_back_elems :: proc "contextless" (a: ^$A/Small_Array($N, $T), items: ..T) -> bool {
|
push_back_elems :: proc "contextless" (a: ^$A/Small_Array($N, $T), items: ..T) -> bool {
|
||||||
if a.len + builtin.len(items) <= cap(a^) {
|
if a.len + builtin.len(items) <= cap(a^) {
|
||||||
n := copy(a.data[a.len:], items[:])
|
n := copy(a.data[a.len:], items[:])
|
||||||
@@ -148,6 +694,36 @@ push_back_elems :: proc "contextless" (a: ^$A/Small_Array($N, $T), items: ..T) -
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- `a`: A pointer to the small-array
|
||||||
|
- `item`: The item to insert
|
||||||
|
- `index`: The index to insert the item at
|
||||||
|
|
||||||
|
**Returns**
|
||||||
|
- true if there was enough space to fit the element, false otherwise
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
import "core:container/small_array"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
inject_at_example :: proc() {
|
||||||
|
arr: small_array.Small_Array(100, rune)
|
||||||
|
small_array.push(&arr, 'A', 'C', 'D')
|
||||||
|
small_array.inject_at(&arr, 'B', 1)
|
||||||
|
fmt.println(small_array.slice(&arr))
|
||||||
|
}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
[A, B, C, D]
|
||||||
|
*/
|
||||||
inject_at :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T, index: int) -> bool #no_bounds_check {
|
inject_at :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T, index: int) -> bool #no_bounds_check {
|
||||||
if a.len < cap(a^) && index >= 0 && index <= len(a^) {
|
if a.len < cap(a^) && index >= 0 && index <= len(a^) {
|
||||||
a.len += 1
|
a.len += 1
|
||||||
@@ -160,7 +736,38 @@ inject_at :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T, index: int
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Alias for `push_back`
|
||||||
append_elem :: push_back
|
append_elem :: push_back
|
||||||
|
// Alias for `push_back_elems`
|
||||||
append_elems :: push_back_elems
|
append_elems :: push_back_elems
|
||||||
|
|
||||||
|
/*
|
||||||
|
Tries to append the element(s) to the small-array.
|
||||||
|
|
||||||
|
**Inputs**
|
||||||
|
- `a`: A pointer to the small-array
|
||||||
|
- `item`: The item to append
|
||||||
|
- ..:
|
||||||
|
|
||||||
|
**Returns**
|
||||||
|
- true if there was enough space to fit the element, false otherwise
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
import "core:container/small_array"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
push_example :: proc() {
|
||||||
|
a: small_array.Small_Array(100, int)
|
||||||
|
small_array.push(&a, 0)
|
||||||
|
small_array.push(&a, 1, 2, 3, 4)
|
||||||
|
fmt.println(small_array.slice(&a))
|
||||||
|
}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
[0, 1, 2, 3, 4]
|
||||||
|
*/
|
||||||
push :: proc{push_back, push_back_elems}
|
push :: proc{push_back, push_back_elems}
|
||||||
|
// Alias for `push`
|
||||||
append :: proc{push_back, push_back_elems}
|
append :: proc{push_back, push_back_elems}
|
||||||
|
|||||||
@@ -25,4 +25,5 @@ GHASH_BLOCK_SIZE :: 16
|
|||||||
GHASH_TAG_SIZE :: 16
|
GHASH_TAG_SIZE :: 16
|
||||||
|
|
||||||
// RCON is the AES keyschedule round constants.
|
// RCON is the AES keyschedule round constants.
|
||||||
|
@(rodata)
|
||||||
RCON := [10]byte{0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36}
|
RCON := [10]byte{0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36}
|
||||||
|
|||||||
@@ -22,8 +22,6 @@
|
|||||||
|
|
||||||
package aes_ct64
|
package aes_ct64
|
||||||
|
|
||||||
import "base:intrinsics"
|
|
||||||
|
|
||||||
// Bitsliced AES for 64-bit general purpose (integer) registers. Each
|
// Bitsliced AES for 64-bit general purpose (integer) registers. Each
|
||||||
// invocation will process up to 4 blocks at a time. This implementation
|
// invocation will process up to 4 blocks at a time. This implementation
|
||||||
// is derived from the BearSSL ct64 code, and distributed under a 1-clause
|
// is derived from the BearSSL ct64 code, and distributed under a 1-clause
|
||||||
@@ -212,11 +210,8 @@ orthogonalize :: proc "contextless" (q: ^[8]u64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
interleave_in :: proc "contextless" (w: []u32) -> (q0, q1: u64) #no_bounds_check {
|
interleave_in :: proc "contextless" (w0, w1, w2, w3: u32) -> (q0, q1: u64) #no_bounds_check {
|
||||||
if len(w) < 4 {
|
x0, x1, x2, x3 := u64(w0), u64(w1), u64(w2), u64(w3)
|
||||||
intrinsics.trap()
|
|
||||||
}
|
|
||||||
x0, x1, x2, x3 := u64(w[0]), u64(w[1]), u64(w[2]), u64(w[3])
|
|
||||||
x0 |= (x0 << 16)
|
x0 |= (x0 << 16)
|
||||||
x1 |= (x1 << 16)
|
x1 |= (x1 << 16)
|
||||||
x2 |= (x2 << 16)
|
x2 |= (x2 << 16)
|
||||||
|
|||||||
@@ -22,12 +22,8 @@
|
|||||||
|
|
||||||
package aes_ct64
|
package aes_ct64
|
||||||
|
|
||||||
import "base:intrinsics"
|
|
||||||
|
|
||||||
add_round_key :: proc "contextless" (q: ^[8]u64, sk: []u64) #no_bounds_check {
|
add_round_key :: proc "contextless" (q: ^[8]u64, sk: []u64) #no_bounds_check {
|
||||||
if len(sk) < 8 {
|
ensure_contextless(len(sk) >= 8, "aes/ct64: invalid round key size")
|
||||||
intrinsics.trap()
|
|
||||||
}
|
|
||||||
|
|
||||||
q[0] ~= sk[0]
|
q[0] ~= sk[0]
|
||||||
q[1] ~= sk[1]
|
q[1] ~= sk[1]
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
|
|
||||||
package aes_ct64
|
package aes_ct64
|
||||||
|
|
||||||
import "base:intrinsics"
|
|
||||||
import "core:crypto/_aes"
|
import "core:crypto/_aes"
|
||||||
import "core:encoding/endian"
|
import "core:encoding/endian"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
@@ -42,7 +41,7 @@ sub_word :: proc "contextless" (x: u32) -> u32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(private, require_results)
|
@(private, require_results)
|
||||||
keysched :: proc(comp_skey: []u64, key: []byte) -> int {
|
keysched :: proc "contextless" (comp_skey: []u64, key: []byte) -> int {
|
||||||
num_rounds, key_len := 0, len(key)
|
num_rounds, key_len := 0, len(key)
|
||||||
switch key_len {
|
switch key_len {
|
||||||
case _aes.KEY_SIZE_128:
|
case _aes.KEY_SIZE_128:
|
||||||
@@ -52,7 +51,7 @@ keysched :: proc(comp_skey: []u64, key: []byte) -> int {
|
|||||||
case _aes.KEY_SIZE_256:
|
case _aes.KEY_SIZE_256:
|
||||||
num_rounds = _aes.ROUNDS_256
|
num_rounds = _aes.ROUNDS_256
|
||||||
case:
|
case:
|
||||||
panic("crypto/aes: invalid AES key size")
|
panic_contextless("crypto/aes: invalid AES key size")
|
||||||
}
|
}
|
||||||
|
|
||||||
skey: [60]u32 = ---
|
skey: [60]u32 = ---
|
||||||
@@ -78,7 +77,7 @@ keysched :: proc(comp_skey: []u64, key: []byte) -> int {
|
|||||||
|
|
||||||
q: [8]u64 = ---
|
q: [8]u64 = ---
|
||||||
for i, j := 0, 0; i < nkf; i, j = i + 4, j + 2 {
|
for i, j := 0, 0; i < nkf; i, j = i + 4, j + 2 {
|
||||||
q[0], q[4] = interleave_in(skey[i:])
|
q[0], q[4] = interleave_in(skey[i], skey[i+1], skey[i+2], skey[i+3])
|
||||||
q[1] = q[0]
|
q[1] = q[0]
|
||||||
q[2] = q[0]
|
q[2] = q[0]
|
||||||
q[3] = q[0]
|
q[3] = q[0]
|
||||||
@@ -123,57 +122,3 @@ skey_expand :: proc "contextless" (skey, comp_skey: []u64, num_rounds: int) {
|
|||||||
skey[v + 3] = (x3 << 4) - x3
|
skey[v + 3] = (x3 << 4) - x3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
orthogonalize_roundkey :: proc "contextless" (qq: []u64, key: []byte) {
|
|
||||||
if len(qq) < 8 || len(key) != 16 {
|
|
||||||
intrinsics.trap()
|
|
||||||
}
|
|
||||||
|
|
||||||
skey: [4]u32 = ---
|
|
||||||
skey[0] = endian.unchecked_get_u32le(key[0:])
|
|
||||||
skey[1] = endian.unchecked_get_u32le(key[4:])
|
|
||||||
skey[2] = endian.unchecked_get_u32le(key[8:])
|
|
||||||
skey[3] = endian.unchecked_get_u32le(key[12:])
|
|
||||||
|
|
||||||
q: [8]u64 = ---
|
|
||||||
q[0], q[4] = interleave_in(skey[:])
|
|
||||||
q[1] = q[0]
|
|
||||||
q[2] = q[0]
|
|
||||||
q[3] = q[0]
|
|
||||||
q[5] = q[4]
|
|
||||||
q[6] = q[4]
|
|
||||||
q[7] = q[4]
|
|
||||||
orthogonalize(&q)
|
|
||||||
|
|
||||||
comp_skey: [2]u64 = ---
|
|
||||||
comp_skey[0] =
|
|
||||||
(q[0] & 0x1111111111111111) |
|
|
||||||
(q[1] & 0x2222222222222222) |
|
|
||||||
(q[2] & 0x4444444444444444) |
|
|
||||||
(q[3] & 0x8888888888888888)
|
|
||||||
comp_skey[1] =
|
|
||||||
(q[4] & 0x1111111111111111) |
|
|
||||||
(q[5] & 0x2222222222222222) |
|
|
||||||
(q[6] & 0x4444444444444444) |
|
|
||||||
(q[7] & 0x8888888888888888)
|
|
||||||
|
|
||||||
for x, u in comp_skey {
|
|
||||||
x0 := x
|
|
||||||
x1, x2, x3 := x0, x0, x0
|
|
||||||
x0 &= 0x1111111111111111
|
|
||||||
x1 &= 0x2222222222222222
|
|
||||||
x2 &= 0x4444444444444444
|
|
||||||
x3 &= 0x8888888888888888
|
|
||||||
x1 >>= 1
|
|
||||||
x2 >>= 2
|
|
||||||
x3 >>= 3
|
|
||||||
qq[u * 4 + 0] = (x0 << 4) - x0
|
|
||||||
qq[u * 4 + 1] = (x1 << 4) - x1
|
|
||||||
qq[u * 4 + 2] = (x2 << 4) - x2
|
|
||||||
qq[u * 4 + 3] = (x3 << 4) - x3
|
|
||||||
}
|
|
||||||
|
|
||||||
mem.zero_explicit(&skey, size_of(skey))
|
|
||||||
mem.zero_explicit(&q, size_of(q))
|
|
||||||
mem.zero_explicit(&comp_skey, size_of(comp_skey))
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
|
|
||||||
package aes_ct64
|
package aes_ct64
|
||||||
|
|
||||||
import "base:intrinsics"
|
|
||||||
import "core:crypto/_aes"
|
import "core:crypto/_aes"
|
||||||
import "core:encoding/endian"
|
import "core:encoding/endian"
|
||||||
|
|
||||||
@@ -64,9 +63,8 @@ rev64 :: proc "contextless" (x: u64) -> u64 {
|
|||||||
// Note: `dst` is both an input and an output, to support easy implementation
|
// Note: `dst` is both an input and an output, to support easy implementation
|
||||||
// of GCM.
|
// of GCM.
|
||||||
ghash :: proc "contextless" (dst, key, data: []byte) {
|
ghash :: proc "contextless" (dst, key, data: []byte) {
|
||||||
if len(dst) != _aes.GHASH_BLOCK_SIZE || len(key) != _aes.GHASH_BLOCK_SIZE {
|
ensure_contextless(len(dst) == _aes.GHASH_BLOCK_SIZE)
|
||||||
intrinsics.trap()
|
ensure_contextless(len(key) == _aes.GHASH_BLOCK_SIZE)
|
||||||
}
|
|
||||||
|
|
||||||
buf := data
|
buf := data
|
||||||
l := len(buf)
|
l := len(buf)
|
||||||
|
|||||||
@@ -1,60 +1,61 @@
|
|||||||
package aes_ct64
|
package aes_ct64
|
||||||
|
|
||||||
import "base:intrinsics"
|
|
||||||
import "core:crypto/_aes"
|
import "core:crypto/_aes"
|
||||||
import "core:encoding/endian"
|
import "core:encoding/endian"
|
||||||
|
|
||||||
load_blockx1 :: proc "contextless" (q: ^[8]u64, src: []byte) {
|
@(require_results)
|
||||||
if len(src) != _aes.BLOCK_SIZE {
|
load_interleaved :: proc "contextless" (src: []byte) -> (u64, u64) #no_bounds_check {
|
||||||
intrinsics.trap()
|
w0 := endian.unchecked_get_u32le(src[0:])
|
||||||
}
|
w1 := endian.unchecked_get_u32le(src[4:])
|
||||||
|
w2 := endian.unchecked_get_u32le(src[8:])
|
||||||
w: [4]u32 = ---
|
w3 := endian.unchecked_get_u32le(src[12:])
|
||||||
w[0] = endian.unchecked_get_u32le(src[0:])
|
return interleave_in(w0, w1, w2, w3)
|
||||||
w[1] = endian.unchecked_get_u32le(src[4:])
|
|
||||||
w[2] = endian.unchecked_get_u32le(src[8:])
|
|
||||||
w[3] = endian.unchecked_get_u32le(src[12:])
|
|
||||||
q[0], q[4] = interleave_in(w[:])
|
|
||||||
orthogonalize(q)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
store_blockx1 :: proc "contextless" (dst: []byte, q: ^[8]u64) {
|
store_interleaved :: proc "contextless" (dst: []byte, a0, a1: u64) #no_bounds_check {
|
||||||
if len(dst) != _aes.BLOCK_SIZE {
|
w0, w1, w2, w3 := interleave_out(a0, a1)
|
||||||
intrinsics.trap()
|
|
||||||
}
|
|
||||||
|
|
||||||
orthogonalize(q)
|
|
||||||
w0, w1, w2, w3 := interleave_out(q[0], q[4])
|
|
||||||
endian.unchecked_put_u32le(dst[0:], w0)
|
endian.unchecked_put_u32le(dst[0:], w0)
|
||||||
endian.unchecked_put_u32le(dst[4:], w1)
|
endian.unchecked_put_u32le(dst[4:], w1)
|
||||||
endian.unchecked_put_u32le(dst[8:], w2)
|
endian.unchecked_put_u32le(dst[8:], w2)
|
||||||
endian.unchecked_put_u32le(dst[12:], w3)
|
endian.unchecked_put_u32le(dst[12:], w3)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
|
xor_interleaved :: #force_inline proc "contextless" (a0, a1, b0, b1: u64) -> (u64, u64) {
|
||||||
|
return a0 ~ b0, a1 ~ b1
|
||||||
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
|
and_interleaved :: #force_inline proc "contextless" (a0, a1, b0, b1: u64) -> (u64, u64) {
|
||||||
|
return a0 & b0, a1 & b1
|
||||||
|
}
|
||||||
|
|
||||||
|
load_blockx1 :: proc "contextless" (q: ^[8]u64, src: []byte) {
|
||||||
|
ensure_contextless(len(src) == _aes.BLOCK_SIZE, "aes/ct64: invalid block size")
|
||||||
|
|
||||||
|
q[0], q[4] = #force_inline load_interleaved(src)
|
||||||
|
orthogonalize(q)
|
||||||
|
}
|
||||||
|
|
||||||
|
store_blockx1 :: proc "contextless" (dst: []byte, q: ^[8]u64) {
|
||||||
|
ensure_contextless(len(dst) == _aes.BLOCK_SIZE, "aes/ct64: invalid block size")
|
||||||
|
|
||||||
|
orthogonalize(q)
|
||||||
|
#force_inline store_interleaved(dst, q[0], q[4])
|
||||||
|
}
|
||||||
|
|
||||||
load_blocks :: proc "contextless" (q: ^[8]u64, src: [][]byte) {
|
load_blocks :: proc "contextless" (q: ^[8]u64, src: [][]byte) {
|
||||||
if n := len(src); n > STRIDE || n == 0 {
|
ensure_contextless(len(src) == 0 || len(src) <= STRIDE, "aes/ct64: invalid block(s) size")
|
||||||
intrinsics.trap()
|
|
||||||
}
|
|
||||||
|
|
||||||
w: [4]u32 = ---
|
|
||||||
for s, i in src {
|
for s, i in src {
|
||||||
if len(s) != _aes.BLOCK_SIZE {
|
ensure_contextless(len(s) == _aes.BLOCK_SIZE, "aes/ct64: invalid block size")
|
||||||
intrinsics.trap()
|
q[i], q[i + 4] = #force_inline load_interleaved(s)
|
||||||
}
|
|
||||||
|
|
||||||
w[0] = endian.unchecked_get_u32le(s[0:])
|
|
||||||
w[1] = endian.unchecked_get_u32le(s[4:])
|
|
||||||
w[2] = endian.unchecked_get_u32le(s[8:])
|
|
||||||
w[3] = endian.unchecked_get_u32le(s[12:])
|
|
||||||
q[i], q[i + 4] = interleave_in(w[:])
|
|
||||||
}
|
}
|
||||||
orthogonalize(q)
|
orthogonalize(q)
|
||||||
}
|
}
|
||||||
|
|
||||||
store_blocks :: proc "contextless" (dst: [][]byte, q: ^[8]u64) {
|
store_blocks :: proc "contextless" (dst: [][]byte, q: ^[8]u64) {
|
||||||
if n := len(dst); n > STRIDE || n == 0 {
|
ensure_contextless(len(dst) == 0 || len(dst) <= STRIDE, "aes/ct64: invalid block(s) size")
|
||||||
intrinsics.trap()
|
|
||||||
}
|
|
||||||
|
|
||||||
orthogonalize(q)
|
orthogonalize(q)
|
||||||
for d, i in dst {
|
for d, i in dst {
|
||||||
@@ -62,14 +63,7 @@ store_blocks :: proc "contextless" (dst: [][]byte, q: ^[8]u64) {
|
|||||||
if d == nil {
|
if d == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if len(d) != _aes.BLOCK_SIZE {
|
ensure_contextless(len(d) == _aes.BLOCK_SIZE, "aes/ct64: invalid block size")
|
||||||
intrinsics.trap()
|
#force_inline store_interleaved(d, q[i], q[i + 4])
|
||||||
}
|
|
||||||
|
|
||||||
w0, w1, w2, w3 := interleave_out(q[i], q[i + 4])
|
|
||||||
endian.unchecked_put_u32le(d[0:], w0)
|
|
||||||
endian.unchecked_put_u32le(d[4:], w1)
|
|
||||||
endian.unchecked_put_u32le(d[8:], w2)
|
|
||||||
endian.unchecked_put_u32le(d[12:], w3)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ GHASH_STRIDE_BYTES_HW :: GHASH_STRIDE_HW * _aes.GHASH_BLOCK_SIZE
|
|||||||
// that it is right-shifted by 1 bit. The left-shift is relatively
|
// that it is right-shifted by 1 bit. The left-shift is relatively
|
||||||
// inexpensive, and it can be mutualised.
|
// inexpensive, and it can be mutualised.
|
||||||
//
|
//
|
||||||
// Since SSE2 opcodes do not have facilities for shitfting full 128-bit
|
// Since SSE2 opcodes do not have facilities for shifting full 128-bit
|
||||||
// values with bit precision, we have to break down values into 64-bit
|
// values with bit precision, we have to break down values into 64-bit
|
||||||
// chunks. We number chunks from 0 to 3 in left to right order.
|
// chunks. We number chunks from 0 to 3 in left to right order.
|
||||||
|
|
||||||
@@ -155,7 +155,7 @@ square_f128 :: #force_inline proc "contextless" (kw: x86.__m128i) -> (x86.__m128
|
|||||||
@(enable_target_feature = "sse2,ssse3,pclmul")
|
@(enable_target_feature = "sse2,ssse3,pclmul")
|
||||||
ghash :: proc "contextless" (dst, key, data: []byte) #no_bounds_check {
|
ghash :: proc "contextless" (dst, key, data: []byte) #no_bounds_check {
|
||||||
if len(dst) != _aes.GHASH_BLOCK_SIZE || len(key) != _aes.GHASH_BLOCK_SIZE {
|
if len(dst) != _aes.GHASH_BLOCK_SIZE || len(key) != _aes.GHASH_BLOCK_SIZE {
|
||||||
intrinsics.trap()
|
panic_contextless("aes/ghash: invalid dst or key size")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: BearSSL opts to copy the remainder into a zero-filled
|
// Note: BearSSL opts to copy the remainder into a zero-filled
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ BLAKE2S_SIZE :: 32
|
|||||||
BLAKE2B_BLOCK_SIZE :: 128
|
BLAKE2B_BLOCK_SIZE :: 128
|
||||||
BLAKE2B_SIZE :: 64
|
BLAKE2B_SIZE :: 64
|
||||||
|
|
||||||
|
MAX_SIZE :: 255
|
||||||
|
|
||||||
Blake2s_Context :: struct {
|
Blake2s_Context :: struct {
|
||||||
h: [8]u32,
|
h: [8]u32,
|
||||||
t: [2]u32,
|
t: [2]u32,
|
||||||
@@ -68,13 +70,13 @@ Blake2_Tree :: struct {
|
|||||||
is_last_node: bool,
|
is_last_node: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private, rodata)
|
||||||
BLAKE2S_IV := [8]u32 {
|
BLAKE2S_IV := [8]u32 {
|
||||||
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
|
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
|
||||||
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
|
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private, rodata)
|
||||||
BLAKE2B_IV := [8]u64 {
|
BLAKE2B_IV := [8]u64 {
|
||||||
0x6a09e667f3bcc908, 0xbb67ae8584caa73b,
|
0x6a09e667f3bcc908, 0xbb67ae8584caa73b,
|
||||||
0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
|
0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
|
||||||
@@ -82,16 +84,13 @@ BLAKE2B_IV := [8]u64 {
|
|||||||
0x1f83d9abfb41bd6b, 0x5be0cd19137e2179,
|
0x1f83d9abfb41bd6b, 0x5be0cd19137e2179,
|
||||||
}
|
}
|
||||||
|
|
||||||
init :: proc(ctx: ^$T, cfg: ^Blake2_Config) {
|
init :: proc "contextless" (ctx: ^$T, cfg: ^Blake2_Config) {
|
||||||
when T == Blake2s_Context {
|
when T == Blake2s_Context {
|
||||||
max_size :: BLAKE2S_SIZE
|
max_size :: BLAKE2S_SIZE
|
||||||
} else when T == Blake2b_Context {
|
} 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")
|
||||||
if cfg.size > max_size {
|
|
||||||
panic("blake2: requested output size exceeeds algorithm max")
|
|
||||||
}
|
|
||||||
|
|
||||||
// To save having to allocate a scratch buffer, use the internal
|
// To save having to allocate a scratch buffer, use the internal
|
||||||
// data buffer (`ctx.x`), as it is exactly the correct size.
|
// data buffer (`ctx.x`), as it is exactly the correct size.
|
||||||
@@ -167,8 +166,8 @@ init :: proc(ctx: ^$T, cfg: ^Blake2_Config) {
|
|||||||
ctx.is_initialized = true
|
ctx.is_initialized = true
|
||||||
}
|
}
|
||||||
|
|
||||||
update :: proc(ctx: ^$T, p: []byte) {
|
update :: proc "contextless" (ctx: ^$T, p: []byte) {
|
||||||
assert(ctx.is_initialized)
|
ensure_contextless(ctx.is_initialized)
|
||||||
|
|
||||||
p := p
|
p := p
|
||||||
when T == Blake2s_Context {
|
when T == Blake2s_Context {
|
||||||
@@ -195,8 +194,8 @@ update :: proc(ctx: ^$T, p: []byte) {
|
|||||||
ctx.nx += copy(ctx.x[ctx.nx:], p)
|
ctx.nx += copy(ctx.x[ctx.nx:], p)
|
||||||
}
|
}
|
||||||
|
|
||||||
final :: proc(ctx: ^$T, hash: []byte, finalize_clone: bool = false) {
|
final :: proc "contextless" (ctx: ^$T, hash: []byte, finalize_clone: bool = false) {
|
||||||
assert(ctx.is_initialized)
|
ensure_contextless(ctx.is_initialized)
|
||||||
|
|
||||||
ctx := ctx
|
ctx := ctx
|
||||||
if finalize_clone {
|
if finalize_clone {
|
||||||
@@ -206,24 +205,19 @@ final :: proc(ctx: ^$T, hash: []byte, finalize_clone: bool = false) {
|
|||||||
}
|
}
|
||||||
defer(reset(ctx))
|
defer(reset(ctx))
|
||||||
|
|
||||||
|
ensure_contextless(len(hash) >= int(ctx.size), "crypto/blake2: invalid destination digest size")
|
||||||
when T == Blake2s_Context {
|
when T == Blake2s_Context {
|
||||||
if len(hash) < int(ctx.size) {
|
|
||||||
panic("crypto/blake2s: invalid destination digest size")
|
|
||||||
}
|
|
||||||
blake2s_final(ctx, hash)
|
blake2s_final(ctx, hash)
|
||||||
} else when T == Blake2b_Context {
|
} else when T == Blake2b_Context {
|
||||||
if len(hash) < int(ctx.size) {
|
|
||||||
panic("crypto/blake2b: invalid destination digest size")
|
|
||||||
}
|
|
||||||
blake2b_final(ctx, hash)
|
blake2b_final(ctx, hash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clone :: proc(ctx, other: ^$T) {
|
clone :: proc "contextless" (ctx, other: ^$T) {
|
||||||
ctx^ = other^
|
ctx^ = other^
|
||||||
}
|
}
|
||||||
|
|
||||||
reset :: proc(ctx: ^$T) {
|
reset :: proc "contextless" (ctx: ^$T) {
|
||||||
if !ctx.is_initialized {
|
if !ctx.is_initialized {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package _chacha20
|
package _chacha20
|
||||||
|
|
||||||
import "base:intrinsics"
|
|
||||||
import "core:encoding/endian"
|
import "core:encoding/endian"
|
||||||
import "core:math/bits"
|
import "core:math/bits"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
@@ -46,9 +45,8 @@ Context :: struct {
|
|||||||
// derivation is expected to be handled by the caller, so that the
|
// derivation is expected to be handled by the caller, so that the
|
||||||
// HChaCha call can be suitably accelerated.
|
// HChaCha call can be suitably accelerated.
|
||||||
init :: proc "contextless" (ctx: ^Context, key, iv: []byte, is_xchacha: bool) {
|
init :: proc "contextless" (ctx: ^Context, key, iv: []byte, is_xchacha: bool) {
|
||||||
if len(key) != KEY_SIZE || len(iv) != IV_SIZE {
|
ensure_contextless(len(key) == KEY_SIZE, "chacha20: invalid key size")
|
||||||
intrinsics.trap()
|
ensure_contextless(len(iv) == IV_SIZE, "chacha20: invalid key size")
|
||||||
}
|
|
||||||
|
|
||||||
k, n := key, iv
|
k, n := key, iv
|
||||||
|
|
||||||
@@ -76,12 +74,10 @@ init :: proc "contextless" (ctx: ^Context, key, iv: []byte, is_xchacha: bool) {
|
|||||||
|
|
||||||
// seek seeks the (X)ChaCha20 stream counter to the specified block.
|
// seek seeks the (X)ChaCha20 stream counter to the specified block.
|
||||||
seek :: proc(ctx: ^Context, block_nr: u64) {
|
seek :: proc(ctx: ^Context, block_nr: u64) {
|
||||||
assert(ctx._is_initialized)
|
ensure(ctx._is_initialized)
|
||||||
|
|
||||||
if ctx._is_ietf_flavor {
|
if ctx._is_ietf_flavor {
|
||||||
if block_nr > MAX_CTR_IETF {
|
ensure(block_nr <= MAX_CTR_IETF, "crypto/chacha20: attempted to seek past maximum counter")
|
||||||
panic("crypto/chacha20: attempted to seek past maximum counter")
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
ctx._s[13] = u32(block_nr >> 32)
|
ctx._s[13] = u32(block_nr >> 32)
|
||||||
}
|
}
|
||||||
@@ -102,7 +98,7 @@ check_counter_limit :: proc(ctx: ^Context, nr_blocks: int) {
|
|||||||
// Enforce the maximum consumed keystream per IV.
|
// Enforce the maximum consumed keystream per IV.
|
||||||
//
|
//
|
||||||
// While all modern "standard" definitions of ChaCha20 use
|
// While all modern "standard" definitions of ChaCha20 use
|
||||||
// the IETF 32-bit counter, for XChaCha20 most common
|
// the IETF 32-bit counter, for XChaCha20 historical
|
||||||
// implementations allow for a 64-bit counter.
|
// implementations allow for a 64-bit counter.
|
||||||
//
|
//
|
||||||
// Honestly, the answer here is "use a MRAE primitive", but
|
// Honestly, the answer here is "use a MRAE primitive", but
|
||||||
@@ -110,14 +106,14 @@ check_counter_limit :: proc(ctx: ^Context, nr_blocks: int) {
|
|||||||
|
|
||||||
ERR_CTR_EXHAUSTED :: "crypto/chacha20: maximum (X)ChaCha20 keystream per IV reached"
|
ERR_CTR_EXHAUSTED :: "crypto/chacha20: maximum (X)ChaCha20 keystream per IV reached"
|
||||||
|
|
||||||
|
ctr_ok: bool
|
||||||
if ctx._is_ietf_flavor {
|
if ctx._is_ietf_flavor {
|
||||||
if u64(ctx._s[12]) + u64(nr_blocks) > MAX_CTR_IETF {
|
ctr_ok = u64(ctx._s[12]) + u64(nr_blocks) <= MAX_CTR_IETF
|
||||||
panic(ERR_CTR_EXHAUSTED)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
ctr := (u64(ctx._s[13]) << 32) | u64(ctx._s[12])
|
ctr := (u64(ctx._s[13]) << 32) | u64(ctx._s[12])
|
||||||
if _, carry := bits.add_u64(ctr, u64(nr_blocks), 0); carry != 0 {
|
_, carry := bits.add_u64(ctr, u64(nr_blocks), 0)
|
||||||
panic(ERR_CTR_EXHAUSTED)
|
ctr_ok = carry == 0
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ensure(ctr_ok, "crypto/chacha20: maximum (X)ChaCha20 keystream per IV reached")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,11 +29,24 @@ when ODIN_ARCH == .arm64 || ODIN_ARCH == .arm32 {
|
|||||||
// explicitly using simd.u8x16 shuffles.
|
// explicitly using simd.u8x16 shuffles.
|
||||||
@(private = "file")
|
@(private = "file")
|
||||||
TARGET_SIMD_FEATURES :: "sse2,ssse3"
|
TARGET_SIMD_FEATURES :: "sse2,ssse3"
|
||||||
|
} else when ODIN_ARCH == .riscv64 {
|
||||||
|
@(private = "file")
|
||||||
|
TARGET_SIMD_FEATURES :: "v"
|
||||||
} else {
|
} else {
|
||||||
@(private = "file")
|
@(private = "file")
|
||||||
TARGET_SIMD_FEATURES :: ""
|
TARGET_SIMD_FEATURES :: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Some targets lack runtime feature detection, and will flat out refuse
|
||||||
|
// to load binaries that have unknown instructions. This is distinct from
|
||||||
|
// `simd.IS_EMULATED` as actually good designs support runtime feature
|
||||||
|
// detection and that constant establishes a baseline.
|
||||||
|
//
|
||||||
|
// See:
|
||||||
|
// - https://github.com/WebAssembly/design/issues/1161
|
||||||
|
@(private = "file")
|
||||||
|
TARGET_IS_DESIGNED_BY_IDIOTS :: (ODIN_ARCH == .wasm64p32 || ODIN_ARCH == .wasm32) && !intrinsics.has_target_feature("simd128")
|
||||||
|
|
||||||
@(private = "file")
|
@(private = "file")
|
||||||
_ROT_7L: simd.u32x4 : {7, 7, 7, 7}
|
_ROT_7L: simd.u32x4 : {7, 7, 7, 7}
|
||||||
@(private = "file")
|
@(private = "file")
|
||||||
@@ -205,11 +218,13 @@ _store_simd128 :: #force_inline proc "contextless" (
|
|||||||
// is_performant returns true iff the target and current host both support
|
// is_performant returns true iff the target and current host both support
|
||||||
// "enough" 128-bit SIMD to make this implementation performant.
|
// "enough" 128-bit SIMD to make this implementation performant.
|
||||||
is_performant :: proc "contextless" () -> bool {
|
is_performant :: proc "contextless" () -> bool {
|
||||||
when ODIN_ARCH == .arm64 || ODIN_ARCH == .arm32 || ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 {
|
when ODIN_ARCH == .arm64 || ODIN_ARCH == .arm32 || ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 || ODIN_ARCH == .riscv64 {
|
||||||
when ODIN_ARCH == .arm64 || ODIN_ARCH == .arm32 {
|
when ODIN_ARCH == .arm64 || ODIN_ARCH == .arm32 {
|
||||||
req_features :: info.CPU_Features{.asimd}
|
req_features :: info.CPU_Features{.asimd}
|
||||||
} else when ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 {
|
} else when ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 {
|
||||||
req_features :: info.CPU_Features{.sse2, .ssse3}
|
req_features :: info.CPU_Features{.sse2, .ssse3}
|
||||||
|
} else when ODIN_ARCH == .riscv64 {
|
||||||
|
req_features :: info.CPU_Features{.V}
|
||||||
}
|
}
|
||||||
|
|
||||||
features, ok := info.cpu_features.?
|
features, ok := info.cpu_features.?
|
||||||
@@ -245,8 +260,17 @@ stream_blocks :: proc(ctx: ^_chacha20.Context, dst, src: []byte, nr_blocks: int)
|
|||||||
|
|
||||||
// 8 blocks at a time.
|
// 8 blocks at a time.
|
||||||
//
|
//
|
||||||
// Note: This is only worth it on Aarch64.
|
// Note:
|
||||||
when ODIN_ARCH == .arm64 {
|
// This uses a ton of registers so it is only worth it on targets
|
||||||
|
// that have something like 32 128-bit registers. This is currently
|
||||||
|
// all ARMv8 targets, and RISC-V Zvl128b (`V` application profile)
|
||||||
|
// targets.
|
||||||
|
//
|
||||||
|
// While our current definition of `.arm32` is 32-bit ARMv8, this
|
||||||
|
// may change in the future (ARMv7 is still relevant), and things
|
||||||
|
// like Cortex-A8/A9 does "pretend" 128-bit SIMD 64-bits at a time
|
||||||
|
// thus needs bemchmarking.
|
||||||
|
when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
|
||||||
for ; n >= 8; n = n - 8 {
|
for ; n >= 8; n = n - 8 {
|
||||||
v0, v1, v2, v3 := s0, s1, s2, s3
|
v0, v1, v2, v3 := s0, s1, s2, s3
|
||||||
|
|
||||||
@@ -354,9 +378,11 @@ stream_blocks :: proc(ctx: ^_chacha20.Context, dst, src: []byte, nr_blocks: int)
|
|||||||
|
|
||||||
// 4 blocks at a time.
|
// 4 blocks at a time.
|
||||||
//
|
//
|
||||||
// Note: The i386 target lacks the required number of registers
|
// Note: This is skipped on several targets for various reasons.
|
||||||
// for this to be performant, so it is skipped.
|
// - i386 lacks the required number of registers
|
||||||
when ODIN_ARCH != .i386 {
|
// - Generating code when runtime "hardware" SIMD support is impossible
|
||||||
|
// to detect is pointless, since this will be emulated using GP regs.
|
||||||
|
when ODIN_ARCH != .i386 && !TARGET_IS_DESIGNED_BY_IDIOTS {
|
||||||
for ; n >= 4; n = n - 4 {
|
for ; n >= 4; n = n - 4 {
|
||||||
v0, v1, v2, v3 := s0, s1, s2, s3
|
v0, v1, v2, v3 := s0, s1, s2, s3
|
||||||
|
|
||||||
|
|||||||
@@ -13,5 +13,5 @@ stream_blocks :: proc(ctx: ^_chacha20.Context, dst, src: []byte, nr_blocks: int)
|
|||||||
}
|
}
|
||||||
|
|
||||||
hchacha20 :: proc "contextless" (dst, key, iv: []byte) {
|
hchacha20 :: proc "contextless" (dst, key, iv: []byte) {
|
||||||
intrinsics.trap()
|
panic_contextless("crypto/chacha20: simd256 implementation unsupported")
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,6 @@ See:
|
|||||||
- https://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html
|
- https://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import "base:intrinsics"
|
|
||||||
import "core:crypto"
|
import "core:crypto"
|
||||||
import field "core:crypto/_fiat/field_curve25519"
|
import field "core:crypto/_fiat/field_curve25519"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
@@ -32,6 +31,7 @@ import "core:mem"
|
|||||||
// - The group element decoding routine takes the opinionated stance of
|
// - The group element decoding routine takes the opinionated stance of
|
||||||
// rejecting non-canonical encodings.
|
// rejecting non-canonical encodings.
|
||||||
|
|
||||||
|
@(rodata)
|
||||||
FE_D := field.Tight_Field_Element {
|
FE_D := field.Tight_Field_Element {
|
||||||
929955233495203,
|
929955233495203,
|
||||||
466365720129213,
|
466365720129213,
|
||||||
@@ -39,7 +39,7 @@ FE_D := field.Tight_Field_Element {
|
|||||||
2033849074728123,
|
2033849074728123,
|
||||||
1442794654840575,
|
1442794654840575,
|
||||||
}
|
}
|
||||||
@(private)
|
@(private, rodata)
|
||||||
FE_A := field.Tight_Field_Element {
|
FE_A := field.Tight_Field_Element {
|
||||||
2251799813685228,
|
2251799813685228,
|
||||||
2251799813685247,
|
2251799813685247,
|
||||||
@@ -47,7 +47,7 @@ FE_A := field.Tight_Field_Element {
|
|||||||
2251799813685247,
|
2251799813685247,
|
||||||
2251799813685247,
|
2251799813685247,
|
||||||
}
|
}
|
||||||
@(private)
|
@(private, rodata)
|
||||||
FE_D2 := field.Tight_Field_Element {
|
FE_D2 := field.Tight_Field_Element {
|
||||||
1859910466990425,
|
1859910466990425,
|
||||||
932731440258426,
|
932731440258426,
|
||||||
@@ -55,7 +55,7 @@ FE_D2 := field.Tight_Field_Element {
|
|||||||
1815898335770999,
|
1815898335770999,
|
||||||
633789495995903,
|
633789495995903,
|
||||||
}
|
}
|
||||||
@(private)
|
@(private, rodata)
|
||||||
GE_BASEPOINT := Group_Element {
|
GE_BASEPOINT := Group_Element {
|
||||||
field.Tight_Field_Element {
|
field.Tight_Field_Element {
|
||||||
1738742601995546,
|
1738742601995546,
|
||||||
@@ -80,6 +80,7 @@ GE_BASEPOINT := Group_Element {
|
|||||||
1821297809914039,
|
1821297809914039,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@(rodata)
|
||||||
GE_IDENTITY := Group_Element {
|
GE_IDENTITY := Group_Element {
|
||||||
field.Tight_Field_Element{0, 0, 0, 0, 0},
|
field.Tight_Field_Element{0, 0, 0, 0, 0},
|
||||||
field.Tight_Field_Element{1, 0, 0, 0, 0},
|
field.Tight_Field_Element{1, 0, 0, 0, 0},
|
||||||
@@ -107,9 +108,7 @@ ge_set :: proc "contextless" (ge, a: ^Group_Element) {
|
|||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
ge_set_bytes :: proc "contextless" (ge: ^Group_Element, b: []byte) -> bool {
|
ge_set_bytes :: proc "contextless" (ge: ^Group_Element, b: []byte) -> bool {
|
||||||
if len(b) != 32 {
|
ensure_contextless(len(b) == 32, "edwards25519: invalid group element size")
|
||||||
intrinsics.trap()
|
|
||||||
}
|
|
||||||
b_ := (^[32]byte)(raw_data(b))
|
b_ := (^[32]byte)(raw_data(b))
|
||||||
|
|
||||||
// Do the work in a scratch element, so that ge is unchanged on
|
// Do the work in a scratch element, so that ge is unchanged on
|
||||||
@@ -166,9 +165,7 @@ ge_set_bytes :: proc "contextless" (ge: ^Group_Element, b: []byte) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ge_bytes :: proc "contextless" (ge: ^Group_Element, dst: []byte) {
|
ge_bytes :: proc "contextless" (ge: ^Group_Element, dst: []byte) {
|
||||||
if len(dst) != 32 {
|
ensure_contextless(len(dst) == 32, "edwards25519: invalid group element size")
|
||||||
intrinsics.trap()
|
|
||||||
}
|
|
||||||
dst_ := (^[32]byte)(raw_data(dst))
|
dst_ := (^[32]byte)(raw_data(dst))
|
||||||
|
|
||||||
// Convert the element to affine (x, y) representation.
|
// Convert the element to affine (x, y) representation.
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package _edwards25519
|
package _edwards25519
|
||||||
|
|
||||||
import "base:intrinsics"
|
|
||||||
import field "core:crypto/_fiat/field_scalar25519"
|
import field "core:crypto/_fiat/field_scalar25519"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
|
||||||
@@ -8,7 +7,7 @@ Scalar :: field.Montgomery_Domain_Field_Element
|
|||||||
|
|
||||||
// WARNING: This is non-canonical and only to be used when checking if
|
// WARNING: This is non-canonical and only to be used when checking if
|
||||||
// a group element is on the prime-order subgroup.
|
// a group element is on the prime-order subgroup.
|
||||||
@(private)
|
@(private, rodata)
|
||||||
SC_ELL := field.Non_Montgomery_Domain_Field_Element {
|
SC_ELL := field.Non_Montgomery_Domain_Field_Element {
|
||||||
field.ELL[0],
|
field.ELL[0],
|
||||||
field.ELL[1],
|
field.ELL[1],
|
||||||
@@ -25,17 +24,13 @@ sc_set_u64 :: proc "contextless" (sc: ^Scalar, i: u64) {
|
|||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
sc_set_bytes :: proc "contextless" (sc: ^Scalar, b: []byte) -> bool {
|
sc_set_bytes :: proc "contextless" (sc: ^Scalar, b: []byte) -> bool {
|
||||||
if len(b) != 32 {
|
ensure_contextless(len(b) == 32, "edwards25519: invalid scalar size")
|
||||||
intrinsics.trap()
|
|
||||||
}
|
|
||||||
b_ := (^[32]byte)(raw_data(b))
|
b_ := (^[32]byte)(raw_data(b))
|
||||||
return field.fe_from_bytes(sc, b_)
|
return field.fe_from_bytes(sc, b_)
|
||||||
}
|
}
|
||||||
|
|
||||||
sc_set_bytes_rfc8032 :: proc "contextless" (sc: ^Scalar, b: []byte) {
|
sc_set_bytes_rfc8032 :: proc "contextless" (sc: ^Scalar, b: []byte) {
|
||||||
if len(b) != 32 {
|
ensure_contextless(len(b) == 32, "edwards25519: invalid scalar size")
|
||||||
intrinsics.trap()
|
|
||||||
}
|
|
||||||
b_ := (^[32]byte)(raw_data(b))
|
b_ := (^[32]byte)(raw_data(b))
|
||||||
field.fe_from_bytes_rfc8032(sc, b_)
|
field.fe_from_bytes_rfc8032(sc, b_)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,9 +42,12 @@ import "core:math/bits"
|
|||||||
Loose_Field_Element :: distinct [5]u64
|
Loose_Field_Element :: distinct [5]u64
|
||||||
Tight_Field_Element :: distinct [5]u64
|
Tight_Field_Element :: distinct [5]u64
|
||||||
|
|
||||||
|
@(rodata)
|
||||||
FE_ZERO := Tight_Field_Element{0, 0, 0, 0, 0}
|
FE_ZERO := Tight_Field_Element{0, 0, 0, 0, 0}
|
||||||
|
@(rodata)
|
||||||
FE_ONE := Tight_Field_Element{1, 0, 0, 0, 0}
|
FE_ONE := Tight_Field_Element{1, 0, 0, 0, 0}
|
||||||
|
|
||||||
|
@(rodata)
|
||||||
FE_SQRT_M1 := Tight_Field_Element {
|
FE_SQRT_M1 := Tight_Field_Element {
|
||||||
1718705420411056,
|
1718705420411056,
|
||||||
234908883556509,
|
234908883556509,
|
||||||
|
|||||||
@@ -0,0 +1,235 @@
|
|||||||
|
package field_curve448
|
||||||
|
|
||||||
|
import "core:mem"
|
||||||
|
|
||||||
|
fe_relax_cast :: #force_inline proc "contextless" (
|
||||||
|
arg1: ^Tight_Field_Element,
|
||||||
|
) -> ^Loose_Field_Element {
|
||||||
|
return (^Loose_Field_Element)(arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fe_tighten_cast :: #force_inline proc "contextless" (
|
||||||
|
arg1: ^Loose_Field_Element,
|
||||||
|
) -> ^Tight_Field_Element {
|
||||||
|
return (^Tight_Field_Element)(arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fe_clear :: proc "contextless" (
|
||||||
|
arg1: $T,
|
||||||
|
) where T == ^Tight_Field_Element || T == ^Loose_Field_Element {
|
||||||
|
mem.zero_explicit(arg1, size_of(arg1^))
|
||||||
|
}
|
||||||
|
|
||||||
|
fe_clear_vec :: proc "contextless" (
|
||||||
|
arg1: $T,
|
||||||
|
) where T == []^Tight_Field_Element || T == []^Loose_Field_Element {
|
||||||
|
for fe in arg1 {
|
||||||
|
fe_clear(fe)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fe_carry_mul_small :: proc "contextless" (
|
||||||
|
out1: ^Tight_Field_Element,
|
||||||
|
arg1: ^Loose_Field_Element,
|
||||||
|
arg2: u64,
|
||||||
|
) {
|
||||||
|
arg2_ := Loose_Field_Element{arg2, 0, 0, 0, 0, 0, 0, 0}
|
||||||
|
fe_carry_mul(out1, arg1, &arg2_)
|
||||||
|
}
|
||||||
|
|
||||||
|
fe_carry_pow2k :: proc "contextless" (
|
||||||
|
out1: ^Tight_Field_Element,
|
||||||
|
arg1: ^Loose_Field_Element,
|
||||||
|
arg2: uint,
|
||||||
|
) {
|
||||||
|
// Special case: `arg1^(2 * 0) = 1`, though this should never happen.
|
||||||
|
if arg2 == 0 {
|
||||||
|
fe_one(out1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fe_carry_square(out1, arg1)
|
||||||
|
for _ in 1 ..< arg2 {
|
||||||
|
fe_carry_square(out1, fe_relax_cast(out1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fe_carry_inv :: proc "contextless" (
|
||||||
|
out1: ^Tight_Field_Element,
|
||||||
|
arg1: ^Loose_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
|
||||||
|
// i34 = x24 << 6
|
||||||
|
// x30 = _111111 + i34
|
||||||
|
// x48 = i34 << 18 + x24
|
||||||
|
// x96 = x48 << 48 + x48
|
||||||
|
// x192 = x96 << 96 + x96
|
||||||
|
// x222 = x192 << 30 + x30
|
||||||
|
// x223 = 2*x222 + 1
|
||||||
|
// return (x223 << 223 + x222) << 2 + 1
|
||||||
|
//
|
||||||
|
// Operations: 447 squares 13 multiplies
|
||||||
|
//
|
||||||
|
// Generated by github.com/mmcloughlin/addchain v0.4.0.
|
||||||
|
|
||||||
|
t0, t1, t2: Tight_Field_Element = ---, ---, ---
|
||||||
|
|
||||||
|
// Step 1: t0 = x^0x2
|
||||||
|
fe_carry_square(&t0, arg1)
|
||||||
|
|
||||||
|
// Step 2: t0 = x^0x3
|
||||||
|
fe_carry_mul(&t0, arg1, fe_relax_cast(&t0))
|
||||||
|
|
||||||
|
// t0.Sqr(t0)
|
||||||
|
fe_carry_square(&t0, fe_relax_cast(&t0))
|
||||||
|
|
||||||
|
// Step 4: t0 = x^0x7
|
||||||
|
fe_carry_mul(&t0, arg1, fe_relax_cast(&t0))
|
||||||
|
|
||||||
|
// Step 7: t1 = x^0x38
|
||||||
|
fe_carry_pow2k(&t1, fe_relax_cast(&t0), 3)
|
||||||
|
|
||||||
|
// Step 8: t0 = x^0x3f
|
||||||
|
fe_carry_mul(&t0, fe_relax_cast(&t0), fe_relax_cast(&t1))
|
||||||
|
|
||||||
|
// Step 14: t1 = x^0xfc0
|
||||||
|
fe_carry_pow2k(&t1, fe_relax_cast(&t0), 6)
|
||||||
|
|
||||||
|
// Step 15: t1 = x^0xfff
|
||||||
|
fe_carry_mul(&t1, fe_relax_cast(&t0), fe_relax_cast(&t1))
|
||||||
|
|
||||||
|
// Step 27: t2 = x^0xfff000
|
||||||
|
fe_carry_pow2k(&t2, fe_relax_cast(&t1), 12)
|
||||||
|
|
||||||
|
// Step 28: t1 = x^0xffffff
|
||||||
|
fe_carry_mul(&t1, fe_relax_cast(&t1), fe_relax_cast(&t2))
|
||||||
|
|
||||||
|
// Step 34: t2 = x^0x3fffffc0
|
||||||
|
fe_carry_pow2k(&t2, fe_relax_cast(&t1), 6)
|
||||||
|
|
||||||
|
// Step 35: t0 = x^0x3fffffff
|
||||||
|
fe_carry_mul(&t0, fe_relax_cast(&t0), fe_relax_cast(&t2))
|
||||||
|
|
||||||
|
// Step 53: t2 = x^0xffffff000000
|
||||||
|
fe_carry_pow2k(&t2, fe_relax_cast(&t2), 18)
|
||||||
|
|
||||||
|
// Step 54: t1 = x^0xffffffffffff
|
||||||
|
fe_carry_mul(&t1, fe_relax_cast(&t1), fe_relax_cast(&t2))
|
||||||
|
|
||||||
|
// Step 102: t2 = x^0xffffffffffff000000000000
|
||||||
|
fe_carry_pow2k(&t2, fe_relax_cast(&t1), 48)
|
||||||
|
|
||||||
|
// Step 103: t1 = x^0xffffffffffffffffffffffff
|
||||||
|
fe_carry_mul(&t1, fe_relax_cast(&t1), fe_relax_cast(&t2))
|
||||||
|
|
||||||
|
// Step 199: t2 = x^0xffffffffffffffffffffffff000000000000000000000000
|
||||||
|
fe_carry_pow2k(&t2, fe_relax_cast(&t1), 96)
|
||||||
|
|
||||||
|
// Step 200: t1 = x^0xffffffffffffffffffffffffffffffffffffffffffffffff
|
||||||
|
fe_carry_mul(&t1, fe_relax_cast(&t1), fe_relax_cast(&t2))
|
||||||
|
|
||||||
|
// Step 230: t1 = x^0x3fffffffffffffffffffffffffffffffffffffffffffffffc0000000
|
||||||
|
fe_carry_pow2k(&t1, fe_relax_cast(&t1), 30)
|
||||||
|
|
||||||
|
// Step 231: t0 = x^0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||||
|
fe_carry_mul(&t0, fe_relax_cast(&t0), fe_relax_cast(&t1))
|
||||||
|
|
||||||
|
// Step 232: t1 = x^0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffe
|
||||||
|
fe_carry_square(&t1, fe_relax_cast(&t0))
|
||||||
|
|
||||||
|
// Step 233: t1 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||||
|
fe_carry_mul(&t1, arg1, fe_relax_cast(&t1))
|
||||||
|
|
||||||
|
// Step 456: t1 = x^0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000000000000000000000000000000000000000000000000000
|
||||||
|
fe_carry_pow2k(&t1, fe_relax_cast(&t1), 223)
|
||||||
|
|
||||||
|
// Step 457: t0 = x^0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||||
|
fe_carry_mul(&t0, fe_relax_cast(&t0), fe_relax_cast(&t1))
|
||||||
|
|
||||||
|
// Step 459: t0 = x^0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffffffffffffffffffffffffffffffffffffffffffffffffffffc
|
||||||
|
fe_carry_pow2k(&t0, fe_relax_cast(&t0), 2)
|
||||||
|
|
||||||
|
// Step 460: z = x^0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffffffffffffffffffffffffffffffffffffffffffffffffffffd
|
||||||
|
fe_carry_mul(out1, arg1, fe_relax_cast(&t0))
|
||||||
|
|
||||||
|
fe_clear_vec([]^Tight_Field_Element{&t0, &t1, &t2})
|
||||||
|
}
|
||||||
|
|
||||||
|
fe_zero :: proc "contextless" (out1: ^Tight_Field_Element) {
|
||||||
|
out1[0] = 0
|
||||||
|
out1[1] = 0
|
||||||
|
out1[2] = 0
|
||||||
|
out1[3] = 0
|
||||||
|
out1[4] = 0
|
||||||
|
out1[5] = 0
|
||||||
|
out1[6] = 0
|
||||||
|
out1[7] = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fe_one :: proc "contextless" (out1: ^Tight_Field_Element) {
|
||||||
|
out1[0] = 1
|
||||||
|
out1[1] = 0
|
||||||
|
out1[2] = 0
|
||||||
|
out1[3] = 0
|
||||||
|
out1[4] = 0
|
||||||
|
out1[5] = 0
|
||||||
|
out1[6] = 0
|
||||||
|
out1[7] = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fe_set :: proc "contextless" (out1, arg1: ^Tight_Field_Element) {
|
||||||
|
x1 := arg1[0]
|
||||||
|
x2 := arg1[1]
|
||||||
|
x3 := arg1[2]
|
||||||
|
x4 := arg1[3]
|
||||||
|
x5 := arg1[4]
|
||||||
|
x6 := arg1[5]
|
||||||
|
x7 := arg1[6]
|
||||||
|
x8 := arg1[7]
|
||||||
|
out1[0] = x1
|
||||||
|
out1[1] = x2
|
||||||
|
out1[2] = x3
|
||||||
|
out1[3] = x4
|
||||||
|
out1[4] = x5
|
||||||
|
out1[5] = x6
|
||||||
|
out1[6] = x7
|
||||||
|
out1[7] = x8
|
||||||
|
}
|
||||||
|
|
||||||
|
@(optimization_mode = "none")
|
||||||
|
fe_cond_swap :: #force_no_inline proc "contextless" (out1, out2: ^Tight_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
|
||||||
|
x = (out1[6] ~ out2[6]) & mask
|
||||||
|
x7, y7 := out1[6] ~ x, out2[6] ~ x
|
||||||
|
x = (out1[7] ~ out2[7]) & mask
|
||||||
|
x8, y8 := out1[7] ~ x, out2[7] ~ 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
|
||||||
|
out1[6], out2[6] = x7, y7
|
||||||
|
out1[7], out2[7] = x8, y8
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,5 @@
|
|||||||
package field_poly1305
|
package field_poly1305
|
||||||
|
|
||||||
import "base:intrinsics"
|
|
||||||
import "core:encoding/endian"
|
import "core:encoding/endian"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
|
||||||
@@ -29,9 +28,7 @@ fe_from_bytes :: #force_inline proc "contextless" (
|
|||||||
// makes implementing the actual MAC block processing considerably
|
// makes implementing the actual MAC block processing considerably
|
||||||
// neater.
|
// neater.
|
||||||
|
|
||||||
if len(arg1) != 16 {
|
ensure_contextless(len(arg1) == 16, "poly1305: invalid field element size")
|
||||||
intrinsics.trap()
|
|
||||||
}
|
|
||||||
|
|
||||||
// While it may be unwise to do deserialization here on our
|
// While it may be unwise to do deserialization here on our
|
||||||
// own when fiat-crypto provides equivalent functionality,
|
// own when fiat-crypto provides equivalent functionality,
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
package field_scalar25519
|
package field_scalar25519
|
||||||
|
|
||||||
import "base:intrinsics"
|
|
||||||
import "core:encoding/endian"
|
import "core:encoding/endian"
|
||||||
import "core:math/bits"
|
import "core:math/bits"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
|
||||||
@(private)
|
@(private, rodata)
|
||||||
_TWO_168 := Montgomery_Domain_Field_Element {
|
_TWO_168 := Montgomery_Domain_Field_Element {
|
||||||
0x5b8ab432eac74798,
|
0x5b8ab432eac74798,
|
||||||
0x38afddd6de59d5d7,
|
0x38afddd6de59d5d7,
|
||||||
0xa2c131b399411b7c,
|
0xa2c131b399411b7c,
|
||||||
0x6329a7ed9ce5a30,
|
0x6329a7ed9ce5a30,
|
||||||
}
|
}
|
||||||
@(private)
|
@(private, rodata)
|
||||||
_TWO_336 := Montgomery_Domain_Field_Element {
|
_TWO_336 := Montgomery_Domain_Field_Element {
|
||||||
0xbd3d108e2b35ecc5,
|
0xbd3d108e2b35ecc5,
|
||||||
0x5c3a3718bdf9c90b,
|
0x5c3a3718bdf9c90b,
|
||||||
@@ -95,9 +94,8 @@ fe_from_bytes_wide :: proc "contextless" (
|
|||||||
@(private)
|
@(private)
|
||||||
_fe_from_bytes_short :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element, arg1: []byte) {
|
_fe_from_bytes_short :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element, arg1: []byte) {
|
||||||
// INVARIANT: len(arg1) < 32.
|
// INVARIANT: len(arg1) < 32.
|
||||||
if len(arg1) >= 32 {
|
ensure_contextless(len(arg1) < 32, "edwards25519: oversized short scalar")
|
||||||
intrinsics.trap()
|
|
||||||
}
|
|
||||||
tmp: [32]byte
|
tmp: [32]byte
|
||||||
copy(tmp[:], arg1)
|
copy(tmp[:], arg1)
|
||||||
|
|
||||||
@@ -106,9 +104,7 @@ _fe_from_bytes_short :: proc "contextless" (out1: ^Montgomery_Domain_Field_Eleme
|
|||||||
}
|
}
|
||||||
|
|
||||||
fe_to_bytes :: proc "contextless" (out1: []byte, arg1: ^Montgomery_Domain_Field_Element) {
|
fe_to_bytes :: proc "contextless" (out1: []byte, arg1: ^Montgomery_Domain_Field_Element) {
|
||||||
if len(out1) != 32 {
|
ensure_contextless(len(out1) == 32, "edwards25519: oversized scalar output buffer")
|
||||||
intrinsics.trap()
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp: Non_Montgomery_Domain_Field_Element
|
tmp: Non_Montgomery_Domain_Field_Element
|
||||||
fe_from_montgomery(&tmp, arg1)
|
fe_from_montgomery(&tmp, arg1)
|
||||||
|
|||||||
+18
-21
@@ -44,7 +44,7 @@ Context :: struct {
|
|||||||
is_finalized: bool, // For SHAKE (unlimited squeeze is allowed)
|
is_finalized: bool, // For SHAKE (unlimited squeeze is allowed)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private, rodata)
|
||||||
keccakf_rndc := [?]u64 {
|
keccakf_rndc := [?]u64 {
|
||||||
0x0000000000000001, 0x0000000000008082, 0x800000000000808a,
|
0x0000000000000001, 0x0000000000008082, 0x800000000000808a,
|
||||||
0x8000000080008000, 0x000000000000808b, 0x0000000080000001,
|
0x8000000080008000, 0x000000000000808b, 0x0000000080000001,
|
||||||
@@ -56,13 +56,13 @@ keccakf_rndc := [?]u64 {
|
|||||||
0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
|
0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private, rodata)
|
||||||
keccakf_rotc := [?]int {
|
keccakf_rotc := [?]int {
|
||||||
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14,
|
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14,
|
||||||
27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
|
27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private, rodata)
|
||||||
keccakf_piln := [?]i32 {
|
keccakf_piln := [?]i32 {
|
||||||
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4,
|
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4,
|
||||||
15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
|
15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
|
||||||
@@ -122,7 +122,7 @@ keccakf :: proc "contextless" (st: ^[25]u64) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init :: proc(ctx: ^Context) {
|
init :: proc "contextless" (ctx: ^Context) {
|
||||||
for i := 0; i < 25; i += 1 {
|
for i := 0; i < 25; i += 1 {
|
||||||
ctx.st.q[i] = 0
|
ctx.st.q[i] = 0
|
||||||
}
|
}
|
||||||
@@ -133,9 +133,9 @@ init :: proc(ctx: ^Context) {
|
|||||||
ctx.is_finalized = false
|
ctx.is_finalized = false
|
||||||
}
|
}
|
||||||
|
|
||||||
update :: proc(ctx: ^Context, data: []byte) {
|
update :: proc "contextless" (ctx: ^Context, data: []byte) {
|
||||||
assert(ctx.is_initialized)
|
ensure_contextless(ctx.is_initialized)
|
||||||
assert(!ctx.is_finalized)
|
ensure_contextless(!ctx.is_finalized)
|
||||||
|
|
||||||
j := ctx.pt
|
j := ctx.pt
|
||||||
for i := 0; i < len(data); i += 1 {
|
for i := 0; i < len(data); i += 1 {
|
||||||
@@ -149,12 +149,9 @@ update :: proc(ctx: ^Context, data: []byte) {
|
|||||||
ctx.pt = j
|
ctx.pt = j
|
||||||
}
|
}
|
||||||
|
|
||||||
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
final :: proc "contextless" (ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
||||||
assert(ctx.is_initialized)
|
ensure_contextless(ctx.is_initialized)
|
||||||
|
ensure_contextless(len(hash) >= ctx.mdlen, "crypto/sha3: invalid destination digest size")
|
||||||
if len(hash) < ctx.mdlen {
|
|
||||||
panic("crypto/sha3: invalid destination digest size")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := ctx
|
ctx := ctx
|
||||||
if finalize_clone {
|
if finalize_clone {
|
||||||
@@ -173,11 +170,11 @@ final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clone :: proc(ctx, other: ^Context) {
|
clone :: proc "contextless" (ctx, other: ^Context) {
|
||||||
ctx^ = other^
|
ctx^ = other^
|
||||||
}
|
}
|
||||||
|
|
||||||
reset :: proc(ctx: ^Context) {
|
reset :: proc "contextless" (ctx: ^Context) {
|
||||||
if !ctx.is_initialized {
|
if !ctx.is_initialized {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -185,9 +182,9 @@ reset :: proc(ctx: ^Context) {
|
|||||||
mem.zero_explicit(ctx, size_of(ctx^))
|
mem.zero_explicit(ctx, size_of(ctx^))
|
||||||
}
|
}
|
||||||
|
|
||||||
shake_xof :: proc(ctx: ^Context) {
|
shake_xof :: proc "contextless" (ctx: ^Context) {
|
||||||
assert(ctx.is_initialized)
|
ensure_contextless(ctx.is_initialized)
|
||||||
assert(!ctx.is_finalized)
|
ensure_contextless(!ctx.is_finalized)
|
||||||
|
|
||||||
ctx.st.b[ctx.pt] ~= ctx.dsbyte
|
ctx.st.b[ctx.pt] ~= ctx.dsbyte
|
||||||
ctx.st.b[ctx.rsiz - 1] ~= 0x80
|
ctx.st.b[ctx.rsiz - 1] ~= 0x80
|
||||||
@@ -197,9 +194,9 @@ shake_xof :: proc(ctx: ^Context) {
|
|||||||
ctx.is_finalized = true // No more absorb, unlimited squeeze.
|
ctx.is_finalized = true // No more absorb, unlimited squeeze.
|
||||||
}
|
}
|
||||||
|
|
||||||
shake_out :: proc(ctx: ^Context, hash: []byte) {
|
shake_out :: proc "contextless" (ctx: ^Context, hash: []byte) {
|
||||||
assert(ctx.is_initialized)
|
ensure_contextless(ctx.is_initialized)
|
||||||
assert(ctx.is_finalized)
|
ensure_contextless(ctx.is_finalized)
|
||||||
|
|
||||||
j := ctx.pt
|
j := ctx.pt
|
||||||
for i := 0; i < len(hash); i += 1 {
|
for i := 0; i < len(hash); i += 1 {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package _sha3
|
|||||||
import "core:encoding/endian"
|
import "core:encoding/endian"
|
||||||
import "core:math/bits"
|
import "core:math/bits"
|
||||||
|
|
||||||
init_cshake :: proc(ctx: ^Context, n, s: []byte, sec_strength: int) {
|
init_cshake :: proc "contextless" (ctx: ^Context, n, s: []byte, sec_strength: int) {
|
||||||
ctx.mdlen = sec_strength / 8
|
ctx.mdlen = sec_strength / 8
|
||||||
|
|
||||||
// No domain separator is equivalent to vanilla SHAKE.
|
// No domain separator is equivalent to vanilla SHAKE.
|
||||||
@@ -18,7 +18,7 @@ init_cshake :: proc(ctx: ^Context, n, s: []byte, sec_strength: int) {
|
|||||||
bytepad(ctx, [][]byte{n, s}, rate_cshake(sec_strength))
|
bytepad(ctx, [][]byte{n, s}, rate_cshake(sec_strength))
|
||||||
}
|
}
|
||||||
|
|
||||||
final_cshake :: proc(ctx: ^Context, dst: []byte, finalize_clone: bool = false) {
|
final_cshake :: proc "contextless" (ctx: ^Context, dst: []byte, finalize_clone: bool = false) {
|
||||||
ctx := ctx
|
ctx := ctx
|
||||||
if finalize_clone {
|
if finalize_clone {
|
||||||
tmp_ctx: Context
|
tmp_ctx: Context
|
||||||
@@ -32,7 +32,7 @@ final_cshake :: proc(ctx: ^Context, dst: []byte, finalize_clone: bool = false) {
|
|||||||
shake_out(ctx, dst)
|
shake_out(ctx, dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
rate_cshake :: #force_inline proc(sec_strength: int) -> int {
|
rate_cshake :: #force_inline proc "contextless" (sec_strength: int) -> int {
|
||||||
switch sec_strength {
|
switch sec_strength {
|
||||||
case 128:
|
case 128:
|
||||||
return RATE_128
|
return RATE_128
|
||||||
@@ -40,7 +40,7 @@ rate_cshake :: #force_inline proc(sec_strength: int) -> int {
|
|||||||
return RATE_256
|
return RATE_256
|
||||||
}
|
}
|
||||||
|
|
||||||
panic("crypto/sha3: invalid security strength")
|
panic_contextless("crypto/sha3: invalid security strength")
|
||||||
}
|
}
|
||||||
|
|
||||||
// right_encode and left_encode are defined to support 0 <= x < 2^2040
|
// right_encode and left_encode are defined to support 0 <= x < 2^2040
|
||||||
@@ -52,10 +52,10 @@ rate_cshake :: #force_inline proc(sec_strength: int) -> int {
|
|||||||
//
|
//
|
||||||
// Thus we support 0 <= x < 2^128.
|
// Thus we support 0 <= x < 2^128.
|
||||||
|
|
||||||
@(private)
|
@(private, rodata)
|
||||||
_PAD: [RATE_128]byte // Biggest possible value of w per spec.
|
_PAD: [RATE_128]byte // Biggest possible value of w per spec.
|
||||||
|
|
||||||
bytepad :: proc(ctx: ^Context, x_strings: [][]byte, w: int) {
|
bytepad :: proc "contextless" (ctx: ^Context, x_strings: [][]byte, w: int) {
|
||||||
// 1. z = left_encode(w) || X.
|
// 1. z = left_encode(w) || X.
|
||||||
z_hi: u64
|
z_hi: u64
|
||||||
z_lo := left_right_encode(ctx, 0, u64(w), true)
|
z_lo := left_right_encode(ctx, 0, u64(w), true)
|
||||||
@@ -70,9 +70,7 @@ bytepad :: proc(ctx: ^Context, x_strings: [][]byte, w: int) {
|
|||||||
|
|
||||||
// This isn't actually possible, at least with the currently
|
// This isn't actually possible, at least with the currently
|
||||||
// defined SP 800-185 routines.
|
// defined SP 800-185 routines.
|
||||||
if carry != 0 {
|
ensure_contextless(carry == 0, "crypto/sha3: bytepad input length overflow")
|
||||||
panic("crypto/sha3: bytepad input length overflow")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We skip this step as we are doing a byte-oriented implementation
|
// We skip this step as we are doing a byte-oriented implementation
|
||||||
@@ -95,7 +93,7 @@ bytepad :: proc(ctx: ^Context, x_strings: [][]byte, w: int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
encode_string :: #force_inline proc(ctx: ^Context, s: []byte) -> (u64, u64) {
|
encode_string :: #force_inline proc "contextless" (ctx: ^Context, s: []byte) -> (u64, u64) {
|
||||||
l := encode_byte_len(ctx, len(s), true) // left_encode
|
l := encode_byte_len(ctx, len(s), true) // left_encode
|
||||||
update(ctx, s)
|
update(ctx, s)
|
||||||
|
|
||||||
@@ -104,13 +102,13 @@ encode_string :: #force_inline proc(ctx: ^Context, s: []byte) -> (u64, u64) {
|
|||||||
return hi, lo
|
return hi, lo
|
||||||
}
|
}
|
||||||
|
|
||||||
encode_byte_len :: #force_inline proc(ctx: ^Context, l: int, is_left: bool) -> u64 {
|
encode_byte_len :: #force_inline proc "contextless" (ctx: ^Context, l: int, is_left: bool) -> u64 {
|
||||||
hi, lo := bits.mul_u64(u64(l), 8)
|
hi, lo := bits.mul_u64(u64(l), 8)
|
||||||
return left_right_encode(ctx, hi, lo, is_left)
|
return left_right_encode(ctx, hi, lo, is_left)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
left_right_encode :: proc(ctx: ^Context, hi, lo: u64, is_left: bool) -> u64 {
|
left_right_encode :: proc "contextless" (ctx: ^Context, hi, lo: u64, is_left: bool) -> u64 {
|
||||||
HI_OFFSET :: 1
|
HI_OFFSET :: 1
|
||||||
LO_OFFSET :: HI_OFFSET + 8
|
LO_OFFSET :: HI_OFFSET + 8
|
||||||
RIGHT_OFFSET :: LO_OFFSET + 8
|
RIGHT_OFFSET :: LO_OFFSET + 8
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ seal_oneshot :: proc(algo: Algorithm, dst, tag, key, iv, aad, plaintext: []byte,
|
|||||||
// returning true iff the authentication was successful. If authentication
|
// returning true iff the authentication was successful. If authentication
|
||||||
// fails, the destination buffer will be zeroed.
|
// fails, the destination buffer will be zeroed.
|
||||||
//
|
//
|
||||||
// dst and plaintext MUST alias exactly or not at all.
|
// dst and ciphertext MUST alias exactly or not at all.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
open_oneshot :: proc(algo: Algorithm, dst, key, iv, aad, ciphertext, tag: []byte, impl: Implementation = nil) -> bool {
|
open_oneshot :: proc(algo: Algorithm, dst, key, iv, aad, ciphertext, tag: []byte, impl: Implementation = nil) -> bool {
|
||||||
ctx: Context
|
ctx: Context
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package aead
|
package aead
|
||||||
|
|
||||||
|
import "core:crypto/aegis"
|
||||||
import "core:crypto/aes"
|
import "core:crypto/aes"
|
||||||
import "core:crypto/chacha20"
|
import "core:crypto/chacha20"
|
||||||
import "core:crypto/chacha20poly1305"
|
import "core:crypto/chacha20poly1305"
|
||||||
|
import "core:crypto/deoxysii"
|
||||||
import "core:reflect"
|
import "core:reflect"
|
||||||
|
|
||||||
// Implementation is an AEAD implementation. Most callers will not need
|
// Implementation is an AEAD implementation. Most callers will not need
|
||||||
@@ -15,7 +17,7 @@ Implementation :: union {
|
|||||||
|
|
||||||
// MAX_TAG_SIZE is the maximum size tag that can be returned by any of the
|
// MAX_TAG_SIZE is the maximum size tag that can be returned by any of the
|
||||||
// Algorithms supported via this package.
|
// Algorithms supported via this package.
|
||||||
MAX_TAG_SIZE :: 16
|
MAX_TAG_SIZE :: 32
|
||||||
|
|
||||||
// Algorithm is the algorithm identifier associated with a given Context.
|
// Algorithm is the algorithm identifier associated with a given Context.
|
||||||
Algorithm :: enum {
|
Algorithm :: enum {
|
||||||
@@ -25,9 +27,14 @@ Algorithm :: enum {
|
|||||||
AES_GCM_256,
|
AES_GCM_256,
|
||||||
CHACHA20POLY1305,
|
CHACHA20POLY1305,
|
||||||
XCHACHA20POLY1305,
|
XCHACHA20POLY1305,
|
||||||
|
AEGIS_128L,
|
||||||
|
AEGIS_128L_256, // AEGIS-128L (256-bit tag)
|
||||||
|
AEGIS_256,
|
||||||
|
AEGIS_256_256, // AEGIS-256 (256-bit tag)
|
||||||
|
DEOXYS_II_256,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ALGORITM_NAMES is the Agorithm to algorithm name string.
|
// ALGORITM_NAMES is the Algorithm to algorithm name string.
|
||||||
ALGORITHM_NAMES := [Algorithm]string {
|
ALGORITHM_NAMES := [Algorithm]string {
|
||||||
.Invalid = "Invalid",
|
.Invalid = "Invalid",
|
||||||
.AES_GCM_128 = "AES-GCM-128",
|
.AES_GCM_128 = "AES-GCM-128",
|
||||||
@@ -35,6 +42,11 @@ ALGORITHM_NAMES := [Algorithm]string {
|
|||||||
.AES_GCM_256 = "AES-GCM-256",
|
.AES_GCM_256 = "AES-GCM-256",
|
||||||
.CHACHA20POLY1305 = "chacha20poly1305",
|
.CHACHA20POLY1305 = "chacha20poly1305",
|
||||||
.XCHACHA20POLY1305 = "xchacha20poly1305",
|
.XCHACHA20POLY1305 = "xchacha20poly1305",
|
||||||
|
.AEGIS_128L = "AEGIS-128L",
|
||||||
|
.AEGIS_128L_256 = "AEGIS-128L-256",
|
||||||
|
.AEGIS_256 = "AEGIS-256",
|
||||||
|
.AEGIS_256_256 = "AEGIS-256-256",
|
||||||
|
.DEOXYS_II_256 = "Deoxys-II-256",
|
||||||
}
|
}
|
||||||
|
|
||||||
// TAG_SIZES is the Algorithm to tag size in bytes.
|
// TAG_SIZES is the Algorithm to tag size in bytes.
|
||||||
@@ -45,6 +57,11 @@ TAG_SIZES := [Algorithm]int {
|
|||||||
.AES_GCM_256 = aes.GCM_TAG_SIZE,
|
.AES_GCM_256 = aes.GCM_TAG_SIZE,
|
||||||
.CHACHA20POLY1305 = chacha20poly1305.TAG_SIZE,
|
.CHACHA20POLY1305 = chacha20poly1305.TAG_SIZE,
|
||||||
.XCHACHA20POLY1305 = chacha20poly1305.TAG_SIZE,
|
.XCHACHA20POLY1305 = chacha20poly1305.TAG_SIZE,
|
||||||
|
.AEGIS_128L = aegis.TAG_SIZE_128,
|
||||||
|
.AEGIS_128L_256 = aegis.TAG_SIZE_256,
|
||||||
|
.AEGIS_256 = aegis.TAG_SIZE_128,
|
||||||
|
.AEGIS_256_256 = aegis.TAG_SIZE_256,
|
||||||
|
.DEOXYS_II_256 = deoxysii.TAG_SIZE,
|
||||||
}
|
}
|
||||||
|
|
||||||
// KEY_SIZES is the Algorithm to key size in bytes.
|
// KEY_SIZES is the Algorithm to key size in bytes.
|
||||||
@@ -55,6 +72,11 @@ KEY_SIZES := [Algorithm]int {
|
|||||||
.AES_GCM_256 = aes.KEY_SIZE_256,
|
.AES_GCM_256 = aes.KEY_SIZE_256,
|
||||||
.CHACHA20POLY1305 = chacha20poly1305.KEY_SIZE,
|
.CHACHA20POLY1305 = chacha20poly1305.KEY_SIZE,
|
||||||
.XCHACHA20POLY1305 = chacha20poly1305.KEY_SIZE,
|
.XCHACHA20POLY1305 = chacha20poly1305.KEY_SIZE,
|
||||||
|
.AEGIS_128L = aegis.KEY_SIZE_128L,
|
||||||
|
.AEGIS_128L_256 = aegis.KEY_SIZE_128L,
|
||||||
|
.AEGIS_256 = aegis.KEY_SIZE_256,
|
||||||
|
.AEGIS_256_256 = aegis.KEY_SIZE_256,
|
||||||
|
.DEOXYS_II_256 = deoxysii.KEY_SIZE,
|
||||||
}
|
}
|
||||||
|
|
||||||
// IV_SIZES is the Algorithm to initialization vector size in bytes.
|
// IV_SIZES is the Algorithm to initialization vector size in bytes.
|
||||||
@@ -67,6 +89,11 @@ IV_SIZES := [Algorithm]int {
|
|||||||
.AES_GCM_256 = aes.GCM_IV_SIZE,
|
.AES_GCM_256 = aes.GCM_IV_SIZE,
|
||||||
.CHACHA20POLY1305 = chacha20poly1305.IV_SIZE,
|
.CHACHA20POLY1305 = chacha20poly1305.IV_SIZE,
|
||||||
.XCHACHA20POLY1305 = chacha20poly1305.XIV_SIZE,
|
.XCHACHA20POLY1305 = chacha20poly1305.XIV_SIZE,
|
||||||
|
.AEGIS_128L = aegis.IV_SIZE_128L,
|
||||||
|
.AEGIS_128L_256 = aegis.IV_SIZE_128L,
|
||||||
|
.AEGIS_256 = aegis.IV_SIZE_256,
|
||||||
|
.AEGIS_256_256 = aegis.IV_SIZE_256,
|
||||||
|
.DEOXYS_II_256 = deoxysii.IV_SIZE,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Context is a concrete instantiation of a specific AEAD algorithm.
|
// Context is a concrete instantiation of a specific AEAD algorithm.
|
||||||
@@ -75,6 +102,8 @@ Context :: struct {
|
|||||||
_impl: union {
|
_impl: union {
|
||||||
aes.Context_GCM,
|
aes.Context_GCM,
|
||||||
chacha20poly1305.Context,
|
chacha20poly1305.Context,
|
||||||
|
aegis.Context,
|
||||||
|
deoxysii.Context,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,6 +115,11 @@ _IMPL_IDS := [Algorithm]typeid {
|
|||||||
.AES_GCM_256 = typeid_of(aes.Context_GCM),
|
.AES_GCM_256 = typeid_of(aes.Context_GCM),
|
||||||
.CHACHA20POLY1305 = typeid_of(chacha20poly1305.Context),
|
.CHACHA20POLY1305 = typeid_of(chacha20poly1305.Context),
|
||||||
.XCHACHA20POLY1305 = typeid_of(chacha20poly1305.Context),
|
.XCHACHA20POLY1305 = typeid_of(chacha20poly1305.Context),
|
||||||
|
.AEGIS_128L = typeid_of(aegis.Context),
|
||||||
|
.AEGIS_128L_256 = typeid_of(aegis.Context),
|
||||||
|
.AEGIS_256 = typeid_of(aegis.Context),
|
||||||
|
.AEGIS_256_256 = typeid_of(aegis.Context),
|
||||||
|
.DEOXYS_II_256 = typeid_of(deoxysii.Context),
|
||||||
}
|
}
|
||||||
|
|
||||||
// init initializes a Context with a specific AEAD Algorithm.
|
// init initializes a Context with a specific AEAD Algorithm.
|
||||||
@@ -94,9 +128,7 @@ init :: proc(ctx: ^Context, algorithm: Algorithm, key: []byte, impl: Implementat
|
|||||||
reset(ctx)
|
reset(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(key) != KEY_SIZES[algorithm] {
|
ensure(len(key) == KEY_SIZES[algorithm], "crypto/aead: invalid key size")
|
||||||
panic("crypto/aead: invalid key size")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Directly specialize the union by setting the type ID (save a copy).
|
// Directly specialize the union by setting the type ID (save a copy).
|
||||||
reflect.set_union_variant_typeid(
|
reflect.set_union_variant_typeid(
|
||||||
@@ -113,6 +145,12 @@ init :: proc(ctx: ^Context, algorithm: Algorithm, key: []byte, impl: Implementat
|
|||||||
case .XCHACHA20POLY1305:
|
case .XCHACHA20POLY1305:
|
||||||
impl_ := impl != nil ? impl.(chacha20.Implementation) : chacha20.DEFAULT_IMPLEMENTATION
|
impl_ := impl != nil ? impl.(chacha20.Implementation) : chacha20.DEFAULT_IMPLEMENTATION
|
||||||
chacha20poly1305.init_xchacha(&ctx._impl.(chacha20poly1305.Context), key, impl_)
|
chacha20poly1305.init_xchacha(&ctx._impl.(chacha20poly1305.Context), key, impl_)
|
||||||
|
case .AEGIS_128L, .AEGIS_128L_256, .AEGIS_256, .AEGIS_256_256:
|
||||||
|
impl_ := impl != nil ? impl.(aes.Implementation) : aes.DEFAULT_IMPLEMENTATION
|
||||||
|
aegis.init(&ctx._impl.(aegis.Context), key, impl_)
|
||||||
|
case .DEOXYS_II_256:
|
||||||
|
impl_ := impl != nil ? impl.(aes.Implementation) : aes.DEFAULT_IMPLEMENTATION
|
||||||
|
deoxysii.init(&ctx._impl.(deoxysii.Context), key, impl_)
|
||||||
case .Invalid:
|
case .Invalid:
|
||||||
panic("crypto/aead: uninitialized algorithm")
|
panic("crypto/aead: uninitialized algorithm")
|
||||||
case:
|
case:
|
||||||
@@ -127,11 +165,17 @@ init :: proc(ctx: ^Context, algorithm: Algorithm, key: []byte, impl: Implementat
|
|||||||
//
|
//
|
||||||
// dst and plaintext MUST alias exactly or not at all.
|
// dst and plaintext MUST alias exactly or not at all.
|
||||||
seal_ctx :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
|
seal_ctx :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
|
||||||
|
ensure(len(tag) == TAG_SIZES[ctx._algo], "crypto/aead: invalid tag size")
|
||||||
|
|
||||||
switch &impl in ctx._impl {
|
switch &impl in ctx._impl {
|
||||||
case aes.Context_GCM:
|
case aes.Context_GCM:
|
||||||
aes.seal_gcm(&impl, dst, tag, iv, aad, plaintext)
|
aes.seal_gcm(&impl, dst, tag, iv, aad, plaintext)
|
||||||
case chacha20poly1305.Context:
|
case chacha20poly1305.Context:
|
||||||
chacha20poly1305.seal(&impl, dst, tag, iv, aad, plaintext)
|
chacha20poly1305.seal(&impl, dst, tag, iv, aad, plaintext)
|
||||||
|
case aegis.Context:
|
||||||
|
aegis.seal(&impl, dst, tag, iv, aad, plaintext)
|
||||||
|
case deoxysii.Context:
|
||||||
|
deoxysii.seal(&impl, dst, tag, iv, aad, plaintext)
|
||||||
case:
|
case:
|
||||||
panic("crypto/aead: uninitialized algorithm")
|
panic("crypto/aead: uninitialized algorithm")
|
||||||
}
|
}
|
||||||
@@ -145,11 +189,17 @@ seal_ctx :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
|
|||||||
// dst and plaintext MUST alias exactly or not at all.
|
// dst and plaintext MUST alias exactly or not at all.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
open_ctx :: proc(ctx: ^Context, dst, iv, aad, ciphertext, tag: []byte) -> bool {
|
open_ctx :: proc(ctx: ^Context, dst, iv, aad, ciphertext, tag: []byte) -> bool {
|
||||||
|
ensure(len(tag) == TAG_SIZES[ctx._algo], "crypto/aead: invalid tag size")
|
||||||
|
|
||||||
switch &impl in ctx._impl {
|
switch &impl in ctx._impl {
|
||||||
case aes.Context_GCM:
|
case aes.Context_GCM:
|
||||||
return aes.open_gcm(&impl, dst, iv, aad, ciphertext, tag)
|
return aes.open_gcm(&impl, dst, iv, aad, ciphertext, tag)
|
||||||
case chacha20poly1305.Context:
|
case chacha20poly1305.Context:
|
||||||
return chacha20poly1305.open(&impl, dst, iv, aad, ciphertext, tag)
|
return chacha20poly1305.open(&impl, dst, iv, aad, ciphertext, tag)
|
||||||
|
case aegis.Context:
|
||||||
|
return aegis.open(&impl, dst, iv, aad, ciphertext, tag)
|
||||||
|
case deoxysii.Context:
|
||||||
|
return deoxysii.open(&impl, dst, iv, aad, ciphertext, tag)
|
||||||
case:
|
case:
|
||||||
panic("crypto/aead: uninitialized algorithm")
|
panic("crypto/aead: uninitialized algorithm")
|
||||||
}
|
}
|
||||||
@@ -163,6 +213,10 @@ reset :: proc(ctx: ^Context) {
|
|||||||
aes.reset_gcm(&impl)
|
aes.reset_gcm(&impl)
|
||||||
case chacha20poly1305.Context:
|
case chacha20poly1305.Context:
|
||||||
chacha20poly1305.reset(&impl)
|
chacha20poly1305.reset(&impl)
|
||||||
|
case aegis.Context:
|
||||||
|
aegis.reset(&impl)
|
||||||
|
case deoxysii.Context:
|
||||||
|
deoxysii.reset(&impl)
|
||||||
case:
|
case:
|
||||||
// Calling reset repeatedly is fine.
|
// Calling reset repeatedly is fine.
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,213 @@
|
|||||||
|
/*
|
||||||
|
package aegis implements the AEGIS-128L and AEGIS-256 Authenticated
|
||||||
|
Encryption with Additional Data algorithms.
|
||||||
|
|
||||||
|
See:
|
||||||
|
- [[ https://www.ietf.org/archive/id/draft-irtf-cfrg-aegis-aead-12.txt ]]
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
// KEY_SIZE_256 is the AEGIS-256 key size in bytes.
|
||||||
|
KEY_SIZE_256 :: 32
|
||||||
|
// IV_SIZE_128L is the AEGIS-128L IV size in bytes.
|
||||||
|
IV_SIZE_128L :: 16
|
||||||
|
// IV_SIZE_256 is the AEGIS-256 IV size in bytes.
|
||||||
|
IV_SIZE_256 :: 32
|
||||||
|
// TAG_SIZE_128 is the AEGIS-128L or AEGIS-256 128-bit tag size in bytes.
|
||||||
|
TAG_SIZE_128 :: 16
|
||||||
|
// TAG_SIZE_256 is the AEGIS-128L or AEGIS-256 256-bit tag size in bytes.
|
||||||
|
TAG_SIZE_256 :: 32
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_RATE_128L :: 32
|
||||||
|
@(private)
|
||||||
|
_RATE_256 :: 16
|
||||||
|
@(private)
|
||||||
|
_RATE_MAX :: _RATE_128L
|
||||||
|
|
||||||
|
@(private, rodata)
|
||||||
|
_C0 := [16]byte{
|
||||||
|
0x00, 0x01, 0x01, 0x02, 0x03, 0x05, 0x08, 0x0d,
|
||||||
|
0x15, 0x22, 0x37, 0x59, 0x90, 0xe9, 0x79, 0x62,
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private, rodata)
|
||||||
|
_C1 := [16]byte {
|
||||||
|
0xdb, 0x3d, 0x18, 0x55, 0x6d, 0xc2, 0x2f, 0xf1,
|
||||||
|
0x20, 0x11, 0x31, 0x42, 0x73, 0xb5, 0x28, 0xdd,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context is a keyed AEGIS-128L or AEGIS-256 instance.
|
||||||
|
Context :: struct {
|
||||||
|
_key: [KEY_SIZE_256]byte,
|
||||||
|
_key_len: int,
|
||||||
|
_impl: aes.Implementation,
|
||||||
|
_is_initialized: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_validate_common_slice_sizes :: proc (ctx: ^Context, tag, iv, aad, text: []byte) {
|
||||||
|
switch len(tag) {
|
||||||
|
case TAG_SIZE_128, TAG_SIZE_256:
|
||||||
|
case:
|
||||||
|
panic("crypto/aegis: invalid tag size")
|
||||||
|
}
|
||||||
|
|
||||||
|
iv_ok: bool
|
||||||
|
switch ctx._key_len {
|
||||||
|
case KEY_SIZE_128L:
|
||||||
|
iv_ok = len(iv) == IV_SIZE_128L
|
||||||
|
case KEY_SIZE_256:
|
||||||
|
iv_ok = len(iv) == IV_SIZE_256
|
||||||
|
}
|
||||||
|
ensure(iv_ok,"crypto/aegis: invalid IV size")
|
||||||
|
|
||||||
|
#assert(size_of(int) == 8 || size_of(int) <= 4)
|
||||||
|
// As A_MAX and P_MAX are both defined to be 2^61 - 1 bytes, and
|
||||||
|
// the maximum length of a slice is bound by `size_of(int)`, where
|
||||||
|
// `int` is register sized, there is no need to check AAD/text
|
||||||
|
// lengths.
|
||||||
|
}
|
||||||
|
|
||||||
|
// init initializes a Context with the provided key, for AEGIS-128L or AEGIS-256.
|
||||||
|
init :: proc(ctx: ^Context, key: []byte, impl := aes.DEFAULT_IMPLEMENTATION) {
|
||||||
|
switch len(key) {
|
||||||
|
case KEY_SIZE_128L, KEY_SIZE_256:
|
||||||
|
case:
|
||||||
|
panic("crypto/aegis: invalid key size")
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(ctx._key[:], key)
|
||||||
|
ctx._key_len = len(key)
|
||||||
|
ctx._impl = impl
|
||||||
|
if ctx._impl == .Hardware && !is_hardware_accelerated() {
|
||||||
|
ctx._impl = .Portable
|
||||||
|
}
|
||||||
|
ctx._is_initialized = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// seal encrypts the plaintext and authenticates the aad and ciphertext,
|
||||||
|
// with the provided Context and iv, stores the output in dst and tag.
|
||||||
|
//
|
||||||
|
// dst and plaintext MUST alias exactly or not at all.
|
||||||
|
seal :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
|
||||||
|
ensure(ctx._is_initialized)
|
||||||
|
|
||||||
|
_validate_common_slice_sizes(ctx, tag, iv, aad, plaintext)
|
||||||
|
ensure(len(dst) == len(plaintext), "crypto/aegis: invalid destination ciphertext size")
|
||||||
|
ensure(!bytes.alias_inexactly(dst, plaintext), "crypto/aegis: dst and plaintext alias inexactly")
|
||||||
|
|
||||||
|
switch ctx._impl {
|
||||||
|
case .Hardware:
|
||||||
|
st: State_HW
|
||||||
|
defer reset_state_hw(&st)
|
||||||
|
|
||||||
|
init_hw(ctx, &st, iv)
|
||||||
|
|
||||||
|
aad_len, pt_len := len(aad), len(plaintext)
|
||||||
|
if aad_len > 0 {
|
||||||
|
absorb_hw(&st, aad)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pt_len > 0 {
|
||||||
|
enc_hw(&st, dst, plaintext)
|
||||||
|
}
|
||||||
|
|
||||||
|
finalize_hw(&st, tag, aad_len, pt_len)
|
||||||
|
case .Portable:
|
||||||
|
st: State_SW
|
||||||
|
defer reset_state_sw(&st)
|
||||||
|
|
||||||
|
init_sw(ctx, &st, iv)
|
||||||
|
|
||||||
|
aad_len, pt_len := len(aad), len(plaintext)
|
||||||
|
if aad_len > 0 {
|
||||||
|
absorb_sw(&st, aad)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pt_len > 0 {
|
||||||
|
enc_sw(&st, dst, plaintext)
|
||||||
|
}
|
||||||
|
|
||||||
|
finalize_sw(&st, tag, aad_len, pt_len)
|
||||||
|
case:
|
||||||
|
panic("core/crypto/aegis: not implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// fails, the destination buffer will be zeroed.
|
||||||
|
//
|
||||||
|
// dst and plaintext MUST alias exactly or not at all.
|
||||||
|
@(require_results)
|
||||||
|
open :: proc(ctx: ^Context, dst, iv, aad, ciphertext, tag: []byte) -> bool {
|
||||||
|
ensure(ctx._is_initialized)
|
||||||
|
|
||||||
|
_validate_common_slice_sizes(ctx, tag, iv, aad, ciphertext)
|
||||||
|
ensure(len(dst) == len(ciphertext), "crypto/aegis: invalid destination plaintext size")
|
||||||
|
ensure(!bytes.alias_inexactly(dst, ciphertext), "crypto/aegis: dst and ciphertext alias inexactly")
|
||||||
|
|
||||||
|
tmp: [TAG_SIZE_256]byte
|
||||||
|
derived_tag := tmp[:len(tag)]
|
||||||
|
aad_len, ct_len := len(aad), len(ciphertext)
|
||||||
|
|
||||||
|
switch ctx._impl {
|
||||||
|
case .Hardware:
|
||||||
|
st: State_HW
|
||||||
|
defer reset_state_hw(&st)
|
||||||
|
|
||||||
|
init_hw(ctx, &st, iv)
|
||||||
|
|
||||||
|
if aad_len > 0 {
|
||||||
|
absorb_hw(&st, aad)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ct_len > 0 {
|
||||||
|
dec_hw(&st, dst, ciphertext)
|
||||||
|
}
|
||||||
|
|
||||||
|
finalize_hw(&st, derived_tag, aad_len, ct_len)
|
||||||
|
case .Portable:
|
||||||
|
st: State_SW
|
||||||
|
defer reset_state_sw(&st)
|
||||||
|
|
||||||
|
init_sw(ctx, &st, iv)
|
||||||
|
|
||||||
|
if aad_len > 0 {
|
||||||
|
absorb_sw(&st, aad)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ct_len > 0 {
|
||||||
|
dec_sw(&st, dst, ciphertext)
|
||||||
|
}
|
||||||
|
|
||||||
|
finalize_sw(&st, derived_tag, aad_len, ct_len)
|
||||||
|
case:
|
||||||
|
panic("core/crypto/aegis: not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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))
|
||||||
|
ctx._key_len = 0
|
||||||
|
ctx._is_initialized = false
|
||||||
|
}
|
||||||
@@ -0,0 +1,452 @@
|
|||||||
|
package aegis
|
||||||
|
|
||||||
|
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
|
||||||
|
// but NOT orthogonalized form, as leaving things in the orthgonalized
|
||||||
|
// format would overly complicate the update implementation.
|
||||||
|
//
|
||||||
|
// Note/perf: Per Frank Denis and a review of the specification, it is
|
||||||
|
// possible to gain slightly more performance by leaving the state in
|
||||||
|
// orthogonalized form while doing initialization, finalization, and
|
||||||
|
// absorbing AAD. This implementation opts out of those optimizations
|
||||||
|
// for the sake of simplicity.
|
||||||
|
//
|
||||||
|
// The update function leverages the paralleism (4xblocks) at once.
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
State_SW :: struct {
|
||||||
|
s0_0, s0_1: u64,
|
||||||
|
s1_0, s1_1: u64,
|
||||||
|
s2_0, s2_1: u64,
|
||||||
|
s3_0, s3_1: u64,
|
||||||
|
s4_0, s4_1: u64,
|
||||||
|
s5_0, s5_1: u64,
|
||||||
|
s6_0, s6_1: u64,
|
||||||
|
s7_0, s7_1: u64,
|
||||||
|
q_k, q_b: [8]u64,
|
||||||
|
rate: int,
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
init_sw :: proc "contextless" (ctx: ^Context, st: ^State_SW, iv: []byte) {
|
||||||
|
switch ctx._key_len {
|
||||||
|
case KEY_SIZE_128L:
|
||||||
|
key_0, key_1 := aes.load_interleaved(ctx._key[:16])
|
||||||
|
iv_0, iv_1 := aes.load_interleaved(iv)
|
||||||
|
|
||||||
|
st.s0_0, st.s0_1 = aes.xor_interleaved(key_0, key_1, iv_0, iv_1)
|
||||||
|
st.s1_0, st.s1_1 = aes.load_interleaved(_C1[:])
|
||||||
|
st.s2_0, st.s2_1 = aes.load_interleaved(_C0[:])
|
||||||
|
st.s3_0, st.s3_1 = st.s1_0, st.s1_1
|
||||||
|
st.s4_0, st.s4_1 = st.s0_0, st.s0_1
|
||||||
|
st.s5_0, st.s5_1 = aes.xor_interleaved(key_0, key_1, st.s2_0, st.s2_1)
|
||||||
|
st.s6_0, st.s6_1 = aes.xor_interleaved(key_0, key_1, st.s1_0, st.s1_1)
|
||||||
|
st.s7_0, st.s7_1 = st.s5_0, st.s5_1
|
||||||
|
st.rate = _RATE_128L
|
||||||
|
|
||||||
|
for _ in 0 ..< 10 {
|
||||||
|
update_sw_128l(st, iv_0, iv_1, key_0, key_1)
|
||||||
|
}
|
||||||
|
case KEY_SIZE_256:
|
||||||
|
k0_0, k0_1 := aes.load_interleaved(ctx._key[:16])
|
||||||
|
k1_0, k1_1 := aes.load_interleaved(ctx._key[16:])
|
||||||
|
n0_0, n0_1 := aes.load_interleaved(iv[:16])
|
||||||
|
n1_0, n1_1 := aes.load_interleaved(iv[16:])
|
||||||
|
|
||||||
|
st.s0_0, st.s0_1 = aes.xor_interleaved(k0_0, k0_1, n0_0, n0_1)
|
||||||
|
st.s1_0, st.s1_1 = aes.xor_interleaved(k1_0, k1_1, n1_0, n1_1)
|
||||||
|
st.s2_0, st.s2_1 = aes.load_interleaved(_C1[:])
|
||||||
|
st.s3_0, st.s3_1 = aes.load_interleaved(_C0[:])
|
||||||
|
st.s4_0, st.s4_1 = aes.xor_interleaved(k0_0, k0_1, st.s3_0, st.s3_1)
|
||||||
|
st.s5_0, st.s5_1 = aes.xor_interleaved(k1_0, k1_1, st.s2_0, st.s2_1)
|
||||||
|
st.rate = _RATE_256
|
||||||
|
|
||||||
|
u0_0, u0_1, u1_0, u1_1 := st.s0_0, st.s0_1, st.s1_0, st.s1_1
|
||||||
|
for _ in 0 ..< 4 {
|
||||||
|
update_sw_256(st, k0_0, k0_1)
|
||||||
|
update_sw_256(st, k1_0, k1_1)
|
||||||
|
update_sw_256(st, u0_0, u0_1)
|
||||||
|
update_sw_256(st, u1_0, u1_1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
update_sw_128l :: proc "contextless" (st: ^State_SW, m0_0, m0_1, m1_0, m1_1: u64) {
|
||||||
|
st.q_k[0], st.q_k[4] = aes.xor_interleaved(st.s0_0, st.s0_1, m0_0, m0_1)
|
||||||
|
st.q_k[1], st.q_k[5] = st.s1_0, st.s1_1
|
||||||
|
st.q_k[2], st.q_k[6] = st.s2_0, st.s2_1
|
||||||
|
st.q_k[3], st.q_k[7] = st.s3_0, st.s3_1
|
||||||
|
aes.orthogonalize(&st.q_k)
|
||||||
|
|
||||||
|
st.q_b[0], st.q_b[4] = st.s7_0, st.s7_1
|
||||||
|
st.q_b[1], st.q_b[5] = st.s0_0, st.s0_1
|
||||||
|
st.q_b[2], st.q_b[6] = st.s1_0, st.s1_1
|
||||||
|
st.q_b[3], st.q_b[7] = st.s2_0, st.s2_1
|
||||||
|
aes.orthogonalize(&st.q_b)
|
||||||
|
|
||||||
|
aes.sub_bytes(&st.q_b)
|
||||||
|
aes.shift_rows(&st.q_b)
|
||||||
|
aes.mix_columns(&st.q_b)
|
||||||
|
aes.add_round_key(&st.q_b, st.q_k[:])
|
||||||
|
aes.orthogonalize(&st.q_b)
|
||||||
|
|
||||||
|
st.s0_0, st.s0_1 = st.q_b[0], st.q_b[4]
|
||||||
|
st.s1_0, st.s1_1 = st.q_b[1], st.q_b[5]
|
||||||
|
st.s2_0, st.s2_1 = st.q_b[2], st.q_b[6]
|
||||||
|
s3_0, s3_1 := st.q_b[3], st.q_b[7]
|
||||||
|
|
||||||
|
st.q_k[0], st.q_k[4] = aes.xor_interleaved(st.s4_0, st.s4_1, m1_0, m1_1)
|
||||||
|
st.q_k[1], st.q_k[5] = st.s5_0, st.s5_1
|
||||||
|
st.q_k[2], st.q_k[6] = st.s6_0, st.s6_1
|
||||||
|
st.q_k[3], st.q_k[7] = st.s7_0, st.s7_1
|
||||||
|
aes.orthogonalize(&st.q_k)
|
||||||
|
|
||||||
|
st.q_b[0], st.q_b[4] = st.s3_0, st.s3_1
|
||||||
|
st.q_b[1], st.q_b[5] = st.s4_0, st.s4_1
|
||||||
|
st.q_b[2], st.q_b[6] = st.s5_0, st.s5_1
|
||||||
|
st.q_b[3], st.q_b[7] = st.s6_0, st.s6_1
|
||||||
|
aes.orthogonalize(&st.q_b)
|
||||||
|
|
||||||
|
aes.sub_bytes(&st.q_b)
|
||||||
|
aes.shift_rows(&st.q_b)
|
||||||
|
aes.mix_columns(&st.q_b)
|
||||||
|
aes.add_round_key(&st.q_b, st.q_k[:])
|
||||||
|
aes.orthogonalize(&st.q_b)
|
||||||
|
|
||||||
|
st.s3_0, st.s3_1 = s3_0, s3_1
|
||||||
|
st.s4_0, st.s4_1 = st.q_b[0], st.q_b[4]
|
||||||
|
st.s5_0, st.s5_1 = st.q_b[1], st.q_b[5]
|
||||||
|
st.s6_0, st.s6_1 = st.q_b[2], st.q_b[6]
|
||||||
|
st.s7_0, st.s7_1 = st.q_b[3], st.q_b[7]
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
update_sw_256 :: proc "contextless" (st: ^State_SW, m_0, m_1: u64) {
|
||||||
|
st.q_k[0], st.q_k[4] = aes.xor_interleaved(st.s0_0, st.s0_1, m_0, m_1)
|
||||||
|
st.q_k[1], st.q_k[5] = st.s1_0, st.s1_1
|
||||||
|
st.q_k[2], st.q_k[6] = st.s2_0, st.s2_1
|
||||||
|
st.q_k[3], st.q_k[7] = st.s3_0, st.s3_1
|
||||||
|
aes.orthogonalize(&st.q_k)
|
||||||
|
|
||||||
|
st.q_b[0], st.q_b[4] = st.s5_0, st.s5_1
|
||||||
|
st.q_b[1], st.q_b[5] = st.s0_0, st.s0_1
|
||||||
|
st.q_b[2], st.q_b[6] = st.s1_0, st.s1_1
|
||||||
|
st.q_b[3], st.q_b[7] = st.s2_0, st.s2_1
|
||||||
|
aes.orthogonalize(&st.q_b)
|
||||||
|
|
||||||
|
aes.sub_bytes(&st.q_b)
|
||||||
|
aes.shift_rows(&st.q_b)
|
||||||
|
aes.mix_columns(&st.q_b)
|
||||||
|
aes.add_round_key(&st.q_b, st.q_k[:])
|
||||||
|
aes.orthogonalize(&st.q_b)
|
||||||
|
|
||||||
|
st.s0_0, st.s0_1 = st.q_b[0], st.q_b[4]
|
||||||
|
st.s1_0, st.s1_1 = st.q_b[1], st.q_b[5]
|
||||||
|
st.s2_0, st.s2_1 = st.q_b[2], st.q_b[6]
|
||||||
|
s3_0, s3_1 := st.q_b[3], st.q_b[7]
|
||||||
|
|
||||||
|
st.q_k[0], st.q_k[4] = st.s4_0, st.s4_1
|
||||||
|
st.q_k[1], st.q_k[5] = st.s5_0, st.s5_1
|
||||||
|
aes.orthogonalize(&st.q_k)
|
||||||
|
|
||||||
|
st.q_b[0], st.q_b[4] = st.s3_0, st.s3_1
|
||||||
|
st.q_b[1], st.q_b[5] = st.s4_0, st.s4_1
|
||||||
|
aes.orthogonalize(&st.q_b)
|
||||||
|
|
||||||
|
aes.sub_bytes(&st.q_b)
|
||||||
|
aes.shift_rows(&st.q_b)
|
||||||
|
aes.mix_columns(&st.q_b)
|
||||||
|
aes.add_round_key(&st.q_b, st.q_k[:])
|
||||||
|
aes.orthogonalize(&st.q_b)
|
||||||
|
|
||||||
|
st.s3_0, st.s3_1 = s3_0, s3_1
|
||||||
|
st.s4_0, st.s4_1 = st.q_b[0], st.q_b[4]
|
||||||
|
st.s5_0, st.s5_1 = st.q_b[1], st.q_b[5]
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
absorb_sw_128l :: #force_inline proc "contextless" (st: ^State_SW, ai: []byte) #no_bounds_check {
|
||||||
|
t0_0, t0_1 := aes.load_interleaved(ai[:16])
|
||||||
|
t1_0, t1_1 := aes.load_interleaved(ai[16:])
|
||||||
|
update_sw_128l(st, t0_0, t0_1, t1_0, t1_1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
absorb_sw_256 :: #force_inline proc "contextless" (st: ^State_SW, ai: []byte) {
|
||||||
|
m_0, m_1 := aes.load_interleaved(ai)
|
||||||
|
update_sw_256(st, m_0, m_1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
absorb_sw :: proc "contextless" (st: ^State_SW, aad: []byte) #no_bounds_check {
|
||||||
|
ai, l := aad, len(aad)
|
||||||
|
|
||||||
|
switch st.rate {
|
||||||
|
case _RATE_128L:
|
||||||
|
for l >= _RATE_128L {
|
||||||
|
absorb_sw_128l(st, ai)
|
||||||
|
ai = ai[_RATE_128L:]
|
||||||
|
l -= _RATE_128L
|
||||||
|
}
|
||||||
|
case _RATE_256:
|
||||||
|
for l >= _RATE_256 {
|
||||||
|
absorb_sw_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_sw_128l(st, tmp[:])
|
||||||
|
case _RATE_256:
|
||||||
|
absorb_sw_256(st, tmp[:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file", require_results)
|
||||||
|
z_sw_128l :: proc "contextless" (st: ^State_SW) -> (u64, u64, u64, u64) {
|
||||||
|
z0_0, z0_1 := aes.and_interleaved(st.s2_0, st.s2_1, st.s3_0, st.s3_1)
|
||||||
|
z0_0, z0_1 = aes.xor_interleaved(st.s1_0, st.s1_1, z0_0, z0_1)
|
||||||
|
z0_0, z0_1 = aes.xor_interleaved(st.s6_0, st.s6_1, z0_0, z0_1)
|
||||||
|
|
||||||
|
z1_0, z1_1 := aes.and_interleaved(st.s6_0, st.s6_1, st.s7_0, st.s7_1)
|
||||||
|
z1_0, z1_1 = aes.xor_interleaved(st.s5_0, st.s5_1, z1_0, z1_1)
|
||||||
|
z1_0, z1_1 = aes.xor_interleaved(st.s2_0, st.s2_1, z1_0, z1_1)
|
||||||
|
|
||||||
|
return z0_0, z0_1, z1_0, z1_1
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file", require_results)
|
||||||
|
z_sw_256 :: proc "contextless" (st: ^State_SW) -> (u64, u64) {
|
||||||
|
z_0, z_1 := aes.and_interleaved(st.s2_0, st.s2_1, st.s3_0, st.s3_1)
|
||||||
|
z_0, z_1 = aes.xor_interleaved(st.s5_0, st.s5_1, z_0, z_1)
|
||||||
|
z_0, z_1 = aes.xor_interleaved(st.s4_0, st.s4_1, z_0, z_1)
|
||||||
|
return aes.xor_interleaved(st.s1_0, st.s1_1, z_0, z_1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
enc_sw_128l :: #force_inline proc "contextless" (st: ^State_SW, ci, xi: []byte) #no_bounds_check {
|
||||||
|
z0_0, z0_1, z1_0, z1_1 := z_sw_128l(st)
|
||||||
|
|
||||||
|
t0_0, t0_1 := aes.load_interleaved(xi[:16])
|
||||||
|
t1_0, t1_1 := aes.load_interleaved(xi[16:])
|
||||||
|
update_sw_128l(st, t0_0, t0_1, t1_0, t1_1)
|
||||||
|
|
||||||
|
out0_0, out0_1 := aes.xor_interleaved(t0_0, t0_1, z0_0, z0_1)
|
||||||
|
out1_0, out1_1 := aes.xor_interleaved(t1_0, t1_1, z1_0, z1_1)
|
||||||
|
aes.store_interleaved(ci[:16], out0_0, out0_1)
|
||||||
|
aes.store_interleaved(ci[16:], out1_0, out1_1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
enc_sw_256 :: #force_inline proc "contextless" (st: ^State_SW, ci, xi: []byte) #no_bounds_check {
|
||||||
|
z_0, z_1 := z_sw_256(st)
|
||||||
|
|
||||||
|
xi_0, xi_1 := aes.load_interleaved(xi)
|
||||||
|
update_sw_256(st, xi_0, xi_1)
|
||||||
|
|
||||||
|
ci_0, ci_1 := aes.xor_interleaved(xi_0, xi_1, z_0, z_1)
|
||||||
|
aes.store_interleaved(ci, ci_0, ci_1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
enc_sw :: proc "contextless" (st: ^State_SW, dst, src: []byte) #no_bounds_check {
|
||||||
|
ci, xi, l := dst, src, len(src)
|
||||||
|
|
||||||
|
switch st.rate {
|
||||||
|
case _RATE_128L:
|
||||||
|
for l >= _RATE_128L {
|
||||||
|
enc_sw_128l(st, ci, xi)
|
||||||
|
ci = ci[_RATE_128L:]
|
||||||
|
xi = xi[_RATE_128L:]
|
||||||
|
l -= _RATE_128L
|
||||||
|
}
|
||||||
|
case _RATE_256:
|
||||||
|
for l >= _RATE_256 {
|
||||||
|
enc_sw_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_sw_128l(st, tmp[:], tmp[:])
|
||||||
|
case _RATE_256:
|
||||||
|
enc_sw_256(st, tmp[:], tmp[:])
|
||||||
|
}
|
||||||
|
copy(ci, tmp[:l])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
dec_sw_128l :: #force_inline proc "contextless" (st: ^State_SW, xi, ci: []byte) #no_bounds_check {
|
||||||
|
z0_0, z0_1, z1_0, z1_1 := z_sw_128l(st)
|
||||||
|
|
||||||
|
t0_0, t0_1 := aes.load_interleaved(ci[:16])
|
||||||
|
t1_0, t1_1 := aes.load_interleaved(ci[16:])
|
||||||
|
out0_0, out0_1 := aes.xor_interleaved(t0_0, t0_1, z0_0, z0_1)
|
||||||
|
out1_0, out1_1 := aes.xor_interleaved(t1_0, t1_1, z1_0, z1_1)
|
||||||
|
|
||||||
|
update_sw_128l(st, out0_0, out0_1, out1_0, out1_1)
|
||||||
|
aes.store_interleaved(xi[:16], out0_0, out0_1)
|
||||||
|
aes.store_interleaved(xi[16:], out1_0, out1_1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
dec_sw_256 :: #force_inline proc "contextless" (st: ^State_SW, xi, ci: []byte) #no_bounds_check {
|
||||||
|
z_0, z_1 := z_sw_256(st)
|
||||||
|
|
||||||
|
ci_0, ci_1 := aes.load_interleaved(ci)
|
||||||
|
xi_0, xi_1 := aes.xor_interleaved(ci_0, ci_1, z_0, z_1)
|
||||||
|
|
||||||
|
update_sw_256(st, xi_0, xi_1)
|
||||||
|
aes.store_interleaved(xi, xi_0, xi_1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(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))
|
||||||
|
|
||||||
|
z0_0, z0_1, z1_0, z1_1 := z_sw_128l(st)
|
||||||
|
copy(tmp[:], cn)
|
||||||
|
|
||||||
|
t0_0, t0_1 := aes.load_interleaved(tmp[:16])
|
||||||
|
t1_0, t1_1 := aes.load_interleaved(tmp[16:])
|
||||||
|
out0_0, out0_1 := aes.xor_interleaved(t0_0, t0_1, z0_0, z0_1)
|
||||||
|
out1_0, out1_1 := aes.xor_interleaved(t1_0, t1_1, z1_0, z1_1)
|
||||||
|
|
||||||
|
aes.store_interleaved(tmp[:16], out0_0, out0_1)
|
||||||
|
aes.store_interleaved(tmp[16:], out1_0, out1_1)
|
||||||
|
copy(xn, tmp[:])
|
||||||
|
|
||||||
|
for off := len(xn); off < _RATE_128L; off += 1 {
|
||||||
|
tmp[off] = 0
|
||||||
|
}
|
||||||
|
out0_0, out0_1 = aes.load_interleaved(tmp[:16])
|
||||||
|
out1_0, out1_1 = aes.load_interleaved(tmp[16:])
|
||||||
|
update_sw_128l(st, out0_0, out0_1, out1_0, out1_1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(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))
|
||||||
|
|
||||||
|
z_0, z_1 := z_sw_256(st)
|
||||||
|
copy(tmp[:], cn)
|
||||||
|
|
||||||
|
cn_0, cn_1 := aes.load_interleaved(tmp[:])
|
||||||
|
xn_0, xn_1 := aes.xor_interleaved(cn_0, cn_1, z_0, z_1)
|
||||||
|
|
||||||
|
aes.store_interleaved(tmp[:], xn_0, xn_1)
|
||||||
|
copy(xn, tmp[:])
|
||||||
|
|
||||||
|
for off := len(xn); off < _RATE_256; off += 1 {
|
||||||
|
tmp[off] = 0
|
||||||
|
}
|
||||||
|
xn_0, xn_1 = aes.load_interleaved(tmp[:])
|
||||||
|
update_sw_256(st, xn_0, xn_1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
dec_sw :: proc "contextless" (st: ^State_SW, dst, src: []byte) #no_bounds_check {
|
||||||
|
xi, ci, l := dst, src, len(src)
|
||||||
|
|
||||||
|
switch st.rate {
|
||||||
|
case _RATE_128L:
|
||||||
|
for l >= _RATE_128L {
|
||||||
|
dec_sw_128l(st, xi, ci)
|
||||||
|
xi = xi[_RATE_128L:]
|
||||||
|
ci = ci[_RATE_128L:]
|
||||||
|
l -= _RATE_128L
|
||||||
|
}
|
||||||
|
case _RATE_256:
|
||||||
|
for l >= _RATE_256 {
|
||||||
|
dec_sw_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_sw_128l(st, xi, ci)
|
||||||
|
case _RATE_256:
|
||||||
|
dec_partial_sw_256(st, xi, ci)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
finalize_sw :: proc "contextless" (st: ^State_SW, 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_0, t_1 := aes.load_interleaved(tmp[:])
|
||||||
|
|
||||||
|
t0_0, t0_1, t1_0, t1_1: u64 = ---, ---, ---, ---
|
||||||
|
switch st.rate {
|
||||||
|
case _RATE_128L:
|
||||||
|
t_0, t_1 = aes.xor_interleaved(st.s2_0, st.s2_1, t_0, t_1)
|
||||||
|
for _ in 0 ..< 7 {
|
||||||
|
update_sw_128l(st, t_0, t_1, t_0, t_1)
|
||||||
|
}
|
||||||
|
|
||||||
|
t0_0, t0_1 = aes.xor_interleaved(st.s0_0, st.s0_1, st.s1_0, st.s1_1)
|
||||||
|
t0_0, t0_1 = aes.xor_interleaved(t0_0, t0_1, st.s2_0, st.s2_1)
|
||||||
|
t0_0, t0_1 = aes.xor_interleaved(t0_0, t0_1, st.s3_0, st.s3_1)
|
||||||
|
|
||||||
|
t1_0, t1_1 = aes.xor_interleaved(st.s4_0, st.s4_1, st.s5_0, st.s5_1)
|
||||||
|
t1_0, t1_1 = aes.xor_interleaved(t1_0, t1_1, st.s6_0, st.s6_1)
|
||||||
|
if len(tag) == TAG_SIZE_256 {
|
||||||
|
t1_0, t1_1 = aes.xor_interleaved(t1_0, t1_1, st.s7_0, st.s7_1)
|
||||||
|
}
|
||||||
|
case _RATE_256:
|
||||||
|
t_0, t_1 = aes.xor_interleaved(st.s3_0, st.s3_1, t_0, t_1)
|
||||||
|
for _ in 0 ..< 7 {
|
||||||
|
update_sw_256(st, t_0, t_1)
|
||||||
|
}
|
||||||
|
|
||||||
|
t0_0, t0_1 = aes.xor_interleaved(st.s0_0, st.s0_1, st.s1_0, st.s1_1)
|
||||||
|
t0_0, t0_1 = aes.xor_interleaved(t0_0, t0_1, st.s2_0, st.s2_1)
|
||||||
|
|
||||||
|
t1_0, t1_1 = aes.xor_interleaved(st.s3_0, st.s3_1, st.s4_0, st.s4_1)
|
||||||
|
t1_0, t1_1 = aes.xor_interleaved(t1_0, t1_1, st.s5_0, st.s5_1)
|
||||||
|
}
|
||||||
|
switch len(tag) {
|
||||||
|
case TAG_SIZE_128:
|
||||||
|
t0_0, t0_1 = aes.xor_interleaved(t0_0, t0_1, t1_0, t1_1)
|
||||||
|
aes.store_interleaved(tag, t0_0, t0_1)
|
||||||
|
case TAG_SIZE_256:
|
||||||
|
aes.store_interleaved(tag[:16], t0_0, t0_1)
|
||||||
|
aes.store_interleaved(tag[16:], t1_0, t1_1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
reset_state_sw :: proc "contextless" (st: ^State_SW) {
|
||||||
|
mem.zero_explicit(st, size_of(st^))
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
#+build !amd64
|
||||||
|
package aegis
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
ERR_HW_NOT_SUPPORTED :: "crypto/aegis: hardware implementation unsupported"
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
State_HW :: struct {}
|
||||||
|
|
||||||
|
// is_hardware_accelerated returns true iff hardware accelerated AEGIS
|
||||||
|
// is supported.
|
||||||
|
is_hardware_accelerated :: proc "contextless" () -> bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
init_hw :: proc "contextless" (ctx: ^Context, st: ^State_HW, iv: []byte) {
|
||||||
|
panic_contextless(ERR_HW_NOT_SUPPORTED)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
absorb_hw :: proc "contextless" (st: ^State_HW, aad: []byte) {
|
||||||
|
panic_contextless(ERR_HW_NOT_SUPPORTED)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
enc_hw :: proc "contextless" (st: ^State_HW, dst, src: []byte) {
|
||||||
|
panic_contextless(ERR_HW_NOT_SUPPORTED)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
dec_hw :: proc "contextless" (st: ^State_HW, dst, src: []byte) {
|
||||||
|
panic_contextless(ERR_HW_NOT_SUPPORTED)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
finalize_hw :: proc "contextless" (st: ^State_HW, tag: []byte, ad_len, msg_len: int) {
|
||||||
|
panic_contextless(ERR_HW_NOT_SUPPORTED)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
reset_state_hw :: proc "contextless" (st: ^State_HW) {
|
||||||
|
panic_contextless(ERR_HW_NOT_SUPPORTED)
|
||||||
|
}
|
||||||
@@ -0,0 +1,389 @@
|
|||||||
|
#+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^))
|
||||||
|
}
|
||||||
@@ -21,9 +21,7 @@ Context_CTR :: struct {
|
|||||||
|
|
||||||
// init_ctr initializes a Context_CTR with the provided key and IV.
|
// init_ctr initializes a Context_CTR with the provided key and IV.
|
||||||
init_ctr :: proc(ctx: ^Context_CTR, key, iv: []byte, impl := DEFAULT_IMPLEMENTATION) {
|
init_ctr :: proc(ctx: ^Context_CTR, key, iv: []byte, impl := DEFAULT_IMPLEMENTATION) {
|
||||||
if len(iv) != CTR_IV_SIZE {
|
ensure(len(iv) == CTR_IV_SIZE, "crypto/aes: invalid CTR IV size")
|
||||||
panic("crypto/aes: invalid CTR IV size")
|
|
||||||
}
|
|
||||||
|
|
||||||
init_impl(&ctx._impl, key, impl)
|
init_impl(&ctx._impl, key, impl)
|
||||||
ctx._off = BLOCK_SIZE
|
ctx._off = BLOCK_SIZE
|
||||||
@@ -36,16 +34,14 @@ init_ctr :: proc(ctx: ^Context_CTR, key, iv: []byte, impl := DEFAULT_IMPLEMENTAT
|
|||||||
// keystream, and writes the resulting output to dst. dst and src MUST
|
// keystream, and writes the resulting output to dst. dst and src MUST
|
||||||
// alias exactly or not at all.
|
// alias exactly or not at all.
|
||||||
xor_bytes_ctr :: proc(ctx: ^Context_CTR, dst, src: []byte) {
|
xor_bytes_ctr :: proc(ctx: ^Context_CTR, dst, src: []byte) {
|
||||||
assert(ctx._is_initialized)
|
ensure(ctx._is_initialized)
|
||||||
|
|
||||||
src, dst := src, dst
|
src, dst := src, dst
|
||||||
if dst_len := len(dst); dst_len < len(src) {
|
if dst_len := len(dst); dst_len < len(src) {
|
||||||
src = src[:dst_len]
|
src = src[:dst_len]
|
||||||
}
|
}
|
||||||
|
|
||||||
if bytes.alias_inexactly(dst, src) {
|
ensure(!bytes.alias_inexactly(dst, src), "crypto/aes: dst and src alias inexactly")
|
||||||
panic("crypto/aes: dst and src alias inexactly")
|
|
||||||
}
|
|
||||||
|
|
||||||
#no_bounds_check for remaining := len(src); remaining > 0; {
|
#no_bounds_check for remaining := len(src); remaining > 0; {
|
||||||
// Process multiple blocks at once
|
// Process multiple blocks at once
|
||||||
@@ -82,7 +78,7 @@ xor_bytes_ctr :: proc(ctx: ^Context_CTR, dst, src: []byte) {
|
|||||||
|
|
||||||
// keystream_bytes_ctr fills dst with the raw AES-CTR keystream output.
|
// keystream_bytes_ctr fills dst with the raw AES-CTR keystream output.
|
||||||
keystream_bytes_ctr :: proc(ctx: ^Context_CTR, dst: []byte) {
|
keystream_bytes_ctr :: proc(ctx: ^Context_CTR, dst: []byte) {
|
||||||
assert(ctx._is_initialized)
|
ensure(ctx._is_initialized)
|
||||||
|
|
||||||
dst := dst
|
dst := dst
|
||||||
#no_bounds_check for remaining := len(dst); remaining > 0; {
|
#no_bounds_check for remaining := len(dst); remaining > 0; {
|
||||||
|
|||||||
@@ -19,11 +19,9 @@ init_ecb :: proc(ctx: ^Context_ECB, key: []byte, impl := DEFAULT_IMPLEMENTATION)
|
|||||||
|
|
||||||
// encrypt_ecb encrypts the BLOCK_SIZE buffer src, and writes the result to dst.
|
// encrypt_ecb encrypts the BLOCK_SIZE buffer src, and writes the result to dst.
|
||||||
encrypt_ecb :: proc(ctx: ^Context_ECB, dst, src: []byte) {
|
encrypt_ecb :: proc(ctx: ^Context_ECB, dst, src: []byte) {
|
||||||
assert(ctx._is_initialized)
|
ensure(ctx._is_initialized)
|
||||||
|
ensure(len(dst) == BLOCK_SIZE, "crypto/aes: invalid dst size")
|
||||||
if len(dst) != BLOCK_SIZE || len(src) != BLOCK_SIZE {
|
ensure(len(dst) == BLOCK_SIZE, "crypto/aes: invalid src size")
|
||||||
panic("crypto/aes: invalid buffer size(s)")
|
|
||||||
}
|
|
||||||
|
|
||||||
switch &impl in ctx._impl {
|
switch &impl in ctx._impl {
|
||||||
case ct64.Context:
|
case ct64.Context:
|
||||||
@@ -35,11 +33,9 @@ encrypt_ecb :: proc(ctx: ^Context_ECB, dst, src: []byte) {
|
|||||||
|
|
||||||
// decrypt_ecb decrypts the BLOCK_SIZE buffer src, and writes the result to dst.
|
// decrypt_ecb decrypts the BLOCK_SIZE buffer src, and writes the result to dst.
|
||||||
decrypt_ecb :: proc(ctx: ^Context_ECB, dst, src: []byte) {
|
decrypt_ecb :: proc(ctx: ^Context_ECB, dst, src: []byte) {
|
||||||
assert(ctx._is_initialized)
|
ensure(ctx._is_initialized)
|
||||||
|
ensure(len(dst) == BLOCK_SIZE, "crypto/aes: invalid dst size")
|
||||||
if len(dst) != BLOCK_SIZE || len(src) != BLOCK_SIZE {
|
ensure(len(dst) == BLOCK_SIZE, "crypto/aes: invalid src size")
|
||||||
panic("crypto/aes: invalid buffer size(s)")
|
|
||||||
}
|
|
||||||
|
|
||||||
switch &impl in ctx._impl {
|
switch &impl in ctx._impl {
|
||||||
case ct64.Context:
|
case ct64.Context:
|
||||||
|
|||||||
@@ -36,15 +36,11 @@ init_gcm :: proc(ctx: ^Context_GCM, key: []byte, impl := DEFAULT_IMPLEMENTATION)
|
|||||||
//
|
//
|
||||||
// dst and plaintext MUST alias exactly or not at all.
|
// dst and plaintext MUST alias exactly or not at all.
|
||||||
seal_gcm :: proc(ctx: ^Context_GCM, dst, tag, iv, aad, plaintext: []byte) {
|
seal_gcm :: proc(ctx: ^Context_GCM, dst, tag, iv, aad, plaintext: []byte) {
|
||||||
assert(ctx._is_initialized)
|
ensure(ctx._is_initialized)
|
||||||
|
|
||||||
gcm_validate_common_slice_sizes(tag, iv, aad, plaintext)
|
gcm_validate_common_slice_sizes(tag, iv, aad, plaintext)
|
||||||
if len(dst) != len(plaintext) {
|
ensure(len(dst) == len(plaintext), "crypto/aes: invalid destination ciphertext size")
|
||||||
panic("crypto/aes: invalid destination ciphertext size")
|
ensure(!bytes.alias_inexactly(dst, plaintext), "crypto/aes: dst and plaintext alias inexactly")
|
||||||
}
|
|
||||||
if bytes.alias_inexactly(dst, plaintext) {
|
|
||||||
panic("crypto/aes: dst and plaintext alias inexactly")
|
|
||||||
}
|
|
||||||
|
|
||||||
if impl, is_hw := ctx._impl.(Context_Impl_Hardware); is_hw {
|
if impl, is_hw := ctx._impl.(Context_Impl_Hardware); is_hw {
|
||||||
gcm_seal_hw(&impl, dst, tag, iv, aad, plaintext)
|
gcm_seal_hw(&impl, dst, tag, iv, aad, plaintext)
|
||||||
@@ -76,15 +72,11 @@ seal_gcm :: proc(ctx: ^Context_GCM, dst, tag, iv, aad, plaintext: []byte) {
|
|||||||
// dst and plaintext MUST alias exactly or not at all.
|
// dst and plaintext MUST alias exactly or not at all.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
open_gcm :: proc(ctx: ^Context_GCM, dst, iv, aad, ciphertext, tag: []byte) -> bool {
|
open_gcm :: proc(ctx: ^Context_GCM, dst, iv, aad, ciphertext, tag: []byte) -> bool {
|
||||||
assert(ctx._is_initialized)
|
ensure(ctx._is_initialized)
|
||||||
|
|
||||||
gcm_validate_common_slice_sizes(tag, iv, aad, ciphertext)
|
gcm_validate_common_slice_sizes(tag, iv, aad, ciphertext)
|
||||||
if len(dst) != len(ciphertext) {
|
ensure(len(dst) == len(ciphertext), "crypto/aes: invalid destination plaintext size")
|
||||||
panic("crypto/aes: invalid destination plaintext size")
|
ensure(!bytes.alias_inexactly(dst, ciphertext), "crypto/aes: dst and ciphertext alias inexactly")
|
||||||
}
|
|
||||||
if bytes.alias_inexactly(dst, ciphertext) {
|
|
||||||
panic("crypto/aes: dst and ciphertext alias inexactly")
|
|
||||||
}
|
|
||||||
|
|
||||||
if impl, is_hw := ctx._impl.(Context_Impl_Hardware); is_hw {
|
if impl, is_hw := ctx._impl.(Context_Impl_Hardware); is_hw {
|
||||||
return gcm_open_hw(&impl, dst, iv, aad, ciphertext, tag)
|
return gcm_open_hw(&impl, dst, iv, aad, ciphertext, tag)
|
||||||
@@ -122,21 +114,13 @@ reset_gcm :: proc "contextless" (ctx: ^Context_GCM) {
|
|||||||
|
|
||||||
@(private = "file")
|
@(private = "file")
|
||||||
gcm_validate_common_slice_sizes :: proc(tag, iv, aad, text: []byte) {
|
gcm_validate_common_slice_sizes :: proc(tag, iv, aad, text: []byte) {
|
||||||
if len(tag) != GCM_TAG_SIZE {
|
ensure(len(tag) == GCM_TAG_SIZE, "crypto/aes: invalid GCM tag size")
|
||||||
panic("crypto/aes: invalid GCM tag size")
|
|
||||||
}
|
|
||||||
|
|
||||||
// The specification supports IVs in the range [1, 2^64) bits.
|
// The specification supports IVs in the range [1, 2^64) bits.
|
||||||
if l := len(iv); l == 0 || u64(l) >= GCM_IV_SIZE_MAX {
|
ensure(len(iv) == 0 || u64(len(iv)) <= GCM_IV_SIZE_MAX, "crypto/aes: invalid GCM IV size")
|
||||||
panic("crypto/aes: invalid GCM IV size")
|
|
||||||
}
|
|
||||||
|
|
||||||
if aad_len := u64(len(aad)); aad_len > GCM_A_MAX {
|
ensure(u64(len(aad)) <= GCM_A_MAX, "crypto/aes: oversized GCM aad")
|
||||||
panic("crypto/aes: oversized GCM aad")
|
ensure(u64(len(text)) <= GCM_P_MAX, "crypto/aes: oversized GCM data")
|
||||||
}
|
|
||||||
if text_len := u64(len(text)); text_len > GCM_P_MAX {
|
|
||||||
panic("crypto/aes: oversized GCM src data")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private = "file")
|
@(private = "file")
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ gctr_hw :: proc(
|
|||||||
// BUG: Sticking this in gctr_hw (like the other implementations) crashes
|
// BUG: Sticking this in gctr_hw (like the other implementations) crashes
|
||||||
// the compiler.
|
// the compiler.
|
||||||
//
|
//
|
||||||
// src/check_expr.cpp(7892): Assertion Failure: `c->curr_proc_decl->entity`
|
// src/check_expr.cpp(8104): Assertion Failure: `c->curr_proc_decl->entity`
|
||||||
@(private = "file", enable_target_feature = "sse4.1")
|
@(private = "file", enable_target_feature = "sse4.1")
|
||||||
hw_inc_ctr32 :: #force_inline proc "contextless" (src: ^x86.__m128i, ctr: u32) -> (x86.__m128i, u32) {
|
hw_inc_ctr32 :: #force_inline proc "contextless" (src: ^x86.__m128i, ctr: u32) -> (x86.__m128i, u32) {
|
||||||
ret := x86._mm_insert_epi32(src^, i32(intrinsics.byte_swap(ctr)), 3)
|
ret := x86._mm_insert_epi32(src^, i32(intrinsics.byte_swap(ctr)), 3)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ package blake2b
|
|||||||
import "../_blake2"
|
import "../_blake2"
|
||||||
|
|
||||||
// DIGEST_SIZE is the BLAKE2b digest size in bytes.
|
// DIGEST_SIZE is the BLAKE2b digest size in bytes.
|
||||||
DIGEST_SIZE :: 64
|
DIGEST_SIZE :: _blake2.BLAKE2B_SIZE
|
||||||
|
|
||||||
// BLOCK_SIZE is the BLAKE2b block size in bytes.
|
// BLOCK_SIZE is the BLAKE2b block size in bytes.
|
||||||
BLOCK_SIZE :: _blake2.BLAKE2B_BLOCK_SIZE
|
BLOCK_SIZE :: _blake2.BLAKE2B_BLOCK_SIZE
|
||||||
@@ -27,9 +27,11 @@ BLOCK_SIZE :: _blake2.BLAKE2B_BLOCK_SIZE
|
|||||||
Context :: _blake2.Blake2b_Context
|
Context :: _blake2.Blake2b_Context
|
||||||
|
|
||||||
// init initializes a Context with the default BLAKE2b config.
|
// init initializes a Context with the default BLAKE2b config.
|
||||||
init :: proc(ctx: ^Context) {
|
init :: proc(ctx: ^Context, digest_size := DIGEST_SIZE) {
|
||||||
|
ensure(digest_size <= _blake2.MAX_SIZE, "crypto/blake2b: invalid digest size")
|
||||||
|
|
||||||
cfg: _blake2.Blake2_Config
|
cfg: _blake2.Blake2_Config
|
||||||
cfg.size = _blake2.BLAKE2B_SIZE
|
cfg.size = u8(digest_size)
|
||||||
_blake2.init(ctx, &cfg)
|
_blake2.init(ctx, &cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ package blake2s
|
|||||||
import "../_blake2"
|
import "../_blake2"
|
||||||
|
|
||||||
// DIGEST_SIZE is the BLAKE2s digest size in bytes.
|
// DIGEST_SIZE is the BLAKE2s digest size in bytes.
|
||||||
DIGEST_SIZE :: 32
|
DIGEST_SIZE :: _blake2.BLAKE2S_SIZE
|
||||||
|
|
||||||
// BLOCK_SIZE is the BLAKE2s block size in bytes.
|
// BLOCK_SIZE is the BLAKE2s block size in bytes.
|
||||||
BLOCK_SIZE :: _blake2.BLAKE2S_BLOCK_SIZE
|
BLOCK_SIZE :: _blake2.BLAKE2S_BLOCK_SIZE
|
||||||
@@ -27,9 +27,11 @@ BLOCK_SIZE :: _blake2.BLAKE2S_BLOCK_SIZE
|
|||||||
Context :: _blake2.Blake2s_Context
|
Context :: _blake2.Blake2s_Context
|
||||||
|
|
||||||
// init initializes a Context with the default BLAKE2s config.
|
// init initializes a Context with the default BLAKE2s config.
|
||||||
init :: proc(ctx: ^Context) {
|
init :: proc(ctx: ^Context, digest_size := DIGEST_SIZE) {
|
||||||
|
ensure(digest_size <= _blake2.MAX_SIZE, "crypto/blake2s: invalid digest size")
|
||||||
|
|
||||||
cfg: _blake2.Blake2_Config
|
cfg: _blake2.Blake2_Config
|
||||||
cfg.size = _blake2.BLAKE2S_SIZE
|
cfg.size = u8(digest_size)
|
||||||
_blake2.init(ctx, &cfg)
|
_blake2.init(ctx, &cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,12 +27,8 @@ Context :: struct {
|
|||||||
// init inititializes a Context for ChaCha20 or XChaCha20 with the provided
|
// init inititializes a Context for ChaCha20 or XChaCha20 with the provided
|
||||||
// key and iv.
|
// key and iv.
|
||||||
init :: proc(ctx: ^Context, key, iv: []byte, impl := DEFAULT_IMPLEMENTATION) {
|
init :: proc(ctx: ^Context, key, iv: []byte, impl := DEFAULT_IMPLEMENTATION) {
|
||||||
if len(key) != KEY_SIZE {
|
ensure(len(key) == KEY_SIZE, "crypto/chacha20: invalid (X)ChaCha20 key size")
|
||||||
panic("crypto/chacha20: invalid (X)ChaCha20 key size")
|
ensure(len(iv) == IV_SIZE || len(iv) == XIV_SIZE, "crypto/chacha20: invalid (X)ChaCha20 IV size")
|
||||||
}
|
|
||||||
if l := len(iv); l != IV_SIZE && l != XIV_SIZE {
|
|
||||||
panic("crypto/chacha20: invalid (X)ChaCha20 IV size")
|
|
||||||
}
|
|
||||||
|
|
||||||
k, n := key, iv
|
k, n := key, iv
|
||||||
|
|
||||||
@@ -67,16 +63,14 @@ seek :: proc(ctx: ^Context, block_nr: u64) {
|
|||||||
// keystream, and writes the resulting output to dst. Dst and src MUST
|
// keystream, and writes the resulting output to dst. Dst and src MUST
|
||||||
// alias exactly or not at all.
|
// alias exactly or not at all.
|
||||||
xor_bytes :: proc(ctx: ^Context, dst, src: []byte) {
|
xor_bytes :: proc(ctx: ^Context, dst, src: []byte) {
|
||||||
assert(ctx._state._is_initialized)
|
ensure(ctx._state._is_initialized)
|
||||||
|
|
||||||
src, dst := src, dst
|
src, dst := src, dst
|
||||||
if dst_len := len(dst); dst_len < len(src) {
|
if dst_len := len(dst); dst_len < len(src) {
|
||||||
src = src[:dst_len]
|
src = src[:dst_len]
|
||||||
}
|
}
|
||||||
|
|
||||||
if bytes.alias_inexactly(dst, src) {
|
ensure(!bytes.alias_inexactly(dst, src), "crypto/chacha20: dst and src alias inexactly")
|
||||||
panic("crypto/chacha20: dst and src alias inexactly")
|
|
||||||
}
|
|
||||||
|
|
||||||
st := &ctx._state
|
st := &ctx._state
|
||||||
#no_bounds_check for remaining := len(src); remaining > 0; {
|
#no_bounds_check for remaining := len(src); remaining > 0; {
|
||||||
@@ -114,7 +108,7 @@ xor_bytes :: proc(ctx: ^Context, dst, src: []byte) {
|
|||||||
|
|
||||||
// keystream_bytes fills dst with the raw (X)ChaCha20 keystream output.
|
// keystream_bytes fills dst with the raw (X)ChaCha20 keystream output.
|
||||||
keystream_bytes :: proc(ctx: ^Context, dst: []byte) {
|
keystream_bytes :: proc(ctx: ^Context, dst: []byte) {
|
||||||
assert(ctx._state._is_initialized)
|
ensure(ctx._state._is_initialized)
|
||||||
|
|
||||||
dst, st := dst, &ctx._state
|
dst, st := dst, &ctx._state
|
||||||
#no_bounds_check for remaining := len(dst); remaining > 0; {
|
#no_bounds_check for remaining := len(dst); remaining > 0; {
|
||||||
|
|||||||
@@ -29,13 +29,9 @@ _P_MAX :: 64 * 0xffffffff // 64 * (2^32-1)
|
|||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
_validate_common_slice_sizes :: proc (tag, iv, aad, text: []byte, is_xchacha: bool) {
|
_validate_common_slice_sizes :: proc (tag, iv, aad, text: []byte, is_xchacha: bool) {
|
||||||
if len(tag) != TAG_SIZE {
|
|
||||||
panic("crypto/chacha20poly1305: invalid destination tag size")
|
|
||||||
}
|
|
||||||
expected_iv_len := is_xchacha ? XIV_SIZE : IV_SIZE
|
expected_iv_len := is_xchacha ? XIV_SIZE : IV_SIZE
|
||||||
if len(iv) != expected_iv_len {
|
ensure(len(tag) == TAG_SIZE, "crypto/chacha20poly1305: invalid destination tag size")
|
||||||
panic("crypto/chacha20poly1305: invalid IV size")
|
ensure(len(iv) == expected_iv_len, "crypto/chacha20poly1305: invalid IV size")
|
||||||
}
|
|
||||||
|
|
||||||
#assert(size_of(int) == 8 || size_of(int) <= 4)
|
#assert(size_of(int) == 8 || size_of(int) <= 4)
|
||||||
when size_of(int) == 8 {
|
when size_of(int) == 8 {
|
||||||
@@ -45,13 +41,11 @@ _validate_common_slice_sizes :: proc (tag, iv, aad, text: []byte, is_xchacha: bo
|
|||||||
// A_MAX is limited by size_of(int), so there is no need to
|
// A_MAX is limited by size_of(int), so there is no need to
|
||||||
// enforce it. P_MAX only needs to be checked on 64-bit targets,
|
// enforce it. P_MAX only needs to be checked on 64-bit targets,
|
||||||
// for reasons that should be obvious.
|
// for reasons that should be obvious.
|
||||||
if text_len := len(text); text_len > _P_MAX {
|
ensure(len(text) <= _P_MAX, "crypto/chacha20poly1305: oversized src data")
|
||||||
panic("crypto/chacha20poly1305: oversized src data")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private, rodata)
|
||||||
_PAD: [16]byte
|
_PAD: [16]byte
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
@@ -71,9 +65,7 @@ Context :: struct {
|
|||||||
|
|
||||||
// init initializes a Context with the provided key, for AEAD_CHACHA20_POLY1305.
|
// init initializes a Context with the provided key, for AEAD_CHACHA20_POLY1305.
|
||||||
init :: proc(ctx: ^Context, key: []byte, impl := chacha20.DEFAULT_IMPLEMENTATION) {
|
init :: proc(ctx: ^Context, key: []byte, impl := chacha20.DEFAULT_IMPLEMENTATION) {
|
||||||
if len(key) != KEY_SIZE {
|
ensure(len(key) == KEY_SIZE, "crypto/chacha20poly1305: invalid key size")
|
||||||
panic("crypto/chacha20poly1305: invalid key size")
|
|
||||||
}
|
|
||||||
|
|
||||||
copy(ctx._key[:], key)
|
copy(ctx._key[:], key)
|
||||||
ctx._impl = impl
|
ctx._impl = impl
|
||||||
@@ -96,11 +88,11 @@ init_xchacha :: proc(ctx: ^Context, key: []byte, impl := chacha20.DEFAULT_IMPLEM
|
|||||||
//
|
//
|
||||||
// dst and plaintext MUST alias exactly or not at all.
|
// dst and plaintext MUST alias exactly or not at all.
|
||||||
seal :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
|
seal :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
|
||||||
|
ensure(ctx._is_initialized)
|
||||||
|
|
||||||
ciphertext := dst
|
ciphertext := dst
|
||||||
_validate_common_slice_sizes(tag, iv, aad, plaintext, ctx._is_xchacha)
|
_validate_common_slice_sizes(tag, iv, aad, plaintext, ctx._is_xchacha)
|
||||||
if len(ciphertext) != len(plaintext) {
|
ensure(len(ciphertext) == len(plaintext), "crypto/chacha20poly1305: invalid destination ciphertext size")
|
||||||
panic("crypto/chacha20poly1305: invalid destination ciphertext size")
|
|
||||||
}
|
|
||||||
|
|
||||||
stream_ctx: chacha20.Context = ---
|
stream_ctx: chacha20.Context = ---
|
||||||
chacha20.init(&stream_ctx, ctx._key[:],iv, ctx._impl)
|
chacha20.init(&stream_ctx, ctx._key[:],iv, ctx._impl)
|
||||||
@@ -151,11 +143,11 @@ seal :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
|
|||||||
// dst and plaintext MUST alias exactly or not at all.
|
// dst and plaintext MUST alias exactly or not at all.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
open :: proc(ctx: ^Context, dst, iv, aad, ciphertext, tag: []byte) -> bool {
|
open :: proc(ctx: ^Context, dst, iv, aad, ciphertext, tag: []byte) -> bool {
|
||||||
|
ensure(ctx._is_initialized)
|
||||||
|
|
||||||
plaintext := dst
|
plaintext := dst
|
||||||
_validate_common_slice_sizes(tag, iv, aad, ciphertext, ctx._is_xchacha)
|
_validate_common_slice_sizes(tag, iv, aad, ciphertext, ctx._is_xchacha)
|
||||||
if len(ciphertext) != len(plaintext) {
|
ensure(len(ciphertext) == len(plaintext), "crypto/chacha20poly1305: invalid destination plaintext size")
|
||||||
panic("crypto/chacha20poly1305: invalid destination plaintext size")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: Unlike encrypt, this can fail early, so use defer for
|
// Note: Unlike encrypt, this can fail early, so use defer for
|
||||||
// sanitization rather than assuming control flow reaches certain
|
// sanitization rather than assuming control flow reaches certain
|
||||||
|
|||||||
@@ -0,0 +1,280 @@
|
|||||||
|
/*
|
||||||
|
package deoxysii implements the Deoxys-II-256 Authenticated Encryption
|
||||||
|
with Additional Data algorithm.
|
||||||
|
|
||||||
|
- [[ https://sites.google.com/view/deoxyscipher ]]
|
||||||
|
- [[ https://thomaspeyrin.github.io/web/assets/docs/papers/Jean-etal-JoC2021.pdf ]]
|
||||||
|
*/
|
||||||
|
package deoxysii
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
import "core:bytes"
|
||||||
|
import "core:crypto/aes"
|
||||||
|
import "core:mem"
|
||||||
|
import "core:simd"
|
||||||
|
|
||||||
|
// KEY_SIZE is the Deoxys-II-256 key size in bytes.
|
||||||
|
KEY_SIZE :: 32
|
||||||
|
// IV_SIZE iss the Deoxys-II-256 IV size in bytes.
|
||||||
|
IV_SIZE :: 15 // 120-bits
|
||||||
|
// TAG_SIZE is the Deoxys-II-256 tag size in bytes.
|
||||||
|
TAG_SIZE :: 16
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
PREFIX_AD_BLOCK :: 0b0010
|
||||||
|
@(private)
|
||||||
|
PREFIX_AD_FINAL :: 0b0110
|
||||||
|
@(private)
|
||||||
|
PREFIX_MSG_BLOCK :: 0b0000
|
||||||
|
@(private)
|
||||||
|
PREFIX_MSG_FINAL :: 0b0100
|
||||||
|
@(private)
|
||||||
|
PREFIX_TAG :: 0b0001
|
||||||
|
@(private)
|
||||||
|
PREFIX_SHIFT :: 4
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
BC_ROUNDS :: 16
|
||||||
|
@(private)
|
||||||
|
BLOCK_SIZE :: aes.BLOCK_SIZE
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
_LFSR2_MASK :: simd.u8x16{
|
||||||
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||||
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||||
|
}
|
||||||
|
@(private = "file")
|
||||||
|
_LFSR3_MASK :: simd.u8x16{
|
||||||
|
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
||||||
|
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
||||||
|
}
|
||||||
|
@(private = "file")
|
||||||
|
_LFSR_SH1 :: _LFSR2_MASK
|
||||||
|
@(private = "file")
|
||||||
|
_LFSR_SH5 :: simd.u8x16{
|
||||||
|
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
||||||
|
0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
|
||||||
|
}
|
||||||
|
@(private = "file")
|
||||||
|
_LFSR_SH7 :: simd.u8x16{
|
||||||
|
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
||||||
|
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
||||||
|
}
|
||||||
|
@(private = "file", rodata)
|
||||||
|
_RCONS := []byte {
|
||||||
|
0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a,
|
||||||
|
0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39,
|
||||||
|
0x72,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context is a keyed Deoxys-II-256 instance.
|
||||||
|
Context :: struct {
|
||||||
|
_subkeys: [BC_ROUNDS+1][16]byte,
|
||||||
|
_impl: aes.Implementation,
|
||||||
|
_is_initialized: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_validate_common_slice_sizes :: proc (ctx: ^Context, tag, iv, aad, text: []byte) {
|
||||||
|
ensure(len(tag) == TAG_SIZE, "crypto/deoxysii: invalid tag size")
|
||||||
|
ensure(len(iv) == IV_SIZE, "crypto/deoxysii: invalid IV size")
|
||||||
|
|
||||||
|
#assert(size_of(int) == 8 || size_of(int) <= 4)
|
||||||
|
// For the nonce-misuse resistant mode, the total size of the
|
||||||
|
// associated data and the total size of the message do not exceed
|
||||||
|
// `16 * 2^max_l * 2^max_m bytes`, thus 2^128 bytes for all variants
|
||||||
|
// of Deoxys-II. Moreover, the maximum number of messages that can
|
||||||
|
// be handled for a same key is 2^max_m, that is 2^64 for all variants
|
||||||
|
// of Deoxys.
|
||||||
|
}
|
||||||
|
|
||||||
|
// init initializes a Context with the provided key.
|
||||||
|
init :: proc(ctx: ^Context, key: []byte, impl := aes.DEFAULT_IMPLEMENTATION) {
|
||||||
|
ensure(len(key) == KEY_SIZE, "crypto/deoxysii: invalid key size")
|
||||||
|
|
||||||
|
ctx._impl = impl
|
||||||
|
if ctx._impl == .Hardware && !is_hardware_accelerated() {
|
||||||
|
ctx._impl = .Portable
|
||||||
|
}
|
||||||
|
|
||||||
|
derive_ks(ctx, key)
|
||||||
|
|
||||||
|
ctx._is_initialized = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// seal encrypts the plaintext and authenticates the aad and ciphertext,
|
||||||
|
// with the provided Context and iv, stores the output in dst and tag.
|
||||||
|
//
|
||||||
|
// dst and plaintext MUST alias exactly or not at all.
|
||||||
|
seal :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
|
||||||
|
ensure(ctx._is_initialized)
|
||||||
|
|
||||||
|
_validate_common_slice_sizes(ctx, tag, iv, aad, plaintext)
|
||||||
|
ensure(len(dst) == len(plaintext), "crypto/deoxysii: invalid destination ciphertext size")
|
||||||
|
ensure(!bytes.alias_inexactly(dst, plaintext), "crypto/deoxysii: dst and plaintext alias inexactly")
|
||||||
|
|
||||||
|
switch ctx._impl {
|
||||||
|
case .Hardware:
|
||||||
|
e_hw(ctx, dst, tag, iv, aad, plaintext)
|
||||||
|
case .Portable:
|
||||||
|
e_ref(ctx, dst, tag, iv, aad, plaintext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// fails, the destination buffer will be zeroed.
|
||||||
|
//
|
||||||
|
// dst and plaintext MUST alias exactly or not at all.
|
||||||
|
@(require_results)
|
||||||
|
open :: proc(ctx: ^Context, dst, iv, aad, ciphertext, tag: []byte) -> bool {
|
||||||
|
ensure(ctx._is_initialized)
|
||||||
|
|
||||||
|
_validate_common_slice_sizes(ctx, tag, iv, aad, ciphertext)
|
||||||
|
ensure(len(dst) == len(ciphertext), "crypto/deoxysii: invalid destination plaintext size")
|
||||||
|
ensure(!bytes.alias_inexactly(dst, ciphertext), "crypto/deoxysii: dst and ciphertext alias inexactly")
|
||||||
|
|
||||||
|
ok: bool
|
||||||
|
switch ctx._impl {
|
||||||
|
case .Hardware:
|
||||||
|
ok = d_hw(ctx, dst, iv, aad, ciphertext, tag)
|
||||||
|
case .Portable:
|
||||||
|
ok = d_ref(ctx, dst, iv, aad, ciphertext, tag)
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
mem.zero_explicit(raw_data(dst), len(ciphertext))
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset sanitizes the Context. The Context must be
|
||||||
|
// re-initialized to be used again.
|
||||||
|
reset :: proc "contextless" (ctx: ^Context) {
|
||||||
|
mem.zero_explicit(&ctx._subkeys, len(ctx._subkeys))
|
||||||
|
ctx._is_initialized = false
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
derive_ks :: proc "contextless" (ctx: ^Context, key: []byte) {
|
||||||
|
// Derive the constant component of each subtweakkey.
|
||||||
|
//
|
||||||
|
// The key schedule is as thus:
|
||||||
|
//
|
||||||
|
// STK_i = TK1_i ^ TK2_i ^ TK3_i ^ RC_i
|
||||||
|
//
|
||||||
|
// TK1_i = h(TK1_(i-1))
|
||||||
|
// TK2_i = h(LFSR2(TK2_(i-1)))
|
||||||
|
// TK3_i = h(LFSR3(TK2_(i-1)))
|
||||||
|
//
|
||||||
|
// where:
|
||||||
|
//
|
||||||
|
// KT = K || T
|
||||||
|
// W3 = KT[:16]
|
||||||
|
// W2 = KT[16:32]
|
||||||
|
// W1 = KT[32:]
|
||||||
|
//
|
||||||
|
// TK1_0 = W1
|
||||||
|
// TK2_0 = W2
|
||||||
|
// TK3_0 = W3
|
||||||
|
//
|
||||||
|
// As `K` is fixed per Context, the XORs of `TK3_0 .. TK3_n`,
|
||||||
|
// `TK2_0 .. TK2_n` and RC_i can be precomputed in advance like
|
||||||
|
// thus:
|
||||||
|
//
|
||||||
|
// subkey_i = TK3_i ^ TK2_i ^ RC_i
|
||||||
|
//
|
||||||
|
// When it is time to actually call Deoxys-BC-384, it is then
|
||||||
|
// a simple matter of deriving each round subtweakkey via:
|
||||||
|
//
|
||||||
|
// TK1_0 = T (Tweak)
|
||||||
|
// STK_0 = subkey_0 ^ TK1_0
|
||||||
|
// STK_i = subkey_i (precomputed) ^ H(TK1_(i-1))
|
||||||
|
//
|
||||||
|
// We opt to use SIMD here and for the subtweakkey deriviation
|
||||||
|
// as `H()` is typically a single vector instruction.
|
||||||
|
|
||||||
|
tk2 := intrinsics.unaligned_load((^simd.u8x16)(raw_data(key[16:])))
|
||||||
|
tk3 := intrinsics.unaligned_load((^simd.u8x16)(raw_data(key)))
|
||||||
|
|
||||||
|
// subkey_0 does not apply LFSR2/3 or H.
|
||||||
|
intrinsics.unaligned_store(
|
||||||
|
(^simd.u8x16)(&ctx._subkeys[0]),
|
||||||
|
simd.bit_xor(
|
||||||
|
tk2,
|
||||||
|
simd.bit_xor(
|
||||||
|
tk3,
|
||||||
|
rcon(0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Precompute k_1 .. k_16.
|
||||||
|
for i in 1 ..< BC_ROUNDS+1 {
|
||||||
|
tk2 = h(lfsr2(tk2))
|
||||||
|
tk3 = h(lfsr3(tk3))
|
||||||
|
intrinsics.unaligned_store(
|
||||||
|
(^simd.u8x16)(&ctx._subkeys[i]),
|
||||||
|
simd.bit_xor(
|
||||||
|
tk2,
|
||||||
|
simd.bit_xor(
|
||||||
|
tk3,
|
||||||
|
rcon(i),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
lfsr2 :: #force_inline proc "contextless" (tk: simd.u8x16) -> simd.u8x16 {
|
||||||
|
// LFSR2 is a application of the following LFSR to each byte of input.
|
||||||
|
// (x7||x6||x5||x4||x3||x2||x1||x0) -> (x6||x5||x4||x3||x2||x1||x0||x7 ^ x5)
|
||||||
|
return simd.bit_or(
|
||||||
|
simd.shl(tk, _LFSR_SH1),
|
||||||
|
simd.bit_and(
|
||||||
|
simd.bit_xor(
|
||||||
|
simd.shr(tk, _LFSR_SH7), // x7
|
||||||
|
simd.shr(tk, _LFSR_SH5), // x5
|
||||||
|
),
|
||||||
|
_LFSR2_MASK,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
lfsr3 :: #force_inline proc "contextless" (tk: simd.u8x16) -> simd.u8x16 {
|
||||||
|
// LFSR3 is a application of the following LFSR to each byte of input.
|
||||||
|
// (x7||x6||x5||x4||x3||x2||x1||x0) -> (x0 ^ x6||x7||x6||x5||x4||x3||x2||x1)
|
||||||
|
return simd.bit_or(
|
||||||
|
simd.shr(tk, _LFSR_SH1),
|
||||||
|
simd.bit_and(
|
||||||
|
simd.bit_xor(
|
||||||
|
simd.shl(tk, _LFSR_SH7), // x0
|
||||||
|
simd.shl(tk, _LFSR_SH1), // x6
|
||||||
|
),
|
||||||
|
_LFSR3_MASK,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
h :: #force_inline proc "contextless" (tk: simd.u8x16) -> simd.u8x16 {
|
||||||
|
return simd.swizzle(
|
||||||
|
tk,
|
||||||
|
0x01, 0x06, 0x0b, 0x0c, 0x05, 0x0a, 0x0f, 0x00,
|
||||||
|
0x09, 0x0e, 0x03, 0x04, 0x0d, 0x02, 0x07, 0x08,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
rcon :: #force_inline proc "contextless" (rd: int) -> simd.u8x16 #no_bounds_check {
|
||||||
|
rc := _RCONS[rd]
|
||||||
|
return simd.u8x16{
|
||||||
|
1, 2, 4, 8,
|
||||||
|
rc, rc, rc, rc,
|
||||||
|
0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,399 @@
|
|||||||
|
package deoxysii
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
import "core:crypto"
|
||||||
|
import aes "core:crypto/_aes/ct64"
|
||||||
|
import "core:encoding/endian"
|
||||||
|
import "core:mem"
|
||||||
|
import "core:simd"
|
||||||
|
|
||||||
|
// This uses the bitlsiced 64-bit general purpose register SWAR AES
|
||||||
|
// round function. The encryption pass skips orthogonalizing the
|
||||||
|
// AES round function input as it is aways going to be the leading 0
|
||||||
|
// padded IV, and doing a 64-byte copy is faster.
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
TWEAK_SIZE :: 16
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
State_SW :: struct {
|
||||||
|
ctx: ^Context,
|
||||||
|
q_stk, q_b: [8]u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
auth_tweak :: #force_inline proc "contextless" (
|
||||||
|
dst: ^[TWEAK_SIZE]byte,
|
||||||
|
prefix: byte,
|
||||||
|
block_nr: int,
|
||||||
|
) {
|
||||||
|
endian.unchecked_put_u64be(dst[8:], u64(block_nr))
|
||||||
|
endian.unchecked_put_u64le(dst[0:], u64(prefix) << PREFIX_SHIFT) // dst[0] = prefix << PREFIX_SHIFT
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
enc_tweak :: #force_inline proc "contextless" (
|
||||||
|
dst: ^[TWEAK_SIZE]byte,
|
||||||
|
tag: ^[TAG_SIZE]byte,
|
||||||
|
block_nr: int,
|
||||||
|
) {
|
||||||
|
tmp: [8]byte
|
||||||
|
endian.unchecked_put_u64be(tmp[:], u64(block_nr))
|
||||||
|
|
||||||
|
copy(dst[:], tag[:])
|
||||||
|
dst[0] |= 0x80
|
||||||
|
for i in 0 ..< 8 {
|
||||||
|
dst[i+8] ~= tmp[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
enc_plaintext :: #force_inline proc "contextless" (
|
||||||
|
dst: ^[8]u64,
|
||||||
|
iv: []byte,
|
||||||
|
) {
|
||||||
|
tmp: [BLOCK_SIZE]byte = ---
|
||||||
|
tmp[0] = 0
|
||||||
|
copy(tmp[1:], iv[:])
|
||||||
|
|
||||||
|
q_0, q_1 := aes.load_interleaved(tmp[:])
|
||||||
|
for i in 0 ..< 4 {
|
||||||
|
dst[i], dst[i+4] = q_0, q_1
|
||||||
|
}
|
||||||
|
aes.orthogonalize(dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
bc_x4 :: proc "contextless" (
|
||||||
|
ctx: ^Context,
|
||||||
|
dst: []byte,
|
||||||
|
tweaks: ^[4][TWEAK_SIZE]byte,
|
||||||
|
q_stk: ^[8]u64,
|
||||||
|
q_b: ^[8]u64, // Orthogonalized
|
||||||
|
n: int,
|
||||||
|
) {
|
||||||
|
tk1s: [4]simd.u8x16
|
||||||
|
for j in 0 ..< n {
|
||||||
|
tk1s[j] = intrinsics.unaligned_load((^simd.u8x16)(&tweaks[j]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deoxys-BC-384
|
||||||
|
for i in 0 ..= BC_ROUNDS {
|
||||||
|
// Derive the round's subtweakkey
|
||||||
|
sk := intrinsics.unaligned_load((^simd.u8x16)(&ctx._subkeys[i]))
|
||||||
|
for j in 0 ..< n {
|
||||||
|
if i != 0 {
|
||||||
|
tk1s[j] = h(tk1s[j])
|
||||||
|
}
|
||||||
|
intrinsics.unaligned_store(
|
||||||
|
(^simd.u8x16)(raw_data(dst)),
|
||||||
|
simd.bit_xor(sk, tk1s[j]),
|
||||||
|
)
|
||||||
|
q_stk[j], q_stk[j+4] = aes.load_interleaved(dst[:])
|
||||||
|
}
|
||||||
|
aes.orthogonalize(q_stk)
|
||||||
|
|
||||||
|
if i != 0 {
|
||||||
|
aes.sub_bytes(q_b)
|
||||||
|
aes.shift_rows(q_b)
|
||||||
|
aes.mix_columns(q_b)
|
||||||
|
}
|
||||||
|
aes.add_round_key(q_b, q_stk[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
aes.orthogonalize(q_b)
|
||||||
|
for i in 0 ..< n {
|
||||||
|
aes.store_interleaved(dst[i*BLOCK_SIZE:], q_b[i], q_b[i+4])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file", require_results)
|
||||||
|
bc_absorb :: proc "contextless" (
|
||||||
|
st: ^State_SW,
|
||||||
|
dst: []byte,
|
||||||
|
src: []byte,
|
||||||
|
tweak_prefix: byte,
|
||||||
|
stk_block_nr: int,
|
||||||
|
) -> int {
|
||||||
|
tweaks: [4][TWEAK_SIZE]byte = ---
|
||||||
|
tmp: [BLOCK_SIZE*4]byte = ---
|
||||||
|
|
||||||
|
src, stk_block_nr := src, stk_block_nr
|
||||||
|
dst_ := intrinsics.unaligned_load((^simd.u8x16)(raw_data(dst)))
|
||||||
|
|
||||||
|
nr_blocks := len(src) / BLOCK_SIZE
|
||||||
|
for nr_blocks > 0 {
|
||||||
|
// Derive the tweak(s), orthogonalize the plaintext
|
||||||
|
n := min(nr_blocks, 4)
|
||||||
|
for i in 0 ..< n {
|
||||||
|
auth_tweak(&tweaks[i], tweak_prefix, stk_block_nr + i)
|
||||||
|
st.q_b[i], st.q_b[i + 4] = aes.load_interleaved(src)
|
||||||
|
src = src[BLOCK_SIZE:]
|
||||||
|
}
|
||||||
|
aes.orthogonalize(&st.q_b)
|
||||||
|
|
||||||
|
// Deoxys-BC-384
|
||||||
|
bc_x4(st.ctx, tmp[:], &tweaks, &st.q_stk, &st.q_b, n)
|
||||||
|
|
||||||
|
// XOR in the existing Auth/tag
|
||||||
|
for i in 0 ..< n {
|
||||||
|
dst_ = simd.bit_xor(
|
||||||
|
dst_,
|
||||||
|
intrinsics.unaligned_load((^simd.u8x16)(raw_data(tmp[i*BLOCK_SIZE:]))),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
stk_block_nr += n
|
||||||
|
nr_blocks -= n
|
||||||
|
}
|
||||||
|
|
||||||
|
intrinsics.unaligned_store((^simd.u8x16)(raw_data(dst)), dst_)
|
||||||
|
|
||||||
|
mem.zero_explicit(&tweaks, size_of(tweaks))
|
||||||
|
mem.zero_explicit(&tmp, size_of(tmp))
|
||||||
|
|
||||||
|
return stk_block_nr
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
bc_final :: proc "contextless" (
|
||||||
|
st: ^State_SW,
|
||||||
|
dst: []byte,
|
||||||
|
iv: []byte,
|
||||||
|
) {
|
||||||
|
tweaks: [4][TWEAK_SIZE]byte = ---
|
||||||
|
|
||||||
|
tweaks[0][0] = PREFIX_TAG << PREFIX_SHIFT
|
||||||
|
copy(tweaks[0][1:], iv)
|
||||||
|
|
||||||
|
st.q_b[0], st.q_b[4] = aes.load_interleaved(dst)
|
||||||
|
aes.orthogonalize(&st.q_b)
|
||||||
|
|
||||||
|
bc_x4(st.ctx, dst, &tweaks, &st.q_stk, &st.q_b, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file", require_results)
|
||||||
|
bc_encrypt :: proc "contextless" (
|
||||||
|
st: ^State_SW,
|
||||||
|
dst: []byte,
|
||||||
|
src: []byte,
|
||||||
|
q_n: ^[8]u64, // Orthogonalized
|
||||||
|
tweak_tag: ^[TAG_SIZE]byte,
|
||||||
|
stk_block_nr: int,
|
||||||
|
) -> int {
|
||||||
|
tweaks: [4][TWEAK_SIZE]byte = ---
|
||||||
|
tmp: [BLOCK_SIZE*4]byte = ---
|
||||||
|
|
||||||
|
dst, src, stk_block_nr := dst, src, stk_block_nr
|
||||||
|
|
||||||
|
nr_blocks := len(src) / BLOCK_SIZE
|
||||||
|
for nr_blocks > 0 {
|
||||||
|
// Derive the tweak(s)
|
||||||
|
n := min(nr_blocks, 4)
|
||||||
|
for i in 0 ..< n {
|
||||||
|
enc_tweak(&tweaks[i], tweak_tag, stk_block_nr + i)
|
||||||
|
}
|
||||||
|
st.q_b = q_n^ // The plaintext is always `0^8 || N`
|
||||||
|
|
||||||
|
// Deoxys-BC-384
|
||||||
|
bc_x4(st.ctx, tmp[:], &tweaks, &st.q_stk, &st.q_b, n)
|
||||||
|
|
||||||
|
// XOR the ciphertext
|
||||||
|
for i in 0 ..< n {
|
||||||
|
intrinsics.unaligned_store(
|
||||||
|
(^simd.u8x16)(raw_data(dst[i*BLOCK_SIZE:])),
|
||||||
|
simd.bit_xor(
|
||||||
|
intrinsics.unaligned_load((^simd.u8x16)(raw_data(src[i*BLOCK_SIZE:]))),
|
||||||
|
intrinsics.unaligned_load((^simd.u8x16)(raw_data(tmp[i*BLOCK_SIZE:]))),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
dst, src = dst[n*BLOCK_SIZE:], src[n*BLOCK_SIZE:]
|
||||||
|
stk_block_nr += n
|
||||||
|
nr_blocks -= n
|
||||||
|
}
|
||||||
|
|
||||||
|
mem.zero_explicit(&tweaks, size_of(tweaks))
|
||||||
|
mem.zero_explicit(&tmp, size_of(tmp))
|
||||||
|
|
||||||
|
return stk_block_nr
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
e_ref :: proc "contextless" (ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) #no_bounds_check {
|
||||||
|
st: State_SW = ---
|
||||||
|
st.ctx = ctx
|
||||||
|
|
||||||
|
// Algorithm 3
|
||||||
|
//
|
||||||
|
// Associated data
|
||||||
|
// A_1 || ... || A_la || A_∗ <- A where each |A_i| = n and |A_∗| < n
|
||||||
|
// Auth <- 0^n
|
||||||
|
// for i = 0 to la − 1 do
|
||||||
|
// Auth <- Auth ^ EK(0010 || i, A_i+1)
|
||||||
|
// end
|
||||||
|
// if A_∗ != nil then
|
||||||
|
// Auth <- Auth ^ EK(0110 || la, pad10∗(A_∗))
|
||||||
|
// end
|
||||||
|
auth: [TAG_SIZE]byte
|
||||||
|
aad := aad
|
||||||
|
n := bc_absorb(&st, auth[:], aad, PREFIX_AD_BLOCK, 0)
|
||||||
|
aad = aad[n*BLOCK_SIZE:]
|
||||||
|
if l := len(aad); l > 0 {
|
||||||
|
a_star: [BLOCK_SIZE]byte
|
||||||
|
|
||||||
|
copy(a_star[:], aad)
|
||||||
|
a_star[l] = 0x80
|
||||||
|
|
||||||
|
_ = bc_absorb(&st, auth[:], a_star[:], PREFIX_AD_FINAL, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message authentication and tag generation
|
||||||
|
// M_1 || ... || M_l || M_∗ <- M where each |M_j| = n and |M_∗| < n
|
||||||
|
// tag <- Auth
|
||||||
|
// for j = 0 to l − 1 do
|
||||||
|
// tag <- tag ^ EK(0000 || j, M_j+1)
|
||||||
|
// end
|
||||||
|
// if M_∗ != nil then
|
||||||
|
// tag <- tag ^ EK(0100 || l, pad10∗(M_∗))
|
||||||
|
// end
|
||||||
|
// tag <- EK(0001 || 0^4 || N, tag)
|
||||||
|
m := plaintext
|
||||||
|
n = bc_absorb(&st, auth[:], m, PREFIX_MSG_BLOCK, 0)
|
||||||
|
m = m[n*BLOCK_SIZE:]
|
||||||
|
if l := len(m); l > 0 {
|
||||||
|
m_star: [BLOCK_SIZE]byte
|
||||||
|
|
||||||
|
copy(m_star[:], m)
|
||||||
|
m_star[l] = 0x80
|
||||||
|
|
||||||
|
_ = bc_absorb(&st, auth[:], m_star[:], PREFIX_MSG_FINAL, n)
|
||||||
|
}
|
||||||
|
bc_final(&st, auth[:], iv)
|
||||||
|
|
||||||
|
// Message encryption
|
||||||
|
// for j = 0 to l − 1 do
|
||||||
|
// C_j <- M_j ^ EK(1 || tag ^ j, 0^8 || N)
|
||||||
|
// end
|
||||||
|
// if M_∗ != nil then
|
||||||
|
// C_∗ <- M_* ^ EK(1 || tag ^ l, 0^8 || N)
|
||||||
|
// end
|
||||||
|
//
|
||||||
|
// return (C_1 || ... || C_l || C_∗, tag)
|
||||||
|
q_iv: [8]u64 = ---
|
||||||
|
enc_plaintext(&q_iv, iv)
|
||||||
|
|
||||||
|
m = plaintext
|
||||||
|
n = bc_encrypt(&st, dst, m, &q_iv, &auth, 0)
|
||||||
|
m = m[n*BLOCK_SIZE:]
|
||||||
|
if l := len(m); l > 0 {
|
||||||
|
m_star: [BLOCK_SIZE]byte
|
||||||
|
|
||||||
|
copy(m_star[:], m)
|
||||||
|
_ = bc_encrypt(&st, m_star[:], m_star[:], &q_iv, &auth, n)
|
||||||
|
|
||||||
|
copy(dst[n*BLOCK_SIZE:], m_star[:])
|
||||||
|
|
||||||
|
mem.zero_explicit(&m_star, size_of(m_star))
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(tag, auth[:])
|
||||||
|
|
||||||
|
mem.zero_explicit(&st.q_stk, size_of(st.q_stk))
|
||||||
|
mem.zero_explicit(&st.q_b, size_of(st.q_b))
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private, require_results)
|
||||||
|
d_ref :: proc "contextless" (ctx: ^Context, dst, iv, aad, ciphertext, tag: []byte) -> bool {
|
||||||
|
st: State_SW = ---
|
||||||
|
st.ctx = ctx
|
||||||
|
|
||||||
|
// Algorithm 4
|
||||||
|
//
|
||||||
|
// Message decryption
|
||||||
|
// C_1 || ... || C_l || C_∗ <- C where each |C_j| = n and |C_∗| < n
|
||||||
|
// for j = 0 to l − 1 do
|
||||||
|
// M_j <- C_j ^ EK(1 || tag ^ j, 0^8 || N)
|
||||||
|
// end
|
||||||
|
// if C_∗ != nil then
|
||||||
|
// M_∗ <- C_∗ ^ EK(1 || tag ^ l, 0^8 || N)
|
||||||
|
// end
|
||||||
|
q_iv: [8]u64 = ---
|
||||||
|
enc_plaintext(&q_iv, iv)
|
||||||
|
|
||||||
|
auth: [TAG_SIZE]byte
|
||||||
|
copy(auth[:], tag)
|
||||||
|
|
||||||
|
m := ciphertext
|
||||||
|
n := bc_encrypt(&st, dst, m, &q_iv, &auth, 0)
|
||||||
|
m = m[n*BLOCK_SIZE:]
|
||||||
|
if l := len(m); l > 0 {
|
||||||
|
m_star: [BLOCK_SIZE]byte
|
||||||
|
|
||||||
|
copy(m_star[:], m)
|
||||||
|
_ = bc_encrypt(&st, m_star[:], m_star[:], &q_iv, &auth, n)
|
||||||
|
|
||||||
|
copy(dst[n*BLOCK_SIZE:], m_star[:])
|
||||||
|
|
||||||
|
mem.zero_explicit(&m_star, size_of(m_star))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Associated data
|
||||||
|
// A_1 || ... || Al_a || A_∗ <- A where each |Ai_| = n and |A_∗| < n
|
||||||
|
// Auth <- 0
|
||||||
|
// for i = 0 to la − 1 do
|
||||||
|
// Auth <- Auth ^ EK(0010 || i, A_i+1)
|
||||||
|
// end
|
||||||
|
// if A∗ != nil then
|
||||||
|
// Auth <- Auth ^ EK(0110| | l_a, pad10∗(A_∗))
|
||||||
|
// end
|
||||||
|
auth = 0
|
||||||
|
aad := aad
|
||||||
|
n = bc_absorb(&st, auth[:], aad, PREFIX_AD_BLOCK, 0)
|
||||||
|
aad = aad[n*BLOCK_SIZE:]
|
||||||
|
if l := len(aad); l > 0 {
|
||||||
|
a_star: [BLOCK_SIZE]byte
|
||||||
|
|
||||||
|
copy(a_star[:], aad)
|
||||||
|
a_star[l] = 0x80
|
||||||
|
|
||||||
|
_ = bc_absorb(&st, auth[:], a_star[:], PREFIX_AD_FINAL, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message authentication and tag generation
|
||||||
|
// M_1 || ... || M_l || M_∗ <- M where each |M_j| = n and |M_∗| < n
|
||||||
|
// tag0 <- Auth
|
||||||
|
// for j = 0 to l − 1 do
|
||||||
|
// tag0 <- tag0 ^ EK(0000 || j, M_j+1)
|
||||||
|
// end
|
||||||
|
// if M_∗ != nil then
|
||||||
|
// tag0 <- tag0 ^ EK(0100 || l, pad10∗(M_∗))
|
||||||
|
// end
|
||||||
|
// tag0 <- EK(0001 || 0^4 || N, tag0)
|
||||||
|
m = dst[:len(ciphertext)]
|
||||||
|
n = bc_absorb(&st, auth[:], m, PREFIX_MSG_BLOCK, 0)
|
||||||
|
m = m[n*BLOCK_SIZE:]
|
||||||
|
if l := len(m); l > 0 {
|
||||||
|
m_star: [BLOCK_SIZE]byte
|
||||||
|
|
||||||
|
copy(m_star[:], m)
|
||||||
|
m_star[l] = 0x80
|
||||||
|
|
||||||
|
_ = bc_absorb(&st, auth[:], m_star[:], PREFIX_MSG_FINAL, n)
|
||||||
|
|
||||||
|
mem.zero_explicit(&m_star, size_of(m_star))
|
||||||
|
}
|
||||||
|
bc_final(&st, auth[:], iv)
|
||||||
|
|
||||||
|
// Tag verification
|
||||||
|
// if tag0 = tag then return (M_1 || ... || M_l || M_∗)
|
||||||
|
// else return false
|
||||||
|
ok := crypto.compare_constant_time(auth[:], tag) == 1
|
||||||
|
|
||||||
|
mem.zero_explicit(&auth, size_of(auth))
|
||||||
|
mem.zero_explicit(&st.q_stk, size_of(st.q_stk))
|
||||||
|
mem.zero_explicit(&st.q_b, size_of(st.q_b))
|
||||||
|
|
||||||
|
return ok
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#+build !amd64
|
||||||
|
package deoxysii
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
ERR_HW_NOT_SUPPORTED :: "crypto/deoxysii: hardware implementation unsupported"
|
||||||
|
|
||||||
|
// is_hardware_accelerated returns true iff hardware accelerated Deoxys-II
|
||||||
|
// is supported.
|
||||||
|
is_hardware_accelerated :: proc "contextless" () -> bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
e_hw :: proc "contextless" (ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) #no_bounds_check {
|
||||||
|
panic_contextless(ERR_HW_NOT_SUPPORTED)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private, require_results)
|
||||||
|
d_hw :: proc "contextless" (ctx: ^Context, dst, iv, aad, ciphertext, tag: []byte) -> bool {
|
||||||
|
panic_contextless(ERR_HW_NOT_SUPPORTED)
|
||||||
|
}
|
||||||
@@ -0,0 +1,434 @@
|
|||||||
|
#+build amd64
|
||||||
|
package deoxysii
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
import "core:crypto"
|
||||||
|
import "core:crypto/aes"
|
||||||
|
import "core:mem"
|
||||||
|
import "core:simd"
|
||||||
|
import "core:simd/x86"
|
||||||
|
|
||||||
|
// This processes a maximum of 4 blocks at a time, as that is suitable
|
||||||
|
// for most current hardware that doesn't say "Xeon".
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
_BIT_ENC :: x86.__m128i{0x80, 0}
|
||||||
|
@(private = "file")
|
||||||
|
_PREFIX_AD_BLOCK :: x86.__m128i{PREFIX_AD_BLOCK << PREFIX_SHIFT, 0}
|
||||||
|
@(private = "file")
|
||||||
|
_PREFIX_AD_FINAL :: x86.__m128i{PREFIX_AD_FINAL << PREFIX_SHIFT, 0}
|
||||||
|
@(private = "file")
|
||||||
|
_PREFIX_MSG_BLOCK :: x86.__m128i{PREFIX_MSG_BLOCK << PREFIX_SHIFT, 0}
|
||||||
|
@(private = "file")
|
||||||
|
_PREFIX_MSG_FINAL :: x86.__m128i{PREFIX_MSG_FINAL << PREFIX_SHIFT, 0}
|
||||||
|
|
||||||
|
// is_hardware_accelerated returns true iff hardware accelerated Deoxys-II
|
||||||
|
// is supported.
|
||||||
|
is_hardware_accelerated :: proc "contextless" () -> bool {
|
||||||
|
return aes.is_hardware_accelerated()
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file", enable_target_feature = "sse4.1", require_results)
|
||||||
|
auth_tweak :: #force_inline proc "contextless" (
|
||||||
|
prefix: x86.__m128i,
|
||||||
|
block_nr: int,
|
||||||
|
) -> x86.__m128i {
|
||||||
|
return x86._mm_insert_epi64(prefix, i64(intrinsics.byte_swap(u64(block_nr))), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file", enable_target_feature = "sse2", require_results)
|
||||||
|
enc_tweak :: #force_inline proc "contextless" (
|
||||||
|
tag: x86.__m128i,
|
||||||
|
block_nr: int,
|
||||||
|
) -> x86.__m128i {
|
||||||
|
return x86._mm_xor_si128(
|
||||||
|
x86._mm_or_si128(tag, _BIT_ENC),
|
||||||
|
x86.__m128i{0, i64(intrinsics.byte_swap(u64(block_nr)))},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file", enable_target_feature = "ssse3", require_results)
|
||||||
|
h_ :: #force_inline proc "contextless" (tk1: x86.__m128i) -> x86.__m128i {
|
||||||
|
return transmute(x86.__m128i)h(transmute(simd.u8x16)tk1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file", enable_target_feature = "sse2,ssse3,aes", require_results)
|
||||||
|
bc_x4 :: #force_inline proc "contextless" (
|
||||||
|
ctx: ^Context,
|
||||||
|
s_0, s_1, s_2, s_3: x86.__m128i,
|
||||||
|
tweak_0, tweak_1, tweak_2, tweak_3: x86.__m128i,
|
||||||
|
) -> (x86.__m128i, x86.__m128i, x86.__m128i, x86.__m128i) #no_bounds_check {
|
||||||
|
s_0, s_1, s_2, s_3 := s_0, s_1, s_2, s_3
|
||||||
|
tk1_0, tk1_1, tk1_2, tk1_3 := tweak_0, tweak_1, tweak_2, tweak_3
|
||||||
|
|
||||||
|
sk := intrinsics.unaligned_load((^x86.__m128i)(&ctx._subkeys[0]))
|
||||||
|
stk_0 := x86._mm_xor_si128(tk1_0, sk)
|
||||||
|
stk_1 := x86._mm_xor_si128(tk1_1, sk)
|
||||||
|
stk_2 := x86._mm_xor_si128(tk1_2, sk)
|
||||||
|
stk_3 := x86._mm_xor_si128(tk1_3, sk)
|
||||||
|
|
||||||
|
s_0 = x86._mm_xor_si128(s_0, stk_0)
|
||||||
|
s_1 = x86._mm_xor_si128(s_1, stk_1)
|
||||||
|
s_2 = x86._mm_xor_si128(s_2, stk_2)
|
||||||
|
s_3 = x86._mm_xor_si128(s_3, stk_3)
|
||||||
|
|
||||||
|
for i in 1 ..= BC_ROUNDS {
|
||||||
|
sk = intrinsics.unaligned_load((^x86.__m128i)(&ctx._subkeys[i]))
|
||||||
|
|
||||||
|
tk1_0 = h_(tk1_0)
|
||||||
|
tk1_1 = h_(tk1_1)
|
||||||
|
tk1_2 = h_(tk1_2)
|
||||||
|
tk1_3 = h_(tk1_3)
|
||||||
|
|
||||||
|
stk_0 = x86._mm_xor_si128(tk1_0, sk)
|
||||||
|
stk_1 = x86._mm_xor_si128(tk1_1, sk)
|
||||||
|
stk_2 = x86._mm_xor_si128(tk1_2, sk)
|
||||||
|
stk_3 = x86._mm_xor_si128(tk1_3, sk)
|
||||||
|
|
||||||
|
s_0 = x86._mm_aesenc_si128(s_0, stk_0)
|
||||||
|
s_1 = x86._mm_aesenc_si128(s_1, stk_1)
|
||||||
|
s_2 = x86._mm_aesenc_si128(s_2, stk_2)
|
||||||
|
s_3 = x86._mm_aesenc_si128(s_3, stk_3)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s_0, s_1, s_2, s_3
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file", enable_target_feature = "sse2,ssse3,aes", require_results)
|
||||||
|
bc_x1 :: #force_inline proc "contextless" (
|
||||||
|
ctx: ^Context,
|
||||||
|
s: x86.__m128i,
|
||||||
|
tweak: x86.__m128i,
|
||||||
|
) -> x86.__m128i #no_bounds_check {
|
||||||
|
s, tk1 := s, tweak
|
||||||
|
|
||||||
|
sk := intrinsics.unaligned_load((^x86.__m128i)(&ctx._subkeys[0]))
|
||||||
|
stk := x86._mm_xor_si128(tk1, sk)
|
||||||
|
|
||||||
|
s = x86._mm_xor_si128(s, stk)
|
||||||
|
|
||||||
|
for i in 1 ..= BC_ROUNDS {
|
||||||
|
sk = intrinsics.unaligned_load((^x86.__m128i)(&ctx._subkeys[i]))
|
||||||
|
|
||||||
|
tk1 = h_(tk1)
|
||||||
|
|
||||||
|
stk = x86._mm_xor_si128(tk1, sk)
|
||||||
|
|
||||||
|
s = x86._mm_aesenc_si128(s, stk)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file", enable_target_feature = "sse2,ssse3,sse4.1,aes", require_results)
|
||||||
|
bc_absorb :: proc "contextless" (
|
||||||
|
ctx: ^Context,
|
||||||
|
tag: x86.__m128i,
|
||||||
|
src: []byte,
|
||||||
|
tweak_prefix: x86.__m128i,
|
||||||
|
stk_block_nr: int,
|
||||||
|
) -> (x86.__m128i, int) #no_bounds_check {
|
||||||
|
src, stk_block_nr, tag := src, stk_block_nr, tag
|
||||||
|
|
||||||
|
nr_blocks := len(src) / BLOCK_SIZE
|
||||||
|
for nr_blocks >= 4 {
|
||||||
|
d_0, d_1, d_2, d_3 := bc_x4(
|
||||||
|
ctx,
|
||||||
|
intrinsics.unaligned_load((^x86.__m128i)(raw_data(src))),
|
||||||
|
intrinsics.unaligned_load((^x86.__m128i)(raw_data(src[BLOCK_SIZE:]))),
|
||||||
|
intrinsics.unaligned_load((^x86.__m128i)(raw_data(src[2*BLOCK_SIZE:]))),
|
||||||
|
intrinsics.unaligned_load((^x86.__m128i)(raw_data(src[3*BLOCK_SIZE:]))),
|
||||||
|
auth_tweak(tweak_prefix, stk_block_nr),
|
||||||
|
auth_tweak(tweak_prefix, stk_block_nr + 1),
|
||||||
|
auth_tweak(tweak_prefix, stk_block_nr + 2),
|
||||||
|
auth_tweak(tweak_prefix, stk_block_nr + 3),
|
||||||
|
)
|
||||||
|
|
||||||
|
tag = x86._mm_xor_si128(tag, d_0)
|
||||||
|
tag = x86._mm_xor_si128(tag, d_1)
|
||||||
|
tag = x86._mm_xor_si128(tag, d_2)
|
||||||
|
tag = x86._mm_xor_si128(tag, d_3)
|
||||||
|
|
||||||
|
src = src[4*BLOCK_SIZE:]
|
||||||
|
stk_block_nr += 4
|
||||||
|
nr_blocks -= 4
|
||||||
|
}
|
||||||
|
|
||||||
|
for nr_blocks > 0 {
|
||||||
|
d := bc_x1(
|
||||||
|
ctx,
|
||||||
|
intrinsics.unaligned_load((^x86.__m128i)(raw_data(src))),
|
||||||
|
auth_tweak(tweak_prefix, stk_block_nr),
|
||||||
|
)
|
||||||
|
|
||||||
|
tag = x86._mm_xor_si128(tag, d)
|
||||||
|
|
||||||
|
src = src[BLOCK_SIZE:]
|
||||||
|
stk_block_nr += 1
|
||||||
|
nr_blocks -= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return tag, stk_block_nr
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file", enable_target_feature = "sse2,ssse3,aes", require_results)
|
||||||
|
bc_final :: proc "contextless" (
|
||||||
|
ctx: ^Context,
|
||||||
|
tag: x86.__m128i,
|
||||||
|
iv: []byte,
|
||||||
|
) -> x86.__m128i {
|
||||||
|
tmp: [BLOCK_SIZE]byte
|
||||||
|
|
||||||
|
tmp[0] = PREFIX_TAG << PREFIX_SHIFT
|
||||||
|
copy(tmp[1:], iv)
|
||||||
|
|
||||||
|
tweak := intrinsics.unaligned_load((^x86.__m128i)(&tmp))
|
||||||
|
|
||||||
|
return bc_x1(ctx, tag, tweak)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file", enable_target_feature = "sse2,ssse3,aes", require_results)
|
||||||
|
bc_encrypt :: proc "contextless" (
|
||||||
|
ctx: ^Context,
|
||||||
|
dst: []byte,
|
||||||
|
src: []byte,
|
||||||
|
iv: x86.__m128i,
|
||||||
|
tweak_tag: x86.__m128i,
|
||||||
|
stk_block_nr: int,
|
||||||
|
) -> int {
|
||||||
|
dst, src, stk_block_nr := dst, src, stk_block_nr
|
||||||
|
|
||||||
|
nr_blocks := len(src) / BLOCK_SIZE
|
||||||
|
for nr_blocks >= 4 {
|
||||||
|
d_0, d_1, d_2, d_3 := bc_x4(
|
||||||
|
ctx,
|
||||||
|
iv, iv, iv, iv,
|
||||||
|
enc_tweak(tweak_tag, stk_block_nr),
|
||||||
|
enc_tweak(tweak_tag, stk_block_nr + 1),
|
||||||
|
enc_tweak(tweak_tag, stk_block_nr + 2),
|
||||||
|
enc_tweak(tweak_tag, stk_block_nr + 3),
|
||||||
|
)
|
||||||
|
|
||||||
|
intrinsics.unaligned_store(
|
||||||
|
(^x86.__m128i)(raw_data(dst)),
|
||||||
|
x86._mm_xor_si128(
|
||||||
|
d_0,
|
||||||
|
intrinsics.unaligned_load((^x86.__m128i)(raw_data(src))),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
intrinsics.unaligned_store(
|
||||||
|
(^x86.__m128i)(raw_data(dst[BLOCK_SIZE:])),
|
||||||
|
x86._mm_xor_si128(
|
||||||
|
d_1,
|
||||||
|
intrinsics.unaligned_load((^x86.__m128i)(raw_data(src[BLOCK_SIZE:]))),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
intrinsics.unaligned_store(
|
||||||
|
(^x86.__m128i)(raw_data(dst[2*BLOCK_SIZE:])),
|
||||||
|
x86._mm_xor_si128(
|
||||||
|
d_2,
|
||||||
|
intrinsics.unaligned_load((^x86.__m128i)(raw_data(src[2*BLOCK_SIZE:]))),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
intrinsics.unaligned_store(
|
||||||
|
(^x86.__m128i)(raw_data(dst[3*BLOCK_SIZE:])),
|
||||||
|
x86._mm_xor_si128(
|
||||||
|
d_3,
|
||||||
|
intrinsics.unaligned_load((^x86.__m128i)(raw_data(src[3*BLOCK_SIZE:]))),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
src, dst = src[4*BLOCK_SIZE:], dst[4*BLOCK_SIZE:]
|
||||||
|
stk_block_nr += 4
|
||||||
|
nr_blocks -= 4
|
||||||
|
}
|
||||||
|
|
||||||
|
for nr_blocks > 0 {
|
||||||
|
d := bc_x1(
|
||||||
|
ctx,
|
||||||
|
iv,
|
||||||
|
enc_tweak(tweak_tag, stk_block_nr),
|
||||||
|
)
|
||||||
|
|
||||||
|
intrinsics.unaligned_store(
|
||||||
|
(^x86.__m128i)(raw_data(dst)),
|
||||||
|
x86._mm_xor_si128(
|
||||||
|
d,
|
||||||
|
intrinsics.unaligned_load((^x86.__m128i)(raw_data(src))),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
src, dst = src[BLOCK_SIZE:], dst[BLOCK_SIZE:]
|
||||||
|
stk_block_nr += 1
|
||||||
|
nr_blocks -= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return stk_block_nr
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
e_hw :: proc "contextless" (ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) #no_bounds_check {
|
||||||
|
tmp: [BLOCK_SIZE]byte
|
||||||
|
copy(tmp[1:], iv)
|
||||||
|
iv_ := intrinsics.unaligned_load((^x86.__m128i)(raw_data(&tmp)))
|
||||||
|
|
||||||
|
// Algorithm 3
|
||||||
|
//
|
||||||
|
// Associated data
|
||||||
|
// A_1 || ... || A_la || A_∗ <- A where each |A_i| = n and |A_∗| < n
|
||||||
|
// Auth <- 0^n
|
||||||
|
// for i = 0 to la − 1 do
|
||||||
|
// Auth <- Auth ^ EK(0010 || i, A_i+1)
|
||||||
|
// end
|
||||||
|
// if A_∗ != nil then
|
||||||
|
// Auth <- Auth ^ EK(0110 || la, pad10∗(A_∗))
|
||||||
|
// end
|
||||||
|
auth: x86.__m128i
|
||||||
|
n: int
|
||||||
|
|
||||||
|
aad := aad
|
||||||
|
auth, n = bc_absorb(ctx, auth, aad, _PREFIX_AD_BLOCK, 0)
|
||||||
|
aad = aad[n*BLOCK_SIZE:]
|
||||||
|
if l := len(aad); l > 0 {
|
||||||
|
a_star: [BLOCK_SIZE]byte
|
||||||
|
|
||||||
|
copy(a_star[:], aad)
|
||||||
|
a_star[l] = 0x80
|
||||||
|
|
||||||
|
auth, _ = bc_absorb(ctx, auth, a_star[:], _PREFIX_AD_FINAL, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message authentication and tag generation
|
||||||
|
// M_1 || ... || M_l || M_∗ <- M where each |M_j| = n and |M_∗| < n
|
||||||
|
// tag <- Auth
|
||||||
|
// for j = 0 to l − 1 do
|
||||||
|
// tag <- tag ^ EK(0000 || j, M_j+1)
|
||||||
|
// end
|
||||||
|
// if M_∗ != nil then
|
||||||
|
// tag <- tag ^ EK(0100 || l, pad10∗(M_∗))
|
||||||
|
// end
|
||||||
|
// tag <- EK(0001 || 0^4 ||N, tag)
|
||||||
|
m := plaintext
|
||||||
|
auth, n = bc_absorb(ctx, auth, m, _PREFIX_MSG_BLOCK, 0)
|
||||||
|
m = m[n*BLOCK_SIZE:]
|
||||||
|
if l := len(m); l > 0 {
|
||||||
|
m_star: [BLOCK_SIZE]byte
|
||||||
|
|
||||||
|
copy(m_star[:], m)
|
||||||
|
m_star[l] = 0x80
|
||||||
|
|
||||||
|
auth, _ = bc_absorb(ctx, auth, m_star[:], _PREFIX_MSG_FINAL, n)
|
||||||
|
}
|
||||||
|
auth = bc_final(ctx, auth, iv)
|
||||||
|
|
||||||
|
// Message encryption
|
||||||
|
// for j = 0 to l − 1 do
|
||||||
|
// C_j <- M_j ^ EK(1 || tag ^ j, 0^8 || N)
|
||||||
|
// end
|
||||||
|
// if M_∗ != nil then
|
||||||
|
// C_∗ <- M_* ^ EK(1 || tag ^ l, 0^8 || N)
|
||||||
|
// end
|
||||||
|
//
|
||||||
|
// return (C_1 || ... || C_l || C_∗, tag)
|
||||||
|
m = plaintext
|
||||||
|
n = bc_encrypt(ctx, dst, m, iv_, auth, 0)
|
||||||
|
m = m[n*BLOCK_SIZE:]
|
||||||
|
if l := len(m); l > 0 {
|
||||||
|
m_star: [BLOCK_SIZE]byte
|
||||||
|
|
||||||
|
copy(m_star[:], m)
|
||||||
|
_ = bc_encrypt(ctx, m_star[:], m_star[:], iv_, auth, n)
|
||||||
|
|
||||||
|
copy(dst[n*BLOCK_SIZE:], m_star[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
intrinsics.unaligned_store((^x86.__m128i)(raw_data(tag)), auth)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private, require_results)
|
||||||
|
d_hw :: proc "contextless" (ctx: ^Context, dst, iv, aad, ciphertext, tag: []byte) -> bool {
|
||||||
|
tmp: [BLOCK_SIZE]byte
|
||||||
|
copy(tmp[1:], iv)
|
||||||
|
iv_ := intrinsics.unaligned_load((^x86.__m128i)(raw_data(&tmp)))
|
||||||
|
|
||||||
|
// Algorithm 4
|
||||||
|
//
|
||||||
|
// Message decryption
|
||||||
|
// C_1 || ... || C_l || C_∗ <- C where each |C_j| = n and |C_∗| < n
|
||||||
|
// for j = 0 to l − 1 do
|
||||||
|
// M_j <- C_j ^ EK(1 || tag ^ j, 0^8 || N)
|
||||||
|
// end
|
||||||
|
// if C_∗ != nil then
|
||||||
|
// M_∗ <- C_∗ ^ EK(1 || tag ^ l, 0^8 || N)
|
||||||
|
// end
|
||||||
|
auth := intrinsics.unaligned_load((^x86.__m128i)(raw_data(tag)))
|
||||||
|
|
||||||
|
m := ciphertext
|
||||||
|
n := bc_encrypt(ctx, dst, m, iv_, auth, 0)
|
||||||
|
m = m[n*BLOCK_SIZE:]
|
||||||
|
if l := len(m); l > 0 {
|
||||||
|
m_star: [BLOCK_SIZE]byte
|
||||||
|
|
||||||
|
copy(m_star[:], m)
|
||||||
|
_ = bc_encrypt(ctx, m_star[:], m_star[:], iv_, auth, n)
|
||||||
|
|
||||||
|
copy(dst[n*BLOCK_SIZE:], m_star[:])
|
||||||
|
|
||||||
|
mem.zero_explicit(&m_star, size_of(m_star))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Associated data
|
||||||
|
// A_1 || ... || Al_a || A_∗ <- A where each |Ai_| = n and |A_∗| < n
|
||||||
|
// Auth <- 0
|
||||||
|
// for i = 0 to la − 1 do
|
||||||
|
// Auth <- Auth ^ EK(0010 || i, A_i+1)
|
||||||
|
// end
|
||||||
|
// if A∗ != nil then
|
||||||
|
// Auth <- Auth ^ EK(0110| | l_a, pad10∗(A_∗))
|
||||||
|
// end
|
||||||
|
auth = x86.__m128i{0, 0}
|
||||||
|
aad := aad
|
||||||
|
auth, n = bc_absorb(ctx, auth, aad, _PREFIX_AD_BLOCK, 0)
|
||||||
|
aad = aad[BLOCK_SIZE*n:]
|
||||||
|
if l := len(aad); l > 0 {
|
||||||
|
a_star: [BLOCK_SIZE]byte
|
||||||
|
|
||||||
|
copy(a_star[:], aad)
|
||||||
|
a_star[l] = 0x80
|
||||||
|
|
||||||
|
auth, _ = bc_absorb(ctx, auth, a_star[:], _PREFIX_AD_FINAL, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message authentication and tag generation
|
||||||
|
// M_1 || ... || M_l || M_∗ <- M where each |M_j| = n and |M_∗| < n
|
||||||
|
// tag0 <- Auth
|
||||||
|
// for j = 0 to l − 1 do
|
||||||
|
// tag0 <- tag0 ^ EK(0000 || j, M_j+1)
|
||||||
|
// end
|
||||||
|
// if M_∗ != nil then
|
||||||
|
// tag0 <- tag0 ^ EK(0100 || l, pad10∗(M_∗))
|
||||||
|
// end
|
||||||
|
// tag0 <- EK(0001 || 0^4 || N, tag0)
|
||||||
|
m = dst[:len(ciphertext)]
|
||||||
|
auth, n = bc_absorb(ctx, auth, m, _PREFIX_MSG_BLOCK, 0)
|
||||||
|
m = m[n*BLOCK_SIZE:]
|
||||||
|
if l := len(m); l > 0 {
|
||||||
|
m_star: [BLOCK_SIZE]byte
|
||||||
|
|
||||||
|
copy(m_star[:], m)
|
||||||
|
m_star[l] = 0x80
|
||||||
|
|
||||||
|
auth, _ = bc_absorb(ctx, auth, m_star[:], _PREFIX_MSG_FINAL, n)
|
||||||
|
}
|
||||||
|
auth = bc_final(ctx, auth, iv)
|
||||||
|
|
||||||
|
// Tag verification
|
||||||
|
// if tag0 = tag then return (M_1 || ... || M_l || M_∗)
|
||||||
|
// else return false
|
||||||
|
intrinsics.unaligned_store((^x86.__m128i)(raw_data(&tmp)), auth)
|
||||||
|
ok := crypto.compare_constant_time(tmp[:], tag) == 1
|
||||||
|
|
||||||
|
mem.zero_explicit(&tmp, size_of(tmp))
|
||||||
|
|
||||||
|
return ok
|
||||||
|
}
|
||||||
@@ -81,12 +81,8 @@ private_key_set_bytes :: proc(priv_key: ^Private_Key, b: []byte) -> bool {
|
|||||||
|
|
||||||
// private_key_bytes sets dst to byte-encoding of priv_key.
|
// private_key_bytes sets dst to byte-encoding of priv_key.
|
||||||
private_key_bytes :: proc(priv_key: ^Private_Key, dst: []byte) {
|
private_key_bytes :: proc(priv_key: ^Private_Key, dst: []byte) {
|
||||||
if !priv_key._is_initialized {
|
ensure(priv_key._is_initialized, "crypto/ed25519: uninitialized private key")
|
||||||
panic("crypto/ed25519: uninitialized private key")
|
ensure(len(dst) == PRIVATE_KEY_SIZE, "crypto/ed25519: invalid destination size")
|
||||||
}
|
|
||||||
if len(dst) != PRIVATE_KEY_SIZE {
|
|
||||||
panic("crypto/ed25519: invalid destination size")
|
|
||||||
}
|
|
||||||
|
|
||||||
copy(dst, priv_key._b[:])
|
copy(dst, priv_key._b[:])
|
||||||
}
|
}
|
||||||
@@ -98,12 +94,8 @@ private_key_clear :: proc "contextless" (priv_key: ^Private_Key) {
|
|||||||
|
|
||||||
// sign writes the signature by priv_key over msg to sig.
|
// sign writes the signature by priv_key over msg to sig.
|
||||||
sign :: proc(priv_key: ^Private_Key, msg, sig: []byte) {
|
sign :: proc(priv_key: ^Private_Key, msg, sig: []byte) {
|
||||||
if !priv_key._is_initialized {
|
ensure(priv_key._is_initialized, "crypto/ed25519: uninitialized private key")
|
||||||
panic("crypto/ed25519: uninitialized private key")
|
ensure(len(sig) == SIGNATURE_SIZE, "crypto/ed25519: invalid destination size")
|
||||||
}
|
|
||||||
if len(sig) != SIGNATURE_SIZE {
|
|
||||||
panic("crypto/ed25519: invalid destination size")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. Compute the hash of the private key d, H(d) = (h_0, h_1, ..., h_2b-1)
|
// 1. Compute the hash of the private key d, H(d) = (h_0, h_1, ..., h_2b-1)
|
||||||
// using SHA-512 for Ed25519. H(d) may be precomputed.
|
// using SHA-512 for Ed25519. H(d) may be precomputed.
|
||||||
@@ -178,9 +170,7 @@ public_key_set_bytes :: proc "contextless" (pub_key: ^Public_Key, b: []byte) ->
|
|||||||
|
|
||||||
// public_key_set_priv sets pub_key to the public component of priv_key.
|
// public_key_set_priv sets pub_key to the public component of priv_key.
|
||||||
public_key_set_priv :: proc(pub_key: ^Public_Key, priv_key: ^Private_Key) {
|
public_key_set_priv :: proc(pub_key: ^Public_Key, priv_key: ^Private_Key) {
|
||||||
if !priv_key._is_initialized {
|
ensure(priv_key._is_initialized, "crypto/ed25519: uninitialized public key")
|
||||||
panic("crypto/ed25519: uninitialized public key")
|
|
||||||
}
|
|
||||||
|
|
||||||
src := &priv_key._pub_key
|
src := &priv_key._pub_key
|
||||||
copy(pub_key._b[:], src._b[:])
|
copy(pub_key._b[:], src._b[:])
|
||||||
@@ -191,21 +181,15 @@ public_key_set_priv :: proc(pub_key: ^Public_Key, priv_key: ^Private_Key) {
|
|||||||
|
|
||||||
// public_key_bytes sets dst to byte-encoding of pub_key.
|
// public_key_bytes sets dst to byte-encoding of pub_key.
|
||||||
public_key_bytes :: proc(pub_key: ^Public_Key, dst: []byte) {
|
public_key_bytes :: proc(pub_key: ^Public_Key, dst: []byte) {
|
||||||
if !pub_key._is_initialized {
|
ensure(pub_key._is_initialized, "crypto/ed25519: uninitialized public key")
|
||||||
panic("crypto/ed25519: uninitialized public key")
|
ensure(len(dst) == PUBLIC_KEY_SIZE, "crypto/ed25519: invalid destination size")
|
||||||
}
|
|
||||||
if len(dst) != PUBLIC_KEY_SIZE {
|
|
||||||
panic("crypto/ed25519: invalid destination size")
|
|
||||||
}
|
|
||||||
|
|
||||||
copy(dst, pub_key._b[:])
|
copy(dst, pub_key._b[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
// public_key_equal returns true iff pub_key is equal to other.
|
// public_key_equal returns true iff pub_key is equal to other.
|
||||||
public_key_equal :: proc(pub_key, other: ^Public_Key) -> bool {
|
public_key_equal :: proc(pub_key, other: ^Public_Key) -> bool {
|
||||||
if !pub_key._is_initialized || !other._is_initialized {
|
ensure(pub_key._is_initialized && other._is_initialized, "crypto/ed25519: uninitialized public key")
|
||||||
panic("crypto/ed25519: uninitialized public key")
|
|
||||||
}
|
|
||||||
|
|
||||||
return crypto.compare_constant_time(pub_key._b[:], other._b[:]) == 1
|
return crypto.compare_constant_time(pub_key._b[:], other._b[:]) == 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ init :: proc(ctx: ^Context, algorithm: hash.Algorithm, key: []byte) {
|
|||||||
|
|
||||||
// update adds more data to the Context.
|
// update adds more data to the Context.
|
||||||
update :: proc(ctx: ^Context, data: []byte) {
|
update :: proc(ctx: ^Context, data: []byte) {
|
||||||
assert(ctx._is_initialized)
|
ensure(ctx._is_initialized)
|
||||||
|
|
||||||
hash.update(&ctx._i_hash, data)
|
hash.update(&ctx._i_hash, data)
|
||||||
}
|
}
|
||||||
@@ -64,13 +64,10 @@ update :: proc(ctx: ^Context, data: []byte) {
|
|||||||
// final finalizes the Context, writes the tag to dst, and calls
|
// final finalizes the Context, writes the tag to dst, and calls
|
||||||
// reset on the Context.
|
// reset on the Context.
|
||||||
final :: proc(ctx: ^Context, dst: []byte) {
|
final :: proc(ctx: ^Context, dst: []byte) {
|
||||||
assert(ctx._is_initialized)
|
|
||||||
|
|
||||||
defer (reset(ctx))
|
defer (reset(ctx))
|
||||||
|
|
||||||
if len(dst) != ctx._tag_sz {
|
ensure(ctx._is_initialized)
|
||||||
panic("crypto/hmac: invalid destination tag size")
|
ensure(len(dst) == ctx._tag_sz, "crypto/hmac: invalid destination tag size")
|
||||||
}
|
|
||||||
|
|
||||||
hash.final(&ctx._i_hash, dst) // H((k ^ ipad) || text)
|
hash.final(&ctx._i_hash, dst) // H((k ^ ipad) || text)
|
||||||
|
|
||||||
@@ -105,14 +102,14 @@ reset :: proc(ctx: ^Context) {
|
|||||||
|
|
||||||
// algorithm returns the Algorithm used by a Context instance.
|
// algorithm returns the Algorithm used by a Context instance.
|
||||||
algorithm :: proc(ctx: ^Context) -> hash.Algorithm {
|
algorithm :: proc(ctx: ^Context) -> hash.Algorithm {
|
||||||
assert(ctx._is_initialized)
|
ensure(ctx._is_initialized)
|
||||||
|
|
||||||
return hash.algorithm(&ctx._i_hash)
|
return hash.algorithm(&ctx._i_hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// tag_size returns the tag size of a Context instance in bytes.
|
// tag_size returns the tag size of a Context instance in bytes.
|
||||||
tag_size :: proc(ctx: ^Context) -> int {
|
tag_size :: proc(ctx: ^Context) -> int {
|
||||||
assert(ctx._is_initialized)
|
ensure(ctx._is_initialized)
|
||||||
|
|
||||||
return ctx._tag_sz
|
return ctx._tag_sz
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ sum :: proc(sec_strength: int, dst, msg, key, domain_sep: []byte) {
|
|||||||
// tag is valid.
|
// tag is valid.
|
||||||
verify :: proc(sec_strength: int, tag, msg, key, domain_sep: []byte, allocator := context.temp_allocator) -> bool {
|
verify :: proc(sec_strength: int, tag, msg, key, domain_sep: []byte, allocator := context.temp_allocator) -> bool {
|
||||||
derived_tag := make([]byte, len(tag), allocator)
|
derived_tag := make([]byte, len(tag), allocator)
|
||||||
|
defer(delete(derived_tag))
|
||||||
|
|
||||||
sum(sec_strength, derived_tag, msg, key, domain_sep)
|
sum(sec_strength, derived_tag, msg, key, domain_sep)
|
||||||
|
|
||||||
@@ -59,8 +60,6 @@ init_256 :: proc(ctx: ^Context, key, domain_sep: []byte) {
|
|||||||
|
|
||||||
// update adds more data to the Context.
|
// update adds more data to the Context.
|
||||||
update :: proc(ctx: ^Context, data: []byte) {
|
update :: proc(ctx: ^Context, data: []byte) {
|
||||||
assert(ctx.is_initialized)
|
|
||||||
|
|
||||||
shake.write((^shake.Context)(ctx), data)
|
shake.write((^shake.Context)(ctx), data)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,12 +67,9 @@ update :: proc(ctx: ^Context, data: []byte) {
|
|||||||
// on the Context. This routine will panic if the dst length is less than
|
// on the Context. This routine will panic if the dst length is less than
|
||||||
// MIN_TAG_SIZE.
|
// MIN_TAG_SIZE.
|
||||||
final :: proc(ctx: ^Context, dst: []byte) {
|
final :: proc(ctx: ^Context, dst: []byte) {
|
||||||
assert(ctx.is_initialized)
|
|
||||||
defer reset(ctx)
|
defer reset(ctx)
|
||||||
|
|
||||||
if len(dst) < MIN_TAG_SIZE {
|
ensure(len(dst) >= MIN_TAG_SIZE, "crypto/kmac: invalid KMAC tag_size, too short")
|
||||||
panic("crypto/kmac: invalid KMAC tag_size, too short")
|
|
||||||
}
|
|
||||||
|
|
||||||
_sha3.final_cshake((^_sha3.Context)(ctx), dst)
|
_sha3.final_cshake((^_sha3.Context)(ctx), dst)
|
||||||
}
|
}
|
||||||
@@ -103,14 +99,12 @@ _init_kmac :: proc(ctx: ^Context, key, s: []byte, sec_strength: int) {
|
|||||||
reset(ctx)
|
reset(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(key) < sec_strength / 8 {
|
ensure(len(key) >= sec_strength / 8, "crypto/kmac: invalid KMAC key, too short")
|
||||||
panic("crypto/kmac: invalid KMAC key, too short")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx_ := (^_sha3.Context)(ctx)
|
ctx_ := (^_sha3.Context)(ctx)
|
||||||
_sha3.init_cshake(ctx_, N_KMAC, s, sec_strength)
|
_sha3.init_cshake(ctx_, N_KMAC, s, sec_strength)
|
||||||
_sha3.bytepad(ctx_, [][]byte{key}, _sha3.rate_cshake(sec_strength))
|
_sha3.bytepad(ctx_, [][]byte{key}, _sha3.rate_cshake(sec_strength))
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private, rodata)
|
||||||
N_KMAC := []byte{'K', 'M', 'A', 'C'}
|
N_KMAC := []byte{'K', 'M', 'A', 'C'}
|
||||||
|
|||||||
@@ -40,37 +40,37 @@ BLOCK_SIZE_512 :: _sha3.RATE_512
|
|||||||
Context :: distinct _sha3.Context
|
Context :: distinct _sha3.Context
|
||||||
|
|
||||||
// init_224 initializes a Context for Keccak-224.
|
// init_224 initializes a Context for Keccak-224.
|
||||||
init_224 :: proc(ctx: ^Context) {
|
init_224 :: proc "contextless" (ctx: ^Context) {
|
||||||
ctx.mdlen = DIGEST_SIZE_224
|
ctx.mdlen = DIGEST_SIZE_224
|
||||||
_init(ctx)
|
_init(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// init_256 initializes a Context for Keccak-256.
|
// init_256 initializes a Context for Keccak-256.
|
||||||
init_256 :: proc(ctx: ^Context) {
|
init_256 :: proc "contextless" (ctx: ^Context) {
|
||||||
ctx.mdlen = DIGEST_SIZE_256
|
ctx.mdlen = DIGEST_SIZE_256
|
||||||
_init(ctx)
|
_init(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// init_384 initializes a Context for Keccak-384.
|
// init_384 initializes a Context for Keccak-384.
|
||||||
init_384 :: proc(ctx: ^Context) {
|
init_384 :: proc "contextless" (ctx: ^Context) {
|
||||||
ctx.mdlen = DIGEST_SIZE_384
|
ctx.mdlen = DIGEST_SIZE_384
|
||||||
_init(ctx)
|
_init(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// init_512 initializes a Context for Keccak-512.
|
// init_512 initializes a Context for Keccak-512.
|
||||||
init_512 :: proc(ctx: ^Context) {
|
init_512 :: proc "contextless" (ctx: ^Context) {
|
||||||
ctx.mdlen = DIGEST_SIZE_512
|
ctx.mdlen = DIGEST_SIZE_512
|
||||||
_init(ctx)
|
_init(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
_init :: proc(ctx: ^Context) {
|
_init :: proc "contextless" (ctx: ^Context) {
|
||||||
ctx.dsbyte = _sha3.DS_KECCAK
|
ctx.dsbyte = _sha3.DS_KECCAK
|
||||||
_sha3.init((^_sha3.Context)(ctx))
|
_sha3.init((^_sha3.Context)(ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
// update adds more data to the Context.
|
// update adds more data to the Context.
|
||||||
update :: proc(ctx: ^Context, data: []byte) {
|
update :: proc "contextless" (ctx: ^Context, data: []byte) {
|
||||||
_sha3.update((^_sha3.Context)(ctx), data)
|
_sha3.update((^_sha3.Context)(ctx), data)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,17 +79,17 @@ update :: proc(ctx: ^Context, data: []byte) {
|
|||||||
//
|
//
|
||||||
// Iff finalize_clone is set, final will work on a copy of the Context,
|
// Iff finalize_clone is set, final will work on a copy of the Context,
|
||||||
// which is useful for for calculating rolling digests.
|
// which is useful for for calculating rolling digests.
|
||||||
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
final :: proc "contextless" (ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
||||||
_sha3.final((^_sha3.Context)(ctx), hash, finalize_clone)
|
_sha3.final((^_sha3.Context)(ctx), hash, finalize_clone)
|
||||||
}
|
}
|
||||||
|
|
||||||
// clone clones the Context other into ctx.
|
// clone clones the Context other into ctx.
|
||||||
clone :: proc(ctx, other: ^Context) {
|
clone :: proc "contextless" (ctx, other: ^Context) {
|
||||||
_sha3.clone((^_sha3.Context)(ctx), (^_sha3.Context)(other))
|
_sha3.clone((^_sha3.Context)(ctx), (^_sha3.Context)(other))
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset sanitizes the Context. The Context must be re-initialized to
|
// reset sanitizes the Context. The Context must be re-initialized to
|
||||||
// be used again.
|
// be used again.
|
||||||
reset :: proc(ctx: ^Context) {
|
reset :: proc "contextless" (ctx: ^Context) {
|
||||||
_sha3.reset((^_sha3.Context)(ctx))
|
_sha3.reset((^_sha3.Context)(ctx))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ init :: proc(ctx: ^Context) {
|
|||||||
|
|
||||||
// update adds more data to the Context.
|
// update adds more data to the Context.
|
||||||
update :: proc(ctx: ^Context, data: []byte) {
|
update :: proc(ctx: ^Context, data: []byte) {
|
||||||
assert(ctx.is_initialized)
|
ensure(ctx.is_initialized)
|
||||||
|
|
||||||
for i := 0; i < len(data); i += 1 {
|
for i := 0; i < len(data); i += 1 {
|
||||||
ctx.data[ctx.datalen] = data[i]
|
ctx.data[ctx.datalen] = data[i]
|
||||||
@@ -72,11 +72,8 @@ update :: proc(ctx: ^Context, data: []byte) {
|
|||||||
// Iff finalize_clone is set, final will work on a copy of the Context,
|
// Iff finalize_clone is set, final will work on a copy of the Context,
|
||||||
// which is useful for for calculating rolling digests.
|
// which is useful for for calculating rolling digests.
|
||||||
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
||||||
assert(ctx.is_initialized)
|
ensure(ctx.is_initialized)
|
||||||
|
ensure(len(hash) >= DIGEST_SIZE, "crypto/md5: invalid destination digest size")
|
||||||
if len(hash) < DIGEST_SIZE {
|
|
||||||
panic("crypto/md5: invalid destination digest size")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := ctx
|
ctx := ctx
|
||||||
if finalize_clone {
|
if finalize_clone {
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ init :: proc(ctx: ^Context) {
|
|||||||
|
|
||||||
// update adds more data to the Context.
|
// update adds more data to the Context.
|
||||||
update :: proc(ctx: ^Context, data: []byte) {
|
update :: proc(ctx: ^Context, data: []byte) {
|
||||||
assert(ctx.is_initialized)
|
ensure(ctx.is_initialized)
|
||||||
|
|
||||||
for i := 0; i < len(data); i += 1 {
|
for i := 0; i < len(data); i += 1 {
|
||||||
ctx.data[ctx.datalen] = data[i]
|
ctx.data[ctx.datalen] = data[i]
|
||||||
@@ -79,11 +79,8 @@ update :: proc(ctx: ^Context, data: []byte) {
|
|||||||
// Iff finalize_clone is set, final will work on a copy of the Context,
|
// Iff finalize_clone is set, final will work on a copy of the Context,
|
||||||
// which is useful for for calculating rolling digests.
|
// which is useful for for calculating rolling digests.
|
||||||
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
||||||
assert(ctx.is_initialized)
|
ensure(ctx.is_initialized)
|
||||||
|
ensure(len(hash) >= DIGEST_SIZE, "crypto/sha1: invalid destination digest size")
|
||||||
if len(hash) < DIGEST_SIZE {
|
|
||||||
panic("crypto/sha1: invalid destination digest size")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := ctx
|
ctx := ctx
|
||||||
if finalize_clone {
|
if finalize_clone {
|
||||||
|
|||||||
@@ -60,9 +60,7 @@ Context :: struct {
|
|||||||
// init initializes a Context with the specified key. The key SHOULD be
|
// init initializes a Context with the specified key. The key SHOULD be
|
||||||
// unique and MUST be unpredictable for each invocation.
|
// unique and MUST be unpredictable for each invocation.
|
||||||
init :: proc(ctx: ^Context, key: []byte) {
|
init :: proc(ctx: ^Context, key: []byte) {
|
||||||
if len(key) != KEY_SIZE {
|
ensure(len(key) == KEY_SIZE, "crypto/poly1305: invalid key size")
|
||||||
panic("crypto/poly1305: invalid key size")
|
|
||||||
}
|
|
||||||
|
|
||||||
// r = le_bytes_to_num(key[0..15])
|
// r = le_bytes_to_num(key[0..15])
|
||||||
// r = clamp(r) (r &= 0xffffffc0ffffffc0ffffffc0fffffff)
|
// r = clamp(r) (r &= 0xffffffc0ffffffc0ffffffc0fffffff)
|
||||||
@@ -85,7 +83,7 @@ init :: proc(ctx: ^Context, key: []byte) {
|
|||||||
|
|
||||||
// update adds more data to the Context.
|
// update adds more data to the Context.
|
||||||
update :: proc(ctx: ^Context, data: []byte) {
|
update :: proc(ctx: ^Context, data: []byte) {
|
||||||
assert(ctx._is_initialized)
|
ensure(ctx._is_initialized)
|
||||||
|
|
||||||
msg := data
|
msg := data
|
||||||
msg_len := len(data)
|
msg_len := len(data)
|
||||||
@@ -124,12 +122,10 @@ update :: proc(ctx: ^Context, data: []byte) {
|
|||||||
// final finalizes the Context, writes the tag to dst, and calls
|
// final finalizes the Context, writes the tag to dst, and calls
|
||||||
// reset on the Context.
|
// reset on the Context.
|
||||||
final :: proc(ctx: ^Context, dst: []byte) {
|
final :: proc(ctx: ^Context, dst: []byte) {
|
||||||
assert(ctx._is_initialized)
|
|
||||||
defer reset(ctx)
|
defer reset(ctx)
|
||||||
|
|
||||||
if len(dst) != TAG_SIZE {
|
ensure(ctx._is_initialized)
|
||||||
panic("poly1305: invalid destination tag size")
|
ensure(len(dst) == TAG_SIZE, "poly1305: invalid destination tag size")
|
||||||
}
|
|
||||||
|
|
||||||
// Process remaining block
|
// Process remaining block
|
||||||
if ctx._leftover > 0 {
|
if ctx._leftover > 0 {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#+build !netbsd
|
#+build !netbsd
|
||||||
#+build !darwin
|
#+build !darwin
|
||||||
#+build !js
|
#+build !js
|
||||||
|
#+build !wasi
|
||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
HAS_RAND_BYTES :: false
|
HAS_RAND_BYTES :: false
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:sys/wasm/wasi"
|
||||||
|
|
||||||
|
HAS_RAND_BYTES :: true
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_rand_bytes :: proc(dst: []byte) {
|
||||||
|
if err := wasi.random_get(dst); err != nil {
|
||||||
|
fmt.panicf("crypto: wasi.random_get failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,7 +16,7 @@ ELEMENT_SIZE :: 32
|
|||||||
// group element.
|
// group element.
|
||||||
WIDE_ELEMENT_SIZE :: 64
|
WIDE_ELEMENT_SIZE :: 64
|
||||||
|
|
||||||
@(private)
|
@(private, rodata)
|
||||||
FE_NEG_ONE := field.Tight_Field_Element {
|
FE_NEG_ONE := field.Tight_Field_Element {
|
||||||
2251799813685228,
|
2251799813685228,
|
||||||
2251799813685247,
|
2251799813685247,
|
||||||
@@ -24,7 +24,7 @@ FE_NEG_ONE := field.Tight_Field_Element {
|
|||||||
2251799813685247,
|
2251799813685247,
|
||||||
2251799813685247,
|
2251799813685247,
|
||||||
}
|
}
|
||||||
@(private)
|
@(private, rodata)
|
||||||
FE_INVSQRT_A_MINUS_D := field.Tight_Field_Element {
|
FE_INVSQRT_A_MINUS_D := field.Tight_Field_Element {
|
||||||
278908739862762,
|
278908739862762,
|
||||||
821645201101625,
|
821645201101625,
|
||||||
@@ -32,7 +32,7 @@ FE_INVSQRT_A_MINUS_D := field.Tight_Field_Element {
|
|||||||
1777959178193151,
|
1777959178193151,
|
||||||
2118520810568447,
|
2118520810568447,
|
||||||
}
|
}
|
||||||
@(private)
|
@(private, rodata)
|
||||||
FE_ONE_MINUS_D_SQ := field.Tight_Field_Element {
|
FE_ONE_MINUS_D_SQ := field.Tight_Field_Element {
|
||||||
1136626929484150,
|
1136626929484150,
|
||||||
1998550399581263,
|
1998550399581263,
|
||||||
@@ -40,7 +40,7 @@ FE_ONE_MINUS_D_SQ := field.Tight_Field_Element {
|
|||||||
118527312129759,
|
118527312129759,
|
||||||
45110755273534,
|
45110755273534,
|
||||||
}
|
}
|
||||||
@(private)
|
@(private, rodata)
|
||||||
FE_D_MINUS_ONE_SQUARED := field.Tight_Field_Element {
|
FE_D_MINUS_ONE_SQUARED := field.Tight_Field_Element {
|
||||||
1507062230895904,
|
1507062230895904,
|
||||||
1572317787530805,
|
1572317787530805,
|
||||||
@@ -48,7 +48,7 @@ FE_D_MINUS_ONE_SQUARED := field.Tight_Field_Element {
|
|||||||
317374165784489,
|
317374165784489,
|
||||||
1572899562415810,
|
1572899562415810,
|
||||||
}
|
}
|
||||||
@(private)
|
@(private, rodata)
|
||||||
FE_SQRT_AD_MINUS_ONE := field.Tight_Field_Element {
|
FE_SQRT_AD_MINUS_ONE := field.Tight_Field_Element {
|
||||||
2241493124984347,
|
2241493124984347,
|
||||||
425987919032274,
|
425987919032274,
|
||||||
@@ -76,7 +76,7 @@ ge_clear :: proc "contextless" (ge: ^Group_Element) {
|
|||||||
|
|
||||||
// ge_set sets `ge = a`.
|
// ge_set sets `ge = a`.
|
||||||
ge_set :: proc(ge, a: ^Group_Element) {
|
ge_set :: proc(ge, a: ^Group_Element) {
|
||||||
_ge_assert_initialized([]^Group_Element{a})
|
_ge_ensure_initialized([]^Group_Element{a})
|
||||||
|
|
||||||
grp.ge_set(&ge._p, &a._p)
|
grp.ge_set(&ge._p, &a._p)
|
||||||
ge._is_initialized = true
|
ge._is_initialized = true
|
||||||
@@ -199,9 +199,7 @@ ge_set_bytes :: proc "contextless" (ge: ^Group_Element, b: []byte) -> bool {
|
|||||||
// ge_set_wide_bytes sets ge to the result of deriving a ristretto255
|
// ge_set_wide_bytes sets ge to the result of deriving a ristretto255
|
||||||
// group element, from a wide (512-bit) byte string.
|
// group element, from a wide (512-bit) byte string.
|
||||||
ge_set_wide_bytes :: proc(ge: ^Group_Element, b: []byte) {
|
ge_set_wide_bytes :: proc(ge: ^Group_Element, b: []byte) {
|
||||||
if len(b) != WIDE_ELEMENT_SIZE {
|
ensure(len(b) == WIDE_ELEMENT_SIZE, "crypto/ristretto255: invalid wide input size")
|
||||||
panic("crypto/ristretto255: invalid wide input size")
|
|
||||||
}
|
|
||||||
|
|
||||||
// The element derivation function on an input string b proceeds as
|
// The element derivation function on an input string b proceeds as
|
||||||
// follows:
|
// follows:
|
||||||
@@ -222,10 +220,8 @@ ge_set_wide_bytes :: proc(ge: ^Group_Element, b: []byte) {
|
|||||||
|
|
||||||
// ge_bytes sets dst to the canonical encoding of ge.
|
// ge_bytes sets dst to the canonical encoding of ge.
|
||||||
ge_bytes :: proc(ge: ^Group_Element, dst: []byte) {
|
ge_bytes :: proc(ge: ^Group_Element, dst: []byte) {
|
||||||
_ge_assert_initialized([]^Group_Element{ge})
|
_ge_ensure_initialized([]^Group_Element{ge})
|
||||||
if len(dst) != ELEMENT_SIZE {
|
ensure(len(dst) == ELEMENT_SIZE, "crypto/ristretto255: invalid destination size")
|
||||||
panic("crypto/ristretto255: invalid destination size")
|
|
||||||
}
|
|
||||||
|
|
||||||
x0, y0, z0, t0 := &ge._p.x, &ge._p.y, &ge._p.z, &ge._p.t
|
x0, y0, z0, t0 := &ge._p.x, &ge._p.y, &ge._p.z, &ge._p.t
|
||||||
|
|
||||||
@@ -306,7 +302,7 @@ ge_bytes :: proc(ge: ^Group_Element, dst: []byte) {
|
|||||||
|
|
||||||
// ge_add sets `ge = a + b`.
|
// ge_add sets `ge = a + b`.
|
||||||
ge_add :: proc(ge, a, b: ^Group_Element) {
|
ge_add :: proc(ge, a, b: ^Group_Element) {
|
||||||
_ge_assert_initialized([]^Group_Element{a, b})
|
_ge_ensure_initialized([]^Group_Element{a, b})
|
||||||
|
|
||||||
grp.ge_add(&ge._p, &a._p, &b._p)
|
grp.ge_add(&ge._p, &a._p, &b._p)
|
||||||
ge._is_initialized = true
|
ge._is_initialized = true
|
||||||
@@ -314,7 +310,7 @@ ge_add :: proc(ge, a, b: ^Group_Element) {
|
|||||||
|
|
||||||
// ge_double sets `ge = a + a`.
|
// ge_double sets `ge = a + a`.
|
||||||
ge_double :: proc(ge, a: ^Group_Element) {
|
ge_double :: proc(ge, a: ^Group_Element) {
|
||||||
_ge_assert_initialized([]^Group_Element{a})
|
_ge_ensure_initialized([]^Group_Element{a})
|
||||||
|
|
||||||
grp.ge_double(&ge._p, &a._p)
|
grp.ge_double(&ge._p, &a._p)
|
||||||
ge._is_initialized = true
|
ge._is_initialized = true
|
||||||
@@ -322,7 +318,7 @@ ge_double :: proc(ge, a: ^Group_Element) {
|
|||||||
|
|
||||||
// ge_negate sets `ge = -a`.
|
// ge_negate sets `ge = -a`.
|
||||||
ge_negate :: proc(ge, a: ^Group_Element) {
|
ge_negate :: proc(ge, a: ^Group_Element) {
|
||||||
_ge_assert_initialized([]^Group_Element{a})
|
_ge_ensure_initialized([]^Group_Element{a})
|
||||||
|
|
||||||
grp.ge_negate(&ge._p, &a._p)
|
grp.ge_negate(&ge._p, &a._p)
|
||||||
ge._is_initialized = true
|
ge._is_initialized = true
|
||||||
@@ -330,7 +326,7 @@ ge_negate :: proc(ge, a: ^Group_Element) {
|
|||||||
|
|
||||||
// ge_scalarmult sets `ge = A * sc`.
|
// ge_scalarmult sets `ge = A * sc`.
|
||||||
ge_scalarmult :: proc(ge, A: ^Group_Element, sc: ^Scalar) {
|
ge_scalarmult :: proc(ge, A: ^Group_Element, sc: ^Scalar) {
|
||||||
_ge_assert_initialized([]^Group_Element{A})
|
_ge_ensure_initialized([]^Group_Element{A})
|
||||||
|
|
||||||
grp.ge_scalarmult(&ge._p, &A._p, sc)
|
grp.ge_scalarmult(&ge._p, &A._p, sc)
|
||||||
ge._is_initialized = true
|
ge._is_initialized = true
|
||||||
@@ -344,7 +340,7 @@ ge_scalarmult_generator :: proc "contextless" (ge: ^Group_Element, sc: ^Scalar)
|
|||||||
|
|
||||||
// ge_scalarmult_vartime sets `ge = A * sc` in variable time.
|
// ge_scalarmult_vartime sets `ge = A * sc` in variable time.
|
||||||
ge_scalarmult_vartime :: proc(ge, A: ^Group_Element, sc: ^Scalar) {
|
ge_scalarmult_vartime :: proc(ge, A: ^Group_Element, sc: ^Scalar) {
|
||||||
_ge_assert_initialized([]^Group_Element{A})
|
_ge_ensure_initialized([]^Group_Element{A})
|
||||||
|
|
||||||
grp.ge_scalarmult_vartime(&ge._p, &A._p, sc)
|
grp.ge_scalarmult_vartime(&ge._p, &A._p, sc)
|
||||||
ge._is_initialized = true
|
ge._is_initialized = true
|
||||||
@@ -358,7 +354,7 @@ ge_double_scalarmult_generator_vartime :: proc(
|
|||||||
A: ^Group_Element,
|
A: ^Group_Element,
|
||||||
b: ^Scalar,
|
b: ^Scalar,
|
||||||
) {
|
) {
|
||||||
_ge_assert_initialized([]^Group_Element{A})
|
_ge_ensure_initialized([]^Group_Element{A})
|
||||||
|
|
||||||
grp.ge_double_scalarmult_basepoint_vartime(&ge._p, a, &A._p, b)
|
grp.ge_double_scalarmult_basepoint_vartime(&ge._p, a, &A._p, b)
|
||||||
ge._is_initialized = true
|
ge._is_initialized = true
|
||||||
@@ -367,7 +363,7 @@ ge_double_scalarmult_generator_vartime :: proc(
|
|||||||
// ge_cond_negate sets `ge = a` iff `ctrl == 0` and `ge = -a` iff `ctrl == 1`.
|
// ge_cond_negate sets `ge = a` iff `ctrl == 0` and `ge = -a` iff `ctrl == 1`.
|
||||||
// Behavior for all other values of ctrl are undefined,
|
// Behavior for all other values of ctrl are undefined,
|
||||||
ge_cond_negate :: proc(ge, a: ^Group_Element, ctrl: int) {
|
ge_cond_negate :: proc(ge, a: ^Group_Element, ctrl: int) {
|
||||||
_ge_assert_initialized([]^Group_Element{a})
|
_ge_ensure_initialized([]^Group_Element{a})
|
||||||
|
|
||||||
grp.ge_cond_negate(&ge._p, &a._p, ctrl)
|
grp.ge_cond_negate(&ge._p, &a._p, ctrl)
|
||||||
ge._is_initialized = true
|
ge._is_initialized = true
|
||||||
@@ -376,7 +372,7 @@ ge_cond_negate :: proc(ge, a: ^Group_Element, ctrl: int) {
|
|||||||
// ge_cond_assign sets `ge = ge` iff `ctrl == 0` and `ge = a` iff `ctrl == 1`.
|
// ge_cond_assign sets `ge = ge` iff `ctrl == 0` and `ge = a` iff `ctrl == 1`.
|
||||||
// Behavior for all other values of ctrl are undefined,
|
// Behavior for all other values of ctrl are undefined,
|
||||||
ge_cond_assign :: proc(ge, a: ^Group_Element, ctrl: int) {
|
ge_cond_assign :: proc(ge, a: ^Group_Element, ctrl: int) {
|
||||||
_ge_assert_initialized([]^Group_Element{ge, a})
|
_ge_ensure_initialized([]^Group_Element{ge, a})
|
||||||
|
|
||||||
grp.ge_cond_assign(&ge._p, &a._p, ctrl)
|
grp.ge_cond_assign(&ge._p, &a._p, ctrl)
|
||||||
}
|
}
|
||||||
@@ -384,7 +380,7 @@ ge_cond_assign :: proc(ge, a: ^Group_Element, ctrl: int) {
|
|||||||
// ge_cond_select sets `ge = a` iff `ctrl == 0` and `ge = b` iff `ctrl == 1`.
|
// ge_cond_select sets `ge = a` iff `ctrl == 0` and `ge = b` iff `ctrl == 1`.
|
||||||
// Behavior for all other values of ctrl are undefined,
|
// Behavior for all other values of ctrl are undefined,
|
||||||
ge_cond_select :: proc(ge, a, b: ^Group_Element, ctrl: int) {
|
ge_cond_select :: proc(ge, a, b: ^Group_Element, ctrl: int) {
|
||||||
_ge_assert_initialized([]^Group_Element{a, b})
|
_ge_ensure_initialized([]^Group_Element{a, b})
|
||||||
|
|
||||||
grp.ge_cond_select(&ge._p, &a._p, &b._p, ctrl)
|
grp.ge_cond_select(&ge._p, &a._p, &b._p, ctrl)
|
||||||
ge._is_initialized = true
|
ge._is_initialized = true
|
||||||
@@ -393,7 +389,7 @@ ge_cond_select :: proc(ge, a, b: ^Group_Element, ctrl: int) {
|
|||||||
// ge_equal returns 1 iff `a == b`, and 0 otherwise.
|
// ge_equal returns 1 iff `a == b`, and 0 otherwise.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
ge_equal :: proc(a, b: ^Group_Element) -> int {
|
ge_equal :: proc(a, b: ^Group_Element) -> int {
|
||||||
_ge_assert_initialized([]^Group_Element{a, b})
|
_ge_ensure_initialized([]^Group_Element{a, b})
|
||||||
|
|
||||||
// CT_EQ(x1 * y2, y1 * x2) | CT_EQ(y1 * y2, x1 * x2)
|
// CT_EQ(x1 * y2, y1 * x2) | CT_EQ(y1 * y2, x1 * x2)
|
||||||
ax_by, ay_bx, ay_by, ax_bx: field.Tight_Field_Element = ---, ---, ---, ---
|
ax_by, ay_bx, ay_by, ax_bx: field.Tight_Field_Element = ---, ---, ---, ---
|
||||||
@@ -501,10 +497,8 @@ ge_map :: proc "contextless" (ge: ^Group_Element, b: []byte) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
_ge_assert_initialized :: proc(ges: []^Group_Element) {
|
_ge_ensure_initialized :: proc(ges: []^Group_Element) {
|
||||||
for ge in ges {
|
for ge in ges {
|
||||||
if !ge._is_initialized {
|
ensure(ge._is_initialized, "crypto/ristretto255: uninitialized group element")
|
||||||
panic("crypto/ristretto255: uninitialized group element")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,9 +42,7 @@ sc_set_bytes :: proc(sc: ^Scalar, b: []byte) -> bool {
|
|||||||
// scalar, from a wide (512-bit) byte string by interpreting b as a
|
// scalar, from a wide (512-bit) byte string by interpreting b as a
|
||||||
// little-endian value, and reducing it mod the group order.
|
// little-endian value, and reducing it mod the group order.
|
||||||
sc_set_bytes_wide :: proc(sc: ^Scalar, b: []byte) {
|
sc_set_bytes_wide :: proc(sc: ^Scalar, b: []byte) {
|
||||||
if len(b) != WIDE_SCALAR_SIZE {
|
ensure(len(b) == WIDE_SCALAR_SIZE, "crypto/ristretto255: invalid wide input size")
|
||||||
panic("crypto/ristretto255: invalid wide input size")
|
|
||||||
}
|
|
||||||
|
|
||||||
b_ := (^[WIDE_SCALAR_SIZE]byte)(raw_data(b))
|
b_ := (^[WIDE_SCALAR_SIZE]byte)(raw_data(b))
|
||||||
grp.sc_set_bytes_wide(sc, b_)
|
grp.sc_set_bytes_wide(sc, b_)
|
||||||
@@ -52,9 +50,7 @@ sc_set_bytes_wide :: proc(sc: ^Scalar, b: []byte) {
|
|||||||
|
|
||||||
// sc_bytes sets dst to the canonical encoding of sc.
|
// sc_bytes sets dst to the canonical encoding of sc.
|
||||||
sc_bytes :: proc(sc: ^Scalar, dst: []byte) {
|
sc_bytes :: proc(sc: ^Scalar, dst: []byte) {
|
||||||
if len(dst) != SCALAR_SIZE {
|
ensure(len(dst) == SCALAR_SIZE, "crypto/ristretto255: invalid destination size")
|
||||||
panic("crypto/ristretto255: invalid destination size")
|
|
||||||
}
|
|
||||||
|
|
||||||
grp.sc_bytes(dst, sc)
|
grp.sc_bytes(dst, sc)
|
||||||
}
|
}
|
||||||
|
|||||||
+34
-25
@@ -15,9 +15,9 @@ package sha2
|
|||||||
zhibog, dotbmp: Initial implementation.
|
zhibog, dotbmp: Initial implementation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import "core:encoding/endian"
|
@(require) import "core:encoding/endian"
|
||||||
import "core:math/bits"
|
import "core:math/bits"
|
||||||
import "core:mem"
|
@(require) import "core:mem"
|
||||||
|
|
||||||
// DIGEST_SIZE_224 is the SHA-224 digest size in bytes.
|
// DIGEST_SIZE_224 is the SHA-224 digest size in bytes.
|
||||||
DIGEST_SIZE_224 :: 28
|
DIGEST_SIZE_224 :: 28
|
||||||
@@ -158,7 +158,7 @@ _init :: proc(ctx: ^$T) {
|
|||||||
|
|
||||||
// update adds more data to the Context.
|
// update adds more data to the Context.
|
||||||
update :: proc(ctx: ^$T, data: []byte) {
|
update :: proc(ctx: ^$T, data: []byte) {
|
||||||
assert(ctx.is_initialized)
|
ensure(ctx.is_initialized)
|
||||||
|
|
||||||
when T == Context_256 {
|
when T == Context_256 {
|
||||||
CURR_BLOCK_SIZE :: BLOCK_SIZE_256
|
CURR_BLOCK_SIZE :: BLOCK_SIZE_256
|
||||||
@@ -194,11 +194,8 @@ update :: proc(ctx: ^$T, data: []byte) {
|
|||||||
// Iff finalize_clone is set, final will work on a copy of the Context,
|
// Iff finalize_clone is set, final will work on a copy of the Context,
|
||||||
// which is useful for for calculating rolling digests.
|
// which is useful for for calculating rolling digests.
|
||||||
final :: proc(ctx: ^$T, hash: []byte, finalize_clone: bool = false) {
|
final :: proc(ctx: ^$T, hash: []byte, finalize_clone: bool = false) {
|
||||||
assert(ctx.is_initialized)
|
ensure(ctx.is_initialized)
|
||||||
|
ensure(len(hash) * 8 >= ctx.md_bits, "crypto/sha2: invalid destination digest size")
|
||||||
if len(hash) * 8 < ctx.md_bits {
|
|
||||||
panic("crypto/sha2: invalid destination digest size")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := ctx
|
ctx := ctx
|
||||||
if finalize_clone {
|
if finalize_clone {
|
||||||
@@ -238,7 +235,7 @@ final :: proc(ctx: ^$T, hash: []byte, finalize_clone: bool = false) {
|
|||||||
endian.unchecked_put_u64be(pad[8:], length_lo)
|
endian.unchecked_put_u64be(pad[8:], length_lo)
|
||||||
update(ctx, pad[0:16])
|
update(ctx, pad[0:16])
|
||||||
}
|
}
|
||||||
assert(ctx.bitlength == 0)
|
assert(ctx.bitlength == 0) // Check for bugs
|
||||||
|
|
||||||
when T == Context_256 {
|
when T == Context_256 {
|
||||||
for i := 0; i < ctx.md_bits / 32; i += 1 {
|
for i := 0; i < ctx.md_bits / 32; i += 1 {
|
||||||
@@ -270,8 +267,8 @@ reset :: proc(ctx: ^$T) {
|
|||||||
SHA2 implementation
|
SHA2 implementation
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@(private)
|
@(private, rodata)
|
||||||
sha256_k := [64]u32 {
|
SHA256_K := [64]u32 {
|
||||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
|
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
|
||||||
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
||||||
@@ -290,8 +287,8 @@ sha256_k := [64]u32 {
|
|||||||
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private, rodata)
|
||||||
sha512_k := [80]u64 {
|
SHA512_K := [80]u64 {
|
||||||
0x428a2f98d728ae22, 0x7137449123ef65cd,
|
0x428a2f98d728ae22, 0x7137449123ef65cd,
|
||||||
0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
|
0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
|
||||||
0x3956c25bf348b538, 0x59f111f1b605d019,
|
0x3956c25bf348b538, 0x59f111f1b605d019,
|
||||||
@@ -334,6 +331,11 @@ sha512_k := [80]u64 {
|
|||||||
0x5fcb6fab3ad6faec, 0x6c44198c4a475817,
|
0x5fcb6fab3ad6faec, 0x6c44198c4a475817,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
SHA256_ROUNDS :: 64
|
||||||
|
@(private)
|
||||||
|
SHA512_ROUNDS :: 80
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
SHA256_CH :: #force_inline proc "contextless" (x, y, z: u32) -> u32 {
|
SHA256_CH :: #force_inline proc "contextless" (x, y, z: u32) -> u32 {
|
||||||
return (x & y) ~ (~x & z)
|
return (x & y) ~ (~x & z)
|
||||||
@@ -395,22 +397,29 @@ SHA512_F4 :: #force_inline proc "contextless" (x: u64) -> u64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
sha2_transf :: proc "contextless" (ctx: ^$T, data: []byte) {
|
sha2_transf :: proc "contextless" (ctx: ^$T, data: []byte) #no_bounds_check {
|
||||||
when T == Context_256 {
|
when T == Context_256 {
|
||||||
w: [64]u32
|
if is_hardware_accelerated_256() {
|
||||||
|
sha256_transf_hw(ctx, data)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w: [SHA256_ROUNDS]u32
|
||||||
wv: [8]u32
|
wv: [8]u32
|
||||||
t1, t2: u32
|
t1, t2: u32
|
||||||
|
|
||||||
CURR_BLOCK_SIZE :: BLOCK_SIZE_256
|
CURR_BLOCK_SIZE :: BLOCK_SIZE_256
|
||||||
} else when T == Context_512 {
|
} else when T == Context_512 {
|
||||||
w: [80]u64
|
w: [SHA512_ROUNDS]u64
|
||||||
wv: [8]u64
|
wv: [8]u64
|
||||||
t1, t2: u64
|
t1, t2: u64
|
||||||
|
|
||||||
CURR_BLOCK_SIZE :: BLOCK_SIZE_512
|
CURR_BLOCK_SIZE :: BLOCK_SIZE_512
|
||||||
}
|
}
|
||||||
|
|
||||||
data := data
|
data := data
|
||||||
for len(data) >= CURR_BLOCK_SIZE {
|
for len(data) >= CURR_BLOCK_SIZE {
|
||||||
for i := 0; i < 16; i += 1 {
|
for i in 0 ..< 16 {
|
||||||
when T == Context_256 {
|
when T == Context_256 {
|
||||||
w[i] = endian.unchecked_get_u32be(data[i * 4:])
|
w[i] = endian.unchecked_get_u32be(data[i * 4:])
|
||||||
} else when T == Context_512 {
|
} else when T == Context_512 {
|
||||||
@@ -419,22 +428,22 @@ sha2_transf :: proc "contextless" (ctx: ^$T, data: []byte) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
when T == Context_256 {
|
when T == Context_256 {
|
||||||
for i := 16; i < 64; i += 1 {
|
for i in 16 ..< SHA256_ROUNDS {
|
||||||
w[i] = SHA256_F4(w[i - 2]) + w[i - 7] + SHA256_F3(w[i - 15]) + w[i - 16]
|
w[i] = SHA256_F4(w[i - 2]) + w[i - 7] + SHA256_F3(w[i - 15]) + w[i - 16]
|
||||||
}
|
}
|
||||||
} else when T == Context_512 {
|
} else when T == Context_512 {
|
||||||
for i := 16; i < 80; i += 1 {
|
for i in 16 ..< SHA512_ROUNDS {
|
||||||
w[i] = SHA512_F4(w[i - 2]) + w[i - 7] + SHA512_F3(w[i - 15]) + w[i - 16]
|
w[i] = SHA512_F4(w[i - 2]) + w[i - 7] + SHA512_F3(w[i - 15]) + w[i - 16]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < 8; i += 1 {
|
for i in 0 ..< 8 {
|
||||||
wv[i] = ctx.h[i]
|
wv[i] = ctx.h[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
when T == Context_256 {
|
when T == Context_256 {
|
||||||
for i := 0; i < 64; i += 1 {
|
for i in 0 ..< SHA256_ROUNDS {
|
||||||
t1 = wv[7] + SHA256_F2(wv[4]) + SHA256_CH(wv[4], wv[5], wv[6]) + sha256_k[i] + w[i]
|
t1 = wv[7] + SHA256_F2(wv[4]) + SHA256_CH(wv[4], wv[5], wv[6]) + SHA256_K[i] + w[i]
|
||||||
t2 = SHA256_F1(wv[0]) + SHA256_MAJ(wv[0], wv[1], wv[2])
|
t2 = SHA256_F1(wv[0]) + SHA256_MAJ(wv[0], wv[1], wv[2])
|
||||||
wv[7] = wv[6]
|
wv[7] = wv[6]
|
||||||
wv[6] = wv[5]
|
wv[6] = wv[5]
|
||||||
@@ -446,8 +455,8 @@ sha2_transf :: proc "contextless" (ctx: ^$T, data: []byte) {
|
|||||||
wv[0] = t1 + t2
|
wv[0] = t1 + t2
|
||||||
}
|
}
|
||||||
} else when T == Context_512 {
|
} else when T == Context_512 {
|
||||||
for i := 0; i < 80; i += 1 {
|
for i in 0 ..< SHA512_ROUNDS {
|
||||||
t1 = wv[7] + SHA512_F2(wv[4]) + SHA512_CH(wv[4], wv[5], wv[6]) + sha512_k[i] + w[i]
|
t1 = wv[7] + SHA512_F2(wv[4]) + SHA512_CH(wv[4], wv[5], wv[6]) + SHA512_K[i] + w[i]
|
||||||
t2 = SHA512_F1(wv[0]) + SHA512_MAJ(wv[0], wv[1], wv[2])
|
t2 = SHA512_F1(wv[0]) + SHA512_MAJ(wv[0], wv[1], wv[2])
|
||||||
wv[7] = wv[6]
|
wv[7] = wv[6]
|
||||||
wv[6] = wv[5]
|
wv[6] = wv[5]
|
||||||
@@ -460,7 +469,7 @@ sha2_transf :: proc "contextless" (ctx: ^$T, data: []byte) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < 8; i += 1 {
|
for i in 0 ..< 8 {
|
||||||
ctx.h[i] += wv[i]
|
ctx.h[i] += wv[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#+build !amd64
|
||||||
|
package sha2
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
ERR_HW_NOT_SUPPORTED :: "crypto/sha2: hardware implementation unsupported"
|
||||||
|
|
||||||
|
// is_hardware_accelerated_256 returns true iff hardware accelerated
|
||||||
|
// SHA-224/SHA-256 is supported.
|
||||||
|
is_hardware_accelerated_256 :: proc "contextless" () -> bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
sha256_transf_hw :: proc "contextless" (ctx: ^Context_256, data: []byte) {
|
||||||
|
panic_contextless(ERR_HW_NOT_SUPPORTED)
|
||||||
|
}
|
||||||
@@ -0,0 +1,260 @@
|
|||||||
|
#+build amd64
|
||||||
|
package sha2
|
||||||
|
|
||||||
|
// Based on the public domain code by Jeffrey Walton, though
|
||||||
|
// realistically, there only is one sensible way to write this
|
||||||
|
// and Intel's whitepaper covers it.
|
||||||
|
//
|
||||||
|
// See: https://github.com/noloader/SHA-Intrinsics
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
import "core:simd"
|
||||||
|
import "core:simd/x86"
|
||||||
|
import "core:sys/info"
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
MASK :: x86.__m128i{0x0405060700010203, 0x0c0d0e0f08090a0b}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
K_0 :: simd.u64x2{0x71374491428a2f98, 0xe9b5dba5b5c0fbcf}
|
||||||
|
@(private = "file")
|
||||||
|
K_1 :: simd.u64x2{0x59f111f13956c25b, 0xab1c5ed5923f82a4}
|
||||||
|
@(private = "file")
|
||||||
|
K_2 :: simd.u64x2{0x12835b01d807aa98, 0x550c7dc3243185be}
|
||||||
|
@(private = "file")
|
||||||
|
K_3 :: simd.u64x2{0x80deb1fe72be5d74, 0xc19bf1749bdc06a7}
|
||||||
|
@(private = "file")
|
||||||
|
K_4 :: simd.u64x2{0xefbe4786e49b69c1, 0x240ca1cc0fc19dc6}
|
||||||
|
@(private = "file")
|
||||||
|
K_5 :: simd.u64x2{0x4a7484aa2de92c6f, 0x76f988da5cb0a9dc}
|
||||||
|
@(private = "file")
|
||||||
|
K_6 :: simd.u64x2{0xa831c66d983e5152, 0xbf597fc7b00327c8}
|
||||||
|
@(private = "file")
|
||||||
|
K_7 :: simd.u64x2{0xd5a79147c6e00bf3, 0x1429296706ca6351}
|
||||||
|
@(private = "file")
|
||||||
|
K_8 :: simd.u64x2{0x2e1b213827b70a85, 0x53380d134d2c6dfc}
|
||||||
|
@(private = "file")
|
||||||
|
K_9 :: simd.u64x2{0x766a0abb650a7354, 0x92722c8581c2c92e}
|
||||||
|
@(private = "file")
|
||||||
|
K_10 :: simd.u64x2{0xa81a664ba2bfe8a1, 0xc76c51a3c24b8b70}
|
||||||
|
@(private = "file")
|
||||||
|
K_11 :: simd.u64x2{0xd6990624d192e819, 0x106aa070f40e3585}
|
||||||
|
@(private = "file")
|
||||||
|
K_12 :: simd.u64x2{0x1e376c0819a4c116, 0x34b0bcb52748774c}
|
||||||
|
@(private = "file")
|
||||||
|
K_13 :: simd.u64x2{0x4ed8aa4a391c0cb3, 0x682e6ff35b9cca4f}
|
||||||
|
@(private = "file")
|
||||||
|
K_14 :: simd.u64x2{0x78a5636f748f82ee, 0x8cc7020884c87814}
|
||||||
|
@(private = "file")
|
||||||
|
K_15 :: simd.u64x2{0xa4506ceb90befffa, 0xc67178f2bef9a3f7}
|
||||||
|
|
||||||
|
|
||||||
|
// is_hardware_accelerated_256 returns true iff hardware accelerated
|
||||||
|
// SHA-224/SHA-256 is supported.
|
||||||
|
is_hardware_accelerated_256 :: proc "contextless" () -> bool {
|
||||||
|
features, ok := info.cpu_features.?
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
req_features :: info.CPU_Features{
|
||||||
|
.sse2,
|
||||||
|
.ssse3,
|
||||||
|
.sse41,
|
||||||
|
.sha,
|
||||||
|
}
|
||||||
|
return features >= req_features
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private, enable_target_feature="sse2,ssse3,sse4.1,sha")
|
||||||
|
sha256_transf_hw :: proc "contextless" (ctx: ^Context_256, data: []byte) #no_bounds_check {
|
||||||
|
// Load the state
|
||||||
|
tmp := intrinsics.unaligned_load((^x86.__m128i)(&ctx.h[0]))
|
||||||
|
state_1 := intrinsics.unaligned_load((^x86.__m128i)(&ctx.h[4]))
|
||||||
|
|
||||||
|
tmp = x86._mm_shuffle_epi32(tmp, 0xb1) // CDAB
|
||||||
|
state_1 = x86._mm_shuffle_epi32(state_1, 0x1b) // EFGH
|
||||||
|
state_0 := x86._mm_alignr_epi8(tmp, state_1, 8) // ABEF
|
||||||
|
// state_1 = x86._mm_blend_epi16(state_1, tmp, 0xf0) // CDGH
|
||||||
|
state_1 = kludge_mm_blend_epi16_0xf0(state_1, tmp)
|
||||||
|
|
||||||
|
data := data
|
||||||
|
for len(data) >= BLOCK_SIZE_256 {
|
||||||
|
state_0_save, state_1_save := state_0, state_1
|
||||||
|
|
||||||
|
// Rounds 0-3
|
||||||
|
msg := intrinsics.unaligned_load((^x86.__m128i)(raw_data(data)))
|
||||||
|
msg_0 := x86._mm_shuffle_epi8(msg, MASK)
|
||||||
|
msg = x86._mm_add_epi32(msg_0, x86.__m128i(K_0))
|
||||||
|
state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
|
||||||
|
msg = x86._mm_shuffle_epi32(msg, 0xe)
|
||||||
|
state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
|
||||||
|
|
||||||
|
// Rounds 4-7
|
||||||
|
msg_1 := intrinsics.unaligned_load((^x86.__m128i)(raw_data(data[16:])))
|
||||||
|
msg_1 = x86._mm_shuffle_epi8(msg_1, MASK)
|
||||||
|
msg = x86._mm_add_epi32(msg_1, x86.__m128i(K_1))
|
||||||
|
state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
|
||||||
|
msg = x86._mm_shuffle_epi32(msg, 0xe)
|
||||||
|
state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
|
||||||
|
msg_0 = x86._mm_sha256msg1_epu32(msg_0, msg_1)
|
||||||
|
|
||||||
|
// Rounds 8-11
|
||||||
|
msg_2 := intrinsics.unaligned_load((^x86.__m128i)(raw_data(data[32:])))
|
||||||
|
msg_2 = x86._mm_shuffle_epi8(msg_2, MASK)
|
||||||
|
msg = x86._mm_add_epi32(msg_2, x86.__m128i(K_2))
|
||||||
|
state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
|
||||||
|
msg = x86._mm_shuffle_epi32(msg, 0xe)
|
||||||
|
state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
|
||||||
|
msg_1 = x86._mm_sha256msg1_epu32(msg_1, msg_2)
|
||||||
|
|
||||||
|
// Rounds 12-15
|
||||||
|
msg_3 := intrinsics.unaligned_load((^x86.__m128i)(raw_data(data[48:])))
|
||||||
|
msg_3 = x86._mm_shuffle_epi8(msg_3, MASK)
|
||||||
|
msg = x86._mm_add_epi32(msg_3, x86.__m128i(K_3))
|
||||||
|
state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
|
||||||
|
tmp = x86._mm_alignr_epi8(msg_3, msg_2, 4)
|
||||||
|
msg_0 = x86._mm_add_epi32(msg_0, tmp)
|
||||||
|
msg_0 = x86._mm_sha256msg2_epu32(msg_0, msg_3)
|
||||||
|
msg = x86._mm_shuffle_epi32(msg, 0x0e)
|
||||||
|
state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
|
||||||
|
msg_2 = x86._mm_sha256msg1_epu32(msg_2, msg_3)
|
||||||
|
|
||||||
|
// Rounds 16-19
|
||||||
|
msg = x86._mm_add_epi32(msg_0, x86.__m128i(K_4))
|
||||||
|
state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
|
||||||
|
tmp = x86._mm_alignr_epi8(msg_0, msg_3, 4)
|
||||||
|
msg_1 = x86._mm_add_epi32(msg_1, tmp)
|
||||||
|
msg_1 = x86._mm_sha256msg2_epu32(msg_1, msg_0)
|
||||||
|
msg = x86._mm_shuffle_epi32(msg, 0x0e)
|
||||||
|
state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
|
||||||
|
msg_3 = x86._mm_sha256msg1_epu32(msg_3, msg_0)
|
||||||
|
|
||||||
|
// Rounds 20-23
|
||||||
|
msg = x86._mm_add_epi32(msg_1, x86.__m128i(K_5))
|
||||||
|
state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
|
||||||
|
tmp = x86._mm_alignr_epi8(msg_1, msg_0, 4)
|
||||||
|
msg_2 = x86._mm_add_epi32(msg_2, tmp)
|
||||||
|
msg_2 = x86._mm_sha256msg2_epu32(msg_2, msg_1)
|
||||||
|
msg = x86._mm_shuffle_epi32(msg, 0x0e)
|
||||||
|
state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
|
||||||
|
msg_0 = x86._mm_sha256msg1_epu32(msg_0, msg_1)
|
||||||
|
|
||||||
|
// Rounds 24-27
|
||||||
|
msg = x86._mm_add_epi32(msg_2, x86.__m128i(K_6))
|
||||||
|
state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
|
||||||
|
tmp = x86._mm_alignr_epi8(msg_2, msg_1, 4)
|
||||||
|
msg_3 = x86._mm_add_epi32(msg_3, tmp)
|
||||||
|
msg_3 = x86._mm_sha256msg2_epu32(msg_3, msg_2)
|
||||||
|
msg = x86._mm_shuffle_epi32(msg, 0x0e)
|
||||||
|
state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
|
||||||
|
msg_1 = x86._mm_sha256msg1_epu32(msg_1, msg_2)
|
||||||
|
|
||||||
|
// Rounds 28-31
|
||||||
|
msg = x86._mm_add_epi32(msg_3, x86.__m128i(K_7))
|
||||||
|
state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
|
||||||
|
tmp = x86._mm_alignr_epi8(msg_3, msg_2, 4)
|
||||||
|
msg_0 = x86._mm_add_epi32(msg_0, tmp)
|
||||||
|
msg_0 = x86._mm_sha256msg2_epu32(msg_0, msg_3)
|
||||||
|
msg = x86._mm_shuffle_epi32(msg, 0x0e)
|
||||||
|
state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
|
||||||
|
msg_2 = x86._mm_sha256msg1_epu32(msg_2, msg_3)
|
||||||
|
|
||||||
|
// Rounds 32-35
|
||||||
|
msg = x86._mm_add_epi32(msg_0, x86.__m128i(K_8))
|
||||||
|
state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
|
||||||
|
tmp = x86._mm_alignr_epi8(msg_0, msg_3, 4)
|
||||||
|
msg_1 = x86._mm_add_epi32(msg_1, tmp)
|
||||||
|
msg_1 = x86._mm_sha256msg2_epu32(msg_1, msg_0)
|
||||||
|
msg = x86._mm_shuffle_epi32(msg, 0x0e)
|
||||||
|
state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
|
||||||
|
msg_3 = x86._mm_sha256msg1_epu32(msg_3, msg_0)
|
||||||
|
|
||||||
|
// Rounds 36-39
|
||||||
|
msg = x86._mm_add_epi32(msg_1, x86.__m128i(K_9))
|
||||||
|
state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
|
||||||
|
tmp = x86._mm_alignr_epi8(msg_1, msg_0, 4)
|
||||||
|
msg_2 = x86._mm_add_epi32(msg_2, tmp)
|
||||||
|
msg_2 = x86._mm_sha256msg2_epu32(msg_2, msg_1)
|
||||||
|
msg = x86._mm_shuffle_epi32(msg, 0x0e)
|
||||||
|
state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
|
||||||
|
msg_0 = x86._mm_sha256msg1_epu32(msg_0, msg_1)
|
||||||
|
|
||||||
|
// Rounds 40-43
|
||||||
|
msg = x86._mm_add_epi32(msg_2, x86.__m128i(K_10))
|
||||||
|
state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
|
||||||
|
tmp = x86._mm_alignr_epi8(msg_2, msg_1, 4)
|
||||||
|
msg_3 = x86._mm_add_epi32(msg_3, tmp)
|
||||||
|
msg_3 = x86._mm_sha256msg2_epu32(msg_3, msg_2)
|
||||||
|
msg = x86._mm_shuffle_epi32(msg, 0x0e)
|
||||||
|
state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
|
||||||
|
msg_1 = x86._mm_sha256msg1_epu32(msg_1, msg_2)
|
||||||
|
|
||||||
|
// Rounds 44-47
|
||||||
|
msg = x86._mm_add_epi32(msg_3, x86.__m128i(K_11))
|
||||||
|
state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
|
||||||
|
tmp = x86._mm_alignr_epi8(msg_3, msg_2, 4)
|
||||||
|
msg_0 = x86._mm_add_epi32(msg_0, tmp)
|
||||||
|
msg_0 = x86._mm_sha256msg2_epu32(msg_0, msg_3)
|
||||||
|
msg = x86._mm_shuffle_epi32(msg, 0x0e)
|
||||||
|
state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
|
||||||
|
msg_2 = x86._mm_sha256msg1_epu32(msg_2, msg_3)
|
||||||
|
|
||||||
|
// Rounds 48-51
|
||||||
|
msg = x86._mm_add_epi32(msg_0, x86.__m128i(K_12))
|
||||||
|
state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
|
||||||
|
tmp = x86._mm_alignr_epi8(msg_0, msg_3, 4)
|
||||||
|
msg_1 = x86._mm_add_epi32(msg_1, tmp)
|
||||||
|
msg_1 = x86._mm_sha256msg2_epu32(msg_1, msg_0)
|
||||||
|
msg = x86._mm_shuffle_epi32(msg, 0x0e)
|
||||||
|
state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
|
||||||
|
msg_3 = x86._mm_sha256msg1_epu32(msg_3, msg_0)
|
||||||
|
|
||||||
|
// Rounds 52-55
|
||||||
|
msg = x86._mm_add_epi32(msg_1, x86.__m128i(K_13))
|
||||||
|
state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
|
||||||
|
tmp = x86._mm_alignr_epi8(msg_1, msg_0, 4)
|
||||||
|
msg_2 = x86._mm_add_epi32(msg_2, tmp)
|
||||||
|
msg_2 = x86._mm_sha256msg2_epu32(msg_2, msg_1)
|
||||||
|
msg = x86._mm_shuffle_epi32(msg, 0x0e)
|
||||||
|
state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
|
||||||
|
|
||||||
|
/* Rounds 56-59 */
|
||||||
|
msg = x86._mm_add_epi32(msg_2, x86.__m128i(K_14))
|
||||||
|
state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
|
||||||
|
tmp = x86._mm_alignr_epi8(msg_2, msg_1, 4)
|
||||||
|
msg_3 = x86._mm_add_epi32(msg_3, tmp)
|
||||||
|
msg_3 = x86._mm_sha256msg2_epu32(msg_3, msg_2)
|
||||||
|
msg = x86._mm_shuffle_epi32(msg, 0x0e)
|
||||||
|
state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
|
||||||
|
|
||||||
|
// Rounds 60-63
|
||||||
|
msg = x86._mm_add_epi32(msg_3, x86.__m128i(K_15))
|
||||||
|
state_1 = x86._mm_sha256rnds2_epu32(state_1, state_0, msg)
|
||||||
|
msg = x86._mm_shuffle_epi32(msg, 0x0e)
|
||||||
|
state_0 = x86._mm_sha256rnds2_epu32(state_0, state_1, msg)
|
||||||
|
|
||||||
|
state_0 = x86._mm_add_epi32(state_0, state_0_save)
|
||||||
|
state_1 = x86._mm_add_epi32(state_1, state_1_save)
|
||||||
|
|
||||||
|
data = data[BLOCK_SIZE_256:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write back the updated state
|
||||||
|
tmp = x86._mm_shuffle_epi32(state_0, 0x1b) // FEBA
|
||||||
|
state_1 = x86._mm_shuffle_epi32(state_1, 0xb1) // DCHG
|
||||||
|
// state_0 = x86._mm_blend_epi16(tmp, state_1, 0xf0) // DCBA
|
||||||
|
state_0 = kludge_mm_blend_epi16_0xf0(tmp, state_1)
|
||||||
|
state_1 = x86._mm_alignr_epi8(state_1, tmp, 8) // ABEF
|
||||||
|
|
||||||
|
intrinsics.unaligned_store((^x86.__m128i)(&ctx.h[0]), state_0)
|
||||||
|
intrinsics.unaligned_store((^x86.__m128i)(&ctx.h[4]), state_1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
kludge_mm_blend_epi16_0xf0 :: #force_inline proc "contextless"(a, b: x86.__m128i) -> x86.__m128i {
|
||||||
|
// HACK HACK HACK: LLVM got rid of `llvm.x86.sse41.pblendw`.
|
||||||
|
a_ := simd.to_array(a)
|
||||||
|
b_ := simd.to_array(b)
|
||||||
|
return x86.__m128i{a_[0], b_[1]}
|
||||||
|
}
|
||||||
@@ -219,18 +219,14 @@ verify_4_8 :: proc {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
init :: proc(ctx: ^Context, key: []byte, c_rounds, d_rounds: int) {
|
init :: proc(ctx: ^Context, key: []byte, c_rounds, d_rounds: int) {
|
||||||
if len(key) != KEY_SIZE {
|
ensure(len(key) == KEY_SIZE,"crypto/siphash; invalid key size")
|
||||||
panic("crypto/siphash; invalid key size")
|
|
||||||
}
|
|
||||||
ctx.c_rounds = c_rounds
|
ctx.c_rounds = c_rounds
|
||||||
ctx.d_rounds = d_rounds
|
ctx.d_rounds = d_rounds
|
||||||
is_valid_setting :=
|
is_valid_setting :=
|
||||||
(ctx.c_rounds == 1 && ctx.d_rounds == 3) ||
|
(ctx.c_rounds == 1 && ctx.d_rounds == 3) ||
|
||||||
(ctx.c_rounds == 2 && ctx.d_rounds == 4) ||
|
(ctx.c_rounds == 2 && ctx.d_rounds == 4) ||
|
||||||
(ctx.c_rounds == 4 && ctx.d_rounds == 8)
|
(ctx.c_rounds == 4 && ctx.d_rounds == 8)
|
||||||
if !is_valid_setting {
|
ensure(is_valid_setting, "crypto/siphash: incorrect rounds set up")
|
||||||
panic("crypto/siphash: incorrect rounds set up")
|
|
||||||
}
|
|
||||||
ctx.k0 = endian.unchecked_get_u64le(key[:8])
|
ctx.k0 = endian.unchecked_get_u64le(key[:8])
|
||||||
ctx.k1 = endian.unchecked_get_u64le(key[8:])
|
ctx.k1 = endian.unchecked_get_u64le(key[8:])
|
||||||
ctx.v0 = 0x736f6d6570736575 ~ ctx.k0
|
ctx.v0 = 0x736f6d6570736575 ~ ctx.k0
|
||||||
@@ -245,7 +241,7 @@ init :: proc(ctx: ^Context, key: []byte, c_rounds, d_rounds: int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
update :: proc(ctx: ^Context, data: []byte) {
|
update :: proc(ctx: ^Context, data: []byte) {
|
||||||
assert(ctx.is_initialized, "crypto/siphash: context is not initialized")
|
ensure(ctx.is_initialized)
|
||||||
|
|
||||||
data := data
|
data := data
|
||||||
ctx.total_length += len(data)
|
ctx.total_length += len(data)
|
||||||
@@ -269,7 +265,7 @@ update :: proc(ctx: ^Context, data: []byte) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final :: proc(ctx: ^Context, dst: ^u64) {
|
final :: proc(ctx: ^Context, dst: ^u64) {
|
||||||
assert(ctx.is_initialized, "crypto/siphash: context is not initialized")
|
ensure(ctx.is_initialized)
|
||||||
|
|
||||||
tmp: [BLOCK_SIZE]byte
|
tmp: [BLOCK_SIZE]byte
|
||||||
copy(tmp[:], ctx.buf[:ctx.last_block])
|
copy(tmp[:], ctx.buf[:ctx.last_block])
|
||||||
@@ -336,9 +332,8 @@ _get_byte :: #force_inline proc "contextless" (byte_num: byte, into: u64) -> byt
|
|||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
_collect_output :: #force_inline proc(dst: []byte, hash: u64) {
|
_collect_output :: #force_inline proc(dst: []byte, hash: u64) {
|
||||||
if len(dst) < DIGEST_SIZE {
|
ensure(len(dst) >= DIGEST_SIZE, "crypto/siphash: invalid tag size")
|
||||||
panic("crypto/siphash: invalid tag size")
|
|
||||||
}
|
|
||||||
dst[0] = _get_byte(7, hash)
|
dst[0] = _get_byte(7, hash)
|
||||||
dst[1] = _get_byte(6, hash)
|
dst[1] = _get_byte(6, hash)
|
||||||
dst[2] = _get_byte(5, hash)
|
dst[2] = _get_byte(5, hash)
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ init :: proc(ctx: ^Context) {
|
|||||||
|
|
||||||
// update adds more data to the Context.
|
// update adds more data to the Context.
|
||||||
update :: proc(ctx: ^Context, data: []byte) {
|
update :: proc(ctx: ^Context, data: []byte) {
|
||||||
assert(ctx.is_initialized)
|
ensure(ctx.is_initialized)
|
||||||
|
|
||||||
data := data
|
data := data
|
||||||
ctx.length += u64(len(data))
|
ctx.length += u64(len(data))
|
||||||
@@ -83,11 +83,8 @@ update :: proc(ctx: ^Context, data: []byte) {
|
|||||||
// Iff finalize_clone is set, final will work on a copy of the Context,
|
// Iff finalize_clone is set, final will work on a copy of the Context,
|
||||||
// which is useful for for calculating rolling digests.
|
// which is useful for for calculating rolling digests.
|
||||||
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
||||||
assert(ctx.is_initialized)
|
ensure(ctx.is_initialized)
|
||||||
|
ensure(len(hash) >= DIGEST_SIZE, "crypto/sm3: invalid destination digest size")
|
||||||
if len(hash) < DIGEST_SIZE {
|
|
||||||
panic("crypto/sm3: invalid destination digest size")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := ctx
|
ctx := ctx
|
||||||
if finalize_clone {
|
if finalize_clone {
|
||||||
@@ -110,7 +107,7 @@ final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
|||||||
length <<= 3
|
length <<= 3
|
||||||
endian.unchecked_put_u64be(pad[:], length)
|
endian.unchecked_put_u64be(pad[:], length)
|
||||||
update(ctx, pad[0:8])
|
update(ctx, pad[0:8])
|
||||||
assert(ctx.bitlength == 0)
|
assert(ctx.bitlength == 0) // Check for bugs
|
||||||
|
|
||||||
for i := 0; i < DIGEST_SIZE / 4; i += 1 {
|
for i := 0; i < DIGEST_SIZE / 4; i += 1 {
|
||||||
endian.unchecked_put_u32be(hash[i * 4:], ctx.state[i])
|
endian.unchecked_put_u32be(hash[i * 4:], ctx.state[i])
|
||||||
@@ -136,7 +133,7 @@ reset :: proc(ctx: ^Context) {
|
|||||||
SM3 implementation
|
SM3 implementation
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@(private)
|
@(private, rodata)
|
||||||
IV := [8]u32 {
|
IV := [8]u32 {
|
||||||
0x7380166f, 0x4914b2b9, 0x172442d7, 0xda8a0600,
|
0x7380166f, 0x4914b2b9, 0x172442d7, 0xda8a0600,
|
||||||
0xa96f30bc, 0x163138aa, 0xe38dee4d, 0xb0fb0e4e,
|
0xa96f30bc, 0x163138aa, 0xe38dee4d, 0xb0fb0e4e,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ SCALAR_SIZE :: 32
|
|||||||
// POINT_SIZE is the size of a X25519 point (public key/shared secret) in bytes.
|
// POINT_SIZE is the size of a X25519 point (public key/shared secret) in bytes.
|
||||||
POINT_SIZE :: 32
|
POINT_SIZE :: 32
|
||||||
|
|
||||||
@(private)
|
@(private, rodata)
|
||||||
_BASE_POINT: [32]byte = {9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
_BASE_POINT: [32]byte = {9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
@@ -101,15 +101,9 @@ _scalarmult :: proc "contextless" (out, scalar, point: ^[32]byte) {
|
|||||||
// scalarmult "multiplies" the provided scalar and point, and writes the
|
// scalarmult "multiplies" the provided scalar and point, and writes the
|
||||||
// resulting point to dst.
|
// resulting point to dst.
|
||||||
scalarmult :: proc(dst, scalar, point: []byte) {
|
scalarmult :: proc(dst, scalar, point: []byte) {
|
||||||
if len(scalar) != SCALAR_SIZE {
|
ensure(len(scalar) == SCALAR_SIZE, "crypto/x25519: invalid scalar size")
|
||||||
panic("crypto/x25519: invalid scalar size")
|
ensure(len(point) == POINT_SIZE, "crypto/x25519: invalid point size")
|
||||||
}
|
ensure(len(dst) == POINT_SIZE, "crypto/x25519: invalid destination point size")
|
||||||
if len(point) != POINT_SIZE {
|
|
||||||
panic("crypto/x25519: invalid point size")
|
|
||||||
}
|
|
||||||
if len(dst) != POINT_SIZE {
|
|
||||||
panic("crypto/x25519: invalid destination point size")
|
|
||||||
}
|
|
||||||
|
|
||||||
// "clamp" the scalar
|
// "clamp" the scalar
|
||||||
e: [32]byte = ---
|
e: [32]byte = ---
|
||||||
|
|||||||
@@ -0,0 +1,155 @@
|
|||||||
|
/*
|
||||||
|
package x448 implements the X448 (aka curve448) Elliptic-Curve
|
||||||
|
Diffie-Hellman key exchange protocol.
|
||||||
|
|
||||||
|
See:
|
||||||
|
- [[ https://www.rfc-editor.org/rfc/rfc7748 ]]
|
||||||
|
*/
|
||||||
|
package x448
|
||||||
|
|
||||||
|
import field "core:crypto/_fiat/field_curve448"
|
||||||
|
import "core:mem"
|
||||||
|
|
||||||
|
// SCALAR_SIZE is the size of a X448 scalar (private key) in bytes.
|
||||||
|
SCALAR_SIZE :: 56
|
||||||
|
// POINT_SIZE is the size of a X448 point (public key/shared secret) in bytes.
|
||||||
|
POINT_SIZE :: 56
|
||||||
|
|
||||||
|
@(private, rodata)
|
||||||
|
_BASE_POINT: [56]byte = {
|
||||||
|
5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_scalar_bit :: #force_inline proc "contextless" (s: ^[56]byte, i: int) -> u8 {
|
||||||
|
if i < 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return (s[i >> 3] >> uint(i & 7)) & 1
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_scalarmult :: proc "contextless" (out, scalar, point: ^[56]byte) {
|
||||||
|
// Montgomery pseudo-multiplication, using the RFC 7748 formula.
|
||||||
|
t1, t2: field.Loose_Field_Element = ---, ---
|
||||||
|
|
||||||
|
// x_1 = u
|
||||||
|
// x_2 = 1
|
||||||
|
// z_2 = 0
|
||||||
|
// x_3 = u
|
||||||
|
// z_3 = 1
|
||||||
|
x1: field.Tight_Field_Element = ---
|
||||||
|
field.fe_from_bytes(&x1, point)
|
||||||
|
|
||||||
|
x2, x3, z2, z3: field.Tight_Field_Element = ---, ---, ---, ---
|
||||||
|
field.fe_one(&x2)
|
||||||
|
field.fe_zero(&z2)
|
||||||
|
field.fe_set(&x3, &x1)
|
||||||
|
field.fe_one(&z3)
|
||||||
|
|
||||||
|
// swap = 0
|
||||||
|
swap: int
|
||||||
|
|
||||||
|
// For t = bits-1 down to 0:a
|
||||||
|
for t := 448 - 1; t >= 0; t -= 1 {
|
||||||
|
// k_t = (k >> t) & 1
|
||||||
|
k_t := int(_scalar_bit(scalar, t))
|
||||||
|
// swap ^= k_t
|
||||||
|
swap ~= k_t
|
||||||
|
// Conditional swap; see text below.
|
||||||
|
// (x_2, x_3) = cswap(swap, x_2, x_3)
|
||||||
|
field.fe_cond_swap(&x2, &x3, swap)
|
||||||
|
// (z_2, z_3) = cswap(swap, z_2, z_3)
|
||||||
|
field.fe_cond_swap(&z2, &z3, swap)
|
||||||
|
// swap = k_t
|
||||||
|
swap = k_t
|
||||||
|
|
||||||
|
// Note: This deliberately omits reductions after add/sub operations
|
||||||
|
// if the result is only ever used as the input to a mul/square since
|
||||||
|
// the implementations of those can deal with non-reduced inputs.
|
||||||
|
//
|
||||||
|
// fe_tighten_cast is only used to store a fully reduced
|
||||||
|
// output in a Loose_Field_Element, or to provide such a
|
||||||
|
// Loose_Field_Element as a Tight_Field_Element argument.
|
||||||
|
|
||||||
|
// A = x_2 + z_2
|
||||||
|
field.fe_add(&t1, &x2, &z2)
|
||||||
|
// B = x_2 - z_2
|
||||||
|
field.fe_sub(&t2, &x2, &z2)
|
||||||
|
// D = x_3 - z_3
|
||||||
|
field.fe_sub(field.fe_relax_cast(&z2), &x3, &z3) // (z2 unreduced)
|
||||||
|
// DA = D * A
|
||||||
|
field.fe_carry_mul(&x2, field.fe_relax_cast(&z2), &t1)
|
||||||
|
// C = x_3 + z_3
|
||||||
|
field.fe_add(field.fe_relax_cast(&z3), &x3, &z3) // (z3 unreduced)
|
||||||
|
// CB = C * B
|
||||||
|
field.fe_carry_mul(&x3, &t2, field.fe_relax_cast(&z3))
|
||||||
|
// z_3 = x_1 * (DA - CB)^2
|
||||||
|
field.fe_sub(field.fe_relax_cast(&z3), &x2, &x3) // (z3 unreduced)
|
||||||
|
field.fe_carry_square(&z3, field.fe_relax_cast(&z3))
|
||||||
|
field.fe_carry_mul(&z3, field.fe_relax_cast(&x1), field.fe_relax_cast(&z3))
|
||||||
|
// x_3 = (DA + CB)^2
|
||||||
|
field.fe_add(field.fe_relax_cast(&z2), &x2, &x3) // (z2 unreduced)
|
||||||
|
field.fe_carry_square(&x3, field.fe_relax_cast(&z2))
|
||||||
|
|
||||||
|
// AA = A^2
|
||||||
|
field.fe_carry_square(&z2, &t1)
|
||||||
|
// BB = B^2
|
||||||
|
field.fe_carry_square(field.fe_tighten_cast(&t1), &t2) // (t1 reduced)
|
||||||
|
// x_2 = AA * BB
|
||||||
|
field.fe_carry_mul(&x2, field.fe_relax_cast(&z2), &t1)
|
||||||
|
// E = AA - BB
|
||||||
|
field.fe_sub(&t2, &z2, field.fe_tighten_cast(&t1)) // (t1 (input) is reduced)
|
||||||
|
// z_2 = E * (AA + a24 * E)
|
||||||
|
field.fe_carry_mul_small(field.fe_tighten_cast(&t1), &t2, 39081) // (t1 reduced)
|
||||||
|
field.fe_add(&t1, &z2, field.fe_tighten_cast(&t1)) // (t1 (input) is reduced)
|
||||||
|
field.fe_carry_mul(&z2, &t2, &t1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conditional swap; see text below.
|
||||||
|
// (x_2, x_3) = cswap(swap, x_2, x_3)
|
||||||
|
field.fe_cond_swap(&x2, &x3, swap)
|
||||||
|
// (z_2, z_3) = cswap(swap, z_2, z_3)
|
||||||
|
field.fe_cond_swap(&z2, &z3, swap)
|
||||||
|
|
||||||
|
// Return x_2 * (z_2^(p - 2))
|
||||||
|
field.fe_carry_inv(&z2, field.fe_relax_cast(&z2))
|
||||||
|
field.fe_carry_mul(&x2, field.fe_relax_cast(&x2), field.fe_relax_cast(&z2))
|
||||||
|
field.fe_to_bytes(out, &x2)
|
||||||
|
|
||||||
|
field.fe_clear_vec([]^field.Tight_Field_Element{&x1, &x2, &x3, &z2, &z3})
|
||||||
|
field.fe_clear_vec([]^field.Loose_Field_Element{&t1, &t2})
|
||||||
|
}
|
||||||
|
|
||||||
|
// scalarmult "multiplies" the provided scalar and point, and writes the
|
||||||
|
// resulting point to dst.
|
||||||
|
scalarmult :: proc(dst, scalar, point: []byte) {
|
||||||
|
ensure(len(scalar) == SCALAR_SIZE, "crypto/x448: invalid scalar size")
|
||||||
|
ensure(len(point) == POINT_SIZE, "crypto/x448: invalid point size")
|
||||||
|
ensure(len(dst) == POINT_SIZE, "crypto/x448: invalid destination point size")
|
||||||
|
|
||||||
|
// "clamp" the scalar
|
||||||
|
e: [56]byte = ---
|
||||||
|
copy_slice(e[:], scalar)
|
||||||
|
e[0] &= 252
|
||||||
|
e[55] |= 128
|
||||||
|
|
||||||
|
p: [56]byte = ---
|
||||||
|
copy_slice(p[:], point)
|
||||||
|
|
||||||
|
d: [56]byte = ---
|
||||||
|
_scalarmult(&d, &e, &p)
|
||||||
|
copy_slice(dst, d[:])
|
||||||
|
|
||||||
|
mem.zero_explicit(&e, size_of(e))
|
||||||
|
mem.zero_explicit(&d, size_of(d))
|
||||||
|
}
|
||||||
|
|
||||||
|
// scalarmult_basepoint "multiplies" the provided scalar with the X448
|
||||||
|
// base point and writes the resulting point to dst.
|
||||||
|
scalarmult_basepoint :: proc(dst, scalar: []byte) {
|
||||||
|
scalarmult(dst, scalar, _BASE_POINT[:])
|
||||||
|
}
|
||||||
@@ -49,7 +49,9 @@ _resolve :: proc(ctx: ^Context, frame: Frame, allocator: runtime.Allocator) -> (
|
|||||||
|
|
||||||
data: [size_of(win32.SYMBOL_INFOW) + size_of([256]win32.WCHAR)]byte
|
data: [size_of(win32.SYMBOL_INFOW) + size_of([256]win32.WCHAR)]byte
|
||||||
symbol := (^win32.SYMBOL_INFOW)(&data[0])
|
symbol := (^win32.SYMBOL_INFOW)(&data[0])
|
||||||
symbol.SizeOfStruct = size_of(symbol)
|
// The value of SizeOfStruct must be the size of the whole struct,
|
||||||
|
// not just the size of the pointer
|
||||||
|
symbol.SizeOfStruct = size_of(symbol^)
|
||||||
symbol.MaxNameLen = 255
|
symbol.MaxNameLen = 255
|
||||||
if win32.SymFromAddrW(ctx.impl.hProcess, win32.DWORD64(frame), &{}, symbol) {
|
if win32.SymFromAddrW(ctx.impl.hProcess, win32.DWORD64(frame), &{}, symbol) {
|
||||||
fl.procedure, _ = win32.wstring_to_utf8(&symbol.Name[0], -1, allocator)
|
fl.procedure, _ = win32.wstring_to_utf8(&symbol.Name[0], -1, allocator)
|
||||||
|
|||||||
@@ -21,12 +21,14 @@ Symbols :: struct {
|
|||||||
main :: proc() {
|
main :: proc() {
|
||||||
sym: Symbols
|
sym: Symbols
|
||||||
|
|
||||||
|
LIB_PATH :: "lib." + dynlib.LIBRARY_FILE_EXTENSION
|
||||||
|
|
||||||
// Load symbols from `lib.dll` into Symbols struct.
|
// Load symbols from `lib.dll` into Symbols struct.
|
||||||
// Each struct field is prefixed with `foo_` before lookup in the DLL's symbol table.
|
// Each struct field is prefixed with `foo_` before lookup in the DLL's symbol table.
|
||||||
// The library's Handle (to unload) will be stored in `sym._my_lib_handle`. This way you can load multiple DLLs in one struct.
|
// The library's Handle (to unload) will be stored in `sym._my_lib_handle`. This way you can load multiple DLLs in one struct.
|
||||||
count, ok := dynlib.initialize_symbols(&sym, "lib.dll", "foo_", "_my_lib_handle")
|
count, ok := dynlib.initialize_symbols(&sym, LIB_PATH, "foo_", "_my_lib_handle")
|
||||||
defer dynlib.unload_library(sym._my_lib_handle)
|
defer dynlib.unload_library(sym._my_lib_handle)
|
||||||
fmt.printf("(Initial DLL Load) ok: %v. %v symbols loaded from lib.dll (%p).\n", ok, count, sym._my_lib_handle)
|
fmt.printf("(Initial DLL Load) ok: %v. %v symbols loaded from " + LIB_PATH + " (%p).\n", ok, count, sym._my_lib_handle)
|
||||||
|
|
||||||
if count > 0 {
|
if count > 0 {
|
||||||
fmt.println("42 + 42 =", sym.add(42, 42))
|
fmt.println("42 + 42 =", sym.add(42, 42))
|
||||||
@@ -34,8 +36,8 @@ main :: proc() {
|
|||||||
fmt.println("hellope =", sym.hellope^)
|
fmt.println("hellope =", sym.hellope^)
|
||||||
}
|
}
|
||||||
|
|
||||||
count, ok = dynlib.initialize_symbols(&sym, "lib.dll", "foo_", "_my_lib_handle")
|
count, ok = dynlib.initialize_symbols(&sym, LIB_PATH, "foo_", "_my_lib_handle")
|
||||||
fmt.printf("(DLL Reload) ok: %v. %v symbols loaded from lib.dll (%p).\n", ok, count, sym._my_lib_handle)
|
fmt.printf("(DLL Reload) ok: %v. %v symbols loaded from " + LIB_PATH + " (%p).\n", ok, count, sym._my_lib_handle)
|
||||||
|
|
||||||
if count > 0 {
|
if count > 0 {
|
||||||
fmt.println("42 + 42 =", sym.add(42, 42))
|
fmt.println("42 + 42 =", sym.add(42, 42))
|
||||||
|
|||||||
+31
-16
@@ -12,6 +12,11 @@ A handle to a dynamically loaded library.
|
|||||||
*/
|
*/
|
||||||
Library :: distinct rawptr
|
Library :: distinct rawptr
|
||||||
|
|
||||||
|
/*
|
||||||
|
The file extension for dynamic libraries on the target OS.
|
||||||
|
*/
|
||||||
|
LIBRARY_FILE_EXTENSION :: _LIBRARY_FILE_EXTENSION
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Loads a dynamic library from the filesystem. The paramater `global_symbols` makes the symbols in the loaded
|
Loads a dynamic library from the filesystem. The paramater `global_symbols` makes the symbols in the loaded
|
||||||
library available to resolve references in subsequently loaded libraries.
|
library available to resolve references in subsequently loaded libraries.
|
||||||
@@ -123,31 +128,41 @@ initialize_symbols :: proc(
|
|||||||
) -> (count: int = -1, ok: bool = false) where intrinsics.type_is_struct(T) {
|
) -> (count: int = -1, ok: bool = false) where intrinsics.type_is_struct(T) {
|
||||||
assert(symbol_table != nil)
|
assert(symbol_table != nil)
|
||||||
|
|
||||||
handle := load_library(library_path) or_return
|
// First, (re)load the library.
|
||||||
|
handle: Library
|
||||||
|
for field in reflect.struct_fields_zipped(T) {
|
||||||
|
if field.name == handle_field_name {
|
||||||
|
field_ptr := rawptr(uintptr(symbol_table) + field.offset)
|
||||||
|
|
||||||
|
// We appear to be hot reloading. Unload previous incarnation of the library.
|
||||||
|
if old_handle := (^Library)(field_ptr)^; old_handle != nil {
|
||||||
|
unload_library(old_handle) or_return
|
||||||
|
}
|
||||||
|
|
||||||
|
handle = load_library(library_path) or_return
|
||||||
|
(^Library)(field_ptr)^ = handle
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No field for it in the struct.
|
||||||
|
if handle == nil {
|
||||||
|
handle = load_library(library_path) or_return
|
||||||
|
}
|
||||||
|
|
||||||
// Buffer to concatenate the prefix + symbol name.
|
// Buffer to concatenate the prefix + symbol name.
|
||||||
prefixed_symbol_buf: [2048]u8 = ---
|
prefixed_symbol_buf: [2048]u8 = ---
|
||||||
|
|
||||||
count = 0
|
count = 0
|
||||||
for field in reflect.struct_fields_zipped(T) {
|
for field in reflect.struct_fields_zipped(T) {
|
||||||
|
// If we're not the library handle, the field needs to be a pointer type, be it a procedure pointer or an exported global.
|
||||||
|
if field.name == handle_field_name || !(reflect.is_procedure(field.type) || reflect.is_pointer(field.type)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate address of struct member
|
// Calculate address of struct member
|
||||||
field_ptr := rawptr(uintptr(symbol_table) + field.offset)
|
field_ptr := rawptr(uintptr(symbol_table) + field.offset)
|
||||||
|
|
||||||
// If we've come across the struct member for the handle, store it and continue scanning for other symbols.
|
|
||||||
if field.name == handle_field_name {
|
|
||||||
// We appear to be hot reloading. Unload previous incarnation of the library.
|
|
||||||
if old_handle := (^Library)(field_ptr)^; old_handle != nil {
|
|
||||||
unload_library(old_handle) or_return
|
|
||||||
}
|
|
||||||
(^Library)(field_ptr)^ = handle
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// We're not the library handle, so the field needs to be a pointer type, be it a procedure pointer or an exported global.
|
|
||||||
if !(reflect.is_procedure(field.type) || reflect.is_pointer(field.type)) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Let's look up or construct the symbol name to find in the library
|
// Let's look up or construct the symbol name to find in the library
|
||||||
prefixed_name: string
|
prefixed_name: string
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ package dynlib
|
|||||||
|
|
||||||
import "base:runtime"
|
import "base:runtime"
|
||||||
|
|
||||||
|
_LIBRARY_FILE_EXTENSION :: ""
|
||||||
|
|
||||||
_load_library :: proc(path: string, global_symbols: bool, allocator: runtime.Allocator) -> (Library, bool) {
|
_load_library :: proc(path: string, global_symbols: bool, allocator: runtime.Allocator) -> (Library, bool) {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user