mirror of
https://github.com/odin-lang/Odin.git
synced 2026-08-21 12:40:49 +00:00
Merge branch 'master' into bill/feoramalloc
This commit is contained in:
14
.gitattributes
vendored
14
.gitattributes
vendored
@@ -1,6 +1,16 @@
|
||||
*.odin linguist-language=Odin
|
||||
* text=auto
|
||||
|
||||
# These files must always have *nix line-endings
|
||||
Makefile text eol=lf
|
||||
*.sh text eol=lf
|
||||
*.sh text eol=lf
|
||||
vendor/box2d/lib/box2d_windows_amd64_avx2.lib filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/box2d/lib/box2d_windows_amd64_sse2.lib filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/miniaudio/lib/miniaudio.lib filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/sdl3/SDL3.dll filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/sdl3/SDL3.lib filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/sdl3/mixer/*.dll filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/sdl3/mixer/*.lib filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/wgpu/lib/wgpu-windows-x86_64-msvc-release/lib/wgpu_native.dll filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/wgpu/lib/wgpu-windows-x86_64-msvc-release/lib/wgpu_native.dll.lib filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/wgpu/lib/wgpu-windows-x86_64-msvc-release/lib/wgpu_native.lib filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/wgpu/lib/wgpu-windows-x86_64-msvc-release/lib/wgpu_native.pdb filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
237
.github/workflows/ci.yml
vendored
237
.github/workflows/ci.yml
vendored
@@ -6,7 +6,7 @@ jobs:
|
||||
name: NetBSD Build, Check, and Test
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
PKGSRC_BRANCH: 2024Q3
|
||||
PKGSRC_BRANCH: 2025Q4
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build, Check, and Test
|
||||
@@ -30,14 +30,14 @@ jobs:
|
||||
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 check vendor/sdl3 -vet -strict-style -disallow-do -target:netbsd_amd64 -no-entry-point
|
||||
./odin check vendor/sdl3 -vet -strict-style -disallow-do -target:netbsd_arm64 -no-entry-point
|
||||
./odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:netbsd_amd64
|
||||
./odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:netbsd_arm64
|
||||
./odin check examples/all/sdl3 -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:netbsd_amd64 -no-entry-point
|
||||
./odin check examples/all/sdl3 -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:netbsd_arm64 -no-entry-point
|
||||
./odin run tests/heap_allocator -vet -strict-style -disallow-do -debug -- -allocator=feoramalloc -vmem-tests -serial-tests -parallel-tests
|
||||
./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
|
||||
./odin test tests/core/normal.odin -file -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||
./odin test tests/core/speed.odin -file -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -o:speed -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||
./odin test tests/vendor -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -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
|
||||
|
||||
@@ -64,52 +64,64 @@ jobs:
|
||||
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 check vendor/sdl3 -vet -strict-style -disallow-do -target:freebsd_amd64 -no-entry-point
|
||||
./odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freebsd_amd64
|
||||
./odin check examples/all/sdl3 -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freebsd_amd64 -no-entry-point
|
||||
./odin run tests/heap_allocator -vet -strict-style -disallow-do -debug -- -allocator=feoramalloc -vmem-tests -serial-tests -parallel-tests
|
||||
./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
|
||||
./odin test tests/core/normal.odin -file -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -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 -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -o:speed -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||
./odin test tests/vendor -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -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]
|
||||
os: [macos-15-intel, macos-latest, ubuntu-latest, ubuntu-24.04-arm]
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: ${{ matrix.os == 'macos-14' && 'MacOS ARM' || (matrix.os == 'macos-13' && 'MacOS Intel' || 'Ubuntu') }} Build, Check, and Test
|
||||
name: ${{ matrix.os == 'macos-latest' && 'MacOS ARM' || (matrix.os == 'macos-15-intel' && 'MacOS Intel') || (matrix.os == 'ubuntu-latest' && 'Ubuntu') || (matrix.os == 'ubuntu-24.04-arm' && 'Ubuntu ARM') }} Build, Check, and Test
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Download LLVM (Linux)
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
- name: Download LLVM (MacOS Intel)
|
||||
if: matrix.os == 'macos-15-intel'
|
||||
run: |
|
||||
brew update
|
||||
brew install llvm@20 lua@5.4 lld
|
||||
echo "$(brew --prefix llvm@20)/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Download LLVM (MacOS ARM)
|
||||
if: matrix.os == 'macos-latest'
|
||||
run: |
|
||||
brew update
|
||||
brew install llvm@22 wasmtime lua@5.4 lld
|
||||
echo "$(brew --prefix llvm@22)/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Download LLVM (Ubuntu)
|
||||
if: matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm'
|
||||
run: |
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh 20
|
||||
echo "/usr/lib/llvm-20/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Download LLVM (MacOS Intel)
|
||||
if: matrix.os == 'macos-13'
|
||||
run: |
|
||||
brew update
|
||||
brew install llvm@20 lua@5.4 lld
|
||||
|
||||
- name: Download LLVM (MacOS ARM)
|
||||
if: matrix.os == 'macos-14'
|
||||
run: |
|
||||
brew update
|
||||
brew install llvm@20 wasmtime lua@5.4 lld
|
||||
|
||||
- name: Build Odin
|
||||
run: ./build_odin.sh release
|
||||
- name: Odin version
|
||||
run: ./odin version
|
||||
- name: Odin report
|
||||
run: ./odin report
|
||||
- name: Get needed vendor libs
|
||||
if: matrix.os == 'ubuntu-24.04-arm'
|
||||
run: sudo apt-get install -y liblua5.4-dev
|
||||
- name: Install libcurl (Ubuntu)
|
||||
if: matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm'
|
||||
run: |
|
||||
sudo apt-get install libcurl4-openssl-dev libmbedtls-dev
|
||||
- name: Install libcurl (macOS)
|
||||
if: matrix.os == 'macos-15-intel' || matrix.os == 'macos-latest'
|
||||
run: |
|
||||
brew install curl mbedtls
|
||||
- name: Compile needed Vendor
|
||||
run: |
|
||||
make -C vendor/stb/src
|
||||
@@ -122,58 +134,65 @@ jobs:
|
||||
- name: Odin run -debug
|
||||
run: ./odin run examples/demo -debug
|
||||
- name: Odin check examples/all
|
||||
run: ./odin check examples/all -strict-style -vet -disallow-do
|
||||
- name: Odin check vendor/sdl3
|
||||
run: ./odin check vendor/sdl3 -strict-style -vet -disallow-do -no-entry-point
|
||||
run: ./odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do
|
||||
- name: Odin check examples/all/sdl3
|
||||
run: ./odin check examples/all/sdl3 -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -no-entry-point
|
||||
- name: Odin heap allocator tests
|
||||
run: ./odin run tests/heap_allocator -vet -strict-style -disallow-do -debug -sanitize:thread -- -allocator=feoramalloc -vmem-tests -serial-tests -parallel-tests
|
||||
- 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
|
||||
run: ./odin test tests/core/normal.odin -file -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -sanitize:address
|
||||
- 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
|
||||
run: ./odin test tests/core/speed.odin -o:speed -file -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||
- name: Wycheproof tests
|
||||
run: ./odin test tests/core/crypto/wycheproof -vet -vet-tabs -strict-style -vet-style -vet-cast -warnings-as-errors -disallow-do -o:speed
|
||||
- name: Noise Protocol Framework tests
|
||||
run: ./odin test tests/core/crypto/noise -vet -vet-tabs -strict-style -vet-style -vet-cast -warnings-as-errors -disallow-do -o:speed
|
||||
- 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
|
||||
run: ./odin test tests/vendor -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -sanitize:address
|
||||
if: matrix.os != 'macos-15-intel' && matrix.os != 'macos-latest'
|
||||
- name: Vendor library tests (MacOS ARM)
|
||||
run: ./odin test tests/vendor -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -sanitize:address -extra-linker-flags:"-L/opt/homebrew/opt/lua@5.4/lib"
|
||||
if: matrix.os == 'macos-latest'
|
||||
- name: Vendor library tests (MacOS Intel)
|
||||
run: ./odin test tests/vendor -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -sanitize:address -extra-linker-flags:"-L/usr/local/opt/lua@5.4/lib"
|
||||
if: matrix.os == 'macos-15-intel'
|
||||
|
||||
- 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
|
||||
run: ./odin test tests/internal -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -sanitize:address
|
||||
- name: GitHub Issue tests
|
||||
run: |
|
||||
cd tests/issues
|
||||
./run.sh
|
||||
|
||||
- name: Check benchmarks
|
||||
run: ./odin check tests/benchmark -vet -strict-style -no-entry-point
|
||||
- name: Odin check examples/all for Linux i386
|
||||
run: ./odin check examples/all -vet -strict-style -disallow-do -target:linux_i386
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
- name: Odin check examples/all for Linux arm64
|
||||
run: ./odin check examples/all -vet -strict-style -disallow-do -target:linux_arm64
|
||||
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: Odin check vendor/sdl3 for Linux i386
|
||||
run: ./odin check vendor/sdl3 -vet -strict-style -disallow-do -no-entry-point -target:linux_i386
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
- name: Odin check vendor/sdl3 for Linux arm64
|
||||
run: ./odin check vendor/sdl3 -vet -strict-style -disallow-do -no-entry-point -target:linux_arm64
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
- name: Odin check vendor/sdl3 for FreeBSD amd64
|
||||
run: ./odin check vendor/sdl3 -vet -strict-style -disallow-do -no-entry-point -target:freebsd_amd64
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
- name: Odin check vendor/sdl3 for OpenBSD amd64
|
||||
run: ./odin check vendor/sdl3 -vet -strict-style -disallow-do -no-entry-point -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
|
||||
./odin build examples/demo -target:wasi_wasm32 -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -out:demo
|
||||
wasmtime ./demo.wasm
|
||||
if: matrix.os == 'macos-14'
|
||||
if: matrix.os == 'macos-latest'
|
||||
|
||||
- name: Check benchmarks
|
||||
run: ./odin check tests/benchmark -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -no-entry-point
|
||||
|
||||
- name: Odin check examples/all for Linux i386
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:linux_i386
|
||||
- name: Odin check examples/all for OpenBSD amd64
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:openbsd_amd64
|
||||
|
||||
- name: Odin check examples/all for js_wasm32
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -no-entry-point -target:js_wasm32
|
||||
- name: Odin check examples/all for js_wasm64p32
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -no-entry-point -target:js_wasm64p32
|
||||
|
||||
- name: Odin check examples/all/sdl3 for Linux i386
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all/sdl3 -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -no-entry-point -target:linux_i386
|
||||
- name: Odin check examples/all/sdl3 for OpenBSD amd64
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all/sdl3 -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -no-entry-point -target:openbsd_amd64
|
||||
|
||||
build_windows:
|
||||
name: Windows Build, Check, and Test
|
||||
@@ -184,7 +203,7 @@ jobs:
|
||||
- name: build Odin
|
||||
shell: cmd
|
||||
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"
|
||||
./build.bat 1
|
||||
- name: Odin version
|
||||
run: ./odin version
|
||||
@@ -193,28 +212,28 @@ jobs:
|
||||
- name: Odin check
|
||||
shell: cmd
|
||||
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
|
||||
- name: Odin run
|
||||
shell: cmd
|
||||
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
|
||||
- name: Odin run -debug
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin run examples/demo -debug -vet -strict-style -disallow-do
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
|
||||
odin run examples/demo -debug -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do
|
||||
- name: Odin check examples/all
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin check examples/all -vet -strict-style -disallow-do
|
||||
- name: Odin check vendor/sdl3
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do
|
||||
- name: Odin check examples/all/sdl3
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin check vendor/sdl3 -vet -strict-style -disallow-do -no-entry-point
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
|
||||
odin check examples/all/sdl3 -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -no-entry-point
|
||||
- name: Odin heap allocator tests
|
||||
shell: cmd
|
||||
run: |
|
||||
@@ -223,51 +242,55 @@ jobs:
|
||||
- name: Core library tests
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin test tests/core/normal.odin -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
|
||||
odin test tests/core/normal.odin -file -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -sanitize:address
|
||||
- name: Optimized core library tests
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin test tests/core/speed.odin -o:speed -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||
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 -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||
- name: Wycheproof tests
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
|
||||
odin test tests/core/crypto/wycheproof -vet -vet-tabs -strict-style -vet-style -vet-cast -warnings-as-errors -disallow-do -o:speed -microarch:native
|
||||
- name: Noise Protocol Framework tests
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
|
||||
odin test tests/core/crypto/noise -vet -vet-tabs -strict-style -vet-style -vet-cast -warnings-as-errors -disallow-do -o:speed -microarch:native
|
||||
- name: Vendor library tests
|
||||
shell: cmd
|
||||
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"
|
||||
copy vendor\lua\5.4\windows\*.dll .
|
||||
odin test tests/vendor -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||
odin test tests/vendor -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -sanitize:address
|
||||
- name: Odin internals tests
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin test tests/internal -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
|
||||
odin test tests/internal -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -sanitize:address
|
||||
- name: Check issues
|
||||
shell: cmd
|
||||
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/issues
|
||||
call run.bat
|
||||
- name: Check benchmarks
|
||||
shell: cmd
|
||||
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 tests/benchmark -vet -strict-style -no-entry-point
|
||||
- name: Odin documentation tests
|
||||
shell: cmd
|
||||
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
|
||||
call build.bat
|
||||
- name: core:math/big tests
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
cd tests\core\math\big
|
||||
call build.bat
|
||||
- name: Odin check examples/all for Windows 32bits
|
||||
shell: cmd
|
||||
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
|
||||
|
||||
build_linux_riscv64:
|
||||
@@ -284,6 +307,10 @@ jobs:
|
||||
sudo ./llvm.sh 18
|
||||
echo "/usr/lib/llvm-18/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Install libcurl
|
||||
run: |
|
||||
sudo apt-get install libcurl4-openssl-dev libmbedtls-dev
|
||||
|
||||
- name: Build Odin
|
||||
run: ./build_odin.sh release
|
||||
|
||||
@@ -300,28 +327,28 @@ jobs:
|
||||
make -C vendor/miniaudio/src
|
||||
|
||||
- name: Odin check examples/all
|
||||
run: ./odin check examples/all -target:linux_riscv64 -vet -strict-style -disallow-do
|
||||
run: ./odin check examples/all -target:linux_riscv64 -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do
|
||||
|
||||
- name: Odin check vendor/sdl3
|
||||
run: ./odin check vendor/sdl3 -target:linux_riscv64 -vet -strict-style -disallow-do -no-entry-point
|
||||
- name: Odin check examples/all/sdl3
|
||||
run: ./odin check examples/all/sdl3 -target:linux_riscv64 -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -no-entry-point
|
||||
|
||||
- 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
|
||||
run: ./odin run examples/demo -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -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
|
||||
run: ./odin run examples/demo -debug -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static" -no-rpath
|
||||
|
||||
- name: Odin heap allocator tests
|
||||
run: ./odin run tests/heap_allocator -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 -debug -- -allocator=feoramalloc -vmem-tests -serial-tests -parallel-tests
|
||||
|
||||
- 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
|
||||
run: ./odin test tests/core/normal.odin -file -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -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
|
||||
run: ./odin test tests/core/speed.odin -o:speed -file -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -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
|
||||
run: ./odin test tests/internal -all-packages -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -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
|
||||
|
||||
109
.github/workflows/nightly.yml
vendored
109
.github/workflows/nightly.yml
vendored
@@ -12,6 +12,8 @@ jobs:
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
lfs: true
|
||||
- name: build Odin
|
||||
shell: cmd
|
||||
run: |
|
||||
@@ -42,30 +44,34 @@ jobs:
|
||||
name: windows_artifacts
|
||||
path: dist
|
||||
build_linux:
|
||||
name: Linux Build
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [amd64, arm64]
|
||||
env:
|
||||
ARCH: ${{ matrix.arch }}
|
||||
DOCKER_IMAGE: ${{ matrix.arch == 'amd64' && 'alpine:3.24' || 'arm64v8/alpine:3.24' }}
|
||||
name: Linux Build ${{ matrix.arch }}
|
||||
if: github.repository == 'odin-lang/Odin'
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ${{ matrix.arch == 'amd64' && 'ubuntu-latest' || 'ubuntu-24.04-arm' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: jirutka/setup-alpine@v1
|
||||
with:
|
||||
branch: edge
|
||||
- name: (Linux) Download LLVM
|
||||
lfs: true
|
||||
- name: (Linux) Download LLVM and Build Odin
|
||||
run: |
|
||||
apk add --no-cache \
|
||||
musl-dev llvm20-dev clang20 git mold lz4 \
|
||||
libxml2-static llvm20-static zlib-static zstd-static \
|
||||
make
|
||||
shell: alpine.sh --root {0}
|
||||
- name: build odin
|
||||
# NOTE: this build does slow compile times because of musl
|
||||
run: ci/build_linux_static.sh
|
||||
shell: alpine.sh {0}
|
||||
docker run --rm -v "$PWD:/src" -w /src $DOCKER_IMAGE sh -c '
|
||||
apk add --no-cache \
|
||||
musl-dev llvm20-dev clang20 build-base git mold lz4 \
|
||||
libxml2-static llvm20-static zlib-static zstd-static \
|
||||
make &&
|
||||
git config --global --add safe.directory /src &&
|
||||
./ci/build_linux_static.sh
|
||||
'
|
||||
- name: Odin run
|
||||
run: ./odin run examples/demo
|
||||
- name: Copy artifacts
|
||||
run: |
|
||||
FILE="odin-linux-amd64-nightly+$(date -I)"
|
||||
FILE="odin-linux-$ARCH-nightly+$(date -I)"
|
||||
mkdir $FILE
|
||||
cp odin $FILE
|
||||
cp LICENSE $FILE
|
||||
@@ -74,27 +80,36 @@ jobs:
|
||||
cp -r core $FILE
|
||||
cp -r vendor $FILE
|
||||
cp -r examples $FILE
|
||||
./ci/remove_windows_binaries.sh $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-linux-$ARCH-nightly+$(date -I)"
|
||||
$FILE/odin run examples/demo
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: linux_artifacts
|
||||
name: ${{ matrix.arch == 'amd64' && 'linux_artifacts' || 'linux_arm_artifacts' }}
|
||||
path: dist.tar.gz
|
||||
build_macos:
|
||||
name: MacOS Build
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [amd64, arm64]
|
||||
env:
|
||||
ARCH: ${{ matrix.arch }}
|
||||
name: Macos Build ${{ matrix.arch }}
|
||||
if: github.repository == 'odin-lang/Odin'
|
||||
runs-on: macos-13
|
||||
runs-on: ${{ matrix.arch == 'amd64' && 'macos-15-intel' || 'macos-latest' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
lfs: true
|
||||
- name: Download LLVM and setup PATH
|
||||
run: |
|
||||
brew update
|
||||
brew install llvm@20 dylibbundler lld
|
||||
brew install llvm@20 dylibbundler lld@20
|
||||
brew link llvm@20
|
||||
|
||||
- name: build odin
|
||||
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
|
||||
@@ -102,7 +117,7 @@ jobs:
|
||||
run: CXXFLAGS="-L/usr/lib/system -L/usr/lib" make nightly
|
||||
- name: Bundle
|
||||
run: |
|
||||
FILE="odin-macos-amd64-nightly+$(date -I)"
|
||||
FILE="odin-macos-$ARCH-nightly+$(date -I)"
|
||||
mkdir $FILE
|
||||
cp odin $FILE
|
||||
cp LICENSE $FILE
|
||||
@@ -111,59 +126,22 @@ jobs:
|
||||
cp -r core $FILE
|
||||
cp -r vendor $FILE
|
||||
cp -r examples $FILE
|
||||
./ci/remove_windows_binaries.sh $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-macos-$ARCH-nightly+$(date -I)"
|
||||
$FILE/odin run examples/demo
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: macos_artifacts
|
||||
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 update
|
||||
brew install llvm@20 dylibbundler lld
|
||||
|
||||
- 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
|
||||
name: ${{ matrix.arch == 'amd64' && 'macos_artifacts' || 'macos_arm_artifacts' }}
|
||||
path: dist.tar.gz
|
||||
upload_b2:
|
||||
runs-on: [ubuntu-latest]
|
||||
needs: [build_windows, build_macos, build_macos_arm, build_linux]
|
||||
needs: [build_windows, build_macos, build_linux]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
@@ -192,6 +170,12 @@ jobs:
|
||||
name: linux_artifacts
|
||||
path: linux_artifacts
|
||||
|
||||
- name: Download Ubuntu ARM artifacts
|
||||
uses: actions/download-artifact@v4.1.7
|
||||
with:
|
||||
name: linux_arm_artifacts
|
||||
path: linux_arm_artifacts
|
||||
|
||||
- name: Download macOS artifacts
|
||||
uses: actions/download-artifact@v4.1.7
|
||||
with:
|
||||
@@ -219,6 +203,7 @@ jobs:
|
||||
file linux_artifacts/dist.tar.gz
|
||||
python3 ci/nightly.py artifact windows-amd64 windows_artifacts/
|
||||
python3 ci/nightly.py artifact linux-amd64 linux_artifacts/dist.tar.gz
|
||||
python3 ci/nightly.py artifact linux-arm64 linux_arm_artifacts/dist.tar.gz
|
||||
python3 ci/nightly.py artifact macos-amd64 macos_artifacts/dist.tar.gz
|
||||
python3 ci/nightly.py artifact macos-arm64 macos_arm_artifacts/dist.tar.gz
|
||||
python3 ci/nightly.py prune
|
||||
|
||||
18
.gitignore
vendored
18
.gitignore
vendored
@@ -277,6 +277,10 @@ odin
|
||||
*.bin
|
||||
demo.bin
|
||||
libLLVM*.so*
|
||||
*.a
|
||||
|
||||
# WASM
|
||||
*.wasm
|
||||
|
||||
# shared collection
|
||||
shared/
|
||||
@@ -293,5 +297,17 @@ build.sh
|
||||
|
||||
# RAD debugger project file
|
||||
*.raddbg
|
||||
|
||||
*.rdi
|
||||
tests/issues/build/*
|
||||
misc/featuregen/featuregen
|
||||
|
||||
# Clangd stuff
|
||||
.cache/
|
||||
.clangd
|
||||
compile_commands.json
|
||||
|
||||
# Dev cmake helpers
|
||||
build/
|
||||
cmake-build*/
|
||||
CMakeLists.txt
|
||||
sandbox/
|
||||
|
||||
37
LICENSE
37
LICENSE
@@ -1,26 +1,17 @@
|
||||
Copyright (c) 2016-2024 Ginger Bill. All rights reserved.
|
||||
Copyright (c) 2016-2025 Ginger Bill. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
@@ -7,13 +7,232 @@ nil :: nil
|
||||
false :: 0!=0
|
||||
true :: 0==0
|
||||
|
||||
ODIN_OS :: ODIN_OS
|
||||
ODIN_ARCH :: ODIN_ARCH
|
||||
ODIN_ENDIAN :: ODIN_ENDIAN
|
||||
ODIN_VENDOR :: ODIN_VENDOR
|
||||
ODIN_VERSION :: ODIN_VERSION
|
||||
ODIN_ROOT :: ODIN_ROOT
|
||||
ODIN_DEBUG :: ODIN_DEBUG
|
||||
// The following constants are added in `checker.cpp`'s `init_universal` procedure.
|
||||
|
||||
/*
|
||||
An `enum` value indicating the target's CPU architecture.
|
||||
Possible values are: `.amd64`, `.i386`, `.arm32`, `.arm64`, `.wasm32`, `.wasm64p32`, and `.riscv64`.
|
||||
*/
|
||||
ODIN_ARCH :: ODIN_ARCH
|
||||
|
||||
/*
|
||||
A `string` indicating the target's CPU architecture.
|
||||
Possible values are: "amd64", "i386", "arm32", "arm64", "wasm32", "wasm64p32", "riscv64".
|
||||
*/
|
||||
ODIN_ARCH_STRING :: ODIN_ARCH_STRING
|
||||
|
||||
/*
|
||||
An `enum` value indicating the type of compiled output, chosen using `-build-mode`.
|
||||
Possible values are: `.Executable`, `.Dynamic`, `.Static`, `.Object`, `.Assembly`, and `.LLVM_IR`.
|
||||
*/
|
||||
ODIN_BUILD_MODE :: ODIN_BUILD_MODE
|
||||
|
||||
/*
|
||||
A `string` containing the name of the folder that contains the entry point,
|
||||
e.g. for `%ODIN_ROOT%/examples/demo`, this would contain `demo`.
|
||||
*/
|
||||
ODIN_BUILD_PROJECT_NAME :: ODIN_BUILD_PROJECT_NAME
|
||||
|
||||
/*
|
||||
An `i64` containing the time at which the executable was compiled, in nanoseconds.
|
||||
This is compatible with the `time.Time` type, i.e. `time.Time{_nsec=ODIN_COMPILE_TIMESTAMP}`
|
||||
*/
|
||||
ODIN_COMPILE_TIMESTAMP :: ODIN_COMPILE_TIMESTAMP
|
||||
|
||||
/*
|
||||
`true` if the `-debug` command line switch is passed, which enables debug info generation.
|
||||
*/
|
||||
ODIN_DEBUG :: ODIN_DEBUG
|
||||
|
||||
/*
|
||||
`true` if the `-default-to-nil-allocator` command line switch is passed,
|
||||
which sets the initial `context.allocator` to an allocator that does nothing.
|
||||
*/
|
||||
ODIN_DEFAULT_TO_NIL_ALLOCATOR :: ODIN_DEFAULT_TO_NIL_ALLOCATOR
|
||||
|
||||
/*
|
||||
`true` if the `-default-to-panic-allocator` command line switch is passed,
|
||||
which sets the initial `context.allocator` to an allocator that panics if allocated from.
|
||||
*/
|
||||
ODIN_DEFAULT_TO_PANIC_ALLOCATOR :: ODIN_DEFAULT_TO_PANIC_ALLOCATOR
|
||||
|
||||
/*
|
||||
`true` if the `-disable-assert` command line switch is passed,
|
||||
which removes all calls to `assert` from the program.
|
||||
*/
|
||||
ODIN_DISABLE_ASSERT :: ODIN_DISABLE_ASSERT
|
||||
|
||||
/*
|
||||
An `enum` value indicating the endianness of the target.
|
||||
Possible values are: `.Little` and `.Big`.
|
||||
*/
|
||||
ODIN_ENDIAN :: ODIN_ENDIAN
|
||||
|
||||
/*
|
||||
An `string` indicating the endianness of the target.
|
||||
Possible values are: "little" and "big".
|
||||
*/
|
||||
ODIN_ENDIAN_STRING :: ODIN_ENDIAN_STRING
|
||||
|
||||
/*
|
||||
An `enum` value set using the `-error-pos-style` switch, indicating the source location style used for compile errors and warnings.
|
||||
Possible values are: `.Default` (Odin-style) and `.Unix`.
|
||||
*/
|
||||
ODIN_ERROR_POS_STYLE :: ODIN_ERROR_POS_STYLE
|
||||
|
||||
/*
|
||||
`true` if the `-foreign-error-procedures` command line switch is passed,
|
||||
which inhibits generation of runtime error procedures, so that they can be in a separate compilation unit.
|
||||
*/
|
||||
ODIN_FOREIGN_ERROR_PROCEDURES :: ODIN_FOREIGN_ERROR_PROCEDURES
|
||||
|
||||
/*
|
||||
A `string` describing the microarchitecture used for code generation.
|
||||
If not set using the `-microarch` command line switch, the compiler will pick a default.
|
||||
Possible values include, but are not limited to: "sandybridge", "x86-64-v2".
|
||||
*/
|
||||
ODIN_MICROARCH_STRING :: ODIN_MICROARCH_STRING
|
||||
|
||||
/*
|
||||
An `int` value representing the minimum OS version given to the linker, calculated as `major * 10_000 + minor * 100 + revision`.
|
||||
If not set using the `-minimum-os-version` command line switch, it defaults to `0`, except on Darwin, where it's `11_00_00`.
|
||||
*/
|
||||
ODIN_MINIMUM_OS_VERSION :: ODIN_MINIMUM_OS_VERSION
|
||||
|
||||
/*
|
||||
`true` if the `-no-bounds-check` command line switch is passed, which disables bounds checking at runtime.
|
||||
*/
|
||||
ODIN_NO_BOUNDS_CHECK :: ODIN_NO_BOUNDS_CHECK
|
||||
|
||||
/*
|
||||
`true` if the `-no-crt` command line switch is passed, which inhibits linking with the C Runtime Library, a.k.a. LibC.
|
||||
*/
|
||||
ODIN_NO_CRT :: ODIN_NO_CRT
|
||||
|
||||
/*
|
||||
`true` if the `-no-entry-point` command line switch is passed, which makes the declaration of a `main` procedure optional.
|
||||
*/
|
||||
ODIN_NO_ENTRY_POINT :: ODIN_NO_ENTRY_POINT
|
||||
|
||||
/*
|
||||
`true` if the `-no-rtti` command line switch is passed, which inhibits generation of full Runtime Type Information.
|
||||
*/
|
||||
ODIN_NO_RTTI :: ODIN_NO_RTTI
|
||||
|
||||
/*
|
||||
`true` if the `-no-type-assert` command line switch is passed, which disables type assertion checking program wide.
|
||||
*/
|
||||
ODIN_NO_TYPE_ASSERT :: ODIN_NO_TYPE_ASSERT
|
||||
|
||||
/*
|
||||
An `enum` value indicating the optimization level selected using the `-o` command line switch.
|
||||
Possible values are: `.None`, `.Minimal`, `.Size`, `.Speed`, and `.Aggressive`.
|
||||
|
||||
If `ODIN_OPTIMIZATION_MODE` is anything other than `.None` or `.Minimal`, the compiler will also perform a unity build,
|
||||
and `ODIN_USE_SEPARATE_MODULES` will be set to `false` as a result.
|
||||
*/
|
||||
ODIN_OPTIMIZATION_MODE :: ODIN_OPTIMIZATION_MODE
|
||||
|
||||
/*
|
||||
An `enum` value indicating what the target operating system is.
|
||||
*/
|
||||
ODIN_OS :: ODIN_OS
|
||||
|
||||
/*
|
||||
A `string` indicating what the target operating system is.
|
||||
*/
|
||||
ODIN_OS_STRING :: ODIN_OS_STRING
|
||||
|
||||
/*
|
||||
An `enum` value indicating the platform subtarget, chosen using the `-subtarget` switch.
|
||||
Possible values are: `.Default` `.iPhone`, .iPhoneSimulator, and `.Android`.
|
||||
*/
|
||||
ODIN_PLATFORM_SUBTARGET :: ODIN_PLATFORM_SUBTARGET
|
||||
|
||||
/*
|
||||
A `string` representing the path of the folder containing the Odin compiler,
|
||||
relative to which we expect to find the `base` and `core` package collections.
|
||||
*/
|
||||
ODIN_ROOT :: ODIN_ROOT
|
||||
|
||||
/*
|
||||
A `bit_set` indicating the sanitizer flags set using the `-sanitize` command line switch.
|
||||
Supported flags are `.Address`, `.Memory`, and `.Thread`.
|
||||
*/
|
||||
ODIN_SANITIZER_FLAGS :: ODIN_SANITIZER_FLAGS
|
||||
|
||||
/*
|
||||
`true` if the code is being compiled via an invocation of `odin test`.
|
||||
*/
|
||||
ODIN_TEST :: ODIN_TEST
|
||||
|
||||
/*
|
||||
`true` if built using the experimental Tilde backend.
|
||||
*/
|
||||
ODIN_TILDE :: ODIN_TILDE
|
||||
|
||||
/*
|
||||
`true` by default, meaning each each package is built into its own object file, and then linked together.
|
||||
`false` if the `-use-single-module` command line switch to force a unity build is provided.
|
||||
|
||||
If `ODIN_OPTIMIZATION_MODE` is anything other than `.None` or `.Minimal`, the compiler will also perform a unity build,
|
||||
and this constant will also be set to `false`.
|
||||
*/
|
||||
ODIN_USE_SEPARATE_MODULES :: ODIN_USE_SEPARATE_MODULES
|
||||
|
||||
/*
|
||||
`true` if Valgrind integration is supported on the target.
|
||||
*/
|
||||
ODIN_VALGRIND_SUPPORT :: ODIN_VALGRIND_SUPPORT
|
||||
|
||||
/*
|
||||
A `string` which identifies the compiler being used. The official compiler sets this to `"odin"`.
|
||||
*/
|
||||
ODIN_VENDOR :: ODIN_VENDOR
|
||||
|
||||
/*
|
||||
A `string` containing the version of the Odin compiler, typically in the format `dev-YYYY-MM`.
|
||||
*/
|
||||
ODIN_VERSION :: ODIN_VERSION
|
||||
|
||||
/*
|
||||
A `string` containing the Git hash part of the Odin version.
|
||||
Empty if `.git` could not be detected at the time the compiler was built.
|
||||
*/
|
||||
ODIN_VERSION_HASH :: ODIN_VERSION_HASH
|
||||
|
||||
/*
|
||||
An `enum` set by the `-subsystem` flag, specifying which Windows subsystem the PE file was created for.
|
||||
Possible values are:
|
||||
`.Unknown` - Default and only value on non-Windows platforms
|
||||
`.Console` - Default on Windows
|
||||
`.Windows` - Can be used by graphical applications so Windows doesn't open an empty console
|
||||
|
||||
There are some other possible values for e.g. EFI applications, but only Console and Windows are supported.
|
||||
|
||||
See also: https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header64
|
||||
*/
|
||||
ODIN_WINDOWS_SUBSYSTEM :: ODIN_WINDOWS_SUBSYSTEM
|
||||
|
||||
/*
|
||||
An `string` set by the `-subsystem` flag, specifying which Windows subsystem the PE file was created for.
|
||||
Possible values are:
|
||||
"UNKNOWN" - Default and only value on non-Windows platforms
|
||||
"CONSOLE" - Default on Windows
|
||||
"WINDOWS" - Can be used by graphical applications so Windows doesn't open an empty console
|
||||
|
||||
There are some other possible values for e.g. EFI applications, but only Console and Windows are supported.
|
||||
|
||||
See also: https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header64
|
||||
*/
|
||||
ODIN_WINDOWS_SUBSYSTEM_STRING :: ODIN_WINDOWS_SUBSYSTEM_STRING
|
||||
|
||||
/*
|
||||
`true` if LLVM supports the f16 type.
|
||||
*/
|
||||
__ODIN_LLVM_F16_SUPPORTED :: __ODIN_LLVM_F16_SUPPORTED
|
||||
|
||||
|
||||
|
||||
byte :: u8 // alias
|
||||
|
||||
@@ -119,7 +338,8 @@ jmag :: proc(value: Quaternion) -> Float ---
|
||||
kmag :: proc(value: Quaternion) -> Float ---
|
||||
conj :: proc(value: Complex_Or_Quaternion) -> Complex_Or_Quaternion ---
|
||||
|
||||
expand_values :: proc(value: Struct_Or_Array) -> (A, B, C, ...) ---
|
||||
expand_values :: proc(value: Struct_Or_Array) -> (A, B, C, ...) ---
|
||||
compress_values :: proc(values: ...) -> Struct_Or_Array_Like_Type ---
|
||||
|
||||
min :: proc(values: ..T) -> T ---
|
||||
max :: proc(values: ..T) -> T ---
|
||||
@@ -130,3 +350,6 @@ soa_zip :: proc(slices: ...) -> #soa[]Struct ---
|
||||
soa_unzip :: proc(value: $S/#soa[]$E) -> (slices: ...) ---
|
||||
|
||||
unreachable :: proc() -> ! ---
|
||||
|
||||
// Where T is a string, slice, dynamic array, or pointer to an array type
|
||||
raw_data :: proc(t: $T) -> rawptr
|
||||
@@ -4,6 +4,7 @@ package intrinsics
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
|
||||
// Package-Related
|
||||
is_package_imported :: proc(package_name: string) -> bool ---
|
||||
|
||||
@@ -32,11 +33,14 @@ trap :: proc() -> ! ---
|
||||
alloca :: proc(size, align: int) -> [^]u8 ---
|
||||
cpu_relax :: proc() ---
|
||||
read_cycle_counter :: proc() -> i64 ---
|
||||
read_cycle_counter_frequency :: proc() -> i64 ---
|
||||
|
||||
count_ones :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
|
||||
count_zeros :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
|
||||
count_trailing_zeros :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
|
||||
count_leading_zeros :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
|
||||
count_trailing_ones :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
|
||||
count_leading_ones :: 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) ---
|
||||
|
||||
@@ -74,7 +78,9 @@ prefetch_write_instruction :: proc(address: rawptr, #const locality: i32 /* 0..=
|
||||
prefetch_write_data :: proc(address: rawptr, #const locality: i32 /* 0..=3 */) ---
|
||||
|
||||
// Compiler Hints
|
||||
expect :: proc(val, expected_val: $T) -> T ---
|
||||
expect :: proc(val, expected_val: $T) -> T ---
|
||||
likely :: proc(val: $T) -> T where type_is_boolean(T) ---
|
||||
unlikely :: proc(val: $T) -> T where type_is_boolean(T) ---
|
||||
|
||||
// Linux and Darwin Only
|
||||
syscall :: proc(id: uintptr, args: ..uintptr) -> uintptr ---
|
||||
@@ -137,9 +143,13 @@ type_is_rune :: proc($T: typeid) -> bool ---
|
||||
type_is_float :: proc($T: typeid) -> bool ---
|
||||
type_is_complex :: proc($T: typeid) -> bool ---
|
||||
type_is_quaternion :: proc($T: typeid) -> bool ---
|
||||
type_is_string :: proc($T: typeid) -> bool ---
|
||||
type_is_typeid :: proc($T: typeid) -> bool ---
|
||||
type_is_any :: proc($T: typeid) -> bool ---
|
||||
type_is_string :: proc($T: typeid) -> bool ---
|
||||
type_is_string16 :: proc($T: typeid) -> bool ---
|
||||
type_is_cstring :: proc($T: typeid) -> bool ---
|
||||
type_is_cstring16 :: proc($T: typeid) -> bool ---
|
||||
|
||||
|
||||
type_is_endian_platform :: proc($T: typeid) -> bool ---
|
||||
type_is_endian_little :: proc($T: typeid) -> bool ---
|
||||
@@ -152,6 +162,7 @@ type_is_indexable :: proc($T: typeid) -> bool ---
|
||||
type_is_sliceable :: proc($T: typeid) -> bool ---
|
||||
type_is_comparable :: proc($T: typeid) -> bool ---
|
||||
type_is_simple_compare :: proc($T: typeid) -> bool --- // easily compared using memcmp (== and !=)
|
||||
type_is_nearly_simple_compare :: proc($T: typeid) -> bool --- // easily compared using memcmp (including floats)
|
||||
type_is_dereferenceable :: proc($T: typeid) -> bool ---
|
||||
type_is_valid_map_key :: proc($T: typeid) -> bool ---
|
||||
type_is_valid_matrix_elements :: proc($T: typeid) -> bool ---
|
||||
@@ -169,8 +180,12 @@ type_is_union :: proc($T: typeid) -> bool ---
|
||||
type_is_enum :: proc($T: typeid) -> bool ---
|
||||
type_is_proc :: proc($T: typeid) -> bool ---
|
||||
type_is_bit_set :: proc($T: typeid) -> bool ---
|
||||
type_is_bit_field :: proc($T: typeid) -> bool ---
|
||||
type_is_simd_vector :: proc($T: typeid) -> bool ---
|
||||
type_is_matrix :: proc($T: typeid) -> bool ---
|
||||
type_is_fixed_capacity_dynamic_array :: proc($T: typeid) -> bool ---
|
||||
|
||||
type_is_internally_pointer_like :: proc($T: typeid) -> bool ---
|
||||
|
||||
type_has_nil :: proc($T: typeid) -> bool ---
|
||||
|
||||
@@ -193,12 +208,17 @@ type_bit_set_underlying_type :: proc($T: typeid) -> typeid where type_is_bit_set
|
||||
type_has_field :: proc($T: typeid, $name: string) -> bool ---
|
||||
type_field_type :: proc($T: typeid, $name: string) -> typeid ---
|
||||
|
||||
type_field_bit_size :: proc($T: typeid, $name: string) -> int where type_is_bit_field(T) ---
|
||||
type_field_bit_offset :: proc($T: typeid, $name: string) -> int where type_is_bit_field(T) ---
|
||||
|
||||
type_proc_parameter_count :: proc($T: typeid) -> int where type_is_proc(T) ---
|
||||
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_return_type :: proc($T: typeid, index: int) -> typeid where type_is_proc(T) ---
|
||||
|
||||
type_proc_calling_convention :: proc($T: typeid) -> Odin_Calling_Convention where type_is_proc(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) ---
|
||||
|
||||
@@ -208,10 +228,17 @@ type_polymorphic_record_parameter_value :: proc($T: typeid, index: int) -> $V --
|
||||
type_is_specialized_polymorphic_record :: proc($T: typeid) -> bool ---
|
||||
type_is_unspecialized_polymorphic_record :: proc($T: typeid) -> bool ---
|
||||
|
||||
type_is_subtype_of :: proc($T, $U: typeid) -> bool ---
|
||||
type_is_subtype_of :: proc($T, $U: typeid) -> bool ---
|
||||
type_is_superset_of :: proc($Super, $Sub: typeid) -> bool ---
|
||||
|
||||
type_field_index_of :: proc($T: typeid, $name: string) -> uintptr ---
|
||||
|
||||
type_fixed_capacity_dynamic_array_len_offset :: proc($T: typeid/[dynamic; $N]$E) -> uintptr ---
|
||||
|
||||
// "Contiguous" means that the set of enum constants, when sorted, have a difference of either 0 or 1 between consecutive values.
|
||||
// This is the exact opposite of "sparse".
|
||||
type_enum_is_contiguous :: proc($T: typeid) -> bool where type_is_enum(T) ---
|
||||
|
||||
type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, rawptr) -> bool) where type_is_comparable(T) ---
|
||||
type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) where type_is_comparable(T) ---
|
||||
|
||||
@@ -221,12 +248,25 @@ type_map_cell_info :: proc($T: typeid) -> ^runtime.Map_Cell_Info ---
|
||||
type_convert_variants_to_pointers :: proc($T: typeid) -> typeid where type_is_union(T) ---
|
||||
type_merge :: proc($U, $V: typeid) -> typeid where type_is_union(U), type_is_union(V) ---
|
||||
|
||||
type_integer_to_unsigned :: proc($T: typeid) -> type where type_is_integer(T), !type_is_unsigned(T) ---
|
||||
type_integer_to_signed :: proc($T: typeid) -> type where type_is_integer(T), type_is_unsigned(T) ---
|
||||
|
||||
type_has_shared_fields :: proc($U, $V: typeid) -> bool where type_is_struct(U), type_is_struct(V) ---
|
||||
|
||||
|
||||
|
||||
// Returns the canonicalized name of the type, of which is used to produce the pseudo-unique 'typeid'
|
||||
type_canonical_name :: proc($T: typeid) -> string ---
|
||||
|
||||
constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
|
||||
|
||||
constant_log2 :: proc($v: $T) -> T where type_is_integer(T) ---
|
||||
|
||||
constant_floor :: proc($v: $T) -> T where type_is_integer(T) || type_is_float(T) ---
|
||||
constant_trunc :: proc($v: $T) -> T where type_is_integer(T) || type_is_float(T) ---
|
||||
constant_ceil :: proc($v: $T) -> T where type_is_integer(T) || type_is_float(T) ---
|
||||
constant_round :: proc($v: $T) -> T where type_is_integer(T) || type_is_float(T) ---
|
||||
|
||||
// SIMD related
|
||||
simd_add :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||
simd_sub :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||
@@ -306,6 +346,7 @@ simd_indices :: proc($T: typeid/#simd[$N]$E) -> T where type_is_numeric(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_runtime_swizzle :: proc(table: #simd[N]T, indices: #simd[N]T) -> #simd[N]T where type_is_integer(T) ---
|
||||
|
||||
// Lane-wise operations
|
||||
simd_ceil :: proc(a: #simd[N]any_float) -> #simd[N]any_float ---
|
||||
@@ -314,7 +355,11 @@ simd_trunc :: proc(a: #simd[N]any_float) -> #simd[N]any_float ---
|
||||
// rounding to the nearest integral value; if two values are equally near, rounds to the even one
|
||||
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_approx_recip :: proc(x: #simd[N]T) -> #simd[N]T where type_is_float(T)) ---
|
||||
simd_approx_recip_sqrt :: proc(x: #simd[N]T) -> #simd[N]T where type_is_float(T)) ---
|
||||
|
||||
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_signed :: proc(v: #simd[N]T) -> #simd[N]Integer where size_of(T) == size_of(Integer), !type_is_unsigned(Integer) ---
|
||||
|
||||
// equivalent to a swizzle with descending indices, e.g. reserve(a, 3, 2, 1, 0)
|
||||
simd_lanes_reverse :: proc(a: #simd[N]T) -> #simd[N]T ---
|
||||
@@ -322,6 +367,19 @@ simd_lanes_reverse :: proc(a: #simd[N]T) -> #simd[N]T ---
|
||||
simd_lanes_rotate_left :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
|
||||
simd_lanes_rotate_right :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
|
||||
|
||||
// return {b[0], a[1], b[2], a[3], ...}
|
||||
simd_odd_even :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||
|
||||
// Returns the sums of N consecutive lanes
|
||||
simd_sums_of_n :: proc(a: #simd[LANES]T, $N: uint) -> #simd[LANES/N]T where is_power_of_two(N) ---
|
||||
|
||||
simd_pairwise_add :: proc(a, b: #simd[LANES]T) -> #simd[LANES]T where LANES % 2 == 0 ---
|
||||
simd_pairwise_sub :: proc(a, b: #simd[LANES]T) -> #simd[LANES]T where LANES % 2 == 0 ---
|
||||
|
||||
simd_interleave :: proc(a, ..#simd[LANES/N]T) -> #simd[LANES]T where N >= 1 ---
|
||||
simd_deinterleave :: proc(a: #simd[LANES]T, $N: uint) -> (..#simd[LANES/N]T) where N >= 1, LANES % N == 0 --- // returns N multiple vectors
|
||||
|
||||
|
||||
// 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
|
||||
@@ -330,6 +388,9 @@ simd_lanes_rotate_right :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
|
||||
has_target_feature :: proc($test: $T) -> bool where type_is_string(T) || type_is_proc(T) ---
|
||||
|
||||
|
||||
// Utility Calls
|
||||
concatenate :: proc(x, y: $T, z: ..T) -> T where type_is_array(T) || type_is_slice(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) ---
|
||||
|
||||
@@ -353,22 +414,34 @@ x86_cpuid :: proc(ax, cx: u32) -> (eax, ebx, ecx, edx: u32) ---
|
||||
x86_xgetbv :: proc(cx: u32) -> (eax, edx: u32) ---
|
||||
|
||||
|
||||
// C specific things
|
||||
c_va_list :: struct{/*platform specific implementation*/}
|
||||
|
||||
c_va_start :: proc(list: ^c_va_list, /*#c_vararg parameter*/ args: ..$T) ---
|
||||
c_va_end :: proc(list: ^c_va_list) ---
|
||||
c_va_copy :: proc(dst, src: ^c_va_list) ---
|
||||
c_va_arg :: proc(list: ^c_va_list, $T: typeid) -> T ---
|
||||
|
||||
|
||||
// Darwin targets only
|
||||
objc_object :: struct{}
|
||||
objc_selector :: struct{}
|
||||
objc_class :: struct{}
|
||||
objc_ivar :: struct{}
|
||||
|
||||
objc_id :: ^objc_object
|
||||
objc_SEL :: ^objc_selector
|
||||
objc_Class :: ^objc_class
|
||||
objc_Ivar :: ^objc_ivar
|
||||
objc_id :: ^objc_object
|
||||
objc_SEL :: ^objc_selector
|
||||
objc_Class :: ^objc_class
|
||||
objc_Ivar :: ^objc_ivar
|
||||
objc_instancetype :: distinct objc_id
|
||||
|
||||
objc_find_selector :: proc($name: string) -> objc_SEL ---
|
||||
objc_register_selector :: proc($name: string) -> objc_SEL ---
|
||||
objc_find_class :: proc($name: string) -> objc_Class ---
|
||||
objc_register_class :: proc($name: string) -> objc_Class ---
|
||||
objc_ivar_get :: proc(self: ^$T) -> ^$U ---
|
||||
objc_block :: proc(invoke: $T, ..any) -> ^Objc_Block(T) where type_is_proc(T) ---
|
||||
objc_super :: proc(obj: ^$T) -> ^$U where type_is_subtype_of(T, objc_object) && type_is_subtype_of(U, objc_object) ---
|
||||
|
||||
valgrind_client_request :: proc(default: uintptr, request: uintptr, a0, a1, a2, a3, a4: uintptr) -> uintptr ---
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
//
|
||||
// IMPORTANT NOTE(bill): `type_info_of` cannot be used within a
|
||||
// #shared_global_scope due to the internals of the compiler.
|
||||
// This could change at a later date if the all these data structures are
|
||||
// This could change at a later date if all these data structures are
|
||||
// implemented within the compiler rather than in this "preload" file
|
||||
//
|
||||
#+no-instrumentation
|
||||
@@ -23,8 +23,27 @@ package runtime
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
/*
|
||||
Fast_Math_Flag :: enum u8 {
|
||||
Allow_Reassoc = 0,
|
||||
No_NaNs = 1,
|
||||
No_Infs = 2,
|
||||
No_Signed_Zeros = 3,
|
||||
Allow_Reciprocal = 4,
|
||||
Allow_Contract = 5,
|
||||
Approx_Func = 6,
|
||||
}
|
||||
*/
|
||||
Fast_Math_Flag :: intrinsics.Fast_Math_Flag
|
||||
|
||||
// Fast_Math_Flags :: distinct bit_set[Fast_Math_Flag; u32]
|
||||
Fast_Math_Flags :: intrinsics.Fast_Math_Flags
|
||||
|
||||
|
||||
// NOTE(bill): This must match the compiler's
|
||||
Calling_Convention :: enum u8 {
|
||||
|
||||
/*
|
||||
enum u8 {
|
||||
Invalid = 0,
|
||||
Odin = 1,
|
||||
Contextless = 2,
|
||||
@@ -39,7 +58,13 @@ Calling_Convention :: enum u8 {
|
||||
|
||||
Win64 = 9,
|
||||
SysV = 10,
|
||||
|
||||
Preserve_None = 11,
|
||||
Preserve_Most = 12,
|
||||
Preserve_All = 13,
|
||||
}
|
||||
*/
|
||||
Calling_Convention :: type_of(ODIN_DEFAULT_CALLING_CONVENTION)
|
||||
|
||||
Type_Info_Enum_Value :: distinct i64
|
||||
|
||||
@@ -61,6 +86,11 @@ Type_Info_Struct_Soa_Kind :: enum u8 {
|
||||
Dynamic = 3,
|
||||
}
|
||||
|
||||
Type_Info_String_Encoding_Kind :: enum u8 {
|
||||
UTF_8 = 0,
|
||||
UTF_16 = 1,
|
||||
}
|
||||
|
||||
// Variant Types
|
||||
Type_Info_Named :: struct {
|
||||
name: string,
|
||||
@@ -73,7 +103,7 @@ Type_Info_Rune :: struct {}
|
||||
Type_Info_Float :: struct {endianness: Platform_Endianness}
|
||||
Type_Info_Complex :: struct {}
|
||||
Type_Info_Quaternion :: struct {}
|
||||
Type_Info_String :: struct {is_cstring: bool}
|
||||
Type_Info_String :: struct {is_cstring: bool, encoding: Type_Info_String_Encoding_Kind}
|
||||
Type_Info_Boolean :: struct {}
|
||||
Type_Info_Any :: struct {}
|
||||
Type_Info_Type_Id :: struct {}
|
||||
@@ -113,10 +143,11 @@ Type_Info_Parameters :: struct { // Only used for procedures parameters and resu
|
||||
|
||||
Type_Info_Struct_Flags :: distinct bit_set[Type_Info_Struct_Flag; u8]
|
||||
Type_Info_Struct_Flag :: enum u8 {
|
||||
packed = 0,
|
||||
raw_union = 1,
|
||||
no_copy = 2,
|
||||
align = 3,
|
||||
packed = 0,
|
||||
raw_union = 1,
|
||||
all_or_none = 2,
|
||||
align = 3,
|
||||
simple = 4,
|
||||
}
|
||||
|
||||
Type_Info_Struct :: struct {
|
||||
@@ -131,7 +162,7 @@ Type_Info_Struct :: struct {
|
||||
|
||||
flags: Type_Info_Struct_Flags,
|
||||
|
||||
// These are only set iff this structure is an SOA structure
|
||||
// These are only set if and only if (⟺) this structure is an SOA structure
|
||||
soa_kind: Type_Info_Struct_Soa_Kind,
|
||||
soa_len: i32,
|
||||
soa_base_type: ^Type_Info,
|
||||
@@ -160,10 +191,11 @@ Type_Info_Map :: struct {
|
||||
map_info: ^Map_Info,
|
||||
}
|
||||
Type_Info_Bit_Set :: struct {
|
||||
elem: ^Type_Info,
|
||||
underlying: ^Type_Info, // Possibly nil
|
||||
lower: i64,
|
||||
upper: i64,
|
||||
elem: ^Type_Info,
|
||||
underlying: ^Type_Info,
|
||||
explicit_underlying: bool, // false = bit_set[T], true = bit_set[T, U]
|
||||
lower: i64,
|
||||
upper: i64,
|
||||
}
|
||||
Type_Info_Simd_Vector :: struct {
|
||||
elem: ^Type_Info,
|
||||
@@ -195,6 +227,14 @@ Type_Info_Bit_Field :: struct {
|
||||
field_count: int,
|
||||
}
|
||||
|
||||
Type_Info_Fixed_Capacity_Dynamic_Array :: struct {
|
||||
elem: ^Type_Info,
|
||||
elem_size: int,
|
||||
capacity: int,
|
||||
len_offset: uintptr,
|
||||
}
|
||||
|
||||
|
||||
Type_Info_Flag :: enum u8 {
|
||||
Comparable = 0,
|
||||
Simple_Compare = 1,
|
||||
@@ -235,6 +275,7 @@ Type_Info :: struct {
|
||||
Type_Info_Matrix,
|
||||
Type_Info_Soa_Pointer,
|
||||
Type_Info_Bit_Field,
|
||||
Type_Info_Fixed_Capacity_Dynamic_Array,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -259,7 +300,79 @@ when ODIN_OS == .Windows {
|
||||
dll_instance: rawptr
|
||||
}
|
||||
|
||||
// IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it)
|
||||
|
||||
// This is safe to change. The log2 size of a cache-line. At minimum it has to
|
||||
// be six though. Higher cache line sizes are permitted.
|
||||
MAP_CACHE_LINE_LOG2 :: 6
|
||||
|
||||
// The size of a cache-line.
|
||||
MAP_CACHE_LINE_SIZE :: 1 << MAP_CACHE_LINE_LOG2
|
||||
|
||||
// The minimum cache-line size allowed by this implementation is 64 bytes since
|
||||
// we need 6 bits in the base pointer to store the integer log2 capacity, which
|
||||
// at maximum is 63. Odin uses signed integers to represent length and capacity,
|
||||
// so only 63 bits are needed in the maximum case.
|
||||
#assert(MAP_CACHE_LINE_SIZE >= 64)
|
||||
|
||||
// Map_Cell type that packs multiple T in such a way to ensure that each T stays
|
||||
// aligned by align_of(T) and such that align_of(Map_Cell(T)) % MAP_CACHE_LINE_SIZE == 0
|
||||
//
|
||||
// This means a value of type T will never straddle a cache-line.
|
||||
//
|
||||
// When multiple Ts can fit in a single cache-line the data array will have more
|
||||
// than one element. When it cannot, the data array will have one element and
|
||||
// an array of Map_Cell(T) will be padded to stay a multiple of MAP_CACHE_LINE_SIZE.
|
||||
//
|
||||
// We rely on the type system to do all the arithmetic and padding for us here.
|
||||
//
|
||||
// The usual array[index] indexing for []T backed by a []Map_Cell(T) becomes a bit
|
||||
// more involved as there now may be internal padding. The indexing now becomes
|
||||
//
|
||||
// N :: len(Map_Cell(T){}.data)
|
||||
// i := index / N
|
||||
// j := index % N
|
||||
// cell[i].data[j]
|
||||
//
|
||||
// However, since len(Map_Cell(T){}.data) is a compile-time constant, there are some
|
||||
// optimizations we can do to eliminate the need for any divisions as N will
|
||||
// be bounded by [1, 64).
|
||||
//
|
||||
// In the optimal case, len(Map_Cell(T){}.data) = 1 so the cell array can be treated
|
||||
// as a regular array of T, which is the case for hashes.
|
||||
Map_Cell :: struct($T: typeid) #align(MAP_CACHE_LINE_SIZE) {
|
||||
data: [MAP_CACHE_LINE_SIZE / size_of(T) when 0 < size_of(T) && size_of(T) < MAP_CACHE_LINE_SIZE else 1]T,
|
||||
}
|
||||
|
||||
// So we can operate on a cell data structure at runtime without any type
|
||||
// information, we have a simple table that stores some traits about the cell.
|
||||
//
|
||||
// 32-bytes on 64-bit
|
||||
// 16-bytes on 32-bit
|
||||
Map_Cell_Info :: struct {
|
||||
size_of_type: uintptr, // 8-bytes on 64-bit, 4-bytes on 32-bits
|
||||
align_of_type: uintptr, // 8-bytes on 64-bit, 4-bytes on 32-bits
|
||||
size_of_cell: uintptr, // 8-bytes on 64-bit, 4-bytes on 32-bits
|
||||
elements_per_cell: uintptr, // 8-bytes on 64-bit, 4-bytes on 32-bits
|
||||
}
|
||||
|
||||
Map_Hash :: uintptr
|
||||
|
||||
// When working with the type-erased structure at runtime we need information
|
||||
// about the map to make working with it possible. This info structure stores
|
||||
// that.
|
||||
//
|
||||
// `Map_Info` and `Map_Cell_Info` are read only data structures and cannot be
|
||||
// modified after creation
|
||||
//
|
||||
// 32-bytes on 64-bit
|
||||
// 16-bytes on 32-bit
|
||||
Map_Info :: struct {
|
||||
ks: ^Map_Cell_Info, // 8-bytes on 64-bit, 4-bytes on 32-bit
|
||||
vs: ^Map_Cell_Info, // 8-bytes on 64-bit, 4-bytes on 32-bit
|
||||
key_hasher: proc "contextless" (key: rawptr, seed: Map_Hash) -> Map_Hash, // 8-bytes on 64-bit, 4-bytes on 32-bit
|
||||
key_equal: proc "contextless" (lhs, rhs: rawptr) -> bool, // 8-bytes on 64-bit, 4-bytes on 32-bit
|
||||
}
|
||||
|
||||
|
||||
|
||||
Source_Code_Location :: struct {
|
||||
@@ -397,6 +510,11 @@ Raw_String :: struct {
|
||||
len: int,
|
||||
}
|
||||
|
||||
Raw_String16 :: struct {
|
||||
data: [^]u16,
|
||||
len: int,
|
||||
}
|
||||
|
||||
Raw_Slice :: struct {
|
||||
data: rawptr,
|
||||
len: int,
|
||||
@@ -409,6 +527,11 @@ Raw_Dynamic_Array :: struct {
|
||||
allocator: Allocator,
|
||||
}
|
||||
|
||||
Raw_Fixed_Capacity_Dynamic_Array :: struct($Capacity: uint, $T: typeid) {
|
||||
data: [Capacity]T,
|
||||
len: int,
|
||||
}
|
||||
|
||||
// The raw, type-erased representation of a map.
|
||||
//
|
||||
// 32-bytes on 64-bit
|
||||
@@ -450,6 +573,12 @@ Raw_Cstring :: struct {
|
||||
}
|
||||
#assert(size_of(Raw_Cstring) == size_of(cstring))
|
||||
|
||||
Raw_Cstring16 :: struct {
|
||||
data: [^]u16,
|
||||
}
|
||||
#assert(size_of(Raw_Cstring16) == size_of(cstring16))
|
||||
|
||||
|
||||
Raw_Soa_Pointer :: struct {
|
||||
data: rawptr,
|
||||
index: int,
|
||||
@@ -473,11 +602,9 @@ Raw_Quaternion256_Vector_Scalar :: struct {vector: [3]f64, scalar: f64}
|
||||
Windows,
|
||||
Darwin,
|
||||
Linux,
|
||||
Essence,
|
||||
FreeBSD,
|
||||
OpenBSD,
|
||||
NetBSD,
|
||||
Haiku,
|
||||
WASI,
|
||||
JS,
|
||||
Orca,
|
||||
@@ -529,7 +656,6 @@ Odin_Build_Mode_Type :: type_of(ODIN_BUILD_MODE)
|
||||
/*
|
||||
// Defined internally by the compiler
|
||||
Odin_Endian_Type :: enum int {
|
||||
Unknown,
|
||||
Little,
|
||||
Big,
|
||||
}
|
||||
@@ -542,11 +668,9 @@ ALL_ODIN_OS_TYPES :: Odin_OS_Types{
|
||||
.Windows,
|
||||
.Darwin,
|
||||
.Linux,
|
||||
.Essence,
|
||||
.FreeBSD,
|
||||
.OpenBSD,
|
||||
.NetBSD,
|
||||
.Haiku,
|
||||
.WASI,
|
||||
.JS,
|
||||
.Orca,
|
||||
@@ -557,7 +681,8 @@ ALL_ODIN_OS_TYPES :: Odin_OS_Types{
|
||||
// Defined internally by the compiler
|
||||
Odin_Platform_Subtarget_Type :: enum int {
|
||||
Default,
|
||||
iOS,
|
||||
iPhone,
|
||||
iPhoneSimulator
|
||||
Android,
|
||||
}
|
||||
*/
|
||||
@@ -565,6 +690,8 @@ Odin_Platform_Subtarget_Type :: type_of(ODIN_PLATFORM_SUBTARGET)
|
||||
|
||||
Odin_Platform_Subtarget_Types :: bit_set[Odin_Platform_Subtarget_Type]
|
||||
|
||||
@(builtin)
|
||||
ODIN_PLATFORM_SUBTARGET_IOS :: ODIN_PLATFORM_SUBTARGET == .iPhone || ODIN_PLATFORM_SUBTARGET == .iPhoneSimulator
|
||||
|
||||
/*
|
||||
// Defined internally by the compiler
|
||||
@@ -617,6 +744,8 @@ _cleanup_runtime_contextless :: proc "contextless" () {
|
||||
/////////////////////////////
|
||||
|
||||
|
||||
// type_info_base returns the base-type of a `^Type_Info` stripping the `distinct`ness from the first level
|
||||
@(require_results)
|
||||
type_info_base :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
|
||||
if info == nil {
|
||||
return nil
|
||||
@@ -632,7 +761,10 @@ type_info_base :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
|
||||
return base
|
||||
}
|
||||
|
||||
|
||||
// type_info_core returns the core-type of a `^Type_Info` stripping the `distinct`ness from the first level AND/OR
|
||||
// returns the backing integer type of an enum `^Type_Info`.
|
||||
// This is also aliased as `type_info_base_without_enum`
|
||||
@(require_results)
|
||||
type_info_core :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
|
||||
if info == nil {
|
||||
return nil
|
||||
@@ -649,8 +781,36 @@ type_info_core :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
|
||||
|
||||
// type_info_underlying returns the underlying (backing) type of a `^Type_Info` stripping the `distinct`ness from the first level AND/OR
|
||||
// returns the backing integer type of an enum `^Type_Info` AND/OR the underlying integer type of a bit_set or bit_field.
|
||||
@(require_results)
|
||||
type_info_underlying :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
|
||||
if info == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
base := info
|
||||
loop: for {
|
||||
#partial switch i in base.variant {
|
||||
case Type_Info_Named: base = i.base
|
||||
case Type_Info_Enum: base = i.base
|
||||
case Type_Info_Bit_Set: base = i.underlying
|
||||
case Type_Info_Bit_Field: base = i.backing_type
|
||||
case: break loop
|
||||
}
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
// `type_info_base_without_enum` returns the core-type of a `^Type_Info` stripping the `distinct`ness from the first level AND/OR
|
||||
// returns the backing integer type of an enum or bit_set `^Type_Info`.
|
||||
// This is also aliased as `type_info_core`
|
||||
type_info_base_without_enum :: type_info_core
|
||||
|
||||
@(require_results)
|
||||
__type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info #no_bounds_check {
|
||||
n := u64(len(type_table))
|
||||
i := transmute(u64)id % n
|
||||
@@ -665,16 +825,32 @@ __type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info #no_bounds_check
|
||||
}
|
||||
|
||||
when !ODIN_NO_RTTI {
|
||||
// typeid_base returns the base-type of a `typeid` stripping the `distinct`ness from the first level
|
||||
@(require_results)
|
||||
typeid_base :: proc "contextless" (id: typeid) -> typeid {
|
||||
ti := type_info_of(id)
|
||||
ti = type_info_base(ti)
|
||||
return ti.id
|
||||
}
|
||||
// typeid_core returns the core-type of a `typeid` stripping the `distinct`ness from the first level AND/OR
|
||||
// returns the backing integer type of an enum `typeid`.
|
||||
// This is also aliased as `typeid_base_without_enum`
|
||||
@(require_results)
|
||||
typeid_core :: proc "contextless" (id: typeid) -> typeid {
|
||||
ti := type_info_core(type_info_of(id))
|
||||
return ti.id
|
||||
}
|
||||
|
||||
// typeid_base_without_enum returns the core-type of a `typeid` stripping the `distinct`ness from the first level AND/OR
|
||||
// returns the backing integer type of an enum or bit_set `typeid`.
|
||||
// This is also aliased as `typeid_core`
|
||||
typeid_base_without_enum :: typeid_core
|
||||
|
||||
@(require_results)
|
||||
typeid_underlying :: proc "contextless" (id: typeid) -> typeid {
|
||||
ti := type_info_underlying(type_info_of(id))
|
||||
return ti.id
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -689,11 +865,15 @@ default_logger_proc :: proc(data: rawptr, level: Logger_Level, text: string, opt
|
||||
// Nothing
|
||||
}
|
||||
|
||||
// Returns the default logger used by `context.logger`
|
||||
@(require_results)
|
||||
default_logger :: proc() -> Logger {
|
||||
return Logger{default_logger_proc, nil, Logger_Level.Debug, nil}
|
||||
}
|
||||
|
||||
|
||||
// Returns the default `context`
|
||||
@(require_results)
|
||||
default_context :: proc "contextless" () -> Context {
|
||||
c: Context
|
||||
__init_context(&c)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -105,8 +105,8 @@ make_soa_aligned :: proc($T: typeid/#soa[]$E, #any_int length, alignment: int, a
|
||||
allocator := allocator
|
||||
if allocator.procedure == nil {
|
||||
allocator = context.allocator
|
||||
assert(allocator.procedure != nil)
|
||||
}
|
||||
assert(allocator.procedure != nil)
|
||||
|
||||
new_bytes: []byte
|
||||
new_bytes, err = allocator.procedure(
|
||||
@@ -178,8 +178,31 @@ resize_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int length: int, loc := #cal
|
||||
if array == nil {
|
||||
return nil
|
||||
}
|
||||
reserve_soa(array, length, loc) or_return
|
||||
|
||||
footer := raw_soa_footer(array)
|
||||
|
||||
if length > footer.cap {
|
||||
reserve_soa(array, length, loc) or_return
|
||||
} else if size_of(E) > 0 && length > footer.len {
|
||||
ti := type_info_base(type_info_of(typeid_of(T)))
|
||||
si := &ti.variant.(Type_Info_Struct)
|
||||
|
||||
field_count := len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)
|
||||
|
||||
data := (^rawptr)(array)^
|
||||
|
||||
soa_offset := 0
|
||||
for i in 0..<field_count {
|
||||
type := si.types[i].variant.(Type_Info_Multi_Pointer).elem
|
||||
|
||||
soa_offset = align_forward_int(soa_offset, align_of(E))
|
||||
|
||||
mem_zero(rawptr(uintptr(data) + uintptr(soa_offset) + uintptr(type.size * footer.len)), type.size * (length - footer.len))
|
||||
|
||||
soa_offset += type.size * footer.cap
|
||||
}
|
||||
}
|
||||
|
||||
footer.len = length
|
||||
return nil
|
||||
}
|
||||
@@ -217,8 +240,8 @@ _reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, zero_memory: boo
|
||||
|
||||
if array.allocator.procedure == nil {
|
||||
array.allocator = context.allocator
|
||||
assert(array.allocator.procedure != nil)
|
||||
}
|
||||
assert(array.allocator.procedure != nil)
|
||||
|
||||
footer := raw_soa_footer(array)
|
||||
if size_of(E) == 0 {
|
||||
@@ -249,17 +272,77 @@ _reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, zero_memory: boo
|
||||
|
||||
old_data := (^rawptr)(array)^
|
||||
|
||||
resize: if old_data != nil {
|
||||
|
||||
new_bytes, resize_err := array.allocator.procedure(
|
||||
array.allocator.data, .Resize_Non_Zeroed, new_size, max_align,
|
||||
old_data, old_size, loc,
|
||||
)
|
||||
new_data := raw_data(new_bytes)
|
||||
|
||||
#partial switch resize_err {
|
||||
case .Mode_Not_Implemented: break resize
|
||||
case .None: // continue resizing
|
||||
case: return resize_err
|
||||
}
|
||||
|
||||
footer.cap = capacity
|
||||
|
||||
old_offset := 0
|
||||
new_offset := 0
|
||||
|
||||
// Correct data memory
|
||||
// from: |x x y y z z _ _ _|
|
||||
// to: |x x _ y y _ z z _|
|
||||
|
||||
// move old data to the end of the new allocation to avoid overlap
|
||||
old_data = rawptr(uintptr(new_data) + uintptr(new_size - old_size))
|
||||
mem_copy(old_data, new_data, old_size)
|
||||
|
||||
// now: |_ _ _ x x y y z z|
|
||||
|
||||
for i in 0..<field_count {
|
||||
type := si.types[i].variant.(Type_Info_Multi_Pointer).elem
|
||||
|
||||
old_offset = align_forward_int(old_offset, max_align)
|
||||
new_offset = align_forward_int(new_offset, max_align)
|
||||
|
||||
new_data_elem := rawptr(uintptr(new_data) + uintptr(new_offset))
|
||||
old_data_elem := rawptr(uintptr(old_data) + uintptr(old_offset))
|
||||
|
||||
old_size_elem := type.size * old_cap
|
||||
new_size_elem := type.size * capacity
|
||||
|
||||
mem_copy(new_data_elem, old_data_elem, old_size_elem)
|
||||
|
||||
(^rawptr)(uintptr(array) + i*size_of(rawptr))^ = new_data_elem
|
||||
|
||||
if zero_memory {
|
||||
mem_zero(rawptr(uintptr(new_data_elem) + uintptr(old_size_elem)), new_size_elem - old_size_elem)
|
||||
}
|
||||
|
||||
old_offset += old_size_elem
|
||||
new_offset += new_size_elem
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
new_bytes := array.allocator.procedure(
|
||||
array.allocator.data, .Alloc if zero_memory else .Alloc_Non_Zeroed, new_size, max_align,
|
||||
nil, old_size, loc,
|
||||
) or_return
|
||||
new_data := raw_data(new_bytes)
|
||||
|
||||
|
||||
footer.cap = capacity
|
||||
|
||||
old_offset := 0
|
||||
new_offset := 0
|
||||
|
||||
// Correct data memory
|
||||
// from: |x x y y z z| ... |_ _ _ _ _ _ _ _ _|
|
||||
// to: |x x _ y y _ z z _|
|
||||
|
||||
for i in 0..<field_count {
|
||||
type := si.types[i].variant.(Type_Info_Multi_Pointer).elem
|
||||
|
||||
@@ -277,10 +360,12 @@ _reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, zero_memory: boo
|
||||
new_offset += type.size * capacity
|
||||
}
|
||||
|
||||
array.allocator.procedure(
|
||||
array.allocator.data, .Free, 0, max_align,
|
||||
old_data, old_size, loc,
|
||||
) or_return
|
||||
if old_data != nil {
|
||||
array.allocator.procedure(
|
||||
array.allocator.data, .Free, 0, max_align,
|
||||
old_data, old_size, loc,
|
||||
) or_return
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -416,6 +501,121 @@ append_soa :: proc{
|
||||
}
|
||||
|
||||
|
||||
// `append_nothing_soa` appends an empty value to a dynamic SOA array. It returns `1, nil` if successful, and `0, err` when it was not possible,
|
||||
// whatever `err` happens to be.
|
||||
@builtin
|
||||
append_nothing_soa :: proc(array: ^$T/#soa[dynamic]$E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
if array == nil {
|
||||
return 0, nil
|
||||
}
|
||||
prev_len := len(array)
|
||||
resize_soa(array, len(array)+1, loc) or_return
|
||||
return len(array)-prev_len, nil
|
||||
}
|
||||
|
||||
|
||||
// `inject_at_elem_soa` injects an element in a dynamic SOA array at a specified index and moves the previous elements after that index "across"
|
||||
@builtin
|
||||
inject_at_elem_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int index: int, #no_broadcast arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||
when !ODIN_NO_BOUNDS_CHECK {
|
||||
ensure(index >= 0, "Index must be positive.", loc)
|
||||
}
|
||||
if array == nil {
|
||||
return
|
||||
}
|
||||
n := max(len(array), index)
|
||||
m :: 1
|
||||
new_len := n + m
|
||||
|
||||
resize_soa(array, new_len, loc) or_return
|
||||
|
||||
when size_of(E) != 0 {
|
||||
ti := type_info_base(type_info_of(typeid_of(T)))
|
||||
si := &ti.variant.(Type_Info_Struct)
|
||||
|
||||
field_count := len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)
|
||||
|
||||
item_offset := 0
|
||||
|
||||
arg_copy := arg
|
||||
arg_ptr := &arg_copy
|
||||
|
||||
for i in 0..<field_count {
|
||||
data := (^uintptr)(uintptr(array) + uintptr(si.offsets[i]))^
|
||||
type := si.types[i].variant.(Type_Info_Multi_Pointer).elem
|
||||
item_offset = align_forward_int(item_offset, type.align)
|
||||
|
||||
src := data + uintptr(index * type.size)
|
||||
dst := data + uintptr((index + m) * type.size)
|
||||
mem_copy(rawptr(dst), rawptr(src), (n - index) * type.size)
|
||||
|
||||
mem_copy(rawptr(src), rawptr(uintptr(arg_ptr) + uintptr(item_offset)), type.size)
|
||||
|
||||
item_offset += type.size
|
||||
}
|
||||
}
|
||||
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
// `inject_at_elems_soa` injects multiple elements in a dynamic SOA array at a specified index and moves the previous elements after that index "across"
|
||||
@builtin
|
||||
inject_at_elems_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||
when !ODIN_NO_BOUNDS_CHECK {
|
||||
ensure(index >= 0, "Index must be positive.", loc)
|
||||
}
|
||||
if array == nil {
|
||||
return
|
||||
}
|
||||
if len(args) == 0 {
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
n := max(len(array), index)
|
||||
m := len(args)
|
||||
new_len := n + m
|
||||
|
||||
resize_soa(array, new_len, loc) or_return
|
||||
|
||||
when size_of(E) != 0 {
|
||||
ti := type_info_base(type_info_of(typeid_of(T)))
|
||||
si := &ti.variant.(Type_Info_Struct)
|
||||
|
||||
field_count := len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)
|
||||
|
||||
item_offset := 0
|
||||
|
||||
args_ptr := &args[0]
|
||||
|
||||
for i in 0..<field_count {
|
||||
data := (^uintptr)(uintptr(array) + uintptr(si.offsets[i]))^
|
||||
type := si.types[i].variant.(Type_Info_Multi_Pointer).elem
|
||||
item_offset = align_forward_int(item_offset, type.align)
|
||||
|
||||
src := data + uintptr(index * type.size)
|
||||
dst := data + uintptr((index + m) * type.size)
|
||||
mem_copy(rawptr(dst), rawptr(src), (n - index) * type.size)
|
||||
|
||||
for j in 0..<len(args) {
|
||||
d := rawptr(src + uintptr(j*type.size))
|
||||
s := rawptr(uintptr(args_ptr) + uintptr(item_offset) + uintptr(j*size_of(E)))
|
||||
mem_copy(d, s, type.size)
|
||||
}
|
||||
|
||||
item_offset += type.size
|
||||
}
|
||||
}
|
||||
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
// `inject_at_soa` injects something into a dynamic SOA array at a specified index and moves the previous elements after that index "across"
|
||||
@builtin inject_at_soa :: proc{inject_at_elem_soa, inject_at_elems_soa}
|
||||
|
||||
@builtin
|
||||
delete_soa_slice :: proc(array: $T/#soa[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
field_count :: len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)
|
||||
when field_count != 0 {
|
||||
@@ -426,6 +626,7 @@ delete_soa_slice :: proc(array: $T/#soa[]$E, allocator := context.allocator, loc
|
||||
return nil
|
||||
}
|
||||
|
||||
@builtin
|
||||
delete_soa_dynamic_array :: proc(array: $T/#soa[dynamic]$E, loc := #caller_location) -> Allocator_Error {
|
||||
field_count :: len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)
|
||||
when field_count != 0 {
|
||||
@@ -444,7 +645,7 @@ delete_soa :: proc{
|
||||
delete_soa_dynamic_array,
|
||||
}
|
||||
|
||||
|
||||
@builtin
|
||||
clear_soa_dynamic_array :: proc(array: ^$T/#soa[dynamic]$E) {
|
||||
field_count :: len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)
|
||||
when field_count != 0 {
|
||||
|
||||
@@ -23,7 +23,16 @@ nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return nil, .None
|
||||
}
|
||||
|
||||
nil_allocator :: proc() -> Allocator {
|
||||
// nil_allocator returns an allocator which will return `nil` for any result.
|
||||
// * `.Alloc`, `.Alloc_Non_Zero`, `.Resize`, `.Resize_Non_Zeroed` will return `nil, .Out_Of_Memory`
|
||||
// * `.Free` will return `nil, .None`
|
||||
// * `.Free_All` will return `nil, .Mode_Not_Implemented`
|
||||
// * `.Query_Features`, `.Query_Info` will return `nil, .Mode_Not_Implemented`
|
||||
//
|
||||
// This is extremely useful for creating a dynamic array from a buffer which does not nothing
|
||||
// on a resize/reserve beyond the originally allocated memory.
|
||||
@(require_results)
|
||||
nil_allocator :: proc "contextless" () -> Allocator {
|
||||
return Allocator{
|
||||
procedure = nil_allocator_proc,
|
||||
data = nil,
|
||||
@@ -72,6 +81,10 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// panic_allocator returns an allocator which will panic for any non-zero-sized allocation or `query_info`
|
||||
//
|
||||
// This is extremely useful for to check when something does a memory operation when it should not, and thus panic.
|
||||
@(require_results)
|
||||
panic_allocator :: proc() -> Allocator {
|
||||
return Allocator{
|
||||
procedure = panic_allocator_proc,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package runtime
|
||||
|
||||
import "base:intrinsics"
|
||||
import "base:sanitizer"
|
||||
// import "base:sanitizer"
|
||||
|
||||
DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE :: uint(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE)
|
||||
|
||||
@@ -44,7 +44,7 @@ memory_block_alloc :: proc(allocator: Allocator, capacity: uint, alignment: uint
|
||||
block.base = ([^]byte)(uintptr(block) + base_offset)
|
||||
block.capacity = uint(end - uintptr(block.base))
|
||||
|
||||
sanitizer.address_poison(block.base, block.capacity)
|
||||
// sanitizer.address_poison(block.base, block.capacity)
|
||||
|
||||
// Should be zeroed
|
||||
assert(block.used == 0)
|
||||
@@ -52,10 +52,13 @@ memory_block_alloc :: proc(allocator: Allocator, capacity: uint, alignment: uint
|
||||
return
|
||||
}
|
||||
|
||||
memory_block_dealloc :: proc(block_to_free: ^Memory_Block, loc := #caller_location) {
|
||||
memory_block_dealloc :: proc "contextless" (block_to_free: ^Memory_Block, loc := #caller_location) {
|
||||
if block_to_free != nil {
|
||||
|
||||
allocator := block_to_free.allocator
|
||||
sanitizer.address_unpoison(block_to_free.base, block_to_free.capacity)
|
||||
// sanitizer.address_unpoison(block_to_free.base, block_to_free.capacity)
|
||||
context = default_context()
|
||||
context.allocator = allocator
|
||||
mem_free(block_to_free, allocator, loc)
|
||||
}
|
||||
}
|
||||
@@ -87,22 +90,13 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: uint)
|
||||
return
|
||||
}
|
||||
data = block.base[block.used+alignment_offset:][:min_size]
|
||||
sanitizer.address_unpoison(block.base[block.used:block.used+size])
|
||||
// sanitizer.address_unpoison(block.base[block.used:block.used+size])
|
||||
block.used += size
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
arena_alloc :: proc(arena: ^Arena, size, alignment: uint, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||
align_forward_uint :: proc "contextless" (ptr, align: uint) -> uint {
|
||||
p := ptr
|
||||
modulo := p & (align-1)
|
||||
if modulo != 0 {
|
||||
p += align - modulo
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
assert(alignment & (alignment-1) == 0, "non-power of two alignment", loc)
|
||||
|
||||
size := size
|
||||
@@ -167,12 +161,12 @@ arena_free_all :: proc(arena: ^Arena, loc := #caller_location) {
|
||||
if arena.curr_block != nil {
|
||||
intrinsics.mem_zero(arena.curr_block.base, arena.curr_block.used)
|
||||
arena.curr_block.used = 0
|
||||
sanitizer.address_poison(arena.curr_block.base, arena.curr_block.capacity)
|
||||
// sanitizer.address_poison(arena.curr_block.base, arena.curr_block.capacity)
|
||||
}
|
||||
arena.total_used = 0
|
||||
}
|
||||
|
||||
arena_destroy :: proc(arena: ^Arena, loc := #caller_location) {
|
||||
arena_destroy :: proc "contextless" (arena: ^Arena, loc := #caller_location) {
|
||||
for arena.curr_block != nil {
|
||||
free_block := arena.curr_block
|
||||
arena.curr_block = free_block.prev
|
||||
@@ -184,6 +178,7 @@ arena_destroy :: proc(arena: ^Arena, loc := #caller_location) {
|
||||
arena.total_capacity = 0
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
arena_allocator :: proc(arena: ^Arena) -> Allocator {
|
||||
return Allocator{arena_allocator_proc, arena}
|
||||
}
|
||||
@@ -231,8 +226,9 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
if start < old_end && old_end == block.used && new_end <= block.capacity {
|
||||
// grow data in-place, adjusting next allocation
|
||||
block.used = uint(new_end)
|
||||
arena.total_used = uint(new_end)
|
||||
data = block.base[start:new_end]
|
||||
sanitizer.address_unpoison(data)
|
||||
// sanitizer.address_unpoison(data)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -306,7 +302,7 @@ arena_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) {
|
||||
assert(block.used >= temp.used, "out of order use of arena_temp_end", loc)
|
||||
amount_to_zero := block.used-temp.used
|
||||
intrinsics.mem_zero(block.base[temp.used:], amount_to_zero)
|
||||
sanitizer.address_poison(block.base[temp.used:block.capacity])
|
||||
// sanitizer.address_poison(block.base[temp.used:block.capacity])
|
||||
block.used = temp.used
|
||||
arena.total_used -= amount_to_zero
|
||||
}
|
||||
|
||||
@@ -4,11 +4,12 @@ DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE: int : #config(DEFAULT_TEMP_ALLOCATOR_BACKIN
|
||||
NO_DEFAULT_TEMP_ALLOCATOR: bool : ODIN_OS == .Freestanding || ODIN_DEFAULT_TO_NIL_ALLOCATOR
|
||||
|
||||
when NO_DEFAULT_TEMP_ALLOCATOR {
|
||||
// `Default_Temp_Allocator` is a `nil_allocator` when `NO_DEFAULT_TEMP_ALLOCATOR` is `true`.
|
||||
Default_Temp_Allocator :: struct {}
|
||||
|
||||
default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backing_allocator := context.allocator) {}
|
||||
default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backing_allocator: Allocator) {}
|
||||
|
||||
default_temp_allocator_destroy :: proc(s: ^Default_Temp_Allocator) {}
|
||||
default_temp_allocator_destroy :: proc "contextless" (s: ^Default_Temp_Allocator) {}
|
||||
|
||||
default_temp_allocator_proc :: nil_allocator_proc
|
||||
|
||||
@@ -20,15 +21,20 @@ when NO_DEFAULT_TEMP_ALLOCATOR {
|
||||
default_temp_allocator_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) {
|
||||
}
|
||||
} else {
|
||||
// `Default_Temp_Allocator` is an `Arena` based type of allocator. See `runtime.Arena` for its implementation.
|
||||
// The default `context.temp_allocator` is typically called with `free_all(context.temp_allocator)` once per "frame-loop"
|
||||
// to prevent it from "leaking" memory.
|
||||
//
|
||||
// Note: `Default_Temp_Allocator` is a `nil_allocator` when `NO_DEFAULT_TEMP_ALLOCATOR` is `true`.
|
||||
Default_Temp_Allocator :: struct {
|
||||
arena: Arena,
|
||||
}
|
||||
|
||||
default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backing_allocator := context.allocator) {
|
||||
default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backing_allocator: Allocator) {
|
||||
_ = arena_init(&s.arena, uint(size), backing_allocator)
|
||||
}
|
||||
|
||||
default_temp_allocator_destroy :: proc(s: ^Default_Temp_Allocator) {
|
||||
default_temp_allocator_destroy :: proc "contextless" (s: ^Default_Temp_Allocator) {
|
||||
if s != nil {
|
||||
arena_destroy(&s.arena)
|
||||
s^ = {}
|
||||
@@ -56,7 +62,7 @@ when NO_DEFAULT_TEMP_ALLOCATOR {
|
||||
}
|
||||
|
||||
@(fini, private)
|
||||
_destroy_temp_allocator_fini :: proc() {
|
||||
_destroy_temp_allocator_fini :: proc "contextless" () {
|
||||
default_temp_allocator_destroy(&global_default_temp_allocator_data)
|
||||
}
|
||||
}
|
||||
@@ -70,7 +76,7 @@ DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD :: #force_inline proc(ignore := false, loc :=
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
default_temp_allocator :: proc(allocator: ^Default_Temp_Allocator) -> Allocator {
|
||||
return Allocator{
|
||||
procedure = default_temp_allocator_proc,
|
||||
|
||||
244
base/runtime/doc.odin
Normal file
244
base/runtime/doc.odin
Normal file
@@ -0,0 +1,244 @@
|
||||
|
||||
/*
|
||||
Declarations which are required by the compiler
|
||||
|
||||
## Descriptions of files
|
||||
|
||||
There are a lot of files in this package and below is described roughly what
|
||||
kind of functionality is placed in different files:
|
||||
|
||||
| File pattern | Description
|
||||
|----------------------|------------------------------------------------------|
|
||||
| `core.odin` | Contains the declarations that compiler will require to be present. Contains context-related declarations, `Type_Info` declarations and some other types used to implement the runtime and other packages. |
|
||||
| `core_builtin*.odin` | Contain `@(builtin)` declarations that can be used without importing the package. Most of them aren't required by the compiler |
|
||||
| `default_*.odin` | Contain default implementations for context allocators |
|
||||
| `entry_*.odin` | Contain OS-specific entry points |
|
||||
| `os_specific_*.odin` | Contain OS-specific utility procedures |
|
||||
| `*internal*.odin` | Contain implementations for internal procedures that can be called by the compiler |
|
||||
|
||||
## Implementing custom runtime
|
||||
|
||||
For embedded and kernel development it might be required to re-implement parts
|
||||
of the `base:runtime` package. This can include changing the default printing
|
||||
procedures that handle console output when the program panics, custom
|
||||
entry-points, tailored for a specific platform or execution environment, or
|
||||
simply switching up implementations of some procedures.
|
||||
|
||||
In case this is required, the following is suggested:
|
||||
|
||||
1. Define `$ODIN_ROOT` environment variable to point to a directory within your
|
||||
project that contains the following directories: `base/`, `core/` and `vendor/`.
|
||||
2. Inside the `$ODIN_ROOT/base` subdirectory, implement the *necessary
|
||||
declarations*.
|
||||
|
||||
What constitutes the necessary definitions is described below.
|
||||
|
||||
### Context-related
|
||||
|
||||
The compiler will require these declarations as they concern the `context`
|
||||
variable.
|
||||
|
||||
* `Maybe`
|
||||
* `Source_Code_Location`
|
||||
* `Context`
|
||||
* `Allocator`
|
||||
* `Random_Generator`
|
||||
* `Logger`
|
||||
* `__init_context`
|
||||
|
||||
### Runtime initialization/cleanup
|
||||
|
||||
These are not strictly required for compilation, but if global variables or
|
||||
`@(init)`/`@(fini)` blocks are used, these procedures need to be called inside
|
||||
the entry point.
|
||||
|
||||
* `_startup_runtime`
|
||||
* `_cleanup_runtime`
|
||||
|
||||
### Type assertion check
|
||||
|
||||
These procedures are called every time `.(Type)` expressions are used in order
|
||||
to check the union tag or the underlying type of `any` before returning the
|
||||
value of the underlying type. These are not required if `-no-type-assert` is
|
||||
specified.
|
||||
|
||||
* `type_assertion_check`
|
||||
* `type_assertion_check2` (takes in typeid)
|
||||
|
||||
### Bounds checking procedures
|
||||
|
||||
These procedures are called every time index or slicing expression are used in
|
||||
order to perform bounds-checking before the actual operation. These are not
|
||||
required if the `-no-bounds-check` option is specified.
|
||||
|
||||
* `bounds_check_error`
|
||||
* `matrix_bounds_check_error`
|
||||
* `slice_expr_error_hi`
|
||||
* `slice_expr_error_lo_hi`
|
||||
* `multi_pointer_slice_expr_error`
|
||||
|
||||
### cstring calls
|
||||
|
||||
If `cstring` or `cstring16` types are used, these procedures are required.
|
||||
|
||||
* `cstring_to_string`
|
||||
* `cstring_len`
|
||||
* `cstring16_to_string16`
|
||||
* `cstring16_len`
|
||||
|
||||
### Comparison
|
||||
|
||||
These procedures are required for comparison operators between strings and other
|
||||
compound types to function properly. If strings, structs nor unions are compared,
|
||||
only `string_eq` procedure is required.
|
||||
|
||||
* `memory_equal`
|
||||
* `memory_compare`
|
||||
* `memory_compare_zero`
|
||||
* `cstring_eq`
|
||||
* `cstring16_eq`
|
||||
* `cstring_ne`
|
||||
* `cstring16_ne`
|
||||
* `cstring_lt`
|
||||
* `cstring16_lt`
|
||||
* `cstring_gt`
|
||||
* `cstring16_gt`
|
||||
* `cstring_le`
|
||||
* `cstring16_le`
|
||||
* `cstring_ge`
|
||||
* `cstring16_ge`
|
||||
* `string_eq`
|
||||
* `string16_eq`
|
||||
* `string_ne`
|
||||
* `string16_ne`
|
||||
* `string_lt`
|
||||
* `string16_lt`
|
||||
* `string_gt`
|
||||
* `string16_gt`
|
||||
* `string_le`
|
||||
* `string16_le`
|
||||
* `string_ge`
|
||||
* `string16_ge`
|
||||
* `complex32_eq`
|
||||
* `complex32_ne`
|
||||
* `complex64_eq`
|
||||
* `complex64_ne`
|
||||
* `complex128_eq`
|
||||
* `complex128_ne`
|
||||
* `quaternion64_eq`
|
||||
* `quaternion64_ne`
|
||||
* `quaternion128_eq`
|
||||
* `quaternion128_ne`
|
||||
* `quaternion256_eq`
|
||||
* `quaternion256_ne`
|
||||
|
||||
### for-in `string` type
|
||||
|
||||
These procedures are required to iterate strings using `for ... in` loop. If this
|
||||
kind of loop isn't used, these procedures aren't required.
|
||||
|
||||
* `string_decode_rune`
|
||||
* `string_decode_last_rune` (for `#reverse for`)
|
||||
|
||||
### Required when RTTI is enabled (the vast majority of targets)
|
||||
|
||||
These declarations are required unless the `-no-rtti` compiler option is
|
||||
specified. Note that in order to be useful, some other procedures need to be
|
||||
implemented. Those procedures aren't mentioned here as the compiler won't
|
||||
complain if they're missing.
|
||||
|
||||
* `Type_Info`
|
||||
* `type_table`
|
||||
* `__type_info_of`
|
||||
|
||||
### Hashing
|
||||
|
||||
Required if maps are used
|
||||
|
||||
* `default_hasher`
|
||||
* `default_hasher_cstring`
|
||||
* `default_hasher_string`
|
||||
|
||||
### Pseudo-CRT required procedured due to LLVM but useful in general
|
||||
|
||||
* `memset`
|
||||
* `memcpy`
|
||||
* `memove`
|
||||
|
||||
### Procedures required by the LLVM backend if u128/i128 is used
|
||||
|
||||
* `umodti3`
|
||||
* `udivti3`
|
||||
* `modti3`
|
||||
* `divti3`
|
||||
* `fixdfti`
|
||||
* `fixunsdfti`
|
||||
* `fixunsdfdi`
|
||||
* `floattidf`
|
||||
* `floattidf_unsigned`
|
||||
* `truncsfhf2`
|
||||
* `truncdfhf2`
|
||||
* `gnu_h2f_ieee`
|
||||
* `gnu_f2h_ieee`
|
||||
* `extendhfsf2`
|
||||
|
||||
### Procedures required by the LLVM backend if f16 is used (WASM only)
|
||||
|
||||
* `__ashlti3`
|
||||
* `__multi3`
|
||||
|
||||
### When -no-crt is defined (windows only)
|
||||
|
||||
* `_tls_index`
|
||||
* `_fltused`
|
||||
|
||||
### Arithmetic
|
||||
|
||||
* `quo_complex32`
|
||||
* `quo_complex64`
|
||||
* `quo_complex128`
|
||||
|
||||
* `mul_quaternion64`
|
||||
* `mul_quaternion128`
|
||||
* `mul_quaternion256`
|
||||
|
||||
* `quo_quaternion64`
|
||||
* `quo_quaternion128`
|
||||
* `quo_quaternion256`
|
||||
|
||||
* `abs_complex32`
|
||||
* `abs_complex64`
|
||||
* `abs_complex128`
|
||||
|
||||
* `abs_quaternion64`
|
||||
* `abs_quaternion128`
|
||||
* `abs_quaternion256`
|
||||
|
||||
## Map specific calls
|
||||
|
||||
* `map_seed_from_map_data`
|
||||
* `__dynamic_map_check_grow` (for static map calls)
|
||||
* `map_insert_hash_dynamic` (for static map calls)
|
||||
* `__dynamic_map_get` (for dynamic map calls)
|
||||
* `__dynamic_map_set` (for dynamic map calls)
|
||||
|
||||
## Dynamic literals (`[dynamic]T` and `map[K]V`) (can be disabled with `-no-dynamic-literals`)
|
||||
|
||||
* `__dynamic_array_reserve`
|
||||
* `__dynamic_array_append`
|
||||
* `__dynamic_map_reserve`
|
||||
|
||||
### Objective-C specific
|
||||
|
||||
* `objc_lookUpClass`
|
||||
* `sel_registerName`
|
||||
* `objc_allocateClassPair`
|
||||
|
||||
### Other required declarations
|
||||
|
||||
This is required without conditions.
|
||||
|
||||
* `Load_Directory_File`
|
||||
|
||||
*/
|
||||
package runtime
|
||||
@@ -1,180 +0,0 @@
|
||||
package runtime
|
||||
|
||||
/*
|
||||
|
||||
package runtime has numerous entities (declarations) which are required by the compiler to function.
|
||||
|
||||
|
||||
## Basic types and calls (and anything they rely on)
|
||||
|
||||
Source_Code_Location
|
||||
Context
|
||||
Allocator
|
||||
Logger
|
||||
|
||||
__init_context
|
||||
_cleanup_runtime
|
||||
|
||||
|
||||
## cstring calls
|
||||
|
||||
cstring_to_string
|
||||
cstring_len
|
||||
|
||||
|
||||
|
||||
## Required when RTTI is enabled (the vast majority of targets)
|
||||
|
||||
Type_Info
|
||||
|
||||
type_table
|
||||
__type_info_of
|
||||
|
||||
|
||||
## Hashing
|
||||
|
||||
default_hasher
|
||||
default_hasher_cstring
|
||||
default_hasher_string
|
||||
|
||||
|
||||
## Pseudo-CRT required procedured due to LLVM but useful in general
|
||||
memset
|
||||
memcpy
|
||||
memove
|
||||
|
||||
|
||||
## Procedures required by the LLVM backend if u128/i128 is used
|
||||
umodti3
|
||||
udivti3
|
||||
modti3
|
||||
divti3
|
||||
fixdfti
|
||||
fixunsdfti
|
||||
fixunsdfdi
|
||||
floattidf
|
||||
floattidf_unsigned
|
||||
truncsfhf2
|
||||
truncdfhf2
|
||||
gnu_h2f_ieee
|
||||
gnu_f2h_ieee
|
||||
extendhfsf2
|
||||
|
||||
## Procedures required by the LLVM backend if f16 is used
|
||||
__ashlti3 // wasm specific
|
||||
__multi3 // wasm specific
|
||||
|
||||
|
||||
## Required an entry point is defined (i.e. 'main')
|
||||
|
||||
args__
|
||||
|
||||
|
||||
## When -no-crt is defined (and not a wasm target) (mostly due to LLVM)
|
||||
_tls_index
|
||||
_fltused
|
||||
|
||||
|
||||
## Bounds checking procedures (when not disabled with -no-bounds-check)
|
||||
|
||||
bounds_check_error
|
||||
matrix_bounds_check_error
|
||||
slice_expr_error_hi
|
||||
slice_expr_error_lo_hi
|
||||
multi_pointer_slice_expr_error
|
||||
|
||||
|
||||
## Type assertion check
|
||||
|
||||
type_assertion_check
|
||||
type_assertion_check2 // takes in typeid
|
||||
|
||||
|
||||
## Arithmetic
|
||||
|
||||
quo_complex32
|
||||
quo_complex64
|
||||
quo_complex128
|
||||
|
||||
mul_quaternion64
|
||||
mul_quaternion128
|
||||
mul_quaternion256
|
||||
|
||||
quo_quaternion64
|
||||
quo_quaternion128
|
||||
quo_quaternion256
|
||||
|
||||
abs_complex32
|
||||
abs_complex64
|
||||
abs_complex128
|
||||
|
||||
abs_quaternion64
|
||||
abs_quaternion128
|
||||
abs_quaternion256
|
||||
|
||||
|
||||
## Comparison
|
||||
|
||||
memory_equal
|
||||
memory_compare
|
||||
memory_compare_zero
|
||||
|
||||
cstring_eq
|
||||
cstring_ne
|
||||
cstring_lt
|
||||
cstring_gt
|
||||
cstring_le
|
||||
cstring_gt
|
||||
|
||||
string_eq
|
||||
string_ne
|
||||
string_lt
|
||||
string_gt
|
||||
string_le
|
||||
string_gt
|
||||
|
||||
complex32_eq
|
||||
complex32_ne
|
||||
complex64_eq
|
||||
complex64_ne
|
||||
complex128_eq
|
||||
complex128_ne
|
||||
|
||||
quaternion64_eq
|
||||
quaternion64_ne
|
||||
quaternion128_eq
|
||||
quaternion128_ne
|
||||
quaternion256_eq
|
||||
quaternion256_ne
|
||||
|
||||
|
||||
## Map specific calls
|
||||
|
||||
map_seed_from_map_data
|
||||
__dynamic_map_check_grow // static map calls
|
||||
map_insert_hash_dynamic // static map calls
|
||||
__dynamic_map_get // dynamic map calls
|
||||
__dynamic_map_set // dynamic map calls
|
||||
|
||||
|
||||
## Dynamic literals ([dynamic]T and map[K]V) (can be disabled with -no-dynamic-literals)
|
||||
|
||||
__dynamic_array_reserve
|
||||
__dynamic_array_append
|
||||
|
||||
__dynamic_map_reserve
|
||||
|
||||
|
||||
## Objective-C specific
|
||||
|
||||
objc_lookUpClass
|
||||
sel_registerName
|
||||
objc_allocateClassPair
|
||||
|
||||
|
||||
## for-in `string` type
|
||||
|
||||
string_decode_rune
|
||||
string_decode_last_rune // #reverse for
|
||||
|
||||
*/
|
||||
@@ -18,8 +18,8 @@ __dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap:
|
||||
// assuming that appending/reserving will set the allocator, if it is not already set.
|
||||
if array.allocator.procedure == nil {
|
||||
array.allocator = context.allocator
|
||||
assert(array.allocator.procedure != nil)
|
||||
}
|
||||
assert(array.allocator.procedure != nil)
|
||||
|
||||
if cap <= array.cap {
|
||||
return true
|
||||
@@ -52,8 +52,8 @@ __dynamic_array_shrink :: proc(array_: rawptr, elem_size, elem_align: int, new_c
|
||||
// assuming that appending/reserving will set the allocator, if it is not already set.
|
||||
if array.allocator.procedure == nil {
|
||||
array.allocator = context.allocator
|
||||
assert(array.allocator.procedure != nil)
|
||||
}
|
||||
assert(array.allocator.procedure != nil)
|
||||
|
||||
if new_cap > array.cap {
|
||||
return
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#+build !bedrock
|
||||
package runtime
|
||||
|
||||
import "base:intrinsics"
|
||||
@@ -6,8 +7,6 @@ _ :: intrinsics
|
||||
// High performance, cache-friendly, open-addressed Robin Hood hashing hash map
|
||||
// data structure with various optimizations for Odin.
|
||||
//
|
||||
// Copyright 2022 (c) Dale Weiler
|
||||
//
|
||||
// The core of the hash map data structure is the Raw_Map struct which is a
|
||||
// type-erased representation of the map. This type-erased representation is
|
||||
// used in two ways: static and dynamic. When static type information is known,
|
||||
@@ -49,60 +48,6 @@ MAP_MIN_LOG2_CAPACITY :: 3 // 8 elements
|
||||
// Has to be less than 100% though.
|
||||
#assert(MAP_LOAD_FACTOR < 100)
|
||||
|
||||
// This is safe to change. The log2 size of a cache-line. At minimum it has to
|
||||
// be six though. Higher cache line sizes are permitted.
|
||||
MAP_CACHE_LINE_LOG2 :: 6
|
||||
|
||||
// The size of a cache-line.
|
||||
MAP_CACHE_LINE_SIZE :: 1 << MAP_CACHE_LINE_LOG2
|
||||
|
||||
// The minimum cache-line size allowed by this implementation is 64 bytes since
|
||||
// we need 6 bits in the base pointer to store the integer log2 capacity, which
|
||||
// at maximum is 63. Odin uses signed integers to represent length and capacity,
|
||||
// so only 63 bits are needed in the maximum case.
|
||||
#assert(MAP_CACHE_LINE_SIZE >= 64)
|
||||
|
||||
// Map_Cell type that packs multiple T in such a way to ensure that each T stays
|
||||
// aligned by align_of(T) and such that align_of(Map_Cell(T)) % MAP_CACHE_LINE_SIZE == 0
|
||||
//
|
||||
// This means a value of type T will never straddle a cache-line.
|
||||
//
|
||||
// When multiple Ts can fit in a single cache-line the data array will have more
|
||||
// than one element. When it cannot, the data array will have one element and
|
||||
// an array of Map_Cell(T) will be padded to stay a multiple of MAP_CACHE_LINE_SIZE.
|
||||
//
|
||||
// We rely on the type system to do all the arithmetic and padding for us here.
|
||||
//
|
||||
// The usual array[index] indexing for []T backed by a []Map_Cell(T) becomes a bit
|
||||
// more involved as there now may be internal padding. The indexing now becomes
|
||||
//
|
||||
// N :: len(Map_Cell(T){}.data)
|
||||
// i := index / N
|
||||
// j := index % N
|
||||
// cell[i].data[j]
|
||||
//
|
||||
// However, since len(Map_Cell(T){}.data) is a compile-time constant, there are some
|
||||
// optimizations we can do to eliminate the need for any divisions as N will
|
||||
// be bounded by [1, 64).
|
||||
//
|
||||
// In the optimal case, len(Map_Cell(T){}.data) = 1 so the cell array can be treated
|
||||
// as a regular array of T, which is the case for hashes.
|
||||
Map_Cell :: struct($T: typeid) #align(MAP_CACHE_LINE_SIZE) {
|
||||
data: [MAP_CACHE_LINE_SIZE / size_of(T) when 0 < size_of(T) && size_of(T) < MAP_CACHE_LINE_SIZE else 1]T,
|
||||
}
|
||||
|
||||
// So we can operate on a cell data structure at runtime without any type
|
||||
// information, we have a simple table that stores some traits about the cell.
|
||||
//
|
||||
// 32-bytes on 64-bit
|
||||
// 16-bytes on 32-bit
|
||||
Map_Cell_Info :: struct {
|
||||
size_of_type: uintptr, // 8-bytes on 64-bit, 4-bytes on 32-bits
|
||||
align_of_type: uintptr, // 8-bytes on 64-bit, 4-bytes on 32-bits
|
||||
size_of_cell: uintptr, // 8-bytes on 64-bit, 4-bytes on 32-bits
|
||||
elements_per_cell: uintptr, // 8-bytes on 64-bit, 4-bytes on 32-bits
|
||||
}
|
||||
|
||||
// map_cell_info :: proc "contextless" ($T: typeid) -> ^Map_Cell_Info {...}
|
||||
map_cell_info :: intrinsics.type_map_cell_info
|
||||
|
||||
@@ -228,8 +173,6 @@ map_data :: #force_inline proc "contextless" (m: Raw_Map) -> uintptr {
|
||||
}
|
||||
|
||||
|
||||
Map_Hash :: uintptr
|
||||
|
||||
TOMBSTONE_MASK :: 1<<(size_of(Map_Hash)*8 - 1)
|
||||
|
||||
// Procedure to check if a slot is empty for a given hash. This is represented
|
||||
@@ -285,26 +228,11 @@ map_desired_position :: #force_inline proc "contextless" (m: Raw_Map, hash: Map_
|
||||
map_probe_distance :: #force_inline proc "contextless" (m: Raw_Map, hash: Map_Hash, slot: uintptr) -> uintptr {
|
||||
// We do not use map_cap since we know the capacity will not be zero here.
|
||||
capacity := uintptr(1) << map_log2_cap(m)
|
||||
return (slot + capacity - map_desired_position(m, hash)) & (capacity - 1)
|
||||
}
|
||||
|
||||
// When working with the type-erased structure at runtime we need information
|
||||
// about the map to make working with it possible. This info structure stores
|
||||
// that.
|
||||
//
|
||||
// `Map_Info` and `Map_Cell_Info` are read only data structures and cannot be
|
||||
// modified after creation
|
||||
//
|
||||
// 32-bytes on 64-bit
|
||||
// 16-bytes on 32-bit
|
||||
Map_Info :: struct {
|
||||
ks: ^Map_Cell_Info, // 8-bytes on 64-bit, 4-bytes on 32-bit
|
||||
vs: ^Map_Cell_Info, // 8-bytes on 64-bit, 4-bytes on 32-bit
|
||||
key_hasher: proc "contextless" (key: rawptr, seed: Map_Hash) -> Map_Hash, // 8-bytes on 64-bit, 4-bytes on 32-bit
|
||||
key_equal: proc "contextless" (lhs, rhs: rawptr) -> bool, // 8-bytes on 64-bit, 4-bytes on 32-bit
|
||||
// return (slot + capacity - map_desired_position(m, hash)) & (capacity - 1)
|
||||
return (slot - uintptr(hash)) & (capacity - 1) // NOTE(bill): this is equivalent to the above, but less operations
|
||||
}
|
||||
|
||||
|
||||
// The Map_Info structure is basically a pseudo-table of information for a given K and V pair.
|
||||
// map_info :: proc "contextless" ($T: typeid/map[$K]$V) -> ^Map_Info {...}
|
||||
map_info :: intrinsics.type_map_info
|
||||
@@ -905,22 +833,22 @@ __dynamic_map_check_grow :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info:
|
||||
return nil, false
|
||||
}
|
||||
|
||||
__dynamic_map_set_without_hash :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, key, value: rawptr, loc := #caller_location) -> rawptr {
|
||||
__dynamic_map_set_without_hash :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, key, value: rawptr, loc := #caller_location) -> (value_ptr: rawptr, err: Allocator_Error) #optional_allocator_error {
|
||||
return __dynamic_map_set(m, info, info.key_hasher(key, map_seed(m^)), key, value, loc)
|
||||
}
|
||||
|
||||
|
||||
// IMPORTANT: USED WITHIN THE COMPILER
|
||||
__dynamic_map_set :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, hash: Map_Hash, key, value: rawptr, loc := #caller_location) -> rawptr {
|
||||
__dynamic_map_set :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, hash: Map_Hash, key, value: rawptr, loc := #caller_location) -> (value_ptr: rawptr, err: Allocator_Error) #optional_allocator_error {
|
||||
if found := __dynamic_map_get(m, info, hash, key); found != nil {
|
||||
intrinsics.mem_copy_non_overlapping(found, value, info.vs.size_of_type)
|
||||
return found
|
||||
return found, nil
|
||||
}
|
||||
|
||||
hash := hash
|
||||
err, has_grown := __dynamic_map_check_grow(m, info, loc)
|
||||
if err != nil {
|
||||
return nil
|
||||
err_grow, has_grown := __dynamic_map_check_grow(m, info, loc)
|
||||
if err_grow != nil {
|
||||
return nil, err_grow
|
||||
}
|
||||
if has_grown {
|
||||
hash = info.key_hasher(key, map_seed(m^))
|
||||
@@ -930,7 +858,7 @@ __dynamic_map_set :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_In
|
||||
if result != 0 {
|
||||
m.len += 1
|
||||
}
|
||||
return rawptr(result)
|
||||
return rawptr(result), nil
|
||||
}
|
||||
__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)
|
||||
@@ -985,6 +913,9 @@ __dynamic_map_entry :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_
|
||||
// IMPORTANT: USED WITHIN THE COMPILER
|
||||
@(private)
|
||||
__dynamic_map_reserve :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, new_capacity: uint, loc := #caller_location) -> Allocator_Error {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
return map_reserve_dynamic(m, info, uintptr(new_capacity), loc)
|
||||
}
|
||||
|
||||
@@ -1057,4 +988,4 @@ default_hasher_quaternion256 :: proc "contextless" (x, y, z, w: f64, seed: uintp
|
||||
seed = default_hasher_f64(z, seed)
|
||||
seed = default_hasher_f64(w, seed)
|
||||
return seed
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#+private
|
||||
#+build linux, darwin, freebsd, openbsd, netbsd, haiku
|
||||
#+build linux, darwin, freebsd, openbsd, netbsd
|
||||
#+no-instrumentation
|
||||
package runtime
|
||||
|
||||
@@ -10,13 +10,15 @@ when ODIN_BUILD_MODE == .Dynamic {
|
||||
_odin_entry_point :: proc "c" (argc: i32, argv: [^]cstring) {
|
||||
args__ = argv[:argc]
|
||||
context = default_context()
|
||||
#force_no_inline _startup_runtime()
|
||||
when !ODIN_BEDROCK { #force_no_inline _startup_runtime() }
|
||||
intrinsics.__entry_point()
|
||||
}
|
||||
@(link_name="_odin_exit_point", linkage="strong", require/*, link_section=".fini"*/)
|
||||
_odin_exit_point :: proc "c" () {
|
||||
context = default_context()
|
||||
#force_no_inline _cleanup_runtime()
|
||||
when !ODIN_BEDROCK {
|
||||
#force_no_inline _cleanup_runtime()
|
||||
}
|
||||
}
|
||||
@(link_name="main", linkage="strong", require)
|
||||
main :: proc "c" (argc: i32, argv: [^]cstring) -> i32 {
|
||||
@@ -43,9 +45,9 @@ when ODIN_BUILD_MODE == .Dynamic {
|
||||
_start_odin :: proc "c" (argc: i32, argv: [^]cstring) -> ! {
|
||||
args__ = argv[:argc]
|
||||
context = default_context()
|
||||
#force_no_inline _startup_runtime()
|
||||
when !ODIN_BEDROCK { #force_no_inline _startup_runtime() }
|
||||
intrinsics.__entry_point()
|
||||
#force_no_inline _cleanup_runtime()
|
||||
when !ODIN_BEDROCK { #force_no_inline _cleanup_runtime() }
|
||||
intrinsics.syscall(SYS_exit, 0)
|
||||
unreachable()
|
||||
}
|
||||
@@ -54,9 +56,9 @@ when ODIN_BUILD_MODE == .Dynamic {
|
||||
main :: proc "c" (argc: i32, argv: [^]cstring) -> i32 {
|
||||
args__ = argv[:argc]
|
||||
context = default_context()
|
||||
#force_no_inline _startup_runtime()
|
||||
when !ODIN_BEDROCK { #force_no_inline _startup_runtime() }
|
||||
intrinsics.__entry_point()
|
||||
#force_no_inline _cleanup_runtime()
|
||||
when !ODIN_BEDROCK { #force_no_inline _cleanup_runtime() }
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ bits 64
|
||||
extern _start_odin
|
||||
global _start
|
||||
|
||||
section .note.GNU-stack
|
||||
section .text
|
||||
|
||||
;; Entry point for programs that specify -no-crt option
|
||||
@@ -35,7 +36,7 @@ _start:
|
||||
xor rbp, rbp
|
||||
;; Load argc into 1st param reg, argv into 2nd param reg
|
||||
pop rdi
|
||||
mov rdx, rsi
|
||||
mov rsi, rsp
|
||||
;; Align stack pointer down to 16-bytes (sysv calling convention)
|
||||
and rsp, -16
|
||||
;; Call into odin entry point
|
||||
|
||||
@@ -16,10 +16,10 @@ when ODIN_BUILD_MODE == .Dynamic {
|
||||
|
||||
switch dll_forward_reason {
|
||||
case .Process_Attach:
|
||||
#force_no_inline _startup_runtime()
|
||||
when !ODIN_BEDROCK { #force_no_inline _startup_runtime() }
|
||||
intrinsics.__entry_point()
|
||||
case .Process_Detach:
|
||||
#force_no_inline _cleanup_runtime()
|
||||
when !ODIN_BEDROCK { #force_no_inline _cleanup_runtime() }
|
||||
case .Thread_Attach:
|
||||
break
|
||||
case .Thread_Detach:
|
||||
@@ -28,13 +28,25 @@ when ODIN_BUILD_MODE == .Dynamic {
|
||||
return true
|
||||
}
|
||||
} else when !ODIN_TEST && !ODIN_NO_ENTRY_POINT {
|
||||
when ODIN_ARCH == .i386 || ODIN_NO_CRT {
|
||||
when ODIN_ARCH == .i386 && !ODIN_NO_CRT {
|
||||
// Windows i386 with CRT: libcmt provides mainCRTStartup which calls _main
|
||||
// Note: "c" calling convention adds underscore prefix automatically on i386
|
||||
@(link_name="main", linkage="strong", require)
|
||||
main :: proc "c" (argc: i32, argv: [^]cstring) -> i32 {
|
||||
args__ = argv[:argc]
|
||||
context = default_context()
|
||||
when !ODIN_BEDROCK { #force_no_inline _startup_runtime() }
|
||||
intrinsics.__entry_point()
|
||||
when !ODIN_BEDROCK { #force_no_inline _cleanup_runtime() }
|
||||
return 0
|
||||
}
|
||||
} else when ODIN_NO_CRT {
|
||||
@(link_name="mainCRTStartup", linkage="strong", require)
|
||||
mainCRTStartup :: proc "system" () -> i32 {
|
||||
context = default_context()
|
||||
#force_no_inline _startup_runtime()
|
||||
when !ODIN_BEDROCK { #force_no_inline _startup_runtime() }
|
||||
intrinsics.__entry_point()
|
||||
#force_no_inline _cleanup_runtime()
|
||||
when !ODIN_BEDROCK { #force_no_inline _cleanup_runtime() }
|
||||
return 0
|
||||
}
|
||||
} else {
|
||||
@@ -42,9 +54,9 @@ when ODIN_BUILD_MODE == .Dynamic {
|
||||
main :: proc "c" (argc: i32, argv: [^]cstring) -> i32 {
|
||||
args__ = argv[:argc]
|
||||
context = default_context()
|
||||
#force_no_inline _startup_runtime()
|
||||
when !ODIN_BEDROCK { #force_no_inline _startup_runtime() }
|
||||
intrinsics.__entry_point()
|
||||
#force_no_inline _cleanup_runtime()
|
||||
when !ODIN_BEDROCK { #force_no_inline _cleanup_runtime() }
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ bounds_trap :: proc "contextless" () -> ! {
|
||||
}
|
||||
|
||||
@(no_instrumentation)
|
||||
type_assertion_trap :: proc "contextless" () -> ! {
|
||||
type_assertion_trap_contextless :: proc "contextless" () -> ! {
|
||||
when ODIN_OS == .Windows {
|
||||
windows_trap_type_assertion()
|
||||
} else when ODIN_OS == .Orca {
|
||||
@@ -137,20 +137,22 @@ matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32
|
||||
|
||||
|
||||
when ODIN_NO_RTTI {
|
||||
type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: i32) {
|
||||
type_assertion_check_with_context :: proc "odin" (ok: bool, file: string, line, column: i32) {
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
@(cold, no_instrumentation)
|
||||
handle_error :: proc "contextless" (file: string, line, column: i32) -> ! {
|
||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||
print_string(" Invalid type assertion\n")
|
||||
type_assertion_trap()
|
||||
handle_error :: proc "odin" (file: string, line, column: i32) -> ! {
|
||||
p := context.assertion_failure_proc
|
||||
if p == nil {
|
||||
p = default_assertion_failure_proc
|
||||
}
|
||||
p("type assertion", "Invalid type assertion", Source_Code_Location{file, line, column, ""})
|
||||
}
|
||||
handle_error(file, line, column)
|
||||
}
|
||||
|
||||
type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: i32) {
|
||||
type_assertion_check_contextless :: proc "contextless" (ok: bool, file: string, line, column: i32) {
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
@@ -158,12 +160,71 @@ when ODIN_NO_RTTI {
|
||||
handle_error :: proc "contextless" (file: string, line, column: i32) -> ! {
|
||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||
print_string(" Invalid type assertion\n")
|
||||
type_assertion_trap()
|
||||
type_assertion_trap_contextless()
|
||||
}
|
||||
handle_error(file, line, column)
|
||||
}
|
||||
|
||||
type_assertion_check2_with_context :: proc "odin" (ok: bool, file: string, line, column: i32) {
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
@(cold, no_instrumentation)
|
||||
handle_error :: proc "odin" (file: string, line, column: i32) -> ! {
|
||||
p := context.assertion_failure_proc
|
||||
if p == nil {
|
||||
p = default_assertion_failure_proc
|
||||
}
|
||||
p("type assertion", "Invalid type assertion", Source_Code_Location{file, line, column, ""})
|
||||
}
|
||||
|
||||
handle_error(file, line, column)
|
||||
}
|
||||
|
||||
type_assertion_check2_contextless :: proc "contextless" (ok: bool, file: string, line, column: i32) {
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
@(cold, no_instrumentation)
|
||||
handle_error :: proc "contextless" (file: string, line, column: i32) -> ! {
|
||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||
print_string(" Invalid type assertion\n")
|
||||
type_assertion_trap_contextless()
|
||||
}
|
||||
handle_error(file, line, column)
|
||||
}
|
||||
} else {
|
||||
type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid) {
|
||||
@(private="file")
|
||||
TYPE_ASSERTION_BUFFER_SIZE :: 1024
|
||||
|
||||
type_assertion_check_with_context :: proc "odin" (ok: bool, file: string, line, column: i32, from, to: typeid) {
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
@(cold, no_instrumentation)
|
||||
handle_error :: proc "odin" (file: string, line, column: i32, from, to: typeid) -> ! {
|
||||
do_msg :: proc "contextless" (i: ^int, buf: []byte, file: string, line, column: i32, from, to: typeid) -> bool {
|
||||
write_string(i, buf, "Invalid type assertion from ") or_return
|
||||
write_typeid(i, buf, from) or_return
|
||||
write_string(i, buf, " to ") or_return
|
||||
write_typeid(i, buf, to) or_return
|
||||
return true
|
||||
}
|
||||
|
||||
buf: [TYPE_ASSERTION_BUFFER_SIZE]byte
|
||||
i := 0
|
||||
_ = do_msg(&i, buf[:], file, line, column, from, to)
|
||||
|
||||
p := context.assertion_failure_proc
|
||||
if p == nil {
|
||||
p = default_assertion_failure_proc
|
||||
}
|
||||
p("type assertion", string(buf[:i]), Source_Code_Location{file, line, column, ""})
|
||||
}
|
||||
handle_error(file, line, column, from, to)
|
||||
}
|
||||
|
||||
type_assertion_check_contextless :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid) {
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
@@ -175,47 +236,90 @@ when ODIN_NO_RTTI {
|
||||
print_string(" to ")
|
||||
print_typeid(to)
|
||||
print_byte('\n')
|
||||
type_assertion_trap()
|
||||
type_assertion_trap_contextless()
|
||||
}
|
||||
handle_error(file, line, column, from, to)
|
||||
}
|
||||
|
||||
type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid, from_data: rawptr) {
|
||||
@(private="file")
|
||||
type_assertion_variant_type :: proc "contextless" (id: typeid, data: rawptr) -> typeid {
|
||||
if id == nil || data == nil {
|
||||
return id
|
||||
}
|
||||
ti := type_info_base(type_info_of(id))
|
||||
#partial switch v in ti.variant {
|
||||
case Type_Info_Any:
|
||||
return (^any)(data).id
|
||||
case Type_Info_Union:
|
||||
if v.tag_type == nil {
|
||||
if (^rawptr)(data)^ == nil {
|
||||
return nil
|
||||
}
|
||||
return v.variants[0].id
|
||||
|
||||
}
|
||||
|
||||
tag_ptr := uintptr(data) + v.tag_offset
|
||||
idx := 0
|
||||
switch v.tag_type.size {
|
||||
case 1: idx = int( (^u8)(tag_ptr)^); if !v.no_nil { idx -= 1 }
|
||||
case 2: idx = int( (^u16)(tag_ptr)^); if !v.no_nil { idx -= 1 }
|
||||
case 4: idx = int( (^u32)(tag_ptr)^); if !v.no_nil { idx -= 1 }
|
||||
case 8: idx = int( (^u64)(tag_ptr)^); if !v.no_nil { idx -= 1 }
|
||||
case 16: idx = int((^u128)(tag_ptr)^); if !v.no_nil { idx -= 1 }
|
||||
}
|
||||
if idx < 0 {
|
||||
return nil
|
||||
} else if idx < len(v.variants) {
|
||||
return v.variants[idx].id
|
||||
}
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
type_assertion_check2_with_context :: proc "odin" (ok: bool, file: string, line, column: i32, from, to: typeid, from_data: rawptr) {
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
|
||||
variant_type :: proc "contextless" (id: typeid, data: rawptr) -> typeid {
|
||||
if id == nil || data == nil {
|
||||
return id
|
||||
}
|
||||
ti := type_info_base(type_info_of(id))
|
||||
#partial switch v in ti.variant {
|
||||
case Type_Info_Any:
|
||||
return (^any)(data).id
|
||||
case Type_Info_Union:
|
||||
tag_ptr := uintptr(data) + v.tag_offset
|
||||
idx := 0
|
||||
switch v.tag_type.size {
|
||||
case 1: idx = int((^u8)(tag_ptr)^) - 1
|
||||
case 2: idx = int((^u16)(tag_ptr)^) - 1
|
||||
case 4: idx = int((^u32)(tag_ptr)^) - 1
|
||||
case 8: idx = int((^u64)(tag_ptr)^) - 1
|
||||
case 16: idx = int((^u128)(tag_ptr)^) - 1
|
||||
}
|
||||
if idx < 0 {
|
||||
return nil
|
||||
} else if idx < len(v.variants) {
|
||||
return v.variants[idx].id
|
||||
@(cold, no_instrumentation)
|
||||
handle_error :: proc "odin" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) -> ! {
|
||||
do_msg :: proc "contextless" (i: ^int, buf: []byte, file: string, line, column: i32, from, to, actual: typeid) -> bool {
|
||||
write_string(i, buf, "Invalid type assertion from ") or_return
|
||||
write_typeid(i, buf, from) or_return
|
||||
write_string(i, buf, " to ") or_return
|
||||
write_typeid(i, buf, to) or_return
|
||||
if actual != from {
|
||||
write_string(i, buf, ", actual type: ") or_return
|
||||
write_typeid(i, buf, actual) or_return
|
||||
}
|
||||
return true
|
||||
}
|
||||
return id
|
||||
|
||||
actual := type_assertion_variant_type(from, from_data)
|
||||
|
||||
buf: [TYPE_ASSERTION_BUFFER_SIZE]byte
|
||||
i := 0
|
||||
_ = do_msg(&i, buf[:], file, line, column, from, to, actual)
|
||||
|
||||
p := context.assertion_failure_proc
|
||||
if p == nil {
|
||||
p = default_assertion_failure_proc
|
||||
}
|
||||
p("type assertion", string(buf[:i]), Source_Code_Location{file, line, column, ""})
|
||||
}
|
||||
handle_error(file, line, column, from, to, from_data)
|
||||
}
|
||||
|
||||
type_assertion_check2_contextless :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid, from_data: rawptr) {
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
|
||||
@(cold, no_instrumentation)
|
||||
handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) -> ! {
|
||||
|
||||
actual := variant_type(from, from_data)
|
||||
actual := type_assertion_variant_type(from, from_data)
|
||||
|
||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||
print_string(" Invalid type assertion from ")
|
||||
@@ -227,7 +331,7 @@ when ODIN_NO_RTTI {
|
||||
print_typeid(actual)
|
||||
}
|
||||
print_byte('\n')
|
||||
type_assertion_trap()
|
||||
type_assertion_trap_contextless()
|
||||
}
|
||||
handle_error(file, line, column, from, to, from_data)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
217
base/runtime/internal_i128.odin
Normal file
217
base/runtime/internal_i128.odin
Normal file
@@ -0,0 +1,217 @@
|
||||
#+vet !cast
|
||||
#+build !bedrock
|
||||
package runtime
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
@(private="file")
|
||||
IS_WASM :: ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32
|
||||
|
||||
@(link_name="__floattidf", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||
floattidf :: proc "c" (a: i128) -> f64 {
|
||||
DBL_MANT_DIG :: 53
|
||||
if a == 0 {
|
||||
return 0.0
|
||||
}
|
||||
a := a
|
||||
N :: size_of(i128) * 8
|
||||
s := a >> (N-1)
|
||||
a = (a ~ s) - s
|
||||
sd: = N - intrinsics.count_leading_zeros(a) // number of significant digits
|
||||
e := i32(sd - 1) // exponent
|
||||
if sd > DBL_MANT_DIG {
|
||||
switch sd {
|
||||
case DBL_MANT_DIG + 1:
|
||||
a <<= 1
|
||||
case DBL_MANT_DIG + 2:
|
||||
// okay
|
||||
case:
|
||||
a = i128(u128(a) >> u128(sd - (DBL_MANT_DIG+2))) |
|
||||
i128(u128(a) & (~u128(0) >> u128(N + DBL_MANT_DIG+2 - sd)) != 0)
|
||||
}
|
||||
|
||||
a |= i128((a & 4) != 0)
|
||||
a += 1
|
||||
a >>= 2
|
||||
|
||||
if a & (i128(1) << DBL_MANT_DIG) != 0 {
|
||||
a >>= 1
|
||||
e += 1
|
||||
}
|
||||
} else {
|
||||
a <<= u128(DBL_MANT_DIG - sd) & 127
|
||||
}
|
||||
fb: [2]u32
|
||||
fb[1] = (u32(s) & 0x80000000) | // sign
|
||||
(u32(e + 1023) << 20) | // exponent
|
||||
u32((u64(a) >> 32) & 0x000FFFFF) // mantissa-high
|
||||
fb[0] = u32(a) // mantissa-low
|
||||
return transmute(f64)fb
|
||||
}
|
||||
|
||||
|
||||
@(link_name="__floattidf_unsigned", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||
floattidf_unsigned :: proc "c" (a: u128) -> f64 {
|
||||
DBL_MANT_DIG :: 53
|
||||
if a == 0 {
|
||||
return 0.0
|
||||
}
|
||||
a := a
|
||||
N :: size_of(u128) * 8
|
||||
sd: = N - intrinsics.count_leading_zeros(a) // number of significant digits
|
||||
e := i32(sd - 1) // exponent
|
||||
if sd > DBL_MANT_DIG {
|
||||
switch sd {
|
||||
case DBL_MANT_DIG + 1:
|
||||
a <<= 1
|
||||
case DBL_MANT_DIG + 2:
|
||||
// okay
|
||||
case:
|
||||
a = u128(u128(a) >> u128(sd - (DBL_MANT_DIG+2))) |
|
||||
u128(u128(a) & (~u128(0) >> u128(N + DBL_MANT_DIG+2 - sd)) != 0)
|
||||
}
|
||||
|
||||
a |= u128((a & 4) != 0)
|
||||
a += 1
|
||||
a >>= 2
|
||||
|
||||
if a & (1 << DBL_MANT_DIG) != 0 {
|
||||
a >>= 1
|
||||
e += 1
|
||||
}
|
||||
} else {
|
||||
a <<= u128(DBL_MANT_DIG - sd)
|
||||
}
|
||||
fb: [2]u32
|
||||
fb[1] = (0) | // sign
|
||||
u32((e + 1023) << 20) | // exponent
|
||||
u32((u64(a) >> 32) & 0x000FFFFF) // mantissa-high
|
||||
fb[0] = u32(a) // mantissa-low
|
||||
return transmute(f64)fb
|
||||
}
|
||||
|
||||
|
||||
|
||||
@(link_name="__fixunsdfti", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||
fixunsdfti :: #force_no_inline proc "c" (a: f64) -> u128 {
|
||||
// TODO(bill): implement `fixunsdfti` correctly
|
||||
x := u64(a)
|
||||
return u128(x)
|
||||
}
|
||||
|
||||
@(link_name="__fixunsdfdi", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||
fixunsdfdi :: #force_no_inline proc "c" (a: f64) -> i128 {
|
||||
// TODO(bill): implement `fixunsdfdi` correctly
|
||||
x := i64(a)
|
||||
return i128(x)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@(link_name="__umodti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||
umodti3 :: proc "c" (a, b: u128) -> u128 {
|
||||
r: u128 = ---
|
||||
_ = udivmod128(a, b, &r)
|
||||
return r
|
||||
}
|
||||
|
||||
|
||||
@(link_name="__udivmodti4", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||
udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
|
||||
return udivmod128(a, b, rem)
|
||||
}
|
||||
|
||||
when !IS_WASM {
|
||||
@(link_name="__udivti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||
udivti3 :: proc "c" (a, b: u128) -> u128 {
|
||||
return udivmodti4(a, b, nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@(link_name="__modti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||
modti3 :: proc "c" (a, b: i128) -> i128 {
|
||||
s_a := a >> (128 - 1)
|
||||
s_b := b >> (128 - 1)
|
||||
an := (a ~ s_a) - s_a
|
||||
bn := (b ~ s_b) - s_b
|
||||
|
||||
r: u128 = ---
|
||||
_ = udivmod128(u128(an), u128(bn), &r)
|
||||
return (i128(r) ~ s_a) - s_a
|
||||
}
|
||||
|
||||
|
||||
@(link_name="__divmodti4", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||
divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 {
|
||||
s_a := a >> (128 - 1) // -1 if negative or 0
|
||||
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)
|
||||
divti3 :: proc "c" (a, b: i128) -> i128 {
|
||||
s_a := a >> (128 - 1) // -1 if negative or 0
|
||||
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)
|
||||
fixdfti :: proc "c" (a: u64) -> i128 {
|
||||
significandBits :: 52
|
||||
typeWidth :: (size_of(u64)*8)
|
||||
exponentBits :: (typeWidth - significandBits - 1)
|
||||
maxExponent :: ((1 << exponentBits) - 1)
|
||||
exponentBias :: (maxExponent >> 1)
|
||||
|
||||
implicitBit :: (u64(1) << significandBits)
|
||||
significandMask :: (implicitBit - 1)
|
||||
signBit :: (u64(1) << (significandBits + exponentBits))
|
||||
absMask :: (signBit - 1)
|
||||
exponentMask :: (absMask ~ significandMask)
|
||||
|
||||
// Break a into sign, exponent, significand
|
||||
aRep := a
|
||||
aAbs := aRep & absMask
|
||||
sign := i128(-1 if aRep & signBit != 0 else 1)
|
||||
exponent := u64((aAbs >> significandBits) - exponentBias)
|
||||
significand := u64((aAbs & significandMask) | implicitBit)
|
||||
|
||||
// If exponent is negative, the result is zero.
|
||||
if exponent < 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// If the value is too large for the integer type, saturate.
|
||||
if exponent >= size_of(i128) * 8 {
|
||||
return max(i128) if sign == 1 else min(i128)
|
||||
}
|
||||
|
||||
// If 0 <= exponent < significandBits, right shift to get the result.
|
||||
// Otherwise, shift left.
|
||||
if exponent < significandBits {
|
||||
return sign * i128(significand >> (significandBits - exponent))
|
||||
} else {
|
||||
return sign * (i128(significand) << (exponent - significandBits))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,20 @@ package runtime
|
||||
|
||||
_OS_Errno :: distinct int
|
||||
|
||||
HAS_RAND_BYTES :: _HAS_RAND_BYTES
|
||||
|
||||
stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
return _stderr_write(data)
|
||||
}
|
||||
|
||||
rand_bytes :: proc "contextless" (dst: []byte) {
|
||||
when HAS_RAND_BYTES {
|
||||
_rand_bytes(dst)
|
||||
} else {
|
||||
panic_contextless("base/runtime: no runtime entropy source")
|
||||
}
|
||||
}
|
||||
|
||||
exit :: proc "contextless" (code: int) -> ! {
|
||||
_exit(code)
|
||||
}
|
||||
@@ -4,6 +4,8 @@ package runtime
|
||||
|
||||
foreign import libc "system:c"
|
||||
|
||||
_HAS_RAND_BYTES :: true
|
||||
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
@(link_name="write")
|
||||
@@ -14,6 +16,8 @@ foreign libc {
|
||||
} else {
|
||||
__error :: proc() -> ^i32 ---
|
||||
}
|
||||
|
||||
arc4random_buf :: proc(buf: [^]byte, nbytes: uint) ---
|
||||
}
|
||||
|
||||
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
@@ -24,3 +28,15 @@ _stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
}
|
||||
return int(ret), 0
|
||||
}
|
||||
|
||||
_rand_bytes :: proc "contextless" (dst: []byte) {
|
||||
arc4random_buf(raw_data(dst), len(dst))
|
||||
}
|
||||
|
||||
_exit :: proc "contextless" (code: int) -> ! {
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
exit :: proc(status: i32) -> ! ---
|
||||
}
|
||||
exit(i32(code))
|
||||
}
|
||||
@@ -4,6 +4,8 @@ package runtime
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
_HAS_RAND_BYTES :: true
|
||||
|
||||
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
STDERR :: 2
|
||||
when ODIN_NO_CRT {
|
||||
@@ -26,3 +28,25 @@ _stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
return 0, _OS_Errno(__error()^)
|
||||
}
|
||||
}
|
||||
|
||||
foreign import libc "system:System"
|
||||
|
||||
_rand_bytes :: proc "contextless" (dst: []byte) {
|
||||
// This process used to use Security/RandomCopyBytes, however
|
||||
// on every version of MacOS (>= 10.12) that we care about,
|
||||
// arc4random is implemented securely.
|
||||
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
arc4random_buf :: proc(buf: [^]byte, nbytes: uint) ---
|
||||
}
|
||||
arc4random_buf(raw_data(dst), len(dst))
|
||||
}
|
||||
|
||||
_exit :: proc "contextless" (code: int) -> ! {
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
exit :: proc(status: i32) -> ! ---
|
||||
}
|
||||
exit(i32(code))
|
||||
}
|
||||
@@ -2,7 +2,13 @@
|
||||
#+private
|
||||
package runtime
|
||||
|
||||
_HAS_RAND_BYTES :: false
|
||||
|
||||
// TODO(bill): reimplement `os.write`
|
||||
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
return 0, -1
|
||||
}
|
||||
|
||||
_exit :: proc "contextless" (code: int) -> ! {
|
||||
trap()
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
#+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
|
||||
}
|
||||
@@ -4,6 +4,8 @@ package runtime
|
||||
|
||||
foreign import "odin_env"
|
||||
|
||||
_HAS_RAND_BYTES :: true
|
||||
|
||||
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
foreign odin_env {
|
||||
write :: proc "contextless" (fd: u32, p: []byte) ---
|
||||
@@ -11,3 +13,24 @@ _stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
write(1, data)
|
||||
return len(data), 0
|
||||
}
|
||||
|
||||
_rand_bytes :: proc "contextless" (dst: []byte) {
|
||||
foreign odin_env {
|
||||
@(link_name = "rand_bytes")
|
||||
env_rand_bytes :: proc "contextless" (buf: []byte) ---
|
||||
}
|
||||
|
||||
MAX_PER_CALL_BYTES :: 65536 // 64kiB
|
||||
|
||||
dst := dst
|
||||
for len(dst) > 0 {
|
||||
to_read := min(len(dst), MAX_PER_CALL_BYTES)
|
||||
env_rand_bytes(dst[:to_read])
|
||||
|
||||
dst = dst[to_read:]
|
||||
}
|
||||
}
|
||||
|
||||
_exit :: proc "contextless" (code: int) -> ! {
|
||||
trap()
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package runtime
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
_HAS_RAND_BYTES :: true
|
||||
|
||||
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
when ODIN_ARCH == .amd64 {
|
||||
SYS_write :: uintptr(1)
|
||||
@@ -24,3 +26,64 @@ _stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
}
|
||||
return ret, 0
|
||||
}
|
||||
|
||||
_rand_bytes :: proc "contextless" (dst: []byte) {
|
||||
when ODIN_ARCH == .amd64 {
|
||||
SYS_getrandom :: uintptr(318)
|
||||
} else when ODIN_ARCH == .arm64 {
|
||||
SYS_getrandom :: uintptr(278)
|
||||
} else when ODIN_ARCH == .i386 {
|
||||
SYS_getrandom :: uintptr(355)
|
||||
} else when ODIN_ARCH == .arm32 {
|
||||
SYS_getrandom :: uintptr(384)
|
||||
} else when ODIN_ARCH == .riscv64 {
|
||||
SYS_getrandom :: uintptr(278)
|
||||
} else {
|
||||
#panic("base/runtime: no SYS_getrandom definition for target")
|
||||
}
|
||||
|
||||
ERR_EINTR :: 4
|
||||
ERR_ENOSYS :: 38
|
||||
|
||||
MAX_PER_CALL_BYTES :: 33554431 // 2^25 - 1
|
||||
|
||||
dst := dst
|
||||
l := len(dst)
|
||||
|
||||
for l > 0 {
|
||||
to_read := min(l, MAX_PER_CALL_BYTES)
|
||||
ret := int(intrinsics.syscall(SYS_getrandom, uintptr(raw_data(dst[:to_read])), uintptr(to_read), uintptr(0)))
|
||||
switch ret {
|
||||
case -ERR_EINTR:
|
||||
// Call interupted by a signal handler, just retry the
|
||||
// request.
|
||||
continue
|
||||
case -ERR_ENOSYS:
|
||||
// The kernel is apparently prehistoric (< 3.17 circa 2014)
|
||||
// and does not support getrandom.
|
||||
panic_contextless("base/runtime: getrandom not available in kernel")
|
||||
case:
|
||||
if ret < 0 {
|
||||
// All other failures are things that should NEVER happen
|
||||
// unless the kernel interface changes (ie: the Linux
|
||||
// developers break userland).
|
||||
panic_contextless("base/runtime: getrandom failed")
|
||||
}
|
||||
}
|
||||
l -= ret
|
||||
dst = dst[ret:]
|
||||
}
|
||||
}
|
||||
|
||||
_exit :: proc "contextless" (code: int) -> ! {
|
||||
SYS_exit_group ::
|
||||
231 when ODIN_ARCH == .amd64 else
|
||||
248 when ODIN_ARCH == .arm32 else
|
||||
94 when ODIN_ARCH == .arm64 else
|
||||
252 when ODIN_ARCH == .i386 else
|
||||
94 when ODIN_ARCH == .riscv64 else
|
||||
0
|
||||
|
||||
intrinsics.syscall(uintptr(SYS_exit_group), uintptr(i32(code)))
|
||||
unreachable()
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ package runtime
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
_HAS_RAND_BYTES :: false
|
||||
|
||||
// Constants allowing to specify the level of logging verbosity.
|
||||
log_level :: enum u32 {
|
||||
// Only errors are logged.
|
||||
@@ -41,3 +43,8 @@ _stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
|
||||
return len(data), 0
|
||||
}
|
||||
|
||||
|
||||
_exit :: proc "contextless" (code: int) -> ! {
|
||||
trap()
|
||||
}
|
||||
@@ -4,6 +4,8 @@ package runtime
|
||||
|
||||
foreign import wasi "wasi_snapshot_preview1"
|
||||
|
||||
_HAS_RAND_BYTES :: true
|
||||
|
||||
@(default_calling_convention="contextless")
|
||||
foreign wasi {
|
||||
fd_write :: proc(
|
||||
@@ -23,6 +25,12 @@ foreign wasi {
|
||||
argv: [^]cstring,
|
||||
argv_buf: [^]byte,
|
||||
) -> u16 ---
|
||||
|
||||
@(private="file")
|
||||
proc_exit :: proc(rval: u32) -> ! ---
|
||||
|
||||
@(private ="file")
|
||||
random_get :: proc(buf: []u8) -> u16 ---
|
||||
}
|
||||
|
||||
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
@@ -31,6 +39,12 @@ _stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
return int(n), _OS_Errno(err)
|
||||
}
|
||||
|
||||
_rand_bytes :: proc "contextless" (dst: []byte) {
|
||||
if errno := random_get(dst); errno != 0 {
|
||||
panic_contextless("base/runtime: wasi.random_get failed")
|
||||
}
|
||||
}
|
||||
|
||||
_wasi_setup_args :: proc() {
|
||||
num_of_args, size_of_args: uint
|
||||
if errno := args_sizes_get(&num_of_args, &size_of_args); errno != 0 {
|
||||
@@ -53,3 +67,8 @@ _wasi_setup_args :: proc() {
|
||||
delete(args_buf)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_exit :: proc "contextless" (code: int) -> ! {
|
||||
proc_exit(u32(code))
|
||||
}
|
||||
@@ -2,8 +2,11 @@
|
||||
#+private
|
||||
package runtime
|
||||
|
||||
foreign import bcrypt "system:Bcrypt.lib"
|
||||
foreign import kernel32 "system:Kernel32.lib"
|
||||
|
||||
_HAS_RAND_BYTES :: true
|
||||
|
||||
@(private="file")
|
||||
@(default_calling_convention="system")
|
||||
foreign kernel32 {
|
||||
@@ -14,6 +17,14 @@ foreign kernel32 {
|
||||
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 ---
|
||||
|
||||
ExitProcess :: proc(code: u32) -> ! ---
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
@(default_calling_convention="system")
|
||||
foreign bcrypt {
|
||||
BCryptGenRandom :: proc(hAlgorithm: rawptr, pBuffer: [^]u8, cbBuffer: u32, dwFlags: u32) -> i32 ---
|
||||
}
|
||||
|
||||
_stderr_write :: proc "contextless" (data: []byte) -> (n: int, err: _OS_Errno) #no_bounds_check {
|
||||
@@ -49,3 +60,31 @@ _stderr_write :: proc "contextless" (data: []byte) -> (n: int, err: _OS_Errno) #
|
||||
n = int(total_write)
|
||||
return
|
||||
}
|
||||
|
||||
_rand_bytes :: proc "contextless" (dst: []byte) {
|
||||
ensure_contextless(u64(len(dst)) <= u64(max(u32)), "base/runtime: oversized rand_bytes request")
|
||||
|
||||
BCRYPT_USE_SYSTEM_PREFERRED_RNG :: 0x00000002
|
||||
|
||||
ERROR_INVALID_HANDLE :: 6
|
||||
ERROR_INVALID_PARAMETER :: 87
|
||||
|
||||
ret := BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), BCRYPT_USE_SYSTEM_PREFERRED_RNG)
|
||||
switch ret {
|
||||
case 0:
|
||||
case ERROR_INVALID_HANDLE:
|
||||
// The handle to the first parameter is invalid.
|
||||
// This should not happen here, since we explicitly pass nil to it
|
||||
panic_contextless("base/runtime: BCryptGenRandom Invalid handle for hAlgorithm")
|
||||
case ERROR_INVALID_PARAMETER:
|
||||
// One of the parameters was invalid
|
||||
panic_contextless("base/runtime: BCryptGenRandom Invalid parameter")
|
||||
case:
|
||||
// Unknown error
|
||||
panic_contextless("base/runtime: BCryptGenRandom failed")
|
||||
}
|
||||
}
|
||||
|
||||
_exit :: proc "contextless" (code: int) -> ! {
|
||||
ExitProcess(u32(code))
|
||||
}
|
||||
@@ -184,10 +184,11 @@ print_rune :: #force_no_inline proc "contextless" (r: rune) -> int #no_bounds_ch
|
||||
|
||||
|
||||
print_u64 :: #force_no_inline proc "contextless" (x: u64) #no_bounds_check {
|
||||
b :: u64(10)
|
||||
u := x
|
||||
|
||||
a: [129]byte
|
||||
i := len(a)
|
||||
b := u64(10)
|
||||
u := x
|
||||
for u >= b {
|
||||
i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
|
||||
u /= b
|
||||
@@ -199,11 +200,9 @@ print_u64 :: #force_no_inline proc "contextless" (x: u64) #no_bounds_check {
|
||||
|
||||
|
||||
print_i64 :: #force_no_inline proc "contextless" (x: i64) #no_bounds_check {
|
||||
b :: i64(10)
|
||||
|
||||
u := x
|
||||
neg := u < 0
|
||||
u = abs(u)
|
||||
b :: u64(10)
|
||||
u := u64(abs(x))
|
||||
neg := x < 0
|
||||
|
||||
a: [129]byte
|
||||
i := len(a)
|
||||
@@ -281,11 +280,22 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
|
||||
print_byte('i' if info.signed else 'u')
|
||||
print_u64(u64(8*ti.size))
|
||||
}
|
||||
switch info.endianness {
|
||||
case .Platform: // nothing
|
||||
case .Little: print_string("le")
|
||||
case .Big: print_string("be")
|
||||
}
|
||||
|
||||
case Type_Info_Rune:
|
||||
print_string("rune")
|
||||
case Type_Info_Float:
|
||||
print_byte('f')
|
||||
print_u64(u64(8*ti.size))
|
||||
switch info.endianness {
|
||||
case .Platform: // nothing
|
||||
case .Little: print_string("le")
|
||||
case .Big: print_string("be")
|
||||
}
|
||||
case Type_Info_Complex:
|
||||
print_string("complex")
|
||||
print_u64(u64(8*ti.size))
|
||||
@@ -293,7 +303,14 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
|
||||
print_string("quaternion")
|
||||
print_u64(u64(8*ti.size))
|
||||
case Type_Info_String:
|
||||
if info.is_cstring {
|
||||
print_byte('c')
|
||||
}
|
||||
print_string("string")
|
||||
switch info.encoding {
|
||||
case .UTF_8: /**/
|
||||
case .UTF_16: print_string("16")
|
||||
}
|
||||
case Type_Info_Boolean:
|
||||
switch ti.id {
|
||||
case bool: print_string("bool")
|
||||
@@ -375,6 +392,12 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
|
||||
print_string("[]")
|
||||
print_type(info.elem)
|
||||
|
||||
case Type_Info_Fixed_Capacity_Dynamic_Array:
|
||||
print_string("[dynamic; ")
|
||||
print_u64(u64(info.capacity))
|
||||
print_string("]")
|
||||
print_type(info.elem)
|
||||
|
||||
case Type_Info_Map:
|
||||
print_string("map[")
|
||||
print_type(info.key)
|
||||
@@ -401,9 +424,10 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
|
||||
}
|
||||
|
||||
print_string("struct ")
|
||||
if .packed in info.flags { print_string("#packed ") }
|
||||
if .raw_union in info.flags { print_string("#raw_union ") }
|
||||
if .no_copy in info.flags { print_string("#no_copy ") }
|
||||
if .packed in info.flags { print_string("#packed ") }
|
||||
if .raw_union in info.flags { print_string("#raw_union ") }
|
||||
if .all_or_none in info.flags { print_string("#all_or_none ") }
|
||||
if .simple in info.flags { print_string("#simple ") }
|
||||
if .align in info.flags {
|
||||
print_string("#align(")
|
||||
print_u64(u64(ti.align))
|
||||
@@ -460,7 +484,7 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
|
||||
print_string("..")
|
||||
print_i64(info.upper)
|
||||
}
|
||||
if info.underlying != nil {
|
||||
if info.explicit_underlying {
|
||||
print_string("; ")
|
||||
print_type(info.underlying)
|
||||
}
|
||||
@@ -488,6 +512,9 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
|
||||
print_type(info.elem)
|
||||
|
||||
case Type_Info_Matrix:
|
||||
if info.layout == .Row_Major {
|
||||
print_string("#row_major ")
|
||||
}
|
||||
print_string("matrix[")
|
||||
print_u64(u64(info.row_count))
|
||||
print_string(", ")
|
||||
@@ -496,3 +523,425 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
|
||||
print_type(info.elem)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
write_string :: proc "contextless" (i: ^int, dst: []byte, src: string) -> bool {
|
||||
if i^ < len(dst) {
|
||||
i^ += copy(dst[i^:], src)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
write_byte :: proc "contextless" (i: ^int, dst: []byte, src: byte) -> bool {
|
||||
if i^ < len(dst) {
|
||||
dst[i^] = src
|
||||
i^ += 1
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
write_u64 :: proc "contextless" (j: ^int, dst: []byte, x: u64) -> bool {
|
||||
if j^ < len(dst) {
|
||||
b :: u64(10)
|
||||
u := x
|
||||
|
||||
a: [129]byte
|
||||
i := len(a)
|
||||
for u >= b {
|
||||
i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
|
||||
u /= b
|
||||
}
|
||||
i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
|
||||
|
||||
return write_string(j, dst, string(a[i:]))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
write_i64 :: proc "contextless" (j: ^int, dst: []byte, x: i64) -> bool {
|
||||
if j^ < len(dst) {
|
||||
b :: u64(10)
|
||||
u := u64(abs(x))
|
||||
neg := x < 0
|
||||
|
||||
a: [129]byte
|
||||
i := len(a)
|
||||
for u >= b {
|
||||
i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
|
||||
u /= b
|
||||
}
|
||||
i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
|
||||
if neg {
|
||||
i -= 1; a[i] = '-'
|
||||
}
|
||||
|
||||
return write_string(j, dst, string(a[i:]))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
write_caller_location :: #force_no_inline proc "contextless" (i: ^int, buf: []byte, loc: Source_Code_Location) -> bool {
|
||||
write_string(i, buf, loc.file_path) or_return
|
||||
|
||||
when ODIN_ERROR_POS_STYLE == .Default {
|
||||
write_byte(i, buf, '(') or_return
|
||||
write_u64 (i, buf, u64(loc.line)) or_return
|
||||
if loc.column != 0 {
|
||||
write_byte(i, buf, ':') or_return
|
||||
write_u64 (i, buf, u64(loc.column)) or_return
|
||||
}
|
||||
write_byte(i, buf, ')') or_return
|
||||
return true
|
||||
} else when ODIN_ERROR_POS_STYLE == .Unix {
|
||||
write_byte(i, buf, ':') or_return
|
||||
write_u64 (i, buf, u64(loc.line)) or_return
|
||||
if loc.column != 0 {
|
||||
write_byte(i, buf, ':') or_return
|
||||
write_u64 (i, buf, u64(loc.column)) or_return
|
||||
}
|
||||
write_byte(i, buf, ':') or_return
|
||||
return true
|
||||
} else {
|
||||
#panic("unhandled ODIN_ERROR_POS_STYLE")
|
||||
}
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
write_typeid :: #force_no_inline proc "contextless" (i: ^int, buf: []byte, id: typeid) -> bool {
|
||||
when ODIN_NO_RTTI {
|
||||
if id == nil {
|
||||
write_string(i, buf, "nil") or_return
|
||||
} else {
|
||||
write_string(i, buf, "<unknown type>") or_return
|
||||
}
|
||||
} else {
|
||||
if id == nil {
|
||||
write_string(i, buf, "nil") or_return
|
||||
} else {
|
||||
ti := type_info_of(id)
|
||||
write_write_type(i, buf, ti) or_return
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
write_rune :: #force_no_inline proc "contextless" (i: ^int, buf: []byte, r: rune) -> (written: int, ok: bool) #no_bounds_check {
|
||||
RUNE_SELF :: 0x80
|
||||
|
||||
if r < RUNE_SELF {
|
||||
write_byte(i, buf,byte(r)) or_return
|
||||
return 1, true
|
||||
}
|
||||
|
||||
b, n := encode_rune(r)
|
||||
prev := i^
|
||||
write_string(i, buf, string(b[:n])) or_return
|
||||
return i^ - prev, true
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
write_encoded_rune :: #force_no_inline proc "contextless" (i: ^int, buf: []byte, r: rune) -> bool {
|
||||
write_byte(i, buf, '\'') or_return
|
||||
|
||||
switch r {
|
||||
case '\a': write_string(i, buf, "\\a") or_return
|
||||
case '\b': write_string(i, buf, "\\b") or_return
|
||||
case '\e': write_string(i, buf, "\\e") or_return
|
||||
case '\f': write_string(i, buf, "\\f") or_return
|
||||
case '\n': write_string(i, buf, "\\n") or_return
|
||||
case '\r': write_string(i, buf, "\\r") or_return
|
||||
case '\t': write_string(i, buf, "\\t") or_return
|
||||
case '\v': write_string(i, buf, "\\v") or_return
|
||||
case:
|
||||
if r <= 0 {
|
||||
write_string(i, buf, "\\x00") or_return
|
||||
} else if r < 32 {
|
||||
n0, n1 := u8(r) >> 4, u8(r) & 0xf
|
||||
write_string(i, buf, "\\x") or_return
|
||||
write_byte (i, buf, _INTEGER_DIGITS_VAR[n0]) or_return
|
||||
write_byte (i, buf, _INTEGER_DIGITS_VAR[n1]) or_return
|
||||
} else {
|
||||
_ = write_rune(i, buf, r) or_return
|
||||
}
|
||||
}
|
||||
|
||||
write_byte(i, buf, '\'') or_return
|
||||
return true
|
||||
}
|
||||
|
||||
@(optimization_mode="favor_size")
|
||||
write_write_type :: #force_no_inline proc "contextless" (i: ^int, buf: []byte, ti: ^Type_Info) -> bool {
|
||||
if ti == nil {
|
||||
write_string(i, buf, "nil") or_return
|
||||
return true
|
||||
}
|
||||
|
||||
switch info in ti.variant {
|
||||
case Type_Info_Named:
|
||||
write_string(i, buf, info.name) or_return
|
||||
case Type_Info_Integer:
|
||||
switch ti.id {
|
||||
case int: write_string(i, buf, "int") or_return
|
||||
case uint: write_string(i, buf, "uint") or_return
|
||||
case uintptr: write_string(i, buf, "uintptr") or_return
|
||||
case:
|
||||
write_byte(i, buf, 'i' if info.signed else 'u') or_return
|
||||
write_u64 (i, buf, u64(8*ti.size)) or_return
|
||||
}
|
||||
switch info.endianness {
|
||||
case .Platform: // nothing
|
||||
case .Little: write_string(i, buf, "le") or_return
|
||||
case .Big: write_string(i, buf, "be") or_return
|
||||
}
|
||||
|
||||
case Type_Info_Rune:
|
||||
write_string(i, buf, "rune") or_return
|
||||
case Type_Info_Float:
|
||||
write_byte(i, buf, 'f') or_return
|
||||
write_u64(i, buf, u64(8*ti.size)) or_return
|
||||
switch info.endianness {
|
||||
case .Platform: // nothing
|
||||
case .Little: write_string(i, buf, "le") or_return
|
||||
case .Big: write_string(i, buf, "be") or_return
|
||||
}
|
||||
|
||||
case Type_Info_Complex:
|
||||
write_string(i, buf, "complex") or_return
|
||||
write_u64 (i, buf, u64(8*ti.size)) or_return
|
||||
case Type_Info_Quaternion:
|
||||
write_string(i, buf, "quaternion") or_return
|
||||
write_u64 (i, buf, u64(8*ti.size)) or_return
|
||||
case Type_Info_String:
|
||||
if info.is_cstring {
|
||||
write_byte(i, buf, 'c') or_return
|
||||
}
|
||||
write_string(i, buf, "string") or_return
|
||||
switch info.encoding {
|
||||
case .UTF_8: /**/
|
||||
case .UTF_16: write_string(i, buf, "16") or_return
|
||||
}
|
||||
case Type_Info_Boolean:
|
||||
switch ti.id {
|
||||
case bool: write_string(i, buf, "bool") or_return
|
||||
case:
|
||||
write_byte(i, buf, 'b') or_return
|
||||
write_u64 (i, buf, u64(8*ti.size)) or_return
|
||||
}
|
||||
case Type_Info_Any:
|
||||
write_string(i, buf, "any") or_return
|
||||
case Type_Info_Type_Id:
|
||||
write_string(i, buf, "typeid") or_return
|
||||
|
||||
case Type_Info_Pointer:
|
||||
if info.elem == nil {
|
||||
write_string(i, buf, "rawptr") or_return
|
||||
} else {
|
||||
write_string (i, buf, "^") or_return
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
}
|
||||
case Type_Info_Multi_Pointer:
|
||||
write_string (i, buf, "[^]") or_return
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
case Type_Info_Soa_Pointer:
|
||||
write_string (i, buf, "#soa ^") or_return
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
case Type_Info_Procedure:
|
||||
write_string(i, buf, "proc") or_return
|
||||
if info.params == nil {
|
||||
write_string(i, buf, "()") or_return
|
||||
} else {
|
||||
t := info.params.variant.(Type_Info_Parameters)
|
||||
write_byte(i, buf, '(') or_return
|
||||
for t, j in t.types {
|
||||
if j > 0 { write_string(i, buf, ", ") or_return }
|
||||
write_write_type(i, buf, t) or_return
|
||||
}
|
||||
write_string(i, buf, ")") or_return
|
||||
}
|
||||
if info.results != nil {
|
||||
write_string (i, buf, " -> ") or_return
|
||||
write_write_type(i, buf, info.results) or_return
|
||||
}
|
||||
case Type_Info_Parameters:
|
||||
count := len(info.names)
|
||||
if count != 1 { write_byte(i, buf, '(') or_return }
|
||||
for name, j in info.names {
|
||||
if j > 0 { write_string(i, buf, ", ") or_return }
|
||||
|
||||
t := info.types[j]
|
||||
|
||||
if len(name) > 0 {
|
||||
write_string(i, buf, name) or_return
|
||||
write_string(i, buf, ": ") or_return
|
||||
}
|
||||
write_write_type(i, buf, t) or_return
|
||||
}
|
||||
if count != 1 { write_string(i, buf, ")") or_return }
|
||||
|
||||
case Type_Info_Array:
|
||||
write_byte (i, buf, '[') or_return
|
||||
write_u64 (i, buf, u64(info.count)) or_return
|
||||
write_byte (i, buf, ']') or_return
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
|
||||
case Type_Info_Enumerated_Array:
|
||||
if info.is_sparse {
|
||||
write_string(i, buf, "#sparse") or_return
|
||||
}
|
||||
write_byte (i, buf, '[') or_return
|
||||
write_write_type(i, buf, info.index) or_return
|
||||
write_byte (i, buf, ']') or_return
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
|
||||
|
||||
case Type_Info_Dynamic_Array:
|
||||
write_string (i, buf, "[dynamic]") or_return
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
case Type_Info_Slice:
|
||||
write_string (i, buf, "[]") or_return
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
|
||||
case Type_Info_Fixed_Capacity_Dynamic_Array:
|
||||
write_string (i, buf, "[dynamic; ") or_return
|
||||
write_u64 (i, buf, u64(info.capacity)) or_return
|
||||
write_string (i, buf, "]") or_return
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
|
||||
case Type_Info_Map:
|
||||
write_string (i, buf, "map[") or_return
|
||||
write_write_type(i, buf, info.key) or_return
|
||||
write_byte (i, buf, ']') or_return
|
||||
write_write_type(i, buf, info.value) or_return
|
||||
|
||||
case Type_Info_Struct:
|
||||
switch info.soa_kind {
|
||||
case .None: // Ignore
|
||||
case .Fixed:
|
||||
write_string (i, buf, "#soa[") or_return
|
||||
write_u64 (i, buf, u64(info.soa_len)) or_return
|
||||
write_byte (i, buf, ']') or_return
|
||||
write_write_type(i, buf, info.soa_base_type) or_return
|
||||
return true
|
||||
case .Slice:
|
||||
write_string (i, buf, "#soa[]") or_return
|
||||
write_write_type(i, buf, info.soa_base_type) or_return
|
||||
return true
|
||||
case .Dynamic:
|
||||
write_string (i, buf, "#soa[dynamic]") or_return
|
||||
write_write_type(i, buf, info.soa_base_type) or_return
|
||||
return true
|
||||
}
|
||||
|
||||
write_string(i, buf, "struct ") or_return
|
||||
if .packed in info.flags { write_string(i, buf, "#packed ") or_return }
|
||||
if .raw_union in info.flags { write_string(i, buf, "#raw_union ") or_return }
|
||||
if .all_or_none in info.flags { write_string(i, buf, "#all_or_none ") or_return }
|
||||
if .simple in info.flags { write_string(i, buf, "#simple ") or_return }
|
||||
if .align in info.flags {
|
||||
write_string(i, buf, "#align(") or_return
|
||||
write_u64(i, buf, u64(ti.align)) or_return
|
||||
write_string(i, buf, ") ") or_return
|
||||
}
|
||||
write_byte(i, buf, '{') or_return
|
||||
for name, j in info.names[:info.field_count] {
|
||||
if j > 0 { write_string(i, buf, ", ") or_return }
|
||||
write_string (i, buf, name) or_return
|
||||
write_string (i, buf, ": ") or_return
|
||||
write_write_type(i, buf, info.types[j]) or_return
|
||||
}
|
||||
write_byte(i, buf, '}') or_return
|
||||
|
||||
case Type_Info_Union:
|
||||
write_string(i, buf, "union ") or_return
|
||||
if info.custom_align {
|
||||
write_string(i, buf, "#align(") or_return
|
||||
write_u64 (i, buf, u64(ti.align)) or_return
|
||||
write_string(i, buf, ") ") or_return
|
||||
}
|
||||
if info.no_nil {
|
||||
write_string(i, buf, "#no_nil ") or_return
|
||||
}
|
||||
write_byte(i, buf, '{') or_return
|
||||
for variant, j in info.variants {
|
||||
if j > 0 { write_string(i, buf, ", ") or_return }
|
||||
write_write_type(i, buf, variant) or_return
|
||||
}
|
||||
write_string(i, buf, "}") or_return
|
||||
|
||||
case Type_Info_Enum:
|
||||
write_string (i, buf, "enum ") or_return
|
||||
write_write_type(i, buf, info.base) or_return
|
||||
write_string (i, buf, " {") or_return
|
||||
for name, j in info.names {
|
||||
if j > 0 { write_string(i, buf, ", ") or_return }
|
||||
write_string(i, buf, name) or_return
|
||||
}
|
||||
write_string(i, buf, "}") or_return
|
||||
|
||||
case Type_Info_Bit_Set:
|
||||
write_string(i, buf, "bit_set[") or_return
|
||||
|
||||
#partial switch elem in type_info_base(info.elem).variant {
|
||||
case Type_Info_Enum:
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
case Type_Info_Rune:
|
||||
write_encoded_rune(i, buf, rune(info.lower)) or_return
|
||||
write_string (i, buf, "..") or_return
|
||||
write_encoded_rune(i, buf, rune(info.upper)) or_return
|
||||
case:
|
||||
write_i64 (i, buf, info.lower) or_return
|
||||
write_string(i, buf, "..") or_return
|
||||
write_i64 (i, buf, info.upper) or_return
|
||||
}
|
||||
if info.explicit_underlying {
|
||||
write_string (i, buf, "; ") or_return
|
||||
write_write_type(i, buf, info.underlying) or_return
|
||||
}
|
||||
write_byte(i, buf, ']') or_return
|
||||
|
||||
case Type_Info_Bit_Field:
|
||||
write_string (i, buf, "bit_field ") or_return
|
||||
write_write_type(i, buf, info.backing_type) or_return
|
||||
write_string (i, buf, " {") or_return
|
||||
for name, j in info.names[:info.field_count] {
|
||||
if j > 0 { write_string(i, buf, ", ") or_return }
|
||||
write_string (i, buf, name) or_return
|
||||
write_string (i, buf, ": ") or_return
|
||||
write_write_type(i, buf, info.types[j]) or_return
|
||||
write_string (i, buf, " | ") or_return
|
||||
write_u64 (i, buf, u64(info.bit_sizes[j])) or_return
|
||||
}
|
||||
write_byte(i, buf, '}') or_return
|
||||
|
||||
|
||||
case Type_Info_Simd_Vector:
|
||||
write_string (i, buf, "#simd[") or_return
|
||||
write_u64 (i, buf, u64(info.count)) or_return
|
||||
write_byte (i, buf, ']') or_return
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
|
||||
case Type_Info_Matrix:
|
||||
if info.layout == .Row_Major {
|
||||
write_string(i, buf, "#row_major ") or_return
|
||||
}
|
||||
write_string (i, buf, "matrix[") or_return
|
||||
write_u64 (i, buf, u64(info.row_count)) or_return
|
||||
write_string (i, buf, ", ") or_return
|
||||
write_u64 (i, buf, u64(info.column_count)) or_return
|
||||
write_string (i, buf, "]") or_return
|
||||
write_write_type(i, buf, info.elem) or_return
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
#+private
|
||||
package runtime
|
||||
|
||||
@(priority_index=-1e6)
|
||||
@(priority_index=-1e5)
|
||||
foreign import ObjC "system:objc"
|
||||
|
||||
@(priority_index=-1e6)
|
||||
foreign import libSystem "system:System"
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
objc_id :: ^intrinsics.objc_object
|
||||
@@ -12,16 +15,25 @@ objc_SEL :: ^intrinsics.objc_selector
|
||||
objc_Ivar :: ^intrinsics.objc_ivar
|
||||
objc_BOOL :: bool
|
||||
|
||||
objc_super :: struct {
|
||||
receiver: objc_id,
|
||||
super_class: objc_Class,
|
||||
}
|
||||
|
||||
objc_IMP :: proc "c" (object: objc_id, sel: objc_SEL, #c_vararg args: ..any) -> objc_id
|
||||
|
||||
foreign ObjC {
|
||||
sel_registerName :: proc "c" (name: cstring) -> objc_SEL ---
|
||||
|
||||
objc_msgSend :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) ---
|
||||
objc_msgSend_fpret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> f64 ---
|
||||
objc_msgSend_fp2ret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> complex128 ---
|
||||
objc_msgSend_stret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) ---
|
||||
objc_msgSend :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) ---
|
||||
objc_msgSend_fpret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> f64 ---
|
||||
objc_msgSend_fp2ret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> complex128 ---
|
||||
objc_msgSend_stret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) ---
|
||||
|
||||
// See: https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/objc-abi.h#L111
|
||||
objc_msgSendSuper2 :: proc "c" (super: rawptr, op: objc_SEL, #c_vararg args: ..any) -> objc_id ---
|
||||
objc_msgSendSuper2_stret :: proc "c" (super: ^objc_super, op: objc_SEL, #c_vararg args: ..any) ---
|
||||
|
||||
|
||||
objc_lookUpClass :: proc "c" (name: cstring) -> objc_Class ---
|
||||
objc_allocateClassPair :: proc "c" (superclass: objc_Class, name: cstring, extraBytes: uint) -> objc_Class ---
|
||||
@@ -30,6 +42,15 @@ foreign ObjC {
|
||||
class_addIvar :: proc "c" (cls: objc_Class, name: cstring, size: uint, alignment: u8, types: cstring) -> objc_BOOL ---
|
||||
class_getInstanceVariable :: proc "c" (cls : objc_Class, name: cstring) -> objc_Ivar ---
|
||||
class_getInstanceSize :: proc "c" (cls : objc_Class) -> uint ---
|
||||
class_getSuperclass :: proc "c" (cls : objc_Class) -> objc_Class ---
|
||||
ivar_getOffset :: proc "c" (v: objc_Ivar) -> uintptr ---
|
||||
object_getClass :: proc "c" (obj: objc_id) -> objc_Class ---
|
||||
}
|
||||
|
||||
foreign libSystem {
|
||||
_NSConcreteGlobalBlock: intrinsics.objc_class
|
||||
_NSConcreteStackBlock: intrinsics.objc_class
|
||||
|
||||
_Block_object_assign :: proc "c" (rawptr, rawptr, i32) ---
|
||||
_Block_object_dispose :: proc "c" (rawptr, i32) ---
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ package runtime
|
||||
|
||||
init_default_context_for_js: Context
|
||||
@(init, private="file")
|
||||
init_default_context :: proc() {
|
||||
init_default_context_for_js = context
|
||||
init_default_context :: proc "contextless" () {
|
||||
__init_context(&init_default_context_for_js)
|
||||
}
|
||||
|
||||
@(export)
|
||||
|
||||
@@ -41,88 +41,3 @@ random_generator_reset_u64 :: proc(rg: Random_Generator, p: u64) {
|
||||
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}
|
||||
}
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
default_random_generator :: proc "contextless" (state: ^Default_Random_State = nil) -> Random_Generator {
|
||||
return {
|
||||
procedure = default_random_generator_proc,
|
||||
data = state,
|
||||
}
|
||||
}
|
||||
164
base/runtime/random_generator_chacha8.odin
Normal file
164
base/runtime/random_generator_chacha8.odin
Normal file
@@ -0,0 +1,164 @@
|
||||
package runtime
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
// This is an implementation of the Chacha8Rand DRBG, as specified
|
||||
// in https://github.com/C2SP/C2SP/blob/main/chacha8rand.md
|
||||
//
|
||||
// There is a tradeoff to be made between state-size and performance,
|
||||
// in terms of the amount of rng output buffered.
|
||||
//
|
||||
// The sensible buffer sizes are:
|
||||
// - 256-bytes: 128-bit SIMD with 16x vector registers (SSE2)
|
||||
// - 512-bytes: 128-bit SIMD with 32x vector registers (ARMv8),
|
||||
// 256-bit SIMD with 16x vector registers (AVX2),
|
||||
// - 1024-bytes: AVX-512
|
||||
//
|
||||
// Notes:
|
||||
// - Smaller than 256-bytes is possible but would require redundant
|
||||
// calls to the ChaCha8 function, which is prohibitively expensive.
|
||||
// - Larger than 1024-bytes is possible but pointless as the construct
|
||||
// is defined around 992-bytes of RNG output and 32-bytes of input
|
||||
// per iteration.
|
||||
//
|
||||
// This implementation opts for a 1024-byte buffer for simplicity,
|
||||
// under the rationale that modern extremely memory constrained targets
|
||||
// provide suitable functionality in hardware, and the language makes
|
||||
// supporting the various SIMD flavors easy.
|
||||
|
||||
@(private = "file")
|
||||
RNG_SEED_SIZE :: 32
|
||||
@(private)
|
||||
RNG_OUTPUT_PER_ITER :: 1024 - RNG_SEED_SIZE
|
||||
|
||||
@(private)
|
||||
CHACHA_SIGMA_0: u32 : 0x61707865
|
||||
@(private)
|
||||
CHACHA_SIGMA_1: u32 : 0x3320646e
|
||||
@(private)
|
||||
CHACHA_SIGMA_2: u32 : 0x79622d32
|
||||
@(private)
|
||||
CHACHA_SIGMA_3: u32 : 0x6b206574
|
||||
@(private)
|
||||
CHACHA_ROUNDS :: 8
|
||||
|
||||
Default_Random_State :: struct {
|
||||
_buf: [1024]byte,
|
||||
_off: int,
|
||||
_seeded: bool,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
default_random_generator :: proc "contextless" (state: ^Default_Random_State = nil) -> Random_Generator {
|
||||
return {
|
||||
procedure = default_random_generator_proc,
|
||||
data = state,
|
||||
}
|
||||
}
|
||||
|
||||
default_random_generator_proc :: proc(data: rawptr, mode: Random_Generator_Mode, p: []byte) {
|
||||
@(thread_local)
|
||||
state: Default_Random_State
|
||||
|
||||
r: ^Default_Random_State = &state
|
||||
if data != nil {
|
||||
r = cast(^Default_Random_State)data
|
||||
}
|
||||
next_seed := r._buf[RNG_OUTPUT_PER_ITER:]
|
||||
|
||||
switch mode {
|
||||
case .Read:
|
||||
if !r._seeded { // Unlikely.
|
||||
rand_bytes(next_seed)
|
||||
r._off = RNG_OUTPUT_PER_ITER // Force refill.
|
||||
r._seeded = true
|
||||
}
|
||||
|
||||
assert(r._off <= RNG_OUTPUT_PER_ITER, "chacha8rand/BUG: outputed key material")
|
||||
if r._off >= RNG_OUTPUT_PER_ITER { // Unlikely.
|
||||
chacha8rand_refill(r)
|
||||
}
|
||||
|
||||
// We are guaranteed to have at least some RNG output buffered.
|
||||
//
|
||||
// As an invariant each read will consume a multiple of 8-bytes
|
||||
// of output at a time.
|
||||
assert(r._off <= RNG_OUTPUT_PER_ITER - 8, "chacha8rand/BUG: less than 8-bytes of output available")
|
||||
assert(r._off % 8 == 0, "chacha8rand/BUG: buffered output is not a multiple of 8-bytes")
|
||||
|
||||
p_len := len(p)
|
||||
if p_len == size_of(u64) {
|
||||
#no_bounds_check {
|
||||
// Fast path for a 64-bit destination.
|
||||
src := (^u64)(raw_data(r._buf[r._off:]))
|
||||
intrinsics.unaligned_store((^u64)(raw_data(p)), src^)
|
||||
src^ = 0 // Erasure (backtrack resistance)
|
||||
r._off += 8
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
p_ := p
|
||||
for remaining := p_len; remaining > 0; {
|
||||
sz := min(remaining, RNG_OUTPUT_PER_ITER - r._off)
|
||||
#no_bounds_check {
|
||||
copy(p_[:sz], r._buf[r._off:])
|
||||
p_ = p_[sz:]
|
||||
remaining -= sz
|
||||
}
|
||||
rounded_sz := ((sz + 7) / 8) * 8
|
||||
new_off := r._off + rounded_sz
|
||||
#no_bounds_check if new_off < RNG_OUTPUT_PER_ITER {
|
||||
// Erasure (backtrack resistance)
|
||||
intrinsics.mem_zero(raw_data(r._buf[r._off:]), rounded_sz)
|
||||
r._off = new_off
|
||||
} else {
|
||||
// Can omit erasure since we are overwriting the entire
|
||||
// buffer.
|
||||
chacha8rand_refill(r)
|
||||
}
|
||||
}
|
||||
|
||||
case .Reset:
|
||||
// If no seed is passed, the next call to .Read will attempt to
|
||||
// reseed from the system entropy source.
|
||||
if len(p) == 0 {
|
||||
r._seeded = false
|
||||
return
|
||||
}
|
||||
|
||||
// The cryptographic security of the output depends entirely
|
||||
// on the quality of the entropy in the seed, we will allow
|
||||
// re-seeding (as it makes testing easier), but callers that
|
||||
// decide to provide arbitrary seeds are on their own as far
|
||||
// as ensuring high-quality entropy.
|
||||
intrinsics.mem_zero(raw_data(next_seed), RNG_SEED_SIZE)
|
||||
copy(next_seed, p)
|
||||
r._seeded = true
|
||||
r._off = RNG_OUTPUT_PER_ITER // Force a refill.
|
||||
|
||||
case .Query_Info:
|
||||
if len(p) != size_of(Random_Generator_Query_Info) {
|
||||
return
|
||||
}
|
||||
info := (^Random_Generator_Query_Info)(raw_data(p))
|
||||
info^ += {.Uniform, .Cryptographic, .Resettable}
|
||||
}
|
||||
}
|
||||
|
||||
@(private = "file")
|
||||
chacha8rand_refill :: proc(r: ^Default_Random_State) {
|
||||
assert(r._seeded == true, "chacha8rand/BUG: unseeded refill")
|
||||
|
||||
// i386 has insufficient vector registers to use the
|
||||
// accelerated path at the moment.
|
||||
when ODIN_ARCH == .amd64 && intrinsics.has_target_feature("avx2") {
|
||||
chacha8rand_refill_simd256(r)
|
||||
} else when HAS_HARDWARE_SIMD && ODIN_ARCH != .i386 {
|
||||
chacha8rand_refill_simd128(r)
|
||||
} else {
|
||||
chacha8rand_refill_ref(r)
|
||||
}
|
||||
|
||||
r._off = 0
|
||||
}
|
||||
145
base/runtime/random_generator_chacha8_ref.odin
Normal file
145
base/runtime/random_generator_chacha8_ref.odin
Normal file
@@ -0,0 +1,145 @@
|
||||
package runtime
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
@(private)
|
||||
chacha8rand_refill_ref :: proc(r: ^Default_Random_State) {
|
||||
// Initialize the base state.
|
||||
k: [^]u32 = (^u32)(raw_data(r._buf[RNG_OUTPUT_PER_ITER:]))
|
||||
when ODIN_ENDIAN == .Little {
|
||||
s4 := k[0]
|
||||
s5 := k[1]
|
||||
s6 := k[2]
|
||||
s7 := k[3]
|
||||
s8 := k[4]
|
||||
s9 := k[5]
|
||||
s10 := k[6]
|
||||
s11 := k[7]
|
||||
} else {
|
||||
s4 := intrinsics.byte_swap(k[0])
|
||||
s5 := intrinsics.byte_swap(k[1])
|
||||
s6 := intrinsics.byte_swap(k[2])
|
||||
s7 := intrinsics.byte_swap(k[3])
|
||||
s8 := intrinsics.byte_swap(k[4])
|
||||
s9 := intrinsics.byte_swap(k[5])
|
||||
s10 := intrinsics.byte_swap(k[6])
|
||||
s11 := intrinicss.byte_swap(k[7])
|
||||
}
|
||||
s12: u32 // Counter starts at 0.
|
||||
s13, s14, s15: u32 // IV of all 0s.
|
||||
|
||||
dst: [^]u32 = (^u32)(raw_data(r._buf[:]))
|
||||
|
||||
// At least with LLVM21 force_inline produces identical perf to
|
||||
// manual inlining, yay.
|
||||
quarter_round := #force_inline proc "contextless" (a, b, c, d: u32) -> (u32, u32, u32, u32) {
|
||||
a, b, c, d := a, b, c, d
|
||||
|
||||
a += b
|
||||
d ~= a
|
||||
d = rotl(d, 16)
|
||||
|
||||
c += d
|
||||
b ~= c
|
||||
b = rotl(b, 12)
|
||||
|
||||
a += b
|
||||
d ~= a
|
||||
d = rotl(d, 8)
|
||||
|
||||
c += d
|
||||
b ~= c
|
||||
b = rotl(b, 7)
|
||||
|
||||
return a, b, c, d
|
||||
}
|
||||
|
||||
// Filippo Valsorda made an observation that only one of the column
|
||||
// round depends on the counter (s12), so it is worth precomputing
|
||||
// and reusing across multiple blocks. As far as I know, only Go's
|
||||
// chacha implementation does this.
|
||||
|
||||
p1, p5, p9, p13 := quarter_round(CHACHA_SIGMA_1, s5, s9, s13)
|
||||
p2, p6, p10, p14 := quarter_round(CHACHA_SIGMA_2, s6, s10, s14)
|
||||
p3, p7, p11, p15 := quarter_round(CHACHA_SIGMA_3, s7, s11, s15)
|
||||
|
||||
// 4 groups
|
||||
for g := 0; g < 4; g = g + 1 {
|
||||
// 4 blocks per group
|
||||
for n := 0; n < 4; n = n + 1 {
|
||||
// First column round that depends on the counter
|
||||
p0, p4, p8, p12 := quarter_round(CHACHA_SIGMA_0, s4, s8, s12)
|
||||
|
||||
// First diagonal round
|
||||
x0, x5, x10, x15 := quarter_round(p0, p5, p10, p15)
|
||||
x1, x6, x11, x12 := quarter_round(p1, p6, p11, p12)
|
||||
x2, x7, x8, x13 := quarter_round(p2, p7, p8, p13)
|
||||
x3, x4, x9, x14 := quarter_round(p3, p4, p9, p14)
|
||||
|
||||
for i := CHACHA_ROUNDS - 2; i > 0; i = i - 2 {
|
||||
x0, x4, x8, x12 = quarter_round(x0, x4, x8, x12)
|
||||
x1, x5, x9, x13 = quarter_round(x1, x5, x9, x13)
|
||||
x2, x6, x10, x14 = quarter_round(x2, x6, x10, x14)
|
||||
x3, x7, x11, x15 = quarter_round(x3, x7, x11, x15)
|
||||
|
||||
x0, x5, x10, x15 = quarter_round(x0, x5, x10, x15)
|
||||
x1, x6, x11, x12 = quarter_round(x1, x6, x11, x12)
|
||||
x2, x7, x8, x13 = quarter_round(x2, x7, x8, x13)
|
||||
x3, x4, x9, x14 = quarter_round(x3, x4, x9, x14)
|
||||
}
|
||||
|
||||
// Interleave 4 blocks
|
||||
// NB: The additions of sigma and the counter are omitted
|
||||
STRIDE :: 4
|
||||
d_ := dst[n:]
|
||||
when ODIN_ENDIAN == .Little {
|
||||
d_[STRIDE*0] = x0
|
||||
d_[STRIDE*1] = x1
|
||||
d_[STRIDE*2] = x2
|
||||
d_[STRIDE*3] = x3
|
||||
d_[STRIDE*4] = x4 + s4
|
||||
d_[STRIDE*5] = x5 + s5
|
||||
d_[STRIDE*6] = x6 + s6
|
||||
d_[STRIDE*7] = x7 + s7
|
||||
d_[STRIDE*8] = x8 + s8
|
||||
d_[STRIDE*9] = x9 + s9
|
||||
d_[STRIDE*10] = x10 + s10
|
||||
d_[STRIDE*11] = x11 + s11
|
||||
d_[STRIDE*12] = x12
|
||||
d_[STRIDE*13] = x13 + s13
|
||||
d_[STRIDE*14] = x14 + s14
|
||||
d_[STRIDE*15] = x15 + s15
|
||||
} else {
|
||||
d_[STRIDE*0] = intrinsics.byte_swap(x0)
|
||||
d_[STRIDE*1] = intrinsics.byte_swap(x1)
|
||||
d_[STRIDE*2] = intrinsics.byte_swap(x2)
|
||||
d_[STRIDE*3] = intrinsics.byte_swap(x3)
|
||||
d_[STRIDE*4] = intrinsics.byte_swap(x4 + s4)
|
||||
d_[STRIDE*5] = intrinsics.byte_swap(x5 + s5)
|
||||
d_[STRIDE*6] = intrinsics.byte_swap(x6 + s6)
|
||||
d_[STRIDE*7] = intrinsics.byte_swap(x7 + s7)
|
||||
d_[STRIDE*8] = intrinsics.byte_swap(x8 + s8)
|
||||
d_[STRIDE*9] = intrinsics.byte_swap(x9 + s9)
|
||||
d_[STRIDE*10] = intrinsics.byte_swap(x10 + s10)
|
||||
d_[STRIDE*11] = intrinsics.byte_swap(x11 + s11)
|
||||
d_[STRIDE*12] = intrinsics.byte_swap(x12)
|
||||
d_[STRIDE*13] = intrinsics.byte_swap(x13 + s13)
|
||||
d_[STRIDE*14] = intrinsics.byte_swap(x14 + s14)
|
||||
d_[STRIDE*15] = intrinsics.byte_swap(x15 + s15)
|
||||
}
|
||||
|
||||
s12 = s12 + 1 // Increment the counter
|
||||
}
|
||||
|
||||
dst = dst[16*4:]
|
||||
}
|
||||
}
|
||||
|
||||
// This replicates `rotate_left32` from `core:math/bits`, under the
|
||||
// assumption that this will live in `base:runtime`.
|
||||
@(require_results, private = "file")
|
||||
rotl :: #force_inline proc "contextless" (x: u32, k: int) -> u32 {
|
||||
n :: 32
|
||||
s := uint(k) & (n-1)
|
||||
return x << s | x >> (n-s)
|
||||
}
|
||||
290
base/runtime/random_generator_chacha8_simd128.odin
Normal file
290
base/runtime/random_generator_chacha8_simd128.odin
Normal file
@@ -0,0 +1,290 @@
|
||||
#+build !i386
|
||||
package runtime
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
@(private = "file")
|
||||
u32x4 :: #simd[4]u32
|
||||
|
||||
@(private = "file")
|
||||
S0: u32x4 : {CHACHA_SIGMA_0, CHACHA_SIGMA_0, CHACHA_SIGMA_0, CHACHA_SIGMA_0}
|
||||
@(private = "file")
|
||||
S1: u32x4 : {CHACHA_SIGMA_1, CHACHA_SIGMA_1, CHACHA_SIGMA_1, CHACHA_SIGMA_1}
|
||||
@(private = "file")
|
||||
S2: u32x4 : {CHACHA_SIGMA_2, CHACHA_SIGMA_2, CHACHA_SIGMA_2, CHACHA_SIGMA_2}
|
||||
@(private = "file")
|
||||
S3: u32x4 : {CHACHA_SIGMA_3, CHACHA_SIGMA_3, CHACHA_SIGMA_3, CHACHA_SIGMA_3}
|
||||
|
||||
@(private = "file")
|
||||
_ROT_7L: u32x4 : {7, 7, 7, 7}
|
||||
@(private = "file")
|
||||
_ROT_7R: u32x4 : {25, 25, 25, 25}
|
||||
@(private = "file")
|
||||
_ROT_12L: u32x4 : {12, 12, 12, 12}
|
||||
@(private = "file")
|
||||
_ROT_12R: u32x4 : {20, 20, 20, 20}
|
||||
@(private = "file")
|
||||
_ROT_8L: u32x4 : {8, 8, 8, 8}
|
||||
@(private = "file")
|
||||
_ROT_8R: u32x4 : {24, 24, 24, 24}
|
||||
@(private = "file")
|
||||
_ROT_16: u32x4 : {16, 16, 16, 16}
|
||||
@(private = "file")
|
||||
_CTR_INC_4: u32x4 : {4, 4, 4, 4}
|
||||
@(private = "file")
|
||||
_CTR_INC_8: u32x4 : {8, 8, 8, 8}
|
||||
|
||||
when ODIN_ENDIAN == .Big {
|
||||
@(private = "file")
|
||||
_byteswap_u32x4 :: #force_inline proc "contextless" (v: u32x4) -> u32x4 {
|
||||
u8x16 :: #simd[16]u8
|
||||
return(
|
||||
transmute(u32x4)simd.shuffle(
|
||||
transmute(u8x16)v,
|
||||
transmute(u8x16)v,
|
||||
3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
chacha8rand_refill_simd128 :: proc(r: ^Default_Random_State) {
|
||||
// Initialize the base state.
|
||||
k: [^]u32 = (^u32)(raw_data(r._buf[RNG_OUTPUT_PER_ITER:]))
|
||||
when ODIN_ENDIAN == .Little {
|
||||
s4_ := k[0]
|
||||
s5_ := k[1]
|
||||
s6_ := k[2]
|
||||
s7_ := k[3]
|
||||
s8_ := k[4]
|
||||
s9_ := k[5]
|
||||
s10_ := k[6]
|
||||
s11_ := k[7]
|
||||
} else {
|
||||
s4_ := intrinsics.byte_swap(k[0])
|
||||
s5_ := intrinsics.byte_swap(k[1])
|
||||
s6_ := intrinsics.byte_swap(k[2])
|
||||
s7_ := intrinsics.byte_swap(k[3])
|
||||
s8_ := intrinsics.byte_swap(k[4])
|
||||
s9_ := intrinsics.byte_swap(k[5])
|
||||
s10_ := intrinsics.byte_swap(k[6])
|
||||
s11_ := intrinicss.byte_swap(k[7])
|
||||
}
|
||||
|
||||
// 4-lane ChaCha8.
|
||||
s4 := u32x4{s4_, s4_, s4_, s4_}
|
||||
s5 := u32x4{s5_, s5_, s5_, s5_}
|
||||
s6 := u32x4{s6_, s6_, s6_, s6_}
|
||||
s7 := u32x4{s7_, s7_, s7_, s7_}
|
||||
s8 := u32x4{s8_, s8_, s8_, s8_}
|
||||
s9 := u32x4{s9_, s9_, s9_, s9_}
|
||||
s10 := u32x4{s10_, s10_, s10_, s10_}
|
||||
s11 := u32x4{s11_, s11_, s11_, s11_}
|
||||
s12 := u32x4{0, 1, 2, 3}
|
||||
s13, s14, s15: u32x4
|
||||
|
||||
dst: [^]u32x4 = (^u32x4)(raw_data(r._buf[:]))
|
||||
|
||||
quarter_round := #force_inline proc "contextless" (a, b, c, d: u32x4) -> (u32x4, u32x4, u32x4, u32x4) {
|
||||
a, b, c, d := a, b, c, d
|
||||
|
||||
a = intrinsics.simd_add(a, b)
|
||||
d = intrinsics.simd_bit_xor(d, a)
|
||||
d = intrinsics.simd_bit_xor(intrinsics.simd_shl(d, _ROT_16), intrinsics.simd_shr(d, _ROT_16))
|
||||
|
||||
c = intrinsics.simd_add(c, d)
|
||||
b = intrinsics.simd_bit_xor(b, c)
|
||||
b = intrinsics.simd_bit_xor(intrinsics.simd_shl(b, _ROT_12L), intrinsics.simd_shr(b, _ROT_12R))
|
||||
|
||||
a = intrinsics.simd_add(a, b)
|
||||
d = intrinsics.simd_bit_xor(d, a)
|
||||
d = intrinsics.simd_bit_xor(intrinsics.simd_shl(d, _ROT_8L), intrinsics.simd_shr(d, _ROT_8R))
|
||||
|
||||
c = intrinsics.simd_add(c, d)
|
||||
b = intrinsics.simd_bit_xor(b, c)
|
||||
b = intrinsics.simd_bit_xor(intrinsics.simd_shl(b, _ROT_7L), intrinsics.simd_shr(b, _ROT_7R))
|
||||
|
||||
return a, b, c, d
|
||||
}
|
||||
|
||||
// 8 blocks at a time.
|
||||
//
|
||||
// Note:
|
||||
// This uses a ton of registers so it is only worth it on targets
|
||||
// that have something like 32 128-bit registers. This is currently
|
||||
// all ARMv8 targets, and RISC-V Zvl128b (`V` application profile)
|
||||
// targets.
|
||||
//
|
||||
// While our current definition of `.arm32` is 32-bit ARMv8, this
|
||||
// may change in the future (ARMv7 is still relevant), and things
|
||||
// like Cortex-A8/A9 does "pretend" 128-bit SIMD 64-bits at a time
|
||||
// thus needs bemchmarking.
|
||||
when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
|
||||
for _ in 0..<2 {
|
||||
x0_0, x1_0, x2_0, x3_0 := S0, S1, S2, S3
|
||||
x4_0, x5_0, x6_0, x7_0 := s4, s5, s6, s7
|
||||
x8_0, x9_0, x10_0, x11_0 := s8, s9, s10, s11
|
||||
x12_0, x13_0, x14_0, x15_0 := s12, s13, s14, s15
|
||||
|
||||
x0_1, x1_1, x2_1, x3_1 := S0, S1, S2, S3
|
||||
x4_1, x5_1, x6_1, x7_1 := s4, s5, s6, s7
|
||||
x8_1, x9_1, x10_1, x11_1 := s8, s9, s10, s11
|
||||
x12_1 := intrinsics.simd_add(s12, _CTR_INC_4)
|
||||
x13_1, x14_1, x15_1 := s13, s14, s15
|
||||
|
||||
for i := CHACHA_ROUNDS; i > 0; i = i - 2 {
|
||||
x0_0, x4_0, x8_0, x12_0 = quarter_round(x0_0, x4_0, x8_0, x12_0)
|
||||
x0_1, x4_1, x8_1, x12_1 = quarter_round(x0_1, x4_1, x8_1, x12_1)
|
||||
x1_0, x5_0, x9_0, x13_0 = quarter_round(x1_0, x5_0, x9_0, x13_0)
|
||||
x1_1, x5_1, x9_1, x13_1 = quarter_round(x1_1, x5_1, x9_1, x13_1)
|
||||
x2_0, x6_0, x10_0, x14_0 = quarter_round(x2_0, x6_0, x10_0, x14_0)
|
||||
x2_1, x6_1, x10_1, x14_1 = quarter_round(x2_1, x6_1, x10_1, x14_1)
|
||||
x3_0, x7_0, x11_0, x15_0 = quarter_round(x3_0, x7_0, x11_0, x15_0)
|
||||
x3_1, x7_1, x11_1, x15_1 = quarter_round(x3_1, x7_1, x11_1, x15_1)
|
||||
|
||||
x0_0, x5_0, x10_0, x15_0 = quarter_round(x0_0, x5_0, x10_0, x15_0)
|
||||
x0_1, x5_1, x10_1, x15_1 = quarter_round(x0_1, x5_1, x10_1, x15_1)
|
||||
x1_0, x6_0, x11_0, x12_0 = quarter_round(x1_0, x6_0, x11_0, x12_0)
|
||||
x1_1, x6_1, x11_1, x12_1 = quarter_round(x1_1, x6_1, x11_1, x12_1)
|
||||
x2_0, x7_0, x8_0, x13_0 = quarter_round(x2_0, x7_0, x8_0, x13_0)
|
||||
x2_1, x7_1, x8_1, x13_1 = quarter_round(x2_1, x7_1, x8_1, x13_1)
|
||||
x3_0, x4_0, x9_0, x14_0 = quarter_round(x3_0, x4_0, x9_0, x14_0)
|
||||
x3_1, x4_1, x9_1, x14_1 = quarter_round(x3_1, x4_1, x9_1, x14_1)
|
||||
}
|
||||
|
||||
when ODIN_ENDIAN == .Little {
|
||||
intrinsics.unaligned_store((^u32x4)(dst[0:]), x0_0)
|
||||
intrinsics.unaligned_store((^u32x4)(dst[1:]), x1_0)
|
||||
intrinsics.unaligned_store((^u32x4)(dst[2:]), x2_0)
|
||||
intrinsics.unaligned_store((^u32x4)(dst[3:]), x3_0)
|
||||
intrinsics.unaligned_store((^u32x4)(dst[4:]), intrinsics.simd_add(x4_0, s4))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[5:]), intrinsics.simd_add(x5_0, s5))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[6:]), intrinsics.simd_add(x6_0, s6))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[7:]), intrinsics.simd_add(x7_0, s7))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[8:]), intrinsics.simd_add(x8_0, s8))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[9:]), intrinsics.simd_add(x9_0, s9))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[10:]), intrinsics.simd_add(x10_0, s10))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[11:]), intrinsics.simd_add(x11_0, s11))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[12:]), x12_0)
|
||||
intrinsics.unaligned_store((^u32x4)(dst[13:]), intrinsics.simd_add(x13_0, s13))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[14:]), intrinsics.simd_add(x14_0, s14))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[15:]), intrinsics.simd_add(x15_0, s15))
|
||||
|
||||
intrinsics.unaligned_store((^u32x4)(dst[16:]), x0_1)
|
||||
intrinsics.unaligned_store((^u32x4)(dst[17:]), x1_1)
|
||||
intrinsics.unaligned_store((^u32x4)(dst[18:]), x2_1)
|
||||
intrinsics.unaligned_store((^u32x4)(dst[19:]), x3_1)
|
||||
intrinsics.unaligned_store((^u32x4)(dst[20:]), intrinsics.simd_add(x4_1, s4))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[21:]), intrinsics.simd_add(x5_1, s5))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[22:]), intrinsics.simd_add(x6_1, s6))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[23:]), intrinsics.simd_add(x7_1, s7))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[24:]), intrinsics.simd_add(x8_1, s8))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[25:]), intrinsics.simd_add(x9_1, s9))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[26:]), intrinsics.simd_add(x10_1, s10))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[27:]), intrinsics.simd_add(x11_1, s11))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[28:]), x12_1)
|
||||
intrinsics.unaligned_store((^u32x4)(dst[29:]), intrinsics.simd_add(x13_1, s13))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[30:]), intrinsics.simd_add(x14_1, s14))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[31:]), intrinsics.simd_add(x15_1, s15))
|
||||
} else {
|
||||
intrinsics.unaligned_store((^u32x4)(dst[0:]), _byteswap_u32x4(x0_0))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[1:]), _byteswap_u32x4(x1_0))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[2:]), _byteswap_u32x4(x2_0))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[3:]), _byteswap_u32x4(x3_0))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[4:]), _byteswap_u32x4(intrinsics.simd_add(x4_0, s4)))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[5:]), _byteswap_u32x4(intrinsics.simd_add(x5_0, s5)))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[6:]), _byteswap_u32x4(intrinsics.simd_add(x6_0, s6)))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[7:]), _byteswap_u32x4(intrinsics.simd_add(x7_0, s7)))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[8:]), _byteswap_u32x4(intrinsics.simd_add(x8_0, s8)))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[9:]), _byteswap_u32x4(intrinsics.simd_add(x9_0, s9)))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[10:]), _byteswap_u32x4(intrinsics.simd_add(x10_0, s10)))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[11:]), _byteswap_u32x4(intrinsics.simd_add(x11_0, s11)))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[12:]), _byteswap_u32x4(x12_0))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[13:]), _byteswap_u32x4(intrinsics.simd_add(x13_0, s13)))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[14:]), _byteswap_u32x4(intrinsics.simd_add(x14_0, s14)))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[15:]), _byteswap_u32x4(intrinsics.simd_add(x15_0, s15)))
|
||||
|
||||
intrinsics.unaligned_store((^u32x4)(dst[16:]), _byteswap_u32x4(x0_1))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[17:]), _byteswap_u32x4(x1_1))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[18:]), _byteswap_u32x4(x2_1))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[19:]), _byteswap_u32x4(x3_1))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[20:]), _byteswap_u32x4(intrinsics.simd_add(x4_1, s4)))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[21:]), _byteswap_u32x4(intrinsics.simd_add(x5_1, s5)))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[22:]), _byteswap_u32x4(intrinsics.simd_add(x6_1, s6)))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[23:]), _byteswap_u32x4(intrinsics.simd_add(x7_1, s7)))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[24:]), _byteswap_u32x4(intrinsics.simd_add(x8_1, s8)))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[25:]), _byteswap_u32x4(intrinsics.simd_add(x9_1, s9)))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[26:]), _byteswap_u32x4(intrinsics.simd_add(x10_1, s10)))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[27:]), _byteswap_u32x4(intrinsics.simd_add(x11_1, s11)))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[28:]), _byteswap_u32x4(x12_1))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[29:]), _byteswap_u32x4(intrinsics.simd_add(x13_1, s13)))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[30:]), _byteswap_u32x4(intrinsics.simd_add(x14_1, s14)))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[31:]), _byteswap_u32x4(intrinsics.simd_add(x15_1, s15)))
|
||||
}
|
||||
|
||||
s12 = intrinsics.simd_add(s12, _CTR_INC_8)
|
||||
|
||||
dst = dst[32:]
|
||||
}
|
||||
} else {
|
||||
for _ in 0..<4 {
|
||||
x0, x1, x2, x3 := S0, S1, S2, S3
|
||||
x4, x5, x6, x7 := s4, s5, s6, s7
|
||||
x8, x9, x10, x11 := s8, s9, s10, s11
|
||||
x12, x13, x14, x15 := s12, s13, s14, s15
|
||||
|
||||
for i := CHACHA_ROUNDS; i > 0; i = i - 2 {
|
||||
x0, x4, x8, x12 = quarter_round(x0, x4, x8, x12)
|
||||
x1, x5, x9, x13 = quarter_round(x1, x5, x9, x13)
|
||||
x2, x6, x10, x14 = quarter_round(x2, x6, x10, x14)
|
||||
x3, x7, x11, x15 = quarter_round(x3, x7, x11, x15)
|
||||
|
||||
x0, x5, x10, x15 = quarter_round(x0, x5, x10, x15)
|
||||
x1, x6, x11, x12 = quarter_round(x1, x6, x11, x12)
|
||||
x2, x7, x8, x13 = quarter_round(x2, x7, x8, x13)
|
||||
x3, x4, x9, x14 = quarter_round(x3, x4, x9, x14)
|
||||
}
|
||||
|
||||
when ODIN_ENDIAN == .Little {
|
||||
intrinsics.unaligned_store((^u32x4)(dst[0:]), x0)
|
||||
intrinsics.unaligned_store((^u32x4)(dst[1:]), x1)
|
||||
intrinsics.unaligned_store((^u32x4)(dst[2:]), x2)
|
||||
intrinsics.unaligned_store((^u32x4)(dst[3:]), x3)
|
||||
intrinsics.unaligned_store((^u32x4)(dst[4:]), intrinsics.simd_add(x4, s4))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[5:]), intrinsics.simd_add(x5, s5))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[6:]), intrinsics.simd_add(x6, s6))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[7:]), intrinsics.simd_add(x7, s7))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[8:]), intrinsics.simd_add(x8, s8))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[9:]), intrinsics.simd_add(x9, s9))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[10:]), intrinsics.simd_add(x10, s10))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[11:]), intrinsics.simd_add(x11, s11))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[12:]), x12)
|
||||
intrinsics.unaligned_store((^u32x4)(dst[13:]), intrinsics.simd_add(x13, s13))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[14:]), intrinsics.simd_add(x14, s14))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[15:]), intrinsics.simd_add(x15, s15))
|
||||
} else {
|
||||
intrinsics.unaligned_store((^u32x4)(dst[0:]), _byteswap_u32x4(x0))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[1:]), _byteswap_u32x4(x1))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[2:]), _byteswap_u32x4(x2))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[3:]), _byteswap_u32x4(x3))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[4:]), _byteswap_u32x4(intrinsics.simd_add(x4, s4)))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[5:]), _byteswap_u32x4(intrinsics.simd_add(x5, s5)))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[6:]), _byteswap_u32x4(intrinsics.simd_add(x6, s6)))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[7:]), _byteswap_u32x4(intrinsics.simd_add(x7, s7)))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[8:]), _byteswap_u32x4(intrinsics.simd_add(x8, s8)))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[9:]), _byteswap_u32x4(intrinsics.simd_add(x9, s9)))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[10:]), _byteswap_u32x4(intrinsics.simd_add(x10, s10)))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[11:]), _byteswap_u32x4(intrinsics.simd_add(x11, s11)))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[12:]), _byteswap_u32x4(x12))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[13:]), _byteswap_u32x4(intrinsics.simd_add(x13, s13)))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[14:]), _byteswap_u32x4(intrinsics.simd_add(x14, s14)))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[15:]), _byteswap_u32x4(intrinsics.simd_add(x15, s15)))
|
||||
}
|
||||
|
||||
s12 = intrinsics.simd_add(s12, _CTR_INC_4)
|
||||
|
||||
dst = dst[16:]
|
||||
}
|
||||
}
|
||||
}
|
||||
197
base/runtime/random_generator_chacha8_simd256.odin
Normal file
197
base/runtime/random_generator_chacha8_simd256.odin
Normal file
@@ -0,0 +1,197 @@
|
||||
#+build amd64
|
||||
package runtime
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
#assert(ODIN_ENDIAN == .Little)
|
||||
|
||||
@(private = "file")
|
||||
u32x8 :: #simd[8]u32
|
||||
@(private = "file")
|
||||
u32x4 :: #simd[4]u32
|
||||
|
||||
@(private = "file")
|
||||
S0: u32x8 : {
|
||||
CHACHA_SIGMA_0, CHACHA_SIGMA_0, CHACHA_SIGMA_0, CHACHA_SIGMA_0,
|
||||
CHACHA_SIGMA_0, CHACHA_SIGMA_0, CHACHA_SIGMA_0, CHACHA_SIGMA_0,
|
||||
}
|
||||
@(private = "file")
|
||||
S1: u32x8 : {
|
||||
CHACHA_SIGMA_1, CHACHA_SIGMA_1, CHACHA_SIGMA_1, CHACHA_SIGMA_1,
|
||||
CHACHA_SIGMA_1, CHACHA_SIGMA_1, CHACHA_SIGMA_1, CHACHA_SIGMA_1,
|
||||
}
|
||||
@(private = "file")
|
||||
S2: u32x8 : {
|
||||
CHACHA_SIGMA_2, CHACHA_SIGMA_2, CHACHA_SIGMA_2, CHACHA_SIGMA_2,
|
||||
CHACHA_SIGMA_2, CHACHA_SIGMA_2, CHACHA_SIGMA_2, CHACHA_SIGMA_2,
|
||||
}
|
||||
@(private = "file")
|
||||
S3: u32x8 : {
|
||||
CHACHA_SIGMA_3, CHACHA_SIGMA_3, CHACHA_SIGMA_3, CHACHA_SIGMA_3,
|
||||
CHACHA_SIGMA_3, CHACHA_SIGMA_3, CHACHA_SIGMA_3, CHACHA_SIGMA_3,
|
||||
}
|
||||
|
||||
@(private = "file")
|
||||
_ROT_7L: u32x8 : {7, 7, 7, 7, 7, 7, 7, 7}
|
||||
@(private = "file")
|
||||
_ROT_7R: u32x8 : {25, 25, 25, 25, 25, 25, 25, 25}
|
||||
@(private = "file")
|
||||
_ROT_12L: u32x8 : {12, 12, 12, 12, 12, 12, 12, 12}
|
||||
@(private = "file")
|
||||
_ROT_12R: u32x8 : {20, 20, 20, 20, 20, 20, 20, 20}
|
||||
@(private = "file")
|
||||
_ROT_8L: u32x8 : {8, 8, 8, 8, 8, 8, 8, 8}
|
||||
@(private = "file")
|
||||
_ROT_8R: u32x8 : {24, 24, 24, 24, 24, 24, 24, 24}
|
||||
@(private = "file")
|
||||
_ROT_16: u32x8 : {16, 16, 16, 16, 16, 16, 16, 16}
|
||||
@(private = "file")
|
||||
_CTR_INC_8: u32x8 : {8, 8, 8, 8, 8, 8, 8, 8}
|
||||
|
||||
// To the best of my knowledge this is only really useful on
|
||||
// modern x86-64 as most ARM silicon is missing support for SVE2.
|
||||
|
||||
@(private, enable_target_feature = "avx,avx2")
|
||||
chacha8rand_refill_simd256 :: proc(r: ^Default_Random_State) {
|
||||
// Initialize the base state.
|
||||
k: [^]u32 = (^u32)(raw_data(r._buf[RNG_OUTPUT_PER_ITER:]))
|
||||
s4_ := k[0]
|
||||
s5_ := k[1]
|
||||
s6_ := k[2]
|
||||
s7_ := k[3]
|
||||
s8_ := k[4]
|
||||
s9_ := k[5]
|
||||
s10_ := k[6]
|
||||
s11_ := k[7]
|
||||
|
||||
// 8-lane ChaCha8.
|
||||
s4 := u32x8{s4_, s4_, s4_, s4_, s4_, s4_, s4_, s4_}
|
||||
s5 := u32x8{s5_, s5_, s5_, s5_, s5_, s5_, s5_, s5_}
|
||||
s6 := u32x8{s6_, s6_, s6_, s6_, s6_, s6_, s6_, s6_}
|
||||
s7 := u32x8{s7_, s7_, s7_, s7_, s7_, s7_, s7_, s7_}
|
||||
s8 := u32x8{s8_, s8_, s8_, s8_, s8_, s8_, s8_, s8_}
|
||||
s9 := u32x8{s9_, s9_, s9_, s9_, s9_, s9_, s9_, s9_}
|
||||
s10 := u32x8{s10_, s10_, s10_, s10_, s10_, s10_, s10_, s10_}
|
||||
s11 := u32x8{s11_, s11_, s11_, s11_, s11_, s11_, s11_, s11_}
|
||||
s12 := u32x8{0, 1, 2, 3, 4, 5, 6, 7}
|
||||
s13, s14, s15: u32x8
|
||||
|
||||
u32x4 :: #simd[4]u32
|
||||
dst: [^]u32x4 = (^u32x4)(raw_data(r._buf[:]))
|
||||
|
||||
quarter_round := #force_inline proc "contextless" (a, b, c, d: u32x8) -> (u32x8, u32x8, u32x8, u32x8) {
|
||||
a, b, c, d := a, b, c, d
|
||||
|
||||
a = intrinsics.simd_add(a, b)
|
||||
d = intrinsics.simd_bit_xor(d, a)
|
||||
d = intrinsics.simd_bit_xor(intrinsics.simd_shl(d, _ROT_16), intrinsics.simd_shr(d, _ROT_16))
|
||||
|
||||
c = intrinsics.simd_add(c, d)
|
||||
b = intrinsics.simd_bit_xor(b, c)
|
||||
b = intrinsics.simd_bit_xor(intrinsics.simd_shl(b, _ROT_12L), intrinsics.simd_shr(b, _ROT_12R))
|
||||
|
||||
a = intrinsics.simd_add(a, b)
|
||||
d = intrinsics.simd_bit_xor(d, a)
|
||||
d = intrinsics.simd_bit_xor(intrinsics.simd_shl(d, _ROT_8L), intrinsics.simd_shr(d, _ROT_8R))
|
||||
|
||||
c = intrinsics.simd_add(c, d)
|
||||
b = intrinsics.simd_bit_xor(b, c)
|
||||
b = intrinsics.simd_bit_xor(intrinsics.simd_shl(b, _ROT_7L), intrinsics.simd_shr(b, _ROT_7R))
|
||||
|
||||
return a, b, c, d
|
||||
}
|
||||
|
||||
for _ in 0..<2 {
|
||||
x0, x1, x2, x3 := S0, S1, S2, S3
|
||||
x4, x5, x6, x7 := s4, s5, s6, s7
|
||||
x8, x9, x10, x11 := s8, s9, s10, s11
|
||||
x12, x13, x14, x15 := s12, s13, s14, s15
|
||||
|
||||
for i := CHACHA_ROUNDS; i > 0; i = i - 2 {
|
||||
x0, x4, x8, x12 = quarter_round(x0, x4, x8, x12)
|
||||
x1, x5, x9, x13 = quarter_round(x1, x5, x9, x13)
|
||||
x2, x6, x10, x14 = quarter_round(x2, x6, x10, x14)
|
||||
x3, x7, x11, x15 = quarter_round(x3, x7, x11, x15)
|
||||
|
||||
x0, x5, x10, x15 = quarter_round(x0, x5, x10, x15)
|
||||
x1, x6, x11, x12 = quarter_round(x1, x6, x11, x12)
|
||||
x2, x7, x8, x13 = quarter_round(x2, x7, x8, x13)
|
||||
x3, x4, x9, x14 = quarter_round(x3, x4, x9, x14)
|
||||
}
|
||||
|
||||
x4 = intrinsics.simd_add(x4, s4)
|
||||
x5 = intrinsics.simd_add(x5, s5)
|
||||
x6 = intrinsics.simd_add(x6, s6)
|
||||
x7 = intrinsics.simd_add(x7, s7)
|
||||
x8 = intrinsics.simd_add(x8, s8)
|
||||
x9 = intrinsics.simd_add(x9, s9)
|
||||
x10 = intrinsics.simd_add(x10, s10)
|
||||
x11 = intrinsics.simd_add(x11, s11)
|
||||
x13 = intrinsics.simd_add(x13, s13)
|
||||
x14 = intrinsics.simd_add(x14, s14)
|
||||
x15 = intrinsics.simd_add(x15, s15)
|
||||
|
||||
// Ok, now we have x0->x15 with 8 lanes, but we need to
|
||||
// output the first 4 blocks, then the second 4 blocks.
|
||||
//
|
||||
// LLVM appears not to consider "this instruction is totally
|
||||
// awful on the given microarchitcture", which leads to
|
||||
// `VPCOMPRESSED` being generated if and only if (⟺) AVX512 support is
|
||||
// enabled for `intrinsics.simd_masked_compress_store`.
|
||||
// On Zen 4, this leads to a 50% performance regression vs
|
||||
// the 128-bit SIMD code.
|
||||
//
|
||||
// The fake intrinsic (because LLVM doesn't appear to have
|
||||
// an amd64 specific one), doesn't generate `VEXTRACTI128`,
|
||||
// but instead does cleverness without horrible regressions.
|
||||
|
||||
intrinsics.unaligned_store((^u32x4)(dst[0:]), _mm_mm256_extracti128_si256(x0, 0))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[1:]), _mm_mm256_extracti128_si256(x1, 0))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[2:]), _mm_mm256_extracti128_si256(x2, 0))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[3:]), _mm_mm256_extracti128_si256(x3, 0))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[4:]), _mm_mm256_extracti128_si256(x4, 0))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[5:]), _mm_mm256_extracti128_si256(x5, 0))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[6:]), _mm_mm256_extracti128_si256(x6, 0))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[7:]), _mm_mm256_extracti128_si256(x7, 0))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[8:]), _mm_mm256_extracti128_si256(x8, 0))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[9:]), _mm_mm256_extracti128_si256(x9, 0))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[10:]), _mm_mm256_extracti128_si256(x10, 0))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[11:]), _mm_mm256_extracti128_si256(x11, 0))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[12:]), _mm_mm256_extracti128_si256(x12, 0))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[13:]), _mm_mm256_extracti128_si256(x13, 0))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[14:]), _mm_mm256_extracti128_si256(x14, 0))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[15:]), _mm_mm256_extracti128_si256(x15, 0))
|
||||
|
||||
intrinsics.unaligned_store((^u32x4)(dst[16:]), _mm_mm256_extracti128_si256(x0, 1))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[17:]), _mm_mm256_extracti128_si256(x1, 1))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[18:]), _mm_mm256_extracti128_si256(x2, 1))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[19:]), _mm_mm256_extracti128_si256(x3, 1))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[20:]), _mm_mm256_extracti128_si256(x4, 1))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[21:]), _mm_mm256_extracti128_si256(x5, 1))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[22:]), _mm_mm256_extracti128_si256(x6, 1))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[23:]), _mm_mm256_extracti128_si256(x7, 1))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[24:]), _mm_mm256_extracti128_si256(x8, 1))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[25:]), _mm_mm256_extracti128_si256(x9, 1))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[26:]), _mm_mm256_extracti128_si256(x10, 1))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[27:]), _mm_mm256_extracti128_si256(x11, 1))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[28:]), _mm_mm256_extracti128_si256(x12, 1))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[29:]), _mm_mm256_extracti128_si256(x13, 1))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[30:]), _mm_mm256_extracti128_si256(x14, 1))
|
||||
intrinsics.unaligned_store((^u32x4)(dst[31:]), _mm_mm256_extracti128_si256(x15, 1))
|
||||
|
||||
s12 = intrinsics.simd_add(s12, _CTR_INC_8)
|
||||
|
||||
dst = dst[32:]
|
||||
}
|
||||
}
|
||||
|
||||
@(private = "file", require_results, enable_target_feature="avx2")
|
||||
_mm_mm256_extracti128_si256 :: #force_inline proc "c" (a: u32x8, $OFFSET: int) -> u32x4 {
|
||||
when OFFSET == 0 {
|
||||
return intrinsics.simd_shuffle(a, a, 0, 1, 2, 3)
|
||||
} else when OFFSET == 1 {
|
||||
return intrinsics.simd_shuffle(a, a, 4, 5, 6, 7)
|
||||
} else {
|
||||
#panic("chacha8rand: invalid offset")
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,13 @@
|
||||
package runtime
|
||||
|
||||
Thread_Local_Cleaner :: #type proc "odin" ()
|
||||
Thread_Local_Cleaner_Odin :: #type proc "odin" ()
|
||||
Thread_Local_Cleaner_Contextless :: #type proc "contextless" ()
|
||||
|
||||
Thread_Local_Cleaner :: union #shared_nil {Thread_Local_Cleaner_Odin, Thread_Local_Cleaner_Contextless}
|
||||
|
||||
@(private="file")
|
||||
thread_local_cleaners: [8]Thread_Local_Cleaner
|
||||
|
||||
// The thread ID is cached here to save time from making syscalls with every
|
||||
// call.
|
||||
@(thread_local) local_thread_id: int
|
||||
|
||||
// Return the current thread ID.
|
||||
@(require_results)
|
||||
get_current_thread_id :: proc "contextless" () -> int {
|
||||
if local_thread_id == 0 {
|
||||
local_thread_id = _get_current_thread_id()
|
||||
assert_contextless(local_thread_id != 0, "_get_current_thread_id returned 0.")
|
||||
}
|
||||
return local_thread_id
|
||||
}
|
||||
|
||||
// Add a procedure that will be run at the end of a thread for the purpose of
|
||||
// deallocating state marked as `thread_local`.
|
||||
@@ -43,6 +33,9 @@ run_thread_local_cleaners :: proc "odin" () {
|
||||
if p == nil {
|
||||
break
|
||||
}
|
||||
p()
|
||||
switch v in p {
|
||||
case Thread_Local_Cleaner_Odin: v()
|
||||
case Thread_Local_Cleaner_Contextless: v()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#+build !bedrock
|
||||
package runtime
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
@@ -44,7 +44,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
WASM_Allocator :: struct #no_copy {
|
||||
WASM_Allocator :: struct {
|
||||
// The minimum alignment of allocations.
|
||||
alignment: uint,
|
||||
// A region that contains as payload a single forward linked list of pointers to
|
||||
@@ -90,10 +90,12 @@ wasm_allocator_init :: proc(a: ^WASM_Allocator, alignment: uint = 8) {
|
||||
|
||||
global_default_wasm_allocator_data: WASM_Allocator
|
||||
|
||||
@(require_results)
|
||||
default_wasm_allocator :: proc() -> Allocator {
|
||||
return wasm_allocator(&global_default_wasm_allocator_data)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
wasm_allocator :: proc(a: ^WASM_Allocator) -> Allocator {
|
||||
return {
|
||||
data = a,
|
||||
|
||||
74
base/sanitizer/memory.odin
Normal file
74
base/sanitizer/memory.odin
Normal file
@@ -0,0 +1,74 @@
|
||||
#+no-instrumentation
|
||||
package sanitizer
|
||||
|
||||
@(private="file")
|
||||
MSAN_ENABLED :: .Memory in ODIN_SANITIZER_FLAGS
|
||||
|
||||
@(private="file")
|
||||
@(default_calling_convention="system")
|
||||
foreign {
|
||||
__msan_unpoison :: proc(addr: rawptr, size: uint) ---
|
||||
}
|
||||
|
||||
/*
|
||||
Marks a slice as fully initialized.
|
||||
|
||||
Code instrumented with `-sanitize:memory` will be permitted to access any
|
||||
address within the slice as if it had already been initialized.
|
||||
|
||||
When msan is not enabled this procedure does nothing.
|
||||
*/
|
||||
memory_unpoison_slice :: proc "contextless" (region: $T/[]$E) {
|
||||
when MSAN_ENABLED {
|
||||
__msan_unpoison(raw_data(region), size_of(E) * len(region))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Marks a pointer as fully initialized.
|
||||
|
||||
Code instrumented with `-sanitize:memory` will be permitted to access memory
|
||||
within the region the pointer points to as if it had already been initialized.
|
||||
|
||||
When msan is not enabled this procedure does nothing.
|
||||
*/
|
||||
memory_unpoison_ptr :: proc "contextless" (ptr: ^$T) {
|
||||
when MSAN_ENABLED {
|
||||
__msan_unpoison(ptr, size_of(T))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Marks the region covering `[ptr, ptr+len)` as fully initialized.
|
||||
|
||||
Code instrumented with `-sanitize:memory` will be permitted to access memory
|
||||
within this range as if it had already been initialized.
|
||||
|
||||
When msan is not enabled this procedure does nothing.
|
||||
*/
|
||||
memory_unpoison_rawptr :: proc "contextless" (ptr: rawptr, len: int) {
|
||||
when MSAN_ENABLED {
|
||||
__msan_unpoison(ptr, uint(len))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Marks the region covering `[ptr, ptr+len)` as fully initialized.
|
||||
|
||||
Code instrumented with `-sanitize:memory` will be permitted to access memory
|
||||
within this range as if it had already been initialized.
|
||||
|
||||
When msan is not enabled this procedure does nothing.
|
||||
*/
|
||||
memory_unpoison_rawptr_uint :: proc "contextless" (ptr: rawptr, len: uint) {
|
||||
when MSAN_ENABLED {
|
||||
__msan_unpoison(ptr, len)
|
||||
}
|
||||
}
|
||||
|
||||
memory_unpoison :: proc {
|
||||
memory_unpoison_slice,
|
||||
memory_unpoison_ptr,
|
||||
memory_unpoison_rawptr,
|
||||
memory_unpoison_rawptr_uint,
|
||||
}
|
||||
BIN
bin/radlink.exe
BIN
bin/radlink.exe
Binary file not shown.
55
build.bat
55
build.bat
@@ -19,16 +19,27 @@ if "%VSCMD_ARG_TGT_ARCH%" neq "x64" (
|
||||
)
|
||||
)
|
||||
|
||||
where /Q git.exe || 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-%%d" --no-patch --no-notes HEAD') do (
|
||||
set CURR_DATE_TIME=%%i
|
||||
set GIT_SHA=%%j
|
||||
)
|
||||
if %ERRORLEVEL% equ 0 (
|
||||
goto have_git_hash_and_date
|
||||
)
|
||||
:skip_git_hash
|
||||
pushd misc
|
||||
cl /nologo get-date.c
|
||||
popd
|
||||
|
||||
for /f %%i in ('misc\get-date') do (
|
||||
for /f %%i in ('get-date') do (
|
||||
set CURR_DATE_TIME=%%i
|
||||
rem Don't set GIT_SHA
|
||||
)
|
||||
popd
|
||||
:have_git_hash_and_date
|
||||
set curr_year=%CURR_DATE_TIME:~0,4%
|
||||
set curr_month=%CURR_DATE_TIME:~4,2%
|
||||
set curr_day=%CURR_DATE_TIME:~6,2%
|
||||
set curr_month=%CURR_DATE_TIME:~5,2%
|
||||
set curr_day=%CURR_DATE_TIME:~8,2%
|
||||
|
||||
:: Make sure this is a decent name and not generic
|
||||
set exe_name=odin.exe
|
||||
@@ -61,31 +72,14 @@ if %release_mode% equ 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
|
||||
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%\" -DGIT_SHA=\"%GIT_SHA%\"
|
||||
|
||||
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
|
||||
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 GIT_SHA=%%j
|
||||
)
|
||||
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
|
||||
set rc_flags=-nologo "-DGIT_SHA=%GIT_SHA% -DVP=dev-%V1%-%V2%:%GIT_SHA% nologo -DV1=%V1% -DV2=%V2% -DV3=%V3% -DV4=%V4% -DVF=%odin_version_full% -DNIGHTLY=%nightly%"
|
||||
|
||||
if %nightly% equ 1 set compiler_defines=%compiler_defines% -DNIGHTLY
|
||||
|
||||
@@ -100,6 +94,7 @@ if %release_mode% EQU 0 ( rem Debug
|
||||
set compiler_warnings= ^
|
||||
-W4 -WX ^
|
||||
-wd4100 -wd4101 -wd4127 -wd4146 ^
|
||||
-wd4324 ^
|
||||
-wd4505 ^
|
||||
-wd4456 -wd4457
|
||||
|
||||
@@ -112,16 +107,6 @@ set libs= ^
|
||||
set odin_res=misc\odin.res
|
||||
set odin_rc=misc\odin.rc
|
||||
|
||||
rem DO NOT TOUCH!
|
||||
rem THIS TILDE STUFF IS FOR DEVELOPMENT ONLY!
|
||||
set tilde_backend=0
|
||||
if %tilde_backend% EQU 1 (
|
||||
set libs=%libs% src\tilde\tb.lib
|
||||
set compiler_defines=%compiler_defines% -DODIN_TILDE_BACKEND
|
||||
)
|
||||
rem DO NOT TOUCH!
|
||||
|
||||
|
||||
set linker_flags= -incremental:no -opt:ref -subsystem:console -MANIFEST:EMBED
|
||||
|
||||
if %release_mode% EQU 0 ( rem Debug
|
||||
@@ -153,4 +138,4 @@ if %release_mode% EQU 0 echo: & echo Debug compiler built. Note: run "build.bat
|
||||
|
||||
del *.obj > NUL 2> NUL
|
||||
|
||||
:end_of_build
|
||||
:end_of_build
|
||||
@@ -6,7 +6,6 @@ set -eu
|
||||
: ${LDFLAGS=}
|
||||
: ${LLVM_CONFIG=}
|
||||
|
||||
CPPFLAGS="$CPPFLAGS -DODIN_VERSION_RAW=\"dev-$(date +"%Y-%m")\""
|
||||
CXXFLAGS="$CXXFLAGS -std=c++14"
|
||||
DISABLED_WARNINGS="-Wno-switch -Wno-macro-redefined -Wno-unused-value"
|
||||
LDFLAGS="$LDFLAGS -pthread -lm"
|
||||
@@ -15,47 +14,54 @@ OS_NAME="$(uname -s)"
|
||||
|
||||
if [ -d ".git" ] && [ -n "$(command -v git)" ]; then
|
||||
GIT_SHA=$(git show --pretty='%h' --no-patch --no-notes HEAD)
|
||||
GIT_DATE=$(git show "--pretty=%cd" "--date=format:%Y-%m" --no-patch --no-notes HEAD)
|
||||
CPPFLAGS="$CPPFLAGS -DGIT_SHA=\"$GIT_SHA\""
|
||||
else
|
||||
GIT_DATE=$(date +"%Y-%m")
|
||||
fi
|
||||
CPPFLAGS="$CPPFLAGS -DODIN_VERSION_RAW=\"dev-$GIT_DATE\""
|
||||
|
||||
error() {
|
||||
printf "ERROR: %s\n" "$1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
SUPPORTED_LLVM_VERSIONS="22 21 20 19 18 17 14"
|
||||
|
||||
# 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@20)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@20)/bin/llvm-config"
|
||||
elif [ -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
|
||||
for V in $SUPPORTED_LLVM_VERSIONS; do
|
||||
if [ -n "$(command -v $(brew --prefix llvm@$V)/bin/llvm-config)" ]; then
|
||||
LLVM_CONFIG="$(brew --prefix llvm@$V)/bin/llvm-config"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [ -z "$LLVM_CONFIG" ]; then
|
||||
# darwin, linux, openbsd
|
||||
if [ -n "$(command -v llvm-config-20)" ]; then LLVM_CONFIG="llvm-config-20"
|
||||
elif [ -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-12)" ]; then LLVM_CONFIG="llvm-config-12"
|
||||
elif [ -n "$(command -v llvm-config-11)" ]; then LLVM_CONFIG="llvm-config-11"
|
||||
# freebsd
|
||||
elif [ -n "$(command -v llvm-config20)" ]; then LLVM_CONFIG="llvm-config20"
|
||||
elif [ -n "$(command -v llvm-config19)" ]; then LLVM_CONFIG="llvm-config19"
|
||||
elif [ -n "$(command -v llvm-config18)" ]; then LLVM_CONFIG="llvm-config18"
|
||||
elif [ -n "$(command -v llvm-config17)" ]; then LLVM_CONFIG="llvm-config17"
|
||||
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
|
||||
elif [ -n "$(command -v llvm-config)" ]; then LLVM_CONFIG="llvm-config"
|
||||
else
|
||||
error "No llvm-config command found. Set LLVM_CONFIG to proceed."
|
||||
DEFAULT_VERSION=""
|
||||
|
||||
if [ -n "$(command -v llvm-config)" ]; then
|
||||
DEFAULT_VERSION=$(llvm-config --version | awk -F. '{print $1}')
|
||||
fi
|
||||
|
||||
for V in $SUPPORTED_LLVM_VERSIONS; do
|
||||
if [ "$DEFAULT_VERSION" = "$V" ]; then
|
||||
LLVM_CONFIG="llvm-config"
|
||||
break
|
||||
# darwin, linux, openbsd
|
||||
elif [ -n "$(command -v "llvm-config-$V")" ]; then
|
||||
LLVM_CONFIG="llvm-config-$V"
|
||||
break
|
||||
# freebsd
|
||||
elif [ -n "$(command -v "llvm-config$V")" ]; then
|
||||
LLVM_CONFIG="llvm-config$V"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$LLVM_CONFIG" ]; then
|
||||
error "No supported llvm-config command found. Set LLVM_CONFIG to proceed."
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -72,18 +78,12 @@ LLVM_VERSION_MAJOR="$(echo $LLVM_VERSION | awk -F. '{print $1}')"
|
||||
LLVM_VERSION_MINOR="$(echo $LLVM_VERSION | awk -F. '{print $2}')"
|
||||
LLVM_VERSION_PATCH="$(echo $LLVM_VERSION | awk -F. '{print $3}')"
|
||||
|
||||
if [ $LLVM_VERSION_MAJOR -lt 11 ] || ([ $LLVM_VERSION_MAJOR -gt 14 ] && [ $LLVM_VERSION_MAJOR -lt 17 ]) || [ $LLVM_VERSION_MAJOR -gt 20 ]; then
|
||||
error "Invalid LLVM version $LLVM_VERSION: must be 11, 12, 13, 14, 17, 18, 19 or 20"
|
||||
if [ $LLVM_VERSION_MAJOR -lt 14 ] || ([ $LLVM_VERSION_MAJOR -gt 14 ] && [ $LLVM_VERSION_MAJOR -lt 17 ]) || [ $LLVM_VERSION_MAJOR -gt 22 ]; then
|
||||
error "Invalid LLVM version $LLVM_VERSION: must be 14, 17, 18, 19, 20, 21 or 22"
|
||||
fi
|
||||
|
||||
case "$OS_NAME" in
|
||||
Darwin)
|
||||
if [ "$OS_ARCH" = "arm64" ]; then
|
||||
if [ $LLVM_VERSION_MAJOR -lt 13 ]; then
|
||||
error "Invalid LLVM version $LLVM_VERSION: Darwin Arm64 requires LLVM 13, 14, 17, 18, 19 or 20"
|
||||
fi
|
||||
fi
|
||||
|
||||
darwin_sysroot=
|
||||
if [ $(which xcrun) ]; then
|
||||
darwin_sysroot="--sysroot $(xcrun --sdk macosx --show-sdk-path)"
|
||||
@@ -114,12 +114,7 @@ Linux)
|
||||
;;
|
||||
OpenBSD)
|
||||
CXXFLAGS="$CXXFLAGS -I/usr/local/include $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
LDFLAGS="$LDFLAGS -lstdc++ -L/usr/local/lib -liconv"
|
||||
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
||||
;;
|
||||
Haiku)
|
||||
CXXFLAGS="$CXXFLAGS -D_GNU_SOURCE $($LLVM_CONFIG --cxxflags --ldflags) -I/system/develop/headers/private/shared -I/system/develop/headers/private/kernel"
|
||||
LDFLAGS="$LDFLAGS -lstdc++ -liconv"
|
||||
LDFLAGS="$LDFLAGS -lstdc++ -L/usr/local/lib -Wl,-rpath,$($LLVM_CONFIG --libdir) -liconv"
|
||||
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
||||
;;
|
||||
*)
|
||||
|
||||
71
check_all.bat
Normal file
71
check_all.bat
Normal file
@@ -0,0 +1,71 @@
|
||||
@echo off
|
||||
|
||||
if "%1" == "" (
|
||||
echo Checking darwin_amd64 - expect vendor:cgltf panic
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:darwin_amd64
|
||||
echo Checking darwin_arm64 - expect vendor:cgltf panic
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:darwin_arm64
|
||||
echo Checking linux_i386
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:linux_i386
|
||||
echo Checking linux_amd64
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:linux_amd64
|
||||
echo Checking linux_arm64
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:linux_arm64
|
||||
echo Checking linux_arm32
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:linux_arm32
|
||||
echo Checking linux_riscv64
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:linux_riscv64
|
||||
echo Checking windows_i386
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:windows_i386
|
||||
echo Checking windows_amd64
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:windows_amd64
|
||||
echo Checking freebsd_amd64
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freebsd_amd64
|
||||
echo Checking freebsd_arm64
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freebsd_arm64
|
||||
echo Checking netbsd_amd64
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:netbsd_amd64
|
||||
echo Checking netbsd_arm64
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:netbsd_arm64
|
||||
echo Checking openbsd_amd64
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:openbsd_amd64
|
||||
)
|
||||
|
||||
if "%1" == "freestanding" (
|
||||
echo Checking freestanding_wasm32
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freestanding_wasm32
|
||||
echo Checking freestanding_wasm64p32
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freestanding_wasm64p32
|
||||
echo Checking freestanding_amd64_sysv
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freestanding_amd64_sysv
|
||||
echo Checking freestanding_amd64_win64
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freestanding_amd64_win64
|
||||
echo Checking freestanding_arm64
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freestanding_arm64
|
||||
echo Checking freestanding_arm32
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freestanding_arm32
|
||||
echo Checking freestanding_riscv64
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freestanding_riscv64
|
||||
)
|
||||
|
||||
if "%1" == "rare" (
|
||||
echo Checking freebsd_i386
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freebsd_i386
|
||||
)
|
||||
|
||||
if "%1" == "wasm" (
|
||||
echo Checking freestanding_wasm32
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freestanding_wasm32
|
||||
echo Checking freestanding_wasm64p32
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freestanding_wasm64p32
|
||||
echo Checking wasi_wasm64p32
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:wasi_wasm64p32
|
||||
echo Checking wasi_wasm32
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:wasi_wasm32
|
||||
echo Checking js_wasm32
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:js_wasm32
|
||||
echo Checking orca_wasm32
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:orca_wasm32
|
||||
echo Checking js_wasm64p32
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:js_wasm64p32
|
||||
)
|
||||
74
check_all.sh
Executable file
74
check_all.sh
Executable file
@@ -0,0 +1,74 @@
|
||||
#!/bin/sh
|
||||
|
||||
case $1 in
|
||||
freestanding)
|
||||
echo Checking freestanding_wasm32
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freestanding_wasm32
|
||||
echo Checking freestanding_wasm64p32
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freestanding_wasm64p32
|
||||
echo Checking freestanding_amd64_sysv
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freestanding_amd64_sysv
|
||||
echo Checking freestanding_amd64_win64
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freestanding_amd64_win64
|
||||
echo Checking freestanding_arm64
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freestanding_arm64
|
||||
echo Checking freestanding_arm32
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freestanding_arm32
|
||||
echo Checking freestanding_riscv64
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freestanding_riscv64
|
||||
;;
|
||||
|
||||
rare)
|
||||
echo Checking freebsd_i386
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freebsd_i386
|
||||
;;
|
||||
|
||||
wasm)
|
||||
echo Checking freestanding_wasm32
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freestanding_wasm32
|
||||
echo Checking freestanding_wasm64p32
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freestanding_wasm64p32
|
||||
echo Checking wasi_wasm64p32
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:wasi_wasm64p32
|
||||
echo Checking wasi_wasm32
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:wasi_wasm32
|
||||
echo Checking js_wasm32
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:js_wasm32
|
||||
echo Checking orca_wasm32
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:orca_wasm32
|
||||
echo Checking js_wasm64p32
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:js_wasm64p32
|
||||
;;
|
||||
|
||||
*)
|
||||
echo Checking darwin_amd64 - expect vendor:cgltf panic
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:darwin_amd64
|
||||
echo Checking darwin_arm64 - expect vendor:cgltf panic
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:darwin_arm64
|
||||
echo Checking linux_i386
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:linux_i386
|
||||
echo Checking linux_amd64
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:linux_amd64
|
||||
echo Checking linux_arm64
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:linux_arm64
|
||||
echo Checking linux_arm32
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:linux_arm32
|
||||
echo Checking linux_riscv64
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:linux_riscv64
|
||||
echo Checking windows_i386
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:windows_i386
|
||||
echo Checking windows_amd64
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:windows_amd64
|
||||
echo Checking freebsd_amd64
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freebsd_amd64
|
||||
echo Checking freebsd_arm64
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freebsd_arm64
|
||||
echo Checking netbsd_amd64
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:netbsd_amd64
|
||||
echo Checking netbsd_arm64
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:netbsd_arm64
|
||||
echo Checking openbsd_amd64
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:openbsd_amd64
|
||||
;;
|
||||
|
||||
esac
|
||||
@@ -6,7 +6,14 @@ LLVM_CONFIG="llvm-config-20"
|
||||
|
||||
DISABLED_WARNINGS="-Wno-switch -Wno-macro-redefined -Wno-unused-value"
|
||||
|
||||
CPPFLAGS="-DODIN_VERSION_RAW=\"dev-$(date +"%Y-%m")\""
|
||||
if [ -d ".git" ] && [ -n "$(command -v git)" ]; then
|
||||
GIT_SHA=$(git show --pretty='%h' --no-patch --no-notes HEAD)
|
||||
GIT_DATE=$(git show "--pretty=%cd" "--date=format:%Y-%m" --no-patch --no-notes HEAD)
|
||||
CPPFLAGS="$CPPFLAGS -DGIT_SHA=\"$GIT_SHA\""
|
||||
else
|
||||
GIT_DATE=$(date +"%Y-%m")
|
||||
fi
|
||||
CPPFLAGS="$CPPFLAGS -DODIN_VERSION_RAW=\"dev-$GIT_DATE\""
|
||||
CXXFLAGS="-std=c++14 $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
|
||||
LDFLAGS="-static -lm -lzstd -lz -lffi -pthread -ldl -fuse-ld=mold"
|
||||
|
||||
8
ci/remove_windows_binaries.sh
Executable file
8
ci/remove_windows_binaries.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
find "$1" -type f \(\
|
||||
-iname "*.exe" \
|
||||
-o -iname "*.dll" \
|
||||
-o -iname "*.lib" \
|
||||
-o -iname "*.pdb" \
|
||||
\) -delete
|
||||
6
codecov.yml
Normal file
6
codecov.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
comment: false
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
threshold: 1%
|
||||
2
core/bufio/doc.odin
Normal file
2
core/bufio/doc.odin
Normal file
@@ -0,0 +1,2 @@
|
||||
// Wraps an `io.Stream` interface to provide buffered I/O.
|
||||
package bufio
|
||||
@@ -38,5 +38,5 @@ _read_writer_procedure := proc(stream_data: rawptr, mode: io.Stream_Mode, p: []b
|
||||
case .Query:
|
||||
return io.query_utility({.Flush, .Read, .Write, .Query})
|
||||
}
|
||||
return 0, .Empty
|
||||
return 0, .Unsupported
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
package bufio
|
||||
|
||||
import "base:runtime"
|
||||
import "core:io"
|
||||
import "core:mem"
|
||||
import "core:unicode/utf8"
|
||||
import "core:bytes"
|
||||
|
||||
// Reader is a buffered wrapper for an io.Reader
|
||||
Reader :: struct {
|
||||
buf: []byte,
|
||||
buf_allocator: mem.Allocator,
|
||||
buf_allocator: runtime.Allocator,
|
||||
|
||||
rd: io.Reader, // reader
|
||||
r, w: int, // read and write positions for buf
|
||||
@@ -29,6 +29,7 @@ MIN_READ_BUFFER_SIZE :: 16
|
||||
@(private)
|
||||
DEFAULT_MAX_CONSECUTIVE_EMPTY_READS :: 128
|
||||
|
||||
// reader_init initializes using an `allocator`
|
||||
reader_init :: proc(b: ^Reader, rd: io.Reader, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator, loc := #caller_location) {
|
||||
size := size
|
||||
size = max(size, MIN_READ_BUFFER_SIZE)
|
||||
@@ -37,22 +38,25 @@ reader_init :: proc(b: ^Reader, rd: io.Reader, size: int = DEFAULT_BUF_SIZE, all
|
||||
b.buf = make([]byte, size, allocator, loc)
|
||||
}
|
||||
|
||||
// reader_init initializes using a user provided bytes buffer `buf`
|
||||
reader_init_with_buf :: proc(b: ^Reader, rd: io.Reader, buf: []byte) {
|
||||
reader_reset(b, rd)
|
||||
b.buf_allocator = {}
|
||||
b.buf = buf
|
||||
}
|
||||
|
||||
// reader_destroy destroys the underlying buffer with its associated allocator IFF that allocator has been set
|
||||
// reader_destroy destroys the underlying buffer with its associated allocator if and only if (⟺) that allocator has been set
|
||||
reader_destroy :: proc(b: ^Reader) {
|
||||
delete(b.buf, b.buf_allocator)
|
||||
b^ = {}
|
||||
}
|
||||
|
||||
// reader_size returns the number of bytes in the backing buffer
|
||||
reader_size :: proc(b: ^Reader) -> int {
|
||||
return len(b.buf)
|
||||
}
|
||||
|
||||
// reader_reset resets the read and write positions, and the error values
|
||||
reader_reset :: proc(b: ^Reader, r: io.Reader) {
|
||||
b.rd = r
|
||||
b.r, b.w = 0, 0
|
||||
@@ -343,7 +347,7 @@ _reader_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offse
|
||||
case .Query:
|
||||
return io.query_utility({.Read, .Destroy, .Query})
|
||||
}
|
||||
return 0, .Empty
|
||||
return 0, .Unsupported
|
||||
}
|
||||
|
||||
//
|
||||
@@ -351,16 +355,15 @@ _reader_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offse
|
||||
//
|
||||
|
||||
|
||||
// reader_read_slice reads until the first occurrence of delim from the reader
|
||||
// It returns a slice pointing at the bytes in the buffer
|
||||
// The bytes stop being valid at the next read
|
||||
// If reader_read_slice encounters an error before finding a delimiter
|
||||
// reader_read_slice fails with error .Buffer_Full if the buffer fills without a delim
|
||||
// Because the data returned from reader_read_slice will be overwritten on the
|
||||
// next IO operation, reader_read_bytes or reader_read_string is usually preferred
|
||||
//
|
||||
// reader_read_slice returns err != nil if and only if line does not end in delim
|
||||
// reader_read_slice reads until the first occurrence of delim in the input,
|
||||
// returning a slice pointing at the bytes in the internal buffer.
|
||||
// The returned slice is only valid until the next read call.
|
||||
// If the buffer fills without finding delim, it returns .Buffer_Full.
|
||||
// If the underlying reader returns an error before finding delim, that error is returned.
|
||||
// Because the returned data will be overwritten by the next I/O operation,
|
||||
// reader_read_bytes or reader_read_string is usually preferred.
|
||||
//
|
||||
// reader_read_slice returns err != nil if and only if line does not end in delim.
|
||||
reader_read_slice :: proc(b: ^Reader, delim: byte) -> (line: []byte, err: io.Error) {
|
||||
s := 0
|
||||
for {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package bufio
|
||||
|
||||
import "core:bytes"
|
||||
import "core:io"
|
||||
import "core:mem"
|
||||
import "core:unicode/utf8"
|
||||
import "base:runtime"
|
||||
import "base:intrinsics"
|
||||
import "core:io"
|
||||
import "core:bytes"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
// Extra errors returns by scanning procedures
|
||||
Scanner_Extra_Error :: enum i32 {
|
||||
@@ -46,6 +46,7 @@ DEFAULT_MAX_SCAN_TOKEN_SIZE :: 1<<16
|
||||
@(private)
|
||||
_INIT_BUF_SIZE :: 4096
|
||||
|
||||
// Initializes a Scanner buffer an allocator `buf_allocator`
|
||||
scanner_init :: proc(s: ^Scanner, r: io.Reader, buf_allocator := context.allocator) -> ^Scanner {
|
||||
s.r = r
|
||||
s.split = scan_lines
|
||||
@@ -53,11 +54,21 @@ scanner_init :: proc(s: ^Scanner, r: io.Reader, buf_allocator := context.allocat
|
||||
s.buf.allocator = buf_allocator
|
||||
return s
|
||||
}
|
||||
|
||||
// Initializes a Scanner buffer a user provided bytes buffer `buf`
|
||||
scanner_init_with_buffer :: proc(s: ^Scanner, r: io.Reader, buf: []byte) -> ^Scanner {
|
||||
s.r = r
|
||||
s.split = scan_lines
|
||||
s.max_token_size = DEFAULT_MAX_SCAN_TOKEN_SIZE
|
||||
s.buf = mem.buffer_from_slice(buf)
|
||||
s.buf = transmute([dynamic]byte)runtime.Raw_Dynamic_Array{
|
||||
data = raw_data(buf),
|
||||
len = 0,
|
||||
cap = len(buf),
|
||||
allocator = runtime.Allocator{
|
||||
procedure = runtime.nil_allocator_proc,
|
||||
data = nil,
|
||||
},
|
||||
}
|
||||
resize(&s.buf, cap(s.buf))
|
||||
return s
|
||||
}
|
||||
@@ -75,24 +86,27 @@ scanner_error :: proc(s: ^Scanner) -> Scanner_Error {
|
||||
return s._err
|
||||
}
|
||||
|
||||
// Returns the most recent token created by scanner_scan.
|
||||
// Returns the most recent token created by 'scan'.
|
||||
// The underlying array may point to data that may be overwritten
|
||||
// by another call to scanner_scan.
|
||||
// by another call to 'scan'.
|
||||
// Treat the returned value as if it is immutable.
|
||||
scanner_bytes :: proc(s: ^Scanner) -> []byte {
|
||||
return s.token
|
||||
}
|
||||
|
||||
// Returns the most recent token created by scanner_scan.
|
||||
// Returns the most recent token created by 'scan'.
|
||||
// The underlying array may point to data that may be overwritten
|
||||
// by another call to scanner_scan.
|
||||
// by another call to 'scan'.
|
||||
// Treat the returned value as if it is immutable.
|
||||
scanner_text :: proc(s: ^Scanner) -> string {
|
||||
return string(s.token)
|
||||
}
|
||||
|
||||
// scanner_scan advances the scanner
|
||||
scanner_scan :: proc(s: ^Scanner) -> bool {
|
||||
// scanner_scan is an alias of scan
|
||||
scanner_scan :: scan
|
||||
|
||||
// scan advances the Scanner
|
||||
scan :: proc(s: ^Scanner) -> bool {
|
||||
set_err :: proc(s: ^Scanner, err: Scanner_Error) {
|
||||
switch s._err {
|
||||
case nil, .EOF:
|
||||
@@ -229,6 +243,7 @@ scanner_scan :: proc(s: ^Scanner) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
// scan_bytes is a splitting procedure that returns each byte as a token
|
||||
scan_bytes :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool) {
|
||||
if at_eof && len(data) == 0 {
|
||||
return
|
||||
@@ -236,6 +251,10 @@ scan_bytes :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte,
|
||||
return 1, data[0:1], nil, false
|
||||
}
|
||||
|
||||
// scan_runes is a splitting procedure that returns each UTF-8 encoded rune as a token.
|
||||
// The lsit of runes return is equivalent to that of iterating over a string in a 'for in' loop, meaning any
|
||||
// erroneous UTF-8 encodings will be returned as U+FFFD. Unfortunately this means it is impossible for the "client"
|
||||
// to know whether a U+FFFD is an expected replacement rune or an encoding of an error.
|
||||
scan_runes :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool) {
|
||||
if at_eof && len(data) == 0 {
|
||||
return
|
||||
@@ -264,7 +283,8 @@ scan_runes :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte,
|
||||
token = ERROR_RUNE
|
||||
return
|
||||
}
|
||||
|
||||
// scan_words is a splitting procedure that returns each Unicode-space-separated word of text, excluding the surrounded spaces.
|
||||
// It will never return return an empty string.
|
||||
scan_words :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool) {
|
||||
is_space :: proc "contextless" (r: rune) -> bool {
|
||||
switch r {
|
||||
@@ -312,6 +332,8 @@ scan_words :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte,
|
||||
return
|
||||
}
|
||||
|
||||
// scan_lines is a splitting procedure that returns each line of text stripping of any trailing newline and an optional preceding carriage return (\r?\n).
|
||||
// A new line is allowed to be empty.
|
||||
scan_lines :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool) {
|
||||
trim_carriage_return :: proc "contextless" (data: []byte) -> []byte {
|
||||
if len(data) > 0 && data[len(data)-1] == '\r' {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package bufio
|
||||
|
||||
import "base:runtime"
|
||||
import "core:io"
|
||||
import "core:mem"
|
||||
import "core:unicode/utf8"
|
||||
// import "core:bytes"
|
||||
|
||||
// Writer is a buffered wrapper for an io.Writer
|
||||
Writer :: struct {
|
||||
buf: []byte,
|
||||
buf_allocator: mem.Allocator,
|
||||
buf_allocator: runtime.Allocator,
|
||||
|
||||
wr: io.Writer,
|
||||
n: int,
|
||||
@@ -19,6 +19,7 @@ Writer :: struct {
|
||||
|
||||
}
|
||||
|
||||
// Initialized a Writer with an `allocator`
|
||||
writer_init :: proc(b: ^Writer, wr: io.Writer, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator) {
|
||||
size := size
|
||||
size = max(size, MIN_READ_BUFFER_SIZE)
|
||||
@@ -27,13 +28,14 @@ writer_init :: proc(b: ^Writer, wr: io.Writer, size: int = DEFAULT_BUF_SIZE, all
|
||||
b.buf = make([]byte, size, allocator)
|
||||
}
|
||||
|
||||
// Initialized a Writer with a user provided buffer `buf`
|
||||
writer_init_with_buf :: proc(b: ^Writer, wr: io.Writer, buf: []byte) {
|
||||
writer_reset(b, wr)
|
||||
b.buf_allocator = {}
|
||||
b.buf = buf
|
||||
}
|
||||
|
||||
// writer_destroy destroys the underlying buffer with its associated allocator IFF that allocator has been set
|
||||
// writer_destroy destroys the underlying buffer with its associated allocator if and only if (⟺) that allocator has been set
|
||||
writer_destroy :: proc(b: ^Writer) {
|
||||
delete(b.buf, b.buf_allocator)
|
||||
b^ = {}
|
||||
@@ -247,5 +249,5 @@ _writer_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offse
|
||||
case .Query:
|
||||
return io.query_utility({.Flush, .Write, .Destroy, .Query})
|
||||
}
|
||||
return 0, .Empty
|
||||
return 0, .Unsupported
|
||||
}
|
||||
|
||||
@@ -176,6 +176,11 @@ buffer_write_ptr :: proc(b: ^Buffer, ptr: rawptr, size: int, loc := #caller_loca
|
||||
return buffer_write(b, ([^]byte)(ptr)[:size], loc=loc)
|
||||
}
|
||||
|
||||
buffer_write_slice :: proc(b: ^Buffer, slice: $S/[]$T, loc := #caller_location) -> (n: int, err: io.Error) {
|
||||
size := len(slice)*size_of(T)
|
||||
return buffer_write(b, ([^]byte)(raw_data(slice))[:size], loc=loc)
|
||||
}
|
||||
|
||||
buffer_write_string :: proc(b: ^Buffer, s: string, loc := #caller_location) -> (n: int, err: io.Error) {
|
||||
b.last_read = .Invalid
|
||||
m, ok := _buffer_try_grow(b, len(s), loc=loc)
|
||||
@@ -248,6 +253,12 @@ buffer_read_ptr :: proc(b: ^Buffer, ptr: rawptr, size: int) -> (n: int, err: io.
|
||||
return buffer_read(b, ([^]byte)(ptr)[:size])
|
||||
}
|
||||
|
||||
buffer_read_slice :: proc(b: ^Buffer, slice: $S/[]$T) -> (n: int, err: io.Error) {
|
||||
size := len(slice)*size_of(T)
|
||||
return buffer_read(b, ([^]byte)(raw_data(slice))[:size])
|
||||
}
|
||||
|
||||
|
||||
buffer_read_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) {
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
@@ -434,5 +445,5 @@ _buffer_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offse
|
||||
case .Query:
|
||||
return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Destroy, .Query})
|
||||
}
|
||||
return 0, .Empty
|
||||
return 0, .Unsupported
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// Procedures for manipulation of `[]byte` slices.
|
||||
package bytes
|
||||
|
||||
import "base:runtime"
|
||||
import "base:intrinsics"
|
||||
import "core:mem"
|
||||
import "core:simd"
|
||||
import "core:unicode"
|
||||
import "core:unicode/utf8"
|
||||
@@ -34,20 +35,20 @@ SCANNER_SENTINEL_MIN_128: simd.u8x16 : u8(0xff)
|
||||
SIMD_REG_SIZE_128 :: 16
|
||||
|
||||
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=loc)
|
||||
copy(c, s)
|
||||
return c[:len(s)]
|
||||
}
|
||||
|
||||
clone_safe :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: mem.Allocator_Error) {
|
||||
c := make([]byte, len(s), allocator, loc) or_return
|
||||
clone_safe :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: runtime.Allocator_Error) {
|
||||
c := make([]byte, len(s), allocator, loc=loc) or_return
|
||||
copy(c, s)
|
||||
return c[:len(s)], nil
|
||||
}
|
||||
|
||||
ptr_from_slice :: ptr_from_bytes
|
||||
ptr_from_bytes :: proc(str: []byte) -> ^byte {
|
||||
d := transmute(mem.Raw_String)str
|
||||
d := transmute(runtime.Raw_String)str
|
||||
return d.data
|
||||
}
|
||||
|
||||
@@ -66,10 +67,16 @@ truncate_to_rune :: proc(str: []byte, r: rune) -> []byte {
|
||||
return str[:n]
|
||||
}
|
||||
|
||||
// Compares two strings, returning a value representing which one comes first lexiographically.
|
||||
// -1 for `a`; 1 for `b`, or 0 if they are equal.
|
||||
// Compares two []byte, returning a value representing which one comes first lexiographically.
|
||||
// Returns: -1 for `lhs`, 1 for `rhs`, or 0 if they are equal.
|
||||
compare :: proc(lhs, rhs: []byte) -> int {
|
||||
return mem.compare(lhs, rhs)
|
||||
res := runtime.memory_compare(raw_data(lhs), raw_data(rhs), min(len(lhs), len(rhs)))
|
||||
if res == 0 && len(lhs) != len(rhs) {
|
||||
return len(lhs) <= len(rhs) ? -1 : +1
|
||||
} else if len(lhs) == 0 && len(rhs) == 0 {
|
||||
return 0
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
contains_rune :: proc(s: []byte, r: rune) -> int {
|
||||
@@ -134,8 +141,13 @@ equal_fold :: proc(u, v: []byte) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO(bill): Unicode folding
|
||||
|
||||
r := unicode.simple_fold(sr)
|
||||
for r != sr && r < tr {
|
||||
r = unicode.simple_fold(sr)
|
||||
}
|
||||
if r == tr {
|
||||
continue loop
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -151,7 +163,7 @@ has_suffix :: proc(s, suffix: []byte) -> bool {
|
||||
}
|
||||
|
||||
|
||||
join :: proc(a: [][]byte, sep: []byte, allocator := context.allocator) -> []byte {
|
||||
join :: proc(a: [][]byte, sep: []byte, allocator := context.allocator, loc := #caller_location) -> []byte {
|
||||
if len(a) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -161,7 +173,7 @@ join :: proc(a: [][]byte, sep: []byte, allocator := context.allocator) -> []byte
|
||||
n += len(s)
|
||||
}
|
||||
|
||||
b := make([]byte, n, allocator)
|
||||
b := make([]byte, n, allocator, loc=loc)
|
||||
i := copy(b, a[0])
|
||||
for s in a[1:] {
|
||||
i += copy(b[i:], sep)
|
||||
@@ -170,7 +182,7 @@ join :: proc(a: [][]byte, sep: []byte, allocator := context.allocator) -> []byte
|
||||
return b
|
||||
}
|
||||
|
||||
join_safe :: proc(a: [][]byte, sep: []byte, allocator := context.allocator) -> (data: []byte, err: mem.Allocator_Error) {
|
||||
join_safe :: proc(a: [][]byte, sep: []byte, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: runtime.Allocator_Error) {
|
||||
if len(a) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -180,7 +192,7 @@ join_safe :: proc(a: [][]byte, sep: []byte, allocator := context.allocator) -> (
|
||||
n += len(s)
|
||||
}
|
||||
|
||||
b := make([]byte, n, allocator) or_return
|
||||
b := make([]byte, n, allocator, loc=loc) or_return
|
||||
i := copy(b, a[0])
|
||||
for s in a[1:] {
|
||||
i += copy(b[i:], sep)
|
||||
@@ -189,7 +201,7 @@ join_safe :: proc(a: [][]byte, sep: []byte, allocator := context.allocator) -> (
|
||||
return b, nil
|
||||
}
|
||||
|
||||
concatenate :: proc(a: [][]byte, allocator := context.allocator) -> []byte {
|
||||
concatenate :: proc(a: [][]byte, allocator := context.allocator, loc := #caller_location) -> []byte {
|
||||
if len(a) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -198,7 +210,7 @@ concatenate :: proc(a: [][]byte, allocator := context.allocator) -> []byte {
|
||||
for s in a {
|
||||
n += len(s)
|
||||
}
|
||||
b := make([]byte, n, allocator)
|
||||
b := make([]byte, n, allocator, loc=loc)
|
||||
i := 0
|
||||
for s in a {
|
||||
i += copy(b[i:], s)
|
||||
@@ -206,7 +218,7 @@ concatenate :: proc(a: [][]byte, allocator := context.allocator) -> []byte {
|
||||
return b
|
||||
}
|
||||
|
||||
concatenate_safe :: proc(a: [][]byte, allocator := context.allocator) -> (data: []byte, err: mem.Allocator_Error) {
|
||||
concatenate_safe :: proc(a: [][]byte, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: runtime.Allocator_Error) {
|
||||
if len(a) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -215,7 +227,7 @@ concatenate_safe :: proc(a: [][]byte, allocator := context.allocator) -> (data:
|
||||
for s in a {
|
||||
n += len(s)
|
||||
}
|
||||
b := make([]byte, n, allocator) or_return
|
||||
b := make([]byte, n, allocator, loc=loc) or_return
|
||||
i := 0
|
||||
for s in a {
|
||||
i += copy(b[i:], s)
|
||||
@@ -225,7 +237,7 @@ concatenate_safe :: proc(a: [][]byte, allocator := context.allocator) -> (data:
|
||||
|
||||
|
||||
@private
|
||||
_split :: proc(s, sep: []byte, sep_save, n: int, allocator := context.allocator) -> [][]byte {
|
||||
_split :: proc(s, sep: []byte, sep_save, n: int, allocator := context.allocator, loc := #caller_location) -> [][]byte {
|
||||
s, n := s, n
|
||||
|
||||
if n == 0 {
|
||||
@@ -238,7 +250,7 @@ _split :: proc(s, sep: []byte, sep_save, n: int, allocator := context.allocator)
|
||||
n = l
|
||||
}
|
||||
|
||||
res := make([dynamic][]byte, n, allocator)
|
||||
res := make([dynamic][]byte, n, allocator, loc=loc)
|
||||
for i := 0; i < n-1; i += 1 {
|
||||
_, w := utf8.decode_rune(s)
|
||||
res[i] = s[:w]
|
||||
@@ -254,7 +266,7 @@ _split :: proc(s, sep: []byte, sep_save, n: int, allocator := context.allocator)
|
||||
n = count(s, sep) + 1
|
||||
}
|
||||
|
||||
res := make([dynamic][]byte, n, allocator)
|
||||
res := make([dynamic][]byte, n, allocator, loc=loc)
|
||||
|
||||
n -= 1
|
||||
|
||||
@@ -272,20 +284,20 @@ _split :: proc(s, sep: []byte, sep_save, n: int, allocator := context.allocator)
|
||||
return res[:i+1]
|
||||
}
|
||||
|
||||
split :: proc(s, sep: []byte, allocator := context.allocator) -> [][]byte {
|
||||
return _split(s, sep, 0, -1, allocator)
|
||||
split :: proc(s, sep: []byte, allocator := context.allocator, loc := #caller_location) -> [][]byte {
|
||||
return _split(s, sep, 0, -1, allocator, loc)
|
||||
}
|
||||
|
||||
split_n :: proc(s, sep: []byte, n: int, allocator := context.allocator) -> [][]byte {
|
||||
return _split(s, sep, 0, n, allocator)
|
||||
split_n :: proc(s, sep: []byte, n: int, allocator := context.allocator, loc := #caller_location) -> [][]byte {
|
||||
return _split(s, sep, 0, n, allocator, loc)
|
||||
}
|
||||
|
||||
split_after :: proc(s, sep: []byte, allocator := context.allocator) -> [][]byte {
|
||||
return _split(s, sep, len(sep), -1, allocator)
|
||||
split_after :: proc(s, sep: []byte, allocator := context.allocator, loc := #caller_location) -> [][]byte {
|
||||
return _split(s, sep, len(sep), -1, allocator, loc)
|
||||
}
|
||||
|
||||
split_after_n :: proc(s, sep: []byte, n: int, allocator := context.allocator) -> [][]byte {
|
||||
return _split(s, sep, len(sep), n, allocator)
|
||||
split_after_n :: proc(s, sep: []byte, n: int, allocator := context.allocator, loc := #caller_location) -> [][]byte {
|
||||
return _split(s, sep, len(sep), n, allocator, loc)
|
||||
}
|
||||
|
||||
|
||||
@@ -350,7 +362,7 @@ index_byte :: proc "contextless" (s: []byte, c: byte) -> (index: int) #no_bounds
|
||||
}
|
||||
|
||||
c_vec: simd.u8x16 = c
|
||||
when !simd.IS_EMULATED {
|
||||
when simd.HAS_HARDWARE_SIMD {
|
||||
// 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
|
||||
@@ -485,7 +497,7 @@ last_index_byte :: proc "contextless" (s: []byte, c: byte) -> int #no_bounds_che
|
||||
}
|
||||
|
||||
c_vec: simd.u8x16 = c
|
||||
when !simd.IS_EMULATED {
|
||||
when simd.HAS_HARDWARE_SIMD {
|
||||
// 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
|
||||
@@ -762,14 +774,14 @@ count :: proc(s, substr: []byte) -> int {
|
||||
}
|
||||
|
||||
|
||||
repeat :: proc(s: []byte, count: int, allocator := context.allocator) -> []byte {
|
||||
repeat :: proc(s: []byte, count: int, allocator := context.allocator, loc := #caller_location) -> []byte {
|
||||
if count < 0 {
|
||||
panic("bytes: negative repeat count")
|
||||
} else if count > 0 && (len(s)*count)/count != len(s) {
|
||||
panic("bytes: repeat count will cause an overflow")
|
||||
}
|
||||
|
||||
b := make([]byte, len(s)*count, allocator)
|
||||
b := make([]byte, len(s)*count, allocator, loc=loc)
|
||||
i := copy(b, s)
|
||||
for i < len(b) { // 2^N trick to reduce the need to copy
|
||||
copy(b[i:], b[:i])
|
||||
@@ -778,12 +790,12 @@ repeat :: proc(s: []byte, count: int, allocator := context.allocator) -> []byte
|
||||
return b
|
||||
}
|
||||
|
||||
replace_all :: proc(s, old, new: []byte, allocator := context.allocator) -> (output: []byte, was_allocation: bool) {
|
||||
return replace(s, old, new, -1, allocator)
|
||||
replace_all :: proc(s, old, new: []byte, allocator := context.allocator, loc := #caller_location) -> (output: []byte, was_allocation: bool) {
|
||||
return replace(s, old, new, -1, allocator, loc)
|
||||
}
|
||||
|
||||
// if n < 0, no limit on the number of replacements
|
||||
replace :: proc(s, old, new: []byte, n: int, allocator := context.allocator) -> (output: []byte, was_allocation: bool) {
|
||||
replace :: proc(s, old, new: []byte, n: int, allocator := context.allocator, loc := #caller_location) -> (output: []byte, was_allocation: bool) {
|
||||
if string(old) == string(new) || n == 0 {
|
||||
was_allocation = false
|
||||
output = s
|
||||
@@ -799,7 +811,7 @@ replace :: proc(s, old, new: []byte, n: int, allocator := context.allocator) ->
|
||||
}
|
||||
|
||||
|
||||
t := make([]byte, len(s) + byte_count*(len(new) - len(old)), allocator)
|
||||
t := make([]byte, len(s) + byte_count*(len(new) - len(old)), allocator, loc=loc)
|
||||
was_allocation = true
|
||||
|
||||
w := 0
|
||||
@@ -823,12 +835,12 @@ replace :: proc(s, old, new: []byte, n: int, allocator := context.allocator) ->
|
||||
return
|
||||
}
|
||||
|
||||
remove :: proc(s, key: []byte, n: int, allocator := context.allocator) -> (output: []byte, was_allocation: bool) {
|
||||
return replace(s, key, {}, n, allocator)
|
||||
remove :: proc(s, key: []byte, n: int, allocator := context.allocator, loc := #caller_location) -> (output: []byte, was_allocation: bool) {
|
||||
return replace(s, key, {}, n, allocator, loc)
|
||||
}
|
||||
|
||||
remove_all :: proc(s, key: []byte, allocator := context.allocator) -> (output: []byte, was_allocation: bool) {
|
||||
return remove(s, key, -1, allocator)
|
||||
remove_all :: proc(s, key: []byte, allocator := context.allocator, loc := #caller_location) -> (output: []byte, was_allocation: bool) {
|
||||
return remove(s, key, -1, allocator, loc)
|
||||
}
|
||||
|
||||
@(private) _ascii_space := [256]u8{'\t' = 1, '\n' = 1, '\v' = 1, '\f' = 1, '\r' = 1, ' ' = 1}
|
||||
@@ -985,16 +997,18 @@ trim_left :: proc(s: []byte, cutset: []byte) -> []byte {
|
||||
if s == nil || cutset == nil {
|
||||
return s
|
||||
}
|
||||
state := cutset
|
||||
return trim_left_proc_with_state(s, is_in_cutset, &state)
|
||||
begin := 0; end := len(s)
|
||||
for ; begin < end && index_byte(cutset, s[begin]) >= 0; begin += 1 {}
|
||||
return s[begin:]
|
||||
}
|
||||
|
||||
trim_right :: proc(s: []byte, cutset: []byte) -> []byte {
|
||||
if s == nil || cutset == nil {
|
||||
return s
|
||||
}
|
||||
state := cutset
|
||||
return trim_right_proc_with_state(s, is_in_cutset, &state)
|
||||
begin := 0; end := len(s)
|
||||
for ; end > begin && index_byte(cutset, s[end - 1]) >= 0; end -= 1 {}
|
||||
return s[:end]
|
||||
}
|
||||
|
||||
trim :: proc(s: []byte, cutset: []byte) -> []byte {
|
||||
@@ -1040,7 +1054,7 @@ trim_suffix :: proc(s, suffix: []byte) -> []byte {
|
||||
return s
|
||||
}
|
||||
|
||||
split_multi :: proc(s: []byte, substrs: [][]byte, skip_empty := false, allocator := context.allocator) -> [][]byte #no_bounds_check {
|
||||
split_multi :: proc(s: []byte, substrs: [][]byte, skip_empty := false, allocator := context.allocator, loc := #caller_location) -> [][]byte #no_bounds_check {
|
||||
if s == nil || len(substrs) <= 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -1088,7 +1102,7 @@ split_multi :: proc(s: []byte, substrs: [][]byte, skip_empty := false, allocator
|
||||
return nil
|
||||
}
|
||||
|
||||
buf := make([][]byte, n, allocator)
|
||||
buf := make([][]byte, n, allocator, loc=loc)
|
||||
|
||||
n, i, l = 0, 0, 0
|
||||
|
||||
@@ -1175,10 +1189,10 @@ split_multi_iterator :: proc(s: ^[]byte, substrs: [][]byte, skip_empty := false)
|
||||
|
||||
// Scrubs invalid utf-8 characters and replaces them with the replacement string
|
||||
// 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, loc := #caller_location) -> []byte {
|
||||
str := s
|
||||
b: Buffer
|
||||
buffer_init_allocator(&b, 0, len(s), allocator)
|
||||
buffer_init_allocator(&b, 0, len(s), allocator, loc)
|
||||
|
||||
has_error := false
|
||||
cursor := 0
|
||||
@@ -1190,11 +1204,11 @@ scrub :: proc(s: []byte, replacement: []byte, allocator := context.allocator) ->
|
||||
if r == utf8.RUNE_ERROR {
|
||||
if !has_error {
|
||||
has_error = true
|
||||
buffer_write(&b, origin[:cursor])
|
||||
buffer_write(&b, origin[:cursor], loc)
|
||||
}
|
||||
} else if has_error {
|
||||
has_error = false
|
||||
buffer_write(&b, replacement)
|
||||
buffer_write(&b, replacement, loc)
|
||||
|
||||
origin = origin[cursor:]
|
||||
cursor = 0
|
||||
@@ -1208,10 +1222,10 @@ scrub :: proc(s: []byte, replacement: []byte, allocator := context.allocator) ->
|
||||
}
|
||||
|
||||
|
||||
reverse :: proc(s: []byte, allocator := context.allocator) -> []byte {
|
||||
reverse :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> []byte {
|
||||
str := s
|
||||
n := len(str)
|
||||
buf := make([]byte, n)
|
||||
buf := make([]byte, n, loc=loc)
|
||||
i := n
|
||||
|
||||
for len(str) > 0 {
|
||||
@@ -1223,7 +1237,7 @@ reverse :: proc(s: []byte, allocator := context.allocator) -> []byte {
|
||||
return buf
|
||||
}
|
||||
|
||||
expand_tabs :: proc(s: []byte, tab_size: int, allocator := context.allocator) -> []byte {
|
||||
expand_tabs :: proc(s: []byte, tab_size: int, allocator := context.allocator, loc := #caller_location) -> []byte {
|
||||
if tab_size <= 0 {
|
||||
panic("tab size must be positive")
|
||||
}
|
||||
@@ -1234,7 +1248,7 @@ expand_tabs :: proc(s: []byte, tab_size: int, allocator := context.allocator) ->
|
||||
}
|
||||
|
||||
b: Buffer
|
||||
buffer_init_allocator(&b, 0, len(s), allocator)
|
||||
buffer_init_allocator(&b, 0, len(s), allocator, loc=loc)
|
||||
|
||||
str := s
|
||||
column: int
|
||||
@@ -1246,7 +1260,7 @@ expand_tabs :: proc(s: []byte, tab_size: int, allocator := context.allocator) ->
|
||||
expand := tab_size - column%tab_size
|
||||
|
||||
for i := 0; i < expand; i += 1 {
|
||||
buffer_write_byte(&b, ' ')
|
||||
buffer_write_byte(&b, ' ', loc)
|
||||
}
|
||||
|
||||
column += expand
|
||||
@@ -1257,7 +1271,7 @@ expand_tabs :: proc(s: []byte, tab_size: int, allocator := context.allocator) ->
|
||||
column += w
|
||||
}
|
||||
|
||||
buffer_write_rune(&b, r)
|
||||
buffer_write_rune(&b, r, loc)
|
||||
}
|
||||
|
||||
str = str[w:]
|
||||
@@ -1282,59 +1296,59 @@ partition :: proc(str, sep: []byte) -> (head, match, tail: []byte) {
|
||||
center_justify :: centre_justify // NOTE(bill): Because Americans exist
|
||||
|
||||
// centre_justify returns a byte slice with a pad byte slice at boths sides if the str's rune length is smaller than length
|
||||
centre_justify :: proc(str: []byte, length: int, pad: []byte, allocator := context.allocator) -> []byte {
|
||||
centre_justify :: proc(str: []byte, length: int, pad: []byte, allocator := context.allocator, loc := #caller_location) -> []byte {
|
||||
n := rune_count(str)
|
||||
if n >= length || pad == nil {
|
||||
return clone(str, allocator)
|
||||
return clone(str, allocator, loc)
|
||||
}
|
||||
|
||||
remains := length-1
|
||||
pad_len := rune_count(pad)
|
||||
|
||||
b: Buffer
|
||||
buffer_init_allocator(&b, 0, len(str) + (remains/pad_len + 1)*len(pad), allocator)
|
||||
buffer_init_allocator(&b, 0, len(str) + (remains/pad_len + 1)*len(pad), allocator, loc)
|
||||
|
||||
write_pad_string(&b, pad, pad_len, remains/2)
|
||||
buffer_write(&b, str)
|
||||
buffer_write(&b, str, loc)
|
||||
write_pad_string(&b, pad, pad_len, (remains+1)/2)
|
||||
|
||||
return buffer_to_bytes(&b)
|
||||
}
|
||||
|
||||
// left_justify returns a byte slice with a pad byte slice at left side if the str's rune length is smaller than length
|
||||
left_justify :: proc(str: []byte, length: int, pad: []byte, allocator := context.allocator) -> []byte {
|
||||
left_justify :: proc(str: []byte, length: int, pad: []byte, allocator := context.allocator, loc := #caller_location) -> []byte {
|
||||
n := rune_count(str)
|
||||
if n >= length || pad == nil {
|
||||
return clone(str, allocator)
|
||||
return clone(str, allocator, loc)
|
||||
}
|
||||
|
||||
remains := length-1
|
||||
pad_len := rune_count(pad)
|
||||
|
||||
b: Buffer
|
||||
buffer_init_allocator(&b, 0, len(str) + (remains/pad_len + 1)*len(pad), allocator)
|
||||
buffer_init_allocator(&b, 0, len(str) + (remains/pad_len + 1)*len(pad), allocator, loc)
|
||||
|
||||
buffer_write(&b, str)
|
||||
buffer_write(&b, str, loc)
|
||||
write_pad_string(&b, pad, pad_len, remains)
|
||||
|
||||
return buffer_to_bytes(&b)
|
||||
}
|
||||
|
||||
// right_justify returns a byte slice with a pad byte slice at right side if the str's rune length is smaller than length
|
||||
right_justify :: proc(str: []byte, length: int, pad: []byte, allocator := context.allocator) -> []byte {
|
||||
right_justify :: proc(str: []byte, length: int, pad: []byte, allocator := context.allocator, loc := #caller_location) -> []byte {
|
||||
n := rune_count(str)
|
||||
if n >= length || pad == nil {
|
||||
return clone(str, allocator)
|
||||
return clone(str, allocator, loc)
|
||||
}
|
||||
|
||||
remains := length-1
|
||||
pad_len := rune_count(pad)
|
||||
|
||||
b: Buffer
|
||||
buffer_init_allocator(&b, 0, len(str) + (remains/pad_len + 1)*len(pad), allocator)
|
||||
buffer_init_allocator(&b, 0, len(str) + (remains/pad_len + 1)*len(pad), allocator, loc)
|
||||
|
||||
write_pad_string(&b, pad, pad_len, remains)
|
||||
buffer_write(&b, str)
|
||||
buffer_write(&b, str, loc)
|
||||
|
||||
return buffer_to_bytes(&b)
|
||||
}
|
||||
@@ -1343,11 +1357,11 @@ right_justify :: proc(str: []byte, length: int, pad: []byte, allocator := contex
|
||||
|
||||
|
||||
@private
|
||||
write_pad_string :: proc(b: ^Buffer, pad: []byte, pad_len, remains: int) {
|
||||
write_pad_string :: proc(b: ^Buffer, pad: []byte, pad_len, remains: int, loc := #caller_location) {
|
||||
repeats := remains / pad_len
|
||||
|
||||
for i := 0; i < repeats; i += 1 {
|
||||
buffer_write(b, pad)
|
||||
buffer_write(b, pad, loc)
|
||||
}
|
||||
|
||||
n := remains % pad_len
|
||||
@@ -1355,7 +1369,7 @@ write_pad_string :: proc(b: ^Buffer, pad: []byte, pad_len, remains: int) {
|
||||
|
||||
for i := 0; i < n; i += 1 {
|
||||
r, width := utf8.decode_rune(p)
|
||||
buffer_write_rune(b, r)
|
||||
buffer_write_rune(b, r, loc)
|
||||
p = p[width:]
|
||||
}
|
||||
}
|
||||
@@ -1363,7 +1377,7 @@ write_pad_string :: proc(b: ^Buffer, pad: []byte, pad_len, remains: int) {
|
||||
|
||||
// fields splits the byte slice s around each instance of one or more consecutive white space character, defined by unicode.is_space
|
||||
// returning a slice of subslices of s or an empty slice if s only contains white space
|
||||
fields :: proc(s: []byte, allocator := context.allocator) -> [][]byte #no_bounds_check {
|
||||
fields :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> [][]byte #no_bounds_check {
|
||||
n := 0
|
||||
was_space := 1
|
||||
set_bits := u8(0)
|
||||
@@ -1378,14 +1392,14 @@ fields :: proc(s: []byte, allocator := context.allocator) -> [][]byte #no_bounds
|
||||
}
|
||||
|
||||
if set_bits >= utf8.RUNE_SELF {
|
||||
return fields_proc(s, unicode.is_space, allocator)
|
||||
return fields_proc(s, unicode.is_space, allocator, loc)
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
a := make([][]byte, n, allocator)
|
||||
a := make([][]byte, n, allocator, loc=loc)
|
||||
na := 0
|
||||
field_start := 0
|
||||
i := 0
|
||||
@@ -1419,15 +1433,15 @@ fields :: proc(s: []byte, allocator := context.allocator) -> [][]byte #no_bounds
|
||||
//
|
||||
// fields_proc makes no guarantee about the order in which it calls f(ch)
|
||||
// it assumes that `f` always returns the same value for a given ch
|
||||
fields_proc :: proc(s: []byte, f: proc(rune) -> bool, allocator := context.allocator) -> [][]byte #no_bounds_check {
|
||||
subslices := make([dynamic][]byte, 0, 32, allocator)
|
||||
fields_proc :: proc(s: []byte, f: proc(rune) -> bool, allocator := context.allocator, loc := #caller_location) -> [][]byte #no_bounds_check {
|
||||
subslices := make([dynamic][]byte, 0, 32, allocator, loc=loc)
|
||||
|
||||
start, end := -1, -1
|
||||
for r, offset in string(s) {
|
||||
end = offset
|
||||
if f(r) {
|
||||
if start >= 0 {
|
||||
append(&subslices, s[start : end])
|
||||
append(&subslices, s[start : end], loc=loc)
|
||||
// -1 could be used, but just speed it up through bitwise not
|
||||
// gotta love 2's complement
|
||||
start = ~start
|
||||
@@ -1440,13 +1454,13 @@ fields_proc :: proc(s: []byte, f: proc(rune) -> bool, allocator := context.alloc
|
||||
}
|
||||
|
||||
if start >= 0 {
|
||||
append(&subslices, s[start : len(s)])
|
||||
append(&subslices, s[start : len(s)], loc=loc)
|
||||
}
|
||||
|
||||
return subslices[:]
|
||||
}
|
||||
|
||||
// alias returns true iff a and b have a non-zero length, and any part of
|
||||
// alias returns true if and only if (⟺) 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)
|
||||
@@ -1460,7 +1474,7 @@ alias :: proc "contextless" (a, b: []byte) -> bool {
|
||||
return a_start <= b_end && b_start <= a_end
|
||||
}
|
||||
|
||||
// alias_inexactly returns true iff a and b have a non-zero length,
|
||||
// alias_inexactly returns true if and only if (⟺) 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).
|
||||
|
||||
@@ -61,6 +61,27 @@ reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Erro
|
||||
}
|
||||
return
|
||||
}
|
||||
reader_read_slice :: proc(r: ^Reader, slice: $T/[]$S) -> (n: int, err: io.Error) {
|
||||
b := ([^]byte)(raw_data(slice))[:len(slice)*size_of(S)]
|
||||
return reader_read(r, b)
|
||||
}
|
||||
|
||||
reader_read_slice_at :: proc(r: ^Reader, slice: $T/[]$S, off: i64) -> (n: int, err: io.Error) {
|
||||
b := ([^]byte)(raw_data(slice))[:len(slice)*size_of(S)]
|
||||
return reader_read_at(r, b, off)
|
||||
}
|
||||
|
||||
reader_read_ptr :: proc(r: ^Reader, data: rawptr, len: int) -> (n: int, err: io.Error) {
|
||||
b := ([^]byte)(data)[:len]
|
||||
return reader_read(r, b)
|
||||
}
|
||||
|
||||
reader_read_ptr_at :: proc(r: ^Reader, data: rawptr, len: int, off: i64) -> (n: int, err: io.Error) {
|
||||
b := ([^]byte)(data)[:len]
|
||||
return reader_read_at(r, b, off)
|
||||
}
|
||||
|
||||
|
||||
reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) {
|
||||
r.prev_rune = -1
|
||||
if r.i >= i64(len(r.s)) {
|
||||
@@ -160,6 +181,6 @@ _reader_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offse
|
||||
case .Query:
|
||||
return io.query_utility({.Read, .Read_At, .Seek, .Size, .Query})
|
||||
}
|
||||
return 0, .Empty
|
||||
return 0, .Unsupported
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Defines the basic types used by `C` programs for foreign function and data structure interop.
|
||||
package c
|
||||
|
||||
import builtin "base:builtin"
|
||||
import builtin "base:builtin"
|
||||
import intrinsics "base:intrinsics"
|
||||
|
||||
char :: builtin.u8 // assuming -funsigned-char
|
||||
|
||||
@@ -48,7 +50,7 @@ int_least64_t :: builtin.i64
|
||||
uint_least64_t :: builtin.u64
|
||||
|
||||
// Same on Windows, Linux, and FreeBSD
|
||||
when ODIN_ARCH == .i386 || ODIN_ARCH == .amd64 {
|
||||
when ODIN_ARCH == .i386 {
|
||||
int_fast8_t :: builtin.i8
|
||||
uint_fast8_t :: builtin.u8
|
||||
int_fast16_t :: builtin.i32
|
||||
@@ -57,6 +59,15 @@ when ODIN_ARCH == .i386 || ODIN_ARCH == .amd64 {
|
||||
uint_fast32_t :: builtin.u32
|
||||
int_fast64_t :: builtin.i64
|
||||
uint_fast64_t :: builtin.u64
|
||||
} else when ODIN_ARCH == .amd64 {
|
||||
int_fast8_t :: builtin.i8
|
||||
uint_fast8_t :: builtin.u8
|
||||
int_fast16_t :: long
|
||||
uint_fast16_t :: ulong
|
||||
int_fast32_t :: long
|
||||
uint_fast32_t :: ulong
|
||||
int_fast64_t :: builtin.i64
|
||||
uint_fast64_t :: builtin.u64
|
||||
} else {
|
||||
int_fast8_t :: builtin.i8
|
||||
uint_fast8_t :: builtin.u8
|
||||
@@ -105,14 +116,6 @@ NDEBUG :: !ODIN_DEBUG
|
||||
|
||||
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,
|
||||
}
|
||||
va_list :: intrinsics.c_va_list
|
||||
|
||||
FILE :: struct {}
|
||||
|
||||
@@ -5,7 +5,7 @@ package libc
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
foreign import libc "system:System"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package libc
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
foreign import libc "system:System"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
|
||||
2
core/c/libc/doc.odin
Normal file
2
core/c/libc/doc.odin
Normal file
@@ -0,0 +1,2 @@
|
||||
// Declares the commonly used things in `libc` (`C` standard library).
|
||||
package libc
|
||||
@@ -5,7 +5,7 @@ package libc
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
foreign import libc "system:System"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
@@ -80,25 +80,6 @@ when ODIN_OS == .Darwin {
|
||||
ERANGE :: 34
|
||||
}
|
||||
|
||||
when ODIN_OS == .Haiku {
|
||||
@(private="file")
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
@(link_name="_errnop")
|
||||
_get_errno :: proc() -> ^int ---
|
||||
}
|
||||
|
||||
_HAIKU_USE_POSITIVE_POSIX_ERRORS :: #config(HAIKU_USE_POSITIVE_POSIX_ERRORS, false)
|
||||
_POSIX_ERROR_FACTOR :: -1 when _HAIKU_USE_POSITIVE_POSIX_ERRORS else 1
|
||||
|
||||
@(private="file") _GENERAL_ERROR_BASE :: min(int)
|
||||
@(private="file") _POSIX_ERROR_BASE :: _GENERAL_ERROR_BASE + 0x7000
|
||||
|
||||
EDOM :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 16)
|
||||
EILSEQ :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 38)
|
||||
ERANGE :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 17)
|
||||
}
|
||||
|
||||
when ODIN_OS == .JS {
|
||||
_ :: libc
|
||||
_get_errno :: proc "c" () -> ^int {
|
||||
|
||||
@@ -5,7 +5,7 @@ import "core:c"
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
foreign import libc "system:System"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
@@ -72,14 +72,14 @@ when ODIN_OS == .Windows {
|
||||
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"`,
|
||||
_W_decimal_point: cstring16,
|
||||
_W_thousands_sep: cstring16,
|
||||
_W_int_curr_symbol: cstring16,
|
||||
_W_currency_symbol: cstring16,
|
||||
_W_mon_decimal_point: cstring16,
|
||||
_W_mon_thousands_sep: cstring16,
|
||||
_W_positive_sign: cstring16,
|
||||
_W_negative_sign: cstring16,
|
||||
}
|
||||
} else {
|
||||
lconv :: struct {
|
||||
@@ -110,7 +110,7 @@ when ODIN_OS == .Windows {
|
||||
}
|
||||
}
|
||||
|
||||
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku || ODIN_OS == .Windows {
|
||||
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Windows {
|
||||
|
||||
LC_ALL :: 0
|
||||
LC_COLLATE :: 1
|
||||
|
||||
@@ -7,7 +7,7 @@ import "base:intrinsics"
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
foreign import libc "system:System"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
@@ -154,12 +154,12 @@ _nan_bit_pattern := ~u64(0)
|
||||
|
||||
// On amd64 Windows and Linux, float_t and double_t are respectively both
|
||||
// their usual types. On x86 it's not possible to define these types correctly
|
||||
// since they would be long double which Odin does have support for.
|
||||
// since they would be long double which Odin does NOT have support for.
|
||||
float_t :: float
|
||||
double_t :: double
|
||||
|
||||
NAN := transmute(double)(_nan_bit_pattern)
|
||||
INFINITY :: 1e5000
|
||||
INFINITY :: 0h7ff00000_00000000 // +Inf
|
||||
|
||||
HUGE_VALF :: INFINITY
|
||||
HUGE_VAL :: double(INFINITY)
|
||||
|
||||
@@ -5,7 +5,7 @@ package libc
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
foreign import libc "system:System"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ package libc
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
foreign import libc "system:System"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
@@ -34,7 +34,7 @@ when ODIN_OS == .Windows {
|
||||
SIGTERM :: 15
|
||||
}
|
||||
|
||||
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Haiku || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Darwin {
|
||||
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Darwin {
|
||||
SIG_ERR :: rawptr(~uintptr(0))
|
||||
SIG_DFL :: rawptr(uintptr(0))
|
||||
SIG_IGN :: rawptr(uintptr(1))
|
||||
|
||||
@@ -4,29 +4,12 @@ package libc
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
import "core:c"
|
||||
va_list :: intrinsics.c_va_list
|
||||
|
||||
@(private="file")
|
||||
@(default_calling_convention="none")
|
||||
foreign _ {
|
||||
@(link_name="llvm.va_start") _va_start :: proc(arglist: ^i8) ---
|
||||
@(link_name="llvm.va_end") _va_end :: proc(arglist: ^i8) ---
|
||||
@(link_name="llvm.va_copy") _va_copy :: proc(dst, src: ^i8) ---
|
||||
}
|
||||
va_start :: intrinsics.c_va_start
|
||||
va_end :: intrinsics.c_va_end
|
||||
va_copy :: intrinsics.c_va_copy
|
||||
|
||||
va_list :: c.va_list
|
||||
|
||||
va_start :: #force_inline proc(ap: ^va_list, _: any) {
|
||||
_va_start(cast(^i8)ap)
|
||||
}
|
||||
|
||||
va_end :: #force_inline proc(ap: ^va_list) {
|
||||
_va_end(cast(^i8)ap)
|
||||
}
|
||||
|
||||
va_copy :: #force_inline proc(dst, src: ^va_list) {
|
||||
_va_copy(cast(^i8)dst, cast(^i8)src)
|
||||
}
|
||||
|
||||
// We cannot provide va_arg as there is no way to create "C" style procedures
|
||||
// in Odin which take variable arguments the C way. The #c_vararg attribute only
|
||||
|
||||
@@ -9,7 +9,7 @@ when ODIN_OS == .Windows {
|
||||
"system:legacy_stdio_definitions.lib",
|
||||
}
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
foreign import libc "system:System"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
@@ -200,36 +200,6 @@ 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"
|
||||
@@ -275,7 +245,7 @@ foreign libc {
|
||||
// 7.21.7 Character input/output functions
|
||||
fgetc :: proc(stream: ^FILE) -> int ---
|
||||
fgets :: proc(s: [^]char, n: int, stream: ^FILE) -> [^]char ---
|
||||
fputc :: proc(s: cstring, stream: ^FILE) -> int ---
|
||||
fputc :: proc(s: c.int, stream: ^FILE) -> int ---
|
||||
getc :: proc(stream: ^FILE) -> int ---
|
||||
getchar :: proc() -> int ---
|
||||
putc :: proc(c: int, stream: ^FILE) -> int ---
|
||||
@@ -390,7 +360,7 @@ to_stream :: proc(file: ^FILE) -> io.Stream {
|
||||
}
|
||||
|
||||
case .Destroy:
|
||||
return 0, .Empty
|
||||
return 0, .Unsupported
|
||||
|
||||
case .Query:
|
||||
return io.query_utility({ .Close, .Flush, .Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Query })
|
||||
|
||||
@@ -5,7 +5,7 @@ package libc
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
foreign import libc "system:System"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
@@ -42,22 +42,6 @@ when ODIN_OS == .Linux {
|
||||
}
|
||||
}
|
||||
|
||||
when ODIN_OS == .Haiku {
|
||||
RAND_MAX :: 0x7fffffff
|
||||
|
||||
// GLIBC and MUSL only
|
||||
@(private="file")
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
__ctype_get_mb_cur_max :: proc() -> ushort ---
|
||||
}
|
||||
|
||||
MB_CUR_MAX :: #force_inline proc() -> size_t {
|
||||
return size_t(__ctype_get_mb_cur_max())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD {
|
||||
RAND_MAX :: 0x7fffffff
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import "base:runtime"
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
foreign import libc "system:System"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ package libc
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
foreign import libc "system:System"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
@@ -45,7 +45,7 @@ when ODIN_OS == .Windows {
|
||||
}
|
||||
}
|
||||
|
||||
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku || ODIN_OS == .JS {
|
||||
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .JS {
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
// 7.27.2 Time manipulation functions
|
||||
@@ -95,7 +95,7 @@ when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS =
|
||||
|
||||
time_t :: distinct i64
|
||||
|
||||
when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku {
|
||||
when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD {
|
||||
clock_t :: distinct int32_t
|
||||
} else {
|
||||
clock_t :: distinct long
|
||||
|
||||
@@ -5,7 +5,7 @@ package libc
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
foreign import libc "system:System"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ package libc
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
foreign import libc "system:System"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
@@ -17,12 +17,12 @@ foreign libc {
|
||||
fwscanf :: proc(stream: ^FILE, format: [^]wchar_t, #c_vararg arg: ..any) -> int ---
|
||||
swprintf :: proc(stream: ^FILE, n: size_t, format: [^]wchar_t, #c_vararg arg: ..any) -> int ---
|
||||
swscanf :: proc(s, format: [^]wchar_t, #c_vararg arg: ..any) -> int ---
|
||||
vfwprintf :: proc(stream: ^FILE, format: [^]wchar_t, arg: va_list) -> int ---
|
||||
vfwscanf :: proc(stream: ^FILE, format: [^]wchar_t, arg: va_list) -> int ---
|
||||
vswprintf :: proc(s: [^]wchar_t, n: size_t, format: [^]wchar_t, arg: va_list) -> int ---
|
||||
vswscanf :: proc(s, format: [^]wchar_t, arg: va_list) -> int ---
|
||||
vwprintf :: proc(format: [^]wchar_t, arg: va_list) -> int ---
|
||||
vwscanf :: proc(format: [^]wchar_t, arg: va_list) -> int ---
|
||||
vfwprintf :: proc(stream: ^FILE, format: [^]wchar_t, arg: ^va_list) -> int ---
|
||||
vfwscanf :: proc(stream: ^FILE, format: [^]wchar_t, arg: ^va_list) -> int ---
|
||||
vswprintf :: proc(s: [^]wchar_t, n: size_t, format: [^]wchar_t, arg: ^va_list) -> int ---
|
||||
vswscanf :: proc(s, format: [^]wchar_t, arg: ^va_list) -> int ---
|
||||
vwprintf :: proc(format: [^]wchar_t, arg: ^va_list) -> int ---
|
||||
vwscanf :: proc(format: [^]wchar_t, arg: ^va_list) -> int ---
|
||||
wprintf :: proc(format: [^]wchar_t, #c_vararg arg: ..any) -> int ---
|
||||
wscanf :: proc(format: [^]wchar_t, #c_vararg arg: ..any) -> int ---
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ package libc
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
foreign import libc "system:System"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
@@ -30,10 +30,6 @@ when ODIN_OS == .Windows {
|
||||
wctrans_t :: distinct int
|
||||
wctype_t :: distinct ulong
|
||||
|
||||
} else when ODIN_OS == .Haiku {
|
||||
wctrans_t :: distinct i32
|
||||
wctype_t :: distinct i32
|
||||
|
||||
}
|
||||
|
||||
@(default_calling_convention="c")
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
// A collection of utilities to aid with other `compress`ion packages.
|
||||
package compress
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
Made available under Odin's license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation, optimization.
|
||||
*/
|
||||
|
||||
|
||||
// package compress is a collection of utilities to aid with other compression packages
|
||||
package compress
|
||||
|
||||
import "core:io"
|
||||
import "core:bytes"
|
||||
import "base:runtime"
|
||||
@@ -139,9 +138,6 @@ Context_Memory_Input :: struct #packed {
|
||||
}
|
||||
when size_of(rawptr) == 8 {
|
||||
#assert(size_of(Context_Memory_Input) == 64)
|
||||
} else {
|
||||
// e.g. `-target:windows_i386`
|
||||
#assert(size_of(Context_Memory_Input) == 52)
|
||||
}
|
||||
|
||||
Context_Stream_Input :: struct #packed {
|
||||
@@ -301,7 +297,7 @@ peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid
|
||||
curr := z.input->impl_seek(0, .Current) or_return
|
||||
r, e1 := io.to_reader_at(z.input)
|
||||
if !e1 {
|
||||
return T{}, .Empty
|
||||
return T{}, .Unsupported
|
||||
}
|
||||
when size <= 128 {
|
||||
b: [size]u8
|
||||
@@ -310,7 +306,7 @@ peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid
|
||||
}
|
||||
_, e2 := io.read_at(r, b[:], curr)
|
||||
if e2 != .None {
|
||||
return T{}, .Empty
|
||||
return T{}, .Unsupported
|
||||
}
|
||||
|
||||
res = (^T)(&b[0])^
|
||||
@@ -328,7 +324,7 @@ peek_data_at_offset_from_stream :: #force_inline proc(z: ^Context_Stream_Input,
|
||||
|
||||
r, e3 := io.to_reader_at(z.input)
|
||||
if !e3 {
|
||||
return T{}, .Empty
|
||||
return T{}, .Unsupported
|
||||
}
|
||||
when size <= 128 {
|
||||
b: [size]u8
|
||||
@@ -337,7 +333,7 @@ peek_data_at_offset_from_stream :: #force_inline proc(z: ^Context_Stream_Input,
|
||||
}
|
||||
_, e4 := io.read_at(r, b[:], pos)
|
||||
if e4 != .None {
|
||||
return T{}, .Empty
|
||||
return T{}, .Unsupported
|
||||
}
|
||||
|
||||
// Return read head to original position.
|
||||
@@ -364,23 +360,26 @@ refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width :=
|
||||
refill := u64(width)
|
||||
b := u64(0)
|
||||
|
||||
if z.num_bits > refill {
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
if z.num_bits > refill {
|
||||
break
|
||||
}
|
||||
if z.code_buffer == 0 && z.num_bits > 63 {
|
||||
z.num_bits = 0
|
||||
}
|
||||
if z.code_buffer >= 1 << uint(z.num_bits) {
|
||||
// Code buffer is malformed.
|
||||
z.num_bits = max(u64)
|
||||
return
|
||||
}
|
||||
if len(z.input_data) != 0 {
|
||||
b = u64(z.input_data[0])
|
||||
z.input_data = z.input_data[1:]
|
||||
} else {
|
||||
b = 0
|
||||
return
|
||||
}
|
||||
|
||||
z.code_buffer |= b << u8(z.num_bits)
|
||||
z.num_bits += 8
|
||||
if z.num_bits > refill {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +1,11 @@
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
A small `GZIP` unpacker.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
Ginger Bill: Cosmetic changes.
|
||||
|
||||
A small GZIP implementation as an example.
|
||||
*/
|
||||
|
||||
/*
|
||||
Example:
|
||||
import "core:bytes"
|
||||
import "core:os"
|
||||
import "core:compress"
|
||||
import "core:compress/gzip"
|
||||
import "core:fmt"
|
||||
|
||||
// Small GZIP file with fextra, fname and fcomment present.
|
||||
@@ -31,7 +23,8 @@ Example:
|
||||
|
||||
main :: proc() {
|
||||
// Set up output buffer.
|
||||
buf := bytes.Buffer{}
|
||||
buf: bytes.Buffer
|
||||
defer bytes.buffer_destroy(&buf)
|
||||
|
||||
stdout :: proc(s: string) {
|
||||
os.write_string(os.stdout, s)
|
||||
@@ -40,15 +33,13 @@ Example:
|
||||
os.write_string(os.stderr, s)
|
||||
}
|
||||
|
||||
args := os.args
|
||||
|
||||
if len(args) < 2 {
|
||||
if len(os.args) < 2 {
|
||||
stderr("No input file specified.\n")
|
||||
err := load(data=TEST, buf=&buf, known_gzip_size=len(TEST))
|
||||
err := gzip.load(data=TEST, buf=&buf, known_gzip_size=len(TEST))
|
||||
if err == nil {
|
||||
stdout("Displaying test vector: ")
|
||||
stdout("Displaying test vector: \"")
|
||||
stdout(bytes.buffer_to_string(&buf))
|
||||
stdout("\n")
|
||||
stdout("\"\n")
|
||||
} else {
|
||||
fmt.printf("gzip.load returned %v\n", err)
|
||||
}
|
||||
@@ -56,35 +47,42 @@ Example:
|
||||
os.exit(0)
|
||||
}
|
||||
|
||||
// The rest are all files.
|
||||
args = args[1:]
|
||||
err: Error
|
||||
for file in os.args[1:] {
|
||||
err: gzip.Error
|
||||
|
||||
for file in args {
|
||||
if file == "-" {
|
||||
// Read from stdin
|
||||
s := os.stream_from_handle(os.stdin)
|
||||
ctx := &compress.Context_Stream_Input{
|
||||
input = s,
|
||||
input = os.stdin.stream,
|
||||
}
|
||||
err = load(ctx, &buf)
|
||||
err = gzip.load(ctx, &buf)
|
||||
} else {
|
||||
err = load(file, &buf)
|
||||
err = gzip.load(file, &buf)
|
||||
}
|
||||
if err != nil {
|
||||
if err != E_General.File_Not_Found {
|
||||
stderr("File not found: ")
|
||||
stderr(file)
|
||||
stderr("\n")
|
||||
os.exit(1)
|
||||
}
|
||||
switch err {
|
||||
case nil:
|
||||
stdout(bytes.buffer_to_string(&buf))
|
||||
case gzip.E_General.File_Not_Found:
|
||||
stderr("File not found: ")
|
||||
stderr(file)
|
||||
stderr("\n")
|
||||
os.exit(1)
|
||||
case:
|
||||
stderr("GZIP returned an error.\n")
|
||||
bytes.buffer_destroy(&buf)
|
||||
os.exit(2)
|
||||
}
|
||||
stdout(bytes.buffer_to_string(&buf))
|
||||
}
|
||||
bytes.buffer_destroy(&buf)
|
||||
}
|
||||
*/
|
||||
package compress_gzip
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
Ginger Bill: Cosmetic changes.
|
||||
|
||||
A small GZIP implementation as an example.
|
||||
*/
|
||||
@@ -2,7 +2,7 @@ package compress_gzip
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
Made available under Odin's license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
@@ -107,14 +107,10 @@ load :: proc{load_from_bytes, load_from_file, load_from_context}
|
||||
load_from_file :: proc(filename: string, buf: ^bytes.Buffer, expected_output_size := -1, allocator := context.allocator) -> (err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
data, ok := os.read_entire_file(filename)
|
||||
defer delete(data)
|
||||
file_data, file_err := os.read_entire_file(filename, allocator)
|
||||
defer delete(file_data)
|
||||
|
||||
err = E_General.File_Not_Found
|
||||
if ok {
|
||||
err = load_from_bytes(data, buf, len(data), expected_output_size)
|
||||
}
|
||||
return
|
||||
return load_from_bytes(file_data, buf, len(file_data), expected_output_size) if file_err == nil else E_General.File_Not_Found
|
||||
}
|
||||
|
||||
load_from_bytes :: proc(data: []byte, buf: ^bytes.Buffer, known_gzip_size := -1, expected_output_size := -1, allocator := context.allocator) -> (err: Error) {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package compress_shoco
|
||||
|
||||
/*
|
||||
This file was generated, so don't edit this by hand.
|
||||
Transliterated from https://github.com/Ed-von-Schleck/shoco/blob/master/shoco_model.h,
|
||||
which is an English word model.
|
||||
*/
|
||||
|
||||
package compress_shoco
|
||||
|
||||
DEFAULT_MODEL :: Shoco_Model {
|
||||
min_char = 39,
|
||||
max_char = 122,
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
// `Shoco` short string compression and decompression.
|
||||
package compress_shoco
|
||||
|
||||
/*
|
||||
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
Made available under Odin's license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
@@ -8,9 +11,6 @@
|
||||
An implementation of [shoco](https://github.com/Ed-von-Schleck/shoco) by Christian Schramm.
|
||||
*/
|
||||
|
||||
// package shoco is an implementation of the shoco short string compressor.
|
||||
package compress_shoco
|
||||
|
||||
import "base:intrinsics"
|
||||
import "core:compress"
|
||||
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
`Deflate` decompression of raw and `ZLIB`-type streams.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
|
||||
An example of how to use `zlib.inflate`.
|
||||
*/
|
||||
|
||||
/*
|
||||
Example:
|
||||
package main
|
||||
|
||||
@@ -49,3 +41,13 @@ Example:
|
||||
}
|
||||
*/
|
||||
package compress_zlib
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
|
||||
An example of how to use `zlib.inflate`.
|
||||
*/
|
||||
@@ -1,9 +1,9 @@
|
||||
#+vet !using-param
|
||||
#+feature using-stmt
|
||||
package compress_zlib
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
Made available under Odin's license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation, optimization.
|
||||
@@ -322,9 +322,6 @@ decode_huffman_slowpath :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Erro
|
||||
@(optimization_mode="favor_size")
|
||||
decode_huffman :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
|
||||
if z.num_bits < 16 {
|
||||
if z.num_bits > 63 {
|
||||
return 0, .Code_Buffer_Malformed
|
||||
}
|
||||
compress.refill_lsb(z)
|
||||
if z.num_bits > 63 {
|
||||
return 0, .Stream_Too_Short
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
/*
|
||||
package avl implements an AVL tree.
|
||||
|
||||
The implementation is non-intrusive, and non-recursive.
|
||||
*/
|
||||
// A non-intrusive and non-recursive implementation of `AVL` trees.
|
||||
package container_avl
|
||||
|
||||
@(require) import "base:intrinsics"
|
||||
@@ -104,20 +100,20 @@ len :: proc "contextless" (t: ^$T/Tree($Value)) -> int {
|
||||
return t._size
|
||||
}
|
||||
|
||||
// first returns the first node in the tree (in-order) or nil iff
|
||||
// first returns the first node in the tree (in-order) or nil if and only if (⟺)
|
||||
// the tree is empty.
|
||||
first :: proc "contextless" (t: ^$T/Tree($Value)) -> ^Node(Value) {
|
||||
return tree_first_or_last_in_order(t, Direction.Backward)
|
||||
}
|
||||
|
||||
// last returns the last element in the tree (in-order) or nil iff
|
||||
// last returns the last element in the tree (in-order) or nil if and only if (⟺)
|
||||
// the tree is empty.
|
||||
last :: proc "contextless" (t: ^$T/Tree($Value)) -> ^Node(Value) {
|
||||
return tree_first_or_last_in_order(t, Direction.Forward)
|
||||
}
|
||||
|
||||
// find finds the value in the tree, and returns the corresponding
|
||||
// node or nil iff the value is not present.
|
||||
// node or nil if and only if (⟺) the value is not present.
|
||||
find :: proc(t: ^$T/Tree($Value), value: Value) -> ^Node(Value) {
|
||||
cur := t._root
|
||||
descend_loop: for cur != nil {
|
||||
@@ -172,7 +168,7 @@ find_or_insert :: proc(
|
||||
return
|
||||
}
|
||||
|
||||
// remove removes a node or value from the tree, and returns true iff the
|
||||
// remove removes a node or value from the tree, and returns true if and only if (⟺) the
|
||||
// removal was successful. While the node's value will be left intact,
|
||||
// the node itself will be freed via the tree's node allocator.
|
||||
remove :: proc {
|
||||
@@ -180,7 +176,7 @@ remove :: proc {
|
||||
remove_node,
|
||||
}
|
||||
|
||||
// remove_value removes a value from the tree, and returns true iff the
|
||||
// remove_value removes a value from the tree, and returns true if and only if (⟺) the
|
||||
// removal was successful. While the node's value will be left intact,
|
||||
// the node itself will be freed via the tree's node allocator.
|
||||
remove_value :: proc(t: ^$T/Tree($Value), value: Value, call_on_remove: bool = true) -> bool {
|
||||
@@ -191,7 +187,7 @@ remove_value :: proc(t: ^$T/Tree($Value), value: Value, call_on_remove: bool = t
|
||||
return remove_node(t, n, call_on_remove)
|
||||
}
|
||||
|
||||
// remove_node removes a node from the tree, and returns true iff the
|
||||
// remove_node removes a node from the tree, and returns true if and only if (⟺) the
|
||||
// removal was successful. While the node's value will be left intact,
|
||||
// the node itself will be freed via the tree's node allocator.
|
||||
remove_node :: proc(t: ^$T/Tree($Value), node: ^Node(Value), call_on_remove: bool = true) -> bool {
|
||||
@@ -285,14 +281,14 @@ iterator_from_pos :: proc "contextless" (
|
||||
}
|
||||
|
||||
// iterator_get returns the node currently pointed to by the iterator,
|
||||
// or nil iff the node has been removed, the tree is empty, or the end
|
||||
// or nil if and only if (⟺) the node has been removed, the tree is empty, or the end
|
||||
// of the tree has been reached.
|
||||
iterator_get :: proc "contextless" (it: ^$I/Iterator($Value)) -> ^Node(Value) {
|
||||
return it._cur
|
||||
}
|
||||
|
||||
// iterator_remove removes the node currently pointed to by the iterator,
|
||||
// and returns true iff the removal was successful. Semantics are the
|
||||
// and returns true if and only if (⟺) the removal was successful. Semantics are the
|
||||
// same as the Tree remove.
|
||||
iterator_remove :: proc(it: ^$I/Iterator($Value), call_on_remove: bool = true) -> bool {
|
||||
if it._cur == nil {
|
||||
@@ -308,7 +304,7 @@ iterator_remove :: proc(it: ^$I/Iterator($Value), call_on_remove: bool = true) -
|
||||
}
|
||||
|
||||
// iterator_next advances the iterator and returns the (node, true) or
|
||||
// or (nil, false) iff the end of the tree has been reached.
|
||||
// or (nil, false) if and only if (⟺) the end of the tree has been reached.
|
||||
//
|
||||
// Note: The first call to iterator_next will return the first node instead
|
||||
// of advancing the iterator.
|
||||
|
||||
@@ -2,7 +2,6 @@ package container_dynamic_bit_array
|
||||
|
||||
import "base:builtin"
|
||||
import "base:intrinsics"
|
||||
import "core:mem"
|
||||
|
||||
/*
|
||||
Note that these constants are dependent on the backing being a u64.
|
||||
@@ -329,7 +328,7 @@ Inputs:
|
||||
*/
|
||||
clear :: proc(ba: ^Bit_Array) {
|
||||
if ba == nil { return }
|
||||
mem.zero_slice(ba.bits[:])
|
||||
intrinsics.mem_zero(raw_data(ba.bits), builtin.len(ba.bits) * NUM_BITS / 8)
|
||||
}
|
||||
/*
|
||||
Gets the length of set and unset valid bits in the Bit_Array.
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
/*
|
||||
A dynamically-sized array of bits.
|
||||
|
||||
The Bit Array can be used in several ways:
|
||||
|
||||
By default you don't need to instantiate a Bit Array.
|
||||
By default you don't need to instantiate a `Bit_Array`.
|
||||
Example:
|
||||
package test
|
||||
|
||||
@@ -18,11 +20,11 @@ Example:
|
||||
|
||||
// returns `false`, `false`, because this Bit Array wasn't created to allow negative indices.
|
||||
was_set, was_retrieved := get(&bits, -1)
|
||||
fmt.println(was_set, was_retrieved)
|
||||
fmt.println(was_set, was_retrieved)
|
||||
destroy(&bits)
|
||||
}
|
||||
|
||||
A Bit Array can optionally allow for negative indices, if the minimum value was given during creation.
|
||||
A `Bit_Array` can optionally allow for negative indices, if the minimum value was given during creation.
|
||||
Example:
|
||||
package test
|
||||
|
||||
@@ -49,4 +51,4 @@ Example:
|
||||
fmt.printf("Freed.\n")
|
||||
}
|
||||
*/
|
||||
package container_dynamic_bit_array
|
||||
package container_dynamic_bit_array
|
||||
60
core/container/handle_map/doc.odin
Normal file
60
core/container/handle_map/doc.odin
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
Handle-based map using either fixed-length arrays, or exponential arrays from "core:container/xar".
|
||||
|
||||
Example:
|
||||
import hm "core:container/handle_map"
|
||||
|
||||
Handle :: hm.Handle32
|
||||
|
||||
Entity :: struct {
|
||||
handle: Handle,
|
||||
pos: [2]f32,
|
||||
}
|
||||
|
||||
{ // static map
|
||||
entities: hm.Static_Handle_Map(1024, Entity, Handle)
|
||||
|
||||
h1 := hm.add(&entities, Entity{pos = {1, 4}})
|
||||
h2 := hm.add(&entities, Entity{pos = {9, 16}})
|
||||
|
||||
if e, ok := hm.get(&entities, h2); ok {
|
||||
e.pos.x += 32
|
||||
}
|
||||
|
||||
hm.remove(&entities, h1)
|
||||
|
||||
h3 := hm.add(&entities, Entity{pos = {6, 7}})
|
||||
assert(hm.is_valid(entities, h3))
|
||||
|
||||
it := hm.iterator_make(&entities)
|
||||
for e, h in hm.iterate(&it) {
|
||||
assert(hm.is_valid(entities, h))
|
||||
e.pos += {1, 2}
|
||||
}
|
||||
}
|
||||
|
||||
{ // dynamic map
|
||||
entities: hm.Dynamic_Handle_Map(Entity, Handle)
|
||||
hm.dynamic_init(&entities, context.allocator)
|
||||
defer hm.dynamic_destroy(&entities)
|
||||
|
||||
h1 := hm.add(&entities, Entity{pos = {1, 4}})
|
||||
h2 := hm.add(&entities, Entity{pos = {9, 16}})
|
||||
|
||||
if e, ok := hm.get(&entities, h2); ok {
|
||||
e.pos.x += 32
|
||||
}
|
||||
|
||||
hm.remove(&entities, h1)
|
||||
|
||||
h3 := hm.add(&entities, Entity{pos = {6, 7}})
|
||||
assert(hm.is_valid(entities, h3))
|
||||
|
||||
it := hm.iterator_make(&entities)
|
||||
for e, h in hm.iterate(&it) {
|
||||
assert(hm.is_valid(entities, h))
|
||||
e.pos += {1, 2}
|
||||
}
|
||||
}
|
||||
*/
|
||||
package container_handle_map
|
||||
142
core/container/handle_map/dynamic_handle_map.odin
Normal file
142
core/container/handle_map/dynamic_handle_map.odin
Normal file
@@ -0,0 +1,142 @@
|
||||
package container_handle_map
|
||||
|
||||
import "base:runtime"
|
||||
import "base:builtin"
|
||||
import "base:intrinsics"
|
||||
@(require) import "core:container/xar"
|
||||
|
||||
Dynamic_Handle_Map :: struct($T: typeid, $Handle_Type: typeid)
|
||||
where
|
||||
intrinsics.type_has_field(Handle_Type, "idx"),
|
||||
intrinsics.type_has_field(Handle_Type, "gen"),
|
||||
intrinsics.type_is_unsigned(intrinsics.type_field_type(Handle_Type, "idx")),
|
||||
intrinsics.type_is_unsigned(intrinsics.type_field_type(Handle_Type, "gen")),
|
||||
intrinsics.type_field_type(Handle_Type, "idx") == intrinsics.type_field_type(Handle_Type, "gen"),
|
||||
|
||||
intrinsics.type_has_field (T, "handle"),
|
||||
intrinsics.type_field_type(T, "handle") == Handle_Type {
|
||||
|
||||
items: xar.Array(T, 4),
|
||||
unused_items: xar.Array(u32, 4),
|
||||
}
|
||||
|
||||
dynamic_init :: proc(m: ^$D/Dynamic_Handle_Map($T, $Handle_Type), allocator: runtime.Allocator) {
|
||||
xar.init(&m.items, allocator)
|
||||
xar.init(&m.unused_items, allocator)
|
||||
}
|
||||
|
||||
dynamic_destroy :: proc(m: ^$D/Dynamic_Handle_Map($T, $Handle_Type)) {
|
||||
xar.destroy(&m.unused_items)
|
||||
xar.destroy(&m.items)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
dynamic_add :: proc(m: ^$D/Dynamic_Handle_Map($T, $Handle_Type), item: T, loc := #caller_location) -> (handle: Handle_Type, err: runtime.Allocator_Error) #optional_allocator_error {
|
||||
if xar.len(m.unused_items) > 0 {
|
||||
i := xar.pop(&m.unused_items)
|
||||
ptr := xar.get_ptr_unsafe(&m.items, i)
|
||||
prev_gen := ptr.handle.gen
|
||||
ptr^ = item
|
||||
|
||||
ptr.handle.idx = auto_cast i
|
||||
ptr.handle.gen = auto_cast (prev_gen + 1)
|
||||
return ptr.handle, nil
|
||||
}
|
||||
|
||||
if xar.len(m.items) == 0 {
|
||||
// initialize the zero-value sentinel
|
||||
xar.append(&m.items, T{}, loc) or_return
|
||||
}
|
||||
|
||||
_ = xar.append(&m.items, item, loc) or_return
|
||||
i := xar.len(m.items)-1
|
||||
|
||||
ptr := xar.get_ptr_unsafe(&m.items, i)
|
||||
|
||||
ptr.handle.idx = auto_cast i
|
||||
ptr.handle.gen = 1
|
||||
return ptr.handle, nil
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
dynamic_get :: proc "contextless" (m: ^$D/Dynamic_Handle_Map($T, $Handle_Type), h: Handle_Type) -> (^T, bool) #optional_ok {
|
||||
if h.idx <= 0 || int(u32(h.idx)) >= xar.len(m.items) {
|
||||
return nil, false
|
||||
}
|
||||
if e := xar.get_ptr_unsafe(&m.items, h.idx); e.handle == h {
|
||||
return e, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
dynamic_remove :: proc(m: ^$D/Dynamic_Handle_Map($T, $Handle_Type), h: Handle_Type, loc := #caller_location) -> (found: bool, err: runtime.Allocator_Error) {
|
||||
if h.idx <= 0 || int(u32(h.idx)) >= xar.len(m.items) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if item := xar.get_ptr(&m.items, h.idx); item.handle == h {
|
||||
xar.append(&m.unused_items, u32(h.idx), loc) or_return
|
||||
item.handle.idx = 0
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
dynamic_is_valid :: proc "contextless" (m: ^$D/Dynamic_Handle_Map($T, $Handle_Type), h: Handle_Type) -> bool {
|
||||
return h.idx > 0 && int(u32(h.idx)) < xar.len(m.items) && xar.get_ptr_unsafe(&m.items, h.idx).handle == h
|
||||
}
|
||||
|
||||
// Returns the number of possibly valid items in the handle map.
|
||||
@(require_results)
|
||||
dynamic_len :: proc "contextless" (m: $D/Dynamic_Handle_Map($T, $Handle_Type)) -> uint {
|
||||
n := xar.len(m.items) - xar.len(m.unused_items)
|
||||
return uint(n-1 if n > 0 else 0)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
dynamic_cap :: proc "contextless" (m: $D/Dynamic_Handle_Map($T, $Handle_Type)) -> uint {
|
||||
n := xar.cap(m.items)
|
||||
return uint(n-1 if n > 0 else 0)
|
||||
}
|
||||
|
||||
dynamic_clear :: proc "contextless" (m: ^$D/Dynamic_Handle_Map($T, $Handle_Type)) {
|
||||
xar.clear(&m.items)
|
||||
xar.clear(&m.unused_items)
|
||||
}
|
||||
|
||||
|
||||
// An iterator for a handle map.
|
||||
Dynamic_Handle_Map_Iterator :: struct($D: typeid) {
|
||||
m: ^D,
|
||||
index: int,
|
||||
}
|
||||
|
||||
// Makes an iterator from a handle map.
|
||||
@(require_results)
|
||||
dynamic_iterator_make :: proc "contextless" (m: ^$D/Dynamic_Handle_Map($T, $Handle_Type)) -> Dynamic_Handle_Map_Iterator(D) {
|
||||
return {m, 1}
|
||||
}
|
||||
|
||||
/*
|
||||
Iterate over a handle map. It will skip over unused item slots (e.g. handle.idx == 0).
|
||||
Usage:
|
||||
it := hm.dynamic_iterator_make(&the_dynamic_handle_map)
|
||||
for item, handle in hm.iterate(&it) {
|
||||
...
|
||||
}
|
||||
*/
|
||||
@(require_results)
|
||||
dynamic_iterate :: proc "contextless" (it: ^$DHI/Dynamic_Handle_Map_Iterator($D/Dynamic_Handle_Map($T, $Handle_Type))) -> (val: ^T, h: Handle_Type, ok: bool) {
|
||||
for _ in it.index..<xar.len(it.m.items) {
|
||||
e := xar.get_ptr_unsafe(&it.m.items, it.index)
|
||||
it.index += 1
|
||||
|
||||
if e.handle.idx != 0 {
|
||||
return e, e.handle, true
|
||||
}
|
||||
}
|
||||
it.index = 0
|
||||
return
|
||||
}
|
||||
222
core/container/handle_map/static_handle_map.odin
Normal file
222
core/container/handle_map/static_handle_map.odin
Normal file
@@ -0,0 +1,222 @@
|
||||
package container_handle_map
|
||||
|
||||
import "base:builtin"
|
||||
import "base:intrinsics"
|
||||
|
||||
// Default 16-bit Handle type which can be used for handle maps which only need a maximum of 254 (1<<8 - 2) items
|
||||
Handle16 :: struct {
|
||||
idx: u8,
|
||||
gen: u8,
|
||||
}
|
||||
|
||||
// Default 32-bit Handle type which can be used for handle maps which only need a maximum of 65534 (1<<16 - 2) items
|
||||
Handle32 :: struct {
|
||||
idx: u16,
|
||||
gen: u16,
|
||||
}
|
||||
|
||||
// Default 64-bit Handle type which can be used for handle maps which only need a maximum of 4294967294 (1<<32 - 2) items
|
||||
Handle64 :: struct {
|
||||
idx: u32,
|
||||
gen: u32,
|
||||
}
|
||||
|
||||
Static_Handle_Map :: struct($N: uint, $T: typeid, $Handle_Type: typeid)
|
||||
where
|
||||
0 < N, N < uint(1<<31 - 1),
|
||||
|
||||
intrinsics.type_has_field(Handle_Type, "idx"),
|
||||
intrinsics.type_has_field(Handle_Type, "gen"),
|
||||
intrinsics.type_is_unsigned(intrinsics.type_field_type(Handle_Type, "idx")),
|
||||
intrinsics.type_is_unsigned(intrinsics.type_field_type(Handle_Type, "gen")),
|
||||
intrinsics.type_field_type(Handle_Type, "idx") == intrinsics.type_field_type(Handle_Type, "gen"),
|
||||
|
||||
N < uint(max(intrinsics.type_field_type(Handle_Type, "idx"))),
|
||||
|
||||
intrinsics.type_has_field (T, "handle"),
|
||||
intrinsics.type_field_type(T, "handle") == Handle_Type {
|
||||
|
||||
// The zero element represent a zero-value sentinel (dummy value), allowing for `idx == 0` to mean a no-handle.
|
||||
// This means the capacity is actually N-1 items.
|
||||
items: [N]T,
|
||||
|
||||
used_len: u32, // How many of the items are in use
|
||||
unused_len: u32, // Use to calculate the number of valid items
|
||||
unused_items: [N]u32,
|
||||
next_unused: u32,
|
||||
}
|
||||
|
||||
|
||||
// `add` a value of type `T` to the handle map. This will return a pointer to the item and an optional boolean to check for validity.
|
||||
@(require_results)
|
||||
static_add :: proc "contextless" (m: ^$H/Static_Handle_Map($N, $T, $Handle_Type), item: T) -> (handle: Handle_Type, ok: bool) #optional_ok {
|
||||
if i := m.next_unused; i != 0 {
|
||||
ptr := &m.items[i]
|
||||
|
||||
m.next_unused = m.unused_items[i]
|
||||
m.unused_items[i] = 0
|
||||
|
||||
prev_gen := ptr.handle.gen
|
||||
ptr^ = item
|
||||
|
||||
ptr.handle.idx = auto_cast i
|
||||
ptr.handle.gen = auto_cast (prev_gen + 1)
|
||||
m.unused_len -= 1
|
||||
return ptr.handle, true
|
||||
}
|
||||
|
||||
if m.used_len == 0 {
|
||||
// initialize the zero-value sentinel
|
||||
m.items[0] = {}
|
||||
m.used_len += 1
|
||||
}
|
||||
|
||||
if m.used_len == builtin.len(m.items) {
|
||||
return {}, false
|
||||
}
|
||||
|
||||
ptr := &m.items[m.used_len]
|
||||
ptr^ = item
|
||||
|
||||
ptr.handle.idx = auto_cast m.used_len
|
||||
ptr.handle.gen = 1
|
||||
m.used_len += 1
|
||||
return ptr.handle, true
|
||||
}
|
||||
|
||||
// `get` a stable pointer of type `^T` by resolving the handle `h`. If the handle is not valid, then `nil, false` is returned.
|
||||
@(require_results)
|
||||
static_get :: proc "contextless" (m: ^$H/Static_Handle_Map($N, $T, $Handle_Type), h: Handle_Type) -> (^T, bool) #optional_ok {
|
||||
if h.idx <= 0 || u32(h.idx) >= m.used_len {
|
||||
return nil, false
|
||||
}
|
||||
if e := &m.items[h.idx]; e.handle == h {
|
||||
return e, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// `remove` an item from the handle map from the handle `h`.
|
||||
static_remove :: proc "contextless" (m: ^$H/Static_Handle_Map($N, $T, $Handle_Type), h: Handle_Type) -> bool {
|
||||
if h.idx <= 0 || u32(h.idx) >= m.used_len {
|
||||
return false
|
||||
}
|
||||
|
||||
if item := &m.items[h.idx]; item.handle == h {
|
||||
m.unused_items[h.idx] = m.next_unused
|
||||
m.next_unused = u32(h.idx)
|
||||
m.unused_len += 1
|
||||
item.handle.idx = 0
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Returns true when the handle `h` is valid relating to the handle map.
|
||||
@(require_results)
|
||||
static_is_valid :: proc "contextless" (m: $H/Static_Handle_Map($N, $T, $Handle_Type), h: Handle_Type) -> bool {
|
||||
return h.idx > 0 && u32(h.idx) < m.used_len && m.items[h.idx].handle == h
|
||||
}
|
||||
|
||||
// Returns the number of possibly valid items in the handle map.
|
||||
@(require_results)
|
||||
static_len :: proc "contextless" (m: $H/Static_Handle_Map($N, $T, $Handle_Type)) -> uint {
|
||||
n := uint(m.used_len) - uint(m.unused_len)
|
||||
return n-1 if n > 0 else 0
|
||||
}
|
||||
|
||||
// Returns the capacity of the items in a handle map.
|
||||
// This is equivalent to `N-1` as the zero value is reserved for the zero-value sentinel.
|
||||
@(require_results)
|
||||
static_cap :: proc "contextless" (m: $H/Static_Handle_Map($N, $T, $Handle_Type)) -> uint {
|
||||
// We could just return `N` but I am doing this for clarity
|
||||
return builtin.len(m.items)-1
|
||||
}
|
||||
|
||||
// `clear` the handle map by zeroing all of the memory.
|
||||
// Internally this does not do `m^ = {}` but rather uses `intrinsics.mem_zero` explicitly improve performance.
|
||||
static_clear :: proc "contextless" (m: ^$H/Static_Handle_Map($N, $T, $Handle_Type)) {
|
||||
intrinsics.mem_zero(m, size_of(m^))
|
||||
}
|
||||
|
||||
// An iterator for a handle map.
|
||||
Static_Handle_Map_Iterator :: struct($H: typeid) {
|
||||
m: ^H,
|
||||
index: u32,
|
||||
}
|
||||
|
||||
// Makes an iterator from a handle map.
|
||||
@(require_results)
|
||||
static_iterator_make :: proc "contextless" (m: ^$H/Static_Handle_Map($N, $T, $Handle_Type)) -> Static_Handle_Map_Iterator(H) {
|
||||
return {m, 1}
|
||||
}
|
||||
|
||||
/*
|
||||
Iterate over a handle map. It will skip over unused item slots (e.g. handle.idx == 0).
|
||||
Usage:
|
||||
it := hm.iterator_make(&the_handle_map)
|
||||
for item, handle in hm.iterate(&it) {
|
||||
...
|
||||
}
|
||||
*/
|
||||
@(require_results)
|
||||
static_iterate :: proc "contextless" (it: ^$HI/Static_Handle_Map_Iterator($H/Static_Handle_Map($N, $T, $Handle_Type))) -> (val: ^T, h: Handle_Type, ok: bool) {
|
||||
for _ in it.index..<it.m.used_len {
|
||||
e := &it.m.items[it.index]
|
||||
it.index += 1
|
||||
|
||||
if e.handle.idx != 0 {
|
||||
return e, e.handle, true
|
||||
}
|
||||
}
|
||||
it.index = 0
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
add :: proc{
|
||||
static_add,
|
||||
dynamic_add,
|
||||
}
|
||||
|
||||
get :: proc{
|
||||
static_get,
|
||||
dynamic_get,
|
||||
}
|
||||
|
||||
remove :: proc{
|
||||
static_remove,
|
||||
dynamic_remove,
|
||||
}
|
||||
|
||||
is_valid :: proc{
|
||||
static_is_valid,
|
||||
dynamic_is_valid,
|
||||
}
|
||||
|
||||
len :: proc{
|
||||
static_len,
|
||||
dynamic_len,
|
||||
}
|
||||
|
||||
cap :: proc{
|
||||
static_cap,
|
||||
dynamic_cap,
|
||||
}
|
||||
|
||||
clear :: proc{
|
||||
static_clear,
|
||||
dynamic_clear,
|
||||
}
|
||||
|
||||
iterator_make :: proc{
|
||||
static_iterator_make,
|
||||
dynamic_iterator_make,
|
||||
}
|
||||
|
||||
iterate :: proc{
|
||||
static_iterate,
|
||||
dynamic_iterate,
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Package list implements an intrusive doubly-linked list.
|
||||
An intrusive doubly-linked list.
|
||||
|
||||
An intrusive container requires a `Node` to be embedded in your own structure, like this.
|
||||
The intrusive container requires a `Node` to be embedded in your own structure, like this.
|
||||
Example:
|
||||
My_String :: struct {
|
||||
node: list.Node,
|
||||
@@ -46,4 +46,4 @@ Output:
|
||||
Hello
|
||||
World
|
||||
*/
|
||||
package container_intrusive_list
|
||||
package container_intrusive_list
|
||||
@@ -1,3 +1,4 @@
|
||||
// A least-recently-used (`LRU`) cache. It automatically removes older entries if its capacity is reached.
|
||||
package container_lru
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
146
core/container/pool/pool.odin
Normal file
146
core/container/pool/pool.odin
Normal file
@@ -0,0 +1,146 @@
|
||||
package container_pool
|
||||
|
||||
import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
import "base:sanitizer"
|
||||
import "core:sync"
|
||||
|
||||
_ :: sanitizer
|
||||
_ :: sync
|
||||
|
||||
DEFAULT_BLOCK_SIZE :: _DEFAULT_BLOCK_SIZE
|
||||
|
||||
Pool_Arena :: _Pool_Arena
|
||||
|
||||
/*
|
||||
A thread-safe (between init and destroy) object pool backed by virtual growing arena returning stable pointers.
|
||||
The element type requires an intrusive link node.
|
||||
|
||||
Example:
|
||||
Elem :: struct {
|
||||
link: ^Elem,
|
||||
}
|
||||
|
||||
p: pool.Pool(Elem)
|
||||
pool.init(&p, "link")
|
||||
*/
|
||||
Pool :: struct($T: typeid) {
|
||||
arena: Pool_Arena,
|
||||
num_outstanding: int,
|
||||
num_ready: int,
|
||||
link_off: uintptr,
|
||||
// Guards free_list. An untagged Treiber stack is vulnerable to ABA:
|
||||
// between get's head load and its CAS, another thread can pop the head,
|
||||
// reuse it, and push it back — the CAS then succeeds and installs a
|
||||
// stale next pointer, handing an in-flight element to two owners.
|
||||
// A tagged head would keep this lock-free but requires a double-width
|
||||
// CAS, which is not portably available.
|
||||
mu: sync.Mutex,
|
||||
free_list: ^T,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
init :: proc(p: ^Pool($T), $link_field: string, block_size: uint = DEFAULT_BLOCK_SIZE) -> (err: runtime.Allocator_Error)
|
||||
where intrinsics.type_has_field(T, link_field),
|
||||
intrinsics.type_field_type(T, link_field) == ^T {
|
||||
p.link_off = offset_of_by_string(T, link_field)
|
||||
return _pool_arena_init(&p.arena, block_size)
|
||||
}
|
||||
|
||||
destroy :: proc(p: ^Pool($T)) {
|
||||
elem := sync.atomic_exchange_explicit(&p.free_list, nil, .Acquire)
|
||||
|
||||
sync.atomic_store_explicit(&p.num_ready, 0, .Relaxed)
|
||||
|
||||
when .Address in ODIN_SANITIZER_FLAGS {
|
||||
for ; elem != nil; elem = _get_next(p, elem) {
|
||||
_unpoison_elem(p, elem)
|
||||
}
|
||||
} else {
|
||||
_ = elem
|
||||
}
|
||||
|
||||
_pool_arena_destroy(&p.arena)
|
||||
p.arena = {}
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get :: proc(p: ^Pool($T)) -> (elem: ^T, err: runtime.Allocator_Error) #optional_allocator_error {
|
||||
defer sync.atomic_add_explicit(&p.num_outstanding, 1, .Relaxed)
|
||||
|
||||
sync.mutex_lock(&p.mu)
|
||||
elem = p.free_list
|
||||
if elem == nil {
|
||||
sync.mutex_unlock(&p.mu)
|
||||
// NOTE: pool arena has an internal lock.
|
||||
return new(T, _pool_arena_allocator(&p.arena))
|
||||
}
|
||||
p.free_list = _get_next(p, elem)
|
||||
sync.mutex_unlock(&p.mu)
|
||||
|
||||
_set_next(p, elem, nil)
|
||||
_unpoison_elem(p, elem)
|
||||
sync.atomic_sub_explicit(&p.num_ready, 1, .Relaxed)
|
||||
return
|
||||
}
|
||||
|
||||
put :: proc(p: ^Pool($T), elem: ^T) {
|
||||
intrinsics.mem_zero(elem, size_of(T))
|
||||
_poison_elem(p, elem)
|
||||
|
||||
defer sync.atomic_sub_explicit(&p.num_outstanding, 1, .Relaxed)
|
||||
defer sync.atomic_add_explicit(&p.num_ready, 1, .Relaxed)
|
||||
|
||||
sync.mutex_lock(&p.mu)
|
||||
_set_next(p, elem, p.free_list)
|
||||
p.free_list = elem
|
||||
sync.mutex_unlock(&p.mu)
|
||||
}
|
||||
|
||||
num_outstanding :: proc(p: ^Pool($T)) -> int {
|
||||
return sync.atomic_load(&p.num_outstanding)
|
||||
}
|
||||
|
||||
num_ready :: proc(p: ^Pool($T)) -> int {
|
||||
return sync.atomic_load(&p.num_ready)
|
||||
}
|
||||
|
||||
cap :: proc(p: ^Pool($T)) -> int {
|
||||
return sync.atomic_load(&p.num_ready) + sync.atomic_load(&p.num_outstanding)
|
||||
}
|
||||
|
||||
_get_next :: proc(p: ^Pool($T), elem: ^T) -> ^T {
|
||||
return (^^T)(uintptr(elem) + p.link_off)^
|
||||
}
|
||||
|
||||
_set_next :: proc(p: ^Pool($T), elem: ^T, next: ^T) {
|
||||
(^^T)(uintptr(elem) + p.link_off)^ = next
|
||||
}
|
||||
|
||||
_poison_elem :: proc(p: ^Pool($T), elem: ^T) {
|
||||
when .Address in ODIN_SANITIZER_FLAGS {
|
||||
if p.link_off > 0 {
|
||||
sanitizer.address_poison_rawptr(elem, int(p.link_off))
|
||||
}
|
||||
|
||||
len := size_of(T) - p.link_off - size_of(rawptr)
|
||||
if len > 0 {
|
||||
ptr := rawptr(uintptr(elem) + p.link_off + size_of(rawptr))
|
||||
sanitizer.address_poison_rawptr(ptr, int(len))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_unpoison_elem :: proc(p: ^Pool($T), elem: ^T) {
|
||||
when .Address in ODIN_SANITIZER_FLAGS {
|
||||
if p.link_off > 0 {
|
||||
sanitizer.address_unpoison_rawptr(elem, int(p.link_off))
|
||||
}
|
||||
|
||||
len := size_of(T) - p.link_off - size_of(rawptr)
|
||||
if len > 0 {
|
||||
ptr := rawptr(uintptr(elem) + p.link_off + size_of(rawptr))
|
||||
sanitizer.address_unpoison_rawptr(ptr, int(len))
|
||||
}
|
||||
}
|
||||
}
|
||||
27
core/container/pool/pool_arena_others.odin
Normal file
27
core/container/pool/pool_arena_others.odin
Normal file
@@ -0,0 +1,27 @@
|
||||
#+build !darwin
|
||||
#+build !freebsd
|
||||
#+build !openbsd
|
||||
#+build !netbsd
|
||||
#+build !linux
|
||||
#+build !windows
|
||||
#+private
|
||||
package container_pool
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
_Pool_Arena :: runtime.Arena
|
||||
|
||||
_DEFAULT_BLOCK_SIZE :: runtime.Megabyte
|
||||
|
||||
_pool_arena_init :: proc(arena: ^Pool_Arena, block_size: uint = DEFAULT_BLOCK_SIZE) -> (err: runtime.Allocator_Error) {
|
||||
runtime.arena_init(arena, block_size, runtime.default_allocator()) or_return
|
||||
return
|
||||
}
|
||||
|
||||
_pool_arena_allocator :: proc(arena: ^Pool_Arena) -> runtime.Allocator {
|
||||
return runtime.arena_allocator(arena)
|
||||
}
|
||||
|
||||
_pool_arena_destroy :: proc(arena: ^Pool_Arena) {
|
||||
runtime.arena_destroy(arena)
|
||||
}
|
||||
22
core/container/pool/pool_arena_virtual.odin
Normal file
22
core/container/pool/pool_arena_virtual.odin
Normal file
@@ -0,0 +1,22 @@
|
||||
#+build darwin, freebsd, openbsd, netbsd, linux, windows
|
||||
package container_pool
|
||||
|
||||
import "base:runtime"
|
||||
import "core:mem/virtual"
|
||||
|
||||
_Pool_Arena :: virtual.Arena
|
||||
|
||||
_DEFAULT_BLOCK_SIZE :: runtime.Gigabyte
|
||||
|
||||
_pool_arena_init :: proc(arena: ^Pool_Arena, block_size: uint = DEFAULT_BLOCK_SIZE) -> (err: runtime.Allocator_Error) {
|
||||
virtual.arena_init_growing(arena, block_size) or_return
|
||||
return
|
||||
}
|
||||
|
||||
_pool_arena_allocator :: proc(arena: ^Pool_Arena) -> runtime.Allocator {
|
||||
return virtual.arena_allocator(arena)
|
||||
}
|
||||
|
||||
_pool_arena_destroy :: proc(arena: ^Pool_Arena) {
|
||||
virtual.arena_destroy(arena)
|
||||
}
|
||||
53
core/container/priority_queue/doc.odin
Normal file
53
core/container/priority_queue/doc.odin
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
A priority queue data structure.
|
||||
|
||||
Important: It needs to be initialized with `less` and `swap` procedures, see `init` and `init_from_dynamic_array`.
|
||||
|
||||
Example:
|
||||
import "base:runtime"
|
||||
import pq "core:container/priority_queue"
|
||||
|
||||
main :: proc() {
|
||||
Printer_Job :: struct {
|
||||
user_id: u64,
|
||||
weight: enum u8 {Highest, High, Normal, Low, Idle},
|
||||
}
|
||||
|
||||
q: pq.Priority_Queue(Printer_Job)
|
||||
pq.init(
|
||||
pq = &q,
|
||||
less = proc(a, b: Printer_Job) -> bool {
|
||||
// Jobs will be sorted in order of increasing weight
|
||||
return a.weight < b.weight
|
||||
},
|
||||
swap = pq.default_swap_proc(Printer_Job),
|
||||
)
|
||||
defer pq.destroy(&q)
|
||||
|
||||
// Add jobs with random weights
|
||||
for _ in 0..<100 {
|
||||
job: Printer_Job = ---
|
||||
assert(runtime.random_generator_read_ptr(context.random_generator, &job, size_of(job)))
|
||||
pq.push(&q, job)
|
||||
}
|
||||
|
||||
// Drain jobs in order of importance
|
||||
last: Printer_Job
|
||||
for pq.len(q) > 0 {
|
||||
v := pq.pop(&q)
|
||||
assert(v.weight >= last.weight)
|
||||
last = v
|
||||
}
|
||||
|
||||
// Queue empty?
|
||||
assert(pq.len(q) == 0)
|
||||
|
||||
// Add one more job
|
||||
pq.push(&q, Printer_Job{user_id = 42, weight = .Idle})
|
||||
|
||||
// Cancel all jobs
|
||||
pq.clear(&q)
|
||||
assert(pq.len(q) == 0)
|
||||
}
|
||||
*/
|
||||
package container_priority_queue
|
||||
@@ -3,6 +3,10 @@ package container_priority_queue
|
||||
import "base:builtin"
|
||||
import "base:runtime"
|
||||
|
||||
// Priority Queue.
|
||||
//
|
||||
// Important: It needs to be initialized with `less` and `swap` procedures, see `init` and `init_from_dynamic_array`.
|
||||
// See `doc.odin` for an example.
|
||||
Priority_Queue :: struct($T: typeid) {
|
||||
queue: [dynamic]T,
|
||||
|
||||
@@ -133,12 +137,10 @@ pop_safe :: proc(pq: ^$Q/Priority_Queue($T), loc := #caller_location) -> (value:
|
||||
remove :: proc(pq: ^$Q/Priority_Queue($T), i: int) -> (value: T, ok: bool) {
|
||||
n := builtin.len(pq.queue)
|
||||
if 0 <= i && i < n {
|
||||
if n != i {
|
||||
pq.swap(pq.queue[:], i, n)
|
||||
_shift_down(pq, i, n)
|
||||
_shift_up(pq, i)
|
||||
}
|
||||
value, ok = builtin.pop_safe(&pq.queue)
|
||||
pq.swap(pq.queue[:], i, n-1)
|
||||
_shift_down(pq, i, n-1)
|
||||
_shift_up(pq, i)
|
||||
value, ok = builtin.pop(&pq.queue), true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
// A dynamically resizable double-ended queue/ring-buffer.
|
||||
package container_queue
|
||||
|
||||
import "base:builtin"
|
||||
import "base:runtime"
|
||||
_ :: runtime
|
||||
|
||||
// Dynamically resizable double-ended queue/ring-buffer
|
||||
/*
|
||||
`Queue` is a dynamically resizable double-ended queue/ring-buffer.
|
||||
|
||||
Being double-ended means that either end may be pushed onto or popped from
|
||||
across the same block of memory, in any order, thus providing both stack and
|
||||
queue-like behaviors in the same data structure.
|
||||
*/
|
||||
Queue :: struct($T: typeid) {
|
||||
data: [dynamic]T,
|
||||
len: uint,
|
||||
@@ -13,18 +20,31 @@ Queue :: struct($T: typeid) {
|
||||
|
||||
DEFAULT_CAPACITY :: 16
|
||||
|
||||
// Procedure to initialize a queue
|
||||
init :: proc(q: ^$Q/Queue($T), capacity := DEFAULT_CAPACITY, allocator := context.allocator) -> runtime.Allocator_Error {
|
||||
if q.data.allocator.procedure == nil {
|
||||
q.data.allocator = allocator
|
||||
}
|
||||
/*
|
||||
Initialize a `Queue` with a starting `capacity` and an `allocator`.
|
||||
*/
|
||||
init :: proc(q: ^$Q/Queue($T), capacity := DEFAULT_CAPACITY, allocator := context.allocator, loc := #caller_location) -> runtime.Allocator_Error {
|
||||
clear(q)
|
||||
return reserve(q, capacity)
|
||||
q.data = transmute([dynamic]T)runtime.Raw_Dynamic_Array{
|
||||
data = nil,
|
||||
len = 0,
|
||||
cap = 0,
|
||||
allocator = allocator,
|
||||
}
|
||||
return reserve(q, capacity, loc)
|
||||
}
|
||||
|
||||
// Procedure to initialize a queue from a fixed backing slice.
|
||||
// The contents of the `backing` will be overwritten as items are pushed onto the `Queue`.
|
||||
// Any previous contents are not available.
|
||||
/*
|
||||
Initialize a `Queue` from a fixed `backing` slice into which modifications are
|
||||
made directly.
|
||||
|
||||
The contents of the `backing` will be overwritten as items are pushed onto the
|
||||
`Queue`. Any previous contents will not be available through the API but are
|
||||
not explicitly zeroed either.
|
||||
|
||||
Note that procedures which need space to work (`push_back`, ...) will fail if
|
||||
the backing slice runs out of space.
|
||||
*/
|
||||
init_from_slice :: proc(q: ^$Q/Queue($T), backing: []T) -> bool {
|
||||
clear(q)
|
||||
q.data = transmute([dynamic]T)runtime.Raw_Dynamic_Array{
|
||||
@@ -36,8 +56,14 @@ init_from_slice :: proc(q: ^$Q/Queue($T), backing: []T) -> bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Procedure to initialize a queue from a fixed backing slice.
|
||||
// Existing contents are preserved and available on the queue.
|
||||
/*
|
||||
Initialize a `Queue` from a fixed `backing` slice into which modifications are
|
||||
made directly.
|
||||
|
||||
The contents of the queue will start out with all of the elements in `backing`,
|
||||
effectively creating a full queue from the slice. As such, no procedures will
|
||||
be able to add more elements to the queue until some are taken off.
|
||||
*/
|
||||
init_with_contents :: proc(q: ^$Q/Queue($T), backing: []T) -> bool {
|
||||
clear(q)
|
||||
q.data = transmute([dynamic]T)runtime.Raw_Dynamic_Array{
|
||||
@@ -50,87 +76,193 @@ init_with_contents :: proc(q: ^$Q/Queue($T), backing: []T) -> bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Procedure to destroy a queue
|
||||
/*
|
||||
Delete memory that has been dynamically allocated from a `Queue` that was setup with `init`.
|
||||
|
||||
Note that this procedure should not be used on queues setup with
|
||||
`init_from_slice` or `init_with_contents`, as neither of those procedures keep
|
||||
track of the allocator state of the underlying `backing` slice.
|
||||
*/
|
||||
destroy :: proc(q: ^$Q/Queue($T)) {
|
||||
delete(q.data)
|
||||
}
|
||||
|
||||
// The length of the queue
|
||||
/*
|
||||
Return the length of the queue.
|
||||
*/
|
||||
len :: proc(q: $Q/Queue($T)) -> int {
|
||||
return int(q.len)
|
||||
}
|
||||
|
||||
// The current capacity of the queue
|
||||
/*
|
||||
Return the capacity of the queue.
|
||||
*/
|
||||
cap :: proc(q: $Q/Queue($T)) -> int {
|
||||
return builtin.len(q.data)
|
||||
}
|
||||
|
||||
// Remaining space in the queue (cap-len)
|
||||
/*
|
||||
Return the remaining space in the queue.
|
||||
|
||||
This will be `cap() - len()`.
|
||||
*/
|
||||
space :: proc(q: $Q/Queue($T)) -> int {
|
||||
return builtin.len(q.data) - int(q.len)
|
||||
}
|
||||
|
||||
// Reserve enough space for at least the specified capacity
|
||||
reserve :: proc(q: ^$Q/Queue($T), capacity: int) -> runtime.Allocator_Error {
|
||||
/*
|
||||
Reserve enough space in the queue for at least the specified capacity.
|
||||
|
||||
This may return an error if allocation failed.
|
||||
*/
|
||||
reserve :: proc(q: ^$Q/Queue($T), capacity: int, loc := #caller_location) -> runtime.Allocator_Error {
|
||||
if capacity > space(q^) {
|
||||
return _grow(q, uint(capacity))
|
||||
return _grow(q, uint(capacity), loc)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
Shrink a queue's dynamically allocated array.
|
||||
|
||||
This has no effect if the queue was initialized with a backing slice.
|
||||
*/
|
||||
shrink :: proc(q: ^$Q/Queue($T), temp_allocator := context.temp_allocator, loc := #caller_location) {
|
||||
if q.data.allocator.procedure == runtime.nil_allocator_proc {
|
||||
return
|
||||
}
|
||||
|
||||
if q.len > 0 && q.offset > 0 {
|
||||
// Make the array contiguous again.
|
||||
buffer := make([]T, q.len, temp_allocator)
|
||||
defer delete(buffer, temp_allocator)
|
||||
|
||||
right := uint(builtin.len(q.data)) - q.offset
|
||||
copy(buffer[:], q.data[q.offset:])
|
||||
copy(buffer[right:], q.data[:q.offset])
|
||||
|
||||
copy(q.data[:], buffer[:])
|
||||
|
||||
q.offset = 0
|
||||
}
|
||||
|
||||
builtin.shrink(&q.data, q.len, loc)
|
||||
}
|
||||
|
||||
/*
|
||||
Get the element at index `i`.
|
||||
|
||||
This will raise a bounds checking error if `i` is an invalid index.
|
||||
*/
|
||||
get :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> T {
|
||||
runtime.bounds_check_error_loc(loc, i, builtin.len(q.data))
|
||||
runtime.bounds_check_error_loc(loc, i, int(q.len))
|
||||
|
||||
idx := (uint(i)+q.offset)%builtin.len(q.data)
|
||||
return q.data[idx]
|
||||
}
|
||||
|
||||
front :: proc(q: ^$Q/Queue($T)) -> T {
|
||||
return q.data[q.offset]
|
||||
}
|
||||
front_ptr :: proc(q: ^$Q/Queue($T)) -> ^T {
|
||||
return &q.data[q.offset]
|
||||
}
|
||||
/*
|
||||
Get a pointer to the element at index `i`.
|
||||
|
||||
back :: proc(q: ^$Q/Queue($T)) -> T {
|
||||
idx := (q.offset+uint(q.len - 1))%builtin.len(q.data)
|
||||
return q.data[idx]
|
||||
}
|
||||
back_ptr :: proc(q: ^$Q/Queue($T)) -> ^T {
|
||||
idx := (q.offset+uint(q.len - 1))%builtin.len(q.data)
|
||||
This will raise a bounds checking error if `i` is an invalid index.
|
||||
*/
|
||||
get_ptr :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> ^T {
|
||||
runtime.bounds_check_error_loc(loc, i, int(q.len))
|
||||
|
||||
idx := (uint(i)+q.offset)%builtin.len(q.data)
|
||||
return &q.data[idx]
|
||||
}
|
||||
|
||||
/*
|
||||
Set the element at index `i` to `val`.
|
||||
|
||||
This will raise a bounds checking error if `i` is an invalid index.
|
||||
*/
|
||||
set :: proc(q: ^$Q/Queue($T), #any_int i: int, val: T, loc := #caller_location) {
|
||||
runtime.bounds_check_error_loc(loc, i, builtin.len(q.data))
|
||||
|
||||
runtime.bounds_check_error_loc(loc, i, int(q.len))
|
||||
|
||||
idx := (uint(i)+q.offset)%builtin.len(q.data)
|
||||
q.data[idx] = val
|
||||
}
|
||||
get_ptr :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> ^T {
|
||||
runtime.bounds_check_error_loc(loc, i, builtin.len(q.data))
|
||||
|
||||
idx := (uint(i)+q.offset)%builtin.len(q.data)
|
||||
|
||||
/*
|
||||
Get the element at the front of the queue.
|
||||
|
||||
This will raise a bounds checking error if the queue is empty.
|
||||
*/
|
||||
front :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> T {
|
||||
when !ODIN_NO_BOUNDS_CHECK {
|
||||
ensure(q.len > 0, "Queue is empty.", loc)
|
||||
}
|
||||
return q.data[q.offset]
|
||||
}
|
||||
|
||||
/*
|
||||
Get a pointer to the element at the front of the queue.
|
||||
|
||||
This will raise a bounds checking error if the queue is empty.
|
||||
*/
|
||||
front_ptr :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> ^T {
|
||||
when !ODIN_NO_BOUNDS_CHECK {
|
||||
ensure(q.len > 0, "Queue is empty.", loc)
|
||||
}
|
||||
return &q.data[q.offset]
|
||||
}
|
||||
|
||||
/*
|
||||
Get the element at the back of the queue.
|
||||
|
||||
This will raise a bounds checking error if the queue is empty.
|
||||
*/
|
||||
back :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> T {
|
||||
when !ODIN_NO_BOUNDS_CHECK {
|
||||
ensure(q.len > 0, "Queue is empty.", loc)
|
||||
}
|
||||
idx := (q.offset+uint(q.len - 1))%builtin.len(q.data)
|
||||
return q.data[idx]
|
||||
}
|
||||
|
||||
/*
|
||||
Get a pointer to the element at the back of the queue.
|
||||
|
||||
This will raise a bounds checking error if the queue is empty.
|
||||
*/
|
||||
back_ptr :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> ^T {
|
||||
when !ODIN_NO_BOUNDS_CHECK {
|
||||
ensure(q.len > 0, "Queue is empty.", loc)
|
||||
}
|
||||
idx := (q.offset+uint(q.len - 1))%builtin.len(q.data)
|
||||
return &q.data[idx]
|
||||
}
|
||||
|
||||
peek_front :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> ^T {
|
||||
runtime.bounds_check_error_loc(loc, 0, builtin.len(q.data))
|
||||
idx := q.offset%builtin.len(q.data)
|
||||
return &q.data[idx]
|
||||
}
|
||||
|
||||
peek_back :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> ^T {
|
||||
runtime.bounds_check_error_loc(loc, int(q.len - 1), builtin.len(q.data))
|
||||
idx := (uint(q.len - 1)+q.offset)%builtin.len(q.data)
|
||||
return &q.data[idx]
|
||||
}
|
||||
/*
|
||||
Push an element to the back of the queue.
|
||||
|
||||
// Push an element to the back of the queue
|
||||
push_back :: proc(q: ^$Q/Queue($T), elem: T) -> (ok: bool, err: runtime.Allocator_Error) {
|
||||
If there is no more space left and allocation fails to get more, this will
|
||||
return false with an `Allocator_Error`.
|
||||
|
||||
Example:
|
||||
|
||||
import "base:runtime"
|
||||
import "core:container/queue"
|
||||
|
||||
// This demonstrates typical queue behavior (First-In First-Out).
|
||||
main :: proc() {
|
||||
q: queue.Queue(int)
|
||||
queue.init(&q)
|
||||
queue.push_back(&q, 1)
|
||||
queue.push_back(&q, 2)
|
||||
queue.push_back(&q, 3)
|
||||
// q.data is now [1, 2, 3, ...]
|
||||
assert(queue.pop_front(&q) == 1)
|
||||
assert(queue.pop_front(&q) == 2)
|
||||
assert(queue.pop_front(&q) == 3)
|
||||
}
|
||||
*/
|
||||
push_back :: proc(q: ^$Q/Queue($T), elem: T, loc := #caller_location) -> (ok: bool, err: runtime.Allocator_Error) {
|
||||
if space(q^) == 0 {
|
||||
_grow(q) or_return
|
||||
_grow(q, loc = loc) or_return
|
||||
}
|
||||
idx := (q.offset+uint(q.len))%builtin.len(q.data)
|
||||
q.data[idx] = elem
|
||||
@@ -138,27 +270,78 @@ push_back :: proc(q: ^$Q/Queue($T), elem: T) -> (ok: bool, err: runtime.Allocato
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Push an element to the front of the queue
|
||||
push_front :: proc(q: ^$Q/Queue($T), elem: T) -> (ok: bool, err: runtime.Allocator_Error) {
|
||||
/*
|
||||
Push an element to the front of the queue.
|
||||
|
||||
If there is no more space left and allocation fails to get more, this will
|
||||
return false with an `Allocator_Error`.
|
||||
|
||||
Example:
|
||||
|
||||
import "base:runtime"
|
||||
import "core:container/queue"
|
||||
|
||||
// This demonstrates stack behavior (First-In Last-Out).
|
||||
main :: proc() {
|
||||
q: queue.Queue(int)
|
||||
queue.init(&q)
|
||||
queue.push_back(&q, 1)
|
||||
queue.push_back(&q, 2)
|
||||
queue.push_back(&q, 3)
|
||||
// q.data is now [1, 2, 3, ...]
|
||||
assert(queue.pop_back(&q) == 3)
|
||||
assert(queue.pop_back(&q) == 2)
|
||||
assert(queue.pop_back(&q) == 1)
|
||||
}
|
||||
*/
|
||||
push_front :: proc(q: ^$Q/Queue($T), elem: T, loc := #caller_location) -> (ok: bool, err: runtime.Allocator_Error) {
|
||||
if space(q^) == 0 {
|
||||
_grow(q) or_return
|
||||
}
|
||||
_grow(q, loc = loc) or_return
|
||||
}
|
||||
q.offset = uint(q.offset - 1 + builtin.len(q.data)) % builtin.len(q.data)
|
||||
q.len += 1
|
||||
q.data[q.offset] = elem
|
||||
return true, nil
|
||||
}
|
||||
|
||||
/*
|
||||
Pop an element from the back of the queue.
|
||||
|
||||
// Pop an element from the back of the queue
|
||||
This will raise a bounds checking error if the queue is empty.
|
||||
|
||||
Example:
|
||||
|
||||
import "base:runtime"
|
||||
import "core:container/queue"
|
||||
|
||||
// This demonstrates stack behavior (First-In Last-Out) at the far end of the data array.
|
||||
main :: proc() {
|
||||
q: queue.Queue(int)
|
||||
queue.init(&q)
|
||||
queue.push_front(&q, 1)
|
||||
queue.push_front(&q, 2)
|
||||
queue.push_front(&q, 3)
|
||||
// q.data is now [..., 3, 2, 1]
|
||||
log.infof("%#v", q)
|
||||
assert(queue.pop_front(&q) == 3)
|
||||
assert(queue.pop_front(&q) == 2)
|
||||
assert(queue.pop_front(&q) == 1)
|
||||
}
|
||||
*/
|
||||
pop_back :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> (elem: T) {
|
||||
assert(condition=q.len > 0, loc=loc)
|
||||
when !ODIN_NO_BOUNDS_CHECK {
|
||||
ensure(q.len > 0, "Queue is empty.", loc)
|
||||
}
|
||||
q.len -= 1
|
||||
idx := (q.offset+uint(q.len))%builtin.len(q.data)
|
||||
elem = q.data[idx]
|
||||
return
|
||||
}
|
||||
// Safely pop an element from the back of the queue
|
||||
|
||||
/*
|
||||
Pop an element from the back of the queue if one exists and return true.
|
||||
Otherwise, return a nil element and false.
|
||||
*/
|
||||
pop_back_safe :: proc(q: ^$Q/Queue($T)) -> (elem: T, ok: bool) {
|
||||
if q.len > 0 {
|
||||
q.len -= 1
|
||||
@@ -169,15 +352,25 @@ pop_back_safe :: proc(q: ^$Q/Queue($T)) -> (elem: T, ok: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
// Pop an element from the front of the queue
|
||||
/*
|
||||
Pop an element from the front of the queue
|
||||
|
||||
This will raise a bounds checking error if the queue is empty.
|
||||
*/
|
||||
pop_front :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> (elem: T) {
|
||||
assert(condition=q.len > 0, loc=loc)
|
||||
when !ODIN_NO_BOUNDS_CHECK {
|
||||
ensure(q.len > 0, "Queue is empty.", loc)
|
||||
}
|
||||
elem = q.data[q.offset]
|
||||
q.offset = (q.offset+1)%builtin.len(q.data)
|
||||
q.len -= 1
|
||||
return
|
||||
}
|
||||
// Safely pop an element from the front of the queue
|
||||
|
||||
/*
|
||||
Pop an element from the front of the queue if one exists and return true.
|
||||
Otherwise, return a nil element and false.
|
||||
*/
|
||||
pop_front_safe :: proc(q: ^$Q/Queue($T)) -> (elem: T, ok: bool) {
|
||||
if q.len > 0 {
|
||||
elem = q.data[q.offset]
|
||||
@@ -188,13 +381,18 @@ pop_front_safe :: proc(q: ^$Q/Queue($T)) -> (elem: T, ok: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
// Push multiple elements to the back of the queue
|
||||
push_back_elems :: proc(q: ^$Q/Queue($T), elems: ..T) -> (ok: bool, err: runtime.Allocator_Error) {
|
||||
/*
|
||||
Push many elements at once to the back of the queue.
|
||||
|
||||
If there is not enough space left and allocation fails to get more, this will
|
||||
return false with an `Allocator_Error`.
|
||||
*/
|
||||
push_back_elems :: proc(q: ^$Q/Queue($T), elems: ..T, loc := #caller_location) -> (ok: bool, err: runtime.Allocator_Error) {
|
||||
n := uint(builtin.len(elems))
|
||||
if space(q^) < int(n) {
|
||||
_grow(q, q.len + n) or_return
|
||||
_grow(q, q.len + n, loc) or_return
|
||||
}
|
||||
|
||||
|
||||
sz := uint(builtin.len(q.data))
|
||||
insert_from := (q.offset + q.len) % sz
|
||||
insert_to := n
|
||||
@@ -207,19 +405,31 @@ push_back_elems :: proc(q: ^$Q/Queue($T), elems: ..T) -> (ok: bool, err: runtime
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Consume `n` elements from the front of the queue
|
||||
/*
|
||||
Consume `n` elements from the back of the queue.
|
||||
|
||||
This will raise a bounds checking error if the queue does not have enough elements.
|
||||
*/
|
||||
consume_front :: proc(q: ^$Q/Queue($T), n: int, loc := #caller_location) {
|
||||
assert(condition=int(q.len) >= n, loc=loc)
|
||||
when !ODIN_NO_BOUNDS_CHECK {
|
||||
ensure(q.len >= uint(n), "Queue does not have enough elements to consume.", loc)
|
||||
}
|
||||
if n > 0 {
|
||||
nu := uint(n)
|
||||
q.offset = (q.offset + nu) % builtin.len(q.data)
|
||||
q.len -= nu
|
||||
q.len -= nu
|
||||
}
|
||||
}
|
||||
|
||||
// Consume `n` elements from the back of the queue
|
||||
/*
|
||||
Consume `n` elements from the back of the queue.
|
||||
|
||||
This will raise a bounds checking error if the queue does not have enough elements.
|
||||
*/
|
||||
consume_back :: proc(q: ^$Q/Queue($T), n: int, loc := #caller_location) {
|
||||
assert(condition=int(q.len) >= n, loc=loc)
|
||||
when !ODIN_NO_BOUNDS_CHECK {
|
||||
ensure(q.len >= uint(n), "Queue does not have enough elements to consume.", loc)
|
||||
}
|
||||
if n > 0 {
|
||||
q.len -= uint(n)
|
||||
}
|
||||
@@ -231,9 +441,14 @@ append_elem :: push_back
|
||||
append_elems :: push_back_elems
|
||||
push :: proc{push_back, push_back_elems}
|
||||
append :: proc{push_back, push_back_elems}
|
||||
enqueue :: push_back
|
||||
dequeue :: pop_front
|
||||
|
||||
|
||||
// Clear the contents of the queue
|
||||
/*
|
||||
Reset the queue's length and offset to zero, letting it write new elements over
|
||||
old memory, in effect clearing the accessible contents.
|
||||
*/
|
||||
clear :: proc(q: ^$Q/Queue($T)) {
|
||||
q.len = 0
|
||||
q.offset = 0
|
||||
@@ -241,10 +456,10 @@ clear :: proc(q: ^$Q/Queue($T)) {
|
||||
|
||||
|
||||
// Internal growing procedure
|
||||
_grow :: proc(q: ^$Q/Queue($T), min_capacity: uint = 0) -> runtime.Allocator_Error {
|
||||
_grow :: proc(q: ^$Q/Queue($T), min_capacity: uint = 0, loc := #caller_location) -> runtime.Allocator_Error {
|
||||
new_capacity := max(min_capacity, uint(8), uint(builtin.len(q.data))*2)
|
||||
n := uint(builtin.len(q.data))
|
||||
builtin.resize(&q.data, int(new_capacity)) or_return
|
||||
builtin.resize(&q.data, int(new_capacity), loc) or_return
|
||||
if q.offset + q.len > n {
|
||||
diff := n - q.offset
|
||||
copy(q.data[new_capacity-diff:], q.data[q.offset:][:diff])
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// This package implements a red-black tree
|
||||
// A red-black tree with the same API as our AVL tree.
|
||||
package container_rbtree
|
||||
|
||||
@(require) import "base:intrinsics"
|
||||
@@ -91,24 +91,24 @@ destroy :: proc(t: ^$T/Tree($Key, $Value), call_on_remove: bool = true) {
|
||||
}
|
||||
}
|
||||
|
||||
len :: proc "contextless" (t: ^$T/Tree($Key, $Value)) -> (node_count: int) {
|
||||
len :: proc "contextless" (t: $T/Tree($Key, $Value)) -> (node_count: int) {
|
||||
return t._size
|
||||
}
|
||||
|
||||
// first returns the first node in the tree (in-order) or nil iff
|
||||
// first returns the first node in the tree (in-order) or nil if and only if (⟺)
|
||||
// the tree is empty.
|
||||
first :: proc "contextless" (t: ^$T/Tree($Key, $Value)) -> ^Node(Key, Value) {
|
||||
return tree_first_or_last_in_order(t, Direction.Backward)
|
||||
}
|
||||
|
||||
// last returns the last element in the tree (in-order) or nil iff
|
||||
// last returns the last element in the tree (in-order) or nil if and only if (⟺)
|
||||
// the tree is empty.
|
||||
last :: proc "contextless" (t: ^$T/Tree($Key, $Value)) -> ^Node(Key, Value) {
|
||||
return tree_first_or_last_in_order(t, Direction.Forward)
|
||||
}
|
||||
|
||||
// find finds the key in the tree, and returns the corresponding node, or nil iff the value is not present.
|
||||
find :: proc(t: ^$T/Tree($Key, $Value), key: Key) -> (node: ^Node(Key, Value)) {
|
||||
// find finds the key in the tree, and returns the corresponding node, or nil if and only if (⟺) the value is not present.
|
||||
find :: proc(t: $T/Tree($Key, $Value), key: Key) -> (node: ^Node(Key, Value)) {
|
||||
node = t._root
|
||||
for node != nil {
|
||||
switch t._cmp_fn(key, node.key) {
|
||||
@@ -120,17 +120,17 @@ find :: proc(t: ^$T/Tree($Key, $Value), key: Key) -> (node: ^Node(Key, Value)) {
|
||||
return node
|
||||
}
|
||||
|
||||
// find_value finds the key in the tree, and returns the corresponding value, or nil iff the value is not present.
|
||||
find_value :: proc(t: ^$T/Tree($Key, $Value), key: Key) -> (value: Value, ok: bool) #optional_ok {
|
||||
// find_value finds the key in the tree, and returns the corresponding value, or nil if and only if (⟺) the value is not present.
|
||||
find_value :: proc(t: $T/Tree($Key, $Value), key: Key) -> (value: Value, ok: bool) #optional_ok {
|
||||
if n := find(t, key); n != nil {
|
||||
return n.value, true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// find_or_insert attempts to insert the value into the tree, and returns
|
||||
// the node, a boolean indicating if the value was inserted, and the
|
||||
// node allocator error if relevant. If the value is already present, the existing node is updated.
|
||||
// find_or_insert attempts to insert the key-value pair into the tree, and returns
|
||||
// the node, a boolean indicating if a new node was inserted, and the
|
||||
// node allocator error if relevant. If the key is already present, the existing node is updated and returned.
|
||||
find_or_insert :: proc(t: ^$T/Tree($Key, $Value), key: Key, value: Value) -> (n: ^Node(Key, Value), inserted: bool, err: runtime.Allocator_Error) {
|
||||
n_ptr := &t._root
|
||||
for n_ptr^ != nil {
|
||||
@@ -141,6 +141,7 @@ find_or_insert :: proc(t: ^$T/Tree($Key, $Value), key: Key, value: Value) -> (n:
|
||||
case .Greater:
|
||||
n_ptr = &n._right
|
||||
case .Equal:
|
||||
n.value = value
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -153,7 +154,7 @@ find_or_insert :: proc(t: ^$T/Tree($Key, $Value), key: Key, value: Value) -> (n:
|
||||
return n, true, nil
|
||||
}
|
||||
|
||||
// remove removes a node or value from the tree, and returns true iff the
|
||||
// remove removes a node or value from the tree, and returns true if and only if (⟺) the
|
||||
// removal was successful. While the node's value will be left intact,
|
||||
// the node itself will be freed via the tree's node allocator.
|
||||
remove :: proc {
|
||||
@@ -161,18 +162,18 @@ remove :: proc {
|
||||
remove_node,
|
||||
}
|
||||
|
||||
// remove_value removes a value from the tree, and returns true iff the
|
||||
// remove_value removes a value from the tree, and returns true if and only if (⟺) the
|
||||
// removal was successful. While the node's key + value will be left intact,
|
||||
// the node itself will be freed via the tree's node allocator.
|
||||
remove_key :: proc(t: ^$T/Tree($Key, $Value), key: Key, call_on_remove := true) -> bool {
|
||||
n := find(t, key)
|
||||
n := find(t^, key)
|
||||
if n == nil {
|
||||
return false // Key not found, nothing to do
|
||||
}
|
||||
return remove_node(t, n, call_on_remove)
|
||||
}
|
||||
|
||||
// remove_node removes a node from the tree, and returns true iff the
|
||||
// remove_node removes a node from the tree, and returns true if and only if (⟺) the
|
||||
// removal was successful. While the node's key + value will be left intact,
|
||||
// the node itself will be freed via the tree's node allocator.
|
||||
remove_node :: proc(t: ^$T/Tree($Key, $Value), node: ^$N/Node(Key, Value), call_on_remove := true) -> (found: bool) {
|
||||
@@ -234,14 +235,14 @@ iterator_from_pos :: proc "contextless" (t: ^$T/Tree($Key, $Value), pos: ^Node(K
|
||||
}
|
||||
|
||||
// iterator_get returns the node currently pointed to by the iterator,
|
||||
// or nil iff the node has been removed, the tree is empty, or the end
|
||||
// or nil if and only if (⟺) the node has been removed, the tree is empty, or the end
|
||||
// of the tree has been reached.
|
||||
iterator_get :: proc "contextless" (it: ^$I/Iterator($Key, $Value)) -> ^Node(Key, Value) {
|
||||
return it._cur
|
||||
}
|
||||
|
||||
// iterator_remove removes the node currently pointed to by the iterator,
|
||||
// and returns true iff the removal was successful. Semantics are the
|
||||
// and returns true if and only if (⟺) the removal was successful. Semantics are the
|
||||
// same as the Tree remove.
|
||||
iterator_remove :: proc(it: ^$I/Iterator($Key, $Value), call_on_remove: bool = true) -> bool {
|
||||
if it._cur == nil {
|
||||
@@ -257,7 +258,7 @@ iterator_remove :: proc(it: ^$I/Iterator($Key, $Value), call_on_remove: bool = t
|
||||
}
|
||||
|
||||
// iterator_next advances the iterator and returns the (node, true) or
|
||||
// or (nil, false) iff the end of the tree has been reached.
|
||||
// or (nil, false) if and only if (⟺) the end of the tree has been reached.
|
||||
//
|
||||
// Note: The first call to iterator_next will return the first node instead
|
||||
// of advancing the iterator.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user