浏览代码

Merge branch 'master' into macharena

Colin Davidson 11 月之前
父节点
当前提交
f3ab14b8cc
共有 100 个文件被更改,包括 869 次插入591 次删除
  1. 21 21
      .github/workflows/ci.yml
  2. 86 59
      .github/workflows/nightly.yml
  3. 3 1
      base/intrinsics/intrinsics.odin
  4. 30 1
      base/runtime/core.odin
  5. 12 12
      base/runtime/core_builtin.odin
  6. 8 8
      base/runtime/core_builtin_soa.odin
  7. 2 2
      base/runtime/default_allocators_nil.odin
  8. 3 3
      base/runtime/entry_unix.odin
  9. 3 3
      base/runtime/entry_wasm.odin
  10. 3 3
      base/runtime/entry_windows.odin
  11. 12 10
      base/runtime/heap_allocator.odin
  12. 2 2
      base/runtime/heap_allocator_orca.odin
  13. 2 2
      base/runtime/heap_allocator_other.odin
  14. 2 2
      base/runtime/heap_allocator_unix.odin
  15. 8 5
      base/runtime/internal.odin
  16. 2 2
      base/runtime/os_specific_bsd.odin
  17. 2 2
      base/runtime/os_specific_darwin.odin
  18. 2 2
      base/runtime/os_specific_freestanding.odin
  19. 2 2
      base/runtime/os_specific_haiku.odin
  20. 2 2
      base/runtime/os_specific_js.odin
  21. 1 1
      base/runtime/os_specific_linux.odin
  22. 2 2
      base/runtime/os_specific_orca.odin
  23. 2 2
      base/runtime/os_specific_wasi.odin
  24. 2 2
      base/runtime/os_specific_windows.odin
  25. 1 1
      base/runtime/procs_darwin.odin
  26. 1 1
      base/runtime/procs_js.odin
  27. 42 20
      base/runtime/procs_wasm.odin
  28. 2 2
      base/runtime/procs_windows_amd64.odin
  29. 2 2
      base/runtime/procs_windows_i386.odin
  30. 34 0
      base/runtime/thread_management.odin
  31. 1 1
      base/runtime/wasm_allocator.odin
  32. 19 0
      ci/build_linux_static.sh
  33. 24 19
      ci/nightly.py
  34. 33 3
      core/bytes/buffer.odin
  35. 5 7
      core/bytes/bytes.odin
  36. 9 2
      core/bytes/reader.odin
  37. 8 0
      core/c/libc/errno.odin
  38. 25 1
      core/c/libc/stdio.odin
  39. 10 0
      core/c/libc/stdlib.odin
  40. 1 0
      core/c/libc/string.odin
  41. 2 2
      core/c/libc/time.odin
  42. 1 1
      core/c/libc/wctype.odin
  43. 90 0
      core/compress/gzip/doc.odin
  44. 0 89
      core/compress/gzip/example.odin
  45. 1 2
      core/compress/shoco/model.odin
  46. 2 2
      core/compress/shoco/shoco.odin
  47. 50 0
      core/compress/zlib/doc.odin
  48. 0 47
      core/compress/zlib/example.odin
  49. 1 1
      core/compress/zlib/zlib.odin
  50. 60 14
      core/container/bit_array/bit_array.odin
  51. 4 4
      core/container/bit_array/doc.odin
  52. 9 6
      core/container/intrusive/list/doc.odin
  53. 7 3
      core/container/small_array/small_array.odin
  54. 1 1
      core/crypto/_aes/hw_intel/api.odin
  55. 1 1
      core/crypto/_aes/hw_intel/ghash.odin
  56. 1 1
      core/crypto/_aes/hw_intel/hw_intel_keysched.odin
  57. 1 1
      core/crypto/_chacha20/simd256/chacha20_simd256.odin
  58. 1 1
      core/crypto/_chacha20/simd256/chacha20_simd256_stub.odin
  59. 43 44
      core/crypto/aead/doc.odin
  60. 3 3
      core/crypto/aes/aes.odin
  61. 1 1
      core/crypto/aes/aes_ctr_hw_intel.odin
  62. 1 1
      core/crypto/aes/aes_ecb_hw_intel.odin
  63. 1 1
      core/crypto/aes/aes_gcm_hw_intel.odin
  64. 1 1
      core/crypto/aes/aes_impl_hw_gen.odin
  65. 1 1
      core/crypto/aes/aes_impl_hw_intel.odin
  66. 2 2
      core/crypto/blake2b/blake2b.odin
  67. 2 2
      core/crypto/blake2s/blake2s.odin
  68. 2 2
      core/crypto/chacha20/chacha20.odin
  69. 2 2
      core/crypto/chacha20poly1305/chacha20poly1305.odin
  70. 3 3
      core/crypto/ed25519/ed25519.odin
  71. 28 30
      core/crypto/hash/doc.odin
  72. 4 38
      core/crypto/hash/hash.odin
  73. 10 0
      core/crypto/hash/hash_freestanding.odin
  74. 38 0
      core/crypto/hash/hash_os.odin
  75. 1 1
      core/crypto/hkdf/hkdf.odin
  76. 1 1
      core/crypto/hmac/hmac.odin
  77. 1 1
      core/crypto/kmac/kmac.odin
  78. 2 2
      core/crypto/legacy/md5/md5.odin
  79. 3 3
      core/crypto/legacy/sha1/sha1.odin
  80. 1 1
      core/crypto/pbkdf2/pbkdf2.odin
  81. 1 1
      core/crypto/poly1305/poly1305.odin
  82. 1 1
      core/crypto/rand_bsd.odin
  83. 7 7
      core/crypto/rand_generic.odin
  84. 1 1
      core/crypto/ristretto255/ristretto255.odin
  85. 2 2
      core/crypto/sha2/sha2.odin
  86. 1 1
      core/crypto/sha3/sha3.odin
  87. 2 2
      core/crypto/shake/shake.odin
  88. 9 4
      core/crypto/siphash/siphash.odin
  89. 1 1
      core/crypto/sm3/sm3.odin
  90. 1 1
      core/crypto/tuplehash/tuplehash.odin
  91. 1 1
      core/crypto/x25519/x25519.odin
  92. 2 2
      core/debug/trace/trace_cpp.odin
  93. 3 1
      core/debug/trace/trace_nil.odin
  94. 2 2
      core/debug/trace/trace_windows.odin
  95. 1 2
      core/dynlib/doc.odin
  96. 2 2
      core/dynlib/lib_js.odin
  97. 2 2
      core/dynlib/lib_unix.odin
  98. 2 2
      core/dynlib/lib_windows.odin
  99. 3 3
      core/encoding/ansi/doc.odin
  100. 11 27
      core/encoding/cbor/unmarshal.odin

+ 21 - 21
.github/workflows/ci.yml

@@ -32,9 +32,9 @@ jobs:
           gmake -C vendor/miniaudio/src
           ./odin check examples/all -vet -strict-style -disallow-do -target:netbsd_amd64
           ./odin check examples/all -vet -strict-style -disallow-do -target:netbsd_arm64
-          ./odin test tests/core/normal.odin -file -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
-          ./odin test tests/core/speed.odin -file -all-packages -o:speed -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
-          ./odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
+          ./odin test tests/core/normal.odin -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
+          ./odin test tests/core/speed.odin -file -all-packages -vet -strict-style -disallow-do -o:speed -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
+          ./odin test tests/vendor -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
           (cd tests/issues; ./run.sh)
   build_freebsd:
     name: FreeBSD Build, Check, and Test
@@ -60,9 +60,9 @@ jobs:
           gmake -C vendor/cgltf/src
           gmake -C vendor/miniaudio/src
           ./odin check examples/all -vet -strict-style -disallow-do -target:freebsd_amd64
-          ./odin test tests/core/normal.odin -file -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
-          ./odin test tests/core/speed.odin -file -all-packages -o:speed -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
-          ./odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
+          ./odin test tests/core/normal.odin -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
+          ./odin test tests/core/speed.odin -file -all-packages -vet -strict-style -disallow-do -o:speed -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
+          ./odin test tests/vendor -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
           (cd tests/issues; ./run.sh)
   ci:
     strategy:
@@ -116,13 +116,13 @@ jobs:
       - name: Odin check examples/all
         run: ./odin check examples/all -strict-style
       - name: Normal Core library tests
-        run: ./odin test tests/core/normal.odin -file -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
+        run: ./odin test tests/core/normal.odin -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
       - name: Optimized Core library tests
-        run: ./odin test tests/core/speed.odin -o:speed -file -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
+        run: ./odin test tests/core/speed.odin -o:speed -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
       - name: Vendor library tests
-        run: ./odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
+        run: ./odin test tests/vendor -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
       - name: Internals tests
-        run: ./odin test tests/internal -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
+        run: ./odin test tests/internal -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
       - name: GitHub Issue tests
         run: |
           cd tests/issues
@@ -176,33 +176,33 @@ jobs:
         shell: cmd
         run: |
           call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
-          odin run examples/demo -debug
+          odin run examples/demo -debug -vet -strict-style -disallow-do
       - name: Odin check examples/all
         shell: cmd
         run: |
           call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
-          odin check examples/all -strict-style
+          odin check examples/all -vet -strict-style -disallow-do
       - name: Core library tests
         shell: cmd
         run: |
           call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
-          odin test tests/core/normal.odin -file -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
+          odin test tests/core/normal.odin -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
       - name: Optimized core library tests
         shell: cmd
         run: |
           call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
-          odin test tests/core/speed.odin -o:speed -file -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
+          odin test tests/core/speed.odin -o:speed -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
       - name: Vendor library tests
         shell: cmd
         run: |
           call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
           copy vendor\lua\5.4\windows\*.dll .
-          odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
+          odin test tests/vendor -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
       - name: Odin internals tests
         shell: cmd
         run: |
           call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
-          odin test tests/internal -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
+          odin test tests/internal -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
       - name: Odin documentation tests
         shell: cmd
         run: |
@@ -257,16 +257,16 @@ jobs:
         run: sudo apt-get install -y qemu-user qemu-user-static gcc-12-riscv64-linux-gnu libc6-riscv64-cross
 
       - name: Odin run
-        run: ./odin run examples/demo -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static"
+        run: ./odin run examples/demo -vet -strict-style -disallow-do -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static" -no-rpath
 
       - name: Odin run -debug
-        run: ./odin run examples/demo -debug -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static"
+        run: ./odin run examples/demo -debug -vet -strict-style -disallow-do -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static" -no-rpath
 
       - name: Normal Core library tests
-        run: ./odin test tests/core/normal.odin -file -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static"
+        run: ./odin test tests/core/normal.odin -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static" -no-rpath
 
       - name: Optimized Core library tests
-        run: ./odin test tests/core/speed.odin -o:speed -file -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static"
+        run: ./odin test tests/core/speed.odin -o:speed -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static" -no-rpath
 
       - name: Internals tests
-        run: ./odin test tests/internal -all-packages -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static"
+        run: ./odin test tests/internal -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static" -no-rpath

+ 86 - 59
.github/workflows/nightly.yml

@@ -36,43 +36,54 @@ jobs:
           cp -r bin dist
           cp -r examples dist
       - name: Upload artifact
-        uses: actions/upload-artifact@v1
+        uses: actions/upload-artifact@v4
         with:
           name: windows_artifacts
           path: dist
-  build_ubuntu:
-    name: Ubuntu Build
+  build_linux:
+    name: Linux Build
     if: github.repository == 'odin-lang/Odin'
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v4
+      - uses: jirutka/setup-alpine@v1
+        with:
+          branch: v3.20
       - name: (Linux) Download LLVM
         run: |
-          wget https://apt.llvm.org/llvm.sh
-          chmod +x llvm.sh
-          sudo ./llvm.sh 18
-          echo "/usr/lib/llvm-18/bin" >> $GITHUB_PATH
+          apk add --no-cache \
+          musl-dev llvm18-dev clang18 git mold lz4 \
+          libxml2-static llvm18-static zlib-static zstd-static \
+          make
+        shell: alpine.sh --root {0}
       - name: build odin
-        run: make nightly
+        # NOTE: this build does slow compile times because of musl
+        run: ci/build_linux_static.sh
+        shell: alpine.sh {0}
       - name: Odin run
         run: ./odin run examples/demo
       - name: Copy artifacts
         run: |
-          mkdir dist
-          cp odin dist
-          cp LICENSE dist
-          cp -r shared dist
-          cp -r base dist
-          cp -r core dist
-          cp -r vendor dist
-          cp -r examples dist
-          # Zipping so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38
-          zip -r dist.zip dist
+          FILE="odin-linux-amd64-nightly+$(date -I)"
+          mkdir $FILE
+          cp odin $FILE
+          cp LICENSE $FILE
+          cp -r shared $FILE
+          cp -r base $FILE
+          cp -r core $FILE
+          cp -r vendor $FILE
+          cp -r examples $FILE
+          # Creating a tarball so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38
+          tar -czvf dist.tar.gz $FILE
+      - name: Odin run
+        run: |
+          FILE="odin-linux-amd64-nightly+$(date -I)"
+          $FILE/odin run examples/demo
       - name: Upload artifact
-        uses: actions/upload-artifact@v1
+        uses: actions/upload-artifact@v4
         with:
-          name: ubuntu_artifacts
-          path: dist.zip
+          name: linux_artifacts
+          path: dist.tar.gz
   build_macos:
     name: MacOS Build
     if: github.repository == 'odin-lang/Odin'
@@ -89,24 +100,27 @@ jobs:
         run: CXXFLAGS="-L/usr/lib/system -L/usr/lib" make nightly
       - name: Bundle
         run: |
-          mkdir dist
-          cp odin dist
-          cp LICENSE dist
-          cp -r shared dist
-          cp -r base dist
-          cp -r core dist
-          cp -r vendor dist
-          cp -r examples dist
-          dylibbundler -b -x dist/odin -d dist/libs -od -p @executable_path/libs
-          # Zipping so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38
-          zip -r dist.zip dist
+          FILE="odin-macos-amd64-nightly+$(date -I)"
+          mkdir $FILE
+          cp odin $FILE
+          cp LICENSE $FILE
+          cp -r shared $FILE
+          cp -r base $FILE
+          cp -r core $FILE
+          cp -r vendor $FILE
+          cp -r examples $FILE
+          dylibbundler -b -x $FILE/odin -d $FILE/libs -od -p @executable_path/libs
+          # Creating a tarball so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38
+          tar -czvf dist.tar.gz $FILE
       - name: Odin run
-        run: ./dist/odin run examples/demo
+        run: |
+          FILE="odin-macos-amd64-nightly+$(date -I)"
+          $FILE/odin run examples/demo
       - name: Upload artifact
-        uses: actions/upload-artifact@v1
+        uses: actions/upload-artifact@v4
         with:
           name: macos_artifacts
-          path: dist.zip
+          path: dist.tar.gz
   build_macos_arm:
     name: MacOS ARM Build
     if: github.repository == 'odin-lang/Odin'
@@ -123,30 +137,33 @@ jobs:
         run: CXXFLAGS="-L/usr/lib/system -L/usr/lib" make nightly
       - name: Bundle
         run: |
-          mkdir dist
-          cp odin dist
-          cp LICENSE dist
-          cp -r shared dist
-          cp -r base dist
-          cp -r core dist
-          cp -r vendor dist
-          cp -r examples dist
-          dylibbundler -b -x dist/odin -d dist/libs -od -p @executable_path/libs
-          # Zipping so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38
-          zip -r dist.zip dist
+          FILE="odin-macos-arm64-nightly+$(date -I)"
+          mkdir $FILE
+          cp odin $FILE
+          cp LICENSE $FILE
+          cp -r shared $FILE
+          cp -r base $FILE
+          cp -r core $FILE
+          cp -r vendor $FILE
+          cp -r examples $FILE
+          dylibbundler -b -x $FILE/odin -d $FILE/libs -od -p @executable_path/libs
+          # Creating a tarball so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38
+          tar -czvf dist.tar.gz $FILE
       - name: Odin run
-        run: ./dist/odin run examples/demo
+        run: |
+          FILE="odin-macos-arm64-nightly+$(date -I)"
+          $FILE/odin run examples/demo
       - name: Upload artifact
-        uses: actions/upload-artifact@v1
+        uses: actions/upload-artifact@v4
         with:
           name: macos_arm_artifacts
-          path: dist.zip
+          path: dist.tar.gz
   upload_b2:
     runs-on: [ubuntu-latest]
-    needs: [build_windows, build_macos, build_macos_arm, build_ubuntu]
+    needs: [build_windows, build_macos, build_macos_arm, build_linux]
     steps:
       - uses: actions/checkout@v4
-      - uses: actions/setup-python@v2
+      - uses: actions/setup-python@v5
         with:
           python-version: '3.8.x'
 
@@ -160,24 +177,33 @@ jobs:
         run: python -c "import sys; print(sys.version)"
 
       - name: Download Windows artifacts
-        uses: actions/download-artifact@v1
+
+        uses: actions/[email protected]
         with:
           name: windows_artifacts
+          path: windows_artifacts
 
       - name: Download Ubuntu artifacts
-        uses: actions/download-artifact@v1
+        uses: actions/download-artifact@v4.1.7
         with:
-          name: ubuntu_artifacts
+          name: linux_artifacts
+          path: linux_artifacts
 
       - name: Download macOS artifacts
-        uses: actions/download-artifact@v1
+        uses: actions/download-artifact@v4.1.7
         with:
           name: macos_artifacts
+          path: macos_artifacts
 
       - name: Download macOS arm artifacts
-        uses: actions/download-artifact@v1
+        uses: actions/download-artifact@v4.1.7
         with:
           name: macos_arm_artifacts
+          path: macos_arm_artifacts
+
+      - name: Debug
+        run: |
+          tree -L 2
 
       - name: Create archives and upload
         shell: bash
@@ -187,9 +213,10 @@ jobs:
           BUCKET: ${{ secrets.B2_BUCKET }}
           DAYS_TO_KEEP: ${{ secrets.B2_DAYS_TO_KEEP }}
         run: |
+          file linux_artifacts/dist.tar.gz
           python3 ci/nightly.py artifact windows-amd64 windows_artifacts/
-          python3 ci/nightly.py artifact ubuntu-amd64 ubuntu_artifacts/dist.zip
-          python3 ci/nightly.py artifact macos-amd64 macos_artifacts/dist.zip
-          python3 ci/nightly.py artifact macos-arm64 macos_arm_artifacts/dist.zip
+          python3 ci/nightly.py artifact linux-amd64 linux_artifacts/dist.tar.gz
+          python3 ci/nightly.py artifact macos-amd64 macos_artifacts/dist.tar.gz
+          python3 ci/nightly.py artifact macos-arm64 macos_arm_artifacts/dist.tar.gz
           python3 ci/nightly.py prune
           python3 ci/nightly.py json

+ 3 - 1
base/intrinsics/intrinsics.odin

@@ -1,5 +1,5 @@
 // This is purely for documentation
-//+build ignore
+#+build ignore
 package intrinsics
 
 // Package-Related
@@ -219,6 +219,8 @@ type_map_cell_info :: proc($T: typeid)           -> ^runtime.Map_Cell_Info ---
 type_convert_variants_to_pointers :: proc($T: typeid) -> typeid where type_is_union(T) ---
 type_merge :: proc($U, $V: typeid) -> typeid where type_is_union(U), type_is_union(V) ---
 
+type_has_shared_fields :: proc($U, $V: typeid) -> bool typeid where type_is_struct(U), type_is_struct(V) ---
+
 constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
 
 constant_log2 :: proc($v: $T) -> T where type_is_integer(T) ---

+ 30 - 1
base/runtime/core.odin

@@ -18,7 +18,7 @@
 // This could change at a later date if the all these data structures are
 // implemented within the compiler rather than in this "preload" file
 //
-//+no-instrumentation
+#+no-instrumentation
 package runtime
 
 import "base:intrinsics"
@@ -546,10 +546,23 @@ Odin_OS_Type :: type_of(ODIN_OS)
 		arm64,
 		wasm32,
 		wasm64p32,
+		riscv64,
 	}
 */
 Odin_Arch_Type :: type_of(ODIN_ARCH)
 
+Odin_Arch_Types :: bit_set[Odin_Arch_Type]
+
+ALL_ODIN_ARCH_TYPES :: Odin_Arch_Types{
+	.amd64,
+	.i386,
+	.arm32,
+	.arm64,
+	.wasm32,
+	.wasm64p32,
+	.riscv64,
+}
+
 /*
 	// Defined internally by the compiler
 	Odin_Build_Mode_Type :: enum int {
@@ -573,6 +586,22 @@ Odin_Build_Mode_Type :: type_of(ODIN_BUILD_MODE)
 */
 Odin_Endian_Type :: type_of(ODIN_ENDIAN)
 
+Odin_OS_Types :: bit_set[Odin_OS_Type]
+
+ALL_ODIN_OS_TYPES :: Odin_OS_Types{
+	.Windows,
+	.Darwin,
+	.Linux,
+	.Essence,
+	.FreeBSD,
+	.OpenBSD,
+	.NetBSD,
+	.Haiku,
+	.WASI,
+	.JS,
+	.Orca,
+	.Freestanding,
+}
 
 /*
 	// Defined internally by the compiler

+ 12 - 12
base/runtime/core_builtin.odin

@@ -68,7 +68,7 @@ copy :: proc{copy_slice, copy_from_string}
 // Note: If you want the elements to remain in their order, use `ordered_remove`.
 // Note: If the index is out of bounds, this procedure will panic.
 @builtin
-unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) #no_bounds_check {
+unordered_remove :: proc(array: ^$D/[dynamic]$T, #any_int index: int, loc := #caller_location) #no_bounds_check {
 	bounds_check_error_loc(loc, index, len(array))
 	n := len(array)-1
 	if index != n {
@@ -82,7 +82,7 @@ unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_loca
 // Note: If the elements do not have to remain in their order, prefer `unordered_remove`.
 // Note: If the index is out of bounds, this procedure will panic.
 @builtin
-ordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) #no_bounds_check {
+ordered_remove :: proc(array: ^$D/[dynamic]$T, #any_int index: int, loc := #caller_location) #no_bounds_check {
 	bounds_check_error_loc(loc, index, len(array))
 	if index+1 < len(array) {
 		copy(array[index:], array[index+1:])
@@ -95,7 +95,7 @@ ordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_locati
 // Note: This is an O(N) operation.
 // Note: If the range is out of bounds, this procedure will panic.
 @builtin
-remove_range :: proc(array: ^$D/[dynamic]$T, lo, hi: int, loc := #caller_location) #no_bounds_check {
+remove_range :: proc(array: ^$D/[dynamic]$T, #any_int lo, hi: int, loc := #caller_location) #no_bounds_check {
 	slice_expr_error_lo_hi_loc(loc, lo, hi, len(array))
 	n := max(hi-lo, 0)
 	if n > 0 {
@@ -602,7 +602,7 @@ append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (n: i
 
 
 @builtin
-inject_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
+inject_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
 	if array == nil {
 		return
 	}
@@ -620,7 +620,7 @@ inject_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast arg: E,
 }
 
 @builtin
-inject_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
+inject_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
 	if array == nil {
 		return
 	}
@@ -643,7 +643,7 @@ inject_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast args:
 }
 
 @builtin
-inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
+inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, #any_int index: int, arg: string, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
 	if array == nil {
 		return
 	}
@@ -668,7 +668,7 @@ inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string
 
 
 @builtin
-assign_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
+assign_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
 	if index < len(array) {
 		array[index] = arg
 		ok = true
@@ -682,7 +682,7 @@ assign_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #calle
 
 
 @builtin
-assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
+assign_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
 	new_size := index + len(args)
 	if len(args) == 0 {
 		ok = true
@@ -699,7 +699,7 @@ assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast args:
 
 
 @builtin
-assign_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
+assign_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, #any_int index: int, arg: string, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
 	new_size := index + len(arg)
 	if len(arg) == 0 {
 		ok = true
@@ -838,7 +838,7 @@ non_zero_resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: i
 
 	Note: Prefer the procedure group `shrink`
 */
-shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) {
+shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int new_cap := -1, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) {
 	return _shrink_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), new_cap, loc)
 }
 
@@ -913,7 +913,7 @@ card :: proc "contextless" (s: $S/bit_set[$E; $U]) -> int {
 
 @builtin
 @(disabled=ODIN_DISABLE_ASSERT)
-assert :: proc(condition: bool, message := "", loc := #caller_location) {
+assert :: proc(condition: bool, message := #caller_expression(condition), loc := #caller_location) {
 	if !condition {
 		// NOTE(bill): This is wrapped in a procedure call
 		// to improve performance to make the CPU not
@@ -952,7 +952,7 @@ unimplemented :: proc(message := "", loc := #caller_location) -> ! {
 
 @builtin
 @(disabled=ODIN_DISABLE_ASSERT)
-assert_contextless :: proc "contextless" (condition: bool, message := "", loc := #caller_location) {
+assert_contextless :: proc "contextless" (condition: bool, message := #caller_expression(condition), loc := #caller_location) {
 	if !condition {
 		// NOTE(bill): This is wrapped in a procedure call
 		// to improve performance to make the CPU not

+ 8 - 8
base/runtime/core_builtin_soa.odin

@@ -76,7 +76,7 @@ raw_soa_footer :: proc{
 
 
 @(builtin, require_results)
-make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
+make_soa_aligned :: proc($T: typeid/#soa[]$E, #any_int length, alignment: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
 	if length <= 0 {
 		return
 	}
@@ -135,7 +135,7 @@ make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, alloc
 }
 
 @(builtin, require_results)
-make_soa_slice :: proc($T: typeid/#soa[]$E, length: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
+make_soa_slice :: proc($T: typeid/#soa[]$E, #any_int length: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
 	return make_soa_aligned(T, length, align_of(E), allocator, loc)
 }
 
@@ -172,7 +172,7 @@ make_soa :: proc{
 
 
 @builtin
-resize_soa :: proc(array: ^$T/#soa[dynamic]$E, length: int, loc := #caller_location) -> Allocator_Error {
+resize_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error {
 	if array == nil {
 		return nil
 	}
@@ -183,7 +183,7 @@ resize_soa :: proc(array: ^$T/#soa[dynamic]$E, length: int, loc := #caller_locat
 }
 
 @builtin
-non_zero_resize_soa :: proc(array: ^$T/#soa[dynamic]$E, length: int, loc := #caller_location) -> Allocator_Error {
+non_zero_resize_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error {
 	if array == nil {
 		return nil
 	}
@@ -194,12 +194,12 @@ non_zero_resize_soa :: proc(array: ^$T/#soa[dynamic]$E, length: int, loc := #cal
 }
 
 @builtin
-reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error {
+reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error {
 	return _reserve_soa(array, capacity, true, loc)
 }
 
 @builtin
-non_zero_reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error {
+non_zero_reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error {
 	return _reserve_soa(array, capacity, false, loc)
 }
 
@@ -484,7 +484,7 @@ into_dynamic_soa :: proc(array: $T/#soa[]$E) -> #soa[dynamic]E {
 // Note: If you the elements to remain in their order, use `ordered_remove_soa`.
 // Note: If the index is out of bounds, this procedure will panic.
 @builtin
-unordered_remove_soa :: proc(array: ^$T/#soa[dynamic]$E, index: int, loc := #caller_location) #no_bounds_check {
+unordered_remove_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int index: int, loc := #caller_location) #no_bounds_check {
 	bounds_check_error_loc(loc, index, len(array))
 	if index+1 < len(array) {
 		ti := type_info_of(typeid_of(T))
@@ -512,7 +512,7 @@ unordered_remove_soa :: proc(array: ^$T/#soa[dynamic]$E, index: int, loc := #cal
 // Note: If you the elements do not have to remain in their order, prefer `unordered_remove_soa`.
 // Note: If the index is out of bounds, this procedure will panic.
 @builtin
-ordered_remove_soa :: proc(array: ^$T/#soa[dynamic]$E, index: int, loc := #caller_location) #no_bounds_check {
+ordered_remove_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int index: int, loc := #caller_location) #no_bounds_check {
 	bounds_check_error_loc(loc, index, len(array))
 	if index+1 < len(array) {
 		ti := type_info_of(typeid_of(T))

+ 2 - 2
base/runtime/default_allocators_nil.odin

@@ -1,8 +1,8 @@
 package runtime
 
 nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
-                               size, alignment: int,
-                               old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
+                           size, alignment: int,
+                           old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
 	switch mode {
 	case .Alloc, .Alloc_Non_Zeroed:
 		return nil, .Out_Of_Memory

+ 3 - 3
base/runtime/entry_unix.odin

@@ -1,6 +1,6 @@
-//+private
-//+build linux, darwin, freebsd, openbsd, netbsd, haiku
-//+no-instrumentation
+#+private
+#+build linux, darwin, freebsd, openbsd, netbsd, haiku
+#+no-instrumentation
 package runtime
 
 import "base:intrinsics"

+ 3 - 3
base/runtime/entry_wasm.odin

@@ -1,6 +1,6 @@
-//+private
-//+build wasm32, wasm64p32
-//+no-instrumentation
+#+private
+#+build wasm32, wasm64p32
+#+no-instrumentation
 package runtime
 
 import "base:intrinsics"

+ 3 - 3
base/runtime/entry_windows.odin

@@ -1,6 +1,6 @@
-//+private
-//+build windows
-//+no-instrumentation
+#+private
+#+build windows
+#+no-instrumentation
 package runtime
 
 import "base:intrinsics"

+ 12 - 10
base/runtime/heap_allocator.odin

@@ -20,25 +20,27 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 	//
 
 	aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr, old_size: int, zero_memory := true) -> ([]byte, Allocator_Error) {
+		// Not(flysand): We need to reserve enough space for alignment, which
+		// includes the user data itself, the space to store the pointer to
+		// allocation start, as well as the padding required to align both
+		// the user data and the pointer.
 		a := max(alignment, align_of(rawptr))
-		space := size + a - 1
-
+		space := a-1 + size_of(rawptr) + size
 		allocated_mem: rawptr
 
-		force_copy := old_ptr != nil && a > align_of(rawptr)
+		force_copy := old_ptr != nil && alignment > align_of(rawptr)
 
-		if !force_copy && old_ptr != nil {
+		if old_ptr != nil && !force_copy {
 			original_old_ptr := ([^]rawptr)(old_ptr)[-1]
-			allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
+			allocated_mem = heap_resize(original_old_ptr, space)
 		} else {
-			allocated_mem = heap_alloc(space+size_of(rawptr), zero_memory)
+			allocated_mem = heap_alloc(space, zero_memory)
 		}
 		aligned_mem := rawptr(([^]u8)(allocated_mem)[size_of(rawptr):])
 
 		ptr := uintptr(aligned_mem)
-		aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a)
-		diff := int(aligned_ptr - ptr)
-		if (size + diff) > space || allocated_mem == nil {
+		aligned_ptr := (ptr + uintptr(a)-1) & ~(uintptr(a)-1)
+		if allocated_mem == nil {
 			aligned_free(old_ptr)
 			aligned_free(allocated_mem)
 			return nil, .Out_Of_Memory
@@ -48,7 +50,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
 		([^]rawptr)(aligned_mem)[-1] = allocated_mem
 
 		if force_copy {
-			mem_copy_non_overlapping(aligned_mem, old_ptr, old_size)
+			mem_copy_non_overlapping(aligned_mem, old_ptr, min(old_size, size))
 			aligned_free(old_ptr)
 		}
 

+ 2 - 2
base/runtime/heap_allocator_orca.odin

@@ -1,5 +1,5 @@
-//+build orca
-//+private
+#+build orca
+#+private
 package runtime
 
 foreign {

+ 2 - 2
base/runtime/heap_allocator_other.odin

@@ -1,5 +1,5 @@
-//+build js, wasi, freestanding, essence
-//+private
+#+build js, wasi, freestanding, essence
+#+private
 package runtime
 
 _heap_alloc :: proc "contextless" (size: int, zero_memory := true) -> rawptr {

+ 2 - 2
base/runtime/heap_allocator_unix.odin

@@ -1,5 +1,5 @@
-//+build linux, darwin, freebsd, openbsd, netbsd, haiku
-//+private
+#+build linux, darwin, freebsd, openbsd, netbsd, haiku
+#+private
 package runtime
 
 when ODIN_OS == .Darwin {

+ 8 - 5
base/runtime/internal.odin

@@ -1,4 +1,4 @@
-//+vet !cast
+#+vet !cast
 package runtime
 
 import "base:intrinsics"
@@ -118,16 +118,15 @@ mem_copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> r
 DEFAULT_ALIGNMENT :: 2*align_of(rawptr)
 
 mem_alloc_bytes :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
-	if size == 0 {
-		return nil, nil
-	}
-	if allocator.procedure == nil {
+	assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc)
+	if size == 0 || allocator.procedure == nil{
 		return nil, nil
 	}
 	return allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc)
 }
 
 mem_alloc :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
+	assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc)
 	if size == 0 || allocator.procedure == nil {
 		return nil, nil
 	}
@@ -135,6 +134,7 @@ mem_alloc :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, a
 }
 
 mem_alloc_non_zeroed :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
+	assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc)
 	if size == 0 || allocator.procedure == nil {
 		return nil, nil
 	}
@@ -174,6 +174,7 @@ mem_free_all :: #force_inline proc(allocator := context.allocator, loc := #calle
 }
 
 _mem_resize :: #force_inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, should_zero: bool, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
+	assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc)
 	if allocator.procedure == nil {
 		return nil, nil
 	}
@@ -215,9 +216,11 @@ _mem_resize :: #force_inline proc(ptr: rawptr, old_size, new_size: int, alignmen
 }
 
 mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
+	assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc)
 	return _mem_resize(ptr, old_size, new_size, alignment, allocator, true, loc)
 }
 non_zero_mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
+	assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc)
 	return _mem_resize(ptr, old_size, new_size, alignment, allocator, false, loc)
 }
 

+ 2 - 2
base/runtime/os_specific_bsd.odin

@@ -1,5 +1,5 @@
-//+build freebsd, openbsd, netbsd
-//+private
+#+build freebsd, openbsd, netbsd
+#+private
 package runtime
 
 foreign import libc "system:c"

+ 2 - 2
base/runtime/os_specific_darwin.odin

@@ -1,5 +1,5 @@
-//+build darwin
-//+private
+#+build darwin
+#+private
 package runtime
 
 import "base:intrinsics"

+ 2 - 2
base/runtime/os_specific_freestanding.odin

@@ -1,5 +1,5 @@
-//+build freestanding
-//+private
+#+build freestanding
+#+private
 package runtime
 
 // TODO(bill): reimplement `os.write`

+ 2 - 2
base/runtime/os_specific_haiku.odin

@@ -1,5 +1,5 @@
-//+build haiku
-//+private
+#+build haiku
+#+private
 package runtime
 
 foreign import libc "system:c"

+ 2 - 2
base/runtime/os_specific_js.odin

@@ -1,5 +1,5 @@
-//+build js
-//+private
+#+build js
+#+private
 package runtime
 
 foreign import "odin_env"

+ 1 - 1
base/runtime/os_specific_linux.odin

@@ -1,4 +1,4 @@
-//+private
+#+private
 package runtime
 
 import "base:intrinsics"

+ 2 - 2
base/runtime/os_specific_orca.odin

@@ -1,5 +1,5 @@
-//+build orca
-//+private
+#+build orca
+#+private
 package runtime
 
 import "base:intrinsics"

+ 2 - 2
base/runtime/os_specific_wasi.odin

@@ -1,5 +1,5 @@
-//+build wasi
-//+private
+#+build wasi
+#+private
 package runtime
 
 foreign import wasi "wasi_snapshot_preview1"

+ 2 - 2
base/runtime/os_specific_windows.odin

@@ -1,5 +1,5 @@
-//+build windows
-//+private
+#+build windows
+#+private
 package runtime
 
 foreign import kernel32 "system:Kernel32.lib"

+ 1 - 1
base/runtime/procs_darwin.odin

@@ -1,4 +1,4 @@
-//+private
+#+private
 package runtime
 
 foreign import "system:Foundation.framework"

+ 1 - 1
base/runtime/procs_js.odin

@@ -1,4 +1,4 @@
-//+build js
+#+build js
 package runtime
 
 init_default_context_for_js: Context

+ 42 - 20
base/runtime/procs_wasm.odin

@@ -1,4 +1,4 @@
-//+build wasm32, wasm64p32
+#+build wasm32, wasm64p32
 package runtime
 
 @(private="file")
@@ -14,33 +14,57 @@ ti_uint :: struct #raw_union {
 }
 
 @(link_name="__ashlti3", linkage="strong")
-__ashlti3 :: proc "contextless" (la, ha: u64, b_: u32) -> i128 {
-	bits_in_dword :: size_of(u32)*8
-	b := u32(b_)
+__ashlti3 :: proc "contextless" (a: i128, b: u32) -> i128 {
+	bits :: 64
 	
-	input, result: ti_int
-	input.lo, input.hi = la, ha
-	if b & bits_in_dword != 0 {
+	input: ti_int = ---
+	result: ti_int = ---
+	input.all = a
+	if b & bits != 0 {
 		result.lo = 0
-		result.hi = input.lo << (b-bits_in_dword)
+		result.hi = input.lo << (b-bits)
 	} else {
 		if b == 0 {
-			return input.all
+			return a
 		}
 		result.lo = input.lo<<b
-		result.hi = (input.hi<<b) | (input.lo>>(bits_in_dword-b))
+		result.hi = (input.hi<<b) | (input.lo>>(bits-b))
 	}
 	return result.all
 }
 
+__ashlti3_unsigned :: proc "contextless" (a: u128, b: u32) -> u128 {
+	return cast(u128)__ashlti3(cast(i128)a, b)
+}
+
+@(link_name="__mulddi3", linkage="strong")
+__mulddi3 :: proc "contextless" (a, b: u64) -> i128 {
+	r: ti_int
+	bits :: 32
+
+	mask :: ~u64(0) >> bits
+	r.lo = (a & mask) * (b & mask)
+	t := r.lo >> bits
+	r.lo &= mask
+	t += (a >> bits) * (b & mask)
+	r.lo += (t & mask) << bits
+	r.hi = t >> bits
+	t = r.lo >> bits
+	r.lo &= mask
+	t += (b >> bits) * (a & mask)
+	r.lo += (t & mask) << bits
+	r.hi += t >> bits
+	r.hi += (a >> bits) * (b >> bits)
+	return r.all
+}
 
 @(link_name="__multi3", linkage="strong")
-__multi3 :: proc "contextless" (la, ha, lb, hb: u64) -> i128 {
+__multi3 :: proc "contextless" (a, b: i128) -> i128 {
 	x, y, r: ti_int
 
-	x.lo, x.hi = la, ha
-	y.lo, y.hi = lb, hb
-	r.all = i128(x.lo * y.lo) // TODO this is incorrect
+	x.all = a
+	y.all = b
+	r.all = __mulddi3(x.lo, y.lo)
 	r.hi += x.hi*y.lo + x.lo*y.hi
 	return r.all
 }
@@ -54,18 +78,16 @@ udivti3 :: proc "c" (la, ha, lb, hb: u64) -> u128 {
 }
 
 @(link_name="__lshrti3", linkage="strong")
-__lshrti3 :: proc "c" (la, ha: u64, b: u32) -> i128 {
-	bits :: size_of(u32)*8
+__lshrti3 :: proc "c" (a: i128, b: u32) -> i128 {
+	bits :: 64
 
 	input, result: ti_int
-	input.lo = la
-	input.hi = ha
-
+	input.all = a
 	if b & bits != 0 {
 		result.hi = 0
 		result.lo = input.hi >> (b - bits)
 	} else if b == 0 {
-		return input.all
+		return a
 	} else {
 		result.hi = input.hi >> b
 		result.lo = (input.hi << (bits - b)) | (input.lo >> b)

+ 2 - 2
base/runtime/procs_windows_amd64.odin

@@ -1,5 +1,5 @@
-//+private
-//+no-instrumentation
+#+private
+#+no-instrumentation
 package runtime
 
 foreign import kernel32 "system:Kernel32.lib"

+ 2 - 2
base/runtime/procs_windows_i386.odin

@@ -1,5 +1,5 @@
-//+private
-//+no-instrumentation
+#+private
+#+no-instrumentation
 package runtime
 
 @require foreign import "system:int64.lib"

+ 34 - 0
base/runtime/thread_management.odin

@@ -0,0 +1,34 @@
+package runtime
+
+Thread_Local_Cleaner :: #type proc "odin" ()
+
+@(private="file")
+thread_local_cleaners: [8]Thread_Local_Cleaner
+
+// Add a procedure that will be run at the end of a thread for the purpose of
+// deallocating state marked as `thread_local`.
+//
+// Intended to be called in an `init` procedure of a package with
+// dynamically-allocated memory that is stored in `thread_local` variables.
+add_thread_local_cleaner :: proc "contextless" (p: Thread_Local_Cleaner) {
+	for &v in thread_local_cleaners {
+		if v == nil {
+			v = p
+			return
+		}
+	}
+	panic_contextless("There are no more thread-local cleaner slots available.")
+}
+
+// Run all of the thread-local cleaner procedures.
+//
+// Intended to be called by the internals of a threading API at the end of a
+// thread's lifetime.
+run_thread_local_cleaners :: proc "odin" () {
+	for p in thread_local_cleaners {
+		if p == nil {
+			break
+		}
+		p()
+	}
+}

+ 1 - 1
base/runtime/wasm_allocator.odin

@@ -1,4 +1,4 @@
-//+build wasm32, wasm64p32
+#+build wasm32, wasm64p32
 package runtime
 
 import "base:intrinsics"

+ 19 - 0
ci/build_linux_static.sh

@@ -0,0 +1,19 @@
+#!/usr/bin/env sh
+# Intended for use in Alpine containers, see the "nightly" Github action for a list of dependencies
+
+CXX="clang++-18"
+LLVM_CONFIG="llvm-config-18"
+
+DISABLED_WARNINGS="-Wno-switch -Wno-macro-redefined -Wno-unused-value"
+
+CPPFLAGS="-DODIN_VERSION_RAW=\"dev-$(date +"%Y-%m")\""
+CXXFLAGS="-std=c++14 $($LLVM_CONFIG --cxxflags --ldflags)"
+
+LDFLAGS="-static -lm -lzstd -lz -lffi -pthread -ldl -fuse-ld=mold"
+LDFLAGS="$LDFLAGS $($LLVM_CONFIG --link-static --ldflags --libs --system-libs --libfiles)"
+LDFLAGS="$LDFLAGS -Wl,-rpath=\$ORIGIN"
+
+EXTRAFLAGS="-DNIGHTLY -O3"
+
+set -x
+$CXX src/main.cpp src/libtommath.cpp $DISABLED_WARNINGS $CPPFLAGS $CXXFLAGS $EXTRAFLAGS $LDFLAGS -o odin

+ 24 - 19
ci/nightly.py

@@ -2,7 +2,7 @@ import os
 import sys
 from zipfile  import ZipFile, ZIP_DEFLATED
 from b2sdk.v2 import InMemoryAccountInfo, B2Api
-from datetime import datetime
+from datetime import datetime, timezone
 import json
 
 UPLOAD_FOLDER = "nightly/"
@@ -22,7 +22,7 @@ def auth() -> bool:
 		pass        # Not yet authenticated
 
 	err = b2_api.authorize_account("production", application_key_id, application_key)
-	return err == None
+	return err is None
 
 def get_bucket():
 	if not auth(): sys.exit(1)
@@ -32,30 +32,35 @@ def remove_prefix(text: str, prefix: str) -> str:
 	return text[text.startswith(prefix) and len(prefix):]
 
 def create_and_upload_artifact_zip(platform: str, artifact: str) -> int:
-	now = datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)
-	destination_zip_name = "odin-{}-nightly+{}.zip".format(platform, now.strftime("%Y-%m-%d"))
+	now = datetime.now(timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0)
 
-	source_zip_name = artifact
-	if not artifact.endswith(".zip"):
-		print(f"Creating archive {destination_zip_name} from {artifact} and uploading to {bucket_name}")
+	source_archive: str
+	destination_name = f'odin-{platform}-nightly+{now.strftime("%Y-%m-%d")}'
 
-		source_zip_name = destination_zip_name
-		with ZipFile(source_zip_name, mode='w', compression=ZIP_DEFLATED, compresslevel=9) as z:
+	if platform.startswith("linux") or platform.startswith("macos"):
+		destination_name += ".tar.gz"
+		source_archive = artifact
+	else:
+		destination_name += ".zip"
+		source_archive = destination_name
+
+		print(f"Creating archive {destination_name} from {artifact} and uploading to {bucket_name}")
+		with ZipFile(source_archive, mode='w', compression=ZIP_DEFLATED, compresslevel=9) as z:
 			for root, directory, filenames in os.walk(artifact):
 				for file in filenames:
 					file_path = os.path.join(root, file)
 					zip_path  = os.path.join("dist", os.path.relpath(file_path, artifact))
 					z.write(file_path, zip_path)
 
-		if not os.path.exists(source_zip_name):
-			print(f"Error: Newly created ZIP archive {source_zip_name} not found.")
-			return 1
+	if not os.path.exists(source_archive):
+		print(f"Error: archive {source_archive} not found.")
+		return 1
 
-	print("Uploading {} to {}".format(source_zip_name, UPLOAD_FOLDER + destination_zip_name))
+	print("Uploading {} to {}".format(source_archive, UPLOAD_FOLDER + destination_name))
 	bucket = get_bucket()
 	res = bucket.upload_local_file(
-		source_zip_name,                   # Local file to upload
-		"nightly/" + destination_zip_name, # B2 destination path
+		source_archive,               # Local file to upload
+		"nightly/" + destination_name, # B2 destination path
 	)
 	return 0
 
@@ -65,8 +70,8 @@ def prune_artifacts():
 	bucket = get_bucket()
 	for file, _ in bucket.ls(UPLOAD_FOLDER, latest_only=False):
 		# Timestamp is in milliseconds
-		date  = datetime.fromtimestamp(file.upload_timestamp / 1_000.0).replace(hour=0, minute=0, second=0, microsecond=0)
-		now   = datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)
+		date  = datetime.fromtimestamp(file.upload_timestamp / 1_000.0, tz=timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0)
+		now   = datetime.now(timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0)
 		delta = now - date
 
 		if delta.days > int(days_to_keep):
@@ -100,7 +105,7 @@ def update_nightly_json():
 			'sizeInBytes': size,
 		})
 
-	now = datetime.utcnow().isoformat()
+	now = datetime.now(timezone.utc).isoformat()
 
 	nightly = json.dumps({
 		'last_updated' : now,
@@ -137,4 +142,4 @@ if __name__ == "__main__":
 
 		elif command == "json":
 			res = update_nightly_json()
-			sys.exit(res)
+			sys.exit(res)

+ 33 - 3
core/bytes/buffer.odin

@@ -144,6 +144,9 @@ buffer_grow :: proc(b: ^Buffer, n: int, loc := #caller_location) {
 }
 
 buffer_write_at :: proc(b: ^Buffer, p: []byte, offset: int, loc := #caller_location) -> (n: int, err: io.Error) {
+	if len(p) == 0 {
+		return 0, nil
+	}
 	b.last_read = .Invalid
 	if offset < 0 {
 		err = .Invalid_Offset
@@ -246,10 +249,13 @@ buffer_read_ptr :: proc(b: ^Buffer, ptr: rawptr, size: int) -> (n: int, err: io.
 }
 
 buffer_read_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) {
+	if len(p) == 0 {
+		return 0, nil
+	}
 	b.last_read = .Invalid
 
 	if uint(offset) >= len(b.buf) {
-		err = .Invalid_Offset
+		err = .EOF
 		return
 	}
 	n = copy(p, b.buf[offset:])
@@ -310,6 +316,27 @@ buffer_unread_rune :: proc(b: ^Buffer) -> io.Error {
 	return nil
 }
 
+buffer_seek :: proc(b: ^Buffer, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
+	abs: i64
+	switch whence {
+	case .Start:
+		abs = offset
+	case .Current:
+		abs = i64(b.off) + offset
+	case .End:
+		abs = i64(len(b.buf)) + offset
+	case:
+		return 0, .Invalid_Whence
+	}
+
+	abs_int := int(abs)
+	if abs_int < 0 {
+		return 0, .Invalid_Offset
+	}
+	b.last_read = .Invalid
+	b.off = abs_int
+	return abs, nil
+}
 
 buffer_read_bytes :: proc(b: ^Buffer, delim: byte) -> (line: []byte, err: io.Error) {
 	i := index_byte(b.buf[b.off:], delim)
@@ -395,14 +422,17 @@ _buffer_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offse
 		return io._i64_err(buffer_write(b, p))
 	case .Write_At:
 		return io._i64_err(buffer_write_at(b, p, int(offset)))
+	case .Seek:
+		n, err = buffer_seek(b, offset, whence)
+		return
 	case .Size:
-		n = i64(buffer_capacity(b))
+		n = i64(buffer_length(b))
 		return
 	case .Destroy:
 		buffer_destroy(b)
 		return
 	case .Query:
-		return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Size, .Destroy})
+		return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Destroy, .Query})
 	}
 	return 0, .Empty
 }

+ 5 - 7
core/bytes/bytes.odin

@@ -334,7 +334,7 @@ Inputs:
 Returns:
 - index: The index of the byte `c`, or -1 if it was not found.
 */
-index_byte :: proc(s: []byte, c: byte) -> (index: int) #no_bounds_check {
+index_byte :: proc "contextless" (s: []byte, c: byte) -> (index: int) #no_bounds_check {
 	i, l := 0, len(s)
 
 	// Guard against small strings.  On modern systems, it is ALWAYS
@@ -469,18 +469,16 @@ Inputs:
 Returns:
 - index: The index of the byte `c`, or -1 if it was not found.
 */
-last_index_byte :: proc(s: []byte, c: byte) -> int #no_bounds_check {
+last_index_byte :: proc "contextless" (s: []byte, c: byte) -> int #no_bounds_check {
 	i := len(s)
 
 	// Guard against small strings.  On modern systems, it is ALWAYS
 	// worth vectorizing assuming there is a hardware vector unit, and
 	// the data size is large enough.
 	if i < SIMD_REG_SIZE_128 {
-		if i > 0 { // Handle s == nil.
-			for /**/; i >= 0; i -= 1 {
-				if s[i] == c {
-					return i
-				}
+		#reverse for ch, j in s {
+			if ch == c {
+				return j
 			}
 		}
 		return -1

+ 9 - 2
core/bytes/reader.odin

@@ -9,10 +9,11 @@ Reader :: struct {
 	prev_rune: int,    // previous reading index of rune or < 0
 }
 
-reader_init :: proc(r: ^Reader, s: []byte) {
+reader_init :: proc(r: ^Reader, s: []byte) -> io.Stream {
 	r.s = s
 	r.i = 0
 	r.prev_rune = -1
+	return reader_to_stream(r)
 }
 
 reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
@@ -33,6 +34,9 @@ reader_size :: proc(r: ^Reader) -> i64 {
 }
 
 reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
+	if len(p) == 0 {
+		return 0, nil
+	}
 	if r.i >= i64(len(r.s)) {
 		return 0, .EOF
 	}
@@ -42,6 +46,9 @@ reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
 	return
 }
 reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Error) {
+	if len(p) == 0 {
+		return 0, nil
+	}
 	if off < 0 {
 		return 0, .Invalid_Offset
 	}
@@ -97,7 +104,6 @@ reader_unread_rune :: proc(r: ^Reader) -> io.Error {
 	return nil
 }
 reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
-	r.prev_rune = -1
 	abs: i64
 	switch whence {
 	case .Start:
@@ -114,6 +120,7 @@ reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.E
 		return 0, .Invalid_Offset
 	}
 	r.i = abs
+	r.prev_rune = -1
 	return abs, nil
 }
 reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {

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

@@ -98,6 +98,14 @@ when ODIN_OS == .Haiku {
 	ERANGE :: B_POSIX_ERROR_BASE + 17
 }
 
+when ODIN_OS == .JS {
+	_ :: libc
+	_get_errno :: proc "c" () -> ^int {
+		@(static) errno: int
+		return &errno
+	}
+}
+
 // Odin has no way to make an identifier "errno" behave as a function call to
 // read the value, or to produce an lvalue such that you can assign a different
 // error value to errno. To work around this, just expose it as a function like

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

@@ -89,6 +89,30 @@ when ODIN_OS == .Linux {
 	}
 }
 
+when ODIN_OS == .JS {
+	fpos_t        :: struct #raw_union { _: [16]char, _: longlong, _: double, }
+
+	_IOFBF        :: 0
+	_IOLBF        :: 1
+	_IONBF        :: 2
+
+	BUFSIZ        :: 1024
+
+	EOF           :: int(-1)
+
+	FOPEN_MAX     :: 1000
+
+	FILENAME_MAX  :: 4096
+
+	L_tmpnam      :: 20
+
+	SEEK_SET      :: 0
+	SEEK_CUR      :: 1
+	SEEK_END      :: 2
+
+	TMP_MAX       :: 308915776
+}
+
 when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
 	fpos_t :: distinct i64
 
@@ -368,7 +392,7 @@ to_stream :: proc(file: ^FILE) -> io.Stream {
 			return 0, .Empty
 		
 		case .Query:
-			return io.query_utility({ .Close, .Flush, .Read, .Read_At, .Write, .Write_At, .Seek, .Size })
+			return io.query_utility({ .Close, .Flush, .Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Query })
 		}
 		return
 	}

+ 10 - 0
core/c/libc/stdlib.odin

@@ -10,6 +10,9 @@ when ODIN_OS == .Windows {
 	foreign import libc "system:c"
 }
 
+@(require)
+import "base:runtime"
+
 when ODIN_OS == .Windows {
 	RAND_MAX :: 0x7fff
 
@@ -145,6 +148,10 @@ aligned_alloc :: #force_inline proc "c" (alignment, size: size_t) -> rawptr {
 			_aligned_malloc :: proc(size, alignment: size_t) -> rawptr ---
 		}
 		return _aligned_malloc(size=size, alignment=alignment)
+	} else when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 {
+		context = runtime.default_context()
+		data, _ := runtime.mem_alloc_bytes(auto_cast size, auto_cast alignment)
+		return raw_data(data)
 	} else {
 		foreign libc {
 			aligned_alloc :: proc(alignment, size: size_t) -> rawptr ---
@@ -160,6 +167,9 @@ aligned_free :: #force_inline proc "c" (ptr: rawptr) {
 			_aligned_free :: proc(ptr: rawptr) ---
 		}
 		_aligned_free(ptr)
+	} else when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 {
+		context = runtime.default_context()
+		runtime.mem_free(ptr)
 	} else {
 		free(ptr)
 	}

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

@@ -12,6 +12,7 @@ when ODIN_OS == .Windows {
 	foreign import libc "system:c"
 }
 
+@(default_calling_convention="c")
 foreign libc {
 	// 7.24.2 Copying functions
 	memcpy   :: proc(s1, s2: rawptr, n: size_t) -> rawptr ---

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

@@ -45,7 +45,7 @@ when ODIN_OS == .Windows {
 	}
 }
 
-when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku {
+when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku || ODIN_OS == .JS  {
 	@(default_calling_convention="c")
 	foreign libc {
 		// 7.27.2 Time manipulation functions
@@ -79,7 +79,7 @@ when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS =
 	} else {
 		@(private) LDIFFTIME  :: "difftime"
 		@(private) LMKTIME    :: "mktime"
-		@(private) LTIME      :: "ltime"
+		@(private) LTIME      :: "time"
 		@(private) LCTIME     :: "ctime"
 		@(private) LGMTIME    :: "gmtime"
 		@(private) LLOCALTIME :: "localtime"

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

@@ -14,7 +14,7 @@ when ODIN_OS == .Windows {
 	wctrans_t :: distinct wchar_t
 	wctype_t  :: distinct ushort
 
-} else when ODIN_OS == .Linux {
+} else when ODIN_OS == .Linux || ODIN_OS == .JS {
 	wctrans_t :: distinct intptr_t
 	wctype_t  :: distinct ulong
 

+ 90 - 0
core/compress/gzip/doc.odin

@@ -0,0 +1,90 @@
+/*
+	Copyright 2021 Jeroen van Rijn <[email protected]>.
+	Made available under Odin's BSD-3 license.
+
+	List of contributors:
+		Jeroen van Rijn: Initial implementation.
+		Ginger Bill:     Cosmetic changes.
+
+	A small GZIP implementation as an example.
+*/
+
+/*
+Example:
+	import "core:bytes"
+	import "core:os"
+	import "core:compress"
+	import "core:fmt"
+
+	// Small GZIP file with fextra, fname and fcomment present.
+	@private
+	TEST: []u8 = {
+		0x1f, 0x8b, 0x08, 0x1c, 0xcb, 0x3b, 0x3a, 0x5a,
+		0x02, 0x03, 0x07, 0x00, 0x61, 0x62, 0x03, 0x00,
+		0x63, 0x64, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x6e,
+		0x61, 0x6d, 0x65, 0x00, 0x54, 0x68, 0x69, 0x73,
+		0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x63, 0x6f,
+		0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x2b, 0x48,
+		0xac, 0xcc, 0xc9, 0x4f, 0x4c, 0x01, 0x00, 0x15,
+		0x6a, 0x2c, 0x42, 0x07, 0x00, 0x00, 0x00,
+	}
+
+	main :: proc() {
+		// Set up output buffer.
+		buf := bytes.Buffer{}
+
+		stdout :: proc(s: string) {
+			os.write_string(os.stdout, s)
+		}
+		stderr :: proc(s: string) {
+			os.write_string(os.stderr, s)
+		}
+
+		args := os.args
+
+		if len(args) < 2 {
+			stderr("No input file specified.\n")
+			err := load(data=TEST, buf=&buf, known_gzip_size=len(TEST))
+			if err == nil {
+				stdout("Displaying test vector: ")
+				stdout(bytes.buffer_to_string(&buf))
+				stdout("\n")
+			} else {
+				fmt.printf("gzip.load returned %v\n", err)
+			}
+			bytes.buffer_destroy(&buf)
+			os.exit(0)
+		}
+
+		// The rest are all files.
+		args = args[1:]
+		err: Error
+
+		for file in args {
+			if file == "-" {
+				// Read from stdin
+				s := os.stream_from_handle(os.stdin)
+				ctx := &compress.Context_Stream_Input{
+					input = s,
+				}
+				err = load(ctx, &buf)
+			} else {
+				err = load(file, &buf)
+			}
+			if err != nil {
+				if err != E_General.File_Not_Found {
+					stderr("File not found: ")
+					stderr(file)
+					stderr("\n")
+					os.exit(1)
+				}
+				stderr("GZIP returned an error.\n")
+					bytes.buffer_destroy(&buf)
+				os.exit(2)
+			}
+			stdout(bytes.buffer_to_string(&buf))
+		}
+		bytes.buffer_destroy(&buf)
+	}
+*/
+package compress_gzip

+ 0 - 89
core/compress/gzip/example.odin

@@ -1,89 +0,0 @@
-//+build ignore
-package compress_gzip
-
-/*
-	Copyright 2021 Jeroen van Rijn <[email protected]>.
-	Made available under Odin's BSD-3 license.
-
-	List of contributors:
-		Jeroen van Rijn: Initial implementation.
-		Ginger Bill:     Cosmetic changes.
-
-	A small GZIP implementation as an example.
-*/
-
-import "core:bytes"
-import "core:os"
-import "core:compress"
-import "core:fmt"
-
-// Small GZIP file with fextra, fname and fcomment present.
-@private
-TEST: []u8 = {
-	0x1f, 0x8b, 0x08, 0x1c, 0xcb, 0x3b, 0x3a, 0x5a,
-	0x02, 0x03, 0x07, 0x00, 0x61, 0x62, 0x03, 0x00,
-	0x63, 0x64, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x6e,
-	0x61, 0x6d, 0x65, 0x00, 0x54, 0x68, 0x69, 0x73,
-	0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x63, 0x6f,
-	0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x2b, 0x48,
-	0xac, 0xcc, 0xc9, 0x4f, 0x4c, 0x01, 0x00, 0x15,
-	0x6a, 0x2c, 0x42, 0x07, 0x00, 0x00, 0x00,
-}
-
-main :: proc() {
-	// Set up output buffer.
-	buf := bytes.Buffer{}
-
-	stdout :: proc(s: string) {
-		os.write_string(os.stdout, s)
-	}
-	stderr :: proc(s: string) {
-		os.write_string(os.stderr, s)
-	}
-
-	args := os.args
-
-	if len(args) < 2 {
-		stderr("No input file specified.\n")
-		err := load(data=TEST, buf=&buf, known_gzip_size=len(TEST))
-		if err == nil {
-			stdout("Displaying test vector: ")
-			stdout(bytes.buffer_to_string(&buf))
-			stdout("\n")
-		} else {
-			fmt.printf("gzip.load returned %v\n", err)
-		}
-		bytes.buffer_destroy(&buf)
-		os.exit(0)
-	}
-
-	// The rest are all files.
-	args = args[1:]
-	err: Error
-
-	for file in args {
-		if file == "-" {
-			// Read from stdin
-			s := os.stream_from_handle(os.stdin)
-			ctx := &compress.Context_Stream_Input{
-				input = s,
-			}
-			err = load(ctx, &buf)
-		} else {
-			err = load(file, &buf)
-		}
-		if err != nil {
-			if err != E_General.File_Not_Found {
-				stderr("File not found: ")
-				stderr(file)
-				stderr("\n")
-				os.exit(1)
-			}
-			stderr("GZIP returned an error.\n")
-				bytes.buffer_destroy(&buf)
-			os.exit(2)
-		}
-		stdout(bytes.buffer_to_string(&buf))
-	}
-	bytes.buffer_destroy(&buf)
-}

+ 1 - 2
core/compress/shoco/model.odin

@@ -4,7 +4,6 @@
 	which is an English word model.
 */
 
-// package shoco is an implementation of the shoco short string compressor
 package compress_shoco
 
 DEFAULT_MODEL :: Shoco_Model {
@@ -145,4 +144,4 @@ DEFAULT_MODEL :: Shoco_Model {
 		{ 0xc0000000, 2, 4, { 25, 22, 19, 16, 16, 16, 16, 16 }, { 15,  7,  7,  7, 0, 0, 0, 0 }, 0xe0, 0xc0 },
 		{ 0xe0000000, 4, 8, { 23, 19, 15, 11,  8,  5,  2,  0 }, { 31, 15, 15, 15, 7, 7, 7, 3 }, 0xf0, 0xe0 },
 	},
-}
+}

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

@@ -8,7 +8,7 @@
 	An implementation of [shoco](https://github.com/Ed-von-Schleck/shoco) by Christian Schramm.
 */
 
-// package shoco is an implementation of the shoco short string compressor
+// package shoco is an implementation of the shoco short string compressor.
 package compress_shoco
 
 import "base:intrinsics"
@@ -308,4 +308,4 @@ compress_string :: proc(input: string, model := DEFAULT_MODEL, allocator := cont
 	resize(&buf, length) or_return
 	return buf[:length], result
 }
-compress :: proc{compress_string_to_buffer, compress_string}
+compress :: proc{compress_string_to_buffer, compress_string}

+ 50 - 0
core/compress/zlib/doc.odin

@@ -0,0 +1,50 @@
+/*
+	Copyright 2021 Jeroen van Rijn <[email protected]>.
+	Made available under Odin's BSD-3 license.
+
+	List of contributors:
+		Jeroen van Rijn: Initial implementation.
+
+	An example of how to use `zlib.inflate`.
+*/
+
+/*
+Example:
+	package main
+
+	import "core:bytes"
+	import "core:fmt"
+
+	main :: proc() {
+		ODIN_DEMO := []u8{
+			120, 218, 101, 144,  65, 110, 131,  48,  16,  69, 215, 246,  41, 190,  44,  69,  73,  32, 148, 182,
+			 75,  75,  28,  32, 251,  46, 217,  88, 238,   0,  86, 192,  32, 219,  36, 170, 170, 172, 122, 137,
+			238, 122, 197,  30, 161,  70, 162,  20,  81, 203, 139,  25, 191, 255, 191,  60,  51,  40, 125,  81,
+			 53,  33, 144,  15, 156, 155, 110, 232,  93, 128, 208, 189,  35,  89, 117,  65, 112, 222,  41,  99,
+			 33,  37,   6, 215, 235, 195,  17, 239, 156, 197, 170, 118, 170, 131,  44,  32,  82, 164,  72, 240,
+			253, 245, 249, 129,  12, 185, 224,  76, 105,  61, 118,  99, 171,  66, 239,  38, 193,  35, 103,  85,
+			172,  66, 127,  33, 139,  24, 244, 235, 141,  49, 204, 223,  76, 208, 205, 204, 166,   7, 173,  60,
+			 97, 159, 238,  37, 214,  41, 105, 129, 167,   5, 102,  27, 152, 173,  97, 178, 129,  73, 129, 231,
+			  5, 230,  27, 152, 175, 225,  52, 192, 127, 243, 170, 157, 149,  18, 121, 142, 115, 109, 227, 122,
+			 64,  87, 114, 111, 161,  49, 182,   6, 181, 158, 162, 226, 206, 167,  27, 215, 246,  48,  56,  99,
+			 67, 117,  16,  47,  13,  45,  35, 151,  98, 231,  75,   1, 173,  90,  61, 101, 146,  71, 136, 244,
+			170, 218, 145, 176, 123,  45, 173,  56, 113, 134, 191,  51, 219,  78, 235,  95,  28, 249, 253,   7,
+			159, 150, 133, 125,
+		}
+		OUTPUT_SIZE :: 432
+
+		buf: bytes.Buffer
+
+		// We can pass ", true" to inflate a raw DEFLATE stream instead of a ZLIB wrapped one.
+		err := inflate(input=ODIN_DEMO, buf=&buf, expected_output_size=OUTPUT_SIZE)
+		defer bytes.buffer_destroy(&buf)
+
+		if err != nil {
+			fmt.printf("\nError: %v\n", err)
+		}
+		s := bytes.buffer_to_string(&buf)
+		fmt.printf("Input: %v bytes, output (%v bytes):\n%v\n", len(ODIN_DEMO), len(s), s)
+		assert(len(s) == OUTPUT_SIZE)
+	}
+*/
+package compress_zlib

+ 0 - 47
core/compress/zlib/example.odin

@@ -1,47 +0,0 @@
-//+build ignore
-package compress_zlib
-
-/*
-	Copyright 2021 Jeroen van Rijn <[email protected]>.
-	Made available under Odin's BSD-3 license.
-
-	List of contributors:
-		Jeroen van Rijn: Initial implementation.
-
-	An example of how to use `zlib.inflate`.
-*/
-
-import "core:bytes"
-import "core:fmt"
-
-main :: proc() {
-	ODIN_DEMO := []u8{
-		120, 218, 101, 144,  65, 110, 131,  48,  16,  69, 215, 246,  41, 190,  44,  69,  73,  32, 148, 182,
-		 75,  75,  28,  32, 251,  46, 217,  88, 238,   0,  86, 192,  32, 219,  36, 170, 170, 172, 122, 137,
-		238, 122, 197,  30, 161,  70, 162,  20,  81, 203, 139,  25, 191, 255, 191,  60,  51,  40, 125,  81,
-		 53,  33, 144,  15, 156, 155, 110, 232,  93, 128, 208, 189,  35,  89, 117,  65, 112, 222,  41,  99,
-		 33,  37,   6, 215, 235, 195,  17, 239, 156, 197, 170, 118, 170, 131,  44,  32,  82, 164,  72, 240,
-		253, 245, 249, 129,  12, 185, 224,  76, 105,  61, 118,  99, 171,  66, 239,  38, 193,  35, 103,  85,
-		172,  66, 127,  33, 139,  24, 244, 235, 141,  49, 204, 223,  76, 208, 205, 204, 166,   7, 173,  60,
-		 97, 159, 238,  37, 214,  41, 105, 129, 167,   5, 102,  27, 152, 173,  97, 178, 129,  73, 129, 231,
-		  5, 230,  27, 152, 175, 225,  52, 192, 127, 243, 170, 157, 149,  18, 121, 142, 115, 109, 227, 122,
-		 64,  87, 114, 111, 161,  49, 182,   6, 181, 158, 162, 226, 206, 167,  27, 215, 246,  48,  56,  99,
-		 67, 117,  16,  47,  13,  45,  35, 151,  98, 231,  75,   1, 173,  90,  61, 101, 146,  71, 136, 244,
-		170, 218, 145, 176, 123,  45, 173,  56, 113, 134, 191,  51, 219,  78, 235,  95,  28, 249, 253,   7,
-		159, 150, 133, 125,
-	}
-	OUTPUT_SIZE :: 432
-
-	buf: bytes.Buffer
-
-	// We can pass ", true" to inflate a raw DEFLATE stream instead of a ZLIB wrapped one.
-	err := inflate(input=ODIN_DEMO, buf=&buf, expected_output_size=OUTPUT_SIZE)
-	defer bytes.buffer_destroy(&buf)
-
-	if err != nil {
-		fmt.printf("\nError: %v\n", err)
-	}
-	s := bytes.buffer_to_string(&buf)
-	fmt.printf("Input: %v bytes, output (%v bytes):\n%v\n", len(ODIN_DEMO), len(s), s)
-	assert(len(s) == OUTPUT_SIZE)
-}

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

@@ -1,4 +1,4 @@
-//+vet !using-param
+#+vet !using-param
 package compress_zlib
 
 /*

+ 60 - 14
core/container/bit_array/bit_array.odin

@@ -1,5 +1,6 @@
 package container_dynamic_bit_array
 
+import "base:builtin"
 import "base:intrinsics"
 import "core:mem"
 
@@ -18,7 +19,7 @@ NUM_BITS :: 64
 Bit_Array :: struct {
 	bits:         [dynamic]u64,
 	bias:         int,
-	max_index:    int,
+	length:       int,
 	free_pointer: bool,
 }
 
@@ -52,9 +53,9 @@ Returns:
 */
 iterate_by_all :: proc (it: ^Bit_Array_Iterator) -> (set: bool, index: int, ok: bool) {
 	index = it.word_idx * NUM_BITS + int(it.bit_idx) + it.array.bias
-	if index > it.array.max_index { return false, 0, false }
+	if index >= it.array.length + it.array.bias { return false, 0, false }
 
-	word := it.array.bits[it.word_idx] if len(it.array.bits) > it.word_idx else 0
+	word := it.array.bits[it.word_idx] if builtin.len(it.array.bits) > it.word_idx else 0
 	set = (word >> it.bit_idx & 1) == 1
 
 	it.bit_idx += 1
@@ -106,22 +107,22 @@ Returns:
 */
 @(private="file")
 iterate_internal_ :: proc (it: ^Bit_Array_Iterator, $ITERATE_SET_BITS: bool) -> (index: int, ok: bool) {
-	word := it.array.bits[it.word_idx] if len(it.array.bits) > it.word_idx else 0
+	word := it.array.bits[it.word_idx] if builtin.len(it.array.bits) > it.word_idx else 0
 	when ! ITERATE_SET_BITS { word = ~word }
 
 	// If the word is empty or we have already gone over all the bits in it,
 	// b.bit_idx is greater than the index of any set bit in the word,
 	// meaning that word >> b.bit_idx == 0.
-	for it.word_idx < len(it.array.bits) && word >> it.bit_idx == 0 {
+	for it.word_idx < builtin.len(it.array.bits) && word >> it.bit_idx == 0 {
 		it.word_idx += 1
 		it.bit_idx = 0
-		word = it.array.bits[it.word_idx] if len(it.array.bits) > it.word_idx else 0
+		word = it.array.bits[it.word_idx] if builtin.len(it.array.bits) > it.word_idx else 0
 		when ! ITERATE_SET_BITS { word = ~word }
 	}
 
 	// If we are iterating the set bits, reaching the end of the array means we have no more bits to check
 	when ITERATE_SET_BITS {
-		if it.word_idx >= len(it.array.bits) {
+		if it.word_idx >= builtin.len(it.array.bits) {
 			return 0, false
 		}
 	}
@@ -135,7 +136,7 @@ iterate_internal_ :: proc (it: ^Bit_Array_Iterator, $ITERATE_SET_BITS: bool) ->
 		it.bit_idx = 0
 		it.word_idx += 1
 	}
-	return index, index <= it.array.max_index
+	return index, index < it.array.length + it.array.bias
 }
 /*
 Gets the state of a bit in the bit-array
@@ -160,7 +161,7 @@ get :: proc(ba: ^Bit_Array, #any_int index: uint) -> (res: bool, ok: bool) #opti
 		If we `get` a bit that doesn't fit in the Bit Array, it's naturally `false`.
 		This early-out prevents unnecessary resizing.
 	*/
-	if leg_index + 1 > len(ba.bits) { return false, true }
+	if leg_index + 1 > builtin.len(ba.bits) { return false, true }
 
 	val := u64(1 << uint(bit_index))
 	res = ba.bits[leg_index] & val == val
@@ -208,7 +209,7 @@ set :: proc(ba: ^Bit_Array, #any_int index: uint, set_to: bool = true, allocator
 
 	resize_if_needed(ba, leg_index) or_return
 
-	ba.max_index = max(idx, ba.max_index)
+	ba.length = max(1 + idx, ba.length)
 
 	if set_to {
 		ba.bits[leg_index] |=  1 << uint(bit_index)
@@ -261,6 +262,9 @@ unsafe_unset :: proc(b: ^Bit_Array, bit: int) #no_bounds_check {
 /*
 A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
 
+The range of bits created by this procedure is `min_index..<max_index`, and the
+array will be able to expand beyond `max_index` if needed.
+
 *Allocates (`new(Bit_Array) & make(ba.bits)`)*
 
 Inputs:
@@ -275,7 +279,7 @@ create :: proc(max_index: int, min_index: int = 0, allocator := context.allocato
 	context.allocator = allocator
 	size_in_bits := max_index - min_index
 
-	if size_in_bits < 1 { return {}, false }
+	if size_in_bits < 0 { return {}, false }
 
 	legs := size_in_bits >> INDEX_SHIFT
 	if size_in_bits & INDEX_MASK > 0 {legs+=1}
@@ -284,7 +288,7 @@ create :: proc(max_index: int, min_index: int = 0, allocator := context.allocato
 	res = new(Bit_Array)
 	res.bits         = bits
 	res.bias         = min_index
-	res.max_index    = max_index
+	res.length       = max_index - min_index
 	res.free_pointer = true
 	return
 }
@@ -299,6 +303,48 @@ clear :: proc(ba: ^Bit_Array) {
 	mem.zero_slice(ba.bits[:])
 }
 /*
+Gets the length of set and unset valid bits in the Bit_Array.
+
+Inputs:
+- ba: The target Bit_Array
+
+Returns:
+- length: The length of valid bits.
+*/
+len :: proc(ba: ^Bit_Array) -> (length: int) {
+	if ba == nil { return }
+	return ba.length
+}
+/*
+Shrinks the Bit_Array's backing storage to the smallest possible size.
+
+Inputs:
+- ba: The target Bit_Array
+*/
+shrink :: proc(ba: ^Bit_Array) #no_bounds_check {
+	if ba == nil { return }
+	legs_needed := builtin.len(ba.bits)
+	for i := legs_needed - 1; i >= 0; i -= 1 {
+		if ba.bits[i] == 0 {
+			legs_needed -= 1
+		} else {
+			break
+		}
+	}
+	if legs_needed == builtin.len(ba.bits) {
+		return
+	}
+	ba.length = 0
+	if legs_needed > 0 {
+		if legs_needed > 1 {
+			ba.length = (legs_needed - 1) * NUM_BITS
+		}
+		ba.length += NUM_BITS - int(intrinsics.count_leading_zeros(ba.bits[legs_needed - 1]))
+	}
+	resize(&ba.bits, legs_needed)
+	builtin.shrink(&ba.bits)
+}
+/*
 Deallocates the Bit_Array and its backing storage
 
 Inputs:
@@ -321,8 +367,8 @@ resize_if_needed :: proc(ba: ^Bit_Array, legs: int, allocator := context.allocat
 
 	context.allocator = allocator
 
-	if legs + 1 > len(ba.bits) {
+	if legs + 1 > builtin.len(ba.bits) {
 		resize(&ba.bits, legs + 1)
 	}
-	return len(ba.bits) > legs
+	return builtin.len(ba.bits) > legs
 }

+ 4 - 4
core/container/bit_array/doc.odin

@@ -1,8 +1,8 @@
 /*
 The Bit Array can be used in several ways:
 
-- By default you don't need to instantiate a Bit Array:
-
+By default you don't need to instantiate a Bit Array.
+Example:
 	package test
 
 	import "core:fmt"
@@ -22,8 +22,8 @@ The Bit Array can be used in several ways:
 		destroy(&bits)
 	}
 
-- A Bit Array can optionally allow for negative indices, if the minimum value was given during creation:
-
+A Bit Array can optionally allow for negative indices, if the minimum value was given during creation.
+Example:
 	package test
 
 	import "core:fmt"

+ 9 - 6
core/container/intrusive/list/doc.odin

@@ -1,22 +1,22 @@
 /*
 Package list implements an intrusive doubly-linked list.
 
-An intrusive container requires a `Node` to be embedded in your own structure, like this:
-
+An intrusive container requires a `Node` to be embedded in your own structure, like this.
+Example:
 	My_String :: struct {
 		node:  list.Node,
 		value: string,
 	}
 
-Embedding the members of a `list.Node` in your structure with the `using` keyword is also allowed:
-
+Embedding the members of a `list.Node` in your structure with the `using` keyword is also allowed.
+Example:
 	My_String :: struct {
 		using node: list.Node,
 		value: string,
 	}
 
-Here is a full example:
-
+Here is a full example.
+Example:
 	package test
 	
 	import "core:fmt"
@@ -42,5 +42,8 @@ Here is a full example:
 	    value: string,
 	}
 
+Output:
+	Hello
+	World
 */
 package container_intrusive_list

+ 7 - 3
core/container/small_array/small_array.odin

@@ -139,9 +139,13 @@ clear :: proc "contextless" (a: ^$A/Small_Array($N, $T)) {
 	resize(a, 0)
 }
 
-push_back_elems :: proc "contextless" (a: ^$A/Small_Array($N, $T), items: ..T) {
-	n := copy(a.data[a.len:], items[:])
-	a.len += n
+push_back_elems :: proc "contextless" (a: ^$A/Small_Array($N, $T), items: ..T) -> bool {
+	if a.len + builtin.len(items) <= cap(a^) {
+		n := copy(a.data[a.len:], items[:])
+		a.len += n
+		return true
+	}
+	return false
 }
 
 inject_at :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T, index: int) -> bool #no_bounds_check {

+ 1 - 1
core/crypto/_aes/hw_intel/api.odin

@@ -1,4 +1,4 @@
-//+build amd64
+#+build amd64
 package aes_hw_intel
 
 import "core:sys/info"

+ 1 - 1
core/crypto/_aes/hw_intel/ghash.odin

@@ -20,7 +20,7 @@
 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-//+build amd64
+#+build amd64
 package aes_hw_intel
 
 import "base:intrinsics"

+ 1 - 1
core/crypto/_aes/hw_intel/hw_intel_keysched.odin

@@ -20,7 +20,7 @@
 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-//+build amd64
+#+build amd64
 package aes_hw_intel
 
 import "base:intrinsics"

+ 1 - 1
core/crypto/_chacha20/simd256/chacha20_simd256.odin

@@ -1,4 +1,4 @@
-//+build amd64
+#+build amd64
 package chacha20_simd256
 
 import "base:intrinsics"

+ 1 - 1
core/crypto/_chacha20/simd256/chacha20_simd256_stub.odin

@@ -1,4 +1,4 @@
-//+build !amd64
+#+build !amd64
 package chacha20_simd256
 
 import "base:intrinsics"

+ 43 - 44
core/crypto/aead/doc.odin

@@ -10,49 +10,48 @@ algorithm.
 WARNING: Reusing the same key + iv to seal (encrypt) multiple messages
 results in catastrophic loss of security for most algorithms.
 
-```odin
-package aead_example
-
-import "core:bytes"
-import "core:crypto"
-import "core:crypto/aead"
-
-main :: proc() {
-	algo := aead.Algorithm.XCHACHA20POLY1305
-
-	// The example added associated data, and plaintext.
-	aad_str := "Get your ass in gear boys."
-	pt_str := "They're immanetizing the Eschaton."
-
-	aad := transmute([]byte)aad_str
-	plaintext := transmute([]byte)pt_str
-	pt_len := len(plaintext)
-
-	// Generate a random key for the purposes of illustration.
-	key := make([]byte, aead.KEY_SIZES[algo])
-	defer delete(key)
-	crypto.rand_bytes(key)
-
-	// `ciphertext || tag`, is a common way data is transmitted, so
-	// demonstrate that.
-	buf := make([]byte, pt_len + aead.TAG_SIZES[algo])
-	defer delete(buf)
-	ciphertext, tag := buf[:pt_len], buf[pt_len:]
-
-	// Seal the AAD + Plaintext.
-	iv := make([]byte, aead.IV_SIZES[algo])
-	defer delete(iv)
-	crypto.rand_bytes(iv) // Random IVs are safe with XChaCha20-Poly1305.
-	aead.seal(algo, ciphertext, tag, key, iv, aad, plaintext)
-
-	// Open the AAD + Ciphertext.
-	opened_pt := buf[:pt_len]
-	if ok := aead.open(algo, opened_pt, key, iv, aad, ciphertext, tag); !ok {
-		panic("aead example: failed to open")
+Example:
+	package aead_example
+
+	import "core:bytes"
+	import "core:crypto"
+	import "core:crypto/aead"
+
+	main :: proc() {
+		algo := aead.Algorithm.XCHACHA20POLY1305
+
+		// The example added associated data, and plaintext.
+		aad_str := "Get your ass in gear boys."
+		pt_str := "They're immanetizing the Eschaton."
+
+		aad := transmute([]byte)aad_str
+		plaintext := transmute([]byte)pt_str
+		pt_len := len(plaintext)
+
+		// Generate a random key for the purposes of illustration.
+		key := make([]byte, aead.KEY_SIZES[algo])
+		defer delete(key)
+		crypto.rand_bytes(key)
+
+		// `ciphertext || tag`, is a common way data is transmitted, so
+		// demonstrate that.
+		buf := make([]byte, pt_len + aead.TAG_SIZES[algo])
+		defer delete(buf)
+		ciphertext, tag := buf[:pt_len], buf[pt_len:]
+
+		// Seal the AAD + Plaintext.
+		iv := make([]byte, aead.IV_SIZES[algo])
+		defer delete(iv)
+		crypto.rand_bytes(iv) // Random IVs are safe with XChaCha20-Poly1305.
+		aead.seal(algo, ciphertext, tag, key, iv, aad, plaintext)
+
+		// Open the AAD + Ciphertext.
+		opened_pt := buf[:pt_len]
+		if ok := aead.open(algo, opened_pt, key, iv, aad, ciphertext, tag); !ok {
+			panic("aead example: failed to open")
+		}
+
+		assert(bytes.equal(opened_pt, plaintext))
 	}
-
-	assert(bytes.equal(opened_pt, plaintext))
-}
-```
 */
-package aead
+package aead

+ 3 - 3
core/crypto/aes/aes.odin

@@ -2,9 +2,9 @@
 package aes implements the AES block cipher and some common modes.
 
 See:
-- https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.197-upd1.pdf
-- https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf
-- https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
+- [[ https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.197-upd1.pdf ]]
+- [[ https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf ]]
+- [[ https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf ]]
 */
 package aes
 

+ 1 - 1
core/crypto/aes/aes_ctr_hw_intel.odin

@@ -1,4 +1,4 @@
-//+build amd64
+#+build amd64
 package aes
 
 import "base:intrinsics"

+ 1 - 1
core/crypto/aes/aes_ecb_hw_intel.odin

@@ -1,4 +1,4 @@
-//+build amd64
+#+build amd64
 package aes
 
 import "base:intrinsics"

+ 1 - 1
core/crypto/aes/aes_gcm_hw_intel.odin

@@ -1,4 +1,4 @@
-//+build amd64
+#+build amd64
 package aes
 
 import "base:intrinsics"

+ 1 - 1
core/crypto/aes/aes_impl_hw_gen.odin

@@ -1,4 +1,4 @@
-//+build !amd64
+#+build !amd64
 package aes
 
 @(private = "file")

+ 1 - 1
core/crypto/aes/aes_impl_hw_intel.odin

@@ -1,4 +1,4 @@
-//+build amd64
+#+build amd64
 package aes
 
 import "core:crypto/_aes/hw_intel"

+ 2 - 2
core/crypto/blake2b/blake2b.odin

@@ -2,8 +2,8 @@
 package blake2b implements the BLAKE2b hash algorithm.
 
 See:
-- https://datatracker.ietf.org/doc/html/rfc7693
-- https://www.blake2.net
+- [[ https://datatracker.ietf.org/doc/html/rfc7693 ]]
+- [[ https://www.blake2.net ]]
 */
 package blake2b
 

+ 2 - 2
core/crypto/blake2s/blake2s.odin

@@ -2,8 +2,8 @@
 package blake2s implements the BLAKE2s hash algorithm.
 
 See:
-- https://datatracker.ietf.org/doc/html/rfc7693
-- https://www.blake2.net/
+- [[ https://datatracker.ietf.org/doc/html/rfc7693 ]]
+- [[ https://www.blake2.net/ ]]
 */
 package blake2s
 

+ 2 - 2
core/crypto/chacha20/chacha20.odin

@@ -2,8 +2,8 @@
 package chacha20 implements the ChaCha20 and XChaCha20 stream ciphers.
 
 See:
-- https://datatracker.ietf.org/doc/html/rfc8439
-- https://datatracker.ietf.org/doc/draft-irtf-cfrg-xchacha/03/
+- [[ https://datatracker.ietf.org/doc/html/rfc8439 ]]
+- [[ https://datatracker.ietf.org/doc/draft-irtf-cfrg-xchacha/03/ ]]
 */
 package chacha20
 

+ 2 - 2
core/crypto/chacha20poly1305/chacha20poly1305.odin

@@ -4,8 +4,8 @@ AEAD_XChaCha20_Poly1305 Authenticated Encryption with Additional Data
 algorithms.
 
 See:
-- https://www.rfc-editor.org/rfc/rfc8439
-- https://datatracker.ietf.org/doc/html/draft-arciszewski-xchacha-03
+- [[ https://www.rfc-editor.org/rfc/rfc8439 ]]
+- [[ https://datatracker.ietf.org/doc/html/draft-arciszewski-xchacha-03 ]]
 */
 package chacha20poly1305
 

+ 3 - 3
core/crypto/ed25519/ed25519.odin

@@ -2,9 +2,9 @@
 package ed25519 implements the Ed25519 EdDSA signature algorithm.
 
 See:
-- https://datatracker.ietf.org/doc/html/rfc8032
-- https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf
-- https://eprint.iacr.org/2020/1244.pdf
+- [[ https://datatracker.ietf.org/doc/html/rfc8032 ]]
+- [[ https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf ]]
+- [[ https://eprint.iacr.org/2020/1244.pdf ]]
 */
 package ed25519
 

+ 28 - 30
core/crypto/hash/doc.odin

@@ -17,46 +17,44 @@ accomplish common tasks.
   A third optional boolean parameter controls if the file is streamed
   (default), or or read at once.
 
-```odin
-package hash_example
+Example:
+	package hash_example
 
-import "core:crypto/hash"
+	import "core:crypto/hash"
 
-main :: proc() {
-	input := "Feed the fire."
+	main :: proc() {
+		input := "Feed the fire."
 
-	// Compute the digest, using the high level API.
-	returned_digest := hash.hash(hash.Algorithm.SHA512_256, input)
-	defer delete(returned_digest)
+		// Compute the digest, using the high level API.
+		returned_digest := hash.hash(hash.Algorithm.SHA512_256, input)
+		defer delete(returned_digest)
 
-	// Variant that takes a destination buffer, instead of returning
-	// the digest.
-	digest := make([]byte, hash.DIGEST_SIZES[hash.Algorithm.BLAKE2B]) // @note: Destination buffer has to be at least as big as the digest size of the hash.
-	defer delete(digest)
-	hash.hash(hash.Algorithm.BLAKE2B, input, digest)
-}
-```
+		// Variant that takes a destination buffer, instead of returning
+		// the digest.
+		digest := make([]byte, hash.DIGEST_SIZES[hash.Algorithm.BLAKE2B]) // @note: Destination buffer has to be at least as big as the digest size of the hash.
+		defer delete(digest)
+		hash.hash(hash.Algorithm.BLAKE2B, input, digest)
+	}
 
 A generic low level API is provided supporting the init/update/final interface
 that is typical with cryptographic hash function implementations.
 
-```odin
-package hash_example
+Example:
+	package hash_example
 
-import "core:crypto/hash"
+	import "core:crypto/hash"
 
-main :: proc() {
-    input := "Let the cinders burn."
+	main :: proc() {
+		input := "Let the cinders burn."
 
-    // Compute the digest, using the low level API.
-    ctx: hash.Context
-    digest := make([]byte, hash.DIGEST_SIZES[hash.Algorithm.SHA3_512])
-    defer delete(digest)
+		// Compute the digest, using the low level API.
+		ctx: hash.Context
+		digest := make([]byte, hash.DIGEST_SIZES[hash.Algorithm.SHA3_512])
+		defer delete(digest)
 
-    hash.init(&ctx, hash.Algorithm.SHA3_512)
-    hash.update(&ctx, transmute([]byte)input)
-    hash.final(&ctx, digest)
-}
-```
+		hash.init(&ctx, hash.Algorithm.SHA3_512)
+		hash.update(&ctx, transmute([]byte)input)
+		hash.final(&ctx, digest)
+	}
 */
-package crypto_hash
+package crypto_hash

+ 4 - 38
core/crypto/hash/hash.odin

@@ -1,16 +1,15 @@
 package crypto_hash
 
 /*
-    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.
+	List of contributors:
+		zhibog, dotbmp:  Initial implementation.
 */
 
 import "core:io"
 import "core:mem"
-import "core:os"
 
 // hash_bytes will hash the given input and return the computed digest
 // in a newly allocated slice.
@@ -87,36 +86,3 @@ hash_stream :: proc(
 
 	return dst, io.Error.None
 }
-
-// hash_file will read the file provided by the given handle and return the
-// computed digest in a newly allocated slice.
-hash_file :: proc(
-	algorithm: Algorithm,
-	hd: os.Handle,
-	load_at_once := false,
-	allocator := context.allocator,
-) -> (
-	[]byte,
-	io.Error,
-) {
-	if !load_at_once {
-		return hash_stream(algorithm, os.stream_from_handle(hd), allocator)
-	}
-
-	buf, ok := os.read_entire_file(hd, allocator)
-	if !ok {
-		return nil, io.Error.Unknown
-	}
-	defer delete(buf, allocator)
-
-	return hash_bytes(algorithm, buf, allocator), io.Error.None
-}
-
-hash :: proc {
-	hash_stream,
-	hash_file,
-	hash_bytes,
-	hash_string,
-	hash_bytes_to_buffer,
-	hash_string_to_buffer,
-}

+ 10 - 0
core/crypto/hash/hash_freestanding.odin

@@ -0,0 +1,10 @@
+#+build freestanding
+package crypto_hash
+
+hash :: proc {
+	hash_stream,
+	hash_bytes,
+	hash_string,
+	hash_bytes_to_buffer,
+	hash_string_to_buffer,
+}

+ 38 - 0
core/crypto/hash/hash_os.odin

@@ -0,0 +1,38 @@
+#+build !freestanding
+package crypto_hash
+
+import "core:io"
+import "core:os"
+
+// hash_file will read the file provided by the given handle and return the
+// computed digest in a newly allocated slice.
+hash_file :: proc(
+	algorithm: Algorithm,
+	hd: os.Handle,
+	load_at_once := false,
+	allocator := context.allocator,
+) -> (
+	[]byte,
+	io.Error,
+) {
+	if !load_at_once {
+		return hash_stream(algorithm, os.stream_from_handle(hd), allocator)
+	}
+
+	buf, ok := os.read_entire_file(hd, allocator)
+	if !ok {
+		return nil, io.Error.Unknown
+	}
+	defer delete(buf, allocator)
+
+	return hash_bytes(algorithm, buf, allocator), io.Error.None
+}
+
+hash :: proc {
+	hash_stream,
+	hash_file,
+	hash_bytes,
+	hash_string,
+	hash_bytes_to_buffer,
+	hash_string_to_buffer,
+}

+ 1 - 1
core/crypto/hkdf/hkdf.odin

@@ -2,7 +2,7 @@
 package hkdf implements the HKDF HMAC-based Extract-and-Expand Key
 Derivation Function.
 
-See: https://www.rfc-editor.org/rfc/rfc5869
+See: [[ https://www.rfc-editor.org/rfc/rfc5869 ]]
 */
 package hkdf
 

+ 1 - 1
core/crypto/hmac/hmac.odin

@@ -2,7 +2,7 @@
 package hmac implements the HMAC MAC algorithm.
 
 See:
-- https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.198-1.pdf
+- [[ https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.198-1.pdf ]]
 */
 package hmac
 

+ 1 - 1
core/crypto/kmac/kmac.odin

@@ -2,7 +2,7 @@
 package kmac implements the KMAC MAC algorithm.
 
 See:
-- https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-185.pdf
+- [[ https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-185.pdf ]]
 */
 package kmac
 

+ 2 - 2
core/crypto/legacy/md5/md5.odin

@@ -5,8 +5,8 @@ WARNING: The MD5 algorithm is known to be insecure and should only be
 used for interoperating with legacy applications.
 
 See:
-- https://eprint.iacr.org/2005/075
-- https://datatracker.ietf.org/doc/html/rfc1321
+- [[ https://eprint.iacr.org/2005/075 ]]
+- [[ https://datatracker.ietf.org/doc/html/rfc1321 ]]
 */
 package md5
 

+ 3 - 3
core/crypto/legacy/sha1/sha1.odin

@@ -5,9 +5,9 @@ WARNING: The SHA1 algorithm is known to be insecure and should only be
 used for interoperating with legacy applications.
 
 See:
-- https://eprint.iacr.org/2017/190
-- https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf
-- https://datatracker.ietf.org/doc/html/rfc3174
+- [[ https://eprint.iacr.org/2017/190 ]]
+- [[ https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf ]]
+- [[ https://datatracker.ietf.org/doc/html/rfc3174 ]]
 */
 package sha1
 

+ 1 - 1
core/crypto/pbkdf2/pbkdf2.odin

@@ -1,7 +1,7 @@
 /*
 package pbkdf2 implements the PBKDF2 password-based key derivation function.
 
-See: https://www.rfc-editor.org/rfc/rfc2898
+See: [[ https://www.rfc-editor.org/rfc/rfc2898 ]]
 */
 package pbkdf2
 

+ 1 - 1
core/crypto/poly1305/poly1305.odin

@@ -2,7 +2,7 @@
 package poly1305 implements the Poly1305 one-time MAC algorithm.
 
 See:
-- https://datatracker.ietf.org/doc/html/rfc8439
+- [[ https://datatracker.ietf.org/doc/html/rfc8439 ]]
 */
 package poly1305
 

+ 1 - 1
core/crypto/rand_bsd.odin

@@ -1,4 +1,4 @@
-//+build freebsd, openbsd, netbsd
+#+build freebsd, openbsd, netbsd
 package crypto
 
 foreign import libc "system:c"

+ 7 - 7
core/crypto/rand_generic.odin

@@ -1,10 +1,10 @@
-//+build !linux
-//+build !windows
-//+build !openbsd
-//+build !freebsd
-//+build !netbsd
-//+build !darwin
-//+build !js
+#+build !linux
+#+build !windows
+#+build !openbsd
+#+build !freebsd
+#+build !netbsd
+#+build !darwin
+#+build !js
 package crypto
 
 HAS_RAND_BYTES :: false

+ 1 - 1
core/crypto/ristretto255/ristretto255.odin

@@ -2,7 +2,7 @@
 package ristretto255 implement the ristretto255 prime-order group.
 
 See:
-- https://www.rfc-editor.org/rfc/rfc9496
+- [[ https://www.rfc-editor.org/rfc/rfc9496 ]]
 */
 package ristretto255
 

+ 2 - 2
core/crypto/sha2/sha2.odin

@@ -2,8 +2,8 @@
 package sha2 implements the SHA2 hash algorithm family.
 
 See:
-- https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf
-- https://datatracker.ietf.org/doc/html/rfc3874
+- [[ https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf ]]
+- [[ https://datatracker.ietf.org/doc/html/rfc3874 ]]
 */
 package sha2
 

+ 1 - 1
core/crypto/sha3/sha3.odin

@@ -6,7 +6,7 @@ pre-standardization Keccak algorithm is required, it can be found in
 crypto/legacy/keccak.
 
 See:
-- https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.202.pdf
+- [[ https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.202.pdf ]]
 */
 package sha3
 

+ 2 - 2
core/crypto/shake/shake.odin

@@ -4,8 +4,8 @@ package shake implements the SHAKE and cSHAKE XOF algorithm families.
 The SHA3 hash algorithm can be found in the crypto/sha3.
 
 See:
-- https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.202.pdf
-- https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-185.pdf
+- [[ https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.202.pdf ]]
+- [[ https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-185.pdf ]]
 */
 package shake
 

+ 9 - 4
core/crypto/siphash/siphash.odin

@@ -1,3 +1,12 @@
+/*
+package siphash Implements the SipHash hashing algorithm.
+
+Use the specific procedures for a certain setup. The generic procedures will default to Siphash 2-4.
+
+See:
+- [[ https://github.com/veorq/SipHash ]]
+- [[ https://www.aumasson.jp/siphash/siphash.pdf ]]
+*/
 package siphash
 
 /*
@@ -6,10 +15,6 @@ package siphash
 
     List of contributors:
         zhibog:  Initial implementation.
-
-    Implementation of the SipHash hashing algorithm, as defined at <https://github.com/veorq/SipHash> and <https://www.aumasson.jp/siphash/siphash.pdf>
-
-    Use the specific procedures for a certain setup. The generic procdedures will default to Siphash 2-4
 */
 
 import "core:crypto"

+ 1 - 1
core/crypto/sm3/sm3.odin

@@ -2,7 +2,7 @@
 package sm3 implements the SM3 hash algorithm.
 
 See:
-- https://datatracker.ietf.org/doc/html/draft-sca-cfrg-sm3-02
+- [[ https://datatracker.ietf.org/doc/html/draft-sca-cfrg-sm3-02 ]]
 */
 package sm3
 

+ 1 - 1
core/crypto/tuplehash/tuplehash.odin

@@ -2,7 +2,7 @@
 package tuplehash implements the TupleHash and TupleHashXOF algorithms.
 
 See:
-- https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-185.pdf
+- [[ https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-185.pdf ]]
 */
 package tuplehash
 

+ 1 - 1
core/crypto/x25519/x25519.odin

@@ -3,7 +3,7 @@ package x25519 implements the X25519 (aka curve25519) Elliptic-Curve
 Diffie-Hellman key exchange protocol.
 
 See:
-- https://www.rfc-editor.org/rfc/rfc7748
+- [[ https://www.rfc-editor.org/rfc/rfc7748 ]]
 */
 package x25519
 

+ 2 - 2
core/debug/trace/trace_cpp.odin

@@ -1,5 +1,5 @@
-//+private file
-//+build linux, darwin
+#+private file
+#+build linux, darwin
 package debug_trace
 
 import "base:intrinsics"

+ 3 - 1
core/debug/trace/trace_nil.odin

@@ -1,4 +1,6 @@
-//+build !windows !linux !darwin
+#+build !windows
+#+build !linux
+#+build !darwin
 package debug_trace
 
 import "base:runtime"

+ 2 - 2
core/debug/trace/trace_windows.odin

@@ -1,5 +1,5 @@
-//+private
-//+build windows
+#+private
+#+build windows
 package debug_trace
 
 import "base:intrinsics"

+ 1 - 2
core/dynlib/doc.odin

@@ -4,7 +4,6 @@ Package `core:dynlib` implements loading of shared libraries/DLLs and their symb
 The behaviour of dynamically loaded libraries is specific to the target platform of the program.
 For in depth detail on the underlying behaviour please refer to your target platform's documentation.
 
-See `example` directory for an example library exporting 3 symbols and a host program loading them automatically
-by defining a symbol table struct.
+For a full example, see: [[ core/dynlib/example; https://github.com/odin-lang/Odin/tree/master/core/dynlib/example ]]
 */
 package dynlib

+ 2 - 2
core/dynlib/lib_js.odin

@@ -1,5 +1,5 @@
-//+build js
-//+private
+#+build js
+#+private
 package dynlib
 
 _load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {

+ 2 - 2
core/dynlib/lib_unix.odin

@@ -1,5 +1,5 @@
-//+build linux, darwin, freebsd, openbsd, netbsd
-//+private
+#+build linux, darwin, freebsd, openbsd, netbsd
+#+private
 package dynlib
 
 import "core:os"

+ 2 - 2
core/dynlib/lib_windows.odin

@@ -1,5 +1,5 @@
-//+build windows
-//+private
+#+build windows
+#+private
 package dynlib
 
 import win32 "core:sys/windows"

+ 3 - 3
core/encoding/ansi/doc.odin

@@ -13,8 +13,8 @@ If your terminal supports 24-bit true color mode, you can also do this:
 	fmt.println(ansi.CSI + ansi.FG_COLOR_24_BIT + ";0;255;255" + ansi.SGR + "Hellope!" + ansi.CSI + ansi.RESET + ansi.SGR)
 
 For more information, see:
-	1. https://en.wikipedia.org/wiki/ANSI_escape_code
-	2. https://www.vt100.net/docs/vt102-ug/chapter5.html
-	3. https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
+- [[ https://en.wikipedia.org/wiki/ANSI_escape_code ]]
+- [[ https://www.vt100.net/docs/vt102-ug/chapter5.html ]]
+- [[ https://invisible-island.net/xterm/ctlseqs/ctlseqs.html ]]
 */
 package ansi

+ 11 - 27
core/encoding/cbor/unmarshal.odin

@@ -675,10 +675,6 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header,
 		return
 
 	case reflect.Type_Info_Map:
-		if !reflect.is_string(t.key) {
-			return _unsupported(v, hdr)
-		}
-
 		raw_map := (^mem.Raw_Map)(v.data)
 		if raw_map.allocator.procedure == nil {
 			raw_map.allocator = context.allocator
@@ -695,43 +691,31 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header,
 			new_len := uintptr(min(scap, runtime.map_len(raw_map^)+length))
 			runtime.map_reserve_dynamic(raw_map, t.map_info, new_len) or_return
 		}
-		
-		// Temporary memory to unmarshal keys into before inserting them into the map.
+
+		// Temporary memory to unmarshal values into before inserting them into the map.
 		elem_backing := mem.alloc_bytes_non_zeroed(t.value.size, t.value.align, context.temp_allocator) or_return
 		defer delete(elem_backing, context.temp_allocator)
-
 		map_backing_value := any{raw_data(elem_backing), t.value.id}
 
-		for idx := 0; unknown || idx < length; idx += 1 {
-			// Decode key, keys can only be strings.
-			key: string
-			if keyv, kerr := decode_key(d, v); unknown && kerr == .Break {
-				break
-			} else if kerr != nil {
-				err = kerr
-				return
-			} else {
-				key = keyv
-			}
+		// Temporary memory to unmarshal keys into.
+		key_backing := mem.alloc_bytes_non_zeroed(t.key.size, t.key.align, context.temp_allocator) or_return
+		defer delete(key_backing, context.temp_allocator)
+		key_backing_value := any{raw_data(key_backing), t.key.id}
 
+		for idx := 0; unknown || idx < length; idx += 1 {
 			if unknown || idx > scap {
 				// Reserve space for new element so we can return allocator errors.
 				new_len := uintptr(runtime.map_len(raw_map^)+1)
 				runtime.map_reserve_dynamic(raw_map, t.map_info, new_len) or_return
 			}
 
+			mem.zero_slice(key_backing)
+			_unmarshal_value(d, key_backing_value, _decode_header(r) or_return) or_return
+
 			mem.zero_slice(elem_backing)
 			_unmarshal_value(d, map_backing_value, _decode_header(r) or_return) or_return
 
-			key_ptr := rawptr(&key)
-			key_cstr: cstring
-			if reflect.is_cstring(t.key) {
-				assert_safe_for_cstring(key)
-				key_cstr = cstring(raw_data(key))
-				key_ptr = &key_cstr
-			}
-
-			set_ptr := runtime.__dynamic_map_set_without_hash(raw_map, t.map_info, key_ptr, map_backing_value.data)
+			set_ptr := runtime.__dynamic_map_set_without_hash(raw_map, t.map_info, key_backing_value.data, map_backing_value.data)
 			// We already reserved space for it, so this shouldn't fail.
 			assert(set_ptr != nil)
 		}

部分文件因为文件数量过多而无法显示