Procházet zdrojové kódy

Merge branch 'master' into windows-llvm-11.1.0

gingerBill před 3 roky
rodič
revize
d84d789d11
100 změnil soubory, kde provedl 5684 přidání a 6880 odebrání
  1. 26 4
      .github/workflows/ci.yml
  2. 1 1
      build.bat
  3. 2 0
      core/c/libc/complex.odin
  4. 2 0
      core/c/libc/ctype.odin
  5. 16 0
      core/c/libc/errno.odin
  6. 2 0
      core/c/libc/math.odin
  7. 2 1
      core/c/libc/setjmp.odin
  8. 16 1
      core/c/libc/signal.odin
  9. 33 1
      core/c/libc/stdio.odin
  10. 19 1
      core/c/libc/stdlib.odin
  11. 2 0
      core/c/libc/string.odin
  12. 5 0
      core/c/libc/threads.odin
  13. 5 3
      core/c/libc/time.odin
  14. 2 0
      core/c/libc/uchar.odin
  15. 2 0
      core/c/libc/wchar.odin
  16. 8 1
      core/c/libc/wctype.odin
  17. 35 43
      core/crypto/README.md
  18. 22 14
      core/crypto/_blake2/_blake2.odin
  19. 0 79
      core/crypto/_ctx/_ctx.odin
  20. 5 6
      core/crypto/_sha3/_sha3.odin
  21. 10 11
      core/crypto/_tiger/_tiger.odin
  22. 289 486
      core/crypto/blake/blake.odin
  23. 41 126
      core/crypto/blake2b/blake2b.odin
  24. 41 126
      core/crypto/blake2s/blake2s.odin
  25. 0 498
      core/crypto/botan/hash.odin
  26. 110 207
      core/crypto/gost/gost.odin
  27. 188 354
      core/crypto/groestl/groestl.odin
  28. 574 390
      core/crypto/haval/haval.odin
  29. 210 376
      core/crypto/jh/jh.odin
  30. 136 284
      core/crypto/keccak/keccak.odin
  31. 49 151
      core/crypto/md2/md2.odin
  32. 73 174
      core/crypto/md4/md4.odin
  33. 75 177
      core/crypto/md5/md5.odin
  34. 202 416
      core/crypto/ripemd/ripemd.odin
  35. 84 187
      core/crypto/sha1/sha1.odin
  36. 215 407
      core/crypto/sha2/sha2.odin
  37. 127 284
      core/crypto/sha3/sha3.odin
  38. 72 185
      core/crypto/shake/shake.odin
  39. 0 487
      core/crypto/skein/skein.odin
  40. 74 180
      core/crypto/sm3/sm3.odin
  41. 106 229
      core/crypto/streebog/streebog.odin
  42. 101 219
      core/crypto/tiger/tiger.odin
  43. 100 218
      core/crypto/tiger2/tiger2.odin
  44. 115 201
      core/crypto/whirlpool/whirlpool.odin
  45. 2 2
      core/encoding/json/marshal.odin
  46. 4 3
      core/encoding/json/parser.odin
  47. 1 0
      core/encoding/json/unmarshal.odin
  48. 11 9
      core/fmt/fmt.odin
  49. 44 0
      core/fmt/fmt_js.odin
  50. 1 1
      core/fmt/fmt_os.odin
  51. 1 1
      core/math/big/rat.odin
  52. 49 0
      core/math/linalg/specific.odin
  53. 435 126
      core/math/math.odin
  54. 169 0
      core/math/math_basic.odin
  55. 42 0
      core/math/math_basic_js.odin
  56. 410 0
      core/math/math_erf.odin
  57. 226 0
      core/math/math_gamma.odin
  58. 361 0
      core/math/math_lgamma.odin
  59. 198 0
      core/math/math_log1p.odin
  60. 1 0
      core/odin/ast/ast.odin
  61. 0 1
      core/os/os2/file_util.odin
  62. 0 1
      core/os/os2/heap_windows.odin
  63. 4 0
      core/os/os_freestanding.odin
  64. 4 0
      core/os/os_js.odin
  65. 10 6
      core/os/stat_windows.odin
  66. 12 9
      core/runtime/core.odin
  67. 1 0
      core/runtime/default_allocators_general.odin
  68. 5 0
      core/runtime/default_allocators_js.odin
  69. 1 1
      core/runtime/default_temporary_allocator.odin
  70. 24 17
      core/runtime/internal.odin
  71. 1 1
      core/runtime/os_specific_any.odin
  72. 12 0
      core/runtime/os_specific_js.odin
  73. 0 2
      core/runtime/procs.odin
  74. 15 0
      core/runtime/procs_js_wasm32.odin
  75. 18 3
      core/runtime/procs_wasm32.odin
  76. 2 2
      core/runtime/udivmod128.odin
  77. 8 3
      core/sys/windows/ws2_32.odin
  78. 16 0
      core/time/time_freestanding.odin
  79. 21 0
      core/time/time_js.odin
  80. 23 0
      core/time/time_wasi.odin
  81. 2 2
      examples/demo/demo.odin
  82. 33 16
      src/build_settings.cpp
  83. 2 2
      src/check_builtin.cpp
  84. 0 3
      src/check_decl.cpp
  85. 3 3
      src/check_expr.cpp
  86. 7 9
      src/check_stmt.cpp
  87. 3 2
      src/check_type.cpp
  88. 50 13
      src/checker.cpp
  89. 0 1
      src/checker.hpp
  90. 51 12
      src/common.cpp
  91. 1 1
      src/docs_writer.cpp
  92. 6 4
      src/llvm_backend.cpp
  93. 6 0
      src/llvm_backend.hpp
  94. 6 0
      src/llvm_backend_const.cpp
  95. 7 16
      src/llvm_backend_debug.cpp
  96. 26 21
      src/llvm_backend_expr.cpp
  97. 1 1
      src/llvm_backend_general.cpp
  98. 120 43
      src/llvm_backend_opt.cpp
  99. 20 5
      src/llvm_backend_proc.cpp
  100. 11 10
      src/llvm_backend_stmt.cpp

+ 26 - 4
.github/workflows/ci.yml

@@ -6,8 +6,8 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v1
-      - name: Download LLVM
-        run: sudo apt-get install llvm-11 clang-11 llvm
+      - name: Download LLVM, botan
+        run: sudo apt-get install llvm-11 clang-11 llvm libbotan-2-dev botan
       - name: build odin
         run: make release
       - name: Odin version
@@ -30,13 +30,18 @@ jobs:
           cd tests/core
           make
         timeout-minutes: 10
+      - name: Vendor library tests
+        run: |
+          cd tests/vendor
+          make
+        timeout-minutes: 10
   build_macOS:
     runs-on: macos-latest
     steps:
       - uses: actions/checkout@v1
-      - name: Download LLVM and setup PATH
+      - name: Download LLVM, botan and setup PATH
         run: |
-          brew install llvm@11
+          brew install llvm@11 botan
           echo "/usr/local/opt/llvm@11/bin" >> $GITHUB_PATH
           TMP_PATH=$(xcrun --show-sdk-path)/user/include
           echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
@@ -57,6 +62,16 @@ jobs:
       - name: Odin run -debug
         run: ./odin run examples/demo/demo.odin -debug
         timeout-minutes: 10
+      - name: Core library tests
+        run: |
+          cd tests/core
+          make
+        timeout-minutes: 10
+      - name: Vendor library tests
+        run: |
+          cd tests/vendor
+          make
+        timeout-minutes: 10
   build_windows:
     runs-on: windows-latest
     steps:
@@ -97,6 +112,13 @@ jobs:
           cd tests\core
           call build.bat
         timeout-minutes: 10
+      - name: Vendor library tests
+        shell: cmd
+        run: |
+          call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
+          cd tests\vendor
+          call build.bat
+        timeout-minutes: 10
       - name: core:math/big tests
         shell: cmd
         run: |

+ 1 - 1
build.bat

@@ -79,4 +79,4 @@ if %release_mode% EQU 0 odin run examples/demo
 
 del *.obj > NUL 2> NUL
 
-:end_of_build
+:end_of_build

+ 2 - 0
core/c/libc/complex.odin

@@ -4,6 +4,8 @@ package libc
 
 when ODIN_OS == "windows" {
 	foreign import libc "system:libucrt.lib"
+} else when ODIN_OS == "darwin" {
+	foreign import libc "system:System.framework"
 } else {
 	foreign import libc "system:c"
 }

+ 2 - 0
core/c/libc/ctype.odin

@@ -2,6 +2,8 @@ package libc
 
 when ODIN_OS == "windows" {
 	foreign import libc "system:libucrt.lib"
+} else when ODIN_OS == "darwin" {
+	foreign import libc "system:System.framework"
 } else {
 	foreign import libc "system:c"
 }

+ 16 - 0
core/c/libc/errno.odin

@@ -4,6 +4,8 @@ package libc
 
 when ODIN_OS == "windows" {
 	foreign import libc "system:libucrt.lib"
+} else when ODIN_OS == "darwin" {
+	foreign import libc "system:System.framework"
 } else {
 	foreign import libc "system:c"
 }
@@ -38,6 +40,20 @@ when ODIN_OS == "windows" {
 	ERANGE :: 34
 }
 
+when ODIN_OS == "darwin" {
+	@(private="file")
+	@(default_calling_convention="c")
+	foreign libc {
+		@(link_name="__error")
+		_get_errno :: proc() -> ^int ---
+	}
+
+	// Unknown
+	EDOM   :: 33
+	EILSEQ :: 92
+	ERANGE :: 34
+}
+
 // Odin has no way to make an identifier "errno" behave as a function call to
 // read the value, or to produce an lvalue such that you can assign a different
 // error value to errno. To work around this, just expose it as a function like

+ 2 - 0
core/c/libc/math.odin

@@ -6,6 +6,8 @@ import "core:intrinsics"
 
 when ODIN_OS == "windows" {
 	foreign import libc "system:libucrt.lib"
+} else when ODIN_OS == "darwin" {
+	foreign import libc "system:System.framework"
 } else {
 	foreign import libc "system:c"
 }

+ 2 - 1
core/c/libc/setjmp.odin

@@ -4,10 +4,11 @@ package libc
 
 when ODIN_OS == "windows" {
 	foreign import libc "system:libucrt.lib"
+} else when ODIN_OS == "darwin" {
+	foreign import libc "system:System.framework"
 } else {
 	foreign import libc "system:c"
 }
-
 when ODIN_OS == "windows" {
 	@(default_calling_convention="c")
 	foreign libc {

+ 16 - 1
core/c/libc/signal.odin

@@ -4,6 +4,8 @@ package libc
 
 when ODIN_OS == "windows" {
 	foreign import libc "system:libucrt.lib"
+} else when ODIN_OS == "darwin" {
+	foreign import libc "system:System.framework"
 } else {
 	foreign import libc "system:c"
 }
@@ -32,7 +34,20 @@ when ODIN_OS == "windows" {
 	SIGTERM :: 15
 }
 
-when ODIN_OS == "linux" || ODIN_OS == "freebsd" || ODIN_OS == "darwin" {
+when ODIN_OS == "linux" || ODIN_OS == "freebsd" {
+	SIG_ERR  :: rawptr(~uintptr(0))
+	SIG_DFL  :: rawptr(uintptr(0))
+	SIG_IGN  :: rawptr(uintptr(1)) 
+
+	SIGABRT  :: 6
+	SIGFPE   :: 8
+	SIGILL   :: 4
+	SIGINT   :: 2
+	SIGSEGV  :: 11
+	SIGTERM  :: 15
+}
+
+when ODIN_OS == "darwin" {
 	SIG_ERR  :: rawptr(~uintptr(0))
 	SIG_DFL  :: rawptr(uintptr(0))
 	SIG_IGN  :: rawptr(uintptr(1)) 

+ 33 - 1
core/c/libc/stdio.odin

@@ -2,6 +2,8 @@ package libc
 
 when ODIN_OS == "windows" {
 	foreign import libc "system:libucrt.lib"
+} else when ODIN_OS == "darwin" {
+	foreign import libc "system:System.framework"
 } else {
 	foreign import libc "system:c"
 }
@@ -67,7 +69,7 @@ when ODIN_OS == "linux" {
 	SEEK_CUR      :: 1
 	SEEK_END      :: 2
 
-	TMP_MAX       :: 10000
+	TMP_MAX       :: 308915776
 
 	foreign libc {
 		stderr: ^FILE
@@ -76,6 +78,36 @@ when ODIN_OS == "linux" {
 	}
 }
 
+when ODIN_OS == "darwin" {
+	fpos_t :: distinct i64
+	
+	_IOFBF        :: 0
+	_IOLBF        :: 1
+	_IONBF        :: 2
+
+	BUFSIZ        :: 1024
+
+	EOF           :: int(-1)
+
+	FOPEN_MAX     :: 20
+
+	FILENAME_MAX  :: 1024
+
+	L_tmpnam      :: 1024
+
+	SEEK_SET      :: 0
+	SEEK_CUR      :: 1
+	SEEK_END      :: 2
+
+	TMP_MAX       :: 308915776
+
+	foreign libc {
+		@(link_name="__stderrp") stderr: ^FILE
+		@(link_name="__stdinp")  stdin:  ^FILE
+		@(link_name="__stdoutp") stdout: ^FILE
+	}
+}
+
 @(default_calling_convention="c")
 foreign libc {
 	// 7.21.4 Operations on files

+ 19 - 1
core/c/libc/stdlib.odin

@@ -4,6 +4,8 @@ package libc
 
 when ODIN_OS == "windows" {
 	foreign import libc "system:libucrt.lib"
+} else when ODIN_OS == "darwin" {
+	foreign import libc "system:System.framework"
 } else {
 	foreign import libc "system:c"
 }
@@ -33,7 +35,23 @@ when ODIN_OS == "linux" {
 	}
 
 	MB_CUR_MAX :: #force_inline proc() -> size_t {
-		return __ctype_get_mb_cur_max()
+		return size_t(__ctype_get_mb_cur_max())
+	}
+}
+
+
+when ODIN_OS == "darwin" {
+	RAND_MAX :: 0x7fffffff
+
+	// GLIBC and MUSL only
+	@(private="file")
+	@(default_calling_convention="c")
+	foreign libc {
+		___mb_cur_max :: proc() -> int ---
+	}
+
+	MB_CUR_MAX :: #force_inline proc() -> size_t {
+		return size_t(___mb_cur_max())
 	}
 }
 

+ 2 - 0
core/c/libc/string.odin

@@ -6,6 +6,8 @@ import "core:runtime"
 
 when ODIN_OS == "windows" {
 	foreign import libc "system:libucrt.lib"
+} else when ODIN_OS == "darwin" {
+	foreign import libc "system:System.framework"
 } else {
 	foreign import libc "system:c"
 }

+ 5 - 0
core/c/libc/threads.odin

@@ -136,3 +136,8 @@ when ODIN_OS == "linux" {
 		tss_set       :: proc(key: tss_t, val: rawptr) -> int ---
 	}
 }
+
+
+when ODIN_OS == "darwin" {
+	// TODO: find out what this is meant to be!
+}

+ 5 - 3
core/c/libc/time.odin

@@ -4,6 +4,8 @@ package libc
 
 when ODIN_OS == "windows" {
 	foreign import libc "system:libucrt.lib"
+} else when ODIN_OS == "darwin" {
+	foreign import libc "system:System.framework"
 } else {
 	foreign import libc "system:c"
 }
@@ -43,7 +45,7 @@ when ODIN_OS == "windows" {
 	}
 }
 
-when ODIN_OS == "linux" || ODIN_OS == "freebsd" {
+when ODIN_OS == "linux" || ODIN_OS == "freebsd" || ODIN_OS == "darwin" {
 	@(default_calling_convention="c")
 	foreign libc {
 		// 7.27.2 Time manipulation functions
@@ -75,7 +77,7 @@ when ODIN_OS == "linux" || ODIN_OS == "freebsd" {
 
 	tm :: struct {
 		tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year, tm_wday, tm_yday, tm_isdst: int,
-		_: long,
-		_: rawptr,
+		tm_gmtoff: long,
+		tm_zone: rawptr,
 	}
 }

+ 2 - 0
core/c/libc/uchar.odin

@@ -4,6 +4,8 @@ package libc
 
 when ODIN_OS == "windows" {
 	foreign import libc "system:libucrt.lib"
+} else when ODIN_OS == "darwin" {
+	foreign import libc "system:System.framework"
 } else {
 	foreign import libc "system:c"
 }

+ 2 - 0
core/c/libc/wchar.odin

@@ -4,6 +4,8 @@ package libc
 
 when ODIN_OS == "windows" {
 	foreign import libc "system:libucrt.lib"
+} else when ODIN_OS == "darwin" {
+	foreign import libc "system:System.framework"
 } else {
 	foreign import libc "system:c"
 }

+ 8 - 1
core/c/libc/wctype.odin

@@ -4,6 +4,8 @@ package libc
 
 when ODIN_OS == "windows" {
 	foreign import libc "system:libucrt.lib"
+} else when ODIN_OS == "darwin" {
+	foreign import libc "system:System.framework"
 } else {
 	foreign import libc "system:c"
 }
@@ -14,10 +16,15 @@ when ODIN_OS == "windows" {
 }
 
 when ODIN_OS == "linux" {
-	wctrans_t :: distinct rawptr
+	wctrans_t :: distinct intptr_t
 	wctype_t  :: distinct ulong
 }
 
+when ODIN_OS == "darwin" {
+	wctrans_t :: distinct int
+	wctype_t  :: distinct u32
+}
+
 @(default_calling_convention="c")
 foreign libc {
 	// 7.30.2.1 Wide character classification functions

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

+ 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]
-    }
-}

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 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])
-	}
-}

+ 2 - 2
core/encoding/json/marshal.odin

@@ -18,7 +18,7 @@ Marshal_Error :: union {
 
 marshal :: proc(v: any, allocator := context.allocator) -> (data: []byte, err: Marshal_Error) {
 	b := strings.make_builder(allocator)
-	defer if err != nil || data == nil {
+	defer if err != .None {
 		strings.destroy_builder(&b)
 	}
 
@@ -27,7 +27,7 @@ marshal :: proc(v: any, allocator := context.allocator) -> (data: []byte, err: M
 	if len(b.buf) != 0 {
 		data = b.buf[:]
 	}
-	return
+	return data, .None
 }
 
 marshal_to_builder :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {

+ 4 - 3
core/encoding/json/parser.odin

@@ -106,6 +106,7 @@ parse_comma :: proc(p: ^Parser) -> (do_break: bool) {
 }
 
 parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
+	err = .None
 	token := p.curr_token
 	#partial switch token.kind {
 	case .Null:
@@ -175,6 +176,7 @@ parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
 }
 
 parse_array :: proc(p: ^Parser) -> (value: Value, err: Error) {
+	err = .None
 	expect_token(p, .Open_Bracket) or_return
 
 	array: Array
@@ -266,15 +268,14 @@ parse_object_body :: proc(p: ^Parser, end_token: Token_Kind) -> (obj: Object, er
 			break
 		}
 	}	
-	return
+	return obj, .None
 }
 
 parse_object :: proc(p: ^Parser) -> (value: Value, err: Error) {
 	expect_token(p, .Open_Brace) or_return
 	obj := parse_object_body(p, .Close_Brace) or_return
 	expect_token(p, .Close_Brace) or_return
-	value = obj
-	return
+	return obj, .None
 }
 
 

+ 1 - 0
core/encoding/json/unmarshal.odin

@@ -222,6 +222,7 @@ unmarsal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
 		advance_token(p)
 		return
 	case .False, .True:
+		advance_token(p)
 		if assign_bool(v, token.kind == .True) {
 			return
 		}

+ 11 - 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)
@@ -2076,9 +2076,11 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) {
 	case f32be:      fmt_float(fi, f64(a), 32, verb)
 	case f64be:      fmt_float(fi, f64(a), 64, verb)
 
+	case complex32:  fmt_complex(fi, complex128(a), 32, verb)
 	case complex64:  fmt_complex(fi, complex128(a), 64, verb)
 	case complex128: fmt_complex(fi, a, 128, verb)
 
+	case quaternion64:  fmt_quaternion(fi, quaternion256(a),  64, verb)
 	case quaternion128: fmt_quaternion(fi, quaternion256(a), 128, verb)
 	case quaternion256: fmt_quaternion(fi, a, 256, verb)
 

+ 44 - 0
core/fmt/fmt_js.odin

@@ -0,0 +1,44 @@
+//+build js
+package fmt
+
+import "core:io"
+
+foreign import "odin_env"
+
+@(private="file")
+foreign odin_env {
+	write :: proc "c" (fd: u32, p: []byte) ---
+}
+
+@(private="file")
+write_vtable := &io.Stream_VTable{
+	impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
+		fd := u32(uintptr(s.stream_data))
+		write(fd, p)
+		return len(p), nil
+	},	
+}
+
+@(private="file")
+stdout := io.Writer{
+	stream = {
+		stream_vtable = write_vtable,
+		stream_data = rawptr(uintptr(1)),
+	},
+}
+@(private="file")
+stderr := io.Writer{
+	stream = {
+		stream_vtable = write_vtable,
+		stream_data = rawptr(uintptr(2)),
+	},
+}
+
+// print* procedures return the number of bytes written
+print   :: proc(args: ..any, sep := " ") -> int { return wprint(w=stdout, args=args, sep=sep) }
+println :: proc(args: ..any, sep := " ") -> int { return wprintln(w=stdout, args=args, sep=sep) }
+printf  :: proc(fmt: string, args: ..any) -> int { return wprintf(stdout, fmt, ..args) }
+
+eprint   :: proc(args: ..any, sep := " ") -> int { return wprint(w=stderr, args=args, sep=sep) }
+eprintln :: proc(args: ..any, sep := " ") -> int { return wprintln(w=stderr, args=args, sep=sep) }
+eprintf  :: proc(fmt: string, args: ..any) -> int { return wprintf(stderr, fmt, ..args) }

+ 1 - 1
core/fmt/fmt_os.odin

@@ -1,4 +1,4 @@
-//+build !freestanding
+//+build !freestanding !js
 package fmt
 
 import "core:runtime"

+ 1 - 1
core/math/big/rat.odin

@@ -436,7 +436,7 @@ internal_rat_to_float :: proc($T: typeid, z: ^Rat, allocator := context.allocato
 	
 	mantissa >>= 1
 	
-	f = T(math.ldexp(f64(mantissa), i32(exp-MSIZE1)))
+	f = T(math.ldexp(f64(mantissa), exp-MSIZE1))
 	if math.is_inf(f, 0) {
 		exact = false
 	}

+ 49 - 0
core/math/linalg/specific.odin

@@ -2033,6 +2033,55 @@ matrix4_look_at :: proc{
 }
 
 
+matrix4_look_at_from_fru_f16 :: proc(eye, f, r, u: Vector3f16, flip_z_axis := true) -> (m: Matrix4f16) {
+	f, s, u := f, r, u
+	f = normalize(f)
+	s = normalize(s)
+	u = normalize(u)
+	fe := dot(f, eye)
+
+	return {
+		{+s.x, +u.x, -f.x, 0},
+		{+s.y, +u.y, -f.y, 0},
+		{+s.z, +u.z, -f.z, 0},
+		{-dot(s, eye), -dot(u, eye), +fe if flip_z_axis else -fe, 1},
+	}
+}
+matrix4_look_at_from_fru_f32 :: proc(eye, f, r, u: Vector3f32, flip_z_axis := true) -> (m: Matrix4f32) {
+	f, s, u := f, r, u
+	f = normalize(f)
+	s = normalize(s)
+	u = normalize(u)
+	fe := dot(f, eye)
+
+	return {
+		{+s.x, +u.x, -f.x, 0},
+		{+s.y, +u.y, -f.y, 0},
+		{+s.z, +u.z, -f.z, 0},
+		{-dot(s, eye), -dot(u, eye), +fe if flip_z_axis else -fe, 1},
+	}
+}
+matrix4_look_at_from_fru_f64 :: proc(eye, f, r, u: Vector3f64, flip_z_axis := true) -> (m: Matrix4f64) {
+	f, s, u := f, r, u
+	f = normalize(f)
+	s = normalize(s)
+	u = normalize(u)
+	fe := dot(f, eye)
+
+	return {
+		{+s.x, +u.x, -f.x, 0},
+		{+s.y, +u.y, -f.y, 0},
+		{+s.z, +u.z, -f.z, 0},
+		{-dot(s, eye), -dot(u, eye), +fe if flip_z_axis else -fe, 1},
+	}
+}
+matrix4_look_at_from_fru :: proc{
+	matrix4_look_at_from_fru_f16,
+	matrix4_look_at_from_fru_f32,
+	matrix4_look_at_from_fru_f64,
+}
+
+
 matrix4_perspective_f16 :: proc(fovy, aspect, near, far: f16, flip_z_axis := true) -> (m: Matrix4f16) {
 	tan_half_fovy := math.tan(0.5 * fovy)
 	m[0][0] = 1 / (aspect*tan_half_fovy)

+ 435 - 126
core/math/math.odin

@@ -36,66 +36,6 @@ MAX_F16_PRECISION ::  4 // Maximum number of meaningful digits after the decimal
 RAD_PER_DEG :: TAU/360.0
 DEG_PER_RAD :: 360.0/TAU
 
-
-@(default_calling_convention="none")
-foreign _ {
-	@(link_name="llvm.sqrt.f16")
-	sqrt_f16 :: proc(x: f16) -> f16 ---
-	@(link_name="llvm.sqrt.f32")
-	sqrt_f32 :: proc(x: f32) -> f32 ---
-	@(link_name="llvm.sqrt.f64")
-	sqrt_f64 :: proc(x: f64) -> f64 ---
-
-	@(link_name="llvm.sin.f16")
-	sin_f16 :: proc(θ: f16) -> f16 ---
-	@(link_name="llvm.sin.f32")
-	sin_f32 :: proc(θ: f32) -> f32 ---
-	@(link_name="llvm.sin.f64")
-	sin_f64 :: proc(θ: f64) -> f64 ---
-
-	@(link_name="llvm.cos.f16")
-	cos_f16 :: proc(θ: f16) -> f16 ---
-	@(link_name="llvm.cos.f32")
-	cos_f32 :: proc(θ: f32) -> f32 ---
-	@(link_name="llvm.cos.f64")
-	cos_f64 :: proc(θ: f64) -> f64 ---
-
-	@(link_name="llvm.pow.f16")
-	pow_f16 :: proc(x, power: f16) -> f16 ---
-	@(link_name="llvm.pow.f32")
-	pow_f32 :: proc(x, power: f32) -> f32 ---
-	@(link_name="llvm.pow.f64")
-	pow_f64 :: proc(x, power: f64) -> f64 ---
-
-	@(link_name="llvm.fmuladd.f16")
-	fmuladd_f16 :: proc(a, b, c: f16) -> f16 ---
-	@(link_name="llvm.fmuladd.f32")
-	fmuladd_f32 :: proc(a, b, c: f32) -> f32 ---
-	@(link_name="llvm.fmuladd.f64")
-	fmuladd_f64 :: proc(a, b, c: f64) -> f64 ---
-
-	@(link_name="llvm.log.f16")
-	ln_f16 :: proc(x: f16) -> f16 ---
-	@(link_name="llvm.log.f32")
-	ln_f32 :: proc(x: f32) -> f32 ---
-	@(link_name="llvm.log.f64")
-	ln_f64 :: proc(x: f64) -> f64 ---
-
-	@(link_name="llvm.exp.f16")
-	exp_f16 :: proc(x: f16) -> f16 ---
-	@(link_name="llvm.exp.f32")
-	exp_f32 :: proc(x: f32) -> f32 ---
-	@(link_name="llvm.exp.f64")
-	exp_f64 :: proc(x: f64) -> f64 ---
-
-	@(link_name="llvm.ldexp.f16")
-	ldexp_f16 :: proc(val: f16, exp: i32) -> f16 ---
-	@(link_name="llvm.ldexp.f32")
-	ldexp_f32 :: proc(val: f32, exp: i32) -> f32 ---
-	@(link_name="llvm.ldexp.f64")
-	ldexp_f64 :: proc(val: f64, exp: i32) -> f64 ---
-}
-
 sqrt_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(sqrt_f16(f16(x))) }
 sqrt_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(sqrt_f16(f16(x))) }
 sqrt_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(sqrt_f32(f32(x))) }
@@ -156,18 +96,6 @@ fmuladd       :: proc{
 	fmuladd_f64, fmuladd_f64le, fmuladd_f64be,
 }
 
-ln_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(ln_f16(f16(x))) }
-ln_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(ln_f16(f16(x))) }
-ln_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(ln_f32(f32(x))) }
-ln_f32be :: proc "contextless" (x: f32be) -> f32be { return #force_inline f32be(ln_f32(f32(x))) }
-ln_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(ln_f64(f64(x))) }
-ln_f64be :: proc "contextless" (x: f64be) -> f64be { return #force_inline f64be(ln_f64(f64(x))) }
-ln       :: proc{
-	ln_f16, ln_f16le, ln_f16be,
-	ln_f32, ln_f32le, ln_f32be,
-	ln_f64, ln_f64le, ln_f64be,
-}
-
 exp_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(exp_f16(f16(x))) }
 exp_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(exp_f16(f16(x))) }
 exp_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(exp_f32(f32(x))) }
@@ -180,13 +108,60 @@ exp       :: proc{
 	exp_f64, exp_f64le, exp_f64be,
 }
 
-ldexp_f16le :: proc "contextless" (val: f16le, exp: i32) -> f16le { return #force_inline f16le(ldexp_f16(f16(val), exp)) }
-ldexp_f16be :: proc "contextless" (val: f16be, exp: i32) -> f16be { return #force_inline f16be(ldexp_f16(f16(val), exp)) }
-ldexp_f32le :: proc "contextless" (val: f32le, exp: i32) -> f32le { return #force_inline f32le(ldexp_f32(f32(val), exp)) }
-ldexp_f32be :: proc "contextless" (val: f32be, exp: i32) -> f32be { return #force_inline f32be(ldexp_f32(f32(val), exp)) }
-ldexp_f64le :: proc "contextless" (val: f64le, exp: i32) -> f64le { return #force_inline f64le(ldexp_f64(f64(val), exp)) }
-ldexp_f64be :: proc "contextless" (val: f64be, exp: i32) -> f64be { return #force_inline f64be(ldexp_f64(f64(val), exp)) }
-ldexp       :: proc{
+
+
+ldexp_f64 :: proc "contextless" (val: f64, exp: int) -> f64 {
+	mask  :: F64_MASK
+	shift :: F64_SHIFT
+	bias  :: F64_BIAS
+	
+	switch {
+	case val == 0:
+		return val
+	case is_inf(val) || is_nan(val):
+		return val
+	}
+	exp := exp
+	frac, e := normalize_f64(val)
+	exp += e
+	x := transmute(u64)frac
+	exp += int(x>>shift)&mask - bias
+	if exp < -1075 { // underflow
+		return copy_sign(0, frac) 
+	} else if exp > 1023 { // overflow
+		if frac < 0 {
+			return inf_f64(-1)
+		}
+		return inf_f64(+1)
+	}
+	
+	m: f64 = 1
+	if exp < -1022 { // denormal
+		exp += 53
+		m = 1.0 / (1<<53)
+	}
+	x &~= mask << shift
+	x |= u64(exp+bias) << shift
+	return m * transmute(f64)x	
+}
+ldexp_f16   :: proc "contextless" (val: f16, exp: int) -> f16 { return f16(ldexp_f64(f64(val), exp)) }
+ldexp_f32   :: proc "contextless" (val: f32, exp: int) -> f32 { return f32(ldexp_f64(f64(val), exp)) }
+ldexp_f16le :: proc "contextless" (val: f16le, exp: int) -> f16le { return #force_inline f16le(ldexp_f16(f16(val), exp)) }
+ldexp_f16be :: proc "contextless" (val: f16be, exp: int) -> f16be { return #force_inline f16be(ldexp_f16(f16(val), exp)) }
+ldexp_f32le :: proc "contextless" (val: f32le, exp: int) -> f32le { return #force_inline f32le(ldexp_f32(f32(val), exp)) }
+ldexp_f32be :: proc "contextless" (val: f32be, exp: int) -> f32be { return #force_inline f32be(ldexp_f32(f32(val), exp)) }
+ldexp_f64le :: proc "contextless" (val: f64le, exp: int) -> f64le { return #force_inline f64le(ldexp_f64(f64(val), exp)) }
+ldexp_f64be :: proc "contextless" (val: f64be, exp: int) -> f64be { return #force_inline f64be(ldexp_f64(f64(val), exp)) }
+// ldexp is the inverse of frexp
+// it returns val * 2**exp.
+// 
+// Special cases:
+// 	ldexp(+0,   exp) = +0
+// 	ldexp(-0,   exp) = -0
+// 	ldexp(+inf, exp) = +inf
+// 	ldexp(-inf, exp) = -inf
+// 	ldexp(NaN,  exp) = NaN
+ldexp :: proc{
 	ldexp_f16, ldexp_f16le, ldexp_f16be,
 	ldexp_f32, ldexp_f32le, ldexp_f32be,
 	ldexp_f64, ldexp_f64le, ldexp_f64be,
@@ -210,22 +185,16 @@ log       :: proc{
 	log_f64, log_f64le, log_f64be,
 }
 
-log2_f16   :: proc "contextless" (x: f16)   -> f16   { return ln(x)/LN2 }
-log2_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(log2_f16(f16(x))) }
-log2_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(log2_f16(f16(x))) }
-
-log2_f32   :: proc "contextless" (x: f32)   -> f32   { return ln(x)/LN2 }
-log2_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(log2_f32(f32(x))) }
-log2_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(log2_f32(f32(x))) }
-
-log2_f64   :: proc "contextless" (x: f64)   -> f64   { return ln(x)/LN2 }
-log2_f64le :: proc "contextless" (x: f64le) -> f64le { return f64le(log2_f64(f64(x))) }
-log2_f64be :: proc "contextless" (x: f64be) -> f64be { return f64be(log2_f64(f64(x))) }
-log2       :: proc{
-	log2_f16, log2_f16le, log2_f16be,
-	log2_f32, log2_f32le, log2_f32be,
-	log2_f64, log2_f64le, log2_f64be,
-}
+log2_f16   :: logb_f16
+log2_f16le :: logb_f16le
+log2_f16be :: logb_f16be
+log2_f32   :: logb_f32
+log2_f32le :: logb_f32le
+log2_f32be :: logb_f32be
+log2_f64   :: logb_f64
+log2_f64le :: logb_f64le
+log2_f64be :: logb_f64be
+log2       :: logb
 
 log10_f16   :: proc "contextless" (x: f16)   -> f16   { return ln(x)/LN10 }
 log10_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(log10_f16(f16(x))) }
@@ -411,9 +380,9 @@ to_degrees       :: proc{
 
 trunc_f16   :: proc "contextless" (x: f16) -> f16 {
 	trunc_internal :: proc "contextless" (f: f16) -> f16 {
-		mask :: 0x1f
-		shift :: 16 - 6
-		bias :: 0xf
+		mask  :: F16_MASK
+		shift :: F16_SHIFT
+		bias  :: F16_BIAS
 
 		if f < 1 {
 			switch {
@@ -443,9 +412,9 @@ trunc_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16
 
 trunc_f32   :: proc "contextless" (x: f32) -> f32 {
 	trunc_internal :: proc "contextless" (f: f32) -> f32 {
-		mask :: 0xff
-		shift :: 32 - 9
-		bias :: 0x7f
+		mask  :: F32_MASK
+		shift :: F32_SHIFT
+		bias  :: F32_BIAS
 
 		if f < 1 {
 			switch {
@@ -475,9 +444,9 @@ trunc_f32be :: proc "contextless" (x: f32be) -> f32be { return #force_inline f32
 
 trunc_f64   :: proc "contextless" (x: f64) -> f64 {
 	trunc_internal :: proc "contextless" (f: f64) -> f64 {
-		mask :: 0x7ff
-		shift :: 64 - 12
-		bias :: 0x3ff
+		mask  :: F64_MASK
+		shift :: F64_SHIFT
+		bias  :: F64_BIAS
 
 		if f < 1 {
 			switch {
@@ -812,6 +781,44 @@ lcm :: proc "contextless" (x, y: $T) -> T
 	return x / gcd(x, y) * y
 }
 
+normalize_f16 :: proc "contextless" (x: f16) -> (y: f16, exponent: int) {
+	if abs(x) < F16_MIN {
+		return x * (1<<F16_SHIFT), -F16_SHIFT
+	}
+	return x, 0
+}
+normalize_f32 :: proc "contextless" (x: f32) -> (y: f32, exponent: int) {
+	if abs(x) < F32_MIN {
+		return x * (1<<F32_SHIFT), -F32_SHIFT
+	}
+	return x, 0
+}
+normalize_f64 :: proc "contextless" (x: f64) -> (y: f64, exponent: int) {
+	if abs(x) < F64_MIN {
+		return x * (1<<F64_SHIFT), -F64_SHIFT
+	}
+	return x, 0
+}
+
+normalize_f16le :: proc "contextless" (x: f16le) -> (y: f16le, exponent: int) { y0, e := normalize_f16(f16(x)); return f16le(y0), e }
+normalize_f16be :: proc "contextless" (x: f16be) -> (y: f16be, exponent: int) { y0, e := normalize_f16(f16(x)); return f16be(y0), e }
+normalize_f32le :: proc "contextless" (x: f32le) -> (y: f32le, exponent: int) { y0, e := normalize_f32(f32(x)); return f32le(y0), e }
+normalize_f32be :: proc "contextless" (x: f32be) -> (y: f32be, exponent: int) { y0, e := normalize_f32(f32(x)); return f32be(y0), e }
+normalize_f64le :: proc "contextless" (x: f64le) -> (y: f64le, exponent: int) { y0, e := normalize_f64(f64(x)); return f64le(y0), e }
+normalize_f64be :: proc "contextless" (x: f64be) -> (y: f64be, exponent: int) { y0, e := normalize_f64(f64(x)); return f64be(y0), e }
+
+normalize :: proc{
+	normalize_f16,
+	normalize_f32,
+	normalize_f64,
+	normalize_f16le,
+	normalize_f16be,
+	normalize_f32le,
+	normalize_f32be,
+	normalize_f64le,
+	normalize_f64be,
+}
+
 frexp_f16   :: proc "contextless" (x: f16)   -> (significand: f16,   exponent: int) {
 	f, e := frexp_f64(f64(x))
 	return f16(f), e
@@ -836,24 +843,25 @@ frexp_f32be :: proc "contextless" (x: f32be) -> (significand: f32be, exponent: i
 	f, e := frexp_f64(f64(x))
 	return f32be(f), e
 }
-frexp_f64 :: proc "contextless" (x: f64) -> (significand: f64, exponent: int) {
+frexp_f64 :: proc "contextless" (f: f64) -> (significand: f64, exponent: int) {
+	mask  :: F64_MASK
+	shift :: F64_SHIFT
+	bias  :: F64_BIAS
+	
 	switch {
-	case x == 0:
+	case f == 0:
 		return 0, 0
-	case x < 0:
-		significand, exponent = frexp(-x)
-		return -significand, exponent
-	}
-	ex := trunc(log2(x))
-	exponent = int(ex)
-	significand = x / pow(2.0, ex)
-	if abs(significand) >= 1 {
-		exponent += 1
-		significand /= 2
-	}
-	if exponent == 1024 && significand == 0 {
-		significand = 0.99999999999999988898
+	case is_inf(f) || is_nan(f):
+		return f, 0
 	}
+	f := f
+	
+	f, exponent = normalize_f64(f)
+	x := transmute(u64)f
+	exponent += int((x>>shift)&mask) - bias + 1
+	x &~= mask << shift
+	x |= (-1 + bias) << shift
+	significand = transmute(f64)x
 	return
 }
 frexp_f64le :: proc "contextless" (x: f64le) -> (significand: f64le, exponent: int) {
@@ -864,7 +872,18 @@ frexp_f64be :: proc "contextless" (x: f64be) -> (significand: f64be, exponent: i
 	f, e := frexp_f64(f64(x))
 	return f64be(f), e
 }
-frexp       :: proc{
+
+// frexp breaks the value into a normalized fraction, and an integral power of two
+// It returns a significand and exponent satisfying x == significand * 2**exponent
+// with the absolute value of significand in the intervalue of [0.5, 1).
+//
+// Special cases: 
+// 	frexp(+0)   = +0,   0
+// 	frexp(-0)   = -0,   0
+// 	frexp(+inf) = +inf, 0
+// 	frexp(-inf) = -inf, 0
+// 	frexp(NaN)  = NaN,  0
+frexp :: proc{
 	frexp_f16, frexp_f16le, frexp_f16be,
 	frexp_f32, frexp_f32le, frexp_f32be,
 	frexp_f64, frexp_f64le, frexp_f64be, 
@@ -1357,18 +1376,295 @@ tanh :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
 	return (t - 1) / (t + 1)
 }
 
-asinh :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
-	return ln(x + sqrt(x*x + 1))
+asinh :: proc "contextless" (y: $T) -> T where intrinsics.type_is_float(T) {
+	// The original C code, the long comment, and the constants
+	// below are from FreeBSD's /usr/src/lib/msun/src/s_asinh.c
+	// and came with this notice. 
+	//
+	// ====================================================
+	// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+	//
+	// Developed at SunPro, a Sun Microsystems, Inc. business.
+	// Permission to use, copy, modify, and distribute this
+	// software is freely granted, provided that this notice
+	// is preserved.
+	// ====================================================
+	
+	LN2       :: 0h3FE62E42FEFA39EF
+	NEAR_ZERO :: 1.0 / (1 << 28)
+	LARGE     :: 1 << 28
+	
+	x := f64(y)
+	
+	if is_nan(x) || is_inf(x) {
+		return T(x)
+	}
+	sign := false
+	if x < 0 {
+		x = -x
+		sign = true
+	}
+	temp: f64
+	switch {
+	case x > LARGE:
+		temp = ln(x) + LN2
+	case x > 2:
+		temp = ln(2*x + 1/(sqrt(x*x + 1) + x))
+	case x < NEAR_ZERO:
+		temp = x
+	case:
+		temp = log1p(x + x*x/(1 + sqrt(1 + x*x)))
+	}
+	
+	if sign {
+		temp = -temp
+	}
+	return T(temp)
+}
+
+acosh :: proc "contextless" (y: $T) -> T where intrinsics.type_is_float(T) {
+	// The original C code, the long comment, and the constants
+	// below are from FreeBSD's /usr/src/lib/msun/src/e_acosh.c
+	// and came with this notice. 
+	//
+	// ====================================================
+	// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+	//
+	// Developed at SunPro, a Sun Microsystems, Inc. business.
+	// Permission to use, copy, modify, and distribute this
+	// software is freely granted, provided that this notice
+	// is preserved.
+	// ====================================================
+	
+	LARGE :: 1<<28
+	LN2 :: 0h3FE62E42FEFA39EF
+	x := f64(y)
+	switch {
+	case x < 1 || is_nan(x):
+		return T(nan_f64())
+	case x == 1:
+		return 0
+	case x >= LARGE:
+		return T(ln(x) + LN2)
+	case x > 2:
+		return T(ln(2*x - 1/(x+sqrt(x*x-1))))
+	}
+	t := x-1
+	return T(log1p(t + sqrt(2*t + t*t)))
+}
+
+atanh :: proc "contextless" (y: $T) -> T where intrinsics.type_is_float(T) {
+	// The original C code, the long comment, and the constants
+	// below are from FreeBSD's /usr/src/lib/msun/src/e_atanh.c
+	// and came with this notice. 
+	//
+	// ====================================================
+	// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+	//
+	// Developed at SunPro, a Sun Microsystems, Inc. business.
+	// Permission to use, copy, modify, and distribute this
+	// software is freely granted, provided that this notice
+	// is preserved.
+	// ====================================================
+	NEAR_ZERO :: 1.0 / (1 << 28)
+	x := f64(y)
+	switch {
+	case x < -1 || x > 1 || is_nan(x):
+		return T(nan_f64())
+	case x == 1:
+		return T(inf_f64(1))
+	case x == -1:
+		return T(inf_f64(-1))
+	}
+	sign := false
+	if x < 0 {
+		x = -x
+		sign = true
+	}
+	temp: f64
+	switch {
+	case x < NEAR_ZERO:
+		temp = x
+	case x < 0.5:
+		temp = x + x
+		temp = 0.5 * log1p(temp + temp*x/(1-x))
+	case:
+		temp = 0.5 * log1p((x+x)/(1-x))
+	}
+	if sign {
+		temp = -temp
+	}
+	return T(temp)
+}
+
+ilogb_f16 :: proc "contextless" (val: f16) -> int {
+	switch {
+	case val == 0:    return int(min(i32))
+	case is_nan(val): return int(max(i32))
+	case is_inf(val): return int(max(i32))
+	}
+	x, exp := normalize_f16(val)
+	return int(((transmute(u16)x)>>F16_SHIFT)&F16_MASK) - F16_BIAS + exp
+}
+ilogb_f32 :: proc "contextless" (val: f32) -> int {
+	switch {
+	case val == 0:    return int(min(i32))
+	case is_nan(val): return int(max(i32))
+	case is_inf(val): return int(max(i32))
+	}
+	x, exp := normalize_f32(val)
+	return int(((transmute(u32)x)>>F32_SHIFT)&F32_MASK) - F32_BIAS + exp
+}
+ilogb_f64 :: proc "contextless" (val: f64) -> int {
+	switch {
+	case val == 0:    return int(min(i32))
+	case is_nan(val): return int(max(i32))
+	case is_inf(val): return int(max(i32))
+	}
+	x, exp := normalize_f64(val)
+	return int(((transmute(u64)x)>>F64_SHIFT)&F64_MASK) - F64_BIAS + exp
+}
+ilogb_f16le :: proc "contextless" (value: f16le) -> int { return ilogb_f16(f16(value)) }
+ilogb_f16be :: proc "contextless" (value: f16be) -> int { return ilogb_f16(f16(value)) }
+ilogb_f32le :: proc "contextless" (value: f32le) -> int { return ilogb_f32(f32(value)) }
+ilogb_f32be :: proc "contextless" (value: f32be) -> int { return ilogb_f32(f32(value)) }
+ilogb_f64le :: proc "contextless" (value: f64le) -> int { return ilogb_f64(f64(value)) }
+ilogb_f64be :: proc "contextless" (value: f64be) -> int { return ilogb_f64(f64(value)) }
+ilogb :: proc {
+	ilogb_f16,
+	ilogb_f32,
+	ilogb_f64,
+	ilogb_f16le,
+	ilogb_f16be,
+	ilogb_f32le,
+	ilogb_f32be,
+	ilogb_f64le,
+	ilogb_f64be,
+}
+
+logb_f16 :: proc "contextless" (val: f16) -> f16 {
+	switch {
+	case val == 0:    return inf_f16(-1)
+	case is_inf(val): return inf_f16(+1)
+	case is_nan(val): return val
+	}
+	return f16(ilogb(val))
+}
+logb_f32 :: proc "contextless" (val: f32) -> f32 {
+	switch {
+	case val == 0:    return inf_f32(-1)
+	case is_inf(val): return inf_f32(+1)
+	case is_nan(val): return val
+	}
+	return f32(ilogb(val))
+}
+logb_f64 :: proc "contextless" (val: f64) -> f64 {
+	switch {
+	case val == 0:    return inf_f64(-1)
+	case is_inf(val): return inf_f64(+1)
+	case is_nan(val): return val
+	}
+	return f64(ilogb(val))
+}
+logb_f16le :: proc "contextless" (value: f16le) -> f16le { return f16le(logb_f16(f16(value))) }
+logb_f16be :: proc "contextless" (value: f16be) -> f16be { return f16be(logb_f16(f16(value))) }
+logb_f32le :: proc "contextless" (value: f32le) -> f32le { return f32le(logb_f32(f32(value))) }
+logb_f32be :: proc "contextless" (value: f32be) -> f32be { return f32be(logb_f32(f32(value))) }
+logb_f64le :: proc "contextless" (value: f64le) -> f64le { return f64le(logb_f64(f64(value))) }
+logb_f64be :: proc "contextless" (value: f64be) -> f64be { return f64be(logb_f64(f64(value))) }
+logb :: proc {
+	logb_f16,
+	logb_f32,
+	logb_f64,
+	logb_f16le,
+	logb_f16be,
+	logb_f32le,
+	logb_f32be,
+	logb_f64le,
+	logb_f64be,
+}
+
+nextafter_f16 :: proc "contextless" (x, y: f16) -> (r: f16) {
+	switch {
+	case is_nan(x) || is_nan(y):
+		r = nan_f16()
+	case x == y:
+		r = x
+	case x == 0:
+		r = copy_sign_f16(1, y)
+	case (y > x) == (x > 0):
+		r = transmute(f16)(transmute(u16)x + 1)
+	case:
+		r = transmute(f16)(transmute(u16)x - 1)
+	}
+	return
+}
+nextafter_f32 :: proc "contextless" (x, y: f32) -> (r: f32) {
+	switch {
+	case is_nan(x) || is_nan(y):
+		r = nan_f32()
+	case x == y:
+		r = x
+	case x == 0:
+		r = copy_sign_f32(1, y)
+	case (y > x) == (x > 0):
+		r = transmute(f32)(transmute(u32)x + 1)
+	case:
+		r = transmute(f32)(transmute(u32)x - 1)
+	}
+	return
+}
+nextafter_f64 :: proc "contextless" (x, y: f64) -> (r: f64) {
+	switch {
+	case is_nan(x) || is_nan(y):
+		r = nan_f64()
+	case x == y:
+		r = x
+	case x == 0:
+		r = copy_sign_f64(1, y)
+	case (y > x) == (x > 0):
+		r = transmute(f64)(transmute(u64)x + 1)
+	case:
+		r = transmute(f64)(transmute(u64)x - 1)
+	}
+	return
+}
+nextafter_f16le :: proc "contextless" (x, y: f16le) -> (r: f16le) { return f16le(nextafter_f16(f16(x), f16(y))) }
+nextafter_f16be :: proc "contextless" (x, y: f16be) -> (r: f16be) { return f16be(nextafter_f16(f16(x), f16(y))) }
+nextafter_f32le :: proc "contextless" (x, y: f32le) -> (r: f32le) { return f32le(nextafter_f32(f32(x), f32(y))) }
+nextafter_f32be :: proc "contextless" (x, y: f32be) -> (r: f32be) { return f32be(nextafter_f32(f32(x), f32(y))) }
+nextafter_f64le :: proc "contextless" (x, y: f64le) -> (r: f64le) { return f64le(nextafter_f64(f64(x), f64(y))) }
+nextafter_f64be :: proc "contextless" (x, y: f64be) -> (r: f64be) { return f64be(nextafter_f64(f64(x), f64(y))) }
+
+nextafter :: proc{
+	nextafter_f16, nextafter_f16le, nextafter_f16be,
+	nextafter_f32, nextafter_f32le, nextafter_f32be,
+	nextafter_f64, nextafter_f64le, nextafter_f64be,
 }
 
-acosh :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
-	return ln(x + sqrt(x*x - 1))
+signbit_f16 :: proc "contextless" (x: f16) -> bool {
+	return (transmute(u16)x)&(1<<15) != 0
+}
+signbit_f32 :: proc "contextless" (x: f32) -> bool {
+	return (transmute(u32)x)&(1<<31) != 0
+}
+signbit_f64 :: proc "contextless" (x: f64) -> bool {
+	return (transmute(u64)x)&(1<<63) != 0
 }
+signbit_f16le :: proc "contextless" (x: f16le) -> bool { return signbit_f16(f16(x)) }
+signbit_f32le :: proc "contextless" (x: f32le) -> bool { return signbit_f32(f32(x)) }
+signbit_f64le :: proc "contextless" (x: f64le) -> bool { return signbit_f64(f64(x)) }
+signbit_f16be :: proc "contextless" (x: f16be) -> bool { return signbit_f16(f16(x)) }
+signbit_f32be :: proc "contextless" (x: f32be) -> bool { return signbit_f32(f32(x)) }
+signbit_f64be :: proc "contextless" (x: f64be) -> bool { return signbit_f64(f64(x)) }
 
-atanh :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
-	return 0.5*ln((1+x)/(1-x))
+signbit :: proc{
+	signbit_f16, signbit_f16le, signbit_f16be,
+	signbit_f32, signbit_f32le, signbit_f32be,
+	signbit_f64, signbit_f64le, signbit_f64be,
 }
 
+
 F16_DIG        :: 3
 F16_EPSILON    :: 0.00097656
 F16_GUARD      :: 0
@@ -1409,3 +1705,16 @@ F64_MIN_10_EXP :: -307                     // min decimal exponent
 F64_MIN_EXP    :: -1021                    // min binary exponent
 F64_RADIX      :: 2                        // exponent radix
 F64_ROUNDS     :: 1                        // addition rounding: near
+
+
+F16_MASK  :: 0x1f
+F16_SHIFT :: 16 - 6
+F16_BIAS  :: 0xf
+
+F32_MASK  :: 0xff
+F32_SHIFT :: 32 - 9
+F32_BIAS  :: 0x7f
+
+F64_MASK  :: 0x7ff
+F64_SHIFT :: 64 - 12
+F64_BIAS  :: 0x3ff

+ 169 - 0
core/math/math_basic.odin

@@ -0,0 +1,169 @@
+//+build !js
+package math
+
+import "core:intrinsics"
+
+@(default_calling_convention="none")
+foreign _ {
+	@(link_name="llvm.sin.f16")
+	sin_f16 :: proc(θ: f16) -> f16 ---
+	@(link_name="llvm.sin.f32")
+	sin_f32 :: proc(θ: f32) -> f32 ---
+	@(link_name="llvm.sin.f64")
+	sin_f64 :: proc(θ: f64) -> f64 ---
+
+	@(link_name="llvm.cos.f16")
+	cos_f16 :: proc(θ: f16) -> f16 ---
+	@(link_name="llvm.cos.f32")
+	cos_f32 :: proc(θ: f32) -> f32 ---
+	@(link_name="llvm.cos.f64")
+	cos_f64 :: proc(θ: f64) -> f64 ---
+
+	@(link_name="llvm.pow.f16")
+	pow_f16 :: proc(x, power: f16) -> f16 ---
+	@(link_name="llvm.pow.f32")
+	pow_f32 :: proc(x, power: f32) -> f32 ---
+	@(link_name="llvm.pow.f64")
+	pow_f64 :: proc(x, power: f64) -> f64 ---
+
+	@(link_name="llvm.fmuladd.f16")
+	fmuladd_f16 :: proc(a, b, c: f16) -> f16 ---
+	@(link_name="llvm.fmuladd.f32")
+	fmuladd_f32 :: proc(a, b, c: f32) -> f32 ---
+	@(link_name="llvm.fmuladd.f64")
+	fmuladd_f64 :: proc(a, b, c: f64) -> f64 ---
+
+	@(link_name="llvm.exp.f16")
+	exp_f16 :: proc(x: f16) -> f16 ---
+	@(link_name="llvm.exp.f32")
+	exp_f32 :: proc(x: f32) -> f32 ---
+	@(link_name="llvm.exp.f64")
+	exp_f64 :: proc(x: f64) -> f64 ---
+}
+
+sqrt_f16 :: proc "contextless" (x: f16) -> f16 {
+	return intrinsics.sqrt(x)
+}
+sqrt_f32 :: proc "contextless" (x: f32) -> f32 {
+	return intrinsics.sqrt(x)
+}
+sqrt_f64 :: proc "contextless" (x: f64) -> f64 {
+	return intrinsics.sqrt(x)
+}
+
+
+
+ln_f64 :: proc "contextless" (x: f64) -> f64 {
+	// The original C code, the long comment, and the constants
+	// below are from FreeBSD's /usr/src/lib/msun/src/e_log.c
+	// and came with this notice.
+	//
+	// ====================================================
+	// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+	//
+	// Developed at SunPro, a Sun Microsystems, Inc. business.
+	// Permission to use, copy, modify, and distribute this
+	// software is freely granted, provided that this notice
+	// is preserved.
+	// ====================================================
+	//
+	// __ieee754_log(x)
+	// Return the logarithm of x
+	//
+	// Method :
+	//   1. Argument Reduction: find k and f such that
+	//			x = 2**k * (1+f),
+	//	   where  sqrt(2)/2 < 1+f < sqrt(2) .
+	//
+	//   2. Approximation of log(1+f).
+	//	Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s)
+	//		 = 2s + 2/3 s**3 + 2/5 s**5 + .....,
+	//	     	 = 2s + s*R
+	//      We use a special Reme algorithm on [0,0.1716] to generate
+	//	a polynomial of degree 14 to approximate R.  The maximum error
+	//	of this polynomial approximation is bounded by 2**-58.45. In
+	//	other words,
+	//		        2      4      6      8      10      12      14
+	//	    R(z) ~ L1*s +L2*s +L3*s +L4*s +L5*s  +L6*s  +L7*s
+	//	(the values of L1 to L7 are listed in the program) and
+	//	    |      2          14          |     -58.45
+	//	    | L1*s +...+L7*s    -  R(z) | <= 2
+	//	    |                             |
+	//	Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2.
+	//	In order to guarantee error in log below 1ulp, we compute log by
+	//		log(1+f) = f - s*(f - R)		(if f is not too large)
+	//		log(1+f) = f - (hfsq - s*(hfsq+R)).	(better accuracy)
+	//
+	//	3. Finally,  log(x) = k*Ln2 + log(1+f).
+	//			    = k*Ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*Ln2_lo)))
+	//	   Here Ln2 is split into two floating point number:
+	//			Ln2_hi + Ln2_lo,
+	//	   where n*Ln2_hi is always exact for |n| < 2000.
+	//
+	// Special cases:
+	//	log(x) is NaN with signal if x < 0 (including -INF) ;
+	//	log(+INF) is +INF; log(0) is -INF with signal;
+	//	log(NaN) is that NaN with no signal.
+	//
+	// Accuracy:
+	//	according to an error analysis, the error is always less than
+	//	1 ulp (unit in the last place).
+	//
+	// Constants:
+	// The hexadecimal values are the intended ones for the following
+	// constants. The decimal values may be used, provided that the
+	// compiler will convert from decimal to binary accurately enough
+	// to produce the hexadecimal values shown.
+	
+	LN2_HI :: 0h3fe62e42_fee00000 // 6.93147180369123816490e-01
+	LN2_LO :: 0h3dea39ef_35793c76 // 1.90821492927058770002e-10
+	L1     :: 0h3fe55555_55555593 // 6.666666666666735130e-01
+	L2     :: 0h3fd99999_9997fa04 // 3.999999999940941908e-01
+	L3     :: 0h3fd24924_94229359 // 2.857142874366239149e-01
+	L4     :: 0h3fcc71c5_1d8e78af // 2.222219843214978396e-01
+	L5     :: 0h3fc74664_96cb03de // 1.818357216161805012e-01
+	L6     :: 0h3fc39a09_d078c69f // 1.531383769920937332e-01
+	L7     :: 0h3fc2f112_df3e5244 // 1.479819860511658591e-01
+	
+	switch {
+	case is_nan(x) || is_inf(x, 1):
+		return x
+	case x < 0:
+		return nan_f64()
+	case x == 0:
+		return inf_f64(-1)
+	}
+
+	// reduce
+	f1, ki := frexp(x)
+	if f1 < SQRT_TWO/2 {
+		f1 *= 2
+		ki -= 1
+	}
+	f := f1 - 1
+	k := f64(ki)
+
+	// compute
+	s := f / (2 + f)
+	s2 := s * s
+	s4 := s2 * s2
+	t1 := s2 * (L1 + s4*(L3+s4*(L5+s4*L7)))
+	t2 := s4 * (L2 + s4*(L4+s4*L6))
+	R := t1 + t2
+	hfsq := 0.5 * f * f
+	return k*LN2_HI - ((hfsq - (s*(hfsq+R) + k*LN2_LO)) - f)
+}
+
+ln_f16 :: proc "contextless" (x: f16) -> f16 { return #force_inline f16(ln_f64(f64(x))) }
+ln_f32 :: proc "contextless" (x: f32) -> f32 { return #force_inline f32(ln_f64(f64(x))) }
+ln_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(ln_f64(f64(x))) }
+ln_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(ln_f64(f64(x))) }
+ln_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(ln_f64(f64(x))) }
+ln_f32be :: proc "contextless" (x: f32be) -> f32be { return #force_inline f32be(ln_f64(f64(x))) }
+ln_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(ln_f64(f64(x))) }
+ln_f64be :: proc "contextless" (x: f64be) -> f64be { return #force_inline f64be(ln_f64(f64(x))) }
+ln :: proc{
+	ln_f16, ln_f16le, ln_f16be,
+	ln_f32, ln_f32le, ln_f32be,
+	ln_f64, ln_f64le, ln_f64be,
+}

+ 42 - 0
core/math/math_basic_js.odin

@@ -0,0 +1,42 @@
+//+build js
+package math
+
+import "core:intrinsics"
+
+foreign import "odin_env"
+
+@(default_calling_convention="c")
+foreign odin_env {
+	@(link_name="sin")
+	sin_f64 :: proc(θ: f64) -> f64 ---
+	@(link_name="cos")
+	cos_f64 :: proc(θ: f64) -> f64 ---
+	@(link_name="pow")
+	pow_f64 :: proc(x, power: f64) -> f64 ---
+	@(link_name="fmuladd")
+	fmuladd_f64 :: proc(a, b, c: f64) -> f64 ---
+	@(link_name="ln")
+	ln_f64 :: proc(x: f64) -> f64 ---
+	@(link_name="exp")
+	exp_f64 :: proc(x: f64) -> f64 ---
+}
+
+sqrt_f64 :: proc "contextless" (x: f64) -> f64 {
+	return intrinsics.sqrt(x)
+}
+
+sqrt_f16    :: proc "c" (x: f16) -> f16             { return f16(sqrt_f64(f64(x)))                    }
+sin_f16     :: proc "c" (θ: f16) -> f16             { return f16(sin_f64(f64(θ)))                     }
+cos_f16     :: proc "c" (θ: f16) -> f16             { return f16(cos_f64(f64(θ)))                     }
+pow_f16     :: proc "c" (x, power: f16) -> f16      { return f16(pow_f64(f64(x), f64(power)))         }
+fmuladd_f16 :: proc "c" (a, b, c: f16) -> f16       { return f16(fmuladd_f64(f64(a), f64(a), f64(c))) }
+ln_f16      :: proc "c" (x: f16) -> f16             { return f16(ln_f64(f64(x)))                      }
+exp_f16     :: proc "c" (x: f16) -> f16             { return f16(exp_f64(f64(x)))                     }
+
+sqrt_f32    :: proc "c" (x: f32) -> f32             { return f32(sqrt_f64(f64(x)))                    }
+sin_f32     :: proc "c" (θ: f32) -> f32             { return f32(sin_f64(f64(θ)))                     }
+cos_f32     :: proc "c" (θ: f32) -> f32             { return f32(cos_f64(f64(θ)))                     }
+pow_f32     :: proc "c" (x, power: f32) -> f32      { return f32(pow_f64(f64(x), f64(power)))         }
+fmuladd_f32 :: proc "c" (a, b, c: f32) -> f32       { return f32(fmuladd_f64(f64(a), f64(a), f64(c))) }
+ln_f32      :: proc "c" (x: f32) -> f32             { return f32(ln_f64(f64(x)))                      }
+exp_f32     :: proc "c" (x: f32) -> f32             { return f32(exp_f64(f64(x)))                     }

+ 410 - 0
core/math/math_erf.odin

@@ -0,0 +1,410 @@
+package math
+
+// The original C code and the long comment below are
+// from FreeBSD's /usr/src/lib/msun/src/s_erf.c and
+// came with this notice. 
+//
+// ====================================================
+// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+//
+// Developed at SunPro, a Sun Microsystems, Inc. business.
+// Permission to use, copy, modify, and distribute this
+// software is freely granted, provided that this notice
+// is preserved.
+// ====================================================
+//
+//
+// double erf(double x)
+// double erfc(double x)
+//                           x
+//                    2      |\
+//     erf(x)  =  ---------  | exp(-t*t)dt
+//                 sqrt(pi) \|
+//                           0
+//
+//     erfc(x) =  1-erf(x)
+//  Note that
+//              erf(-x) = -erf(x)
+//              erfc(-x) = 2 - erfc(x)
+//
+// Method:
+//      1. For |x| in [0, 0.84375]
+//          erf(x)  = x + x*R(x**2)
+//          erfc(x) = 1 - erf(x)           if x in [-.84375,0.25]
+//                  = 0.5 + ((0.5-x)-x*R)  if x in [0.25,0.84375]
+//         where R = P/Q where P is an odd poly of degree 8 and
+//         Q is an odd poly of degree 10.
+//                                               -57.90
+//                      | R - (erf(x)-x)/x | <= 2
+//
+//
+//         Remark. The formula is derived by noting
+//          erf(x) = (2/sqrt(pi))*(x - x**3/3 + x**5/10 - x**7/42 + ....)
+//         and that
+//          2/sqrt(pi) = 1.128379167095512573896158903121545171688
+//         is close to one. The interval is chosen because the fix
+//         point of erf(x) is near 0.6174 (i.e., erf(x)=x when x is
+//         near 0.6174), and by some experiment, 0.84375 is chosen to
+//         guarantee the error is less than one ulp for erf.
+//
+//      2. For |x| in [0.84375,1.25], let s = |x| - 1, and
+//         c = 0.84506291151 rounded to single (24 bits)
+//              erf(x)  = sign(x) * (c  + P1(s)/Q1(s))
+//              erfc(x) = (1-c)  - P1(s)/Q1(s) if x > 0
+//                        1+(c+P1(s)/Q1(s))    if x < 0
+//              |P1/Q1 - (erf(|x|)-c)| <= 2**-59.06
+//         Remark: here we use the taylor series expansion at x=1.
+//              erf(1+s) = erf(1) + s*Poly(s)
+//                       = 0.845.. + P1(s)/Q1(s)
+//         That is, we use rational approximation to approximate
+//                      erf(1+s) - (c = (single)0.84506291151)
+//         Note that |P1/Q1|< 0.078 for x in [0.84375,1.25]
+//         where
+//              P1(s) = degree 6 poly in s
+//              Q1(s) = degree 6 poly in s
+//
+//      3. For x in [1.25,1/0.35(~2.857143)],
+//              erfc(x) = (1/x)*exp(-x*x-0.5625+R1/S1)
+//              erf(x)  = 1 - erfc(x)
+//         where
+//              R1(z) = degree 7 poly in z, (z=1/x**2)
+//              S1(z) = degree 8 poly in z
+//
+//      4. For x in [1/0.35,28]
+//              erfc(x) = (1/x)*exp(-x*x-0.5625+R2/S2) if x > 0
+//                      = 2.0 - (1/x)*exp(-x*x-0.5625+R2/S2) if -6<x<0
+//                      = 2.0 - tiny            (if x <= -6)
+//              erf(x)  = sign(x)*(1.0 - erfc(x)) if x < 6, else
+//              erf(x)  = sign(x)*(1.0 - tiny)
+//         where
+//              R2(z) = degree 6 poly in z, (z=1/x**2)
+//              S2(z) = degree 7 poly in z
+//
+//      Note1:
+//         To compute exp(-x*x-0.5625+R/S), let s be a single
+//         precision number and s := x; then
+//              -x*x = -s*s + (s-x)*(s+x)
+//              exp(-x*x-0.5626+R/S) =
+//                      exp(-s*s-0.5625)*exp((s-x)*(s+x)+R/S);
+//      Note2:
+//         Here 4 and 5 make use of the asymptotic series
+//                        exp(-x*x)
+//              erfc(x) ~ ---------- * ( 1 + Poly(1/x**2) )
+//                        x*sqrt(pi)
+//         We use rational approximation to approximate
+//              g(s)=f(1/x**2) = log(erfc(x)*x) - x*x + 0.5625
+//         Here is the error bound for R1/S1 and R2/S2
+//              |R1/S1 - f(x)|  < 2**(-62.57)
+//              |R2/S2 - f(x)|  < 2**(-61.52)
+//
+//      5. For inf > x >= 28
+//              erf(x)  = sign(x) *(1 - tiny)  (raise inexact)
+//              erfc(x) = tiny*tiny (raise underflow) if x > 0
+//                      = 2 - tiny if x<0
+//
+//      7. Special case:
+//              erf(0)  = 0, erf(inf)  = 1, erf(-inf) = -1,
+//              erfc(0) = 1, erfc(inf) = 0, erfc(-inf) = 2,
+//              erfc/erf(NaN) is NaN
+
+erf :: proc{
+	erf_f16,
+	erf_f16le,
+	erf_f16be,
+	erf_f32,
+	erf_f32le,
+	erf_f32be,
+	erf_f64,
+}
+
+erf_f16   :: proc "contextless" (x: f16)   -> f16   { return f16(erf_f64(f64(x))) }
+erf_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(erf_f64(f64(x))) }
+erf_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(erf_f64(f64(x))) }
+erf_f32   :: proc "contextless" (x: f32)   -> f32   { return f32(erf_f64(f64(x))) }
+erf_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(erf_f64(f64(x))) }
+erf_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(erf_f64(f64(x))) }
+
+erf_f64 :: proc "contextless" (x: f64) -> f64 {
+	erx :: 0h3FEB0AC160000000
+	// Coefficients for approximation to  erf in [0, 0.84375]
+	efx  :: 0h3FC06EBA8214DB69
+	efx8 :: 0h3FF06EBA8214DB69
+	pp0  :: 0h3FC06EBA8214DB68
+	pp1  :: 0hBFD4CD7D691CB913
+	pp2  :: 0hBF9D2A51DBD7194F
+	pp3  :: 0hBF77A291236668E4
+	pp4  :: 0hBEF8EAD6120016AC
+	qq1  :: 0h3FD97779CDDADC09
+	qq2  :: 0h3FB0A54C5536CEBA
+	qq3  :: 0h3F74D022C4D36B0F
+	qq4  :: 0h3F215DC9221C1A10
+	qq5  :: 0hBED09C4342A26120
+	// Coefficients for approximation to  erf  in [0.84375, 1.25]
+	pa0 :: 0hBF6359B8BEF77538
+	pa1 :: 0h3FDA8D00AD92B34D
+	pa2 :: 0hBFD7D240FBB8C3F1
+	pa3 :: 0h3FD45FCA805120E4
+	pa4 :: 0hBFBC63983D3E28EC
+	pa5 :: 0h3FA22A36599795EB
+	pa6 :: 0hBF61BF380A96073F
+	qa1 :: 0h3FBB3E6618EEE323
+	qa2 :: 0h3FE14AF092EB6F33
+	qa3 :: 0h3FB2635CD99FE9A7
+	qa4 :: 0h3FC02660E763351F
+	qa5 :: 0h3F8BEDC26B51DD1C
+	qa6 :: 0h3F888B545735151D
+	// Coefficients for approximation to  erfc in [1.25, 1/0.35]
+	ra0 :: 0hBF843412600D6435
+	ra1 :: 0hBFE63416E4BA7360
+	ra2 :: 0hC0251E0441B0E726
+	ra3 :: 0hC04F300AE4CBA38D
+	ra4 :: 0hC0644CB184282266
+	ra5 :: 0hC067135CEBCCABB2
+	ra6 :: 0hC054526557E4D2F2
+	ra7 :: 0hC023A0EFC69AC25C
+	sa1 :: 0h4033A6B9BD707687
+	sa2 :: 0h4061350C526AE721
+	sa3 :: 0h407B290DD58A1A71
+	sa4 :: 0h40842B1921EC2868
+	sa5 :: 0h407AD02157700314
+	sa6 :: 0h405B28A3EE48AE2C
+	sa7 :: 0h401A47EF8E484A93
+	sa8 :: 0hBFAEEFF2EE749A62
+	// Coefficients for approximation to  erfc in [1/.35, 28]
+	rb0 :: 0hBF84341239E86F4A
+	rb1 :: 0hBFE993BA70C285DE
+	rb2 :: 0hC031C209555F995A
+	rb3 :: 0hC064145D43C5ED98
+	rb4 :: 0hC083EC881375F228
+	rb5 :: 0hC09004616A2E5992
+	rb6 :: 0hC07E384E9BDC383F
+	sb1 :: 0h403E568B261D5190
+	sb2 :: 0h40745CAE221B9F0A
+	sb3 :: 0h409802EB189D5118
+	sb4 :: 0h40A8FFB7688C246A
+	sb5 :: 0h40A3F219CEDF3BE6
+	sb6 :: 0h407DA874E79FE763
+	sb7 :: 0hC03670E242712D62
+	
+	
+	VERY_TINY :: 0h0080000000000000
+	SMALL     :: 1.0 / (1 << 28)        // 2**-28
+
+	// special cases
+	switch {
+	case is_nan(x):
+		return nan_f64()
+	case is_inf(x, 1):
+		return 1
+	case is_inf(x, -1):
+		return -1
+	}
+	x := x
+	sign := false
+	if x < 0 {
+		x = -x
+		sign = true
+	}
+	if x < 0.84375 { // |x| < 0.84375
+		temp: f64
+		if x < SMALL { // |x| < 2**-28
+			if x < VERY_TINY {
+				temp = 0.125 * (8.0*x + efx8*x) // avoid underflow
+			} else {
+				temp = x + efx*x
+			}
+		} else {
+			z := x * x
+			r := pp0 + z*(pp1+z*(pp2+z*(pp3+z*pp4)))
+			s := 1 + z*(qq1+z*(qq2+z*(qq3+z*(qq4+z*qq5))))
+			y := r / s
+			temp = x + x*y
+		}
+		if sign {
+			return -temp
+		}
+		return temp
+	}
+	if x < 1.25 { // 0.84375 <= |x| < 1.25
+		s := x - 1
+		P := pa0 + s*(pa1+s*(pa2+s*(pa3+s*(pa4+s*(pa5+s*pa6)))))
+		Q := 1 + s*(qa1+s*(qa2+s*(qa3+s*(qa4+s*(qa5+s*qa6)))))
+		if sign {
+			return -erx - P/Q
+		}
+		return erx + P/Q
+	}
+	if x >= 6 { // inf > |x| >= 6
+		if sign {
+			return -1
+		}
+		return 1
+	}
+	s := 1 / (x * x)
+	R, S: f64
+	if x < 1/0.35 { // |x| < 1 / 0.35  ~ 2.857143
+		R = ra0 + s*(ra1+s*(ra2+s*(ra3+s*(ra4+s*(ra5+s*(ra6+s*ra7))))))
+		S = 1 + s*(sa1+s*(sa2+s*(sa3+s*(sa4+s*(sa5+s*(sa6+s*(sa7+s*sa8)))))))
+	} else { // |x| >= 1 / 0.35  ~ 2.857143
+		R = rb0 + s*(rb1+s*(rb2+s*(rb3+s*(rb4+s*(rb5+s*rb6)))))
+		S = 1 + s*(sb1+s*(sb2+s*(sb3+s*(sb4+s*(sb5+s*(sb6+s*sb7))))))
+	}
+	z := transmute(f64)(0xffffffff00000000 & transmute(u64)x) // pseudo-single (20-bit) precision x
+	r := exp(-z*z-0.5625) * exp((z-x)*(z+x)+R/S)
+	if sign {
+		return r/x - 1
+	}
+	return 1 - r/x
+}
+
+
+erfc :: proc{
+	erfc_f16,
+	erfc_f16le,
+	erfc_f16be,
+	erfc_f32,
+	erfc_f32le,
+	erfc_f32be,
+	erfc_f64,
+}
+
+erfc_f16   :: proc "contextless" (x: f16)   -> f16   { return f16(erfc_f64(f64(x))) }
+erfc_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(erfc_f64(f64(x))) }
+erfc_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(erfc_f64(f64(x))) }
+erfc_f32   :: proc "contextless" (x: f32)   -> f32   { return f32(erfc_f64(f64(x))) }
+erfc_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(erfc_f64(f64(x))) }
+erfc_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(erfc_f64(f64(x))) }
+
+erfc_f64 :: proc "contextless" (x: f64) -> f64 {
+	erx :: 0h3FEB0AC160000000
+	// Coefficients for approximation to  erf in [0, 0.84375]
+	efx  :: 0h3FC06EBA8214DB69
+	efx8 :: 0h3FF06EBA8214DB69
+	pp0  :: 0h3FC06EBA8214DB68
+	pp1  :: 0hBFD4CD7D691CB913
+	pp2  :: 0hBF9D2A51DBD7194F
+	pp3  :: 0hBF77A291236668E4
+	pp4  :: 0hBEF8EAD6120016AC
+	qq1  :: 0h3FD97779CDDADC09
+	qq2  :: 0h3FB0A54C5536CEBA
+	qq3  :: 0h3F74D022C4D36B0F
+	qq4  :: 0h3F215DC9221C1A10
+	qq5  :: 0hBED09C4342A26120
+	// Coefficients for approximation to  erf  in [0.84375, 1.25]
+	pa0 :: 0hBF6359B8BEF77538
+	pa1 :: 0h3FDA8D00AD92B34D
+	pa2 :: 0hBFD7D240FBB8C3F1
+	pa3 :: 0h3FD45FCA805120E4
+	pa4 :: 0hBFBC63983D3E28EC
+	pa5 :: 0h3FA22A36599795EB
+	pa6 :: 0hBF61BF380A96073F
+	qa1 :: 0h3FBB3E6618EEE323
+	qa2 :: 0h3FE14AF092EB6F33
+	qa3 :: 0h3FB2635CD99FE9A7
+	qa4 :: 0h3FC02660E763351F
+	qa5 :: 0h3F8BEDC26B51DD1C
+	qa6 :: 0h3F888B545735151D
+	// Coefficients for approximation to  erfc in [1.25, 1/0.35]
+	ra0 :: 0hBF843412600D6435
+	ra1 :: 0hBFE63416E4BA7360
+	ra2 :: 0hC0251E0441B0E726
+	ra3 :: 0hC04F300AE4CBA38D
+	ra4 :: 0hC0644CB184282266
+	ra5 :: 0hC067135CEBCCABB2
+	ra6 :: 0hC054526557E4D2F2
+	ra7 :: 0hC023A0EFC69AC25C
+	sa1 :: 0h4033A6B9BD707687
+	sa2 :: 0h4061350C526AE721
+	sa3 :: 0h407B290DD58A1A71
+	sa4 :: 0h40842B1921EC2868
+	sa5 :: 0h407AD02157700314
+	sa6 :: 0h405B28A3EE48AE2C
+	sa7 :: 0h401A47EF8E484A93
+	sa8 :: 0hBFAEEFF2EE749A62
+	// Coefficients for approximation to  erfc in [1/.35, 28]
+	rb0 :: 0hBF84341239E86F4A
+	rb1 :: 0hBFE993BA70C285DE
+	rb2 :: 0hC031C209555F995A
+	rb3 :: 0hC064145D43C5ED98
+	rb4 :: 0hC083EC881375F228
+	rb5 :: 0hC09004616A2E5992
+	rb6 :: 0hC07E384E9BDC383F
+	sb1 :: 0h403E568B261D5190
+	sb2 :: 0h40745CAE221B9F0A
+	sb3 :: 0h409802EB189D5118
+	sb4 :: 0h40A8FFB7688C246A
+	sb5 :: 0h40A3F219CEDF3BE6
+	sb6 :: 0h407DA874E79FE763
+	sb7 :: 0hC03670E242712D62
+	
+	TINY :: 1.0 / (1 << 56) // 2**-56
+	// special cases
+	switch {
+	case is_nan(x):
+		return nan_f64()
+	case is_inf(x, 1):
+		return 0
+	case is_inf(x, -1):
+		return 2
+	}
+	x := x
+	sign := false
+	if x < 0 {
+		x = -x
+		sign = true
+	}
+	if x < 0.84375 { // |x| < 0.84375
+		temp: f64
+		if x < TINY { // |x| < 2**-56
+			temp = x
+		} else {
+			z := x * x
+			r := pp0 + z*(pp1+z*(pp2+z*(pp3+z*pp4)))
+			s := 1 + z*(qq1+z*(qq2+z*(qq3+z*(qq4+z*qq5))))
+			y := r / s
+			if x < 0.25 { // |x| < 1/4
+				temp = x + x*y
+			} else {
+				temp = 0.5 + (x*y + (x - 0.5))
+			}
+		}
+		if sign {
+			return 1 + temp
+		}
+		return 1 - temp
+	}
+	if x < 1.25 { // 0.84375 <= |x| < 1.25
+		s := x - 1
+		P := pa0 + s*(pa1+s*(pa2+s*(pa3+s*(pa4+s*(pa5+s*pa6)))))
+		Q := 1 + s*(qa1+s*(qa2+s*(qa3+s*(qa4+s*(qa5+s*qa6)))))
+		if sign {
+			return 1 + erx + P/Q
+		}
+		return 1 - erx - P/Q
+
+	}
+	if x < 28 { // |x| < 28
+		s := 1 / (x * x)
+		R, S: f64
+		if x < 1/0.35 { // |x| < 1 / 0.35 ~ 2.857143
+			R = ra0 + s*(ra1+s*(ra2+s*(ra3+s*(ra4+s*(ra5+s*(ra6+s*ra7))))))
+			S = 1 + s*(sa1+s*(sa2+s*(sa3+s*(sa4+s*(sa5+s*(sa6+s*(sa7+s*sa8)))))))
+		} else { // |x| >= 1 / 0.35 ~ 2.857143
+			if sign && x > 6 {
+				return 2 // x < -6
+			}
+			R = rb0 + s*(rb1+s*(rb2+s*(rb3+s*(rb4+s*(rb5+s*rb6)))))
+			S = 1 + s*(sb1+s*(sb2+s*(sb3+s*(sb4+s*(sb5+s*(sb6+s*sb7))))))
+		}
+		z := transmute(f64)(0xffffffff00000000 & transmute(u64)x) // pseudo-single (20-bit) precision x
+		r := exp(-z*z-0.5625) * exp((z-x)*(z+x)+R/S)
+		if sign {
+			return 2 - r/x
+		}
+		return r / x
+	}
+	if sign {
+		return 2
+	}
+	return 0
+}

+ 226 - 0
core/math/math_gamma.odin

@@ -0,0 +1,226 @@
+package math
+
+// The original C code, the long comment, and the constants
+// below are from http://netlib.sandia.gov/cephes/cprob/gamma.c.
+//
+//      tgamma.c
+//
+//      Gamma function
+//
+// SYNOPSIS:
+//
+// double x, y, tgamma();
+// extern int signgam;
+//
+// y = tgamma( x );
+//
+// DESCRIPTION:
+//
+// Returns gamma function of the argument. The result is
+// correctly signed, and the sign (+1 or -1) is also
+// returned in a global (extern) variable named signgam.
+// This variable is also filled in by the logarithmic gamma
+// function lgamma().
+//
+// Arguments |x| <= 34 are reduced by recurrence and the function
+// approximated by a rational function of degree 6/7 in the
+// interval (2,3).  Large arguments are handled by Stirling's
+// formula. Large negative arguments are made positive using
+// a reflection formula.
+//
+// ACCURACY:
+//
+//                      Relative error:
+// arithmetic   domain     # trials      peak         rms
+//    DEC      -34, 34      10000       1.3e-16     2.5e-17
+//    IEEE    -170,-33      20000       2.3e-15     3.3e-16
+//    IEEE     -33,  33     20000       9.4e-16     2.2e-16
+//    IEEE      33, 171.6   20000       2.3e-15     3.2e-16
+//
+// Error for arguments outside the test range will be larger
+// owing to error amplification by the exponential function.
+//
+// Cephes Math Library Release 2.8:  June, 2000
+// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier
+//
+// The readme file at http://netlib.sandia.gov/cephes/ says:
+//    Some software in this archive may be from the book _Methods and
+// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster
+// International, 1989) or from the Cephes Mathematical Library, a
+// commercial product. In either event, it is copyrighted by the author.
+// What you see here may be used freely but it comes with no support or
+// guarantee.
+//
+//   The two known misprints in the book are repaired here in the
+// source listings for the gamma function and the incomplete beta
+// integral.
+//
+//   Stephen L. Moshier
+//   [email protected]
+
+// Gamma function computed by Stirling's formula.
+// The pair of results must be multiplied together to get the actual answer.
+// The multiplication is left to the caller so that, if careful, the caller can avoid
+// infinity for 172 <= x <= 180.
+// The polynomial is valid for 33 <= x <= 172; larger values are only used
+// in reciprocal and produce denormalized floats. The lower precision there
+// masks any imprecision in the polynomial.
+@(private="file")
+stirling :: proc "contextless" (x: f64) -> (f64, f64) {
+	@(static) gamS := [?]f64{
+		7.87311395793093628397e-04,
+		-2.29549961613378126380e-04,
+		-2.68132617805781232825e-03,
+		3.47222221605458667310e-03,
+		8.33333333333482257126e-02,
+	}
+	
+	if x > 200 {
+		return inf_f64(1), 1
+	}
+	SQRT_TWO_PI :: 2.506628274631000502417
+	MAX_STIRLING :: 143.01608
+	w := 1 / x
+	w = 1 + w*((((gamS[0]*w+gamS[1])*w+gamS[2])*w+gamS[3])*w+gamS[4])
+	y1 := exp(x)
+	y2 := 1.0
+	if x > MAX_STIRLING { // avoid pow() overflow
+		v := pow(x, 0.5*x-0.25)
+		y1, y2 = v, v/y1
+	} else {
+		y1 = pow(x, x-0.5) / y1
+	}
+	return y1, SQRT_TWO_PI * w * y2
+}
+
+gamma_f64 :: proc "contextless" (x: f64) -> f64 {
+	is_neg_int :: proc "contextless" (x: f64) -> bool {
+		if x < 0 {
+			_, xf := modf(x)
+			return xf == 0
+		}
+		return false
+	}
+	
+	@(static) gamP := [?]f64{
+		1.60119522476751861407e-04,
+		1.19135147006586384913e-03,
+		1.04213797561761569935e-02,
+		4.76367800457137231464e-02,
+		2.07448227648435975150e-01,
+		4.94214826801497100753e-01,
+		9.99999999999999996796e-01,
+	}
+	@(static) gamQ := [?]f64{
+		-2.31581873324120129819e-05,
+		5.39605580493303397842e-04,
+		-4.45641913851797240494e-03,
+		1.18139785222060435552e-02,
+		3.58236398605498653373e-02,
+		-2.34591795718243348568e-01,
+		7.14304917030273074085e-02,
+		1.00000000000000000320e+00,
+	}
+
+	
+	EULER :: 0.57721566490153286060651209008240243104215933593992 // A001620
+	
+	switch {
+	case is_neg_int(x) || is_inf(x, -1) || is_nan(x):
+		return nan_f64()
+	case is_inf(x, 1):
+		return inf_f64(1)
+	case x == 0:
+		if signbit(x) {
+			return inf_f64(-1)
+		}
+		return inf_f64(1)
+	}
+	
+	x := x
+	q := abs(x)
+	p := floor(q)
+	if q > 33 {
+		if x >= 0 {
+			y1, y2 := stirling(x)
+			return y1 * y2
+		}
+		// Note: x is negative but (checked above) not a negative integer,
+		// so x must be small enough to be in range for conversion to i64.
+		// If |x| were >= 2⁶³ it would have to be an integer.
+		signgam := 1
+		if ip := i64(p); ip&1 == 0 {
+			signgam = -1
+		}
+		z := q - p
+		if z > 0.5 {
+			p = p + 1
+			z = q - p
+		}
+		z = q * sin(PI*z)
+		if z == 0 {
+			return inf_f64(signgam)
+		}
+		sq1, sq2 := stirling(q)
+		absz := abs(z)
+		d := absz * sq1 * sq2
+		if is_inf(d, 0) {
+			z = PI / absz / sq1 / sq2
+		} else {
+			z = PI / d
+		}
+		return f64(signgam) * z
+	}
+
+	// Reduce argument
+	z := 1.0
+	for x >= 3 {
+		x = x - 1
+		z = z * x
+	}
+	for x < 0 {
+		if x > -1e-09 {
+			if x == 0 {
+				return inf_f64(1)
+			}
+			return z / ((1 + EULER*x) * x)
+		}
+		z = z / x
+		x = x + 1
+	}
+	for x < 2 {
+		if x < 1e-09 {
+			if x == 0 {
+				return inf_f64(1)
+			}
+			return z / ((1 + EULER*x) * x)
+		}
+		z = z / x
+		x = x + 1
+	}
+
+	if x == 2 {
+		return z
+	}
+
+	x = x - 2
+	p = (((((x*gamP[0]+gamP[1])*x+gamP[2])*x+gamP[3])*x+gamP[4])*x+gamP[5])*x + gamP[6]
+	q = ((((((x*gamQ[0]+gamQ[1])*x+gamQ[2])*x+gamQ[3])*x+gamQ[4])*x+gamQ[5])*x+gamQ[6])*x + gamQ[7]
+	return z * p / q
+}
+
+
+gamma_f16   :: proc "contextless" (x: f16)   -> f16   { return f16(gamma_f64(f64(x))) }
+gamma_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(gamma_f64(f64(x))) }
+gamma_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(gamma_f64(f64(x))) }
+gamma_f32   :: proc "contextless" (x: f32)   -> f32   { return f32(gamma_f64(f64(x))) }
+gamma_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(gamma_f64(f64(x))) }
+gamma_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(gamma_f64(f64(x))) }
+gamma_f64le :: proc "contextless" (x: f64le) -> f64le { return f64le(gamma_f64(f64(x))) }
+gamma_f64be :: proc "contextless" (x: f64be) -> f64be { return f64be(gamma_f64(f64(x))) }
+
+gamma :: proc{
+	gamma_f16, gamma_f16le, gamma_f16be,
+	gamma_f32, gamma_f32le, gamma_f32be,
+	gamma_f64, gamma_f64le, gamma_f64be,
+}

+ 361 - 0
core/math/math_lgamma.odin

@@ -0,0 +1,361 @@
+package math
+
+// The original C code and the long comment below are
+// from FreeBSD's /usr/src/lib/msun/src/e_lgamma_r.c and
+// came with this notice. 
+//
+// ====================================================
+// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+//
+// Developed at SunPro, a Sun Microsystems, Inc. business.
+// Permission to use, copy, modify, and distribute this
+// software is freely granted, provided that this notice
+// is preserved.
+// ====================================================
+//
+// __ieee754_lgamma_r(x, signgamp)
+// Reentrant version of the logarithm of the Gamma function
+// with user provided pointer for the sign of Gamma(x).
+//
+// Method:
+//   1. Argument Reduction for 0 < x <= 8
+//      Since gamma(1+s)=s*gamma(s), for x in [0,8], we may
+//      reduce x to a number in [1.5,2.5] by
+//              lgamma(1+s) = log(s) + lgamma(s)
+//      for example,
+//              lgamma(7.3) = log(6.3) + lgamma(6.3)
+//                          = log(6.3*5.3) + lgamma(5.3)
+//                          = log(6.3*5.3*4.3*3.3*2.3) + lgamma(2.3)
+//   2. Polynomial approximation of lgamma around its
+//      minimum (ymin=1.461632144968362245) to maintain monotonicity.
+//      On [ymin-0.23, ymin+0.27] (i.e., [1.23164,1.73163]), use
+//              Let z = x-ymin;
+//              lgamma(x) = -1.214862905358496078218 + z**2*poly(z)
+//              poly(z) is a 14 degree polynomial.
+//   2. Rational approximation in the primary interval [2,3]
+//      We use the following approximation:
+//              s = x-2.0;
+//              lgamma(x) = 0.5*s + s*P(s)/Q(s)
+//      with accuracy
+//              |P/Q - (lgamma(x)-0.5s)| < 2**-61.71
+//      Our algorithms are based on the following observation
+//
+//                             zeta(2)-1    2    zeta(3)-1    3
+// lgamma(2+s) = s*(1-Euler) + --------- * s  -  --------- * s  + ...
+//                                 2                 3
+//
+//      where Euler = 0.5772156649... is the Euler constant, which
+//      is very close to 0.5.
+//
+//   3. For x>=8, we have
+//      lgamma(x)~(x-0.5)log(x)-x+0.5*log(2pi)+1/(12x)-1/(360x**3)+....
+//      (better formula:
+//         lgamma(x)~(x-0.5)*(log(x)-1)-.5*(log(2pi)-1) + ...)
+//      Let z = 1/x, then we approximation
+//              f(z) = lgamma(x) - (x-0.5)(log(x)-1)
+//      by
+//                                  3       5             11
+//              w = w0 + w1*z + w2*z  + w3*z  + ... + w6*z
+//      where
+//              |w - f(z)| < 2**-58.74
+//
+//   4. For negative x, since (G is gamma function)
+//              -x*G(-x)*G(x) = pi/sin(pi*x),
+//      we have
+//              G(x) = pi/(sin(pi*x)*(-x)*G(-x))
+//      since G(-x) is positive, sign(G(x)) = sign(sin(pi*x)) for x<0
+//      Hence, for x<0, signgam = sign(sin(pi*x)) and
+//              lgamma(x) = log(|Gamma(x)|)
+//                        = log(pi/(|x*sin(pi*x)|)) - lgamma(-x);
+//      Note: one should avoid computing pi*(-x) directly in the
+//            computation of sin(pi*(-x)).
+//
+//   5. Special Cases
+//              lgamma(2+s) ~ s*(1-Euler) for tiny s
+//              lgamma(1)=lgamma(2)=0
+//              lgamma(x) ~ -log(x) for tiny x
+//              lgamma(0) = lgamma(inf) = inf
+//              lgamma(-integer) = +-inf
+//
+//
+
+
+lgamma_f64 :: proc "contextless" (x: f64) -> (lgamma: f64, sign: int) {
+	sin_pi :: proc "contextless" (x: f64) -> f64 {
+		if x < 0.25 {
+			return -sin(PI * x)
+		}
+		x := x
+
+		// argument reduction
+		z := floor(x)
+		n: int
+		if z != x { // inexact
+			x = mod(x, 2)
+			n = int(x * 4)
+		} else {
+			if x >= TWO_53 { // x must be even
+				x = 0
+				n = 0
+			} else {
+				if x < TWO_52 {
+					z = x + TWO_52 // exact
+				}
+				n = int(1 & transmute(u64)z)
+				x = f64(n)
+				n <<= 2
+			}
+		}
+		switch n {
+		case 0:
+			x = sin(PI * x)
+		case 1, 2:
+			x = cos(PI * (0.5 - x))
+		case 3, 4:
+			x = sin(PI * (1 - x))
+		case 5, 6:
+			x = -cos(PI * (x - 1.5))
+		case:
+			x = sin(PI * (x - 2))
+		}
+		return -x
+	}
+	
+	@static lgamA := [?]f64{
+		0h3FB3C467E37DB0C8,
+		0h3FD4A34CC4A60FAD,
+		0h3FB13E001A5562A7,
+		0h3F951322AC92547B,
+		0h3F7E404FB68FEFE8,
+		0h3F67ADD8CCB7926B,
+		0h3F538A94116F3F5D,
+		0h3F40B6C689B99C00,
+		0h3F2CF2ECED10E54D,
+		0h3F1C5088987DFB07,
+		0h3EFA7074428CFA52,
+		0h3F07858E90A45837,
+	}
+	@static lgamR := [?]f64{
+		1.0,
+		0h3FF645A762C4AB74,
+		0h3FE71A1893D3DCDC,
+		0h3FC601EDCCFBDF27,
+		0h3F9317EA742ED475,
+		0h3F497DDACA41A95B,
+		0h3EDEBAF7A5B38140,
+	}
+	@static lgamS := [?]f64{
+		0hBFB3C467E37DB0C8,
+		0h3FCB848B36E20878,
+		0h3FD4D98F4F139F59,
+		0h3FC2BB9CBEE5F2F7,
+		0h3F9B481C7E939961,
+		0h3F5E26B67368F239,
+		0h3F00BFECDD17E945,
+	}
+	@static lgamT := [?]f64{
+		0h3FDEF72BC8EE38A2,
+		0hBFC2E4278DC6C509,
+		0h3FB08B4294D5419B,
+		0hBFA0C9A8DF35B713,
+		0h3F9266E7970AF9EC,
+		0hBF851F9FBA91EC6A,
+		0h3F78FCE0E370E344,
+		0hBF6E2EFFB3E914D7,
+		0h3F6282D32E15C915,
+		0hBF56FE8EBF2D1AF1,
+		0h3F4CDF0CEF61A8E9,
+		0hBF41A6109C73E0EC,
+		0h3F34AF6D6C0EBBF7,
+		0hBF347F24ECC38C38,
+		0h3F35FD3EE8C2D3F4,
+	}
+	@static lgamU := [?]f64{
+		0hBFB3C467E37DB0C8,
+		0h3FE4401E8B005DFF,
+		0h3FF7475CD119BD6F,
+		0h3FEF497644EA8450,
+		0h3FCD4EAEF6010924,
+		0h3F8B678BBF2BAB09,
+	}
+	@static lgamV := [?]f64{
+		1.0,
+		0h4003A5D7C2BD619C,
+		0h40010725A42B18F5,
+		0h3FE89DFBE45050AF,
+		0h3FBAAE55D6537C88,
+		0h3F6A5ABB57D0CF61,
+	}
+	@static lgamW := [?]f64{
+		0h3FDACFE390C97D69,
+		0h3FB555555555553B,
+		0hBF66C16C16B02E5C,
+		0h3F4A019F98CF38B6,
+		0hBF4380CB8C0FE741,
+		0h3F4B67BA4CDAD5D1,
+		0hBF5AB89D0B9E43E4,
+	}
+
+	
+	Y_MIN  :: 1.461632144968362245
+	TWO_52 :: 0h4330000000000000 // ~4.5036e+15
+	TWO_53  :: 0h4340000000000000 // ~9.0072e+15
+	TWO_58 :: 0h4390000000000000 // ~2.8823e+17
+	TINY   :: 0h3b90000000000000 // ~8.47033e-22
+	Tc     :: 0h3FF762D86356BE3F
+	Tf     :: 0hBFBF19B9BCC38A42
+	Tt     :: 0hBC50C7CAA48A971F
+	
+	// special cases
+	sign = 1
+	switch {
+	case is_nan(x):
+		lgamma = x
+		return
+	case is_inf(x):
+		lgamma = x
+		return
+	case x == 0:
+		lgamma = inf_f64(1)
+		return
+	}
+
+	x := x
+	neg := false
+	if x < 0 {
+		x = -x
+		neg = true
+	}
+
+	if x < TINY { // if |x| < 2**-70, return -log(|x|)
+		if neg {
+			sign = -1
+		}
+		lgamma = -ln(x)
+		return
+	}
+	nadj: f64
+	if neg {
+		if x >= TWO_52 { // |x| >= 2**52, must be -integer
+			lgamma = inf_f64(1)
+			return
+		}
+		t := sin_pi(x)
+		if t == 0 {
+			lgamma = inf_f64(1) // -integer
+			return
+		}
+		nadj = ln(PI / abs(t*x))
+		if t < 0 {
+			sign = -1
+		}
+	}
+
+	switch {
+	case x == 1 || x == 2: // purge off 1 and 2
+		lgamma = 0
+		return
+	case x < 2: // use lgamma(x) = lgamma(x+1) - log(x)
+		y: f64
+		i: int
+		if x <= 0.9 {
+			lgamma = -ln(x)
+			switch {
+			case x >= (Y_MIN - 1 + 0.27): // 0.7316 <= x <=  0.9
+				y = 1 - x
+				i = 0
+			case x >= (Y_MIN - 1 - 0.27): // 0.2316 <= x < 0.7316
+				y = x - (Tc - 1)
+				i = 1
+			case: // 0 < x < 0.2316
+				y = x
+				i = 2
+			}
+		} else {
+			lgamma = 0
+			switch {
+			case x >= (Y_MIN + 0.27): // 1.7316 <= x < 2
+				y = 2 - x
+				i = 0
+			case x >= (Y_MIN - 0.27): // 1.2316 <= x < 1.7316
+				y = x - Tc
+				i = 1
+			case: // 0.9 < x < 1.2316
+				y = x - 1
+				i = 2
+			}
+		}
+		switch i {
+		case 0:
+			z := y * y
+			p1 := lgamA[0] + z*(lgamA[2]+z*(lgamA[4]+z*(lgamA[6]+z*(lgamA[8]+z*lgamA[10]))))
+			p2 := z * (lgamA[1] + z*(+lgamA[3]+z*(lgamA[5]+z*(lgamA[7]+z*(lgamA[9]+z*lgamA[11])))))
+			p := y*p1 + p2
+			lgamma += (p - 0.5*y)
+		case 1:
+			z := y * y
+			w := z * y
+			p1 := lgamT[0] + w*(lgamT[3]+w*(lgamT[6]+w*(lgamT[9]+w*lgamT[12]))) // parallel comp
+			p2 := lgamT[1] + w*(lgamT[4]+w*(lgamT[7]+w*(lgamT[10]+w*lgamT[13])))
+			p3 := lgamT[2] + w*(lgamT[5]+w*(lgamT[8]+w*(lgamT[11]+w*lgamT[14])))
+			p := z*p1 - (Tt - w*(p2+y*p3))
+			lgamma += (Tf + p)
+		case 2:
+			p1 := y * (lgamU[0] + y*(lgamU[1]+y*(lgamU[2]+y*(lgamU[3]+y*(lgamU[4]+y*lgamU[5])))))
+			p2 := 1 + y*(lgamV[1]+y*(lgamV[2]+y*(lgamV[3]+y*(lgamV[4]+y*lgamV[5]))))
+			lgamma += (-0.5*y + p1/p2)
+		}
+	case x < 8: // 2 <= x < 8
+		i := int(x)
+		y := x - f64(i)
+		p := y * (lgamS[0] + y*(lgamS[1]+y*(lgamS[2]+y*(lgamS[3]+y*(lgamS[4]+y*(lgamS[5]+y*lgamS[6]))))))
+		q := 1 + y*(lgamR[1]+y*(lgamR[2]+y*(lgamR[3]+y*(lgamR[4]+y*(lgamR[5]+y*lgamR[6])))))
+		lgamma = 0.5*y + p/q
+		z := 1.0 // lgamma(1+s) = ln(s) + lgamma(s)
+		switch i {
+		case 7:
+			z *= (y + 6)
+			fallthrough
+		case 6:
+			z *= (y + 5)
+			fallthrough
+		case 5:
+			z *= (y + 4)
+			fallthrough
+		case 4:
+			z *= (y + 3)
+			fallthrough
+		case 3:
+			z *= (y + 2)
+			lgamma += ln(z)
+		}
+	case x < TWO_58: // 8 <= x < 2**58
+		t := ln(x)
+		z := 1 / x
+		y := z * z
+		w := lgamW[0] + z*(lgamW[1]+y*(lgamW[2]+y*(lgamW[3]+y*(lgamW[4]+y*(lgamW[5]+y*lgamW[6])))))
+		lgamma = (x-0.5)*(t-1) + w
+	case: // 2**58 <= x <= Inf
+		lgamma = x * (ln(x) - 1)
+	}
+	if neg {
+		lgamma = nadj - lgamma
+	}
+	return
+}
+
+
+lgamma_f16 :: proc "contextless" (x: f16) -> (lgamma: f16, sign: int) { r, s := lgamma_f64(f64(x)); return f16(r), s }
+lgamma_f32 :: proc "contextless" (x: f32) -> (lgamma: f32, sign: int) { r, s := lgamma_f64(f64(x)); return f32(r), s }
+lgamma_f16le :: proc "contextless" (x: f16le) -> (lgamma: f16le, sign: int) { r, s := lgamma_f64(f64(x)); return f16le(r), s }
+lgamma_f16be :: proc "contextless" (x: f16be) -> (lgamma: f16be, sign: int) { r, s := lgamma_f64(f64(x)); return f16be(r), s }
+lgamma_f32le :: proc "contextless" (x: f32le) -> (lgamma: f32le, sign: int) { r, s := lgamma_f64(f64(x)); return f32le(r), s }
+lgamma_f32be :: proc "contextless" (x: f32be) -> (lgamma: f32be, sign: int) { r, s := lgamma_f64(f64(x)); return f32be(r), s }
+lgamma_f64le :: proc "contextless" (x: f64le) -> (lgamma: f64le, sign: int) { r, s := lgamma_f64(f64(x)); return f64le(r), s }
+lgamma_f64be :: proc "contextless" (x: f64be) -> (lgamma: f64be, sign: int) { r, s := lgamma_f64(f64(x)); return f64be(r), s }
+
+lgamma :: proc{
+	lgamma_f16, lgamma_f16le, lgamma_f16be,
+	lgamma_f32, lgamma_f32le, lgamma_f32be,
+	lgamma_f64, lgamma_f64le, lgamma_f64be,
+}

+ 198 - 0
core/math/math_log1p.odin

@@ -0,0 +1,198 @@
+package math
+
+// The original C code, the long comment, and the constants
+// below are from FreeBSD's /usr/src/lib/msun/src/s_log1p.c
+// and came with this notice. The go code is a simplified
+// version of the original C.
+//
+// ====================================================
+// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+//
+// Developed at SunPro, a Sun Microsystems, Inc. business.
+// Permission to use, copy, modify, and distribute this
+// software is freely granted, provided that this notice
+// is preserved.
+// ====================================================
+//
+//
+// double log1p(double x)
+//
+// Method :
+//   1. Argument Reduction: find k and f such that
+//                      1+x = 2**k * (1+f),
+//         where  sqrt(2)/2 < 1+f < sqrt(2) .
+//
+//      Note. If k=0, then f=x is exact. However, if k!=0, then f
+//      may not be representable exactly. In that case, a correction
+//      term is need. Let u=1+x rounded. Let c = (1+x)-u, then
+//      log(1+x) - log(u) ~ c/u. Thus, we proceed to compute log(u),
+//      and add back the correction term c/u.
+//      (Note: when x > 2**53, one can simply return log(x))
+//
+//   2. Approximation of log1p(f).
+//      Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s)
+//               = 2s + 2/3 s**3 + 2/5 s**5 + .....,
+//               = 2s + s*R
+//      We use a special Reme algorithm on [0,0.1716] to generate
+//      a polynomial of degree 14 to approximate R The maximum error
+//      of this polynomial approximation is bounded by 2**-58.45. In
+//      other words,
+//                      2      4      6      8      10      12      14
+//          R(z) ~ Lp1*s +Lp2*s +Lp3*s +Lp4*s +Lp5*s  +Lp6*s  +Lp7*s
+//      (the values of Lp1 to Lp7 are listed in the program)
+//      and
+//          |      2          14          |     -58.45
+//          | Lp1*s +...+Lp7*s    -  R(z) | <= 2
+//          |                             |
+//      Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2.
+//      In order to guarantee error in log below 1ulp, we compute log
+//      by
+//              log1p(f) = f - (hfsq - s*(hfsq+R)).
+//
+//   3. Finally, log1p(x) = k*ln2 + log1p(f).
+//                        = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo)))
+//      Here ln2 is split into two floating point number:
+//                   ln2_hi + ln2_lo,
+//      where n*ln2_hi is always exact for |n| < 2000.
+//
+// Special cases:
+//      log1p(x) is NaN with signal if x < -1 (including -INF) ;
+//      log1p(+INF) is +INF; log1p(-1) is -INF with signal;
+//      log1p(NaN) is that NaN with no signal.
+//
+// Accuracy:
+//      according to an error analysis, the error is always less than
+//      1 ulp (unit in the last place).
+//
+// Constants:
+// The hexadecimal values are the intended ones for the following
+// constants. The decimal values may be used, provided that the
+// compiler will convert from decimal to binary accurately enough
+// to produce the hexadecimal values shown.
+//
+// Note: Assuming log() return accurate answer, the following
+//       algorithm can be used to compute log1p(x) to within a few ULP:
+//
+//              u = 1+x;
+//              if(u==1.0) return x ; else
+//                         return log(u)*(x/(u-1.0));
+//
+//       See HP-15C Advanced Functions Handbook, p.193.
+
+log1p :: proc {
+	log1p_f16,
+	log1p_f32,
+	log1p_f64,
+	log1p_f16le,
+	log1p_f16be,
+	log1p_f32le,
+	log1p_f32be,
+	log1p_f64le,
+	log1p_f64be,
+}
+log1p_f16   :: proc "contextless" (x: f16)   -> f16   { return f16(log1p_f64(f64(x))) }
+log1p_f32   :: proc "contextless" (x: f32)   -> f32   { return f32(log1p_f64(f64(x))) }
+log1p_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(log1p_f64(f64(x))) }
+log1p_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(log1p_f64(f64(x))) }
+log1p_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(log1p_f64(f64(x))) }
+log1p_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(log1p_f64(f64(x))) }
+log1p_f64le :: proc "contextless" (x: f64le) -> f64le { return f64le(log1p_f64(f64(x))) }
+log1p_f64be :: proc "contextless" (x: f64be) -> f64be { return f64be(log1p_f64(f64(x))) }
+
+log1p_f64 :: proc "contextless" (x: f64) -> f64 {
+	SQRT2_M1      :: 0h3fda827999fcef34 // Sqrt(2)-1 
+	SQRT2_HALF_M1 :: 0hbfd2bec333018866 // Sqrt(2)/2-1
+	SMALL         :: 0h3e20000000000000 // 2**-29
+	TINY          :: 1.0 / (1 << 54)    // 2**-54
+	TWO53         :: 1 << 53            // 2**53
+	LN2HI         :: 0h3fe62e42fee00000
+	LN2LO         :: 0h3dea39ef35793c76
+	LP1           :: 0h3FE5555555555593
+	LP2           :: 0h3FD999999997FA04
+	LP3           :: 0h3FD2492494229359
+	LP4           :: 0h3FCC71C51D8E78AF
+	LP5           :: 0h3FC7466496CB03DE
+	LP6           :: 0h3FC39A09D078C69F
+	LP7           :: 0h3FC2F112DF3E5244
+	
+	switch {
+	case x < -1 || is_nan(x):
+		return nan_f64()
+	case x == -1:
+		return inf_f64(-1)
+	case is_inf(x, 1):
+		return inf_f64(+1)
+	}
+	absx := abs(x)
+	
+	f: f64
+	iu: u64
+	k := 1
+	if absx < SQRT2_M1 { //  |x| < Sqrt(2)-1
+		if absx < SMALL { // |x| < 2**-29
+			if absx < TINY { // |x| < 2**-54
+				return x
+			}
+			return x - x*x*0.5
+		}
+		if x > SQRT2_HALF_M1 { // Sqrt(2)/2-1 < x
+			// (Sqrt(2)/2-1) < x < (Sqrt(2)-1)
+			k = 0
+			f = x
+			iu = 1
+		}
+	}
+	c: f64
+	if k != 0 {
+		u: f64
+		if absx < TWO53 { // 1<<53
+			u = 1.0 + x
+			iu = transmute(u64)u
+			k = int((iu >> 52) - 1023)
+			// correction term
+			if k > 0 {
+				c = 1.0 - (u - x)
+			} else {
+				c = x - (u - 1.0)
+			}
+			c /= u
+		} else {
+			u = x
+			iu = transmute(u64)u
+			k = int((iu >> 52) - 1023)
+			c = 0
+		}
+		iu &= 0x000fffffffffffff
+		if iu < 0x0006a09e667f3bcd { // mantissa of Sqrt(2)
+			u = transmute(f64)(iu | 0x3ff0000000000000) // normalize u
+		} else {
+			k += 1
+			u = transmute(f64)(iu | 0x3fe0000000000000) // normalize u/2
+			iu = (0x0010000000000000 - iu) >> 2
+		}
+		f = u - 1.0 // Sqrt(2)/2 < u < Sqrt(2)
+	}
+	hfsq := 0.5 * f * f
+	s, R, z: f64
+	if iu == 0 { // |f| < 2**-20
+		if f == 0 {
+			if k == 0 {
+				return 0
+			}
+			c += f64(k) * LN2LO
+			return f64(k)*LN2HI + c
+		}
+		R = hfsq * (1.0 - 0.66666666666666666*f) // avoid division
+		if k == 0 {
+			return f - R
+		}
+		return f64(k)*LN2HI - ((R - (f64(k)*LN2LO + c)) - f)
+	}
+	s = f / (2.0 + f)
+	z = s * s
+	R = z * (LP1 + z*(LP2+z*(LP3+z*(LP4+z*(LP5+z*(LP6+z*LP7))))))
+	if k == 0 {
+		return f - (hfsq - s*(hfsq+R))
+	}
+	return f64(k)*LN2HI - ((hfsq - (s*(hfsq+R) + (f64(k)*LN2LO + c))) - f)
+}

+ 1 - 0
core/odin/ast/ast.odin

@@ -579,6 +579,7 @@ Field_Flags_Signature :: Field_Flags{
 	.No_Alias,
 	.C_Vararg,
 	.Auto_Cast,
+	.Any_Int,
 	.Default_Parameters,
 }
 

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

+ 4 - 0
core/os/os_freestanding.odin

@@ -0,0 +1,4 @@
+//+freestanding
+package os
+
+#panic("package os does not support a freestanding target")

+ 4 - 0
core/os/os_js.odin

@@ -0,0 +1,4 @@
+//+js
+package os
+
+#panic("package os does not support a js target")

+ 10 - 6
core/os/stat_windows.odin

@@ -115,12 +115,16 @@ cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 {
 	}
 	buf = buf[:N]
 
-	if len(buf) >= 4 {
-		if buf[0] == '\\' &&
-		   buf[1] == '\\' &&
-		   buf[2] == '?'  &&
-		   buf[3] == '\\' {
-			buf = buf[4:]
+	if len(buf) >= 4 && buf[0] == '\\' && buf[1] == '\\' && buf[2] == '?' && buf[3] == '\\' {
+		buf = buf[4:]
+
+		/*
+			NOTE(Jeroen): Properly handle UNC paths.
+			We need to turn `\\?\UNC\synology.local` into `\\synology.local`.
+		*/
+		if len(buf) >= 3 && buf[0] == 'U' && buf[1] == 'N' && buf[2] == 'C' {
+			buf = buf[2:]
+			buf[0] = '\\'
 		}
 	}
 	return buf

+ 12 - 9
core/runtime/core.odin

@@ -516,14 +516,17 @@ __init_context :: proc "contextless" (c: ^Context) {
 }
 
 default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code_Location) -> ! {
-	print_caller_location(loc)
-	print_string(" ")
-	print_string(prefix)
-	if len(message) > 0 {
-		print_string(": ")
-		print_string(message)
+	when ODIN_OS == "freestanding" {
+		// Do nothing
+	} else {
+		print_caller_location(loc)
+		print_string(" ")
+		print_string(prefix)
+		if len(message) > 0 {
+			print_string(": ")
+			print_string(message)
+		}
+		print_byte('\n')
 	}
-	print_byte('\n')
-	// intrinsics.debug_trap();
-	intrinsics.trap()
+	trap()
 }

+ 1 - 0
core/runtime/default_allocators_general.odin

@@ -1,6 +1,7 @@
 //+build !windows
 //+build !freestanding
 //+build !wasi
+//+build !js
 package runtime
 
 when ODIN_DEFAULT_TO_NIL_ALLOCATOR {

+ 5 - 0
core/runtime/default_allocators_js.odin

@@ -0,0 +1,5 @@
+//+build js
+package runtime
+
+default_allocator_proc :: nil_allocator_proc
+default_allocator :: nil_allocator

+ 1 - 1
core/runtime/default_temporary_allocator.odin

@@ -3,7 +3,7 @@ package runtime
 DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE: int : #config(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, 1<<22)
 
 
-when ODIN_OS == "freestanding" || ODIN_DEFAULT_TO_NIL_ALLOCATOR {
+when ODIN_OS == "freestanding" || ODIN_OS == "js" || ODIN_DEFAULT_TO_NIL_ALLOCATOR {
 	Default_Temp_Allocator :: struct {}
 	
 	default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backup_allocator := context.allocator) {}

+ 24 - 17
core/runtime/internal.odin

@@ -3,7 +3,14 @@ package runtime
 import "core:intrinsics"
 
 @(private)
-RUNTIME_LINKAGE :: "strong" when (ODIN_USE_SEPARATE_MODULES || ODIN_BUILD_MODE == "dynamic" || !ODIN_NO_CRT) else "internal"
+RUNTIME_LINKAGE :: "strong" when (
+	(ODIN_USE_SEPARATE_MODULES || 
+	ODIN_BUILD_MODE == "dynamic" || 
+	!ODIN_NO_CRT) &&
+	!(ODIN_ARCH == "wasm32" || 
+	  ODIN_ARCH == "wasm64")) else "internal"
+RUNTIME_REQUIRE :: true
+
 
 @(private)
 byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte #no_bounds_check {
@@ -649,7 +656,7 @@ quo_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
 	return quaternion(t0, t1, t2, t3)
 }
 
-@(link_name="__truncsfhf2", linkage=RUNTIME_LINKAGE, require)
+@(link_name="__truncsfhf2", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
 truncsfhf2 :: proc "c" (value: f32) -> u16 {
 	v: struct #raw_union { i: u32, f: f32 }
 	i, s, e, m: i32
@@ -707,12 +714,12 @@ truncsfhf2 :: proc "c" (value: f32) -> u16 {
 }
 
 
-@(link_name="__truncdfhf2", linkage=RUNTIME_LINKAGE, require)
+@(link_name="__truncdfhf2", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
 truncdfhf2 :: proc "c" (value: f64) -> u16 {
 	return truncsfhf2(f32(value))
 }
 
-@(link_name="__gnu_h2f_ieee", linkage=RUNTIME_LINKAGE, require)
+@(link_name="__gnu_h2f_ieee", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
 gnu_h2f_ieee :: proc "c" (value: u16) -> f32 {
 	fp32 :: struct #raw_union { u: u32, f: f32 }
 
@@ -731,19 +738,19 @@ gnu_h2f_ieee :: proc "c" (value: u16) -> f32 {
 }
 
 
-@(link_name="__gnu_f2h_ieee", linkage=RUNTIME_LINKAGE, require)
+@(link_name="__gnu_f2h_ieee", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
 gnu_f2h_ieee :: proc "c" (value: f32) -> u16 {
 	return truncsfhf2(value)
 }
 
-@(link_name="__extendhfsf2", linkage=RUNTIME_LINKAGE, require)
+@(link_name="__extendhfsf2", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
 extendhfsf2 :: proc "c" (value: u16) -> f32 {
 	return gnu_h2f_ieee(value)
 }
 
 
 
-@(link_name="__floattidf", linkage=RUNTIME_LINKAGE, require)
+@(link_name="__floattidf", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
 floattidf :: proc "c" (a: i128) -> f64 {
 	DBL_MANT_DIG :: 53
 	if a == 0 {
@@ -786,7 +793,7 @@ floattidf :: proc "c" (a: i128) -> f64 {
 }
 
 
-@(link_name="__floattidf_unsigned", linkage=RUNTIME_LINKAGE, require)
+@(link_name="__floattidf_unsigned", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
 floattidf_unsigned :: proc "c" (a: u128) -> f64 {
 	DBL_MANT_DIG :: 53
 	if a == 0 {
@@ -828,14 +835,14 @@ floattidf_unsigned :: proc "c" (a: u128) -> f64 {
 
 
 
-@(link_name="__fixunsdfti", linkage=RUNTIME_LINKAGE, require)
+@(link_name="__fixunsdfti", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
 fixunsdfti :: #force_no_inline proc "c" (a: f64) -> u128 {
 	// TODO(bill): implement `fixunsdfti` correctly
 	x := u64(a)
 	return u128(x)
 }
 
-@(link_name="__fixunsdfdi", linkage=RUNTIME_LINKAGE, require)
+@(link_name="__fixunsdfdi", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
 fixunsdfdi :: #force_no_inline proc "c" (a: f64) -> i128 {
 	// TODO(bill): implement `fixunsdfdi` correctly
 	x := i64(a)
@@ -845,7 +852,7 @@ fixunsdfdi :: #force_no_inline proc "c" (a: f64) -> i128 {
 
 
 
-@(link_name="__umodti3", linkage=RUNTIME_LINKAGE, require)
+@(link_name="__umodti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
 umodti3 :: proc "c" (a, b: u128) -> u128 {
 	r: u128 = ---
 	_ = udivmod128(a, b, &r)
@@ -853,18 +860,18 @@ umodti3 :: proc "c" (a, b: u128) -> u128 {
 }
 
 
-@(link_name="__udivmodti4", linkage=RUNTIME_LINKAGE, require)
+@(link_name="__udivmodti4", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
 udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
 	return udivmod128(a, b, rem)
 }
 
-@(link_name="__udivti3", linkage=RUNTIME_LINKAGE, require)
+@(link_name="__udivti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
 udivti3 :: proc "c" (a, b: u128) -> u128 {
 	return udivmodti4(a, b, nil)
 }
 
 
-@(link_name="__modti3", linkage=RUNTIME_LINKAGE, require)
+@(link_name="__modti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
 modti3 :: proc "c" (a, b: i128) -> i128 {
 	s_a := a >> (128 - 1)
 	s_b := b >> (128 - 1)
@@ -877,20 +884,20 @@ modti3 :: proc "c" (a, b: i128) -> i128 {
 }
 
 
-@(link_name="__divmodti4", linkage=RUNTIME_LINKAGE, require)
+@(link_name="__divmodti4", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
 divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 {
 	u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem)
 	return transmute(i128)u
 }
 
-@(link_name="__divti3", linkage=RUNTIME_LINKAGE, require)
+@(link_name="__divti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
 divti3 :: proc "c" (a, b: i128) -> i128 {
 	u := udivmodti4(transmute(u128)a, transmute(u128)b, nil)
 	return transmute(i128)u
 }
 
 
-@(link_name="__fixdfti", linkage=RUNTIME_LINKAGE, require)
+@(link_name="__fixdfti", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
 fixdfti :: proc(a: u64) -> i128 {
 	significandBits :: 52
 	typeWidth       :: (size_of(u64)*8)

+ 1 - 1
core/runtime/os_specific_any.odin

@@ -1,4 +1,4 @@
-//+build !freestanding !wasi !windows
+//+build !freestanding !wasi !windows !js
 package runtime
 
 import "core:os"

+ 12 - 0
core/runtime/os_specific_js.odin

@@ -0,0 +1,12 @@
+//+build js
+package runtime
+
+foreign import "odin_env"
+
+_os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
+	foreign odin_env {
+		write :: proc "c" (fd: u32, p: []byte) ---
+	}
+	write(1, data)
+	return len(data), 0
+}

+ 0 - 2
core/runtime/procs.odin

@@ -42,7 +42,6 @@ when ODIN_NO_CRT && ODIN_OS == "windows" {
 	memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
 		if dst != src {
 			d, s := ([^]byte)(dst), ([^]byte)(src)
-			d_end, s_end := d[len:], s[len:]
 			for i := len-1; i >= 0; i -= 1 {
 				d[i] = s[i]
 			}
@@ -54,7 +53,6 @@ when ODIN_NO_CRT && ODIN_OS == "windows" {
 	memcpy :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
 		if dst != src {
 			d, s := ([^]byte)(dst), ([^]byte)(src)
-			d_end, s_end := d[len:], s[len:]
 			for i := len-1; i >= 0; i -= 1 {
 				d[i] = s[i]
 			}

+ 15 - 0
core/runtime/procs_js_wasm32.odin

@@ -0,0 +1,15 @@
+//+build js wasm32
+package runtime
+
+init_default_context_for_js: Context
+@(init, private="file")
+init_default_context :: proc() {
+	init_default_context_for_js = context
+}
+
+@(export)
+@(link_name="default_context_ptr")
+default_context_ptr :: proc "contextless" () -> ^Context {
+	return &init_default_context_for_js
+}
+

+ 18 - 3
core/runtime/procs_wasm32.odin

@@ -2,7 +2,22 @@
 package runtime
 
 @(link_name="__ashlti3", linkage="strong")
-__ashlti3 :: proc "c" (a: i64, b: i32) -> i64 {
-	// TODO(bill): __ashlti3 on wasm32
-	return a
+__ashlti3 :: proc "c" (a: i64, b_: i32) -> i64 {
+	/*
+	b := u32(b_)
+	input := transmute([2]i32)a
+	result: [2]i32
+	if b & 32 != 0 {
+		result[0] = 0
+		result[1] = input[0] << (b - 32)
+	} else {
+		if b == 0 {
+			return a
+		}
+		result[0] = input[0]<<b
+		result[1] = (input[1]<<b) | (input[0]>>(32-b))
+	}
+	return transmute(i64)result
+	*/
+	return 0
 }

+ 2 - 2
core/runtime/udivmod128.odin

@@ -8,7 +8,7 @@ udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
 
 	n := transmute([2]u64)a
 	d := transmute([2]u64)b
-	q, r: [2]u64 = ---, ---
+	q, r: [2]u64
 	sr: u32 = 0
 
 	low  :: 1 when ODIN_ENDIAN == "big" else 0
@@ -132,7 +132,7 @@ udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
 	}
 
 	carry: u32 = 0
-	r_all: u128 = ---
+	r_all: u128
 
 	for ; sr > 0; sr -= 1 {
 		r[high] = (r[high] << 1) | (r[low]  >> (U64_BITS - 1))

+ 8 - 3
core/sys/windows/ws2_32.odin

@@ -39,6 +39,11 @@ foreign ws2_32 {
 		g: GROUP,
 		dwFlags: DWORD,
 	) -> SOCKET ---
+	socket :: proc(
+		af: c_int,
+		type: c_int,
+		protocol: c_int,
+	) -> SOCKET ---
 
 	ioctlsocket :: proc(s: SOCKET, cmd: c_long, argp: ^c_ulong) -> c_int ---
 	closesocket :: proc(socket: SOCKET) -> c_int ---
@@ -76,10 +81,10 @@ foreign ws2_32 {
 	listen :: proc(socket: SOCKET, backlog: c_int) -> c_int ---
 	connect :: proc(socket: SOCKET, address: ^SOCKADDR, len: c_int) -> c_int ---
 	getaddrinfo :: proc(
-		node: ^c_char,
-		service: ^c_char,
+		node: cstring,
+		service: cstring,
 		hints: ^ADDRINFOA,
-		res: ^ADDRINFOA,
+		res: ^^ADDRINFOA,
 	) -> c_int ---
 	freeaddrinfo :: proc(res: ^ADDRINFOA) ---
 	select :: proc(

+ 16 - 0
core/time/time_freestanding.odin

@@ -0,0 +1,16 @@
+//+build freestanding
+package time
+
+IS_SUPPORTED :: false
+
+now :: proc() -> Time {
+	return {}
+}
+
+sleep :: proc(d: Duration) {
+}
+
+_tick_now :: proc "contextless" () -> Tick {
+	return {}
+}
+

+ 21 - 0
core/time/time_js.odin

@@ -0,0 +1,21 @@
+//+build js
+package time
+
+IS_SUPPORTED :: false
+
+now :: proc() -> Time {
+	return {}
+}
+
+sleep :: proc(d: Duration) {
+}
+
+_tick_now :: proc "contextless" () -> Tick {
+	// mul_div_u64 :: proc "contextless" (val, num, den: i64) -> i64 {
+	// 	q := val / den
+	// 	r := val % den
+	// 	return q * num + r * num / den
+	// }
+	return {}
+}
+

+ 23 - 0
core/time/time_wasi.odin

@@ -0,0 +1,23 @@
+//+build wasi
+package time
+
+import wasi "core:sys/wasm/wasi"
+
+IS_SUPPORTED :: false
+
+now :: proc() -> Time {
+	return {}
+}
+
+sleep :: proc(d: Duration) {
+}
+
+_tick_now :: proc "contextless" () -> Tick {
+	// mul_div_u64 :: proc "contextless" (val, num, den: i64) -> i64 {
+	// 	q := val / den
+	// 	r := val % den
+	// 	return q * num + r * num / den
+	// }
+	return {}
+}
+

+ 2 - 2
examples/demo/demo.odin

@@ -2127,7 +2127,7 @@ or_return_operator :: proc() {
 	foo_2()
 }
 
-arbitrary_precision_maths :: proc() {
+arbitrary_precision_mathematics :: proc() {
 	fmt.println("\n# core:math/big")
 
 	print_bigint :: proc(name: string, a: ^big.Int, base := i8(10), print_name := true, newline := true, print_extra_info := true) {
@@ -2454,7 +2454,7 @@ main :: proc() {
 		relative_data_types()
 		or_else_operator()
 		or_return_operator()
-		arbitrary_precision_maths()
+		arbitrary_precision_mathematics()
 		matrix_type()
 	}
 }

+ 33 - 16
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"),
 };
@@ -155,7 +157,11 @@ enum CmdDocFlag : u32 {
 	CmdDocFlag_DocFormat   = 1<<2,
 };
 
-
+enum TimingsExportFormat : i32 {
+	TimingsExportUnspecified = 0,
+	TimingsExportJson        = 1,
+	TimingsExportCSV         = 2,
+};
 
 // This stores the information for the specify architecture of this build
 struct BuildContext {
@@ -195,6 +201,8 @@ struct BuildContext {
 	bool   generate_docs;
 	i32    optimization_level;
 	bool   show_timings;
+	TimingsExportFormat export_timings_format;
+	String export_timings_file;
 	bool   show_unused;
 	bool   show_unused_with_location;
 	bool   show_more_timings;
@@ -344,12 +352,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 +371,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 +388,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;

+ 2 - 2
src/check_builtin.cpp

@@ -446,9 +446,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 				} else if (hash_kind == "fnv64") {
 					hash_value = gb_fnv64(data, file_size);
 				} else if (hash_kind == "fnv32a") {
-					hash_value = gb_fnv32a(data, file_size);
+					hash_value = fnv32a(data, file_size);
 				} else if (hash_kind == "fnv64a") {
-					hash_value = gb_fnv64a(data, file_size);
+					hash_value = fnv64a(data, file_size);
 				} else if (hash_kind == "murmur32") {
 					hash_value = gb_murmur32(data, file_size);
 				} else if (hash_kind == "murmur64") {

+ 0 - 3
src/check_decl.cpp

@@ -1302,9 +1302,6 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
 				Type *t = base_type(type_deref(e->type));
 				if (t->kind == Type_Struct) {
 					Scope *scope = t->Struct.scope;
-					if (scope == nullptr) {
-						scope = scope_of_node(t->Struct.node);
-					}
 					GB_ASSERT(scope != nullptr);
 					for_array(i, scope->elements.entries) {
 						Entity *f = scope->elements.entries[i].value;

+ 3 - 3
src/check_expr.cpp

@@ -8413,14 +8413,14 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
 			if (check_is_assignable_to(c, &z, first_type)) {
 				// NOTE(bill): AST GENERATION HACK!
 				Token op = {Token_Pointer};
-				first_arg = ast_deref_expr(first_arg->file, first_arg, op);
+				first_arg = ast_deref_expr(first_arg->file(), first_arg, op);
 			} else if (y.mode == Addressing_Variable) {
 				Operand w = y;
 				w.type = alloc_type_pointer(y.type);
 				if (check_is_assignable_to(c, &w, first_type)) {
 					// NOTE(bill): AST GENERATION HACK!
 					Token op = {Token_And};
-					first_arg = ast_unary_expr(first_arg->file, op, first_arg);
+					first_arg = ast_unary_expr(first_arg->file(), op, first_arg);
 				}
 			}
 		}
@@ -8443,7 +8443,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
 			}
 			if (!fail && first_is_field_value) {
 				Token op = {Token_Eq};
-				AstFile *f = first_arg->file;
+				AstFile *f = first_arg->file();
 				first_arg = ast_field_value(f, ast_ident(f, make_token_ident(first_arg_name)), first_arg, op);
 			}
 		}

+ 7 - 9
src/check_stmt.cpp

@@ -635,10 +635,7 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b
 		bool is_ptr = is_type_pointer(e->type);
 		Type *t = base_type(type_deref(e->type));
 		if (t->kind == Type_Struct) {
-			Scope *found = scope_of_node(t->Struct.node);
-			if (found == nullptr) {
-				found = t->Struct.scope;
-			}
+			Scope *found = t->Struct.scope;
 			GB_ASSERT(found != nullptr);
 			for_array(i, found->elements.entries) {
 				Entity *f = found->elements.entries[i].value;
@@ -1399,9 +1396,9 @@ void check_block_stmt_for_errors(CheckerContext *ctx, Ast *body)  {
 	ast_node(bs, BlockStmt, body);
 	// NOTE(bill, 2020-09-23): This logic is prevent common erros with block statements
 	// e.g. if cond { x := 123; } // this is an error
-	if (body->scope != nullptr && body->scope->elements.entries.count > 0) {
-		if (body->scope->parent->node != nullptr) {
-			switch (body->scope->parent->node->kind) {
+	if (bs->scope != nullptr && bs->scope->elements.entries.count > 0) {
+		if (bs->scope->parent->node != nullptr) {
+			switch (bs->scope->parent->node->kind) {
 			case Ast_IfStmt:
 			case Ast_ForStmt:
 			case Ast_RangeStmt:
@@ -1616,7 +1613,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
 			}
 			Operand lhs = {Addressing_Invalid};
 			Operand rhs = {Addressing_Invalid};
-			Ast *binary_expr = alloc_ast_node(node->file, Ast_BinaryExpr);
+			Ast *binary_expr = alloc_ast_node(node->file(), Ast_BinaryExpr);
 			ast_node(be, BinaryExpr, binary_expr);
 			be->op = op;
 			be->op.kind = cast(TokenKind)(cast(i32)be->op.kind - (Token_AddEq - Token_Add));
@@ -2339,7 +2336,8 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
 					} else if (is_type_struct(t) || is_type_raw_union(t)) {
 						ERROR_BLOCK();
 						
-						Scope *scope = scope_of_node(t->Struct.node);
+						Scope *scope = t->Struct.scope;
+						GB_ASSERT(scope != nullptr);
 						for_array(i, scope->elements.entries) {
 							Entity *f = scope->elements.entries[i].value;
 							if (f->kind == Entity_Variable) {

+ 3 - 2
src/check_type.cpp

@@ -1939,8 +1939,9 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node,
 			error(proc_type_node, "A procedure type with the #optional_second tag requires 2 return values, got %td", result_count);
 		} else {
 			bool ok = false;
-			if (proc_type_node->file && proc_type_node->file->pkg) {
-				ok = proc_type_node->file->pkg->scope == ctx->info->runtime_package->scope;
+			AstFile *file = proc_type_node->file();
+			if (file && file->pkg) {
+				ok = file->pkg->scope == ctx->info->runtime_package->scope;
 			}
 
 			if (!ok) {

+ 50 - 13
src/checker.cpp

@@ -225,8 +225,8 @@ bool decl_info_has_init(DeclInfo *d) {
 Scope *create_scope(CheckerInfo *info, Scope *parent, isize init_elements_capacity=DEFAULT_SCOPE_CAPACITY) {
 	Scope *s = gb_alloc_item(permanent_allocator(), Scope);
 	s->parent = parent;
-	string_map_init(&s->elements, heap_allocator(), init_elements_capacity);
-	ptr_set_init(&s->imported, heap_allocator(), 0);
+	string_map_init(&s->elements, permanent_allocator(), init_elements_capacity);
+	ptr_set_init(&s->imported, permanent_allocator(), 0);
 	mutex_init(&s->mutex);
 
 	if (parent != nullptr && parent != builtin_pkg->scope) {
@@ -318,7 +318,43 @@ void add_scope(CheckerContext *c, Ast *node, Scope *scope) {
 	GB_ASSERT(node != nullptr);
 	GB_ASSERT(scope != nullptr);
 	scope->node = node;
-	node->scope = scope;
+	switch (node->kind) {
+	case Ast_BlockStmt:       node->BlockStmt.scope       = scope; break;
+	case Ast_IfStmt:          node->IfStmt.scope          = scope; break;
+	case Ast_ForStmt:         node->ForStmt.scope         = scope; break;
+	case Ast_RangeStmt:       node->RangeStmt.scope       = scope; break;
+	case Ast_UnrollRangeStmt: node->UnrollRangeStmt.scope = scope; break;
+	case Ast_CaseClause:      node->CaseClause.scope      = scope; break;
+	case Ast_SwitchStmt:      node->SwitchStmt.scope      = scope; break;
+	case Ast_TypeSwitchStmt:  node->TypeSwitchStmt.scope  = scope; break;
+	case Ast_ProcType:        node->ProcType.scope        = scope; break;
+	case Ast_StructType:      node->StructType.scope      = scope; break;
+	case Ast_UnionType:       node->UnionType.scope       = scope; break;
+	case Ast_EnumType:        node->EnumType.scope        = scope; break;
+	default: GB_PANIC("Invalid node for add_scope: %.*s", LIT(ast_strings[node->kind]));
+	}
+}
+
+Scope *scope_of_node(Ast *node) {
+	if (node == nullptr) {
+		return nullptr;
+	}
+	switch (node->kind) {
+	case Ast_BlockStmt:       return node->BlockStmt.scope;
+	case Ast_IfStmt:          return node->IfStmt.scope;
+	case Ast_ForStmt:         return node->ForStmt.scope;
+	case Ast_RangeStmt:       return node->RangeStmt.scope;
+	case Ast_UnrollRangeStmt: return node->UnrollRangeStmt.scope;
+	case Ast_CaseClause:      return node->CaseClause.scope;
+	case Ast_SwitchStmt:      return node->SwitchStmt.scope;
+	case Ast_TypeSwitchStmt:  return node->TypeSwitchStmt.scope;
+	case Ast_ProcType:        return node->ProcType.scope;
+	case Ast_StructType:      return node->StructType.scope;
+	case Ast_UnionType:       return node->UnionType.scope;
+	case Ast_EnumType:        return node->EnumType.scope;
+	}
+	GB_PANIC("Invalid node for add_scope: %.*s", LIT(ast_strings[node->kind]));
+	return nullptr;
 }
 
 
@@ -1081,9 +1117,6 @@ AstFile *ast_file_of_filename(CheckerInfo *i, String filename) {
 	}
 	return nullptr;
 }
-Scope *scope_of_node(Ast *node) {
-	return node->scope;
-}
 ExprInfo *check_get_expr_info(CheckerContext *c, Ast *expr) {
 	if (c->untyped != nullptr) {
 		ExprInfo **found = map_get(c->untyped, expr);
@@ -2660,10 +2693,14 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
 		}
 		return true;
 	} else if (name == "require") {
-		if (value != nullptr) {
-			error(elem, "'require' does not have any parameters");
+		ExactValue ev = check_decl_attribute_value(c, value);
+		if (ev.kind == ExactValue_Invalid) {
+			ac->require_declaration = true;
+		} else if (ev.kind == ExactValue_Bool) {
+			ac->require_declaration = ev.value_bool;
+		} else {
+			error(value, "Expected either a boolean or no parameter for 'require'");
 		}
-		ac->require_declaration = true;
 		return true;
 	} else if (name == "init") {
 		if (value != nullptr) {
@@ -3708,7 +3745,7 @@ String path_to_entity_name(String name, String fullpath, bool strip_extension=tr
 #if 1
 
 void add_import_dependency_node(Checker *c, Ast *decl, PtrMap<AstPackage *, ImportGraphNode *> *M) {
-	AstPackage *parent_pkg = decl->file->pkg;
+	AstPackage *parent_pkg = decl->file()->pkg;
 
 	switch (decl->kind) {
 	case_ast_node(id, ImportDecl, decl);
@@ -5257,9 +5294,6 @@ void check_parsed_files(Checker *c) {
 		check_scope_usage(c, f->scope);
 	}
 
-	TIME_SECTION("check test procedures");
-	check_test_procedures(c);
-
 	TIME_SECTION("add untyped expression values");
 	// Add untyped expression values
 	for (UntypedExprInfo u = {}; mpmc_dequeue(&c->global_untyped_queue, &u); /**/) {
@@ -5312,6 +5346,9 @@ void check_parsed_files(Checker *c) {
 	TIME_SECTION("generate minimum dependency set");
 	generate_minimum_dependency_set(c, c->info.entry_point);
 
+	TIME_SECTION("check test procedures");
+	check_test_procedures(c);
+
 	TIME_SECTION("check bodies have all been checked");
 	check_unchecked_bodies(c);
 

+ 0 - 1
src/checker.hpp

@@ -410,7 +410,6 @@ gb_global AstPackage *config_pkg      = nullptr;
 TypeAndValue type_and_value_of_expr (Ast *expr);
 Type *       type_of_expr           (Ast *expr);
 Entity *     implicit_entity_of_node(Ast *clause);
-Scope *      scope_of_node          (Ast *node);
 DeclInfo *   decl_info_of_ident     (Ast *ident);
 DeclInfo *   decl_info_of_entity    (Entity * e);
 AstFile *    ast_file_of_filename   (CheckerInfo *i, String   filename);

+ 51 - 12
src/common.cpp

@@ -83,9 +83,20 @@ int i32_cmp(i32 x, i32 y) {
 u32 fnv32a(void const *data, isize len) {
 	u8 const *bytes = cast(u8 const *)data;
 	u32 h = 0x811c9dc5;
-	for (isize i = 0; i < len; i++) {
-		u32 b = cast(u32)bytes[i];
-		h = (h ^ b) * 0x01000193;
+	
+	for (; len >= 8; len -= 8, bytes += 8) {
+		h = (h ^ bytes[0]) * 0x01000193;
+		h = (h ^ bytes[1]) * 0x01000193;
+		h = (h ^ bytes[2]) * 0x01000193;
+		h = (h ^ bytes[3]) * 0x01000193;
+		h = (h ^ bytes[4]) * 0x01000193;
+		h = (h ^ bytes[5]) * 0x01000193;
+		h = (h ^ bytes[6]) * 0x01000193;
+		h = (h ^ bytes[7]) * 0x01000193;
+	}
+
+	while (len--) {
+		h = (h ^ *bytes++) * 0x01000193;
 	}
 	return h;
 }
@@ -93,20 +104,48 @@ u32 fnv32a(void const *data, isize len) {
 u64 fnv64a(void const *data, isize len) {
 	u8 const *bytes = cast(u8 const *)data;
 	u64 h = 0xcbf29ce484222325ull;
-	for (isize i = 0; i < len; i++) {
-		u64 b = cast(u64)bytes[i];
-		h = (h ^ b) * 0x100000001b3ull;
+	
+	for (; len >= 8; len -= 8, bytes += 8) {
+		h = (h ^ bytes[0]) * 0x100000001b3ull;
+		h = (h ^ bytes[1]) * 0x100000001b3ull;
+		h = (h ^ bytes[2]) * 0x100000001b3ull;
+		h = (h ^ bytes[3]) * 0x100000001b3ull;
+		h = (h ^ bytes[4]) * 0x100000001b3ull;
+		h = (h ^ bytes[5]) * 0x100000001b3ull;
+		h = (h ^ bytes[6]) * 0x100000001b3ull;
+		h = (h ^ bytes[7]) * 0x100000001b3ull;
+	}
+
+	while (len--) {
+		h = (h ^ *bytes++) * 0x100000001b3ull;
 	}
 	return h;
 }
 
 u64 u64_digit_value(Rune r) {
-	if ('0' <= r && r <= '9') {
-		return r - '0';
-	} else if ('a' <= r && r <= 'f') {
-		return r - 'a' + 10;
-	} else if ('A' <= r && r <= 'F') {
-		return r - 'A' + 10;
+	switch (r) {
+	case '0': return 0;
+	case '1': return 1;
+	case '2': return 2;
+	case '3': return 3;
+	case '4': return 4;
+	case '5': return 5;
+	case '6': return 6;
+	case '7': return 7;
+	case '8': return 8;
+	case '9': return 9;
+	case 'a': return 10;
+	case 'b': return 11;
+	case 'c': return 12;
+	case 'd': return 13;
+	case 'e': return 14;
+	case 'f': return 15;
+	case 'A': return 10;
+	case 'B': return 11;
+	case 'C': return 12;
+	case 'D': return 13;
+	case 'E': return 14;
+	case 'F': return 15;
 	}
 	return 16; // NOTE(bill): Larger than highest possible
 }

+ 1 - 1
src/docs_writer.cpp

@@ -257,7 +257,7 @@ OdinDocArray<T> odin_write_item_as_slice(OdinDocWriter *w, T data) {
 OdinDocPosition odin_doc_token_pos_cast(OdinDocWriter *w, TokenPos const &pos) {
 	OdinDocFileIndex file_index = 0;
 	if (pos.file_id != 0) {
-		AstFile *file = get_ast_file_from_id(pos.file_id);
+		AstFile *file = global_files[pos.file_id];
 		if (file != nullptr) {
 			OdinDocFileIndex *file_index_found = map_get(&w->file_cache, file);
 			GB_ASSERT(file_index_found != nullptr);

+ 6 - 4
src/llvm_backend.cpp

@@ -454,7 +454,7 @@ lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, A
 	token.kind = Token_Ident;
 	token.string = name;
 	Entity *e = alloc_entity_procedure(nullptr, token, type, pl->tags);
-	e->file = expr->file;
+	e->file = expr->file();
 	e->decl_info = pl->decl;
 	e->code_gen_module = m;
 	e->flags |= EntityFlag_ProcBodyChecked;
@@ -684,7 +684,8 @@ lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *start
 			if (init.value == nullptr) {
 				LLVMTypeRef global_type = LLVMGetElementType(LLVMTypeOf(var->var.value));
 				if (is_type_untyped_undef(init.type)) {
-					LLVMSetInitializer(var->var.value, LLVMGetUndef(global_type));
+					// LLVMSetInitializer(var->var.value, LLVMGetUndef(global_type));
+					LLVMSetInitializer(var->var.value, LLVMConstNull(global_type));
 					var->is_initialized = true;
 					continue;
 				} else if (is_type_untyped_nil(init.type)) {
@@ -1277,8 +1278,8 @@ void lb_generate_code(lbGenerator *gen) {
 
 			if (Entity *entry_point = m->info->entry_point) {
 				if (Ast *ident = entry_point->identifier.load()) {
-					if (ident->file) {
-						init_file = ident->file;
+					if (ident->file_id) {
+						init_file = ident->file();
 					}
 				}
 			}
@@ -1675,6 +1676,7 @@ void lb_generate_code(lbGenerator *gen) {
 		lbModule *m = gen->modules.entries[i].value;
 		
 		lb_run_remove_unused_function_pass(m);
+		lb_run_remove_unused_globals_pass(m);
 
 		auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData);
 		wd->m = m;

+ 6 - 0
src/llvm_backend.hpp

@@ -471,6 +471,12 @@ lbValue lb_consume_copy_elision_hint(lbProcedure *p);
 lbStructFieldRemapping lb_get_struct_remapping(lbModule *m, Type *t);
 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);

+ 7 - 16
src/llvm_backend_debug.cpp

@@ -18,7 +18,7 @@ LLVMMetadataRef lb_get_llvm_file_metadata_from_node(lbModule *m, Ast *node) {
 	if (node == nullptr) {
 		return nullptr;
 	}
-	return lb_get_llvm_metadata(m, node->file);
+	return lb_get_llvm_metadata(m, node->file());
 }
 
 LLVMMetadataRef lb_get_current_debug_scope(lbProcedure *p) {
@@ -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);
@@ -669,7 +660,7 @@ void lb_debug_complete_types(lbModule *m) {
 			case Type_Struct:
 				if (file == nullptr) {
 					if (bt->Struct.node) {
-						file = lb_get_llvm_metadata(m, bt->Struct.node->file);
+						file = lb_get_llvm_metadata(m, bt->Struct.node->file());
 						line_number = cast(unsigned)ast_token(bt->Struct.node).pos.line;
 					}
 				}
@@ -750,7 +741,7 @@ void lb_debug_complete_types(lbModule *m) {
 				{
 					if (file == nullptr) {
 						GB_ASSERT(bt->Union.node != nullptr);
-						file = lb_get_llvm_metadata(m, bt->Union.node->file);
+						file = lb_get_llvm_metadata(m, bt->Union.node->file());
 						line_number = cast(unsigned)ast_token(bt->Union.node).pos.line;
 					}
 
@@ -810,7 +801,7 @@ void lb_debug_complete_types(lbModule *m) {
 				{
 					if (file == nullptr) {
 						GB_ASSERT(bt->BitSet.node != nullptr);
-						file = lb_get_llvm_metadata(m, bt->BitSet.node->file);
+						file = lb_get_llvm_metadata(m, bt->BitSet.node->file());
 						line_number = cast(unsigned)ast_token(bt->BitSet.node).pos.line;
 					}
 
@@ -938,7 +929,7 @@ void lb_add_debug_local_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, T
 	}
 
 
-	AstFile *file = p->body->file;
+	AstFile *file = p->body->file();
 
 	LLVMMetadataRef llvm_scope = lb_get_current_debug_scope(p);
 	LLVMMetadataRef llvm_file = lb_get_llvm_metadata(m, file);
@@ -984,7 +975,7 @@ void lb_add_debug_context_variable(lbProcedure *p, lbAddr const &ctx) {
 	}
 	TokenPos pos = {};
 
-	pos.file_id = p->body->file ? p->body->file->id : 0;
+	pos.file_id = p->body->file_id;
 	pos.line = LLVMDILocationGetLine(loc);
 	pos.column = LLVMDILocationGetColumn(loc);
 

+ 26 - 21
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) {
@@ -577,7 +573,7 @@ LLVMValueRef lb_matrix_to_trimmed_vector(lbProcedure *p, lbValue m) {
 	}
 	
 	LLVMValueRef mask = lb_matrix_trimmed_vector_mask(p, mt);
-	LLVMValueRef trimmed_vector = LLVMBuildShuffleVector(p->builder, vector, LLVMGetUndef(LLVMTypeOf(vector)), mask, "");
+	LLVMValueRef trimmed_vector = llvm_basic_shuffle(p, vector, mask);
 	return trimmed_vector;
 }
 
@@ -608,7 +604,7 @@ lbValue lb_emit_matrix_tranpose(lbProcedure *p, lbValue m, Type *type) {
 			
 			// transpose mask
 			LLVMValueRef mask = LLVMConstVector(mask_elems.data, column_count);
-			LLVMValueRef row = LLVMBuildShuffleVector(p->builder, vector, LLVMGetUndef(LLVMTypeOf(vector)), mask, "");
+			LLVMValueRef row = llvm_basic_shuffle(p, vector, mask);
 			rows[i] = row;
 		}
 		
@@ -747,13 +743,13 @@ lbValue lb_emit_matrix_mul(lbProcedure *p, lbValue lhs, lbValue rhs, Type *type)
 			
 			// transpose mask
 			LLVMValueRef mask = LLVMConstVector(mask_elems.data, inner);
-			LLVMValueRef row = LLVMBuildShuffleVector(p->builder, x_vector, LLVMGetUndef(LLVMTypeOf(x_vector)), mask, "");
+			LLVMValueRef row = llvm_basic_shuffle(p, x_vector, mask);
 			x_rows[i] = row;
 		}
 		
 		for (unsigned i = 0; i < outer_columns; i++) {
 			LLVMValueRef mask = llvm_mask_iota(p->module, y_stride*i, inner);
-			LLVMValueRef column = LLVMBuildShuffleVector(p->builder, y_vector, LLVMGetUndef(LLVMTypeOf(y_vector)), mask, "");
+			LLVMValueRef column = llvm_basic_shuffle(p, y_vector, mask);
 			y_columns[i] = column;
 		}
 		
@@ -825,7 +821,7 @@ lbValue lb_emit_matrix_mul_vector(lbProcedure *p, lbValue lhs, lbValue rhs, Type
 		
 		for (unsigned column_index = 0; column_index < column_count; column_index++) {
 			LLVMValueRef mask = llvm_mask_iota(p->module, stride*column_index, row_count);
-			LLVMValueRef column = LLVMBuildShuffleVector(p->builder, matrix_vector, LLVMGetUndef(LLVMTypeOf(matrix_vector)), mask, "");
+			LLVMValueRef column = llvm_basic_shuffle(p, matrix_vector, mask);
 			m_columns[column_index] = column;
 		}
 		
@@ -901,7 +897,7 @@ lbValue lb_emit_vector_mul_matrix(lbProcedure *p, lbValue lhs, lbValue rhs, Type
 			
 			// transpose mask
 			LLVMValueRef mask = LLVMConstVector(mask_elems.data, column_count);
-			LLVMValueRef column = LLVMBuildShuffleVector(p->builder, matrix_vector, LLVMGetUndef(LLVMTypeOf(matrix_vector)), mask, "");
+			LLVMValueRef column = llvm_basic_shuffle(p, matrix_vector, mask);
 			m_columns[row_index] = column;
 		}
 		
@@ -1373,7 +1369,7 @@ lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) {
 			Type *rt = base_type(right.type);
 			if (is_type_pointer(rt)) {
 				right = lb_emit_load(p, right);
-				rt = type_deref(rt);
+				rt = base_type(type_deref(rt));
 			}
 
 			switch (rt->kind) {
@@ -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);
 				}
 			}
 		}

+ 1 - 1
src/llvm_backend_general.cpp

@@ -1084,7 +1084,7 @@ lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) {
 				scalars[i] = LLVMConstInt(lb_type(p->module, t_u32), addr.swizzle.indices[i], false);
 			}
 			LLVMValueRef mask = LLVMConstVector(scalars, addr.swizzle.count);
-			LLVMValueRef sv = LLVMBuildShuffleVector(p->builder, v, LLVMGetUndef(vector_type), mask, "");
+			LLVMValueRef sv = llvm_basic_shuffle(p, v, mask);
 
 			LLVMValueRef dst = LLVMBuildPointerCast(p->builder, ptr.value, LLVMPointerType(LLVMTypeOf(sv), 0), "");
 			LLVMBuildStore(p->builder, sv, dst);

+ 120 - 43
src/llvm_backend_opt.cpp

@@ -1,3 +1,37 @@
+/**************************************************************************
+
+	IMPORTANT NOTE(bill, 2021-11-06): Regarding Optimization Passes
+
+	A lot of the passes taken here have been modified with what was 
+	partially done in LLVM 11. 
+
+	Passes that CANNOT be used by Odin due to C-like optimizations which 
+	are not compatible with Odin:
+		
+		LLVMAddCorrelatedValuePropagationPass 
+		LLVMAddAggressiveInstCombinerPass
+		LLVMAddInstructionCombiningPass
+		LLVMAddIndVarSimplifyPass
+		LLVMAddLoopUnrollPass
+		LLVMAddEarlyCSEMemSSAPass
+		LLVMAddGVNPass
+		LLVMAddDeadStoreEliminationPass - Causes too many false positive
+		
+	Odin does not allow poison-value based optimizations. 
+	
+	For example, *-flowing integers in C is "undefined behaviour" and thus 
+	many optimizers, including LLVM, take advantage of this for a certain 
+	class of optimizations. Odin on the other hand defines *-flowing 
+	behaviour to obey the rules of 2's complement, meaning wrapping is a 
+	expected. This means any outputted IR containing the following flags 
+	may cause incorrect behaviour:
+	
+		nsw (no signed wrap)
+		nuw (no unsigned wrap)
+		poison (poison value)
+**************************************************************************/
+
+
 void lb_populate_function_pass_manager(lbModule *m, LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level);
 void lb_add_function_simplifcation_passes(LLVMPassManagerRef mpm, i32 optimization_level);
 void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPassManagerRef mpm, i32 optimization_level);
@@ -22,9 +56,9 @@ void lb_add_must_preserve_predicate_pass(lbModule *m, LLVMPassManagerRef fpm, i3
 
 
 #if LLVM_VERSION_MAJOR < 12
-#define LLVM_ADD_CONSTANT_VALUE_PASS LLVMAddConstantPropagationPass
+#define LLVM_ADD_CONSTANT_VALUE_PASS(fpm) LLVMAddConstantPropagationPass(fpm)
 #else
-#define LLVM_ADD_CONSTANT_VALUE_PASS LLVMAddCorrelatedValuePropagationPass
+#define LLVM_ADD_CONSTANT_VALUE_PASS(fpm) 
 #endif
 
 void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm) {
@@ -33,10 +67,10 @@ void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm) {
 	LLVM_ADD_CONSTANT_VALUE_PASS(fpm);
 	LLVMAddEarlyCSEPass(fpm);
 
-	LLVM_ADD_CONSTANT_VALUE_PASS(fpm);
-	LLVMAddMergedLoadStoreMotionPass(fpm);
-	LLVMAddPromoteMemoryToRegisterPass(fpm);
-	LLVMAddCFGSimplificationPass(fpm);
+	// LLVM_ADD_CONSTANT_VALUE_PASS(fpm);
+	// LLVMAddMergedLoadStoreMotionPass(fpm);
+	// LLVMAddPromoteMemoryToRegisterPass(fpm);
+	// LLVMAddCFGSimplificationPass(fpm);
 }
 
 void lb_populate_function_pass_manager(lbModule *m, LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level) {
@@ -62,15 +96,7 @@ void lb_populate_function_pass_manager(lbModule *m, LLVMPassManagerRef fpm, bool
 	LLVMPassManagerBuilderPopulateFunctionPassManager(pmb, fpm);
 #else
 	LLVMAddMemCpyOptPass(fpm);
-	LLVMAddPromoteMemoryToRegisterPass(fpm);
-	LLVMAddMergedLoadStoreMotionPass(fpm);
-	LLVM_ADD_CONSTANT_VALUE_PASS(fpm);
-	LLVMAddEarlyCSEPass(fpm);
-
-	LLVM_ADD_CONSTANT_VALUE_PASS(fpm);
-	LLVMAddMergedLoadStoreMotionPass(fpm);
-	LLVMAddPromoteMemoryToRegisterPass(fpm);
-	LLVMAddCFGSimplificationPass(fpm);
+	lb_basic_populate_function_pass_manager(fpm);
 
 	LLVMAddSCCPPass(fpm);
 
@@ -125,17 +151,10 @@ void lb_populate_function_pass_manager_specific(lbModule *m, LLVMPassManagerRef
 }
 
 void lb_add_function_simplifcation_passes(LLVMPassManagerRef mpm, i32 optimization_level) {
-	LLVMAddEarlyCSEMemSSAPass(mpm);
-
-	LLVMAddGVNPass(mpm);
 	LLVMAddCFGSimplificationPass(mpm);
 
 	LLVMAddJumpThreadingPass(mpm);
 
-	// if (optimization_level > 2) {
-		// LLVMAddAggressiveInstCombinerPass(mpm);
-	// }
-	LLVMAddInstructionCombiningPass(mpm);
 	LLVMAddSimplifyLibCallsPass(mpm);
 
 	LLVMAddTailCallEliminationPass(mpm);
@@ -147,32 +166,23 @@ void lb_add_function_simplifcation_passes(LLVMPassManagerRef mpm, i32 optimizati
 	LLVMAddLoopUnswitchPass(mpm);
 
 	LLVMAddCFGSimplificationPass(mpm);
-	LLVMAddInstructionCombiningPass(mpm);
-	LLVMAddIndVarSimplifyPass(mpm);
 	LLVMAddLoopIdiomPass(mpm);
 	LLVMAddLoopDeletionPass(mpm);
 
-	LLVMAddLoopUnrollPass(mpm);
-
 	LLVMAddMergedLoadStoreMotionPass(mpm);
 
-	LLVMAddGVNPass(mpm);
-
 	LLVMAddMemCpyOptPass(mpm);
 	LLVMAddSCCPPass(mpm);
 
 	LLVMAddBitTrackingDCEPass(mpm);
 
-	LLVMAddInstructionCombiningPass(mpm);
 	LLVMAddJumpThreadingPass(mpm);
 	LLVM_ADD_CONSTANT_VALUE_PASS(mpm);
-	LLVMAddDeadStoreEliminationPass(mpm);
 	LLVMAddLICMPass(mpm);
 
 	LLVMAddLoopRerollPass(mpm);
 	LLVMAddAggressiveDCEPass(mpm);
 	LLVMAddCFGSimplificationPass(mpm);
-	LLVMAddInstructionCombiningPass(mpm);
 }
 
 
@@ -200,6 +210,7 @@ void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPa
 		// LLVMPassManagerBuilderPopulateLTOPassManager(pmb, mpm, false, true);
 		// return;
 	}
+	
 
 	LLVMAddIPSCCPPass(mpm);
 	LLVMAddCalledValuePropagationPass(mpm);
@@ -207,8 +218,6 @@ void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPa
 	LLVMAddGlobalOptimizerPass(mpm);
 	LLVMAddDeadArgEliminationPass(mpm);
 
-	// LLVMAddConstantMergePass(mpm); // ???
-	LLVMAddInstructionCombiningPass(mpm);
 	LLVMAddCFGSimplificationPass(mpm);
 
 	LLVMAddPruneEHPass(mpm);
@@ -217,25 +226,24 @@ void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPa
 	}
 
 	LLVMAddFunctionInliningPass(mpm);
+	
+	
 	lb_add_function_simplifcation_passes(mpm, optimization_level);
-
+		
 	LLVMAddGlobalDCEPass(mpm);
 	LLVMAddGlobalOptimizerPass(mpm);
-
-	// LLVMAddLowerConstantIntrinsicsPass(mpm);
+	
 
 	LLVMAddLoopRotatePass(mpm);
 
 	LLVMAddLoopVectorizePass(mpm);
-
-	LLVMAddInstructionCombiningPass(mpm);
+	
 	if (optimization_level >= 2) {
 		LLVMAddEarlyCSEPass(mpm);
 		LLVM_ADD_CONSTANT_VALUE_PASS(mpm);
 		LLVMAddLICMPass(mpm);
 		LLVMAddLoopUnswitchPass(mpm);
 		LLVMAddCFGSimplificationPass(mpm);
-		LLVMAddInstructionCombiningPass(mpm);
 	}
 
 	LLVMAddCFGSimplificationPass(mpm);
@@ -255,6 +263,15 @@ void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPa
 	LLVMAddCFGSimplificationPass(mpm);
 }
 
+
+
+/**************************************************************************
+	IMPORTANT NOTE(bill, 2021-11-06): Custom Passes
+	
+	The procedures below are custom written passes to aid in the 
+	optimization of Odin programs	
+**************************************************************************/
+
 void lb_run_remove_dead_instruction_pass(lbProcedure *p) {
 	isize removal_count = 0;
 	isize pass_count = 0;
@@ -356,6 +373,20 @@ void lb_run_function_pass_manager(LLVMPassManagerRef fpm, lbProcedure *p) {
 	lb_run_remove_dead_instruction_pass(p);
 }
 
+void llvm_delete_function(LLVMValueRef func) {
+	// for (LLVMBasicBlockRef block = LLVMGetFirstBasicBlock(func); block != nullptr; /**/) {
+	// 	LLVMBasicBlockRef curr_block = block;
+	// 	block = LLVMGetNextBasicBlock(block);
+	// 	for (LLVMValueRef instr = LLVMGetFirstInstruction(curr_block); instr != nullptr; /**/) {
+	// 		LLVMValueRef curr_instr = instr;
+	// 		instr = LLVMGetNextInstruction(instr);
+			
+	// 		LLVMInstructionEraseFromParent(curr_instr);
+	// 	}
+	// 	LLVMRemoveBasicBlockFromParent(curr_block);
+	// }
+	LLVMDeleteFunction(func);
+}
 
 void lb_run_remove_unused_function_pass(lbModule *m) {
 	isize removal_count = 0;
@@ -363,7 +394,7 @@ void lb_run_remove_unused_function_pass(lbModule *m) {
 	isize const max_pass_count = 10;
 	// Custom remove dead function pass
 	for (; pass_count < max_pass_count; pass_count++) {
-		bool was_dead_function = false;	
+		bool was_dead = false;	
 		for (LLVMValueRef func = LLVMGetFirstFunction(m->mod);
 		     func != nullptr;
 		     /**/
@@ -395,12 +426,58 @@ void lb_run_remove_unused_function_pass(lbModule *m) {
 					continue;
 				}
 			}
+			
+			llvm_delete_function(curr_func);
+			was_dead = true;
+			removal_count += 1;
+		}
+		if (!was_dead) {
+			break;
+		}
+	}
+}
+
+
+void lb_run_remove_unused_globals_pass(lbModule *m) {
+	isize removal_count = 0;
+	isize pass_count = 0;
+	isize const max_pass_count = 10;
+	// Custom remove dead function pass
+	for (; pass_count < max_pass_count; pass_count++) {
+		bool was_dead = false;	
+		for (LLVMValueRef global = LLVMGetFirstGlobal(m->mod);
+		     global != nullptr;
+		     /**/
+		     ) {
+		     	LLVMValueRef curr_global = global;
+		     	global = LLVMGetNextGlobal(global);
+		     	
+			LLVMUseRef first_use = LLVMGetFirstUse(curr_global);
+			if (first_use != nullptr)  {
+				continue;
+			}
+			String name = {};
+			name.text = cast(u8 *)LLVMGetValueName2(curr_global, cast(size_t *)&name.len);
+						
+			LLVMLinkage linkage = LLVMGetLinkage(curr_global);
+			if (linkage != LLVMInternalLinkage) {
+				continue;
+			}
+			
+			Entity **found = map_get(&m->procedure_values, curr_global);
+			if (found && *found) {
+				Entity *e = *found;
+				bool is_required = (e->flags & EntityFlag_Require) == EntityFlag_Require;
+				if (is_required) {
+					continue;
+				}
+			}
 
-			LLVMDeleteFunction(curr_func);
-			was_dead_function = true;
+			LLVMDeleteGlobal(curr_global);
+			was_dead = true;
 			removal_count += 1;
 		}
-		if (!was_dead_function) {
+		if (!was_dead) {
 			break;
 		}
 	}

+ 20 - 5
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);
@@ -258,8 +258,8 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
 		if (entity->file != nullptr) {
 			file = lb_get_llvm_metadata(m, entity->file);
 			scope = file;
-		} else if (ident != nullptr && ident->file != nullptr) {
-			file = lb_get_llvm_metadata(m, ident->file);
+		} else if (ident != nullptr && ident->file_id != 0) {
+			file = lb_get_llvm_metadata(m, ident->file());
 			scope = file;
 		} else if (entity->scope != nullptr) {
 			file = lb_get_llvm_metadata(m, entity->scope->file);
@@ -2002,7 +2002,22 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
 						constraints = gb_string_appendc(constraints, regs[i]);
 						constraints = gb_string_appendc(constraints, "}");
 					}
-					
+
+					// The SYSCALL instruction stores the address of the
+					// following instruction into RCX, and RFLAGS in R11.
+					//
+					// RSP is not saved, but at least on Linux it appears
+					// that the kernel system-call handler does the right
+					// thing.
+					//
+					// Some but not all system calls will additionally
+					// clobber memory.
+					//
+					// TODO: FreeBSD is different and will also clobber
+					// R8, R9, and R10.  Additionally CF is used to
+					// indicate an error instead of -errno.
+					constraints = gb_string_appendc(constraints, ",~{rcx},~{r11},~{memory}");
+
 					inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
 				}
 				break;

+ 11 - 10
src/llvm_backend_stmt.cpp

@@ -212,8 +212,9 @@ void lb_open_scope(lbProcedure *p, Scope *s) {
 			unsigned column = cast(unsigned)token.pos.column;
 
 			LLVMMetadataRef file = nullptr;
-			if (s->node->file != nullptr) {
-				file = lb_get_llvm_metadata(m, s->node->file);
+			AstFile *ast_file = s->node->file();
+			if (ast_file != nullptr) {
+				file = lb_get_llvm_metadata(m, ast_file);
 			}
 			LLVMMetadataRef scope = nullptr;
 			if (p->scope_stack.count > 0) {
@@ -1624,7 +1625,7 @@ void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return_results) {
 
 void lb_build_if_stmt(lbProcedure *p, Ast *node) {
 	ast_node(is, IfStmt, node);
-	lb_open_scope(p, node->scope); // Scope #1
+	lb_open_scope(p, is->scope); // Scope #1
 	defer (lb_close_scope(p, lbDeferExit_Default, nullptr));
 
 	if (is->init != nullptr) {
@@ -1674,7 +1675,7 @@ void lb_build_if_stmt(lbProcedure *p, Ast *node) {
 				lb_emit_jump(p, else_);
 				lb_start_block(p, else_);
 
-				lb_open_scope(p, is->else_stmt->scope);
+				lb_open_scope(p, scope_of_node(is->else_stmt));
 				lb_build_stmt(p, is->else_stmt);
 				lb_close_scope(p, lbDeferExit_Default, nullptr);
 			}
@@ -1691,7 +1692,7 @@ void lb_build_if_stmt(lbProcedure *p, Ast *node) {
 		if (is->else_stmt != nullptr) {
 			lb_start_block(p, else_);
 
-			lb_open_scope(p, is->else_stmt->scope);
+			lb_open_scope(p, scope_of_node(is->else_stmt));
 			lb_build_stmt(p, is->else_stmt);
 			lb_close_scope(p, lbDeferExit_Default, nullptr);
 
@@ -1709,7 +1710,7 @@ void lb_build_if_stmt(lbProcedure *p, Ast *node) {
 void lb_build_for_stmt(lbProcedure *p, Ast *node) {
 	ast_node(fs, ForStmt, node);
 
-	lb_open_scope(p, node->scope); // Open Scope here
+	lb_open_scope(p, fs->scope); // Open Scope here
 
 	if (fs->init != nullptr) {
 	#if 1
@@ -2055,7 +2056,7 @@ void lb_build_stmt(lbProcedure *p, Ast *node) {
 			tl->is_block = true;
 		}
 
-		lb_open_scope(p, node->scope);
+		lb_open_scope(p, bs->scope);
 		lb_build_stmt_list(p, bs->stmts);
 		lb_close_scope(p, lbDeferExit_Default, nullptr);
 
@@ -2136,15 +2137,15 @@ void lb_build_stmt(lbProcedure *p, Ast *node) {
 	case_end;
 
 	case_ast_node(rs, RangeStmt, node);
-		lb_build_range_stmt(p, rs, node->scope);
+		lb_build_range_stmt(p, rs, rs->scope);
 	case_end;
 
 	case_ast_node(rs, UnrollRangeStmt, node);
-		lb_build_unroll_range_stmt(p, rs, node->scope);
+		lb_build_unroll_range_stmt(p, rs, rs->scope);
 	case_end;
 
 	case_ast_node(ss, SwitchStmt, node);
-		lb_build_switch_stmt(p, ss, node->scope);
+		lb_build_switch_stmt(p, ss, ss->scope);
 	case_end;
 
 	case_ast_node(ss, TypeSwitchStmt, node);

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů