mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-05 07:08:48 +00:00
Merge branch 'master' into d3d11-annotations
This commit is contained in:
@@ -1 +1,6 @@
|
|||||||
*.odin linguist-language=Odin
|
*.odin linguist-language=Odin
|
||||||
|
* text=auto
|
||||||
|
|
||||||
|
# These files must always have *nix line-endings
|
||||||
|
Makefile text eol=lf
|
||||||
|
*.sh text eol=lf
|
||||||
+200
-98
@@ -2,106 +2,162 @@ name: CI
|
|||||||
on: [push, pull_request, workflow_dispatch]
|
on: [push, pull_request, workflow_dispatch]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_linux:
|
build_netbsd:
|
||||||
|
name: NetBSD Build, Check, and Test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
PKGSRC_BRANCH: 2024Q3
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Build, Check, and Test
|
||||||
|
timeout-minutes: 15
|
||||||
|
uses: vmactions/netbsd-vm@v1
|
||||||
|
with:
|
||||||
|
release: "10.0"
|
||||||
|
envs: PKGSRC_BRANCH
|
||||||
|
usesh: true
|
||||||
|
copyback: false
|
||||||
|
prepare: |
|
||||||
|
PKG_PATH="https://cdn.NetBSD.org/pub/pkgsrc/packages/NetBSD/amd64/$(uname -r | cut -d_ -f1)_${PKGSRC_BRANCH}/All" /usr/sbin/pkg_add pkgin
|
||||||
|
pkgin -y in gmake git bash python311 llvm clang
|
||||||
|
ln -s /usr/pkg/bin/python3.11 /usr/bin/python3
|
||||||
|
run: |
|
||||||
|
set -e -x
|
||||||
|
git config --global --add safe.directory $(pwd)
|
||||||
|
gmake release
|
||||||
|
./odin version
|
||||||
|
./odin report
|
||||||
|
gmake -C vendor/stb/src
|
||||||
|
gmake -C vendor/cgltf/src
|
||||||
|
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 -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)
|
||||||
|
./odin check tests/benchmark -vet -strict-style -no-entry-point
|
||||||
|
|
||||||
|
build_freebsd:
|
||||||
|
name: FreeBSD Build, Check, and Test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v4
|
||||||
- name: Download LLVM, botan
|
- name: Build, Check, and Test
|
||||||
run: sudo apt-get install llvm-11 clang-11 libbotan-2-dev botan
|
timeout-minutes: 15
|
||||||
- name: build odin
|
uses: vmactions/freebsd-vm@v1
|
||||||
|
with:
|
||||||
|
usesh: true
|
||||||
|
copyback: false
|
||||||
|
prepare: |
|
||||||
|
pkg install -y gmake git bash python3 libxml2 llvm17
|
||||||
|
run: |
|
||||||
|
# `set -e` is needed for test failures to register. https://github.com/vmactions/freebsd-vm/issues/72
|
||||||
|
set -e -x
|
||||||
|
git config --global --add safe.directory $(pwd)
|
||||||
|
gmake release
|
||||||
|
./odin version
|
||||||
|
./odin report
|
||||||
|
gmake -C vendor/stb/src
|
||||||
|
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 -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)
|
||||||
|
./odin check tests/benchmark -vet -strict-style -no-entry-point
|
||||||
|
ci:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
# MacOS 13 runs on Intel, 14 runs on ARM
|
||||||
|
os: [ubuntu-latest, macos-13, macos-14]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
name: ${{ matrix.os == 'macos-14' && 'MacOS ARM' || (matrix.os == 'macos-13' && 'MacOS Intel' || 'Ubuntu') }} Build, Check, and Test
|
||||||
|
timeout-minutes: 15
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Download LLVM (Linux)
|
||||||
|
if: matrix.os == 'ubuntu-latest'
|
||||||
|
run: |
|
||||||
|
wget https://apt.llvm.org/llvm.sh
|
||||||
|
chmod +x llvm.sh
|
||||||
|
sudo ./llvm.sh 17
|
||||||
|
echo "/usr/lib/llvm-17/bin" >> $GITHUB_PATH
|
||||||
|
|
||||||
|
- name: Download LLVM (MacOS Intel)
|
||||||
|
if: matrix.os == 'macos-13'
|
||||||
|
run: |
|
||||||
|
brew install llvm@17 lua@5.4
|
||||||
|
echo "/usr/local/opt/llvm@17/bin" >> $GITHUB_PATH
|
||||||
|
|
||||||
|
- name: Download LLVM (MacOS ARM)
|
||||||
|
if: matrix.os == 'macos-14'
|
||||||
|
run: |
|
||||||
|
brew install llvm@17 wasmtime lua@5.4
|
||||||
|
echo "/opt/homebrew/opt/llvm@17/bin" >> $GITHUB_PATH
|
||||||
|
|
||||||
|
- name: Build Odin
|
||||||
run: ./build_odin.sh release
|
run: ./build_odin.sh release
|
||||||
- name: Odin version
|
- name: Odin version
|
||||||
run: ./odin version
|
run: ./odin version
|
||||||
timeout-minutes: 1
|
|
||||||
- name: Odin report
|
- name: Odin report
|
||||||
run: ./odin report
|
run: ./odin report
|
||||||
timeout-minutes: 1
|
- name: Compile needed Vendor
|
||||||
|
run: |
|
||||||
|
make -C vendor/stb/src
|
||||||
|
make -C vendor/cgltf/src
|
||||||
|
make -C vendor/miniaudio/src
|
||||||
- name: Odin check
|
- name: Odin check
|
||||||
run: ./odin check examples/demo -vet
|
run: ./odin check examples/demo -vet
|
||||||
timeout-minutes: 10
|
|
||||||
- name: Odin run
|
- name: Odin run
|
||||||
run: ./odin run examples/demo
|
run: ./odin run examples/demo
|
||||||
timeout-minutes: 10
|
|
||||||
- name: Odin run -debug
|
- name: Odin run -debug
|
||||||
run: ./odin run examples/demo -debug
|
run: ./odin run examples/demo -debug
|
||||||
timeout-minutes: 10
|
|
||||||
- name: Odin check examples/all
|
- name: Odin check examples/all
|
||||||
run: ./odin check examples/all -strict-style
|
run: ./odin check examples/all -strict-style
|
||||||
timeout-minutes: 10
|
- name: Normal Core library tests
|
||||||
- name: Core library tests
|
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
|
||||||
run: |
|
- name: Optimized Core library tests
|
||||||
cd tests/core
|
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
|
||||||
make
|
|
||||||
timeout-minutes: 10
|
|
||||||
- name: Vendor library tests
|
- name: Vendor library tests
|
||||||
|
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 -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||||
|
- name: GitHub Issue tests
|
||||||
run: |
|
run: |
|
||||||
cd tests/vendor
|
cd tests/issues
|
||||||
make
|
./run.sh
|
||||||
timeout-minutes: 10
|
|
||||||
- name: Odin internals tests
|
- name: Check benchmarks
|
||||||
run: |
|
run: ./odin check tests/benchmark -vet -strict-style -no-entry-point
|
||||||
cd tests/internal
|
|
||||||
make
|
|
||||||
timeout-minutes: 10
|
|
||||||
- name: Odin check examples/all for Linux i386
|
- name: Odin check examples/all for Linux i386
|
||||||
run: ./odin check examples/all -vet -strict-style -target:linux_i386
|
run: ./odin check examples/all -vet -strict-style -disallow-do -target:linux_i386
|
||||||
timeout-minutes: 10
|
if: matrix.os == 'ubuntu-latest'
|
||||||
- name: Odin check examples/all for FreeBSD amd64
|
|
||||||
run: ./odin check examples/all -vet -strict-style -target:freebsd_amd64
|
|
||||||
timeout-minutes: 10
|
|
||||||
- name: Odin check examples/all for OpenBSD amd64
|
|
||||||
run: ./odin check examples/all -vet -strict-style -target:openbsd_amd64
|
|
||||||
timeout-minutes: 10
|
|
||||||
build_macOS:
|
|
||||||
runs-on: macos-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v1
|
|
||||||
- name: Download LLVM, botan and setup PATH
|
|
||||||
run: |
|
|
||||||
brew install llvm@13 botan
|
|
||||||
echo "/usr/local/opt/llvm@13/bin" >> $GITHUB_PATH
|
|
||||||
TMP_PATH=$(xcrun --show-sdk-path)/user/include
|
|
||||||
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
|
|
||||||
- name: build odin
|
|
||||||
run: ./build_odin.sh release
|
|
||||||
- name: Odin version
|
|
||||||
run: ./odin version
|
|
||||||
timeout-minutes: 1
|
|
||||||
- name: Odin report
|
|
||||||
run: ./odin report
|
|
||||||
timeout-minutes: 1
|
|
||||||
- name: Odin check
|
|
||||||
run: ./odin check examples/demo -vet
|
|
||||||
timeout-minutes: 10
|
|
||||||
- name: Odin run
|
|
||||||
run: ./odin run examples/demo
|
|
||||||
timeout-minutes: 10
|
|
||||||
- name: Odin run -debug
|
|
||||||
run: ./odin run examples/demo -debug
|
|
||||||
timeout-minutes: 10
|
|
||||||
- name: Odin check examples/all
|
|
||||||
run: ./odin check examples/all -strict-style
|
|
||||||
timeout-minutes: 10
|
|
||||||
- name: Core library tests
|
|
||||||
run: |
|
|
||||||
cd tests/core
|
|
||||||
make
|
|
||||||
timeout-minutes: 10
|
|
||||||
- name: Odin internals tests
|
|
||||||
run: |
|
|
||||||
cd tests/internal
|
|
||||||
make
|
|
||||||
timeout-minutes: 10
|
|
||||||
- name: Odin check examples/all for Darwin arm64
|
|
||||||
run: ./odin check examples/all -vet -strict-style -target:darwin_arm64
|
|
||||||
timeout-minutes: 10
|
|
||||||
- name: Odin check examples/all for Linux arm64
|
- name: Odin check examples/all for Linux arm64
|
||||||
run: ./odin check examples/all -vet -strict-style -target:linux_arm64
|
run: ./odin check examples/all -vet -strict-style -disallow-do -target:linux_arm64
|
||||||
timeout-minutes: 10
|
if: matrix.os == 'ubuntu-latest'
|
||||||
|
- name: Odin check examples/all for FreeBSD amd64
|
||||||
|
run: ./odin check examples/all -vet -strict-style -disallow-do -target:freebsd_amd64
|
||||||
|
if: matrix.os == 'ubuntu-latest'
|
||||||
|
- name: Odin check examples/all for OpenBSD amd64
|
||||||
|
run: ./odin check examples/all -vet -strict-style -disallow-do -target:openbsd_amd64
|
||||||
|
if: matrix.os == 'ubuntu-latest'
|
||||||
|
|
||||||
|
- name: Run demo on WASI WASM32
|
||||||
|
run: |
|
||||||
|
./odin build examples/demo -target:wasi_wasm32 -vet -strict-style -disallow-do -out:demo.wasm
|
||||||
|
wasmtime ./demo.wasm
|
||||||
|
if: matrix.os == 'macos-14'
|
||||||
|
|
||||||
build_windows:
|
build_windows:
|
||||||
|
name: Windows Build, Check, and Test
|
||||||
runs-on: windows-2022
|
runs-on: windows-2022
|
||||||
|
timeout-minutes: 15
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v4
|
||||||
- name: build Odin
|
- name: build Odin
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
@@ -109,72 +165,118 @@ jobs:
|
|||||||
./build.bat 1
|
./build.bat 1
|
||||||
- name: Odin version
|
- name: Odin version
|
||||||
run: ./odin version
|
run: ./odin version
|
||||||
timeout-minutes: 1
|
|
||||||
- name: Odin report
|
- name: Odin report
|
||||||
run: ./odin report
|
run: ./odin report
|
||||||
timeout-minutes: 1
|
|
||||||
- name: Odin check
|
- name: Odin check
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||||
odin check examples/demo -vet
|
odin check examples/demo -vet
|
||||||
timeout-minutes: 10
|
|
||||||
- name: Odin run
|
- name: Odin run
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||||
odin run examples/demo
|
odin run examples/demo
|
||||||
timeout-minutes: 10
|
|
||||||
- name: Odin run -debug
|
- name: Odin run -debug
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
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
|
||||||
timeout-minutes: 10
|
|
||||||
- name: Odin check examples/all
|
- name: Odin check examples/all
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
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
|
||||||
timeout-minutes: 10
|
|
||||||
- name: Core library tests
|
- name: Core library tests
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||||
cd tests\core
|
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
|
||||||
call build.bat
|
- name: Optimized core library tests
|
||||||
timeout-minutes: 10
|
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 -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||||
- name: Vendor library tests
|
- name: Vendor library tests
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||||
cd tests\vendor
|
copy vendor\lua\5.4\windows\*.dll .
|
||||||
call build.bat
|
odin test tests/vendor -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||||
timeout-minutes: 10
|
|
||||||
- name: Odin internals tests
|
- name: Odin internals tests
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||||
cd tests\internal
|
odin test tests/internal -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||||
call build.bat
|
- name: Check benchmarks
|
||||||
timeout-minutes: 10
|
shell: cmd
|
||||||
|
run: |
|
||||||
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||||
|
odin check tests/benchmark -vet -strict-style -no-entry-point
|
||||||
- name: Odin documentation tests
|
- name: Odin documentation tests
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||||
cd tests\documentation
|
cd tests\documentation
|
||||||
call build.bat
|
call build.bat
|
||||||
timeout-minutes: 10
|
|
||||||
- name: core:math/big tests
|
- name: core:math/big tests
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||||
cd tests\core\math\big
|
cd tests\core\math\big
|
||||||
call build.bat
|
call build.bat
|
||||||
timeout-minutes: 10
|
|
||||||
- name: Odin check examples/all for Windows 32bits
|
- name: Odin check examples/all for Windows 32bits
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||||
odin check examples/all -strict-style -target:windows_i386
|
odin check examples/all -strict-style -target:windows_i386
|
||||||
timeout-minutes: 10
|
|
||||||
|
build_linux_riscv64:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Linux riscv64 (emulated) Build, Check and Test
|
||||||
|
timeout-minutes: 15
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Download LLVM (Linux)
|
||||||
|
run: |
|
||||||
|
wget https://apt.llvm.org/llvm.sh
|
||||||
|
chmod +x llvm.sh
|
||||||
|
sudo ./llvm.sh 18
|
||||||
|
echo "/usr/lib/llvm-18/bin" >> $GITHUB_PATH
|
||||||
|
|
||||||
|
- name: Build Odin
|
||||||
|
run: ./build_odin.sh release
|
||||||
|
|
||||||
|
- name: Odin version
|
||||||
|
run: ./odin version
|
||||||
|
|
||||||
|
- name: Odin report
|
||||||
|
run: ./odin report
|
||||||
|
|
||||||
|
- name: Compile needed Vendor
|
||||||
|
run: |
|
||||||
|
make -C vendor/stb/src
|
||||||
|
make -C vendor/cgltf/src
|
||||||
|
make -C vendor/miniaudio/src
|
||||||
|
|
||||||
|
- name: Odin check
|
||||||
|
run: ./odin check examples/all -target:linux_riscv64 -vet -strict-style -disallow-do
|
||||||
|
|
||||||
|
- name: Install riscv64 toolchain and qemu
|
||||||
|
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 -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 -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 -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 -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 -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
|
||||||
|
|||||||
+132
-65
@@ -7,10 +7,11 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build_windows:
|
build_windows:
|
||||||
|
name: Windows Build
|
||||||
if: github.repository == 'odin-lang/Odin'
|
if: github.repository == 'odin-lang/Odin'
|
||||||
runs-on: windows-2022
|
runs-on: windows-2022
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v4
|
||||||
- name: build Odin
|
- name: build Odin
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
@@ -29,102 +30,181 @@ jobs:
|
|||||||
cp LICENSE dist
|
cp LICENSE dist
|
||||||
cp LLVM-C.dll dist
|
cp LLVM-C.dll dist
|
||||||
cp -r shared dist
|
cp -r shared dist
|
||||||
|
cp -r base dist
|
||||||
cp -r core dist
|
cp -r core dist
|
||||||
cp -r vendor dist
|
cp -r vendor dist
|
||||||
cp -r bin dist
|
cp -r bin dist
|
||||||
cp -r examples dist
|
cp -r examples dist
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
|
include-hidden-files: true
|
||||||
name: windows_artifacts
|
name: windows_artifacts
|
||||||
path: dist
|
path: dist
|
||||||
build_ubuntu:
|
build_linux:
|
||||||
|
name: Linux Build
|
||||||
if: github.repository == 'odin-lang/Odin'
|
if: github.repository == 'odin-lang/Odin'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v4
|
||||||
|
- uses: jirutka/setup-alpine@v1
|
||||||
|
with:
|
||||||
|
branch: v3.20
|
||||||
- name: (Linux) Download LLVM
|
- name: (Linux) Download LLVM
|
||||||
run: sudo apt-get install llvm-11 clang-11
|
run: |
|
||||||
|
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
|
- 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
|
- name: Odin run
|
||||||
run: ./odin run examples/demo
|
run: ./odin run examples/demo
|
||||||
- name: Copy artifacts
|
- name: Copy artifacts
|
||||||
run: |
|
run: |
|
||||||
mkdir dist
|
FILE="odin-linux-amd64-nightly+$(date -I)"
|
||||||
cp odin dist
|
mkdir $FILE
|
||||||
cp LICENSE dist
|
cp odin $FILE
|
||||||
cp libLLVM* dist
|
cp LICENSE $FILE
|
||||||
cp -r shared dist
|
cp -r shared $FILE
|
||||||
cp -r core dist
|
cp -r base $FILE
|
||||||
cp -r vendor dist
|
cp -r core $FILE
|
||||||
cp -r examples dist
|
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
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ubuntu_artifacts
|
name: linux_artifacts
|
||||||
path: dist
|
path: dist.tar.gz
|
||||||
build_macos:
|
build_macos:
|
||||||
|
name: MacOS Build
|
||||||
if: github.repository == 'odin-lang/Odin'
|
if: github.repository == 'odin-lang/Odin'
|
||||||
runs-on: macOS-latest
|
runs-on: macos-13
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v4
|
||||||
- name: Download LLVM and setup PATH
|
- name: Download LLVM and setup PATH
|
||||||
run: |
|
run: |
|
||||||
brew install llvm@13
|
brew install llvm@18 dylibbundler
|
||||||
echo "/usr/local/opt/llvm@13/bin" >> $GITHUB_PATH
|
echo "/usr/local/opt/llvm@18/bin" >> $GITHUB_PATH
|
||||||
TMP_PATH=$(xcrun --show-sdk-path)/user/include
|
|
||||||
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
|
|
||||||
- name: build odin
|
- name: build odin
|
||||||
run: make nightly
|
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
|
||||||
- name: Odin run
|
# not link with libunwind bundled with LLVM but link with libunwind on the system.
|
||||||
run: ./odin run examples/demo
|
run: CXXFLAGS="-L/usr/lib/system -L/usr/lib" make nightly
|
||||||
- name: Copy artifacts
|
- name: Bundle
|
||||||
run: |
|
run: |
|
||||||
mkdir dist
|
FILE="odin-macos-amd64-nightly+$(date -I)"
|
||||||
cp odin dist
|
mkdir $FILE
|
||||||
cp LICENSE dist
|
cp odin $FILE
|
||||||
cp -r shared dist
|
cp LICENSE $FILE
|
||||||
cp -r core dist
|
cp -r shared $FILE
|
||||||
cp -r vendor dist
|
cp -r base $FILE
|
||||||
cp -r examples dist
|
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: |
|
||||||
|
FILE="odin-macos-amd64-nightly+$(date -I)"
|
||||||
|
$FILE/odin run examples/demo
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: macos_artifacts
|
name: macos_artifacts
|
||||||
path: dist
|
path: dist.tar.gz
|
||||||
|
build_macos_arm:
|
||||||
|
name: MacOS ARM Build
|
||||||
|
if: github.repository == 'odin-lang/Odin'
|
||||||
|
runs-on: macos-14 # ARM machine
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Download LLVM and setup PATH
|
||||||
|
run: |
|
||||||
|
brew install llvm@18 dylibbundler
|
||||||
|
echo "/opt/homebrew/opt/llvm@18/bin" >> $GITHUB_PATH
|
||||||
|
- name: build odin
|
||||||
|
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
|
||||||
|
# not link with libunwind bundled with LLVM but link with libunwind on the system.
|
||||||
|
run: CXXFLAGS="-L/usr/lib/system -L/usr/lib" make nightly
|
||||||
|
- name: Bundle
|
||||||
|
run: |
|
||||||
|
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: |
|
||||||
|
FILE="odin-macos-arm64-nightly+$(date -I)"
|
||||||
|
$FILE/odin run examples/demo
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: macos_arm_artifacts
|
||||||
|
path: dist.tar.gz
|
||||||
upload_b2:
|
upload_b2:
|
||||||
runs-on: [ubuntu-latest]
|
runs-on: [ubuntu-latest]
|
||||||
needs: [build_windows, build_macos, build_ubuntu]
|
needs: [build_windows, build_macos, build_macos_arm, build_linux]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v2
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.8.x'
|
python-version: '3.8.x'
|
||||||
|
|
||||||
- name: Install B2 CLI
|
- name: Install B2 SDK
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install --upgrade b2
|
pip install --upgrade b2sdk
|
||||||
|
|
||||||
- name: Display Python version
|
- name: Display Python version
|
||||||
run: python -c "import sys; print(sys.version)"
|
run: python -c "import sys; print(sys.version)"
|
||||||
|
|
||||||
- name: Download Windows artifacts
|
- name: Download Windows artifacts
|
||||||
uses: actions/download-artifact@v1
|
|
||||||
|
uses: actions/download-artifact@v4.1.7
|
||||||
with:
|
with:
|
||||||
name: windows_artifacts
|
name: windows_artifacts
|
||||||
|
path: windows_artifacts
|
||||||
|
|
||||||
- name: Download Ubuntu artifacts
|
- name: Download Ubuntu artifacts
|
||||||
uses: actions/download-artifact@v1
|
uses: actions/download-artifact@v4.1.7
|
||||||
with:
|
with:
|
||||||
name: ubuntu_artifacts
|
name: linux_artifacts
|
||||||
|
path: linux_artifacts
|
||||||
|
|
||||||
- name: Download macOS artifacts
|
- name: Download macOS artifacts
|
||||||
uses: actions/download-artifact@v1
|
uses: actions/download-artifact@v4.1.7
|
||||||
with:
|
with:
|
||||||
name: macos_artifacts
|
name: macos_artifacts
|
||||||
|
path: macos_artifacts
|
||||||
|
|
||||||
|
- name: Download macOS arm artifacts
|
||||||
|
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
|
- name: Create archives and upload
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -134,23 +214,10 @@ jobs:
|
|||||||
BUCKET: ${{ secrets.B2_BUCKET }}
|
BUCKET: ${{ secrets.B2_BUCKET }}
|
||||||
DAYS_TO_KEEP: ${{ secrets.B2_DAYS_TO_KEEP }}
|
DAYS_TO_KEEP: ${{ secrets.B2_DAYS_TO_KEEP }}
|
||||||
run: |
|
run: |
|
||||||
echo Authorizing B2 account
|
file linux_artifacts/dist.tar.gz
|
||||||
b2 authorize-account "$APPID" "$APPKEY"
|
python3 ci/nightly.py artifact windows-amd64 windows_artifacts/
|
||||||
|
python3 ci/nightly.py artifact linux-amd64 linux_artifacts/dist.tar.gz
|
||||||
echo Uploading artifcates to B2
|
python3 ci/nightly.py artifact macos-amd64 macos_artifacts/dist.tar.gz
|
||||||
chmod +x ./ci/upload_create_nightly.sh
|
python3 ci/nightly.py artifact macos-arm64 macos_arm_artifacts/dist.tar.gz
|
||||||
./ci/upload_create_nightly.sh "$BUCKET" windows-amd64 windows_artifacts/
|
python3 ci/nightly.py prune
|
||||||
./ci/upload_create_nightly.sh "$BUCKET" ubuntu-amd64 ubuntu_artifacts/
|
python3 ci/nightly.py json
|
||||||
./ci/upload_create_nightly.sh "$BUCKET" macos-amd64 macos_artifacts/
|
|
||||||
|
|
||||||
echo Deleting old artifacts in B2
|
|
||||||
python3 ci/delete_old_binaries.py "$BUCKET" "$DAYS_TO_KEEP"
|
|
||||||
|
|
||||||
echo Creating nightly.json
|
|
||||||
python3 ci/create_nightly_json.py "$BUCKET" > nightly.json
|
|
||||||
|
|
||||||
echo Uploading nightly.json
|
|
||||||
b2 upload-file "$BUCKET" nightly.json nightly.json
|
|
||||||
|
|
||||||
echo Clear B2 account info
|
|
||||||
b2 clear-account
|
|
||||||
|
|||||||
+10
-5
@@ -17,15 +17,12 @@
|
|||||||
[Rr]eleases/
|
[Rr]eleases/
|
||||||
x64/
|
x64/
|
||||||
x86/
|
x86/
|
||||||
|
!/core/simd/x86
|
||||||
bld/
|
bld/
|
||||||
[Bb]in/
|
[Bb]in/
|
||||||
[Oo]bj/
|
[Oo]bj/
|
||||||
[Ll]og/
|
[Ll]og/
|
||||||
![Cc]ore/[Ll]og/
|
![Cc]ore/[Ll]og/
|
||||||
tests/documentation/verify/
|
|
||||||
tests/documentation/all.odin-doc
|
|
||||||
tests/internal/test_map
|
|
||||||
tests/internal/test_rtti
|
|
||||||
# Visual Studio 2015 cache/options directory
|
# Visual Studio 2015 cache/options directory
|
||||||
.vs/
|
.vs/
|
||||||
# Visual Studio Code options directory
|
# Visual Studio Code options directory
|
||||||
@@ -269,11 +266,14 @@ bin/
|
|||||||
*.exe
|
*.exe
|
||||||
*.obj
|
*.obj
|
||||||
*.pdb
|
*.pdb
|
||||||
|
*.res
|
||||||
|
desktop.ini
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
# - Linux/MacOS
|
# - Linux/MacOS
|
||||||
odin
|
odin
|
||||||
!odin/
|
!odin/
|
||||||
odin.dSYM
|
**/*.dSYM
|
||||||
*.bin
|
*.bin
|
||||||
demo.bin
|
demo.bin
|
||||||
libLLVM*.so*
|
libLLVM*.so*
|
||||||
@@ -290,3 +290,8 @@ shared/
|
|||||||
examples/bug/
|
examples/bug/
|
||||||
build.sh
|
build.sh
|
||||||
!core/debug/
|
!core/debug/
|
||||||
|
|
||||||
|
# RAD debugger project file
|
||||||
|
*.raddbg
|
||||||
|
|
||||||
|
misc/featuregen/featuregen
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2016-2022 Ginger Bill. All rights reserved.
|
Copyright (c) 2016-2024 Ginger Bill. All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are met:
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|||||||
BIN
Binary file not shown.
@@ -1,4 +1,4 @@
|
|||||||
all: debug
|
all: default
|
||||||
|
|
||||||
demo:
|
demo:
|
||||||
./odin run examples/demo/demo.odin -file
|
./odin run examples/demo/demo.odin -file
|
||||||
@@ -6,12 +6,18 @@ demo:
|
|||||||
report:
|
report:
|
||||||
./odin report
|
./odin report
|
||||||
|
|
||||||
|
default:
|
||||||
|
PROGRAM=make ./build_odin.sh # debug
|
||||||
|
|
||||||
debug:
|
debug:
|
||||||
./build_odin.sh debug
|
./build_odin.sh debug
|
||||||
|
|
||||||
release:
|
release:
|
||||||
./build_odin.sh release
|
./build_odin.sh release
|
||||||
|
|
||||||
|
release-native:
|
||||||
|
./build_odin.sh release-native
|
||||||
|
|
||||||
release_native:
|
release_native:
|
||||||
./build_odin.sh release-native
|
./build_odin.sh release-native
|
||||||
|
|
||||||
|
|||||||
@@ -76,9 +76,9 @@ Answers to common questions about Odin.
|
|||||||
|
|
||||||
Documentation for all the official packages part of the [core](https://pkg.odin-lang.org/core/) and [vendor](https://pkg.odin-lang.org/vendor/) library collections.
|
Documentation for all the official packages part of the [core](https://pkg.odin-lang.org/core/) and [vendor](https://pkg.odin-lang.org/vendor/) library collections.
|
||||||
|
|
||||||
#### [The Odin Wiki](https://github.com/odin-lang/Odin/wiki)
|
#### [Odin Documentation](https://odin-lang.org/docs/)
|
||||||
|
|
||||||
A wiki maintained by the Odin community.
|
Documentation for the Odin language itself.
|
||||||
|
|
||||||
#### [Odin Discord](https://discord.gg/sVBPHEv)
|
#### [Odin Discord](https://discord.gg/sVBPHEv)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
// This is purely for documentation
|
// This is purely for documentation
|
||||||
package builtin
|
package builtin
|
||||||
|
|
||||||
|
import "base:runtime"
|
||||||
|
|
||||||
nil :: nil
|
nil :: nil
|
||||||
false :: 0!=0
|
false :: 0!=0
|
||||||
true :: 0==0
|
true :: 0==0
|
||||||
@@ -110,7 +112,7 @@ typeid_of :: proc($T: typeid) -> typeid ---
|
|||||||
swizzle :: proc(x: [N]T, indices: ..int) -> [len(indices)]T ---
|
swizzle :: proc(x: [N]T, indices: ..int) -> [len(indices)]T ---
|
||||||
|
|
||||||
complex :: proc(real, imag: Float) -> Complex_Type ---
|
complex :: proc(real, imag: Float) -> Complex_Type ---
|
||||||
quaternion :: proc(real, imag, jmag, kmag: Float) -> Quaternion_Type ---
|
quaternion :: proc(imag, jmag, kmag, real: Float) -> Quaternion_Type --- // fields must be named
|
||||||
real :: proc(value: Complex_Or_Quaternion) -> Float ---
|
real :: proc(value: Complex_Or_Quaternion) -> Float ---
|
||||||
imag :: proc(value: Complex_Or_Quaternion) -> Float ---
|
imag :: proc(value: Complex_Or_Quaternion) -> Float ---
|
||||||
jmag :: proc(value: Quaternion) -> Float ---
|
jmag :: proc(value: Quaternion) -> Float ---
|
||||||
@@ -126,3 +128,5 @@ clamp :: proc(value, minimum, maximum: T) -> T ---
|
|||||||
|
|
||||||
soa_zip :: proc(slices: ...) -> #soa[]Struct ---
|
soa_zip :: proc(slices: ...) -> #soa[]Struct ---
|
||||||
soa_unzip :: proc(value: $S/#soa[]$E) -> (slices: ...) ---
|
soa_unzip :: proc(value: $S/#soa[]$E) -> (slices: ...) ---
|
||||||
|
|
||||||
|
unreachable :: proc() -> ! ---
|
||||||
@@ -1,10 +1,18 @@
|
|||||||
// This is purely for documentation
|
// This is purely for documentation
|
||||||
//+build ignore
|
#+build ignore
|
||||||
package intrinsics
|
package intrinsics
|
||||||
|
|
||||||
|
import "base:runtime"
|
||||||
|
|
||||||
// Package-Related
|
// Package-Related
|
||||||
is_package_imported :: proc(package_name: string) -> bool ---
|
is_package_imported :: proc(package_name: string) -> bool ---
|
||||||
|
|
||||||
|
// Matrix Related Procedures
|
||||||
|
transpose :: proc(m: $T/matrix[$R, $C]$E) -> matrix[C, R]E ---
|
||||||
|
outer_product :: proc(a: $A/[$X]$E, b: $B/[$Y]E) -> matrix[X, Y]E ---
|
||||||
|
hadamard_product :: proc(a, b: $T/matrix[$R, $C]$E) -> T ---
|
||||||
|
matrix_flatten :: proc(m: $T/matrix[$R, $C]$E) -> [R*C]E ---
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
soa_struct :: proc($N: int, $T: typeid) -> type/#soa[N]T
|
soa_struct :: proc($N: int, $T: typeid) -> type/#soa[N]T
|
||||||
|
|
||||||
@@ -32,9 +40,12 @@ count_leading_zeros :: proc(x: $T) -> T where type_is_integer(T) || type_is_sim
|
|||||||
reverse_bits :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
|
reverse_bits :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
|
||||||
byte_swap :: proc(x: $T) -> T where type_is_integer(T) || type_is_float(T) ---
|
byte_swap :: proc(x: $T) -> T where type_is_integer(T) || type_is_float(T) ---
|
||||||
|
|
||||||
overflow_add :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok ---
|
overflow_add :: proc(lhs, rhs: $T) -> (T, bool) where type_is_integer(T) #optional_ok ---
|
||||||
overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok ---
|
overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) where type_is_integer(T) #optional_ok ---
|
||||||
overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok ---
|
overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) where type_is_integer(T) #optional_ok ---
|
||||||
|
|
||||||
|
saturating_add :: proc(lhs, rhs: $T) -> T where type_is_integer(T) ---
|
||||||
|
saturating_sub :: proc(lhs, rhs: $T) -> T where type_is_integer(T) ---
|
||||||
|
|
||||||
sqrt :: proc(x: $T) -> T where type_is_float(T) || (type_is_simd_vector(T) && type_is_float(type_elem_type(T))) ---
|
sqrt :: proc(x: $T) -> T where type_is_float(T) || (type_is_simd_vector(T) && type_is_float(type_elem_type(T))) ---
|
||||||
|
|
||||||
@@ -63,10 +74,12 @@ prefetch_write_instruction :: proc(address: rawptr, #const locality: i32 /* 0..=
|
|||||||
prefetch_write_data :: proc(address: rawptr, #const locality: i32 /* 0..=3 */) ---
|
prefetch_write_data :: proc(address: rawptr, #const locality: i32 /* 0..=3 */) ---
|
||||||
|
|
||||||
// Compiler Hints
|
// Compiler Hints
|
||||||
expect :: proc(val, expected_val: T) -> T ---
|
expect :: proc(val, expected_val: $T) -> T ---
|
||||||
|
|
||||||
// Linux and Darwin Only
|
// Linux and Darwin Only
|
||||||
syscall :: proc(id: uintptr, args: ..uintptr) -> uintptr ---
|
syscall :: proc(id: uintptr, args: ..uintptr) -> uintptr ---
|
||||||
|
// FreeBSD, NetBSD, et cetera
|
||||||
|
syscall_bsd :: proc(id: uintptr, args: ..uintptr) -> (uintptr, bool) ---
|
||||||
|
|
||||||
|
|
||||||
// Atomics
|
// Atomics
|
||||||
@@ -161,10 +174,23 @@ type_is_matrix :: proc($T: typeid) -> bool ---
|
|||||||
|
|
||||||
type_has_nil :: proc($T: typeid) -> bool ---
|
type_has_nil :: proc($T: typeid) -> bool ---
|
||||||
|
|
||||||
type_is_specialization_of :: proc($T, $S: typeid) -> bool ---
|
type_is_matrix_row_major :: proc($T: typeid) -> bool where type_is_matrix(T) ---
|
||||||
type_is_variant_of :: proc($U, $V: typeid) -> bool where type_is_union(U) ---
|
type_is_matrix_column_major :: proc($T: typeid) -> bool where type_is_matrix(T) ---
|
||||||
|
|
||||||
type_has_field :: proc($T: typeid, $name: string) -> bool ---
|
type_is_specialization_of :: proc($T, $S: typeid) -> bool ---
|
||||||
|
|
||||||
|
type_is_variant_of :: proc($U, $V: typeid) -> bool where type_is_union(U) ---
|
||||||
|
type_union_tag_type :: proc($T: typeid) -> typeid where type_is_union(T) ---
|
||||||
|
type_union_tag_offset :: proc($T: typeid) -> uintptr where type_is_union(T) ---
|
||||||
|
type_union_base_tag_value :: proc($T: typeid) -> int where type_is_union(U) ---
|
||||||
|
type_union_variant_count :: proc($T: typeid) -> int where type_is_union(T) ---
|
||||||
|
type_variant_type_of :: proc($T: typeid, $index: int) -> typeid where type_is_union(T) ---
|
||||||
|
type_variant_index_of :: proc($U, $V: typeid) -> int where type_is_union(U) ---
|
||||||
|
|
||||||
|
type_bit_set_elem_type :: proc($T: typeid) -> typeid where type_is_bit_set(T) ---
|
||||||
|
type_bit_set_underlying_type :: proc($T: typeid) -> typeid where type_is_bit_set(T) ---
|
||||||
|
|
||||||
|
type_has_field :: proc($T: typeid, $name: string) -> bool ---
|
||||||
type_field_type :: proc($T: typeid, $name: string) -> typeid ---
|
type_field_type :: proc($T: typeid, $name: string) -> typeid ---
|
||||||
|
|
||||||
type_proc_parameter_count :: proc($T: typeid) -> int where type_is_proc(T) ---
|
type_proc_parameter_count :: proc($T: typeid) -> int where type_is_proc(T) ---
|
||||||
@@ -173,7 +199,8 @@ type_proc_return_count :: proc($T: typeid) -> int where type_is_proc(T) ---
|
|||||||
type_proc_parameter_type :: proc($T: typeid, index: int) -> typeid where type_is_proc(T) ---
|
type_proc_parameter_type :: proc($T: typeid, index: int) -> typeid where type_is_proc(T) ---
|
||||||
type_proc_return_type :: proc($T: typeid, index: int) -> typeid where type_is_proc(T) ---
|
type_proc_return_type :: proc($T: typeid, index: int) -> typeid where type_is_proc(T) ---
|
||||||
|
|
||||||
type_struct_field_count :: proc($T: typeid) -> int where type_is_struct(T) ---
|
type_struct_field_count :: proc($T: typeid) -> int where type_is_struct(T) ---
|
||||||
|
type_struct_has_implicit_padding :: proc($T: typeid) -> bool where type_is_struct(T) ---
|
||||||
|
|
||||||
type_polymorphic_record_parameter_count :: proc($T: typeid) -> typeid ---
|
type_polymorphic_record_parameter_count :: proc($T: typeid) -> typeid ---
|
||||||
type_polymorphic_record_parameter_value :: proc($T: typeid, index: int) -> $V ---
|
type_polymorphic_record_parameter_value :: proc($T: typeid, index: int) -> $V ---
|
||||||
@@ -194,14 +221,21 @@ 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_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_merge :: proc($U, $V: typeid) -> typeid where type_is_union(U), type_is_union(V) ---
|
||||||
|
|
||||||
|
type_has_shared_fields :: proc($U, $V: typeid) -> bool where type_is_struct(U), type_is_struct(V) ---
|
||||||
|
|
||||||
constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
|
constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
|
||||||
|
|
||||||
|
constant_log2 :: proc($v: $T) -> T where type_is_integer(T) ---
|
||||||
|
|
||||||
// SIMD related
|
// SIMD related
|
||||||
simd_add :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
simd_add :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||||
simd_sub :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
simd_sub :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||||
simd_mul :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
simd_mul :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||||
simd_div :: proc(a, b: #simd[N]T) -> #simd[N]T where type_is_float(T) ---
|
simd_div :: proc(a, b: #simd[N]T) -> #simd[N]T where type_is_float(T) ---
|
||||||
|
|
||||||
|
simd_saturating_add :: proc(a, b: #simd[N]T) -> #simd[N]T where type_is_integer(T) ---
|
||||||
|
simd_saturating_sub :: proc(a, b: #simd[N]T) -> #simd[N]T where type_is_integer(T) ---
|
||||||
|
|
||||||
// Keeps Odin's Behaviour
|
// Keeps Odin's Behaviour
|
||||||
// (x << y) if y <= mask else 0
|
// (x << y) if y <= mask else 0
|
||||||
simd_shl :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T ---
|
simd_shl :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T ---
|
||||||
@@ -212,9 +246,6 @@ simd_shr :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T ---
|
|||||||
simd_shl_masked :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T ---
|
simd_shl_masked :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T ---
|
||||||
simd_shr_masked :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T ---
|
simd_shr_masked :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T ---
|
||||||
|
|
||||||
simd_add_sat :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
|
||||||
simd_sub_sat :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
|
||||||
|
|
||||||
simd_bit_and :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
simd_bit_and :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||||
simd_bit_or :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
simd_bit_or :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||||
simd_bit_xor :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
simd_bit_xor :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||||
@@ -243,13 +274,28 @@ simd_lanes_ge :: proc(a, b: #simd[N]T) -> #simd[N]Integer ---
|
|||||||
simd_extract :: proc(a: #simd[N]T, idx: uint) -> T ---
|
simd_extract :: proc(a: #simd[N]T, idx: uint) -> T ---
|
||||||
simd_replace :: proc(a: #simd[N]T, idx: uint, elem: T) -> #simd[N]T ---
|
simd_replace :: proc(a: #simd[N]T, idx: uint, elem: T) -> #simd[N]T ---
|
||||||
|
|
||||||
simd_reduce_add_ordered :: proc(a: #simd[N]T) -> T ---
|
simd_reduce_add_ordered :: proc(a: #simd[N]T) -> T where type_is_integer(T) || type_is_float(T)---
|
||||||
simd_reduce_mul_ordered :: proc(a: #simd[N]T) -> T ---
|
simd_reduce_mul_ordered :: proc(a: #simd[N]T) -> T where type_is_integer(T) || type_is_float(T)---
|
||||||
simd_reduce_min :: proc(a: #simd[N]T) -> T ---
|
simd_reduce_min :: proc(a: #simd[N]T) -> T where type_is_integer(T) || type_is_float(T)---
|
||||||
simd_reduce_max :: proc(a: #simd[N]T) -> T ---
|
simd_reduce_max :: proc(a: #simd[N]T) -> T where type_is_integer(T) || type_is_float(T)---
|
||||||
simd_reduce_and :: proc(a: #simd[N]T) -> T ---
|
simd_reduce_and :: proc(a: #simd[N]T) -> T where type_is_integer(T) || type_is_float(T)---
|
||||||
simd_reduce_or :: proc(a: #simd[N]T) -> T ---
|
simd_reduce_or :: proc(a: #simd[N]T) -> T where type_is_integer(T) || type_is_float(T)---
|
||||||
simd_reduce_xor :: proc(a: #simd[N]T) -> T ---
|
simd_reduce_xor :: proc(a: #simd[N]T) -> T where type_is_integer(T) || type_is_float(T)---
|
||||||
|
|
||||||
|
simd_reduce_any :: proc(a: #simd[N]T) -> T where type_is_boolean(T) ---
|
||||||
|
simd_reduce_all :: proc(a: #simd[N]T) -> T where type_is_boolean(T) ---
|
||||||
|
|
||||||
|
|
||||||
|
simd_gather :: proc(ptr: #simd[N]rawptr, val: #simd[N]T, mask: #simd[N]U) -> #simd[N]T where type_is_integer(U) || type_is_boolean(U) ---
|
||||||
|
simd_scatter :: proc(ptr: #simd[N]rawptr, val: #simd[N]T, mask: #simd[N]U) where type_is_integer(U) || type_is_boolean(U) ---
|
||||||
|
|
||||||
|
simd_masked_load :: proc(ptr: rawptr, val: #simd[N]T, mask: #simd[N]U) -> #simd[N]T where type_is_integer(U) || type_is_boolean(U) ---
|
||||||
|
simd_masked_store :: proc(ptr: rawptr, val: #simd[N]T, mask: #simd[N]U) where type_is_integer(U) || type_is_boolean(U) ---
|
||||||
|
|
||||||
|
simd_masked_expand_load :: proc(ptr: rawptr, val: #simd[N]T, mask: #simd[N]U) -> #simd[N]T where type_is_integer(U) || type_is_boolean(U) ---
|
||||||
|
simd_masked_compress_store :: proc(ptr: rawptr, val: #simd[N]T, mask: #simd[N]U) where type_is_integer(U) || type_is_boolean(U) ---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
simd_shuffle :: proc(a, b: #simd[N]T, indices: ..int) -> #simd[len(indices)]T ---
|
simd_shuffle :: proc(a, b: #simd[N]T, indices: ..int) -> #simd[len(indices)]T ---
|
||||||
simd_select :: proc(cond: #simd[N]boolean_or_integer, true, false: #simd[N]T) -> #simd[N]T ---
|
simd_select :: proc(cond: #simd[N]boolean_or_integer, true, false: #simd[N]T) -> #simd[N]T ---
|
||||||
@@ -263,12 +309,22 @@ simd_nearest :: proc(a: #simd[N]any_float) -> #simd[N]any_float ---
|
|||||||
|
|
||||||
simd_to_bits :: proc(v: #simd[N]T) -> #simd[N]Integer where size_of(T) == size_of(Integer), type_is_unsigned(Integer) ---
|
simd_to_bits :: proc(v: #simd[N]T) -> #simd[N]Integer where size_of(T) == size_of(Integer), type_is_unsigned(Integer) ---
|
||||||
|
|
||||||
// equivalent a swizzle with descending indices, e.g. reserve(a, 3, 2, 1, 0)
|
// equivalent to a swizzle with descending indices, e.g. reserve(a, 3, 2, 1, 0)
|
||||||
simd_reverse :: proc(a: #simd[N]T) -> #simd[N]T ---
|
simd_lanes_reverse :: proc(a: #simd[N]T) -> #simd[N]T ---
|
||||||
|
|
||||||
simd_rotate_left :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
|
simd_lanes_rotate_left :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
|
||||||
simd_rotate_right :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
|
simd_lanes_rotate_right :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
|
||||||
|
|
||||||
|
// Checks if the current target supports the given target features.
|
||||||
|
//
|
||||||
|
// Takes a constant comma-seperated string (eg: "sha512,sse4.1"), or a procedure type which has either
|
||||||
|
// `@(require_target_feature)` or `@(enable_target_feature)` as its input and returns a boolean indicating
|
||||||
|
// if all listed features are supported.
|
||||||
|
has_target_feature :: proc($test: $T) -> bool where type_is_string(T) || type_is_proc(T) ---
|
||||||
|
|
||||||
|
|
||||||
|
// Returns the value of the procedure where `x` must be a call expression
|
||||||
|
procedure_of :: proc(x: $T) -> T where type_is_proc(T) ---
|
||||||
|
|
||||||
// WASM targets only
|
// WASM targets only
|
||||||
wasm_memory_grow :: proc(index, delta: uintptr) -> int ---
|
wasm_memory_grow :: proc(index, delta: uintptr) -> int ---
|
||||||
@@ -280,7 +336,9 @@ wasm_memory_size :: proc(index: uintptr) -> int ---
|
|||||||
// 0 - indicates that the thread blocked and then was woken up
|
// 0 - indicates that the thread blocked and then was woken up
|
||||||
// 1 - the loaded value from `ptr` did not match `expected`, the thread did not block
|
// 1 - the loaded value from `ptr` did not match `expected`, the thread did not block
|
||||||
// 2 - the thread blocked, but the timeout
|
// 2 - the thread blocked, but the timeout
|
||||||
|
@(require_target_feature="atomics")
|
||||||
wasm_memory_atomic_wait32 :: proc(ptr: ^u32, expected: u32, timeout_ns: i64) -> u32 ---
|
wasm_memory_atomic_wait32 :: proc(ptr: ^u32, expected: u32, timeout_ns: i64) -> u32 ---
|
||||||
|
@(require_target_feature="atomics")
|
||||||
wasm_memory_atomic_notify32 :: proc(ptr: ^u32, waiters: u32) -> (waiters_woken_up: u32) ---
|
wasm_memory_atomic_notify32 :: proc(ptr: ^u32, waiters: u32) -> (waiters_woken_up: u32) ---
|
||||||
|
|
||||||
// x86 Targets (i386, amd64)
|
// x86 Targets (i386, amd64)
|
||||||
@@ -18,9 +18,10 @@
|
|||||||
// This could change at a later date if the all these data structures are
|
// This could change at a later date if the all these data structures are
|
||||||
// implemented within the compiler rather than in this "preload" file
|
// implemented within the compiler rather than in this "preload" file
|
||||||
//
|
//
|
||||||
|
#+no-instrumentation
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
// NOTE(bill): This must match the compiler's
|
// NOTE(bill): This must match the compiler's
|
||||||
Calling_Convention :: enum u8 {
|
Calling_Convention :: enum u8 {
|
||||||
@@ -65,7 +66,7 @@ Type_Info_Named :: struct {
|
|||||||
name: string,
|
name: string,
|
||||||
base: ^Type_Info,
|
base: ^Type_Info,
|
||||||
pkg: string,
|
pkg: string,
|
||||||
loc: Source_Code_Location,
|
loc: ^Source_Code_Location,
|
||||||
}
|
}
|
||||||
Type_Info_Integer :: struct {signed: bool, endianness: Platform_Endianness}
|
Type_Info_Integer :: struct {signed: bool, endianness: Platform_Endianness}
|
||||||
Type_Info_Rune :: struct {}
|
Type_Info_Rune :: struct {}
|
||||||
@@ -111,23 +112,32 @@ Type_Info_Parameters :: struct { // Only used for procedures parameters and resu
|
|||||||
}
|
}
|
||||||
Type_Info_Tuple :: Type_Info_Parameters // Will be removed eventually
|
Type_Info_Tuple :: Type_Info_Parameters // Will be removed eventually
|
||||||
|
|
||||||
Type_Info_Struct :: struct {
|
Type_Info_Struct_Flags :: distinct bit_set[Type_Info_Struct_Flag; u8]
|
||||||
types: []^Type_Info,
|
Type_Info_Struct_Flag :: enum u8 {
|
||||||
names: []string,
|
packed = 0,
|
||||||
offsets: []uintptr,
|
raw_union = 1,
|
||||||
usings: []bool,
|
no_copy = 2,
|
||||||
tags: []string,
|
align = 3,
|
||||||
is_packed: bool,
|
}
|
||||||
is_raw_union: bool,
|
|
||||||
is_no_copy: bool,
|
|
||||||
custom_align: bool,
|
|
||||||
|
|
||||||
equal: Equal_Proc, // set only when the struct has .Comparable set but does not have .Simple_Compare set
|
Type_Info_Struct :: struct {
|
||||||
|
// Slice these with `field_count`
|
||||||
|
types: [^]^Type_Info `fmt:"v,field_count"`,
|
||||||
|
names: [^]string `fmt:"v,field_count"`,
|
||||||
|
offsets: [^]uintptr `fmt:"v,field_count"`,
|
||||||
|
usings: [^]bool `fmt:"v,field_count"`,
|
||||||
|
tags: [^]string `fmt:"v,field_count"`,
|
||||||
|
|
||||||
|
field_count: i32,
|
||||||
|
|
||||||
|
flags: Type_Info_Struct_Flags,
|
||||||
|
|
||||||
// These are only set iff this structure is an SOA structure
|
// These are only set iff this structure is an SOA structure
|
||||||
soa_kind: Type_Info_Struct_Soa_Kind,
|
soa_kind: Type_Info_Struct_Soa_Kind,
|
||||||
|
soa_len: i32,
|
||||||
soa_base_type: ^Type_Info,
|
soa_base_type: ^Type_Info,
|
||||||
soa_len: int,
|
|
||||||
|
equal: Equal_Proc, // set only when the struct has .Comparable set but does not have .Simple_Compare set
|
||||||
}
|
}
|
||||||
Type_Info_Union :: struct {
|
Type_Info_Union :: struct {
|
||||||
variants: []^Type_Info,
|
variants: []^Type_Info,
|
||||||
@@ -141,9 +151,9 @@ Type_Info_Union :: struct {
|
|||||||
shared_nil: bool,
|
shared_nil: bool,
|
||||||
}
|
}
|
||||||
Type_Info_Enum :: struct {
|
Type_Info_Enum :: struct {
|
||||||
base: ^Type_Info,
|
base: ^Type_Info,
|
||||||
names: []string,
|
names: []string,
|
||||||
values: []Type_Info_Enum_Value,
|
values: []Type_Info_Enum_Value,
|
||||||
}
|
}
|
||||||
Type_Info_Map :: struct {
|
Type_Info_Map :: struct {
|
||||||
key: ^Type_Info,
|
key: ^Type_Info,
|
||||||
@@ -161,14 +171,6 @@ Type_Info_Simd_Vector :: struct {
|
|||||||
elem_size: int,
|
elem_size: int,
|
||||||
count: int,
|
count: int,
|
||||||
}
|
}
|
||||||
Type_Info_Relative_Pointer :: struct {
|
|
||||||
pointer: ^Type_Info, // ^T
|
|
||||||
base_integer: ^Type_Info,
|
|
||||||
}
|
|
||||||
Type_Info_Relative_Multi_Pointer :: struct {
|
|
||||||
pointer: ^Type_Info, // [^]T
|
|
||||||
base_integer: ^Type_Info,
|
|
||||||
}
|
|
||||||
Type_Info_Matrix :: struct {
|
Type_Info_Matrix :: struct {
|
||||||
elem: ^Type_Info,
|
elem: ^Type_Info,
|
||||||
elem_size: int,
|
elem_size: int,
|
||||||
@@ -176,10 +178,23 @@ Type_Info_Matrix :: struct {
|
|||||||
row_count: int,
|
row_count: int,
|
||||||
column_count: int,
|
column_count: int,
|
||||||
// Total element count = column_count * elem_stride
|
// Total element count = column_count * elem_stride
|
||||||
|
layout: enum u8 {
|
||||||
|
Column_Major, // array of column vectors
|
||||||
|
Row_Major, // array of row vectors
|
||||||
|
},
|
||||||
}
|
}
|
||||||
Type_Info_Soa_Pointer :: struct {
|
Type_Info_Soa_Pointer :: struct {
|
||||||
elem: ^Type_Info,
|
elem: ^Type_Info,
|
||||||
}
|
}
|
||||||
|
Type_Info_Bit_Field :: struct {
|
||||||
|
backing_type: ^Type_Info,
|
||||||
|
names: [^]string `fmt:"v,field_count"`,
|
||||||
|
types: [^]^Type_Info `fmt:"v,field_count"`,
|
||||||
|
bit_sizes: [^]uintptr `fmt:"v,field_count"`,
|
||||||
|
bit_offsets: [^]uintptr `fmt:"v,field_count"`,
|
||||||
|
tags: [^]string `fmt:"v,field_count"`,
|
||||||
|
field_count: int,
|
||||||
|
}
|
||||||
|
|
||||||
Type_Info_Flag :: enum u8 {
|
Type_Info_Flag :: enum u8 {
|
||||||
Comparable = 0,
|
Comparable = 0,
|
||||||
@@ -218,10 +233,9 @@ Type_Info :: struct {
|
|||||||
Type_Info_Map,
|
Type_Info_Map,
|
||||||
Type_Info_Bit_Set,
|
Type_Info_Bit_Set,
|
||||||
Type_Info_Simd_Vector,
|
Type_Info_Simd_Vector,
|
||||||
Type_Info_Relative_Pointer,
|
|
||||||
Type_Info_Relative_Multi_Pointer,
|
|
||||||
Type_Info_Matrix,
|
Type_Info_Matrix,
|
||||||
Type_Info_Soa_Pointer,
|
Type_Info_Soa_Pointer,
|
||||||
|
Type_Info_Bit_Field,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,25 +265,24 @@ Typeid_Kind :: enum u8 {
|
|||||||
Map,
|
Map,
|
||||||
Bit_Set,
|
Bit_Set,
|
||||||
Simd_Vector,
|
Simd_Vector,
|
||||||
Relative_Pointer,
|
|
||||||
Relative_Multi_Pointer,
|
|
||||||
Matrix,
|
Matrix,
|
||||||
Soa_Pointer,
|
Soa_Pointer,
|
||||||
|
Bit_Field,
|
||||||
}
|
}
|
||||||
#assert(len(Typeid_Kind) < 32)
|
#assert(len(Typeid_Kind) < 32)
|
||||||
|
|
||||||
// Typeid_Bit_Field :: bit_field #align(align_of(uintptr)) {
|
Typeid_Bit_Field :: bit_field uintptr {
|
||||||
// index: 8*size_of(uintptr) - 8,
|
index: uintptr | 8*size_of(uintptr) - 8,
|
||||||
// kind: 5, // Typeid_Kind
|
kind: Typeid_Kind | 5, // Typeid_Kind
|
||||||
// named: 1,
|
named: bool | 1,
|
||||||
// special: 1, // signed, cstring, etc
|
special: bool | 1, // signed, cstring, etc
|
||||||
// reserved: 1,
|
reserved: bool | 1,
|
||||||
// }
|
}
|
||||||
// #assert(size_of(Typeid_Bit_Field) == size_of(uintptr));
|
#assert(size_of(Typeid_Bit_Field) == size_of(uintptr))
|
||||||
|
|
||||||
// NOTE(bill): only the ones that are needed (not all types)
|
// NOTE(bill): only the ones that are needed (not all types)
|
||||||
// This will be set by the compiler
|
// This will be set by the compiler
|
||||||
type_table: []Type_Info
|
type_table: []^Type_Info
|
||||||
|
|
||||||
args__: []cstring
|
args__: []cstring
|
||||||
|
|
||||||
@@ -284,6 +297,8 @@ when ODIN_OS == .Windows {
|
|||||||
Thread_Detach = 3,
|
Thread_Detach = 3,
|
||||||
}
|
}
|
||||||
dll_forward_reason: DLL_Forward_Reason
|
dll_forward_reason: DLL_Forward_Reason
|
||||||
|
|
||||||
|
dll_instance: rawptr
|
||||||
}
|
}
|
||||||
|
|
||||||
// IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it)
|
// IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it)
|
||||||
@@ -295,6 +310,14 @@ Source_Code_Location :: struct {
|
|||||||
procedure: string,
|
procedure: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Used by the built-in directory `#load_directory(path: string) -> []Load_Directory_File`
|
||||||
|
*/
|
||||||
|
Load_Directory_File :: struct {
|
||||||
|
name: string,
|
||||||
|
data: []byte, // immutable data
|
||||||
|
}
|
||||||
|
|
||||||
Assertion_Failure_Proc :: #type proc(prefix, message: string, loc: Source_Code_Location) -> !
|
Assertion_Failure_Proc :: #type proc(prefix, message: string, loc: Source_Code_Location) -> !
|
||||||
|
|
||||||
// Allocation Stuff
|
// Allocation Stuff
|
||||||
@@ -306,6 +329,7 @@ Allocator_Mode :: enum byte {
|
|||||||
Query_Features,
|
Query_Features,
|
||||||
Query_Info,
|
Query_Info,
|
||||||
Alloc_Non_Zeroed,
|
Alloc_Non_Zeroed,
|
||||||
|
Resize_Non_Zeroed,
|
||||||
}
|
}
|
||||||
|
|
||||||
Allocator_Mode_Set :: distinct bit_set[Allocator_Mode]
|
Allocator_Mode_Set :: distinct bit_set[Allocator_Mode]
|
||||||
@@ -373,11 +397,34 @@ Logger :: struct {
|
|||||||
options: Logger_Options,
|
options: Logger_Options,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Random_Generator_Mode :: enum {
|
||||||
|
Read,
|
||||||
|
Reset,
|
||||||
|
Query_Info,
|
||||||
|
}
|
||||||
|
|
||||||
|
Random_Generator_Query_Info_Flag :: enum u32 {
|
||||||
|
Cryptographic,
|
||||||
|
Uniform,
|
||||||
|
External_Entropy,
|
||||||
|
Resettable,
|
||||||
|
}
|
||||||
|
Random_Generator_Query_Info :: distinct bit_set[Random_Generator_Query_Info_Flag; u32]
|
||||||
|
|
||||||
|
Random_Generator_Proc :: #type proc(data: rawptr, mode: Random_Generator_Mode, p: []byte)
|
||||||
|
|
||||||
|
Random_Generator :: struct {
|
||||||
|
procedure: Random_Generator_Proc,
|
||||||
|
data: rawptr,
|
||||||
|
}
|
||||||
|
|
||||||
Context :: struct {
|
Context :: struct {
|
||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
temp_allocator: Allocator,
|
temp_allocator: Allocator,
|
||||||
assertion_failure_proc: Assertion_Failure_Proc,
|
assertion_failure_proc: Assertion_Failure_Proc,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
|
random_generator: Random_Generator,
|
||||||
|
|
||||||
user_ptr: rawptr,
|
user_ptr: rawptr,
|
||||||
user_index: int,
|
user_index: int,
|
||||||
@@ -446,6 +493,15 @@ Raw_Soa_Pointer :: struct {
|
|||||||
index: int,
|
index: int,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Raw_Complex32 :: struct {real, imag: f16}
|
||||||
|
Raw_Complex64 :: struct {real, imag: f32}
|
||||||
|
Raw_Complex128 :: struct {real, imag: f64}
|
||||||
|
Raw_Quaternion64 :: struct {imag, jmag, kmag: f16, real: f16}
|
||||||
|
Raw_Quaternion128 :: struct {imag, jmag, kmag: f32, real: f32}
|
||||||
|
Raw_Quaternion256 :: struct {imag, jmag, kmag: f64, real: f64}
|
||||||
|
Raw_Quaternion64_Vector_Scalar :: struct {vector: [3]f16, scalar: f16}
|
||||||
|
Raw_Quaternion128_Vector_Scalar :: struct {vector: [3]f32, scalar: f32}
|
||||||
|
Raw_Quaternion256_Vector_Scalar :: struct {vector: [3]f64, scalar: f64}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -458,8 +514,11 @@ Raw_Soa_Pointer :: struct {
|
|||||||
Essence,
|
Essence,
|
||||||
FreeBSD,
|
FreeBSD,
|
||||||
OpenBSD,
|
OpenBSD,
|
||||||
|
NetBSD,
|
||||||
|
Haiku,
|
||||||
WASI,
|
WASI,
|
||||||
JS,
|
JS,
|
||||||
|
Orca,
|
||||||
Freestanding,
|
Freestanding,
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
@@ -475,15 +534,29 @@ Odin_OS_Type :: type_of(ODIN_OS)
|
|||||||
arm64,
|
arm64,
|
||||||
wasm32,
|
wasm32,
|
||||||
wasm64p32,
|
wasm64p32,
|
||||||
|
riscv64,
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
Odin_Arch_Type :: type_of(ODIN_ARCH)
|
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
|
// Defined internally by the compiler
|
||||||
Odin_Build_Mode_Type :: enum int {
|
Odin_Build_Mode_Type :: enum int {
|
||||||
Executable,
|
Executable,
|
||||||
Dynamic,
|
Dynamic,
|
||||||
|
Static,
|
||||||
Object,
|
Object,
|
||||||
Assembly,
|
Assembly,
|
||||||
LLVM_IR,
|
LLVM_IR,
|
||||||
@@ -501,6 +574,22 @@ Odin_Build_Mode_Type :: type_of(ODIN_BUILD_MODE)
|
|||||||
*/
|
*/
|
||||||
Odin_Endian_Type :: type_of(ODIN_ENDIAN)
|
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
|
// Defined internally by the compiler
|
||||||
@@ -518,12 +607,25 @@ Odin_Platform_Subtarget_Type :: type_of(ODIN_PLATFORM_SUBTARGET)
|
|||||||
Memory = 1,
|
Memory = 1,
|
||||||
Thread = 2,
|
Thread = 2,
|
||||||
}
|
}
|
||||||
Odin_Sanitizer_Flags :: distinct bitset[Odin_Sanitizer_Flag; u32]
|
Odin_Sanitizer_Flags :: distinct bit_set[Odin_Sanitizer_Flag; u32]
|
||||||
|
|
||||||
ODIN_SANITIZER_FLAGS // is a constant
|
ODIN_SANITIZER_FLAGS // is a constant
|
||||||
*/
|
*/
|
||||||
Odin_Sanitizer_Flags :: type_of(ODIN_SANITIZER_FLAGS)
|
Odin_Sanitizer_Flags :: type_of(ODIN_SANITIZER_FLAGS)
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Defined internally by the compiler
|
||||||
|
Odin_Optimization_Mode :: enum int {
|
||||||
|
None = -1,
|
||||||
|
Minimal = 0,
|
||||||
|
Size = 1,
|
||||||
|
Speed = 2,
|
||||||
|
Aggressive = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
ODIN_OPTIMIZATION_MODE // is a constant
|
||||||
|
*/
|
||||||
|
Odin_Optimization_Mode :: type_of(ODIN_OPTIMIZATION_MODE)
|
||||||
|
|
||||||
/////////////////////////////
|
/////////////////////////////
|
||||||
// Init Startup Procedures //
|
// Init Startup Procedures //
|
||||||
@@ -573,8 +675,9 @@ type_info_core :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
|
|||||||
base := info
|
base := info
|
||||||
loop: for {
|
loop: for {
|
||||||
#partial switch i in base.variant {
|
#partial switch i in base.variant {
|
||||||
case Type_Info_Named: base = i.base
|
case Type_Info_Named: base = i.base
|
||||||
case Type_Info_Enum: base = i.base
|
case Type_Info_Enum: base = i.base
|
||||||
|
case Type_Info_Bit_Field: base = i.backing_type
|
||||||
case: break loop
|
case: break loop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -589,7 +692,7 @@ __type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info #no_bounds_check
|
|||||||
if n < 0 || n >= len(type_table) {
|
if n < 0 || n >= len(type_table) {
|
||||||
n = 0
|
n = 0
|
||||||
}
|
}
|
||||||
return &type_table[n]
|
return type_table[n]
|
||||||
}
|
}
|
||||||
|
|
||||||
when !ODIN_NO_RTTI {
|
when !ODIN_NO_RTTI {
|
||||||
@@ -658,13 +761,20 @@ __init_context :: proc "contextless" (c: ^Context) {
|
|||||||
|
|
||||||
c.logger.procedure = default_logger_proc
|
c.logger.procedure = default_logger_proc
|
||||||
c.logger.data = nil
|
c.logger.data = nil
|
||||||
|
|
||||||
|
c.random_generator.procedure = default_random_generator_proc
|
||||||
|
c.random_generator.data = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code_Location) -> ! {
|
default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code_Location) -> ! {
|
||||||
|
default_assertion_contextless_failure_proc(prefix, message, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
default_assertion_contextless_failure_proc :: proc "contextless" (prefix, message: string, loc: Source_Code_Location) -> ! {
|
||||||
when ODIN_OS == .Freestanding {
|
when ODIN_OS == .Freestanding {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
} else {
|
} else {
|
||||||
when !ODIN_DISABLE_ASSERT {
|
when ODIN_OS != .Orca && !ODIN_DISABLE_ASSERT {
|
||||||
print_caller_location(loc)
|
print_caller_location(loc)
|
||||||
print_string(" ")
|
print_string(" ")
|
||||||
}
|
}
|
||||||
@@ -673,7 +783,18 @@ default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code
|
|||||||
print_string(": ")
|
print_string(": ")
|
||||||
print_string(message)
|
print_string(message)
|
||||||
}
|
}
|
||||||
print_byte('\n')
|
|
||||||
|
when ODIN_OS == .Orca {
|
||||||
|
assert_fail(
|
||||||
|
cstring(raw_data(loc.file_path)),
|
||||||
|
cstring(raw_data(loc.procedure)),
|
||||||
|
loc.line,
|
||||||
|
"",
|
||||||
|
cstring(raw_data(orca_stderr_buffer[:orca_stderr_buffer_idx])),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
print_byte('\n')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
trap()
|
trap()
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,44 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
Maybe :: union($T: typeid) {T}
|
Maybe :: union($T: typeid) {T}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Recovers the containing/parent struct from a pointer to one of its fields.
|
||||||
|
Works by "walking back" to the struct's starting address using the offset between the field and the struct.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- ptr: Pointer to the field of a container struct
|
||||||
|
- T: The type of the container struct
|
||||||
|
- field_name: The name of the field in the `T` struct
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- A pointer to the container struct based on a pointer to a field in it
|
||||||
|
|
||||||
|
Example:
|
||||||
|
package container_of
|
||||||
|
import "base:runtime"
|
||||||
|
|
||||||
|
Node :: struct {
|
||||||
|
value: int,
|
||||||
|
prev: ^Node,
|
||||||
|
next: ^Node,
|
||||||
|
}
|
||||||
|
|
||||||
|
main :: proc() {
|
||||||
|
node: Node
|
||||||
|
field_ptr := &node.next
|
||||||
|
container_struct_ptr: ^Node = runtime.container_of(field_ptr, Node, "next")
|
||||||
|
assert(container_struct_ptr == &node)
|
||||||
|
assert(uintptr(field_ptr) - uintptr(container_struct_ptr) == size_of(node.value) + size_of(node.prev))
|
||||||
|
}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
^Node
|
||||||
|
*/
|
||||||
@(builtin, require_results)
|
@(builtin, require_results)
|
||||||
container_of :: #force_inline proc "contextless" (ptr: $P/^$Field_Type, $T: typeid, $field_name: string) -> ^T
|
container_of :: #force_inline proc "contextless" (ptr: $P/^$Field_Type, $T: typeid, $field_name: string) -> ^T
|
||||||
where intrinsics.type_has_field(T, field_name),
|
where intrinsics.type_has_field(T, field_name),
|
||||||
@@ -40,7 +73,7 @@ copy_slice :: proc "contextless" (dst, src: $T/[]$E) -> int {
|
|||||||
}
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
// `copy_from_string` is a built-in procedure that copies elements from a source slice `src` to a destination string `dst`.
|
// `copy_from_string` is a built-in procedure that copies elements from a source string `src` to a destination slice `dst`.
|
||||||
// The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum
|
// The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum
|
||||||
// of len(src) and len(dst).
|
// of len(src) and len(dst).
|
||||||
//
|
//
|
||||||
@@ -53,7 +86,7 @@ copy_from_string :: proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int
|
|||||||
}
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
// `copy` is a built-in procedure that copies elements from a source slice `src` to a destination slice/string `dst`.
|
// `copy` is a built-in procedure that copies elements from a source slice/string `src` to a destination slice `dst`.
|
||||||
// The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum
|
// The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum
|
||||||
// of len(src) and len(dst).
|
// of len(src) and len(dst).
|
||||||
@builtin
|
@builtin
|
||||||
@@ -65,10 +98,10 @@ copy :: proc{copy_slice, copy_from_string}
|
|||||||
// with the old value, and reducing the length of the dynamic array by 1.
|
// with the old value, and reducing the length of the dynamic array by 1.
|
||||||
//
|
//
|
||||||
// Note: This is an O(1) operation.
|
// Note: This is an O(1) operation.
|
||||||
// Note: If you the elements to remain in their order, use `ordered_remove`.
|
// 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.
|
// Note: If the index is out of bounds, this procedure will panic.
|
||||||
@builtin
|
@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))
|
bounds_check_error_loc(loc, index, len(array))
|
||||||
n := len(array)-1
|
n := len(array)-1
|
||||||
if index != n {
|
if index != n {
|
||||||
@@ -79,10 +112,10 @@ unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_loca
|
|||||||
// `ordered_remove` removed the element at the specified `index` whilst keeping the order of the other elements.
|
// `ordered_remove` removed the element at the specified `index` whilst keeping the order of the other elements.
|
||||||
//
|
//
|
||||||
// Note: This is an O(N) operation.
|
// Note: This is an O(N) operation.
|
||||||
// Note: If you the elements do not have to remain in their order, prefer `unordered_remove`.
|
// 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.
|
// Note: If the index is out of bounds, this procedure will panic.
|
||||||
@builtin
|
@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))
|
bounds_check_error_loc(loc, index, len(array))
|
||||||
if index+1 < len(array) {
|
if index+1 < len(array) {
|
||||||
copy(array[index:], array[index+1:])
|
copy(array[index:], array[index+1:])
|
||||||
@@ -95,7 +128,7 @@ ordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_locati
|
|||||||
// Note: This is an O(N) operation.
|
// Note: This is an O(N) operation.
|
||||||
// Note: If the range is out of bounds, this procedure will panic.
|
// Note: If the range is out of bounds, this procedure will panic.
|
||||||
@builtin
|
@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))
|
slice_expr_error_lo_hi_loc(loc, lo, hi, len(array))
|
||||||
n := max(hi-lo, 0)
|
n := max(hi-lo, 0)
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
@@ -109,7 +142,7 @@ remove_range :: proc(array: ^$D/[dynamic]$T, lo, hi: int, loc := #caller_locatio
|
|||||||
|
|
||||||
// `pop` will remove and return the end value of dynamic array `array` and reduces the length of `array` by 1.
|
// `pop` will remove and return the end value of dynamic array `array` and reduces the length of `array` by 1.
|
||||||
//
|
//
|
||||||
// Note: If the dynamic array as no elements (`len(array) == 0`), this procedure will panic.
|
// Note: If the dynamic array has no elements (`len(array) == 0`), this procedure will panic.
|
||||||
@builtin
|
@builtin
|
||||||
pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
|
pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
|
||||||
assert(len(array) > 0, loc=loc)
|
assert(len(array) > 0, loc=loc)
|
||||||
@@ -122,7 +155,7 @@ pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bou
|
|||||||
// `pop_safe` trys to remove and return the end value of dynamic array `array` and reduces the length of `array` by 1.
|
// `pop_safe` trys to remove and return the end value of dynamic array `array` and reduces the length of `array` by 1.
|
||||||
// If the operation is not possible, it will return false.
|
// If the operation is not possible, it will return false.
|
||||||
@builtin
|
@builtin
|
||||||
pop_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
|
pop_safe :: proc "contextless" (array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
|
||||||
if len(array) == 0 {
|
if len(array) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -148,7 +181,7 @@ pop_front :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #
|
|||||||
// `pop_front_safe` trys to return and remove the first value of dynamic array `array` and reduces the length of `array` by 1.
|
// `pop_front_safe` trys to return and remove the first value of dynamic array `array` and reduces the length of `array` by 1.
|
||||||
// If the operation is not possible, it will return false.
|
// If the operation is not possible, it will return false.
|
||||||
@builtin
|
@builtin
|
||||||
pop_front_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
|
pop_front_safe :: proc "contextless" (array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
|
||||||
if len(array) == 0 {
|
if len(array) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -163,15 +196,43 @@ pop_front_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_
|
|||||||
|
|
||||||
// `clear` will set the length of a passed dynamic array or map to `0`
|
// `clear` will set the length of a passed dynamic array or map to `0`
|
||||||
@builtin
|
@builtin
|
||||||
clear :: proc{clear_dynamic_array, clear_map}
|
clear :: proc{
|
||||||
|
clear_dynamic_array,
|
||||||
|
clear_map,
|
||||||
|
|
||||||
|
clear_soa_dynamic_array,
|
||||||
|
}
|
||||||
|
|
||||||
// `reserve` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`).
|
// `reserve` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`).
|
||||||
@builtin
|
@builtin
|
||||||
reserve :: proc{reserve_dynamic_array, reserve_map}
|
reserve :: proc{
|
||||||
|
reserve_dynamic_array,
|
||||||
|
reserve_map,
|
||||||
|
|
||||||
|
reserve_soa,
|
||||||
|
}
|
||||||
|
|
||||||
// `resize` will try to resize memory of a passed dynamic array or map to the requested element count (setting the `len`, and possibly `cap`).
|
|
||||||
@builtin
|
@builtin
|
||||||
resize :: proc{resize_dynamic_array}
|
non_zero_reserve :: proc{
|
||||||
|
non_zero_reserve_dynamic_array,
|
||||||
|
|
||||||
|
non_zero_reserve_soa,
|
||||||
|
}
|
||||||
|
|
||||||
|
// `resize` will try to resize memory of a passed dynamic array to the requested element count (setting the `len`, and possibly `cap`).
|
||||||
|
@builtin
|
||||||
|
resize :: proc{
|
||||||
|
resize_dynamic_array,
|
||||||
|
|
||||||
|
resize_soa,
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
non_zero_resize :: proc{
|
||||||
|
non_zero_resize_dynamic_array,
|
||||||
|
|
||||||
|
non_zero_resize_soa,
|
||||||
|
}
|
||||||
|
|
||||||
// Shrinks the capacity of a dynamic array or map down to the current length, or the given capacity.
|
// Shrinks the capacity of a dynamic array or map down to the current length, or the given capacity.
|
||||||
@builtin
|
@builtin
|
||||||
@@ -234,6 +295,8 @@ delete :: proc{
|
|||||||
delete_dynamic_array,
|
delete_dynamic_array,
|
||||||
delete_slice,
|
delete_slice,
|
||||||
delete_map,
|
delete_map,
|
||||||
|
delete_soa_slice,
|
||||||
|
delete_soa_dynamic_array,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -260,7 +323,7 @@ new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_locat
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFAULT_RESERVE_CAPACITY :: 16
|
DEFAULT_DYNAMIC_ARRAY_CAPACITY :: 8
|
||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_allocator_error {
|
make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_allocator_error {
|
||||||
@@ -287,7 +350,7 @@ make_slice :: proc($T: typeid/[]$E, #any_int len: int, allocator := context.allo
|
|||||||
// Note: Prefer using the procedure group `make`.
|
// Note: Prefer using the procedure group `make`.
|
||||||
@(builtin, require_results)
|
@(builtin, require_results)
|
||||||
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_allocator_error {
|
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_allocator_error {
|
||||||
return make_dynamic_array_len_cap(T, 0, DEFAULT_RESERVE_CAPACITY, allocator, loc)
|
return make_dynamic_array_len_cap(T, 0, 0, allocator, loc)
|
||||||
}
|
}
|
||||||
// `make_dynamic_array_len` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value.
|
// `make_dynamic_array_len` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value.
|
||||||
// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
|
// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
|
||||||
@@ -303,28 +366,47 @@ make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, alloca
|
|||||||
// Note: Prefer using the procedure group `make`.
|
// Note: Prefer using the procedure group `make`.
|
||||||
@(builtin, require_results)
|
@(builtin, require_results)
|
||||||
make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
||||||
make_dynamic_array_error_loc(loc, len, cap)
|
err = _make_dynamic_array_len_cap((^Raw_Dynamic_Array)(&array), size_of(E), align_of(E), len, cap, allocator, loc)
|
||||||
data := mem_alloc_bytes(size_of(E)*cap, align_of(E), allocator, loc) or_return
|
|
||||||
s := Raw_Dynamic_Array{raw_data(data), len, cap, allocator}
|
|
||||||
if data == nil && size_of(E) != 0 {
|
|
||||||
s.len, s.cap = 0, 0
|
|
||||||
}
|
|
||||||
array = transmute(T)s
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// `make_map` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value.
|
|
||||||
|
@(require_results)
|
||||||
|
_make_dynamic_array_len_cap :: proc(array: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (err: Allocator_Error) {
|
||||||
|
make_dynamic_array_error_loc(loc, len, cap)
|
||||||
|
array.allocator = allocator // initialize allocator before just in case it fails to allocate any memory
|
||||||
|
data := mem_alloc_bytes(size_of_elem*cap, align_of_elem, allocator, loc) or_return
|
||||||
|
use_zero := data == nil && size_of_elem != 0
|
||||||
|
array.data = raw_data(data)
|
||||||
|
array.len = 0 if use_zero else len
|
||||||
|
array.cap = 0 if use_zero else cap
|
||||||
|
array.allocator = allocator
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// `make_map` initializes a map with an allocator. Like `new`, the first argument is a type, not a value.
|
||||||
// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
|
// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
|
||||||
//
|
//
|
||||||
// Note: Prefer using the procedure group `make`.
|
// Note: Prefer using the procedure group `make`.
|
||||||
@(builtin, require_results)
|
@(builtin, require_results)
|
||||||
make_map :: proc($T: typeid/map[$K]$E, #any_int capacity: int = 1<<MAP_MIN_LOG2_CAPACITY, allocator := context.allocator, loc := #caller_location) -> (m: T, err: Allocator_Error) #optional_allocator_error {
|
make_map :: proc($T: typeid/map[$K]$E, allocator := context.allocator, loc := #caller_location) -> (m: T) {
|
||||||
|
m.allocator = allocator
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// `make_map_cap` initializes a map with an allocator and allocates space using `capacity`.
|
||||||
|
// Like `new`, the first argument is a type, not a value.
|
||||||
|
// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
|
||||||
|
//
|
||||||
|
// Note: Prefer using the procedure group `make`.
|
||||||
|
@(builtin, require_results)
|
||||||
|
make_map_cap :: proc($T: typeid/map[$K]$E, #any_int capacity: int, allocator := context.allocator, loc := #caller_location) -> (m: T, err: Allocator_Error) #optional_allocator_error {
|
||||||
make_map_expr_error_loc(loc, capacity)
|
make_map_expr_error_loc(loc, capacity)
|
||||||
context.allocator = allocator
|
context.allocator = allocator
|
||||||
|
|
||||||
err = reserve_map(&m, capacity, loc)
|
err = reserve_map(&m, capacity, loc)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// `make_multi_pointer` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value.
|
// `make_multi_pointer` allocates and initializes a multi-pointer. Like `new`, the first argument is a type, not a value.
|
||||||
// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
|
// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
|
||||||
//
|
//
|
||||||
// This is "similar" to doing `raw_data(make([]E, len, allocator))`.
|
// This is "similar" to doing `raw_data(make([]E, len, allocator))`.
|
||||||
@@ -346,7 +428,7 @@ make_multi_pointer :: proc($T: typeid/[^]$E, #any_int len: int, allocator := con
|
|||||||
//
|
//
|
||||||
// Similar to `new`, the first argument is a type, not a value. Unlike new, make's return type is the same as the
|
// Similar to `new`, the first argument is a type, not a value. Unlike new, make's return type is the same as the
|
||||||
// type of its argument, not a pointer to it.
|
// type of its argument, not a pointer to it.
|
||||||
// Make uses the specified allocator, default is context.allocator, default is context.allocator
|
// Make uses the specified allocator, default is context.allocator.
|
||||||
@builtin
|
@builtin
|
||||||
make :: proc{
|
make :: proc{
|
||||||
make_slice,
|
make_slice,
|
||||||
@@ -354,7 +436,13 @@ make :: proc{
|
|||||||
make_dynamic_array_len,
|
make_dynamic_array_len,
|
||||||
make_dynamic_array_len_cap,
|
make_dynamic_array_len_cap,
|
||||||
make_map,
|
make_map,
|
||||||
|
make_map_cap,
|
||||||
make_multi_pointer,
|
make_multi_pointer,
|
||||||
|
|
||||||
|
make_soa_slice,
|
||||||
|
make_soa_dynamic_array,
|
||||||
|
make_soa_dynamic_array_len,
|
||||||
|
make_soa_dynamic_array_len_cap,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -374,7 +462,7 @@ clear_map :: proc "contextless" (m: ^$T/map[$K]$V) {
|
|||||||
//
|
//
|
||||||
// Note: Prefer the procedure group `reserve`
|
// Note: Prefer the procedure group `reserve`
|
||||||
@builtin
|
@builtin
|
||||||
reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int, loc := #caller_location) -> Allocator_Error {
|
reserve_map :: proc(m: ^$T/map[$K]$V, #any_int capacity: int, loc := #caller_location) -> Allocator_Error {
|
||||||
return __dynamic_map_reserve((^Raw_Map)(m), map_info(T), uint(capacity), loc) if m != nil else nil
|
return __dynamic_map_reserve((^Raw_Map)(m), map_info(T), uint(capacity), loc) if m != nil else nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -404,75 +492,112 @@ delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_append_elem :: #force_inline proc(array: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, arg_ptr: rawptr, should_zero: bool, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
if array == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if array.cap < array.len+1 {
|
||||||
|
// Same behavior as _append_elems but there's only one arg, so we always just add DEFAULT_DYNAMIC_ARRAY_CAPACITY.
|
||||||
|
cap := 2 * array.cap + DEFAULT_DYNAMIC_ARRAY_CAPACITY
|
||||||
|
|
||||||
|
// do not 'or_return' here as it could be a partial success
|
||||||
|
err = _reserve_dynamic_array(array, size_of_elem, align_of_elem, cap, should_zero, loc)
|
||||||
|
}
|
||||||
|
if array.cap-array.len > 0 {
|
||||||
|
data := ([^]byte)(array.data)
|
||||||
|
assert(data != nil, loc=loc)
|
||||||
|
data = data[array.len*size_of_elem:]
|
||||||
|
intrinsics.mem_copy_non_overlapping(data, arg_ptr, size_of_elem)
|
||||||
|
array.len += 1
|
||||||
|
n = 1
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
if array == nil {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
when size_of(E) == 0 {
|
when size_of(E) == 0 {
|
||||||
array := (^Raw_Dynamic_Array)(array)
|
(^Raw_Dynamic_Array)(array).len += 1
|
||||||
array.len += 1
|
|
||||||
return 1, nil
|
return 1, nil
|
||||||
} else {
|
} else {
|
||||||
if cap(array) < len(array)+1 {
|
arg := arg
|
||||||
cap := 2 * cap(array) + max(8, 1)
|
return _append_elem((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), &arg, true, loc=loc)
|
||||||
err = reserve(array, cap, loc) // do not 'or_return' here as it could be a partial success
|
|
||||||
}
|
|
||||||
if cap(array)-len(array) > 0 {
|
|
||||||
a := (^Raw_Dynamic_Array)(array)
|
|
||||||
when size_of(E) != 0 {
|
|
||||||
data := ([^]E)(a.data)
|
|
||||||
assert(data != nil, loc=loc)
|
|
||||||
data[a.len] = arg
|
|
||||||
}
|
|
||||||
a.len += 1
|
|
||||||
return 1, err
|
|
||||||
}
|
|
||||||
return 0, err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
non_zero_append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
when size_of(E) == 0 {
|
||||||
|
(^Raw_Dynamic_Array)(array).len += 1
|
||||||
|
return 1, nil
|
||||||
|
} else {
|
||||||
|
arg := arg
|
||||||
|
return _append_elem((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), &arg, false, loc=loc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_append_elems :: #force_inline proc(array: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, should_zero: bool, loc := #caller_location, args: rawptr, arg_len: int) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
if array == nil {
|
if array == nil {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
arg_len := len(args)
|
|
||||||
if arg_len <= 0 {
|
if arg_len <= 0 {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
when size_of(E) == 0 {
|
if array.cap < array.len+arg_len {
|
||||||
array := (^Raw_Dynamic_Array)(array)
|
cap := 2 * array.cap + max(DEFAULT_DYNAMIC_ARRAY_CAPACITY, arg_len)
|
||||||
|
|
||||||
|
// do not 'or_return' here as it could be a partial success
|
||||||
|
err = _reserve_dynamic_array(array, size_of_elem, align_of_elem, cap, should_zero, loc)
|
||||||
|
}
|
||||||
|
arg_len := arg_len
|
||||||
|
arg_len = min(array.cap-array.len, arg_len)
|
||||||
|
if arg_len > 0 {
|
||||||
|
data := ([^]byte)(array.data)
|
||||||
|
assert(data != nil, loc=loc)
|
||||||
|
data = data[array.len*size_of_elem:]
|
||||||
|
intrinsics.mem_copy(data, args, size_of_elem * arg_len) // must be mem_copy (overlapping)
|
||||||
array.len += arg_len
|
array.len += arg_len
|
||||||
return arg_len, nil
|
}
|
||||||
|
return arg_len, err
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
when size_of(E) == 0 {
|
||||||
|
a := (^Raw_Dynamic_Array)(array)
|
||||||
|
a.len += len(args)
|
||||||
|
return len(args), nil
|
||||||
} else {
|
} else {
|
||||||
if cap(array) < len(array)+arg_len {
|
return _append_elems((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), true, loc, raw_data(args), len(args))
|
||||||
cap := 2 * cap(array) + max(8, arg_len)
|
}
|
||||||
err = reserve(array, cap, loc) // do not 'or_return' here as it could be a partial success
|
}
|
||||||
}
|
|
||||||
arg_len = min(cap(array)-len(array), arg_len)
|
@builtin
|
||||||
if arg_len > 0 {
|
non_zero_append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
a := (^Raw_Dynamic_Array)(array)
|
when size_of(E) == 0 {
|
||||||
when size_of(E) != 0 {
|
a := (^Raw_Dynamic_Array)(array)
|
||||||
data := ([^]E)(a.data)
|
a.len += len(args)
|
||||||
assert(data != nil, loc=loc)
|
return len(args), nil
|
||||||
intrinsics.mem_copy(&data[a.len], raw_data(args), size_of(E) * arg_len)
|
} else {
|
||||||
}
|
return _append_elems((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), false, loc, raw_data(args), len(args))
|
||||||
a.len += arg_len
|
|
||||||
}
|
|
||||||
return arg_len, err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type
|
// The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type
|
||||||
|
_append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, should_zero: bool, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
return _append_elems((^Raw_Dynamic_Array)(array), 1, 1, should_zero, loc, raw_data(arg), len(arg))
|
||||||
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
args := transmute([]E)arg
|
return _append_elem_string(array, arg, true, loc)
|
||||||
return append_elems(array, ..args, loc=loc)
|
}
|
||||||
|
@builtin
|
||||||
|
non_zero_append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
return _append_elem_string(array, arg, false, loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -491,7 +616,23 @@ append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The append built-in procedure appends elements to the end of a dynamic array
|
// The append built-in procedure appends elements to the end of a dynamic array
|
||||||
@builtin append :: proc{append_elem, append_elems, append_elem_string}
|
@builtin append :: proc{
|
||||||
|
append_elem,
|
||||||
|
append_elems,
|
||||||
|
append_elem_string,
|
||||||
|
|
||||||
|
append_soa_elem,
|
||||||
|
append_soa_elems,
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin non_zero_append :: proc{
|
||||||
|
non_zero_append_elem,
|
||||||
|
non_zero_append_elems,
|
||||||
|
non_zero_append_elem_string,
|
||||||
|
|
||||||
|
non_zero_append_soa_elem,
|
||||||
|
non_zero_append_soa_elems,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
@@ -506,7 +647,7 @@ append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (n: i
|
|||||||
|
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
inject_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 {
|
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 {
|
if array == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -524,7 +665,7 @@ inject_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #calle
|
|||||||
}
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
inject_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, 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 {
|
if array == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -547,7 +688,7 @@ inject_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #c
|
|||||||
}
|
}
|
||||||
|
|
||||||
@builtin
|
@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 {
|
if array == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -572,7 +713,7 @@ inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string
|
|||||||
|
|
||||||
|
|
||||||
@builtin
|
@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) {
|
if index < len(array) {
|
||||||
array[index] = arg
|
array[index] = arg
|
||||||
ok = true
|
ok = true
|
||||||
@@ -586,12 +727,15 @@ assign_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #calle
|
|||||||
|
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, 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 {
|
||||||
if index+len(args) < len(array) {
|
new_size := index + len(args)
|
||||||
|
if len(args) == 0 {
|
||||||
|
ok = true
|
||||||
|
} else if new_size < len(array) {
|
||||||
copy(array[index:], args)
|
copy(array[index:], args)
|
||||||
ok = true
|
ok = true
|
||||||
} else {
|
} else {
|
||||||
resize(array, index+1+len(args), loc) or_return
|
resize(array, new_size, loc) or_return
|
||||||
copy(array[index:], args)
|
copy(array[index:], args)
|
||||||
ok = true
|
ok = true
|
||||||
}
|
}
|
||||||
@@ -600,7 +744,7 @@ assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #c
|
|||||||
|
|
||||||
|
|
||||||
@builtin
|
@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)
|
new_size := index + len(arg)
|
||||||
if len(arg) == 0 {
|
if len(arg) == 0 {
|
||||||
ok = true
|
ok = true
|
||||||
@@ -633,12 +777,10 @@ clear_dynamic_array :: proc "contextless" (array: ^$T/[dynamic]$E) {
|
|||||||
// `reserve_dynamic_array` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`).
|
// `reserve_dynamic_array` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`).
|
||||||
//
|
//
|
||||||
// Note: Prefer the procedure group `reserve`.
|
// Note: Prefer the procedure group `reserve`.
|
||||||
@builtin
|
_reserve_dynamic_array :: #force_inline proc(a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, capacity: int, should_zero: bool, loc := #caller_location) -> Allocator_Error {
|
||||||
reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error {
|
if a == nil {
|
||||||
if array == nil {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
a := (^Raw_Dynamic_Array)(array)
|
|
||||||
|
|
||||||
if capacity <= a.cap {
|
if capacity <= a.cap {
|
||||||
return nil
|
return nil
|
||||||
@@ -649,11 +791,16 @@ reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #cal
|
|||||||
}
|
}
|
||||||
assert(a.allocator.procedure != nil)
|
assert(a.allocator.procedure != nil)
|
||||||
|
|
||||||
old_size := a.cap * size_of(E)
|
old_size := a.cap * size_of_elem
|
||||||
new_size := capacity * size_of(E)
|
new_size := capacity * size_of_elem
|
||||||
allocator := a.allocator
|
allocator := a.allocator
|
||||||
|
|
||||||
new_data := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
new_data: []byte
|
||||||
|
if should_zero {
|
||||||
|
new_data = mem_resize(a.data, old_size, new_size, align_of_elem, allocator, loc) or_return
|
||||||
|
} else {
|
||||||
|
new_data = non_zero_mem_resize(a.data, old_size, new_size, align_of_elem, allocator, loc) or_return
|
||||||
|
}
|
||||||
if new_data == nil && new_size > 0 {
|
if new_data == nil && new_size > 0 {
|
||||||
return .Out_Of_Memory
|
return .Out_Of_Memory
|
||||||
}
|
}
|
||||||
@@ -663,15 +810,26 @@ reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #cal
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// `resize_dynamic_array` will try to resize memory of a passed dynamic array or map to the requested element count (setting the `len`, and possibly `cap`).
|
|
||||||
//
|
|
||||||
// Note: Prefer the procedure group `resize`
|
|
||||||
@builtin
|
@builtin
|
||||||
resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> Allocator_Error {
|
reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error {
|
||||||
if array == nil {
|
return _reserve_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), capacity, true, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
non_zero_reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error {
|
||||||
|
return _reserve_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), capacity, false, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_resize_dynamic_array :: #force_inline proc(a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, length: int, should_zero: bool, loc := #caller_location) -> Allocator_Error {
|
||||||
|
if a == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
a := (^Raw_Dynamic_Array)(array)
|
|
||||||
|
if should_zero && a.len < length {
|
||||||
|
num_reused := min(a.cap, length) - a.len
|
||||||
|
intrinsics.mem_zero(([^]byte)(a.data)[a.len*size_of_elem:], num_reused*size_of_elem)
|
||||||
|
}
|
||||||
|
|
||||||
if length <= a.cap {
|
if length <= a.cap {
|
||||||
a.len = max(length, 0)
|
a.len = max(length, 0)
|
||||||
@@ -683,11 +841,16 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller
|
|||||||
}
|
}
|
||||||
assert(a.allocator.procedure != nil)
|
assert(a.allocator.procedure != nil)
|
||||||
|
|
||||||
old_size := a.cap * size_of(E)
|
old_size := a.cap * size_of_elem
|
||||||
new_size := length * size_of(E)
|
new_size := length * size_of_elem
|
||||||
allocator := a.allocator
|
allocator := a.allocator
|
||||||
|
|
||||||
new_data := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
new_data : []byte
|
||||||
|
if should_zero {
|
||||||
|
new_data = mem_resize(a.data, old_size, new_size, align_of_elem, allocator, loc) or_return
|
||||||
|
} else {
|
||||||
|
new_data = non_zero_mem_resize(a.data, old_size, new_size, align_of_elem, allocator, loc) or_return
|
||||||
|
}
|
||||||
if new_data == nil && new_size > 0 {
|
if new_data == nil && new_size > 0 {
|
||||||
return .Out_Of_Memory
|
return .Out_Of_Memory
|
||||||
}
|
}
|
||||||
@@ -698,6 +861,19 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `resize_dynamic_array` will try to resize memory of a passed dynamic array or map to the requested element count (setting the `len`, and possibly `cap`).
|
||||||
|
//
|
||||||
|
// Note: Prefer the procedure group `resize`
|
||||||
|
@builtin
|
||||||
|
resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error {
|
||||||
|
return _resize_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), length, true, loc=loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
non_zero_resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error {
|
||||||
|
return _resize_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), length, false, loc=loc)
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Shrinks the capacity of a dynamic array down to the current length, or the given capacity.
|
Shrinks the capacity of a dynamic array down to the current length, or the given capacity.
|
||||||
|
|
||||||
@@ -709,11 +885,14 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller
|
|||||||
|
|
||||||
Note: Prefer the procedure group `shrink`
|
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) {
|
||||||
if array == nil {
|
return _shrink_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), new_cap, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
_shrink_dynamic_array :: proc(a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, new_cap := -1, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) {
|
||||||
|
if a == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
a := (^Raw_Dynamic_Array)(array)
|
|
||||||
|
|
||||||
new_cap := new_cap if new_cap >= 0 else a.len
|
new_cap := new_cap if new_cap >= 0 else a.len
|
||||||
|
|
||||||
@@ -726,10 +905,10 @@ shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #call
|
|||||||
}
|
}
|
||||||
assert(a.allocator.procedure != nil)
|
assert(a.allocator.procedure != nil)
|
||||||
|
|
||||||
old_size := a.cap * size_of(E)
|
old_size := a.cap * size_of_elem
|
||||||
new_size := new_cap * size_of(E)
|
new_size := new_cap * size_of_elem
|
||||||
|
|
||||||
new_data := mem_resize(a.data, old_size, new_size, align_of(E), a.allocator, loc) or_return
|
new_data := mem_resize(a.data, old_size, new_size, align_of_elem, a.allocator, loc) or_return
|
||||||
|
|
||||||
a.data = raw_data(new_data)
|
a.data = raw_data(new_data)
|
||||||
a.len = min(new_cap, a.len)
|
a.len = min(new_cap, a.len)
|
||||||
@@ -743,62 +922,59 @@ map_insert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location)
|
|||||||
return (^V)(__dynamic_map_set_without_hash((^Raw_Map)(m), map_info(T), rawptr(&key), rawptr(&value), loc))
|
return (^V)(__dynamic_map_set_without_hash((^Raw_Map)(m), map_info(T), rawptr(&key), rawptr(&value), loc))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Explicitly inserts a key and value into a map `m`, the same as `map_insert`, but the return values differ.
|
||||||
@builtin
|
// - `prev_key` will return the previous pointer of a key if it exists, check `found_previous` if was previously found
|
||||||
incl_elem :: proc(s: ^$S/bit_set[$E; $U], elem: E) {
|
// - `value_ptr` will return the pointer of the memory where the insertion happens, and `nil` if the map failed to resize
|
||||||
s^ |= {elem}
|
// - `found_previous` will be true a previous key was found
|
||||||
}
|
@(builtin, require_results)
|
||||||
@builtin
|
map_upsert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (prev_key: K, value_ptr: ^V, found_previous: bool) {
|
||||||
incl_elems :: proc(s: ^$S/bit_set[$E; $U], elems: ..E) {
|
key, value := key, value
|
||||||
for elem in elems {
|
kp, vp := __dynamic_map_set_extra_without_hash((^Raw_Map)(m), map_info(T), rawptr(&key), rawptr(&value), loc)
|
||||||
s^ |= {elem}
|
if kp != nil {
|
||||||
|
prev_key = (^K)(kp)^
|
||||||
|
found_previous = true
|
||||||
}
|
}
|
||||||
}
|
value_ptr = (^V)(vp)
|
||||||
@builtin
|
return
|
||||||
incl_bit_set :: proc(s: ^$S/bit_set[$E; $U], other: S) {
|
|
||||||
s^ |= other
|
|
||||||
}
|
|
||||||
@builtin
|
|
||||||
excl_elem :: proc(s: ^$S/bit_set[$E; $U], elem: E) {
|
|
||||||
s^ &~= {elem}
|
|
||||||
}
|
|
||||||
@builtin
|
|
||||||
excl_elems :: proc(s: ^$S/bit_set[$E; $U], elems: ..E) {
|
|
||||||
for elem in elems {
|
|
||||||
s^ &~= {elem}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@builtin
|
|
||||||
excl_bit_set :: proc(s: ^$S/bit_set[$E; $U], other: S) {
|
|
||||||
s^ &~= other
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@builtin incl :: proc{incl_elem, incl_elems, incl_bit_set}
|
/*
|
||||||
@builtin excl :: proc{excl_elem, excl_elems, excl_bit_set}
|
Retrieves a pointer to the key and value for a possibly just inserted entry into the map.
|
||||||
|
|
||||||
|
If the `key` was not in the map `m`, an entry is inserted with the zero value and `just_inserted` will be `true`.
|
||||||
|
Otherwise the existing entry is left untouched and pointers to its key and value are returned.
|
||||||
|
|
||||||
|
If the map has to grow in order to insert the entry and the allocation fails, `err` is set and returned.
|
||||||
|
|
||||||
|
If `err` is `nil`, `key_ptr` and `value_ptr` are valid pointers and will not be `nil`.
|
||||||
|
|
||||||
|
WARN: User modification of the key pointed at by `key_ptr` should only be done if the new key is equal to (in hash) the old key.
|
||||||
|
If that is not the case you will corrupt the map.
|
||||||
|
*/
|
||||||
|
@(builtin, require_results)
|
||||||
|
map_entry :: proc(m: ^$T/map[$K]$V, key: K, loc := #caller_location) -> (key_ptr: ^K, value_ptr: ^V, just_inserted: bool, err: Allocator_Error) {
|
||||||
|
key := key
|
||||||
|
zero: V
|
||||||
|
|
||||||
|
_key_ptr, _value_ptr: rawptr
|
||||||
|
_key_ptr, _value_ptr, just_inserted, err = __dynamic_map_entry((^Raw_Map)(m), map_info(T), &key, &zero, loc)
|
||||||
|
|
||||||
|
key_ptr = (^K)(_key_ptr)
|
||||||
|
value_ptr = (^V)(_value_ptr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
card :: proc(s: $S/bit_set[$E; $U]) -> int {
|
card :: proc "contextless" (s: $S/bit_set[$E; $U]) -> int {
|
||||||
when size_of(S) == 1 {
|
return int(intrinsics.count_ones(transmute(intrinsics.type_bit_set_underlying_type(S))s))
|
||||||
return int(intrinsics.count_ones(transmute(u8)s))
|
|
||||||
} else when size_of(S) == 2 {
|
|
||||||
return int(intrinsics.count_ones(transmute(u16)s))
|
|
||||||
} else when size_of(S) == 4 {
|
|
||||||
return int(intrinsics.count_ones(transmute(u32)s))
|
|
||||||
} else when size_of(S) == 8 {
|
|
||||||
return int(intrinsics.count_ones(transmute(u64)s))
|
|
||||||
} else when size_of(S) == 16 {
|
|
||||||
return int(intrinsics.count_ones(transmute(u128)s))
|
|
||||||
} else {
|
|
||||||
#panic("Unhandled card bit_set size")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
@(disabled=ODIN_DISABLE_ASSERT)
|
@(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 {
|
if !condition {
|
||||||
// NOTE(bill): This is wrapped in a procedure call
|
// NOTE(bill): This is wrapped in a procedure call
|
||||||
// to improve performance to make the CPU not
|
// to improve performance to make the CPU not
|
||||||
@@ -816,6 +992,24 @@ assert :: proc(condition: bool, message := "", loc := #caller_location) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Evaluates the condition and aborts the program iff the condition is
|
||||||
|
// false. This routine ignores `ODIN_DISABLE_ASSERT`, and will always
|
||||||
|
// execute.
|
||||||
|
@builtin
|
||||||
|
ensure :: proc(condition: bool, message := #caller_expression(condition), loc := #caller_location) {
|
||||||
|
if !condition {
|
||||||
|
@(cold)
|
||||||
|
internal :: proc(message: string, loc: Source_Code_Location) {
|
||||||
|
p := context.assertion_failure_proc
|
||||||
|
if p == nil {
|
||||||
|
p = default_assertion_failure_proc
|
||||||
|
}
|
||||||
|
p("unsatisfied ensure", message, loc)
|
||||||
|
}
|
||||||
|
internal(message, loc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
panic :: proc(message: string, loc := #caller_location) -> ! {
|
panic :: proc(message: string, loc := #caller_location) -> ! {
|
||||||
p := context.assertion_failure_proc
|
p := context.assertion_failure_proc
|
||||||
@@ -833,3 +1027,41 @@ unimplemented :: proc(message := "", loc := #caller_location) -> ! {
|
|||||||
}
|
}
|
||||||
p("not yet implemented", message, loc)
|
p("not yet implemented", message, loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
@(disabled=ODIN_DISABLE_ASSERT)
|
||||||
|
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
|
||||||
|
// execute speculatively, making it about an order of
|
||||||
|
// magnitude faster
|
||||||
|
@(cold)
|
||||||
|
internal :: proc "contextless" (message: string, loc: Source_Code_Location) {
|
||||||
|
default_assertion_contextless_failure_proc("runtime assertion", message, loc)
|
||||||
|
}
|
||||||
|
internal(message, loc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
ensure_contextless :: proc "contextless" (condition: bool, message := #caller_expression(condition), loc := #caller_location) {
|
||||||
|
if !condition {
|
||||||
|
@(cold)
|
||||||
|
internal :: proc "contextless" (message: string, loc: Source_Code_Location) {
|
||||||
|
default_assertion_contextless_failure_proc("unsatisfied ensure", message, loc)
|
||||||
|
}
|
||||||
|
internal(message, loc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
panic_contextless :: proc "contextless" (message: string, loc := #caller_location) -> ! {
|
||||||
|
default_assertion_contextless_failure_proc("panic", message, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
unimplemented_contextless :: proc "contextless" (message := "", loc := #caller_location) -> ! {
|
||||||
|
default_assertion_contextless_failure_proc("not yet implemented", message, loc)
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
_ :: intrinsics
|
_ :: intrinsics
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -55,7 +55,7 @@ raw_soa_footer_slice :: proc(array: ^$T/#soa[]$E) -> (footer: ^Raw_SOA_Footer_Sl
|
|||||||
if array == nil {
|
if array == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
field_count := uintptr(intrinsics.type_struct_field_count(E))
|
field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E))
|
||||||
footer = (^Raw_SOA_Footer_Slice)(uintptr(array) + field_count*size_of(rawptr))
|
footer = (^Raw_SOA_Footer_Slice)(uintptr(array) + field_count*size_of(rawptr))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -64,12 +64,7 @@ raw_soa_footer_dynamic_array :: proc(array: ^$T/#soa[dynamic]$E) -> (footer: ^Ra
|
|||||||
if array == nil {
|
if array == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
field_count: uintptr
|
field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E))
|
||||||
when intrinsics.type_is_array(E) {
|
|
||||||
field_count = len(E)
|
|
||||||
} else {
|
|
||||||
field_count = uintptr(intrinsics.type_struct_field_count(E))
|
|
||||||
}
|
|
||||||
footer = (^Raw_SOA_Footer_Dynamic_Array)(uintptr(array) + field_count*size_of(rawptr))
|
footer = (^Raw_SOA_Footer_Dynamic_Array)(uintptr(array) + field_count*size_of(rawptr))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -81,7 +76,7 @@ raw_soa_footer :: proc{
|
|||||||
|
|
||||||
|
|
||||||
@(builtin, require_results)
|
@(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 {
|
if length <= 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -98,11 +93,11 @@ make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, alloc
|
|||||||
ti = type_info_base(ti)
|
ti = type_info_base(ti)
|
||||||
si := &ti.variant.(Type_Info_Struct)
|
si := &ti.variant.(Type_Info_Struct)
|
||||||
|
|
||||||
field_count := uintptr(intrinsics.type_struct_field_count(E))
|
field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E))
|
||||||
|
|
||||||
total_size := 0
|
total_size := 0
|
||||||
for i in 0..<field_count {
|
for i in 0..<field_count {
|
||||||
type := si.types[i].variant.(Type_Info_Pointer).elem
|
type := si.types[i].variant.(Type_Info_Multi_Pointer).elem
|
||||||
total_size += type.size * length
|
total_size += type.size * length
|
||||||
total_size = align_forward_int(total_size, max_align)
|
total_size = align_forward_int(total_size, max_align)
|
||||||
}
|
}
|
||||||
@@ -126,7 +121,7 @@ make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, alloc
|
|||||||
data := uintptr(&array)
|
data := uintptr(&array)
|
||||||
offset := 0
|
offset := 0
|
||||||
for i in 0..<field_count {
|
for i in 0..<field_count {
|
||||||
type := si.types[i].variant.(Type_Info_Pointer).elem
|
type := si.types[i].variant.(Type_Info_Multi_Pointer).elem
|
||||||
|
|
||||||
offset = align_forward_int(offset, max_align)
|
offset = align_forward_int(offset, max_align)
|
||||||
|
|
||||||
@@ -140,20 +135,22 @@ make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, alloc
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(builtin, require_results)
|
@(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)
|
return make_soa_aligned(T, length, align_of(E), allocator, loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(builtin, require_results)
|
@(builtin, require_results)
|
||||||
make_soa_dynamic_array :: proc($T: typeid/#soa[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
make_soa_dynamic_array :: proc($T: typeid/#soa[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
||||||
context.allocator = allocator
|
context.allocator = allocator
|
||||||
reserve_soa(&array, DEFAULT_RESERVE_CAPACITY, loc) or_return
|
array.allocator = allocator
|
||||||
|
reserve_soa(&array, 0, loc) or_return
|
||||||
return array, nil
|
return array, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@(builtin, require_results)
|
@(builtin, require_results)
|
||||||
make_soa_dynamic_array_len :: proc($T: typeid/#soa[dynamic]$E, #any_int length: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
make_soa_dynamic_array_len :: proc($T: typeid/#soa[dynamic]$E, #any_int length: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
||||||
context.allocator = allocator
|
context.allocator = allocator
|
||||||
|
array.allocator = allocator
|
||||||
resize_soa(&array, length, loc) or_return
|
resize_soa(&array, length, loc) or_return
|
||||||
return array, nil
|
return array, nil
|
||||||
}
|
}
|
||||||
@@ -177,7 +174,7 @@ make_soa :: proc{
|
|||||||
|
|
||||||
|
|
||||||
@builtin
|
@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 {
|
if array == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -188,7 +185,27 @@ resize_soa :: proc(array: ^$T/#soa[dynamic]$E, length: int, loc := #caller_locat
|
|||||||
}
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: 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
|
||||||
|
}
|
||||||
|
non_zero_reserve_soa(array, length, loc) or_return
|
||||||
|
footer := raw_soa_footer(array)
|
||||||
|
footer.len = length
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
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, #any_int capacity: int, loc := #caller_location) -> Allocator_Error {
|
||||||
|
return _reserve_soa(array, capacity, false, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
_reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, zero_memory: bool, loc := #caller_location) -> Allocator_Error {
|
||||||
if array == nil {
|
if array == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -213,12 +230,7 @@ reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_lo
|
|||||||
ti = type_info_base(ti)
|
ti = type_info_base(ti)
|
||||||
si := &ti.variant.(Type_Info_Struct)
|
si := &ti.variant.(Type_Info_Struct)
|
||||||
|
|
||||||
field_count: uintptr
|
field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E))
|
||||||
when intrinsics.type_is_array(E) {
|
|
||||||
field_count = len(E)
|
|
||||||
} else {
|
|
||||||
field_count = uintptr(intrinsics.type_struct_field_count(E))
|
|
||||||
}
|
|
||||||
assert(footer.cap == old_cap)
|
assert(footer.cap == old_cap)
|
||||||
|
|
||||||
old_size := 0
|
old_size := 0
|
||||||
@@ -226,7 +238,7 @@ reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_lo
|
|||||||
|
|
||||||
max_align :: align_of(E)
|
max_align :: align_of(E)
|
||||||
for i in 0..<field_count {
|
for i in 0..<field_count {
|
||||||
type := si.types[i].variant.(Type_Info_Pointer).elem
|
type := si.types[i].variant.(Type_Info_Multi_Pointer).elem
|
||||||
|
|
||||||
old_size += type.size * old_cap
|
old_size += type.size * old_cap
|
||||||
new_size += type.size * capacity
|
new_size += type.size * capacity
|
||||||
@@ -238,7 +250,7 @@ reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_lo
|
|||||||
old_data := (^rawptr)(array)^
|
old_data := (^rawptr)(array)^
|
||||||
|
|
||||||
new_bytes := array.allocator.procedure(
|
new_bytes := array.allocator.procedure(
|
||||||
array.allocator.data, .Alloc, new_size, max_align,
|
array.allocator.data, .Alloc if zero_memory else .Alloc_Non_Zeroed, new_size, max_align,
|
||||||
nil, old_size, loc,
|
nil, old_size, loc,
|
||||||
) or_return
|
) or_return
|
||||||
new_data := raw_data(new_bytes)
|
new_data := raw_data(new_bytes)
|
||||||
@@ -249,7 +261,7 @@ reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_lo
|
|||||||
old_offset := 0
|
old_offset := 0
|
||||||
new_offset := 0
|
new_offset := 0
|
||||||
for i in 0..<field_count {
|
for i in 0..<field_count {
|
||||||
type := si.types[i].variant.(Type_Info_Pointer).elem
|
type := si.types[i].variant.(Type_Info_Multi_Pointer).elem
|
||||||
|
|
||||||
old_offset = align_forward_int(old_offset, max_align)
|
old_offset = align_forward_int(old_offset, max_align)
|
||||||
new_offset = align_forward_int(new_offset, max_align)
|
new_offset = align_forward_int(new_offset, max_align)
|
||||||
@@ -273,29 +285,35 @@ reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_lo
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
return _append_soa_elem(array, true, arg, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
non_zero_append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
return _append_soa_elem(array, false, arg, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
_append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, zero_memory: bool, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
if array == nil {
|
if array == nil {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if cap(array) <= len(array) + 1 {
|
if cap(array) <= len(array) + 1 {
|
||||||
cap := 2 * cap(array) + 8
|
// Same behavior as append_soa_elems but there's only one arg, so we always just add DEFAULT_DYNAMIC_ARRAY_CAPACITY.
|
||||||
err = reserve_soa(array, cap, loc) // do not 'or_return' here as it could be a partial success
|
cap := 2 * cap(array) + DEFAULT_DYNAMIC_ARRAY_CAPACITY
|
||||||
|
err = _reserve_soa(array, cap, zero_memory, loc) // do not 'or_return' here as it could be a partial success
|
||||||
}
|
}
|
||||||
|
|
||||||
footer := raw_soa_footer(array)
|
footer := raw_soa_footer(array)
|
||||||
|
|
||||||
if size_of(E) > 0 && cap(array)-len(array) > 0 {
|
if size_of(E) > 0 && cap(array)-len(array) > 0 {
|
||||||
ti := type_info_of(typeid_of(T))
|
ti := type_info_of(T)
|
||||||
ti = type_info_base(ti)
|
ti = type_info_base(ti)
|
||||||
si := &ti.variant.(Type_Info_Struct)
|
si := &ti.variant.(Type_Info_Struct)
|
||||||
field_count: uintptr
|
field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E))
|
||||||
when intrinsics.type_is_array(E) {
|
|
||||||
field_count = len(E)
|
|
||||||
} else {
|
|
||||||
field_count = uintptr(intrinsics.type_struct_field_count(E))
|
|
||||||
}
|
|
||||||
|
|
||||||
data := (^rawptr)(array)^
|
data := (^rawptr)(array)^
|
||||||
|
|
||||||
@@ -307,7 +325,7 @@ append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, arg: E, loc := #caller_locat
|
|||||||
|
|
||||||
max_align :: align_of(E)
|
max_align :: align_of(E)
|
||||||
for i in 0..<field_count {
|
for i in 0..<field_count {
|
||||||
type := si.types[i].variant.(Type_Info_Pointer).elem
|
type := si.types[i].variant.(Type_Info_Multi_Pointer).elem
|
||||||
|
|
||||||
soa_offset = align_forward_int(soa_offset, max_align)
|
soa_offset = align_forward_int(soa_offset, max_align)
|
||||||
item_offset = align_forward_int(item_offset, type.align)
|
item_offset = align_forward_int(item_offset, type.align)
|
||||||
@@ -326,7 +344,17 @@ append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, arg: E, loc := #caller_locat
|
|||||||
}
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
return _append_soa_elems(array, true, args=args, loc=loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
non_zero_append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
return _append_soa_elems(array, false, args=args, loc=loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, zero_memory: bool, #no_broadcast args: []E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
if array == nil {
|
if array == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -337,8 +365,8 @@ append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_l
|
|||||||
}
|
}
|
||||||
|
|
||||||
if cap(array) <= len(array)+arg_len {
|
if cap(array) <= len(array)+arg_len {
|
||||||
cap := 2 * cap(array) + max(8, arg_len)
|
cap := 2 * cap(array) + max(DEFAULT_DYNAMIC_ARRAY_CAPACITY, arg_len)
|
||||||
err = reserve_soa(array, cap, loc) // do not 'or_return' here as it could be a partial success
|
err = _reserve_soa(array, cap, zero_memory, loc) // do not 'or_return' here as it could be a partial success
|
||||||
}
|
}
|
||||||
arg_len = min(cap(array)-len(array), arg_len)
|
arg_len = min(cap(array)-len(array), arg_len)
|
||||||
|
|
||||||
@@ -347,7 +375,7 @@ append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_l
|
|||||||
ti := type_info_of(typeid_of(T))
|
ti := type_info_of(typeid_of(T))
|
||||||
ti = type_info_base(ti)
|
ti = type_info_base(ti)
|
||||||
si := &ti.variant.(Type_Info_Struct)
|
si := &ti.variant.(Type_Info_Struct)
|
||||||
field_count := uintptr(intrinsics.type_struct_field_count(E))
|
field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E))
|
||||||
|
|
||||||
data := (^rawptr)(array)^
|
data := (^rawptr)(array)^
|
||||||
|
|
||||||
@@ -358,7 +386,7 @@ append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_l
|
|||||||
|
|
||||||
max_align :: align_of(E)
|
max_align :: align_of(E)
|
||||||
for i in 0..<field_count {
|
for i in 0..<field_count {
|
||||||
type := si.types[i].variant.(Type_Info_Pointer).elem
|
type := si.types[i].variant.(Type_Info_Multi_Pointer).elem
|
||||||
|
|
||||||
soa_offset = align_forward_int(soa_offset, max_align)
|
soa_offset = align_forward_int(soa_offset, max_align)
|
||||||
item_offset = align_forward_int(item_offset, type.align)
|
item_offset = align_forward_int(item_offset, type.align)
|
||||||
@@ -389,7 +417,8 @@ append_soa :: proc{
|
|||||||
|
|
||||||
|
|
||||||
delete_soa_slice :: proc(array: $T/#soa[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
delete_soa_slice :: proc(array: $T/#soa[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||||
when intrinsics.type_struct_field_count(E) != 0 {
|
field_count :: len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)
|
||||||
|
when field_count != 0 {
|
||||||
array := array
|
array := array
|
||||||
ptr := (^rawptr)(&array)^
|
ptr := (^rawptr)(&array)^
|
||||||
free(ptr, allocator, loc) or_return
|
free(ptr, allocator, loc) or_return
|
||||||
@@ -398,7 +427,8 @@ delete_soa_slice :: proc(array: $T/#soa[]$E, allocator := context.allocator, loc
|
|||||||
}
|
}
|
||||||
|
|
||||||
delete_soa_dynamic_array :: proc(array: $T/#soa[dynamic]$E, loc := #caller_location) -> Allocator_Error {
|
delete_soa_dynamic_array :: proc(array: $T/#soa[dynamic]$E, loc := #caller_location) -> Allocator_Error {
|
||||||
when intrinsics.type_struct_field_count(E) != 0 {
|
field_count :: len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)
|
||||||
|
when field_count != 0 {
|
||||||
array := array
|
array := array
|
||||||
ptr := (^rawptr)(&array)^
|
ptr := (^rawptr)(&array)^
|
||||||
footer := raw_soa_footer(&array)
|
footer := raw_soa_footer(&array)
|
||||||
@@ -416,7 +446,8 @@ delete_soa :: proc{
|
|||||||
|
|
||||||
|
|
||||||
clear_soa_dynamic_array :: proc(array: ^$T/#soa[dynamic]$E) {
|
clear_soa_dynamic_array :: proc(array: ^$T/#soa[dynamic]$E) {
|
||||||
when intrinsics.type_struct_field_count(E) != 0 {
|
field_count :: len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)
|
||||||
|
when field_count != 0 {
|
||||||
footer := raw_soa_footer(array)
|
footer := raw_soa_footer(array)
|
||||||
footer.len = 0
|
footer.len = 0
|
||||||
}
|
}
|
||||||
@@ -426,3 +457,81 @@ clear_soa_dynamic_array :: proc(array: ^$T/#soa[dynamic]$E) {
|
|||||||
clear_soa :: proc{
|
clear_soa :: proc{
|
||||||
clear_soa_dynamic_array,
|
clear_soa_dynamic_array,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Converts soa slice into a soa dynamic array without cloning or allocating memory
|
||||||
|
@(require_results)
|
||||||
|
into_dynamic_soa :: proc(array: $T/#soa[]$E) -> #soa[dynamic]E {
|
||||||
|
d: #soa[dynamic]E
|
||||||
|
footer := raw_soa_footer_dynamic_array(&d)
|
||||||
|
footer^ = {
|
||||||
|
cap = len(array),
|
||||||
|
len = 0,
|
||||||
|
allocator = nil_allocator(),
|
||||||
|
}
|
||||||
|
|
||||||
|
field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E))
|
||||||
|
|
||||||
|
array := array
|
||||||
|
dynamic_data := ([^]rawptr)(&d)[:field_count]
|
||||||
|
slice_data := ([^]rawptr)(&array)[:field_count]
|
||||||
|
copy(dynamic_data, slice_data)
|
||||||
|
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
// `unordered_remove_soa` removed the element at the specified `index`. It does so by replacing the current end value
|
||||||
|
// with the old value, and reducing the length of the dynamic array by 1.
|
||||||
|
//
|
||||||
|
// Note: This is an O(1) operation.
|
||||||
|
// 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, #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))
|
||||||
|
ti = type_info_base(ti)
|
||||||
|
si := &ti.variant.(Type_Info_Struct)
|
||||||
|
|
||||||
|
field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E))
|
||||||
|
|
||||||
|
data := uintptr(array)
|
||||||
|
for i in 0..<field_count {
|
||||||
|
type := si.types[i].variant.(Type_Info_Multi_Pointer).elem
|
||||||
|
|
||||||
|
offset := rawptr((^uintptr)(data)^ + uintptr(index*type.size))
|
||||||
|
final := rawptr((^uintptr)(data)^ + uintptr((len(array)-1)*type.size))
|
||||||
|
mem_copy(offset, final, type.size)
|
||||||
|
data += size_of(rawptr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
raw_soa_footer_dynamic_array(array).len -= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// `ordered_remove_soa` removed the element at the specified `index` whilst keeping the order of the other elements.
|
||||||
|
//
|
||||||
|
// Note: This is an O(N) operation.
|
||||||
|
// 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, #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))
|
||||||
|
ti = type_info_base(ti)
|
||||||
|
si := &ti.variant.(Type_Info_Struct)
|
||||||
|
|
||||||
|
field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E))
|
||||||
|
|
||||||
|
data := uintptr(array)
|
||||||
|
for i in 0..<field_count {
|
||||||
|
type := si.types[i].variant.(Type_Info_Multi_Pointer).elem
|
||||||
|
|
||||||
|
offset := (^uintptr)(data)^ + uintptr(index*type.size)
|
||||||
|
length := type.size*(len(array) - index - 1)
|
||||||
|
mem_copy(rawptr(offset), rawptr(offset + uintptr(type.size)), length)
|
||||||
|
data += size_of(rawptr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
raw_soa_footer_dynamic_array(array).len -= 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
|
||||||
|
default_allocator_proc :: nil_allocator_proc
|
||||||
|
default_allocator :: nil_allocator
|
||||||
|
} else when ODIN_DEFAULT_TO_PANIC_ALLOCATOR {
|
||||||
|
default_allocator_proc :: panic_allocator_proc
|
||||||
|
default_allocator :: panic_allocator
|
||||||
|
} else when ODIN_OS != .Orca && (ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32) {
|
||||||
|
default_allocator :: default_wasm_allocator
|
||||||
|
default_allocator_proc :: wasm_allocator_proc
|
||||||
|
} else {
|
||||||
|
default_allocator :: heap_allocator
|
||||||
|
default_allocator_proc :: heap_allocator_proc
|
||||||
|
}
|
||||||
+8
-14
@@ -1,8 +1,8 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||||
size, alignment: int,
|
size, alignment: int,
|
||||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||||
switch mode {
|
switch mode {
|
||||||
case .Alloc, .Alloc_Non_Zeroed:
|
case .Alloc, .Alloc_Non_Zeroed:
|
||||||
return nil, .Out_Of_Memory
|
return nil, .Out_Of_Memory
|
||||||
@@ -10,7 +10,7 @@ nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
return nil, .None
|
return nil, .None
|
||||||
case .Free_All:
|
case .Free_All:
|
||||||
return nil, .Mode_Not_Implemented
|
return nil, .Mode_Not_Implemented
|
||||||
case .Resize:
|
case .Resize, .Resize_Non_Zeroed:
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
return nil, .None
|
return nil, .None
|
||||||
}
|
}
|
||||||
@@ -31,14 +31,6 @@ nil_allocator :: proc() -> Allocator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
when ODIN_OS == .Freestanding {
|
|
||||||
default_allocator_proc :: nil_allocator_proc
|
|
||||||
default_allocator :: nil_allocator
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||||
size, alignment: int,
|
size, alignment: int,
|
||||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||||
@@ -55,6 +47,10 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
if size > 0 {
|
if size > 0 {
|
||||||
panic("panic allocator, .Resize called", loc=loc)
|
panic("panic allocator, .Resize called", loc=loc)
|
||||||
}
|
}
|
||||||
|
case .Resize_Non_Zeroed:
|
||||||
|
if size > 0 {
|
||||||
|
panic("panic allocator, .Alloc_Non_Zeroed called", loc=loc)
|
||||||
|
}
|
||||||
case .Free:
|
case .Free:
|
||||||
if old_memory != nil {
|
if old_memory != nil {
|
||||||
panic("panic allocator, .Free called", loc=loc)
|
panic("panic allocator, .Free called", loc=loc)
|
||||||
@@ -78,9 +74,7 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
|
|
||||||
panic_allocator :: proc() -> Allocator {
|
panic_allocator :: proc() -> Allocator {
|
||||||
return Allocator{
|
return Allocator{
|
||||||
procedure = nil_allocator_proc,
|
procedure = panic_allocator_proc,
|
||||||
data = nil,
|
data = nil,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
+14
-12
@@ -1,6 +1,6 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE :: uint(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE)
|
DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE :: uint(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE)
|
||||||
|
|
||||||
@@ -12,6 +12,8 @@ Memory_Block :: struct {
|
|||||||
capacity: uint,
|
capacity: uint,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: This is a growing arena that is only used for the default temp allocator.
|
||||||
|
// For your own growing arena needs, prefer `Arena` from `core:mem/virtual`.
|
||||||
Arena :: struct {
|
Arena :: struct {
|
||||||
backing_allocator: Allocator,
|
backing_allocator: Allocator,
|
||||||
curr_block: ^Memory_Block,
|
curr_block: ^Memory_Block,
|
||||||
@@ -28,11 +30,11 @@ safe_add :: #force_inline proc "contextless" (x, y: uint) -> (uint, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
memory_block_alloc :: proc(allocator: Allocator, capacity: uint, loc := #caller_location) -> (block: ^Memory_Block, err: Allocator_Error) {
|
memory_block_alloc :: proc(allocator: Allocator, capacity: uint, alignment: uint, loc := #caller_location) -> (block: ^Memory_Block, err: Allocator_Error) {
|
||||||
total_size := uint(capacity + size_of(Memory_Block))
|
total_size := uint(capacity + max(alignment, size_of(Memory_Block)))
|
||||||
base_offset := uintptr(size_of(Memory_Block))
|
base_offset := uintptr(max(alignment, size_of(Memory_Block)))
|
||||||
|
|
||||||
min_alignment: int = max(16, align_of(Memory_Block))
|
min_alignment: int = max(16, align_of(Memory_Block), int(alignment))
|
||||||
data := mem_alloc(int(total_size), min_alignment, allocator, loc) or_return
|
data := mem_alloc(int(total_size), min_alignment, allocator, loc) or_return
|
||||||
block = (^Memory_Block)(raw_data(data))
|
block = (^Memory_Block)(raw_data(data))
|
||||||
end := uintptr(raw_data(data)[len(data):])
|
end := uintptr(raw_data(data)[len(data):])
|
||||||
@@ -103,19 +105,19 @@ arena_alloc :: proc(arena: ^Arena, size, alignment: uint, loc := #caller_locatio
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if arena.curr_block == nil || (safe_add(arena.curr_block.used, size) or_else 0) > arena.curr_block.capacity {
|
needed := align_forward_uint(size, alignment)
|
||||||
size = align_forward_uint(size, alignment)
|
if arena.curr_block == nil || (safe_add(arena.curr_block.used, needed) or_else 0) > arena.curr_block.capacity {
|
||||||
if arena.minimum_block_size == 0 {
|
if arena.minimum_block_size == 0 {
|
||||||
arena.minimum_block_size = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE
|
arena.minimum_block_size = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE
|
||||||
}
|
}
|
||||||
|
|
||||||
block_size := max(size, arena.minimum_block_size)
|
block_size := max(needed, arena.minimum_block_size)
|
||||||
|
|
||||||
if arena.backing_allocator.procedure == nil {
|
if arena.backing_allocator.procedure == nil {
|
||||||
arena.backing_allocator = default_allocator()
|
arena.backing_allocator = default_allocator()
|
||||||
}
|
}
|
||||||
|
|
||||||
new_block := memory_block_alloc(arena.backing_allocator, block_size, loc) or_return
|
new_block := memory_block_alloc(arena.backing_allocator, block_size, alignment, loc) or_return
|
||||||
new_block.prev = arena.curr_block
|
new_block.prev = arena.curr_block
|
||||||
arena.curr_block = new_block
|
arena.curr_block = new_block
|
||||||
arena.total_capacity += new_block.capacity
|
arena.total_capacity += new_block.capacity
|
||||||
@@ -127,14 +129,14 @@ arena_alloc :: proc(arena: ^Arena, size, alignment: uint, loc := #caller_locatio
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// `arena_init` will initialize the arena with a usuable block.
|
// `arena_init` will initialize the arena with a usable block.
|
||||||
// This procedure is not necessary to use the Arena as the default zero as `arena_alloc` will set things up if necessary
|
// This procedure is not necessary to use the Arena as the default zero as `arena_alloc` will set things up if necessary
|
||||||
@(require_results)
|
@(require_results)
|
||||||
arena_init :: proc(arena: ^Arena, size: uint, backing_allocator: Allocator, loc := #caller_location) -> Allocator_Error {
|
arena_init :: proc(arena: ^Arena, size: uint, backing_allocator: Allocator, loc := #caller_location) -> Allocator_Error {
|
||||||
arena^ = {}
|
arena^ = {}
|
||||||
arena.backing_allocator = backing_allocator
|
arena.backing_allocator = backing_allocator
|
||||||
arena.minimum_block_size = max(size, 1<<12) // minimum block size of 4 KiB
|
arena.minimum_block_size = max(size, 1<<12) // minimum block size of 4 KiB
|
||||||
new_block := memory_block_alloc(arena.backing_allocator, arena.minimum_block_size, loc) or_return
|
new_block := memory_block_alloc(arena.backing_allocator, arena.minimum_block_size, 0, loc) or_return
|
||||||
arena.curr_block = new_block
|
arena.curr_block = new_block
|
||||||
arena.total_capacity += new_block.capacity
|
arena.total_capacity += new_block.capacity
|
||||||
return nil
|
return nil
|
||||||
@@ -195,7 +197,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
err = .Mode_Not_Implemented
|
err = .Mode_Not_Implemented
|
||||||
case .Free_All:
|
case .Free_All:
|
||||||
arena_free_all(arena, location)
|
arena_free_all(arena, location)
|
||||||
case .Resize:
|
case .Resize, .Resize_Non_Zeroed:
|
||||||
old_data := ([^]byte)(old_memory)
|
old_data := ([^]byte)(old_memory)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE: int : #config(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, 4 * Megabyte)
|
DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE: int : #config(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, 4 * Megabyte)
|
||||||
NO_DEFAULT_TEMP_ALLOCATOR: bool : ODIN_OS == .Freestanding || ODIN_OS == .JS || ODIN_DEFAULT_TO_NIL_ALLOCATOR
|
NO_DEFAULT_TEMP_ALLOCATOR: bool : ODIN_OS == .Freestanding || ODIN_DEFAULT_TO_NIL_ALLOCATOR
|
||||||
|
|
||||||
when NO_DEFAULT_TEMP_ALLOCATOR {
|
when NO_DEFAULT_TEMP_ALLOCATOR {
|
||||||
Default_Temp_Allocator :: struct {}
|
Default_Temp_Allocator :: struct {}
|
||||||
@@ -44,7 +44,7 @@ memcpy
|
|||||||
memove
|
memove
|
||||||
|
|
||||||
|
|
||||||
## Procedures required by the LLVM backend
|
## Procedures required by the LLVM backend if u128/i128 is used
|
||||||
umodti3
|
umodti3
|
||||||
udivti3
|
udivti3
|
||||||
modti3
|
modti3
|
||||||
@@ -59,11 +59,12 @@ truncdfhf2
|
|||||||
gnu_h2f_ieee
|
gnu_h2f_ieee
|
||||||
gnu_f2h_ieee
|
gnu_f2h_ieee
|
||||||
extendhfsf2
|
extendhfsf2
|
||||||
|
|
||||||
|
## Procedures required by the LLVM backend if f16 is used
|
||||||
__ashlti3 // wasm specific
|
__ashlti3 // wasm specific
|
||||||
__multi3 // wasm specific
|
__multi3 // wasm specific
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Required an entry point is defined (i.e. 'main')
|
## Required an entry point is defined (i.e. 'main')
|
||||||
|
|
||||||
args__
|
args__
|
||||||
@@ -156,7 +157,7 @@ __dynamic_map_get // dynamic map calls
|
|||||||
__dynamic_map_set // dynamic map calls
|
__dynamic_map_set // dynamic map calls
|
||||||
|
|
||||||
|
|
||||||
## Dynamic literals ([dymamic]T and map[K]V) (can be disabled with -no-dynamic-literals)
|
## Dynamic literals ([dynamic]T and map[K]V) (can be disabled with -no-dynamic-literals)
|
||||||
|
|
||||||
__dynamic_array_reserve
|
__dynamic_array_reserve
|
||||||
__dynamic_array_append
|
__dynamic_array_append
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
_ :: intrinsics
|
_ :: intrinsics
|
||||||
|
|
||||||
// High performance, cache-friendly, open-addressed Robin Hood hashing hash map
|
// High performance, cache-friendly, open-addressed Robin Hood hashing hash map
|
||||||
@@ -44,7 +44,7 @@ _ :: intrinsics
|
|||||||
MAP_LOAD_FACTOR :: 75
|
MAP_LOAD_FACTOR :: 75
|
||||||
|
|
||||||
// Minimum log2 capacity.
|
// Minimum log2 capacity.
|
||||||
MAP_MIN_LOG2_CAPACITY :: 6 // 64 elements
|
MAP_MIN_LOG2_CAPACITY :: 3 // 8 elements
|
||||||
|
|
||||||
// Has to be less than 100% though.
|
// Has to be less than 100% though.
|
||||||
#assert(MAP_LOAD_FACTOR < 100)
|
#assert(MAP_LOAD_FACTOR < 100)
|
||||||
@@ -158,21 +158,21 @@ map_cell_index_static :: #force_inline proc "contextless" (cells: [^]Map_Cell($T
|
|||||||
} else when (N & (N - 1)) == 0 && N <= 8*size_of(uintptr) {
|
} else when (N & (N - 1)) == 0 && N <= 8*size_of(uintptr) {
|
||||||
// Likely case, N is a power of two because T is a power of two.
|
// Likely case, N is a power of two because T is a power of two.
|
||||||
|
|
||||||
|
// Unique case, no need to index data here since only one element.
|
||||||
|
when N == 1 {
|
||||||
|
return &cells[index].data[0]
|
||||||
|
}
|
||||||
|
|
||||||
// Compute the integer log 2 of N, this is the shift amount to index the
|
// Compute the integer log 2 of N, this is the shift amount to index the
|
||||||
// correct cell. Odin's intrinsics.count_leading_zeros does not produce a
|
// correct cell. Odin's intrinsics.count_leading_zeros does not produce a
|
||||||
// constant, hence this approach. We only need to check up to N = 64.
|
// constant, hence this approach. We only need to check up to N = 64.
|
||||||
SHIFT :: 1 when N < 2 else
|
SHIFT :: 1 when N == 2 else
|
||||||
2 when N < 4 else
|
2 when N == 4 else
|
||||||
3 when N < 8 else
|
3 when N == 8 else
|
||||||
4 when N < 16 else
|
4 when N == 16 else
|
||||||
5 when N < 32 else 6
|
5 when N == 32 else 6
|
||||||
#assert(SHIFT <= MAP_CACHE_LINE_LOG2)
|
#assert(SHIFT <= MAP_CACHE_LINE_LOG2)
|
||||||
// Unique case, no need to index data here since only one element.
|
return &cells[index >> SHIFT].data[index & (N - 1)]
|
||||||
when N == 1 {
|
|
||||||
return &cells[index >> SHIFT].data[0]
|
|
||||||
} else {
|
|
||||||
return &cells[index >> SHIFT].data[index & (N - 1)]
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Least likely (and worst case), we pay for a division operation but we
|
// Least likely (and worst case), we pay for a division operation but we
|
||||||
// assume the compiler does not actually generate a division. N will be in the
|
// assume the compiler does not actually generate a division. N will be in the
|
||||||
@@ -333,7 +333,7 @@ map_kvh_data_values_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@(private, require_results)
|
@(require_results)
|
||||||
map_total_allocation_size :: #force_inline proc "contextless" (capacity: uintptr, info: ^Map_Info) -> uintptr {
|
map_total_allocation_size :: #force_inline proc "contextless" (capacity: uintptr, info: ^Map_Info) -> uintptr {
|
||||||
round :: #force_inline proc "contextless" (value: uintptr) -> uintptr {
|
round :: #force_inline proc "contextless" (value: uintptr) -> uintptr {
|
||||||
CACHE_MASK :: MAP_CACHE_LINE_SIZE - 1
|
CACHE_MASK :: MAP_CACHE_LINE_SIZE - 1
|
||||||
@@ -350,6 +350,12 @@ map_total_allocation_size :: #force_inline proc "contextless" (capacity: uintptr
|
|||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
|
map_total_allocation_size_from_value :: #force_inline proc "contextless" (m: $M/map[$K]$V) -> uintptr {
|
||||||
|
return map_total_allocation_size(uintptr(cap(m)), map_info(M))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// The only procedure which needs access to the context is the one which allocates the map.
|
// The only procedure which needs access to the context is the one which allocates the map.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
map_alloc_dynamic :: proc "odin" (info: ^Map_Info, log2_capacity: uintptr, allocator := context.allocator, loc := #caller_location) -> (result: Raw_Map, err: Allocator_Error) {
|
map_alloc_dynamic :: proc "odin" (info: ^Map_Info, log2_capacity: uintptr, allocator := context.allocator, loc := #caller_location) -> (result: Raw_Map, err: Allocator_Error) {
|
||||||
@@ -391,7 +397,8 @@ map_alloc_dynamic :: proc "odin" (info: ^Map_Info, log2_capacity: uintptr, alloc
|
|||||||
// arrays to reduce variance. This swapping can only be done with memcpy since
|
// arrays to reduce variance. This swapping can only be done with memcpy since
|
||||||
// there is no type information.
|
// there is no type information.
|
||||||
//
|
//
|
||||||
// This procedure returns the address of the just inserted value.
|
// This procedure returns the address of the just inserted value, and will
|
||||||
|
// return 'nil' if there was no room to insert the entry
|
||||||
@(require_results)
|
@(require_results)
|
||||||
map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, h: Map_Hash, ik: uintptr, iv: uintptr) -> (result: uintptr) {
|
map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, h: Map_Hash, ik: uintptr, iv: uintptr) -> (result: uintptr) {
|
||||||
h := h
|
h := h
|
||||||
@@ -415,6 +422,11 @@ map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^
|
|||||||
tv := map_cell_index_dynamic(sv, info.vs, 1)
|
tv := map_cell_index_dynamic(sv, info.vs, 1)
|
||||||
|
|
||||||
swap_loop: for {
|
swap_loop: for {
|
||||||
|
if distance > mask {
|
||||||
|
// Failed to find an empty slot and prevent infinite loop
|
||||||
|
panic("unable to insert into a map")
|
||||||
|
}
|
||||||
|
|
||||||
element_hash := hs[pos]
|
element_hash := hs[pos]
|
||||||
|
|
||||||
if map_hash_is_empty(element_hash) {
|
if map_hash_is_empty(element_hash) {
|
||||||
@@ -565,7 +577,7 @@ map_grow_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Inf
|
|||||||
|
|
||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
map_reserve_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, new_capacity: uintptr, loc := #caller_location) -> Allocator_Error {
|
map_reserve_dynamic :: #force_no_inline proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, new_capacity: uintptr, loc := #caller_location) -> Allocator_Error {
|
||||||
@(require_results)
|
@(require_results)
|
||||||
ceil_log2 :: #force_inline proc "contextless" (x: uintptr) -> uintptr {
|
ceil_log2 :: #force_inline proc "contextless" (x: uintptr) -> uintptr {
|
||||||
z := intrinsics.count_leading_zeros(x)
|
z := intrinsics.count_leading_zeros(x)
|
||||||
@@ -629,7 +641,7 @@ map_reserve_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_
|
|||||||
|
|
||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
map_shrink_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) {
|
map_shrink_dynamic :: #force_no_inline proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) {
|
||||||
if m.allocator.procedure == nil {
|
if m.allocator.procedure == nil {
|
||||||
m.allocator = context.allocator
|
m.allocator = context.allocator
|
||||||
}
|
}
|
||||||
@@ -676,7 +688,7 @@ map_shrink_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_I
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
map_free_dynamic :: proc "odin" (m: Raw_Map, info: ^Map_Info, loc := #caller_location) -> Allocator_Error {
|
map_free_dynamic :: #force_no_inline proc "odin" (m: Raw_Map, info: ^Map_Info, loc := #caller_location) -> Allocator_Error {
|
||||||
ptr := rawptr(map_data(m))
|
ptr := rawptr(map_data(m))
|
||||||
size := int(map_total_allocation_size(uintptr(map_cap(m)), info))
|
size := int(map_total_allocation_size(uintptr(map_cap(m)), info))
|
||||||
err := mem_free_with_size(ptr, size, m.allocator, loc)
|
err := mem_free_with_size(ptr, size, m.allocator, loc)
|
||||||
@@ -688,7 +700,7 @@ map_free_dynamic :: proc "odin" (m: Raw_Map, info: ^Map_Info, loc := #caller_loc
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
map_lookup_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info, k: uintptr) -> (index: uintptr, ok: bool) {
|
map_lookup_dynamic :: #force_no_inline proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info, k: uintptr) -> (index: uintptr, ok: bool) {
|
||||||
if map_len(m) == 0 {
|
if map_len(m) == 0 {
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
@@ -711,7 +723,7 @@ map_lookup_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
@(require_results)
|
@(require_results)
|
||||||
map_exists_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info, k: uintptr) -> (ok: bool) {
|
map_exists_dynamic :: #force_no_inline proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info, k: uintptr) -> (ok: bool) {
|
||||||
if map_len(m) == 0 {
|
if map_len(m) == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -737,7 +749,7 @@ map_exists_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info,
|
|||||||
|
|
||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
map_erase_dynamic :: #force_inline proc "contextless" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, k: uintptr) -> (old_k, old_v: uintptr, ok: bool) {
|
map_erase_dynamic :: #force_no_inline proc "contextless" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, k: uintptr) -> (old_k, old_v: uintptr, ok: bool) {
|
||||||
index := map_lookup_dynamic(m^, info, k) or_return
|
index := map_lookup_dynamic(m^, info, k) or_return
|
||||||
ks, vs, hs, _, _ := map_kvh_data_dynamic(m^, info)
|
ks, vs, hs, _, _ := map_kvh_data_dynamic(m^, info)
|
||||||
hs[index] |= TOMBSTONE_MASK
|
hs[index] |= TOMBSTONE_MASK
|
||||||
@@ -841,6 +853,33 @@ __dynamic_map_get :: proc "contextless" (#no_alias m: ^Raw_Map, #no_alias info:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__dynamic_map_get_key_and_value :: proc "contextless" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, h: Map_Hash, key: rawptr) -> (key_ptr, value_ptr: rawptr) {
|
||||||
|
if m.len == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
pos := map_desired_position(m^, h)
|
||||||
|
distance := uintptr(0)
|
||||||
|
mask := (uintptr(1) << map_log2_cap(m^)) - 1
|
||||||
|
ks, vs, hs, _, _ := map_kvh_data_dynamic(m^, info)
|
||||||
|
for {
|
||||||
|
element_hash := hs[pos]
|
||||||
|
if map_hash_is_empty(element_hash) {
|
||||||
|
return nil, nil
|
||||||
|
} else if distance > map_probe_distance(m^, element_hash, pos) {
|
||||||
|
return nil, nil
|
||||||
|
} else if element_hash == h {
|
||||||
|
other_key := rawptr(map_cell_index_dynamic(ks, info.ks, pos))
|
||||||
|
if info.key_equal(key, other_key) {
|
||||||
|
key_ptr = other_key
|
||||||
|
value_ptr = rawptr(map_cell_index_dynamic(vs, info.vs, pos))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pos = (pos + 1) & mask
|
||||||
|
distance += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// IMPORTANT: USED WITHIN THE COMPILER
|
// IMPORTANT: USED WITHIN THE COMPILER
|
||||||
__dynamic_map_check_grow :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, loc := #caller_location) -> (err: Allocator_Error, has_grown: bool) {
|
__dynamic_map_check_grow :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, loc := #caller_location) -> (err: Allocator_Error, has_grown: bool) {
|
||||||
if m.len >= map_resize_threshold(m^) {
|
if m.len >= map_resize_threshold(m^) {
|
||||||
@@ -871,9 +910,60 @@ __dynamic_map_set :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_In
|
|||||||
}
|
}
|
||||||
|
|
||||||
result := map_insert_hash_dynamic(m, info, hash, uintptr(key), uintptr(value))
|
result := map_insert_hash_dynamic(m, info, hash, uintptr(key), uintptr(value))
|
||||||
m.len += 1
|
if result != 0 {
|
||||||
|
m.len += 1
|
||||||
|
}
|
||||||
return rawptr(result)
|
return rawptr(result)
|
||||||
}
|
}
|
||||||
|
__dynamic_map_set_extra_without_hash :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, key, value: rawptr, loc := #caller_location) -> (prev_key_ptr, value_ptr: rawptr) {
|
||||||
|
return __dynamic_map_set_extra(m, info, info.key_hasher(key, map_seed(m^)), key, value, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
__dynamic_map_set_extra :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, hash: Map_Hash, key, value: rawptr, loc := #caller_location) -> (prev_key_ptr, value_ptr: rawptr) {
|
||||||
|
if prev_key_ptr, value_ptr = __dynamic_map_get_key_and_value(m, info, hash, key); value_ptr != nil {
|
||||||
|
intrinsics.mem_copy_non_overlapping(value_ptr, value, info.vs.size_of_type)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := hash
|
||||||
|
err, has_grown := __dynamic_map_check_grow(m, info, loc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if has_grown {
|
||||||
|
hash = info.key_hasher(key, map_seed(m^))
|
||||||
|
}
|
||||||
|
|
||||||
|
result := map_insert_hash_dynamic(m, info, hash, uintptr(key), uintptr(value))
|
||||||
|
if result != 0 {
|
||||||
|
m.len += 1
|
||||||
|
}
|
||||||
|
return nil, rawptr(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
__dynamic_map_entry :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, key: rawptr, zero: rawptr, loc := #caller_location) -> (key_ptr: rawptr, value_ptr: rawptr, just_inserted: bool, err: Allocator_Error) {
|
||||||
|
hash := info.key_hasher(key, map_seed(m^))
|
||||||
|
|
||||||
|
if key_ptr, value_ptr = __dynamic_map_get_key_and_value(m, info, hash, key); value_ptr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
has_grown: bool
|
||||||
|
if err, has_grown = __dynamic_map_check_grow(m, info, loc); err != nil {
|
||||||
|
return
|
||||||
|
} else if has_grown {
|
||||||
|
hash = info.key_hasher(key, map_seed(m^))
|
||||||
|
}
|
||||||
|
|
||||||
|
value_ptr = rawptr(map_insert_hash_dynamic(m, info, hash, uintptr(key), uintptr(zero)))
|
||||||
|
assert(value_ptr != nil)
|
||||||
|
key_ptr = rawptr(map_cell_index_dynamic(map_data(m^), info.ks, map_desired_position(m^, hash)))
|
||||||
|
|
||||||
|
m.len += 1
|
||||||
|
just_inserted = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// IMPORTANT: USED WITHIN THE COMPILER
|
// IMPORTANT: USED WITHIN THE COMPILER
|
||||||
@(private)
|
@(private)
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
//+private
|
#+private
|
||||||
//+build linux, darwin, freebsd, openbsd
|
#+build linux, darwin, freebsd, openbsd, netbsd, haiku
|
||||||
|
#+no-instrumentation
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
when ODIN_BUILD_MODE == .Dynamic {
|
when ODIN_BUILD_MODE == .Dynamic {
|
||||||
@(link_name="_odin_entry_point", linkage="strong", require/*, link_section=".init"*/)
|
@(link_name="_odin_entry_point", linkage="strong", require/*, link_section=".init"*/)
|
||||||
@@ -26,8 +27,16 @@ when ODIN_BUILD_MODE == .Dynamic {
|
|||||||
// to retrieve argc and argv from the stack
|
// to retrieve argc and argv from the stack
|
||||||
when ODIN_ARCH == .amd64 {
|
when ODIN_ARCH == .amd64 {
|
||||||
@require foreign import entry "entry_unix_no_crt_amd64.asm"
|
@require foreign import entry "entry_unix_no_crt_amd64.asm"
|
||||||
|
SYS_exit :: 60
|
||||||
} else when ODIN_ARCH == .i386 {
|
} else when ODIN_ARCH == .i386 {
|
||||||
@require foreign import entry "entry_unix_no_crt_i386.asm"
|
@require foreign import entry "entry_unix_no_crt_i386.asm"
|
||||||
|
SYS_exit :: 1
|
||||||
|
} else when ODIN_OS == .Darwin && ODIN_ARCH == .arm64 {
|
||||||
|
@require foreign import entry "entry_unix_no_crt_darwin_arm64.asm"
|
||||||
|
SYS_exit :: 1
|
||||||
|
} else when ODIN_ARCH == .riscv64 {
|
||||||
|
@require foreign import entry "entry_unix_no_crt_riscv64.asm"
|
||||||
|
SYS_exit :: 93
|
||||||
}
|
}
|
||||||
@(link_name="_start_odin", linkage="strong", require)
|
@(link_name="_start_odin", linkage="strong", require)
|
||||||
_start_odin :: proc "c" (argc: i32, argv: [^]cstring) -> ! {
|
_start_odin :: proc "c" (argc: i32, argv: [^]cstring) -> ! {
|
||||||
@@ -36,11 +45,7 @@ when ODIN_BUILD_MODE == .Dynamic {
|
|||||||
#force_no_inline _startup_runtime()
|
#force_no_inline _startup_runtime()
|
||||||
intrinsics.__entry_point()
|
intrinsics.__entry_point()
|
||||||
#force_no_inline _cleanup_runtime()
|
#force_no_inline _cleanup_runtime()
|
||||||
when ODIN_ARCH == .amd64 {
|
intrinsics.syscall(SYS_exit, 0)
|
||||||
intrinsics.syscall(/*SYS_exit = */60)
|
|
||||||
} else when ODIN_ARCH == .i386 {
|
|
||||||
intrinsics.syscall(/*SYS_exit = */1)
|
|
||||||
}
|
|
||||||
unreachable()
|
unreachable()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
.section __TEXT,__text
|
||||||
|
|
||||||
|
; NOTE(laytan): this should ideally be the -minimum-os-version flag but there is no nice way of preprocessing assembly in Odin.
|
||||||
|
; 10 seems to be the lowest it goes and I don't see it mess with any targeted os version so this seems fine.
|
||||||
|
.build_version macos, 10, 0
|
||||||
|
|
||||||
|
.extern __start_odin
|
||||||
|
|
||||||
|
.global _main
|
||||||
|
.align 2
|
||||||
|
_main:
|
||||||
|
mov x5, sp ; use x5 as the stack pointer
|
||||||
|
|
||||||
|
str x0, [x5] ; get argc into x0 (kernel passes 32-bit int argc as 64-bits on stack to keep alignment)
|
||||||
|
str x1, [x5, #8] ; get argv into x1
|
||||||
|
|
||||||
|
and sp, x5, #~15 ; force 16-byte alignment of the stack
|
||||||
|
|
||||||
|
bl __start_odin ; call into Odin entry point
|
||||||
|
ret ; should never get here
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
.text
|
||||||
|
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
ld a0, 0(sp)
|
||||||
|
addi a1, sp, 8
|
||||||
|
addi sp, sp, ~15
|
||||||
|
call _start_odin
|
||||||
|
ebreak
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
#+private
|
||||||
|
#+build wasm32, wasm64p32
|
||||||
|
#+no-instrumentation
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
|
||||||
|
when !ODIN_TEST && !ODIN_NO_ENTRY_POINT {
|
||||||
|
when ODIN_OS == .Orca {
|
||||||
|
@(linkage="strong", require, export)
|
||||||
|
oc_on_init :: proc "c" () {
|
||||||
|
context = default_context()
|
||||||
|
#force_no_inline _startup_runtime()
|
||||||
|
intrinsics.__entry_point()
|
||||||
|
}
|
||||||
|
@(linkage="strong", require, export)
|
||||||
|
oc_on_terminate :: proc "c" () {
|
||||||
|
context = default_context()
|
||||||
|
#force_no_inline _cleanup_runtime()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
@(link_name="_start", linkage="strong", require, export)
|
||||||
|
_start :: proc "c" () {
|
||||||
|
context = default_context()
|
||||||
|
|
||||||
|
when ODIN_OS == .WASI {
|
||||||
|
_wasi_setup_args()
|
||||||
|
}
|
||||||
|
|
||||||
|
#force_no_inline _startup_runtime()
|
||||||
|
intrinsics.__entry_point()
|
||||||
|
}
|
||||||
|
@(link_name="_end", linkage="strong", require, export)
|
||||||
|
_end :: proc "c" () {
|
||||||
|
context = default_context()
|
||||||
|
#force_no_inline _cleanup_runtime()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,18 @@
|
|||||||
//+private
|
#+private
|
||||||
//+build windows
|
#+build windows
|
||||||
|
#+no-instrumentation
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
when ODIN_BUILD_MODE == .Dynamic {
|
when ODIN_BUILD_MODE == .Dynamic {
|
||||||
@(link_name="DllMain", linkage="strong", require)
|
@(link_name="DllMain", linkage="strong", require)
|
||||||
DllMain :: proc "stdcall" (hinstDLL: rawptr, fdwReason: u32, lpReserved: rawptr) -> b32 {
|
DllMain :: proc "system" (hinstDLL: rawptr, fdwReason: u32, lpReserved: rawptr) -> b32 {
|
||||||
context = default_context()
|
context = default_context()
|
||||||
|
|
||||||
// Populate Windows DLL-specific global
|
// Populate Windows DLL-specific globals
|
||||||
dll_forward_reason = DLL_Forward_Reason(fdwReason)
|
dll_forward_reason = DLL_Forward_Reason(fdwReason)
|
||||||
|
dll_instance = hinstDLL
|
||||||
|
|
||||||
switch dll_forward_reason {
|
switch dll_forward_reason {
|
||||||
case .Process_Attach:
|
case .Process_Attach:
|
||||||
@@ -28,7 +30,7 @@ when ODIN_BUILD_MODE == .Dynamic {
|
|||||||
} else when !ODIN_TEST && !ODIN_NO_ENTRY_POINT {
|
} else when !ODIN_TEST && !ODIN_NO_ENTRY_POINT {
|
||||||
when ODIN_ARCH == .i386 || ODIN_NO_CRT {
|
when ODIN_ARCH == .i386 || ODIN_NO_CRT {
|
||||||
@(link_name="mainCRTStartup", linkage="strong", require)
|
@(link_name="mainCRTStartup", linkage="strong", require)
|
||||||
mainCRTStartup :: proc "stdcall" () -> i32 {
|
mainCRTStartup :: proc "system" () -> i32 {
|
||||||
context = default_context()
|
context = default_context()
|
||||||
#force_no_inline _startup_runtime()
|
#force_no_inline _startup_runtime()
|
||||||
intrinsics.__entry_point()
|
intrinsics.__entry_point()
|
||||||
@@ -1,27 +1,34 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
|
@(no_instrumentation)
|
||||||
bounds_trap :: proc "contextless" () -> ! {
|
bounds_trap :: proc "contextless" () -> ! {
|
||||||
when ODIN_OS == .Windows {
|
when ODIN_OS == .Windows {
|
||||||
windows_trap_array_bounds()
|
windows_trap_array_bounds()
|
||||||
|
} else when ODIN_OS == .Orca {
|
||||||
|
abort_ext("", "", 0, "bounds trap")
|
||||||
} else {
|
} else {
|
||||||
trap()
|
trap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(no_instrumentation)
|
||||||
type_assertion_trap :: proc "contextless" () -> ! {
|
type_assertion_trap :: proc "contextless" () -> ! {
|
||||||
when ODIN_OS == .Windows {
|
when ODIN_OS == .Windows {
|
||||||
windows_trap_type_assertion()
|
windows_trap_type_assertion()
|
||||||
|
} else when ODIN_OS == .Orca {
|
||||||
|
abort_ext("", "", 0, "type assertion trap")
|
||||||
} else {
|
} else {
|
||||||
trap()
|
trap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@(disabled=ODIN_NO_BOUNDS_CHECK)
|
||||||
bounds_check_error :: proc "contextless" (file: string, line, column: i32, index, count: int) {
|
bounds_check_error :: proc "contextless" (file: string, line, column: i32, index, count: int) {
|
||||||
if uint(index) < uint(count) {
|
if uint(index) < uint(count) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@(cold)
|
@(cold, no_instrumentation)
|
||||||
handle_error :: proc "contextless" (file: string, line, column: i32, index, count: int) -> ! {
|
handle_error :: proc "contextless" (file: string, line, column: i32, index, count: int) -> ! {
|
||||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||||
print_string(" Index ")
|
print_string(" Index ")
|
||||||
@@ -34,6 +41,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: i32, index
|
|||||||
handle_error(file, line, column, index, count)
|
handle_error(file, line, column, index, count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(no_instrumentation)
|
||||||
slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int, len: int) -> ! {
|
slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int, len: int) -> ! {
|
||||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||||
print_string(" Invalid slice indices ")
|
print_string(" Invalid slice indices ")
|
||||||
@@ -46,6 +54,7 @@ slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, h
|
|||||||
bounds_trap()
|
bounds_trap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(no_instrumentation)
|
||||||
multi_pointer_slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int) -> ! {
|
multi_pointer_slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int) -> ! {
|
||||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||||
print_string(" Invalid slice indices ")
|
print_string(" Invalid slice indices ")
|
||||||
@@ -57,6 +66,7 @@ multi_pointer_slice_handle_error :: proc "contextless" (file: string, line, colu
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@(disabled=ODIN_NO_BOUNDS_CHECK)
|
||||||
multi_pointer_slice_expr_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int) {
|
multi_pointer_slice_expr_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int) {
|
||||||
if lo <= hi {
|
if lo <= hi {
|
||||||
return
|
return
|
||||||
@@ -64,6 +74,7 @@ multi_pointer_slice_expr_error :: proc "contextless" (file: string, line, column
|
|||||||
multi_pointer_slice_handle_error(file, line, column, lo, hi)
|
multi_pointer_slice_handle_error(file, line, column, lo, hi)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(disabled=ODIN_NO_BOUNDS_CHECK)
|
||||||
slice_expr_error_hi :: proc "contextless" (file: string, line, column: i32, hi: int, len: int) {
|
slice_expr_error_hi :: proc "contextless" (file: string, line, column: i32, hi: int, len: int) {
|
||||||
if 0 <= hi && hi <= len {
|
if 0 <= hi && hi <= len {
|
||||||
return
|
return
|
||||||
@@ -71,6 +82,7 @@ slice_expr_error_hi :: proc "contextless" (file: string, line, column: i32, hi:
|
|||||||
slice_handle_error(file, line, column, 0, hi, len)
|
slice_handle_error(file, line, column, 0, hi, len)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(disabled=ODIN_NO_BOUNDS_CHECK)
|
||||||
slice_expr_error_lo_hi :: proc "contextless" (file: string, line, column: i32, lo, hi: int, len: int) {
|
slice_expr_error_lo_hi :: proc "contextless" (file: string, line, column: i32, lo, hi: int, len: int) {
|
||||||
if 0 <= lo && lo <= len && lo <= hi && hi <= len {
|
if 0 <= lo && lo <= len && lo <= hi && hi <= len {
|
||||||
return
|
return
|
||||||
@@ -78,11 +90,12 @@ slice_expr_error_lo_hi :: proc "contextless" (file: string, line, column: i32, l
|
|||||||
slice_handle_error(file, line, column, lo, hi, len)
|
slice_handle_error(file, line, column, lo, hi, len)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(disabled=ODIN_NO_BOUNDS_CHECK)
|
||||||
dynamic_array_expr_error :: proc "contextless" (file: string, line, column: i32, low, high, max: int) {
|
dynamic_array_expr_error :: proc "contextless" (file: string, line, column: i32, low, high, max: int) {
|
||||||
if 0 <= low && low <= high && high <= max {
|
if 0 <= low && low <= high && high <= max {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@(cold)
|
@(cold, no_instrumentation)
|
||||||
handle_error :: proc "contextless" (file: string, line, column: i32, low, high, max: int) -> ! {
|
handle_error :: proc "contextless" (file: string, line, column: i32, low, high, max: int) -> ! {
|
||||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||||
print_string(" Invalid dynamic array indices ")
|
print_string(" Invalid dynamic array indices ")
|
||||||
@@ -98,12 +111,13 @@ dynamic_array_expr_error :: proc "contextless" (file: string, line, column: i32,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@(disabled=ODIN_NO_BOUNDS_CHECK)
|
||||||
matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) {
|
matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) {
|
||||||
if uint(row_index) < uint(row_count) &&
|
if uint(row_index) < uint(row_count) &&
|
||||||
uint(column_index) < uint(column_count) {
|
uint(column_index) < uint(column_count) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@(cold)
|
@(cold, no_instrumentation)
|
||||||
handle_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) -> ! {
|
handle_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) -> ! {
|
||||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||||
print_string(" Matrix indices [")
|
print_string(" Matrix indices [")
|
||||||
@@ -127,7 +141,7 @@ when ODIN_NO_RTTI {
|
|||||||
if ok {
|
if ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@(cold)
|
@(cold, no_instrumentation)
|
||||||
handle_error :: proc "contextless" (file: string, line, column: i32) -> ! {
|
handle_error :: proc "contextless" (file: string, line, column: i32) -> ! {
|
||||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||||
print_string(" Invalid type assertion\n")
|
print_string(" Invalid type assertion\n")
|
||||||
@@ -140,7 +154,7 @@ when ODIN_NO_RTTI {
|
|||||||
if ok {
|
if ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@(cold)
|
@(cold, no_instrumentation)
|
||||||
handle_error :: proc "contextless" (file: string, line, column: i32) -> ! {
|
handle_error :: proc "contextless" (file: string, line, column: i32) -> ! {
|
||||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||||
print_string(" Invalid type assertion\n")
|
print_string(" Invalid type assertion\n")
|
||||||
@@ -153,7 +167,7 @@ when ODIN_NO_RTTI {
|
|||||||
if ok {
|
if ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@(cold)
|
@(cold, no_instrumentation)
|
||||||
handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid) -> ! {
|
handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid) -> ! {
|
||||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||||
print_string(" Invalid type assertion from ")
|
print_string(" Invalid type assertion from ")
|
||||||
@@ -198,7 +212,7 @@ when ODIN_NO_RTTI {
|
|||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
@(cold)
|
@(cold, no_instrumentation)
|
||||||
handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) -> ! {
|
handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) -> ! {
|
||||||
|
|
||||||
actual := variant_type(from, from_data)
|
actual := variant_type(from, from_data)
|
||||||
@@ -220,11 +234,12 @@ when ODIN_NO_RTTI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@(disabled=ODIN_NO_BOUNDS_CHECK)
|
||||||
make_slice_error_loc :: #force_inline proc "contextless" (loc := #caller_location, len: int) {
|
make_slice_error_loc :: #force_inline proc "contextless" (loc := #caller_location, len: int) {
|
||||||
if 0 <= len {
|
if 0 <= len {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@(cold)
|
@(cold, no_instrumentation)
|
||||||
handle_error :: proc "contextless" (loc: Source_Code_Location, len: int) -> ! {
|
handle_error :: proc "contextless" (loc: Source_Code_Location, len: int) -> ! {
|
||||||
print_caller_location(loc)
|
print_caller_location(loc)
|
||||||
print_string(" Invalid slice length for make: ")
|
print_string(" Invalid slice length for make: ")
|
||||||
@@ -235,11 +250,12 @@ make_slice_error_loc :: #force_inline proc "contextless" (loc := #caller_locatio
|
|||||||
handle_error(loc, len)
|
handle_error(loc, len)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(disabled=ODIN_NO_BOUNDS_CHECK)
|
||||||
make_dynamic_array_error_loc :: #force_inline proc "contextless" (loc := #caller_location, len, cap: int) {
|
make_dynamic_array_error_loc :: #force_inline proc "contextless" (loc := #caller_location, len, cap: int) {
|
||||||
if 0 <= len && len <= cap {
|
if 0 <= len && len <= cap {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@(cold)
|
@(cold, no_instrumentation)
|
||||||
handle_error :: proc "contextless" (loc: Source_Code_Location, len, cap: int) -> ! {
|
handle_error :: proc "contextless" (loc: Source_Code_Location, len, cap: int) -> ! {
|
||||||
print_caller_location(loc)
|
print_caller_location(loc)
|
||||||
print_string(" Invalid dynamic array parameters for make: ")
|
print_string(" Invalid dynamic array parameters for make: ")
|
||||||
@@ -252,11 +268,12 @@ make_dynamic_array_error_loc :: #force_inline proc "contextless" (loc := #caller
|
|||||||
handle_error(loc, len, cap)
|
handle_error(loc, len, cap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(disabled=ODIN_NO_BOUNDS_CHECK)
|
||||||
make_map_expr_error_loc :: #force_inline proc "contextless" (loc := #caller_location, cap: int) {
|
make_map_expr_error_loc :: #force_inline proc "contextless" (loc := #caller_location, cap: int) {
|
||||||
if 0 <= cap {
|
if 0 <= cap {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@(cold)
|
@(cold, no_instrumentation)
|
||||||
handle_error :: proc "contextless" (loc: Source_Code_Location, cap: int) -> ! {
|
handle_error :: proc "contextless" (loc: Source_Code_Location, cap: int) -> ! {
|
||||||
print_caller_location(loc)
|
print_caller_location(loc)
|
||||||
print_string(" Invalid map capacity for make: ")
|
print_string(" Invalid map capacity for make: ")
|
||||||
@@ -270,19 +287,22 @@ make_map_expr_error_loc :: #force_inline proc "contextless" (loc := #caller_loca
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@(disabled=ODIN_NO_BOUNDS_CHECK)
|
||||||
bounds_check_error_loc :: #force_inline proc "contextless" (loc := #caller_location, index, count: int) {
|
bounds_check_error_loc :: #force_inline proc "contextless" (loc := #caller_location, index, count: int) {
|
||||||
bounds_check_error(loc.file_path, loc.line, loc.column, index, count)
|
bounds_check_error(loc.file_path, loc.line, loc.column, index, count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(disabled=ODIN_NO_BOUNDS_CHECK)
|
||||||
slice_expr_error_hi_loc :: #force_inline proc "contextless" (loc := #caller_location, hi: int, len: int) {
|
slice_expr_error_hi_loc :: #force_inline proc "contextless" (loc := #caller_location, hi: int, len: int) {
|
||||||
slice_expr_error_hi(loc.file_path, loc.line, loc.column, hi, len)
|
slice_expr_error_hi(loc.file_path, loc.line, loc.column, hi, len)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(disabled=ODIN_NO_BOUNDS_CHECK)
|
||||||
slice_expr_error_lo_hi_loc :: #force_inline proc "contextless" (loc := #caller_location, lo, hi: int, len: int) {
|
slice_expr_error_lo_hi_loc :: #force_inline proc "contextless" (loc := #caller_location, lo, hi: int, len: int) {
|
||||||
slice_expr_error_lo_hi(loc.file_path, loc.line, loc.column, lo, hi, len)
|
slice_expr_error_lo_hi(loc.file_path, loc.line, loc.column, lo, hi, len)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(disabled=ODIN_NO_BOUNDS_CHECK)
|
||||||
dynamic_array_expr_error_loc :: #force_inline proc "contextless" (loc := #caller_location, low, high, max: int) {
|
dynamic_array_expr_error_loc :: #force_inline proc "contextless" (loc := #caller_location, low, high, max: int) {
|
||||||
dynamic_array_expr_error(loc.file_path, loc.line, loc.column, low, high, max)
|
dynamic_array_expr_error(loc.file_path, loc.line, loc.column, low, high, max)
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
|
||||||
|
heap_allocator :: proc() -> Allocator {
|
||||||
|
return Allocator{
|
||||||
|
procedure = heap_allocator_proc,
|
||||||
|
data = nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
heap_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||||
|
size, alignment: int,
|
||||||
|
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||||
|
//
|
||||||
|
// NOTE(tetra, 2020-01-14): The heap doesn't respect alignment.
|
||||||
|
// Instead, we overallocate by `alignment + size_of(rawptr) - 1`, and insert
|
||||||
|
// padding. We also store the original pointer returned by heap_alloc right before
|
||||||
|
// the pointer we return to the user.
|
||||||
|
//
|
||||||
|
|
||||||
|
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 := a-1 + size_of(rawptr) + size
|
||||||
|
allocated_mem: rawptr
|
||||||
|
|
||||||
|
force_copy := old_ptr != nil && alignment > align_of(rawptr)
|
||||||
|
|
||||||
|
if old_ptr != nil && !force_copy {
|
||||||
|
original_old_ptr := ([^]rawptr)(old_ptr)[-1]
|
||||||
|
allocated_mem = heap_resize(original_old_ptr, space)
|
||||||
|
} else {
|
||||||
|
allocated_mem = heap_alloc(space, zero_memory)
|
||||||
|
}
|
||||||
|
aligned_mem := rawptr(([^]u8)(allocated_mem)[size_of(rawptr):])
|
||||||
|
|
||||||
|
ptr := uintptr(aligned_mem)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
aligned_mem = rawptr(aligned_ptr)
|
||||||
|
([^]rawptr)(aligned_mem)[-1] = allocated_mem
|
||||||
|
|
||||||
|
if force_copy {
|
||||||
|
mem_copy_non_overlapping(aligned_mem, old_ptr, min(old_size, size))
|
||||||
|
aligned_free(old_ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return byte_slice(aligned_mem, size), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
aligned_free :: proc(p: rawptr) {
|
||||||
|
if p != nil {
|
||||||
|
heap_free(([^]rawptr)(p)[-1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int, zero_memory := true) -> (new_memory: []byte, err: Allocator_Error) {
|
||||||
|
if p == nil {
|
||||||
|
return aligned_alloc(new_size, new_alignment, nil, old_size, zero_memory)
|
||||||
|
}
|
||||||
|
|
||||||
|
new_memory = aligned_alloc(new_size, new_alignment, p, old_size, zero_memory) or_return
|
||||||
|
|
||||||
|
// NOTE: heap_resize does not zero the new memory, so we do it
|
||||||
|
if zero_memory && new_size > old_size {
|
||||||
|
new_region := raw_data(new_memory[old_size:])
|
||||||
|
intrinsics.mem_zero(new_region, new_size - old_size)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch mode {
|
||||||
|
case .Alloc, .Alloc_Non_Zeroed:
|
||||||
|
return aligned_alloc(size, alignment, nil, 0, mode == .Alloc)
|
||||||
|
|
||||||
|
case .Free:
|
||||||
|
aligned_free(old_memory)
|
||||||
|
|
||||||
|
case .Free_All:
|
||||||
|
return nil, .Mode_Not_Implemented
|
||||||
|
|
||||||
|
case .Resize, .Resize_Non_Zeroed:
|
||||||
|
return aligned_resize(old_memory, old_size, size, alignment, mode == .Resize)
|
||||||
|
|
||||||
|
case .Query_Features:
|
||||||
|
set := (^Allocator_Mode_Set)(old_memory)
|
||||||
|
if set != nil {
|
||||||
|
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Resize, .Resize_Non_Zeroed, .Query_Features}
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
|
||||||
|
case .Query_Info:
|
||||||
|
return nil, .Mode_Not_Implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
heap_alloc :: proc "contextless" (size: int, zero_memory := true) -> rawptr {
|
||||||
|
return _heap_alloc(size, zero_memory)
|
||||||
|
}
|
||||||
|
|
||||||
|
heap_resize :: proc "contextless" (ptr: rawptr, new_size: int) -> rawptr {
|
||||||
|
return _heap_resize(ptr, new_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
heap_free :: proc "contextless" (ptr: rawptr) {
|
||||||
|
_heap_free(ptr)
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#+build orca
|
||||||
|
#+private
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
foreign {
|
||||||
|
@(link_name="malloc") _orca_malloc :: proc "c" (size: int) -> rawptr ---
|
||||||
|
@(link_name="calloc") _orca_calloc :: proc "c" (num, size: int) -> rawptr ---
|
||||||
|
@(link_name="free") _orca_free :: proc "c" (ptr: rawptr) ---
|
||||||
|
@(link_name="realloc") _orca_realloc :: proc "c" (ptr: rawptr, size: int) -> rawptr ---
|
||||||
|
}
|
||||||
|
|
||||||
|
_heap_alloc :: proc "contextless" (size: int, zero_memory := true) -> rawptr {
|
||||||
|
if size <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if zero_memory {
|
||||||
|
return _orca_calloc(1, size)
|
||||||
|
} else {
|
||||||
|
return _orca_malloc(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_heap_resize :: proc "contextless" (ptr: rawptr, new_size: int) -> rawptr {
|
||||||
|
return _orca_realloc(ptr, new_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
_heap_free :: proc "contextless" (ptr: rawptr) {
|
||||||
|
_orca_free(ptr)
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#+build js, wasi, freestanding, essence
|
||||||
|
#+private
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
_heap_alloc :: proc "contextless" (size: int, zero_memory := true) -> rawptr {
|
||||||
|
context = default_context()
|
||||||
|
unimplemented("base:runtime 'heap_alloc' procedure is not supported on this platform")
|
||||||
|
}
|
||||||
|
|
||||||
|
_heap_resize :: proc "contextless" (ptr: rawptr, new_size: int) -> rawptr {
|
||||||
|
context = default_context()
|
||||||
|
unimplemented("base:runtime 'heap_resize' procedure is not supported on this platform")
|
||||||
|
}
|
||||||
|
|
||||||
|
_heap_free :: proc "contextless" (ptr: rawptr) {
|
||||||
|
context = default_context()
|
||||||
|
unimplemented("base:runtime 'heap_free' procedure is not supported on this platform")
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#+build linux, darwin, freebsd, openbsd, netbsd, haiku
|
||||||
|
#+private
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
when ODIN_OS == .Darwin {
|
||||||
|
foreign import libc "system:System.framework"
|
||||||
|
} else {
|
||||||
|
foreign import libc "system:c"
|
||||||
|
}
|
||||||
|
|
||||||
|
@(default_calling_convention="c")
|
||||||
|
foreign libc {
|
||||||
|
@(link_name="malloc") _unix_malloc :: proc(size: int) -> rawptr ---
|
||||||
|
@(link_name="calloc") _unix_calloc :: proc(num, size: int) -> rawptr ---
|
||||||
|
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---
|
||||||
|
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr ---
|
||||||
|
}
|
||||||
|
|
||||||
|
_heap_alloc :: proc "contextless" (size: int, zero_memory := true) -> rawptr {
|
||||||
|
if size <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if zero_memory {
|
||||||
|
return _unix_calloc(1, size)
|
||||||
|
} else {
|
||||||
|
return _unix_malloc(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_heap_resize :: proc "contextless" (ptr: rawptr, new_size: int) -> rawptr {
|
||||||
|
// NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on
|
||||||
|
// POSIX platforms. Ensure your caller takes this into account.
|
||||||
|
return _unix_realloc(ptr, new_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
_heap_free :: proc "contextless" (ptr: rawptr) {
|
||||||
|
_unix_free(ptr)
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
foreign import kernel32 "system:Kernel32.lib"
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
@(default_calling_convention="system")
|
||||||
|
foreign kernel32 {
|
||||||
|
// NOTE(bill): The types are not using the standard names (e.g. DWORD and LPVOID) to just minimizing the dependency
|
||||||
|
|
||||||
|
// default_allocator
|
||||||
|
GetProcessHeap :: proc() -> rawptr ---
|
||||||
|
HeapAlloc :: proc(hHeap: rawptr, dwFlags: u32, dwBytes: uint) -> rawptr ---
|
||||||
|
HeapReAlloc :: proc(hHeap: rawptr, dwFlags: u32, lpMem: rawptr, dwBytes: uint) -> rawptr ---
|
||||||
|
HeapFree :: proc(hHeap: rawptr, dwFlags: u32, lpMem: rawptr) -> b32 ---
|
||||||
|
}
|
||||||
|
|
||||||
|
_heap_alloc :: proc "contextless" (size: int, zero_memory := true) -> rawptr {
|
||||||
|
HEAP_ZERO_MEMORY :: 0x00000008
|
||||||
|
return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY if zero_memory else 0, uint(size))
|
||||||
|
}
|
||||||
|
_heap_resize :: proc "contextless" (ptr: rawptr, new_size: int) -> rawptr {
|
||||||
|
if new_size == 0 {
|
||||||
|
_heap_free(ptr)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if ptr == nil {
|
||||||
|
return _heap_alloc(new_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
HEAP_ZERO_MEMORY :: 0x00000008
|
||||||
|
return HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ptr, uint(new_size))
|
||||||
|
}
|
||||||
|
_heap_free :: proc "contextless" (ptr: rawptr) {
|
||||||
|
if ptr == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
HeapFree(GetProcessHeap(), 0, ptr)
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,17 +1,17 @@
|
|||||||
|
#+vet !cast
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
@(private="file")
|
@(private="file")
|
||||||
IS_WASM :: ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32
|
IS_WASM :: ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
RUNTIME_LINKAGE :: "strong" when (
|
RUNTIME_LINKAGE :: "strong" when (
|
||||||
(ODIN_USE_SEPARATE_MODULES ||
|
ODIN_USE_SEPARATE_MODULES ||
|
||||||
ODIN_BUILD_MODE == .Dynamic ||
|
ODIN_BUILD_MODE == .Dynamic ||
|
||||||
!ODIN_NO_CRT) &&
|
!ODIN_NO_CRT) else "internal"
|
||||||
!IS_WASM) else "internal"
|
RUNTIME_REQUIRE :: false // !ODIN_TILDE
|
||||||
RUNTIME_REQUIRE :: !ODIN_TILDE
|
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
__float16 :: f16 when __ODIN_LLVM_F16_SUPPORTED else u16
|
__float16 :: f16 when __ODIN_LLVM_F16_SUPPORTED else u16
|
||||||
@@ -22,58 +22,14 @@ byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byt
|
|||||||
return ([^]byte)(data)[:max(len, 0)]
|
return ([^]byte)(data)[:max(len, 0)]
|
||||||
}
|
}
|
||||||
|
|
||||||
bswap_16 :: proc "contextless" (x: u16) -> u16 {
|
is_power_of_two_int :: #force_inline proc "contextless" (x: int) -> bool {
|
||||||
return x>>8 | x<<8
|
|
||||||
}
|
|
||||||
|
|
||||||
bswap_32 :: proc "contextless" (x: u32) -> u32 {
|
|
||||||
return x>>24 | (x>>8)&0xff00 | (x<<8)&0xff0000 | x<<24
|
|
||||||
}
|
|
||||||
|
|
||||||
bswap_64 :: proc "contextless" (x: u64) -> u64 {
|
|
||||||
z := x
|
|
||||||
z = (z & 0x00000000ffffffff) << 32 | (z & 0xffffffff00000000) >> 32
|
|
||||||
z = (z & 0x0000ffff0000ffff) << 16 | (z & 0xffff0000ffff0000) >> 16
|
|
||||||
z = (z & 0x00ff00ff00ff00ff) << 8 | (z & 0xff00ff00ff00ff00) >> 8
|
|
||||||
return z
|
|
||||||
}
|
|
||||||
|
|
||||||
bswap_128 :: proc "contextless" (x: u128) -> u128 {
|
|
||||||
z := transmute([4]u32)x
|
|
||||||
z[0], z[3] = bswap_32(z[3]), bswap_32(z[0])
|
|
||||||
z[1], z[2] = bswap_32(z[2]), bswap_32(z[1])
|
|
||||||
return transmute(u128)z
|
|
||||||
}
|
|
||||||
|
|
||||||
bswap_f16 :: proc "contextless" (f: f16) -> f16 {
|
|
||||||
x := transmute(u16)f
|
|
||||||
z := bswap_16(x)
|
|
||||||
return transmute(f16)z
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
bswap_f32 :: proc "contextless" (f: f32) -> f32 {
|
|
||||||
x := transmute(u32)f
|
|
||||||
z := bswap_32(x)
|
|
||||||
return transmute(f32)z
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
bswap_f64 :: proc "contextless" (f: f64) -> f64 {
|
|
||||||
x := transmute(u64)f
|
|
||||||
z := bswap_64(x)
|
|
||||||
return transmute(f64)z
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
is_power_of_two_int :: #force_inline proc(x: int) -> bool {
|
|
||||||
if x <= 0 {
|
if x <= 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return (x & (x-1)) == 0
|
return (x & (x-1)) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
align_forward_int :: #force_inline proc(ptr, align: int) -> int {
|
align_forward_int :: #force_inline proc "odin" (ptr, align: int) -> int {
|
||||||
assert(is_power_of_two_int(align))
|
assert(is_power_of_two_int(align))
|
||||||
|
|
||||||
p := ptr
|
p := ptr
|
||||||
@@ -84,14 +40,32 @@ align_forward_int :: #force_inline proc(ptr, align: int) -> int {
|
|||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
is_power_of_two_uintptr :: #force_inline proc(x: uintptr) -> bool {
|
is_power_of_two_uint :: #force_inline proc "contextless" (x: uint) -> bool {
|
||||||
if x <= 0 {
|
if x <= 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return (x & (x-1)) == 0
|
return (x & (x-1)) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
align_forward_uintptr :: #force_inline proc(ptr, align: uintptr) -> uintptr {
|
align_forward_uint :: #force_inline proc "odin" (ptr, align: uint) -> uint {
|
||||||
|
assert(is_power_of_two_uint(align))
|
||||||
|
|
||||||
|
p := ptr
|
||||||
|
modulo := p & (align-1)
|
||||||
|
if modulo != 0 {
|
||||||
|
p += align - modulo
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
is_power_of_two_uintptr :: #force_inline proc "contextless" (x: uintptr) -> bool {
|
||||||
|
if x <= 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return (x & (x-1)) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
align_forward_uintptr :: #force_inline proc "odin" (ptr, align: uintptr) -> uintptr {
|
||||||
assert(is_power_of_two_uintptr(align))
|
assert(is_power_of_two_uintptr(align))
|
||||||
|
|
||||||
p := ptr
|
p := ptr
|
||||||
@@ -102,6 +76,18 @@ align_forward_uintptr :: #force_inline proc(ptr, align: uintptr) -> uintptr {
|
|||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is_power_of_two :: proc {
|
||||||
|
is_power_of_two_int,
|
||||||
|
is_power_of_two_uint,
|
||||||
|
is_power_of_two_uintptr,
|
||||||
|
}
|
||||||
|
|
||||||
|
align_forward :: proc {
|
||||||
|
align_forward_int,
|
||||||
|
align_forward_uint,
|
||||||
|
align_forward_uintptr,
|
||||||
|
}
|
||||||
|
|
||||||
mem_zero :: proc "contextless" (data: rawptr, len: int) -> rawptr {
|
mem_zero :: proc "contextless" (data: rawptr, len: int) -> rawptr {
|
||||||
if data == nil {
|
if data == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -132,16 +118,15 @@ mem_copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> r
|
|||||||
DEFAULT_ALIGNMENT :: 2*align_of(rawptr)
|
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) {
|
mem_alloc_bytes :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||||
if size == 0 {
|
assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc)
|
||||||
return nil, nil
|
if size == 0 || allocator.procedure == nil{
|
||||||
}
|
|
||||||
if allocator.procedure == nil {
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
return allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc)
|
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) {
|
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 {
|
if size == 0 || allocator.procedure == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -149,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) {
|
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 {
|
if size == 0 || allocator.procedure == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -187,7 +173,8 @@ mem_free_all :: #force_inline proc(allocator := context.allocator, loc := #calle
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
_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 {
|
if allocator.procedure == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -198,15 +185,27 @@ mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAUL
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
} else if ptr == nil {
|
} else if ptr == nil {
|
||||||
return allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
|
if should_zero {
|
||||||
|
return allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
|
||||||
|
} else {
|
||||||
|
return allocator.procedure(allocator.data, .Alloc_Non_Zeroed, new_size, alignment, nil, 0, loc)
|
||||||
|
}
|
||||||
} else if old_size == new_size && uintptr(ptr) % uintptr(alignment) == 0 {
|
} else if old_size == new_size && uintptr(ptr) % uintptr(alignment) == 0 {
|
||||||
data = ([^]byte)(ptr)[:old_size]
|
data = ([^]byte)(ptr)[:old_size]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err = allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, loc)
|
if should_zero {
|
||||||
|
data, err = allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, loc)
|
||||||
|
} else {
|
||||||
|
data, err = allocator.procedure(allocator.data, .Resize_Non_Zeroed, new_size, alignment, ptr, old_size, loc)
|
||||||
|
}
|
||||||
if err == .Mode_Not_Implemented {
|
if err == .Mode_Not_Implemented {
|
||||||
data, err = allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
|
if should_zero {
|
||||||
|
data, err = allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
|
||||||
|
} else {
|
||||||
|
data, err = allocator.procedure(allocator.data, .Alloc_Non_Zeroed, new_size, alignment, nil, 0, loc)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -216,6 +215,15 @@ mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAUL
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
|
memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
|
||||||
switch {
|
switch {
|
||||||
case n == 0: return true
|
case n == 0: return true
|
||||||
@@ -478,7 +486,7 @@ quaternion256_ne :: #force_inline proc "contextless" (a, b: quaternion256) -> bo
|
|||||||
string_decode_rune :: #force_inline proc "contextless" (s: string) -> (rune, int) {
|
string_decode_rune :: #force_inline proc "contextless" (s: string) -> (rune, int) {
|
||||||
// NOTE(bill): Duplicated here to remove dependency on package unicode/utf8
|
// NOTE(bill): Duplicated here to remove dependency on package unicode/utf8
|
||||||
|
|
||||||
@static accept_sizes := [256]u8{
|
@(static, rodata) accept_sizes := [256]u8{
|
||||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x00-0x0f
|
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x00-0x0f
|
||||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x10-0x1f
|
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x10-0x1f
|
||||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x20-0x2f
|
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x20-0x2f
|
||||||
@@ -499,7 +507,7 @@ string_decode_rune :: #force_inline proc "contextless" (s: string) -> (rune, int
|
|||||||
}
|
}
|
||||||
Accept_Range :: struct {lo, hi: u8}
|
Accept_Range :: struct {lo, hi: u8}
|
||||||
|
|
||||||
@static accept_ranges := [5]Accept_Range{
|
@(static, rodata) accept_ranges := [5]Accept_Range{
|
||||||
{0x80, 0xbf},
|
{0x80, 0xbf},
|
||||||
{0xa0, 0xbf},
|
{0xa0, 0xbf},
|
||||||
{0x80, 0x9f},
|
{0x80, 0x9f},
|
||||||
@@ -589,36 +597,6 @@ string_decode_last_rune :: proc "contextless" (s: string) -> (rune, int) {
|
|||||||
return r, size
|
return r, size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
abs_f16 :: #force_inline proc "contextless" (x: f16) -> f16 {
|
|
||||||
return -x if x < 0 else x
|
|
||||||
}
|
|
||||||
abs_f32 :: #force_inline proc "contextless" (x: f32) -> f32 {
|
|
||||||
return -x if x < 0 else x
|
|
||||||
}
|
|
||||||
abs_f64 :: #force_inline proc "contextless" (x: f64) -> f64 {
|
|
||||||
return -x if x < 0 else x
|
|
||||||
}
|
|
||||||
|
|
||||||
min_f16 :: #force_inline proc "contextless" (a, b: f16) -> f16 {
|
|
||||||
return a if a < b else b
|
|
||||||
}
|
|
||||||
min_f32 :: #force_inline proc "contextless" (a, b: f32) -> f32 {
|
|
||||||
return a if a < b else b
|
|
||||||
}
|
|
||||||
min_f64 :: #force_inline proc "contextless" (a, b: f64) -> f64 {
|
|
||||||
return a if a < b else b
|
|
||||||
}
|
|
||||||
max_f16 :: #force_inline proc "contextless" (a, b: f16) -> f16 {
|
|
||||||
return a if a > b else b
|
|
||||||
}
|
|
||||||
max_f32 :: #force_inline proc "contextless" (a, b: f32) -> f32 {
|
|
||||||
return a if a > b else b
|
|
||||||
}
|
|
||||||
max_f64 :: #force_inline proc "contextless" (a, b: f64) -> f64 {
|
|
||||||
return a if a > b else b
|
|
||||||
}
|
|
||||||
|
|
||||||
abs_complex32 :: #force_inline proc "contextless" (x: complex32) -> f16 {
|
abs_complex32 :: #force_inline proc "contextless" (x: complex32) -> f16 {
|
||||||
p, q := abs(real(x)), abs(imag(x))
|
p, q := abs(real(x)), abs(imag(x))
|
||||||
if p < q {
|
if p < q {
|
||||||
@@ -667,21 +645,24 @@ abs_quaternion256 :: #force_inline proc "contextless" (x: quaternion256) -> f64
|
|||||||
|
|
||||||
|
|
||||||
quo_complex32 :: proc "contextless" (n, m: complex32) -> complex32 {
|
quo_complex32 :: proc "contextless" (n, m: complex32) -> complex32 {
|
||||||
e, f: f16
|
nr, ni := f32(real(n)), f32(imag(n))
|
||||||
|
mr, mi := f32(real(m)), f32(imag(m))
|
||||||
|
|
||||||
if abs(real(m)) >= abs(imag(m)) {
|
e, f: f32
|
||||||
ratio := imag(m) / real(m)
|
|
||||||
denom := real(m) + ratio*imag(m)
|
if abs(mr) >= abs(mi) {
|
||||||
e = (real(n) + imag(n)*ratio) / denom
|
ratio := mi / mr
|
||||||
f = (imag(n) - real(n)*ratio) / denom
|
denom := mr + ratio*mi
|
||||||
|
e = (nr + ni*ratio) / denom
|
||||||
|
f = (ni - nr*ratio) / denom
|
||||||
} else {
|
} else {
|
||||||
ratio := real(m) / imag(m)
|
ratio := mr / mi
|
||||||
denom := imag(m) + ratio*real(m)
|
denom := mi + ratio*mr
|
||||||
e = (real(n)*ratio + imag(n)) / denom
|
e = (nr*ratio + ni) / denom
|
||||||
f = (imag(n)*ratio - real(n)) / denom
|
f = (ni*ratio - nr) / denom
|
||||||
}
|
}
|
||||||
|
|
||||||
return complex(e, f)
|
return complex(f16(e), f16(f))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -722,15 +703,15 @@ quo_complex128 :: proc "contextless" (n, m: complex128) -> complex128 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mul_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
|
mul_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
|
||||||
q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q)
|
q0, q1, q2, q3 := f32(real(q)), f32(imag(q)), f32(jmag(q)), f32(kmag(q))
|
||||||
r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r)
|
r0, r1, r2, r3 := f32(real(r)), f32(imag(r)), f32(jmag(r)), f32(kmag(r))
|
||||||
|
|
||||||
t0 := r0*q0 - r1*q1 - r2*q2 - r3*q3
|
t0 := r0*q0 - r1*q1 - r2*q2 - r3*q3
|
||||||
t1 := r0*q1 + r1*q0 - r2*q3 + r3*q2
|
t1 := r0*q1 + r1*q0 - r2*q3 + r3*q2
|
||||||
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
|
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
|
||||||
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0
|
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0
|
||||||
|
|
||||||
return quaternion(t0, t1, t2, t3)
|
return quaternion(w=f16(t0), x=f16(t1), y=f16(t2), z=f16(t3))
|
||||||
}
|
}
|
||||||
|
|
||||||
mul_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
|
mul_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
|
||||||
@@ -742,7 +723,7 @@ mul_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
|
|||||||
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
|
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
|
||||||
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0
|
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0
|
||||||
|
|
||||||
return quaternion(t0, t1, t2, t3)
|
return quaternion(w=t0, x=t1, y=t2, z=t3)
|
||||||
}
|
}
|
||||||
|
|
||||||
mul_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
|
mul_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
|
||||||
@@ -754,12 +735,12 @@ mul_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
|
|||||||
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
|
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
|
||||||
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0
|
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0
|
||||||
|
|
||||||
return quaternion(t0, t1, t2, t3)
|
return quaternion(w=t0, x=t1, y=t2, z=t3)
|
||||||
}
|
}
|
||||||
|
|
||||||
quo_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
|
quo_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
|
||||||
q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q)
|
q0, q1, q2, q3 := f32(real(q)), f32(imag(q)), f32(jmag(q)), f32(kmag(q))
|
||||||
r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r)
|
r0, r1, r2, r3 := f32(real(r)), f32(imag(r)), f32(jmag(r)), f32(kmag(r))
|
||||||
|
|
||||||
invmag2 := 1.0 / (r0*r0 + r1*r1 + r2*r2 + r3*r3)
|
invmag2 := 1.0 / (r0*r0 + r1*r1 + r2*r2 + r3*r3)
|
||||||
|
|
||||||
@@ -768,7 +749,7 @@ quo_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
|
|||||||
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2
|
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2
|
||||||
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2
|
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2
|
||||||
|
|
||||||
return quaternion(t0, t1, t2, t3)
|
return quaternion(w=f16(t0), x=f16(t1), y=f16(t2), z=f16(t3))
|
||||||
}
|
}
|
||||||
|
|
||||||
quo_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
|
quo_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
|
||||||
@@ -782,7 +763,7 @@ quo_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
|
|||||||
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2
|
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2
|
||||||
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2
|
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2
|
||||||
|
|
||||||
return quaternion(t0, t1, t2, t3)
|
return quaternion(w=t0, x=t1, y=t2, z=t3)
|
||||||
}
|
}
|
||||||
|
|
||||||
quo_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
|
quo_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
|
||||||
@@ -796,7 +777,7 @@ quo_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
|
|||||||
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2
|
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2
|
||||||
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2
|
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2
|
||||||
|
|
||||||
return quaternion(t0, t1, t2, t3)
|
return quaternion(w=t0, x=t1, y=t2, z=t3)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(link_name="__truncsfhf2", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
@(link_name="__truncsfhf2", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||||
@@ -856,6 +837,10 @@ truncsfhf2 :: proc "c" (value: f32) -> __float16 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(link_name="__aeabi_d2h", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||||
|
aeabi_d2h :: proc "c" (value: f64) -> __float16 {
|
||||||
|
return truncsfhf2(f32(value))
|
||||||
|
}
|
||||||
|
|
||||||
@(link_name="__truncdfhf2", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
@(link_name="__truncdfhf2", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||||
truncdfhf2 :: proc "c" (value: f64) -> __float16 {
|
truncdfhf2 :: proc "c" (value: f64) -> __float16 {
|
||||||
@@ -896,9 +881,6 @@ extendhfsf2 :: proc "c" (value: __float16) -> f32 {
|
|||||||
|
|
||||||
@(link_name="__floattidf", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
@(link_name="__floattidf", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||||
floattidf :: proc "c" (a: i128) -> f64 {
|
floattidf :: proc "c" (a: i128) -> f64 {
|
||||||
when IS_WASM {
|
|
||||||
return 0
|
|
||||||
} else {
|
|
||||||
DBL_MANT_DIG :: 53
|
DBL_MANT_DIG :: 53
|
||||||
if a == 0 {
|
if a == 0 {
|
||||||
return 0.0
|
return 0.0
|
||||||
@@ -938,14 +920,10 @@ when IS_WASM {
|
|||||||
fb[0] = u32(a) // mantissa-low
|
fb[0] = u32(a) // mantissa-low
|
||||||
return transmute(f64)fb
|
return transmute(f64)fb
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@(link_name="__floattidf_unsigned", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
@(link_name="__floattidf_unsigned", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||||
floattidf_unsigned :: proc "c" (a: u128) -> f64 {
|
floattidf_unsigned :: proc "c" (a: u128) -> f64 {
|
||||||
when IS_WASM {
|
|
||||||
return 0
|
|
||||||
} else {
|
|
||||||
DBL_MANT_DIG :: 53
|
DBL_MANT_DIG :: 53
|
||||||
if a == 0 {
|
if a == 0 {
|
||||||
return 0.0
|
return 0.0
|
||||||
@@ -983,7 +961,6 @@ when IS_WASM {
|
|||||||
fb[0] = u32(a) // mantissa-low
|
fb[0] = u32(a) // mantissa-low
|
||||||
return transmute(f64)fb
|
return transmute(f64)fb
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1017,9 +994,11 @@ udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
|
|||||||
return udivmod128(a, b, rem)
|
return udivmod128(a, b, rem)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(link_name="__udivti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
when !IS_WASM {
|
||||||
udivti3 :: proc "c" (a, b: u128) -> u128 {
|
@(link_name="__udivti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||||
return udivmodti4(a, b, nil)
|
udivti3 :: proc "c" (a, b: u128) -> u128 {
|
||||||
|
return udivmodti4(a, b, nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1031,26 +1010,44 @@ modti3 :: proc "c" (a, b: i128) -> i128 {
|
|||||||
bn := (b ~ s_b) - s_b
|
bn := (b ~ s_b) - s_b
|
||||||
|
|
||||||
r: u128 = ---
|
r: u128 = ---
|
||||||
_ = udivmod128(transmute(u128)an, transmute(u128)bn, &r)
|
_ = udivmod128(u128(an), u128(bn), &r)
|
||||||
return (transmute(i128)r ~ s_a) - s_a
|
return (i128(r) ~ s_a) - s_a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@(link_name="__divmodti4", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
@(link_name="__divmodti4", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||||
divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 {
|
divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 {
|
||||||
u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem)
|
s_a := a >> (128 - 1) // -1 if negative or 0
|
||||||
return transmute(i128)u
|
s_b := b >> (128 - 1)
|
||||||
|
an := (a ~ s_a) - s_a // absolute
|
||||||
|
bn := (b ~ s_b) - s_b
|
||||||
|
|
||||||
|
s_b ~= s_a // quotient sign
|
||||||
|
u_s_b := u128(s_b)
|
||||||
|
u_s_a := u128(s_a)
|
||||||
|
|
||||||
|
r: u128 = ---
|
||||||
|
u := i128((udivmodti4(u128(an), u128(bn), &r) ~ u_s_b) - u_s_b) // negate if negative
|
||||||
|
rem^ = i128((r ~ u_s_a) - u_s_a)
|
||||||
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
@(link_name="__divti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
@(link_name="__divti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||||
divti3 :: proc "c" (a, b: i128) -> i128 {
|
divti3 :: proc "c" (a, b: i128) -> i128 {
|
||||||
u := udivmodti4(transmute(u128)a, transmute(u128)b, nil)
|
s_a := a >> (128 - 1) // -1 if negative or 0
|
||||||
return transmute(i128)u
|
s_b := b >> (128 - 1)
|
||||||
|
an := (a ~ s_a) - s_a // absolute
|
||||||
|
bn := (b ~ s_b) - s_b
|
||||||
|
|
||||||
|
s_a ~= s_b // quotient sign
|
||||||
|
u_s_a := u128(s_a)
|
||||||
|
|
||||||
|
return i128((udivmodti4(u128(an), u128(bn), nil) ~ u_s_a) - u_s_a) // negate if negative
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@(link_name="__fixdfti", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
@(link_name="__fixdfti", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||||
fixdfti :: proc(a: u64) -> i128 {
|
fixdfti :: proc "c" (a: u64) -> i128 {
|
||||||
significandBits :: 52
|
significandBits :: 52
|
||||||
typeWidth :: (size_of(u64)*8)
|
typeWidth :: (size_of(u64)*8)
|
||||||
exponentBits :: (typeWidth - significandBits - 1)
|
exponentBits :: (typeWidth - significandBits - 1)
|
||||||
@@ -1089,3 +1086,23 @@ fixdfti :: proc(a: u64) -> i128 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
__write_bits :: proc "contextless" (dst, src: [^]byte, offset: uintptr, size: uintptr) {
|
||||||
|
for i in 0..<size {
|
||||||
|
j := offset+i
|
||||||
|
the_bit := byte((src[i>>3]) & (1<<(i&7)) != 0)
|
||||||
|
dst[j>>3] &~= 1<<(j&7)
|
||||||
|
dst[j>>3] |= the_bit<<(j&7)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__read_bits :: proc "contextless" (dst, src: [^]byte, offset: uintptr, size: uintptr) {
|
||||||
|
for j in 0..<size {
|
||||||
|
i := offset+j
|
||||||
|
the_bit := byte((src[i>>3]) & (1<<(i&7)) != 0)
|
||||||
|
dst[j>>3] &~= 1<<(j&7)
|
||||||
|
dst[j>>3] |= the_bit<<(j&7)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
_OS_Errno :: distinct int
|
||||||
|
|
||||||
|
stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||||
|
return _stderr_write(data)
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
#+build freebsd, openbsd, netbsd
|
||||||
|
#+private
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
foreign import libc "system:c"
|
||||||
|
|
||||||
|
@(default_calling_convention="c")
|
||||||
|
foreign libc {
|
||||||
|
@(link_name="write")
|
||||||
|
_unix_write :: proc(fd: i32, buf: rawptr, size: int) -> int ---
|
||||||
|
|
||||||
|
when ODIN_OS == .NetBSD {
|
||||||
|
@(link_name="__errno") __error :: proc() -> ^i32 ---
|
||||||
|
} else {
|
||||||
|
__error :: proc() -> ^i32 ---
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||||
|
ret := _unix_write(2, raw_data(data), len(data))
|
||||||
|
if ret < len(data) {
|
||||||
|
err := __error()
|
||||||
|
return int(ret), _OS_Errno(err^ if err != nil else 0)
|
||||||
|
}
|
||||||
|
return int(ret), 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#+build darwin
|
||||||
|
#+private
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
|
||||||
|
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||||
|
STDERR :: 2
|
||||||
|
when ODIN_NO_CRT {
|
||||||
|
WRITE :: 0x2000004
|
||||||
|
ret := intrinsics.syscall(WRITE, STDERR, uintptr(raw_data(data)), uintptr(len(data)))
|
||||||
|
if ret < 0 {
|
||||||
|
return 0, _OS_Errno(-ret)
|
||||||
|
}
|
||||||
|
return int(ret), 0
|
||||||
|
} else {
|
||||||
|
foreign {
|
||||||
|
write :: proc(handle: i32, buffer: [^]byte, count: uint) -> int ---
|
||||||
|
__error :: proc() -> ^i32 ---
|
||||||
|
}
|
||||||
|
|
||||||
|
if ret := write(STDERR, raw_data(data), len(data)); ret >= 0 {
|
||||||
|
return int(ret), 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, _OS_Errno(__error()^)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#+build freestanding
|
||||||
|
#+private
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
// TODO(bill): reimplement `os.write`
|
||||||
|
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||||
|
return 0, -1
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#+build haiku
|
||||||
|
#+private
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
foreign import libc "system:c"
|
||||||
|
|
||||||
|
foreign libc {
|
||||||
|
@(link_name="write")
|
||||||
|
_unix_write :: proc(fd: i32, buf: rawptr, size: int) -> int ---
|
||||||
|
|
||||||
|
_errnop :: proc() -> ^i32 ---
|
||||||
|
}
|
||||||
|
|
||||||
|
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||||
|
ret := _unix_write(2, raw_data(data), len(data))
|
||||||
|
if ret < len(data) {
|
||||||
|
err := _errnop()
|
||||||
|
return int(ret), _OS_Errno(err^ if err != nil else 0)
|
||||||
|
}
|
||||||
|
return int(ret), 0
|
||||||
|
}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
//+build js
|
#+build js
|
||||||
|
#+private
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
foreign import "odin_env"
|
foreign import "odin_env"
|
||||||
|
|
||||||
_os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||||
foreign odin_env {
|
foreign odin_env {
|
||||||
write :: proc "contextless" (fd: u32, p: []byte) ---
|
write :: proc "contextless" (fd: u32, p: []byte) ---
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
#+private
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
|
||||||
|
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||||
|
when ODIN_ARCH == .amd64 {
|
||||||
|
SYS_write :: uintptr(1)
|
||||||
|
} else when ODIN_ARCH == .arm64 {
|
||||||
|
SYS_write :: uintptr(64)
|
||||||
|
} else when ODIN_ARCH == .i386 {
|
||||||
|
SYS_write :: uintptr(4)
|
||||||
|
} else when ODIN_ARCH == .arm32 {
|
||||||
|
SYS_write :: uintptr(4)
|
||||||
|
} else when ODIN_ARCH == .riscv64 {
|
||||||
|
SYS_write :: uintptr(64)
|
||||||
|
}
|
||||||
|
|
||||||
|
stderr :: 2
|
||||||
|
|
||||||
|
ret := int(intrinsics.syscall(SYS_write, uintptr(stderr), uintptr(raw_data(data)), uintptr(len(data))))
|
||||||
|
if ret < 0 && ret > -4096 {
|
||||||
|
return 0, _OS_Errno(-ret)
|
||||||
|
}
|
||||||
|
return ret, 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
#+build orca
|
||||||
|
#+private
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
|
||||||
|
// Constants allowing to specify the level of logging verbosity.
|
||||||
|
log_level :: enum u32 {
|
||||||
|
// Only errors are logged.
|
||||||
|
ERROR = 0,
|
||||||
|
// Only warnings and errors are logged.
|
||||||
|
WARNING = 1,
|
||||||
|
// All messages are logged.
|
||||||
|
INFO = 2,
|
||||||
|
COUNT = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
@(default_calling_convention="c", link_prefix="oc_")
|
||||||
|
foreign {
|
||||||
|
abort_ext :: proc(file: cstring, function: cstring, line: i32, fmt: cstring, #c_vararg args: ..any) -> ! ---
|
||||||
|
assert_fail :: proc(file: cstring, function: cstring, line: i32, src: cstring, fmt: cstring, #c_vararg args: ..any) -> ! ---
|
||||||
|
log_ext :: proc(level: log_level, function: cstring, file: cstring, line: i32, fmt: cstring, #c_vararg args: ..any) ---
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: This is all pretty gross, don't look.
|
||||||
|
|
||||||
|
// WASM is single threaded so this should be fine.
|
||||||
|
orca_stderr_buffer: [4096]byte
|
||||||
|
orca_stderr_buffer_idx: int
|
||||||
|
|
||||||
|
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||||
|
for b in data {
|
||||||
|
orca_stderr_buffer[orca_stderr_buffer_idx] = b
|
||||||
|
orca_stderr_buffer_idx += 1
|
||||||
|
|
||||||
|
if b == '\n' || orca_stderr_buffer_idx == len(orca_stderr_buffer)-1 {
|
||||||
|
log_ext(.ERROR, "", "", 0, cstring(raw_data(orca_stderr_buffer[:orca_stderr_buffer_idx])))
|
||||||
|
orca_stderr_buffer_idx = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(data), 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
#+build wasi
|
||||||
|
#+private
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
foreign import wasi "wasi_snapshot_preview1"
|
||||||
|
|
||||||
|
@(default_calling_convention="contextless")
|
||||||
|
foreign wasi {
|
||||||
|
fd_write :: proc(
|
||||||
|
fd: i32,
|
||||||
|
iovs: [][]byte,
|
||||||
|
n: ^uint,
|
||||||
|
) -> u16 ---
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
args_sizes_get :: proc(
|
||||||
|
num_of_args: ^uint,
|
||||||
|
size_of_args: ^uint,
|
||||||
|
) -> u16 ---
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
args_get :: proc(
|
||||||
|
argv: [^]cstring,
|
||||||
|
argv_buf: [^]byte,
|
||||||
|
) -> u16 ---
|
||||||
|
}
|
||||||
|
|
||||||
|
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||||
|
n: uint
|
||||||
|
err := fd_write(1, {data}, &n)
|
||||||
|
return int(n), _OS_Errno(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_wasi_setup_args :: proc() {
|
||||||
|
num_of_args, size_of_args: uint
|
||||||
|
if errno := args_sizes_get(&num_of_args, &size_of_args); errno != 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err: Allocator_Error
|
||||||
|
if args__, err = make([]cstring, num_of_args); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
args_buf: []byte
|
||||||
|
if args_buf, err = make([]byte, size_of_args); err != nil {
|
||||||
|
delete(args__)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if errno := args_get(raw_data(args__), raw_data(args_buf)); errno != 0 {
|
||||||
|
delete(args__)
|
||||||
|
delete(args_buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
#+build windows
|
||||||
|
#+private
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
foreign import kernel32 "system:Kernel32.lib"
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
@(default_calling_convention="system")
|
||||||
|
foreign kernel32 {
|
||||||
|
// NOTE(bill): The types are not using the standard names (e.g. DWORD and LPVOID) to just minimizing the dependency
|
||||||
|
|
||||||
|
// stderr_write
|
||||||
|
GetStdHandle :: proc(which: u32) -> rawptr ---
|
||||||
|
SetHandleInformation :: proc(hObject: rawptr, dwMask: u32, dwFlags: u32) -> b32 ---
|
||||||
|
WriteFile :: proc(hFile: rawptr, lpBuffer: rawptr, nNumberOfBytesToWrite: u32, lpNumberOfBytesWritten: ^u32, lpOverlapped: rawptr) -> b32 ---
|
||||||
|
GetLastError :: proc() -> u32 ---
|
||||||
|
}
|
||||||
|
|
||||||
|
_stderr_write :: proc "contextless" (data: []byte) -> (n: int, err: _OS_Errno) #no_bounds_check {
|
||||||
|
if len(data) == 0 {
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
STD_ERROR_HANDLE :: ~u32(0) -12 + 1
|
||||||
|
HANDLE_FLAG_INHERIT :: 0x00000001
|
||||||
|
MAX_RW :: 1<<30
|
||||||
|
|
||||||
|
h := GetStdHandle(STD_ERROR_HANDLE)
|
||||||
|
when size_of(uintptr) == 8 {
|
||||||
|
SetHandleInformation(h, HANDLE_FLAG_INHERIT, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
single_write_length: u32
|
||||||
|
total_write: i64
|
||||||
|
length := i64(len(data))
|
||||||
|
|
||||||
|
for total_write < length {
|
||||||
|
remaining := length - total_write
|
||||||
|
to_write := u32(min(i32(remaining), MAX_RW))
|
||||||
|
|
||||||
|
e := WriteFile(h, &data[total_write], to_write, &single_write_length, nil)
|
||||||
|
if single_write_length <= 0 || !e {
|
||||||
|
err = _OS_Errno(GetLastError())
|
||||||
|
n = int(total_write)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
total_write += i64(single_write_length)
|
||||||
|
}
|
||||||
|
n = int(total_write)
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ _INTEGER_DIGITS :: "0123456789abcdefghijklmnopqrstuvwxyz"
|
|||||||
_INTEGER_DIGITS_VAR := _INTEGER_DIGITS
|
_INTEGER_DIGITS_VAR := _INTEGER_DIGITS
|
||||||
|
|
||||||
when !ODIN_NO_RTTI {
|
when !ODIN_NO_RTTI {
|
||||||
print_any_single :: proc "contextless" (arg: any) {
|
print_any_single :: #force_no_inline proc "contextless" (arg: any) {
|
||||||
x := arg
|
x := arg
|
||||||
if x.data == nil {
|
if x.data == nil {
|
||||||
print_string("nil")
|
print_string("nil")
|
||||||
@@ -72,7 +72,7 @@ when !ODIN_NO_RTTI {
|
|||||||
print_string("<invalid-value>")
|
print_string("<invalid-value>")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println_any :: proc "contextless" (args: ..any) {
|
println_any :: #force_no_inline proc "contextless" (args: ..any) {
|
||||||
context = default_context()
|
context = default_context()
|
||||||
loop: for arg, i in args {
|
loop: for arg, i in args {
|
||||||
assert(arg.id != nil)
|
assert(arg.id != nil)
|
||||||
@@ -122,14 +122,14 @@ encode_rune :: proc "contextless" (c: rune) -> ([4]u8, int) {
|
|||||||
return buf, 4
|
return buf, 4
|
||||||
}
|
}
|
||||||
|
|
||||||
print_string :: proc "contextless" (str: string) -> (n: int) {
|
print_string :: #force_no_inline proc "contextless" (str: string) -> (n: int) {
|
||||||
n, _ = os_write(transmute([]byte)str)
|
n, _ = stderr_write(transmute([]byte)str)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
print_strings :: proc "contextless" (args: ..string) -> (n: int) {
|
print_strings :: #force_no_inline proc "contextless" (args: ..string) -> (n: int) {
|
||||||
for str in args {
|
for str in args {
|
||||||
m, err := os_write(transmute([]byte)str)
|
m, err := stderr_write(transmute([]byte)str)
|
||||||
n += m
|
n += m
|
||||||
if err != 0 {
|
if err != 0 {
|
||||||
break
|
break
|
||||||
@@ -138,12 +138,12 @@ print_strings :: proc "contextless" (args: ..string) -> (n: int) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
print_byte :: proc "contextless" (b: byte) -> (n: int) {
|
print_byte :: #force_no_inline proc "contextless" (b: byte) -> (n: int) {
|
||||||
n, _ = os_write([]byte{b})
|
n, _ = stderr_write([]byte{b})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
print_encoded_rune :: proc "contextless" (r: rune) {
|
print_encoded_rune :: #force_no_inline proc "contextless" (r: rune) {
|
||||||
print_byte('\'')
|
print_byte('\'')
|
||||||
|
|
||||||
switch r {
|
switch r {
|
||||||
@@ -170,7 +170,7 @@ print_encoded_rune :: proc "contextless" (r: rune) {
|
|||||||
print_byte('\'')
|
print_byte('\'')
|
||||||
}
|
}
|
||||||
|
|
||||||
print_rune :: proc "contextless" (r: rune) -> int #no_bounds_check {
|
print_rune :: #force_no_inline proc "contextless" (r: rune) -> int #no_bounds_check {
|
||||||
RUNE_SELF :: 0x80
|
RUNE_SELF :: 0x80
|
||||||
|
|
||||||
if r < RUNE_SELF {
|
if r < RUNE_SELF {
|
||||||
@@ -178,12 +178,12 @@ print_rune :: proc "contextless" (r: rune) -> int #no_bounds_check {
|
|||||||
}
|
}
|
||||||
|
|
||||||
b, n := encode_rune(r)
|
b, n := encode_rune(r)
|
||||||
m, _ := os_write(b[:n])
|
m, _ := stderr_write(b[:n])
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
print_u64 :: proc "contextless" (x: u64) #no_bounds_check {
|
print_u64 :: #force_no_inline proc "contextless" (x: u64) #no_bounds_check {
|
||||||
a: [129]byte
|
a: [129]byte
|
||||||
i := len(a)
|
i := len(a)
|
||||||
b := u64(10)
|
b := u64(10)
|
||||||
@@ -194,11 +194,11 @@ print_u64 :: proc "contextless" (x: u64) #no_bounds_check {
|
|||||||
}
|
}
|
||||||
i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
|
i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
|
||||||
|
|
||||||
os_write(a[i:])
|
stderr_write(a[i:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
print_i64 :: proc "contextless" (x: i64) #no_bounds_check {
|
print_i64 :: #force_no_inline proc "contextless" (x: i64) #no_bounds_check {
|
||||||
b :: i64(10)
|
b :: i64(10)
|
||||||
|
|
||||||
u := x
|
u := x
|
||||||
@@ -216,32 +216,36 @@ print_i64 :: proc "contextless" (x: i64) #no_bounds_check {
|
|||||||
i -= 1; a[i] = '-'
|
i -= 1; a[i] = '-'
|
||||||
}
|
}
|
||||||
|
|
||||||
os_write(a[i:])
|
stderr_write(a[i:])
|
||||||
}
|
}
|
||||||
|
|
||||||
print_uint :: proc "contextless" (x: uint) { print_u64(u64(x)) }
|
print_uint :: proc "contextless" (x: uint) { print_u64(u64(x)) }
|
||||||
print_uintptr :: proc "contextless" (x: uintptr) { print_u64(u64(x)) }
|
print_uintptr :: proc "contextless" (x: uintptr) { print_u64(u64(x)) }
|
||||||
print_int :: proc "contextless" (x: int) { print_i64(i64(x)) }
|
print_int :: proc "contextless" (x: int) { print_i64(i64(x)) }
|
||||||
|
|
||||||
print_caller_location :: proc "contextless" (loc: Source_Code_Location) {
|
print_caller_location :: #force_no_inline proc "contextless" (loc: Source_Code_Location) {
|
||||||
print_string(loc.file_path)
|
print_string(loc.file_path)
|
||||||
when ODIN_ERROR_POS_STYLE == .Default {
|
when ODIN_ERROR_POS_STYLE == .Default {
|
||||||
print_byte('(')
|
print_byte('(')
|
||||||
print_u64(u64(loc.line))
|
print_u64(u64(loc.line))
|
||||||
print_byte(':')
|
if loc.column != 0 {
|
||||||
print_u64(u64(loc.column))
|
print_byte(':')
|
||||||
|
print_u64(u64(loc.column))
|
||||||
|
}
|
||||||
print_byte(')')
|
print_byte(')')
|
||||||
} else when ODIN_ERROR_POS_STYLE == .Unix {
|
} else when ODIN_ERROR_POS_STYLE == .Unix {
|
||||||
print_byte(':')
|
print_byte(':')
|
||||||
print_u64(u64(loc.line))
|
print_u64(u64(loc.line))
|
||||||
print_byte(':')
|
if loc.column != 0 {
|
||||||
print_u64(u64(loc.column))
|
print_byte(':')
|
||||||
|
print_u64(u64(loc.column))
|
||||||
|
}
|
||||||
print_byte(':')
|
print_byte(':')
|
||||||
} else {
|
} else {
|
||||||
#panic("unhandled ODIN_ERROR_POS_STYLE")
|
#panic("unhandled ODIN_ERROR_POS_STYLE")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
print_typeid :: proc "contextless" (id: typeid) {
|
print_typeid :: #force_no_inline proc "contextless" (id: typeid) {
|
||||||
when ODIN_NO_RTTI {
|
when ODIN_NO_RTTI {
|
||||||
if id == nil {
|
if id == nil {
|
||||||
print_string("nil")
|
print_string("nil")
|
||||||
@@ -257,7 +261,9 @@ print_typeid :: proc "contextless" (id: typeid) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
print_type :: proc "contextless" (ti: ^Type_Info) {
|
|
||||||
|
@(optimization_mode="favor_size")
|
||||||
|
print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
|
||||||
if ti == nil {
|
if ti == nil {
|
||||||
print_string("nil")
|
print_string("nil")
|
||||||
return
|
return
|
||||||
@@ -395,15 +401,16 @@ print_type :: proc "contextless" (ti: ^Type_Info) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
print_string("struct ")
|
print_string("struct ")
|
||||||
if info.is_packed { print_string("#packed ") }
|
if .packed in info.flags { print_string("#packed ") }
|
||||||
if info.is_raw_union { print_string("#raw_union ") }
|
if .raw_union in info.flags { print_string("#raw_union ") }
|
||||||
if info.custom_align {
|
if .no_copy in info.flags { print_string("#no_copy ") }
|
||||||
|
if .align in info.flags {
|
||||||
print_string("#align(")
|
print_string("#align(")
|
||||||
print_u64(u64(ti.align))
|
print_u64(u64(ti.align))
|
||||||
print_string(") ")
|
print_string(") ")
|
||||||
}
|
}
|
||||||
print_byte('{')
|
print_byte('{')
|
||||||
for name, i in info.names {
|
for name, i in info.names[:info.field_count] {
|
||||||
if i > 0 { print_string(", ") }
|
if i > 0 { print_string(", ") }
|
||||||
print_string(name)
|
print_string(name)
|
||||||
print_string(": ")
|
print_string(": ")
|
||||||
@@ -459,6 +466,20 @@ print_type :: proc "contextless" (ti: ^Type_Info) {
|
|||||||
}
|
}
|
||||||
print_byte(']')
|
print_byte(']')
|
||||||
|
|
||||||
|
case Type_Info_Bit_Field:
|
||||||
|
print_string("bit_field ")
|
||||||
|
print_type(info.backing_type)
|
||||||
|
print_string(" {")
|
||||||
|
for name, i in info.names[:info.field_count] {
|
||||||
|
if i > 0 { print_string(", ") }
|
||||||
|
print_string(name)
|
||||||
|
print_string(": ")
|
||||||
|
print_type(info.types[i])
|
||||||
|
print_string(" | ")
|
||||||
|
print_u64(u64(info.bit_sizes[i]))
|
||||||
|
}
|
||||||
|
print_byte('}')
|
||||||
|
|
||||||
|
|
||||||
case Type_Info_Simd_Vector:
|
case Type_Info_Simd_Vector:
|
||||||
print_string("#simd[")
|
print_string("#simd[")
|
||||||
@@ -466,18 +487,6 @@ print_type :: proc "contextless" (ti: ^Type_Info) {
|
|||||||
print_byte(']')
|
print_byte(']')
|
||||||
print_type(info.elem)
|
print_type(info.elem)
|
||||||
|
|
||||||
case Type_Info_Relative_Pointer:
|
|
||||||
print_string("#relative(")
|
|
||||||
print_type(info.base_integer)
|
|
||||||
print_string(") ")
|
|
||||||
print_type(info.pointer)
|
|
||||||
|
|
||||||
case Type_Info_Relative_Multi_Pointer:
|
|
||||||
print_string("#relative(")
|
|
||||||
print_type(info.base_integer)
|
|
||||||
print_string(") ")
|
|
||||||
print_type(info.pointer)
|
|
||||||
|
|
||||||
case Type_Info_Matrix:
|
case Type_Info_Matrix:
|
||||||
print_string("matrix[")
|
print_string("matrix[")
|
||||||
print_u64(u64(info.row_count))
|
print_u64(u64(info.row_count))
|
||||||
@@ -4,7 +4,7 @@ when ODIN_NO_CRT && ODIN_OS == .Windows {
|
|||||||
foreign import lib "system:NtDll.lib"
|
foreign import lib "system:NtDll.lib"
|
||||||
|
|
||||||
@(private="file")
|
@(private="file")
|
||||||
@(default_calling_convention="stdcall")
|
@(default_calling_convention="system")
|
||||||
foreign lib {
|
foreign lib {
|
||||||
RtlMoveMemory :: proc(dst, s: rawptr, length: int) ---
|
RtlMoveMemory :: proc(dst, s: rawptr, length: int) ---
|
||||||
RtlFillMemory :: proc(dst: rawptr, length: int, fill: i32) ---
|
RtlFillMemory :: proc(dst: rawptr, length: int, fill: i32) ---
|
||||||
@@ -25,21 +25,38 @@ when ODIN_NO_CRT && ODIN_OS == .Windows {
|
|||||||
RtlMoveMemory(dst, src, len)
|
RtlMoveMemory(dst, src, len)
|
||||||
return dst
|
return dst
|
||||||
}
|
}
|
||||||
} else when ODIN_NO_CRT || (ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32) {
|
} else when ODIN_NO_CRT || (ODIN_OS != .Orca && (ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32)) {
|
||||||
|
// NOTE: on wasm, calls to these procs are generated (by LLVM) with type `i32` instead of `int`.
|
||||||
|
//
|
||||||
|
// NOTE: `#any_int` is also needed, because calls that we generate (and package code)
|
||||||
|
// will be using `int` and need to be converted.
|
||||||
|
int_t :: i32 when ODIN_ARCH == .wasm64p32 else int
|
||||||
|
|
||||||
@(link_name="memset", linkage="strong", require)
|
@(link_name="memset", linkage="strong", require)
|
||||||
memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
|
memset :: proc "c" (ptr: rawptr, val: i32, #any_int len: int_t) -> rawptr {
|
||||||
if ptr != nil && len != 0 {
|
if ptr != nil && len != 0 {
|
||||||
b := byte(val)
|
b := byte(val)
|
||||||
p := ([^]byte)(ptr)
|
p := ([^]byte)(ptr)
|
||||||
for i := 0; i < len; i += 1 {
|
for i := int_t(0); i < len; i += 1 {
|
||||||
p[i] = b
|
p[i] = b
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ptr
|
return ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(link_name="bzero", linkage="strong", require)
|
||||||
|
bzero :: proc "c" (ptr: rawptr, #any_int len: int_t) -> rawptr {
|
||||||
|
if ptr != nil && len != 0 {
|
||||||
|
p := ([^]byte)(ptr)
|
||||||
|
for i := int_t(0); i < len; i += 1 {
|
||||||
|
p[i] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ptr
|
||||||
|
}
|
||||||
|
|
||||||
@(link_name="memmove", linkage="strong", require)
|
@(link_name="memmove", linkage="strong", require)
|
||||||
memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
|
memmove :: proc "c" (dst, src: rawptr, #any_int len: int_t) -> rawptr {
|
||||||
d, s := ([^]byte)(dst), ([^]byte)(src)
|
d, s := ([^]byte)(dst), ([^]byte)(src)
|
||||||
if d == s || len == 0 {
|
if d == s || len == 0 {
|
||||||
return dst
|
return dst
|
||||||
@@ -52,7 +69,7 @@ when ODIN_NO_CRT && ODIN_OS == .Windows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if s > d && uintptr(s)-uintptr(d) < uintptr(len) {
|
if s > d && uintptr(s)-uintptr(d) < uintptr(len) {
|
||||||
for i := 0; i < len; i += 1 {
|
for i := int_t(0); i < len; i += 1 {
|
||||||
d[i] = s[i]
|
d[i] = s[i]
|
||||||
}
|
}
|
||||||
return dst
|
return dst
|
||||||
@@ -60,10 +77,10 @@ when ODIN_NO_CRT && ODIN_OS == .Windows {
|
|||||||
return memcpy(dst, src, len)
|
return memcpy(dst, src, len)
|
||||||
}
|
}
|
||||||
@(link_name="memcpy", linkage="strong", require)
|
@(link_name="memcpy", linkage="strong", require)
|
||||||
memcpy :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
|
memcpy :: proc "c" (dst, src: rawptr, #any_int len: int_t) -> rawptr {
|
||||||
d, s := ([^]byte)(dst), ([^]byte)(src)
|
d, s := ([^]byte)(dst), ([^]byte)(src)
|
||||||
if d != s {
|
if d != s {
|
||||||
for i := 0; i < len; i += 1 {
|
for i := int_t(0); i < len; i += 1 {
|
||||||
d[i] = s[i]
|
d[i] = s[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
//+private
|
#+private
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
foreign import "system:Foundation.framework"
|
foreign import "system:Foundation.framework"
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
objc_id :: ^intrinsics.objc_object
|
objc_id :: ^intrinsics.objc_object
|
||||||
objc_Class :: ^intrinsics.objc_class
|
objc_Class :: ^intrinsics.objc_class
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
//+build js
|
#+build js
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
init_default_context_for_js: Context
|
init_default_context_for_js: Context
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
#+build wasm32, wasm64p32
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
ti_int :: struct #raw_union {
|
||||||
|
using s: struct { lo, hi: u64 },
|
||||||
|
all: i128,
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
ti_uint :: struct #raw_union {
|
||||||
|
using s: struct { lo, hi: u64 },
|
||||||
|
all: u128,
|
||||||
|
}
|
||||||
|
|
||||||
|
@(link_name="__ashlti3", linkage="strong")
|
||||||
|
__ashlti3 :: proc "contextless" (a: i128, b: u32) -> i128 {
|
||||||
|
bits :: 64
|
||||||
|
|
||||||
|
input: ti_int = ---
|
||||||
|
result: ti_int = ---
|
||||||
|
input.all = a
|
||||||
|
if b & bits != 0 {
|
||||||
|
result.lo = 0
|
||||||
|
result.hi = input.lo << (b-bits)
|
||||||
|
} else {
|
||||||
|
if b == 0 {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
result.lo = input.lo<<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" (a, b: i128) -> i128 {
|
||||||
|
x, y, r: ti_int
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
@(link_name="__udivti3", linkage="strong")
|
||||||
|
udivti3 :: proc "c" (la, ha, lb, hb: u64) -> u128 {
|
||||||
|
a, b: ti_uint
|
||||||
|
a.lo, a.hi = la, ha
|
||||||
|
b.lo, b.hi = lb, hb
|
||||||
|
return udivmodti4(a.all, b.all, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(link_name="__lshrti3", linkage="strong")
|
||||||
|
__lshrti3 :: proc "c" (a: i128, b: u32) -> i128 {
|
||||||
|
bits :: 64
|
||||||
|
|
||||||
|
input, result: ti_int
|
||||||
|
input.all = a
|
||||||
|
if b & bits != 0 {
|
||||||
|
result.hi = 0
|
||||||
|
result.lo = input.hi >> (b - bits)
|
||||||
|
} else if b == 0 {
|
||||||
|
return a
|
||||||
|
} else {
|
||||||
|
result.hi = input.hi >> b
|
||||||
|
result.lo = (input.hi << (bits - b)) | (input.lo >> b)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.all
|
||||||
|
}
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
//+private
|
#+private
|
||||||
|
#+no-instrumentation
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
foreign import kernel32 "system:Kernel32.lib"
|
foreign import kernel32 "system:Kernel32.lib"
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
foreign kernel32 {
|
foreign kernel32 {
|
||||||
RaiseException :: proc "stdcall" (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: u32, lpArguments: ^uint) -> ! ---
|
RaiseException :: proc "system" (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: u32, lpArguments: ^uint) -> ! ---
|
||||||
}
|
}
|
||||||
|
|
||||||
windows_trap_array_bounds :: proc "contextless" () -> ! {
|
windows_trap_array_bounds :: proc "contextless" () -> ! {
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//+private
|
#+private
|
||||||
|
#+no-instrumentation
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
@require foreign import "system:int64.lib"
|
@require foreign import "system:int64.lib"
|
||||||
@@ -12,7 +13,7 @@ windows_trap_array_bounds :: proc "contextless" () -> ! {
|
|||||||
EXCEPTION_ARRAY_BOUNDS_EXCEEDED :: 0xC000008C
|
EXCEPTION_ARRAY_BOUNDS_EXCEEDED :: 0xC000008C
|
||||||
|
|
||||||
foreign kernel32 {
|
foreign kernel32 {
|
||||||
RaiseException :: proc "stdcall" (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: DWORD, lpArguments: ^ULONG_PTR) -> ! ---
|
RaiseException :: proc "system" (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: DWORD, lpArguments: ^ULONG_PTR) -> ! ---
|
||||||
}
|
}
|
||||||
|
|
||||||
RaiseException(EXCEPTION_ARRAY_BOUNDS_EXCEEDED, 0, 0, nil)
|
RaiseException(EXCEPTION_ARRAY_BOUNDS_EXCEEDED, 0, 0, nil)
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
|
random_generator_read_bytes :: proc(rg: Random_Generator, p: []byte) -> bool {
|
||||||
|
if rg.procedure != nil {
|
||||||
|
rg.procedure(rg.data, .Read, p)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
|
random_generator_read_ptr :: proc(rg: Random_Generator, p: rawptr, len: uint) -> bool {
|
||||||
|
if rg.procedure != nil {
|
||||||
|
rg.procedure(rg.data, .Read, ([^]byte)(p)[:len])
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
|
random_generator_query_info :: proc(rg: Random_Generator) -> (info: Random_Generator_Query_Info) {
|
||||||
|
if rg.procedure != nil {
|
||||||
|
rg.procedure(rg.data, .Query_Info, ([^]byte)(&info)[:size_of(info)])
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
random_generator_reset_bytes :: proc(rg: Random_Generator, p: []byte) {
|
||||||
|
if rg.procedure != nil {
|
||||||
|
rg.procedure(rg.data, .Reset, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
random_generator_reset_u64 :: proc(rg: Random_Generator, p: u64) {
|
||||||
|
if rg.procedure != nil {
|
||||||
|
p := p
|
||||||
|
rg.procedure(rg.data, .Reset, ([^]byte)(&p)[:size_of(p)])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Default_Random_State :: struct {
|
||||||
|
state: u64,
|
||||||
|
inc: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
default_random_generator_proc :: proc(data: rawptr, mode: Random_Generator_Mode, p: []byte) {
|
||||||
|
@(require_results)
|
||||||
|
read_u64 :: proc "contextless" (r: ^Default_Random_State) -> u64 {
|
||||||
|
old_state := r.state
|
||||||
|
r.state = old_state * 6364136223846793005 + (r.inc|1)
|
||||||
|
xor_shifted := (((old_state >> 59) + 5) ~ old_state) * 12605985483714917081
|
||||||
|
rot := (old_state >> 59)
|
||||||
|
return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 63))
|
||||||
|
}
|
||||||
|
|
||||||
|
@(thread_local)
|
||||||
|
global_rand_seed: Default_Random_State
|
||||||
|
|
||||||
|
init :: proc "contextless" (r: ^Default_Random_State, seed: u64) {
|
||||||
|
seed := seed
|
||||||
|
if seed == 0 {
|
||||||
|
seed = u64(intrinsics.read_cycle_counter())
|
||||||
|
}
|
||||||
|
r.state = 0
|
||||||
|
r.inc = (seed << 1) | 1
|
||||||
|
_ = read_u64(r)
|
||||||
|
r.state += seed
|
||||||
|
_ = read_u64(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
r: ^Default_Random_State = ---
|
||||||
|
if data == nil {
|
||||||
|
r = &global_rand_seed
|
||||||
|
} else {
|
||||||
|
r = cast(^Default_Random_State)data
|
||||||
|
}
|
||||||
|
|
||||||
|
switch mode {
|
||||||
|
case .Read:
|
||||||
|
if r.state == 0 && r.inc == 0 {
|
||||||
|
init(r, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch len(p) {
|
||||||
|
case size_of(u64):
|
||||||
|
// Fast path for a 64-bit destination.
|
||||||
|
intrinsics.unaligned_store((^u64)(raw_data(p)), read_u64(r))
|
||||||
|
case:
|
||||||
|
// All other cases.
|
||||||
|
pos := i8(0)
|
||||||
|
val := u64(0)
|
||||||
|
for &v in p {
|
||||||
|
if pos == 0 {
|
||||||
|
val = read_u64(r)
|
||||||
|
pos = 7
|
||||||
|
}
|
||||||
|
v = byte(val)
|
||||||
|
val >>= 8
|
||||||
|
pos -= 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case .Reset:
|
||||||
|
seed: u64
|
||||||
|
mem_copy_non_overlapping(&seed, raw_data(p), min(size_of(seed), len(p)))
|
||||||
|
init(r, seed)
|
||||||
|
|
||||||
|
case .Query_Info:
|
||||||
|
if len(p) != size_of(Random_Generator_Query_Info) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
info := (^Random_Generator_Query_Info)(raw_data(p))
|
||||||
|
info^ += {.Uniform, .Resettable}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default_random_generator :: proc "contextless" (state: ^Default_Random_State = nil) -> Random_Generator {
|
||||||
|
return {
|
||||||
|
procedure = default_random_generator_proc,
|
||||||
|
data = state,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,6 +1,6 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
|
udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
|
||||||
_ctz :: intrinsics.count_trailing_zeros
|
_ctz :: intrinsics.count_trailing_zeros
|
||||||
@@ -58,7 +58,7 @@ udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
|
|||||||
return u128(n[high] >> _ctz(d[high]))
|
return u128(n[high] >> _ctz(d[high]))
|
||||||
}
|
}
|
||||||
|
|
||||||
sr = transmute(u32)(i32(_clz(d[high])) - i32(_clz(n[high])))
|
sr = u32(i32(_clz(d[high])) - i32(_clz(n[high])))
|
||||||
if sr > U64_BITS - 2 {
|
if sr > U64_BITS - 2 {
|
||||||
if rem != nil {
|
if rem != nil {
|
||||||
rem^ = a
|
rem^ = a
|
||||||
@@ -107,7 +107,7 @@ udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
|
|||||||
r[low] = n[high] >> (sr - U64_BITS)
|
r[low] = n[high] >> (sr - U64_BITS)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sr = transmute(u32)(i32(_clz(d[high])) - i32(_clz(n[high])))
|
sr = u32(i32(_clz(d[high])) - i32(_clz(n[high])))
|
||||||
|
|
||||||
if sr > U64_BITS - 1 {
|
if sr > U64_BITS - 1 {
|
||||||
if rem != nil {
|
if rem != nil {
|
||||||
@@ -143,7 +143,7 @@ udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
|
|||||||
r_all = transmute(u128)r
|
r_all = transmute(u128)r
|
||||||
s := i128(b - r_all - 1) >> (U128_BITS - 1)
|
s := i128(b - r_all - 1) >> (U128_BITS - 1)
|
||||||
carry = u32(s & 1)
|
carry = u32(s & 1)
|
||||||
r_all -= b & transmute(u128)s
|
r_all -= b & u128(s)
|
||||||
r = transmute([2]u64)r_all
|
r = transmute([2]u64)r_all
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,871 @@
|
|||||||
|
#+build wasm32, wasm64p32
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
|
||||||
|
/*
|
||||||
|
Port of emmalloc, modified for use in Odin.
|
||||||
|
|
||||||
|
Invariants:
|
||||||
|
- Per-allocation header overhead is 8 bytes, smallest allocated payload
|
||||||
|
amount is 8 bytes, and a multiple of 4 bytes.
|
||||||
|
- Acquired memory blocks are subdivided into disjoint regions that lie
|
||||||
|
next to each other.
|
||||||
|
- A region is either in used or free.
|
||||||
|
Used regions may be adjacent, and a used and unused region
|
||||||
|
may be adjacent, but not two unused ones - they would be
|
||||||
|
merged.
|
||||||
|
- Memory allocation takes constant time, unless the alloc needs to wasm_memory_grow()
|
||||||
|
or memory is very close to being exhausted.
|
||||||
|
- Free and used regions are managed inside "root regions", which are slabs
|
||||||
|
of memory acquired via wasm_memory_grow().
|
||||||
|
- Memory retrieved using wasm_memory_grow() can not be given back to the OS.
|
||||||
|
Therefore, frees are internal to the allocator.
|
||||||
|
|
||||||
|
Copyright (c) 2010-2014 Emscripten authors, see AUTHORS file.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
WASM_Allocator :: struct #no_copy {
|
||||||
|
// The minimum alignment of allocations.
|
||||||
|
alignment: uint,
|
||||||
|
// A region that contains as payload a single forward linked list of pointers to
|
||||||
|
// root regions of each disjoint region blocks.
|
||||||
|
list_of_all_regions: ^Root_Region,
|
||||||
|
// For each of the buckets, maintain a linked list head node. The head node for each
|
||||||
|
// free region is a sentinel node that does not actually represent any free space, but
|
||||||
|
// the sentinel is used to avoid awkward testing against (if node == freeRegionHeadNode)
|
||||||
|
// when adding and removing elements from the linked list, i.e. we are guaranteed that
|
||||||
|
// the sentinel node is always fixed and there, and the actual free region list elements
|
||||||
|
// start at free_region_buckets[i].next each.
|
||||||
|
free_region_buckets: [NUM_FREE_BUCKETS]Region,
|
||||||
|
// A bitmask that tracks the population status for each of the 64 distinct memory regions:
|
||||||
|
// a zero at bit position i means that the free list bucket i is empty. This bitmask is
|
||||||
|
// used to avoid redundant scanning of the 64 different free region buckets: instead by
|
||||||
|
// looking at the bitmask we can find in constant time an index to a free region bucket
|
||||||
|
// that contains free memory of desired size.
|
||||||
|
free_region_buckets_used: BUCKET_BITMASK_T,
|
||||||
|
// Because wasm memory can only be allocated in pages of 64k at a time, we keep any
|
||||||
|
// spilled/unused bytes that are left from the allocated pages here, first using this
|
||||||
|
// when bytes are needed.
|
||||||
|
spill: []byte,
|
||||||
|
// Mutex for thread safety, only used if the target feature "atomics" is enabled.
|
||||||
|
mu: Mutex_State,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not required to be called, called on first allocation otherwise.
|
||||||
|
wasm_allocator_init :: proc(a: ^WASM_Allocator, alignment: uint = 8) {
|
||||||
|
assert(is_power_of_two(alignment), "alignment must be a power of two")
|
||||||
|
assert(alignment > 4, "alignment must be more than 4")
|
||||||
|
|
||||||
|
a.alignment = alignment
|
||||||
|
|
||||||
|
for i in 0..<NUM_FREE_BUCKETS {
|
||||||
|
a.free_region_buckets[i].next = &a.free_region_buckets[i]
|
||||||
|
a.free_region_buckets[i].prev = a.free_region_buckets[i].next
|
||||||
|
}
|
||||||
|
|
||||||
|
if !claim_more_memory(a, 3*size_of(Region)) {
|
||||||
|
panic("wasm_allocator: initial memory could not be allocated")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
global_default_wasm_allocator_data: WASM_Allocator
|
||||||
|
|
||||||
|
default_wasm_allocator :: proc() -> Allocator {
|
||||||
|
return wasm_allocator(&global_default_wasm_allocator_data)
|
||||||
|
}
|
||||||
|
|
||||||
|
wasm_allocator :: proc(a: ^WASM_Allocator) -> Allocator {
|
||||||
|
return {
|
||||||
|
data = a,
|
||||||
|
procedure = wasm_allocator_proc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wasm_allocator_proc :: proc(a: rawptr, mode: Allocator_Mode, size, alignment: int, old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||||
|
a := (^WASM_Allocator)(a)
|
||||||
|
if a == nil {
|
||||||
|
a = &global_default_wasm_allocator_data
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.alignment == 0 {
|
||||||
|
wasm_allocator_init(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch mode {
|
||||||
|
case .Alloc:
|
||||||
|
ptr := aligned_alloc(a, uint(alignment), uint(size), loc)
|
||||||
|
if ptr == nil {
|
||||||
|
return nil, .Out_Of_Memory
|
||||||
|
}
|
||||||
|
intrinsics.mem_zero(ptr, size)
|
||||||
|
return ([^]byte)(ptr)[:size], nil
|
||||||
|
|
||||||
|
case .Alloc_Non_Zeroed:
|
||||||
|
ptr := aligned_alloc(a, uint(alignment), uint(size), loc)
|
||||||
|
if ptr == nil {
|
||||||
|
return nil, .Out_Of_Memory
|
||||||
|
}
|
||||||
|
return ([^]byte)(ptr)[:size], nil
|
||||||
|
|
||||||
|
case .Resize:
|
||||||
|
ptr := aligned_realloc(a, old_memory, uint(alignment), uint(size), loc)
|
||||||
|
if ptr == nil {
|
||||||
|
return nil, .Out_Of_Memory
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes := ([^]byte)(ptr)[:size]
|
||||||
|
|
||||||
|
if size > old_size {
|
||||||
|
new_region := raw_data(bytes[old_size:])
|
||||||
|
intrinsics.mem_zero(new_region, size - old_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes, nil
|
||||||
|
|
||||||
|
case .Resize_Non_Zeroed:
|
||||||
|
ptr := aligned_realloc(a, old_memory, uint(alignment), uint(size), loc)
|
||||||
|
if ptr == nil {
|
||||||
|
return nil, .Out_Of_Memory
|
||||||
|
}
|
||||||
|
return ([^]byte)(ptr)[:size], nil
|
||||||
|
|
||||||
|
case .Free:
|
||||||
|
free(a, old_memory, loc)
|
||||||
|
return nil, nil
|
||||||
|
|
||||||
|
case .Free_All, .Query_Info:
|
||||||
|
return nil, .Mode_Not_Implemented
|
||||||
|
|
||||||
|
case .Query_Features:
|
||||||
|
set := (^Allocator_Mode_Set)(old_memory)
|
||||||
|
if set != nil {
|
||||||
|
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Resize, .Resize_Non_Zeroed, .Query_Features }
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
unreachable()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the allocated size of the allocator (both free and used).
|
||||||
|
// If `nil` is given, the global allocator is used.
|
||||||
|
wasm_allocator_size :: proc(a: ^WASM_Allocator = nil) -> (size: uint) {
|
||||||
|
a := a
|
||||||
|
if a == nil {
|
||||||
|
a = &global_default_wasm_allocator_data
|
||||||
|
}
|
||||||
|
|
||||||
|
lock(a)
|
||||||
|
defer unlock(a)
|
||||||
|
|
||||||
|
root := a.list_of_all_regions
|
||||||
|
for root != nil {
|
||||||
|
size += uint(uintptr(root.end_ptr) - uintptr(root))
|
||||||
|
root = root.next
|
||||||
|
}
|
||||||
|
|
||||||
|
size += len(a.spill)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the amount of free memory on the allocator.
|
||||||
|
// If `nil` is given, the global allocator is used.
|
||||||
|
wasm_allocator_free_space :: proc(a: ^WASM_Allocator = nil) -> (free: uint) {
|
||||||
|
a := a
|
||||||
|
if a == nil {
|
||||||
|
a = &global_default_wasm_allocator_data
|
||||||
|
}
|
||||||
|
|
||||||
|
lock(a)
|
||||||
|
defer unlock(a)
|
||||||
|
|
||||||
|
bucket_index: u64 = 0
|
||||||
|
bucket_mask := a.free_region_buckets_used
|
||||||
|
|
||||||
|
for bucket_mask != 0 {
|
||||||
|
index_add := intrinsics.count_trailing_zeros(bucket_mask)
|
||||||
|
bucket_index += index_add
|
||||||
|
bucket_mask >>= index_add
|
||||||
|
for free_region := a.free_region_buckets[bucket_index].next; free_region != &a.free_region_buckets[bucket_index]; free_region = free_region.next {
|
||||||
|
free += free_region.size - REGION_HEADER_SIZE
|
||||||
|
}
|
||||||
|
bucket_index += 1
|
||||||
|
bucket_mask >>= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
free += len(a.spill)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
NUM_FREE_BUCKETS :: 64
|
||||||
|
@(private="file")
|
||||||
|
BUCKET_BITMASK_T :: u64
|
||||||
|
|
||||||
|
// Dynamic memory is subdivided into regions, in the format
|
||||||
|
|
||||||
|
// <size:u32> ..... <size:u32> | <size:u32> ..... <size:u32> | <size:u32> ..... <size:u32> | .....
|
||||||
|
|
||||||
|
// That is, at the bottom and top end of each memory region, the size of that region is stored. That allows traversing the
|
||||||
|
// memory regions backwards and forwards. Because each allocation must be at least a multiple of 4 bytes, the lowest two bits of
|
||||||
|
// each size field is unused. Free regions are distinguished by used regions by having the FREE_REGION_FLAG bit present
|
||||||
|
// in the size field. I.e. for free regions, the size field is odd, and for used regions, the size field reads even.
|
||||||
|
@(private="file")
|
||||||
|
FREE_REGION_FLAG :: 0x1
|
||||||
|
|
||||||
|
// Attempts to alloc more than this many bytes would cause an overflow when calculating the size of a region,
|
||||||
|
// therefore allocations larger than this are short-circuited immediately on entry.
|
||||||
|
@(private="file")
|
||||||
|
MAX_ALLOC_SIZE :: 0xFFFFFFC7
|
||||||
|
|
||||||
|
// A free region has the following structure:
|
||||||
|
// <size:uint> <prevptr> <nextptr> ... <size:uint>
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
Region :: struct {
|
||||||
|
size: uint,
|
||||||
|
prev, next: ^Region,
|
||||||
|
_at_the_end_of_this_struct_size: uint,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each memory block starts with a Root_Region at the beginning.
|
||||||
|
// The Root_Region specifies the size of the region block, and forms a linked
|
||||||
|
// list of all Root_Regions in the program, starting with `list_of_all_regions`
|
||||||
|
// below.
|
||||||
|
@(private="file")
|
||||||
|
Root_Region :: struct {
|
||||||
|
size: u32,
|
||||||
|
next: ^Root_Region,
|
||||||
|
end_ptr: ^byte,
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
Mutex_State :: enum u32 {
|
||||||
|
Unlocked = 0,
|
||||||
|
Locked = 1,
|
||||||
|
Waiting = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
lock :: proc(a: ^WASM_Allocator) {
|
||||||
|
when intrinsics.has_target_feature("atomics") {
|
||||||
|
@(cold)
|
||||||
|
lock_slow :: proc(a: ^WASM_Allocator, curr_state: Mutex_State) {
|
||||||
|
new_state := curr_state // Make a copy of it
|
||||||
|
|
||||||
|
spin_lock: for spin in 0..<i32(100) {
|
||||||
|
state, ok := intrinsics.atomic_compare_exchange_weak_explicit(&a.mu, .Unlocked, new_state, .Acquire, .Consume)
|
||||||
|
if ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if state == .Waiting {
|
||||||
|
break spin_lock
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := min(spin+1, 32); i > 0; i -= 1 {
|
||||||
|
intrinsics.cpu_relax()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set just in case 100 iterations did not do it
|
||||||
|
new_state = .Waiting
|
||||||
|
|
||||||
|
for {
|
||||||
|
if intrinsics.atomic_exchange_explicit(&a.mu, .Waiting, .Acquire) == .Unlocked {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := intrinsics.wasm_memory_atomic_wait32((^u32)(&a.mu), u32(new_state), -1)
|
||||||
|
assert(ret != 0)
|
||||||
|
intrinsics.cpu_relax()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if v := intrinsics.atomic_exchange_explicit(&a.mu, .Locked, .Acquire); v != .Unlocked {
|
||||||
|
lock_slow(a, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
unlock :: proc(a: ^WASM_Allocator) {
|
||||||
|
when intrinsics.has_target_feature("atomics") {
|
||||||
|
@(cold)
|
||||||
|
unlock_slow :: proc(a: ^WASM_Allocator) {
|
||||||
|
for {
|
||||||
|
s := intrinsics.wasm_memory_atomic_notify32((^u32)(&a.mu), 1)
|
||||||
|
if s >= 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch intrinsics.atomic_exchange_explicit(&a.mu, .Unlocked, .Release) {
|
||||||
|
case .Unlocked:
|
||||||
|
unreachable()
|
||||||
|
case .Locked:
|
||||||
|
// Okay
|
||||||
|
case .Waiting:
|
||||||
|
unlock_slow(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
assert_locked :: proc(a: ^WASM_Allocator) {
|
||||||
|
when intrinsics.has_target_feature("atomics") {
|
||||||
|
assert(intrinsics.atomic_load(&a.mu) != .Unlocked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
has_alignment_uintptr :: proc(ptr: uintptr, #any_int alignment: uintptr) -> bool {
|
||||||
|
return ptr & (alignment-1) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
has_alignment_uint :: proc(ptr: uint, alignment: uint) -> bool {
|
||||||
|
return ptr & (alignment-1) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
has_alignment :: proc {
|
||||||
|
has_alignment_uintptr,
|
||||||
|
has_alignment_uint,
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
REGION_HEADER_SIZE :: 2*size_of(uint)
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
SMALLEST_ALLOCATION_SIZE :: 2*size_of(rawptr)
|
||||||
|
|
||||||
|
// Subdivide regions of free space into distinct circular doubly linked lists, where each linked list
|
||||||
|
// represents a range of free space blocks. The following function compute_free_list_bucket() converts
|
||||||
|
// an allocation size to the bucket index that should be looked at.
|
||||||
|
#assert(NUM_FREE_BUCKETS == 64, "Following function is tailored specifically for the NUM_FREE_BUCKETS == 64 case")
|
||||||
|
@(private="file")
|
||||||
|
compute_free_list_bucket :: proc(size: uint) -> uint {
|
||||||
|
if size < 128 { return (size >> 3) - 1 }
|
||||||
|
|
||||||
|
clz := intrinsics.count_leading_zeros(i32(size))
|
||||||
|
bucket_index: i32 = ((clz > 19) \
|
||||||
|
? 110 - (clz<<2) + ((i32)(size >> (u32)(29-clz)) ~ 4) \
|
||||||
|
: min( 71 - (clz<<1) + ((i32)(size >> (u32)(30-clz)) ~ 2), NUM_FREE_BUCKETS-1))
|
||||||
|
|
||||||
|
assert(bucket_index >= 0)
|
||||||
|
assert(bucket_index < NUM_FREE_BUCKETS)
|
||||||
|
return uint(bucket_index)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
prev_region :: proc(region: ^Region) -> ^Region {
|
||||||
|
prev_region_size := ([^]uint)(region)[-1]
|
||||||
|
prev_region_size = prev_region_size & ~uint(FREE_REGION_FLAG)
|
||||||
|
return (^Region)(uintptr(region)-uintptr(prev_region_size))
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
next_region :: proc(region: ^Region) -> ^Region {
|
||||||
|
return (^Region)(uintptr(region)+uintptr(region.size))
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
region_ceiling_size :: proc(region: ^Region) -> uint {
|
||||||
|
return ([^]uint)(uintptr(region)+uintptr(region.size))[-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
region_is_free :: proc(r: ^Region) -> bool {
|
||||||
|
return region_ceiling_size(r) & FREE_REGION_FLAG >= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
region_is_in_use :: proc(r: ^Region) -> bool {
|
||||||
|
return r.size == region_ceiling_size(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
region_payload_start_ptr :: proc(r: ^Region) -> [^]byte {
|
||||||
|
return ([^]byte)(r)[size_of(uint):]
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
region_payload_end_ptr :: proc(r: ^Region) -> [^]byte {
|
||||||
|
return ([^]byte)(r)[r.size-size_of(uint):]
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
create_used_region :: proc(ptr: rawptr, size: uint) {
|
||||||
|
assert(has_alignment(uintptr(ptr), size_of(uint)))
|
||||||
|
assert(has_alignment(size, size_of(uint)))
|
||||||
|
assert(size >= size_of(Region))
|
||||||
|
|
||||||
|
uptr := ([^]uint)(ptr)
|
||||||
|
uptr[0] = size
|
||||||
|
uptr[size/size_of(uint)-1] = size
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
create_free_region :: proc(ptr: rawptr, size: uint) {
|
||||||
|
assert(has_alignment(uintptr(ptr), size_of(uint)))
|
||||||
|
assert(has_alignment(size, size_of(uint)))
|
||||||
|
assert(size >= size_of(Region))
|
||||||
|
|
||||||
|
free_region := (^Region)(ptr)
|
||||||
|
free_region.size = size
|
||||||
|
([^]uint)(ptr)[size/size_of(uint)-1] = size | FREE_REGION_FLAG
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
prepend_to_free_list :: proc(region: ^Region, prepend_to: ^Region) {
|
||||||
|
assert(region_is_free(region))
|
||||||
|
region.next = prepend_to
|
||||||
|
region.prev = prepend_to.prev
|
||||||
|
prepend_to.prev = region
|
||||||
|
region.prev.next = region
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
unlink_from_free_list :: proc(region: ^Region) {
|
||||||
|
assert(region_is_free(region))
|
||||||
|
region.prev.next = region.next
|
||||||
|
region.next.prev = region.prev
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
link_to_free_list :: proc(a: ^WASM_Allocator, free_region: ^Region) {
|
||||||
|
assert(free_region.size >= size_of(Region))
|
||||||
|
bucket_index := compute_free_list_bucket(free_region.size-REGION_HEADER_SIZE)
|
||||||
|
free_list_head := &a.free_region_buckets[bucket_index]
|
||||||
|
free_region.prev = free_list_head
|
||||||
|
free_region.next = free_list_head.next
|
||||||
|
free_list_head.next = free_region
|
||||||
|
free_region.next.prev = free_region
|
||||||
|
a.free_region_buckets_used |= BUCKET_BITMASK_T(1) << bucket_index
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
claim_more_memory :: proc(a: ^WASM_Allocator, num_bytes: uint) -> bool {
|
||||||
|
|
||||||
|
PAGE_SIZE :: 64 * 1024
|
||||||
|
|
||||||
|
page_alloc :: proc(page_count: int) -> []byte {
|
||||||
|
prev_page_count := intrinsics.wasm_memory_grow(0, uintptr(page_count))
|
||||||
|
if prev_page_count < 0 { return nil }
|
||||||
|
|
||||||
|
ptr := ([^]byte)(uintptr(prev_page_count) * PAGE_SIZE)
|
||||||
|
return ptr[:page_count * PAGE_SIZE]
|
||||||
|
}
|
||||||
|
|
||||||
|
alloc :: proc(a: ^WASM_Allocator, num_bytes: uint) -> (bytes: [^]byte) #no_bounds_check {
|
||||||
|
if uint(len(a.spill)) >= num_bytes {
|
||||||
|
bytes = raw_data(a.spill[:num_bytes])
|
||||||
|
a.spill = a.spill[num_bytes:]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pages := int((num_bytes / PAGE_SIZE) + 1)
|
||||||
|
allocated := page_alloc(pages)
|
||||||
|
if allocated == nil { return nil }
|
||||||
|
|
||||||
|
// If the allocated memory is a direct continuation of the spill from before,
|
||||||
|
// we can just extend the spill.
|
||||||
|
spill_end := uintptr(raw_data(a.spill)) + uintptr(len(a.spill))
|
||||||
|
if spill_end == uintptr(raw_data(allocated)) {
|
||||||
|
raw_spill := (^Raw_Slice)(&a.spill)
|
||||||
|
raw_spill.len += len(allocated)
|
||||||
|
} else {
|
||||||
|
// Otherwise, we have to "waste" the previous spill.
|
||||||
|
// Now this is probably uncommon, and will only happen if another code path
|
||||||
|
// is also requesting pages.
|
||||||
|
a.spill = allocated
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = raw_data(a.spill)
|
||||||
|
a.spill = a.spill[num_bytes:]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
num_bytes := num_bytes
|
||||||
|
num_bytes = align_forward(num_bytes, a.alignment)
|
||||||
|
|
||||||
|
start_ptr := alloc(a, uint(num_bytes))
|
||||||
|
if start_ptr == nil { return false }
|
||||||
|
|
||||||
|
assert(has_alignment(uintptr(start_ptr), align_of(uint)))
|
||||||
|
end_ptr := start_ptr[num_bytes:]
|
||||||
|
|
||||||
|
end_sentinel_region := (^Region)(end_ptr[-size_of(Region):])
|
||||||
|
create_used_region(end_sentinel_region, size_of(Region))
|
||||||
|
|
||||||
|
// If we are the sole user of wasm_memory_grow(), it will feed us continuous/consecutive memory addresses - take advantage
|
||||||
|
// of that if so: instead of creating two disjoint memory regions blocks, expand the previous one to a larger size.
|
||||||
|
prev_alloc_end_address := a.list_of_all_regions != nil ? a.list_of_all_regions.end_ptr : nil
|
||||||
|
if start_ptr == prev_alloc_end_address {
|
||||||
|
prev_end_sentinel := prev_region((^Region)(start_ptr))
|
||||||
|
assert(region_is_in_use(prev_end_sentinel))
|
||||||
|
prev_region := prev_region(prev_end_sentinel)
|
||||||
|
|
||||||
|
a.list_of_all_regions.end_ptr = end_ptr
|
||||||
|
|
||||||
|
// Two scenarios, either the last region of the previous block was in use, in which case we need to create
|
||||||
|
// a new free region in the newly allocated space; or it was free, in which case we can extend that region
|
||||||
|
// to cover a larger size.
|
||||||
|
if region_is_free(prev_region) {
|
||||||
|
new_free_region_size := uint(uintptr(end_sentinel_region) - uintptr(prev_region))
|
||||||
|
unlink_from_free_list(prev_region)
|
||||||
|
create_free_region(prev_region, new_free_region_size)
|
||||||
|
link_to_free_list(a, prev_region)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
start_ptr = start_ptr[-size_of(Region):]
|
||||||
|
} else {
|
||||||
|
create_used_region(start_ptr, size_of(Region))
|
||||||
|
|
||||||
|
new_region_block := (^Root_Region)(start_ptr)
|
||||||
|
new_region_block.next = a.list_of_all_regions
|
||||||
|
new_region_block.end_ptr = end_ptr
|
||||||
|
a.list_of_all_regions = new_region_block
|
||||||
|
start_ptr = start_ptr[size_of(Region):]
|
||||||
|
}
|
||||||
|
|
||||||
|
create_free_region(start_ptr, uint(uintptr(end_sentinel_region)-uintptr(start_ptr)))
|
||||||
|
link_to_free_list(a, (^Region)(start_ptr))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
validate_alloc_size :: proc(size: uint) -> uint {
|
||||||
|
#assert(size_of(uint) >= size_of(uintptr))
|
||||||
|
#assert(size_of(uint) % size_of(uintptr) == 0)
|
||||||
|
|
||||||
|
// NOTE: emmalloc aligns this forward on pointer size, but I think that is a mistake and will
|
||||||
|
// do bad on wasm64p32.
|
||||||
|
|
||||||
|
validated_size := size > SMALLEST_ALLOCATION_SIZE ? align_forward(size, size_of(uint)) : SMALLEST_ALLOCATION_SIZE
|
||||||
|
assert(validated_size >= size) // Assert we haven't wrapped.
|
||||||
|
|
||||||
|
return validated_size
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
allocate_memory :: proc(a: ^WASM_Allocator, alignment: uint, size: uint, loc := #caller_location) -> rawptr {
|
||||||
|
|
||||||
|
attempt_allocate :: proc(a: ^WASM_Allocator, free_region: ^Region, alignment, size: uint) -> rawptr {
|
||||||
|
assert_locked(a)
|
||||||
|
free_region := free_region
|
||||||
|
|
||||||
|
payload_start_ptr := uintptr(region_payload_start_ptr(free_region))
|
||||||
|
payload_start_ptr_aligned := align_forward(payload_start_ptr, uintptr(alignment))
|
||||||
|
payload_end_ptr := uintptr(region_payload_end_ptr(free_region))
|
||||||
|
|
||||||
|
if payload_start_ptr_aligned + uintptr(size) > payload_end_ptr {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have enough free space, so the memory allocation will be made into this region. Remove this free region
|
||||||
|
// from the list of free regions: whatever slop remains will be later added back to the free region pool.
|
||||||
|
unlink_from_free_list(free_region)
|
||||||
|
|
||||||
|
// Before we proceed further, fix up the boundary between this and the preceding region,
|
||||||
|
// so that the boundary between the two regions happens at a right spot for the payload to be aligned.
|
||||||
|
if payload_start_ptr != payload_start_ptr_aligned {
|
||||||
|
prev := prev_region(free_region)
|
||||||
|
assert(region_is_in_use(prev))
|
||||||
|
region_boundary_bump_amount := payload_start_ptr_aligned - payload_start_ptr
|
||||||
|
new_this_region_size := free_region.size - uint(region_boundary_bump_amount)
|
||||||
|
create_used_region(prev, prev.size + uint(region_boundary_bump_amount))
|
||||||
|
free_region = (^Region)(uintptr(free_region) + region_boundary_bump_amount)
|
||||||
|
free_region.size = new_this_region_size
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next, we need to decide whether this region is so large that it should be split into two regions,
|
||||||
|
// one representing the newly used memory area, and at the high end a remaining leftover free area.
|
||||||
|
// This splitting to two is done always if there is enough space for the high end to fit a region.
|
||||||
|
// Carve 'size' bytes of payload off this region. So,
|
||||||
|
// [sz prev next sz]
|
||||||
|
// becomes
|
||||||
|
// [sz payload sz] [sz prev next sz]
|
||||||
|
if size_of(Region) + REGION_HEADER_SIZE + size <= free_region.size {
|
||||||
|
new_free_region := (^Region)(uintptr(free_region) + REGION_HEADER_SIZE + uintptr(size))
|
||||||
|
create_free_region(new_free_region, free_region.size - size - REGION_HEADER_SIZE)
|
||||||
|
link_to_free_list(a, new_free_region)
|
||||||
|
create_used_region(free_region, size + REGION_HEADER_SIZE)
|
||||||
|
} else {
|
||||||
|
// There is not enough space to split the free memory region into used+free parts, so consume the whole
|
||||||
|
// region as used memory, not leaving a free memory region behind.
|
||||||
|
// Initialize the free region as used by resetting the ceiling size to the same value as the size at bottom.
|
||||||
|
([^]uint)(uintptr(free_region) + uintptr(free_region.size))[-1] = free_region.size
|
||||||
|
}
|
||||||
|
|
||||||
|
return rawptr(uintptr(free_region) + size_of(uint))
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_locked(a)
|
||||||
|
assert(is_power_of_two(alignment))
|
||||||
|
assert(size <= MAX_ALLOC_SIZE, "allocation too big", loc=loc)
|
||||||
|
|
||||||
|
alignment := alignment
|
||||||
|
alignment = max(alignment, a.alignment)
|
||||||
|
|
||||||
|
size := size
|
||||||
|
size = validate_alloc_size(size)
|
||||||
|
|
||||||
|
// Attempt to allocate memory starting from smallest bucket that can contain the required amount of memory.
|
||||||
|
// Under normal alignment conditions this should always be the first or second bucket we look at, but if
|
||||||
|
// performing an allocation with complex alignment, we may need to look at multiple buckets.
|
||||||
|
bucket_index := compute_free_list_bucket(size)
|
||||||
|
bucket_mask := a.free_region_buckets_used >> bucket_index
|
||||||
|
|
||||||
|
// Loop through each bucket that has free regions in it, based on bits set in free_region_buckets_used bitmap.
|
||||||
|
for bucket_mask != 0 {
|
||||||
|
index_add := intrinsics.count_trailing_zeros(bucket_mask)
|
||||||
|
bucket_index += uint(index_add)
|
||||||
|
bucket_mask >>= index_add
|
||||||
|
assert(bucket_index <= NUM_FREE_BUCKETS-1)
|
||||||
|
assert(a.free_region_buckets_used & (BUCKET_BITMASK_T(1) << bucket_index) > 0)
|
||||||
|
|
||||||
|
free_region := a.free_region_buckets[bucket_index].next
|
||||||
|
assert(free_region != nil)
|
||||||
|
if free_region != &a.free_region_buckets[bucket_index] {
|
||||||
|
ptr := attempt_allocate(a, free_region, alignment, size)
|
||||||
|
if ptr != nil {
|
||||||
|
return ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// We were not able to allocate from the first region found in this bucket, so penalize
|
||||||
|
// the region by cycling it to the end of the doubly circular linked list. (constant time)
|
||||||
|
// This provides a randomized guarantee that when performing allocations of size k to a
|
||||||
|
// bucket of [k-something, k+something] range, we will not always attempt to satisfy the
|
||||||
|
// allocation from the same available region at the front of the list, but we try each
|
||||||
|
// region in turn.
|
||||||
|
unlink_from_free_list(free_region)
|
||||||
|
prepend_to_free_list(free_region, &a.free_region_buckets[bucket_index])
|
||||||
|
// But do not stick around to attempt to look at other regions in this bucket - move
|
||||||
|
// to search the next populated bucket index if this did not fit. This gives a practical
|
||||||
|
// "allocation in constant time" guarantee, since the next higher bucket will only have
|
||||||
|
// regions that are all of strictly larger size than the requested allocation. Only if
|
||||||
|
// there is a difficult alignment requirement we may fail to perform the allocation from
|
||||||
|
// a region in the next bucket, and if so, we keep trying higher buckets until one of them
|
||||||
|
// works.
|
||||||
|
bucket_index += 1
|
||||||
|
bucket_mask >>= 1
|
||||||
|
} else {
|
||||||
|
// This bucket was not populated after all with any regions,
|
||||||
|
// but we just had a stale bit set to mark a populated bucket.
|
||||||
|
// Reset the bit to update latest status so that we do not
|
||||||
|
// redundantly look at this bucket again.
|
||||||
|
a.free_region_buckets_used &~= BUCKET_BITMASK_T(1) << bucket_index
|
||||||
|
bucket_mask ~= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
assert((bucket_index == NUM_FREE_BUCKETS && bucket_mask == 0) || (bucket_mask == a.free_region_buckets_used >> bucket_index))
|
||||||
|
}
|
||||||
|
|
||||||
|
// None of the buckets were able to accommodate an allocation. If this happens we are almost out of memory.
|
||||||
|
// The largest bucket might contain some suitable regions, but we only looked at one region in that bucket, so
|
||||||
|
// as a last resort, loop through more free regions in the bucket that represents the largest allocations available.
|
||||||
|
// But only if the bucket representing largest allocations available is not any of the first thirty buckets,
|
||||||
|
// these represent allocatable areas less than <1024 bytes - which could be a lot of scrap.
|
||||||
|
// In such case, prefer to claim more memory right away.
|
||||||
|
largest_bucket_index := NUM_FREE_BUCKETS - 1 - intrinsics.count_leading_zeros(a.free_region_buckets_used)
|
||||||
|
// free_region will be null if there is absolutely no memory left. (all buckets are 100% used)
|
||||||
|
free_region := a.free_region_buckets_used > 0 ? a.free_region_buckets[largest_bucket_index].next : nil
|
||||||
|
// The 30 first free region buckets cover memory blocks < 2048 bytes, so skip looking at those here (too small)
|
||||||
|
if a.free_region_buckets_used >> 30 > 0 {
|
||||||
|
// Look only at a constant number of regions in this bucket max, to avoid bad worst case behavior.
|
||||||
|
// If this many regions cannot find free space, we give up and prefer to claim more memory instead.
|
||||||
|
max_regions_to_try_before_giving_up :: 99
|
||||||
|
num_tries_left := max_regions_to_try_before_giving_up
|
||||||
|
for ; free_region != &a.free_region_buckets[largest_bucket_index] && num_tries_left > 0; num_tries_left -= 1 {
|
||||||
|
ptr := attempt_allocate(a, free_region, alignment, size)
|
||||||
|
if ptr != nil {
|
||||||
|
return ptr
|
||||||
|
}
|
||||||
|
free_region = free_region.next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We were unable to find a free memory region. Must claim more memory!
|
||||||
|
num_bytes_to_claim := size+size_of(Region)*3
|
||||||
|
if alignment > a.alignment {
|
||||||
|
num_bytes_to_claim += alignment
|
||||||
|
}
|
||||||
|
success := claim_more_memory(a, num_bytes_to_claim)
|
||||||
|
if (success) {
|
||||||
|
// Try allocate again with the newly available memory.
|
||||||
|
return allocate_memory(a, alignment, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// also claim_more_memory failed, we are really really constrained :( As a last resort, go back to looking at the
|
||||||
|
// bucket we already looked at above, continuing where the above search left off - perhaps there are
|
||||||
|
// regions we overlooked the first time that might be able to satisfy the allocation.
|
||||||
|
if free_region != nil {
|
||||||
|
for free_region != &a.free_region_buckets[largest_bucket_index] {
|
||||||
|
ptr := attempt_allocate(a, free_region, alignment, size)
|
||||||
|
if ptr != nil {
|
||||||
|
return ptr
|
||||||
|
}
|
||||||
|
free_region = free_region.next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fully out of memory.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
aligned_alloc :: proc(a: ^WASM_Allocator, alignment, size: uint, loc := #caller_location) -> rawptr {
|
||||||
|
lock(a)
|
||||||
|
defer unlock(a)
|
||||||
|
|
||||||
|
return allocate_memory(a, alignment, size, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
free :: proc(a: ^WASM_Allocator, ptr: rawptr, loc := #caller_location) {
|
||||||
|
if ptr == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
region_start_ptr := uintptr(ptr) - size_of(uint)
|
||||||
|
region := (^Region)(region_start_ptr)
|
||||||
|
assert(has_alignment(region_start_ptr, size_of(uint)))
|
||||||
|
|
||||||
|
lock(a)
|
||||||
|
defer unlock(a)
|
||||||
|
|
||||||
|
size := region.size
|
||||||
|
assert(region_is_in_use(region), "double free or corrupt region", loc=loc)
|
||||||
|
|
||||||
|
prev_region_size_field := ([^]uint)(region)[-1]
|
||||||
|
prev_region_size := prev_region_size_field & ~uint(FREE_REGION_FLAG)
|
||||||
|
if prev_region_size_field != prev_region_size {
|
||||||
|
prev_region := (^Region)(uintptr(region) - uintptr(prev_region_size))
|
||||||
|
unlink_from_free_list(prev_region)
|
||||||
|
region_start_ptr = uintptr(prev_region)
|
||||||
|
size += prev_region_size
|
||||||
|
}
|
||||||
|
|
||||||
|
next_reg := next_region(region)
|
||||||
|
size_at_end := (^uint)(region_payload_end_ptr(next_reg))^
|
||||||
|
if next_reg.size != size_at_end {
|
||||||
|
unlink_from_free_list(next_reg)
|
||||||
|
size += next_reg.size
|
||||||
|
}
|
||||||
|
|
||||||
|
create_free_region(rawptr(region_start_ptr), size)
|
||||||
|
link_to_free_list(a, (^Region)(region_start_ptr))
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
aligned_realloc :: proc(a: ^WASM_Allocator, ptr: rawptr, alignment, size: uint, loc := #caller_location) -> rawptr {
|
||||||
|
|
||||||
|
attempt_region_resize :: proc(a: ^WASM_Allocator, region: ^Region, size: uint) -> bool {
|
||||||
|
lock(a)
|
||||||
|
defer unlock(a)
|
||||||
|
|
||||||
|
// First attempt to resize this region, if the next region that follows this one
|
||||||
|
// is a free region.
|
||||||
|
next_reg := next_region(region)
|
||||||
|
next_region_end_ptr := uintptr(next_reg) + uintptr(next_reg.size)
|
||||||
|
size_at_ceiling := ([^]uint)(next_region_end_ptr)[-1]
|
||||||
|
if next_reg.size != size_at_ceiling { // Next region is free?
|
||||||
|
assert(region_is_free(next_reg))
|
||||||
|
new_next_region_start_ptr := uintptr(region) + uintptr(size)
|
||||||
|
assert(has_alignment(new_next_region_start_ptr, size_of(uint)))
|
||||||
|
// Next region does not shrink to too small size?
|
||||||
|
if new_next_region_start_ptr + size_of(Region) <= next_region_end_ptr {
|
||||||
|
unlink_from_free_list(next_reg)
|
||||||
|
create_free_region(rawptr(new_next_region_start_ptr), uint(next_region_end_ptr - new_next_region_start_ptr))
|
||||||
|
link_to_free_list(a, (^Region)(new_next_region_start_ptr))
|
||||||
|
create_used_region(region, uint(new_next_region_start_ptr - uintptr(region)))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// If we remove the next region altogether, allocation is satisfied?
|
||||||
|
if new_next_region_start_ptr <= next_region_end_ptr {
|
||||||
|
unlink_from_free_list(next_reg)
|
||||||
|
create_used_region(region, region.size + next_reg.size)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Next region is an used region - we cannot change its starting address. However if we are shrinking the
|
||||||
|
// size of this region, we can create a new free region between this and the next used region.
|
||||||
|
if size + size_of(Region) <= region.size {
|
||||||
|
free_region_size := region.size - size
|
||||||
|
create_used_region(region, size)
|
||||||
|
free_region := (^Region)(uintptr(region) + uintptr(size))
|
||||||
|
create_free_region(free_region, free_region_size)
|
||||||
|
link_to_free_list(a, free_region)
|
||||||
|
return true
|
||||||
|
} else if size <= region.size {
|
||||||
|
// Caller was asking to shrink the size, but due to not being able to fit a full Region in the shrunk
|
||||||
|
// area, we cannot actually do anything. This occurs if the shrink amount is really small. In such case,
|
||||||
|
// just call it success without doing any work.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if ptr == nil {
|
||||||
|
return aligned_alloc(a, alignment, size, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
if size == 0 {
|
||||||
|
free(a, ptr, loc)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if size > MAX_ALLOC_SIZE {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(is_power_of_two(alignment))
|
||||||
|
assert(has_alignment(uintptr(ptr), alignment), "realloc on different alignment than original allocation", loc=loc)
|
||||||
|
|
||||||
|
size := size
|
||||||
|
size = validate_alloc_size(size)
|
||||||
|
|
||||||
|
region := (^Region)(uintptr(ptr) - size_of(uint))
|
||||||
|
|
||||||
|
// Attempt an in-place resize.
|
||||||
|
if attempt_region_resize(a, region, size + REGION_HEADER_SIZE) {
|
||||||
|
return ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can't do it in-place, allocate new region and copy over.
|
||||||
|
newptr := aligned_alloc(a, alignment, size, loc)
|
||||||
|
if newptr != nil {
|
||||||
|
intrinsics.mem_copy(newptr, ptr, min(size, region.size - REGION_HEADER_SIZE))
|
||||||
|
free(a, ptr, loc=loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newptr
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2024 Epic Games Tools
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the “Software”), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -19,12 +19,16 @@ if "%VSCMD_ARG_TGT_ARCH%" neq "x64" (
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
for /f "usebackq tokens=1,2 delims=,=- " %%i in (`wmic os get LocalDateTime /value`) do @if %%i==LocalDateTime (
|
pushd misc
|
||||||
set CURR_DATE_TIME=%%j
|
cl /nologo get-date.c
|
||||||
)
|
popd
|
||||||
|
|
||||||
|
for /f %%i in ('misc\get-date') do (
|
||||||
|
set CURR_DATE_TIME=%%i
|
||||||
|
)
|
||||||
set curr_year=%CURR_DATE_TIME:~0,4%
|
set curr_year=%CURR_DATE_TIME:~0,4%
|
||||||
set curr_month=%CURR_DATE_TIME:~4,2%
|
set curr_month=%CURR_DATE_TIME:~4,2%
|
||||||
|
set curr_day=%CURR_DATE_TIME:~6,2%
|
||||||
|
|
||||||
:: Make sure this is a decent name and not generic
|
:: Make sure this is a decent name and not generic
|
||||||
set exe_name=odin.exe
|
set exe_name=odin.exe
|
||||||
@@ -45,23 +49,49 @@ if "%2" == "1" (
|
|||||||
set nightly=0
|
set nightly=0
|
||||||
)
|
)
|
||||||
|
|
||||||
set odin_version_raw="dev-%curr_year%-%curr_month%"
|
if %release_mode% equ 0 (
|
||||||
|
set V1=%curr_year%
|
||||||
|
set V2=%curr_month%
|
||||||
|
set V3=%curr_day%
|
||||||
|
) else (
|
||||||
|
set V1=%curr_year%
|
||||||
|
set V2=%curr_month%
|
||||||
|
set V3=0
|
||||||
|
)
|
||||||
|
set V4=0
|
||||||
|
set odin_version_full="%V1%.%V2%.%V3%.%V4%"
|
||||||
|
set odin_version_raw="dev-%V1%-%V2%"
|
||||||
|
|
||||||
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF
|
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF
|
||||||
|
rem Parse source code as utf-8 even on shift-jis and other codepages
|
||||||
|
rem See https://learn.microsoft.com/en-us/cpp/build/reference/utf-8-set-source-and-executable-character-sets-to-utf-8?view=msvc-170
|
||||||
|
set compiler_flags= %compiler_flags% /utf-8
|
||||||
set compiler_defines= -DODIN_VERSION_RAW=\"%odin_version_raw%\"
|
set compiler_defines= -DODIN_VERSION_RAW=\"%odin_version_raw%\"
|
||||||
|
|
||||||
|
rem fileversion is defined as {Major,Minor,Build,Private: u16} so a bit limited
|
||||||
|
set rc_flags=-nologo ^
|
||||||
|
-DV1=%V1% -DV2=%V2% -DV3=%V3% -DV4=%V4% ^
|
||||||
|
-DVF=%odin_version_full% -DNIGHTLY=%nightly%
|
||||||
|
|
||||||
|
where /Q git.exe || goto skip_git_hash
|
||||||
if not exist .git\ goto skip_git_hash
|
if not exist .git\ goto skip_git_hash
|
||||||
for /f "tokens=1,2" %%i IN ('git show "--pretty=%%cd %%h" "--date=format:%%Y-%%m" --no-patch --no-notes HEAD') do (
|
for /f "tokens=1,2" %%i IN ('git show "--pretty=%%cd %%h" "--date=format:%%Y-%%m" --no-patch --no-notes HEAD') do (
|
||||||
set odin_version_raw=dev-%%i
|
set odin_version_raw=dev-%%i
|
||||||
set GIT_SHA=%%j
|
set GIT_SHA=%%j
|
||||||
)
|
)
|
||||||
if %ERRORLEVEL% equ 0 set compiler_defines=%compiler_defines% -DGIT_SHA=\"%GIT_SHA%\"
|
if %ERRORLEVEL% equ 0 (
|
||||||
|
set compiler_defines=%compiler_defines% -DGIT_SHA=\"%GIT_SHA%\"
|
||||||
|
set rc_flags=%rc_flags% -DGIT_SHA=%GIT_SHA% -DVP=%odin_version_raw%:%GIT_SHA%
|
||||||
|
) else (
|
||||||
|
set rc_flags=%rc_flags% -DVP=%odin_version_raw%
|
||||||
|
)
|
||||||
:skip_git_hash
|
:skip_git_hash
|
||||||
|
|
||||||
if %nightly% equ 1 set compiler_defines=%compiler_defines% -DNIGHTLY
|
if %nightly% equ 1 set compiler_defines=%compiler_defines% -DNIGHTLY
|
||||||
|
|
||||||
if %release_mode% EQU 0 ( rem Debug
|
if %release_mode% EQU 0 ( rem Debug
|
||||||
set compiler_flags=%compiler_flags% -Od -MDd -Z7
|
set compiler_flags=%compiler_flags% -Od -MDd -Z7
|
||||||
|
set rc_flags=%rc_flags% -D_DEBUG
|
||||||
) else ( rem Release
|
) else ( rem Release
|
||||||
set compiler_flags=%compiler_flags% -O2 -MT -Z7
|
set compiler_flags=%compiler_flags% -O2 -MT -Z7
|
||||||
set compiler_defines=%compiler_defines% -DNO_ARRAY_BOUNDS_CHECK
|
set compiler_defines=%compiler_defines% -DNO_ARRAY_BOUNDS_CHECK
|
||||||
@@ -79,6 +109,8 @@ set libs= ^
|
|||||||
kernel32.lib ^
|
kernel32.lib ^
|
||||||
Synchronization.lib ^
|
Synchronization.lib ^
|
||||||
bin\llvm\windows\LLVM-C.lib
|
bin\llvm\windows\LLVM-C.lib
|
||||||
|
set odin_res=misc\odin.res
|
||||||
|
set odin_rc=misc\odin.rc
|
||||||
|
|
||||||
rem DO NOT TOUCH!
|
rem DO NOT TOUCH!
|
||||||
rem THIS TILDE STUFF IS FOR DEVELOPMENT ONLY!
|
rem THIS TILDE STUFF IS FOR DEVELOPMENT ONLY!
|
||||||
@@ -90,7 +122,7 @@ if %tilde_backend% EQU 1 (
|
|||||||
rem DO NOT TOUCH!
|
rem DO NOT TOUCH!
|
||||||
|
|
||||||
|
|
||||||
set linker_flags= -incremental:no -opt:ref -subsystem:console
|
set linker_flags= -incremental:no -opt:ref -subsystem:console -MANIFEST:EMBED
|
||||||
|
|
||||||
if %release_mode% EQU 0 ( rem Debug
|
if %release_mode% EQU 0 ( rem Debug
|
||||||
set linker_flags=%linker_flags% -debug /NATVIS:src\odin_compiler.natvis
|
set linker_flags=%linker_flags% -debug /NATVIS:src\odin_compiler.natvis
|
||||||
@@ -99,18 +131,24 @@ if %release_mode% EQU 0 ( rem Debug
|
|||||||
)
|
)
|
||||||
|
|
||||||
set compiler_settings=%compiler_includes% %compiler_flags% %compiler_warnings% %compiler_defines%
|
set compiler_settings=%compiler_includes% %compiler_flags% %compiler_warnings% %compiler_defines%
|
||||||
set linker_settings=%libs% %linker_flags%
|
set linker_settings=%libs% %odin_res% %linker_flags%
|
||||||
|
|
||||||
del *.pdb > NUL 2> NUL
|
del *.pdb > NUL 2> NUL
|
||||||
del *.ilk > NUL 2> NUL
|
del *.ilk > NUL 2> NUL
|
||||||
|
|
||||||
|
rc %rc_flags% %odin_rc%
|
||||||
cl %compiler_settings% "src\main.cpp" "src\libtommath.cpp" /link %linker_settings% -OUT:%exe_name%
|
cl %compiler_settings% "src\main.cpp" "src\libtommath.cpp" /link %linker_settings% -OUT:%exe_name%
|
||||||
|
mt -nologo -inputresource:%exe_name%;#1 -manifest misc\odin.manifest -outputresource:%exe_name%;#1 -validate_manifest -identity:"odin, processorArchitecture=amd64, version=%odin_version_full%, type=win32"
|
||||||
if %errorlevel% neq 0 goto end_of_build
|
if %errorlevel% neq 0 goto end_of_build
|
||||||
|
|
||||||
call build_vendor.bat
|
call build_vendor.bat
|
||||||
if %errorlevel% neq 0 goto end_of_build
|
if %errorlevel% neq 0 goto end_of_build
|
||||||
|
|
||||||
if %release_mode% EQU 0 odin run examples/demo
|
rem If the demo doesn't run for you and your CPU is more than a decade old, try -microarch:native
|
||||||
|
if %release_mode% EQU 0 odin run examples/demo -vet -strict-style -resource:examples/demo/demo.rc -- Hellope World
|
||||||
|
|
||||||
|
rem Many non-compiler devs seem to run debug build but don't realize.
|
||||||
|
if %release_mode% EQU 0 echo: & echo Debug compiler built. Note: run "build.bat release" if you want a faster, release mode compiler.
|
||||||
|
|
||||||
del *.obj > NUL 2> NUL
|
del *.obj > NUL 2> NUL
|
||||||
|
|
||||||
|
|||||||
+68
-21
@@ -2,7 +2,6 @@
|
|||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
: ${CPPFLAGS=}
|
: ${CPPFLAGS=}
|
||||||
: ${CXX=clang++}
|
|
||||||
: ${CXXFLAGS=}
|
: ${CXXFLAGS=}
|
||||||
: ${LDFLAGS=}
|
: ${LDFLAGS=}
|
||||||
: ${LLVM_CONFIG=}
|
: ${LLVM_CONFIG=}
|
||||||
@@ -24,17 +23,32 @@ error() {
|
|||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Brew advises people not to add llvm to their $PATH, so try and use brew to find it.
|
||||||
|
if [ -z "$LLVM_CONFIG" ] && [ -n "$(command -v brew)" ]; then
|
||||||
|
if [ -n "$(command -v $(brew --prefix llvm@19)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@19)/bin/llvm-config"
|
||||||
|
elif [ -n "$(command -v $(brew --prefix llvm@18)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@18)/bin/llvm-config"
|
||||||
|
elif [ -n "$(command -v $(brew --prefix llvm@17)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@17)/bin/llvm-config"
|
||||||
|
elif [ -n "$(command -v $(brew --prefix llvm@14)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@14)/bin/llvm-config"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -z "$LLVM_CONFIG" ]; then
|
if [ -z "$LLVM_CONFIG" ]; then
|
||||||
# darwin, linux, openbsd
|
# darwin, linux, openbsd
|
||||||
if [ -n "$(command -v llvm-config-17)" ]; then LLVM_CONFIG="llvm-config-17"
|
if [ -n "$(command -v llvm-config-19)" ]; then LLVM_CONFIG="llvm-config-19"
|
||||||
|
elif [ -n "$(command -v llvm-config-18)" ]; then LLVM_CONFIG="llvm-config-18"
|
||||||
|
elif [ -n "$(command -v llvm-config-17)" ]; then LLVM_CONFIG="llvm-config-17"
|
||||||
|
elif [ -n "$(command -v llvm-config-14)" ]; then LLVM_CONFIG="llvm-config-14"
|
||||||
elif [ -n "$(command -v llvm-config-13)" ]; then LLVM_CONFIG="llvm-config-13"
|
elif [ -n "$(command -v llvm-config-13)" ]; then LLVM_CONFIG="llvm-config-13"
|
||||||
elif [ -n "$(command -v llvm-config-12)" ]; then LLVM_CONFIG="llvm-config-12"
|
elif [ -n "$(command -v llvm-config-12)" ]; then LLVM_CONFIG="llvm-config-12"
|
||||||
elif [ -n "$(command -v llvm-config-11)" ]; then LLVM_CONFIG="llvm-config-11"
|
elif [ -n "$(command -v llvm-config-11)" ]; then LLVM_CONFIG="llvm-config-11"
|
||||||
# freebsd
|
# freebsd
|
||||||
elif [ -n "$(command -v llvm-config17)" ]; then LLVM_CONFIG="llvm-config-17"
|
elif [ -n "$(command -v llvm-config19)" ]; then LLVM_CONFIG="llvm-config19"
|
||||||
elif [ -n "$(command -v llvm-config13)" ]; then LLVM_CONFIG="llvm-config-13"
|
elif [ -n "$(command -v llvm-config18)" ]; then LLVM_CONFIG="llvm-config18"
|
||||||
elif [ -n "$(command -v llvm-config12)" ]; then LLVM_CONFIG="llvm-config-12"
|
elif [ -n "$(command -v llvm-config17)" ]; then LLVM_CONFIG="llvm-config17"
|
||||||
elif [ -n "$(command -v llvm-config11)" ]; then LLVM_CONFIG="llvm-config-11"
|
elif [ -n "$(command -v llvm-config14)" ]; then LLVM_CONFIG="llvm-config14"
|
||||||
|
elif [ -n "$(command -v llvm-config13)" ]; then LLVM_CONFIG="llvm-config13"
|
||||||
|
elif [ -n "$(command -v llvm-config12)" ]; then LLVM_CONFIG="llvm-config12"
|
||||||
|
elif [ -n "$(command -v llvm-config11)" ]; then LLVM_CONFIG="llvm-config11"
|
||||||
# fallback
|
# fallback
|
||||||
elif [ -n "$(command -v llvm-config)" ]; then LLVM_CONFIG="llvm-config"
|
elif [ -n "$(command -v llvm-config)" ]; then LLVM_CONFIG="llvm-config"
|
||||||
else
|
else
|
||||||
@@ -42,42 +56,66 @@ if [ -z "$LLVM_CONFIG" ]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ -x "$(which clang++)" ]; then
|
||||||
|
: ${CXX="clang++"}
|
||||||
|
elif [ -x "$($LLVM_CONFIG --bindir)/clang++" ]; then
|
||||||
|
: ${CXX=$($LLVM_CONFIG --bindir)/clang++}
|
||||||
|
else
|
||||||
|
error "No clang++ command found. Set CXX to proceed."
|
||||||
|
fi
|
||||||
|
|
||||||
LLVM_VERSION="$($LLVM_CONFIG --version)"
|
LLVM_VERSION="$($LLVM_CONFIG --version)"
|
||||||
LLVM_VERSION_MAJOR="$(echo $LLVM_VERSION | awk -F. '{print $1}')"
|
LLVM_VERSION_MAJOR="$(echo $LLVM_VERSION | awk -F. '{print $1}')"
|
||||||
LLVM_VERSION_MINOR="$(echo $LLVM_VERSION | awk -F. '{print $2}')"
|
LLVM_VERSION_MINOR="$(echo $LLVM_VERSION | awk -F. '{print $2}')"
|
||||||
LLVM_VERSION_PATCH="$(echo $LLVM_VERSION | awk -F. '{print $3}')"
|
LLVM_VERSION_PATCH="$(echo $LLVM_VERSION | awk -F. '{print $3}')"
|
||||||
|
|
||||||
if [ $LLVM_VERSION_MAJOR -lt 11 ] ||
|
if [ $LLVM_VERSION_MAJOR -lt 11 ] || ([ $LLVM_VERSION_MAJOR -gt 14 ] && [ $LLVM_VERSION_MAJOR -lt 17 ]) || [ $LLVM_VERSION_MAJOR -gt 19 ]; then
|
||||||
([ $LLVM_VERSION_MAJOR -gt 14 ] && [ $LLVM_VERSION_MAJOR -lt 17 ]); then
|
error "Invalid LLVM version $LLVM_VERSION: must be 11, 12, 13, 14, 17, 18 or 19"
|
||||||
error "Invalid LLVM version $LLVM_VERSION: must be 11, 12, 13, 14 or 17"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
case "$OS_NAME" in
|
case "$OS_NAME" in
|
||||||
Darwin)
|
Darwin)
|
||||||
if [ "$OS_ARCH" == "arm64" ]; then
|
if [ "$OS_ARCH" = "arm64" ]; then
|
||||||
if [ $LLVM_VERSION_MAJOR -lt 13 ] || [ $LLVM_VERSION_MAJOR -gt 17 ]; then
|
if [ $LLVM_VERSION_MAJOR -lt 13 ]; then
|
||||||
error "Darwin Arm64 requires LLVM 13, 14 or 17"
|
error "Invalid LLVM version $LLVM_VERSION: Darwin Arm64 requires LLVM 13, 14, 17, 18 or 19"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
darwin_sysroot=
|
||||||
LDFLAGS="$LDFLAGS -liconv -ldl -framework System"
|
if [ $(which xcrun) ]; then
|
||||||
LDFLAGS="$LDFLAGS -lLLVM-C"
|
darwin_sysroot="--sysroot $(xcrun --sdk macosx --show-sdk-path)"
|
||||||
|
elif [[ -e "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk" ]]; then
|
||||||
|
darwin_sysroot="--sysroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk"
|
||||||
|
else
|
||||||
|
echo "Warning: MacOSX.sdk not found."
|
||||||
|
fi
|
||||||
|
|
||||||
|
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags) ${darwin_sysroot}"
|
||||||
|
LDFLAGS="$LDFLAGS -liconv -ldl -framework System -lLLVM"
|
||||||
;;
|
;;
|
||||||
FreeBSD)
|
FreeBSD)
|
||||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||||
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
||||||
;;
|
;;
|
||||||
|
NetBSD)
|
||||||
|
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||||
|
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
||||||
|
;;
|
||||||
Linux)
|
Linux)
|
||||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||||
LDFLAGS="$LDFLAGS -ldl $($LLVM_CONFIG --libs core native --system-libs --libfiles)"
|
LDFLAGS="$LDFLAGS -ldl $($LLVM_CONFIG --libs core native --system-libs --libfiles)"
|
||||||
# Copy libLLVM*.so into current directory for linking
|
# Copy libLLVM*.so into current directory for linking
|
||||||
# NOTE: This is needed by the Linux release pipeline!
|
# NOTE: This is needed by the Linux release pipeline!
|
||||||
cp $(readlink -f $($LLVM_CONFIG --libfiles)) ./
|
# cp $(readlink -f $($LLVM_CONFIG --libfiles)) ./
|
||||||
LDFLAGS="$LDFLAGS -Wl,-rpath=\$ORIGIN"
|
LDFLAGS="$LDFLAGS -Wl,-rpath=\$ORIGIN"
|
||||||
;;
|
;;
|
||||||
OpenBSD)
|
OpenBSD)
|
||||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
CXXFLAGS="$CXXFLAGS -I/usr/local/include $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||||
|
LDFLAGS="$LDFLAGS -L/usr/local/lib -liconv"
|
||||||
|
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
||||||
|
;;
|
||||||
|
Haiku)
|
||||||
|
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags) -I/system/develop/headers/private/shared -I/system/develop/headers/private/kernel"
|
||||||
LDFLAGS="$LDFLAGS -liconv"
|
LDFLAGS="$LDFLAGS -liconv"
|
||||||
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
||||||
;;
|
;;
|
||||||
@@ -95,7 +133,7 @@ build_odin() {
|
|||||||
EXTRAFLAGS="-O3"
|
EXTRAFLAGS="-O3"
|
||||||
;;
|
;;
|
||||||
release-native)
|
release-native)
|
||||||
if [ "$OS_ARCH" == "arm64" ]; then
|
if [ "$OS_ARCH" = "arm64" ] || [ "$OS_ARCH" = "aarch64" ]; then
|
||||||
# Use preferred flag for Arm (ie arm64 / aarch64 / etc)
|
# Use preferred flag for Arm (ie arm64 / aarch64 / etc)
|
||||||
EXTRAFLAGS="-O3 -mcpu=native"
|
EXTRAFLAGS="-O3 -mcpu=native"
|
||||||
else
|
else
|
||||||
@@ -117,23 +155,32 @@ build_odin() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
run_demo() {
|
run_demo() {
|
||||||
./odin run examples/demo/demo.odin -file
|
./odin run examples/demo -vet -strict-style -- Hellope World
|
||||||
}
|
}
|
||||||
|
|
||||||
if [ $# -eq 0 ]; then
|
if [ $# -eq 0 ]; then
|
||||||
build_odin debug
|
build_odin debug
|
||||||
run_demo
|
run_demo
|
||||||
|
|
||||||
|
: ${PROGRAM:=$0}
|
||||||
|
printf "\nDebug compiler built. Note: run \"$PROGRAM release\" or \"$PROGRAM release-native\" if you want a faster, release mode compiler.\n"
|
||||||
elif [ $# -eq 1 ]; then
|
elif [ $# -eq 1 ]; then
|
||||||
case $1 in
|
case $1 in
|
||||||
report)
|
report)
|
||||||
[ ! -f "./odin" ] && build_odin debug
|
if [ ! -f "./odin" ]; then
|
||||||
|
build_odin debug
|
||||||
|
run_demo
|
||||||
|
fi
|
||||||
./odin report
|
./odin report
|
||||||
;;
|
;;
|
||||||
|
debug)
|
||||||
|
build_odin debug
|
||||||
|
run_demo
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
build_odin $1
|
build_odin $1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
run_demo
|
|
||||||
else
|
else
|
||||||
error "Too many arguments!"
|
error "Too many arguments!"
|
||||||
fi
|
fi
|
||||||
|
|||||||
Executable
+19
@@ -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
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import json
|
|
||||||
import datetime
|
|
||||||
import urllib.parse
|
|
||||||
import sys
|
|
||||||
|
|
||||||
def main():
|
|
||||||
files_by_date = {}
|
|
||||||
bucket = sys.argv[1]
|
|
||||||
|
|
||||||
files_lines = execute_cli(f"b2 ls --long {bucket} nightly").split("\n")
|
|
||||||
for x in files_lines:
|
|
||||||
parts = x.split(" ", 1)
|
|
||||||
if parts[0]:
|
|
||||||
json_str = execute_cli(f"b2 get-file-info {parts[0]}")
|
|
||||||
data = json.loads(json_str)
|
|
||||||
name = remove_prefix(data['fileName'], "nightly/")
|
|
||||||
url = f"https://f001.backblazeb2.com/file/{bucket}/nightly/{urllib.parse.quote_plus(name)}"
|
|
||||||
sha1 = data['contentSha1']
|
|
||||||
size = int(data['size'])
|
|
||||||
ts = int(data['fileInfo']['src_last_modified_millis'])
|
|
||||||
date = datetime.datetime.fromtimestamp(ts/1000).strftime('%Y-%m-%d')
|
|
||||||
|
|
||||||
if date not in files_by_date.keys():
|
|
||||||
files_by_date[date] = []
|
|
||||||
|
|
||||||
files_by_date[date].append({
|
|
||||||
'name': name,
|
|
||||||
'url': url,
|
|
||||||
'sha1': sha1,
|
|
||||||
'sizeInBytes': size,
|
|
||||||
})
|
|
||||||
|
|
||||||
now = datetime.datetime.utcnow().isoformat()
|
|
||||||
|
|
||||||
print(json.dumps({
|
|
||||||
'last_updated' : now,
|
|
||||||
'files': files_by_date
|
|
||||||
}, sort_keys=True, indent=4))
|
|
||||||
|
|
||||||
def remove_prefix(text, prefix):
|
|
||||||
return text[text.startswith(prefix) and len(prefix):]
|
|
||||||
|
|
||||||
def execute_cli(command):
|
|
||||||
sb = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
|
|
||||||
return sb.stdout.read().decode("utf-8");
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
sys.exit(main())
|
|
||||||
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import json
|
|
||||||
import datetime
|
|
||||||
import urllib.parse
|
|
||||||
import sys
|
|
||||||
|
|
||||||
def main():
|
|
||||||
files_by_date = {}
|
|
||||||
bucket = sys.argv[1]
|
|
||||||
days_to_keep = int(sys.argv[2])
|
|
||||||
print(f"Looking for binaries to delete older than {days_to_keep} days")
|
|
||||||
|
|
||||||
files_lines = execute_cli(f"b2 ls --long --versions {bucket} nightly").split("\n")
|
|
||||||
for x in files_lines:
|
|
||||||
parts = [y for y in x.split(' ') if y]
|
|
||||||
|
|
||||||
if parts and parts[0]:
|
|
||||||
date = datetime.datetime.strptime(parts[2], '%Y-%m-%d').replace(hour=0, minute=0, second=0, microsecond=0)
|
|
||||||
now = datetime.datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)
|
|
||||||
delta = now - date
|
|
||||||
|
|
||||||
if delta.days > days_to_keep:
|
|
||||||
print(f'Deleting {parts[5]}')
|
|
||||||
execute_cli(f'b2 delete-file-version {parts[0]}')
|
|
||||||
|
|
||||||
|
|
||||||
def execute_cli(command):
|
|
||||||
sb = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
|
|
||||||
return sb.stdout.read().decode("utf-8");
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
sys.exit(main())
|
|
||||||
|
|
||||||
+145
@@ -0,0 +1,145 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from zipfile import ZipFile, ZIP_DEFLATED
|
||||||
|
from b2sdk.v2 import InMemoryAccountInfo, B2Api
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
import json
|
||||||
|
|
||||||
|
UPLOAD_FOLDER = "nightly/"
|
||||||
|
|
||||||
|
info = InMemoryAccountInfo()
|
||||||
|
b2_api = B2Api(info)
|
||||||
|
application_key_id = os.environ['APPID']
|
||||||
|
application_key = os.environ['APPKEY']
|
||||||
|
bucket_name = os.environ['BUCKET']
|
||||||
|
days_to_keep = os.environ['DAYS_TO_KEEP']
|
||||||
|
|
||||||
|
def auth() -> bool:
|
||||||
|
try:
|
||||||
|
realm = b2_api.account_info.get_realm()
|
||||||
|
return True # Already authenticated
|
||||||
|
except:
|
||||||
|
pass # Not yet authenticated
|
||||||
|
|
||||||
|
err = b2_api.authorize_account("production", application_key_id, application_key)
|
||||||
|
return err is None
|
||||||
|
|
||||||
|
def get_bucket():
|
||||||
|
if not auth(): sys.exit(1)
|
||||||
|
return b2_api.get_bucket_by_name(bucket_name)
|
||||||
|
|
||||||
|
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.now(timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0)
|
||||||
|
|
||||||
|
source_archive: str
|
||||||
|
destination_name = f'odin-{platform}-nightly+{now.strftime("%Y-%m-%d")}'
|
||||||
|
|
||||||
|
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_archive):
|
||||||
|
print(f"Error: archive {source_archive} not found.")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
print("Uploading {} to {}".format(source_archive, UPLOAD_FOLDER + destination_name))
|
||||||
|
bucket = get_bucket()
|
||||||
|
res = bucket.upload_local_file(
|
||||||
|
source_archive, # Local file to upload
|
||||||
|
"nightly/" + destination_name, # B2 destination path
|
||||||
|
)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def prune_artifacts():
|
||||||
|
print(f"Looking for binaries to delete older than {days_to_keep} days")
|
||||||
|
|
||||||
|
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, 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):
|
||||||
|
print("Deleting {}".format(file.file_name))
|
||||||
|
file.delete()
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def update_nightly_json():
|
||||||
|
print(f"Updating nightly.json with files {days_to_keep} days or newer")
|
||||||
|
|
||||||
|
files_by_date = {}
|
||||||
|
|
||||||
|
bucket = get_bucket()
|
||||||
|
|
||||||
|
for file, _ in bucket.ls(UPLOAD_FOLDER, latest_only=True):
|
||||||
|
# Timestamp is in milliseconds
|
||||||
|
date = datetime.fromtimestamp(file.upload_timestamp / 1_000.0).replace(hour=0, minute=0, second=0, microsecond=0).strftime('%Y-%m-%d')
|
||||||
|
name = remove_prefix(file.file_name, UPLOAD_FOLDER)
|
||||||
|
sha1 = file.content_sha1
|
||||||
|
size = file.size
|
||||||
|
url = bucket.get_download_url(file.file_name)
|
||||||
|
|
||||||
|
if date not in files_by_date.keys():
|
||||||
|
files_by_date[date] = []
|
||||||
|
|
||||||
|
files_by_date[date].append({
|
||||||
|
'name': name,
|
||||||
|
'url': url,
|
||||||
|
'sha1': sha1,
|
||||||
|
'sizeInBytes': size,
|
||||||
|
})
|
||||||
|
|
||||||
|
now = datetime.now(timezone.utc).isoformat()
|
||||||
|
|
||||||
|
nightly = json.dumps({
|
||||||
|
'last_updated' : now,
|
||||||
|
'files': files_by_date
|
||||||
|
}, sort_keys=True, indent=4, ensure_ascii=False).encode('utf-8')
|
||||||
|
|
||||||
|
res = bucket.upload_bytes(
|
||||||
|
nightly, # JSON bytes
|
||||||
|
"nightly.json", # B2 destination path
|
||||||
|
)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if len(sys.argv) == 1:
|
||||||
|
print("Usage: {} <verb> [arguments]".format(sys.argv[0]))
|
||||||
|
print("\tartifact <platform prefix> <artifact path>\n\t\tCreates and uploads a platform artifact zip.")
|
||||||
|
print("\tprune\n\t\tDeletes old artifacts from bucket")
|
||||||
|
print("\tjson\n\t\tUpdate and upload nightly.json")
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
command = sys.argv[1].lower()
|
||||||
|
if command == "artifact":
|
||||||
|
if len(sys.argv) != 4:
|
||||||
|
print("Usage: {} artifact <platform prefix> <artifact path>".format(sys.argv[0]))
|
||||||
|
print("Error: Expected artifact command to be given platform prefix and artifact path.\n")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
res = create_and_upload_artifact_zip(sys.argv[2], sys.argv[3])
|
||||||
|
sys.exit(res)
|
||||||
|
|
||||||
|
elif command == "prune":
|
||||||
|
res = prune_artifacts()
|
||||||
|
sys.exit(res)
|
||||||
|
|
||||||
|
elif command == "json":
|
||||||
|
res = update_nightly_json()
|
||||||
|
sys.exit(res)
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
bucket=$1
|
|
||||||
platform=$2
|
|
||||||
artifact=$3
|
|
||||||
|
|
||||||
now=$(date +'%Y-%m-%d')
|
|
||||||
filename="odin-$platform-nightly+$now.zip"
|
|
||||||
|
|
||||||
echo "Creating archive $filename from $artifact and uploading to $bucket"
|
|
||||||
|
|
||||||
7z a -bd "output/$filename" -r "$artifact"
|
|
||||||
b2 upload-file --noProgress "$bucket" "output/$filename" "nightly/$filename"
|
|
||||||
@@ -29,12 +29,12 @@ MIN_READ_BUFFER_SIZE :: 16
|
|||||||
@(private)
|
@(private)
|
||||||
DEFAULT_MAX_CONSECUTIVE_EMPTY_READS :: 128
|
DEFAULT_MAX_CONSECUTIVE_EMPTY_READS :: 128
|
||||||
|
|
||||||
reader_init :: proc(b: ^Reader, rd: io.Reader, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator) {
|
reader_init :: proc(b: ^Reader, rd: io.Reader, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator, loc := #caller_location) {
|
||||||
size := size
|
size := size
|
||||||
size = max(size, MIN_READ_BUFFER_SIZE)
|
size = max(size, MIN_READ_BUFFER_SIZE)
|
||||||
reader_reset(b, rd)
|
reader_reset(b, rd)
|
||||||
b.buf_allocator = allocator
|
b.buf_allocator = allocator
|
||||||
b.buf = make([]byte, size, allocator)
|
b.buf = make([]byte, size, allocator, loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
reader_init_with_buf :: proc(b: ^Reader, rd: io.Reader, buf: []byte) {
|
reader_init_with_buf :: proc(b: ^Reader, rd: io.Reader, buf: []byte) {
|
||||||
@@ -81,7 +81,7 @@ _reader_read_new_chunk :: proc(b: ^Reader) -> io.Error {
|
|||||||
for i := b.max_consecutive_empty_reads; i > 0; i -= 1 {
|
for i := b.max_consecutive_empty_reads; i > 0; i -= 1 {
|
||||||
n, err := io.read(b.rd, b.buf[b.w:])
|
n, err := io.read(b.rd, b.buf[b.w:])
|
||||||
if n < 0 {
|
if n < 0 {
|
||||||
return .Negative_Read
|
return err if err != nil else .Negative_Read
|
||||||
}
|
}
|
||||||
b.w += n
|
b.w += n
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -189,7 +189,7 @@ reader_read :: proc(b: ^Reader, p: []byte) -> (n: int, err: io.Error) {
|
|||||||
if len(p) >= len(b.buf) {
|
if len(p) >= len(b.buf) {
|
||||||
n, b.err = io.read(b.rd, p)
|
n, b.err = io.read(b.rd, p)
|
||||||
if n < 0 {
|
if n < 0 {
|
||||||
return 0, .Negative_Read
|
return 0, b.err if b.err != nil else .Negative_Read
|
||||||
}
|
}
|
||||||
|
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
@@ -202,7 +202,7 @@ reader_read :: proc(b: ^Reader, p: []byte) -> (n: int, err: io.Error) {
|
|||||||
b.r, b.w = 0, 0
|
b.r, b.w = 0, 0
|
||||||
n, b.err = io.read(b.rd, b.buf)
|
n, b.err = io.read(b.rd, b.buf)
|
||||||
if n < 0 {
|
if n < 0 {
|
||||||
return 0, .Negative_Read
|
return 0, b.err if b.err != nil else .Negative_Read
|
||||||
}
|
}
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
return 0, _reader_consume_err(b)
|
return 0, _reader_consume_err(b)
|
||||||
@@ -290,7 +290,7 @@ reader_write_to :: proc(b: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
|
|||||||
write_buf :: proc(b: ^Reader, w: io.Writer) -> (i64, io.Error) {
|
write_buf :: proc(b: ^Reader, w: io.Writer) -> (i64, io.Error) {
|
||||||
n, err := io.write(w, b.buf[b.r:b.w])
|
n, err := io.write(w, b.buf[b.r:b.w])
|
||||||
if n < 0 {
|
if n < 0 {
|
||||||
return 0, .Negative_Write
|
return 0, err if err != nil else .Negative_Write
|
||||||
}
|
}
|
||||||
b.r += n
|
b.r += n
|
||||||
return i64(n), err
|
return i64(n), err
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import "core:bytes"
|
|||||||
import "core:io"
|
import "core:io"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:unicode/utf8"
|
import "core:unicode/utf8"
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
// Extra errors returns by scanning procedures
|
// Extra errors returns by scanning procedures
|
||||||
Scanner_Extra_Error :: enum i32 {
|
Scanner_Extra_Error :: enum i32 {
|
||||||
|
|||||||
@@ -95,6 +95,10 @@ writer_write :: proc(b: ^Writer, p: []byte) -> (n: int, err: io.Error) {
|
|||||||
m: int
|
m: int
|
||||||
if writer_buffered(b) == 0 {
|
if writer_buffered(b) == 0 {
|
||||||
m, b.err = io.write(b.wr, p)
|
m, b.err = io.write(b.wr, p)
|
||||||
|
if m < 0 && b.err == nil {
|
||||||
|
b.err = .Negative_Write
|
||||||
|
break
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
m = copy(b.buf[b.n:], p)
|
m = copy(b.buf[b.n:], p)
|
||||||
b.n += m
|
b.n += m
|
||||||
@@ -226,7 +230,6 @@ writer_to_writer :: proc(b: ^Writer) -> (s: io.Writer) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
@(private)
|
|
||||||
_writer_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
|
_writer_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
|
||||||
b := (^Writer)(stream_data)
|
b := (^Writer)(stream_data)
|
||||||
#partial switch mode {
|
#partial switch mode {
|
||||||
|
|||||||
+69
-39
@@ -27,19 +27,19 @@ Read_Op :: enum i8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
buffer_init :: proc(b: ^Buffer, buf: []byte) {
|
buffer_init :: proc(b: ^Buffer, buf: []byte, loc := #caller_location) {
|
||||||
resize(&b.buf, len(buf))
|
resize(&b.buf, len(buf), loc=loc)
|
||||||
copy(b.buf[:], buf)
|
copy(b.buf[:], buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer_init_string :: proc(b: ^Buffer, s: string) {
|
buffer_init_string :: proc(b: ^Buffer, s: string, loc := #caller_location) {
|
||||||
resize(&b.buf, len(s))
|
resize(&b.buf, len(s), loc=loc)
|
||||||
copy(b.buf[:], s)
|
copy(b.buf[:], s)
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer_init_allocator :: proc(b: ^Buffer, len, cap: int, allocator := context.allocator) {
|
buffer_init_allocator :: proc(b: ^Buffer, len, cap: int, allocator := context.allocator, loc := #caller_location) {
|
||||||
if b.buf == nil {
|
if b.buf == nil {
|
||||||
b.buf = make([dynamic]byte, len, cap, allocator)
|
b.buf = make([dynamic]byte, len, cap, allocator, loc)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,28 +96,28 @@ buffer_truncate :: proc(b: ^Buffer, n: int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
_buffer_try_grow :: proc(b: ^Buffer, n: int) -> (int, bool) {
|
_buffer_try_grow :: proc(b: ^Buffer, n: int, loc := #caller_location) -> (int, bool) {
|
||||||
if l := len(b.buf); n <= cap(b.buf)-l {
|
if l := len(b.buf); n <= cap(b.buf)-l {
|
||||||
resize(&b.buf, l+n)
|
resize(&b.buf, l+n, loc=loc)
|
||||||
return l, true
|
return l, true
|
||||||
}
|
}
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
_buffer_grow :: proc(b: ^Buffer, n: int) -> int {
|
_buffer_grow :: proc(b: ^Buffer, n: int, loc := #caller_location) -> int {
|
||||||
m := buffer_length(b)
|
m := buffer_length(b)
|
||||||
if m == 0 && b.off != 0 {
|
if m == 0 && b.off != 0 {
|
||||||
buffer_reset(b)
|
buffer_reset(b)
|
||||||
}
|
}
|
||||||
if i, ok := _buffer_try_grow(b, n); ok {
|
if i, ok := _buffer_try_grow(b, n, loc=loc); ok {
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.buf == nil && n <= SMALL_BUFFER_SIZE {
|
if b.buf == nil && n <= SMALL_BUFFER_SIZE {
|
||||||
// Fixes #2756 by preserving allocator if already set on Buffer via init_buffer_allocator
|
// Fixes #2756 by preserving allocator if already set on Buffer via init_buffer_allocator
|
||||||
reserve(&b.buf, SMALL_BUFFER_SIZE)
|
reserve(&b.buf, SMALL_BUFFER_SIZE, loc=loc)
|
||||||
resize(&b.buf, n)
|
resize(&b.buf, n, loc=loc)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,31 +127,34 @@ _buffer_grow :: proc(b: ^Buffer, n: int) -> int {
|
|||||||
} else if c > max(int) - c - n {
|
} else if c > max(int) - c - n {
|
||||||
panic("bytes.Buffer: too large")
|
panic("bytes.Buffer: too large")
|
||||||
} else {
|
} else {
|
||||||
resize(&b.buf, 2*c + n)
|
resize(&b.buf, 2*c + n, loc=loc)
|
||||||
copy(b.buf[:], b.buf[b.off:])
|
copy(b.buf[:], b.buf[b.off:])
|
||||||
}
|
}
|
||||||
b.off = 0
|
b.off = 0
|
||||||
resize(&b.buf, m+n)
|
resize(&b.buf, m+n, loc=loc)
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer_grow :: proc(b: ^Buffer, n: int) {
|
buffer_grow :: proc(b: ^Buffer, n: int, loc := #caller_location) {
|
||||||
if n < 0 {
|
if n < 0 {
|
||||||
panic("bytes.buffer_grow: negative count")
|
panic("bytes.buffer_grow: negative count")
|
||||||
}
|
}
|
||||||
m := _buffer_grow(b, n)
|
m := _buffer_grow(b, n, loc=loc)
|
||||||
resize(&b.buf, m)
|
resize(&b.buf, m, loc=loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer_write_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) {
|
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
|
b.last_read = .Invalid
|
||||||
if offset < 0 {
|
if offset < 0 {
|
||||||
err = .Invalid_Offset
|
err = .Invalid_Offset
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, ok := _buffer_try_grow(b, offset+len(p))
|
_, ok := _buffer_try_grow(b, offset+len(p), loc=loc)
|
||||||
if !ok {
|
if !ok {
|
||||||
_ = _buffer_grow(b, offset+len(p))
|
_ = _buffer_grow(b, offset+len(p), loc=loc)
|
||||||
}
|
}
|
||||||
if len(b.buf) <= offset {
|
if len(b.buf) <= offset {
|
||||||
return 0, .Short_Write
|
return 0, .Short_Write
|
||||||
@@ -160,47 +163,47 @@ buffer_write_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
buffer_write :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
|
buffer_write :: proc(b: ^Buffer, p: []byte, loc := #caller_location) -> (n: int, err: io.Error) {
|
||||||
b.last_read = .Invalid
|
b.last_read = .Invalid
|
||||||
m, ok := _buffer_try_grow(b, len(p))
|
m, ok := _buffer_try_grow(b, len(p), loc=loc)
|
||||||
if !ok {
|
if !ok {
|
||||||
m = _buffer_grow(b, len(p))
|
m = _buffer_grow(b, len(p), loc=loc)
|
||||||
}
|
}
|
||||||
return copy(b.buf[m:], p), nil
|
return copy(b.buf[m:], p), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer_write_ptr :: proc(b: ^Buffer, ptr: rawptr, size: int) -> (n: int, err: io.Error) {
|
buffer_write_ptr :: proc(b: ^Buffer, ptr: rawptr, size: int, loc := #caller_location) -> (n: int, err: io.Error) {
|
||||||
return buffer_write(b, ([^]byte)(ptr)[:size])
|
return buffer_write(b, ([^]byte)(ptr)[:size], loc=loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer_write_string :: proc(b: ^Buffer, s: string) -> (n: int, err: io.Error) {
|
buffer_write_string :: proc(b: ^Buffer, s: string, loc := #caller_location) -> (n: int, err: io.Error) {
|
||||||
b.last_read = .Invalid
|
b.last_read = .Invalid
|
||||||
m, ok := _buffer_try_grow(b, len(s))
|
m, ok := _buffer_try_grow(b, len(s), loc=loc)
|
||||||
if !ok {
|
if !ok {
|
||||||
m = _buffer_grow(b, len(s))
|
m = _buffer_grow(b, len(s), loc=loc)
|
||||||
}
|
}
|
||||||
return copy(b.buf[m:], s), nil
|
return copy(b.buf[m:], s), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer_write_byte :: proc(b: ^Buffer, c: byte) -> io.Error {
|
buffer_write_byte :: proc(b: ^Buffer, c: byte, loc := #caller_location) -> io.Error {
|
||||||
b.last_read = .Invalid
|
b.last_read = .Invalid
|
||||||
m, ok := _buffer_try_grow(b, 1)
|
m, ok := _buffer_try_grow(b, 1, loc=loc)
|
||||||
if !ok {
|
if !ok {
|
||||||
m = _buffer_grow(b, 1)
|
m = _buffer_grow(b, 1, loc=loc)
|
||||||
}
|
}
|
||||||
b.buf[m] = c
|
b.buf[m] = c
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer_write_rune :: proc(b: ^Buffer, r: rune) -> (n: int, err: io.Error) {
|
buffer_write_rune :: proc(b: ^Buffer, r: rune, loc := #caller_location) -> (n: int, err: io.Error) {
|
||||||
if r < utf8.RUNE_SELF {
|
if r < utf8.RUNE_SELF {
|
||||||
buffer_write_byte(b, byte(r))
|
buffer_write_byte(b, byte(r), loc=loc)
|
||||||
return 1, nil
|
return 1, nil
|
||||||
}
|
}
|
||||||
b.last_read = .Invalid
|
b.last_read = .Invalid
|
||||||
m, ok := _buffer_try_grow(b, utf8.UTF_MAX)
|
m, ok := _buffer_try_grow(b, utf8.UTF_MAX, loc=loc)
|
||||||
if !ok {
|
if !ok {
|
||||||
m = _buffer_grow(b, utf8.UTF_MAX)
|
m = _buffer_grow(b, utf8.UTF_MAX, loc=loc)
|
||||||
}
|
}
|
||||||
res: [4]byte
|
res: [4]byte
|
||||||
res, n = utf8.encode_rune(r)
|
res, n = utf8.encode_rune(r)
|
||||||
@@ -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) {
|
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
|
b.last_read = .Invalid
|
||||||
|
|
||||||
if uint(offset) >= len(b.buf) {
|
if uint(offset) >= len(b.buf) {
|
||||||
err = .Invalid_Offset
|
err = .EOF
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
n = copy(p, b.buf[offset:])
|
n = copy(p, b.buf[offset:])
|
||||||
@@ -310,6 +316,27 @@ buffer_unread_rune :: proc(b: ^Buffer) -> io.Error {
|
|||||||
return nil
|
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) {
|
buffer_read_bytes :: proc(b: ^Buffer, delim: byte) -> (line: []byte, err: io.Error) {
|
||||||
i := index_byte(b.buf[b.off:], delim)
|
i := index_byte(b.buf[b.off:], delim)
|
||||||
@@ -359,7 +386,7 @@ buffer_read_from :: proc(b: ^Buffer, r: io.Reader) -> (n: i64, err: io.Error) #n
|
|||||||
resize(&b.buf, i)
|
resize(&b.buf, i)
|
||||||
m, e := io.read(r, b.buf[i:cap(b.buf)])
|
m, e := io.read(r, b.buf[i:cap(b.buf)])
|
||||||
if m < 0 {
|
if m < 0 {
|
||||||
err = .Negative_Read
|
err = e if e != nil else .Negative_Read
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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))
|
return io._i64_err(buffer_write(b, p))
|
||||||
case .Write_At:
|
case .Write_At:
|
||||||
return io._i64_err(buffer_write_at(b, p, int(offset)))
|
return io._i64_err(buffer_write_at(b, p, int(offset)))
|
||||||
|
case .Seek:
|
||||||
|
n, err = buffer_seek(b, offset, whence)
|
||||||
|
return
|
||||||
case .Size:
|
case .Size:
|
||||||
n = i64(buffer_capacity(b))
|
n = i64(buffer_length(b))
|
||||||
return
|
return
|
||||||
case .Destroy:
|
case .Destroy:
|
||||||
buffer_destroy(b)
|
buffer_destroy(b)
|
||||||
return
|
return
|
||||||
case .Query:
|
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
|
return 0, .Empty
|
||||||
}
|
}
|
||||||
|
|||||||
+310
-7
@@ -1,9 +1,38 @@
|
|||||||
package bytes
|
package bytes
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
import "core:simd"
|
||||||
import "core:unicode"
|
import "core:unicode"
|
||||||
import "core:unicode/utf8"
|
import "core:unicode/utf8"
|
||||||
|
|
||||||
|
when ODIN_ARCH == .amd64 && intrinsics.has_target_feature("avx2") {
|
||||||
|
@(private)
|
||||||
|
SCANNER_INDICES_256 : simd.u8x32 : {
|
||||||
|
0, 1, 2, 3, 4, 5, 6, 7,
|
||||||
|
8, 9, 10, 11, 12, 13, 14, 15,
|
||||||
|
16, 17, 18, 19, 20, 21, 22, 23,
|
||||||
|
24, 25, 26, 27, 28, 29, 30, 31,
|
||||||
|
}
|
||||||
|
@(private)
|
||||||
|
SCANNER_SENTINEL_MAX_256: simd.u8x32 : u8(0x00)
|
||||||
|
@(private)
|
||||||
|
SCANNER_SENTINEL_MIN_256: simd.u8x32 : u8(0xff)
|
||||||
|
@(private)
|
||||||
|
SIMD_REG_SIZE_256 :: 32
|
||||||
|
}
|
||||||
|
@(private)
|
||||||
|
SCANNER_INDICES_128 : simd.u8x16 : {
|
||||||
|
0, 1, 2, 3, 4, 5, 6, 7,
|
||||||
|
8, 9, 10, 11, 12, 13, 14, 15,
|
||||||
|
}
|
||||||
|
@(private)
|
||||||
|
SCANNER_SENTINEL_MAX_128: simd.u8x16 : u8(0x00)
|
||||||
|
@(private)
|
||||||
|
SCANNER_SENTINEL_MIN_128: simd.u8x16 : u8(0xff)
|
||||||
|
@(private)
|
||||||
|
SIMD_REG_SIZE_128 :: 16
|
||||||
|
|
||||||
clone :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> []byte {
|
clone :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> []byte {
|
||||||
c := make([]byte, len(s), allocator, loc)
|
c := make([]byte, len(s), allocator, loc)
|
||||||
copy(c, s)
|
copy(c, s)
|
||||||
@@ -293,28 +322,277 @@ split_after_iterator :: proc(s: ^[]byte, sep: []byte) -> ([]byte, bool) {
|
|||||||
return _split_iterator(s, sep, len(sep))
|
return _split_iterator(s, sep, len(sep))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Scan a slice of bytes for a specific byte.
|
||||||
|
|
||||||
index_byte :: proc(s: []byte, c: byte) -> int {
|
This procedure safely handles slices of any length, including empty slices.
|
||||||
for i := 0; i < len(s); i += 1 {
|
|
||||||
|
Inputs:
|
||||||
|
- data: A slice of bytes.
|
||||||
|
- c: The byte to search for.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- index: The index of the byte `c`, or -1 if it was not found.
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
// worth vectorizing assuming there is a hardware vector unit, and
|
||||||
|
// the data size is large enough.
|
||||||
|
if l < SIMD_REG_SIZE_128 {
|
||||||
|
for /**/; i < l; i += 1 {
|
||||||
|
if s[i] == c {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
c_vec: simd.u8x16 = c
|
||||||
|
when !simd.IS_EMULATED {
|
||||||
|
// Note: While this is something that could also logically take
|
||||||
|
// advantage of AVX512, the various downclocking and power
|
||||||
|
// consumption related woes make premature to have a dedicated
|
||||||
|
// code path.
|
||||||
|
when ODIN_ARCH == .amd64 && intrinsics.has_target_feature("avx2") {
|
||||||
|
c_vec_256: simd.u8x32 = c
|
||||||
|
|
||||||
|
s_vecs: [4]simd.u8x32 = ---
|
||||||
|
c_vecs: [4]simd.u8x32 = ---
|
||||||
|
m_vec: [4]u8 = ---
|
||||||
|
|
||||||
|
// Scan 128-byte chunks, using 256-bit SIMD.
|
||||||
|
for nr_blocks := l / (4 * SIMD_REG_SIZE_256); nr_blocks > 0; nr_blocks -= 1 {
|
||||||
|
#unroll for j in 0..<4 {
|
||||||
|
s_vecs[j] = intrinsics.unaligned_load(cast(^simd.u8x32)raw_data(s[i+j*SIMD_REG_SIZE_256:]))
|
||||||
|
c_vecs[j] = simd.lanes_eq(s_vecs[j], c_vec_256)
|
||||||
|
m_vec[j] = simd.reduce_or(c_vecs[j])
|
||||||
|
}
|
||||||
|
if m_vec[0] | m_vec[1] | m_vec[2] | m_vec[3] > 0 {
|
||||||
|
#unroll for j in 0..<4 {
|
||||||
|
if m_vec[j] > 0 {
|
||||||
|
sel := simd.select(c_vecs[j], SCANNER_INDICES_256, SCANNER_SENTINEL_MIN_256)
|
||||||
|
off := simd.reduce_min(sel)
|
||||||
|
return i + j * SIMD_REG_SIZE_256 + int(off)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 4 * SIMD_REG_SIZE_256
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan 64-byte chunks, using 256-bit SIMD.
|
||||||
|
for nr_blocks := (l - i) / (2 * SIMD_REG_SIZE_256); nr_blocks > 0; nr_blocks -= 1 {
|
||||||
|
#unroll for j in 0..<2 {
|
||||||
|
s_vecs[j] = intrinsics.unaligned_load(cast(^simd.u8x32)raw_data(s[i+j*SIMD_REG_SIZE_256:]))
|
||||||
|
c_vecs[j] = simd.lanes_eq(s_vecs[j], c_vec_256)
|
||||||
|
m_vec[j] = simd.reduce_or(c_vecs[j])
|
||||||
|
}
|
||||||
|
if m_vec[0] | m_vec[1] > 0 {
|
||||||
|
#unroll for j in 0..<2 {
|
||||||
|
if m_vec[j] > 0 {
|
||||||
|
sel := simd.select(c_vecs[j], SCANNER_INDICES_256, SCANNER_SENTINEL_MIN_256)
|
||||||
|
off := simd.reduce_min(sel)
|
||||||
|
return i + j * SIMD_REG_SIZE_256 + int(off)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 2 * SIMD_REG_SIZE_256
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s_vecs: [4]simd.u8x16 = ---
|
||||||
|
c_vecs: [4]simd.u8x16 = ---
|
||||||
|
m_vecs: [4]u8 = ---
|
||||||
|
|
||||||
|
// Scan 64-byte chunks, using 128-bit SIMD.
|
||||||
|
for nr_blocks := l / (4 * SIMD_REG_SIZE_128); nr_blocks > 0; nr_blocks -= 1 {
|
||||||
|
#unroll for j in 0..<4 {
|
||||||
|
s_vecs[j]= intrinsics.unaligned_load(cast(^simd.u8x16)raw_data(s[i+j*SIMD_REG_SIZE_128:]))
|
||||||
|
c_vecs[j] = simd.lanes_eq(s_vecs[j], c_vec)
|
||||||
|
m_vecs[j] = simd.reduce_or(c_vecs[j])
|
||||||
|
}
|
||||||
|
if m_vecs[0] | m_vecs[1] | m_vecs[2] | m_vecs[3] > 0 {
|
||||||
|
#unroll for j in 0..<4 {
|
||||||
|
if m_vecs[j] > 0 {
|
||||||
|
sel := simd.select(c_vecs[j], SCANNER_INDICES_128, SCANNER_SENTINEL_MIN_128)
|
||||||
|
off := simd.reduce_min(sel)
|
||||||
|
return i + j * SIMD_REG_SIZE_128 + int(off)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 4 * SIMD_REG_SIZE_128
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan the remaining SIMD register sized chunks.
|
||||||
|
//
|
||||||
|
// Apparently LLVM does ok with 128-bit SWAR, so this path is also taken
|
||||||
|
// on potato targets. Scanning more at a time when LLVM is emulating SIMD
|
||||||
|
// likely does not buy much, as all that does is increase GP register
|
||||||
|
// pressure.
|
||||||
|
for nr_blocks := (l - i) / SIMD_REG_SIZE_128; nr_blocks > 0; nr_blocks -= 1 {
|
||||||
|
s0 := intrinsics.unaligned_load(cast(^simd.u8x16)raw_data(s[i:]))
|
||||||
|
c0 := simd.lanes_eq(s0, c_vec)
|
||||||
|
if simd.reduce_or(c0) > 0 {
|
||||||
|
sel := simd.select(c0, SCANNER_INDICES_128, SCANNER_SENTINEL_MIN_128)
|
||||||
|
off := simd.reduce_min(sel)
|
||||||
|
return i + int(off)
|
||||||
|
}
|
||||||
|
|
||||||
|
i += SIMD_REG_SIZE_128
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan serially for the remainder.
|
||||||
|
for /**/; i < l; i += 1 {
|
||||||
if s[i] == c {
|
if s[i] == c {
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns -1 if c is not present
|
/*
|
||||||
last_index_byte :: proc(s: []byte, c: byte) -> int {
|
Scan a slice of bytes for a specific byte, starting from the end and working
|
||||||
for i := len(s)-1; i >= 0; i -= 1 {
|
backwards to the start.
|
||||||
|
|
||||||
|
This procedure safely handles slices of any length, including empty slices.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- data: A slice of bytes.
|
||||||
|
- c: The byte to search for.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- index: The index of the byte `c`, or -1 if it was not found.
|
||||||
|
*/
|
||||||
|
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 {
|
||||||
|
#reverse for ch, j in s {
|
||||||
|
if ch == c {
|
||||||
|
return j
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
c_vec: simd.u8x16 = c
|
||||||
|
when !simd.IS_EMULATED {
|
||||||
|
// Note: While this is something that could also logically take
|
||||||
|
// advantage of AVX512, the various downclocking and power
|
||||||
|
// consumption related woes make premature to have a dedicated
|
||||||
|
// code path.
|
||||||
|
when ODIN_ARCH == .amd64 && intrinsics.has_target_feature("avx2") {
|
||||||
|
c_vec_256: simd.u8x32 = c
|
||||||
|
|
||||||
|
s_vecs: [4]simd.u8x32 = ---
|
||||||
|
c_vecs: [4]simd.u8x32 = ---
|
||||||
|
m_vec: [4]u8 = ---
|
||||||
|
|
||||||
|
// Scan 128-byte chunks, using 256-bit SIMD.
|
||||||
|
for i >= 4 * SIMD_REG_SIZE_256 {
|
||||||
|
i -= 4 * SIMD_REG_SIZE_256
|
||||||
|
|
||||||
|
#unroll for j in 0..<4 {
|
||||||
|
s_vecs[j] = intrinsics.unaligned_load(cast(^simd.u8x32)raw_data(s[i+j*SIMD_REG_SIZE_256:]))
|
||||||
|
c_vecs[j] = simd.lanes_eq(s_vecs[j], c_vec_256)
|
||||||
|
m_vec[j] = simd.reduce_or(c_vecs[j])
|
||||||
|
}
|
||||||
|
if m_vec[0] | m_vec[1] | m_vec[2] | m_vec[3] > 0 {
|
||||||
|
#unroll for j in 0..<4 {
|
||||||
|
if m_vec[3-j] > 0 {
|
||||||
|
sel := simd.select(c_vecs[3-j], SCANNER_INDICES_256, SCANNER_SENTINEL_MAX_256)
|
||||||
|
off := simd.reduce_max(sel)
|
||||||
|
return i + (3-j) * SIMD_REG_SIZE_256 + int(off)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan 64-byte chunks, using 256-bit SIMD.
|
||||||
|
for i >= 2 * SIMD_REG_SIZE_256 {
|
||||||
|
i -= 2 * SIMD_REG_SIZE_256
|
||||||
|
|
||||||
|
#unroll for j in 0..<2 {
|
||||||
|
s_vecs[j] = intrinsics.unaligned_load(cast(^simd.u8x32)raw_data(s[i+j*SIMD_REG_SIZE_256:]))
|
||||||
|
c_vecs[j] = simd.lanes_eq(s_vecs[j], c_vec_256)
|
||||||
|
m_vec[j] = simd.reduce_or(c_vecs[j])
|
||||||
|
}
|
||||||
|
if m_vec[0] | m_vec[1] > 0 {
|
||||||
|
#unroll for j in 0..<2 {
|
||||||
|
if m_vec[1-j] > 0 {
|
||||||
|
sel := simd.select(c_vecs[1-j], SCANNER_INDICES_256, SCANNER_SENTINEL_MAX_256)
|
||||||
|
off := simd.reduce_max(sel)
|
||||||
|
return i + (1-j) * SIMD_REG_SIZE_256 + int(off)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s_vecs: [4]simd.u8x16 = ---
|
||||||
|
c_vecs: [4]simd.u8x16 = ---
|
||||||
|
m_vecs: [4]u8 = ---
|
||||||
|
|
||||||
|
// Scan 64-byte chunks, using 128-bit SIMD.
|
||||||
|
for i >= 4 * SIMD_REG_SIZE_128 {
|
||||||
|
i -= 4 * SIMD_REG_SIZE_128
|
||||||
|
|
||||||
|
#unroll for j in 0..<4 {
|
||||||
|
s_vecs[j] = intrinsics.unaligned_load(cast(^simd.u8x16)raw_data(s[i+j*SIMD_REG_SIZE_128:]))
|
||||||
|
c_vecs[j] = simd.lanes_eq(s_vecs[j], c_vec)
|
||||||
|
m_vecs[j] = simd.reduce_or(c_vecs[j])
|
||||||
|
}
|
||||||
|
if m_vecs[0] | m_vecs[1] | m_vecs[2] | m_vecs[3] > 0 {
|
||||||
|
#unroll for j in 0..<4 {
|
||||||
|
if m_vecs[3-j] > 0 {
|
||||||
|
sel := simd.select(c_vecs[3-j], SCANNER_INDICES_128, SCANNER_SENTINEL_MAX_128)
|
||||||
|
off := simd.reduce_max(sel)
|
||||||
|
return i + (3-j) * SIMD_REG_SIZE_128 + int(off)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan the remaining SIMD register sized chunks.
|
||||||
|
//
|
||||||
|
// Apparently LLVM does ok with 128-bit SWAR, so this path is also taken
|
||||||
|
// on potato targets. Scanning more at a time when LLVM is emulating SIMD
|
||||||
|
// likely does not buy much, as all that does is increase GP register
|
||||||
|
// pressure.
|
||||||
|
for i >= SIMD_REG_SIZE_128 {
|
||||||
|
i -= SIMD_REG_SIZE_128
|
||||||
|
|
||||||
|
s0 := intrinsics.unaligned_load(cast(^simd.u8x16)raw_data(s[i:]))
|
||||||
|
c0 := simd.lanes_eq(s0, c_vec)
|
||||||
|
if simd.reduce_or(c0) > 0 {
|
||||||
|
sel := simd.select(c0, SCANNER_INDICES_128, SCANNER_SENTINEL_MAX_128)
|
||||||
|
off := simd.reduce_max(sel)
|
||||||
|
return i + int(off)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan serially for the remainder.
|
||||||
|
for i > 0 {
|
||||||
|
i -= 1
|
||||||
if s[i] == c {
|
if s[i] == c {
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@private PRIME_RABIN_KARP :: 16777619
|
@private PRIME_RABIN_KARP :: 16777619
|
||||||
|
|
||||||
index :: proc(s, substr: []byte) -> int {
|
index :: proc(s, substr: []byte) -> int {
|
||||||
@@ -895,7 +1173,7 @@ split_multi_iterator :: proc(s: ^[]byte, substrs: [][]byte, skip_empty := false)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// scrub scruvs invalid utf-8 characters and replaces them with the replacement string
|
// Scrubs invalid utf-8 characters and replaces them with the replacement string
|
||||||
// Adjacent invalid bytes are only replaced once
|
// Adjacent invalid bytes are only replaced once
|
||||||
scrub :: proc(s: []byte, replacement: []byte, allocator := context.allocator) -> []byte {
|
scrub :: proc(s: []byte, replacement: []byte, allocator := context.allocator) -> []byte {
|
||||||
str := s
|
str := s
|
||||||
@@ -1167,3 +1445,28 @@ fields_proc :: proc(s: []byte, f: proc(rune) -> bool, allocator := context.alloc
|
|||||||
|
|
||||||
return subslices[:]
|
return subslices[:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// alias returns true iff a and b have a non-zero length, and any part of
|
||||||
|
// a overlaps with b.
|
||||||
|
alias :: proc "contextless" (a, b: []byte) -> bool {
|
||||||
|
a_len, b_len := len(a), len(b)
|
||||||
|
if a_len == 0 || b_len == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
a_start, b_start := uintptr(raw_data(a)), uintptr(raw_data(b))
|
||||||
|
a_end, b_end := a_start + uintptr(a_len-1), b_start + uintptr(b_len-1)
|
||||||
|
|
||||||
|
return a_start <= b_end && b_start <= a_end
|
||||||
|
}
|
||||||
|
|
||||||
|
// alias_inexactly returns true iff a and b have a non-zero length,
|
||||||
|
// the base pointer of a and b are NOT equal, and any part of a overlaps
|
||||||
|
// with b (ie: `alias(a, b)` with an exception that returns false for
|
||||||
|
// `a == b`, `b = a[:len(a)-69]` and similar conditions).
|
||||||
|
alias_inexactly :: proc "contextless" (a, b: []byte) -> bool {
|
||||||
|
if raw_data(a) == raw_data(b) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return alias(a, b)
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,10 +9,11 @@ Reader :: struct {
|
|||||||
prev_rune: int, // previous reading index of rune or < 0
|
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.s = s
|
||||||
r.i = 0
|
r.i = 0
|
||||||
r.prev_rune = -1
|
r.prev_rune = -1
|
||||||
|
return reader_to_stream(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
|
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) {
|
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)) {
|
if r.i >= i64(len(r.s)) {
|
||||||
return 0, .EOF
|
return 0, .EOF
|
||||||
}
|
}
|
||||||
@@ -42,6 +46,9 @@ reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Error) {
|
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 {
|
if off < 0 {
|
||||||
return 0, .Invalid_Offset
|
return 0, .Invalid_Offset
|
||||||
}
|
}
|
||||||
@@ -97,7 +104,6 @@ reader_unread_rune :: proc(r: ^Reader) -> io.Error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
|
reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
|
||||||
r.prev_rune = -1
|
|
||||||
abs: i64
|
abs: i64
|
||||||
switch whence {
|
switch whence {
|
||||||
case .Start:
|
case .Start:
|
||||||
@@ -114,6 +120,7 @@ reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.E
|
|||||||
return 0, .Invalid_Offset
|
return 0, .Invalid_Offset
|
||||||
}
|
}
|
||||||
r.i = abs
|
r.i = abs
|
||||||
|
r.prev_rune = -1
|
||||||
return abs, nil
|
return abs, nil
|
||||||
}
|
}
|
||||||
reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
|
reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||||
|
|||||||
+11
-1
@@ -1,6 +1,6 @@
|
|||||||
package c
|
package c
|
||||||
|
|
||||||
import builtin "core:builtin"
|
import builtin "base:builtin"
|
||||||
|
|
||||||
char :: builtin.u8 // assuming -funsigned-char
|
char :: builtin.u8 // assuming -funsigned-char
|
||||||
|
|
||||||
@@ -104,3 +104,13 @@ NULL :: rawptr(uintptr(0))
|
|||||||
NDEBUG :: !ODIN_DEBUG
|
NDEBUG :: !ODIN_DEBUG
|
||||||
|
|
||||||
CHAR_BIT :: 8
|
CHAR_BIT :: 8
|
||||||
|
|
||||||
|
// Since there are no types in C with an alignment larger than that of
|
||||||
|
// max_align_t, which cannot be larger than sizeof(long double) as any other
|
||||||
|
// exposed type wouldn't be valid C, the maximum alignment possible in a
|
||||||
|
// strictly conformant C implementation is 16 on the platforms we care about.
|
||||||
|
// The choice of 4096 bytes for storage of this type is more than enough on all
|
||||||
|
// relevant platforms.
|
||||||
|
va_list :: struct #align(16) {
|
||||||
|
_: [4096]u8,
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
package c_frontend_preprocess
|
|
||||||
|
|
||||||
import "core:c/frontend/tokenizer"
|
|
||||||
|
|
||||||
const_expr :: proc(rest: ^^Token, tok: ^Token) -> i64 {
|
|
||||||
// TODO(bill): Handle const_expr correctly
|
|
||||||
// This is effectively a mini-parser
|
|
||||||
|
|
||||||
assert(rest != nil)
|
|
||||||
assert(tok != nil)
|
|
||||||
rest^ = tokenizer.new_eof(tok)
|
|
||||||
switch v in tok.val {
|
|
||||||
case i64:
|
|
||||||
return v
|
|
||||||
case f64:
|
|
||||||
return i64(v)
|
|
||||||
case string:
|
|
||||||
return 0
|
|
||||||
case []u16:
|
|
||||||
// TODO
|
|
||||||
case []u32:
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,154 +0,0 @@
|
|||||||
package c_frontend_preprocess
|
|
||||||
|
|
||||||
import "core:unicode/utf8"
|
|
||||||
|
|
||||||
unquote_char :: proc(str: string, quote: byte) -> (r: rune, multiple_bytes: bool, tail_string: string, success: bool) {
|
|
||||||
hex_to_int :: proc(c: byte) -> int {
|
|
||||||
switch c {
|
|
||||||
case '0'..='9': return int(c-'0')
|
|
||||||
case 'a'..='f': return int(c-'a')+10
|
|
||||||
case 'A'..='F': return int(c-'A')+10
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
w: int
|
|
||||||
|
|
||||||
if str[0] == quote && quote == '"' {
|
|
||||||
return
|
|
||||||
} else if str[0] >= 0x80 {
|
|
||||||
r, w = utf8.decode_rune_in_string(str)
|
|
||||||
return r, true, str[w:], true
|
|
||||||
} else if str[0] != '\\' {
|
|
||||||
return rune(str[0]), false, str[1:], true
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(str) <= 1 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s := str
|
|
||||||
c := s[1]
|
|
||||||
s = s[2:]
|
|
||||||
|
|
||||||
switch c {
|
|
||||||
case: r = rune(c)
|
|
||||||
|
|
||||||
case 'a': r = '\a'
|
|
||||||
case 'b': r = '\b'
|
|
||||||
case 'e': r = '\e'
|
|
||||||
case 'f': r = '\f'
|
|
||||||
case 'n': r = '\n'
|
|
||||||
case 'r': r = '\r'
|
|
||||||
case 't': r = '\t'
|
|
||||||
case 'v': r = '\v'
|
|
||||||
case '\\': r = '\\'
|
|
||||||
|
|
||||||
case '"': r = '"'
|
|
||||||
case '\'': r = '\''
|
|
||||||
|
|
||||||
case '0'..='7':
|
|
||||||
v := int(c-'0')
|
|
||||||
if len(s) < 2 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for i in 0..<len(s) {
|
|
||||||
d := int(s[i]-'0')
|
|
||||||
if d < 0 || d > 7 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
v = (v<<3) | d
|
|
||||||
}
|
|
||||||
s = s[2:]
|
|
||||||
if v > 0xff {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r = rune(v)
|
|
||||||
|
|
||||||
case 'x', 'u', 'U':
|
|
||||||
count: int
|
|
||||||
switch c {
|
|
||||||
case 'x': count = 2
|
|
||||||
case 'u': count = 4
|
|
||||||
case 'U': count = 8
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(s) < count {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for i in 0..<count {
|
|
||||||
d := hex_to_int(s[i])
|
|
||||||
if d < 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
r = (r<<4) | rune(d)
|
|
||||||
}
|
|
||||||
s = s[count:]
|
|
||||||
if c == 'x' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if r > utf8.MAX_RUNE {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
multiple_bytes = true
|
|
||||||
}
|
|
||||||
|
|
||||||
success = true
|
|
||||||
tail_string = s
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
unquote_string :: proc(lit: string, allocator := context.allocator) -> (res: string, allocated, success: bool) {
|
|
||||||
contains_rune :: proc(s: string, r: rune) -> int {
|
|
||||||
for c, offset in s {
|
|
||||||
if c == r {
|
|
||||||
return offset
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(len(lit) >= 2)
|
|
||||||
|
|
||||||
s := lit
|
|
||||||
quote := '"'
|
|
||||||
|
|
||||||
if s == `""` {
|
|
||||||
return "", false, true
|
|
||||||
}
|
|
||||||
|
|
||||||
if contains_rune(s, '\n') >= 0 {
|
|
||||||
return s, false, false
|
|
||||||
}
|
|
||||||
|
|
||||||
if contains_rune(s, '\\') < 0 && contains_rune(s, quote) < 0 {
|
|
||||||
if quote == '"' {
|
|
||||||
return s, false, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s = s[1:len(s)-1]
|
|
||||||
|
|
||||||
|
|
||||||
buf_len := 3*len(s) / 2
|
|
||||||
buf := make([]byte, buf_len, allocator)
|
|
||||||
offset := 0
|
|
||||||
for len(s) > 0 {
|
|
||||||
r, multiple_bytes, tail_string, ok := unquote_char(s, byte(quote))
|
|
||||||
if !ok {
|
|
||||||
delete(buf)
|
|
||||||
return s, false, false
|
|
||||||
}
|
|
||||||
s = tail_string
|
|
||||||
if r < 0x80 || !multiple_bytes {
|
|
||||||
buf[offset] = byte(r)
|
|
||||||
offset += 1
|
|
||||||
} else {
|
|
||||||
b, w := utf8.encode_rune(r)
|
|
||||||
copy(buf[offset:], b[:w])
|
|
||||||
offset += w
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
new_string := string(buf[:offset])
|
|
||||||
|
|
||||||
return new_string, true, true
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
package demo
|
|
||||||
|
|
||||||
import tokenizer "core:c/frontend/tokenizer"
|
|
||||||
import preprocessor "core:c/frontend/preprocessor"
|
|
||||||
import "core:fmt"
|
|
||||||
|
|
||||||
main :: proc() {
|
|
||||||
t := &tokenizer.Tokenizer{};
|
|
||||||
tokenizer.init_defaults(t);
|
|
||||||
|
|
||||||
cpp := &preprocessor.Preprocessor{};
|
|
||||||
cpp.warn, cpp.err = t.warn, t.err;
|
|
||||||
preprocessor.init_lookup_tables(cpp);
|
|
||||||
preprocessor.init_default_macros(cpp);
|
|
||||||
cpp.include_paths = {"my/path/to/include"};
|
|
||||||
|
|
||||||
tok := tokenizer.tokenize_file(t, "the/source/file.c", 1);
|
|
||||||
|
|
||||||
tok = preprocessor.preprocess(cpp, tok);
|
|
||||||
if tok != nil {
|
|
||||||
for t := tok; t.kind != .EOF; t = t.next {
|
|
||||||
fmt.println(t.lit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.println("[Done]");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
package c_frontend_tokenizer
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
package c_frontend_tokenizer
|
|
||||||
|
|
||||||
// NOTE(bill): This is a really dumb approach for a hide set,
|
|
||||||
// but it's really simple and probably fast enough in practice
|
|
||||||
|
|
||||||
|
|
||||||
Hide_Set :: struct {
|
|
||||||
next: ^Hide_Set,
|
|
||||||
name: string,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
new_hide_set :: proc(name: string) -> ^Hide_Set {
|
|
||||||
hs := new(Hide_Set)
|
|
||||||
hs.name = name
|
|
||||||
return hs
|
|
||||||
}
|
|
||||||
|
|
||||||
hide_set_contains :: proc(hs: ^Hide_Set, name: string) -> bool {
|
|
||||||
for h := hs; h != nil; h = h.next {
|
|
||||||
if h.name == name {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
hide_set_union :: proc(a, b: ^Hide_Set) -> ^Hide_Set {
|
|
||||||
head: Hide_Set
|
|
||||||
curr := &head
|
|
||||||
|
|
||||||
for h := a; h != nil; h = h.next {
|
|
||||||
curr.next = new_hide_set(h.name)
|
|
||||||
curr = curr.next
|
|
||||||
}
|
|
||||||
curr.next = b
|
|
||||||
return head.next
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
hide_set_intersection :: proc(a, b: ^Hide_Set) -> ^Hide_Set {
|
|
||||||
head: Hide_Set
|
|
||||||
curr := &head
|
|
||||||
|
|
||||||
for h := a; h != nil; h = h.next {
|
|
||||||
if hide_set_contains(b, h.name) {
|
|
||||||
curr.next = new_hide_set(h.name)
|
|
||||||
curr = curr.next
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return head.next
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
add_hide_set :: proc(tok: ^Token, hs: ^Hide_Set) -> ^Token {
|
|
||||||
head: Token
|
|
||||||
curr := &head
|
|
||||||
|
|
||||||
tok := tok
|
|
||||||
for ; tok != nil; tok = tok.next {
|
|
||||||
t := copy_token(tok)
|
|
||||||
t.hide_set = hide_set_union(t.hide_set, hs)
|
|
||||||
curr.next = t
|
|
||||||
curr = curr.next
|
|
||||||
}
|
|
||||||
return head.next
|
|
||||||
}
|
|
||||||
@@ -1,169 +0,0 @@
|
|||||||
package c_frontend_tokenizer
|
|
||||||
|
|
||||||
|
|
||||||
Pos :: struct {
|
|
||||||
file: string,
|
|
||||||
line: int,
|
|
||||||
column: int,
|
|
||||||
offset: int,
|
|
||||||
}
|
|
||||||
|
|
||||||
Token_Kind :: enum {
|
|
||||||
Invalid,
|
|
||||||
Ident,
|
|
||||||
Punct,
|
|
||||||
Keyword,
|
|
||||||
Char,
|
|
||||||
String,
|
|
||||||
Number,
|
|
||||||
PP_Number,
|
|
||||||
Comment,
|
|
||||||
EOF,
|
|
||||||
}
|
|
||||||
|
|
||||||
File :: struct {
|
|
||||||
name: string,
|
|
||||||
id: int,
|
|
||||||
src: []byte,
|
|
||||||
|
|
||||||
display_name: string,
|
|
||||||
line_delta: int,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Token_Type_Hint :: enum u8 {
|
|
||||||
None,
|
|
||||||
|
|
||||||
Int,
|
|
||||||
Long,
|
|
||||||
Long_Long,
|
|
||||||
|
|
||||||
Unsigned_Int,
|
|
||||||
Unsigned_Long,
|
|
||||||
Unsigned_Long_Long,
|
|
||||||
|
|
||||||
Float,
|
|
||||||
Double,
|
|
||||||
Long_Double,
|
|
||||||
|
|
||||||
UTF_8,
|
|
||||||
UTF_16,
|
|
||||||
UTF_32,
|
|
||||||
UTF_Wide,
|
|
||||||
}
|
|
||||||
|
|
||||||
Token_Value :: union {
|
|
||||||
i64,
|
|
||||||
f64,
|
|
||||||
string,
|
|
||||||
[]u16,
|
|
||||||
[]u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
Token :: struct {
|
|
||||||
kind: Token_Kind,
|
|
||||||
next: ^Token,
|
|
||||||
lit: string,
|
|
||||||
|
|
||||||
pos: Pos,
|
|
||||||
file: ^File,
|
|
||||||
line_delta: int,
|
|
||||||
at_bol: bool,
|
|
||||||
has_space: bool,
|
|
||||||
|
|
||||||
type_hint: Token_Type_Hint,
|
|
||||||
val: Token_Value,
|
|
||||||
prefix: string,
|
|
||||||
|
|
||||||
// Preprocessor values
|
|
||||||
hide_set: ^Hide_Set,
|
|
||||||
origin: ^Token,
|
|
||||||
}
|
|
||||||
|
|
||||||
Is_Keyword_Proc :: #type proc(tok: ^Token) -> bool
|
|
||||||
|
|
||||||
copy_token :: proc(tok: ^Token) -> ^Token {
|
|
||||||
t, _ := new_clone(tok^)
|
|
||||||
t.next = nil
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
new_eof :: proc(tok: ^Token) -> ^Token {
|
|
||||||
t, _ := new_clone(tok^)
|
|
||||||
t.kind = .EOF
|
|
||||||
t.lit = ""
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
default_is_keyword :: proc(tok: ^Token) -> bool {
|
|
||||||
if tok.kind == .Keyword {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if len(tok.lit) > 0 {
|
|
||||||
return default_keyword_set[tok.lit]
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
token_name := [Token_Kind]string {
|
|
||||||
.Invalid = "invalid",
|
|
||||||
.Ident = "ident",
|
|
||||||
.Punct = "punct",
|
|
||||||
.Keyword = "keyword",
|
|
||||||
.Char = "char",
|
|
||||||
.String = "string",
|
|
||||||
.Number = "number",
|
|
||||||
.PP_Number = "preprocessor number",
|
|
||||||
.Comment = "comment",
|
|
||||||
.EOF = "eof",
|
|
||||||
}
|
|
||||||
|
|
||||||
default_keyword_set := map[string]bool{
|
|
||||||
"auto" = true,
|
|
||||||
"break" = true,
|
|
||||||
"case" = true,
|
|
||||||
"char" = true,
|
|
||||||
"const" = true,
|
|
||||||
"continue" = true,
|
|
||||||
"default" = true,
|
|
||||||
"do" = true,
|
|
||||||
"double" = true,
|
|
||||||
"else" = true,
|
|
||||||
"enum" = true,
|
|
||||||
"extern" = true,
|
|
||||||
"float" = true,
|
|
||||||
"for" = true,
|
|
||||||
"goto" = true,
|
|
||||||
"if" = true,
|
|
||||||
"int" = true,
|
|
||||||
"long" = true,
|
|
||||||
"register" = true,
|
|
||||||
"restrict" = true,
|
|
||||||
"return" = true,
|
|
||||||
"short" = true,
|
|
||||||
"signed" = true,
|
|
||||||
"sizeof" = true,
|
|
||||||
"static" = true,
|
|
||||||
"struct" = true,
|
|
||||||
"switch" = true,
|
|
||||||
"typedef" = true,
|
|
||||||
"union" = true,
|
|
||||||
"unsigned" = true,
|
|
||||||
"void" = true,
|
|
||||||
"volatile" = true,
|
|
||||||
"while" = true,
|
|
||||||
"_Alignas" = true,
|
|
||||||
"_Alignof" = true,
|
|
||||||
"_Atomic" = true,
|
|
||||||
"_Bool" = true,
|
|
||||||
"_Generic" = true,
|
|
||||||
"_Noreturn" = true,
|
|
||||||
"_Thread_local" = true,
|
|
||||||
"__restrict" = true,
|
|
||||||
"typeof" = true,
|
|
||||||
"asm" = true,
|
|
||||||
"__restrict__" = true,
|
|
||||||
"__thread" = true,
|
|
||||||
"__attribute__" = true,
|
|
||||||
}
|
|
||||||
@@ -1,667 +0,0 @@
|
|||||||
package c_frontend_tokenizer
|
|
||||||
|
|
||||||
import "core:fmt"
|
|
||||||
import "core:os"
|
|
||||||
import "core:strings"
|
|
||||||
import "core:unicode/utf8"
|
|
||||||
|
|
||||||
|
|
||||||
Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any)
|
|
||||||
|
|
||||||
|
|
||||||
Tokenizer :: struct {
|
|
||||||
// Immutable data
|
|
||||||
path: string,
|
|
||||||
src: []byte,
|
|
||||||
|
|
||||||
|
|
||||||
// Tokenizing state
|
|
||||||
ch: rune,
|
|
||||||
offset: int,
|
|
||||||
read_offset: int,
|
|
||||||
line_offset: int,
|
|
||||||
line_count: int,
|
|
||||||
|
|
||||||
// Extra information for tokens
|
|
||||||
at_bol: bool,
|
|
||||||
has_space: bool,
|
|
||||||
|
|
||||||
// Mutable data
|
|
||||||
err: Error_Handler,
|
|
||||||
warn: Error_Handler,
|
|
||||||
error_count: int,
|
|
||||||
warning_count: int,
|
|
||||||
}
|
|
||||||
|
|
||||||
init_defaults :: proc(t: ^Tokenizer, err: Error_Handler = default_error_handler, warn: Error_Handler = default_warn_handler) {
|
|
||||||
t.err = err
|
|
||||||
t.warn = warn
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@(private)
|
|
||||||
offset_to_pos :: proc(t: ^Tokenizer, offset: int) -> (pos: Pos) {
|
|
||||||
pos.file = t.path
|
|
||||||
pos.offset = offset
|
|
||||||
pos.line = t.line_count
|
|
||||||
pos.column = offset - t.line_offset + 1
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
default_error_handler :: proc(pos: Pos, msg: string, args: ..any) {
|
|
||||||
fmt.eprintf("%s(%d:%d) ", pos.file, pos.line, pos.column)
|
|
||||||
fmt.eprintf(msg, ..args)
|
|
||||||
fmt.eprintf("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
default_warn_handler :: proc(pos: Pos, msg: string, args: ..any) {
|
|
||||||
fmt.eprintf("%s(%d:%d) warning: ", pos.file, pos.line, pos.column)
|
|
||||||
fmt.eprintf(msg, ..args)
|
|
||||||
fmt.eprintf("\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
error_offset :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
|
|
||||||
pos := offset_to_pos(t, offset)
|
|
||||||
if t.err != nil {
|
|
||||||
t.err(pos, msg, ..args)
|
|
||||||
}
|
|
||||||
t.error_count += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
warn_offset :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
|
|
||||||
pos := offset_to_pos(t, offset)
|
|
||||||
if t.warn != nil {
|
|
||||||
t.warn(pos, msg, ..args)
|
|
||||||
}
|
|
||||||
t.warning_count += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
error :: proc(t: ^Tokenizer, tok: ^Token, msg: string, args: ..any) {
|
|
||||||
pos := tok.pos
|
|
||||||
if t.err != nil {
|
|
||||||
t.err(pos, msg, ..args)
|
|
||||||
}
|
|
||||||
t.error_count += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
warn :: proc(t: ^Tokenizer, tok: ^Token, msg: string, args: ..any) {
|
|
||||||
pos := tok.pos
|
|
||||||
if t.warn != nil {
|
|
||||||
t.warn(pos, msg, ..args)
|
|
||||||
}
|
|
||||||
t.warning_count += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
advance_rune :: proc(t: ^Tokenizer) {
|
|
||||||
if t.read_offset < len(t.src) {
|
|
||||||
t.offset = t.read_offset
|
|
||||||
if t.ch == '\n' {
|
|
||||||
t.at_bol = true
|
|
||||||
t.line_offset = t.offset
|
|
||||||
t.line_count += 1
|
|
||||||
}
|
|
||||||
r, w := rune(t.src[t.read_offset]), 1
|
|
||||||
switch {
|
|
||||||
case r == 0:
|
|
||||||
error_offset(t, t.offset, "illegal character NUL")
|
|
||||||
case r >= utf8.RUNE_SELF:
|
|
||||||
r, w = utf8.decode_rune(t.src[t.read_offset:])
|
|
||||||
if r == utf8.RUNE_ERROR && w == 1 {
|
|
||||||
error_offset(t, t.offset, "illegal UTF-8 encoding")
|
|
||||||
} else if r == utf8.RUNE_BOM && t.offset > 0 {
|
|
||||||
error_offset(t, t.offset, "illegal byte order mark")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.read_offset += w
|
|
||||||
t.ch = r
|
|
||||||
} else {
|
|
||||||
t.offset = len(t.src)
|
|
||||||
if t.ch == '\n' {
|
|
||||||
t.at_bol = true
|
|
||||||
t.line_offset = t.offset
|
|
||||||
t.line_count += 1
|
|
||||||
}
|
|
||||||
t.ch = -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
advance_rune_n :: proc(t: ^Tokenizer, n: int) {
|
|
||||||
for _ in 0..<n {
|
|
||||||
advance_rune(t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is_digit :: proc(r: rune) -> bool {
|
|
||||||
return '0' <= r && r <= '9'
|
|
||||||
}
|
|
||||||
|
|
||||||
skip_whitespace :: proc(t: ^Tokenizer) {
|
|
||||||
for {
|
|
||||||
switch t.ch {
|
|
||||||
case ' ', '\t', '\r', '\v', '\f', '\n':
|
|
||||||
t.has_space = true
|
|
||||||
advance_rune(t)
|
|
||||||
case:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scan_comment :: proc(t: ^Tokenizer) -> string {
|
|
||||||
offset := t.offset-1
|
|
||||||
next := -1
|
|
||||||
general: {
|
|
||||||
if t.ch == '/'{ // line comments
|
|
||||||
advance_rune(t)
|
|
||||||
for t.ch != '\n' && t.ch >= 0 {
|
|
||||||
advance_rune(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
next = t.offset
|
|
||||||
if t.ch == '\n' {
|
|
||||||
next += 1
|
|
||||||
}
|
|
||||||
break general
|
|
||||||
}
|
|
||||||
|
|
||||||
/* style comment */
|
|
||||||
advance_rune(t)
|
|
||||||
for t.ch >= 0 {
|
|
||||||
ch := t.ch
|
|
||||||
advance_rune(t)
|
|
||||||
if ch == '*' && t.ch == '/' {
|
|
||||||
advance_rune(t)
|
|
||||||
next = t.offset
|
|
||||||
break general
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
error_offset(t, offset, "comment not terminated")
|
|
||||||
}
|
|
||||||
|
|
||||||
lit := t.src[offset : t.offset]
|
|
||||||
|
|
||||||
// NOTE(bill): Strip CR for line comments
|
|
||||||
for len(lit) > 2 && lit[1] == '/' && lit[len(lit)-1] == '\r' {
|
|
||||||
lit = lit[:len(lit)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return string(lit)
|
|
||||||
}
|
|
||||||
|
|
||||||
scan_identifier :: proc(t: ^Tokenizer) -> string {
|
|
||||||
offset := t.offset
|
|
||||||
|
|
||||||
for is_ident1(t.ch) {
|
|
||||||
advance_rune(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(t.src[offset : t.offset])
|
|
||||||
}
|
|
||||||
|
|
||||||
scan_string :: proc(t: ^Tokenizer) -> string {
|
|
||||||
offset := t.offset-1
|
|
||||||
|
|
||||||
for {
|
|
||||||
ch := t.ch
|
|
||||||
if ch == '\n' || ch < 0 {
|
|
||||||
error_offset(t, offset, "string literal was not terminated")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
advance_rune(t)
|
|
||||||
if ch == '"' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if ch == '\\' {
|
|
||||||
scan_escape(t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(t.src[offset : t.offset])
|
|
||||||
}
|
|
||||||
|
|
||||||
digit_val :: proc(r: rune) -> int {
|
|
||||||
switch r {
|
|
||||||
case '0'..='9':
|
|
||||||
return int(r-'0')
|
|
||||||
case 'A'..='F':
|
|
||||||
return int(r-'A' + 10)
|
|
||||||
case 'a'..='f':
|
|
||||||
return int(r-'a' + 10)
|
|
||||||
}
|
|
||||||
return 16
|
|
||||||
}
|
|
||||||
|
|
||||||
scan_escape :: proc(t: ^Tokenizer) -> bool {
|
|
||||||
offset := t.offset
|
|
||||||
|
|
||||||
esc := t.ch
|
|
||||||
n: int
|
|
||||||
base, max: u32
|
|
||||||
switch esc {
|
|
||||||
case 'a', 'b', 'e', 'f', 'n', 't', 'v', 'r', '\\', '\'', '"':
|
|
||||||
advance_rune(t)
|
|
||||||
return true
|
|
||||||
|
|
||||||
case '0'..='7':
|
|
||||||
for digit_val(t.ch) < 8 {
|
|
||||||
advance_rune(t)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
case 'x':
|
|
||||||
advance_rune(t)
|
|
||||||
for digit_val(t.ch) < 16 {
|
|
||||||
advance_rune(t)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
case 'u':
|
|
||||||
advance_rune(t)
|
|
||||||
n, base, max = 4, 16, utf8.MAX_RUNE
|
|
||||||
case 'U':
|
|
||||||
advance_rune(t)
|
|
||||||
n, base, max = 8, 16, utf8.MAX_RUNE
|
|
||||||
case:
|
|
||||||
if t.ch < 0 {
|
|
||||||
error_offset(t, offset, "escape sequence was not terminated")
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
x: u32
|
|
||||||
main_loop: for n > 0 {
|
|
||||||
d := u32(digit_val(t.ch))
|
|
||||||
if d >= base {
|
|
||||||
if t.ch == '"' || t.ch == '\'' {
|
|
||||||
break main_loop
|
|
||||||
}
|
|
||||||
if t.ch < 0 {
|
|
||||||
error_offset(t, t.offset, "escape sequence was not terminated")
|
|
||||||
} else {
|
|
||||||
error_offset(t, t.offset, "illegal character '%r' : %d in escape sequence", t.ch, t.ch)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
x = x*base + d
|
|
||||||
advance_rune(t)
|
|
||||||
n -= 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if x > max || 0xd800 <= x && x <= 0xe000 {
|
|
||||||
error_offset(t, offset, "escape sequence is an invalid Unicode code point")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
scan_rune :: proc(t: ^Tokenizer) -> string {
|
|
||||||
offset := t.offset-1
|
|
||||||
valid := true
|
|
||||||
n := 0
|
|
||||||
for {
|
|
||||||
ch := t.ch
|
|
||||||
if ch == '\n' || ch < 0 {
|
|
||||||
if valid {
|
|
||||||
error_offset(t, offset, "rune literal not terminated")
|
|
||||||
valid = false
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
advance_rune(t)
|
|
||||||
if ch == '\'' {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
n += 1
|
|
||||||
if ch == '\\' {
|
|
||||||
if !scan_escape(t) {
|
|
||||||
valid = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if valid && n != 1 {
|
|
||||||
error_offset(t, offset, "illegal rune literal")
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(t.src[offset : t.offset])
|
|
||||||
}
|
|
||||||
|
|
||||||
scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Token_Kind, string) {
|
|
||||||
scan_mantissa :: proc(t: ^Tokenizer, base: int) {
|
|
||||||
for digit_val(t.ch) < base {
|
|
||||||
advance_rune(t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scan_exponent :: proc(t: ^Tokenizer) {
|
|
||||||
if t.ch == 'e' || t.ch == 'E' || t.ch == 'p' || t.ch == 'P' {
|
|
||||||
advance_rune(t)
|
|
||||||
if t.ch == '-' || t.ch == '+' {
|
|
||||||
advance_rune(t)
|
|
||||||
}
|
|
||||||
if digit_val(t.ch) < 10 {
|
|
||||||
scan_mantissa(t, 10)
|
|
||||||
} else {
|
|
||||||
error_offset(t, t.offset, "illegal floating-point exponent")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
scan_fraction :: proc(t: ^Tokenizer) -> (early_exit: bool) {
|
|
||||||
if t.ch == '.' && peek(t) == '.' {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if t.ch == '.' {
|
|
||||||
advance_rune(t)
|
|
||||||
scan_mantissa(t, 10)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
check_end := true
|
|
||||||
|
|
||||||
|
|
||||||
offset := t.offset
|
|
||||||
seen_point := seen_decimal_point
|
|
||||||
|
|
||||||
if seen_point {
|
|
||||||
offset -= 1
|
|
||||||
scan_mantissa(t, 10)
|
|
||||||
scan_exponent(t)
|
|
||||||
} else {
|
|
||||||
if t.ch == '0' {
|
|
||||||
int_base :: proc(t: ^Tokenizer, base: int, msg: string) {
|
|
||||||
prev := t.offset
|
|
||||||
advance_rune(t)
|
|
||||||
scan_mantissa(t, base)
|
|
||||||
if t.offset - prev <= 1 {
|
|
||||||
error_offset(t, t.offset, msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
advance_rune(t)
|
|
||||||
switch t.ch {
|
|
||||||
case 'b', 'B':
|
|
||||||
int_base(t, 2, "illegal binary integer")
|
|
||||||
case 'x', 'X':
|
|
||||||
int_base(t, 16, "illegal hexadecimal integer")
|
|
||||||
case:
|
|
||||||
seen_point = false
|
|
||||||
scan_mantissa(t, 10)
|
|
||||||
if t.ch == '.' {
|
|
||||||
seen_point = true
|
|
||||||
if scan_fraction(t) {
|
|
||||||
check_end = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if check_end {
|
|
||||||
scan_exponent(t)
|
|
||||||
check_end = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if check_end {
|
|
||||||
scan_mantissa(t, 10)
|
|
||||||
|
|
||||||
if !scan_fraction(t) {
|
|
||||||
scan_exponent(t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return .Number, string(t.src[offset : t.offset])
|
|
||||||
}
|
|
||||||
|
|
||||||
scan_punct :: proc(t: ^Tokenizer, ch: rune) -> (kind: Token_Kind) {
|
|
||||||
kind = .Punct
|
|
||||||
switch ch {
|
|
||||||
case:
|
|
||||||
kind = .Invalid
|
|
||||||
|
|
||||||
case '<', '>':
|
|
||||||
if t.ch == ch {
|
|
||||||
advance_rune(t)
|
|
||||||
}
|
|
||||||
if t.ch == '=' {
|
|
||||||
advance_rune(t)
|
|
||||||
}
|
|
||||||
case '!', '+', '-', '*', '/', '%', '^', '=':
|
|
||||||
if t.ch == '=' {
|
|
||||||
advance_rune(t)
|
|
||||||
}
|
|
||||||
case '#':
|
|
||||||
if t.ch == '#' {
|
|
||||||
advance_rune(t)
|
|
||||||
}
|
|
||||||
case '&':
|
|
||||||
if t.ch == '=' || t.ch == '&' {
|
|
||||||
advance_rune(t)
|
|
||||||
}
|
|
||||||
case '|':
|
|
||||||
if t.ch == '=' || t.ch == '|' {
|
|
||||||
advance_rune(t)
|
|
||||||
}
|
|
||||||
case '(', ')', '[', ']', '{', '}':
|
|
||||||
// okay
|
|
||||||
case '~', ',', ':', ';', '?':
|
|
||||||
// okay
|
|
||||||
case '`':
|
|
||||||
// okay
|
|
||||||
case '.':
|
|
||||||
if t.ch == '.' && peek(t) == '.' {
|
|
||||||
advance_rune(t)
|
|
||||||
advance_rune(t) // consume last '.'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
peek :: proc(t: ^Tokenizer) -> byte {
|
|
||||||
if t.read_offset < len(t.src) {
|
|
||||||
return t.src[t.read_offset]
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
peek_str :: proc(t: ^Tokenizer, str: string) -> bool {
|
|
||||||
if t.read_offset < len(t.src) {
|
|
||||||
return strings.has_prefix(string(t.src[t.offset:]), str)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
scan_literal_prefix :: proc(t: ^Tokenizer, str: string, prefix: ^string) -> bool {
|
|
||||||
if peek_str(t, str) {
|
|
||||||
offset := t.offset
|
|
||||||
for _ in str {
|
|
||||||
advance_rune(t)
|
|
||||||
}
|
|
||||||
prefix^ = string(t.src[offset:][:len(str)-1])
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
allow_next_to_be_newline :: proc(t: ^Tokenizer) -> bool {
|
|
||||||
if t.ch == '\n' {
|
|
||||||
advance_rune(t)
|
|
||||||
return true
|
|
||||||
} else if t.ch == '\r' && peek(t) == '\n' { // allow for MS-DOS style line endings
|
|
||||||
advance_rune(t) // \r
|
|
||||||
advance_rune(t) // \n
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
scan :: proc(t: ^Tokenizer, f: ^File) -> ^Token {
|
|
||||||
skip_whitespace(t)
|
|
||||||
|
|
||||||
offset := t.offset
|
|
||||||
|
|
||||||
kind: Token_Kind
|
|
||||||
lit: string
|
|
||||||
prefix: string
|
|
||||||
|
|
||||||
switch ch := t.ch; {
|
|
||||||
case scan_literal_prefix(t, `u8"`, &prefix):
|
|
||||||
kind = .String
|
|
||||||
lit = scan_string(t)
|
|
||||||
case scan_literal_prefix(t, `u"`, &prefix):
|
|
||||||
kind = .String
|
|
||||||
lit = scan_string(t)
|
|
||||||
case scan_literal_prefix(t, `L"`, &prefix):
|
|
||||||
kind = .String
|
|
||||||
lit = scan_string(t)
|
|
||||||
case scan_literal_prefix(t, `U"`, &prefix):
|
|
||||||
kind = .String
|
|
||||||
lit = scan_string(t)
|
|
||||||
case scan_literal_prefix(t, `u'`, &prefix):
|
|
||||||
kind = .Char
|
|
||||||
lit = scan_rune(t)
|
|
||||||
case scan_literal_prefix(t, `L'`, &prefix):
|
|
||||||
kind = .Char
|
|
||||||
lit = scan_rune(t)
|
|
||||||
case scan_literal_prefix(t, `U'`, &prefix):
|
|
||||||
kind = .Char
|
|
||||||
lit = scan_rune(t)
|
|
||||||
|
|
||||||
case is_ident0(ch):
|
|
||||||
lit = scan_identifier(t)
|
|
||||||
kind = .Ident
|
|
||||||
case '0' <= ch && ch <= '9':
|
|
||||||
kind, lit = scan_number(t, false)
|
|
||||||
case:
|
|
||||||
advance_rune(t)
|
|
||||||
switch ch {
|
|
||||||
case -1:
|
|
||||||
kind = .EOF
|
|
||||||
case '\\':
|
|
||||||
kind = .Punct
|
|
||||||
if allow_next_to_be_newline(t) {
|
|
||||||
t.at_bol = true
|
|
||||||
t.has_space = false
|
|
||||||
return scan(t, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
case '.':
|
|
||||||
if is_digit(t.ch) {
|
|
||||||
kind, lit = scan_number(t, true)
|
|
||||||
} else {
|
|
||||||
kind = scan_punct(t, ch)
|
|
||||||
}
|
|
||||||
case '"':
|
|
||||||
kind = .String
|
|
||||||
lit = scan_string(t)
|
|
||||||
case '\'':
|
|
||||||
kind = .Char
|
|
||||||
lit = scan_rune(t)
|
|
||||||
case '/':
|
|
||||||
if t.ch == '/' || t.ch == '*' {
|
|
||||||
kind = .Comment
|
|
||||||
lit = scan_comment(t)
|
|
||||||
t.has_space = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
fallthrough
|
|
||||||
case:
|
|
||||||
kind = scan_punct(t, ch)
|
|
||||||
if kind == .Invalid && ch != utf8.RUNE_BOM {
|
|
||||||
error_offset(t, t.offset, "illegal character '%r': %d", ch, ch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if lit == "" {
|
|
||||||
lit = string(t.src[offset : t.offset])
|
|
||||||
}
|
|
||||||
|
|
||||||
if kind == .Comment {
|
|
||||||
return scan(t, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
tok := new(Token)
|
|
||||||
tok.kind = kind
|
|
||||||
tok.lit = lit
|
|
||||||
tok.pos = offset_to_pos(t, offset)
|
|
||||||
tok.file = f
|
|
||||||
tok.prefix = prefix
|
|
||||||
tok.at_bol = t.at_bol
|
|
||||||
tok.has_space = t.has_space
|
|
||||||
|
|
||||||
t.at_bol, t.has_space = false, false
|
|
||||||
|
|
||||||
return tok
|
|
||||||
}
|
|
||||||
|
|
||||||
tokenize :: proc(t: ^Tokenizer, f: ^File) -> ^Token {
|
|
||||||
setup_tokenizer: {
|
|
||||||
t.src = f.src
|
|
||||||
t.ch = ' '
|
|
||||||
t.offset = 0
|
|
||||||
t.read_offset = 0
|
|
||||||
t.line_offset = 0
|
|
||||||
t.line_count = len(t.src) > 0 ? 1 : 0
|
|
||||||
t.error_count = 0
|
|
||||||
t.path = f.name
|
|
||||||
|
|
||||||
|
|
||||||
advance_rune(t)
|
|
||||||
if t.ch == utf8.RUNE_BOM {
|
|
||||||
advance_rune(t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
t.at_bol = true
|
|
||||||
t.has_space = false
|
|
||||||
|
|
||||||
head: Token
|
|
||||||
curr := &head
|
|
||||||
for {
|
|
||||||
tok := scan(t, f)
|
|
||||||
if tok == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
curr.next = tok
|
|
||||||
curr = curr.next
|
|
||||||
if tok.kind == .EOF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return head.next
|
|
||||||
}
|
|
||||||
|
|
||||||
add_new_file :: proc(t: ^Tokenizer, name: string, src: []byte, id: int) -> ^File {
|
|
||||||
file := new(File)
|
|
||||||
file.id = id
|
|
||||||
file.src = src
|
|
||||||
file.name = name
|
|
||||||
file.display_name = name
|
|
||||||
return file
|
|
||||||
}
|
|
||||||
|
|
||||||
tokenize_file :: proc(t: ^Tokenizer, path: string, id: int, loc := #caller_location) -> ^Token {
|
|
||||||
src, ok := os.read_entire_file(path)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return tokenize(t, add_new_file(t, path, src, id))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline_tokenize :: proc(t: ^Tokenizer, tok: ^Token, src: []byte) -> ^Token {
|
|
||||||
file := new(File)
|
|
||||||
file.src = src
|
|
||||||
if tok.file != nil {
|
|
||||||
file.id = tok.file.id
|
|
||||||
file.name = tok.file.name
|
|
||||||
file.display_name = tok.file.name
|
|
||||||
}
|
|
||||||
|
|
||||||
return tokenize(t, file)
|
|
||||||
}
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
package c_frontend_tokenizer
|
|
||||||
|
|
||||||
|
|
||||||
in_range :: proc(range: []rune, c: rune) -> bool #no_bounds_check {
|
|
||||||
for i := 0; range[i] != -1; i += 2 {
|
|
||||||
if range[i] <= c && c <= range[i+1] {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// [https://www.sigbus.info/n1570#D] C11 allows ASCII and some multibyte characters in certan Unicode ranges to be used in an identifier.
|
|
||||||
//
|
|
||||||
// is_ident0 returns true if a given character is acceptable as the first character of an identifier.
|
|
||||||
is_ident0 :: proc(c: rune) -> bool {
|
|
||||||
return in_range(_range_ident0, c)
|
|
||||||
}
|
|
||||||
// is_ident0 returns true if a given character is acceptable as a non-first character of an identifier.
|
|
||||||
is_ident1 :: proc(c: rune) -> bool {
|
|
||||||
return is_ident0(c) || in_range(_range_ident1, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the number of columns needed to display a given character in a fixed-width font.
|
|
||||||
// Based on https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
|
|
||||||
char_width :: proc(c: rune) -> int {
|
|
||||||
switch {
|
|
||||||
case in_range(_range_width0, c):
|
|
||||||
return 0
|
|
||||||
case in_range(_range_width2, c):
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
display_width :: proc(str: string) -> (w: int) {
|
|
||||||
for c in str {
|
|
||||||
w += char_width(c)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_range_ident0 := []rune{
|
|
||||||
'_', '_', 'a', 'z', 'A', 'Z', '$', '$',
|
|
||||||
0x00A8, 0x00A8, 0x00AA, 0x00AA, 0x00AD, 0x00AD, 0x00AF, 0x00AF,
|
|
||||||
0x00B2, 0x00B5, 0x00B7, 0x00BA, 0x00BC, 0x00BE, 0x00C0, 0x00D6,
|
|
||||||
0x00D8, 0x00F6, 0x00F8, 0x00FF, 0x0100, 0x02FF, 0x0370, 0x167F,
|
|
||||||
0x1681, 0x180D, 0x180F, 0x1DBF, 0x1E00, 0x1FFF, 0x200B, 0x200D,
|
|
||||||
0x202A, 0x202E, 0x203F, 0x2040, 0x2054, 0x2054, 0x2060, 0x206F,
|
|
||||||
0x2070, 0x20CF, 0x2100, 0x218F, 0x2460, 0x24FF, 0x2776, 0x2793,
|
|
||||||
0x2C00, 0x2DFF, 0x2E80, 0x2FFF, 0x3004, 0x3007, 0x3021, 0x302F,
|
|
||||||
0x3031, 0x303F, 0x3040, 0xD7FF, 0xF900, 0xFD3D, 0xFD40, 0xFDCF,
|
|
||||||
0xFDF0, 0xFE1F, 0xFE30, 0xFE44, 0xFE47, 0xFFFD,
|
|
||||||
0x10000, 0x1FFFD, 0x20000, 0x2FFFD, 0x30000, 0x3FFFD, 0x40000, 0x4FFFD,
|
|
||||||
0x50000, 0x5FFFD, 0x60000, 0x6FFFD, 0x70000, 0x7FFFD, 0x80000, 0x8FFFD,
|
|
||||||
0x90000, 0x9FFFD, 0xA0000, 0xAFFFD, 0xB0000, 0xBFFFD, 0xC0000, 0xCFFFD,
|
|
||||||
0xD0000, 0xDFFFD, 0xE0000, 0xEFFFD,
|
|
||||||
-1,
|
|
||||||
}
|
|
||||||
|
|
||||||
_range_ident1 := []rune{
|
|
||||||
'0', '9', '$', '$', 0x0300, 0x036F, 0x1DC0, 0x1DFF, 0x20D0, 0x20FF, 0xFE20, 0xFE2F,
|
|
||||||
-1,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
_range_width0 := []rune{
|
|
||||||
0x0000, 0x001F, 0x007f, 0x00a0, 0x0300, 0x036F, 0x0483, 0x0486,
|
|
||||||
0x0488, 0x0489, 0x0591, 0x05BD, 0x05BF, 0x05BF, 0x05C1, 0x05C2,
|
|
||||||
0x05C4, 0x05C5, 0x05C7, 0x05C7, 0x0600, 0x0603, 0x0610, 0x0615,
|
|
||||||
0x064B, 0x065E, 0x0670, 0x0670, 0x06D6, 0x06E4, 0x06E7, 0x06E8,
|
|
||||||
0x06EA, 0x06ED, 0x070F, 0x070F, 0x0711, 0x0711, 0x0730, 0x074A,
|
|
||||||
0x07A6, 0x07B0, 0x07EB, 0x07F3, 0x0901, 0x0902, 0x093C, 0x093C,
|
|
||||||
0x0941, 0x0948, 0x094D, 0x094D, 0x0951, 0x0954, 0x0962, 0x0963,
|
|
||||||
0x0981, 0x0981, 0x09BC, 0x09BC, 0x09C1, 0x09C4, 0x09CD, 0x09CD,
|
|
||||||
0x09E2, 0x09E3, 0x0A01, 0x0A02, 0x0A3C, 0x0A3C, 0x0A41, 0x0A42,
|
|
||||||
0x0A47, 0x0A48, 0x0A4B, 0x0A4D, 0x0A70, 0x0A71, 0x0A81, 0x0A82,
|
|
||||||
0x0ABC, 0x0ABC, 0x0AC1, 0x0AC5, 0x0AC7, 0x0AC8, 0x0ACD, 0x0ACD,
|
|
||||||
0x0AE2, 0x0AE3, 0x0B01, 0x0B01, 0x0B3C, 0x0B3C, 0x0B3F, 0x0B3F,
|
|
||||||
0x0B41, 0x0B43, 0x0B4D, 0x0B4D, 0x0B56, 0x0B56, 0x0B82, 0x0B82,
|
|
||||||
0x0BC0, 0x0BC0, 0x0BCD, 0x0BCD, 0x0C3E, 0x0C40, 0x0C46, 0x0C48,
|
|
||||||
0x0C4A, 0x0C4D, 0x0C55, 0x0C56, 0x0CBC, 0x0CBC, 0x0CBF, 0x0CBF,
|
|
||||||
0x0CC6, 0x0CC6, 0x0CCC, 0x0CCD, 0x0CE2, 0x0CE3, 0x0D41, 0x0D43,
|
|
||||||
0x0D4D, 0x0D4D, 0x0DCA, 0x0DCA, 0x0DD2, 0x0DD4, 0x0DD6, 0x0DD6,
|
|
||||||
0x0E31, 0x0E31, 0x0E34, 0x0E3A, 0x0E47, 0x0E4E, 0x0EB1, 0x0EB1,
|
|
||||||
0x0EB4, 0x0EB9, 0x0EBB, 0x0EBC, 0x0EC8, 0x0ECD, 0x0F18, 0x0F19,
|
|
||||||
0x0F35, 0x0F35, 0x0F37, 0x0F37, 0x0F39, 0x0F39, 0x0F71, 0x0F7E,
|
|
||||||
0x0F80, 0x0F84, 0x0F86, 0x0F87, 0x0F90, 0x0F97, 0x0F99, 0x0FBC,
|
|
||||||
0x0FC6, 0x0FC6, 0x102D, 0x1030, 0x1032, 0x1032, 0x1036, 0x1037,
|
|
||||||
0x1039, 0x1039, 0x1058, 0x1059, 0x1160, 0x11FF, 0x135F, 0x135F,
|
|
||||||
0x1712, 0x1714, 0x1732, 0x1734, 0x1752, 0x1753, 0x1772, 0x1773,
|
|
||||||
0x17B4, 0x17B5, 0x17B7, 0x17BD, 0x17C6, 0x17C6, 0x17C9, 0x17D3,
|
|
||||||
0x17DD, 0x17DD, 0x180B, 0x180D, 0x18A9, 0x18A9, 0x1920, 0x1922,
|
|
||||||
0x1927, 0x1928, 0x1932, 0x1932, 0x1939, 0x193B, 0x1A17, 0x1A18,
|
|
||||||
0x1B00, 0x1B03, 0x1B34, 0x1B34, 0x1B36, 0x1B3A, 0x1B3C, 0x1B3C,
|
|
||||||
0x1B42, 0x1B42, 0x1B6B, 0x1B73, 0x1DC0, 0x1DCA, 0x1DFE, 0x1DFF,
|
|
||||||
0x200B, 0x200F, 0x202A, 0x202E, 0x2060, 0x2063, 0x206A, 0x206F,
|
|
||||||
0x20D0, 0x20EF, 0x302A, 0x302F, 0x3099, 0x309A, 0xA806, 0xA806,
|
|
||||||
0xA80B, 0xA80B, 0xA825, 0xA826, 0xFB1E, 0xFB1E, 0xFE00, 0xFE0F,
|
|
||||||
0xFE20, 0xFE23, 0xFEFF, 0xFEFF, 0xFFF9, 0xFFFB, 0x10A01, 0x10A03,
|
|
||||||
0x10A05, 0x10A06, 0x10A0C, 0x10A0F, 0x10A38, 0x10A3A, 0x10A3F, 0x10A3F,
|
|
||||||
0x1D167, 0x1D169, 0x1D173, 0x1D182, 0x1D185, 0x1D18B, 0x1D1AA, 0x1D1AD,
|
|
||||||
0x1D242, 0x1D244, 0xE0001, 0xE0001, 0xE0020, 0xE007F, 0xE0100, 0xE01EF,
|
|
||||||
-1,
|
|
||||||
}
|
|
||||||
|
|
||||||
_range_width2 := []rune{
|
|
||||||
0x1100, 0x115F, 0x2329, 0x2329, 0x232A, 0x232A, 0x2E80, 0x303E,
|
|
||||||
0x3040, 0xA4CF, 0xAC00, 0xD7A3, 0xF900, 0xFAFF, 0xFE10, 0xFE19,
|
|
||||||
0xFE30, 0xFE6F, 0xFF00, 0xFF60, 0xFFE0, 0xFFE6, 0x1F000, 0x1F644,
|
|
||||||
0x20000, 0x2FFFD, 0x30000, 0x3FFFD,
|
|
||||||
-1,
|
|
||||||
}
|
|
||||||
@@ -14,7 +14,7 @@ The following is a mostly-complete projection of the C11 standard library as def
|
|||||||
| `<inttypes.h>` | Fully projected |
|
| `<inttypes.h>` | Fully projected |
|
||||||
| `<iso646.h>` | Not applicable, use Odin's operators |
|
| `<iso646.h>` | Not applicable, use Odin's operators |
|
||||||
| `<limits.h>` | Not projected |
|
| `<limits.h>` | Not projected |
|
||||||
| `<locale.h>` | Not projected |
|
| `<locale.h>` | Fully projected |
|
||||||
| `<math.h>` | Mostly projected, see [limitations](#Limitations) |
|
| `<math.h>` | Mostly projected, see [limitations](#Limitations) |
|
||||||
| `<setjmp.h>` | Fully projected |
|
| `<setjmp.h>` | Fully projected |
|
||||||
| `<signal.h>` | Fully projected |
|
| `<signal.h>` | Fully projected |
|
||||||
|
|||||||
@@ -47,8 +47,8 @@ foreign libc {
|
|||||||
clogf :: proc(z: complex_float) -> complex_float ---
|
clogf :: proc(z: complex_float) -> complex_float ---
|
||||||
|
|
||||||
// 7.3.8 Power and absolute-value functions
|
// 7.3.8 Power and absolute-value functions
|
||||||
cabs :: proc(z: complex_double) -> complex_double ---
|
cabs :: proc(z: complex_double) -> double ---
|
||||||
cabsf :: proc(z: complex_float) -> complex_float ---
|
cabsf :: proc(z: complex_float) -> float ---
|
||||||
cpow :: proc(x, y: complex_double) -> complex_double ---
|
cpow :: proc(x, y: complex_double) -> complex_double ---
|
||||||
cpowf :: proc(x, y: complex_float) -> complex_float ---
|
cpowf :: proc(x, y: complex_float) -> complex_float ---
|
||||||
csqrt :: proc(z: complex_double) -> complex_double ---
|
csqrt :: proc(z: complex_double) -> complex_double ---
|
||||||
@@ -67,7 +67,7 @@ foreign libc {
|
|||||||
crealf :: proc(z: complex_float) -> float ---
|
crealf :: proc(z: complex_float) -> float ---
|
||||||
}
|
}
|
||||||
|
|
||||||
import builtin "core:builtin"
|
import builtin "base:builtin"
|
||||||
|
|
||||||
complex_float :: distinct builtin.complex64
|
complex_float :: distinct builtin.complex64
|
||||||
complex_double :: distinct builtin.complex128
|
complex_double :: distinct builtin.complex128
|
||||||
|
|||||||
+28
-2
@@ -40,7 +40,7 @@ when ODIN_OS == .FreeBSD {
|
|||||||
ERANGE :: 34
|
ERANGE :: 34
|
||||||
}
|
}
|
||||||
|
|
||||||
when ODIN_OS == .OpenBSD {
|
when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
|
||||||
@(private="file")
|
@(private="file")
|
||||||
@(default_calling_convention="c")
|
@(default_calling_convention="c")
|
||||||
foreign libc {
|
foreign libc {
|
||||||
@@ -80,10 +80,36 @@ when ODIN_OS == .Darwin {
|
|||||||
ERANGE :: 34
|
ERANGE :: 34
|
||||||
}
|
}
|
||||||
|
|
||||||
|
when ODIN_OS == .Haiku {
|
||||||
|
@(private="file")
|
||||||
|
@(default_calling_convention="c")
|
||||||
|
foreign libc {
|
||||||
|
@(link_name="_errnop")
|
||||||
|
_get_errno :: proc() -> ^int ---
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
B_GENERAL_ERROR_BASE :: min(i32)
|
||||||
|
@(private="file")
|
||||||
|
B_POSIX_ERROR_BASE :: B_GENERAL_ERROR_BASE + 0x7000
|
||||||
|
|
||||||
|
EDOM :: B_POSIX_ERROR_BASE + 16
|
||||||
|
EILSEQ :: B_POSIX_ERROR_BASE + 38
|
||||||
|
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
|
// 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
|
// 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
|
// error value to errno. To work around this, just expose it as a function like
|
||||||
// it actually is.
|
// it actually is.
|
||||||
errno :: #force_inline proc() -> ^int {
|
errno :: #force_inline proc "contextless" () -> ^int {
|
||||||
return _get_errno()
|
return _get_errno()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,133 @@
|
|||||||
|
package libc
|
||||||
|
|
||||||
|
import "core:c"
|
||||||
|
|
||||||
|
when ODIN_OS == .Windows {
|
||||||
|
foreign import libc "system:libucrt.lib"
|
||||||
|
} else when ODIN_OS == .Darwin {
|
||||||
|
foreign import libc "system:System.framework"
|
||||||
|
} else {
|
||||||
|
foreign import libc "system:c"
|
||||||
|
}
|
||||||
|
|
||||||
|
// locale.h - category macros
|
||||||
|
|
||||||
|
foreign libc {
|
||||||
|
/*
|
||||||
|
Sets the components of an object with the type lconv with the values appropriate for the
|
||||||
|
formatting of numeric quantities (monetary and otherwise) according to the rules of the current
|
||||||
|
locale.
|
||||||
|
|
||||||
|
Returns: a pointer to the lconv structure, might be invalidated by subsequent calls to localeconv() and setlocale()
|
||||||
|
|
||||||
|
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/localeconv.html ]]
|
||||||
|
*/
|
||||||
|
localeconv :: proc() -> ^lconv ---
|
||||||
|
|
||||||
|
/*
|
||||||
|
Selects the appropriate piece of the global locale, as specified by the category and locale arguments,
|
||||||
|
and can be used to change or query the entire global locale or portions thereof.
|
||||||
|
|
||||||
|
Returns: the current locale if `locale` is `nil`, the set locale otherwise
|
||||||
|
|
||||||
|
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setlocale.html ]]
|
||||||
|
*/
|
||||||
|
@(link_name=LSETLOCALE)
|
||||||
|
setlocale :: proc(category: Locale_Category, locale: cstring) -> cstring ---
|
||||||
|
}
|
||||||
|
|
||||||
|
Locale_Category :: enum c.int {
|
||||||
|
ALL = LC_ALL,
|
||||||
|
COLLATE = LC_COLLATE,
|
||||||
|
CTYPE = LC_CTYPE,
|
||||||
|
MESSAGES = LC_MESSAGES,
|
||||||
|
MONETARY = LC_MONETARY,
|
||||||
|
NUMERIC = LC_NUMERIC,
|
||||||
|
TIME = LC_TIME,
|
||||||
|
}
|
||||||
|
|
||||||
|
when ODIN_OS == .NetBSD {
|
||||||
|
@(private) LSETLOCALE :: "__setlocale50"
|
||||||
|
} else {
|
||||||
|
@(private) LSETLOCALE :: "setlocale"
|
||||||
|
}
|
||||||
|
|
||||||
|
when ODIN_OS == .Windows {
|
||||||
|
lconv :: struct {
|
||||||
|
decimal_point: cstring,
|
||||||
|
thousand_sep: cstring,
|
||||||
|
grouping: cstring,
|
||||||
|
int_curr_symbol: cstring,
|
||||||
|
currency_symbol: cstring,
|
||||||
|
mon_decimal_points: cstring,
|
||||||
|
mon_thousands_sep: cstring,
|
||||||
|
mon_grouping: cstring,
|
||||||
|
positive_sign: cstring,
|
||||||
|
negative_sign: cstring,
|
||||||
|
int_frac_digits: c.char,
|
||||||
|
frac_digits: c.char,
|
||||||
|
p_cs_precedes: c.char,
|
||||||
|
p_sep_by_space: c.char,
|
||||||
|
n_cs_precedes: c.char,
|
||||||
|
n_sep_by_space: c.char,
|
||||||
|
p_sign_posn: c.char,
|
||||||
|
n_sign_posn: c.char,
|
||||||
|
_W_decimal_point: [^]u16 `fmt:"s,0"`,
|
||||||
|
_W_thousands_sep: [^]u16 `fmt:"s,0"`,
|
||||||
|
_W_int_curr_symbol: [^]u16 `fmt:"s,0"`,
|
||||||
|
_W_currency_symbol: [^]u16 `fmt:"s,0"`,
|
||||||
|
_W_mon_decimal_point: [^]u16 `fmt:"s,0"`,
|
||||||
|
_W_mon_thousands_sep: [^]u16 `fmt:"s,0"`,
|
||||||
|
_W_positive_sign: [^]u16 `fmt:"s,0"`,
|
||||||
|
_W_negative_sign: [^]u16 `fmt:"s,0"`,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lconv :: struct {
|
||||||
|
decimal_point: cstring,
|
||||||
|
thousand_sep: cstring,
|
||||||
|
grouping: cstring,
|
||||||
|
int_curr_symbol: cstring,
|
||||||
|
currency_symbol: cstring,
|
||||||
|
mon_decimal_points: cstring,
|
||||||
|
mon_thousands_sep: cstring,
|
||||||
|
mon_grouping: cstring,
|
||||||
|
positive_sign: cstring,
|
||||||
|
negative_sign: cstring,
|
||||||
|
int_frac_digits: c.char,
|
||||||
|
frac_digits: c.char,
|
||||||
|
p_cs_precedes: c.char,
|
||||||
|
p_sep_by_space: c.char,
|
||||||
|
n_cs_precedes: c.char,
|
||||||
|
n_sep_by_space: c.char,
|
||||||
|
p_sign_posn: c.char,
|
||||||
|
n_sign_posn: c.char,
|
||||||
|
_int_p_cs_precedes: c.char,
|
||||||
|
_int_n_cs_precedes: c.char,
|
||||||
|
_int_p_sep_by_space: c.char,
|
||||||
|
_int_n_sep_by_space: c.char,
|
||||||
|
_int_p_sign_posn: c.char,
|
||||||
|
_int_n_sign_posn: c.char,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Windows {
|
||||||
|
|
||||||
|
LC_ALL :: 0
|
||||||
|
LC_COLLATE :: 1
|
||||||
|
LC_CTYPE :: 2
|
||||||
|
LC_MESSAGES :: 6
|
||||||
|
LC_MONETARY :: 3
|
||||||
|
LC_NUMERIC :: 4
|
||||||
|
LC_TIME :: 5
|
||||||
|
|
||||||
|
} else when ODIN_OS == .Linux {
|
||||||
|
|
||||||
|
LC_CTYPE :: 0
|
||||||
|
LC_NUMERIC :: 1
|
||||||
|
LC_TIME :: 2
|
||||||
|
LC_COLLATE :: 3
|
||||||
|
LC_MONETARY :: 4
|
||||||
|
LC_MESSAGES :: 5
|
||||||
|
LC_ALL :: 6
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ package libc
|
|||||||
|
|
||||||
// 7.12 Mathematics
|
// 7.12 Mathematics
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
when ODIN_OS == .Windows {
|
when ODIN_OS == .Windows {
|
||||||
foreign import libc "system:libucrt.lib"
|
foreign import libc "system:libucrt.lib"
|
||||||
|
|||||||
+12
-7
@@ -32,24 +32,21 @@ when ODIN_OS == .Windows {
|
|||||||
// the RDX register will contain zero and correctly set the flag to disable
|
// the RDX register will contain zero and correctly set the flag to disable
|
||||||
// stack unwinding.
|
// stack unwinding.
|
||||||
@(link_name="_setjmp")
|
@(link_name="_setjmp")
|
||||||
setjmp :: proc(env: ^jmp_buf, hack: rawptr = nil) -> int ---
|
setjmp :: proc(env: ^jmp_buf, hack: rawptr = nil) -> int ---
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@(default_calling_convention="c")
|
@(default_calling_convention="c")
|
||||||
foreign libc {
|
foreign libc {
|
||||||
// 7.13.1 Save calling environment
|
// 7.13.1 Save calling environment
|
||||||
//
|
@(link_name=LSETJMP)
|
||||||
// NOTE(dweiler): C11 requires setjmp be a macro, which means it won't
|
setjmp :: proc(env: ^jmp_buf) -> int ---
|
||||||
// necessarily export a symbol named setjmp but rather _setjmp in the case
|
|
||||||
// of musl, glibc, BSD libc, and msvcrt.
|
|
||||||
@(link_name="_setjmp")
|
|
||||||
setjmp :: proc(env: ^jmp_buf) -> int ---
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(default_calling_convention="c")
|
@(default_calling_convention="c")
|
||||||
foreign libc {
|
foreign libc {
|
||||||
// 7.13.2 Restore calling environment
|
// 7.13.2 Restore calling environment
|
||||||
|
@(link_name=LLONGJMP)
|
||||||
longjmp :: proc(env: ^jmp_buf, val: int) -> ! ---
|
longjmp :: proc(env: ^jmp_buf, val: int) -> ! ---
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,3 +61,11 @@ foreign libc {
|
|||||||
// The choice of 4096 bytes for storage of this type is more than enough on all
|
// The choice of 4096 bytes for storage of this type is more than enough on all
|
||||||
// relevant platforms.
|
// relevant platforms.
|
||||||
jmp_buf :: struct #align(16) { _: [4096]char, }
|
jmp_buf :: struct #align(16) { _: [4096]char, }
|
||||||
|
|
||||||
|
when ODIN_OS == .NetBSD {
|
||||||
|
@(private) LSETJMP :: "__setjmp14"
|
||||||
|
@(private) LLONGJMP :: "__longjmp14"
|
||||||
|
} else {
|
||||||
|
@(private) LSETJMP :: "setjmp"
|
||||||
|
@(private) LLONGJMP :: "longjmp"
|
||||||
|
}
|
||||||
|
|||||||
+1
-14
@@ -34,20 +34,7 @@ when ODIN_OS == .Windows {
|
|||||||
SIGTERM :: 15
|
SIGTERM :: 15
|
||||||
}
|
}
|
||||||
|
|
||||||
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD {
|
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Haiku || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Darwin {
|
||||||
SIG_ERR :: rawptr(~uintptr(0))
|
|
||||||
SIG_DFL :: rawptr(uintptr(0))
|
|
||||||
SIG_IGN :: rawptr(uintptr(1))
|
|
||||||
|
|
||||||
SIGABRT :: 6
|
|
||||||
SIGFPE :: 8
|
|
||||||
SIGILL :: 4
|
|
||||||
SIGINT :: 2
|
|
||||||
SIGSEGV :: 11
|
|
||||||
SIGTERM :: 15
|
|
||||||
}
|
|
||||||
|
|
||||||
when ODIN_OS == .Darwin {
|
|
||||||
SIG_ERR :: rawptr(~uintptr(0))
|
SIG_ERR :: rawptr(~uintptr(0))
|
||||||
SIG_DFL :: rawptr(uintptr(0))
|
SIG_DFL :: rawptr(uintptr(0))
|
||||||
SIG_IGN :: rawptr(uintptr(1))
|
SIG_IGN :: rawptr(uintptr(1))
|
||||||
|
|||||||
+4
-10
@@ -2,7 +2,9 @@ package libc
|
|||||||
|
|
||||||
// 7.16 Variable arguments
|
// 7.16 Variable arguments
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
|
import "core:c"
|
||||||
|
|
||||||
@(private="file")
|
@(private="file")
|
||||||
@(default_calling_convention="none")
|
@(default_calling_convention="none")
|
||||||
@@ -12,15 +14,7 @@ foreign _ {
|
|||||||
@(link_name="llvm.va_copy") _va_copy :: proc(dst, src: ^i8) ---
|
@(link_name="llvm.va_copy") _va_copy :: proc(dst, src: ^i8) ---
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since there are no types in C with an alignment larger than that of
|
va_list :: c.va_list
|
||||||
// max_align_t, which cannot be larger than sizeof(long double) as any other
|
|
||||||
// exposed type wouldn't be valid C, the maximum alignment possible in a
|
|
||||||
// strictly conformant C implementation is 16 on the platforms we care about.
|
|
||||||
// The choice of 4096 bytes for storage of this type is more than enough on all
|
|
||||||
// relevant platforms.
|
|
||||||
va_list :: struct #align(16) {
|
|
||||||
_: [4096]u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
va_start :: #force_inline proc(ap: ^va_list, _: any) {
|
va_start :: #force_inline proc(ap: ^va_list, _: any) {
|
||||||
_va_start(cast(^i8)ap)
|
_va_start(cast(^i8)ap)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package libc
|
|||||||
|
|
||||||
// 7.17 Atomics
|
// 7.17 Atomics
|
||||||
|
|
||||||
import "core:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
ATOMIC_BOOL_LOCK_FREE :: true
|
ATOMIC_BOOL_LOCK_FREE :: true
|
||||||
ATOMIC_CHAR_LOCK_FREE :: true
|
ATOMIC_CHAR_LOCK_FREE :: true
|
||||||
@@ -235,7 +235,7 @@ atomic_compare_exchange_weak :: #force_inline proc(object, expected: ^$T, desire
|
|||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic_compare_exchange_weak_explicit :: #force_inline proc(object, expected: ^$T, desited: T, success, failure: memory_order) -> bool {
|
atomic_compare_exchange_weak_explicit :: #force_inline proc(object, expected: ^$T, desired: T, success, failure: memory_order) -> bool {
|
||||||
assert(failure != .release)
|
assert(failure != .release)
|
||||||
assert(failure != .acq_rel)
|
assert(failure != .acq_rel)
|
||||||
|
|
||||||
|
|||||||
+192
-8
@@ -1,5 +1,7 @@
|
|||||||
package libc
|
package libc
|
||||||
|
|
||||||
|
import "core:io"
|
||||||
|
|
||||||
when ODIN_OS == .Windows {
|
when ODIN_OS == .Windows {
|
||||||
foreign import libc {
|
foreign import libc {
|
||||||
"system:libucrt.lib",
|
"system:libucrt.lib",
|
||||||
@@ -15,6 +17,12 @@ when ODIN_OS == .Windows {
|
|||||||
|
|
||||||
FILE :: struct {}
|
FILE :: struct {}
|
||||||
|
|
||||||
|
Whence :: enum int {
|
||||||
|
SET = SEEK_SET,
|
||||||
|
CUR = SEEK_CUR,
|
||||||
|
END = SEEK_END,
|
||||||
|
}
|
||||||
|
|
||||||
// MSVCRT compatible.
|
// MSVCRT compatible.
|
||||||
when ODIN_OS == .Windows {
|
when ODIN_OS == .Windows {
|
||||||
_IOFBF :: 0x0000
|
_IOFBF :: 0x0000
|
||||||
@@ -81,7 +89,31 @@ when ODIN_OS == .Linux {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
when ODIN_OS == .OpenBSD {
|
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
|
fpos_t :: distinct i64
|
||||||
|
|
||||||
_IOFBF :: 0
|
_IOFBF :: 0
|
||||||
@@ -99,11 +131,15 @@ when ODIN_OS == .OpenBSD {
|
|||||||
SEEK_CUR :: 1
|
SEEK_CUR :: 1
|
||||||
SEEK_END :: 2
|
SEEK_END :: 2
|
||||||
|
|
||||||
|
TMP_MAX :: 308915776
|
||||||
|
|
||||||
foreign libc {
|
foreign libc {
|
||||||
stderr: ^FILE
|
__sF: [3]FILE
|
||||||
stdin: ^FILE
|
|
||||||
stdout: ^FILE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stdin: ^FILE = &__sF[0]
|
||||||
|
stdout: ^FILE = &__sF[1]
|
||||||
|
stderr: ^FILE = &__sF[2]
|
||||||
}
|
}
|
||||||
|
|
||||||
when ODIN_OS == .FreeBSD {
|
when ODIN_OS == .FreeBSD {
|
||||||
@@ -124,10 +160,12 @@ when ODIN_OS == .FreeBSD {
|
|||||||
SEEK_CUR :: 1
|
SEEK_CUR :: 1
|
||||||
SEEK_END :: 2
|
SEEK_END :: 2
|
||||||
|
|
||||||
|
TMP_MAX :: 308915776
|
||||||
|
|
||||||
foreign libc {
|
foreign libc {
|
||||||
stderr: ^FILE
|
@(link_name="__stderrp") stderr: ^FILE
|
||||||
stdin: ^FILE
|
@(link_name="__stdinp") stdin: ^FILE
|
||||||
stdout: ^FILE
|
@(link_name="__stdoutp") stdout: ^FILE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,10 +199,51 @@ when ODIN_OS == .Darwin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
when ODIN_OS == .Haiku {
|
||||||
|
fpos_t :: distinct i64
|
||||||
|
|
||||||
|
_IOFBF :: 0
|
||||||
|
_IOLBF :: 1
|
||||||
|
_IONBF :: 2
|
||||||
|
|
||||||
|
BUFSIZ :: 8192
|
||||||
|
|
||||||
|
EOF :: int(-1)
|
||||||
|
|
||||||
|
FOPEN_MAX :: 128
|
||||||
|
|
||||||
|
FILENAME_MAX :: 256
|
||||||
|
|
||||||
|
L_tmpnam :: 512
|
||||||
|
|
||||||
|
SEEK_SET :: 0
|
||||||
|
SEEK_CUR :: 1
|
||||||
|
SEEK_END :: 2
|
||||||
|
|
||||||
|
TMP_MAX :: 32768
|
||||||
|
|
||||||
|
foreign libc {
|
||||||
|
stderr: ^FILE
|
||||||
|
stdin: ^FILE
|
||||||
|
stdout: ^FILE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when ODIN_OS == .NetBSD {
|
||||||
|
@(private) LRENAME :: "__posix_rename"
|
||||||
|
@(private) LFGETPOS :: "__fgetpos50"
|
||||||
|
@(private) LFSETPOS :: "__fsetpos50"
|
||||||
|
} else {
|
||||||
|
@(private) LRENAME :: "rename"
|
||||||
|
@(private) LFGETPOS :: "fgetpos"
|
||||||
|
@(private) LFSETPOS :: "fsetpos"
|
||||||
|
}
|
||||||
|
|
||||||
@(default_calling_convention="c")
|
@(default_calling_convention="c")
|
||||||
foreign libc {
|
foreign libc {
|
||||||
// 7.21.4 Operations on files
|
// 7.21.4 Operations on files
|
||||||
remove :: proc(filename: cstring) -> int ---
|
remove :: proc(filename: cstring) -> int ---
|
||||||
|
@(link_name=LRENAME)
|
||||||
rename :: proc(old, new: cstring) -> int ---
|
rename :: proc(old, new: cstring) -> int ---
|
||||||
tmpfile :: proc() -> ^FILE ---
|
tmpfile :: proc() -> ^FILE ---
|
||||||
tmpnam :: proc(s: [^]char) -> [^]char ---
|
tmpnam :: proc(s: [^]char) -> [^]char ---
|
||||||
@@ -206,8 +285,10 @@ foreign libc {
|
|||||||
fwrite :: proc(ptr: rawptr, size: size_t, nmemb: size_t, stream: ^FILE) -> size_t ---
|
fwrite :: proc(ptr: rawptr, size: size_t, nmemb: size_t, stream: ^FILE) -> size_t ---
|
||||||
|
|
||||||
// 7.21.9 File positioning functions
|
// 7.21.9 File positioning functions
|
||||||
|
@(link_name=LFGETPOS)
|
||||||
fgetpos :: proc(stream: ^FILE, pos: ^fpos_t) -> int ---
|
fgetpos :: proc(stream: ^FILE, pos: ^fpos_t) -> int ---
|
||||||
fseek :: proc(stream: ^FILE, offset: long, whence: int) -> int ---
|
fseek :: proc(stream: ^FILE, offset: long, whence: Whence) -> int ---
|
||||||
|
@(link_name=LFSETPOS)
|
||||||
fsetpos :: proc(stream: ^FILE, pos: ^fpos_t) -> int ---
|
fsetpos :: proc(stream: ^FILE, pos: ^fpos_t) -> int ---
|
||||||
ftell :: proc(stream: ^FILE) -> long ---
|
ftell :: proc(stream: ^FILE) -> long ---
|
||||||
rewind :: proc(stream: ^FILE) ---
|
rewind :: proc(stream: ^FILE) ---
|
||||||
@@ -218,3 +299,106 @@ foreign libc {
|
|||||||
ferror :: proc(stream: ^FILE) -> int ---
|
ferror :: proc(stream: ^FILE) -> int ---
|
||||||
perror :: proc(s: cstring) ---
|
perror :: proc(s: cstring) ---
|
||||||
}
|
}
|
||||||
|
|
||||||
|
to_stream :: proc(file: ^FILE) -> io.Stream {
|
||||||
|
stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
|
||||||
|
unknown_or_eof :: proc(f: ^FILE) -> io.Error {
|
||||||
|
switch {
|
||||||
|
case ferror(f) != 0:
|
||||||
|
return .Unknown
|
||||||
|
case feof(f) != 0:
|
||||||
|
return .EOF
|
||||||
|
case:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file := (^FILE)(stream_data)
|
||||||
|
switch mode {
|
||||||
|
case .Close:
|
||||||
|
if fclose(file) != 0 {
|
||||||
|
return 0, unknown_or_eof(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
case .Flush:
|
||||||
|
if fflush(file) != 0 {
|
||||||
|
return 0, unknown_or_eof(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
case .Read:
|
||||||
|
n = i64(fread(raw_data(p), size_of(byte), len(p), file))
|
||||||
|
if n == 0 { err = unknown_or_eof(file) }
|
||||||
|
|
||||||
|
case .Read_At:
|
||||||
|
curr := ftell(file)
|
||||||
|
if curr == -1 {
|
||||||
|
return 0, unknown_or_eof(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fseek(file, long(offset), .SET) != 0 {
|
||||||
|
return 0, unknown_or_eof(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer fseek(file, long(curr), .SET)
|
||||||
|
|
||||||
|
n = i64(fread(raw_data(p), size_of(byte), len(p), file))
|
||||||
|
if n == 0 { err = unknown_or_eof(file) }
|
||||||
|
|
||||||
|
case .Write:
|
||||||
|
n = i64(fwrite(raw_data(p), size_of(byte), len(p), file))
|
||||||
|
if n == 0 { err = unknown_or_eof(file) }
|
||||||
|
|
||||||
|
case .Write_At:
|
||||||
|
curr := ftell(file)
|
||||||
|
if curr == -1 {
|
||||||
|
return 0, unknown_or_eof(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fseek(file, long(offset), .SET) != 0 {
|
||||||
|
return 0, unknown_or_eof(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer fseek(file, long(curr), .SET)
|
||||||
|
|
||||||
|
n = i64(fwrite(raw_data(p), size_of(byte), len(p), file))
|
||||||
|
if n == 0 { err = unknown_or_eof(file) }
|
||||||
|
|
||||||
|
case .Seek:
|
||||||
|
#assert(int(Whence.SET) == int(io.Seek_From.Start))
|
||||||
|
#assert(int(Whence.CUR) == int(io.Seek_From.Current))
|
||||||
|
#assert(int(Whence.END) == int(io.Seek_From.End))
|
||||||
|
|
||||||
|
if fseek(file, long(offset), Whence(whence)) != 0 {
|
||||||
|
return 0, unknown_or_eof(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
case .Size:
|
||||||
|
curr := ftell(file)
|
||||||
|
if curr == -1 {
|
||||||
|
return 0, unknown_or_eof(file)
|
||||||
|
}
|
||||||
|
defer fseek(file, curr, .SET)
|
||||||
|
|
||||||
|
if fseek(file, 0, .END) != 0 {
|
||||||
|
return 0, unknown_or_eof(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
n = i64(ftell(file))
|
||||||
|
if n == -1 {
|
||||||
|
return 0, unknown_or_eof(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
case .Destroy:
|
||||||
|
return 0, .Empty
|
||||||
|
|
||||||
|
case .Query:
|
||||||
|
return io.query_utility({ .Close, .Flush, .Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Query })
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
data = file,
|
||||||
|
procedure = stream_proc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+26
-3
@@ -10,6 +10,9 @@ when ODIN_OS == .Windows {
|
|||||||
foreign import libc "system:c"
|
foreign import libc "system:c"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require)
|
||||||
|
import "base:runtime"
|
||||||
|
|
||||||
when ODIN_OS == .Windows {
|
when ODIN_OS == .Windows {
|
||||||
RAND_MAX :: 0x7fff
|
RAND_MAX :: 0x7fff
|
||||||
|
|
||||||
@@ -40,10 +43,9 @@ when ODIN_OS == .Linux {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
when ODIN_OS == .Darwin {
|
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD {
|
||||||
RAND_MAX :: 0x7fffffff
|
RAND_MAX :: 0x7fffffff
|
||||||
|
|
||||||
// GLIBC and MUSL only
|
|
||||||
@(private="file")
|
@(private="file")
|
||||||
@(default_calling_convention="c")
|
@(default_calling_convention="c")
|
||||||
foreign libc {
|
foreign libc {
|
||||||
@@ -55,6 +57,20 @@ when ODIN_OS == .Darwin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
when ODIN_OS == .NetBSD {
|
||||||
|
RAND_MAX :: 0x7fffffff
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
@(default_calling_convention="c")
|
||||||
|
foreign libc {
|
||||||
|
__mb_cur_max: size_t
|
||||||
|
}
|
||||||
|
|
||||||
|
MB_CUR_MAX :: #force_inline proc() -> size_t {
|
||||||
|
return __mb_cur_max
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// C does not declare what these values should be, as an implementation is free
|
// C does not declare what these values should be, as an implementation is free
|
||||||
// to use any two distinct values it wants to indicate success or failure.
|
// to use any two distinct values it wants to indicate success or failure.
|
||||||
// However, nobody actually does and everyone appears to have agreed upon these
|
// However, nobody actually does and everyone appears to have agreed upon these
|
||||||
@@ -99,7 +115,7 @@ foreign libc {
|
|||||||
at_quick_exit :: proc(func: proc "c" ()) -> int ---
|
at_quick_exit :: proc(func: proc "c" ()) -> int ---
|
||||||
exit :: proc(status: int) -> ! ---
|
exit :: proc(status: int) -> ! ---
|
||||||
_Exit :: proc(status: int) -> ! ---
|
_Exit :: proc(status: int) -> ! ---
|
||||||
getenv :: proc(name: cstring) -> [^]char ---
|
getenv :: proc(name: cstring) -> cstring ---
|
||||||
quick_exit :: proc(status: int) -> ! ---
|
quick_exit :: proc(status: int) -> ! ---
|
||||||
system :: proc(cmd: cstring) -> int ---
|
system :: proc(cmd: cstring) -> int ---
|
||||||
|
|
||||||
@@ -132,6 +148,10 @@ aligned_alloc :: #force_inline proc "c" (alignment, size: size_t) -> rawptr {
|
|||||||
_aligned_malloc :: proc(size, alignment: size_t) -> rawptr ---
|
_aligned_malloc :: proc(size, alignment: size_t) -> rawptr ---
|
||||||
}
|
}
|
||||||
return _aligned_malloc(size=size, alignment=alignment)
|
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 {
|
} else {
|
||||||
foreign libc {
|
foreign libc {
|
||||||
aligned_alloc :: proc(alignment, size: size_t) -> rawptr ---
|
aligned_alloc :: proc(alignment, size: size_t) -> rawptr ---
|
||||||
@@ -147,6 +167,9 @@ aligned_free :: #force_inline proc "c" (ptr: rawptr) {
|
|||||||
_aligned_free :: proc(ptr: rawptr) ---
|
_aligned_free :: proc(ptr: rawptr) ---
|
||||||
}
|
}
|
||||||
_aligned_free(ptr)
|
_aligned_free(ptr)
|
||||||
|
} else when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 {
|
||||||
|
context = runtime.default_context()
|
||||||
|
runtime.mem_free(ptr)
|
||||||
} else {
|
} else {
|
||||||
free(ptr)
|
free(ptr)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package libc
|
package libc
|
||||||
|
|
||||||
import "core:runtime"
|
import "base:runtime"
|
||||||
|
|
||||||
// 7.24 String handling
|
// 7.24 String handling
|
||||||
|
|
||||||
@@ -12,6 +12,7 @@ when ODIN_OS == .Windows {
|
|||||||
foreign import libc "system:c"
|
foreign import libc "system:c"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(default_calling_convention="c")
|
||||||
foreign libc {
|
foreign libc {
|
||||||
// 7.24.2 Copying functions
|
// 7.24.2 Copying functions
|
||||||
memcpy :: proc(s1, s2: rawptr, n: size_t) -> rawptr ---
|
memcpy :: proc(s1, s2: rawptr, n: size_t) -> rawptr ---
|
||||||
@@ -40,7 +41,7 @@ foreign libc {
|
|||||||
strtok :: proc(s1: [^]char, s2: cstring) -> [^]char ---
|
strtok :: proc(s1: [^]char, s2: cstring) -> [^]char ---
|
||||||
|
|
||||||
// 7.24.6 Miscellaneous functions
|
// 7.24.6 Miscellaneous functions
|
||||||
strerror :: proc(errnum: int) -> [^]char ---
|
strerror :: proc(errnum: int) -> cstring ---
|
||||||
strlen :: proc(s: cstring) -> size_t ---
|
strlen :: proc(s: cstring) -> size_t ---
|
||||||
}
|
}
|
||||||
memset :: proc "c" (s: rawptr, c: int, n: size_t) -> rawptr {
|
memset :: proc "c" (s: rawptr, c: int, n: size_t) -> rawptr {
|
||||||
|
|||||||
+30
-4
@@ -45,35 +45,61 @@ when ODIN_OS == .Windows {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD {
|
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")
|
@(default_calling_convention="c")
|
||||||
foreign libc {
|
foreign libc {
|
||||||
// 7.27.2 Time manipulation functions
|
// 7.27.2 Time manipulation functions
|
||||||
clock :: proc() -> clock_t ---
|
clock :: proc() -> clock_t ---
|
||||||
|
@(link_name=LDIFFTIME)
|
||||||
difftime :: proc(time1, time2: time_t) -> double ---
|
difftime :: proc(time1, time2: time_t) -> double ---
|
||||||
|
@(link_name=LMKTIME)
|
||||||
mktime :: proc(timeptr: ^tm) -> time_t ---
|
mktime :: proc(timeptr: ^tm) -> time_t ---
|
||||||
|
@(link_name=LTIME)
|
||||||
time :: proc(timer: ^time_t) -> time_t ---
|
time :: proc(timer: ^time_t) -> time_t ---
|
||||||
timespec_get :: proc(ts: ^timespec, base: int) -> int ---
|
timespec_get :: proc(ts: ^timespec, base: int) -> int ---
|
||||||
|
|
||||||
// 7.27.3 Time conversion functions
|
// 7.27.3 Time conversion functions
|
||||||
asctime :: proc(timeptr: ^tm) -> [^]char ---
|
asctime :: proc(timeptr: ^tm) -> [^]char ---
|
||||||
|
@(link_name=LCTIME)
|
||||||
ctime :: proc(timer: ^time_t) -> [^]char ---
|
ctime :: proc(timer: ^time_t) -> [^]char ---
|
||||||
|
@(link_name=LGMTIME)
|
||||||
gmtime :: proc(timer: ^time_t) -> ^tm ---
|
gmtime :: proc(timer: ^time_t) -> ^tm ---
|
||||||
|
@(link_name=LLOCALTIME)
|
||||||
localtime :: proc(timer: ^time_t) -> ^tm ---
|
localtime :: proc(timer: ^time_t) -> ^tm ---
|
||||||
strftime :: proc(s: [^]char, maxsize: size_t, format: cstring, timeptr: ^tm) -> size_t ---
|
strftime :: proc(s: [^]char, maxsize: size_t, format: cstring, timeptr: ^tm) -> size_t ---
|
||||||
}
|
}
|
||||||
|
|
||||||
|
when ODIN_OS == .NetBSD {
|
||||||
|
@(private) LDIFFTIME :: "__difftime50"
|
||||||
|
@(private) LMKTIME :: "__mktime50"
|
||||||
|
@(private) LTIME :: "__time50"
|
||||||
|
@(private) LCTIME :: "__ctime50"
|
||||||
|
@(private) LGMTIME :: "__gmtime50"
|
||||||
|
@(private) LLOCALTIME :: "__localtime50"
|
||||||
|
} else {
|
||||||
|
@(private) LDIFFTIME :: "difftime"
|
||||||
|
@(private) LMKTIME :: "mktime"
|
||||||
|
@(private) LTIME :: "time"
|
||||||
|
@(private) LCTIME :: "ctime"
|
||||||
|
@(private) LGMTIME :: "gmtime"
|
||||||
|
@(private) LLOCALTIME :: "localtime"
|
||||||
|
}
|
||||||
|
|
||||||
when ODIN_OS == .OpenBSD {
|
when ODIN_OS == .OpenBSD {
|
||||||
CLOCKS_PER_SEC :: 100
|
CLOCKS_PER_SEC :: 100
|
||||||
} else {
|
} else {
|
||||||
CLOCKS_PER_SEC :: 1000000
|
CLOCKS_PER_SEC :: 1000000
|
||||||
}
|
}
|
||||||
|
|
||||||
TIME_UTC :: 1
|
TIME_UTC :: 1
|
||||||
|
|
||||||
time_t :: distinct i64
|
time_t :: distinct i64
|
||||||
|
|
||||||
clock_t :: long
|
when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD {
|
||||||
|
clock_t :: distinct int32_t
|
||||||
|
} else {
|
||||||
|
clock_t :: distinct long
|
||||||
|
}
|
||||||
|
|
||||||
timespec :: struct {
|
timespec :: struct {
|
||||||
tv_sec: time_t,
|
tv_sec: time_t,
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package libc
|
|||||||
|
|
||||||
import "core:c"
|
import "core:c"
|
||||||
|
|
||||||
|
#assert(!ODIN_NO_CRT, `"core:c/libc" cannot be imported when '-no-crt' is used`)
|
||||||
|
|
||||||
char :: c.char // assuming -funsigned-char
|
char :: c.char // assuming -funsigned-char
|
||||||
|
|
||||||
schar :: c.schar
|
schar :: c.schar
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ when ODIN_OS == .Windows {
|
|||||||
wctrans_t :: distinct wchar_t
|
wctrans_t :: distinct wchar_t
|
||||||
wctype_t :: distinct ushort
|
wctype_t :: distinct ushort
|
||||||
|
|
||||||
} else when ODIN_OS == .Linux {
|
} else when ODIN_OS == .Linux || ODIN_OS == .JS {
|
||||||
wctrans_t :: distinct intptr_t
|
wctrans_t :: distinct intptr_t
|
||||||
wctype_t :: distinct ulong
|
wctype_t :: distinct ulong
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ when ODIN_OS == .Windows {
|
|||||||
wctrans_t :: distinct int
|
wctrans_t :: distinct int
|
||||||
wctype_t :: distinct u32
|
wctype_t :: distinct u32
|
||||||
|
|
||||||
} else when ODIN_OS == .OpenBSD {
|
} else when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
|
||||||
wctrans_t :: distinct rawptr
|
wctrans_t :: distinct rawptr
|
||||||
wctype_t :: distinct rawptr
|
wctype_t :: distinct rawptr
|
||||||
|
|
||||||
@@ -30,6 +30,10 @@ when ODIN_OS == .Windows {
|
|||||||
wctrans_t :: distinct int
|
wctrans_t :: distinct int
|
||||||
wctype_t :: distinct ulong
|
wctype_t :: distinct ulong
|
||||||
|
|
||||||
|
} else when ODIN_OS == .Haiku {
|
||||||
|
wctrans_t :: distinct i32
|
||||||
|
wctype_t :: distinct i32
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@(default_calling_convention="c")
|
@(default_calling_convention="c")
|
||||||
|
|||||||
+56
-66
@@ -12,7 +12,7 @@ package compress
|
|||||||
|
|
||||||
import "core:io"
|
import "core:io"
|
||||||
import "core:bytes"
|
import "core:bytes"
|
||||||
import "core:runtime"
|
import "base:runtime"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
These settings bound how much compression algorithms will allocate for their output buffer.
|
These settings bound how much compression algorithms will allocate for their output buffer.
|
||||||
@@ -20,10 +20,9 @@ import "core:runtime"
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
When a decompression routine doesn't stream its output, but writes to a buffer,
|
// When a decompression routine doesn't stream its output, but writes to a buffer,
|
||||||
we pre-allocate an output buffer to speed up decompression. The default is 1 MiB.
|
// we pre-allocate an output buffer to speed up decompression. The default is 1 MiB.
|
||||||
*/
|
|
||||||
COMPRESS_OUTPUT_ALLOCATE_MIN :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MIN, 1 << 20))
|
COMPRESS_OUTPUT_ALLOCATE_MIN :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MIN, 1 << 20))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -34,15 +33,13 @@ COMPRESS_OUTPUT_ALLOCATE_MIN :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MIN, 1 << 2
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
when size_of(uintptr) == 8 {
|
when size_of(uintptr) == 8 {
|
||||||
/*
|
|
||||||
For 64-bit platforms, we set the default max buffer size to 4 GiB,
|
// For 64-bit platforms, we set the default max buffer size to 4 GiB,
|
||||||
which is GZIP and PKZIP's max payload size.
|
// which is GZIP and PKZIP's max payload size.
|
||||||
*/
|
|
||||||
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 32))
|
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 32))
|
||||||
} else {
|
} else {
|
||||||
/*
|
|
||||||
For 32-bit platforms, we set the default max buffer size to 512 MiB.
|
// For 32-bit platforms, we set the default max buffer size to 512 MiB.
|
||||||
*/
|
|
||||||
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 29))
|
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 29))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,9 +66,8 @@ General_Error :: enum {
|
|||||||
Incompatible_Options,
|
Incompatible_Options,
|
||||||
Unimplemented,
|
Unimplemented,
|
||||||
|
|
||||||
/*
|
// Memory errors
|
||||||
Memory errors
|
|
||||||
*/
|
|
||||||
Allocation_Failed,
|
Allocation_Failed,
|
||||||
Resize_Failed,
|
Resize_Failed,
|
||||||
}
|
}
|
||||||
@@ -86,17 +82,16 @@ GZIP_Error :: enum {
|
|||||||
Payload_Length_Invalid,
|
Payload_Length_Invalid,
|
||||||
Payload_CRC_Invalid,
|
Payload_CRC_Invalid,
|
||||||
|
|
||||||
/*
|
// GZIP's payload can be a maximum of max(u32le), or 4 GiB.
|
||||||
GZIP's payload can be a maximum of max(u32le), or 4 GiB.
|
// If you tell it you expect it to contain more, that's obviously an error.
|
||||||
If you tell it you expect it to contain more, that's obviously an error.
|
|
||||||
*/
|
|
||||||
Payload_Size_Exceeds_Max_Payload,
|
Payload_Size_Exceeds_Max_Payload,
|
||||||
/*
|
|
||||||
For buffered instead of streamed output, the payload size can't exceed
|
// For buffered instead of streamed output, the payload size can't exceed
|
||||||
the max set by the `COMPRESS_OUTPUT_ALLOCATE_MAX` switch in compress/common.odin.
|
// the max set by the `COMPRESS_OUTPUT_ALLOCATE_MAX` switch in compress/common.odin.
|
||||||
|
//
|
||||||
|
// You can tweak this setting using `-define:COMPRESS_OUTPUT_ALLOCATE_MAX=size_in_bytes`
|
||||||
|
|
||||||
You can tweak this setting using `-define:COMPRESS_OUTPUT_ALLOCATE_MAX=size_in_bytes`
|
|
||||||
*/
|
|
||||||
Output_Exceeds_COMPRESS_OUTPUT_ALLOCATE_MAX,
|
Output_Exceeds_COMPRESS_OUTPUT_ALLOCATE_MAX,
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -137,9 +132,8 @@ Context_Memory_Input :: struct #packed {
|
|||||||
code_buffer: u64,
|
code_buffer: u64,
|
||||||
num_bits: u64,
|
num_bits: u64,
|
||||||
|
|
||||||
/*
|
// If we know the data size, we can optimize the reads and writes.
|
||||||
If we know the data size, we can optimize the reads and writes.
|
|
||||||
*/
|
|
||||||
size_packed: i64,
|
size_packed: i64,
|
||||||
size_unpacked: i64,
|
size_unpacked: i64,
|
||||||
}
|
}
|
||||||
@@ -159,18 +153,16 @@ Context_Stream_Input :: struct #packed {
|
|||||||
code_buffer: u64,
|
code_buffer: u64,
|
||||||
num_bits: u64,
|
num_bits: u64,
|
||||||
|
|
||||||
/*
|
// If we know the data size, we can optimize the reads and writes.
|
||||||
If we know the data size, we can optimize the reads and writes.
|
|
||||||
*/
|
|
||||||
size_packed: i64,
|
size_packed: i64,
|
||||||
size_unpacked: i64,
|
size_unpacked: i64,
|
||||||
|
|
||||||
/*
|
// Flags:
|
||||||
Flags:
|
// `input_fully_in_memory`
|
||||||
`input_fully_in_memory`
|
// true = This tells us we read input from `input_data` exclusively. [] = EOF.
|
||||||
true = This tells us we read input from `input_data` exclusively. [] = EOF.
|
// false = Try to refill `input_data` from the `input` stream.
|
||||||
false = Try to refill `input_data` from the `input` stream.
|
|
||||||
*/
|
|
||||||
input_fully_in_memory: b8,
|
input_fully_in_memory: b8,
|
||||||
|
|
||||||
padding: [1]u8,
|
padding: [1]u8,
|
||||||
@@ -194,7 +186,7 @@ input_size_from_stream :: proc(z: ^Context_Stream_Input) -> (res: i64, err: Erro
|
|||||||
|
|
||||||
input_size :: proc{input_size_from_memory, input_size_from_stream}
|
input_size :: proc{input_size_from_memory, input_size_from_stream}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_slice_from_memory :: #force_inline proc(z: ^Context_Memory_Input, size: int) -> (res: []u8, err: io.Error) {
|
read_slice_from_memory :: #force_inline proc(z: ^Context_Memory_Input, size: int) -> (res: []u8, err: io.Error) {
|
||||||
#no_bounds_check {
|
#no_bounds_check {
|
||||||
if len(z.input_data) >= size {
|
if len(z.input_data) >= size {
|
||||||
@@ -211,10 +203,10 @@ read_slice_from_memory :: #force_inline proc(z: ^Context_Memory_Input, size: int
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_slice_from_stream :: #force_inline proc(z: ^Context_Stream_Input, size: int) -> (res: []u8, err: io.Error) {
|
read_slice_from_stream :: #force_inline proc(z: ^Context_Stream_Input, size: int) -> (res: []u8, err: io.Error) {
|
||||||
// TODO: REMOVE ALL USE OF context.temp_allocator here
|
// TODO: REMOVE ALL USE OF context.temp_allocator here
|
||||||
// the is literally no need for it
|
// there is literally no need for it
|
||||||
b := make([]u8, size, context.temp_allocator)
|
b := make([]u8, size, context.temp_allocator)
|
||||||
_ = io.read(z.input, b[:]) or_return
|
_ = io.read(z.input, b[:]) or_return
|
||||||
return b, nil
|
return b, nil
|
||||||
@@ -222,13 +214,13 @@ read_slice_from_stream :: #force_inline proc(z: ^Context_Stream_Input, size: int
|
|||||||
|
|
||||||
read_slice :: proc{read_slice_from_memory, read_slice_from_stream}
|
read_slice :: proc{read_slice_from_memory, read_slice_from_stream}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_data :: #force_inline proc(z: ^$C, $T: typeid) -> (res: T, err: io.Error) {
|
read_data :: #force_inline proc(z: ^$C, $T: typeid) -> (res: T, err: io.Error) {
|
||||||
b := read_slice(z, size_of(T)) or_return
|
b := read_slice(z, size_of(T)) or_return
|
||||||
return (^T)(&b[0])^, nil
|
return (^T)(&b[0])^, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_u8_from_memory :: #force_inline proc(z: ^Context_Memory_Input) -> (res: u8, err: io.Error) {
|
read_u8_from_memory :: #force_inline proc(z: ^Context_Memory_Input) -> (res: u8, err: io.Error) {
|
||||||
#no_bounds_check {
|
#no_bounds_check {
|
||||||
if len(z.input_data) >= 1 {
|
if len(z.input_data) >= 1 {
|
||||||
@@ -240,7 +232,7 @@ read_u8_from_memory :: #force_inline proc(z: ^Context_Memory_Input) -> (res: u8,
|
|||||||
return 0, .EOF
|
return 0, .EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_u8_from_stream :: #force_inline proc(z: ^Context_Stream_Input) -> (res: u8, err: io.Error) {
|
read_u8_from_stream :: #force_inline proc(z: ^Context_Stream_Input) -> (res: u8, err: io.Error) {
|
||||||
b := read_slice_from_stream(z, 1) or_return
|
b := read_slice_from_stream(z, 1) or_return
|
||||||
return b[0], nil
|
return b[0], nil
|
||||||
@@ -248,11 +240,9 @@ read_u8_from_stream :: #force_inline proc(z: ^Context_Stream_Input) -> (res: u8,
|
|||||||
|
|
||||||
read_u8 :: proc{read_u8_from_memory, read_u8_from_stream}
|
read_u8 :: proc{read_u8_from_memory, read_u8_from_stream}
|
||||||
|
|
||||||
/*
|
// You would typically only use this at the end of Inflate, to drain bits from the code buffer
|
||||||
You would typically only use this at the end of Inflate, to drain bits from the code buffer
|
// preferentially.
|
||||||
preferentially.
|
@(optimization_mode="favor_size")
|
||||||
*/
|
|
||||||
@(optimization_mode="speed")
|
|
||||||
read_u8_prefer_code_buffer_lsb :: #force_inline proc(z: ^$C) -> (res: u8, err: io.Error) {
|
read_u8_prefer_code_buffer_lsb :: #force_inline proc(z: ^$C) -> (res: u8, err: io.Error) {
|
||||||
if z.num_bits >= 8 {
|
if z.num_bits >= 8 {
|
||||||
res = u8(read_bits_no_refill_lsb(z, 8))
|
res = u8(read_bits_no_refill_lsb(z, 8))
|
||||||
@@ -267,7 +257,7 @@ read_u8_prefer_code_buffer_lsb :: #force_inline proc(z: ^$C) -> (res: u8, err: i
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
peek_data_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid) -> (res: T, err: io.Error) {
|
peek_data_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid) -> (res: T, err: io.Error) {
|
||||||
size :: size_of(T)
|
size :: size_of(T)
|
||||||
|
|
||||||
@@ -285,7 +275,7 @@ peek_data_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
peek_data_at_offset_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid, #any_int offset: int) -> (res: T, err: io.Error) {
|
peek_data_at_offset_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid, #any_int offset: int) -> (res: T, err: io.Error) {
|
||||||
size :: size_of(T)
|
size :: size_of(T)
|
||||||
|
|
||||||
@@ -303,7 +293,7 @@ peek_data_at_offset_from_memory :: #force_inline proc(z: ^Context_Memory_Input,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid) -> (res: T, err: io.Error) {
|
peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid) -> (res: T, err: io.Error) {
|
||||||
size :: size_of(T)
|
size :: size_of(T)
|
||||||
|
|
||||||
@@ -327,7 +317,7 @@ peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid
|
|||||||
return res, .None
|
return res, .None
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
peek_data_at_offset_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid, #any_int offset: int) -> (res: T, err: io.Error) {
|
peek_data_at_offset_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid, #any_int offset: int) -> (res: T, err: io.Error) {
|
||||||
size :: size_of(T)
|
size :: size_of(T)
|
||||||
|
|
||||||
@@ -362,14 +352,14 @@ peek_data :: proc{peek_data_from_memory, peek_data_from_stream, peek_data_at_off
|
|||||||
|
|
||||||
|
|
||||||
// Sliding window read back
|
// Sliding window read back
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
peek_back_byte :: #force_inline proc(z: ^$C, offset: i64) -> (res: u8, err: io.Error) {
|
peek_back_byte :: #force_inline proc(z: ^$C, offset: i64) -> (res: u8, err: io.Error) {
|
||||||
// Look back into the sliding window.
|
// Look back into the sliding window.
|
||||||
return z.output.buf[z.bytes_written - offset], .None
|
return z.output.buf[z.bytes_written - offset], .None
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generalized bit reader LSB
|
// Generalized bit reader LSB
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width := i8(48)) {
|
refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width := i8(48)) {
|
||||||
refill := u64(width)
|
refill := u64(width)
|
||||||
b := u64(0)
|
b := u64(0)
|
||||||
@@ -395,7 +385,7 @@ refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width :=
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generalized bit reader LSB
|
// Generalized bit reader LSB
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
refill_lsb_from_stream :: proc(z: ^Context_Stream_Input, width := i8(24)) {
|
refill_lsb_from_stream :: proc(z: ^Context_Stream_Input, width := i8(24)) {
|
||||||
refill := u64(width)
|
refill := u64(width)
|
||||||
|
|
||||||
@@ -424,13 +414,13 @@ refill_lsb_from_stream :: proc(z: ^Context_Stream_Input, width := i8(24)) {
|
|||||||
refill_lsb :: proc{refill_lsb_from_memory, refill_lsb_from_stream}
|
refill_lsb :: proc{refill_lsb_from_memory, refill_lsb_from_stream}
|
||||||
|
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
consume_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) {
|
consume_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) {
|
||||||
z.code_buffer >>= width
|
z.code_buffer >>= width
|
||||||
z.num_bits -= u64(width)
|
z.num_bits -= u64(width)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
consume_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) {
|
consume_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) {
|
||||||
z.code_buffer >>= width
|
z.code_buffer >>= width
|
||||||
z.num_bits -= u64(width)
|
z.num_bits -= u64(width)
|
||||||
@@ -438,7 +428,7 @@ consume_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, wid
|
|||||||
|
|
||||||
consume_bits_lsb :: proc{consume_bits_lsb_from_memory, consume_bits_lsb_from_stream}
|
consume_bits_lsb :: proc{consume_bits_lsb_from_memory, consume_bits_lsb_from_stream}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
peek_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
peek_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
||||||
if z.num_bits < u64(width) {
|
if z.num_bits < u64(width) {
|
||||||
refill_lsb(z)
|
refill_lsb(z)
|
||||||
@@ -446,7 +436,7 @@ peek_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width:
|
|||||||
return u32(z.code_buffer &~ (~u64(0) << width))
|
return u32(z.code_buffer &~ (~u64(0) << width))
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
peek_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
peek_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
||||||
if z.num_bits < u64(width) {
|
if z.num_bits < u64(width) {
|
||||||
refill_lsb(z)
|
refill_lsb(z)
|
||||||
@@ -456,13 +446,13 @@ peek_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width:
|
|||||||
|
|
||||||
peek_bits_lsb :: proc{peek_bits_lsb_from_memory, peek_bits_lsb_from_stream}
|
peek_bits_lsb :: proc{peek_bits_lsb_from_memory, peek_bits_lsb_from_stream}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
peek_bits_no_refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
peek_bits_no_refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
||||||
assert(z.num_bits >= u64(width))
|
assert(z.num_bits >= u64(width))
|
||||||
return u32(z.code_buffer &~ (~u64(0) << width))
|
return u32(z.code_buffer &~ (~u64(0) << width))
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
peek_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
peek_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
||||||
assert(z.num_bits >= u64(width))
|
assert(z.num_bits >= u64(width))
|
||||||
return u32(z.code_buffer &~ (~u64(0) << width))
|
return u32(z.code_buffer &~ (~u64(0) << width))
|
||||||
@@ -470,14 +460,14 @@ peek_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Inp
|
|||||||
|
|
||||||
peek_bits_no_refill_lsb :: proc{peek_bits_no_refill_lsb_from_memory, peek_bits_no_refill_lsb_from_stream}
|
peek_bits_no_refill_lsb :: proc{peek_bits_no_refill_lsb_from_memory, peek_bits_no_refill_lsb_from_stream}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
read_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
||||||
k := #force_inline peek_bits_lsb(z, width)
|
k := #force_inline peek_bits_lsb(z, width)
|
||||||
#force_inline consume_bits_lsb(z, width)
|
#force_inline consume_bits_lsb(z, width)
|
||||||
return k
|
return k
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
read_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
||||||
k := peek_bits_lsb(z, width)
|
k := peek_bits_lsb(z, width)
|
||||||
consume_bits_lsb(z, width)
|
consume_bits_lsb(z, width)
|
||||||
@@ -486,14 +476,14 @@ read_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width:
|
|||||||
|
|
||||||
read_bits_lsb :: proc{read_bits_lsb_from_memory, read_bits_lsb_from_stream}
|
read_bits_lsb :: proc{read_bits_lsb_from_memory, read_bits_lsb_from_stream}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_bits_no_refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
read_bits_no_refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
||||||
k := #force_inline peek_bits_no_refill_lsb(z, width)
|
k := #force_inline peek_bits_no_refill_lsb(z, width)
|
||||||
#force_inline consume_bits_lsb(z, width)
|
#force_inline consume_bits_lsb(z, width)
|
||||||
return k
|
return k
|
||||||
}
|
}
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
read_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
read_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
||||||
k := peek_bits_no_refill_lsb(z, width)
|
k := peek_bits_no_refill_lsb(z, width)
|
||||||
consume_bits_lsb(z, width)
|
consume_bits_lsb(z, width)
|
||||||
@@ -503,14 +493,14 @@ read_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Inp
|
|||||||
read_bits_no_refill_lsb :: proc{read_bits_no_refill_lsb_from_memory, read_bits_no_refill_lsb_from_stream}
|
read_bits_no_refill_lsb :: proc{read_bits_no_refill_lsb_from_memory, read_bits_no_refill_lsb_from_stream}
|
||||||
|
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
discard_to_next_byte_lsb_from_memory :: proc(z: ^Context_Memory_Input) {
|
discard_to_next_byte_lsb_from_memory :: proc(z: ^Context_Memory_Input) {
|
||||||
discard := u8(z.num_bits & 7)
|
discard := u8(z.num_bits & 7)
|
||||||
#force_inline consume_bits_lsb(z, discard)
|
#force_inline consume_bits_lsb(z, discard)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@(optimization_mode="speed")
|
@(optimization_mode="favor_size")
|
||||||
discard_to_next_byte_lsb_from_stream :: proc(z: ^Context_Stream_Input) {
|
discard_to_next_byte_lsb_from_stream :: proc(z: ^Context_Stream_Input) {
|
||||||
discard := u8(z.num_bits & 7)
|
discard := u8(z.num_bits & 7)
|
||||||
consume_bits_lsb(z, discard)
|
consume_bits_lsb(z, discard)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user