Browse Source

Merge branch 'master' into freestanding_amd64

gingerBill 3 years ago
parent
commit
a7adb2fb6e
100 changed files with 3697 additions and 1616 deletions
  1. 1 0
      .github/FUNDING.yml
  2. 15 0
      .github/workflows/ci.yml
  3. 2 0
      .gitignore
  4. 17 3
      Makefile
  5. 13 0
      core/c/libc/errno.odin
  6. 25 0
      core/c/libc/stdio.odin
  7. 7 2
      core/c/libc/time.odin
  8. 5 0
      core/c/libc/wctype.odin
  9. 8 2
      core/compress/common.odin
  10. 2 2
      core/compress/gzip/gzip.odin
  11. 1 1
      core/compress/zlib/zlib.odin
  12. 17 12
      core/container/bit_array/bit_array.odin
  13. 7 6
      core/container/bit_array/doc.odin
  14. 1 1
      core/crypto/rand_generic.odin
  15. 12 0
      core/crypto/rand_openbsd.odin
  16. 23 0
      core/crypto/rand_windows.odin
  17. 0 1
      core/crypto/siphash/siphash.odin
  18. 1 1
      core/dynlib/lib_unix.odin
  19. 27 0
      core/encoding/varint/doc.odin
  20. 139 0
      core/encoding/varint/leb128.odin
  21. 22 6
      core/fmt/fmt.odin
  22. 1 1
      core/hash/hash.odin
  23. 2 0
      core/intrinsics/intrinsics.odin
  24. 13 13
      core/mem/doc.odin
  25. 35 19
      core/mem/virtual/virtual.odin
  26. 5 5
      core/mem/virtual/virtual_linux.odin
  27. 21 5
      core/mem/virtual/virtual_platform.odin
  28. 5 5
      core/mem/virtual/virtual_windows.odin
  29. 174 3
      core/odin/ast/ast.odin
  30. 117 83
      core/odin/ast/clone.odin
  31. 77 77
      core/odin/ast/walk.odin
  32. 90 90
      core/odin/parser/parser.odin
  33. 124 86
      core/odin/printer/visit.odin
  34. 71 0
      core/os/dir_openbsd.odin
  35. 1 0
      core/os/os_darwin.odin
  36. 3 2
      core/os/os_freebsd.odin
  37. 1 0
      core/os/os_linux.odin
  38. 707 0
      core/os/os_openbsd.odin
  39. 2 0
      core/os/os_wasi.odin
  40. 2 0
      core/os/os_windows.odin
  41. 1 1
      core/os/stat_unix.odin
  42. 10 33
      core/os/stat_windows.odin
  43. 6 1
      core/path/filepath/path_unix.odin
  44. 13 1
      core/reflect/reflect.odin
  45. 5 0
      core/runtime/core.odin
  46. 1 1
      core/runtime/entry_unix.odin
  47. 96 5
      core/slice/slice.odin
  48. 1 0
      core/strconv/strconv.odin
  49. 1 1
      core/sync/channel_unix.odin
  50. 78 0
      core/sync/sync2/futex_openbsd.odin
  51. 1 1
      core/sync/sync2/primitives.odin
  52. 9 0
      core/sync/sync2/primitives_openbsd.odin
  53. 1 1
      core/sync/sync2/primitives_pthreads.odin
  54. 36 0
      core/sync/sync_openbsd.odin
  55. 1 1
      core/sync/sync_unix.odin
  56. 65 0
      core/sys/unix/pthread_openbsd.odin
  57. 1 1
      core/sys/unix/pthread_unix.odin
  58. 1 1
      core/thread/thread_unix.odin
  59. 23 11
      core/time/time_unix.odin
  60. 2 0
      examples/all/all_main.odin
  61. 1 22
      examples/all/all_vendor.odin
  62. 10 0
      examples/all/all_vendor_directx.odin
  63. 15 0
      examples/all/all_vendor_stl.odin
  64. 22 1
      src/bug_report.cpp
  65. 57 12
      src/build_settings.cpp
  66. 6 5
      src/check_builtin.cpp
  67. 4 4
      src/check_expr.cpp
  68. 1 0
      src/checker.cpp
  69. 1 1
      src/common.cpp
  70. 86 2
      src/gb/gb.h
  71. 4 0
      src/llvm_backend.cpp
  72. 15 0
      src/llvm_backend_expr.cpp
  73. 17 8
      src/llvm_backend_proc.cpp
  74. 2 2
      src/llvm_backend_utility.cpp
  75. 43 37
      src/main.cpp
  76. 18 1
      src/parser.cpp
  77. 1 0
      src/parser.hpp
  78. 1 1
      src/threading.cpp
  79. 23 14
      src/types.cpp
  80. 6 2
      tests/core/Makefile
  81. 2 1
      tests/core/build.bat
  82. 4 3
      tests/core/compress/test_core_compress.odin
  83. 890 888
      tests/core/crypto/test_core_crypto.odin
  84. BIN
      tests/core/crypto_hash
  85. 21 19
      tests/core/encoding/json/test_core_json.odin
  86. 156 0
      tests/core/encoding/varint/test_core_varint.odin
  87. 5 3
      tests/core/hash/test_core_hash.odin
  88. 17 16
      tests/core/image/test_core_image.odin
  89. 18 16
      tests/core/math/noise/test_core_math_noise.odin
  90. 22 22
      tests/core/odin/test_parser.odin
  91. 33 31
      tests/core/strings/test_core_strings.odin
  92. 8 1
      tests/vendor/Makefile
  93. 4 0
      tests/vendor/botan/test_vendor_botan.odin
  94. 4 0
      tests/vendor/glfw/test_vendor_glfw.odin
  95. 3 3
      vendor/ENet/unix.odin
  96. 2 4
      vendor/botan/bindings/botan.odin
  97. 5 2
      vendor/glfw/bindings/bindings.odin
  98. 7 2
      vendor/miniaudio/common.odin
  99. 7 3
      vendor/miniaudio/data_conversion.odin
  100. 8 5
      vendor/miniaudio/decoding.odin

+ 1 - 0
.github/FUNDING.yml

@@ -1,3 +1,4 @@
 # These are supported funding model platforms
 
+github: odin-lang
 patreon: gingerbill

+ 15 - 0
.github/workflows/ci.yml

@@ -38,6 +38,12 @@ jobs:
           cd tests/vendor
           make
         timeout-minutes: 10
+      - name: Odin check examples/all for Linux i386
+        run: ./odin check examples/all -vet -strict-style -target:linux_i386
+        timeout-minutes: 10
+      - name: Odin check examples/all for OpenBSD amd64
+        run: ./odin check examples/all -vet -strict-style -target:openbsd_amd64
+        timeout-minutes: 10
   build_macOS:
     runs-on: macos-latest
     steps:
@@ -78,6 +84,9 @@ jobs:
           cd tests/vendor
           make
         timeout-minutes: 10
+      - name: Odin check examples/all for Darwin arm64
+        run: ./odin check examples/all -vet -strict-style -target:darwin_arm64
+        timeout-minutes: 10
   build_windows:
     runs-on: windows-2019
     steps:
@@ -138,3 +147,9 @@ jobs:
           cd tests\core\math\big
           call build.bat
         timeout-minutes: 10
+      - name: Odin check examples/all for Windows 32bits
+        shell: cmd
+        run: |
+          call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
+          odin check examples/all -strict-style -target:windows_i386
+        timeout-minutes: 10

+ 2 - 0
.gitignore

@@ -279,3 +279,5 @@ shared/
 *.ll
 
 *.sublime-workspace
+examples/bug/
+build.sh

+ 17 - 3
Makefile

@@ -1,6 +1,6 @@
 GIT_SHA=$(shell git rev-parse --short HEAD)
 DISABLED_WARNINGS=-Wno-switch -Wno-macro-redefined -Wno-unused-value
-LDFLAGS=-pthread -ldl -lm -lstdc++
+LDFLAGS=-pthread -lm -lstdc++
 CFLAGS=-std=c++14 -DGIT_SHA=\"$(GIT_SHA)\"
 CFLAGS:=$(CFLAGS) -DODIN_VERSION_RAW=\"dev-$(shell date +"%Y-%m")\"
 CC=clang
@@ -8,7 +8,7 @@ CC=clang
 OS=$(shell uname)
 
 ifeq ($(OS), Darwin)
-    
+
     ARCH=$(shell uname -m)
     LLVM_CONFIG=llvm-config
 
@@ -35,7 +35,7 @@ ifeq ($(OS), Darwin)
         endif 
     endif 
 
-    LDFLAGS:=$(LDFLAGS) -liconv
+    LDFLAGS:=$(LDFLAGS) -liconv -ldl
     CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
     LDFLAGS:=$(LDFLAGS) -lLLVM-C
 endif
@@ -51,6 +51,20 @@ ifeq ($(OS), Linux)
         endif
     endif
 
+    LDFLAGS:=$(LDFLAGS) -ldl
+    CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
+    LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
+endif
+ifeq ($(OS), OpenBSD)
+    LLVM_CONFIG=/usr/local/bin/llvm-config
+
+    LDFLAGS:=$(LDFLAGS) -liconv
+    CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
+    LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
+endif
+ifeq ($(OS), FreeBSD)
+    LLVM_CONFIG=/usr/local/bin/llvm-config11
+
     CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
     LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
 endif

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

@@ -27,6 +27,19 @@ when ODIN_OS == .Linux || ODIN_OS == .FreeBSD {
 	ERANGE :: 34
 }
 
+when ODIN_OS == .OpenBSD {
+	@(private="file")
+	@(default_calling_convention="c")
+	foreign libc {
+		@(link_name="__errno")
+		_get_errno :: proc() -> ^int ---
+	}
+
+	EDOM   :: 33
+	EILSEQ :: 84
+	ERANGE :: 34
+}
+
 when ODIN_OS == .Windows {
 	@(private="file")
 	@(default_calling_convention="c")

+ 25 - 0
core/c/libc/stdio.odin

@@ -78,6 +78,31 @@ when ODIN_OS == .Linux {
 	}
 }
 
+when ODIN_OS == .OpenBSD {
+	fpos_t :: i64
+
+	_IOFBF :: 0
+	_IOLBF :: 1
+	_IONBF :: 1
+
+	BUFSIZ :: 1024
+
+	EOF :: int(-1)
+
+	FOPEN_MAX	:: 20
+	FILENAME_MAX	:: 1024
+
+	SEEK_SET :: 0
+	SEEK_CUR :: 1
+	SEEK_END :: 2
+
+	foreign libc {
+		stderr: ^FILE
+		stdin:  ^FILE
+		stdout: ^FILE
+	}
+}
+
 when ODIN_OS == .Darwin {
 	fpos_t :: distinct i64
 	

+ 7 - 2
core/c/libc/time.odin

@@ -45,7 +45,7 @@ when ODIN_OS == .Windows {
 	}
 }
 
-when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin {
+when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD {
 	@(default_calling_convention="c")
 	foreign libc {
 		// 7.27.2 Time manipulation functions
@@ -63,7 +63,12 @@ when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin {
 		strftime     :: proc(s: [^]char, maxsize: size_t, format: cstring, timeptr: ^tm) -> size_t ---
 	}
 
-	CLOCKS_PER_SEC :: 1000000
+	when ODIN_OS == .OpenBSD {
+		CLOCKS_PER_SEC :: 100
+	} else {
+		CLOCKS_PER_SEC :: 1000000
+	}
+
 	TIME_UTC       :: 1
 
 	time_t         :: distinct i64

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

@@ -25,6 +25,11 @@ when ODIN_OS == .Darwin {
 	wctype_t  :: distinct u32
 }
 
+when ODIN_OS == .OpenBSD {
+	wctrans_t :: distinct rawptr
+	wctype_t  :: distinct rawptr
+}
+
 @(default_calling_convention="c")
 foreign libc {
 	// 7.30.2.1 Wide character classification functions

+ 8 - 2
core/compress/common.odin

@@ -139,7 +139,13 @@ Context_Memory_Input :: struct #packed {
 	size_packed:       i64,
 	size_unpacked:     i64,
 }
-#assert(size_of(Context_Memory_Input) == 64)
+when size_of(rawptr) == 8 {
+	#assert(size_of(Context_Memory_Input) == 64)
+} else {
+	// e.g. `-target:windows_i386`
+	#assert(size_of(Context_Memory_Input) == 52)
+}
+
 
 Context_Stream_Input :: struct #packed {
 	input_data:        []u8,
@@ -473,4 +479,4 @@ discard_to_next_byte_lsb_from_stream :: proc(z: ^Context_Stream_Input) {
 	consume_bits_lsb(z, discard)
 }
 
-discard_to_next_byte_lsb :: proc{discard_to_next_byte_lsb_from_memory, discard_to_next_byte_lsb_from_stream};
+discard_to_next_byte_lsb :: proc{discard_to_next_byte_lsb_from_memory, discard_to_next_byte_lsb_from_stream}

+ 2 - 2
core/compress/gzip/gzip.odin

@@ -100,7 +100,7 @@ E_GZIP    :: compress.GZIP_Error
 E_ZLIB    :: compress.ZLIB_Error
 E_Deflate :: compress.Deflate_Error
 
-GZIP_MAX_PAYLOAD_SIZE :: int(max(u32le))
+GZIP_MAX_PAYLOAD_SIZE :: i64(max(u32le))
 
 load :: proc{load_from_slice, load_from_file, load_from_context}
 
@@ -136,7 +136,7 @@ load_from_context :: proc(z: ^$C, buf: ^bytes.Buffer, known_gzip_size := -1, exp
 
 	z.output = buf
 
-	if expected_output_size > GZIP_MAX_PAYLOAD_SIZE {
+	if i64(expected_output_size) > i64(GZIP_MAX_PAYLOAD_SIZE) {
 		return E_GZIP.Payload_Size_Exceeds_Max_Payload
 	}
 

+ 1 - 1
core/compress/zlib/zlib.odin

@@ -677,4 +677,4 @@ inflate_from_byte_array_raw :: proc(input: []u8, buf: ^bytes.Buffer, raw := fals
 	return inflate_raw(z=&ctx, expected_output_size=expected_output_size)
 }
 
-inflate :: proc{inflate_from_context, inflate_from_byte_array};
+inflate :: proc{inflate_from_context, inflate_from_byte_array}

+ 17 - 12
core/container/bit_array/bit_array.odin

@@ -1,6 +1,7 @@
 package dynamic_bit_array
 
 import "core:intrinsics"
+import "core:mem"
 
 /*
 	Note that these constants are dependent on the backing being a u64.
@@ -15,15 +16,16 @@ INDEX_MASK  :: 63
 NUM_BITS :: 64
 
 Bit_Array :: struct {
-	bits: [dynamic]u64,
-	bias: int,
-	max_index: int,
+	bits:         [dynamic]u64,
+	bias:         int,
+	max_index:    int,
+	free_pointer: bool,
 }
 
 Bit_Array_Iterator :: struct {
-	array: ^Bit_Array,
+	array:    ^Bit_Array,
 	word_idx: int,
-	bit_idx: uint,
+	bit_idx:  uint,
 }
 
 /*
@@ -186,7 +188,7 @@ set :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator
 /*
 	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 {
+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
 
@@ -194,11 +196,11 @@ create :: proc(max_index: int, min_index := 0, allocator := context.allocator) -
 
 	legs := size_in_bits >> INDEX_SHIFT
 
-	res = Bit_Array{
-		bias = min_index,
-		max_index = max_index,
-	}
-	return res, resize_if_needed(&res, legs)
+	res = new(Bit_Array)
+	res.bias         = min_index
+	res.max_index    = max_index
+	res.free_pointer = true
+	return res, resize_if_needed(res, legs)
 }
 
 /*
@@ -206,7 +208,7 @@ create :: proc(max_index: int, min_index := 0, allocator := context.allocator) -
 */
 clear :: proc(ba: ^Bit_Array) {
 	if ba == nil { return }
-	ba.bits = {}
+	mem.zero_slice(ba.bits[:])
 }
 
 /*
@@ -215,6 +217,9 @@ clear :: proc(ba: ^Bit_Array) {
 destroy :: proc(ba: ^Bit_Array) {
 	if ba == nil { return }
 	delete(ba.bits)
+	if ba.free_pointer { // Only free if this Bit_Array was created using `create`, not when on the stack.
+		free(ba)
+	}
 }
 
 /*

+ 7 - 6
core/container/bit_array/doc.odin

@@ -21,6 +21,7 @@ package dynamic_bit_array
 			// 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) 
+			destroy(&bits)
 		}
 
 	-- A Bit Array can optionally allow for negative indices, if the mininum value was given during creation:
@@ -40,13 +41,13 @@ package dynamic_bit_array
 			using bit_array
 
 			bits := create(int(max(Foo)), int(min(Foo)))
-			defer destroy(&bits)
+			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("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")
 		}
 */

+ 1 - 1
core/crypto/rand_generic.odin

@@ -1,6 +1,6 @@
 package crypto
 
-when ODIN_OS != .Linux {
+when ODIN_OS != .Linux && ODIN_OS != .OpenBSD && ODIN_OS != .Windows {
 	_rand_bytes :: proc (dst: []byte) {
 		unimplemented("crypto: rand_bytes not supported on this OS")
 	}

+ 12 - 0
core/crypto/rand_openbsd.odin

@@ -0,0 +1,12 @@
+package crypto
+
+import "core:c"
+
+foreign import libc "system:c"
+foreign libc {
+	arc4random_buf :: proc "c" (buf: rawptr, nbytes: c.size_t) ---
+}
+
+_rand_bytes :: proc (dst: []byte) {
+	arc4random_buf(raw_data(dst), len(dst))
+}

+ 23 - 0
core/crypto/rand_windows.odin

@@ -0,0 +1,23 @@
+package crypto
+
+import win32 "core:sys/windows"
+import "core:os"
+import "core:fmt"
+
+_rand_bytes :: proc(dst: []byte) {
+	ret := (os.Errno)(win32.BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG))
+	if ret != os.ERROR_NONE {
+		switch ret {
+			case os.ERROR_INVALID_HANDLE:
+				// The handle to the first parameter is invalid.
+				// This should not happen here, since we explicitly pass nil to it
+				panic("crypto: BCryptGenRandom Invalid handle for hAlgorithm")
+			case os.ERROR_INVALID_PARAMETER:
+				// One of the parameters was invalid
+				panic("crypto: BCryptGenRandom Invalid parameter")
+			case:
+				// Unknown error
+				panic(fmt.tprintf("crypto: BCryptGenRandom failed: %d\n", ret))
+		}
+	}
+}

+ 0 - 1
core/crypto/siphash/siphash.odin

@@ -14,7 +14,6 @@ package siphash
 
 import "core:crypto"
 import "core:crypto/util"
-import "core:mem"
 
 /*
     High level API

+ 1 - 1
core/dynlib/lib_unix.odin

@@ -1,4 +1,4 @@
-// +build linux, darwin, freebsd
+// +build linux, darwin, freebsd, openbsd
 package dynlib
 
 import "core:os"

+ 27 - 0
core/encoding/varint/doc.odin

@@ -0,0 +1,27 @@
+/*
+	Implementation of the LEB128 variable integer encoding as used by DWARF encoding and DEX files, among others.
+
+	Author of this Odin package: Jeroen van Rijn
+
+	Example:
+		```odin
+		import "core:encoding/varint"
+		import "core:fmt"
+
+		main :: proc() {
+			buf: [varint.LEB128_MAX_BYTES]u8
+
+			value := u128(42)
+
+			encode_size, encode_err := varint.encode_uleb128(buf[:], value)
+			assert(encode_size == 1 && encode_err == .None)
+
+			fmt.println(buf[:encode_size])
+
+			decoded_val, decode_size, decode_err := varint.decode_uleb128(buf[:encode_size])
+			assert(decoded_val == value && decode_size == encode_size && decode_err == .None)
+		}
+		```
+
+*/
+package varint

+ 139 - 0
core/encoding/varint/leb128.odin

@@ -0,0 +1,139 @@
+/*
+	Copyright 2022 Jeroen van Rijn <[email protected]>.
+	Made available under Odin's BSD-3 license.
+
+	List of contributors:
+		Jeroen van Rijn: Initial implementation.
+*/
+
+// package varint implements variable length integer encoding and decoding using
+// the LEB128 format as used by DWARF debug info, Android .dex and other file formats.
+package varint
+
+import "core:fmt"
+
+// In theory we should use the bigint package. In practice, varints bigger than this indicate a corrupted file.
+// Instead we'll set limits on the values we'll encode/decode
+// 18 * 7 bits = 126, which means that a possible 19th byte may at most be `0b0000_0011`.
+LEB128_MAX_BYTES    :: 19
+
+Error :: enum {
+	None             = 0,
+	Buffer_Too_Small = 1,
+	Value_Too_Large  = 2,
+}
+
+// Decode a slice of bytes encoding an unsigned LEB128 integer into value and number of bytes used.
+// Returns `size` == 0 for an invalid value, empty slice, or a varint > 18 bytes.
+decode_uleb128 :: proc(buf: []u8) -> (val: u128, size: int, err: Error) {
+	more := true
+
+	for v, i in buf {
+		size = i + 1
+
+		// 18 * 7 bits = 126, which means that a possible 19th byte may at most be 0b0000_0011.
+		if size > LEB128_MAX_BYTES || size == LEB128_MAX_BYTES && v > 0b0000_0011 {
+			return 0, 0, .Value_Too_Large
+		}
+
+		val |= u128(v & 0x7f) << uint(i * 7)
+
+		if v < 128 {
+			more = false
+			break
+		}
+	}
+
+	// If the buffer runs out before the number ends, return an error.
+	if more {
+		return 0, 0, .Buffer_Too_Small
+	}
+	return
+}
+
+// Decode a slice of bytes encoding a signed LEB128 integer into value and number of bytes used.
+// Returns `size` == 0 for an invalid value, empty slice, or a varint > 18 bytes.
+decode_ileb128 :: proc(buf: []u8) -> (val: i128, size: int, err: Error) {
+	shift: uint
+
+	if len(buf) == 0 {
+		return 0, 0, .Buffer_Too_Small
+	}
+
+	for v in buf {
+		size += 1
+
+		// 18 * 7 bits = 126, which including sign means we can have a 19th byte.
+		if size > LEB128_MAX_BYTES || size == LEB128_MAX_BYTES && v > 0x7f {
+			return 0, 0, .Value_Too_Large
+		}
+
+		val |= i128(v & 0x7f) << shift
+		shift += 7
+
+		if v < 128 { break }
+	}
+
+	if buf[size - 1] & 0x40 == 0x40 {
+		val |= max(i128) << shift
+	}
+	return
+}
+
+// Encode `val` into `buf` as an unsigned LEB128 encoded series of bytes.
+// `buf` must be appropriately sized.
+encode_uleb128 :: proc(buf: []u8, val: u128) -> (size: int, err: Error) {
+	val := val
+
+	for {
+		size += 1
+
+		if size > len(buf) {
+			fmt.println(val, buf[:size - 1])
+			return 0, .Buffer_Too_Small
+		}
+
+		low := val & 0x7f
+		val >>= 7
+
+		if val > 0 {
+			low |= 0x80 // more bytes to follow
+		}
+		buf[size - 1] = u8(low)
+
+		if val == 0 { break }
+	}
+	return
+}
+
+@(private)
+SIGN_MASK :: (i128(1) << 121) // sign extend mask
+
+// Encode `val` into `buf` as a signed LEB128 encoded series of bytes.
+// `buf` must be appropriately sized.
+encode_ileb128 :: proc(buf: []u8, val: i128) -> (size: int, err: Error) {
+	val      := val
+	more     := true
+
+	for more {
+		size += 1
+
+		if size > len(buf) {
+			return 0, .Buffer_Too_Small
+		}
+
+		low := val & 0x7f
+		val >>= 7
+
+		low = (low ~ SIGN_MASK) - SIGN_MASK
+
+		if (val == 0 && low & 0x40 != 0x40) || (val == -1 && low & 0x40 == 0x40) {
+			more = false
+		} else {
+			low |= 0x80
+		}
+
+		buf[size - 1] = u8(low)
+	}
+	return
+}

+ 22 - 6
core/fmt/fmt.odin

@@ -605,7 +605,7 @@ fmt_bad_verb :: proc(using fi: ^Info, verb: rune) {
 fmt_bool :: proc(using fi: ^Info, b: bool, verb: rune) {
 	switch verb {
 	case 't', 'v':
-		io.write_string(writer, b ? "true" : "false", &fi.n)
+		fmt_string(fi, b ? "true" : "false", 's')
 	case:
 		fmt_bad_verb(fi, verb)
 	}
@@ -943,11 +943,27 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
 fmt_string :: proc(fi: ^Info, s: string, verb: rune) {
 	switch verb {
 	case 's', 'v':
-		io.write_string(fi.writer, s, &fi.n)
-		if fi.width_set && len(s) < fi.width {
-			for _ in 0..<fi.width - len(s) {
-				io.write_byte(fi.writer, ' ', &fi.n)
+		if fi.width_set {
+			if fi.width > len(s) {
+				if fi.minus {
+					io.write_string(fi.writer, s, &fi.n)
+				}
+
+				for _ in 0..<fi.width - len(s) {
+					io.write_byte(fi.writer, ' ', &fi.n)
+				}
+
+				if !fi.minus {
+					io.write_string(fi.writer, s, &fi.n)
+				}
 			}
+			else {
+				io.write_string(fi.writer, s[:fi.width], &fi.n)
+			}
+		}
+		else
+		{
+			io.write_string(fi.writer, s, &fi.n)
 		}
 
 	case 'q': // quoted string
@@ -1058,7 +1074,7 @@ fmt_enum :: proc(fi: ^Info, v: any, verb: rune) {
 			fmt_arg(fi, any{v.data, runtime.type_info_base(e.base).id}, verb)
 		case 's', 'v':
 			if str, ok := enum_value_to_string(v); ok {
-				io.write_string(fi.writer, str, &fi.n)
+				fmt_string(fi, str, 's')
 			} else {
 				io.write_string(fi.writer, "%!(BAD ENUM VALUE=", &fi.n)
 				fmt_arg(fi, any{v.data, runtime.type_info_base(e.base).id}, 'i')

+ 1 - 1
core/hash/hash.odin

@@ -151,7 +151,7 @@ murmur32 :: proc(data: []byte, seed := u32(0)) -> u32 {
 		k1 ~= u32(tail[2]) << 16
 		fallthrough
 	case 2:
-		k1 ~= u32(tail[2]) << 8
+		k1 ~= u32(tail[1]) << 8
 		fallthrough
 	case 1:
 		k1 ~= u32(tail[0])

+ 2 - 0
core/intrinsics/intrinsics.odin

@@ -41,6 +41,8 @@ mem_copy_non_overlapping :: proc(dst, src: rawptr, len: int) ---
 mem_zero                 :: proc(ptr: rawptr, len: int) ---
 mem_zero_volatile        :: proc(ptr: rawptr, len: int) ---
 
+unaligned_load           :: proc(src: ^$T) -> T ---
+unaligned_store          :: proc(dst: ^$T, val: T) -> T ---
 
 fixed_point_mul     :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) ---
 fixed_point_div     :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) ---

+ 13 - 13
core/mem/doc.odin

@@ -12,22 +12,22 @@ import "core:mem"
 import "core:fmt"
 
 _main :: proc() {
-   do stuff
+	do stuff
 }
 
 main :: proc() {
-    track: mem.Tracking_Allocator
-    mem.tracking_allocator_init(&track, context.allocator)
-    context.allocator = mem.tracking_allocator(&track)
-
-    _main()
-
-    for _, v in track.allocation_map {
-        fmt.printf("%v leaked %v bytes", v.location, v.size)
-    }
-    for bf in track.bad_free_array {
-        fmt.printf("%v allocation %p was freed badly", bf.location, bf.memory)
-    }
+	track: mem.Tracking_Allocator
+	mem.tracking_allocator_init(&track, context.allocator)
+	context.allocator = mem.tracking_allocator(&track)
+
+	_main()
+
+	for _, leak in track.allocation_map {
+		fmt.printf("%v leaked %v bytes\n", leak.location, leak.size)
+	}
+	for bad_free in track.bad_free_array {
+		fmt.printf("%v allocation %p was freed badly\n", bad_free.location, bad_free.memory)
+	}
 }
 ```
 */

+ 35 - 19
core/mem/virtual/virtual.odin

@@ -6,25 +6,25 @@ DEFAULT_PAGE_SIZE := uint(4096)
 
 Allocator_Error :: mem.Allocator_Error
 
-reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
+reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
 	return _reserve(size)
 }
 
-commit :: proc(data: rawptr, size: uint) -> Allocator_Error {
+commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
 	return _commit(data, size)
 }
 
-reserve_and_commit :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
+reserve_and_commit :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
 	data = reserve(size) or_return
 	commit(raw_data(data), size) or_return
 	return
 }
 
-decommit :: proc(data: rawptr, size: uint) {
+decommit :: proc "contextless" (data: rawptr, size: uint) {
 	_decommit(data, size)
 }
 
-release :: proc(data: rawptr, size: uint) {
+release :: proc "contextless" (data: rawptr, size: uint) {
 	_release(data, size)
 }
 
@@ -36,7 +36,7 @@ Protect_Flag :: enum u32 {
 Protect_Flags :: distinct bit_set[Protect_Flag; u32]
 Protect_No_Access :: Protect_Flags{}
 
-protect :: proc(data: rawptr, size: uint, flags: Protect_Flags) -> bool {
+protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
 	return _protect(data, size, flags)
 }
 
@@ -82,11 +82,13 @@ memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags)
 	pmblock := platform_memory_alloc(0, total_size) or_return
 	
 	pmblock.block.base = ([^]byte)(uintptr(pmblock) + base_offset)
-	commit(pmblock.block.base, committed) or_return
+	commit_err := platform_memory_commit(pmblock, uint(base_offset) + committed)
+	assert(commit_err == nil)
+
 	// Should be zeroed
 	assert(pmblock.block.used == 0)
 	assert(pmblock.block.prev == nil)	
-	if (do_protection) {
+	if do_protection {
 		protect(rawptr(uintptr(pmblock) + protect_offset), page_size, Protect_No_Access)
 	}
 	
@@ -105,7 +107,7 @@ memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags)
 }
 
 alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int) -> (data: []byte, err: Allocator_Error) {
-	calc_alignment_offset :: proc(block: ^Memory_Block, alignment: uintptr) -> uint {
+	calc_alignment_offset :: proc "contextless" (block: ^Memory_Block, alignment: uintptr) -> uint {
 		alignment_offset := uint(0)
 		ptr := uintptr(block.base[block.used:])
 		mask := alignment-1
@@ -115,23 +117,37 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int)
 		return alignment_offset
 		
 	}
-	
+	do_commit_if_necessary :: proc(block: ^Memory_Block, size: uint) -> (err: Allocator_Error) {
+		if block.committed - block.used < size {
+			pmblock := (^Platform_Memory_Block)(block)
+			base_offset := uint(uintptr(block) - uintptr(pmblock))
+			platform_total_commit := base_offset + block.used + size
+
+			assert(pmblock.committed <= pmblock.reserved)
+			assert(pmblock.committed < platform_total_commit)
+
+			platform_memory_commit(pmblock, platform_total_commit) or_return
+
+			pmblock.committed = platform_total_commit
+			block.committed = pmblock.committed - base_offset
+		}
+		return nil
+	}
+
+
 	alignment_offset := calc_alignment_offset(block, uintptr(alignment))
-	
 	size := uint(min_size) + alignment_offset
-	
+
 	if block.used + size > block.reserved {
 		err = .Out_Of_Memory
 		return
 	}
-	
-	ptr := block.base[block.used:]
-	ptr = ptr[alignment_offset:]
-	
+	assert(block.committed <= block.reserved)
+	do_commit_if_necessary(block, size) or_return
+
+	data = block.base[block.used+alignment_offset:][:min_size]
 	block.used += size
-	assert(block.used <= block.reserved)
-	
-	return ptr[:min_size], nil	
+	return
 }
 
 

+ 5 - 5
core/mem/virtual/virtual_linux.odin

@@ -58,7 +58,7 @@ madvise :: proc "contextless" (addr: rawptr, length: uint, advice: c.int) -> c.i
 }
 
 
-_reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
+_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
 	MAP_FAILED := rawptr(~uintptr(0))
 	result := mmap(nil, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
 	if result == MAP_FAILED {
@@ -67,7 +67,7 @@ _reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
 	return ([^]byte)(result)[:size], nil
 }
 
-_commit :: proc(data: rawptr, size: uint) -> Allocator_Error {
+_commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
 	result := mprotect(data, size, PROT_READ|PROT_WRITE)
 	if result != 0 {
 		// TODO(bill): Handle error value correctly
@@ -75,14 +75,14 @@ _commit :: proc(data: rawptr, size: uint) -> Allocator_Error {
 	}
 	return nil
 }
-_decommit :: proc(data: rawptr, size: uint) {
+_decommit :: proc "contextless" (data: rawptr, size: uint) {
 	mprotect(data, size, PROT_NONE)
 	madvise(data, size, MADV_FREE)
 }
-_release :: proc(data: rawptr, size: uint) {
+_release :: proc "contextless" (data: rawptr, size: uint) {
 	munmap(data, size)
 }
-_protect :: proc(data: rawptr, size: uint, flags: Protect_Flags) -> bool {
+_protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
 	pflags: c.int
 	pflags = PROT_NONE
 	if .Read    in flags { pflags |= PROT_READ  }

+ 21 - 5
core/mem/virtual/virtual_platform.odin

@@ -4,12 +4,13 @@ package mem_virtual
 import sync "core:sync/sync2"
 
 Platform_Memory_Block :: struct {
-	block:    Memory_Block,
-	reserved: uint,
+	block:      Memory_Block,
+	committed:  uint,
+	reserved:   uint,
 	prev, next: ^Platform_Memory_Block,
 } 
 
-platform_memory_alloc :: proc(to_commit, to_reserve: uint) -> (block: ^Platform_Memory_Block, err: Allocator_Error) {
+platform_memory_alloc :: proc "contextless" (to_commit, to_reserve: uint) -> (block: ^Platform_Memory_Block, err: Allocator_Error) {
 	to_commit, to_reserve := to_commit, to_reserve
 	to_reserve = max(to_commit, to_reserve)
 	
@@ -20,12 +21,13 @@ platform_memory_alloc :: proc(to_commit, to_reserve: uint) -> (block: ^Platform_
 	commit(raw_data(data), to_commit)
 	
 	block = (^Platform_Memory_Block)(raw_data(data))
-	block.reserved = to_reserve
+	block.committed = to_commit
+	block.reserved  = to_reserve
 	return
 }
 
 
-platform_memory_free :: proc(block: ^Platform_Memory_Block) {
+platform_memory_free :: proc "contextless" (block: ^Platform_Memory_Block) {
 	if block != nil {
 		release(block, block.reserved)
 	}
@@ -52,3 +54,17 @@ platform_memory_init :: proc() {
 		global_platform_memory_block_sentinel_set = true
 	}
 }
+
+platform_memory_commit :: proc "contextless" (block: ^Platform_Memory_Block, to_commit: uint) -> (err: Allocator_Error) {
+	if to_commit < block.committed {
+		return nil
+	}
+	if to_commit > block.reserved {
+		return .Out_Of_Memory
+	}
+
+
+	commit(block, to_commit) or_return
+	block.committed = to_commit
+	return nil
+}

+ 5 - 5
core/mem/virtual/virtual_windows.odin

@@ -62,7 +62,7 @@ foreign Kernel32 {
 }
 
 
-_reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
+_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
 	result := VirtualAlloc(nil, size, MEM_RESERVE, PAGE_READWRITE)
 	if result == nil {
 		err = .Out_Of_Memory
@@ -72,7 +72,7 @@ _reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
 	return
 }
 
-_commit :: proc(data: rawptr, size: uint) -> Allocator_Error {
+_commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
 	result := VirtualAlloc(data, size, MEM_COMMIT, PAGE_READWRITE)
 	if result == nil {
 		switch err := GetLastError(); err {
@@ -85,13 +85,13 @@ _commit :: proc(data: rawptr, size: uint) -> Allocator_Error {
 	}
 	return nil
 }
-_decommit :: proc(data: rawptr, size: uint) {
+_decommit :: proc "contextless" (data: rawptr, size: uint) {
 	VirtualFree(data, size, MEM_DECOMMIT)
 }
-_release :: proc(data: rawptr, size: uint) {
+_release :: proc "contextless" (data: rawptr, size: uint) {
 	VirtualFree(data, 0, MEM_RELEASE)
 }
-_protect :: proc(data: rawptr, size: uint, flags: Protect_Flags) -> bool {
+_protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
 	pflags: u32
 	pflags = PAGE_NOACCESS
 	switch flags {

+ 174 - 3
core/odin/ast/ast.odin

@@ -34,7 +34,7 @@ Node :: struct {
 	pos:         tokenizer.Pos,
 	end:         tokenizer.Pos,
 	state_flags: Node_State_Flags,
-	derived:     any,
+	derived:     Any_Node,
 }
 
 Comment_Group :: struct {
@@ -88,9 +88,11 @@ File :: struct {
 
 Expr :: struct {
 	using expr_base: Node,
+	derived_expr: Any_Expr,
 }
 Stmt :: struct {
 	using stmt_base: Node,
+	derived_stmt: Any_Stmt,
 }
 Decl :: struct {
 	using decl_base: Stmt,
@@ -541,7 +543,7 @@ unparen_expr :: proc(expr: ^Expr) -> (val: ^Expr) {
 		return
 	}
 	for {
-		e, ok := val.derived.(Paren_Expr)
+		e, ok := val.derived.(^Paren_Expr)
 		if !ok || e.expr == nil {
 			break
 		}
@@ -758,4 +760,173 @@ Matrix_Type :: struct {
 	row_count:    ^Expr,
 	column_count: ^Expr,
 	elem:         ^Expr,
-}
+}
+
+
+Any_Node :: union {
+	^Package,
+	^File,
+	^Comment_Group,
+
+	^Bad_Expr,
+	^Ident,
+	^Implicit,
+	^Undef,
+	^Basic_Lit,
+	^Basic_Directive,
+	^Ellipsis,
+	^Proc_Lit,
+	^Comp_Lit,
+	^Tag_Expr,
+	^Unary_Expr,
+	^Binary_Expr,
+	^Paren_Expr,
+	^Selector_Expr,
+	^Implicit_Selector_Expr,
+	^Selector_Call_Expr,
+	^Index_Expr,
+	^Deref_Expr,
+	^Slice_Expr,
+	^Matrix_Index_Expr,
+	^Call_Expr,
+	^Field_Value,
+	^Ternary_If_Expr,
+	^Ternary_When_Expr,
+	^Or_Else_Expr,
+	^Or_Return_Expr,
+	^Type_Assertion,
+	^Type_Cast,
+	^Auto_Cast,
+	^Inline_Asm_Expr,
+
+	^Proc_Group,
+
+	^Typeid_Type,
+	^Helper_Type,
+	^Distinct_Type,
+	^Poly_Type,
+	^Proc_Type,
+	^Pointer_Type,
+	^Multi_Pointer_Type,
+	^Array_Type,
+	^Dynamic_Array_Type,
+	^Struct_Type,
+	^Union_Type,
+	^Enum_Type,
+	^Bit_Set_Type,
+	^Map_Type,
+	^Relative_Type,
+	^Matrix_Type,
+
+	^Bad_Stmt,
+	^Empty_Stmt,
+	^Expr_Stmt,
+	^Tag_Stmt,
+	^Assign_Stmt,
+	^Block_Stmt,
+	^If_Stmt,
+	^When_Stmt,
+	^Return_Stmt,
+	^Defer_Stmt,
+	^For_Stmt,
+	^Range_Stmt,
+	^Inline_Range_Stmt,
+	^Case_Clause,
+	^Switch_Stmt,
+	^Type_Switch_Stmt,
+	^Branch_Stmt,
+	^Using_Stmt,
+
+	^Bad_Decl,
+	^Value_Decl,
+	^Package_Decl,
+	^Import_Decl,
+	^Foreign_Block_Decl,
+	^Foreign_Import_Decl,
+
+	^Attribute,
+	^Field,
+	^Field_List,
+}
+
+
+Any_Expr :: union {
+	^Bad_Expr,
+	^Ident,
+	^Implicit,
+	^Undef,
+	^Basic_Lit,
+	^Basic_Directive,
+	^Ellipsis,
+	^Proc_Lit,
+	^Comp_Lit,
+	^Tag_Expr,
+	^Unary_Expr,
+	^Binary_Expr,
+	^Paren_Expr,
+	^Selector_Expr,
+	^Implicit_Selector_Expr,
+	^Selector_Call_Expr,
+	^Index_Expr,
+	^Deref_Expr,
+	^Slice_Expr,
+	^Matrix_Index_Expr,
+	^Call_Expr,
+	^Field_Value,
+	^Ternary_If_Expr,
+	^Ternary_When_Expr,
+	^Or_Else_Expr,
+	^Or_Return_Expr,
+	^Type_Assertion,
+	^Type_Cast,
+	^Auto_Cast,
+	^Inline_Asm_Expr,
+
+	^Proc_Group,
+
+	^Typeid_Type,
+	^Helper_Type,
+	^Distinct_Type,
+	^Poly_Type,
+	^Proc_Type,
+	^Pointer_Type,
+	^Multi_Pointer_Type,
+	^Array_Type,
+	^Dynamic_Array_Type,
+	^Struct_Type,
+	^Union_Type,
+	^Enum_Type,
+	^Bit_Set_Type,
+	^Map_Type,
+	^Relative_Type,
+	^Matrix_Type,
+}
+
+
+Any_Stmt :: union {
+	^Bad_Stmt,
+	^Empty_Stmt,
+	^Expr_Stmt,
+	^Tag_Stmt,
+	^Assign_Stmt,
+	^Block_Stmt,
+	^If_Stmt,
+	^When_Stmt,
+	^Return_Stmt,
+	^Defer_Stmt,
+	^For_Stmt,
+	^Range_Stmt,
+	^Inline_Range_Stmt,
+	^Case_Clause,
+	^Switch_Stmt,
+	^Type_Switch_Stmt,
+	^Branch_Stmt,
+	^Using_Stmt,
+
+	^Bad_Decl,
+	^Value_Decl,
+	^Package_Decl,
+	^Import_Decl,
+	^Foreign_Block_Decl,
+	^Foreign_Import_Decl,
+}

+ 117 - 83
core/odin/ast/clone.odin

@@ -1,16 +1,25 @@
 package odin_ast
 
+import "core:intrinsics"
 import "core:mem"
 import "core:fmt"
+import "core:reflect"
 import "core:odin/tokenizer"
+_ :: intrinsics
 
 new :: proc($T: typeid, pos, end: tokenizer.Pos) -> ^T {
 	n, _ := mem.new(T)
 	n.pos = pos
 	n.end = end
-	n.derived = n^
+	n.derived = n
 	base: ^Node = n // dummy check
 	_ = base // "Use" type to make -vet happy
+	when intrinsics.type_has_field(T, "derived_expr") {
+		n.derived_expr = n
+	}
+	when intrinsics.type_has_field(T, "derived_stmt") {
+		n.derived_stmt = n
+	}
 	return n
 }
 
@@ -59,232 +68,257 @@ clone_node :: proc(node: ^Node) -> ^Node {
 		return nil
 	}
 
-	size := size_of(Node)
+	size  := size_of(Node)
 	align := align_of(Node)
-	ti := type_info_of(node.derived.id)
+	ti := reflect.union_variant_type_info(node.derived)
 	if ti != nil {
-		size = ti.size
-		align = ti.align
+		elem := ti.variant.(reflect.Type_Info_Pointer).elem
+		size  = elem.size
+		align = elem.align
 	}
 
-	switch in node.derived {
-	case Package, File:
+	#partial switch in node.derived {
+	case ^Package, ^File:
 		panic("Cannot clone this node type")
 	}
 
 	res := cast(^Node)mem.alloc(size, align)
 	src: rawptr = node
 	if node.derived != nil {
-		src = node.derived.data
+		src = (^rawptr)(&node.derived)^
 	}
 	mem.copy(res, src, size)
-	res.derived.data = rawptr(res)
-	res.derived.id = node.derived.id
+	res_ptr_any: any
+	res_ptr_any.data = &res
+	res_ptr_any.id = ti.id
+
+	reflect.set_union_value(res.derived, res_ptr_any)
 
-	switch r in &res.derived {
-	case Bad_Expr:
-	case Ident:
-	case Implicit:
-	case Undef:
-	case Basic_Lit:
+	res_ptr := reflect.deref(res_ptr_any)
+
+	if de := reflect.struct_field_value_by_name(res_ptr, "derived_expr", true); de != nil {
+		reflect.set_union_value(de, res_ptr_any)
+	}
+	if ds := reflect.struct_field_value_by_name(res_ptr, "derived_stmt", true); ds != nil {
+		reflect.set_union_value(ds, res_ptr_any)
+	}
 
-	case Ellipsis:
+	if res.derived != nil do switch r in res.derived {
+	case ^Package, ^File:
+	case ^Bad_Expr:
+	case ^Ident:
+	case ^Implicit:
+	case ^Undef:
+	case ^Basic_Lit:
+	case ^Basic_Directive:
+	case ^Comment_Group:
+
+	case ^Ellipsis:
 		r.expr = clone(r.expr)
-	case Proc_Lit:
+	case ^Proc_Lit:
 		r.type = auto_cast clone(r.type)
 		r.body = clone(r.body)
-	case Comp_Lit:
+	case ^Comp_Lit:
 		r.type  = clone(r.type)
 		r.elems = clone(r.elems)
 
-	case Tag_Expr:
+	case ^Tag_Expr:
 		r.expr = clone(r.expr)
-	case Unary_Expr:
+	case ^Unary_Expr:
 		r.expr = clone(r.expr)
-	case Binary_Expr:
+	case ^Binary_Expr:
 		r.left  = clone(r.left)
 		r.right = clone(r.right)
-	case Paren_Expr:
+	case ^Paren_Expr:
 		r.expr = clone(r.expr)
-	case Selector_Expr:
+	case ^Selector_Expr:
 		r.expr = clone(r.expr)
 		r.field = auto_cast clone(r.field)
-	case Implicit_Selector_Expr:
+	case ^Implicit_Selector_Expr:
 		r.field = auto_cast clone(r.field)
-	case Selector_Call_Expr:
+	case ^Selector_Call_Expr:
 		r.expr = clone(r.expr)
 		r.call = auto_cast clone(r.call)
-	case Index_Expr:
+	case ^Index_Expr:
 		r.expr = clone(r.expr)
 		r.index = clone(r.index)
-	case Matrix_Index_Expr:
+	case ^Matrix_Index_Expr:
 		r.expr = clone(r.expr)
 		r.row_index = clone(r.row_index)
 		r.column_index = clone(r.column_index)
-	case Deref_Expr:
+	case ^Deref_Expr:
 		r.expr = clone(r.expr)
-	case Slice_Expr:
+	case ^Slice_Expr:
 		r.expr = clone(r.expr)
 		r.low  = clone(r.low)
 		r.high = clone(r.high)
-	case Call_Expr:
+	case ^Call_Expr:
 		r.expr = clone(r.expr)
 		r.args = clone(r.args)
-	case Field_Value:
+	case ^Field_Value:
 		r.field = clone(r.field)
 		r.value = clone(r.value)
-	case Ternary_If_Expr:
+	case ^Ternary_If_Expr:
 		r.x    = clone(r.x)
 		r.cond = clone(r.cond)
 		r.y    = clone(r.y)
-	case Ternary_When_Expr:
+	case ^Ternary_When_Expr:
 		r.x    = clone(r.x)
 		r.cond = clone(r.cond)
 		r.y    = clone(r.y)
-	case Or_Else_Expr:
+	case ^Or_Else_Expr:
 		r.x    = clone(r.x)
 		r.y    = clone(r.y)
-	case Or_Return_Expr:
+	case ^Or_Return_Expr:
 		r.expr = clone(r.expr)
-	case Type_Assertion:
+	case ^Type_Assertion:
 		r.expr = clone(r.expr)
 		r.type = clone(r.type)
-	case Type_Cast:
+	case ^Type_Cast:
 		r.type = clone(r.type)
 		r.expr = clone(r.expr)
-	case Auto_Cast:
+	case ^Auto_Cast:
 		r.expr = clone(r.expr)
-	case Inline_Asm_Expr:
+	case ^Inline_Asm_Expr:
 		r.param_types        = clone(r.param_types)
 		r.return_type        = clone(r.return_type)
 		r.constraints_string = clone(r.constraints_string)
 		r.asm_string         = clone(r.asm_string)
 
-	case Bad_Stmt:
+	case ^Bad_Stmt:
 		// empty
-	case Empty_Stmt:
+	case ^Empty_Stmt:
 		// empty
-	case Expr_Stmt:
+	case ^Expr_Stmt:
 		r.expr = clone(r.expr)
-	case Tag_Stmt:
+	case ^Tag_Stmt:
 		r.stmt = clone(r.stmt)
 
-	case Assign_Stmt:
+	case ^Assign_Stmt:
 		r.lhs = clone(r.lhs)
 		r.rhs = clone(r.rhs)
-	case Block_Stmt:
+	case ^Block_Stmt:
 		r.label = clone(r.label)
 		r.stmts = clone(r.stmts)
-	case If_Stmt:
+	case ^If_Stmt:
 		r.label     = clone(r.label)
 		r.init      = clone(r.init)
 		r.cond      = clone(r.cond)
 		r.body      = clone(r.body)
 		r.else_stmt = clone(r.else_stmt)
-	case When_Stmt:
+	case ^When_Stmt:
 		r.cond      = clone(r.cond)
 		r.body      = clone(r.body)
 		r.else_stmt = clone(r.else_stmt)
-	case Return_Stmt:
+	case ^Return_Stmt:
 		r.results = clone(r.results)
-	case Defer_Stmt:
+	case ^Defer_Stmt:
 		r.stmt = clone(r.stmt)
-	case For_Stmt:
+	case ^For_Stmt:
 		r.label = clone(r.label)
 		r.init = clone(r.init)
 		r.cond = clone(r.cond)
 		r.post = clone(r.post)
 		r.body = clone(r.body)
-	case Range_Stmt:
+	case ^Range_Stmt:
 		r.label = clone(r.label)
 		r.vals = clone(r.vals)
 		r.expr = clone(r.expr)
 		r.body = clone(r.body)
-	case Case_Clause:
+	case ^Inline_Range_Stmt:
+		r.label = clone(r.label)
+		r.val0 = clone(r.val0)
+		r.val1 = clone(r.val1)
+		r.expr = clone(r.expr)
+		r.body = clone(r.body)
+	case ^Case_Clause:
 		r.list = clone(r.list)
 		r.body = clone(r.body)
-	case Switch_Stmt:
+	case ^Switch_Stmt:
 		r.label = clone(r.label)
 		r.init = clone(r.init)
 		r.cond = clone(r.cond)
 		r.body = clone(r.body)
-	case Type_Switch_Stmt:
+	case ^Type_Switch_Stmt:
 		r.label = clone(r.label)
 		r.tag  = clone(r.tag)
 		r.expr = clone(r.expr)
 		r.body = clone(r.body)
-	case Branch_Stmt:
+	case ^Branch_Stmt:
 		r.label = auto_cast clone(r.label)
-	case Using_Stmt:
+	case ^Using_Stmt:
 		r.list = clone(r.list)
-	case Bad_Decl:
-	case Value_Decl:
+	case ^Bad_Decl:
+	case ^Value_Decl:
 		r.attributes = clone(r.attributes)
 		r.names      = clone(r.names)
 		r.type       = clone(r.type)
 		r.values     = clone(r.values)
-	case Package_Decl:
-	case Import_Decl:
-	case Foreign_Block_Decl:
+	case ^Package_Decl:
+	case ^Import_Decl:
+	case ^Foreign_Block_Decl:
 		r.attributes      = clone(r.attributes)
 		r.foreign_library = clone(r.foreign_library)
 		r.body            = clone(r.body)
-	case Foreign_Import_Decl:
+	case ^Foreign_Import_Decl:
 		r.name = auto_cast clone(r.name)
-	case Proc_Group:
+	case ^Proc_Group:
 		r.args = clone(r.args)
-	case Attribute:
+	case ^Attribute:
 		r.elems = clone(r.elems)
-	case Field:
+	case ^Field:
 		r.names         = clone(r.names)
 		r.type          = clone(r.type)
 		r.default_value = clone(r.default_value)
-	case Field_List:
+	case ^Field_List:
 		r.list = clone(r.list)
-	case Typeid_Type:
+	case ^Typeid_Type:
 		r.specialization = clone(r.specialization)
-	case Helper_Type:
+	case ^Helper_Type:
 		r.type = clone(r.type)
-	case Distinct_Type:
+	case ^Distinct_Type:
 		r.type = clone(r.type)
-	case Poly_Type:
+	case ^Poly_Type:
 		r.type = auto_cast clone(r.type)
 		r.specialization = clone(r.specialization)
-	case Proc_Type:
+	case ^Proc_Type:
 		r.params  = auto_cast clone(r.params)
 		r.results = auto_cast clone(r.results)
-	case Pointer_Type:
+	case ^Pointer_Type:
 		r.elem = clone(r.elem)
-	case Multi_Pointer_Type:
+	case ^Multi_Pointer_Type:
 		r.elem = clone(r.elem)
-	case Array_Type:
+	case ^Array_Type:
 		r.len  = clone(r.len)
 		r.elem = clone(r.elem)
-	case Dynamic_Array_Type:
+	case ^Dynamic_Array_Type:
 		r.elem = clone(r.elem)
-	case Struct_Type:
+	case ^Struct_Type:
 		r.poly_params = auto_cast clone(r.poly_params)
 		r.align = clone(r.align)
 		r.fields = auto_cast clone(r.fields)
-	case Union_Type:
+	case ^Union_Type:
 		r.poly_params = auto_cast clone(r.poly_params)
 		r.align = clone(r.align)
 		r.variants = clone(r.variants)
-	case Enum_Type:
+	case ^Enum_Type:
 		r.base_type = clone(r.base_type)
 		r.fields = clone(r.fields)
-	case Bit_Set_Type:
+	case ^Bit_Set_Type:
 		r.elem = clone(r.elem)
 		r.underlying = clone(r.underlying)
-	case Map_Type:
+	case ^Map_Type:
 		r.key = clone(r.key)
 		r.value = clone(r.value)
-	case Matrix_Type:
+	case ^Matrix_Type:
 		r.row_count = clone(r.row_count)
 		r.column_count = clone(r.column_count)
 		r.elem = clone(r.elem)
+	case ^Relative_Type:
+		r.tag = clone(r.tag)
+		r.type = clone(r.type)
 	case:
-		fmt.panicf("Unhandled node kind: %T", r)
+		fmt.panicf("Unhandled node kind: %v", r)
 	}
 
 	return res

+ 77 - 77
core/odin/ast/walk.odin

@@ -59,64 +59,64 @@ walk :: proc(v: ^Visitor, node: ^Node) {
 	}
 
 	switch n in &node.derived {
-	case File:
+	case ^File:
 		if n.docs != nil {
 			walk(v, n.docs)
 		}
 		walk_stmt_list(v, n.decls[:])
-	case Package:
+	case ^Package:
 		for _, f in n.files {
 			walk(v, f)
 		}
 
-	case Comment_Group:
+	case ^Comment_Group:
 		// empty
-	case Bad_Expr:
-	case Ident:
-	case Implicit:
-	case Undef:
-	case Basic_Lit:
-	case Basic_Directive:
-	case Ellipsis:
+	case ^Bad_Expr:
+	case ^Ident:
+	case ^Implicit:
+	case ^Undef:
+	case ^Basic_Lit:
+	case ^Basic_Directive:
+	case ^Ellipsis:
 		if n.expr != nil {
 			walk(v, n.expr)
 		}
-	case Proc_Lit:
+	case ^Proc_Lit:
 		walk(v, n.type)
 		walk(v, n.body)
 		walk_expr_list(v, n.where_clauses)
-	case Comp_Lit:
+	case ^Comp_Lit:
 		if n.type != nil {
 			walk(v, n.type)
 		}
 		walk_expr_list(v, n.elems)
-	case Tag_Expr:
+	case ^Tag_Expr:
 		walk(v, n.expr)
-	case Unary_Expr:
+	case ^Unary_Expr:
 		walk(v, n.expr)
-	case Binary_Expr:
+	case ^Binary_Expr:
 		walk(v, n.left)
 		walk(v, n.right)
-	case Paren_Expr:
+	case ^Paren_Expr:
 		walk(v, n.expr)
-	case Selector_Expr:
+	case ^Selector_Expr:
 		walk(v, n.expr)
 		walk(v, n.field)
-	case Implicit_Selector_Expr:
+	case ^Implicit_Selector_Expr:
 		walk(v, n.field)
-	case Selector_Call_Expr:
+	case ^Selector_Call_Expr:
 		walk(v, n.expr)
 		walk(v, n.call)
-	case Index_Expr:
+	case ^Index_Expr:
 		walk(v, n.expr)
 		walk(v, n.index)
-	case Matrix_Index_Expr:
+	case ^Matrix_Index_Expr:
 		walk(v, n.expr)
 		walk(v, n.row_index)
 		walk(v, n.column_index)
-	case Deref_Expr:
+	case ^Deref_Expr:
 		walk(v, n.expr)
-	case Slice_Expr:
+	case ^Slice_Expr:
 		walk(v, n.expr)
 		if n.low != nil {
 			walk(v, n.low)
@@ -124,57 +124,57 @@ walk :: proc(v: ^Visitor, node: ^Node) {
 		if n.high != nil {
 			walk(v, n.high)
 		}
-	case Call_Expr:
+	case ^Call_Expr:
 		walk(v, n.expr)
 		walk_expr_list(v, n.args)
-	case Field_Value:
+	case ^Field_Value:
 		walk(v, n.field)
 		walk(v, n.value)
-	case Ternary_If_Expr:
+	case ^Ternary_If_Expr:
 		walk(v, n.x)
 		walk(v, n.cond)
 		walk(v, n.y)
-	case Ternary_When_Expr:
+	case ^Ternary_When_Expr:
 		walk(v, n.x)
 		walk(v, n.cond)
 		walk(v, n.y)
-	case Or_Else_Expr:
+	case ^Or_Else_Expr:
 		walk(v, n.x)
 		walk(v, n.y)
-	case Or_Return_Expr:
+	case ^Or_Return_Expr:
 		walk(v, n.expr)
-	case Type_Assertion:
+	case ^Type_Assertion:
 		walk(v, n.expr)
 		if n.type != nil {
 			walk(v, n.type)
 		}
-	case Type_Cast:
+	case ^Type_Cast:
 		walk(v, n.type)
 		walk(v, n.expr)
-	case Auto_Cast:
+	case ^Auto_Cast:
 		walk(v, n.expr)
-	case Inline_Asm_Expr:
+	case ^Inline_Asm_Expr:
 		walk_expr_list(v, n.param_types)
 		walk(v, n.return_type)
 		walk(v, n.constraints_string)
 		walk(v, n.asm_string)
 
 
-	case Bad_Stmt:
-	case Empty_Stmt:
-	case Expr_Stmt:
+	case ^Bad_Stmt:
+	case ^Empty_Stmt:
+	case ^Expr_Stmt:
 		walk(v, n.expr)
-	case Tag_Stmt:
+	case ^Tag_Stmt:
 		walk(v, n.stmt)
-	case Assign_Stmt:
+	case ^Assign_Stmt:
 		walk_expr_list(v, n.lhs)
 		walk_expr_list(v, n.rhs)
-	case Block_Stmt:
+	case ^Block_Stmt:
 		if n.label != nil {
 			walk(v, n.label)
 		}
 		walk_stmt_list(v, n.stmts)
-	case If_Stmt:
+	case ^If_Stmt:
 		if n.label != nil {
 			walk(v, n.label)
 		}
@@ -186,17 +186,17 @@ walk :: proc(v: ^Visitor, node: ^Node) {
 		if n.else_stmt != nil {
 			walk(v, n.else_stmt)
 		}
-	case When_Stmt:
+	case ^When_Stmt:
 		walk(v, n.cond)
 		walk(v, n.body)
 		if n.else_stmt != nil {
 			walk(v, n.else_stmt)
 		}
-	case Return_Stmt:
+	case ^Return_Stmt:
 		walk_expr_list(v, n.results)
-	case Defer_Stmt:
+	case ^Defer_Stmt:
 		walk(v, n.stmt)
-	case For_Stmt:
+	case ^For_Stmt:
 		if n.label != nil {
 			walk(v, n.label)
 		}
@@ -210,7 +210,7 @@ walk :: proc(v: ^Visitor, node: ^Node) {
 			walk(v, n.post)
 		}
 		walk(v, n.body)
-	case Range_Stmt:
+	case ^Range_Stmt:
 		if n.label != nil {
 			walk(v, n.label)
 		}
@@ -221,7 +221,7 @@ walk :: proc(v: ^Visitor, node: ^Node) {
 		}
 		walk(v, n.expr)
 		walk(v, n.body)
-	case Inline_Range_Stmt:
+	case ^Inline_Range_Stmt:
 		if n.label != nil {
 			walk(v, n.label)
 		}
@@ -233,10 +233,10 @@ walk :: proc(v: ^Visitor, node: ^Node) {
 		}
 		walk(v, n.expr)
 		walk(v, n.body)
-	case Case_Clause:
+	case ^Case_Clause:
 		walk_expr_list(v, n.list)
 		walk_stmt_list(v, n.body)
-	case Switch_Stmt:
+	case ^Switch_Stmt:
 		if n.label != nil {
 			walk(v, n.label)
 		}
@@ -247,7 +247,7 @@ walk :: proc(v: ^Visitor, node: ^Node) {
 			walk(v, n.cond)
 		}
 		walk(v, n.body)
-	case Type_Switch_Stmt:
+	case ^Type_Switch_Stmt:
 		if n.label != nil {
 			walk(v, n.label)
 		}
@@ -258,16 +258,16 @@ walk :: proc(v: ^Visitor, node: ^Node) {
 			walk(v, n.expr)
 		}
 		walk(v, n.body)
-	case Branch_Stmt:
+	case ^Branch_Stmt:
 		if n.label != nil {
 			walk(v, n.label)
 		}
-	case Using_Stmt:
+	case ^Using_Stmt:
 		walk_expr_list(v, n.list)
 
 
-	case Bad_Decl:
-	case Value_Decl:
+	case ^Bad_Decl:
+	case ^Value_Decl:
 		if n.docs != nil {
 			walk(v, n.docs)
 		}
@@ -280,21 +280,21 @@ walk :: proc(v: ^Visitor, node: ^Node) {
 		if n.comment != nil {
 			walk(v, n.comment)
 		}
-	case Package_Decl:
+	case ^Package_Decl:
 		if n.docs != nil {
 			walk(v, n.docs)
 		}
 		if n.comment != nil {
 			walk(v, n.comment)
 		}
-	case Import_Decl:
+	case ^Import_Decl:
 		if n.docs != nil {
 			walk(v, n.docs)
 		}
 		if n.comment != nil {
 			walk(v, n.comment)
 		}
-	case Foreign_Block_Decl:
+	case ^Foreign_Block_Decl:
 		if n.docs != nil {
 			walk(v, n.docs)
 		}
@@ -303,7 +303,7 @@ walk :: proc(v: ^Visitor, node: ^Node) {
 			walk(v, n.foreign_library)
 		}
 		walk(v, n.body)
-	case Foreign_Import_Decl:
+	case ^Foreign_Import_Decl:
 		if n.docs != nil {
 			walk(v, n.docs)
 		}
@@ -313,11 +313,11 @@ walk :: proc(v: ^Visitor, node: ^Node) {
 			walk(v, n.comment)
 		}
 
-	case Proc_Group:
+	case ^Proc_Group:
 		walk_expr_list(v, n.args)
-	case Attribute:
+	case ^Attribute:
 		walk_expr_list(v, n.elems)
-	case Field:
+	case ^Field:
 		if n.docs != nil {
 			walk(v, n.docs)
 		}
@@ -331,31 +331,31 @@ walk :: proc(v: ^Visitor, node: ^Node) {
 		if n.comment != nil {
 			walk(v, n.comment)
 		}
-	case Field_List:
+	case ^Field_List:
 		for x in n.list {
 			walk(v, x)
 		}
-	case Typeid_Type:
+	case ^Typeid_Type:
 		if n.specialization != nil {
 			walk(v, n.specialization)
 		}
-	case Helper_Type:
+	case ^Helper_Type:
 		walk(v, n.type)
-	case Distinct_Type:
+	case ^Distinct_Type:
 		walk(v, n.type)
-	case Poly_Type:
+	case ^Poly_Type:
 		walk(v, n.type)
 		if n.specialization != nil {
 			walk(v, n.specialization)
 		}
-	case Proc_Type:
+	case ^Proc_Type:
 		walk(v, n.params)
 		walk(v, n.results)
-	case Pointer_Type:
+	case ^Pointer_Type:
 		walk(v, n.elem)
-	case Multi_Pointer_Type:
+	case ^Multi_Pointer_Type:
 		walk(v, n.elem)
-	case Array_Type:
+	case ^Array_Type:
 		if n.tag != nil {
 			walk(v, n.tag)
 		}
@@ -363,12 +363,12 @@ walk :: proc(v: ^Visitor, node: ^Node) {
 			walk(v, n.len)
 		}
 		walk(v, n.elem)
-	case Dynamic_Array_Type:
+	case ^Dynamic_Array_Type:
 		if n.tag != nil {
 			walk(v, n.tag)
 		}
 		walk(v, n.elem)
-	case Struct_Type:
+	case ^Struct_Type:
 		if n.poly_params != nil {
 			walk(v, n.poly_params)
 		}
@@ -377,7 +377,7 @@ walk :: proc(v: ^Visitor, node: ^Node) {
 		}
 		walk_expr_list(v, n.where_clauses)
 		walk(v, n.fields)
-	case Union_Type:
+	case ^Union_Type:
 		if n.poly_params != nil {
 			walk(v, n.poly_params)
 		}
@@ -386,23 +386,23 @@ walk :: proc(v: ^Visitor, node: ^Node) {
 		}
 		walk_expr_list(v, n.where_clauses)
 		walk_expr_list(v, n.variants)
-	case Enum_Type:
+	case ^Enum_Type:
 		if n.base_type != nil {
 			walk(v, n.base_type)
 		}
 		walk_expr_list(v, n.fields)
-	case Bit_Set_Type:
+	case ^Bit_Set_Type:
 		walk(v, n.elem)
 		if n.underlying != nil {
 			walk(v, n.underlying)
 		}
-	case Map_Type:
+	case ^Map_Type:
 		walk(v, n.key)
 		walk(v, n.value)
-	case Relative_Type:
+	case ^Relative_Type:
 		walk(v, n.tag)
 		walk(v, n.type)
-	case Matrix_Type:
+	case ^Matrix_Type:
 		walk(v, n.row_count)
 		walk(v, n.column_count)
 		walk(v, n.elem)

+ 90 - 90
core/odin/parser/parser.odin

@@ -195,10 +195,10 @@ parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool {
 	for p.curr_tok.kind != .EOF {
 		stmt := parse_stmt(p)
 		if stmt != nil {
-			if _, ok := stmt.derived.(ast.Empty_Stmt); !ok {
+			if _, ok := stmt.derived.(^ast.Empty_Stmt); !ok {
 				append(&p.file.decls, stmt)
-				if es, es_ok := stmt.derived.(ast.Expr_Stmt); es_ok && es.expr != nil {
-					if _, pl_ok := es.expr.derived.(ast.Proc_Lit); pl_ok {
+				if es, es_ok := stmt.derived.(^ast.Expr_Stmt); es_ok && es.expr != nil {
+					if _, pl_ok := es.expr.derived.(^ast.Proc_Lit); pl_ok {
 						error(p, stmt.pos, "procedure literal evaluated but not used")
 					}
 				}
@@ -459,7 +459,7 @@ is_blank_ident_token :: proc(tok: tokenizer.Token) -> bool {
 	return false
 }
 is_blank_ident_node :: proc(node: ^ast.Node) -> bool {
-	if ident, ok := node.derived.(ast.Ident); ok {
+	if ident, ok := node.derived.(^ast.Ident); ok {
 		return is_blank_ident(ident.name)
 	}
 	return true
@@ -502,34 +502,34 @@ is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool {
 		return true
 	}
 
-	switch n in node.derived {
-	case ast.Empty_Stmt, ast.Block_Stmt:
+	#partial switch n in node.derived {
+	case ^ast.Empty_Stmt, ^ast.Block_Stmt:
 		return true
 
-	case ast.If_Stmt, ast.When_Stmt,
-	     ast.For_Stmt, ast.Range_Stmt, ast.Inline_Range_Stmt,
-	     ast.Switch_Stmt, ast.Type_Switch_Stmt:
+	case ^ast.If_Stmt, ^ast.When_Stmt,
+	     ^ast.For_Stmt, ^ast.Range_Stmt, ^ast.Inline_Range_Stmt,
+	     ^ast.Switch_Stmt, ^ast.Type_Switch_Stmt:
 		return true
 
-	case ast.Helper_Type:
+	case ^ast.Helper_Type:
 		return is_semicolon_optional_for_node(p, n.type)
-	case ast.Distinct_Type:
+	case ^ast.Distinct_Type:
 		return is_semicolon_optional_for_node(p, n.type)
-	case ast.Pointer_Type:
+	case ^ast.Pointer_Type:
 		return is_semicolon_optional_for_node(p, n.elem)
-	case ast.Struct_Type, ast.Union_Type, ast.Enum_Type:
+	case ^ast.Struct_Type, ^ast.Union_Type, ^ast.Enum_Type:
 		// Require semicolon within a procedure body
 		return p.curr_proc == nil
-	case ast.Proc_Lit:
+	case ^ast.Proc_Lit:
 		return true
 
-	case ast.Package_Decl, ast.Import_Decl, ast.Foreign_Import_Decl:
+	case ^ast.Package_Decl, ^ast.Import_Decl, ^ast.Foreign_Import_Decl:
 		return true
 
-	case ast.Foreign_Block_Decl:
+	case ^ast.Foreign_Block_Decl:
 		return is_semicolon_optional_for_node(p, n.body)
 
-	case ast.Value_Decl:
+	case ^ast.Value_Decl:
 		if n.is_mutable {
 			return false
 		}
@@ -641,10 +641,10 @@ parse_stmt_list :: proc(p: ^Parser) -> []^ast.Stmt {
 	    p.curr_tok.kind != .EOF  {
 		stmt := parse_stmt(p)
 		if stmt != nil {
-			if _, ok := stmt.derived.(ast.Empty_Stmt); !ok {
+			if _, ok := stmt.derived.(^ast.Empty_Stmt); !ok {
 				append(&list, stmt)
-				if es, es_ok := stmt.derived.(ast.Expr_Stmt); es_ok && es.expr != nil {
-					if _, pl_ok := es.expr.derived.(ast.Proc_Lit); pl_ok {
+				if es, es_ok := stmt.derived.(^ast.Expr_Stmt); es_ok && es.expr != nil {
+					if _, pl_ok := es.expr.derived.(^ast.Proc_Lit); pl_ok {
 						error(p, stmt.pos, "procedure literal evaluated but not used")
 					}
 				}
@@ -722,7 +722,7 @@ convert_stmt_to_expr :: proc(p: ^Parser, stmt: ^ast.Stmt, kind: string) -> ^ast.
 	if stmt == nil {
 		return nil
 	}
-	if es, ok := stmt.derived.(ast.Expr_Stmt); ok {
+	if es, ok := stmt.derived.(^ast.Expr_Stmt); ok {
 		return es.expr
 	}
 	error(p, stmt.pos, "expected %s, found a simple statement", kind)
@@ -864,7 +864,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
 
 		if p.curr_tok.kind != .Semicolon {
 			cond = parse_simple_stmt(p, {Stmt_Allow_Flag.In})
-			if as, ok := cond.derived.(ast.Assign_Stmt); ok && as.op.kind == .In {
+			if as, ok := cond.derived.(^ast.Assign_Stmt); ok && as.op.kind == .In {
 				is_range = true
 			}
 		}
@@ -906,7 +906,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
 
 
 	if is_range {
-		assign_stmt := cond.derived.(ast.Assign_Stmt)
+		assign_stmt := cond.derived.(^ast.Assign_Stmt)
 		vals := assign_stmt.lhs[:]
 
 		rhs: ^ast.Expr
@@ -987,7 +987,7 @@ parse_switch_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
 			tag = as
 		} else {
 			tag = parse_simple_stmt(p, {Stmt_Allow_Flag.In})
-			if as, ok := tag.derived.(ast.Assign_Stmt); ok && as.op.kind == .In {
+			if as, ok := tag.derived.(^ast.Assign_Stmt); ok && as.op.kind == .In {
 				is_type_switch = true
 			} else if parse_control_statement_semicolon_separator(p) {
 				init = tag
@@ -1074,14 +1074,14 @@ parse_attribute :: proc(p: ^Parser, tok: tokenizer.Token, open_kind, close_kind:
 	skip_possible_newline(p)
 
 	decl := parse_stmt(p)
-	switch d in &decl.derived {
-	case ast.Value_Decl:
+	#partial switch d in decl.derived_stmt {
+	case ^ast.Value_Decl:
 		if d.docs == nil { d.docs = docs }
 		append(&d.attributes, attribute)
-	case ast.Foreign_Block_Decl:
+	case ^ast.Foreign_Block_Decl:
 		if d.docs == nil { d.docs = docs }
 		append(&d.attributes, attribute)
-	case ast.Foreign_Import_Decl:
+	case ^ast.Foreign_Import_Decl:
 		if d.docs == nil { d.docs = docs }
 		append(&d.attributes, attribute)
 	case:
@@ -1095,11 +1095,11 @@ parse_attribute :: proc(p: ^Parser, tok: tokenizer.Token, open_kind, close_kind:
 
 parse_foreign_block_decl :: proc(p: ^Parser) -> ^ast.Stmt {
 	decl := parse_stmt(p)
-	switch in decl.derived {
-	case ast.Empty_Stmt, ast.Bad_Stmt, ast.Bad_Decl:
+	#partial switch in decl.derived_stmt {
+	case ^ast.Empty_Stmt, ^ast.Bad_Stmt, ^ast.Bad_Decl:
 		// Ignore
 		return nil
-	case ast.When_Stmt, ast.Value_Decl:
+	case ^ast.When_Stmt, ^ast.Value_Decl:
 		return decl
 	}
 
@@ -1303,13 +1303,13 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
 	case .Defer:
 		tok := advance_token(p)
 		stmt := parse_stmt(p)
-		switch s in stmt.derived {
-		case ast.Empty_Stmt:
+		#partial switch s in stmt.derived_stmt {
+		case ^ast.Empty_Stmt:
 			error(p, s.pos, "empty statement after defer (e.g. ';')")
-		case ast.Defer_Stmt:
+		case ^ast.Defer_Stmt:
 			error(p, s.pos, "you cannot defer a defer statement")
 			stmt = s.stmt
-		case ast.Return_Stmt:
+		case ^ast.Return_Stmt:
 			error(p, s.pos, "you cannot defer a return statement")
 		}
 		ds := ast.new(ast.Defer_Stmt, tok.pos, stmt.end)
@@ -1381,8 +1381,8 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
 		expect_token_after(p, .Colon, "identifier list")
 		decl := parse_value_decl(p, list, docs)
 		if decl != nil {
-			switch d in &decl.derived {
-			case ast.Value_Decl:
+			#partial switch d in decl.derived_stmt {
+			case ^ast.Value_Decl:
 				d.is_using = true
 				return decl
 			}
@@ -1413,9 +1413,9 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
 			return stmt
 		case "partial":
 			stmt := parse_stmt(p)
-			switch s in &stmt.derived {
-			case ast.Switch_Stmt:      s.partial = true
-			case ast.Type_Switch_Stmt: s.partial = true
+			#partial switch s in stmt.derived_stmt {
+			case ^ast.Switch_Stmt:      s.partial = true
+			case ^ast.Type_Switch_Stmt: s.partial = true
 			case: error(p, stmt.pos, "#partial can only be applied to a switch statement")
 			}
 			return stmt
@@ -1560,11 +1560,11 @@ parse_body :: proc(p: ^Parser) -> ^ast.Block_Stmt {
 }
 
 convert_stmt_to_body :: proc(p: ^Parser, stmt: ^ast.Stmt) -> ^ast.Stmt {
-	switch s in stmt.derived {
-	case ast.Block_Stmt:
+	#partial switch s in stmt.derived_stmt {
+	case ^ast.Block_Stmt:
 		error(p, stmt.pos, "expected a normal statement rather than a block statement")
 		return stmt
-	case ast.Empty_Stmt:
+	case ^ast.Empty_Stmt:
 		error(p, stmt.pos, "expected a non-empty statement")
 	}
 
@@ -1641,10 +1641,10 @@ convert_to_ident_list :: proc(p: ^Parser, list: []Expr_And_Flags, ignore_flags,
 
 		id: ^ast.Expr = ident.expr
 
-		switch n in ident.expr.derived {
-		case ast.Ident:
-		case ast.Bad_Expr:
-		case ast.Poly_Type:
+		#partial switch n in ident.expr.derived_expr {
+		case ^ast.Ident:
+		case ^ast.Bad_Expr:
+		case ^ast.Poly_Type:
 			if allow_poly_names {
 				if n.specialization == nil {
 					break
@@ -1806,21 +1806,21 @@ check_procedure_name_list :: proc(p: ^Parser, names: []^ast.Expr) -> bool {
 		return false
 	}
 
-	_, first_is_polymorphic := names[0].derived.(ast.Poly_Type)
+	_, first_is_polymorphic := names[0].derived.(^ast.Poly_Type)
 	any_polymorphic_names := first_is_polymorphic
 
 	for i := 1; i < len(names); i += 1 {
 		name := names[i]
 
 		if first_is_polymorphic {
-			if _, ok := name.derived.(ast.Poly_Type); ok {
+			if _, ok := name.derived.(^ast.Poly_Type); ok {
 				any_polymorphic_names = true
 			} else {
 				error(p, name.pos, "mixture of polymorphic and non-polymorphic identifiers")
 				return any_polymorphic_names
 			}
 		} else {
-			if _, ok := name.derived.(ast.Poly_Type); ok {
+			if _, ok := name.derived.(^ast.Poly_Type); ok {
 				any_polymorphic_names = true
 				error(p, name.pos, "mixture of polymorphic and non-polymorphic identifiers")
 				return any_polymorphic_names
@@ -1885,7 +1885,7 @@ parse_field_list :: proc(p: ^Parser, follow: tokenizer.Token_Kind, allowed_flags
 			if type == nil {
 				return false
 			}
-			_, ok := type.derived.(ast.Ellipsis)
+			_, ok := type.derived.(^ast.Ellipsis)
 			return ok
 		}
 
@@ -1903,7 +1903,7 @@ parse_field_list :: proc(p: ^Parser, follow: tokenizer.Token_Kind, allowed_flags
 			type = parse_var_type(p, allowed_flags)
 			tt := ast.unparen_expr(type)
 			if is_signature && !any_polymorphic_names {
-				if ti, ok := tt.derived.(ast.Typeid_Type); ok && ti.specialization != nil {
+				if ti, ok := tt.derived.(^ast.Typeid_Type); ok && ti.specialization != nil {
 					error(p, tt.pos, "specialization of typeid is not allowed without polymorphic names")
 				}
 			}
@@ -1979,7 +1979,7 @@ parse_field_list :: proc(p: ^Parser, follow: tokenizer.Token_Kind, allowed_flags
 	    p.curr_tok.kind != .EOF {
 		prefix_flags := parse_field_prefixes(p)
 		param := parse_var_type(p, allowed_flags & {.Typeid_Token, .Ellipsis})
-		if _, ok := param.derived.(ast.Ellipsis); ok {
+		if _, ok := param.derived.(^ast.Ellipsis); ok {
 			if seen_ellipsis {
 				error(p, param.pos, "extra variadic parameter after ellipsis")
 			}
@@ -2006,8 +2006,8 @@ parse_field_list :: proc(p: ^Parser, follow: tokenizer.Token_Kind, allowed_flags
 
 			names := make([]^ast.Expr, 1)
 			names[0] = ast.new(ast.Ident, tok.pos, end_pos(tok))
-			switch ident in &names[0].derived {
-			case ast.Ident:
+			#partial switch ident in names[0].derived_expr {
+			case ^ast.Ident:
 				ident.name = tok.text
 			case:
 				unreachable()
@@ -2137,12 +2137,12 @@ parse_proc_type :: proc(p: ^Parser, tok: tokenizer.Token) -> ^ast.Proc_Type {
 
 	loop: for param in params.list {
 		if param.type != nil {
-			if _, ok := param.type.derived.(ast.Poly_Type); ok {
+			if _, ok := param.type.derived.(^ast.Poly_Type); ok {
 				is_generic = true
 				break loop
 			}
 			for name in param.names {
-				if _, ok := name.derived.(ast.Poly_Type); ok {
+				if _, ok := name.derived.(^ast.Poly_Type); ok {
 					is_generic = true
 					break loop
 				}
@@ -2179,13 +2179,13 @@ parse_inlining_operand :: proc(p: ^Parser, lhs: bool, tok: tokenizer.Token) -> ^
 		}
 	}
 
-	switch e in &ast.unparen_expr(expr).derived {
-	case ast.Proc_Lit:
+	#partial switch e in ast.unparen_expr(expr).derived_expr {
+	case ^ast.Proc_Lit:
 		if e.inlining != .None && e.inlining != pi {
 			error(p, expr.pos, "both 'inline' and 'no_inline' cannot be applied to a procedure literal")
 		}
 		e.inlining = pi
-	case ast.Call_Expr:
+	case ^ast.Call_Expr:
 		if e.inlining != .None && e.inlining != pi {
 			error(p, expr.pos, "both 'inline' and 'no_inline' cannot be applied to a procedure call")
 		}
@@ -2276,9 +2276,9 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 			bd.name = name.text
 			original_type := parse_type(p)
 			type := ast.unparen_expr(original_type)
-			switch t in &type.derived {
-			case ast.Array_Type:         t.tag = bd
-			case ast.Dynamic_Array_Type: t.tag = bd
+			#partial switch t in type.derived_expr {
+			case ^ast.Array_Type:         t.tag = bd
+			case ^ast.Dynamic_Array_Type: t.tag = bd
 			case:
 				error(p, original_type.pos, "expected an array type after #%s", name.text)
 			}
@@ -2290,10 +2290,10 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 			tag.name = name.text
 			original_expr := parse_expr(p, lhs)
 			expr := ast.unparen_expr(original_expr)
-			switch t in &expr.derived {
-			case ast.Comp_Lit:
+			#partial switch t in expr.derived_expr {
+			case ^ast.Comp_Lit:
 				t.tag = tag
-			case ast.Array_Type:
+			case ^ast.Array_Type:
 				t.tag = tag
 				error(p, tok.pos, "#%s has been replaced with #sparse for non-contiguous enumerated array types", name.text)
 			case:
@@ -2308,8 +2308,8 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 			tag.name = name.text
 			original_type := parse_type(p)
 			type := ast.unparen_expr(original_type)
-			switch t in &type.derived {
-			case ast.Array_Type:
+			#partial switch t in type.derived_expr {
+			case ^ast.Array_Type:
 				t.tag = tag
 			case:
 				error(p, tok.pos, "expected an enumerated array type after #%s", name.text)
@@ -2689,7 +2689,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 		variants: [dynamic]^ast.Expr
 		for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF {
 			type := parse_type(p)
-			if _, ok := type.derived.(ast.Bad_Expr); !ok {
+			if _, ok := type.derived.(^ast.Bad_Expr); !ok {
 				append(&variants, type)
 			}
 			if !allow_token(p, .Comma) {
@@ -2864,19 +2864,19 @@ is_literal_type :: proc(expr: ^ast.Expr) -> bool {
 	if val == nil {
 		return false
 	}
-	switch _ in val.derived {
-	case ast.Bad_Expr,
-		ast.Ident,
-		ast.Selector_Expr,
-		ast.Array_Type,
-		ast.Struct_Type,
-		ast.Union_Type,
-		ast.Enum_Type,
-		ast.Dynamic_Array_Type,
-		ast.Map_Type,
-		ast.Bit_Set_Type,
-		ast.Matrix_Type,
-		ast.Call_Expr:
+	#partial switch _ in val.derived_expr {
+	case ^ast.Bad_Expr,
+		^ast.Ident,
+		^ast.Selector_Expr,
+		^ast.Array_Type,
+		^ast.Struct_Type,
+		^ast.Union_Type,
+		^ast.Enum_Type,
+		^ast.Dynamic_Array_Type,
+		^ast.Map_Type,
+		^ast.Bit_Set_Type,
+		^ast.Matrix_Type,
+		^ast.Call_Expr:
 		return true
 	}
 	return false
@@ -2998,7 +2998,7 @@ parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Expr {
 	ce.close    = close.pos
 
 	o := ast.unparen_expr(operand)
-	if se, ok := o.derived.(ast.Selector_Expr); ok && se.op.kind == .Arrow_Right {
+	if se, ok := o.derived.(^ast.Selector_Expr); ok && se.op.kind == .Arrow_Right {
 		sce := ast.new(ast.Selector_Call_Expr, ce.pos, ce.end)
 		sce.expr = o
 		sce.call = ce
@@ -3428,13 +3428,13 @@ parse_simple_stmt :: proc(p: ^Parser, flags: Stmt_Allow_Flags) -> ^ast.Stmt {
 				stmt := parse_stmt(p)
 
 				if stmt != nil {
-					switch n in &stmt.derived {
-					case ast.Block_Stmt:       n.label = label
-					case ast.If_Stmt:          n.label = label
-					case ast.For_Stmt:         n.label = label
-					case ast.Switch_Stmt:      n.label = label
-					case ast.Type_Switch_Stmt: n.label = label
-					case ast.Range_Stmt:	   n.label = label
+					#partial switch n in stmt.derived_stmt {
+					case ^ast.Block_Stmt:       n.label = label
+					case ^ast.If_Stmt:          n.label = label
+					case ^ast.For_Stmt:         n.label = label
+					case ^ast.Switch_Stmt:      n.label = label
+					case ^ast.Type_Switch_Stmt: n.label = label
+					case ^ast.Range_Stmt:	    n.label = label
 					}
 				}
 

+ 124 - 86
core/odin/printer/visit.odin

@@ -342,16 +342,16 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
 		return
 	}
 
-	switch v in &decl.derived {
-	case Expr_Stmt:
+	#partial switch v in decl.derived_stmt {
+	case ^Expr_Stmt:
 		move_line(p, decl.pos)
 		visit_expr(p, v.expr)
 		if p.config.semicolons {
 			push_generic_token(p, .Semicolon, 0)
 		}
-	case When_Stmt:
+	case ^When_Stmt:
 		visit_stmt(p, cast(^Stmt)decl)
-	case Foreign_Import_Decl:
+	case ^Foreign_Import_Decl:
 		if len(v.attributes) > 0 {
 			sort.sort(sort_attribute(&v.attributes))
 			move_line(p, v.attributes[0].pos)
@@ -370,7 +370,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
 		for path in v.fullpaths {
 			push_ident_token(p, path, 0)
 		}
-	case Foreign_Block_Decl:
+	case ^Foreign_Block_Decl:
 		if len(v.attributes) > 0 {
 			sort.sort(sort_attribute(&v.attributes))
 			move_line(p, v.attributes[0].pos)
@@ -383,7 +383,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
 
 		visit_expr(p, v.foreign_library)
 		visit_stmt(p, v.body)
-	case Import_Decl:
+	case ^Import_Decl:
 		move_line(p, decl.pos)
 
 		if v.name.text != "" {
@@ -395,7 +395,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
 			push_ident_token(p, v.fullpath, 1)
 		}
 
-	case Value_Decl:
+	case ^Value_Decl:
 		if len(v.attributes) > 0 {
 			sort.sort(sort_attribute(&v.attributes))
 			move_line(p, v.attributes[0].pos)
@@ -446,10 +446,10 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
 		add_semicolon := true
 
 		for value in v.values {
-			switch a in value.derived {
-			case Union_Type, Enum_Type, Struct_Type:
+			#partial switch a in value.derived {
+			case ^Union_Type, ^Enum_Type, ^Struct_Type:
 				add_semicolon = false || called_in_stmt
-			case Proc_Lit:
+			case ^Proc_Lit:
 				add_semicolon = false
 			}
 		}
@@ -516,23 +516,34 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
 		return
 	}
 
-	switch v in stmt.derived {
-	case Import_Decl:
+
+	switch v in stmt.derived_stmt {
+	case ^Bad_Stmt:
+	case ^Bad_Decl:
+	case ^Package_Decl:
+
+	case ^Empty_Stmt:
+		push_generic_token(p, .Semicolon, 0)
+	case ^Tag_Stmt:
+		push_generic_token(p, .Hash, 1)
+		push_generic_token(p, v.op.kind, 1, v.op.text)
+		visit_stmt(p, v.stmt)
+
+
+	case ^Import_Decl:
 		visit_decl(p, cast(^Decl)stmt, true)
 		return
-	case Value_Decl:
+	case ^Value_Decl:
 		visit_decl(p, cast(^Decl)stmt, true)
 		return
-	case Foreign_Import_Decl:
+	case ^Foreign_Import_Decl:
 		visit_decl(p, cast(^Decl)stmt, true)
 		return
-	case Foreign_Block_Decl:
+	case ^Foreign_Block_Decl:
 		visit_decl(p, cast(^Decl)stmt, true)
 		return
-	}
 
-	switch v in stmt.derived {
-	case Using_Stmt:
+	case ^Using_Stmt:
 		move_line(p, v.pos)
 
 		push_generic_token(p, .Using, 1)
@@ -542,7 +553,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
 		if p.config.semicolons {
 			push_generic_token(p, .Semicolon, 0)
 		}
-	case Block_Stmt:
+	case ^Block_Stmt:
 		move_line(p, v.pos)
 
 		if v.pos.line == v.end.line {
@@ -572,7 +583,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
 				visit_end_brace(p, v.end)
 			}
 		}
-	case If_Stmt:
+	case ^If_Stmt:
 		move_line(p, v.pos)
 
 		if v.label != nil {
@@ -595,7 +606,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
 
 		uses_do := false
 
-		if check_stmt, ok := v.body.derived.(Block_Stmt); ok && check_stmt.uses_do {
+		if check_stmt, ok := v.body.derived.(^Block_Stmt); ok && check_stmt.uses_do {
 			uses_do = true
 		}
 
@@ -626,7 +637,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
 
 			visit_stmt(p, v.else_stmt)
 		}
-	case Switch_Stmt:
+	case ^Switch_Stmt:
 		move_line(p, v.pos)
 
 		if v.label != nil {
@@ -654,7 +665,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
 
 		visit_expr(p, v.cond)
 		visit_stmt(p, v.body)
-	case Case_Clause:
+	case ^Case_Clause:
 		move_line(p, v.pos)
 
 		if !p.config.indent_cases {
@@ -678,7 +689,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
 		if !p.config.indent_cases {
 			indent(p)
 		}
-	case Type_Switch_Stmt:
+	case ^Type_Switch_Stmt:
 		move_line(p, v.pos)
 
 		hint_current_line(p, {.Switch_Stmt})
@@ -696,7 +707,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
 
 		visit_stmt(p, v.tag)
 		visit_stmt(p, v.body)
-	case Assign_Stmt:
+	case ^Assign_Stmt:
 		move_line(p, v.pos)
 
 		hint_current_line(p, {.Assign})
@@ -710,13 +721,13 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
 		if block_stmt && p.config.semicolons {
 			push_generic_token(p, .Semicolon, 0)
 		}
-	case Expr_Stmt:
+	case ^Expr_Stmt:
 		move_line(p, v.pos)
 		visit_expr(p, v.expr)
 		if block_stmt && p.config.semicolons {
 			push_generic_token(p, .Semicolon, 0)
 		}
-	case For_Stmt:
+	case ^For_Stmt:
 		// this should be simplified
 		move_line(p, v.pos)
 
@@ -753,7 +764,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
 
 		visit_stmt(p, v.body)
 
-	case Inline_Range_Stmt:
+	case ^Inline_Range_Stmt:
 		move_line(p, v.pos)
 
 		if v.label != nil {
@@ -779,7 +790,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
 		visit_expr(p, v.expr)
 		visit_stmt(p, v.body)
 
-	case Range_Stmt:
+	case ^Range_Stmt:
 		move_line(p, v.pos)
 
 		if v.label != nil {
@@ -805,7 +816,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
 		visit_expr(p, v.expr)
 
 		visit_stmt(p, v.body)
-	case Return_Stmt:
+	case ^Return_Stmt:
 		move_line(p, v.pos)
 
 		push_generic_token(p, .Return, 1)
@@ -817,7 +828,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
 		if block_stmt && p.config.semicolons {
 			push_generic_token(p, .Semicolon, 0)
 		}
-	case Defer_Stmt:
+	case ^Defer_Stmt:
 		move_line(p, v.pos)
 		push_generic_token(p, .Defer, 0)
 
@@ -826,7 +837,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
 		if p.config.semicolons {
 			push_generic_token(p, .Semicolon, 0)
 		}
-	case When_Stmt:
+	case ^When_Stmt:
 		move_line(p, v.pos)
 		push_generic_token(p, .When, 1)
 		visit_expr(p, v.cond)
@@ -846,7 +857,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
 			visit_stmt(p, v.else_stmt)
 		}
 
-	case Branch_Stmt:
+	case ^Branch_Stmt:
 		move_line(p, v.pos)
 
 		push_generic_token(p, v.tok.kind, 0)
@@ -918,8 +929,15 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
 
 	set_source_position(p, expr.pos)
 
-	switch v in expr.derived {
-	case Inline_Asm_Expr:
+	switch v in expr.derived_expr {
+	case ^Bad_Expr:
+
+	case ^Tag_Expr:
+		push_generic_token(p, .Hash, 1)
+		push_generic_token(p, v.op.kind, 1, v.op.text)
+		visit_expr(p, v.expr)
+
+	case ^Inline_Asm_Expr:
 		push_generic_token(p, v.tok.kind, 1, v.tok.text)
 
 		push_generic_token(p, .Open_Paren, 1)
@@ -936,42 +954,42 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
 		push_generic_token(p, .Comma, 0)
 		visit_expr(p, v.constraints_string)
 		push_generic_token(p, .Close_Brace, 0)
-	case Undef:
+	case ^Undef:
 		push_generic_token(p, .Undef, 1)
-	case Auto_Cast:
+	case ^Auto_Cast:
 		push_generic_token(p, v.op.kind, 1)
 		visit_expr(p, v.expr)
-	case Ternary_If_Expr:
+	case ^Ternary_If_Expr:
 		visit_expr(p, v.x)
 		push_generic_token(p, v.op1.kind, 1)
 		visit_expr(p, v.cond)
 		push_generic_token(p, v.op2.kind, 1)
 		visit_expr(p, v.y)
-	case Ternary_When_Expr:
+	case ^Ternary_When_Expr:
 		visit_expr(p, v.x)
 		push_generic_token(p, v.op1.kind, 1)
 		visit_expr(p, v.cond)
 		push_generic_token(p, v.op2.kind, 1)
 		visit_expr(p, v.y)
-	case Or_Else_Expr:
+	case ^Or_Else_Expr:
 		visit_expr(p, v.x)
 		push_generic_token(p, v.token.kind, 1)
 		visit_expr(p, v.y)
-	case Or_Return_Expr:
+	case ^Or_Return_Expr:
 		visit_expr(p, v.expr)
 		push_generic_token(p, v.token.kind, 1)
-	case Selector_Call_Expr:
+	case ^Selector_Call_Expr:
 		visit_expr(p, v.call.expr)
 		push_generic_token(p, .Open_Paren, 1)
 		visit_exprs(p, v.call.args, {.Add_Comma})
 		push_generic_token(p, .Close_Paren, 0)
-	case Ellipsis:
+	case ^Ellipsis:
 		push_generic_token(p, .Ellipsis, 1)
 		visit_expr(p, v.expr)
-	case Relative_Type:
+	case ^Relative_Type:
 		visit_expr(p, v.tag)
 		visit_expr(p, v.type)
-	case Slice_Expr:
+	case ^Slice_Expr:
 		visit_expr(p, v.expr)
 		push_generic_token(p, .Open_Bracket, 0)
 		visit_expr(p, v.low)
@@ -981,37 +999,37 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
 			visit_expr(p, v.high)
 		}
 		push_generic_token(p, .Close_Bracket, 0)
-	case Ident:
+	case ^Ident:
 		if .Enforce_Poly_Names in options {
 			push_generic_token(p, .Dollar, 1)
 			push_ident_token(p, v.name, 0)
 		} else {
 			push_ident_token(p, v.name, 1)
 		}
-	case Deref_Expr:
+	case ^Deref_Expr:
 		visit_expr(p, v.expr)
 		push_generic_token(p, v.op.kind, 0)
-	case Type_Cast:
+	case ^Type_Cast:
 		push_generic_token(p, v.tok.kind, 1)
 		push_generic_token(p, .Open_Paren, 0)
 		visit_expr(p, v.type)
 		push_generic_token(p, .Close_Paren, 0)
 		merge_next_token(p)
 		visit_expr(p, v.expr)
-	case Basic_Directive:
+	case ^Basic_Directive:
 		push_generic_token(p, v.tok.kind, 1)
 		push_ident_token(p, v.name, 0)
-	case Distinct_Type:
+	case ^Distinct_Type:
 		push_generic_token(p, .Distinct, 1)
 		visit_expr(p, v.type)
-	case Dynamic_Array_Type:
+	case ^Dynamic_Array_Type:
 		visit_expr(p, v.tag)
 		push_generic_token(p, .Open_Bracket, 1)
 		push_generic_token(p, .Dynamic, 0)
 		push_generic_token(p, .Close_Bracket, 0)
 		merge_next_token(p)
 		visit_expr(p, v.elem)
-	case Bit_Set_Type:
+	case ^Bit_Set_Type:
 		push_generic_token(p, .Bit_Set, 1)
 		push_generic_token(p, .Open_Bracket, 0)
 
@@ -1023,7 +1041,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
 		}
 
 		push_generic_token(p, .Close_Bracket, 0)
-	case Union_Type:
+	case ^Union_Type:
 		push_generic_token(p, .Union, 1)
 
 		push_poly_params(p, v.poly_params)
@@ -1045,7 +1063,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
 			visit_exprs(p, v.variants, {.Add_Comma, .Trailing})
 			visit_end_brace(p, v.end)
 		}
-	case Enum_Type:
+	case ^Enum_Type:
 		push_generic_token(p, .Enum, 1)
 
 		hint_current_line(p, {.Enum})
@@ -1068,7 +1086,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
 		}
 
 		set_source_position(p, v.end)
-	case Struct_Type:
+	case ^Struct_Type:
 		push_generic_token(p, .Struct, 1)
 
 		hint_current_line(p, {.Struct})
@@ -1103,7 +1121,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
 		}
 
 		set_source_position(p, v.end)
-	case Proc_Lit:
+	case ^Proc_Lit:
 		switch v.inlining {
 		case .None:
 		case .Inline:
@@ -1112,7 +1130,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
 			push_ident_token(p, "#force_no_inline", 0)
 		}
 
-		visit_proc_type(p, v.type^, true)
+		visit_proc_type(p, v.type, true)
 
 		push_where_clauses(p, v.where_clauses)
 
@@ -1122,16 +1140,16 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
 		} else {
 			push_generic_token(p, .Undef, 1)
 		}
-	case Proc_Type:
+	case ^Proc_Type:
 		visit_proc_type(p, v)
-	case Basic_Lit:
+	case ^Basic_Lit:
 		push_generic_token(p, v.tok.kind, 1, v.tok.text)
-	case Binary_Expr:
+	case ^Binary_Expr:
 		visit_binary_expr(p, v)
-	case Implicit_Selector_Expr:
+	case ^Implicit_Selector_Expr:
 		push_generic_token(p, .Period, 1)
 		push_ident_token(p, v.field.name, 0)
-	case Call_Expr:
+	case ^Call_Expr:
 		visit_expr(p, v.expr)
 
 		push_format_token(p,
@@ -1146,27 +1164,34 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
 
 		visit_call_exprs(p, v.args, v.ellipsis.kind == .Ellipsis)
 		push_generic_token(p, .Close_Paren, 0)
-	case Typeid_Type:
+	case ^Typeid_Type:
 		push_generic_token(p, .Typeid, 1)
 
 		if v.specialization != nil {
 			push_generic_token(p, .Quo, 0)
 			visit_expr(p, v.specialization)
 		}
-	case Selector_Expr:
+	case ^Selector_Expr:
 		visit_expr(p, v.expr)
 		push_generic_token(p, v.op.kind, 0)
 		visit_expr(p, v.field)
-	case Paren_Expr:
+	case ^Paren_Expr:
 		push_generic_token(p, .Open_Paren, 1)
 		visit_expr(p, v.expr)
 		push_generic_token(p, .Close_Paren, 0)
-	case Index_Expr:
+	case ^Index_Expr:
 		visit_expr(p, v.expr)
 		push_generic_token(p, .Open_Bracket, 0)
 		visit_expr(p, v.index)
 		push_generic_token(p, .Close_Bracket, 0)
-	case Proc_Group:
+	case ^Matrix_Index_Expr:
+		visit_expr(p, v.expr)
+		push_generic_token(p, .Open_Bracket, 0)
+		visit_expr(p, v.row_index)
+		push_generic_token(p, .Comma, 0)
+		visit_expr(p, v.column_index)
+		push_generic_token(p, .Close_Bracket, 0)
+	case ^Proc_Group:
 		push_generic_token(p, v.tok.kind, 1)
 
 		if len(v.args) != 0 && v.pos.line != v.args[len(v.args) - 1].pos.line {
@@ -1181,7 +1206,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
 			push_generic_token(p, .Close_Brace, 0)
 		}
 
-	case Comp_Lit:
+	case ^Comp_Lit:
 		if v.type != nil {
 			visit_expr(p, v.type)
 		}
@@ -1198,18 +1223,18 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
 			push_generic_token(p, .Close_Brace, 0)
 		}
 
-	case Unary_Expr:
+	case ^Unary_Expr:
 		push_generic_token(p, v.op.kind, 1)
 		merge_next_token(p)
 		visit_expr(p, v.expr)
-	case Field_Value:
+	case ^Field_Value:
 		visit_expr(p, v.field)
 		push_generic_token(p, .Eq, 1)
 		visit_expr(p, v.value)
-	case Type_Assertion:
+	case ^Type_Assertion:
 		visit_expr(p, v.expr)
 
-		if unary, ok := v.type.derived.(Unary_Expr); ok && unary.op.text == "?" {
+		if unary, ok := v.type.derived.(^Unary_Expr); ok && unary.op.text == "?" {
 			push_generic_token(p, .Period, 0)
 			visit_expr(p, v.type)
 		} else {
@@ -1219,13 +1244,13 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
 			push_generic_token(p, .Close_Paren, 0)
 		}
 
-	case Pointer_Type:
+	case ^Pointer_Type:
 		push_generic_token(p, .Pointer, 1)
 		merge_next_token(p)
 		visit_expr(p, v.elem)
-	case Implicit:
+	case ^Implicit:
 		push_generic_token(p, v.tok.kind, 1)
-	case Poly_Type:
+	case ^Poly_Type:
 		push_generic_token(p, .Dollar, 1)
 		merge_next_token(p)
 		visit_expr(p, v.type)
@@ -1235,22 +1260,35 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
 			merge_next_token(p)
 			visit_expr(p, v.specialization)
 		}
-	case Array_Type:
+	case ^Array_Type:
 		visit_expr(p, v.tag)
 		push_generic_token(p, .Open_Bracket, 1)
 		visit_expr(p, v.len)
 		push_generic_token(p, .Close_Bracket, 0)
 		merge_next_token(p)
 		visit_expr(p, v.elem)
-	case Map_Type:
+	case ^Map_Type:
 		push_generic_token(p, .Map, 1)
 		push_generic_token(p, .Open_Bracket, 0)
 		visit_expr(p, v.key)
 		push_generic_token(p, .Close_Bracket, 0)
 		merge_next_token(p)
 		visit_expr(p, v.value)
-	case Helper_Type:
+	case ^Helper_Type:
 		visit_expr(p, v.type)
+	case ^Multi_Pointer_Type:
+		push_generic_token(p, .Open_Bracket, 1)
+		push_generic_token(p, .Pointer, 0)
+		push_generic_token(p, .Close_Bracket, 0)
+		visit_expr(p, v.elem)
+	case ^Matrix_Type:
+		push_generic_token(p, .Matrix, 1)
+		push_generic_token(p, .Open_Bracket, 0)
+		visit_expr(p, v.row_count)
+		push_generic_token(p, .Comma, 0)
+		visit_expr(p, v.column_count)
+		push_generic_token(p, .Close_Bracket, 0)
+		visit_expr(p, v.elem)
 	case:
 		panic(fmt.aprint(expr.derived))
 	}
@@ -1348,7 +1386,7 @@ visit_field_list :: proc(p: ^Printer, list: ^ast.Field_List, options := List_Opt
 	}
 }
 
-visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type, is_proc_lit := false) {
+visit_proc_type :: proc(p: ^Printer, proc_type: ^ast.Proc_Type, is_proc_lit := false) {
 	if is_proc_lit {
 		push_format_token(p, Format_Token {
 			kind = .Proc,
@@ -1392,7 +1430,7 @@ visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type, is_proc_lit := fa
 		} else if len(proc_type.results.list) == 1 {
 
 			for name in proc_type.results.list[0].names {
-				if ident, ok := name.derived.(ast.Ident); ok {
+				if ident, ok := name.derived.(^ast.Ident); ok {
 					if ident.name != "_" {
 						use_parens = true
 					}
@@ -1410,19 +1448,19 @@ visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type, is_proc_lit := fa
 	}
 }
 
-visit_binary_expr :: proc(p: ^Printer, binary: ast.Binary_Expr) {
+visit_binary_expr :: proc(p: ^Printer, binary: ^ast.Binary_Expr) {
 	move_line(p, binary.left.pos)
 
-	if v, ok := binary.left.derived.(ast.Binary_Expr); ok {
+	if v, ok := binary.left.derived.(^ast.Binary_Expr); ok {
 		visit_binary_expr(p, v)
 	} else {
 		visit_expr(p, binary.left)
 	}
 
 	either_implicit_selector := false
-	if _, ok := binary.left.derived.(ast.Implicit_Selector_Expr); ok {
+	if _, ok := binary.left.derived.(^ast.Implicit_Selector_Expr); ok {
 		either_implicit_selector = true
-	} else if _, ok := binary.right.derived.(ast.Implicit_Selector_Expr); ok {
+	} else if _, ok := binary.right.derived.(^ast.Implicit_Selector_Expr); ok {
 		either_implicit_selector = true
 	}
 
@@ -1439,7 +1477,7 @@ visit_binary_expr :: proc(p: ^Printer, binary: ast.Binary_Expr) {
 	move_line(p, binary.right.pos)
 
 
-	if v, ok := binary.right.derived.(ast.Binary_Expr); ok {
+	if v, ok := binary.right.derived.(^ast.Binary_Expr); ok {
 		visit_binary_expr(p, v)
 	} else {
 		visit_expr(p, binary.right)
@@ -1499,7 +1537,7 @@ visit_signature_list :: proc(p: ^Printer, list: ^ast.Field_List, remove_blank :=
 		named := false
 
 		for name in field.names {
-			if ident, ok := name.derived.(ast.Ident); ok {
+			if ident, ok := name.derived.(^ast.Ident); ok {
 				//for some reason the parser uses _ to mean empty
 				if ident.name != "_" || !remove_blank {
 					named = true

+ 71 - 0
core/os/dir_openbsd.odin

@@ -0,0 +1,71 @@
+package os
+
+import "core:strings"
+import "core:mem"
+
+read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
+	dirp: Dir
+	dirp, err = _fdopendir(fd)
+	if err != ERROR_NONE {
+		return
+	}
+
+	defer _closedir(dirp)
+
+	// XXX OpenBSD
+	dirpath: string
+	dirpath, err = absolute_path_from_handle(fd)
+
+	if err != ERROR_NONE {
+		return
+	}
+
+	defer delete(dirpath)
+
+	n := n
+	size := n
+	if n <= 0 {
+		n = -1
+		size = 100
+	}
+
+	dfi := make([dynamic]File_Info, 0, size, allocator)
+
+	for {
+		entry: Dirent
+		end_of_stream: bool
+		entry, err, end_of_stream = _readdir(dirp)
+		if err != ERROR_NONE {
+			for fi_ in dfi {
+				file_info_delete(fi_, allocator)
+			}
+			delete(dfi)
+			return
+		} else if end_of_stream {
+			break
+		}
+
+		fi_: File_Info
+		filename := cast(string)(transmute(cstring)mem.Raw_Cstring{ data = &entry.name[0] })
+
+		if filename == "." || filename == ".." {
+			continue
+		}
+
+		fullpath := strings.join( []string{ dirpath, filename }, "/", context.temp_allocator)
+		defer delete(fullpath, context.temp_allocator)
+
+		fi_, err = stat(fullpath, allocator)
+		if err != ERROR_NONE {
+			for fi__ in dfi {
+				file_info_delete(fi__, allocator)
+			}
+			delete(dfi)
+			return
+		}
+
+		append(&dfi, fi_)
+	}
+
+	return dfi[:], ERROR_NONE
+}

+ 1 - 0
core/os/os_darwin.odin

@@ -680,6 +680,7 @@ make_directory :: proc(path: string, mode: u32 = 0o775) -> Errno {
 }
 
 exit :: proc "contextless" (code: int) -> ! {
+	runtime._cleanup_runtime_contextless()
 	_unix_exit(i32(code))
 }
 

+ 3 - 2
core/os/os_freebsd.odin

@@ -232,10 +232,10 @@ foreign libc {
 	@(link_name="close")            _unix_close         :: proc(fd: Handle) -> c.int ---
 	@(link_name="read")             _unix_read          :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
 	@(link_name="write")            _unix_write         :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
-	@(link_name="lseek64")          _unix_seek          :: proc(fd: Handle, offset: i64, whence: c.int) -> i64 ---
+	@(link_name="lseek")          _unix_seek          :: proc(fd: Handle, offset: i64, whence: c.int) -> i64 ---
 	@(link_name="gettid")           _unix_gettid        :: proc() -> u64 ---
 	@(link_name="getpagesize")      _unix_getpagesize   :: proc() -> c.int ---
-	@(link_name="stat64")           _unix_stat          :: proc(path: cstring, stat: ^OS_Stat) -> c.int ---
+	@(link_name="stat")           _unix_stat          :: proc(path: cstring, stat: ^OS_Stat) -> c.int ---
 	@(link_name="fstat")            _unix_fstat         :: proc(fd: Handle, stat: ^OS_Stat) -> c.int ---
 	@(link_name="access")           _unix_access        :: proc(path: cstring, mask: c.int) -> c.int ---
 
@@ -419,6 +419,7 @@ set_current_directory :: proc(path: string) -> (err: Errno) {
 }
 
 exit :: proc "contextless" (code: int) -> ! {
+	runtime._cleanup_runtime_contextless()
 	_unix_exit(c.int(code))
 }
 

+ 1 - 0
core/os/os_linux.odin

@@ -802,6 +802,7 @@ set_current_directory :: proc(path: string) -> (err: Errno) {
 }
 
 exit :: proc "contextless" (code: int) -> ! {
+	runtime._cleanup_runtime_contextless()
 	_unix_exit(c.int(code))
 }
 

+ 707 - 0
core/os/os_openbsd.odin

@@ -0,0 +1,707 @@
+package os
+
+foreign import libc "system:c"
+
+import "core:strings"
+import "core:c"
+import "core:runtime"
+
+Handle    :: distinct i32
+Pid       :: distinct i32
+File_Time :: distinct u64
+Errno     :: distinct i32
+
+INVALID_HANDLE :: ~Handle(0)
+
+ERROR_NONE:	Errno: 0
+
+EPERM:		Errno: 1
+ENOENT:		Errno: 2
+ESRCH:		Errno: 3
+EINTR:		Errno: 4
+EIO:		Errno: 5
+ENXIO:		Errno: 6
+E2BIG:		Errno: 7
+ENOEXEC:	Errno: 8
+EBADF:		Errno: 9
+ECHILD:		Errno: 10
+EDEADLK:	Errno: 11
+ENOMEM:		Errno: 12
+EACCES:		Errno: 13
+EFAULT:		Errno: 14
+ENOTBLK:	Errno: 15
+EBUSY:		Errno: 16
+EEXIST:		Errno: 17
+EXDEV:		Errno: 18
+ENODEV:		Errno: 19
+ENOTDIR:	Errno: 20
+EISDIR:		Errno: 21
+EINVAL:		Errno: 22
+ENFILE:		Errno: 23
+EMFILE:		Errno: 24
+ENOTTY:		Errno: 25
+ETXTBSY:	Errno: 26
+EFBIG:		Errno: 27
+ENOSPC:		Errno: 28
+ESPIPE:		Errno: 29
+EROFS:		Errno: 30
+EMLINK:		Errno: 31
+EPIPE:		Errno: 32
+EDOM:		Errno: 33
+ERANGE:		Errno: 34
+EAGAIN:		Errno: 35
+EWOULDBLOCK:	Errno: EAGAIN
+EINPROGRESS:	Errno: 36
+EALREADY:	Errno: 37
+ENOTSOCK:	Errno: 38
+EDESTADDRREQ:	Errno: 39
+EMSGSIZE:	Errno: 40
+EPROTOTYPE:	Errno: 41
+ENOPROTOOPT:	Errno: 42
+EPROTONOSUPPORT: Errno: 43
+ESOCKTNOSUPPORT: Errno: 44
+EOPNOTSUPP:	Errno: 45
+EPFNOSUPPORT:	Errno: 46
+EAFNOSUPPORT:	Errno: 47
+EADDRINUSE:	Errno: 48
+EADDRNOTAVAIL:	Errno: 49
+ENETDOWN:	Errno: 50
+ENETUNREACH:	Errno: 51
+ENETRESET:	Errno: 52
+ECONNABORTED:	Errno: 53
+ECONNRESET:	Errno: 54
+ENOBUFS:	Errno: 55
+EISCONN:	Errno: 56
+ENOTCONN:	Errno: 57
+ESHUTDOWN:	Errno: 58
+ETOOMANYREFS:	Errno: 59
+ETIMEDOUT:	Errno: 60
+ECONNREFUSED:	Errno: 61
+ELOOP:		Errno: 62
+ENAMETOOLONG:	Errno: 63
+EHOSTDOWN:	Errno: 64
+EHOSTUNREACH:	Errno: 65
+ENOTEMPTY:	Errno: 66
+EPROCLIM:	Errno: 67
+EUSERS:		Errno: 68
+EDQUOT:		Errno: 69
+ESTALE:		Errno: 70
+EREMOTE:	Errno: 71
+EBADRPC:	Errno: 72
+ERPCMISMATCH:	Errno: 73
+EPROGUNAVAIL:	Errno: 74
+EPROGMISMATCH:	Errno: 75
+EPROCUNAVAIL:	Errno: 76
+ENOLCK:		Errno: 77
+ENOSYS:		Errno: 78
+EFTYPE:		Errno: 79
+EAUTH:		Errno: 80
+ENEEDAUTH:	Errno: 81
+EIPSEC:		Errno: 82
+ENOATTR:	Errno: 83
+EILSEQ:		Errno: 84
+ENOMEDIUM:	Errno: 85
+EMEDIUMTYPE:	Errno: 86
+EOVERFLOW:	Errno: 87
+ECANCELED:	Errno: 88
+EIDRM:		Errno: 89
+ENOMSG:		Errno: 90
+ENOTSUP:	Errno: 91
+EBADMSG:	Errno: 92
+ENOTRECOVERABLE: Errno: 93
+EOWNERDEAD:	Errno: 94
+EPROTO:		Errno: 95
+
+O_RDONLY   :: 0x00000
+O_WRONLY   :: 0x00001
+O_RDWR     :: 0x00002
+O_NONBLOCK :: 0x00004
+O_APPEND   :: 0x00008
+O_ASYNC    :: 0x00040
+O_SYNC     :: 0x00080
+O_CREATE   :: 0x00200
+O_TRUNC    :: 0x00400
+O_EXCL     :: 0x00800
+O_NOCTTY   :: 0x08000
+O_CLOEXEC  :: 0x10000
+
+SEEK_SET :: 0
+SEEK_CUR :: 1
+SEEK_END :: 2
+
+RTLD_LAZY     :: 0x001
+RTLD_NOW      :: 0x002
+RTLD_LOCAL    :: 0x000
+RTLD_GLOBAL   :: 0x100
+RTLD_TRACE    :: 0x200
+RTLD_NODELETE :: 0x400
+
+MAX_PATH :: 1024
+
+// "Argv" arguments converted to Odin strings
+args := _alloc_command_line_arguments()
+
+pid_t     :: i32
+time_t    :: i64
+mode_t    :: u32
+dev_t     :: i32
+ino_t     :: u64
+nlink_t   :: u32
+uid_t     :: u32
+gid_t     :: u32
+off_t     :: i64
+blkcnt_t  :: u64
+blksize_t :: i32
+
+Unix_File_Time :: struct {
+	seconds:     time_t,
+	nanoseconds: c.long,
+}
+
+OS_Stat :: struct {
+	mode: mode_t,			// inode protection mode
+	device_id: dev_t,		// inode's device
+	serial: ino_t,			// inode's number
+	nlink: nlink_t,			// number of hard links
+	uid: uid_t,			// user ID of the file's owner
+	gid: gid_t,			// group ID of the file's group
+	rdev: dev_t,			// device type
+
+	last_access: Unix_File_Time,	// time of last access
+	modified: Unix_File_Time,	// time of last data modification
+	status_change: Unix_File_Time,	// time of last file status change
+
+	size: off_t,			// file size, in bytes
+	blocks: blkcnt_t,		// blocks allocated for file
+	block_size:	blksize_t,	// optimal blocksize for I/O
+
+	flags:		u32,		// user defined flags for file
+	gen:		u32,		// file generation number
+	birthtime:	Unix_File_Time,	// time of file creation
+}
+
+MAXNAMLEN :: 255
+
+// NOTE(laleksic, 2021-01-21): Comment and rename these to match OS_Stat above
+Dirent :: struct {
+	ino:      ino_t,	// file number of entry
+	off:      off_t,	// offset after this entry
+	reclen:   u16,		// length of this record
+	type:     u8,		// file type
+	namlen:   u8,		// length of string in name
+	_padding: [4]u8,
+	name:     [MAXNAMLEN + 1]byte, // name
+}
+
+Dir :: distinct rawptr // DIR*
+
+// File type
+S_IFMT   :: 0o170000 // Type of file mask
+S_IFIFO  :: 0o010000 // Named pipe (fifo)
+S_IFCHR  :: 0o020000 // Character special
+S_IFDIR  :: 0o040000 // Directory
+S_IFBLK  :: 0o060000 // Block special
+S_IFREG  :: 0o100000 // Regular
+S_IFLNK  :: 0o120000 // Symbolic link
+S_IFSOCK :: 0o140000 // Socket
+S_ISVTX  :: 0o001000 // Save swapped text even after use
+
+// File mode
+	// Read, write, execute/search by owner
+S_IRWXU :: 0o0700 // RWX mask for owner
+S_IRUSR :: 0o0400 // R for owner
+S_IWUSR :: 0o0200 // W for owner
+S_IXUSR :: 0o0100 // X for owner
+
+	// Read, write, execute/search by group
+S_IRWXG :: 0o0070 // RWX mask for group
+S_IRGRP :: 0o0040 // R for group
+S_IWGRP :: 0o0020 // W for group
+S_IXGRP :: 0o0010 // X for group
+
+	// Read, write, execute/search by others
+S_IRWXO :: 0o0007 // RWX mask for other
+S_IROTH :: 0o0004 // R for other
+S_IWOTH :: 0o0002 // W for other
+S_IXOTH :: 0o0001 // X for other
+
+S_ISUID :: 0o4000 // Set user id on execution
+S_ISGID :: 0o2000 // Set group id on execution
+S_ISTXT :: 0o1000 // Sticky bit
+
+S_ISLNK  :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK  }
+S_ISREG  :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG  }
+S_ISDIR  :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR  }
+S_ISCHR  :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR  }
+S_ISBLK  :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK  }
+S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO  }
+S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK }
+
+F_OK :: 0x00 // Test for file existance
+X_OK :: 0x01 // Test for execute permission
+W_OK :: 0x02 // Test for write permission
+R_OK :: 0x04 // Test for read permission
+
+AT_FDCWD            :: -100
+AT_EACCESS          :: 0x01
+AT_SYMLINK_NOFOLLOW :: 0x02
+AT_SYMLINK_FOLLOW   :: 0x04
+AT_REMOVEDIR        :: 0x08
+
+@(default_calling_convention="c")
+foreign libc {
+	@(link_name="__errno")	__errno		:: proc() -> ^int ---
+
+	@(link_name="fork")	_unix_fork	:: proc() -> pid_t ---
+	@(link_name="getthrid")	_unix_getthrid	:: proc() -> int ---
+
+	@(link_name="open")	_unix_open	:: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---
+	@(link_name="close")	_unix_close	:: proc(fd: Handle) -> c.int ---
+	@(link_name="read")	_unix_read	:: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
+	@(link_name="write")	_unix_write	:: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
+	@(link_name="lseek")	_unix_seek	:: proc(fd: Handle, offset: off_t, whence: c.int) -> off_t ---
+	@(link_name="stat")	_unix_stat	:: proc(path: cstring, sb: ^OS_Stat) -> c.int ---
+	@(link_name="fstat")	_unix_fstat	:: proc(fd: Handle, sb: ^OS_Stat) -> c.int ---
+	@(link_name="lstat")	_unix_lstat	:: proc(path: cstring, sb: ^OS_Stat) -> c.int ---
+	@(link_name="readlink")	_unix_readlink	:: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t ---
+	@(link_name="access")	_unix_access	:: proc(path: cstring, mask: c.int) -> c.int ---
+	@(link_name="getcwd")	_unix_getcwd	:: proc(buf: cstring, len: c.size_t) -> cstring ---
+	@(link_name="chdir")	_unix_chdir	:: proc(path: cstring) -> c.int ---
+	@(link_name="rename")	_unix_rename	:: proc(old, new: cstring) -> c.int ---
+	@(link_name="unlink")	_unix_unlink	:: proc(path: cstring) -> c.int ---
+	@(link_name="rmdir")	_unix_rmdir	:: proc(path: cstring) -> c.int ---
+	@(link_name="mkdir")	_unix_mkdir	:: proc(path: cstring, mode: mode_t) -> c.int ---
+
+	@(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int ---
+	@(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir ---
+	@(link_name="closedir")	_unix_closedir	:: proc(dirp: Dir) -> c.int ---
+	@(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) ---
+	@(link_name="readdir_r") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int ---
+
+	@(link_name="malloc")	_unix_malloc	:: proc(size: c.size_t) -> rawptr ---
+	@(link_name="calloc")	_unix_calloc	:: proc(num, size: c.size_t) -> rawptr ---
+	@(link_name="free")	_unix_free	:: proc(ptr: rawptr) ---
+	@(link_name="realloc")	_unix_realloc	:: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
+
+	@(link_name="getenv")	_unix_getenv	:: proc(cstring) -> cstring ---
+	@(link_name="realpath")	_unix_realpath	:: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
+
+	@(link_name="exit")	_unix_exit	:: proc(status: c.int) -> ! ---
+
+	@(link_name="dlopen")	_unix_dlopen	:: proc(filename: cstring, flags: c.int) -> rawptr ---
+	@(link_name="dlsym")	_unix_dlsym	:: proc(handle: rawptr, symbol: cstring) -> rawptr ---
+	@(link_name="dlclose")	_unix_dlclose	:: proc(handle: rawptr) -> c.int ---
+	@(link_name="dlerror")	_unix_dlerror	:: proc() -> cstring ---
+}
+
+is_path_separator :: proc(r: rune) -> bool {
+	return r == '/'
+}
+
+get_last_error :: proc() -> int {
+	return __errno()^
+}
+
+fork :: proc() -> (Pid, Errno) {
+	pid := _unix_fork()
+	if pid == -1 {
+		return Pid(-1), Errno(get_last_error())
+	}
+	return Pid(pid), ERROR_NONE
+}
+
+open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
+	cstr := strings.clone_to_cstring(path, context.temp_allocator)
+	handle := _unix_open(cstr, c.int(flags), c.int(mode))
+	if handle == -1 {
+		return INVALID_HANDLE, Errno(get_last_error())
+	}
+	return handle, ERROR_NONE
+}
+
+close :: proc(fd: Handle) -> Errno {
+	result := _unix_close(fd)
+	if result == -1 {
+		return Errno(get_last_error())
+	}
+	return ERROR_NONE
+}
+
+read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
+	bytes_read := _unix_read(fd, &data[0], c.size_t(len(data)))
+	if bytes_read == -1 {
+		return -1, Errno(get_last_error())
+	}
+	return int(bytes_read), ERROR_NONE
+}
+
+write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
+	if len(data) == 0 {
+		return 0, ERROR_NONE
+	}
+	bytes_written := _unix_write(fd, &data[0], c.size_t(len(data)))
+	if bytes_written == -1 {
+		return -1, Errno(get_last_error())
+	}
+	return int(bytes_written), ERROR_NONE
+}
+
+seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
+	res := _unix_seek(fd, offset, c.int(whence))
+	if res == -1 {
+		return -1, Errno(get_last_error())
+	}
+	return res, ERROR_NONE
+}
+
+file_size :: proc(fd: Handle) -> (i64, Errno) {
+	s, err := _fstat(fd)
+	if err != ERROR_NONE {
+		return -1, err
+	}
+	return s.size, ERROR_NONE
+}
+
+rename :: proc(old_path, new_path: string) -> Errno {
+	old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator)
+	new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator)
+	res := _unix_rename(old_path_cstr, new_path_cstr)
+	if res == -1 {
+		return Errno(get_last_error())
+	}
+	return ERROR_NONE
+}
+
+remove :: proc(path: string) -> Errno {
+	path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+	res := _unix_unlink(path_cstr)
+	if res == -1 {
+		return Errno(get_last_error())
+	}
+	return ERROR_NONE
+}
+
+make_directory :: proc(path: string, mode: mode_t = 0o775) -> Errno {
+	path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+	res := _unix_mkdir(path_cstr, mode)
+	if res == -1 {
+		return Errno(get_last_error())
+	}
+	return ERROR_NONE
+}
+
+remove_directory :: proc(path: string) -> Errno {
+	path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+	res := _unix_rmdir(path_cstr)
+	if res == -1 {
+		return Errno(get_last_error())
+	}
+	return ERROR_NONE
+}
+
+is_file_handle :: proc(fd: Handle) -> bool {
+	s, err := _fstat(fd)
+	if err != ERROR_NONE {
+		return false
+	}
+	return S_ISREG(s.mode)
+}
+
+is_file_path :: proc(path: string, follow_links: bool = true) -> bool {
+	s: OS_Stat
+	err: Errno
+	if follow_links {
+		s, err = _stat(path)
+	} else {
+		s, err = _lstat(path)
+	}
+	if err != ERROR_NONE {
+		return false
+	}
+	return S_ISREG(s.mode)
+}
+
+is_dir_handle :: proc(fd: Handle) -> bool {
+	s, err := _fstat(fd)
+	if err != ERROR_NONE {
+		return false
+	}
+	return S_ISDIR(s.mode)
+}
+
+is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
+	s: OS_Stat
+	err: Errno
+	if follow_links {
+		s, err = _stat(path)
+	} else {
+		s, err = _lstat(path)
+	}
+	if err != ERROR_NONE {
+		return false
+	}
+	return S_ISDIR(s.mode)
+}
+
+is_file :: proc {is_file_path, is_file_handle}
+is_dir :: proc {is_dir_path, is_dir_handle}
+
+// NOTE(bill): Uses startup to initialize it
+
+stdin:  Handle = 0
+stdout: Handle = 1
+stderr: Handle = 2
+
+/* TODO(zangent): Implement these!                                                                                
+last_write_time :: proc(fd: Handle) -> File_Time {}                                                               
+last_write_time_by_name :: proc(name: string) -> File_Time {}                                                     
+*/
+last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
+	s, err := _fstat(fd)
+	if err != ERROR_NONE {
+		return 0, err
+	}
+	modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
+	return File_Time(modified), ERROR_NONE
+}
+
+last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
+	s, err := _stat(name)
+	if err != ERROR_NONE {
+		return 0, err
+	}
+	modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
+	return File_Time(modified), ERROR_NONE
+}
+
+@private
+_stat :: proc(path: string) -> (OS_Stat, Errno) {
+	cstr := strings.clone_to_cstring(path, context.temp_allocator)
+
+	// deliberately uninitialized
+	s: OS_Stat = ---
+	res := _unix_stat(cstr, &s)
+	if res == -1 {
+		return s, Errno(get_last_error())
+	}
+	return s, ERROR_NONE
+}
+
+@private
+_lstat :: proc(path: string) -> (OS_Stat, Errno) {
+	cstr := strings.clone_to_cstring(path, context.temp_allocator)
+
+	// deliberately uninitialized
+	s: OS_Stat = ---
+	res := _unix_lstat(cstr, &s)
+	if res == -1 {
+		return s, Errno(get_last_error())
+	}
+	return s, ERROR_NONE
+}
+
+@private
+_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) {
+	// deliberately uninitialized
+	s: OS_Stat = ---
+	res := _unix_fstat(fd, &s)
+	if res == -1 {
+		return s, Errno(get_last_error())
+	}
+	return s, ERROR_NONE
+}
+
+@private
+_fdopendir :: proc(fd: Handle) -> (Dir, Errno) {
+	dirp := _unix_fdopendir(fd)
+	if dirp == cast(Dir)nil {
+		return nil, Errno(get_last_error())
+	}
+	return dirp, ERROR_NONE
+}
+
+@private
+_closedir :: proc(dirp: Dir) -> Errno {
+	rc := _unix_closedir(dirp)
+	if rc != 0 {
+		return Errno(get_last_error())
+	}
+	return ERROR_NONE
+}
+
+@private
+_rewinddir :: proc(dirp: Dir) {
+	_unix_rewinddir(dirp)
+}
+
+@private
+_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) {
+	result: ^Dirent
+	rc := _unix_readdir_r(dirp, &entry, &result)
+
+	if rc != 0 {
+		err = Errno(get_last_error())
+		return
+	}
+	err = ERROR_NONE
+
+	if result == nil {
+		end_of_stream = true
+		return
+	}
+
+	return
+}
+
+@private
+_readlink :: proc(path: string) -> (string, Errno) {
+	path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+
+	bufsz : uint = MAX_PATH
+	buf := make([]byte, MAX_PATH)
+	for {
+		rc := _unix_readlink(path_cstr, &(buf[0]), bufsz)
+		if rc == -1 {
+			delete(buf)
+			return "", Errno(get_last_error())
+		} else if rc == int(bufsz) {
+			bufsz += MAX_PATH
+			delete(buf)
+			buf = make([]byte, bufsz)
+		} else {
+			return strings.string_from_ptr(&buf[0], rc), ERROR_NONE
+		}	
+	}
+	unreachable()
+}
+
+// XXX OpenBSD
+absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) {
+	return "", Errno(ENOSYS)
+}
+
+absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
+	rel := rel
+	if rel == "" {
+		rel = "."
+	}
+
+	rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator)
+
+	path_ptr := _unix_realpath(rel_cstr, nil)
+	if path_ptr == nil {
+		return "", Errno(get_last_error())
+	}
+	defer _unix_free(path_ptr)
+
+	path_cstr := transmute(cstring)path_ptr
+	path = strings.clone( string(path_cstr) )
+
+	return path, ERROR_NONE
+}
+
+access :: proc(path: string, mask: int) -> (bool, Errno) {
+	cstr := strings.clone_to_cstring(path, context.temp_allocator)
+	res := _unix_access(cstr, c.int(mask))
+	if res == -1 {
+		return false, Errno(get_last_error())
+	}
+	return true, ERROR_NONE
+}
+
+heap_alloc :: proc(size: int) -> rawptr {
+	assert(size >= 0)
+	return _unix_calloc(1, c.size_t(size))
+}
+
+heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
+	// NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on
+	// POSIX platforms. Ensure your caller takes this into account.
+	return _unix_realloc(ptr, c.size_t(new_size))
+}
+
+heap_free :: proc(ptr: rawptr) {
+	_unix_free(ptr)
+}
+
+getenv :: proc(name: string) -> (string, bool) {
+	path_str := strings.clone_to_cstring(name, context.temp_allocator)
+	cstr := _unix_getenv(path_str)
+	if cstr == nil {
+		return "", false
+	}
+	return string(cstr), true
+}
+
+get_current_directory :: proc() -> string {
+	buf := make([dynamic]u8, MAX_PATH)
+	for {
+		cwd := _unix_getcwd(cstring(raw_data(buf)), c.size_t(len(buf)))
+		if cwd != nil {
+			return string(cwd)
+		}
+		if Errno(get_last_error()) != ERANGE {
+			return ""
+		}
+		resize(&buf, len(buf) + MAX_PATH)
+	}
+	unreachable()
+}
+
+set_current_directory :: proc(path: string) -> (err: Errno) {
+	cstr := strings.clone_to_cstring(path, context.temp_allocator)
+	res := _unix_chdir(cstr)
+	if res == -1 {
+		return Errno(get_last_error())
+	}
+	return ERROR_NONE
+}
+
+exit :: proc "contextless" (code: int) -> ! {
+	runtime._cleanup_runtime_contextless()
+	_unix_exit(c.int(code))
+}
+
+current_thread_id :: proc "contextless" () -> int {
+	return _unix_getthrid()
+}
+
+dlopen :: proc(filename: string, flags: int) -> rawptr {
+	cstr := strings.clone_to_cstring(filename, context.temp_allocator)
+	handle := _unix_dlopen(cstr, c.int(flags))
+	return handle
+}
+dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
+	assert(handle != nil)
+	cstr := strings.clone_to_cstring(symbol, context.temp_allocator)
+	proc_handle := _unix_dlsym(handle, cstr)
+	return proc_handle
+}
+dlclose :: proc(handle: rawptr) -> bool {
+	assert(handle != nil)
+	return _unix_dlclose(handle) == 0
+}
+dlerror :: proc() -> string {
+	return string(_unix_dlerror())
+}
+
+get_page_size :: proc() -> int {
+	// NOTE(tetra): The page size never changes, so why do anything complicated
+	// if we don't have to.
+	@static page_size := -1
+	if page_size != -1 {
+		return page_size
+	}
+
+	page_size = int(_unix_getpagesize())
+	return page_size
+}
+
+
+_alloc_command_line_arguments :: proc() -> []string {
+	res := make([]string, len(runtime.args__))
+	for arg, i in runtime.args__ {
+		res[i] = string(arg)
+	}
+	return res
+}

+ 2 - 0
core/os/os_wasi.odin

@@ -1,6 +1,7 @@
 package os
 
 import "core:sys/wasm/wasi"
+import "core:runtime"
 
 Handle :: distinct i32
 Errno :: distinct i32
@@ -93,5 +94,6 @@ heap_free :: proc(ptr: rawptr) {
 
 
 exit :: proc "contextless" (code: int) -> ! {
+	runtime._cleanup_runtime_contextless()
 	wasi.proc_exit(wasi.exitcode_t(code))
 }

+ 2 - 0
core/os/os_windows.odin

@@ -2,6 +2,7 @@
 package os
 
 import win32 "core:sys/windows"
+import "core:runtime"
 
 Handle    :: distinct uintptr
 File_Time :: distinct u64
@@ -128,6 +129,7 @@ get_page_size :: proc() -> int {
 
 
 exit :: proc "contextless" (code: int) -> ! {
+	runtime._cleanup_runtime_contextless()
 	win32.ExitProcess(win32.DWORD(code))
 }
 

+ 1 - 1
core/os/stat_unix.odin

@@ -1,4 +1,4 @@
-//+build linux, darwin, freebsd
+//+build linux, darwin, openbsd
 package os
 
 import "core:time"

+ 10 - 33
core/os/stat_windows.odin

@@ -132,26 +132,11 @@ cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 {
 
 @(private)
 cleanpath_from_handle :: proc(fd: Handle) -> (string, Errno) {
-	if fd == 0 {
-		return "", ERROR_INVALID_HANDLE
-	}
-	h := win32.HANDLE(fd)
-
-	MAX_PATH := win32.DWORD(260) + 1
-	buf: []u16
-	for {
-		buf = make([]u16, MAX_PATH, context.temp_allocator)
-		err := win32.GetFinalPathNameByHandleW(h, raw_data(buf), MAX_PATH, 0)
-		switch Errno(err) {
-		case ERROR_PATH_NOT_FOUND, ERROR_INVALID_PARAMETER:
-			return "", Errno(err)
-		case ERROR_NOT_ENOUGH_MEMORY:
-			MAX_PATH = MAX_PATH*2 + 1
-			continue
-		}
-		break
+	buf, err := cleanpath_from_handle_u16(fd)
+	if err != 0 {
+		return "", err
 	}
-	return cleanpath_from_buf(buf), ERROR_NONE
+	return win32.utf16_to_utf8(buf, context.allocator), err
 }
 @(private)
 cleanpath_from_handle_u16 :: proc(fd: Handle) -> ([]u16, Errno) {
@@ -160,21 +145,13 @@ cleanpath_from_handle_u16 :: proc(fd: Handle) -> ([]u16, Errno) {
 	}
 	h := win32.HANDLE(fd)
 
-	MAX_PATH := win32.DWORD(260) + 1
-	buf: []u16
-	for {
-		buf = make([]u16, MAX_PATH, context.temp_allocator)
-		err := win32.GetFinalPathNameByHandleW(h, raw_data(buf), MAX_PATH, 0)
-		switch Errno(err) {
-		case ERROR_PATH_NOT_FOUND, ERROR_INVALID_PARAMETER:
-			return nil, Errno(err)
-		case ERROR_NOT_ENOUGH_MEMORY:
-			MAX_PATH = MAX_PATH*2 + 1
-			continue
-		}
-		break
+	n := win32.GetFinalPathNameByHandleW(h, nil, 0, 0)
+	if n == 0 {
+		return nil, Errno(win32.GetLastError())
 	}
-	return cleanpath_strip_prefix(buf), ERROR_NONE
+	buf := make([]u16, max(n, win32.DWORD(260))+1, context.temp_allocator)
+	buf_len := win32.GetFinalPathNameByHandleW(h, raw_data(buf), n, 0)
+	return buf[:buf_len], ERROR_NONE
 }
 @(private)
 cleanpath_from_buf :: proc(buf: []u16) -> string {

+ 6 - 1
core/path/filepath/path_unix.odin

@@ -1,4 +1,4 @@
-//+build linux, darwin, freebsd
+//+build linux, darwin, freebsd, openbsd
 package filepath
 
 when ODIN_OS == .Darwin {
@@ -59,6 +59,11 @@ when ODIN_OS == .Darwin {
 	foreign libc {
 		@(link_name="__error")          __error :: proc() -> ^i32 ---
 	}
+} else when ODIN_OS == .OpenBSD {
+	@(private)
+	foreign libc {
+		@(link_name="__errno")		__error :: proc() -> ^i32 ---
+	}
 } else {
 	@(private)
 	foreign libc {

+ 13 - 1
core/reflect/reflect.odin

@@ -365,6 +365,19 @@ index :: proc(val: any, i: int, loc := #caller_location) -> any {
 	return nil
 }
 
+deref :: proc(val: any) -> any {
+	if val != nil {
+		ti := type_info_base(type_info_of(val.id))
+		if info, ok := ti.variant.(Type_Info_Pointer); ok {
+			return any{
+				(^rawptr)(val.data)^,
+				info.elem.id,
+			}
+		}
+	}
+	return val
+}
+
 
 
 // Struct_Tag represents the type of the string of a struct field
@@ -680,7 +693,6 @@ union_variant_typeid :: proc(a: any) -> typeid {
 		return nil
 	}
 	panic("expected a union to reflect.union_variant_typeid")
-
 }
 
 get_union_variant_raw_tag :: proc(a: any) -> i64 {

+ 5 - 0
core/runtime/core.odin

@@ -459,6 +459,11 @@ _cleanup_runtime :: proc() {
 	default_temp_allocator_destroy(&global_default_temp_allocator_data)
 }
 
+_cleanup_runtime_contextless :: proc "contextless" () {
+	context = default_context()
+	_cleanup_runtime()
+}
+
 
 /////////////////////////////
 /////////////////////////////

+ 1 - 1
core/runtime/entry_unix.odin

@@ -1,5 +1,5 @@
 //+private
-//+build linux, darwin, freebsd
+//+build linux, darwin, freebsd, openbsd
 package runtime
 
 import "core:intrinsics"

+ 96 - 5
core/slice/slice.odin

@@ -304,7 +304,7 @@ filter :: proc(s: $S/[]$U, f: proc(U) -> bool, allocator := context.allocator) -
 	return r[:]
 }
 
-scanner :: proc (s: $S/[]$U, initializer: $V, f: proc(V, U)->V, allocator := context.allocator) -> []V {
+scanner :: proc (s: $S/[]$U, initializer: $V, f: proc(V, U) -> V, allocator := context.allocator) -> []V {
 	if len(s) == 0 { return {} }
 
 	res := make([]V, len(s), allocator)
@@ -344,15 +344,106 @@ max :: proc(s: $S/[]$T) -> (res: T, ok: bool) where intrinsics.type_is_ordered(T
 	return
 }
 
+min_max :: proc(s: $S/[]$T) -> (min, max: T, ok: bool) where intrinsics.type_is_ordered(T) {
+	if len(s) != 0 {
+		min, max = s[0], s[0]
+		ok = true
+		for v in s[1:] {
+			min = builtin.min(min, v)
+			max = builtin.max(max, v)
+		}
+	}
+	return
+}
+
+any_of :: proc(s: $S/[]$T, value: T) -> bool where intrinsics.type_is_comparable(T) {
+	for v in s {
+		if v == value {
+			return true
+		}
+	}
+	return false
+}
+
+none_of :: proc(s: $S/[]$T, value: T) -> bool where intrinsics.type_is_comparable(T) {
+	for v in s {
+		if v == value {
+			return false
+		}
+	}
+	return true
+}
+
+all_of :: proc(s: $S/[]$T, value: T) -> bool where intrinsics.type_is_comparable(T) {
+	if len(s) == 0 {
+		return false
+	}
+	for v in s {
+		if v != value {
+			return false
+		}
+	}
+	return true
+}
+
 
-dot_product :: proc(a, b: $S/[]$T) -> T
+any_of_proc :: proc(s: $S/[]$T, f: proc(T) -> bool) -> bool {
+	for v in s {
+		if f(v) {
+			return true
+		}
+	}
+	return false
+}
+
+none_of_proc :: proc(s: $S/[]$T, f: proc(T) -> bool) -> bool {
+	for v in s {
+		if f(v) {
+			return false
+		}
+	}
+	return true
+}
+
+all_of_proc :: proc(s: $S/[]$T, f: proc(T) -> bool) -> bool {
+	if len(s) == 0 {
+		return false
+	}
+	for v in s {
+		if !f(v) {
+			return false
+		}
+	}
+	return true
+}
+
+
+count :: proc(s: $S/[]$T, value: T) -> (n: int) where intrinsics.type_is_comparable(T) {
+	for v in s {
+		if v == value {
+			n += 1
+		}
+	}
+	return
+}
+
+count_proc :: proc(s: $S/[]$T, f: proc(T) -> bool) -> (n: int) {
+	for v in s {
+		if f(v) {
+			n += 1
+		}
+	}
+	return
+}
+
+
+dot_product :: proc(a, b: $S/[]$T) -> (r: T, ok: bool)
 	where intrinsics.type_is_numeric(T) {
 	if len(a) != len(b) {
-		panic("slice.dot_product: slices of unequal length")
+		return
 	}
-	r: T
 	#no_bounds_check for _, i in a {
 		r += a[i] * b[i]
 	}
-	return r
+	return r, true
 }

+ 1 - 0
core/strconv/strconv.odin

@@ -895,6 +895,7 @@ unquote_string :: proc(lit: string, allocator := context.allocator) -> (res: str
 	if s == `""` {
 		return "", false, true
 	}
+	s = s[1:len(s)-1]
 
 	if contains_rune(s, '\n') >= 0 {
 		return s, false, false

+ 1 - 1
core/sync/channel_unix.odin

@@ -1,4 +1,4 @@
-// +build linux, darwin, freebsd
+// +build linux, darwin, freebsd, openbsd
 package sync
 
 import "core:time"

+ 78 - 0
core/sync/sync2/futex_openbsd.odin

@@ -0,0 +1,78 @@
+//+private
+//+build openbsd
+package sync2
+
+import "core:c"
+import "core:os"
+import "core:time"
+
+FUTEX_WAIT :: 1
+FUTEX_WAKE :: 2
+
+FUTEX_PRIVATE_FLAG :: 128
+
+FUTEX_WAIT_PRIVATE :: (FUTEX_WAIT | FUTEX_PRIVATE_FLAG)
+FUTEX_WAKE_PRIVATE :: (FUTEX_WAKE | FUTEX_PRIVATE_FLAG)
+
+foreign import libc "system:c"
+
+foreign libc {
+	@(link_name="futex")
+	_unix_futex :: proc "c" (f: ^Futex, op: c.int, val: u32, timeout: rawptr) -> c.int ---
+}
+
+_futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
+	res := _unix_futex(f, FUTEX_WAIT_PRIVATE, expected, nil)
+
+	if res != -1 {
+		return true
+	}
+
+	if os.Errno(os.get_last_error()) == os.ETIMEDOUT {
+		return false
+	}
+
+	panic("futex_wait failure")
+}
+
+_futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
+	if duration <= 0 {
+		return false
+	}
+
+	timespec_t :: struct {
+		tv_sec:  c.long,
+		tv_nsec: c.long,
+        }
+
+	res := _unix_futex(f, FUTEX_WAIT_PRIVATE, expected, &timespec_t{
+		tv_sec  = (c.long)(duration/1e9),
+		tv_nsec = (c.long)(duration%1e9),
+	})
+
+	if res != -1 {
+		return true
+	}
+
+	if os.Errno(os.get_last_error()) == os.ETIMEDOUT {
+		return false
+	}
+
+	panic("futex_wait_with_timeout failure")
+}
+
+_futex_signal :: proc(f: ^Futex) {
+	res := _unix_futex(f, FUTEX_WAKE_PRIVATE, 1, nil)
+
+	if res == -1 {
+		panic("futex_wake_single failure")
+	}
+}
+
+_futex_broadcast :: proc(f: ^Futex)  {
+	res := _unix_futex(f, FUTEX_WAKE_PRIVATE, u32(max(i32)), nil)
+
+	if res == -1 {
+		panic("_futex_wake_all failure")
+	}
+}

+ 1 - 1
core/sync/sync2/primitives.odin

@@ -11,7 +11,7 @@ current_thread_id :: proc "contextless" () -> int {
 //
 // A Mutex must not be copied after first use
 Mutex :: struct {
-	impl: _Mutex `This is a tag`,
+	impl: _Mutex,
 }
 
 // mutex_lock locks m

+ 9 - 0
core/sync/sync2/primitives_openbsd.odin

@@ -0,0 +1,9 @@
+//+build openbsd
+//+private
+package sync2
+
+import "core:os"
+
+_current_thread_id :: proc "contextless" () -> int {
+	return os.current_thread_id()
+}

+ 1 - 1
core/sync/sync2/primitives_pthreads.odin

@@ -1,4 +1,4 @@
-//+build linux, freebsd
+//+build linux, freebsd, openbsd
 //+private
 package sync2
 

+ 36 - 0
core/sync/sync_openbsd.odin

@@ -0,0 +1,36 @@
+package sync
+
+import "core:sys/unix"
+import "core:os"
+
+current_thread_id :: proc "contextless" () -> int {
+	return os.current_thread_id()
+}
+
+// The Darwin docs say it best:
+// A semaphore is much like a lock, except that a finite number of threads can hold it simultaneously.
+// Semaphores can be thought of as being much like piles of tokens; multiple threads can take these tokens, 
+// but when there are none left, a thread must wait until another thread returns one.
+Semaphore :: struct #align 16 {
+	handle: unix.sem_t,
+}
+
+semaphore_init :: proc(s: ^Semaphore, initial_count := 0) {
+	assert(unix.sem_init(&s.handle, 0, u32(initial_count)) == 0)
+}
+
+semaphore_destroy :: proc(s: ^Semaphore) {
+	assert(unix.sem_destroy(&s.handle) == 0)
+	s.handle = {}
+}
+
+semaphore_post :: proc(s: ^Semaphore, count := 1) {
+    // NOTE: SPEED: If there's one syscall to do this, we should use it instead of the loop.
+    for in 0..<count {
+	    assert(unix.sem_post(&s.handle) == 0)
+    }
+}
+
+semaphore_wait_for :: proc(s: ^Semaphore) {
+	assert(unix.sem_wait(&s.handle) == 0)
+}

+ 1 - 1
core/sync/sync_unix.odin

@@ -1,4 +1,4 @@
-// +build linux, darwin, freebsd
+// +build linux, darwin, freebsd, openbsd
 package sync
 
 import "core:sys/unix"

+ 65 - 0
core/sys/unix/pthread_openbsd.odin

@@ -0,0 +1,65 @@
+//+build openbsd
+package unix
+
+import "core:c"
+
+pthread_t             :: distinct rawptr
+pthread_attr_t        :: distinct rawptr
+pthread_mutex_t       :: distinct rawptr
+pthread_mutexattr_t   :: distinct rawptr
+pthread_cond_t        :: distinct rawptr
+pthread_condattr_t    :: distinct rawptr
+pthread_rwlock_t      :: distinct rawptr
+pthread_rwlockattr_t  :: distinct rawptr
+pthread_barrier_t     :: distinct rawptr
+pthread_barrierattr_t :: distinct rawptr
+pthread_spinlock_t    :: distinct rawptr
+
+pthread_key_t  :: distinct c.int
+pthread_once_t :: struct {
+	state: c.int,
+	mutex: pthread_mutex_t,
+}
+
+PTHREAD_MUTEX_ERRORCHECK :: 1
+PTHREAD_MUTEX_RECURSIVE  :: 2
+PTHREAD_MUTEX_NORMAL     :: 3
+PTHREAD_MUTEX_STRICT_NP  :: 4
+
+PTHREAD_DETACHED      :: 0x1
+PTHREAD_SCOPE_SYSTEM  :: 0x2
+PTHREAD_INHERIT_SCHED :: 0x4
+PTHREAD_NOFLOAT       :: 0x8
+
+PTHREAD_CREATE_DETACHED :: PTHREAD_DETACHED
+PTHREAD_CREATE_JOINABLE :: 0
+PTHREAD_SCOPE_PROCESS   :: 0
+PTHREAD_EXPLICIT_SCHED  :: 0
+
+SCHED_FIFO  :: 1
+SCHED_OTHER :: 2
+SCHED_RR    :: 3
+
+sched_param :: struct {
+	sched_priority: c.int,
+}
+
+sem_t :: distinct rawptr
+
+foreign import libc "system:c"
+
+@(default_calling_convention="c")
+foreign libc {
+	sem_open :: proc(name: cstring, flags: c.int) -> ^sem_t ---
+
+	sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int ---
+	sem_destroy :: proc(sem: ^sem_t) -> c.int ---
+	sem_post :: proc(sem: ^sem_t) -> c.int ---
+	sem_wait :: proc(sem: ^sem_t) -> c.int ---
+	sem_trywait :: proc(sem: ^sem_t) -> c.int ---
+	//sem_timedwait :: proc(sem: ^sem_t, timeout: time.TimeSpec) -> c.int ---
+
+	// NOTE: unclear whether pthread_yield is well-supported on Linux systems,
+	// see https://linux.die.net/man/3/pthread_yield
+	pthread_yield :: proc() ---
+}

+ 1 - 1
core/sys/unix/pthread_unix.odin

@@ -1,4 +1,4 @@
-//+build linux, darwin, freebsd
+//+build linux, darwin, freebsd, openbsd
 package unix
 
 foreign import "system:pthread"

+ 1 - 1
core/thread/thread_unix.odin

@@ -1,4 +1,4 @@
-// +build linux, darwin, freebsd
+// +build linux, darwin, freebsd, openbsd
 // +private
 package thread
 

+ 23 - 11
core/time/time_unix.odin

@@ -1,4 +1,4 @@
-//+build linux, darwin, freebsd
+//+build linux, darwin, freebsd, openbsd
 package time
 
 IS_SUPPORTED :: true // NOTE: Times on Darwin are UTC.
@@ -22,16 +22,28 @@ TimeSpec :: struct {
 	tv_nsec : i64,  /* nanoseconds */
 }
 
-CLOCK_REALTIME           :: 0 // NOTE(tetra): May jump in time, when user changes the system time.
-CLOCK_MONOTONIC          :: 1 // NOTE(tetra): May stand still while system is asleep.
-CLOCK_PROCESS_CPUTIME_ID :: 2
-CLOCK_THREAD_CPUTIME_ID  :: 3
-CLOCK_MONOTONIC_RAW      :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP.
-CLOCK_REALTIME_COARSE    :: 5 // NOTE(tetra): "COARSE" clocks are apparently much faster, but not "fine-grained."
-CLOCK_MONOTONIC_COARSE   :: 6
-CLOCK_BOOTTIME           :: 7 // NOTE(tetra): Same as MONOTONIC, except also including time system was asleep.
-CLOCK_REALTIME_ALARM     :: 8
-CLOCK_BOOTTIME_ALARM     :: 9
+when ODIN_OS == .OpenBSD {
+	CLOCK_REALTIME           :: 0
+	CLOCK_PROCESS_CPUTIME_ID :: 2
+	CLOCK_MONOTONIC          :: 3
+	CLOCK_THREAD_CPUTIME_ID  :: 4
+	CLOCK_UPTIME             :: 5
+	CLOCK_BOOTTIME           :: 6
+
+	// CLOCK_MONOTONIC_RAW doesn't exist, use CLOCK_MONOTONIC
+	CLOCK_MONOTONIC_RAW :: CLOCK_MONOTONIC
+} else {
+	CLOCK_REALTIME           :: 0 // NOTE(tetra): May jump in time, when user changes the system time.
+	CLOCK_MONOTONIC          :: 1 // NOTE(tetra): May stand still while system is asleep.
+	CLOCK_PROCESS_CPUTIME_ID :: 2
+	CLOCK_THREAD_CPUTIME_ID  :: 3
+	CLOCK_MONOTONIC_RAW      :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP.
+	CLOCK_REALTIME_COARSE    :: 5 // NOTE(tetra): "COARSE" clocks are apparently much faster, but not "fine-grained."
+	CLOCK_MONOTONIC_COARSE   :: 6
+	CLOCK_BOOTTIME           :: 7 // NOTE(tetra): Same as MONOTONIC, except also including time system was asleep.
+	CLOCK_REALTIME_ALARM     :: 8
+	CLOCK_BOOTTIME_ALARM     :: 9
+}
 
 // TODO(tetra, 2019-11-05): The original implementation of this package for Darwin used this constants.
 // I do not know if Darwin programmers are used to the existance of these constants or not, so

+ 2 - 0
examples/all/all_main.odin

@@ -54,6 +54,7 @@ import base64         "core:encoding/base64"
 import csv            "core:encoding/csv"
 import hxa            "core:encoding/hxa"
 import json           "core:encoding/json"
+import varint         "core:encoding/varint"
 
 import fmt            "core:fmt"
 import hash           "core:hash"
@@ -153,6 +154,7 @@ _ :: base64
 _ :: csv
 _ :: hxa
 _ :: json
+_ :: varint
 _ :: fmt
 _ :: hash
 _ :: image

+ 1 - 22
examples/all/all_vendor.odin

@@ -1,4 +1,3 @@
-//+build windows
 package all
 
 import botan     "vendor:botan"
@@ -16,24 +15,12 @@ import IMG    "vendor:sdl2/image"
 import MIX    "vendor:sdl2/mixer"
 import TTF    "vendor:sdl2/ttf"
 
-import stb_easy_font "vendor:stb/easy_font"
-import stbi          "vendor:stb/image"
-import stbrp         "vendor:stb/rect_pack"
-import stbtt         "vendor:stb/truetype"
-import stb_vorbis    "vendor:stb/vorbis"
-
 import vk "vendor:vulkan"
 
-import D3D11 "vendor:directx/d3d11"
-import D3D12 "vendor:directx/d3d12"
-import DXGI  "vendor:directx/dxgi"
-
-// note these are technicaly darwin only but they are added to aid with documentation generation
 import NS  "vendor:darwin/Foundation"
 import MTL "vendor:darwin/Metal"
 import CA  "vendor:darwin/QuartzCore"
 
-
 _ :: botan
 _ :: ENet
 _ :: gl
@@ -47,15 +34,7 @@ _ :: SDLNet
 _ :: IMG
 _ :: MIX
 _ :: TTF
-_ :: stb_easy_font
-_ :: stbi
-_ :: stbrp
-_ :: stbtt
-_ :: stb_vorbis
 _ :: vk
-_ :: D3D11
-_ :: D3D12
-_ :: DXGI
 _ :: NS
 _ :: MTL
-_ :: CA
+_ :: CA

+ 10 - 0
examples/all/all_vendor_directx.odin

@@ -0,0 +1,10 @@
+//+build windows
+package all
+
+import D3D11 "vendor:directx/d3d11"
+import D3D12 "vendor:directx/d3d12"
+import DXGI  "vendor:directx/dxgi"
+
+_ :: D3D11
+_ :: D3D12
+_ :: DXGI

+ 15 - 0
examples/all/all_vendor_stl.odin

@@ -0,0 +1,15 @@
+//+build windows, linux
+package all
+
+import stb_easy_font "vendor:stb/easy_font"
+import stbi          "vendor:stb/image"
+import stbrp         "vendor:stb/rect_pack"
+import stbtt         "vendor:stb/truetype"
+import stb_vorbis    "vendor:stb/vorbis"
+
+_ :: stb_easy_font
+_ :: stbi
+_ :: stbrp
+_ :: stbtt
+_ :: stb_vorbis
+

+ 22 - 1
src/bug_report.cpp

@@ -17,6 +17,11 @@
 	#include <sys/sysctl.h>
 #endif
 
+#if defined(GB_SYSTEM_OPENBSD)
+	#include <sys/sysctl.h>
+	#include <sys/utsname.h>
+#endif
+
 /*
 	NOTE(Jeroen): This prints the Windows product edition only, to be called from `print_platform_details`.
 */
@@ -242,6 +247,14 @@ void report_ram_info() {
 		if (sysctl(sysctls, 2, &ram_amount, &val_size, NULL, 0) != -1) {
 			gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1));
 		}
+	#elif defined(GB_SYSTEM_OPENBSD)
+		uint64_t ram_amount;
+		size_t   val_size = sizeof(ram_amount);
+
+		int sysctls[] = { CTL_HW, HW_PHYSMEM64 };
+		if (sysctl(sysctls, 2, &ram_amount, &val_size, NULL, 0) != -1) {
+			gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1));
+		}
 	#else
 		gb_printf("Unknown.\n");
 	#endif
@@ -643,6 +656,14 @@ void print_bug_report_help() {
 		} else {
 			gb_printf("macOS: Unknown\n");
 		}			
+	#elif defined(GB_SYSTEM_OPENBSD)
+		struct utsname un;
+		
+		if (uname(&un) != -1) {
+			gb_printf("%s %s %s %s\n", un.sysname, un.release, un.version, un.machine);
+		} else {
+			gb_printf("OpenBSD: Unknown\n");    
+		}
 	#else
 		gb_printf("Unknown\n");
 
@@ -657,4 +678,4 @@ void print_bug_report_help() {
 		And RAM info.
 	*/
 	report_ram_info();
-}
+}

+ 57 - 12
src/build_settings.cpp

@@ -1,4 +1,4 @@
-#if defined(GB_SYSTEM_FREEBSD)
+#if defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
 #include <sys/types.h>
 #include <sys/sysctl.h>
 #endif
@@ -16,6 +16,7 @@ enum TargetOsKind : u16 {
 	TargetOs_linux,
 	TargetOs_essence,
 	TargetOs_freebsd,
+	TargetOs_openbsd,
 	
 	TargetOs_wasi,
 	TargetOs_js,
@@ -63,6 +64,7 @@ String target_os_names[TargetOs_COUNT] = {
 	str_lit("linux"),
 	str_lit("essence"),
 	str_lit("freebsd"),
+	str_lit("openbsd"),
 	
 	str_lit("wasi"),
 	str_lit("js"),
@@ -310,7 +312,7 @@ bool global_ignore_warnings(void) {
 }
 
 
-gb_global TargetMetrics target_windows_386 = {
+gb_global TargetMetrics target_windows_i386 = {
 	TargetOs_windows,
 	TargetArch_i386,
 	4,
@@ -326,7 +328,7 @@ gb_global TargetMetrics target_windows_amd64 = {
 	str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
 };
 
-gb_global TargetMetrics target_linux_386 = {
+gb_global TargetMetrics target_linux_i386 = {
 	TargetOs_linux,
 	TargetArch_i386,
 	4,
@@ -369,7 +371,7 @@ gb_global TargetMetrics target_darwin_arm64 = {
 	str_lit("e-m:o-i64:64-i128:128-n32:64-S128"), // TODO(bill): Is this correct?
 };
 
-gb_global TargetMetrics target_freebsd_386 = {
+gb_global TargetMetrics target_freebsd_i386 = {
 	TargetOs_freebsd,
 	TargetArch_i386,
 	4,
@@ -386,6 +388,15 @@ gb_global TargetMetrics target_freebsd_amd64 = {
 	str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
 };
 
+gb_global TargetMetrics target_openbsd_amd64 = {
+	TargetOs_openbsd,
+	TargetArch_amd64,
+	8,
+	16,
+	str_lit("x86_64-unknown-openbsd-elf"),
+	str_lit("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"),
+};
+
 gb_global TargetMetrics target_essence_amd64 = {
 	TargetOs_essence,
 	TargetArch_amd64,
@@ -452,13 +463,14 @@ gb_global NamedTargetMetrics named_targets[] = {
 	{ str_lit("darwin_amd64"),        &target_darwin_amd64   },
 	{ str_lit("darwin_arm64"),        &target_darwin_arm64   },
 	{ str_lit("essence_amd64"),       &target_essence_amd64  },
-	{ str_lit("linux_386"),           &target_linux_386      },
+	{ str_lit("linux_i386"),          &target_linux_i386     },
 	{ str_lit("linux_amd64"),         &target_linux_amd64    },
 	{ str_lit("linux_arm64"),         &target_linux_arm64    },
-	{ str_lit("windows_386"),         &target_windows_386    },
+	{ str_lit("windows_i386"),        &target_windows_i386   },
 	{ str_lit("windows_amd64"),       &target_windows_amd64  },
-	{ str_lit("freebsd_386"),         &target_freebsd_386    },
+	{ str_lit("freebsd_i386"),        &target_freebsd_i386   },
 	{ str_lit("freebsd_amd64"),       &target_freebsd_amd64  },
+	{ str_lit("openbsd_amd64"),       &target_openbsd_amd64  },
 	{ str_lit("freestanding_wasm32"), &target_freestanding_wasm32 },
 	{ str_lit("wasi_wasm32"),         &target_wasi_wasm32 },
 	{ str_lit("js_wasm32"),           &target_js_wasm32 },
@@ -765,10 +777,38 @@ String internal_odin_root_dir(void) {
 		len = readlink("/proc/curproc/exe", &path_buf[0], path_buf.count);
 #elif defined(GB_SYSTEM_DRAGONFLYBSD)
 		len = readlink("/proc/curproc/file", &path_buf[0], path_buf.count);
-#else
+#elif defined(GB_SYSTEM_LINUX)
 		len = readlink("/proc/self/exe", &path_buf[0], path_buf.count);
+#elif defined(GB_SYSTEM_OPENBSD)
+		int error;
+		int mib[] = {
+			CTL_KERN,
+			KERN_PROC_ARGS,
+			getpid(),
+			KERN_PROC_ARGV,
+		};
+		// get argv size
+		error = sysctl(mib, 4, NULL, (size_t *) &len, NULL, 0);
+		if (error == -1) {
+			// sysctl error
+			return make_string(nullptr, 0);
+		}
+		// get argv
+		char **argv = (char **)gb_malloc(len);
+		error = sysctl(mib, 4, argv, (size_t *) &len, NULL, 0);
+		if (error == -1) {
+			// sysctl error
+			gb_mfree(argv);
+			return make_string(nullptr, 0);
+		}
+		// copy argv[0] to path_buf
+		len = gb_strlen(argv[0]);
+		if(len < path_buf.count) {
+			gb_memmove(&path_buf[0], argv[0], len);
+		}
+		gb_mfree(argv);
 #endif
-		if(len == 0) {
+		if(len == 0 || len == -1) {
 			return make_string(nullptr, 0);
 		}
 		if (len < path_buf.count) {
@@ -964,6 +1004,8 @@ void init_build_context(TargetMetrics *cross_target) {
 			#endif
 		#elif defined(GB_SYSTEM_FREEBSD)
 			metrics = &target_freebsd_amd64;
+		#elif defined(GB_SYSTEM_OPENBSD)
+			metrics = &target_openbsd_amd64;
 		#elif defined(GB_CPU_ARM)
 			metrics = &target_linux_arm64;
 		#else
@@ -971,13 +1013,13 @@ void init_build_context(TargetMetrics *cross_target) {
 		#endif
 	#else
 		#if defined(GB_SYSTEM_WINDOWS)
-			metrics = &target_windows_386;
+			metrics = &target_windows_i386;
 		#elif defined(GB_SYSTEM_OSX)
 			#error "Build Error: Unsupported architecture"
 		#elif defined(GB_SYSTEM_FREEBSD)
-			metrics = &target_freebsd_386;
+			metrics = &target_freebsd_i386;
 		#else
-			metrics = &target_linux_386;
+			metrics = &target_linux_i386;
 		#endif
 	#endif
 
@@ -1037,6 +1079,9 @@ void init_build_context(TargetMetrics *cross_target) {
 		case TargetOs_freebsd:
 			bc->link_flags = str_lit("-arch x86-64 ");
 			break;
+		case TargetOs_openbsd:
+			bc->link_flags = str_lit("-arch x86-64 ");
+			break;
 		}
 	} else if (bc->metrics.arch == TargetArch_i386) {
 		switch (bc->metrics.os) {

+ 6 - 5
src/check_builtin.cpp

@@ -952,7 +952,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 			mode = Addressing_Constant;
 			value = exact_value_i64(at->EnumeratedArray.count);
 			type = t_untyped_integer;
-		} else if (is_type_slice(op_type) && id == BuiltinProc_len) {
+		} else if ((is_type_slice(op_type) || is_type_relative_slice(op_type)) && id == BuiltinProc_len) {
 			mode = Addressing_Value;
 		} else if (is_type_dynamic_array(op_type)) {
 			mode = Addressing_Value;
@@ -1106,7 +1106,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 		
 		Selection sel = lookup_field(type, field_name, false);
 		if (sel.entity == nullptr) {
-			gbString type_str = type_to_string(type);
+			gbString type_str = type_to_string_shorthand(type);
 			error(ce->args[0],
 			      "'%s' has no field named '%.*s'", type_str, LIT(field_name));
 			gb_string_free(type_str);
@@ -1118,7 +1118,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 			return false;
 		}
 		if (sel.indirect) {
-			gbString type_str = type_to_string(type);
+			gbString type_str = type_to_string_shorthand(type);
 			error(ce->args[0],
 			      "Field '%.*s' is embedded via a pointer in '%s'", LIT(field_name), type_str);
 			gb_string_free(type_str);
@@ -1179,7 +1179,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 		
 		Selection sel = lookup_field(type, field_name, false);
 		if (sel.entity == nullptr) {
-			gbString type_str = type_to_string(type);
+			gbString type_str = type_to_string_shorthand(type);
 			error(ce->args[0],
 			      "'%s' has no field named '%.*s'", type_str, LIT(field_name));
 			gb_string_free(type_str);
@@ -1191,7 +1191,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 			return false;
 		}
 		if (sel.indirect) {
-			gbString type_str = type_to_string(type);
+			gbString type_str = type_to_string_shorthand(type);
 			error(ce->args[0],
 			      "Field '%.*s' is embedded via a pointer in '%s'", LIT(field_name), type_str);
 			gb_string_free(type_str);
@@ -3514,6 +3514,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 			case TargetOs_linux:
 			case TargetOs_essence:
 			case TargetOs_freebsd:
+			case TargetOs_openbsd:
 				switch (build_context.metrics.arch) {
 				case TargetArch_i386:
 				case TargetArch_amd64:

+ 4 - 4
src/check_expr.cpp

@@ -4470,7 +4470,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
 
 	if (entity == nullptr) {
 		gbString op_str   = expr_to_string(op_expr);
-		gbString type_str = type_to_string(operand->type);
+		gbString type_str = type_to_string_shorthand(operand->type);
 		gbString sel_str  = expr_to_string(selector);
 		error(op_expr, "'%s' of type '%s' has no field '%s'", op_str, type_str, sel_str);
 
@@ -4511,7 +4511,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
 		}
 
 		gbString op_str   = expr_to_string(op_expr);
-		gbString type_str = type_to_string(operand->type);
+		gbString type_str = type_to_string_shorthand(operand->type);
 		gbString sel_str  = expr_to_string(selector);
 		error(op_expr, "Cannot access non-constant field '%s' from '%s'", sel_str, op_str);
 		gb_string_free(sel_str);
@@ -4536,7 +4536,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
 		}
 
 		gbString op_str   = expr_to_string(op_expr);
-		gbString type_str = type_to_string(operand->type);
+		gbString type_str = type_to_string_shorthand(operand->type);
 		gbString sel_str  = expr_to_string(selector);
 		error(op_expr, "Cannot access non-constant field '%s' from '%s'", sel_str, op_str);
 		gb_string_free(sel_str);
@@ -4549,7 +4549,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
 
 	if (expr_entity != nullptr && is_type_polymorphic(expr_entity->type)) {
 		gbString op_str   = expr_to_string(op_expr);
-		gbString type_str = type_to_string(operand->type);
+		gbString type_str = type_to_string_shorthand(operand->type);
 		gbString sel_str  = expr_to_string(selector);
 		error(op_expr, "Cannot access field '%s' from non-specialized polymorphic type '%s'", sel_str, op_str);
 		gb_string_free(sel_str);

+ 1 - 0
src/checker.cpp

@@ -924,6 +924,7 @@ void init_universal(void) {
 			{"Linux",        TargetOs_linux},
 			{"Essence",      TargetOs_essence},
 			{"FreeBSD",      TargetOs_freebsd},
+			{"OpenBSD",      TargetOs_openbsd},
 			{"WASI",         TargetOs_wasi},
 			{"JS",           TargetOs_js},
 			{"Freestanding", TargetOs_freestanding},

+ 1 - 1
src/common.cpp

@@ -848,7 +848,7 @@ ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
 
 	return ReadDirectory_None;
 }
-#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD)
+#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
 
 #include <dirent.h>
 

+ 86 - 2
src/gb/gb.h

@@ -79,6 +79,10 @@ extern "C" {
 		#ifndef GB_SYSTEM_FREEBSD
 		#define GB_SYSTEM_FREEBSD 1
 		#endif
+	#elif defined(__OpenBSD__)
+		#ifndef GB_SYSTEM_OPENBSD
+		#define GB_SYSTEM_OPENBSD 1
+		#endif
 	#else
 		#error This UNIX operating system is not supported
 	#endif
@@ -199,7 +203,7 @@ extern "C" {
 	#endif
 	#include <stdlib.h> // NOTE(bill): malloc on linux
 	#include <sys/mman.h>
-	#if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__)
+	#if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
 		#include <sys/sendfile.h>
 	#endif
 	#include <sys/stat.h>
@@ -235,6 +239,12 @@ extern "C" {
 	#define sendfile(out, in, offset, count) sendfile(out, in, offset, count, NULL, NULL, 0)
 #endif
 
+#if defined(GB_SYSTEM_OPENBSD)
+	#include <stdio.h>
+	#include <pthread_np.h>
+	#define lseek64 lseek
+#endif
+    
 #if defined(GB_SYSTEM_UNIX)
 	#include <semaphore.h>
 #endif
@@ -783,6 +793,13 @@ typedef struct gbAffinity {
 	isize thread_count;
 	isize threads_per_core;
 } gbAffinity;
+#elif defined(GB_SYSTEM_OPENBSD)
+typedef struct gbAffinity {
+	b32   is_accurate;
+	isize core_count;
+	isize thread_count;
+	isize threads_per_core;
+} gbAffinity;
 #else
 #error TODO(bill): Unknown system
 #endif
@@ -3678,6 +3695,30 @@ b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
 	return true;
 }
 
+isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) {
+	GB_ASSERT(0 <= core && core < a->core_count);
+	return a->threads_per_core;
+}
+
+#elif defined(GB_SYSTEM_OPENBSD)
+#include <unistd.h>
+
+void gb_affinity_init(gbAffinity *a) {
+	a->core_count       = sysconf(_SC_NPROCESSORS_ONLN);
+	a->threads_per_core = 1;
+	a->is_accurate      = a->core_count > 0;
+	a->core_count       = a->is_accurate ? a->core_count : 1;
+	a->thread_count     = a->core_count;
+}
+
+void gb_affinity_destroy(gbAffinity *a) {
+	gb_unused(a);
+}
+
+b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
+	return true;
+}
+
 isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) {
 	GB_ASSERT(0 <= core && core < a->core_count);
 	return a->threads_per_core;
@@ -6025,7 +6066,7 @@ gbFileTime gb_file_last_write_time(char const *filepath) {
 gb_inline b32 gb_file_copy(char const *existing_filename, char const *new_filename, b32 fail_if_exists) {
 #if defined(GB_SYSTEM_OSX)
 	return copyfile(existing_filename, new_filename, NULL, COPYFILE_DATA) == 0;
-#else
+#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_FREEBSD)
 	isize size;
 	int existing_fd = open(existing_filename, O_RDONLY, 0);
 	int new_fd      = open(new_filename, O_WRONLY|O_CREAT, 0666);
@@ -6041,6 +6082,49 @@ gb_inline b32 gb_file_copy(char const *existing_filename, char const *new_filena
 	close(new_fd);
 	close(existing_fd);
 
+	return size == stat_existing.st_size;
+#else
+	int new_flags = O_WRONLY | O_CREAT;
+	if (fail_if_exists) {
+		new_flags |= O_EXCL;
+	}
+	int existing_fd = open(existing_filename, O_RDONLY, 0);
+	int new_fd      = open(new_filename, new_flags, 0666);
+
+	struct stat stat_existing;
+	if (fstat(existing_fd, &stat_existing) == -1) {
+		return 0;
+	}
+
+	size_t bsize = stat_existing.st_blksize > BUFSIZ ? stat_existing.st_blksize : BUFSIZ;
+	char *buf = (char *)malloc(bsize);
+	if (buf == NULL) {
+		close(new_fd);
+		close(existing_fd);
+		return 0;
+	}
+
+	isize size = 0;
+	ssize_t nread, nwrite, offset;
+	while ((nread = read(existing_fd, buf, bsize)) != -1 && nread != 0) {
+		for (offset = 0; nread; nread -= nwrite, offset += nwrite) {
+			if ((nwrite = write(new_fd, buf + offset, nread)) == -1 || nwrite == 0) {
+				free(buf);
+				close(new_fd);
+				close(existing_fd);
+				return 0;
+			}
+			size += nwrite;
+		}
+	}
+	
+	free(buf);
+	close(new_fd);
+	close(existing_fd);
+
+	if (nread == -1) {
+		return 0;
+	}
 	return size == stat_existing.st_size;
 #endif
 }

+ 4 - 0
src/llvm_backend.cpp

@@ -1325,6 +1325,10 @@ void lb_generate_code(lbGenerator *gen) {
 
 	switch (build_context.reloc_mode) {
 	case RelocMode_Default:
+		if (build_context.metrics.os == TargetOs_openbsd) {
+			// Always use PIC for OpenBSD: it defaults to PIE
+			reloc_mode = LLVMRelocPIC;
+		}
 		break;
 	case RelocMode_Static:
 		reloc_mode = LLVMRelocStatic;

+ 15 - 0
src/llvm_backend_expr.cpp

@@ -2202,6 +2202,21 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri
 		}
 	}
 
+	if (is_type_matrix(a) && (op_kind == Token_CmpEq || op_kind == Token_NotEq)) {
+		Type *tl = base_type(a);
+		lbValue lhs = lb_address_from_load_or_generate_local(p, left);
+		lbValue rhs = lb_address_from_load_or_generate_local(p, right);
+
+
+		// TODO(bill): Test to see if this is actually faster!!!!
+		auto args = array_make<lbValue>(permanent_allocator(), 3);
+		args[0] = lb_emit_conv(p, lhs, t_rawptr);
+		args[1] = lb_emit_conv(p, rhs, t_rawptr);
+		args[2] = lb_const_int(p->module, t_int, type_size_of(tl));
+		lbValue val = lb_emit_runtime_call(p, "memory_compare", args);
+		lbValue res = lb_emit_comp(p, op_kind, val, lb_const_nil(p->module, val.type));
+		return lb_emit_conv(p, res, t_bool);
+	}
 	if (is_type_array(a) || is_type_enumerated_array(a)) {
 		Type *tl = base_type(a);
 		lbValue lhs = lb_address_from_load_or_generate_local(p, left);

+ 17 - 8
src/llvm_backend_proc.cpp

@@ -1046,7 +1046,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
 			return lb_string_len(p, v);
 		} else if (is_type_array(t)) {
 			GB_PANIC("Array lengths are constant");
-		} else if (is_type_slice(t)) {
+		} else if (is_type_slice(t) || is_type_relative_slice(t)) {
 			return lb_slice_len(p, v);
 		} else if (is_type_dynamic_array(t)) {
 			return lb_dynamic_array_len(p, v);
@@ -1072,7 +1072,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
 			GB_PANIC("Unreachable");
 		} else if (is_type_array(t)) {
 			GB_PANIC("Array lengths are constant");
-		} else if (is_type_slice(t)) {
+		} else if (is_type_slice(t) || is_type_relative_slice(t)) {
 			return lb_slice_len(p, v);
 		} else if (is_type_dynamic_array(t)) {
 			return lb_dynamic_array_cap(p, v);
@@ -1406,14 +1406,23 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
 
 	case BuiltinProc_read_cycle_counter:
 		{
-			char const *name = "llvm.readcyclecounter";
-			unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
-			GB_ASSERT_MSG(id != 0, "Unable to find %s", name);
-			LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0);
-
 			lbValue res = {};
-			res.value = LLVMBuildCall(p->builder, ip, nullptr, 0, "");
 			res.type = tv.type;
+
+			if (build_context.metrics.arch == TargetArch_arm64) {
+				LLVMTypeRef func_type = LLVMFunctionType(LLVMInt64TypeInContext(p->module->ctx), nullptr, 0, false);
+				bool has_side_effects = false;
+				LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("mrs x9, cntvct_el0"), str_lit("=r"), has_side_effects);
+				GB_ASSERT(the_asm != nullptr);
+				res.value = LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, "");
+			} else {
+				char const *name = "llvm.readcyclecounter";
+				unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+				GB_ASSERT_MSG(id != 0, "Unable to find %s", name);
+				LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0);
+
+				res.value = LLVMBuildCall(p->builder, ip, nullptr, 0, "");
+			}
 			return res;
 		}
 

+ 2 - 2
src/llvm_backend_utility.cpp

@@ -1387,7 +1387,7 @@ lbValue lb_slice_elem(lbProcedure *p, lbValue slice) {
 	return lb_emit_struct_ev(p, slice, 0);
 }
 lbValue lb_slice_len(lbProcedure *p, lbValue slice) {
-	GB_ASSERT(is_type_slice(slice.type));
+	GB_ASSERT(is_type_slice(slice.type) || is_type_relative_slice(slice.type));
 	return lb_emit_struct_ev(p, slice, 1);
 }
 lbValue lb_dynamic_array_elem(lbProcedure *p, lbValue da) {
@@ -1793,7 +1793,7 @@ LLVMValueRef llvm_get_inline_asm(LLVMTypeRef func_type, String const &str, Strin
 	return LLVMGetInlineAsm(func_type,
 		cast(char *)str.text, cast(size_t)str.len,
 		cast(char *)clobbers.text, cast(size_t)clobbers.len,
-		/*HasSideEffects*/true, /*IsAlignStack*/false,
+		has_side_effects, is_align_stack,
 		dialect
 	#if LLVM_VERSION_MAJOR >= 13 
 		, /*CanThrow*/false

+ 43 - 37
src/main.cpp

@@ -384,7 +384,7 @@ i32 linker_stage(lbGenerator *gen) {
 			// NOTE(zangent): Sometimes, you have to use -framework on MacOS.
 			//   This allows you to specify '-f' in a #foreign_system_library,
 			//   without having to implement any new syntax specifically for MacOS.
-			#if defined(GB_SYSTEM_OSX)
+			if (build_context.metrics.os == TargetOs_darwin) {
 				if (string_ends_with(lib, str_lit(".framework"))) {
 					// framework thingie
 					String lib_name = lib;
@@ -400,14 +400,14 @@ i32 linker_stage(lbGenerator *gen) {
 					// dynamic or static system lib, just link regularly searching system library paths
 					lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
 				}
-			#else
+			} else {
 				// NOTE(vassvik): static libraries (.a files) in linux can be linked to directly using the full path,
 				//                since those are statically linked to at link time. shared libraries (.so) has to be
 				//                available at runtime wherever the executable is run, so we make require those to be
 				//                local to the executable (unless the system collection is used, in which case we search
 				//                the system library paths for the library file).
-				if (string_ends_with(lib, str_lit(".a"))) {
-					// static libs, absolute full path relative to the file in which the lib was imported from
+				if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o"))) {
+					// static libs and object files, absolute full path relative to the file in which the lib was imported from
 					lib_str = gb_string_append_fmt(lib_str, " -l:\"%.*s\" ", LIT(lib));
 				} else if (string_ends_with(lib, str_lit(".so"))) {
 					// dynamic lib, relative path to executable
@@ -418,7 +418,7 @@ i32 linker_stage(lbGenerator *gen) {
 					// dynamic or static system lib, just link regularly searching system library paths
 					lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
 				}
-			#endif
+			}
 		}
 
 		gbString object_files = gb_string_make(heap_allocator(), "");
@@ -456,14 +456,15 @@ i32 linker_stage(lbGenerator *gen) {
 			// line arguments prepared previously are incompatible with ld.
 			//
 			// Shared libraries are .dylib on MacOS and .so on Linux.
-			#if defined(GB_SYSTEM_OSX)
+			if (build_context.metrics.os == TargetOs_darwin) {
 				output_ext = STR_LIT(".dylib");
-			#else
+			} else {
 				output_ext = STR_LIT(".so");
-			#endif
+			}
 			link_settings = gb_string_appendc(link_settings, "-Wl,-init,'_odin_entry_point' ");
 			link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' ");
-		} else {
+		} else if (build_context.metrics.os != TargetOs_openbsd) {
+			// OpenBSD defaults to PIE executable. do not pass -no-pie for it.
 			link_settings = gb_string_appendc(link_settings, "-no-pie ");
 		}
 		if (build_context.out_filepath.len > 0) {
@@ -474,34 +475,39 @@ i32 linker_stage(lbGenerator *gen) {
 			}
 		}
 
-		result = system_exec_command_line_app("ld-link",
-			"clang -Wno-unused-command-line-argument %s -o \"%.*s%.*s\" %s "
-			" %s "
-			" %.*s "
-			" %.*s "
-			" %s "
-			#if defined(GB_SYSTEM_OSX)
-				// This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
-				// NOTE: If you change this (although this minimum is as low as you can go with Odin working)
-				//       make sure to also change the 'mtriple' param passed to 'opt'
-				#if defined(GB_CPU_ARM)
-				" -mmacosx-version-min=12.0.0 "
-				#else
-				" -mmacosx-version-min=10.8.0 "
-				#endif
-				// This points the linker to where the entry point is
-				" -e _main "
-			#endif
-			, object_files, LIT(output_base), LIT(output_ext),
-			#if defined(GB_SYSTEM_OSX)
-			  "-lSystem -lm -Wl,-syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib",
-			#else
-			  "-lc -lm",
-			#endif
-			lib_str,
-			LIT(build_context.link_flags),
-			LIT(build_context.extra_linker_flags),
-			link_settings);
+		gbString platform_lib_str = gb_string_make(heap_allocator(), "");
+		defer (gb_string_free(platform_lib_str));
+		if (build_context.metrics.os == TargetOs_darwin) {
+			platform_lib_str = gb_string_appendc(platform_lib_str, "-lSystem -lm -Wl,-syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib");
+		} else {
+			platform_lib_str = gb_string_appendc(platform_lib_str, "-lc -lm");
+		}
+
+		if (build_context.metrics.os == TargetOs_darwin) {
+			// This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
+			// NOTE: If you change this (although this minimum is as low as you can go with Odin working)
+			//       make sure to also change the 'mtriple' param passed to 'opt'
+			if (build_context.metrics.arch == TargetArch_arm64) {
+				link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=12.0.0 ");
+			} else {
+				link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=10.8.0 ");
+			}
+			// This points the linker to where the entry point is
+			link_settings = gb_string_appendc(link_settings, " -e _main ");
+		}
+
+		gbString link_command_line = gb_string_make(heap_allocator(), "clang -Wno-unused-command-line-argument ");
+		defer (gb_string_free(link_command_line));
+
+		link_command_line = gb_string_appendc(link_command_line, object_files);
+		link_command_line = gb_string_append_fmt(link_command_line, " -o \"%.*s%.*s\" ", LIT(output_base), LIT(output_ext));
+		link_command_line = gb_string_append_fmt(link_command_line, " %s ", platform_lib_str);
+		link_command_line = gb_string_append_fmt(link_command_line, " %s ", lib_str);
+		link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.link_flags));
+		link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.extra_linker_flags));
+		link_command_line = gb_string_append_fmt(link_command_line, " %s ", link_settings);
+
+		result = system_exec_command_line_app("ld-link", link_command_line);
 
 		if (result) {
 			return result;

+ 18 - 1
src/parser.cpp

@@ -1538,7 +1538,7 @@ void fix_advance_to_next_stmt(AstFile *f) {
 Token expect_closing(AstFile *f, TokenKind kind, String context) {
 	if (f->curr_token.kind != kind &&
 	    f->curr_token.kind == Token_Semicolon &&
-	    f->curr_token.string == "\n") {
+	    (f->curr_token.string == "\n" || f->curr_token.kind == Token_EOF)) {
 		Token tok = f->prev_token;
 		tok.pos.column += cast(i32)tok.string.len;
 		syntax_error(tok, "Missing ',' before newline in %.*s", LIT(context));
@@ -1560,6 +1560,7 @@ void assign_removal_flag_to_semicolon(AstFile *f) {
 			switch (curr_token->kind) {
 			case Token_CloseBrace:
 			case Token_CloseParen:
+			case Token_EOF:
 				ok = true;
 				break;
 			}
@@ -5719,6 +5720,22 @@ ParseFileError parse_packages(Parser *p, String init_filename) {
 			error_line("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename));
 			return ParseFile_WrongExtension;
 		}
+	} else if (init_fullpath.len != 0) {
+		String path = init_fullpath;
+		if (path[path.len-1] == '/') {
+			path.len -= 1;
+		}
+		if ((build_context.command_kind & Command__does_build) &&
+		    build_context.build_mode == BuildMode_Executable) {
+			String short_path = filename_from_path(path);
+			char *cpath = alloc_cstring(heap_allocator(), short_path);
+			defer (gb_free(heap_allocator(), cpath));
+
+			if (gb_file_exists(cpath)) {
+			    	error_line("Please specify the executable name with -out:<string> as a directory exists with the same name in the current working directory");
+			    	return ParseFile_DirectoryAlreadyExists;
+			}
+		}
 	}
 	
 

+ 1 - 0
src/parser.hpp

@@ -46,6 +46,7 @@ enum ParseFileError {
 	ParseFile_InvalidToken,
 	ParseFile_GeneralError,
 	ParseFile_FileTooLarge,
+	ParseFile_DirectoryAlreadyExists,
 
 	ParseFile_Count,
 };

+ 1 - 1
src/threading.cpp

@@ -486,7 +486,7 @@ void thread_set_name(Thread *t, char const *name) {
 #elif defined(GB_SYSTEM_OSX)
 	// TODO(bill): Test if this works
 	pthread_setname_np(name);
-#elif defined(GB_SYSTEM_FREEBSD)
+#elif defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
 	pthread_set_name_np(t->posix_handle, name);
 #else
 	// TODO(bill): Test if this works

+ 23 - 14
src/types.cpp

@@ -703,7 +703,7 @@ struct TypePath;
 i64      type_size_of         (Type *t);
 i64      type_align_of        (Type *t);
 i64      type_offset_of       (Type *t, i32 index);
-gbString type_to_string       (Type *type);
+gbString type_to_string       (Type *type, bool shorthand=false);
 i64      type_size_of_internal(Type *t, TypePath *path);
 void     init_map_internal_types(Type *type);
 Type *   bit_set_to_int(Type *t);
@@ -3936,7 +3936,7 @@ Type *alloc_type_proc_from_types(Type **param_types, unsigned param_count, Type
 
 
 
-gbString write_type_to_string(gbString str, Type *type) {
+gbString write_type_to_string(gbString str, Type *type, bool shorthand=false) {
 	if (type == nullptr) {
 		return gb_string_appendc(str, "<no type>");
 	}
@@ -4051,15 +4051,21 @@ gbString write_type_to_string(gbString str, Type *type) {
 		if (type->Struct.is_raw_union) str = gb_string_appendc(str, " #raw_union");
 		if (type->Struct.custom_align != 0) str = gb_string_append_fmt(str, " #align %d", cast(int)type->Struct.custom_align);
 		str = gb_string_appendc(str, " {");
-		for_array(i, type->Struct.fields) {
-			Entity *f = type->Struct.fields[i];
-			GB_ASSERT(f->kind == Entity_Variable);
-			if (i > 0) {
-				str = gb_string_appendc(str, ", ");
+
+
+		if (shorthand && type->Struct.fields.count > 16) {
+			str = gb_string_append_fmt(str, "%lld fields...", cast(long long)type->Struct.fields.count);
+		} else {
+			for_array(i, type->Struct.fields) {
+				Entity *f = type->Struct.fields[i];
+				GB_ASSERT(f->kind == Entity_Variable);
+				if (i > 0) {
+					str = gb_string_appendc(str, ", ");
+				}
+				str = gb_string_append_length(str, f->token.string.text, f->token.string.len);
+				str = gb_string_appendc(str, ": ");
+				str = write_type_to_string(str, f->type);
 			}
-			str = gb_string_append_length(str, f->token.string.text, f->token.string.len);
-			str = gb_string_appendc(str, ": ");
-			str = write_type_to_string(str, f->type);
 		}
 		str = gb_string_append_rune(str, '}');
 	} break;
@@ -4234,13 +4240,16 @@ gbString write_type_to_string(gbString str, Type *type) {
 }
 
 
-gbString type_to_string(Type *type, gbAllocator allocator) {
-	return write_type_to_string(gb_string_make(allocator, ""), type);
+gbString type_to_string(Type *type, gbAllocator allocator, bool shorthand=false) {
+	return write_type_to_string(gb_string_make(allocator, ""), type, shorthand);
 }
-gbString type_to_string(Type *type) {
-	return write_type_to_string(gb_string_make(heap_allocator(), ""), type);
+gbString type_to_string(Type *type, bool shorthand) {
+	return write_type_to_string(gb_string_make(heap_allocator(), ""), type, shorthand);
 }
 
+gbString type_to_string_shorthand(Type *type) {
+	return type_to_string(type, true);
+}
 
 
 

+ 6 - 2
tests/core/Makefile

@@ -1,7 +1,7 @@
 ODIN=../../odin
 PYTHON=$(shell which python3)
 
-all: download_test_assets image_test compress_test strings_test hash_test crypto_test noise_test
+all: download_test_assets image_test compress_test strings_test hash_test crypto_test noise_test encoding_test
 
 download_test_assets:
 	$(PYTHON) download_assets.py
@@ -22,4 +22,8 @@ crypto_test:
 	$(ODIN) run crypto -out=crypto_hash -o:speed -no-bounds-check
 
 noise_test:
-	$(ODIN) run math/noise -out=test_noise
+	$(ODIN) run math/noise -out=test_noise
+
+encoding_test:
+	$(ODIN) run encoding/json -out=test_json
+	$(ODIN) run encoding/varint -out=test_varint

+ 2 - 1
tests/core/build.bat

@@ -35,7 +35,8 @@ echo ---
 echo ---
 echo Running core:encoding tests
 echo ---
-%PATH_TO_ODIN% run encoding %COMMON%
+%PATH_TO_ODIN% run encoding/json %COMMON%
+%PATH_TO_ODIN% run encoding/varint %COMMON%
 
 echo ---
 echo Running core:math/noise tests

+ 4 - 3
tests/core/compress/test_core_compress.odin

@@ -30,14 +30,12 @@ when ODIN_TEST {
 	log     :: testing.log
 } else {
 	expect  :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
-		fmt.printf("[%v] ", loc)
 		TEST_count += 1
 		if !condition {
 			TEST_fail += 1
-			fmt.println(message)
+			fmt.printf("[%v] %v\n", loc, message)
 			return
 		}
-		fmt.println(" PASS")
 	}
 	log     :: proc(t: ^testing.T, v: any, loc := #caller_location) {
 		fmt.printf("[%v] ", loc)
@@ -52,6 +50,9 @@ main :: proc() {
 	gzip_test(&t)
 
 	fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
+	if TEST_fail > 0 {
+		os.exit(1)
+	}
 }
 
 @test

+ 890 - 888
tests/core/crypto/test_core_crypto.odin

@@ -1,15 +1,15 @@
 package test_core_crypto
 
 /*
-    Copyright 2021 zhibog
-    Made available under the BSD-3 license.
+	Copyright 2021 zhibog
+	Made available under the BSD-3 license.
 
-    List of contributors:
-        zhibog, dotbmp:  Initial implementation.
-        Jeroen van Rijn: Test runner setup.
+	List of contributors:
+		zhibog, dotbmp:  Initial implementation.
+		Jeroen van Rijn: Test runner setup.
 
-    Tests for the hashing algorithms within the crypto library.
-    Where possible, the official test vectors are used to validate the implementation.
+	Tests for the hashing algorithms within the crypto library.
+	Where possible, the official test vectors are used to validate the implementation.
 */
 
 import "core:testing"
@@ -37,1112 +37,1114 @@ import "core:crypto/jh"
 import "core:crypto/groestl"
 import "core:crypto/haval"
 import "core:crypto/siphash"
+import "core:os"
 
 TEST_count := 0
 TEST_fail  := 0
 
 when ODIN_TEST {
-    expect  :: testing.expect
-    log     :: testing.log
+	expect  :: testing.expect
+	log     :: testing.log
 } else {
-    expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
-        fmt.printf("[%v] ", loc)
-        TEST_count += 1
-        if !condition {
-            TEST_fail += 1
-            fmt.println(message)
-            return
-        }
-        fmt.println(" PASS")
-    }
-    log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
-        fmt.printf("[%v] ", loc)
-        fmt.printf("log: %v\n", v)
-    }
+	expect  :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
+		TEST_count += 1
+		if !condition {
+			TEST_fail += 1
+			fmt.printf("[%v] %v\n", loc, message)
+			return
+		}
+	}
+	log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
+		fmt.printf("[%v] ", loc)
+		fmt.printf("log: %v\n", v)
+	}
 }
 
 main :: proc() {
-    t := testing.T{}
-    test_md2(&t)
-    test_md4(&t)
-    test_md5(&t)
-    test_sha1(&t)
-    test_sha224(&t)
-    test_sha256(&t)
-    test_sha384(&t)
-    test_sha512(&t)
-    test_sha3_224(&t)
-    test_sha3_256(&t)
-    test_sha3_384(&t)
-    test_sha3_512(&t)
-    test_shake_128(&t)
-    test_shake_256(&t)
-    test_keccak_224(&t)
-    test_keccak_256(&t)
-    test_keccak_384(&t)
-    test_keccak_512(&t)
-    test_whirlpool(&t)
-    test_gost(&t)
-    test_streebog_256(&t)
-    test_streebog_512(&t)
-    test_blake_224(&t)
-    test_blake_256(&t)
-    test_blake_384(&t)
-    test_blake_512(&t)
-    test_blake2b(&t)
-    test_blake2s(&t)
-    test_ripemd_128(&t)
-    test_ripemd_160(&t)
-    test_ripemd_256(&t)
-    test_ripemd_320(&t)
-    test_tiger_128(&t)
-    test_tiger_160(&t)
-    test_tiger_192(&t)
-    test_tiger2_128(&t)
-    test_tiger2_160(&t)
-    test_tiger2_192(&t)
-    test_sm3(&t)
-    test_jh_224(&t)
-    test_jh_256(&t)
-    test_jh_384(&t)
-    test_jh_512(&t)
-    test_groestl_224(&t)
-    test_groestl_256(&t)
-    test_groestl_384(&t)
-    test_groestl_512(&t)
-    test_haval_128(&t)
-    test_haval_160(&t)
-    test_haval_192(&t)
-    test_haval_224(&t)
-    test_haval_256(&t)
-    test_siphash_2_4(&t)
-
-    // "modern" crypto tests
-    test_chacha20(&t)
-    test_poly1305(&t)
-    test_chacha20poly1305(&t)
-    test_x25519(&t)
-    test_rand_bytes(&t)
-
-    bench_modern(&t)
-
-    fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
+	t := testing.T{}
+	test_md2(&t)
+	test_md4(&t)
+	test_md5(&t)
+	test_sha1(&t)
+	test_sha224(&t)
+	test_sha256(&t)
+	test_sha384(&t)
+	test_sha512(&t)
+	test_sha3_224(&t)
+	test_sha3_256(&t)
+	test_sha3_384(&t)
+	test_sha3_512(&t)
+	test_shake_128(&t)
+	test_shake_256(&t)
+	test_keccak_224(&t)
+	test_keccak_256(&t)
+	test_keccak_384(&t)
+	test_keccak_512(&t)
+	test_whirlpool(&t)
+	test_gost(&t)
+	test_streebog_256(&t)
+	test_streebog_512(&t)
+	test_blake_224(&t)
+	test_blake_256(&t)
+	test_blake_384(&t)
+	test_blake_512(&t)
+	test_blake2b(&t)
+	test_blake2s(&t)
+	test_ripemd_128(&t)
+	test_ripemd_160(&t)
+	test_ripemd_256(&t)
+	test_ripemd_320(&t)
+	test_tiger_128(&t)
+	test_tiger_160(&t)
+	test_tiger_192(&t)
+	test_tiger2_128(&t)
+	test_tiger2_160(&t)
+	test_tiger2_192(&t)
+	test_sm3(&t)
+	test_jh_224(&t)
+	test_jh_256(&t)
+	test_jh_384(&t)
+	test_jh_512(&t)
+	test_groestl_224(&t)
+	test_groestl_256(&t)
+	test_groestl_384(&t)
+	test_groestl_512(&t)
+	test_haval_128(&t)
+	test_haval_160(&t)
+	test_haval_192(&t)
+	test_haval_224(&t)
+	test_haval_256(&t)
+	test_siphash_2_4(&t)
+
+	// "modern" crypto tests
+	test_chacha20(&t)
+	test_poly1305(&t)
+	test_chacha20poly1305(&t)
+	test_x25519(&t)
+	test_rand_bytes(&t)
+
+	bench_modern(&t)
+
+	fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
+	if TEST_fail > 0 {
+		os.exit(1)
+	}
 }
 
 TestHash :: struct {
-    hash: string,
-    str:  string,
+	hash: string,
+	str:  string,
 }
 
 hex_string :: proc(bytes: []byte, allocator := context.temp_allocator) -> string {
-    lut: [16]byte = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}
-    buf := make([]byte, len(bytes) * 2, allocator)
-    for i := 0; i < len(bytes); i += 1 {
-        buf[i * 2 + 0] = lut[bytes[i] >> 4 & 0xf]
-        buf[i * 2 + 1] = lut[bytes[i]      & 0xf]
-    }
-    return string(buf)
+	lut: [16]byte = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}
+	buf := make([]byte, len(bytes) * 2, allocator)
+	for i := 0; i < len(bytes); i += 1 {
+		buf[i * 2 + 0] = lut[bytes[i] >> 4 & 0xf]
+		buf[i * 2 + 1] = lut[bytes[i]      & 0xf]
+	}
+	return string(buf)
 }
 
 @(test)
 test_md2 :: proc(t: ^testing.T) {
-    // Official test vectors from https://datatracker.ietf.org/doc/html/rfc1319
-    test_vectors := [?]TestHash {
-        TestHash{"8350e5a3e24c153df2275c9f80692773", ""},
-        TestHash{"32ec01ec4a6dac72c0ab96fb34c0b5d1", "a"},
-        TestHash{"da853b0d3f88d99b30283a69e6ded6bb", "abc"},
-        TestHash{"ab4f496bfb2a530b219ff33031fe06b0", "message digest"},
-        TestHash{"4e8ddff3650292ab5a4108c3aa47940b", "abcdefghijklmnopqrstuvwxyz"},
-        TestHash{"da33def2a42df13975352846c30338cd", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
-        TestHash{"d5976f79d83d3a0dc9806c3c66f3efd8", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
-    }
-    for v, _ in test_vectors {
-        computed     := md2.hash(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	// Official test vectors from https://datatracker.ietf.org/doc/html/rfc1319
+	test_vectors := [?]TestHash {
+		TestHash{"8350e5a3e24c153df2275c9f80692773", ""},
+		TestHash{"32ec01ec4a6dac72c0ab96fb34c0b5d1", "a"},
+		TestHash{"da853b0d3f88d99b30283a69e6ded6bb", "abc"},
+		TestHash{"ab4f496bfb2a530b219ff33031fe06b0", "message digest"},
+		TestHash{"4e8ddff3650292ab5a4108c3aa47940b", "abcdefghijklmnopqrstuvwxyz"},
+		TestHash{"da33def2a42df13975352846c30338cd", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
+		TestHash{"d5976f79d83d3a0dc9806c3c66f3efd8", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
+	}
+	for v, _ in test_vectors {
+		computed     := md2.hash(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_md4 :: proc(t: ^testing.T) {
-    // Official test vectors from https://datatracker.ietf.org/doc/html/rfc1320
-    test_vectors := [?]TestHash {
-        TestHash{"31d6cfe0d16ae931b73c59d7e0c089c0", ""},
-        TestHash{"bde52cb31de33e46245e05fbdbd6fb24", "a"},
-        TestHash{"a448017aaf21d8525fc10ae87aa6729d", "abc"},
-        TestHash{"d9130a8164549fe818874806e1c7014b", "message digest"},
-        TestHash{"d79e1c308aa5bbcdeea8ed63df412da9", "abcdefghijklmnopqrstuvwxyz"},
-        TestHash{"043f8582f241db351ce627e153e7f0e4", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
-        TestHash{"e33b4ddc9c38f2199c3e7b164fcc0536", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
-    }
-    for v, _ in test_vectors {
-        computed     := md4.hash(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	// Official test vectors from https://datatracker.ietf.org/doc/html/rfc1320
+	test_vectors := [?]TestHash {
+		TestHash{"31d6cfe0d16ae931b73c59d7e0c089c0", ""},
+		TestHash{"bde52cb31de33e46245e05fbdbd6fb24", "a"},
+		TestHash{"a448017aaf21d8525fc10ae87aa6729d", "abc"},
+		TestHash{"d9130a8164549fe818874806e1c7014b", "message digest"},
+		TestHash{"d79e1c308aa5bbcdeea8ed63df412da9", "abcdefghijklmnopqrstuvwxyz"},
+		TestHash{"043f8582f241db351ce627e153e7f0e4", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
+		TestHash{"e33b4ddc9c38f2199c3e7b164fcc0536", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
+	}
+	for v, _ in test_vectors {
+		computed     := md4.hash(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_md5 :: proc(t: ^testing.T) {
-    // Official test vectors from https://datatracker.ietf.org/doc/html/rfc1321
-    test_vectors := [?]TestHash {
-        TestHash{"d41d8cd98f00b204e9800998ecf8427e", ""},
-        TestHash{"0cc175b9c0f1b6a831c399e269772661", "a"},
-        TestHash{"900150983cd24fb0d6963f7d28e17f72", "abc"},
-        TestHash{"f96b697d7cb7938d525a2f31aaf161d0", "message digest"},
-        TestHash{"c3fcd3d76192e4007dfb496cca67e13b", "abcdefghijklmnopqrstuvwxyz"},
-        TestHash{"d174ab98d277d9f5a5611c2c9f419d9f", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
-        TestHash{"57edf4a22be3c955ac49da2e2107b67a", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
-    }
-    for v, _ in test_vectors {
-        computed     := md5.hash(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	// Official test vectors from https://datatracker.ietf.org/doc/html/rfc1321
+	test_vectors := [?]TestHash {
+		TestHash{"d41d8cd98f00b204e9800998ecf8427e", ""},
+		TestHash{"0cc175b9c0f1b6a831c399e269772661", "a"},
+		TestHash{"900150983cd24fb0d6963f7d28e17f72", "abc"},
+		TestHash{"f96b697d7cb7938d525a2f31aaf161d0", "message digest"},
+		TestHash{"c3fcd3d76192e4007dfb496cca67e13b", "abcdefghijklmnopqrstuvwxyz"},
+		TestHash{"d174ab98d277d9f5a5611c2c9f419d9f", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
+		TestHash{"57edf4a22be3c955ac49da2e2107b67a", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
+	}
+	for v, _ in test_vectors {
+		computed     := md5.hash(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_sha1 :: proc(t: ^testing.T) {
-    // Test vectors from 
-    // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf
-    // https://www.di-mgt.com.au/sha_testvectors.html
-    test_vectors := [?]TestHash {
-        TestHash{"da39a3ee5e6b4b0d3255bfef95601890afd80709", ""},
-        TestHash{"a9993e364706816aba3e25717850c26c9cd0d89d", "abc"},
-        TestHash{"f9537c23893d2014f365adf8ffe33b8eb0297ed1", "abcdbcdecdefdefgefghfghighijhi"},
-        TestHash{"346fb528a24b48f563cb061470bcfd23740427ad", "jkijkljklmklmnlmnomnopnopq"},
-        TestHash{"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8", "a"},
-        TestHash{"c729c8996ee0a6f74f4f3248e8957edf704fb624", "01234567012345670123456701234567"},
-        TestHash{"84983e441c3bd26ebaae4aa1f95129e5e54670f1", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
-        TestHash{"a49b2446a02c645bf419f995b67091253a04a259", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"},
-    }
-    for v, _ in test_vectors {
-        computed     := sha1.hash(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	// Test vectors from
+	// https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf
+	// https://www.di-mgt.com.au/sha_testvectors.html
+	test_vectors := [?]TestHash {
+		TestHash{"da39a3ee5e6b4b0d3255bfef95601890afd80709", ""},
+		TestHash{"a9993e364706816aba3e25717850c26c9cd0d89d", "abc"},
+		TestHash{"f9537c23893d2014f365adf8ffe33b8eb0297ed1", "abcdbcdecdefdefgefghfghighijhi"},
+		TestHash{"346fb528a24b48f563cb061470bcfd23740427ad", "jkijkljklmklmnlmnomnopnopq"},
+		TestHash{"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8", "a"},
+		TestHash{"c729c8996ee0a6f74f4f3248e8957edf704fb624", "01234567012345670123456701234567"},
+		TestHash{"84983e441c3bd26ebaae4aa1f95129e5e54670f1", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
+		TestHash{"a49b2446a02c645bf419f995b67091253a04a259", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"},
+	}
+	for v, _ in test_vectors {
+		computed     := sha1.hash(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_sha224 :: proc(t: ^testing.T) {
-    // Test vectors from 
-    // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf
-    // https://www.di-mgt.com.au/sha_testvectors.html
-    test_vectors := [?]TestHash {
-        TestHash{"d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", ""},
-        TestHash{"23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", "abc"},
-        TestHash{"75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
-        TestHash{"c97ca9a559850ce97a04a96def6d99a9e0e0e2ab14e6b8df265fc0b3", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"},
-    }
-    for v, _ in test_vectors {
-        computed     := sha2.hash_224(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	// Test vectors from
+	// https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf
+	// https://www.di-mgt.com.au/sha_testvectors.html
+	test_vectors := [?]TestHash {
+		TestHash{"d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", ""},
+		TestHash{"23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", "abc"},
+		TestHash{"75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
+		TestHash{"c97ca9a559850ce97a04a96def6d99a9e0e0e2ab14e6b8df265fc0b3", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"},
+	}
+	for v, _ in test_vectors {
+		computed     := sha2.hash_224(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_sha256 :: proc(t: ^testing.T) {
-    // Test vectors from 
-    // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf
-    // https://www.di-mgt.com.au/sha_testvectors.html
-    test_vectors := [?]TestHash {
-        TestHash{"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", ""},
-        TestHash{"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", "abc"},
-        TestHash{"248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
-        TestHash{"cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"},
-    }
-    for v, _ in test_vectors {
-        computed     := sha2.hash_256(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	// Test vectors from
+	// https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf
+	// https://www.di-mgt.com.au/sha_testvectors.html
+	test_vectors := [?]TestHash {
+		TestHash{"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", ""},
+		TestHash{"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", "abc"},
+		TestHash{"248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
+		TestHash{"cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"},
+	}
+	for v, _ in test_vectors {
+		computed     := sha2.hash_256(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_sha384 :: proc(t: ^testing.T) {
-    // Test vectors from 
-    // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf
-    // https://www.di-mgt.com.au/sha_testvectors.html
-    test_vectors := [?]TestHash {
-        TestHash{"38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b", ""},
-        TestHash{"cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7", "abc"},
-        TestHash{"3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
-        TestHash{"09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"},
-    }
-    for v, _ in test_vectors {
-        computed     := sha2.hash_384(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	// Test vectors from
+	// https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf
+	// https://www.di-mgt.com.au/sha_testvectors.html
+	test_vectors := [?]TestHash {
+		TestHash{"38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b", ""},
+		TestHash{"cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7", "abc"},
+		TestHash{"3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
+		TestHash{"09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"},
+	}
+	for v, _ in test_vectors {
+		computed     := sha2.hash_384(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_sha512 :: proc(t: ^testing.T) {
-    // Test vectors from 
-    // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf
-    // https://www.di-mgt.com.au/sha_testvectors.html
-    test_vectors := [?]TestHash {
-        TestHash{"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", ""},
-        TestHash{"ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f", "abc"},
-        TestHash{"204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
-        TestHash{"8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"},
-    }
-    for v, _ in test_vectors {
-        computed     := sha2.hash_512(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	// Test vectors from
+	// https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf
+	// https://www.di-mgt.com.au/sha_testvectors.html
+	test_vectors := [?]TestHash {
+		TestHash{"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", ""},
+		TestHash{"ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f", "abc"},
+		TestHash{"204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
+		TestHash{"8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"},
+	}
+	for v, _ in test_vectors {
+		computed     := sha2.hash_512(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_sha3_224 :: proc(t: ^testing.T) {
-    // Test vectors from 
-    // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf
-    // https://www.di-mgt.com.au/sha_testvectors.html
-    test_vectors := [?]TestHash {
-        TestHash{"6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7", ""},
-        TestHash{"e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf", "abc"},
-        TestHash{"10241ac5187380bd501192e4e56b5280908727dd8fe0d10d4e5ad91e", "abcdbcdecdefdefgefghfghighijhi"},
-        TestHash{"fd645fe07d814c397e85e85f92fe58b949f55efa4d3468b2468da45a", "jkijkljklmklmnlmnomnopnopq"},
-        TestHash{"9e86ff69557ca95f405f081269685b38e3a819b309ee942f482b6a8b", "a"},
-        TestHash{"6961f694b2ff3ed6f0c830d2c66da0c5e7ca9445f7c0dca679171112", "01234567012345670123456701234567"},
-        TestHash{"8a24108b154ada21c9fd5574494479ba5c7e7ab76ef264ead0fcce33", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
-        TestHash{"543e6868e1666c1a643630df77367ae5a62a85070a51c14cbf665cbc", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"},
-    }
-    for v, _ in test_vectors {
-        computed     := sha3.hash_224(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	// Test vectors from
+	// https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf
+	// https://www.di-mgt.com.au/sha_testvectors.html
+	test_vectors := [?]TestHash {
+		TestHash{"6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7", ""},
+		TestHash{"e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf", "abc"},
+		TestHash{"10241ac5187380bd501192e4e56b5280908727dd8fe0d10d4e5ad91e", "abcdbcdecdefdefgefghfghighijhi"},
+		TestHash{"fd645fe07d814c397e85e85f92fe58b949f55efa4d3468b2468da45a", "jkijkljklmklmnlmnomnopnopq"},
+		TestHash{"9e86ff69557ca95f405f081269685b38e3a819b309ee942f482b6a8b", "a"},
+		TestHash{"6961f694b2ff3ed6f0c830d2c66da0c5e7ca9445f7c0dca679171112", "01234567012345670123456701234567"},
+		TestHash{"8a24108b154ada21c9fd5574494479ba5c7e7ab76ef264ead0fcce33", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
+		TestHash{"543e6868e1666c1a643630df77367ae5a62a85070a51c14cbf665cbc", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"},
+	}
+	for v, _ in test_vectors {
+		computed     := sha3.hash_224(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_sha3_256 :: proc(t: ^testing.T) {
-    // Test vectors from 
-    // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf
-    // https://www.di-mgt.com.au/sha_testvectors.html
-    test_vectors := [?]TestHash {
-        TestHash{"a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a", ""},
-        TestHash{"3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", "abc"},
-        TestHash{"565ada1ced21278cfaffdde00dea0107964121ac25e4e978abc59412be74550a", "abcdbcdecdefdefgefghfghighijhi"},
-        TestHash{"8cc1709d520f495ce972ece48b0d2e1f74ec80d53bc5c47457142158fae15d98", "jkijkljklmklmnlmnomnopnopq"},
-        TestHash{"80084bf2fba02475726feb2cab2d8215eab14bc6bdd8bfb2c8151257032ecd8b", "a"},
-        TestHash{"e4786de5f88f7d374b7288f225ea9f2f7654da200bab5d417e1fb52d49202767", "01234567012345670123456701234567"},
-        TestHash{"41c0dba2a9d6240849100376a8235e2c82e1b9998a999e21db32dd97496d3376", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
-        TestHash{"916f6061fe879741ca6469b43971dfdb28b1a32dc36cb3254e812be27aad1d18", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"},
-    }
-    for v, _ in test_vectors {
-        computed     := sha3.hash_256(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	// Test vectors from
+	// https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf
+	// https://www.di-mgt.com.au/sha_testvectors.html
+	test_vectors := [?]TestHash {
+		TestHash{"a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a", ""},
+		TestHash{"3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", "abc"},
+		TestHash{"565ada1ced21278cfaffdde00dea0107964121ac25e4e978abc59412be74550a", "abcdbcdecdefdefgefghfghighijhi"},
+		TestHash{"8cc1709d520f495ce972ece48b0d2e1f74ec80d53bc5c47457142158fae15d98", "jkijkljklmklmnlmnomnopnopq"},
+		TestHash{"80084bf2fba02475726feb2cab2d8215eab14bc6bdd8bfb2c8151257032ecd8b", "a"},
+		TestHash{"e4786de5f88f7d374b7288f225ea9f2f7654da200bab5d417e1fb52d49202767", "01234567012345670123456701234567"},
+		TestHash{"41c0dba2a9d6240849100376a8235e2c82e1b9998a999e21db32dd97496d3376", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
+		TestHash{"916f6061fe879741ca6469b43971dfdb28b1a32dc36cb3254e812be27aad1d18", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"},
+	}
+	for v, _ in test_vectors {
+		computed     := sha3.hash_256(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_sha3_384 :: proc(t: ^testing.T) {
-    // Test vectors from 
-    // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf
-    // https://www.di-mgt.com.au/sha_testvectors.html
-    test_vectors := [?]TestHash {
-        TestHash{"0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004", ""},
-        TestHash{"ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b298d88cea927ac7f539f1edf228376d25", "abc"},
-        TestHash{"9aa92dbb716ebb573def0d5e3cdd28d6add38ada310b602b8916e690a3257b7144e5ddd3d0dbbc559c48480d34d57a9a", "abcdbcdecdefdefgefghfghighijhi"},
-        TestHash{"77c90323d7392bcdee8a3e7f74f19f47b7d1b1a825ac6a2d8d882a72317879cc26597035f1fc24fe65090b125a691282", "jkijkljklmklmnlmnomnopnopq"},
-        TestHash{"1815f774f320491b48569efec794d249eeb59aae46d22bf77dafe25c5edc28d7ea44f93ee1234aa88f61c91912a4ccd9", "a"},
-        TestHash{"51072590ad4c51b27ff8265590d74f92de7cc55284168e414ca960087c693285b08a283c6b19d77632994cb9eb93f1be", "01234567012345670123456701234567"},
-        TestHash{"991c665755eb3a4b6bbdfb75c78a492e8c56a22c5c4d7e429bfdbc32b9d4ad5aa04a1f076e62fea19eef51acd0657c22", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
-        TestHash{"79407d3b5916b59c3e30b09822974791c313fb9ecc849e406f23592d04f625dc8c709b98b43b3852b337216179aa7fc7", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"},
-    }
-    for v, _ in test_vectors {
-        computed     := sha3.hash_384(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	// Test vectors from
+	// https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf
+	// https://www.di-mgt.com.au/sha_testvectors.html
+	test_vectors := [?]TestHash {
+		TestHash{"0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004", ""},
+		TestHash{"ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b298d88cea927ac7f539f1edf228376d25", "abc"},
+		TestHash{"9aa92dbb716ebb573def0d5e3cdd28d6add38ada310b602b8916e690a3257b7144e5ddd3d0dbbc559c48480d34d57a9a", "abcdbcdecdefdefgefghfghighijhi"},
+		TestHash{"77c90323d7392bcdee8a3e7f74f19f47b7d1b1a825ac6a2d8d882a72317879cc26597035f1fc24fe65090b125a691282", "jkijkljklmklmnlmnomnopnopq"},
+		TestHash{"1815f774f320491b48569efec794d249eeb59aae46d22bf77dafe25c5edc28d7ea44f93ee1234aa88f61c91912a4ccd9", "a"},
+		TestHash{"51072590ad4c51b27ff8265590d74f92de7cc55284168e414ca960087c693285b08a283c6b19d77632994cb9eb93f1be", "01234567012345670123456701234567"},
+		TestHash{"991c665755eb3a4b6bbdfb75c78a492e8c56a22c5c4d7e429bfdbc32b9d4ad5aa04a1f076e62fea19eef51acd0657c22", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
+		TestHash{"79407d3b5916b59c3e30b09822974791c313fb9ecc849e406f23592d04f625dc8c709b98b43b3852b337216179aa7fc7", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"},
+	}
+	for v, _ in test_vectors {
+		computed     := sha3.hash_384(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_sha3_512 :: proc(t: ^testing.T) {
-    // Test vectors from 
-    // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf
-    // https://www.di-mgt.com.au/sha_testvectors.html
-    test_vectors := [?]TestHash {
-        TestHash{"a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26", ""},
-        TestHash{"b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0", "abc"},
-        TestHash{"9f9a327944a35988d67effc4fa748b3c07744f736ac70b479d8e12a3d10d6884d00a7ef593690305462e9e9030a67c51636fd346fd8fa0ee28a5ac2aee103d2e", "abcdbcdecdefdefgefghfghighijhi"},
-        TestHash{"dbb124a0deda966eb4d199d0844fa0beb0770ea1ccddabcd335a7939a931ac6fb4fa6aebc6573f462ced2e4e7178277803be0d24d8bc2864626d9603109b7891", "jkijkljklmklmnlmnomnopnopq"},
-        TestHash{"697f2d856172cb8309d6b8b97dac4de344b549d4dee61edfb4962d8698b7fa803f4f93ff24393586e28b5b957ac3d1d369420ce53332712f997bd336d09ab02a", "a"},
-        TestHash{"5679e353bc8eeea3e801ca60448b249bcfd3ac4a6c3abe429a807bcbd4c9cd12da87a5a9dc74fde64c0d44718632cae966b078397c6f9ec155c6a238f2347cf1", "01234567012345670123456701234567"},
-        TestHash{"04a371e84ecfb5b8b77cb48610fca8182dd457ce6f326a0fd3d7ec2f1e91636dee691fbe0c985302ba1b0d8dc78c086346b533b49c030d99a27daf1139d6e75e", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
-        TestHash{"afebb2ef542e6579c50cad06d2e578f9f8dd6881d7dc824d26360feebf18a4fa73e3261122948efcfd492e74e82e2189ed0fb440d187f382270cb455f21dd185", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"},
-    }
-    for v, _ in test_vectors {
-        computed     := sha3.hash_512(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	// Test vectors from
+	// https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf
+	// https://www.di-mgt.com.au/sha_testvectors.html
+	test_vectors := [?]TestHash {
+		TestHash{"a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26", ""},
+		TestHash{"b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0", "abc"},
+		TestHash{"9f9a327944a35988d67effc4fa748b3c07744f736ac70b479d8e12a3d10d6884d00a7ef593690305462e9e9030a67c51636fd346fd8fa0ee28a5ac2aee103d2e", "abcdbcdecdefdefgefghfghighijhi"},
+		TestHash{"dbb124a0deda966eb4d199d0844fa0beb0770ea1ccddabcd335a7939a931ac6fb4fa6aebc6573f462ced2e4e7178277803be0d24d8bc2864626d9603109b7891", "jkijkljklmklmnlmnomnopnopq"},
+		TestHash{"697f2d856172cb8309d6b8b97dac4de344b549d4dee61edfb4962d8698b7fa803f4f93ff24393586e28b5b957ac3d1d369420ce53332712f997bd336d09ab02a", "a"},
+		TestHash{"5679e353bc8eeea3e801ca60448b249bcfd3ac4a6c3abe429a807bcbd4c9cd12da87a5a9dc74fde64c0d44718632cae966b078397c6f9ec155c6a238f2347cf1", "01234567012345670123456701234567"},
+		TestHash{"04a371e84ecfb5b8b77cb48610fca8182dd457ce6f326a0fd3d7ec2f1e91636dee691fbe0c985302ba1b0d8dc78c086346b533b49c030d99a27daf1139d6e75e", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
+		TestHash{"afebb2ef542e6579c50cad06d2e578f9f8dd6881d7dc824d26360feebf18a4fa73e3261122948efcfd492e74e82e2189ed0fb440d187f382270cb455f21dd185", "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"},
+	}
+	for v, _ in test_vectors {
+		computed     := sha3.hash_512(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_shake_128 :: proc(t: ^testing.T) {
-    test_vectors := [?]TestHash {
-        TestHash{"7f9c2ba4e88f827d616045507605853e", ""},
-        TestHash{"f4202e3c5852f9182a0430fd8144f0a7", "The quick brown fox jumps over the lazy dog"},
-        TestHash{"853f4538be0db9621a6cea659a06c110", "The quick brown fox jumps over the lazy dof"},
-    }
-    for v, _ in test_vectors {
-        computed     := shake.hash_128(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors := [?]TestHash {
+		TestHash{"7f9c2ba4e88f827d616045507605853e", ""},
+		TestHash{"f4202e3c5852f9182a0430fd8144f0a7", "The quick brown fox jumps over the lazy dog"},
+		TestHash{"853f4538be0db9621a6cea659a06c110", "The quick brown fox jumps over the lazy dof"},
+	}
+	for v, _ in test_vectors {
+		computed     := shake.hash_128(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_shake_256 :: proc(t: ^testing.T) {
-    test_vectors := [?]TestHash {
-        TestHash{"46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762f", ""},
-        TestHash{"2f671343d9b2e1604dc9dcf0753e5fe15c7c64a0d283cbbf722d411a0e36f6ca", "The quick brown fox jumps over the lazy dog"},
-        TestHash{"46b1ebb2e142c38b9ac9081bef72877fe4723959640fa57119b366ce6899d401", "The quick brown fox jumps over the lazy dof"},
-    }
-    for v, _ in test_vectors {
-        computed     := shake.hash_256(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors := [?]TestHash {
+		TestHash{"46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762f", ""},
+		TestHash{"2f671343d9b2e1604dc9dcf0753e5fe15c7c64a0d283cbbf722d411a0e36f6ca", "The quick brown fox jumps over the lazy dog"},
+		TestHash{"46b1ebb2e142c38b9ac9081bef72877fe4723959640fa57119b366ce6899d401", "The quick brown fox jumps over the lazy dof"},
+	}
+	for v, _ in test_vectors {
+		computed     := shake.hash_256(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_keccak_224 :: proc(t: ^testing.T) {
-    // Test vectors from 
-    // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf
-    // https://www.di-mgt.com.au/sha_testvectors.html
-    test_vectors := [?]TestHash {
-        TestHash{"f71837502ba8e10837bdd8d365adb85591895602fc552b48b7390abd", ""},
-        TestHash{"c30411768506ebe1c2871b1ee2e87d38df342317300a9b97a95ec6a8", "abc"},
-    }
-    for v, _ in test_vectors {
-        computed     := keccak.hash_224(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	// Test vectors from
+	// https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf
+	// https://www.di-mgt.com.au/sha_testvectors.html
+	test_vectors := [?]TestHash {
+		TestHash{"f71837502ba8e10837bdd8d365adb85591895602fc552b48b7390abd", ""},
+		TestHash{"c30411768506ebe1c2871b1ee2e87d38df342317300a9b97a95ec6a8", "abc"},
+	}
+	for v, _ in test_vectors {
+		computed     := keccak.hash_224(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_keccak_256 :: proc(t: ^testing.T) {
-    // Test vectors from 
-    // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf
-    // https://www.di-mgt.com.au/sha_testvectors.html
-    test_vectors := [?]TestHash {
-        TestHash{"c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", ""},
-        TestHash{"4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45", "abc"},
-    }
-    for v, _ in test_vectors {
-        computed     := keccak.hash_256(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	// Test vectors from
+	// https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf
+	// https://www.di-mgt.com.au/sha_testvectors.html
+	test_vectors := [?]TestHash {
+		TestHash{"c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", ""},
+		TestHash{"4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45", "abc"},
+	}
+	for v, _ in test_vectors {
+		computed     := keccak.hash_256(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_keccak_384 :: proc(t: ^testing.T) {
-    // Test vectors from 
-    // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf
-    // https://www.di-mgt.com.au/sha_testvectors.html
-    test_vectors := [?]TestHash {
-        TestHash{"2c23146a63a29acf99e73b88f8c24eaa7dc60aa771780ccc006afbfa8fe2479b2dd2b21362337441ac12b515911957ff", ""},
-        TestHash{"f7df1165f033337be098e7d288ad6a2f74409d7a60b49c36642218de161b1f99f8c681e4afaf31a34db29fb763e3c28e", "abc"},
-    }
-    for v, _ in test_vectors {
-        computed     := keccak.hash_384(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	// Test vectors from
+	// https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf
+	// https://www.di-mgt.com.au/sha_testvectors.html
+	test_vectors := [?]TestHash {
+		TestHash{"2c23146a63a29acf99e73b88f8c24eaa7dc60aa771780ccc006afbfa8fe2479b2dd2b21362337441ac12b515911957ff", ""},
+		TestHash{"f7df1165f033337be098e7d288ad6a2f74409d7a60b49c36642218de161b1f99f8c681e4afaf31a34db29fb763e3c28e", "abc"},
+	}
+	for v, _ in test_vectors {
+		computed     := keccak.hash_384(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_keccak_512 :: proc(t: ^testing.T) {
-    // Test vectors from 
-    // https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf
-    // https://www.di-mgt.com.au/sha_testvectors.html
-    test_vectors := [?]TestHash {
-        TestHash{"0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e", ""},
-        TestHash{"18587dc2ea106b9a1563e32b3312421ca164c7f1f07bc922a9c83d77cea3a1e5d0c69910739025372dc14ac9642629379540c17e2a65b19d77aa511a9d00bb96", "abc"},
-    }
-    for v, _ in test_vectors {
-        computed     := keccak.hash_512(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	// Test vectors from
+	// https://csrc.nist.gov/csrc/media/projects/cryptographic-standards-and-guidelines/documents/examples/sha_all.pdf
+	// https://www.di-mgt.com.au/sha_testvectors.html
+	test_vectors := [?]TestHash {
+		TestHash{"0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e", ""},
+		TestHash{"18587dc2ea106b9a1563e32b3312421ca164c7f1f07bc922a9c83d77cea3a1e5d0c69910739025372dc14ac9642629379540c17e2a65b19d77aa511a9d00bb96", "abc"},
+	}
+	for v, _ in test_vectors {
+		computed     := keccak.hash_512(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_whirlpool :: proc(t: ^testing.T) {
-    // Test vectors from 
-    // https://web.archive.org/web/20171129084214/http://www.larc.usp.br/~pbarreto/WhirlpoolPage.html
-    test_vectors := [?]TestHash {
-        TestHash{"19fa61d75522a4669b44e39c1d2e1726c530232130d407f89afee0964997f7a73e83be698b288febcf88e3e03c4f0757ea8964e59b63d93708b138cc42a66eb3", ""},
-        TestHash{"8aca2602792aec6f11a67206531fb7d7f0dff59413145e6973c45001d0087b42d11bc645413aeff63a42391a39145a591a92200d560195e53b478584fdae231a", "a"},
-        TestHash{"33e24e6cbebf168016942df8a7174048f9cebc45cbd829c3b94b401a498acb11c5abcca7f2a1238aaf534371e87a4e4b19758965d5a35a7cad87cf5517043d97", "ab"},
-        TestHash{"4e2448a4c6f486bb16b6562c73b4020bf3043e3a731bce721ae1b303d97e6d4c7181eebdb6c57e277d0e34957114cbd6c797fc9d95d8b582d225292076d4eef5", "abc"},
-        TestHash{"bda164f0b930c43a1bacb5df880b205d15ac847add35145bf25d991ae74f0b72b1ac794f8aacda5fcb3c47038c954742b1857b5856519de4d1e54bfa2fa4eac5", "abcd"},
-        TestHash{"5d745e26ccb20fe655d39c9e7f69455758fbae541cb892b3581e4869244ab35b4fd6078f5d28b1f1a217452a67d9801033d92724a221255a5e377fe9e9e5f0b2", "abcde"},
-        TestHash{"a73e425459567308ba5f9eb2ae23570d0d0575eb1357ecf6ac88d4e0358b0ac3ea2371261f5d4c070211784b525911b9eec0ad968429bb7c7891d341cff4e811", "abcdef"},
-        TestHash{"08b388f68fd3eb51906ac3d3c699b8e9c3ac65d7ceb49d2e34f8a482cbc3082bc401cead90e85a97b8647c948bf35e448740b79659f3bee42145f0bd653d1f25", "abcdefg"},
-        TestHash{"1f1a84d30612820243afe2022712f9dac6d07c4c8bb41b40eacab0184c8d82275da5bcadbb35c7ca1960ff21c90acbae8c14e48d9309e4819027900e882c7ad9", "abcdefgh"},
-        TestHash{"11882bc9a31ac1cf1c41dcd9fd6fdd3ccdb9b017fc7f4582680134f314d7bb49af4c71f5a920bc0a6a3c1ff9a00021bf361d9867fe636b0bc1da1552e4237de4", "abcdefghi"},
-        TestHash{"717163de24809ffcf7ff6d5aba72b8d67c2129721953c252a4ddfb107614be857cbd76a9d5927de14633d6bdc9ddf335160b919db5c6f12cb2e6549181912eef", "abcdefghij"},
-        TestHash{"b97de512e91e3828b40d2b0fdce9ceb3c4a71f9bea8d88e75c4fa854df36725fd2b52eb6544edcacd6f8beddfea403cb55ae31f03ad62a5ef54e42ee82c3fb35", "The quick brown fox jumps over the lazy dog"},
-        TestHash{"c27ba124205f72e6847f3e19834f925cc666d0974167af915bb462420ed40cc50900d85a1f923219d832357750492d5c143011a76988344c2635e69d06f2d38c", "The quick brown fox jumps over the lazy eog"},
-    }
-    for v, _ in test_vectors {
-        computed     := whirlpool.hash(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	// Test vectors from
+	// https://web.archive.org/web/20171129084214/http://www.larc.usp.br/~pbarreto/WhirlpoolPage.html
+	test_vectors := [?]TestHash {
+		TestHash{"19fa61d75522a4669b44e39c1d2e1726c530232130d407f89afee0964997f7a73e83be698b288febcf88e3e03c4f0757ea8964e59b63d93708b138cc42a66eb3", ""},
+		TestHash{"8aca2602792aec6f11a67206531fb7d7f0dff59413145e6973c45001d0087b42d11bc645413aeff63a42391a39145a591a92200d560195e53b478584fdae231a", "a"},
+		TestHash{"33e24e6cbebf168016942df8a7174048f9cebc45cbd829c3b94b401a498acb11c5abcca7f2a1238aaf534371e87a4e4b19758965d5a35a7cad87cf5517043d97", "ab"},
+		TestHash{"4e2448a4c6f486bb16b6562c73b4020bf3043e3a731bce721ae1b303d97e6d4c7181eebdb6c57e277d0e34957114cbd6c797fc9d95d8b582d225292076d4eef5", "abc"},
+		TestHash{"bda164f0b930c43a1bacb5df880b205d15ac847add35145bf25d991ae74f0b72b1ac794f8aacda5fcb3c47038c954742b1857b5856519de4d1e54bfa2fa4eac5", "abcd"},
+		TestHash{"5d745e26ccb20fe655d39c9e7f69455758fbae541cb892b3581e4869244ab35b4fd6078f5d28b1f1a217452a67d9801033d92724a221255a5e377fe9e9e5f0b2", "abcde"},
+		TestHash{"a73e425459567308ba5f9eb2ae23570d0d0575eb1357ecf6ac88d4e0358b0ac3ea2371261f5d4c070211784b525911b9eec0ad968429bb7c7891d341cff4e811", "abcdef"},
+		TestHash{"08b388f68fd3eb51906ac3d3c699b8e9c3ac65d7ceb49d2e34f8a482cbc3082bc401cead90e85a97b8647c948bf35e448740b79659f3bee42145f0bd653d1f25", "abcdefg"},
+		TestHash{"1f1a84d30612820243afe2022712f9dac6d07c4c8bb41b40eacab0184c8d82275da5bcadbb35c7ca1960ff21c90acbae8c14e48d9309e4819027900e882c7ad9", "abcdefgh"},
+		TestHash{"11882bc9a31ac1cf1c41dcd9fd6fdd3ccdb9b017fc7f4582680134f314d7bb49af4c71f5a920bc0a6a3c1ff9a00021bf361d9867fe636b0bc1da1552e4237de4", "abcdefghi"},
+		TestHash{"717163de24809ffcf7ff6d5aba72b8d67c2129721953c252a4ddfb107614be857cbd76a9d5927de14633d6bdc9ddf335160b919db5c6f12cb2e6549181912eef", "abcdefghij"},
+		TestHash{"b97de512e91e3828b40d2b0fdce9ceb3c4a71f9bea8d88e75c4fa854df36725fd2b52eb6544edcacd6f8beddfea403cb55ae31f03ad62a5ef54e42ee82c3fb35", "The quick brown fox jumps over the lazy dog"},
+		TestHash{"c27ba124205f72e6847f3e19834f925cc666d0974167af915bb462420ed40cc50900d85a1f923219d832357750492d5c143011a76988344c2635e69d06f2d38c", "The quick brown fox jumps over the lazy eog"},
+	}
+	for v, _ in test_vectors {
+		computed     := whirlpool.hash(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_gost :: proc(t: ^testing.T) {
-    test_vectors := [?]TestHash {
-        TestHash{"981e5f3ca30c841487830f84fb433e13ac1101569b9c13584ac483234cd656c0", ""},
-        TestHash{"e74c52dd282183bf37af0079c9f78055715a103f17e3133ceff1aacf2f403011", "a"},
-        TestHash{"b285056dbf18d7392d7677369524dd14747459ed8143997e163b2986f92fd42c", "abc"},
-        TestHash{"bc6041dd2aa401ebfa6e9886734174febdb4729aa972d60f549ac39b29721ba0", "message digest"},
-        TestHash{"9004294a361a508c586fe53d1f1b02746765e71b765472786e4770d565830a76", "The quick brown fox jumps over the lazy dog"},
-        TestHash{"73b70a39497de53a6e08c67b6d4db853540f03e9389299d9b0156ef7e85d0f61", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
-        TestHash{"6bc7b38989b28cf93ae8842bf9d752905910a7528a61e5bce0782de43e610c90", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
-        TestHash{"2cefc2f7b7bdc514e18ea57fa74ff357e7fa17d652c75f69cb1be7893ede48eb", "This is message, length=32 bytes"},
-        TestHash{"c3730c5cbccacf915ac292676f21e8bd4ef75331d9405e5f1a61dc3130a65011", "Suppose the original message has length = 50 bytes"},
-    }
-    for v, _ in test_vectors {
-        computed     := gost.hash(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors := [?]TestHash {
+		TestHash{"981e5f3ca30c841487830f84fb433e13ac1101569b9c13584ac483234cd656c0", ""},
+		TestHash{"e74c52dd282183bf37af0079c9f78055715a103f17e3133ceff1aacf2f403011", "a"},
+		TestHash{"b285056dbf18d7392d7677369524dd14747459ed8143997e163b2986f92fd42c", "abc"},
+		TestHash{"bc6041dd2aa401ebfa6e9886734174febdb4729aa972d60f549ac39b29721ba0", "message digest"},
+		TestHash{"9004294a361a508c586fe53d1f1b02746765e71b765472786e4770d565830a76", "The quick brown fox jumps over the lazy dog"},
+		TestHash{"73b70a39497de53a6e08c67b6d4db853540f03e9389299d9b0156ef7e85d0f61", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
+		TestHash{"6bc7b38989b28cf93ae8842bf9d752905910a7528a61e5bce0782de43e610c90", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
+		TestHash{"2cefc2f7b7bdc514e18ea57fa74ff357e7fa17d652c75f69cb1be7893ede48eb", "This is message, length=32 bytes"},
+		TestHash{"c3730c5cbccacf915ac292676f21e8bd4ef75331d9405e5f1a61dc3130a65011", "Suppose the original message has length = 50 bytes"},
+	}
+	for v, _ in test_vectors {
+		computed     := gost.hash(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_streebog_256 :: proc(t: ^testing.T) {
-    test_vectors := [?]TestHash {
-        TestHash{"3f539a213e97c802cc229d474c6aa32a825a360b2a933a949fd925208d9ce1bb", ""},
-        TestHash{"3e7dea7f2384b6c5a3d0e24aaa29c05e89ddd762145030ec22c71a6db8b2c1f4", "The quick brown fox jumps over the lazy dog"},
-        TestHash{"36816a824dcbe7d6171aa58500741f2ea2757ae2e1784ab72c5c3c6c198d71da", "The quick brown fox jumps over the lazy dog."},
-    }
-    for v, _ in test_vectors {
-        computed     := streebog.hash_256(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors := [?]TestHash {
+		TestHash{"3f539a213e97c802cc229d474c6aa32a825a360b2a933a949fd925208d9ce1bb", ""},
+		TestHash{"3e7dea7f2384b6c5a3d0e24aaa29c05e89ddd762145030ec22c71a6db8b2c1f4", "The quick brown fox jumps over the lazy dog"},
+		TestHash{"36816a824dcbe7d6171aa58500741f2ea2757ae2e1784ab72c5c3c6c198d71da", "The quick brown fox jumps over the lazy dog."},
+	}
+	for v, _ in test_vectors {
+		computed     := streebog.hash_256(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_streebog_512 :: proc(t: ^testing.T) {
-    test_vectors := [?]TestHash {
-        TestHash{"8e945da209aa869f0455928529bcae4679e9873ab707b55315f56ceb98bef0a7362f715528356ee83cda5f2aac4c6ad2ba3a715c1bcd81cb8e9f90bf4c1c1a8a", ""},
-        TestHash{"d2b793a0bb6cb5904828b5b6dcfb443bb8f33efc06ad09368878ae4cdc8245b97e60802469bed1e7c21a64ff0b179a6a1e0bb74d92965450a0adab69162c00fe", "The quick brown fox jumps over the lazy dog"},
-        TestHash{"fe0c42f267d921f940faa72bd9fcf84f9f1bd7e9d055e9816e4c2ace1ec83be82d2957cd59b86e123d8f5adee80b3ca08a017599a9fc1a14d940cf87c77df070", "The quick brown fox jumps over the lazy dog."},
-    }
-    for v, _ in test_vectors {
-        computed     := streebog.hash_512(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors := [?]TestHash {
+		TestHash{"8e945da209aa869f0455928529bcae4679e9873ab707b55315f56ceb98bef0a7362f715528356ee83cda5f2aac4c6ad2ba3a715c1bcd81cb8e9f90bf4c1c1a8a", ""},
+		TestHash{"d2b793a0bb6cb5904828b5b6dcfb443bb8f33efc06ad09368878ae4cdc8245b97e60802469bed1e7c21a64ff0b179a6a1e0bb74d92965450a0adab69162c00fe", "The quick brown fox jumps over the lazy dog"},
+		TestHash{"fe0c42f267d921f940faa72bd9fcf84f9f1bd7e9d055e9816e4c2ace1ec83be82d2957cd59b86e123d8f5adee80b3ca08a017599a9fc1a14d940cf87c77df070", "The quick brown fox jumps over the lazy dog."},
+	}
+	for v, _ in test_vectors {
+		computed     := streebog.hash_512(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_blake_224 :: proc(t: ^testing.T) {
-    test_vectors := [?]TestHash {
-        TestHash{"7dc5313b1c04512a174bd6503b89607aecbee0903d40a8a569c94eed", ""},
-        TestHash{"304c27fdbf308aea06955e331adc6814223a21fccd24c09fde9eda7b", "ube"},
-        TestHash{"cfb6848add73e1cb47994c4765df33b8f973702705a30a71fe4747a3", "BLAKE"},
-    }
-    for v, _ in test_vectors {
-        computed     := blake.hash_224(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors := [?]TestHash {
+		TestHash{"7dc5313b1c04512a174bd6503b89607aecbee0903d40a8a569c94eed", ""},
+		TestHash{"304c27fdbf308aea06955e331adc6814223a21fccd24c09fde9eda7b", "ube"},
+		TestHash{"cfb6848add73e1cb47994c4765df33b8f973702705a30a71fe4747a3", "BLAKE"},
+	}
+	for v, _ in test_vectors {
+		computed     := blake.hash_224(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_blake_256 :: proc(t: ^testing.T) {
-    test_vectors := [?]TestHash {
-        TestHash{"716f6e863f744b9ac22c97ec7b76ea5f5908bc5b2f67c61510bfc4751384ea7a", ""},
-        TestHash{"e802fe2a73fbe5853408f051d040aeb3a76a4d7a0fc5c3415d1af090f76a2c81", "ube"},
-        TestHash{"07663e00cf96fbc136cf7b1ee099c95346ba3920893d18cc8851f22ee2e36aa6", "BLAKE"},
-    }
-    for v, _ in test_vectors {
-        computed     := blake.hash_256(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors := [?]TestHash {
+		TestHash{"716f6e863f744b9ac22c97ec7b76ea5f5908bc5b2f67c61510bfc4751384ea7a", ""},
+		TestHash{"e802fe2a73fbe5853408f051d040aeb3a76a4d7a0fc5c3415d1af090f76a2c81", "ube"},
+		TestHash{"07663e00cf96fbc136cf7b1ee099c95346ba3920893d18cc8851f22ee2e36aa6", "BLAKE"},
+	}
+	for v, _ in test_vectors {
+		computed     := blake.hash_256(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_blake_384 :: proc(t: ^testing.T) {
-    test_vectors := [?]TestHash {
-        TestHash{"c6cbd89c926ab525c242e6621f2f5fa73aa4afe3d9e24aed727faaadd6af38b620bdb623dd2b4788b1c8086984af8706", ""},
-        TestHash{"8f22f120b2b99dd4fd32b98c8c83bd87abd6413f7317be936b1997511247fc68ae781c6f42113224ccbc1567b0e88593", "ube"},
-        TestHash{"f28742f7243990875d07e6afcff962edabdf7e9d19ddea6eae31d094c7fa6d9b00c8213a02ddf1e2d9894f3162345d85", "BLAKE"},
-    }
-    for v, _ in test_vectors {
-        computed     := blake.hash_384(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors := [?]TestHash {
+		TestHash{"c6cbd89c926ab525c242e6621f2f5fa73aa4afe3d9e24aed727faaadd6af38b620bdb623dd2b4788b1c8086984af8706", ""},
+		TestHash{"8f22f120b2b99dd4fd32b98c8c83bd87abd6413f7317be936b1997511247fc68ae781c6f42113224ccbc1567b0e88593", "ube"},
+		TestHash{"f28742f7243990875d07e6afcff962edabdf7e9d19ddea6eae31d094c7fa6d9b00c8213a02ddf1e2d9894f3162345d85", "BLAKE"},
+	}
+	for v, _ in test_vectors {
+		computed     := blake.hash_384(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_blake_512 :: proc(t: ^testing.T) {
-    test_vectors := [?]TestHash {
-        TestHash{"a8cfbbd73726062df0c6864dda65defe58ef0cc52a5625090fa17601e1eecd1b628e94f396ae402a00acc9eab77b4d4c2e852aaaa25a636d80af3fc7913ef5b8", ""},
-        TestHash{"49a24ca8f230936f938c19484d46b58f13ea4448ddadafecdf01419b1e1dd922680be2de84069187973ab61b10574da2ee50cbeaade68ea9391c8ec041b76be0", "ube"},
-        TestHash{"7bf805d0d8de36802b882e65d0515aa7682a2be97a9d9ec1399f4be2eff7de07684d7099124c8ac81c1c7c200d24ba68c6222e75062e04feb0e9dd589aa6e3b7", "BLAKE"},
-    }
-    for v, _ in test_vectors {
-        computed     := blake.hash_512(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors := [?]TestHash {
+		TestHash{"a8cfbbd73726062df0c6864dda65defe58ef0cc52a5625090fa17601e1eecd1b628e94f396ae402a00acc9eab77b4d4c2e852aaaa25a636d80af3fc7913ef5b8", ""},
+		TestHash{"49a24ca8f230936f938c19484d46b58f13ea4448ddadafecdf01419b1e1dd922680be2de84069187973ab61b10574da2ee50cbeaade68ea9391c8ec041b76be0", "ube"},
+		TestHash{"7bf805d0d8de36802b882e65d0515aa7682a2be97a9d9ec1399f4be2eff7de07684d7099124c8ac81c1c7c200d24ba68c6222e75062e04feb0e9dd589aa6e3b7", "BLAKE"},
+	}
+	for v, _ in test_vectors {
+		computed     := blake.hash_512(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_blake2b :: proc(t: ^testing.T) {
-    test_vectors := [?]TestHash {
-        TestHash{"786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce", ""},
-        TestHash{"a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918", "The quick brown fox jumps over the lazy dog"},
-    }
-    for v, _ in test_vectors {
-        computed     := blake2b.hash(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors := [?]TestHash {
+		TestHash{"786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce", ""},
+		TestHash{"a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918", "The quick brown fox jumps over the lazy dog"},
+	}
+	for v, _ in test_vectors {
+		computed     := blake2b.hash(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_blake2s :: proc(t: ^testing.T) {
-    test_vectors := [?]TestHash {
-        TestHash{"69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9", ""},
-        TestHash{"606beeec743ccbeff6cbcdf5d5302aa855c256c29b88c8ed331ea1a6bf3c8812", "The quick brown fox jumps over the lazy dog"},
-    }
-    for v, _ in test_vectors {
-        computed     := blake2s.hash(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors := [?]TestHash {
+		TestHash{"69217a3079908094e11121d042354a7c1f55b6482ca1a51e1b250dfd1ed0eef9", ""},
+		TestHash{"606beeec743ccbeff6cbcdf5d5302aa855c256c29b88c8ed331ea1a6bf3c8812", "The quick brown fox jumps over the lazy dog"},
+	}
+	for v, _ in test_vectors {
+		computed     := blake2s.hash(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_ripemd_128 :: proc(t: ^testing.T) {
-    // Test vectors from 
-    // https://homes.esat.kuleuven.be/~bosselae/ripemd160.html
-    test_vectors := [?]TestHash {
-        TestHash{"cdf26213a150dc3ecb610f18f6b38b46", ""},
-        TestHash{"86be7afa339d0fc7cfc785e72f578d33", "a"},
-        TestHash{"c14a12199c66e4ba84636b0f69144c77", "abc"},
-        TestHash{"9e327b3d6e523062afc1132d7df9d1b8", "message digest"},
-        TestHash{"fd2aa607f71dc8f510714922b371834e", "abcdefghijklmnopqrstuvwxyz"},
-        TestHash{"a1aa0689d0fafa2ddc22e88b49133a06", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
-        TestHash{"d1e959eb179c911faea4624c60c5c702", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
-    }
-    for v, _ in test_vectors {
-        computed     := ripemd.hash_128(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	// Test vectors from
+	// https://homes.esat.kuleuven.be/~bosselae/ripemd160.html
+	test_vectors := [?]TestHash {
+		TestHash{"cdf26213a150dc3ecb610f18f6b38b46", ""},
+		TestHash{"86be7afa339d0fc7cfc785e72f578d33", "a"},
+		TestHash{"c14a12199c66e4ba84636b0f69144c77", "abc"},
+		TestHash{"9e327b3d6e523062afc1132d7df9d1b8", "message digest"},
+		TestHash{"fd2aa607f71dc8f510714922b371834e", "abcdefghijklmnopqrstuvwxyz"},
+		TestHash{"a1aa0689d0fafa2ddc22e88b49133a06", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
+		TestHash{"d1e959eb179c911faea4624c60c5c702", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
+	}
+	for v, _ in test_vectors {
+		computed     := ripemd.hash_128(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_ripemd_160 :: proc(t: ^testing.T) {
-    // Test vectors from 
-    // https://homes.esat.kuleuven.be/~bosselae/ripemd160.html
-    test_vectors := [?]TestHash {
-        TestHash{"9c1185a5c5e9fc54612808977ee8f548b2258d31", ""},
-        TestHash{"0bdc9d2d256b3ee9daae347be6f4dc835a467ffe", "a"},
-        TestHash{"8eb208f7e05d987a9b044a8e98c6b087f15a0bfc", "abc"},
-        TestHash{"5d0689ef49d2fae572b881b123a85ffa21595f36", "message digest"},
-        TestHash{"f71c27109c692c1b56bbdceb5b9d2865b3708dbc", "abcdefghijklmnopqrstuvwxyz"},
-        TestHash{"12a053384a9c0c88e405a06c27dcf49ada62eb2b", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
-        TestHash{"b0e20b6e3116640286ed3a87a5713079b21f5189", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
-    }
-    for v, _ in test_vectors {
-        computed     := ripemd.hash_160(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	// Test vectors from
+	// https://homes.esat.kuleuven.be/~bosselae/ripemd160.html
+	test_vectors := [?]TestHash {
+		TestHash{"9c1185a5c5e9fc54612808977ee8f548b2258d31", ""},
+		TestHash{"0bdc9d2d256b3ee9daae347be6f4dc835a467ffe", "a"},
+		TestHash{"8eb208f7e05d987a9b044a8e98c6b087f15a0bfc", "abc"},
+		TestHash{"5d0689ef49d2fae572b881b123a85ffa21595f36", "message digest"},
+		TestHash{"f71c27109c692c1b56bbdceb5b9d2865b3708dbc", "abcdefghijklmnopqrstuvwxyz"},
+		TestHash{"12a053384a9c0c88e405a06c27dcf49ada62eb2b", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
+		TestHash{"b0e20b6e3116640286ed3a87a5713079b21f5189", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
+	}
+	for v, _ in test_vectors {
+		computed     := ripemd.hash_160(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_ripemd_256 :: proc(t: ^testing.T) {
-    // Test vectors from 
-    // https://homes.esat.kuleuven.be/~bosselae/ripemd160.html
-    test_vectors := [?]TestHash {
-        TestHash{"02ba4c4e5f8ecd1877fc52d64d30e37a2d9774fb1e5d026380ae0168e3c5522d", ""},
-        TestHash{"f9333e45d857f5d90a91bab70a1eba0cfb1be4b0783c9acfcd883a9134692925", "a"},
-        TestHash{"afbd6e228b9d8cbbcef5ca2d03e6dba10ac0bc7dcbe4680e1e42d2e975459b65", "abc"},
-        TestHash{"87e971759a1ce47a514d5c914c392c9018c7c46bc14465554afcdf54a5070c0e", "message digest"},
-        TestHash{"649d3034751ea216776bf9a18acc81bc7896118a5197968782dd1fd97d8d5133", "abcdefghijklmnopqrstuvwxyz"},
-        TestHash{"3843045583aac6c8c8d9128573e7a9809afb2a0f34ccc36ea9e72f16f6368e3f", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
-        TestHash{"5740a408ac16b720b84424ae931cbb1fe363d1d0bf4017f1a89f7ea6de77a0b8", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, 
-    }
-    for v, _ in test_vectors {
-        computed     := ripemd.hash_256(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	// Test vectors from
+	// https://homes.esat.kuleuven.be/~bosselae/ripemd160.html
+	test_vectors := [?]TestHash {
+		TestHash{"02ba4c4e5f8ecd1877fc52d64d30e37a2d9774fb1e5d026380ae0168e3c5522d", ""},
+		TestHash{"f9333e45d857f5d90a91bab70a1eba0cfb1be4b0783c9acfcd883a9134692925", "a"},
+		TestHash{"afbd6e228b9d8cbbcef5ca2d03e6dba10ac0bc7dcbe4680e1e42d2e975459b65", "abc"},
+		TestHash{"87e971759a1ce47a514d5c914c392c9018c7c46bc14465554afcdf54a5070c0e", "message digest"},
+		TestHash{"649d3034751ea216776bf9a18acc81bc7896118a5197968782dd1fd97d8d5133", "abcdefghijklmnopqrstuvwxyz"},
+		TestHash{"3843045583aac6c8c8d9128573e7a9809afb2a0f34ccc36ea9e72f16f6368e3f", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
+		TestHash{"5740a408ac16b720b84424ae931cbb1fe363d1d0bf4017f1a89f7ea6de77a0b8", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
+	}
+	for v, _ in test_vectors {
+		computed     := ripemd.hash_256(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_ripemd_320 :: proc(t: ^testing.T) {
-    // Test vectors from 
-    // https://homes.esat.kuleuven.be/~bosselae/ripemd160.html
-    test_vectors := [?]TestHash {
-        TestHash{"22d65d5661536cdc75c1fdf5c6de7b41b9f27325ebc61e8557177d705a0ec880151c3a32a00899b8", ""},
-        TestHash{"ce78850638f92658a5a585097579926dda667a5716562cfcf6fbe77f63542f99b04705d6970dff5d", "a"},
-        TestHash{"de4c01b3054f8930a79d09ae738e92301e5a17085beffdc1b8d116713e74f82fa942d64cdbc4682d", "abc"},
-        TestHash{"3a8e28502ed45d422f68844f9dd316e7b98533fa3f2a91d29f84d425c88d6b4eff727df66a7c0197", "message digest"},
-        TestHash{"cabdb1810b92470a2093aa6bce05952c28348cf43ff60841975166bb40ed234004b8824463e6b009", "abcdefghijklmnopqrstuvwxyz"},
-        TestHash{"d034a7950cf722021ba4b84df769a5de2060e259df4c9bb4a4268c0e935bbc7470a969c9d072a1ac", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
-        TestHash{"ed544940c86d67f250d232c30b7b3e5770e0c60c8cb9a4cafe3b11388af9920e1b99230b843c86a4", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
-    }
-    for v, _ in test_vectors {
-        computed     := ripemd.hash_320(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	// Test vectors from
+	// https://homes.esat.kuleuven.be/~bosselae/ripemd160.html
+	test_vectors := [?]TestHash {
+		TestHash{"22d65d5661536cdc75c1fdf5c6de7b41b9f27325ebc61e8557177d705a0ec880151c3a32a00899b8", ""},
+		TestHash{"ce78850638f92658a5a585097579926dda667a5716562cfcf6fbe77f63542f99b04705d6970dff5d", "a"},
+		TestHash{"de4c01b3054f8930a79d09ae738e92301e5a17085beffdc1b8d116713e74f82fa942d64cdbc4682d", "abc"},
+		TestHash{"3a8e28502ed45d422f68844f9dd316e7b98533fa3f2a91d29f84d425c88d6b4eff727df66a7c0197", "message digest"},
+		TestHash{"cabdb1810b92470a2093aa6bce05952c28348cf43ff60841975166bb40ed234004b8824463e6b009", "abcdefghijklmnopqrstuvwxyz"},
+		TestHash{"d034a7950cf722021ba4b84df769a5de2060e259df4c9bb4a4268c0e935bbc7470a969c9d072a1ac", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
+		TestHash{"ed544940c86d67f250d232c30b7b3e5770e0c60c8cb9a4cafe3b11388af9920e1b99230b843c86a4", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
+	}
+	for v, _ in test_vectors {
+		computed     := ripemd.hash_320(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_tiger_128 :: proc(t: ^testing.T) {
-    test_vectors := [?]TestHash {
-        TestHash{"3293ac630c13f0245f92bbb1766e1616", ""},
-        TestHash{"77befbef2e7ef8ab2ec8f93bf587a7fc", "a"},
-        TestHash{"2aab1484e8c158f2bfb8c5ff41b57a52", "abc"},
-        TestHash{"d981f8cb78201a950dcf3048751e441c", "message digest"},
-        TestHash{"1714a472eee57d30040412bfcc55032a", "abcdefghijklmnopqrstuvwxyz"},
-        TestHash{"0f7bf9a19b9c58f2b7610df7e84f0ac3", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
-        TestHash{"8dcea680a17583ee502ba38a3c368651", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
-        TestHash{"1c14795529fd9f207a958f84c52f11e8", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
-        TestHash{"6d12a41e72e644f017b6f0e2f7b44c62", "The quick brown fox jumps over the lazy dog"},
-    }
-    for v, _ in test_vectors {
-        computed     := tiger.hash_128(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors := [?]TestHash {
+		TestHash{"3293ac630c13f0245f92bbb1766e1616", ""},
+		TestHash{"77befbef2e7ef8ab2ec8f93bf587a7fc", "a"},
+		TestHash{"2aab1484e8c158f2bfb8c5ff41b57a52", "abc"},
+		TestHash{"d981f8cb78201a950dcf3048751e441c", "message digest"},
+		TestHash{"1714a472eee57d30040412bfcc55032a", "abcdefghijklmnopqrstuvwxyz"},
+		TestHash{"0f7bf9a19b9c58f2b7610df7e84f0ac3", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
+		TestHash{"8dcea680a17583ee502ba38a3c368651", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
+		TestHash{"1c14795529fd9f207a958f84c52f11e8", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
+		TestHash{"6d12a41e72e644f017b6f0e2f7b44c62", "The quick brown fox jumps over the lazy dog"},
+	}
+	for v, _ in test_vectors {
+		computed     := tiger.hash_128(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_tiger_160 :: proc(t: ^testing.T) {
-    test_vectors := [?]TestHash {
-        TestHash{"3293ac630c13f0245f92bbb1766e16167a4e5849", ""},
-        TestHash{"77befbef2e7ef8ab2ec8f93bf587a7fc613e247f", "a"},
-        TestHash{"2aab1484e8c158f2bfb8c5ff41b57a525129131c", "abc"},
-        TestHash{"d981f8cb78201a950dcf3048751e441c517fca1a", "message digest"},
-        TestHash{"1714a472eee57d30040412bfcc55032a0b11602f", "abcdefghijklmnopqrstuvwxyz"},
-        TestHash{"0f7bf9a19b9c58f2b7610df7e84f0ac3a71c631e", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
-        TestHash{"8dcea680a17583ee502ba38a3c368651890ffbcc", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
-        TestHash{"1c14795529fd9f207a958f84c52f11e887fa0cab", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
-        TestHash{"6d12a41e72e644f017b6f0e2f7b44c6285f06dd5", "The quick brown fox jumps over the lazy dog"},
-    }
-    for v, _ in test_vectors {
-        computed     := tiger.hash_160(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors := [?]TestHash {
+		TestHash{"3293ac630c13f0245f92bbb1766e16167a4e5849", ""},
+		TestHash{"77befbef2e7ef8ab2ec8f93bf587a7fc613e247f", "a"},
+		TestHash{"2aab1484e8c158f2bfb8c5ff41b57a525129131c", "abc"},
+		TestHash{"d981f8cb78201a950dcf3048751e441c517fca1a", "message digest"},
+		TestHash{"1714a472eee57d30040412bfcc55032a0b11602f", "abcdefghijklmnopqrstuvwxyz"},
+		TestHash{"0f7bf9a19b9c58f2b7610df7e84f0ac3a71c631e", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
+		TestHash{"8dcea680a17583ee502ba38a3c368651890ffbcc", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
+		TestHash{"1c14795529fd9f207a958f84c52f11e887fa0cab", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
+		TestHash{"6d12a41e72e644f017b6f0e2f7b44c6285f06dd5", "The quick brown fox jumps over the lazy dog"},
+	}
+	for v, _ in test_vectors {
+		computed     := tiger.hash_160(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_tiger_192 :: proc(t: ^testing.T) {
-    test_vectors := [?]TestHash {
-        TestHash{"3293ac630c13f0245f92bbb1766e16167a4e58492dde73f3", ""},
-        TestHash{"77befbef2e7ef8ab2ec8f93bf587a7fc613e247f5f247809", "a"},
-        TestHash{"2aab1484e8c158f2bfb8c5ff41b57a525129131c957b5f93", "abc"},
-        TestHash{"d981f8cb78201a950dcf3048751e441c517fca1aa55a29f6", "message digest"},
-        TestHash{"1714a472eee57d30040412bfcc55032a0b11602ff37beee9", "abcdefghijklmnopqrstuvwxyz"},
-        TestHash{"0f7bf9a19b9c58f2b7610df7e84f0ac3a71c631e7b53f78e", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
-        TestHash{"8dcea680a17583ee502ba38a3c368651890ffbccdc49a8cc", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
-        TestHash{"1c14795529fd9f207a958f84c52f11e887fa0cabdfd91bfd", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
-        TestHash{"6d12a41e72e644f017b6f0e2f7b44c6285f06dd5d2c5b075", "The quick brown fox jumps over the lazy dog"},
-    }
-    for v, _ in test_vectors {
-        computed     := tiger.hash_192(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors := [?]TestHash {
+		TestHash{"3293ac630c13f0245f92bbb1766e16167a4e58492dde73f3", ""},
+		TestHash{"77befbef2e7ef8ab2ec8f93bf587a7fc613e247f5f247809", "a"},
+		TestHash{"2aab1484e8c158f2bfb8c5ff41b57a525129131c957b5f93", "abc"},
+		TestHash{"d981f8cb78201a950dcf3048751e441c517fca1aa55a29f6", "message digest"},
+		TestHash{"1714a472eee57d30040412bfcc55032a0b11602ff37beee9", "abcdefghijklmnopqrstuvwxyz"},
+		TestHash{"0f7bf9a19b9c58f2b7610df7e84f0ac3a71c631e7b53f78e", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
+		TestHash{"8dcea680a17583ee502ba38a3c368651890ffbccdc49a8cc", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
+		TestHash{"1c14795529fd9f207a958f84c52f11e887fa0cabdfd91bfd", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
+		TestHash{"6d12a41e72e644f017b6f0e2f7b44c6285f06dd5d2c5b075", "The quick brown fox jumps over the lazy dog"},
+	}
+	for v, _ in test_vectors {
+		computed     := tiger.hash_192(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_tiger2_128 :: proc(t: ^testing.T) {
-    test_vectors := [?]TestHash {
-        TestHash{"4441be75f6018773c206c22745374b92", ""},
-        TestHash{"976abff8062a2e9dcea3a1ace966ed9c", "The quick brown fox jumps over the lazy dog"},
-        TestHash{"09c11330283a27efb51930aa7dc1ec62", "The quick brown fox jumps over the lazy cog"},
-    }
-    for v, _ in test_vectors {
-        computed     := tiger2.hash_128(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors := [?]TestHash {
+		TestHash{"4441be75f6018773c206c22745374b92", ""},
+		TestHash{"976abff8062a2e9dcea3a1ace966ed9c", "The quick brown fox jumps over the lazy dog"},
+		TestHash{"09c11330283a27efb51930aa7dc1ec62", "The quick brown fox jumps over the lazy cog"},
+	}
+	for v, _ in test_vectors {
+		computed     := tiger2.hash_128(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_tiger2_160 :: proc(t: ^testing.T) {
-    test_vectors := [?]TestHash {
-        TestHash{"4441be75f6018773c206c22745374b924aa8313f", ""},
-        TestHash{"976abff8062a2e9dcea3a1ace966ed9c19cb8555", "The quick brown fox jumps over the lazy dog"},
-        TestHash{"09c11330283a27efb51930aa7dc1ec624ff738a8", "The quick brown fox jumps over the lazy cog"},
-    }
-    for v, _ in test_vectors {
-        computed     := tiger2.hash_160(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors := [?]TestHash {
+		TestHash{"4441be75f6018773c206c22745374b924aa8313f", ""},
+		TestHash{"976abff8062a2e9dcea3a1ace966ed9c19cb8555", "The quick brown fox jumps over the lazy dog"},
+		TestHash{"09c11330283a27efb51930aa7dc1ec624ff738a8", "The quick brown fox jumps over the lazy cog"},
+	}
+	for v, _ in test_vectors {
+		computed     := tiger2.hash_160(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_tiger2_192 :: proc(t: ^testing.T) {
-    test_vectors := [?]TestHash {
-        TestHash{"4441be75f6018773c206c22745374b924aa8313fef919f41", ""},
-        TestHash{"976abff8062a2e9dcea3a1ace966ed9c19cb85558b4976d8", "The quick brown fox jumps over the lazy dog"},
-        TestHash{"09c11330283a27efb51930aa7dc1ec624ff738a8d9bdd3df", "The quick brown fox jumps over the lazy cog"},
-    }
-    for v, _ in test_vectors {
-        computed     := tiger2.hash_192(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors := [?]TestHash {
+		TestHash{"4441be75f6018773c206c22745374b924aa8313fef919f41", ""},
+		TestHash{"976abff8062a2e9dcea3a1ace966ed9c19cb85558b4976d8", "The quick brown fox jumps over the lazy dog"},
+		TestHash{"09c11330283a27efb51930aa7dc1ec624ff738a8d9bdd3df", "The quick brown fox jumps over the lazy cog"},
+	}
+	for v, _ in test_vectors {
+		computed     := tiger2.hash_192(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_sm3 :: proc(t: ^testing.T) {
-    test_vectors := [?]TestHash {
-        TestHash{"1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b", ""},
-        TestHash{"66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0", "abc"},
-        TestHash{"debe9ff92275b8a138604889c18e5a4d6fdb70e5387e5765293dcba39c0c5732", "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd"}, 
-        TestHash{"5fdfe814b8573ca021983970fc79b2218c9570369b4859684e2e4c3fc76cb8ea", "The quick brown fox jumps over the lazy dog"},
-        TestHash{"ca27d14a42fc04c1e5ecf574a95a8c2d70ecb5805e9b429026ccac8f28b20098", "The quick brown fox jumps over the lazy cog"},
-    }
-    for v, _ in test_vectors {
-        computed     := sm3.hash(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors := [?]TestHash {
+		TestHash{"1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b", ""},
+		TestHash{"66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0", "abc"},
+		TestHash{"debe9ff92275b8a138604889c18e5a4d6fdb70e5387e5765293dcba39c0c5732", "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd"},
+		TestHash{"5fdfe814b8573ca021983970fc79b2218c9570369b4859684e2e4c3fc76cb8ea", "The quick brown fox jumps over the lazy dog"},
+		TestHash{"ca27d14a42fc04c1e5ecf574a95a8c2d70ecb5805e9b429026ccac8f28b20098", "The quick brown fox jumps over the lazy cog"},
+	}
+	for v, _ in test_vectors {
+		computed     := sm3.hash(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_jh_224 :: proc(t: ^testing.T) {
-    test_vectors := [?]TestHash {
-        TestHash{"2c99df889b019309051c60fecc2bd285a774940e43175b76b2626630", ""},
-        TestHash{"e715f969fb61b203a97e494aab92d91a9cec52f0933436b0d63bf722", "a"},
-        TestHash{"c2b1967e635bd55b6a4d36f863ac4a877be302251d68692873007281", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
-    }
-    for v, _ in test_vectors {
-        computed     := jh.hash_224(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors := [?]TestHash {
+		TestHash{"2c99df889b019309051c60fecc2bd285a774940e43175b76b2626630", ""},
+		TestHash{"e715f969fb61b203a97e494aab92d91a9cec52f0933436b0d63bf722", "a"},
+		TestHash{"c2b1967e635bd55b6a4d36f863ac4a877be302251d68692873007281", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
+	}
+	for v, _ in test_vectors {
+		computed     := jh.hash_224(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_jh_256 :: proc(t: ^testing.T) {
-    test_vectors := [?]TestHash {
-        TestHash{"46e64619c18bb0a92a5e87185a47eef83ca747b8fcc8e1412921357e326df434", ""},
-        TestHash{"d52c0c130a1bc0ae5136375637a52773e150c71efe1c968df8956f6745b05386", "a"},
-        TestHash{"fc4214867025a8af94c614353b3553b10e561ae749fc18c40e5fd44a7a4ecd1b", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
-    }
-    for v, _ in test_vectors {
-        computed     := jh.hash_256(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors := [?]TestHash {
+		TestHash{"46e64619c18bb0a92a5e87185a47eef83ca747b8fcc8e1412921357e326df434", ""},
+		TestHash{"d52c0c130a1bc0ae5136375637a52773e150c71efe1c968df8956f6745b05386", "a"},
+		TestHash{"fc4214867025a8af94c614353b3553b10e561ae749fc18c40e5fd44a7a4ecd1b", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
+	}
+	for v, _ in test_vectors {
+		computed     := jh.hash_256(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_jh_384 :: proc(t: ^testing.T) {
-    test_vectors := [?]TestHash {
-        TestHash{"2fe5f71b1b3290d3c017fb3c1a4d02a5cbeb03a0476481e25082434a881994b0ff99e078d2c16b105ad069b569315328", ""},
-        TestHash{"77de897ca4fd5dadfbcbd1d8d4ea3c3c1426855e38661325853e92b069f3fe156729f6bbb9a5892c7c18a77f1cb9d0bb", "a"},
-        TestHash{"6f73d9b9b8ed362f8180fb26020725b40bd6ca75b3b947405f26c4c37a885ce028876dc42e379d2faf6146fed3ea0e42", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
-    }
-    for v, _ in test_vectors {
-        computed     := jh.hash_384(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors := [?]TestHash {
+		TestHash{"2fe5f71b1b3290d3c017fb3c1a4d02a5cbeb03a0476481e25082434a881994b0ff99e078d2c16b105ad069b569315328", ""},
+		TestHash{"77de897ca4fd5dadfbcbd1d8d4ea3c3c1426855e38661325853e92b069f3fe156729f6bbb9a5892c7c18a77f1cb9d0bb", "a"},
+		TestHash{"6f73d9b9b8ed362f8180fb26020725b40bd6ca75b3b947405f26c4c37a885ce028876dc42e379d2faf6146fed3ea0e42", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
+	}
+	for v, _ in test_vectors {
+		computed     := jh.hash_384(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_jh_512 :: proc(t: ^testing.T) {
-    test_vectors := [?]TestHash {
-        TestHash{"90ecf2f76f9d2c8017d979ad5ab96b87d58fc8fc4b83060f3f900774faa2c8fabe69c5f4ff1ec2b61d6b316941cedee117fb04b1f4c5bc1b919ae841c50eec4f", ""},
-        TestHash{"f12c87e986daff17c481c81a99a39b603ca6bafcd320c5735523b97cb9a26f7681bad62ffad9aad0e21160a05f773fb0d1434ca4cbcb0483f480a171ada1561b", "a"},
-        TestHash{"bafb8e710b35eabeb1a48220c4b0987c2c985b6e73b7b31d164bfb9d67c94d99d7bc43b474a25e647cd6cc36334b6a00a5f2a85fae74907fd2885c6168132fe7", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
-    }
-    for v, _ in test_vectors {
-        computed     := jh.hash_512(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors := [?]TestHash {
+		TestHash{"90ecf2f76f9d2c8017d979ad5ab96b87d58fc8fc4b83060f3f900774faa2c8fabe69c5f4ff1ec2b61d6b316941cedee117fb04b1f4c5bc1b919ae841c50eec4f", ""},
+		TestHash{"f12c87e986daff17c481c81a99a39b603ca6bafcd320c5735523b97cb9a26f7681bad62ffad9aad0e21160a05f773fb0d1434ca4cbcb0483f480a171ada1561b", "a"},
+		TestHash{"bafb8e710b35eabeb1a48220c4b0987c2c985b6e73b7b31d164bfb9d67c94d99d7bc43b474a25e647cd6cc36334b6a00a5f2a85fae74907fd2885c6168132fe7", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
+	}
+	for v, _ in test_vectors {
+		computed     := jh.hash_512(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_groestl_224 :: proc(t: ^testing.T) {
-    test_vectors := [?]TestHash {
-        TestHash{"f2e180fb5947be964cd584e22e496242c6a329c577fc4ce8c36d34c3", ""},
-        TestHash{"2dfa5bd326c23c451b1202d99e6cee98a98c45927e1a31077f538712", "a"},
-        TestHash{"c8a3e7274d599900ae673419683c3626a2e49ed57308ed2687508bef", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
-    }
-    for v, _ in test_vectors {
-        computed     := groestl.hash_224(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors := [?]TestHash {
+		TestHash{"f2e180fb5947be964cd584e22e496242c6a329c577fc4ce8c36d34c3", ""},
+		TestHash{"2dfa5bd326c23c451b1202d99e6cee98a98c45927e1a31077f538712", "a"},
+		TestHash{"c8a3e7274d599900ae673419683c3626a2e49ed57308ed2687508bef", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
+	}
+	for v, _ in test_vectors {
+		computed     := groestl.hash_224(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_groestl_256 :: proc(t: ^testing.T) {
-    test_vectors := [?]TestHash {
-        TestHash{"1a52d11d550039be16107f9c58db9ebcc417f16f736adb2502567119f0083467", ""},
-        TestHash{"3645c245bb31223ad93c80885b719aa40b4bed0a9d9d6e7c11fe99e59ca350b5", "a"},
-        TestHash{"2679d98913bee62e57fdbdde97ddb328373548c6b24fc587cc3d08f2a02a529c", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
-    }
-    for v, _ in test_vectors {
-        computed     := groestl.hash_256(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors := [?]TestHash {
+		TestHash{"1a52d11d550039be16107f9c58db9ebcc417f16f736adb2502567119f0083467", ""},
+		TestHash{"3645c245bb31223ad93c80885b719aa40b4bed0a9d9d6e7c11fe99e59ca350b5", "a"},
+		TestHash{"2679d98913bee62e57fdbdde97ddb328373548c6b24fc587cc3d08f2a02a529c", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
+	}
+	for v, _ in test_vectors {
+		computed     := groestl.hash_256(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_groestl_384 :: proc(t: ^testing.T) {
-    test_vectors := [?]TestHash {
-        TestHash{"ac353c1095ace21439251007862d6c62f829ddbe6de4f78e68d310a9205a736d8b11d99bffe448f57a1cfa2934f044a5", ""},
-        TestHash{"13fce7bd9fc69b67cc12c77e765a0a97794c585f89df39fbff32408e060d7d9225c7e80fd87da647686888bda896c342", "a"},
-        TestHash{"1c446cd70a6de52c9db386f5305aae029fe5a4120bc6230b7cd3a5e1ef1949cc8e6d2548c24cd7347b5ba512628a62f6", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
-    }
-    for v, _ in test_vectors {
-        computed     := groestl.hash_384(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors := [?]TestHash {
+		TestHash{"ac353c1095ace21439251007862d6c62f829ddbe6de4f78e68d310a9205a736d8b11d99bffe448f57a1cfa2934f044a5", ""},
+		TestHash{"13fce7bd9fc69b67cc12c77e765a0a97794c585f89df39fbff32408e060d7d9225c7e80fd87da647686888bda896c342", "a"},
+		TestHash{"1c446cd70a6de52c9db386f5305aae029fe5a4120bc6230b7cd3a5e1ef1949cc8e6d2548c24cd7347b5ba512628a62f6", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
+	}
+	for v, _ in test_vectors {
+		computed     := groestl.hash_384(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_groestl_512 :: proc(t: ^testing.T) {
-    test_vectors := [?]TestHash {
-        TestHash{"6d3ad29d279110eef3adbd66de2a0345a77baede1557f5d099fce0c03d6dc2ba8e6d4a6633dfbd66053c20faa87d1a11f39a7fbe4a6c2f009801370308fc4ad8", ""},
-        TestHash{"9ef345a835ee35d6d0d462ce45f722d84b5ca41fde9c81a98a22cfb4f7425720511b03a258cdc055bf8e9179dc9bdb5d88bed906c71125d4cf0cd39d3d7bebc7", "a"},
-        TestHash{"862849fd911852cd54beefa88759db4cead0ef8e36aaf15398303c5c4cbc016d9b4c42b32081cbdcba710d2693e7663d244fae116ec29ffb40168baf44f944e7", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
-    }
-    for v, _ in test_vectors {
-        computed     := groestl.hash_512(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors := [?]TestHash {
+		TestHash{"6d3ad29d279110eef3adbd66de2a0345a77baede1557f5d099fce0c03d6dc2ba8e6d4a6633dfbd66053c20faa87d1a11f39a7fbe4a6c2f009801370308fc4ad8", ""},
+		TestHash{"9ef345a835ee35d6d0d462ce45f722d84b5ca41fde9c81a98a22cfb4f7425720511b03a258cdc055bf8e9179dc9bdb5d88bed906c71125d4cf0cd39d3d7bebc7", "a"},
+		TestHash{"862849fd911852cd54beefa88759db4cead0ef8e36aaf15398303c5c4cbc016d9b4c42b32081cbdcba710d2693e7663d244fae116ec29ffb40168baf44f944e7", "12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
+	}
+	for v, _ in test_vectors {
+		computed     := groestl.hash_512(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_haval_128 :: proc(t: ^testing.T) {
-    test_vectors_3 := [?]TestHash {
-        TestHash{"c68f39913f901f3ddf44c707357a7d70", ""},
-        TestHash{"0cd40739683e15f01ca5dbceef4059f1", "a"},
-        TestHash{"9e40ed883fb63e985d299b40cda2b8f2", "abc"},
-        TestHash{"3caf4a79e81adcd6d1716bcc1cef4573", "message digest"},
-        TestHash{"dc502247fb3eb8376109eda32d361d82", "abcdefghijklmnopqrstuvwxyz"},
-        TestHash{"44068770868768964d1f2c3bff4aa3d8", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
-        TestHash{"de5eb3f7d9eb08fae7a07d68e3047ec6", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},  
-    }
-    for v, _ in test_vectors_3 {
-        computed     := haval.hash_128_3(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
-    test_vectors_4 := [?]TestHash {
-        TestHash{"ee6bbf4d6a46a679b3a856c88538bb98", ""},
-        TestHash{"5cd07f03330c3b5020b29ba75911e17d", "a"},
-    }
-    for v, _ in test_vectors_4 {
-        computed     := haval.hash_128_4(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
-    test_vectors_5 := [?]TestHash {
-        TestHash{"184b8482a0c050dca54b59c7f05bf5dd", ""},
-        TestHash{"f23fbe704be8494bfa7a7fb4f8ab09e5", "a"},
-    }
-    for v, _ in test_vectors_5 {
-        computed     := haval.hash_128_5(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors_3 := [?]TestHash {
+		TestHash{"c68f39913f901f3ddf44c707357a7d70", ""},
+		TestHash{"0cd40739683e15f01ca5dbceef4059f1", "a"},
+		TestHash{"9e40ed883fb63e985d299b40cda2b8f2", "abc"},
+		TestHash{"3caf4a79e81adcd6d1716bcc1cef4573", "message digest"},
+		TestHash{"dc502247fb3eb8376109eda32d361d82", "abcdefghijklmnopqrstuvwxyz"},
+		TestHash{"44068770868768964d1f2c3bff4aa3d8", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
+		TestHash{"de5eb3f7d9eb08fae7a07d68e3047ec6", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
+	}
+	for v, _ in test_vectors_3 {
+		computed     := haval.hash_128_3(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
+	test_vectors_4 := [?]TestHash {
+		TestHash{"ee6bbf4d6a46a679b3a856c88538bb98", ""},
+		TestHash{"5cd07f03330c3b5020b29ba75911e17d", "a"},
+	}
+	for v, _ in test_vectors_4 {
+		computed     := haval.hash_128_4(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
+	test_vectors_5 := [?]TestHash {
+		TestHash{"184b8482a0c050dca54b59c7f05bf5dd", ""},
+		TestHash{"f23fbe704be8494bfa7a7fb4f8ab09e5", "a"},
+	}
+	for v, _ in test_vectors_5 {
+		computed     := haval.hash_128_5(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_haval_160 :: proc(t: ^testing.T) {
-    test_vectors_3 := [?]TestHash {
-        TestHash{"d353c3ae22a25401d257643836d7231a9a95f953", ""},
-        TestHash{"4da08f514a7275dbc4cece4a347385983983a830", "a"},
-        TestHash{"b21e876c4d391e2a897661149d83576b5530a089", "abc"},
-        TestHash{"43a47f6f1c016207f08be8115c0977bf155346da", "message digest"},
-        TestHash{"eba9fa6050f24c07c29d1834a60900ea4e32e61b", "abcdefghijklmnopqrstuvwxyz"},
-        TestHash{"c30bce448cf8cfe957c141e90c0a063497cdfeeb", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
-        TestHash{"97dc988d97caae757be7523c4e8d4ea63007a4b9", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"}, 
-    }
-    for v, _ in test_vectors_3 {
-        computed     := haval.hash_160_3(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
-    test_vectors_4 := [?]TestHash {
-        TestHash{"1d33aae1be4146dbaaca0b6e70d7a11f10801525", ""},
-        TestHash{"e0a5be29627332034d4dd8a910a1a0e6fe04084d", "a"},
-    }
-    for v, _ in test_vectors_4 {
-        computed     := haval.hash_160_4(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
-    test_vectors_5 := [?]TestHash {
-        TestHash{"255158cfc1eed1a7be7c55ddd64d9790415b933b", ""},
-        TestHash{"f5147df7abc5e3c81b031268927c2b5761b5a2b5", "a"},
-    }
-    for v, _ in test_vectors_5 {
-        computed     := haval.hash_160_5(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors_3 := [?]TestHash {
+		TestHash{"d353c3ae22a25401d257643836d7231a9a95f953", ""},
+		TestHash{"4da08f514a7275dbc4cece4a347385983983a830", "a"},
+		TestHash{"b21e876c4d391e2a897661149d83576b5530a089", "abc"},
+		TestHash{"43a47f6f1c016207f08be8115c0977bf155346da", "message digest"},
+		TestHash{"eba9fa6050f24c07c29d1834a60900ea4e32e61b", "abcdefghijklmnopqrstuvwxyz"},
+		TestHash{"c30bce448cf8cfe957c141e90c0a063497cdfeeb", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"},
+		TestHash{"97dc988d97caae757be7523c4e8d4ea63007a4b9", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
+	}
+	for v, _ in test_vectors_3 {
+		computed     := haval.hash_160_3(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
+	test_vectors_4 := [?]TestHash {
+		TestHash{"1d33aae1be4146dbaaca0b6e70d7a11f10801525", ""},
+		TestHash{"e0a5be29627332034d4dd8a910a1a0e6fe04084d", "a"},
+	}
+	for v, _ in test_vectors_4 {
+		computed     := haval.hash_160_4(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
+	test_vectors_5 := [?]TestHash {
+		TestHash{"255158cfc1eed1a7be7c55ddd64d9790415b933b", ""},
+		TestHash{"f5147df7abc5e3c81b031268927c2b5761b5a2b5", "a"},
+	}
+	for v, _ in test_vectors_5 {
+		computed     := haval.hash_160_5(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_haval_192 :: proc(t: ^testing.T) {
-    test_vectors_3 := [?]TestHash {
-        TestHash{"e9c48d7903eaf2a91c5b350151efcb175c0fc82de2289a4e", ""},
-        TestHash{"b359c8835647f5697472431c142731ff6e2cddcacc4f6e08", "a"},
-    }
-    for v, _ in test_vectors_3 {
-        computed     := haval.hash_192_3(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
-    test_vectors_4 := [?]TestHash {
-        TestHash{"4a8372945afa55c7dead800311272523ca19d42ea47b72da", ""},
-        TestHash{"856c19f86214ea9a8a2f0c4b758b973cce72a2d8ff55505c", "a"},
-    }
-    for v, _ in test_vectors_4 {
-        computed     := haval.hash_192_4(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
-    test_vectors_5 := [?]TestHash {
-        TestHash{"4839d0626f95935e17ee2fc4509387bbe2cc46cb382ffe85", ""},
-        TestHash{"5ffa3b3548a6e2cfc06b7908ceb5263595df67cf9c4b9341", "a"},
-    }
-    for v, _ in test_vectors_5 {
-        computed     := haval.hash_192_5(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors_3 := [?]TestHash {
+		TestHash{"e9c48d7903eaf2a91c5b350151efcb175c0fc82de2289a4e", ""},
+		TestHash{"b359c8835647f5697472431c142731ff6e2cddcacc4f6e08", "a"},
+	}
+	for v, _ in test_vectors_3 {
+		computed     := haval.hash_192_3(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
+	test_vectors_4 := [?]TestHash {
+		TestHash{"4a8372945afa55c7dead800311272523ca19d42ea47b72da", ""},
+		TestHash{"856c19f86214ea9a8a2f0c4b758b973cce72a2d8ff55505c", "a"},
+	}
+	for v, _ in test_vectors_4 {
+		computed     := haval.hash_192_4(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
+	test_vectors_5 := [?]TestHash {
+		TestHash{"4839d0626f95935e17ee2fc4509387bbe2cc46cb382ffe85", ""},
+		TestHash{"5ffa3b3548a6e2cfc06b7908ceb5263595df67cf9c4b9341", "a"},
+	}
+	for v, _ in test_vectors_5 {
+		computed     := haval.hash_192_5(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_haval_224 :: proc(t: ^testing.T) {
-    test_vectors_3 := [?]TestHash {
-        TestHash{"c5aae9d47bffcaaf84a8c6e7ccacd60a0dd1932be7b1a192b9214b6d", ""},
-        TestHash{"731814ba5605c59b673e4caae4ad28eeb515b3abc2b198336794e17b", "a"},
-    }
-    for v, _ in test_vectors_3 {
-        computed     := haval.hash_224_3(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
-    test_vectors_4 := [?]TestHash {
-        TestHash{"3e56243275b3b81561750550e36fcd676ad2f5dd9e15f2e89e6ed78e", ""},
-        TestHash{"742f1dbeeaf17f74960558b44f08aa98bdc7d967e6c0ab8f799b3ac1", "a"},
-    }
-    for v, _ in test_vectors_4 {
-        computed     := haval.hash_224_4(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
-    test_vectors_5 := [?]TestHash {
-        TestHash{"4a0513c032754f5582a758d35917ac9adf3854219b39e3ac77d1837e", ""},
-        TestHash{"67b3cb8d4068e3641fa4f156e03b52978b421947328bfb9168c7655d", "a"},
-    }
-    for v, _ in test_vectors_5 {
-        computed     := haval.hash_224_5(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors_3 := [?]TestHash {
+		TestHash{"c5aae9d47bffcaaf84a8c6e7ccacd60a0dd1932be7b1a192b9214b6d", ""},
+		TestHash{"731814ba5605c59b673e4caae4ad28eeb515b3abc2b198336794e17b", "a"},
+	}
+	for v, _ in test_vectors_3 {
+		computed     := haval.hash_224_3(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
+	test_vectors_4 := [?]TestHash {
+		TestHash{"3e56243275b3b81561750550e36fcd676ad2f5dd9e15f2e89e6ed78e", ""},
+		TestHash{"742f1dbeeaf17f74960558b44f08aa98bdc7d967e6c0ab8f799b3ac1", "a"},
+	}
+	for v, _ in test_vectors_4 {
+		computed     := haval.hash_224_4(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
+	test_vectors_5 := [?]TestHash {
+		TestHash{"4a0513c032754f5582a758d35917ac9adf3854219b39e3ac77d1837e", ""},
+		TestHash{"67b3cb8d4068e3641fa4f156e03b52978b421947328bfb9168c7655d", "a"},
+	}
+	for v, _ in test_vectors_5 {
+		computed     := haval.hash_224_5(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_haval_256 :: proc(t: ^testing.T) {
-    test_vectors_3 := [?]TestHash {
-        TestHash{"4f6938531f0bc8991f62da7bbd6f7de3fad44562b8c6f4ebf146d5b4e46f7c17", ""},
-        TestHash{"47c838fbb4081d9525a0ff9b1e2c05a98f625714e72db289010374e27db021d8", "a"},
-    }
-    for v, _ in test_vectors_3 {
-        computed     := haval.hash_256_3(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
-    test_vectors_4 := [?]TestHash {
-        TestHash{"c92b2e23091e80e375dadce26982482d197b1a2521be82da819f8ca2c579b99b", ""},
-        TestHash{"e686d2394a49b44d306ece295cf9021553221db132b36cc0ff5b593d39295899", "a"},
-    }
-    for v, _ in test_vectors_4 {
-        computed     := haval.hash_256_4(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
-    test_vectors_5 := [?]TestHash {
-        TestHash{"be417bb4dd5cfb76c7126f4f8eeb1553a449039307b1a3cd451dbfdc0fbbe330", ""},
-        TestHash{"de8fd5ee72a5e4265af0a756f4e1a1f65c9b2b2f47cf17ecf0d1b88679a3e22f", "a"},
-    }
-    for v, _ in test_vectors_5 {
-        computed     := haval.hash_256_5(v.str)
-        computed_str := hex_string(computed[:])
-        expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
-    }
+	test_vectors_3 := [?]TestHash {
+		TestHash{"4f6938531f0bc8991f62da7bbd6f7de3fad44562b8c6f4ebf146d5b4e46f7c17", ""},
+		TestHash{"47c838fbb4081d9525a0ff9b1e2c05a98f625714e72db289010374e27db021d8", "a"},
+	}
+	for v, _ in test_vectors_3 {
+		computed     := haval.hash_256_3(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
+	test_vectors_4 := [?]TestHash {
+		TestHash{"c92b2e23091e80e375dadce26982482d197b1a2521be82da819f8ca2c579b99b", ""},
+		TestHash{"e686d2394a49b44d306ece295cf9021553221db132b36cc0ff5b593d39295899", "a"},
+	}
+	for v, _ in test_vectors_4 {
+		computed     := haval.hash_256_4(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
+	test_vectors_5 := [?]TestHash {
+		TestHash{"be417bb4dd5cfb76c7126f4f8eeb1553a449039307b1a3cd451dbfdc0fbbe330", ""},
+		TestHash{"de8fd5ee72a5e4265af0a756f4e1a1f65c9b2b2f47cf17ecf0d1b88679a3e22f", "a"},
+	}
+	for v, _ in test_vectors_5 {
+		computed     := haval.hash_256_5(v.str)
+		computed_str := hex_string(computed[:])
+		expect(t, computed_str == v.hash, fmt.tprintf("Expected: %s for input of %s, but got %s instead", v.hash, v.str, computed_str))
+	}
 }
 
 @(test)
 test_siphash_2_4 :: proc(t: ^testing.T) {
-    // Test vectors from 
-    // https://github.com/veorq/SipHash/blob/master/vectors.h
-    test_vectors := [?]u64 {
-        0x726fdb47dd0e0e31, 0x74f839c593dc67fd, 0x0d6c8009d9a94f5a, 0x85676696d7fb7e2d,
-        0xcf2794e0277187b7, 0x18765564cd99a68d, 0xcbc9466e58fee3ce, 0xab0200f58b01d137,
-        0x93f5f5799a932462, 0x9e0082df0ba9e4b0, 0x7a5dbbc594ddb9f3, 0xf4b32f46226bada7,
-        0x751e8fbc860ee5fb, 0x14ea5627c0843d90, 0xf723ca908e7af2ee, 0xa129ca6149be45e5,
-        0x3f2acc7f57c29bdb, 0x699ae9f52cbe4794, 0x4bc1b3f0968dd39c, 0xbb6dc91da77961bd,
-        0xbed65cf21aa2ee98, 0xd0f2cbb02e3b67c7, 0x93536795e3a33e88, 0xa80c038ccd5ccec8,
-        0xb8ad50c6f649af94, 0xbce192de8a85b8ea, 0x17d835b85bbb15f3, 0x2f2e6163076bcfad,
-        0xde4daaaca71dc9a5, 0xa6a2506687956571, 0xad87a3535c49ef28, 0x32d892fad841c342,
-        0x7127512f72f27cce, 0xa7f32346f95978e3, 0x12e0b01abb051238, 0x15e034d40fa197ae,
-        0x314dffbe0815a3b4, 0x027990f029623981, 0xcadcd4e59ef40c4d, 0x9abfd8766a33735c,
-        0x0e3ea96b5304a7d0, 0xad0c42d6fc585992, 0x187306c89bc215a9, 0xd4a60abcf3792b95,
-        0xf935451de4f21df2, 0xa9538f0419755787, 0xdb9acddff56ca510, 0xd06c98cd5c0975eb,
-        0xe612a3cb9ecba951, 0xc766e62cfcadaf96, 0xee64435a9752fe72, 0xa192d576b245165a,
-        0x0a8787bf8ecb74b2, 0x81b3e73d20b49b6f, 0x7fa8220ba3b2ecea, 0x245731c13ca42499,
-        0xb78dbfaf3a8d83bd, 0xea1ad565322a1a0b, 0x60e61c23a3795013, 0x6606d7e446282b93,
-        0x6ca4ecb15c5f91e1, 0x9f626da15c9625f3, 0xe51b38608ef25f57, 0x958a324ceb064572,
-    }
-
-    key: [16]byte
-    for i in 0..<16 {
-        key[i] = byte(i)
-    }
-
-    for i in 0..<len(test_vectors) {
-        data := make([]byte, i)
-        for j in 0..<i {
-            data[j] = byte(j)
-        }
-
-        vector   := test_vectors[i]
-        computed := siphash.sum_2_4(data[:], key[:])
-
-        expect(t, computed == vector, fmt.tprintf("Expected: 0x%x for input of %v, but got 0x%x instead", vector, data, computed))
-    }  
+	// Test vectors from
+	// https://github.com/veorq/SipHash/blob/master/vectors.h
+	test_vectors := [?]u64 {
+		0x726fdb47dd0e0e31, 0x74f839c593dc67fd, 0x0d6c8009d9a94f5a, 0x85676696d7fb7e2d,
+		0xcf2794e0277187b7, 0x18765564cd99a68d, 0xcbc9466e58fee3ce, 0xab0200f58b01d137,
+		0x93f5f5799a932462, 0x9e0082df0ba9e4b0, 0x7a5dbbc594ddb9f3, 0xf4b32f46226bada7,
+		0x751e8fbc860ee5fb, 0x14ea5627c0843d90, 0xf723ca908e7af2ee, 0xa129ca6149be45e5,
+		0x3f2acc7f57c29bdb, 0x699ae9f52cbe4794, 0x4bc1b3f0968dd39c, 0xbb6dc91da77961bd,
+		0xbed65cf21aa2ee98, 0xd0f2cbb02e3b67c7, 0x93536795e3a33e88, 0xa80c038ccd5ccec8,
+		0xb8ad50c6f649af94, 0xbce192de8a85b8ea, 0x17d835b85bbb15f3, 0x2f2e6163076bcfad,
+		0xde4daaaca71dc9a5, 0xa6a2506687956571, 0xad87a3535c49ef28, 0x32d892fad841c342,
+		0x7127512f72f27cce, 0xa7f32346f95978e3, 0x12e0b01abb051238, 0x15e034d40fa197ae,
+		0x314dffbe0815a3b4, 0x027990f029623981, 0xcadcd4e59ef40c4d, 0x9abfd8766a33735c,
+		0x0e3ea96b5304a7d0, 0xad0c42d6fc585992, 0x187306c89bc215a9, 0xd4a60abcf3792b95,
+		0xf935451de4f21df2, 0xa9538f0419755787, 0xdb9acddff56ca510, 0xd06c98cd5c0975eb,
+		0xe612a3cb9ecba951, 0xc766e62cfcadaf96, 0xee64435a9752fe72, 0xa192d576b245165a,
+		0x0a8787bf8ecb74b2, 0x81b3e73d20b49b6f, 0x7fa8220ba3b2ecea, 0x245731c13ca42499,
+		0xb78dbfaf3a8d83bd, 0xea1ad565322a1a0b, 0x60e61c23a3795013, 0x6606d7e446282b93,
+		0x6ca4ecb15c5f91e1, 0x9f626da15c9625f3, 0xe51b38608ef25f57, 0x958a324ceb064572,
+	}
+
+	key: [16]byte
+	for i in 0..<16 {
+		key[i] = byte(i)
+	}
+
+	for i in 0..<len(test_vectors) {
+		data := make([]byte, i)
+		for j in 0..<i {
+			data[j] = byte(j)
+		}
+
+		vector   := test_vectors[i]
+		computed := siphash.sum_2_4(data[:], key[:])
+
+		expect(t, computed == vector, fmt.tprintf("Expected: 0x%x for input of %v, but got 0x%x instead", vector, data, computed))
+	}
 }

BIN
tests/core/crypto_hash


+ 21 - 19
tests/core/encoding/test_core_json.odin → tests/core/encoding/json/test_core_json.odin

@@ -3,37 +3,39 @@ package test_core_json
 import "core:encoding/json"
 import "core:testing"
 import "core:fmt"
+import "core:os"
 
 TEST_count := 0
 TEST_fail  := 0
 
 when ODIN_TEST {
-    expect  :: testing.expect
-    log     :: testing.log
+	expect  :: testing.expect
+	log     :: testing.log
 } else {
-    expect  :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
-        fmt.printf("[%v] ", loc)
-        TEST_count += 1
-        if !condition {
-            TEST_fail += 1
-            fmt.println(message)
-            return
-        }
-        fmt.println(" PASS")
-    }
-    log     :: proc(t: ^testing.T, v: any, loc := #caller_location) {
-        fmt.printf("[%v] ", loc)
-        fmt.printf("log: %v\n", v)
-    }
+	expect  :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
+		TEST_count += 1
+		if !condition {
+			TEST_fail += 1
+			fmt.printf("[%v] %v\n", loc, message)
+			return
+		}
+	}
+	log     :: proc(t: ^testing.T, v: any, loc := #caller_location) {
+		fmt.printf("[%v] ", loc)
+		fmt.printf("log: %v\n", v)
+	}
 }
 
 main :: proc() {
-    t := testing.T{}
-	
+	t := testing.T{}
+
 	parse_json(&t)
 	marshal_json(&t)
 
-    fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
+	fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
+	if TEST_fail > 0 {
+		os.exit(1)
+	}
 }
 
 @test

+ 156 - 0
tests/core/encoding/varint/test_core_varint.odin

@@ -0,0 +1,156 @@
+package test_core_varint
+
+import "core:encoding/varint"
+import "core:testing"
+import "core:fmt"
+import "core:os"
+import "core:slice"
+import "core:math/rand"
+
+TEST_count := 0
+TEST_fail  := 0
+
+RANDOM_TESTS :: 100
+
+when ODIN_TEST {
+	expect  :: testing.expect
+	log     :: testing.log
+} else {
+	expect  :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
+		TEST_count += 1
+		if !condition {
+			TEST_fail += 1
+			fmt.printf("[%v] %v\n", loc, message)
+			return
+		}
+	}
+	log     :: proc(t: ^testing.T, v: any, loc := #caller_location) {
+		fmt.printf("[%v] ", loc)
+		fmt.printf("log: %v\n", v)
+	}
+}
+
+main :: proc() {
+	t := testing.T{}
+
+	test_leb128(&t)
+
+	fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
+	if TEST_fail > 0 {
+		os.exit(1)
+	}
+}
+
+@(test)
+test_leb128 :: proc(t: ^testing.T) {
+	buf: [varint.LEB128_MAX_BYTES]u8
+
+	for vector in ULEB_Vectors {
+		val, size, err := varint.decode_uleb128(vector.encoded)
+
+		msg := fmt.tprintf("Expected %02x to decode to %v consuming %v bytes, got %v and %v", vector.encoded, vector.value, vector.size, val, size)
+		expect(t, size == vector.size && val == vector.value, msg)
+
+		msg  = fmt.tprintf("Expected decoder to return error %v, got %v", vector.error, err)
+		expect(t, err == vector.error, msg)
+
+		if err == .None { // Try to roundtrip
+			size, err = varint.encode_uleb128(buf[:], vector.value)
+
+			msg = fmt.tprintf("Expected %v to encode to %02x, got %02x", vector.value, vector.encoded, buf[:size])
+			expect(t, size == vector.size && slice.simple_equal(vector.encoded, buf[:size]), msg)
+		}
+	}
+
+	for vector in ILEB_Vectors {
+		val, size, err := varint.decode_ileb128(vector.encoded)
+
+		msg := fmt.tprintf("Expected %02x to decode to %v consuming %v bytes, got %v and %v", vector.encoded, vector.value, vector.size, val, size)
+		expect(t, size == vector.size && val == vector.value, msg)
+
+		msg  = fmt.tprintf("Expected decoder to return error %v, got %v", vector.error, err)
+		expect(t, err == vector.error, msg)
+
+		if err == .None { // Try to roundtrip
+			size, err = varint.encode_ileb128(buf[:], vector.value)
+
+			msg = fmt.tprintf("Expected %v to encode to %02x, got %02x", vector.value, vector.encoded, buf[:size])
+			expect(t, size == vector.size && slice.simple_equal(vector.encoded, buf[:size]), msg)
+		}
+	}
+
+	for num_bytes in 1..uint(16) {
+		for _ in 0..RANDOM_TESTS {
+			unsigned, signed := get_random(num_bytes)
+
+			{
+				encode_size, encode_err := varint.encode_uleb128(buf[:], unsigned)
+				msg := fmt.tprintf("%v failed to encode as an unsigned LEB128 value, got %v", unsigned, encode_err)
+				expect(t, encode_err == .None, msg)
+
+				decoded, decode_size, decode_err := varint.decode_uleb128(buf[:])
+				msg = fmt.tprintf("Expected %02x to decode as %v, got %v", buf[:encode_size], unsigned, decoded)
+				expect(t, decode_err == .None && decode_size == encode_size && decoded == unsigned, msg)
+			}
+
+			{
+				encode_size, encode_err := varint.encode_ileb128(buf[:], signed)
+				msg := fmt.tprintf("%v failed to encode as a signed LEB128 value, got %v", signed, encode_err)
+				expect(t, encode_err == .None, msg)
+
+				decoded, decode_size, decode_err := varint.decode_ileb128(buf[:])
+				msg = fmt.tprintf("Expected %02x to decode as %v, got %v, err: %v", buf[:encode_size], signed, decoded, decode_err)
+				expect(t, decode_err == .None && decode_size == encode_size && decoded == signed, msg)
+			}
+		}
+	}
+}
+
+get_random :: proc(byte_count: uint) -> (u: u128, i: i128) {
+	assert(byte_count >= 0 && byte_count <= size_of(u128))
+
+	for _ in 1..byte_count {
+		u <<= 8
+		u |= u128(rand.uint32() & 0xff)
+	}
+
+	bias := i128(1 << (byte_count * 7)) - 1
+	i     = i128(u) - bias
+
+	return
+}
+
+ULEB_Test_Vector :: struct {
+	encoded: []u8,
+	value:   u128,
+	size:    int,
+	error:   varint.Error,
+}
+
+ULEB_Vectors :: []ULEB_Test_Vector{
+	{ []u8{0x00},             0,         1, .None },
+	{ []u8{0x7f},             127,       1, .None },
+	{ []u8{0xE5, 0x8E, 0x26}, 624485,    3, .None },
+	{ []u8{0x80},             0,         0, .Buffer_Too_Small },
+	{ []u8{},                 0,         0, .Buffer_Too_Small },
+
+	{ []u8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03}, max(u128), 19, .None },
+}
+
+ILEB_Test_Vector :: struct {
+	encoded: []u8,
+	value:   i128,
+	size:    int,
+	error:   varint.Error,
+}
+
+ILEB_Vectors :: []ILEB_Test_Vector{
+	{ []u8{0x00},             0,       1, .None },
+	{ []u8{0x3f},             63,      1, .None },
+	{ []u8{0x40},             -64,     1, .None },
+	{ []u8{0xC0, 0xBB, 0x78}, -123456, 3, .None },
+	{ []u8{},                 0,       0, .Buffer_Too_Small },
+
+	{ []u8{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7e}, min(i128), 19, .None },
+	{ []u8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01}, max(i128), 19, .None },
+}

+ 5 - 3
tests/core/hash/test_core_hash.odin

@@ -5,6 +5,7 @@ import "core:hash"
 import "core:time"
 import "core:testing"
 import "core:fmt"
+import "core:os"
 
 TEST_count := 0
 TEST_fail  := 0
@@ -14,14 +15,12 @@ when ODIN_TEST {
 	log     :: testing.log
 } else {
 	expect  :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
-		fmt.printf("[%v] ", loc)
 		TEST_count += 1
 		if !condition {
 			TEST_fail += 1
-			fmt.println(" FAIL:", message)
+			fmt.printf("[%v] %v\n", loc, message)
 			return
 		}
-		fmt.println(" PASS")
 	}
 	log     :: proc(t: ^testing.T, v: any, loc := #caller_location) {
 		fmt.printf("[%v] ", loc)
@@ -35,6 +34,9 @@ main :: proc() {
 	test_xxhash_vectors(&t)
 	test_crc64_vectors(&t)
 	fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
+	if TEST_fail > 0 {
+		os.exit(1)
+	}
 }
 
 /*

+ 17 - 16
tests/core/image/test_core_image.odin

@@ -32,23 +32,21 @@ TEST_count := 0
 TEST_fail  := 0
 
 when ODIN_TEST {
-    expect  :: testing.expect
-    log     :: testing.log
+	expect  :: testing.expect
+	log     :: testing.log
 } else {
-    expect  :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
-        fmt.printf("[%v] ", loc)
-        TEST_count += 1
-        if !condition {
-            TEST_fail += 1
-            fmt.println(message)
-            return
-        }
-        fmt.println(" PASS")
-    }
-    log     :: proc(t: ^testing.T, v: any, loc := #caller_location) {
-        fmt.printf("[%v] ", loc)
-        fmt.printf("log: %v\n", v)
-    }
+	expect  :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
+		TEST_count += 1
+		if !condition {
+			TEST_fail += 1
+			fmt.printf("[%v] %v\n", loc, message)
+			return
+		}
+	}
+	log     :: proc(t: ^testing.T, v: any, loc := #caller_location) {
+		fmt.printf("[%v] ", loc)
+		fmt.printf("log: %v\n", v)
+	}
 }
 I_Error :: image.Error
 
@@ -57,6 +55,9 @@ main :: proc() {
 	png_test(&t)
 
 	fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
+	if TEST_fail > 0 {
+		os.exit(1)
+	}
 }
 
 PNG_Test :: struct {

+ 18 - 16
tests/core/math/noise/test_core_math_noise.odin

@@ -3,6 +3,7 @@ package test_core_math_noise
 import "core:testing"
 import "core:math/noise"
 import "core:fmt"
+import "core:os"
 
 TEST_count := 0
 TEST_fail  := 0
@@ -12,29 +13,30 @@ V3 :: noise.Vec3
 V4 :: noise.Vec4
 
 when ODIN_TEST {
-    expect  :: testing.expect
-    log     :: testing.log
+	expect  :: testing.expect
+	log     :: testing.log
 } else {
-    expect  :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
-        fmt.printf("[%v] ", loc)
-        TEST_count += 1
-        if !condition {
-            TEST_fail += 1
-            fmt.println(message)
-            return
-        }
-        fmt.println(" PASS")
-    }
-    log     :: proc(t: ^testing.T, v: any, loc := #caller_location) {
-        fmt.printf("[%v] ", loc)
-        fmt.printf("log: %v\n", v)
-    }
+	expect  :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
+		TEST_count += 1
+		if !condition {
+			TEST_fail += 1
+			fmt.printf("[%v] %v\n", loc, message)
+			return
+		}
+	}
+	log     :: proc(t: ^testing.T, v: any, loc := #caller_location) {
+		fmt.printf("[%v] ", loc)
+		fmt.printf("log: %v\n", v)
+	}
 }
 
 main :: proc() {
 	t := testing.T{}
 	noise_test(&t)
 	fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
+	if TEST_fail > 0 {
+		os.exit(1)
+	}
 }
 
 Test_Vector :: struct {

+ 22 - 22
tests/core/odin/test_parser.odin

@@ -2,7 +2,7 @@ package test_core_odin_parser
 
 import "core:testing"
 import "core:fmt"
-
+import "core:os"
 import "core:odin/parser"
 
 
@@ -10,31 +10,31 @@ TEST_count := 0
 TEST_fail  := 0
 
 when ODIN_TEST {
-    expect  :: testing.expect
-    log     :: testing.log
+	expect  :: testing.expect
+	log     :: testing.log
 } else {
-    expect  :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
-        fmt.printf("[%v] ", loc)
-        TEST_count += 1
-        if !condition {
-            TEST_fail += 1
-            fmt.println(message)
-            return
-        }
-        fmt.println(" PASS")
-    }
-    log     :: proc(t: ^testing.T, v: any, loc := #caller_location) {
-        fmt.printf("[%v] ", loc)
-        fmt.printf("log: %v\n", v)
-    }
+	expect  :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
+		TEST_count += 1
+		if !condition {
+			TEST_fail += 1
+			fmt.printf("[%v] %v\n", loc, message)
+			return
+		}
+	}
+	log     :: proc(t: ^testing.T, v: any, loc := #caller_location) {
+		fmt.printf("[%v] ", loc)
+		fmt.printf("log: %v\n", v)
+	}
 }
 
-
 main :: proc() {
-    t := testing.T{}
-    test_parse_demo(&t)
+	t := testing.T{}
+	test_parse_demo(&t)
 
-    fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
+	fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
+	if TEST_fail > 0 {
+		os.exit(1)
+	}
 }
 
 
@@ -47,4 +47,4 @@ test_parse_demo :: proc(t: ^testing.T) {
 	for key, value in pkg.files {
 		expect(t, value.syntax_error_count == 0, fmt.tprintf("%v should contain zero errors", key))
 	}
-}
+}

+ 33 - 31
tests/core/strings/test_core_strings.odin

@@ -3,61 +3,63 @@ package test_core_image
 import "core:strings"
 import "core:testing"
 import "core:fmt"
+import "core:os"
 
 TEST_count := 0
 TEST_fail  := 0
 
 when ODIN_TEST {
-    expect  :: testing.expect
-    log     :: testing.log
+	expect  :: testing.expect
+	log     :: testing.log
 } else {
-    expect  :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
-        fmt.printf("[%v] ", loc)
-        TEST_count += 1
-        if !condition {
-            TEST_fail += 1
-            fmt.println(message)
-            return
-        }
-        fmt.println(" PASS")
-    }
-    log     :: proc(t: ^testing.T, v: any, loc := #caller_location) {
-        fmt.printf("[%v] ", loc)
-        fmt.printf("log: %v\n", v)
-    }
+	expect  :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
+		TEST_count += 1
+		if !condition {
+			TEST_fail += 1
+			fmt.printf("[%v] %v\n", loc, message)
+			return
+		}
+	}
+	log     :: proc(t: ^testing.T, v: any, loc := #caller_location) {
+		fmt.printf("[%v] ", loc)
+		fmt.printf("log: %v\n", v)
+	}
 }
 
 main :: proc() {
-    t := testing.T{}
-    test_index_any_small_string_not_found(&t)
-    test_index_any_larger_string_not_found(&t)
-    test_index_any_small_string_found(&t)
-    test_index_any_larger_string_found(&t)
+	t := testing.T{}
+	test_index_any_small_string_not_found(&t)
+	test_index_any_larger_string_not_found(&t)
+	test_index_any_small_string_found(&t)
+	test_index_any_larger_string_found(&t)
 
-    fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
+	fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
+	if TEST_fail > 0 {
+		os.exit(1)
+	}
 }
 
 @test
 test_index_any_small_string_not_found :: proc(t: ^testing.T) {
-    index := strings.index_any(".", "/:\"")
-    log(t, index)
-    expect(t, index == -1, "index_any should be negative")
+	index := strings.index_any(".", "/:\"")
+	log(t, index)
+	expect(t, index == -1, "index_any should be negative")
 }
 
 @test
 test_index_any_larger_string_not_found :: proc(t: ^testing.T) {
-    index := strings.index_any("aaaaaaaa.aaaaaaaa", "/:\"")
-    expect(t, index == -1, "index_any should be negative")
+	index := strings.index_any("aaaaaaaa.aaaaaaaa", "/:\"")
+	expect(t, index == -1, "index_any should be negative")
 }
 
 @test
 test_index_any_small_string_found :: proc(t: ^testing.T) {
-    index := strings.index_any(".", "/:.\"")
-    expect(t, index == 0, "index_any should be 0")
+	index := strings.index_any(".", "/:.\"")
+	expect(t, index == 0, "index_any should be 0")
 }
 
 @test
 test_index_any_larger_string_found :: proc(t: ^testing.T) {
-    index := strings.index_any("aaaaaaaa:aaaaaaaa", "/:\"")
-    expect(t, index == 8, "index_any should be 8")
+	index := strings.index_any("aaaaaaaa:aaaaaaaa", "/:\"")
+	expect(t, index == 8, "index_any should be 8")
 }

+ 8 - 1
tests/vendor/Makefile

@@ -1,6 +1,13 @@
 ODIN=../../odin
+ODINFLAGS=
+
+OS=$(shell uname)
+
+ifeq ($(OS), OpenBSD)
+    ODINFLAGS:=$(ODINFLAGS) -extra-linker-flags:-L/usr/local/lib
+endif
 
 all: botan_test
 
 botan_test:
-	$(ODIN) run botan -out=botan_hash -o:speed -no-bounds-check
+	$(ODIN) run botan -out=botan_hash -o:speed -no-bounds-check $(ODINFLAGS)

+ 4 - 0
tests/vendor/botan/test_vendor_botan.odin

@@ -14,6 +14,7 @@ package test_vendor_botan
 
 import "core:testing"
 import "core:fmt"
+import "core:os"
 
 import "vendor:botan/md4"
 import "vendor:botan/md5"
@@ -86,6 +87,9 @@ main :: proc() {
     test_siphash_2_4(&t)
 
     fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
+    if TEST_fail > 0 {
+        os.exit(1)
+    }
 }
 
 TestHash :: struct {

+ 4 - 0
tests/vendor/glfw/test_vendor_glfw.odin

@@ -3,6 +3,7 @@ package test_vendor_glfw
 import "core:testing"
 import "core:fmt"
 import "vendor:glfw"
+import "core:os"
 
 GLFW_MAJOR :: 3
 GLFW_MINOR :: 3
@@ -36,6 +37,9 @@ main :: proc() {
 	test_glfw(&t)
 
 	fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
+	if TEST_fail > 0 {
+		os.exit(1)
+	}
 }
 
 @(test)

+ 3 - 3
vendor/ENet/unix.odin

@@ -1,4 +1,4 @@
-//+build linux, darwin, freebsd
+//+build linux, darwin, freebsd, openbsd
 package ENet
 
 // When we implement the appropriate bindings for Unix, the section separated
@@ -14,7 +14,7 @@ import "core:c"
 
 @(private="file") FD_ZERO :: #force_inline proc(s: ^fd_set) {
 	for i := size_of(fd_set) / size_of(c.long); i != 0; i -= 1 {
-		s.fds_bits[i] = 0;
+		s.fds_bits[i] = 0
 	}
 }
 
@@ -56,4 +56,4 @@ SOCKETSET_REMOVE :: #force_inline proc(sockset: ^SocketSet, socket: Socket) {
 
 SOCKSET_CHECK :: #force_inline proc(sockset: ^SocketSet, socket: Socket) -> bool {
 	return FD_ISSET(i32(socket), cast(^fd_set)sockset)
-}
+}

+ 2 - 4
vendor/botan/bindings/botan.odin

@@ -142,9 +142,7 @@ fpe_t                :: ^fpe_struct
 
 when ODIN_OS == .Windows {
     foreign import botan_lib "botan.lib"
-} else when ODIN_OS == .Linux {
-    foreign import botan_lib "system:botan-2"
-} else when ODIN_OS == .Darwin {
+} else {
     foreign import botan_lib "system:botan-2"
 }
 
@@ -471,4 +469,4 @@ foreign botan_lib {
     fpe_destroy                         :: proc(fpe: fpe_t) -> c.int ---
     fpe_encrypt                         :: proc(fpe: fpe_t, x: mp_t, tweak: ^c.char, tweak_len: c.size_t) -> c.int ---
     fpe_decrypt                         :: proc(fpe: fpe_t, x: mp_t, tweak: ^c.char, tweak_len: c.size_t) -> c.int ---
-}
+}

+ 5 - 2
vendor/glfw/bindings/bindings.odin

@@ -3,8 +3,6 @@ package glfw_bindings
 import "core:c"
 import vk "vendor:vulkan"
 
-when ODIN_OS == .Linux   { foreign import glfw "system:glfw" } // TODO: Add the billion-or-so static libs to link to in linux
-when ODIN_OS == .Darwin  { foreign import glfw "system:glfw" }
 when ODIN_OS == .Windows {
 	foreign import glfw { 
 		"../lib/glfw3_mt.lib",
@@ -12,6 +10,11 @@ when ODIN_OS == .Windows {
 		"system:gdi32.lib", 
 		"system:shell32.lib",
 	} 
+} else when ODIN_OS == .Linux {
+	// TODO: Add the billion-or-so static libs to link to in linux
+	foreign import glfw "system:glfw"
+} else {
+	foreign import glfw "system:glfw"
 }
 
 #assert(size_of(c.int) == size_of(b32))

+ 7 - 2
vendor/miniaudio/common.odin

@@ -2,8 +2,13 @@ package miniaudio
 
 import "core:c"
 
-when ODIN_OS == .Windows { foreign import lib "lib/miniaudio.lib" }
-when ODIN_OS == .Linux   { foreign import lib "lib/miniaudio.a" }
+when ODIN_OS == .Windows {
+	foreign import lib "lib/miniaudio.lib"
+} else when ODIN_OS == .Linux {
+	foreign import lib "lib/miniaudio.a"
+} else {
+	foreign import lib "system:miniaudio"
+}
 
 handle :: distinct rawptr
 

+ 7 - 3
vendor/miniaudio/data_conversion.odin

@@ -2,9 +2,13 @@ package miniaudio
 
 import "core:c"
 
-when ODIN_OS == .Windows { foreign import lib "lib/miniaudio.lib" }
-when ODIN_OS == .Linux   { foreign import lib "lib/miniaudio.a" }
-
+when ODIN_OS == .Windows {
+	foreign import lib "lib/miniaudio.lib"
+} else when ODIN_OS == .Linux {
+	foreign import lib "lib/miniaudio.a"
+} else {
+	foreign import lib "system:miniaudio"
+}
 
 /************************************************************************************************************************************************************
 *************************************************************************************************************************************************************

+ 8 - 5
vendor/miniaudio/decoding.odin

@@ -2,10 +2,13 @@ package miniaudio
 
 import "core:c"
 
-when ODIN_OS == .Windows { foreign import lib "lib/miniaudio.lib" }
-when ODIN_OS == .Linux   { foreign import lib "lib/miniaudio.a" }
-
-
+when ODIN_OS == .Windows {
+	foreign import lib "lib/miniaudio.lib"
+} else when ODIN_OS == .Linux {
+	foreign import lib "lib/miniaudio.a"
+} else {
+	foreign import lib "system:miniaudio"
+}
 
 /************************************************************************************************************************************************************
 
@@ -164,4 +167,4 @@ foreign lib {
 	decode_from_vfs :: proc(pVFS: ^vfs, pFilePath: cstring,    pConfig: ^decoder_config, pFrameCountOut: ^u64, ppPCMFramesOut: ^rawptr) -> result ---
 	decode_file     :: proc(pFilePath: cstring,                pConfig: ^decoder_config, pFrameCountOut: ^u64, ppPCMFramesOut: ^rawptr) -> result ---
 	decode_memory   :: proc(pData: rawptr, dataSize: c.size_t, pConfig: ^decoder_config, pFrameCountOut: ^u64, ppPCMFramesOut: ^rawptr) -> result ---
-}
+}

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