Ver Fonte

Merge branch 'master' into windows-llvm-13.0.0

gingerBill há 3 anos atrás
pai
commit
d8b0b12e02
100 ficheiros alterados com 5742 adições e 2265 exclusões
  1. 12 2
      Makefile
  2. 29 0
      bin/nasm/windows/LICENSE
  3. BIN
      bin/nasm/windows/nasm.exe
  4. BIN
      bin/nasm/windows/ndisasm.exe
  5. 2 3
      core/bytes/bytes.odin
  6. 0 216
      core/container/array.odin
  7. 124 0
      core/container/bit_array/bit_array.odin
  8. 52 0
      core/container/bit_array/doc.odin
  9. 0 80
      core/container/bloom_filter.odin
  10. 0 377
      core/container/map.odin
  11. 0 121
      core/container/priority_queue.odin
  12. 143 0
      core/container/priority_queue/priority_queue.odin
  13. 0 175
      core/container/queue.odin
  14. 205 0
      core/container/queue/queue.odin
  15. 0 74
      core/container/ring.odin
  16. 0 240
      core/container/set.odin
  17. 0 95
      core/container/small_array.odin
  18. 117 0
      core/container/small_array/small_array.odin
  19. 7 1
      core/crypto/README.md
  20. 35 0
      core/crypto/_fiat/README.md
  21. 24 0
      core/crypto/_fiat/fiat.odin
  22. 138 0
      core/crypto/_fiat/field_curve25519/field.odin
  23. 616 0
      core/crypto/_fiat/field_curve25519/field51.odin
  24. 66 0
      core/crypto/_fiat/field_poly1305/field.odin
  25. 356 0
      core/crypto/_fiat/field_poly1305/field4344.odin
  26. 117 28
      core/crypto/blake/blake.odin
  27. 33 7
      core/crypto/blake2b/blake2b.odin
  28. 33 7
      core/crypto/blake2s/blake2s.odin
  29. 581 0
      core/crypto/chacha20/chacha20.odin
  30. 146 0
      core/crypto/chacha20poly1305/chacha20poly1305.odin
  31. 52 0
      core/crypto/crypto.odin
  32. 29 7
      core/crypto/gost/gost.odin
  33. 117 28
      core/crypto/groestl/groestl.odin
  34. 400 95
      core/crypto/haval/haval.odin
  35. 117 28
      core/crypto/jh/jh.odin
  36. 130 37
      core/crypto/keccak/keccak.odin
  37. 43 21
      core/crypto/md2/md2.odin
  38. 31 9
      core/crypto/md4/md4.odin
  39. 31 9
      core/crypto/md5/md5.odin
  40. 163 0
      core/crypto/poly1305/poly1305.odin
  41. 7 0
      core/crypto/rand_generic.odin
  42. 37 0
      core/crypto/rand_linux.odin
  43. 113 28
      core/crypto/ripemd/ripemd.odin
  44. 30 7
      core/crypto/sha1/sha1.odin
  45. 117 28
      core/crypto/sha2/sha2.odin
  46. 125 36
      core/crypto/sha3/sha3.odin
  47. 66 19
      core/crypto/shake/shake.odin
  48. 33 10
      core/crypto/sm3/sm3.odin
  49. 58 14
      core/crypto/streebog/streebog.odin
  50. 88 21
      core/crypto/tiger/tiger.odin
  51. 88 21
      core/crypto/tiger2/tiger2.odin
  52. 29 7
      core/crypto/whirlpool/whirlpool.odin
  53. 126 0
      core/crypto/x25519/x25519.odin
  54. 25 25
      core/encoding/json/unmarshal.odin
  55. 17 0
      core/hash/hash.odin
  56. 12 11
      core/math/big/common.odin
  57. 76 92
      core/math/big/internal.odin
  58. 1 32
      core/math/big/logical.odin
  59. 93 25
      core/math/big/private.odin
  60. 8 8
      core/math/big/rat.odin
  61. 2 2
      core/math/linalg/extended.odin
  62. 1 0
      core/math/math.odin
  63. 34 42
      core/mem/virtual/virtual_linux.odin
  64. 1 0
      core/odin/ast/ast.odin
  65. 24 3
      core/odin/parser/parser.odin
  66. 64 9
      core/os/file_windows.odin
  67. 40 21
      core/os/os.odin
  68. 2 3
      core/os/os_linux.odin
  69. 12 2
      core/runtime/internal.odin
  70. 29 12
      core/runtime/procs_wasm32.odin
  71. 13 0
      core/runtime/procs_windows_amd64.asm
  72. 2 9
      core/runtime/procs_windows_amd64.odin
  73. 4 3
      core/slice/slice.odin
  74. 33 0
      core/sort/map.odin
  75. 3 1
      core/strconv/strconv.odin
  76. 4 5
      core/strings/strings.odin
  77. 2 1
      core/sync/sync2/futex_linux.odin
  78. 2 3
      core/sync/sync2/primitives_linux.odin
  79. 1 3
      core/sync/sync_linux.odin
  80. 60 0
      core/sys/unix/syscalls_linux.odin
  81. 48 0
      core/unicode/utf16/utf16.odin
  82. 27 0
      src/build_settings.cpp
  83. 10 3
      src/check_builtin.cpp
  84. 32 4
      src/check_expr.cpp
  85. 7 0
      src/check_stmt.cpp
  86. 3 3
      src/check_type.cpp
  87. 13 0
      src/checker.cpp
  88. 5 2
      src/docs_writer.cpp
  89. 0 1
      src/entity.cpp
  90. 2 0
      src/gb/gb.h
  91. 20 7
      src/llvm_abi.cpp
  92. 17 23
      src/llvm_backend_expr.cpp
  93. 11 15
      src/llvm_backend_opt.cpp
  94. 4 0
      src/llvm_backend_proc.cpp
  95. 1 1
      src/llvm_backend_utility.cpp
  96. 67 21
      src/main.cpp
  97. 9 0
      src/parser.cpp
  98. 27 18
      src/queue.cpp
  99. 4 0
      src/threading.cpp
  100. 4 4
      src/tokenizer.cpp

+ 12 - 2
Makefile

@@ -8,11 +8,21 @@ CC=clang
 OS=$(shell uname)
 
 ifeq ($(OS), Darwin)
+    ARCH=$(shell uname -m)
     LLVM_CONFIG=llvm-config
-    ifneq ($(shell llvm-config --version | grep '^11\.'),)
+
+    # LLVM Version Setting  
+    LLVM_VERSION_PATTERN="^11\."
+    LLVM_VERSION="11"
+    ifeq ($(ARCH), arm64)
+	LLVM_VERSION="13"
+        LLVM_VERSION_PATTERN="^13"
+    endif 
+
+    ifneq ($(shell llvm-config --version | grep $(LLVM_VERSION_PATTERN)),)
         LLVM_CONFIG=llvm-config
     else
-        $(error "Requirement: llvm-config must be version 11")
+        $(error "Requirement: llvm-config must be version $(LLVM_VERSION)")
     endif
 
     LDFLAGS:=$(LDFLAGS) -liconv

+ 29 - 0
bin/nasm/windows/LICENSE

@@ -0,0 +1,29 @@
+NASM is now licensed under the 2-clause BSD license, also known as the
+simplified BSD license.
+
+    Copyright 1996-2010 the NASM Authors - All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following
+    conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+      
+      THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+      CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+      INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+      MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+      DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+      CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+      SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+      NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+      LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+      HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+      CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+      OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+      EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

BIN
bin/nasm/windows/nasm.exe


BIN
bin/nasm/windows/ndisasm.exe


+ 2 - 3
core/bytes/bytes.odin

@@ -5,9 +5,8 @@ import "core:unicode"
 import "core:unicode/utf8"
 
 clone :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> []byte {
-	c := make([]byte, len(s)+1, allocator, loc)
+	c := make([]byte, len(s), allocator, loc)
 	copy(c, s)
-	c[len(s)] = 0
 	return c[:len(s)]
 }
 
@@ -1143,7 +1142,7 @@ fields_proc :: proc(s: []byte, f: proc(rune) -> bool, allocator := context.alloc
 	}
 
 	if start >= 0 {
-		append(&subslices, s[start : end])
+		append(&subslices, s[start : len(s)])
 	}
 
 	return subslices[:]

+ 0 - 216
core/container/array.odin

@@ -1,216 +0,0 @@
-package container
-
-import "core:mem"
-import "core:runtime"
-
-Array :: struct($T: typeid) {
-	data:      ^T,
-	len:       int,
-	cap:       int,
-	allocator: mem.Allocator,
-}
-
-ARRAY_DEFAULT_CAPACITY :: 16
-
-/*
-array_init :: proc {
-	array_init_none,
-	array_init_len,
-	array_init_len_cap,
-}
-array_init
-array_delete
-array_len
-array_cap
-array_space
-array_slice
-array_get
-array_get_ptr
-array_set
-array_reserve
-array_resize
-array_push = array_append :: proc{
-	array_push_back,
-	array_push_back_elems,
-}
-array_push_front
-array_pop_back
-array_pop_front
-array_consume
-array_trim
-array_clear
-array_clone
-array_set_capacity
-array_grow
-*/
-
-
-array_init_none :: proc(a: ^$A/Array, allocator := context.allocator) {
-	array_init_len_cap(a, 0, ARRAY_DEFAULT_CAPACITY, allocator)
-}
-array_init_len :: proc(a: ^$A/Array, len: int, allocator := context.allocator) {
-	array_init_len_cap(a, len, len, allocator)
-}
-array_init_len_cap :: proc(a: ^$A/Array($T), len: int, cap: int, allocator := context.allocator) {
-	a.allocator = allocator
-	a.data = (^T)(mem.alloc(size_of(T)*cap, align_of(T), a.allocator))
-	a.len = len
-	a.cap = cap
-}
-
-array_init :: proc{array_init_none, array_init_len, array_init_len_cap}
-
-array_delete :: proc(a: $A/Array) {
-	mem.free(a.data, a.allocator)
-}
-
-array_len :: proc(a: $A/Array) -> int {
-	return a.len
-}
-
-array_cap :: proc(a: $A/Array) -> int {
-	return a.cap
-}
-
-array_space :: proc(a: $A/Array) -> int {
-	return a.cap - a.len
-}
-
-array_slice :: proc(a: $A/Array($T)) -> []T {
-	s := mem.Raw_Slice{a.data, a.len}
-	return transmute([]T)s
-}
-
-array_cap_slice :: proc(a: $A/Array($T)) -> []T {
-	s := mem.Raw_Slice{a.data, a.cap}
-	return transmute([]T)s
-}
-
-array_get :: proc(a: $A/Array($T), index: int, loc := #caller_location) -> T {
-	runtime.bounds_check_error_loc(loc, index, array_len(a))
-	return (^T)(uintptr(a.data) + size_of(T)*uintptr(index))^
-}
-array_get_ptr :: proc(a: $A/Array($T), index: int, loc := #caller_location) -> ^T {
-	runtime.bounds_check_error_loc(loc, index, array_len(a))
-	return (^T)(uintptr(a.data) + size_of(T)*uintptr(index))
-}
-
-array_set :: proc(a: ^$A/Array($T), index: int, item: T, loc := #caller_location)  {
-	runtime.bounds_check_error_loc(loc, index, array_len(a^))
-	(^T)(uintptr(a.data) + size_of(T)*uintptr(index))^ = item
-}
-
-
-array_reserve :: proc(a: ^$A/Array, capacity: int) {
-	if capacity > a.len {
-		array_set_capacity(a, capacity)
-	}
-}
-
-array_resize :: proc(a: ^$A/Array, length: int) {
-	if length > a.len {
-		array_set_capacity(a, length)
-	}
-	a.len = length
-}
-
-
-
-array_push_back :: proc(a: ^$A/Array($T), item: T) {
-	if array_space(a^) == 0 {
-		array_grow(a)
-	}
-
-	a.len += 1
-	array_set(a, a.len-1, item)
-}
-
-array_push_front :: proc(a: ^$A/Array($T), item: T) {
-	if array_space(a^) == 0 {
-		array_grow(a)
-	}
-
-	a.len += 1
-	data := array_slice(a^)
-	copy(data[1:], data[:])
-	data[0] = item
-}
-
-array_pop_back :: proc(a: ^$A/Array($T), loc := #caller_location) -> T {
-	assert(condition=a.len > 0, loc=loc)
-	item := array_get(a^, a.len-1)
-	a.len -= 1
-	return item
-}
-
-array_pop_front :: proc(a: ^$A/Array($T), loc := #caller_location) -> T {
-	assert(condition=a.len > 0, loc=loc)
-	item := array_get(a^, 0)
-	s := array_slice(a^)
-	copy(s[:], s[1:])
-	a.len -= 1
-	return item
-}
-
-
-array_consume :: proc(a: ^$A/Array($T), count: int, loc := #caller_location) {
-	assert(condition=a.len >= count, loc=loc)
-	a.len -= count
-}
-
-
-array_trim :: proc(a: ^$A/Array($T)) {
-	array_set_capacity(a, a.len)
-}
-
-array_clear :: proc(a: ^$A/Array($T)) {
-	array_resize(a, 0)
-}
-
-array_clone :: proc(a: $A/Array($T), allocator := context.allocator) -> A {
-	res: A
-	array_init(&res, array_len(a), array_len(a), allocator)
-	copy(array_slice(res), array_slice(a))
-	return res
-}
-
-array_push_back_elems :: proc(a: ^$A/Array($T), items: ..T) {
-	if array_space(a^) < len(items) {
-		array_grow(a, a.len + len(items))
-	}
-	offset := a.len
-	data := array_cap_slice(a^)
-	n := copy(data[a.len:], items)
-	a.len += n
-}
-
-array_push   :: proc{array_push_back, array_push_back_elems}
-array_append :: proc{array_push_back, array_push_back_elems}
-
-array_set_capacity :: proc(a: ^$A/Array($T), new_capacity: int) {
-	if new_capacity == a.cap {
-		return
-	}
-
-	if new_capacity < a.len {
-		array_resize(a, new_capacity)
-	}
-
-	new_data: ^T
-	if new_capacity > 0 {
-		if a.allocator.procedure == nil {
-			a.allocator = context.allocator
-		}
-		new_data = (^T)(mem.alloc(size_of(T)*new_capacity, align_of(T), a.allocator))
-		if new_data != nil {
-			mem.copy(new_data, a.data, size_of(T)*a.len)
-		}
-	}
-	mem.free(a.data, a.allocator)
-	a.data = new_data
-	a.cap = new_capacity
-}
-array_grow :: proc(a: ^$A/Array, min_capacity: int = 0) {
-	new_capacity := max(array_len(a^)*2 + 8, min_capacity)
-	array_set_capacity(a, new_capacity)
-}

+ 124 - 0
core/container/bit_array/bit_array.odin

@@ -0,0 +1,124 @@
+package dynamic_bit_array
+
+import "core:intrinsics"
+
+/*
+	Note that these constants are dependent on the backing being a u64.
+*/
+@(private="file")
+INDEX_SHIFT :: 6
+
+@(private="file")
+INDEX_MASK  :: 63
+
+Bit_Array :: struct {
+	bits: [dynamic]u64,
+	bias: int,
+}
+
+/*
+	In:
+		- ba:    ^Bit_Array - a pointer to the Bit Array
+		- index: The bit index. Can be an enum member.
+
+	Out:
+		- res:   The bit you're interested in.
+		- ok:    Whether the index was valid. Returns `false` if the index is smaller than the bias.
+
+	The `ok` return value may be ignored.
+*/
+get :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator) -> (res: bool, ok: bool) {
+	idx := int(index) - ba.bias
+
+	if ba == nil || int(index) < ba.bias { return false, false }
+	context.allocator = allocator
+
+	leg_index := idx >> INDEX_SHIFT
+	bit_index := idx &  INDEX_MASK
+
+	/*
+		If we `get` a bit that doesn't fit in the Bit Array, it's naturally `false`.
+		This early-out prevents unnecessary resizing.
+	*/
+	if leg_index + 1 > len(ba.bits) { return false, true }
+
+	val := u64(1 << uint(bit_index))
+	res = ba.bits[leg_index] & val == val
+
+	return res, true
+}
+
+/*
+	In:
+		- ba:    ^Bit_Array - a pointer to the Bit Array
+		- index: The bit index. Can be an enum member.
+
+	Out:
+		- ok:    Whether or not we managed to set requested bit.
+
+	`set` automatically resizes the Bit Array to accommodate the requested index if needed.
+*/
+set :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator) -> (ok: bool) {
+
+	idx := int(index) - ba.bias
+
+	if ba == nil || int(index) < ba.bias { return false }
+	context.allocator = allocator
+
+	leg_index := idx >> INDEX_SHIFT
+	bit_index := idx &  INDEX_MASK
+
+	resize_if_needed(ba, leg_index) or_return
+
+	ba.bits[leg_index] |= 1 << uint(bit_index)
+	return true
+}
+
+/*
+	A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
+*/
+create :: proc(max_index: int, min_index := 0, allocator := context.allocator) -> (res: Bit_Array, ok: bool) #optional_ok {
+	context.allocator = allocator
+	size_in_bits := max_index - min_index
+
+	if size_in_bits < 1 { return {}, false }
+
+	legs := size_in_bits >> INDEX_SHIFT
+
+	res = Bit_Array{
+		bias = min_index,
+	}
+	return res, resize_if_needed(&res, size_in_bits)
+}
+
+/*
+	Sets all bits to `false`.
+*/
+clear :: proc(ba: ^Bit_Array) {
+	if ba == nil { return }
+	ba.bits = {}
+}
+
+/*
+	Releases the memory used by the Bit Array.
+*/
+destroy :: proc(ba: ^Bit_Array) {
+	if ba == nil { return }
+	delete(ba.bits)
+}
+
+/*
+	Resizes the Bit Array. For internal use.
+	If you want to reserve the memory for a given-sized Bit Array up front, you can use `create`.
+*/
+@(private="file")
+resize_if_needed :: proc(ba: ^Bit_Array, legs: int, allocator := context.allocator) -> (ok: bool) {
+	if ba == nil { return false }
+
+	context.allocator = allocator
+
+	if legs + 1 > len(ba.bits) {
+		resize(&ba.bits, legs + 1)
+	}
+	return len(ba.bits) > legs
+}

+ 52 - 0
core/container/bit_array/doc.odin

@@ -0,0 +1,52 @@
+package dynamic_bit_array
+
+/*
+	The Bit Array can be used in several ways:
+
+	-- By default you don't need to instantiate a Bit Array:
+
+		package test
+
+		import "core:fmt"
+		import "core:container/bit_array"
+
+		main :: proc() {
+			using bit_array
+
+			bits: Bit_Array
+
+			// returns `true`
+			fmt.println(set(&bits, 42))
+
+			// returns `false`, `false`, because this Bit Array wasn't created to allow negative indices.
+			was_set, was_retrieved := get(&bits, -1)
+			fmt.println(was_set, was_retrieved) 
+		}
+
+	-- A Bit Array can optionally allow for negative indices, if the mininum value was given during creation:
+
+		package test
+
+		import "core:fmt"
+		import "core:container/bit_array"
+
+		main :: proc() {
+			Foo :: enum int {
+				Negative_Test = -42,
+				Bar           = 420,
+				Leaves        = 69105,
+			}
+
+			using bit_array
+
+			bits := create(int(max(Foo)), int(min(Foo)))
+			defer destroy(&bits)
+
+			fmt.printf("Set(Bar):           %v\n",     set(&bits, Foo.Bar))
+			fmt.printf("Get(Bar):           %v, %v\n", get(&bits, Foo.Bar))
+			fmt.printf("Set(Negative_Test): %v\n",     set(&bits, Foo.Negative_Test))
+			fmt.printf("Get(Leaves):        %v, %v\n", get(&bits, Foo.Leaves))
+			fmt.printf("Get(Negative_Test): %v, %v\n", get(&bits, Foo.Negative_Test))
+			fmt.printf("Freed.\n")
+		}
+*/

+ 0 - 80
core/container/bloom_filter.odin

@@ -1,80 +0,0 @@
-package container
-
-import "core:mem"
-
-Bloom_Hash_Proc :: #type proc(data: []byte) -> u32
-
-Bloom_Hash :: struct {
-	hash_proc: Bloom_Hash_Proc,
-	next:     ^Bloom_Hash,
-}
-
-Bloom_Filter :: struct {
-	allocator: mem.Allocator,
-	hash:      ^Bloom_Hash,
-	bits:      []byte,
-}
-
-bloom_filter_init :: proc(b: ^Bloom_Filter, size: int, allocator := context.allocator) {
-	b.allocator = allocator
-	b.bits = make([]byte, size, allocator)
-}
-
-bloom_filter_destroy :: proc(b: ^Bloom_Filter) {
-	context.allocator = b.allocator
-	delete(b.bits)
-	for b.hash != nil {
-		hash := b.hash
-		b.hash = b.hash.next
-		free(hash)
-	}
-}
-
-bloom_filter_add_hash_proc :: proc(b: ^Bloom_Filter, hash_proc: Bloom_Hash_Proc) {
-	context.allocator = b.allocator
-	h := new(Bloom_Hash)
-	h.hash_proc = hash_proc
-
-	head := &b.hash
-	for head^ != nil {
-		head = &(head^.next)
-	}
-	head^ = h
-}
-
-bloom_filter_add :: proc(b: ^Bloom_Filter, item: []byte) {
-	#no_bounds_check for h := b.hash; h != nil; h = h.next {
-		hash := h.hash_proc(item)
-		hash %= u32(len(b.bits) * 8)
-		b.bits[hash >> 3] |= 1 << (hash & 3)
-	}
-}
-
-bloom_filter_add_string :: proc(b: ^Bloom_Filter, item: string) {
-	bloom_filter_add(b, transmute([]byte)item)
-}
-
-bloom_filter_add_raw :: proc(b: ^Bloom_Filter, data: rawptr, size: int) {
-	item := mem.slice_ptr((^byte)(data), size)
-	bloom_filter_add(b, item)
-}
-
-bloom_filter_test :: proc(b: ^Bloom_Filter, item: []byte) -> bool {
-	#no_bounds_check for h := b.hash; h != nil; h = h.next {
-		hash := h.hash_proc(item)
-		hash %= u32(len(b.bits) * 8)
-		if (b.bits[hash >> 3] & (1 << (hash & 3)) == 0) {
-			return false
-		}
-	}
-	return true
-}
-
-bloom_filter_test_string :: proc(b: ^Bloom_Filter, item: string) -> bool {
-	return bloom_filter_test(b, transmute([]byte)item)
-}
-
-bloom_filter_test_raw :: proc(b: ^Bloom_Filter, data: rawptr, size: int) -> bool {
-	item := mem.slice_ptr((^byte)(data), size)
-	return bloom_filter_test(b, item)
-}

+ 0 - 377
core/container/map.odin

@@ -1,377 +0,0 @@
-package container
-
-import "core:intrinsics"
-_ :: intrinsics
-
-
-Map :: struct($Key, $Value: typeid) where intrinsics.type_is_valid_map_key(Key) {
-	hash: Array(int),
-	entries: Array(Map_Entry(Key, Value)),
-}
-
-Map_Entry :: struct($Key, $Value: typeid) where intrinsics.type_is_valid_map_key(Key) {
-	hash:  uintptr,
-	next:  int,
-	key:   Key,
-	value: Value,
-}
-
-
-/*
-map_init :: proc{
-	map_init_none,
-	map_init_cap,
-}
-map_delete
-
-map_has
-map_get
-map_get_default
-map_get_ptr
-map_set
-map_remove
-map_reserve
-map_clear
-
-// Multi Map
-
-multi_map_find_first
-multi_map_find_next
-multi_map_count
-multi_map_get :: proc{
-	multi_map_get_array,
-	multi_map_get_slice,
-};
-multi_map_get_as_slice
-multi_map_insert
-multi_map_remove
-multi_map_remove_all
-
-*/
-
-map_init :: proc{map_init_none, map_init_cap}
-
-map_init_none :: proc(m: ^$M/Map($Key, $Value), allocator := context.allocator) {
-	m.hash.allocator = allocator
-	m.entries.allocator = allocator
-}
-
-map_init_cap :: proc(m: ^$M/Map($Key, $Value), cap: int, allocator := context.allocator) {
-	m.hash.allocator = allocator
-	m.entries.allocator = allocator
-	map_reserve(m, cap)
-}
-
-map_delete :: proc(m: $M/Map($Key, $Value)) {
-	array_delete(m.hash)
-	array_delete(m.entries)
-}
-
-
-map_has :: proc(m: $M/Map($Key, $Value), key: Key) -> bool {
-	return _map_find_or_fail(m, key) >= 0
-}
-
-map_get :: proc(m: $M/Map($Key, $Value), key: Key) -> (res: Value, ok: bool) #optional_ok {
-	i := _map_find_or_fail(m, key)
-	if i < 0 {
-		return {}, false
-	}
-	return array_get(m.entries, i).value, true
-}
-
-map_get_default :: proc(m: $M/Map($Key, $Value), key: Key, default: Value) -> (res: Value, ok: bool) #optional_ok {
-	i := _map_find_or_fail(m, key)
-	if i < 0 {
-		return default, false
-	}
-	return array_get(m.entries, i).value, true
-}
-
-map_get_ptr :: proc(m: $M/Map($Key, $Value), key: Key) -> ^Value {
-	i := _map_find_or_fail(m, key)
-	if i < 0 {
-		return nil
-	}
-	return array_get_ptr(m.entries, i).value
-}
-
-map_set :: proc(m: ^$M/Map($Key, $Value), key: Key, value: Value) {
-	if array_len(m.hash) == 0 {
-		_map_grow(m)
-	}
-
-	i := _map_find_or_make(m, key)
-	array_get_ptr(m.entries, i).value = value
-	if _map_full(m^) {
-		_map_grow(m)
-	}
-}
-
-map_remove :: proc(m: ^$M/Map($Key, $Value), key: Key) {
-	fr := _map_find_key(m^, key)
-	if fr.entry_index >= 0 {
-		_map_erase(m, fr)
-	}
-}
-
-
-map_reserve :: proc(m: ^$M/Map($Key, $Value), new_size: int) {
-	nm: M
-	map_init(&nm, m.hash.allocator)
-	array_resize(&nm.hash, new_size)
-	array_reserve(&nm.entries, array_len(m.entries))
-
-	for i in 0..<new_size {
-		array_set(&nm.hash, i, -1)
-	}
-	for i in 0..<array_len(m.entries) {
-		e := array_get(m.entries, i)
-		multi_map_insert(&nm, e.key, e.value)
-	}
-
-	map_delete(m^)
-	m^ = nm
-}
-
-map_clear :: proc(m: ^$M/Map($Key, $Value)) {
-	array_clear(&m.hash)
-	array_clear(&m.entries)
-}
-
-
-
-multi_map_find_first :: proc(m: $M/Map($Key, $Value), key: Key) -> ^Map_Entry(Key, Value) {
-	i := _map_find_or_fail(m, key)
-	if i < 0 {
-		return nil
-	}
-	return array_get_ptr(m.entries, i)
-}
-
-multi_map_find_next :: proc(m: $M/Map($Key, $Value), e: ^Map_Entry(Key, Value)) -> ^Map_Entry(Key, Value) {
-	i := e.next
-	for i >= 0 {
-		it := array_get_ptr(m.entries, i)
-		if it.hash == e.hash && it.key == e.key {
-			return it
-		}
-		i = it.next
-	}
-	return nil
-}
-
-multi_map_count :: proc(m: $M/Map($Key, $Value), key: Key) -> int {
-	n := 0
-	e := multi_map_find_first(m, key)
-	for e != nil {
-		n += 1
-		e = multi_map_find_next(m, e)
-	}
-	return n
-}
-
-multi_map_get :: proc{multi_map_get_array, multi_map_get_slice}
-
-multi_map_get_array :: proc(m: $M/Map($Key, $Value), key: Key, items: ^Array(Value)) {
-	if items == nil {
-		return
-	}
-	e := multi_map_find_first(m, key)
-	for e != nil {
-		array_append(items, e.value)
-		e = multi_map_find_next(m, e)
-	}
-}
-
-multi_map_get_slice :: proc(m: $M/Map($Key, $Value), key: Key, items: []Value) {
-	e := multi_map_find_first(m, key)
-	i := 0
-	for e != nil && i < len(items) {
-		items[i] = e.value
-		i += 1
-		e = multi_map_find_next(m, e)
-	}
-}
-
-multi_map_get_as_slice :: proc(m: $M/Map($Key, $Value), key: Key) -> []Value {
-	items: Array(Value)
-	array_init(&items, 0)
-
-	e := multi_map_find_first(m, key)
-	for e != nil {
-		array_append(&items, e.value)
-		e = multi_map_find_next(m, e)
-	}
-
-	return array_slice(items)
-}
-
-
-multi_map_insert :: proc(m: ^$M/Map($Key, $Value), key: Key, value: Value) {
-	if array_len(m.hash) == 0 {
-		_map_grow(m)
-	}
-
-	i := _map_make(m, key)
-	array_get_ptr(m.entries, i).value = value
-	if _map_full(m^) {
-		_map_grow(m)
-	}
-}
-
-multi_map_remove :: proc(m: ^$M/Map($Key, $Value), e: ^Map_Entry(Key, Value)) {
-	fr := _map_find_entry(m, e)
-	if fr.entry_index >= 0 {
-		_map_erase(m, fr)
-	}
-}
-
-multi_map_remove_all :: proc(m: ^$M/Map($Key, $Value), key: Key) {
-	for map_exist(m^, key) {
-		map_remove(m, key)
-	}
-}
-
-
-/// Internal
-
-
-Map_Find_Result :: struct {
-	hash_index:  int,
-	entry_prev:  int,
-	entry_index: int,
-}
-
-_map_add_entry :: proc(m: ^$M/Map($Key, $Value), key: Key) -> int where intrinsics.type_is_valid_map_key(Key) {
-	hasher := intrinsics.type_hasher_proc(Key)
-
-	e: Map_Entry(Key, Value)
-	e.key = key
-	e.hash = hasher(&e.key, 0)
-	e.next = -1
-	idx := array_len(m.entries)
-	array_push(&m.entries, e)
-	return idx
-}
-
-_map_erase :: proc(m: ^$M/Map, fr: Map_Find_Result) {
-	if fr.entry_prev < 0 {
-		array_set(&m.hash, fr.hash_index, array_get(m.entries, fr.entry_index).next)
-	} else {
-		array_get_ptr(m.entries, fr.entry_prev).next = array_get(m.entries, fr.entry_index).next
-	}
-
-	if fr.entry_index == array_len(m.entries)-1 {
-		array_pop_back(&m.entries)
-		return
-	}
-
-	array_set(&m.entries, fr.entry_index, array_get(m.entries, array_len(m.entries)-1))
-	last := _map_find_key(m^, array_get(m.entries, fr.entry_index).key)
-
-	if last.entry_prev < 0 {
-		array_get_ptr(m.entries, last.entry_prev).next = fr.entry_index
-	} else {
-		array_set(&m.hash, last.hash_index, fr.entry_index)
-	}
-}
-
-
-_map_find_key :: proc(m: $M/Map($Key, $Value), key: Key) -> Map_Find_Result where intrinsics.type_is_valid_map_key(Key) {
-	fr: Map_Find_Result
-	fr.hash_index = -1
-	fr.entry_prev = -1
-	fr.entry_index = -1
-
-	if array_len(m.hash) == 0 {
-		return fr
-	}
-
-	hasher := intrinsics.type_hasher_proc(Key)
-
-	key := key
-	hash := hasher(&key, 0)
-
-	fr.hash_index = int(hash % uintptr(array_len(m.hash)))
-	fr.entry_index = array_get(m.hash, fr.hash_index)
-	for fr.entry_index >= 0 {
-		it := array_get_ptr(m.entries, fr.entry_index)
-		if it.hash == hash && it.key == key {
-			return fr
-		}
-		fr.entry_prev = fr.entry_index
-		fr.entry_index = it.next
-	}
-	return fr
-}
-
-_map_find_entry :: proc(m: ^$M/Map($Key, $Value), e: ^Map_Entry(Key, Value)) -> Map_Find_Result {
-	fr: Map_Find_Result
-	fr.hash_index = -1
-	fr.entry_prev = -1
-	fr.entry_index = -1
-
-	if array_len(m.hash) == 0 {
-		return fr
-	}
-
-	fr.hash_index = int(e.hash % uintptr(array_len(m.hash)))
-	fr.entry_index = array_get(m.hash, fr.hash_index)
-	for fr.entry_index >= 0 {
-		it := array_get_ptr(m.entries, fr.entry_index)
-		if it == e {
-			return fr
-		}
-		fr.entry_prev = fr.entry_index
-		fr.entry_index = it.next
-	}
-	return fr
-}
-
-_map_find_or_fail :: proc(m: $M/Map($Key, $Value), key: Key) -> int {
-	return _map_find_key(m, key).entry_index
-}
-_map_find_or_make :: proc(m: ^$M/Map($Key, $Value), key: Key) -> int {
-	fr := _map_find_key(m^, key)
-	if fr.entry_index >= 0 {
-		return fr.entry_index
-	}
-
-	i := _map_add_entry(m, key)
-	if fr.entry_prev < 0 {
-		array_set(&m.hash, fr.hash_index, i)
-	} else {
-		array_get_ptr(m.entries, fr.entry_prev).next = i
-	}
-	return i
-}
-
-
-_map_make :: proc(m: ^$M/Map($Key, $Value), key: Key) -> int {
-	fr := _map_find_key(m^, key)
-	i := _map_add_entry(m, key)
-
-	if fr.entry_prev < 0 {
-		array_set(&m.hash, fr.hash_index, i)
-	} else {
-		array_get_ptr(m.entries, fr.entry_prev).next = i
-	}
-
-	array_get_ptr(m.entries, i).next = fr.entry_index
-
-	return i
-}
-
-
-_map_full :: proc(m: $M/Map($Key, $Value)) -> bool {
-	// TODO(bill): Determine good max load factor
-	return array_len(m.entries) >= (array_len(m.hash) / 4)*3
-}
-
-_map_grow :: proc(m: ^$M/Map($Key, $Value)) {
-	new_size := array_len(m.entries) * 4 + 7 // TODO(bill): Determine good grow rate
-	map_reserve(m, new_size)
-}
-
-

+ 0 - 121
core/container/priority_queue.odin

@@ -1,121 +0,0 @@
-package container
-
-Priority_Queue :: struct($T: typeid) {
-	data: Array(T),
-	len: int,
-	priority: proc(item: T) -> int,
-}
-
-priority_queue_init_none :: proc(q: ^$Q/Priority_Queue($T), f: proc(item: T) -> int, allocator := context.allocator) {
-	queue_init_len(q, f, 0, allocator)
-}
-priority_queue_init_len :: proc(q: ^$Q/Priority_Queue($T), f: proc(item: T) -> int, len: int, allocator := context.allocator) {
-	queue_init_len_cap(q, f, 0, 16, allocator)
-}
-priority_queue_init_len_cap :: proc(q: ^$Q/Priority_Queue($T), f: proc(item: T) -> int, len: int, cap: int, allocator := context.allocator) {
-	array_init(&q.data, len, cap, allocator)
-	q.len = len
-	q.priority = f
-}
-
-priority_queue_init :: proc{priority_queue_init_none, priority_queue_init_len, priority_queue_init_len_cap}
-
-
-priority_queue_delete :: proc(q: $Q/Priority_Queue($T)) {
-	array_delete(q.data)
-}
-
-priority_queue_clear :: proc(q: ^$Q/Priority_Queue($T)) {
-	q.len = 0
-}
-
-priority_queue_len :: proc(q: $Q/Priority_Queue($T)) -> int {
-	return q.len
-}
-
-priority_queue_cap :: proc(q: $Q/Priority_Queue($T)) -> int {
-	return array_cap(q.data)
-}
-
-priority_queue_space :: proc(q: $Q/Priority_Queue($T)) -> int {
-	return array_len(q.data) - q.len
-}
-
-priority_queue_reserve :: proc(q: ^$Q/Priority_Queue($T), capacity: int) {
-	if capacity > q.len {
-		array_resize(&q.data, new_capacity)
-	}
-}
-
-priority_queue_resize :: proc(q: ^$Q/Priority_Queue($T), length: int) {
-	if length > q.len {
-		array_resize(&q.data, new_capacity)
-	}
-	q.len = length
-}
-
-_priority_queue_grow :: proc(q: ^$Q/Priority_Queue($T), min_capacity: int = 0) {
-	new_capacity := max(array_len(q.data)*2 + 8, min_capacity)
-	array_resize(&q.data, new_capacity)
-}
-
-
-priority_queue_push :: proc(q: ^$Q/Priority_Queue($T), item: T) {
-	if array_len(q.data) - q.len == 0 {
-		_priority_queue_grow(q)
-	}
-
-	s := array_slice(q.data)
-	s[q.len] = item
-
-	i := q.len
-	for i > 0 {
-		p := (i - 1) / 2
-		if q.priority(s[p]) <= q.priority(item) { 
-			break 
-		}
-		s[i] = s[p]
-		i = p
-	}
-
-	q.len += 1
-	if q.len > 0 { 
-		s[i] = item 
-	} 
-}
-
-
-
-priority_queue_pop :: proc(q: ^$Q/Priority_Queue($T)) -> T {
-	assert(q.len > 0)
-
-	s := array_slice(q.data)
-	min := s[0]
-	root := s[q.len-1]
-	q.len -= 1
-
-	i := 0
-	for i * 2 + 1 < q.len {
-		a := i * 2 + 1
-		b := i * 2 + 2
-		c := b < q.len && q.priority(s[b]) < q.priority(s[a]) ? b : a
-
-		if q.priority(s[c]) >= q.priority(root) {
-			break
-		}
-		s[i] = s[c]
-		i = c
-	}
-
-	if q.len > 0 {
-		s[i] = root
-	}
-	return min
-}
-
-priority_queue_peek :: proc(q: ^$Q/Priority_Queue($T)) -> T {
-	assert(q.len > 0)
-
-	s := array_slice(q.data)
-	return s[0]
-}

+ 143 - 0
core/container/priority_queue/priority_queue.odin

@@ -0,0 +1,143 @@
+package container_priority_queue
+
+import "core:builtin"
+
+Priority_Queue :: struct($T: typeid) {
+	queue: [dynamic]T,
+	
+	less:  proc(a, b: T) -> bool,
+	swap:  proc(q: []T, i, j: int),
+}
+
+DEFAULT_CAPACITY :: 16
+
+default_swap_proc :: proc($T: typeid) -> proc(q: []T, i, j: int) {
+	return proc(q: []T, i, j: int) {
+		q[i], q[j] = q[j], q[i]
+	}
+}
+
+init :: proc(pq: ^$Q/Priority_Queue($T), less: proc(a, b: T) -> bool, swap: proc(q: []T, i, j: int), capacity := DEFAULT_CAPACITY, allocator := context.allocator) {
+	if pq.queue.allocator.procedure == nil {
+		pq.queue.allocator = allocator
+	}
+	reserve(pq, capacity)
+	pq.less = less
+	pq.swap = swap
+}
+
+init_from_dynamic_array :: proc(pq: ^$Q/Priority_Queue($T), queue: [dynamic]T, less: proc(a, b: T) -> bool, swap: proc(q: []T, i, j: int)) {
+	pq.queue = queue
+	pq.less = less
+	pq.swap = swap
+	n := builtin.len(pq.queue)
+	for i := n/2 - 1; i >= 0; i -= 1 {
+		_shift_down(pq, i, n)
+	}
+}
+
+destroy :: proc(pq: ^$Q/Priority_Queue($T)) {
+	clear(pq)
+	delete(pq.queue)
+}
+
+reserve :: proc(pq: ^$Q/Priority_Queue($T), capacity: int) {
+	builtin.reserve(&pq.queue, capacity)
+}
+clear :: proc(pq: ^$Q/Priority_Queue($T)) {
+	builtin.clear(&pq.queue)
+}
+len :: proc(pq: $Q/Priority_Queue($T)) -> int {
+	return builtin.len(pq.queue)
+}
+cap :: proc(pq: $Q/Priority_Queue($T)) -> int {
+	return builtin.cap(pq.queue)
+}
+
+_shift_down :: proc(pq: ^$Q/Priority_Queue($T), i0, n: int) -> bool {
+	// O(n log n)
+	if 0 > i0 || i0 > n {
+		return false
+	}
+	
+	i := i0
+	queue := pq.queue[:]
+	
+	for {
+		j1 := 2*i + 1
+		if j1 < 0 || j1 >= n {
+			break
+		}
+		j := j1
+		if j2 := j1+1; j2 < n && pq.less(queue[j2], queue[j1]) {
+			j = j2
+		}
+		if !pq.less(queue[j], queue[i]) {
+			break
+		}
+		
+		pq.swap(queue, i, j)
+		i = j
+	}
+	return i > i0
+}
+
+_shift_up :: proc(pq: ^$Q/Priority_Queue($T), j: int) {
+	j := j
+	queue := pq.queue[:]
+	n := builtin.len(queue)
+	for 0 <= j {
+		i := (j-1)/2
+		if i == j || !pq.less(queue[j], queue[i]) {
+			break
+		}
+		pq.swap(queue, i, j)
+		j = i
+	}
+}
+
+// NOTE(bill): When an element at index 'i' has changed its value, this will fix the
+// the heap ordering. This is using a basic "heapsort" with shift up and a shift down parts.
+fix :: proc(pq: ^$Q/Priority_Queue($T), i: int) {
+	if !_shift_down(pq, i, builtin.len(pq.queue)) {
+		_shift_up(pq, i)
+	}
+}
+
+push :: proc(pq: ^$Q/Priority_Queue($T), value: T) {
+	append(&pq.queue, value)
+	_shift_up(pq, builtin.len(pq.queue)-1)
+}
+
+pop :: proc(pq: ^$Q/Priority_Queue($T), loc := #caller_location) -> (value: T) {
+	assert(condition=builtin.len(pq.queue)>0, loc=loc)
+	
+	n := builtin.len(pq.queue)-1
+	pq.swap(pq.queue[:], 0, n)
+	_shift_down(pq, 0, n)
+	return builtin.pop(&pq.queue)
+}
+
+pop_safe :: proc(pq: ^$Q/Priority_Queue($T), loc := #caller_location) -> (value: T, ok: bool) {
+	if builtin.len(pq.queue) > 0 {
+		n := builtin.len(pq.queue)-1
+		pq.swap(pq.queue[:], 0, n)
+		_shift_down(pq, 0, n)
+		return builtin.pop_safe(&pq.queue)
+	}
+	return
+}
+
+remove :: proc(pq: ^$Q/Priority_Queue($T), i: int) -> (value: T, ok: bool) {
+	n := builtin.len(pq.queue)
+	if 0 <= i && i < n {
+		if n != i {
+			pq.swap(pq.queue[:], i, n)
+			_shift_down(pq, i, n)
+			_shift_up(pq, i)
+		}
+		value, ok = builtin.pop_safe(&pq.queue)
+	}
+	return
+}
+

+ 0 - 175
core/container/queue.odin

@@ -1,175 +0,0 @@
-package container
-
-Queue :: struct($T: typeid) {
-	data: Array(T),
-	len: int,
-	offset: int,
-}
-
-/*
-queue_init :: proc{
-	queue_init_none,
-	queue_init_len,
-	queue_init_len_cap,
-}
-queue_delete
-queue_clear
-queue_len
-queue_cap
-queue_space
-queue_get
-queue_set
-queue_reserve
-queue_resize
-queue_push :: proc{
-	queue_push_back, 
-	queue_push_elems,
-};
-queue_push_front
-queue_pop_front
-queue_pop_back
-queue_consume
-*/
-
-queue_init_none :: proc(q: ^$Q/Queue($T), allocator := context.allocator) {
-	queue_init_len(q, 0, allocator)
-}
-queue_init_len :: proc(q: ^$Q/Queue($T), len: int, allocator := context.allocator) {
-	queue_init_len_cap(q, 0, 16, allocator)
-}
-queue_init_len_cap :: proc(q: ^$Q/Queue($T), len: int, cap: int, allocator := context.allocator) {
-	array_init(&q.data, len, cap, allocator)
-	q.len = len
-	q.offset = 0
-}
-
-queue_init :: proc{queue_init_none, queue_init_len, queue_init_len_cap}
-
-queue_delete :: proc(q: $Q/Queue($T)) {
-	array_delete(q.data)
-}
-
-queue_clear :: proc(q: ^$Q/Queue($T)) {
-	q.len = 0
-}
-
-queue_len :: proc(q: $Q/Queue($T)) -> int {
-	return q.len
-}
-
-queue_cap :: proc(q: $Q/Queue($T)) -> int {
-	return array_cap(q.data)
-}
-
-queue_space :: proc(q: $Q/Queue($T)) -> int {
-	return array_len(q.data) - q.len
-}
-
-queue_get :: proc(q: $Q/Queue($T), index: int) -> T {
-	i := (index + q.offset) % array_len(q.data)
-	data := array_slice(q.data)
-	return data[i]
-}
-
-queue_set :: proc(q: ^$Q/Queue($T), index: int, item: T)  {
-	i := (index + q.offset) % array_len(q.data)
-	data := array_slice(q.data)
-	data[i] = item
-}
-
-
-queue_reserve :: proc(q: ^$Q/Queue($T), capacity: int) {
-	if capacity > q.len {
-		_queue_increase_capacity(q, capacity)
-	}
-}
-
-queue_resize :: proc(q: ^$Q/Queue($T), length: int) {
-	if length > q.len {
-		_queue_increase_capacity(q, length)
-	}
-	q.len = length
-}
-
-queue_push_back :: proc(q: ^$Q/Queue($T), item: T) {
-	if queue_space(q^) == 0 {
-		_queue_grow(q)
-	}
-
-	queue_set(q, q.len, item)
-	q.len += 1
-}
-
-queue_push_front :: proc(q: ^$Q/Queue($T), item: T) {
-	if queue_space(q^) == 0 {
-		_queue_grow(q)
-	}
-
-	q.offset = (q.offset - 1 + array_len(q.data)) % array_len(q.data)
-	q.len += 1
-	queue_set(q, 0, item)
-}
-
-queue_pop_front :: proc(q: ^$Q/Queue($T)) -> T {
-	assert(q.len > 0)
-	item := queue_get(q^, 0)
-	q.offset = (q.offset + 1) % array_len(q.data)
-	q.len -= 1
-	if q.len == 0 {
-		q.offset = 0
-	}
-	return item
-}
-
-queue_pop_back :: proc(q: ^$Q/Queue($T)) -> T {
-	assert(q.len > 0)
-	item := queue_get(q^, q.len-1)
-	q.len -= 1
-	return item
-}
-
-queue_consume :: proc(q: ^$Q/Queue($T), count: int) {
-	q.offset = (q.offset + count) & array_len(q.data)
-	q.len -= count
-}
-
-
-queue_push_elems :: proc(q: ^$Q/Queue($T), items: ..T) {
-	if queue_space(q^) < len(items) {
-		_queue_grow(q, q.len + len(items))
-	}
-	size := array_len(q.data)
-	insert := (q.offset + q.len) % size
-
-	to_insert := len(items)
-	if insert + to_insert > size {
-		to_insert = size - insert
-	}
-
-	the_items := items[:]
-
-	data := array_slice(q.data)
-
-	q.len += copy(data[insert:][:to_insert], the_items)
-	the_items = the_items[to_insert:]
-	q.len += copy(data[:], the_items)
-}
-
-queue_push :: proc{queue_push_back, queue_push_elems}
-
-
-
-_queue_increase_capacity :: proc(q: ^$Q/Queue($T), new_capacity: int) {
-	end := array_len(q.data)
-	array_resize(&q.data, new_capacity)
-	if q.offset + q.len > end {
-		end_items := q.len + end
-		data := array_slice(q.data)
-		copy(data[new_capacity-end_items:][:end_items], data[q.offset:][:end_items])
-		q.offset += new_capacity - end
-	}
-}
-_queue_grow :: proc(q: ^$Q/Queue($T), min_capacity: int = 0) {
-	new_capacity := max(array_len(q.data)*2 + 8, min_capacity)
-	_queue_increase_capacity(q, new_capacity)
-}

+ 205 - 0
core/container/queue/queue.odin

@@ -0,0 +1,205 @@
+package container_queue
+
+import "core:builtin"
+import "core:runtime"
+
+// Dynamically resizable double-ended queue/ring-buffer
+Queue :: struct($T: typeid) {
+	data:   [dynamic]T,
+	len:    uint,
+	offset: uint,
+}
+
+DEFAULT_CAPACITY :: 16
+
+// Procedure to initialize a queue
+init :: proc(q: ^$Q/Queue($T), capacity := DEFAULT_CAPACITY, allocator := context.allocator) -> bool {
+	if q.data.allocator.procedure == nil {
+		q.data.allocator = allocator
+	}
+	clear(q)
+	return reserve(q, capacity)
+}
+
+// Procedure to initialize a queue from a fixed backing slice
+init_from_slice :: proc(q: ^$Q/Queue($T), backing: []T) -> bool {
+	clear(q)
+	q.data = transmute([dynamic]T)runtime.Raw_Dynamic_Array{
+		data = raw_data(backing),
+		len = builtin.len(backing),
+		cap = builtin.len(backing),
+		allocator = {procedure=runtime.nil_allocator_proc, data=nil},
+	}
+	return true
+}
+
+// Procedure to destroy a queue
+destroy :: proc(q: ^$Q/Queue($T)) {
+	delete(q.data)
+}
+
+// The length of the queue
+len :: proc(q: $Q/Queue($T)) -> int {
+	return int(q.len)
+}
+
+// The current capacity of the queue
+cap :: proc(q: $Q/Queue($T)) -> int {
+	return builtin.len(q.data)
+}
+
+// Remaining space in the queue (cap-len)
+space :: proc(q: $Q/Queue($T)) -> int {
+	return builtin.len(q.data) - int(q.len)
+}
+
+// Reserve enough space for at least the specified capacity
+reserve :: proc(q: ^$Q/Queue($T), capacity: int) -> bool {
+	if uint(capacity) > q.len {
+		return _grow(q, uint(capacity)) 
+	}
+	return true
+}
+
+
+get :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> T {
+	runtime.bounds_check_error_loc(loc, i, builtin.len(q.data))
+
+	idx := (uint(i)+q.offset)%builtin.len(q.data)
+	return q.data[idx]
+}
+set :: proc(q: ^$Q/Queue($T), #any_int i: int, val: T, loc := #caller_location) {
+	runtime.bounds_check_error_loc(loc, i, builtin.len(q.data))
+	
+	idx := (uint(i)+q.offset)%builtin.len(q.data)
+	q.data[idx] = val
+}
+get_ptr :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> ^T {
+	runtime.bounds_check_error_loc(loc, i, builtin.len(q.data))
+	
+	idx := (uint(i)+q.offset)%builtin.len(q.data)
+	return &q.data[idx]
+}
+
+// Push an element to the back of the queue
+push_back :: proc(q: ^$Q/Queue($T), elem: T) -> bool {
+	if space(q^) == 0 {
+		_grow(q) or_return
+	}
+	q.data[q.len] = elem
+	q.len += 1
+	return true
+}
+
+// Push an element to the front of the queue
+push_front :: proc(q: ^$Q/Queue($T), elem: T) -> bool {
+	if space(q^) == 0 {
+		_grow(q) or_return
+	}	
+	q.offset = uint(q.offset - 1 + builtin.len(q.data)) % builtin.len(q.data)
+	q.len += 1
+	q.data[q.offset] = elem
+	return true
+}
+
+
+// Pop an element from the back of the queue
+pop_back :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> (elem: T) {
+	assert(condition=q.len > 0, loc=loc)
+	q.len -= 1
+	idx := (q.offset+uint(q.len))%builtin.len(q.data)
+	elem = q.data[idx]
+	return
+}
+// Safely pop an element from the back of the queue
+pop_back_safe :: proc(q: ^$Q/Queue($T)) -> (elem: T, ok: bool) {
+	if q.len > 0 {
+		q.len -= 1
+		idx := (q.offset+uint(q.len))%builtin.len(q.data)
+		elem = q.data[idx]
+		ok = true
+	}
+	return
+}
+
+// Pop an element from the front of the queue
+pop_front :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> (elem: T) {
+	assert(condition=q.len > 0, loc=loc)
+	elem = q.data[q.offset]
+	q.len -= 1
+	return
+}
+// Safely pop an element from the front of the queue
+pop_front_safe :: proc(q: ^$Q/Queue($T)) -> (elem: T, ok: bool) {
+	if q.len > 0 {
+		elem = q.data[q.offset]
+		q.len -= 1
+		ok = true
+	}
+	return
+}
+
+// Push multiple elements to the front of the queue
+push_back_elems :: proc(q: ^$Q/Queue($T), elems: ..T) -> bool {
+	n := uint(builtin.len(elems))
+	if space(q^) < int(n) {
+		_grow(q, q.len + n) or_return
+	}
+	
+	sz := uint(builtin.len(q.data))
+	insert_from := (q.offset + q.len) % sz
+	insert_to := n
+	if insert_from + insert_to > sz {
+		insert_to = sz - insert_from
+	}
+	copy(q.data[insert_from:], elems[:insert_to])
+	copy(q.data[:insert_from], elems[insert_to:])
+	q.len += n
+	return true
+}
+
+// Consume `n` elements from the front of the queue
+consume_front :: proc(q: ^$Q/Queue($T), n: int, loc := #caller_location) {
+	assert(condition=int(q.len) >= n, loc=loc)
+	if n > 0 {
+		nu := uint(n)
+		q.offset = (q.offset + nu) % builtin.len(q.data)
+		q.len -= nu	
+	}
+}
+
+// Consume `n` elements from the back of the queue
+consume_back :: proc(q: ^$Q/Queue($T), n: int, loc := #caller_location) {
+	assert(condition=int(q.len) >= n, loc=loc)
+	if n > 0 {
+		q.len -= uint(n)
+	}
+}
+
+
+
+append_elem  :: push_back
+append_elems :: push_back_elems
+push   :: proc{push_back, push_back_elems}
+append :: proc{push_back, push_back_elems}
+
+
+// Clear the contents of the queue
+clear :: proc(q: ^$Q/Queue($T)) {
+	q.len = 0
+	q.offset = 0
+}
+
+
+// Internal growinh procedure
+_grow :: proc(q: ^$Q/Queue($T), min_capacity: uint = 0) -> bool {
+	new_capacity := max(min_capacity, uint(8), uint(builtin.len(q.data))*2)
+	n := uint(builtin.len(q.data))
+	builtin.resize(&q.data, int(new_capacity)) or_return
+	if q.offset + q.len > n {
+		diff := n - q.offset
+		copy(q.data[new_capacity-diff:], q.data[q.offset:][:diff])
+		q.offset += new_capacity - n
+	}
+	return true
+}

+ 0 - 74
core/container/ring.odin

@@ -1,74 +0,0 @@
-package container
-
-
-Ring :: struct($T: typeid) {
-	next, prev: ^Ring(T),
-	value: T,
-}
-
-ring_init :: proc(r: ^$R/Ring) -> ^R {
-	r.prev, r.next = r, r
-	return r
-}
-
-ring_next :: proc(r: ^$R/Ring) -> ^R {
-	if r.next == nil {
-		return ring_init(r)
-	}
-	return r.next
-}
-ring_prev :: proc(r: ^$R/Ring) -> ^R {
-	if r.prev == nil {
-		return ring_init(r)
-	}
-	return r.prev
-}
-
-
-ring_move :: proc(r: ^$R/Ring, n: int) -> ^R {
-  r := r
-	if r.next == nil {
-		return ring_init(r)
-	}
-
-	switch {
-	case n < 0:
-		for _ in n..<0 {
-			r = r.prev
-		}
-	case n > 0:
-		for _ in 0..<n {
-			r = r.next
-		}
-	}
-	return r
-}
-
-ring_link :: proc(r, s: ^$R/Ring) -> ^R {
-	n := ring_next(r)
-	if s != nil {
-		p := ring_prev(s)
-		r.next = s
-		s.prev = r
-		n.prev = p
-		p.next = n
-	}
-	return n
-}
-ring_unlink :: proc(r: ^$R/Ring, n: int) -> ^R {
-	if n <= 0 {
-		return nil
-	}
-	return ring_link(r, ring_move(r, n+1))
-}
-ring_len :: proc(r: ^$R/Ring) -> int {
-	n := 0
-	if r != nil {
-		n = 1
-		for p := ring_next(r); p != r; p = p.next {
-			n += 1
-		}
-	}
-	return n
-}
-

+ 0 - 240
core/container/set.odin

@@ -1,240 +0,0 @@
-package container
-
-Set :: struct {
-	hash:    Array(int),
-	entries: Array(Set_Entry),
-}
-
-Set_Entry :: struct {
-	key:   u64,
-	next:  int,
-}
-
-
-/*
-set_init :: proc{
-	set_init_none,
-	set_init_cap,
-}
-set_delete
-
-set_in
-set_not_in
-set_add
-set_remove
-set_reserve
-set_clear
-*/
-
-set_init :: proc{set_init_none, set_init_cap}
-
-set_init_none :: proc(m: ^Set, allocator := context.allocator) {
-	m.hash.allocator = allocator
-	m.entries.allocator = allocator
-}
-
-set_init_cap :: proc(m: ^Set, cap: int, allocator := context.allocator) {
-	m.hash.allocator = allocator
-	m.entries.allocator = allocator
-	set_reserve(m, cap)
-}
-
-set_delete :: proc(m: Set) {
-	array_delete(m.hash)
-	array_delete(m.entries)
-}
-
-
-set_in :: proc(m: Set, key: u64) -> bool {
-	return _set_find_or_fail(m, key) >= 0
-}
-set_not_in :: proc(m: Set, key: u64) -> bool {
-	return _set_find_or_fail(m, key) < 0
-}
-
-set_add :: proc(m: ^Set, key: u64) {
-	if array_len(m.hash) == 0 {
-		_set_grow(m)
-	}
-
-	_ = _set_find_or_make(m, key)
-	if _set_full(m^) {
-		_set_grow(m)
-	}
-}
-
-set_remove :: proc(m: ^Set, key: u64) {
-	fr := _set_find_key(m^, key)
-	if fr.entry_index >= 0 {
-		_set_erase(m, fr)
-	}
-}
-
-
-set_reserve :: proc(m: ^Set, new_size: int) {
-	nm: Set
-	set_init(&nm, m.hash.allocator)
-	array_resize(&nm.hash, new_size)
-	array_reserve(&nm.entries, array_len(m.entries))
-
-	for i in 0..<new_size {
-		array_set(&nm.hash, i, -1)
-	}
-	for i in 0..<array_len(m.entries) {
-		e := array_get(m.entries, i)
-		set_add(&nm, e.key)
-	}
-
-	set_delete(m^)
-	m^ = nm
-}
-
-set_clear :: proc(m: ^Set) {
-	array_clear(&m.hash)
-	array_clear(&m.entries)
-}
-
-
-set_equal :: proc(a, b: Set) -> bool {
-	a_entries := array_slice(a.entries)
-	b_entries := array_slice(b.entries)
-	if len(a_entries) != len(b_entries) {
-		return false
-	}
-	for e in a_entries {
-		if set_not_in(b, e.key) {
-			return false
-		}
-	}
-
-	return true
-}
-
-
-
-/// Internal
-
-_set_add_entry :: proc(m: ^Set, key: u64) -> int {
-	e: Set_Entry
-	e.key = key
-	e.next = -1
-	idx := array_len(m.entries)
-	array_push(&m.entries, e)
-	return idx
-}
-
-_set_erase :: proc(m: ^Set, fr: Map_Find_Result) {
-	if fr.entry_prev < 0 {
-		array_set(&m.hash, fr.hash_index, array_get(m.entries, fr.entry_index).next)
-	} else {
-		array_get_ptr(m.entries, fr.entry_prev).next = array_get(m.entries, fr.entry_index).next
-	}
-
-	if fr.entry_index == array_len(m.entries)-1 {
-		array_pop_back(&m.entries)
-		return
-	}
-
-	array_set(&m.entries, fr.entry_index, array_get(m.entries, array_len(m.entries)-1))
-	last := _set_find_key(m^, array_get(m.entries, fr.entry_index).key)
-
-	if last.entry_prev < 0 {
-		array_get_ptr(m.entries, last.entry_prev).next = fr.entry_index
-	} else {
-		array_set(&m.hash, last.hash_index, fr.entry_index)
-	}
-}
-
-
-_set_find_key :: proc(m: Set, key: u64) -> Map_Find_Result {
-	fr: Map_Find_Result
-	fr.hash_index = -1
-	fr.entry_prev = -1
-	fr.entry_index = -1
-
-	if array_len(m.hash) == 0 {
-		return fr
-	}
-
-	fr.hash_index = int(key % u64(array_len(m.hash)))
-	fr.entry_index = array_get(m.hash, fr.hash_index)
-	for fr.entry_index >= 0 {
-		it := array_get_ptr(m.entries, fr.entry_index)
-		if it.key == key {
-			return fr
-		}
-		fr.entry_prev = fr.entry_index
-		fr.entry_index = it.next
-	}
-	return fr
-}
-
-_set_find_entry :: proc(m: ^Set, e: ^Set_Entry) -> Map_Find_Result {
-	fr: Map_Find_Result
-	fr.hash_index = -1
-	fr.entry_prev = -1
-	fr.entry_index = -1
-
-	if array_len(m.hash) == 0 {
-		return fr
-	}
-
-	fr.hash_index = int(e.key % u64(array_len(m.hash)))
-	fr.entry_index = array_get(m.hash, fr.hash_index)
-	for fr.entry_index >= 0 {
-		it := array_get_ptr(m.entries, fr.entry_index)
-		if it == e {
-			return fr
-		}
-		fr.entry_prev = fr.entry_index
-		fr.entry_index = it.next
-	}
-	return fr
-}
-
-_set_find_or_fail :: proc(m: Set, key: u64) -> int {
-	return _set_find_key(m, key).entry_index
-}
-_set_find_or_make :: proc(m: ^Set, key: u64) -> int {
-	fr := _set_find_key(m^, key)
-	if fr.entry_index >= 0 {
-		return fr.entry_index
-	}
-
-	i := _set_add_entry(m, key)
-	if fr.entry_prev < 0 {
-		array_set(&m.hash, fr.hash_index, i)
-	} else {
-		array_get_ptr(m.entries, fr.entry_prev).next = i
-	}
-	return i
-}
-
-
-_set_make :: proc(m: ^Set, key: u64) -> int {
-	fr := _set_find_key(m^, key)
-	i := _set_add_entry(m, key)
-
-	if fr.entry_prev < 0 {
-		array_set(&m.hash, fr.hash_index, i)
-	} else {
-		array_get_ptr(m.entries, fr.entry_prev).next = i
-	}
-
-	array_get_ptr(m.entries, i).next = fr.entry_index
-
-	return i
-}
-
-
-_set_full :: proc(m: Set) -> bool {
-	// TODO(bill): Determine good max load factor
-	return array_len(m.entries) >= (array_len(m.hash) / 4)*3
-}
-
-_set_grow :: proc(m: ^Set) {
-	new_size := array_len(m.entries) * 4 + 7 // TODO(bill): Determine good grow rate
-	set_reserve(m, new_size)
-}
-
-

+ 0 - 95
core/container/small_array.odin

@@ -1,95 +0,0 @@
-package container
-
-Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
-	data: [N]T,
-	len:  int,
-}
-
-
-small_array_len :: proc(a: $A/Small_Array) -> int {
-	return a.len
-}
-
-small_array_cap :: proc(a: $A/Small_Array) -> int {
-	return len(a.data)
-}
-
-small_array_space :: proc(a: $A/Small_Array) -> int {
-	return len(a.data) - a.len
-}
-
-small_array_slice :: proc(a: ^$A/Small_Array($N, $T)) -> []T {
-	return a.data[:a.len]
-}
-
-
-small_array_get :: proc(a: $A/Small_Array($N, $T), index: int, loc := #caller_location) -> T {
-	return a.data[index]
-}
-small_array_get_ptr :: proc(a: $A/Small_Array($N, $T), index: int, loc := #caller_location) -> ^T {
-	return &a.data[index]
-}
-
-small_array_set :: proc(a: ^$A/Small_Array($N, $T), index: int, item: T, loc := #caller_location) {
-	a.data[index] = item
-}
-
-small_array_resize :: proc(a: ^$A/Small_Array, length: int) {
-	a.len = min(length, len(a.data))
-}
-
-
-small_array_push_back :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
-	if a.len < len(a.data) {
-		a.len += 1
-		a.data[a.len-1] = item
-		return true
-	}
-	return false
-}
-
-small_array_push_front :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
-	if a.len < len(a.data) {
-		a.len += 1
-		data := small_array_slice(a)
-		copy(data[1:], data[:])
-		data[0] = item
-		return true
-	}
-	return false
-}
-
-small_array_pop_back :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
-	assert(condition=a.len > 0, loc=loc)
-	item := a.data[a.len-1]
-	a.len -= 1
-	return item
-}
-
-small_array_pop_front :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
-	assert(condition=a.len > 0, loc=loc)
-	item := a.data[0]
-	s := small_array_slice(a)
-	copy(s[:], s[1:])
-	a.len -= 1
-	return item
-}
-
-
-small_array_consume :: proc(a: ^$A/Small_Array($N, $T), count: int, loc := #caller_location) {
-	assert(condition=a.len >= count, loc=loc)
-	a.len -= count
-}
-
-small_array_clear :: proc(a: ^$A/Small_Array($N, $T)) {
-	small_array_resize(a, 0)
-}
-
-small_array_push_back_elems :: proc(a: ^$A/Small_Array($N, $T), items: ..T) {
-	n := copy(a.data[a.len:], items[:])
-	a.len += n
-}
-
-small_array_push   :: proc{small_array_push_back, small_array_push_back_elems}
-small_array_append :: proc{small_array_push_back, small_array_push_back_elems}
-

+ 117 - 0
core/container/small_array/small_array.odin

@@ -0,0 +1,117 @@
+package container_small_array
+
+import "core:builtin"
+
+Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
+	data: [N]T,
+	len:  int,
+}
+
+
+len :: proc(a: $A/Small_Array) -> int {
+	return a.len
+}
+
+cap :: proc(a: $A/Small_Array) -> int {
+	return builtin.len(a.data)
+}
+
+space :: proc(a: $A/Small_Array) -> int {
+	return builtin.len(a.data) - a.len
+}
+
+slice :: proc(a: ^$A/Small_Array($N, $T)) -> []T {
+	return a.data[:a.len]
+}
+
+
+get :: proc(a: $A/Small_Array($N, $T), index: int, loc := #caller_location) -> T {
+	return a.data[index]
+}
+get_ptr :: proc(a: $A/Small_Array($N, $T), index: int, loc := #caller_location) -> ^T {
+	return &a.data[index]
+}
+
+set :: proc(a: ^$A/Small_Array($N, $T), index: int, item: T, loc := #caller_location) {
+	a.data[index] = item
+}
+
+resize :: proc(a: ^$A/Small_Array, length: int) {
+	a.len = min(length, builtin.len(a.data))
+}
+
+
+push_back :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
+	if a.len < cap(a^) {
+		a.data[a.len] = item
+		a.len += 1
+		return true
+	}
+	return false
+}
+
+push_front :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
+	if a.len < cap(a^) {
+		a.len += 1
+		data := slice(a)
+		copy(data[1:], data[:])
+		data[0] = item
+		return true
+	}
+	return false
+}
+
+pop_back :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
+	assert(condition=(N > 0 && a.len > 0), loc=loc)
+	item := a.data[a.len-1]
+	a.len -= 1
+	return item
+}
+
+pop_front :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
+	assert(condition=(N > 0 && a.len > 0), loc=loc)
+	item := a.data[0]
+	s := slice(a)
+	copy(s[:], s[1:])
+	a.len -= 1
+	return item
+}
+
+pop_back_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
+	if N > 0 && a.len > 0 {
+		item = a.data[a.len-1]
+		a.len -= 1
+		ok = true
+	}
+	return
+}
+
+pop_front_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (T, bool) {
+	if N > 0 && a.len > 0 {
+		item = a.data[0]
+		s := slice(a)
+		copy(s[:], s[1:])
+		a.len -= 1
+		ok = true
+	} 
+	return
+}
+
+consume :: proc(a: ^$A/Small_Array($N, $T), count: int, loc := #caller_location) {
+	assert(condition=a.len >= count, loc=loc)
+	a.len -= count
+}
+
+clear :: proc(a: ^$A/Small_Array($N, $T)) {
+	resize(a, 0)
+}
+
+push_back_elems :: proc(a: ^$A/Small_Array($N, $T), items: ..T) {
+	n := copy(a.data[a.len:], items[:])
+	a.len += n
+}
+
+append_elem  :: push_back
+append_elems :: push_back_elems
+push   :: proc{push_back, push_back_elems}
+append :: proc{push_back, push_back_elems}

+ 7 - 1
core/crypto/README.md

@@ -32,9 +32,11 @@ Please see the chart below for the options.
 
 #### High level API
 Each hash algorithm contains a procedure group named `hash`, or if the algorithm provides more than one digest size `hash_<size>`\*.  
-Included in these groups are four procedures.
+Included in these groups are six 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_string_to_buffer` - Hash a given string and put the computed hash in the second proc parameter. Just calls `hash_bytes_to_buffer` internally
+* `hash_bytes_to_buffer` - Hash a given string and put the computed hash in the second proc parameter. The destination buffer has to be at least as big as the digest size of the 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)
 
@@ -59,6 +61,10 @@ main :: proc() {
     // Compute the hash, using the high level API
     computed_hash := md4.hash(input)
 
+    // Variant that takes a destination buffer, instead of returning the computed hash
+    hash := make([]byte, md4.DIGEST_SIZE) // @note: Destination buffer has to be at least as big as the digest size of the hash
+    md4.hash(input, hash[:])
+
     // Compute the hash, using the low level API
     ctx: md4.Md4_Context
     computed_hash_low: [16]byte

+ 35 - 0
core/crypto/_fiat/README.md

@@ -0,0 +1,35 @@
+# fiat
+
+This package contains low level arithmetic required to implement certain
+cryptographic primitives, ported from the [fiat-crypto project][1]
+along with some higher-level helpers.
+
+## Notes
+
+fiat-crypto gives the choice of 3 licenses for derived works.  The 1-Clause
+BSD license is chosen as it is compatible with Odin's existing licensing.
+
+The routines are intended to be timing-safe, as long as the underlying
+integer arithmetic is constant time.  This is true on most systems commonly
+used today, with the notable exception of WASM.
+
+While fiat-crypto provides both output targeting both 32-bit and 64-bit
+architectures, only the 64-bit versions were used, as 32-bit architectures
+are becoming increasingly uncommon and irrelevant.
+
+With the current Odin syntax, the Go output is trivially ported in most
+cases and was used as the basis of the port.
+
+In the future, it would be better to auto-generate Odin either directly
+by adding an appropriate code-gen backend written in Coq, or perhaps by
+parsing the JSON output.
+
+As this is a port rather than autogenerated output, none of fiat-crypto's
+formal verification guarantees apply, unless it is possible to prove binary
+equivalence.
+
+For the most part, alterations to the base fiat-crypto generated code was
+kept to a minimum, to aid auditability.  This results in a somewhat
+ideosyncratic style, and in some cases minor performance penalties.
+
+[1]: https://github.com/mit-plv/fiat-crypto

+ 24 - 0
core/crypto/_fiat/fiat.odin

@@ -0,0 +1,24 @@
+package fiat
+
+// This package provides various helpers and types common to all of the
+// fiat-crypto derived backends.
+
+// This code only works on a two's complement system.
+#assert((-1 & 3) == 3)
+
+u1 :: distinct u8
+i1 :: distinct i8
+
+cmovznz_u64 :: #force_inline proc "contextless" (arg1: u1, arg2, arg3: u64) -> (out1: u64) {
+	x1 := (u64(arg1) * 0xffffffffffffffff)
+	x2 := ((x1 & arg3) | ((~x1) & arg2))
+	out1 = x2
+	return
+}
+
+cmovznz_u32 :: #force_inline proc "contextless" (arg1: u1, arg2, arg3: u32) -> (out1: u32) {
+	x1 := (u32(arg1) * 0xffffffff)
+	x2 := ((x1 & arg3) | ((~x1) & arg2))
+	out1 = x2
+	return
+}

+ 138 - 0
core/crypto/_fiat/field_curve25519/field.odin

@@ -0,0 +1,138 @@
+package field_curve25519
+
+import "core:crypto"
+import "core:mem"
+
+fe_relax_cast :: #force_inline proc "contextless" (arg1: ^Tight_Field_Element) -> ^Loose_Field_Element {
+	return transmute(^Loose_Field_Element)(arg1)
+}
+
+fe_tighten_cast :: #force_inline proc "contextless" (arg1: ^Loose_Field_Element) -> ^Tight_Field_Element {
+	return transmute(^Tight_Field_Element)(arg1)
+}
+
+fe_from_bytes :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^[32]byte) {
+	// Ignore the unused bit by copying the input and masking the bit off
+	// prior to deserialization.
+	tmp1: [32]byte = ---
+	copy_slice(tmp1[:], arg1[:])
+	tmp1[31] &= 127
+
+	_fe_from_bytes(out1, &tmp1)
+
+	mem.zero_explicit(&tmp1, size_of(tmp1))
+}
+
+fe_equal :: proc "contextless" (arg1, arg2: ^Tight_Field_Element) -> int {
+	tmp2: [32]byte = ---
+
+	fe_to_bytes(&tmp2, arg2)
+	ret := fe_equal_bytes(arg1, &tmp2)
+
+	mem.zero_explicit(&tmp2, size_of(tmp2))
+
+	return ret
+}
+
+fe_equal_bytes :: proc "contextless" (arg1: ^Tight_Field_Element, arg2: ^[32]byte) -> int {
+	tmp1: [32]byte = ---
+
+	fe_to_bytes(&tmp1, arg1)
+
+	ret := crypto.compare_constant_time(tmp1[:], arg2[:])
+
+	mem.zero_explicit(&tmp1, size_of(tmp1))
+
+	return ret
+}
+
+fe_carry_pow2k :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element, arg2: uint) {
+	// Special case: `arg1^(2 * 0) = 1`, though this should never happen.
+	if arg2 == 0 {
+		fe_one(out1)
+		return
+	}
+
+	fe_carry_square(out1, arg1)
+	for _ in 1..<arg2 {
+		fe_carry_square(out1, fe_relax_cast(out1))
+	}
+}
+
+fe_carry_opp :: #force_inline proc "contextless" (out1, arg1: ^Tight_Field_Element) {
+	fe_opp(fe_relax_cast(out1), arg1)
+	fe_carry(out1, fe_relax_cast(out1))
+}
+
+fe_carry_invsqrt :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) -> int {
+	// Inverse square root taken from Monocypher.
+
+	tmp1, tmp2, tmp3: Tight_Field_Element = ---, ---, ---
+
+	// t0 = x^((p-5)/8)
+	// Can be achieved with a simple double & add ladder,
+	// but it would be slower.
+	fe_carry_pow2k(&tmp1, arg1, 1)
+	fe_carry_pow2k(&tmp2, fe_relax_cast(&tmp1), 2)
+	fe_carry_mul(&tmp2, arg1, fe_relax_cast(&tmp2))
+	fe_carry_mul(&tmp1, fe_relax_cast(&tmp1), fe_relax_cast(&tmp2))
+	fe_carry_pow2k(&tmp1, fe_relax_cast(&tmp1), 1)
+	fe_carry_mul(&tmp1, fe_relax_cast(&tmp2), fe_relax_cast(&tmp1))
+	fe_carry_pow2k(&tmp2, fe_relax_cast(&tmp1), 5)
+	fe_carry_mul(&tmp1, fe_relax_cast(&tmp2), fe_relax_cast(&tmp1))
+	fe_carry_pow2k(&tmp2, fe_relax_cast(&tmp1), 10)
+	fe_carry_mul(&tmp2, fe_relax_cast(&tmp2), fe_relax_cast(&tmp1))
+	fe_carry_pow2k(&tmp3, fe_relax_cast(&tmp2), 20)
+	fe_carry_mul(&tmp2, fe_relax_cast(&tmp3), fe_relax_cast(&tmp2))
+	fe_carry_pow2k(&tmp2, fe_relax_cast(&tmp2), 10)
+	fe_carry_mul(&tmp1, fe_relax_cast(&tmp2), fe_relax_cast(&tmp1))
+	fe_carry_pow2k(&tmp2, fe_relax_cast(&tmp1), 50)
+	fe_carry_mul(&tmp2, fe_relax_cast(&tmp2), fe_relax_cast(&tmp1))
+	fe_carry_pow2k(&tmp3, fe_relax_cast(&tmp2), 100)
+	fe_carry_mul(&tmp2, fe_relax_cast(&tmp3), fe_relax_cast(&tmp2))
+	fe_carry_pow2k(&tmp2, fe_relax_cast(&tmp2), 50)
+	fe_carry_mul(&tmp1, fe_relax_cast(&tmp2), fe_relax_cast(&tmp1))
+	fe_carry_pow2k(&tmp1, fe_relax_cast(&tmp1), 2)
+	fe_carry_mul(&tmp1, fe_relax_cast(&tmp1), arg1)
+
+	// quartic = x^((p-1)/4)
+	quartic := &tmp2
+	fe_carry_square(quartic, fe_relax_cast(&tmp1))
+	fe_carry_mul(quartic, fe_relax_cast(quartic), arg1)
+
+	// Serialize quartic once to save on repeated serialization/sanitization.
+	quartic_buf: [32]byte = ---
+	fe_to_bytes(&quartic_buf, quartic)
+	check := &tmp3
+
+	fe_one(check)
+	p1 := fe_equal_bytes(check, &quartic_buf)
+	fe_carry_opp(check, check)
+	m1 := fe_equal_bytes(check, &quartic_buf)
+	fe_carry_opp(check, &SQRT_M1)
+	ms := fe_equal_bytes(check, &quartic_buf)
+
+	// if quartic == -1 or sqrt(-1)
+	// then  isr = x^((p-1)/4) * sqrt(-1)
+	// else  isr = x^((p-1)/4)
+	fe_carry_mul(out1, fe_relax_cast(&tmp1), fe_relax_cast(&SQRT_M1))
+	fe_cond_assign(out1, &tmp1, (m1|ms) ~ 1)
+
+	mem.zero_explicit(&tmp1, size_of(tmp1))
+	mem.zero_explicit(&tmp2, size_of(tmp2))
+	mem.zero_explicit(&tmp3, size_of(tmp3))
+	mem.zero_explicit(&quartic_buf, size_of(quartic_buf))
+
+	return p1 | m1
+}
+
+fe_carry_inv :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
+	tmp1: Tight_Field_Element
+
+	fe_carry_square(&tmp1, arg1)
+	_ = fe_carry_invsqrt(&tmp1, fe_relax_cast(&tmp1))
+	fe_carry_square(&tmp1, fe_relax_cast(&tmp1))
+	fe_carry_mul(out1, fe_relax_cast(&tmp1), arg1)
+
+	mem.zero_explicit(&tmp1, size_of(tmp1))
+}

+ 616 - 0
core/crypto/_fiat/field_curve25519/field51.odin

@@ -0,0 +1,616 @@
+// The BSD 1-Clause License (BSD-1-Clause)
+//
+// Copyright (c) 2015-2020 the fiat-crypto authors (see the AUTHORS file)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     1. Redistributions of source code must retain the above copyright
+//        notice, this list of conditions and the following disclaimer.
+//
+// THIS SOFTWARE IS PROVIDED BY the fiat-crypto authors "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design,
+// Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package field_curve25519
+
+// The file provides arithmetic on the field Z/(2^255-19) using
+// unsaturated 64-bit integer arithmetic.  It is derived primarily
+// from the machine generated Golang output from the fiat-crypto project.
+//
+// While the base implementation is provably correct, this implementation
+// makes no such claims as the port and optimizations were done by hand.
+// At some point, it may be worth adding support to fiat-crypto for
+// generating Odin output.
+//
+// TODO:
+//  * When fiat-crypto supports it, using a saturated 64-bit limbs
+//    instead of 51-bit limbs will be faster, though the gains are
+//    minimal unless adcx/adox/mulx are used.
+
+import fiat "core:crypto/_fiat"
+import "core:math/bits"
+
+Loose_Field_Element :: distinct [5]u64
+Tight_Field_Element :: distinct [5]u64
+
+SQRT_M1 := Tight_Field_Element{
+	1718705420411056,
+	234908883556509,
+	2233514472574048,
+	2117202627021982,
+	765476049583133,
+}
+
+_addcarryx_u51 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u64) -> (out1: u64, out2: fiat.u1) {
+	x1 := ((u64(arg1) + arg2) + arg3)
+	x2 := (x1 & 0x7ffffffffffff)
+	x3 := fiat.u1((x1 >> 51))
+	out1 = x2
+	out2 = x3
+	return
+}
+
+_subborrowx_u51 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u64) -> (out1: u64, out2: fiat.u1) {
+	x1 := ((i64(arg2) - i64(arg1)) - i64(arg3))
+	x2 := fiat.i1((x1 >> 51))
+	x3 := (u64(x1) & 0x7ffffffffffff)
+	out1 = x3
+	out2 = (0x0 - fiat.u1(x2))
+	return
+}
+
+fe_carry_mul :: proc (out1: ^Tight_Field_Element, arg1, arg2: ^Loose_Field_Element) {
+	x2, x1 := bits.mul_u64(arg1[4], (arg2[4] * 0x13))
+	x4, x3 := bits.mul_u64(arg1[4], (arg2[3] * 0x13))
+	x6, x5 := bits.mul_u64(arg1[4], (arg2[2] * 0x13))
+	x8, x7 := bits.mul_u64(arg1[4], (arg2[1] * 0x13))
+	x10, x9 := bits.mul_u64(arg1[3], (arg2[4] * 0x13))
+	x12, x11 := bits.mul_u64(arg1[3], (arg2[3] * 0x13))
+	x14, x13 := bits.mul_u64(arg1[3], (arg2[2] * 0x13))
+	x16, x15 := bits.mul_u64(arg1[2], (arg2[4] * 0x13))
+	x18, x17 := bits.mul_u64(arg1[2], (arg2[3] * 0x13))
+	x20, x19 := bits.mul_u64(arg1[1], (arg2[4] * 0x13))
+	x22, x21 := bits.mul_u64(arg1[4], arg2[0])
+	x24, x23 := bits.mul_u64(arg1[3], arg2[1])
+	x26, x25 := bits.mul_u64(arg1[3], arg2[0])
+	x28, x27 := bits.mul_u64(arg1[2], arg2[2])
+	x30, x29 := bits.mul_u64(arg1[2], arg2[1])
+	x32, x31 := bits.mul_u64(arg1[2], arg2[0])
+	x34, x33 := bits.mul_u64(arg1[1], arg2[3])
+	x36, x35 := bits.mul_u64(arg1[1], arg2[2])
+	x38, x37 := bits.mul_u64(arg1[1], arg2[1])
+	x40, x39 := bits.mul_u64(arg1[1], arg2[0])
+	x42, x41 := bits.mul_u64(arg1[0], arg2[4])
+	x44, x43 := bits.mul_u64(arg1[0], arg2[3])
+	x46, x45 := bits.mul_u64(arg1[0], arg2[2])
+	x48, x47 := bits.mul_u64(arg1[0], arg2[1])
+	x50, x49 := bits.mul_u64(arg1[0], arg2[0])
+	x51, x52 := bits.add_u64(x13, x7, u64(0x0))
+	x53, _ := bits.add_u64(x14, x8, u64(fiat.u1(x52)))
+	x55, x56 := bits.add_u64(x17, x51, u64(0x0))
+	x57, _ := bits.add_u64(x18, x53, u64(fiat.u1(x56)))
+	x59, x60 := bits.add_u64(x19, x55, u64(0x0))
+	x61, _ := bits.add_u64(x20, x57, u64(fiat.u1(x60)))
+	x63, x64 := bits.add_u64(x49, x59, u64(0x0))
+	x65, _ := bits.add_u64(x50, x61, u64(fiat.u1(x64)))
+	x67 := ((x63 >> 51) | ((x65 << 13) & 0xffffffffffffffff))
+	x68 := (x63 & 0x7ffffffffffff)
+	x69, x70 := bits.add_u64(x23, x21, u64(0x0))
+	x71, _ := bits.add_u64(x24, x22, u64(fiat.u1(x70)))
+	x73, x74 := bits.add_u64(x27, x69, u64(0x0))
+	x75, _ := bits.add_u64(x28, x71, u64(fiat.u1(x74)))
+	x77, x78 := bits.add_u64(x33, x73, u64(0x0))
+	x79, _ := bits.add_u64(x34, x75, u64(fiat.u1(x78)))
+	x81, x82 := bits.add_u64(x41, x77, u64(0x0))
+	x83, _ := bits.add_u64(x42, x79, u64(fiat.u1(x82)))
+	x85, x86 := bits.add_u64(x25, x1, u64(0x0))
+	x87, _ := bits.add_u64(x26, x2, u64(fiat.u1(x86)))
+	x89, x90 := bits.add_u64(x29, x85, u64(0x0))
+	x91, _ := bits.add_u64(x30, x87, u64(fiat.u1(x90)))
+	x93, x94 := bits.add_u64(x35, x89, u64(0x0))
+	x95, _ := bits.add_u64(x36, x91, u64(fiat.u1(x94)))
+	x97, x98 := bits.add_u64(x43, x93, u64(0x0))
+	x99, _ := bits.add_u64(x44, x95, u64(fiat.u1(x98)))
+	x101, x102 := bits.add_u64(x9, x3, u64(0x0))
+	x103, _ := bits.add_u64(x10, x4, u64(fiat.u1(x102)))
+	x105, x106 := bits.add_u64(x31, x101, u64(0x0))
+	x107, _ := bits.add_u64(x32, x103, u64(fiat.u1(x106)))
+	x109, x110 := bits.add_u64(x37, x105, u64(0x0))
+	x111, _ := bits.add_u64(x38, x107, u64(fiat.u1(x110)))
+	x113, x114 := bits.add_u64(x45, x109, u64(0x0))
+	x115, _ := bits.add_u64(x46, x111, u64(fiat.u1(x114)))
+	x117, x118 := bits.add_u64(x11, x5, u64(0x0))
+	x119, _ := bits.add_u64(x12, x6, u64(fiat.u1(x118)))
+	x121, x122 := bits.add_u64(x15, x117, u64(0x0))
+	x123, _ := bits.add_u64(x16, x119, u64(fiat.u1(x122)))
+	x125, x126 := bits.add_u64(x39, x121, u64(0x0))
+	x127, _ := bits.add_u64(x40, x123, u64(fiat.u1(x126)))
+	x129, x130 := bits.add_u64(x47, x125, u64(0x0))
+	x131, _ := bits.add_u64(x48, x127, u64(fiat.u1(x130)))
+	x133, x134 := bits.add_u64(x67, x129, u64(0x0))
+	x135 := (u64(fiat.u1(x134)) + x131)
+	x136 := ((x133 >> 51) | ((x135 << 13) & 0xffffffffffffffff))
+	x137 := (x133 & 0x7ffffffffffff)
+	x138, x139 := bits.add_u64(x136, x113, u64(0x0))
+	x140 := (u64(fiat.u1(x139)) + x115)
+	x141 := ((x138 >> 51) | ((x140 << 13) & 0xffffffffffffffff))
+	x142 := (x138 & 0x7ffffffffffff)
+	x143, x144 := bits.add_u64(x141, x97, u64(0x0))
+	x145 := (u64(fiat.u1(x144)) + x99)
+	x146 := ((x143 >> 51) | ((x145 << 13) & 0xffffffffffffffff))
+	x147 := (x143 & 0x7ffffffffffff)
+	x148, x149 := bits.add_u64(x146, x81, u64(0x0))
+	x150 := (u64(fiat.u1(x149)) + x83)
+	x151 := ((x148 >> 51) | ((x150 << 13) & 0xffffffffffffffff))
+	x152 := (x148 & 0x7ffffffffffff)
+	x153 := (x151 * 0x13)
+	x154 := (x68 + x153)
+	x155 := (x154 >> 51)
+	x156 := (x154 & 0x7ffffffffffff)
+	x157 := (x155 + x137)
+	x158 := fiat.u1((x157 >> 51))
+	x159 := (x157 & 0x7ffffffffffff)
+	x160 := (u64(x158) + x142)
+	out1[0] = x156
+	out1[1] = x159
+	out1[2] = x160
+	out1[3] = x147
+	out1[4] = x152
+}
+
+fe_carry_square :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
+	x1 := (arg1[4] * 0x13)
+	x2 := (x1 * 0x2)
+	x3 := (arg1[4] * 0x2)
+	x4 := (arg1[3] * 0x13)
+	x5 := (x4 * 0x2)
+	x6 := (arg1[3] * 0x2)
+	x7 := (arg1[2] * 0x2)
+	x8 := (arg1[1] * 0x2)
+	x10, x9 := bits.mul_u64(arg1[4], x1)
+	x12, x11 := bits.mul_u64(arg1[3], x2)
+	x14, x13 := bits.mul_u64(arg1[3], x4)
+	x16, x15 := bits.mul_u64(arg1[2], x2)
+	x18, x17 := bits.mul_u64(arg1[2], x5)
+	x20, x19 := bits.mul_u64(arg1[2], arg1[2])
+	x22, x21 := bits.mul_u64(arg1[1], x2)
+	x24, x23 := bits.mul_u64(arg1[1], x6)
+	x26, x25 := bits.mul_u64(arg1[1], x7)
+	x28, x27 := bits.mul_u64(arg1[1], arg1[1])
+	x30, x29 := bits.mul_u64(arg1[0], x3)
+	x32, x31 := bits.mul_u64(arg1[0], x6)
+	x34, x33 := bits.mul_u64(arg1[0], x7)
+	x36, x35 := bits.mul_u64(arg1[0], x8)
+	x38, x37 := bits.mul_u64(arg1[0], arg1[0])
+	x39, x40 := bits.add_u64(x21, x17, u64(0x0))
+	x41, _ := bits.add_u64(x22, x18, u64(fiat.u1(x40)))
+	x43, x44 := bits.add_u64(x37, x39, u64(0x0))
+	x45, _ := bits.add_u64(x38, x41, u64(fiat.u1(x44)))
+	x47 := ((x43 >> 51) | ((x45 << 13) & 0xffffffffffffffff))
+	x48 := (x43 & 0x7ffffffffffff)
+	x49, x50 := bits.add_u64(x23, x19, u64(0x0))
+	x51, _ := bits.add_u64(x24, x20, u64(fiat.u1(x50)))
+	x53, x54 := bits.add_u64(x29, x49, u64(0x0))
+	x55, _ := bits.add_u64(x30, x51, u64(fiat.u1(x54)))
+	x57, x58 := bits.add_u64(x25, x9, u64(0x0))
+	x59, _ := bits.add_u64(x26, x10, u64(fiat.u1(x58)))
+	x61, x62 := bits.add_u64(x31, x57, u64(0x0))
+	x63, _ := bits.add_u64(x32, x59, u64(fiat.u1(x62)))
+	x65, x66 := bits.add_u64(x27, x11, u64(0x0))
+	x67, _ := bits.add_u64(x28, x12, u64(fiat.u1(x66)))
+	x69, x70 := bits.add_u64(x33, x65, u64(0x0))
+	x71, _ := bits.add_u64(x34, x67, u64(fiat.u1(x70)))
+	x73, x74 := bits.add_u64(x15, x13, u64(0x0))
+	x75, _ := bits.add_u64(x16, x14, u64(fiat.u1(x74)))
+	x77, x78 := bits.add_u64(x35, x73, u64(0x0))
+	x79, _ := bits.add_u64(x36, x75, u64(fiat.u1(x78)))
+	x81, x82 := bits.add_u64(x47, x77, u64(0x0))
+	x83 := (u64(fiat.u1(x82)) + x79)
+	x84 := ((x81 >> 51) | ((x83 << 13) & 0xffffffffffffffff))
+	x85 := (x81 & 0x7ffffffffffff)
+	x86, x87 := bits.add_u64(x84, x69, u64(0x0))
+	x88 := (u64(fiat.u1(x87)) + x71)
+	x89 := ((x86 >> 51) | ((x88 << 13) & 0xffffffffffffffff))
+	x90 := (x86 & 0x7ffffffffffff)
+	x91, x92 := bits.add_u64(x89, x61, u64(0x0))
+	x93 := (u64(fiat.u1(x92)) + x63)
+	x94 := ((x91 >> 51) | ((x93 << 13) & 0xffffffffffffffff))
+	x95 := (x91 & 0x7ffffffffffff)
+	x96, x97 := bits.add_u64(x94, x53, u64(0x0))
+	x98 := (u64(fiat.u1(x97)) + x55)
+	x99 := ((x96 >> 51) | ((x98 << 13) & 0xffffffffffffffff))
+	x100 := (x96 & 0x7ffffffffffff)
+	x101 := (x99 * 0x13)
+	x102 := (x48 + x101)
+	x103 := (x102 >> 51)
+	x104 := (x102 & 0x7ffffffffffff)
+	x105 := (x103 + x85)
+	x106 := fiat.u1((x105 >> 51))
+	x107 := (x105 & 0x7ffffffffffff)
+	x108 := (u64(x106) + x90)
+	out1[0] = x104
+	out1[1] = x107
+	out1[2] = x108
+	out1[3] = x95
+	out1[4] = x100
+}
+
+fe_carry :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
+	x1 := arg1[0]
+	x2 := ((x1 >> 51) + arg1[1])
+	x3 := ((x2 >> 51) + arg1[2])
+	x4 := ((x3 >> 51) + arg1[3])
+	x5 := ((x4 >> 51) + arg1[4])
+	x6 := ((x1 & 0x7ffffffffffff) + ((x5 >> 51) * 0x13))
+	x7 := (u64(fiat.u1((x6 >> 51))) + (x2 & 0x7ffffffffffff))
+	x8 := (x6 & 0x7ffffffffffff)
+	x9 := (x7 & 0x7ffffffffffff)
+	x10 := (u64(fiat.u1((x7 >> 51))) + (x3 & 0x7ffffffffffff))
+	x11 := (x4 & 0x7ffffffffffff)
+	x12 := (x5 & 0x7ffffffffffff)
+	out1[0] = x8
+	out1[1] = x9
+	out1[2] = x10
+	out1[3] = x11
+	out1[4] = x12
+}
+
+fe_add :: proc "contextless" (out1: ^Loose_Field_Element, arg1, arg2: ^Tight_Field_Element) {
+	x1 := (arg1[0] + arg2[0])
+	x2 := (arg1[1] + arg2[1])
+	x3 := (arg1[2] + arg2[2])
+	x4 := (arg1[3] + arg2[3])
+	x5 := (arg1[4] + arg2[4])
+	out1[0] = x1
+	out1[1] = x2
+	out1[2] = x3
+	out1[3] = x4
+	out1[4] = x5
+}
+
+fe_sub :: proc "contextless" (out1: ^Loose_Field_Element, arg1, arg2: ^Tight_Field_Element) {
+	x1 := ((0xfffffffffffda + arg1[0]) - arg2[0])
+	x2 := ((0xffffffffffffe + arg1[1]) - arg2[1])
+	x3 := ((0xffffffffffffe + arg1[2]) - arg2[2])
+	x4 := ((0xffffffffffffe + arg1[3]) - arg2[3])
+	x5 := ((0xffffffffffffe + arg1[4]) - arg2[4])
+	out1[0] = x1
+	out1[1] = x2
+	out1[2] = x3
+	out1[3] = x4
+	out1[4] = x5
+}
+
+fe_opp :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_Element) {
+	x1 := (0xfffffffffffda - arg1[0])
+	x2 := (0xffffffffffffe - arg1[1])
+	x3 := (0xffffffffffffe - arg1[2])
+	x4 := (0xffffffffffffe - arg1[3])
+	x5 := (0xffffffffffffe - arg1[4])
+	out1[0] = x1
+	out1[1] = x2
+	out1[2] = x3
+	out1[3] = x4
+	out1[4] = x5
+}
+
+fe_cond_assign :: proc "contextless" (out1, arg1: ^Tight_Field_Element, arg2: int) {
+	x1 := fiat.cmovznz_u64(fiat.u1(arg2), out1[0], arg1[0])
+	x2 := fiat.cmovznz_u64(fiat.u1(arg2), out1[1], arg1[1])
+	x3 := fiat.cmovznz_u64(fiat.u1(arg2), out1[2], arg1[2])
+	x4 := fiat.cmovznz_u64(fiat.u1(arg2), out1[3], arg1[3])
+	x5 := fiat.cmovznz_u64(fiat.u1(arg2), out1[4], arg1[4])
+	out1[0] = x1
+	out1[1] = x2
+	out1[2] = x3
+	out1[3] = x4
+	out1[4] = x5
+}
+
+fe_to_bytes :: proc "contextless" (out1: ^[32]byte, arg1: ^Tight_Field_Element) {
+	x1, x2 := _subborrowx_u51(0x0, arg1[0], 0x7ffffffffffed)
+	x3, x4 := _subborrowx_u51(x2, arg1[1], 0x7ffffffffffff)
+	x5, x6 := _subborrowx_u51(x4, arg1[2], 0x7ffffffffffff)
+	x7, x8 := _subborrowx_u51(x6, arg1[3], 0x7ffffffffffff)
+	x9, x10 := _subborrowx_u51(x8, arg1[4], 0x7ffffffffffff)
+	x11 := fiat.cmovznz_u64(x10, u64(0x0), 0xffffffffffffffff)
+	x12, x13 := _addcarryx_u51(0x0, x1, (x11 & 0x7ffffffffffed))
+	x14, x15 := _addcarryx_u51(x13, x3, (x11 & 0x7ffffffffffff))
+	x16, x17 := _addcarryx_u51(x15, x5, (x11 & 0x7ffffffffffff))
+	x18, x19 := _addcarryx_u51(x17, x7, (x11 & 0x7ffffffffffff))
+	x20, _ := _addcarryx_u51(x19, x9, (x11 & 0x7ffffffffffff))
+	x22 := (x20 << 4)
+	x23 := (x18 * u64(0x2))
+	x24 := (x16 << 6)
+	x25 := (x14 << 3)
+	x26 := (u8(x12) & 0xff)
+	x27 := (x12 >> 8)
+	x28 := (u8(x27) & 0xff)
+	x29 := (x27 >> 8)
+	x30 := (u8(x29) & 0xff)
+	x31 := (x29 >> 8)
+	x32 := (u8(x31) & 0xff)
+	x33 := (x31 >> 8)
+	x34 := (u8(x33) & 0xff)
+	x35 := (x33 >> 8)
+	x36 := (u8(x35) & 0xff)
+	x37 := u8((x35 >> 8))
+	x38 := (x25 + u64(x37))
+	x39 := (u8(x38) & 0xff)
+	x40 := (x38 >> 8)
+	x41 := (u8(x40) & 0xff)
+	x42 := (x40 >> 8)
+	x43 := (u8(x42) & 0xff)
+	x44 := (x42 >> 8)
+	x45 := (u8(x44) & 0xff)
+	x46 := (x44 >> 8)
+	x47 := (u8(x46) & 0xff)
+	x48 := (x46 >> 8)
+	x49 := (u8(x48) & 0xff)
+	x50 := u8((x48 >> 8))
+	x51 := (x24 + u64(x50))
+	x52 := (u8(x51) & 0xff)
+	x53 := (x51 >> 8)
+	x54 := (u8(x53) & 0xff)
+	x55 := (x53 >> 8)
+	x56 := (u8(x55) & 0xff)
+	x57 := (x55 >> 8)
+	x58 := (u8(x57) & 0xff)
+	x59 := (x57 >> 8)
+	x60 := (u8(x59) & 0xff)
+	x61 := (x59 >> 8)
+	x62 := (u8(x61) & 0xff)
+	x63 := (x61 >> 8)
+	x64 := (u8(x63) & 0xff)
+	x65 := fiat.u1((x63 >> 8))
+	x66 := (x23 + u64(x65))
+	x67 := (u8(x66) & 0xff)
+	x68 := (x66 >> 8)
+	x69 := (u8(x68) & 0xff)
+	x70 := (x68 >> 8)
+	x71 := (u8(x70) & 0xff)
+	x72 := (x70 >> 8)
+	x73 := (u8(x72) & 0xff)
+	x74 := (x72 >> 8)
+	x75 := (u8(x74) & 0xff)
+	x76 := (x74 >> 8)
+	x77 := (u8(x76) & 0xff)
+	x78 := u8((x76 >> 8))
+	x79 := (x22 + u64(x78))
+	x80 := (u8(x79) & 0xff)
+	x81 := (x79 >> 8)
+	x82 := (u8(x81) & 0xff)
+	x83 := (x81 >> 8)
+	x84 := (u8(x83) & 0xff)
+	x85 := (x83 >> 8)
+	x86 := (u8(x85) & 0xff)
+	x87 := (x85 >> 8)
+	x88 := (u8(x87) & 0xff)
+	x89 := (x87 >> 8)
+	x90 := (u8(x89) & 0xff)
+	x91 := u8((x89 >> 8))
+	out1[0] = x26
+	out1[1] = x28
+	out1[2] = x30
+	out1[3] = x32
+	out1[4] = x34
+	out1[5] = x36
+	out1[6] = x39
+	out1[7] = x41
+	out1[8] = x43
+	out1[9] = x45
+	out1[10] = x47
+	out1[11] = x49
+	out1[12] = x52
+	out1[13] = x54
+	out1[14] = x56
+	out1[15] = x58
+	out1[16] = x60
+	out1[17] = x62
+	out1[18] = x64
+	out1[19] = x67
+	out1[20] = x69
+	out1[21] = x71
+	out1[22] = x73
+	out1[23] = x75
+	out1[24] = x77
+	out1[25] = x80
+	out1[26] = x82
+	out1[27] = x84
+	out1[28] = x86
+	out1[29] = x88
+	out1[30] = x90
+	out1[31] = x91
+}
+
+_fe_from_bytes :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^[32]byte) {
+	x1 := (u64(arg1[31]) << 44)
+	x2 := (u64(arg1[30]) << 36)
+	x3 := (u64(arg1[29]) << 28)
+	x4 := (u64(arg1[28]) << 20)
+	x5 := (u64(arg1[27]) << 12)
+	x6 := (u64(arg1[26]) << 4)
+	x7 := (u64(arg1[25]) << 47)
+	x8 := (u64(arg1[24]) << 39)
+	x9 := (u64(arg1[23]) << 31)
+	x10 := (u64(arg1[22]) << 23)
+	x11 := (u64(arg1[21]) << 15)
+	x12 := (u64(arg1[20]) << 7)
+	x13 := (u64(arg1[19]) << 50)
+	x14 := (u64(arg1[18]) << 42)
+	x15 := (u64(arg1[17]) << 34)
+	x16 := (u64(arg1[16]) << 26)
+	x17 := (u64(arg1[15]) << 18)
+	x18 := (u64(arg1[14]) << 10)
+	x19 := (u64(arg1[13]) << 2)
+	x20 := (u64(arg1[12]) << 45)
+	x21 := (u64(arg1[11]) << 37)
+	x22 := (u64(arg1[10]) << 29)
+	x23 := (u64(arg1[9]) << 21)
+	x24 := (u64(arg1[8]) << 13)
+	x25 := (u64(arg1[7]) << 5)
+	x26 := (u64(arg1[6]) << 48)
+	x27 := (u64(arg1[5]) << 40)
+	x28 := (u64(arg1[4]) << 32)
+	x29 := (u64(arg1[3]) << 24)
+	x30 := (u64(arg1[2]) << 16)
+	x31 := (u64(arg1[1]) << 8)
+	x32 := arg1[0]
+	x33 := (x31 + u64(x32))
+	x34 := (x30 + x33)
+	x35 := (x29 + x34)
+	x36 := (x28 + x35)
+	x37 := (x27 + x36)
+	x38 := (x26 + x37)
+	x39 := (x38 & 0x7ffffffffffff)
+	x40 := u8((x38 >> 51))
+	x41 := (x25 + u64(x40))
+	x42 := (x24 + x41)
+	x43 := (x23 + x42)
+	x44 := (x22 + x43)
+	x45 := (x21 + x44)
+	x46 := (x20 + x45)
+	x47 := (x46 & 0x7ffffffffffff)
+	x48 := u8((x46 >> 51))
+	x49 := (x19 + u64(x48))
+	x50 := (x18 + x49)
+	x51 := (x17 + x50)
+	x52 := (x16 + x51)
+	x53 := (x15 + x52)
+	x54 := (x14 + x53)
+	x55 := (x13 + x54)
+	x56 := (x55 & 0x7ffffffffffff)
+	x57 := u8((x55 >> 51))
+	x58 := (x12 + u64(x57))
+	x59 := (x11 + x58)
+	x60 := (x10 + x59)
+	x61 := (x9 + x60)
+	x62 := (x8 + x61)
+	x63 := (x7 + x62)
+	x64 := (x63 & 0x7ffffffffffff)
+	x65 := u8((x63 >> 51))
+	x66 := (x6 + u64(x65))
+	x67 := (x5 + x66)
+	x68 := (x4 + x67)
+	x69 := (x3 + x68)
+	x70 := (x2 + x69)
+	x71 := (x1 + x70)
+	out1[0] = x39
+	out1[1] = x47
+	out1[2] = x56
+	out1[3] = x64
+	out1[4] = x71
+}
+
+fe_relax :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_Element) {
+	x1 := arg1[0]
+	x2 := arg1[1]
+	x3 := arg1[2]
+	x4 := arg1[3]
+	x5 := arg1[4]
+	out1[0] = x1
+	out1[1] = x2
+	out1[2] = x3
+	out1[3] = x4
+	out1[4] = x5
+}
+
+fe_carry_scmul_121666 :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
+	x2, x1 := bits.mul_u64(0x1db42, arg1[4])
+	x4, x3 := bits.mul_u64(0x1db42, arg1[3])
+	x6, x5 := bits.mul_u64(0x1db42, arg1[2])
+	x8, x7 := bits.mul_u64(0x1db42, arg1[1])
+	x10, x9 := bits.mul_u64(0x1db42, arg1[0])
+	x11 := ((x9 >> 51) | ((x10 << 13) & 0xffffffffffffffff))
+	x12 := (x9 & 0x7ffffffffffff)
+	x13, x14 := bits.add_u64(x11, x7, u64(0x0))
+	x15 := (u64(fiat.u1(x14)) + x8)
+	x16 := ((x13 >> 51) | ((x15 << 13) & 0xffffffffffffffff))
+	x17 := (x13 & 0x7ffffffffffff)
+	x18, x19 := bits.add_u64(x16, x5, u64(0x0))
+	x20 := (u64(fiat.u1(x19)) + x6)
+	x21 := ((x18 >> 51) | ((x20 << 13) & 0xffffffffffffffff))
+	x22 := (x18 & 0x7ffffffffffff)
+	x23, x24 := bits.add_u64(x21, x3, u64(0x0))
+	x25 := (u64(fiat.u1(x24)) + x4)
+	x26 := ((x23 >> 51) | ((x25 << 13) & 0xffffffffffffffff))
+	x27 := (x23 & 0x7ffffffffffff)
+	x28, x29 := bits.add_u64(x26, x1, u64(0x0))
+	x30 := (u64(fiat.u1(x29)) + x2)
+	x31 := ((x28 >> 51) | ((x30 << 13) & 0xffffffffffffffff))
+	x32 := (x28 & 0x7ffffffffffff)
+	x33 := (x31 * 0x13)
+	x34 := (x12 + x33)
+	x35 := fiat.u1((x34 >> 51))
+	x36 := (x34 & 0x7ffffffffffff)
+	x37 := (u64(x35) + x17)
+	x38 := fiat.u1((x37 >> 51))
+	x39 := (x37 & 0x7ffffffffffff)
+	x40 := (u64(x38) + x22)
+	out1[0] = x36
+	out1[1] = x39
+	out1[2] = x40
+	out1[3] = x27
+	out1[4] = x32
+}
+
+// The following routines were added by hand, and do not come from fiat-crypto.
+
+fe_zero :: proc "contextless" (out1: ^Tight_Field_Element) {
+	out1[0] = 0
+	out1[1] = 0
+	out1[2] = 0
+	out1[3] = 0
+	out1[4] = 0
+}
+
+fe_one :: proc "contextless" (out1: ^Tight_Field_Element) {
+	out1[0] = 1
+	out1[1] = 0
+	out1[2] = 0
+	out1[3] = 0
+	out1[4] = 0
+}
+
+fe_set :: proc "contextless" (out1, arg1: ^Tight_Field_Element) {
+	x1 := arg1[0]
+	x2 := arg1[1]
+	x3 := arg1[2]
+	x4 := arg1[3]
+	x5 := arg1[4]
+	out1[0] = x1
+	out1[1] = x2
+	out1[2] = x3
+	out1[3] = x4
+	out1[4] = x5
+}
+
+fe_cond_swap :: proc "contextless" (out1, out2: ^Tight_Field_Element, arg1: int) {
+	mask := -u64(arg1)
+	x := (out1[0] ~ out2[0]) & mask
+	x1, y1 := out1[0] ~ x, out2[0] ~ x
+	x = (out1[1] ~ out2[1]) & mask
+	x2, y2 := out1[1] ~ x, out2[1] ~ x
+	x = (out1[2] ~ out2[2]) & mask
+	x3, y3 := out1[2] ~ x, out2[2] ~ x
+	x = (out1[3] ~ out2[3]) & mask
+	x4, y4 := out1[3] ~ x, out2[3] ~ x
+	x = (out1[4] ~ out2[4]) & mask
+	x5, y5 := out1[4] ~ x, out2[4] ~ x
+	out1[0], out2[0] = x1, y1
+	out1[1], out2[1] = x2, y2
+	out1[2], out2[2] = x3, y3
+	out1[3], out2[3] = x4, y4
+	out1[4], out2[4] = x5, y5
+}

+ 66 - 0
core/crypto/_fiat/field_poly1305/field.odin

@@ -0,0 +1,66 @@
+package field_poly1305
+
+import "core:crypto/util"
+import "core:mem"
+
+fe_relax_cast :: #force_inline proc "contextless" (arg1: ^Tight_Field_Element) -> ^Loose_Field_Element {
+	return transmute(^Loose_Field_Element)(arg1)
+}
+
+fe_tighten_cast :: #force_inline proc "contextless" (arg1: ^Loose_Field_Element) -> ^Tight_Field_Element {
+	return transmute(^Tight_Field_Element)(arg1)
+}
+
+fe_from_bytes :: #force_inline proc (out1: ^Tight_Field_Element, arg1: []byte, arg2: byte, sanitize: bool = true) {
+	// fiat-crypto's deserialization routine effectively processes a
+	// single byte at a time, and wants 256-bits of input for a value
+	// that will be 128-bits or 129-bits.
+	//
+	// This is somewhat cumbersome to use, so at a minimum a wrapper
+	// makes implementing the actual MAC block processing considerably
+	// neater.
+
+	assert(len(arg1) == 16)
+
+	when ODIN_ARCH == "386" || ODIN_ARCH == "amd64" {
+		// While it may be unwise to do deserialization here on our
+		// own when fiat-crypto provides equivalent functionality,
+		// doing it this way provides a little under 3x performance
+		// improvement when optimization is enabled.
+		src_p := transmute(^[2]u64)(&arg1[0])
+		lo := src_p[0]
+		hi := src_p[1]
+
+		// This is inspired by poly1305-donna, though adjustments were
+		// made since a Tight_Field_Element's limbs are 44-bits, 43-bits,
+		// and 43-bits wide.
+		//
+		// Note: This could be transplated into fe_from_u64s, but that
+		// code is called once per MAC, and is non-criticial path.
+		hibit := u64(arg2) << 41 // arg2 << 128
+		out1[0] = lo & 0xfffffffffff
+		out1[1] = ((lo >> 44) | (hi << 20)) & 0x7ffffffffff
+		out1[2] = ((hi >> 23) & 0x7ffffffffff) | hibit
+	} else {
+		tmp: [32]byte
+		copy_slice(tmp[0:16], arg1[:])
+		tmp[16] = arg2
+
+		_fe_from_bytes(out1, &tmp)
+		if sanitize {
+			// This is used to deserialize `s` which is confidential.
+			mem.zero_explicit(&tmp, size_of(tmp))
+		}
+	}
+}
+
+fe_from_u64s :: proc "contextless" (out1: ^Tight_Field_Element, lo, hi: u64) {
+	tmp: [32]byte
+	util.PUT_U64_LE(tmp[0:8], lo)
+	util.PUT_U64_LE(tmp[8:16], hi)
+
+	_fe_from_bytes(out1, &tmp)
+
+	// This routine is only used to deserialize `r` which is confidential.
+	mem.zero_explicit(&tmp, size_of(tmp))
+}

+ 356 - 0
core/crypto/_fiat/field_poly1305/field4344.odin

@@ -0,0 +1,356 @@
+// The BSD 1-Clause License (BSD-1-Clause)
+//
+// Copyright (c) 2015-2020 the fiat-crypto authors (see the AUTHORS file)
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     1. Redistributions of source code must retain the above copyright
+//        notice, this list of conditions and the following disclaimer.
+//
+// THIS SOFTWARE IS PROVIDED BY the fiat-crypto authors "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design,
+// Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package field_poly1305
+
+// This file provides arithmetic on the field Z/(2^130 - 5) using
+// unsaturated 64-bit integer arithmetic.  It is derived primarily
+// from the machine generate Golang output from the fiat-crypto project.
+//
+// While the base implementation is provably correct, this implementation
+// makes no such claims as the port and optimizations were done by hand.
+// At some point, it may be worth adding support to fiat-crypto for
+// generating Odin output.
+
+import fiat "core:crypto/_fiat"
+import "core:math/bits"
+
+Loose_Field_Element :: distinct [3]u64
+Tight_Field_Element :: distinct [3]u64
+
+_addcarryx_u44 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u64) -> (out1: u64, out2: fiat.u1) {
+	x1 := ((u64(arg1) + arg2) + arg3)
+	x2 := (x1 & 0xfffffffffff)
+	x3 := fiat.u1((x1 >> 44))
+	out1 = x2
+	out2 = x3
+	return
+}
+
+_subborrowx_u44 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u64) -> (out1: u64, out2: fiat.u1) {
+	x1 := ((i64(arg2) - i64(arg1)) - i64(arg3))
+	x2 := fiat.i1((x1 >> 44))
+	x3 := (u64(x1) & 0xfffffffffff)
+	out1 = x3
+	out2 = (0x0 - fiat.u1(x2))
+	return
+}
+
+_addcarryx_u43 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u64) -> (out1: u64, out2: fiat.u1) {
+	x1 := ((u64(arg1) + arg2) + arg3)
+	x2 := (x1 & 0x7ffffffffff)
+	x3 := fiat.u1((x1 >> 43))
+	out1 = x2
+	out2 = x3
+	return
+}
+
+_subborrowx_u43 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u64) -> (out1: u64, out2: fiat.u1) {
+	x1 := ((i64(arg2) - i64(arg1)) - i64(arg3))
+	x2 := fiat.i1((x1 >> 43))
+	x3 := (u64(x1) & 0x7ffffffffff)
+	out1 = x3
+	out2 = (0x0 - fiat.u1(x2))
+	return
+}
+
+fe_carry_mul :: proc (out1: ^Tight_Field_Element, arg1, arg2: ^Loose_Field_Element) {
+	x2, x1 := bits.mul_u64(arg1[2], (arg2[2] * 0x5))
+	x4, x3 := bits.mul_u64(arg1[2], (arg2[1] * 0xa))
+	x6, x5 := bits.mul_u64(arg1[1], (arg2[2] * 0xa))
+	x8, x7 := bits.mul_u64(arg1[2], arg2[0])
+	x10, x9 := bits.mul_u64(arg1[1], (arg2[1] * 0x2))
+	x12, x11 := bits.mul_u64(arg1[1], arg2[0])
+	x14, x13 := bits.mul_u64(arg1[0], arg2[2])
+	x16, x15 := bits.mul_u64(arg1[0], arg2[1])
+	x18, x17 := bits.mul_u64(arg1[0], arg2[0])
+	x19, x20 := bits.add_u64(x5, x3, u64(0x0))
+	x21, _ := bits.add_u64(x6, x4, u64(fiat.u1(x20)))
+	x23, x24 := bits.add_u64(x17, x19, u64(0x0))
+	x25, _ := bits.add_u64(x18, x21, u64(fiat.u1(x24)))
+	x27 := ((x23 >> 44) | ((x25 << 20) & 0xffffffffffffffff))
+	x28 := (x23 & 0xfffffffffff)
+	x29, x30 := bits.add_u64(x9, x7, u64(0x0))
+	x31, _ := bits.add_u64(x10, x8, u64(fiat.u1(x30)))
+	x33, x34 := bits.add_u64(x13, x29, u64(0x0))
+	x35, _ := bits.add_u64(x14, x31, u64(fiat.u1(x34)))
+	x37, x38 := bits.add_u64(x11, x1, u64(0x0))
+	x39, _ := bits.add_u64(x12, x2, u64(fiat.u1(x38)))
+	x41, x42 := bits.add_u64(x15, x37, u64(0x0))
+	x43, _ := bits.add_u64(x16, x39, u64(fiat.u1(x42)))
+	x45, x46 := bits.add_u64(x27, x41, u64(0x0))
+	x47 := (u64(fiat.u1(x46)) + x43)
+	x48 := ((x45 >> 43) | ((x47 << 21) & 0xffffffffffffffff))
+	x49 := (x45 & 0x7ffffffffff)
+	x50, x51 := bits.add_u64(x48, x33, u64(0x0))
+	x52 := (u64(fiat.u1(x51)) + x35)
+	x53 := ((x50 >> 43) | ((x52 << 21) & 0xffffffffffffffff))
+	x54 := (x50 & 0x7ffffffffff)
+	x55 := (x53 * 0x5)
+	x56 := (x28 + x55)
+	x57 := (x56 >> 44)
+	x58 := (x56 & 0xfffffffffff)
+	x59 := (x57 + x49)
+	x60 := fiat.u1((x59 >> 43))
+	x61 := (x59 & 0x7ffffffffff)
+	x62 := (u64(x60) + x54)
+	out1[0] = x58
+	out1[1] = x61
+	out1[2] = x62
+}
+
+fe_carry_square :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
+	x1 := (arg1[2] * 0x5)
+	x2 := (x1 * 0x2)
+	x3 := (arg1[2] * 0x2)
+	x4 := (arg1[1] * 0x2)
+	x6, x5 := bits.mul_u64(arg1[2], x1)
+	x8, x7 := bits.mul_u64(arg1[1], (x2 * 0x2))
+	x10, x9 := bits.mul_u64(arg1[1], (arg1[1] * 0x2))
+	x12, x11 := bits.mul_u64(arg1[0], x3)
+	x14, x13 := bits.mul_u64(arg1[0], x4)
+	x16, x15 := bits.mul_u64(arg1[0], arg1[0])
+	x17, x18 := bits.add_u64(x15, x7, u64(0x0))
+	x19, _ := bits.add_u64(x16, x8, u64(fiat.u1(x18)))
+	x21 := ((x17 >> 44) | ((x19 << 20) & 0xffffffffffffffff))
+	x22 := (x17 & 0xfffffffffff)
+	x23, x24 := bits.add_u64(x11, x9, u64(0x0))
+	x25, _ := bits.add_u64(x12, x10, u64(fiat.u1(x24)))
+	x27, x28 := bits.add_u64(x13, x5, u64(0x0))
+	x29, _ := bits.add_u64(x14, x6, u64(fiat.u1(x28)))
+	x31, x32 := bits.add_u64(x21, x27, u64(0x0))
+	x33 := (u64(fiat.u1(x32)) + x29)
+	x34 := ((x31 >> 43) | ((x33 << 21) & 0xffffffffffffffff))
+	x35 := (x31 & 0x7ffffffffff)
+	x36, x37 := bits.add_u64(x34, x23, u64(0x0))
+	x38 := (u64(fiat.u1(x37)) + x25)
+	x39 := ((x36 >> 43) | ((x38 << 21) & 0xffffffffffffffff))
+	x40 := (x36 & 0x7ffffffffff)
+	x41 := (x39 * 0x5)
+	x42 := (x22 + x41)
+	x43 := (x42 >> 44)
+	x44 := (x42 & 0xfffffffffff)
+	x45 := (x43 + x35)
+	x46 := fiat.u1((x45 >> 43))
+	x47 := (x45 & 0x7ffffffffff)
+	x48 := (u64(x46) + x40)
+	out1[0] = x44
+	out1[1] = x47
+	out1[2] = x48
+}
+
+fe_carry :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
+	x1 := arg1[0]
+	x2 := ((x1 >> 44) + arg1[1])
+	x3 := ((x2 >> 43) + arg1[2])
+	x4 := ((x1 & 0xfffffffffff) + ((x3 >> 43) * 0x5))
+	x5 := (u64(fiat.u1((x4 >> 44))) + (x2 & 0x7ffffffffff))
+	x6 := (x4 & 0xfffffffffff)
+	x7 := (x5 & 0x7ffffffffff)
+	x8 := (u64(fiat.u1((x5 >> 43))) + (x3 & 0x7ffffffffff))
+	out1[0] = x6
+	out1[1] = x7
+	out1[2] = x8
+}
+
+fe_add :: proc "contextless" (out1: ^Loose_Field_Element, arg1, arg2: ^Tight_Field_Element) {
+	x1 := (arg1[0] + arg2[0])
+	x2 := (arg1[1] + arg2[1])
+	x3 := (arg1[2] + arg2[2])
+	out1[0] = x1
+	out1[1] = x2
+	out1[2] = x3
+}
+
+fe_sub :: proc "contextless" (out1: ^Loose_Field_Element, arg1, arg2: ^Tight_Field_Element) {
+	x1 := ((0x1ffffffffff6 + arg1[0]) - arg2[0])
+	x2 := ((0xffffffffffe + arg1[1]) - arg2[1])
+	x3 := ((0xffffffffffe + arg1[2]) - arg2[2])
+	out1[0] = x1
+	out1[1] = x2
+	out1[2] = x3
+}
+
+fe_opp :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_Element) {
+	x1 := (0x1ffffffffff6 - arg1[0])
+	x2 := (0xffffffffffe - arg1[1])
+	x3 := (0xffffffffffe - arg1[2])
+	out1[0] = x1
+	out1[1] = x2
+	out1[2] = x3
+}
+
+fe_cond_assign :: proc "contextless" (out1, arg1: ^Tight_Field_Element, arg2: bool) {
+	x1 := fiat.cmovznz_u64(fiat.u1(arg2), out1[0], arg1[0])
+	x2 := fiat.cmovznz_u64(fiat.u1(arg2), out1[1], arg1[1])
+	x3 := fiat.cmovznz_u64(fiat.u1(arg2), out1[2], arg1[2])
+	out1[0] = x1
+	out1[1] = x2
+	out1[2] = x3
+}
+
+fe_to_bytes :: proc "contextless" (out1: ^[32]byte, arg1: ^Tight_Field_Element) {
+	x1, x2 := _subborrowx_u44(0x0, arg1[0], 0xffffffffffb)
+	x3, x4 := _subborrowx_u43(x2, arg1[1], 0x7ffffffffff)
+	x5, x6 := _subborrowx_u43(x4, arg1[2], 0x7ffffffffff)
+	x7 := fiat.cmovznz_u64(x6, u64(0x0), 0xffffffffffffffff)
+	x8, x9 := _addcarryx_u44(0x0, x1, (x7 & 0xffffffffffb))
+	x10, x11 := _addcarryx_u43(x9, x3, (x7 & 0x7ffffffffff))
+	x12, _ := _addcarryx_u43(x11, x5, (x7 & 0x7ffffffffff))
+	x14 := (x12 << 7)
+	x15 := (x10 << 4)
+	x16 := (u8(x8) & 0xff)
+	x17 := (x8 >> 8)
+	x18 := (u8(x17) & 0xff)
+	x19 := (x17 >> 8)
+	x20 := (u8(x19) & 0xff)
+	x21 := (x19 >> 8)
+	x22 := (u8(x21) & 0xff)
+	x23 := (x21 >> 8)
+	x24 := (u8(x23) & 0xff)
+	x25 := u8((x23 >> 8))
+	x26 := (x15 + u64(x25))
+	x27 := (u8(x26) & 0xff)
+	x28 := (x26 >> 8)
+	x29 := (u8(x28) & 0xff)
+	x30 := (x28 >> 8)
+	x31 := (u8(x30) & 0xff)
+	x32 := (x30 >> 8)
+	x33 := (u8(x32) & 0xff)
+	x34 := (x32 >> 8)
+	x35 := (u8(x34) & 0xff)
+	x36 := u8((x34 >> 8))
+	x37 := (x14 + u64(x36))
+	x38 := (u8(x37) & 0xff)
+	x39 := (x37 >> 8)
+	x40 := (u8(x39) & 0xff)
+	x41 := (x39 >> 8)
+	x42 := (u8(x41) & 0xff)
+	x43 := (x41 >> 8)
+	x44 := (u8(x43) & 0xff)
+	x45 := (x43 >> 8)
+	x46 := (u8(x45) & 0xff)
+	x47 := (x45 >> 8)
+	x48 := (u8(x47) & 0xff)
+	x49 := u8((x47 >> 8))
+	out1[0] = x16
+	out1[1] = x18
+	out1[2] = x20
+	out1[3] = x22
+	out1[4] = x24
+	out1[5] = x27
+	out1[6] = x29
+	out1[7] = x31
+	out1[8] = x33
+	out1[9] = x35
+	out1[10] = x38
+	out1[11] = x40
+	out1[12] = x42
+	out1[13] = x44
+	out1[14] = x46
+	out1[15] = x48
+	out1[16] = x49
+}
+
+_fe_from_bytes :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^[32]byte) {
+	x1 := (u64(arg1[16]) << 41)
+	x2 := (u64(arg1[15]) << 33)
+	x3 := (u64(arg1[14]) << 25)
+	x4 := (u64(arg1[13]) << 17)
+	x5 := (u64(arg1[12]) << 9)
+	x6 := (u64(arg1[11]) * u64(0x2))
+	x7 := (u64(arg1[10]) << 36)
+	x8 := (u64(arg1[9]) << 28)
+	x9 := (u64(arg1[8]) << 20)
+	x10 := (u64(arg1[7]) << 12)
+	x11 := (u64(arg1[6]) << 4)
+	x12 := (u64(arg1[5]) << 40)
+	x13 := (u64(arg1[4]) << 32)
+	x14 := (u64(arg1[3]) << 24)
+	x15 := (u64(arg1[2]) << 16)
+	x16 := (u64(arg1[1]) << 8)
+	x17 := arg1[0]
+	x18 := (x16 + u64(x17))
+	x19 := (x15 + x18)
+	x20 := (x14 + x19)
+	x21 := (x13 + x20)
+	x22 := (x12 + x21)
+	x23 := (x22 & 0xfffffffffff)
+	x24 := u8((x22 >> 44))
+	x25 := (x11 + u64(x24))
+	x26 := (x10 + x25)
+	x27 := (x9 + x26)
+	x28 := (x8 + x27)
+	x29 := (x7 + x28)
+	x30 := (x29 & 0x7ffffffffff)
+	x31 := fiat.u1((x29 >> 43))
+	x32 := (x6 + u64(x31))
+	x33 := (x5 + x32)
+	x34 := (x4 + x33)
+	x35 := (x3 + x34)
+	x36 := (x2 + x35)
+	x37 := (x1 + x36)
+	out1[0] = x23
+	out1[1] = x30
+	out1[2] = x37
+}
+
+fe_relax :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_Element) {
+	x1 := arg1[0]
+	x2 := arg1[1]
+	x3 := arg1[2]
+	out1[0] = x1
+	out1[1] = x2
+	out1[2] = x3
+}
+
+// The following routines were added by hand, and do not come from fiat-crypto.
+
+fe_zero :: proc "contextless" (out1: ^Tight_Field_Element) {
+	out1[0] = 0
+	out1[1] = 0
+	out1[2] = 0
+}
+
+fe_set :: #force_inline proc "contextless" (out1, arg1: ^Tight_Field_Element) {
+	x1 := arg1[0]
+	x2 := arg1[1]
+	x3 := arg1[2]
+	out1[0] = x1
+	out1[1] = x2
+	out1[2] = x3
+}
+
+fe_cond_swap :: proc "contextless" (out1, out2: ^Tight_Field_Element, arg1: bool) {
+	mask := -u64(arg1)
+	x := (out1[0] ~ out2[0]) & mask
+	x1, y1 := out1[0] ~ x, out2[0] ~ x
+	x = (out1[1] ~ out2[1]) & mask
+	x2, y2 := out1[1] ~ x, out2[1] ~ x
+	x = (out1[2] ~ out2[2]) & mask
+	x3, y3 := out1[2] ~ x, out2[2] ~ x
+	out1[0], out2[0] = x1, y1
+	out1[1], out2[1] = x2, y2
+	out1[2], out2[2] = x3, y3
+}

+ 117 - 28
core/crypto/blake/blake.odin

@@ -17,16 +17,21 @@ import "core:io"
     High level API
 */
 
+DIGEST_SIZE_224 :: 28
+DIGEST_SIZE_256 :: 32
+DIGEST_SIZE_384 :: 48
+DIGEST_SIZE_512 :: 64
+
 // hash_string_224 will hash the given input and return the
 // computed hash
-hash_string_224 :: proc "contextless" (data: string) -> [28]byte {
+hash_string_224 :: proc "contextless" (data: string) -> [DIGEST_SIZE_224]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 "contextless" (data: []byte) -> [28]byte {
-    hash: [28]byte
+hash_bytes_224 :: proc "contextless" (data: []byte) -> [DIGEST_SIZE_224]byte {
+    hash: [DIGEST_SIZE_224]byte
     ctx: Blake256_Context
     ctx.is224 = true
     init(&ctx)
@@ -35,10 +40,29 @@ hash_bytes_224 :: proc "contextless" (data: []byte) -> [28]byte {
     return hash
 }
 
+// hash_string_to_buffer_224 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_224(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_224 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
+    ctx: Blake256_Context
+    ctx.is224 = true
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, 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) {
-    hash: [28]byte
+hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
+    hash: [DIGEST_SIZE_224]byte
     ctx: Blake256_Context
     ctx.is224 = true
     init(&ctx)
@@ -57,7 +81,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
 
 // 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) {
+hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_224]byte, bool) {
     if !load_at_once {
         return hash_stream_224(os.stream_from_handle(hd))
     } else {
@@ -65,7 +89,7 @@ hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool)
             return hash_bytes_224(buf[:]), ok
         }
     }
-    return [28]byte{}, false
+    return [DIGEST_SIZE_224]byte{}, false
 }
 
 hash_224 :: proc {
@@ -73,18 +97,20 @@ hash_224 :: proc {
     hash_file_224,
     hash_bytes_224,
     hash_string_224,
+    hash_bytes_to_buffer_224,
+    hash_string_to_buffer_224,
 }
 
 // hash_string_256 will hash the given input and return the
 // computed hash
-hash_string_256 :: proc "contextless" (data: string) -> [32]byte {
+hash_string_256 :: proc "contextless" (data: string) -> [DIGEST_SIZE_256]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 "contextless" (data: []byte) -> [32]byte {
-    hash: [32]byte
+hash_bytes_256 :: proc "contextless" (data: []byte) -> [DIGEST_SIZE_256]byte {
+    hash: [DIGEST_SIZE_256]byte
     ctx: Blake256_Context
     ctx.is224 = false
     init(&ctx)
@@ -93,10 +119,29 @@ hash_bytes_256 :: proc "contextless" (data: []byte) -> [32]byte {
     return hash
 }
 
+// hash_string_to_buffer_256 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_256(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_256 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
+    ctx: Blake256_Context
+    ctx.is224 = false
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, 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) {
-    hash: [32]byte
+hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
+    hash: [DIGEST_SIZE_256]byte
     ctx: Blake256_Context
     ctx.is224 = false
     init(&ctx)
@@ -115,7 +160,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
 
 // 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) {
+hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
     if !load_at_once {
         return hash_stream_256(os.stream_from_handle(hd))
     } else {
@@ -123,7 +168,7 @@ hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool)
             return hash_bytes_256(buf[:]), ok
         }
     }
-    return [32]byte{}, false
+    return [DIGEST_SIZE_256]byte{}, false
 }
 
 hash_256 :: proc {
@@ -131,18 +176,20 @@ hash_256 :: proc {
     hash_file_256,
     hash_bytes_256,
     hash_string_256,
+    hash_bytes_to_buffer_256,
+    hash_string_to_buffer_256,
 }
 
 // hash_string_384 will hash the given input and return the
 // computed hash
-hash_string_384 :: proc "contextless" (data: string) -> [48]byte {
+hash_string_384 :: proc "contextless" (data: string) -> [DIGEST_SIZE_384]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 "contextless" (data: []byte) -> [48]byte {
-    hash: [48]byte
+hash_bytes_384 :: proc "contextless" (data: []byte) -> [DIGEST_SIZE_384]byte {
+    hash: [DIGEST_SIZE_384]byte
     ctx: Blake512_Context
     ctx.is384 = true
     init(&ctx)
@@ -151,10 +198,29 @@ hash_bytes_384 :: proc "contextless" (data: []byte) -> [48]byte {
     return hash
 }
 
+// hash_string_to_buffer_384 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_384(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_384 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_384, "Size of destination buffer is smaller than the digest size")
+    ctx: Blake512_Context
+    ctx.is384 = true
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, 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) {
-    hash: [48]byte
+hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
+    hash: [DIGEST_SIZE_384]byte
     ctx: Blake512_Context
     ctx.is384 = true
     init(&ctx)
@@ -173,7 +239,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
 
 // 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) {
+hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_384]byte, bool) {
     if !load_at_once {
         return hash_stream_384(os.stream_from_handle(hd))
     } else {
@@ -181,7 +247,7 @@ hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool)
             return hash_bytes_384(buf[:]), ok
         }
     }
-    return [48]byte{}, false
+    return [DIGEST_SIZE_384]byte{}, false
 }
 
 hash_384 :: proc {
@@ -189,18 +255,20 @@ hash_384 :: proc {
     hash_file_384,
     hash_bytes_384,
     hash_string_384,
+    hash_bytes_to_buffer_384,
+    hash_string_to_buffer_384,
 }
 
 // hash_string_512 will hash the given input and return the
 // computed hash
-hash_string_512 :: proc "contextless" (data: string) -> [64]byte {
+hash_string_512 :: proc "contextless" (data: string) -> [DIGEST_SIZE_512]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 "contextless" (data: []byte) -> [64]byte {
-    hash: [64]byte
+hash_bytes_512 :: proc "contextless" (data: []byte) -> [DIGEST_SIZE_512]byte {
+    hash: [DIGEST_SIZE_512]byte
     ctx: Blake512_Context
     ctx.is384 = false
     init(&ctx)
@@ -209,10 +277,29 @@ hash_bytes_512 :: proc "contextless" (data: []byte) -> [64]byte {
     return hash
 }
 
+// hash_string_to_buffer_512 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_512(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_512 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
+    ctx: Blake512_Context
+    ctx.is384 = false
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, 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) {
-    hash: [64]byte
+hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
+    hash: [DIGEST_SIZE_512]byte
     ctx: Blake512_Context
     ctx.is384 = false
     init(&ctx)
@@ -231,7 +318,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
 
 // 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) {
+hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512]byte, bool) {
     if !load_at_once {
         return hash_stream_512(os.stream_from_handle(hd))
     } else {
@@ -239,7 +326,7 @@ hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool)
             return hash_bytes_512(buf[:]), ok
         }
     }
-    return [64]byte{}, false
+    return [DIGEST_SIZE_512]byte{}, false
 }
 
 hash_512 :: proc {
@@ -247,6 +334,8 @@ hash_512 :: proc {
     hash_file_512,
     hash_bytes_512,
     hash_string_512,
+    hash_bytes_to_buffer_512,
+    hash_string_to_buffer_512,
 }
 
 /*

+ 33 - 7
core/crypto/blake2b/blake2b.odin

@@ -20,16 +20,18 @@ import "../_blake2"
     High level API
 */
 
+DIGEST_SIZE :: 64
+
 // hash_string will hash the given input and return the
 // computed hash
-hash_string :: proc(data: string) -> [64]byte {
+hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
     return hash_bytes(transmute([]byte)(data))
 }
 
 // hash_bytes will hash the given input and return the
 // computed hash
-hash_bytes :: proc(data: []byte) -> [64]byte {
-    hash: [64]byte
+hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
+    hash: [DIGEST_SIZE]byte
     ctx: _blake2.Blake2b_Context
     cfg: _blake2.Blake2_Config
     cfg.size = _blake2.BLAKE2B_SIZE
@@ -40,10 +42,32 @@ hash_bytes :: proc(data: []byte) -> [64]byte {
     return hash
 }
 
+// hash_string_to_buffer will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
+    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)
+}
+
+
 // hash_stream will read the stream in chunks and compute a
 // hash from its contents
-hash_stream :: proc(s: io.Stream) -> ([64]byte, bool) {
-    hash: [64]byte
+hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
+    hash: [DIGEST_SIZE]byte
     ctx: _blake2.Blake2b_Context
     cfg: _blake2.Blake2_Config
     cfg.size = _blake2.BLAKE2B_SIZE
@@ -64,7 +88,7 @@ hash_stream :: proc(s: io.Stream) -> ([64]byte, bool) {
 
 // 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) {
+hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
     if !load_at_once {
         return hash_stream(os.stream_from_handle(hd))
     } else {
@@ -72,7 +96,7 @@ hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
             return hash_bytes(buf[:]), ok
         }
     }
-    return [64]byte{}, false
+    return [DIGEST_SIZE]byte{}, false
 }
 
 hash :: proc {
@@ -80,6 +104,8 @@ hash :: proc {
     hash_file,
     hash_bytes,
     hash_string,
+    hash_bytes_to_buffer,
+    hash_string_to_buffer,
 }
 
 /*

+ 33 - 7
core/crypto/blake2s/blake2s.odin

@@ -20,16 +20,18 @@ import "../_blake2"
     High level API
 */
 
+DIGEST_SIZE :: 32
+
 // hash_string will hash the given input and return the
 // computed hash
-hash_string :: proc(data: string) -> [32]byte {
+hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
     return hash_bytes(transmute([]byte)(data))
 }
 
 // hash_bytes will hash the given input and return the
 // computed hash
-hash_bytes :: proc(data: []byte) -> [32]byte {
-    hash: [32]byte
+hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
+    hash: [DIGEST_SIZE]byte
     ctx: _blake2.Blake2s_Context
     cfg: _blake2.Blake2_Config
     cfg.size = _blake2.BLAKE2S_SIZE
@@ -40,10 +42,32 @@ hash_bytes :: proc(data: []byte) -> [32]byte {
     return hash
 }
 
+
+// hash_string_to_buffer will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
+    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)
+}
+
 // hash_stream will read the stream in chunks and compute a
 // hash from its contents
-hash_stream :: proc(s: io.Stream) -> ([32]byte, bool) {
-    hash: [32]byte
+hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
+    hash: [DIGEST_SIZE]byte
     ctx: _blake2.Blake2s_Context
     cfg: _blake2.Blake2_Config
     cfg.size = _blake2.BLAKE2S_SIZE
@@ -64,7 +88,7 @@ hash_stream :: proc(s: io.Stream) -> ([32]byte, bool) {
 
 // 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) {
+hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
     if !load_at_once {
         return hash_stream(os.stream_from_handle(hd))
     } else {
@@ -72,7 +96,7 @@ hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
             return hash_bytes(buf[:]), ok
         }
     }
-    return [32]byte{}, false
+    return [DIGEST_SIZE]byte{}, false
 }
 
 hash :: proc {
@@ -80,6 +104,8 @@ hash :: proc {
     hash_file,
     hash_bytes,
     hash_string,
+    hash_bytes_to_buffer,
+    hash_string_to_buffer,
 }
 
 /*

+ 581 - 0
core/crypto/chacha20/chacha20.odin

@@ -0,0 +1,581 @@
+package chacha20
+
+import "core:crypto/util"
+import "core:math/bits"
+import "core:mem"
+
+KEY_SIZE :: 32
+NONCE_SIZE :: 12
+XNONCE_SIZE :: 24
+
+_MAX_CTR_IETF :: 0xffffffff
+
+_BLOCK_SIZE :: 64
+_STATE_SIZE_U32 :: 16
+_ROUNDS :: 20
+
+_SIGMA_0 : u32 : 0x61707865
+_SIGMA_1 : u32 : 0x3320646e
+_SIGMA_2 : u32 : 0x79622d32
+_SIGMA_3 : u32 : 0x6b206574
+
+Context :: struct {
+	_s: [_STATE_SIZE_U32]u32,
+
+	_buffer: [_BLOCK_SIZE]byte,
+	_off: int,
+
+	_is_ietf_flavor: bool,
+	_is_initialized: bool,
+}
+
+init :: proc (ctx: ^Context, key, nonce: []byte) {
+	if len(key) != KEY_SIZE {
+		panic("crypto/chacha20: invalid ChaCha20 key size")
+	}
+	if n_len := len(nonce); n_len != NONCE_SIZE && n_len != XNONCE_SIZE {
+		panic("crypto/chacha20: invalid (X)ChaCha20 nonce size")
+	}
+
+	k, n := key, nonce
+
+	// Derive the XChaCha20 subkey and sub-nonce via HChaCha20.
+	is_xchacha := len(nonce) == XNONCE_SIZE
+	if is_xchacha {
+		sub_key := ctx._buffer[:KEY_SIZE]
+		_hchacha20(sub_key, k, n)
+		k = sub_key
+		n = n[16:24]
+	}
+
+	ctx._s[0] = _SIGMA_0
+	ctx._s[1] = _SIGMA_1
+	ctx._s[2] = _SIGMA_2
+	ctx._s[3] = _SIGMA_3
+	ctx._s[4] = util.U32_LE(k[0:4])
+	ctx._s[5] = util.U32_LE(k[4:8])
+	ctx._s[6] = util.U32_LE(k[8:12])
+	ctx._s[7] = util.U32_LE(k[12:16])
+	ctx._s[8] = util.U32_LE(k[16:20])
+	ctx._s[9] = util.U32_LE(k[20:24])
+	ctx._s[10] = util.U32_LE(k[24:28])
+	ctx._s[11] = util.U32_LE(k[28:32])
+	ctx._s[12] = 0
+	if !is_xchacha {
+		ctx._s[13] = util.U32_LE(n[0:4])
+		ctx._s[14] = util.U32_LE(n[4:8])
+		ctx._s[15] = util.U32_LE(n[8:12])
+	} else {
+		ctx._s[13] = 0
+		ctx._s[14] = util.U32_LE(n[0:4])
+		ctx._s[15] = util.U32_LE(n[4:8])
+
+		// The sub-key is stored in the keystream buffer.  While
+		// this will be overwritten in most circumstances, explicitly
+		// clear it out early.
+		mem.zero_explicit(&ctx._buffer, KEY_SIZE)
+	}
+
+	ctx._off = _BLOCK_SIZE
+	ctx._is_ietf_flavor = !is_xchacha
+	ctx._is_initialized = true
+}
+
+seek :: proc (ctx: ^Context, block_nr: u64) {
+	assert(ctx._is_initialized)
+
+	if ctx._is_ietf_flavor {
+		if block_nr > _MAX_CTR_IETF {
+			panic("crypto/chacha20: attempted to seek past maximum counter")
+		}
+	} else {
+		ctx._s[13] = u32(block_nr >> 32)
+	}
+	ctx._s[12] = u32(block_nr)
+	ctx._off = _BLOCK_SIZE
+}
+
+xor_bytes :: proc (ctx: ^Context, dst, src: []byte) {
+	assert(ctx._is_initialized)
+
+	// TODO: Enforcing that dst and src alias exactly or not at all
+	// is a good idea, though odd aliasing should be extremely uncommon.
+
+	src, dst := src, dst
+	if dst_len := len(dst); dst_len < len(src) {
+		src = src[:dst_len]
+	}
+
+	for remaining := len(src); remaining > 0; {
+		// Process multiple blocks at once
+		if ctx._off == _BLOCK_SIZE {
+			if nr_blocks := remaining / _BLOCK_SIZE; nr_blocks > 0 {
+				direct_bytes := nr_blocks * _BLOCK_SIZE
+				_do_blocks(ctx, dst, src, nr_blocks)
+				remaining -= direct_bytes
+				if remaining == 0 {
+					return
+				}
+				dst = dst[direct_bytes:]
+				src = src[direct_bytes:]
+			}
+
+			// If there is a partial block, generate and buffer 1 block
+			// worth of keystream.
+			_do_blocks(ctx, ctx._buffer[:], nil, 1)
+			ctx._off = 0
+		}
+
+		// Process partial blocks from the buffered keystream.
+		to_xor := min(_BLOCK_SIZE - ctx._off, remaining)
+		buffered_keystream := ctx._buffer[ctx._off:]
+		for i := 0; i < to_xor; i = i + 1 {
+			dst[i] = buffered_keystream[i] ~ src[i]
+		}
+		ctx._off += to_xor
+		dst = dst[to_xor:]
+		src = src[to_xor:]
+		remaining -= to_xor
+	}
+}
+
+keystream_bytes :: proc (ctx: ^Context, dst: []byte) {
+	assert(ctx._is_initialized)
+
+	dst := dst
+	for remaining := len(dst); remaining > 0; {
+		// Process multiple blocks at once
+		if ctx._off == _BLOCK_SIZE {
+			if nr_blocks := remaining / _BLOCK_SIZE; nr_blocks > 0 {
+				direct_bytes := nr_blocks * _BLOCK_SIZE
+				_do_blocks(ctx, dst, nil, nr_blocks)
+				remaining -= direct_bytes
+				if remaining == 0 {
+					return
+				}
+				dst = dst[direct_bytes:]
+			}
+
+			// If there is a partial block, generate and buffer 1 block
+			// worth of keystream.
+			_do_blocks(ctx, ctx._buffer[:], nil, 1)
+			ctx._off = 0
+		}
+
+		// Process partial blocks from the buffered keystream.
+		to_copy := min(_BLOCK_SIZE - ctx._off, remaining)
+		buffered_keystream := ctx._buffer[ctx._off:]
+		copy(dst[:to_copy], buffered_keystream[:to_copy])
+		ctx._off += to_copy
+		dst = dst[to_copy:]
+		remaining -= to_copy
+	}
+}
+
+reset :: proc (ctx: ^Context) {
+	mem.zero_explicit(&ctx._s, size_of(ctx._s))
+	mem.zero_explicit(&ctx._buffer, size_of(ctx._buffer))
+
+	ctx._is_initialized = false
+}
+
+_do_blocks :: proc (ctx: ^Context, dst, src: []byte, nr_blocks: int) {
+	// Enforce the maximum consumed keystream per nonce.
+	//
+	// While all modern "standard" definitions of ChaCha20 use
+	// the IETF 32-bit counter, for XChaCha20 most common
+	// implementations allow for a 64-bit counter.
+	//
+	// Honestly, the answer here is "use a MRAE primitive", but
+	// go with common practice in the case of XChaCha20.
+	if ctx._is_ietf_flavor {
+		if u64(ctx._s[12]) + u64(nr_blocks) > 0xffffffff {
+			panic("crypto/chacha20: maximum ChaCha20 keystream per nonce reached")
+		}
+	} else {
+		ctr := (u64(ctx._s[13]) << 32) | u64(ctx._s[12])
+		if _, carry := bits.add_u64(ctr, u64(nr_blocks), 0); carry != 0 {
+			panic("crypto/chacha20: maximum XChaCha20 keystream per nonce reached")
+		}
+	}
+
+	dst, src := dst, src
+	x := &ctx._s
+	for n := 0; n < nr_blocks; n = n + 1 {
+		x0, x1, x2, x3 := _SIGMA_0, _SIGMA_1, _SIGMA_2, _SIGMA_3
+		x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15 := x[4], x[5], x[6], x[7], x[8], x[9], x[10], x[11], x[12], x[13], x[14], x[15]
+
+		for i := _ROUNDS; i > 0; i = i - 2 {
+			// Even when forcing inlining manually inlining all of
+			// these is decently faster.
+
+			// quarterround(x, 0, 4, 8, 12)
+			x0 += x4
+			x12 ~= x0
+			x12 = util.ROTL32(x12, 16)
+			x8 += x12
+			x4 ~= x8
+			x4 = util.ROTL32(x4, 12)
+			x0 += x4
+			x12 ~= x0
+			x12 = util.ROTL32(x12, 8)
+			x8 += x12
+			x4 ~= x8
+			x4 = util.ROTL32(x4, 7)
+
+			// quarterround(x, 1, 5, 9, 13)
+			x1 += x5
+			x13 ~= x1
+			x13 = util.ROTL32(x13, 16)
+			x9 += x13
+			x5 ~= x9
+			x5 = util.ROTL32(x5, 12)
+			x1 += x5
+			x13 ~= x1
+			x13 = util.ROTL32(x13, 8)
+			x9 += x13
+			x5 ~= x9
+			x5 = util.ROTL32(x5, 7)
+
+			// quarterround(x, 2, 6, 10, 14)
+			x2 += x6
+			x14 ~= x2
+			x14 = util.ROTL32(x14, 16)
+			x10 += x14
+			x6 ~= x10
+			x6 = util.ROTL32(x6, 12)
+			x2 += x6
+			x14 ~= x2
+			x14 = util.ROTL32(x14, 8)
+			x10 += x14
+			x6 ~= x10
+			x6 = util.ROTL32(x6, 7)
+
+			// quarterround(x, 3, 7, 11, 15)
+			x3 += x7
+			x15 ~= x3
+			x15 = util.ROTL32(x15, 16)
+			x11 += x15
+			x7 ~= x11
+			x7 = util.ROTL32(x7, 12)
+			x3 += x7
+			x15 ~= x3
+			x15 = util.ROTL32(x15, 8)
+			x11 += x15
+			x7 ~= x11
+			x7 = util.ROTL32(x7, 7)
+
+			// quarterround(x, 0, 5, 10, 15)
+			x0 += x5
+			x15 ~= x0
+			x15 = util.ROTL32(x15, 16)
+			x10 += x15
+			x5 ~= x10
+			x5 = util.ROTL32(x5, 12)
+			x0 += x5
+			x15 ~= x0
+			x15 = util.ROTL32(x15, 8)
+			x10 += x15
+			x5 ~= x10
+			x5 = util.ROTL32(x5, 7)
+
+			// quarterround(x, 1, 6, 11, 12)
+			x1 += x6
+			x12 ~= x1
+			x12 = util.ROTL32(x12, 16)
+			x11 += x12
+			x6 ~= x11
+			x6 = util.ROTL32(x6, 12)
+			x1 += x6
+			x12 ~= x1
+			x12 = util.ROTL32(x12, 8)
+			x11 += x12
+			x6 ~= x11
+			x6 = util.ROTL32(x6, 7)
+
+			// quarterround(x, 2, 7, 8, 13)
+			x2 += x7
+			x13 ~= x2
+			x13 = util.ROTL32(x13, 16)
+			x8 += x13
+			x7 ~= x8
+			x7 = util.ROTL32(x7, 12)
+			x2 += x7
+			x13 ~= x2
+			x13 = util.ROTL32(x13, 8)
+			x8 += x13
+			x7 ~= x8
+			x7 = util.ROTL32(x7, 7)
+
+			// quarterround(x, 3, 4, 9, 14)
+			x3 += x4
+			x14 ~= x3
+			x14 = util.ROTL32(x14, 16)
+			x9 += x14
+			x4 ~= x9
+			x4 = util.ROTL32(x4, 12)
+			x3 += x4
+			x14 ~= x3
+			x14 = util.ROTL32(x14, 8)
+			x9 += x14
+			x4 ~= x9
+			x4 = util.ROTL32(x4, 7)
+		}
+
+		x0 += _SIGMA_0
+		x1 += _SIGMA_1
+		x2 += _SIGMA_2
+		x3 += _SIGMA_3
+		x4 += x[4]
+		x5 += x[5]
+		x6 += x[6]
+		x7 += x[7]
+		x8 += x[8]
+		x9 += x[9]
+		x10 += x[10]
+		x11 += x[11]
+		x12 += x[12]
+		x13 += x[13]
+		x14 += x[14]
+		x15 += x[15]
+
+		// While the "correct" answer to getting more performance out of
+		// this is "use vector operations", support for that is currently
+		// a work in progress/to be designed.
+		//
+		// Until dedicated assembly can be written leverage the fact that
+		// the callers of this routine ensure that src/dst are valid.
+
+		when ODIN_ARCH == "386" || ODIN_ARCH == "amd64" {
+			// util.PUT_U32_LE/util.U32_LE are not required on little-endian
+			// systems that also happen to not be strict about aligned
+			// memory access.
+
+			dst_p := transmute(^[16]u32)(&dst[0])
+			if src != nil {
+				src_p := transmute(^[16]u32)(&src[0])
+				dst_p[0] = src_p[0] ~ x0
+				dst_p[1] = src_p[1] ~ x1
+				dst_p[2] = src_p[2] ~ x2
+				dst_p[3] = src_p[3] ~ x3
+				dst_p[4] = src_p[4] ~ x4
+				dst_p[5] = src_p[5] ~ x5
+				dst_p[6] = src_p[6] ~ x6
+				dst_p[7] = src_p[7] ~ x7
+				dst_p[8] = src_p[8] ~ x8
+				dst_p[9] = src_p[9] ~ x9
+				dst_p[10] = src_p[10] ~ x10
+				dst_p[11] = src_p[11] ~ x11
+				dst_p[12] = src_p[12] ~ x12
+				dst_p[13] = src_p[13] ~ x13
+				dst_p[14] = src_p[14] ~ x14
+				dst_p[15] = src_p[15] ~ x15
+				src = src[_BLOCK_SIZE:]
+			} else {
+				dst_p[0] = x0
+				dst_p[1] = x1
+				dst_p[2] = x2
+				dst_p[3] = x3
+				dst_p[4] = x4
+				dst_p[5] = x5
+				dst_p[6] = x6
+				dst_p[7] = x7
+				dst_p[8] = x8
+				dst_p[9] = x9
+				dst_p[10] = x10
+				dst_p[11] = x11
+				dst_p[12] = x12
+				dst_p[13] = x13
+				dst_p[14] = x14
+				dst_p[15] = x15
+			}
+			dst = dst[_BLOCK_SIZE:]
+		} else {
+			#no_bounds_check {
+				if src != nil {
+					util.PUT_U32_LE(dst[0:4], util.U32_LE(src[0:4]) ~ x0)
+					util.PUT_U32_LE(dst[4:8], util.U32_LE(src[4:8]) ~ x1)
+					util.PUT_U32_LE(dst[8:12], util.U32_LE(src[8:12]) ~ x2)
+					util.PUT_U32_LE(dst[12:16], util.U32_LE(src[12:16]) ~ x3)
+					util.PUT_U32_LE(dst[16:20], util.U32_LE(src[16:20]) ~ x4)
+					util.PUT_U32_LE(dst[20:24], util.U32_LE(src[20:24]) ~ x5)
+					util.PUT_U32_LE(dst[24:28], util.U32_LE(src[24:28]) ~ x6)
+					util.PUT_U32_LE(dst[28:32], util.U32_LE(src[28:32]) ~ x7)
+					util.PUT_U32_LE(dst[32:36], util.U32_LE(src[32:36]) ~ x8)
+					util.PUT_U32_LE(dst[36:40], util.U32_LE(src[36:40]) ~ x9)
+					util.PUT_U32_LE(dst[40:44], util.U32_LE(src[40:44]) ~ x10)
+					util.PUT_U32_LE(dst[44:48], util.U32_LE(src[44:48]) ~ x11)
+					util.PUT_U32_LE(dst[48:52], util.U32_LE(src[48:52]) ~ x12)
+					util.PUT_U32_LE(dst[52:56], util.U32_LE(src[52:56]) ~ x13)
+					util.PUT_U32_LE(dst[56:60], util.U32_LE(src[56:60]) ~ x14)
+					util.PUT_U32_LE(dst[60:64], util.U32_LE(src[60:64]) ~ x15)
+					src = src[_BLOCK_SIZE:]
+				} else {
+					util.PUT_U32_LE(dst[0:4], x0)
+					util.PUT_U32_LE(dst[4:8], x1)
+					util.PUT_U32_LE(dst[8:12], x2)
+					util.PUT_U32_LE(dst[12:16], x3)
+					util.PUT_U32_LE(dst[16:20], x4)
+					util.PUT_U32_LE(dst[20:24], x5)
+					util.PUT_U32_LE(dst[24:28], x6)
+					util.PUT_U32_LE(dst[28:32], x7)
+					util.PUT_U32_LE(dst[32:36], x8)
+					util.PUT_U32_LE(dst[36:40], x9)
+					util.PUT_U32_LE(dst[40:44], x10)
+					util.PUT_U32_LE(dst[44:48], x11)
+					util.PUT_U32_LE(dst[48:52], x12)
+					util.PUT_U32_LE(dst[52:56], x13)
+					util.PUT_U32_LE(dst[56:60], x14)
+					util.PUT_U32_LE(dst[60:64], x15)
+				}
+				dst = dst[_BLOCK_SIZE:]
+			}
+		}
+
+		// Increment the counter.  Overflow checking is done upon
+		// entry into the routine, so a 64-bit increment safely
+		// covers both cases.
+		new_ctr := ((u64(ctx._s[13]) << 32) | u64(ctx._s[12])) + 1
+		x[12] = u32(new_ctr)
+		x[13] = u32(new_ctr >> 32)
+	}
+}
+
+_hchacha20 :: proc (dst, key, nonce: []byte) {
+	x0, x1, x2, x3 := _SIGMA_0, _SIGMA_1, _SIGMA_2, _SIGMA_3
+	x4 := util.U32_LE(key[0:4])
+	x5 := util.U32_LE(key[4:8])
+	x6 := util.U32_LE(key[8:12])
+	x7 := util.U32_LE(key[12:16])
+	x8 := util.U32_LE(key[16:20])
+	x9 := util.U32_LE(key[20:24])
+	x10 := util.U32_LE(key[24:28])
+	x11 := util.U32_LE(key[28:32])
+	x12 := util.U32_LE(nonce[0:4])
+	x13 := util.U32_LE(nonce[4:8])
+	x14 := util.U32_LE(nonce[8:12])
+	x15 := util.U32_LE(nonce[12:16])
+
+	for i := _ROUNDS; i > 0; i = i - 2 {
+		// quarterround(x, 0, 4, 8, 12)
+		x0 += x4
+		x12 ~= x0
+		x12 = util.ROTL32(x12, 16)
+		x8 += x12
+		x4 ~= x8
+		x4 = util.ROTL32(x4, 12)
+		x0 += x4
+		x12 ~= x0
+		x12 = util.ROTL32(x12, 8)
+		x8 += x12
+		x4 ~= x8
+		x4 = util.ROTL32(x4, 7)
+
+		// quarterround(x, 1, 5, 9, 13)
+		x1 += x5
+		x13 ~= x1
+		x13 = util.ROTL32(x13, 16)
+		x9 += x13
+		x5 ~= x9
+		x5 = util.ROTL32(x5, 12)
+		x1 += x5
+		x13 ~= x1
+		x13 = util.ROTL32(x13, 8)
+		x9 += x13
+		x5 ~= x9
+		x5 = util.ROTL32(x5, 7)
+
+		// quarterround(x, 2, 6, 10, 14)
+		x2 += x6
+		x14 ~= x2
+		x14 = util.ROTL32(x14, 16)
+		x10 += x14
+		x6 ~= x10
+		x6 = util.ROTL32(x6, 12)
+		x2 += x6
+		x14 ~= x2
+		x14 = util.ROTL32(x14, 8)
+		x10 += x14
+		x6 ~= x10
+		x6 = util.ROTL32(x6, 7)
+
+		// quarterround(x, 3, 7, 11, 15)
+		x3 += x7
+		x15 ~= x3
+		x15 = util.ROTL32(x15, 16)
+		x11 += x15
+		x7 ~= x11
+		x7 = util.ROTL32(x7, 12)
+		x3 += x7
+		x15 ~= x3
+		x15 = util.ROTL32(x15, 8)
+		x11 += x15
+		x7 ~= x11
+		x7 = util.ROTL32(x7, 7)
+
+		// quarterround(x, 0, 5, 10, 15)
+		x0 += x5
+		x15 ~= x0
+		x15 = util.ROTL32(x15, 16)
+		x10 += x15
+		x5 ~= x10
+		x5 = util.ROTL32(x5, 12)
+		x0 += x5
+		x15 ~= x0
+		x15 = util.ROTL32(x15, 8)
+		x10 += x15
+		x5 ~= x10
+		x5 = util.ROTL32(x5, 7)
+
+		// quarterround(x, 1, 6, 11, 12)
+		x1 += x6
+		x12 ~= x1
+		x12 = util.ROTL32(x12, 16)
+		x11 += x12
+		x6 ~= x11
+		x6 = util.ROTL32(x6, 12)
+		x1 += x6
+		x12 ~= x1
+		x12 = util.ROTL32(x12, 8)
+		x11 += x12
+		x6 ~= x11
+		x6 = util.ROTL32(x6, 7)
+
+		// quarterround(x, 2, 7, 8, 13)
+		x2 += x7
+		x13 ~= x2
+		x13 = util.ROTL32(x13, 16)
+		x8 += x13
+		x7 ~= x8
+		x7 = util.ROTL32(x7, 12)
+		x2 += x7
+		x13 ~= x2
+		x13 = util.ROTL32(x13, 8)
+		x8 += x13
+		x7 ~= x8
+		x7 = util.ROTL32(x7, 7)
+
+		// quarterround(x, 3, 4, 9, 14)
+		x3 += x4
+		x14 ~= x3
+		x14 = util.ROTL32(x14, 16)
+		x9 += x14
+		x4 ~= x9
+		x4 = util.ROTL32(x4, 12)
+		x3 += x4
+		x14 ~= x3
+		x14 = util.ROTL32(x14, 8)
+		x9 += x14
+		x4 ~= x9
+		x4 = util.ROTL32(x4, 7)
+	}
+
+	util.PUT_U32_LE(dst[0:4], x0)
+	util.PUT_U32_LE(dst[4:8], x1)
+	util.PUT_U32_LE(dst[8:12], x2)
+	util.PUT_U32_LE(dst[12:16], x3)
+	util.PUT_U32_LE(dst[16:20], x12)
+	util.PUT_U32_LE(dst[20:24], x13)
+	util.PUT_U32_LE(dst[24:28], x14)
+	util.PUT_U32_LE(dst[28:32], x15)
+}

+ 146 - 0
core/crypto/chacha20poly1305/chacha20poly1305.odin

@@ -0,0 +1,146 @@
+package chacha20poly1305
+
+import "core:crypto"
+import "core:crypto/chacha20"
+import "core:crypto/poly1305"
+import "core:crypto/util"
+import "core:mem"
+
+KEY_SIZE :: chacha20.KEY_SIZE
+NONCE_SIZE :: chacha20.NONCE_SIZE
+TAG_SIZE :: poly1305.TAG_SIZE
+
+_P_MAX :: 64 * 0xffffffff // 64 * (2^32-1)
+
+_validate_common_slice_sizes :: proc (tag, key, nonce, aad, text: []byte) {
+	if len(tag) != TAG_SIZE {
+		panic("crypto/chacha20poly1305: invalid destination tag size")
+	}
+	if len(key) != KEY_SIZE {
+		panic("crypto/chacha20poly1305: invalid key size")
+	}
+	if len(nonce) != NONCE_SIZE {
+		panic("crypto/chacha20poly1305: invalid nonce size")
+	}
+
+	#assert(size_of(int) == 8 || size_of(int) <= 4)
+	when size_of(int) == 8 {
+		// A_MAX = 2^64 - 1 due to the length field limit.
+		// P_MAX = 64 * (2^32 - 1) due to the IETF ChaCha20 counter limit.
+		//
+		// A_MAX is limited by size_of(int), so there is no need to
+		// enforce it. P_MAX only needs to be checked on 64-bit targets,
+		// for reasons that should be obvious.
+		if text_len := len(text); text_len > _P_MAX {
+			panic("crypto/chacha20poly1305: oversized src data")
+		}
+	}
+}
+
+_PAD: [16]byte
+_update_mac_pad16 :: #force_inline proc (ctx: ^poly1305.Context, x_len: int) {
+	if pad_len := 16 - (x_len & (16-1)); pad_len != 16 {
+		poly1305.update(ctx, _PAD[:pad_len])
+	}
+}
+
+encrypt :: proc (ciphertext, tag, key, nonce, aad, plaintext: []byte) {
+	_validate_common_slice_sizes(tag, key, nonce, aad, plaintext)
+	if len(ciphertext) != len(plaintext) {
+		panic("crypto/chacha20poly1305: invalid destination ciphertext size")
+	}
+
+	stream_ctx: chacha20.Context = ---
+	chacha20.init(&stream_ctx, key, nonce)
+
+	// otk = poly1305_key_gen(key, nonce)
+	otk: [poly1305.KEY_SIZE]byte = ---
+	chacha20.keystream_bytes(&stream_ctx, otk[:])
+	mac_ctx: poly1305.Context = ---
+	poly1305.init(&mac_ctx, otk[:])
+	mem.zero_explicit(&otk, size_of(otk))
+
+	aad_len, ciphertext_len := len(aad), len(ciphertext)
+
+	// There is nothing preventing aad and ciphertext from overlapping
+	// so auth the AAD before encrypting (slightly different from the
+	// RFC, since the RFC encrypts into a new buffer).
+	//
+	// mac_data = aad | pad16(aad)
+	poly1305.update(&mac_ctx, aad)
+	_update_mac_pad16(&mac_ctx, aad_len)
+
+	// ciphertext = chacha20_encrypt(key, 1, nonce, plaintext)
+	chacha20.seek(&stream_ctx, 1)
+	chacha20.xor_bytes(&stream_ctx, ciphertext, plaintext)
+	chacha20.reset(&stream_ctx) // Don't need the stream context anymore.
+
+	// mac_data |= ciphertext | pad16(ciphertext)
+	poly1305.update(&mac_ctx, ciphertext)
+	_update_mac_pad16(&mac_ctx, ciphertext_len)
+
+	// mac_data |= num_to_8_le_bytes(aad.length)
+	// mac_data |= num_to_8_le_bytes(ciphertext.length)
+	l_buf := otk[0:16] // Reuse the scratch buffer.
+	util.PUT_U64_LE(l_buf[0:8], u64(aad_len))
+	util.PUT_U64_LE(l_buf[8:16], u64(ciphertext_len))
+	poly1305.update(&mac_ctx, l_buf)
+
+	// tag = poly1305_mac(mac_data, otk)
+	poly1305.final(&mac_ctx, tag) // Implicitly sanitizes context.
+}
+
+decrypt :: proc (plaintext, tag, key, nonce, aad, ciphertext: []byte) -> bool {
+	_validate_common_slice_sizes(tag, key, nonce, aad, ciphertext)
+	if len(ciphertext) != len(plaintext) {
+		panic("crypto/chacha20poly1305: invalid destination plaintext size")
+	}
+
+	// Note: Unlike encrypt, this can fail early, so use defer for
+	// sanitization rather than assuming control flow reaches certain
+	// points where needed.
+
+	stream_ctx: chacha20.Context = ---
+	chacha20.init(&stream_ctx, key, nonce)
+
+	// otk = poly1305_key_gen(key, nonce)
+	otk: [poly1305.KEY_SIZE]byte = ---
+	chacha20.keystream_bytes(&stream_ctx, otk[:])
+	defer chacha20.reset(&stream_ctx)
+
+	mac_ctx: poly1305.Context = ---
+	poly1305.init(&mac_ctx, otk[:])
+	defer mem.zero_explicit(&otk, size_of(otk))
+
+	aad_len, ciphertext_len := len(aad), len(ciphertext)
+
+	// mac_data = aad | pad16(aad)
+	// mac_data |= ciphertext | pad16(ciphertext)
+	// mac_data |= num_to_8_le_bytes(aad.length)
+	// mac_data |= num_to_8_le_bytes(ciphertext.length)
+	poly1305.update(&mac_ctx, aad)
+	_update_mac_pad16(&mac_ctx, aad_len)
+	poly1305.update(&mac_ctx, ciphertext)
+	_update_mac_pad16(&mac_ctx, ciphertext_len)
+	l_buf := otk[0:16] // Reuse the scratch buffer.
+	util.PUT_U64_LE(l_buf[0:8], u64(aad_len))
+	util.PUT_U64_LE(l_buf[8:16], u64(ciphertext_len))
+	poly1305.update(&mac_ctx, l_buf)
+
+	// tag = poly1305_mac(mac_data, otk)
+	derived_tag := otk[0:poly1305.TAG_SIZE] // Reuse the scratch buffer again.
+	poly1305.final(&mac_ctx, derived_tag) // Implicitly sanitizes context.
+
+	// Validate the tag in constant time.
+	if crypto.compare_constant_time(tag, derived_tag) != 1 {
+		// Zero out the plaintext, as a defense in depth measure.
+		mem.zero_explicit(raw_data(plaintext), ciphertext_len)
+		return false
+	}
+
+	// plaintext = chacha20_decrypt(key, 1, nonce, ciphertext)
+	chacha20.seek(&stream_ctx, 1)
+	chacha20.xor_bytes(&stream_ctx, plaintext, ciphertext)
+
+	return true
+}

+ 52 - 0
core/crypto/crypto.odin

@@ -0,0 +1,52 @@
+package crypto
+
+import "core:mem"
+
+// compare_constant_time returns 1 iff a and b are equal, 0 otherwise.
+//
+// The execution time of this routine is constant regardless of the contents
+// of the slices being compared, as long as the length of the slices is equal.
+// If the length of the two slices is different, it will early-return 0.
+compare_constant_time :: proc "contextless" (a, b: []byte) -> int {
+	// If the length of the slices is different, early return.
+	//
+	// This leaks the fact that the slices have a different length,
+	// but the routine is primarily intended for comparing things
+	// like MACS and password digests.
+	n := len(a)
+	if n != len(b) {
+		return 0
+	}
+
+	return compare_byte_ptrs_constant_time(raw_data(a), raw_data(b), n)
+}
+
+// compare_byte_ptrs_constant_time returns 1 iff the bytes pointed to by
+// a and b are equal, 0 otherwise.
+//
+// The execution time of this routine is constant regardless of the
+// contents of the memory being compared.
+compare_byte_ptrs_constant_time :: proc "contextless" (a, b: ^byte, n: int) -> int {
+	x := mem.slice_ptr(a, n)
+	y := mem.slice_ptr(b, n)
+
+	v: byte
+	for i in 0..<n {
+		v |= x[i] ~ y[i]
+	}
+
+	// After the loop, v == 0 iff a == b.  The subtraction will underflow
+	// iff v == 0, setting the sign-bit, which gets returned.
+	return int((u32(v)-1) >> 31)
+}
+
+// rand_bytes fills the dst buffer with cryptographic entropy taken from
+// the system entropy source.  This routine will block if the system entropy
+// source is not ready yet.  All system entropy source failures are treated
+// as catastrophic, resulting in a panic.
+rand_bytes :: proc (dst: []byte) {
+	// zero-fill the buffer first
+	mem.zero_explicit(raw_data(dst), len(dst))
+
+	_rand_bytes(dst)
+}

+ 29 - 7
core/crypto/gost/gost.odin

@@ -18,16 +18,18 @@ import "core:io"
     High level API
 */
 
+DIGEST_SIZE :: 32
+
 // hash_string will hash the given input and return the
 // computed hash
-hash_string :: proc(data: string) -> [32]byte {
+hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
     return hash_bytes(transmute([]byte)(data))
 }
 
 // hash_bytes will hash the given input and return the
 // computed hash
-hash_bytes :: proc(data: []byte) -> [32]byte {
-    hash: [32]byte
+hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
+    hash: [DIGEST_SIZE]byte
     ctx: Gost_Context
     init(&ctx)
     update(&ctx, data)
@@ -35,10 +37,28 @@ hash_bytes :: proc(data: []byte) -> [32]byte {
     return hash
 }
 
+// hash_string_to_buffer will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
+    ctx: Gost_Context
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, 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) {
-    hash: [32]byte
+hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
+    hash: [DIGEST_SIZE]byte
     ctx: Gost_Context
     init(&ctx)
     buf := make([]byte, 512)
@@ -56,7 +76,7 @@ hash_stream :: proc(s: io.Stream) -> ([32]byte, bool) {
 
 // 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) {
+hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
     if !load_at_once {
         return hash_stream(os.stream_from_handle(hd))
     } else {
@@ -64,7 +84,7 @@ hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
             return hash_bytes(buf[:]), ok
         }
     }
-    return [32]byte{}, false
+    return [DIGEST_SIZE]byte{}, false
 }
 
 hash :: proc {
@@ -72,6 +92,8 @@ hash :: proc {
     hash_file,
     hash_bytes,
     hash_string,
+    hash_bytes_to_buffer,
+    hash_string_to_buffer,
 }
 
 /*

+ 117 - 28
core/crypto/groestl/groestl.odin

@@ -17,16 +17,21 @@ import "core:io"
     High level API
 */
 
+DIGEST_SIZE_224 :: 28
+DIGEST_SIZE_256 :: 32
+DIGEST_SIZE_384 :: 48
+DIGEST_SIZE_512 :: 64
+
 // 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(data: string) -> [DIGEST_SIZE_224]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 {
-    hash: [28]byte
+hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
+    hash: [DIGEST_SIZE_224]byte
     ctx: Groestl_Context
     ctx.hashbitlen = 224
     init(&ctx)
@@ -35,10 +40,29 @@ hash_bytes_224 :: proc(data: []byte) -> [28]byte {
     return hash
 }
 
+// hash_string_to_buffer_224 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_224(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_224 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
+    ctx: Groestl_Context
+    ctx.hashbitlen = 224
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, 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) {
-    hash: [28]byte
+hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
+    hash: [DIGEST_SIZE_224]byte
     ctx: Groestl_Context
     ctx.hashbitlen = 224
     init(&ctx)
@@ -57,7 +81,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
 
 // 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) {
+hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_224]byte, bool) {
     if !load_at_once {
         return hash_stream_224(os.stream_from_handle(hd))
     } else {
@@ -65,7 +89,7 @@ hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool)
             return hash_bytes_224(buf[:]), ok
         }
     }
-    return [28]byte{}, false
+    return [DIGEST_SIZE_224]byte{}, false
 }
 
 hash_224 :: proc {
@@ -73,18 +97,20 @@ hash_224 :: proc {
     hash_file_224,
     hash_bytes_224,
     hash_string_224,
+    hash_bytes_to_buffer_224,
+    hash_string_to_buffer_224,
 }
 
 // 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(data: string) -> [DIGEST_SIZE_256]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 {
-    hash: [32]byte
+hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
+    hash: [DIGEST_SIZE_256]byte
     ctx: Groestl_Context
     ctx.hashbitlen = 256
     init(&ctx)
@@ -93,10 +119,29 @@ hash_bytes_256 :: proc(data: []byte) -> [32]byte {
     return hash
 }
 
+// hash_string_to_buffer_256 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_256(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_256 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
+    ctx: Groestl_Context
+    ctx.hashbitlen = 256
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, 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) {
-    hash: [32]byte
+hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
+    hash: [DIGEST_SIZE_256]byte
     ctx: Groestl_Context
     ctx.hashbitlen = 256
     init(&ctx)
@@ -115,7 +160,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
 
 // 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) {
+hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
     if !load_at_once {
         return hash_stream_256(os.stream_from_handle(hd))
     } else {
@@ -123,7 +168,7 @@ hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool)
             return hash_bytes_256(buf[:]), ok
         }
     }
-    return [32]byte{}, false
+    return [DIGEST_SIZE_256]byte{}, false
 }
 
 hash_256 :: proc {
@@ -131,18 +176,20 @@ hash_256 :: proc {
     hash_file_256,
     hash_bytes_256,
     hash_string_256,
+    hash_bytes_to_buffer_256,
+    hash_string_to_buffer_256,
 }
 
 // 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(data: string) -> [DIGEST_SIZE_384]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 {
-    hash: [48]byte
+hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
+    hash: [DIGEST_SIZE_384]byte
     ctx: Groestl_Context
     ctx.hashbitlen = 384
     init(&ctx)
@@ -151,10 +198,29 @@ hash_bytes_384 :: proc(data: []byte) -> [48]byte {
     return hash
 }
 
+// hash_string_to_buffer_384 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_384(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_384 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_384, "Size of destination buffer is smaller than the digest size")
+    ctx: Groestl_Context
+    ctx.hashbitlen = 384
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, 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) {
-    hash: [48]byte
+hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
+    hash: [DIGEST_SIZE_384]byte
     ctx: Groestl_Context
     ctx.hashbitlen = 384
     init(&ctx)
@@ -173,7 +239,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
 
 // 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) {
+hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_384]byte, bool) {
     if !load_at_once {
         return hash_stream_384(os.stream_from_handle(hd))
     } else {
@@ -181,7 +247,7 @@ hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool)
             return hash_bytes_384(buf[:]), ok
         }
     }
-    return [48]byte{}, false
+    return [DIGEST_SIZE_384]byte{}, false
 }
 
 hash_384 :: proc {
@@ -189,18 +255,20 @@ hash_384 :: proc {
     hash_file_384,
     hash_bytes_384,
     hash_string_384,
+    hash_bytes_to_buffer_384,
+    hash_string_to_buffer_384,
 }
 
 // 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(data: string) -> [DIGEST_SIZE_512]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 {
-    hash: [64]byte
+hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
+    hash: [DIGEST_SIZE_512]byte
     ctx: Groestl_Context
     ctx.hashbitlen = 512
     init(&ctx)
@@ -209,10 +277,29 @@ hash_bytes_512 :: proc(data: []byte) -> [64]byte {
     return hash
 }
 
+// hash_string_to_buffer_512 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_512(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_512 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
+    ctx: Groestl_Context
+    ctx.hashbitlen = 512
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, 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) {
-    hash: [64]byte
+hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
+    hash: [DIGEST_SIZE_512]byte
     ctx: Groestl_Context
     ctx.hashbitlen = 512
     init(&ctx)
@@ -231,7 +318,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
 
 // 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) {
+hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512]byte, bool) {
     if !load_at_once {
         return hash_stream_512(os.stream_from_handle(hd))
     } else {
@@ -239,7 +326,7 @@ hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool)
             return hash_bytes_512(buf[:]), ok
         }
     }
-    return [64]byte{}, false
+    return [DIGEST_SIZE_512]byte{}, false
 }
 
 hash_512 :: proc {
@@ -247,6 +334,8 @@ hash_512 :: proc {
     hash_file_512,
     hash_bytes_512,
     hash_string_512,
+    hash_bytes_to_buffer_512,
+    hash_string_to_buffer_512,
 }
 
 /*

Diff do ficheiro suprimidas por serem muito extensas
+ 400 - 95
core/crypto/haval/haval.odin


+ 117 - 28
core/crypto/jh/jh.odin

@@ -17,16 +17,21 @@ import "core:io"
     High level API
 */
 
+DIGEST_SIZE_224 :: 28
+DIGEST_SIZE_256 :: 32
+DIGEST_SIZE_384 :: 48
+DIGEST_SIZE_512 :: 64
+
 // 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(data: string) -> [DIGEST_SIZE_224]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 {
-    hash: [28]byte
+hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
+    hash: [DIGEST_SIZE_224]byte
     ctx: Jh_Context
     ctx.hashbitlen = 224
     init(&ctx)
@@ -35,10 +40,29 @@ hash_bytes_224 :: proc(data: []byte) -> [28]byte {
     return hash
 }
 
+// hash_string_to_buffer_224 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_224(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_224 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
+    ctx: Jh_Context
+    ctx.hashbitlen = 224
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, 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) {
-    hash: [28]byte
+hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
+    hash: [DIGEST_SIZE_224]byte
     ctx: Jh_Context
     ctx.hashbitlen = 224
     init(&ctx)
@@ -57,7 +81,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
 
 // 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) {
+hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_224]byte, bool) {
     if !load_at_once {
         return hash_stream_224(os.stream_from_handle(hd))
     } else {
@@ -65,7 +89,7 @@ hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool)
             return hash_bytes_224(buf[:]), ok
         }
     }
-    return [28]byte{}, false
+    return [DIGEST_SIZE_224]byte{}, false
 }
 
 hash_224 :: proc {
@@ -73,18 +97,20 @@ hash_224 :: proc {
     hash_file_224,
     hash_bytes_224,
     hash_string_224,
+    hash_bytes_to_buffer_224,
+    hash_string_to_buffer_224,
 }
 
 // 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(data: string) -> [DIGEST_SIZE_256]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 {
-    hash: [32]byte
+hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
+    hash: [DIGEST_SIZE_256]byte
     ctx: Jh_Context
     ctx.hashbitlen = 256
     init(&ctx)
@@ -93,10 +119,29 @@ hash_bytes_256 :: proc(data: []byte) -> [32]byte {
     return hash
 }
 
+// hash_string_to_buffer_256 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_256(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_256 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
+    ctx: Jh_Context
+    ctx.hashbitlen = 256
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, 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) {
-    hash: [32]byte
+hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
+    hash: [DIGEST_SIZE_256]byte
     ctx: Jh_Context
     ctx.hashbitlen = 256
     init(&ctx)
@@ -115,7 +160,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
 
 // 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) {
+hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
     if !load_at_once {
         return hash_stream_256(os.stream_from_handle(hd))
     } else {
@@ -123,7 +168,7 @@ hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool)
             return hash_bytes_256(buf[:]), ok
         }
     }
-    return [32]byte{}, false
+    return [DIGEST_SIZE_256]byte{}, false
 }
 
 hash_256 :: proc {
@@ -131,18 +176,20 @@ hash_256 :: proc {
     hash_file_256,
     hash_bytes_256,
     hash_string_256,
+    hash_bytes_to_buffer_256,
+    hash_string_to_buffer_256,
 }
 
 // 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(data: string) -> [DIGEST_SIZE_384]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 {
-    hash: [48]byte
+hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
+    hash: [DIGEST_SIZE_384]byte
     ctx: Jh_Context
     ctx.hashbitlen = 384
     init(&ctx)
@@ -151,10 +198,29 @@ hash_bytes_384 :: proc(data: []byte) -> [48]byte {
     return hash
 }
 
+// hash_string_to_buffer_384 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_384(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_384 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_384, "Size of destination buffer is smaller than the digest size")
+    ctx: Jh_Context
+    ctx.hashbitlen = 384
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, 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) {
-    hash: [48]byte
+hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
+    hash: [DIGEST_SIZE_384]byte
     ctx: Jh_Context
     ctx.hashbitlen = 384
     init(&ctx)
@@ -173,7 +239,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
 
 // 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) {
+hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_384]byte, bool) {
     if !load_at_once {
         return hash_stream_384(os.stream_from_handle(hd))
     } else {
@@ -181,7 +247,7 @@ hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool)
             return hash_bytes_384(buf[:]), ok
         }
     }
-    return [48]byte{}, false
+    return [DIGEST_SIZE_384]byte{}, false
 }
 
 hash_384 :: proc {
@@ -189,18 +255,20 @@ hash_384 :: proc {
     hash_file_384,
     hash_bytes_384,
     hash_string_384,
+    hash_bytes_to_buffer_384,
+    hash_string_to_buffer_384,
 }
 
 // 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(data: string) -> [DIGEST_SIZE_512]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 {
-    hash: [64]byte
+hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
+    hash: [DIGEST_SIZE_512]byte
     ctx: Jh_Context
     ctx.hashbitlen = 512
     init(&ctx)
@@ -209,10 +277,29 @@ hash_bytes_512 :: proc(data: []byte) -> [64]byte {
     return hash
 }
 
+// hash_string_to_buffer_512 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_512(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_512 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
+    ctx: Jh_Context
+    ctx.hashbitlen = 512
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, 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) {
-    hash: [64]byte
+hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
+    hash: [DIGEST_SIZE_512]byte
     ctx: Jh_Context
     ctx.hashbitlen = 512
     init(&ctx)
@@ -231,7 +318,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
 
 // 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) {
+hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512]byte, bool) {
     if !load_at_once {
         return hash_stream_512(os.stream_from_handle(hd))
     } else {
@@ -239,7 +326,7 @@ hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool)
             return hash_bytes_512(buf[:]), ok
         }
     }
-    return [64]byte{}, false
+    return [DIGEST_SIZE_512]byte{}, false
 }
 
 hash_512 :: proc {
@@ -247,6 +334,8 @@ hash_512 :: proc {
     hash_file_512,
     hash_bytes_512,
     hash_string_512,
+    hash_bytes_to_buffer_512,
+    hash_string_to_buffer_512,
 }
 
 /*

+ 130 - 37
core/crypto/keccak/keccak.odin

@@ -21,18 +21,23 @@ import "../_sha3"
     High level API
 */
 
+DIGEST_SIZE_224 :: 28
+DIGEST_SIZE_256 :: 32
+DIGEST_SIZE_384 :: 48
+DIGEST_SIZE_512 :: 64
+
 // 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(data: string) -> [DIGEST_SIZE_224]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 {
-    hash: [28]byte
+hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
+    hash: [DIGEST_SIZE_224]byte
     ctx: _sha3.Sha3_Context
-    ctx.mdlen = 28
+    ctx.mdlen = DIGEST_SIZE_224
     ctx.is_keccak = true
     _sha3.init(&ctx)
     _sha3.update(&ctx, data)
@@ -40,12 +45,32 @@ hash_bytes_224 :: proc(data: []byte) -> [28]byte {
     return hash
 }
 
+// hash_string_to_buffer_224 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_224(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_224 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
+    ctx: _sha3.Sha3_Context
+    ctx.mdlen = DIGEST_SIZE_224
+    ctx.is_keccak = true
+    _sha3.init(&ctx)
+    _sha3.update(&ctx, data)
+    _sha3.final(&ctx, 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) {
-    hash: [28]byte
+hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
+    hash: [DIGEST_SIZE_224]byte
     ctx: _sha3.Sha3_Context
-    ctx.mdlen = 28
+    ctx.mdlen = DIGEST_SIZE_224
     ctx.is_keccak = true
     _sha3.init(&ctx)
     buf := make([]byte, 512)
@@ -63,7 +88,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
 
 // 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) {
+hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_224]byte, bool) {
     if !load_at_once {
         return hash_stream_224(os.stream_from_handle(hd))
     } else {
@@ -71,7 +96,7 @@ hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool)
             return hash_bytes_224(buf[:]), ok
         }
     }
-    return [28]byte{}, false
+    return [DIGEST_SIZE_224]byte{}, false
 }
 
 hash_224 :: proc {
@@ -79,20 +104,22 @@ hash_224 :: proc {
     hash_file_224,
     hash_bytes_224,
     hash_string_224,
+    hash_bytes_to_buffer_224,
+    hash_string_to_buffer_224,
 }
 
 // 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(data: string) -> [DIGEST_SIZE_256]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 {
-    hash: [32]byte
+hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
+    hash: [DIGEST_SIZE_256]byte
     ctx: _sha3.Sha3_Context
-    ctx.mdlen = 32
+    ctx.mdlen = DIGEST_SIZE_256
     ctx.is_keccak = true
     _sha3.init(&ctx)
     _sha3.update(&ctx, data)
@@ -100,12 +127,32 @@ hash_bytes_256 :: proc(data: []byte) -> [32]byte {
     return hash
 }
 
+// hash_string_to_buffer_256 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_256(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_256 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
+    ctx: _sha3.Sha3_Context
+    ctx.mdlen = DIGEST_SIZE_256
+    ctx.is_keccak = true
+    _sha3.init(&ctx)
+    _sha3.update(&ctx, data)
+    _sha3.final(&ctx, 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) {
-    hash: [32]byte
+hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
+    hash: [DIGEST_SIZE_256]byte
     ctx: _sha3.Sha3_Context
-    ctx.mdlen = 32
+    ctx.mdlen = DIGEST_SIZE_256
     ctx.is_keccak = true
     _sha3.init(&ctx)
     buf := make([]byte, 512)
@@ -123,7 +170,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
 
 // 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) {
+hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
     if !load_at_once {
         return hash_stream_256(os.stream_from_handle(hd))
     } else {
@@ -131,7 +178,7 @@ hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool)
             return hash_bytes_256(buf[:]), ok
         }
     }
-    return [32]byte{}, false
+    return [DIGEST_SIZE_256]byte{}, false
 }
 
 hash_256 :: proc {
@@ -139,20 +186,22 @@ hash_256 :: proc {
     hash_file_256,
     hash_bytes_256,
     hash_string_256,
+    hash_bytes_to_buffer_256,
+    hash_string_to_buffer_256,
 }
 
 // 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(data: string) -> [DIGEST_SIZE_384]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 {
-    hash: [48]byte
+hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
+    hash: [DIGEST_SIZE_384]byte
     ctx: _sha3.Sha3_Context
-    ctx.mdlen = 48
+    ctx.mdlen = DIGEST_SIZE_384
     ctx.is_keccak = true
     _sha3.init(&ctx)
     _sha3.update(&ctx, data)
@@ -160,12 +209,32 @@ hash_bytes_384 :: proc(data: []byte) -> [48]byte {
     return hash
 }
 
+// hash_string_to_buffer_384 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_384(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_384 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_384, "Size of destination buffer is smaller than the digest size")
+    ctx: _sha3.Sha3_Context
+    ctx.mdlen = DIGEST_SIZE_384
+    ctx.is_keccak = true
+    _sha3.init(&ctx)
+    _sha3.update(&ctx, data)
+    _sha3.final(&ctx, 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) {
-    hash: [48]byte
+hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
+    hash: [DIGEST_SIZE_384]byte
     ctx: _sha3.Sha3_Context
-    ctx.mdlen = 48
+    ctx.mdlen = DIGEST_SIZE_384
     ctx.is_keccak = true
     _sha3.init(&ctx)
     buf := make([]byte, 512)
@@ -183,7 +252,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
 
 // 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) {
+hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_384]byte, bool) {
     if !load_at_once {
         return hash_stream_384(os.stream_from_handle(hd))
     } else {
@@ -191,7 +260,7 @@ hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool)
             return hash_bytes_384(buf[:]), ok
         }
     }
-    return [48]byte{}, false
+    return [DIGEST_SIZE_384]byte{}, false
 }
 
 hash_384 :: proc {
@@ -199,20 +268,22 @@ hash_384 :: proc {
     hash_file_384,
     hash_bytes_384,
     hash_string_384,
+    hash_bytes_to_buffer_384,
+    hash_string_to_buffer_384,
 }
 
 // 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(data: string) -> [DIGEST_SIZE_512]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 {
-    hash: [64]byte
+hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
+    hash: [DIGEST_SIZE_512]byte
     ctx: _sha3.Sha3_Context
-    ctx.mdlen = 64
+    ctx.mdlen = DIGEST_SIZE_512
     ctx.is_keccak = true
     _sha3.init(&ctx)
     _sha3.update(&ctx, data)
@@ -220,12 +291,32 @@ hash_bytes_512 :: proc(data: []byte) -> [64]byte {
     return hash
 }
 
+// hash_string_to_buffer_512 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_512(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_512 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
+    ctx: _sha3.Sha3_Context
+    ctx.mdlen = DIGEST_SIZE_512
+    ctx.is_keccak = true
+    _sha3.init(&ctx)
+    _sha3.update(&ctx, data)
+    _sha3.final(&ctx, 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) {
-    hash: [64]byte
+hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
+    hash: [DIGEST_SIZE_512]byte
     ctx: _sha3.Sha3_Context
-    ctx.mdlen = 64
+    ctx.mdlen = DIGEST_SIZE_512
     ctx.is_keccak = true
     _sha3.init(&ctx)
     buf := make([]byte, 512)
@@ -243,7 +334,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
 
 // 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) {
+hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512]byte, bool) {
     if !load_at_once {
         return hash_stream_512(os.stream_from_handle(hd))
     } else {
@@ -251,7 +342,7 @@ hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool)
             return hash_bytes_512(buf[:]), ok
         }
     }
-    return [64]byte{}, false
+    return [DIGEST_SIZE_512]byte{}, false
 }
 
 hash_512 :: proc {
@@ -259,13 +350,15 @@ hash_512 :: proc {
     hash_file_512,
     hash_bytes_512,
     hash_string_512,
+    hash_bytes_to_buffer_512,
+    hash_string_to_buffer_512,
 }
 
 /*
     Low level API
 */
 
-Sha3_Context :: _sha3.Sha3_Context
+Keccak_Context :: _sha3.Sha3_Context
 
 init :: proc(ctx: ^_sha3.Sha3_Context) {
     ctx.is_keccak = true

+ 43 - 21
core/crypto/md2/md2.odin

@@ -17,16 +17,18 @@ import "core:io"
     High level API
 */
 
+DIGEST_SIZE :: 16
+
 // hash_string will hash the given input and return the
 // computed hash
-hash_string :: proc(data: string) -> [16]byte {
+hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
     return hash_bytes(transmute([]byte)(data))
 }
 
 // hash_bytes will hash the given input and return the
 // computed hash
-hash_bytes :: proc(data: []byte) -> [16]byte {
-	hash: [16]byte
+hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
+	hash: [DIGEST_SIZE]byte
 	ctx: Md2_Context
     // init(&ctx) No-op
     update(&ctx, data)
@@ -34,10 +36,28 @@ hash_bytes :: proc(data: []byte) -> [16]byte {
     return hash
 }
 
+// hash_string_to_buffer will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer :: proc(data: string, hash: []byte) {
+	hash_bytes_to_buffer(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer :: proc(data, hash: []byte) {
+	assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
+    ctx: Md2_Context
+    // init(&ctx) No-op
+    update(&ctx, data)
+    final(&ctx, 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) {
-	hash: [16]byte
+hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
+	hash: [DIGEST_SIZE]byte
 	ctx: Md2_Context
 	// init(&ctx) No-op
 	buf := make([]byte, 512)
@@ -55,7 +75,7 @@ hash_stream :: proc(s: io.Stream) -> ([16]byte, bool) {
 
 // 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) {
+hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
 	if !load_at_once {
         return hash_stream(os.stream_from_handle(hd))
     } else {
@@ -63,7 +83,7 @@ hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
             return hash_bytes(buf[:]), ok
         }
     }
-    return [16]byte{}, false
+    return [DIGEST_SIZE]byte{}, false
 }
 
 hash :: proc {
@@ -71,6 +91,8 @@ hash :: proc {
     hash_file,
     hash_bytes,
     hash_string,
+    hash_bytes_to_buffer,
+    hash_string_to_buffer,
 }
 
 /*
@@ -86,7 +108,7 @@ 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) {
+		if (ctx.datalen == DIGEST_SIZE) {
 			transform(ctx, ctx.data[:])
 			ctx.datalen = 0
 		}
@@ -94,14 +116,14 @@ update :: proc(ctx: ^Md2_Context, data: []byte) {
 }
 
 final :: proc(ctx: ^Md2_Context, hash: []byte) {
-	to_pad := byte(16 - ctx.datalen)
-    for ctx.datalen < 16 {
+	to_pad := byte(DIGEST_SIZE - ctx.datalen)
+    for ctx.datalen < DIGEST_SIZE {
         ctx.data[ctx.datalen] = to_pad
 		ctx.datalen += 1
     }
 	transform(ctx, ctx.data[:])
 	transform(ctx, ctx.checksum[:])
-    for i := 0; i < 16; i += 1 {
+    for i := 0; i < DIGEST_SIZE; i += 1 {
         hash[i] = ctx.state[i]
     }
 }
@@ -111,9 +133,9 @@ final :: proc(ctx: ^Md2_Context, hash: []byte) {
 */
 
 Md2_Context :: struct {
-    data:     [16]byte,
-    state:    [16 * 3]byte,
-    checksum: [16]byte,
+    data:     [DIGEST_SIZE]byte,
+    state:    [DIGEST_SIZE * 3]byte,
+    checksum: [DIGEST_SIZE]byte,
     datalen:  int,
 }
 
@@ -140,20 +162,20 @@ PI_TABLE := [?]byte {
 
 transform :: proc(ctx: ^Md2_Context, data: []byte) {
     j,k,t: byte
-	for j = 0; j < 16; j += 1 {
-		ctx.state[j + 16] = data[j]
-		ctx.state[j + 16 * 2] = (ctx.state[j + 16] ~ ctx.state[j])
+	for j = 0; j < DIGEST_SIZE; j += 1 {
+		ctx.state[j + DIGEST_SIZE] = data[j]
+		ctx.state[j + DIGEST_SIZE * 2] = (ctx.state[j + DIGEST_SIZE] ~ ctx.state[j])
 	}
 	t = 0
-	for j = 0; j < 16 + 2; j += 1 {
-		for k = 0; k < 16 * 3; k += 1 {
+	for j = 0; j < DIGEST_SIZE + 2; j += 1 {
+		for k = 0; k < DIGEST_SIZE * 3; k += 1 {
 			ctx.state[k] ~= PI_TABLE[t]
 			t = ctx.state[k]
 		}
 		t = (t + j) & 0xff
 	}
-	t = ctx.checksum[16 - 1]
-	for j = 0; j < 16; j += 1 {
+	t = ctx.checksum[DIGEST_SIZE - 1]
+	for j = 0; j < DIGEST_SIZE; j += 1 {
 		ctx.checksum[j] ~= PI_TABLE[data[j] ~ t]
 		t = ctx.checksum[j]
 	}

+ 31 - 9
core/crypto/md4/md4.odin

@@ -21,16 +21,18 @@ import "../util"
     High level API
 */
 
+DIGEST_SIZE :: 16
+
 // hash_string will hash the given input and return the
 // computed hash
-hash_string :: proc(data: string) -> [16]byte {
+hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
     return hash_bytes(transmute([]byte)(data))
 }
 
 // hash_bytes will hash the given input and return the
 // computed hash
-hash_bytes :: proc(data: []byte) -> [16]byte {
-    hash: [16]byte
+hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
+    hash: [DIGEST_SIZE]byte
     ctx: Md4_Context
     init(&ctx)
     update(&ctx, data)
@@ -38,10 +40,28 @@ hash_bytes :: proc(data: []byte) -> [16]byte {
     return hash
 }
 
+// hash_string_to_buffer will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
+    ctx: Md4_Context
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, 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) {
-    hash: [16]byte
+hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
+    hash: [DIGEST_SIZE]byte
     ctx: Md4_Context
     init(&ctx)
     buf := make([]byte, 512)
@@ -59,7 +79,7 @@ hash_stream :: proc(s: io.Stream) -> ([16]byte, bool) {
 
 // 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) {
+hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
     if !load_at_once {
         return hash_stream(os.stream_from_handle(hd))
     } else {
@@ -67,7 +87,7 @@ hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
             return hash_bytes(buf[:]), ok
         }
     }
-    return [16]byte{}, false
+    return [DIGEST_SIZE]byte{}, false
 }
 
 hash :: proc {
@@ -75,6 +95,8 @@ hash :: proc {
     hash_file,
     hash_bytes,
     hash_string,
+    hash_bytes_to_buffer,
+    hash_string_to_buffer,
 }
 
 /*
@@ -171,9 +193,9 @@ HH :: #force_inline proc "contextless"(a, b, c, d, x: u32, s : int) -> u32 {
 
 transform :: proc(ctx: ^Md4_Context, data: []byte) {
     a, b, c, d, i, j: u32
-    m: [16]u32
+    m: [DIGEST_SIZE]u32
 
-    for i, j = 0, 0; i < 16; i += 1 {
+    for i, j = 0, 0; i < DIGEST_SIZE; i += 1 {
         m[i] = u32(data[j]) | (u32(data[j + 1]) << 8) | (u32(data[j + 2]) << 16) | (u32(data[j + 3]) << 24)
         j += 4
     }

+ 31 - 9
core/crypto/md5/md5.odin

@@ -20,16 +20,18 @@ import "../util"
     High level API
 */
 
+DIGEST_SIZE :: 16
+
 // hash_string will hash the given input and return the
 // computed hash
-hash_string :: proc(data: string) -> [16]byte {
+hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
     return hash_bytes(transmute([]byte)(data))
 }
 
 // hash_bytes will hash the given input and return the
 // computed hash
-hash_bytes :: proc(data: []byte) -> [16]byte {
-    hash: [16]byte
+hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
+    hash: [DIGEST_SIZE]byte
     ctx: Md5_Context
     init(&ctx)
     update(&ctx, data)
@@ -37,10 +39,28 @@ hash_bytes :: proc(data: []byte) -> [16]byte {
     return hash
 }
 
+// hash_string_to_buffer will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
+    ctx: Md5_Context
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, 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) {
-    hash: [16]byte
+hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
+    hash: [DIGEST_SIZE]byte
     ctx: Md5_Context
     init(&ctx)
     buf := make([]byte, 512)
@@ -58,7 +78,7 @@ hash_stream :: proc(s: io.Stream) -> ([16]byte, bool) {
 
 // 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) {
+hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
     if !load_at_once {
         return hash_stream(os.stream_from_handle(hd))
     } else {
@@ -66,7 +86,7 @@ hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
             return hash_bytes(buf[:]), ok
         }
     }
-    return [16]byte{}, false
+    return [DIGEST_SIZE]byte{}, false
 }
 
 hash :: proc {
@@ -74,6 +94,8 @@ hash :: proc {
     hash_file,
     hash_bytes,
     hash_string,
+    hash_bytes_to_buffer,
+    hash_string_to_buffer,
 }
 
 /*
@@ -176,9 +198,9 @@ II :: #force_inline proc "contextless" (a, b, c, d, m: u32, s: int, t: u32) -> u
 
 transform :: proc(ctx: ^Md5_Context, data: []byte) {
     i, j: u32
-    m: [16]u32
+    m: [DIGEST_SIZE]u32
 
-    for i, j = 0, 0; i < 16; i+=1 {
+    for i, j = 0, 0; i < DIGEST_SIZE; i+=1 {
         m[i] = u32(data[j]) + u32(data[j + 1]) << 8 + u32(data[j + 2]) << 16 + u32(data[j + 3]) << 24
         j += 4
     }

+ 163 - 0
core/crypto/poly1305/poly1305.odin

@@ -0,0 +1,163 @@
+package poly1305
+
+import "core:crypto"
+import "core:crypto/util"
+import field "core:crypto/_fiat/field_poly1305"
+import "core:mem"
+
+KEY_SIZE :: 32
+TAG_SIZE :: 16
+
+_BLOCK_SIZE :: 16
+
+sum :: proc (dst, msg, key: []byte) {
+	ctx: Context = ---
+
+	init(&ctx, key)
+	update(&ctx, msg)
+	final(&ctx, dst)
+}
+
+verify :: proc (tag, msg, key: []byte) -> bool {
+	ctx: Context = ---
+	derived_tag: [16]byte = ---
+
+	if len(tag) != TAG_SIZE {
+		panic("crypto/poly1305: invalid tag size")
+	}
+
+	init(&ctx, key)
+	update(&ctx, msg)
+	final(&ctx, derived_tag[:])
+
+	return crypto.compare_constant_time(derived_tag[:], tag) == 1
+}
+
+Context :: struct {
+	_r: field.Tight_Field_Element,
+	_a: field.Tight_Field_Element,
+	_s: field.Tight_Field_Element,
+
+	_buffer: [_BLOCK_SIZE]byte,
+	_leftover: int,
+
+	_is_initialized: bool,
+}
+
+init :: proc (ctx: ^Context, key: []byte) {
+	if len(key) != KEY_SIZE {
+		panic("crypto/poly1305: invalid key size")
+	}
+
+	// r = le_bytes_to_num(key[0..15])
+	// r = clamp(r) (r &= 0xffffffc0ffffffc0ffffffc0fffffff)
+	tmp_lo := util.U64_LE(key[0:8]) & 0x0ffffffc0fffffff
+	tmp_hi := util.U64_LE(key[8:16]) & 0xffffffc0ffffffc
+	field.fe_from_u64s(&ctx._r, tmp_lo, tmp_hi)
+
+	// s = le_bytes_to_num(key[16..31])
+	field.fe_from_bytes(&ctx._s, key[16:32], 0)
+
+	// a = 0
+	field.fe_zero(&ctx._a)
+
+	// No leftover in buffer
+	ctx._leftover = 0
+
+	ctx._is_initialized = true
+}
+
+update :: proc (ctx: ^Context, data: []byte) {
+	assert(ctx._is_initialized)
+
+	msg := data
+	msg_len := len(data)
+
+	// Handle leftover
+	if ctx._leftover > 0 {
+		want := min(_BLOCK_SIZE - ctx._leftover, msg_len)
+		copy_slice(ctx._buffer[ctx._leftover:], msg[:want])
+		msg_len = msg_len - want
+		msg = msg[want:]
+		ctx._leftover = ctx._leftover + want
+		if ctx._leftover < _BLOCK_SIZE {
+			return
+		}
+		_blocks(ctx, ctx._buffer[:])
+		ctx._leftover = 0
+	}
+
+	// Process full blocks
+	if msg_len >= _BLOCK_SIZE {
+		want := msg_len & (~int(_BLOCK_SIZE - 1))
+		_blocks(ctx, msg[:want])
+		msg = msg[want:]
+		msg_len = msg_len - want
+	}
+
+	// Store leftover
+	if msg_len > 0 {
+		// TODO: While -donna does it this way, I'm fairly sure that
+		// `ctx._leftover == 0` is an invariant at this point.
+		copy(ctx._buffer[ctx._leftover:], msg)
+		ctx._leftover = ctx._leftover + msg_len
+	}
+}
+
+final :: proc (ctx: ^Context, dst: []byte) {
+	assert(ctx._is_initialized)
+
+	if len(dst) != TAG_SIZE {
+		panic("poly1305: invalid destination tag size")
+	}
+
+	// Process remaining block
+	if ctx._leftover > 0 {
+		ctx._buffer[ctx._leftover] = 1
+		for i := ctx._leftover + 1; i < _BLOCK_SIZE; i = i + 1 {
+			ctx._buffer[i] = 0
+		}
+		_blocks(ctx, ctx._buffer[:], true)
+	}
+
+	// a += s
+	field.fe_add(field.fe_relax_cast(&ctx._a), &ctx._a, &ctx._s) // _a unreduced
+	field.fe_carry(&ctx._a, field.fe_relax_cast(&ctx._a)) // _a reduced
+
+	// return num_to_16_le_bytes(a)
+	tmp: [32]byte = ---
+	field.fe_to_bytes(&tmp, &ctx._a)
+	copy_slice(dst, tmp[0:16])
+
+	reset(ctx)
+}
+
+reset :: proc (ctx: ^Context) {
+	mem.zero_explicit(&ctx._r, size_of(ctx._r))
+	mem.zero_explicit(&ctx._a, size_of(ctx._a))
+	mem.zero_explicit(&ctx._s, size_of(ctx._s))
+	mem.zero_explicit(&ctx._buffer, size_of(ctx._buffer))
+
+	ctx._is_initialized = false
+}
+
+_blocks :: proc (ctx: ^Context, msg: []byte, final := false) {
+	n: field.Tight_Field_Element = ---
+	final_byte := byte(!final)
+
+	data := msg
+	data_len := len(data)
+	for data_len >= _BLOCK_SIZE {
+		// n = le_bytes_to_num(msg[((i-1)*16)..*i*16] | [0x01])
+		field.fe_from_bytes(&n, data[:_BLOCK_SIZE], final_byte, false)
+
+		// a += n
+		field.fe_add(field.fe_relax_cast(&ctx._a), &ctx._a, &n) // _a unreduced
+
+		// a = (r * a) % p
+		field.fe_carry_mul(&ctx._a, field.fe_relax_cast(&ctx._a), field.fe_relax_cast(&ctx._r)) // _a reduced
+
+		data = data[_BLOCK_SIZE:]
+		data_len = data_len - _BLOCK_SIZE
+	}
+}

+ 7 - 0
core/crypto/rand_generic.odin

@@ -0,0 +1,7 @@
+package crypto
+
+when ODIN_OS != "linux" {
+	_rand_bytes :: proc (dst: []byte) {
+		unimplemented("crypto: rand_bytes not supported on this OS")
+	}
+}

+ 37 - 0
core/crypto/rand_linux.odin

@@ -0,0 +1,37 @@
+package crypto
+
+import "core:fmt"
+import "core:os"
+import "core:sys/unix"
+
+_MAX_PER_CALL_BYTES :: 33554431 // 2^25 - 1
+
+_rand_bytes :: proc (dst: []byte) {
+	dst := dst
+	l := len(dst)
+
+	for l > 0 {
+		to_read := min(l, _MAX_PER_CALL_BYTES)
+		ret := unix.sys_getrandom(raw_data(dst), to_read, 0)
+		if ret < 0 {
+			switch os.Errno(-ret) {
+			case os.EINTR:
+				// Call interupted by a signal handler, just retry the
+				// request.
+				continue
+			case os.ENOSYS:
+				// The kernel is apparently prehistoric (< 3.17 circa 2014)
+				// and does not support getrandom.
+				panic("crypto: getrandom not available in kernel")
+			case:
+				// All other failures are things that should NEVER happen
+				// unless the kernel interface changes (ie: the Linux
+				// developers break userland).
+				panic(fmt.tprintf("crypto: getrandom failed: %d", ret))
+			}
+		}
+
+		l -= ret
+		dst = dst[ret:]
+	}
+}

+ 113 - 28
core/crypto/ripemd/ripemd.odin

@@ -19,16 +19,21 @@ import "../util"
     High level API
 */
 
+DIGEST_SIZE_128 :: 16
+DIGEST_SIZE_160 :: 20
+DIGEST_SIZE_256 :: 32
+DIGEST_SIZE_320 :: 40
+
 // hash_string_128 will hash the given input and return the
 // computed hash
-hash_string_128 :: proc(data: string) -> [16]byte {
+hash_string_128 :: proc(data: string) -> [DIGEST_SIZE_128]byte {
     return hash_bytes_128(transmute([]byte)(data))
 }
 
 // hash_bytes_128 will hash the given input and return the
 // computed hash
-hash_bytes_128 :: proc(data: []byte) -> [16]byte {
-    hash: [16]byte
+hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte {
+    hash: [DIGEST_SIZE_128]byte
     ctx: Ripemd128_Context
     init(&ctx)
     update(&ctx, data)
@@ -36,10 +41,28 @@ hash_bytes_128 :: proc(data: []byte) -> [16]byte {
     return hash
 }
 
+// hash_string_to_buffer_128 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_128 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_128(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_128 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_128 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_128, "Size of destination buffer is smaller than the digest size")
+    ctx: Ripemd128_Context
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, 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) {
-    hash: [16]byte
+hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
+    hash: [DIGEST_SIZE_128]byte
     ctx: Ripemd128_Context
     init(&ctx)
     buf := make([]byte, 512)
@@ -57,7 +80,7 @@ hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
 
 // 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) {
+hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_128]byte, bool) {
     if !load_at_once {
         return hash_stream_128(os.stream_from_handle(hd))
     } else {
@@ -65,7 +88,7 @@ hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool)
             return hash_bytes_128(buf[:]), ok
         }
     }
-    return [16]byte{}, false
+    return [DIGEST_SIZE_128]byte{}, false
 }
 
 hash_128 :: proc {
@@ -73,18 +96,20 @@ hash_128 :: proc {
     hash_file_128,
     hash_bytes_128,
     hash_string_128,
+    hash_bytes_to_buffer_128,
+    hash_string_to_buffer_128,
 }
 
 // hash_string_160 will hash the given input and return the
 // computed hash
-hash_string_160 :: proc(data: string) -> [20]byte {
+hash_string_160 :: proc(data: string) -> [DIGEST_SIZE_160]byte {
     return hash_bytes_160(transmute([]byte)(data))
 }
 
 // hash_bytes_160 will hash the given input and return the
 // computed hash
-hash_bytes_160 :: proc(data: []byte) -> [20]byte {
-    hash: [20]byte
+hash_bytes_160 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte {
+    hash: [DIGEST_SIZE_160]byte
     ctx: Ripemd160_Context
     init(&ctx)
     update(&ctx, data)
@@ -92,10 +117,28 @@ hash_bytes_160 :: proc(data: []byte) -> [20]byte {
     return hash
 }
 
+// hash_string_to_buffer_160 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_160 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_160(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_160 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_160 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_160, "Size of destination buffer is smaller than the digest size")
+    ctx: Ripemd160_Context
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, 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) {
-    hash: [20]byte
+hash_stream_160 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) {
+    hash: [DIGEST_SIZE_160]byte
     ctx: Ripemd160_Context
     init(&ctx)
     buf := make([]byte, 512)
@@ -113,7 +156,7 @@ hash_stream_160 :: proc(s: io.Stream) -> ([20]byte, bool) {
 
 // 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) {
+hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_160]byte, bool) {
     if !load_at_once {
         return hash_stream_160(os.stream_from_handle(hd))
     } else {
@@ -121,7 +164,7 @@ hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool)
             return hash_bytes_160(buf[:]), ok
         }
     }
-    return [20]byte{}, false
+    return [DIGEST_SIZE_160]byte{}, false
 }
 
 hash_160 :: proc {
@@ -129,18 +172,20 @@ hash_160 :: proc {
     hash_file_160,
     hash_bytes_160,
     hash_string_160,
+    hash_bytes_to_buffer_160,
+    hash_string_to_buffer_160,
 }
 
 // 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(data: string) -> [DIGEST_SIZE_256]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 {
-    hash: [32]byte
+hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
+    hash: [DIGEST_SIZE_256]byte
     ctx: Ripemd256_Context
     init(&ctx)
     update(&ctx, data)
@@ -148,10 +193,28 @@ hash_bytes_256 :: proc(data: []byte) -> [32]byte {
     return hash
 }
 
+// hash_string_to_buffer_256 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_256(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_256 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
+    ctx: Ripemd256_Context
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, 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) {
-    hash: [32]byte
+hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
+    hash: [DIGEST_SIZE_256]byte
     ctx: Ripemd256_Context
     init(&ctx)
     buf := make([]byte, 512)
@@ -169,7 +232,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
 
 // 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) {
+hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
     if !load_at_once {
         return hash_stream_256(os.stream_from_handle(hd))
     } else {
@@ -177,7 +240,7 @@ hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool)
             return hash_bytes_256(buf[:]), ok
         }
     }
-    return [32]byte{}, false
+    return [DIGEST_SIZE_256]byte{}, false
 }
 
 hash_256 :: proc {
@@ -185,18 +248,20 @@ hash_256 :: proc {
     hash_file_256,
     hash_bytes_256,
     hash_string_256,
+    hash_bytes_to_buffer_256,
+    hash_string_to_buffer_256,
 }
 
 // hash_string_320 will hash the given input and return the
 // computed hash
-hash_string_320 :: proc(data: string) -> [40]byte {
+hash_string_320 :: proc(data: string) -> [DIGEST_SIZE_320]byte {
     return hash_bytes_320(transmute([]byte)(data))
 }
 
 // hash_bytes_320 will hash the given input and return the
 // computed hash
-hash_bytes_320 :: proc(data: []byte) -> [40]byte {
-    hash: [40]byte
+hash_bytes_320 :: proc(data: []byte) -> [DIGEST_SIZE_320]byte {
+    hash: [DIGEST_SIZE_320]byte
     ctx: Ripemd320_Context
     init(&ctx)
     update(&ctx, data)
@@ -204,10 +269,28 @@ hash_bytes_320 :: proc(data: []byte) -> [40]byte {
     return hash
 }
 
+// hash_string_to_buffer_320 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_320 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_320(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_320 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_320 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_320, "Size of destination buffer is smaller than the digest size")
+    ctx: Ripemd320_Context
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, 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) {
-    hash: [40]byte
+hash_stream_320 :: proc(s: io.Stream) -> ([DIGEST_SIZE_320]byte, bool) {
+    hash: [DIGEST_SIZE_320]byte
     ctx: Ripemd320_Context
     init(&ctx)
     buf := make([]byte, 512)
@@ -225,7 +308,7 @@ hash_stream_320 :: proc(s: io.Stream) -> ([40]byte, bool) {
 
 // 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) {
+hash_file_320 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_320]byte, bool) {
     if !load_at_once {
         return hash_stream_320(os.stream_from_handle(hd))
     } else {
@@ -233,7 +316,7 @@ hash_file_320 :: proc(hd: os.Handle, load_at_once := false) -> ([40]byte, bool)
             return hash_bytes_320(buf[:]), ok
         }
     }
-    return [40]byte{}, false
+    return [DIGEST_SIZE_320]byte{}, false
 }
 
 hash_320 :: proc {
@@ -241,6 +324,8 @@ hash_320 :: proc {
     hash_file_320,
     hash_bytes_320,
     hash_string_320,
+    hash_bytes_to_buffer_320,
+    hash_string_to_buffer_320,
 }
 
 /*

+ 30 - 7
core/crypto/sha1/sha1.odin

@@ -19,16 +19,19 @@ import "../util"
 /*
     High level API
 */
+
+DIGEST_SIZE :: 20
+
 // hash_string will hash the given input and return the
 // computed hash
-hash_string :: proc(data: string) -> [20]byte {
+hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
     return hash_bytes(transmute([]byte)(data))
 }
 
 // hash_bytes will hash the given input and return the
 // computed hash
-hash_bytes :: proc(data: []byte) -> [20]byte {
-    hash: [20]byte
+hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
+    hash: [DIGEST_SIZE]byte
     ctx: Sha1_Context
     init(&ctx)
     update(&ctx, data)
@@ -36,10 +39,28 @@ hash_bytes :: proc(data: []byte) -> [20]byte {
     return hash
 }
 
+// hash_string_to_buffer will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
+    ctx: Sha1_Context
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, 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) {
-    hash: [20]byte
+hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
+    hash: [DIGEST_SIZE]byte
     ctx: Sha1_Context
     init(&ctx)
     buf := make([]byte, 512)
@@ -57,7 +78,7 @@ hash_stream :: proc(s: io.Stream) -> ([20]byte, bool) {
 
 // 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) {
+hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
     if !load_at_once {
         return hash_stream(os.stream_from_handle(hd))
     } else {
@@ -65,7 +86,7 @@ hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
             return hash_bytes(buf[:]), ok
         }
     }
-    return [20]byte{}, false
+    return [DIGEST_SIZE]byte{}, false
 }
 
 hash :: proc {
@@ -73,6 +94,8 @@ hash :: proc {
     hash_file,
     hash_bytes,
     hash_string,
+    hash_bytes_to_buffer,
+    hash_string_to_buffer,
 }
 
 /*

+ 117 - 28
core/crypto/sha2/sha2.odin

@@ -21,16 +21,21 @@ import "../util"
     High level API
 */
 
+DIGEST_SIZE_224 :: 28
+DIGEST_SIZE_256 :: 32
+DIGEST_SIZE_384 :: 48
+DIGEST_SIZE_512 :: 64
+
 // 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(data: string) -> [DIGEST_SIZE_224]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 {
-    hash: [28]byte
+hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
+    hash: [DIGEST_SIZE_224]byte
 	ctx: Sha256_Context
     ctx.is224 = true
     init(&ctx)
@@ -39,10 +44,29 @@ hash_bytes_224 :: proc(data: []byte) -> [28]byte {
     return hash
 }
 
+// hash_string_to_buffer_224 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_224(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_224 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
+    ctx: Sha256_Context
+    ctx.is224 = true
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, 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) {
-	hash: [28]byte
+hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
+	hash: [DIGEST_SIZE_224]byte
     ctx: Sha512_Context
     ctx.is384 = false
     init(&ctx)
@@ -61,7 +85,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
 
 // 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) {
+hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_224]byte, bool) {
     if !load_at_once {
         return hash_stream_224(os.stream_from_handle(hd))
     } else {
@@ -69,7 +93,7 @@ hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool)
             return hash_bytes_224(buf[:]), ok
         }
     }
-    return [28]byte{}, false
+    return [DIGEST_SIZE_224]byte{}, false
 }
 
 hash_224 :: proc {
@@ -77,18 +101,20 @@ hash_224 :: proc {
     hash_file_224,
     hash_bytes_224,
     hash_string_224,
+    hash_bytes_to_buffer_224,
+    hash_string_to_buffer_224,
 }
 
 // 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(data: string) -> [DIGEST_SIZE_256]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 {
-    hash: [32]byte
+hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
+    hash: [DIGEST_SIZE_256]byte
 	ctx: Sha256_Context
     ctx.is224 = false
     init(&ctx)
@@ -97,10 +123,29 @@ hash_bytes_256 :: proc(data: []byte) -> [32]byte {
     return hash
 }
 
+// hash_string_to_buffer_256 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_256(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_256 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
+    ctx: Sha256_Context
+    ctx.is224 = false
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, 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) {
-	hash: [32]byte
+hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
+	hash: [DIGEST_SIZE_256]byte
     ctx: Sha512_Context
     ctx.is384 = false
     init(&ctx)
@@ -119,7 +164,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
 
 // 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) {
+hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
     if !load_at_once {
         return hash_stream_256(os.stream_from_handle(hd))
     } else {
@@ -127,7 +172,7 @@ hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool)
             return hash_bytes_256(buf[:]), ok
         }
     }
-    return [32]byte{}, false
+    return [DIGEST_SIZE_256]byte{}, false
 }
 
 hash_256 :: proc {
@@ -135,18 +180,20 @@ hash_256 :: proc {
     hash_file_256,
     hash_bytes_256,
     hash_string_256,
+    hash_bytes_to_buffer_256,
+    hash_string_to_buffer_256,
 }
 
 // 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(data: string) -> [DIGEST_SIZE_384]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 {
-    hash: [48]byte
+hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
+    hash: [DIGEST_SIZE_384]byte
 	ctx: Sha512_Context
     ctx.is384 = true
     init(&ctx)
@@ -155,10 +202,29 @@ hash_bytes_384 :: proc(data: []byte) -> [48]byte {
     return hash
 }
 
+// hash_string_to_buffer_384 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_384(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_384 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_384, "Size of destination buffer is smaller than the digest size")
+    ctx: Sha512_Context
+    ctx.is384 = true
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, 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) {
-	hash: [48]byte
+hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
+	hash: [DIGEST_SIZE_384]byte
     ctx: Sha512_Context
     ctx.is384 = true
     init(&ctx)
@@ -177,7 +243,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
 
 // 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) {
+hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_384]byte, bool) {
     if !load_at_once {
         return hash_stream_384(os.stream_from_handle(hd))
     } else {
@@ -185,7 +251,7 @@ hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool)
             return hash_bytes_384(buf[:]), ok
         }
     }
-    return [48]byte{}, false
+    return [DIGEST_SIZE_384]byte{}, false
 }
 
 hash_384 :: proc {
@@ -193,18 +259,20 @@ hash_384 :: proc {
     hash_file_384,
     hash_bytes_384,
     hash_string_384,
+    hash_bytes_to_buffer_384,
+    hash_string_to_buffer_384,
 }
 
 // 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(data: string) -> [DIGEST_SIZE_512]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 {
-    hash: [64]byte
+hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
+    hash: [DIGEST_SIZE_512]byte
 	ctx: Sha512_Context
     ctx.is384 = false
     init(&ctx)
@@ -213,10 +281,29 @@ hash_bytes_512 :: proc(data: []byte) -> [64]byte {
     return hash
 }
 
+// hash_string_to_buffer_512 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_512(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_512 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
+    ctx: Sha512_Context
+    ctx.is384 = false
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, 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) {
-    hash: [64]byte
+hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
+    hash: [DIGEST_SIZE_512]byte
     ctx: Sha512_Context
     ctx.is384 = false
 	init(&ctx)
@@ -235,7 +322,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
 
 // 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) {
+hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512]byte, bool) {
     if !load_at_once {
         return hash_stream_512(os.stream_from_handle(hd))
     } else {
@@ -243,7 +330,7 @@ hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool)
             return hash_bytes_512(buf[:]), ok
         }
     }
-    return [64]byte{}, false
+    return [DIGEST_SIZE_512]byte{}, false
 }
 
 hash_512 :: proc {
@@ -251,6 +338,8 @@ hash_512 :: proc {
     hash_file_512,
     hash_bytes_512,
     hash_string_512,
+    hash_bytes_to_buffer_512,
+    hash_string_to_buffer_512,
 }
 
 /*

+ 125 - 36
core/crypto/sha3/sha3.odin

@@ -20,30 +20,54 @@ import "../_sha3"
     High level API
 */
 
+DIGEST_SIZE_224 :: 28
+DIGEST_SIZE_256 :: 32
+DIGEST_SIZE_384 :: 48
+DIGEST_SIZE_512 :: 64
+
 // 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(data: string) -> [DIGEST_SIZE_224]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 {
-    hash: [28]byte
+hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
+    hash: [DIGEST_SIZE_224]byte
     ctx: _sha3.Sha3_Context
-    ctx.mdlen = 28
+    ctx.mdlen = DIGEST_SIZE_224
     _sha3.init(&ctx)
     _sha3.update(&ctx, data)
     _sha3.final(&ctx, hash[:])
     return hash
 }
 
+// hash_string_to_buffer_224 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_224(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_224 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
+    ctx: _sha3.Sha3_Context
+    ctx.mdlen = DIGEST_SIZE_224
+    _sha3.init(&ctx)
+    _sha3.update(&ctx, data)
+    _sha3.final(&ctx, 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) {
-    hash: [28]byte
+hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
+    hash: [DIGEST_SIZE_224]byte
     ctx: _sha3.Sha3_Context
-    ctx.mdlen = 28
+    ctx.mdlen = DIGEST_SIZE_224
     _sha3.init(&ctx)
     buf := make([]byte, 512)
     defer delete(buf)
@@ -60,7 +84,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
 
 // 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) {
+hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_224]byte, bool) {
     if !load_at_once {
         return hash_stream_224(os.stream_from_handle(hd))
     } else {
@@ -68,7 +92,7 @@ hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool)
             return hash_bytes_224(buf[:]), ok
         }
     }
-    return [28]byte{}, false
+    return [DIGEST_SIZE_224]byte{}, false
 }
 
 hash_224 :: proc {
@@ -76,32 +100,53 @@ hash_224 :: proc {
     hash_file_224,
     hash_bytes_224,
     hash_string_224,
+    hash_bytes_to_buffer_224,
+    hash_string_to_buffer_224,
 }
 
 // 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(data: string) -> [DIGEST_SIZE_256]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 {
-    hash: [32]byte
+hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
+    hash: [DIGEST_SIZE_256]byte
     ctx: _sha3.Sha3_Context
-    ctx.mdlen = 32
+    ctx.mdlen = DIGEST_SIZE_256
     _sha3.init(&ctx)
     _sha3.update(&ctx, data)
     _sha3.final(&ctx, hash[:])
     return hash
 }
 
+// hash_string_to_buffer_256 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_256(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_256 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
+    ctx: _sha3.Sha3_Context
+    ctx.mdlen = DIGEST_SIZE_256
+    _sha3.init(&ctx)
+    _sha3.update(&ctx, data)
+    _sha3.final(&ctx, 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) {
-    hash: [32]byte
+hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
+    hash: [DIGEST_SIZE_256]byte
     ctx: _sha3.Sha3_Context
-    ctx.mdlen = 32
+    ctx.mdlen = DIGEST_SIZE_256
     _sha3.init(&ctx)
     buf := make([]byte, 512)
     defer delete(buf)
@@ -118,7 +163,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
 
 // 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) {
+hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
     if !load_at_once {
         return hash_stream_256(os.stream_from_handle(hd))
     } else {
@@ -126,7 +171,7 @@ hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool)
             return hash_bytes_256(buf[:]), ok
         }
     }
-    return [32]byte{}, false
+    return [DIGEST_SIZE_256]byte{}, false
 }
 
 hash_256 :: proc {
@@ -134,32 +179,53 @@ hash_256 :: proc {
     hash_file_256,
     hash_bytes_256,
     hash_string_256,
+    hash_bytes_to_buffer_256,
+    hash_string_to_buffer_256,
 }
 
 // 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(data: string) -> [DIGEST_SIZE_384]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 {
-    hash: [48]byte
+hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
+    hash: [DIGEST_SIZE_384]byte
     ctx: _sha3.Sha3_Context
-    ctx.mdlen = 48
+    ctx.mdlen = DIGEST_SIZE_384
     _sha3.init(&ctx)
     _sha3.update(&ctx, data)
     _sha3.final(&ctx, hash[:])
     return hash
 }
 
+// hash_string_to_buffer_384 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_384(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_384 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_384, "Size of destination buffer is smaller than the digest size")
+    ctx: _sha3.Sha3_Context
+    ctx.mdlen = DIGEST_SIZE_384
+    _sha3.init(&ctx)
+    _sha3.update(&ctx, data)
+    _sha3.final(&ctx, 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) {
-    hash: [48]byte
+hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
+    hash: [DIGEST_SIZE_384]byte
     ctx: _sha3.Sha3_Context
-    ctx.mdlen = 48
+    ctx.mdlen = DIGEST_SIZE_384
     _sha3.init(&ctx)
     buf := make([]byte, 512)
     defer delete(buf)
@@ -176,7 +242,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
 
 // 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) {
+hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_384]byte, bool) {
     if !load_at_once {
         return hash_stream_384(os.stream_from_handle(hd))
     } else {
@@ -184,7 +250,7 @@ hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool)
             return hash_bytes_384(buf[:]), ok
         }
     }
-    return [48]byte{}, false
+    return [DIGEST_SIZE_384]byte{}, false
 }
 
 hash_384 :: proc {
@@ -192,32 +258,53 @@ hash_384 :: proc {
     hash_file_384,
     hash_bytes_384,
     hash_string_384,
+    hash_bytes_to_buffer_384,
+    hash_string_to_buffer_384,
 }
 
 // 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(data: string) -> [DIGEST_SIZE_512]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 {
-    hash: [64]byte
+hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
+    hash: [DIGEST_SIZE_512]byte
     ctx: _sha3.Sha3_Context
-    ctx.mdlen = 64
+    ctx.mdlen = DIGEST_SIZE_512
     _sha3.init(&ctx)
     _sha3.update(&ctx, data)
     _sha3.final(&ctx, hash[:])
     return hash
 }
 
+// hash_string_to_buffer_512 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_512(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_512 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
+    ctx: _sha3.Sha3_Context
+    ctx.mdlen = DIGEST_SIZE_512
+    _sha3.init(&ctx)
+    _sha3.update(&ctx, data)
+    _sha3.final(&ctx, 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) {
-    hash: [64]byte
+hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
+    hash: [DIGEST_SIZE_512]byte
     ctx: _sha3.Sha3_Context
-    ctx.mdlen = 64
+    ctx.mdlen = DIGEST_SIZE_512
     _sha3.init(&ctx)
     buf := make([]byte, 512)
     defer delete(buf)
@@ -234,7 +321,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
 
 // 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) {
+hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512]byte, bool) {
     if !load_at_once {
         return hash_stream_512(os.stream_from_handle(hd))
     } else {
@@ -242,7 +329,7 @@ hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool)
             return hash_bytes_512(buf[:]), ok
         }
     }
-    return [64]byte{}, false
+    return [DIGEST_SIZE_512]byte{}, false
 }
 
 hash_512 :: proc {
@@ -250,6 +337,8 @@ hash_512 :: proc {
     hash_file_512,
     hash_bytes_512,
     hash_string_512,
+    hash_bytes_to_buffer_512,
+    hash_string_to_buffer_512,
 }
 
 /*

+ 66 - 19
core/crypto/shake/shake.odin

@@ -20,18 +20,21 @@ import "../_sha3"
     High level API
 */
 
+DIGEST_SIZE_128 :: 16
+DIGEST_SIZE_256 :: 32
+
 // hash_string_128 will hash the given input and return the
 // computed hash
-hash_string_128 :: proc(data: string) -> [16]byte {
+hash_string_128 :: proc(data: string) -> [DIGEST_SIZE_128]byte {
     return hash_bytes_128(transmute([]byte)(data))
 }
 
 // hash_bytes_128 will hash the given input and return the
 // computed hash
-hash_bytes_128 :: proc(data: []byte) -> [16]byte {
-    hash: [16]byte
+hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte {
+    hash: [DIGEST_SIZE_128]byte
     ctx: _sha3.Sha3_Context
-    ctx.mdlen = 16
+    ctx.mdlen = DIGEST_SIZE_128
     _sha3.init(&ctx)
     _sha3.update(&ctx, data)
     _sha3.shake_xof(&ctx)
@@ -39,12 +42,32 @@ hash_bytes_128 :: proc(data: []byte) -> [16]byte {
     return hash
 }
 
+// hash_string_to_buffer_128 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_128 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_128(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_128 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_128 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_128, "Size of destination buffer is smaller than the digest size")
+    ctx: _sha3.Sha3_Context
+    ctx.mdlen = DIGEST_SIZE_128
+    _sha3.init(&ctx)
+    _sha3.update(&ctx, data)
+    _sha3.shake_xof(&ctx)
+    _sha3.shake_out(&ctx, 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) {
-    hash: [16]byte
+hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
+    hash: [DIGEST_SIZE_128]byte
     ctx: _sha3.Sha3_Context
-    ctx.mdlen = 16
+    ctx.mdlen = DIGEST_SIZE_128
     _sha3.init(&ctx)
     buf := make([]byte, 512)
     defer delete(buf)
@@ -62,7 +85,7 @@ hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
 
 // 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) {
+hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_128]byte, bool) {
     if !load_at_once {
         return hash_stream_128(os.stream_from_handle(hd))
     } else {
@@ -70,7 +93,7 @@ hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool)
             return hash_bytes_128(buf[:]), ok
         }
     }
-    return [16]byte{}, false
+    return [DIGEST_SIZE_128]byte{}, false
 }
 
 hash_128 :: proc {
@@ -78,20 +101,22 @@ hash_128 :: proc {
     hash_file_128,
     hash_bytes_128,
     hash_string_128,
+    hash_bytes_to_buffer_128,
+    hash_string_to_buffer_128,
 }
 
 // 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(data: string) -> [DIGEST_SIZE_256]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 {
-    hash: [32]byte
+hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
+    hash: [DIGEST_SIZE_256]byte
     ctx: _sha3.Sha3_Context
-    ctx.mdlen = 32
+    ctx.mdlen = DIGEST_SIZE_256
     _sha3.init(&ctx)
     _sha3.update(&ctx, data)
     _sha3.shake_xof(&ctx)
@@ -99,12 +124,32 @@ hash_bytes_256 :: proc(data: []byte) -> [32]byte {
     return hash
 }
 
+// hash_string_to_buffer_256 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_256(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_256 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
+    ctx: _sha3.Sha3_Context
+    ctx.mdlen = DIGEST_SIZE_256
+    _sha3.init(&ctx)
+    _sha3.update(&ctx, data)
+    _sha3.shake_xof(&ctx)
+    _sha3.shake_out(&ctx, 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) {
-    hash: [32]byte
+hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
+    hash: [DIGEST_SIZE_256]byte
     ctx: _sha3.Sha3_Context
-    ctx.mdlen = 32
+    ctx.mdlen = DIGEST_SIZE_256
     _sha3.init(&ctx)
     buf := make([]byte, 512)
     defer delete(buf)
@@ -122,7 +167,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
 
 // 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) {
+hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
     if !load_at_once {
         return hash_stream_256(os.stream_from_handle(hd))
     } else {
@@ -130,7 +175,7 @@ hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool)
             return hash_bytes_256(buf[:]), ok
         }
     }
-    return [32]byte{}, false
+    return [DIGEST_SIZE_256]byte{}, false
 }
 
 hash_256 :: proc {
@@ -138,13 +183,15 @@ hash_256 :: proc {
     hash_file_256,
     hash_bytes_256,
     hash_string_256,
+    hash_bytes_to_buffer_256,
+    hash_string_to_buffer_256,
 }
 
 /*
     Low level API
 */
 
-Sha3_Context :: _sha3.Sha3_Context
+Shake_Context :: _sha3.Sha3_Context
 
 init :: proc(ctx: ^_sha3.Sha3_Context) {
     _sha3.init(ctx)

+ 33 - 10
core/crypto/sm3/sm3.odin

@@ -15,16 +15,22 @@ import "core:io"
 
 import "../util"
 
+/*
+    High level API
+*/
+
+DIGEST_SIZE :: 32
+
 // hash_string will hash the given input and return the
 // computed hash
-hash_string :: proc(data: string) -> [32]byte {
+hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
     return hash_bytes(transmute([]byte)(data))
 }
 
 // hash_bytes will hash the given input and return the
 // computed hash
-hash_bytes :: proc(data: []byte) -> [32]byte {
-    hash: [32]byte
+hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
+    hash: [DIGEST_SIZE]byte
     ctx: Sm3_Context
     init(&ctx)
     update(&ctx, data)
@@ -32,10 +38,28 @@ hash_bytes :: proc(data: []byte) -> [32]byte {
     return hash
 }
 
+// hash_string_to_buffer will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
+    ctx: Sm3_Context
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, 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) {
-    hash: [32]byte
+hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
+    hash: [DIGEST_SIZE]byte
     ctx: Sm3_Context
     init(&ctx)
     buf := make([]byte, 512)
@@ -53,7 +77,7 @@ hash_stream :: proc(s: io.Stream) -> ([32]byte, bool) {
 
 // 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) {
+hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
     if !load_at_once {
         return hash_stream(os.stream_from_handle(hd))
     } else {
@@ -61,7 +85,7 @@ hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
             return hash_bytes(buf[:]), ok
         }
     }
-    return [32]byte{}, false
+    return [DIGEST_SIZE]byte{}, false
 }
 
 hash :: proc {
@@ -69,6 +93,8 @@ hash :: proc {
     hash_file,
     hash_bytes,
     hash_string,
+    hash_bytes_to_buffer,
+    hash_string_to_buffer,
 }
 
 /*
@@ -146,9 +172,6 @@ Sm3_Context :: struct {
     length:    u64,
 }
 
-BLOCK_SIZE_IN_BYTES :: 64
-BLOCK_SIZE_IN_32    :: 16
-
 IV := [8]u32 {
     0x7380166f, 0x4914b2b9, 0x172442d7, 0xda8a0600,
     0xa96f30bc, 0x163138aa, 0xe38dee4d, 0xb0fb0e4e,

+ 58 - 14
core/crypto/streebog/streebog.odin

@@ -19,16 +19,19 @@ import "../util"
     High level API
 */
 
+DIGEST_SIZE_256 :: 32
+DIGEST_SIZE_512 :: 64
+
 // 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(data: string) -> [DIGEST_SIZE_256]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 {
-    hash: [32]byte
+hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
+    hash: [DIGEST_SIZE_256]byte
     ctx: Streebog_Context
     ctx.is256 = true
     init(&ctx)
@@ -37,10 +40,29 @@ hash_bytes_256 :: proc(data: []byte) -> [32]byte {
     return hash
 }
 
+// hash_string_to_buffer_256 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_256(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_256 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
+    ctx: Streebog_Context
+    ctx.is256 = true
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, 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) {
-    hash: [32]byte
+hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
+    hash: [DIGEST_SIZE_256]byte
     ctx: Streebog_Context
     ctx.is256 = true
     init(&ctx)
@@ -59,7 +81,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
 
 // 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) {
+hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
     if !load_at_once {
         return hash_stream_256(os.stream_from_handle(hd))
     } else {
@@ -67,7 +89,7 @@ hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool)
             return hash_bytes_256(buf[:]), ok
         }
     }
-    return [32]byte{}, false
+    return [DIGEST_SIZE_256]byte{}, false
 }
 
 hash_256 :: proc {
@@ -75,18 +97,20 @@ hash_256 :: proc {
     hash_file_256,
     hash_bytes_256,
     hash_string_256,
+	hash_bytes_to_buffer_256,
+    hash_string_to_buffer_256,
 }
 
 // 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(data: string) -> [DIGEST_SIZE_512]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 {
-    hash: [64]byte
+hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
+    hash: [DIGEST_SIZE_512]byte
     ctx: Streebog_Context
     init(&ctx)
     update(&ctx, data)
@@ -94,10 +118,28 @@ hash_bytes_512 :: proc(data: []byte) -> [64]byte {
     return hash
 }
 
+// hash_string_to_buffer_512 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_512(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_512 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
+    ctx: Streebog_Context
+    init(&ctx)
+    update(&ctx, data)
+    final(&ctx, 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) {
-    hash: [64]byte
+hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
+    hash: [DIGEST_SIZE_512]byte
     ctx: Streebog_Context
     init(&ctx)
     buf := make([]byte, 512)
@@ -115,7 +157,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
 
 // 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) {
+hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512]byte, bool) {
     if !load_at_once {
         return hash_stream_512(os.stream_from_handle(hd))
     } else {
@@ -123,7 +165,7 @@ hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool)
             return hash_bytes_512(buf[:]), ok
         }
     }
-    return [64]byte{}, false
+    return [DIGEST_SIZE_512]byte{}, false
 }
 
 hash_512 :: proc {
@@ -131,6 +173,8 @@ hash_512 :: proc {
     hash_file_512,
     hash_bytes_512,
     hash_string_512,
+    hash_bytes_to_buffer_512,
+    hash_string_to_buffer_512,
 }
 
 /*

+ 88 - 21
core/crypto/tiger/tiger.odin

@@ -19,16 +19,20 @@ import "../_tiger"
     High level API
 */
 
+DIGEST_SIZE_128 :: 16
+DIGEST_SIZE_160 :: 20
+DIGEST_SIZE_192 :: 24
+
 // hash_string_128 will hash the given input and return the
 // computed hash
-hash_string_128 :: proc(data: string) -> [16]byte {
+hash_string_128 :: proc(data: string) -> [DIGEST_SIZE_128]byte {
     return hash_bytes_128(transmute([]byte)(data))
 }
 
 // hash_bytes_128 will hash the given input and return the
 // computed hash
-hash_bytes_128 :: proc(data: []byte) -> [16]byte {
-    hash: [16]byte
+hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte {
+    hash: [DIGEST_SIZE_128]byte
     ctx: _tiger.Tiger_Context
     ctx.ver = 1
     _tiger.init(&ctx)
@@ -37,10 +41,29 @@ hash_bytes_128 :: proc(data: []byte) -> [16]byte {
     return hash
 }
 
+// hash_string_to_buffer_128 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_128 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_128(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_128 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_128 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_128, "Size of destination buffer is smaller than the digest size")
+    ctx: _tiger.Tiger_Context
+    ctx.ver = 1
+    _tiger.init(&ctx)
+    _tiger.update(&ctx, data)
+    _tiger.final(&ctx, 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) {
-    hash: [16]byte
+hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
+    hash: [DIGEST_SIZE_128]byte
     ctx: _tiger.Tiger_Context
     ctx.ver = 1
     _tiger.init(&ctx)
@@ -59,7 +82,7 @@ hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
 
 // 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) {
+hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_128]byte, bool) {
     if !load_at_once {
         return hash_stream_128(os.stream_from_handle(hd))
     } else {
@@ -67,7 +90,7 @@ hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool)
             return hash_bytes_128(buf[:]), ok
         }
     }
-    return [16]byte{}, false
+    return [DIGEST_SIZE_128]byte{}, false
 }
 
 hash_128 :: proc {
@@ -75,18 +98,20 @@ hash_128 :: proc {
     hash_file_128,
     hash_bytes_128,
     hash_string_128,
+    hash_bytes_to_buffer_128,
+    hash_string_to_buffer_128,
 }
 
 // hash_string_160 will hash the given input and return the
 // computed hash
-hash_string_160 :: proc(data: string) -> [20]byte {
+hash_string_160 :: proc(data: string) -> [DIGEST_SIZE_160]byte {
     return hash_bytes_160(transmute([]byte)(data))
 }
 
 // hash_bytes_160 will hash the given input and return the
 // computed hash
-hash_bytes_160 :: proc(data: []byte) -> [20]byte {
-    hash: [20]byte
+hash_bytes_160 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte {
+    hash: [DIGEST_SIZE_160]byte
     ctx: _tiger.Tiger_Context
     ctx.ver = 1
     _tiger.init(&ctx)
@@ -95,10 +120,29 @@ hash_bytes_160 :: proc(data: []byte) -> [20]byte {
     return hash
 }
 
+// hash_string_to_buffer_160 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_160 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_160(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_160 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_160 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_160, "Size of destination buffer is smaller than the digest size")
+    ctx: _tiger.Tiger_Context
+    ctx.ver = 1
+    _tiger.init(&ctx)
+    _tiger.update(&ctx, data)
+    _tiger.final(&ctx, 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) {
-    hash: [20]byte
+hash_stream_160 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) {
+    hash: [DIGEST_SIZE_160]byte
     ctx: _tiger.Tiger_Context
     ctx.ver = 1
     _tiger.init(&ctx)
@@ -117,7 +161,7 @@ hash_stream_160 :: proc(s: io.Stream) -> ([20]byte, bool) {
 
 // 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) {
+hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_160]byte, bool) {
     if !load_at_once {
         return hash_stream_160(os.stream_from_handle(hd))
     } else {
@@ -125,7 +169,7 @@ hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool)
             return hash_bytes_160(buf[:]), ok
         }
     }
-    return [20]byte{}, false
+    return [DIGEST_SIZE_160]byte{}, false
 }
 
 hash_160 :: proc {
@@ -133,18 +177,20 @@ hash_160 :: proc {
     hash_file_160,
     hash_bytes_160,
     hash_string_160,
+    hash_bytes_to_buffer_160,
+    hash_string_to_buffer_160,
 }
 
 // hash_string_192 will hash the given input and return the
 // computed hash
-hash_string_192 :: proc(data: string) -> [24]byte {
+hash_string_192 :: proc(data: string) -> [DIGEST_SIZE_192]byte {
     return hash_bytes_192(transmute([]byte)(data))
 }
 
 // hash_bytes_192 will hash the given input and return the
 // computed hash
-hash_bytes_192 :: proc(data: []byte) -> [24]byte {
-    hash: [24]byte
+hash_bytes_192 :: proc(data: []byte) -> [DIGEST_SIZE_192]byte {
+    hash: [DIGEST_SIZE_192]byte
     ctx: _tiger.Tiger_Context
     ctx.ver = 1
     _tiger.init(&ctx)
@@ -153,10 +199,29 @@ hash_bytes_192 :: proc(data: []byte) -> [24]byte {
     return hash
 }
 
+// hash_string_to_buffer_192 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_192 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_192(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_192 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_192 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_192, "Size of destination buffer is smaller than the digest size")
+    ctx: _tiger.Tiger_Context
+    ctx.ver = 1
+    _tiger.init(&ctx)
+    _tiger.update(&ctx, data)
+    _tiger.final(&ctx, 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) {
-    hash: [24]byte
+hash_stream_192 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) {
+    hash: [DIGEST_SIZE_192]byte
     ctx: _tiger.Tiger_Context
     ctx.ver = 1
     _tiger.init(&ctx)
@@ -175,7 +240,7 @@ hash_stream_192 :: proc(s: io.Stream) -> ([24]byte, bool) {
 
 // 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) {
+hash_file_192 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_192]byte, bool) {
     if !load_at_once {
         return hash_stream_192(os.stream_from_handle(hd))
     } else {
@@ -183,7 +248,7 @@ hash_file_192 :: proc(hd: os.Handle, load_at_once := false) -> ([24]byte, bool)
             return hash_bytes_192(buf[:]), ok
         }
     }
-    return [24]byte{}, false
+    return [DIGEST_SIZE_192]byte{}, false
 }
 
 hash_192 :: proc {
@@ -191,6 +256,8 @@ hash_192 :: proc {
     hash_file_192,
     hash_bytes_192,
     hash_string_192,
+    hash_bytes_to_buffer_192,
+    hash_string_to_buffer_192,
 }
 
 /*

+ 88 - 21
core/crypto/tiger2/tiger2.odin

@@ -19,16 +19,20 @@ import "../_tiger"
     High level API
 */
 
+DIGEST_SIZE_128 :: 16
+DIGEST_SIZE_160 :: 20
+DIGEST_SIZE_192 :: 24
+
 // hash_string_128 will hash the given input and return the
 // computed hash
-hash_string_128 :: proc(data: string) -> [16]byte {
+hash_string_128 :: proc(data: string) -> [DIGEST_SIZE_128]byte {
     return hash_bytes_128(transmute([]byte)(data))
 }
 
 // hash_bytes_128 will hash the given input and return the
 // computed hash
-hash_bytes_128 :: proc(data: []byte) -> [16]byte {
-    hash: [16]byte
+hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte {
+    hash: [DIGEST_SIZE_128]byte
     ctx: _tiger.Tiger_Context
     ctx.ver = 2
     _tiger.init(&ctx)
@@ -37,10 +41,29 @@ hash_bytes_128 :: proc(data: []byte) -> [16]byte {
     return hash
 }
 
+// hash_string_to_buffer_128 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_128 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_128(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_128 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_128 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_128, "Size of destination buffer is smaller than the digest size")
+    ctx: _tiger.Tiger_Context
+    ctx.ver = 2
+    _tiger.init(&ctx)
+    _tiger.update(&ctx, data)
+    _tiger.final(&ctx, 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) {
-    hash: [16]byte
+hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
+    hash: [DIGEST_SIZE_128]byte
     ctx: _tiger.Tiger_Context
     ctx.ver = 2
     _tiger.init(&ctx)
@@ -59,7 +82,7 @@ hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
 
 // 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) {
+hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_128]byte, bool) {
     if !load_at_once {
         return hash_stream_128(os.stream_from_handle(hd))
     } else {
@@ -67,7 +90,7 @@ hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool)
             return hash_bytes_128(buf[:]), ok
         }
     }
-    return [16]byte{}, false
+    return [DIGEST_SIZE_128]byte{}, false
 }
 
 hash_128 :: proc {
@@ -75,18 +98,20 @@ hash_128 :: proc {
     hash_file_128,
     hash_bytes_128,
     hash_string_128,
+    hash_bytes_to_buffer_128,
+    hash_string_to_buffer_128,
 }
 
 // hash_string_160 will hash the given input and return the
 // computed hash
-hash_string_160 :: proc(data: string) -> [20]byte {
+hash_string_160 :: proc(data: string) -> [DIGEST_SIZE_160]byte {
     return hash_bytes_160(transmute([]byte)(data))
 }
 
 // hash_bytes_160 will hash the given input and return the
 // computed hash
-hash_bytes_160 :: proc(data: []byte) -> [20]byte {
-    hash: [20]byte
+hash_bytes_160 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte {
+    hash: [DIGEST_SIZE_160]byte
     ctx: _tiger.Tiger_Context
     ctx.ver = 2
     _tiger.init(&ctx)
@@ -95,10 +120,29 @@ hash_bytes_160 :: proc(data: []byte) -> [20]byte {
     return hash
 }
 
+// hash_string_to_buffer_160 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_160 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_160(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_160 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_160 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_160, "Size of destination buffer is smaller than the digest size")
+    ctx: _tiger.Tiger_Context
+    ctx.ver = 2
+    _tiger.init(&ctx)
+    _tiger.update(&ctx, data)
+    _tiger.final(&ctx, 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) {
-    hash: [20]byte
+hash_stream_160 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) {
+    hash: [DIGEST_SIZE_160]byte
     ctx: _tiger.Tiger_Context
     ctx.ver = 2
     _tiger.init(&ctx)
@@ -117,7 +161,7 @@ hash_stream_160 :: proc(s: io.Stream) -> ([20]byte, bool) {
 
 // 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) {
+hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_160]byte, bool) {
     if !load_at_once {
         return hash_stream_160(os.stream_from_handle(hd))
     } else {
@@ -125,7 +169,7 @@ hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool)
             return hash_bytes_160(buf[:]), ok
         }
     }
-    return [20]byte{}, false
+    return [DIGEST_SIZE_160]byte{}, false
 }
 
 hash_160 :: proc {
@@ -133,18 +177,20 @@ hash_160 :: proc {
     hash_file_160,
     hash_bytes_160,
     hash_string_160,
+    hash_bytes_to_buffer_160,
+    hash_string_to_buffer_160,
 }
 
 // hash_string_192 will hash the given input and return the
 // computed hash
-hash_string_192 :: proc(data: string) -> [24]byte {
+hash_string_192 :: proc(data: string) -> [DIGEST_SIZE_192]byte {
     return hash_bytes_192(transmute([]byte)(data))
 }
 
 // hash_bytes_192 will hash the given input and return the
 // computed hash
-hash_bytes_192 :: proc(data: []byte) -> [24]byte {
-    hash: [24]byte
+hash_bytes_192 :: proc(data: []byte) -> [DIGEST_SIZE_192]byte {
+    hash: [DIGEST_SIZE_192]byte
     ctx: _tiger.Tiger_Context
     ctx.ver = 2
     _tiger.init(&ctx)
@@ -153,10 +199,29 @@ hash_bytes_192 :: proc(data: []byte) -> [24]byte {
     return hash
 }
 
+// hash_string_to_buffer_192 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_192 :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer_192(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer_192 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_192 :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE_192, "Size of destination buffer is smaller than the digest size")
+    ctx: _tiger.Tiger_Context
+    ctx.ver = 2
+    _tiger.init(&ctx)
+    _tiger.update(&ctx, data)
+    _tiger.final(&ctx, 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) {
-    hash: [24]byte
+hash_stream_192 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) {
+    hash: [DIGEST_SIZE_192]byte
     ctx: _tiger.Tiger_Context
     ctx.ver = 2
     _tiger.init(&ctx)
@@ -175,7 +240,7 @@ hash_stream_192 :: proc(s: io.Stream) -> ([24]byte, bool) {
 
 // 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) {
+hash_file_192 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_192]byte, bool) {
     if !load_at_once {
         return hash_stream_192(os.stream_from_handle(hd))
     } else {
@@ -183,7 +248,7 @@ hash_file_192 :: proc(hd: os.Handle, load_at_once := false) -> ([24]byte, bool)
             return hash_bytes_192(buf[:]), ok
         }
     }
-    return [24]byte{}, false
+    return [DIGEST_SIZE_192]byte{}, false
 }
 
 hash_192 :: proc {
@@ -191,6 +256,8 @@ hash_192 :: proc {
     hash_file_192,
     hash_bytes_192,
     hash_string_192,
+    hash_bytes_to_buffer_192,
+    hash_string_to_buffer_192,
 }
 
 /*

+ 29 - 7
core/crypto/whirlpool/whirlpool.odin

@@ -19,16 +19,18 @@ import "../util"
     High level API
 */
 
+DIGEST_SIZE :: 64
+
 // hash_string will hash the given input and return the
 // computed hash
-hash_string :: proc(data: string) -> [64]byte {
+hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
     return hash_bytes(transmute([]byte)(data))
 }
 
 // hash_bytes will hash the given input and return the
 // computed hash
-hash_bytes :: proc(data: []byte) -> [64]byte {
-	hash: [64]byte
+hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
+	hash: [DIGEST_SIZE]byte
 	ctx: Whirlpool_Context
     // init(&ctx) No-op
     update(&ctx, data)
@@ -36,10 +38,28 @@ hash_bytes :: proc(data: []byte) -> [64]byte {
     return hash
 }
 
+// hash_string_to_buffer will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer :: proc(data: string, hash: []byte) {
+    hash_bytes_to_buffer(transmute([]byte)(data), hash);
+}
+
+// hash_bytes_to_buffer will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer :: proc(data, hash: []byte) {
+    assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
+    ctx: Whirlpool_Context
+    // init(&ctx) No-op
+    update(&ctx, data)
+    final(&ctx, 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) {
-	hash: [64]byte
+hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
+	hash: [DIGEST_SIZE]byte
 	ctx: Whirlpool_Context
 	// init(&ctx) No-op
 	buf := make([]byte, 512)
@@ -57,7 +77,7 @@ hash_stream :: proc(s: io.Stream) -> ([64]byte, bool) {
 
 // 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) {
+hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
 	if !load_at_once {
         return hash_stream(os.stream_from_handle(hd))
     } else {
@@ -65,7 +85,7 @@ hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
             return hash_bytes(buf[:]), ok
         }
     }
-    return [64]byte{}, false
+    return [DIGEST_SIZE]byte{}, false
 }
 
 hash :: proc {
@@ -73,6 +93,8 @@ hash :: proc {
     hash_file,
     hash_bytes,
     hash_string,
+    hash_bytes_to_buffer,
+    hash_string_to_buffer,
 }
 
 /*

+ 126 - 0
core/crypto/x25519/x25519.odin

@@ -0,0 +1,126 @@
+package x25519
+
+import field "core:crypto/_fiat/field_curve25519"
+import "core:mem"
+
+SCALAR_SIZE :: 32
+POINT_SIZE :: 32
+
+_BASE_POINT: [32]byte = {9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+
+_scalar_bit :: #force_inline proc "contextless" (s: ^[32]byte, i: int) -> u8 {
+	if i < 0 {
+		return 0
+	}
+	return (s[i>>3] >> uint(i&7)) & 1
+}
+
+_scalarmult :: proc (out, scalar, point: ^[32]byte) {
+	// Montgomery pseduo-multiplication taken from Monocypher.
+
+	// computes the scalar product
+	x1: field.Tight_Field_Element = ---
+	field.fe_from_bytes(&x1, point)
+
+	// computes the actual scalar product (the result is in x2 and z2)
+	x2, x3, z2, z3: field.Tight_Field_Element =  ---, ---, ---, ---
+	t0, t1: field.Loose_Field_Element = ---, ---
+
+	// Montgomery ladder
+	// In projective coordinates, to avoid divisions: x = X / Z
+	// We don't care about the y coordinate, it's only 1 bit of information
+	field.fe_one(&x2) // "zero" point
+	field.fe_zero(&z2)
+	field.fe_set(&x3, &x1) // "one" point
+	field.fe_one(&z3)
+
+	swap: int
+	for pos := 255-1; pos >= 0; pos = pos - 1 	{
+		// constant time conditional swap before ladder step
+		b := int(_scalar_bit(scalar, pos))
+		swap ~= b // xor trick avoids swapping at the end of the loop
+		field.fe_cond_swap(&x2, &x3, swap)
+		field.fe_cond_swap(&z2, &z3, swap)
+		swap = b // anticipates one last swap after the loop
+
+		// Montgomery ladder step: replaces (P2, P3) by (P2*2, P2+P3)
+		// with differential addition
+		//
+		// Note: This deliberately omits reductions after add/sub operations
+		// if the result is only ever used as the input to a mul/square since
+		// the implementations of those can deal with non-reduced inputs.
+		//
+		// fe_tighten_cast is only used to store a fully reduced
+		// output in a Loose_Field_Element, or to provide such a
+		// Loose_Field_Element as a Tight_Field_Element argument.
+		field.fe_sub(&t0, &x3, &z3)
+		field.fe_sub(&t1, &x2, &z2)
+		field.fe_add(field.fe_relax_cast(&x2), &x2, &z2) // x2 - unreduced
+		field.fe_add(field.fe_relax_cast(&z2), &x3, &z3) // z2 - unreduced
+		field.fe_carry_mul(&z3, &t0, field.fe_relax_cast(&x2))
+		field.fe_carry_mul(&z2, field.fe_relax_cast(&z2), &t1) // z2 - reduced
+		field.fe_carry_square(field.fe_tighten_cast(&t0), &t1) // t0 - reduced
+		field.fe_carry_square(field.fe_tighten_cast(&t1), field.fe_relax_cast(&x2)) // t1 - reduced
+		field.fe_add(field.fe_relax_cast(&x3), &z3, &z2) // x3 - unreduced
+		field.fe_sub(field.fe_relax_cast(&z2), &z3, &z2) // z2 - unreduced
+		field.fe_carry_mul(&x2, &t1, &t0) // x2 - reduced
+		field.fe_sub(&t1, field.fe_tighten_cast(&t1), field.fe_tighten_cast(&t0)) // safe - t1/t0 is reduced
+		field.fe_carry_square(&z2, field.fe_relax_cast(&z2)) // z2 - reduced
+		field.fe_carry_scmul_121666(&z3, &t1)
+		field.fe_carry_square(&x3, field.fe_relax_cast(&x3)) // x3 - reduced
+		field.fe_add(&t0, field.fe_tighten_cast(&t0), &z3) // safe - t0 is reduced
+		field.fe_carry_mul(&z3, field.fe_relax_cast(&x1), field.fe_relax_cast(&z2))
+		field.fe_carry_mul(&z2, &t1, &t0)
+	}
+	// last swap is necessary to compensate for the xor trick
+	// Note: after this swap, P3 == P2 + P1.
+	field.fe_cond_swap(&x2, &x3, swap)
+	field.fe_cond_swap(&z2, &z3, swap)
+
+	// normalises the coordinates: x == X / Z
+	field.fe_carry_inv(&z2, field.fe_relax_cast(&z2))
+	field.fe_carry_mul(&x2, field.fe_relax_cast(&x2), field.fe_relax_cast(&z2))
+	field.fe_to_bytes(out, &x2)
+
+	mem.zero_explicit(&x1, size_of(x1))
+	mem.zero_explicit(&x2, size_of(x2))
+	mem.zero_explicit(&x3, size_of(x3))
+	mem.zero_explicit(&z2, size_of(z2))
+	mem.zero_explicit(&z3, size_of(z3))
+	mem.zero_explicit(&t0, size_of(t0))
+	mem.zero_explicit(&t1, size_of(t1))
+}
+
+scalarmult :: proc (dst, scalar, point: []byte) {
+	if len(scalar) != SCALAR_SIZE {
+		panic("crypto/x25519: invalid scalar size")
+	}
+	if len(point) != POINT_SIZE {
+		panic("crypto/x25519: invalid point size")
+	}
+	if len(dst) != POINT_SIZE {
+		panic("crypto/x25519: invalid destination point size")
+	}
+
+	// "clamp" the scalar
+	e: [32]byte = ---
+	copy_slice(e[:], scalar)
+	e[0] &= 248
+	e[31] &= 127
+	e[31] |= 64
+
+	p: [32]byte = ---
+	copy_slice(p[:], point)
+
+	d: [32]byte = ---
+	_scalarmult(&d, &e, &p)
+	copy_slice(dst, d[:])
+
+	mem.zero_explicit(&e, size_of(e))
+	mem.zero_explicit(&d, size_of(d))
+}
+
+scalarmult_basepoint :: proc (dst, scalar: []byte) {
+	// TODO/perf: Switch to using a precomputed table.
+	scalarmult(dst, scalar, _BASE_POINT[:])
+}

+ 25 - 25
core/encoding/json/unmarshal.odin

@@ -52,11 +52,11 @@ unmarshal_any :: proc(data: []byte, v: any, spec := DEFAULT_SPECIFICATION, alloc
 	if p.spec == .MJSON {
 		#partial switch p.curr_token.kind {
 		case .Ident, .String:
-			return unmarsal_object(&p, data, .EOF)
+			return unmarshal_object(&p, data, .EOF)
 		}
 	}
 
-	return unmarsal_value(&p, data)
+	return unmarshal_value(&p, data)
 }
 
 
@@ -148,7 +148,7 @@ assign_float :: proc(val: any, f: $T) -> bool {
 
 
 @(private)
-unmarsal_string :: proc(p: ^Parser, val: any, str: string, ti: ^reflect.Type_Info) -> bool {
+unmarshal_string_token :: proc(p: ^Parser, val: any, str: string, ti: ^reflect.Type_Info) -> bool {
 	val := val
 	switch dst in &val {
 	case string:
@@ -198,7 +198,7 @@ unmarsal_string :: proc(p: ^Parser, val: any, str: string, ti: ^reflect.Type_Inf
 
 
 @(private)
-unmarsal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
+unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
 	UNSUPPORTED_TYPE := Unsupported_Type_Error{v.id, p.curr_token}
 	token := p.curr_token
 	
@@ -257,7 +257,7 @@ unmarsal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
 	case .Ident:
 		advance_token(p)
 		if p.spec == .MJSON {
-			if unmarsal_string(p, any{v.data, ti.id}, token.text, ti) {
+			if unmarshal_string_token(p, any{v.data, ti.id}, token.text, ti) {
 				return nil
 			}
 		}
@@ -266,7 +266,7 @@ unmarsal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
 	case .String:
 		advance_token(p)
 		str := unquote_string(token, p.spec, p.allocator) or_return
-		if unmarsal_string(p, any{v.data, ti.id}, str, ti) {
+		if unmarshal_string_token(p, any{v.data, ti.id}, str, ti) {
 			return nil
 		}
 		delete(str, p.allocator)
@@ -274,10 +274,10 @@ unmarsal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
 
 
 	case .Open_Brace:
-		return unmarsal_object(p, v, .Close_Brace)
+		return unmarshal_object(p, v, .Close_Brace)
 
 	case .Open_Bracket:
-		return unmarsal_array(p, v)
+		return unmarshal_array(p, v)
 
 	case:
 		if p.spec != .JSON {
@@ -312,16 +312,16 @@ unmarsal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
 
 
 @(private)
-unmarsal_expect_token :: proc(p: ^Parser, kind: Token_Kind, loc := #caller_location) -> Token {
+unmarshal_expect_token :: proc(p: ^Parser, kind: Token_Kind, loc := #caller_location) -> Token {
 	prev := p.curr_token
 	err := expect_token(p, kind)
-	assert(err == nil, "unmarsal_expect_token")
+	assert(err == nil, "unmarshal_expect_token")
 	return prev
 }
 
 
 @(private)
-unmarsal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unmarshal_Error) {
+unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unmarshal_Error) {
 	UNSUPPORTED_TYPE := Unsupported_Type_Error{v.id, p.curr_token}
 	
 	if end_token == .Close_Brace {
@@ -342,7 +342,7 @@ unmarsal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unma
 			key, _ := parse_object_key(p, p.allocator)
 			defer delete(key, p.allocator)
 			
-			unmarsal_expect_token(p, .Colon)						
+			unmarshal_expect_token(p, .Colon)						
 			
 			fields := reflect.struct_fields_zipped(ti.id)
 			
@@ -378,7 +378,7 @@ unmarsal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unma
 				
 				field_ptr := rawptr(uintptr(v.data) + offset)
 				field := any{field_ptr, type.id}
-				unmarsal_value(p, field) or_return
+				unmarshal_value(p, field) or_return
 					
 				if parse_comma(p) {
 					break struct_loop
@@ -407,11 +407,11 @@ unmarsal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unma
 		
 		map_loop: for p.curr_token.kind != end_token {
 			key, _ := parse_object_key(p, p.allocator)
-			unmarsal_expect_token(p, .Colon)
+			unmarshal_expect_token(p, .Colon)
 			
 			
 			mem.zero_slice(elem_backing)
-			if err := unmarsal_value(p, map_backing_value); err != nil {
+			if err := unmarshal_value(p, map_backing_value); err != nil {
 				delete(key, p.allocator)
 				return err
 			}
@@ -443,7 +443,7 @@ unmarsal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unma
 	
 		enumerated_array_loop: for p.curr_token.kind != end_token {
 			key, _ := parse_object_key(p, p.allocator)
-			unmarsal_expect_token(p, .Colon)
+			unmarshal_expect_token(p, .Colon)
 			defer delete(key, p.allocator)
 
 			index := -1
@@ -460,7 +460,7 @@ unmarsal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unma
 			index_ptr := rawptr(uintptr(v.data) + uintptr(index*t.elem_size))
 			index_any := any{index_ptr, t.elem.id}
 			
-			unmarsal_value(p, index_any) or_return
+			unmarshal_value(p, index_any) or_return
 			
 			if parse_comma(p) {
 				break enumerated_array_loop
@@ -480,10 +480,10 @@ unmarsal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unma
 
 
 @(private)
-unmarsal_count_array :: proc(p: ^Parser) -> (length: uintptr) {
+unmarshal_count_array :: proc(p: ^Parser) -> (length: uintptr) {
 	p_backup := p^
 	p.allocator = mem.nil_allocator()
-	unmarsal_expect_token(p, .Open_Bracket)
+	unmarshal_expect_token(p, .Open_Bracket)
 	array_length_loop: for p.curr_token.kind != .Close_Bracket {
 		_, _ = parse_value(p)
 		length += 1
@@ -497,9 +497,9 @@ unmarsal_count_array :: proc(p: ^Parser) -> (length: uintptr) {
 }
 
 @(private)
-unmarsal_array :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
+unmarshal_array :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
 	assign_array :: proc(p: ^Parser, base: rawptr, elem: ^reflect.Type_Info, length: uintptr) -> Unmarshal_Error {
-		unmarsal_expect_token(p, .Open_Bracket)
+		unmarshal_expect_token(p, .Open_Bracket)
 		
 		for idx: uintptr = 0; p.curr_token.kind != .Close_Bracket; idx += 1 {
 			assert(idx < length)
@@ -507,14 +507,14 @@ unmarsal_array :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
 			elem_ptr := rawptr(uintptr(base) + idx*uintptr(elem.size))
 			elem := any{elem_ptr, elem.id}
 			
-			unmarsal_value(p, elem) or_return
+			unmarshal_value(p, elem) or_return
 			
 			if parse_comma(p) {
 				break
 			}	
 		}
 		
-		unmarsal_expect_token(p, .Close_Bracket)
+		unmarshal_expect_token(p, .Close_Bracket)
 		
 		
 		return nil
@@ -524,7 +524,7 @@ unmarsal_array :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
 	
 	ti := reflect.type_info_base(type_info_of(v.id))
 	
-	length := unmarsal_count_array(p)
+	length := unmarshal_count_array(p)
 	
 	#partial switch t in ti.variant {
 	case reflect.Type_Info_Slice:	
@@ -578,4 +578,4 @@ unmarsal_array :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
 	}
 		
 	return UNSUPPORTED_TYPE
-}
+}

+ 17 - 0
core/hash/hash.odin

@@ -55,6 +55,23 @@ djb2 :: proc(data: []byte, seed := u32(5381)) -> u32 {
 	return hash
 }
 
+djbx33a :: proc(data: []byte, seed := u32(5381)) -> (result: [16]byte) #no_bounds_check {
+	state := [4]u32{seed, seed, seed, seed}
+	
+	s: u32 = 0
+	for p in data {
+		state[s] = (state[s] << 5) + state[s] + u32(p) // hash * 33 + u32(b)
+		s = (s + 1) & 3
+	}
+	
+	
+	(^u32le)(&result[0])^  = u32le(state[0])
+	(^u32le)(&result[4])^  = u32le(state[1])
+	(^u32le)(&result[8])^  = u32le(state[2])
+	(^u32le)(&result[12])^ = u32le(state[3])
+	return
+}
+
 @(optimization_mode="speed")
 fnv32 :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
 	h: u32 = seed

+ 12 - 11
core/math/big/common.odin

@@ -158,13 +158,14 @@ Error :: enum int {
 	Invalid_Pointer         = 2,
 	Invalid_Argument        = 3,
 
-	Assignment_To_Immutable = 4,
-	Max_Iterations_Reached  = 5,
-	Buffer_Overflow         = 6,
-	Integer_Overflow        = 7,
+	Assignment_To_Immutable = 10,
+	Max_Iterations_Reached  = 11,
+	Buffer_Overflow         = 12,
+	Integer_Overflow        = 13,
+	Integer_Underflow       = 14,
 
-	Division_by_Zero        = 8,
-	Math_Domain_Error       = 9,
+	Division_by_Zero        = 30,
+	Math_Domain_Error       = 31,
 
 	Cannot_Open_File        = 50,
 	Cannot_Read_File        = 51,
@@ -215,7 +216,7 @@ _MIN_DIGIT_COUNT :: max(3, ((size_of(u128) + _DIGIT_BITS) - 1) / _DIGIT_BITS)
 /*
 	Maximum number of digits.
 	- Must be small enough such that `_bit_count` does not overflow.
- 	- Must be small enough such that `_radix_size` for base 2 does not overflow.
+	- Must be small enough such that `_radix_size` for base 2 does not overflow.
 	`_radix_size` needs two additional bytes for zero termination and sign.
 */
 _MAX_BIT_COUNT   :: (max(int) - 2)
@@ -251,7 +252,7 @@ Order :: enum i8 {
 }
 
 Endianness :: enum i8 {
-   Little   = -1,
-   Platform =  0,
-   Big      =  1,
-};
+	Little   = -1,
+	Platform =  0,
+	Big      =  1,
+}

+ 76 - 92
core/math/big/internal.odin

@@ -34,6 +34,7 @@ package math_big
 import "core:mem"
 import "core:intrinsics"
 import rnd "core:math/rand"
+import "core:builtin"
 
 /*
 	Low-level addition, unsigned. Handbook of Applied Cryptography, algorithm 14.7.
@@ -1880,8 +1881,6 @@ internal_int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, al
 	where intrinsics.type_is_integer(T) {
 	context.allocator = allocator
 
-	src := src
-
 	internal_error_if_immutable(dest) or_return
 	/*
 		Most internal procs asssume an Int to have already been initialize,
@@ -1892,13 +1891,27 @@ internal_int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, al
 	dest.flags = {} // We're not -Inf, Inf, NaN or Immutable.
 
 	dest.used  = 0
-	dest.sign = .Zero_or_Positive if src >= 0 else .Negative
-	src = internal_abs(src)
+	dest.sign = .Negative if src < 0 else .Zero_or_Positive
+
+	temp := src
+
+	is_maximally_negative := src == min(T)
+	if is_maximally_negative {
+		/*
+			Prevent overflow on abs()
+		*/
+		temp += 1
+	}
+	temp = -temp if temp < 0 else temp
 
-	#no_bounds_check for src != 0 {
-		dest.digit[dest.used] = DIGIT(src) & _MASK
+	#no_bounds_check for temp != 0 {
+		dest.digit[dest.used] = DIGIT(temp) & _MASK
 		dest.used += 1
-		src >>= _DIGIT_BITS
+		temp >>= _DIGIT_BITS
+	}
+
+	if is_maximally_negative {
+		return internal_sub(dest, dest, 1)
 	}
 	internal_zero_unused(dest)
 	return nil
@@ -2307,28 +2320,69 @@ internal_int_get_i32 :: proc(a: ^Int) -> (res: i32, err: Error) {
 }
 internal_get_i32 :: proc { internal_int_get_i32, }
 
+internal_get_low_u32 :: proc(a: ^Int) -> u32 #no_bounds_check {
+	if a == nil {
+		return 0
+	}
+	
+	if a.used == 0 {
+		return 0
+	}
+	
+	return u32(a.digit[0])
+}
+internal_get_low_u64 :: proc(a: ^Int) -> u64 #no_bounds_check {
+	if a == nil {
+		return 0
+	}
+	
+	if a.used == 0 {
+		return 0
+	}
+	
+	v := u64(a.digit[0])
+	when size_of(DIGIT) == 4 {
+		if a.used > 1 {
+			return u64(a.digit[1])<<32 | v
+		}
+	}
+	return v
+}
+
 /*
 	TODO: Think about using `count_bits` to check if the value could be returned completely,
 	and maybe return max(T), .Integer_Overflow if not?
 */
 internal_int_get :: proc(a: ^Int, $T: typeid) -> (res: T, err: Error) where intrinsics.type_is_integer(T) {
-	size_in_bits := int(size_of(T) * 8)
-	i := int((size_in_bits + _DIGIT_BITS - 1) / _DIGIT_BITS)
-	i  = min(int(a.used), i)
-
-	#no_bounds_check for ; i >= 0; i -= 1 {
-		res <<= uint(0) if size_in_bits <= _DIGIT_BITS else _DIGIT_BITS
-		res |= T(a.digit[i])
-		if size_in_bits <= _DIGIT_BITS {
-			break
+	/*
+		Calculate target bit size.
+	*/
+	target_bit_size := int(size_of(T) * 8)
+	when !intrinsics.type_is_unsigned(T) {
+		if a.sign == .Zero_or_Positive {
+			target_bit_size -= 1
+		}
+	} else {
+		if a.sign == .Negative {
+			return 0, .Integer_Underflow
+		}
+	}
+
+	bits_used := internal_count_bits(a)
+
+	if bits_used > target_bit_size {
+		if a.sign == .Negative {
+			return min(T), .Integer_Underflow
 		}
+		return max(T), .Integer_Overflow
+	}
+
+	for i := a.used; i > 0; i -= 1 {
+		res <<= _DIGIT_BITS
+		res |=  T(a.digit[i - 1])
 	}
 
 	when !intrinsics.type_is_unsigned(T) {
-		/*
-			Mask off sign bit.
-		*/
-		res ~= 1 << uint(size_in_bits - 1)
 		/*
 			Set the sign.
 		*/
@@ -2594,7 +2648,7 @@ internal_int_shrmod :: proc(quotient, remainder, numerator: ^Int, bits: int, all
 		Shift by as many digits in the bit count.
 	*/
 	if bits >= _DIGIT_BITS {
-		internal_shr_digit(quotient, bits / _DIGIT_BITS) or_return
+		_private_int_shr_leg(quotient, bits / _DIGIT_BITS) or_return
 	}
 
 	/*
@@ -2633,37 +2687,6 @@ internal_int_shr :: proc(dest, source: ^Int, bits: int, allocator := context.all
 }
 internal_shr :: proc { internal_int_shr, }
 
-/*
-	Shift right by `digits` * _DIGIT_BITS bits.
-*/
-internal_int_shr_digit :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) {
-	context.allocator = allocator
-
-	if digits <= 0 { return nil }
-
-	/*
-		If digits > used simply zero and return.
-	*/
-	if digits > quotient.used { return internal_zero(quotient) }
-
-	/*
-		Much like `int_shl_digit`, this is implemented using a sliding window,
-		except the window goes the other way around.
-
-		b-2 | b-1 | b0 | b1 | b2 | ... | bb |   ---->
-					/\                   |      ---->
-					 \-------------------/      ---->
-	*/
-
-	#no_bounds_check for x := 0; x < (quotient.used - digits); x += 1 {
-		quotient.digit[x] = quotient.digit[x + digits]
-	}
-	quotient.used -= digits
-	internal_zero_unused(quotient)
-	return internal_clamp(quotient)
-}
-internal_shr_digit :: proc { internal_int_shr_digit, }
-
 /*
 	Shift right by a certain bit count with sign extension.
 */
@@ -2702,7 +2725,7 @@ internal_int_shl :: proc(dest, src: ^Int, bits: int, allocator := context.alloca
 		Shift by as many digits in the bit count as we have.
 	*/
 	if bits >= _DIGIT_BITS {
-		internal_shl_digit(dest, bits / _DIGIT_BITS) or_return
+		_private_int_shl_leg(dest, bits / _DIGIT_BITS) or_return
 	}
 
 	/*
@@ -2732,45 +2755,6 @@ internal_int_shl :: proc(dest, src: ^Int, bits: int, allocator := context.alloca
 }
 internal_shl :: proc { internal_int_shl, }
 
-
-/*
-	Shift left by `digits` * _DIGIT_BITS bits.
-*/
-internal_int_shl_digit :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) {
-	context.allocator = allocator
-
-	if digits <= 0 { return nil }
-
-	/*
-		No need to shift a zero.
-	*/
-	if #force_inline internal_is_zero(quotient) {
-		return nil
-	}
-
-	/*
-		Resize `quotient` to accomodate extra digits.
-	*/
-	#force_inline internal_grow(quotient, quotient.used + digits) or_return
-
-	/*
-		Increment the used by the shift amount then copy upwards.
-	*/
-
-	/*
-		Much like `int_shr_digit`, this is implemented using a sliding window,
-		except the window goes the other way around.
-	*/
-	#no_bounds_check for x := quotient.used; x > 0; x -= 1 {
-		quotient.digit[x+digits-1] = quotient.digit[x-1]
-	}
-
-	quotient.used += digits
-	mem.zero_slice(quotient.digit[:digits])
-	return nil
-}
-internal_shl_digit :: proc { internal_int_shl_digit, }
-
 /*
 	Count bits in an `Int`.
 	Assumes `a` not to be `nil` and to have been initialized.

+ 1 - 32
core/math/big/logical.odin

@@ -86,21 +86,6 @@ int_shr :: proc(dest, source: ^Int, bits: int, allocator := context.allocator) -
 }
 shr :: proc { int_shr, }
 
-/*
-	Shift right by `digits` * _DIGIT_BITS bits.
-*/
-int_shr_digit :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) {
-	/*
-		Check that `quotient` is usable.
-	*/
-	assert_if_nil(quotient)
-	context.allocator = allocator
-
-	internal_clear_if_uninitialized(quotient) or_return
-	return #force_inline internal_int_shr_digit(quotient, digits)
-}
-shr_digit :: proc { int_shr_digit, }
-
 /*
 	Shift right by a certain bit count with sign extension.
 */
@@ -124,20 +109,4 @@ int_shl :: proc(dest, src: ^Int, bits: int, allocator := context.allocator) -> (
 	internal_clear_if_uninitialized(dest, src) or_return
 	return #force_inline internal_int_shl(dest, src, bits)
 }
-shl :: proc { int_shl, }
-
-
-/*
-	Shift left by `digits` * _DIGIT_BITS bits.
-*/
-int_shl_digit :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) {
-	/*
-		Check that `quotient` is usable.
-	*/
-	assert_if_nil(quotient)
-	context.allocator = allocator
-
-	internal_clear_if_uninitialized(quotient) or_return
-	return #force_inline internal_int_shl_digit(quotient, digits)
-}
-shl_digit :: proc { int_shl_digit, };
+shl :: proc { int_shl, }

+ 93 - 25
core/math/big/private.odin

@@ -211,12 +211,12 @@ _private_int_mul_toom :: proc(dest, a, b: ^Int, allocator := context.allocator)
 	/*
 		P = b1*x^4+ S2*x^3+ S1*x^2+ a1*x + a0;
 	*/
-	internal_shl_digit(b1, 4 * B)               or_return
-	internal_shl_digit(S2, 3 * B)               or_return
+	_private_int_shl_leg(b1, 4 * B)             or_return
+	_private_int_shl_leg(S2, 3 * B)             or_return
 	internal_add(b1, b1, S2)                    or_return
-	internal_shl_digit(S1, 2 * B)               or_return
+	_private_int_shl_leg(S1, 2 * B)             or_return
 	internal_add(b1, b1, S1)                    or_return
-	internal_shl_digit(a1, 1 * B)               or_return
+	_private_int_shl_leg(a1, 1 * B)             or_return
 	internal_add(b1, b1, a1)                    or_return
 	internal_add(dest, b1, a0)                  or_return
 
@@ -317,8 +317,8 @@ _private_int_mul_karatsuba :: proc(dest, a, b: ^Int, allocator := context.alloca
 	/*
 		shift by B.
 	*/
-	internal_shl_digit(t1, B)       or_return /* t1 = (x0y0 + x1y1 - (x1-x0)*(y1-y0))<<B */
-	internal_shl_digit(x1y1, B * 2) or_return /* x1y1 = x1y1 << 2*B */
+	_private_int_shl_leg(t1, B)       or_return /* t1 = (x0y0 + x1y1 - (x1-x0)*(y1-y0))<<B */
+	_private_int_shl_leg(x1y1, B * 2) or_return /* x1y1 = x1y1 << 2*B */
 
 	internal_add(t1, x0y0, t1)      or_return /* t1 = x0y0 + t1 */
 	internal_add(dest, t1, x1y1)    or_return /* t1 = x0y0 + t1 + x1y1 */
@@ -588,7 +588,7 @@ _private_int_mul_balance :: proc(dest, a, b: ^Int, allocator := context.allocato
 		/*
 			Shift `tmp` to the correct position.
 		*/
-		internal_shl_digit(tmp, b_size * i)                          or_return
+		_private_int_shl_leg(tmp, b_size * i)                          or_return
 
 		/*
 			Add to output. No carry needed.
@@ -606,7 +606,7 @@ _private_int_mul_balance :: proc(dest, a, b: ^Int, allocator := context.allocato
 		internal_clamp(a0)
 
 		internal_mul(tmp, a0, b)                                     or_return
-		internal_shl_digit(tmp, b_size * i)                          or_return
+		_private_int_shl_leg(tmp, b_size * i)                          or_return
 		internal_add(r, r, tmp)                                      or_return
 	}
 
@@ -840,8 +840,8 @@ _private_int_sqr_karatsuba :: proc(dest, src: ^Int, allocator := context.allocat
 	/*
 		Shift by B.
 	*/
-	internal_shl_digit(t1, B) or_return
-	internal_shl_digit(x1x1, B * 2) or_return
+	_private_int_shl_leg(t1, B) or_return
+	_private_int_shl_leg(x1x1, B * 2) or_return
 	internal_add(t1, t1, x0x0) or_return
 	internal_add(dest, t1, x1x1) or_return
 
@@ -942,10 +942,10 @@ _private_int_sqr_toom :: proc(dest, src: ^Int, allocator := context.allocator) -
 	internal_sub(dest, dest, S0) or_return
 	/** \\P = S4*x^4 + S3*x^3 + S2*x^2 + S1*x + S0; */
 	/** P = a2*x^4 + a1*x^3 + b*x^2 + a0*x + S0; */
-	internal_shl_digit(  a2, 4 * B) or_return
-	internal_shl_digit(  a1, 3 * B) or_return
-	internal_shl_digit(dest, 2 * B) or_return
-	internal_shl_digit(  a0, 1 * B) or_return
+	_private_int_shl_leg(  a2, 4 * B) or_return
+	_private_int_shl_leg(  a1, 3 * B) or_return
+	_private_int_shl_leg(dest, 2 * B) or_return
+	_private_int_shl_leg(  a0, 1 * B) or_return
 
 	internal_add(a2, a2, a1) or_return
 	internal_add(dest, dest, a2) or_return
@@ -1069,7 +1069,7 @@ _private_int_div_school :: proc(quotient, remainder, numerator, denominator: ^In
 		y = y*b**{n-t}
 	*/
 
-	internal_shl_digit(y, n - t) or_return
+	_private_int_shl_leg(y, n - t) or_return
 
 	gte := internal_gte(x, y)
 	for gte {
@@ -1081,7 +1081,7 @@ _private_int_div_school :: proc(quotient, remainder, numerator, denominator: ^In
 	/*
 		Reset y by shifting it back down.
 	*/
-	internal_shr_digit(y, n - t)
+	_private_int_shr_leg(y, n - t)
 
 	/*
 		Step 3. for i from n down to (t + 1).
@@ -1146,7 +1146,7 @@ _private_int_div_school :: proc(quotient, remainder, numerator, denominator: ^In
 			Step 3.3 x = x - q{i-t-1} * y * b**{i-t-1}
 		*/
 		int_mul_digit(t1, y, q.digit[(i - t) - 1]) or_return
-		internal_shl_digit(t1, (i - t) - 1) or_return
+		_private_int_shl_leg(t1, (i - t) - 1) or_return
 		internal_sub(x, x, t1) or_return
 
 		/*
@@ -1154,7 +1154,7 @@ _private_int_div_school :: proc(quotient, remainder, numerator, denominator: ^In
 		*/
 		if x.sign == .Negative {
 			internal_copy(t1, y) or_return
-			internal_shl_digit(t1, (i - t) - 1) or_return
+			_private_int_shl_leg(t1, (i - t) - 1) or_return
 			internal_add(x, x, t1) or_return
 
 			q.digit[(i - t) - 1] = (q.digit[(i - t) - 1] - 1) & _MASK
@@ -1220,7 +1220,7 @@ _private_div_recursion :: proc(quotient, remainder, a, b: ^Int, allocator := con
 	/*
 		A1 = (R1 * beta^(2k)) + (A % beta^(2k)) - (Q1 * B0 * beta^k)
 	*/
-	internal_shl_digit(R1, 2 * k) or_return
+	_private_int_shl_leg(R1, 2 * k) or_return
 	internal_add(A1, R1, t) or_return
 	internal_mul(t, Q1, B0) or_return
 
@@ -1246,7 +1246,7 @@ _private_div_recursion :: proc(quotient, remainder, a, b: ^Int, allocator := con
 	/*
 		A2 = (R0*beta^k) +  (A1 % beta^k) - (Q0*B0)
 	*/
-	internal_shl_digit(R0, k) or_return
+	_private_int_shl_leg(R0, k) or_return
 	internal_add(A2, R0, t) or_return
 	internal_mul(t, Q0, B0) or_return
 	internal_sub(A2, A2, t) or_return
@@ -1262,7 +1262,7 @@ _private_div_recursion :: proc(quotient, remainder, a, b: ^Int, allocator := con
 	/*
 		Return q = (Q1*beta^k) + Q0, r = A2.
 	*/
-	internal_shl_digit(Q1, k) or_return
+	_private_int_shl_leg(Q1, k) or_return
 	internal_add(quotient, Q1, Q0) or_return
 
 	return internal_copy(remainder, A2)
@@ -1923,7 +1923,7 @@ _private_int_montgomery_reduce :: proc(x, n: ^Int, rho: DIGIT, allocator := cont
 		x = x/b**n.used.
 	*/
 	internal_clamp(x)
-	internal_shr_digit(x, n.used)
+	_private_int_shr_leg(x, n.used)
 
 	/*
 		if x >= n then x = x - n
@@ -2026,7 +2026,7 @@ _private_int_reduce :: proc(x, m, mu: ^Int, allocator := context.allocator) -> (
 	/*
 		q1 = x / b**(k-1)
 	*/
-	internal_shr_digit(q, um - 1)
+	_private_int_shr_leg(q, um - 1)
 
 	/*
 		According to HAC this optimization is ok.
@@ -2040,7 +2040,7 @@ _private_int_reduce :: proc(x, m, mu: ^Int, allocator := context.allocator) -> (
 	/*
 		q3 = q2 / b**(k+1)
 	*/
-	internal_shr_digit(q, um + 1)
+	_private_int_shr_leg(q, um + 1)
 
 	/*
 		x = x mod b**(k+1), quick (no division)
@@ -2062,7 +2062,7 @@ _private_int_reduce :: proc(x, m, mu: ^Int, allocator := context.allocator) -> (
 	*/
 	if internal_is_negative(x) {
 		internal_set(q, 1)                                           or_return
-		internal_shl_digit(q, um + 1)                                or_return
+		_private_int_shl_leg(q, um + 1)                                or_return
 		internal_add(x, x, q)                                        or_return
 	}
 
@@ -3192,6 +3192,74 @@ _private_copy_digits :: proc(dest, src: ^Int, digits: int, offset := int(0)) ->
 	return nil
 }
 
+
+/*
+	Shift left by `digits` * _DIGIT_BITS bits.
+*/
+_private_int_shl_leg :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) {
+	context.allocator = allocator
+
+	if digits <= 0 { return nil }
+
+	/*
+		No need to shift a zero.
+	*/
+	if #force_inline internal_is_zero(quotient) {
+		return nil
+	}
+
+	/*
+		Resize `quotient` to accomodate extra digits.
+	*/
+	#force_inline internal_grow(quotient, quotient.used + digits) or_return
+
+	/*
+		Increment the used by the shift amount then copy upwards.
+	*/
+
+	/*
+		Much like `_private_int_shr_leg`, this is implemented using a sliding window,
+		except the window goes the other way around.
+	*/
+	#no_bounds_check for x := quotient.used; x > 0; x -= 1 {
+		quotient.digit[x+digits-1] = quotient.digit[x-1]
+	}
+
+	quotient.used += digits
+	mem.zero_slice(quotient.digit[:digits])
+	return nil
+}
+
+/*
+	Shift right by `digits` * _DIGIT_BITS bits.
+*/
+_private_int_shr_leg :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) {
+	context.allocator = allocator
+
+	if digits <= 0 { return nil }
+
+	/*
+		If digits > used simply zero and return.
+	*/
+	if digits > quotient.used { return internal_zero(quotient) }
+
+	/*
+		Much like `int_shl_digit`, this is implemented using a sliding window,
+		except the window goes the other way around.
+
+		b-2 | b-1 | b0 | b1 | b2 | ... | bb |   ---->
+					/\                   |      ---->
+					 \-------------------/      ---->
+	*/
+
+	#no_bounds_check for x := 0; x < (quotient.used - digits); x += 1 {
+		quotient.digit[x] = quotient.digit[x + digits]
+	}
+	quotient.used -= digits
+	internal_zero_unused(quotient)
+	return internal_clamp(quotient)
+}
+
 /*	
 	========================    End of private procedures    =======================
 

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

@@ -42,9 +42,9 @@ rat_set_f64 :: proc(dst: ^Rat, f: f64, allocator := context.allocator) -> (err:
 	dst.a.sign = .Negative if f < 0 else .Zero_or_Positive
 	
 	if shift > 0 {
-		internal_int_shl_digit(&dst.b, shift) or_return
+		internal_int_shl(&dst.b, &dst.b, shift) or_return
 	} else {
-		internal_int_shl_digit(&dst.a, -shift) or_return
+		internal_int_shl(&dst.a, &dst.a, -shift) or_return
 	}
 	
 	return internal_rat_norm(dst)
@@ -112,14 +112,14 @@ rat_set_u64 :: proc(dst: ^Rat, x: u64, allocator := context.allocator) -> (err:
 	assert_if_nil(dst)
 	context.allocator = allocator
 	internal_set(&dst.a, x) or_return
-	internal_set(&dst.a, 1) or_return
+	internal_set(&dst.b, 1) or_return
 	return
 }
 rat_set_i64 :: proc(dst: ^Rat, x: i64, allocator := context.allocator) -> (err: Error) {
 	assert_if_nil(dst)
 	context.allocator = allocator
 	internal_set(&dst.a, x) or_return
-	internal_set(&dst.a, 1) or_return
+	internal_set(&dst.b, 1) or_return
 	return
 }
 
@@ -265,7 +265,7 @@ rat_mul_rat :: proc(dst, x, y: ^Rat, allocator := context.allocator) -> (err: Er
 		return
 	}
 	
-	int_sub(&dst.a, &x.a, &y.a)                or_return
+	int_mul(&dst.a, &x.a, &y.a)                or_return
 	internal_int_mul_denom(&dst.b, &x.b, &y.b) or_return
 	return internal_rat_norm(dst)
 }
@@ -389,9 +389,9 @@ internal_rat_to_float :: proc($T: typeid, z: ^Rat, allocator := context.allocato
 	internal_int_abs(b2, b) or_return
 	
 	if shift := MSIZE2 - exp; shift > 0 {
-		internal_int_shl_digit(a2, shift) or_return
-	} else {
-		internal_int_shl_digit(b2, -shift) or_return
+		internal_int_shl(a2, a2, shift) or_return
+	} else if shift < 0 {
+		internal_int_shl(b2, b2, -shift) or_return
 	}
 	
 	q, r := &Int{}, &Int{}

+ 2 - 2
core/math/linalg/extended.odin

@@ -103,10 +103,10 @@ max :: proc{max_single, max_double, max_triple}
 abs :: proc(a: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
 	when IS_ARRAY(T) {
 		for i in 0..<len(T) {
-			out[i] = builtin.abs(a[i])
+			out[i] = auto_cast builtin.abs(a[i])
 		}
 	} else {
-		out = builtin.abs(a)
+		out = auto_cast builtin.abs(a)
 	}
 	return
 }

+ 1 - 0
core/math/math.odin

@@ -1196,6 +1196,7 @@ sum :: proc "contextless" (x: $T/[]$E) -> (res: E)
 
 prod :: proc "contextless" (x: $T/[]$E) -> (res: E)
 	where intrinsics.type_is_numeric(E) {
+	res = 1
 	for i in x {
 		res *= i
 	}

+ 34 - 42
core/mem/virtual/virtual_linux.odin

@@ -4,64 +4,56 @@ package mem_virtual
 
 import "core:c"
 import "core:intrinsics"
+import "core:sys/unix"
 
-when ODIN_ARCH == "amd64" {
-	SYS_mmap     :: 9
-	SYS_mprotect :: 10
-	SYS_munmap   :: 11
-	SYS_madvise  :: 28
-	
-	PROT_NONE  :: 0x0
-	PROT_READ  :: 0x1
-	PROT_WRITE :: 0x2
-	PROT_EXEC  :: 0x4
-	PROT_GROWSDOWN :: 0x01000000
-	PROT_GROWSUP :: 0x02000000
+PROT_NONE  :: 0x0
+PROT_READ  :: 0x1
+PROT_WRITE :: 0x2
+PROT_EXEC  :: 0x4
+PROT_GROWSDOWN :: 0x01000000
+PROT_GROWSUP :: 0x02000000
 
-	MAP_FIXED     :: 0x1
-	MAP_PRIVATE   :: 0x2
-	MAP_SHARED    :: 0x4
-	MAP_ANONYMOUS :: 0x20
-	
-	MADV_NORMAL      :: 0
-	MADV_RANDOM      :: 1
-	MADV_SEQUENTIAL  :: 2
-	MADV_WILLNEED    :: 3
-	MADV_DONTNEED    :: 4
-	MADV_FREE        :: 8
-	MADV_REMOVE      :: 9
-	MADV_DONTFORK    :: 10
-	MADV_DOFORK      :: 11
-	MADV_MERGEABLE   :: 12
-	MADV_UNMERGEABLE :: 13
-	MADV_HUGEPAGE    :: 14
-	MADV_NOHUGEPAGE  :: 15
-	MADV_DONTDUMP    :: 16
-	MADV_DODUMP      :: 17
-	MADV_WIPEONFORK  :: 18
-	MADV_KEEPONFORK  :: 19
-	MADV_HWPOISON    :: 100
-} else {
-	#panic("Unsupported architecture")
-}
+MAP_FIXED     :: 0x1
+MAP_PRIVATE   :: 0x2
+MAP_SHARED    :: 0x4
+MAP_ANONYMOUS :: 0x20
+
+MADV_NORMAL      :: 0
+MADV_RANDOM      :: 1
+MADV_SEQUENTIAL  :: 2
+MADV_WILLNEED    :: 3
+MADV_DONTNEED    :: 4
+MADV_FREE        :: 8
+MADV_REMOVE      :: 9
+MADV_DONTFORK    :: 10
+MADV_DOFORK      :: 11
+MADV_MERGEABLE   :: 12
+MADV_UNMERGEABLE :: 13
+MADV_HUGEPAGE    :: 14
+MADV_NOHUGEPAGE  :: 15
+MADV_DONTDUMP    :: 16
+MADV_DODUMP      :: 17
+MADV_WIPEONFORK  :: 18
+MADV_KEEPONFORK  :: 19
+MADV_HWPOISON    :: 100
 
 mmap :: proc "contextless" (addr: rawptr, length: uint, prot: c.int, flags: c.int, fd: c.int, offset: uintptr) -> rawptr {
-	res := intrinsics.syscall(SYS_mmap, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), offset)
+	res := intrinsics.syscall(unix.SYS_mmap, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), offset)
 	return rawptr(res)
 }
 
 munmap :: proc "contextless" (addr: rawptr, length: uint) -> c.int {
-	res := intrinsics.syscall(SYS_munmap, uintptr(addr), uintptr(length))
+	res := intrinsics.syscall(unix.SYS_munmap, uintptr(addr), uintptr(length))
 	return c.int(res)
 }
 
 mprotect :: proc "contextless" (addr: rawptr, length: uint, prot: c.int) -> c.int {
-	res := intrinsics.syscall(SYS_mprotect, uintptr(addr), uintptr(length), uint(prot))
+	res := intrinsics.syscall(unix.SYS_mprotect, uintptr(addr), uintptr(length), uint(prot))
 	return c.int(res)
 }
 
 madvise :: proc "contextless" (addr: rawptr, length: uint, advice: c.int) -> c.int {
-	res := intrinsics.syscall(SYS_madvise, uintptr(addr), uintptr(length), uintptr(advice))
+	res := intrinsics.syscall(unix.SYS_madvise, uintptr(addr), uintptr(length), uintptr(advice))
 	return c.int(res)
 }
 

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

@@ -711,6 +711,7 @@ Union_Type :: struct {
 	poly_params:   ^Field_List,
 	align:         ^Expr,
 	is_maybe:      bool,
+	is_no_nil:     bool,
 	where_token:   tokenizer.Token,
 	where_clauses: []^Expr,
 	variants:      []^Expr,

+ 24 - 3
core/odin/parser/parser.odin

@@ -47,6 +47,8 @@ Parser :: struct {
 
 	fix_count: int,
 	fix_prev_pos: tokenizer.Pos,
+
+	peeking: bool,
 }
 
 MAX_FIX_COUNT :: 10
@@ -209,7 +211,12 @@ parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool {
 
 peek_token_kind :: proc(p: ^Parser, kind: tokenizer.Token_Kind, lookahead := 0) -> (ok: bool) {
 	prev_parser := p^
-	defer p^ = prev_parser
+	p.peeking = true
+
+	defer {
+		p^ = prev_parser
+		p.peeking = false
+	}
 
 	p.tok.err = nil
 	for i := 0; i <= lookahead; i += 1 {
@@ -222,7 +229,12 @@ peek_token_kind :: proc(p: ^Parser, kind: tokenizer.Token_Kind, lookahead := 0)
 
 peek_token :: proc(p: ^Parser, lookahead := 0) -> (tok: tokenizer.Token) {
 	prev_parser := p^
-	defer p^ = prev_parser
+	p.peeking = true
+
+	defer {
+		p^ = prev_parser
+		p.peeking = false
+	}
 
 	p.tok.err = nil
 	for i := 0; i <= lookahead; i += 1 {
@@ -305,7 +317,7 @@ consume_comment_group :: proc(p: ^Parser, n: int) -> (comments: ^ast.Comment_Gro
 		append(&list, comment)
 	}
 
-	if len(list) > 0 {
+	if len(list) > 0 && !p.peeking {
 		comments = ast.new(ast.Comment_Group, list[0].pos, end_pos(list[len(list)-1]))
 		comments.list = list[:]
 		append(&p.file.comments, comments)
@@ -876,6 +888,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
 			error(p, body.pos, "the body of a 'do' must be on the same line as the 'for' token")
 		}
 	} else {
+		allow_token(p, .Semicolon)
 		body = parse_body(p)
 	}
 
@@ -2588,6 +2601,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 		poly_params: ^ast.Field_List
 		align:       ^ast.Expr
 		is_maybe:    bool
+		is_no_nil:   bool
 
 		if allow_token(p, .Open_Paren) {
 			param_count: int
@@ -2614,6 +2628,11 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 					error(p, tag.pos, "duplicate union tag '#%s'", tag.text)
 				}
 				is_maybe = true
+			case "no_nil":
+				if is_no_nil {
+					error(p, tag.pos, "duplicate union tag '#%s'", tag.text)
+				}
+				is_no_nil = true
 			case:
 				error(p, tag.pos, "invalid union tag '#%s", tag.text)
 			}
@@ -2657,6 +2676,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 		ut.where_token   = where_token
 		ut.where_clauses = where_clauses
 		ut.is_maybe      = is_maybe
+		ut.is_no_nil     = is_no_nil
 
 		return ut
 
@@ -2825,6 +2845,7 @@ is_literal_type :: proc(expr: ^ast.Expr) -> bool {
 		ast.Dynamic_Array_Type,
 		ast.Map_Type,
 		ast.Bit_Set_Type,
+		ast.Matrix_Type,
 		ast.Call_Expr:
 		return true
 	}

+ 64 - 9
core/os/file_windows.odin

@@ -2,6 +2,7 @@ package os
 
 import win32 "core:sys/windows"
 import "core:intrinsics"
+import "core:unicode/utf16"
 
 is_path_separator :: proc(c: byte) -> bool {
 	return c == '/' || c == '\\'
@@ -96,26 +97,78 @@ write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
 	return int(total_write), ERROR_NONE
 }
 
+@(private="file")
+read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) {
+	if len(b) == 0 {
+		return 0, 0
+	}
+	
+	BUF_SIZE :: 386
+	buf16: [BUF_SIZE]u16
+	buf8: [4*BUF_SIZE]u8
+	
+	for n < len(b) && err == 0 {
+		max_read := u32(min(BUF_SIZE, len(b)/4))
+		
+		single_read_length: u32
+		ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil)
+		if !ok {
+			err = Errno(win32.GetLastError())
+		}
+		
+		buf8_len := utf16.decode_to_utf8(buf8[:], buf16[:single_read_length])
+		src := buf8[:buf8_len]
+		
+		ctrl_z := false
+		for i := 0; i < len(src) && n+i < len(b); i += 1 {
+			x := src[i]
+			if x == 0x1a { // ctrl-z
+				ctrl_z = true
+				break
+			}
+			b[n] = x
+			n += 1
+		}
+		if ctrl_z || single_read_length < len(buf16) {
+			break
+		}
+	}
+	
+	return
+}
+
 read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
 	if len(data) == 0 {
 		return 0, ERROR_NONE
 	}
+	
+	handle := win32.HANDLE(fd)
+	
+	m: u32
+	is_console := win32.GetConsoleMode(handle, &m)
 
 	single_read_length: win32.DWORD
-	total_read: i64
-	length := i64(len(data))
+	total_read: int
+	length := len(data)
 
-	for total_read < length {
-		remaining := length - total_read
-		to_read := min(win32.DWORD(remaining), MAX_RW)
+	to_read := min(win32.DWORD(length), MAX_RW)
 
-		e := win32.ReadFile(win32.HANDLE(fd), &data[total_read], to_read, &single_read_length, nil)
-		if single_read_length <= 0 || !e {
-			err := Errno(win32.GetLastError())
+	e: win32.BOOL
+	if is_console {
+		n, err := read_console(handle, data[total_read:][:to_read])
+		total_read += n
+		if err != 0 {
 			return int(total_read), err
 		}
-		total_read += i64(single_read_length)
+	} else {
+		e = win32.ReadFile(handle, &data[total_read], to_read, &single_read_length, nil)
 	}
+	if single_read_length <= 0 || !e {
+		err := Errno(win32.GetLastError())
+		return int(total_read), err
+	}
+	total_read += int(single_read_length)
+	
 	return int(total_read), ERROR_NONE
 }
 
@@ -172,6 +225,8 @@ pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
 		Offset = u32(offset),
 	}
 
+	// TODO(bill): Determine the correct behaviour for consoles
+
 	h := win32.HANDLE(fd)
 	done: win32.DWORD
 	if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) {

+ 40 - 21
core/os/os.odin

@@ -55,6 +55,25 @@ write_encoded_rune :: proc(fd: Handle, r: rune) {
 	write_byte(fd, '\'')
 }
 
+read_at_least :: proc(fd: Handle, buf: []byte, min: int) -> (n: int, err: Errno) {
+	if len(buf) < min {
+		return 0, -1
+	}
+	for n < min && err == 0 {
+		nn: int
+		nn, err = read(fd, buf[n:])
+		n += nn
+	}
+	if n >= min {
+		err = 0
+	}
+	return
+}
+
+read_full :: proc(fd: Handle, buf: []byte) -> (n: int, err: Errno) {
+	return read_at_least(fd, buf, len(buf))
+}
+
 
 file_size_from_path :: proc(path: string) -> i64 {
 	fd, err := open(path, O_RDONLY, 0)
@@ -85,27 +104,27 @@ read_entire_file_from_filename :: proc(name: string, allocator := context.alloca
 read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator) -> (data: []byte, success: bool) {
 	context.allocator = allocator
 
-    length: i64
-    err: Errno
-    if length, err = file_size(fd); err != 0 {
-        return nil, false
-    }
-
-    if length <= 0 {
-        return nil, true
-    }
-
-    data = make([]byte, int(length), allocator)
-    if data == nil {
-        return nil, false
-    }
-
-    bytes_read, read_err := read(fd, data)
-    if read_err != ERROR_NONE {
-        delete(data)
-        return nil, false
-    }
-    return data[:bytes_read], true
+	length: i64
+	err: Errno
+	if length, err = file_size(fd); err != 0 {
+	    return nil, false
+	}
+
+	if length <= 0 {
+		return nil, true
+	}
+
+	data = make([]byte, int(length), allocator)
+	if data == nil {
+	return nil, false
+	}
+
+	bytes_read, read_err := read_full(fd, data)
+	if read_err != ERROR_NONE {
+		delete(data)
+		return nil, false
+	}
+	return data[:bytes_read], true
 }
 
 read_entire_file :: proc {

+ 2 - 3
core/os/os_linux.odin

@@ -8,6 +8,7 @@ import "core:strings"
 import "core:c"
 import "core:strconv"
 import "core:intrinsics"
+import "core:sys/unix"
 
 Handle    :: distinct i32
 File_Time :: distinct u64
@@ -265,8 +266,6 @@ X_OK :: 1 // Test for execute permission
 W_OK :: 2 // Test for write permission
 R_OK :: 4 // Test for read permission
 
-SYS_GETTID :: 186
-
 foreign libc {
 	@(link_name="__errno_location") __errno_location    :: proc() -> ^int ---
 
@@ -594,7 +593,7 @@ exit :: proc "contextless" (code: int) -> ! {
 }
 
 current_thread_id :: proc "contextless" () -> int {
-	return cast(int)intrinsics.syscall(SYS_GETTID)
+	return unix.sys_gettid()
 }
 
 dlopen :: proc(filename: string, flags: int) -> rawptr {

+ 12 - 2
core/runtime/internal.odin

@@ -2,13 +2,15 @@ package runtime
 
 import "core:intrinsics"
 
+@(private="file")
+IS_WASM :: ODIN_ARCH == "wasm32" || ODIN_ARCH == "wasm64"
+
 @(private)
 RUNTIME_LINKAGE :: "strong" when (
 	(ODIN_USE_SEPARATE_MODULES || 
 	ODIN_BUILD_MODE == "dynamic" || 
 	!ODIN_NO_CRT) &&
-	!(ODIN_ARCH == "wasm32" || 
-	  ODIN_ARCH == "wasm64")) else "internal"
+	!IS_WASM) else "internal"
 RUNTIME_REQUIRE :: true
 
 
@@ -752,6 +754,9 @@ extendhfsf2 :: proc "c" (value: u16) -> f32 {
 
 @(link_name="__floattidf", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
 floattidf :: proc "c" (a: i128) -> f64 {
+when IS_WASM {
+	return 0
+} else {
 	DBL_MANT_DIG :: 53
 	if a == 0 {
 		return 0.0
@@ -791,10 +796,14 @@ floattidf :: proc "c" (a: i128) -> f64 {
 	fb[0] = u32(a)                           // mantissa-low
 	return transmute(f64)fb
 }
+}
 
 
 @(link_name="__floattidf_unsigned", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
 floattidf_unsigned :: proc "c" (a: u128) -> f64 {
+when IS_WASM {
+	return 0
+} else {
 	DBL_MANT_DIG :: 53
 	if a == 0 {
 		return 0.0
@@ -832,6 +841,7 @@ floattidf_unsigned :: proc "c" (a: u128) -> f64 {
 	fb[0] = u32(a)                           // mantissa-low
 	return transmute(f64)fb
 }
+}
 
 
 

+ 29 - 12
core/runtime/procs_wasm32.odin

@@ -1,23 +1,40 @@
 //+build wasm32
 package runtime
 
+@(private="file")
+ti_int :: struct #raw_union {
+	using s: struct { lo, hi: u64 },
+	all: i128,
+}
+
 @(link_name="__ashlti3", linkage="strong")
-__ashlti3 :: proc "c" (a: i64, b_: i32) -> i64 {
-	/*
+__ashlti3 :: proc "c" (a: i128, b_: u32) -> i128 {
+	bits_in_dword :: size_of(u32)*8
 	b := u32(b_)
-	input := transmute([2]i32)a
-	result: [2]i32
-	if b & 32 != 0 {
-		result[0] = 0
-		result[1] = input[0] << (b - 32)
+	
+	input, result: ti_int
+	input.all = a
+	if b & bits_in_dword != 0 {
+		result.lo = 0
+		result.hi = input.lo << (b-bits_in_dword)
 	} else {
 		if b == 0 {
 			return a
 		}
-		result[0] = input[0]<<b
-		result[1] = (input[1]<<b) | (input[0]>>(32-b))
+		result.lo = input.lo<<b
+		result.hi = (input.hi<<b) | (input.lo>>(bits_in_dword-b))
 	}
-	return transmute(i64)result
-	*/
-	return 0
+	return result.all
+}
+
+
+@(link_name="__multi3", linkage="strong")
+__multi3 :: proc "c" (a, b: i128) -> i128 {
+	x, y, r: ti_int
+	
+	x.all = a
+	y.all = b
+	r.all = i128(x.lo * y.lo) // TODO this is incorrect
+	r.hi += x.hi*y.lo + x.lo*y.hi
+	return r.all
 }

+ 13 - 0
core/runtime/procs_windows_amd64.asm

@@ -0,0 +1,13 @@
+global __chkstk
+global _tls_index
+global _fltused
+
+section .data
+	_tls_index: dd 0
+	_fltused:   dd 0x9875
+	
+
+section .text
+__chkstk: ; proc "c" (rawptr)
+	; TODO implement correctly
+	ret

+ 2 - 9
core/runtime/procs_windows_amd64.odin

@@ -20,13 +20,6 @@ windows_trap_type_assertion :: proc "contextless" () -> ! {
 }
 
 when ODIN_NO_CRT {
-	@(private, export, link_name="_tls_index")
-	_tls_index: u32
-
-	@(private, export, link_name="_fltused")
-	_fltused: i32 = 0x9875
-	
-	@(private, export, link_name="__chkstk")
-	__chkstk :: proc "c" (rawptr) {
-	}
+	@(require)
+	foreign import crt_lib "procs_windows_amd64.asm"
 }

+ 4 - 3
core/slice/slice.odin

@@ -185,7 +185,7 @@ concatenate :: proc(a: []$T/[]$E, allocator := context.allocator) -> (res: T) {
 	return
 }
 
-// copies slice into a new dynamic array
+// copies a slice into a new slice
 clone :: proc(a: $T/[]$E, allocator := context.allocator) -> []E {
 	d := make([]E, len(a), allocator)
 	copy(d[:], a)
@@ -194,11 +194,12 @@ clone :: proc(a: $T/[]$E, allocator := context.allocator) -> []E {
 
 
 // copies slice into a new dynamic array
-to_dynamic :: proc(a: $T/[]$E, allocator := context.allocator) -> [dynamic]E {
+clone_to_dynamic :: proc(a: $T/[]$E, allocator := context.allocator) -> [dynamic]E {
 	d := make([dynamic]E, len(a), allocator)
 	copy(d[:], a)
 	return d
 }
+to_dynamic :: clone_to_dynamic
 
 // Converts slice into a dynamic array without cloning or allocating memory
 into_dynamic :: proc(a: $T/[]$E) -> [dynamic]E {
@@ -272,7 +273,7 @@ get_ptr :: proc(array: $T/[]$E, index: int) -> (value: ^E, ok: bool) {
 	return
 }
 
-as_ptr :: proc(array: $T/[]$E) -> ^E {
+as_ptr :: proc(array: $T/[]$E) -> [^]E {
 	return raw_data(array)
 }
 

+ 33 - 0
core/sort/map.odin

@@ -0,0 +1,33 @@
+package sort
+
+import "core:intrinsics"
+import "core:runtime"
+import "core:slice"
+
+map_entries_by_key :: proc(m: ^$M/map[$K]$V, loc := #caller_location) where intrinsics.type_is_ordered(K) {
+	Entry :: struct {
+		hash:  uintptr,
+		next:  int,
+		key:   K,
+		value: V,
+	}
+	
+	header := runtime.__get_map_header(m)
+	entries := (^[dynamic]Entry)(&header.m.entries)
+	slice.sort_by_key(entries[:], proc(e: Entry) -> K { return e.key })
+	runtime.__dynamic_map_reset_entries(header, loc)
+}
+
+map_entries_by_value :: proc(m: ^$M/map[$K]$V, loc := #caller_location) where intrinsics.type_is_ordered(V) {
+	Entry :: struct {
+		hash:  uintptr,
+		next:  int,
+		key:   K,
+		value: V,
+	}
+	
+	header := runtime.__get_map_header(m)
+	entries := (^[dynamic]Entry)(&header.m.entries)
+	slice.sort_by_key(entries[:], proc(e: Entry) -> V { return e.value })
+	runtime.__dynamic_map_reset_entries(header, loc)
+}

+ 3 - 1
core/strconv/strconv.odin

@@ -882,7 +882,9 @@ unquote_string :: proc(lit: string, allocator := context.allocator) -> (res: str
 		return -1
 	}
 
-	assert(len(lit) >= 2)
+	if len(lit) < 2 {
+		return
+	}
 	if lit[0] == '`' {
 		return lit[1:len(lit)-1], false, true
 	}

+ 4 - 5
core/strings/strings.odin

@@ -6,9 +6,8 @@ import "core:unicode"
 import "core:unicode/utf8"
 
 clone :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> string {
-	c := make([]byte, len(s)+1, allocator, loc)
+	c := make([]byte, len(s), allocator, loc)
 	copy(c, s)
-	c[len(s)] = 0
 	return string(c[:len(s)])
 }
 
@@ -504,8 +503,8 @@ index_any :: proc(s, chars: string) -> int {
 		}
 	}
 
-	for c in chars {
-		if i := index_rune(s, c); i >= 0 {
+	for c, i in s {
+		if index_rune(chars, c) >= 0 {
 			return i
 		}
 	}
@@ -1288,7 +1287,7 @@ fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.alloc
 	}
 
 	if start >= 0 {
-		append(&substrings, s[start : end])
+		append(&substrings, s[start : len(s)])
 	}
 
 	return substrings[:]

+ 2 - 1
core/sync/sync2/futex_linux.odin

@@ -5,6 +5,7 @@ package sync2
 import "core:c"
 import "core:time"
 import "core:intrinsics"
+import "core:sys/unix"
 
 FUTEX_WAIT :: 0
 FUTEX_WAKE :: 1
@@ -34,7 +35,7 @@ get_errno :: proc(r: int) -> int {
 }
 
 internal_futex :: proc(f: ^Futex, op: c.int, val: u32, timeout: rawptr) -> int {
-	code := int(intrinsics.syscall(202, uintptr(f), uintptr(op), uintptr(val), uintptr(timeout), 0, 0))
+	code := int(intrinsics.syscall(unix.SYS_futex, uintptr(f), uintptr(op), uintptr(val), uintptr(timeout), 0, 0))
 	return get_errno(code)
 }
 

+ 2 - 3
core/sync/sync2/primitives_linux.odin

@@ -2,9 +2,8 @@
 //+private
 package sync2
 
-import "core:intrinsics"
+import "core:sys/unix"
 
 _current_thread_id :: proc "contextless" () -> int {
-	SYS_GETTID :: 186
-	return int(intrinsics.syscall(SYS_GETTID))
+	return unix.sys_gettid()
 }

+ 1 - 3
core/sync/sync_linux.odin

@@ -1,11 +1,9 @@
 package sync
 
 import "core:sys/unix"
-import "core:intrinsics"
 
 current_thread_id :: proc "contextless" () -> int {
-	SYS_GETTID :: 186
-	return int(intrinsics.syscall(SYS_GETTID))
+	return unix.sys_gettid()
 }
 
 

+ 60 - 0
core/sys/unix/syscalls_linux.odin

@@ -0,0 +1,60 @@
+package unix
+
+import "core:intrinsics"
+
+// Linux has inconsistent system call numbering across architectures,
+// for largely historical reasons.  This attempts to provide a unified
+// Odin-side interface for system calls that are required for the core
+// library to work.
+
+// For authorative system call numbers, the following files in the kernel
+// source can be used:
+//
+//  amd64: arch/x86/entry/syscalls/syscall_64.tbl
+//  arm64: include/uapi/asm-generic/unistd.h
+//  386: arch/x86/entry/syscalls/sycall_32.tbl
+//  arm: arch/arm/tools/syscall.tbl
+
+when ODIN_ARCH == "amd64" {
+	SYS_mmap : uintptr : 9
+	SYS_mprotect : uintptr : 10
+	SYS_munmap : uintptr : 11
+	SYS_madvise : uintptr : 28
+	SYS_futex : uintptr : 202
+	SYS_gettid : uintptr : 186
+	SYS_getrandom : uintptr : 318
+} else when ODIN_ARCH == "arm64" {
+	SYS_mmap : uintptr : 222
+	SYS_mprotect : uintptr : 226
+	SYS_munmap : uintptr : 215
+	SYS_madvise : uintptr : 233
+	SYS_futex : uintptr : 98
+	SYS_gettid : uintptr : 178
+	SYS_getrandom : uintptr : 278
+} else when ODIN_ARCH == "386" {
+	SYS_mmap : uintptr : 192 // 90 is "sys_old_mmap", we want mmap2
+	SYS_mprotect : uintptr : 125
+	SYS_munmap : uintptr : 91
+	SYS_madvise : uintptr : 219
+	SYS_futex : uintptr : 240
+	SYS_gettid : uintptr : 224
+	SYS_getrandom : uintptr : 355
+} else when ODIN_ARCH == "arm" {
+	SYS_mmap : uintptr : 192 // 90 is "sys_old_mmap", we want mmap2
+	SYS_mprotect : uintptr : 125
+	SYS_munmap: uintptr : 91
+	SYS_madvise: uintptr : 220
+	SYS_futex : uintptr : 240
+	SYS_gettid : uintptr: 224
+	SYS_getrandom : uintptr : 384
+} else {
+	#panic("Unsupported architecture")
+}
+
+sys_gettid :: proc "contextless" () -> int {
+	return cast(int)intrinsics.syscall(SYS_gettid)
+}
+
+sys_getrandom :: proc "contextless" (buf: ^byte, buflen: int, flags: uint) -> int {
+	return cast(int)intrinsics.syscall(SYS_getrandom, buf, cast(uintptr)(buflen), cast(uintptr)(flags))
+}

+ 48 - 0
core/unicode/utf16/utf16.odin

@@ -1,5 +1,7 @@
 package utf16
 
+import "core:unicode/utf8"
+
 REPLACEMENT_CHAR :: '\ufffd'
 MAX_RUNE         :: '\U0010ffff'
 
@@ -80,3 +82,49 @@ encode_string :: proc(d: []u16, s: string) -> int {
 	}
 	return n
 }
+
+decode :: proc(d: []rune, s: []u16) -> (n: int) {
+	for i := 0; i < len(s); i += 1 {
+		if n >= len(d) {
+			return
+		}
+		
+		r := rune(REPLACEMENT_CHAR)
+		
+		switch c := s[i]; {
+		case c < _surr1, _surr3 <= c:
+			r = rune(c)
+		case _surr1 <= c && c < _surr2 && i+1 < len(s) && 
+			_surr2 <= s[i+1] && s[i+1] < _surr3:
+			r = decode_surrogate_pair(rune(c), rune(s[i+1]))
+			i += 1
+		}
+		d[n] = r
+		
+		n += 1
+	}
+	return
+}
+
+
+decode_to_utf8 :: proc(d: []byte, s: []u16) -> (n: int) {
+	for i := 0; i < len(s); i += 1 {
+		if n >= len(d) {
+			return
+		}
+		r := rune(REPLACEMENT_CHAR)
+		
+		switch c := s[i]; {
+		case c < _surr1, _surr3 <= c:
+			r = rune(c)
+		case _surr1 <= r && r < _surr2 && i+1 < len(s) && 
+			_surr2 <= s[i+1] && s[i+1] < _surr3:
+			r = decode_surrogate_pair(rune(r), rune(s[i+1]))
+			i += 1
+		}
+		
+		b, w := utf8.encode_rune(rune(r))
+		n += copy(d[n:], b[:w])
+	}
+	return
+}

+ 27 - 0
src/build_settings.cpp

@@ -196,6 +196,7 @@ struct BuildContext {
 	bool   has_resource;
 	String link_flags;
 	String extra_linker_flags;
+	String extra_assembler_flags;
 	String microarch;
 	BuildModeKind build_mode;
 	bool   generate_docs;
@@ -299,6 +300,14 @@ gb_global TargetMetrics target_linux_amd64 = {
 	str_lit("x86_64-pc-linux-gnu"),
 	str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
 };
+gb_global TargetMetrics target_linux_arm64 = {
+	TargetOs_linux,
+	TargetArch_arm64,
+	8,
+	16,
+	str_lit("aarch64-linux-elf"),
+	str_lit("e-m:e-i8:8:32-i16:32-i64:64-i128:128-n32:64-S128"),
+};
 
 gb_global TargetMetrics target_darwin_amd64 = {
 	TargetOs_darwin,
@@ -393,6 +402,7 @@ gb_global NamedTargetMetrics named_targets[] = {
 	{ str_lit("essence_amd64"),       &target_essence_amd64  },
 	{ str_lit("linux_386"),           &target_linux_386      },
 	{ str_lit("linux_amd64"),         &target_linux_amd64    },
+	{ str_lit("linux_arm64"),         &target_linux_arm64    },
 	{ str_lit("windows_386"),         &target_windows_386    },
 	{ str_lit("windows_amd64"),       &target_windows_amd64  },
 	{ str_lit("freebsd_386"),         &target_freebsd_386    },
@@ -821,6 +831,18 @@ bool show_error_line(void) {
 	return build_context.show_error_line;
 }
 
+bool has_asm_extension(String const &path) {
+	String ext = path_extension(path);
+	if (ext == ".asm") {
+		return true;
+	} else if (ext == ".s") {
+		return true;
+	} else if (ext == ".S") {
+		return true;
+	}
+	return false;
+}
+
 
 void init_build_context(TargetMetrics *cross_target) {
 	BuildContext *bc = &build_context;
@@ -867,6 +889,8 @@ void init_build_context(TargetMetrics *cross_target) {
 			#endif
 		#elif defined(GB_SYSTEM_FREEBSD)
 			metrics = &target_freebsd_amd64;
+		#elif defined(GB_CPU_ARM)
+			metrics = &target_linux_arm64;
 		#else
 			metrics = &target_linux_amd64;
 		#endif
@@ -946,6 +970,9 @@ void init_build_context(TargetMetrics *cross_target) {
 		case TargetOs_darwin:
 			bc->link_flags = str_lit("-arch arm64 ");
 			break;
+		case TargetOs_linux:
+			bc->link_flags = str_lit("-arch aarch64 ");
+			break;
 		}
 	} else if (is_arch_wasm()) {
 		gbString link_flags = gb_string_make(heap_allocator(), " ");

+ 10 - 3
src/check_builtin.cpp

@@ -1858,7 +1858,14 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 				f64 r = operand->value.value_complex->real;
 				f64 i = operand->value.value_complex->imag;
 				operand->value = exact_value_float(gb_sqrt(r*r + i*i));
-
+				break;
+			}
+			case ExactValue_Quaternion: {
+				f64 r = operand->value.value_quaternion->real;
+				f64 i = operand->value.value_quaternion->imag;
+				f64 j = operand->value.value_quaternion->jmag;
+				f64 k = operand->value.value_quaternion->kmag;
+				operand->value = exact_value_float(gb_sqrt(r*r + i*i + j*j + k*k));
 				break;
 			}
 			default:
@@ -1877,10 +1884,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 			}
 		}
 
-		if (is_type_complex(operand->type)) {
+		if (is_type_complex_or_quaternion(operand->type)) {
 			operand->type = base_complex_elem_type(operand->type);
 		}
-		GB_ASSERT(!is_type_complex(operand->type));
+		GB_ASSERT(!is_type_complex_or_quaternion(operand->type));
 
 		break;
 	}

+ 32 - 4
src/check_expr.cpp

@@ -649,6 +649,13 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
 			return 4;
 		}
 	}
+	
+	if (is_type_complex_or_quaternion(dst)) {
+		Type *elem = base_complex_elem_type(dst);
+		if (are_types_identical(elem, base_type(src))) {
+			return 5;
+		}
+	}
 
 	if (is_type_array(dst)) {
 		Type *elem = base_array_type(dst);
@@ -1837,11 +1844,12 @@ void check_cast_error_suggestion(CheckerContext *c, Operand *o, Type *type) {
 }
 
 
-void check_is_expressible(CheckerContext *ctx, Operand *o, Type *type) {
+bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *type) {
 	GB_ASSERT(o->mode == Addressing_Constant);
 	ExactValue out_value = o->value;
 	if (is_type_constant_type(type) && check_representable_as_constant(ctx, o->value, type, &out_value)) {
 		o->value = out_value;
+		return true;
 	} else {
 		o->value = out_value;
 
@@ -1866,6 +1874,7 @@ void check_is_expressible(CheckerContext *ctx, Operand *o, Type *type) {
 			error(o->expr, "Cannot convert '%s' to '%s' from '%s", a, b, c);
 			check_assignment_error_suggestion(ctx, o, type);
 		}
+		return false;
 	}
 }
 
@@ -3204,6 +3213,16 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint
 	x->mode = Addressing_Value;
 }
 
+Operand make_operand_from_node(Ast *node) {
+	GB_ASSERT(node != nullptr);
+	Operand x = {};
+	x.expr  = node;
+	x.mode  = node->tav.mode;
+	x.type  = node->tav.type;
+	x.value = node->tav.value;
+	return x;
+}
+
 
 void update_untyped_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) {
 	GB_ASSERT(e != nullptr);
@@ -3252,9 +3271,18 @@ void update_untyped_expr_type(CheckerContext *c, Ast *e, Type *type, bool final)
 			// See above note in UnaryExpr case
 			break;
 		}
-
-		update_untyped_expr_type(c, te->x, type, final);
-		update_untyped_expr_type(c, te->y, type, final);
+		
+		// NOTE(bill): This is a bit of a hack to get around the edge cases of ternary if expressions
+		// having an untyped value
+		Operand x = make_operand_from_node(te->x);
+		Operand y = make_operand_from_node(te->y);		
+		if (x.mode != Addressing_Constant || check_is_expressible(c, &x, type)) {
+			update_untyped_expr_type(c, te->x, type, final);
+		}
+		if (y.mode != Addressing_Constant || check_is_expressible(c, &y, type)) {
+			update_untyped_expr_type(c, te->y, type, final);
+		}
+		
 	case_end;
 
 	case_ast_node(te, TernaryWhenExpr, e);

+ 7 - 0
src/check_stmt.cpp

@@ -2243,6 +2243,9 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
 						error(e->token, "The 'static' attribute is not allowed to be applied to '_'");
 					} else {
 						e->flags |= EntityFlag_Static;
+						if (ctx->in_defer) {
+							error(e->token, "'static' variables cannot be declared within a defer statement");
+						}
 					}
 				}
 				if (ac.thread_local_model != "") {
@@ -2251,9 +2254,13 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
 						error(e->token, "The 'thread_local' attribute is not allowed to be applied to '_'");
 					} else {
 						e->flags |= EntityFlag_Static;
+						if (ctx->in_defer) {
+							error(e->token, "'thread_local' variables cannot be declared within a defer statement");
+						}
 					}
 					e->Variable.thread_local_model = ac.thread_local_model;
 				}
+				
 
 				if (ac.is_static && ac.thread_local_model != "") {
 					error(e->token, "The 'static' attribute is not needed if 'thread_local' is applied");

+ 3 - 3
src/check_type.cpp

@@ -1564,7 +1564,7 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
 					p->flags &= ~FieldFlag_const;
 				}
 				if (p->flags&FieldFlag_any_int) {
-					error(name, "'#const' can only be applied to variable fields");
+					error(name, "'#any_int' can only be applied to variable fields");
 					p->flags &= ~FieldFlag_any_int;
 				}
 
@@ -1614,7 +1614,7 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
 								ok = false;
 							}
 						} else if (p->flags&FieldFlag_any_int) {
-							if (!is_type_integer(op.type) || !is_type_integer(type)) {
+							if ((!is_type_integer(op.type) && !is_type_enum(op.type)) || (!is_type_integer(type) && !is_type_enum(type))) {
 								ok = false;
 							} else if (!check_is_castable_to(ctx, &op, type)) {
 								ok = false;
@@ -1684,7 +1684,7 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
 				param->flags |= EntityFlag_AutoCast;
 			}
 			if (p->flags&FieldFlag_any_int) {
-				if (!is_type_integer(param->type)) {
+				if (!is_type_integer(param->type) && !is_type_enum(param->type)) {
 					gbString str = type_to_string(param->type);
 					error(name, "A parameter with '#any_int' must be an integer, got %s", str);
 					gb_string_free(str);

+ 13 - 0
src/checker.cpp

@@ -2050,6 +2050,7 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
 		
 		// WASM Specific
 		str_lit("__ashlti3"),
+		str_lit("__multi3"),
 	};
 	for (isize i = 0; i < gb_count_of(required_runtime_entities); i++) {
 		force_add_dependency_entity(c, c->info.runtime_package->scope, required_runtime_entities[i]);
@@ -4120,6 +4121,14 @@ void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) {
 		mpmc_enqueue(&ctx->info->required_foreign_imports_through_force_queue, e);
 		add_entity_use(ctx, nullptr, e);
 	}
+	
+	if (has_asm_extension(fullpath)) {
+		if (build_context.metrics.arch != TargetArch_amd64 ||
+		    build_context.metrics.os   != TargetOs_windows) {
+			error(decl, "Assembly files are not yet supported on this platform: %.*s_%.*s", 
+			      LIT(target_os_names[build_context.metrics.os]), LIT(target_arch_names[build_context.metrics.arch]));
+		}
+	}
 }
 
 // Returns true if a new package is present
@@ -4842,6 +4851,10 @@ void check_unchecked_bodies(Checker *c) {
 }
 
 void check_test_procedures(Checker *c) {
+	if (build_context.test_names.entries.count == 0) {
+		return;
+	}
+
 	AstPackage *pkg = c->info.init_package;
 	Scope *s = pkg->scope;
 

+ 5 - 2
src/docs_writer.cpp

@@ -826,6 +826,9 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
 		}
 		if (e->flags & EntityFlag_Static) { flags |= OdinDocEntityFlag_Var_Static; }
 		link_name = e->Variable.link_name;
+		if (init_expr == nullptr) {
+			init_expr = e->Variable.init_expr;
+		}
 		break;
 	case Entity_Procedure:
 		if (e->Procedure.is_foreign) { flags |= OdinDocEntityFlag_Foreign; }
@@ -856,8 +859,8 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
 				init_string = odin_doc_write_string(w, make_string_c(exact_value_to_string(e->Constant.value)));
 			}
 		} else if (e->kind == Entity_Variable) {
-			if (e->Variable.param_expr) {
-				init_string = odin_doc_expr_string(w, e->Variable.param_expr);
+			if (e->Variable.param_value.original_ast_expr) {
+				init_string = odin_doc_expr_string(w, e->Variable.param_value.original_ast_expr);
 			}
 		}
 	}

+ 0 - 1
src/entity.cpp

@@ -166,7 +166,6 @@ struct Entity {
 			i32  field_index;
 
 			ParameterValue param_value;
-			Ast *          param_expr;
 
 			String     thread_local_model;
 			Entity *   foreign_library;

+ 2 - 0
src/gb/gb.h

@@ -3355,6 +3355,8 @@ gb_inline u32 gb_thread_current_id(void) {
 	__asm__("mov %%gs:0x08,%0" : "=r"(thread_id));
 #elif defined(GB_ARCH_64_BIT) && defined(GB_CPU_X86)
 	__asm__("mov %%fs:0x10,%0" : "=r"(thread_id));
+#elif defined(GB_SYSTEM_LINUX)
+	thread_id = gettid();
 #else
 	#error Unsupported architecture for gb_thread_current_id()
 #endif

+ 20 - 7
src/llvm_abi.cpp

@@ -965,6 +965,10 @@ namespace lbAbiArm64 {
 		}
 		return false;
 	}
+    
+    unsigned is_homogenous_aggregate_small_enough(LLVMTypeRef *base_type_, unsigned member_count_) {
+        return (member_count_ <= 4);
+    }
 
 	lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef type, bool return_is_defined) {
 		LLVMTypeRef homo_base_type = {};
@@ -975,22 +979,31 @@ namespace lbAbiArm64 {
 		} else if (is_register(type)) {
 			return non_struct(c, type);
 		} else if (is_homogenous_aggregate(c, type, &homo_base_type, &homo_member_count)) {
-			return lb_arg_type_direct(type, LLVMArrayType(homo_base_type, homo_member_count), nullptr, nullptr);
+            if(is_homogenous_aggregate_small_enough(&homo_base_type, homo_member_count)) {
+                return lb_arg_type_direct(type, LLVMArrayType(homo_base_type, homo_member_count), nullptr, nullptr);
+            } else {
+                //TODO(Platin): do i need to create stuff that can handle the diffrent return type?
+                //              else this needs a fix in llvm_backend_proc as we would need to cast it to the correct array type
+                
+                //LLVMTypeRef array_type = LLVMArrayType(homo_base_type, homo_member_count);
+                LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", type);
+                return lb_arg_type_indirect(type, attr);
+            }
 		} else {
 			i64 size = lb_sizeof(type);
 			if (size <= 16) {
 				LLVMTypeRef cast_type = nullptr;
 				if (size <= 1) {
-					cast_type = LLVMIntTypeInContext(c, 8);
+					cast_type = LLVMInt8TypeInContext(c);
 				} else if (size <= 2) {
-					cast_type = LLVMIntTypeInContext(c, 16);
+					cast_type = LLVMInt16TypeInContext(c);
 				} else if (size <= 4) {
-					cast_type = LLVMIntTypeInContext(c, 32);
+					cast_type = LLVMInt32TypeInContext(c);
 				} else if (size <= 8) {
-					cast_type = LLVMIntTypeInContext(c, 64);
+					cast_type = LLVMInt64TypeInContext(c);
 				} else {
 					unsigned count = cast(unsigned)((size+7)/8);
-					cast_type = LLVMArrayType(LLVMIntTypeInContext(c, 64), count);
+					cast_type = LLVMArrayType(LLVMInt64TypeInContext(c), count);
 				}
 				return lb_arg_type_direct(type, cast_type, nullptr, nullptr);
 			} else {
@@ -999,7 +1012,7 @@ namespace lbAbiArm64 {
 			}
 		}
 	}
-
+    
 	Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
 		auto args = array_make<lbArgType>(heap_allocator(), arg_count);
 

+ 17 - 23
src/llvm_backend_expr.cpp

@@ -1655,26 +1655,10 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
 		return res;
 	}
 	
-	if (is_type_float(src) && is_type_complex(dst)) {
-		Type *ft = base_complex_elem_type(dst);
-		lbAddr gen = lb_add_local_generated(p, dst, false);
-		lbValue gp = lb_addr_get_ptr(p, gen);
-		lbValue real = lb_emit_conv(p, value, ft);
-		lb_emit_store(p, lb_emit_struct_ep(p, gp, 0), real);
-		return lb_addr_load(p, gen);
-	}
-	if (is_type_float(src) && is_type_quaternion(dst)) {
-		Type *ft = base_complex_elem_type(dst);
-		lbAddr gen = lb_add_local_generated(p, dst, false);
-		lbValue gp = lb_addr_get_ptr(p, gen);
-		lbValue real = lb_emit_conv(p, value, ft);
-		lb_emit_store(p, lb_emit_struct_ep(p, gp, 0), real);
-		return lb_addr_load(p, gen);
-	}
 
 	if (is_type_complex(src) && is_type_complex(dst)) {
 		Type *ft = base_complex_elem_type(dst);
-		lbAddr gen = lb_add_local_generated(p, dst, false);
+		lbAddr gen = lb_add_local_generated(p, t, false);
 		lbValue gp = lb_addr_get_ptr(p, gen);
 		lbValue real = lb_emit_conv(p, lb_emit_struct_ev(p, value, 0), ft);
 		lbValue imag = lb_emit_conv(p, lb_emit_struct_ev(p, value, 1), ft);
@@ -1686,7 +1670,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
 	if (is_type_quaternion(src) && is_type_quaternion(dst)) {
 		// @QuaternionLayout
 		Type *ft = base_complex_elem_type(dst);
-		lbAddr gen = lb_add_local_generated(p, dst, false);
+		lbAddr gen = lb_add_local_generated(p, t, false);
 		lbValue gp = lb_addr_get_ptr(p, gen);
 		lbValue q0 = lb_emit_conv(p, lb_emit_struct_ev(p, value, 0), ft);
 		lbValue q1 = lb_emit_conv(p, lb_emit_struct_ev(p, value, 1), ft);
@@ -1701,7 +1685,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
 
 	if (is_type_integer(src) && is_type_complex(dst)) {
 		Type *ft = base_complex_elem_type(dst);
-		lbAddr gen = lb_add_local_generated(p, dst, true);
+		lbAddr gen = lb_add_local_generated(p, t, true);
 		lbValue gp = lb_addr_get_ptr(p, gen);
 		lbValue real = lb_emit_conv(p, value, ft);
 		lb_emit_store(p, lb_emit_struct_ep(p, gp, 0), real);
@@ -1709,7 +1693,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
 	}
 	if (is_type_float(src) && is_type_complex(dst)) {
 		Type *ft = base_complex_elem_type(dst);
-		lbAddr gen = lb_add_local_generated(p, dst, true);
+		lbAddr gen = lb_add_local_generated(p, t, true);
 		lbValue gp = lb_addr_get_ptr(p, gen);
 		lbValue real = lb_emit_conv(p, value, ft);
 		lb_emit_store(p, lb_emit_struct_ep(p, gp, 0), real);
@@ -1719,7 +1703,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
 
 	if (is_type_integer(src) && is_type_quaternion(dst)) {
 		Type *ft = base_complex_elem_type(dst);
-		lbAddr gen = lb_add_local_generated(p, dst, true);
+		lbAddr gen = lb_add_local_generated(p, t, true);
 		lbValue gp = lb_addr_get_ptr(p, gen);
 		lbValue real = lb_emit_conv(p, value, ft);
 		// @QuaternionLayout
@@ -1728,7 +1712,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
 	}
 	if (is_type_float(src) && is_type_quaternion(dst)) {
 		Type *ft = base_complex_elem_type(dst);
-		lbAddr gen = lb_add_local_generated(p, dst, true);
+		lbAddr gen = lb_add_local_generated(p, t, true);
 		lbValue gp = lb_addr_get_ptr(p, gen);
 		lbValue real = lb_emit_conv(p, value, ft);
 		// @QuaternionLayout
@@ -1737,7 +1721,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
 	}
 	if (is_type_complex(src) && is_type_quaternion(dst)) {
 		Type *ft = base_complex_elem_type(dst);
-		lbAddr gen = lb_add_local_generated(p, dst, true);
+		lbAddr gen = lb_add_local_generated(p, t, true);
 		lbValue gp = lb_addr_get_ptr(p, gen);
 		lbValue real = lb_emit_conv(p, lb_emit_struct_ev(p, value, 0), ft);
 		lbValue imag = lb_emit_conv(p, lb_emit_struct_ev(p, value, 1), ft);
@@ -4611,6 +4595,16 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
 
 		return lb_addr(res);
 	case_end;
+	
+	case_ast_node(oe, OrElseExpr, expr);
+		lbValue ptr = lb_address_from_load_or_generate_local(p, lb_build_expr(p, expr));
+		return lb_addr(ptr);
+	case_end;
+	
+	case_ast_node(oe, OrReturnExpr, expr);
+		lbValue ptr = lb_address_from_load_or_generate_local(p, lb_build_expr(p, expr));
+		return lb_addr(ptr);
+	case_end;
 	}
 
 	TokenPos token_pos = ast_token(expr).pos;

+ 11 - 15
src/llvm_backend_opt.cpp

@@ -48,12 +48,6 @@ LLVMBool lb_must_preserve_predicate_callback(LLVMValueRef value, void *user_data
 	return LLVMIsAAllocaInst(value) != nullptr;
 }
 
-void lb_add_must_preserve_predicate_pass(lbModule *m, LLVMPassManagerRef fpm, i32 optimization_level) {
-	if (false && optimization_level == 0 && m->debug_builder) {
-		// LLVMAddInternalizePassWithMustPreservePredicate(fpm, m, lb_must_preserve_predicate_callback);
-	}
-}
-
 
 #if LLVM_VERSION_MAJOR < 12
 #define LLVM_ADD_CONSTANT_VALUE_PASS(fpm) LLVMAddConstantPropagationPass(fpm)
@@ -61,7 +55,10 @@ void lb_add_must_preserve_predicate_pass(lbModule *m, LLVMPassManagerRef fpm, i3
 #define LLVM_ADD_CONSTANT_VALUE_PASS(fpm) 
 #endif
 
-void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm) {
+void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm, i32 optimization_level) {
+	if (optimization_level == 0 && build_context.ODIN_DEBUG) {
+		return;
+	}
 	LLVMAddPromoteMemoryToRegisterPass(fpm);
 	LLVMAddMergedLoadStoreMotionPass(fpm);
 	LLVM_ADD_CONSTANT_VALUE_PASS(fpm);
@@ -78,14 +75,12 @@ void lb_populate_function_pass_manager(lbModule *m, LLVMPassManagerRef fpm, bool
 	// TODO(bill): Determine which opt definitions should exist in the first place
 	optimization_level = gb_clamp(optimization_level, 0, 2);
 
-	lb_add_must_preserve_predicate_pass(m, fpm, optimization_level);
-
 	if (ignore_memcpy_pass) {
-		lb_basic_populate_function_pass_manager(fpm);
+		lb_basic_populate_function_pass_manager(fpm, optimization_level);
 		return;
 	} else if (optimization_level == 0) {
 		LLVMAddMemCpyOptPass(fpm);
-		lb_basic_populate_function_pass_manager(fpm);
+		lb_basic_populate_function_pass_manager(fpm, optimization_level);
 		return;
 	}
 
@@ -96,7 +91,7 @@ void lb_populate_function_pass_manager(lbModule *m, LLVMPassManagerRef fpm, bool
 	LLVMPassManagerBuilderPopulateFunctionPassManager(pmb, fpm);
 #else
 	LLVMAddMemCpyOptPass(fpm);
-	lb_basic_populate_function_pass_manager(fpm);
+	lb_basic_populate_function_pass_manager(fpm, optimization_level);
 
 	LLVMAddSCCPPass(fpm);
 
@@ -114,11 +109,9 @@ void lb_populate_function_pass_manager_specific(lbModule *m, LLVMPassManagerRef
 	// TODO(bill): Determine which opt definitions should exist in the first place
 	optimization_level = gb_clamp(optimization_level, 0, 2);
 
-	lb_add_must_preserve_predicate_pass(m, fpm, optimization_level);
-
 	if (optimization_level == 0) {
 		LLVMAddMemCpyOptPass(fpm);
-		lb_basic_populate_function_pass_manager(fpm);
+		lb_basic_populate_function_pass_manager(fpm, optimization_level);
 		return;
 	}
 
@@ -191,6 +184,9 @@ void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPa
 	// NOTE(bill): Treat -opt:3 as if it was -opt:2
 	// TODO(bill): Determine which opt definitions should exist in the first place
 	optimization_level = gb_clamp(optimization_level, 0, 2);
+	if (optimization_level == 0 && build_context.ODIN_DEBUG) {
+		return;
+	}
 
 	LLVMAddAlwaysInlinerPass(mpm);
 	LLVMAddStripDeadPrototypesPass(mpm);

+ 4 - 0
src/llvm_backend_proc.cpp

@@ -736,6 +736,10 @@ lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr,
 
 		LLVMValueRef ret = LLVMBuildCall2(p->builder, fnp, fn, args, arg_count, "");
 
+		if (return_ptr.value != nullptr) {
+			LLVMAddCallSiteAttribute(ret, 1, lb_create_enum_attribute_with_type(p->module->ctx, "sret", LLVMTypeOf(args[0])));
+		}
+
 		switch (inlining) {
 		case ProcInlining_none:
 			break;

+ 1 - 1
src/llvm_backend_utility.cpp

@@ -1200,7 +1200,7 @@ lbValue lb_emit_array_epi(lbProcedure *p, lbValue s, isize index) {
 }
 
 lbValue lb_emit_ptr_offset(lbProcedure *p, lbValue ptr, lbValue index) {
-	index = lb_correct_endianness(p, index);
+	index = lb_emit_conv(p, index, t_int);
 	LLVMValueRef indices[1] = {index.value};
 	lbValue res = {};
 	res.type = ptr.type;

+ 67 - 21
src/main.cpp

@@ -177,7 +177,6 @@ i32 linker_stage(lbGenerator *gen) {
 
 		gbString lib_str = gb_string_make(heap_allocator(), "");
 		defer (gb_string_free(lib_str));
-		char lib_str_buf[1024] = {0};
 
 		char const *output_ext = "exe";
 		gbString link_settings = gb_string_make_reserve(heap_allocator(), 256);
@@ -212,28 +211,43 @@ i32 linker_stage(lbGenerator *gen) {
 			add_path(find_result.windows_sdk_ucrt_library_path);
 			add_path(find_result.vs_library_path);
 		}
+		
+		
+		StringSet libs = {};
+		string_set_init(&libs, heap_allocator(), 64);
+		defer (string_set_destroy(&libs));
+		
+		StringSet asm_files = {};
+		string_set_init(&asm_files, heap_allocator(), 64);
+		defer (string_set_destroy(&asm_files));
 
 		for_array(j, gen->modules.entries) {
 			lbModule *m = gen->modules.entries[j].value;
 			for_array(i, m->foreign_library_paths) {
 				String lib = m->foreign_library_paths[i];
-				GB_ASSERT(lib.len < gb_count_of(lib_str_buf)-1);
-				gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
-							" \"%.*s\"", LIT(lib));
-				lib_str = gb_string_appendc(lib_str, lib_str_buf);
+				if (has_asm_extension(lib)) {
+					string_set_add(&asm_files, lib);
+				} else {
+					string_set_add(&libs, lib);
+				}
 			}
 		}
 
 		for_array(i, gen->default_module.foreign_library_paths) {
 			String lib = gen->default_module.foreign_library_paths[i];
-			GB_ASSERT(lib.len < gb_count_of(lib_str_buf)-1);
-			gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
-						" \"%.*s\"", LIT(lib));
-			lib_str = gb_string_appendc(lib_str, lib_str_buf);
+			if (has_asm_extension(lib)) {
+				string_set_add(&asm_files, lib);
+			} else {
+				string_set_add(&libs, lib);
+			}
 		}
-
-
-
+		
+		for_array(i, libs.entries) {
+			String lib = libs.entries[i].value;
+			lib_str = gb_string_append_fmt(lib_str, " \"%.*s\"", LIT(lib));
+		}
+		
+		
 		if (build_context.build_mode == BuildMode_DynamicLibrary) {
 			output_ext = "dll";
 			link_settings = gb_string_append_fmt(link_settings, " /DLL");
@@ -254,6 +268,27 @@ i32 linker_stage(lbGenerator *gen) {
 		if (build_context.ODIN_DEBUG) {
 			link_settings = gb_string_append_fmt(link_settings, " /DEBUG");
 		}
+		
+		for_array(i, asm_files.entries) {
+			String asm_file = asm_files.entries[i].value;
+			String obj_file = concatenate_strings(permanent_allocator(), asm_file, str_lit(".obj"));
+
+			result = system_exec_command_line_app("nasm",
+				"\"%.*s\\bin\\nasm\\windows\\nasm.exe\" \"%.*s\" "
+				"-f win64 "
+				"-o \"%.*s\" "
+				"%.*s "
+				"",
+				LIT(build_context.ODIN_ROOT), LIT(asm_file),
+				LIT(obj_file),
+				LIT(build_context.extra_assembler_flags)
+			);
+			
+			if (result) {
+				return result;
+			}
+			array_add(&gen->output_object_paths, obj_file);
+		}
 
 		gbString object_files = gb_string_make(heap_allocator(), "");
 		defer (gb_string_free(object_files));
@@ -325,10 +360,10 @@ i32 linker_stage(lbGenerator *gen) {
 				LIT(build_context.extra_linker_flags),
 				lib_str
 			);
-			  
-			  if (result) {
+			
+			if (result) {
 				return result;
-			  }
+			}
 		}
 	#else
 		timings_start_section(timings, str_lit("ld-link"));
@@ -404,13 +439,14 @@ i32 linker_stage(lbGenerator *gen) {
 			// so use ld instead.
 			// :UseLDForShared
 			linker = "ld";
-			link_settings = gb_string_appendc(link_settings, "-init '__$startup_runtime' ");
 			// Shared libraries are .dylib on MacOS and .so on Linux.
 			#if defined(GB_SYSTEM_OSX)
 				output_ext = STR_LIT(".dylib");
+				link_settings = gb_string_appendc(link_settings, "-init '___$startup_runtime' ");
 				link_settings = gb_string_appendc(link_settings, "-dylib -dynamic ");
 			#else
 				output_ext = STR_LIT(".so");
+				link_settings = gb_string_appendc(link_settings, "-init '__$startup_runtime' ");
 				link_settings = gb_string_appendc(link_settings, "-shared ");
 			#endif
 		} else {
@@ -545,8 +581,8 @@ void usage(String argv0) {
 	print_usage_line(1, "version   print version");
 	print_usage_line(1, "report    print information useful to reporting a bug");
 	print_usage_line(0, "");
-	print_usage_line(0, "For more information of flags, apply the flag to see what is possible");
-	print_usage_line(1, "-help");
+	print_usage_line(0, "For further details on a command, use -help after the command name");
+	print_usage_line(1, "e.g. odin build -help");
 }
 
 
@@ -617,6 +653,7 @@ enum BuildFlagKind {
 	BuildFlag_UseLLVMApi,
 	BuildFlag_IgnoreUnknownAttributes,
 	BuildFlag_ExtraLinkerFlags,
+	BuildFlag_ExtraAssemblerFlags,
 	BuildFlag_Microarch,
 
 	BuildFlag_TestName,
@@ -768,7 +805,8 @@ bool parse_build_flags(Array<String> args) {
 	add_flag(&build_flags, BuildFlag_VetExtra,          str_lit("vet-extra"),           BuildFlagParam_None, Command__does_check);
 	add_flag(&build_flags, BuildFlag_UseLLVMApi,        str_lit("llvm-api"),            BuildFlagParam_None, Command__does_build);
 	add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None, Command__does_check);
-	add_flag(&build_flags, BuildFlag_ExtraLinkerFlags,  str_lit("extra-linker-flags"),              BuildFlagParam_String, Command__does_build);
+	add_flag(&build_flags, BuildFlag_ExtraLinkerFlags,    str_lit("extra-linker-flags"),            BuildFlagParam_String, Command__does_build);
+	add_flag(&build_flags, BuildFlag_ExtraAssemblerFlags, str_lit("extra-assembler-flags"),         BuildFlagParam_String, Command__does_build);
 	add_flag(&build_flags, BuildFlag_Microarch,         str_lit("microarch"),                       BuildFlagParam_String, Command__does_build);
 
 	add_flag(&build_flags, BuildFlag_TestName,         str_lit("test-name"),                       BuildFlagParam_String, Command_test);
@@ -1314,11 +1352,14 @@ bool parse_build_flags(Array<String> args) {
 						case BuildFlag_IgnoreUnknownAttributes:
 							build_context.ignore_unknown_attributes = true;
 							break;
-						case BuildFlag_ExtraLinkerFlags: {
+						case BuildFlag_ExtraLinkerFlags:
 							GB_ASSERT(value.kind == ExactValue_String);
 							build_context.extra_linker_flags = value.value_string;
 							break;
-						}
+						case BuildFlag_ExtraAssemblerFlags: 
+							GB_ASSERT(value.kind == ExactValue_String);
+							build_context.extra_assembler_flags = value.value_string;
+							break;
 						case BuildFlag_Microarch: {
 							GB_ASSERT(value.kind == ExactValue_String);
 							build_context.microarch = value.value_string;
@@ -2031,6 +2072,11 @@ void print_show_help(String const arg0, String const &command) {
 		print_usage_line(1, "-extra-linker-flags:<string>");
 		print_usage_line(2, "Adds extra linker specific flags in a string");
 		print_usage_line(0, "");
+		
+		print_usage_line(1, "-extra-assembler-flags:<string>");
+		print_usage_line(2, "Adds extra assembler specific flags in a string");
+		print_usage_line(0, "");
+
 
 		print_usage_line(1, "-microarch:<string>");
 		print_usage_line(2, "Specifies the specific micro-architecture for the build in a string");

+ 9 - 0
src/parser.cpp

@@ -5362,6 +5362,15 @@ isize calc_decl_count(Ast *decl) {
 			count += calc_decl_count(decl->BlockStmt.stmts.data[i]);
 		}
 		break;
+	case Ast_WhenStmt:
+		{
+			isize inner_count = calc_decl_count(decl->WhenStmt.body);
+			if (decl->WhenStmt.else_stmt) {
+				inner_count = gb_max(inner_count, calc_decl_count(decl->WhenStmt.else_stmt));
+			}
+			count += inner_count;
+		}
+		break;
 	case Ast_ValueDecl:
 		count = decl->ValueDecl.names.count;
 		break;

+ 27 - 18
src/queue.cpp

@@ -71,6 +71,29 @@ void mpmc_destroy(MPMCQueue<T> *q) {
 }
 
 
+template <typename T>
+bool mpmc_internal_grow(MPMCQueue<T> *q) {
+	mutex_lock(&q->mutex);
+	i32 old_size = q->mask+1;
+	i32 new_size = old_size*2;
+	resize_array_raw(&q->nodes, q->allocator, old_size, new_size);
+	if (q->nodes == nullptr) {
+		GB_PANIC("Unable to resize enqueue: %td -> %td", old_size, new_size);
+		mutex_unlock(&q->mutex);
+		return false;
+	}
+	resize_array_raw(&q->indices, q->allocator, old_size, new_size);
+	if (q->indices == nullptr) {
+		GB_PANIC("Unable to resize enqueue: %td -> %td", old_size, new_size);
+		mutex_unlock(&q->mutex);
+		return false;
+	}
+	mpmc_internal_init_indices(q->indices, old_size, new_size);
+	q->mask = new_size-1;
+	mutex_unlock(&q->mutex);
+	return true;
+}
+
 template <typename T>
 i32 mpmc_enqueue(MPMCQueue<T> *q, T const &data) {
 	GB_ASSERT(q->mask != 0);
@@ -78,8 +101,9 @@ i32 mpmc_enqueue(MPMCQueue<T> *q, T const &data) {
 	i32 head_idx = q->head_idx.load(std::memory_order_relaxed);
 
 	for (;;) {
-		auto node = &q->nodes[head_idx & q->mask];
-		auto node_idx_ptr = &q->indices[head_idx & q->mask];
+		i32 index = head_idx & q->mask;
+		auto node = &q->nodes[index];
+		auto node_idx_ptr = &q->indices[index];
 		i32 node_idx = node_idx_ptr->load(std::memory_order_acquire);
 		i32 diff = node_idx - head_idx;
 
@@ -91,24 +115,9 @@ i32 mpmc_enqueue(MPMCQueue<T> *q, T const &data) {
 				return q->count.fetch_add(1, std::memory_order_release);
 			}
 		} else if (diff < 0) {
-			mutex_lock(&q->mutex);
-			i32 old_size = q->mask+1;
-			i32 new_size = old_size*2;
-			resize_array_raw(&q->nodes, q->allocator, old_size, new_size);
-			if (q->nodes == nullptr) {
-				GB_PANIC("Unable to resize enqueue: %td -> %td", old_size, new_size);
-				mutex_unlock(&q->mutex);
-				return -1;
-			}
-			resize_array_raw(&q->indices, q->allocator, old_size, new_size);
-			if (q->indices == nullptr) {
-				GB_PANIC("Unable to resize enqueue: %td -> %td", old_size, new_size);
-				mutex_unlock(&q->mutex);
+			if (!mpmc_internal_grow(q)) {
 				return -1;
 			}
-			mpmc_internal_init_indices(q->indices, old_size, new_size);
-			q->mask = new_size-1;
-			mutex_unlock(&q->mutex);
 		} else {
 			head_idx = q->head_idx.load(std::memory_order_relaxed);
 		}

+ 4 - 0
src/threading.cpp

@@ -296,6 +296,8 @@ u32 thread_current_id(void) {
 	__asm__("mov %%gs:0x08,%0" : "=r"(thread_id));
 #elif defined(GB_ARCH_64_BIT) && defined(GB_CPU_X86)
 	__asm__("mov %%fs:0x10,%0" : "=r"(thread_id));
+#elif defined(GB_SYSTEM_LINUX)
+	thread_id = gettid();
 #else
 	#error Unsupported architecture for thread_current_id()
 #endif
@@ -315,6 +317,8 @@ gb_inline void yield_thread(void) {
 	#endif
 #elif defined(GB_CPU_X86)
 	_mm_pause();
+#elif defined(GB_CPU_ARM)
+	__asm__ volatile ("yield" : : : "memory");
 #else
 #error Unknown architecture
 #endif

+ 4 - 4
src/tokenizer.cpp

@@ -295,8 +295,8 @@ void init_global_error_collector(void) {
 	mutex_init(&global_error_collector.string_mutex);
 	array_init(&global_error_collector.errors, heap_allocator());
 	array_init(&global_error_collector.error_buffer, heap_allocator());
-	array_init(&global_file_path_strings, heap_allocator(), 4096);
-	array_init(&global_files, heap_allocator(), 4096);
+	array_init(&global_file_path_strings, heap_allocator(), 1, 4096);
+	array_init(&global_files,             heap_allocator(), 1, 4096);
 }
 
 
@@ -306,7 +306,7 @@ bool set_file_path_string(i32 index, String const &path) {
 	mutex_lock(&global_error_collector.string_mutex);
 
 	if (index >= global_file_path_strings.count) {
-		array_resize(&global_file_path_strings, index);
+		array_resize(&global_file_path_strings, index+1);
 	}
 	String prev = global_file_path_strings[index];
 	if (prev.len == 0) {
@@ -324,7 +324,7 @@ bool thread_safe_set_ast_file_from_id(i32 index, AstFile *file) {
 	mutex_lock(&global_error_collector.string_mutex);
 
 	if (index >= global_files.count) {
-		array_resize(&global_files, index);
+		array_resize(&global_files, index+1);
 	}
 	AstFile *prev = global_files[index];
 	if (prev == nullptr) {

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff