mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-06 19:51:38 -07:00
Compare commits
274 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c20230509f | |||
| f03e0bee73 | |||
| c5cd97dd89 | |||
| c09d3e7bf4 | |||
| 46a394815c | |||
| a4b68b93f2 | |||
| e2e9b5d3b0 | |||
| b01e0fbbc2 | |||
| 4f0a3eec24 | |||
| 2a0311797c | |||
| 46e2b4e936 | |||
| 96ba8f35d4 | |||
| 9a5ddc5218 | |||
| 0b4cbcf409 | |||
| d0cd3a7415 | |||
| 3a167e3a75 | |||
| c9e21907ea | |||
| a422d0455e | |||
| 0bc3652fc7 | |||
| 672fc9fc4d | |||
| 796a0c3968 | |||
| ff36bd3d85 | |||
| 141299eb02 | |||
| efe05b3e13 | |||
| b1de429d2c | |||
| 5f51337a01 | |||
| fca7142a3c | |||
| bfa33bf5d3 | |||
| 235dae552a | |||
| a36c1ad406 | |||
| 32506a34ff | |||
| 3224d04df8 | |||
| c13c30b466 | |||
| 3de1719c17 | |||
| 0c1675c8b0 | |||
| 3d89c6b35d | |||
| 22982586f1 | |||
| 0d2c8dfeec | |||
| 305e965bcb | |||
| 906c7ef0fc | |||
| 2a7937e2ba | |||
| 2a5b8f53fe | |||
| c6e08b059b | |||
| 9a5216921c | |||
| 8a2078aa90 | |||
| 841a96691b | |||
| 8ef6f9dd7b | |||
| 5bc8a491a7 | |||
| 87952fdb8e | |||
| c474e137ac | |||
| 7b8b2f042c | |||
| 729e721b40 | |||
| e4ce017183 | |||
| bbccf9ddbf | |||
| a8425cfb47 | |||
| ef29ffeb21 | |||
| 3794d2417d | |||
| 70793236ab | |||
| 0a1ef1e59d | |||
| 44a43eae10 | |||
| 5b7f273165 | |||
| fa07e45eda | |||
| 90d587df13 | |||
| 793a6479ef | |||
| 47bef7a557 | |||
| 208ba2c116 | |||
| 42a8ac7096 | |||
| ccab715bbc | |||
| ee724f183a | |||
| c4d2aae0ed | |||
| c722665c32 | |||
| 8868bcf57a | |||
| 9e754cb0f1 | |||
| 802df73183 | |||
| 9331ebebcc | |||
| c64674c39e | |||
| 549a383cf0 | |||
| ce8ddd0c3f | |||
| 98490454b6 | |||
| bb334007cf | |||
| 9522c42460 | |||
| d165de0d4d | |||
| 5fb70c4c94 | |||
| 72cc92dc5c | |||
| de0fbb0445 | |||
| 12ebd422c6 | |||
| 7d715fe113 | |||
| 7ac156755b | |||
| 973ca6824c | |||
| 9c7956be9e | |||
| 4475454632 | |||
| f15825d2c6 | |||
| a440d8d812 | |||
| aaaddd03a6 | |||
| d62c701a43 | |||
| 79ad6f4564 | |||
| c60c7a7621 | |||
| 306bdf8869 | |||
| 37d3a8a861 | |||
| 7a29f6cff0 | |||
| c9e6862332 | |||
| b77e2f59ed | |||
| cac8582de1 | |||
| b4df51e483 | |||
| 06f4762144 | |||
| 357d085ffb | |||
| 2a50f9c8d4 | |||
| d54b708ba8 | |||
| fba20c9bd3 | |||
| 18ed444a63 | |||
| 2d6c43c260 | |||
| 71932da7e3 | |||
| a074c367ff | |||
| e6fb4ee265 | |||
| 7c336a6a82 | |||
| 71ae424aa8 | |||
| 88d6407092 | |||
| e15f245339 | |||
| eebf49ba72 | |||
| faa9df8735 | |||
| 8113182d5c | |||
| 3323c2730f | |||
| 6399f2b014 | |||
| 7218a68e89 | |||
| 7375074d2d | |||
| c9effb9b9f | |||
| ef73a284e3 | |||
| 28af376d10 | |||
| 48de1a01a9 | |||
| 209684d5a4 | |||
| 1405420935 | |||
| 48d277a3c4 | |||
| e0b9475378 | |||
| c561de33ee | |||
| d67d7168e2 | |||
| 3b3e7550f6 | |||
| 9b7a25d4a9 | |||
| bb0855b35a | |||
| e6f725dc2c | |||
| 3e4c2e4932 | |||
| 2f4f6894bd | |||
| 30c141ceb9 | |||
| d3abc1a2b4 | |||
| 465c87bd5a | |||
| cdf881a378 | |||
| e8c602b98f | |||
| d0d9a3a4f4 | |||
| 9e43072113 | |||
| 1bfbed0e02 | |||
| 0fd525d778 | |||
| 07bf64ae52 | |||
| cee45c1b15 | |||
| 68afbb37f4 | |||
| 7faca7066c | |||
| 3eaac057da | |||
| 3f1930b76e | |||
| afac1a2cc1 | |||
| 445696d660 | |||
| f454ac3150 | |||
| 82b6772ea4 | |||
| 662cbaf425 | |||
| 1556fad65a | |||
| 243e2e2b8a | |||
| 35111b39b8 | |||
| ba331024af | |||
| 4c655865e5 | |||
| 7aac8df2f2 | |||
| e98e62ec13 | |||
| eb3c1506f0 | |||
| 5bd4536372 | |||
| 7fa05238b8 | |||
| dd7449b8b5 | |||
| b7a0627d09 | |||
| c632125d82 | |||
| a509e112ea | |||
| 7c26b0abdb | |||
| f48ee00c2f | |||
| ced0ea515c | |||
| 8876328e4d | |||
| f4ff11a7ff | |||
| 77be7144c3 | |||
| 3337d38651 | |||
| fc3b0dcc80 | |||
| fc33cf6846 | |||
| 2c469a39c7 | |||
| d0001dc11d | |||
| 68719779d7 | |||
| 2ad6aa7886 | |||
| 75e3df6da2 | |||
| 753acc6971 | |||
| 0d258e8b55 | |||
| 3a60bee804 | |||
| b67e0002c1 | |||
| 0070119392 | |||
| 310fd1936b | |||
| 077bf28d26 | |||
| d97c6a7657 | |||
| 46b33854c9 | |||
| 5ee3686569 | |||
| d23144fd27 | |||
| e64eb631df | |||
| 129a62d4f1 | |||
| 8c9299c139 | |||
| 3e9b7120be | |||
| 675ba44e67 | |||
| 240b6aab13 | |||
| a1e8769cff | |||
| 73cba2cf13 | |||
| 49c761dc6d | |||
| 2a8d4c5bf5 | |||
| 2b8807eb73 | |||
| 2ef0e6b8f6 | |||
| d386563344 | |||
| 9ecc2ab15b | |||
| cb9101e0a0 | |||
| fd9adaf1de | |||
| af7008aa44 | |||
| d3a18fbe9a | |||
| b4b53aeb71 | |||
| c4b4a841d6 | |||
| 263d63aa56 | |||
| 8fcd1794a6 | |||
| 9b5ae95677 | |||
| 21c6d691d8 | |||
| 2e9eec156c | |||
| c9468adcfd | |||
| b345176bde | |||
| 879a4d49ae | |||
| 15594706c9 | |||
| 58e3f779f2 | |||
| 455d64fbd4 | |||
| 16ca677c1f | |||
| 1f1434b384 | |||
| f83370235f | |||
| 4961aff51b | |||
| 0ed3143006 | |||
| bc2b8b597c | |||
| ccc4c641c4 | |||
| 0ca8a4ad3b | |||
| 1931e3147d | |||
| 9d9ec192f1 | |||
| 586a000152 | |||
| 728f143e33 | |||
| f2e78dcc0b | |||
| a463609e4c | |||
| e05a305764 | |||
| a437c95fed | |||
| 8fe70978ff | |||
| 647fee31f8 | |||
| 8af6b6fa18 | |||
| f48f06e7b7 | |||
| 51b5a973e2 | |||
| 2bdae52fed | |||
| b3a66b3950 | |||
| a971fb6e94 | |||
| 036fa5cb24 | |||
| 81fe50d623 | |||
| b9b9bd8612 | |||
| 171a5d6476 | |||
| 00671a59a0 | |||
| ebca0398a7 | |||
| 58a405cc9f | |||
| 444fedd8d4 | |||
| f7137bf367 | |||
| 92a0ce991e | |||
| 1e7c60e171 | |||
| 2d2e3ed6a1 | |||
| 21a06df5e1 | |||
| c04efe8762 | |||
| 41071ee351 | |||
| 594049027b | |||
| b1ea291942 | |||
| 254c504465 | |||
| 9ceb6b79fd |
@@ -13,12 +13,18 @@ jobs:
|
||||
- name: Odin version
|
||||
run: ./odin version
|
||||
timeout-minutes: 1
|
||||
- name: Odin report
|
||||
run: ./odin report
|
||||
timeout-minutes: 1
|
||||
- name: Odin check
|
||||
run: ./odin check examples/demo/demo.odin -vet
|
||||
timeout-minutes: 10
|
||||
- name: Odin run
|
||||
run: ./odin run examples/demo/demo.odin
|
||||
timeout-minutes: 10
|
||||
- name: Odin run -debug
|
||||
run: ./odin run examples/demo/demo.odin -debug
|
||||
timeout-minutes: 10
|
||||
- name: Core library tests
|
||||
run: |
|
||||
cd tests/core
|
||||
@@ -39,12 +45,18 @@ jobs:
|
||||
- name: Odin version
|
||||
run: ./odin version
|
||||
timeout-minutes: 1
|
||||
- name: Odin report
|
||||
run: ./odin report
|
||||
timeout-minutes: 1
|
||||
- name: Odin check
|
||||
run: ./odin check examples/demo/demo.odin -vet
|
||||
timeout-minutes: 10
|
||||
- name: Odin run
|
||||
run: ./odin run examples/demo/demo.odin
|
||||
timeout-minutes: 10
|
||||
- name: Odin run -debug
|
||||
run: ./odin run examples/demo/demo.odin -debug
|
||||
timeout-minutes: 10
|
||||
build_windows:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
@@ -57,6 +69,9 @@ jobs:
|
||||
- name: Odin version
|
||||
run: ./odin version
|
||||
timeout-minutes: 1
|
||||
- name: Odin report
|
||||
run: ./odin report
|
||||
timeout-minutes: 1
|
||||
- name: Odin check
|
||||
shell: cmd
|
||||
run: |
|
||||
@@ -69,6 +84,12 @@ jobs:
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin run examples/demo/demo.odin
|
||||
timeout-minutes: 10
|
||||
- name: Odin run -debug
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin run examples/demo/demo.odin -debug
|
||||
timeout-minutes: 10
|
||||
- name: Core library tests
|
||||
shell: cmd
|
||||
run: |
|
||||
|
||||
@@ -93,7 +93,7 @@ jobs:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
python-version: '3.8.x'
|
||||
|
||||
- name: Install B2 CLI
|
||||
shell: bash
|
||||
@@ -127,16 +127,23 @@ jobs:
|
||||
BUCKET: ${{ secrets.B2_BUCKET }}
|
||||
DAYS_TO_KEEP: ${{ secrets.B2_DAYS_TO_KEEP }}
|
||||
run: |
|
||||
echo Authorizing B2 account
|
||||
b2 authorize-account "$APPID" "$APPKEY"
|
||||
|
||||
|
||||
echo Uploading artifcates to B2
|
||||
chmod +x ./ci/upload_create_nightly.sh
|
||||
./ci/upload_create_nightly.sh "$BUCKET" windows-amd64 windows_artifacts/
|
||||
./ci/upload_create_nightly.sh "$BUCKET" ubuntu-amd64 ubuntu_artifacts/
|
||||
./ci/upload_create_nightly.sh "$BUCKET" macos-amd64 macos_artifacts/
|
||||
|
||||
echo Deleting old artifacts in B2
|
||||
python3 ci/delete_old_binaries.py "$BUCKET" "$DAYS_TO_KEEP"
|
||||
|
||||
echo Creating nightly.json
|
||||
python3 ci/create_nightly_json.py "$BUCKET" > nightly.json
|
||||
|
||||
echo Uploading nightly.json
|
||||
b2 upload-file "$BUCKET" nightly.json nightly.json
|
||||
|
||||
echo Clear B2 account info
|
||||
b2 clear-account
|
||||
|
||||
@@ -23,6 +23,8 @@ ifeq ($(OS), Linux)
|
||||
LLVM_CONFIG=llvm-config-11
|
||||
ifneq ($(shell which llvm-config-11 2>/dev/null),)
|
||||
LLVM_CONFIG=llvm-config-11
|
||||
else ifneq ($(shell which llvm-config-11-64 2>/dev/null),)
|
||||
LLVM_CONFIG=llvm-config-11-64
|
||||
else
|
||||
ifneq ($(shell llvm-config --version | grep '^11\.'),)
|
||||
LLVM_CONFIG=llvm-config
|
||||
@@ -40,6 +42,9 @@ all: debug demo
|
||||
demo:
|
||||
./odin run examples/demo/demo.odin
|
||||
|
||||
report:
|
||||
./odin report
|
||||
|
||||
debug:
|
||||
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -g $(LDFLAGS) -o odin
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ Get live support and talk with other odiners on the Odin Discord.
|
||||
|
||||
### References
|
||||
|
||||
#### [Language Specification](https://odin-lang.org/ref/spec)
|
||||
#### [Language Specification](https://odin-lang.org/docs/spec/)
|
||||
|
||||
The official Odin Language specification.
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
package compress
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
@@ -7,10 +5,11 @@ package compress
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation, optimization.
|
||||
*/
|
||||
package compress
|
||||
|
||||
import "core:io"
|
||||
import "core:image"
|
||||
import "core:bytes"
|
||||
import "core:runtime"
|
||||
|
||||
/*
|
||||
These settings bound how much compression algorithms will allocate for their output buffer.
|
||||
@@ -51,11 +50,8 @@ Error :: union {
|
||||
ZLIB_Error,
|
||||
GZIP_Error,
|
||||
ZIP_Error,
|
||||
/*
|
||||
This is here because png.load will return a this type of error union,
|
||||
as it may involve an I/O error, a Deflate error, etc.
|
||||
*/
|
||||
image.Error,
|
||||
|
||||
runtime.Allocator_Error,
|
||||
}
|
||||
|
||||
General_Error :: enum {
|
||||
@@ -69,7 +65,6 @@ General_Error :: enum {
|
||||
Incompatible_Options,
|
||||
Unimplemented,
|
||||
|
||||
|
||||
/*
|
||||
Memory errors
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
# crypto
|
||||
A crypto library for the Odin language
|
||||
|
||||
## Supported
|
||||
This library offers various algorithms available in either native Odin or via bindings to the [Botan](https://botan.randombit.net/) crypto library.
|
||||
Please see the chart below for the options.
|
||||
**Note:** All crypto hash algorithms, offered by [Botan\'s FFI](https://botan.randombit.net/handbook/api_ref/hash.html), have been added.
|
||||
|
||||
## Hashing algorithms
|
||||
| Algorithm | Odin | Botan |
|
||||
|:-------------------------------------------------------------------------------------------------------------|:-----------------|:---------------------|
|
||||
| [BLAKE](https://web.archive.org/web/20190915215948/https://131002.net/blake) | ✔️ | |
|
||||
| [BLAKE2B](https://datatracker.ietf.org/doc/html/rfc7693) | ✔️ | ✔️ |
|
||||
| [BLAKE2S](https://datatracker.ietf.org/doc/html/rfc7693) | ✔️ | |
|
||||
| [GOST](https://datatracker.ietf.org/doc/html/rfc5831) | ✔️ | ✔️ |
|
||||
| [Grøstl](http://www.groestl.info/Groestl.zip) | ✔️ | |
|
||||
| [HAVAL](https://web.archive.org/web/20150111210116/http://labs.calyptix.com/haval.php) | ✔️ | |
|
||||
| [JH](https://www3.ntu.edu.sg/home/wuhj/research/jh/index.html) | ✔️ | |
|
||||
| [Keccak](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ | ✔️ |
|
||||
| [MD2](https://datatracker.ietf.org/doc/html/rfc1319) | ✔️ | |
|
||||
| [MD4](https://datatracker.ietf.org/doc/html/rfc1320) | ✔️ | ✔️ |
|
||||
| [MD5](https://datatracker.ietf.org/doc/html/rfc1321) | ✔️ | ✔️ |
|
||||
| [RIPEMD](https://homes.esat.kuleuven.be/~bosselae/ripemd160.html) | ✔️ | ✔️\* |
|
||||
| [SHA-1](https://datatracker.ietf.org/doc/html/rfc3174) | ✔️ | ✔️ |
|
||||
| [SHA-2](https://csrc.nist.gov/csrc/media/publications/fips/180/2/archive/2002-08-01/documents/fips180-2.pdf) | ✔️ | ✔️ |
|
||||
| [SHA-3](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ | ✔️ |
|
||||
| [SHAKE](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ | ✔️ |
|
||||
| [Skein](https://www.schneier.com/academic/skein/) | | ✔️\*\* |
|
||||
| [SM3](https://datatracker.ietf.org/doc/html/draft-sca-cfrg-sm3-02) | ✔️ | ✔️ |
|
||||
| [Streebog](https://datatracker.ietf.org/doc/html/rfc6986) | ✔️ | ✔️ |
|
||||
| [Tiger](https://www.cs.technion.ac.il/~biham/Reports/Tiger/) | ✔️ | ✔️ |
|
||||
| [Tiger2](https://www.cs.technion.ac.il/~biham/Reports/Tiger/) | ✔️ | |
|
||||
| [Whirlpool](https://web.archive.org/web/20171129084214/http://www.larc.usp.br/~pbarreto/WhirlpoolPage.html) | ✔️ | ✔️ |
|
||||
|
||||
\* Only `RIPEMD-160`
|
||||
\*\* Only `SKEIN-512`
|
||||
|
||||
#### High level API
|
||||
Each hash algorithm contains a procedure group named `hash`, or if the algorithm provides more than one digest size `hash_<size>`\*\*\*.
|
||||
Included in these groups are four procedures.
|
||||
* `hash_string` - Hash a given string and return the computed hash. Just calls `hash_bytes` internally
|
||||
* `hash_bytes` - Hash a given byte slice and return the computed hash
|
||||
* `hash_stream` - Takes a stream from io.Stream and returns the computed hash from it
|
||||
* `hash_file` - Takes a file handle and returns the computed hash from it. A second optional boolean parameter controls if the file is streamed (this is the default) or read at once (set to true)
|
||||
|
||||
\*\*\* On some algorithms there is another part to the name, since they might offer control about additional parameters.
|
||||
For instance, `HAVAL` offers different sizes as well as three different round amounts.
|
||||
Computing a 256-bit hash with 3 rounds is therefore achieved by calling `haval.hash_256_3(...)`.
|
||||
|
||||
#### Low level API
|
||||
The above mentioned procedures internally call three procedures: `init`, `update` and `final`.
|
||||
You may also directly call them, if you wish.
|
||||
|
||||
#### Context system
|
||||
The library uses a context system internally to be able to switch between Odin / Botan implementations freely.
|
||||
When an Odin implementation is available, it is the default.
|
||||
You may change what is used during runtime by calling `foo.use_botan()` or `foo.use_odin()`.
|
||||
It is also possible to set this during compile time via `USE_BOTAN_LIB=true`.
|
||||
Internally a vtable is used to set the appropriate procedures when switching. This works for all the procedures mentioned in the APIs above.
|
||||
|
||||
#### Example
|
||||
```odin
|
||||
package crypto_example
|
||||
|
||||
// Import the desired package
|
||||
import "core:crypto/md4"
|
||||
|
||||
main :: proc() {
|
||||
input := "foo"
|
||||
// Compute the hash via Odin implementation
|
||||
computed_hash := md4.hash(input)
|
||||
// Switch to Botan
|
||||
md4.use_botan()
|
||||
// Compute the hash via Botan bindings
|
||||
computed_hash_botan := md4.hash(input)
|
||||
}
|
||||
```
|
||||
For example uses of all available algorithms, please see the tests within `tests/core/crypto`.
|
||||
|
||||
#### Thread safety
|
||||
The crypto package is not thread-safe at the moment. This may change in the future.
|
||||
|
||||
### Disclaimer
|
||||
The algorithms were ported out of curiosity and due to interest in the field.
|
||||
We have not had any of the code verified by a third party or tested/fuzzed by any automatic means.
|
||||
Whereever we were able to find official test vectors, those were used to verify the implementation.
|
||||
We do not recommend using them in a production environment, without any additional testing and/or verification.
|
||||
|
||||
### ToDo
|
||||
* Ciphers (Symmetric, Asymmetric)
|
||||
* MACs (Message Authentication Code)
|
||||
* CSPRNGs (Cryptographically Secure PseudoRandom Number Generator)
|
||||
* KDFs (Key Derivation Function)
|
||||
* KEAs (Key Exchange Algorithm)
|
||||
|
||||
### License
|
||||
This library is made available under the BSD-3 license.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,79 @@
|
||||
package _ctx
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog: Initial creation and testing of the bindings.
|
||||
|
||||
Implementation of the context, used internally by the crypto library.
|
||||
*/
|
||||
|
||||
import "core:io"
|
||||
import "core:os"
|
||||
|
||||
Hash_Size :: enum {
|
||||
_16,
|
||||
_20,
|
||||
_24,
|
||||
_28,
|
||||
_32,
|
||||
_40,
|
||||
_48,
|
||||
_64,
|
||||
_128,
|
||||
}
|
||||
|
||||
Hash_Context :: struct {
|
||||
botan_hash_algo: cstring,
|
||||
external_ctx: any,
|
||||
internal_ctx: any,
|
||||
hash_size: Hash_Size,
|
||||
hash_size_val: int,
|
||||
is_using_odin: bool,
|
||||
using vtbl: ^Hash_Context_Vtable,
|
||||
}
|
||||
|
||||
Hash_Context_Vtable :: struct {
|
||||
hash_bytes_16 : proc (ctx: ^Hash_Context, input: []byte) -> [16]byte,
|
||||
hash_bytes_20 : proc (ctx: ^Hash_Context, input: []byte) -> [20]byte,
|
||||
hash_bytes_24 : proc (ctx: ^Hash_Context, input: []byte) -> [24]byte,
|
||||
hash_bytes_28 : proc (ctx: ^Hash_Context, input: []byte) -> [28]byte,
|
||||
hash_bytes_32 : proc (ctx: ^Hash_Context, input: []byte) -> [32]byte,
|
||||
hash_bytes_40 : proc (ctx: ^Hash_Context, input: []byte) -> [40]byte,
|
||||
hash_bytes_48 : proc (ctx: ^Hash_Context, input: []byte) -> [48]byte,
|
||||
hash_bytes_64 : proc (ctx: ^Hash_Context, input: []byte) -> [64]byte,
|
||||
hash_bytes_128 : proc (ctx: ^Hash_Context, input: []byte) -> [128]byte,
|
||||
hash_file_16 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool),
|
||||
hash_file_20 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([20]byte, bool),
|
||||
hash_file_24 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([24]byte, bool),
|
||||
hash_file_28 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool),
|
||||
hash_file_32 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool),
|
||||
hash_file_40 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([40]byte, bool),
|
||||
hash_file_48 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([48]byte, bool),
|
||||
hash_file_64 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool),
|
||||
hash_file_128 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([128]byte, bool),
|
||||
hash_stream_16 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([16]byte, bool),
|
||||
hash_stream_20 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([20]byte, bool),
|
||||
hash_stream_24 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([24]byte, bool),
|
||||
hash_stream_28 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([28]byte, bool),
|
||||
hash_stream_32 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([32]byte, bool),
|
||||
hash_stream_40 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([40]byte, bool),
|
||||
hash_stream_48 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([48]byte, bool),
|
||||
hash_stream_64 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([64]byte, bool),
|
||||
hash_stream_128 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([128]byte, bool),
|
||||
hash_bytes_slice : proc (ctx: ^Hash_Context, input: []byte, out_size: int, allocator := context.allocator) -> []byte,
|
||||
hash_file_slice : proc (ctx: ^Hash_Context, hd: os.Handle, out_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool),
|
||||
hash_stream_slice : proc (ctx: ^Hash_Context, s: io.Stream, out_size: int, allocator := context.allocator) -> ([]byte, bool),
|
||||
init : proc (ctx: ^Hash_Context),
|
||||
update : proc (ctx: ^Hash_Context, data: []byte),
|
||||
final : proc (ctx: ^Hash_Context, hash: []byte),
|
||||
}
|
||||
|
||||
_init_vtable :: #force_inline proc() -> ^Hash_Context {
|
||||
ctx := new(Hash_Context)
|
||||
vtbl := new(Hash_Context_Vtable)
|
||||
ctx.vtbl = vtbl
|
||||
return ctx
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
package _sha3
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the Keccak hashing algorithm, standardized as SHA3 in <https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf>
|
||||
To use the original Keccak padding, set the is_keccak bool to true, otherwise it will use SHA3 padding.
|
||||
*/
|
||||
|
||||
import "../util"
|
||||
|
||||
ROUNDS :: 24
|
||||
|
||||
Sha3_Context :: struct {
|
||||
st: struct #raw_union {
|
||||
b: [200]u8,
|
||||
q: [25]u64,
|
||||
},
|
||||
pt: int,
|
||||
rsiz: int,
|
||||
mdlen: int,
|
||||
is_keccak: bool,
|
||||
}
|
||||
|
||||
keccakf :: proc "contextless" (st: ^[25]u64) {
|
||||
keccakf_rndc := [?]u64 {
|
||||
0x0000000000000001, 0x0000000000008082, 0x800000000000808a,
|
||||
0x8000000080008000, 0x000000000000808b, 0x0000000080000001,
|
||||
0x8000000080008081, 0x8000000000008009, 0x000000000000008a,
|
||||
0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
|
||||
0x000000008000808b, 0x800000000000008b, 0x8000000000008089,
|
||||
0x8000000000008003, 0x8000000000008002, 0x8000000000000080,
|
||||
0x000000000000800a, 0x800000008000000a, 0x8000000080008081,
|
||||
0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
|
||||
}
|
||||
|
||||
keccakf_rotc := [?]i32 {
|
||||
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14,
|
||||
27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
|
||||
}
|
||||
|
||||
keccakf_piln := [?]i32 {
|
||||
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4,
|
||||
15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
|
||||
}
|
||||
|
||||
i, j, r: i32 = ---, ---, ---
|
||||
t: u64 = ---
|
||||
bc: [5]u64 = ---
|
||||
|
||||
when ODIN_ENDIAN != "little" {
|
||||
v: uintptr = ---
|
||||
for i = 0; i < 25; i += 1 {
|
||||
v := uintptr(&st[i])
|
||||
st[i] = u64((^u8)(v + 0)^ << 0) | u64((^u8)(v + 1)^ << 8) |
|
||||
u64((^u8)(v + 2)^ << 16) | u64((^u8)(v + 3)^ << 24) |
|
||||
u64((^u8)(v + 4)^ << 32) | u64((^u8)(v + 5)^ << 40) |
|
||||
u64((^u8)(v + 6)^ << 48) | u64((^u8)(v + 7)^ << 56)
|
||||
}
|
||||
}
|
||||
|
||||
for r = 0; r < ROUNDS; r += 1 {
|
||||
// theta
|
||||
for i = 0; i < 5; i += 1 {
|
||||
bc[i] = st[i] ~ st[i + 5] ~ st[i + 10] ~ st[i + 15] ~ st[i + 20]
|
||||
}
|
||||
|
||||
for i = 0; i < 5; i += 1 {
|
||||
t = bc[(i + 4) % 5] ~ util.ROTL64(bc[(i + 1) % 5], 1)
|
||||
for j = 0; j < 25; j += 5 {
|
||||
st[j + i] ~= t
|
||||
}
|
||||
}
|
||||
|
||||
// rho pi
|
||||
t = st[1]
|
||||
for i = 0; i < 24; i += 1 {
|
||||
j = keccakf_piln[i]
|
||||
bc[0] = st[j]
|
||||
st[j] = util.ROTL64(t, u64(keccakf_rotc[i]))
|
||||
t = bc[0]
|
||||
}
|
||||
|
||||
// chi
|
||||
for j = 0; j < 25; j += 5 {
|
||||
for i = 0; i < 5; i += 1 {
|
||||
bc[i] = st[j + i]
|
||||
}
|
||||
for i = 0; i < 5; i += 1 {
|
||||
st[j + i] ~= ~bc[(i + 1) % 5] & bc[(i + 2) % 5]
|
||||
}
|
||||
}
|
||||
|
||||
st[0] ~= keccakf_rndc[r]
|
||||
}
|
||||
|
||||
when ODIN_ENDIAN != "little" {
|
||||
for i = 0; i < 25; i += 1 {
|
||||
v = uintptr(&st[i])
|
||||
t = st[i]
|
||||
(^u8)(v + 0)^ = (t >> 0) & 0xff
|
||||
(^u8)(v + 1)^ = (t >> 8) & 0xff
|
||||
(^u8)(v + 2)^ = (t >> 16) & 0xff
|
||||
(^u8)(v + 3)^ = (t >> 24) & 0xff
|
||||
(^u8)(v + 4)^ = (t >> 32) & 0xff
|
||||
(^u8)(v + 5)^ = (t >> 40) & 0xff
|
||||
(^u8)(v + 6)^ = (t >> 48) & 0xff
|
||||
(^u8)(v + 7)^ = (t >> 56) & 0xff
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init_odin :: proc "contextless" (c: ^Sha3_Context) {
|
||||
for i := 0; i < 25; i += 1 {
|
||||
c.st.q[i] = 0
|
||||
}
|
||||
c.rsiz = 200 - 2 * c.mdlen
|
||||
}
|
||||
|
||||
update_odin :: proc "contextless" (c: ^Sha3_Context, data: []byte) {
|
||||
j := c.pt
|
||||
for i := 0; i < len(data); i += 1 {
|
||||
c.st.b[j] ~= data[i]
|
||||
j += 1
|
||||
if j >= c.rsiz {
|
||||
keccakf(&c.st.q)
|
||||
j = 0
|
||||
}
|
||||
}
|
||||
c.pt = j
|
||||
}
|
||||
|
||||
final_odin :: proc "contextless" (c: ^Sha3_Context, hash: []byte) {
|
||||
if c.is_keccak {
|
||||
c.st.b[c.pt] ~= 0x01
|
||||
} else {
|
||||
c.st.b[c.pt] ~= 0x06
|
||||
}
|
||||
|
||||
c.st.b[c.rsiz - 1] ~= 0x80
|
||||
keccakf(&c.st.q)
|
||||
for i := 0; i < c.mdlen; i += 1 {
|
||||
hash[i] = c.st.b[i]
|
||||
}
|
||||
}
|
||||
|
||||
shake_xof_odin :: proc "contextless" (c: ^Sha3_Context) {
|
||||
c.st.b[c.pt] ~= 0x1F
|
||||
c.st.b[c.rsiz - 1] ~= 0x80
|
||||
keccakf(&c.st.q)
|
||||
c.pt = 0
|
||||
}
|
||||
|
||||
shake_out_odin :: proc "contextless" (c: ^Sha3_Context, hash: []byte) {
|
||||
j := c.pt
|
||||
for i := 0; i < len(hash); i += 1 {
|
||||
if j >= c.rsiz {
|
||||
keccakf(&c.st.q)
|
||||
j = 0
|
||||
}
|
||||
hash[i] = c.st.b[j]
|
||||
j += 1
|
||||
}
|
||||
c.pt = j
|
||||
}
|
||||
@@ -0,0 +1,411 @@
|
||||
package _tiger
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the Tiger hashing algorithm, as defined in <https://www.cs.technion.ac.il/~biham/Reports/Tiger/>
|
||||
*/
|
||||
|
||||
import "../util"
|
||||
|
||||
T1 := [?]u64 {
|
||||
0x02aab17cf7e90c5e, 0xac424b03e243a8ec, 0x72cd5be30dd5fcd3, 0x6d019b93f6f97f3a,
|
||||
0xcd9978ffd21f9193, 0x7573a1c9708029e2, 0xb164326b922a83c3, 0x46883eee04915870,
|
||||
0xeaace3057103ece6, 0xc54169b808a3535c, 0x4ce754918ddec47c, 0x0aa2f4dfdc0df40c,
|
||||
0x10b76f18a74dbefa, 0xc6ccb6235ad1ab6a, 0x13726121572fe2ff, 0x1a488c6f199d921e,
|
||||
0x4bc9f9f4da0007ca, 0x26f5e6f6e85241c7, 0x859079dbea5947b6, 0x4f1885c5c99e8c92,
|
||||
0xd78e761ea96f864b, 0x8e36428c52b5c17d, 0x69cf6827373063c1, 0xb607c93d9bb4c56e,
|
||||
0x7d820e760e76b5ea, 0x645c9cc6f07fdc42, 0xbf38a078243342e0, 0x5f6b343c9d2e7d04,
|
||||
0xf2c28aeb600b0ec6, 0x6c0ed85f7254bcac, 0x71592281a4db4fe5, 0x1967fa69ce0fed9f,
|
||||
0xfd5293f8b96545db, 0xc879e9d7f2a7600b, 0x860248920193194e, 0xa4f9533b2d9cc0b3,
|
||||
0x9053836c15957613, 0xdb6dcf8afc357bf1, 0x18beea7a7a370f57, 0x037117ca50b99066,
|
||||
0x6ab30a9774424a35, 0xf4e92f02e325249b, 0x7739db07061ccae1, 0xd8f3b49ceca42a05,
|
||||
0xbd56be3f51382f73, 0x45faed5843b0bb28, 0x1c813d5c11bf1f83, 0x8af0e4b6d75fa169,
|
||||
0x33ee18a487ad9999, 0x3c26e8eab1c94410, 0xb510102bc0a822f9, 0x141eef310ce6123b,
|
||||
0xfc65b90059ddb154, 0xe0158640c5e0e607, 0x884e079826c3a3cf, 0x930d0d9523c535fd,
|
||||
0x35638d754e9a2b00, 0x4085fccf40469dd5, 0xc4b17ad28be23a4c, 0xcab2f0fc6a3e6a2e,
|
||||
0x2860971a6b943fcd, 0x3dde6ee212e30446, 0x6222f32ae01765ae, 0x5d550bb5478308fe,
|
||||
0xa9efa98da0eda22a, 0xc351a71686c40da7, 0x1105586d9c867c84, 0xdcffee85fda22853,
|
||||
0xccfbd0262c5eef76, 0xbaf294cb8990d201, 0xe69464f52afad975, 0x94b013afdf133e14,
|
||||
0x06a7d1a32823c958, 0x6f95fe5130f61119, 0xd92ab34e462c06c0, 0xed7bde33887c71d2,
|
||||
0x79746d6e6518393e, 0x5ba419385d713329, 0x7c1ba6b948a97564, 0x31987c197bfdac67,
|
||||
0xde6c23c44b053d02, 0x581c49fed002d64d, 0xdd474d6338261571, 0xaa4546c3e473d062,
|
||||
0x928fce349455f860, 0x48161bbacaab94d9, 0x63912430770e6f68, 0x6ec8a5e602c6641c,
|
||||
0x87282515337ddd2b, 0x2cda6b42034b701b, 0xb03d37c181cb096d, 0xe108438266c71c6f,
|
||||
0x2b3180c7eb51b255, 0xdf92b82f96c08bbc, 0x5c68c8c0a632f3ba, 0x5504cc861c3d0556,
|
||||
0xabbfa4e55fb26b8f, 0x41848b0ab3baceb4, 0xb334a273aa445d32, 0xbca696f0a85ad881,
|
||||
0x24f6ec65b528d56c, 0x0ce1512e90f4524a, 0x4e9dd79d5506d35a, 0x258905fac6ce9779,
|
||||
0x2019295b3e109b33, 0xf8a9478b73a054cc, 0x2924f2f934417eb0, 0x3993357d536d1bc4,
|
||||
0x38a81ac21db6ff8b, 0x47c4fbf17d6016bf, 0x1e0faadd7667e3f5, 0x7abcff62938beb96,
|
||||
0xa78dad948fc179c9, 0x8f1f98b72911e50d, 0x61e48eae27121a91, 0x4d62f7ad31859808,
|
||||
0xeceba345ef5ceaeb, 0xf5ceb25ebc9684ce, 0xf633e20cb7f76221, 0xa32cdf06ab8293e4,
|
||||
0x985a202ca5ee2ca4, 0xcf0b8447cc8a8fb1, 0x9f765244979859a3, 0xa8d516b1a1240017,
|
||||
0x0bd7ba3ebb5dc726, 0xe54bca55b86adb39, 0x1d7a3afd6c478063, 0x519ec608e7669edd,
|
||||
0x0e5715a2d149aa23, 0x177d4571848ff194, 0xeeb55f3241014c22, 0x0f5e5ca13a6e2ec2,
|
||||
0x8029927b75f5c361, 0xad139fabc3d6e436, 0x0d5df1a94ccf402f, 0x3e8bd948bea5dfc8,
|
||||
0xa5a0d357bd3ff77e, 0xa2d12e251f74f645, 0x66fd9e525e81a082, 0x2e0c90ce7f687a49,
|
||||
0xc2e8bcbeba973bc5, 0x000001bce509745f, 0x423777bbe6dab3d6, 0xd1661c7eaef06eb5,
|
||||
0xa1781f354daacfd8, 0x2d11284a2b16affc, 0xf1fc4f67fa891d1f, 0x73ecc25dcb920ada,
|
||||
0xae610c22c2a12651, 0x96e0a810d356b78a, 0x5a9a381f2fe7870f, 0xd5ad62ede94e5530,
|
||||
0xd225e5e8368d1427, 0x65977b70c7af4631, 0x99f889b2de39d74f, 0x233f30bf54e1d143,
|
||||
0x9a9675d3d9a63c97, 0x5470554ff334f9a8, 0x166acb744a4f5688, 0x70c74caab2e4aead,
|
||||
0xf0d091646f294d12, 0x57b82a89684031d1, 0xefd95a5a61be0b6b, 0x2fbd12e969f2f29a,
|
||||
0x9bd37013feff9fe8, 0x3f9b0404d6085a06, 0x4940c1f3166cfe15, 0x09542c4dcdf3defb,
|
||||
0xb4c5218385cd5ce3, 0xc935b7dc4462a641, 0x3417f8a68ed3b63f, 0xb80959295b215b40,
|
||||
0xf99cdaef3b8c8572, 0x018c0614f8fcb95d, 0x1b14accd1a3acdf3, 0x84d471f200bb732d,
|
||||
0xc1a3110e95e8da16, 0x430a7220bf1a82b8, 0xb77e090d39df210e, 0x5ef4bd9f3cd05e9d,
|
||||
0x9d4ff6da7e57a444, 0xda1d60e183d4a5f8, 0xb287c38417998e47, 0xfe3edc121bb31886,
|
||||
0xc7fe3ccc980ccbef, 0xe46fb590189bfd03, 0x3732fd469a4c57dc, 0x7ef700a07cf1ad65,
|
||||
0x59c64468a31d8859, 0x762fb0b4d45b61f6, 0x155baed099047718, 0x68755e4c3d50baa6,
|
||||
0xe9214e7f22d8b4df, 0x2addbf532eac95f4, 0x32ae3909b4bd0109, 0x834df537b08e3450,
|
||||
0xfa209da84220728d, 0x9e691d9b9efe23f7, 0x0446d288c4ae8d7f, 0x7b4cc524e169785b,
|
||||
0x21d87f0135ca1385, 0xcebb400f137b8aa5, 0x272e2b66580796be, 0x3612264125c2b0de,
|
||||
0x057702bdad1efbb2, 0xd4babb8eacf84be9, 0x91583139641bc67b, 0x8bdc2de08036e024,
|
||||
0x603c8156f49f68ed, 0xf7d236f7dbef5111, 0x9727c4598ad21e80, 0xa08a0896670a5fd7,
|
||||
0xcb4a8f4309eba9cb, 0x81af564b0f7036a1, 0xc0b99aa778199abd, 0x959f1ec83fc8e952,
|
||||
0x8c505077794a81b9, 0x3acaaf8f056338f0, 0x07b43f50627a6778, 0x4a44ab49f5eccc77,
|
||||
0x3bc3d6e4b679ee98, 0x9cc0d4d1cf14108c, 0x4406c00b206bc8a0, 0x82a18854c8d72d89,
|
||||
0x67e366b35c3c432c, 0xb923dd61102b37f2, 0x56ab2779d884271d, 0xbe83e1b0ff1525af,
|
||||
0xfb7c65d4217e49a9, 0x6bdbe0e76d48e7d4, 0x08df828745d9179e, 0x22ea6a9add53bd34,
|
||||
0xe36e141c5622200a, 0x7f805d1b8cb750ee, 0xafe5c7a59f58e837, 0xe27f996a4fb1c23c,
|
||||
0xd3867dfb0775f0d0, 0xd0e673de6e88891a, 0x123aeb9eafb86c25, 0x30f1d5d5c145b895,
|
||||
0xbb434a2dee7269e7, 0x78cb67ecf931fa38, 0xf33b0372323bbf9c, 0x52d66336fb279c74,
|
||||
0x505f33ac0afb4eaa, 0xe8a5cd99a2cce187, 0x534974801e2d30bb, 0x8d2d5711d5876d90,
|
||||
0x1f1a412891bc038e, 0xd6e2e71d82e56648, 0x74036c3a497732b7, 0x89b67ed96361f5ab,
|
||||
0xffed95d8f1ea02a2, 0xe72b3bd61464d43d, 0xa6300f170bdc4820, 0xebc18760ed78a77a,
|
||||
}
|
||||
|
||||
T2 := [?]u64 {
|
||||
0xe6a6be5a05a12138, 0xb5a122a5b4f87c98, 0x563c6089140b6990, 0x4c46cb2e391f5dd5,
|
||||
0xd932addbc9b79434, 0x08ea70e42015aff5, 0xd765a6673e478cf1, 0xc4fb757eab278d99,
|
||||
0xdf11c6862d6e0692, 0xddeb84f10d7f3b16, 0x6f2ef604a665ea04, 0x4a8e0f0ff0e0dfb3,
|
||||
0xa5edeef83dbcba51, 0xfc4f0a2a0ea4371e, 0xe83e1da85cb38429, 0xdc8ff882ba1b1ce2,
|
||||
0xcd45505e8353e80d, 0x18d19a00d4db0717, 0x34a0cfeda5f38101, 0x0be77e518887caf2,
|
||||
0x1e341438b3c45136, 0xe05797f49089ccf9, 0xffd23f9df2591d14, 0x543dda228595c5cd,
|
||||
0x661f81fd99052a33, 0x8736e641db0f7b76, 0x15227725418e5307, 0xe25f7f46162eb2fa,
|
||||
0x48a8b2126c13d9fe, 0xafdc541792e76eea, 0x03d912bfc6d1898f, 0x31b1aafa1b83f51b,
|
||||
0xf1ac2796e42ab7d9, 0x40a3a7d7fcd2ebac, 0x1056136d0afbbcc5, 0x7889e1dd9a6d0c85,
|
||||
0xd33525782a7974aa, 0xa7e25d09078ac09b, 0xbd4138b3eac6edd0, 0x920abfbe71eb9e70,
|
||||
0xa2a5d0f54fc2625c, 0xc054e36b0b1290a3, 0xf6dd59ff62fe932b, 0x3537354511a8ac7d,
|
||||
0xca845e9172fadcd4, 0x84f82b60329d20dc, 0x79c62ce1cd672f18, 0x8b09a2add124642c,
|
||||
0xd0c1e96a19d9e726, 0x5a786a9b4ba9500c, 0x0e020336634c43f3, 0xc17b474aeb66d822,
|
||||
0x6a731ae3ec9baac2, 0x8226667ae0840258, 0x67d4567691caeca5, 0x1d94155c4875adb5,
|
||||
0x6d00fd985b813fdf, 0x51286efcb774cd06, 0x5e8834471fa744af, 0xf72ca0aee761ae2e,
|
||||
0xbe40e4cdaee8e09a, 0xe9970bbb5118f665, 0x726e4beb33df1964, 0x703b000729199762,
|
||||
0x4631d816f5ef30a7, 0xb880b5b51504a6be, 0x641793c37ed84b6c, 0x7b21ed77f6e97d96,
|
||||
0x776306312ef96b73, 0xae528948e86ff3f4, 0x53dbd7f286a3f8f8, 0x16cadce74cfc1063,
|
||||
0x005c19bdfa52c6dd, 0x68868f5d64d46ad3, 0x3a9d512ccf1e186a, 0x367e62c2385660ae,
|
||||
0xe359e7ea77dcb1d7, 0x526c0773749abe6e, 0x735ae5f9d09f734b, 0x493fc7cc8a558ba8,
|
||||
0xb0b9c1533041ab45, 0x321958ba470a59bd, 0x852db00b5f46c393, 0x91209b2bd336b0e5,
|
||||
0x6e604f7d659ef19f, 0xb99a8ae2782ccb24, 0xccf52ab6c814c4c7, 0x4727d9afbe11727b,
|
||||
0x7e950d0c0121b34d, 0x756f435670ad471f, 0xf5add442615a6849, 0x4e87e09980b9957a,
|
||||
0x2acfa1df50aee355, 0xd898263afd2fd556, 0xc8f4924dd80c8fd6, 0xcf99ca3d754a173a,
|
||||
0xfe477bacaf91bf3c, 0xed5371f6d690c12d, 0x831a5c285e687094, 0xc5d3c90a3708a0a4,
|
||||
0x0f7f903717d06580, 0x19f9bb13b8fdf27f, 0xb1bd6f1b4d502843, 0x1c761ba38fff4012,
|
||||
0x0d1530c4e2e21f3b, 0x8943ce69a7372c8a, 0xe5184e11feb5ce66, 0x618bdb80bd736621,
|
||||
0x7d29bad68b574d0b, 0x81bb613e25e6fe5b, 0x071c9c10bc07913f, 0xc7beeb7909ac2d97,
|
||||
0xc3e58d353bc5d757, 0xeb017892f38f61e8, 0xd4effb9c9b1cc21a, 0x99727d26f494f7ab,
|
||||
0xa3e063a2956b3e03, 0x9d4a8b9a4aa09c30, 0x3f6ab7d500090fb4, 0x9cc0f2a057268ac0,
|
||||
0x3dee9d2dedbf42d1, 0x330f49c87960a972, 0xc6b2720287421b41, 0x0ac59ec07c00369c,
|
||||
0xef4eac49cb353425, 0xf450244eef0129d8, 0x8acc46e5caf4deb6, 0x2ffeab63989263f7,
|
||||
0x8f7cb9fe5d7a4578, 0x5bd8f7644e634635, 0x427a7315bf2dc900, 0x17d0c4aa2125261c,
|
||||
0x3992486c93518e50, 0xb4cbfee0a2d7d4c3, 0x7c75d6202c5ddd8d, 0xdbc295d8e35b6c61,
|
||||
0x60b369d302032b19, 0xce42685fdce44132, 0x06f3ddb9ddf65610, 0x8ea4d21db5e148f0,
|
||||
0x20b0fce62fcd496f, 0x2c1b912358b0ee31, 0xb28317b818f5a308, 0xa89c1e189ca6d2cf,
|
||||
0x0c6b18576aaadbc8, 0xb65deaa91299fae3, 0xfb2b794b7f1027e7, 0x04e4317f443b5beb,
|
||||
0x4b852d325939d0a6, 0xd5ae6beefb207ffc, 0x309682b281c7d374, 0xbae309a194c3b475,
|
||||
0x8cc3f97b13b49f05, 0x98a9422ff8293967, 0x244b16b01076ff7c, 0xf8bf571c663d67ee,
|
||||
0x1f0d6758eee30da1, 0xc9b611d97adeb9b7, 0xb7afd5887b6c57a2, 0x6290ae846b984fe1,
|
||||
0x94df4cdeacc1a5fd, 0x058a5bd1c5483aff, 0x63166cc142ba3c37, 0x8db8526eb2f76f40,
|
||||
0xe10880036f0d6d4e, 0x9e0523c9971d311d, 0x45ec2824cc7cd691, 0x575b8359e62382c9,
|
||||
0xfa9e400dc4889995, 0xd1823ecb45721568, 0xdafd983b8206082f, 0xaa7d29082386a8cb,
|
||||
0x269fcd4403b87588, 0x1b91f5f728bdd1e0, 0xe4669f39040201f6, 0x7a1d7c218cf04ade,
|
||||
0x65623c29d79ce5ce, 0x2368449096c00bb1, 0xab9bf1879da503ba, 0xbc23ecb1a458058e,
|
||||
0x9a58df01bb401ecc, 0xa070e868a85f143d, 0x4ff188307df2239e, 0x14d565b41a641183,
|
||||
0xee13337452701602, 0x950e3dcf3f285e09, 0x59930254b9c80953, 0x3bf299408930da6d,
|
||||
0xa955943f53691387, 0xa15edecaa9cb8784, 0x29142127352be9a0, 0x76f0371fff4e7afb,
|
||||
0x0239f450274f2228, 0xbb073af01d5e868b, 0xbfc80571c10e96c1, 0xd267088568222e23,
|
||||
0x9671a3d48e80b5b0, 0x55b5d38ae193bb81, 0x693ae2d0a18b04b8, 0x5c48b4ecadd5335f,
|
||||
0xfd743b194916a1ca, 0x2577018134be98c4, 0xe77987e83c54a4ad, 0x28e11014da33e1b9,
|
||||
0x270cc59e226aa213, 0x71495f756d1a5f60, 0x9be853fb60afef77, 0xadc786a7f7443dbf,
|
||||
0x0904456173b29a82, 0x58bc7a66c232bd5e, 0xf306558c673ac8b2, 0x41f639c6b6c9772a,
|
||||
0x216defe99fda35da, 0x11640cc71c7be615, 0x93c43694565c5527, 0xea038e6246777839,
|
||||
0xf9abf3ce5a3e2469, 0x741e768d0fd312d2, 0x0144b883ced652c6, 0xc20b5a5ba33f8552,
|
||||
0x1ae69633c3435a9d, 0x97a28ca4088cfdec, 0x8824a43c1e96f420, 0x37612fa66eeea746,
|
||||
0x6b4cb165f9cf0e5a, 0x43aa1c06a0abfb4a, 0x7f4dc26ff162796b, 0x6cbacc8e54ed9b0f,
|
||||
0xa6b7ffefd2bb253e, 0x2e25bc95b0a29d4f, 0x86d6a58bdef1388c, 0xded74ac576b6f054,
|
||||
0x8030bdbc2b45805d, 0x3c81af70e94d9289, 0x3eff6dda9e3100db, 0xb38dc39fdfcc8847,
|
||||
0x123885528d17b87e, 0xf2da0ed240b1b642, 0x44cefadcd54bf9a9, 0x1312200e433c7ee6,
|
||||
0x9ffcc84f3a78c748, 0xf0cd1f72248576bb, 0xec6974053638cfe4, 0x2ba7b67c0cec4e4c,
|
||||
0xac2f4df3e5ce32ed, 0xcb33d14326ea4c11, 0xa4e9044cc77e58bc, 0x5f513293d934fcef,
|
||||
0x5dc9645506e55444, 0x50de418f317de40a, 0x388cb31a69dde259, 0x2db4a83455820a86,
|
||||
0x9010a91e84711ae9, 0x4df7f0b7b1498371, 0xd62a2eabc0977179, 0x22fac097aa8d5c0e,
|
||||
}
|
||||
|
||||
T3 := [?]u64 {
|
||||
0xf49fcc2ff1daf39b, 0x487fd5c66ff29281, 0xe8a30667fcdca83f, 0x2c9b4be3d2fcce63,
|
||||
0xda3ff74b93fbbbc2, 0x2fa165d2fe70ba66, 0xa103e279970e93d4, 0xbecdec77b0e45e71,
|
||||
0xcfb41e723985e497, 0xb70aaa025ef75017, 0xd42309f03840b8e0, 0x8efc1ad035898579,
|
||||
0x96c6920be2b2abc5, 0x66af4163375a9172, 0x2174abdcca7127fb, 0xb33ccea64a72ff41,
|
||||
0xf04a4933083066a5, 0x8d970acdd7289af5, 0x8f96e8e031c8c25e, 0xf3fec02276875d47,
|
||||
0xec7bf310056190dd, 0xf5adb0aebb0f1491, 0x9b50f8850fd58892, 0x4975488358b74de8,
|
||||
0xa3354ff691531c61, 0x0702bbe481d2c6ee, 0x89fb24057deded98, 0xac3075138596e902,
|
||||
0x1d2d3580172772ed, 0xeb738fc28e6bc30d, 0x5854ef8f63044326, 0x9e5c52325add3bbe,
|
||||
0x90aa53cf325c4623, 0xc1d24d51349dd067, 0x2051cfeea69ea624, 0x13220f0a862e7e4f,
|
||||
0xce39399404e04864, 0xd9c42ca47086fcb7, 0x685ad2238a03e7cc, 0x066484b2ab2ff1db,
|
||||
0xfe9d5d70efbf79ec, 0x5b13b9dd9c481854, 0x15f0d475ed1509ad, 0x0bebcd060ec79851,
|
||||
0xd58c6791183ab7f8, 0xd1187c5052f3eee4, 0xc95d1192e54e82ff, 0x86eea14cb9ac6ca2,
|
||||
0x3485beb153677d5d, 0xdd191d781f8c492a, 0xf60866baa784ebf9, 0x518f643ba2d08c74,
|
||||
0x8852e956e1087c22, 0xa768cb8dc410ae8d, 0x38047726bfec8e1a, 0xa67738b4cd3b45aa,
|
||||
0xad16691cec0dde19, 0xc6d4319380462e07, 0xc5a5876d0ba61938, 0x16b9fa1fa58fd840,
|
||||
0x188ab1173ca74f18, 0xabda2f98c99c021f, 0x3e0580ab134ae816, 0x5f3b05b773645abb,
|
||||
0x2501a2be5575f2f6, 0x1b2f74004e7e8ba9, 0x1cd7580371e8d953, 0x7f6ed89562764e30,
|
||||
0xb15926ff596f003d, 0x9f65293da8c5d6b9, 0x6ecef04dd690f84c, 0x4782275fff33af88,
|
||||
0xe41433083f820801, 0xfd0dfe409a1af9b5, 0x4325a3342cdb396b, 0x8ae77e62b301b252,
|
||||
0xc36f9e9f6655615a, 0x85455a2d92d32c09, 0xf2c7dea949477485, 0x63cfb4c133a39eba,
|
||||
0x83b040cc6ebc5462, 0x3b9454c8fdb326b0, 0x56f56a9e87ffd78c, 0x2dc2940d99f42bc6,
|
||||
0x98f7df096b096e2d, 0x19a6e01e3ad852bf, 0x42a99ccbdbd4b40b, 0xa59998af45e9c559,
|
||||
0x366295e807d93186, 0x6b48181bfaa1f773, 0x1fec57e2157a0a1d, 0x4667446af6201ad5,
|
||||
0xe615ebcacfb0f075, 0xb8f31f4f68290778, 0x22713ed6ce22d11e, 0x3057c1a72ec3c93b,
|
||||
0xcb46acc37c3f1f2f, 0xdbb893fd02aaf50e, 0x331fd92e600b9fcf, 0xa498f96148ea3ad6,
|
||||
0xa8d8426e8b6a83ea, 0xa089b274b7735cdc, 0x87f6b3731e524a11, 0x118808e5cbc96749,
|
||||
0x9906e4c7b19bd394, 0xafed7f7e9b24a20c, 0x6509eadeeb3644a7, 0x6c1ef1d3e8ef0ede,
|
||||
0xb9c97d43e9798fb4, 0xa2f2d784740c28a3, 0x7b8496476197566f, 0x7a5be3e6b65f069d,
|
||||
0xf96330ed78be6f10, 0xeee60de77a076a15, 0x2b4bee4aa08b9bd0, 0x6a56a63ec7b8894e,
|
||||
0x02121359ba34fef4, 0x4cbf99f8283703fc, 0x398071350caf30c8, 0xd0a77a89f017687a,
|
||||
0xf1c1a9eb9e423569, 0x8c7976282dee8199, 0x5d1737a5dd1f7abd, 0x4f53433c09a9fa80,
|
||||
0xfa8b0c53df7ca1d9, 0x3fd9dcbc886ccb77, 0xc040917ca91b4720, 0x7dd00142f9d1dcdf,
|
||||
0x8476fc1d4f387b58, 0x23f8e7c5f3316503, 0x032a2244e7e37339, 0x5c87a5d750f5a74b,
|
||||
0x082b4cc43698992e, 0xdf917becb858f63c, 0x3270b8fc5bf86dda, 0x10ae72bb29b5dd76,
|
||||
0x576ac94e7700362b, 0x1ad112dac61efb8f, 0x691bc30ec5faa427, 0xff246311cc327143,
|
||||
0x3142368e30e53206, 0x71380e31e02ca396, 0x958d5c960aad76f1, 0xf8d6f430c16da536,
|
||||
0xc8ffd13f1be7e1d2, 0x7578ae66004ddbe1, 0x05833f01067be646, 0xbb34b5ad3bfe586d,
|
||||
0x095f34c9a12b97f0, 0x247ab64525d60ca8, 0xdcdbc6f3017477d1, 0x4a2e14d4decad24d,
|
||||
0xbdb5e6d9be0a1eeb, 0x2a7e70f7794301ab, 0xdef42d8a270540fd, 0x01078ec0a34c22c1,
|
||||
0xe5de511af4c16387, 0x7ebb3a52bd9a330a, 0x77697857aa7d6435, 0x004e831603ae4c32,
|
||||
0xe7a21020ad78e312, 0x9d41a70c6ab420f2, 0x28e06c18ea1141e6, 0xd2b28cbd984f6b28,
|
||||
0x26b75f6c446e9d83, 0xba47568c4d418d7f, 0xd80badbfe6183d8e, 0x0e206d7f5f166044,
|
||||
0xe258a43911cbca3e, 0x723a1746b21dc0bc, 0xc7caa854f5d7cdd3, 0x7cac32883d261d9c,
|
||||
0x7690c26423ba942c, 0x17e55524478042b8, 0xe0be477656a2389f, 0x4d289b5e67ab2da0,
|
||||
0x44862b9c8fbbfd31, 0xb47cc8049d141365, 0x822c1b362b91c793, 0x4eb14655fb13dfd8,
|
||||
0x1ecbba0714e2a97b, 0x6143459d5cde5f14, 0x53a8fbf1d5f0ac89, 0x97ea04d81c5e5b00,
|
||||
0x622181a8d4fdb3f3, 0xe9bcd341572a1208, 0x1411258643cce58a, 0x9144c5fea4c6e0a4,
|
||||
0x0d33d06565cf620f, 0x54a48d489f219ca1, 0xc43e5eac6d63c821, 0xa9728b3a72770daf,
|
||||
0xd7934e7b20df87ef, 0xe35503b61a3e86e5, 0xcae321fbc819d504, 0x129a50b3ac60bfa6,
|
||||
0xcd5e68ea7e9fb6c3, 0xb01c90199483b1c7, 0x3de93cd5c295376c, 0xaed52edf2ab9ad13,
|
||||
0x2e60f512c0a07884, 0xbc3d86a3e36210c9, 0x35269d9b163951ce, 0x0c7d6e2ad0cdb5fa,
|
||||
0x59e86297d87f5733, 0x298ef221898db0e7, 0x55000029d1a5aa7e, 0x8bc08ae1b5061b45,
|
||||
0xc2c31c2b6c92703a, 0x94cc596baf25ef42, 0x0a1d73db22540456, 0x04b6a0f9d9c4179a,
|
||||
0xeffdafa2ae3d3c60, 0xf7c8075bb49496c4, 0x9cc5c7141d1cd4e3, 0x78bd1638218e5534,
|
||||
0xb2f11568f850246a, 0xedfabcfa9502bc29, 0x796ce5f2da23051b, 0xaae128b0dc93537c,
|
||||
0x3a493da0ee4b29ae, 0xb5df6b2c416895d7, 0xfcabbd25122d7f37, 0x70810b58105dc4b1,
|
||||
0xe10fdd37f7882a90, 0x524dcab5518a3f5c, 0x3c9e85878451255b, 0x4029828119bd34e2,
|
||||
0x74a05b6f5d3ceccb, 0xb610021542e13eca, 0x0ff979d12f59e2ac, 0x6037da27e4f9cc50,
|
||||
0x5e92975a0df1847d, 0xd66de190d3e623fe, 0x5032d6b87b568048, 0x9a36b7ce8235216e,
|
||||
0x80272a7a24f64b4a, 0x93efed8b8c6916f7, 0x37ddbff44cce1555, 0x4b95db5d4b99bd25,
|
||||
0x92d3fda169812fc0, 0xfb1a4a9a90660bb6, 0x730c196946a4b9b2, 0x81e289aa7f49da68,
|
||||
0x64669a0f83b1a05f, 0x27b3ff7d9644f48b, 0xcc6b615c8db675b3, 0x674f20b9bcebbe95,
|
||||
0x6f31238275655982, 0x5ae488713e45cf05, 0xbf619f9954c21157, 0xeabac46040a8eae9,
|
||||
0x454c6fe9f2c0c1cd, 0x419cf6496412691c, 0xd3dc3bef265b0f70, 0x6d0e60f5c3578a9e,
|
||||
}
|
||||
|
||||
T4 := [?]u64 {
|
||||
0x5b0e608526323c55, 0x1a46c1a9fa1b59f5, 0xa9e245a17c4c8ffa, 0x65ca5159db2955d7,
|
||||
0x05db0a76ce35afc2, 0x81eac77ea9113d45, 0x528ef88ab6ac0a0d, 0xa09ea253597be3ff,
|
||||
0x430ddfb3ac48cd56, 0xc4b3a67af45ce46f, 0x4ececfd8fbe2d05e, 0x3ef56f10b39935f0,
|
||||
0x0b22d6829cd619c6, 0x17fd460a74df2069, 0x6cf8cc8e8510ed40, 0xd6c824bf3a6ecaa7,
|
||||
0x61243d581a817049, 0x048bacb6bbc163a2, 0xd9a38ac27d44cc32, 0x7fddff5baaf410ab,
|
||||
0xad6d495aa804824b, 0xe1a6a74f2d8c9f94, 0xd4f7851235dee8e3, 0xfd4b7f886540d893,
|
||||
0x247c20042aa4bfda, 0x096ea1c517d1327c, 0xd56966b4361a6685, 0x277da5c31221057d,
|
||||
0x94d59893a43acff7, 0x64f0c51ccdc02281, 0x3d33bcc4ff6189db, 0xe005cb184ce66af1,
|
||||
0xff5ccd1d1db99bea, 0xb0b854a7fe42980f, 0x7bd46a6a718d4b9f, 0xd10fa8cc22a5fd8c,
|
||||
0xd31484952be4bd31, 0xc7fa975fcb243847, 0x4886ed1e5846c407, 0x28cddb791eb70b04,
|
||||
0xc2b00be2f573417f, 0x5c9590452180f877, 0x7a6bddfff370eb00, 0xce509e38d6d9d6a4,
|
||||
0xebeb0f00647fa702, 0x1dcc06cf76606f06, 0xe4d9f28ba286ff0a, 0xd85a305dc918c262,
|
||||
0x475b1d8732225f54, 0x2d4fb51668ccb5fe, 0xa679b9d9d72bba20, 0x53841c0d912d43a5,
|
||||
0x3b7eaa48bf12a4e8, 0x781e0e47f22f1ddf, 0xeff20ce60ab50973, 0x20d261d19dffb742,
|
||||
0x16a12b03062a2e39, 0x1960eb2239650495, 0x251c16fed50eb8b8, 0x9ac0c330f826016e,
|
||||
0xed152665953e7671, 0x02d63194a6369570, 0x5074f08394b1c987, 0x70ba598c90b25ce1,
|
||||
0x794a15810b9742f6, 0x0d5925e9fcaf8c6c, 0x3067716cd868744e, 0x910ab077e8d7731b,
|
||||
0x6a61bbdb5ac42f61, 0x93513efbf0851567, 0xf494724b9e83e9d5, 0xe887e1985c09648d,
|
||||
0x34b1d3c675370cfd, 0xdc35e433bc0d255d, 0xd0aab84234131be0, 0x08042a50b48b7eaf,
|
||||
0x9997c4ee44a3ab35, 0x829a7b49201799d0, 0x263b8307b7c54441, 0x752f95f4fd6a6ca6,
|
||||
0x927217402c08c6e5, 0x2a8ab754a795d9ee, 0xa442f7552f72943d, 0x2c31334e19781208,
|
||||
0x4fa98d7ceaee6291, 0x55c3862f665db309, 0xbd0610175d53b1f3, 0x46fe6cb840413f27,
|
||||
0x3fe03792df0cfa59, 0xcfe700372eb85e8f, 0xa7be29e7adbce118, 0xe544ee5cde8431dd,
|
||||
0x8a781b1b41f1873e, 0xa5c94c78a0d2f0e7, 0x39412e2877b60728, 0xa1265ef3afc9a62c,
|
||||
0xbcc2770c6a2506c5, 0x3ab66dd5dce1ce12, 0xe65499d04a675b37, 0x7d8f523481bfd216,
|
||||
0x0f6f64fcec15f389, 0x74efbe618b5b13c8, 0xacdc82b714273e1d, 0xdd40bfe003199d17,
|
||||
0x37e99257e7e061f8, 0xfa52626904775aaa, 0x8bbbf63a463d56f9, 0xf0013f1543a26e64,
|
||||
0xa8307e9f879ec898, 0xcc4c27a4150177cc, 0x1b432f2cca1d3348, 0xde1d1f8f9f6fa013,
|
||||
0x606602a047a7ddd6, 0xd237ab64cc1cb2c7, 0x9b938e7225fcd1d3, 0xec4e03708e0ff476,
|
||||
0xfeb2fbda3d03c12d, 0xae0bced2ee43889a, 0x22cb8923ebfb4f43, 0x69360d013cf7396d,
|
||||
0x855e3602d2d4e022, 0x073805bad01f784c, 0x33e17a133852f546, 0xdf4874058ac7b638,
|
||||
0xba92b29c678aa14a, 0x0ce89fc76cfaadcd, 0x5f9d4e0908339e34, 0xf1afe9291f5923b9,
|
||||
0x6e3480f60f4a265f, 0xeebf3a2ab29b841c, 0xe21938a88f91b4ad, 0x57dfeff845c6d3c3,
|
||||
0x2f006b0bf62caaf2, 0x62f479ef6f75ee78, 0x11a55ad41c8916a9, 0xf229d29084fed453,
|
||||
0x42f1c27b16b000e6, 0x2b1f76749823c074, 0x4b76eca3c2745360, 0x8c98f463b91691bd,
|
||||
0x14bcc93cf1ade66a, 0x8885213e6d458397, 0x8e177df0274d4711, 0xb49b73b5503f2951,
|
||||
0x10168168c3f96b6b, 0x0e3d963b63cab0ae, 0x8dfc4b5655a1db14, 0xf789f1356e14de5c,
|
||||
0x683e68af4e51dac1, 0xc9a84f9d8d4b0fd9, 0x3691e03f52a0f9d1, 0x5ed86e46e1878e80,
|
||||
0x3c711a0e99d07150, 0x5a0865b20c4e9310, 0x56fbfc1fe4f0682e, 0xea8d5de3105edf9b,
|
||||
0x71abfdb12379187a, 0x2eb99de1bee77b9c, 0x21ecc0ea33cf4523, 0x59a4d7521805c7a1,
|
||||
0x3896f5eb56ae7c72, 0xaa638f3db18f75dc, 0x9f39358dabe9808e, 0xb7defa91c00b72ac,
|
||||
0x6b5541fd62492d92, 0x6dc6dee8f92e4d5b, 0x353f57abc4beea7e, 0x735769d6da5690ce,
|
||||
0x0a234aa642391484, 0xf6f9508028f80d9d, 0xb8e319a27ab3f215, 0x31ad9c1151341a4d,
|
||||
0x773c22a57bef5805, 0x45c7561a07968633, 0xf913da9e249dbe36, 0xda652d9b78a64c68,
|
||||
0x4c27a97f3bc334ef, 0x76621220e66b17f4, 0x967743899acd7d0b, 0xf3ee5bcae0ed6782,
|
||||
0x409f753600c879fc, 0x06d09a39b5926db6, 0x6f83aeb0317ac588, 0x01e6ca4a86381f21,
|
||||
0x66ff3462d19f3025, 0x72207c24ddfd3bfb, 0x4af6b6d3e2ece2eb, 0x9c994dbec7ea08de,
|
||||
0x49ace597b09a8bc4, 0xb38c4766cf0797ba, 0x131b9373c57c2a75, 0xb1822cce61931e58,
|
||||
0x9d7555b909ba1c0c, 0x127fafdd937d11d2, 0x29da3badc66d92e4, 0xa2c1d57154c2ecbc,
|
||||
0x58c5134d82f6fe24, 0x1c3ae3515b62274f, 0xe907c82e01cb8126, 0xf8ed091913e37fcb,
|
||||
0x3249d8f9c80046c9, 0x80cf9bede388fb63, 0x1881539a116cf19e, 0x5103f3f76bd52457,
|
||||
0x15b7e6f5ae47f7a8, 0xdbd7c6ded47e9ccf, 0x44e55c410228bb1a, 0xb647d4255edb4e99,
|
||||
0x5d11882bb8aafc30, 0xf5098bbb29d3212a, 0x8fb5ea14e90296b3, 0x677b942157dd025a,
|
||||
0xfb58e7c0a390acb5, 0x89d3674c83bd4a01, 0x9e2da4df4bf3b93b, 0xfcc41e328cab4829,
|
||||
0x03f38c96ba582c52, 0xcad1bdbd7fd85db2, 0xbbb442c16082ae83, 0xb95fe86ba5da9ab0,
|
||||
0xb22e04673771a93f, 0x845358c9493152d8, 0xbe2a488697b4541e, 0x95a2dc2dd38e6966,
|
||||
0xc02c11ac923c852b, 0x2388b1990df2a87b, 0x7c8008fa1b4f37be, 0x1f70d0c84d54e503,
|
||||
0x5490adec7ece57d4, 0x002b3c27d9063a3a, 0x7eaea3848030a2bf, 0xc602326ded2003c0,
|
||||
0x83a7287d69a94086, 0xc57a5fcb30f57a8a, 0xb56844e479ebe779, 0xa373b40f05dcbce9,
|
||||
0xd71a786e88570ee2, 0x879cbacdbde8f6a0, 0x976ad1bcc164a32f, 0xab21e25e9666d78b,
|
||||
0x901063aae5e5c33c, 0x9818b34448698d90, 0xe36487ae3e1e8abb, 0xafbdf931893bdcb4,
|
||||
0x6345a0dc5fbbd519, 0x8628fe269b9465ca, 0x1e5d01603f9c51ec, 0x4de44006a15049b7,
|
||||
0xbf6c70e5f776cbb1, 0x411218f2ef552bed, 0xcb0c0708705a36a3, 0xe74d14754f986044,
|
||||
0xcd56d9430ea8280e, 0xc12591d7535f5065, 0xc83223f1720aef96, 0xc3a0396f7363a51f,
|
||||
}
|
||||
|
||||
Tiger_Context :: struct {
|
||||
a: u64,
|
||||
b: u64,
|
||||
c: u64,
|
||||
x: [64]byte,
|
||||
nx: int,
|
||||
length: u64,
|
||||
ver: int,
|
||||
}
|
||||
|
||||
round :: #force_inline proc "contextless"(a, b, c, x, mul: u64) -> (u64, u64, u64) {
|
||||
a, b, c := a, b, c
|
||||
c ~= x
|
||||
a -= T1[c & 0xff] ~ T2[(c >> 16) & 0xff] ~ T3[(c >> 32) & 0xff] ~ T4[(c >> 48) & 0xff]
|
||||
b += T4[(c >> 8) & 0xff] ~ T3[(c >> 24) & 0xff] ~ T2[(c >> 40) & 0xff] ~ T1[(c >> 56) & 0xff]
|
||||
b *= mul
|
||||
return a, b, c
|
||||
}
|
||||
|
||||
pass :: #force_inline proc "contextless"(a, b, c: u64, d: []u64, mul: u64) -> (x, y, z: u64) {
|
||||
x, y, z = round(a, b, c, d[0], mul)
|
||||
y, z, x = round(y, z, x, d[1], mul)
|
||||
z, x, y = round(z, x, y, d[2], mul)
|
||||
x, y, z = round(x, y, z, d[3], mul)
|
||||
y, z, x = round(y, z, x, d[4], mul)
|
||||
z, x, y = round(z, x, y, d[5], mul)
|
||||
x, y, z = round(x, y, z, d[6], mul)
|
||||
y, z, x = round(y, z, x, d[7], mul)
|
||||
return
|
||||
}
|
||||
|
||||
key_schedule :: #force_inline proc "contextless"(x: []u64) {
|
||||
x[0] -= x[7] ~ 0xa5a5a5a5a5a5a5a5
|
||||
x[1] ~= x[0]
|
||||
x[2] += x[1]
|
||||
x[3] -= x[2] ~ ((~x[1]) << 19)
|
||||
x[4] ~= x[3]
|
||||
x[5] += x[4]
|
||||
x[6] -= x[5] ~ ((~x[4]) >> 23)
|
||||
x[7] ~= x[6]
|
||||
x[0] += x[7]
|
||||
x[1] -= x[0] ~ ((~x[7]) << 19)
|
||||
x[2] ~= x[1]
|
||||
x[3] += x[2]
|
||||
x[4] -= x[3] ~ ((~x[2]) >> 23)
|
||||
x[5] ~= x[4]
|
||||
x[6] += x[5]
|
||||
x[7] -= x[6] ~ 0x0123456789abcdef
|
||||
}
|
||||
|
||||
compress :: #force_inline proc "contextless"(ctx: ^Tiger_Context, data: []byte) {
|
||||
a := ctx.a
|
||||
b := ctx.b
|
||||
c := ctx.c
|
||||
x := util.cast_slice([]u64, data)
|
||||
ctx.a, ctx.b, ctx.c = pass(ctx.a, ctx.b, ctx.c, x, 5)
|
||||
key_schedule(x)
|
||||
ctx.c, ctx.a, ctx.b = pass(ctx.c, ctx.a, ctx.b, x, 7)
|
||||
key_schedule(x)
|
||||
ctx.b, ctx.c, ctx.a = pass(ctx.b, ctx.c, ctx.a, x, 9)
|
||||
ctx.a ~= a
|
||||
ctx.b -= b
|
||||
ctx.c += c
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^Tiger_Context) {
|
||||
ctx.a = 0x0123456789abcdef
|
||||
ctx.b = 0xfedcba9876543210
|
||||
ctx.c = 0xf096a5b4c3b2e187
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^Tiger_Context, input: []byte) {
|
||||
p := make([]byte, len(input))
|
||||
copy(p, input)
|
||||
|
||||
length := len(p)
|
||||
ctx.length += u64(length)
|
||||
if ctx.nx > 0 {
|
||||
n := len(p)
|
||||
if n > 64 - ctx.nx {
|
||||
n = 64 - ctx.nx
|
||||
}
|
||||
copy(ctx.x[ctx.nx:ctx.nx + n], p[:n])
|
||||
ctx.nx += n
|
||||
if ctx.nx == 64 {
|
||||
compress(ctx, ctx.x[:64 - 1])
|
||||
ctx.nx = 0
|
||||
}
|
||||
p = p[n:]
|
||||
}
|
||||
for len(p) >= 64 {
|
||||
compress(ctx, p[:64])
|
||||
p = p[64:]
|
||||
}
|
||||
if len(p) > 0 {
|
||||
ctx.nx = copy(ctx.x[:], p)
|
||||
}
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^Tiger_Context, hash: []byte) {
|
||||
length := ctx.length
|
||||
tmp: [64]byte
|
||||
if ctx.ver == 1 {
|
||||
tmp[0] = 0x01
|
||||
} else {
|
||||
tmp[0] = 0x80
|
||||
}
|
||||
|
||||
size := length & 0x3f
|
||||
if size < 56 {
|
||||
update_odin(ctx, tmp[:56 - size])
|
||||
} else {
|
||||
update_odin(ctx, tmp[:64 + 56 - size])
|
||||
}
|
||||
|
||||
length <<= 3
|
||||
for i := uint(0); i < 8; i += 1 {
|
||||
tmp[i] = byte(length >> (8 * i))
|
||||
}
|
||||
update_odin(ctx, tmp[:8])
|
||||
|
||||
for i := uint(0); i < 8; i += 1 {
|
||||
tmp[i] = byte(ctx.a >> (8 * i))
|
||||
tmp[i + 8] = byte(ctx.b >> (8 * i))
|
||||
tmp[i + 16] = byte(ctx.c >> (8 * i))
|
||||
}
|
||||
copy(hash[:], tmp[:len(hash)])
|
||||
}
|
||||
@@ -0,0 +1,834 @@
|
||||
package blake
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the BLAKE hashing algorithm, as defined in <https://web.archive.org/web/20190915215948/https://131002.net/blake>
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_28 = hash_bytes_odin_28
|
||||
ctx.hash_file_28 = hash_file_odin_28
|
||||
ctx.hash_stream_28 = hash_stream_odin_28
|
||||
ctx.hash_bytes_32 = hash_bytes_odin_32
|
||||
ctx.hash_file_32 = hash_file_odin_32
|
||||
ctx.hash_stream_32 = hash_stream_odin_32
|
||||
ctx.hash_bytes_48 = hash_bytes_odin_48
|
||||
ctx.hash_file_48 = hash_file_odin_48
|
||||
ctx.hash_stream_48 = hash_stream_odin_48
|
||||
ctx.hash_bytes_64 = hash_bytes_odin_64
|
||||
ctx.hash_file_64 = hash_file_odin_64
|
||||
ctx.hash_stream_64 = hash_stream_odin_64
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan does nothing, since BLAKE is not available in Botan
|
||||
@(warning="BLAKE is not provided by the Botan API. Odin implementation will be used")
|
||||
use_botan :: #force_inline proc() {
|
||||
use_odin()
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_blake256_ctx :: #force_inline proc(is224: bool, size: _ctx.Hash_Size) {
|
||||
ctx: Blake256_Context
|
||||
ctx.is224 = is224
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = size
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_blake512_ctx :: #force_inline proc(is384: bool, size: _ctx.Hash_Size) {
|
||||
ctx: Blake512_Context
|
||||
ctx.is384 = is384
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = size
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// hash_string_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_224 :: proc(data: string) -> [28]byte {
|
||||
return hash_bytes_224(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_224 :: proc(data: []byte) -> [28]byte {
|
||||
_create_blake256_ctx(true, ._28)
|
||||
return _hash_impl->hash_bytes_28(data)
|
||||
}
|
||||
|
||||
// hash_stream_224 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
|
||||
_create_blake256_ctx(true, ._28)
|
||||
return _hash_impl->hash_stream_28(s)
|
||||
}
|
||||
|
||||
// hash_file_224 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
|
||||
_create_blake256_ctx(true, ._28)
|
||||
return _hash_impl->hash_file_28(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_224 :: proc {
|
||||
hash_stream_224,
|
||||
hash_file_224,
|
||||
hash_bytes_224,
|
||||
hash_string_224,
|
||||
}
|
||||
|
||||
// hash_string_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_256 :: proc(data: string) -> [32]byte {
|
||||
return hash_bytes_256(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_256 :: proc(data: []byte) -> [32]byte {
|
||||
_create_blake256_ctx(false, ._32)
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
}
|
||||
|
||||
// hash_stream_256 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
|
||||
_create_blake256_ctx(false, ._32)
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
}
|
||||
|
||||
// hash_file_256 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
_create_blake256_ctx(false, ._32)
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_256 :: proc {
|
||||
hash_stream_256,
|
||||
hash_file_256,
|
||||
hash_bytes_256,
|
||||
hash_string_256,
|
||||
}
|
||||
|
||||
// hash_string_384 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_384 :: proc(data: string) -> [48]byte {
|
||||
return hash_bytes_384(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_384 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_384 :: proc(data: []byte) -> [48]byte {
|
||||
_create_blake512_ctx(true, ._48)
|
||||
return _hash_impl->hash_bytes_48(data)
|
||||
}
|
||||
|
||||
// hash_stream_384 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
|
||||
_create_blake512_ctx(true, ._48)
|
||||
return _hash_impl->hash_stream_48(s)
|
||||
}
|
||||
|
||||
// hash_file_384 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
|
||||
_create_blake512_ctx(true, ._48)
|
||||
return _hash_impl->hash_file_48(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_384 :: proc {
|
||||
hash_stream_384,
|
||||
hash_file_384,
|
||||
hash_bytes_384,
|
||||
hash_string_384,
|
||||
}
|
||||
|
||||
// hash_string_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_512 :: proc(data: string) -> [64]byte {
|
||||
return hash_bytes_512(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_512 :: proc(data: []byte) -> [64]byte {
|
||||
_create_blake512_ctx(false, ._64)
|
||||
return _hash_impl->hash_bytes_64(data)
|
||||
}
|
||||
|
||||
// hash_stream_512 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
|
||||
_create_blake512_ctx(false, ._64)
|
||||
return _hash_impl->hash_stream_64(s)
|
||||
}
|
||||
|
||||
// hash_file_512 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
_create_blake512_ctx(false, ._64)
|
||||
return _hash_impl->hash_file_64(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_512 :: proc {
|
||||
hash_stream_512,
|
||||
hash_file_512,
|
||||
hash_bytes_512,
|
||||
hash_string_512,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte {
|
||||
hash: [28]byte
|
||||
if c, ok := ctx.internal_ctx.(Blake256_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) {
|
||||
hash: [28]byte
|
||||
if c, ok := ctx.internal_ctx.(Blake256_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_28(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_28(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [28]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Blake256_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Blake256_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_32(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [32]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte {
|
||||
hash: [48]byte
|
||||
if c, ok := ctx.internal_ctx.(Blake512_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([48]byte, bool) {
|
||||
hash: [48]byte
|
||||
if c, ok := ctx.internal_ctx.(Blake512_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_48(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_48(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [48]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(Blake512_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(Blake512_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_64(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_64(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [64]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
if ctx.hash_size == ._28 || ctx.hash_size == ._32 {
|
||||
_create_blake256_ctx(ctx.hash_size == ._28, ctx.hash_size)
|
||||
if c, ok := ctx.internal_ctx.(Blake256_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
return
|
||||
}
|
||||
if ctx.hash_size == ._48 || ctx.hash_size == ._64 {
|
||||
_create_blake512_ctx(ctx.hash_size == ._48, ctx.hash_size)
|
||||
if c, ok := ctx.internal_ctx.(Blake512_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
#partial switch ctx.hash_size {
|
||||
case ._28, ._32:
|
||||
if c, ok := ctx.internal_ctx.(Blake256_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
case ._48, ._64:
|
||||
if c, ok := ctx.internal_ctx.(Blake512_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
#partial switch ctx.hash_size {
|
||||
case ._28, ._32:
|
||||
if c, ok := ctx.internal_ctx.(Blake256_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
case ._48, ._64:
|
||||
if c, ok := ctx.internal_ctx.(Blake512_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
BLAKE implementation
|
||||
*/
|
||||
|
||||
SIZE_224 :: 28
|
||||
SIZE_256 :: 32
|
||||
SIZE_384 :: 48
|
||||
SIZE_512 :: 64
|
||||
BLOCKSIZE_256 :: 64
|
||||
BLOCKSIZE_512 :: 128
|
||||
|
||||
Blake256_Context :: struct {
|
||||
h: [8]u32,
|
||||
s: [4]u32,
|
||||
t: u64,
|
||||
x: [64]byte,
|
||||
nx: int,
|
||||
is224: bool,
|
||||
nullt: bool,
|
||||
}
|
||||
|
||||
Blake512_Context :: struct {
|
||||
h: [8]u64,
|
||||
s: [4]u64,
|
||||
t: u64,
|
||||
x: [128]byte,
|
||||
nx: int,
|
||||
is384: bool,
|
||||
nullt: bool,
|
||||
}
|
||||
|
||||
SIGMA := [?]int {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3,
|
||||
11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4,
|
||||
7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8,
|
||||
9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13,
|
||||
2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9,
|
||||
12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11,
|
||||
13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10,
|
||||
6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5,
|
||||
10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0,
|
||||
}
|
||||
|
||||
U256 := [16]u32 {
|
||||
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
|
||||
0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
|
||||
0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
|
||||
0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
|
||||
}
|
||||
|
||||
U512 := [16]u64 {
|
||||
0x243f6a8885a308d3, 0x13198a2e03707344, 0xa4093822299f31d0, 0x082efa98ec4e6c89,
|
||||
0x452821e638d01377, 0xbe5466cf34e90c6c, 0xc0ac29b7c97c50dd, 0x3f84d5b5b5470917,
|
||||
0x9216d5d98979fb1b, 0xd1310ba698dfb5ac, 0x2ffd72dbd01adfb7, 0xb8e1afed6a267e96,
|
||||
0xba7c9045f12c7f99, 0x24a19947b3916cf7, 0x0801f2e2858efc16, 0x636920d871574e69,
|
||||
}
|
||||
|
||||
G256 :: #force_inline proc "contextless" (a, b, c, d: u32, m: [16]u32, i, j: int) -> (u32, u32, u32, u32) {
|
||||
a, b, c, d := a, b, c, d
|
||||
a += m[SIGMA[(i % 10) * 16 + (2 * j)]] ~ U256[SIGMA[(i % 10) * 16 + (2 * j + 1)]]
|
||||
a += b
|
||||
d ~= a
|
||||
d = d << (32 - 16) | d >> 16
|
||||
c += d
|
||||
b ~= c
|
||||
b = b << (32 - 12) | b >> 12
|
||||
a += m[SIGMA[(i % 10) * 16 + (2 * j + 1)]] ~ U256[SIGMA[(i % 10) * 16 + (2 * j)]]
|
||||
a += b
|
||||
d ~= a
|
||||
d = d << (32 - 8) | d >> 8
|
||||
c += d
|
||||
b ~= c
|
||||
b = b << (32 - 7) | b >> 7
|
||||
return a, b, c, d
|
||||
}
|
||||
|
||||
G512 :: #force_inline proc "contextless" (a, b, c, d: u64, m: [16]u64, i, j: int) -> (u64, u64, u64, u64) {
|
||||
a, b, c, d := a, b, c, d
|
||||
a += m[SIGMA[(i % 10) * 16 + (2 * j)]] ~ U512[SIGMA[(i % 10) * 16 + (2 * j + 1)]]
|
||||
a += b
|
||||
d ~= a
|
||||
d = d << (64 - 32) | d >> 32
|
||||
c += d
|
||||
b ~= c
|
||||
b = b << (64 - 25) | b >> 25
|
||||
a += m[SIGMA[(i % 10) * 16 + (2 * j + 1)]] ~ U512[SIGMA[(i % 10) * 16 + (2 * j)]]
|
||||
a += b
|
||||
d ~= a
|
||||
d = d << (64 - 16) | d >> 16
|
||||
c += d
|
||||
b ~= c
|
||||
b = b << (64 - 11) | b >> 11
|
||||
return a, b, c, d
|
||||
}
|
||||
|
||||
block256 :: proc "contextless" (ctx: ^Blake256_Context, p: []byte) {
|
||||
i, j: int = ---, ---
|
||||
v, m: [16]u32 = ---, ---
|
||||
p := p
|
||||
for len(p) >= BLOCKSIZE_256 {
|
||||
v[0] = ctx.h[0]
|
||||
v[1] = ctx.h[1]
|
||||
v[2] = ctx.h[2]
|
||||
v[3] = ctx.h[3]
|
||||
v[4] = ctx.h[4]
|
||||
v[5] = ctx.h[5]
|
||||
v[6] = ctx.h[6]
|
||||
v[7] = ctx.h[7]
|
||||
v[8] = ctx.s[0] ~ U256[0]
|
||||
v[9] = ctx.s[1] ~ U256[1]
|
||||
v[10] = ctx.s[2] ~ U256[2]
|
||||
v[11] = ctx.s[3] ~ U256[3]
|
||||
v[12] = U256[4]
|
||||
v[13] = U256[5]
|
||||
v[14] = U256[6]
|
||||
v[15] = U256[7]
|
||||
|
||||
ctx.t += 512
|
||||
if !ctx.nullt {
|
||||
v[12] ~= u32(ctx.t)
|
||||
v[13] ~= u32(ctx.t)
|
||||
v[14] ~= u32(ctx.t >> 32)
|
||||
v[15] ~= u32(ctx.t >> 32)
|
||||
}
|
||||
|
||||
for i, j = 0, 0; i < 16; i, j = i+1, j+4 {
|
||||
m[i] = u32(p[j]) << 24 | u32(p[j + 1]) << 16 | u32(p[j + 2]) << 8 | u32(p[j + 3])
|
||||
}
|
||||
|
||||
for i = 0; i < 14; i += 1 {
|
||||
v[0], v[4], v[8], v[12] = G256(v[0], v[4], v[8], v[12], m, i, 0)
|
||||
v[1], v[5], v[9], v[13] = G256(v[1], v[5], v[9], v[13], m, i, 1)
|
||||
v[2], v[6], v[10], v[14] = G256(v[2], v[6], v[10], v[14], m, i, 2)
|
||||
v[3], v[7], v[11], v[15] = G256(v[3], v[7], v[11], v[15], m, i, 3)
|
||||
v[0], v[5], v[10], v[15] = G256(v[0], v[5], v[10], v[15], m, i, 4)
|
||||
v[1], v[6], v[11], v[12] = G256(v[1], v[6], v[11], v[12], m, i, 5)
|
||||
v[2], v[7], v[8], v[13] = G256(v[2], v[7], v[8], v[13], m, i, 6)
|
||||
v[3], v[4], v[9], v[14] = G256(v[3], v[4], v[9], v[14], m, i, 7)
|
||||
}
|
||||
|
||||
for i = 0; i < 8; i += 1 {
|
||||
ctx.h[i] ~= ctx.s[i % 4] ~ v[i] ~ v[i + 8]
|
||||
}
|
||||
p = p[BLOCKSIZE_256:]
|
||||
}
|
||||
}
|
||||
|
||||
block512 :: proc "contextless" (ctx: ^Blake512_Context, p: []byte) #no_bounds_check {
|
||||
i, j: int = ---, ---
|
||||
v, m: [16]u64 = ---, ---
|
||||
p := p
|
||||
for len(p) >= BLOCKSIZE_512 {
|
||||
v[0] = ctx.h[0]
|
||||
v[1] = ctx.h[1]
|
||||
v[2] = ctx.h[2]
|
||||
v[3] = ctx.h[3]
|
||||
v[4] = ctx.h[4]
|
||||
v[5] = ctx.h[5]
|
||||
v[6] = ctx.h[6]
|
||||
v[7] = ctx.h[7]
|
||||
v[8] = ctx.s[0] ~ U512[0]
|
||||
v[9] = ctx.s[1] ~ U512[1]
|
||||
v[10] = ctx.s[2] ~ U512[2]
|
||||
v[11] = ctx.s[3] ~ U512[3]
|
||||
v[12] = U512[4]
|
||||
v[13] = U512[5]
|
||||
v[14] = U512[6]
|
||||
v[15] = U512[7]
|
||||
|
||||
ctx.t += 1024
|
||||
if !ctx.nullt {
|
||||
v[12] ~= ctx.t
|
||||
v[13] ~= ctx.t
|
||||
v[14] ~= 0
|
||||
v[15] ~= 0
|
||||
}
|
||||
|
||||
for i, j = 0, 0; i < 16; i, j = i + 1, j + 8 {
|
||||
m[i] = u64(p[j]) << 56 | u64(p[j + 1]) << 48 | u64(p[j + 2]) << 40 | u64(p[j + 3]) << 32 |
|
||||
u64(p[j + 4]) << 24 | u64(p[j + 5]) << 16 | u64(p[j + 6]) << 8 | u64(p[j + 7])
|
||||
}
|
||||
for i = 0; i < 16; i += 1 {
|
||||
v[0], v[4], v[8], v[12] = G512(v[0], v[4], v[8], v[12], m, i, 0)
|
||||
v[1], v[5], v[9], v[13] = G512(v[1], v[5], v[9], v[13], m, i, 1)
|
||||
v[2], v[6], v[10], v[14] = G512(v[2], v[6], v[10], v[14], m, i, 2)
|
||||
v[3], v[7], v[11], v[15] = G512(v[3], v[7], v[11], v[15], m, i, 3)
|
||||
v[0], v[5], v[10], v[15] = G512(v[0], v[5], v[10], v[15], m, i, 4)
|
||||
v[1], v[6], v[11], v[12] = G512(v[1], v[6], v[11], v[12], m, i, 5)
|
||||
v[2], v[7], v[8], v[13] = G512(v[2], v[7], v[8], v[13], m, i, 6)
|
||||
v[3], v[4], v[9], v[14] = G512(v[3], v[4], v[9], v[14], m, i, 7)
|
||||
}
|
||||
|
||||
for i = 0; i < 8; i += 1 {
|
||||
ctx.h[i] ~= ctx.s[i % 4] ~ v[i] ~ v[i + 8]
|
||||
}
|
||||
p = p[BLOCKSIZE_512:]
|
||||
}
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^$T) {
|
||||
when T == Blake256_Context {
|
||||
if ctx.is224 {
|
||||
ctx.h[0] = 0xc1059ed8
|
||||
ctx.h[1] = 0x367cd507
|
||||
ctx.h[2] = 0x3070dd17
|
||||
ctx.h[3] = 0xf70e5939
|
||||
ctx.h[4] = 0xffc00b31
|
||||
ctx.h[5] = 0x68581511
|
||||
ctx.h[6] = 0x64f98fa7
|
||||
ctx.h[7] = 0xbefa4fa4
|
||||
} else {
|
||||
ctx.h[0] = 0x6a09e667
|
||||
ctx.h[1] = 0xbb67ae85
|
||||
ctx.h[2] = 0x3c6ef372
|
||||
ctx.h[3] = 0xa54ff53a
|
||||
ctx.h[4] = 0x510e527f
|
||||
ctx.h[5] = 0x9b05688c
|
||||
ctx.h[6] = 0x1f83d9ab
|
||||
ctx.h[7] = 0x5be0cd19
|
||||
}
|
||||
} else when T == Blake512_Context {
|
||||
if ctx.is384 {
|
||||
ctx.h[0] = 0xcbbb9d5dc1059ed8
|
||||
ctx.h[1] = 0x629a292a367cd507
|
||||
ctx.h[2] = 0x9159015a3070dd17
|
||||
ctx.h[3] = 0x152fecd8f70e5939
|
||||
ctx.h[4] = 0x67332667ffc00b31
|
||||
ctx.h[5] = 0x8eb44a8768581511
|
||||
ctx.h[6] = 0xdb0c2e0d64f98fa7
|
||||
ctx.h[7] = 0x47b5481dbefa4fa4
|
||||
} else {
|
||||
ctx.h[0] = 0x6a09e667f3bcc908
|
||||
ctx.h[1] = 0xbb67ae8584caa73b
|
||||
ctx.h[2] = 0x3c6ef372fe94f82b
|
||||
ctx.h[3] = 0xa54ff53a5f1d36f1
|
||||
ctx.h[4] = 0x510e527fade682d1
|
||||
ctx.h[5] = 0x9b05688c2b3e6c1f
|
||||
ctx.h[6] = 0x1f83d9abfb41bd6b
|
||||
ctx.h[7] = 0x5be0cd19137e2179
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^$T, data: []byte) {
|
||||
data := data
|
||||
when T == Blake256_Context {
|
||||
if ctx.nx > 0 {
|
||||
n := copy(ctx.x[ctx.nx:], data)
|
||||
ctx.nx += n
|
||||
if ctx.nx == BLOCKSIZE_256 {
|
||||
block256(ctx, ctx.x[:])
|
||||
ctx.nx = 0
|
||||
}
|
||||
data = data[n:]
|
||||
}
|
||||
if len(data) >= BLOCKSIZE_256 {
|
||||
n := len(data) &~ (BLOCKSIZE_256 - 1)
|
||||
block256(ctx, data[:n])
|
||||
data = data[n:]
|
||||
}
|
||||
if len(data) > 0 {
|
||||
ctx.nx = copy(ctx.x[:], data)
|
||||
}
|
||||
} else when T == Blake512_Context {
|
||||
if ctx.nx > 0 {
|
||||
n := copy(ctx.x[ctx.nx:], data)
|
||||
ctx.nx += n
|
||||
if ctx.nx == BLOCKSIZE_512 {
|
||||
block512(ctx, ctx.x[:])
|
||||
ctx.nx = 0
|
||||
}
|
||||
data = data[n:]
|
||||
}
|
||||
if len(data) >= BLOCKSIZE_512 {
|
||||
n := len(data) &~ (BLOCKSIZE_512 - 1)
|
||||
block512(ctx, data[:n])
|
||||
data = data[n:]
|
||||
}
|
||||
if len(data) > 0 {
|
||||
ctx.nx = copy(ctx.x[:], data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^$T, hash: []byte) {
|
||||
when T == Blake256_Context {
|
||||
tmp: [65]byte
|
||||
} else when T == Blake512_Context {
|
||||
tmp: [129]byte
|
||||
}
|
||||
nx := u64(ctx.nx)
|
||||
tmp[0] = 0x80
|
||||
length := (ctx.t + nx) << 3
|
||||
|
||||
when T == Blake256_Context {
|
||||
if nx == 55 {
|
||||
if ctx.is224 {
|
||||
write_additional(ctx, {0x80})
|
||||
} else {
|
||||
write_additional(ctx, {0x81})
|
||||
}
|
||||
} else {
|
||||
if nx < 55 {
|
||||
if nx == 0 {
|
||||
ctx.nullt = true
|
||||
}
|
||||
write_additional(ctx, tmp[0 : 55 - nx])
|
||||
} else {
|
||||
write_additional(ctx, tmp[0 : 64 - nx])
|
||||
write_additional(ctx, tmp[1:56])
|
||||
ctx.nullt = true
|
||||
}
|
||||
if ctx.is224 {
|
||||
write_additional(ctx, {0x00})
|
||||
} else {
|
||||
write_additional(ctx, {0x01})
|
||||
}
|
||||
}
|
||||
|
||||
for i : uint = 0; i < 8; i += 1 {
|
||||
tmp[i] = byte(length >> (56 - 8 * i))
|
||||
}
|
||||
write_additional(ctx, tmp[0:8])
|
||||
|
||||
h := ctx.h[:]
|
||||
if ctx.is224 {
|
||||
h = h[0:7]
|
||||
}
|
||||
for s, i in h {
|
||||
hash[i * 4] = byte(s >> 24)
|
||||
hash[i * 4 + 1] = byte(s >> 16)
|
||||
hash[i * 4 + 2] = byte(s >> 8)
|
||||
hash[i * 4 + 3] = byte(s)
|
||||
}
|
||||
} else when T == Blake512_Context {
|
||||
if nx == 111 {
|
||||
if ctx.is384 {
|
||||
write_additional(ctx, {0x80})
|
||||
} else {
|
||||
write_additional(ctx, {0x81})
|
||||
}
|
||||
} else {
|
||||
if nx < 111 {
|
||||
if nx == 0 {
|
||||
ctx.nullt = true
|
||||
}
|
||||
write_additional(ctx, tmp[0 : 111 - nx])
|
||||
} else {
|
||||
write_additional(ctx, tmp[0 : 128 - nx])
|
||||
write_additional(ctx, tmp[1:112])
|
||||
ctx.nullt = true
|
||||
}
|
||||
if ctx.is384 {
|
||||
write_additional(ctx, {0x00})
|
||||
} else {
|
||||
write_additional(ctx, {0x01})
|
||||
}
|
||||
}
|
||||
|
||||
for i : uint = 0; i < 16; i += 1 {
|
||||
tmp[i] = byte(length >> (120 - 8 * i))
|
||||
}
|
||||
write_additional(ctx, tmp[0:16])
|
||||
|
||||
h := ctx.h[:]
|
||||
if ctx.is384 {
|
||||
h = h[0:6]
|
||||
}
|
||||
for s, i in h {
|
||||
hash[i * 8] = byte(s >> 56)
|
||||
hash[i * 8 + 1] = byte(s >> 48)
|
||||
hash[i * 8 + 2] = byte(s >> 40)
|
||||
hash[i * 8 + 3] = byte(s >> 32)
|
||||
hash[i * 8 + 4] = byte(s >> 24)
|
||||
hash[i * 8 + 5] = byte(s >> 16)
|
||||
hash[i * 8 + 6] = byte(s >> 8)
|
||||
hash[i * 8 + 7] = byte(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
write_additional :: proc(ctx: ^$T, data: []byte) {
|
||||
ctx.t -= u64(len(data)) << 3
|
||||
update_odin(ctx, data)
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
package blake2b
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Interface for the BLAKE2B hashing algorithm.
|
||||
BLAKE2B and BLAKE2B share the implementation in the _blake2 package.
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
import "../_blake2"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_64 = hash_bytes_odin
|
||||
ctx.hash_file_64 = hash_file_odin
|
||||
ctx.hash_stream_64 = hash_stream_odin
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_BLAKE2B)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// hash_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string :: proc(data: string) -> [64]byte {
|
||||
return hash_bytes(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes :: proc(data: []byte) -> [64]byte {
|
||||
_create_blake2b_ctx()
|
||||
return _hash_impl->hash_bytes_64(data)
|
||||
}
|
||||
|
||||
// hash_stream will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream :: proc(s: io.Stream) -> ([64]byte, bool) {
|
||||
_create_blake2b_ctx()
|
||||
return _hash_impl->hash_stream_64(s)
|
||||
}
|
||||
|
||||
// hash_file will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
_create_blake2b_ctx()
|
||||
return _hash_impl->hash_file_64(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash :: proc {
|
||||
hash_stream,
|
||||
hash_file,
|
||||
hash_bytes,
|
||||
hash_string,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(_blake2.Blake2b_Context); ok {
|
||||
_blake2.init_odin(&c)
|
||||
_blake2.update_odin(&c, data)
|
||||
_blake2.blake2b_final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(_blake2.Blake2b_Context); ok {
|
||||
_blake2.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_blake2.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_blake2.blake2b_final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [64]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_blake2b_ctx :: #force_inline proc() {
|
||||
ctx: _blake2.Blake2b_Context
|
||||
cfg: _blake2.Blake2_Config
|
||||
cfg.size = _blake2.BLAKE2B_SIZE
|
||||
ctx.cfg = cfg
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._64
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_blake2b_ctx()
|
||||
if c, ok := ctx.internal_ctx.(_blake2.Blake2b_Context); ok {
|
||||
_blake2.init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_blake2.Blake2b_Context); ok {
|
||||
_blake2.update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_blake2.Blake2b_Context); ok {
|
||||
_blake2.blake2b_final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
package blake2s
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Interface for the BLAKE2S hashing algorithm.
|
||||
BLAKE2B and BLAKE2B share the implementation in the _blake2 package.
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../_ctx"
|
||||
import "../_blake2"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_32 = hash_bytes_odin
|
||||
ctx.hash_file_32 = hash_file_odin
|
||||
ctx.hash_stream_32 = hash_stream_odin
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan does nothing, since Blake2s is not available in Botan
|
||||
@(warning="Blake2s is not provided by the Botan API. Odin implementation will be used")
|
||||
use_botan :: #force_inline proc() {
|
||||
use_odin()
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// hash_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string :: proc(data: string) -> [32]byte {
|
||||
return hash_bytes(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes :: proc(data: []byte) -> [32]byte {
|
||||
_create_blake2s_ctx()
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
}
|
||||
|
||||
// hash_stream will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream :: proc(s: io.Stream) -> ([32]byte, bool) {
|
||||
_create_blake2s_ctx()
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
}
|
||||
|
||||
// hash_file will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
_create_blake2s_ctx()
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash :: proc {
|
||||
hash_stream,
|
||||
hash_file,
|
||||
hash_bytes,
|
||||
hash_string,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(_blake2.Blake2s_Context); ok {
|
||||
_blake2.init_odin(&c)
|
||||
_blake2.update_odin(&c, data)
|
||||
_blake2.blake2s_final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(_blake2.Blake2s_Context); ok {
|
||||
_blake2.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_blake2.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_blake2.blake2s_final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [32]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_blake2s_ctx :: #force_inline proc() {
|
||||
ctx: _blake2.Blake2s_Context
|
||||
cfg: _blake2.Blake2_Config
|
||||
cfg.size = _blake2.BLAKE2S_SIZE
|
||||
ctx.cfg = cfg
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._32
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_blake2s_ctx()
|
||||
if c, ok := ctx.internal_ctx.(_blake2.Blake2s_Context); ok {
|
||||
_blake2.init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_blake2.Blake2s_Context); ok {
|
||||
_blake2.update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_blake2.Blake2s_Context); ok {
|
||||
_blake2.blake2s_final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,480 @@
|
||||
package botan
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog: Initial creation and testing of the bindings.
|
||||
|
||||
Bindings for the Botan crypto library.
|
||||
Created for version 2.18.1, using the provided FFI header within Botan.
|
||||
|
||||
The "botan_" prefix has been stripped from the identifiers to remove redundancy,
|
||||
since the package is already named botan.
|
||||
*/
|
||||
|
||||
import "core:c"
|
||||
|
||||
FFI_ERROR :: #type c.int
|
||||
FFI_SUCCESS :: FFI_ERROR(0)
|
||||
FFI_INVALID_VERIFIER :: FFI_ERROR(1)
|
||||
FFI_ERROR_INVALID_INPUT :: FFI_ERROR(-1)
|
||||
FFI_ERROR_BAD_MAC :: FFI_ERROR(-2)
|
||||
FFI_ERROR_INSUFFICIENT_BUFFER_SPACE :: FFI_ERROR(-10)
|
||||
FFI_ERROR_EXCEPTION_THROWN :: FFI_ERROR(-20)
|
||||
FFI_ERROR_OUT_OF_MEMORY :: FFI_ERROR(-21)
|
||||
FFI_ERROR_BAD_FLAG :: FFI_ERROR(-30)
|
||||
FFI_ERROR_NULL_POINTER :: FFI_ERROR(-31)
|
||||
FFI_ERROR_BAD_PARAMETER :: FFI_ERROR(-32)
|
||||
FFI_ERROR_KEY_NOT_SET :: FFI_ERROR(-33)
|
||||
FFI_ERROR_INVALID_KEY_LENGTH :: FFI_ERROR(-34)
|
||||
FFI_ERROR_NOT_IMPLEMENTED :: FFI_ERROR(-40)
|
||||
FFI_ERROR_INVALID_OBJECT :: FFI_ERROR(-50)
|
||||
FFI_ERROR_UNKNOWN_ERROR :: FFI_ERROR(-100)
|
||||
|
||||
FFI_HEX_LOWER_CASE :: 1
|
||||
|
||||
CIPHER_INIT_FLAG_MASK_DIRECTION :: 1
|
||||
CIPHER_INIT_FLAG_ENCRYPT :: 0
|
||||
CIPHER_INIT_FLAG_DECRYPT :: 1
|
||||
|
||||
CIPHER_UPDATE_FLAG_FINAL :: 1 << 0
|
||||
|
||||
CHECK_KEY_EXPENSIVE_TESTS :: 1
|
||||
|
||||
PRIVKEY_EXPORT_FLAG_DER :: 0
|
||||
PRIVKEY_EXPORT_FLAG_PEM :: 1
|
||||
|
||||
PUBKEY_DER_FORMAT_SIGNATURE :: 1
|
||||
|
||||
FPE_FLAG_FE1_COMPAT_MODE :: 1
|
||||
|
||||
x509_cert_key_constraints :: #type c.int
|
||||
NO_CONSTRAINTS :: x509_cert_key_constraints(0)
|
||||
DIGITAL_SIGNATURE :: x509_cert_key_constraints(32768)
|
||||
NON_REPUDIATION :: x509_cert_key_constraints(16384)
|
||||
KEY_ENCIPHERMENT :: x509_cert_key_constraints(8192)
|
||||
DATA_ENCIPHERMENT :: x509_cert_key_constraints(4096)
|
||||
KEY_AGREEMENT :: x509_cert_key_constraints(2048)
|
||||
KEY_CERT_SIGN :: x509_cert_key_constraints(1024)
|
||||
CRL_SIGN :: x509_cert_key_constraints(512)
|
||||
ENCIPHER_ONLY :: x509_cert_key_constraints(256)
|
||||
DECIPHER_ONLY :: x509_cert_key_constraints(128)
|
||||
|
||||
HASH_SHA1 :: "SHA1"
|
||||
HASH_SHA_224 :: "SHA-224"
|
||||
HASH_SHA_256 :: "SHA-256"
|
||||
HASH_SHA_384 :: "SHA-384"
|
||||
HASH_SHA_512 :: "SHA-512"
|
||||
HASH_SHA3_224 :: "SHA-3(224)"
|
||||
HASH_SHA3_256 :: "SHA-3(256)"
|
||||
HASH_SHA3_384 :: "SHA-3(384)"
|
||||
HASH_SHA3_512 :: "SHA-3(512)"
|
||||
HASH_SHAKE_128 :: "SHAKE-128"
|
||||
HASH_SHAKE_256 :: "SHAKE-256"
|
||||
HASH_KECCAK_224 :: "KECCAK(224)"
|
||||
HASH_KECCAK_256 :: "KECCAK(256)"
|
||||
HASH_KECCAK_384 :: "KECCAK(384)"
|
||||
HASH_KECCAK_512 :: "KECCAK(512)"
|
||||
HASH_RIPEMD_160 :: "RIPEMD-160"
|
||||
HASH_WHIRLPOOL :: "Whirlpool"
|
||||
HASH_BLAKE2B :: "BLAKE2b"
|
||||
HASH_MD4 :: "MD4"
|
||||
HASH_MD5 :: "MD5"
|
||||
HASH_TIGER_128 :: "Tiger(16,3)"
|
||||
HASH_TIGER_160 :: "Tiger(20,3)"
|
||||
HASH_TIGER_192 :: "Tiger(24,3)"
|
||||
HASH_GOST :: "GOST-34.11"
|
||||
HASH_STREEBOG_256 :: "Streebog-256"
|
||||
HASH_STREEBOG_512 :: "Streebog-512"
|
||||
HASH_SM3 :: "SM3"
|
||||
HASH_SKEIN_512_256 :: "Skein-512(256)"
|
||||
HASH_SKEIN_512_512 :: "Skein-512(512)"
|
||||
HASH_SKEIN_512_1024 :: "Skein-512(1024)"
|
||||
|
||||
// Not real values from Botan, only used for context setup within the crypto lib
|
||||
HASH_SHA2 :: "SHA2"
|
||||
HASH_SHA3 :: "SHA3"
|
||||
HASH_SHAKE :: "SHAKE"
|
||||
HASH_KECCAK :: "KECCAK"
|
||||
HASH_STREEBOG :: "STREEBOG"
|
||||
HASH_TIGER :: "TIGER"
|
||||
HASH_SKEIN_512 :: "SKEIN_512"
|
||||
|
||||
MAC_HMAC_SHA1 :: "HMAC(SHA1)"
|
||||
MAC_HMAC_SHA_224 :: "HMAC(SHA-224)"
|
||||
MAC_HMAC_SHA_256 :: "HMAC(SHA-256)"
|
||||
MAC_HMAC_SHA_384 :: "HMAC(SHA-384)"
|
||||
MAC_HMAC_SHA_512 :: "HMAC(SHA-512)"
|
||||
MAC_HMAC_MD5 :: "HMAC(MD5)"
|
||||
|
||||
hash_struct :: struct{}
|
||||
hash_t :: ^hash_struct
|
||||
rng_struct :: struct{}
|
||||
rng_t :: ^rng_struct
|
||||
mac_struct :: struct{}
|
||||
mac_t :: ^mac_struct
|
||||
cipher_struct :: struct{}
|
||||
cipher_t :: ^cipher_struct
|
||||
block_cipher_struct :: struct{}
|
||||
block_cipher_t :: ^block_cipher_struct
|
||||
mp_struct :: struct{}
|
||||
mp_t :: ^mp_struct
|
||||
privkey_struct :: struct{}
|
||||
privkey_t :: ^privkey_struct
|
||||
pubkey_struct :: struct{}
|
||||
pubkey_t :: ^pubkey_struct
|
||||
pk_op_encrypt_struct :: struct{}
|
||||
pk_op_encrypt_t :: ^pk_op_encrypt_struct
|
||||
pk_op_decrypt_struct :: struct{}
|
||||
pk_op_decrypt_t :: ^pk_op_decrypt_struct
|
||||
pk_op_sign_struct :: struct{}
|
||||
pk_op_sign_t :: ^pk_op_sign_struct
|
||||
pk_op_verify_struct :: struct{}
|
||||
pk_op_verify_t :: ^pk_op_verify_struct
|
||||
pk_op_ka_struct :: struct{}
|
||||
pk_op_ka_t :: ^pk_op_ka_struct
|
||||
x509_cert_struct :: struct{}
|
||||
x509_cert_t :: ^x509_cert_struct
|
||||
x509_crl_struct :: struct{}
|
||||
x509_crl_t :: ^x509_crl_struct
|
||||
hotp_struct :: struct{}
|
||||
hotp_t :: ^hotp_struct
|
||||
totp_struct :: struct{}
|
||||
totp_t :: ^totp_struct
|
||||
fpe_struct :: struct{}
|
||||
fpe_t :: ^fpe_struct
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
foreign import botan_lib "botan.lib"
|
||||
} else when ODIN_OS == "linux" {
|
||||
foreign import botan_lib "system:botan-2"
|
||||
} else when ODIN_OS == "darwin" {
|
||||
foreign import botan_lib "system:botan-2"
|
||||
}
|
||||
|
||||
@(default_calling_convention="c")
|
||||
@(link_prefix="botan_")
|
||||
foreign botan_lib {
|
||||
error_description :: proc(err: c.int) -> cstring ---
|
||||
ffi_api_version :: proc() -> c.int ---
|
||||
ffi_supports_api :: proc(api_version: c.int) -> c.int ---
|
||||
version_string :: proc() -> cstring ---
|
||||
version_major :: proc() -> c.int ---
|
||||
version_minor :: proc() -> c.int ---
|
||||
version_patch :: proc() -> c.int ---
|
||||
version_datestamp :: proc() -> c.int ---
|
||||
|
||||
constant_time_compare :: proc(x, y: ^c.char, length: c.size_t) -> c.int ---
|
||||
same_mem :: proc(x, y: ^c.char, length: c.size_t) -> c.int ---
|
||||
scrub_mem :: proc(mem: rawptr, bytes: c.size_t) -> c.int ---
|
||||
|
||||
hex_encode :: proc(x: ^c.char, length: c.size_t, out: ^c.char, flags: c.uint) -> c.int ---
|
||||
hex_decode :: proc(hex_str: cstring, in_len: c.size_t, out: ^c.char, out_len: c.size_t) -> c.int ---
|
||||
|
||||
base64_encode :: proc(x: ^c.char, length: c.size_t, out: ^c.char, out_len: c.size_t) -> c.int ---
|
||||
base64_decode :: proc(base64_str: cstring, in_len: c.size_t, out: ^c.char, out_len: c.size_t) -> c.int ---
|
||||
|
||||
rng_init :: proc(rng: ^rng_t, rng_type: cstring) -> c.int ---
|
||||
rng_init_custom :: proc(rng_out: ^rng_t, rng_name: cstring, ctx: rawptr,
|
||||
get_cb: proc(ctx: rawptr, out: ^c.char, out_len: c.size_t) -> ^c.int,
|
||||
add_entropy_cb: proc(ctx: rawptr, input: ^c.char, length: c.size_t) -> ^c.int,
|
||||
destroy_cb: proc(ctx: rawptr) -> rawptr) -> c.int ---
|
||||
rng_get :: proc(rng: rng_t, out: ^c.char, out_len: c.size_t) -> c.int ---
|
||||
rng_reseed :: proc(rng: rng_t, bits: c.size_t) -> c.int ---
|
||||
rng_reseed_from_rng :: proc(rng, source_rng: rng_t, bits: c.size_t) -> c.int ---
|
||||
rng_add_entropy :: proc(rng: rng_t, entropy: ^c.char, entropy_len: c.size_t) -> c.int ---
|
||||
rng_destroy :: proc(rng: rng_t) -> c.int ---
|
||||
|
||||
hash_init :: proc(hash: ^hash_t, hash_name: cstring, flags: c.uint) -> c.int ---
|
||||
hash_copy_state :: proc(dest: ^hash_t, source: hash_t) -> c.int ---
|
||||
hash_output_length :: proc(hash: hash_t, output_length: ^c.size_t) -> c.int ---
|
||||
hash_block_size :: proc(hash: hash_t, block_size: ^c.size_t) -> c.int ---
|
||||
hash_update :: proc(hash: hash_t, input: ^c.char, input_len: c.size_t) -> c.int ---
|
||||
hash_final :: proc(hash: hash_t, out: ^c.char) -> c.int ---
|
||||
hash_clear :: proc(hash: hash_t) -> c.int ---
|
||||
hash_destroy :: proc(hash: hash_t) -> c.int ---
|
||||
hash_name :: proc(hash: hash_t, name: ^c.char, name_len: ^c.size_t) -> c.int ---
|
||||
|
||||
mac_init :: proc(mac: ^mac_t, hash_name: cstring, flags: c.uint) -> c.int ---
|
||||
mac_output_length :: proc(mac: mac_t, output_length: ^c.size_t) -> c.int ---
|
||||
mac_set_key :: proc(mac: mac_t, key: ^c.char, key_len: c.size_t) -> c.int ---
|
||||
mac_update :: proc(mac: mac_t, buf: ^c.char, length: c.size_t) -> c.int ---
|
||||
mac_final :: proc(mac: mac_t, out: ^c.char) -> c.int ---
|
||||
mac_clear :: proc(mac: mac_t) -> c.int ---
|
||||
mac_name :: proc(mac: mac_t, name: ^c.char, name_len: ^c.size_t) -> c.int ---
|
||||
mac_get_keyspec :: proc(mac: mac_t, out_minimum_keylength, out_maximum_keylength, out_keylength_modulo: ^c.size_t) -> c.int ---
|
||||
mac_destroy :: proc(mac: mac_t) -> c.int ---
|
||||
|
||||
cipher_init :: proc(cipher: ^cipher_t, name: cstring, flags: c.uint) -> c.int ---
|
||||
cipher_name :: proc(cipher: cipher_t, name: ^c.char, name_len: ^c.size_t) -> c.int ---
|
||||
cipher_output_length :: proc(cipher: cipher_t, output_length: ^c.size_t) -> c.int ---
|
||||
cipher_valid_nonce_length :: proc(cipher: cipher_t, nl: c.size_t) -> c.int ---
|
||||
cipher_get_tag_length :: proc(cipher: cipher_t, tag_size: ^c.size_t) -> c.int ---
|
||||
cipher_get_default_nonce_length :: proc(cipher: cipher_t, nl: ^c.size_t) -> c.int ---
|
||||
cipher_get_update_granularity :: proc(cipher: cipher_t, ug: ^c.size_t) -> c.int ---
|
||||
cipher_query_keylen :: proc(cipher: cipher_t, out_minimum_keylength, out_maximum_keylength: ^c.size_t) -> c.int ---
|
||||
cipher_get_keyspec :: proc(cipher: cipher_t, min_keylen, max_keylen, mod_keylen: ^c.size_t) -> c.int ---
|
||||
cipher_set_key :: proc(cipher: cipher_t, key: ^c.char, key_len: c.size_t) -> c.int ---
|
||||
cipher_reset :: proc(cipher: cipher_t) -> c.int ---
|
||||
cipher_set_associated_data :: proc(cipher: cipher_t, ad: ^c.char, ad_len: c.size_t) -> c.int ---
|
||||
cipher_start :: proc(cipher: cipher_t, nonce: ^c.char, nonce_len: c.size_t) -> c.int ---
|
||||
cipher_update :: proc(cipher: cipher_t, flags: c.uint, output: ^c.char, output_size: c.size_t, output_written: ^c.size_t,
|
||||
input_bytes: ^c.char, input_size: c.size_t, input_consumed: ^c.size_t) -> c.int ---
|
||||
cipher_clear :: proc(hash: cipher_t) -> c.int ---
|
||||
cipher_destroy :: proc(cipher: cipher_t) -> c.int ---
|
||||
|
||||
@(deprecated="Use botan.pwdhash")
|
||||
pbkdf :: proc(pbkdf_algo: cstring, out: ^c.char, out_len: c.size_t, passphrase: cstring, salt: ^c.char,
|
||||
salt_len, iterations: c.size_t) -> c.int ---
|
||||
@(deprecated="Use botan.pwdhash_timed")
|
||||
pbkdf_timed :: proc(pbkdf_algo: cstring, out: ^c.char, out_len: c.size_t, passphrase: cstring, salt: ^c.char,
|
||||
salt_len, milliseconds_to_run: c.size_t, out_iterations_used: ^c.size_t) -> c.int ---
|
||||
pwdhash :: proc(algo: cstring, param1, param2, param3: c.size_t, out: ^c.char, out_len: c.size_t, passphrase: cstring,
|
||||
passphrase_len: c.size_t, salt: ^c.char, salt_len: c.size_t) -> c.int ---
|
||||
pwdhash_timed :: proc(algo: cstring, msec: c.uint, param1, param2, param3: c.size_t, out: ^c.char, out_len: c.size_t,
|
||||
passphrase: cstring, passphrase_len: c.size_t, salt: ^c.char, salt_len: c.size_t) -> c.int ---
|
||||
@(deprecated="Use botan.pwdhash")
|
||||
scrypt :: proc(out: ^c.char, out_len: c.size_t, passphrase: cstring, salt: ^c.char, salt_len, N, r, p: c.size_t) -> c.int ---
|
||||
kdf :: proc(kdf_algo: cstring, out: ^c.char, out_len: c.size_t, secret: ^c.char, secret_lent: c.size_t, salt: ^c.char,
|
||||
salt_len: c.size_t, label: ^c.char, label_len: c.size_t) -> c.int ---
|
||||
|
||||
block_cipher_init :: proc(bc: ^block_cipher_t, name: cstring) -> c.int ---
|
||||
block_cipher_destroy :: proc(bc: block_cipher_t) -> c.int ---
|
||||
block_cipher_clear :: proc(bc: block_cipher_t) -> c.int ---
|
||||
block_cipher_set_key :: proc(bc: block_cipher_t, key: ^c.char, key_len: c.size_t) -> c.int ---
|
||||
block_cipher_block_size :: proc(bc: block_cipher_t) -> c.int ---
|
||||
block_cipher_encrypt_blocks :: proc(bc: block_cipher_t, input, out: ^c.char, blocks: c.size_t) -> c.int ---
|
||||
block_cipher_decrypt_blocks :: proc(bc: block_cipher_t, input, out: ^c.char, blocks: c.size_t) -> c.int ---
|
||||
block_cipher_name :: proc(bc: block_cipher_t, name: ^c.char, name_len: ^c.size_t) -> c.int ---
|
||||
block_cipher_get_keyspec :: proc(bc: block_cipher_t, out_minimum_keylength, out_maximum_keylength, out_keylength_modulo: ^c.size_t) -> c.int ---
|
||||
|
||||
mp_init :: proc(mp: ^mp_t) -> c.int ---
|
||||
mp_destroy :: proc(mp: mp_t) -> c.int ---
|
||||
mp_to_hex :: proc(mp: mp_t, out: ^c.char) -> c.int ---
|
||||
mp_to_str :: proc(mp: mp_t, base: c.char, out: ^c.char, out_len: ^c.size_t) -> c.int ---
|
||||
mp_clear :: proc(mp: mp_t) -> c.int ---
|
||||
mp_set_from_int :: proc(mp: mp_t, initial_value: c.int) -> c.int ---
|
||||
mp_set_from_mp :: proc(dest, source: mp_t) -> c.int ---
|
||||
mp_set_from_str :: proc(dest: mp_t, str: cstring) -> c.int ---
|
||||
mp_set_from_radix_str :: proc(mp: mp_t, str: cstring, radix: c.size_t) -> c.int ---
|
||||
mp_num_bits :: proc(n: mp_t, bits: ^c.size_t) -> c.int ---
|
||||
mp_num_bytes :: proc(n: mp_t, bytes: ^c.size_t) -> c.int ---
|
||||
mp_to_bin :: proc(mp: mp_t, vec: ^c.char) -> c.int ---
|
||||
mp_from_bin :: proc(mp: mp_t, vec: ^c.char, vec_len: c.size_t) -> c.int ---
|
||||
mp_to_uint32 :: proc(mp: mp_t, val: ^c.uint) -> c.int ---
|
||||
mp_is_positive :: proc(mp: mp_t) -> c.int ---
|
||||
mp_is_negative :: proc(mp: mp_t) -> c.int ---
|
||||
mp_flip_sign :: proc(mp: mp_t) -> c.int ---
|
||||
mp_is_zero :: proc(mp: mp_t) -> c.int ---
|
||||
@(deprecated="Use botan.mp_get_bit(0)")
|
||||
mp_is_odd :: proc(mp: mp_t) -> c.int ---
|
||||
@(deprecated="Use botan.mp_get_bit(0)")
|
||||
mp_is_even :: proc(mp: mp_t) -> c.int ---
|
||||
mp_add_u32 :: proc(result, x: mp_t, y: c.uint) -> c.int ---
|
||||
mp_sub_u32 :: proc(result, x: mp_t, y: c.uint) -> c.int ---
|
||||
mp_add :: proc(result, x, y: mp_t) -> c.int ---
|
||||
mp_sub :: proc(result, x, y: mp_t) -> c.int ---
|
||||
mp_mul :: proc(result, x, y: mp_t) -> c.int ---
|
||||
mp_div :: proc(quotient, remainder, x, y: mp_t) -> c.int ---
|
||||
mp_mod_mul :: proc(result, x, y, mod: mp_t) -> c.int ---
|
||||
mp_equal :: proc(x, y: mp_t) -> c.int ---
|
||||
mp_cmp :: proc(result: ^c.int, x, y: mp_t) -> c.int ---
|
||||
mp_swap :: proc(x, y: mp_t) -> c.int ---
|
||||
mp_powmod :: proc(out, base, exponent, modulus: mp_t) -> c.int ---
|
||||
mp_lshift :: proc(out, input: mp_t, shift: c.size_t) -> c.int ---
|
||||
mp_rshift :: proc(out, input: mp_t, shift: c.size_t) -> c.int ---
|
||||
mp_mod_inverse :: proc(out, input, modulus: mp_t) -> c.int ---
|
||||
mp_rand_bits :: proc(rand_out: mp_t, rng: rng_t, bits: c.size_t) -> c.int ---
|
||||
mp_rand_range :: proc(rand_out: mp_t, rng: rng_t, lower_bound, upper_bound: mp_t) -> c.int ---
|
||||
mp_gcd :: proc(out, x, y: mp_t) -> c.int ---
|
||||
mp_is_prime :: proc(n: mp_t, rng: rng_t, test_prob: c.size_t) -> c.int ---
|
||||
mp_get_bit :: proc(n: mp_t, bit: c.size_t) -> c.int ---
|
||||
mp_set_bit :: proc(n: mp_t, bit: c.size_t) -> c.int ---
|
||||
mp_clear_bit :: proc(n: mp_t, bit: c.size_t) -> c.int ---
|
||||
|
||||
bcrypt_generate :: proc(out: ^c.char, out_len: ^c.size_t, password: cstring, rng: rng_t, work_factor: c.size_t, flags: c.uint) -> c.int ---
|
||||
bcrypt_is_valid :: proc(pass, hash: cstring) -> c.int ---
|
||||
|
||||
privkey_create :: proc(key: ^privkey_t, algo_name, algo_params: cstring, rng: rng_t) -> c.int ---
|
||||
@(deprecated="Use botan.privkey_create")
|
||||
privkey_check_key :: proc(key: privkey_t, rng: rng_t, flags: c.uint) -> c.int ---
|
||||
@(deprecated="Use botan.privkey_create")
|
||||
privkey_create_rsa :: proc(key: ^privkey_t, rng: rng_t, bits: c.size_t) -> c.int ---
|
||||
@(deprecated="Use botan.privkey_create")
|
||||
privkey_create_ecdsa :: proc(key: ^privkey_t, rng: rng_t, params: cstring) -> c.int ---
|
||||
@(deprecated="Use botan.privkey_create")
|
||||
privkey_create_ecdh :: proc(key: ^privkey_t, rng: rng_t, params: cstring) -> c.int ---
|
||||
@(deprecated="Use botan.privkey_create")
|
||||
privkey_create_mceliece :: proc(key: ^privkey_t, rng: rng_t, n, t: c.size_t) -> c.int ---
|
||||
@(deprecated="Use botan.privkey_create")
|
||||
privkey_create_dh :: proc(key: ^privkey_t, rng: rng_t, param: cstring) -> c.int ---
|
||||
privkey_create_dsa :: proc(key: ^privkey_t, rng: rng_t, pbits, qbits: c.size_t) -> c.int ---
|
||||
privkey_create_elgamal :: proc(key: ^privkey_t, rng: rng_t, pbits, qbits: c.size_t) -> c.int ---
|
||||
privkey_load :: proc(key: ^privkey_t, rng: rng_t, bits: ^c.char, length: c.size_t, password: cstring) -> c.int ---
|
||||
privkey_destroy :: proc(key: privkey_t) -> c.int ---
|
||||
privkey_export :: proc(key: privkey_t, out: ^c.char, out_len: ^c.size_t, flags: c.uint) -> c.int ---
|
||||
privkey_algo_name :: proc(key: privkey_t, out: ^c.char, out_len: ^c.size_t) -> c.int ---
|
||||
@(deprecated="Use botan.privkey_export_encrypted_pbkdf_{msec,iter}")
|
||||
privkey_export_encrypted :: proc(key: privkey_t, out: ^c.char, out_len: ^c.size_t, rng: rng_t, passphrase, encryption_algo: cstring, flags: c.uint) -> c.int ---
|
||||
privkey_export_encrypted_pbkdf_msec :: proc(key: privkey_t, out: ^c.char, out_len: ^c.size_t, rng: rng_t, passphrase: cstring, pbkdf_msec_runtime: c.uint,
|
||||
pbkdf_iterations_out: ^c.size_t, cipher_algo, pbkdf_algo: cstring, flags: c.uint) -> c.int ---
|
||||
privkey_export_encrypted_pbkdf_iter :: proc(key: privkey_t, out: ^c.char, out_len: ^c.size_t, rng: rng_t, passphrase: cstring, pbkdf_iterations: c.size_t,
|
||||
cipher_algo, pbkdf_algo: cstring, flags: c.uint) -> c.int ---
|
||||
pubkey_load :: proc(key: ^pubkey_t, bits: ^c.char, length: c.size_t) -> c.int ---
|
||||
privkey_export_pubkey :: proc(out: ^pubkey_t, input: privkey_t) -> c.int ---
|
||||
pubkey_export :: proc(key: pubkey_t, out: ^c.char, out_len: ^c.size_t, flags: c.uint) -> c.int ---
|
||||
pubkey_algo_name :: proc(key: pubkey_t, out: ^c.char, out_len: ^c.size_t) -> c.int ---
|
||||
pubkey_check_key :: proc(key: pubkey_t, rng: rng_t, flags: c.uint) -> c.int ---
|
||||
pubkey_estimated_strength :: proc(key: pubkey_t, estimate: ^c.size_t) -> c.int ---
|
||||
pubkey_fingerprint :: proc(key: pubkey_t, hash: cstring, out: ^c.char, out_len: ^c.size_t) -> c.int ---
|
||||
pubkey_destroy :: proc(key: pubkey_t) -> c.int ---
|
||||
pubkey_get_field :: proc(output: mp_t, key: pubkey_t, field_name: cstring) -> c.int ---
|
||||
privkey_get_field :: proc(output: mp_t, key: privkey_t, field_name: cstring) -> c.int ---
|
||||
|
||||
privkey_load_rsa :: proc(key: ^privkey_t, p, q, e: mp_t) -> c.int ---
|
||||
privkey_load_rsa_pkcs1 :: proc(key: ^privkey_t, bits: ^c.char, length: c.size_t) -> c.int ---
|
||||
@(deprecated="Use botan.privkey_get_field")
|
||||
privkey_rsa_get_p :: proc(p: mp_t, rsa_key: privkey_t) -> c.int ---
|
||||
@(deprecated="Use botan.privkey_get_field")
|
||||
privkey_rsa_get_q :: proc(q: mp_t, rsa_key: privkey_t) -> c.int ---
|
||||
@(deprecated="Use botan.privkey_get_field")
|
||||
privkey_rsa_get_d :: proc(d: mp_t, rsa_key: privkey_t) -> c.int ---
|
||||
@(deprecated="Use botan.privkey_get_field")
|
||||
privkey_rsa_get_n :: proc(n: mp_t, rsa_key: privkey_t) -> c.int ---
|
||||
@(deprecated="Use botan.privkey_get_field")
|
||||
privkey_rsa_get_e :: proc(e: mp_t, rsa_key: privkey_t) -> c.int ---
|
||||
privkey_rsa_get_privkey :: proc(rsa_key: privkey_t, out: ^c.char, out_len: ^c.size_t, flags: c.uint) -> c.int ---
|
||||
pubkey_load_rsa :: proc(key: ^pubkey_t, n, e: mp_t) -> c.int ---
|
||||
@(deprecated="Use botan.pubkey_get_field")
|
||||
pubkey_rsa_get_e :: proc(e: mp_t, rsa_key: pubkey_t) -> c.int ---
|
||||
@(deprecated="Use botan.pubkey_get_field")
|
||||
pubkey_rsa_get_n :: proc(n: mp_t, rsa_key: pubkey_t) -> c.int ---
|
||||
|
||||
privkey_load_dsa :: proc(key: ^privkey_t, p, q, g, x: mp_t) -> c.int ---
|
||||
pubkey_load_dsa :: proc(key: ^pubkey_t, p, q, g, y: mp_t) -> c.int ---
|
||||
@(deprecated="Use botan.pubkey_get_field")
|
||||
privkey_dsa_get_x :: proc(n: mp_t, key: privkey_t) -> c.int ---
|
||||
@(deprecated="Use botan.pubkey_get_field")
|
||||
pubkey_dsa_get_p :: proc(p: mp_t, key: pubkey_t) -> c.int ---
|
||||
@(deprecated="Use botan.pubkey_get_field")
|
||||
pubkey_dsa_get_q :: proc(q: mp_t, key: pubkey_t) -> c.int ---
|
||||
@(deprecated="Use botan.pubkey_get_field")
|
||||
pubkey_dsa_get_g :: proc(d: mp_t, key: pubkey_t) -> c.int ---
|
||||
@(deprecated="Use botan.pubkey_get_field")
|
||||
pubkey_dsa_get_y :: proc(y: mp_t, key: pubkey_t) -> c.int ---
|
||||
|
||||
privkey_load_dh :: proc(key: ^privkey_t, p, g, y: mp_t) -> c.int ---
|
||||
pubkey_load_dh :: proc(key: ^pubkey_t, p, g, x: mp_t) -> c.int ---
|
||||
|
||||
privkey_load_elgamal :: proc(key: ^privkey_t, p, g, y: mp_t) -> c.int ---
|
||||
pubkey_load_elgamal :: proc(key: ^pubkey_t, p, g, x: mp_t) -> c.int ---
|
||||
|
||||
privkey_load_ed25519 :: proc(key: ^privkey_t, privkey: [32]c.char) -> c.int ---
|
||||
pubkey_load_ed25519 :: proc(key: ^pubkey_t, pubkey: [32]c.char) -> c.int ---
|
||||
privkey_ed25519_get_privkey :: proc(key: ^privkey_t, output: [64]c.char) -> c.int ---
|
||||
pubkey_ed25519_get_pubkey :: proc(key: ^pubkey_t, pubkey: [32]c.char) -> c.int ---
|
||||
|
||||
privkey_load_x25519 :: proc(key: ^privkey_t, privkey: [32]c.char) -> c.int ---
|
||||
pubkey_load_x25519 :: proc(key: ^pubkey_t, pubkey: [32]c.char) -> c.int ---
|
||||
privkey_x25519_get_privkey :: proc(key: ^privkey_t, output: [32]c.char) -> c.int ---
|
||||
pubkey_x25519_get_pubkey :: proc(key: ^pubkey_t, pubkey: [32]c.char) -> c.int ---
|
||||
|
||||
privkey_load_ecdsa :: proc(key: ^privkey_t, scalar: mp_t, curve_name: cstring) -> c.int ---
|
||||
pubkey_load_ecdsa :: proc(key: ^pubkey_t, public_x, public_y: mp_t, curve_name: cstring) -> c.int ---
|
||||
pubkey_load_ecdh :: proc(key: ^pubkey_t, public_x, public_y: mp_t, curve_name: cstring) -> c.int ---
|
||||
privkey_load_ecdh :: proc(key: ^privkey_t, scalar: mp_t, curve_name: cstring) -> c.int ---
|
||||
pubkey_load_sm2 :: proc(key: ^pubkey_t, public_x, public_y: mp_t, curve_name: cstring) -> c.int ---
|
||||
privkey_load_sm2 :: proc(key: ^privkey_t, scalar: mp_t, curve_name: cstring) -> c.int ---
|
||||
@(deprecated="Use botan.pubkey_load_sm2")
|
||||
pubkey_load_sm2_enc :: proc(key: ^pubkey_t, public_x, public_y: mp_t, curve_name: cstring) -> c.int ---
|
||||
@(deprecated="Use botan.privkey_load_sm2")
|
||||
privkey_load_sm2_enc :: proc(key: ^privkey_t, scalar: mp_t, curve_name: cstring) -> c.int ---
|
||||
pubkey_sm2_compute_za :: proc(out: ^c.char, out_len: ^c.size_t, ident, hash_algo: cstring, key: pubkey_t) -> c.int ---
|
||||
|
||||
pk_op_encrypt_create :: proc(op: ^pk_op_encrypt_t, key: pubkey_t, padding: cstring, flags: c.uint) -> c.int ---
|
||||
pk_op_encrypt_destroy :: proc(op: pk_op_encrypt_t) -> c.int ---
|
||||
pk_op_encrypt_output_length :: proc(op: pk_op_encrypt_t, ptext_len: c.size_t, ctext_len: ^c.size_t) -> c.int ---
|
||||
pk_op_encrypt :: proc(op: pk_op_encrypt_t, rng: rng_t, out: ^c.char, out_len: ^c.size_t, plaintext: cstring, plaintext_len: c.size_t) -> c.int ---
|
||||
|
||||
pk_op_decrypt_create :: proc(op: ^pk_op_decrypt_t, key: privkey_t, padding: cstring, flags: c.uint) -> c.int ---
|
||||
pk_op_decrypt_destroy :: proc(op: pk_op_decrypt_t) -> c.int ---
|
||||
pk_op_decrypt_output_length :: proc(op: pk_op_decrypt_t, ptext_len: c.size_t, ctext_len: ^c.size_t) -> c.int ---
|
||||
pk_op_decrypt :: proc(op: pk_op_decrypt_t, rng: rng_t, out: ^c.char, out_len: ^c.size_t, ciphertext: cstring, ciphertext_len: c.size_t) -> c.int ---
|
||||
|
||||
pk_op_sign_create :: proc(op: ^pk_op_sign_t, key: privkey_t, hash_and_padding: cstring, flags: c.uint) -> c.int ---
|
||||
pk_op_sign_destroy :: proc(op: pk_op_sign_t) -> c.int ---
|
||||
pk_op_sign_output_length :: proc(op: pk_op_sign_t, olen: ^c.size_t) -> c.int ---
|
||||
pk_op_sign_update :: proc(op: pk_op_sign_t, input: ^c.char, input_len: c.size_t) -> c.int ---
|
||||
pk_op_sign_finish :: proc(op: pk_op_sign_t, rng: rng_t, sig: ^c.char, sig_len: ^c.size_t) -> c.int ---
|
||||
|
||||
pk_op_verify_create :: proc(op: ^pk_op_verify_t, hash_and_padding: cstring, flags: c.uint) -> c.int ---
|
||||
pk_op_verify_destroy :: proc(op: pk_op_verify_t) -> c.int ---
|
||||
pk_op_verify_update :: proc(op: pk_op_verify_t, input: ^c.char, input_len: c.size_t) -> c.int ---
|
||||
pk_op_verify_finish :: proc(op: pk_op_verify_t, sig: ^c.char, sig_len: c.size_t) -> c.int ---
|
||||
|
||||
pk_op_key_agreement_create :: proc(op: ^pk_op_ka_t, kdf: cstring, flags: c.uint) -> c.int ---
|
||||
pk_op_key_agreement_destroy :: proc(op: pk_op_ka_t) -> c.int ---
|
||||
pk_op_key_agreement_export_public :: proc(key: privkey_t, out: ^c.char, out_len: ^c.size_t) -> c.int ---
|
||||
pk_op_key_agreement_size :: proc(op: pk_op_ka_t, out_len: ^c.size_t) -> c.int ---
|
||||
pk_op_key_agreement :: proc(op: pk_op_ka_t, out: ^c.char, out_len: ^c.size_t, other_key: ^c.char, other_key_len: c.size_t, salt: ^c.char,
|
||||
salt_len: c.size_t) -> c.int ---
|
||||
|
||||
pkcs_hash_id :: proc(hash_name: cstring, pkcs_id: ^c.char, pkcs_id_len: ^c.size_t) -> c.int ---
|
||||
|
||||
@(deprecated="Poorly specified, avoid in new code")
|
||||
mceies_encrypt :: proc(mce_key: pubkey_t, rng: rng_t, aead: cstring, pt: ^c.char, pt_len: c.size_t, ad: ^c.char, ad_len: c.size_t,
|
||||
ct: ^c.char, ct_len: ^c.size_t) -> c.int ---
|
||||
@(deprecated="Poorly specified, avoid in new code")
|
||||
mceies_decrypt :: proc(mce_key: privkey_t, aead: cstring, ct: ^c.char, ct_len: c.size_t, ad: ^c.char, ad_len: c.size_t, pt: ^c.char,
|
||||
pt_len: ^c.size_t) -> c.int ---
|
||||
|
||||
x509_cert_load :: proc(cert_obj: ^x509_cert_t, cert: ^c.char, cert_len: c.size_t) -> c.int ---
|
||||
x509_cert_load_file :: proc(cert_obj: ^x509_cert_t, filename: cstring) -> c.int ---
|
||||
x509_cert_destroy :: proc(cert: x509_cert_t) -> c.int ---
|
||||
x509_cert_dup :: proc(new_cert: ^x509_cert_t, cert: x509_cert_t) -> c.int ---
|
||||
x509_cert_get_time_starts :: proc(cert: x509_cert_t, out: ^c.char, out_len: ^c.size_t) -> c.int ---
|
||||
x509_cert_get_time_expires :: proc(cert: x509_cert_t, out: ^c.char, out_len: ^c.size_t) -> c.int ---
|
||||
x509_cert_not_before :: proc(cert: x509_cert_t, time_since_epoch: ^c.ulonglong) -> c.int ---
|
||||
x509_cert_not_after :: proc(cert: x509_cert_t, time_since_epoch: ^c.ulonglong) -> c.int ---
|
||||
x509_cert_get_fingerprint :: proc(cert: x509_cert_t, hash: cstring, out: ^c.char, out_len: ^c.size_t) -> c.int ---
|
||||
x509_cert_get_serial_number :: proc(cert: x509_cert_t, out: ^c.char, out_len: ^c.size_t) -> c.int ---
|
||||
x509_cert_get_authority_key_id :: proc(cert: x509_cert_t, out: ^c.char, out_len: ^c.size_t) -> c.int ---
|
||||
x509_cert_get_subject_key_id :: proc(cert: x509_cert_t, out: ^c.char, out_len: ^c.size_t) -> c.int ---
|
||||
x509_cert_get_public_key_bits :: proc(cert: x509_cert_t, out: ^c.char, out_len: ^c.size_t) -> c.int ---
|
||||
x509_cert_get_public_key :: proc(cert: x509_cert_t, key: ^pubkey_t) -> c.int ---
|
||||
x509_cert_get_issuer_dn :: proc(cert: x509_cert_t, key: ^c.char, index: c.size_t, out: ^c.char, out_len: ^c.size_t) -> c.int ---
|
||||
x509_cert_get_subject_dn :: proc(cert: x509_cert_t, key: ^c.char, index: c.size_t, out: ^c.char, out_len: ^c.size_t) -> c.int ---
|
||||
x509_cert_to_string :: proc(cert: x509_cert_t, out: ^c.char, out_len: ^c.size_t) -> c.int ---
|
||||
x509_cert_allowed_usage :: proc(cert: x509_cert_t, key_usage: c.uint) -> c.int ---
|
||||
x509_cert_hostname_match :: proc(cert: x509_cert_t, hostname: cstring) -> c.int ---
|
||||
x509_cert_verify :: proc(validation_result: ^c.int, cert: x509_cert_t, intermediates: ^x509_cert_t, intermediates_len: c.size_t, trusted: ^x509_cert_t,
|
||||
trusted_len: c.size_t, trusted_path: cstring, required_strength: c.size_t, hostname: cstring, reference_time: c.ulonglong) -> c.int ---
|
||||
x509_cert_validation_status :: proc(code: c.int) -> cstring ---
|
||||
x509_crl_load_file :: proc(crl_obj: ^x509_crl_t, crl_path: cstring) -> c.int ---
|
||||
x509_crl_load :: proc(crl_obj: ^x509_crl_t, crl_bits: ^c.char, crl_bits_len: c.size_t) -> c.int ---
|
||||
x509_crl_destroy :: proc(crl: x509_crl_t) -> c.int ---
|
||||
x509_is_revoked :: proc(crl: x509_crl_t, cert: x509_cert_t) -> c.int ---
|
||||
x509_cert_verify_with_crl :: proc(validation_result: ^c.int, cert: x509_cert_t, intermediates: ^x509_cert_t, intermediates_len: c.size_t, trusted: ^x509_cert_t,
|
||||
trusted_len: c.size_t, crls: ^x509_crl_t, crls_len: c.size_t, trusted_path: cstring, required_strength: c.size_t,
|
||||
hostname: cstring, reference_time: c.ulonglong) -> c.int ---
|
||||
|
||||
key_wrap3394 :: proc(key: ^c.char, key_len: c.size_t, kek: ^c.char, kek_len: c.size_t, wrapped_key: ^c.char, wrapped_key_len: ^c.size_t) -> c.int ---
|
||||
key_unwrap3394 :: proc(wrapped_key: ^c.char, wrapped_key_len: c.size_t, kek: ^c.char, kek_len: c.size_t, key: ^c.char, key_len: ^c.size_t) -> c.int ---
|
||||
|
||||
hotp_init :: proc(hotp: ^hotp_t, key: ^c.char, key_len: c.size_t, hash_algo: cstring, digits: c.size_t) -> c.int ---
|
||||
hotp_destroy :: proc(hotp: hotp_t) -> c.int ---
|
||||
hotp_generate :: proc(hotp: hotp_t, hotp_code: ^c.uint, hotp_counter: c.ulonglong) -> c.int ---
|
||||
hotp_check :: proc(hotp: hotp_t, next_hotp_counter: ^c.ulonglong, hotp_code: c.uint, hotp_counter: c.ulonglong, resync_range: c.size_t) -> c.int ---
|
||||
|
||||
totp_init :: proc(totp: ^totp_t, key: ^c.char, key_len: c.size_t, hash_algo: cstring, digits, time_step: c.size_t) -> c.int ---
|
||||
totp_destroy :: proc(totp: totp_t) -> c.int ---
|
||||
totp_generate :: proc(totp: totp_t, totp_code: ^c.uint, timestamp: c.ulonglong) -> c.int ---
|
||||
totp_check :: proc(totp: totp_t, totp_code: ^c.uint, timestamp: c.ulonglong, acceptable_clock_drift: c.size_t) -> c.int ---
|
||||
|
||||
fpe_fe1_init :: proc(fpe: ^fpe_t, n: mp_t, key: ^c.char, key_len, rounds: c.size_t, flags: c.uint) -> c.int ---
|
||||
fpe_destroy :: proc(fpe: fpe_t) -> c.int ---
|
||||
fpe_encrypt :: proc(fpe: fpe_t, x: mp_t, tweak: ^c.char, tweak_len: c.size_t) -> c.int ---
|
||||
fpe_decrypt :: proc(fpe: fpe_t, x: mp_t, tweak: ^c.char, tweak_len: c.size_t) -> c.int ---
|
||||
}
|
||||
@@ -0,0 +1,498 @@
|
||||
package botan
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog: Initial creation and testing of the bindings.
|
||||
|
||||
Implementation of the context for the Botan side.
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
import "core:fmt"
|
||||
import "core:strings"
|
||||
|
||||
import "../_ctx"
|
||||
|
||||
hash_bytes_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
|
||||
hash: [16]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._16, 16), 0)
|
||||
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_bytes_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [20]byte {
|
||||
hash: [20]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._20, 20), 0)
|
||||
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_bytes_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [24]byte {
|
||||
hash: [24]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._24, 24), 0)
|
||||
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_bytes_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte {
|
||||
hash: [28]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._28, 28), 0)
|
||||
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_bytes_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._32, 32), 0)
|
||||
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_bytes_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte {
|
||||
hash: [48]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._48, 48), 0)
|
||||
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_bytes_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
|
||||
hash: [64]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._64, 64), 0)
|
||||
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_bytes_128 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [128]byte {
|
||||
hash: [128]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._128, 128), 0)
|
||||
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_bytes_slice :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
|
||||
hash := make([]byte, bit_size, allocator)
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, nil, bit_size), 0)
|
||||
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash[:]
|
||||
}
|
||||
|
||||
hash_file_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_16(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_16(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [16]byte{}, false
|
||||
}
|
||||
|
||||
hash_file_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_20(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_20(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [20]byte{}, false
|
||||
}
|
||||
|
||||
hash_file_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([24]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_24(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_24(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [24]byte{}, false
|
||||
}
|
||||
|
||||
hash_file_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_28(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_28(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [28]byte{}, false
|
||||
}
|
||||
|
||||
hash_file_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_32(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_32(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [32]byte{}, false
|
||||
}
|
||||
|
||||
hash_file_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_48(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_48(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [48]byte{}, false
|
||||
}
|
||||
|
||||
hash_file_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_64(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_64(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [64]byte{}, false
|
||||
}
|
||||
|
||||
hash_file_128 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([128]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_128(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_128(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [128]byte{}, false
|
||||
}
|
||||
|
||||
hash_file_slice :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_slice(ctx, os.stream_from_handle(hd), bit_size, allocator)
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_slice(ctx, buf[:], bit_size, allocator), ok
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
hash_stream_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([16]byte, bool) {
|
||||
hash: [16]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._16, 16), 0)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
i := 1
|
||||
for i > 0 {
|
||||
i, _ = s->impl_read(buf)
|
||||
if i > 0 {
|
||||
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
|
||||
}
|
||||
}
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash, true
|
||||
}
|
||||
|
||||
hash_stream_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([20]byte, bool) {
|
||||
hash: [20]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._20, 20), 0)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
i := 1
|
||||
for i > 0 {
|
||||
i, _ = s->impl_read(buf)
|
||||
if i > 0 {
|
||||
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
|
||||
}
|
||||
}
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash, true
|
||||
}
|
||||
|
||||
hash_stream_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([24]byte, bool) {
|
||||
hash: [24]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._24, 24), 0)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
i := 1
|
||||
for i > 0 {
|
||||
i, _ = s->impl_read(buf)
|
||||
if i > 0 {
|
||||
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
|
||||
}
|
||||
}
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash, true
|
||||
}
|
||||
|
||||
hash_stream_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([28]byte, bool) {
|
||||
hash: [28]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._28, 28), 0)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
i := 1
|
||||
for i > 0 {
|
||||
i, _ = s->impl_read(buf)
|
||||
if i > 0 {
|
||||
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
|
||||
}
|
||||
}
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash, true
|
||||
}
|
||||
|
||||
hash_stream_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._32, 32), 0)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
i := 1
|
||||
for i > 0 {
|
||||
i, _ = s->impl_read(buf)
|
||||
if i > 0 {
|
||||
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
|
||||
}
|
||||
}
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash, true
|
||||
}
|
||||
|
||||
hash_stream_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([48]byte, bool) {
|
||||
hash: [48]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._48, 48), 0)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
i := 1
|
||||
for i > 0 {
|
||||
i, _ = s->impl_read(buf)
|
||||
if i > 0 {
|
||||
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
|
||||
}
|
||||
}
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash, true
|
||||
}
|
||||
|
||||
hash_stream_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([64]byte, bool) {
|
||||
hash: [64]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._64, 64), 0)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
i := 1
|
||||
for i > 0 {
|
||||
i, _ = s->impl_read(buf)
|
||||
if i > 0 {
|
||||
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
|
||||
}
|
||||
}
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash, true
|
||||
}
|
||||
|
||||
hash_stream_128 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([128]byte, bool) {
|
||||
hash: [128]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._128, 128), 0)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
i := 1
|
||||
for i > 0 {
|
||||
i, _ = s->impl_read(buf)
|
||||
if i > 0 {
|
||||
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
|
||||
}
|
||||
}
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash, true
|
||||
}
|
||||
|
||||
hash_stream_slice :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
|
||||
hash := make([]byte, bit_size, allocator)
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, nil, bit_size), 0)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
i := 1
|
||||
for i > 0 {
|
||||
i, _ = s->impl_read(buf)
|
||||
if i > 0 {
|
||||
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
|
||||
}
|
||||
}
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash[:], true
|
||||
}
|
||||
|
||||
init :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
c: hash_t
|
||||
hash_init(&c, ctx.botan_hash_algo, 0)
|
||||
ctx.external_ctx = c
|
||||
}
|
||||
|
||||
update :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.external_ctx.(hash_t); ok {
|
||||
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
|
||||
}
|
||||
}
|
||||
|
||||
final :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.external_ctx.(hash_t); ok {
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
}
|
||||
}
|
||||
|
||||
assign_hash_vtable :: proc(ctx: ^_ctx.Hash_Context, hash_algo: cstring) {
|
||||
ctx.init = init
|
||||
ctx.update = update
|
||||
ctx.final = final
|
||||
ctx.botan_hash_algo = hash_algo
|
||||
|
||||
switch hash_algo {
|
||||
case HASH_MD4, HASH_MD5:
|
||||
ctx.hash_bytes_16 = hash_bytes_16
|
||||
ctx.hash_file_16 = hash_file_16
|
||||
ctx.hash_stream_16 = hash_stream_16
|
||||
|
||||
case HASH_SHA1, HASH_RIPEMD_160:
|
||||
ctx.hash_bytes_20 = hash_bytes_20
|
||||
ctx.hash_file_20 = hash_file_20
|
||||
ctx.hash_stream_20 = hash_stream_20
|
||||
|
||||
case HASH_SHA2, HASH_SHA3:
|
||||
ctx.hash_bytes_28 = hash_bytes_28
|
||||
ctx.hash_file_28 = hash_file_28
|
||||
ctx.hash_stream_28 = hash_stream_28
|
||||
ctx.hash_bytes_32 = hash_bytes_32
|
||||
ctx.hash_file_32 = hash_file_32
|
||||
ctx.hash_stream_32 = hash_stream_32
|
||||
ctx.hash_bytes_48 = hash_bytes_48
|
||||
ctx.hash_file_48 = hash_file_48
|
||||
ctx.hash_stream_48 = hash_stream_48
|
||||
ctx.hash_bytes_64 = hash_bytes_64
|
||||
ctx.hash_file_64 = hash_file_64
|
||||
ctx.hash_stream_64 = hash_stream_64
|
||||
|
||||
case HASH_GOST, HASH_WHIRLPOOL, HASH_SM3:
|
||||
ctx.hash_bytes_32 = hash_bytes_32
|
||||
ctx.hash_file_32 = hash_file_32
|
||||
ctx.hash_stream_32 = hash_stream_32
|
||||
|
||||
case HASH_STREEBOG:
|
||||
ctx.hash_bytes_32 = hash_bytes_32
|
||||
ctx.hash_file_32 = hash_file_32
|
||||
ctx.hash_stream_32 = hash_stream_32
|
||||
ctx.hash_bytes_64 = hash_bytes_64
|
||||
ctx.hash_file_64 = hash_file_64
|
||||
ctx.hash_stream_64 = hash_stream_64
|
||||
|
||||
case HASH_BLAKE2B:
|
||||
ctx.hash_bytes_64 = hash_bytes_64
|
||||
ctx.hash_file_64 = hash_file_64
|
||||
ctx.hash_stream_64 = hash_stream_64
|
||||
|
||||
case HASH_TIGER:
|
||||
ctx.hash_bytes_16 = hash_bytes_16
|
||||
ctx.hash_file_16 = hash_file_16
|
||||
ctx.hash_stream_16 = hash_stream_16
|
||||
ctx.hash_bytes_20 = hash_bytes_20
|
||||
ctx.hash_file_20 = hash_file_20
|
||||
ctx.hash_stream_20 = hash_stream_20
|
||||
ctx.hash_bytes_24 = hash_bytes_24
|
||||
ctx.hash_file_24 = hash_file_24
|
||||
ctx.hash_stream_24 = hash_stream_24
|
||||
|
||||
case HASH_SKEIN_512:
|
||||
ctx.hash_bytes_slice = hash_bytes_slice
|
||||
ctx.hash_file_slice = hash_file_slice
|
||||
ctx.hash_stream_slice = hash_stream_slice
|
||||
}
|
||||
}
|
||||
|
||||
_check_ctx :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash_size: _ctx.Hash_Size, hash_size_val: int) -> cstring {
|
||||
ctx.hash_size = hash_size
|
||||
ctx.hash_size_val = hash_size_val
|
||||
switch ctx.botan_hash_algo {
|
||||
case HASH_SHA2:
|
||||
#partial switch hash_size {
|
||||
case ._28: return HASH_SHA_224
|
||||
case ._32: return HASH_SHA_256
|
||||
case ._48: return HASH_SHA_384
|
||||
case ._64: return HASH_SHA_512
|
||||
}
|
||||
case HASH_SHA3:
|
||||
#partial switch hash_size {
|
||||
case ._28: return HASH_SHA3_224
|
||||
case ._32: return HASH_SHA3_256
|
||||
case ._48: return HASH_SHA3_384
|
||||
case ._64: return HASH_SHA3_512
|
||||
}
|
||||
case HASH_KECCAK:
|
||||
#partial switch hash_size {
|
||||
case ._28: return HASH_KECCAK_224
|
||||
case ._32: return HASH_KECCAK_256
|
||||
case ._48: return HASH_KECCAK_384
|
||||
case ._64: return HASH_KECCAK_512
|
||||
}
|
||||
case HASH_STREEBOG:
|
||||
#partial switch hash_size {
|
||||
case ._32: return HASH_STREEBOG_256
|
||||
case ._64: return HASH_STREEBOG_512
|
||||
}
|
||||
case HASH_TIGER:
|
||||
#partial switch hash_size {
|
||||
case ._16: return HASH_TIGER_128
|
||||
case ._20: return HASH_TIGER_160
|
||||
case ._24: return HASH_TIGER_192
|
||||
}
|
||||
case HASH_SKEIN_512:
|
||||
return strings.unsafe_string_to_cstring(fmt.tprintf("Skein-512(%d)", hash_size_val * 8))
|
||||
case: return ctx.botan_hash_algo
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,457 @@
|
||||
package gost
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the GOST hashing algorithm, as defined in RFC 5831 <https://datatracker.ietf.org/doc/html/rfc5831>
|
||||
*/
|
||||
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
_assign_hash_vtable(ctx)
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_32 = hash_bytes_odin
|
||||
ctx.hash_file_32 = hash_file_odin
|
||||
ctx.hash_stream_32 = hash_stream_odin
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan does nothing, since MD2 is not available in Botan
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_GOST)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// hash_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string :: proc(data: string) -> [32]byte {
|
||||
return hash_bytes(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes :: proc(data: []byte) -> [32]byte {
|
||||
_create_gost_ctx()
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
}
|
||||
|
||||
// hash_stream will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream :: proc(s: io.Stream) -> ([32]byte, bool) {
|
||||
_create_gost_ctx()
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
}
|
||||
|
||||
// hash_file will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
_create_gost_ctx()
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash :: proc {
|
||||
hash_stream,
|
||||
hash_file,
|
||||
hash_bytes,
|
||||
hash_string,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Gost_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Gost_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [32]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_gost_ctx :: #force_inline proc() {
|
||||
ctx: Gost_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._32
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_gost_ctx()
|
||||
if c, ok := ctx.internal_ctx.(Gost_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Gost_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Gost_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
GOST implementation
|
||||
*/
|
||||
|
||||
Gost_Context :: struct {
|
||||
sum: [8]u32,
|
||||
hash: [8]u32,
|
||||
len: [8]u32,
|
||||
partial: [32]byte,
|
||||
partial_bytes: byte,
|
||||
}
|
||||
|
||||
SBOX_1 : [256]u32
|
||||
SBOX_2 : [256]u32
|
||||
SBOX_3 : [256]u32
|
||||
SBOX_4 : [256]u32
|
||||
|
||||
GOST_ENCRYPT_ROUND :: #force_inline proc "contextless"(l, r, t, k1, k2: u32) -> (u32, u32, u32) {
|
||||
l, r, t := l, r, t
|
||||
t = (k1) + r
|
||||
l ~= SBOX_1[t & 0xff] ~ SBOX_2[(t >> 8) & 0xff] ~ SBOX_3[(t >> 16) & 0xff] ~ SBOX_4[t >> 24]
|
||||
t = (k2) + l
|
||||
r ~= SBOX_1[t & 0xff] ~ SBOX_2[(t >> 8) & 0xff] ~ SBOX_3[(t >> 16) & 0xff] ~ SBOX_4[t >> 24]
|
||||
return l, r, t
|
||||
}
|
||||
|
||||
GOST_ENCRYPT :: #force_inline proc "contextless"(a, b, c: u32, key: []u32) -> (l, r, t: u32) {
|
||||
l, r, t = GOST_ENCRYPT_ROUND(a, b, c, key[0], key[1])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[2], key[3])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[4], key[5])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[6], key[7])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[0], key[1])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[2], key[3])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[4], key[5])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[6], key[7])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[0], key[1])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[2], key[3])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[4], key[5])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[6], key[7])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[7], key[6])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[5], key[4])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[3], key[2])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[1], key[0])
|
||||
t = r
|
||||
r = l
|
||||
l = t
|
||||
return
|
||||
}
|
||||
|
||||
gost_bytes :: proc(ctx: ^Gost_Context, buf: []byte, bits: u32) {
|
||||
a, c: u32
|
||||
m: [8]u32
|
||||
|
||||
for i, j := 0, 0; i < 8; i += 1 {
|
||||
a = u32(buf[j]) | u32(buf[j + 1]) << 8 | u32(buf[j + 2]) << 16 | u32(buf[j + 3]) << 24
|
||||
j += 4
|
||||
m[i] = a
|
||||
c = a + c + ctx.sum[i]
|
||||
ctx.sum[i] = c
|
||||
c = c < a ? 1 : 0
|
||||
}
|
||||
|
||||
gost_compress(ctx.hash[:], m[:])
|
||||
ctx.len[0] += bits
|
||||
if ctx.len[0] < bits {
|
||||
ctx.len[1] += 1
|
||||
}
|
||||
}
|
||||
|
||||
gost_compress :: proc(h, m: []u32) {
|
||||
key, u, v, w, s: [8]u32
|
||||
|
||||
copy(u[:], h)
|
||||
copy(v[:], m)
|
||||
|
||||
for i := 0; i < 8; i += 2 {
|
||||
w[0] = u[0] ~ v[0]
|
||||
w[1] = u[1] ~ v[1]
|
||||
w[2] = u[2] ~ v[2]
|
||||
w[3] = u[3] ~ v[3]
|
||||
w[4] = u[4] ~ v[4]
|
||||
w[5] = u[5] ~ v[5]
|
||||
w[6] = u[6] ~ v[6]
|
||||
w[7] = u[7] ~ v[7]
|
||||
|
||||
key[0] = (w[0] & 0x000000ff) | (w[2] & 0x000000ff) << 8 | (w[4] & 0x000000ff) << 16 | (w[6] & 0x000000ff) << 24
|
||||
key[1] = (w[0] & 0x0000ff00) >> 8 | (w[2] & 0x0000ff00) | (w[4] & 0x0000ff00) << 8 | (w[6] & 0x0000ff00) << 16
|
||||
key[2] = (w[0] & 0x00ff0000) >> 16 | (w[2] & 0x00ff0000) >> 8 | (w[4] & 0x00ff0000) | (w[6] & 0x00ff0000) << 8
|
||||
key[3] = (w[0] & 0xff000000) >> 24 | (w[2] & 0xff000000) >> 16 | (w[4] & 0xff000000) >> 8 | (w[6] & 0xff000000)
|
||||
key[4] = (w[1] & 0x000000ff) | (w[3] & 0x000000ff) << 8 | (w[5] & 0x000000ff) << 16 | (w[7] & 0x000000ff) << 24
|
||||
key[5] = (w[1] & 0x0000ff00) >> 8 | (w[3] & 0x0000ff00) | (w[5] & 0x0000ff00) << 8 | (w[7] & 0x0000ff00) << 16
|
||||
key[6] = (w[1] & 0x00ff0000) >> 16 | (w[3] & 0x00ff0000) >> 8 | (w[5] & 0x00ff0000) | (w[7] & 0x00ff0000) << 8
|
||||
key[7] = (w[1] & 0xff000000) >> 24 | (w[3] & 0xff000000) >> 16 | (w[5] & 0xff000000) >> 8 | (w[7] & 0xff000000)
|
||||
|
||||
r := h[i]
|
||||
l := h[i + 1]
|
||||
t: u32
|
||||
l, r, t = GOST_ENCRYPT(l, r, 0, key[:])
|
||||
|
||||
s[i] = r
|
||||
s[i + 1] = l
|
||||
|
||||
if i == 6 {
|
||||
break
|
||||
}
|
||||
|
||||
l = u[0] ~ u[2]
|
||||
r = u[1] ~ u[3]
|
||||
u[0] = u[2]
|
||||
u[1] = u[3]
|
||||
u[2] = u[4]
|
||||
u[3] = u[5]
|
||||
u[4] = u[6]
|
||||
u[5] = u[7]
|
||||
u[6] = l
|
||||
u[7] = r
|
||||
|
||||
if i == 2 {
|
||||
u[0] ~= 0xff00ff00
|
||||
u[1] ~= 0xff00ff00
|
||||
u[2] ~= 0x00ff00ff
|
||||
u[3] ~= 0x00ff00ff
|
||||
u[4] ~= 0x00ffff00
|
||||
u[5] ~= 0xff0000ff
|
||||
u[6] ~= 0x000000ff
|
||||
u[7] ~= 0xff00ffff
|
||||
}
|
||||
|
||||
l = v[0]
|
||||
r = v[2]
|
||||
v[0] = v[4]
|
||||
v[2] = v[6]
|
||||
v[4] = l ~ r
|
||||
v[6] = v[0] ~ r
|
||||
l = v[1]
|
||||
r = v[3]
|
||||
v[1] = v[5]
|
||||
v[3] = v[7]
|
||||
v[5] = l ~ r
|
||||
v[7] = v[1] ~ r
|
||||
}
|
||||
|
||||
u[0] = m[0] ~ s[6]
|
||||
u[1] = m[1] ~ s[7]
|
||||
u[2] = m[2] ~ (s[0] << 16) ~ (s[0] >> 16) ~ (s[0] & 0xffff) ~
|
||||
(s[1] & 0xffff) ~ (s[1] >> 16) ~ (s[2] << 16) ~ s[6] ~ (s[6] << 16) ~
|
||||
(s[7] & 0xffff0000) ~ (s[7] >> 16)
|
||||
u[3] = m[3] ~ (s[0] & 0xffff) ~ (s[0] << 16) ~ (s[1] & 0xffff) ~
|
||||
(s[1] << 16) ~ (s[1] >> 16) ~ (s[2] << 16) ~ (s[2] >> 16) ~
|
||||
(s[3] << 16) ~ s[6] ~ (s[6] << 16) ~ (s[6] >> 16) ~ (s[7] & 0xffff) ~
|
||||
(s[7] << 16) ~ (s[7] >> 16)
|
||||
u[4] = m[4] ~
|
||||
(s[0] & 0xffff0000) ~ (s[0] << 16) ~ (s[0] >> 16) ~
|
||||
(s[1] & 0xffff0000) ~ (s[1] >> 16) ~ (s[2] << 16) ~ (s[2] >> 16) ~
|
||||
(s[3] << 16) ~ (s[3] >> 16) ~ (s[4] << 16) ~ (s[6] << 16) ~
|
||||
(s[6] >> 16) ~(s[7] & 0xffff) ~ (s[7] << 16) ~ (s[7] >> 16)
|
||||
u[5] = m[5] ~ (s[0] << 16) ~ (s[0] >> 16) ~ (s[0] & 0xffff0000) ~
|
||||
(s[1] & 0xffff) ~ s[2] ~ (s[2] >> 16) ~ (s[3] << 16) ~ (s[3] >> 16) ~
|
||||
(s[4] << 16) ~ (s[4] >> 16) ~ (s[5] << 16) ~ (s[6] << 16) ~
|
||||
(s[6] >> 16) ~ (s[7] & 0xffff0000) ~ (s[7] << 16) ~ (s[7] >> 16)
|
||||
u[6] = m[6] ~ s[0] ~ (s[1] >> 16) ~ (s[2] << 16) ~ s[3] ~ (s[3] >> 16) ~
|
||||
(s[4] << 16) ~ (s[4] >> 16) ~ (s[5] << 16) ~ (s[5] >> 16) ~ s[6] ~
|
||||
(s[6] << 16) ~ (s[6] >> 16) ~ (s[7] << 16)
|
||||
u[7] = m[7] ~ (s[0] & 0xffff0000) ~ (s[0] << 16) ~ (s[1] & 0xffff) ~
|
||||
(s[1] << 16) ~ (s[2] >> 16) ~ (s[3] << 16) ~ s[4] ~ (s[4] >> 16) ~
|
||||
(s[5] << 16) ~ (s[5] >> 16) ~ (s[6] >> 16) ~ (s[7] & 0xffff) ~
|
||||
(s[7] << 16) ~ (s[7] >> 16)
|
||||
|
||||
v[0] = h[0] ~ (u[1] << 16) ~ (u[0] >> 16)
|
||||
v[1] = h[1] ~ (u[2] << 16) ~ (u[1] >> 16)
|
||||
v[2] = h[2] ~ (u[3] << 16) ~ (u[2] >> 16)
|
||||
v[3] = h[3] ~ (u[4] << 16) ~ (u[3] >> 16)
|
||||
v[4] = h[4] ~ (u[5] << 16) ~ (u[4] >> 16)
|
||||
v[5] = h[5] ~ (u[6] << 16) ~ (u[5] >> 16)
|
||||
v[6] = h[6] ~ (u[7] << 16) ~ (u[6] >> 16)
|
||||
v[7] = h[7] ~ (u[0] & 0xffff0000) ~ (u[0] << 16) ~ (u[7] >> 16) ~ (u[1] & 0xffff0000) ~ (u[1] << 16) ~ (u[6] << 16) ~ (u[7] & 0xffff0000)
|
||||
|
||||
h[0] = (v[0] & 0xffff0000) ~ (v[0] << 16) ~ (v[0] >> 16) ~ (v[1] >> 16) ~
|
||||
(v[1] & 0xffff0000) ~ (v[2] << 16) ~ (v[3] >> 16) ~ (v[4] << 16) ~
|
||||
(v[5] >> 16) ~ v[5] ~ (v[6] >> 16) ~ (v[7] << 16) ~ (v[7] >> 16) ~
|
||||
(v[7] & 0xffff)
|
||||
h[1] = (v[0] << 16) ~ (v[0] >> 16) ~ (v[0] & 0xffff0000) ~ (v[1] & 0xffff) ~
|
||||
v[2] ~ (v[2] >> 16) ~ (v[3] << 16) ~ (v[4] >> 16) ~ (v[5] << 16) ~
|
||||
(v[6] << 16) ~ v[6] ~ (v[7] & 0xffff0000) ~ (v[7] >> 16)
|
||||
h[2] = (v[0] & 0xffff) ~ (v[0] << 16) ~ (v[1] << 16) ~ (v[1] >> 16) ~
|
||||
(v[1] & 0xffff0000) ~ (v[2] << 16) ~ (v[3] >> 16) ~ v[3] ~ (v[4] << 16) ~
|
||||
(v[5] >> 16) ~ v[6] ~ (v[6] >> 16) ~ (v[7] & 0xffff) ~ (v[7] << 16) ~
|
||||
(v[7] >> 16)
|
||||
h[3] = (v[0] << 16) ~ (v[0] >> 16) ~ (v[0] & 0xffff0000) ~
|
||||
(v[1] & 0xffff0000) ~ (v[1] >> 16) ~ (v[2] << 16) ~ (v[2] >> 16) ~ v[2] ~
|
||||
(v[3] << 16) ~ (v[4] >> 16) ~ v[4] ~ (v[5] << 16) ~ (v[6] << 16) ~
|
||||
(v[7] & 0xffff) ~ (v[7] >> 16)
|
||||
h[4] = (v[0] >> 16) ~ (v[1] << 16) ~ v[1] ~ (v[2] >> 16) ~ v[2] ~
|
||||
(v[3] << 16) ~ (v[3] >> 16) ~ v[3] ~ (v[4] << 16) ~ (v[5] >> 16) ~
|
||||
v[5] ~ (v[6] << 16) ~ (v[6] >> 16) ~ (v[7] << 16)
|
||||
h[5] = (v[0] << 16) ~ (v[0] & 0xffff0000) ~ (v[1] << 16) ~ (v[1] >> 16) ~
|
||||
(v[1] & 0xffff0000) ~ (v[2] << 16) ~ v[2] ~ (v[3] >> 16) ~ v[3] ~
|
||||
(v[4] << 16) ~ (v[4] >> 16) ~ v[4] ~ (v[5] << 16) ~ (v[6] << 16) ~
|
||||
(v[6] >> 16) ~ v[6] ~ (v[7] << 16) ~ (v[7] >> 16) ~ (v[7] & 0xffff0000)
|
||||
h[6] = v[0] ~ v[2] ~ (v[2] >> 16) ~ v[3] ~ (v[3] << 16) ~ v[4] ~
|
||||
(v[4] >> 16) ~ (v[5] << 16) ~ (v[5] >> 16) ~ v[5] ~ (v[6] << 16) ~
|
||||
(v[6] >> 16) ~ v[6] ~ (v[7] << 16) ~ v[7]
|
||||
h[7] = v[0] ~ (v[0] >> 16) ~ (v[1] << 16) ~ (v[1] >> 16) ~ (v[2] << 16) ~
|
||||
(v[3] >> 16) ~ v[3] ~ (v[4] << 16) ~ v[4] ~ (v[5] >> 16) ~ v[5] ~
|
||||
(v[6] << 16) ~ (v[6] >> 16) ~ (v[7] << 16) ~ v[7]
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^Gost_Context) {
|
||||
sbox: [8][16]u32 = {
|
||||
{ 10, 4, 5, 6, 8, 1, 3, 7, 13, 12, 14, 0, 9, 2, 11, 15 },
|
||||
{ 5, 15, 4, 0, 2, 13, 11, 9, 1, 7, 6, 3, 12, 14, 10, 8 },
|
||||
{ 7, 15, 12, 14, 9, 4, 1, 0, 3, 11, 5, 2, 6, 10, 8, 13 },
|
||||
{ 4, 10, 7, 12, 0, 15, 2, 8, 14, 1, 6, 5, 13, 11, 9, 3 },
|
||||
{ 7, 6, 4, 11, 9, 12, 2, 10, 1, 8, 0, 14, 15, 13, 3, 5 },
|
||||
{ 7, 6, 2, 4, 13, 9, 15, 0, 10, 1, 5, 11, 8, 14, 12, 3 },
|
||||
{ 13, 14, 4, 1, 7, 0, 5, 10, 3, 12, 8, 15, 6, 2, 9, 11 },
|
||||
{ 1, 3, 10, 9, 5, 11, 4, 15, 8, 6, 7, 14, 13, 0, 2, 12 },
|
||||
}
|
||||
|
||||
i := 0
|
||||
for a := 0; a < 16; a += 1 {
|
||||
ax := sbox[1][a] << 15
|
||||
bx := sbox[3][a] << 23
|
||||
cx := sbox[5][a]
|
||||
cx = (cx >> 1) | (cx << 31)
|
||||
dx := sbox[7][a] << 7
|
||||
for b := 0; b < 16; b, i = b + 1, i + 1 {
|
||||
SBOX_1[i] = ax | (sbox[0][b] << 11)
|
||||
SBOX_2[i] = bx | (sbox[2][b] << 19)
|
||||
SBOX_3[i] = cx | (sbox[4][b] << 27)
|
||||
SBOX_4[i] = dx | (sbox[6][b] << 3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^Gost_Context, data: []byte) {
|
||||
length := byte(len(data))
|
||||
j: byte
|
||||
|
||||
i := ctx.partial_bytes
|
||||
for i < 32 && j < length {
|
||||
ctx.partial[i] = data[j]
|
||||
i, j = i + 1, j + 1
|
||||
}
|
||||
|
||||
if i < 32 {
|
||||
ctx.partial_bytes = i
|
||||
return
|
||||
}
|
||||
gost_bytes(ctx, ctx.partial[:], 256)
|
||||
|
||||
for (j + 32) < length {
|
||||
gost_bytes(ctx, data[j:], 256)
|
||||
j += 32
|
||||
}
|
||||
|
||||
i = 0
|
||||
for j < length {
|
||||
ctx.partial[i] = data[j]
|
||||
i, j = i + 1, j + 1
|
||||
}
|
||||
ctx.partial_bytes = i
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^Gost_Context, hash: []byte) {
|
||||
if ctx.partial_bytes > 0 {
|
||||
mem.set(&ctx.partial[ctx.partial_bytes], 0, 32 - int(ctx.partial_bytes))
|
||||
gost_bytes(ctx, ctx.partial[:], u32(ctx.partial_bytes) << 3)
|
||||
}
|
||||
|
||||
gost_compress(ctx.hash[:], ctx.len[:])
|
||||
gost_compress(ctx.hash[:], ctx.sum[:])
|
||||
|
||||
for i, j := 0, 0; i < 8; i, j = i + 1, j + 4 {
|
||||
hash[j] = byte(ctx.hash[i])
|
||||
hash[j + 1] = byte(ctx.hash[i] >> 8)
|
||||
hash[j + 2] = byte(ctx.hash[i] >> 16)
|
||||
hash[j + 3] = byte(ctx.hash[i] >> 24)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,730 @@
|
||||
package groestl
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the GROESTL hashing algorithm, as defined in <http://www.groestl.info/Groestl.zip>
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_28 = hash_bytes_odin_28
|
||||
ctx.hash_file_28 = hash_file_odin_28
|
||||
ctx.hash_stream_28 = hash_stream_odin_28
|
||||
ctx.hash_bytes_32 = hash_bytes_odin_32
|
||||
ctx.hash_file_32 = hash_file_odin_32
|
||||
ctx.hash_stream_32 = hash_stream_odin_32
|
||||
ctx.hash_bytes_48 = hash_bytes_odin_48
|
||||
ctx.hash_file_48 = hash_file_odin_48
|
||||
ctx.hash_stream_48 = hash_stream_odin_48
|
||||
ctx.hash_bytes_64 = hash_bytes_odin_64
|
||||
ctx.hash_file_64 = hash_file_odin_64
|
||||
ctx.hash_stream_64 = hash_stream_odin_64
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan does nothing, since GROESTL is not available in Botan
|
||||
@(warning="GROESTL is not provided by the Botan API. Odin implementation will be used")
|
||||
use_botan :: #force_inline proc() {
|
||||
use_odin()
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_groestl_ctx :: #force_inline proc(size: _ctx.Hash_Size) {
|
||||
ctx: Groestl_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = size
|
||||
#partial switch size {
|
||||
case ._28: ctx.hashbitlen = 224
|
||||
case ._32: ctx.hashbitlen = 256
|
||||
case ._48: ctx.hashbitlen = 384
|
||||
case ._64: ctx.hashbitlen = 512
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// hash_string_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_224 :: proc(data: string) -> [28]byte {
|
||||
return hash_bytes_224(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_224 :: proc(data: []byte) -> [28]byte {
|
||||
_create_groestl_ctx(._28)
|
||||
return _hash_impl->hash_bytes_28(data)
|
||||
}
|
||||
|
||||
// hash_stream_224 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
|
||||
_create_groestl_ctx(._28)
|
||||
return _hash_impl->hash_stream_28(s)
|
||||
}
|
||||
|
||||
// hash_file_224 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
|
||||
_create_groestl_ctx(._28)
|
||||
return _hash_impl->hash_file_28(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_224 :: proc {
|
||||
hash_stream_224,
|
||||
hash_file_224,
|
||||
hash_bytes_224,
|
||||
hash_string_224,
|
||||
}
|
||||
|
||||
// hash_string_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_256 :: proc(data: string) -> [32]byte {
|
||||
return hash_bytes_256(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_256 :: proc(data: []byte) -> [32]byte {
|
||||
_create_groestl_ctx(._32)
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
}
|
||||
|
||||
// hash_stream_256 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
|
||||
_create_groestl_ctx(._32)
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
}
|
||||
|
||||
// hash_file_256 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
_create_groestl_ctx(._32)
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_256 :: proc {
|
||||
hash_stream_256,
|
||||
hash_file_256,
|
||||
hash_bytes_256,
|
||||
hash_string_256,
|
||||
}
|
||||
|
||||
// hash_string_384 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_384 :: proc(data: string) -> [48]byte {
|
||||
return hash_bytes_384(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_384 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_384 :: proc(data: []byte) -> [48]byte {
|
||||
_create_groestl_ctx(._48)
|
||||
return _hash_impl->hash_bytes_48(data)
|
||||
}
|
||||
|
||||
// hash_stream_384 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
|
||||
_create_groestl_ctx(._48)
|
||||
return _hash_impl->hash_stream_48(s)
|
||||
}
|
||||
|
||||
// hash_file_384 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
|
||||
_create_groestl_ctx(._48)
|
||||
return _hash_impl->hash_file_48(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_384 :: proc {
|
||||
hash_stream_384,
|
||||
hash_file_384,
|
||||
hash_bytes_384,
|
||||
hash_string_384,
|
||||
}
|
||||
|
||||
// hash_string_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_512 :: proc(data: string) -> [64]byte {
|
||||
return hash_bytes_512(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_512 :: proc(data: []byte) -> [64]byte {
|
||||
_create_groestl_ctx(._64)
|
||||
return _hash_impl->hash_bytes_64(data)
|
||||
}
|
||||
|
||||
// hash_stream_512 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
|
||||
_create_groestl_ctx(._64)
|
||||
return _hash_impl->hash_stream_64(s)
|
||||
}
|
||||
|
||||
// hash_file_512 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
_create_groestl_ctx(._64)
|
||||
return _hash_impl->hash_file_64(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_512 :: proc {
|
||||
hash_stream_512,
|
||||
hash_file_512,
|
||||
hash_bytes_512,
|
||||
hash_string_512,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte {
|
||||
hash: [28]byte
|
||||
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) {
|
||||
hash: [28]byte
|
||||
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_28(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_28(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [28]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_32(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [32]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte {
|
||||
hash: [48]byte
|
||||
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([48]byte, bool) {
|
||||
hash: [48]byte
|
||||
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_48(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_48(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [48]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_64(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_64(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [64]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_groestl_ctx(ctx.hash_size)
|
||||
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
GROESTL implementation
|
||||
*/
|
||||
|
||||
SBOX := [256]byte {
|
||||
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
|
||||
0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
|
||||
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
|
||||
0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
|
||||
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
|
||||
0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
|
||||
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
|
||||
0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
|
||||
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
|
||||
0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
|
||||
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
|
||||
0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
|
||||
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
|
||||
0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
|
||||
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
|
||||
0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
|
||||
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
|
||||
0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
|
||||
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
|
||||
0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
|
||||
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
|
||||
0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
|
||||
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
|
||||
0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
|
||||
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
|
||||
0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
|
||||
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
|
||||
0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
|
||||
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
|
||||
0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
|
||||
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
|
||||
0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
|
||||
}
|
||||
|
||||
SHIFT := [2][2][8]int {
|
||||
{{0, 1, 2, 3, 4, 5, 6, 7}, {1, 3, 5, 7, 0, 2, 4, 6}},
|
||||
{{0, 1, 2, 3, 4, 5, 6, 11}, {1, 3, 5, 11, 0, 2, 4, 6}},
|
||||
}
|
||||
|
||||
Groestl_Context :: struct {
|
||||
chaining: [8][16]byte,
|
||||
block_counter: u64,
|
||||
hashbitlen: int,
|
||||
buffer: [128]byte,
|
||||
buf_ptr: int,
|
||||
bits_in_last_byte: int,
|
||||
columns: int,
|
||||
rounds: int,
|
||||
statesize: int,
|
||||
}
|
||||
|
||||
Groestl_Variant :: enum {
|
||||
P512 = 0,
|
||||
Q512 = 1,
|
||||
P1024 = 2,
|
||||
Q1024 = 3,
|
||||
}
|
||||
|
||||
MUL2 :: #force_inline proc "contextless"(b: byte) -> byte {
|
||||
return (b >> 7) != 0 ? (b << 1) ~ 0x1b : (b << 1)
|
||||
}
|
||||
|
||||
MUL3 :: #force_inline proc "contextless"(b: byte) -> byte {
|
||||
return MUL2(b) ~ b
|
||||
}
|
||||
|
||||
MUL4 :: #force_inline proc "contextless"(b: byte) -> byte {
|
||||
return MUL2(MUL2(b))
|
||||
}
|
||||
|
||||
MUL5 :: #force_inline proc "contextless"(b: byte) -> byte {
|
||||
return MUL4(b) ~ b
|
||||
}
|
||||
|
||||
MUL6 :: #force_inline proc "contextless"(b: byte) -> byte {
|
||||
return MUL4(b) ~ MUL2(b)
|
||||
}
|
||||
|
||||
MUL7 :: #force_inline proc "contextless"(b: byte) -> byte {
|
||||
return MUL4(b) ~ MUL2(b) ~ b
|
||||
}
|
||||
|
||||
sub_bytes :: #force_inline proc (x: [][16]byte, columns: int) {
|
||||
for i := 0; i < 8; i += 1 {
|
||||
for j := 0; j < columns; j += 1 {
|
||||
x[i][j] = SBOX[x[i][j]]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shift_bytes :: #force_inline proc (x: [][16]byte, columns: int, v: Groestl_Variant) {
|
||||
temp: [16]byte
|
||||
R := &SHIFT[int(v) / 2][int(v) & 1]
|
||||
|
||||
for i := 0; i < 8; i += 1 {
|
||||
for j := 0; j < columns; j += 1 {
|
||||
temp[j] = x[i][(j + R[i]) % columns]
|
||||
}
|
||||
for j := 0; j < columns; j += 1 {
|
||||
x[i][j] = temp[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mix_bytes :: #force_inline proc (x: [][16]byte, columns: int) {
|
||||
temp: [8]byte
|
||||
|
||||
for i := 0; i < columns; i += 1 {
|
||||
for j := 0; j < 8; j += 1 {
|
||||
temp[j] = MUL2(x[(j + 0) % 8][i]) ~
|
||||
MUL2(x[(j + 1) % 8][i]) ~
|
||||
MUL3(x[(j + 2) % 8][i]) ~
|
||||
MUL4(x[(j + 3) % 8][i]) ~
|
||||
MUL5(x[(j + 4) % 8][i]) ~
|
||||
MUL3(x[(j + 5) % 8][i]) ~
|
||||
MUL5(x[(j + 6) % 8][i]) ~
|
||||
MUL7(x[(j + 7) % 8][i])
|
||||
}
|
||||
for j := 0; j < 8; j += 1 {
|
||||
x[j][i] = temp[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p :: #force_inline proc (ctx: ^Groestl_Context, x: [][16]byte) {
|
||||
v := ctx.columns == 8 ? Groestl_Variant.P512 : Groestl_Variant.P1024
|
||||
for i := 0; i < ctx.rounds; i += 1 {
|
||||
add_roundconstant(x, ctx.columns, byte(i), v)
|
||||
sub_bytes(x, ctx.columns)
|
||||
shift_bytes(x, ctx.columns, v)
|
||||
mix_bytes(x, ctx.columns)
|
||||
}
|
||||
}
|
||||
|
||||
q :: #force_inline proc (ctx: ^Groestl_Context, x: [][16]byte) {
|
||||
v := ctx.columns == 8 ? Groestl_Variant.Q512 : Groestl_Variant.Q1024
|
||||
for i := 0; i < ctx.rounds; i += 1 {
|
||||
add_roundconstant(x, ctx.columns, byte(i), v)
|
||||
sub_bytes(x, ctx.columns)
|
||||
shift_bytes(x, ctx.columns, v)
|
||||
mix_bytes(x, ctx.columns)
|
||||
}
|
||||
}
|
||||
|
||||
transform :: proc(ctx: ^Groestl_Context, input: []byte, msglen: u32) {
|
||||
tmp1, tmp2: [8][16]byte
|
||||
input, msglen := input, msglen
|
||||
|
||||
for msglen >= u32(ctx.statesize) {
|
||||
for i := 0; i < 8; i += 1 {
|
||||
for j := 0; j < ctx.columns; j += 1 {
|
||||
tmp1[i][j] = ctx.chaining[i][j] ~ input[j * 8 + i]
|
||||
tmp2[i][j] = input[j * 8 + i]
|
||||
}
|
||||
}
|
||||
|
||||
p(ctx, tmp1[:])
|
||||
q(ctx, tmp2[:])
|
||||
|
||||
for i := 0; i < 8; i += 1 {
|
||||
for j := 0; j < ctx.columns; j += 1 {
|
||||
ctx.chaining[i][j] ~= tmp1[i][j] ~ tmp2[i][j]
|
||||
}
|
||||
}
|
||||
|
||||
ctx.block_counter += 1
|
||||
msglen -= u32(ctx.statesize)
|
||||
input = input[ctx.statesize:]
|
||||
}
|
||||
}
|
||||
|
||||
output_transformation :: proc(ctx: ^Groestl_Context) {
|
||||
temp: [8][16]byte
|
||||
|
||||
for i := 0; i < 8; i += 1 {
|
||||
for j := 0; j < ctx.columns; j += 1 {
|
||||
temp[i][j] = ctx.chaining[i][j]
|
||||
}
|
||||
}
|
||||
|
||||
p(ctx, temp[:])
|
||||
|
||||
for i := 0; i < 8; i += 1 {
|
||||
for j := 0; j < ctx.columns; j += 1 {
|
||||
ctx.chaining[i][j] ~= temp[i][j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add_roundconstant :: proc(x: [][16]byte, columns: int, round: byte, v: Groestl_Variant) {
|
||||
switch (i32(v) & 1) {
|
||||
case 0:
|
||||
for i := 0; i < columns; i += 1 {
|
||||
x[0][i] ~= byte(i << 4) ~ round
|
||||
}
|
||||
case 1:
|
||||
for i := 0; i < columns; i += 1 {
|
||||
for j := 0; j < 7; j += 1 {
|
||||
x[j][i] ~= 0xff
|
||||
}
|
||||
}
|
||||
for i := 0; i < columns; i += 1 {
|
||||
x[7][i] ~= byte(i << 4) ~ 0xff ~ round
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^Groestl_Context) {
|
||||
if ctx.hashbitlen <= 256 {
|
||||
ctx.rounds = 10
|
||||
ctx.columns = 8
|
||||
ctx.statesize = 64
|
||||
} else {
|
||||
ctx.rounds = 14
|
||||
ctx.columns = 16
|
||||
ctx.statesize = 128
|
||||
}
|
||||
for i := 8 - size_of(i32); i < 8; i += 1 {
|
||||
ctx.chaining[i][ctx.columns - 1] = byte(ctx.hashbitlen >> (8 * (7 - uint(i))))
|
||||
}
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^Groestl_Context, data: []byte) {
|
||||
databitlen := len(data) * 8
|
||||
msglen := databitlen / 8
|
||||
rem := databitlen % 8
|
||||
|
||||
i: int
|
||||
assert(ctx.bits_in_last_byte == 0)
|
||||
|
||||
if ctx.buf_ptr != 0 {
|
||||
for i = 0; ctx.buf_ptr < ctx.statesize && i < msglen; i, ctx.buf_ptr = i + 1, ctx.buf_ptr + 1 {
|
||||
ctx.buffer[ctx.buf_ptr] = data[i]
|
||||
}
|
||||
|
||||
if ctx.buf_ptr < ctx.statesize {
|
||||
if rem != 0 {
|
||||
ctx.bits_in_last_byte = rem
|
||||
ctx.buffer[ctx.buf_ptr] = data[i]
|
||||
ctx.buf_ptr += 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ctx.buf_ptr = 0
|
||||
transform(ctx, ctx.buffer[:], u32(ctx.statesize))
|
||||
}
|
||||
|
||||
transform(ctx, data[i:], u32(msglen - i))
|
||||
i += ((msglen - i) / ctx.statesize) * ctx.statesize
|
||||
for i < msglen {
|
||||
ctx.buffer[ctx.buf_ptr] = data[i]
|
||||
i, ctx.buf_ptr = i + 1, ctx.buf_ptr + 1
|
||||
}
|
||||
|
||||
if rem != 0 {
|
||||
ctx.bits_in_last_byte = rem
|
||||
ctx.buffer[ctx.buf_ptr] = data[i]
|
||||
ctx.buf_ptr += 1
|
||||
}
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^Groestl_Context, hash: []byte) {
|
||||
hashbytelen := ctx.hashbitlen / 8
|
||||
|
||||
if ctx.bits_in_last_byte != 0 {
|
||||
ctx.buffer[ctx.buf_ptr - 1] &= ((1 << uint(ctx.bits_in_last_byte)) - 1) << (8 - uint(ctx.bits_in_last_byte))
|
||||
ctx.buffer[ctx.buf_ptr - 1] ~= 0x1 << (7 - uint(ctx.bits_in_last_byte))
|
||||
} else {
|
||||
ctx.buffer[ctx.buf_ptr] = 0x80
|
||||
ctx.buf_ptr += 1
|
||||
}
|
||||
|
||||
if ctx.buf_ptr > ctx.statesize - 8 {
|
||||
for ctx.buf_ptr < ctx.statesize {
|
||||
ctx.buffer[ctx.buf_ptr] = 0
|
||||
ctx.buf_ptr += 1
|
||||
}
|
||||
transform(ctx, ctx.buffer[:], u32(ctx.statesize))
|
||||
ctx.buf_ptr = 0
|
||||
}
|
||||
|
||||
for ctx.buf_ptr < ctx.statesize - 8 {
|
||||
ctx.buffer[ctx.buf_ptr] = 0
|
||||
ctx.buf_ptr += 1
|
||||
}
|
||||
|
||||
ctx.block_counter += 1
|
||||
ctx.buf_ptr = ctx.statesize
|
||||
|
||||
for ctx.buf_ptr > ctx.statesize - 8 {
|
||||
ctx.buf_ptr -= 1
|
||||
ctx.buffer[ctx.buf_ptr] = byte(ctx.block_counter)
|
||||
ctx.block_counter >>= 8
|
||||
}
|
||||
|
||||
transform(ctx, ctx.buffer[:], u32(ctx.statesize))
|
||||
output_transformation(ctx)
|
||||
|
||||
for i, j := ctx.statesize - hashbytelen , 0; i < ctx.statesize; i, j = i + 1, j + 1 {
|
||||
hash[j] = ctx.chaining[i % 8][i / 8]
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,661 @@
|
||||
package jh
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the JH hashing algorithm, as defined in <https://www3.ntu.edu.sg/home/wuhj/research/jh/index.html>
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_28 = hash_bytes_odin_28
|
||||
ctx.hash_file_28 = hash_file_odin_28
|
||||
ctx.hash_stream_28 = hash_stream_odin_28
|
||||
ctx.hash_bytes_32 = hash_bytes_odin_32
|
||||
ctx.hash_file_32 = hash_file_odin_32
|
||||
ctx.hash_stream_32 = hash_stream_odin_32
|
||||
ctx.hash_bytes_48 = hash_bytes_odin_48
|
||||
ctx.hash_file_48 = hash_file_odin_48
|
||||
ctx.hash_stream_48 = hash_stream_odin_48
|
||||
ctx.hash_bytes_64 = hash_bytes_odin_64
|
||||
ctx.hash_file_64 = hash_file_odin_64
|
||||
ctx.hash_stream_64 = hash_stream_odin_64
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan does nothing, since JH is not available in Botan
|
||||
@(warning="JH is not provided by the Botan API. Odin implementation will be used")
|
||||
use_botan :: #force_inline proc() {
|
||||
use_odin()
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_jh_ctx :: #force_inline proc(size: _ctx.Hash_Size) {
|
||||
ctx: Jh_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = size
|
||||
#partial switch size {
|
||||
case ._28: ctx.hashbitlen = 224
|
||||
case ._32: ctx.hashbitlen = 256
|
||||
case ._48: ctx.hashbitlen = 384
|
||||
case ._64: ctx.hashbitlen = 512
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// hash_string_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_224 :: proc(data: string) -> [28]byte {
|
||||
return hash_bytes_224(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_224 :: proc(data: []byte) -> [28]byte {
|
||||
_create_jh_ctx(._28)
|
||||
return _hash_impl->hash_bytes_28(data)
|
||||
}
|
||||
|
||||
// hash_stream_224 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
|
||||
_create_jh_ctx(._28)
|
||||
return _hash_impl->hash_stream_28(s)
|
||||
}
|
||||
|
||||
// hash_file_224 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
|
||||
_create_jh_ctx(._28)
|
||||
return _hash_impl->hash_file_28(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_224 :: proc {
|
||||
hash_stream_224,
|
||||
hash_file_224,
|
||||
hash_bytes_224,
|
||||
hash_string_224,
|
||||
}
|
||||
|
||||
// hash_string_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_256 :: proc(data: string) -> [32]byte {
|
||||
return hash_bytes_256(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_256 :: proc(data: []byte) -> [32]byte {
|
||||
_create_jh_ctx(._32)
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
}
|
||||
|
||||
// hash_stream_256 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
|
||||
_create_jh_ctx(._32)
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
}
|
||||
|
||||
// hash_file_256 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
_create_jh_ctx(._32)
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_256 :: proc {
|
||||
hash_stream_256,
|
||||
hash_file_256,
|
||||
hash_bytes_256,
|
||||
hash_string_256,
|
||||
}
|
||||
|
||||
// hash_string_384 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_384 :: proc(data: string) -> [48]byte {
|
||||
return hash_bytes_384(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_384 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_384 :: proc(data: []byte) -> [48]byte {
|
||||
_create_jh_ctx(._48)
|
||||
return _hash_impl->hash_bytes_48(data)
|
||||
}
|
||||
|
||||
// hash_stream_384 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
|
||||
_create_jh_ctx(._48)
|
||||
return _hash_impl->hash_stream_48(s)
|
||||
}
|
||||
|
||||
// hash_file_384 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
|
||||
_create_jh_ctx(._48)
|
||||
return _hash_impl->hash_file_48(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_384 :: proc {
|
||||
hash_stream_384,
|
||||
hash_file_384,
|
||||
hash_bytes_384,
|
||||
hash_string_384,
|
||||
}
|
||||
|
||||
// hash_string_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_512 :: proc(data: string) -> [64]byte {
|
||||
return hash_bytes_512(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_512 :: proc(data: []byte) -> [64]byte {
|
||||
_create_jh_ctx(._64)
|
||||
return _hash_impl->hash_bytes_64(data)
|
||||
}
|
||||
|
||||
// hash_stream_512 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
|
||||
_create_jh_ctx(._64)
|
||||
return _hash_impl->hash_stream_64(s)
|
||||
}
|
||||
|
||||
// hash_file_512 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
_create_jh_ctx(._64)
|
||||
return _hash_impl->hash_file_64(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_512 :: proc {
|
||||
hash_stream_512,
|
||||
hash_file_512,
|
||||
hash_bytes_512,
|
||||
hash_string_512,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte {
|
||||
hash: [28]byte
|
||||
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) {
|
||||
hash: [28]byte
|
||||
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_28(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_28(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [28]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_32(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [32]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte {
|
||||
hash: [48]byte
|
||||
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([48]byte, bool) {
|
||||
hash: [48]byte
|
||||
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_48(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_48(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [48]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_64(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_64(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [64]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_jh_ctx(ctx.hash_size)
|
||||
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
JH implementation
|
||||
*/
|
||||
|
||||
JH_ROUNDCONSTANT_ZERO := [64]byte {
|
||||
0x6, 0xa, 0x0, 0x9, 0xe, 0x6, 0x6, 0x7,
|
||||
0xf, 0x3, 0xb, 0xc, 0xc, 0x9, 0x0, 0x8,
|
||||
0xb, 0x2, 0xf, 0xb, 0x1, 0x3, 0x6, 0x6,
|
||||
0xe, 0xa, 0x9, 0x5, 0x7, 0xd, 0x3, 0xe,
|
||||
0x3, 0xa, 0xd, 0xe, 0xc, 0x1, 0x7, 0x5,
|
||||
0x1, 0x2, 0x7, 0x7, 0x5, 0x0, 0x9, 0x9,
|
||||
0xd, 0xa, 0x2, 0xf, 0x5, 0x9, 0x0, 0xb,
|
||||
0x0, 0x6, 0x6, 0x7, 0x3, 0x2, 0x2, 0xa,
|
||||
}
|
||||
|
||||
JH_S := [2][16]byte {
|
||||
{9, 0, 4, 11, 13, 12, 3, 15, 1, 10, 2, 6, 7, 5, 8, 14},
|
||||
{3, 12, 6, 13, 5, 7, 1, 9, 15, 2, 0, 4, 11, 10, 14, 8},
|
||||
}
|
||||
|
||||
Jh_Context :: struct {
|
||||
hashbitlen: int,
|
||||
databitlen: u64,
|
||||
buffer_size: u64,
|
||||
H: [128]byte,
|
||||
A: [256]byte,
|
||||
roundconstant: [64]byte,
|
||||
buffer: [64]byte,
|
||||
}
|
||||
|
||||
JH_E8_finaldegroup :: proc(ctx: ^Jh_Context) {
|
||||
t0,t1,t2,t3: byte
|
||||
tem: [256]byte
|
||||
for i := 0; i < 128; i += 1 {
|
||||
tem[i] = ctx.A[i << 1]
|
||||
tem[i + 128] = ctx.A[(i << 1) + 1]
|
||||
}
|
||||
for i := 0; i < 128; i += 1 {
|
||||
ctx.H[i] = 0
|
||||
}
|
||||
for i := 0; i < 256; i += 1 {
|
||||
t0 = (tem[i] >> 3) & 1
|
||||
t1 = (tem[i] >> 2) & 1
|
||||
t2 = (tem[i] >> 1) & 1
|
||||
t3 = (tem[i] >> 0) & 1
|
||||
|
||||
ctx.H[uint(i) >> 3] |= t0 << (7 - (uint(i) & 7))
|
||||
ctx.H[(uint(i) + 256) >> 3] |= t1 << (7 - (uint(i) & 7))
|
||||
ctx.H[(uint(i) + 512) >> 3] |= t2 << (7 - (uint(i) & 7))
|
||||
ctx.H[(uint(i) + 768) >> 3] |= t3 << (7 - (uint(i) & 7))
|
||||
}
|
||||
}
|
||||
|
||||
jh_update_roundconstant :: proc(ctx: ^Jh_Context) {
|
||||
tem: [64]byte
|
||||
t: byte
|
||||
for i := 0; i < 64; i += 1 {
|
||||
tem[i] = JH_S[0][ctx.roundconstant[i]]
|
||||
}
|
||||
for i := 0; i < 64; i += 2 {
|
||||
tem[i + 1] ~= ((tem[i] << 1) ~ (tem[i] >> 3) ~ ((tem[i] >> 2) & 2)) & 0xf
|
||||
tem[i] ~= ((tem[i + 1] << 1) ~ (tem[i + 1] >> 3) ~ ((tem[i + 1] >> 2) & 2)) & 0xf
|
||||
}
|
||||
for i := 0; i < 64; i += 4 {
|
||||
t = tem[i + 2]
|
||||
tem[i + 2] = tem[i + 3]
|
||||
tem[i + 3] = t
|
||||
}
|
||||
for i := 0; i < 32; i += 1 {
|
||||
ctx.roundconstant[i] = tem[i << 1]
|
||||
ctx.roundconstant[i + 32] = tem[(i << 1) + 1]
|
||||
}
|
||||
for i := 32; i < 64; i += 2 {
|
||||
t = ctx.roundconstant[i]
|
||||
ctx.roundconstant[i] = ctx.roundconstant[i + 1]
|
||||
ctx.roundconstant[i + 1] = t
|
||||
}
|
||||
}
|
||||
|
||||
JH_R8 :: proc(ctx: ^Jh_Context) {
|
||||
t: byte
|
||||
tem, roundconstant_expanded: [256]byte
|
||||
for i := u32(0); i < 256; i += 1 {
|
||||
roundconstant_expanded[i] = (ctx.roundconstant[i >> 2] >> (3 - (i & 3)) ) & 1
|
||||
}
|
||||
for i := 0; i < 256; i += 1 {
|
||||
tem[i] = JH_S[roundconstant_expanded[i]][ctx.A[i]]
|
||||
}
|
||||
for i := 0; i < 256; i += 2 {
|
||||
tem[i+1] ~= ((tem[i] << 1) ~ (tem[i] >> 3) ~ ((tem[i] >> 2) & 2)) & 0xf
|
||||
tem[i] ~= ((tem[i + 1] << 1) ~ (tem[i + 1] >> 3) ~ ((tem[i + 1] >> 2) & 2)) & 0xf
|
||||
}
|
||||
for i := 0; i < 256; i += 4 {
|
||||
t = tem[i + 2]
|
||||
tem[i+2] = tem[i + 3]
|
||||
tem[i+3] = t
|
||||
}
|
||||
for i := 0; i < 128; i += 1 {
|
||||
ctx.A[i] = tem[i << 1]
|
||||
ctx.A[i + 128] = tem[(i << 1) + 1]
|
||||
}
|
||||
for i := 128; i < 256; i += 2 {
|
||||
t = ctx.A[i]
|
||||
ctx.A[i] = ctx.A[i + 1]
|
||||
ctx.A[i + 1] = t
|
||||
}
|
||||
}
|
||||
|
||||
JH_E8_initialgroup :: proc(ctx: ^Jh_Context) {
|
||||
t0, t1, t2, t3: byte
|
||||
tem: [256]byte
|
||||
for i := u32(0); i < 256; i += 1 {
|
||||
t0 = (ctx.H[i >> 3] >> (7 - (i & 7))) & 1
|
||||
t1 = (ctx.H[(i + 256) >> 3] >> (7 - (i & 7))) & 1
|
||||
t2 = (ctx.H[(i + 512) >> 3] >> (7 - (i & 7))) & 1
|
||||
t3 = (ctx.H[(i + 768) >> 3] >> (7 - (i & 7))) & 1
|
||||
tem[i] = (t0 << 3) | (t1 << 2) | (t2 << 1) | (t3 << 0)
|
||||
}
|
||||
for i := 0; i < 128; i += 1 {
|
||||
ctx.A[i << 1] = tem[i]
|
||||
ctx.A[(i << 1) + 1] = tem[i + 128]
|
||||
}
|
||||
}
|
||||
|
||||
JH_E8 :: proc(ctx: ^Jh_Context) {
|
||||
for i := 0; i < 64; i += 1 {
|
||||
ctx.roundconstant[i] = JH_ROUNDCONSTANT_ZERO[i]
|
||||
}
|
||||
JH_E8_initialgroup(ctx)
|
||||
for i := 0; i < 42; i += 1 {
|
||||
JH_R8(ctx)
|
||||
jh_update_roundconstant(ctx)
|
||||
}
|
||||
JH_E8_finaldegroup(ctx)
|
||||
}
|
||||
|
||||
JH_F8 :: proc(ctx: ^Jh_Context) {
|
||||
for i := 0; i < 64; i += 1 {
|
||||
ctx.H[i] ~= ctx.buffer[i]
|
||||
}
|
||||
JH_E8(ctx)
|
||||
for i := 0; i < 64; i += 1 {
|
||||
ctx.H[i + 64] ~= ctx.buffer[i]
|
||||
}
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^Jh_Context) {
|
||||
ctx.H[1] = byte(ctx.hashbitlen) & 0xff
|
||||
ctx.H[0] = byte(ctx.hashbitlen >> 8) & 0xff
|
||||
JH_F8(ctx)
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^Jh_Context, data: []byte) {
|
||||
databitlen := u64(len(data)) * 8
|
||||
ctx.databitlen += databitlen
|
||||
i := u64(0)
|
||||
|
||||
if (ctx.buffer_size > 0) && ((ctx.buffer_size + databitlen) < 512) {
|
||||
if (databitlen & 7) == 0 {
|
||||
copy(ctx.buffer[ctx.buffer_size >> 3:], data[:64 - (ctx.buffer_size >> 3)])
|
||||
} else {
|
||||
copy(ctx.buffer[ctx.buffer_size >> 3:], data[:64 - (ctx.buffer_size >> 3) + 1])
|
||||
}
|
||||
ctx.buffer_size += databitlen
|
||||
databitlen = 0
|
||||
}
|
||||
|
||||
if (ctx.buffer_size > 0 ) && ((ctx.buffer_size + databitlen) >= 512) {
|
||||
copy(ctx.buffer[ctx.buffer_size >> 3:], data[:64 - (ctx.buffer_size >> 3)])
|
||||
i = 64 - (ctx.buffer_size >> 3)
|
||||
databitlen = databitlen - (512 - ctx.buffer_size)
|
||||
JH_F8(ctx)
|
||||
ctx.buffer_size = 0
|
||||
}
|
||||
|
||||
for databitlen >= 512 {
|
||||
copy(ctx.buffer[:], data[i:i + 64])
|
||||
JH_F8(ctx)
|
||||
i += 64
|
||||
databitlen -= 512
|
||||
}
|
||||
|
||||
if databitlen > 0 {
|
||||
if (databitlen & 7) == 0 {
|
||||
copy(ctx.buffer[:], data[i:i + ((databitlen & 0x1ff) >> 3)])
|
||||
} else {
|
||||
copy(ctx.buffer[:], data[i:i + ((databitlen & 0x1ff) >> 3) + 1])
|
||||
}
|
||||
ctx.buffer_size = databitlen
|
||||
}
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^Jh_Context, hash: []byte) {
|
||||
if ctx.databitlen & 0x1ff == 0 {
|
||||
for i := 0; i < 64; i += 1 {
|
||||
ctx.buffer[i] = 0
|
||||
}
|
||||
ctx.buffer[0] = 0x80
|
||||
ctx.buffer[63] = byte(ctx.databitlen) & 0xff
|
||||
ctx.buffer[62] = byte(ctx.databitlen >> 8) & 0xff
|
||||
ctx.buffer[61] = byte(ctx.databitlen >> 16) & 0xff
|
||||
ctx.buffer[60] = byte(ctx.databitlen >> 24) & 0xff
|
||||
ctx.buffer[59] = byte(ctx.databitlen >> 32) & 0xff
|
||||
ctx.buffer[58] = byte(ctx.databitlen >> 40) & 0xff
|
||||
ctx.buffer[57] = byte(ctx.databitlen >> 48) & 0xff
|
||||
ctx.buffer[56] = byte(ctx.databitlen >> 56) & 0xff
|
||||
JH_F8(ctx)
|
||||
} else {
|
||||
if ctx.buffer_size & 7 == 0 {
|
||||
for i := (ctx.databitlen & 0x1ff) >> 3; i < 64; i += 1 {
|
||||
ctx.buffer[i] = 0
|
||||
}
|
||||
} else {
|
||||
for i := ((ctx.databitlen & 0x1ff) >> 3) + 1; i < 64; i += 1 {
|
||||
ctx.buffer[i] = 0
|
||||
}
|
||||
}
|
||||
ctx.buffer[(ctx.databitlen & 0x1ff) >> 3] |= 1 << (7 - (ctx.databitlen & 7))
|
||||
JH_F8(ctx)
|
||||
for i := 0; i < 64; i += 1 {
|
||||
ctx.buffer[i] = 0
|
||||
}
|
||||
ctx.buffer[63] = byte(ctx.databitlen) & 0xff
|
||||
ctx.buffer[62] = byte(ctx.databitlen >> 8) & 0xff
|
||||
ctx.buffer[61] = byte(ctx.databitlen >> 16) & 0xff
|
||||
ctx.buffer[60] = byte(ctx.databitlen >> 24) & 0xff
|
||||
ctx.buffer[59] = byte(ctx.databitlen >> 32) & 0xff
|
||||
ctx.buffer[58] = byte(ctx.databitlen >> 40) & 0xff
|
||||
ctx.buffer[57] = byte(ctx.databitlen >> 48) & 0xff
|
||||
ctx.buffer[56] = byte(ctx.databitlen >> 56) & 0xff
|
||||
JH_F8(ctx)
|
||||
}
|
||||
switch ctx.hashbitlen {
|
||||
case 224: copy(hash[:], ctx.H[100:128])
|
||||
case 256: copy(hash[:], ctx.H[96:128])
|
||||
case 384: copy(hash[:], ctx.H[80:128])
|
||||
case 512: copy(hash[:], ctx.H[64:128])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,429 @@
|
||||
package keccak
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Interface for the Keccak hashing algorithm.
|
||||
This is done because the padding in the SHA3 standard was changed by the NIST, resulting in a different output.
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
import "../_sha3"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_28 = hash_bytes_odin_28
|
||||
ctx.hash_file_28 = hash_file_odin_28
|
||||
ctx.hash_stream_28 = hash_stream_odin_28
|
||||
ctx.hash_bytes_32 = hash_bytes_odin_32
|
||||
ctx.hash_file_32 = hash_file_odin_32
|
||||
ctx.hash_stream_32 = hash_stream_odin_32
|
||||
ctx.hash_bytes_48 = hash_bytes_odin_48
|
||||
ctx.hash_file_48 = hash_file_odin_48
|
||||
ctx.hash_stream_48 = hash_stream_odin_48
|
||||
ctx.hash_bytes_64 = hash_bytes_odin_64
|
||||
ctx.hash_file_64 = hash_file_odin_64
|
||||
ctx.hash_stream_64 = hash_stream_odin_64
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_KECCAK)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// hash_string_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_224 :: proc(data: string) -> [28]byte {
|
||||
return hash_bytes_224(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_224 :: proc(data: []byte) -> [28]byte {
|
||||
_create_keccak_ctx(28)
|
||||
return _hash_impl->hash_bytes_28(data)
|
||||
}
|
||||
|
||||
// hash_stream_224 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
|
||||
_create_keccak_ctx(28)
|
||||
return _hash_impl->hash_stream_28(s)
|
||||
}
|
||||
|
||||
// hash_file_224 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
|
||||
_create_keccak_ctx(28)
|
||||
return _hash_impl->hash_file_28(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_224 :: proc {
|
||||
hash_stream_224,
|
||||
hash_file_224,
|
||||
hash_bytes_224,
|
||||
hash_string_224,
|
||||
}
|
||||
|
||||
// hash_string_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_256 :: proc(data: string) -> [32]byte {
|
||||
return hash_bytes_256(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_256 :: proc(data: []byte) -> [32]byte {
|
||||
_create_keccak_ctx(32)
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
}
|
||||
|
||||
// hash_stream_256 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
|
||||
_create_keccak_ctx(32)
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
}
|
||||
|
||||
// hash_file_256 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
_create_keccak_ctx(32)
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_256 :: proc {
|
||||
hash_stream_256,
|
||||
hash_file_256,
|
||||
hash_bytes_256,
|
||||
hash_string_256,
|
||||
}
|
||||
|
||||
// hash_string_384 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_384 :: proc(data: string) -> [48]byte {
|
||||
return hash_bytes_384(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_384 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_384 :: proc(data: []byte) -> [48]byte {
|
||||
_create_keccak_ctx(48)
|
||||
return _hash_impl->hash_bytes_48(data)
|
||||
}
|
||||
|
||||
// hash_stream_384 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
|
||||
_create_keccak_ctx(48)
|
||||
return _hash_impl->hash_stream_48(s)
|
||||
}
|
||||
|
||||
// hash_file_384 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
|
||||
_create_keccak_ctx(48)
|
||||
return _hash_impl->hash_file_48(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_384 :: proc {
|
||||
hash_stream_384,
|
||||
hash_file_384,
|
||||
hash_bytes_384,
|
||||
hash_string_384,
|
||||
}
|
||||
|
||||
// hash_string_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_512 :: proc(data: string) -> [64]byte {
|
||||
return hash_bytes_512(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_512 :: proc(data: []byte) -> [64]byte {
|
||||
_create_keccak_ctx(64)
|
||||
return _hash_impl->hash_bytes_64(data)
|
||||
}
|
||||
|
||||
// hash_stream_512 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
|
||||
_create_keccak_ctx(64)
|
||||
return _hash_impl->hash_stream_64(s)
|
||||
}
|
||||
|
||||
// hash_file_512 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
_create_keccak_ctx(64)
|
||||
return _hash_impl->hash_file_64(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_512 :: proc {
|
||||
hash_stream_512,
|
||||
hash_file_512,
|
||||
hash_bytes_512,
|
||||
hash_string_512,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte {
|
||||
hash: [28]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
_sha3.update_odin(&c, data)
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) {
|
||||
hash: [28]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_28(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_28(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [28]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
_sha3.update_odin(&c, data)
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_32(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [32]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte {
|
||||
hash: [48]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
_sha3.update_odin(&c, data)
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([48]byte, bool) {
|
||||
hash: [48]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_48(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_48(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [48]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
_sha3.update_odin(&c, data)
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_64(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_64(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [64]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_keccak_ctx :: #force_inline proc(mdlen: int) {
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = mdlen
|
||||
ctx.is_keccak = true
|
||||
_hash_impl.internal_ctx = ctx
|
||||
switch mdlen {
|
||||
case 28: _hash_impl.hash_size = ._28
|
||||
case 32: _hash_impl.hash_size = ._32
|
||||
case 48: _hash_impl.hash_size = ._48
|
||||
case 64: _hash_impl.hash_size = ._64
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
#partial switch ctx.hash_size {
|
||||
case ._28: _create_keccak_ctx(28)
|
||||
case ._32: _create_keccak_ctx(32)
|
||||
case ._48: _create_keccak_ctx(48)
|
||||
case ._64: _create_keccak_ctx(64)
|
||||
}
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
package md2
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the MD2 hashing algorithm, as defined in RFC 1319 <https://datatracker.ietf.org/doc/html/rfc1319>
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_16 = hash_bytes_odin
|
||||
ctx.hash_file_16 = hash_file_odin
|
||||
ctx.hash_stream_16 = hash_stream_odin
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan does nothing, since MD2 is not available in Botan
|
||||
@(warning="MD2 is not provided by the Botan API. Odin implementation will be used")
|
||||
use_botan :: #force_inline proc() {
|
||||
use_odin()
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// hash_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string :: proc(data: string) -> [16]byte {
|
||||
return hash_bytes(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes :: proc(data: []byte) -> [16]byte {
|
||||
_create_md2_ctx()
|
||||
return _hash_impl->hash_bytes_16(data)
|
||||
}
|
||||
|
||||
// hash_stream will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream :: proc(s: io.Stream) -> ([16]byte, bool) {
|
||||
_create_md2_ctx()
|
||||
return _hash_impl->hash_stream_16(s)
|
||||
}
|
||||
|
||||
// hash_file will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
|
||||
_create_md2_ctx()
|
||||
return _hash_impl->hash_file_16(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash :: proc {
|
||||
hash_stream,
|
||||
hash_file,
|
||||
hash_bytes,
|
||||
hash_string,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(Md2_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(Md2_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [16]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_md2_ctx :: #force_inline proc() {
|
||||
ctx: Md2_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._16
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_md2_ctx()
|
||||
if c, ok := ctx.internal_ctx.(Md2_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Md2_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Md2_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
MD2 implementation
|
||||
*/
|
||||
|
||||
Md2_Context :: struct {
|
||||
data: [16]byte,
|
||||
state: [16 * 3]byte,
|
||||
checksum: [16]byte,
|
||||
datalen: int,
|
||||
}
|
||||
|
||||
PI_TABLE := [?]byte {
|
||||
41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6,
|
||||
19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188, 76,
|
||||
130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24, 138,
|
||||
23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251, 245, 142,
|
||||
187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63, 148, 194, 16,
|
||||
137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50, 39, 53, 62,
|
||||
204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165, 181, 209, 215,
|
||||
94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210, 150, 164, 125, 182,
|
||||
118, 252, 107, 226, 156, 116, 4, 241, 69, 157, 112, 89, 100, 113, 135,
|
||||
32, 134, 91, 207, 101, 230, 45, 168, 2, 27, 96, 37, 173, 174, 176,
|
||||
185, 246, 28, 70, 97, 105, 52, 64, 126, 15, 85, 71, 163, 35, 221,
|
||||
81, 175, 58, 195, 92, 249, 206, 186, 197, 234, 38, 44, 83, 13, 110,
|
||||
133, 40, 132, 9, 211, 223, 205, 244, 65, 129, 77, 82, 106, 220, 55,
|
||||
200, 108, 193, 171, 250, 36, 225, 123, 8, 12, 189, 177, 74, 120, 136,
|
||||
149, 139, 227, 99, 232, 109, 233, 203, 213, 254, 59, 0, 29, 57, 242,
|
||||
239, 183, 14, 102, 88, 208, 228, 166, 119, 114, 248, 235, 117, 75, 10,
|
||||
49, 68, 80, 180, 143, 237, 31, 26, 219, 153, 141, 51, 159, 17, 131,
|
||||
20,
|
||||
}
|
||||
|
||||
transform :: proc(ctx: ^Md2_Context, data: []byte) {
|
||||
j,k,t: byte
|
||||
for j = 0; j < 16; j += 1 {
|
||||
ctx.state[j + 16] = data[j]
|
||||
ctx.state[j + 16 * 2] = (ctx.state[j + 16] ~ ctx.state[j])
|
||||
}
|
||||
t = 0
|
||||
for j = 0; j < 16 + 2; j += 1 {
|
||||
for k = 0; k < 16 * 3; k += 1 {
|
||||
ctx.state[k] ~= PI_TABLE[t]
|
||||
t = ctx.state[k]
|
||||
}
|
||||
t = (t + j) & 0xff
|
||||
}
|
||||
t = ctx.checksum[16 - 1]
|
||||
for j = 0; j < 16; j += 1 {
|
||||
ctx.checksum[j] ~= PI_TABLE[data[j] ~ t]
|
||||
t = ctx.checksum[j]
|
||||
}
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^Md2_Context) {
|
||||
// No action needed here
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^Md2_Context, data: []byte) {
|
||||
for i := 0; i < len(data); i += 1 {
|
||||
ctx.data[ctx.datalen] = data[i]
|
||||
ctx.datalen += 1
|
||||
if (ctx.datalen == 16) {
|
||||
transform(ctx, ctx.data[:])
|
||||
ctx.datalen = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^Md2_Context, hash: []byte) {
|
||||
to_pad := byte(16 - ctx.datalen)
|
||||
for ctx.datalen < 16 {
|
||||
ctx.data[ctx.datalen] = to_pad
|
||||
ctx.datalen += 1
|
||||
}
|
||||
transform(ctx, ctx.data[:])
|
||||
transform(ctx, ctx.checksum[:])
|
||||
for i := 0; i < 16; i += 1 {
|
||||
hash[i] = ctx.state[i]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,342 @@
|
||||
package md4
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the MD4 hashing algorithm, as defined in RFC 1320 <https://datatracker.ietf.org/doc/html/rfc1320>
|
||||
*/
|
||||
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../util"
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_16 = hash_bytes_odin
|
||||
ctx.hash_file_16 = hash_file_odin
|
||||
ctx.hash_stream_16 = hash_stream_odin
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_MD4)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// hash_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string :: proc(data: string) -> [16]byte {
|
||||
return hash_bytes(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes :: proc(data: []byte) -> [16]byte {
|
||||
_create_md4_ctx()
|
||||
return _hash_impl->hash_bytes_16(data)
|
||||
}
|
||||
|
||||
// hash_stream will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream :: proc(s: io.Stream) -> ([16]byte, bool) {
|
||||
_create_md4_ctx()
|
||||
return _hash_impl->hash_stream_16(s)
|
||||
}
|
||||
|
||||
// hash_file will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
|
||||
_create_md4_ctx()
|
||||
return _hash_impl->hash_file_16(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash :: proc {
|
||||
hash_stream,
|
||||
hash_file,
|
||||
hash_bytes,
|
||||
hash_string,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(Md4_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(Md4_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [16]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_md4_ctx :: #force_inline proc() {
|
||||
ctx: Md4_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._16
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_md4_ctx()
|
||||
if c, ok := ctx.internal_ctx.(Md4_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Md4_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Md4_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
MD4 implementation
|
||||
*/
|
||||
|
||||
BLOCK_SIZE :: 64
|
||||
|
||||
Md4_Context :: struct {
|
||||
data: [64]byte,
|
||||
state: [4]u32,
|
||||
bitlen: u64,
|
||||
datalen: u32,
|
||||
}
|
||||
|
||||
/*
|
||||
@note(zh): F, G and H, as mentioned in the RFC, have been inlined into FF, GG
|
||||
and HH respectively, instead of declaring them separately.
|
||||
*/
|
||||
|
||||
FF :: #force_inline proc "contextless"(a, b, c, d, x: u32, s : int) -> u32 {
|
||||
return util.ROTL32(a + ((b & c) | (~b & d)) + x, s)
|
||||
}
|
||||
|
||||
GG :: #force_inline proc "contextless"(a, b, c, d, x: u32, s : int) -> u32 {
|
||||
return util.ROTL32(a + ((b & c) | (b & d) | (c & d)) + x + 0x5a827999, s)
|
||||
}
|
||||
|
||||
HH :: #force_inline proc "contextless"(a, b, c, d, x: u32, s : int) -> u32 {
|
||||
return util.ROTL32(a + (b ~ c ~ d) + x + 0x6ed9eba1, s)
|
||||
}
|
||||
|
||||
transform :: proc(ctx: ^Md4_Context, data: []byte) {
|
||||
a, b, c, d, i, j: u32
|
||||
m: [16]u32
|
||||
|
||||
for i, j = 0, 0; i < 16; i += 1 {
|
||||
m[i] = u32(data[j]) | (u32(data[j + 1]) << 8) | (u32(data[j + 2]) << 16) | (u32(data[j + 3]) << 24)
|
||||
j += 4
|
||||
}
|
||||
|
||||
a = ctx.state[0]
|
||||
b = ctx.state[1]
|
||||
c = ctx.state[2]
|
||||
d = ctx.state[3]
|
||||
|
||||
a = FF(a, b, c, d, m[0], 3)
|
||||
d = FF(d, a, b, c, m[1], 7)
|
||||
c = FF(c, d, a, b, m[2], 11)
|
||||
b = FF(b, c, d, a, m[3], 19)
|
||||
a = FF(a, b, c, d, m[4], 3)
|
||||
d = FF(d, a, b, c, m[5], 7)
|
||||
c = FF(c, d, a, b, m[6], 11)
|
||||
b = FF(b, c, d, a, m[7], 19)
|
||||
a = FF(a, b, c, d, m[8], 3)
|
||||
d = FF(d, a, b, c, m[9], 7)
|
||||
c = FF(c, d, a, b, m[10], 11)
|
||||
b = FF(b, c, d, a, m[11], 19)
|
||||
a = FF(a, b, c, d, m[12], 3)
|
||||
d = FF(d, a, b, c, m[13], 7)
|
||||
c = FF(c, d, a, b, m[14], 11)
|
||||
b = FF(b, c, d, a, m[15], 19)
|
||||
|
||||
a = GG(a, b, c, d, m[0], 3)
|
||||
d = GG(d, a, b, c, m[4], 5)
|
||||
c = GG(c, d, a, b, m[8], 9)
|
||||
b = GG(b, c, d, a, m[12], 13)
|
||||
a = GG(a, b, c, d, m[1], 3)
|
||||
d = GG(d, a, b, c, m[5], 5)
|
||||
c = GG(c, d, a, b, m[9], 9)
|
||||
b = GG(b, c, d, a, m[13], 13)
|
||||
a = GG(a, b, c, d, m[2], 3)
|
||||
d = GG(d, a, b, c, m[6], 5)
|
||||
c = GG(c, d, a, b, m[10], 9)
|
||||
b = GG(b, c, d, a, m[14], 13)
|
||||
a = GG(a, b, c, d, m[3], 3)
|
||||
d = GG(d, a, b, c, m[7], 5)
|
||||
c = GG(c, d, a, b, m[11], 9)
|
||||
b = GG(b, c, d, a, m[15], 13)
|
||||
|
||||
a = HH(a, b, c, d, m[0], 3)
|
||||
d = HH(d, a, b, c, m[8], 9)
|
||||
c = HH(c, d, a, b, m[4], 11)
|
||||
b = HH(b, c, d, a, m[12], 15)
|
||||
a = HH(a, b, c, d, m[2], 3)
|
||||
d = HH(d, a, b, c, m[10], 9)
|
||||
c = HH(c, d, a, b, m[6], 11)
|
||||
b = HH(b, c, d, a, m[14], 15)
|
||||
a = HH(a, b, c, d, m[1], 3)
|
||||
d = HH(d, a, b, c, m[9], 9)
|
||||
c = HH(c, d, a, b, m[5], 11)
|
||||
b = HH(b, c, d, a, m[13], 15)
|
||||
a = HH(a, b, c, d, m[3], 3)
|
||||
d = HH(d, a, b, c, m[11], 9)
|
||||
c = HH(c, d, a, b, m[7], 11)
|
||||
b = HH(b, c, d, a, m[15], 15)
|
||||
|
||||
ctx.state[0] += a
|
||||
ctx.state[1] += b
|
||||
ctx.state[2] += c
|
||||
ctx.state[3] += d
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^Md4_Context) {
|
||||
ctx.state[0] = 0x67452301
|
||||
ctx.state[1] = 0xefcdab89
|
||||
ctx.state[2] = 0x98badcfe
|
||||
ctx.state[3] = 0x10325476
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^Md4_Context, data: []byte) {
|
||||
for i := 0; i < len(data); i += 1 {
|
||||
ctx.data[ctx.datalen] = data[i]
|
||||
ctx.datalen += 1
|
||||
if(ctx.datalen == BLOCK_SIZE) {
|
||||
transform(ctx, ctx.data[:])
|
||||
ctx.bitlen += 512
|
||||
ctx.datalen = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^Md4_Context, hash: []byte) {
|
||||
i := ctx.datalen
|
||||
if ctx.datalen < 56 {
|
||||
ctx.data[i] = 0x80
|
||||
i += 1
|
||||
for i < 56 {
|
||||
ctx.data[i] = 0x00
|
||||
i += 1
|
||||
}
|
||||
} else if ctx.datalen >= 56 {
|
||||
ctx.data[i] = 0x80
|
||||
i += 1
|
||||
for i < BLOCK_SIZE {
|
||||
ctx.data[i] = 0x00
|
||||
i += 1
|
||||
}
|
||||
transform(ctx, ctx.data[:])
|
||||
mem.set(&ctx.data, 0, 56)
|
||||
}
|
||||
|
||||
ctx.bitlen += u64(ctx.datalen * 8)
|
||||
ctx.data[56] = byte(ctx.bitlen)
|
||||
ctx.data[57] = byte(ctx.bitlen >> 8)
|
||||
ctx.data[58] = byte(ctx.bitlen >> 16)
|
||||
ctx.data[59] = byte(ctx.bitlen >> 24)
|
||||
ctx.data[60] = byte(ctx.bitlen >> 32)
|
||||
ctx.data[61] = byte(ctx.bitlen >> 40)
|
||||
ctx.data[62] = byte(ctx.bitlen >> 48)
|
||||
ctx.data[63] = byte(ctx.bitlen >> 56)
|
||||
transform(ctx, ctx.data[:])
|
||||
|
||||
for i = 0; i < 4; i += 1 {
|
||||
hash[i] = byte(ctx.state[0] >> (i * 8)) & 0x000000ff
|
||||
hash[i + 4] = byte(ctx.state[1] >> (i * 8)) & 0x000000ff
|
||||
hash[i + 8] = byte(ctx.state[2] >> (i * 8)) & 0x000000ff
|
||||
hash[i + 12] = byte(ctx.state[3] >> (i * 8)) & 0x000000ff
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,365 @@
|
||||
package md5
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the MD5 hashing algorithm, as defined in RFC 1321 <https://datatracker.ietf.org/doc/html/rfc1321>
|
||||
*/
|
||||
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../util"
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_16 = hash_bytes_odin
|
||||
ctx.hash_file_16 = hash_file_odin
|
||||
ctx.hash_stream_16 = hash_stream_odin
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_MD5)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// hash_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string :: proc(data: string) -> [16]byte {
|
||||
return hash_bytes(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes :: proc(data: []byte) -> [16]byte {
|
||||
_create_md5_ctx()
|
||||
return _hash_impl->hash_bytes_16(data)
|
||||
}
|
||||
|
||||
// hash_stream will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream :: proc(s: io.Stream) -> ([16]byte, bool) {
|
||||
_create_md5_ctx()
|
||||
return _hash_impl->hash_stream_16(s)
|
||||
}
|
||||
|
||||
// hash_file will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
|
||||
_create_md5_ctx()
|
||||
return _hash_impl->hash_file_16(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash :: proc {
|
||||
hash_stream,
|
||||
hash_file,
|
||||
hash_bytes,
|
||||
hash_string,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(Md5_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(Md5_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [16]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_md5_ctx :: #force_inline proc() {
|
||||
ctx: Md5_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._16
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_md5_ctx()
|
||||
if c, ok := ctx.internal_ctx.(Md5_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Md5_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Md5_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
MD4 implementation
|
||||
*/
|
||||
|
||||
BLOCK_SIZE :: 64
|
||||
|
||||
Md5_Context :: struct {
|
||||
data: [BLOCK_SIZE]byte,
|
||||
state: [4]u32,
|
||||
bitlen: u64,
|
||||
datalen: u32,
|
||||
}
|
||||
|
||||
/*
|
||||
@note(zh): F, G, H and I, as mentioned in the RFC, have been inlined into FF, GG, HH
|
||||
and II respectively, instead of declaring them separately.
|
||||
*/
|
||||
|
||||
FF :: #force_inline proc "contextless" (a, b, c, d, m: u32, s: int, t: u32) -> u32 {
|
||||
return b + util.ROTL32(a + ((b & c) | (~b & d)) + m + t, s)
|
||||
}
|
||||
|
||||
GG :: #force_inline proc "contextless" (a, b, c, d, m: u32, s: int, t: u32) -> u32 {
|
||||
return b + util.ROTL32(a + ((b & d) | (c & ~d)) + m + t, s)
|
||||
}
|
||||
|
||||
HH :: #force_inline proc "contextless" (a, b, c, d, m: u32, s: int, t: u32) -> u32 {
|
||||
return b + util.ROTL32(a + (b ~ c ~ d) + m + t, s)
|
||||
}
|
||||
|
||||
II :: #force_inline proc "contextless" (a, b, c, d, m: u32, s: int, t: u32) -> u32 {
|
||||
return b + util.ROTL32(a + (c ~ (b | ~d)) + m + t, s)
|
||||
}
|
||||
|
||||
transform :: proc(ctx: ^Md5_Context, data: []byte) {
|
||||
i, j: u32
|
||||
m: [16]u32
|
||||
|
||||
for i, j = 0, 0; i < 16; i+=1 {
|
||||
m[i] = u32(data[j]) + u32(data[j + 1]) << 8 + u32(data[j + 2]) << 16 + u32(data[j + 3]) << 24
|
||||
j += 4
|
||||
}
|
||||
|
||||
a := ctx.state[0]
|
||||
b := ctx.state[1]
|
||||
c := ctx.state[2]
|
||||
d := ctx.state[3]
|
||||
|
||||
a = FF(a, b, c, d, m[0], 7, 0xd76aa478)
|
||||
d = FF(d, a, b, c, m[1], 12, 0xe8c7b756)
|
||||
c = FF(c, d, a, b, m[2], 17, 0x242070db)
|
||||
b = FF(b, c, d, a, m[3], 22, 0xc1bdceee)
|
||||
a = FF(a, b, c, d, m[4], 7, 0xf57c0faf)
|
||||
d = FF(d, a, b, c, m[5], 12, 0x4787c62a)
|
||||
c = FF(c, d, a, b, m[6], 17, 0xa8304613)
|
||||
b = FF(b, c, d, a, m[7], 22, 0xfd469501)
|
||||
a = FF(a, b, c, d, m[8], 7, 0x698098d8)
|
||||
d = FF(d, a, b, c, m[9], 12, 0x8b44f7af)
|
||||
c = FF(c, d, a, b, m[10], 17, 0xffff5bb1)
|
||||
b = FF(b, c, d, a, m[11], 22, 0x895cd7be)
|
||||
a = FF(a, b, c, d, m[12], 7, 0x6b901122)
|
||||
d = FF(d, a, b, c, m[13], 12, 0xfd987193)
|
||||
c = FF(c, d, a, b, m[14], 17, 0xa679438e)
|
||||
b = FF(b, c, d, a, m[15], 22, 0x49b40821)
|
||||
|
||||
a = GG(a, b, c, d, m[1], 5, 0xf61e2562)
|
||||
d = GG(d, a, b, c, m[6], 9, 0xc040b340)
|
||||
c = GG(c, d, a, b, m[11], 14, 0x265e5a51)
|
||||
b = GG(b, c, d, a, m[0], 20, 0xe9b6c7aa)
|
||||
a = GG(a, b, c, d, m[5], 5, 0xd62f105d)
|
||||
d = GG(d, a, b, c, m[10], 9, 0x02441453)
|
||||
c = GG(c, d, a, b, m[15], 14, 0xd8a1e681)
|
||||
b = GG(b, c, d, a, m[4], 20, 0xe7d3fbc8)
|
||||
a = GG(a, b, c, d, m[9], 5, 0x21e1cde6)
|
||||
d = GG(d, a, b, c, m[14], 9, 0xc33707d6)
|
||||
c = GG(c, d, a, b, m[3], 14, 0xf4d50d87)
|
||||
b = GG(b, c, d, a, m[8], 20, 0x455a14ed)
|
||||
a = GG(a, b, c, d, m[13], 5, 0xa9e3e905)
|
||||
d = GG(d, a, b, c, m[2], 9, 0xfcefa3f8)
|
||||
c = GG(c, d, a, b, m[7], 14, 0x676f02d9)
|
||||
b = GG(b, c, d, a, m[12], 20, 0x8d2a4c8a)
|
||||
|
||||
a = HH(a, b, c, d, m[5], 4, 0xfffa3942)
|
||||
d = HH(d, a, b, c, m[8], 11, 0x8771f681)
|
||||
c = HH(c, d, a, b, m[11], 16, 0x6d9d6122)
|
||||
b = HH(b, c, d, a, m[14], 23, 0xfde5380c)
|
||||
a = HH(a, b, c, d, m[1], 4, 0xa4beea44)
|
||||
d = HH(d, a, b, c, m[4], 11, 0x4bdecfa9)
|
||||
c = HH(c, d, a, b, m[7], 16, 0xf6bb4b60)
|
||||
b = HH(b, c, d, a, m[10], 23, 0xbebfbc70)
|
||||
a = HH(a, b, c, d, m[13], 4, 0x289b7ec6)
|
||||
d = HH(d, a, b, c, m[0], 11, 0xeaa127fa)
|
||||
c = HH(c, d, a, b, m[3], 16, 0xd4ef3085)
|
||||
b = HH(b, c, d, a, m[6], 23, 0x04881d05)
|
||||
a = HH(a, b, c, d, m[9], 4, 0xd9d4d039)
|
||||
d = HH(d, a, b, c, m[12], 11, 0xe6db99e5)
|
||||
c = HH(c, d, a, b, m[15], 16, 0x1fa27cf8)
|
||||
b = HH(b, c, d, a, m[2], 23, 0xc4ac5665)
|
||||
|
||||
a = II(a, b, c, d, m[0], 6, 0xf4292244)
|
||||
d = II(d, a, b, c, m[7], 10, 0x432aff97)
|
||||
c = II(c, d, a, b, m[14], 15, 0xab9423a7)
|
||||
b = II(b, c, d, a, m[5], 21, 0xfc93a039)
|
||||
a = II(a, b, c, d, m[12], 6, 0x655b59c3)
|
||||
d = II(d, a, b, c, m[3], 10, 0x8f0ccc92)
|
||||
c = II(c, d, a, b, m[10], 15, 0xffeff47d)
|
||||
b = II(b, c, d, a, m[1], 21, 0x85845dd1)
|
||||
a = II(a, b, c, d, m[8], 6, 0x6fa87e4f)
|
||||
d = II(d, a, b, c, m[15], 10, 0xfe2ce6e0)
|
||||
c = II(c, d, a, b, m[6], 15, 0xa3014314)
|
||||
b = II(b, c, d, a, m[13], 21, 0x4e0811a1)
|
||||
a = II(a, b, c, d, m[4], 6, 0xf7537e82)
|
||||
d = II(d, a, b, c, m[11], 10, 0xbd3af235)
|
||||
c = II(c, d, a, b, m[2], 15, 0x2ad7d2bb)
|
||||
b = II(b, c, d, a, m[9], 21, 0xeb86d391)
|
||||
|
||||
ctx.state[0] += a
|
||||
ctx.state[1] += b
|
||||
ctx.state[2] += c
|
||||
ctx.state[3] += d
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^Md5_Context) {
|
||||
ctx.state[0] = 0x67452301
|
||||
ctx.state[1] = 0xefcdab89
|
||||
ctx.state[2] = 0x98badcfe
|
||||
ctx.state[3] = 0x10325476
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^Md5_Context, data: []byte) {
|
||||
for i := 0; i < len(data); i += 1 {
|
||||
ctx.data[ctx.datalen] = data[i]
|
||||
ctx.datalen += 1
|
||||
if(ctx.datalen == BLOCK_SIZE) {
|
||||
transform(ctx, ctx.data[:])
|
||||
ctx.bitlen += 512
|
||||
ctx.datalen = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^Md5_Context, hash: []byte){
|
||||
i : u32
|
||||
i = ctx.datalen
|
||||
|
||||
if ctx.datalen < 56 {
|
||||
ctx.data[i] = 0x80
|
||||
i += 1
|
||||
for i < 56 {
|
||||
ctx.data[i] = 0x00
|
||||
i += 1
|
||||
}
|
||||
} else if ctx.datalen >= 56 {
|
||||
ctx.data[i] = 0x80
|
||||
i += 1
|
||||
for i < BLOCK_SIZE {
|
||||
ctx.data[i] = 0x00
|
||||
i += 1
|
||||
}
|
||||
transform(ctx, ctx.data[:])
|
||||
mem.set(&ctx.data, 0, 56)
|
||||
}
|
||||
|
||||
ctx.bitlen += u64(ctx.datalen * 8)
|
||||
ctx.data[56] = byte(ctx.bitlen)
|
||||
ctx.data[57] = byte(ctx.bitlen >> 8)
|
||||
ctx.data[58] = byte(ctx.bitlen >> 16)
|
||||
ctx.data[59] = byte(ctx.bitlen >> 24)
|
||||
ctx.data[60] = byte(ctx.bitlen >> 32)
|
||||
ctx.data[61] = byte(ctx.bitlen >> 40)
|
||||
ctx.data[62] = byte(ctx.bitlen >> 48)
|
||||
ctx.data[63] = byte(ctx.bitlen >> 56)
|
||||
transform(ctx, ctx.data[:])
|
||||
|
||||
for i = 0; i < 4; i += 1 {
|
||||
hash[i] = byte(ctx.state[0] >> (i * 8)) & 0x000000ff
|
||||
hash[i + 4] = byte(ctx.state[1] >> (i * 8)) & 0x000000ff
|
||||
hash[i + 8] = byte(ctx.state[2] >> (i * 8)) & 0x000000ff
|
||||
hash[i + 12] = byte(ctx.state[3] >> (i * 8)) & 0x000000ff
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,326 @@
|
||||
package sha1
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the SHA1 hashing algorithm, as defined in RFC 3174 <https://datatracker.ietf.org/doc/html/rfc3174>
|
||||
*/
|
||||
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../util"
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_20 = hash_bytes_odin
|
||||
ctx.hash_file_20 = hash_file_odin
|
||||
ctx.hash_stream_20 = hash_stream_odin
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_SHA1)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// hash_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string :: proc(data: string) -> [20]byte {
|
||||
return hash_bytes(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes :: proc(data: []byte) -> [20]byte {
|
||||
_create_sha1_ctx()
|
||||
return _hash_impl->hash_bytes_20(data)
|
||||
}
|
||||
|
||||
// hash_stream will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream :: proc(s: io.Stream) -> ([20]byte, bool) {
|
||||
_create_sha1_ctx()
|
||||
return _hash_impl->hash_stream_20(s)
|
||||
}
|
||||
|
||||
// hash_file will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
|
||||
_create_sha1_ctx()
|
||||
return _hash_impl->hash_file_20(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash :: proc {
|
||||
hash_stream,
|
||||
hash_file,
|
||||
hash_bytes,
|
||||
hash_string,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [20]byte {
|
||||
hash: [20]byte
|
||||
if c, ok := ctx.internal_ctx.(Sha1_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([20]byte, bool) {
|
||||
hash: [20]byte
|
||||
if c, ok := ctx.internal_ctx.(Sha1_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [20]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_sha1_ctx :: #force_inline proc() {
|
||||
ctx: Sha1_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._20
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_sha1_ctx()
|
||||
if c, ok := ctx.internal_ctx.(Sha1_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Sha1_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Sha1_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
SHA1 implementation
|
||||
*/
|
||||
|
||||
BLOCK_SIZE :: 64
|
||||
|
||||
Sha1_Context :: struct {
|
||||
data: [BLOCK_SIZE]byte,
|
||||
datalen: u32,
|
||||
bitlen: u64,
|
||||
state: [5]u32,
|
||||
k: [4]u32,
|
||||
}
|
||||
|
||||
transform :: proc(ctx: ^Sha1_Context, data: []byte) {
|
||||
a, b, c, d, e, i, j, t: u32
|
||||
m: [80]u32
|
||||
|
||||
for i, j = 0, 0; i < 16; i += 1 {
|
||||
m[i] = u32(data[j]) << 24 + u32(data[j + 1]) << 16 + u32(data[j + 2]) << 8 + u32(data[j + 3])
|
||||
j += 4
|
||||
}
|
||||
for i < 80 {
|
||||
m[i] = (m[i - 3] ~ m[i - 8] ~ m[i - 14] ~ m[i - 16])
|
||||
m[i] = (m[i] << 1) | (m[i] >> 31)
|
||||
i += 1
|
||||
}
|
||||
|
||||
a = ctx.state[0]
|
||||
b = ctx.state[1]
|
||||
c = ctx.state[2]
|
||||
d = ctx.state[3]
|
||||
e = ctx.state[4]
|
||||
|
||||
for i = 0; i < 20; i += 1 {
|
||||
t = util.ROTL32(a, 5) + ((b & c) ~ (~b & d)) + e + ctx.k[0] + m[i]
|
||||
e = d
|
||||
d = c
|
||||
c = util.ROTL32(b, 30)
|
||||
b = a
|
||||
a = t
|
||||
}
|
||||
for i < 40 {
|
||||
t = util.ROTL32(a, 5) + (b ~ c ~ d) + e + ctx.k[1] + m[i]
|
||||
e = d
|
||||
d = c
|
||||
c = util.ROTL32(b, 30)
|
||||
b = a
|
||||
a = t
|
||||
i += 1
|
||||
}
|
||||
for i < 60 {
|
||||
t = util.ROTL32(a, 5) + ((b & c) ~ (b & d) ~ (c & d)) + e + ctx.k[2] + m[i]
|
||||
e = d
|
||||
d = c
|
||||
c = util.ROTL32(b, 30)
|
||||
b = a
|
||||
a = t
|
||||
i += 1
|
||||
}
|
||||
for i < 80 {
|
||||
t = util.ROTL32(a, 5) + (b ~ c ~ d) + e + ctx.k[3] + m[i]
|
||||
e = d
|
||||
d = c
|
||||
c = util.ROTL32(b, 30)
|
||||
b = a
|
||||
a = t
|
||||
i += 1
|
||||
}
|
||||
|
||||
ctx.state[0] += a
|
||||
ctx.state[1] += b
|
||||
ctx.state[2] += c
|
||||
ctx.state[3] += d
|
||||
ctx.state[4] += e
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^Sha1_Context) {
|
||||
ctx.state[0] = 0x67452301
|
||||
ctx.state[1] = 0xefcdab89
|
||||
ctx.state[2] = 0x98badcfe
|
||||
ctx.state[3] = 0x10325476
|
||||
ctx.state[4] = 0xc3d2e1f0
|
||||
ctx.k[0] = 0x5a827999
|
||||
ctx.k[1] = 0x6ed9eba1
|
||||
ctx.k[2] = 0x8f1bbcdc
|
||||
ctx.k[3] = 0xca62c1d6
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^Sha1_Context, data: []byte) {
|
||||
for i := 0; i < len(data); i += 1 {
|
||||
ctx.data[ctx.datalen] = data[i]
|
||||
ctx.datalen += 1
|
||||
if (ctx.datalen == BLOCK_SIZE) {
|
||||
transform(ctx, ctx.data[:])
|
||||
ctx.bitlen += 512
|
||||
ctx.datalen = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^Sha1_Context, hash: []byte) {
|
||||
i := ctx.datalen
|
||||
|
||||
if ctx.datalen < 56 {
|
||||
ctx.data[i] = 0x80
|
||||
i += 1
|
||||
for i < 56 {
|
||||
ctx.data[i] = 0x00
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
else {
|
||||
ctx.data[i] = 0x80
|
||||
i += 1
|
||||
for i < BLOCK_SIZE {
|
||||
ctx.data[i] = 0x00
|
||||
i += 1
|
||||
}
|
||||
transform(ctx, ctx.data[:])
|
||||
mem.set(&ctx.data, 0, 56)
|
||||
}
|
||||
|
||||
ctx.bitlen += u64(ctx.datalen * 8)
|
||||
ctx.data[63] = u8(ctx.bitlen)
|
||||
ctx.data[62] = u8(ctx.bitlen >> 8)
|
||||
ctx.data[61] = u8(ctx.bitlen >> 16)
|
||||
ctx.data[60] = u8(ctx.bitlen >> 24)
|
||||
ctx.data[59] = u8(ctx.bitlen >> 32)
|
||||
ctx.data[58] = u8(ctx.bitlen >> 40)
|
||||
ctx.data[57] = u8(ctx.bitlen >> 48)
|
||||
ctx.data[56] = u8(ctx.bitlen >> 56)
|
||||
transform(ctx, ctx.data[:])
|
||||
|
||||
for j: u32 = 0; j < 4; j += 1 {
|
||||
hash[j] = u8(ctx.state[0] >> (24 - j * 8)) & 0x000000ff
|
||||
hash[j + 4] = u8(ctx.state[1] >> (24 - j * 8)) & 0x000000ff
|
||||
hash[j + 8] = u8(ctx.state[2] >> (24 - j * 8)) & 0x000000ff
|
||||
hash[j + 12] = u8(ctx.state[3] >> (24 - j * 8)) & 0x000000ff
|
||||
hash[j + 16] = u8(ctx.state[4] >> (24 - j * 8)) & 0x000000ff
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,785 @@
|
||||
package sha2
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the SHA2 hashing algorithm, as defined in <https://csrc.nist.gov/csrc/media/publications/fips/180/2/archive/2002-08-01/documents/fips180-2.pdf>
|
||||
and in RFC 3874 <https://datatracker.ietf.org/doc/html/rfc3874>
|
||||
*/
|
||||
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../util"
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_28 = hash_bytes_odin_28
|
||||
ctx.hash_file_28 = hash_file_odin_28
|
||||
ctx.hash_stream_28 = hash_stream_odin_28
|
||||
ctx.hash_bytes_32 = hash_bytes_odin_32
|
||||
ctx.hash_file_32 = hash_file_odin_32
|
||||
ctx.hash_stream_32 = hash_stream_odin_32
|
||||
ctx.hash_bytes_48 = hash_bytes_odin_48
|
||||
ctx.hash_file_48 = hash_file_odin_48
|
||||
ctx.hash_stream_48 = hash_stream_odin_48
|
||||
ctx.hash_bytes_64 = hash_bytes_odin_64
|
||||
ctx.hash_file_64 = hash_file_odin_64
|
||||
ctx.hash_stream_64 = hash_stream_odin_64
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_SHA2)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_sha256_ctx :: #force_inline proc(is224: bool) {
|
||||
ctx: Sha256_Context
|
||||
ctx.is224 = is224
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = is224 ? ._28 : ._32
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_sha512_ctx :: #force_inline proc(is384: bool) {
|
||||
ctx: Sha512_Context
|
||||
ctx.is384 = is384
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = is384 ? ._48 : ._64
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// hash_string_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_224 :: proc(data: string) -> [28]byte {
|
||||
return hash_bytes_224(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_224 :: proc(data: []byte) -> [28]byte {
|
||||
_create_sha256_ctx(true)
|
||||
return _hash_impl->hash_bytes_28(data)
|
||||
}
|
||||
|
||||
// hash_stream_224 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
|
||||
_create_sha256_ctx(true)
|
||||
return _hash_impl->hash_stream_28(s)
|
||||
}
|
||||
|
||||
// hash_file_224 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
|
||||
_create_sha256_ctx(true)
|
||||
return _hash_impl->hash_file_28(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_224 :: proc {
|
||||
hash_stream_224,
|
||||
hash_file_224,
|
||||
hash_bytes_224,
|
||||
hash_string_224,
|
||||
}
|
||||
|
||||
// hash_string_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_256 :: proc(data: string) -> [32]byte {
|
||||
return hash_bytes_256(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_256 :: proc(data: []byte) -> [32]byte {
|
||||
_create_sha256_ctx(false)
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
}
|
||||
|
||||
// hash_stream_256 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
|
||||
_create_sha256_ctx(false)
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
}
|
||||
|
||||
// hash_file_256 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
_create_sha256_ctx(false)
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_256 :: proc {
|
||||
hash_stream_256,
|
||||
hash_file_256,
|
||||
hash_bytes_256,
|
||||
hash_string_256,
|
||||
}
|
||||
|
||||
// hash_string_384 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_384 :: proc(data: string) -> [48]byte {
|
||||
return hash_bytes_384(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_384 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_384 :: proc(data: []byte) -> [48]byte {
|
||||
_create_sha512_ctx(true)
|
||||
return _hash_impl->hash_bytes_48(data)
|
||||
}
|
||||
|
||||
// hash_stream_384 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
|
||||
_create_sha512_ctx(true)
|
||||
return _hash_impl->hash_stream_48(s)
|
||||
}
|
||||
|
||||
// hash_file_384 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
|
||||
_create_sha512_ctx(true)
|
||||
return _hash_impl->hash_file_48(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_384 :: proc {
|
||||
hash_stream_384,
|
||||
hash_file_384,
|
||||
hash_bytes_384,
|
||||
hash_string_384,
|
||||
}
|
||||
|
||||
// hash_string_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_512 :: proc(data: string) -> [64]byte {
|
||||
return hash_bytes_512(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_512 :: proc(data: []byte) -> [64]byte {
|
||||
_create_sha512_ctx(false)
|
||||
return _hash_impl->hash_bytes_64(data)
|
||||
}
|
||||
|
||||
// hash_stream_512 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
|
||||
_create_sha512_ctx(false)
|
||||
return _hash_impl->hash_stream_64(s)
|
||||
}
|
||||
|
||||
// hash_file_512 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
_create_sha512_ctx(false)
|
||||
return _hash_impl->hash_file_64(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_512 :: proc {
|
||||
hash_stream_512,
|
||||
hash_file_512,
|
||||
hash_bytes_512,
|
||||
hash_string_512,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte {
|
||||
hash: [28]byte
|
||||
if c, ok := ctx.internal_ctx.(Sha256_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) {
|
||||
hash: [28]byte
|
||||
if c, ok := ctx.internal_ctx.(Sha256_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_28(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_28(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [28]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Sha256_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Sha256_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_32(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [32]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte {
|
||||
hash: [48]byte
|
||||
if c, ok := ctx.internal_ctx.(Sha512_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([48]byte, bool) {
|
||||
hash: [48]byte
|
||||
if c, ok := ctx.internal_ctx.(Sha512_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_48(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_48(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [48]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(Sha512_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(Sha512_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_64(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_64(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [64]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
if ctx.hash_size == ._28 || ctx.hash_size == ._32 {
|
||||
_create_sha256_ctx(ctx.hash_size == ._28)
|
||||
if c, ok := ctx.internal_ctx.(Sha256_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
return
|
||||
}
|
||||
if ctx.hash_size == ._48 || ctx.hash_size == ._64 {
|
||||
_create_sha512_ctx(ctx.hash_size == ._48)
|
||||
if c, ok := ctx.internal_ctx.(Sha512_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
#partial switch ctx.hash_size {
|
||||
case ._28, ._32:
|
||||
if c, ok := ctx.internal_ctx.(Sha256_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
case ._48, ._64:
|
||||
if c, ok := ctx.internal_ctx.(Sha512_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
#partial switch ctx.hash_size {
|
||||
case ._28, ._32:
|
||||
if c, ok := ctx.internal_ctx.(Sha256_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
case ._48, ._64:
|
||||
if c, ok := ctx.internal_ctx.(Sha512_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
SHA2 implementation
|
||||
*/
|
||||
|
||||
SHA256_BLOCK_SIZE :: 64
|
||||
SHA512_BLOCK_SIZE :: 128
|
||||
|
||||
Sha256_Context :: struct {
|
||||
tot_len: uint,
|
||||
length: uint,
|
||||
block: [128]byte,
|
||||
h: [8]u32,
|
||||
is224: bool,
|
||||
}
|
||||
|
||||
Sha512_Context :: struct {
|
||||
tot_len: uint,
|
||||
length: uint,
|
||||
block: [256]byte,
|
||||
h: [8]u64,
|
||||
is384: bool,
|
||||
}
|
||||
|
||||
sha256_k := [64]u32 {
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
|
||||
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
||||
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
||||
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
||||
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
|
||||
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
||||
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
|
||||
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
||||
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
|
||||
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
||||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
|
||||
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
||||
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
|
||||
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
||||
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
||||
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
||||
}
|
||||
|
||||
sha512_k := [80]u64 {
|
||||
0x428a2f98d728ae22, 0x7137449123ef65cd,
|
||||
0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
|
||||
0x3956c25bf348b538, 0x59f111f1b605d019,
|
||||
0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
|
||||
0xd807aa98a3030242, 0x12835b0145706fbe,
|
||||
0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
|
||||
0x72be5d74f27b896f, 0x80deb1fe3b1696b1,
|
||||
0x9bdc06a725c71235, 0xc19bf174cf692694,
|
||||
0xe49b69c19ef14ad2, 0xefbe4786384f25e3,
|
||||
0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
|
||||
0x2de92c6f592b0275, 0x4a7484aa6ea6e483,
|
||||
0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
|
||||
0x983e5152ee66dfab, 0xa831c66d2db43210,
|
||||
0xb00327c898fb213f, 0xbf597fc7beef0ee4,
|
||||
0xc6e00bf33da88fc2, 0xd5a79147930aa725,
|
||||
0x06ca6351e003826f, 0x142929670a0e6e70,
|
||||
0x27b70a8546d22ffc, 0x2e1b21385c26c926,
|
||||
0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
|
||||
0x650a73548baf63de, 0x766a0abb3c77b2a8,
|
||||
0x81c2c92e47edaee6, 0x92722c851482353b,
|
||||
0xa2bfe8a14cf10364, 0xa81a664bbc423001,
|
||||
0xc24b8b70d0f89791, 0xc76c51a30654be30,
|
||||
0xd192e819d6ef5218, 0xd69906245565a910,
|
||||
0xf40e35855771202a, 0x106aa07032bbd1b8,
|
||||
0x19a4c116b8d2d0c8, 0x1e376c085141ab53,
|
||||
0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8,
|
||||
0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb,
|
||||
0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3,
|
||||
0x748f82ee5defb2fc, 0x78a5636f43172f60,
|
||||
0x84c87814a1f0ab72, 0x8cc702081a6439ec,
|
||||
0x90befffa23631e28, 0xa4506cebde82bde9,
|
||||
0xbef9a3f7b2c67915, 0xc67178f2e372532b,
|
||||
0xca273eceea26619c, 0xd186b8c721c0c207,
|
||||
0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178,
|
||||
0x06f067aa72176fba, 0x0a637dc5a2c898a6,
|
||||
0x113f9804bef90dae, 0x1b710b35131c471b,
|
||||
0x28db77f523047d84, 0x32caab7b40c72493,
|
||||
0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c,
|
||||
0x4cc5d4becb3e42b6, 0x597f299cfc657e2a,
|
||||
0x5fcb6fab3ad6faec, 0x6c44198c4a475817,
|
||||
}
|
||||
|
||||
SHA256_CH :: #force_inline proc "contextless"(x, y, z: u32) -> u32 {
|
||||
return (x & y) ~ (~x & z)
|
||||
}
|
||||
|
||||
SHA256_MAJ :: #force_inline proc "contextless"(x, y, z: u32) -> u32 {
|
||||
return (x & y) ~ (x & z) ~ (y & z)
|
||||
}
|
||||
|
||||
SHA512_CH :: #force_inline proc "contextless"(x, y, z: u64) -> u64 {
|
||||
return (x & y) ~ (~x & z)
|
||||
}
|
||||
|
||||
SHA512_MAJ :: #force_inline proc "contextless"(x, y, z: u64) -> u64 {
|
||||
return (x & y) ~ (x & z) ~ (y & z)
|
||||
}
|
||||
|
||||
SHA256_F1 :: #force_inline proc "contextless"(x: u32) -> u32 {
|
||||
return util.ROTR32(x, 2) ~ util.ROTR32(x, 13) ~ util.ROTR32(x, 22)
|
||||
}
|
||||
|
||||
SHA256_F2 :: #force_inline proc "contextless"(x: u32) -> u32 {
|
||||
return util.ROTR32(x, 6) ~ util.ROTR32(x, 11) ~ util.ROTR32(x, 25)
|
||||
}
|
||||
|
||||
SHA256_F3 :: #force_inline proc "contextless"(x: u32) -> u32 {
|
||||
return util.ROTR32(x, 7) ~ util.ROTR32(x, 18) ~ (x >> 3)
|
||||
}
|
||||
|
||||
SHA256_F4 :: #force_inline proc "contextless"(x: u32) -> u32 {
|
||||
return util.ROTR32(x, 17) ~ util.ROTR32(x, 19) ~ (x >> 10)
|
||||
}
|
||||
|
||||
SHA512_F1 :: #force_inline proc "contextless"(x: u64) -> u64 {
|
||||
return util.ROTR64(x, 28) ~ util.ROTR64(x, 34) ~ util.ROTR64(x, 39)
|
||||
}
|
||||
|
||||
SHA512_F2 :: #force_inline proc "contextless"(x: u64) -> u64 {
|
||||
return util.ROTR64(x, 14) ~ util.ROTR64(x, 18) ~ util.ROTR64(x, 41)
|
||||
}
|
||||
|
||||
SHA512_F3 :: #force_inline proc "contextless"(x: u64) -> u64 {
|
||||
return util.ROTR64(x, 1) ~ util.ROTR64(x, 8) ~ (x >> 7)
|
||||
}
|
||||
|
||||
SHA512_F4 :: #force_inline proc "contextless"(x: u64) -> u64 {
|
||||
return util.ROTR64(x, 19) ~ util.ROTR64(x, 61) ~ (x >> 6)
|
||||
}
|
||||
|
||||
PACK32 :: #force_inline proc "contextless"(b: []byte, x: ^u32) {
|
||||
x^ = u32(b[3]) | u32(b[2]) << 8 | u32(b[1]) << 16 | u32(b[0]) << 24
|
||||
}
|
||||
|
||||
PACK64 :: #force_inline proc "contextless"(b: []byte, x: ^u64) {
|
||||
x^ = u64(b[7]) | u64(b[6]) << 8 | u64(b[5]) << 16 | u64(b[4]) << 24 | u64(b[3]) << 32 | u64(b[2]) << 40 | u64(b[1]) << 48 | u64(b[0]) << 56
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^$T) {
|
||||
when T == Sha256_Context {
|
||||
if ctx.is224 {
|
||||
ctx.h[0] = 0xc1059ed8
|
||||
ctx.h[1] = 0x367cd507
|
||||
ctx.h[2] = 0x3070dd17
|
||||
ctx.h[3] = 0xf70e5939
|
||||
ctx.h[4] = 0xffc00b31
|
||||
ctx.h[5] = 0x68581511
|
||||
ctx.h[6] = 0x64f98fa7
|
||||
ctx.h[7] = 0xbefa4fa4
|
||||
} else {
|
||||
ctx.h[0] = 0x6a09e667
|
||||
ctx.h[1] = 0xbb67ae85
|
||||
ctx.h[2] = 0x3c6ef372
|
||||
ctx.h[3] = 0xa54ff53a
|
||||
ctx.h[4] = 0x510e527f
|
||||
ctx.h[5] = 0x9b05688c
|
||||
ctx.h[6] = 0x1f83d9ab
|
||||
ctx.h[7] = 0x5be0cd19
|
||||
}
|
||||
} else when T == Sha512_Context {
|
||||
if ctx.is384 {
|
||||
ctx.h[0] = 0xcbbb9d5dc1059ed8
|
||||
ctx.h[1] = 0x629a292a367cd507
|
||||
ctx.h[2] = 0x9159015a3070dd17
|
||||
ctx.h[3] = 0x152fecd8f70e5939
|
||||
ctx.h[4] = 0x67332667ffc00b31
|
||||
ctx.h[5] = 0x8eb44a8768581511
|
||||
ctx.h[6] = 0xdb0c2e0d64f98fa7
|
||||
ctx.h[7] = 0x47b5481dbefa4fa4
|
||||
} else {
|
||||
ctx.h[0] = 0x6a09e667f3bcc908
|
||||
ctx.h[1] = 0xbb67ae8584caa73b
|
||||
ctx.h[2] = 0x3c6ef372fe94f82b
|
||||
ctx.h[3] = 0xa54ff53a5f1d36f1
|
||||
ctx.h[4] = 0x510e527fade682d1
|
||||
ctx.h[5] = 0x9b05688c2b3e6c1f
|
||||
ctx.h[6] = 0x1f83d9abfb41bd6b
|
||||
ctx.h[7] = 0x5be0cd19137e2179
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sha2_transf :: proc(ctx: ^$T, data: []byte, block_nb: uint) {
|
||||
when T == Sha256_Context {
|
||||
w: [64]u32
|
||||
wv: [8]u32
|
||||
t1, t2: u32
|
||||
} else when T == Sha512_Context {
|
||||
w: [80]u64
|
||||
wv: [8]u64
|
||||
t1, t2: u64
|
||||
}
|
||||
|
||||
sub_block := make([]byte, len(data))
|
||||
i, j: i32
|
||||
|
||||
for i = 0; i < i32(block_nb); i += 1 {
|
||||
when T == Sha256_Context {
|
||||
sub_block = data[i << 6:]
|
||||
} else when T == Sha512_Context {
|
||||
sub_block = data[i << 7:]
|
||||
}
|
||||
|
||||
for j = 0; j < 16; j += 1 {
|
||||
when T == Sha256_Context {
|
||||
PACK32(sub_block[j << 2:], &w[j])
|
||||
} else when T == Sha512_Context {
|
||||
PACK64(sub_block[j << 3:], &w[j])
|
||||
}
|
||||
}
|
||||
|
||||
when T == Sha256_Context {
|
||||
for j = 16; j < 64; j += 1 {
|
||||
w[j] = SHA256_F4(w[j - 2]) + w[j - 7] + SHA256_F3(w[j - 15]) + w[j - 16]
|
||||
}
|
||||
} else when T == Sha512_Context {
|
||||
for j = 16; j < 80; j += 1 {
|
||||
w[j] = SHA512_F4(w[j - 2]) + w[j - 7] + SHA512_F3(w[j - 15]) + w[j - 16]
|
||||
}
|
||||
}
|
||||
|
||||
for j = 0; j < 8; j += 1 {
|
||||
wv[j] = ctx.h[j]
|
||||
}
|
||||
|
||||
when T == Sha256_Context {
|
||||
for j = 0; j < 64; j += 1 {
|
||||
t1 = wv[7] + SHA256_F2(wv[4]) + SHA256_CH(wv[4], wv[5], wv[6]) + sha256_k[j] + w[j]
|
||||
t2 = SHA256_F1(wv[0]) + SHA256_MAJ(wv[0], wv[1], wv[2])
|
||||
wv[7] = wv[6]
|
||||
wv[6] = wv[5]
|
||||
wv[5] = wv[4]
|
||||
wv[4] = wv[3] + t1
|
||||
wv[3] = wv[2]
|
||||
wv[2] = wv[1]
|
||||
wv[1] = wv[0]
|
||||
wv[0] = t1 + t2
|
||||
}
|
||||
} else when T == Sha512_Context {
|
||||
for j = 0; j < 80; j += 1 {
|
||||
t1 = wv[7] + SHA512_F2(wv[4]) + SHA512_CH(wv[4], wv[5], wv[6]) + sha512_k[j] + w[j]
|
||||
t2 = SHA512_F1(wv[0]) + SHA512_MAJ(wv[0], wv[1], wv[2])
|
||||
wv[7] = wv[6]
|
||||
wv[6] = wv[5]
|
||||
wv[5] = wv[4]
|
||||
wv[4] = wv[3] + t1
|
||||
wv[3] = wv[2]
|
||||
wv[2] = wv[1]
|
||||
wv[1] = wv[0]
|
||||
wv[0] = t1 + t2
|
||||
}
|
||||
}
|
||||
|
||||
for j = 0; j < 8; j += 1 {
|
||||
ctx.h[j] += wv[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^$T, data: []byte) {
|
||||
length := uint(len(data))
|
||||
block_nb: uint
|
||||
new_len, rem_len, tmp_len: uint
|
||||
shifted_message := make([]byte, length)
|
||||
|
||||
when T == Sha256_Context {
|
||||
CURR_BLOCK_SIZE :: SHA256_BLOCK_SIZE
|
||||
} else when T == Sha512_Context {
|
||||
CURR_BLOCK_SIZE :: SHA512_BLOCK_SIZE
|
||||
}
|
||||
|
||||
tmp_len = CURR_BLOCK_SIZE - ctx.length
|
||||
rem_len = length < tmp_len ? length : tmp_len
|
||||
copy(ctx.block[ctx.length:], data[:rem_len])
|
||||
|
||||
if ctx.length + length < CURR_BLOCK_SIZE {
|
||||
ctx.length += length
|
||||
return
|
||||
}
|
||||
|
||||
new_len = length - rem_len
|
||||
block_nb = new_len / CURR_BLOCK_SIZE
|
||||
shifted_message = data[rem_len:]
|
||||
|
||||
sha2_transf(ctx, ctx.block[:], 1)
|
||||
sha2_transf(ctx, shifted_message, block_nb)
|
||||
|
||||
rem_len = new_len % CURR_BLOCK_SIZE
|
||||
when T == Sha256_Context {copy(ctx.block[:], shifted_message[block_nb << 6:rem_len])}
|
||||
else when T == Sha512_Context {copy(ctx.block[:], shifted_message[block_nb << 7:rem_len])}
|
||||
|
||||
ctx.length = rem_len
|
||||
when T == Sha256_Context {ctx.tot_len += (block_nb + 1) << 6}
|
||||
else when T == Sha512_Context {ctx.tot_len += (block_nb + 1) << 7}
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^$T, hash: []byte) {
|
||||
block_nb, pm_len, len_b: u32
|
||||
i: i32
|
||||
|
||||
when T == Sha256_Context {CURR_BLOCK_SIZE :: SHA256_BLOCK_SIZE}
|
||||
else when T == Sha512_Context {CURR_BLOCK_SIZE :: SHA512_BLOCK_SIZE}
|
||||
|
||||
when T == Sha256_Context {block_nb = 1 + ((CURR_BLOCK_SIZE - 9) < (ctx.length % CURR_BLOCK_SIZE) ? 1 : 0)}
|
||||
else when T == Sha512_Context {block_nb = 1 + ((CURR_BLOCK_SIZE - 17) < (ctx.length % CURR_BLOCK_SIZE) ? 1 : 0)}
|
||||
|
||||
len_b = u32(ctx.tot_len + ctx.length) << 3
|
||||
when T == Sha256_Context {pm_len = block_nb << 6}
|
||||
else when T == Sha512_Context {pm_len = block_nb << 7}
|
||||
|
||||
mem.set(rawptr(&(ctx.block[ctx.length:])[0]), 0, int(uint(pm_len) - ctx.length))
|
||||
ctx.block[ctx.length] = 0x80
|
||||
|
||||
util.PUT_U32_BE(ctx.block[pm_len - 4:], len_b)
|
||||
|
||||
sha2_transf(ctx, ctx.block[:], uint(block_nb))
|
||||
|
||||
when T == Sha256_Context {
|
||||
if ctx.is224 {
|
||||
for i = 0; i < 7; i += 1 {util.PUT_U32_BE(hash[i << 2:], ctx.h[i])}
|
||||
} else {
|
||||
for i = 0; i < 8; i += 1 {util.PUT_U32_BE(hash[i << 2:], ctx.h[i])}
|
||||
}
|
||||
} else when T == Sha512_Context {
|
||||
if ctx.is384 {
|
||||
for i = 0; i < 6; i += 1 {util.PUT_U64_BE(hash[i << 3:], ctx.h[i])}
|
||||
} else {
|
||||
for i = 0; i < 8; i += 1 {util.PUT_U64_BE(hash[i << 3:], ctx.h[i])}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,428 @@
|
||||
package sha3
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Interface for the SHA3 hashing algorithm. The SHAKE functionality can be found in package shake.
|
||||
If you wish to compute a Keccak hash, you can use the keccak package, it will use the original padding.
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
import "../_sha3"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_28 = hash_bytes_odin_28
|
||||
ctx.hash_file_28 = hash_file_odin_28
|
||||
ctx.hash_stream_28 = hash_stream_odin_28
|
||||
ctx.hash_bytes_32 = hash_bytes_odin_32
|
||||
ctx.hash_file_32 = hash_file_odin_32
|
||||
ctx.hash_stream_32 = hash_stream_odin_32
|
||||
ctx.hash_bytes_48 = hash_bytes_odin_48
|
||||
ctx.hash_file_48 = hash_file_odin_48
|
||||
ctx.hash_stream_48 = hash_stream_odin_48
|
||||
ctx.hash_bytes_64 = hash_bytes_odin_64
|
||||
ctx.hash_file_64 = hash_file_odin_64
|
||||
ctx.hash_stream_64 = hash_stream_odin_64
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_SHA3)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// hash_string_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_224 :: proc(data: string) -> [28]byte {
|
||||
return hash_bytes_224(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_224 :: proc(data: []byte) -> [28]byte {
|
||||
_create_sha3_ctx(28)
|
||||
return _hash_impl->hash_bytes_28(data)
|
||||
}
|
||||
|
||||
// hash_stream_224 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
|
||||
_create_sha3_ctx(28)
|
||||
return _hash_impl->hash_stream_28(s)
|
||||
}
|
||||
|
||||
// hash_file_224 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
|
||||
_create_sha3_ctx(28)
|
||||
return _hash_impl->hash_file_28(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_224 :: proc {
|
||||
hash_stream_224,
|
||||
hash_file_224,
|
||||
hash_bytes_224,
|
||||
hash_string_224,
|
||||
}
|
||||
|
||||
// hash_string_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_256 :: proc(data: string) -> [32]byte {
|
||||
return hash_bytes_256(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_256 :: proc(data: []byte) -> [32]byte {
|
||||
_create_sha3_ctx(32)
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
}
|
||||
|
||||
// hash_stream_256 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
|
||||
_create_sha3_ctx(32)
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
}
|
||||
|
||||
// hash_file_256 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
_create_sha3_ctx(32)
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_256 :: proc {
|
||||
hash_stream_256,
|
||||
hash_file_256,
|
||||
hash_bytes_256,
|
||||
hash_string_256,
|
||||
}
|
||||
|
||||
// hash_string_384 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_384 :: proc(data: string) -> [48]byte {
|
||||
return hash_bytes_384(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_384 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_384 :: proc(data: []byte) -> [48]byte {
|
||||
_create_sha3_ctx(48)
|
||||
return _hash_impl->hash_bytes_48(data)
|
||||
}
|
||||
|
||||
// hash_stream_384 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
|
||||
_create_sha3_ctx(48)
|
||||
return _hash_impl->hash_stream_48(s)
|
||||
}
|
||||
|
||||
// hash_file_384 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
|
||||
_create_sha3_ctx(48)
|
||||
return _hash_impl->hash_file_48(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_384 :: proc {
|
||||
hash_stream_384,
|
||||
hash_file_384,
|
||||
hash_bytes_384,
|
||||
hash_string_384,
|
||||
}
|
||||
|
||||
// hash_string_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_512 :: proc(data: string) -> [64]byte {
|
||||
return hash_bytes_512(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_512 :: proc(data: []byte) -> [64]byte {
|
||||
_create_sha3_ctx(64)
|
||||
return _hash_impl->hash_bytes_64(data)
|
||||
}
|
||||
|
||||
// hash_stream_512 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
|
||||
_create_sha3_ctx(64)
|
||||
return _hash_impl->hash_stream_64(s)
|
||||
}
|
||||
|
||||
// hash_file_512 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
_create_sha3_ctx(64)
|
||||
return _hash_impl->hash_file_64(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_512 :: proc {
|
||||
hash_stream_512,
|
||||
hash_file_512,
|
||||
hash_bytes_512,
|
||||
hash_string_512,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte {
|
||||
hash: [28]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
_sha3.update_odin(&c, data)
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) {
|
||||
hash: [28]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_28(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_28(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [28]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
_sha3.update_odin(&c, data)
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_32(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [32]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte {
|
||||
hash: [48]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
_sha3.update_odin(&c, data)
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([48]byte, bool) {
|
||||
hash: [48]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_48(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_48(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [48]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
_sha3.update_odin(&c, data)
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_64(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_64(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [64]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_sha3_ctx :: #force_inline proc(mdlen: int) {
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = mdlen
|
||||
_hash_impl.internal_ctx = ctx
|
||||
switch mdlen {
|
||||
case 28: _hash_impl.hash_size = ._28
|
||||
case 32: _hash_impl.hash_size = ._32
|
||||
case 48: _hash_impl.hash_size = ._48
|
||||
case 64: _hash_impl.hash_size = ._64
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
#partial switch ctx.hash_size {
|
||||
case ._28: _create_sha3_ctx(28)
|
||||
case ._32: _create_sha3_ctx(32)
|
||||
case ._48: _create_sha3_ctx(48)
|
||||
case ._64: _create_sha3_ctx(64)
|
||||
}
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
package shake
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Interface for the SHAKE hashing algorithm.
|
||||
The SHA3 functionality can be found in package sha3.
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
import "../_sha3"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_16 = hash_bytes_odin_16
|
||||
ctx.hash_file_16 = hash_file_odin_16
|
||||
ctx.hash_stream_16 = hash_stream_odin_16
|
||||
ctx.hash_bytes_32 = hash_bytes_odin_32
|
||||
ctx.hash_file_32 = hash_file_odin_32
|
||||
ctx.hash_stream_32 = hash_stream_odin_32
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_SHAKE)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// hash_string_128 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_128 :: proc(data: string) -> [16]byte {
|
||||
return hash_bytes_128(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_128 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_128 :: proc(data: []byte) -> [16]byte {
|
||||
_create_shake_ctx(16)
|
||||
return _hash_impl->hash_bytes_16(data)
|
||||
}
|
||||
|
||||
// hash_stream_128 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
|
||||
_create_shake_ctx(16)
|
||||
return _hash_impl->hash_stream_16(s)
|
||||
}
|
||||
|
||||
// hash_file_128 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
|
||||
_create_shake_ctx(16)
|
||||
return _hash_impl->hash_file_16(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_128 :: proc {
|
||||
hash_stream_128,
|
||||
hash_file_128,
|
||||
hash_bytes_128,
|
||||
hash_string_128,
|
||||
}
|
||||
|
||||
// hash_string_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_256 :: proc(data: string) -> [32]byte {
|
||||
return hash_bytes_256(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_256 :: proc(data: []byte) -> [32]byte {
|
||||
_create_shake_ctx(32)
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
}
|
||||
|
||||
// hash_stream_256 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
|
||||
_create_shake_ctx(32)
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
}
|
||||
|
||||
// hash_file_256 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
_create_shake_ctx(32)
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_256 :: proc {
|
||||
hash_stream_256,
|
||||
hash_file_256,
|
||||
hash_bytes_256,
|
||||
hash_string_256,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
_sha3.update_odin(&c, data)
|
||||
_sha3.shake_xof_odin(&c)
|
||||
_sha3.shake_out_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.shake_xof_odin(&c)
|
||||
_sha3.shake_out_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_16(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_16(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [16]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
_sha3.update_odin(&c, data)
|
||||
_sha3.shake_xof_odin(&c)
|
||||
_sha3.shake_out_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.shake_xof_odin(&c)
|
||||
_sha3.shake_out_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_32(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [32]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_shake_ctx :: #force_inline proc(mdlen: int) {
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = mdlen
|
||||
_hash_impl.internal_ctx = ctx
|
||||
switch mdlen {
|
||||
case 16: _hash_impl.hash_size = ._16
|
||||
case 32: _hash_impl.hash_size = ._32
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
#partial switch ctx.hash_size {
|
||||
case ._16: _create_shake_ctx(16)
|
||||
case ._32: _create_shake_ctx(32)
|
||||
}
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.shake_xof_odin(&c)
|
||||
_sha3.shake_out_odin(&c, hash[:])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,487 @@
|
||||
package skein
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the SKEIN hashing algorithm, as defined in <https://www.schneier.com/academic/skein/>
|
||||
|
||||
This package offers the internal state sizes of 256, 512 and 1024 bits and arbitrary output size.
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
ctx.is_using_odin = false
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
ctx.is_using_odin = true
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
// @note(zh): Default to SKEIN-512
|
||||
ctx.hash_bytes_slice = hash_bytes_skein512_odin
|
||||
ctx.hash_file_slice = hash_file_skein512_odin
|
||||
ctx.hash_stream_slice = hash_stream_skein512_odin
|
||||
ctx.init = _init_skein512_odin
|
||||
ctx.update = _update_skein512_odin
|
||||
ctx.final = _final_skein512_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
_hash_impl.is_using_odin = false
|
||||
// @note(zh): Botan only supports SKEIN-512.
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_SKEIN_512)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
@(warning="SKEIN is not yet implemented in Odin. Botan bindings will be used")
|
||||
use_odin :: #force_inline proc() {
|
||||
// _hash_impl.is_using_odin = true
|
||||
// _assign_hash_vtable(_hash_impl)
|
||||
use_botan()
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_skein256_ctx :: #force_inline proc(size: int) {
|
||||
_hash_impl.hash_size_val = size
|
||||
if _hash_impl.is_using_odin {
|
||||
ctx: Skein256_Context
|
||||
ctx.h.bit_length = u64(size)
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_bytes_slice = hash_bytes_skein256_odin
|
||||
_hash_impl.hash_file_slice = hash_file_skein256_odin
|
||||
_hash_impl.hash_stream_slice = hash_stream_skein256_odin
|
||||
_hash_impl.init = _init_skein256_odin
|
||||
_hash_impl.update = _update_skein256_odin
|
||||
_hash_impl.final = _final_skein256_odin
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_skein512_ctx :: #force_inline proc(size: int) {
|
||||
_hash_impl.hash_size_val = size
|
||||
if _hash_impl.is_using_odin {
|
||||
ctx: Skein512_Context
|
||||
ctx.h.bit_length = u64(size)
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_bytes_slice = hash_bytes_skein512_odin
|
||||
_hash_impl.hash_file_slice = hash_file_skein512_odin
|
||||
_hash_impl.hash_stream_slice = hash_stream_skein512_odin
|
||||
_hash_impl.init = _init_skein512_odin
|
||||
_hash_impl.update = _update_skein512_odin
|
||||
_hash_impl.final = _final_skein512_odin
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_skein1024_ctx :: #force_inline proc(size: int) {
|
||||
_hash_impl.hash_size_val = size
|
||||
if _hash_impl.is_using_odin {
|
||||
ctx: Skein1024_Context
|
||||
ctx.h.bit_length = u64(size)
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_bytes_slice = hash_bytes_skein1024_odin
|
||||
_hash_impl.hash_file_slice = hash_file_skein1024_odin
|
||||
_hash_impl.hash_stream_slice = hash_stream_skein1024_odin
|
||||
_hash_impl.init = _init_skein1024_odin
|
||||
_hash_impl.update = _update_skein1024_odin
|
||||
_hash_impl.final = _final_skein1024_odin
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// hash_skein256_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_skein256_string :: proc(data: string, bit_size: int, allocator := context.allocator) -> []byte {
|
||||
return hash_skein256_bytes(transmute([]byte)(data), bit_size, allocator)
|
||||
}
|
||||
|
||||
// hash_skein256_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_skein256_bytes :: proc(data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
|
||||
_create_skein256_ctx(bit_size)
|
||||
return _hash_impl->hash_bytes_slice(data, bit_size, allocator)
|
||||
}
|
||||
|
||||
// hash_skein256_stream will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_skein256_stream :: proc(s: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
|
||||
_create_skein256_ctx(bit_size)
|
||||
return _hash_impl->hash_stream_slice(s, bit_size, allocator)
|
||||
}
|
||||
|
||||
// hash_skein256_file will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_skein256_file :: proc(hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
|
||||
_create_skein256_ctx(bit_size)
|
||||
return _hash_impl->hash_file_slice(hd, bit_size, load_at_once, allocator)
|
||||
}
|
||||
|
||||
hash_skein256 :: proc {
|
||||
hash_skein256_stream,
|
||||
hash_skein256_file,
|
||||
hash_skein256_bytes,
|
||||
hash_skein256_string,
|
||||
}
|
||||
|
||||
// hash_skein512_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_skein512_string :: proc(data: string, bit_size: int, allocator := context.allocator) -> []byte {
|
||||
return hash_skein512_bytes(transmute([]byte)(data), bit_size, allocator)
|
||||
}
|
||||
|
||||
// hash_skein512_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_skein512_bytes :: proc(data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
|
||||
_create_skein512_ctx(bit_size)
|
||||
return _hash_impl->hash_bytes_slice(data, bit_size, allocator)
|
||||
}
|
||||
|
||||
// hash_skein512_stream will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_skein512_stream :: proc(s: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
|
||||
_create_skein512_ctx(bit_size)
|
||||
return _hash_impl->hash_stream_slice(s, bit_size, allocator)
|
||||
}
|
||||
|
||||
// hash_skein512_file will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_skein512_file :: proc(hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
|
||||
_create_skein512_ctx(bit_size)
|
||||
return _hash_impl->hash_file_slice(hd, bit_size, load_at_once, allocator)
|
||||
}
|
||||
|
||||
hash_skein512 :: proc {
|
||||
hash_skein512_stream,
|
||||
hash_skein512_file,
|
||||
hash_skein512_bytes,
|
||||
hash_skein512_string,
|
||||
}
|
||||
|
||||
// hash_skein1024_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_skein1024_string :: proc(data: string, bit_size: int, allocator := context.allocator) -> []byte {
|
||||
return hash_skein1024_bytes(transmute([]byte)(data), bit_size, allocator)
|
||||
}
|
||||
|
||||
// hash_skein1024_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_skein1024_bytes :: proc(data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
|
||||
_create_skein1024_ctx(bit_size)
|
||||
return _hash_impl->hash_bytes_slice(data, bit_size, allocator)
|
||||
}
|
||||
|
||||
// hash_skein1024_stream will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_skein1024_stream :: proc(s: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
|
||||
_create_skein1024_ctx(bit_size)
|
||||
return _hash_impl->hash_stream_slice(s, bit_size, allocator)
|
||||
}
|
||||
|
||||
// hash_skein1024_file will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_skein1024_file :: proc(hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
|
||||
_create_skein1024_ctx(bit_size)
|
||||
return _hash_impl->hash_file_slice(hd, bit_size, load_at_once, allocator)
|
||||
}
|
||||
|
||||
hash_skein1024 :: proc {
|
||||
hash_skein1024_stream,
|
||||
hash_skein1024_file,
|
||||
hash_skein1024_bytes,
|
||||
hash_skein1024_string,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
|
||||
hash := make([]byte, bit_size, allocator)
|
||||
if c, ok := ctx.internal_ctx.(Skein256_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
return hash
|
||||
} else {
|
||||
delete(hash)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
hash_stream_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
|
||||
hash := make([]byte, bit_size, allocator)
|
||||
if c, ok := ctx.internal_ctx.(Skein256_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
delete(hash)
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_skein256_odin(ctx, os.stream_from_handle(hd), bit_size, allocator)
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_skein256_odin(ctx, buf[:], bit_size, allocator), ok
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
hash_bytes_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
|
||||
hash := make([]byte, bit_size, allocator)
|
||||
if c, ok := ctx.internal_ctx.(Skein512_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
return hash
|
||||
} else {
|
||||
delete(hash)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
hash_stream_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
|
||||
hash := make([]byte, bit_size, allocator)
|
||||
if c, ok := ctx.internal_ctx.(Skein512_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
delete(hash)
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_skein512_odin(ctx, os.stream_from_handle(hd), bit_size, allocator)
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_skein512_odin(ctx, buf[:], bit_size, allocator), ok
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
hash_bytes_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
|
||||
hash := make([]byte, bit_size, allocator)
|
||||
if c, ok := ctx.internal_ctx.(Skein1024_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
return hash
|
||||
} else {
|
||||
delete(hash)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
hash_stream_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
|
||||
hash := make([]byte, bit_size, allocator)
|
||||
if c, ok := ctx.internal_ctx.(Skein1024_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
delete(hash)
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_skein512_odin(ctx, os.stream_from_handle(hd), bit_size, allocator)
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_skein512_odin(ctx, buf[:], bit_size, allocator), ok
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_skein256_ctx(ctx.hash_size_val)
|
||||
if c, ok := ctx.internal_ctx.(Skein256_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Skein256_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Skein256_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_skein512_ctx(ctx.hash_size_val)
|
||||
if c, ok := ctx.internal_ctx.(Skein512_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Skein512_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Skein512_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_skein1024_ctx(ctx.hash_size_val)
|
||||
if c, ok := ctx.internal_ctx.(Skein1024_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Skein1024_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Skein1024_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
SKEIN implementation
|
||||
*/
|
||||
|
||||
STATE_WORDS_256 :: 4
|
||||
STATE_WORDS_512 :: 8
|
||||
STATE_WORDS_1024 :: 16
|
||||
|
||||
STATE_BYTES_256 :: 32
|
||||
STATE_BYTES_512 :: 64
|
||||
STATE_BYTES_1024 :: 128
|
||||
|
||||
Skein_Header :: struct {
|
||||
bit_length: u64,
|
||||
bcnt: u64,
|
||||
t: [2]u64,
|
||||
}
|
||||
|
||||
Skein256_Context :: struct {
|
||||
h: Skein_Header,
|
||||
x: [STATE_WORDS_256]u64,
|
||||
b: [STATE_BYTES_256]byte,
|
||||
}
|
||||
|
||||
Skein512_Context :: struct {
|
||||
h: Skein_Header,
|
||||
x: [STATE_WORDS_512]u64,
|
||||
b: [STATE_BYTES_512]byte,
|
||||
}
|
||||
|
||||
Skein1024_Context :: struct {
|
||||
h: Skein_Header,
|
||||
x: [STATE_WORDS_1024]u64,
|
||||
b: [STATE_BYTES_1024]byte,
|
||||
}
|
||||
|
||||
|
||||
init_odin :: proc(ctx: ^$T) {
|
||||
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^$T, data: []byte) {
|
||||
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^$T, hash: []byte) {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,333 @@
|
||||
package sm3
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the SM3 hashing algorithm, as defined in <https://datatracker.ietf.org/doc/html/draft-sca-cfrg-sm3-02>
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../util"
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_32 = hash_bytes_odin
|
||||
ctx.hash_file_32 = hash_file_odin
|
||||
ctx.hash_stream_32 = hash_stream_odin
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_SM3)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// hash_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string :: proc(data: string) -> [32]byte {
|
||||
return hash_bytes(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes :: proc(data: []byte) -> [32]byte {
|
||||
_create_sm3_ctx()
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
}
|
||||
|
||||
// hash_stream will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream :: proc(s: io.Stream) -> ([32]byte, bool) {
|
||||
_create_sm3_ctx()
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
}
|
||||
|
||||
// hash_file will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
_create_sm3_ctx()
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash :: proc {
|
||||
hash_stream,
|
||||
hash_file,
|
||||
hash_bytes,
|
||||
hash_string,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Sm3_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Sm3_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [32]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_sm3_ctx :: #force_inline proc() {
|
||||
ctx: Sm3_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._32
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_sm3_ctx()
|
||||
if c, ok := ctx.internal_ctx.(Sm3_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Sm3_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Sm3_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
SM3 implementation
|
||||
*/
|
||||
|
||||
Sm3_Context :: struct {
|
||||
state: [8]u32,
|
||||
x: [64]byte,
|
||||
bitlength: u64,
|
||||
length: u64,
|
||||
}
|
||||
|
||||
BLOCK_SIZE_IN_BYTES :: 64
|
||||
BLOCK_SIZE_IN_32 :: 16
|
||||
|
||||
IV := [8]u32 {
|
||||
0x7380166f, 0x4914b2b9, 0x172442d7, 0xda8a0600,
|
||||
0xa96f30bc, 0x163138aa, 0xe38dee4d, 0xb0fb0e4e,
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^Sm3_Context) {
|
||||
ctx.state[0] = IV[0]
|
||||
ctx.state[1] = IV[1]
|
||||
ctx.state[2] = IV[2]
|
||||
ctx.state[3] = IV[3]
|
||||
ctx.state[4] = IV[4]
|
||||
ctx.state[5] = IV[5]
|
||||
ctx.state[6] = IV[6]
|
||||
ctx.state[7] = IV[7]
|
||||
}
|
||||
|
||||
block :: proc "contextless" (ctx: ^Sm3_Context, buf: []byte) {
|
||||
buf := buf
|
||||
|
||||
w: [68]u32
|
||||
wp: [64]u32
|
||||
|
||||
state0, state1, state2, state3 := ctx.state[0], ctx.state[1], ctx.state[2], ctx.state[3]
|
||||
state4, state5, state6, state7 := ctx.state[4], ctx.state[5], ctx.state[6], ctx.state[7]
|
||||
|
||||
for len(buf) >= 64 {
|
||||
for i := 0; i < 16; i += 1 {
|
||||
j := i * 4
|
||||
w[i] = u32(buf[j]) << 24 | u32(buf[j + 1]) << 16 | u32(buf[j + 2]) << 8 | u32(buf[j + 3])
|
||||
}
|
||||
for i := 16; i < 68; i += 1 {
|
||||
p1v := w[i - 16] ~ w[i - 9] ~ util.ROTL32(w[i - 3], 15)
|
||||
// @note(zh): inlined P1
|
||||
w[i] = p1v ~ util.ROTL32(p1v, 15) ~ util.ROTL32(p1v, 23) ~ util.ROTL32(w[i - 13], 7) ~ w[i - 6]
|
||||
}
|
||||
for i := 0; i < 64; i += 1 {
|
||||
wp[i] = w[i] ~ w[i + 4]
|
||||
}
|
||||
|
||||
a, b, c, d := state0, state1, state2, state3
|
||||
e, f, g, h := state4, state5, state6, state7
|
||||
|
||||
for i := 0; i < 16; i += 1 {
|
||||
v1 := util.ROTL32(u32(a), 12)
|
||||
ss1 := util.ROTL32(v1 + u32(e) + util.ROTL32(0x79cc4519, i), 7)
|
||||
ss2 := ss1 ~ v1
|
||||
|
||||
// @note(zh): inlined FF1
|
||||
tt1 := u32(a ~ b ~ c) + u32(d) + ss2 + wp[i]
|
||||
// @note(zh): inlined GG1
|
||||
tt2 := u32(e ~ f ~ g) + u32(h) + ss1 + w[i]
|
||||
|
||||
a, b, c, d = tt1, a, util.ROTL32(u32(b), 9), c
|
||||
// @note(zh): inlined P0
|
||||
e, f, g, h = (tt2 ~ util.ROTL32(tt2, 9) ~ util.ROTL32(tt2, 17)), e, util.ROTL32(u32(f), 19), g
|
||||
}
|
||||
|
||||
for i := 16; i < 64; i += 1 {
|
||||
v := util.ROTL32(u32(a), 12)
|
||||
ss1 := util.ROTL32(v + u32(e) + util.ROTL32(0x7a879d8a, i % 32), 7)
|
||||
ss2 := ss1 ~ v
|
||||
|
||||
// @note(zh): inlined FF2
|
||||
tt1 := u32(((a & b) | (a & c) | (b & c)) + d) + ss2 + wp[i]
|
||||
// @note(zh): inlined GG2
|
||||
tt2 := u32(((e & f) | ((~e) & g)) + h) + ss1 + w[i]
|
||||
|
||||
a, b, c, d = tt1, a, util.ROTL32(u32(b), 9), c
|
||||
// @note(zh): inlined P0
|
||||
e, f, g, h = (tt2 ~ util.ROTL32(tt2, 9) ~ util.ROTL32(tt2, 17)), e, util.ROTL32(u32(f), 19), g
|
||||
}
|
||||
|
||||
state0 ~= a
|
||||
state1 ~= b
|
||||
state2 ~= c
|
||||
state3 ~= d
|
||||
state4 ~= e
|
||||
state5 ~= f
|
||||
state6 ~= g
|
||||
state7 ~= h
|
||||
|
||||
buf = buf[64:]
|
||||
}
|
||||
|
||||
ctx.state[0], ctx.state[1], ctx.state[2], ctx.state[3] = state0, state1, state2, state3
|
||||
ctx.state[4], ctx.state[5], ctx.state[6], ctx.state[7] = state4, state5, state6, state7
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^Sm3_Context, data: []byte) {
|
||||
data := data
|
||||
ctx.length += u64(len(data))
|
||||
|
||||
if ctx.bitlength > 0 {
|
||||
n := copy(ctx.x[ctx.bitlength:], data[:])
|
||||
ctx.bitlength += u64(n)
|
||||
if ctx.bitlength == 64 {
|
||||
block(ctx, ctx.x[:])
|
||||
ctx.bitlength = 0
|
||||
}
|
||||
data = data[n:]
|
||||
}
|
||||
if len(data) >= 64 {
|
||||
n := len(data) &~ (64 - 1)
|
||||
block(ctx, data[:n])
|
||||
data = data[n:]
|
||||
}
|
||||
if len(data) > 0 {
|
||||
ctx.bitlength = u64(copy(ctx.x[:], data[:]))
|
||||
}
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^Sm3_Context, hash: []byte) {
|
||||
length := ctx.length
|
||||
|
||||
pad: [64]byte
|
||||
pad[0] = 0x80
|
||||
if length % 64 < 56 {
|
||||
update_odin(ctx, pad[0: 56 - length % 64])
|
||||
} else {
|
||||
update_odin(ctx, pad[0: 64 + 56 - length % 64])
|
||||
}
|
||||
|
||||
length <<= 3
|
||||
util.PUT_U64_BE(pad[:], length)
|
||||
update_odin(ctx, pad[0: 8])
|
||||
assert(ctx.bitlength == 0)
|
||||
|
||||
util.PUT_U32_BE(hash[0:], ctx.state[0])
|
||||
util.PUT_U32_BE(hash[4:], ctx.state[1])
|
||||
util.PUT_U32_BE(hash[8:], ctx.state[2])
|
||||
util.PUT_U32_BE(hash[12:], ctx.state[3])
|
||||
util.PUT_U32_BE(hash[16:], ctx.state[4])
|
||||
util.PUT_U32_BE(hash[20:], ctx.state[5])
|
||||
util.PUT_U32_BE(hash[24:], ctx.state[6])
|
||||
util.PUT_U32_BE(hash[28:], ctx.state[7])
|
||||
}
|
||||
@@ -0,0 +1,596 @@
|
||||
package streebog
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the Streebog hashing algorithm, standardized as GOST R 34.11-2012 in RFC 6986 <https://datatracker.ietf.org/doc/html/rfc6986>
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../util"
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_32 = hash_bytes_odin_32
|
||||
ctx.hash_file_32 = hash_file_odin_32
|
||||
ctx.hash_stream_32 = hash_stream_odin_32
|
||||
ctx.hash_bytes_64 = hash_bytes_odin_64
|
||||
ctx.hash_file_64 = hash_file_odin_64
|
||||
ctx.hash_stream_64 = hash_stream_odin_64
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_STREEBOG)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_streebog_ctx :: #force_inline proc(is256: bool) {
|
||||
ctx: Streebog_Context
|
||||
ctx.is256 = is256
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = is256 ? ._32 : ._64
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// hash_string_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_256 :: proc(data: string) -> [32]byte {
|
||||
return hash_bytes_256(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_256 :: proc(data: []byte) -> [32]byte {
|
||||
_create_streebog_ctx(true)
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
}
|
||||
|
||||
// hash_stream_256 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
|
||||
_create_streebog_ctx(true)
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
}
|
||||
|
||||
// hash_file_256 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
_create_streebog_ctx(true)
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_256 :: proc {
|
||||
hash_stream_256,
|
||||
hash_file_256,
|
||||
hash_bytes_256,
|
||||
hash_string_256,
|
||||
}
|
||||
|
||||
// hash_string_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_512 :: proc(data: string) -> [64]byte {
|
||||
return hash_bytes_512(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_512 :: proc(data: []byte) -> [64]byte {
|
||||
_create_streebog_ctx(false)
|
||||
return _hash_impl->hash_bytes_64(data)
|
||||
}
|
||||
|
||||
// hash_stream_512 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
|
||||
_create_streebog_ctx(false)
|
||||
return _hash_impl->hash_stream_64(s)
|
||||
}
|
||||
|
||||
// hash_file_512 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
_create_streebog_ctx(false)
|
||||
return _hash_impl->hash_file_64(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_512 :: proc {
|
||||
hash_stream_512,
|
||||
hash_file_512,
|
||||
hash_bytes_512,
|
||||
hash_string_512,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Streebog_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Streebog_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_32(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [32]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(Streebog_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(Streebog_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_64(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_64(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [64]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_streebog_ctx(ctx.hash_size == ._32)
|
||||
if c, ok := ctx.internal_ctx.(Streebog_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Streebog_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Streebog_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Streebog implementation
|
||||
*/
|
||||
|
||||
PI := [256]byte {
|
||||
252, 238, 221, 17, 207, 110, 49, 22, 251, 196, 250, 218, 35, 197, 4, 77,
|
||||
233, 119, 240, 219, 147, 46, 153, 186, 23, 54, 241, 187, 20, 205, 95, 193,
|
||||
249, 24, 101, 90, 226, 92, 239, 33, 129, 28, 60, 66, 139, 1, 142, 79,
|
||||
5, 132, 2, 174, 227, 106, 143, 160, 6, 11, 237, 152, 127, 212, 211, 31,
|
||||
235, 52, 44, 81, 234, 200, 72, 171, 242, 42, 104, 162, 253, 58, 206, 204,
|
||||
181, 112, 14, 86, 8, 12, 118, 18, 191, 114, 19, 71, 156, 183, 93, 135,
|
||||
21, 161, 150, 41, 16, 123, 154, 199, 243, 145, 120, 111, 157, 158, 178, 177,
|
||||
50, 117, 25, 61, 255, 53, 138, 126, 109, 84, 198, 128, 195, 189, 13, 87,
|
||||
223, 245, 36, 169, 62, 168, 67, 201, 215, 121, 214, 246, 124, 34, 185, 3,
|
||||
224, 15, 236, 222, 122, 148, 176, 188, 220, 232, 40, 80, 78, 51, 10, 74,
|
||||
167, 151, 96, 115, 30, 0, 98, 68, 26, 184, 56, 130, 100, 159, 38, 65,
|
||||
173, 69, 70, 146, 39, 94, 85, 47, 140, 163, 165, 125, 105, 213, 149, 59,
|
||||
7, 88, 179, 64, 134, 172, 29, 247, 48, 55, 107, 228, 136, 217, 231, 137,
|
||||
225, 27, 131, 73, 76, 63, 248, 254, 141, 83, 170, 144, 202, 216, 133, 97,
|
||||
32, 113, 103, 164, 45, 43, 9, 91, 203, 155, 37, 208, 190, 229, 108, 82,
|
||||
89, 166, 116, 210, 230, 244, 180, 192, 209, 102, 175, 194, 57, 75, 99, 182,
|
||||
}
|
||||
|
||||
TAU := [64]byte {
|
||||
0, 8, 16, 24, 32, 40, 48, 56,
|
||||
1, 9, 17, 25, 33, 41, 49, 57,
|
||||
2, 10, 18, 26, 34, 42, 50, 58,
|
||||
3, 11, 19, 27, 35, 43, 51, 59,
|
||||
4, 12, 20, 28, 36, 44, 52, 60,
|
||||
5, 13, 21, 29, 37, 45, 53, 61,
|
||||
6, 14, 22, 30, 38, 46, 54, 62,
|
||||
7, 15, 23, 31, 39, 47, 55, 63,
|
||||
}
|
||||
|
||||
STREEBOG_A := [64]u64 {
|
||||
0x8e20faa72ba0b470, 0x47107ddd9b505a38, 0xad08b0e0c3282d1c, 0xd8045870ef14980e,
|
||||
0x6c022c38f90a4c07, 0x3601161cf205268d, 0x1b8e0b0e798c13c8, 0x83478b07b2468764,
|
||||
0xa011d380818e8f40, 0x5086e740ce47c920, 0x2843fd2067adea10, 0x14aff010bdd87508,
|
||||
0x0ad97808d06cb404, 0x05e23c0468365a02, 0x8c711e02341b2d01, 0x46b60f011a83988e,
|
||||
0x90dab52a387ae76f, 0x486dd4151c3dfdb9, 0x24b86a840e90f0d2, 0x125c354207487869,
|
||||
0x092e94218d243cba, 0x8a174a9ec8121e5d, 0x4585254f64090fa0, 0xaccc9ca9328a8950,
|
||||
0x9d4df05d5f661451, 0xc0a878a0a1330aa6, 0x60543c50de970553, 0x302a1e286fc58ca7,
|
||||
0x18150f14b9ec46dd, 0x0c84890ad27623e0, 0x0642ca05693b9f70, 0x0321658cba93c138,
|
||||
0x86275df09ce8aaa8, 0x439da0784e745554, 0xafc0503c273aa42a, 0xd960281e9d1d5215,
|
||||
0xe230140fc0802984, 0x71180a8960409a42, 0xb60c05ca30204d21, 0x5b068c651810a89e,
|
||||
0x456c34887a3805b9, 0xac361a443d1c8cd2, 0x561b0d22900e4669, 0x2b838811480723ba,
|
||||
0x9bcf4486248d9f5d, 0xc3e9224312c8c1a0, 0xeffa11af0964ee50, 0xf97d86d98a327728,
|
||||
0xe4fa2054a80b329c, 0x727d102a548b194e, 0x39b008152acb8227, 0x9258048415eb419d,
|
||||
0x492c024284fbaec0, 0xaa16012142f35760, 0x550b8e9e21f7a530, 0xa48b474f9ef5dc18,
|
||||
0x70a6a56e2440598e, 0x3853dc371220a247, 0x1ca76e95091051ad, 0x0edd37c48a08a6d8,
|
||||
0x07e095624504536c, 0x8d70c431ac02a736, 0xc83862965601dd1b, 0x641c314b2b8ee083,
|
||||
}
|
||||
|
||||
STREEBOG_C := [12][64]byte {
|
||||
{
|
||||
0x07, 0x45, 0xa6, 0xf2, 0x59, 0x65, 0x80, 0xdd,
|
||||
0x23, 0x4d, 0x74, 0xcc, 0x36, 0x74, 0x76, 0x05,
|
||||
0x15, 0xd3, 0x60, 0xa4, 0x08, 0x2a, 0x42, 0xa2,
|
||||
0x01, 0x69, 0x67, 0x92, 0x91, 0xe0, 0x7c, 0x4b,
|
||||
0xfc, 0xc4, 0x85, 0x75, 0x8d, 0xb8, 0x4e, 0x71,
|
||||
0x16, 0xd0, 0x45, 0x2e, 0x43, 0x76, 0x6a, 0x2f,
|
||||
0x1f, 0x7c, 0x65, 0xc0, 0x81, 0x2f, 0xcb, 0xeb,
|
||||
0xe9, 0xda, 0xca, 0x1e, 0xda, 0x5b, 0x08, 0xb1,
|
||||
},
|
||||
{
|
||||
0xb7, 0x9b, 0xb1, 0x21, 0x70, 0x04, 0x79, 0xe6,
|
||||
0x56, 0xcd, 0xcb, 0xd7, 0x1b, 0xa2, 0xdd, 0x55,
|
||||
0xca, 0xa7, 0x0a, 0xdb, 0xc2, 0x61, 0xb5, 0x5c,
|
||||
0x58, 0x99, 0xd6, 0x12, 0x6b, 0x17, 0xb5, 0x9a,
|
||||
0x31, 0x01, 0xb5, 0x16, 0x0f, 0x5e, 0xd5, 0x61,
|
||||
0x98, 0x2b, 0x23, 0x0a, 0x72, 0xea, 0xfe, 0xf3,
|
||||
0xd7, 0xb5, 0x70, 0x0f, 0x46, 0x9d, 0xe3, 0x4f,
|
||||
0x1a, 0x2f, 0x9d, 0xa9, 0x8a, 0xb5, 0xa3, 0x6f,
|
||||
},
|
||||
{
|
||||
0xb2, 0x0a, 0xba, 0x0a, 0xf5, 0x96, 0x1e, 0x99,
|
||||
0x31, 0xdb, 0x7a, 0x86, 0x43, 0xf4, 0xb6, 0xc2,
|
||||
0x09, 0xdb, 0x62, 0x60, 0x37, 0x3a, 0xc9, 0xc1,
|
||||
0xb1, 0x9e, 0x35, 0x90, 0xe4, 0x0f, 0xe2, 0xd3,
|
||||
0x7b, 0x7b, 0x29, 0xb1, 0x14, 0x75, 0xea, 0xf2,
|
||||
0x8b, 0x1f, 0x9c, 0x52, 0x5f, 0x5e, 0xf1, 0x06,
|
||||
0x35, 0x84, 0x3d, 0x6a, 0x28, 0xfc, 0x39, 0x0a,
|
||||
0xc7, 0x2f, 0xce, 0x2b, 0xac, 0xdc, 0x74, 0xf5,
|
||||
},
|
||||
{
|
||||
0x2e, 0xd1, 0xe3, 0x84, 0xbc, 0xbe, 0x0c, 0x22,
|
||||
0xf1, 0x37, 0xe8, 0x93, 0xa1, 0xea, 0x53, 0x34,
|
||||
0xbe, 0x03, 0x52, 0x93, 0x33, 0x13, 0xb7, 0xd8,
|
||||
0x75, 0xd6, 0x03, 0xed, 0x82, 0x2c, 0xd7, 0xa9,
|
||||
0x3f, 0x35, 0x5e, 0x68, 0xad, 0x1c, 0x72, 0x9d,
|
||||
0x7d, 0x3c, 0x5c, 0x33, 0x7e, 0x85, 0x8e, 0x48,
|
||||
0xdd, 0xe4, 0x71, 0x5d, 0xa0, 0xe1, 0x48, 0xf9,
|
||||
0xd2, 0x66, 0x15, 0xe8, 0xb3, 0xdf, 0x1f, 0xef,
|
||||
},
|
||||
{
|
||||
0x57, 0xfe, 0x6c, 0x7c, 0xfd, 0x58, 0x17, 0x60,
|
||||
0xf5, 0x63, 0xea, 0xa9, 0x7e, 0xa2, 0x56, 0x7a,
|
||||
0x16, 0x1a, 0x27, 0x23, 0xb7, 0x00, 0xff, 0xdf,
|
||||
0xa3, 0xf5, 0x3a, 0x25, 0x47, 0x17, 0xcd, 0xbf,
|
||||
0xbd, 0xff, 0x0f, 0x80, 0xd7, 0x35, 0x9e, 0x35,
|
||||
0x4a, 0x10, 0x86, 0x16, 0x1f, 0x1c, 0x15, 0x7f,
|
||||
0x63, 0x23, 0xa9, 0x6c, 0x0c, 0x41, 0x3f, 0x9a,
|
||||
0x99, 0x47, 0x47, 0xad, 0xac, 0x6b, 0xea, 0x4b,
|
||||
},
|
||||
{
|
||||
0x6e, 0x7d, 0x64, 0x46, 0x7a, 0x40, 0x68, 0xfa,
|
||||
0x35, 0x4f, 0x90, 0x36, 0x72, 0xc5, 0x71, 0xbf,
|
||||
0xb6, 0xc6, 0xbe, 0xc2, 0x66, 0x1f, 0xf2, 0x0a,
|
||||
0xb4, 0xb7, 0x9a, 0x1c, 0xb7, 0xa6, 0xfa, 0xcf,
|
||||
0xc6, 0x8e, 0xf0, 0x9a, 0xb4, 0x9a, 0x7f, 0x18,
|
||||
0x6c, 0xa4, 0x42, 0x51, 0xf9, 0xc4, 0x66, 0x2d,
|
||||
0xc0, 0x39, 0x30, 0x7a, 0x3b, 0xc3, 0xa4, 0x6f,
|
||||
0xd9, 0xd3, 0x3a, 0x1d, 0xae, 0xae, 0x4f, 0xae,
|
||||
},
|
||||
{
|
||||
0x93, 0xd4, 0x14, 0x3a, 0x4d, 0x56, 0x86, 0x88,
|
||||
0xf3, 0x4a, 0x3c, 0xa2, 0x4c, 0x45, 0x17, 0x35,
|
||||
0x04, 0x05, 0x4a, 0x28, 0x83, 0x69, 0x47, 0x06,
|
||||
0x37, 0x2c, 0x82, 0x2d, 0xc5, 0xab, 0x92, 0x09,
|
||||
0xc9, 0x93, 0x7a, 0x19, 0x33, 0x3e, 0x47, 0xd3,
|
||||
0xc9, 0x87, 0xbf, 0xe6, 0xc7, 0xc6, 0x9e, 0x39,
|
||||
0x54, 0x09, 0x24, 0xbf, 0xfe, 0x86, 0xac, 0x51,
|
||||
0xec, 0xc5, 0xaa, 0xee, 0x16, 0x0e, 0xc7, 0xf4,
|
||||
},
|
||||
{
|
||||
0x1e, 0xe7, 0x02, 0xbf, 0xd4, 0x0d, 0x7f, 0xa4,
|
||||
0xd9, 0xa8, 0x51, 0x59, 0x35, 0xc2, 0xac, 0x36,
|
||||
0x2f, 0xc4, 0xa5, 0xd1, 0x2b, 0x8d, 0xd1, 0x69,
|
||||
0x90, 0x06, 0x9b, 0x92, 0xcb, 0x2b, 0x89, 0xf4,
|
||||
0x9a, 0xc4, 0xdb, 0x4d, 0x3b, 0x44, 0xb4, 0x89,
|
||||
0x1e, 0xde, 0x36, 0x9c, 0x71, 0xf8, 0xb7, 0x4e,
|
||||
0x41, 0x41, 0x6e, 0x0c, 0x02, 0xaa, 0xe7, 0x03,
|
||||
0xa7, 0xc9, 0x93, 0x4d, 0x42, 0x5b, 0x1f, 0x9b,
|
||||
},
|
||||
{
|
||||
0xdb, 0x5a, 0x23, 0x83, 0x51, 0x44, 0x61, 0x72,
|
||||
0x60, 0x2a, 0x1f, 0xcb, 0x92, 0xdc, 0x38, 0x0e,
|
||||
0x54, 0x9c, 0x07, 0xa6, 0x9a, 0x8a, 0x2b, 0x7b,
|
||||
0xb1, 0xce, 0xb2, 0xdb, 0x0b, 0x44, 0x0a, 0x80,
|
||||
0x84, 0x09, 0x0d, 0xe0, 0xb7, 0x55, 0xd9, 0x3c,
|
||||
0x24, 0x42, 0x89, 0x25, 0x1b, 0x3a, 0x7d, 0x3a,
|
||||
0xde, 0x5f, 0x16, 0xec, 0xd8, 0x9a, 0x4c, 0x94,
|
||||
0x9b, 0x22, 0x31, 0x16, 0x54, 0x5a, 0x8f, 0x37,
|
||||
},
|
||||
{
|
||||
0xed, 0x9c, 0x45, 0x98, 0xfb, 0xc7, 0xb4, 0x74,
|
||||
0xc3, 0xb6, 0x3b, 0x15, 0xd1, 0xfa, 0x98, 0x36,
|
||||
0xf4, 0x52, 0x76, 0x3b, 0x30, 0x6c, 0x1e, 0x7a,
|
||||
0x4b, 0x33, 0x69, 0xaf, 0x02, 0x67, 0xe7, 0x9f,
|
||||
0x03, 0x61, 0x33, 0x1b, 0x8a, 0xe1, 0xff, 0x1f,
|
||||
0xdb, 0x78, 0x8a, 0xff, 0x1c, 0xe7, 0x41, 0x89,
|
||||
0xf3, 0xf3, 0xe4, 0xb2, 0x48, 0xe5, 0x2a, 0x38,
|
||||
0x52, 0x6f, 0x05, 0x80, 0xa6, 0xde, 0xbe, 0xab,
|
||||
},
|
||||
{
|
||||
0x1b, 0x2d, 0xf3, 0x81, 0xcd, 0xa4, 0xca, 0x6b,
|
||||
0x5d, 0xd8, 0x6f, 0xc0, 0x4a, 0x59, 0xa2, 0xde,
|
||||
0x98, 0x6e, 0x47, 0x7d, 0x1d, 0xcd, 0xba, 0xef,
|
||||
0xca, 0xb9, 0x48, 0xea, 0xef, 0x71, 0x1d, 0x8a,
|
||||
0x79, 0x66, 0x84, 0x14, 0x21, 0x80, 0x01, 0x20,
|
||||
0x61, 0x07, 0xab, 0xeb, 0xbb, 0x6b, 0xfa, 0xd8,
|
||||
0x94, 0xfe, 0x5a, 0x63, 0xcd, 0xc6, 0x02, 0x30,
|
||||
0xfb, 0x89, 0xc8, 0xef, 0xd0, 0x9e, 0xcd, 0x7b,
|
||||
},
|
||||
{
|
||||
0x20, 0xd7, 0x1b, 0xf1, 0x4a, 0x92, 0xbc, 0x48,
|
||||
0x99, 0x1b, 0xb2, 0xd9, 0xd5, 0x17, 0xf4, 0xfa,
|
||||
0x52, 0x28, 0xe1, 0x88, 0xaa, 0xa4, 0x1d, 0xe7,
|
||||
0x86, 0xcc, 0x91, 0x18, 0x9d, 0xef, 0x80, 0x5d,
|
||||
0x9b, 0x9f, 0x21, 0x30, 0xd4, 0x12, 0x20, 0xf8,
|
||||
0x77, 0x1d, 0xdf, 0xbc, 0x32, 0x3c, 0xa4, 0xcd,
|
||||
0x7a, 0xb1, 0x49, 0x04, 0xb0, 0x80, 0x13, 0xd2,
|
||||
0xba, 0x31, 0x16, 0xf1, 0x67, 0xe7, 0x8e, 0x37,
|
||||
},
|
||||
}
|
||||
|
||||
Streebog_Context :: struct {
|
||||
buffer: [64]byte,
|
||||
h: [64]byte,
|
||||
n: [64]byte,
|
||||
sigma: [64]byte,
|
||||
v_0: [64]byte,
|
||||
v_512: [64]byte,
|
||||
buf_size: u64,
|
||||
hash_size: int,
|
||||
is256: bool,
|
||||
}
|
||||
|
||||
add_mod_512 :: proc(first_vector, second_vector, result_vector: []byte) {
|
||||
t: i32 = 0
|
||||
for i: i32 = 0; i < 64; i += 1 {
|
||||
t = i32(first_vector[i]) + i32(second_vector[i]) + (t >> 8)
|
||||
result_vector[i] = byte(t & 0xff)
|
||||
}
|
||||
}
|
||||
|
||||
X :: #force_inline proc(a, k, out: []byte) {
|
||||
for i := 0; i < 64; i += 1 {
|
||||
out[i] = a[i] ~ k[i]
|
||||
}
|
||||
}
|
||||
|
||||
S :: #force_inline proc(state: []byte) {
|
||||
t: [64]byte
|
||||
for i: i32 = 63; i >= 0; i -= 1 {
|
||||
t[i] = PI[state[i]]
|
||||
}
|
||||
copy(state, t[:])
|
||||
}
|
||||
|
||||
P :: #force_inline proc(state: []byte) {
|
||||
t: [64]byte
|
||||
for i: i32 = 63; i >= 0; i -= 1 {
|
||||
t[i] = state[TAU[i]]
|
||||
}
|
||||
copy(state, t[:])
|
||||
}
|
||||
|
||||
L :: #force_inline proc(state: []byte) {
|
||||
ins := util.cast_slice([]u64, state)
|
||||
out: [8]u64
|
||||
for i: i32 = 7; i >= 0; i -= 1 {
|
||||
for j: i32 = 63; j >= 0; j -= 1 {
|
||||
if (ins[i] >> u32(j)) & 1 != 0 {
|
||||
out[i] ~= STREEBOG_A[63 - j]
|
||||
}
|
||||
}
|
||||
}
|
||||
copy(state, util.cast_slice([]byte, out[:]))
|
||||
}
|
||||
|
||||
E :: #force_inline proc(K, m, state: []byte) {
|
||||
X(m, K, state)
|
||||
for i: i32 = 0; i < 12; i += 1 {
|
||||
S(state)
|
||||
P(state)
|
||||
L(state)
|
||||
get_key(K, i)
|
||||
X(state, K, state)
|
||||
}
|
||||
}
|
||||
|
||||
get_key :: #force_inline proc(K: []byte, i: i32) {
|
||||
X(K, STREEBOG_C[i][:], K)
|
||||
S(K)
|
||||
P(K)
|
||||
L(K)
|
||||
}
|
||||
|
||||
G :: #force_inline proc(h, N, m: []byte) {
|
||||
t, K: [64]byte
|
||||
X(N, h, K[:])
|
||||
S(K[:])
|
||||
P(K[:])
|
||||
L(K[:])
|
||||
E(K[:], m, t[:])
|
||||
X(t[:], h, t[:])
|
||||
X(t[:], m, h)
|
||||
}
|
||||
|
||||
stage2 :: proc(ctx: ^Streebog_Context, m: []byte) {
|
||||
G(ctx.h[:], ctx.n[:], m)
|
||||
add_mod_512(ctx.n[:], ctx.v_512[:], ctx.n[:])
|
||||
add_mod_512(ctx.sigma[:], m, ctx.sigma[:])
|
||||
}
|
||||
|
||||
padding :: proc(ctx: ^Streebog_Context) {
|
||||
if ctx.buf_size < 64 {
|
||||
t: [64]byte
|
||||
copy(t[:], ctx.buffer[:int(ctx.buf_size)])
|
||||
t[ctx.buf_size] = 0x01
|
||||
copy(ctx.buffer[:], t[:])
|
||||
}
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^Streebog_Context) {
|
||||
if ctx.is256 {
|
||||
ctx.hash_size = 256
|
||||
for _, i in ctx.h {
|
||||
ctx.h[i] = 0x01
|
||||
}
|
||||
} else {
|
||||
ctx.hash_size = 512
|
||||
}
|
||||
ctx.v_512[1] = 0x02
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^Streebog_Context, data: []byte) {
|
||||
length := u64(len(data))
|
||||
chk_size: u64
|
||||
data := data
|
||||
for (length > 63) && (ctx.buf_size == 0) {
|
||||
stage2(ctx, data)
|
||||
data = data[64:]
|
||||
length -= 64
|
||||
}
|
||||
|
||||
for length != 0 {
|
||||
chk_size = 64 - ctx.buf_size
|
||||
if chk_size > length {
|
||||
chk_size = length
|
||||
}
|
||||
copy(ctx.buffer[ctx.buf_size:], data[:chk_size])
|
||||
ctx.buf_size += chk_size
|
||||
length -= chk_size
|
||||
data = data[chk_size:]
|
||||
if ctx.buf_size == 64 {
|
||||
stage2(ctx, ctx.buffer[:])
|
||||
ctx.buf_size = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^Streebog_Context, hash: []byte) {
|
||||
t: [64]byte
|
||||
t[1] = byte((ctx.buf_size * 8) >> 8) & 0xff
|
||||
t[0] = byte((ctx.buf_size) * 8) & 0xff
|
||||
|
||||
padding(ctx)
|
||||
|
||||
G(ctx.h[:], ctx.n[:], ctx.buffer[:])
|
||||
|
||||
add_mod_512(ctx.n[:], t[:], ctx.n[:])
|
||||
add_mod_512(ctx.sigma[:], ctx.buffer[:], ctx.sigma[:])
|
||||
|
||||
G(ctx.h[:], ctx.v_0[:], ctx.n[:])
|
||||
G(ctx.h[:], ctx.v_0[:], ctx.sigma[:])
|
||||
|
||||
if ctx.is256 {
|
||||
copy(hash[:], ctx.h[32:])
|
||||
} else {
|
||||
copy(hash[:], ctx.h[:])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,331 @@
|
||||
package tiger
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Interface for the Tiger1 variant of the Tiger hashing algorithm as defined in <https://www.cs.technion.ac.il/~biham/Reports/Tiger/>
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
import "../_tiger"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_16 = hash_bytes_odin_16
|
||||
ctx.hash_file_16 = hash_file_odin_16
|
||||
ctx.hash_stream_16 = hash_stream_odin_16
|
||||
ctx.hash_bytes_20 = hash_bytes_odin_20
|
||||
ctx.hash_file_20 = hash_file_odin_20
|
||||
ctx.hash_stream_20 = hash_stream_odin_20
|
||||
ctx.hash_bytes_24 = hash_bytes_odin_24
|
||||
ctx.hash_file_24 = hash_file_odin_24
|
||||
ctx.hash_stream_24 = hash_stream_odin_24
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_TIGER)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// hash_string_128 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_128 :: proc(data: string) -> [16]byte {
|
||||
return hash_bytes_128(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_128 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_128 :: proc(data: []byte) -> [16]byte {
|
||||
_create_tiger_ctx(16)
|
||||
return _hash_impl->hash_bytes_16(data)
|
||||
}
|
||||
|
||||
// hash_stream_128 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
|
||||
_create_tiger_ctx(16)
|
||||
return _hash_impl->hash_stream_16(s)
|
||||
}
|
||||
|
||||
// hash_file_128 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
|
||||
_create_tiger_ctx(16)
|
||||
return _hash_impl->hash_file_16(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_128 :: proc {
|
||||
hash_stream_128,
|
||||
hash_file_128,
|
||||
hash_bytes_128,
|
||||
hash_string_128,
|
||||
}
|
||||
|
||||
// hash_string_160 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_160 :: proc(data: string) -> [20]byte {
|
||||
return hash_bytes_160(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_160 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_160 :: proc(data: []byte) -> [20]byte {
|
||||
_create_tiger_ctx(20)
|
||||
return _hash_impl->hash_bytes_20(data)
|
||||
}
|
||||
|
||||
// hash_stream_160 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_160 :: proc(s: io.Stream) -> ([20]byte, bool) {
|
||||
_create_tiger_ctx(20)
|
||||
return _hash_impl->hash_stream_20(s)
|
||||
}
|
||||
|
||||
// hash_file_160 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
|
||||
_create_tiger_ctx(20)
|
||||
return _hash_impl->hash_file_20(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_160 :: proc {
|
||||
hash_stream_160,
|
||||
hash_file_160,
|
||||
hash_bytes_160,
|
||||
hash_string_160,
|
||||
}
|
||||
|
||||
// hash_string_192 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_192 :: proc(data: string) -> [24]byte {
|
||||
return hash_bytes_192(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_192 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_192 :: proc(data: []byte) -> [24]byte {
|
||||
_create_tiger_ctx(24)
|
||||
return _hash_impl->hash_bytes_24(data)
|
||||
}
|
||||
|
||||
// hash_stream_192 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_192 :: proc(s: io.Stream) -> ([24]byte, bool) {
|
||||
_create_tiger_ctx(24)
|
||||
return _hash_impl->hash_stream_24(s)
|
||||
}
|
||||
|
||||
// hash_file_192 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_192 :: proc(hd: os.Handle, load_at_once := false) -> ([24]byte, bool) {
|
||||
_create_tiger_ctx(24)
|
||||
return _hash_impl->hash_file_24(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_192 :: proc {
|
||||
hash_stream_192,
|
||||
hash_file_192,
|
||||
hash_bytes_192,
|
||||
hash_string_192,
|
||||
}
|
||||
|
||||
hash_bytes_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
_tiger.update_odin(&c, data)
|
||||
_tiger.final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_tiger.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_tiger.final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_16(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_16(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [16]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [20]byte {
|
||||
hash: [20]byte
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
_tiger.update_odin(&c, data)
|
||||
_tiger.final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([20]byte, bool) {
|
||||
hash: [20]byte
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_tiger.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_tiger.final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_20(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_20(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [20]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [24]byte {
|
||||
hash: [24]byte
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
_tiger.update_odin(&c, data)
|
||||
_tiger.final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([24]byte, bool) {
|
||||
hash: [24]byte
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_tiger.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_tiger.final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([24]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_24(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_24(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [24]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_tiger_ctx :: #force_inline proc(hash_size: int) {
|
||||
ctx: _tiger.Tiger_Context
|
||||
ctx.ver = 1
|
||||
_hash_impl.internal_ctx = ctx
|
||||
switch hash_size {
|
||||
case 16: _hash_impl.hash_size = ._16
|
||||
case 20: _hash_impl.hash_size = ._20
|
||||
case 24: _hash_impl.hash_size = ._24
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
#partial switch ctx.hash_size {
|
||||
case ._16: _create_tiger_ctx(16)
|
||||
case ._20: _create_tiger_ctx(20)
|
||||
case ._24: _create_tiger_ctx(24)
|
||||
}
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,331 @@
|
||||
package tiger2
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Interface for the Tiger2 variant of the Tiger hashing algorithm as defined in <https://www.cs.technion.ac.il/~biham/Reports/Tiger/>
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../_ctx"
|
||||
import "../_tiger"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_16 = hash_bytes_odin_16
|
||||
ctx.hash_file_16 = hash_file_odin_16
|
||||
ctx.hash_stream_16 = hash_stream_odin_16
|
||||
ctx.hash_bytes_20 = hash_bytes_odin_20
|
||||
ctx.hash_file_20 = hash_file_odin_20
|
||||
ctx.hash_stream_20 = hash_stream_odin_20
|
||||
ctx.hash_bytes_24 = hash_bytes_odin_24
|
||||
ctx.hash_file_24 = hash_file_odin_24
|
||||
ctx.hash_stream_24 = hash_stream_odin_24
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan does nothing, since Tiger2 is not available in Botan
|
||||
@(warning="Tiger2 is not provided by the Botan API. Odin implementation will be used")
|
||||
use_botan :: #force_inline proc() {
|
||||
use_odin()
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// hash_string_128 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_128 :: proc(data: string) -> [16]byte {
|
||||
return hash_bytes_128(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_128 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_128 :: proc(data: []byte) -> [16]byte {
|
||||
_create_tiger2_ctx(16)
|
||||
return _hash_impl->hash_bytes_16(data)
|
||||
}
|
||||
|
||||
// hash_stream_128 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
|
||||
_create_tiger2_ctx(16)
|
||||
return _hash_impl->hash_stream_16(s)
|
||||
}
|
||||
|
||||
// hash_file_128 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
|
||||
_create_tiger2_ctx(16)
|
||||
return _hash_impl->hash_file_16(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_128 :: proc {
|
||||
hash_stream_128,
|
||||
hash_file_128,
|
||||
hash_bytes_128,
|
||||
hash_string_128,
|
||||
}
|
||||
|
||||
// hash_string_160 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_160 :: proc(data: string) -> [20]byte {
|
||||
return hash_bytes_160(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_160 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_160 :: proc(data: []byte) -> [20]byte {
|
||||
_create_tiger2_ctx(20)
|
||||
return _hash_impl->hash_bytes_20(data)
|
||||
}
|
||||
|
||||
// hash_stream_160 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_160 :: proc(s: io.Stream) -> ([20]byte, bool) {
|
||||
_create_tiger2_ctx(20)
|
||||
return _hash_impl->hash_stream_20(s)
|
||||
}
|
||||
|
||||
// hash_file_160 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
|
||||
_create_tiger2_ctx(20)
|
||||
return _hash_impl->hash_file_20(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_160 :: proc {
|
||||
hash_stream_160,
|
||||
hash_file_160,
|
||||
hash_bytes_160,
|
||||
hash_string_160,
|
||||
}
|
||||
|
||||
// hash_string_192 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_192 :: proc(data: string) -> [24]byte {
|
||||
return hash_bytes_192(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_192 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_192 :: proc(data: []byte) -> [24]byte {
|
||||
_create_tiger2_ctx(24)
|
||||
return _hash_impl->hash_bytes_24(data)
|
||||
}
|
||||
|
||||
// hash_stream_192 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_192 :: proc(s: io.Stream) -> ([24]byte, bool) {
|
||||
_create_tiger2_ctx(24)
|
||||
return _hash_impl->hash_stream_24(s)
|
||||
}
|
||||
|
||||
// hash_file_192 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_192 :: proc(hd: os.Handle, load_at_once := false) -> ([24]byte, bool) {
|
||||
_create_tiger2_ctx(24)
|
||||
return _hash_impl->hash_file_24(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash_192 :: proc {
|
||||
hash_stream_192,
|
||||
hash_file_192,
|
||||
hash_bytes_192,
|
||||
hash_string_192,
|
||||
}
|
||||
|
||||
hash_bytes_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
_tiger.update_odin(&c, data)
|
||||
_tiger.final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_tiger.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_tiger.final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_16(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_16(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [16]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [20]byte {
|
||||
hash: [20]byte
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
_tiger.update_odin(&c, data)
|
||||
_tiger.final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([20]byte, bool) {
|
||||
hash: [20]byte
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_tiger.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_tiger.final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_20(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_20(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [20]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [24]byte {
|
||||
hash: [24]byte
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
_tiger.update_odin(&c, data)
|
||||
_tiger.final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([24]byte, bool) {
|
||||
hash: [24]byte
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_tiger.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_tiger.final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([24]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_24(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_24(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [24]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_tiger2_ctx :: #force_inline proc(hash_size: int) {
|
||||
ctx: _tiger.Tiger_Context
|
||||
ctx.ver = 2
|
||||
_hash_impl.internal_ctx = ctx
|
||||
switch hash_size {
|
||||
case 16: _hash_impl.hash_size = ._16
|
||||
case 20: _hash_impl.hash_size = ._20
|
||||
case 24: _hash_impl.hash_size = ._24
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
#partial switch ctx.hash_size {
|
||||
case ._16: _create_tiger2_ctx(16)
|
||||
case ._20: _create_tiger2_ctx(20)
|
||||
case ._24: _create_tiger2_ctx(24)
|
||||
}
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package util
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
|
||||
Various utility procedures
|
||||
*/
|
||||
|
||||
import "core:mem"
|
||||
|
||||
// @note(bp): this can replace the other two
|
||||
cast_slice :: #force_inline proc "contextless" ($D: typeid/[]$DE, src: $S/[]$SE) -> D {
|
||||
src := src
|
||||
dst := (^mem.Raw_Slice)(&src)
|
||||
|
||||
when size_of(DE) < size_of(SE) {
|
||||
when size_of(DE) % size_of(SE) == 0 {
|
||||
dst.len /= size_of(SE) / size_of(DE)
|
||||
} else {
|
||||
dst.len *= size_of(SE)
|
||||
dst.len /= size_of(DE)
|
||||
}
|
||||
} else when size_of(DE) > size_of(SE) {
|
||||
when size_of(DE) % size_of(SE) == 0 {
|
||||
dst.len *= size_of(DE) / size_of(SE)
|
||||
} else {
|
||||
dst.len *= size_of(SE)
|
||||
dst.len /= size_of(DE)
|
||||
}
|
||||
} else when size_of(DE) != size_of(SE) {
|
||||
#assert(size_of(DE) % size_of(SE) == 0, "Different size detected")
|
||||
dst.len *= size_of(SE)
|
||||
dst.len /= size_of(DE)
|
||||
}
|
||||
|
||||
return (^D)(dst)^
|
||||
}
|
||||
|
||||
bytes_to_slice :: #force_inline proc "contextless" ($T: typeid/[]$E, bytes: []byte) -> T {
|
||||
s := transmute(mem.Raw_Slice)bytes
|
||||
s.len /= size_of(E)
|
||||
return transmute(T)s
|
||||
}
|
||||
|
||||
slice_to_bytes :: #force_inline proc "contextless" (slice: $E/[]$T) -> []byte {
|
||||
s := transmute(mem.Raw_Slice)slice
|
||||
s.len *= size_of(T)
|
||||
return transmute([]byte)s
|
||||
}
|
||||
|
||||
ROTL16 :: #force_inline proc "contextless" (a, b: u16) -> u16 {
|
||||
return ((a << b) | (a >> (16 - b)))
|
||||
}
|
||||
|
||||
ROTR16 :: #force_inline proc "contextless" (a, b: u16) -> u16 {
|
||||
return ((a >> b) | (a << (16 - b)))
|
||||
}
|
||||
|
||||
ROTL32 :: #force_inline proc "contextless"(a: u32, b: int) -> u32 {
|
||||
s := uint(b) & 31
|
||||
return (a << s) | (a >> (32 - s))
|
||||
}
|
||||
|
||||
ROTR32 :: #force_inline proc "contextless" (a: u32, b: int) -> u32 {
|
||||
s := uint(b) & 31
|
||||
return (a >> s) | (a << (32 - s))
|
||||
}
|
||||
|
||||
ROTL64 :: #force_inline proc "contextless" (a, b: u64) -> u64 {
|
||||
return ((a << b) | (a >> (64 - b)))
|
||||
}
|
||||
|
||||
ROTR64 :: #force_inline proc "contextless" (a, b: u64) -> u64 {
|
||||
return ((a >> b) | (a << (64 - b)))
|
||||
}
|
||||
|
||||
ROTL128 :: #force_inline proc "contextless" (a, b, c, d: ^u32, n: uint) {
|
||||
a, b, c, d := a, b, c, d
|
||||
t := a^ >> (32 - n)
|
||||
a^ = ((a^ << n) | (b^ >> (32 - n)))
|
||||
b^ = ((b^ << n) | (c^ >> (32 - n)))
|
||||
c^ = ((c^ << n) | (d^ >> (32 - n)))
|
||||
d^ = ((d^ << n) | t)
|
||||
}
|
||||
|
||||
U32_LE :: #force_inline proc "contextless" (b: []byte) -> u32 {
|
||||
return u32(b[0]) | u32(b[1]) << 8 | u32(b[2]) << 16 | u32(b[3]) << 24
|
||||
}
|
||||
|
||||
U64_LE :: #force_inline proc "contextless" (b: []byte) -> u64 {
|
||||
return u64(b[0]) | u64(b[1]) << 8 | u64(b[2]) << 16 | u64(b[3]) << 24 |
|
||||
u64(b[4]) << 32 | u64(b[5]) << 40 | u64(b[6]) << 48 | u64(b[7]) << 56
|
||||
}
|
||||
|
||||
U64_BE :: #force_inline proc "contextless" (b: []byte) -> u64 {
|
||||
return u64(b[7]) | u64(b[6]) << 8 | u64(b[5]) << 16 | u64(b[4]) << 24 |
|
||||
u64(b[3]) << 32 | u64(b[2]) << 40 | u64(b[1]) << 48 | u64(b[0]) << 56
|
||||
}
|
||||
|
||||
PUT_U64_LE :: #force_inline proc "contextless" (b: []byte, v: u64) {
|
||||
b[0] = byte(v)
|
||||
b[1] = byte(v >> 8)
|
||||
b[2] = byte(v >> 16)
|
||||
b[3] = byte(v >> 24)
|
||||
b[4] = byte(v >> 32)
|
||||
b[5] = byte(v >> 40)
|
||||
b[6] = byte(v >> 48)
|
||||
b[7] = byte(v >> 56)
|
||||
}
|
||||
|
||||
PUT_U32_LE :: #force_inline proc "contextless" (b: []byte, v: u32) {
|
||||
b[0] = byte(v)
|
||||
b[1] = byte(v >> 8)
|
||||
b[2] = byte(v >> 16)
|
||||
b[3] = byte(v >> 24)
|
||||
}
|
||||
|
||||
PUT_U32_BE :: #force_inline proc "contextless" (b: []byte, v: u32) {
|
||||
b[0] = byte(v >> 24)
|
||||
b[1] = byte(v >> 16)
|
||||
b[2] = byte(v >> 8)
|
||||
b[3] = byte(v)
|
||||
}
|
||||
|
||||
PUT_U64_BE :: #force_inline proc "contextless" (b: []byte, v: u64) {
|
||||
b[0] = byte(v >> 56)
|
||||
b[1] = byte(v >> 48)
|
||||
b[2] = byte(v >> 40)
|
||||
b[3] = byte(v >> 32)
|
||||
b[4] = byte(v >> 24)
|
||||
b[5] = byte(v >> 16)
|
||||
b[6] = byte(v >> 8)
|
||||
b[7] = byte(v)
|
||||
}
|
||||
|
||||
XOR_BUF :: #force_inline proc "contextless" (input, output: []byte) {
|
||||
for i := 0; i < len(input); i += 1 {
|
||||
output[i] ~= input[i]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,870 @@
|
||||
package whirlpool
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the Whirlpool hashing algorithm, as defined in <https://web.archive.org/web/20171129084214/http://www.larc.usp.br/~pbarreto/WhirlpoolPage.html>
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
import "../util"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_64 = hash_bytes_odin
|
||||
ctx.hash_file_64 = hash_file_odin
|
||||
ctx.hash_stream_64 = hash_stream_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_WHIRLPOOL)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// hash_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string :: proc(data: string) -> [64]byte {
|
||||
return hash_bytes(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes :: proc(data: []byte) -> [64]byte {
|
||||
_create_whirlpool_ctx()
|
||||
return _hash_impl->hash_bytes_64(data)
|
||||
}
|
||||
|
||||
// hash_stream will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream :: proc(s: io.Stream) -> ([64]byte, bool) {
|
||||
_create_whirlpool_ctx()
|
||||
return _hash_impl->hash_stream_64(s)
|
||||
}
|
||||
|
||||
// hash_file will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
_create_whirlpool_ctx()
|
||||
return _hash_impl->hash_file_64(hd, load_at_once)
|
||||
}
|
||||
|
||||
hash :: proc {
|
||||
hash_stream,
|
||||
hash_file,
|
||||
hash_bytes,
|
||||
hash_string,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(Whirlpool_Context); ok {
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(Whirlpool_Context); ok {
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [64]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_whirlpool_ctx :: #force_inline proc() {
|
||||
ctx: Whirlpool_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._64
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Whirlpool_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Whirlpool_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Whirlpool implementation
|
||||
*/
|
||||
|
||||
ROUNDS :: 10
|
||||
|
||||
Whirlpool_Context :: struct {
|
||||
bitlength: [32]byte,
|
||||
buffer: [64]byte,
|
||||
buffer_bits: int,
|
||||
buffer_pos: int,
|
||||
hash: [8]u64,
|
||||
}
|
||||
|
||||
C0 := [256]u64 {
|
||||
0x18186018c07830d8, 0x23238c2305af4626, 0xc6c63fc67ef991b8, 0xe8e887e8136fcdfb,
|
||||
0x878726874ca113cb, 0xb8b8dab8a9626d11, 0x0101040108050209, 0x4f4f214f426e9e0d,
|
||||
0x3636d836adee6c9b, 0xa6a6a2a6590451ff, 0xd2d26fd2debdb90c, 0xf5f5f3f5fb06f70e,
|
||||
0x7979f979ef80f296, 0x6f6fa16f5fcede30, 0x91917e91fcef3f6d, 0x52525552aa07a4f8,
|
||||
0x60609d6027fdc047, 0xbcbccabc89766535, 0x9b9b569baccd2b37, 0x8e8e028e048c018a,
|
||||
0xa3a3b6a371155bd2, 0x0c0c300c603c186c, 0x7b7bf17bff8af684, 0x3535d435b5e16a80,
|
||||
0x1d1d741de8693af5, 0xe0e0a7e05347ddb3, 0xd7d77bd7f6acb321, 0xc2c22fc25eed999c,
|
||||
0x2e2eb82e6d965c43, 0x4b4b314b627a9629, 0xfefedffea321e15d, 0x575741578216aed5,
|
||||
0x15155415a8412abd, 0x7777c1779fb6eee8, 0x3737dc37a5eb6e92, 0xe5e5b3e57b56d79e,
|
||||
0x9f9f469f8cd92313, 0xf0f0e7f0d317fd23, 0x4a4a354a6a7f9420, 0xdada4fda9e95a944,
|
||||
0x58587d58fa25b0a2, 0xc9c903c906ca8fcf, 0x2929a429558d527c, 0x0a0a280a5022145a,
|
||||
0xb1b1feb1e14f7f50, 0xa0a0baa0691a5dc9, 0x6b6bb16b7fdad614, 0x85852e855cab17d9,
|
||||
0xbdbdcebd8173673c, 0x5d5d695dd234ba8f, 0x1010401080502090, 0xf4f4f7f4f303f507,
|
||||
0xcbcb0bcb16c08bdd, 0x3e3ef83eedc67cd3, 0x0505140528110a2d, 0x676781671fe6ce78,
|
||||
0xe4e4b7e47353d597, 0x27279c2725bb4e02, 0x4141194132588273, 0x8b8b168b2c9d0ba7,
|
||||
0xa7a7a6a7510153f6, 0x7d7de97dcf94fab2, 0x95956e95dcfb3749, 0xd8d847d88e9fad56,
|
||||
0xfbfbcbfb8b30eb70, 0xeeee9fee2371c1cd, 0x7c7ced7cc791f8bb, 0x6666856617e3cc71,
|
||||
0xdddd53dda68ea77b, 0x17175c17b84b2eaf, 0x4747014702468e45, 0x9e9e429e84dc211a,
|
||||
0xcaca0fca1ec589d4, 0x2d2db42d75995a58, 0xbfbfc6bf9179632e, 0x07071c07381b0e3f,
|
||||
0xadad8ead012347ac, 0x5a5a755aea2fb4b0, 0x838336836cb51bef, 0x3333cc3385ff66b6,
|
||||
0x636391633ff2c65c, 0x02020802100a0412, 0xaaaa92aa39384993, 0x7171d971afa8e2de,
|
||||
0xc8c807c80ecf8dc6, 0x19196419c87d32d1, 0x494939497270923b, 0xd9d943d9869aaf5f,
|
||||
0xf2f2eff2c31df931, 0xe3e3abe34b48dba8, 0x5b5b715be22ab6b9, 0x88881a8834920dbc,
|
||||
0x9a9a529aa4c8293e, 0x262698262dbe4c0b, 0x3232c8328dfa64bf, 0xb0b0fab0e94a7d59,
|
||||
0xe9e983e91b6acff2, 0x0f0f3c0f78331e77, 0xd5d573d5e6a6b733, 0x80803a8074ba1df4,
|
||||
0xbebec2be997c6127, 0xcdcd13cd26de87eb, 0x3434d034bde46889, 0x48483d487a759032,
|
||||
0xffffdbffab24e354, 0x7a7af57af78ff48d, 0x90907a90f4ea3d64, 0x5f5f615fc23ebe9d,
|
||||
0x202080201da0403d, 0x6868bd6867d5d00f, 0x1a1a681ad07234ca, 0xaeae82ae192c41b7,
|
||||
0xb4b4eab4c95e757d, 0x54544d549a19a8ce, 0x93937693ece53b7f, 0x222288220daa442f,
|
||||
0x64648d6407e9c863, 0xf1f1e3f1db12ff2a, 0x7373d173bfa2e6cc, 0x12124812905a2482,
|
||||
0x40401d403a5d807a, 0x0808200840281048, 0xc3c32bc356e89b95, 0xecec97ec337bc5df,
|
||||
0xdbdb4bdb9690ab4d, 0xa1a1bea1611f5fc0, 0x8d8d0e8d1c830791, 0x3d3df43df5c97ac8,
|
||||
0x97976697ccf1335b, 0x0000000000000000, 0xcfcf1bcf36d483f9, 0x2b2bac2b4587566e,
|
||||
0x7676c57697b3ece1, 0x8282328264b019e6, 0xd6d67fd6fea9b128, 0x1b1b6c1bd87736c3,
|
||||
0xb5b5eeb5c15b7774, 0xafaf86af112943be, 0x6a6ab56a77dfd41d, 0x50505d50ba0da0ea,
|
||||
0x45450945124c8a57, 0xf3f3ebf3cb18fb38, 0x3030c0309df060ad, 0xefef9bef2b74c3c4,
|
||||
0x3f3ffc3fe5c37eda, 0x55554955921caac7, 0xa2a2b2a2791059db, 0xeaea8fea0365c9e9,
|
||||
0x656589650fecca6a, 0xbabad2bab9686903, 0x2f2fbc2f65935e4a, 0xc0c027c04ee79d8e,
|
||||
0xdede5fdebe81a160, 0x1c1c701ce06c38fc, 0xfdfdd3fdbb2ee746, 0x4d4d294d52649a1f,
|
||||
0x92927292e4e03976, 0x7575c9758fbceafa, 0x06061806301e0c36, 0x8a8a128a249809ae,
|
||||
0xb2b2f2b2f940794b, 0xe6e6bfe66359d185, 0x0e0e380e70361c7e, 0x1f1f7c1ff8633ee7,
|
||||
0x6262956237f7c455, 0xd4d477d4eea3b53a, 0xa8a89aa829324d81, 0x96966296c4f43152,
|
||||
0xf9f9c3f99b3aef62, 0xc5c533c566f697a3, 0x2525942535b14a10, 0x59597959f220b2ab,
|
||||
0x84842a8454ae15d0, 0x7272d572b7a7e4c5, 0x3939e439d5dd72ec, 0x4c4c2d4c5a619816,
|
||||
0x5e5e655eca3bbc94, 0x7878fd78e785f09f, 0x3838e038ddd870e5, 0x8c8c0a8c14860598,
|
||||
0xd1d163d1c6b2bf17, 0xa5a5aea5410b57e4, 0xe2e2afe2434dd9a1, 0x616199612ff8c24e,
|
||||
0xb3b3f6b3f1457b42, 0x2121842115a54234, 0x9c9c4a9c94d62508, 0x1e1e781ef0663cee,
|
||||
0x4343114322528661, 0xc7c73bc776fc93b1, 0xfcfcd7fcb32be54f, 0x0404100420140824,
|
||||
0x51515951b208a2e3, 0x99995e99bcc72f25, 0x6d6da96d4fc4da22, 0x0d0d340d68391a65,
|
||||
0xfafacffa8335e979, 0xdfdf5bdfb684a369, 0x7e7ee57ed79bfca9, 0x242490243db44819,
|
||||
0x3b3bec3bc5d776fe, 0xabab96ab313d4b9a, 0xcece1fce3ed181f0, 0x1111441188552299,
|
||||
0x8f8f068f0c890383, 0x4e4e254e4a6b9c04, 0xb7b7e6b7d1517366, 0xebeb8beb0b60cbe0,
|
||||
0x3c3cf03cfdcc78c1, 0x81813e817cbf1ffd, 0x94946a94d4fe3540, 0xf7f7fbf7eb0cf31c,
|
||||
0xb9b9deb9a1676f18, 0x13134c13985f268b, 0x2c2cb02c7d9c5851, 0xd3d36bd3d6b8bb05,
|
||||
0xe7e7bbe76b5cd38c, 0x6e6ea56e57cbdc39, 0xc4c437c46ef395aa, 0x03030c03180f061b,
|
||||
0x565645568a13acdc, 0x44440d441a49885e, 0x7f7fe17fdf9efea0, 0xa9a99ea921374f88,
|
||||
0x2a2aa82a4d825467, 0xbbbbd6bbb16d6b0a, 0xc1c123c146e29f87, 0x53535153a202a6f1,
|
||||
0xdcdc57dcae8ba572, 0x0b0b2c0b58271653, 0x9d9d4e9d9cd32701, 0x6c6cad6c47c1d82b,
|
||||
0x3131c43195f562a4, 0x7474cd7487b9e8f3, 0xf6f6fff6e309f115, 0x464605460a438c4c,
|
||||
0xacac8aac092645a5, 0x89891e893c970fb5, 0x14145014a04428b4, 0xe1e1a3e15b42dfba,
|
||||
0x16165816b04e2ca6, 0x3a3ae83acdd274f7, 0x6969b9696fd0d206, 0x09092409482d1241,
|
||||
0x7070dd70a7ade0d7, 0xb6b6e2b6d954716f, 0xd0d067d0ceb7bd1e, 0xeded93ed3b7ec7d6,
|
||||
0xcccc17cc2edb85e2, 0x424215422a578468, 0x98985a98b4c22d2c, 0xa4a4aaa4490e55ed,
|
||||
0x2828a0285d885075, 0x5c5c6d5cda31b886, 0xf8f8c7f8933fed6b, 0x8686228644a411c2,
|
||||
}
|
||||
|
||||
C1 := [256]u64 {
|
||||
0xd818186018c07830, 0x2623238c2305af46, 0xb8c6c63fc67ef991, 0xfbe8e887e8136fcd,
|
||||
0xcb878726874ca113, 0x11b8b8dab8a9626d, 0x0901010401080502, 0x0d4f4f214f426e9e,
|
||||
0x9b3636d836adee6c, 0xffa6a6a2a6590451, 0x0cd2d26fd2debdb9, 0x0ef5f5f3f5fb06f7,
|
||||
0x967979f979ef80f2, 0x306f6fa16f5fcede, 0x6d91917e91fcef3f, 0xf852525552aa07a4,
|
||||
0x4760609d6027fdc0, 0x35bcbccabc897665, 0x379b9b569baccd2b, 0x8a8e8e028e048c01,
|
||||
0xd2a3a3b6a371155b, 0x6c0c0c300c603c18, 0x847b7bf17bff8af6, 0x803535d435b5e16a,
|
||||
0xf51d1d741de8693a, 0xb3e0e0a7e05347dd, 0x21d7d77bd7f6acb3, 0x9cc2c22fc25eed99,
|
||||
0x432e2eb82e6d965c, 0x294b4b314b627a96, 0x5dfefedffea321e1, 0xd5575741578216ae,
|
||||
0xbd15155415a8412a, 0xe87777c1779fb6ee, 0x923737dc37a5eb6e, 0x9ee5e5b3e57b56d7,
|
||||
0x139f9f469f8cd923, 0x23f0f0e7f0d317fd, 0x204a4a354a6a7f94, 0x44dada4fda9e95a9,
|
||||
0xa258587d58fa25b0, 0xcfc9c903c906ca8f, 0x7c2929a429558d52, 0x5a0a0a280a502214,
|
||||
0x50b1b1feb1e14f7f, 0xc9a0a0baa0691a5d, 0x146b6bb16b7fdad6, 0xd985852e855cab17,
|
||||
0x3cbdbdcebd817367, 0x8f5d5d695dd234ba, 0x9010104010805020, 0x07f4f4f7f4f303f5,
|
||||
0xddcbcb0bcb16c08b, 0xd33e3ef83eedc67c, 0x2d0505140528110a, 0x78676781671fe6ce,
|
||||
0x97e4e4b7e47353d5, 0x0227279c2725bb4e, 0x7341411941325882, 0xa78b8b168b2c9d0b,
|
||||
0xf6a7a7a6a7510153, 0xb27d7de97dcf94fa, 0x4995956e95dcfb37, 0x56d8d847d88e9fad,
|
||||
0x70fbfbcbfb8b30eb, 0xcdeeee9fee2371c1, 0xbb7c7ced7cc791f8, 0x716666856617e3cc,
|
||||
0x7bdddd53dda68ea7, 0xaf17175c17b84b2e, 0x454747014702468e, 0x1a9e9e429e84dc21,
|
||||
0xd4caca0fca1ec589, 0x582d2db42d75995a, 0x2ebfbfc6bf917963, 0x3f07071c07381b0e,
|
||||
0xacadad8ead012347, 0xb05a5a755aea2fb4, 0xef838336836cb51b, 0xb63333cc3385ff66,
|
||||
0x5c636391633ff2c6, 0x1202020802100a04, 0x93aaaa92aa393849, 0xde7171d971afa8e2,
|
||||
0xc6c8c807c80ecf8d, 0xd119196419c87d32, 0x3b49493949727092, 0x5fd9d943d9869aaf,
|
||||
0x31f2f2eff2c31df9, 0xa8e3e3abe34b48db, 0xb95b5b715be22ab6, 0xbc88881a8834920d,
|
||||
0x3e9a9a529aa4c829, 0x0b262698262dbe4c, 0xbf3232c8328dfa64, 0x59b0b0fab0e94a7d,
|
||||
0xf2e9e983e91b6acf, 0x770f0f3c0f78331e, 0x33d5d573d5e6a6b7, 0xf480803a8074ba1d,
|
||||
0x27bebec2be997c61, 0xebcdcd13cd26de87, 0x893434d034bde468, 0x3248483d487a7590,
|
||||
0x54ffffdbffab24e3, 0x8d7a7af57af78ff4, 0x6490907a90f4ea3d, 0x9d5f5f615fc23ebe,
|
||||
0x3d202080201da040, 0x0f6868bd6867d5d0, 0xca1a1a681ad07234, 0xb7aeae82ae192c41,
|
||||
0x7db4b4eab4c95e75, 0xce54544d549a19a8, 0x7f93937693ece53b, 0x2f222288220daa44,
|
||||
0x6364648d6407e9c8, 0x2af1f1e3f1db12ff, 0xcc7373d173bfa2e6, 0x8212124812905a24,
|
||||
0x7a40401d403a5d80, 0x4808082008402810, 0x95c3c32bc356e89b, 0xdfecec97ec337bc5,
|
||||
0x4ddbdb4bdb9690ab, 0xc0a1a1bea1611f5f, 0x918d8d0e8d1c8307, 0xc83d3df43df5c97a,
|
||||
0x5b97976697ccf133, 0x0000000000000000, 0xf9cfcf1bcf36d483, 0x6e2b2bac2b458756,
|
||||
0xe17676c57697b3ec, 0xe68282328264b019, 0x28d6d67fd6fea9b1, 0xc31b1b6c1bd87736,
|
||||
0x74b5b5eeb5c15b77, 0xbeafaf86af112943, 0x1d6a6ab56a77dfd4, 0xea50505d50ba0da0,
|
||||
0x5745450945124c8a, 0x38f3f3ebf3cb18fb, 0xad3030c0309df060, 0xc4efef9bef2b74c3,
|
||||
0xda3f3ffc3fe5c37e, 0xc755554955921caa, 0xdba2a2b2a2791059, 0xe9eaea8fea0365c9,
|
||||
0x6a656589650fecca, 0x03babad2bab96869, 0x4a2f2fbc2f65935e, 0x8ec0c027c04ee79d,
|
||||
0x60dede5fdebe81a1, 0xfc1c1c701ce06c38, 0x46fdfdd3fdbb2ee7, 0x1f4d4d294d52649a,
|
||||
0x7692927292e4e039, 0xfa7575c9758fbcea, 0x3606061806301e0c, 0xae8a8a128a249809,
|
||||
0x4bb2b2f2b2f94079, 0x85e6e6bfe66359d1, 0x7e0e0e380e70361c, 0xe71f1f7c1ff8633e,
|
||||
0x556262956237f7c4, 0x3ad4d477d4eea3b5, 0x81a8a89aa829324d, 0x5296966296c4f431,
|
||||
0x62f9f9c3f99b3aef, 0xa3c5c533c566f697, 0x102525942535b14a, 0xab59597959f220b2,
|
||||
0xd084842a8454ae15, 0xc57272d572b7a7e4, 0xec3939e439d5dd72, 0x164c4c2d4c5a6198,
|
||||
0x945e5e655eca3bbc, 0x9f7878fd78e785f0, 0xe53838e038ddd870, 0x988c8c0a8c148605,
|
||||
0x17d1d163d1c6b2bf, 0xe4a5a5aea5410b57, 0xa1e2e2afe2434dd9, 0x4e616199612ff8c2,
|
||||
0x42b3b3f6b3f1457b, 0x342121842115a542, 0x089c9c4a9c94d625, 0xee1e1e781ef0663c,
|
||||
0x6143431143225286, 0xb1c7c73bc776fc93, 0x4ffcfcd7fcb32be5, 0x2404041004201408,
|
||||
0xe351515951b208a2, 0x2599995e99bcc72f, 0x226d6da96d4fc4da, 0x650d0d340d68391a,
|
||||
0x79fafacffa8335e9, 0x69dfdf5bdfb684a3, 0xa97e7ee57ed79bfc, 0x19242490243db448,
|
||||
0xfe3b3bec3bc5d776, 0x9aabab96ab313d4b, 0xf0cece1fce3ed181, 0x9911114411885522,
|
||||
0x838f8f068f0c8903, 0x044e4e254e4a6b9c, 0x66b7b7e6b7d15173, 0xe0ebeb8beb0b60cb,
|
||||
0xc13c3cf03cfdcc78, 0xfd81813e817cbf1f, 0x4094946a94d4fe35, 0x1cf7f7fbf7eb0cf3,
|
||||
0x18b9b9deb9a1676f, 0x8b13134c13985f26, 0x512c2cb02c7d9c58, 0x05d3d36bd3d6b8bb,
|
||||
0x8ce7e7bbe76b5cd3, 0x396e6ea56e57cbdc, 0xaac4c437c46ef395, 0x1b03030c03180f06,
|
||||
0xdc565645568a13ac, 0x5e44440d441a4988, 0xa07f7fe17fdf9efe, 0x88a9a99ea921374f,
|
||||
0x672a2aa82a4d8254, 0x0abbbbd6bbb16d6b, 0x87c1c123c146e29f, 0xf153535153a202a6,
|
||||
0x72dcdc57dcae8ba5, 0x530b0b2c0b582716, 0x019d9d4e9d9cd327, 0x2b6c6cad6c47c1d8,
|
||||
0xa43131c43195f562, 0xf37474cd7487b9e8, 0x15f6f6fff6e309f1, 0x4c464605460a438c,
|
||||
0xa5acac8aac092645, 0xb589891e893c970f, 0xb414145014a04428, 0xbae1e1a3e15b42df,
|
||||
0xa616165816b04e2c, 0xf73a3ae83acdd274, 0x066969b9696fd0d2, 0x4109092409482d12,
|
||||
0xd77070dd70a7ade0, 0x6fb6b6e2b6d95471, 0x1ed0d067d0ceb7bd, 0xd6eded93ed3b7ec7,
|
||||
0xe2cccc17cc2edb85, 0x68424215422a5784, 0x2c98985a98b4c22d, 0xeda4a4aaa4490e55,
|
||||
0x752828a0285d8850, 0x865c5c6d5cda31b8, 0x6bf8f8c7f8933fed, 0xc28686228644a411,
|
||||
}
|
||||
|
||||
C2 := [256]u64 {
|
||||
0x30d818186018c078, 0x462623238c2305af, 0x91b8c6c63fc67ef9, 0xcdfbe8e887e8136f,
|
||||
0x13cb878726874ca1, 0x6d11b8b8dab8a962, 0x0209010104010805, 0x9e0d4f4f214f426e,
|
||||
0x6c9b3636d836adee, 0x51ffa6a6a2a65904, 0xb90cd2d26fd2debd, 0xf70ef5f5f3f5fb06,
|
||||
0xf2967979f979ef80, 0xde306f6fa16f5fce, 0x3f6d91917e91fcef, 0xa4f852525552aa07,
|
||||
0xc04760609d6027fd, 0x6535bcbccabc8976, 0x2b379b9b569baccd, 0x018a8e8e028e048c,
|
||||
0x5bd2a3a3b6a37115, 0x186c0c0c300c603c, 0xf6847b7bf17bff8a, 0x6a803535d435b5e1,
|
||||
0x3af51d1d741de869, 0xddb3e0e0a7e05347, 0xb321d7d77bd7f6ac, 0x999cc2c22fc25eed,
|
||||
0x5c432e2eb82e6d96, 0x96294b4b314b627a, 0xe15dfefedffea321, 0xaed5575741578216,
|
||||
0x2abd15155415a841, 0xeee87777c1779fb6, 0x6e923737dc37a5eb, 0xd79ee5e5b3e57b56,
|
||||
0x23139f9f469f8cd9, 0xfd23f0f0e7f0d317, 0x94204a4a354a6a7f, 0xa944dada4fda9e95,
|
||||
0xb0a258587d58fa25, 0x8fcfc9c903c906ca, 0x527c2929a429558d, 0x145a0a0a280a5022,
|
||||
0x7f50b1b1feb1e14f, 0x5dc9a0a0baa0691a, 0xd6146b6bb16b7fda, 0x17d985852e855cab,
|
||||
0x673cbdbdcebd8173, 0xba8f5d5d695dd234, 0x2090101040108050, 0xf507f4f4f7f4f303,
|
||||
0x8bddcbcb0bcb16c0, 0x7cd33e3ef83eedc6, 0x0a2d050514052811, 0xce78676781671fe6,
|
||||
0xd597e4e4b7e47353, 0x4e0227279c2725bb, 0x8273414119413258, 0x0ba78b8b168b2c9d,
|
||||
0x53f6a7a7a6a75101, 0xfab27d7de97dcf94, 0x374995956e95dcfb, 0xad56d8d847d88e9f,
|
||||
0xeb70fbfbcbfb8b30, 0xc1cdeeee9fee2371, 0xf8bb7c7ced7cc791, 0xcc716666856617e3,
|
||||
0xa77bdddd53dda68e, 0x2eaf17175c17b84b, 0x8e45474701470246, 0x211a9e9e429e84dc,
|
||||
0x89d4caca0fca1ec5, 0x5a582d2db42d7599, 0x632ebfbfc6bf9179, 0x0e3f07071c07381b,
|
||||
0x47acadad8ead0123, 0xb4b05a5a755aea2f, 0x1bef838336836cb5, 0x66b63333cc3385ff,
|
||||
0xc65c636391633ff2, 0x041202020802100a, 0x4993aaaa92aa3938, 0xe2de7171d971afa8,
|
||||
0x8dc6c8c807c80ecf, 0x32d119196419c87d, 0x923b494939497270, 0xaf5fd9d943d9869a,
|
||||
0xf931f2f2eff2c31d, 0xdba8e3e3abe34b48, 0xb6b95b5b715be22a, 0x0dbc88881a883492,
|
||||
0x293e9a9a529aa4c8, 0x4c0b262698262dbe, 0x64bf3232c8328dfa, 0x7d59b0b0fab0e94a,
|
||||
0xcff2e9e983e91b6a, 0x1e770f0f3c0f7833, 0xb733d5d573d5e6a6, 0x1df480803a8074ba,
|
||||
0x6127bebec2be997c, 0x87ebcdcd13cd26de, 0x68893434d034bde4, 0x903248483d487a75,
|
||||
0xe354ffffdbffab24, 0xf48d7a7af57af78f, 0x3d6490907a90f4ea, 0xbe9d5f5f615fc23e,
|
||||
0x403d202080201da0, 0xd00f6868bd6867d5, 0x34ca1a1a681ad072, 0x41b7aeae82ae192c,
|
||||
0x757db4b4eab4c95e, 0xa8ce54544d549a19, 0x3b7f93937693ece5, 0x442f222288220daa,
|
||||
0xc86364648d6407e9, 0xff2af1f1e3f1db12, 0xe6cc7373d173bfa2, 0x248212124812905a,
|
||||
0x807a40401d403a5d, 0x1048080820084028, 0x9b95c3c32bc356e8, 0xc5dfecec97ec337b,
|
||||
0xab4ddbdb4bdb9690, 0x5fc0a1a1bea1611f, 0x07918d8d0e8d1c83, 0x7ac83d3df43df5c9,
|
||||
0x335b97976697ccf1, 0x0000000000000000, 0x83f9cfcf1bcf36d4, 0x566e2b2bac2b4587,
|
||||
0xece17676c57697b3, 0x19e68282328264b0, 0xb128d6d67fd6fea9, 0x36c31b1b6c1bd877,
|
||||
0x7774b5b5eeb5c15b, 0x43beafaf86af1129, 0xd41d6a6ab56a77df, 0xa0ea50505d50ba0d,
|
||||
0x8a5745450945124c, 0xfb38f3f3ebf3cb18, 0x60ad3030c0309df0, 0xc3c4efef9bef2b74,
|
||||
0x7eda3f3ffc3fe5c3, 0xaac755554955921c, 0x59dba2a2b2a27910, 0xc9e9eaea8fea0365,
|
||||
0xca6a656589650fec, 0x6903babad2bab968, 0x5e4a2f2fbc2f6593, 0x9d8ec0c027c04ee7,
|
||||
0xa160dede5fdebe81, 0x38fc1c1c701ce06c, 0xe746fdfdd3fdbb2e, 0x9a1f4d4d294d5264,
|
||||
0x397692927292e4e0, 0xeafa7575c9758fbc, 0x0c3606061806301e, 0x09ae8a8a128a2498,
|
||||
0x794bb2b2f2b2f940, 0xd185e6e6bfe66359, 0x1c7e0e0e380e7036, 0x3ee71f1f7c1ff863,
|
||||
0xc4556262956237f7, 0xb53ad4d477d4eea3, 0x4d81a8a89aa82932, 0x315296966296c4f4,
|
||||
0xef62f9f9c3f99b3a, 0x97a3c5c533c566f6, 0x4a102525942535b1, 0xb2ab59597959f220,
|
||||
0x15d084842a8454ae, 0xe4c57272d572b7a7, 0x72ec3939e439d5dd, 0x98164c4c2d4c5a61,
|
||||
0xbc945e5e655eca3b, 0xf09f7878fd78e785, 0x70e53838e038ddd8, 0x05988c8c0a8c1486,
|
||||
0xbf17d1d163d1c6b2, 0x57e4a5a5aea5410b, 0xd9a1e2e2afe2434d, 0xc24e616199612ff8,
|
||||
0x7b42b3b3f6b3f145, 0x42342121842115a5, 0x25089c9c4a9c94d6, 0x3cee1e1e781ef066,
|
||||
0x8661434311432252, 0x93b1c7c73bc776fc, 0xe54ffcfcd7fcb32b, 0x0824040410042014,
|
||||
0xa2e351515951b208, 0x2f2599995e99bcc7, 0xda226d6da96d4fc4, 0x1a650d0d340d6839,
|
||||
0xe979fafacffa8335, 0xa369dfdf5bdfb684, 0xfca97e7ee57ed79b, 0x4819242490243db4,
|
||||
0x76fe3b3bec3bc5d7, 0x4b9aabab96ab313d, 0x81f0cece1fce3ed1, 0x2299111144118855,
|
||||
0x03838f8f068f0c89, 0x9c044e4e254e4a6b, 0x7366b7b7e6b7d151, 0xcbe0ebeb8beb0b60,
|
||||
0x78c13c3cf03cfdcc, 0x1ffd81813e817cbf, 0x354094946a94d4fe, 0xf31cf7f7fbf7eb0c,
|
||||
0x6f18b9b9deb9a167, 0x268b13134c13985f, 0x58512c2cb02c7d9c, 0xbb05d3d36bd3d6b8,
|
||||
0xd38ce7e7bbe76b5c, 0xdc396e6ea56e57cb, 0x95aac4c437c46ef3, 0x061b03030c03180f,
|
||||
0xacdc565645568a13, 0x885e44440d441a49, 0xfea07f7fe17fdf9e, 0x4f88a9a99ea92137,
|
||||
0x54672a2aa82a4d82, 0x6b0abbbbd6bbb16d, 0x9f87c1c123c146e2, 0xa6f153535153a202,
|
||||
0xa572dcdc57dcae8b, 0x16530b0b2c0b5827, 0x27019d9d4e9d9cd3, 0xd82b6c6cad6c47c1,
|
||||
0x62a43131c43195f5, 0xe8f37474cd7487b9, 0xf115f6f6fff6e309, 0x8c4c464605460a43,
|
||||
0x45a5acac8aac0926, 0x0fb589891e893c97, 0x28b414145014a044, 0xdfbae1e1a3e15b42,
|
||||
0x2ca616165816b04e, 0x74f73a3ae83acdd2, 0xd2066969b9696fd0, 0x124109092409482d,
|
||||
0xe0d77070dd70a7ad, 0x716fb6b6e2b6d954, 0xbd1ed0d067d0ceb7, 0xc7d6eded93ed3b7e,
|
||||
0x85e2cccc17cc2edb, 0x8468424215422a57, 0x2d2c98985a98b4c2, 0x55eda4a4aaa4490e,
|
||||
0x50752828a0285d88, 0xb8865c5c6d5cda31, 0xed6bf8f8c7f8933f, 0x11c28686228644a4,
|
||||
}
|
||||
|
||||
C3 := [256]u64 {
|
||||
0x7830d818186018c0, 0xaf462623238c2305, 0xf991b8c6c63fc67e, 0x6fcdfbe8e887e813,
|
||||
0xa113cb878726874c, 0x626d11b8b8dab8a9, 0x0502090101040108, 0x6e9e0d4f4f214f42,
|
||||
0xee6c9b3636d836ad, 0x0451ffa6a6a2a659, 0xbdb90cd2d26fd2de, 0x06f70ef5f5f3f5fb,
|
||||
0x80f2967979f979ef, 0xcede306f6fa16f5f, 0xef3f6d91917e91fc, 0x07a4f852525552aa,
|
||||
0xfdc04760609d6027, 0x766535bcbccabc89, 0xcd2b379b9b569bac, 0x8c018a8e8e028e04,
|
||||
0x155bd2a3a3b6a371, 0x3c186c0c0c300c60, 0x8af6847b7bf17bff, 0xe16a803535d435b5,
|
||||
0x693af51d1d741de8, 0x47ddb3e0e0a7e053, 0xacb321d7d77bd7f6, 0xed999cc2c22fc25e,
|
||||
0x965c432e2eb82e6d, 0x7a96294b4b314b62, 0x21e15dfefedffea3, 0x16aed55757415782,
|
||||
0x412abd15155415a8, 0xb6eee87777c1779f, 0xeb6e923737dc37a5, 0x56d79ee5e5b3e57b,
|
||||
0xd923139f9f469f8c, 0x17fd23f0f0e7f0d3, 0x7f94204a4a354a6a, 0x95a944dada4fda9e,
|
||||
0x25b0a258587d58fa, 0xca8fcfc9c903c906, 0x8d527c2929a42955, 0x22145a0a0a280a50,
|
||||
0x4f7f50b1b1feb1e1, 0x1a5dc9a0a0baa069, 0xdad6146b6bb16b7f, 0xab17d985852e855c,
|
||||
0x73673cbdbdcebd81, 0x34ba8f5d5d695dd2, 0x5020901010401080, 0x03f507f4f4f7f4f3,
|
||||
0xc08bddcbcb0bcb16, 0xc67cd33e3ef83eed, 0x110a2d0505140528, 0xe6ce78676781671f,
|
||||
0x53d597e4e4b7e473, 0xbb4e0227279c2725, 0x5882734141194132, 0x9d0ba78b8b168b2c,
|
||||
0x0153f6a7a7a6a751, 0x94fab27d7de97dcf, 0xfb374995956e95dc, 0x9fad56d8d847d88e,
|
||||
0x30eb70fbfbcbfb8b, 0x71c1cdeeee9fee23, 0x91f8bb7c7ced7cc7, 0xe3cc716666856617,
|
||||
0x8ea77bdddd53dda6, 0x4b2eaf17175c17b8, 0x468e454747014702, 0xdc211a9e9e429e84,
|
||||
0xc589d4caca0fca1e, 0x995a582d2db42d75, 0x79632ebfbfc6bf91, 0x1b0e3f07071c0738,
|
||||
0x2347acadad8ead01, 0x2fb4b05a5a755aea, 0xb51bef838336836c, 0xff66b63333cc3385,
|
||||
0xf2c65c636391633f, 0x0a04120202080210, 0x384993aaaa92aa39, 0xa8e2de7171d971af,
|
||||
0xcf8dc6c8c807c80e, 0x7d32d119196419c8, 0x70923b4949394972, 0x9aaf5fd9d943d986,
|
||||
0x1df931f2f2eff2c3, 0x48dba8e3e3abe34b, 0x2ab6b95b5b715be2, 0x920dbc88881a8834,
|
||||
0xc8293e9a9a529aa4, 0xbe4c0b262698262d, 0xfa64bf3232c8328d, 0x4a7d59b0b0fab0e9,
|
||||
0x6acff2e9e983e91b, 0x331e770f0f3c0f78, 0xa6b733d5d573d5e6, 0xba1df480803a8074,
|
||||
0x7c6127bebec2be99, 0xde87ebcdcd13cd26, 0xe468893434d034bd, 0x75903248483d487a,
|
||||
0x24e354ffffdbffab, 0x8ff48d7a7af57af7, 0xea3d6490907a90f4, 0x3ebe9d5f5f615fc2,
|
||||
0xa0403d202080201d, 0xd5d00f6868bd6867, 0x7234ca1a1a681ad0, 0x2c41b7aeae82ae19,
|
||||
0x5e757db4b4eab4c9, 0x19a8ce54544d549a, 0xe53b7f93937693ec, 0xaa442f222288220d,
|
||||
0xe9c86364648d6407, 0x12ff2af1f1e3f1db, 0xa2e6cc7373d173bf, 0x5a24821212481290,
|
||||
0x5d807a40401d403a, 0x2810480808200840, 0xe89b95c3c32bc356, 0x7bc5dfecec97ec33,
|
||||
0x90ab4ddbdb4bdb96, 0x1f5fc0a1a1bea161, 0x8307918d8d0e8d1c, 0xc97ac83d3df43df5,
|
||||
0xf1335b97976697cc, 0x0000000000000000, 0xd483f9cfcf1bcf36, 0x87566e2b2bac2b45,
|
||||
0xb3ece17676c57697, 0xb019e68282328264, 0xa9b128d6d67fd6fe, 0x7736c31b1b6c1bd8,
|
||||
0x5b7774b5b5eeb5c1, 0x2943beafaf86af11, 0xdfd41d6a6ab56a77, 0x0da0ea50505d50ba,
|
||||
0x4c8a574545094512, 0x18fb38f3f3ebf3cb, 0xf060ad3030c0309d, 0x74c3c4efef9bef2b,
|
||||
0xc37eda3f3ffc3fe5, 0x1caac75555495592, 0x1059dba2a2b2a279, 0x65c9e9eaea8fea03,
|
||||
0xecca6a656589650f, 0x686903babad2bab9, 0x935e4a2f2fbc2f65, 0xe79d8ec0c027c04e,
|
||||
0x81a160dede5fdebe, 0x6c38fc1c1c701ce0, 0x2ee746fdfdd3fdbb, 0x649a1f4d4d294d52,
|
||||
0xe0397692927292e4, 0xbceafa7575c9758f, 0x1e0c360606180630, 0x9809ae8a8a128a24,
|
||||
0x40794bb2b2f2b2f9, 0x59d185e6e6bfe663, 0x361c7e0e0e380e70, 0x633ee71f1f7c1ff8,
|
||||
0xf7c4556262956237, 0xa3b53ad4d477d4ee, 0x324d81a8a89aa829, 0xf4315296966296c4,
|
||||
0x3aef62f9f9c3f99b, 0xf697a3c5c533c566, 0xb14a102525942535, 0x20b2ab59597959f2,
|
||||
0xae15d084842a8454, 0xa7e4c57272d572b7, 0xdd72ec3939e439d5, 0x6198164c4c2d4c5a,
|
||||
0x3bbc945e5e655eca, 0x85f09f7878fd78e7, 0xd870e53838e038dd, 0x8605988c8c0a8c14,
|
||||
0xb2bf17d1d163d1c6, 0x0b57e4a5a5aea541, 0x4dd9a1e2e2afe243, 0xf8c24e616199612f,
|
||||
0x457b42b3b3f6b3f1, 0xa542342121842115, 0xd625089c9c4a9c94, 0x663cee1e1e781ef0,
|
||||
0x5286614343114322, 0xfc93b1c7c73bc776, 0x2be54ffcfcd7fcb3, 0x1408240404100420,
|
||||
0x08a2e351515951b2, 0xc72f2599995e99bc, 0xc4da226d6da96d4f, 0x391a650d0d340d68,
|
||||
0x35e979fafacffa83, 0x84a369dfdf5bdfb6, 0x9bfca97e7ee57ed7, 0xb44819242490243d,
|
||||
0xd776fe3b3bec3bc5, 0x3d4b9aabab96ab31, 0xd181f0cece1fce3e, 0x5522991111441188,
|
||||
0x8903838f8f068f0c, 0x6b9c044e4e254e4a, 0x517366b7b7e6b7d1, 0x60cbe0ebeb8beb0b,
|
||||
0xcc78c13c3cf03cfd, 0xbf1ffd81813e817c, 0xfe354094946a94d4, 0x0cf31cf7f7fbf7eb,
|
||||
0x676f18b9b9deb9a1, 0x5f268b13134c1398, 0x9c58512c2cb02c7d, 0xb8bb05d3d36bd3d6,
|
||||
0x5cd38ce7e7bbe76b, 0xcbdc396e6ea56e57, 0xf395aac4c437c46e, 0x0f061b03030c0318,
|
||||
0x13acdc565645568a, 0x49885e44440d441a, 0x9efea07f7fe17fdf, 0x374f88a9a99ea921,
|
||||
0x8254672a2aa82a4d, 0x6d6b0abbbbd6bbb1, 0xe29f87c1c123c146, 0x02a6f153535153a2,
|
||||
0x8ba572dcdc57dcae, 0x2716530b0b2c0b58, 0xd327019d9d4e9d9c, 0xc1d82b6c6cad6c47,
|
||||
0xf562a43131c43195, 0xb9e8f37474cd7487, 0x09f115f6f6fff6e3, 0x438c4c464605460a,
|
||||
0x2645a5acac8aac09, 0x970fb589891e893c, 0x4428b414145014a0, 0x42dfbae1e1a3e15b,
|
||||
0x4e2ca616165816b0, 0xd274f73a3ae83acd, 0xd0d2066969b9696f, 0x2d12410909240948,
|
||||
0xade0d77070dd70a7, 0x54716fb6b6e2b6d9, 0xb7bd1ed0d067d0ce, 0x7ec7d6eded93ed3b,
|
||||
0xdb85e2cccc17cc2e, 0x578468424215422a, 0xc22d2c98985a98b4, 0x0e55eda4a4aaa449,
|
||||
0x8850752828a0285d, 0x31b8865c5c6d5cda, 0x3fed6bf8f8c7f893, 0xa411c28686228644,
|
||||
}
|
||||
|
||||
C4 := [256]u64 {
|
||||
0xc07830d818186018, 0x05af462623238c23, 0x7ef991b8c6c63fc6, 0x136fcdfbe8e887e8,
|
||||
0x4ca113cb87872687, 0xa9626d11b8b8dab8, 0x0805020901010401, 0x426e9e0d4f4f214f,
|
||||
0xadee6c9b3636d836, 0x590451ffa6a6a2a6, 0xdebdb90cd2d26fd2, 0xfb06f70ef5f5f3f5,
|
||||
0xef80f2967979f979, 0x5fcede306f6fa16f, 0xfcef3f6d91917e91, 0xaa07a4f852525552,
|
||||
0x27fdc04760609d60, 0x89766535bcbccabc, 0xaccd2b379b9b569b, 0x048c018a8e8e028e,
|
||||
0x71155bd2a3a3b6a3, 0x603c186c0c0c300c, 0xff8af6847b7bf17b, 0xb5e16a803535d435,
|
||||
0xe8693af51d1d741d, 0x5347ddb3e0e0a7e0, 0xf6acb321d7d77bd7, 0x5eed999cc2c22fc2,
|
||||
0x6d965c432e2eb82e, 0x627a96294b4b314b, 0xa321e15dfefedffe, 0x8216aed557574157,
|
||||
0xa8412abd15155415, 0x9fb6eee87777c177, 0xa5eb6e923737dc37, 0x7b56d79ee5e5b3e5,
|
||||
0x8cd923139f9f469f, 0xd317fd23f0f0e7f0, 0x6a7f94204a4a354a, 0x9e95a944dada4fda,
|
||||
0xfa25b0a258587d58, 0x06ca8fcfc9c903c9, 0x558d527c2929a429, 0x5022145a0a0a280a,
|
||||
0xe14f7f50b1b1feb1, 0x691a5dc9a0a0baa0, 0x7fdad6146b6bb16b, 0x5cab17d985852e85,
|
||||
0x8173673cbdbdcebd, 0xd234ba8f5d5d695d, 0x8050209010104010, 0xf303f507f4f4f7f4,
|
||||
0x16c08bddcbcb0bcb, 0xedc67cd33e3ef83e, 0x28110a2d05051405, 0x1fe6ce7867678167,
|
||||
0x7353d597e4e4b7e4, 0x25bb4e0227279c27, 0x3258827341411941, 0x2c9d0ba78b8b168b,
|
||||
0x510153f6a7a7a6a7, 0xcf94fab27d7de97d, 0xdcfb374995956e95, 0x8e9fad56d8d847d8,
|
||||
0x8b30eb70fbfbcbfb, 0x2371c1cdeeee9fee, 0xc791f8bb7c7ced7c, 0x17e3cc7166668566,
|
||||
0xa68ea77bdddd53dd, 0xb84b2eaf17175c17, 0x02468e4547470147, 0x84dc211a9e9e429e,
|
||||
0x1ec589d4caca0fca, 0x75995a582d2db42d, 0x9179632ebfbfc6bf, 0x381b0e3f07071c07,
|
||||
0x012347acadad8ead, 0xea2fb4b05a5a755a, 0x6cb51bef83833683, 0x85ff66b63333cc33,
|
||||
0x3ff2c65c63639163, 0x100a041202020802, 0x39384993aaaa92aa, 0xafa8e2de7171d971,
|
||||
0x0ecf8dc6c8c807c8, 0xc87d32d119196419, 0x7270923b49493949, 0x869aaf5fd9d943d9,
|
||||
0xc31df931f2f2eff2, 0x4b48dba8e3e3abe3, 0xe22ab6b95b5b715b, 0x34920dbc88881a88,
|
||||
0xa4c8293e9a9a529a, 0x2dbe4c0b26269826, 0x8dfa64bf3232c832, 0xe94a7d59b0b0fab0,
|
||||
0x1b6acff2e9e983e9, 0x78331e770f0f3c0f, 0xe6a6b733d5d573d5, 0x74ba1df480803a80,
|
||||
0x997c6127bebec2be, 0x26de87ebcdcd13cd, 0xbde468893434d034, 0x7a75903248483d48,
|
||||
0xab24e354ffffdbff, 0xf78ff48d7a7af57a, 0xf4ea3d6490907a90, 0xc23ebe9d5f5f615f,
|
||||
0x1da0403d20208020, 0x67d5d00f6868bd68, 0xd07234ca1a1a681a, 0x192c41b7aeae82ae,
|
||||
0xc95e757db4b4eab4, 0x9a19a8ce54544d54, 0xece53b7f93937693, 0x0daa442f22228822,
|
||||
0x07e9c86364648d64, 0xdb12ff2af1f1e3f1, 0xbfa2e6cc7373d173, 0x905a248212124812,
|
||||
0x3a5d807a40401d40, 0x4028104808082008, 0x56e89b95c3c32bc3, 0x337bc5dfecec97ec,
|
||||
0x9690ab4ddbdb4bdb, 0x611f5fc0a1a1bea1, 0x1c8307918d8d0e8d, 0xf5c97ac83d3df43d,
|
||||
0xccf1335b97976697, 0x0000000000000000, 0x36d483f9cfcf1bcf, 0x4587566e2b2bac2b,
|
||||
0x97b3ece17676c576, 0x64b019e682823282, 0xfea9b128d6d67fd6, 0xd87736c31b1b6c1b,
|
||||
0xc15b7774b5b5eeb5, 0x112943beafaf86af, 0x77dfd41d6a6ab56a, 0xba0da0ea50505d50,
|
||||
0x124c8a5745450945, 0xcb18fb38f3f3ebf3, 0x9df060ad3030c030, 0x2b74c3c4efef9bef,
|
||||
0xe5c37eda3f3ffc3f, 0x921caac755554955, 0x791059dba2a2b2a2, 0x0365c9e9eaea8fea,
|
||||
0x0fecca6a65658965, 0xb9686903babad2ba, 0x65935e4a2f2fbc2f, 0x4ee79d8ec0c027c0,
|
||||
0xbe81a160dede5fde, 0xe06c38fc1c1c701c, 0xbb2ee746fdfdd3fd, 0x52649a1f4d4d294d,
|
||||
0xe4e0397692927292, 0x8fbceafa7575c975, 0x301e0c3606061806, 0x249809ae8a8a128a,
|
||||
0xf940794bb2b2f2b2, 0x6359d185e6e6bfe6, 0x70361c7e0e0e380e, 0xf8633ee71f1f7c1f,
|
||||
0x37f7c45562629562, 0xeea3b53ad4d477d4, 0x29324d81a8a89aa8, 0xc4f4315296966296,
|
||||
0x9b3aef62f9f9c3f9, 0x66f697a3c5c533c5, 0x35b14a1025259425, 0xf220b2ab59597959,
|
||||
0x54ae15d084842a84, 0xb7a7e4c57272d572, 0xd5dd72ec3939e439, 0x5a6198164c4c2d4c,
|
||||
0xca3bbc945e5e655e, 0xe785f09f7878fd78, 0xddd870e53838e038, 0x148605988c8c0a8c,
|
||||
0xc6b2bf17d1d163d1, 0x410b57e4a5a5aea5, 0x434dd9a1e2e2afe2, 0x2ff8c24e61619961,
|
||||
0xf1457b42b3b3f6b3, 0x15a5423421218421, 0x94d625089c9c4a9c, 0xf0663cee1e1e781e,
|
||||
0x2252866143431143, 0x76fc93b1c7c73bc7, 0xb32be54ffcfcd7fc, 0x2014082404041004,
|
||||
0xb208a2e351515951, 0xbcc72f2599995e99, 0x4fc4da226d6da96d, 0x68391a650d0d340d,
|
||||
0x8335e979fafacffa, 0xb684a369dfdf5bdf, 0xd79bfca97e7ee57e, 0x3db4481924249024,
|
||||
0xc5d776fe3b3bec3b, 0x313d4b9aabab96ab, 0x3ed181f0cece1fce, 0x8855229911114411,
|
||||
0x0c8903838f8f068f, 0x4a6b9c044e4e254e, 0xd1517366b7b7e6b7, 0x0b60cbe0ebeb8beb,
|
||||
0xfdcc78c13c3cf03c, 0x7cbf1ffd81813e81, 0xd4fe354094946a94, 0xeb0cf31cf7f7fbf7,
|
||||
0xa1676f18b9b9deb9, 0x985f268b13134c13, 0x7d9c58512c2cb02c, 0xd6b8bb05d3d36bd3,
|
||||
0x6b5cd38ce7e7bbe7, 0x57cbdc396e6ea56e, 0x6ef395aac4c437c4, 0x180f061b03030c03,
|
||||
0x8a13acdc56564556, 0x1a49885e44440d44, 0xdf9efea07f7fe17f, 0x21374f88a9a99ea9,
|
||||
0x4d8254672a2aa82a, 0xb16d6b0abbbbd6bb, 0x46e29f87c1c123c1, 0xa202a6f153535153,
|
||||
0xae8ba572dcdc57dc, 0x582716530b0b2c0b, 0x9cd327019d9d4e9d, 0x47c1d82b6c6cad6c,
|
||||
0x95f562a43131c431, 0x87b9e8f37474cd74, 0xe309f115f6f6fff6, 0x0a438c4c46460546,
|
||||
0x092645a5acac8aac, 0x3c970fb589891e89, 0xa04428b414145014, 0x5b42dfbae1e1a3e1,
|
||||
0xb04e2ca616165816, 0xcdd274f73a3ae83a, 0x6fd0d2066969b969, 0x482d124109092409,
|
||||
0xa7ade0d77070dd70, 0xd954716fb6b6e2b6, 0xceb7bd1ed0d067d0, 0x3b7ec7d6eded93ed,
|
||||
0x2edb85e2cccc17cc, 0x2a57846842421542, 0xb4c22d2c98985a98, 0x490e55eda4a4aaa4,
|
||||
0x5d8850752828a028, 0xda31b8865c5c6d5c, 0x933fed6bf8f8c7f8, 0x44a411c286862286,
|
||||
}
|
||||
|
||||
C5 := [256]u64 {
|
||||
0x18c07830d8181860, 0x2305af462623238c, 0xc67ef991b8c6c63f, 0xe8136fcdfbe8e887,
|
||||
0x874ca113cb878726, 0xb8a9626d11b8b8da, 0x0108050209010104, 0x4f426e9e0d4f4f21,
|
||||
0x36adee6c9b3636d8, 0xa6590451ffa6a6a2, 0xd2debdb90cd2d26f, 0xf5fb06f70ef5f5f3,
|
||||
0x79ef80f2967979f9, 0x6f5fcede306f6fa1, 0x91fcef3f6d91917e, 0x52aa07a4f8525255,
|
||||
0x6027fdc04760609d, 0xbc89766535bcbcca, 0x9baccd2b379b9b56, 0x8e048c018a8e8e02,
|
||||
0xa371155bd2a3a3b6, 0x0c603c186c0c0c30, 0x7bff8af6847b7bf1, 0x35b5e16a803535d4,
|
||||
0x1de8693af51d1d74, 0xe05347ddb3e0e0a7, 0xd7f6acb321d7d77b, 0xc25eed999cc2c22f,
|
||||
0x2e6d965c432e2eb8, 0x4b627a96294b4b31, 0xfea321e15dfefedf, 0x578216aed5575741,
|
||||
0x15a8412abd151554, 0x779fb6eee87777c1, 0x37a5eb6e923737dc, 0xe57b56d79ee5e5b3,
|
||||
0x9f8cd923139f9f46, 0xf0d317fd23f0f0e7, 0x4a6a7f94204a4a35, 0xda9e95a944dada4f,
|
||||
0x58fa25b0a258587d, 0xc906ca8fcfc9c903, 0x29558d527c2929a4, 0x0a5022145a0a0a28,
|
||||
0xb1e14f7f50b1b1fe, 0xa0691a5dc9a0a0ba, 0x6b7fdad6146b6bb1, 0x855cab17d985852e,
|
||||
0xbd8173673cbdbdce, 0x5dd234ba8f5d5d69, 0x1080502090101040, 0xf4f303f507f4f4f7,
|
||||
0xcb16c08bddcbcb0b, 0x3eedc67cd33e3ef8, 0x0528110a2d050514, 0x671fe6ce78676781,
|
||||
0xe47353d597e4e4b7, 0x2725bb4e0227279c, 0x4132588273414119, 0x8b2c9d0ba78b8b16,
|
||||
0xa7510153f6a7a7a6, 0x7dcf94fab27d7de9, 0x95dcfb374995956e, 0xd88e9fad56d8d847,
|
||||
0xfb8b30eb70fbfbcb, 0xee2371c1cdeeee9f, 0x7cc791f8bb7c7ced, 0x6617e3cc71666685,
|
||||
0xdda68ea77bdddd53, 0x17b84b2eaf17175c, 0x4702468e45474701, 0x9e84dc211a9e9e42,
|
||||
0xca1ec589d4caca0f, 0x2d75995a582d2db4, 0xbf9179632ebfbfc6, 0x07381b0e3f07071c,
|
||||
0xad012347acadad8e, 0x5aea2fb4b05a5a75, 0x836cb51bef838336, 0x3385ff66b63333cc,
|
||||
0x633ff2c65c636391, 0x02100a0412020208, 0xaa39384993aaaa92, 0x71afa8e2de7171d9,
|
||||
0xc80ecf8dc6c8c807, 0x19c87d32d1191964, 0x497270923b494939, 0xd9869aaf5fd9d943,
|
||||
0xf2c31df931f2f2ef, 0xe34b48dba8e3e3ab, 0x5be22ab6b95b5b71, 0x8834920dbc88881a,
|
||||
0x9aa4c8293e9a9a52, 0x262dbe4c0b262698, 0x328dfa64bf3232c8, 0xb0e94a7d59b0b0fa,
|
||||
0xe91b6acff2e9e983, 0x0f78331e770f0f3c, 0xd5e6a6b733d5d573, 0x8074ba1df480803a,
|
||||
0xbe997c6127bebec2, 0xcd26de87ebcdcd13, 0x34bde468893434d0, 0x487a75903248483d,
|
||||
0xffab24e354ffffdb, 0x7af78ff48d7a7af5, 0x90f4ea3d6490907a, 0x5fc23ebe9d5f5f61,
|
||||
0x201da0403d202080, 0x6867d5d00f6868bd, 0x1ad07234ca1a1a68, 0xae192c41b7aeae82,
|
||||
0xb4c95e757db4b4ea, 0x549a19a8ce54544d, 0x93ece53b7f939376, 0x220daa442f222288,
|
||||
0x6407e9c86364648d, 0xf1db12ff2af1f1e3, 0x73bfa2e6cc7373d1, 0x12905a2482121248,
|
||||
0x403a5d807a40401d, 0x0840281048080820, 0xc356e89b95c3c32b, 0xec337bc5dfecec97,
|
||||
0xdb9690ab4ddbdb4b, 0xa1611f5fc0a1a1be, 0x8d1c8307918d8d0e, 0x3df5c97ac83d3df4,
|
||||
0x97ccf1335b979766, 0x0000000000000000, 0xcf36d483f9cfcf1b, 0x2b4587566e2b2bac,
|
||||
0x7697b3ece17676c5, 0x8264b019e6828232, 0xd6fea9b128d6d67f, 0x1bd87736c31b1b6c,
|
||||
0xb5c15b7774b5b5ee, 0xaf112943beafaf86, 0x6a77dfd41d6a6ab5, 0x50ba0da0ea50505d,
|
||||
0x45124c8a57454509, 0xf3cb18fb38f3f3eb, 0x309df060ad3030c0, 0xef2b74c3c4efef9b,
|
||||
0x3fe5c37eda3f3ffc, 0x55921caac7555549, 0xa2791059dba2a2b2, 0xea0365c9e9eaea8f,
|
||||
0x650fecca6a656589, 0xbab9686903babad2, 0x2f65935e4a2f2fbc, 0xc04ee79d8ec0c027,
|
||||
0xdebe81a160dede5f, 0x1ce06c38fc1c1c70, 0xfdbb2ee746fdfdd3, 0x4d52649a1f4d4d29,
|
||||
0x92e4e03976929272, 0x758fbceafa7575c9, 0x06301e0c36060618, 0x8a249809ae8a8a12,
|
||||
0xb2f940794bb2b2f2, 0xe66359d185e6e6bf, 0x0e70361c7e0e0e38, 0x1ff8633ee71f1f7c,
|
||||
0x6237f7c455626295, 0xd4eea3b53ad4d477, 0xa829324d81a8a89a, 0x96c4f43152969662,
|
||||
0xf99b3aef62f9f9c3, 0xc566f697a3c5c533, 0x2535b14a10252594, 0x59f220b2ab595979,
|
||||
0x8454ae15d084842a, 0x72b7a7e4c57272d5, 0x39d5dd72ec3939e4, 0x4c5a6198164c4c2d,
|
||||
0x5eca3bbc945e5e65, 0x78e785f09f7878fd, 0x38ddd870e53838e0, 0x8c148605988c8c0a,
|
||||
0xd1c6b2bf17d1d163, 0xa5410b57e4a5a5ae, 0xe2434dd9a1e2e2af, 0x612ff8c24e616199,
|
||||
0xb3f1457b42b3b3f6, 0x2115a54234212184, 0x9c94d625089c9c4a, 0x1ef0663cee1e1e78,
|
||||
0x4322528661434311, 0xc776fc93b1c7c73b, 0xfcb32be54ffcfcd7, 0x0420140824040410,
|
||||
0x51b208a2e3515159, 0x99bcc72f2599995e, 0x6d4fc4da226d6da9, 0x0d68391a650d0d34,
|
||||
0xfa8335e979fafacf, 0xdfb684a369dfdf5b, 0x7ed79bfca97e7ee5, 0x243db44819242490,
|
||||
0x3bc5d776fe3b3bec, 0xab313d4b9aabab96, 0xce3ed181f0cece1f, 0x1188552299111144,
|
||||
0x8f0c8903838f8f06, 0x4e4a6b9c044e4e25, 0xb7d1517366b7b7e6, 0xeb0b60cbe0ebeb8b,
|
||||
0x3cfdcc78c13c3cf0, 0x817cbf1ffd81813e, 0x94d4fe354094946a, 0xf7eb0cf31cf7f7fb,
|
||||
0xb9a1676f18b9b9de, 0x13985f268b13134c, 0x2c7d9c58512c2cb0, 0xd3d6b8bb05d3d36b,
|
||||
0xe76b5cd38ce7e7bb, 0x6e57cbdc396e6ea5, 0xc46ef395aac4c437, 0x03180f061b03030c,
|
||||
0x568a13acdc565645, 0x441a49885e44440d, 0x7fdf9efea07f7fe1, 0xa921374f88a9a99e,
|
||||
0x2a4d8254672a2aa8, 0xbbb16d6b0abbbbd6, 0xc146e29f87c1c123, 0x53a202a6f1535351,
|
||||
0xdcae8ba572dcdc57, 0x0b582716530b0b2c, 0x9d9cd327019d9d4e, 0x6c47c1d82b6c6cad,
|
||||
0x3195f562a43131c4, 0x7487b9e8f37474cd, 0xf6e309f115f6f6ff, 0x460a438c4c464605,
|
||||
0xac092645a5acac8a, 0x893c970fb589891e, 0x14a04428b4141450, 0xe15b42dfbae1e1a3,
|
||||
0x16b04e2ca6161658, 0x3acdd274f73a3ae8, 0x696fd0d2066969b9, 0x09482d1241090924,
|
||||
0x70a7ade0d77070dd, 0xb6d954716fb6b6e2, 0xd0ceb7bd1ed0d067, 0xed3b7ec7d6eded93,
|
||||
0xcc2edb85e2cccc17, 0x422a578468424215, 0x98b4c22d2c98985a, 0xa4490e55eda4a4aa,
|
||||
0x285d8850752828a0, 0x5cda31b8865c5c6d, 0xf8933fed6bf8f8c7, 0x8644a411c2868622,
|
||||
}
|
||||
|
||||
C6 := [256]u64 {
|
||||
0x6018c07830d81818, 0x8c2305af46262323, 0x3fc67ef991b8c6c6, 0x87e8136fcdfbe8e8,
|
||||
0x26874ca113cb8787, 0xdab8a9626d11b8b8, 0x0401080502090101, 0x214f426e9e0d4f4f,
|
||||
0xd836adee6c9b3636, 0xa2a6590451ffa6a6, 0x6fd2debdb90cd2d2, 0xf3f5fb06f70ef5f5,
|
||||
0xf979ef80f2967979, 0xa16f5fcede306f6f, 0x7e91fcef3f6d9191, 0x5552aa07a4f85252,
|
||||
0x9d6027fdc0476060, 0xcabc89766535bcbc, 0x569baccd2b379b9b, 0x028e048c018a8e8e,
|
||||
0xb6a371155bd2a3a3, 0x300c603c186c0c0c, 0xf17bff8af6847b7b, 0xd435b5e16a803535,
|
||||
0x741de8693af51d1d, 0xa7e05347ddb3e0e0, 0x7bd7f6acb321d7d7, 0x2fc25eed999cc2c2,
|
||||
0xb82e6d965c432e2e, 0x314b627a96294b4b, 0xdffea321e15dfefe, 0x41578216aed55757,
|
||||
0x5415a8412abd1515, 0xc1779fb6eee87777, 0xdc37a5eb6e923737, 0xb3e57b56d79ee5e5,
|
||||
0x469f8cd923139f9f, 0xe7f0d317fd23f0f0, 0x354a6a7f94204a4a, 0x4fda9e95a944dada,
|
||||
0x7d58fa25b0a25858, 0x03c906ca8fcfc9c9, 0xa429558d527c2929, 0x280a5022145a0a0a,
|
||||
0xfeb1e14f7f50b1b1, 0xbaa0691a5dc9a0a0, 0xb16b7fdad6146b6b, 0x2e855cab17d98585,
|
||||
0xcebd8173673cbdbd, 0x695dd234ba8f5d5d, 0x4010805020901010, 0xf7f4f303f507f4f4,
|
||||
0x0bcb16c08bddcbcb, 0xf83eedc67cd33e3e, 0x140528110a2d0505, 0x81671fe6ce786767,
|
||||
0xb7e47353d597e4e4, 0x9c2725bb4e022727, 0x1941325882734141, 0x168b2c9d0ba78b8b,
|
||||
0xa6a7510153f6a7a7, 0xe97dcf94fab27d7d, 0x6e95dcfb37499595, 0x47d88e9fad56d8d8,
|
||||
0xcbfb8b30eb70fbfb, 0x9fee2371c1cdeeee, 0xed7cc791f8bb7c7c, 0x856617e3cc716666,
|
||||
0x53dda68ea77bdddd, 0x5c17b84b2eaf1717, 0x014702468e454747, 0x429e84dc211a9e9e,
|
||||
0x0fca1ec589d4caca, 0xb42d75995a582d2d, 0xc6bf9179632ebfbf, 0x1c07381b0e3f0707,
|
||||
0x8ead012347acadad, 0x755aea2fb4b05a5a, 0x36836cb51bef8383, 0xcc3385ff66b63333,
|
||||
0x91633ff2c65c6363, 0x0802100a04120202, 0x92aa39384993aaaa, 0xd971afa8e2de7171,
|
||||
0x07c80ecf8dc6c8c8, 0x6419c87d32d11919, 0x39497270923b4949, 0x43d9869aaf5fd9d9,
|
||||
0xeff2c31df931f2f2, 0xabe34b48dba8e3e3, 0x715be22ab6b95b5b, 0x1a8834920dbc8888,
|
||||
0x529aa4c8293e9a9a, 0x98262dbe4c0b2626, 0xc8328dfa64bf3232, 0xfab0e94a7d59b0b0,
|
||||
0x83e91b6acff2e9e9, 0x3c0f78331e770f0f, 0x73d5e6a6b733d5d5, 0x3a8074ba1df48080,
|
||||
0xc2be997c6127bebe, 0x13cd26de87ebcdcd, 0xd034bde468893434, 0x3d487a7590324848,
|
||||
0xdbffab24e354ffff, 0xf57af78ff48d7a7a, 0x7a90f4ea3d649090, 0x615fc23ebe9d5f5f,
|
||||
0x80201da0403d2020, 0xbd6867d5d00f6868, 0x681ad07234ca1a1a, 0x82ae192c41b7aeae,
|
||||
0xeab4c95e757db4b4, 0x4d549a19a8ce5454, 0x7693ece53b7f9393, 0x88220daa442f2222,
|
||||
0x8d6407e9c8636464, 0xe3f1db12ff2af1f1, 0xd173bfa2e6cc7373, 0x4812905a24821212,
|
||||
0x1d403a5d807a4040, 0x2008402810480808, 0x2bc356e89b95c3c3, 0x97ec337bc5dfecec,
|
||||
0x4bdb9690ab4ddbdb, 0xbea1611f5fc0a1a1, 0x0e8d1c8307918d8d, 0xf43df5c97ac83d3d,
|
||||
0x6697ccf1335b9797, 0x0000000000000000, 0x1bcf36d483f9cfcf, 0xac2b4587566e2b2b,
|
||||
0xc57697b3ece17676, 0x328264b019e68282, 0x7fd6fea9b128d6d6, 0x6c1bd87736c31b1b,
|
||||
0xeeb5c15b7774b5b5, 0x86af112943beafaf, 0xb56a77dfd41d6a6a, 0x5d50ba0da0ea5050,
|
||||
0x0945124c8a574545, 0xebf3cb18fb38f3f3, 0xc0309df060ad3030, 0x9bef2b74c3c4efef,
|
||||
0xfc3fe5c37eda3f3f, 0x4955921caac75555, 0xb2a2791059dba2a2, 0x8fea0365c9e9eaea,
|
||||
0x89650fecca6a6565, 0xd2bab9686903baba, 0xbc2f65935e4a2f2f, 0x27c04ee79d8ec0c0,
|
||||
0x5fdebe81a160dede, 0x701ce06c38fc1c1c, 0xd3fdbb2ee746fdfd, 0x294d52649a1f4d4d,
|
||||
0x7292e4e039769292, 0xc9758fbceafa7575, 0x1806301e0c360606, 0x128a249809ae8a8a,
|
||||
0xf2b2f940794bb2b2, 0xbfe66359d185e6e6, 0x380e70361c7e0e0e, 0x7c1ff8633ee71f1f,
|
||||
0x956237f7c4556262, 0x77d4eea3b53ad4d4, 0x9aa829324d81a8a8, 0x6296c4f431529696,
|
||||
0xc3f99b3aef62f9f9, 0x33c566f697a3c5c5, 0x942535b14a102525, 0x7959f220b2ab5959,
|
||||
0x2a8454ae15d08484, 0xd572b7a7e4c57272, 0xe439d5dd72ec3939, 0x2d4c5a6198164c4c,
|
||||
0x655eca3bbc945e5e, 0xfd78e785f09f7878, 0xe038ddd870e53838, 0x0a8c148605988c8c,
|
||||
0x63d1c6b2bf17d1d1, 0xaea5410b57e4a5a5, 0xafe2434dd9a1e2e2, 0x99612ff8c24e6161,
|
||||
0xf6b3f1457b42b3b3, 0x842115a542342121, 0x4a9c94d625089c9c, 0x781ef0663cee1e1e,
|
||||
0x1143225286614343, 0x3bc776fc93b1c7c7, 0xd7fcb32be54ffcfc, 0x1004201408240404,
|
||||
0x5951b208a2e35151, 0x5e99bcc72f259999, 0xa96d4fc4da226d6d, 0x340d68391a650d0d,
|
||||
0xcffa8335e979fafa, 0x5bdfb684a369dfdf, 0xe57ed79bfca97e7e, 0x90243db448192424,
|
||||
0xec3bc5d776fe3b3b, 0x96ab313d4b9aabab, 0x1fce3ed181f0cece, 0x4411885522991111,
|
||||
0x068f0c8903838f8f, 0x254e4a6b9c044e4e, 0xe6b7d1517366b7b7, 0x8beb0b60cbe0ebeb,
|
||||
0xf03cfdcc78c13c3c, 0x3e817cbf1ffd8181, 0x6a94d4fe35409494, 0xfbf7eb0cf31cf7f7,
|
||||
0xdeb9a1676f18b9b9, 0x4c13985f268b1313, 0xb02c7d9c58512c2c, 0x6bd3d6b8bb05d3d3,
|
||||
0xbbe76b5cd38ce7e7, 0xa56e57cbdc396e6e, 0x37c46ef395aac4c4, 0x0c03180f061b0303,
|
||||
0x45568a13acdc5656, 0x0d441a49885e4444, 0xe17fdf9efea07f7f, 0x9ea921374f88a9a9,
|
||||
0xa82a4d8254672a2a, 0xd6bbb16d6b0abbbb, 0x23c146e29f87c1c1, 0x5153a202a6f15353,
|
||||
0x57dcae8ba572dcdc, 0x2c0b582716530b0b, 0x4e9d9cd327019d9d, 0xad6c47c1d82b6c6c,
|
||||
0xc43195f562a43131, 0xcd7487b9e8f37474, 0xfff6e309f115f6f6, 0x05460a438c4c4646,
|
||||
0x8aac092645a5acac, 0x1e893c970fb58989, 0x5014a04428b41414, 0xa3e15b42dfbae1e1,
|
||||
0x5816b04e2ca61616, 0xe83acdd274f73a3a, 0xb9696fd0d2066969, 0x2409482d12410909,
|
||||
0xdd70a7ade0d77070, 0xe2b6d954716fb6b6, 0x67d0ceb7bd1ed0d0, 0x93ed3b7ec7d6eded,
|
||||
0x17cc2edb85e2cccc, 0x15422a5784684242, 0x5a98b4c22d2c9898, 0xaaa4490e55eda4a4,
|
||||
0xa0285d8850752828, 0x6d5cda31b8865c5c, 0xc7f8933fed6bf8f8, 0x228644a411c28686,
|
||||
}
|
||||
|
||||
C7 := [256]u64 {
|
||||
0x186018c07830d818, 0x238c2305af462623, 0xc63fc67ef991b8c6, 0xe887e8136fcdfbe8,
|
||||
0x8726874ca113cb87, 0xb8dab8a9626d11b8, 0x0104010805020901, 0x4f214f426e9e0d4f,
|
||||
0x36d836adee6c9b36, 0xa6a2a6590451ffa6, 0xd26fd2debdb90cd2, 0xf5f3f5fb06f70ef5,
|
||||
0x79f979ef80f29679, 0x6fa16f5fcede306f, 0x917e91fcef3f6d91, 0x525552aa07a4f852,
|
||||
0x609d6027fdc04760, 0xbccabc89766535bc, 0x9b569baccd2b379b, 0x8e028e048c018a8e,
|
||||
0xa3b6a371155bd2a3, 0x0c300c603c186c0c, 0x7bf17bff8af6847b, 0x35d435b5e16a8035,
|
||||
0x1d741de8693af51d, 0xe0a7e05347ddb3e0, 0xd77bd7f6acb321d7, 0xc22fc25eed999cc2,
|
||||
0x2eb82e6d965c432e, 0x4b314b627a96294b, 0xfedffea321e15dfe, 0x5741578216aed557,
|
||||
0x155415a8412abd15, 0x77c1779fb6eee877, 0x37dc37a5eb6e9237, 0xe5b3e57b56d79ee5,
|
||||
0x9f469f8cd923139f, 0xf0e7f0d317fd23f0, 0x4a354a6a7f94204a, 0xda4fda9e95a944da,
|
||||
0x587d58fa25b0a258, 0xc903c906ca8fcfc9, 0x29a429558d527c29, 0x0a280a5022145a0a,
|
||||
0xb1feb1e14f7f50b1, 0xa0baa0691a5dc9a0, 0x6bb16b7fdad6146b, 0x852e855cab17d985,
|
||||
0xbdcebd8173673cbd, 0x5d695dd234ba8f5d, 0x1040108050209010, 0xf4f7f4f303f507f4,
|
||||
0xcb0bcb16c08bddcb, 0x3ef83eedc67cd33e, 0x05140528110a2d05, 0x6781671fe6ce7867,
|
||||
0xe4b7e47353d597e4, 0x279c2725bb4e0227, 0x4119413258827341, 0x8b168b2c9d0ba78b,
|
||||
0xa7a6a7510153f6a7, 0x7de97dcf94fab27d, 0x956e95dcfb374995, 0xd847d88e9fad56d8,
|
||||
0xfbcbfb8b30eb70fb, 0xee9fee2371c1cdee, 0x7ced7cc791f8bb7c, 0x66856617e3cc7166,
|
||||
0xdd53dda68ea77bdd, 0x175c17b84b2eaf17, 0x47014702468e4547, 0x9e429e84dc211a9e,
|
||||
0xca0fca1ec589d4ca, 0x2db42d75995a582d, 0xbfc6bf9179632ebf, 0x071c07381b0e3f07,
|
||||
0xad8ead012347acad, 0x5a755aea2fb4b05a, 0x8336836cb51bef83, 0x33cc3385ff66b633,
|
||||
0x6391633ff2c65c63, 0x020802100a041202, 0xaa92aa39384993aa, 0x71d971afa8e2de71,
|
||||
0xc807c80ecf8dc6c8, 0x196419c87d32d119, 0x4939497270923b49, 0xd943d9869aaf5fd9,
|
||||
0xf2eff2c31df931f2, 0xe3abe34b48dba8e3, 0x5b715be22ab6b95b, 0x881a8834920dbc88,
|
||||
0x9a529aa4c8293e9a, 0x2698262dbe4c0b26, 0x32c8328dfa64bf32, 0xb0fab0e94a7d59b0,
|
||||
0xe983e91b6acff2e9, 0x0f3c0f78331e770f, 0xd573d5e6a6b733d5, 0x803a8074ba1df480,
|
||||
0xbec2be997c6127be, 0xcd13cd26de87ebcd, 0x34d034bde4688934, 0x483d487a75903248,
|
||||
0xffdbffab24e354ff, 0x7af57af78ff48d7a, 0x907a90f4ea3d6490, 0x5f615fc23ebe9d5f,
|
||||
0x2080201da0403d20, 0x68bd6867d5d00f68, 0x1a681ad07234ca1a, 0xae82ae192c41b7ae,
|
||||
0xb4eab4c95e757db4, 0x544d549a19a8ce54, 0x937693ece53b7f93, 0x2288220daa442f22,
|
||||
0x648d6407e9c86364, 0xf1e3f1db12ff2af1, 0x73d173bfa2e6cc73, 0x124812905a248212,
|
||||
0x401d403a5d807a40, 0x0820084028104808, 0xc32bc356e89b95c3, 0xec97ec337bc5dfec,
|
||||
0xdb4bdb9690ab4ddb, 0xa1bea1611f5fc0a1, 0x8d0e8d1c8307918d, 0x3df43df5c97ac83d,
|
||||
0x976697ccf1335b97, 0x0000000000000000, 0xcf1bcf36d483f9cf, 0x2bac2b4587566e2b,
|
||||
0x76c57697b3ece176, 0x82328264b019e682, 0xd67fd6fea9b128d6, 0x1b6c1bd87736c31b,
|
||||
0xb5eeb5c15b7774b5, 0xaf86af112943beaf, 0x6ab56a77dfd41d6a, 0x505d50ba0da0ea50,
|
||||
0x450945124c8a5745, 0xf3ebf3cb18fb38f3, 0x30c0309df060ad30, 0xef9bef2b74c3c4ef,
|
||||
0x3ffc3fe5c37eda3f, 0x554955921caac755, 0xa2b2a2791059dba2, 0xea8fea0365c9e9ea,
|
||||
0x6589650fecca6a65, 0xbad2bab9686903ba, 0x2fbc2f65935e4a2f, 0xc027c04ee79d8ec0,
|
||||
0xde5fdebe81a160de, 0x1c701ce06c38fc1c, 0xfdd3fdbb2ee746fd, 0x4d294d52649a1f4d,
|
||||
0x927292e4e0397692, 0x75c9758fbceafa75, 0x061806301e0c3606, 0x8a128a249809ae8a,
|
||||
0xb2f2b2f940794bb2, 0xe6bfe66359d185e6, 0x0e380e70361c7e0e, 0x1f7c1ff8633ee71f,
|
||||
0x62956237f7c45562, 0xd477d4eea3b53ad4, 0xa89aa829324d81a8, 0x966296c4f4315296,
|
||||
0xf9c3f99b3aef62f9, 0xc533c566f697a3c5, 0x25942535b14a1025, 0x597959f220b2ab59,
|
||||
0x842a8454ae15d084, 0x72d572b7a7e4c572, 0x39e439d5dd72ec39, 0x4c2d4c5a6198164c,
|
||||
0x5e655eca3bbc945e, 0x78fd78e785f09f78, 0x38e038ddd870e538, 0x8c0a8c148605988c,
|
||||
0xd163d1c6b2bf17d1, 0xa5aea5410b57e4a5, 0xe2afe2434dd9a1e2, 0x6199612ff8c24e61,
|
||||
0xb3f6b3f1457b42b3, 0x21842115a5423421, 0x9c4a9c94d625089c, 0x1e781ef0663cee1e,
|
||||
0x4311432252866143, 0xc73bc776fc93b1c7, 0xfcd7fcb32be54ffc, 0x0410042014082404,
|
||||
0x515951b208a2e351, 0x995e99bcc72f2599, 0x6da96d4fc4da226d, 0x0d340d68391a650d,
|
||||
0xfacffa8335e979fa, 0xdf5bdfb684a369df, 0x7ee57ed79bfca97e, 0x2490243db4481924,
|
||||
0x3bec3bc5d776fe3b, 0xab96ab313d4b9aab, 0xce1fce3ed181f0ce, 0x1144118855229911,
|
||||
0x8f068f0c8903838f, 0x4e254e4a6b9c044e, 0xb7e6b7d1517366b7, 0xeb8beb0b60cbe0eb,
|
||||
0x3cf03cfdcc78c13c, 0x813e817cbf1ffd81, 0x946a94d4fe354094, 0xf7fbf7eb0cf31cf7,
|
||||
0xb9deb9a1676f18b9, 0x134c13985f268b13, 0x2cb02c7d9c58512c, 0xd36bd3d6b8bb05d3,
|
||||
0xe7bbe76b5cd38ce7, 0x6ea56e57cbdc396e, 0xc437c46ef395aac4, 0x030c03180f061b03,
|
||||
0x5645568a13acdc56, 0x440d441a49885e44, 0x7fe17fdf9efea07f, 0xa99ea921374f88a9,
|
||||
0x2aa82a4d8254672a, 0xbbd6bbb16d6b0abb, 0xc123c146e29f87c1, 0x535153a202a6f153,
|
||||
0xdc57dcae8ba572dc, 0x0b2c0b582716530b, 0x9d4e9d9cd327019d, 0x6cad6c47c1d82b6c,
|
||||
0x31c43195f562a431, 0x74cd7487b9e8f374, 0xf6fff6e309f115f6, 0x4605460a438c4c46,
|
||||
0xac8aac092645a5ac, 0x891e893c970fb589, 0x145014a04428b414, 0xe1a3e15b42dfbae1,
|
||||
0x165816b04e2ca616, 0x3ae83acdd274f73a, 0x69b9696fd0d20669, 0x092409482d124109,
|
||||
0x70dd70a7ade0d770, 0xb6e2b6d954716fb6, 0xd067d0ceb7bd1ed0, 0xed93ed3b7ec7d6ed,
|
||||
0xcc17cc2edb85e2cc, 0x4215422a57846842, 0x985a98b4c22d2c98, 0xa4aaa4490e55eda4,
|
||||
0x28a0285d88507528, 0x5c6d5cda31b8865c, 0xf8c7f8933fed6bf8, 0x86228644a411c286,
|
||||
}
|
||||
|
||||
RC := [ROUNDS + 1]u64 {
|
||||
0x0000000000000000,
|
||||
0x1823c6e887b8014f,
|
||||
0x36a6d2f5796f9152,
|
||||
0x60bc9b8ea30c7b35,
|
||||
0x1de0d7c22e4bfe57,
|
||||
0x157737e59ff04ada,
|
||||
0x58c9290ab1a06b85,
|
||||
0xbd5d10f4cb3e0567,
|
||||
0xe427418ba77d95d8,
|
||||
0xfbee7c66dd17479e,
|
||||
0xca2dbf07ad5a8333,
|
||||
}
|
||||
|
||||
transform :: proc (ctx: ^Whirlpool_Context) {
|
||||
K, block, state, L: [8]u64
|
||||
|
||||
for i := 0; i < 8; i += 1 {block[i] = util.U64_BE(ctx.buffer[8 * i:])}
|
||||
|
||||
for i := 0; i < 8; i += 1 {
|
||||
K[i] = ctx.hash[i]
|
||||
state[i] = block[i] ~ K[i]
|
||||
}
|
||||
|
||||
for r := 1; r <= ROUNDS; r += 1 {
|
||||
for i := 0; i < 8; i += 1 {
|
||||
L[i] = C0[byte(K[i % 8] >> 56)] ~
|
||||
C1[byte(K[(i + 7) % 8] >> 48)] ~
|
||||
C2[byte(K[(i + 6) % 8] >> 40)] ~
|
||||
C3[byte(K[(i + 5) % 8] >> 32)] ~
|
||||
C4[byte(K[(i + 4) % 8] >> 24)] ~
|
||||
C5[byte(K[(i + 3) % 8] >> 16)] ~
|
||||
C6[byte(K[(i + 2) % 8] >> 8)] ~
|
||||
C7[byte(K[(i + 1) % 8])]
|
||||
}
|
||||
L[0] ~= RC[r]
|
||||
|
||||
for i := 0; i < 8; i += 1 {K[i] = L[i]}
|
||||
|
||||
for i := 0; i < 8; i += 1 {
|
||||
L[i] = C0[byte(state[i % 8] >> 56)] ~
|
||||
C1[byte(state[(i + 7) % 8] >> 48)] ~
|
||||
C2[byte(state[(i + 6) % 8] >> 40)] ~
|
||||
C3[byte(state[(i + 5) % 8] >> 32)] ~
|
||||
C4[byte(state[(i + 4) % 8] >> 24)] ~
|
||||
C5[byte(state[(i + 3) % 8] >> 16)] ~
|
||||
C6[byte(state[(i + 2) % 8] >> 8)] ~
|
||||
C7[byte(state[(i + 1) % 8])] ~
|
||||
K[i % 8]
|
||||
}
|
||||
for i := 0; i < 8; i += 1 {state[i] = L[i]}
|
||||
}
|
||||
for i := 0; i < 8; i += 1 {ctx.hash[i] ~= state[i] ~ block[i]}
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^Whirlpool_Context, source: []byte) {
|
||||
source_pos: int
|
||||
nn := len(source)
|
||||
source_bits := u64(nn * 8)
|
||||
source_gap := u32((8 - (int(source_bits & 7))) & 7)
|
||||
buffer_rem := uint(ctx.buffer_bits & 7)
|
||||
b: u32
|
||||
|
||||
for i, carry, value := 31, u32(0), u32(source_bits); i >= 0 && (carry != 0 || value != 0); i -= 1 {
|
||||
carry += u32(ctx.bitlength[i]) + (u32(value & 0xff))
|
||||
ctx.bitlength[i] = byte(carry)
|
||||
carry >>= 8
|
||||
value >>= 8
|
||||
}
|
||||
|
||||
for source_bits > 8 {
|
||||
b = u32(u32((source[source_pos] << source_gap) & 0xff) | u32((source[source_pos+1] & 0xff) >> (8 - source_gap)))
|
||||
|
||||
ctx.buffer[ctx.buffer_pos] |= u8(b >> buffer_rem)
|
||||
ctx.buffer_pos += 1
|
||||
ctx.buffer_bits += int(8 - buffer_rem)
|
||||
|
||||
if ctx.buffer_bits == 512 {
|
||||
transform(ctx)
|
||||
ctx.buffer_bits = 0
|
||||
ctx.buffer_pos = 0
|
||||
}
|
||||
ctx.buffer[ctx.buffer_pos] = byte(b << (8 - buffer_rem))
|
||||
ctx.buffer_bits += int(buffer_rem)
|
||||
source_bits -= 8
|
||||
source_pos += 1
|
||||
}
|
||||
|
||||
if source_bits > 0 {
|
||||
b = u32((source[source_pos] << source_gap) & 0xff)
|
||||
ctx.buffer[ctx.buffer_pos] |= byte(b) >> buffer_rem
|
||||
} else {b = 0}
|
||||
|
||||
if u64(buffer_rem) + source_bits < 8 {
|
||||
ctx.buffer_bits += int(source_bits)
|
||||
} else {
|
||||
ctx.buffer_pos += 1
|
||||
ctx.buffer_bits += 8 - int(buffer_rem)
|
||||
source_bits -= u64(8 - buffer_rem)
|
||||
|
||||
if ctx.buffer_bits == 512 {
|
||||
transform(ctx)
|
||||
ctx.buffer_bits = 0
|
||||
ctx.buffer_pos = 0
|
||||
}
|
||||
ctx.buffer[ctx.buffer_pos] = byte(b << (8 - buffer_rem))
|
||||
ctx.buffer_bits += int(source_bits)
|
||||
}
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^Whirlpool_Context, hash: []byte) {
|
||||
n := ctx
|
||||
n.buffer[n.buffer_pos] |= 0x80 >> (uint(n.buffer_bits) & 7)
|
||||
n.buffer_pos += 1
|
||||
|
||||
if n.buffer_pos > 64 - 32 {
|
||||
if n.buffer_pos < 64 {
|
||||
for i := 0; i < 64 - n.buffer_pos; i += 1 {
|
||||
n.buffer[n.buffer_pos + i] = 0
|
||||
}
|
||||
}
|
||||
transform(ctx)
|
||||
n.buffer_pos = 0
|
||||
}
|
||||
|
||||
if n.buffer_pos < 64 - 32 {
|
||||
for i := 0; i < (64 - 32) - n.buffer_pos; i += 1 {
|
||||
n.buffer[n.buffer_pos + i] = 0
|
||||
}
|
||||
}
|
||||
n.buffer_pos = 64 - 32
|
||||
|
||||
for i := 0; i < 32; i += 1 {
|
||||
n.buffer[n.buffer_pos + i] = n.bitlength[i]
|
||||
}
|
||||
transform(ctx)
|
||||
|
||||
for i := 0; i < 8; i += 1 {
|
||||
hash[i * 8] = byte(n.hash[i] >> 56)
|
||||
hash[i * 8 + 1] = byte(n.hash[i] >> 48)
|
||||
hash[i * 8 + 2] = byte(n.hash[i] >> 40)
|
||||
hash[i * 8 + 3] = byte(n.hash[i] >> 32)
|
||||
hash[i * 8 + 4] = byte(n.hash[i] >> 24)
|
||||
hash[i * 8 + 5] = byte(n.hash[i] >> 16)
|
||||
hash[i * 8 + 6] = byte(n.hash[i] >> 8)
|
||||
hash[i * 8 + 7] = byte(n.hash[i])
|
||||
}
|
||||
}
|
||||
@@ -35,23 +35,6 @@ marshal_to_builder :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
}
|
||||
|
||||
marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
write_f64 :: proc(w: io.Writer, val: f64, size: int) -> io.Error {
|
||||
buf: [386]byte
|
||||
|
||||
str := strconv.append_float(buf[1:], val, 'f', 2*size, 8*size)
|
||||
s := buf[:len(str)+1]
|
||||
if s[1] == '+' || s[1] == '-' {
|
||||
s = s[1:]
|
||||
} else {
|
||||
s[0] = '+'
|
||||
}
|
||||
if s[0] == '+' {
|
||||
s = s[1:]
|
||||
}
|
||||
|
||||
_ = io.write_string(w, string(s)) or_return
|
||||
return nil
|
||||
}
|
||||
if v == nil {
|
||||
io.write_string(w, "null") or_return
|
||||
return
|
||||
@@ -177,6 +160,9 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
|
||||
case runtime.Type_Info_Relative_Slice:
|
||||
return .Unsupported_Type
|
||||
|
||||
case runtime.Type_Info_Matrix:
|
||||
return .Unsupported_Type
|
||||
|
||||
case runtime.Type_Info_Array:
|
||||
io.write_byte(w, '[') or_return
|
||||
|
||||
@@ -34,6 +34,7 @@ Specification :: enum {
|
||||
JSON,
|
||||
JSON5, // https://json5.org/
|
||||
MJSON, // https://bitsquid.blogspot.com/2009/10/simplified-json-notation.html
|
||||
Bitsquid = MJSON,
|
||||
}
|
||||
|
||||
|
||||
|
||||
+40
-33
@@ -2,7 +2,6 @@ package fmt
|
||||
|
||||
import "core:math/bits"
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
import "core:reflect"
|
||||
import "core:runtime"
|
||||
@@ -62,38 +61,6 @@ register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Regist
|
||||
}
|
||||
|
||||
|
||||
fprint :: proc(fd: os.Handle, args: ..any, sep := " ") -> int {
|
||||
w := io.to_writer(os.stream_from_handle(fd))
|
||||
return wprint(w=w, args=args, sep=sep)
|
||||
}
|
||||
|
||||
fprintln :: proc(fd: os.Handle, args: ..any, sep := " ") -> int {
|
||||
w := io.to_writer(os.stream_from_handle(fd))
|
||||
return wprintln(w=w, args=args, sep=sep)
|
||||
}
|
||||
fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int {
|
||||
w := io.to_writer(os.stream_from_handle(fd))
|
||||
return wprintf(w, fmt, ..args)
|
||||
}
|
||||
fprint_type :: proc(fd: os.Handle, info: ^runtime.Type_Info) -> (n: int, err: io.Error) {
|
||||
w := io.to_writer(os.stream_from_handle(fd))
|
||||
return wprint_type(w, info)
|
||||
}
|
||||
fprint_typeid :: proc(fd: os.Handle, id: typeid) -> (n: int, err: io.Error) {
|
||||
w := io.to_writer(os.stream_from_handle(fd))
|
||||
return wprint_typeid(w, id)
|
||||
}
|
||||
|
||||
// print* procedures return the number of bytes written
|
||||
print :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stdout, args=args, sep=sep) }
|
||||
println :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stdout, args=args, sep=sep) }
|
||||
printf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stdout, fmt, ..args) }
|
||||
|
||||
eprint :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stderr, args=args, sep=sep) }
|
||||
eprintln :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stderr, args=args, sep=sep) }
|
||||
eprintf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stderr, fmt, ..args) }
|
||||
|
||||
|
||||
// aprint* procedures return a string that was allocated with the current context
|
||||
// They must be freed accordingly
|
||||
aprint :: proc(args: ..any, sep := " ") -> string {
|
||||
@@ -1953,6 +1920,46 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
}
|
||||
}
|
||||
|
||||
case runtime.Type_Info_Matrix:
|
||||
io.write_string(fi.writer, "matrix[")
|
||||
defer io.write_byte(fi.writer, ']')
|
||||
|
||||
fi.indent += 1
|
||||
|
||||
if fi.hash {
|
||||
io.write_byte(fi.writer, '\n')
|
||||
// TODO(bill): Should this render it like in written form? e.g. tranposed
|
||||
for row in 0..<info.row_count {
|
||||
fmt_write_indent(fi)
|
||||
for col in 0..<info.column_count {
|
||||
if col > 0 { io.write_string(fi.writer, ", ") }
|
||||
|
||||
offset := (row + col*info.elem_stride)*info.elem_size
|
||||
|
||||
data := uintptr(v.data) + uintptr(offset)
|
||||
fmt_arg(fi, any{rawptr(data), info.elem.id}, verb)
|
||||
}
|
||||
io.write_string(fi.writer, ";\n")
|
||||
}
|
||||
} else {
|
||||
for row in 0..<info.row_count {
|
||||
if row > 0 { io.write_string(fi.writer, ", ") }
|
||||
for col in 0..<info.column_count {
|
||||
if col > 0 { io.write_string(fi.writer, "; ") }
|
||||
|
||||
offset := (row + col*info.elem_stride)*info.elem_size
|
||||
|
||||
data := uintptr(v.data) + uintptr(offset)
|
||||
fmt_arg(fi, any{rawptr(data), info.elem.id}, verb)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fi.indent -= 1
|
||||
|
||||
if fi.hash {
|
||||
fmt_write_indent(fi)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
//+build !freestanding
|
||||
package fmt
|
||||
|
||||
import "core:runtime"
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
fprint :: proc(fd: os.Handle, args: ..any, sep := " ") -> int {
|
||||
w := io.to_writer(os.stream_from_handle(fd))
|
||||
return wprint(w=w, args=args, sep=sep)
|
||||
}
|
||||
|
||||
fprintln :: proc(fd: os.Handle, args: ..any, sep := " ") -> int {
|
||||
w := io.to_writer(os.stream_from_handle(fd))
|
||||
return wprintln(w=w, args=args, sep=sep)
|
||||
}
|
||||
fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int {
|
||||
w := io.to_writer(os.stream_from_handle(fd))
|
||||
return wprintf(w, fmt, ..args)
|
||||
}
|
||||
fprint_type :: proc(fd: os.Handle, info: ^runtime.Type_Info) -> (n: int, err: io.Error) {
|
||||
w := io.to_writer(os.stream_from_handle(fd))
|
||||
return wprint_type(w, info)
|
||||
}
|
||||
fprint_typeid :: proc(fd: os.Handle, id: typeid) -> (n: int, err: io.Error) {
|
||||
w := io.to_writer(os.stream_from_handle(fd))
|
||||
return wprint_typeid(w, id)
|
||||
}
|
||||
|
||||
// print* procedures return the number of bytes written
|
||||
print :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stdout, args=args, sep=sep) }
|
||||
println :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stdout, args=args, sep=sep) }
|
||||
printf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stdout, fmt, ..args) }
|
||||
|
||||
eprint :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stderr, args=args, sep=sep) }
|
||||
eprintln :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stderr, args=args, sep=sep) }
|
||||
eprintf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stderr, fmt, ..args) }
|
||||
+123
-12
@@ -1,16 +1,17 @@
|
||||
package image
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-2 license.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation, optimization.
|
||||
Ginger Bill: Cosmetic changes.
|
||||
*/
|
||||
package image
|
||||
|
||||
import "core:bytes"
|
||||
import "core:mem"
|
||||
import "core:compress"
|
||||
import "core:runtime"
|
||||
|
||||
Image :: struct {
|
||||
width: int,
|
||||
@@ -25,8 +26,11 @@ Image :: struct {
|
||||
*/
|
||||
background: Maybe([3]u16),
|
||||
|
||||
metadata_ptr: rawptr,
|
||||
metadata_type: typeid,
|
||||
metadata: Image_Metadata,
|
||||
}
|
||||
|
||||
Image_Metadata :: union {
|
||||
^PNG_Info,
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -112,31 +116,140 @@ Option :: enum {
|
||||
}
|
||||
Options :: distinct bit_set[Option]
|
||||
|
||||
Error :: enum {
|
||||
Error :: union {
|
||||
General_Image_Error,
|
||||
PNG_Error,
|
||||
|
||||
compress.Error,
|
||||
compress.General_Error,
|
||||
compress.Deflate_Error,
|
||||
compress.ZLIB_Error,
|
||||
runtime.Allocator_Error,
|
||||
}
|
||||
|
||||
General_Image_Error :: enum {
|
||||
None = 0,
|
||||
Invalid_Image_Dimensions,
|
||||
Image_Dimensions_Too_Large,
|
||||
Image_Does_Not_Adhere_to_Spec,
|
||||
}
|
||||
|
||||
PNG_Error :: enum {
|
||||
Invalid_PNG_Signature,
|
||||
IHDR_Not_First_Chunk,
|
||||
IHDR_Corrupt,
|
||||
IDAT_Missing,
|
||||
IDAT_Must_Be_Contiguous,
|
||||
IDAT_Corrupt,
|
||||
PNG_Does_Not_Adhere_to_Spec,
|
||||
IDAT_Size_Too_Large,
|
||||
PLTE_Encountered_Unexpectedly,
|
||||
PLTE_Invalid_Length,
|
||||
TRNS_Encountered_Unexpectedly,
|
||||
BKGD_Invalid_Length,
|
||||
Invalid_Image_Dimensions,
|
||||
Unknown_Color_Type,
|
||||
Invalid_Color_Bit_Depth_Combo,
|
||||
Unknown_Filter_Method,
|
||||
Unknown_Interlace_Method,
|
||||
Requested_Channel_Not_Present,
|
||||
Post_Processing_Error,
|
||||
Invalid_Chunk_Length,
|
||||
}
|
||||
|
||||
/*
|
||||
PNG-specific structs
|
||||
*/
|
||||
PNG_Info :: struct {
|
||||
header: PNG_IHDR,
|
||||
chunks: [dynamic]PNG_Chunk,
|
||||
}
|
||||
|
||||
PNG_Chunk_Header :: struct #packed {
|
||||
length: u32be,
|
||||
type: PNG_Chunk_Type,
|
||||
}
|
||||
|
||||
PNG_Chunk :: struct #packed {
|
||||
header: PNG_Chunk_Header,
|
||||
data: []byte,
|
||||
crc: u32be,
|
||||
}
|
||||
|
||||
PNG_Chunk_Type :: enum u32be {
|
||||
// IHDR must come first in a file
|
||||
IHDR = 'I' << 24 | 'H' << 16 | 'D' << 8 | 'R',
|
||||
// PLTE must precede the first IDAT chunk
|
||||
PLTE = 'P' << 24 | 'L' << 16 | 'T' << 8 | 'E',
|
||||
bKGD = 'b' << 24 | 'K' << 16 | 'G' << 8 | 'D',
|
||||
tRNS = 't' << 24 | 'R' << 16 | 'N' << 8 | 'S',
|
||||
IDAT = 'I' << 24 | 'D' << 16 | 'A' << 8 | 'T',
|
||||
|
||||
iTXt = 'i' << 24 | 'T' << 16 | 'X' << 8 | 't',
|
||||
tEXt = 't' << 24 | 'E' << 16 | 'X' << 8 | 't',
|
||||
zTXt = 'z' << 24 | 'T' << 16 | 'X' << 8 | 't',
|
||||
|
||||
iCCP = 'i' << 24 | 'C' << 16 | 'C' << 8 | 'P',
|
||||
pHYs = 'p' << 24 | 'H' << 16 | 'Y' << 8 | 's',
|
||||
gAMA = 'g' << 24 | 'A' << 16 | 'M' << 8 | 'A',
|
||||
tIME = 't' << 24 | 'I' << 16 | 'M' << 8 | 'E',
|
||||
|
||||
sPLT = 's' << 24 | 'P' << 16 | 'L' << 8 | 'T',
|
||||
sRGB = 's' << 24 | 'R' << 16 | 'G' << 8 | 'B',
|
||||
hIST = 'h' << 24 | 'I' << 16 | 'S' << 8 | 'T',
|
||||
cHRM = 'c' << 24 | 'H' << 16 | 'R' << 8 | 'M',
|
||||
sBIT = 's' << 24 | 'B' << 16 | 'I' << 8 | 'T',
|
||||
|
||||
/*
|
||||
eXIf tags are not part of the core spec, but have been ratified
|
||||
in v1.5.0 of the PNG Ext register.
|
||||
|
||||
We will provide unprocessed chunks to the caller if `.return_metadata` is set.
|
||||
Applications are free to implement an Exif decoder.
|
||||
*/
|
||||
eXIf = 'e' << 24 | 'X' << 16 | 'I' << 8 | 'f',
|
||||
|
||||
// PNG files must end with IEND
|
||||
IEND = 'I' << 24 | 'E' << 16 | 'N' << 8 | 'D',
|
||||
|
||||
/*
|
||||
XCode sometimes produces "PNG" files that don't adhere to the PNG spec.
|
||||
We recognize them only in order to avoid doing further work on them.
|
||||
|
||||
Some tools like PNG Defry may be able to repair them, but we're not
|
||||
going to reward Apple for producing proprietary broken files purporting
|
||||
to be PNGs by supporting them.
|
||||
|
||||
*/
|
||||
iDOT = 'i' << 24 | 'D' << 16 | 'O' << 8 | 'T',
|
||||
CbGI = 'C' << 24 | 'b' << 16 | 'H' << 8 | 'I',
|
||||
}
|
||||
|
||||
PNG_IHDR :: struct #packed {
|
||||
width: u32be,
|
||||
height: u32be,
|
||||
bit_depth: u8,
|
||||
color_type: PNG_Color_Type,
|
||||
compression_method: u8,
|
||||
filter_method: u8,
|
||||
interlace_method: PNG_Interlace_Method,
|
||||
}
|
||||
PNG_IHDR_SIZE :: size_of(PNG_IHDR)
|
||||
#assert (PNG_IHDR_SIZE == 13)
|
||||
|
||||
PNG_Color_Value :: enum u8 {
|
||||
Paletted = 0, // 1 << 0 = 1
|
||||
Color = 1, // 1 << 1 = 2
|
||||
Alpha = 2, // 1 << 2 = 4
|
||||
}
|
||||
PNG_Color_Type :: distinct bit_set[PNG_Color_Value; u8]
|
||||
|
||||
PNG_Interlace_Method :: enum u8 {
|
||||
None = 0,
|
||||
Adam7 = 1,
|
||||
}
|
||||
|
||||
/*
|
||||
Functions to help with image buffer calculations
|
||||
*/
|
||||
|
||||
compute_buffer_size :: proc(width, height, channels, depth: int, extra_row_bytes := int(0)) -> (size: int) {
|
||||
size = ((((channels * width * depth) + 7) >> 3) + extra_row_bytes) * height
|
||||
return
|
||||
@@ -145,7 +258,6 @@ compute_buffer_size :: proc(width, height, channels, depth: int, extra_row_bytes
|
||||
/*
|
||||
For when you have an RGB(A) image, but want a particular channel.
|
||||
*/
|
||||
|
||||
Channel :: enum u8 {
|
||||
R = 1,
|
||||
G = 2,
|
||||
@@ -207,8 +319,7 @@ return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok
|
||||
res.depth = img.depth
|
||||
res.pixels = t
|
||||
res.background = img.background
|
||||
res.metadata_ptr = img.metadata_ptr
|
||||
res.metadata_type = img.metadata_type
|
||||
res.metadata = img.metadata
|
||||
|
||||
return res, true
|
||||
}
|
||||
|
||||
+29
-32
@@ -1,9 +1,6 @@
|
||||
//+ignore
|
||||
package png
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-2 license.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
@@ -11,8 +8,9 @@ package png
|
||||
|
||||
An example of how to use `load`.
|
||||
*/
|
||||
//+ignore
|
||||
package png
|
||||
|
||||
import "core:compress"
|
||||
import "core:image"
|
||||
// import "core:image/png"
|
||||
import "core:bytes"
|
||||
@@ -41,8 +39,8 @@ main :: proc() {
|
||||
demo :: proc() {
|
||||
file: string
|
||||
|
||||
options := image.Options{} // {.return_metadata};
|
||||
err: compress.Error
|
||||
options := image.Options{.return_metadata}
|
||||
err: image.Error
|
||||
img: ^image.Image
|
||||
|
||||
file = "../../../misc/logo-slim.png"
|
||||
@@ -53,32 +51,33 @@ demo :: proc() {
|
||||
if err != nil {
|
||||
fmt.printf("Trying to read PNG file %v returned %v\n", file, err)
|
||||
} else {
|
||||
v: ^Info
|
||||
|
||||
fmt.printf("Image: %vx%vx%v, %v-bit.\n", img.width, img.height, img.channels, img.depth)
|
||||
if img.metadata_ptr != nil && img.metadata_type == Info {
|
||||
v = (^Info)(img.metadata_ptr)
|
||||
|
||||
if v, ok := img.metadata.(^image.PNG_Info); ok {
|
||||
// Handle ancillary chunks as you wish.
|
||||
// We provide helper functions for a few types.
|
||||
for c in v.chunks {
|
||||
#partial switch c.header.type {
|
||||
case .tIME:
|
||||
t, _ := core_time(c)
|
||||
fmt.printf("[tIME]: %v\n", t)
|
||||
if t, t_ok := core_time(c); t_ok {
|
||||
fmt.printf("[tIME]: %v\n", t)
|
||||
}
|
||||
case .gAMA:
|
||||
fmt.printf("[gAMA]: %v\n", gamma(c))
|
||||
if gama, gama_ok := gamma(c); gama_ok {
|
||||
fmt.printf("[gAMA]: %v\n", gama)
|
||||
}
|
||||
case .pHYs:
|
||||
phys := phys(c)
|
||||
if phys.unit == .Meter {
|
||||
xm := f32(img.width) / f32(phys.ppu_x)
|
||||
ym := f32(img.height) / f32(phys.ppu_y)
|
||||
dpi_x, dpi_y := phys_to_dpi(phys)
|
||||
fmt.printf("[pHYs] Image resolution is %v x %v pixels per meter.\n", phys.ppu_x, phys.ppu_y)
|
||||
fmt.printf("[pHYs] Image resolution is %v x %v DPI.\n", dpi_x, dpi_y)
|
||||
fmt.printf("[pHYs] Image dimensions are %v x %v meters.\n", xm, ym)
|
||||
} else {
|
||||
fmt.printf("[pHYs] x: %v, y: %v pixels per unknown unit.\n", phys.ppu_x, phys.ppu_y)
|
||||
if phys, phys_ok := phys(c); phys_ok {
|
||||
if phys.unit == .Meter {
|
||||
xm := f32(img.width) / f32(phys.ppu_x)
|
||||
ym := f32(img.height) / f32(phys.ppu_y)
|
||||
dpi_x, dpi_y := phys_to_dpi(phys)
|
||||
fmt.printf("[pHYs] Image resolution is %v x %v pixels per meter.\n", phys.ppu_x, phys.ppu_y)
|
||||
fmt.printf("[pHYs] Image resolution is %v x %v DPI.\n", dpi_x, dpi_y)
|
||||
fmt.printf("[pHYs] Image dimensions are %v x %v meters.\n", xm, ym)
|
||||
} else {
|
||||
fmt.printf("[pHYs] x: %v, y: %v pixels per unknown unit.\n", phys.ppu_x, phys.ppu_y)
|
||||
}
|
||||
}
|
||||
case .iTXt, .zTXt, .tEXt:
|
||||
res, ok_text := text(c)
|
||||
@@ -93,8 +92,7 @@ demo :: proc() {
|
||||
case .bKGD:
|
||||
fmt.printf("[bKGD] %v\n", img.background)
|
||||
case .eXIf:
|
||||
res, ok_exif := exif(c)
|
||||
if ok_exif {
|
||||
if res, ok_exif := exif(c); ok_exif {
|
||||
/*
|
||||
Other than checking the signature and byte order, we don't handle Exif data.
|
||||
If you wish to interpret it, pass it to an Exif parser.
|
||||
@@ -102,20 +100,17 @@ demo :: proc() {
|
||||
fmt.printf("[eXIf] %v\n", res)
|
||||
}
|
||||
case .PLTE:
|
||||
plte, plte_ok := plte(c)
|
||||
if plte_ok {
|
||||
if plte, plte_ok := plte(c); plte_ok {
|
||||
fmt.printf("[PLTE] %v\n", plte)
|
||||
} else {
|
||||
fmt.printf("[PLTE] Error\n")
|
||||
}
|
||||
case .hIST:
|
||||
res, ok_hist := hist(c)
|
||||
if ok_hist {
|
||||
if res, ok_hist := hist(c); ok_hist {
|
||||
fmt.printf("[hIST] %v\n", res)
|
||||
}
|
||||
case .cHRM:
|
||||
res, ok_chrm := chrm(c)
|
||||
if ok_chrm {
|
||||
if res, ok_chrm := chrm(c); ok_chrm {
|
||||
fmt.printf("[cHRM] %v\n", res)
|
||||
}
|
||||
case .sPLT:
|
||||
@@ -147,6 +142,8 @@ demo :: proc() {
|
||||
}
|
||||
}
|
||||
|
||||
fmt.printf("Done parsing metadata.\n")
|
||||
|
||||
if err == nil && .do_not_decompress_image not_in options && .info not_in options {
|
||||
if ok := write_image_as_ppm("out.ppm", img); ok {
|
||||
fmt.println("Saved decoded image.")
|
||||
|
||||
+47
-47
@@ -1,5 +1,3 @@
|
||||
package png
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-2 license.
|
||||
@@ -10,6 +8,7 @@ package png
|
||||
|
||||
These are a few useful utility functions to work with PNG images.
|
||||
*/
|
||||
package png
|
||||
|
||||
import "core:image"
|
||||
import "core:compress/zlib"
|
||||
@@ -34,15 +33,14 @@ destroy :: proc(img: ^Image) {
|
||||
}
|
||||
|
||||
bytes.buffer_destroy(&img.pixels)
|
||||
// Clean up Info.
|
||||
free(img.metadata_ptr)
|
||||
|
||||
/*
|
||||
We don't need to do anything for the individual chunks.
|
||||
They're allocated on the temp allocator, as is info.chunks
|
||||
|
||||
See read_chunk.
|
||||
*/
|
||||
if v, ok := img.metadata.(^image.PNG_Info); ok {
|
||||
for chunk in &v.chunks {
|
||||
delete(chunk.data)
|
||||
}
|
||||
delete(v.chunks)
|
||||
free(v)
|
||||
}
|
||||
free(img)
|
||||
}
|
||||
|
||||
@@ -50,46 +48,50 @@ destroy :: proc(img: ^Image) {
|
||||
Chunk helpers
|
||||
*/
|
||||
|
||||
gamma :: proc(c: Chunk) -> f32 {
|
||||
assert(c.header.type == .gAMA)
|
||||
res := (^gAMA)(raw_data(c.data))^
|
||||
when true {
|
||||
// Returns the wrong result on old backend
|
||||
// Fixed for -llvm-api
|
||||
return f32(res.gamma_100k) / 100_000.0
|
||||
} else {
|
||||
return f32(u32(res.gamma_100k)) / 100_000.0
|
||||
gamma :: proc(c: image.PNG_Chunk) -> (res: f32, ok: bool) {
|
||||
if c.header.type != .gAMA || len(c.data) != size_of(gAMA) {
|
||||
return {}, false
|
||||
}
|
||||
gama := (^gAMA)(raw_data(c.data))^
|
||||
return f32(gama.gamma_100k) / 100_000.0, true
|
||||
}
|
||||
|
||||
INCHES_PER_METER :: 1000.0 / 25.4
|
||||
|
||||
phys :: proc(c: Chunk) -> pHYs {
|
||||
assert(c.header.type == .pHYs)
|
||||
res := (^pHYs)(raw_data(c.data))^
|
||||
return res
|
||||
phys :: proc(c: image.PNG_Chunk) -> (res: pHYs, ok: bool) {
|
||||
if c.header.type != .pHYs || len(c.data) != size_of(pHYs) {
|
||||
return {}, false
|
||||
}
|
||||
|
||||
return (^pHYs)(raw_data(c.data))^, true
|
||||
}
|
||||
|
||||
phys_to_dpi :: proc(p: pHYs) -> (x_dpi, y_dpi: f32) {
|
||||
return f32(p.ppu_x) / INCHES_PER_METER, f32(p.ppu_y) / INCHES_PER_METER
|
||||
}
|
||||
|
||||
time :: proc(c: Chunk) -> tIME {
|
||||
assert(c.header.type == .tIME)
|
||||
res := (^tIME)(raw_data(c.data))^
|
||||
return res
|
||||
time :: proc(c: image.PNG_Chunk) -> (res: tIME, ok: bool) {
|
||||
if c.header.type != .tIME || len(c.data) != size_of(tIME) {
|
||||
return {}, false
|
||||
}
|
||||
|
||||
return (^tIME)(raw_data(c.data))^, true
|
||||
}
|
||||
|
||||
core_time :: proc(c: Chunk) -> (t: coretime.Time, ok: bool) {
|
||||
png_time := time(c)
|
||||
using png_time
|
||||
return coretime.datetime_to_time(
|
||||
int(year), int(month), int(day),
|
||||
int(hour), int(minute), int(second),
|
||||
)
|
||||
core_time :: proc(c: image.PNG_Chunk) -> (t: coretime.Time, ok: bool) {
|
||||
if png_time, png_ok := time(c); png_ok {
|
||||
using png_time
|
||||
return coretime.datetime_to_time(
|
||||
int(year), int(month), int(day),
|
||||
int(hour), int(minute), int(second),
|
||||
)
|
||||
} else {
|
||||
return {}, false
|
||||
}
|
||||
}
|
||||
|
||||
text :: proc(c: Chunk) -> (res: Text, ok: bool) {
|
||||
text :: proc(c: image.PNG_Chunk) -> (res: Text, ok: bool) {
|
||||
assert(len(c.data) == int(c.header.length))
|
||||
#partial switch c.header.type {
|
||||
case .tEXt:
|
||||
ok = true
|
||||
@@ -191,7 +193,7 @@ text_destroy :: proc(text: Text) {
|
||||
delete(text.text)
|
||||
}
|
||||
|
||||
iccp :: proc(c: Chunk) -> (res: iCCP, ok: bool) {
|
||||
iccp :: proc(c: image.PNG_Chunk) -> (res: iCCP, ok: bool) {
|
||||
ok = true
|
||||
|
||||
fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator)
|
||||
@@ -227,10 +229,8 @@ iccp_destroy :: proc(i: iCCP) {
|
||||
|
||||
}
|
||||
|
||||
srgb :: proc(c: Chunk) -> (res: sRGB, ok: bool) {
|
||||
ok = true
|
||||
|
||||
if c.header.type != .sRGB || len(c.data) != 1 {
|
||||
srgb :: proc(c: image.PNG_Chunk) -> (res: sRGB, ok: bool) {
|
||||
if c.header.type != .sRGB || len(c.data) != size_of(sRGB_Rendering_Intent) {
|
||||
return {}, false
|
||||
}
|
||||
|
||||
@@ -238,10 +238,10 @@ srgb :: proc(c: Chunk) -> (res: sRGB, ok: bool) {
|
||||
if res.intent > max(sRGB_Rendering_Intent) {
|
||||
ok = false; return
|
||||
}
|
||||
return
|
||||
return res, true
|
||||
}
|
||||
|
||||
plte :: proc(c: Chunk) -> (res: PLTE, ok: bool) {
|
||||
plte :: proc(c: image.PNG_Chunk) -> (res: PLTE, ok: bool) {
|
||||
if c.header.type != .PLTE {
|
||||
return {}, false
|
||||
}
|
||||
@@ -255,7 +255,7 @@ plte :: proc(c: Chunk) -> (res: PLTE, ok: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
splt :: proc(c: Chunk) -> (res: sPLT, ok: bool) {
|
||||
splt :: proc(c: image.PNG_Chunk) -> (res: sPLT, ok: bool) {
|
||||
if c.header.type != .sPLT {
|
||||
return {}, false
|
||||
}
|
||||
@@ -306,7 +306,7 @@ splt_destroy :: proc(s: sPLT) {
|
||||
delete(s.name)
|
||||
}
|
||||
|
||||
sbit :: proc(c: Chunk) -> (res: [4]u8, ok: bool) {
|
||||
sbit :: proc(c: image.PNG_Chunk) -> (res: [4]u8, ok: bool) {
|
||||
/*
|
||||
Returns [4]u8 with the significant bits in each channel.
|
||||
A channel will contain zero if not applicable to the PNG color type.
|
||||
@@ -324,7 +324,7 @@ sbit :: proc(c: Chunk) -> (res: [4]u8, ok: bool) {
|
||||
|
||||
}
|
||||
|
||||
hist :: proc(c: Chunk) -> (res: hIST, ok: bool) {
|
||||
hist :: proc(c: image.PNG_Chunk) -> (res: hIST, ok: bool) {
|
||||
if c.header.type != .hIST {
|
||||
return {}, false
|
||||
}
|
||||
@@ -346,7 +346,7 @@ hist :: proc(c: Chunk) -> (res: hIST, ok: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
chrm :: proc(c: Chunk) -> (res: cHRM, ok: bool) {
|
||||
chrm :: proc(c: image.PNG_Chunk) -> (res: cHRM, ok: bool) {
|
||||
ok = true
|
||||
if c.header.length != size_of(cHRM_Raw) {
|
||||
return {}, false
|
||||
@@ -364,7 +364,7 @@ chrm :: proc(c: Chunk) -> (res: cHRM, ok: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
exif :: proc(c: Chunk) -> (res: Exif, ok: bool) {
|
||||
exif :: proc(c: image.PNG_Chunk) -> (res: Exif, ok: bool) {
|
||||
|
||||
ok = true
|
||||
|
||||
|
||||
+160
-171
@@ -1,13 +1,12 @@
|
||||
package png
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-2 license.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
Ginger Bill: Cosmetic changes.
|
||||
*/
|
||||
package png
|
||||
|
||||
import "core:compress"
|
||||
import "core:compress/zlib"
|
||||
@@ -21,11 +20,29 @@ import "core:io"
|
||||
import "core:mem"
|
||||
import "core:intrinsics"
|
||||
|
||||
Error :: compress.Error
|
||||
E_General :: compress.General_Error
|
||||
E_PNG :: image.Error
|
||||
E_Deflate :: compress.Deflate_Error
|
||||
/*
|
||||
67_108_864 pixels max by default.
|
||||
Maximum allowed dimensions are capped at 65535 * 65535.
|
||||
*/
|
||||
MAX_DIMENSIONS :: min(#config(PNG_MAX_DIMENSIONS, 8192 * 8192), 65535 * 65535)
|
||||
|
||||
/*
|
||||
Limit chunk sizes.
|
||||
By default: IDAT = 8k x 8k x 16-bits + 8k filter bytes.
|
||||
*/
|
||||
_MAX_IDAT_DEFAULT :: ( 8192 /* Width */ * 8192 /* Height */ * 2 /* 16-bit */) + 8192 /* Filter bytes */
|
||||
_MAX_IDAT :: (65535 /* Width */ * 65535 /* Height */ * 2 /* 16-bit */) + 65535 /* Filter bytes */
|
||||
|
||||
MAX_IDAT_SIZE :: min(#config(PNG_MAX_IDAT_SIZE, _MAX_IDAT_DEFAULT), _MAX_IDAT)
|
||||
|
||||
/*
|
||||
For chunks other than IDAT with a variable size like `zTXT` and `eXIf`,
|
||||
limit their size to 16 MiB each by default. Max of 256 MiB each.
|
||||
*/
|
||||
MAX_CHUNK_SIZE :: min(#config(PNG_MAX_CHUNK_SIZE, 16_777_216), 268_435_456)
|
||||
|
||||
|
||||
Error :: image.Error
|
||||
Image :: image.Image
|
||||
Options :: image.Options
|
||||
|
||||
@@ -34,95 +51,6 @@ Signature :: enum u64be {
|
||||
PNG = 0x89 << 56 | 'P' << 48 | 'N' << 40 | 'G' << 32 | '\r' << 24 | '\n' << 16 | 0x1a << 8 | '\n',
|
||||
}
|
||||
|
||||
Info :: struct {
|
||||
header: IHDR,
|
||||
chunks: [dynamic]Chunk,
|
||||
}
|
||||
|
||||
Chunk_Header :: struct #packed {
|
||||
length: u32be,
|
||||
type: Chunk_Type,
|
||||
}
|
||||
|
||||
Chunk :: struct #packed {
|
||||
header: Chunk_Header,
|
||||
data: []byte,
|
||||
crc: u32be,
|
||||
}
|
||||
|
||||
Chunk_Type :: enum u32be {
|
||||
// IHDR must come first in a file
|
||||
IHDR = 'I' << 24 | 'H' << 16 | 'D' << 8 | 'R',
|
||||
// PLTE must precede the first IDAT chunk
|
||||
PLTE = 'P' << 24 | 'L' << 16 | 'T' << 8 | 'E',
|
||||
bKGD = 'b' << 24 | 'K' << 16 | 'G' << 8 | 'D',
|
||||
tRNS = 't' << 24 | 'R' << 16 | 'N' << 8 | 'S',
|
||||
IDAT = 'I' << 24 | 'D' << 16 | 'A' << 8 | 'T',
|
||||
|
||||
iTXt = 'i' << 24 | 'T' << 16 | 'X' << 8 | 't',
|
||||
tEXt = 't' << 24 | 'E' << 16 | 'X' << 8 | 't',
|
||||
zTXt = 'z' << 24 | 'T' << 16 | 'X' << 8 | 't',
|
||||
|
||||
iCCP = 'i' << 24 | 'C' << 16 | 'C' << 8 | 'P',
|
||||
pHYs = 'p' << 24 | 'H' << 16 | 'Y' << 8 | 's',
|
||||
gAMA = 'g' << 24 | 'A' << 16 | 'M' << 8 | 'A',
|
||||
tIME = 't' << 24 | 'I' << 16 | 'M' << 8 | 'E',
|
||||
|
||||
sPLT = 's' << 24 | 'P' << 16 | 'L' << 8 | 'T',
|
||||
sRGB = 's' << 24 | 'R' << 16 | 'G' << 8 | 'B',
|
||||
hIST = 'h' << 24 | 'I' << 16 | 'S' << 8 | 'T',
|
||||
cHRM = 'c' << 24 | 'H' << 16 | 'R' << 8 | 'M',
|
||||
sBIT = 's' << 24 | 'B' << 16 | 'I' << 8 | 'T',
|
||||
|
||||
/*
|
||||
eXIf tags are not part of the core spec, but have been ratified
|
||||
in v1.5.0 of the PNG Ext register.
|
||||
|
||||
We will provide unprocessed chunks to the caller if `.return_metadata` is set.
|
||||
Applications are free to implement an Exif decoder.
|
||||
*/
|
||||
eXIf = 'e' << 24 | 'X' << 16 | 'I' << 8 | 'f',
|
||||
|
||||
// PNG files must end with IEND
|
||||
IEND = 'I' << 24 | 'E' << 16 | 'N' << 8 | 'D',
|
||||
|
||||
/*
|
||||
XCode sometimes produces "PNG" files that don't adhere to the PNG spec.
|
||||
We recognize them only in order to avoid doing further work on them.
|
||||
|
||||
Some tools like PNG Defry may be able to repair them, but we're not
|
||||
going to reward Apple for producing proprietary broken files purporting
|
||||
to be PNGs by supporting them.
|
||||
|
||||
*/
|
||||
iDOT = 'i' << 24 | 'D' << 16 | 'O' << 8 | 'T',
|
||||
CbGI = 'C' << 24 | 'b' << 16 | 'H' << 8 | 'I',
|
||||
}
|
||||
|
||||
IHDR :: struct #packed {
|
||||
width: u32be,
|
||||
height: u32be,
|
||||
bit_depth: u8,
|
||||
color_type: Color_Type,
|
||||
compression_method: u8,
|
||||
filter_method: u8,
|
||||
interlace_method: Interlace_Method,
|
||||
}
|
||||
IHDR_SIZE :: size_of(IHDR)
|
||||
#assert (IHDR_SIZE == 13)
|
||||
|
||||
Color_Value :: enum u8 {
|
||||
Paletted = 0, // 1 << 0 = 1
|
||||
Color = 1, // 1 << 1 = 2
|
||||
Alpha = 2, // 1 << 2 = 4
|
||||
}
|
||||
Color_Type :: distinct bit_set[Color_Value; u8]
|
||||
|
||||
Interlace_Method :: enum u8 {
|
||||
None = 0,
|
||||
Adam7 = 1,
|
||||
}
|
||||
|
||||
Row_Filter :: enum u8 {
|
||||
None = 0,
|
||||
Sub = 1,
|
||||
@@ -135,22 +63,22 @@ PLTE_Entry :: [3]u8
|
||||
|
||||
PLTE :: struct #packed {
|
||||
entries: [256]PLTE_Entry,
|
||||
used: u16,
|
||||
used: u16,
|
||||
}
|
||||
|
||||
hIST :: struct #packed {
|
||||
entries: [256]u16,
|
||||
used: u16,
|
||||
used: u16,
|
||||
}
|
||||
|
||||
sPLT :: struct #packed {
|
||||
name: string,
|
||||
depth: u8,
|
||||
name: string,
|
||||
depth: u8,
|
||||
entries: union {
|
||||
[][4]u8,
|
||||
[][4]u16,
|
||||
},
|
||||
used: u16,
|
||||
used: u16,
|
||||
}
|
||||
|
||||
// Other chunks
|
||||
@@ -223,14 +151,14 @@ Exif :: struct {
|
||||
}
|
||||
|
||||
iCCP :: struct {
|
||||
name: string,
|
||||
name: string,
|
||||
profile: []u8,
|
||||
}
|
||||
|
||||
sRGB_Rendering_Intent :: enum u8 {
|
||||
Perceptual = 0,
|
||||
Perceptual = 0,
|
||||
Relative_colorimetric = 1,
|
||||
Saturation = 2,
|
||||
Saturation = 2,
|
||||
Absolute_colorimetric = 3,
|
||||
}
|
||||
|
||||
@@ -245,16 +173,30 @@ ADAM7_Y_SPACING := []int{ 8,8,8,4,4,2,2 }
|
||||
|
||||
// Implementation starts here
|
||||
|
||||
read_chunk :: proc(ctx: ^$C) -> (chunk: Chunk, err: Error) {
|
||||
ch, e := compress.read_data(ctx, Chunk_Header)
|
||||
read_chunk :: proc(ctx: ^$C) -> (chunk: image.PNG_Chunk, err: Error) {
|
||||
ch, e := compress.read_data(ctx, image.PNG_Chunk_Header)
|
||||
if e != .None {
|
||||
return {}, E_General.Stream_Too_Short
|
||||
return {}, compress.General_Error.Stream_Too_Short
|
||||
}
|
||||
chunk.header = ch
|
||||
|
||||
/*
|
||||
Sanity check chunk size
|
||||
*/
|
||||
#partial switch ch.type {
|
||||
case .IDAT:
|
||||
if ch.length > MAX_IDAT_SIZE {
|
||||
return {}, image.PNG_Error.IDAT_Size_Too_Large
|
||||
}
|
||||
case:
|
||||
if ch.length > MAX_CHUNK_SIZE {
|
||||
return {}, image.PNG_Error.Invalid_Chunk_Length
|
||||
}
|
||||
}
|
||||
|
||||
chunk.data, e = compress.read_slice(ctx, int(ch.length))
|
||||
if e != .None {
|
||||
return {}, E_General.Stream_Too_Short
|
||||
return {}, compress.General_Error.Stream_Too_Short
|
||||
}
|
||||
|
||||
// Compute CRC over chunk type + data
|
||||
@@ -264,39 +206,68 @@ read_chunk :: proc(ctx: ^$C) -> (chunk: Chunk, err: Error) {
|
||||
|
||||
crc, e3 := compress.read_data(ctx, u32be)
|
||||
if e3 != .None {
|
||||
return {}, E_General.Stream_Too_Short
|
||||
return {}, compress.General_Error.Stream_Too_Short
|
||||
}
|
||||
chunk.crc = crc
|
||||
|
||||
if chunk.crc != u32be(computed_crc) {
|
||||
return {}, E_General.Checksum_Failed
|
||||
return {}, compress.General_Error.Checksum_Failed
|
||||
}
|
||||
return chunk, nil
|
||||
}
|
||||
|
||||
read_header :: proc(ctx: ^$C) -> (IHDR, Error) {
|
||||
copy_chunk :: proc(src: image.PNG_Chunk, allocator := context.allocator) -> (dest: image.PNG_Chunk, err: Error) {
|
||||
if int(src.header.length) != len(src.data) {
|
||||
return {}, .Invalid_Chunk_Length
|
||||
}
|
||||
|
||||
dest.header = src.header
|
||||
dest.crc = src.crc
|
||||
dest.data = make([]u8, dest.header.length, allocator) or_return
|
||||
|
||||
copy(dest.data[:], src.data[:])
|
||||
return
|
||||
}
|
||||
|
||||
append_chunk :: proc(list: ^[dynamic]image.PNG_Chunk, src: image.PNG_Chunk, allocator := context.allocator) -> (err: Error) {
|
||||
if int(src.header.length) != len(src.data) {
|
||||
return .Invalid_Chunk_Length
|
||||
}
|
||||
|
||||
c := copy_chunk(src, allocator) or_return
|
||||
length := len(list)
|
||||
append(list, c)
|
||||
if len(list) != length + 1 {
|
||||
// Resize during append failed.
|
||||
return mem.Allocator_Error.Out_Of_Memory
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
read_header :: proc(ctx: ^$C) -> (image.PNG_IHDR, Error) {
|
||||
c, e := read_chunk(ctx)
|
||||
if e != nil {
|
||||
return {}, e
|
||||
}
|
||||
|
||||
header := (^IHDR)(raw_data(c.data))^
|
||||
header := (^image.PNG_IHDR)(raw_data(c.data))^
|
||||
// Validate IHDR
|
||||
using header
|
||||
if width == 0 || height == 0 {
|
||||
return {}, E_PNG.Invalid_Image_Dimensions
|
||||
if width == 0 || height == 0 || u128(width) * u128(height) > MAX_DIMENSIONS {
|
||||
return {}, .Invalid_Image_Dimensions
|
||||
}
|
||||
|
||||
if compression_method != 0 {
|
||||
return {}, E_General.Unknown_Compression_Method
|
||||
return {}, compress.General_Error.Unknown_Compression_Method
|
||||
}
|
||||
|
||||
if filter_method != 0 {
|
||||
return {}, E_PNG.Unknown_Filter_Method
|
||||
return {}, .Unknown_Filter_Method
|
||||
}
|
||||
|
||||
if interlace_method != .None && interlace_method != .Adam7 {
|
||||
return {}, E_PNG.Unknown_Interlace_Method
|
||||
return {}, .Unknown_Interlace_Method
|
||||
|
||||
}
|
||||
|
||||
@@ -314,7 +285,7 @@ read_header :: proc(ctx: ^$C) -> (IHDR, Error) {
|
||||
}
|
||||
}
|
||||
if !allowed {
|
||||
return {}, E_PNG.Invalid_Color_Bit_Depth_Combo
|
||||
return {}, .Invalid_Color_Bit_Depth_Combo
|
||||
}
|
||||
case 2, 4, 6:
|
||||
/*
|
||||
@@ -322,7 +293,7 @@ read_header :: proc(ctx: ^$C) -> (IHDR, Error) {
|
||||
Allowed bit depths: 8 and 16
|
||||
*/
|
||||
if bit_depth != 8 && bit_depth != 16 {
|
||||
return {}, E_PNG.Invalid_Color_Bit_Depth_Combo
|
||||
return {}, .Invalid_Color_Bit_Depth_Combo
|
||||
}
|
||||
case 3:
|
||||
/*
|
||||
@@ -337,17 +308,17 @@ read_header :: proc(ctx: ^$C) -> (IHDR, Error) {
|
||||
}
|
||||
}
|
||||
if !allowed {
|
||||
return {}, E_PNG.Invalid_Color_Bit_Depth_Combo
|
||||
return {}, .Invalid_Color_Bit_Depth_Combo
|
||||
}
|
||||
|
||||
case:
|
||||
return {}, E_PNG.Unknown_Color_Type
|
||||
return {}, .Unknown_Color_Type
|
||||
}
|
||||
|
||||
return header, nil
|
||||
}
|
||||
|
||||
chunk_type_to_name :: proc(type: ^Chunk_Type) -> string {
|
||||
chunk_type_to_name :: proc(type: ^image.PNG_Chunk_Type) -> string {
|
||||
t := transmute(^u8)type
|
||||
return strings.string_from_ptr(t, 4)
|
||||
}
|
||||
@@ -377,7 +348,7 @@ load_from_file :: proc(filename: string, options := Options{}, allocator := cont
|
||||
return load_from_slice(data, options)
|
||||
} else {
|
||||
img = new(Image)
|
||||
return img, E_General.File_Not_Found
|
||||
return img, compress.General_Error.File_Not_Found
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,7 +362,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
}
|
||||
|
||||
if .alpha_drop_if_present in options && .alpha_add_if_missing in options {
|
||||
return {}, E_General.Incompatible_Options
|
||||
return {}, compress.General_Error.Incompatible_Options
|
||||
}
|
||||
|
||||
if .do_not_expand_channels in options {
|
||||
@@ -402,27 +373,25 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
img = new(Image)
|
||||
}
|
||||
|
||||
info := new(Info)
|
||||
img.metadata_ptr = info
|
||||
img.metadata_type = typeid_of(Info)
|
||||
info := new(image.PNG_Info)
|
||||
img.metadata = info
|
||||
|
||||
signature, io_error := compress.read_data(ctx, Signature)
|
||||
if io_error != .None || signature != .PNG {
|
||||
return img, E_PNG.Invalid_PNG_Signature
|
||||
return img, .Invalid_PNG_Signature
|
||||
}
|
||||
|
||||
idat: []u8
|
||||
idat_b: bytes.Buffer
|
||||
idat_length := u32be(0)
|
||||
defer bytes.buffer_destroy(&idat_b)
|
||||
|
||||
c: Chunk
|
||||
ch: Chunk_Header
|
||||
idat_length := u64(0)
|
||||
|
||||
c: image.PNG_Chunk
|
||||
ch: image.PNG_Chunk_Header
|
||||
e: io.Error
|
||||
|
||||
header: IHDR
|
||||
|
||||
info.chunks.allocator = context.temp_allocator
|
||||
header: image.PNG_IHDR
|
||||
|
||||
// State to ensure correct chunk ordering.
|
||||
seen_ihdr := false; first := true
|
||||
@@ -433,7 +402,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
seen_iend := false
|
||||
|
||||
_plte := PLTE{}
|
||||
trns := Chunk{}
|
||||
trns := image.PNG_Chunk{}
|
||||
|
||||
final_image_channels := 0
|
||||
|
||||
@@ -443,16 +412,16 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
// Peek at next chunk's length and type.
|
||||
// TODO: Some streams may not provide seek/read_at
|
||||
|
||||
ch, e = compress.peek_data(ctx, Chunk_Header)
|
||||
ch, e = compress.peek_data(ctx, image.PNG_Chunk_Header)
|
||||
if e != .None {
|
||||
return img, E_General.Stream_Too_Short
|
||||
return img, compress.General_Error.Stream_Too_Short
|
||||
}
|
||||
// name := chunk_type_to_name(&ch.type); // Only used for debug prints during development.
|
||||
|
||||
#partial switch ch.type {
|
||||
case .IHDR:
|
||||
if seen_ihdr || !first {
|
||||
return {}, E_PNG.IHDR_Not_First_Chunk
|
||||
return {}, .IHDR_Not_First_Chunk
|
||||
}
|
||||
seen_ihdr = true
|
||||
|
||||
@@ -481,14 +450,14 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
}
|
||||
|
||||
if img.channels == 0 || img.depth == 0 {
|
||||
return {}, E_PNG.IHDR_Corrupt
|
||||
return {}, .IHDR_Corrupt
|
||||
}
|
||||
|
||||
img.width = int(header.width)
|
||||
img.height = int(header.height)
|
||||
|
||||
using header
|
||||
h := IHDR{
|
||||
h := image.PNG_IHDR{
|
||||
width = width,
|
||||
height = height,
|
||||
bit_depth = bit_depth,
|
||||
@@ -498,28 +467,30 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
interlace_method = interlace_method,
|
||||
}
|
||||
info.header = h
|
||||
|
||||
case .PLTE:
|
||||
seen_plte = true
|
||||
// PLTE must appear before IDAT and can't appear for color types 0, 4.
|
||||
ct := transmute(u8)info.header.color_type
|
||||
if seen_idat || ct == 0 || ct == 4 {
|
||||
return img, E_PNG.PLTE_Encountered_Unexpectedly
|
||||
return img, .PLTE_Encountered_Unexpectedly
|
||||
}
|
||||
|
||||
c = read_chunk(ctx) or_return
|
||||
|
||||
if c.header.length % 3 != 0 || c.header.length > 768 {
|
||||
return img, E_PNG.PLTE_Invalid_Length
|
||||
return img, .PLTE_Invalid_Length
|
||||
}
|
||||
plte_ok: bool
|
||||
_plte, plte_ok = plte(c)
|
||||
if !plte_ok {
|
||||
return img, E_PNG.PLTE_Invalid_Length
|
||||
return img, .PLTE_Invalid_Length
|
||||
}
|
||||
|
||||
if .return_metadata in options {
|
||||
append(&info.chunks, c)
|
||||
append_chunk(&info.chunks, c) or_return
|
||||
}
|
||||
|
||||
case .IDAT:
|
||||
// If we only want image metadata and don't want the pixel data, we can early out.
|
||||
if .return_metadata not_in options && .do_not_decompress_image in options {
|
||||
@@ -528,11 +499,11 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
}
|
||||
// There must be at least 1 IDAT, contiguous if more.
|
||||
if seen_idat {
|
||||
return img, E_PNG.IDAT_Must_Be_Contiguous
|
||||
return img, .IDAT_Must_Be_Contiguous
|
||||
}
|
||||
|
||||
if idat_length > 0 {
|
||||
return img, E_PNG.IDAT_Must_Be_Contiguous
|
||||
return img, .IDAT_Must_Be_Contiguous
|
||||
}
|
||||
|
||||
next := ch.type
|
||||
@@ -540,22 +511,29 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
c = read_chunk(ctx) or_return
|
||||
|
||||
bytes.buffer_write(&idat_b, c.data)
|
||||
idat_length += c.header.length
|
||||
idat_length += u64(c.header.length)
|
||||
|
||||
ch, e = compress.peek_data(ctx, Chunk_Header)
|
||||
if idat_length > MAX_IDAT_SIZE {
|
||||
return {}, image.PNG_Error.IDAT_Size_Too_Large
|
||||
}
|
||||
|
||||
ch, e = compress.peek_data(ctx, image.PNG_Chunk_Header)
|
||||
if e != .None {
|
||||
return img, E_General.Stream_Too_Short
|
||||
return img, compress.General_Error.Stream_Too_Short
|
||||
}
|
||||
next = ch.type
|
||||
}
|
||||
|
||||
idat = bytes.buffer_to_bytes(&idat_b)
|
||||
if int(idat_length) != len(idat) {
|
||||
return {}, E_PNG.IDAT_Corrupt
|
||||
return {}, .IDAT_Corrupt
|
||||
}
|
||||
seen_idat = true
|
||||
|
||||
case .IEND:
|
||||
c = read_chunk(ctx) or_return
|
||||
seen_iend = true
|
||||
|
||||
case .bKGD:
|
||||
|
||||
// TODO: Make sure that 16-bit bKGD + tRNS chunks return u16 instead of u16be
|
||||
@@ -563,14 +541,14 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
c = read_chunk(ctx) or_return
|
||||
seen_bkgd = true
|
||||
if .return_metadata in options {
|
||||
append(&info.chunks, c)
|
||||
append_chunk(&info.chunks, c) or_return
|
||||
}
|
||||
|
||||
ct := transmute(u8)info.header.color_type
|
||||
switch ct {
|
||||
case 3: // Indexed color
|
||||
if c.header.length != 1 {
|
||||
return {}, E_PNG.BKGD_Invalid_Length
|
||||
return {}, .BKGD_Invalid_Length
|
||||
}
|
||||
col := _plte.entries[c.data[0]]
|
||||
img.background = [3]u16{
|
||||
@@ -580,26 +558,27 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
}
|
||||
case 0, 4: // Grayscale, with and without Alpha
|
||||
if c.header.length != 2 {
|
||||
return {}, E_PNG.BKGD_Invalid_Length
|
||||
return {}, .BKGD_Invalid_Length
|
||||
}
|
||||
col := u16(mem.slice_data_cast([]u16be, c.data[:])[0])
|
||||
img.background = [3]u16{col, col, col}
|
||||
case 2, 6: // Color, with and without Alpha
|
||||
if c.header.length != 6 {
|
||||
return {}, E_PNG.BKGD_Invalid_Length
|
||||
return {}, .BKGD_Invalid_Length
|
||||
}
|
||||
col := mem.slice_data_cast([]u16be, c.data[:])
|
||||
img.background = [3]u16{u16(col[0]), u16(col[1]), u16(col[2])}
|
||||
}
|
||||
|
||||
case .tRNS:
|
||||
c = read_chunk(ctx) or_return
|
||||
|
||||
if .Alpha in info.header.color_type {
|
||||
return img, E_PNG.TRNS_Encountered_Unexpectedly
|
||||
return img, .TRNS_Encountered_Unexpectedly
|
||||
}
|
||||
|
||||
if .return_metadata in options {
|
||||
append(&info.chunks, c)
|
||||
append_chunk(&info.chunks, c) or_return
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -622,20 +601,20 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
}
|
||||
}
|
||||
trns = c
|
||||
|
||||
case .iDOT, .CbGI:
|
||||
/*
|
||||
iPhone PNG bastardization that doesn't adhere to spec with broken IDAT chunk.
|
||||
We're not going to add support for it. If you have the misfortunte of coming
|
||||
across one of these files, use a utility to defry it.s
|
||||
across one of these files, use a utility to defry it.
|
||||
*/
|
||||
return img, E_PNG.PNG_Does_Not_Adhere_to_Spec
|
||||
return img, .Image_Does_Not_Adhere_to_Spec
|
||||
|
||||
case:
|
||||
// Unhandled type
|
||||
c = read_chunk(ctx) or_return
|
||||
|
||||
if .return_metadata in options {
|
||||
// NOTE: Chunk cata is currently allocated on the temp allocator.
|
||||
append(&info.chunks, c)
|
||||
append_chunk(&info.chunks, c) or_return
|
||||
}
|
||||
|
||||
first = false
|
||||
@@ -648,7 +627,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
}
|
||||
|
||||
if !seen_idat {
|
||||
return img, E_PNG.IDAT_Missing
|
||||
return img, .IDAT_Missing
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -685,7 +664,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
|
||||
buf_len := len(buf.buf)
|
||||
if expected_size != buf_len {
|
||||
return {}, E_PNG.IDAT_Corrupt
|
||||
return {}, .IDAT_Corrupt
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -752,7 +731,9 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
// We need to create a new image buffer
|
||||
dest_raw_size := compute_buffer_size(int(header.width), int(header.height), out_image_channels, 8)
|
||||
t := bytes.Buffer{}
|
||||
resize(&t.buf, dest_raw_size)
|
||||
if !resize(&t.buf, dest_raw_size) {
|
||||
return {}, mem.Allocator_Error.Out_Of_Memory
|
||||
}
|
||||
|
||||
i := 0; j := 0
|
||||
|
||||
@@ -831,7 +812,9 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
// We need to create a new image buffer
|
||||
dest_raw_size := compute_buffer_size(int(header.width), int(header.height), out_image_channels, 16)
|
||||
t := bytes.Buffer{}
|
||||
resize(&t.buf, dest_raw_size)
|
||||
if !resize(&t.buf, dest_raw_size) {
|
||||
return {}, mem.Allocator_Error.Out_Of_Memory
|
||||
}
|
||||
|
||||
p16 := mem.slice_data_cast([]u16, temp.buf[:])
|
||||
o16 := mem.slice_data_cast([]u16, t.buf[:])
|
||||
@@ -1028,7 +1011,9 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
// We need to create a new image buffer
|
||||
dest_raw_size := compute_buffer_size(int(header.width), int(header.height), out_image_channels, 8)
|
||||
t := bytes.Buffer{}
|
||||
resize(&t.buf, dest_raw_size)
|
||||
if !resize(&t.buf, dest_raw_size) {
|
||||
return {}, mem.Allocator_Error.Out_Of_Memory
|
||||
}
|
||||
|
||||
p := mem.slice_data_cast([]u8, temp.buf[:])
|
||||
o := mem.slice_data_cast([]u8, t.buf[:])
|
||||
@@ -1524,7 +1509,7 @@ defilter_16 :: proc(params: ^Filter_Params) -> (ok: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^IHDR, options: Options) -> (err: compress.Error) {
|
||||
defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^image.PNG_IHDR, options: Options) -> (err: Error) {
|
||||
input := bytes.buffer_to_bytes(filter_bytes)
|
||||
width := int(header.width)
|
||||
height := int(header.height)
|
||||
@@ -1535,7 +1520,9 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^IHDR, option
|
||||
bytes_per_channel := depth == 16 ? 2 : 1
|
||||
|
||||
num_bytes := compute_buffer_size(width, height, channels, depth == 16 ? 16 : 8)
|
||||
resize(&img.pixels.buf, num_bytes)
|
||||
if !resize(&img.pixels.buf, num_bytes) {
|
||||
return mem.Allocator_Error.Out_Of_Memory
|
||||
}
|
||||
|
||||
filter_ok: bool
|
||||
|
||||
@@ -1560,7 +1547,7 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^IHDR, option
|
||||
}
|
||||
if !filter_ok {
|
||||
// Caller will destroy buffer for us.
|
||||
return E_PNG.Unknown_Filter_Method
|
||||
return .Unknown_Filter_Method
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
@@ -1575,7 +1562,9 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^IHDR, option
|
||||
if x > 0 && y > 0 {
|
||||
temp: bytes.Buffer
|
||||
temp_len := compute_buffer_size(x, y, channels, depth == 16 ? 16 : 8)
|
||||
resize(&temp.buf, temp_len)
|
||||
if !resize(&temp.buf, temp_len) {
|
||||
return mem.Allocator_Error.Out_Of_Memory
|
||||
}
|
||||
|
||||
params := Filter_Params{
|
||||
src = input,
|
||||
@@ -1598,7 +1587,7 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^IHDR, option
|
||||
|
||||
if !filter_ok {
|
||||
// Caller will destroy buffer for us.
|
||||
return E_PNG.Unknown_Filter_Method
|
||||
return .Unknown_Filter_Method
|
||||
}
|
||||
|
||||
t := temp.buf[:]
|
||||
|
||||
@@ -39,6 +39,7 @@ sqrt :: proc(x: $T) -> T where type_is_float(T) ---
|
||||
mem_copy :: proc(dst, src: rawptr, len: int) ---
|
||||
mem_copy_non_overlapping :: proc(dst, src: rawptr, len: int) ---
|
||||
mem_zero :: proc(ptr: rawptr, len: int) ---
|
||||
mem_zero_volatile :: proc(ptr: rawptr, len: int) ---
|
||||
|
||||
|
||||
fixed_point_mul :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) ---
|
||||
|
||||
+1
-1
@@ -443,7 +443,7 @@ read_at_least :: proc(r: Reader, buf: []byte, min: int) -> (n: int, err: Error)
|
||||
for n < min && err == nil {
|
||||
nn: int
|
||||
nn, err = read(r, buf[n:])
|
||||
n += n
|
||||
n += nn
|
||||
}
|
||||
|
||||
if n >= min {
|
||||
|
||||
+10
-10
@@ -116,14 +116,14 @@ write_encoded_rune :: proc(w: Writer, r: rune, write_quote := true, n_written: ^
|
||||
write_byte(w, '\'', &n) or_return
|
||||
}
|
||||
switch r {
|
||||
case '\a': write_string(w, `\a"`, &n) or_return
|
||||
case '\b': write_string(w, `\b"`, &n) or_return
|
||||
case '\e': write_string(w, `\e"`, &n) or_return
|
||||
case '\f': write_string(w, `\f"`, &n) or_return
|
||||
case '\n': write_string(w, `\n"`, &n) or_return
|
||||
case '\r': write_string(w, `\r"`, &n) or_return
|
||||
case '\t': write_string(w, `\t"`, &n) or_return
|
||||
case '\v': write_string(w, `\v"`, &n) or_return
|
||||
case '\a': write_string(w, `\a`, &n) or_return
|
||||
case '\b': write_string(w, `\b`, &n) or_return
|
||||
case '\e': write_string(w, `\e`, &n) or_return
|
||||
case '\f': write_string(w, `\f`, &n) or_return
|
||||
case '\n': write_string(w, `\n`, &n) or_return
|
||||
case '\r': write_string(w, `\r`, &n) or_return
|
||||
case '\t': write_string(w, `\t`, &n) or_return
|
||||
case '\v': write_string(w, `\v`, &n) or_return
|
||||
case:
|
||||
if r < 32 {
|
||||
write_string(w, `\x`, &n) or_return
|
||||
@@ -132,8 +132,8 @@ write_encoded_rune :: proc(w: Writer, r: rune, write_quote := true, n_written: ^
|
||||
s := strconv.append_bits(buf[:], u64(r), 16, true, 64, strconv.digits, nil)
|
||||
switch len(s) {
|
||||
case 0: write_string(w, "00", &n) or_return
|
||||
case 1: write_byte(w, '0', &n) or_return
|
||||
case 2: write_string(w, s, &n) or_return
|
||||
case 1: write_byte(w, '0', &n) or_return
|
||||
case 2: write_string(w, s, &n) or_return
|
||||
}
|
||||
} else {
|
||||
write_rune(w, r, &n) or_return
|
||||
|
||||
@@ -13,6 +13,8 @@ create_multi_logger :: proc(logs: ..Logger) -> Logger {
|
||||
}
|
||||
|
||||
destroy_multi_logger :: proc(log : ^Logger) {
|
||||
data := (^Multi_Logger_Data)(log.data)
|
||||
delete(data.loggers)
|
||||
free(log.data)
|
||||
log^ = nil_logger()
|
||||
}
|
||||
|
||||
@@ -32,6 +32,9 @@ add :: proc {
|
||||
int_add_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context.allocator) -> (err: Error)
|
||||
*/
|
||||
int_add_digit,
|
||||
rat_add_rat,
|
||||
rat_add_int,
|
||||
int_add_rat,
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -46,6 +49,9 @@ sub :: proc {
|
||||
int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error)
|
||||
*/
|
||||
int_sub_digit,
|
||||
rat_sub_rat,
|
||||
rat_sub_int,
|
||||
int_sub_rat,
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -67,6 +73,10 @@ is_zero :: proc {
|
||||
int_is_zero :: proc(a: ^Int) -> bool
|
||||
*/
|
||||
int_is_zero,
|
||||
/*
|
||||
rat_is_zero :: proc(a: ^Rat) -> bool
|
||||
*/
|
||||
rat_is_zero,
|
||||
}
|
||||
|
||||
is_positive :: proc {
|
||||
@@ -74,6 +84,7 @@ is_positive :: proc {
|
||||
int_is_positive :: proc(a: ^Int) -> bool
|
||||
*/
|
||||
int_is_positive,
|
||||
rat_is_positive,
|
||||
}
|
||||
is_pos :: is_positive
|
||||
|
||||
@@ -82,6 +93,7 @@ is_negative :: proc {
|
||||
int_is_negative :: proc(a: ^Int) -> bool
|
||||
*/
|
||||
int_is_negative,
|
||||
rat_is_negative,
|
||||
}
|
||||
is_neg :: is_negative
|
||||
|
||||
@@ -90,6 +102,7 @@ is_even :: proc {
|
||||
int_is_even :: proc(a: ^Int) -> bool
|
||||
*/
|
||||
int_is_even,
|
||||
rat_is_even,
|
||||
}
|
||||
|
||||
is_odd :: proc {
|
||||
@@ -97,6 +110,7 @@ is_odd :: proc {
|
||||
int_is_odd :: proc(a: ^Int) -> bool
|
||||
*/
|
||||
int_is_odd,
|
||||
rat_is_odd,
|
||||
}
|
||||
|
||||
is_power_of_two :: proc {
|
||||
|
||||
@@ -44,7 +44,20 @@ int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, allocator :
|
||||
return #force_inline internal_int_set_from_integer(dest, src, minimize)
|
||||
}
|
||||
|
||||
set :: proc { int_set_from_integer, int_copy, int_atoi, }
|
||||
set :: proc {
|
||||
int_set_from_integer,
|
||||
int_copy,
|
||||
int_atoi,
|
||||
|
||||
rat_set_f64,
|
||||
rat_set_f32,
|
||||
rat_set_f16,
|
||||
rat_set_u64,
|
||||
rat_set_i64,
|
||||
rat_set_int,
|
||||
rat_set_digit,
|
||||
rat_set_rat,
|
||||
}
|
||||
|
||||
/*
|
||||
Copy one `Int` to another.
|
||||
@@ -66,7 +79,10 @@ int_copy :: proc(dest, src: ^Int, minimize := false, allocator := context.alloca
|
||||
|
||||
return #force_inline internal_int_copy(dest, src, minimize)
|
||||
}
|
||||
copy :: proc { int_copy, }
|
||||
copy :: proc {
|
||||
int_copy,
|
||||
rat_copy,
|
||||
}
|
||||
|
||||
/*
|
||||
In normal code, you can also write `a, b = b, a`.
|
||||
@@ -77,7 +93,7 @@ int_swap :: proc(a, b: ^Int) {
|
||||
assert_if_nil(a, b)
|
||||
#force_inline internal_swap(a, b)
|
||||
}
|
||||
swap :: proc { int_swap, }
|
||||
swap :: proc { int_swap, rat_swap }
|
||||
|
||||
/*
|
||||
Set `dest` to |`src`|.
|
||||
@@ -98,7 +114,7 @@ int_abs :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error)
|
||||
platform_abs :: proc(n: $T) -> T where intrinsics.type_is_integer(T) {
|
||||
return n if n >= 0 else -n
|
||||
}
|
||||
abs :: proc{ int_abs, platform_abs, }
|
||||
abs :: proc{ int_abs, platform_abs, rat_abs }
|
||||
|
||||
/*
|
||||
Set `dest` to `-src`.
|
||||
@@ -115,7 +131,7 @@ int_neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error)
|
||||
|
||||
return #force_inline internal_int_neg(dest, src)
|
||||
}
|
||||
neg :: proc { int_neg, }
|
||||
neg :: proc { int_neg, rat_neg }
|
||||
|
||||
/*
|
||||
Helpers to extract values from the `Int`.
|
||||
@@ -788,7 +804,10 @@ destroy_constants :: proc() {
|
||||
}
|
||||
|
||||
|
||||
assert_if_nil :: proc{assert_if_nil_int}
|
||||
assert_if_nil :: proc{
|
||||
assert_if_nil_int,
|
||||
assert_if_nil_rat,
|
||||
}
|
||||
|
||||
assert_if_nil_int :: #force_inline proc(integers: ..^Int, loc := #caller_location) {
|
||||
for i in integers {
|
||||
@@ -796,3 +815,8 @@ assert_if_nil_int :: #force_inline proc(integers: ..^Int, loc := #caller_locatio
|
||||
}
|
||||
}
|
||||
|
||||
assert_if_nil_rat :: #force_inline proc(rationals: ..^Rat, loc := #caller_location) {
|
||||
for r in rationals {
|
||||
assert(r != nil, "(nil)", loc)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1042,7 +1042,10 @@ internal_is_initialized :: proc { internal_int_is_initialized, }
|
||||
internal_int_is_zero :: #force_inline proc(a: ^Int) -> (zero: bool) {
|
||||
return a.used == 0
|
||||
}
|
||||
internal_is_zero :: proc { internal_int_is_zero }
|
||||
internal_is_zero :: proc {
|
||||
internal_rat_is_zero,
|
||||
internal_int_is_zero,
|
||||
}
|
||||
|
||||
/*
|
||||
This procedure will return `true` if the `Int` is positive, `false` if not.
|
||||
@@ -1865,7 +1868,10 @@ internal_int_destroy :: proc(integers: ..^Int) {
|
||||
a = &Int{}
|
||||
}
|
||||
}
|
||||
internal_destroy :: proc{ internal_int_destroy, }
|
||||
internal_destroy :: proc{
|
||||
internal_int_destroy,
|
||||
internal_rat_destroy,
|
||||
}
|
||||
|
||||
/*
|
||||
Helpers to set an `Int` to a specific value.
|
||||
@@ -1950,13 +1956,14 @@ internal_copy :: proc { internal_int_copy, }
|
||||
This helper swaps completely.
|
||||
*/
|
||||
internal_int_swap :: #force_inline proc(a, b: ^Int) {
|
||||
a := a; b := b
|
||||
|
||||
a.used, b.used = b.used, a.used
|
||||
a.sign, b.sign = b.sign, a.sign
|
||||
a.digit, b.digit = b.digit, a.digit
|
||||
}
|
||||
internal_swap :: proc { internal_int_swap, }
|
||||
internal_swap :: proc {
|
||||
internal_int_swap,
|
||||
internal_rat_swap,
|
||||
}
|
||||
|
||||
/*
|
||||
Set `dest` to |`src`|.
|
||||
|
||||
@@ -152,9 +152,18 @@ int_mul :: proc(dest, src, multiplier: ^Int, allocator := context.allocator) ->
|
||||
return #force_inline internal_int_mul(dest, src, multiplier)
|
||||
}
|
||||
|
||||
mul :: proc { int_mul, int_mul_digit, }
|
||||
mul :: proc {
|
||||
int_mul,
|
||||
int_mul_digit,
|
||||
rat_mul_rat,
|
||||
rat_mul_int,
|
||||
int_mul_rat,
|
||||
}
|
||||
|
||||
int_sqr :: proc(dest, src: ^Int) -> (err: Error) { return mul(dest, src, src) }
|
||||
rat_sqr :: proc(dest, src: ^Rat) -> (err: Error) { return mul(dest, src, src) }
|
||||
sqr :: proc { int_sqr, rat_sqr }
|
||||
|
||||
sqr :: proc(dest, src: ^Int) -> (err: Error) { return mul(dest, src, src) }
|
||||
|
||||
/*
|
||||
divmod.
|
||||
@@ -200,7 +209,13 @@ int_div_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT, allocator :
|
||||
_ = #force_inline internal_divmod(quotient, numerator, denominator) or_return
|
||||
return
|
||||
}
|
||||
div :: proc { int_div, int_div_digit, }
|
||||
div :: proc {
|
||||
int_div,
|
||||
int_div_digit,
|
||||
rat_div_rat,
|
||||
rat_div_int,
|
||||
int_div_rat,
|
||||
}
|
||||
|
||||
/*
|
||||
remainder = numerator % denominator.
|
||||
|
||||
@@ -0,0 +1,540 @@
|
||||
package math_big
|
||||
|
||||
import "core:builtin"
|
||||
import "core:intrinsics"
|
||||
import "core:math"
|
||||
|
||||
Rat :: struct {
|
||||
a, b: Int,
|
||||
}
|
||||
|
||||
rat_set_f64 :: proc(dst: ^Rat, f: f64, allocator := context.allocator) -> (err: Error) {
|
||||
assert_if_nil(dst)
|
||||
context.allocator = allocator
|
||||
|
||||
EXP_MASK :: 1<<11 - 1
|
||||
|
||||
bits := transmute(u64)f
|
||||
mantissa := bits & (1<<52 - 1)
|
||||
exp := int((bits>>52) & EXP_MASK)
|
||||
|
||||
int_set_from_integer(&dst.b, 1) or_return
|
||||
|
||||
switch exp {
|
||||
case EXP_MASK:
|
||||
dst.a.flags += {.Inf}
|
||||
return
|
||||
case 0:
|
||||
exp -= 1022
|
||||
case:
|
||||
mantissa |= 1<<52
|
||||
exp -= 1023
|
||||
}
|
||||
|
||||
shift := 52 - exp
|
||||
|
||||
for mantissa&1 == 0 && shift > 0 {
|
||||
mantissa >>= 1
|
||||
shift -= 1
|
||||
}
|
||||
|
||||
int_set_from_integer(&dst.a, mantissa) or_return
|
||||
dst.a.sign = .Negative if f < 0 else .Zero_or_Positive
|
||||
|
||||
if shift > 0 {
|
||||
internal_int_shl_digit(&dst.b, shift) or_return
|
||||
} else {
|
||||
internal_int_shl_digit(&dst.a, -shift) or_return
|
||||
}
|
||||
|
||||
return internal_rat_norm(dst)
|
||||
}
|
||||
|
||||
rat_set_f32 :: proc(dst: ^Rat, f: f32, allocator := context.allocator) -> (err: Error) {
|
||||
return rat_set_f64(dst, f64(f), allocator)
|
||||
}
|
||||
rat_set_f16 :: proc(dst: ^Rat, f: f16, allocator := context.allocator) -> (err: Error) {
|
||||
return rat_set_f64(dst, f64(f), allocator)
|
||||
}
|
||||
|
||||
|
||||
rat_set_frac :: proc{rat_set_frac_digit, rat_set_frac_int}
|
||||
|
||||
rat_set_frac_digit :: proc(dst: ^Rat, a, b: DIGIT, allocator := context.allocator) -> (err: Error) {
|
||||
assert_if_nil(dst)
|
||||
if b == 0 {
|
||||
return .Division_by_Zero
|
||||
}
|
||||
context.allocator = allocator
|
||||
internal_set(&dst.a, a) or_return
|
||||
internal_set(&dst.b, b) or_return
|
||||
return internal_rat_norm(dst)
|
||||
}
|
||||
|
||||
rat_set_frac_int :: proc(dst: ^Rat, a, b: ^Int, allocator := context.allocator) -> (err: Error) {
|
||||
assert_if_nil(dst)
|
||||
assert_if_nil(a, b)
|
||||
if internal_is_zero(b) {
|
||||
return .Division_by_Zero
|
||||
}
|
||||
context.allocator = allocator
|
||||
internal_set(&dst.a, a) or_return
|
||||
internal_set(&dst.b, b) or_return
|
||||
return internal_rat_norm(dst)
|
||||
}
|
||||
|
||||
rat_set_int :: proc(dst: ^Rat, a: ^Int, allocator := context.allocator) -> (err: Error) {
|
||||
assert_if_nil(dst)
|
||||
assert_if_nil(a)
|
||||
context.allocator = allocator
|
||||
internal_set(&dst.a, a) or_return
|
||||
internal_set(&dst.b, 1) or_return
|
||||
return
|
||||
}
|
||||
|
||||
rat_set_digit :: proc(dst: ^Rat, a: DIGIT, allocator := context.allocator) -> (err: Error) {
|
||||
assert_if_nil(dst)
|
||||
context.allocator = allocator
|
||||
internal_set(&dst.a, a) or_return
|
||||
internal_set(&dst.b, 1) or_return
|
||||
return
|
||||
}
|
||||
|
||||
rat_set_rat :: proc(dst, x: ^Rat, allocator := context.allocator) -> (err: Error) {
|
||||
assert_if_nil(dst, x)
|
||||
context.allocator = allocator
|
||||
internal_set(&dst.a, &x.a) or_return
|
||||
internal_set(&dst.b, &x.b) or_return
|
||||
return
|
||||
}
|
||||
|
||||
rat_set_u64 :: proc(dst: ^Rat, x: u64, allocator := context.allocator) -> (err: Error) {
|
||||
assert_if_nil(dst)
|
||||
context.allocator = allocator
|
||||
internal_set(&dst.a, x) or_return
|
||||
internal_set(&dst.a, 1) or_return
|
||||
return
|
||||
}
|
||||
rat_set_i64 :: proc(dst: ^Rat, x: i64, allocator := context.allocator) -> (err: Error) {
|
||||
assert_if_nil(dst)
|
||||
context.allocator = allocator
|
||||
internal_set(&dst.a, x) or_return
|
||||
internal_set(&dst.a, 1) or_return
|
||||
return
|
||||
}
|
||||
|
||||
rat_copy :: proc(dst, src: ^Rat, minimize := false, allocator := context.allocator) -> (err: Error) {
|
||||
if (dst == src) { return nil }
|
||||
|
||||
assert_if_nil(dst, src)
|
||||
context.allocator = allocator
|
||||
int_copy(&dst.a, &src.a, minimize, allocator) or_return
|
||||
int_copy(&dst.b, &src.b, minimize, allocator) or_return
|
||||
internal_rat_norm(dst) or_return
|
||||
return nil
|
||||
}
|
||||
|
||||
internal_rat_destroy :: proc(rationals: ..^Rat) {
|
||||
rationals := rationals
|
||||
|
||||
for z in &rationals {
|
||||
internal_int_destroy(&z.a, &z.b)
|
||||
}
|
||||
}
|
||||
|
||||
internal_rat_norm :: proc(z: ^Rat, allocator := context.allocator) -> (err: Error) {
|
||||
assert_if_nil(z)
|
||||
context.allocator = allocator
|
||||
switch {
|
||||
case internal_is_zero(&z.a):
|
||||
z.a.sign = .Zero_or_Positive
|
||||
fallthrough
|
||||
case internal_is_zero(&z.b):
|
||||
int_set_from_integer(&z.b, 1) or_return
|
||||
case:
|
||||
sign := z.a.sign
|
||||
z.a.sign = .Zero_or_Positive
|
||||
z.b.sign = .Zero_or_Positive
|
||||
|
||||
f := &Int{}
|
||||
internal_int_gcd(f, &z.a, &z.b) or_return
|
||||
if !internal_int_equals_digit(f, 1) {
|
||||
f.sign = .Zero_or_Positive
|
||||
internal_int_div(&z.a, &z.a, f) or_return
|
||||
internal_int_div(&z.b, &z.b, f) or_return
|
||||
}
|
||||
z.a.sign = sign
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
rat_swap :: proc(a, b: ^Rat) {
|
||||
assert_if_nil(a, b)
|
||||
#force_inline internal_swap(a, b)
|
||||
}
|
||||
|
||||
internal_rat_swap :: #force_inline proc(a, b: ^Rat) {
|
||||
internal_int_swap(&a.a, &b.a)
|
||||
internal_int_swap(&a.b, &b.b)
|
||||
}
|
||||
|
||||
rat_sign :: proc(z: ^Rat) -> Sign {
|
||||
if z == nil {
|
||||
return .Zero_or_Positive
|
||||
}
|
||||
return z.a.sign
|
||||
}
|
||||
|
||||
rat_is_int :: proc(z: ^Rat) -> bool {
|
||||
assert_if_nil(z)
|
||||
return internal_is_zero(&z.a) || internal_int_equals_digit(&z.b, 1)
|
||||
}
|
||||
|
||||
rat_is_zero :: proc(z: ^Rat) -> bool {
|
||||
return internal_rat_is_zero(z)
|
||||
}
|
||||
internal_rat_is_zero :: #force_inline proc(z: ^Rat) -> bool {
|
||||
assert_if_nil(z)
|
||||
return internal_is_zero(&z.a)
|
||||
}
|
||||
|
||||
internal_int_mul_denom :: proc(dst, x, y: ^Int, allocator := context.allocator) -> (err: Error) {
|
||||
assert_if_nil(dst, x, y)
|
||||
context.allocator = allocator
|
||||
switch {
|
||||
case internal_is_zero(x) && internal_is_zero(y):
|
||||
return internal_set(dst, 1)
|
||||
case internal_is_zero(x):
|
||||
return internal_set(dst, y)
|
||||
case internal_is_zero(y):
|
||||
return internal_set(dst, x)
|
||||
}
|
||||
return int_mul(dst, x, y)
|
||||
}
|
||||
|
||||
internal_int_scale_denom :: proc(dst, x, y: ^Int, allocator := context.allocator) -> (err: Error) {
|
||||
assert_if_nil(dst, x, y)
|
||||
if internal_is_zero(y) {
|
||||
return internal_set(dst, x)
|
||||
}
|
||||
int_mul(dst, x, y) or_return
|
||||
dst.sign = x.sign
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
rat_add_rat :: proc(dst, x, y: ^Rat, allocator := context.allocator) -> (err: Error) {
|
||||
assert_if_nil(dst, x, y)
|
||||
context.allocator = allocator
|
||||
|
||||
a1, a2: Int
|
||||
defer internal_destroy(&a1, &a2)
|
||||
|
||||
internal_int_scale_denom(&a1, &x.a, &y.b) or_return
|
||||
internal_int_scale_denom(&a2, &y.a, &x.b) or_return
|
||||
int_add(&dst.a, &a1, &a2) or_return
|
||||
internal_int_mul_denom(&dst.b, &x.b, &y.b) or_return
|
||||
return internal_rat_norm(dst)
|
||||
}
|
||||
|
||||
rat_sub_rat :: proc(dst, x, y: ^Rat, allocator := context.allocator) -> (err: Error) {
|
||||
assert_if_nil(dst, x, y)
|
||||
context.allocator = allocator
|
||||
|
||||
a1, a2 := &Int{}, &Int{}
|
||||
defer internal_destroy(a1, a2)
|
||||
|
||||
internal_int_scale_denom(a1, &x.a, &y.b) or_return
|
||||
internal_int_scale_denom(a2, &y.a, &x.b) or_return
|
||||
int_sub(&dst.a, a1, a2) or_return
|
||||
internal_int_mul_denom(&dst.b, &x.b, &y.b) or_return
|
||||
return internal_rat_norm(dst)
|
||||
}
|
||||
|
||||
rat_mul_rat :: proc(dst, x, y: ^Rat, allocator := context.allocator) -> (err: Error) {
|
||||
assert_if_nil(dst, x, y)
|
||||
context.allocator = allocator
|
||||
|
||||
if x == y {
|
||||
internal_sqr(&dst.a, &x.a) or_return
|
||||
if internal_is_zero(&x.b) {
|
||||
internal_set(&dst.b, 1) or_return
|
||||
} else {
|
||||
internal_sqr(&dst.a, &x.b) or_return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
int_sub(&dst.a, &x.a, &y.a) or_return
|
||||
internal_int_mul_denom(&dst.b, &x.b, &y.b) or_return
|
||||
return internal_rat_norm(dst)
|
||||
}
|
||||
|
||||
rat_div_rat :: proc(dst, x, y: ^Rat, allocator := context.allocator) -> (err: Error) {
|
||||
if internal_rat_is_zero(y) {
|
||||
return .Division_by_Zero
|
||||
}
|
||||
context.allocator = allocator
|
||||
|
||||
a, b := &Int{}, &Int{}
|
||||
defer internal_destroy(a, b)
|
||||
|
||||
internal_int_scale_denom(a, &x.a, &y.b) or_return
|
||||
internal_int_scale_denom(b, &y.a, &x.b) or_return
|
||||
internal_set(&dst.a, a) or_return
|
||||
internal_set(&dst.b, b) or_return
|
||||
internal_int_abs(&dst.a, &dst.a)
|
||||
internal_int_abs(&dst.b, &dst.b)
|
||||
dst.a.sign = .Negative if a.sign != b.sign else .Zero_or_Positive
|
||||
return internal_rat_norm(dst)
|
||||
}
|
||||
|
||||
|
||||
rat_abs :: proc(dst, x: ^Rat, allocator := context.allocator) -> (err: Error) {
|
||||
rat_set_rat(dst, x, allocator) or_return
|
||||
internal_abs(&dst.a, &dst.a, allocator) or_return
|
||||
return
|
||||
}
|
||||
rat_neg :: proc(dst, x: ^Rat, allocator := context.allocator) -> (err: Error) {
|
||||
rat_set_rat(dst, x, allocator) or_return
|
||||
internal_neg(&dst.a, &dst.a, allocator) or_return
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
rat_is_positive :: proc(z: ^Rat, allocator := context.allocator) -> (ok: bool, err: Error) {
|
||||
assert_if_nil(z)
|
||||
a := int_is_positive(&z.a, allocator) or_return
|
||||
b := int_is_positive(&z.b, allocator) or_return
|
||||
return !(a ~ b), nil
|
||||
}
|
||||
rat_is_negative :: proc(z: ^Rat, allocator := context.allocator) -> (ok: bool, err: Error) {
|
||||
assert_if_nil(z)
|
||||
a := int_is_positive(&z.a, allocator) or_return
|
||||
b := int_is_positive(&z.b, allocator) or_return
|
||||
return (a ~ b), nil
|
||||
}
|
||||
|
||||
rat_is_even :: proc(z: ^Rat, allocator := context.allocator) -> (ok: bool, err: Error) {
|
||||
assert_if_nil(z)
|
||||
if rat_is_int(z) {
|
||||
return int_is_even(&z.a, allocator)
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
rat_is_odd :: proc(z: ^Rat, allocator := context.allocator) -> (ok: bool, err: Error) {
|
||||
assert_if_nil(z)
|
||||
if rat_is_int(z) {
|
||||
return int_is_odd(&z.a, allocator)
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
rat_to_f16 :: proc(z: ^Rat, allocator := context.allocator) -> (f: f16, exact: bool, err: Error) {
|
||||
assert_if_nil(z)
|
||||
return internal_rat_to_float(f16, z, allocator)
|
||||
}
|
||||
rat_to_f32 :: proc(z: ^Rat, allocator := context.allocator) -> (f: f32, exact: bool, err: Error) {
|
||||
assert_if_nil(z)
|
||||
return internal_rat_to_float(f32, z, allocator)
|
||||
}
|
||||
rat_to_f64 :: proc(z: ^Rat, allocator := context.allocator) -> (f: f64, exact: bool, err: Error) {
|
||||
assert_if_nil(z)
|
||||
return internal_rat_to_float(f64, z, allocator)
|
||||
}
|
||||
|
||||
internal_rat_to_float :: proc($T: typeid, z: ^Rat, allocator := context.allocator) -> (f: T, exact: bool, err: Error) where intrinsics.type_is_float(T) {
|
||||
FSIZE :: 8*size_of(T)
|
||||
when FSIZE == 16 {
|
||||
MSIZE :: 10
|
||||
} else when FSIZE == 32 {
|
||||
MSIZE :: 23
|
||||
} else when FSIZE == 64 {
|
||||
MSIZE :: 52
|
||||
} else {
|
||||
#panic("unsupported float type")
|
||||
}
|
||||
|
||||
MSIZE1 :: MSIZE+1
|
||||
MSIZE2 :: MSIZE+2
|
||||
|
||||
ESIZE :: FSIZE - MSIZE1
|
||||
EBIAS :: 1<<(ESIZE-1) - 1
|
||||
EMIN :: 1 - EBIAS
|
||||
EMAX :: EBIAS
|
||||
|
||||
assert_if_nil(z)
|
||||
a, b := &z.a, &z.b
|
||||
|
||||
context.allocator = allocator
|
||||
|
||||
alen := internal_count_bits(a)
|
||||
if alen == 0 {
|
||||
return 0, true, nil
|
||||
}
|
||||
blen := internal_count_bits(b)
|
||||
if blen == 0 {
|
||||
return T(math.nan_f64()), false, .Division_by_Zero
|
||||
}
|
||||
|
||||
has_sign := a.sign != b.sign
|
||||
defer if has_sign {
|
||||
f = -builtin.abs(f)
|
||||
}
|
||||
|
||||
exp := alen - blen
|
||||
a2, b2 := &Int{}, &Int{}
|
||||
defer internal_destroy(a2, b2)
|
||||
internal_int_abs(a2, a) or_return
|
||||
internal_int_abs(b2, b) or_return
|
||||
|
||||
if shift := MSIZE2 - exp; shift > 0 {
|
||||
internal_int_shl_digit(a2, shift) or_return
|
||||
} else {
|
||||
internal_int_shl_digit(b2, -shift) or_return
|
||||
}
|
||||
|
||||
q, r := &Int{}, &Int{}
|
||||
defer internal_destroy(q, r)
|
||||
|
||||
internal_int_divmod(q, r, a2, b2) or_return
|
||||
|
||||
has_rem := !internal_is_zero(r)
|
||||
mantissa := internal_int_get_u64(q) or_return
|
||||
|
||||
if mantissa>>MSIZE2 == 1 {
|
||||
if mantissa&1 == 1 {
|
||||
has_rem = true
|
||||
}
|
||||
mantissa >>= 1
|
||||
exp += 1
|
||||
}
|
||||
|
||||
assert(mantissa>>MSIZE1 == 1, "invalid bit result")
|
||||
|
||||
|
||||
if EMIN-MSIZE <= exp && exp <= EMIN {
|
||||
shift := uint(EMIN - (exp - 1))
|
||||
lost_bits := mantissa & (1<<shift - 1)
|
||||
has_rem ||= lost_bits != 0
|
||||
mantissa >>= shift
|
||||
exp = 2 - EBIAS // exp + shift
|
||||
}
|
||||
|
||||
|
||||
exact = !has_rem
|
||||
if mantissa&1 != 0 {
|
||||
exact = false
|
||||
if has_rem || mantissa&2 != 0 {
|
||||
mantissa += 1
|
||||
if mantissa >= 1<<MSIZE2 {
|
||||
mantissa >>= 1
|
||||
exp += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mantissa >>= 1
|
||||
|
||||
f = T(math.ldexp(f64(mantissa), i32(exp-MSIZE1)))
|
||||
if math.is_inf(f, 0) {
|
||||
exact = false
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
rat_compare :: proc(x, y: ^Rat, allocator := context.allocator) -> (comparison: int, error: Error) {
|
||||
assert_if_nil(x, y)
|
||||
context.allocator = allocator
|
||||
|
||||
a, b: Int
|
||||
internal_init_multi(&a, &b) or_return
|
||||
defer internal_destroy(&a, &b)
|
||||
internal_int_scale_denom(&a, &x.a, &y.b) or_return
|
||||
internal_int_scale_denom(&b, &y.a, &x.b) or_return
|
||||
return int_compare(&a, &b)
|
||||
}
|
||||
|
||||
|
||||
|
||||
rat_add_int :: proc(dst, x: ^Rat, y: ^Int, allocator := context.allocator) -> (err: Error) {
|
||||
assert_if_nil(dst, x)
|
||||
assert_if_nil(y)
|
||||
|
||||
z: Rat
|
||||
rat_set_int(&z, y, allocator) or_return
|
||||
defer internal_destroy(&z)
|
||||
return rat_add_rat(dst, x, &z, allocator)
|
||||
}
|
||||
|
||||
rat_sub_int :: proc(dst, x: ^Rat, y: ^Int, allocator := context.allocator) -> (err: Error) {
|
||||
assert_if_nil(dst, x)
|
||||
assert_if_nil(y)
|
||||
|
||||
z: Rat
|
||||
rat_set_int(&z, y, allocator) or_return
|
||||
defer internal_destroy(&z)
|
||||
return rat_sub_rat(dst, x, &z, allocator)
|
||||
}
|
||||
|
||||
rat_mul_int :: proc(dst, x: ^Rat, y: ^Int, allocator := context.allocator) -> (err: Error) {
|
||||
assert_if_nil(dst, x)
|
||||
assert_if_nil(y)
|
||||
|
||||
z: Rat
|
||||
rat_set_int(&z, y, allocator) or_return
|
||||
defer internal_destroy(&z)
|
||||
return rat_mul_rat(dst, x, &z, allocator)
|
||||
}
|
||||
|
||||
rat_div_int :: proc(dst, x: ^Rat, y: ^Int, allocator := context.allocator) -> (err: Error) {
|
||||
if internal_is_zero(y) {
|
||||
return .Division_by_Zero
|
||||
}
|
||||
z: Rat
|
||||
rat_set_int(&z, y, allocator) or_return
|
||||
defer internal_destroy(&z)
|
||||
return rat_div_rat(dst, x, &z, allocator)
|
||||
}
|
||||
|
||||
|
||||
int_add_rat :: proc(dst: ^Rat, x: ^Int, y: ^Rat, allocator := context.allocator) -> (err: Error) {
|
||||
assert_if_nil(x)
|
||||
assert_if_nil(dst, y)
|
||||
|
||||
w: Rat
|
||||
rat_set_int(&w, x, allocator) or_return
|
||||
defer internal_destroy(&w)
|
||||
return rat_add_rat(dst, &w, y, allocator)
|
||||
}
|
||||
|
||||
int_sub_rat :: proc(dst: ^Rat, x: ^Int, y: ^Rat, allocator := context.allocator) -> (err: Error) {
|
||||
assert_if_nil(x)
|
||||
assert_if_nil(dst, y)
|
||||
|
||||
w: Rat
|
||||
rat_set_int(&w, x, allocator) or_return
|
||||
defer internal_destroy(&w)
|
||||
return rat_sub_rat(dst, &w, y, allocator)
|
||||
}
|
||||
|
||||
int_mul_rat :: proc(dst: ^Rat, x: ^Int, y: ^Rat, allocator := context.allocator) -> (err: Error) {
|
||||
assert_if_nil(x)
|
||||
assert_if_nil(dst, y)
|
||||
|
||||
w: Rat
|
||||
rat_set_int(&w, x, allocator) or_return
|
||||
defer internal_destroy(&w)
|
||||
return rat_mul_rat(dst, &w, y, allocator)
|
||||
}
|
||||
|
||||
int_div_rat :: proc(dst: ^Rat, x: ^Int, y: ^Rat, allocator := context.allocator) -> (err: Error) {
|
||||
if internal_is_zero(y) {
|
||||
return .Division_by_Zero
|
||||
}
|
||||
w: Rat
|
||||
rat_set_int(&w, x, allocator) or_return
|
||||
defer internal_destroy(&w)
|
||||
return rat_div_rat(dst, &w, y, allocator)
|
||||
}
|
||||
+30
-42
@@ -147,55 +147,53 @@ len :: proc{len_u8, len_u16, len_u32, len_u64, len_uint}
|
||||
|
||||
|
||||
add_u32 :: proc(x, y, carry: u32) -> (sum, carry_out: u32) {
|
||||
yc := y + carry
|
||||
sum = x + yc
|
||||
if sum < x || yc < y {
|
||||
carry_out = 1
|
||||
}
|
||||
tmp_carry, tmp_carry2: bool
|
||||
sum, tmp_carry = intrinsics.overflow_add(x, y)
|
||||
sum, tmp_carry2 = intrinsics.overflow_add(sum, carry)
|
||||
carry_out = u32(tmp_carry | tmp_carry2)
|
||||
return
|
||||
}
|
||||
add_u64 :: proc(x, y, carry: u64) -> (sum, carry_out: u64) {
|
||||
yc := y + carry
|
||||
sum = x + yc
|
||||
if sum < x || yc < y {
|
||||
carry_out = 1
|
||||
}
|
||||
tmp_carry, tmp_carry2: bool
|
||||
sum, tmp_carry = intrinsics.overflow_add(x, y)
|
||||
sum, tmp_carry2 = intrinsics.overflow_add(sum, carry)
|
||||
carry_out = u64(tmp_carry | tmp_carry2)
|
||||
return
|
||||
}
|
||||
add_uint :: proc(x, y, carry: uint) -> (sum, carry_out: uint) {
|
||||
yc := y + carry
|
||||
sum = x + yc
|
||||
if sum < x || yc < y {
|
||||
carry_out = 1
|
||||
when size_of(uint) == size_of(u64) {
|
||||
a, b := add_u64(u64(x), u64(y), u64(carry))
|
||||
} else {
|
||||
#assert(size_of(uint) == size_of(u32))
|
||||
a, b := add_u32(u32(x), u32(y), u32(carry))
|
||||
}
|
||||
return
|
||||
return uint(a), uint(b)
|
||||
}
|
||||
add :: proc{add_u32, add_u64, add_uint}
|
||||
|
||||
|
||||
sub_u32 :: proc(x, y, borrow: u32) -> (diff, borrow_out: u32) {
|
||||
yb := y + borrow
|
||||
diff = x - yb
|
||||
if diff > x || yb < y {
|
||||
borrow_out = 1
|
||||
}
|
||||
tmp_borrow, tmp_borrow2: bool
|
||||
diff, tmp_borrow = intrinsics.overflow_sub(x, y)
|
||||
diff, tmp_borrow2 = intrinsics.overflow_sub(diff, borrow)
|
||||
borrow_out = u32(tmp_borrow | tmp_borrow2)
|
||||
return
|
||||
}
|
||||
sub_u64 :: proc(x, y, borrow: u64) -> (diff, borrow_out: u64) {
|
||||
yb := y + borrow
|
||||
diff = x - yb
|
||||
if diff > x || yb < y {
|
||||
borrow_out = 1
|
||||
}
|
||||
tmp_borrow, tmp_borrow2: bool
|
||||
diff, tmp_borrow = intrinsics.overflow_sub(x, y)
|
||||
diff, tmp_borrow2 = intrinsics.overflow_sub(diff, borrow)
|
||||
borrow_out = u64(tmp_borrow | tmp_borrow2)
|
||||
return
|
||||
}
|
||||
sub_uint :: proc(x, y, borrow: uint) -> (diff, borrow_out: uint) {
|
||||
yb := y + borrow
|
||||
diff = x - yb
|
||||
if diff > x || yb < y {
|
||||
borrow_out = 1
|
||||
when size_of(uint) == size_of(u64) {
|
||||
a, b := sub_u64(u64(x), u64(y), u64(borrow))
|
||||
} else {
|
||||
#assert(size_of(uint) == size_of(u32))
|
||||
a, b := sub_u32(u32(x), u32(y), u32(borrow))
|
||||
}
|
||||
return
|
||||
return uint(a), uint(b)
|
||||
}
|
||||
sub :: proc{sub_u32, sub_u64, sub_uint}
|
||||
|
||||
@@ -206,18 +204,8 @@ mul_u32 :: proc(x, y: u32) -> (hi, lo: u32) {
|
||||
return
|
||||
}
|
||||
mul_u64 :: proc(x, y: u64) -> (hi, lo: u64) {
|
||||
mask :: 1<<32 - 1
|
||||
|
||||
x0, x1 := x & mask, x >> 32
|
||||
y0, y1 := y & mask, y >> 32
|
||||
|
||||
w0 := x0 * y0
|
||||
t := x1*y0 + w0>>32
|
||||
|
||||
w1, w2 := t & mask, t >> 32
|
||||
w1 += x0 * y1
|
||||
hi = x1*y1 + w2 + w1>>32
|
||||
lo = x * y
|
||||
prod_wide := u128(x) * u128(y)
|
||||
hi, lo = u64(prod_wide>>64), u64(prod_wide)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+11
-9
@@ -10,6 +10,15 @@ zero :: proc "contextless" (data: rawptr, len: int) -> rawptr {
|
||||
intrinsics.mem_zero(data, len)
|
||||
return data
|
||||
}
|
||||
zero_explicit :: proc "contextless" (data: rawptr, len: int) -> rawptr {
|
||||
// This routine tries to avoid the compiler optimizing away the call,
|
||||
// so that it is always executed. It is intended to provided
|
||||
// equivalent semantics to those provided by the C11 Annex K 3.7.4.1
|
||||
// memset_s call.
|
||||
intrinsics.mem_zero_volatile(data, len) // Use the volatile mem_zero
|
||||
intrinsics.atomic_fence() // Prevent reordering
|
||||
return data
|
||||
}
|
||||
zero_item :: proc "contextless" (item: $P/^$T) {
|
||||
intrinsics.mem_zero(item, size_of(T))
|
||||
}
|
||||
@@ -128,14 +137,8 @@ compare_ptrs :: proc "contextless" (a, b: rawptr, n: int) -> int {
|
||||
return compare_byte_ptrs((^byte)(a), (^byte)(b), n)
|
||||
}
|
||||
|
||||
ptr_offset :: proc "contextless" (ptr: $P/^$T, n: int) -> P {
|
||||
new := int(uintptr(ptr)) + size_of(T)*n
|
||||
return P(uintptr(new))
|
||||
}
|
||||
|
||||
ptr_sub :: proc "contextless" (a, b: $P/^$T) -> int {
|
||||
return (int(uintptr(a)) - int(uintptr(b)))/size_of(T)
|
||||
}
|
||||
ptr_offset :: intrinsics.ptr_offset
|
||||
ptr_sub :: intrinsics.ptr_sub
|
||||
|
||||
slice_ptr :: proc "contextless" (ptr: ^$T, len: int) -> []T {
|
||||
return ([^]T)(ptr)[:len]
|
||||
@@ -179,7 +182,6 @@ buffer_from_slice :: proc "contextless" (backing: $T/[]$E) -> [dynamic]E {
|
||||
}
|
||||
|
||||
ptr_to_bytes :: proc "contextless" (ptr: ^$T, len := 1) -> []byte {
|
||||
assert(len >= 0)
|
||||
return transmute([]byte)Raw_Slice{ptr, len*size_of(T)}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
package mem_virtual
|
||||
|
||||
import "core:mem"
|
||||
import sync "core:sync/sync2"
|
||||
|
||||
Growing_Arena :: struct {
|
||||
curr_block: ^Memory_Block,
|
||||
total_used: int,
|
||||
total_allocated: int,
|
||||
|
||||
minimum_block_size: int,
|
||||
|
||||
ignore_mutex: bool,
|
||||
mutex: sync.Mutex,
|
||||
}
|
||||
|
||||
DEFAULT_MINIMUM_BLOCK_SIZE :: 8*1024*1024
|
||||
DEFAULT_PAGE_SIZE := 4096
|
||||
|
||||
growing_arena_alloc :: proc(arena: ^Growing_Arena, min_size: int, alignment: int) -> (data: []byte, err: mem.Allocator_Error) {
|
||||
align_forward_offset :: proc(arena: ^Growing_Arena, alignment: int) -> int #no_bounds_check {
|
||||
alignment_offset := 0
|
||||
ptr := uintptr(arena.curr_block.base[arena.curr_block.used:])
|
||||
mask := uintptr(alignment-1)
|
||||
if ptr & mask != 0 {
|
||||
alignment_offset = alignment - int(ptr & mask)
|
||||
}
|
||||
return alignment_offset
|
||||
}
|
||||
|
||||
assert(mem.is_power_of_two(uintptr(alignment)))
|
||||
|
||||
mutex := &arena.mutex
|
||||
if !arena.ignore_mutex {
|
||||
sync.mutex_lock(mutex)
|
||||
}
|
||||
|
||||
size := 0
|
||||
if arena.curr_block != nil {
|
||||
size = min_size + align_forward_offset(arena, alignment)
|
||||
}
|
||||
|
||||
if arena.curr_block == nil || arena.curr_block.used + size > arena.curr_block.size {
|
||||
size = mem.align_forward_int(min_size, alignment)
|
||||
arena.minimum_block_size = max(DEFAULT_MINIMUM_BLOCK_SIZE, arena.minimum_block_size)
|
||||
|
||||
block_size := max(size, arena.minimum_block_size)
|
||||
|
||||
new_block := memory_alloc(block_size) or_return
|
||||
new_block.prev = arena.curr_block
|
||||
arena.curr_block = new_block
|
||||
arena.total_allocated += new_block.size
|
||||
}
|
||||
|
||||
curr_block := arena.curr_block
|
||||
assert(curr_block.used + size <= curr_block.size)
|
||||
|
||||
ptr := curr_block.base[curr_block.used:]
|
||||
ptr = ptr[uintptr(align_forward_offset(arena, alignment)):]
|
||||
|
||||
curr_block.used += size
|
||||
assert(curr_block.used <= curr_block.size)
|
||||
arena.total_used += size
|
||||
|
||||
if !arena.ignore_mutex {
|
||||
sync.mutex_unlock(mutex)
|
||||
}
|
||||
|
||||
return ptr[:min_size], nil
|
||||
}
|
||||
|
||||
growing_arena_free_all :: proc(arena: ^Growing_Arena) {
|
||||
mutex := &arena.mutex
|
||||
if !arena.ignore_mutex {
|
||||
sync.mutex_lock(mutex)
|
||||
}
|
||||
for arena.curr_block != nil {
|
||||
free_block := arena.curr_block
|
||||
arena.curr_block = free_block.prev
|
||||
memory_dealloc(free_block)
|
||||
}
|
||||
arena.total_used = 0
|
||||
if !arena.ignore_mutex {
|
||||
sync.mutex_unlock(mutex)
|
||||
}
|
||||
}
|
||||
|
||||
growing_arena_allocator :: proc(arena: ^Growing_Arena) -> mem.Allocator {
|
||||
return mem.Allocator{growing_arena_allocator_proc, arena}
|
||||
}
|
||||
|
||||
growing_arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int,
|
||||
location := #caller_location) -> (data: []byte, err: mem.Allocator_Error) {
|
||||
arena := (^Growing_Arena)(allocator_data)
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return growing_arena_alloc(arena, size, alignment)
|
||||
case .Free:
|
||||
err = .Mode_Not_Implemented
|
||||
return
|
||||
case .Free_All:
|
||||
growing_arena_free_all(arena)
|
||||
return
|
||||
case .Resize:
|
||||
return mem.default_resize_bytes_align(mem.byte_slice(old_memory, old_size), size, alignment, growing_arena_allocator(arena), location)
|
||||
|
||||
case .Query_Features, .Query_Info:
|
||||
err = .Mode_Not_Implemented
|
||||
return
|
||||
}
|
||||
|
||||
err = .Mode_Not_Implemented
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package mem_virtual
|
||||
|
||||
arena_temp_begin :: proc{
|
||||
static_arena_temp_begin,
|
||||
growing_arena_temp_begin,
|
||||
}
|
||||
|
||||
arena_temp_end :: proc{
|
||||
static_arena_temp_end,
|
||||
growing_arena_temp_end,
|
||||
}
|
||||
|
||||
arena_check_temp :: proc{
|
||||
static_arena_check_temp,
|
||||
growing_arena_check_temp,
|
||||
}
|
||||
|
||||
arena_allocator :: proc{
|
||||
static_arena_allocator,
|
||||
growing_arena_allocator,
|
||||
}
|
||||
|
||||
arena_alloc :: proc{
|
||||
static_arena_alloc,
|
||||
growing_arena_alloc,
|
||||
}
|
||||
|
||||
arena_free_all :: proc{
|
||||
static_arena_free_all,
|
||||
growing_arena_free_all,
|
||||
}
|
||||
|
||||
arena_destroy :: proc{
|
||||
static_arena_destroy,
|
||||
growing_arena_destroy,
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package mem_virtual
|
||||
|
||||
import "core:mem"
|
||||
|
||||
Growing_Arena :: struct {
|
||||
curr_block: ^Memory_Block,
|
||||
total_used: uint,
|
||||
total_reserved: uint,
|
||||
|
||||
minimum_block_size: uint,
|
||||
temp_count: int,
|
||||
}
|
||||
|
||||
DEFAULT_MINIMUM_BLOCK_SIZE :: 1<<20 // 1 MiB should be enough
|
||||
|
||||
growing_arena_alloc :: proc(arena: ^Growing_Arena, min_size: int, alignment: int) -> (data: []byte, err: Allocator_Error) {
|
||||
align_forward_offset :: proc "contextless" (arena: ^Growing_Arena, alignment: int) -> uint #no_bounds_check {
|
||||
alignment_offset := uint(0)
|
||||
ptr := uintptr(arena.curr_block.base[arena.curr_block.used:])
|
||||
mask := uintptr(alignment-1)
|
||||
if ptr & mask != 0 {
|
||||
alignment_offset = uint(alignment) - uint(ptr & mask)
|
||||
}
|
||||
return alignment_offset
|
||||
}
|
||||
|
||||
assert(mem.is_power_of_two(uintptr(alignment)))
|
||||
|
||||
size := uint(0)
|
||||
if arena.curr_block != nil {
|
||||
size = uint(min_size) + align_forward_offset(arena, alignment)
|
||||
}
|
||||
|
||||
if arena.curr_block == nil || arena.curr_block.used + size > arena.curr_block.reserved {
|
||||
size = uint(mem.align_forward_int(min_size, alignment))
|
||||
arena.minimum_block_size = max(DEFAULT_MINIMUM_BLOCK_SIZE, arena.minimum_block_size)
|
||||
|
||||
block_size := max(size, arena.minimum_block_size)
|
||||
|
||||
new_block := memory_block_alloc(block_size, block_size, {}) or_return
|
||||
new_block.prev = arena.curr_block
|
||||
arena.curr_block = new_block
|
||||
arena.total_reserved += new_block.reserved
|
||||
}
|
||||
|
||||
|
||||
data, err = alloc_from_memory_block(arena.curr_block, int(size), alignment)
|
||||
if err == nil {
|
||||
arena.total_used += size
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
growing_arena_free_last_memory_block :: proc(arena: ^Growing_Arena) {
|
||||
free_block := arena.curr_block
|
||||
arena.curr_block = free_block.prev
|
||||
memory_block_dealloc(free_block)
|
||||
}
|
||||
|
||||
growing_arena_free_all :: proc(arena: ^Growing_Arena) {
|
||||
for arena.curr_block != nil {
|
||||
growing_arena_free_last_memory_block(arena)
|
||||
}
|
||||
arena.total_used = 0
|
||||
arena.total_reserved = 0
|
||||
}
|
||||
|
||||
growing_arena_destroy :: proc(arena: ^Growing_Arena) {
|
||||
growing_arena_free_all(arena)
|
||||
}
|
||||
|
||||
growing_arena_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, minimum_block_size := DEFAULT_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) {
|
||||
bootstrap: Growing_Arena
|
||||
bootstrap.minimum_block_size = minimum_block_size
|
||||
|
||||
data := growing_arena_alloc(&bootstrap, size_of(T), align_of(T)) or_return
|
||||
|
||||
ptr = (^T)(raw_data(data))
|
||||
|
||||
(^Growing_Arena)(uintptr(ptr) + offset_to_arena)^ = bootstrap
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
growing_arena_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, minimum_block_size := DEFAULT_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) {
|
||||
return growing_arena_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), minimum_block_size)
|
||||
}
|
||||
growing_arena_bootstrap_new :: proc{
|
||||
growing_arena_bootstrap_new_by_offset,
|
||||
growing_arena_bootstrap_new_by_name,
|
||||
}
|
||||
|
||||
growing_arena_allocator :: proc(arena: ^Growing_Arena) -> mem.Allocator {
|
||||
return mem.Allocator{growing_arena_allocator_proc, arena}
|
||||
}
|
||||
|
||||
growing_arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int,
|
||||
location := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||
arena := (^Growing_Arena)(allocator_data)
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return growing_arena_alloc(arena, size, alignment)
|
||||
case .Free:
|
||||
err = .Mode_Not_Implemented
|
||||
return
|
||||
case .Free_All:
|
||||
growing_arena_free_all(arena)
|
||||
return
|
||||
case .Resize:
|
||||
return mem.default_resize_bytes_align(mem.byte_slice(old_memory, old_size), size, alignment, growing_arena_allocator(arena), location)
|
||||
|
||||
case .Query_Features, .Query_Info:
|
||||
err = .Mode_Not_Implemented
|
||||
return
|
||||
}
|
||||
|
||||
err = .Mode_Not_Implemented
|
||||
return
|
||||
}
|
||||
|
||||
Growing_Arena_Temp :: struct {
|
||||
arena: ^Growing_Arena,
|
||||
block: ^Memory_Block,
|
||||
used: uint,
|
||||
}
|
||||
|
||||
growing_arena_temp_begin :: proc(arena: ^Growing_Arena) -> (temp: Growing_Arena_Temp) {
|
||||
temp.arena = arena
|
||||
temp.block = arena.curr_block
|
||||
if arena.curr_block != nil {
|
||||
temp.used = arena.curr_block.used
|
||||
}
|
||||
arena.temp_count += 1
|
||||
return
|
||||
}
|
||||
|
||||
growing_arena_temp_end :: proc(temp: Growing_Arena_Temp, loc := #caller_location) {
|
||||
assert(temp.arena != nil, "nil arena", loc)
|
||||
arena := temp.arena
|
||||
|
||||
for arena.curr_block != temp.block {
|
||||
growing_arena_free_last_memory_block(arena)
|
||||
}
|
||||
|
||||
if block := arena.curr_block; block != nil {
|
||||
assert(block.used >= temp.used, "out of order use of growing_arena_temp_end", loc)
|
||||
amount_to_zero := min(block.used-temp.used, block.reserved-block.used)
|
||||
mem.zero_slice(block.base[temp.used:][:amount_to_zero])
|
||||
block.used = temp.used
|
||||
}
|
||||
|
||||
assert(arena.temp_count > 0, "double-use of growing_arena_temp_end", loc)
|
||||
arena.temp_count -= 1
|
||||
}
|
||||
|
||||
growing_arena_check_temp :: proc(arena: ^Growing_Arena, loc := #caller_location) {
|
||||
assert(arena.temp_count == 0, "Growing_Arena_Temp not been ended", loc)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
package mem_virtual
|
||||
|
||||
import "core:mem"
|
||||
|
||||
Static_Arena :: struct {
|
||||
block: ^Memory_Block,
|
||||
total_used: uint,
|
||||
total_reserved: uint,
|
||||
|
||||
minimum_block_size: uint,
|
||||
temp_count: int,
|
||||
}
|
||||
|
||||
STATIC_ARENA_DEFAULT_COMMIT_SIZE :: 1<<20 // 1 MiB should be enough to start with
|
||||
|
||||
// 1 GiB on 64-bit systems, 128 MiB on 32-bit systems by default
|
||||
STATIC_ARENA_DEFAULT_RESERVE_SIZE :: 1<<30 when size_of(uintptr) == 8 else 1<<27
|
||||
|
||||
static_arena_init :: proc(arena: ^Static_Arena, reserved: uint, commit_size: uint = STATIC_ARENA_DEFAULT_COMMIT_SIZE) -> (err: Allocator_Error) {
|
||||
arena.block = memory_block_alloc(commit_size, reserved, {}) or_return
|
||||
arena.total_used = 0
|
||||
arena.total_reserved = arena.block.reserved
|
||||
return
|
||||
}
|
||||
|
||||
static_arena_destroy :: proc(arena: ^Static_Arena) {
|
||||
memory_block_dealloc(arena.block)
|
||||
arena^ = {}
|
||||
}
|
||||
|
||||
|
||||
static_arena_alloc :: proc(arena: ^Static_Arena, size: int, alignment: int) -> (data: []byte, err: Allocator_Error) {
|
||||
align_forward :: #force_inline proc "contextless" (ptr: uint, align: uint) -> uint {
|
||||
mask := align-1
|
||||
return (ptr + mask) &~ mask
|
||||
}
|
||||
|
||||
if arena.block == nil {
|
||||
reserve_size := max(arena.minimum_block_size, STATIC_ARENA_DEFAULT_RESERVE_SIZE)
|
||||
static_arena_init(arena, reserve_size, STATIC_ARENA_DEFAULT_COMMIT_SIZE) or_return
|
||||
}
|
||||
|
||||
MINIMUM_ALIGN :: 2*align_of(uintptr)
|
||||
|
||||
defer arena.total_used = arena.block.used
|
||||
return alloc_from_memory_block(arena.block, size, max(MINIMUM_ALIGN, alignment))
|
||||
}
|
||||
|
||||
static_arena_reset_to :: proc(arena: ^Static_Arena, pos: uint) -> bool {
|
||||
if arena.block != nil {
|
||||
prev_pos := arena.block.used
|
||||
arena.block.used = clamp(pos, 0, arena.block.reserved)
|
||||
|
||||
if prev_pos < pos {
|
||||
mem.zero_slice(arena.block.base[arena.block.used:][:pos-prev_pos])
|
||||
}
|
||||
return true
|
||||
} else if pos == 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
static_arena_free_all :: proc(arena: ^Static_Arena) {
|
||||
static_arena_reset_to(arena, 0)
|
||||
}
|
||||
|
||||
|
||||
static_arena_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
|
||||
bootstrap: Static_Arena
|
||||
bootstrap.minimum_block_size = reserved
|
||||
|
||||
data := static_arena_alloc(&bootstrap, size_of(T), align_of(T)) or_return
|
||||
|
||||
ptr = (^T)(raw_data(data))
|
||||
|
||||
(^Static_Arena)(uintptr(ptr) + offset_to_arena)^ = bootstrap
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
static_arena_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
|
||||
return static_arena_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), reserved)
|
||||
}
|
||||
static_arena_bootstrap_new :: proc{
|
||||
static_arena_bootstrap_new_by_offset,
|
||||
static_arena_bootstrap_new_by_name,
|
||||
}
|
||||
|
||||
|
||||
static_arena_allocator :: proc(arena: ^Static_Arena) -> mem.Allocator {
|
||||
return mem.Allocator{static_arena_allocator_proc, arena}
|
||||
}
|
||||
|
||||
static_arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int,
|
||||
location := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||
arena := (^Static_Arena)(allocator_data)
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return static_arena_alloc(arena, size, alignment)
|
||||
case .Free:
|
||||
err = .Mode_Not_Implemented
|
||||
return
|
||||
case .Free_All:
|
||||
static_arena_free_all(arena)
|
||||
return
|
||||
case .Resize:
|
||||
return mem.default_resize_bytes_align(mem.byte_slice(old_memory, old_size), size, alignment, static_arena_allocator(arena), location)
|
||||
|
||||
case .Query_Features, .Query_Info:
|
||||
err = .Mode_Not_Implemented
|
||||
return
|
||||
}
|
||||
|
||||
err = .Mode_Not_Implemented
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Static_Arena_Temp :: struct {
|
||||
arena: ^Static_Arena,
|
||||
used: uint,
|
||||
}
|
||||
|
||||
|
||||
static_arena_temp_begin :: proc(arena: ^Static_Arena) -> (temp: Static_Arena_Temp) {
|
||||
temp.arena = arena
|
||||
temp.used = arena.block.used if arena.block != nil else 0
|
||||
arena.temp_count += 1
|
||||
return
|
||||
}
|
||||
|
||||
static_arena_temp_end :: proc(temp: Static_Arena_Temp, loc := #caller_location) {
|
||||
assert(temp.arena != nil, "nil arena", loc)
|
||||
arena := temp.arena
|
||||
|
||||
used := arena.block.used if arena.block != nil else 0
|
||||
|
||||
assert(temp.used >= used, "invalid Static_Arena_Temp", loc)
|
||||
|
||||
static_arena_reset_to(arena, temp.used)
|
||||
|
||||
assert(arena.temp_count > 0, "double-use of static_arena_temp_end", loc)
|
||||
arena.temp_count -= 1
|
||||
}
|
||||
|
||||
|
||||
static_arena_check_temp :: proc(arena: ^Static_Arena, loc := #caller_location) {
|
||||
assert(arena.temp_count == 0, "Static_Arena_Temp not been ended", loc)
|
||||
}
|
||||
+107
-64
@@ -1,105 +1,148 @@
|
||||
package mem_virtual
|
||||
|
||||
import "core:mem"
|
||||
import sync "core:sync/sync2"
|
||||
|
||||
Memory_Block :: struct {
|
||||
prev: ^Memory_Block,
|
||||
base: [^]byte,
|
||||
size: int,
|
||||
used: int,
|
||||
DEFAULT_PAGE_SIZE := uint(4096)
|
||||
|
||||
Allocator_Error :: mem.Allocator_Error
|
||||
|
||||
reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
|
||||
return _reserve(size)
|
||||
}
|
||||
|
||||
commit :: proc(data: rawptr, size: uint) -> Allocator_Error {
|
||||
return _commit(data, size)
|
||||
}
|
||||
|
||||
reserve_and_commit :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
|
||||
data = reserve(size) or_return
|
||||
commit(raw_data(data), size) or_return
|
||||
return
|
||||
}
|
||||
|
||||
decommit :: proc(data: rawptr, size: uint) {
|
||||
_decommit(data, size)
|
||||
}
|
||||
|
||||
release :: proc(data: rawptr, size: uint) {
|
||||
_release(data, size)
|
||||
}
|
||||
|
||||
Protect_Flag :: enum u32 {
|
||||
Read,
|
||||
Write,
|
||||
Execute,
|
||||
}
|
||||
Protect_Flags :: distinct bit_set[Protect_Flag; u32]
|
||||
Protect_No_Access :: Protect_Flags{}
|
||||
|
||||
protect :: proc(data: rawptr, size: uint, flags: Protect_Flags) -> bool {
|
||||
return _protect(data, size, flags)
|
||||
}
|
||||
|
||||
|
||||
memory_alloc :: proc(size: int) -> (block: ^Memory_Block, err: mem.Allocator_Error) {
|
||||
page_size := DEFAULT_PAGE_SIZE
|
||||
|
||||
|
||||
Memory_Block :: struct {
|
||||
prev: ^Memory_Block,
|
||||
base: [^]byte,
|
||||
used: uint,
|
||||
committed: uint,
|
||||
reserved: uint,
|
||||
}
|
||||
Memory_Block_Flag :: enum u32 {
|
||||
Overflow_Protection,
|
||||
}
|
||||
Memory_Block_Flags :: distinct bit_set[Memory_Block_Flag; u32]
|
||||
|
||||
|
||||
memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags) -> (block: ^Memory_Block, err: Allocator_Error) {
|
||||
align_formula :: proc "contextless" (size, align: uint) -> uint {
|
||||
result := size + align-1
|
||||
return result - result%align
|
||||
}
|
||||
|
||||
total_size := size + size_of(Platform_Memory_Block)
|
||||
page_size := DEFAULT_PAGE_SIZE
|
||||
committed := committed
|
||||
committed = clamp(committed, 0, reserved)
|
||||
|
||||
total_size := uint(reserved + size_of(Platform_Memory_Block))
|
||||
base_offset := uintptr(size_of(Platform_Memory_Block))
|
||||
protect_offset := uintptr(0)
|
||||
|
||||
do_protection := false
|
||||
{ // overflow protection
|
||||
rounded_size := mem.align_formula(size, page_size)
|
||||
total_size = rounded_size + 2*page_size
|
||||
base_offset = uintptr(page_size + rounded_size - size)
|
||||
if .Overflow_Protection in flags { // overflow protection
|
||||
rounded_size := align_formula(uint(reserved), page_size)
|
||||
total_size = uint(rounded_size + 2*page_size)
|
||||
base_offset = uintptr(page_size + rounded_size - uint(reserved))
|
||||
protect_offset = uintptr(page_size + rounded_size)
|
||||
do_protection = true
|
||||
}
|
||||
|
||||
pmblock := platform_memory_alloc(total_size) or_return
|
||||
pmblock := platform_memory_alloc(0, total_size) or_return
|
||||
|
||||
pmblock.block.base = ([^]byte)(uintptr(pmblock) + base_offset)
|
||||
commit(pmblock.block.base, committed) or_return
|
||||
// Should be zeroed
|
||||
assert(pmblock.block.used == 0)
|
||||
assert(pmblock.block.prev == nil)
|
||||
|
||||
assert(pmblock.block.prev == nil)
|
||||
if (do_protection) {
|
||||
platform_memory_protect(rawptr(uintptr(pmblock) + protect_offset), page_size)
|
||||
protect(rawptr(uintptr(pmblock) + protect_offset), page_size, Protect_No_Access)
|
||||
}
|
||||
|
||||
pmblock.block.size = size
|
||||
pmblock.total_size = total_size
|
||||
pmblock.block.committed = committed
|
||||
pmblock.block.reserved = reserved
|
||||
|
||||
sentinel := &global_platform_memory_block_sentinel
|
||||
sync.mutex_lock(&global_memory_block_mutex)
|
||||
platform_mutex_lock()
|
||||
pmblock.next = sentinel
|
||||
pmblock.prev = sentinel.prev
|
||||
pmblock.prev.next = pmblock
|
||||
pmblock.next.prev = pmblock
|
||||
sync.mutex_unlock(&global_memory_block_mutex)
|
||||
platform_mutex_unlock()
|
||||
|
||||
return &pmblock.block, nil
|
||||
}
|
||||
|
||||
alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int) -> (data: []byte, err: Allocator_Error) {
|
||||
calc_alignment_offset :: proc(block: ^Memory_Block, alignment: uintptr) -> uint {
|
||||
alignment_offset := uint(0)
|
||||
ptr := uintptr(block.base[block.used:])
|
||||
mask := alignment-1
|
||||
if ptr & mask != 0 {
|
||||
alignment_offset = uint(alignment - (ptr & mask))
|
||||
}
|
||||
return alignment_offset
|
||||
|
||||
}
|
||||
|
||||
alignment_offset := calc_alignment_offset(block, uintptr(alignment))
|
||||
|
||||
size := uint(min_size) + alignment_offset
|
||||
|
||||
if block.used + size > block.reserved {
|
||||
err = .Out_Of_Memory
|
||||
return
|
||||
}
|
||||
|
||||
ptr := block.base[block.used:]
|
||||
ptr = ptr[alignment_offset:]
|
||||
|
||||
block.used += size
|
||||
assert(block.used <= block.reserved)
|
||||
|
||||
return ptr[:min_size], nil
|
||||
}
|
||||
|
||||
memory_dealloc :: proc(block_to_free: ^Memory_Block) {
|
||||
block := (^Platform_Memory_Block)(block_to_free)
|
||||
if block != nil {
|
||||
sync.mutex_lock(&global_memory_block_mutex)
|
||||
|
||||
memory_block_dealloc :: proc(block_to_free: ^Memory_Block) {
|
||||
if block := (^Platform_Memory_Block)(block_to_free); block != nil {
|
||||
platform_mutex_lock()
|
||||
block.prev.next = block.next
|
||||
block.next.prev = block.prev
|
||||
sync.mutex_unlock(&global_memory_block_mutex)
|
||||
platform_mutex_unlock()
|
||||
|
||||
platform_memory_free(block)
|
||||
}
|
||||
}
|
||||
|
||||
Platform_Memory_Block :: struct {
|
||||
block: Memory_Block,
|
||||
total_size: int,
|
||||
prev, next: ^Platform_Memory_Block,
|
||||
}
|
||||
|
||||
@(private)
|
||||
global_memory_block_mutex: sync.Mutex
|
||||
@(private)
|
||||
global_platform_memory_block_sentinel: Platform_Memory_Block
|
||||
@(private)
|
||||
global_platform_memory_block_sentinel_set: bool
|
||||
|
||||
@(private)
|
||||
platform_memory_init :: proc() {
|
||||
if !global_platform_memory_block_sentinel_set {
|
||||
_platform_memory_init()
|
||||
global_platform_memory_block_sentinel.prev = &global_platform_memory_block_sentinel
|
||||
global_platform_memory_block_sentinel.next = &global_platform_memory_block_sentinel
|
||||
global_platform_memory_block_sentinel_set = true
|
||||
}
|
||||
}
|
||||
|
||||
platform_memory_alloc :: proc(block_size: int) -> (^Platform_Memory_Block, mem.Allocator_Error) {
|
||||
platform_memory_init()
|
||||
return _platform_memory_alloc(block_size)
|
||||
}
|
||||
|
||||
|
||||
platform_memory_free :: proc(block: ^Platform_Memory_Block) {
|
||||
platform_memory_init()
|
||||
_platform_memory_free(block)
|
||||
}
|
||||
|
||||
platform_memory_protect :: proc(memory: rawptr, size: int) {
|
||||
platform_memory_init()
|
||||
_platform_memory_protect(memory, size)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
//+build linux
|
||||
//+private
|
||||
package mem_virtual
|
||||
|
||||
import "core:c"
|
||||
import "core:intrinsics"
|
||||
|
||||
when ODIN_ARCH == "amd64" {
|
||||
SYS_mmap :: 9
|
||||
SYS_mprotect :: 10
|
||||
SYS_munmap :: 11
|
||||
SYS_madvise :: 28
|
||||
|
||||
PROT_NONE :: 0x0
|
||||
PROT_READ :: 0x1
|
||||
PROT_WRITE :: 0x2
|
||||
PROT_EXEC :: 0x4
|
||||
PROT_GROWSDOWN :: 0x01000000
|
||||
PROT_GROWSUP :: 0x02000000
|
||||
|
||||
MAP_FIXED :: 0x1
|
||||
MAP_PRIVATE :: 0x2
|
||||
MAP_SHARED :: 0x4
|
||||
MAP_ANONYMOUS :: 0x20
|
||||
|
||||
MADV_NORMAL :: 0
|
||||
MADV_RANDOM :: 1
|
||||
MADV_SEQUENTIAL :: 2
|
||||
MADV_WILLNEED :: 3
|
||||
MADV_DONTNEED :: 4
|
||||
MADV_FREE :: 8
|
||||
MADV_REMOVE :: 9
|
||||
MADV_DONTFORK :: 10
|
||||
MADV_DOFORK :: 11
|
||||
MADV_MERGEABLE :: 12
|
||||
MADV_UNMERGEABLE :: 13
|
||||
MADV_HUGEPAGE :: 14
|
||||
MADV_NOHUGEPAGE :: 15
|
||||
MADV_DONTDUMP :: 16
|
||||
MADV_DODUMP :: 17
|
||||
MADV_WIPEONFORK :: 18
|
||||
MADV_KEEPONFORK :: 19
|
||||
MADV_HWPOISON :: 100
|
||||
} else {
|
||||
#panic("Unsupported architecture")
|
||||
}
|
||||
|
||||
mmap :: proc "contextless" (addr: rawptr, length: uint, prot: c.int, flags: c.int, fd: c.int, offset: uintptr) -> rawptr {
|
||||
res := intrinsics.syscall(SYS_mmap, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), offset)
|
||||
return rawptr(res)
|
||||
}
|
||||
|
||||
munmap :: proc "contextless" (addr: rawptr, length: uint) -> c.int {
|
||||
res := intrinsics.syscall(SYS_munmap, uintptr(addr), uintptr(length))
|
||||
return c.int(res)
|
||||
}
|
||||
|
||||
mprotect :: proc "contextless" (addr: rawptr, length: uint, prot: c.int) -> c.int {
|
||||
res := intrinsics.syscall(SYS_mprotect, uintptr(addr), uintptr(length), uint(prot))
|
||||
return c.int(res)
|
||||
}
|
||||
|
||||
madvise :: proc "contextless" (addr: rawptr, length: uint, advice: c.int) -> c.int {
|
||||
res := intrinsics.syscall(SYS_madvise, uintptr(addr), uintptr(length), uintptr(advice))
|
||||
return c.int(res)
|
||||
}
|
||||
|
||||
|
||||
_reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
|
||||
MAP_FAILED := rawptr(~uintptr(0))
|
||||
result := mmap(nil, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
|
||||
if result == MAP_FAILED {
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
return ([^]byte)(result)[:size], nil
|
||||
}
|
||||
|
||||
_commit :: proc(data: rawptr, size: uint) -> Allocator_Error {
|
||||
result := mprotect(data, size, PROT_READ|PROT_WRITE)
|
||||
if result != 0 {
|
||||
// TODO(bill): Handle error value correctly
|
||||
return .Out_Of_Memory
|
||||
}
|
||||
return nil
|
||||
}
|
||||
_decommit :: proc(data: rawptr, size: uint) {
|
||||
mprotect(data, size, PROT_NONE)
|
||||
madvise(data, size, MADV_FREE)
|
||||
}
|
||||
_release :: proc(data: rawptr, size: uint) {
|
||||
munmap(data, size)
|
||||
}
|
||||
_protect :: proc(data: rawptr, size: uint, flags: Protect_Flags) -> bool {
|
||||
pflags: c.int
|
||||
pflags = PROT_NONE
|
||||
if .Read in flags { pflags |= PROT_READ }
|
||||
if .Write in flags { pflags |= PROT_WRITE }
|
||||
if .Execute in flags { pflags |= PROT_EXEC }
|
||||
err := mprotect(data, size, pflags)
|
||||
return err != 0
|
||||
}
|
||||
|
||||
|
||||
|
||||
_platform_memory_init :: proc() {
|
||||
DEFAULT_PAGE_SIZE = 4096
|
||||
|
||||
// is power of two
|
||||
assert(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0)
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
//+private
|
||||
package mem_virtual
|
||||
|
||||
import sync "core:sync/sync2"
|
||||
|
||||
Platform_Memory_Block :: struct {
|
||||
block: Memory_Block,
|
||||
reserved: uint,
|
||||
prev, next: ^Platform_Memory_Block,
|
||||
}
|
||||
|
||||
platform_memory_alloc :: proc(to_commit, to_reserve: uint) -> (block: ^Platform_Memory_Block, err: Allocator_Error) {
|
||||
to_commit, to_reserve := to_commit, to_reserve
|
||||
to_reserve = max(to_commit, to_reserve)
|
||||
|
||||
total_to_reserved := max(to_reserve, size_of(Platform_Memory_Block))
|
||||
to_commit = clamp(to_commit, size_of(Platform_Memory_Block), total_to_reserved)
|
||||
|
||||
data := reserve(total_to_reserved) or_return
|
||||
commit(raw_data(data), to_commit)
|
||||
|
||||
block = (^Platform_Memory_Block)(raw_data(data))
|
||||
block.reserved = to_reserve
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
platform_memory_free :: proc(block: ^Platform_Memory_Block) {
|
||||
if block != nil {
|
||||
release(block, block.reserved)
|
||||
}
|
||||
}
|
||||
|
||||
platform_mutex_lock :: proc() {
|
||||
sync.mutex_lock(&global_memory_block_mutex)
|
||||
}
|
||||
|
||||
platform_mutex_unlock :: proc() {
|
||||
sync.mutex_unlock(&global_memory_block_mutex)
|
||||
}
|
||||
|
||||
global_memory_block_mutex: sync.Mutex
|
||||
global_platform_memory_block_sentinel: Platform_Memory_Block
|
||||
global_platform_memory_block_sentinel_set: bool
|
||||
|
||||
@(init)
|
||||
platform_memory_init :: proc() {
|
||||
if !global_platform_memory_block_sentinel_set {
|
||||
_platform_memory_init()
|
||||
global_platform_memory_block_sentinel.prev = &global_platform_memory_block_sentinel
|
||||
global_platform_memory_block_sentinel.next = &global_platform_memory_block_sentinel
|
||||
global_platform_memory_block_sentinel_set = true
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,6 @@
|
||||
//+private
|
||||
package mem_virtual
|
||||
|
||||
import "core:mem"
|
||||
|
||||
foreign import Kernel32 "system:Kernel32.lib"
|
||||
|
||||
LPSYSTEM_INFO :: ^SYSTEM_INFO
|
||||
@@ -52,36 +50,76 @@ PAGE_WRITECOPY :: 0x08
|
||||
PAGE_TARGETS_INVALID :: 0x40000000
|
||||
PAGE_TARGETS_NO_UPDATE :: 0x40000000
|
||||
|
||||
ERROR_INVALID_ADDRESS :: 487
|
||||
|
||||
@(default_calling_convention="stdcall")
|
||||
foreign Kernel32 {
|
||||
GetSystemInfo :: proc(lpSystemInfo: LPSYSTEM_INFO) ---
|
||||
VirtualAlloc :: proc(lpAddress: rawptr, dwSize: uint, flAllocationType: u32, flProtect: u32) -> rawptr ---
|
||||
VirtualFree :: proc(lpAddress: rawptr, dwSize: uint, dwFreeType: u32) -> b32 ---
|
||||
VirtualProtect :: proc(lpAddress: rawptr, dwSize: uint, flNewProtect: u32, lpflOldProtect: ^u32) -> b32 ---
|
||||
GetLastError :: proc() -> u32 ---
|
||||
}
|
||||
|
||||
|
||||
_reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
|
||||
result := VirtualAlloc(nil, size, MEM_RESERVE, PAGE_READWRITE)
|
||||
if result == nil {
|
||||
err = .Out_Of_Memory
|
||||
return
|
||||
}
|
||||
data = ([^]byte)(result)[:size]
|
||||
return
|
||||
}
|
||||
|
||||
_commit :: proc(data: rawptr, size: uint) -> Allocator_Error {
|
||||
result := VirtualAlloc(data, size, MEM_COMMIT, PAGE_READWRITE)
|
||||
if result == nil {
|
||||
switch err := GetLastError(); err {
|
||||
case ERROR_INVALID_ADDRESS:
|
||||
return .Out_Of_Memory
|
||||
}
|
||||
|
||||
// TODO(bill): Handle errors correctly
|
||||
return .Invalid_Argument
|
||||
}
|
||||
return nil
|
||||
}
|
||||
_decommit :: proc(data: rawptr, size: uint) {
|
||||
VirtualFree(data, size, MEM_DECOMMIT)
|
||||
}
|
||||
_release :: proc(data: rawptr, size: uint) {
|
||||
VirtualFree(data, 0, MEM_RELEASE)
|
||||
}
|
||||
_protect :: proc(data: rawptr, size: uint, flags: Protect_Flags) -> bool {
|
||||
pflags: u32
|
||||
pflags = PAGE_NOACCESS
|
||||
switch flags {
|
||||
case {}: pflags = PAGE_NOACCESS
|
||||
case {.Read}: pflags = PAGE_READONLY
|
||||
case {.Read, .Write}: pflags = PAGE_READWRITE
|
||||
case {.Write}: pflags = PAGE_WRITECOPY
|
||||
case {.Execute}: pflags = PAGE_EXECUTE
|
||||
case {.Execute, .Read}: pflags = PAGE_EXECUTE_READ
|
||||
case {.Execute, .Read, .Write}: pflags = PAGE_EXECUTE_READWRITE
|
||||
case {.Execute, .Write}: pflags = PAGE_EXECUTE_WRITECOPY
|
||||
case:
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
old_protect: u32
|
||||
ok := VirtualProtect(data, size, pflags, &old_protect)
|
||||
return bool(ok)
|
||||
}
|
||||
|
||||
|
||||
|
||||
_platform_memory_init :: proc() {
|
||||
sys_info: SYSTEM_INFO
|
||||
GetSystemInfo(&sys_info)
|
||||
DEFAULT_PAGE_SIZE = max(DEFAULT_PAGE_SIZE, int(sys_info.dwPageSize))
|
||||
assert(mem.is_power_of_two(uintptr(DEFAULT_PAGE_SIZE)))
|
||||
DEFAULT_PAGE_SIZE = max(DEFAULT_PAGE_SIZE, uint(sys_info.dwPageSize))
|
||||
|
||||
// is power of two
|
||||
assert(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0)
|
||||
}
|
||||
|
||||
_platform_memory_alloc :: proc(total_size: int) -> (pmblock: ^Platform_Memory_Block, err: mem.Allocator_Error) {
|
||||
pmblock = (^Platform_Memory_Block)(VirtualAlloc(nil, uint(total_size), MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE))
|
||||
if pmblock == nil {
|
||||
err = .Out_Of_Memory
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
_platform_memory_free :: proc(block: ^Platform_Memory_Block) {
|
||||
VirtualFree(block, 0, MEM_RELEASE)
|
||||
}
|
||||
|
||||
_platform_memory_protect :: proc(memory: rawptr, size: int) -> bool {
|
||||
old_protect: u32
|
||||
ok := VirtualProtect(memory, uint(size), PAGE_NOACCESS, &old_protect)
|
||||
return bool(ok)
|
||||
}
|
||||
@@ -224,6 +224,15 @@ Slice_Expr :: struct {
|
||||
close: tokenizer.Pos,
|
||||
}
|
||||
|
||||
Matrix_Index_Expr :: struct {
|
||||
using node: Expr,
|
||||
expr: ^Expr,
|
||||
open: tokenizer.Pos,
|
||||
row_index: ^Expr,
|
||||
column_index: ^Expr,
|
||||
close: tokenizer.Pos,
|
||||
}
|
||||
|
||||
Call_Expr :: struct {
|
||||
using node: Expr,
|
||||
inlining: Proc_Inlining,
|
||||
@@ -739,3 +748,11 @@ Relative_Type :: struct {
|
||||
tag: ^Expr,
|
||||
type: ^Expr,
|
||||
}
|
||||
|
||||
Matrix_Type :: struct {
|
||||
using node: Expr,
|
||||
tok_pos: tokenizer.Pos,
|
||||
row_count: ^Expr,
|
||||
column_count: ^Expr,
|
||||
elem: ^Expr,
|
||||
}
|
||||
@@ -117,6 +117,10 @@ clone_node :: proc(node: ^Node) -> ^Node {
|
||||
case Index_Expr:
|
||||
r.expr = clone(r.expr)
|
||||
r.index = clone(r.index)
|
||||
case Matrix_Index_Expr:
|
||||
r.expr = clone(r.expr)
|
||||
r.row_index = clone(r.row_index)
|
||||
r.column_index = clone(r.column_index)
|
||||
case Deref_Expr:
|
||||
r.expr = clone(r.expr)
|
||||
case Slice_Expr:
|
||||
@@ -275,7 +279,10 @@ clone_node :: proc(node: ^Node) -> ^Node {
|
||||
case Map_Type:
|
||||
r.key = clone(r.key)
|
||||
r.value = clone(r.value)
|
||||
|
||||
case Matrix_Type:
|
||||
r.row_count = clone(r.row_count)
|
||||
r.column_count = clone(r.column_count)
|
||||
r.elem = clone(r.elem)
|
||||
case:
|
||||
fmt.panicf("Unhandled node kind: %T", r)
|
||||
}
|
||||
|
||||
@@ -110,6 +110,10 @@ walk :: proc(v: ^Visitor, node: ^Node) {
|
||||
case Index_Expr:
|
||||
walk(v, n.expr)
|
||||
walk(v, n.index)
|
||||
case Matrix_Index_Expr:
|
||||
walk(v, n.expr)
|
||||
walk(v, n.row_index)
|
||||
walk(v, n.column_index)
|
||||
case Deref_Expr:
|
||||
walk(v, n.expr)
|
||||
case Slice_Expr:
|
||||
@@ -398,6 +402,10 @@ walk :: proc(v: ^Visitor, node: ^Node) {
|
||||
case Relative_Type:
|
||||
walk(v, n.tag)
|
||||
walk(v, n.type)
|
||||
case Matrix_Type:
|
||||
walk(v, n.row_count)
|
||||
walk(v, n.column_count)
|
||||
walk(v, n.elem)
|
||||
|
||||
case:
|
||||
fmt.panicf("ast.walk: unexpected node type %T", n)
|
||||
|
||||
@@ -10,7 +10,7 @@ Array :: struct($T: typeid) {
|
||||
String :: distinct Array(byte)
|
||||
|
||||
Version_Type_Major :: 0
|
||||
Version_Type_Minor :: 1
|
||||
Version_Type_Minor :: 2
|
||||
Version_Type_Patch :: 0
|
||||
|
||||
Version_Type :: struct {
|
||||
@@ -101,17 +101,19 @@ Entity_Flag :: enum u32le {
|
||||
Param_Ellipsis = 5, // Variadic parameter
|
||||
Param_CVararg = 6, // #c_vararg
|
||||
Param_No_Alias = 7, // #no_alias
|
||||
Param_Any_Int = 8, // #any_int
|
||||
|
||||
Type_Alias = 8,
|
||||
Type_Alias = 20,
|
||||
|
||||
Var_Thread_Local = 9,
|
||||
Var_Static = 10,
|
||||
Var_Thread_Local = 40,
|
||||
Var_Static = 41,
|
||||
}
|
||||
|
||||
Entity_Flags :: distinct bit_set[Entity_Flag; u32le]
|
||||
Entity_Flags :: distinct bit_set[Entity_Flag; u64le]
|
||||
|
||||
Entity :: struct {
|
||||
kind: Entity_Kind,
|
||||
_: u32le, // reserved
|
||||
flags: Entity_Flags,
|
||||
pos: Position,
|
||||
name: String,
|
||||
@@ -167,6 +169,7 @@ Type_Kind :: enum u32le {
|
||||
Relative_Pointer = 20,
|
||||
Relative_Slice = 21,
|
||||
Multi_Pointer = 22,
|
||||
Matrix = 23,
|
||||
}
|
||||
|
||||
Type_Elems_Cap :: 4
|
||||
@@ -192,11 +195,12 @@ Type :: struct {
|
||||
custom_align: String,
|
||||
|
||||
// Used by:
|
||||
// .Array - 1 count: 0=len
|
||||
// .Array - 1 count: 0=len
|
||||
// .Enumerated_Array - 1 count: 0=len
|
||||
// .SOA_Struct_Fixed - 1 count: 0=len
|
||||
// .Bit_Set - 2 count: 0=lower, 1=upper
|
||||
// .Simd_Vector - 1 count: 0=len
|
||||
// .Bit_Set - 2 count: 0=lower, 1=upper
|
||||
// .Simd_Vector - 1 count: 0=len
|
||||
// .Matrix - 2 count: 0=row_count, 1=column_count
|
||||
elem_count_len: u32le,
|
||||
elem_counts: [Type_Elems_Cap]i64le,
|
||||
|
||||
@@ -224,6 +228,7 @@ Type :: struct {
|
||||
// .Relative_Pointer - 2 types: 0=pointer type, 1=base integer
|
||||
// .Relative_Slice - 2 types: 0=slice type, 1=base integer
|
||||
// .Multi_Pointer - 1 type: 0=element
|
||||
// .Matrix - 1 type: 0=element
|
||||
types: Array(Type_Index),
|
||||
|
||||
// Used by:
|
||||
|
||||
@@ -2703,6 +2703,22 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
bst.underlying = underlying
|
||||
bst.close = close.pos
|
||||
return bst
|
||||
|
||||
case .Matrix:
|
||||
tok := expect_token(p, .Matrix)
|
||||
expect_token(p, .Open_Bracket)
|
||||
row_count := parse_expr(p, false)
|
||||
expect_token(p, .Comma)
|
||||
column_count := parse_expr(p, false)
|
||||
expect_token(p, .Close_Bracket)
|
||||
elem := parse_type(p)
|
||||
|
||||
mt := ast.new(ast.Matrix_Type, tok.pos, elem.end)
|
||||
mt.tok_pos = tok.pos
|
||||
mt.row_count = row_count
|
||||
mt.column_count = column_count
|
||||
mt.elem = elem
|
||||
return mt
|
||||
|
||||
case .Asm:
|
||||
tok := expect_token(p, .Asm)
|
||||
@@ -2969,7 +2985,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
|
||||
defer p.allow_range = prev_allow_range
|
||||
p.allow_range = false
|
||||
|
||||
indicies: [2]^ast.Expr
|
||||
indices: [2]^ast.Expr
|
||||
interval: tokenizer.Token
|
||||
is_slice_op := false
|
||||
|
||||
@@ -2981,18 +2997,18 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
|
||||
// NOTE(bill): Do not err yet
|
||||
break
|
||||
case:
|
||||
indicies[0] = parse_expr(p, false)
|
||||
indices[0] = parse_expr(p, false)
|
||||
}
|
||||
|
||||
#partial switch p.curr_tok.kind {
|
||||
case .Ellipsis, .Range_Half, .Range_Full:
|
||||
error(p, p.curr_tok.pos, "expected a colon, not a range")
|
||||
fallthrough
|
||||
case .Colon:
|
||||
case .Colon, .Comma/*matrix index*/:
|
||||
interval = advance_token(p)
|
||||
is_slice_op = true
|
||||
if p.curr_tok.kind != .Close_Bracket && p.curr_tok.kind != .EOF {
|
||||
indicies[1] = parse_expr(p, false)
|
||||
indices[1] = parse_expr(p, false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3000,20 +3016,34 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
|
||||
p.expr_level -= 1
|
||||
|
||||
if is_slice_op {
|
||||
se := ast.new(ast.Slice_Expr, operand.pos, end_pos(close))
|
||||
se.expr = operand
|
||||
se.open = open.pos
|
||||
se.low = indicies[0]
|
||||
se.interval = interval
|
||||
se.high = indicies[1]
|
||||
se.close = close.pos
|
||||
if interval.kind == .Comma {
|
||||
if indices[0] == nil || indices[1] == nil {
|
||||
error(p, p.curr_tok.pos, "matrix index expressions require both row and column indices")
|
||||
}
|
||||
se := ast.new(ast.Matrix_Index_Expr, operand.pos, end_pos(close))
|
||||
se.expr = operand
|
||||
se.open = open.pos
|
||||
se.row_index = indices[0]
|
||||
se.column_index = indices[1]
|
||||
se.close = close.pos
|
||||
|
||||
operand = se
|
||||
operand = se
|
||||
} else {
|
||||
se := ast.new(ast.Slice_Expr, operand.pos, end_pos(close))
|
||||
se.expr = operand
|
||||
se.open = open.pos
|
||||
se.low = indices[0]
|
||||
se.interval = interval
|
||||
se.high = indices[1]
|
||||
se.close = close.pos
|
||||
|
||||
operand = se
|
||||
}
|
||||
} else {
|
||||
ie := ast.new(ast.Index_Expr, operand.pos, end_pos(close))
|
||||
ie.expr = operand
|
||||
ie.open = open.pos
|
||||
ie.index = indicies[0]
|
||||
ie.index = indices[0]
|
||||
ie.close = close.pos
|
||||
|
||||
operand = ie
|
||||
|
||||
@@ -150,6 +150,7 @@ Token_Kind :: enum u32 {
|
||||
Asm, // asm
|
||||
Inline, // inline
|
||||
No_Inline, // no_inline
|
||||
Matrix, // matrix
|
||||
B_Keyword_End,
|
||||
|
||||
COUNT,
|
||||
@@ -280,6 +281,7 @@ tokens := [Token_Kind.COUNT]string {
|
||||
"asm",
|
||||
"inline",
|
||||
"no_inline",
|
||||
"matrix",
|
||||
"",
|
||||
}
|
||||
|
||||
@@ -299,10 +301,10 @@ token_to_string :: proc(tok: Token) -> string {
|
||||
}
|
||||
|
||||
to_string :: proc(kind: Token_Kind) -> string {
|
||||
if Token_Kind.Invalid <= kind && kind < Token_Kind.COUNT {
|
||||
if .Invalid <= kind && kind < .COUNT {
|
||||
return tokens[kind]
|
||||
}
|
||||
if Token_Kind.B_Custom_Keyword_Begin < kind {
|
||||
if .B_Custom_Keyword_Begin < kind {
|
||||
n := int(u16(kind)-u16(Token_Kind.B_Custom_Keyword_Begin))
|
||||
if n < len(custom_keyword_tokens) {
|
||||
return custom_keyword_tokens[n]
|
||||
@@ -313,7 +315,7 @@ to_string :: proc(kind: Token_Kind) -> string {
|
||||
}
|
||||
|
||||
is_literal :: proc(kind: Token_Kind) -> bool {
|
||||
return Token_Kind.B_Literal_Begin < kind && kind < Token_Kind.B_Literal_End
|
||||
return .B_Literal_Begin < kind && kind < .B_Literal_End
|
||||
}
|
||||
is_operator :: proc(kind: Token_Kind) -> bool {
|
||||
#partial switch kind {
|
||||
@@ -327,13 +329,13 @@ is_operator :: proc(kind: Token_Kind) -> bool {
|
||||
return false
|
||||
}
|
||||
is_assignment_operator :: proc(kind: Token_Kind) -> bool {
|
||||
return Token_Kind.B_Assign_Op_Begin < kind && kind < Token_Kind.B_Assign_Op_End || kind == Token_Kind.Eq
|
||||
return .B_Assign_Op_Begin < kind && kind < .B_Assign_Op_End || kind == .Eq
|
||||
}
|
||||
is_keyword :: proc(kind: Token_Kind) -> bool {
|
||||
switch {
|
||||
case Token_Kind.B_Keyword_Begin < kind && kind < Token_Kind.B_Keyword_End:
|
||||
case .B_Keyword_Begin < kind && kind < .B_Keyword_End:
|
||||
return true
|
||||
case Token_Kind.B_Custom_Keyword_Begin < kind:
|
||||
case .B_Custom_Keyword_Begin < kind:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
+30
-18
@@ -70,7 +70,7 @@ file_size_from_path :: proc(path: string) -> i64 {
|
||||
return length
|
||||
}
|
||||
|
||||
read_entire_file :: proc(name: string, allocator := context.allocator) -> (data: []byte, success: bool) {
|
||||
read_entire_file_from_filename :: proc(name: string, allocator := context.allocator) -> (data: []byte, success: bool) {
|
||||
context.allocator = allocator
|
||||
|
||||
fd, err := open(name, O_RDONLY, 0)
|
||||
@@ -79,26 +79,38 @@ read_entire_file :: proc(name: string, allocator := context.allocator) -> (data:
|
||||
}
|
||||
defer close(fd)
|
||||
|
||||
length: i64
|
||||
if length, err = file_size(fd); err != 0 {
|
||||
return nil, false
|
||||
}
|
||||
return read_entire_file_from_handle(fd, allocator)
|
||||
}
|
||||
|
||||
if length <= 0 {
|
||||
return nil, true
|
||||
}
|
||||
read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator) -> (data: []byte, success: bool) {
|
||||
context.allocator = allocator
|
||||
|
||||
data = make([]byte, int(length))
|
||||
if data == nil {
|
||||
return nil, false
|
||||
}
|
||||
length: i64
|
||||
err: Errno
|
||||
if length, err = file_size(fd); err != 0 {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
bytes_read, read_err := read(fd, data)
|
||||
if read_err != ERROR_NONE {
|
||||
delete(data)
|
||||
return nil, false
|
||||
}
|
||||
return data[:bytes_read], true
|
||||
if length <= 0 {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
data = make([]byte, int(length), allocator)
|
||||
if data == nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
bytes_read, read_err := read(fd, data)
|
||||
if read_err != ERROR_NONE {
|
||||
delete(data)
|
||||
return nil, false
|
||||
}
|
||||
return data[:bytes_read], true
|
||||
}
|
||||
|
||||
read_entire_file :: proc {
|
||||
read_entire_file_from_filename,
|
||||
read_entire_file_from_handle,
|
||||
}
|
||||
|
||||
write_entire_file :: proc(name: string, data: []byte, truncate := true) -> (success: bool) {
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
package os
|
||||
|
||||
Handle :: distinct i32;
|
||||
Errno :: distinct i32;
|
||||
|
||||
ERROR_NONE :: Errno(0);
|
||||
|
||||
O_RDONLY :: 0x00000;
|
||||
O_WRONLY :: 0x00001;
|
||||
O_RDWR :: 0x00002;
|
||||
O_CREATE :: 0x00040;
|
||||
O_EXCL :: 0x00080;
|
||||
O_NOCTTY :: 0x00100;
|
||||
O_TRUNC :: 0x00200;
|
||||
O_NONBLOCK :: 0x00800;
|
||||
O_APPEND :: 0x00400;
|
||||
O_SYNC :: 0x01000;
|
||||
O_ASYNC :: 0x02000;
|
||||
O_CLOEXEC :: 0x80000;
|
||||
|
||||
stdout: Handle;
|
||||
stderr: Handle;
|
||||
stdin: Handle;
|
||||
|
||||
|
||||
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
return 0, 0;
|
||||
}
|
||||
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
return 0, 0;
|
||||
}
|
||||
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) {
|
||||
return 0, 0;
|
||||
}
|
||||
close :: proc(fd: Handle) -> Errno {
|
||||
return 0;
|
||||
}
|
||||
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
||||
return 0, 0;
|
||||
}
|
||||
current_thread_id :: proc "contextless" () -> int {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
file_size :: proc(fd: Handle) -> (i64, Errno) {
|
||||
return 0, 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
return nil;
|
||||
}
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
if new_size == 0 {
|
||||
heap_free(ptr);
|
||||
return nil;
|
||||
}
|
||||
if ptr == nil {
|
||||
return heap_alloc(new_size);
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
heap_free :: proc(ptr: rawptr) {
|
||||
if ptr == nil {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package os
|
||||
|
||||
import "core:sys/wasm/wasi"
|
||||
|
||||
Handle :: distinct i32
|
||||
Errno :: distinct i32
|
||||
|
||||
ERROR_NONE :: Errno(wasi.errno_t.SUCCESS)
|
||||
|
||||
O_RDONLY :: 0x00000
|
||||
O_WRONLY :: 0x00001
|
||||
O_RDWR :: 0x00002
|
||||
O_CREATE :: 0x00040
|
||||
O_EXCL :: 0x00080
|
||||
O_NOCTTY :: 0x00100
|
||||
O_TRUNC :: 0x00200
|
||||
O_NONBLOCK :: 0x00800
|
||||
O_APPEND :: 0x00400
|
||||
O_SYNC :: 0x01000
|
||||
O_ASYNC :: 0x02000
|
||||
O_CLOEXEC :: 0x80000
|
||||
|
||||
stdin: Handle = 0
|
||||
stdout: Handle = 1
|
||||
stderr: Handle = 2
|
||||
|
||||
|
||||
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
iovs := wasi.ciovec_t(data)
|
||||
n, err := wasi.fd_write(wasi.fd_t(fd), {iovs})
|
||||
return int(n), Errno(err)
|
||||
}
|
||||
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
iovs := wasi.iovec_t(data)
|
||||
n, err := wasi.fd_read(wasi.fd_t(fd), {iovs})
|
||||
return int(n), Errno(err)
|
||||
}
|
||||
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
|
||||
iovs := wasi.ciovec_t(data)
|
||||
n, err := wasi.fd_pwrite(wasi.fd_t(fd), {iovs}, wasi.filesize_t(offset))
|
||||
return int(n), Errno(err)
|
||||
}
|
||||
read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
|
||||
iovs := wasi.iovec_t(data)
|
||||
n, err := wasi.fd_pread(wasi.fd_t(fd), {iovs}, wasi.filesize_t(offset))
|
||||
return int(n), Errno(err)
|
||||
}
|
||||
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) {
|
||||
return 0, -1
|
||||
}
|
||||
close :: proc(fd: Handle) -> Errno {
|
||||
err := wasi.fd_close(wasi.fd_t(fd))
|
||||
return Errno(err)
|
||||
}
|
||||
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
||||
n, err := wasi.fd_seek(wasi.fd_t(fd), wasi.filedelta_t(offset), wasi.whence_t(whence))
|
||||
return i64(n), Errno(err)
|
||||
}
|
||||
current_thread_id :: proc "contextless" () -> int {
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
file_size :: proc(fd: Handle) -> (i64, Errno) {
|
||||
stat, err := wasi.fd_filestat_get(wasi.fd_t(fd))
|
||||
if err != nil {
|
||||
return 0, Errno(err)
|
||||
}
|
||||
return i64(stat.size), 0
|
||||
}
|
||||
|
||||
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
return nil
|
||||
}
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
if new_size == 0 {
|
||||
heap_free(ptr)
|
||||
return nil
|
||||
}
|
||||
if ptr == nil {
|
||||
return heap_alloc(new_size)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
heap_free :: proc(ptr: rawptr) {
|
||||
if ptr == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
exit :: proc "contextless" (code: int) -> ! {
|
||||
wasi.proc_exit(wasi.exitcode_t(code))
|
||||
}
|
||||
+2
-2
@@ -19,7 +19,7 @@ _file_stream_vtable := &io.Stream_VTable{
|
||||
return
|
||||
},
|
||||
impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
|
||||
when ODIN_OS == "windows" {
|
||||
when ODIN_OS == "windows" || ODIN_OS == "wasi" {
|
||||
fd := Handle(uintptr(s.stream_data))
|
||||
os_err: Errno
|
||||
n, os_err = read_at(fd, p, offset)
|
||||
@@ -33,7 +33,7 @@ _file_stream_vtable := &io.Stream_VTable{
|
||||
return
|
||||
},
|
||||
impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
|
||||
when ODIN_OS == "windows" {
|
||||
when ODIN_OS == "windows" || ODIN_OS == "wasi" {
|
||||
fd := Handle(uintptr(s.stream_data))
|
||||
os_err: Errno
|
||||
n, os_err = write_at(fd, p, offset)
|
||||
|
||||
@@ -33,6 +33,7 @@ Type_Info_Bit_Set :: runtime.Type_Info_Bit_Set
|
||||
Type_Info_Simd_Vector :: runtime.Type_Info_Simd_Vector
|
||||
Type_Info_Relative_Pointer :: runtime.Type_Info_Relative_Pointer
|
||||
Type_Info_Relative_Slice :: runtime.Type_Info_Relative_Slice
|
||||
Type_Info_Matrix :: runtime.Type_Info_Matrix
|
||||
|
||||
Type_Info_Enum_Value :: runtime.Type_Info_Enum_Value
|
||||
|
||||
@@ -66,6 +67,7 @@ Type_Kind :: enum {
|
||||
Simd_Vector,
|
||||
Relative_Pointer,
|
||||
Relative_Slice,
|
||||
Matrix,
|
||||
}
|
||||
|
||||
|
||||
@@ -99,6 +101,7 @@ type_kind :: proc(T: typeid) -> Type_Kind {
|
||||
case Type_Info_Simd_Vector: return .Simd_Vector
|
||||
case Type_Info_Relative_Pointer: return .Relative_Pointer
|
||||
case Type_Info_Relative_Slice: return .Relative_Slice
|
||||
case Type_Info_Matrix: return .Matrix
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1401,7 +1404,8 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
|
||||
Type_Info_Bit_Set,
|
||||
Type_Info_Enum,
|
||||
Type_Info_Simd_Vector,
|
||||
Type_Info_Relative_Pointer:
|
||||
Type_Info_Relative_Pointer,
|
||||
Type_Info_Matrix:
|
||||
return mem.compare_byte_ptrs((^byte)(a.data), (^byte)(b.data), t.size) == 0
|
||||
|
||||
case Type_Info_String:
|
||||
|
||||
@@ -164,6 +164,12 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
|
||||
case Type_Info_Relative_Slice:
|
||||
y := b.variant.(Type_Info_Relative_Slice) or_return
|
||||
return x.base_integer == y.base_integer && x.slice == y.slice
|
||||
|
||||
case Type_Info_Matrix:
|
||||
y := b.variant.(Type_Info_Matrix) or_return
|
||||
if x.row_count != y.row_count { return false }
|
||||
if x.column_count != y.column_count { return false }
|
||||
return are_types_identical(x.elem, y.elem)
|
||||
}
|
||||
|
||||
return false
|
||||
@@ -584,6 +590,14 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) -
|
||||
write_type(w, info.base_integer, &n) or_return
|
||||
io.write_string(w, ") ", &n) or_return
|
||||
write_type(w, info.slice, &n) or_return
|
||||
|
||||
case Type_Info_Matrix:
|
||||
io.write_string(w, "matrix[", &n) or_return
|
||||
io.write_i64(w, i64(info.row_count), 10, &n) or_return
|
||||
io.write_string(w, ", ", &n) or_return
|
||||
io.write_i64(w, i64(info.column_count), 10, &n) or_return
|
||||
io.write_string(w, "]", &n) or_return
|
||||
write_type(w, info.elem, &n) or_return
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
+16
-1
@@ -162,6 +162,14 @@ Type_Info_Relative_Slice :: struct {
|
||||
slice: ^Type_Info,
|
||||
base_integer: ^Type_Info,
|
||||
}
|
||||
Type_Info_Matrix :: struct {
|
||||
elem: ^Type_Info,
|
||||
elem_size: int,
|
||||
elem_stride: int, // elem_stride >= row_count
|
||||
row_count: int,
|
||||
column_count: int,
|
||||
// Total element count = column_count * elem_stride
|
||||
}
|
||||
|
||||
Type_Info_Flag :: enum u8 {
|
||||
Comparable = 0,
|
||||
@@ -202,6 +210,7 @@ Type_Info :: struct {
|
||||
Type_Info_Simd_Vector,
|
||||
Type_Info_Relative_Pointer,
|
||||
Type_Info_Relative_Slice,
|
||||
Type_Info_Matrix,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -233,6 +242,7 @@ Typeid_Kind :: enum u8 {
|
||||
Simd_Vector,
|
||||
Relative_Pointer,
|
||||
Relative_Slice,
|
||||
Matrix,
|
||||
}
|
||||
#assert(len(Typeid_Kind) < 32)
|
||||
|
||||
@@ -384,7 +394,12 @@ Raw_Cstring :: struct {
|
||||
// This is probably only useful for freestanding targets
|
||||
foreign {
|
||||
@(link_name="__$startup_runtime")
|
||||
_startup_runtime :: proc "contextless" () ---
|
||||
_startup_runtime :: proc() ---
|
||||
}
|
||||
|
||||
@(link_name="__$cleanup_runtime")
|
||||
_cleanup_runtime :: proc() {
|
||||
default_temp_allocator_destroy(&global_default_temp_allocator_data)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,274 @@
|
||||
package runtime
|
||||
|
||||
import "core:intrinsics"
|
||||
_ :: intrinsics
|
||||
|
||||
|
||||
@(builtin)
|
||||
determinant :: proc{
|
||||
matrix1x1_determinant,
|
||||
matrix2x2_determinant,
|
||||
matrix3x3_determinant,
|
||||
matrix4x4_determinant,
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
adjugate :: proc{
|
||||
matrix1x1_adjugate,
|
||||
matrix2x2_adjugate,
|
||||
matrix3x3_adjugate,
|
||||
matrix4x4_adjugate,
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
inverse_transpose :: proc{
|
||||
matrix1x1_inverse_transpose,
|
||||
matrix2x2_inverse_transpose,
|
||||
matrix3x3_inverse_transpose,
|
||||
matrix4x4_inverse_transpose,
|
||||
}
|
||||
|
||||
|
||||
@(builtin)
|
||||
inverse :: proc{
|
||||
matrix1x1_inverse,
|
||||
matrix2x2_inverse,
|
||||
matrix3x3_inverse,
|
||||
matrix4x4_inverse,
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
hermitian_adjoint :: proc(m: $M/matrix[$N, N]$T) -> M where intrinsics.type_is_complex(T), N >= 1 {
|
||||
return conj(transpose(m))
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
matrix_trace :: proc(m: $M/matrix[$N, N]$T) -> (trace: T) {
|
||||
for i in 0..<N {
|
||||
trace += m[i, i]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
matrix_minor :: proc(m: $M/matrix[$N, N]$T, row, column: int) -> (minor: T) where N > 1 {
|
||||
K :: N-1
|
||||
cut_down: matrix[K, K]T
|
||||
for col_idx in 0..<K {
|
||||
j := col_idx + int(col_idx >= column)
|
||||
for row_idx in 0..<K {
|
||||
i := row_idx + int(row_idx >= row)
|
||||
cut_down[row_idx, col_idx] = m[i, j]
|
||||
}
|
||||
}
|
||||
return determinant(cut_down)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@(builtin)
|
||||
matrix1x1_determinant :: proc(m: $M/matrix[1, 1]$T) -> (det: T) {
|
||||
return m[0, 0]
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
matrix2x2_determinant :: proc(m: $M/matrix[2, 2]$T) -> (det: T) {
|
||||
return m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
|
||||
}
|
||||
@(builtin)
|
||||
matrix3x3_determinant :: proc(m: $M/matrix[3, 3]$T) -> (det: T) {
|
||||
a := +m[0, 0] * (m[1, 1] * m[2, 2] - m[1, 2] * m[2, 1])
|
||||
b := -m[0, 1] * (m[1, 0] * m[2, 2] - m[1, 2] * m[2, 0])
|
||||
c := +m[0, 2] * (m[1, 0] * m[2, 1] - m[1, 1] * m[2, 0])
|
||||
return a + b + c
|
||||
}
|
||||
@(builtin)
|
||||
matrix4x4_determinant :: proc(m: $M/matrix[4, 4]$T) -> (det: T) {
|
||||
a := adjugate(m)
|
||||
#no_bounds_check for i in 0..<4 {
|
||||
det += m[0, i] * a[0, i]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@(builtin)
|
||||
matrix1x1_adjugate :: proc(x: $M/matrix[1, 1]$T) -> (y: M) {
|
||||
y = x
|
||||
return
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
matrix2x2_adjugate :: proc(x: $M/matrix[2, 2]$T) -> (y: M) {
|
||||
y[0, 0] = +x[1, 1]
|
||||
y[0, 1] = -x[1, 0]
|
||||
y[1, 0] = -x[0, 1]
|
||||
y[1, 1] = +x[0, 0]
|
||||
return
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
matrix3x3_adjugate :: proc(m: $M/matrix[3, 3]$T) -> (y: M) {
|
||||
y[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
|
||||
y[0, 1] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
|
||||
y[0, 2] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
|
||||
y[1, 0] = -(m[0, 1] * m[2, 2] - m[2, 1] * m[0, 2])
|
||||
y[1, 1] = +(m[0, 0] * m[2, 2] - m[2, 0] * m[0, 2])
|
||||
y[1, 2] = -(m[0, 0] * m[2, 1] - m[2, 0] * m[0, 1])
|
||||
y[2, 0] = +(m[0, 1] * m[1, 2] - m[1, 1] * m[0, 2])
|
||||
y[2, 1] = -(m[0, 0] * m[1, 2] - m[1, 0] * m[0, 2])
|
||||
y[2, 2] = +(m[0, 0] * m[1, 1] - m[1, 0] * m[0, 1])
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@(builtin)
|
||||
matrix4x4_adjugate :: proc(x: $M/matrix[4, 4]$T) -> (y: M) {
|
||||
for i in 0..<4 {
|
||||
for j in 0..<4 {
|
||||
sign: T = 1 if (i + j) % 2 == 0 else -1
|
||||
y[i, j] = sign * matrix_minor(x, i, j)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
matrix1x1_inverse_transpose :: proc(x: $M/matrix[1, 1]$T) -> (y: M) {
|
||||
y[0, 0] = 1/x[0, 0]
|
||||
return
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
matrix2x2_inverse_transpose :: proc(x: $M/matrix[2, 2]$T) -> (y: M) {
|
||||
d := x[0, 0]*x[1, 1] - x[0, 1]*x[1, 0]
|
||||
when intrinsics.type_is_integer(T) {
|
||||
y[0, 0] = +x[1, 1] / d
|
||||
y[1, 0] = -x[1, 0] / d
|
||||
y[0, 1] = -x[0, 1] / d
|
||||
y[1, 1] = +x[0, 0] / d
|
||||
} else {
|
||||
id := 1 / d
|
||||
y[0, 0] = +x[1, 1] * id
|
||||
y[1, 0] = -x[1, 0] * id
|
||||
y[0, 1] = -x[0, 1] * id
|
||||
y[1, 1] = +x[0, 0] * id
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
matrix3x3_inverse_transpose :: proc(x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
|
||||
a := adjugate(x)
|
||||
d := determinant(x)
|
||||
when intrinsics.type_is_integer(T) {
|
||||
for i in 0..<3 {
|
||||
for j in 0..<3 {
|
||||
y[i, j] = a[i, j] / d
|
||||
}
|
||||
}
|
||||
} else {
|
||||
id := 1/d
|
||||
for i in 0..<3 {
|
||||
for j in 0..<3 {
|
||||
y[i, j] = a[i, j] * id
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
matrix4x4_inverse_transpose :: proc(x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
|
||||
a := adjugate(x)
|
||||
d: T
|
||||
for i in 0..<4 {
|
||||
d += x[0, i] * a[0, i]
|
||||
}
|
||||
when intrinsics.type_is_integer(T) {
|
||||
for i in 0..<4 {
|
||||
for j in 0..<4 {
|
||||
y[i, j] = a[i, j] / d
|
||||
}
|
||||
}
|
||||
} else {
|
||||
id := 1/d
|
||||
for i in 0..<4 {
|
||||
for j in 0..<4 {
|
||||
y[i, j] = a[i, j] * id
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
matrix1x1_inverse :: proc(x: $M/matrix[1, 1]$T) -> (y: M) {
|
||||
y[0, 0] = 1/x[0, 0]
|
||||
return
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
matrix2x2_inverse :: proc(x: $M/matrix[2, 2]$T) -> (y: M) {
|
||||
d := x[0, 0]*x[1, 1] - x[0, 1]*x[1, 0]
|
||||
when intrinsics.type_is_integer(T) {
|
||||
y[0, 0] = x[1, 1] / d
|
||||
y[0, 1] = x[1, 0] / d
|
||||
y[1, 0] = x[0, 1] / d
|
||||
y[1, 1] = x[0, 0] / d
|
||||
} else {
|
||||
id := 1 / d
|
||||
y[0, 0] = x[1, 1] * id
|
||||
y[0, 1] = x[1, 0] * id
|
||||
y[1, 0] = x[0, 1] * id
|
||||
y[1, 1] = x[0, 0] * id
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
matrix3x3_inverse :: proc(x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
|
||||
a := adjugate(x)
|
||||
d := determinant(x)
|
||||
when intrinsics.type_is_integer(T) {
|
||||
for i in 0..<3 {
|
||||
for j in 0..<3 {
|
||||
y[i, j] = a[j, i] / d
|
||||
}
|
||||
}
|
||||
} else {
|
||||
id := 1/d
|
||||
for i in 0..<3 {
|
||||
for j in 0..<3 {
|
||||
y[i, j] = a[j, i] * id
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@(builtin)
|
||||
matrix4x4_inverse :: proc(x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
|
||||
a := adjugate(x)
|
||||
d: T
|
||||
for i in 0..<4 {
|
||||
d += x[0, i] * a[0, i]
|
||||
}
|
||||
when intrinsics.type_is_integer(T) {
|
||||
for i in 0..<4 {
|
||||
for j in 0..<4 {
|
||||
y[i, j] = a[j, i] / d
|
||||
}
|
||||
}
|
||||
} else {
|
||||
id := 1/d
|
||||
for i in 0..<4 {
|
||||
for j in 0..<4 {
|
||||
y[i, j] = a[j, i] * id
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
//+build !windows
|
||||
//+build !freestanding
|
||||
//+build !wasi
|
||||
package runtime
|
||||
|
||||
when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
|
||||
|
||||
@@ -6,12 +6,12 @@ package runtime
|
||||
default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
return nil, .None;
|
||||
return nil, .None
|
||||
}
|
||||
|
||||
default_allocator :: proc() -> Allocator {
|
||||
return Allocator{
|
||||
procedure = default_allocator_proc,
|
||||
data = nil,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
//+build wasi
|
||||
package runtime
|
||||
|
||||
default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return nil, .Out_Of_Memory
|
||||
case .Free:
|
||||
return nil, .None
|
||||
case .Free_All:
|
||||
return nil, .Mode_Not_Implemented
|
||||
case .Resize:
|
||||
if size == 0 {
|
||||
return nil, .None
|
||||
}
|
||||
return nil, .Out_Of_Memory
|
||||
case .Query_Features:
|
||||
return nil, .Mode_Not_Implemented
|
||||
case .Query_Info:
|
||||
return nil, .Mode_Not_Implemented
|
||||
}
|
||||
return nil, .None
|
||||
}
|
||||
|
||||
default_allocator :: proc() -> Allocator {
|
||||
return Allocator{
|
||||
procedure = default_allocator_proc,
|
||||
data = nil,
|
||||
}
|
||||
}
|
||||
@@ -1,191 +1,202 @@
|
||||
package runtime
|
||||
|
||||
@(private)
|
||||
byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte {
|
||||
return transmute([]u8)Raw_Slice{data=data, len=max(len, 0)}
|
||||
}
|
||||
|
||||
|
||||
DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE: int : #config(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, 1<<22)
|
||||
|
||||
|
||||
Default_Temp_Allocator :: struct {
|
||||
data: []byte,
|
||||
curr_offset: int,
|
||||
prev_allocation: rawptr,
|
||||
backup_allocator: Allocator,
|
||||
leaked_allocations: [dynamic][]byte,
|
||||
}
|
||||
|
||||
default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backup_allocator := context.allocator) {
|
||||
s.data = make_aligned([]byte, size, 2*align_of(rawptr), backup_allocator)
|
||||
s.curr_offset = 0
|
||||
s.prev_allocation = nil
|
||||
s.backup_allocator = backup_allocator
|
||||
s.leaked_allocations.allocator = backup_allocator
|
||||
}
|
||||
|
||||
default_temp_allocator_destroy :: proc(s: ^Default_Temp_Allocator) {
|
||||
if s == nil {
|
||||
return
|
||||
when ODIN_OS == "freestanding" {
|
||||
Default_Temp_Allocator :: struct {
|
||||
}
|
||||
for ptr in s.leaked_allocations {
|
||||
free(raw_data(ptr), s.backup_allocator)
|
||||
|
||||
default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backup_allocator := context.allocator) {
|
||||
}
|
||||
delete(s.leaked_allocations)
|
||||
delete(s.data, s.backup_allocator)
|
||||
s^ = {}
|
||||
}
|
||||
|
||||
@(private)
|
||||
default_temp_allocator_alloc :: proc(s: ^Default_Temp_Allocator, size, alignment: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
size := size
|
||||
size = align_forward_int(size, alignment)
|
||||
|
||||
switch {
|
||||
case s.curr_offset+size <= len(s.data):
|
||||
start := uintptr(raw_data(s.data))
|
||||
ptr := start + uintptr(s.curr_offset)
|
||||
ptr = align_forward_uintptr(ptr, uintptr(alignment))
|
||||
mem_zero(rawptr(ptr), size)
|
||||
|
||||
s.prev_allocation = rawptr(ptr)
|
||||
offset := int(ptr - start)
|
||||
s.curr_offset = offset + size
|
||||
return byte_slice(rawptr(ptr), size), .None
|
||||
|
||||
case size <= len(s.data):
|
||||
start := uintptr(raw_data(s.data))
|
||||
ptr := align_forward_uintptr(start, uintptr(alignment))
|
||||
mem_zero(rawptr(ptr), size)
|
||||
|
||||
s.prev_allocation = rawptr(ptr)
|
||||
offset := int(ptr - start)
|
||||
s.curr_offset = offset + size
|
||||
return byte_slice(rawptr(ptr), size), .None
|
||||
|
||||
default_temp_allocator_destroy :: proc(s: ^Default_Temp_Allocator) {
|
||||
}
|
||||
a := s.backup_allocator
|
||||
if a.procedure == nil {
|
||||
a = context.allocator
|
||||
s.backup_allocator = a
|
||||
|
||||
default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
data, err := mem_alloc_bytes(size, alignment, a, loc)
|
||||
if err != nil {
|
||||
return data, err
|
||||
} else {
|
||||
Default_Temp_Allocator :: struct {
|
||||
data: []byte,
|
||||
curr_offset: int,
|
||||
prev_allocation: rawptr,
|
||||
backup_allocator: Allocator,
|
||||
leaked_allocations: [dynamic][]byte,
|
||||
}
|
||||
if s.leaked_allocations == nil {
|
||||
s.leaked_allocations = make([dynamic][]byte, a)
|
||||
}
|
||||
append(&s.leaked_allocations, data)
|
||||
|
||||
// TODO(bill): Should leaks be notified about?
|
||||
if logger := context.logger; logger.lowest_level <= .Warning {
|
||||
if logger.procedure != nil {
|
||||
logger.procedure(logger.data, .Warning, "default temp allocator resorted to backup_allocator" , logger.options, loc)
|
||||
}
|
||||
}
|
||||
|
||||
return data, .None
|
||||
}
|
||||
|
||||
@(private)
|
||||
default_temp_allocator_free :: proc(s: ^Default_Temp_Allocator, old_memory: rawptr, loc := #caller_location) -> Allocator_Error {
|
||||
if old_memory == nil {
|
||||
return .None
|
||||
}
|
||||
|
||||
start := uintptr(raw_data(s.data))
|
||||
end := start + uintptr(len(s.data))
|
||||
old_ptr := uintptr(old_memory)
|
||||
|
||||
if s.prev_allocation == old_memory {
|
||||
s.curr_offset = int(uintptr(s.prev_allocation) - start)
|
||||
|
||||
default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backup_allocator := context.allocator) {
|
||||
s.data = make_aligned([]byte, size, 2*align_of(rawptr), backup_allocator)
|
||||
s.curr_offset = 0
|
||||
s.prev_allocation = nil
|
||||
return .None
|
||||
s.backup_allocator = backup_allocator
|
||||
s.leaked_allocations.allocator = backup_allocator
|
||||
}
|
||||
|
||||
if start <= old_ptr && old_ptr < end {
|
||||
// NOTE(bill): Cannot free this pointer but it is valid
|
||||
return .None
|
||||
default_temp_allocator_destroy :: proc(s: ^Default_Temp_Allocator) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
for ptr in s.leaked_allocations {
|
||||
free(raw_data(ptr), s.backup_allocator)
|
||||
}
|
||||
delete(s.leaked_allocations)
|
||||
delete(s.data, s.backup_allocator)
|
||||
s^ = {}
|
||||
}
|
||||
|
||||
if len(s.leaked_allocations) != 0 {
|
||||
for data, i in s.leaked_allocations {
|
||||
ptr := raw_data(data)
|
||||
if ptr == old_memory {
|
||||
free(ptr, s.backup_allocator)
|
||||
ordered_remove(&s.leaked_allocations, i)
|
||||
return .None
|
||||
@(private)
|
||||
default_temp_allocator_alloc :: proc(s: ^Default_Temp_Allocator, size, alignment: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
size := size
|
||||
size = align_forward_int(size, alignment)
|
||||
|
||||
switch {
|
||||
case s.curr_offset+size <= len(s.data):
|
||||
start := uintptr(raw_data(s.data))
|
||||
ptr := start + uintptr(s.curr_offset)
|
||||
ptr = align_forward_uintptr(ptr, uintptr(alignment))
|
||||
mem_zero(rawptr(ptr), size)
|
||||
|
||||
s.prev_allocation = rawptr(ptr)
|
||||
offset := int(ptr - start)
|
||||
s.curr_offset = offset + size
|
||||
return byte_slice(rawptr(ptr), size), .None
|
||||
|
||||
case size <= len(s.data):
|
||||
start := uintptr(raw_data(s.data))
|
||||
ptr := align_forward_uintptr(start, uintptr(alignment))
|
||||
mem_zero(rawptr(ptr), size)
|
||||
|
||||
s.prev_allocation = rawptr(ptr)
|
||||
offset := int(ptr - start)
|
||||
s.curr_offset = offset + size
|
||||
return byte_slice(rawptr(ptr), size), .None
|
||||
}
|
||||
a := s.backup_allocator
|
||||
if a.procedure == nil {
|
||||
a = context.allocator
|
||||
s.backup_allocator = a
|
||||
}
|
||||
|
||||
data, err := mem_alloc_bytes(size, alignment, a, loc)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
if s.leaked_allocations == nil {
|
||||
s.leaked_allocations = make([dynamic][]byte, a)
|
||||
}
|
||||
append(&s.leaked_allocations, data)
|
||||
|
||||
// TODO(bill): Should leaks be notified about?
|
||||
if logger := context.logger; logger.lowest_level <= .Warning {
|
||||
if logger.procedure != nil {
|
||||
logger.procedure(logger.data, .Warning, "default temp allocator resorted to backup_allocator" , logger.options, loc)
|
||||
}
|
||||
}
|
||||
}
|
||||
return .Invalid_Pointer
|
||||
// panic("invalid pointer passed to default_temp_allocator");
|
||||
}
|
||||
|
||||
@(private)
|
||||
default_temp_allocator_free_all :: proc(s: ^Default_Temp_Allocator, loc := #caller_location) {
|
||||
s.curr_offset = 0
|
||||
s.prev_allocation = nil
|
||||
for data in s.leaked_allocations {
|
||||
free(raw_data(data), s.backup_allocator)
|
||||
}
|
||||
clear(&s.leaked_allocations)
|
||||
}
|
||||
|
||||
@(private)
|
||||
default_temp_allocator_resize :: proc(s: ^Default_Temp_Allocator, old_memory: rawptr, old_size, size, alignment: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
begin := uintptr(raw_data(s.data))
|
||||
end := begin + uintptr(len(s.data))
|
||||
old_ptr := uintptr(old_memory)
|
||||
if old_memory == s.prev_allocation && old_ptr & uintptr(alignment)-1 == 0 {
|
||||
if old_ptr+uintptr(size) < end {
|
||||
s.curr_offset = int(old_ptr-begin)+size
|
||||
return byte_slice(old_memory, size), .None
|
||||
}
|
||||
}
|
||||
data, err := default_temp_allocator_alloc(s, size, alignment, loc)
|
||||
if err == .None {
|
||||
copy(data, byte_slice(old_memory, old_size))
|
||||
err = default_temp_allocator_free(s, old_memory, loc)
|
||||
}
|
||||
return data, err
|
||||
}
|
||||
|
||||
default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||
|
||||
s := (^Default_Temp_Allocator)(allocator_data)
|
||||
|
||||
if s.data == nil {
|
||||
default_temp_allocator_init(s, DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, default_allocator())
|
||||
return data, .None
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
data, err = default_temp_allocator_alloc(s, size, alignment, loc)
|
||||
case .Free:
|
||||
err = default_temp_allocator_free(s, old_memory, loc)
|
||||
|
||||
case .Free_All:
|
||||
default_temp_allocator_free_all(s, loc)
|
||||
|
||||
case .Resize:
|
||||
data, err = default_temp_allocator_resize(s, old_memory, old_size, size, alignment, loc)
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features}
|
||||
@(private)
|
||||
default_temp_allocator_free :: proc(s: ^Default_Temp_Allocator, old_memory: rawptr, loc := #caller_location) -> Allocator_Error {
|
||||
if old_memory == nil {
|
||||
return .None
|
||||
}
|
||||
|
||||
case .Query_Info:
|
||||
// Nothing to give
|
||||
start := uintptr(raw_data(s.data))
|
||||
end := start + uintptr(len(s.data))
|
||||
old_ptr := uintptr(old_memory)
|
||||
|
||||
if s.prev_allocation == old_memory {
|
||||
s.curr_offset = int(uintptr(s.prev_allocation) - start)
|
||||
s.prev_allocation = nil
|
||||
return .None
|
||||
}
|
||||
|
||||
if start <= old_ptr && old_ptr < end {
|
||||
// NOTE(bill): Cannot free this pointer but it is valid
|
||||
return .None
|
||||
}
|
||||
|
||||
if len(s.leaked_allocations) != 0 {
|
||||
for data, i in s.leaked_allocations {
|
||||
ptr := raw_data(data)
|
||||
if ptr == old_memory {
|
||||
free(ptr, s.backup_allocator)
|
||||
ordered_remove(&s.leaked_allocations, i)
|
||||
return .None
|
||||
}
|
||||
}
|
||||
}
|
||||
return .Invalid_Pointer
|
||||
// panic("invalid pointer passed to default_temp_allocator");
|
||||
}
|
||||
|
||||
return
|
||||
@(private)
|
||||
default_temp_allocator_free_all :: proc(s: ^Default_Temp_Allocator, loc := #caller_location) {
|
||||
s.curr_offset = 0
|
||||
s.prev_allocation = nil
|
||||
for data in s.leaked_allocations {
|
||||
free(raw_data(data), s.backup_allocator)
|
||||
}
|
||||
clear(&s.leaked_allocations)
|
||||
}
|
||||
|
||||
@(private)
|
||||
default_temp_allocator_resize :: proc(s: ^Default_Temp_Allocator, old_memory: rawptr, old_size, size, alignment: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
begin := uintptr(raw_data(s.data))
|
||||
end := begin + uintptr(len(s.data))
|
||||
old_ptr := uintptr(old_memory)
|
||||
if old_memory == s.prev_allocation && old_ptr & uintptr(alignment)-1 == 0 {
|
||||
if old_ptr+uintptr(size) < end {
|
||||
s.curr_offset = int(old_ptr-begin)+size
|
||||
return byte_slice(old_memory, size), .None
|
||||
}
|
||||
}
|
||||
data, err := default_temp_allocator_alloc(s, size, alignment, loc)
|
||||
if err == .None {
|
||||
copy(data, byte_slice(old_memory, old_size))
|
||||
err = default_temp_allocator_free(s, old_memory, loc)
|
||||
}
|
||||
return data, err
|
||||
}
|
||||
|
||||
default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||
|
||||
s := (^Default_Temp_Allocator)(allocator_data)
|
||||
|
||||
if s.data == nil {
|
||||
default_temp_allocator_init(s, DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, default_allocator())
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
data, err = default_temp_allocator_alloc(s, size, alignment, loc)
|
||||
case .Free:
|
||||
err = default_temp_allocator_free(s, old_memory, loc)
|
||||
|
||||
case .Free_All:
|
||||
default_temp_allocator_free_all(s, loc)
|
||||
|
||||
case .Resize:
|
||||
data, err = default_temp_allocator_resize(s, old_memory, old_size, size, alignment, loc)
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features}
|
||||
}
|
||||
|
||||
case .Query_Info:
|
||||
// Nothing to give
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
default_temp_allocator :: proc(allocator: ^Default_Temp_Allocator) -> Allocator {
|
||||
@@ -193,4 +204,4 @@ default_temp_allocator :: proc(allocator: ^Default_Temp_Allocator) -> Allocator
|
||||
procedure = default_temp_allocator_proc,
|
||||
data = allocator,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -190,8 +190,6 @@ __slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, l
|
||||
return true
|
||||
}
|
||||
|
||||
assert(allocator.procedure != nil)
|
||||
|
||||
old_size := array.len*size_of(T)
|
||||
new_size := new_count*size_of(T)
|
||||
|
||||
@@ -204,67 +202,45 @@ __slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, l
|
||||
return true
|
||||
}
|
||||
|
||||
__dynamic_map_reserve :: proc(using header: Map_Header, cap: int, loc := #caller_location) {
|
||||
__dynamic_array_reserve(&m.entries, entry_size, entry_align, cap, loc)
|
||||
|
||||
old_len := len(m.hashes)
|
||||
__slice_resize(&m.hashes, cap, m.entries.allocator, loc)
|
||||
for i in old_len..<len(m.hashes) {
|
||||
__dynamic_map_reset_entries :: proc(using header: Map_Header, loc := #caller_location) {
|
||||
for i in 0..<len(m.hashes) {
|
||||
m.hashes[i] = -1
|
||||
}
|
||||
|
||||
for i in 0 ..< m.entries.len {
|
||||
entry_header := __dynamic_map_get_entry(header, i)
|
||||
entry_hash := __get_map_hash_from_entry(header, entry_header)
|
||||
entry_header.next = -1
|
||||
|
||||
fr := __dynamic_map_find(header, entry_hash)
|
||||
if fr.entry_prev < 0 {
|
||||
m.hashes[fr.hash_index] = i
|
||||
} else {
|
||||
e := __dynamic_map_get_entry(header, fr.entry_prev)
|
||||
e.next = i
|
||||
}
|
||||
}
|
||||
}
|
||||
__dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #caller_location) #no_bounds_check {
|
||||
|
||||
__dynamic_map_reserve :: proc(using header: Map_Header, cap: int, loc := #caller_location) {
|
||||
c := context
|
||||
if m.entries.allocator.procedure != nil {
|
||||
c.allocator = m.entries.allocator
|
||||
}
|
||||
context = c
|
||||
|
||||
nm := Raw_Map{}
|
||||
nm.entries.allocator = m.entries.allocator
|
||||
nm.hashes = m.hashes
|
||||
|
||||
new_header: Map_Header = header
|
||||
new_header.m = &nm
|
||||
|
||||
__dynamic_array_reserve(&m.entries, entry_size, entry_align, cap, loc)
|
||||
|
||||
new_count := new_count
|
||||
new_count = max(new_count, 2*m.entries.len)
|
||||
|
||||
__slice_resize(&nm.hashes, new_count, m.entries.allocator, loc)
|
||||
for _, i in nm.hashes {
|
||||
nm.hashes[i] = -1
|
||||
if m.entries.len*2 < len(m.hashes) {
|
||||
return
|
||||
}
|
||||
|
||||
__dynamic_array_reserve(&nm.entries, entry_size, entry_align, m.entries.len, loc)
|
||||
for i in 0 ..< m.entries.len {
|
||||
if len(nm.hashes) == 0 {
|
||||
__dynamic_map_grow(new_header, loc)
|
||||
}
|
||||
|
||||
entry_header := __dynamic_map_get_entry(header, i)
|
||||
entry_hash := __get_map_hash_from_entry(header, entry_header)
|
||||
|
||||
fr := __dynamic_map_find(new_header, entry_hash)
|
||||
j := __dynamic_map_add_entry(new_header, entry_hash, loc)
|
||||
if fr.entry_prev < 0 {
|
||||
nm.hashes[fr.hash_index] = j
|
||||
} else {
|
||||
e := __dynamic_map_get_entry(new_header, fr.entry_prev)
|
||||
e.next = j
|
||||
}
|
||||
|
||||
e := __dynamic_map_get_entry(new_header, j)
|
||||
__dynamic_map_copy_entry(header, e, entry_header)
|
||||
e.next = fr.entry_index
|
||||
|
||||
if __dynamic_map_full(new_header) {
|
||||
__dynamic_map_grow(new_header, loc)
|
||||
}
|
||||
if __slice_resize(&m.hashes, cap*2, m.entries.allocator, loc) {
|
||||
__dynamic_map_reset_entries(header, loc)
|
||||
}
|
||||
}
|
||||
|
||||
free(m.entries.data, m.entries.allocator, loc)
|
||||
header.m^ = nm
|
||||
__dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #caller_location) {
|
||||
#force_inline __dynamic_map_reserve(header, new_count, loc)
|
||||
}
|
||||
|
||||
__dynamic_map_get :: proc(h: Map_Header, hash: Map_Hash) -> rawptr {
|
||||
@@ -278,7 +254,7 @@ __dynamic_map_get :: proc(h: Map_Header, hash: Map_Hash) -> rawptr {
|
||||
|
||||
__dynamic_map_set :: proc(h: Map_Header, hash: Map_Hash, value: rawptr, loc := #caller_location) -> ^Map_Entry_Header #no_bounds_check {
|
||||
index: int
|
||||
assert(value != nil)
|
||||
// assert(value != nil)
|
||||
|
||||
if len(h.m.hashes) == 0 {
|
||||
__dynamic_map_reserve(h, INITIAL_MAP_CAP, loc)
|
||||
@@ -311,8 +287,8 @@ __dynamic_map_set :: proc(h: Map_Header, hash: Map_Hash, value: rawptr, loc := #
|
||||
|
||||
if __dynamic_map_full(h) {
|
||||
__dynamic_map_grow(h, loc)
|
||||
index = __dynamic_map_find(h, hash).entry_index
|
||||
assert(index >= 0)
|
||||
// index = __dynamic_map_find(h, hash).entry_index
|
||||
// assert(index >= 0)
|
||||
}
|
||||
|
||||
return __dynamic_map_get_entry(h, index)
|
||||
@@ -331,10 +307,7 @@ __dynamic_map_full :: #force_inline proc "contextless" (using h: Map_Header) ->
|
||||
|
||||
|
||||
__dynamic_map_hash_equal :: proc "contextless" (h: Map_Header, a, b: Map_Hash) -> bool {
|
||||
if a.hash == b.hash {
|
||||
return h.equal(a.key_ptr, b.key_ptr)
|
||||
}
|
||||
return false
|
||||
return a.hash == b.hash && h.equal(a.key_ptr, b.key_ptr)
|
||||
}
|
||||
|
||||
__dynamic_map_find :: proc(using h: Map_Header, hash: Map_Hash) -> Map_Find_Result #no_bounds_check {
|
||||
@@ -348,6 +321,8 @@ __dynamic_map_find :: proc(using h: Map_Header, hash: Map_Hash) -> Map_Find_Resu
|
||||
if __dynamic_map_hash_equal(h, entry_hash, hash) {
|
||||
return fr
|
||||
}
|
||||
// assert(entry.next < m.entries.len)
|
||||
|
||||
fr.entry_prev = fr.entry_index
|
||||
fr.entry_index = entry.next
|
||||
}
|
||||
@@ -375,15 +350,15 @@ __dynamic_map_delete_key :: proc(using h: Map_Header, hash: Map_Hash) {
|
||||
}
|
||||
|
||||
__dynamic_map_get_entry :: proc(using h: Map_Header, index: int) -> ^Map_Entry_Header {
|
||||
assert(0 <= index && index < m.entries.len)
|
||||
// assert(0 <= index && index < m.entries.len)
|
||||
return (^Map_Entry_Header)(uintptr(m.entries.data) + uintptr(index*entry_size))
|
||||
}
|
||||
|
||||
__dynamic_map_copy_entry :: proc "contextless" (h: Map_Header, new, old: ^Map_Entry_Header) {
|
||||
__dynamic_map_copy_entry :: proc(h: Map_Header, new, old: ^Map_Entry_Header) {
|
||||
mem_copy(new, old, h.entry_size)
|
||||
}
|
||||
|
||||
__dynamic_map_erase :: proc(using h: Map_Header, fr: Map_Find_Result) #no_bounds_check {
|
||||
__dynamic_map_erase :: proc(using h: Map_Header, fr: Map_Find_Result) #no_bounds_check {
|
||||
if fr.entry_prev < 0 {
|
||||
m.hashes[fr.hash_index] = __dynamic_map_get_entry(h, fr.entry_index).next
|
||||
} else {
|
||||
|
||||
@@ -96,6 +96,29 @@ dynamic_array_expr_error :: proc "contextless" (file: string, line, column: i32,
|
||||
}
|
||||
|
||||
|
||||
matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) {
|
||||
if 0 <= row_index && row_index < row_count &&
|
||||
0 <= column_index && column_index < column_count {
|
||||
return
|
||||
}
|
||||
handle_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) {
|
||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||
print_string(" Matrix indices [")
|
||||
print_i64(i64(row_index))
|
||||
print_string(", ")
|
||||
print_i64(i64(column_index))
|
||||
print_string(" is out of bounds range [0..<")
|
||||
print_i64(i64(row_count))
|
||||
print_string(", 0..<")
|
||||
print_i64(i64(column_count))
|
||||
print_string("]")
|
||||
print_byte('\n')
|
||||
bounds_trap()
|
||||
}
|
||||
handle_error(file, line, column, row_index, column_index, row_count, column_count)
|
||||
}
|
||||
|
||||
|
||||
type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid) {
|
||||
if ok {
|
||||
return
|
||||
|
||||
+174
-24
@@ -2,15 +2,20 @@ package runtime
|
||||
|
||||
import "core:intrinsics"
|
||||
|
||||
bswap_16 :: proc "none" (x: u16) -> u16 {
|
||||
@(private)
|
||||
byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte #no_bounds_check {
|
||||
return ([^]byte)(data)[:max(len, 0)]
|
||||
}
|
||||
|
||||
bswap_16 :: proc "contextless" (x: u16) -> u16 {
|
||||
return x>>8 | x<<8
|
||||
}
|
||||
|
||||
bswap_32 :: proc "none" (x: u32) -> u32 {
|
||||
bswap_32 :: proc "contextless" (x: u32) -> u32 {
|
||||
return x>>24 | (x>>8)&0xff00 | (x<<8)&0xff0000 | x<<24
|
||||
}
|
||||
|
||||
bswap_64 :: proc "none" (x: u64) -> u64 {
|
||||
bswap_64 :: proc "contextless" (x: u64) -> u64 {
|
||||
z := x
|
||||
z = (z & 0x00000000ffffffff) << 32 | (z & 0xffffffff00000000) >> 32
|
||||
z = (z & 0x0000ffff0000ffff) << 16 | (z & 0xffff0000ffff0000) >> 16
|
||||
@@ -18,7 +23,7 @@ bswap_64 :: proc "none" (x: u64) -> u64 {
|
||||
return z
|
||||
}
|
||||
|
||||
bswap_128 :: proc "none" (x: u128) -> u128 {
|
||||
bswap_128 :: proc "contextless" (x: u128) -> u128 {
|
||||
z := transmute([4]u32)x
|
||||
z[0] = bswap_32(z[3])
|
||||
z[1] = bswap_32(z[2])
|
||||
@@ -27,33 +32,27 @@ bswap_128 :: proc "none" (x: u128) -> u128 {
|
||||
return transmute(u128)z
|
||||
}
|
||||
|
||||
bswap_f16 :: proc "none" (f: f16) -> f16 {
|
||||
bswap_f16 :: proc "contextless" (f: f16) -> f16 {
|
||||
x := transmute(u16)f
|
||||
z := bswap_16(x)
|
||||
return transmute(f16)z
|
||||
|
||||
}
|
||||
|
||||
bswap_f32 :: proc "none" (f: f32) -> f32 {
|
||||
bswap_f32 :: proc "contextless" (f: f32) -> f32 {
|
||||
x := transmute(u32)f
|
||||
z := bswap_32(x)
|
||||
return transmute(f32)z
|
||||
|
||||
}
|
||||
|
||||
bswap_f64 :: proc "none" (f: f64) -> f64 {
|
||||
bswap_f64 :: proc "contextless" (f: f64) -> f64 {
|
||||
x := transmute(u64)f
|
||||
z := bswap_64(x)
|
||||
return transmute(f64)z
|
||||
}
|
||||
|
||||
|
||||
|
||||
ptr_offset :: #force_inline proc "contextless" (ptr: $P/^$T, n: int) -> P {
|
||||
new := int(uintptr(ptr)) + size_of(T)*n
|
||||
return P(uintptr(new))
|
||||
}
|
||||
|
||||
is_power_of_two_int :: #force_inline proc(x: int) -> bool {
|
||||
if x <= 0 {
|
||||
return false
|
||||
@@ -177,8 +176,70 @@ mem_resize :: #force_inline proc(ptr: rawptr, old_size, new_size: int, alignment
|
||||
new_ptr = raw_data(new_data)
|
||||
return
|
||||
}
|
||||
memory_equal :: proc "contextless" (a, b: rawptr, n: int) -> bool {
|
||||
return memory_compare(a, b, n) == 0
|
||||
memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
|
||||
switch {
|
||||
case n == 0: return true
|
||||
case x == y: return true
|
||||
}
|
||||
|
||||
a, b := ([^]byte)(x), ([^]byte)(y)
|
||||
length := uint(n)
|
||||
|
||||
when size_of(uint) == 8 {
|
||||
if word_length := length >> 3; word_length != 0 {
|
||||
for i in 0..<word_length {
|
||||
if intrinsics.unaligned_load((^u64)(a)) != intrinsics.unaligned_load((^u64)(b)) {
|
||||
return false
|
||||
}
|
||||
a = a[size_of(u64):]
|
||||
b = b[size_of(u64):]
|
||||
}
|
||||
}
|
||||
|
||||
if length & 4 != 0 {
|
||||
if intrinsics.unaligned_load((^u32)(a)) != intrinsics.unaligned_load((^u32)(b)) {
|
||||
return false
|
||||
}
|
||||
a = a[size_of(u32):]
|
||||
b = b[size_of(u32):]
|
||||
}
|
||||
|
||||
if length & 2 != 0 {
|
||||
if intrinsics.unaligned_load((^u16)(a)) != intrinsics.unaligned_load((^u16)(b)) {
|
||||
return false
|
||||
}
|
||||
a = a[size_of(u16):]
|
||||
b = b[size_of(u16):]
|
||||
}
|
||||
|
||||
if length & 1 != 0 && a[0] != b[0] {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
if word_length := length >> 2; word_length != 0 {
|
||||
for i in 0..<word_length {
|
||||
if intrinsics.unaligned_load((^u32)(a)) != intrinsics.unaligned_load((^u32)(b)) {
|
||||
return false
|
||||
}
|
||||
a = a[size_of(u32):]
|
||||
b = b[size_of(u32):]
|
||||
}
|
||||
}
|
||||
|
||||
length &= 3
|
||||
|
||||
if length != 0 {
|
||||
for i in 0..<length {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
memory_compare :: proc "contextless" (a, b: rawptr, n: int) -> int #no_bounds_check {
|
||||
switch {
|
||||
@@ -258,15 +319,13 @@ memory_compare_zero :: proc "contextless" (a: rawptr, n: int) -> int #no_bounds_
|
||||
return 0
|
||||
}
|
||||
|
||||
string_eq :: proc "contextless" (a, b: string) -> bool {
|
||||
x := transmute(Raw_String)a
|
||||
y := transmute(Raw_String)b
|
||||
switch {
|
||||
case x.len != y.len: return false
|
||||
case x.len == 0: return true
|
||||
case x.data == y.data: return true
|
||||
string_eq :: proc "contextless" (lhs, rhs: string) -> bool {
|
||||
x := transmute(Raw_String)lhs
|
||||
y := transmute(Raw_String)rhs
|
||||
if x.len != y.len {
|
||||
return false
|
||||
}
|
||||
return string_cmp(a, b) == 0
|
||||
return #force_inline memory_equal(x.data, y.data, x.len)
|
||||
}
|
||||
|
||||
string_cmp :: proc "contextless" (a, b: string) -> int {
|
||||
@@ -708,7 +767,7 @@ floattidf :: proc "c" (a: i128) -> f64 {
|
||||
a += 1
|
||||
a >>= 2
|
||||
|
||||
if a & (1 << DBL_MANT_DIG) != 0 {
|
||||
if a & (i128(1) << DBL_MANT_DIG) != 0 {
|
||||
a >>= 1
|
||||
e += 1
|
||||
}
|
||||
@@ -768,12 +827,103 @@ floattidf_unsigned :: proc "c" (a: u128) -> f64 {
|
||||
|
||||
@(link_name="__fixunsdfti")
|
||||
fixunsdfti :: #force_no_inline proc "c" (a: f64) -> u128 {
|
||||
// TODO(bill): implement `fixunsdfti` correctly
|
||||
x := u64(a)
|
||||
return u128(x)
|
||||
}
|
||||
|
||||
@(link_name="__fixunsdfdi")
|
||||
fixunsdfdi :: #force_no_inline proc "c" (a: f64) -> i128 {
|
||||
// TODO(bill): implement `fixunsdfdi` correctly
|
||||
x := i64(a)
|
||||
return i128(x)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@(link_name="__umodti3")
|
||||
umodti3 :: proc "c" (a, b: u128) -> u128 {
|
||||
r: u128 = ---
|
||||
_ = udivmod128(a, b, &r)
|
||||
return r
|
||||
}
|
||||
|
||||
|
||||
@(link_name="__udivmodti4")
|
||||
udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
|
||||
return udivmod128(a, b, rem)
|
||||
}
|
||||
|
||||
@(link_name="__udivti3")
|
||||
udivti3 :: proc "c" (a, b: u128) -> u128 {
|
||||
return udivmodti4(a, b, nil)
|
||||
}
|
||||
|
||||
|
||||
@(link_name="__modti3")
|
||||
modti3 :: proc "c" (a, b: i128) -> i128 {
|
||||
s_a := a >> (128 - 1)
|
||||
s_b := b >> (128 - 1)
|
||||
an := (a ~ s_a) - s_a
|
||||
bn := (b ~ s_b) - s_b
|
||||
|
||||
r: u128 = ---
|
||||
_ = udivmod128(transmute(u128)an, transmute(u128)bn, &r)
|
||||
return (transmute(i128)r ~ s_a) - s_a
|
||||
}
|
||||
|
||||
|
||||
@(link_name="__divmodti4")
|
||||
divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 {
|
||||
u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem)
|
||||
return transmute(i128)u
|
||||
}
|
||||
|
||||
@(link_name="__divti3")
|
||||
divti3 :: proc "c" (a, b: i128) -> i128 {
|
||||
u := udivmodti4(transmute(u128)a, transmute(u128)b, nil)
|
||||
return transmute(i128)u
|
||||
}
|
||||
|
||||
|
||||
@(link_name="__fixdfti")
|
||||
fixdfti :: proc(a: u64) -> i128 {
|
||||
significandBits :: 52
|
||||
typeWidth :: (size_of(u64)*8)
|
||||
exponentBits :: (typeWidth - significandBits - 1)
|
||||
maxExponent :: ((1 << exponentBits) - 1)
|
||||
exponentBias :: (maxExponent >> 1)
|
||||
|
||||
implicitBit :: (u64(1) << significandBits)
|
||||
significandMask :: (implicitBit - 1)
|
||||
signBit :: (u64(1) << (significandBits + exponentBits))
|
||||
absMask :: (signBit - 1)
|
||||
exponentMask :: (absMask ~ significandMask)
|
||||
|
||||
// Break a into sign, exponent, significand
|
||||
aRep := a
|
||||
aAbs := aRep & absMask
|
||||
sign := i128(-1 if aRep & signBit != 0 else 1)
|
||||
exponent := u64((aAbs >> significandBits) - exponentBias)
|
||||
significand := u64((aAbs & significandMask) | implicitBit)
|
||||
|
||||
// If exponent is negative, the result is zero.
|
||||
if exponent < 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// If the value is too large for the integer type, saturate.
|
||||
if exponent >= size_of(i128) * 8 {
|
||||
return max(i128) if sign == 1 else min(i128)
|
||||
}
|
||||
|
||||
// If 0 <= exponent < significandBits, right shift to get the result.
|
||||
// Otherwise, shift left.
|
||||
if exponent < significandBits {
|
||||
return sign * i128(significand >> (significandBits - exponent))
|
||||
} else {
|
||||
return sign * (i128(significand) << (exponent - significandBits))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,89 +1 @@
|
||||
package runtime
|
||||
|
||||
import "core:intrinsics"
|
||||
|
||||
@(link_name="__umodti3")
|
||||
umodti3 :: proc "c" (a, b: u128) -> u128 {
|
||||
r: u128 = ---
|
||||
_ = udivmod128(a, b, &r)
|
||||
return r
|
||||
}
|
||||
|
||||
|
||||
@(link_name="__udivmodti4")
|
||||
udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
|
||||
return udivmod128(a, b, rem)
|
||||
}
|
||||
|
||||
@(link_name="__udivti3")
|
||||
udivti3 :: proc "c" (a, b: u128) -> u128 {
|
||||
return udivmodti4(a, b, nil)
|
||||
}
|
||||
|
||||
|
||||
@(link_name="__modti3")
|
||||
modti3 :: proc "c" (a, b: i128) -> i128 {
|
||||
s_a := a >> (128 - 1)
|
||||
s_b := b >> (128 - 1)
|
||||
an := (a ~ s_a) - s_a
|
||||
bn := (b ~ s_b) - s_b
|
||||
|
||||
r: u128 = ---
|
||||
_ = udivmod128(transmute(u128)an, transmute(u128)bn, &r)
|
||||
return (transmute(i128)r ~ s_a) - s_a
|
||||
}
|
||||
|
||||
|
||||
@(link_name="__divmodti4")
|
||||
divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 {
|
||||
u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem)
|
||||
return transmute(i128)u
|
||||
}
|
||||
|
||||
@(link_name="__divti3")
|
||||
divti3 :: proc "c" (a, b: i128) -> i128 {
|
||||
u := udivmodti4(transmute(u128)a, transmute(u128)b, nil)
|
||||
return transmute(i128)u
|
||||
}
|
||||
|
||||
|
||||
@(link_name="__fixdfti")
|
||||
fixdfti :: proc(a: u64) -> i128 {
|
||||
significandBits :: 52
|
||||
typeWidth :: (size_of(u64)*8)
|
||||
exponentBits :: (typeWidth - significandBits - 1)
|
||||
maxExponent :: ((1 << exponentBits) - 1)
|
||||
exponentBias :: (maxExponent >> 1)
|
||||
|
||||
implicitBit :: (u64(1) << significandBits)
|
||||
significandMask :: (implicitBit - 1)
|
||||
signBit :: (u64(1) << (significandBits + exponentBits))
|
||||
absMask :: (signBit - 1)
|
||||
exponentMask :: (absMask ~ significandMask)
|
||||
|
||||
// Break a into sign, exponent, significand
|
||||
aRep := a
|
||||
aAbs := aRep & absMask
|
||||
sign := i128(-1 if aRep & signBit != 0 else 1)
|
||||
exponent := u64((aAbs >> significandBits) - exponentBias)
|
||||
significand := u64((aAbs & significandMask) | implicitBit)
|
||||
|
||||
// If exponent is negative, the result is zero.
|
||||
if exponent < 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// If the value is too large for the integer type, saturate.
|
||||
if exponent >= size_of(i128) * 8 {
|
||||
return max(i128) if sign == 1 else min(i128)
|
||||
}
|
||||
|
||||
// If 0 <= exponent < significandBits, right shift to get the result.
|
||||
// Otherwise, shift left.
|
||||
if exponent < significandBits {
|
||||
return sign * i128(significand >> (significandBits - exponent))
|
||||
} else {
|
||||
return sign * (i128(significand) << (exponent - significandBits))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,89 +1 @@
|
||||
package runtime
|
||||
|
||||
import "core:intrinsics"
|
||||
|
||||
@(link_name="__umodti3")
|
||||
umodti3 :: proc "c" (a, b: u128) -> u128 {
|
||||
r: u128 = ---
|
||||
_ = udivmod128(a, b, &r)
|
||||
return r
|
||||
}
|
||||
|
||||
|
||||
@(link_name="__udivmodti4")
|
||||
udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
|
||||
return udivmod128(a, b, rem)
|
||||
}
|
||||
|
||||
@(link_name="__udivti3")
|
||||
udivti3 :: proc "c" (a, b: u128) -> u128 {
|
||||
return udivmodti4(a, b, nil)
|
||||
}
|
||||
|
||||
|
||||
@(link_name="__modti3")
|
||||
modti3 :: proc "c" (a, b: i128) -> i128 {
|
||||
s_a := a >> (128 - 1)
|
||||
s_b := b >> (128 - 1)
|
||||
an := (a ~ s_a) - s_a
|
||||
bn := (b ~ s_b) - s_b
|
||||
|
||||
r: u128 = ---
|
||||
_ = udivmod128(transmute(u128)an, transmute(u128)bn, &r)
|
||||
return (transmute(i128)r ~ s_a) - s_a
|
||||
}
|
||||
|
||||
|
||||
@(link_name="__divmodti4")
|
||||
divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 {
|
||||
u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem)
|
||||
return transmute(i128)u
|
||||
}
|
||||
|
||||
@(link_name="__divti3")
|
||||
divti3 :: proc "c" (a, b: i128) -> i128 {
|
||||
u := udivmodti4(transmute(u128)a, transmute(u128)b, nil)
|
||||
return transmute(i128)u
|
||||
}
|
||||
|
||||
|
||||
@(link_name="__fixdfti")
|
||||
fixdfti :: proc(a: u64) -> i128 {
|
||||
significandBits :: 52
|
||||
typeWidth :: (size_of(u64)*8)
|
||||
exponentBits :: (typeWidth - significandBits - 1)
|
||||
maxExponent :: ((1 << exponentBits) - 1)
|
||||
exponentBias :: (maxExponent >> 1)
|
||||
|
||||
implicitBit :: (u64(1) << significandBits)
|
||||
significandMask :: (implicitBit - 1)
|
||||
signBit :: (u64(1) << (significandBits + exponentBits))
|
||||
absMask :: (signBit - 1)
|
||||
exponentMask :: (absMask ~ significandMask)
|
||||
|
||||
// Break a into sign, exponent, significand
|
||||
aRep := a
|
||||
aAbs := aRep & absMask
|
||||
sign := i128(-1 if aRep & signBit != 0 else 1)
|
||||
exponent := (aAbs >> significandBits) - exponentBias
|
||||
significand := (aAbs & significandMask) | implicitBit
|
||||
|
||||
// If exponent is negative, the result is zero.
|
||||
if exponent < 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// If the value is too large for the integer type, saturate.
|
||||
if exponent >= size_of(i128) * 8 {
|
||||
return max(i128) if sign == 1 else min(i128)
|
||||
}
|
||||
|
||||
// If 0 <= exponent < significandBits, right shift to get the result.
|
||||
// Otherwise, shift left.
|
||||
if exponent < significandBits {
|
||||
return sign * i128(significand >> (significandBits - exponent))
|
||||
} else {
|
||||
return sign * (i128(significand) << (exponent - significandBits))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//+build !freestanding
|
||||
//+build !windows
|
||||
//+build !freestanding !wasi !windows
|
||||
package runtime
|
||||
|
||||
import "core:os"
|
||||
|
||||
@@ -3,5 +3,5 @@ package runtime
|
||||
|
||||
// TODO(bill): reimplement `os.write`
|
||||
_os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
return 0, -1;
|
||||
return 0, -1
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
//+build wasi
|
||||
package runtime
|
||||
|
||||
import "core:sys/wasm/wasi"
|
||||
|
||||
_os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
data := (wasi.ciovec_t)(data)
|
||||
n, err := wasi.fd_write(1, {data})
|
||||
return int(n), _OS_Errno(err)
|
||||
}
|
||||
@@ -137,6 +137,10 @@ print_i64 :: proc "contextless" (x: i64) #no_bounds_check {
|
||||
os_write(a[i:])
|
||||
}
|
||||
|
||||
print_uint :: proc "contextless" (x: uint) { print_u64(u64(x)) }
|
||||
print_uintptr :: proc "contextless" (x: uintptr) { print_u64(u64(x)) }
|
||||
print_int :: proc "contextless" (x: int) { print_i64(i64(x)) }
|
||||
|
||||
print_caller_location :: proc "contextless" (using loc: Source_Code_Location) {
|
||||
print_string(file_path)
|
||||
print_byte('(')
|
||||
@@ -366,5 +370,13 @@ print_type :: proc "contextless" (ti: ^Type_Info) {
|
||||
print_type(info.base_integer)
|
||||
print_string(") ")
|
||||
print_type(info.slice)
|
||||
|
||||
case Type_Info_Matrix:
|
||||
print_string("matrix[")
|
||||
print_u64(u64(info.row_count))
|
||||
print_string(", ")
|
||||
print_u64(u64(info.column_count))
|
||||
print_string("]")
|
||||
print_type(info.elem)
|
||||
}
|
||||
}
|
||||
|
||||
+34
-8
@@ -1,13 +1,39 @@
|
||||
package runtime
|
||||
|
||||
@(link_name="memset")
|
||||
memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr #no_bounds_check {
|
||||
if ptr != nil && len != 0 {
|
||||
b := byte(val)
|
||||
p := ([^]byte)(ptr)
|
||||
for i in 0..<len {
|
||||
p[i] = b
|
||||
when ODIN_ARCH == "wasm32" || ODIN_ARCH == "wasm64" {
|
||||
@(link_name="memset")
|
||||
memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
|
||||
if ptr != nil && len != 0 {
|
||||
b := byte(val)
|
||||
p := ([^]byte)(ptr)
|
||||
for i in 0..<len {
|
||||
p[i] = b
|
||||
}
|
||||
}
|
||||
return ptr
|
||||
}
|
||||
|
||||
@(link_name="memmove")
|
||||
memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
|
||||
if dst != src {
|
||||
d, s := ([^]byte)(dst), ([^]byte)(src)
|
||||
d_end, s_end := d[len:], s[len:]
|
||||
for i := len-1; i >= 0; i -= 1 {
|
||||
d[i] = s[i]
|
||||
}
|
||||
}
|
||||
return dst
|
||||
|
||||
}
|
||||
} else {
|
||||
memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
|
||||
if ptr != nil && len != 0 {
|
||||
b := byte(val)
|
||||
p := ([^]byte)(ptr)
|
||||
for i in 0..<len {
|
||||
p[i] = b
|
||||
}
|
||||
}
|
||||
return ptr
|
||||
}
|
||||
return ptr
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
//+build wasm32
|
||||
package runtime
|
||||
|
||||
@(link_name="__ashlti3")
|
||||
__ashlti3 :: proc "c" (a: i64, b: i32) -> i64 {
|
||||
return a
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package slice
|
||||
|
||||
import "core:builtin"
|
||||
import "core:mem"
|
||||
|
||||
ptr_add :: proc(p: $P/^$T, x: int) -> ^T {
|
||||
@@ -43,6 +44,29 @@ ptr_swap_non_overlapping :: proc(x, y: rawptr, len: int) {
|
||||
}
|
||||
}
|
||||
|
||||
ptr_swap_overlapping :: proc(x, y: rawptr, len: int) {
|
||||
if len <= 0 {
|
||||
return
|
||||
}
|
||||
if x == y {
|
||||
return
|
||||
}
|
||||
|
||||
N :: 512
|
||||
buffer: [N]byte = ---
|
||||
|
||||
a, b := ([^]byte)(x), ([^]byte)(y)
|
||||
|
||||
for n := len; n > 0; n -= N {
|
||||
m := builtin.min(n, N)
|
||||
mem.copy(&buffer, a, m)
|
||||
mem.copy(a, b, m)
|
||||
mem.copy(b, &buffer, m)
|
||||
|
||||
a, b = a[N:], b[N:]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ptr_rotate :: proc(left: int, mid: ^$T, right: int) {
|
||||
when size_of(T) != 0 {
|
||||
|
||||
@@ -11,7 +11,7 @@ _ :: bits
|
||||
_ :: mem
|
||||
|
||||
|
||||
swap :: proc(array: $T/[]$E, a, b: int, loc := #caller_location) {
|
||||
swap :: proc(array: $T/[]$E, a, b: int) {
|
||||
when size_of(E) > 8 {
|
||||
ptr_swap_non_overlapping(&array[a], &array[b], size_of(E))
|
||||
} else {
|
||||
@@ -19,6 +19,13 @@ swap :: proc(array: $T/[]$E, a, b: int, loc := #caller_location) {
|
||||
}
|
||||
}
|
||||
|
||||
swap_between :: proc(a, b: $T/[]$E) {
|
||||
n := min(len(a), len(b))
|
||||
if n >= 0 {
|
||||
ptr_swap_overlapping(&a[0], &b[0], size_of(E)*n)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
reverse :: proc(array: $T/[]$E) {
|
||||
n := len(array)/2
|
||||
|
||||
+18
-18
@@ -152,8 +152,8 @@ _max_depth :: proc(n: int) -> int { // 2*ceil(log2(n+1))
|
||||
}
|
||||
|
||||
@(private)
|
||||
_quick_sort :: proc(data: $T/[]$E, a, b, max_depth: int) where ORD(E) {
|
||||
median3 :: proc(data: T, m1, m0, m2: int) {
|
||||
_quick_sort :: proc(data: $T/[]$E, a, b, max_depth: int) where ORD(E) #no_bounds_check {
|
||||
median3 :: proc(data: T, m1, m0, m2: int) #no_bounds_check {
|
||||
if data[m1] < data[m0] {
|
||||
swap(data, m1, m0)
|
||||
}
|
||||
@@ -165,7 +165,7 @@ _quick_sort :: proc(data: $T/[]$E, a, b, max_depth: int) where ORD(E) {
|
||||
}
|
||||
}
|
||||
|
||||
do_pivot :: proc(data: T, lo, hi: int) -> (midlo, midhi: int) {
|
||||
do_pivot :: proc(data: T, lo, hi: int) -> (midlo, midhi: int) #no_bounds_check {
|
||||
m := int(uint(lo+hi)>>1)
|
||||
if hi-lo > 40 {
|
||||
s := (hi-lo)/8
|
||||
@@ -265,7 +265,7 @@ _quick_sort :: proc(data: $T/[]$E, a, b, max_depth: int) where ORD(E) {
|
||||
}
|
||||
|
||||
@(private)
|
||||
_insertion_sort :: proc(data: $T/[]$E, a, b: int) where ORD(E) {
|
||||
_insertion_sort :: proc(data: $T/[]$E, a, b: int) where ORD(E) #no_bounds_check {
|
||||
for i in a+1..<b {
|
||||
for j := i; j > a && data[j] < data[j-1]; j -= 1 {
|
||||
swap(data, j, j-1)
|
||||
@@ -274,8 +274,8 @@ _insertion_sort :: proc(data: $T/[]$E, a, b: int) where ORD(E) {
|
||||
}
|
||||
|
||||
@(private)
|
||||
_heap_sort :: proc(data: $T/[]$E, a, b: int) where ORD(E) {
|
||||
sift_down :: proc(data: T, lo, hi, first: int) {
|
||||
_heap_sort :: proc(data: $T/[]$E, a, b: int) where ORD(E) #no_bounds_check {
|
||||
sift_down :: proc(data: T, lo, hi, first: int) #no_bounds_check {
|
||||
root := lo
|
||||
for {
|
||||
child := 2*root + 1
|
||||
@@ -312,8 +312,8 @@ _heap_sort :: proc(data: $T/[]$E, a, b: int) where ORD(E) {
|
||||
|
||||
|
||||
@(private)
|
||||
_quick_sort_less :: proc(data: $T/[]$E, a, b, max_depth: int, less: proc(i, j: E) -> bool) {
|
||||
median3 :: proc(data: T, m1, m0, m2: int, less: proc(i, j: E) -> bool) {
|
||||
_quick_sort_less :: proc(data: $T/[]$E, a, b, max_depth: int, less: proc(i, j: E) -> bool) #no_bounds_check {
|
||||
median3 :: proc(data: T, m1, m0, m2: int, less: proc(i, j: E) -> bool) #no_bounds_check {
|
||||
if less(data[m1], data[m0]) {
|
||||
swap(data, m1, m0)
|
||||
}
|
||||
@@ -325,7 +325,7 @@ _quick_sort_less :: proc(data: $T/[]$E, a, b, max_depth: int, less: proc(i, j: E
|
||||
}
|
||||
}
|
||||
|
||||
do_pivot :: proc(data: T, lo, hi: int, less: proc(i, j: E) -> bool) -> (midlo, midhi: int) {
|
||||
do_pivot :: proc(data: T, lo, hi: int, less: proc(i, j: E) -> bool) -> (midlo, midhi: int) #no_bounds_check {
|
||||
m := int(uint(lo+hi)>>1)
|
||||
if hi-lo > 40 {
|
||||
s := (hi-lo)/8
|
||||
@@ -424,7 +424,7 @@ _quick_sort_less :: proc(data: $T/[]$E, a, b, max_depth: int, less: proc(i, j: E
|
||||
}
|
||||
|
||||
@(private)
|
||||
_insertion_sort_less :: proc(data: $T/[]$E, a, b: int, less: proc(i, j: E) -> bool) {
|
||||
_insertion_sort_less :: proc(data: $T/[]$E, a, b: int, less: proc(i, j: E) -> bool) #no_bounds_check {
|
||||
for i in a+1..<b {
|
||||
for j := i; j > a && less(data[j], data[j-1]); j -= 1 {
|
||||
swap(data, j, j-1)
|
||||
@@ -433,8 +433,8 @@ _insertion_sort_less :: proc(data: $T/[]$E, a, b: int, less: proc(i, j: E) -> bo
|
||||
}
|
||||
|
||||
@(private)
|
||||
_heap_sort_less :: proc(data: $T/[]$E, a, b: int, less: proc(i, j: E) -> bool) {
|
||||
sift_down :: proc(data: T, lo, hi, first: int, less: proc(i, j: E) -> bool) {
|
||||
_heap_sort_less :: proc(data: $T/[]$E, a, b: int, less: proc(i, j: E) -> bool) #no_bounds_check {
|
||||
sift_down :: proc(data: T, lo, hi, first: int, less: proc(i, j: E) -> bool) #no_bounds_check {
|
||||
root := lo
|
||||
for {
|
||||
child := 2*root + 1
|
||||
@@ -471,8 +471,8 @@ _heap_sort_less :: proc(data: $T/[]$E, a, b: int, less: proc(i, j: E) -> bool) {
|
||||
|
||||
|
||||
@(private)
|
||||
_quick_sort_cmp :: proc(data: $T/[]$E, a, b, max_depth: int, cmp: proc(i, j: E) -> Ordering) {
|
||||
median3 :: proc(data: T, m1, m0, m2: int, cmp: proc(i, j: E) -> Ordering) {
|
||||
_quick_sort_cmp :: proc(data: $T/[]$E, a, b, max_depth: int, cmp: proc(i, j: E) -> Ordering) #no_bounds_check {
|
||||
median3 :: proc(data: T, m1, m0, m2: int, cmp: proc(i, j: E) -> Ordering) #no_bounds_check {
|
||||
if cmp(data[m1], data[m0]) == .Less {
|
||||
swap(data, m1, m0)
|
||||
}
|
||||
@@ -484,7 +484,7 @@ _quick_sort_cmp :: proc(data: $T/[]$E, a, b, max_depth: int, cmp: proc(i, j: E)
|
||||
}
|
||||
}
|
||||
|
||||
do_pivot :: proc(data: T, lo, hi: int, cmp: proc(i, j: E) -> Ordering) -> (midlo, midhi: int) {
|
||||
do_pivot :: proc(data: T, lo, hi: int, cmp: proc(i, j: E) -> Ordering) -> (midlo, midhi: int) #no_bounds_check {
|
||||
m := int(uint(lo+hi)>>1)
|
||||
if hi-lo > 40 {
|
||||
s := (hi-lo)/8
|
||||
@@ -583,7 +583,7 @@ _quick_sort_cmp :: proc(data: $T/[]$E, a, b, max_depth: int, cmp: proc(i, j: E)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_insertion_sort_cmp :: proc(data: $T/[]$E, a, b: int, cmp: proc(i, j: E) -> Ordering) {
|
||||
_insertion_sort_cmp :: proc(data: $T/[]$E, a, b: int, cmp: proc(i, j: E) -> Ordering) #no_bounds_check {
|
||||
for i in a+1..<b {
|
||||
for j := i; j > a && cmp(data[j], data[j-1]) == .Less; j -= 1 {
|
||||
swap(data, j, j-1)
|
||||
@@ -592,8 +592,8 @@ _insertion_sort_cmp :: proc(data: $T/[]$E, a, b: int, cmp: proc(i, j: E) -> Orde
|
||||
}
|
||||
|
||||
@(private)
|
||||
_heap_sort_cmp :: proc(data: $T/[]$E, a, b: int, cmp: proc(i, j: E) -> Ordering) {
|
||||
sift_down :: proc(data: T, lo, hi, first: int, cmp: proc(i, j: E) -> Ordering) {
|
||||
_heap_sort_cmp :: proc(data: $T/[]$E, a, b: int, cmp: proc(i, j: E) -> Ordering) #no_bounds_check {
|
||||
sift_down :: proc(data: T, lo, hi, first: int, cmp: proc(i, j: E) -> Ordering) #no_bounds_check {
|
||||
root := lo
|
||||
for {
|
||||
child := 2*root + 1
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package sync2
|
||||
|
||||
import "core:time"
|
||||
|
||||
// A Wait_Group waits for a collection of threads to finish
|
||||
//
|
||||
// A Wait_Group must not be copied after first use
|
||||
@@ -45,6 +47,24 @@ wait_group_wait :: proc(wg: ^Wait_Group) {
|
||||
}
|
||||
}
|
||||
|
||||
wait_group_wait_with_timeout :: proc(wg: ^Wait_Group, duration: time.Duration) -> bool {
|
||||
if duration <= 0 {
|
||||
return false
|
||||
}
|
||||
mutex_lock(&wg.mutex)
|
||||
defer mutex_unlock(&wg.mutex)
|
||||
|
||||
if wg.counter != 0 {
|
||||
if !cond_wait_with_timeout(&wg.cond, &wg.mutex, duration) {
|
||||
return false
|
||||
}
|
||||
if wg.counter != 0 {
|
||||
panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
|
||||
// A barrier enabling multiple threads to synchronize the beginning of some computation
|
||||
@@ -57,31 +77,31 @@ wait_group_wait :: proc(wg: ^Wait_Group) {
|
||||
* import "core:sync"
|
||||
* import "core:thread"
|
||||
*
|
||||
* barrier := &sync.Barrier{};
|
||||
* barrier := &sync.Barrier{}
|
||||
*
|
||||
* main :: proc() {
|
||||
* fmt.println("Start");
|
||||
* fmt.println("Start")
|
||||
*
|
||||
* THREAD_COUNT :: 4;
|
||||
* threads: [THREAD_COUNT]^thread.Thread;
|
||||
* THREAD_COUNT :: 4
|
||||
* threads: [THREAD_COUNT]^thread.Thread
|
||||
*
|
||||
* sync.barrier_init(barrier, THREAD_COUNT);
|
||||
* defer sync.barrier_destroy(barrier);
|
||||
* sync.barrier_init(barrier, THREAD_COUNT)
|
||||
* defer sync.barrier_destroy(barrier)
|
||||
*
|
||||
*
|
||||
* for _, i in threads {
|
||||
* threads[i] = thread.create_and_start(proc(t: ^thread.Thread) {
|
||||
* // Same messages will be printed together but without any interleaving
|
||||
* fmt.println("Getting ready!");
|
||||
* sync.barrier_wait(barrier);
|
||||
* fmt.println("Off their marks they go!");
|
||||
* });
|
||||
* fmt.println("Getting ready!")
|
||||
* sync.barrier_wait(barrier)
|
||||
* fmt.println("Off their marks they go!")
|
||||
* })
|
||||
* }
|
||||
*
|
||||
* for t in threads {
|
||||
* thread.destroy(t); // join and free thread
|
||||
* thread.destroy(t) // join and free thread
|
||||
* }
|
||||
* fmt.println("Finished");
|
||||
* fmt.println("Finished")
|
||||
* }
|
||||
*
|
||||
*/
|
||||
@@ -166,7 +186,11 @@ ticket_mutex_lock :: #force_inline proc(m: ^Ticket_Mutex) {
|
||||
ticket_mutex_unlock :: #force_inline proc(m: ^Ticket_Mutex) {
|
||||
atomic_add_relaxed(&m.serving, 1)
|
||||
}
|
||||
|
||||
@(deferred_in=ticket_mutex_unlock)
|
||||
ticket_mutex_guard :: proc(m: ^Ticket_Mutex) -> bool {
|
||||
ticket_mutex_lock(m)
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
Benaphore :: struct {
|
||||
@@ -191,6 +215,12 @@ benaphore_unlock :: proc(b: ^Benaphore) {
|
||||
}
|
||||
}
|
||||
|
||||
@(deferred_in=benaphore_unlock)
|
||||
benaphore_guard :: proc(m: ^Benaphore) -> bool {
|
||||
benaphore_lock(m)
|
||||
return true
|
||||
}
|
||||
|
||||
Recursive_Benaphore :: struct {
|
||||
counter: int,
|
||||
owner: int,
|
||||
@@ -241,27 +271,37 @@ recursive_benaphore_unlock :: proc(b: ^Recursive_Benaphore) {
|
||||
// outside the lock
|
||||
}
|
||||
|
||||
@(deferred_in=recursive_benaphore_unlock)
|
||||
recursive_benaphore_guard :: proc(m: ^Recursive_Benaphore) -> bool {
|
||||
recursive_benaphore_lock(m)
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Once is a data value that will perform exactly on action.
|
||||
//
|
||||
// A Once must not be copied after first use.
|
||||
Once :: struct {
|
||||
m: Mutex,
|
||||
done: bool,
|
||||
}
|
||||
|
||||
// once_do calls the procedure fn if and only if once_do is being called for the first for this instance of Once.
|
||||
once_do :: proc(o: ^Once, fn: proc()) {
|
||||
if atomic_load_acquire(&o.done) == false {
|
||||
_once_do_slow(o, fn)
|
||||
@(cold)
|
||||
do_slow :: proc(o: ^Once, fn: proc()) {
|
||||
mutex_lock(&o.m)
|
||||
defer mutex_unlock(&o.m)
|
||||
if !o.done {
|
||||
fn()
|
||||
atomic_store_release(&o.done, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@(cold)
|
||||
_once_do_slow :: proc(o: ^Once, fn: proc()) {
|
||||
mutex_lock(&o.m)
|
||||
defer mutex_unlock(&o.m)
|
||||
if !o.done {
|
||||
fn()
|
||||
atomic_store_release(&o.done, true)
|
||||
|
||||
if atomic_load_acquire(&o.done) == false {
|
||||
do_slow(o, fn)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
//+private
|
||||
//+build darwin
|
||||
package sync2
|
||||
|
||||
import "core:c"
|
||||
import "core:time"
|
||||
|
||||
foreign import System "System.framework"
|
||||
|
||||
foreign System {
|
||||
__ulock_wait :: proc "c" (operation: u32, addr: rawptr, value: u64, timeout_ms: u32) -> c.int ---
|
||||
__ulock_wait2 :: proc "c" (operation: u32, addr: rawptr, value: u64, timeout_ns: u64, value2: u64) -> c.int ---
|
||||
__ulock_wake :: proc "c" (operation: u32, addr: rawptr, wake_value: u64) -> c.int ---
|
||||
}
|
||||
|
||||
|
||||
UL_COMPARE_AND_WAIT :: 1
|
||||
ULF_WAKE_ALL :: 0x00000100
|
||||
ULF_NO_ERRNO :: 0x01000000
|
||||
|
||||
ENOENT :: -2
|
||||
EINTR :: -4
|
||||
EFAULT :: -14
|
||||
ETIMEDOUT :: -60
|
||||
|
||||
_futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
|
||||
return _futex_wait_with_timeout(f, expected, 0)
|
||||
}
|
||||
|
||||
_futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
|
||||
timeout_ns := u64(duration)
|
||||
|
||||
s := __ulock_wait2(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, u64(expected), timeout_ns, 0)
|
||||
if s >= 0 {
|
||||
return true
|
||||
}
|
||||
switch s {
|
||||
case EINTR, EFAULT:
|
||||
return true
|
||||
case ETIMEDOUT:
|
||||
return false
|
||||
case:
|
||||
panic("futex_wait failure")
|
||||
}
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
_futex_signal :: proc(f: ^Futex) {
|
||||
loop: for {
|
||||
s := __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, 0)
|
||||
if s >= 0 {
|
||||
return
|
||||
}
|
||||
switch s {
|
||||
case EINTR, EFAULT:
|
||||
continue loop
|
||||
case ENOENT:
|
||||
return
|
||||
case:
|
||||
panic("futex_wake_single failure")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_futex_broadcast :: proc(f: ^Futex) {
|
||||
loop: for {
|
||||
s := __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO | ULF_WAKE_ALL, f, 0)
|
||||
if s >= 0 {
|
||||
return
|
||||
}
|
||||
switch s {
|
||||
case EINTR, EFAULT:
|
||||
continue loop
|
||||
case ENOENT:
|
||||
return
|
||||
case:
|
||||
panic("futex_wake_all failure")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
//+private
|
||||
//+build linux
|
||||
package sync2
|
||||
|
||||
import "core:c"
|
||||
import "core:time"
|
||||
import "core:intrinsics"
|
||||
|
||||
FUTEX_WAIT :: 0
|
||||
FUTEX_WAKE :: 1
|
||||
FUTEX_PRIVATE_FLAG :: 128
|
||||
|
||||
FUTEX_WAIT_PRIVATE :: (FUTEX_WAIT | FUTEX_PRIVATE_FLAG)
|
||||
FUTEX_WAKE_PRIVATE :: (FUTEX_WAKE | FUTEX_PRIVATE_FLAG)
|
||||
|
||||
foreign import libc "system:c"
|
||||
|
||||
foreign libc {
|
||||
__errno_location :: proc "c" () -> ^c.int ---
|
||||
}
|
||||
|
||||
ESUCCESS :: 0
|
||||
EINTR :: -4
|
||||
EAGAIN :: -11
|
||||
EFAULT :: -14
|
||||
EINVAL :: -22
|
||||
ETIMEDOUT :: -110
|
||||
|
||||
get_errno :: proc(r: int) -> int {
|
||||
if -4096 < r && r < 0 {
|
||||
return r
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
internal_futex :: proc(f: ^Futex, op: c.int, val: u32, timeout: rawptr) -> int {
|
||||
code := int(intrinsics.syscall(202, uintptr(f), uintptr(op), uintptr(val), uintptr(timeout), 0, 0))
|
||||
return get_errno(code)
|
||||
}
|
||||
|
||||
|
||||
_futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
|
||||
err := internal_futex(f, FUTEX_WAIT_PRIVATE | FUTEX_WAIT, expected, nil)
|
||||
switch err {
|
||||
case ESUCCESS, EINTR, EAGAIN, EINVAL:
|
||||
// okay
|
||||
case ETIMEDOUT:
|
||||
return false
|
||||
case EFAULT:
|
||||
fallthrough
|
||||
case:
|
||||
panic("futex_wait failure")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
_futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
|
||||
if duration <= 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
timespec_t :: struct {
|
||||
tv_sec: c.long,
|
||||
tv_nsec: c.long,
|
||||
}
|
||||
|
||||
err := internal_futex(f, FUTEX_WAIT_PRIVATE | FUTEX_WAIT, expected, ×pec_t{
|
||||
tv_sec = (c.long)(duration/1e9),
|
||||
tv_nsec = (c.long)(duration%1e9),
|
||||
})
|
||||
switch err {
|
||||
case ESUCCESS, EINTR, EAGAIN, EINVAL:
|
||||
// okay
|
||||
case ETIMEDOUT:
|
||||
return false
|
||||
case EFAULT:
|
||||
fallthrough
|
||||
case:
|
||||
panic("futex_wait_with_timeout failure")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
_futex_signal :: proc(f: ^Futex) {
|
||||
err := internal_futex(f, FUTEX_WAKE_PRIVATE | FUTEX_WAKE, 1, nil)
|
||||
switch err {
|
||||
case ESUCCESS, EINVAL, EFAULT:
|
||||
// okay
|
||||
case:
|
||||
panic("futex_wake_single failure")
|
||||
}
|
||||
}
|
||||
_futex_broadcast :: proc(f: ^Futex) {
|
||||
err := internal_futex(f, FUTEX_WAKE_PRIVATE | FUTEX_WAKE, u32(max(i32)), nil)
|
||||
switch err {
|
||||
case ESUCCESS, EINVAL, EFAULT:
|
||||
// okay
|
||||
case:
|
||||
panic("_futex_wake_all failure")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
//+private
|
||||
//+build windows
|
||||
package sync2
|
||||
|
||||
import "core:time"
|
||||
|
||||
foreign import Synchronization "system:Synchronization.lib"
|
||||
|
||||
@(default_calling_convention="stdcall")
|
||||
foreign Synchronization {
|
||||
WaitOnAddress :: proc(Address: rawptr, CompareAddress: rawptr, AddressSize: uint, Timeout: u32) -> b32 ---
|
||||
WakeByAddressSingle :: proc(Address: rawptr) ---
|
||||
WakeByAddressAll :: proc(Address: rawptr) ---
|
||||
}
|
||||
|
||||
|
||||
|
||||
_futex_wait :: proc(f: ^Futex, expect: u32) -> bool {
|
||||
expect := expect
|
||||
return bool(WaitOnAddress(f, &expect, size_of(expect), ~u32(0)))
|
||||
}
|
||||
|
||||
_futex_wait_with_timeout :: proc(f: ^Futex, expect: u32, duration: time.Duration) -> bool {
|
||||
expect := expect
|
||||
timeout := u32(0)
|
||||
if duration > 0 {
|
||||
timeout = u32(duration/1e6)
|
||||
}
|
||||
return bool(WaitOnAddress(f, &expect, size_of(expect), timeout))
|
||||
}
|
||||
|
||||
_futex_signal :: proc(f: ^Futex) {
|
||||
WakeByAddressSingle(f)
|
||||
}
|
||||
|
||||
_futex_broadcast :: proc(f: ^Futex) {
|
||||
WakeByAddressAll(f)
|
||||
}
|
||||
@@ -24,7 +24,7 @@ mutex_unlock :: proc(m: ^Mutex) {
|
||||
_mutex_unlock(m)
|
||||
}
|
||||
|
||||
// mutex_lock tries to lock m, will return true on success, and false on failure
|
||||
// mutex_try_lock tries to lock m, will return true on success, and false on failure
|
||||
mutex_try_lock :: proc(m: ^Mutex) -> bool {
|
||||
return _mutex_try_lock(m)
|
||||
}
|
||||
@@ -153,8 +153,11 @@ cond_wait :: proc(c: ^Cond, m: ^Mutex) {
|
||||
_cond_wait(c, m)
|
||||
}
|
||||
|
||||
cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, timeout: time.Duration) -> bool {
|
||||
return _cond_wait_with_timeout(c, m, timeout)
|
||||
cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
|
||||
if duration <= 0 {
|
||||
return false
|
||||
}
|
||||
return _cond_wait_with_timeout(c, m, duration)
|
||||
}
|
||||
|
||||
cond_signal :: proc(c: ^Cond) {
|
||||
@@ -166,7 +169,6 @@ cond_broadcast :: proc(c: ^Cond) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
// When waited upon, blocks until the internal count is greater than zero, then subtracts one.
|
||||
// Posting to the semaphore increases the count by one, or the provided amount.
|
||||
//
|
||||
@@ -175,11 +177,49 @@ Sema :: struct {
|
||||
impl: _Sema,
|
||||
}
|
||||
|
||||
sema_post :: proc(s: ^Sema, count := 1) {
|
||||
_sema_post(s, count)
|
||||
}
|
||||
|
||||
sema_wait :: proc(s: ^Sema) {
|
||||
_sema_wait(s)
|
||||
}
|
||||
|
||||
sema_post :: proc(s: ^Sema, count := 1) {
|
||||
_sema_post(s, count)
|
||||
sema_wait_with_timeout :: proc(s: ^Sema, duration: time.Duration) -> bool {
|
||||
return _sema_wait_with_timeout(s, duration)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Futex is a fast userspace mutual exclusion lock, using a 32-bit memory address as a hint
|
||||
//
|
||||
// An Futex must not be copied after first use
|
||||
Futex :: distinct u32
|
||||
|
||||
futex_wait :: proc(f: ^Futex, expected: u32) {
|
||||
if u32(atomic_load(f)) != expected {
|
||||
return
|
||||
}
|
||||
|
||||
assert(_futex_wait(f, expected), "futex_wait failure")
|
||||
}
|
||||
|
||||
// returns true if the wait happened within the duration, false if it exceeded the time duration
|
||||
futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
|
||||
if u32(atomic_load(f)) != expected {
|
||||
return true
|
||||
}
|
||||
if duration <= 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
return _futex_wait_with_timeout(f, expected, duration)
|
||||
}
|
||||
|
||||
futex_signal :: proc(f: ^Futex) {
|
||||
_futex_signal(f)
|
||||
}
|
||||
|
||||
futex_broadcast :: proc(f: ^Futex) {
|
||||
_futex_broadcast(f)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user