Browse Source

Merge branch 'master' into target-js_wasm32

gingerBill 3 years ago
parent
commit
321d93bff1
60 changed files with 3616 additions and 7425 deletions
  1. 42 5
      build.bat
  2. 35 43
      core/crypto/README.md
  3. 22 14
      core/crypto/_blake2/_blake2.odin
  4. 0 79
      core/crypto/_ctx/_ctx.odin
  5. 5 6
      core/crypto/_sha3/_sha3.odin
  6. 10 11
      core/crypto/_tiger/_tiger.odin
  7. 289 486
      core/crypto/blake/blake.odin
  8. 41 126
      core/crypto/blake2b/blake2b.odin
  9. 41 126
      core/crypto/blake2s/blake2s.odin
  10. BIN
      core/crypto/botan/botan.lib
  11. 0 480
      core/crypto/botan/botan.odin
  12. 0 498
      core/crypto/botan/hash.odin
  13. 110 207
      core/crypto/gost/gost.odin
  14. 188 354
      core/crypto/groestl/groestl.odin
  15. 574 390
      core/crypto/haval/haval.odin
  16. 210 376
      core/crypto/jh/jh.odin
  17. 136 284
      core/crypto/keccak/keccak.odin
  18. 49 151
      core/crypto/md2/md2.odin
  19. 73 174
      core/crypto/md4/md4.odin
  20. 75 177
      core/crypto/md5/md5.odin
  21. 202 416
      core/crypto/ripemd/ripemd.odin
  22. 84 187
      core/crypto/sha1/sha1.odin
  23. 215 407
      core/crypto/sha2/sha2.odin
  24. 127 284
      core/crypto/sha3/sha3.odin
  25. 72 185
      core/crypto/shake/shake.odin
  26. 0 487
      core/crypto/skein/skein.odin
  27. 74 180
      core/crypto/sm3/sm3.odin
  28. 106 229
      core/crypto/streebog/streebog.odin
  29. 101 219
      core/crypto/tiger/tiger.odin
  30. 100 218
      core/crypto/tiger2/tiger2.odin
  31. 115 201
      core/crypto/whirlpool/whirlpool.odin
  32. 9 9
      core/fmt/fmt.odin
  33. 0 1
      core/os/os2/file_util.odin
  34. 0 1
      core/os/os2/heap_windows.odin
  35. 26 15
      src/build_settings.cpp
  36. 4 0
      src/llvm_backend.hpp
  37. 6 0
      src/llvm_backend_const.cpp
  38. 1 10
      src/llvm_backend_debug.cpp
  39. 19 14
      src/llvm_backend_expr.cpp
  40. 2 2
      src/llvm_backend_proc.cpp
  41. 7 1
      src/main.cpp
  42. 11 3
      src/types.cpp
  43. BIN
      tests/core/botan.dll
  44. 0 183
      tests/core/crypto/test_core_crypto.odin
  45. 109 0
      vendor/raylib/README.md
  46. BIN
      vendor/raylib/linux/libraylib.a
  47. BIN
      vendor/raylib/linux/libraylib.so
  48. BIN
      vendor/raylib/linux/libraylib.so.3.7.0
  49. BIN
      vendor/raylib/linux/libraylib.so.370
  50. BIN
      vendor/raylib/linux/libraylib.so.4.0.0
  51. BIN
      vendor/raylib/linux/libraylib.so.400
  52. BIN
      vendor/raylib/macos/libraylib.3.7.0.dylib
  53. BIN
      vendor/raylib/macos/libraylib.370.dylib
  54. BIN
      vendor/raylib/macos/libraylib.4.0.0.dylib
  55. BIN
      vendor/raylib/macos/libraylib.400.dylib
  56. BIN
      vendor/raylib/macos/libraylib.a
  57. BIN
      vendor/raylib/macos/libraylib.dylib
  58. BIN
      vendor/raylib/raylib.lib
  59. 246 142
      vendor/raylib/raylib.odin
  60. 80 44
      vendor/raylib/rlgl.odin

+ 42 - 5
build.bat

@@ -69,13 +69,50 @@ set linker_settings=%libs% %linker_flags%
 del *.pdb > NUL 2> NUL
 del *.ilk > NUL 2> NUL
 
-cl %compiler_settings% "src\main.cpp" "src\libtommath.cpp" /link %linker_settings% -OUT:%exe_name%
-if %errorlevel% neq 0 goto end_of_build
+rem cl %compiler_settings% "src\main.cpp" "src\libtommath.cpp" /link %linker_settings% -OUT:%exe_name%
+rem if %errorlevel% neq 0 goto end_of_build
 
-call build_vendor.bat
-if %errorlevel% neq 0 goto end_of_build
+odin run examples/bug
+rem odin build examples/demo -use-separate-modules
+rem odin run examples/demo -o:minimal
+rem odin run examples/demo -o:size
+rem odin run examples/demo -o:speed -keep-temp-files
+rem odin run examples/demo -o:speed -keep-temp-files
 
-if %release_mode% EQU 0 odin run examples/demo
+rem set small_hellope_flags=-strict-style -no-bounds-check -default-to-nil-allocator -disable-assert -no-crt -o:size 
+rem set small_hellope_flags=-strict-style -no-bounds-check -default-to-nil-allocator -disable-assert -o:size 
+
+rem odin build examples/small_hellope %small_hellope_flags% -build-mode:asm
+rem odin run examples/small_hellope %small_hellope_flags% -keep-temp-files
+rem FOR /F "usebackq" %%A IN ('small_hellope.exe') DO echo %%~zA
+
+rem cl /nologo examples\small_hellope\small_hellope.c /O1 /link Kernel32.lib -out:small_hellope_c.exe /NODEFAULTLIB /entry:mainCRTStartup /subsystem:console
+rem small_hellope_c.exe
+rem FOR /F "usebackq" %%A IN ('small_hellope_c.exe') DO echo %%~zA
+
+
+rem odin run examples/demo
+rem odin run examples/sdl2 -vet
+rem odin check examples/all -vet
+rem odin run examples/bug
+
+rem odin check examples/wasm -strict-style -target:wasi_wasm32 
+rem odin check examples/demo -strict-style
+rem odin build examples/wasm -strict-style -target:wasi_wasm32 -keep-temp-files
+rem "C:\Program Files\WAVM\bin\wavm.exe" disassemble wasm.wasm > wasm.wast
+rem "C:\Program Files\WAVM\bin\wavm.exe" run --abi=wasi --function=weird_add wasm.wasm 2 3
+
+rem wasmer inspect wasm.wasm
+rem wasmer run wasm.wasm
+
+rem odin run examples/demo -strict-style
+
+rem call build_vendor.bat
+rem if %errorlevel% neq 0 goto end_of_build
+
+rem if %release_mode% EQU 0 odin check examples/bug -strict-style
+
+rem odin run examples/demo -strict-style
 
 del *.obj > NUL 2> NUL
 

+ 35 - 43
core/crypto/README.md

@@ -2,48 +2,43 @@
 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.
+This library offers various algorithms implemented in Odin.
 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`
+| Algorithm                                                                                                    |                  |
+|:-------------------------------------------------------------------------------------------------------------|:-----------------|
+| [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)                                            | ✔️ |
+| [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)  | ✔️ |
 
 #### High level API
-Each hash algorithm contains a procedure group named `hash`, or if the algorithm provides more than one digest size `hash_<size>`\*\*\*.  
+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.  
+\* 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(...)`.
 
@@ -51,13 +46,6 @@ Computing a 256-bit hash with 3 rounds is therefore achieved by calling `haval.h
 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
@@ -67,12 +55,16 @@ import "core:crypto/md4"
 
 main :: proc() {
     input := "foo"
-    // Compute the hash via Odin implementation
+
+    // Compute the hash, using the high level API
     computed_hash := md4.hash(input)
-    // Switch to Botan
-    md4.use_botan()
-    // Compute the hash via Botan bindings
-    computed_hash_botan := md4.hash(input)
+
+    // Compute the hash, using the low level API
+    ctx: md4.Md4_Context
+    computed_hash_low: [16]byte
+    md4.init(&ctx)
+    md4.update(&ctx, transmute([]byte)input)
+    md4.final(&ctx, computed_hash_low[:])
 }
 ```
 For example uses of all available algorithms, please see the tests within `tests/core/crypto`.

+ 22 - 14
core/crypto/_blake2/_blake2.odin

@@ -6,7 +6,6 @@ package _blake2
 
     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 BLAKE2 hashing algorithm, as defined in <https://datatracker.ietf.org/doc/html/rfc7693> and <https://www.blake2.net/>
 */
@@ -76,7 +75,7 @@ BLAKE2B_IV := [8]u64 {
 	0x1f83d9abfb41bd6b, 0x5be0cd19137e2179,
 }
 
-init_odin :: proc(ctx: ^$T) {
+init :: proc(ctx: ^$T) {
 	when T == Blake2s_Context {
 		block_size :: BLAKE2S_BLOCK_SIZE
 	} else when T == Blake2b_Context {
@@ -139,17 +138,17 @@ init_odin :: proc(ctx: ^$T) {
 	}
 	if len(ctx.cfg.key) > 0 {
 		copy(ctx.padded_key[:], ctx.cfg.key)
-		update_odin(ctx, ctx.padded_key[:])
+		update(ctx, ctx.padded_key[:])
 		ctx.is_keyed = true
 	}
 	copy(ctx.ih[:], ctx.h[:])
 	copy(ctx.h[:],  ctx.ih[:])
 	if ctx.is_keyed {
-		update_odin(ctx, ctx.padded_key[:])
+		update(ctx, ctx.padded_key[:])
 	}
 }
 
-update_odin :: proc(ctx: ^$T, p: []byte) {
+update :: proc "contextless" (ctx: ^$T, p: []byte) {
 	p := p
 	when T == Blake2s_Context {
 		block_size :: BLAKE2S_BLOCK_SIZE
@@ -161,7 +160,7 @@ update_odin :: proc(ctx: ^$T, p: []byte) {
 	if len(p) > left {
 		copy(ctx.x[ctx.nx:], p[:left])
 		p = p[left:]
-		blake2_blocks(ctx, ctx.x[:])
+		blocks(ctx, ctx.x[:])
 		ctx.nx = 0
 	}
 	if len(p) > block_size {
@@ -169,13 +168,22 @@ update_odin :: proc(ctx: ^$T, p: []byte) {
 		if n == len(p) {
 			n -= block_size
 		}
-		blake2_blocks(ctx, p[:n])
+		blocks(ctx, p[:n])
 		p = p[n:]
 	}
 	ctx.nx += copy(ctx.x[ctx.nx:], p)
 }
 
-blake2s_final_odin :: proc(ctx: $T, hash: []byte) {
+final :: proc "contextless" (ctx: ^$T, hash: []byte) {
+	when T == Blake2s_Context {
+		blake2s_final(ctx, hash)
+	}
+	when T == Blake2b_Context {
+		blake2b_final(ctx, hash)
+	}
+}
+
+blake2s_final :: proc "contextless" (ctx: ^Blake2s_Context, hash: []byte) {
 	if ctx.is_keyed {
 		for i := 0; i < len(ctx.padded_key); i += 1 {
 			ctx.padded_key[i] = 0
@@ -193,7 +201,7 @@ blake2s_final_odin :: proc(ctx: $T, hash: []byte) {
 		ctx.f[1] = 0xffffffff
 	}
 
-	blake2_blocks(ctx, ctx.x[:])
+	blocks(ctx, ctx.x[:])
 
 	j := 0
 	for s, _ in ctx.h[:(ctx.size - 1) / 4 + 1] {
@@ -205,7 +213,7 @@ blake2s_final_odin :: proc(ctx: $T, hash: []byte) {
 	}
 }
 
-blake2b_final_odin :: proc(ctx: $T, hash: []byte) {
+blake2b_final :: proc "contextless" (ctx: ^Blake2b_Context, hash: []byte) {
 	if ctx.is_keyed {
 		for i := 0; i < len(ctx.padded_key); i += 1 {
 			ctx.padded_key[i] = 0
@@ -223,7 +231,7 @@ blake2b_final_odin :: proc(ctx: $T, hash: []byte) {
 		ctx.f[1] = 0xffffffffffffffff
 	} 
 
-	blake2_blocks(ctx, ctx.x[:])
+	blocks(ctx, ctx.x[:])
 
 	j := 0
 	for s, _ in ctx.h[:(ctx.size - 1) / 8 + 1] {
@@ -239,7 +247,7 @@ blake2b_final_odin :: proc(ctx: $T, hash: []byte) {
 	}
 }
 
-blake2_blocks :: proc(ctx: ^$T, p: []byte) {
+blocks :: proc "contextless" (ctx: ^$T, p: []byte) {
 	when T == Blake2s_Context {
 		blake2s_blocks(ctx, p)
 	}
@@ -248,7 +256,7 @@ blake2_blocks :: proc(ctx: ^$T, p: []byte) {
 	}
 }
 
-blake2s_blocks :: #force_inline proc "contextless"(ctx: ^Blake2s_Context, p: []byte) {
+blake2s_blocks :: #force_inline proc "contextless" (ctx: ^Blake2s_Context, p: []byte) {
 	h0, h1, h2, h3, h4, h5, h6, h7 := ctx.h[0], ctx.h[1], ctx.h[2], ctx.h[3], ctx.h[4], ctx.h[5], ctx.h[6], ctx.h[7]
 	p := p
 	for len(p) >= BLAKE2S_BLOCK_SIZE {
@@ -1404,7 +1412,7 @@ blake2s_blocks :: #force_inline proc "contextless"(ctx: ^Blake2s_Context, p: []b
 	ctx.h[0], ctx.h[1], ctx.h[2], ctx.h[3], ctx.h[4], ctx.h[5], ctx.h[6], ctx.h[7] = h0, h1, h2, h3, h4, h5, h6, h7
 }
 
-blake2b_blocks :: #force_inline proc "contextless"(ctx: ^Blake2b_Context, p: []byte) {
+blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []byte) {
 	h0, h1, h2, h3, h4, h5, h6, h7 := ctx.h[0], ctx.h[1], ctx.h[2], ctx.h[3], ctx.h[4], ctx.h[5], ctx.h[6], ctx.h[7]
 	p := p
 	for len(p) >= BLAKE2B_BLOCK_SIZE {

+ 0 - 79
core/crypto/_ctx/_ctx.odin

@@ -1,79 +0,0 @@
-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
-}

+ 5 - 6
core/crypto/_sha3/_sha3.odin

@@ -6,7 +6,6 @@ package _sha3
 
     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.
@@ -115,14 +114,14 @@ keccakf :: proc "contextless" (st: ^[25]u64) {
     }
 }
 
-init_odin :: proc "contextless" (c: ^Sha3_Context) {
+init :: 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) {
+update :: proc "contextless" (c: ^Sha3_Context, data: []byte) {
     j := c.pt
     for i := 0; i < len(data); i += 1 {
         c.st.b[j] ~= data[i]
@@ -135,7 +134,7 @@ update_odin :: proc "contextless" (c: ^Sha3_Context, data: []byte) {
     c.pt = j
 }
 
-final_odin :: proc "contextless" (c: ^Sha3_Context, hash: []byte) {
+final :: proc "contextless" (c: ^Sha3_Context, hash: []byte) {
     if c.is_keccak {
         c.st.b[c.pt] ~= 0x01
     } else {
@@ -149,14 +148,14 @@ final_odin :: proc "contextless" (c: ^Sha3_Context, hash: []byte) {
     }
 }
 
-shake_xof_odin :: proc "contextless" (c: ^Sha3_Context) {
+shake_xof :: 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) {
+shake_out :: proc "contextless" (c: ^Sha3_Context, hash: []byte) {
     j := c.pt
     for i := 0; i < len(hash); i += 1 {
         if j >= c.rsiz {

+ 10 - 11
core/crypto/_tiger/_tiger.odin

@@ -6,7 +6,6 @@ package _tiger
 
     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/>
 */
@@ -291,7 +290,7 @@ Tiger_Context :: struct {
 	ver:    int,
 }
 
-round :: #force_inline proc "contextless"(a, b, c, x, mul: u64) -> (u64, u64, u64) {
+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]
@@ -300,7 +299,7 @@ round :: #force_inline proc "contextless"(a, b, c, x, mul: u64) -> (u64, u64, u6
 	return a, b, c
 }
 
-pass :: #force_inline proc "contextless"(a, b, c: u64, d: []u64, mul: u64) -> (x, y, z: u64) {
+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)
@@ -312,7 +311,7 @@ pass :: #force_inline proc "contextless"(a, b, c: u64, d: []u64, mul: u64) -> (x
 	return
 }
 
-key_schedule :: #force_inline proc "contextless"(x: []u64) {
+key_schedule :: #force_inline proc "contextless" (x: []u64) {
 	x[0] -= x[7] ~ 0xa5a5a5a5a5a5a5a5
 	x[1] ~= x[0]
 	x[2] += x[1]
@@ -331,7 +330,7 @@ key_schedule :: #force_inline proc "contextless"(x: []u64) {
 	x[7] -= x[6] ~ 0x0123456789abcdef
 }
 
-compress :: #force_inline proc "contextless"(ctx: ^Tiger_Context, data: []byte) {
+compress :: #force_inline proc "contextless" (ctx: ^Tiger_Context, data: []byte) {
 	a := ctx.a
 	b := ctx.b
 	c := ctx.c
@@ -346,13 +345,13 @@ compress :: #force_inline proc "contextless"(ctx: ^Tiger_Context, data: []byte)
 	ctx.c += c
 }
 
-init_odin :: proc(ctx: ^Tiger_Context) {
+init :: proc "contextless" (ctx: ^Tiger_Context) {
 	ctx.a = 0x0123456789abcdef
 	ctx.b = 0xfedcba9876543210
 	ctx.c = 0xf096a5b4c3b2e187
 }
 
-update_odin :: proc(ctx: ^Tiger_Context, input: []byte) {
+update :: proc(ctx: ^Tiger_Context, input: []byte) {
 	p := make([]byte, len(input))
 	copy(p, input)
 
@@ -380,7 +379,7 @@ update_odin :: proc(ctx: ^Tiger_Context, input: []byte) {
 	}
 }
 
-final_odin :: proc(ctx: ^Tiger_Context, hash: []byte) {
+final :: proc(ctx: ^Tiger_Context, hash: []byte) {
 	length := ctx.length
 	tmp: [64]byte
 	if ctx.ver == 1 {
@@ -391,16 +390,16 @@ final_odin :: proc(ctx: ^Tiger_Context, hash: []byte) {
 
 	size := length & 0x3f
 	if size < 56 {
-		update_odin(ctx, tmp[:56 - size])
+		update(ctx, tmp[:56 - size])
 	} else {
-		update_odin(ctx, tmp[:64 + 56 - size])
+		update(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])
+	update(ctx, tmp[:8])
 
 	for i := uint(0); i < 8; i += 1 {
 		tmp[i]      = byte(ctx.a >> (8 * i))

+ 289 - 486
core/crypto/blake/blake.odin

@@ -6,7 +6,6 @@ package blake
 
     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>
 */
@@ -14,102 +13,59 @@ package 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 {
+hash_string_224 :: proc "contextless" (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_bytes_224 :: proc "contextless" (data: []byte) -> [28]byte {
+    hash: [28]byte
+    ctx: Blake256_Context
+    ctx.is224 = true
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [28]byte
+    ctx: Blake256_Context
+    ctx.is224 = true
+    init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            update(&ctx, buf[:read])
+        } 
+    }
+    final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_224(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_224(buf[:]), ok
+        }
+    }
+    return [28]byte{}, false
 }
 
 hash_224 :: proc {
@@ -121,29 +77,53 @@ hash_224 :: proc {
 
 // hash_string_256 will hash the given input and return the
 // computed hash
-hash_string_256 :: proc(data: string) -> [32]byte {
+hash_string_256 :: proc "contextless" (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_bytes_256 :: proc "contextless" (data: []byte) -> [32]byte {
+    hash: [32]byte
+    ctx: Blake256_Context
+    ctx.is224 = false
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [32]byte
+    ctx: Blake256_Context
+    ctx.is224 = false
+    init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            update(&ctx, buf[:read])
+        } 
+    }
+    final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_256(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_256(buf[:]), ok
+        }
+    }
+    return [32]byte{}, false
 }
 
 hash_256 :: proc {
@@ -155,29 +135,53 @@ hash_256 :: proc {
 
 // hash_string_384 will hash the given input and return the
 // computed hash
-hash_string_384 :: proc(data: string) -> [48]byte {
+hash_string_384 :: proc "contextless" (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_bytes_384 :: proc "contextless" (data: []byte) -> [48]byte {
+    hash: [48]byte
+    ctx: Blake512_Context
+    ctx.is384 = true
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [48]byte
+    ctx: Blake512_Context
+    ctx.is384 = true
+    init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            update(&ctx, buf[:read])
+        } 
+    }
+    final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_384(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_384(buf[:]), ok
+        }
+    }
+    return [48]byte{}, false
 }
 
 hash_384 :: proc {
@@ -189,29 +193,53 @@ hash_384 :: proc {
 
 // hash_string_512 will hash the given input and return the
 // computed hash
-hash_string_512 :: proc(data: string) -> [64]byte {
+hash_string_512 :: proc "contextless" (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_bytes_512 :: proc "contextless" (data: []byte) -> [64]byte {
+    hash: [64]byte
+    ctx: Blake512_Context
+    ctx.is384 = false
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [64]byte
+    ctx: Blake512_Context
+    ctx.is384 = false
+    init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            update(&ctx, buf[:read])
+        } 
+    }
+    final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_512(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_512(buf[:]), ok
+        }
+    }
+    return [64]byte{}, false
 }
 
 hash_512 :: proc {
@@ -225,231 +253,188 @@ hash_512 :: proc {
     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])
-            } 
+init :: proc "contextless" (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
         }
-        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
+    } 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
         }
     }
-    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])
-            } 
+update :: proc "contextless" (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:]
         }
-        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
+        if len(data) >= BLOCKSIZE_256 {
+            n := len(data) &~ (BLOCKSIZE_256 - 1)
+            block256(ctx, data[:n])
+            data = data[n:]
         }
-    }
-    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])
-            } 
+        if len(data) > 0 {
+            ctx.nx = copy(ctx.x[:], data)
         }
-        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
+    } 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)
         }
     }
-    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[:])
+final :: proc "contextless" (ctx: ^$T, hash: []byte) {
+    when T == Blake256_Context {
+        tmp: [65]byte
+    } else when T == Blake512_Context {
+        tmp: [129]byte
     }
-    return hash
-}
+    nx     := u64(ctx.nx)
+    tmp[0]  = 0x80
+    length := (ctx.t + nx) << 3
 
-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])
-            } 
+    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})
+            }
         }
-        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
+        for i : uint = 0; i < 8; i += 1 {
+            tmp[i] = byte(length >> (56 - 8 * i))
         }
-    }
-    return [64]byte{}, false
-}
+        write_additional(ctx, tmp[0:8])
 
-@(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)
+        h := ctx.h[:]
+        if ctx.is224 {
+            h = h[0:7]
         }
-        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)
+        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)
         }
-    }
-}
-
-@(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)
+    } else when T == Blake512_Context {
+        if nx == 111 {
+            if ctx.is384 {
+                write_additional(ctx, {0x80})
+            } else {
+                write_additional(ctx, {0x81})
             }
-    }
-}
-
-@(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)
+        } 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
             }
-        case ._48, ._64:
-            if c, ok := ctx.internal_ctx.(Blake512_Context); ok {
-                final_odin(&c, hash)
+            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)
+        }
     }
 }
 
-/*
-    BLAKE implementation
-*/
-
 SIZE_224 :: 28
 SIZE_256 :: 32
 SIZE_384 :: 48
@@ -542,8 +527,8 @@ G512 :: #force_inline proc "contextless" (a, b, c, d: u64, m: [16]u64, i, j: int
     return a, b, c, d
 }
 
-block256 :: proc "contextless" (ctx: ^Blake256_Context, p: []byte) {
-    i, j: int = ---, ---
+block256 :: proc "contextless" (ctx: ^Blake256_Context, p: []byte) #no_bounds_check {
+    i, j: int     = ---, ---
     v, m: [16]u32 = ---, ---
     p := p
     for len(p) >= BLOCKSIZE_256 {
@@ -595,7 +580,7 @@ block256 :: proc "contextless" (ctx: ^Blake256_Context, p: []byte) {
 }
 
 block512 :: proc "contextless" (ctx: ^Blake512_Context, p: []byte) #no_bounds_check {
-    i, j: int = ---, ---
+    i, j: int     = ---, ---
     v, m: [16]u64 = ---, ---
     p := p
     for len(p) >= BLOCKSIZE_512 {
@@ -646,189 +631,7 @@ block512 :: proc "contextless" (ctx: ^Blake512_Context, p: []byte) #no_bounds_ch
     }
 }
 
-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) {
+write_additional :: proc "contextless" (ctx: ^$T, data: []byte) {
 	ctx.t -= u64(len(data)) << 3
-    update_odin(ctx, data)
+    update(ctx, data)
 }

+ 41 - 126
core/crypto/blake2b/blake2b.odin

@@ -6,7 +6,6 @@ package blake2b
 
     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.
@@ -15,49 +14,8 @@ package blake2b
 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
 */
@@ -71,22 +29,50 @@ hash_string :: proc(data: string) -> [64]byte {
 // 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: [64]byte
+    ctx: _blake2.Blake2b_Context
+    cfg: _blake2.Blake2_Config
+    cfg.size = _blake2.BLAKE2B_SIZE
+    ctx.cfg  = cfg
+    _blake2.init(&ctx)
+    _blake2.update(&ctx, data)
+    _blake2.final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [64]byte
+    ctx: _blake2.Blake2b_Context
+    cfg: _blake2.Blake2_Config
+    cfg.size = _blake2.BLAKE2B_SIZE
+    ctx.cfg  = cfg
+    _blake2.init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            _blake2.update(&ctx, buf[:read])
+        } 
+    }
+    _blake2.final(&ctx, hash[:])
+    return hash, true 
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes(buf[:]), ok
+        }
+    }
+    return [64]byte{}, false
 }
 
 hash :: proc {
@@ -100,87 +86,16 @@ hash :: proc {
     Low level API
 */
 
-init :: proc(ctx: ^_ctx.Hash_Context) {
-    _hash_impl->init()
-}
+Blake2b_Context :: _blake2.Blake2b_Context
 
-update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
-    _hash_impl->update(data)
+init :: proc(ctx: ^_blake2.Blake2b_Context) {
+    _blake2.init(ctx)
 }
 
-final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
-    _hash_impl->final(hash)
+update :: proc "contextless" (ctx: ^_blake2.Blake2b_Context, data: []byte) {
+    _blake2.update(ctx, data)
 }
 
-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)
-    }
+final :: proc "contextless" (ctx: ^_blake2.Blake2b_Context, hash: []byte) {
+    _blake2.final(ctx, hash)
 }

+ 41 - 126
core/crypto/blake2s/blake2s.odin

@@ -6,7 +6,6 @@ package blake2s
 
     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.
@@ -15,49 +14,8 @@ package blake2s
 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
 */
@@ -71,22 +29,50 @@ hash_string :: proc(data: string) -> [32]byte {
 // 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: [32]byte
+    ctx: _blake2.Blake2s_Context
+    cfg: _blake2.Blake2_Config
+    cfg.size = _blake2.BLAKE2S_SIZE
+    ctx.cfg  = cfg
+    _blake2.init(&ctx)
+    _blake2.update(&ctx, data)
+    _blake2.final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [32]byte
+    ctx: _blake2.Blake2s_Context
+    cfg: _blake2.Blake2_Config
+    cfg.size = _blake2.BLAKE2S_SIZE
+    ctx.cfg  = cfg
+    _blake2.init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            _blake2.update(&ctx, buf[:read])
+        } 
+    }
+    _blake2.final(&ctx, hash[:])
+    return hash, true 
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes(buf[:]), ok
+        }
+    }
+    return [32]byte{}, false
 }
 
 hash :: proc {
@@ -100,87 +86,16 @@ hash :: proc {
     Low level API
 */
 
-init :: proc(ctx: ^_ctx.Hash_Context) {
-    _hash_impl->init()
-}
+Blake2s_Context :: _blake2.Blake2b_Context
 
-update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
-    _hash_impl->update(data)
+init :: proc(ctx: ^_blake2.Blake2s_Context) {
+    _blake2.init(ctx)
 }
 
-final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
-    _hash_impl->final(hash)
+update :: proc "contextless" (ctx: ^_blake2.Blake2s_Context, data: []byte) {
+    _blake2.update(ctx, data)
 }
 
-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)
-    }
+final :: proc "contextless" (ctx: ^_blake2.Blake2s_Context, hash: []byte) {
+    _blake2.final(ctx, hash)
 }

BIN
core/crypto/botan/botan.lib


+ 0 - 480
core/crypto/botan/botan.odin

@@ -1,480 +0,0 @@
-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 - 498
core/crypto/botan/hash.odin

@@ -1,498 +0,0 @@
-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
-}

+ 110 - 207
core/crypto/gost/gost.odin

@@ -6,7 +6,6 @@ package gost
 
     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>
 */
@@ -15,42 +14,6 @@ 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
 */
@@ -64,22 +27,44 @@ hash_string :: proc(data: string) -> [32]byte {
 // 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: [32]byte
+    ctx: Gost_Context
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [32]byte
+    ctx: Gost_Context
+    init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            update(&ctx, buf[:read])
+        } 
+    }
+    final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes(buf[:]), ok
+        }
+    }
+    return [32]byte{}, false
 }
 
 hash :: proc {
@@ -93,85 +78,77 @@ hash :: proc {
     Low level API
 */
 
-init :: proc(ctx: ^_ctx.Hash_Context) {
-    _hash_impl->init()
-}
+init :: proc "contextless" (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 },
+    }
 
-update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
-    _hash_impl->update(data)
+    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)
+        }
+    }
 }
 
-final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
-    _hash_impl->final(hash)
-}
+update :: proc(ctx: ^Gost_Context, data: []byte) {
+    length := byte(len(data))
+    j: byte
 
-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[:])
+    i := ctx.partial_bytes
+    for i < 32 && j < length {
+        ctx.partial[i] = data[j]
+        i, j = i + 1, j + 1
     }
-    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
+    if i < 32 {
+        ctx.partial_bytes = i
+        return
     }
-}
+    bytes(ctx, ctx.partial[:], 256)
 
-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
-        }
+    for (j + 32) < length {
+        bytes(ctx, data[j:], 256)
+        j += 32
     }
-    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)
+    i = 0
+    for j < length {
+        ctx.partial[i] = data[j]
+        i, j = i + 1, j + 1
     }
+    ctx.partial_bytes = i
 }
 
-@(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)
+final :: proc(ctx: ^Gost_Context, hash: []byte) {
+    if ctx.partial_bytes > 0 {
+        mem.set(&ctx.partial[ctx.partial_bytes], 0, 32 - int(ctx.partial_bytes))
+        bytes(ctx, ctx.partial[:], u32(ctx.partial_bytes) << 3)
     }
-}
+  
+    compress(ctx.hash[:], ctx.len[:])
+    compress(ctx.hash[:], ctx.sum[:])
 
-@(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)
+    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)
     }
 }
 
@@ -187,12 +164,12 @@ Gost_Context :: struct {
     partial_bytes: byte,
 }
 
-SBOX_1 : [256]u32
-SBOX_2 : [256]u32
-SBOX_3 : [256]u32
-SBOX_4 : [256]u32
+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) {
+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]
@@ -201,30 +178,30 @@ GOST_ENCRYPT_ROUND :: #force_inline proc "contextless"(l, r, t, k1, k2: u32) ->
     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])
+ENCRYPT :: #force_inline proc "contextless" (a, b, c: u32, key: []u32) -> (l, r, t: u32) {
+    l, r, t = ENCRYPT_ROUND(a, b, c, key[0], key[1])
+    l, r, t = ENCRYPT_ROUND(l, r, t, key[2], key[3])
+    l, r, t = ENCRYPT_ROUND(l, r, t, key[4], key[5])
+    l, r, t = ENCRYPT_ROUND(l, r, t, key[6], key[7])
+    l, r, t = ENCRYPT_ROUND(l, r, t, key[0], key[1])
+    l, r, t = ENCRYPT_ROUND(l, r, t, key[2], key[3])
+    l, r, t = ENCRYPT_ROUND(l, r, t, key[4], key[5])
+    l, r, t = ENCRYPT_ROUND(l, r, t, key[6], key[7])
+    l, r, t = ENCRYPT_ROUND(l, r, t, key[0], key[1])
+    l, r, t = ENCRYPT_ROUND(l, r, t, key[2], key[3])
+    l, r, t = ENCRYPT_ROUND(l, r, t, key[4], key[5])
+    l, r, t = ENCRYPT_ROUND(l, r, t, key[6], key[7])
+    l, r, t = ENCRYPT_ROUND(l, r, t, key[7], key[6])
+    l, r, t = ENCRYPT_ROUND(l, r, t, key[5], key[4])
+    l, r, t = ENCRYPT_ROUND(l, r, t, key[3], key[2])
+    l, r, t = 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) {
+bytes :: proc(ctx: ^Gost_Context, buf: []byte, bits: u32) {
     a, c: u32
     m: [8]u32
 
@@ -237,14 +214,14 @@ gost_bytes :: proc(ctx: ^Gost_Context, buf: []byte, bits: u32) {
         c = c < a ? 1 : 0
     }
 
-    gost_compress(ctx.hash[:], m[:])
+    compress(ctx.hash[:], m[:])
     ctx.len[0] += bits
     if ctx.len[0] < bits {
         ctx.len[1] += 1
     }
 }
 
-gost_compress :: proc(h, m: []u32) {
+compress :: proc(h, m: []u32) {
     key, u, v, w, s: [8]u32
 
     copy(u[:], h)
@@ -272,7 +249,7 @@ gost_compress :: proc(h, m: []u32) {
         r := h[i]
         l := h[i + 1]
         t: u32
-        l, r, t = GOST_ENCRYPT(l, r, 0, key[:])
+        l, r, t = ENCRYPT(l, r, 0, key[:])
 
         s[i] = r
         s[i + 1] = l
@@ -380,78 +357,4 @@ gost_compress :: proc(h, m: []u32) {
     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)
-    }
 }

+ 188 - 354
core/crypto/groestl/groestl.odin

@@ -6,7 +6,6 @@ package groestl
 
     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>
 */
@@ -14,70 +13,6 @@ package groestl
 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
 */
@@ -91,22 +26,46 @@ hash_string_224 :: proc(data: string) -> [28]byte {
 // 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: [28]byte
+    ctx: Groestl_Context
+    ctx.hashbitlen = 224
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [28]byte
+    ctx: Groestl_Context
+    ctx.hashbitlen = 224
+    init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            update(&ctx, buf[:read])
+        } 
+    }
+    final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_224(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_224(buf[:]), ok
+        }
+    }
+    return [28]byte{}, false
 }
 
 hash_224 :: proc {
@@ -125,22 +84,46 @@ hash_string_256 :: proc(data: string) -> [32]byte {
 // 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: [32]byte
+    ctx: Groestl_Context
+    ctx.hashbitlen = 256
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [32]byte
+    ctx: Groestl_Context
+    ctx.hashbitlen = 256
+    init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            update(&ctx, buf[:read])
+        } 
+    }
+    final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_256(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_256(buf[:]), ok
+        }
+    }
+    return [32]byte{}, false
 }
 
 hash_256 :: proc {
@@ -159,22 +142,46 @@ hash_string_384 :: proc(data: string) -> [48]byte {
 // 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: [48]byte
+    ctx: Groestl_Context
+    ctx.hashbitlen = 384
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [48]byte
+    ctx: Groestl_Context
+    ctx.hashbitlen = 384
+    init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            update(&ctx, buf[:read])
+        } 
+    }
+    final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_384(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_384(buf[:]), ok
+        }
+    }
+    return [48]byte{}, false
 }
 
 hash_384 :: proc {
@@ -193,22 +200,46 @@ hash_string_512 :: proc(data: string) -> [64]byte {
 // 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: [64]byte
+    ctx: Groestl_Context
+    ctx.hashbitlen = 512
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [64]byte
+    ctx: Groestl_Context
+    ctx.hashbitlen = 512
+    init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            update(&ctx, buf[:read])
+        } 
+    }
+    final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_512(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_512(buf[:]), ok
+        }
+    }
+    return [64]byte{}, false
 }
 
 hash_512 :: proc {
@@ -222,201 +253,101 @@ hash_512 :: proc {
     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
+init :: proc(ctx: ^Groestl_Context) {
+    assert(ctx.hashbitlen == 224 || ctx.hashbitlen == 256 || ctx.hashbitlen == 384 || ctx.hashbitlen == 512, "hashbitlen must be set to 224, 256, 384 or 512")
+    if ctx.hashbitlen <= 256 {
+        ctx.rounds    = 10
+        ctx.columns   = 8
+        ctx.statesize = 64
     } else {
-        return hash, false
+        ctx.rounds    = 14
+        ctx.columns   = 16
+        ctx.statesize = 128
     }
-}
-
-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
-        }
+    for i := 8 - size_of(i32); i < 8; i += 1 {
+        ctx.chaining[i][ctx.columns - 1] = byte(ctx.hashbitlen >> (8 * (7 - uint(i))))
     }
-    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
-}
+update :: proc(ctx: ^Groestl_Context, data: []byte) {
+    databitlen := len(data) * 8
+    msglen     := databitlen / 8
+    rem        := databitlen % 8
 
-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])
-            } 
+    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]
         }
-        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
+        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
         }
-    }
-    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[:])
+        ctx.buf_ptr = 0
+        transform(ctx, ctx.buffer[:], u32(ctx.statesize))
     }
-    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
+    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
     }
-}
-
-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
-        }
+    
+    if rem != 0 {
+        ctx.bits_in_last_byte    = rem
+        ctx.buffer[ctx.buf_ptr]  = data[i]
+        ctx.buf_ptr             += 1
     }
-    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
-}
+final :: proc(ctx: ^Groestl_Context, hash: []byte) {
+    hashbytelen := ctx.hashbitlen / 8
 
-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
+    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 {
-        return hash, false
+        ctx.buffer[ctx.buf_ptr]  = 0x80
+        ctx.buf_ptr             += 1
     }
-}
 
-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
+    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
     }
-    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)
+    for ctx.buf_ptr < ctx.statesize - 8 {
+        ctx.buffer[ctx.buf_ptr]  = 0
+        ctx.buf_ptr             += 1
     }
-}
 
-@(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)
+    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
     }
-}
 
-@(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)
+    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]
     }
 }
 
@@ -631,100 +562,3 @@ add_roundconstant :: proc(x: [][16]byte, columns: int, round: byte, v: Groestl_V
             }
     }
 }
-
-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
+ 574 - 390
core/crypto/haval/haval.odin


+ 210 - 376
core/crypto/jh/jh.odin

@@ -6,7 +6,6 @@ package jh
 
     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>
 */
@@ -14,70 +13,6 @@ package jh
 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
 */
@@ -91,22 +26,46 @@ hash_string_224 :: proc(data: string) -> [28]byte {
 // 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: [28]byte
+    ctx: Jh_Context
+    ctx.hashbitlen = 224
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [28]byte
+    ctx: Jh_Context
+    ctx.hashbitlen = 224
+    init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            update(&ctx, buf[:read])
+        } 
+    }
+    final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_224(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_224(buf[:]), ok
+        }
+    }
+    return [28]byte{}, false
 }
 
 hash_224 :: proc {
@@ -125,22 +84,46 @@ hash_string_256 :: proc(data: string) -> [32]byte {
 // 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: [32]byte
+    ctx: Jh_Context
+    ctx.hashbitlen = 256
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [32]byte
+    ctx: Jh_Context
+    ctx.hashbitlen = 256
+    init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            update(&ctx, buf[:read])
+        } 
+    }
+    final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_256(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_256(buf[:]), ok
+        }
+    }
+    return [32]byte{}, false
 }
 
 hash_256 :: proc {
@@ -159,22 +142,46 @@ hash_string_384 :: proc(data: string) -> [48]byte {
 // 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: [48]byte
+    ctx: Jh_Context
+    ctx.hashbitlen = 384
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [48]byte
+    ctx: Jh_Context
+    ctx.hashbitlen = 384
+    init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            update(&ctx, buf[:read])
+        } 
+    }
+    final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_384(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_384(buf[:]), ok
+        }
+    }
+    return [48]byte{}, false
 }
 
 hash_384 :: proc {
@@ -193,22 +200,46 @@ hash_string_512 :: proc(data: string) -> [64]byte {
 // 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: [64]byte
+    ctx: Jh_Context
+    ctx.hashbitlen = 512
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [64]byte
+    ctx: Jh_Context
+    ctx.hashbitlen = 512
+    init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            update(&ctx, buf[:read])
+        } 
+    }
+    final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_512(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_512(buf[:]), ok
+        }
+    }
+    return [64]byte{}, false
 }
 
 hash_512 :: proc {
@@ -222,201 +253,98 @@ hash_512 :: proc {
     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
+init :: proc(ctx: ^Jh_Context) {
+    assert(ctx.hashbitlen == 224 || ctx.hashbitlen == 256 || ctx.hashbitlen == 384 || ctx.hashbitlen == 512, "hashbitlen must be set to 224, 256, 384 or 512")
+    ctx.H[1] = byte(ctx.hashbitlen)      & 0xff
+    ctx.H[0] = byte(ctx.hashbitlen >> 8) & 0xff
+    F8(ctx)
 }
 
-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
-    }
-}
+update :: proc(ctx: ^Jh_Context, data: []byte) {
+    databitlen     := u64(len(data)) * 8
+    ctx.databitlen += databitlen
+    i              := u64(0)
 
-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
-        }
+    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
     }
-    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[:])
+    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)
+        F8(ctx)
+        ctx.buffer_size = 0
     }
-    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
+    for databitlen >= 512 {
+        copy(ctx.buffer[:], data[i:i + 64])
+        F8(ctx)
+        i += 64
+        databitlen -= 512
     }
-}
 
-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
+    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
     }
-    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 :: proc(ctx: ^Jh_Context, hash: []byte) {
+    if ctx.databitlen & 0x1ff == 0 {
+        for i := 0; i < 64; i += 1 {
+            ctx.buffer[i] = 0
         }
-        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))
+        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
+        F8(ctx)
     } 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])
-            } 
+        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
+            }
         }
-        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
+        ctx.buffer[(ctx.databitlen & 0x1ff) >> 3] |= 1 << (7 - (ctx.databitlen & 7))
+        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
+        F8(ctx)
     }
-    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)
+    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])
     }
 }
 
@@ -424,7 +352,7 @@ _final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
     JH implementation
 */
 
-JH_ROUNDCONSTANT_ZERO := [64]byte {
+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,
@@ -435,7 +363,7 @@ JH_ROUNDCONSTANT_ZERO := [64]byte {
     0x0, 0x6, 0x6, 0x7, 0x3, 0x2, 0x2, 0xa,
 }
 
-JH_S := [2][16]byte {
+SBOX := [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},
 }
@@ -450,7 +378,7 @@ Jh_Context :: struct {
     buffer:        [64]byte,
 }
 
-JH_E8_finaldegroup :: proc(ctx: ^Jh_Context) {
+E8_finaldegroup :: proc(ctx: ^Jh_Context) {
     t0,t1,t2,t3: byte
     tem: [256]byte
     for i := 0; i < 128; i += 1 {
@@ -473,11 +401,11 @@ JH_E8_finaldegroup :: proc(ctx: ^Jh_Context) {
     }
 }
 
-jh_update_roundconstant :: proc(ctx: ^Jh_Context) {
+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]]
+        tem[i] = SBOX[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
@@ -499,14 +427,14 @@ jh_update_roundconstant :: proc(ctx: ^Jh_Context) {
     }
 }
 
-JH_R8 :: proc(ctx: ^Jh_Context) {
+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]]
+        tem[i] = SBOX[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
@@ -528,7 +456,7 @@ JH_R8 :: proc(ctx: ^Jh_Context) {
     }
 }
 
-JH_E8_initialgroup :: proc(ctx: ^Jh_Context) {
+E8_initialgroup :: proc(ctx: ^Jh_Context) {
     t0, t1, t2, t3: byte
     tem:            [256]byte
     for i := u32(0); i < 256; i += 1 {
@@ -544,118 +472,24 @@ JH_E8_initialgroup :: proc(ctx: ^Jh_Context) {
     }
 }
 
-JH_E8 :: proc(ctx: ^Jh_Context) {
+E8 :: proc(ctx: ^Jh_Context) {
     for i := 0; i < 64; i += 1 {
-        ctx.roundconstant[i] = JH_ROUNDCONSTANT_ZERO[i]
+        ctx.roundconstant[i] = ROUNDCONSTANT_ZERO[i]
     }
-    JH_E8_initialgroup(ctx)
+    E8_initialgroup(ctx)
     for i := 0; i < 42; i += 1 {
-        JH_R8(ctx)
-        jh_update_roundconstant(ctx)
+        R8(ctx)
+        update_roundconstant(ctx)
     }
-    JH_E8_finaldegroup(ctx)
+    E8_finaldegroup(ctx)
 }
 
-JH_F8 :: proc(ctx: ^Jh_Context) {
+F8 :: proc(ctx: ^Jh_Context) {
     for i := 0; i < 64; i += 1 {
         ctx.H[i] ~= ctx.buffer[i]
     }
-    JH_E8(ctx)
+    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])
-    }
-}

+ 136 - 284
core/crypto/keccak/keccak.odin

@@ -6,7 +6,6 @@ package keccak
 
     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.
@@ -15,57 +14,8 @@ package keccak
 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
@@ -80,22 +30,48 @@ hash_string_224 :: proc(data: string) -> [28]byte {
 // 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: [28]byte
+    ctx: _sha3.Sha3_Context
+    ctx.mdlen = 28
+    ctx.is_keccak = true
+    _sha3.init(&ctx)
+    _sha3.update(&ctx, data)
+    _sha3.final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [28]byte
+    ctx: _sha3.Sha3_Context
+    ctx.mdlen = 28
+    ctx.is_keccak = true
+    _sha3.init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            _sha3.update(&ctx, buf[:read])
+        } 
+    }
+    _sha3.final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_224(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_224(buf[:]), ok
+        }
+    }
+    return [28]byte{}, false
 }
 
 hash_224 :: proc {
@@ -114,22 +90,48 @@ hash_string_256 :: proc(data: string) -> [32]byte {
 // 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: [32]byte
+    ctx: _sha3.Sha3_Context
+    ctx.mdlen = 32
+    ctx.is_keccak = true
+    _sha3.init(&ctx)
+    _sha3.update(&ctx, data)
+    _sha3.final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [32]byte
+    ctx: _sha3.Sha3_Context
+    ctx.mdlen = 32
+    ctx.is_keccak = true
+    _sha3.init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            _sha3.update(&ctx, buf[:read])
+        } 
+    }
+    _sha3.final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_256(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_256(buf[:]), ok
+        }
+    }
+    return [32]byte{}, false
 }
 
 hash_256 :: proc {
@@ -148,22 +150,48 @@ hash_string_384 :: proc(data: string) -> [48]byte {
 // 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: [48]byte
+    ctx: _sha3.Sha3_Context
+    ctx.mdlen = 48
+    ctx.is_keccak = true
+    _sha3.init(&ctx)
+    _sha3.update(&ctx, data)
+    _sha3.final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [48]byte
+    ctx: _sha3.Sha3_Context
+    ctx.mdlen = 48
+    ctx.is_keccak = true
+    _sha3.init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            _sha3.update(&ctx, buf[:read])
+        } 
+    }
+    _sha3.final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_384(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_384(buf[:]), ok
+        }
+    }
+    return [48]byte{}, false
 }
 
 hash_384 :: proc {
@@ -182,22 +210,48 @@ hash_string_512 :: proc(data: string) -> [64]byte {
 // 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: [64]byte
+    ctx: _sha3.Sha3_Context
+    ctx.mdlen = 64
+    ctx.is_keccak = true
+    _sha3.init(&ctx)
+    _sha3.update(&ctx, data)
+    _sha3.final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [64]byte
+    ctx: _sha3.Sha3_Context
+    ctx.mdlen = 64
+    ctx.is_keccak = true
+    _sha3.init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            _sha3.update(&ctx, buf[:read])
+        } 
+    }
+    _sha3.final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_512(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_512(buf[:]), ok
+        }
+    }
+    return [64]byte{}, false
 }
 
 hash_512 :: proc {
@@ -211,219 +265,17 @@ hash_512 :: proc {
     Low level API
 */
 
-init :: proc(ctx: ^_ctx.Hash_Context) {
-    _hash_impl->init()
-}
+Sha3_Context :: _sha3.Sha3_Context
 
-update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
-    _hash_impl->update(data)
+init :: proc(ctx: ^_sha3.Sha3_Context) {
+    ctx.is_keccak = true
+    _sha3.init(ctx)
 }
 
-final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
-    _hash_impl->final(hash)
+update :: proc "contextless" (ctx: ^_sha3.Sha3_Context, data: []byte) {
+    _sha3.update(ctx, data)
 }
 
-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)
-    }
+final :: proc "contextless" (ctx: ^_sha3.Sha3_Context, hash: []byte) {
+    _sha3.final(ctx, hash)
 }

+ 49 - 151
core/crypto/md2/md2.odin

@@ -6,7 +6,6 @@ package md2
 
     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>
 */
@@ -14,48 +13,6 @@ package md2
 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
 */
@@ -69,22 +26,44 @@ hash_string :: proc(data: string) -> [16]byte {
 // 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: [16]byte
+	ctx: Md2_Context
+    // init(&ctx) No-op
+    update(&ctx, data)
+    final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [16]byte
+	ctx: Md2_Context
+	// init(&ctx) No-op
+	buf := make([]byte, 512)
+	defer delete(buf)
+	read := 1
+	for read > 0 {
+	    read, _ = s->impl_read(buf)
+	    if read > 0 {
+			update(&ctx, buf[:read])
+	    } 
+	}
+	final(&ctx, hash[:])
+	return hash, true
 }
 
 // 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)
+	if !load_at_once {
+        return hash_stream(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes(buf[:]), ok
+        }
+    }
+    return [16]byte{}, false
 }
 
 hash :: proc {
@@ -98,85 +77,32 @@ hash :: proc {
     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
+@(warning="Init is a no-op for MD2")
+init :: proc(ctx: ^Md2_Context) {
+	// No action needed here
 }
 
-@(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)
-    }
+update :: 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
+		}
+	}
 }
 
-@(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)
+final :: 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
     }
-}
-
-@(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)
+	transform(ctx, ctx.data[:])
+	transform(ctx, ctx.checksum[:])
+    for i := 0; i < 16; i += 1 {
+        hash[i] = ctx.state[i]
     }
 }
 
@@ -232,31 +158,3 @@ transform :: proc(ctx: ^Md2_Context, data: []byte) {
 		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]
-    }
-}

+ 73 - 174
core/crypto/md4/md4.odin

@@ -16,47 +16,6 @@ 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
@@ -71,22 +30,44 @@ hash_string :: proc(data: string) -> [16]byte {
 // 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: [16]byte
+    ctx: Md4_Context
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [16]byte
+    ctx: Md4_Context
+    init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            update(&ctx, buf[:read])
+        } 
+    }
+    final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes(buf[:]), ok
+        }
+    }
+    return [16]byte{}, false
 }
 
 hash :: proc {
@@ -100,85 +81,61 @@ hash :: proc {
     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
+init :: proc(ctx: ^Md4_Context) {
+    ctx.state[0] = 0x67452301
+    ctx.state[1] = 0xefcdab89
+    ctx.state[2] = 0x98badcfe
+    ctx.state[3] = 0x10325476
 }
 
-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])
-            } 
+update :: 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(&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
+final :: 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)
     }
-    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)
-    }
-}
+    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[:])
 
-@(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)
+    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
     }
 }
 
@@ -282,61 +239,3 @@ transform :: proc(ctx: ^Md4_Context, data: []byte) {
     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
-    }
-}

+ 75 - 177
core/crypto/md5/md5.odin

@@ -6,7 +6,6 @@ package md5
 
     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>
 */
@@ -16,47 +15,6 @@ 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
@@ -71,22 +29,44 @@ hash_string :: proc(data: string) -> [16]byte {
 // 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: [16]byte
+    ctx: Md5_Context
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [16]byte
+    ctx: Md5_Context
+    init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            update(&ctx, buf[:read])
+        } 
+    }
+    final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes(buf[:]), ok
+        }
+    }
+    return [16]byte{}, false
 }
 
 hash :: proc {
@@ -100,85 +80,63 @@ hash :: proc {
     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
-    }
+init :: proc(ctx: ^Md5_Context) {
+    ctx.state[0] = 0x67452301
+    ctx.state[1] = 0xefcdab89
+    ctx.state[2] = 0x98badcfe
+    ctx.state[3] = 0x10325476
 }
 
-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
+update :: 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
         }
     }
-    return [16]byte{}, false
 }
 
-@(private)
-_create_md5_ctx :: #force_inline proc() {
-    ctx: Md5_Context
-    _hash_impl.internal_ctx = ctx
-    _hash_impl.hash_size    = ._16
-}
+final :: proc(ctx: ^Md5_Context, hash: []byte){
+    i : u32
+    i = ctx.datalen
 
-@(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)
+    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)
     }
-}
 
-@(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)
-    }
-}
+    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[:])
 
-@(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)
+    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
     }
 }
 
@@ -303,63 +261,3 @@ transform :: proc(ctx: ^Md5_Context, data: []byte) {
     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
-    }
-}

+ 202 - 416
core/crypto/ripemd/ripemd.odin

@@ -6,7 +6,6 @@ package ripemd
 
     List of contributors:
         zhibog, dotbmp:  Initial implementation.
-        Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
 
     Implementation for the RIPEMD hashing algorithm as defined in <https://homes.esat.kuleuven.be/~bosselae/ripemd160.html>
 */
@@ -15,56 +14,6 @@ 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_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_32  = hash_bytes_odin_32
-    ctx.hash_file_32   = hash_file_odin_32
-    ctx.hash_stream_32 = hash_stream_odin_32
-    ctx.hash_bytes_40  = hash_bytes_odin_40
-    ctx.hash_file_40   = hash_file_odin_40
-    ctx.hash_stream_40 = hash_stream_odin_40
-    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_RIPEMD_160)
-}
-
-// 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
@@ -79,22 +28,44 @@ hash_string_128 :: proc(data: string) -> [16]byte {
 // hash_bytes_128 will hash the given input and return the
 // computed hash
 hash_bytes_128 :: proc(data: []byte) -> [16]byte {
-    _create_ripemd_ctx(16)
-    return _hash_impl->hash_bytes_16(data)
+    hash: [16]byte
+    ctx: Ripemd128_Context
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, hash[:])
+    return hash
 }
 
 // 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_ripemd_ctx(16)
-    return _hash_impl->hash_stream_16(s)
+    hash: [16]byte
+    ctx: Ripemd128_Context
+    init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            update(&ctx, buf[:read])
+        } 
+    }
+    final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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_ripemd_ctx(16)
-    return _hash_impl->hash_file_16(hd, load_at_once)
+    if !load_at_once {
+        return hash_stream_128(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_128(buf[:]), ok
+        }
+    }
+    return [16]byte{}, false
 }
 
 hash_128 :: proc {
@@ -113,22 +84,44 @@ hash_string_160 :: proc(data: string) -> [20]byte {
 // hash_bytes_160 will hash the given input and return the
 // computed hash
 hash_bytes_160 :: proc(data: []byte) -> [20]byte {
-    _create_ripemd_ctx(20)
-    return _hash_impl->hash_bytes_20(data)
+    hash: [20]byte
+    ctx: Ripemd160_Context
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, hash[:])
+    return hash
 }
 
 // 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_ripemd_ctx(20)
-    return _hash_impl->hash_stream_20(s)
+    hash: [20]byte
+    ctx: Ripemd160_Context
+    init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            update(&ctx, buf[:read])
+        } 
+    }
+    final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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_ripemd_ctx(20)
-    return _hash_impl->hash_file_20(hd, load_at_once)
+    if !load_at_once {
+        return hash_stream_160(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_160(buf[:]), ok
+        }
+    }
+    return [20]byte{}, false
 }
 
 hash_160 :: proc {
@@ -147,22 +140,44 @@ hash_string_256 :: proc(data: string) -> [32]byte {
 // hash_bytes_256 will hash the given input and return the
 // computed hash
 hash_bytes_256 :: proc(data: []byte) -> [32]byte {
-    _create_ripemd_ctx(32)
-    return _hash_impl->hash_bytes_32(data)
+    hash: [32]byte
+    ctx: Ripemd256_Context
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, hash[:])
+    return hash
 }
 
 // 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_ripemd_ctx(32)
-    return _hash_impl->hash_stream_32(s)
+    hash: [32]byte
+    ctx: Ripemd256_Context
+    init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            update(&ctx, buf[:read])
+        } 
+    }
+    final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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_ripemd_ctx(32)
-    return _hash_impl->hash_file_32(hd, load_at_once)
+    if !load_at_once {
+        return hash_stream_256(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_256(buf[:]), ok
+        }
+    }
+    return [32]byte{}, false
 }
 
 hash_256 :: proc {
@@ -181,22 +196,44 @@ hash_string_320 :: proc(data: string) -> [40]byte {
 // hash_bytes_320 will hash the given input and return the
 // computed hash
 hash_bytes_320 :: proc(data: []byte) -> [40]byte {
-    _create_ripemd_ctx(40)
-    return _hash_impl->hash_bytes_40(data)
+    hash: [40]byte
+    ctx: Ripemd320_Context
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, hash[:])
+    return hash
 }
 
 // hash_stream_320 will read the stream in chunks and compute a
 // hash from its contents
 hash_stream_320 :: proc(s: io.Stream) -> ([40]byte, bool) {
-    _create_ripemd_ctx(40)
-    return _hash_impl->hash_stream_40(s)
+    hash: [40]byte
+    ctx: Ripemd320_Context
+    init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            update(&ctx, buf[:read])
+        } 
+    }
+    final(&ctx, hash[:])
+    return hash, true
 }
 
 // hash_file_320 will read the file provided by the given handle
 // and compute a hash
 hash_file_320 :: proc(hd: os.Handle, load_at_once := false) -> ([40]byte, bool) {
-    _create_ripemd_ctx(40)
-    return _hash_impl->hash_file_40(hd, load_at_once)
+    if !load_at_once {
+        return hash_stream_320(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_320(buf[:]), ok
+        }
+    }
+    return [40]byte{}, false
 }
 
 hash_320 :: proc {
@@ -206,261 +243,122 @@ hash_320 :: proc {
     hash_string_320,
 }
 
-hash_bytes_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
-    hash: [16]byte
-    if c, ok := ctx.internal_ctx.(Ripemd128_Context); ok {
-        init_odin(&c)
-        update_odin(&c, data)
-        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.(Ripemd128_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
-    }
-}
+/*
+    Low level API
+*/
 
-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
-        }
+init :: proc(ctx: ^$T) {
+    when T == Ripemd128_Context {
+        ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3] = S0, S1, S2, S3
+    } else when T == Ripemd160_Context {
+        ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3], ctx.s[4] = S0, S1, S2, S3, S4
+    } else when T == Ripemd256_Context {
+        ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3] = S0, S1, S2, S3
+        ctx.s[4], ctx.s[5], ctx.s[6], ctx.s[7] = S5, S6, S7, S8
+    } else when T == Ripemd320_Context {
+        ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3], ctx.s[4] = S0, S1, S2, S3, S4
+        ctx.s[5], ctx.s[6], ctx.s[7], ctx.s[8], ctx.s[9] = S5, S6, S7, S8, S9
     }
-    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.(Ripemd160_Context); ok {
-        init_odin(&c)
-        update_odin(&c, data)
-        final_odin(&c, hash[:])
-    }
-    return hash
-}
+update :: proc(ctx: ^$T, data: []byte) {
+    ctx.tc += u64(len(data))
+    data := data
+    if ctx.nx > 0 {
+        n := len(data)
 
-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.(Ripemd160_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])
-            } 
+        when T == Ripemd128_Context {
+            if n > RIPEMD_128_BLOCK_SIZE - ctx.nx {
+                n = RIPEMD_128_BLOCK_SIZE - ctx.nx
+            }
+        } else when T == Ripemd160_Context {
+            if n > RIPEMD_160_BLOCK_SIZE - ctx.nx {
+                n = RIPEMD_160_BLOCK_SIZE - ctx.nx
+            }
+        } else when T == Ripemd256_Context{
+            if n > RIPEMD_256_BLOCK_SIZE - ctx.nx {
+                n = RIPEMD_256_BLOCK_SIZE - ctx.nx
+            }
+        } else when T == Ripemd320_Context{
+            if n > RIPEMD_320_BLOCK_SIZE - ctx.nx {
+                n = RIPEMD_320_BLOCK_SIZE - ctx.nx
+            }
         }
-        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
+        for i := 0; i < n; i += 1 {
+            ctx.x[ctx.nx + i] = data[i]
         }
-    }
-    return [20]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.(Ripemd256_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.(Ripemd256_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])
-            } 
+        ctx.nx += n
+        when T == Ripemd128_Context {
+            if ctx.nx == RIPEMD_128_BLOCK_SIZE {
+                block(ctx, ctx.x[0:])
+                ctx.nx = 0
+            }
+        } else when T == Ripemd160_Context {
+            if ctx.nx == RIPEMD_160_BLOCK_SIZE {
+                block(ctx, ctx.x[0:])
+                ctx.nx = 0
+            }
+        } else when T == Ripemd256_Context{
+            if ctx.nx == RIPEMD_256_BLOCK_SIZE {
+                block(ctx, ctx.x[0:])
+                ctx.nx = 0
+            }
+        } else when T == Ripemd320_Context{
+            if ctx.nx == RIPEMD_320_BLOCK_SIZE {
+                block(ctx, ctx.x[0:])
+                ctx.nx = 0
+            }
         }
-        final_odin(&c, hash[:])
-        return hash, true
-    } else {
-        return hash, false
+        data = data[n:]
     }
-}
-
-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
-        }
+    n := block(ctx, data)
+    data = data[n:]
+    if len(data) > 0 {
+        ctx.nx = copy(ctx.x[:], data)
     }
-    return [32]byte{}, false
 }
 
-hash_bytes_odin_40 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [40]byte {
-    hash: [40]byte
-    if c, ok := ctx.internal_ctx.(Ripemd320_Context); ok {
-        init_odin(&c)
-        update_odin(&c, data)
-        final_odin(&c, hash[:])
-    }
-    return hash
-}
+final :: proc(ctx: ^$T, hash: []byte) {
+    d := ctx
+    tc := d.tc
+    tmp: [64]byte
+    tmp[0] = 0x80
 
-hash_stream_odin_40 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([40]byte, bool) {
-    hash: [40]byte
-    if c, ok := ctx.internal_ctx.(Ripemd320_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
+    if tc % 64 < 56 {
+        update(d, tmp[0:56 - tc % 64])
     } else {
-        return hash, false
+        update(d, tmp[0:64 + 56 - tc % 64])
     }
-}
 
-hash_file_odin_40 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([40]byte, bool) {
-    if !load_at_once {
-        return hash_stream_odin_40(ctx, os.stream_from_handle(hd))
-    } else {
-        if buf, ok := os.read_entire_file(hd); ok {
-            return hash_bytes_odin_40(ctx, buf[:]), ok
-        }
+    tc <<= 3
+    for i : u32 = 0; i < 8; i += 1 {
+        tmp[i] = byte(tc >> (8 * i))
     }
-    return [40]byte{}, false
-}
 
-@(private)
-_create_ripemd_ctx :: #force_inline proc(hash_size: int) {
-    switch hash_size {
-        case 16: 
-            ctx: Ripemd128_Context
-            _hash_impl.internal_ctx = ctx
-            _hash_impl.hash_size    = ._16
-        case 20: 
-            ctx: Ripemd160_Context
-            _hash_impl.internal_ctx = ctx
-            _hash_impl.hash_size    = ._20
-        case 32: 
-            ctx: Ripemd256_Context
-            _hash_impl.internal_ctx = ctx
-            _hash_impl.hash_size    = ._32
-        case 40: 
-            ctx: Ripemd320_Context
-            _hash_impl.internal_ctx = ctx
-            _hash_impl.hash_size    = ._40
-    }
-}
+    update(d, tmp[0:8])
 
-@(private)
-_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
-    #partial switch ctx.hash_size {
-        case ._16: 
-            _create_ripemd_ctx(16)
-            if c, ok := ctx.internal_ctx.(Ripemd128_Context); ok {
-                init_odin(&c)
-            }
-        case ._20: 
-            _create_ripemd_ctx(20)
-            if c, ok := ctx.internal_ctx.(Ripemd160_Context); ok {
-                init_odin(&c)
-            }
-        case ._32: 
-            _create_ripemd_ctx(32)
-            if c, ok := ctx.internal_ctx.(Ripemd256_Context); ok {
-                init_odin(&c)
-            }
-        case ._40: 
-            _create_ripemd_ctx(40)
-            if c, ok := ctx.internal_ctx.(Ripemd320_Context); ok {
-                init_odin(&c)
-            }
+    when T == Ripemd128_Context {
+        size :: RIPEMD_128_SIZE
+    } else when T == Ripemd160_Context {
+        size :: RIPEMD_160_SIZE
+    } else when T == Ripemd256_Context{
+        size :: RIPEMD_256_SIZE
+    } else when T == Ripemd320_Context{
+        size :: RIPEMD_320_SIZE
     }
-}
 
-@(private)
-_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
-    #partial switch ctx.hash_size {
-        case ._16: 
-            if c, ok := ctx.internal_ctx.(Ripemd128_Context); ok {
-                update_odin(&c, data)
-            }
-        case ._20: 
-            if c, ok := ctx.internal_ctx.(Ripemd160_Context); ok {
-                update_odin(&c, data)
-            }
-        case ._32: 
-            if c, ok := ctx.internal_ctx.(Ripemd256_Context); ok {
-                update_odin(&c, data)
-            }
-        case ._40: 
-            if c, ok := ctx.internal_ctx.(Ripemd320_Context); ok {
-                update_odin(&c, data)
-            }
+    digest: [size]byte
+    for s, i in d.s {
+        digest[i * 4]     = byte(s)
+        digest[i * 4 + 1] = byte(s >> 8)
+        digest[i * 4 + 2] = byte(s >> 16)
+        digest[i * 4 + 3] = byte(s >> 24)
     }
+    copy(hash[:], digest[:])
 }
 
-@(private)
-_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
-    #partial switch ctx.hash_size {
-        case ._16: 
-            if c, ok := ctx.internal_ctx.(Ripemd128_Context); ok {
-                final_odin(&c, hash)
-            }
-        case ._20: 
-            if c, ok := ctx.internal_ctx.(Ripemd160_Context); ok {
-                final_odin(&c, hash)
-            }
-        case ._32: 
-            if c, ok := ctx.internal_ctx.(Ripemd256_Context); ok {
-                final_odin(&c, hash)
-            }
-        case ._40: 
-            if c, ok := ctx.internal_ctx.(Ripemd320_Context); ok {
-                final_odin(&c, hash)
-            }
-    }
-}
 
 /*
     RIPEMD implementation
@@ -574,20 +472,6 @@ RIPEMD_160_R1 := [80]uint {
 	8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11,
 }
 
-init_odin :: proc(ctx: ^$T) {
-    when T == Ripemd128_Context {
-        ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3] = S0, S1, S2, S3
-    } else when T == Ripemd160_Context {
-        ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3], ctx.s[4] = S0, S1, S2, S3, S4
-    } else when T == Ripemd256_Context {
-        ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3] = S0, S1, S2, S3
-        ctx.s[4], ctx.s[5], ctx.s[6], ctx.s[7] = S5, S6, S7, S8
-    } else when T == Ripemd320_Context {
-        ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3], ctx.s[4] = S0, S1, S2, S3, S4
-        ctx.s[5], ctx.s[6], ctx.s[7], ctx.s[8], ctx.s[9] = S5, S6, S7, S8, S9
-    }
-}
-
 block :: #force_inline proc (ctx: ^$T, p: []byte) -> int {
     when T == Ripemd128_Context {
     	return ripemd_128_block(ctx, p)
@@ -948,101 +832,3 @@ ripemd_320_block :: proc(ctx: ^$T, p: []byte) -> int {
 	}
 	return n
 }
-
-update_odin :: proc(ctx: ^$T, p: []byte) {
-    ctx.tc += u64(len(p))
-	p := p
-	if ctx.nx > 0 {
-		n := len(p)
-
-        when T == Ripemd128_Context {
-            if n > RIPEMD_128_BLOCK_SIZE - ctx.nx {
-			    n = RIPEMD_128_BLOCK_SIZE - ctx.nx
-		    }
-        } else when T == Ripemd160_Context {
-            if n > RIPEMD_160_BLOCK_SIZE - ctx.nx {
-			    n = RIPEMD_160_BLOCK_SIZE - ctx.nx
-		    }
-        } else when T == Ripemd256_Context{
-            if n > RIPEMD_256_BLOCK_SIZE - ctx.nx {
-			    n = RIPEMD_256_BLOCK_SIZE - ctx.nx
-		    }
-        } else when T == Ripemd320_Context{
-            if n > RIPEMD_320_BLOCK_SIZE - ctx.nx {
-			    n = RIPEMD_320_BLOCK_SIZE - ctx.nx
-		    }
-        }
-
-		for i := 0; i < n; i += 1 {
-			ctx.x[ctx.nx + i] = p[i]
-		}
-
-		ctx.nx += n
-        when T == Ripemd128_Context {
-            if ctx.nx == RIPEMD_128_BLOCK_SIZE {
-                block(ctx, ctx.x[0:])
-                ctx.nx = 0
-            }
-        } else when T == Ripemd160_Context {
-            if ctx.nx == RIPEMD_160_BLOCK_SIZE {
-                block(ctx, ctx.x[0:])
-                ctx.nx = 0
-            }
-        } else when T == Ripemd256_Context{
-            if ctx.nx == RIPEMD_256_BLOCK_SIZE {
-                block(ctx, ctx.x[0:])
-                ctx.nx = 0
-            }
-        } else when T == Ripemd320_Context{
-            if ctx.nx == RIPEMD_320_BLOCK_SIZE {
-                block(ctx, ctx.x[0:])
-                ctx.nx = 0
-            }
-        }
-		p = p[n:]
-	}
-    n := block(ctx, p)
-	p = p[n:]
-	if len(p) > 0 {
-		ctx.nx = copy(ctx.x[:], p)
-	}
-}
-
-final_odin :: proc(ctx: ^$T, hash: []byte) {
-	d := ctx
-    tc := d.tc
-    tmp: [64]byte
-    tmp[0] = 0x80
-
-    if tc % 64 < 56 {
-        update_odin(d, tmp[0:56 - tc % 64])
-    } else {
-        update_odin(d, tmp[0:64 + 56 - tc % 64])
-    }
-
-    tc <<= 3
-    for i : u32 = 0; i < 8; i += 1 {
-        tmp[i] = byte(tc >> (8 * i))
-    }
-
-    update_odin(d, tmp[0:8])
-
-    when T == Ripemd128_Context {
-        size :: RIPEMD_128_SIZE
-    } else when T == Ripemd160_Context {
-        size :: RIPEMD_160_SIZE
-    } else when T == Ripemd256_Context{
-        size :: RIPEMD_256_SIZE
-    } else when T == Ripemd320_Context{
-        size :: RIPEMD_320_SIZE
-    }
-
-    digest: [size]byte
-    for s, i in d.s {
-		digest[i * 4] 	  = byte(s)
-		digest[i * 4 + 1] = byte(s >> 8)
-		digest[i * 4 + 2] = byte(s >> 16)
-		digest[i * 4 + 3] = byte(s >> 24)
-	}
-    copy(hash[:], digest[:])
-}

+ 84 - 187
core/crypto/sha1/sha1.odin

@@ -6,7 +6,6 @@ package sha1
 
     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>
 */
@@ -16,52 +15,10 @@ 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 {
@@ -71,22 +28,44 @@ hash_string :: proc(data: string) -> [20]byte {
 // 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: [20]byte
+    ctx: Sha1_Context
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [20]byte
+    ctx: Sha1_Context
+    init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            update(&ctx, buf[:read])
+        } 
+    }
+    final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes(buf[:]), ok
+        }
+    }
+    return [20]byte{}, false
 }
 
 hash :: proc {
@@ -100,86 +79,70 @@ hash :: proc {
     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)
+init :: 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
 }
 
-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
+update :: 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
+		}
+	}
 }
 
-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
-    }
-}
+final :: proc(ctx: ^Sha1_Context, hash: []byte) {
+	i := ctx.datalen
 
-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
+	if ctx.datalen < 56 {
+		ctx.data[i] = 0x80
+        i += 1
+        for i < 56 {
+            ctx.data[i] = 0x00
+            i += 1
         }
-    }
-    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)
-    }
-}
+	}
+	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)
+	}
 
-@(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)
-    }
-}
+	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[:])
 
-@(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)
-    }
+	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
+	}
 }
 
 /*
@@ -258,69 +221,3 @@ transform :: proc(ctx: ^Sha1_Context, data: []byte) {
 	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
-	}
-}

+ 215 - 407
core/crypto/sha2/sha2.odin

@@ -6,7 +6,6 @@ package sha2
 
     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>
@@ -17,72 +16,6 @@ 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
@@ -97,22 +30,46 @@ hash_string_224 :: proc(data: string) -> [28]byte {
 // 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: [28]byte
+	ctx: Sha256_Context
+    ctx.is224 = true
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [28]byte
+    ctx: Sha512_Context
+    ctx.is384 = false
+    init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            update(&ctx, buf[:read])
+        } 
+    }
+    final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_224(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_224(buf[:]), ok
+        }
+    }
+    return [28]byte{}, false
 }
 
 hash_224 :: proc {
@@ -131,22 +88,46 @@ hash_string_256 :: proc(data: string) -> [32]byte {
 // 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: [32]byte
+	ctx: Sha256_Context
+    ctx.is224 = false
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [32]byte
+    ctx: Sha512_Context
+    ctx.is384 = false
+    init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            update(&ctx, buf[:read])
+        } 
+    }
+    final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_256(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_256(buf[:]), ok
+        }
+    }
+    return [32]byte{}, false
 }
 
 hash_256 :: proc {
@@ -165,22 +146,46 @@ hash_string_384 :: proc(data: string) -> [48]byte {
 // 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: [48]byte
+	ctx: Sha512_Context
+    ctx.is384 = true
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [48]byte
+    ctx: Sha512_Context
+    ctx.is384 = true
+    init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            update(&ctx, buf[:read])
+        } 
+    }
+    final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_384(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_384(buf[:]), ok
+        }
+    }
+    return [48]byte{}, false
 }
 
 hash_384 :: proc {
@@ -199,22 +204,46 @@ hash_string_512 :: proc(data: string) -> [64]byte {
 // 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: [64]byte
+	ctx: Sha512_Context
+    ctx.is384 = false
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [64]byte
+    ctx: Sha512_Context
+    ctx.is384 = false
+	init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            update(&ctx, buf[:read])
+        } 
+    }
+    final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_512(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_512(buf[:]), ok
+        }
+    }
+    return [64]byte{}, false
 }
 
 hash_512 :: proc {
@@ -228,225 +257,121 @@ hash_512 :: proc {
     Low level API
 */
 
-init :: proc(ctx: ^_ctx.Hash_Context) {
-    _hash_impl->init()
-}
-
-update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
-    _hash_impl->update(data)
+init :: 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
+        }
+    }
 }
 
-final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
-    _hash_impl->final(hash)
-}
+update :: proc(ctx: ^$T, data: []byte) {
+    length := uint(len(data))
+    block_nb: uint
+    new_len, rem_len, tmp_len: uint
+    shifted_message := make([]byte, length)
 
-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[:])
+    when T == Sha256_Context {
+        CURR_BLOCK_SIZE :: SHA256_BLOCK_SIZE
+    } else when T == Sha512_Context {
+        CURR_BLOCK_SIZE :: SHA512_BLOCK_SIZE
     }
-    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
-    }
-}
+    tmp_len = CURR_BLOCK_SIZE - ctx.length
+    rem_len = length < tmp_len ? length : tmp_len
+    copy(ctx.block[ctx.length:], data[:rem_len])
 
-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
-        }
+    if ctx.length + length < CURR_BLOCK_SIZE {
+        ctx.length += length
+        return
     }
-    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
-}
+    new_len = length - rem_len
+    block_nb = new_len / CURR_BLOCK_SIZE
+    shifted_message = data[rem_len:]
 
-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
-    }
-}
+    sha2_transf(ctx, ctx.block[:], 1)
+    sha2_transf(ctx, shifted_message, block_nb)
 
-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
-}
+    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])}
 
-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
+    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}
 }
 
-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
-    }
-}
+final :: proc(ctx: ^$T, hash: []byte) {
+    block_nb, pm_len, len_b: u32
+    i: i32
 
-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
-}
+    when T == Sha256_Context      {CURR_BLOCK_SIZE :: SHA256_BLOCK_SIZE}
+    else when T == Sha512_Context {CURR_BLOCK_SIZE :: SHA512_BLOCK_SIZE}
 
-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
-}
+    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)}
 
-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
-    }
-}
+    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}
 
-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
-}
+    mem.set(rawptr(&(ctx.block[ctx.length:])[0]), 0, int(uint(pm_len) - ctx.length))
+    ctx.block[ctx.length] = 0x80
 
-@(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)
-        }
-    }
-}
+    util.PUT_U32_BE(ctx.block[pm_len - 4:], len_b)
 
-@(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)
-            }
-    }
-}
+    sha2_transf(ctx, ctx.block[:], uint(block_nb))
 
-@(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)
-            }
-    }
+    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])}
+        } 
+    } 
 }
 
 /*
@@ -590,50 +515,6 @@ 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
@@ -710,76 +591,3 @@ sha2_transf :: proc(ctx: ^$T, data: []byte, block_nb: uint) {
 		}
 	}
 }
-
-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])}
-		} 
-	} 
-}

+ 127 - 284
core/crypto/sha3/sha3.odin

@@ -6,7 +6,6 @@ package sha3
 
     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.
@@ -15,58 +14,8 @@ 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_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
 */
@@ -80,22 +29,46 @@ hash_string_224 :: proc(data: string) -> [28]byte {
 // 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: [28]byte
+    ctx: _sha3.Sha3_Context
+    ctx.mdlen = 28
+    _sha3.init(&ctx)
+    _sha3.update(&ctx, data)
+    _sha3.final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [28]byte
+    ctx: _sha3.Sha3_Context
+    ctx.mdlen = 28
+    _sha3.init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            _sha3.update(&ctx, buf[:read])
+        } 
+    }
+    _sha3.final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_224(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_224(buf[:]), ok
+        }
+    }
+    return [28]byte{}, false
 }
 
 hash_224 :: proc {
@@ -114,22 +87,46 @@ hash_string_256 :: proc(data: string) -> [32]byte {
 // 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: [32]byte
+    ctx: _sha3.Sha3_Context
+    ctx.mdlen = 32
+    _sha3.init(&ctx)
+    _sha3.update(&ctx, data)
+    _sha3.final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [32]byte
+    ctx: _sha3.Sha3_Context
+    ctx.mdlen = 32
+    _sha3.init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            _sha3.update(&ctx, buf[:read])
+        } 
+    }
+    _sha3.final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_256(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_256(buf[:]), ok
+        }
+    }
+    return [32]byte{}, false
 }
 
 hash_256 :: proc {
@@ -148,22 +145,46 @@ hash_string_384 :: proc(data: string) -> [48]byte {
 // 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: [48]byte
+    ctx: _sha3.Sha3_Context
+    ctx.mdlen = 48
+    _sha3.init(&ctx)
+    _sha3.update(&ctx, data)
+    _sha3.final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [48]byte
+    ctx: _sha3.Sha3_Context
+    ctx.mdlen = 48
+    _sha3.init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            _sha3.update(&ctx, buf[:read])
+        } 
+    }
+    _sha3.final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_384(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_384(buf[:]), ok
+        }
+    }
+    return [48]byte{}, false
 }
 
 hash_384 :: proc {
@@ -182,22 +203,46 @@ hash_string_512 :: proc(data: string) -> [64]byte {
 // 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: [64]byte
+    ctx: _sha3.Sha3_Context
+    ctx.mdlen = 64
+    _sha3.init(&ctx)
+    _sha3.update(&ctx, data)
+    _sha3.final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [64]byte
+    ctx: _sha3.Sha3_Context
+    ctx.mdlen = 64
+    _sha3.init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            _sha3.update(&ctx, buf[:read])
+        } 
+    }
+    _sha3.final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_512(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_512(buf[:]), ok
+        }
+    }
+    return [64]byte{}, false
 }
 
 hash_512 :: proc {
@@ -211,218 +256,16 @@ hash_512 :: proc {
     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
-}
+Sha3_Context :: _sha3.Sha3_Context
 
-@(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
-    }
+init :: proc(ctx: ^_sha3.Sha3_Context) {
+    _sha3.init(ctx)
 }
 
-@(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)
-    }
+update :: proc "contextless" (ctx: ^_sha3.Sha3_Context, data: []byte) {
+    _sha3.update(ctx, data)
 }
 
-@(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)
-    }
+final :: proc "contextless" (ctx: ^_sha3.Sha3_Context, hash: []byte) {
+    _sha3.final(ctx, hash)
 }

+ 72 - 185
core/crypto/shake/shake.odin

@@ -6,7 +6,6 @@ package shake
 
     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.
@@ -15,52 +14,8 @@ package shake
 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
 */
@@ -74,22 +29,48 @@ hash_string_128 :: proc(data: string) -> [16]byte {
 // 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: [16]byte
+    ctx: _sha3.Sha3_Context
+    ctx.mdlen = 16
+    _sha3.init(&ctx)
+    _sha3.update(&ctx, data)
+    _sha3.shake_xof(&ctx)
+    _sha3.shake_out(&ctx, hash[:])
+    return hash
 }
 
 // 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: [16]byte
+    ctx: _sha3.Sha3_Context
+    ctx.mdlen = 16
+    _sha3.init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            _sha3.update(&ctx, buf[:read])
+        } 
+    }
+    _sha3.shake_xof(&ctx)
+    _sha3.shake_out(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_128(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_128(buf[:]), ok
+        }
+    }
+    return [16]byte{}, false
 }
 
 hash_128 :: proc {
@@ -108,22 +89,48 @@ hash_string_256 :: proc(data: string) -> [32]byte {
 // 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: [32]byte
+    ctx: _sha3.Sha3_Context
+    ctx.mdlen = 32
+    _sha3.init(&ctx)
+    _sha3.update(&ctx, data)
+    _sha3.shake_xof(&ctx)
+    _sha3.shake_out(&ctx, hash[:])
+    return hash
 }
 
 // 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: [32]byte
+    ctx: _sha3.Sha3_Context
+    ctx.mdlen = 32
+    _sha3.init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            _sha3.update(&ctx, buf[:read])
+        } 
+    }
+    _sha3.shake_xof(&ctx)
+    _sha3.shake_out(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_256(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_256(buf[:]), ok
+        }
+    }
+    return [32]byte{}, false
 }
 
 hash_256 :: proc {
@@ -137,137 +144,17 @@ hash_256 :: proc {
     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
-}
+Sha3_Context :: _sha3.Sha3_Context
 
-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
-    }
+init :: proc(ctx: ^_sha3.Sha3_Context) {
+    _sha3.init(ctx)
 }
 
-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
+update :: proc "contextless" (ctx: ^_sha3.Sha3_Context, data: []byte) {
+    _sha3.update(ctx, data)
 }
 
-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[:])
-    }
+final :: proc "contextless" (ctx: ^_sha3.Sha3_Context, hash: []byte) {
+    _sha3.shake_xof(ctx)
+    _sha3.shake_out(ctx, hash[:])
 }

+ 0 - 487
core/crypto/skein/skein.odin

@@ -1,487 +0,0 @@
-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) {
-
-}

+ 74 - 180
core/crypto/sm3/sm3.odin

@@ -6,7 +6,6 @@ package sm3
 
     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>
 */
@@ -15,51 +14,6 @@ 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
@@ -70,22 +24,44 @@ hash_string :: proc(data: string) -> [32]byte {
 // 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: [32]byte
+    ctx: Sm3_Context
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [32]byte
+    ctx: Sm3_Context
+    init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            update(&ctx, buf[:read])
+        } 
+    }
+    final(&ctx, hash[:])
+    return hash, true 
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes(buf[:]), ok
+        }
+    }
+    return [32]byte{}, false
 }
 
 hash :: proc {
@@ -99,86 +75,64 @@ hash :: proc {
     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)
+init :: 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]
 }
 
-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
-}
+update :: proc(ctx: ^Sm3_Context, data: []byte) {
+    data := data
+    ctx.length += u64(len(data))
 
-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])
-            } 
+    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
         }
-        final_odin(&c, hash[:])
-        return hash, true
-    } else {
-        return hash, false
+        data = data[n:]
     }
-}
-
-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
-        }
+    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[:]))
     }
-    return [32]byte{}, false
 }
 
-@(private)
-_create_sm3_ctx :: #force_inline proc() {
-    ctx: Sm3_Context
-    _hash_impl.internal_ctx = ctx
-    _hash_impl.hash_size    = ._32
-}
+final :: proc(ctx: ^Sm3_Context, hash: []byte) {
+    length := ctx.length
 
-@(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)
+    pad: [64]byte
+    pad[0] = 0x80
+    if length % 64 < 56 {
+        update(ctx, pad[0: 56 - length % 64])
+    } else {
+        update(ctx, pad[0: 64 + 56 - length % 64])
     }
-}
 
-@(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)
-    }
-}
+    length <<= 3
+    util.PUT_U64_BE(pad[:], length)
+    update(ctx, pad[0: 8])
+    assert(ctx.bitlength == 0)
 
-@(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)
-    }
+    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])
 }
 
 /*
@@ -200,17 +154,6 @@ IV := [8]u32 {
     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
 
@@ -282,52 +225,3 @@ block :: proc "contextless" (ctx: ^Sm3_Context, buf: []byte) {
     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])
-}

+ 106 - 229
core/crypto/streebog/streebog.odin

@@ -6,7 +6,6 @@ package streebog
 
     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>
 */
@@ -15,58 +14,6 @@ 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
@@ -81,22 +28,46 @@ hash_string_256 :: proc(data: string) -> [32]byte {
 // 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: [32]byte
+    ctx: Streebog_Context
+    ctx.is256 = true
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [32]byte
+    ctx: Streebog_Context
+    ctx.is256 = true
+    init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            update(&ctx, buf[:read])
+        } 
+    }
+    final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_256(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_256(buf[:]), ok
+        }
+    }
+    return [32]byte{}, false
 }
 
 hash_256 :: proc {
@@ -115,22 +86,44 @@ hash_string_512 :: proc(data: string) -> [64]byte {
 // 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: [64]byte
+    ctx: Streebog_Context
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [64]byte
+    ctx: Streebog_Context
+    init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            update(&ctx, buf[:read])
+        } 
+    }
+    final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_512(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_512(buf[:]), ok
+        }
+    }
+    return [64]byte{}, false
 }
 
 hash_512 :: proc {
@@ -144,120 +137,64 @@ hash_512 :: proc {
     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
+init :: 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
 }
 
-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
-    }
-}
+update :: 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
+	}
 
-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
+	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
+		}
+	}
 }
 
-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
-}
+final :: 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
 
-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
-    }
-}
+	padding(ctx)
 
-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
-}
+	G(ctx.h[:], ctx.n[:], ctx.buffer[:])
 
-@(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)
-    }
-}
+	add_mod_512(ctx.n[:], t[:], ctx.n[:])
+	add_mod_512(ctx.sigma[:], ctx.buffer[:], ctx.sigma[:])
 
-@(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)
-    }
-}
+	G(ctx.h[:], ctx.v_0[:], ctx.n[:])
+	G(ctx.h[:], ctx.v_0[:], ctx.sigma[:])
 
-@(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)
-    }
+	if ctx.is256 {
+		copy(hash[:], ctx.h[32:])
+	} else {
+		copy(hash[:], ctx.h[:])
+	}
 }
 
 /*
@@ -534,63 +471,3 @@ padding :: proc(ctx: ^Streebog_Context) {
 		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[:])
-	}
-}

+ 101 - 219
core/crypto/tiger/tiger.odin

@@ -6,7 +6,6 @@ package tiger
 
     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/>
 */
@@ -14,55 +13,8 @@ package 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
 */
@@ -76,22 +28,46 @@ hash_string_128 :: proc(data: string) -> [16]byte {
 // 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: [16]byte
+    ctx: _tiger.Tiger_Context
+    ctx.ver = 1
+    _tiger.init(&ctx)
+    _tiger.update(&ctx, data)
+    _tiger.final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [16]byte
+    ctx: _tiger.Tiger_Context
+    ctx.ver = 1
+    _tiger.init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            _tiger.update(&ctx, buf[:read])
+        } 
+    }
+    _tiger.final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_128(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_128(buf[:]), ok
+        }
+    }
+    return [16]byte{}, false
 }
 
 hash_128 :: proc {
@@ -110,22 +86,46 @@ hash_string_160 :: proc(data: string) -> [20]byte {
 // 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: [20]byte
+    ctx: _tiger.Tiger_Context
+    ctx.ver = 1
+    _tiger.init(&ctx)
+    _tiger.update(&ctx, data)
+    _tiger.final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [20]byte
+    ctx: _tiger.Tiger_Context
+    ctx.ver = 1
+    _tiger.init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            _tiger.update(&ctx, buf[:read])
+        } 
+    }
+    _tiger.final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_160(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_160(buf[:]), ok
+        }
+    }
+    return [20]byte{}, false
 }
 
 hash_160 :: proc {
@@ -144,188 +144,70 @@ hash_string_192 :: proc(data: string) -> [24]byte {
 // 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: [24]byte
+    ctx: _tiger.Tiger_Context
+    ctx.ver = 1
+    _tiger.init(&ctx)
+    _tiger.update(&ctx, data)
+    _tiger.final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [24]byte
+    ctx: _tiger.Tiger_Context
+    ctx.ver = 1
+    _tiger.init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            _tiger.update(&ctx, buf[:read])
+        } 
+    }
+    _tiger.final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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))
+        return hash_stream_192(os.stream_from_handle(hd))
     } else {
         if buf, ok := os.read_entire_file(hd); ok {
-            return hash_bytes_odin_16(ctx, buf[:]), ok
+            return hash_bytes_192(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
+    return [24]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_192 :: proc {
+    hash_stream_192,
+    hash_file_192,
+    hash_bytes_192,
+    hash_string_192,
 }
 
-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
-    }
-}
+/*
+    Low level API
+*/
 
-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
-}
+Tiger_Context :: _tiger.Tiger_Context
 
-@(private)
-_create_tiger_ctx :: #force_inline proc(hash_size: int) {
-    ctx: _tiger.Tiger_Context
+init :: proc(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
-    }
+    _tiger.init(ctx)
 }
 
-@(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)
-    }
+update :: proc(ctx: ^_tiger.Tiger_Context, data: []byte) {
+    _tiger.update(ctx, data)
 }
 
-@(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)
-    }
-}
+final :: proc(ctx: ^_tiger.Tiger_Context, hash: []byte) {
+    _tiger.final(ctx, hash)
+}

+ 100 - 218
core/crypto/tiger2/tiger2.odin

@@ -6,7 +6,6 @@ package tiger2
 
     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/>
 */
@@ -14,55 +13,8 @@ package tiger2
 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
 */
@@ -76,22 +28,46 @@ hash_string_128 :: proc(data: string) -> [16]byte {
 // 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: [16]byte
+    ctx: _tiger.Tiger_Context
+    ctx.ver = 2
+    _tiger.init(&ctx)
+    _tiger.update(&ctx, data)
+    _tiger.final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [16]byte
+    ctx: _tiger.Tiger_Context
+    ctx.ver = 2
+    _tiger.init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            _tiger.update(&ctx, buf[:read])
+        } 
+    }
+    _tiger.final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_128(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_128(buf[:]), ok
+        }
+    }
+    return [16]byte{}, false
 }
 
 hash_128 :: proc {
@@ -110,22 +86,46 @@ hash_string_160 :: proc(data: string) -> [20]byte {
 // 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: [20]byte
+    ctx: _tiger.Tiger_Context
+    ctx.ver = 2
+    _tiger.init(&ctx)
+    _tiger.update(&ctx, data)
+    _tiger.final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [20]byte
+    ctx: _tiger.Tiger_Context
+    ctx.ver = 2
+    _tiger.init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            _tiger.update(&ctx, buf[:read])
+        } 
+    }
+    _tiger.final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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)
+    if !load_at_once {
+        return hash_stream_160(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes_160(buf[:]), ok
+        }
+    }
+    return [20]byte{}, false
 }
 
 hash_160 :: proc {
@@ -144,188 +144,70 @@ hash_string_192 :: proc(data: string) -> [24]byte {
 // 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: [24]byte
+    ctx: _tiger.Tiger_Context
+    ctx.ver = 2
+    _tiger.init(&ctx)
+    _tiger.update(&ctx, data)
+    _tiger.final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [24]byte
+    ctx: _tiger.Tiger_Context
+    ctx.ver = 2
+    _tiger.init(&ctx)
+    buf := make([]byte, 512)
+    defer delete(buf)
+    read := 1
+    for read > 0 {
+        read, _ = s->impl_read(buf)
+        if read > 0 {
+            _tiger.update(&ctx, buf[:read])
+        } 
+    }
+    _tiger.final(&ctx, hash[:])
+    return hash, true
 }
 
 // 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))
+        return hash_stream_192(os.stream_from_handle(hd))
     } else {
         if buf, ok := os.read_entire_file(hd); ok {
-            return hash_bytes_odin_16(ctx, buf[:]), ok
+            return hash_bytes_192(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
+    return [24]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_192 :: proc {
+    hash_stream_192,
+    hash_file_192,
+    hash_bytes_192,
+    hash_string_192,
 }
 
-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
-    }
-}
+/*
+    Low level API
+*/
 
-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
-}
+Tiger_Context :: _tiger.Tiger_Context
 
-@(private)
-_create_tiger2_ctx :: #force_inline proc(hash_size: int) {
-    ctx: _tiger.Tiger_Context
+init :: proc(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
-    }
+    _tiger.init(ctx)
 }
 
-@(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)
-    }
+update :: proc(ctx: ^_tiger.Tiger_Context, data: []byte) {
+    _tiger.update(ctx, data)
 }
 
-@(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)
-    }
+final :: proc(ctx: ^_tiger.Tiger_Context, hash: []byte) {
+    _tiger.final(ctx, hash)
 }

+ 115 - 201
core/crypto/whirlpool/whirlpool.odin

@@ -6,7 +6,6 @@ package whirlpool
 
     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>
 */
@@ -14,48 +13,8 @@ package whirlpool
 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
 */
@@ -69,22 +28,44 @@ hash_string :: proc(data: string) -> [64]byte {
 // 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: [64]byte
+	ctx: Whirlpool_Context
+    // init(&ctx) No-op
+    update(&ctx, data)
+    final(&ctx, hash[:])
+    return hash
 }
 
 // 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: [64]byte
+	ctx: Whirlpool_Context
+	// init(&ctx) No-op
+	buf := make([]byte, 512)
+	defer delete(buf)
+	read := 1
+	for read > 0 {
+	    read, _ = s->impl_read(buf)
+	    if read > 0 {
+			update(&ctx, buf[:read])
+	    } 
+	}
+	final(&ctx, hash[:])
+	return hash, true
 }
 
 // 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)
+	if !load_at_once {
+        return hash_stream(os.stream_from_handle(hd))
+    } else {
+        if buf, ok := os.read_entire_file(hd); ok {
+            return hash_bytes(buf[:]), ok
+        }
+    }
+    return [64]byte{}, false
 }
 
 hash :: proc {
@@ -98,76 +79,103 @@ hash :: proc {
     Low level API
 */
 
-init :: proc(ctx: ^_ctx.Hash_Context) {
-    _hash_impl->init()
+@(warning="Init is a no-op for Whirlpool")
+init :: proc(ctx: ^Whirlpool_Context) {
+	// No action needed here
 }
 
-update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
-    _hash_impl->update(data)
-}
+update :: 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
 
-final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
-    _hash_impl->final(hash)
-}
+	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
+	}
 
-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
-}
+	for source_bits > 8 {
+		b = u32(u32((source[source_pos] << source_gap) & 0xff) | u32((source[source_pos+1] & 0xff) >> (8 - source_gap)))
 
-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
-    }
-}
+		ctx.buffer[ctx.buffer_pos] |= u8(b >> buffer_rem)
+		ctx.buffer_pos += 1
+		ctx.buffer_bits += int(8 - buffer_rem)
 
-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
-}
+		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
+	}
 
-@(private)
-_create_whirlpool_ctx :: #force_inline proc() {
-    ctx: Whirlpool_Context
-    _hash_impl.internal_ctx = ctx
-    _hash_impl.hash_size    = ._64
-}
+	if source_bits > 0 {
+		b = u32((source[source_pos] << source_gap) & 0xff)
+		ctx.buffer[ctx.buffer_pos] |= byte(b) >> buffer_rem
+	} else {b = 0}
 
-@(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)
-    }
+	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)
+	}
 }
 
-@(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)
-    }
+final :: 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])
+	}
 }
 
 /*
@@ -774,97 +782,3 @@ transform :: proc (ctx: ^Whirlpool_Context) {
 	}
 	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])
-	}
-}

+ 9 - 9
core/fmt/fmt.odin

@@ -1929,12 +1929,12 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
 		if fi.hash { 
 			// Printed as it is written
 			io.write_byte(fi.writer, '\n')
-			for col in 0..<info.column_count {
+			for row in 0..<info.row_count {
 				fmt_write_indent(fi)
-				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 := (col + row*info.elem_stride)*info.elem_size
+					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)
@@ -1943,12 +1943,12 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
 			}
 		} else {
 			// Printed in Row-Major layout to match text layout
-			for col in 0..<info.column_count {
-				if col > 0 { io.write_string(fi.writer, "; ") }
-				for row in 0..<info.row_count {
-					if row > 0 { io.write_string(fi.writer, ", ") }
+			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 := (col + row*info.elem_stride)*info.elem_size
+					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)

+ 0 - 1
core/os/os2/file_util.odin

@@ -1,7 +1,6 @@
 package os2
 
 import "core:mem"
-import "core:io"
 import "core:strconv"
 import "core:unicode/utf8"
 

+ 0 - 1
core/os/os2/heap_windows.odin

@@ -1,7 +1,6 @@
 //+private
 package os2
 
-import "core:runtime"
 import "core:mem"
 import win32 "core:sys/windows"
 

+ 26 - 15
src/build_settings.cpp

@@ -18,6 +18,7 @@ enum TargetOsKind {
 	TargetOs_freebsd,
 	
 	TargetOs_wasi,
+	TargetOs_js,
 
 	TargetOs_freestanding,
 
@@ -54,6 +55,7 @@ String target_os_names[TargetOs_COUNT] = {
 	str_lit("freebsd"),
 	
 	str_lit("wasi"),
+	str_lit("js"),
 
 	str_lit("freestanding"),
 };
@@ -344,12 +346,12 @@ gb_global TargetMetrics target_freestanding_wasm32 = {
 	str_lit(""),
 };
 
-gb_global TargetMetrics target_freestanding_wasm64 = {
-	TargetOs_freestanding,
-	TargetArch_wasm64,
+gb_global TargetMetrics target_js_wasm32 = {
+	TargetOs_js,
+	TargetArch_wasm32,
+	4,
 	8,
-	16,
-	str_lit("wasm64-freestanding-js"),
+	str_lit("wasm32-js-js"),
 	str_lit(""),
 };
 
@@ -363,6 +365,14 @@ gb_global TargetMetrics target_wasi_wasm32 = {
 };
 
 
+// gb_global TargetMetrics target_freestanding_wasm64 = {
+// 	TargetOs_freestanding,
+// 	TargetArch_wasm64,
+// 	8,
+// 	16,
+// 	str_lit("wasm64-freestanding-js"),
+// 	str_lit(""),
+// };
 
 
 
@@ -372,18 +382,19 @@ struct NamedTargetMetrics {
 };
 
 gb_global NamedTargetMetrics named_targets[] = {
-	{ str_lit("darwin_amd64"),   &target_darwin_amd64   },
-	{ str_lit("darwin_arm64"),   &target_darwin_arm64   },
-	{ str_lit("essence_amd64"),  &target_essence_amd64  },
-	{ str_lit("linux_386"),      &target_linux_386      },
-	{ str_lit("linux_amd64"),    &target_linux_amd64    },
-	{ str_lit("windows_386"),    &target_windows_386    },
-	{ str_lit("windows_amd64"),  &target_windows_amd64  },
-	{ str_lit("freebsd_386"),    &target_freebsd_386    },
-	{ str_lit("freebsd_amd64"),  &target_freebsd_amd64  },
+	{ str_lit("darwin_amd64"),        &target_darwin_amd64   },
+	{ str_lit("darwin_arm64"),        &target_darwin_arm64   },
+	{ str_lit("essence_amd64"),       &target_essence_amd64  },
+	{ str_lit("linux_386"),           &target_linux_386      },
+	{ str_lit("linux_amd64"),         &target_linux_amd64    },
+	{ str_lit("windows_386"),         &target_windows_386    },
+	{ str_lit("windows_amd64"),       &target_windows_amd64  },
+	{ str_lit("freebsd_386"),         &target_freebsd_386    },
+	{ str_lit("freebsd_amd64"),       &target_freebsd_amd64  },
 	{ str_lit("freestanding_wasm32"), &target_freestanding_wasm32 },
+	{ str_lit("wasi_wasm32"),         &target_wasi_wasm32 },
+	{ str_lit("js_wasm32"),           &target_js_wasm32 },
 	// { str_lit("freestanding_wasm64"), &target_freestanding_wasm64 },
-	{ str_lit("wasi_wasm32"), &target_wasi_wasm32 },
 };
 
 NamedTargetMetrics *selected_target_metrics;

+ 4 - 0
src/llvm_backend.hpp

@@ -473,6 +473,10 @@ LLVMTypeRef lb_type_padding_filler(lbModule *m, i64 padding, i64 padding_align);
 
 LLVMValueRef llvm_basic_shuffle(lbProcedure *p, LLVMValueRef vector, LLVMValueRef mask);
 
+void lb_mem_copy_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile=false);
+void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile=false);
+
+
 #define LB_STARTUP_RUNTIME_PROC_NAME   "__$startup_runtime"
 #define LB_STARTUP_TYPE_INFO_PROC_NAME "__$startup_type_info"
 #define LB_TYPE_INFO_DATA_NAME       "__$type_info_data"

+ 6 - 0
src/llvm_backend_const.cpp

@@ -1010,6 +1010,11 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
 						if (op != Token_RangeHalf) {
 							hi += 1;
 						}
+						GB_ASSERT(0 <= lo && lo <= max_count);
+						GB_ASSERT(0 <= hi && hi <= max_count);
+						GB_ASSERT(lo <= hi);
+						
+						
 						TypeAndValue tav = fv->value->tav;
 						LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value;
 						for (i64 k = lo; k < hi; k++) {
@@ -1021,6 +1026,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
 						TypeAndValue index_tav = fv->field->tav;
 						GB_ASSERT(index_tav.mode == Addressing_Constant);
 						i64 index = exact_value_to_i64(index_tav.value);
+						GB_ASSERT(index < max_count);
 						TypeAndValue tav = fv->value->tav;
 						LLVMValueRef val = lb_const_value(m, elem_type, tav.value, allow_local).value;
 						i64 offset = matrix_row_major_index_to_offset(type, index);

+ 1 - 10
src/llvm_backend_debug.cpp

@@ -538,16 +538,7 @@ LLVMMetadataRef lb_debug_type(lbModule *m, Type *type) {
 			}
 
 
-		case Type_Basic:
-		case Type_Pointer:
-		case Type_Array:
-		case Type_EnumeratedArray:
-		case Type_Tuple:
-		case Type_Proc:
-		case Type_SimdVector:
-		case Type_RelativePointer:
-		case Type_RelativeSlice:
-		case Type_Matrix:
+		default:
 			{
 				LLVMMetadataRef debug_bt = lb_debug_type(m, bt);
 				LLVMMetadataRef final_decl = LLVMDIBuilderCreateTypedef(m->debug_builder, debug_bt, name_text, name_len, file, line, scope, align_in_bits);

+ 19 - 14
src/llvm_backend_expr.cpp

@@ -490,15 +490,11 @@ bool lb_is_matrix_simdable(Type *t) {
 	}
 	
 	switch (build_context.metrics.arch) {
+	default:
+		return false;
 	case TargetArch_amd64:
 	case TargetArch_arm64:
-		// possible
 		break;
-	case TargetArch_386:
-	case TargetArch_wasm32:
-	case TargetArch_wasm64:
-		// nope
-		return false;
 	}
 	
 	if (elem->kind == Type_Basic) {
@@ -2018,14 +2014,23 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
 			i64 src_count = src->Matrix.row_count*src->Matrix.column_count;
 			GB_ASSERT(dst_count == src_count);
 			
-			for (i64 j = 0; j < src->Matrix.column_count; j++) {
-				for (i64 i = 0; i < src->Matrix.row_count; i++) {
-					lbValue s = lb_emit_matrix_ev(p, value, i, j);
-					i64 index = i + j*src->Matrix.row_count;					
-					i64 dst_i = index%dst->Matrix.row_count;
-					i64 dst_j = index/dst->Matrix.row_count;
-					lbValue d = lb_emit_matrix_epi(p, v.addr, dst_i, dst_j);
-					lb_emit_store(p, d, s);
+			lbValue pdst = v.addr;
+			lbValue psrc = lb_address_from_load_or_generate_local(p, value);
+			
+			bool same_elem_base_types = are_types_identical(
+				base_type(dst->Matrix.elem),
+				base_type(src->Matrix.elem)
+			);
+			
+			if (same_elem_base_types && type_size_of(dst) == type_size_of(src)) {
+				lb_mem_copy_overlapping(p, v.addr, psrc, lb_const_int(p->module, t_int, type_size_of(dst)));
+			} else {
+				for (i64 i = 0; i < src_count; i++) {
+					lbValue dp = lb_emit_array_epi(p, v.addr, matrix_column_major_index_to_offset(dst, i));
+					lbValue sp = lb_emit_array_epi(p, psrc,   matrix_column_major_index_to_offset(src, i));
+					lbValue s = lb_emit_load(p, sp);
+					s = lb_emit_conv(p, s, dst->Matrix.elem);
+					lb_emit_store(p, dp, s);
 				}
 			}
 		}

+ 2 - 2
src/llvm_backend_proc.cpp

@@ -1,4 +1,4 @@
-void lb_mem_copy_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile=false) {
+void lb_mem_copy_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile) {
 	dst = lb_emit_conv(p, dst, t_rawptr);
 	src = lb_emit_conv(p, src, t_rawptr);
 	len = lb_emit_conv(p, len, t_int);
@@ -27,7 +27,7 @@ void lb_mem_copy_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue l
 	args[3] = LLVMConstInt(LLVMInt1TypeInContext(p->module->ctx), 0, is_volatile);
 	LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
 }
-void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile=false) {
+void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbValue len, bool is_volatile) {
 	dst = lb_emit_conv(p, dst, t_rawptr);
 	src = lb_emit_conv(p, src, t_rawptr);
 	len = lb_emit_conv(p, len, t_int);

+ 7 - 1
src/main.cpp

@@ -137,11 +137,17 @@ i32 linker_stage(lbGenerator *gen) {
 
 	if (is_arch_wasm()) {
 		timings_start_section(timings, str_lit("wasm-ld"));
-
+		
+	#if defined(GB_SYSTEM_WINDOWS)
 		result = system_exec_command_line_app("wasm-ld",
 			"\"%.*s\\bin\\wasm-ld\" \"%.*s.wasm.o\" -o \"%.*s.wasm\" %.*s %.*s",
 			LIT(build_context.ODIN_ROOT),
 			LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
+	#else
+		result = system_exec_command_line_app("wasm-ld",
+			"wasm-ld \"%.*s.wasm.o\" -o \"%.*s.wasm\" %.*s %.*s",
+			LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
+	#endif
 		return result;
 	}
 

+ 11 - 3
src/types.cpp

@@ -1406,18 +1406,26 @@ i64 matrix_indices_to_offset(Type *t, i64 row_index, i64 column_index) {
 	GB_ASSERT(0 <= row_index && row_index < t->Matrix.row_count);
 	GB_ASSERT(0 <= column_index && column_index < t->Matrix.column_count);
 	i64 stride_elems = matrix_type_stride_in_elems(t);
-	return stride_elems*column_index + row_index;
+	// NOTE(bill): Column-major layout internally
+	return row_index + stride_elems*column_index;
 }
 
 i64 matrix_row_major_index_to_offset(Type *t, i64 index) {
 	t = base_type(t);
 	GB_ASSERT(t->kind == Type_Matrix);
 	
-	i64 column_index = index%t->Matrix.column_count;
 	i64 row_index    = index/t->Matrix.column_count;
+	i64 column_index = index%t->Matrix.column_count;
+	return matrix_indices_to_offset(t, row_index, column_index);
+}
+i64 matrix_column_major_index_to_offset(Type *t, i64 index) {
+	t = base_type(t);
+	GB_ASSERT(t->kind == Type_Matrix);
+	
+	i64 row_index    = index%t->Matrix.row_count;
+	i64 column_index = index/t->Matrix.row_count;
 	return matrix_indices_to_offset(t, row_index, column_index);
 }
-
 
 
 bool is_matrix_square(Type *t) {

BIN
tests/core/botan.dll


+ 0 - 183
tests/core/crypto/test_core_crypto.odin

@@ -33,7 +33,6 @@ import "core:crypto/tiger2"
 import "core:crypto/gost"
 import "core:crypto/streebog"
 import "core:crypto/sm3"
-import "core:crypto/skein"
 import "core:crypto/jh"
 import "core:crypto/groestl"
 import "core:crypto/haval"
@@ -102,7 +101,6 @@ main :: proc() {
     test_tiger2_160(&t)
     test_tiger2_192(&t)
     test_sm3(&t)
-    test_skein512(&t)
     test_jh_224(&t)
     test_jh_256(&t)
     test_jh_384(&t)
@@ -171,13 +169,6 @@ test_md4 :: proc(t: ^testing.T) {
         computed_str := hex_string(computed[:])
         expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
     }
-    md4.use_botan()
-    for v, _ in test_vectors {
-        computed     := md4.hash(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
-
 }
 
 @(test)
@@ -197,12 +188,6 @@ test_md5 :: proc(t: ^testing.T) {
         computed_str := hex_string(computed[:])
         expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
     }
-    md5.use_botan()
-    for v, _ in test_vectors {
-        computed     := md5.hash(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
 }
 
 @(test)
@@ -225,15 +210,8 @@ test_sha1 :: proc(t: ^testing.T) {
         computed_str := hex_string(computed[:])
         expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
     }
-    sha1.use_botan()
-    for v, _ in test_vectors {
-        computed     := sha1.hash(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
 }
 
-
 @(test)
 test_sha224 :: proc(t: ^testing.T) {
     // Test vectors from 
@@ -250,12 +228,6 @@ test_sha224 :: proc(t: ^testing.T) {
         computed_str := hex_string(computed[:])
         expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
     }
-    sha2.use_botan()
-    for v, _ in test_vectors {
-        computed     := sha2.hash_224(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
 }
 
 @(test)
@@ -274,13 +246,6 @@ test_sha256 :: proc(t: ^testing.T) {
         computed_str := hex_string(computed[:])
         expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
     }
-    sha2.use_botan()
-    for v, _ in test_vectors {
-        computed     := sha2.hash_256(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
-
 }
 
 @(test)
@@ -299,13 +264,6 @@ test_sha384 :: proc(t: ^testing.T) {
         computed_str := hex_string(computed[:])
         expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
     }
-    sha2.use_botan()
-    for v, _ in test_vectors {
-        computed     := sha2.hash_384(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
-
 }
 
 @(test)
@@ -324,15 +282,8 @@ test_sha512 :: proc(t: ^testing.T) {
         computed_str := hex_string(computed[:])
         expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
     }
-    sha2.use_botan()
-    for v, _ in test_vectors {
-        computed     := sha2.hash_512(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
 }
 
-
 @(test)
 test_sha3_224 :: proc(t: ^testing.T) {
     // Test vectors from 
@@ -353,12 +304,6 @@ test_sha3_224 :: proc(t: ^testing.T) {
         computed_str := hex_string(computed[:])
         expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
     }
-    sha3.use_botan()
-    for v, _ in test_vectors {
-        computed     := sha3.hash_224(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
 }
 
 @(test)
@@ -381,12 +326,6 @@ test_sha3_256 :: proc(t: ^testing.T) {
         computed_str := hex_string(computed[:])
         expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
     }
-    sha3.use_botan()
-    for v, _ in test_vectors {
-        computed     := sha3.hash_256(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
 }
 
 @(test)
@@ -409,12 +348,6 @@ test_sha3_384 :: proc(t: ^testing.T) {
         computed_str := hex_string(computed[:])
         expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
     }
-    sha3.use_botan()
-    for v, _ in test_vectors {
-        computed     := sha3.hash_384(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
 }
 
 @(test)
@@ -437,12 +370,6 @@ test_sha3_512 :: proc(t: ^testing.T) {
         computed_str := hex_string(computed[:])
         expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
     }
-    sha3.use_botan()
-    for v, _ in test_vectors {
-        computed     := sha3.hash_512(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
 }
 
 @(test)
@@ -457,12 +384,6 @@ test_shake_128 :: proc(t: ^testing.T) {
         computed_str := hex_string(computed[:])
         expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
     }
-    sha3.use_botan()
-    for v, _ in test_vectors {
-        computed     := shake.hash_128(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
 }
 
 @(test)
@@ -477,12 +398,6 @@ test_shake_256 :: proc(t: ^testing.T) {
         computed_str := hex_string(computed[:])
         expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
     }
-    sha3.use_botan()
-    for v, _ in test_vectors {
-        computed     := shake.hash_256(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
 }
 
 @(test)
@@ -499,12 +414,6 @@ test_keccak_224 :: proc(t: ^testing.T) {
         computed_str := hex_string(computed[:])
         expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
     }
-    keccak.use_botan()
-    for v, _ in test_vectors {
-        computed     := keccak.hash_224(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
 }
 
 @(test)
@@ -521,12 +430,6 @@ test_keccak_256 :: proc(t: ^testing.T) {
         computed_str := hex_string(computed[:])
         expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
     }
-    keccak.use_botan()
-    for v, _ in test_vectors {
-        computed     := keccak.hash_256(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
 }
 
 @(test)
@@ -543,12 +446,6 @@ test_keccak_384 :: proc(t: ^testing.T) {
         computed_str := hex_string(computed[:])
         expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
     }
-    keccak.use_botan()
-    for v, _ in test_vectors {
-        computed     := keccak.hash_384(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
 }
 
 @(test)
@@ -565,12 +462,6 @@ test_keccak_512 :: proc(t: ^testing.T) {
         computed_str := hex_string(computed[:])
         expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
     }
-    keccak.use_botan()
-    for v, _ in test_vectors {
-        computed     := keccak.hash_512(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
 }
 
 @(test)
@@ -597,12 +488,6 @@ test_whirlpool :: proc(t: ^testing.T) {
         computed_str := hex_string(computed[:])
         expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
     }
-    whirlpool.use_botan()
-    for v, _ in test_vectors {
-        computed     := whirlpool.hash(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
 }
 
 @(test)
@@ -623,12 +508,6 @@ test_gost :: proc(t: ^testing.T) {
         computed_str := hex_string(computed[:])
         expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
     }
-    gost.use_botan()
-    for v, _ in test_vectors {
-        computed     := gost.hash(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
 }
 
 @(test)
@@ -643,12 +522,6 @@ test_streebog_256 :: proc(t: ^testing.T) {
         computed_str := hex_string(computed[:])
         expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
     }
-    streebog.use_botan()
-    for v, _ in test_vectors {
-        computed     := streebog.hash_256(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
 }
 
 @(test)
@@ -663,12 +536,6 @@ test_streebog_512 :: proc(t: ^testing.T) {
         computed_str := hex_string(computed[:])
         expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
     }
-    streebog.use_botan()
-    for v, _ in test_vectors {
-        computed     := streebog.hash_512(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
 }
 
 @(test)
@@ -738,12 +605,6 @@ test_blake2b :: proc(t: ^testing.T) {
         computed_str := hex_string(computed[:])
         expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
     }
-    blake2b.use_botan()
-    for v, _ in test_vectors {
-        computed     := blake2b.hash(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
 }
 
 @(test)
@@ -797,12 +658,6 @@ test_ripemd_160 :: proc(t: ^testing.T) {
         computed_str := hex_string(computed[:])
         expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
     }
-    ripemd.use_botan()
-    for v, _ in test_vectors {
-        computed     := ripemd.hash_160(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
 }
 
 @(test)
@@ -863,12 +718,6 @@ test_tiger_128 :: proc(t: ^testing.T) {
         computed_str := hex_string(computed[:])
         expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
     }
-    tiger.use_botan()
-    for v, _ in test_vectors {
-        computed     := tiger.hash_128(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
 }
 
 @(test)
@@ -889,12 +738,6 @@ test_tiger_160 :: proc(t: ^testing.T) {
         computed_str := hex_string(computed[:])
         expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
     }
-    tiger.use_botan()
-    for v, _ in test_vectors {
-        computed     := tiger.hash_160(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
 }
 
 @(test)
@@ -915,12 +758,6 @@ test_tiger_192 :: proc(t: ^testing.T) {
         computed_str := hex_string(computed[:])
         expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
     }
-    tiger.use_botan()
-    for v, _ in test_vectors {
-        computed     := tiger.hash_192(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
 }
 
 @(test)
@@ -979,26 +816,6 @@ test_sm3 :: proc(t: ^testing.T) {
         computed_str := hex_string(computed[:])
         expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
     }
-    sm3.use_botan()
-    for v, _ in test_vectors {
-        computed     := sm3.hash(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
-}
-
-@(test)
-test_skein512 :: proc(t: ^testing.T) {
-    test_vectors := [?]TestHash {
-        TestHash{"bc5b4c50925519c290cc634277ae3d6257212395cba733bbad37a4af0fa06af41fca7903d06564fea7a2d3730dbdb80c1f85562dfcc070334ea4d1d9e72cba7a", ""},
-        TestHash{"94c2ae036dba8783d0b3f7d6cc111ff810702f5c77707999be7e1c9486ff238a7044de734293147359b4ac7e1d09cd247c351d69826b78dcddd951f0ef912713", "The quick brown fox jumps over the lazy dog"},
-    }
-    skein.use_botan()
-    for v, _ in test_vectors {
-        computed     := skein.hash_skein512(v.str, 64)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
 }
 
 @(test)

+ 109 - 0
vendor/raylib/README.md

@@ -0,0 +1,109 @@
+<img align="left" src="https://github.com/raysan5/raylib/blob/master/logo/raylib_logo_animation.gif" width="288px">
+
+**raylib is a simple and easy-to-use library to enjoy videogames programming.**
+
+raylib is highly inspired by Borland BGI graphics lib and by XNA framework and it's specially well suited for prototyping, tooling, graphical applications, embedded systems and education.
+
+*NOTE for ADVENTURERS: raylib is a programming library to enjoy videogames programming; no fancy interface, no visual helpers, no debug button... just coding in the most pure spartan-programmers way.*
+
+Ready to learn? Jump to [code examples!](https://www.raylib.com/examples.html)
+
+---
+
+<br>
+
+[![GitHub contributors](https://img.shields.io/github/contributors/raysan5/raylib)](https://github.com/raysan5/raylib/graphs/contributors)
+[![GitHub All Releases](https://img.shields.io/github/downloads/raysan5/raylib/total)](https://github.com/raysan5/raylib/releases)
+[![GitHub commits since tagged version](https://img.shields.io/github/commits-since/raysan5/raylib/4.0.0)](https://github.com/raysan5/raylib/commits/master)
+[![License](https://img.shields.io/badge/license-zlib%2Flibpng-blue.svg)](LICENSE)
+
+[![Chat on Discord](https://img.shields.io/discord/426912293134270465.svg?logo=discord)](https://discord.gg/raylib)
+[![GitHub stars](https://img.shields.io/github/stars/raysan5/raylib?style=social)](https://github.com/raysan5/raylib/stargazers)
+[![Twitter Follow](https://img.shields.io/twitter/follow/raysan5?style=social)](https://twitter.com/raysan5)
+[![Subreddit subscribers](https://img.shields.io/reddit/subreddit-subscribers/raylib?style=social)](https://www.reddit.com/r/raylib/)
+
+[![Windows](https://github.com/raysan5/raylib/workflows/Windows/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3AWindows)
+[![Linux](https://github.com/raysan5/raylib/workflows/Linux/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3ALinux)
+[![macOS](https://github.com/raysan5/raylib/workflows/macOS/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3AmacOS)
+[![Android](https://github.com/raysan5/raylib/workflows/Android/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3AAndroid)
+[![WebAssembly](https://github.com/raysan5/raylib/workflows/WebAssembly/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3AWebAssembly)
+
+[![CMakeBuilds](https://github.com/raysan5/raylib/workflows/CMakeBuilds/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3ACMakeBuilds)
+[![Windows Examples](https://github.com/raysan5/raylib/actions/workflows/windows_examples.yml/badge.svg)](https://github.com/raysan5/raylib/actions/workflows/windows_examples.yml)
+[![Linux Examples](https://github.com/raysan5/raylib/actions/workflows/linux_examples.yml/badge.svg)](https://github.com/raysan5/raylib/actions/workflows/linux_examples.yml)
+
+features
+--------
+  - **NO external dependencies**, all required libraries are [bundled into raylib](https://github.com/raysan5/raylib/tree/master/src/external)
+  - Multiple platforms supported: **Windows, Linux, MacOS, RPI, Android, HTML5... and more!**
+  - Written in plain C code (C99) in PascalCase/camelCase notation
+  - Hardware accelerated with OpenGL (**1.1, 2.1, 3.3, 4.3 or ES 2.0**)
+  - **Unique OpenGL abstraction layer** (usable as standalone module): [rlgl](https://github.com/raysan5/raylib/blob/master/src/rlgl.h)
+  - Multiple **Fonts** formats supported (TTF, XNA fonts, AngelCode fonts)
+  - Multiple texture formats supported, including **compressed formats** (DXT, ETC, ASTC)
+  - **Full 3D support**, including 3D Shapes, Models, Billboards, Heightmaps and more! 
+  - Flexible Materials system, supporting classic maps and **PBR maps**
+  - **Animated 3D models** supported (skeletal bones animation) (IQM)
+  - Shaders support, including model and **postprocessing** shaders.
+  - **Powerful math module** for Vector, Matrix and Quaternion operations: [raymath](https://github.com/raysan5/raylib/blob/master/src/raymath.h)
+  - Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, XM, MOD)
+  - **VR stereo rendering** support with configurable HMD device parameters
+  - Huge examples collection with [+120 code examples](https://github.com/raysan5/raylib/tree/master/examples)!
+  - Bindings to [+50 programming languages](https://github.com/raysan5/raylib/blob/master/BINDINGS.md)!
+  - **Free and open source**.
+
+basic example
+--------------
+This is a basic raylib example, it creates a window and it draws the text `"Congrats! You created your first window!"` in the middle of the screen. Check this example [running live on web here](https://www.raylib.com/examples/core/loader.html?name=core_basic_window).
+```odin
+package example
+
+import rl "vendor:raylib"
+
+main :: proc() {
+	rl.InitWindow(800, 450, "raylib [core] example - basic window")
+
+	for !rl.WindowShouldClose() {
+		rl.BeginDrawing()
+			rl.ClearBackground(rl.RAYWHITE)
+			rl.DrawText("Congrats! You created your first window!", 190, 200, 20, rl.LIGHTGRAY)
+		rl.EndDrawing()
+	}
+
+	rl.CloseWindow()
+}
+```
+
+learning and docs
+------------------
+
+raylib is designed to be learned using [the examples](https://github.com/raysan5/raylib/tree/master/examples) as the main reference. There is no standard API documentation but there is a [**cheatsheet**](https://www.raylib.com/cheatsheet/cheatsheet.html) containing all the functions available on the library and a short description of each one of them, input parameters and result value names should be intuitive enough to understand how each function works. 
+
+Some additional documentation about raylib design can be found in raylib GitHub Wiki. Here the more relevant links:
+
+ - [raylib cheatsheet](https://www.raylib.com/cheatsheet/cheatsheet.html)
+ - [raylib architecture](https://github.com/raysan5/raylib/wiki/raylib-architecture)
+ - [raylib library design](https://github.com/raysan5/raylib/wiki)
+ - [raylib examples collection](https://github.com/raysan5/raylib/tree/master/examples)
+ - [raylib games collection](https://github.com/raysan5/raylib-games)
+
+
+contact and networks
+---------------------
+
+raylib is present in several networks and raylib community is growing everyday. If you are using raylib and enjoying it, feel free to join us in any of these networks. The most active network is our [Discord server](https://discord.gg/raylib)! :)
+
+ - Webpage: [https://www.raylib.com](https://www.raylib.com)
+ - Discord: [https://discord.gg/raylib](https://discord.gg/raylib)
+ - Twitter: [https://www.twitter.com/raysan5](https://www.twitter.com/raysan5)
+ - Twitch:  [https://www.twitch.tv/raysan5](https://www.twitch.tv/raysan5)
+ - Reddit:  [https://www.reddit.com/r/raylib](https://www.reddit.com/r/raylib)
+ - Patreon: [https://www.patreon.com/raylib](https://www.patreon.com/raylib)
+ - YouTube: [https://www.youtube.com/channel/raylib](https://www.youtube.com/c/raylib)
+
+license
+-------
+
+raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified, BSD-like license that allows static linking with closed source software. Check [LICENSE](LICENSE) for further details.
+
+raylib uses internally some libraries for window/graphics/inputs management and also to support different fileformats loading, all those libraries are embedded with and are available in [src/external](https://github.com/raysan5/raylib/tree/master/src/external) directory. Check [raylib dependencies LICENSES](https://github.com/raysan5/raylib/wiki/raylib-dependencies) on raylib Wiki for details.

BIN
vendor/raylib/linux/libraylib.a


BIN
vendor/raylib/linux/libraylib.so


BIN
vendor/raylib/linux/libraylib.so.3.7.0


BIN
vendor/raylib/linux/libraylib.so.370


BIN
vendor/raylib/linux/libraylib.so.4.0.0


BIN
vendor/raylib/linux/libraylib.so.400


BIN
vendor/raylib/macos/libraylib.3.7.0.dylib


BIN
vendor/raylib/macos/libraylib.370.dylib


BIN
vendor/raylib/macos/libraylib.4.0.0.dylib


BIN
vendor/raylib/macos/libraylib.400.dylib


BIN
vendor/raylib/macos/libraylib.a


BIN
vendor/raylib/macos/libraylib.dylib


BIN
vendor/raylib/raylib.lib


+ 246 - 142
vendor/raylib/raylib.odin

@@ -1,3 +1,79 @@
+/**********************************************************************************************
+*
+*   raylib v4.0 - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com)
+*
+*   FEATURES:
+*       - NO external dependencies, all required libraries included with raylib
+*       - Multiplatform: Windows, Linux, FreeBSD, OpenBSD, NetBSD, DragonFly,
+*                        MacOS, Haiku, Android, Raspberry Pi, DRM native, HTML5.
+*       - Written in plain C code (C99) in PascalCase/camelCase notation
+*       - Hardware accelerated with OpenGL (1.1, 2.1, 3.3, 4.3 or ES2 - choose at compile)
+*       - Unique OpenGL abstraction layer (usable as standalone module): [rlgl]
+*       - Multiple Fonts formats supported (TTF, XNA fonts, AngelCode fonts)
+*       - Outstanding texture formats support, including compressed formats (DXT, ETC, ASTC)
+*       - Full 3d support for 3d Shapes, Models, Billboards, Heightmaps and more!
+*       - Flexible Materials system, supporting classic maps and PBR maps
+*       - Animated 3D models supported (skeletal bones animation) (IQM)
+*       - Shaders support, including Model shaders and Postprocessing shaders
+*       - Powerful math module for Vector, Matrix and Quaternion operations: [raymath]
+*       - Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, XM, MOD)
+*       - VR stereo rendering with configurable HMD device parameters
+*       - Bindings to multiple programming languages available!
+*
+*   NOTES:
+*       - One default Font is loaded on InitWindow()->LoadFontDefault() [core, text]
+*       - One default Texture2D is loaded on rlglInit(), 1x1 white pixel R8G8B8A8 [rlgl] (OpenGL 3.3 or ES2)
+*       - One default Shader is loaded on rlglInit()->rlLoadShaderDefault() [rlgl] (OpenGL 3.3 or ES2)
+*       - One default RenderBatch is loaded on rlglInit()->rlLoadRenderBatch() [rlgl] (OpenGL 3.3 or ES2)
+*
+*   DEPENDENCIES (included):
+*       [rcore] rglfw (Camilla Löwy - github.com/glfw/glfw) for window/context management and input (PLATFORM_DESKTOP)
+*       [rlgl] glad (David Herberth - github.com/Dav1dde/glad) for OpenGL 3.3 extensions loading (PLATFORM_DESKTOP)
+*       [raudio] miniaudio (David Reid - github.com/mackron/miniaudio) for audio device/context management
+*
+*   OPTIONAL DEPENDENCIES (included):
+*       [rcore] msf_gif (Miles Fogle) for GIF recording
+*       [rcore] sinfl (Micha Mettke) for DEFLATE decompression algorythm
+*       [rcore] sdefl (Micha Mettke) for DEFLATE compression algorythm
+*       [rtextures] stb_image (Sean Barret) for images loading (BMP, TGA, PNG, JPEG, HDR...)
+*       [rtextures] stb_image_write (Sean Barret) for image writing (BMP, TGA, PNG, JPG)
+*       [rtextures] stb_image_resize (Sean Barret) for image resizing algorithms
+*       [rtext] stb_truetype (Sean Barret) for ttf fonts loading
+*       [rtext] stb_rect_pack (Sean Barret) for rectangles packing
+*       [rmodels] par_shapes (Philip Rideout) for parametric 3d shapes generation
+*       [rmodels] tinyobj_loader_c (Syoyo Fujita) for models loading (OBJ, MTL)
+*       [rmodels] cgltf (Johannes Kuhlmann) for models loading (glTF)
+*       [raudio] dr_wav (David Reid) for WAV audio file loading
+*       [raudio] dr_flac (David Reid) for FLAC audio file loading
+*       [raudio] dr_mp3 (David Reid) for MP3 audio file loading
+*       [raudio] stb_vorbis (Sean Barret) for OGG audio loading
+*       [raudio] jar_xm (Joshua Reisenauer) for XM audio module loading
+*       [raudio] jar_mod (Joshua Reisenauer) for MOD audio module loading
+*
+*
+*   LICENSE: zlib/libpng
+*
+*   raylib is licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+*   BSD-like license that allows static linking with closed source software:
+*
+*   Copyright (c) 2013-2021 Ramon Santamaria (@raysan5)
+*
+*   This software is provided "as-is", without any express or implied warranty. In no event
+*   will the authors be held liable for any damages arising from the use of this software.
+*
+*   Permission is granted to anyone to use this software for any purpose, including commercial
+*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+*     1. The origin of this software must not be misrepresented; you must not claim that you
+*     wrote the original software. If you use this software in a product, an acknowledgment
+*     in the product documentation would be appreciated but is not required.
+*
+*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
+*     as being the original software.
+*
+*     3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
 package raylib
 
 import c "core:c/libc"
@@ -33,9 +109,12 @@ when ODIN_OS == "linux"  {
 }
 when ODIN_OS == "darwin" { foreign import lib "macos/libraylib.a" }
 
-VERSION :: "3.7"
+VERSION :: "4.0"
+
+PI :: 3.14159265358979323846 
+DEG2RAD :: PI/180.0
+RAD2DEG :: 180.0/PI
 
-PI :: 3.14159265358979323846
 
 // Some Basic Colors
 // NOTE: Custom raylib color palette for amazing visuals on WHITE background
@@ -91,27 +170,30 @@ when USE_LINALG {
 
 	// Quaternion type
 	Quaternion :: distinct quaternion128
-
-	// Matrix type (OpenGL style 4x4 - right handed, column major)
+	
+	// Matrix, 4x4 components, column major, OpenGL style, right handed
 	Matrix :: struct {
-		m0, m4, m8, m12:  f32,
-		m1, m5, m9, m13:  f32,
-		m2, m6, m10, m14: f32,
-		m3, m7, m11, m15: f32,
+		m0, m4, m8, m12:  f32, // Matrix first row (4 components)
+		m1, m5, m9, m13:  f32, // Matrix second row (4 components)
+		m2, m6, m10, m14: f32, // Matrix third row (4 components)
+		m3, m7, m11, m15: f32, // Matrix fourth row (4 components)
 	}
 }
 
-// Color type, RGBA (32bit)
+// Color, 4 components, R8G8B8A8 (32bit)
 Color :: struct {
-	r, g, b, a: u8,
+	r: u8,                        // Color red value
+	g: u8,                        // Color green value
+	b: u8,                        // Color blue value
+	a: u8,                        // Color alpha value
 }
 
 // Rectangle type
 Rectangle :: struct {
-	x:      f32,
-	y:      f32,
-	width:  f32,
-	height: f32,
+	x:      f32,                  // Rectangle top-left corner position x
+	y:      f32,                  // Rectangle top-left corner position y
+	width:  f32,                  // Rectangle width
+	height: f32,                  // Rectangle height
 }
 
 // Image type, bpp always RGBA (32bit)
@@ -161,7 +243,7 @@ NPatchInfo :: struct {
 }
 
 // Font character info
-CharInfo :: struct {
+GlyphInfo :: struct {
 	value:    rune,               // Character value (Unicode)
 	offsetX:  c.int,              // Character offset X when drawing
 	offsetY:  c.int,              // Character offset Y when drawing
@@ -176,7 +258,7 @@ Font :: struct {
 	charsPadding: c.int,          // Padding around the chars
 	texture:      Texture2D,      // Characters texture atlas
 	recs:         [^]Rectangle,   // Characters rectangles in texture
-	chars:        [^]CharInfo,    // Characters info data
+	chars:        [^]GlyphInfo,    // Characters info data
 }
 
 SpriteFont :: Font                    // SpriteFont type fallback, defaults to Font
@@ -289,11 +371,11 @@ Ray :: struct {
 	direction: Vector3,           // Ray direction
 }
 
-// Raycast hit information
-RayHitInfo :: struct {
+// RayCollision, ray hit information
+RayCollision :: struct {
 	hit:      bool,               // Did the ray hit something?
 	distance: f32,                // Distance to nearest hit
-	position: Vector3,            // Position of nearest hit
+	point:    Vector3,            // Point of nearest hit
 	normal:   Vector3,            // Surface normal of hit
 }
 
@@ -838,6 +920,16 @@ foreign lib {
 	SetClipboardText         :: proc(text: cstring) ---                         // Set clipboard text content
 	GetClipboardText         :: proc() -> cstring ---                           // Get clipboard text content
 
+	
+	// Custom frame control functions
+	// NOTE: Those functions are intended for advance users that want full control over the frame processing
+	// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timming + PollInputEvents()
+	// To avoid that behaviour and control frame processes manually, enable in config.h: SUPPORT_CUSTOM_FRAME_CONTROL
+	SwapScreenBuffer :: proc() ---        // Swap back buffer with front buffer (screen drawing)
+	PollInputEvents  :: proc() ---        // Register all input events
+	WaitTime         :: proc(ms: f32) --- // Wait for some milliseconds (halt program execution)
+
+
 	// Cursor-related functions
 	ShowCursor       :: proc() ---                                      // Shows cursor
 	HideCursor       :: proc() ---                                      // Hides cursor
@@ -898,6 +990,7 @@ foreign lib {
 
 	// Misc. functions
 	GetRandomValue :: proc(min, max: c.int) -> c.int --- // Returns a random value between min and max (both included)
+	SetRandomSeed  :: proc(seed: c.uint) ---             // Set the seed for the random number generator
 	TakeScreenshot :: proc(fileName: cstring) ---        // Takes a screenshot of current screen (filename extension defines format)
 	SetConfigFlags :: proc(flags: ConfigFlags) ---       // Setup init configuration flags (view FLAGS)
 
@@ -939,8 +1032,10 @@ foreign lib {
 	ClearDroppedFiles     :: proc() ---                                                              // Clear dropped files paths buffer (free memory)
 	GetFileModTime        :: proc(fileName: cstring) -> c.long ---                                   // Get file modification time (last write time)
 
-	CompressData   :: proc(data: [^]u8, dataLength: c.int, compDataLength: ^c.int) -> [^]u8 ---      // Compress data (DEFLATE algorithm)
-	DecompressData :: proc(compData: [^]u8, compDataLength: c.int, dataLength: ^c.int) -> [^]u8 ---  // Decompress data (DEFLATE algorithm)
+	CompressData     :: proc(data: [^]u8, dataLength: c.int, compDataLength: ^c.int) -> [^]u8 ---     // Compress data (DEFLATE algorithm)
+	DecompressData   :: proc(compData: [^]u8, compDataLength: c.int, dataLength: ^c.int) -> [^]u8 --- // Decompress data (DEFLATE algorithm)
+	EncodeDataBase64 :: proc(data: [^]u8, dataLength: c.int, outputLength: ^c.int) -> [^]u8 ---       // Encode data to Base64 string
+	DecodeDataBase64 :: proc(data: [^]u8, outputLength: ^c.int) -> [^]u8 ---                          // Decode Base64 string data
 
 	// Persistent storage management
 	SaveStorageValue :: proc(position: c.uint, value: c.int) -> bool ---   // Save integer value to storage file (to defined position), returns true on success
@@ -989,9 +1084,11 @@ foreign lib {
 	SetMouseCursor        :: proc(cursor: MouseCursor) ---            // Set mouse cursor
 
 	// Input-related functions: touch
-	GetTouchX :: proc() -> c.int ---                      // Returns touch position X for touch point 0 (relative to screen size)
-	GetTouchY :: proc() -> c.int ---                      // Returns touch position Y for touch point 0 (relative to screen size)
-	GetTouchPosition :: proc(index: c.int) -> Vector2 --- // Returns touch position XY for a touch point index (relative to screen size)
+	GetTouchX          :: proc() -> c.int ---               // Returns touch position X for touch point 0 (relative to screen size)
+	GetTouchY          :: proc() -> c.int ---               // Returns touch position Y for touch point 0 (relative to screen size)
+	GetTouchPosition   :: proc(index: c.int) -> Vector2 --- // Returns touch position XY for a touch point index (relative to screen size)
+	GetTouchPointId    :: proc(index: c.int) -> c.int ---   // Get touch point identifier for given index
+	GetTouchPointCount :: proc() -> c.int ---               // Get number of touch points
 
 	//------------------------------------------------------------------------------------
 	// Gestures and Touch Handling Functions (Module: gestures)
@@ -999,7 +1096,6 @@ foreign lib {
 	SetGesturesEnabled     :: proc(flags: Gestures) ---          // Enable a set of gestures using flags
 	IsGestureDetected      :: proc(gesture: Gesture) -> bool --- // Check if a gesture have been detected
 	GetGestureDetected     :: proc() -> Gesture ---              // Get latest detected gesture
-	GetTouchPointsCount    :: proc() -> c.int ---                // Get touch points count
 	GetGestureHoldDuration :: proc() -> f32 ---                  // Get gesture hold time in milliseconds
 	GetGestureDragVector   :: proc() -> Vector2 ---              // Get gesture drag vector
 	GetGestureDragAngle    :: proc() -> f32 ---                  // Get gesture drag angle
@@ -1033,6 +1129,7 @@ foreign lib {
 	DrawLineEx                :: proc(startPos, endPos: Vector2, thick: f32, color: Color) ---                                                         // Draw a line defining thickness
 	DrawLineBezier            :: proc(startPos, endPos: Vector2, thick: f32, color: Color) ---                                                         // Draw a line using cubic-bezier curves in-out
 	DrawLineBezierQuad        :: proc(startPos, endPos: Vector2, controlPos: Vector2, thick: f32, color: Color) ---                                    // Draw line using quadratic bezier curves with a control point
+	DrawLineBezierCubic       :: proc(startPos, endPos: Vector2, startControlPos, endControlPos: Vector2, thick: f32, color: Color) ---                // Draw line using cubic bezier curves with 2 control points
 	DrawLineStrip             :: proc(points: [^]Vector2, pointsCount: c.int, color: Color) ---                                                        // Draw lines sequence
 	DrawCircle                :: proc(centerX, centerY: c.int, radius: f32, color: Color) ---                                                          // Draw a color-filled circle
 	DrawCircleSector          :: proc(center: Vector2, radius: f32, startAngle, endAngle: f32, segments: c.int, color: Color) ---                      // Draw a piece of a circle
@@ -1061,6 +1158,7 @@ foreign lib {
 	DrawTriangleStrip         :: proc(points: [^]Vector2, pointsCount: c.int, color: Color) ---                                                        // Draw a triangle strip defined by points
 	DrawPoly                  :: proc(center: Vector2, sides: c.int, radius: f32, rotation: f32, color: Color) ---                                     // Draw a regular polygon (Vector version)
 	DrawPolyLines             :: proc(center: Vector2, sides: c.int, radius: f32, rotation: f32, color: Color) ---                                     // Draw a polygon outline of n sides
+	DrawPolyLinesEx           :: proc(center: Vector2, sides: c.int, radius: f32, rotation: f32, lineThick: f32, color: Color) ---                     // Draw a polygon outline of n sides with extended parameters
 
 	// Basic shapes collision detection functions
 	CheckCollisionRecs          :: proc(rec1, rec2: Rectangle) -> bool ---                                                     // Check collision between two rectangles
@@ -1070,6 +1168,7 @@ foreign lib {
 	CheckCollisionPointCircle   :: proc(point: Vector2, center: Vector2, radius: f32) -> bool ---                              // Check if point is inside circle
 	CheckCollisionPointTriangle :: proc(point, p1, p2, p3: Vector2) -> bool ---                                                // Check if point is inside a triangle
 	CheckCollisionLines         :: proc(startPos1, endPos1, startPos2, endPos2: Vector2, collisionPoint: ^Vector2) -> bool --- // Check the collision between two lines defined by two points each, returns collision point by reference
+	CheckCollisionPointLine     :: proc(point, p1, p2: Vector2, threshold: c.int) -> bool ---                                  // Check if point belongs to line created between two points [p1] and [p2] with defined margin in pixels [threshold]
 	GetCollisionRec             :: proc(rec1, rec2: Rectangle) -> Rectangle ---                                                // Get collision rectangle for two rectangles collision
 
 	//------------------------------------------------------------------------------------
@@ -1078,13 +1177,14 @@ foreign lib {
 
 	// Image loading functions
 	// NOTE: This functions do not require GPU access
-	LoadImage           :: proc(fileName: cstring) -> Image ---                                                         // Load image from file into CPU memory (RAM)
-	LoadImageRaw        :: proc(fileName: cstring, width, height: c.int, format: PixelFormat, headerSize: c.int) -> Image --- // Load image from RAW file data
-	LoadImageAnim       :: proc(fileName: cstring, frames: ^c.int) -> Image ---                                         // Load image sequence from file (frames appended to image.data)
-	LoadImageFromMemory :: proc(fileType: cstring, fileData: rawptr, dataSize: c.int) -> Image ---                       // Load image from memory buffer, fileType refers to extension: i.e. ".png"
-	UnloadImage         :: proc(image: Image) ---                                                                       // Unload image from CPU memory (RAM)
-	ExportImage         :: proc(image: Image, fileName: cstring) -> bool ---                                            // Export image data to file, returns true on success
-	ExportImageAsCode   :: proc(image: Image, fileName: cstring) -> bool ---                                            // Export image as code file defining an array of bytes, returns true on success
+	LoadImage            :: proc(fileName: cstring) -> Image ---                                                               // Load image from file into CPU memory (RAM)
+	LoadImageRaw         :: proc(fileName: cstring, width, height: c.int, format: PixelFormat, headerSize: c.int) -> Image --- // Load image from RAW file data
+	LoadImageAnim        :: proc(fileName: cstring, frames: ^c.int) -> Image ---                                               // Load image sequence from file (frames appended to image.data)
+	LoadImageFromMemory  :: proc(fileType: cstring, fileData: rawptr, dataSize: c.int) -> Image ---                            // Load image from memory buffer, fileType refers to extension: i.e. ".png"
+	LoadImageFromTexture :: proc(texture: Texture2D) -> Image ---                                                              // Load image from GPU texture data
+	UnloadImage          :: proc(image: Image) ---                                                                             // Unload image from CPU memory (RAM)
+	ExportImage          :: proc(image: Image, fileName: cstring) -> bool ---                                                  // Export image data to file, returns true on success
+	ExportImageAsCode    :: proc(image: Image, fileName: cstring) -> bool ---                                                  // Export image as code file defining an array of bytes, returns true on success
 
 	// Image generation functions
 	GenImageColor          :: proc(width, height: c.int, color: Color) -> Image ---                               // Generate image: plain color
@@ -1093,7 +1193,6 @@ foreign lib {
 	GenImageGradientRadial :: proc(width, height: c.int, density: f32, inner, outer: Color) -> Image ---          // Generate image: radial gradient
 	GenImageChecked        :: proc(width, height: c.int, checksX, checksY: c.int, col1, col2: Color) -> Image --- // Generate image: checked
 	GenImageWhiteNoise     :: proc(width, height: c.int, factor: f32) -> Image ---                                // Generate image: white noise
-	GenImagePerlinNoise    :: proc(width, height: c.int, offsetX, offsetY: c.int, scale: f32) -> Image ---        // Generate image: perlin noise
 	GenImageCellular       :: proc(width, height: c.int, tileSize: c.int) -> Image ---                            // Generate image: cellular algorithm. Bigger tileSize means bigger cells
 
 	// Image manipulation functions
@@ -1128,6 +1227,7 @@ foreign lib {
 	UnloadImageColors     :: proc(colors: [^]Color) ---                                                                // Unload color data loaded with LoadImageColors()
 	UnloadImagePalette    :: proc(colors: [^]Color) ---                                                                // Unload colors palette loaded with LoadImagePalette()
 	GetImageAlphaBorder   :: proc(image: Image, threshold: f32) -> Rectangle ---                                       // Get image alpha border rectangle
+	GetImageColor         :: proc(image: Image, x, y: c.int) -> Color ---                                              // Get image pixel color at (x, y) position
 
 	// Image drawing functions
 	// NOTE: Image software-rendering functions (CPU)
@@ -1156,8 +1256,6 @@ foreign lib {
 	UnloadRenderTexture  :: proc(target: RenderTexture2D) ---                               // Unload render texture from GPU memory (VRAM)
 	UpdateTexture        :: proc(texture: Texture2D, pixels: rawptr) ---                    // Update GPU texture with new data
 	UpdateTextureRec     :: proc(texture: Texture2D, rec: Rectangle, pixels: rawptr) ---    // Update GPU texture rectangle with new data
-	GetTextureData       :: proc(texture: Texture2D) -> Image ---                           // Get pixel data from GPU texture and return an Image
-	GetScreenData        :: proc() -> Image ---                                             // Get pixel data from screen buffer and return an Image (screenshot)
 
 	// Texture configuration functions
 	GenTextureMipmaps :: proc(texture: ^Texture2D) ---                       // Generate GPU mipmaps for a texture
@@ -1199,24 +1297,34 @@ foreign lib {
 	LoadFontEx         :: proc(fileName: cstring, fontSize: c.int, fontChars: [^]rune, charsCount: c.int) -> Font ---                                        // Load font from file with extended parameters
 	LoadFontFromImage  :: proc(image: Image, key: Color, firstChar: rune) -> Font ---                                                                        // Load font from Image (XNA style)
 	LoadFontFromMemory :: proc(fileType: cstring, fileData: rawptr, dataSize: c.int, fontSize: c.int, fontChars: [^]rune, charsCount: c.int) -> Font ---     // Load font from memory buffer, fileType refers to extension: i.e. ".ttf"
-	LoadFontData       :: proc(fileData: rawptr, dataSize: c.int, fontSize: c.int, fontChars: [^]rune, charsCount: c.int, type: FontType) -> [^]CharInfo --- // Load font data for further use
-	GenImageFontAtlas  :: proc(chars: [^]CharInfo, recs: ^[^]Rectangle, charsCount: c.int, fontSize: c.int, padding: c.int, packMethod: c.int) -> Image ---  // Generate image font atlas using chars info
-	UnloadFontData     :: proc(chars: [^]CharInfo, charsCount: c.int) ---                                                                                    // Unload font chars info data (RAM)
+	LoadFontData       :: proc(fileData: rawptr, dataSize: c.int, fontSize: c.int, fontChars: [^]rune, charsCount: c.int, type: FontType) -> [^]GlyphInfo --- // Load font data for further use
+	GenImageFontAtlas  :: proc(chars: [^]GlyphInfo, recs: ^[^]Rectangle, charsCount: c.int, fontSize: c.int, padding: c.int, packMethod: c.int) -> Image ---  // Generate image font atlas using chars info
+	UnloadFontData     :: proc(chars: [^]GlyphInfo, charsCount: c.int) ---                                                                                    // Unload font chars info data (RAM)
 	UnloadFont         :: proc(font: Font) ---                                                                                                               // Unload Font from GPU memory (VRAM)
 
 	// Text drawing functions
 	DrawFPS           :: proc(posX, posY: c.int) ---                                                                              // Draw current FPS
 	DrawText          :: proc(text: cstring, posX, posY: c.int, fontSize: c.int, color: Color) ---                                // Draw text (using default font)
 	DrawTextEx        :: proc(font: Font, text: cstring, position: Vector2, fontSize: f32, spacing: f32, tint: Color) ---         // Draw text using font and additional parameters
-	DrawTextRec       :: proc(font: Font, text: cstring, rec: Rectangle, fontSize, spacing: f32, wordWrap: bool, tint: Color) --- // Draw text using font inside rectangle limits
-	DrawTextRecEx     :: proc(font: Font, text: cstring, rec: Rectangle, fontSize, spacing: f32, wordWrap: bool, tint: Color,
-	                          selectStart, selectLength: c.int, selectTint, selectBackTint: Color) ---                            // Draw text using font inside rectangle limits with support for text selection
+	DrawTextPro       :: proc(font: Font, text: cstring, position, origin: Vector2, 
+	                          rotation: f32, fontSize: f32, spacing: f32, tint: Color) ---                                        // Draw text using Font and pro parameters (rotation)
 	DrawTextCodepoint :: proc(font: Font, codepoint: rune, position: Vector2, fontSize: f32, tint: Color) ---                     // Draw one character (codepoint)
 
 	// Text misc. functions
-	MeasureText   :: proc(text: cstring, fontSize: c.int) -> c.int ---                       // Measure string width for default font
-	MeasureTextEx :: proc(font: Font, text: cstring, fontSize, spacing: f32) -> Vector2 ---  // Measure string size for Font
-	GetGlyphIndex :: proc(font: Font, codepoint: rune) -> c.int ---                          // Get index position for a unicode character on font
+	MeasureText      :: proc(text: cstring, fontSize: c.int) -> c.int ---                       // Measure string width for default font
+	MeasureTextEx    :: proc(font: Font, text: cstring, fontSize, spacing: f32) -> Vector2 ---  // Measure string size for Font
+	GetGlyphIndex    :: proc(font: Font, codepoint: rune) -> c.int ---                          // Get index position for a unicode character on font
+	GetGlyphInfo     :: proc(font: Font, codepoint: rune) -> GlyphInfo ---                      // Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found
+	GetGlyphAtlasRec :: proc(font: Font, codepoint: rune) -> Rectangle ---                      // Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found
+
+	// Text codepoints management functions (unicode characters)
+	LoadCodepoints       :: proc(text: cstring, count: ^c.int) -> [^]rune ---        // Load all codepoints from a UTF-8 text string, codepoints count returned by parameter
+	UnloadCodepoints     :: proc(codepoints: [^]rune) ---                            // Unload codepoints data from memory
+	GetCodepointCount    :: proc(text: cstring) -> c.int ---                         // Get total number of codepoints in a UTF-8 encoded string
+	GetCodepoint         :: proc(text: cstring, bytesProcessed: ^c.int) -> c.int --- // Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure
+	CodepointToUTF8      :: proc(codepoint: rune, byteSize: ^c.int) -> cstring ---   // Encode one codepoint into UTF-8 byte array (array length returned as parameter)
+	TextCodepointsToUTF8 :: proc(codepoints: [^]rune, length: c.int) -> cstring ---  // Encode text as codepoints array into UTF-8 text string (WARNING: memory must be freed!)
+
 
 	// Text strings management functions (no utf8 strings, only byte chars)
 	// NOTE: Some strings allocate memory internally for returned strings, just be careful!
@@ -1234,56 +1342,79 @@ foreign lib {
 	TextToLower   :: proc(text: cstring) -> cstring ---                                           // Get lower case version of provided string
 	TextToPascal  :: proc(text: cstring) -> cstring ---                                           // Get Pascal case notation version of provided string
 	TextToInteger :: proc(text: cstring) -> c.int ---                                             // Get integer value from text (negative values not supported)
-	TextToUtf8    :: proc(codepoints: [^]rune, length: c.int) -> [^]byte ---                      // Encode text codepoint into utf8 text (memory must be freed!)
-
-
-	// UTF8 text strings management functions
-	GetCodepoints      :: proc(text: cstring, count: ^c.int) -> [^]rune ---         // Get all codepoints in a string, codepoints count returned by parameters
-	GetCodepointsCount :: proc(text: cstring) -> c.int ---                          // Get total number of characters (codepoints) in a UTF8 encoded string
-	GetNextCodepoint   :: proc(text: cstring, bytesProcessed: ^c.int) -> c.int ---  // Returns next codepoint in a UTF8 encoded string; 0x3f('?') is returned on failure
-	CodepointToUtf8    :: proc(codepoint: rune, byteLength: ^c.int) -> cstring ---  // Encode codepoint into utf8 text (char array length returned as parameter)
 
 	//------------------------------------------------------------------------------------
 	// Basic 3d Shapes Drawing Functions (Module: models)
 	//------------------------------------------------------------------------------------
 
 	// Basic geometric 3D shapes drawing functions
-	DrawLine3D          :: proc(startPos, endPos: Vector3, color: Color) ---                                                   // Draw a line in 3D world space
-	DrawPoint3D         :: proc(position: Vector3, color: Color) ---                                                           // Draw a point in 3D space, actually a small line
-	DrawCircle3D        :: proc(center: Vector3, radius: f32, rotationAxis: Vector3, rotationAngle: f32, color: Color) ---     // Draw a circle in 3D world space
-	DrawTriangle3D      :: proc(v1, v2, v3: Vector3, color: Color) ---                                                         // Draw a color-filled triangle (vertex in counter-clockwise order!)
-	DrawTriangleStrip3D :: proc(points: [^]Vector3, pointsCount: c.int, color: Color) ---                                      // Draw a triangle strip defined by points
-	DrawCube            :: proc(position: Vector3, width, height, length: f32, color: Color) ---                               // Draw cube
-	DrawCubeV           :: proc(position: Vector3, size: Vector3, color: Color) ---                                            // Draw cube (Vector version)
-	DrawCubeWires       :: proc(position: Vector3, width, height, length: f32, color: Color) ---                               // Draw cube wires
-	DrawCubeWiresV      :: proc(position: Vector3, size: Vector3, color: Color) ---                                            // Draw cube wires (Vector version)
-	DrawCubeTexture     :: proc(texture: Texture2D, position: Vector3, width, height, length: f32, color: Color) ---           // Draw cube textured
-	DrawSphere          :: proc(centerPos: Vector3, radius: f32, color: Color) ---                                             // Draw sphere
-	DrawSphereEx        :: proc(centerPos: Vector3, radius: f32, rings, slices: c.int, color: Color) ---                       // Draw sphere with extended parameters
-	DrawSphereWires     :: proc(centerPos: Vector3, radius: f32, rings, slices: c.int, color: Color) ---                       // Draw sphere wires
-	DrawCylinder        :: proc(position: Vector3, radiusTop, radiusBottom: f32, height: f32, slices: c.int, color: Color) --- // Draw a cylinder/cone
-	DrawCylinderWires   :: proc(position: Vector3, radiusTop, radiusBottom: f32, height: f32, slices: c.int, color: Color) --- // Draw a cylinder/cone wires
+	DrawLine3D          :: proc(startPos, endPos: Vector3, color: Color) ---                                                            // Draw a line in 3D world space
+	DrawPoint3D         :: proc(position: Vector3, color: Color) ---                                                                    // Draw a point in 3D space, actually a small line
+	DrawCircle3D        :: proc(center: Vector3, radius: f32, rotationAxis: Vector3, rotationAngle: f32, color: Color) ---              // Draw a circle in 3D world space
+	DrawTriangle3D      :: proc(v1, v2, v3: Vector3, color: Color) ---                                                                  // Draw a color-filled triangle (vertex in counter-clockwise order!)
+	DrawTriangleStrip3D :: proc(points: [^]Vector3, pointsCount: c.int, color: Color) ---                                               // Draw a triangle strip defined by points
+	DrawCube            :: proc(position: Vector3, width, height, length: f32, color: Color) ---                                        // Draw cube
+	DrawCubeV           :: proc(position: Vector3, size: Vector3, color: Color) ---                                                     // Draw cube (Vector version)
+	DrawCubeWires       :: proc(position: Vector3, width, height, length: f32, color: Color) ---                                        // Draw cube wires
+	DrawCubeWiresV      :: proc(position: Vector3, size: Vector3, color: Color) ---                                                     // Draw cube wires (Vector version)
+	DrawCubeTexture     :: proc(texture: Texture2D, position: Vector3, width, height, length: f32, color: Color) ---                    // Draw cube textured
+	DrawCubeTextureRec  :: proc(texture: Texture2D, source: Rectangle, position: Vector3, width, height, length: f32, color: Color) --- // Draw cube with a region of a texture
+	DrawSphere          :: proc(centerPos: Vector3, radius: f32, color: Color) ---                                                      // Draw sphere
+	DrawSphereEx        :: proc(centerPos: Vector3, radius: f32, rings, slices: c.int, color: Color) ---                                // Draw sphere with extended parameters
+	DrawSphereWires     :: proc(centerPos: Vector3, radius: f32, rings, slices: c.int, color: Color) ---                                // Draw sphere wires
+	DrawCylinder        :: proc(position: Vector3, radiusTop, radiusBottom: f32, height: f32, slices: c.int, color: Color) ---          // Draw a cylinder/cone
+	DrawCylinderEx      :: proc(startPos, endPos: Vector3, startRadius, endRadius: f32, sides: c.int, color: Color) ---                 // Draw a cylinder with base at startPos and top at endPos
+	DrawCylinderWires   :: proc(position: Vector3, radiusTop, radiusBottom: f32, height: f32, slices: c.int, color: Color) ---          // Draw a cylinder/cone wires
+	DrawCylinderWiresEx :: proc(startPos, endPos: Vector3, startRadius, endRadius: f32, sides: c.int, color: Color) ---                 // Draw a cylinder wires with base at startPos and top at endPos
 	DrawPlane           :: proc(centerPos: Vector3, size: Vector2, color: Color) ---                                                    // Draw a plane XZ
-	DrawRay             :: proc(ray: Ray, color: Color) ---                                                                    // Draw a ray line
-	DrawGrid            :: proc(slices: c.int, spacing: f32) ---                                                               // Draw a grid (centered at (0, 0, 0))
+	DrawRay             :: proc(ray: Ray, color: Color) ---                                                                             // Draw a ray line
+	DrawGrid            :: proc(slices: c.int, spacing: f32) ---                                                                        // Draw a grid (centered at (0, 0, 0))
 
 	//------------------------------------------------------------------------------------
 	// Model 3d Loading and Drawing Functions (Module: models)
 	//------------------------------------------------------------------------------------
 
 	// Model loading/unloading functions
-	LoadModel             :: proc(fileName: cstring) -> Model --- // Load model from files (meshes and materials)
-	LoadModelFromMesh     :: proc(mesh: Mesh) -> Model ---        // Load model from generated mesh (default material)
-	UnloadModel           :: proc(model: Model) ---               // Unload model (including meshes) from memory (RAM and/or VRAM)
-	UnloadModelKeepMeshes :: proc(model: Model) ---               // Unload model (but not meshes) from memory (RAM and/or VRAM)
-
-	// Mesh loading/unloading functions
-	UploadMesh        :: proc(mesh: ^Mesh, is_dynamic: bool) ---                                            // Upload mesh vertex data in GPU and provide VAO/VBO ids
-	UpdateMeshBuffer  :: proc(mesh: Mesh, index: c.int, data:rawptr, dataSize, offset: c.int) ---           // Update mesh vertex data in GPU for a specific buffer index
-	DrawMesh          :: proc(mesh: Mesh, material: Material, transform: Matrix) ---                        // Draw a 3d mesh with material and transform
-	DrawMeshInstanced :: proc(mesh: Mesh, material: Material, transforms: [^]Matrix, instances: c.int) ---  // Draw multiple mesh instances with material and different transforms
-	UnloadMesh        :: proc(mesh: Mesh) ---                                                               // Unload mesh data from CPU and GPU
-	ExportMesh        :: proc(mesh: Mesh, fileName: cstring) -> bool ---                                    // Export mesh data to file, returns true on success
+	LoadModel             :: proc(fileName: cstring) -> Model ---  // Load model from files (meshes and materials)
+	LoadModelFromMesh     :: proc(mesh: Mesh) -> Model ---         // Load model from generated mesh (default material)
+	UnloadModel           :: proc(model: Model) ---                // Unload model (including meshes) from memory (RAM and/or VRAM)
+	UnloadModelKeepMeshes :: proc(model: Model) ---                // Unload model (but not meshes) from memory (RAM and/or VRAM)
+	GetModelBoundingBox   :: proc(model: Model) -> BoundingBox --- // Compute model bounding box limits (considers all meshes)
+
+	// Model drawing functions
+	DrawModel        :: proc(model: Model, position: Vector3, scale: f32, tint: Color) ---                                                // Draw a model (with texture if set)
+	DrawModelEx      :: proc(model: Model, position: Vector3, rotationAxis: Vector3, rotationAngle: f32, scale: Vector3, tint: Color) --- // Draw a model with extended parameters
+	DrawModelWires   :: proc(model: Model, position: Vector3, scale: f32, tint: Color) ---                                                // Draw a model wires (with texture if set)
+	DrawModelWiresEx :: proc(model: Model, position: Vector3, rotationAxis: Vector3, rotationAngle: f32, scale: Vector3, tint: Color) --- // Draw a model wires (with texture if set) with extended parameters
+	DrawBoundingBox  :: proc(box: BoundingBox, color: Color) ---                                                                          // Draw bounding box (wires)
+	DrawBillboard    :: proc(camera: Camera, texture: Texture2D, center: Vector3, size: f32, tint: Color) ---                             // Draw a billboard texture
+	DrawBillboardRec :: proc(camera: Camera, texture: Texture2D, source: Rectangle, center: Vector3, size: f32, tint: Color) ---          // Draw a billboard texture defined by source
+	DrawBillboardPro :: proc(camera: Camera, texture: Texture2D, source: Rectangle, 
+	                         position, up: Vector3, size, origin: Vector2, rotation: f32, tint: Color) ---                                // Draw a billboard texture defined by source and rotation
+
+	// Mesh management functions
+	UploadMesh         :: proc(mesh: ^Mesh, is_dynamic: bool) ---                                           // Upload mesh vertex data in GPU and provide VAO/VBO ids
+	UpdateMeshBuffer   :: proc(mesh: Mesh, index: c.int, data: rawptr, dataSize: c.int, offset: c.int) ---  // Update mesh vertex data in GPU for a specific buffer index
+	UnloadMesh         :: proc(mesh: Mesh) ---                                                              // Unload mesh data from CPU and GPU
+	DrawMesh           :: proc(mesh: Mesh, material: Material, transform: Matrix) ---                       // Draw a 3d mesh with material and transform
+	DrawMeshInstanced  :: proc(mesh: Mesh, material: Material, transforms: [^]Matrix, instances: c.int) --- // Draw multiple mesh instances with material and different transforms
+	ExportMesh         :: proc(mesh: Mesh, fileName: cstring) -> bool ---                                   // Export mesh data to file, returns true on success
+	GetMeshBoundingBox :: proc(mesh: Mesh) -> BoundingBox ---                                               // Compute mesh bounding box limits
+	GenMeshTangents    :: proc(mesh: ^Mesh) ---                                                             // Compute mesh tangents
+	GenMeshBinormals   :: proc(mesh: ^Mesh) ---          
+	
+	// Mesh generation functions
+	GenMeshPoly       :: proc(sides: c.int, radius: f32) -> Mesh ---                           // Generate polygonal mesh
+	GenMeshPlane      :: proc(width, length: f32, resX, resZ: c.int) -> Mesh ---               // Generate plane mesh (with subdivisions)
+	GenMeshCube       :: proc(width, height, length: f32) -> Mesh ---                          // Generate cuboid mesh
+	GenMeshSphere     :: proc(radius: f32, rings, slices: c.int) -> Mesh ---                   // Generate sphere mesh (standard sphere)
+	GenMeshHemiSphere :: proc(radius: f32, rings, slices: c.int) -> Mesh ---                   // Generate half-sphere mesh (no bottom cap)
+	GenMeshCylinder   :: proc(radius: f32, height: f32, slices: c.int) -> Mesh ---             // Generate cylinder mesh
+	GenMeshCone       :: proc(radius: f32, height: f32, slices: c.int) -> Mesh ---             // Generate cone/pyramid mesh
+	GenMeshTorus      :: proc(radius: f32, size: f32, radSeg: c.int, sides: c.int) -> Mesh --- // Generate torus mesh
+	GenMeshKnot       :: proc(radius: f32, size: f32, radSeg: c.int, sides: c.int) -> Mesh --- // Generate trefoil knot mesh
+	GenMeshHeightmap  :: proc(heightmap: Image, size: Vector3) -> Mesh ---                     // Generate heightmap mesh from image data
+	GenMeshCubicmap   :: proc(cubicmap: Image, cubeSize: Vector3) -> Mesh ---                  // Generate cubes-based map mesh from image data
 
 	// Material loading/unloading functions
 	LoadMaterials :: proc(fileName: cstring, materialCount: ^c.int) -> [^]Material ---                  // Load materials from model file
@@ -1298,45 +1429,17 @@ foreign lib {
 	UnloadModelAnimation  :: proc(anim: ModelAnimation) ---                                         // Unload animation data
 	UnloadModelAnimations :: proc(animations: [^]ModelAnimation, count: c.uint) ---                 // Unload animation array data
 	IsModelAnimationValid :: proc(model: Model, anim: ModelAnimation) -> bool ---                   // Check model animation skeleton match
-
-	// Mesh generation functions
-	GenMeshPoly       :: proc(sides: c.int, radius: f32) -> Mesh ---                           // Generate polygonal mesh
-	GenMeshPlane      :: proc(width, length: f32, resX, resZ: c.int) -> Mesh ---               // Generate plane mesh (with subdivisions)
-	GenMeshCube       :: proc(width, height, length: f32) -> Mesh ---                          // Generate cuboid mesh
-	GenMeshSphere     :: proc(radius: f32, rings, slices: c.int) -> Mesh ---                   // Generate sphere mesh (standard sphere)
-	GenMeshHemiSphere :: proc(radius: f32, rings, slices: c.int) -> Mesh ---                   // Generate half-sphere mesh (no bottom cap)
-	GenMeshCylinder   :: proc(radius: f32, height: f32, slices: c.int) -> Mesh ---             // Generate cylinder mesh
-	GenMeshTorus      :: proc(radius: f32, size: f32, radSeg: c.int, sides: c.int) -> Mesh --- // Generate torus mesh
-	GenMeshKnot       :: proc(radius: f32, size: f32, radSeg: c.int, sides: c.int) -> Mesh --- // Generate trefoil knot mesh
-	GenMeshHeightmap  :: proc(heightmap: Image, size: Vector3) -> Mesh ---                     // Generate heightmap mesh from image data
-	GenMeshCubicmap   :: proc(cubicmap: Image, cubeSize: Vector3) -> Mesh ---                  // Generate cubes-based map mesh from image data
 	
-	// Mesh manipulation functions
-	MeshBoundingBox :: proc(mesh: Mesh) -> BoundingBox ---  // Compute mesh bounding box limits
-	MeshTangents    :: proc(mesh: ^Mesh) ---                // Compute mesh tangents
-	MeshBinormals   :: proc(mesh: ^Mesh) ---                // Compute mesh binormals
-
-	// Model drawing functions
-	DrawModel        :: proc(model: Model, position: Vector3, scale: f32, tint: Color) ---                                                // Draw a model (with texture if set)
-	DrawModelEx      :: proc(model: Model, position: Vector3, rotationAxis: Vector3, rotationAngle: f32, scale: Vector3, tint: Color) --- // Draw a model with extended parameters
-	DrawModelWires   :: proc(model: Model, position: Vector3, scale: f32, tint: Color) ---                                                // Draw a model wires (with texture if set)
-	DrawModelWiresEx :: proc(model: Model, position: Vector3, rotationAxis: Vector3, rotationAngle: f32, scale: Vector3, tint: Color) --- // Draw a model wires (with texture if set) with extended parameters
-	DrawBoundingBox  :: proc(box: BoundingBox, color: Color) ---                                                                          // Draw bounding box (wires)
-	DrawBillboard    :: proc(camera: Camera, texture: Texture2D, center: Vector3, size: f32, tint: Color) ---                             // Draw a billboard texture
-	DrawBillboardRec :: proc(camera: Camera, texture: Texture2D, source: Rectangle, center: Vector3, size: f32, tint: Color) ---          // Draw a billboard texture defined by source
-
-
 	// Collision detection functions
-	CheckCollisionSpheres     :: proc(center1: Vector3, radius1: f32, center2: Vector3, radius2: f32) -> bool ---      // Detect collision between two spheres
-	CheckCollisionBoxes       :: proc(box1, box2: BoundingBox) -> bool ---                                             // Detect collision between two bounding boxes
-	CheckCollisionBoxSphere   :: proc(box: BoundingBox, center: Vector3, radius: f32) -> bool ---                      // Detect collision between box and sphere
-	CheckCollisionRaySphere   :: proc(ray: Ray, center: Vector3, radius: f32) -> bool ---                              // Detect collision between ray and sphere
-	CheckCollisionRaySphereEx :: proc(ray: Ray, center: Vector3, radius: f32, collisionPoint: [^]Vector3) -> bool ---  // Detect collision between ray and sphere, returns collision point
-	CheckCollisionRayBox      :: proc(ray: Ray, box: BoundingBox) -> bool ---                                          // Detect collision between ray and box
-	GetCollisionRayMesh       :: proc(ray: Ray, mesh: Mesh, transform: Matrix) -> RayHitInfo ---                       // Get collision info between ray and mesh
-	GetCollisionRayModel      :: proc(ray: Ray, model: Model) -> RayHitInfo ---                                        // Get collision info between ray and model
-	GetCollisionRayTriangle   :: proc(ray: Ray, p1, p2, p3: Vector3) -> RayHitInfo ---                                 // Get collision info between ray and triangle
-	GetCollisionRayGround     :: proc(ray: Ray, groundHeight: f32) -> RayHitInfo ---                                   // Get collision info between ray and ground plane (Y-normal plane)
+	CheckCollisionSpheres   :: proc(center1: Vector3, radius1: f32, center2: Vector3, radius2: f32) -> bool --- // Check collision between two spheres
+	CheckCollisionBoxes     :: proc(box1, box2: BoundingBox) -> bool ---                                        // Check collision between two bounding boxes
+	CheckCollisionBoxSphere :: proc(box: BoundingBox, center: Vector3, radius: f32) -> bool ---                 // Check collision between box and sphere
+	GetRayCollisionSphere   :: proc(ray: Ray, center: Vector3, radius: f32) -> RayCollision ---                  // Get collision info between ray and sphere
+	GetRayCollisionBox      :: proc(ray: Ray, box: BoundingBox) -> RayCollision ---                              // Get collision info between ray and box
+	GetRayCollisionModel    :: proc(ray: Ray, model: Model) -> RayCollision ---                                  // Get collision info between ray and model
+	GetRayCollisionMesh     :: proc(ray: Ray, mesh: Mesh, transform: Matrix) -> RayCollision ---                 // Get collision info between ray and mesh
+	GetRayCollisionTriangle :: proc(ray: Ray, p1, p2, p3: Vector3) -> RayCollision ---                           // Get collision info between ray and triangle
+	GetRayCollisionQuad     :: proc(ray: Ray, p1, p2, p3, p4: Vector3) -> RayCollision ---                       // Get collision info between ray and quad
 
 	//------------------------------------------------------------------------------------
 	// Audio Loading and Playing Functions (Module: audio)
@@ -1377,33 +1480,34 @@ foreign lib {
 	UnloadWaveSamples :: proc(samples: [^]f32) ---                                             // Unload samples data loaded with LoadWaveSamples()
 
 	// Music management functions
-	LoadMusicStream           :: proc(fileName: cstring) -> Music ---                               // Load music stream from file
+	LoadMusicStream           :: proc(fileName: cstring) -> Music ---                                // Load music stream from file
 	LoadMusicStreamFromMemory :: proc(fileType: cstring, data: rawptr, dataSize: c.int) -> Music --- // Load music stream from data
-	UnloadMusicStream         :: proc(music: Music) ---                                             // Unload music stream
-	PlayMusicStream           :: proc(music: Music) ---                                             // Start music playing
-	IsMusicPlaying            :: proc(music: Music) -> bool ---                                     // Check if music is playing
-	UpdateMusicStream         :: proc(music: Music) ---                                             // Updates buffers for music streaming
-	StopMusicStream           :: proc(music: Music) ---                                             // Stop music playing
-	PauseMusicStream          :: proc(music: Music) ---                                             // Pause music playing
-	ResumeMusicStream         :: proc(music: Music) ---                                             // Resume playing paused music
-	SetMusicVolume            :: proc(music: Music, volume: f32) ---                                // Set volume for music (1.0 is max level)
-	SetMusicPitch             :: proc(music: Music, pitch: f32) ---                                 // Set pitch for a music (1.0 is base level)
-	GetMusicTimeLength        :: proc(music: Music) -> f32 ---                                      // Get music time length (in seconds)
-	GetMusicTimePlayed        :: proc(music: Music) -> f32 ---                                      // Get current music time played (in seconds)
+	UnloadMusicStream         :: proc(music: Music) ---                                              // Unload music stream
+	PlayMusicStream           :: proc(music: Music) ---                                              // Start music playing
+	IsMusicStreamPlaying      :: proc(music: Music) -> bool ---                                      // Check if music is playing
+	UpdateMusicStream         :: proc(music: Music) ---                                              // Updates buffers for music streaming
+	StopMusicStream           :: proc(music: Music) ---                                              // Stop music playing
+	PauseMusicStream          :: proc(music: Music) ---                                              // Pause music playing
+	ResumeMusicStream         :: proc(music: Music) ---                                              // Resume playing paused music
+	SeekMusicStream           :: proc(music: Music, position: f32) ---                               // Seek music to a position (in seconds)
+	SetMusicVolume            :: proc(music: Music, volume: f32) ---                                 // Set volume for music (1.0 is max level)
+	SetMusicPitch             :: proc(music: Music, pitch: f32) ---                                  // Set pitch for a music (1.0 is base level)
+	GetMusicTimeLength        :: proc(music: Music) -> f32 ---                                       // Get music time length (in seconds)
+	GetMusicTimePlayed        :: proc(music: Music) -> f32 ---                                       // Get current music time played (in seconds)
 
 	// AudioStream management functions
-	InitAudioStream                 :: proc(sampleRate, sampleSize, channels: c.uint) -> AudioStream --- // Init audio stream (to stream raw audio pcm data)
-	UpdateAudioStream               :: proc(stream: AudioStream, data: rawptr, samplesCount: c.int) ---  // Update audio stream buffers with data
-	CloseAudioStream                :: proc(stream: AudioStream) ---                                     // Close audio stream and free memory
-	IsAudioStreamProcessed          :: proc(stream: AudioStream) -> AudioStream ---                      // Check if any audio stream buffers requires refill
-	PlayAudioStream                 :: proc(stream: AudioStream) ---                                     // Play audio stream
-	PauseAudioStream                :: proc(stream: AudioStream) ---                                     // Pause audio stream
-	ResumeAudioStream               :: proc(stream: AudioStream) ---                                     // Resume audio stream
-	IsAudioStreamPlaying            :: proc(stream: AudioStream) -> AudioStream ---                      // Check if audio stream is playing
-	StopAudioStream                 :: proc(stream: AudioStream) ---                                     // Stop audio stream
-	SetAudioStreamVolume            :: proc(stream: AudioStream, volume: f32) ---                        // Set volume for audio stream (1.0 is max level)
-	SetAudioStreamPitch             :: proc(stream: AudioStream, pitch: f32) ---                         // Set pitch for audio stream (1.0 is base level)
-	SetAudioStreamBufferSizeDefault :: proc(size: c.int) ---                                             // Default size for new audio streams
+	LoadAudioStream                  :: proc(sampleRate, sampleSize, channels: c.uint) -> AudioStream --- // Load audio stream (to stream raw audio pcm data)
+	UnloadAudioStream                :: proc(stream: AudioStream) ---                                     // Unload audio stream and free memory
+	UpdateAudioStream                :: proc(stream: AudioStream, data: rawptr, frameCount: c.int) ---    // Update audio stream buffers with data
+	IsAudioStreamProcessed           :: proc(stream: AudioStream) -> bool ---                             // Check if any audio stream buffers requires refill
+	PlayAudioStream                  :: proc(stream: AudioStream) ---                                     // Play audio stream
+	PauseAudioStream                 :: proc(stream: AudioStream) ---                                     // Pause audio stream
+	ResumeAudioStream                :: proc(stream: AudioStream) ---                                     // Resume audio stream
+	IsAudioStreamPlaying             :: proc(stream: AudioStream) -> bool ---                             // Check if audio stream is playing
+	StopAudioStream                  :: proc(stream: AudioStream) ---                                     // Stop audio stream
+	SetAudioStreamVolume             :: proc(stream: AudioStream, volume: f32) ---                        // Set volume for audio stream (1.0 is max level)
+	SetAudioStreamPitch              :: proc(stream: AudioStream, pitch: f32) ---                         // Set pitch for audio stream (1.0 is base level)
+	SetAudioStreamBufferSizeDefault  :: proc(size: c.int) ---                                             // Default size for new audio streams
 }
 
 

+ 80 - 44
vendor/raylib/rlgl.odin

@@ -14,10 +14,11 @@ when ODIN_OS == "windows" {
 when ODIN_OS == "linux"  { foreign import lib "linux/libraylib.a" }
 when ODIN_OS == "darwin" { foreign import lib "macos/libraylib.a" }
 
-GRAPHICS_API_OPENGL_11 :: false
-GRAPHICS_API_OPENGL_21 :: true
-GRAPHICS_API_OPENGL_33 :: GRAPHICS_API_OPENGL_21
+GRAPHICS_API_OPENGL_11  :: false
+GRAPHICS_API_OPENGL_21  :: true
+GRAPHICS_API_OPENGL_33  :: GRAPHICS_API_OPENGL_21
 GRAPHICS_API_OPENGL_ES2 :: false
+GRAPHICS_API_OPENGL_43  :: false
 
 when !GRAPHICS_API_OPENGL_ES2 {
 	// This is the maximum amount of elements (quads) per batch
@@ -35,24 +36,20 @@ DEFAULT_BATCH_DRAWCALLS        :: 256                  // Default number of batc
 MAX_BATCH_ACTIVE_TEXTURES      :: 4                    // Maximum number of additional textures that can be activated on batch drawing (SetShaderValueTexture())
 
 // Internal Matrix stack
-MAX_MATRIX_STACK_SIZE           :: 32                  // Maximum size of Matrix stack
+MAX_MATRIX_STACK_SIZE          :: 32                   // Maximum size of Matrix stack
 
-// Vertex buffers id limit
-MAX_MESH_VERTEX_BUFFERS          :: 7                  // Maximum vertex buffers (VBO) per mesh
-
-// Shader and material limits
-MAX_SHADER_LOCATIONS            :: 32                  // Maximum number of shader locations supported
-MAX_MATERIAL_MAPS               :: 12                  // Maximum number of shader maps supported
+// Shader limits
+MAX_SHADER_LOCATIONS           :: 32                   // Maximum number of shader locations supported
 
 // Projection matrix culling
-RL_CULL_DISTANCE_NEAR         :: 0.01                  // Default near cull distance
-RL_CULL_DISTANCE_FAR          :: 1000.0                // Default far cull distance
+RL_CULL_DISTANCE_NEAR          :: 0.01                 // Default near cull distance
+RL_CULL_DISTANCE_FAR           :: 1000.0               // Default far cull distance
 
 // Texture parameters (equivalent to OpenGL defines)
-RL_TEXTURE_WRAP_S               :: 0x2802              // GL_TEXTURE_WRAP_S
-RL_TEXTURE_WRAP_T               :: 0x2803              // GL_TEXTURE_WRAP_T
-RL_TEXTURE_MAG_FILTER           :: 0x2800              // GL_TEXTURE_MAG_FILTER
-RL_TEXTURE_MIN_FILTER           :: 0x2801              // GL_TEXTURE_MIN_FILTER
+RL_TEXTURE_WRAP_S                       :: 0x2802      // GL_TEXTURE_WRAP_S
+RL_TEXTURE_WRAP_T                       :: 0x2803      // GL_TEXTURE_WRAP_T
+RL_TEXTURE_MAG_FILTER                   :: 0x2800      // GL_TEXTURE_MAG_FILTER
+RL_TEXTURE_MIN_FILTER                   :: 0x2801      // GL_TEXTURE_MIN_FILTER
 
 RL_TEXTURE_FILTER_NEAREST               :: 0x2600      // GL_NEAREST
 RL_TEXTURE_FILTER_LINEAR                :: 0x2601      // GL_LINEAR
@@ -68,18 +65,34 @@ RL_TEXTURE_WRAP_MIRROR_REPEAT           :: 0x8370      // GL_MIRRORED_REPEAT
 RL_TEXTURE_WRAP_MIRROR_CLAMP            :: 0x8742      // GL_MIRROR_CLAMP_EXT
 
 // Matrix modes (equivalent to OpenGL)
-RL_MODELVIEW                    :: 0x1700              // GL_MODELVIEW
-RL_PROJECTION                   :: 0x1701              // GL_PROJECTION
-RL_TEXTURE                      :: 0x1702              // GL_TEXTURE
+RL_MODELVIEW                            :: 0x1700      // GL_MODELVIEW
+RL_PROJECTION                           :: 0x1701      // GL_PROJECTION
+RL_TEXTURE                              :: 0x1702      // GL_TEXTURE
 
 // Primitive assembly draw modes
-RL_LINES                        :: 0x0001              // GL_LINES
-RL_TRIANGLES                    :: 0x0004              // GL_TRIANGLES
-RL_QUADS                        :: 0x0007              // GL_QUADS
+RL_LINES                                :: 0x0001      // GL_LINES
+RL_TRIANGLES                            :: 0x0004      // GL_TRIANGLES
+RL_QUADS                                :: 0x0007      // GL_QUADS
 
 // GL equivalent data types
-RL_UNSIGNED_BYTE                :: 0x1401              // GL_UNSIGNED_BYTE
-RL_f32                        :: 0x1406              // GL_f32
+RL_UNSIGNED_BYTE                        :: 0x1401      // GL_UNSIGNED_BYTE
+RL_FLOAT                                :: 0x1406      // GL_FLOAT
+
+// Buffer usage hint
+RL_STREAM_DRAW                          :: 0x88E0      // GL_STREAM_DRAW
+RL_STREAM_READ                          :: 0x88E1      // GL_STREAM_READ
+RL_STREAM_COPY                          :: 0x88E2      // GL_STREAM_COPY
+RL_STATIC_DRAW                          :: 0x88E4      // GL_STATIC_DRAW
+RL_STATIC_READ                          :: 0x88E5      // GL_STATIC_READ
+RL_STATIC_COPY                          :: 0x88E6      // GL_STATIC_COPY
+RL_DYNAMIC_DRAW                         :: 0x88E8      // GL_DYNAMIC_DRAW
+RL_DYNAMIC_READ                         :: 0x88E9      // GL_DYNAMIC_READ
+RL_DYNAMIC_COPY                         :: 0x88EA      // GL_DYNAMIC_COPY
+
+// GL Shader type
+RL_FRAGMENT_SHADER                      :: 0x8B30      // GL_FRAGMENT_SHADER
+RL_VERTEX_SHADER                        :: 0x8B31      // GL_VERTEX_SHADER
+RL_COMPUTE_SHADER                       :: 0x91B9      // GL_COMPUTE_SHADER
 
 
 //----------------------------------------------------------------------------------
@@ -230,26 +243,29 @@ foreign lib {
 	// Framebuffer state
 	rlEnableFramebuffer  :: proc(id: u32) ---                                     // Enable render texture (fbo)
 	rlDisableFramebuffer :: proc() ---                                            // Disable render texture (fbo), return to default framebuffer
+	rlActiveDrawBuffers  :: proc(count: c.int) ---                                // Activate multiple draw color buffers
 
 	// General render state
-	rlEnableDepthTest        :: proc() ---                                        // Enable depth test
-	rlDisableDepthTest       :: proc() ---                                        // Disable depth test
-	rlEnableDepthMask        :: proc() ---                                        // Enable depth write
-	rlDisableDepthMask       :: proc() ---                                        // Disable depth write
-	rlEnableBackfaceCulling  :: proc() ---                                        // Enable backface culling
-	rlDisableBackfaceCulling :: proc() ---                                        // Disable backface culling
-	rlEnableScissorTest      :: proc() ---                                        // Enable scissor test
-	rlDisableScissorTest     :: proc() ---                                        // Disable scissor test
-	rlScissor                :: proc(x, y, width, height: c.int) ---              // Scissor test
-	rlEnableWireMode         :: proc() ---                                        // Enable wire mode
-	rlDisableWireMode        :: proc() ---                                        // Disable wire mode
-	rlSetLineWidth           :: proc(width: f32) ---                              // Set the line drawing width
-	rlGetLineWidth           :: proc() -> f32 ---                                 // Get the line drawing width
-	rlEnableSmoothLines      :: proc() ---                                        // Enable line aliasing
-	rlDisableSmoothLines     :: proc() ---                                        // Disable line aliasing
-	rlEnableStereoRender     :: proc() ---                                        // Enable stereo rendering
-	rlDisableStereoRender    :: proc() ---                                        // Disable stereo rendering
-	rlIsStereoRenderEnabled  :: proc() -> bool ---                                // Check if stereo render is enabled
+	rlEnableColorBlend       :: proc() ---                           // Enable color blending
+	rlDisableColorBlend      :: proc() ---                           // Disable color blending
+	rlEnableDepthTest        :: proc() ---                           // Enable depth test
+	rlDisableDepthTest       :: proc() ---                           // Disable depth test
+	rlEnableDepthMask        :: proc() ---                           // Enable depth write
+	rlDisableDepthMask       :: proc() ---                           // Disable depth write
+	rlEnableBackfaceCulling  :: proc() ---                           // Enable backface culling
+	rlDisableBackfaceCulling :: proc() ---                           // Disable backface culling
+	rlEnableScissorTest      :: proc() ---                           // Enable scissor test
+	rlDisableScissorTest     :: proc() ---                           // Disable scissor test
+	rlScissor                :: proc(x, y, width, height: c.int) --- // Scissor test
+	rlEnableWireMode         :: proc() ---                           // Enable wire mode
+	rlDisableWireMode        :: proc() ---                           // Disable wire mode
+	rlSetLineWidth           :: proc(width: f32) ---                 // Set the line drawing width
+	rlGetLineWidth           :: proc() -> f32 ---                    // Get the line drawing width
+	rlEnableSmoothLines      :: proc() ---                           // Enable line aliasing
+	rlDisableSmoothLines     :: proc() ---                           // Disable line aliasing
+	rlEnableStereoRender     :: proc() ---                           // Enable stereo rendering
+	rlDisableStereoRender    :: proc() ---                           // Disable stereo rendering
+	rlIsStereoRenderEnabled  :: proc() -> bool ---                   // Check if stereo render is enabled
 
 	rlClearColor         :: proc(r, g, b, a: u8) ---                              // Clear color buffer with color
 	rlClearScreenBuffers :: proc() ---                                            // Clear used screen buffers (color and depth)
@@ -268,8 +284,9 @@ foreign lib {
 	rlGetFramebufferWidth  :: proc() -> c.int ---                                          // Get default framebuffer width
 	rlGetFramebufferHeight :: proc() -> c.int ---                                          // Get default framebuffer height
 
-	rlGetShaderDefault  :: proc() -> Shader ---                                            // Get default shader
-	rlGetTextureDefault :: proc() -> Texture2D ---                                         // Get default texture
+	rlGetTextureIdDefault  :: proc() -> u32 ---                                            // Get default texture id
+	rlGetShaderIdDefault   :: proc() -> u32 ---                                            // Get default shader id
+	rlGetShaderLocsDefault :: proc() -> [^]i32 ---                                         // Get default shader locations
 
 	// Render batch management
 	// NOTE: rlgl provides a default render batch to behave like OpenGL 1.1 immediate mode
@@ -305,6 +322,7 @@ foreign lib {
 	rlLoadTextureCubemap   :: proc(data: rawptr, size: c.int, format: c.int) -> u32 ---                               // Load texture cubemap
 	rlUpdateTexture        :: proc(id: u32, offsetX, offsetY, width, height: c.int, format: c.int, data: rawptr) ---  // Update GPU texture with new data
 	rlGetGlTextureFormats  :: proc(format: c.int, glInternalFormat: ^u32, glFormat: ^u32, glType: ^u32) ---           // Get OpenGL internal formats
+	rlGetPixelFormatName   :: proc(format: PixelFormat) -> cstring ---                                                // Get name string for pixel format
 	rlUnloadTexture        :: proc(id: u32) ---                                                                       // Unload texture from GPU memory
 	rlGenerateMipmaps      :: proc(texture: ^Texture2D) ---                                                           // Generate mipmap data for selected texture
 	rlReadTexturePixels    :: proc(texture: Texture2D) -> rawptr ---                                                  // Read texture pixel data
@@ -328,6 +346,24 @@ foreign lib {
 	rlSetUniformSampler   :: proc(locIndex: c.int, textureId: u32) ---                                  // Set shader value sampler
 	rlSetShader           :: proc(shader: Shader) ---                                                   // Set shader currently active
 
+	// Compute shader management
+	rlLoadComputeShaderProgram :: proc(shaderId: u32) -> u32 ---        // Load compute shader program
+	rlComputeShaderDispatch    :: proc(groupX, groupY, groupZ: u32) --- // Dispatch compute shader (equivalent to *draw* for graphics pilepine)
+
+	
+	// Shader buffer storage object management (ssbo)
+	rlLoadShaderBuffer           :: proc(size: u64, data: rawptr, usageHint: c.int) -> u32 ---  // Load shader storage buffer object (SSBO)
+	rlUnloadShaderBuffer         :: proc(ssboId: u32) ---                                       // Unload shader storage buffer object (SSBO)
+	rlUpdateShaderBufferElements :: proc(id: u32, data: rawptr, dataSize: u64, offset: u64) --- // Update SSBO buffer data
+	rlGetShaderBufferSize        :: proc(id: u32) -> u64 ---                                    // Get SSBO buffer size
+	rlReadShaderBufferElements   :: proc(id: u32, dest: rawptr, count: u64, offset: u64) ---    // Bind SSBO buffer
+	rlBindShaderBuffer           :: proc(id: u32, index: u32) ---                               // Copy SSBO buffer data
+
+	// Buffer management
+	rlCopyBuffersElements  :: proc(destId, srcId: u32, destOffset, srcOffset: u64, count: u64) --- // Copy SSBO buffer data
+	rlBindImageTexture     :: proc(id: u32, index: u32, format: u32, readonly: b32) ---            // Bind image texture
+
+
 	// Matrix state management
 	rlGetMatrixModelview        :: proc() -> Matrix ---           // Get internal modelview matrix
 	rlGetMatrixProjection       :: proc() -> Matrix ---           // Get internal projection matrix

Some files were not shown because too many files changed in this diff