mirror of
https://github.com/odin-lang/Odin.git
synced 2026-04-18 12:30:28 +00:00
Merge branch 'master' into macharena
This commit is contained in:
96
.github/workflows/ci.yml
vendored
96
.github/workflows/ci.yml
vendored
@@ -30,13 +30,13 @@ 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 examples/all/sdl3 -vet -strict-style -disallow-do -target:netbsd_amd64 -no-entry-point
|
||||
./odin check examples/all/sdl3 -vet -strict-style -disallow-do -target:netbsd_arm64 -no-entry-point
|
||||
./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 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 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
|
||||
|
||||
@@ -63,11 +63,11 @@ 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 examples/all/sdl3 -vet -strict-style -disallow-do -target:freebsd_amd64 -no-entry-point
|
||||
./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 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 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:
|
||||
@@ -75,7 +75,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# MacOS 13 runs on Intel, 14 runs on ARM
|
||||
os: [macos-13, macos-14, ubuntu-latest]
|
||||
os: [macos-14, ubuntu-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: ${{ matrix.os == 'macos-14' && 'MacOS ARM' || (matrix.os == 'macos-13' && 'MacOS Intel') || (matrix.os == 'ubuntu-latest' && 'Ubuntu') }} Build, Check, and Test
|
||||
timeout-minutes: 15
|
||||
@@ -123,17 +123,17 @@ 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
|
||||
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 -strict-style -vet -disallow-do -no-entry-point
|
||||
run: ./odin check examples/all/sdl3 -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -no-entry-point
|
||||
- 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 -sanitize:address
|
||||
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 -sanitize:address
|
||||
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 -sanitize:address
|
||||
- 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 -sanitize:address
|
||||
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
|
||||
- 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 -sanitize:address
|
||||
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
|
||||
@@ -141,43 +141,43 @@ jobs:
|
||||
|
||||
- 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'
|
||||
|
||||
- name: Check benchmarks
|
||||
run: ./odin check tests/benchmark -vet -strict-style -no-entry-point
|
||||
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 -strict-style -disallow-do -target:linux_i386
|
||||
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 Linux arm64
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all -vet -strict-style -disallow-do -target:linux_arm64
|
||||
run: ./odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:linux_arm64
|
||||
- name: Odin check examples/all for FreeBSD amd64
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all -vet -strict-style -disallow-do -target:freebsd_amd64
|
||||
run: ./odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freebsd_amd64
|
||||
- name: Odin check examples/all for OpenBSD amd64
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all -vet -strict-style -disallow-do -target:openbsd_amd64
|
||||
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 -strict-style -disallow-do -no-entry-point -target:js_wasm32
|
||||
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 -strict-style -disallow-do -no-entry-point -target:js_wasm64p32
|
||||
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 -strict-style -disallow-do -no-entry-point -target:linux_i386
|
||||
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 Linux arm64
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all/sdl3 -vet -strict-style -disallow-do -no-entry-point -target:linux_arm64
|
||||
run: ./odin check examples/all/sdl3 -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -no-entry-point -target:linux_arm64
|
||||
- name: Odin check examples/all/sdl3 for FreeBSD amd64
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all/sdl3 -vet -strict-style -disallow-do -no-entry-point -target:freebsd_amd64
|
||||
run: ./odin check examples/all/sdl3 -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -no-entry-point -target:freebsd_amd64
|
||||
- name: Odin check examples/all/sdl3 for OpenBSD amd64
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all/sdl3 -vet -strict-style -disallow-do -no-entry-point -target:openbsd_amd64
|
||||
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
|
||||
@@ -208,38 +208,38 @@ jobs:
|
||||
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
|
||||
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
|
||||
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 examples/all/sdl3 -vet -strict-style -disallow-do -no-entry-point
|
||||
odin check examples/all/sdl3 -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -no-entry-point
|
||||
- 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 -sanitize:address
|
||||
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 -sanitize:address
|
||||
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 -sanitize:address
|
||||
- name: Vendor library tests
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
copy vendor\lua\5.4\windows\*.dll .
|
||||
odin test tests/vendor -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -sanitize:address
|
||||
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 -sanitize:address
|
||||
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: |
|
||||
@@ -257,12 +257,6 @@ jobs:
|
||||
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: |
|
||||
@@ -299,25 +293,25 @@ 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 examples/all/sdl3
|
||||
run: ./odin check examples/all/sdl3 -target:linux_riscv64 -vet -strict-style -disallow-do -no-entry-point
|
||||
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: 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
|
||||
|
||||
60
.github/workflows/cover.yml
vendored
Normal file
60
.github/workflows/cover.yml
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
name: Test Coverage
|
||||
on: [push, pull_request, workflow_dispatch]
|
||||
|
||||
jobs:
|
||||
build_linux_amd64:
|
||||
runs-on: ubuntu-latest
|
||||
name: Linux AMD64 Test Coverage
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Download LLVM (Ubuntu)
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
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: Install kcov
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install binutils-dev build-essential cmake libssl-dev libcurl4-openssl-dev libelf-dev libstdc++-12-dev zlib1g-dev libdw-dev libiberty-dev
|
||||
git clone https://github.com/SimonKagstrom/kcov.git
|
||||
mkdir kcov/build
|
||||
cd kcov/build
|
||||
cmake ..
|
||||
sudo make
|
||||
sudo make install
|
||||
cd ../..
|
||||
kcov --version
|
||||
|
||||
- name: Build Odin
|
||||
run: ./build_odin.sh release
|
||||
|
||||
- name: Odin report
|
||||
run: ./odin report
|
||||
|
||||
- name: Normal Core library tests
|
||||
run: |
|
||||
./odin build tests/core/normal.odin -build-mode:test -debug -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -target:linux_amd64
|
||||
mkdir kcov-out
|
||||
kcov --exclude-path=tests,/usr kcov-out ./normal.bin .
|
||||
|
||||
- name: Optimized Core library tests
|
||||
run: |
|
||||
./odin build tests/core/speed.odin -build-mode:test -debug -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -target:linux_amd64
|
||||
kcov --exclude-path=tests,/usr kcov-out ./speed.bin .
|
||||
|
||||
- name: Internals tests
|
||||
run: |
|
||||
./odin build tests/internal -build-mode:test -debug -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -target:linux_amd64
|
||||
kcov --exclude-path=tests,/usr kcov-out ./internal .
|
||||
|
||||
- uses: codecov/codecov-action@v5
|
||||
with:
|
||||
name: Ubuntu Coverage # optional
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
verbose: true # optional (default = false
|
||||
directory: kcov-out/kcov-merged
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -277,6 +277,7 @@ odin
|
||||
*.bin
|
||||
demo.bin
|
||||
libLLVM*.so*
|
||||
*.a
|
||||
|
||||
# shared collection
|
||||
shared/
|
||||
|
||||
@@ -67,7 +67,7 @@ init_global_temporary_allocator :: proc(size: int, backup_allocator := context.a
|
||||
// Prefer the procedure group `copy`.
|
||||
@builtin
|
||||
copy_slice :: proc "contextless" (dst, src: $T/[]$E) -> int {
|
||||
n := max(0, min(len(dst), len(src)))
|
||||
n := min(len(dst), len(src))
|
||||
if n > 0 {
|
||||
intrinsics.mem_copy(raw_data(dst), raw_data(src), n*size_of(E))
|
||||
}
|
||||
@@ -80,7 +80,7 @@ copy_slice :: proc "contextless" (dst, src: $T/[]$E) -> int {
|
||||
// Prefer the procedure group `copy`.
|
||||
@builtin
|
||||
copy_from_string :: proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int {
|
||||
n := max(0, min(len(dst), len(src)))
|
||||
n := min(len(dst), len(src))
|
||||
if n > 0 {
|
||||
intrinsics.mem_copy(raw_data(dst), raw_data(src), n)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -55,7 +55,7 @@ memory_block_alloc :: proc(allocator: Allocator, capacity: uint, alignment: uint
|
||||
memory_block_dealloc :: proc(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)
|
||||
mem_free(block_to_free, allocator, loc)
|
||||
}
|
||||
}
|
||||
@@ -87,7 +87,7 @@ 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
|
||||
}
|
||||
@@ -167,7 +167,7 @@ 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
|
||||
}
|
||||
@@ -232,7 +232,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
// grow data in-place, adjusting next allocation
|
||||
block.used = uint(new_end)
|
||||
data = block.base[start:new_end]
|
||||
sanitizer.address_unpoison(data)
|
||||
// sanitizer.address_unpoison(data)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -306,7 +306,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
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
75
check_all.bat
Normal file
75
check_all.bat
Normal file
@@ -0,0 +1,75 @@
|
||||
@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 essence_amd64
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:essence_amd64
|
||||
echo Checking freebsd_i386
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freebsd_i386
|
||||
echo Checking haiku_amd64
|
||||
odin check examples\all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:haiku_amd64
|
||||
)
|
||||
|
||||
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
|
||||
)
|
||||
78
check_all.sh
Executable file
78
check_all.sh
Executable file
@@ -0,0 +1,78 @@
|
||||
#!/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 essence_amd64
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:essence_amd64
|
||||
echo Checking freebsd_i386
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:freebsd_i386
|
||||
echo Checking haiku_amd64
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:haiku_amd64
|
||||
;;
|
||||
|
||||
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
|
||||
1
codecov.yml
Normal file
1
codecov.yml
Normal file
@@ -0,0 +1 @@
|
||||
comment: false
|
||||
@@ -4,7 +4,13 @@ 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 +19,31 @@ Queue :: struct($T: typeid) {
|
||||
|
||||
DEFAULT_CAPACITY :: 16
|
||||
|
||||
// Procedure to initialize a queue
|
||||
/*
|
||||
Initialize a `Queue` with a starting `capacity` and an `allocator`.
|
||||
*/
|
||||
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
|
||||
}
|
||||
clear(q)
|
||||
q.data = transmute([dynamic]T)runtime.Raw_Dynamic_Array{
|
||||
data = nil,
|
||||
len = 0,
|
||||
cap = 0,
|
||||
allocator = allocator,
|
||||
}
|
||||
return reserve(q, capacity)
|
||||
}
|
||||
|
||||
// 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 +55,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,84 +75,200 @@ 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 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) -> runtime.Allocator_Error {
|
||||
if capacity > space(q^) {
|
||||
return _grow(q, uint(capacity))
|
||||
return _grow(q, uint(capacity))
|
||||
}
|
||||
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]
|
||||
}
|
||||
|
||||
|
||||
@(deprecated="Use `front_ptr` instead")
|
||||
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]
|
||||
return front_ptr(q, loc)
|
||||
}
|
||||
|
||||
@(deprecated="Use `back_ptr` instead")
|
||||
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]
|
||||
return back_ptr(q, loc)
|
||||
}
|
||||
|
||||
// Push an element to the back of the queue
|
||||
/*
|
||||
Push an element to the back 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 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) -> (ok: bool, err: runtime.Allocator_Error) {
|
||||
if space(q^) == 0 {
|
||||
_grow(q) or_return
|
||||
@@ -138,27 +279,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 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) -> (ok: bool, err: runtime.Allocator_Error) {
|
||||
if space(q^) == 0 {
|
||||
_grow(q) or_return
|
||||
}
|
||||
}
|
||||
q.offset = uint(q.offset - 1 + builtin.len(q.data)) % builtin.len(q.data)
|
||||
q.len += 1
|
||||
q.data[q.offset] = elem
|
||||
return true, 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 +361,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 +390,18 @@ pop_front_safe :: proc(q: ^$Q/Queue($T)) -> (elem: T, ok: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
// Push multiple elements to the back of the queue
|
||||
/*
|
||||
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) -> (ok: bool, err: runtime.Allocator_Error) {
|
||||
n := uint(builtin.len(elems))
|
||||
if space(q^) < int(n) {
|
||||
_grow(q, q.len + n) or_return
|
||||
}
|
||||
|
||||
|
||||
sz := uint(builtin.len(q.data))
|
||||
insert_from := (q.offset + q.len) % sz
|
||||
insert_to := n
|
||||
@@ -207,19 +414,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 +450,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
|
||||
|
||||
23
core/dynlib/lb_haiku.odin
Normal file
23
core/dynlib/lb_haiku.odin
Normal file
@@ -0,0 +1,23 @@
|
||||
#+build haiku
|
||||
#+private
|
||||
package dynlib
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
_LIBRARY_FILE_EXTENSION :: ""
|
||||
|
||||
_load_library :: proc(path: string, global_symbols: bool, allocator: runtime.Allocator) -> (Library, bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
_unload_library :: proc(library: Library) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
_symbol_address :: proc(library: Library, symbol: string, allocator: runtime.Allocator) -> (ptr: rawptr, found: bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
_last_error :: proc() -> string {
|
||||
return ""
|
||||
}
|
||||
@@ -175,7 +175,7 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
||||
data = bytes.clone(data)
|
||||
}
|
||||
|
||||
t := &Tokenizer{}
|
||||
t := new(Tokenizer)
|
||||
init(t, string(data), path, error_handler)
|
||||
|
||||
doc = new(Document)
|
||||
@@ -403,6 +403,7 @@ destroy :: proc(doc: ^Document) {
|
||||
}
|
||||
delete(doc.strings_to_free)
|
||||
|
||||
free(doc.tokenizer)
|
||||
free(doc)
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ SUBTAG_NAME :: "name"
|
||||
SUBTAG_POS :: "pos"
|
||||
SUBTAG_REQUIRED :: "required"
|
||||
SUBTAG_HIDDEN :: "hidden"
|
||||
SUBTAG_VARIADIC :: "variadic"
|
||||
SUBTAG_MANIFOLD :: "manifold"
|
||||
SUBTAG_FILE :: "file"
|
||||
SUBTAG_PERMS :: "perms"
|
||||
SUBTAG_INDISTINCT :: "indistinct"
|
||||
@@ -28,7 +28,7 @@ TAG_USAGE :: "usage"
|
||||
|
||||
UNDOCUMENTED_FLAG :: "<This flag has not been documented yet.>"
|
||||
|
||||
INTERNAL_VARIADIC_FLAG :: "varg"
|
||||
INTERNAL_OVERFLOW_FLAG :: #config(ODIN_CORE_FLAGS_OVERFLOW_FLAG, "overflow")
|
||||
|
||||
RESERVED_HELP_FLAG :: "help"
|
||||
RESERVED_HELP_FLAG_SHORT :: "h"
|
||||
|
||||
@@ -20,6 +20,17 @@ The format is similar to the Odin binary's way of handling compiler flags.
|
||||
-<map>:<key>=<value> set map[key] to value
|
||||
|
||||
|
||||
Unhandled Arguments:
|
||||
|
||||
All unhandled positional arguments are placed into the `overflow` field on a
|
||||
struct, if it exists. In UNIX-style parsing, the existence of a `--` on the
|
||||
command line will also pass all arguments afterwards into this field.
|
||||
|
||||
If desired, the name of the field may be changed from `overflow` to any string
|
||||
by setting the `ODIN_CORE_FLAGS_OVERFLOW_FLAG` compile-time config option with
|
||||
`-define:ODIN_CORE_FLAGS_OVERFLOW_FLAG=<name>`.
|
||||
|
||||
|
||||
Struct Tags:
|
||||
|
||||
Users of the `core:encoding/json` package may be familiar with using tags to
|
||||
@@ -32,7 +43,7 @@ Under the `args` tag, there are the following subtags:
|
||||
- `pos=N`: place positional argument `N` into this flag.
|
||||
- `hidden`: hide this flag from the usage documentation.
|
||||
- `required`: cause verification to fail if this argument is not set.
|
||||
- `variadic`: take all remaining arguments when set, UNIX-style only.
|
||||
- `manifold=N`: take several arguments at once, UNIX-style only.
|
||||
- `file`: for `os.Handle` types, file open mode.
|
||||
- `perms`: for `os.Handle` types, file open permissions.
|
||||
- `indistinct`: allow the setting of distinct types by their base type.
|
||||
@@ -47,8 +58,9 @@ you want to require 3 and only 3 arguments in a dynamic array, you would
|
||||
specify `required=3<4`.
|
||||
|
||||
|
||||
`variadic` may be given a number (`variadic=N`) above 1 to limit how many extra
|
||||
arguments it consumes.
|
||||
`manifold` may be given a number (`manifold=N`) above 1 to limit how many extra
|
||||
arguments it consumes at once. If this number is not specified, it will take as
|
||||
many arguments as can be converted to the underlying element type.
|
||||
|
||||
|
||||
`file` determines the file open mode for an `os.Handle`.
|
||||
@@ -160,7 +172,7 @@ at parse time.
|
||||
--flag
|
||||
--flag=argument
|
||||
--flag argument
|
||||
--flag argument repeating-argument
|
||||
--flag argument (manifold-argument)
|
||||
|
||||
`-flag` may also be substituted for `--flag`.
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import "core:os"
|
||||
|
||||
Parse_Error_Reason :: enum {
|
||||
None,
|
||||
// An extra positional argument was given, and there is no `varg` field.
|
||||
// An extra positional argument was given, and there is no `overflow` field.
|
||||
Extra_Positional,
|
||||
// The underlying type does not support the string value it is being set to.
|
||||
Bad_Value,
|
||||
|
||||
@@ -107,14 +107,14 @@ main :: proc() {
|
||||
|
||||
// assignments: map[string]u8 `args:"name=assign" usage:"Number of jobs per worker."`,
|
||||
|
||||
// (Variadic) Only available in UNIX style:
|
||||
// (Manifold) Only available in UNIX style:
|
||||
|
||||
// bots: [dynamic]string `args:"variadic=2,required"`,
|
||||
// bots: [dynamic]string `args:"manifold=2,required"`,
|
||||
|
||||
verbose: bool `usage:"Show verbose output."`,
|
||||
debug: bool `args:"hidden" usage:"print debug info"`,
|
||||
|
||||
varg: [dynamic]string `usage:"Any extra arguments go here."`,
|
||||
overflow: [dynamic]string `usage:"Any extra arguments go here."`,
|
||||
}
|
||||
|
||||
opt: Options
|
||||
|
||||
@@ -33,9 +33,9 @@ push_positional :: #force_no_inline proc (model: ^$T, parser: ^Parser, arg: stri
|
||||
field, index, has_pos_assigned := get_field_by_pos(model, pos)
|
||||
|
||||
if !has_pos_assigned {
|
||||
when intrinsics.type_has_field(T, INTERNAL_VARIADIC_FLAG) {
|
||||
when intrinsics.type_has_field(T, INTERNAL_OVERFLOW_FLAG) {
|
||||
// Add it to the fallback array.
|
||||
field = reflect.struct_field_by_name(T, INTERNAL_VARIADIC_FLAG)
|
||||
field = reflect.struct_field_by_name(T, INTERNAL_OVERFLOW_FLAG)
|
||||
} else {
|
||||
return Parse_Error {
|
||||
.Extra_Positional,
|
||||
@@ -117,8 +117,8 @@ set_unix_flag :: proc(model: ^$T, parser: ^Parser, name: string) -> (future_args
|
||||
case runtime.Type_Info_Dynamic_Array:
|
||||
future_args = 1
|
||||
if tag, ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS); ok {
|
||||
if length, is_variadic := get_struct_subtag(tag, SUBTAG_VARIADIC); is_variadic {
|
||||
// Variadic arrays may specify how many arguments they consume at once.
|
||||
if length, is_manifold := get_struct_subtag(tag, SUBTAG_MANIFOLD); is_manifold {
|
||||
// Manifold arrays may specify how many arguments they consume at once.
|
||||
// Otherwise, they take everything that's left.
|
||||
if value, value_ok := strconv.parse_u64_of_base(length, 10); value_ok {
|
||||
future_args = cast(int)value
|
||||
|
||||
@@ -95,7 +95,7 @@ parse_one_unix_arg :: proc(model: ^$T, parser: ^Parser, arg: string) -> (
|
||||
// `--`, and only `--`.
|
||||
// Everything from now on will be treated as an argument.
|
||||
future_args = max(int)
|
||||
current_flag = INTERNAL_VARIADIC_FLAG
|
||||
current_flag = INTERNAL_OVERFLOW_FLAG
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,8 @@ validate_structure :: proc(model_type: $T, style: Parsing_Style, loc := #caller_
|
||||
}
|
||||
}
|
||||
|
||||
if pos_str, has_pos := get_struct_subtag(args_tag, SUBTAG_POS); has_pos {
|
||||
pos_str, has_pos := get_struct_subtag(args_tag, SUBTAG_POS)
|
||||
if has_pos {
|
||||
#partial switch specific_type_info in field.type.variant {
|
||||
case runtime.Type_Info_Map:
|
||||
fmt.panicf("%T.%s has `%s` defined, and this does not make sense on a map type.",
|
||||
@@ -79,7 +80,7 @@ validate_structure :: proc(model_type: $T, style: Parsing_Style, loc := #caller_
|
||||
fmt.assertf(!reflect.is_boolean(field.type), "%T.%s is a required boolean. This is disallowed.",
|
||||
model_type, field.name, loc = loc)
|
||||
|
||||
fmt.assertf(field.name != INTERNAL_VARIADIC_FLAG, "%T.%s is defined as required. This is disallowed.",
|
||||
fmt.assertf(field.name != INTERNAL_OVERFLOW_FLAG, "%T.%s is defined as required. This is disallowed.",
|
||||
model_type, field.name, loc = loc)
|
||||
|
||||
if len(requirement) > 0 {
|
||||
@@ -109,24 +110,28 @@ validate_structure :: proc(model_type: $T, style: Parsing_Style, loc := #caller_
|
||||
}
|
||||
}
|
||||
|
||||
if length, is_variadic := get_struct_subtag(args_tag, SUBTAG_VARIADIC); is_variadic {
|
||||
if length, is_manifold := get_struct_subtag(args_tag, SUBTAG_MANIFOLD); is_manifold {
|
||||
fmt.assertf(!has_pos,
|
||||
"%T.%s has both `%s` and `%s` defined. This is disallowed.\n\tSuggestion: Use a dynamic array field named `%s` to accept unspecified positional arguments.",
|
||||
model_type, field.name, SUBTAG_POS, SUBTAG_MANIFOLD, INTERNAL_OVERFLOW_FLAG, loc = loc)
|
||||
|
||||
if value, parse_ok := strconv.parse_u64_of_base(length, 10); parse_ok {
|
||||
fmt.assertf(value > 0,
|
||||
"%T.%s has `%s` set to %i. It must be greater than zero.",
|
||||
model_type, field.name, value, SUBTAG_VARIADIC, loc = loc)
|
||||
model_type, field.name, value, SUBTAG_MANIFOLD, loc = loc)
|
||||
fmt.assertf(value != 1,
|
||||
"%T.%s has `%s` set to 1. This has no effect.",
|
||||
model_type, field.name, SUBTAG_VARIADIC, loc = loc)
|
||||
"%T.%s has `%s` set to 1. This is equivalent to not defining `%s`.",
|
||||
model_type, field.name, SUBTAG_MANIFOLD, SUBTAG_MANIFOLD, loc = loc)
|
||||
}
|
||||
|
||||
#partial switch specific_type_info in field.type.variant {
|
||||
case runtime.Type_Info_Dynamic_Array:
|
||||
fmt.assertf(style != .Odin,
|
||||
"%T.%s has `%s` defined, but this only makes sense in UNIX-style parsing mode.",
|
||||
model_type, field.name, SUBTAG_VARIADIC, loc = loc)
|
||||
model_type, field.name, SUBTAG_MANIFOLD, loc = loc)
|
||||
case:
|
||||
fmt.panicf("%T.%s has `%s` defined, but this only makes sense on dynamic arrays.",
|
||||
model_type, field.name, SUBTAG_VARIADIC, loc = loc)
|
||||
model_type, field.name, SUBTAG_MANIFOLD, loc = loc)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ package flags
|
||||
Parsing_Style :: enum {
|
||||
// Odin-style: `-flag`, `-flag:option`, `-map:key=value`
|
||||
Odin,
|
||||
// UNIX-style: `-flag` or `--flag`, `--flag=argument`, `--flag argument repeating-argument`
|
||||
// UNIX-style: `-flag` or `--flag`, `--flag=argument`, `--flag argument (manifold-argument)`
|
||||
Unix,
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ parse :: proc(
|
||||
}
|
||||
|
||||
case .Unix:
|
||||
// Support for `-flag argument (repeating-argument ...)`
|
||||
// Support for `-flag argument (manifold-argument ...)`
|
||||
future_args: int
|
||||
current_flag: string
|
||||
|
||||
|
||||
@@ -30,18 +30,18 @@ write_usage :: proc(out: io.Writer, data_type: typeid, program: string = "", sty
|
||||
is_positional: bool,
|
||||
is_required: bool,
|
||||
is_boolean: bool,
|
||||
is_variadic: bool,
|
||||
variadic_length: int,
|
||||
is_manifold: bool,
|
||||
manifold_length: int,
|
||||
}
|
||||
|
||||
//
|
||||
// POSITIONAL+REQUIRED, POSITIONAL, REQUIRED, NON_REQUIRED+NON_POSITIONAL, ...
|
||||
//
|
||||
sort_flags :: proc(i, j: Flag) -> slice.Ordering {
|
||||
// `varg` goes to the end.
|
||||
if i.name == INTERNAL_VARIADIC_FLAG {
|
||||
// `overflow` goes to the end.
|
||||
if i.name == INTERNAL_OVERFLOW_FLAG {
|
||||
return .Greater
|
||||
} else if j.name == INTERNAL_VARIADIC_FLAG {
|
||||
} else if j.name == INTERNAL_OVERFLOW_FLAG {
|
||||
return .Less
|
||||
}
|
||||
|
||||
@@ -120,10 +120,10 @@ write_usage :: proc(out: io.Writer, data_type: typeid, program: string = "", sty
|
||||
flag.is_required = true
|
||||
flag.required_min, flag.required_max, _ = parse_requirements(requirement)
|
||||
}
|
||||
if length_str, is_variadic := get_struct_subtag(args_tag, SUBTAG_VARIADIC); is_variadic {
|
||||
flag.is_variadic = true
|
||||
if length_str, is_manifold := get_struct_subtag(args_tag, SUBTAG_MANIFOLD); is_manifold {
|
||||
flag.is_manifold = true
|
||||
if length, parse_ok := strconv.parse_u64_of_base(length_str, 10); parse_ok {
|
||||
flag.variadic_length = cast(int)length
|
||||
flag.manifold_length = cast(int)length
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -147,15 +147,15 @@ write_usage :: proc(out: io.Writer, data_type: typeid, program: string = "", sty
|
||||
case runtime.Type_Info_Dynamic_Array:
|
||||
requirement_spec := describe_array_requirements(flag)
|
||||
|
||||
if flag.is_variadic || flag.name == INTERNAL_VARIADIC_FLAG {
|
||||
if flag.variadic_length == 0 {
|
||||
if flag.is_manifold || flag.name == INTERNAL_OVERFLOW_FLAG {
|
||||
if flag.manifold_length == 0 {
|
||||
flag.type_description = fmt.tprintf("<%v, ...>%s",
|
||||
specific_type_info.elem.id,
|
||||
requirement_spec)
|
||||
} else {
|
||||
flag.type_description = fmt.tprintf("<%v, %i at once>%s",
|
||||
specific_type_info.elem.id,
|
||||
flag.variadic_length,
|
||||
flag.manifold_length,
|
||||
requirement_spec)
|
||||
}
|
||||
} else {
|
||||
@@ -177,7 +177,7 @@ write_usage :: proc(out: io.Writer, data_type: typeid, program: string = "", sty
|
||||
}
|
||||
}
|
||||
|
||||
if flag.name == INTERNAL_VARIADIC_FLAG {
|
||||
if flag.name == INTERNAL_OVERFLOW_FLAG {
|
||||
flag.full_length = len(flag.type_description)
|
||||
} else if flag.is_boolean {
|
||||
flag.full_length = len(flag_prefix) + len(flag.name) + len(flag.type_description)
|
||||
@@ -201,13 +201,13 @@ write_usage :: proc(out: io.Writer, data_type: typeid, program: string = "", sty
|
||||
strings.write_string(&builder, program)
|
||||
|
||||
for flag in visible_flags {
|
||||
if keep_it_short && !(flag.is_required || flag.is_positional || flag.name == INTERNAL_VARIADIC_FLAG) {
|
||||
if keep_it_short && !(flag.is_required || flag.is_positional || flag.name == INTERNAL_OVERFLOW_FLAG) {
|
||||
continue
|
||||
}
|
||||
|
||||
strings.write_byte(&builder, ' ')
|
||||
|
||||
if flag.name == INTERNAL_VARIADIC_FLAG {
|
||||
if flag.name == INTERNAL_OVERFLOW_FLAG {
|
||||
strings.write_string(&builder, "...")
|
||||
continue
|
||||
}
|
||||
@@ -252,7 +252,7 @@ write_usage :: proc(out: io.Writer, data_type: typeid, program: string = "", sty
|
||||
|
||||
strings.write_byte(&builder, '\t')
|
||||
|
||||
if flag.name == INTERNAL_VARIADIC_FLAG {
|
||||
if flag.name == INTERNAL_OVERFLOW_FLAG {
|
||||
strings.write_string(&builder, flag.type_description)
|
||||
} else {
|
||||
strings.write_string(&builder, flag_prefix)
|
||||
|
||||
@@ -160,6 +160,5 @@ destroy :: proc {
|
||||
int_destroy :: proc(integers: ..^Int)
|
||||
*/
|
||||
int_destroy,
|
||||
}
|
||||
|
||||
|
||||
internal_rat_destroy,
|
||||
}
|
||||
@@ -1660,13 +1660,13 @@ internal_int_sqrt :: proc(dest, src: ^Int, allocator := context.allocator) -> (e
|
||||
|
||||
if internal_gte(y, x) {
|
||||
internal_swap(dest, x)
|
||||
return nil
|
||||
return internal_clamp(dest)
|
||||
}
|
||||
internal_swap(x, y)
|
||||
}
|
||||
|
||||
internal_swap(dest, x)
|
||||
return err
|
||||
return internal_clamp(dest)
|
||||
}
|
||||
internal_sqrt :: proc { internal_int_sqrt, }
|
||||
|
||||
|
||||
@@ -310,7 +310,7 @@ int_atoi :: proc(res: ^Int, input: string, radix := i8(10), allocator := context
|
||||
res.sign = sign
|
||||
}
|
||||
|
||||
return nil
|
||||
return internal_clamp(res)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -157,6 +157,8 @@ internal_rat_norm :: proc(z: ^Rat, allocator := context.allocator) -> (err: Erro
|
||||
z.b.sign = .Zero_or_Positive
|
||||
|
||||
f := &Int{}
|
||||
defer internal_int_destroy(f)
|
||||
|
||||
internal_int_gcd(f, &z.a, &z.b) or_return
|
||||
if !internal_int_equals_digit(f, 1) {
|
||||
f.sign = .Zero_or_Positive
|
||||
@@ -378,9 +380,6 @@ internal_rat_to_float :: proc($T: typeid, z: ^Rat, allocator := context.allocato
|
||||
}
|
||||
|
||||
has_sign := a.sign != b.sign
|
||||
defer if has_sign {
|
||||
f = -builtin.abs(f)
|
||||
}
|
||||
|
||||
exp := alen - blen
|
||||
a2, b2 := &Int{}, &Int{}
|
||||
@@ -440,6 +439,9 @@ internal_rat_to_float :: proc($T: typeid, z: ^Rat, allocator := context.allocato
|
||||
if math.is_inf(f, 0) {
|
||||
exact = false
|
||||
}
|
||||
if has_sign {
|
||||
f = -builtin.abs(f)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
package mem
|
||||
|
||||
import "base:runtime"
|
||||
import "base:sanitizer"
|
||||
// import "base:sanitizer"
|
||||
|
||||
/*
|
||||
Rollback stack default block size.
|
||||
@@ -134,7 +134,7 @@ rb_free_all :: proc(stack: ^Rollback_Stack) {
|
||||
stack.head.next_block = nil
|
||||
stack.head.last_alloc = nil
|
||||
stack.head.offset = 0
|
||||
sanitizer.address_poison(stack.head.buffer)
|
||||
// sanitizer.address_poison(stack.head.buffer)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -241,7 +241,7 @@ rb_alloc_bytes_non_zeroed :: proc(
|
||||
block.offset = cast(uintptr)len(block.buffer)
|
||||
}
|
||||
res := ptr[:size]
|
||||
sanitizer.address_unpoison(res)
|
||||
// sanitizer.address_unpoison(res)
|
||||
return res, nil
|
||||
}
|
||||
return nil, .Out_Of_Memory
|
||||
@@ -338,7 +338,7 @@ rb_resize_bytes_non_zeroed :: proc(
|
||||
block.offset += cast(uintptr)size - cast(uintptr)old_size
|
||||
}
|
||||
res := (ptr)[:size]
|
||||
sanitizer.address_unpoison(res)
|
||||
// sanitizer.address_unpoison(res)
|
||||
#no_bounds_check return res, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
package mem_tlsf
|
||||
|
||||
import "base:intrinsics"
|
||||
import "base:sanitizer"
|
||||
// import "base:sanitizer"
|
||||
import "base:runtime"
|
||||
|
||||
// log2 of number of linear subdivisions of block sizes.
|
||||
@@ -210,7 +210,7 @@ alloc_bytes_non_zeroed :: proc(control: ^Allocator, size: uint, align: uint) ->
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
|
||||
sanitizer.address_poison(new_pool_buf)
|
||||
// sanitizer.address_poison(new_pool_buf)
|
||||
|
||||
// Allocate a new link in the `control.pool` tracking structure.
|
||||
new_pool := new_clone(Pool{
|
||||
@@ -257,7 +257,7 @@ alloc_bytes_non_zeroed :: proc(control: ^Allocator, size: uint, align: uint) ->
|
||||
return block_prepare_used(control, block, adjust)
|
||||
}
|
||||
|
||||
@(private, require_results, no_sanitize_address)
|
||||
@(private, require_results)
|
||||
alloc_bytes :: proc(control: ^Allocator, size: uint, align: uint) -> (res: []byte, err: runtime.Allocator_Error) {
|
||||
res, err = alloc_bytes_non_zeroed(control, size, align)
|
||||
if err == nil {
|
||||
@@ -267,6 +267,7 @@ alloc_bytes :: proc(control: ^Allocator, size: uint, align: uint) -> (res: []byt
|
||||
}
|
||||
|
||||
|
||||
@(no_sanitize_address)
|
||||
free_with_size :: proc(control: ^Allocator, ptr: rawptr, size: uint) {
|
||||
assert(control != nil)
|
||||
// `size` is currently ignored
|
||||
@@ -276,7 +277,7 @@ free_with_size :: proc(control: ^Allocator, ptr: rawptr, size: uint) {
|
||||
|
||||
block := block_from_ptr(ptr)
|
||||
assert(!block_is_free(block), "block already marked as free") // double free
|
||||
sanitizer.address_poison(ptr, block.size)
|
||||
// sanitizer.address_poison(ptr, block.size)
|
||||
block_mark_as_free(block)
|
||||
block = block_merge_prev(control, block)
|
||||
block = block_merge_next(control, block)
|
||||
@@ -320,7 +321,7 @@ resize :: proc(control: ^Allocator, ptr: rawptr, old_size, new_size: uint, align
|
||||
|
||||
block_trim_used(control, block, adjust)
|
||||
res = ([^]byte)(ptr)[:new_size]
|
||||
sanitizer.address_unpoison(res)
|
||||
// sanitizer.address_unpoison(res)
|
||||
|
||||
if min_size < new_size {
|
||||
to_zero := ([^]byte)(ptr)[min_size:new_size]
|
||||
@@ -483,19 +484,19 @@ block_mark_as_used :: proc(block: ^Block_Header) {
|
||||
block_set_used(block)
|
||||
}
|
||||
|
||||
@(private, require_results, no_sanitize_address)
|
||||
@(private, require_results)
|
||||
align_up :: proc(x, align: uint) -> (aligned: uint) {
|
||||
assert(0 == (align & (align - 1)), "must align to a power of two")
|
||||
return (x + (align - 1)) &~ (align - 1)
|
||||
}
|
||||
|
||||
@(private, require_results, no_sanitize_address)
|
||||
@(private, require_results)
|
||||
align_down :: proc(x, align: uint) -> (aligned: uint) {
|
||||
assert(0 == (align & (align - 1)), "must align to a power of two")
|
||||
return x - (x & (align - 1))
|
||||
}
|
||||
|
||||
@(private, require_results, no_sanitize_address)
|
||||
@(private, require_results)
|
||||
align_ptr :: proc(ptr: rawptr, align: uint) -> (aligned: rawptr) {
|
||||
assert(0 == (align & (align - 1)), "must align to a power of two")
|
||||
align_mask := uintptr(align) - 1
|
||||
@@ -505,7 +506,7 @@ align_ptr :: proc(ptr: rawptr, align: uint) -> (aligned: rawptr) {
|
||||
}
|
||||
|
||||
// Adjust an allocation size to be aligned to word size, and no smaller than internal minimum.
|
||||
@(private, require_results, no_sanitize_address)
|
||||
@(private, require_results)
|
||||
adjust_request_size :: proc(size, align: uint) -> (adjusted: uint) {
|
||||
if size == 0 {
|
||||
return 0
|
||||
@@ -519,7 +520,7 @@ adjust_request_size :: proc(size, align: uint) -> (adjusted: uint) {
|
||||
}
|
||||
|
||||
// Adjust an allocation size to be aligned to word size, and no smaller than internal minimum.
|
||||
@(private, require_results, no_sanitize_address)
|
||||
@(private, require_results)
|
||||
adjust_request_size_with_err :: proc(size, align: uint) -> (adjusted: uint, err: runtime.Allocator_Error) {
|
||||
if size == 0 {
|
||||
return 0, nil
|
||||
@@ -537,7 +538,7 @@ adjust_request_size_with_err :: proc(size, align: uint) -> (adjusted: uint, err:
|
||||
// TLSF utility functions. In most cases these are direct translations of
|
||||
// the documentation in the research paper.
|
||||
|
||||
@(optimization_mode="favor_size", private, require_results, no_sanitize_address)
|
||||
@(optimization_mode="favor_size", private, require_results)
|
||||
mapping_insert :: proc(size: uint) -> (fl, sl: i32) {
|
||||
if size < SMALL_BLOCK_SIZE {
|
||||
// Store small blocks in first list.
|
||||
@@ -550,7 +551,7 @@ mapping_insert :: proc(size: uint) -> (fl, sl: i32) {
|
||||
return
|
||||
}
|
||||
|
||||
@(optimization_mode="favor_size", private, require_results, no_sanitize_address)
|
||||
@(optimization_mode="favor_size", private, require_results)
|
||||
mapping_round :: #force_inline proc(size: uint) -> (rounded: uint) {
|
||||
rounded = size
|
||||
if size >= SMALL_BLOCK_SIZE {
|
||||
@@ -561,7 +562,7 @@ mapping_round :: #force_inline proc(size: uint) -> (rounded: uint) {
|
||||
}
|
||||
|
||||
// This version rounds up to the next block size (for allocations)
|
||||
@(optimization_mode="favor_size", private, require_results, no_sanitize_address)
|
||||
@(optimization_mode="favor_size", private, require_results)
|
||||
mapping_search :: proc(size: uint) -> (fl, sl: i32) {
|
||||
return mapping_insert(mapping_round(size))
|
||||
}
|
||||
@@ -788,7 +789,7 @@ block_prepare_used :: proc(control: ^Allocator, block: ^Block_Header, size: uint
|
||||
block_trim_free(control, block, size)
|
||||
block_mark_as_used(block)
|
||||
res = ([^]byte)(block_to_ptr(block))[:size]
|
||||
sanitizer.address_unpoison(res)
|
||||
// sanitizer.address_unpoison(res)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package mem_virtual
|
||||
import "core:mem"
|
||||
import "core:sync"
|
||||
|
||||
import "base:sanitizer"
|
||||
// import "base:sanitizer"
|
||||
|
||||
Arena_Kind :: enum uint {
|
||||
Growing = 0, // Chained memory blocks (singly linked list).
|
||||
@@ -55,7 +55,7 @@ arena_init_growing :: proc(arena: ^Arena, reserved: uint = DEFAULT_ARENA_GROWING
|
||||
if arena.minimum_block_size == 0 {
|
||||
arena.minimum_block_size = reserved
|
||||
}
|
||||
sanitizer.address_poison(arena.curr_block.base[:arena.curr_block.committed])
|
||||
// sanitizer.address_poison(arena.curr_block.base[:arena.curr_block.committed])
|
||||
return
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ arena_init_static :: proc(arena: ^Arena, reserved: uint = DEFAULT_ARENA_STATIC_R
|
||||
arena.curr_block = memory_block_alloc(commit_size, reserved, {}) or_return
|
||||
arena.total_used = 0
|
||||
arena.total_reserved = arena.curr_block.reserved
|
||||
sanitizer.address_poison(arena.curr_block.base[:arena.curr_block.committed])
|
||||
// sanitizer.address_poison(arena.curr_block.base[:arena.curr_block.committed])
|
||||
return
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ arena_init_buffer :: proc(arena: ^Arena, buffer: []byte) -> (err: Allocator_Erro
|
||||
|
||||
arena.kind = .Buffer
|
||||
|
||||
sanitizer.address_poison(buffer[:])
|
||||
// sanitizer.address_poison(buffer[:])
|
||||
|
||||
block_base := raw_data(buffer)
|
||||
block := (^Memory_Block)(block_base)
|
||||
@@ -163,7 +163,7 @@ arena_alloc :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_l
|
||||
arena.total_used = arena.curr_block.used
|
||||
}
|
||||
|
||||
sanitizer.address_unpoison(data)
|
||||
// sanitizer.address_unpoison(data)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ arena_static_reset_to :: proc(arena: ^Arena, pos: uint, loc := #caller_location)
|
||||
mem.zero_slice(arena.curr_block.base[arena.curr_block.used:][:prev_pos-pos])
|
||||
}
|
||||
arena.total_used = arena.curr_block.used
|
||||
sanitizer.address_poison(arena.curr_block.base[:arena.curr_block.committed])
|
||||
// sanitizer.address_poison(arena.curr_block.base[:arena.curr_block.committed])
|
||||
return true
|
||||
} else if pos == 0 {
|
||||
arena.total_used = 0
|
||||
@@ -200,7 +200,7 @@ arena_growing_free_last_memory_block :: proc(arena: ^Arena, loc := #caller_locat
|
||||
arena.total_reserved -= free_block.reserved
|
||||
|
||||
arena.curr_block = free_block.prev
|
||||
sanitizer.address_poison(free_block.base[:free_block.committed])
|
||||
// sanitizer.address_poison(free_block.base[:free_block.committed])
|
||||
memory_block_dealloc(free_block)
|
||||
}
|
||||
}
|
||||
@@ -219,9 +219,9 @@ arena_free_all :: proc(arena: ^Arena, loc := #caller_location) {
|
||||
if arena.curr_block != nil {
|
||||
curr_block_used := int(arena.curr_block.used)
|
||||
arena.curr_block.used = 0
|
||||
sanitizer.address_unpoison(arena.curr_block.base[:curr_block_used])
|
||||
// sanitizer.address_unpoison(arena.curr_block.base[:curr_block_used])
|
||||
mem.zero(arena.curr_block.base, curr_block_used)
|
||||
sanitizer.address_poison(arena.curr_block.base[:arena.curr_block.committed])
|
||||
// sanitizer.address_poison(arena.curr_block.base[:arena.curr_block.committed])
|
||||
}
|
||||
arena.total_used = 0
|
||||
case .Static, .Buffer:
|
||||
@@ -349,7 +349,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
if size < old_size {
|
||||
// shrink data in-place
|
||||
data = old_data[:size]
|
||||
sanitizer.address_poison(old_data[size:old_size])
|
||||
// sanitizer.address_poison(old_data[size:old_size])
|
||||
return
|
||||
}
|
||||
|
||||
@@ -363,7 +363,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
_ = alloc_from_memory_block(block, new_end - old_end, 1, default_commit_size=arena.default_commit_size) or_return
|
||||
arena.total_used += block.used - prev_used
|
||||
data = block.base[start:new_end]
|
||||
sanitizer.address_unpoison(data)
|
||||
// sanitizer.address_unpoison(data)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -374,7 +374,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
return
|
||||
}
|
||||
copy(new_memory, old_data[:old_size])
|
||||
sanitizer.address_poison(old_data[:old_size])
|
||||
// sanitizer.address_poison(old_data[:old_size])
|
||||
return new_memory, nil
|
||||
case .Query_Features:
|
||||
set := (^mem.Allocator_Mode_Set)(old_memory)
|
||||
|
||||
@@ -2,7 +2,7 @@ package mem_virtual
|
||||
|
||||
import "core:mem"
|
||||
import "base:intrinsics"
|
||||
import "base:sanitizer"
|
||||
// import "base:sanitizer"
|
||||
import "base:runtime"
|
||||
_ :: runtime
|
||||
|
||||
@@ -22,7 +22,7 @@ reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Erro
|
||||
|
||||
@(no_sanitize_address)
|
||||
commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
|
||||
sanitizer.address_unpoison(data, size)
|
||||
// sanitizer.address_unpoison(data, size)
|
||||
return _commit(data, size)
|
||||
}
|
||||
|
||||
@@ -35,13 +35,13 @@ reserve_and_commit :: proc "contextless" (size: uint) -> (data: []byte, err: All
|
||||
|
||||
@(no_sanitize_address)
|
||||
decommit :: proc "contextless" (data: rawptr, size: uint) {
|
||||
sanitizer.address_poison(data, size)
|
||||
// sanitizer.address_poison(data, size)
|
||||
_decommit(data, size)
|
||||
}
|
||||
|
||||
@(no_sanitize_address)
|
||||
release :: proc "contextless" (data: rawptr, size: uint) {
|
||||
sanitizer.address_unpoison(data, size)
|
||||
// sanitizer.address_unpoison(data, size)
|
||||
_release(data, size)
|
||||
}
|
||||
|
||||
@@ -179,7 +179,7 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: uint,
|
||||
|
||||
data = block.base[block.used+alignment_offset:][:min_size]
|
||||
block.used += size
|
||||
sanitizer.address_unpoison(data)
|
||||
// sanitizer.address_unpoison(data)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ Network_Error :: union #shared_nil {
|
||||
UDP_Recv_Error,
|
||||
Shutdown_Error,
|
||||
Interfaces_Error,
|
||||
Socket_Info_Error,
|
||||
Socket_Option_Error,
|
||||
Set_Blocking_Error,
|
||||
Parse_Endpoint_Error,
|
||||
@@ -260,6 +261,9 @@ DNS_Configuration :: struct {
|
||||
resolv_conf: string,
|
||||
hosts_file: string,
|
||||
|
||||
resolv_conf_buf: [128]u8,
|
||||
hosts_file_buf: [128]u8,
|
||||
|
||||
// TODO: Allow loading these up with `reload_configuration()` call or the like,
|
||||
// so we don't have to do it each call.
|
||||
name_servers: []Endpoint,
|
||||
|
||||
@@ -22,69 +22,43 @@ package net
|
||||
Haesbaert: Security fixes
|
||||
*/
|
||||
|
||||
@(require) import "base:runtime"
|
||||
import "core:mem"
|
||||
import "core:strings"
|
||||
import "core:time"
|
||||
import "core:os"
|
||||
import "core:math/rand"
|
||||
/*
|
||||
Default configuration for DNS resolution.
|
||||
*/
|
||||
@(require) import "core:sync"
|
||||
|
||||
dns_config_initialized: sync.Once
|
||||
when ODIN_OS == .Windows {
|
||||
DEFAULT_DNS_CONFIGURATION :: DNS_Configuration{
|
||||
resolv_conf = "",
|
||||
hosts_file = "%WINDIR%\\system32\\drivers\\etc\\hosts",
|
||||
dns_configuration := DNS_Configuration{
|
||||
resolv_conf = "",
|
||||
hosts_file = "%WINDIR%\\system32\\drivers\\etc\\hosts",
|
||||
}
|
||||
} else when ODIN_OS == .Linux || ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
|
||||
DEFAULT_DNS_CONFIGURATION :: DNS_Configuration{
|
||||
resolv_conf = "/etc/resolv.conf",
|
||||
hosts_file = "/etc/hosts",
|
||||
dns_configuration := DNS_Configuration{
|
||||
resolv_conf = "/etc/resolv.conf",
|
||||
hosts_file = "/etc/hosts",
|
||||
}
|
||||
} else {
|
||||
#panic("Please add a configuration for this OS.")
|
||||
}
|
||||
|
||||
@(init)
|
||||
/*
|
||||
Replaces environment placeholders in `dns_configuration`. Only necessary on Windows.
|
||||
Is automatically called, once, by `get_dns_records_*`.
|
||||
*/
|
||||
@(private)
|
||||
init_dns_configuration :: proc() {
|
||||
/*
|
||||
Resolve %ENVIRONMENT% placeholders in their paths.
|
||||
*/
|
||||
dns_configuration.resolv_conf, _ = replace_environment_path(dns_configuration.resolv_conf)
|
||||
dns_configuration.hosts_file, _ = replace_environment_path(dns_configuration.hosts_file)
|
||||
}
|
||||
|
||||
@(fini, private)
|
||||
destroy_dns_configuration :: proc() {
|
||||
delete(dns_configuration.resolv_conf)
|
||||
dns_configuration.resolv_conf = ""
|
||||
delete(dns_configuration.hosts_file)
|
||||
dns_configuration.hosts_file = ""
|
||||
}
|
||||
|
||||
dns_configuration := DEFAULT_DNS_CONFIGURATION
|
||||
|
||||
// Always allocates for consistency.
|
||||
replace_environment_path :: proc(path: string, allocator := context.allocator) -> (res: string, ok: bool) {
|
||||
// Nothing to replace. Return a clone of the original.
|
||||
if strings.count(path, "%") != 2 {
|
||||
return strings.clone(path, allocator), true
|
||||
when ODIN_OS == .Windows {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
val := os.replace_environment_placeholders(dns_configuration.hosts_file, context.temp_allocator)
|
||||
copy(dns_configuration.hosts_file_buf[:], val)
|
||||
dns_configuration.hosts_file = string(dns_configuration.hosts_file_buf[:len(val)])
|
||||
}
|
||||
|
||||
left := strings.index(path, "%") + 1
|
||||
assert(left > 0 && left <= len(path)) // should be covered by there being two %
|
||||
|
||||
right := strings.index(path[left:], "%") + 1
|
||||
assert(right > 0 && right <= len(path)) // should be covered by there being two %
|
||||
|
||||
env_key := path[left: right]
|
||||
env_val := os.get_env(env_key, allocator)
|
||||
defer delete(env_val)
|
||||
|
||||
res, _ = strings.replace(path, path[left - 1: right + 1], env_val, 1, allocator)
|
||||
return res, true
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Resolves a hostname to exactly one IP4 and IP6 endpoint.
|
||||
It's then up to you which one you use.
|
||||
@@ -204,6 +178,9 @@ resolve_ip6 :: proc(hostname_and_maybe_port: string) -> (ep6: Endpoint, err: Net
|
||||
See `destroy_records`.
|
||||
*/
|
||||
get_dns_records_from_os :: proc(hostname: string, type: DNS_Record_Type, allocator := context.allocator) -> (records: []DNS_Record, err: DNS_Error) {
|
||||
when ODIN_OS == .Windows {
|
||||
sync.once_do(&dns_config_initialized, init_dns_configuration)
|
||||
}
|
||||
return _get_dns_records_os(hostname, type, allocator)
|
||||
}
|
||||
|
||||
@@ -219,6 +196,9 @@ get_dns_records_from_os :: proc(hostname: string, type: DNS_Record_Type, allocat
|
||||
See `destroy_records`.
|
||||
*/
|
||||
get_dns_records_from_nameservers :: proc(hostname: string, type: DNS_Record_Type, name_servers: []Endpoint, host_overrides: []DNS_Record, allocator := context.allocator) -> (records: []DNS_Record, err: DNS_Error) {
|
||||
when ODIN_OS == .Windows {
|
||||
sync.once_do(&dns_config_initialized, init_dns_configuration)
|
||||
}
|
||||
context.allocator = allocator
|
||||
|
||||
if type != .SRV {
|
||||
@@ -440,6 +420,8 @@ load_hosts :: proc(hosts_file_path: string, allocator := context.allocator) -> (
|
||||
splits := strings.fields(line)
|
||||
defer delete(splits)
|
||||
|
||||
(len(splits) >= 2) or_continue
|
||||
|
||||
ip_str := splits[0]
|
||||
addr := parse_address(ip_str)
|
||||
if addr == nil {
|
||||
@@ -886,4 +868,4 @@ parse_response :: proc(response: []u8, filter: DNS_Record_Type = nil, allocator
|
||||
xid = hdr.id
|
||||
|
||||
return _records[:], xid, true
|
||||
}
|
||||
}
|
||||
@@ -79,4 +79,4 @@ _get_dns_records_os :: proc(hostname: string, type: DNS_Record_Type, allocator :
|
||||
}
|
||||
|
||||
return get_dns_records_from_nameservers(hostname, type, name_servers, host_overrides[:])
|
||||
}
|
||||
}
|
||||
@@ -246,6 +246,23 @@ Shutdown_Error :: enum i32 {
|
||||
Unknown,
|
||||
}
|
||||
|
||||
Socket_Info_Error :: enum i32 {
|
||||
None,
|
||||
// No network connection, or the network stack is not initialized.
|
||||
Network_Unreachable,
|
||||
// Not enough space in internal tables/buffers to create a new socket, or an unsupported protocol is given.
|
||||
Insufficient_Resources,
|
||||
// Socket is invalid or not connected, or the manner given is invalid.
|
||||
Invalid_Argument,
|
||||
// The socket is valid, but unsupported by this opperation.
|
||||
Unsupported_Socket,
|
||||
// Connection was closed/aborted/shutdown.
|
||||
Connection_Closed,
|
||||
|
||||
// An error unable to be categorized in above categories, `last_platform_error` may have more info.
|
||||
Unknown,
|
||||
}
|
||||
|
||||
Socket_Option_Error :: enum i32 {
|
||||
None,
|
||||
// No network connection, or the network stack is not initialized.
|
||||
|
||||
@@ -226,6 +226,23 @@ _shutdown_error :: proc() -> Shutdown_Error {
|
||||
}
|
||||
}
|
||||
|
||||
_socket_info_error :: proc() -> Socket_Info_Error {
|
||||
#partial switch posix.errno() {
|
||||
case .EBADF, .ENOTSOCK:
|
||||
return .Invalid_Argument
|
||||
case .ENOTCONN:
|
||||
return .Network_Unreachable
|
||||
case .EOPNOTSUPP:
|
||||
return .Unsupported_Socket
|
||||
case .EINVAL:
|
||||
return .Connection_Closed
|
||||
case .ENOBUFS:
|
||||
return .Insufficient_Resources
|
||||
case:
|
||||
return .Unknown
|
||||
}
|
||||
}
|
||||
|
||||
_socket_option_error :: proc() -> Socket_Option_Error {
|
||||
#partial switch posix.errno() {
|
||||
case .ENOBUFS:
|
||||
|
||||
@@ -255,6 +255,24 @@ _shutdown_error :: proc(errno: freebsd.Errno) -> Shutdown_Error {
|
||||
}
|
||||
}
|
||||
|
||||
_socket_info_error :: proc(errno: freebsd.Errno) -> Socket_Info_Error {
|
||||
assert(errno != nil)
|
||||
_last_error = errno
|
||||
|
||||
#partial switch errno {
|
||||
case .EBADF, .ENOTSOCK, .EINVAL, .EFAULT:
|
||||
return .Invalid_Argument
|
||||
case .ENOTCONN:
|
||||
return .Network_Unreachable
|
||||
case .ECONNRESET:
|
||||
return .Connection_Closed
|
||||
case .ENOBUFS:
|
||||
return .Insufficient_Resources
|
||||
case:
|
||||
return .Unknown
|
||||
}
|
||||
}
|
||||
|
||||
_socket_option_error :: proc(errno: freebsd.Errno) -> Socket_Option_Error {
|
||||
assert(errno != nil)
|
||||
_last_error = errno
|
||||
|
||||
@@ -258,6 +258,22 @@ _shutdown_error :: proc(errno: linux.Errno) -> Shutdown_Error {
|
||||
}
|
||||
}
|
||||
|
||||
_socket_info_error :: proc(errno: linux.Errno) -> Socket_Info_Error {
|
||||
assert(errno != nil)
|
||||
_last_error = errno
|
||||
|
||||
#partial switch errno {
|
||||
case .EBADF, .ENOTSOCK, .EFAULT, .EINVAL:
|
||||
return .Invalid_Argument
|
||||
case .ENOTCONN:
|
||||
return .Network_Unreachable
|
||||
case .ENOBUFS:
|
||||
return .Insufficient_Resources
|
||||
case:
|
||||
return .Unknown
|
||||
}
|
||||
}
|
||||
|
||||
_socket_option_error :: proc(errno: linux.Errno) -> Socket_Option_Error {
|
||||
assert(errno != nil)
|
||||
_last_error = errno
|
||||
|
||||
@@ -18,3 +18,10 @@ _last_platform_error_string :: proc() -> string {
|
||||
_set_last_platform_error :: proc(err: i32) {
|
||||
_last_error = err
|
||||
}
|
||||
|
||||
Parse_Endpoint_Error :: enum u32 {
|
||||
None = 0,
|
||||
Bad_Port = 1,
|
||||
Bad_Address,
|
||||
Bad_Hostname,
|
||||
}
|
||||
@@ -234,6 +234,17 @@ _shutdown_error :: proc() -> Shutdown_Error {
|
||||
}
|
||||
}
|
||||
|
||||
_socket_info_error :: proc() -> Socket_Info_Error {
|
||||
#partial switch win.System_Error(win.WSAGetLastError()) {
|
||||
case .WSAEFAULT, .WSAEINPROGRESS, .WSAENOTSOCK, .WSAEINVAL:
|
||||
return .Invalid_Argument
|
||||
case .WSANOTINITIALISED, .WSAENETDOWN, .WSAENOTCONN:
|
||||
return .Network_Unreachable
|
||||
case:
|
||||
return .Unknown
|
||||
}
|
||||
}
|
||||
|
||||
_socket_option_error :: proc() -> Socket_Option_Error {
|
||||
#partial switch win.System_Error(win.WSAGetLastError()) {
|
||||
case .WSAENETDOWN, .WSANOTINITIALISED:
|
||||
|
||||
@@ -174,10 +174,17 @@ listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (socket: TC
|
||||
/*
|
||||
Returns the endpoint that the given socket is listening / bound on.
|
||||
*/
|
||||
bound_endpoint :: proc(socket: Any_Socket) -> (endpoint: Endpoint, err: Listen_Error) {
|
||||
bound_endpoint :: proc(socket: Any_Socket) -> (endpoint: Endpoint, err: Socket_Info_Error) {
|
||||
return _bound_endpoint(socket)
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the endpoint that the given socket is connected to. (Peer's endpoint)
|
||||
*/
|
||||
peer_endpoint :: proc(socket: Any_Socket) -> (endpoint: Endpoint, err: Socket_Info_Error) {
|
||||
return _peer_endpoint(socket)
|
||||
}
|
||||
|
||||
accept_tcp :: proc(socket: TCP_Socket, options := DEFAULT_TCP_OPTIONS) -> (client: TCP_Socket, source: Endpoint, err: Accept_Error) {
|
||||
return _accept_tcp(socket, options)
|
||||
}
|
||||
|
||||
@@ -137,11 +137,24 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (skt: TCP_
|
||||
}
|
||||
|
||||
@(private)
|
||||
_bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Listen_Error) {
|
||||
_bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Socket_Info_Error) {
|
||||
addr: posix.sockaddr_storage
|
||||
addr_len := posix.socklen_t(size_of(addr))
|
||||
if posix.getsockname(posix.FD(any_socket_to_socket(sock)), (^posix.sockaddr)(&addr), &addr_len) != .OK {
|
||||
err = _listen_error()
|
||||
err = _socket_info_error()
|
||||
return
|
||||
}
|
||||
|
||||
ep = _sockaddr_to_endpoint(&addr)
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
_peer_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Socket_Info_Error) {
|
||||
addr: posix.sockaddr_storage
|
||||
addr_len := posix.socklen_t(size_of(addr))
|
||||
if posix.getpeername(posix.FD(any_socket_to_socket(sock)), (^posix.sockaddr)(&addr), &addr_len) != .OK {
|
||||
err = _socket_info_error()
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -140,12 +140,26 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (socket: T
|
||||
}
|
||||
|
||||
@(private)
|
||||
_bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Listen_Error) {
|
||||
_bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Socket_Info_Error) {
|
||||
sockaddr: freebsd.Socket_Address_Storage
|
||||
|
||||
errno := freebsd.getsockname(cast(Fd)any_socket_to_socket(sock), &sockaddr)
|
||||
if errno != nil {
|
||||
err = _listen_error(errno)
|
||||
err = _socket_info_error(errno)
|
||||
return
|
||||
}
|
||||
|
||||
ep = _sockaddr_to_endpoint(&sockaddr)
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
_peer_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Socket_Info_Error) {
|
||||
sockaddr: freebsd.Socket_Address_Storage
|
||||
|
||||
errno := freebsd.getpeername(cast(Fd)any_socket_to_socket(sock), &sockaddr)
|
||||
if errno != nil {
|
||||
err = _socket_info_error(errno)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -218,11 +218,24 @@ _listen_tcp :: proc(endpoint: Endpoint, backlog := 1000) -> (socket: TCP_Socket,
|
||||
}
|
||||
|
||||
@(private)
|
||||
_bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Listen_Error) {
|
||||
_bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Socket_Info_Error) {
|
||||
addr: linux.Sock_Addr_Any
|
||||
errno := linux.getsockname(_unwrap_os_socket(sock), &addr)
|
||||
if errno != .NONE {
|
||||
err = _listen_error(errno)
|
||||
err = _socket_info_error(errno)
|
||||
return
|
||||
}
|
||||
|
||||
ep = _wrap_os_addr(addr)
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
_peer_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Socket_Info_Error) {
|
||||
addr: linux.Sock_Addr_Any
|
||||
errno := linux.getpeername(_unwrap_os_socket(sock), &addr)
|
||||
if errno != .NONE {
|
||||
err = _socket_info_error(errno)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -177,11 +177,25 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (socket: T
|
||||
}
|
||||
|
||||
@(private)
|
||||
_bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Listen_Error) {
|
||||
_bound_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Socket_Info_Error) {
|
||||
sockaddr: win.SOCKADDR_STORAGE_LH
|
||||
sockaddrlen := c.int(size_of(sockaddr))
|
||||
if win.getsockname(win.SOCKET(any_socket_to_socket(sock)), &sockaddr, &sockaddrlen) == win.SOCKET_ERROR {
|
||||
err = _listen_error()
|
||||
err = _socket_info_error()
|
||||
return
|
||||
}
|
||||
|
||||
ep = _sockaddr_to_endpoint(&sockaddr)
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
_peer_endpoint :: proc(sock: Any_Socket) -> (ep: Endpoint, err: Socket_Info_Error) {
|
||||
sockaddr: win.SOCKADDR_STORAGE_LH
|
||||
sockaddrlen := c.int(size_of(sockaddr))
|
||||
res := win.getpeername(win.SOCKET(any_socket_to_socket(sock)), &sockaddr, &sockaddrlen)
|
||||
if res < 0 {
|
||||
err = _socket_info_error()
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -2307,6 +2307,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
open := expect_token(p, .Open_Paren)
|
||||
p.expr_level += 1
|
||||
expr := parse_expr(p, false)
|
||||
skip_possible_newline(p)
|
||||
p.expr_level -= 1
|
||||
close := expect_token(p, .Close_Paren)
|
||||
|
||||
@@ -2922,6 +2923,8 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
|
||||
fields: [dynamic]^ast.Bit_Field_Field
|
||||
for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF {
|
||||
docs := p.lead_comment
|
||||
|
||||
name := parse_ident(p)
|
||||
expect_token(p, .Colon)
|
||||
type := parse_type(p)
|
||||
@@ -2932,6 +2935,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
if p.curr_tok.kind == .String {
|
||||
tag = expect_token(p, .String)
|
||||
}
|
||||
ok := allow_token(p, .Comma)
|
||||
|
||||
field := ast.new(ast.Bit_Field_Field, name.pos, bit_size)
|
||||
|
||||
@@ -2939,10 +2943,14 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
field.type = type
|
||||
field.bit_size = bit_size
|
||||
field.tag = tag
|
||||
field.docs = docs
|
||||
field.comments = p.line_comment
|
||||
|
||||
append(&fields, field)
|
||||
|
||||
allow_token(p, .Comma) or_break
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
close := expect_closing_brace_of_field_list(p)
|
||||
@@ -3526,6 +3534,7 @@ parse_binary_expr :: proc(p: ^Parser, lhs: bool, prec_in: int) -> ^ast.Expr {
|
||||
case .When:
|
||||
x := expr
|
||||
cond := parse_expr(p, lhs)
|
||||
skip_possible_newline(p)
|
||||
else_tok := expect_token(p, .Else)
|
||||
y := parse_expr(p, lhs)
|
||||
te := ast.new(ast.Ternary_When_Expr, expr.pos, end_pos(p.prev_tok))
|
||||
@@ -3780,10 +3789,6 @@ parse_import_decl :: proc(p: ^Parser, kind := Import_Decl_Kind.Standard) -> ^ast
|
||||
import_name.pos = p.curr_tok.pos
|
||||
}
|
||||
|
||||
if !is_using && is_blank_ident(import_name) {
|
||||
error(p, import_name.pos, "illegal import name: '_'")
|
||||
}
|
||||
|
||||
path := expect_token_after(p, .String, "import")
|
||||
|
||||
decl := ast.new(ast.Import_Decl, tok.pos, end_pos(path))
|
||||
|
||||
@@ -8,7 +8,7 @@ import "base:runtime"
|
||||
// Otherwise the returned value will be empty and the boolean will be false
|
||||
// NOTE: the value will be allocated with the supplied allocator
|
||||
@(require_results)
|
||||
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
if key == "" {
|
||||
return
|
||||
}
|
||||
@@ -29,17 +29,54 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
|
||||
return
|
||||
}
|
||||
|
||||
// This version of `lookup_env` doesn't allocate and instead requires the user to provide a buffer.
|
||||
// Note that it is limited to environment names and values of 512 utf-16 values each
|
||||
// due to the necessary utf-8 <> utf-16 conversion.
|
||||
@(require_results)
|
||||
lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) {
|
||||
key_buf: [513]u16
|
||||
wkey := win32.utf8_to_wstring(key_buf[:], key)
|
||||
if wkey == nil {
|
||||
return "", .Buffer_Full
|
||||
}
|
||||
|
||||
n2 := win32.GetEnvironmentVariableW(wkey, nil, 0)
|
||||
if n2 == 0 {
|
||||
return "", .Env_Var_Not_Found
|
||||
}
|
||||
|
||||
val_buf: [513]u16
|
||||
n2 = win32.GetEnvironmentVariableW(wkey, raw_data(val_buf[:]), u32(len(val_buf[:])))
|
||||
if n2 == 0 {
|
||||
return "", .Env_Var_Not_Found
|
||||
} else if int(n2) > len(buf) {
|
||||
return "", .Buffer_Full
|
||||
}
|
||||
|
||||
value = win32.utf16_to_utf8(buf, val_buf[:n2])
|
||||
|
||||
return value, nil
|
||||
}
|
||||
lookup_env :: proc{lookup_env_alloc, lookup_env_buffer}
|
||||
|
||||
// get_env retrieves the value of the environment variable named by the key
|
||||
// It returns the value, which will be empty if the variable is not present
|
||||
// To distinguish between an empty value and an unset value, use lookup_env
|
||||
// NOTE: the value will be allocated with the supplied allocator
|
||||
@(require_results)
|
||||
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
value, _ = lookup_env(key, allocator)
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get_env_buf :: proc(buf: []u8, key: string) -> (value: string) {
|
||||
value, _ = lookup_env(buf, key)
|
||||
return
|
||||
}
|
||||
get_env :: proc{get_env_alloc, get_env_buf}
|
||||
|
||||
|
||||
// set_env sets the value of the environment variable named by the key
|
||||
set_env :: proc(key, value: string) -> Error {
|
||||
k := win32.utf8_to_wstring(key)
|
||||
|
||||
@@ -35,6 +35,9 @@ General_Error :: enum u32 {
|
||||
|
||||
File_Is_Pipe,
|
||||
Not_Dir,
|
||||
|
||||
// Environment variable not found.
|
||||
Env_Var_Not_Found,
|
||||
}
|
||||
|
||||
|
||||
@@ -82,6 +85,7 @@ error_string :: proc "contextless" (ferr: Error) -> string {
|
||||
case .Pattern_Has_Separator: return "pattern has separator"
|
||||
case .File_Is_Pipe: return "file is pipe"
|
||||
case .Not_Dir: return "file is not directory"
|
||||
case .Env_Var_Not_Found: return "environment variable not found"
|
||||
}
|
||||
case io.Error:
|
||||
switch e {
|
||||
|
||||
@@ -4,6 +4,7 @@ import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
import "core:io"
|
||||
import "core:strconv"
|
||||
import "core:strings"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
|
||||
@@ -210,3 +211,55 @@ heap_free :: runtime.heap_free
|
||||
processor_core_count :: proc() -> int {
|
||||
return _processor_core_count()
|
||||
}
|
||||
|
||||
// Always allocates for consistency.
|
||||
replace_environment_placeholders :: proc(path: string, allocator := context.allocator) -> (res: string) {
|
||||
path := path
|
||||
|
||||
sb: strings.Builder
|
||||
strings.builder_init_none(&sb, allocator)
|
||||
for len(path) > 0 {
|
||||
switch path[0] {
|
||||
case '%': // Windows
|
||||
when ODIN_OS == .Windows {
|
||||
for r, i in path[1:] {
|
||||
if r == '%' {
|
||||
env_key := path[1:i+1]
|
||||
env_val := get_env(env_key, context.temp_allocator)
|
||||
strings.write_string(&sb, env_val)
|
||||
path = path[i+1:] // % is part of key, so skip 1 character extra
|
||||
}
|
||||
}
|
||||
} else {
|
||||
strings.write_rune(&sb, rune(path[0]))
|
||||
}
|
||||
|
||||
case '$': // Posix
|
||||
when ODIN_OS != .Windows {
|
||||
env_key := ""
|
||||
dollar_loop: for r, i in path[1:] {
|
||||
switch r {
|
||||
case 'A'..='Z', 'a'..='z', '0'..='9', '_': // Part of key ident
|
||||
case:
|
||||
env_key = path[1:i+1]
|
||||
break dollar_loop
|
||||
}
|
||||
}
|
||||
if len(env_key) > 0 {
|
||||
env_val := get_env(env_key, context.temp_allocator)
|
||||
strings.write_string(&sb, env_val)
|
||||
path = path[len(env_key):]
|
||||
}
|
||||
|
||||
} else {
|
||||
strings.write_rune(&sb, rune(path[0]))
|
||||
}
|
||||
|
||||
case:
|
||||
strings.write_rune(&sb, rune(path[0]))
|
||||
}
|
||||
|
||||
path = path[1:]
|
||||
}
|
||||
return strings.to_string(sb)
|
||||
}
|
||||
@@ -1,26 +1,49 @@
|
||||
package os2
|
||||
|
||||
import "base:runtime"
|
||||
import "core:strings"
|
||||
|
||||
// get_env retrieves the value of the environment variable named by the key
|
||||
// `get_env` retrieves the value of the environment variable named by the key
|
||||
// It returns the value, which will be empty if the variable is not present
|
||||
// To distinguish between an empty value and an unset value, use lookup_env
|
||||
// NOTE: the value will be allocated with the supplied allocator
|
||||
@(require_results)
|
||||
get_env :: proc(key: string, allocator: runtime.Allocator) -> string {
|
||||
get_env_alloc :: proc(key: string, allocator: runtime.Allocator) -> string {
|
||||
value, _ := lookup_env(key, allocator)
|
||||
return value
|
||||
}
|
||||
|
||||
// lookup_env gets the value of the environment variable named by the key
|
||||
// `get_env` retrieves the value of the environment variable named by the key
|
||||
// It returns the value, which will be empty if the variable is not present
|
||||
// To distinguish between an empty value and an unset value, use lookup_env
|
||||
// NOTE: this version takes a backing buffer for the string value
|
||||
@(require_results)
|
||||
get_env_buf :: proc(buf: []u8, key: string) -> string {
|
||||
value, _ := lookup_env(buf, key)
|
||||
return value
|
||||
}
|
||||
|
||||
get_env :: proc{get_env_alloc, get_env_buf}
|
||||
|
||||
// `lookup_env` gets the value of the environment variable named by the key
|
||||
// If the variable is found in the environment the value (which can be empty) is returned and the boolean is true
|
||||
// Otherwise the returned value will be empty and the boolean will be false
|
||||
// NOTE: the value will be allocated with the supplied allocator
|
||||
@(require_results)
|
||||
lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
|
||||
return _lookup_env(key, allocator)
|
||||
lookup_env_alloc :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
|
||||
return _lookup_env_alloc(key, allocator)
|
||||
}
|
||||
|
||||
// This version of `lookup_env` doesn't allocate and instead requires the user to provide a buffer.
|
||||
// Note that it is limited to environment names and values of 512 utf-16 values each
|
||||
// due to the necessary utf-8 <> utf-16 conversion.
|
||||
@(require_results)
|
||||
lookup_env_buf :: proc(buf: []u8, key: string) -> (value: string, err: Error) {
|
||||
return _lookup_env_buf(buf, key)
|
||||
}
|
||||
|
||||
lookup_env :: proc{lookup_env_alloc, lookup_env_buf}
|
||||
|
||||
// set_env sets the value of the environment variable named by the key
|
||||
// Returns Error on failure
|
||||
set_env :: proc(key, value: string) -> Error {
|
||||
@@ -45,4 +68,55 @@ environ :: proc(allocator: runtime.Allocator) -> ([]string, Error) {
|
||||
return _environ(allocator)
|
||||
}
|
||||
|
||||
// Always allocates for consistency.
|
||||
replace_environment_placeholders :: proc(path: string, allocator: runtime.Allocator) -> (res: string) {
|
||||
path := path
|
||||
|
||||
sb: strings.Builder
|
||||
strings.builder_init_none(&sb, allocator)
|
||||
|
||||
for len(path) > 0 {
|
||||
switch path[0] {
|
||||
case '%': // Windows
|
||||
when ODIN_OS == .Windows {
|
||||
for r, i in path[1:] {
|
||||
if r == '%' {
|
||||
env_key := path[1:i+1]
|
||||
env_val := get_env(env_key, context.temp_allocator)
|
||||
strings.write_string(&sb, env_val)
|
||||
path = path[i+1:] // % is part of key, so skip 1 character extra
|
||||
}
|
||||
}
|
||||
} else {
|
||||
strings.write_rune(&sb, rune(path[0]))
|
||||
}
|
||||
|
||||
case '$': // Posix
|
||||
when ODIN_OS != .Windows {
|
||||
env_key := ""
|
||||
dollar_loop: for r, i in path[1:] {
|
||||
switch r {
|
||||
case 'A'..='Z', 'a'..='z', '0'..='9', '_': // Part of key ident
|
||||
case:
|
||||
env_key = path[1:i+1]
|
||||
break dollar_loop
|
||||
}
|
||||
}
|
||||
if len(env_key) > 0 {
|
||||
env_val := get_env(env_key, context.temp_allocator)
|
||||
strings.write_string(&sb, env_val)
|
||||
path = path[len(env_key):]
|
||||
}
|
||||
|
||||
} else {
|
||||
strings.write_rune(&sb, rune(path[0]))
|
||||
}
|
||||
|
||||
case:
|
||||
strings.write_rune(&sb, rune(path[0]))
|
||||
}
|
||||
|
||||
path = path[1:]
|
||||
}
|
||||
return strings.to_string(sb)
|
||||
}
|
||||
@@ -41,7 +41,7 @@ _lookup :: proc(key: string) -> (value: string, idx: int) {
|
||||
return "", -1
|
||||
}
|
||||
|
||||
_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
|
||||
_lookup_env_alloc :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
|
||||
if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
|
||||
_build_env()
|
||||
}
|
||||
@@ -53,6 +53,23 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string
|
||||
return
|
||||
}
|
||||
|
||||
_lookup_env_buf :: proc(buf: []u8, key: string) -> (value: string, err: Error) {
|
||||
if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
|
||||
_build_env()
|
||||
}
|
||||
|
||||
if v, idx := _lookup(key); idx != -1 {
|
||||
if len(buf) >= len(v) {
|
||||
copy(buf, v)
|
||||
return string(buf[:len(v)]), nil
|
||||
}
|
||||
return "", .Buffer_Full
|
||||
}
|
||||
return "", .Env_Var_Not_Found
|
||||
}
|
||||
|
||||
_lookup_env :: proc{_lookup_env_alloc, _lookup_env_buf}
|
||||
|
||||
_set_env :: proc(key, v_new: string) -> Error {
|
||||
if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
|
||||
_build_env()
|
||||
|
||||
@@ -7,7 +7,7 @@ import "base:runtime"
|
||||
import "core:strings"
|
||||
import "core:sys/posix"
|
||||
|
||||
_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
|
||||
_lookup_env_alloc :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
|
||||
if key == "" {
|
||||
return
|
||||
}
|
||||
@@ -26,6 +26,36 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string
|
||||
return
|
||||
}
|
||||
|
||||
_lookup_env_buf :: proc(buf: []u8, key: string) -> (value: string, error: Error) {
|
||||
if key == "" {
|
||||
return
|
||||
}
|
||||
|
||||
if len(key) + 1 > len(buf) {
|
||||
return "", .Buffer_Full
|
||||
} else {
|
||||
copy(buf, key)
|
||||
}
|
||||
|
||||
cval := posix.getenv(cstring(raw_data(buf)))
|
||||
if cval == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if value = string(cval); value == "" {
|
||||
return "", .Env_Var_Not_Found
|
||||
} else {
|
||||
if len(value) > len(buf) {
|
||||
return "", .Buffer_Full
|
||||
} else {
|
||||
copy(buf, value)
|
||||
return string(buf[:len(value)]), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_lookup_env :: proc{_lookup_env_alloc, _lookup_env_buf}
|
||||
|
||||
_set_env :: proc(key, value: string) -> (err: Error) {
|
||||
temp_allocator := TEMP_ALLOCATOR_GUARD({})
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ delete_string_if_not_original :: proc(str: string) {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
|
||||
_lookup_env_alloc :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
|
||||
if err := build_env(); err != nil {
|
||||
return
|
||||
}
|
||||
@@ -79,6 +79,34 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string
|
||||
return
|
||||
}
|
||||
|
||||
_lookup_env_buf :: proc(buf: []u8, key: string) -> (value: string, error: Error) {
|
||||
if key == "" {
|
||||
return
|
||||
}
|
||||
|
||||
if len(key) + 1 > len(buf) {
|
||||
return "", .Buffer_Full
|
||||
} else {
|
||||
copy(buf, key)
|
||||
}
|
||||
|
||||
sync.shared_guard(&g_env_mutex)
|
||||
|
||||
val, ok := g_env[key]
|
||||
|
||||
if !ok {
|
||||
return "", .Env_Var_Not_Found
|
||||
} else {
|
||||
if len(val) > len(buf) {
|
||||
return "", .Buffer_Full
|
||||
} else {
|
||||
copy(buf, val)
|
||||
return string(buf[:len(val)]), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
_lookup_env :: proc{_lookup_env_alloc, _lookup_env_buf}
|
||||
|
||||
@(require_results)
|
||||
_set_env :: proc(key, value: string) -> (err: Error) {
|
||||
build_env() or_return
|
||||
|
||||
@@ -4,7 +4,7 @@ package os2
|
||||
import win32 "core:sys/windows"
|
||||
import "base:runtime"
|
||||
|
||||
_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
|
||||
_lookup_env_alloc :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
|
||||
if key == "" {
|
||||
return
|
||||
}
|
||||
@@ -36,6 +36,36 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string
|
||||
return
|
||||
}
|
||||
|
||||
// This version of `lookup_env` doesn't allocate and instead requires the user to provide a buffer.
|
||||
// Note that it is limited to environment names and values of 512 utf-16 values each
|
||||
// due to the necessary utf-8 <> utf-16 conversion.
|
||||
@(require_results)
|
||||
_lookup_env_buf :: proc(buf: []u8, key: string) -> (value: string, err: Error) {
|
||||
key_buf: [513]u16
|
||||
wkey := win32.utf8_to_wstring(key_buf[:], key)
|
||||
if wkey == nil {
|
||||
return "", .Buffer_Full
|
||||
}
|
||||
|
||||
n2 := win32.GetEnvironmentVariableW(wkey, nil, 0)
|
||||
if n2 == 0 {
|
||||
return "", .Env_Var_Not_Found
|
||||
}
|
||||
|
||||
val_buf: [513]u16
|
||||
n2 = win32.GetEnvironmentVariableW(wkey, raw_data(val_buf[:]), u32(len(val_buf[:])))
|
||||
if n2 == 0 {
|
||||
return "", .Env_Var_Not_Found
|
||||
} else if int(n2) > len(buf) {
|
||||
return "", .Buffer_Full
|
||||
}
|
||||
|
||||
value = win32.utf16_to_utf8(buf, val_buf[:n2])
|
||||
|
||||
return value, nil
|
||||
}
|
||||
_lookup_env :: proc{_lookup_env_alloc, _lookup_env_buf}
|
||||
|
||||
_set_env :: proc(key, value: string) -> Error {
|
||||
temp_allocator := TEMP_ALLOCATOR_GUARD({})
|
||||
k := win32_utf8_to_wstring(key, temp_allocator) or_return
|
||||
|
||||
@@ -28,7 +28,7 @@ General_Error :: enum u32 {
|
||||
Pattern_Has_Separator,
|
||||
|
||||
No_HOME_Variable,
|
||||
Wordexp_Failed,
|
||||
Env_Var_Not_Found,
|
||||
|
||||
Unsupported,
|
||||
}
|
||||
@@ -77,7 +77,7 @@ error_string :: proc(ferr: Error) -> string {
|
||||
case .Unsupported: return "unsupported"
|
||||
case .Pattern_Has_Separator: return "pattern has separator"
|
||||
case .No_HOME_Variable: return "no $HOME variable"
|
||||
case .Wordexp_Failed: return "posix.wordexp was unable to expand"
|
||||
case .Env_Var_Not_Found: return "environment variable not found"
|
||||
}
|
||||
case io.Error:
|
||||
switch e {
|
||||
|
||||
@@ -269,6 +269,7 @@ _write_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (nt: i64, err: Error
|
||||
return
|
||||
}
|
||||
|
||||
@(no_sanitize_memory)
|
||||
_file_size :: proc(f: ^File_Impl) -> (n: i64, err: Error) {
|
||||
// TODO: Identify 0-sized "pseudo" files and return No_Size. This would
|
||||
// eliminate the need for the _read_entire_pseudo_file procs.
|
||||
|
||||
@@ -4,7 +4,6 @@ package os2
|
||||
import "base:runtime"
|
||||
import "core:encoding/ini"
|
||||
import "core:strings"
|
||||
import "core:sys/posix"
|
||||
|
||||
_user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
#partial switch ODIN_OS {
|
||||
@@ -169,14 +168,7 @@ _xdg_user_dirs_lookup :: proc(xdg_key: string, allocator: runtime.Allocator) ->
|
||||
|
||||
for k, v in ini.iterate(&it) {
|
||||
if k == xdg_key {
|
||||
we: posix.wordexp_t
|
||||
defer posix.wordfree(&we)
|
||||
|
||||
if _err := posix.wordexp(strings.clone_to_cstring(v, temp_allocator), &we, nil); _err != nil || we.we_wordc != 1 {
|
||||
return "", .Wordexp_Failed
|
||||
}
|
||||
|
||||
return strings.clone_from_cstring(we.we_wordv[0], allocator)
|
||||
return replace_environment_placeholders(v, allocator), nil
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
@@ -1090,9 +1090,10 @@ flush :: proc(fd: Handle) -> Error {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
|
||||
path_str := strings.clone_to_cstring(key, context.temp_allocator)
|
||||
// NOTE(tetra): Lifetime of 'cstr' is unclear, but _unix_free(cstr) segfaults.
|
||||
cstr := _unix_getenv(path_str)
|
||||
if cstr == nil {
|
||||
return "", false
|
||||
@@ -1101,11 +1102,39 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) {
|
||||
if len(key) + 1 > len(buf) {
|
||||
return "", .Buffer_Full
|
||||
} else {
|
||||
copy(buf, key)
|
||||
}
|
||||
|
||||
if value = string(_unix_getenv(cstring(raw_data(buf)))); value == "" {
|
||||
return "", .Env_Var_Not_Found
|
||||
} else {
|
||||
if len(value) > len(buf) {
|
||||
return "", .Buffer_Full
|
||||
} else {
|
||||
copy(buf, value)
|
||||
return string(buf[:len(value)]), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
lookup_env :: proc{lookup_env_alloc, lookup_env_buffer}
|
||||
|
||||
@(require_results)
|
||||
get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
value, _ = lookup_env(key, allocator)
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get_env_buf :: proc(buf: []u8, key: string) -> (value: string) {
|
||||
value, _ = lookup_env(buf, key)
|
||||
return
|
||||
}
|
||||
get_env :: proc{get_env_alloc, get_env_buf}
|
||||
|
||||
set_env :: proc(key, value: string) -> Error {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
key_cstring := strings.clone_to_cstring(key, context.temp_allocator)
|
||||
@@ -1231,7 +1260,7 @@ _processor_core_count :: proc() -> int {
|
||||
return 1
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
@(private, require_results)
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
res := make([]string, len(runtime.args__))
|
||||
for _, i in res {
|
||||
@@ -1240,6 +1269,11 @@ _alloc_command_line_arguments :: proc() -> []string {
|
||||
return res
|
||||
}
|
||||
|
||||
@(private, fini)
|
||||
_delete_command_line_arguments :: proc() {
|
||||
delete(args)
|
||||
}
|
||||
|
||||
socket :: proc(domain: int, type: int, protocol: int) -> (Socket, Error) {
|
||||
result := _unix_socket(c.int(domain), c.int(type), c.int(protocol))
|
||||
if result < 0 {
|
||||
|
||||
@@ -662,7 +662,7 @@ last_write_time_by_name :: proc(name: string) -> (File_Time, Error) {
|
||||
return File_Time(modified), nil
|
||||
}
|
||||
|
||||
@(private, require_results)
|
||||
@(private, require_results, no_sanitize_memory)
|
||||
_stat :: proc(path: string) -> (OS_Stat, Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
@@ -674,7 +674,7 @@ _stat :: proc(path: string) -> (OS_Stat, Error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@(private, require_results)
|
||||
@(private, require_results, no_sanitize_memory)
|
||||
_lstat :: proc(path: string) -> (OS_Stat, Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
@@ -688,7 +688,7 @@ _lstat :: proc(path: string) -> (OS_Stat, Error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@(private, require_results)
|
||||
@(private, require_results, no_sanitize_memory)
|
||||
_fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
|
||||
s: OS_Stat = ---
|
||||
result := _unix_fstat(fd, &s)
|
||||
@@ -827,10 +827,10 @@ access :: proc(path: string, mask: int) -> (bool, Error) {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
|
||||
|
||||
path_str := strings.clone_to_cstring(key, context.temp_allocator)
|
||||
// NOTE(tetra): Lifetime of 'cstr' is unclear, but _unix_free(cstr) segfaults.
|
||||
cstr := _unix_getenv(path_str)
|
||||
if cstr == nil {
|
||||
return "", false
|
||||
@@ -839,11 +839,39 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) {
|
||||
if len(key) + 1 > len(buf) {
|
||||
return "", .Buffer_Full
|
||||
} else {
|
||||
copy(buf, key)
|
||||
}
|
||||
|
||||
if value = string(_unix_getenv(cstring(raw_data(buf)))); value == "" {
|
||||
return "", .Env_Var_Not_Found
|
||||
} else {
|
||||
if len(value) > len(buf) {
|
||||
return "", .Buffer_Full
|
||||
} else {
|
||||
copy(buf, value)
|
||||
return string(buf[:len(value)]), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
lookup_env :: proc{lookup_env_alloc, lookup_env_buffer}
|
||||
|
||||
@(require_results)
|
||||
get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
value, _ = lookup_env(key, allocator)
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get_env_buf :: proc(buf: []u8, key: string) -> (value: string) {
|
||||
value, _ = lookup_env(buf, key)
|
||||
return
|
||||
}
|
||||
get_env :: proc{get_env_alloc, get_env_buf}
|
||||
|
||||
@(require_results)
|
||||
get_current_directory :: proc(allocator := context.allocator) -> string {
|
||||
context.allocator = allocator
|
||||
@@ -936,7 +964,7 @@ _processor_core_count :: proc() -> int {
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
@(private, require_results)
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
res := make([]string, len(runtime.args__))
|
||||
for arg, i in runtime.args__ {
|
||||
@@ -944,3 +972,8 @@ _alloc_command_line_arguments :: proc() -> []string {
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
@(private, fini)
|
||||
_delete_command_line_arguments :: proc() {
|
||||
delete(args)
|
||||
}
|
||||
|
||||
@@ -316,7 +316,7 @@ file_size :: proc(fd: Handle) -> (i64, Error) {
|
||||
// "Argv" arguments converted to Odin strings
|
||||
args := _alloc_command_line_arguments()
|
||||
|
||||
@(require_results)
|
||||
@(private, require_results)
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
res := make([]string, len(runtime.args__))
|
||||
for arg, i in runtime.args__ {
|
||||
@@ -325,7 +325,12 @@ _alloc_command_line_arguments :: proc() -> []string {
|
||||
return res
|
||||
}
|
||||
|
||||
@(private, require_results)
|
||||
@(private, fini)
|
||||
_delete_command_line_arguments :: proc() {
|
||||
delete(args)
|
||||
}
|
||||
|
||||
@(private, require_results, no_sanitize_memory)
|
||||
_stat :: proc(path: string) -> (OS_Stat, Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
@@ -339,7 +344,7 @@ _stat :: proc(path: string) -> (OS_Stat, Error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@(private, require_results)
|
||||
@(private, require_results, no_sanitize_memory)
|
||||
_lstat :: proc(path: string) -> (OS_Stat, Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
@@ -353,7 +358,7 @@ _lstat :: proc(path: string) -> (OS_Stat, Error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@(private, require_results)
|
||||
@(private, require_results, no_sanitize_memory)
|
||||
_fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
|
||||
// deliberately uninitialized
|
||||
s: OS_Stat = ---
|
||||
@@ -463,9 +468,10 @@ access :: proc(path: string, mask: int) -> (bool, Error) {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
|
||||
path_str := strings.clone_to_cstring(key, context.temp_allocator)
|
||||
// NOTE(tetra): Lifetime of 'cstr' is unclear, but _unix_free(cstr) segfaults.
|
||||
cstr := _unix_getenv(path_str)
|
||||
if cstr == nil {
|
||||
return "", false
|
||||
@@ -474,11 +480,40 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) {
|
||||
if len(key) + 1 > len(buf) {
|
||||
return "", .Buffer_Full
|
||||
} else {
|
||||
copy(buf, key)
|
||||
}
|
||||
|
||||
if value = string(_unix_getenv(cstring(raw_data(buf)))); value == "" {
|
||||
return "", .Env_Var_Not_Found
|
||||
} else {
|
||||
if len(value) > len(buf) {
|
||||
return "", .Buffer_Full
|
||||
} else {
|
||||
copy(buf, value)
|
||||
return string(buf[:len(value)]), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
lookup_env :: proc{lookup_env_alloc, lookup_env_buffer}
|
||||
|
||||
@(require_results)
|
||||
get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
value, _ = lookup_env(key, allocator)
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get_env_buf :: proc(buf: []u8, key: string) -> (value: string) {
|
||||
value, _ = lookup_env(buf, key)
|
||||
return
|
||||
}
|
||||
get_env :: proc{get_env_alloc, get_env_buf}
|
||||
|
||||
|
||||
@(private, require_results)
|
||||
_processor_core_count :: proc() -> int {
|
||||
info: haiku.system_info
|
||||
|
||||
@@ -250,6 +250,26 @@ current_thread_id :: proc "contextless" () -> int {
|
||||
return 0
|
||||
}
|
||||
|
||||
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
@(require_results)
|
||||
lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) {
|
||||
return "", .Env_Var_Not_Found
|
||||
}
|
||||
lookup_env :: proc{lookup_env_alloc, lookup_env_buffer}
|
||||
|
||||
@(require_results)
|
||||
get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
value, _ = lookup_env(key, allocator)
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get_env_buf :: proc(buf: []u8, key: string) -> (value: string) {
|
||||
value, _ = lookup_env(buf, key)
|
||||
return
|
||||
}
|
||||
get_env :: proc{get_env_alloc, get_env_buf}
|
||||
@@ -674,7 +674,7 @@ seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
|
||||
return i64(res), nil
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
@(require_results, no_sanitize_memory)
|
||||
file_size :: proc(fd: Handle) -> (i64, Error) {
|
||||
// deliberately uninitialized; the syscall fills this buffer for us
|
||||
s: OS_Stat = ---
|
||||
@@ -794,7 +794,7 @@ last_write_time_by_name :: proc(name: string) -> (time: File_Time, err: Error) {
|
||||
return File_Time(modified), nil
|
||||
}
|
||||
|
||||
@(private, require_results)
|
||||
@(private, require_results, no_sanitize_memory)
|
||||
_stat :: proc(path: string) -> (OS_Stat, Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
@@ -808,7 +808,7 @@ _stat :: proc(path: string) -> (OS_Stat, Error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@(private, require_results)
|
||||
@(private, require_results, no_sanitize_memory)
|
||||
_lstat :: proc(path: string) -> (OS_Stat, Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
@@ -822,7 +822,7 @@ _lstat :: proc(path: string) -> (OS_Stat, Error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@(private, require_results)
|
||||
@(private, require_results, no_sanitize_memory)
|
||||
_fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
|
||||
// deliberately uninitialized; the syscall fills this buffer for us
|
||||
s: OS_Stat = ---
|
||||
@@ -946,7 +946,7 @@ access :: proc(path: string, mask: int) -> (bool, Error) {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
|
||||
path_str := strings.clone_to_cstring(key, context.temp_allocator)
|
||||
// NOTE(tetra): Lifetime of 'cstr' is unclear, but _unix_free(cstr) segfaults.
|
||||
@@ -958,11 +958,39 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) {
|
||||
if len(key) + 1 > len(buf) {
|
||||
return "", .Buffer_Full
|
||||
} else {
|
||||
copy(buf, key)
|
||||
}
|
||||
|
||||
if value = string(_unix_getenv(cstring(raw_data(buf)))); value == "" {
|
||||
return "", .Env_Var_Not_Found
|
||||
} else {
|
||||
if len(value) > len(buf) {
|
||||
return "", .Buffer_Full
|
||||
} else {
|
||||
copy(buf, value)
|
||||
return string(buf[:len(value)]), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
lookup_env :: proc{lookup_env_alloc, lookup_env_buffer}
|
||||
|
||||
@(require_results)
|
||||
get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
value, _ = lookup_env(key, allocator)
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get_env_buf :: proc(buf: []u8, key: string) -> (value: string) {
|
||||
value, _ = lookup_env(buf, key)
|
||||
return
|
||||
}
|
||||
get_env :: proc{get_env_alloc, get_env_buf}
|
||||
|
||||
set_env :: proc(key, value: string) -> Error {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
key_cstring := strings.clone_to_cstring(key, context.temp_allocator)
|
||||
@@ -1069,7 +1097,7 @@ _processor_core_count :: proc() -> int {
|
||||
return int(_unix_get_nprocs())
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
@(private, require_results)
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
res := make([]string, len(runtime.args__))
|
||||
for arg, i in runtime.args__ {
|
||||
@@ -1078,6 +1106,11 @@ _alloc_command_line_arguments :: proc() -> []string {
|
||||
return res
|
||||
}
|
||||
|
||||
@(private, fini)
|
||||
_delete_command_line_arguments :: proc() {
|
||||
delete(args)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
socket :: proc(domain: int, type: int, protocol: int) -> (Socket, Error) {
|
||||
result := unix.sys_socket(domain, type, protocol)
|
||||
|
||||
@@ -724,7 +724,7 @@ last_write_time_by_name :: proc(name: string) -> (time: File_Time, err: Error) {
|
||||
return File_Time(modified), nil
|
||||
}
|
||||
|
||||
@(private, require_results)
|
||||
@(private, require_results, no_sanitize_memory)
|
||||
_stat :: proc(path: string) -> (OS_Stat, Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
@@ -736,7 +736,7 @@ _stat :: proc(path: string) -> (OS_Stat, Error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@(private, require_results)
|
||||
@(private, require_results, no_sanitize_memory)
|
||||
_lstat :: proc(path: string) -> (OS_Stat, Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
@@ -750,7 +750,7 @@ _lstat :: proc(path: string) -> (OS_Stat, Error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@(private, require_results)
|
||||
@(private, require_results, no_sanitize_memory)
|
||||
_fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
|
||||
s: OS_Stat = ---
|
||||
result := _unix_fstat(fd, &s)
|
||||
@@ -874,10 +874,10 @@ access :: proc(path: string, mask: int) -> (bool, Error) {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
|
||||
|
||||
path_str := strings.clone_to_cstring(key, context.temp_allocator)
|
||||
// NOTE(tetra): Lifetime of 'cstr' is unclear, but _unix_free(cstr) segfaults.
|
||||
cstr := _unix_getenv(path_str)
|
||||
if cstr == nil {
|
||||
return "", false
|
||||
@@ -886,11 +886,39 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) {
|
||||
if len(key) + 1 > len(buf) {
|
||||
return "", .Buffer_Full
|
||||
} else {
|
||||
copy(buf, key)
|
||||
}
|
||||
|
||||
if value = string(_unix_getenv(cstring(raw_data(buf)))); value == "" {
|
||||
return "", .Env_Var_Not_Found
|
||||
} else {
|
||||
if len(value) > len(buf) {
|
||||
return "", .Buffer_Full
|
||||
} else {
|
||||
copy(buf, value)
|
||||
return string(buf[:len(value)]), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
lookup_env :: proc{lookup_env_alloc, lookup_env_buffer}
|
||||
|
||||
@(require_results)
|
||||
get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
value, _ = lookup_env(key, allocator)
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get_env_buf :: proc(buf: []u8, key: string) -> (value: string) {
|
||||
value, _ = lookup_env(buf, key)
|
||||
return
|
||||
}
|
||||
get_env :: proc{get_env_alloc, get_env_buf}
|
||||
|
||||
@(require_results)
|
||||
get_current_directory :: proc(allocator := context.allocator) -> string {
|
||||
context.allocator = allocator
|
||||
@@ -986,7 +1014,7 @@ _processor_core_count :: proc() -> int {
|
||||
return 1
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
@(private, require_results)
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
res := make([]string, len(runtime.args__))
|
||||
for arg, i in runtime.args__ {
|
||||
@@ -994,3 +1022,8 @@ _alloc_command_line_arguments :: proc() -> []string {
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
@(private, fini)
|
||||
_delete_command_line_arguments :: proc() {
|
||||
delete(args)
|
||||
}
|
||||
|
||||
@@ -639,7 +639,7 @@ last_write_time_by_name :: proc(name: string) -> (time: File_Time, err: Error) {
|
||||
return File_Time(modified), nil
|
||||
}
|
||||
|
||||
@(private, require_results)
|
||||
@(private, require_results, no_sanitize_memory)
|
||||
_stat :: proc(path: string) -> (OS_Stat, Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
@@ -653,7 +653,7 @@ _stat :: proc(path: string) -> (OS_Stat, Error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@(private, require_results)
|
||||
@(private, require_results, no_sanitize_memory)
|
||||
_lstat :: proc(path: string) -> (OS_Stat, Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
@@ -667,7 +667,7 @@ _lstat :: proc(path: string) -> (OS_Stat, Error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@(private, require_results)
|
||||
@(private, require_results, no_sanitize_memory)
|
||||
_fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
|
||||
// deliberately uninitialized
|
||||
s: OS_Stat = ---
|
||||
@@ -787,9 +787,10 @@ access :: proc(path: string, mask: int) -> (bool, Error) {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
|
||||
path_str := strings.clone_to_cstring(key, context.temp_allocator)
|
||||
// NOTE(tetra): Lifetime of 'cstr' is unclear, but _unix_free(cstr) segfaults.
|
||||
cstr := _unix_getenv(path_str)
|
||||
if cstr == nil {
|
||||
return "", false
|
||||
@@ -798,11 +799,39 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) {
|
||||
if len(key) + 1 > len(buf) {
|
||||
return "", .Buffer_Full
|
||||
} else {
|
||||
copy(buf, key)
|
||||
}
|
||||
|
||||
if value = string(_unix_getenv(cstring(raw_data(buf)))); value == "" {
|
||||
return "", .Env_Var_Not_Found
|
||||
} else {
|
||||
if len(value) > len(buf) {
|
||||
return "", .Buffer_Full
|
||||
} else {
|
||||
copy(buf, value)
|
||||
return string(buf[:len(value)]), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
lookup_env :: proc{lookup_env_alloc, lookup_env_buffer}
|
||||
|
||||
@(require_results)
|
||||
get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
value, _ = lookup_env(key, allocator)
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get_env_buf :: proc(buf: []u8, key: string) -> (value: string) {
|
||||
value, _ = lookup_env(buf, key)
|
||||
return
|
||||
}
|
||||
get_env :: proc{get_env_alloc, get_env_buf}
|
||||
|
||||
@(require_results)
|
||||
get_current_directory :: proc(allocator := context.allocator) -> string {
|
||||
context.allocator = allocator
|
||||
@@ -885,7 +914,7 @@ _processor_core_count :: proc() -> int {
|
||||
return int(_sysconf(_SC_NPROCESSORS_ONLN))
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
@(private, require_results)
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
res := make([]string, len(runtime.args__))
|
||||
for arg, i in runtime.args__ {
|
||||
@@ -893,3 +922,8 @@ _alloc_command_line_arguments :: proc() -> []string {
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
@(private, fini)
|
||||
_delete_command_line_arguments :: proc() {
|
||||
delete(args)
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ stderr: Handle = 2
|
||||
|
||||
args := _alloc_command_line_arguments()
|
||||
|
||||
@(require_results)
|
||||
@(private, require_results)
|
||||
_alloc_command_line_arguments :: proc() -> (args: []string) {
|
||||
args = make([]string, len(runtime.args__))
|
||||
for &arg, i in args {
|
||||
@@ -36,6 +36,11 @@ _alloc_command_line_arguments :: proc() -> (args: []string) {
|
||||
return
|
||||
}
|
||||
|
||||
@(private, fini)
|
||||
_delete_command_line_arguments :: proc() {
|
||||
delete(args)
|
||||
}
|
||||
|
||||
// WASI works with "preopened" directories, the environment retrieves directories
|
||||
// (for example with `wasmtime --dir=. module.wasm`) and those given directories
|
||||
// are the only ones accessible by the application.
|
||||
@@ -239,3 +244,27 @@ exit :: proc "contextless" (code: int) -> ! {
|
||||
runtime._cleanup_runtime_contextless()
|
||||
wasi.proc_exit(wasi.exitcode_t(code))
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) {
|
||||
return "", .Env_Var_Not_Found
|
||||
}
|
||||
lookup_env :: proc{lookup_env_alloc, lookup_env_buffer}
|
||||
|
||||
@(require_results)
|
||||
get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
value, _ = lookup_env(key, allocator)
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get_env_buf :: proc(buf: []u8, key: string) -> (value: string) {
|
||||
value, _ = lookup_env(buf, key)
|
||||
return
|
||||
}
|
||||
get_env :: proc{get_env_alloc, get_env_buf}
|
||||
@@ -193,7 +193,7 @@ current_thread_id :: proc "contextless" () -> int {
|
||||
|
||||
|
||||
|
||||
@(require_results)
|
||||
@(private, require_results)
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
arg_count: i32
|
||||
arg_list_ptr := win32.CommandLineToArgvW(win32.GetCommandLineW(), &arg_count)
|
||||
@@ -215,6 +215,14 @@ _alloc_command_line_arguments :: proc() -> []string {
|
||||
return arg_list
|
||||
}
|
||||
|
||||
@(private, fini)
|
||||
_delete_command_line_arguments :: proc() {
|
||||
for s in args {
|
||||
delete(s)
|
||||
}
|
||||
delete(args)
|
||||
}
|
||||
|
||||
/*
|
||||
Windows 11 (preview) has the same major and minor version numbers
|
||||
as Windows 10: 10 and 0 respectively.
|
||||
|
||||
@@ -387,6 +387,25 @@ has_prefix :: proc(array: $T/[]$E, needle: T) -> bool where intrinsics.type_is_c
|
||||
return false
|
||||
}
|
||||
|
||||
/*
|
||||
return the suffix length common between slices `a` and `b`.
|
||||
|
||||
slice.suffix_length([]u8{1, 2, 3, 4}, []u8{1, 2, 3, 4}) -> 4
|
||||
slice.suffix_length([]u8{1, 2, 3, 4}, []u8{3, 4}) -> 2
|
||||
slice.suffix_length([]u8{1, 2, 3, 4}, []u8{1}) -> 0
|
||||
slice.suffix_length([]u8{1, 2, 3, 4}, []u8{1, 3, 5}) -> 0
|
||||
slice.suffix_length([]u8{3, 4, 5}, []u8{3, 5}) -> 1
|
||||
*/
|
||||
@(require_results)
|
||||
suffix_length :: proc(a, b: $T/[]$E) -> (n: int) where intrinsics.type_is_comparable(E) {
|
||||
len_a, len_b := len(a), len(b)
|
||||
_len := builtin.min(len_a, len_b)
|
||||
|
||||
#no_bounds_check for i := 1; i <= _len && a[len_a - i] == b[len_b - i]; i += 1 {
|
||||
n += 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
has_suffix :: proc(array: $T/[]$E, needle: T) -> bool where intrinsics.type_is_comparable(E) {
|
||||
|
||||
@@ -7,6 +7,14 @@ import "core:mem"
|
||||
import "core:sync"
|
||||
import "core:math/rand"
|
||||
|
||||
when ODIN_TEST {
|
||||
/*
|
||||
Hook for testing _try_select_raw allowing the test harness to manipulate the
|
||||
channels prior to the select actually operating on them.
|
||||
*/
|
||||
__try_select_raw_pause : proc() = nil
|
||||
}
|
||||
|
||||
/*
|
||||
Determines what operations `Chan` supports.
|
||||
*/
|
||||
@@ -75,6 +83,8 @@ Raw_Chan :: struct {
|
||||
r_waiting: int, // guarded by `mutex`
|
||||
w_waiting: int, // guarded by `mutex`
|
||||
|
||||
did_read: bool, // lets a sender know if the value was read
|
||||
|
||||
// Buffered
|
||||
queue: ^Raw_Queue,
|
||||
|
||||
@@ -412,8 +422,8 @@ as_recv :: #force_inline proc "contextless" (c: $C/Chan($T, $D)) -> (r: Chan(T,
|
||||
Sends the specified message, blocking the current thread if:
|
||||
- the channel is unbuffered
|
||||
- the channel's buffer is full
|
||||
until the channel is being read from. `send` will return
|
||||
`false` when attempting to send on an already closed channel.
|
||||
until the channel is being read from or the channel is closed. `send` will
|
||||
return `false` when attempting to send on an already closed channel.
|
||||
|
||||
**Inputs**
|
||||
- `c`: The channel
|
||||
@@ -484,8 +494,9 @@ try_send :: proc "contextless" (c: $C/Chan($T, $D), data: T) -> (ok: bool) where
|
||||
Reads a message from the channel, blocking the current thread if:
|
||||
- the channel is unbuffered
|
||||
- the channel's buffer is empty
|
||||
until the channel is being written to. `recv` will return
|
||||
`false` when attempting to receive a message on an already closed channel.
|
||||
until the channel is being written to or the channel is closed. `recv` will
|
||||
return `false` when attempting to receive a message on an already closed
|
||||
channel.
|
||||
|
||||
**Inputs**
|
||||
- `c`: The channel
|
||||
@@ -558,8 +569,8 @@ try_recv :: proc "contextless" (c: $C/Chan($T)) -> (data: T, ok: bool) where C.D
|
||||
Sends the specified message, blocking the current thread if:
|
||||
- the channel is unbuffered
|
||||
- the channel's buffer is full
|
||||
until the channel is being read from. `send_raw` will return
|
||||
`false` when attempting to send on an already closed channel.
|
||||
until the channel is being read from or the channel is closed. `send_raw` will
|
||||
return `false` when attempting to send on an already closed channel.
|
||||
|
||||
Note: The message referenced by `msg_out` must match the size
|
||||
and alignment used when the `Raw_Chan` was created.
|
||||
@@ -619,12 +630,23 @@ send_raw :: proc "contextless" (c: ^Raw_Chan, msg_in: rawptr) -> (ok: bool) {
|
||||
return false
|
||||
}
|
||||
|
||||
c.did_read = false
|
||||
defer c.did_read = false
|
||||
|
||||
mem.copy(c.unbuffered_data, msg_in, int(c.msg_size))
|
||||
|
||||
c.w_waiting += 1
|
||||
|
||||
if c.r_waiting > 0 {
|
||||
sync.signal(&c.r_cond)
|
||||
}
|
||||
|
||||
sync.wait(&c.w_cond, &c.mutex)
|
||||
|
||||
if c.closed && !c.did_read {
|
||||
return false
|
||||
}
|
||||
|
||||
ok = true
|
||||
}
|
||||
return
|
||||
@@ -634,8 +656,9 @@ send_raw :: proc "contextless" (c: ^Raw_Chan, msg_in: rawptr) -> (ok: bool) {
|
||||
Reads a message from the channel, blocking the current thread if:
|
||||
- the channel is unbuffered
|
||||
- the channel's buffer is empty
|
||||
until the channel is being written to. `recv_raw` will return
|
||||
`false` when attempting to receive a message on an already closed channel.
|
||||
until the channel is being written to or the channel is closed. `recv_raw`
|
||||
will return `false` when attempting to receive a message on an already closed
|
||||
channel.
|
||||
|
||||
Note: The location pointed to by `msg_out` must match the size
|
||||
and alignment used when the `Raw_Chan` was created.
|
||||
@@ -698,8 +721,7 @@ recv_raw :: proc "contextless" (c: ^Raw_Chan, msg_out: rawptr) -> (ok: bool) {
|
||||
} else if c.unbuffered_data != nil { // unbuffered
|
||||
sync.guard(&c.mutex)
|
||||
|
||||
for !c.closed &&
|
||||
c.w_waiting == 0 {
|
||||
for !c.closed && c.w_waiting == 0 {
|
||||
c.r_waiting += 1
|
||||
sync.wait(&c.r_cond, &c.mutex)
|
||||
c.r_waiting -= 1
|
||||
@@ -712,6 +734,7 @@ recv_raw :: proc "contextless" (c: ^Raw_Chan, msg_out: rawptr) -> (ok: bool) {
|
||||
mem.copy(msg_out, c.unbuffered_data, int(c.msg_size))
|
||||
c.w_waiting -= 1
|
||||
|
||||
c.did_read = true
|
||||
sync.signal(&c.w_cond)
|
||||
ok = true
|
||||
}
|
||||
@@ -771,7 +794,7 @@ try_send_raw :: proc "contextless" (c: ^Raw_Chan, msg_in: rawptr) -> (ok: bool)
|
||||
} else if c.unbuffered_data != nil { // unbuffered
|
||||
sync.guard(&c.mutex)
|
||||
|
||||
if c.closed {
|
||||
if c.closed || c.r_waiting - c.w_waiting <= 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -835,7 +858,7 @@ try_recv_raw :: proc "contextless" (c: ^Raw_Chan, msg_out: rawptr) -> bool {
|
||||
} else if c.unbuffered_data != nil { // unbuffered
|
||||
sync.guard(&c.mutex)
|
||||
|
||||
if c.closed || c.w_waiting == 0 {
|
||||
if c.closed || c.w_waiting - c.r_waiting <= 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1038,8 +1061,9 @@ is_closed :: proc "contextless" (c: ^Raw_Chan) -> bool {
|
||||
}
|
||||
|
||||
/*
|
||||
Returns whether a message is ready to be read, i.e.,
|
||||
if a call to `recv` or `recv_raw` would block
|
||||
Returns whether a message can be read without blocking the current
|
||||
thread. Specifically, it checks if the channel is buffered and not full,
|
||||
or if there is already a writer attempting to send a message.
|
||||
|
||||
**Inputs**
|
||||
- `c`: The channel
|
||||
@@ -1067,7 +1091,7 @@ can_recv :: proc "contextless" (c: ^Raw_Chan) -> bool {
|
||||
if is_buffered(c) {
|
||||
return c.queue.len > 0
|
||||
}
|
||||
return c.w_waiting > 0
|
||||
return c.w_waiting - c.r_waiting > 0
|
||||
}
|
||||
|
||||
|
||||
@@ -1080,7 +1104,7 @@ or if there is already a reader waiting for a message.
|
||||
- `c`: The channel
|
||||
|
||||
**Returns**
|
||||
- `true` if a message can be send, `false` otherwise
|
||||
- `true` if a message can be sent, `false` otherwise
|
||||
|
||||
Example:
|
||||
|
||||
@@ -1102,18 +1126,30 @@ can_send :: proc "contextless" (c: ^Raw_Chan) -> bool {
|
||||
if is_buffered(c) {
|
||||
return c.queue.len < c.queue.cap
|
||||
}
|
||||
return c.w_waiting == 0
|
||||
return c.r_waiting - c.w_waiting > 0
|
||||
}
|
||||
|
||||
/*
|
||||
Specifies the direction of the selected channel.
|
||||
*/
|
||||
Select_Status :: enum {
|
||||
None,
|
||||
Recv,
|
||||
Send,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Attempts to either send or receive messages on the specified channels.
|
||||
Attempts to either send or receive messages on the specified channels without blocking.
|
||||
|
||||
`select_raw` first identifies which channels have messages ready to be received
|
||||
`try_select_raw` first identifies which channels have messages ready to be received
|
||||
and which are available for sending. It then randomly selects one operation
|
||||
(either a send or receive) to perform.
|
||||
|
||||
If no channels have messages ready, the procedure is a noop.
|
||||
|
||||
Note: Each message in `send_msgs` corresponds to the send channel at the same index in `sends`.
|
||||
If the message is nil, corresponding send channel will be skipped.
|
||||
|
||||
**Inputs**
|
||||
- `recv`: A slice of channels to read from
|
||||
@@ -1145,18 +1181,18 @@ Example:
|
||||
// where the value from the read should be stored
|
||||
received_value: int
|
||||
|
||||
idx, ok := chan.select_raw(receive_chans[:], send_chans[:], msgs[:], &received_value)
|
||||
idx, ok := chan.try_select_raw(receive_chans[:], send_chans[:], msgs[:], &received_value)
|
||||
fmt.println("SELECT: ", idx, ok)
|
||||
fmt.println("RECEIVED VALUE ", received_value)
|
||||
|
||||
idx, ok = chan.select_raw(receive_chans[:], send_chans[:], msgs[:], &received_value)
|
||||
idx, ok = chan.try_select_raw(receive_chans[:], send_chans[:], msgs[:], &received_value)
|
||||
fmt.println("SELECT: ", idx, ok)
|
||||
fmt.println("RECEIVED VALUE ", received_value)
|
||||
|
||||
// closing of a channel also affects the select operation
|
||||
chan.close(c)
|
||||
|
||||
idx, ok = chan.select_raw(receive_chans[:], send_chans[:], msgs[:], &received_value)
|
||||
idx, ok = chan.try_select_raw(receive_chans[:], send_chans[:], msgs[:], &received_value)
|
||||
fmt.println("SELECT: ", idx, ok)
|
||||
}
|
||||
|
||||
@@ -1170,7 +1206,7 @@ Output:
|
||||
|
||||
*/
|
||||
@(require_results)
|
||||
select_raw :: proc "odin" (recvs: []^Raw_Chan, sends: []^Raw_Chan, send_msgs: []rawptr, recv_out: rawptr) -> (select_idx: int, ok: bool) #no_bounds_check {
|
||||
try_select_raw :: proc "odin" (recvs: []^Raw_Chan, sends: []^Raw_Chan, send_msgs: []rawptr, recv_out: rawptr) -> (select_idx: int, status: Select_Status) #no_bounds_check {
|
||||
Select_Op :: struct {
|
||||
idx: int, // local to the slice that was given
|
||||
is_recv: bool,
|
||||
@@ -1178,43 +1214,66 @@ select_raw :: proc "odin" (recvs: []^Raw_Chan, sends: []^Raw_Chan, send_msgs: []
|
||||
|
||||
candidate_count := builtin.len(recvs)+builtin.len(sends)
|
||||
candidates := ([^]Select_Op)(intrinsics.alloca(candidate_count*size_of(Select_Op), align_of(Select_Op)))
|
||||
count := 0
|
||||
|
||||
for c, i in recvs {
|
||||
if can_recv(c) {
|
||||
candidates[count] = {
|
||||
is_recv = true,
|
||||
idx = i,
|
||||
try_loop: for {
|
||||
count := 0
|
||||
|
||||
for c, i in recvs {
|
||||
if can_recv(c) {
|
||||
candidates[count] = {
|
||||
is_recv = true,
|
||||
idx = i,
|
||||
}
|
||||
count += 1
|
||||
}
|
||||
count += 1
|
||||
}
|
||||
}
|
||||
|
||||
for c, i in sends {
|
||||
if can_send(c) {
|
||||
candidates[count] = {
|
||||
is_recv = false,
|
||||
idx = i,
|
||||
for c, i in sends {
|
||||
if i > builtin.len(send_msgs)-1 || send_msgs[i] == nil {
|
||||
continue
|
||||
}
|
||||
if can_send(c) {
|
||||
candidates[count] = {
|
||||
is_recv = false,
|
||||
idx = i,
|
||||
}
|
||||
count += 1
|
||||
}
|
||||
count += 1
|
||||
}
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
return
|
||||
}
|
||||
if count == 0 {
|
||||
return -1, .None
|
||||
}
|
||||
|
||||
select_idx = rand.int_max(count) if count > 0 else 0
|
||||
when ODIN_TEST {
|
||||
if __try_select_raw_pause != nil {
|
||||
__try_select_raw_pause()
|
||||
}
|
||||
}
|
||||
|
||||
sel := candidates[select_idx]
|
||||
if sel.is_recv {
|
||||
ok = recv_raw(recvs[sel.idx], recv_out)
|
||||
} else {
|
||||
ok = send_raw(sends[sel.idx], send_msgs[sel.idx])
|
||||
candidate_idx := rand.int_max(count) if count > 0 else 0
|
||||
|
||||
sel := candidates[candidate_idx]
|
||||
if sel.is_recv {
|
||||
status = .Recv
|
||||
if !try_recv_raw(recvs[sel.idx], recv_out) {
|
||||
continue try_loop
|
||||
}
|
||||
} else {
|
||||
status = .Send
|
||||
if !try_send_raw(sends[sel.idx], send_msgs[sel.idx]) {
|
||||
continue try_loop
|
||||
}
|
||||
}
|
||||
|
||||
return sel.idx, status
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results, deprecated = "use try_select_raw")
|
||||
select_raw :: proc "odin" (recvs: []^Raw_Chan, sends: []^Raw_Chan, send_msgs: []rawptr, recv_out: rawptr) -> (select_idx: int, status: Select_Status) #no_bounds_check {
|
||||
return try_select_raw(recvs, sends, send_msgs, recv_out)
|
||||
}
|
||||
|
||||
/*
|
||||
`Raw_Queue` is a non-thread-safe queue implementation designed to store messages
|
||||
|
||||
@@ -223,6 +223,11 @@ _Proc_Bsdinfo :: struct {
|
||||
|
||||
/*--==========================================================================--*/
|
||||
|
||||
/* Get window size */
|
||||
TIOCGWINSZ :: 0x40087468
|
||||
|
||||
/*--==========================================================================--*/
|
||||
|
||||
syscall_fsync :: #force_inline proc "contextless" (fildes: c.int) -> bool {
|
||||
return !(cast(bool)intrinsics.syscall(unix_offset_syscall(.fsync), uintptr(fildes)))
|
||||
}
|
||||
@@ -275,6 +280,10 @@ syscall_lseek :: #force_inline proc "contextless" (fd: c.int, offset: i64, whenc
|
||||
return cast(i64)intrinsics.syscall(unix_offset_syscall(.lseek), uintptr(fd), uintptr(offset), uintptr(whence))
|
||||
}
|
||||
|
||||
syscall_ioctl :: #force_inline proc "contextless" (fd: c.int, request: u32, arg: rawptr) -> c.int {
|
||||
return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.ioctl), uintptr(fd), uintptr(request), uintptr(arg)))
|
||||
}
|
||||
|
||||
syscall_gettid :: #force_inline proc "contextless" () -> u64 {
|
||||
return cast(u64)intrinsics.syscall(unix_offset_syscall(.gettid))
|
||||
}
|
||||
|
||||
1780
core/sys/es/api.odin
1780
core/sys/es/api.odin
File diff suppressed because it is too large
Load Diff
5
core/sys/freebsd/constants.odin
Normal file
5
core/sys/freebsd/constants.odin
Normal file
@@ -0,0 +1,5 @@
|
||||
package sys_freebsd
|
||||
|
||||
/* Get window size */
|
||||
TIOCGWINSZ :: 0x40087468
|
||||
|
||||
@@ -34,6 +34,7 @@ init_platform :: proc() {
|
||||
} else {
|
||||
os_version.platform = .MacOS
|
||||
switch version.majorVersion {
|
||||
case 26: ws(&b, "macOS Tahoe")
|
||||
case 15: ws(&b, "macOS Sequoia")
|
||||
case 14: ws(&b, "macOS Sonoma")
|
||||
case 13: ws(&b, "macOS Ventura")
|
||||
|
||||
@@ -1618,36 +1618,39 @@ PER_HPUX :: 0x0010
|
||||
PER_MASK :: 0x00ff
|
||||
|
||||
/*
|
||||
Bits for access modes for shared memory
|
||||
Bits for SystemV IPC flags.
|
||||
|
||||
In this enum, access modes are common for any shared memory. Prefixed
|
||||
entries (i.e. `IPC_` or `SHM_`) denote flags, where `IPC_` are common flags
|
||||
for all SystemV IPC primitives, and `SHM_`, `SEM_` and `MSG_` are specific
|
||||
to shared memory segments, semaphores and message queues respectively.
|
||||
|
||||
These bits overlap, because they are meant to be used within the
|
||||
context of specific procedures. Creation flags, used for `*get` procedures,
|
||||
and usage flags used by all other IPC procedures. Do not mix creation and
|
||||
usage flags, as well as flags prefixed differently (excluding `IPC_`
|
||||
prefix).
|
||||
*/
|
||||
IPC_Mode_Bits :: enum {
|
||||
IPC_Flags_Bits :: enum {
|
||||
// Access modes for shared memory.
|
||||
WROTH = 1,
|
||||
RDOTH = 2,
|
||||
WRGRP = 4,
|
||||
RDGRP = 5,
|
||||
WRUSR = 7,
|
||||
RDUSR = 8,
|
||||
DEST = 9,
|
||||
LOCKED = 10,
|
||||
}
|
||||
|
||||
/*
|
||||
Shared memory flags bits
|
||||
*/
|
||||
IPC_Flags_Bits :: enum {
|
||||
// Creation flags for shared memory.
|
||||
IPC_CREAT = 9,
|
||||
IPC_EXCL = 10,
|
||||
IPC_NOWAIT = 11,
|
||||
// Semaphore
|
||||
SEM_UNDO = 9,
|
||||
// Shared memory
|
||||
SHM_HUGETLB = 11,
|
||||
SHM_NORESERVE = 12,
|
||||
// Usage flags for shared memory.
|
||||
IPC_NOWAIT = 11,
|
||||
SEM_UNDO = 9,
|
||||
SHM_RDONLY = 12,
|
||||
SHM_RND = 13,
|
||||
SHM_REMAP = 14,
|
||||
SHM_EXEC = 15,
|
||||
// Message queue
|
||||
MSG_NOERROR = 12,
|
||||
MSG_EXCEPT = 13,
|
||||
MSG_COPY = 14,
|
||||
|
||||
@@ -391,4 +391,7 @@ MAP_HUGE_256MB :: transmute(Map_Flags)(u32(28) << MAP_HUGE_SHIFT)
|
||||
MAP_HUGE_512MB :: transmute(Map_Flags)(u32(29) << MAP_HUGE_SHIFT)
|
||||
MAP_HUGE_1GB :: transmute(Map_Flags)(u32(30) << MAP_HUGE_SHIFT)
|
||||
MAP_HUGE_2GB :: transmute(Map_Flags)(u32(31) << MAP_HUGE_SHIFT)
|
||||
MAP_HUGE_16GB :: transmute(Map_Flags)(u32(34) << MAP_HUGE_SHIFT)
|
||||
MAP_HUGE_16GB :: transmute(Map_Flags)(u32(34) << MAP_HUGE_SHIFT)
|
||||
|
||||
/* Get window size */
|
||||
TIOCGWINSZ :: 0x5413
|
||||
|
||||
@@ -937,17 +937,12 @@ IO_Vec :: struct {
|
||||
}
|
||||
|
||||
/*
|
||||
Access mode for shared memory
|
||||
*/
|
||||
IPC_Mode :: bit_set[IPC_Mode_Bits; u32]
|
||||
|
||||
/*
|
||||
Flags used by IPC objects
|
||||
Access modes and flags used by SystemV IPC procedures.
|
||||
*/
|
||||
IPC_Flags :: bit_set[IPC_Flags_Bits; i16]
|
||||
|
||||
/*
|
||||
Permissions for IPC objects
|
||||
Permissions for SystemV IPC primitives.
|
||||
*/
|
||||
IPC_Perm :: struct {
|
||||
key: Key,
|
||||
@@ -955,7 +950,7 @@ IPC_Perm :: struct {
|
||||
gid: u32,
|
||||
cuid: u32,
|
||||
cgid: u32,
|
||||
mode: IPC_Mode,
|
||||
mode: IPC_Flags, // Only contains mode flags.
|
||||
seq: u16,
|
||||
_: [2 + 2*size_of(int)]u8,
|
||||
}
|
||||
|
||||
@@ -8,7 +8,10 @@ when ODIN_OS == .Darwin {
|
||||
} else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD {
|
||||
foreign import lib "system:dl"
|
||||
} else {
|
||||
foreign import lib "system:c"
|
||||
foreign import lib {
|
||||
"system:c",
|
||||
"system:dl",
|
||||
}
|
||||
}
|
||||
|
||||
// dlfcn.h - dynamic linking
|
||||
|
||||
@@ -31,7 +31,7 @@ Unimplemented headers:
|
||||
- iso646.h | Impossible
|
||||
- math.h | See `core:c/libc`
|
||||
- mqueue.h | Targets don't seem to have implemented it
|
||||
- regex.h | See `core:regex`
|
||||
- regex.h | See `core:text/regex`
|
||||
- search.h | Not useful in Odin
|
||||
- spawn.h | Use `fork`, `execve`, etc.
|
||||
- stdarg.h | See `core:c/libc`
|
||||
@@ -53,6 +53,8 @@ import "base:intrinsics"
|
||||
|
||||
import "core:c"
|
||||
|
||||
IS_SUPPORTED :: _IS_SUPPORTED
|
||||
|
||||
result :: enum c.int {
|
||||
// Use `errno` and `strerror` for more information.
|
||||
FAIL = -1,
|
||||
|
||||
10
core/sys/posix/posix_other.odin
Normal file
10
core/sys/posix/posix_other.odin
Normal file
@@ -0,0 +1,10 @@
|
||||
#+build !linux
|
||||
#+build !darwin
|
||||
#+build !netbsd
|
||||
#+build !openbsd
|
||||
#+build !freebsd
|
||||
#+build !haiku
|
||||
package posix
|
||||
|
||||
_IS_SUPPORTED :: false
|
||||
|
||||
5
core/sys/posix/posix_unix.odin
Normal file
5
core/sys/posix/posix_unix.odin
Normal file
@@ -0,0 +1,5 @@
|
||||
#+build linux, darwin, netbsd, openbsd, freebsd, haiku
|
||||
package posix
|
||||
|
||||
_IS_SUPPORTED :: true
|
||||
|
||||
@@ -350,4 +350,4 @@ NEWTEXTMETRICW :: struct {
|
||||
ntmAvgWidth: UINT,
|
||||
}
|
||||
|
||||
FONTENUMPROCW :: #type proc(lpelf: ^ENUMLOGFONTW, lpntm: ^NEWTEXTMETRICW, FontType: DWORD, lParam: LPARAM) -> INT
|
||||
FONTENUMPROCW :: #type proc "system" (lpelf: ^ENUMLOGFONTW, lpntm: ^NEWTEXTMETRICW, FontType: DWORD, lParam: LPARAM) -> INT
|
||||
|
||||
@@ -168,6 +168,7 @@ foreign kernel32 {
|
||||
ResumeThread :: proc(thread: HANDLE) -> DWORD ---
|
||||
GetThreadPriority :: proc(thread: HANDLE) -> c_int ---
|
||||
SetThreadPriority :: proc(thread: HANDLE, priority: c_int) -> BOOL ---
|
||||
GetThreadDescription :: proc(hThread: HANDLE, ppszThreadDescription: ^PCWSTR) -> HRESULT ---
|
||||
SetThreadDescription :: proc(hThread: HANDLE, lpThreadDescription: PCWSTR) -> HRESULT ---
|
||||
GetExitCodeThread :: proc(thread: HANDLE, exit_code: ^DWORD) -> BOOL ---
|
||||
TerminateThread :: proc(thread: HANDLE, exit_code: DWORD) -> BOOL ---
|
||||
|
||||
@@ -75,7 +75,7 @@ LANGIDFROMLCID :: #force_inline proc "contextless" (lcid: LCID) -> LANGID {
|
||||
return LANGID(lcid)
|
||||
}
|
||||
|
||||
utf8_to_utf16 :: proc(s: string, allocator := context.temp_allocator) -> []u16 {
|
||||
utf8_to_utf16_alloc :: proc(s: string, allocator := context.temp_allocator) -> []u16 {
|
||||
if len(s) < 1 {
|
||||
return nil
|
||||
}
|
||||
@@ -101,14 +101,42 @@ utf8_to_utf16 :: proc(s: string, allocator := context.temp_allocator) -> []u16 {
|
||||
}
|
||||
return text[:n]
|
||||
}
|
||||
utf8_to_wstring :: proc(s: string, allocator := context.temp_allocator) -> wstring {
|
||||
|
||||
utf8_to_utf16_buf :: proc(buf: []u16, s: string) -> []u16 {
|
||||
n1 := MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, raw_data(s), i32(len(s)), nil, 0)
|
||||
if n1 == 0 {
|
||||
return nil
|
||||
} else if int(n1) > len(buf) {
|
||||
return nil
|
||||
}
|
||||
|
||||
n1 = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, raw_data(s), i32(len(s)), raw_data(buf[:]), n1)
|
||||
if n1 == 0 {
|
||||
return nil
|
||||
} else if int(n1) > len(buf) {
|
||||
return nil
|
||||
}
|
||||
return buf[:n1]
|
||||
}
|
||||
utf8_to_utf16 :: proc{utf8_to_utf16_alloc, utf8_to_utf16_buf}
|
||||
|
||||
utf8_to_wstring_alloc :: proc(s: string, allocator := context.temp_allocator) -> wstring {
|
||||
if res := utf8_to_utf16(s, allocator); len(res) > 0 {
|
||||
return raw_data(res)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator) -> (res: string, err: runtime.Allocator_Error) {
|
||||
utf8_to_wstring_buf :: proc(buf: []u16, s: string) -> wstring {
|
||||
if res := utf8_to_utf16(buf, s); len(res) > 0 {
|
||||
return raw_data(res)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
utf8_to_wstring :: proc{utf8_to_wstring_alloc, utf8_to_wstring_buf}
|
||||
|
||||
wstring_to_utf8_alloc :: proc(s: wstring, N: int, allocator := context.temp_allocator) -> (res: string, err: runtime.Allocator_Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
if N == 0 {
|
||||
@@ -142,13 +170,49 @@ wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator)
|
||||
return string(text[:n]), nil
|
||||
}
|
||||
|
||||
utf16_to_utf8 :: proc(s: []u16, allocator := context.temp_allocator) -> (res: string, err: runtime.Allocator_Error) {
|
||||
wstring_to_utf8_buf :: proc(buf: []u8, s: wstring) -> (res: string) {
|
||||
n := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, -1, nil, 0, nil, nil)
|
||||
if n == 0 {
|
||||
return
|
||||
} else if int(n) > len(buf) {
|
||||
return
|
||||
}
|
||||
|
||||
n2 := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, -1, raw_data(buf), n, nil, nil)
|
||||
if n2 == 0 {
|
||||
return
|
||||
} else if int(n2) > len(buf) {
|
||||
return
|
||||
}
|
||||
|
||||
for i in 0..<n2 {
|
||||
if buf[i] == 0 {
|
||||
n2 = i
|
||||
break
|
||||
}
|
||||
}
|
||||
return string(buf[:n2])
|
||||
}
|
||||
|
||||
wstring_to_utf8 :: proc{wstring_to_utf8_alloc, wstring_to_utf8_buf}
|
||||
|
||||
utf16_to_utf8_alloc :: proc(s: []u16, allocator := context.temp_allocator) -> (res: string, err: runtime.Allocator_Error) {
|
||||
if len(s) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
return wstring_to_utf8(raw_data(s), len(s), allocator)
|
||||
}
|
||||
|
||||
utf16_to_utf8_buf :: proc(buf: []u8, s: []u16) -> (res: string) {
|
||||
if len(s) == 0 {
|
||||
return
|
||||
}
|
||||
return wstring_to_utf8(buf, raw_data(s))
|
||||
}
|
||||
|
||||
utf16_to_utf8 :: proc{utf16_to_utf8_alloc, utf16_to_utf8_buf}
|
||||
|
||||
|
||||
// AdvAPI32, NetAPI32 and UserENV helpers.
|
||||
|
||||
allowed_username :: proc(username: string) -> bool {
|
||||
|
||||
@@ -589,7 +589,7 @@ WAVE_FORMAT_FLAC :: 0xF1AC /* flac.sourceforge.net */
|
||||
WAVE_FORMAT_EXTENSIBLE :: 0xFFFE /* Microsoft */
|
||||
|
||||
|
||||
WAVEFORMATEX :: struct {
|
||||
WAVEFORMATEX :: struct #packed {
|
||||
wFormatTag: WORD,
|
||||
nChannels: WORD,
|
||||
nSamplesPerSec: DWORD,
|
||||
@@ -603,7 +603,7 @@ LPCWAVEFORMATEX :: ^WAVEFORMATEX
|
||||
// New wave format development should be based on the WAVEFORMATEXTENSIBLE structure.
|
||||
// WAVEFORMATEXTENSIBLE allows you to avoid having to register a new format tag with Microsoft.
|
||||
// Simply define a new GUID value for the WAVEFORMATEXTENSIBLE.SubFormat field and use WAVE_FORMAT_EXTENSIBLE in the WAVEFORMATEXTENSIBLE.Format.wFormatTag field.
|
||||
WAVEFORMATEXTENSIBLE :: struct {
|
||||
WAVEFORMATEXTENSIBLE :: struct #packed {
|
||||
using Format: WAVEFORMATEX,
|
||||
Samples: struct #raw_union {
|
||||
wValidBitsPerSample: WORD, /* bits of precision */
|
||||
|
||||
@@ -11,17 +11,17 @@ import "core:strings"
|
||||
// - [[ https://invisible-island.net/ncurses/terminfo.src.html ]]
|
||||
|
||||
get_no_color :: proc() -> bool {
|
||||
if no_color, ok := os.lookup_env("NO_COLOR"); ok {
|
||||
defer delete(no_color)
|
||||
buf: [128]u8
|
||||
if no_color, err := os.lookup_env(buf[:], "NO_COLOR"); err == nil {
|
||||
return no_color != ""
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
get_environment_color :: proc() -> Color_Depth {
|
||||
buf: [128]u8
|
||||
// `COLORTERM` is non-standard but widespread and unambiguous.
|
||||
if colorterm, ok := os.lookup_env("COLORTERM"); ok {
|
||||
defer delete(colorterm)
|
||||
if colorterm, err := os.lookup_env(buf[:], "COLORTERM"); err == nil {
|
||||
// These are the only values that are typically advertised that have
|
||||
// anything to do with color depth.
|
||||
if colorterm == "truecolor" || colorterm == "24bit" {
|
||||
@@ -29,8 +29,7 @@ get_environment_color :: proc() -> Color_Depth {
|
||||
}
|
||||
}
|
||||
|
||||
if term, ok := os.lookup_env("TERM"); ok {
|
||||
defer delete(term)
|
||||
if term, err := os.lookup_env(buf[:], "TERM"); err == nil {
|
||||
if strings.contains(term, "-truecolor") {
|
||||
return .True_Color
|
||||
}
|
||||
|
||||
@@ -57,6 +57,8 @@ SHARED_RANDOM_SEED : u64 : #config(ODIN_TEST_RANDOM_SEED, 0)
|
||||
// Set the lowest log level for this test run.
|
||||
LOG_LEVEL_DEFAULT : string : "debug" when ODIN_DEBUG else "info"
|
||||
LOG_LEVEL : string : #config(ODIN_TEST_LOG_LEVEL, LOG_LEVEL_DEFAULT)
|
||||
// Report a message at the info level when a test has changed its state.
|
||||
LOG_STATE_CHANGES : bool : #config(ODIN_TEST_LOG_STATE_CHANGES, false)
|
||||
// Show only the most necessary logging information.
|
||||
USING_SHORT_LOGS : bool : #config(ODIN_TEST_SHORT_LOGS, false)
|
||||
// Output a report of the tests to the given path.
|
||||
@@ -631,8 +633,8 @@ runner :: proc(internal_tests: []Internal_Test) -> bool {
|
||||
total_done_count += 1
|
||||
}
|
||||
|
||||
when ODIN_DEBUG {
|
||||
log.debugf("Test #%i %s.%s changed state to %v.", task_channel.test_index, it.pkg, it.name, event.new_state)
|
||||
when LOG_STATE_CHANGES {
|
||||
log.infof("Test #%i %s.%s changed state to %v.", task_channel.test_index, it.pkg, it.name, event.new_state)
|
||||
}
|
||||
|
||||
pkg.last_change_state = event.new_state
|
||||
@@ -741,7 +743,8 @@ runner :: proc(internal_tests: []Internal_Test) -> bool {
|
||||
|
||||
|
||||
if test_index, reason, ok := should_stop_test(); ok {
|
||||
#no_bounds_check report.all_test_states[test_index] = .Failed
|
||||
passed := reason == .Successful_Stop
|
||||
#no_bounds_check report.all_test_states[test_index] = .Successful if passed else .Failed
|
||||
#no_bounds_check it := internal_tests[test_index]
|
||||
#no_bounds_check pkg := report.packages_by_name[it.pkg]
|
||||
pkg.frame_ready = false
|
||||
@@ -762,7 +765,7 @@ runner :: proc(internal_tests: []Internal_Test) -> bool {
|
||||
fmt.assertf(task_data != nil, "A signal (%v) was raised to stop test #%i %s.%s, but its task data is missing.",
|
||||
reason, test_index, it.pkg, it.name)
|
||||
|
||||
if !task_data.t._fail_now_called {
|
||||
if !passed && !task_data.t._fail_now_called {
|
||||
if test_index not_in failed_test_reason_map {
|
||||
// We only write a new error message here if there wasn't one
|
||||
// already, because the message we can provide based only on
|
||||
@@ -780,7 +783,11 @@ runner :: proc(internal_tests: []Internal_Test) -> bool {
|
||||
|
||||
end_t(&task_data.t)
|
||||
|
||||
total_failure_count += 1
|
||||
if passed {
|
||||
total_success_count += 1
|
||||
} else {
|
||||
total_failure_count += 1
|
||||
}
|
||||
total_done_count += 1
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,26 @@ package testing
|
||||
import "base:runtime"
|
||||
import "core:log"
|
||||
|
||||
@(private, thread_local)
|
||||
local_test_expected_failures: struct {
|
||||
signal: i32,
|
||||
|
||||
message_count: int,
|
||||
messages: [MAX_EXPECTED_ASSERTIONS_PER_TEST]string,
|
||||
|
||||
location_count: int,
|
||||
locations: [MAX_EXPECTED_ASSERTIONS_PER_TEST]runtime.Source_Code_Location,
|
||||
}
|
||||
|
||||
@(private, thread_local)
|
||||
local_test_assertion_raised: struct {
|
||||
message: string,
|
||||
location: runtime.Source_Code_Location,
|
||||
}
|
||||
|
||||
Stop_Reason :: enum {
|
||||
Unknown,
|
||||
Successful_Stop,
|
||||
Illegal_Instruction,
|
||||
Arithmetic_Error,
|
||||
Segmentation_Fault,
|
||||
@@ -21,7 +39,12 @@ Stop_Reason :: enum {
|
||||
}
|
||||
|
||||
test_assertion_failure_proc :: proc(prefix, message: string, loc: runtime.Source_Code_Location) -> ! {
|
||||
log.fatalf("%s: %s", prefix, message, location = loc)
|
||||
if local_test_expected_failures.message_count + local_test_expected_failures.location_count > 0 {
|
||||
local_test_assertion_raised = { message, loc }
|
||||
log.debugf("%s\n\tmessage: %q\n\tlocation: %w", prefix, message, loc)
|
||||
} else {
|
||||
log.fatalf("%s: %s", prefix, message, location = loc)
|
||||
}
|
||||
runtime.trap()
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,8 @@ import "core:terminal/ansi"
|
||||
|
||||
@(private="file") stop_test_gate: sync.Mutex
|
||||
@(private="file") stop_test_index: libc.sig_atomic_t
|
||||
@(private="file") stop_test_reason: libc.sig_atomic_t
|
||||
@(private="file") stop_test_signal: libc.sig_atomic_t
|
||||
@(private="file") stop_test_passed: libc.sig_atomic_t
|
||||
@(private="file") stop_test_alert: libc.sig_atomic_t
|
||||
|
||||
@(private="file", thread_local)
|
||||
@@ -99,7 +100,30 @@ This is a dire bug and should be reported to the Odin developers.
|
||||
|
||||
if sync.mutex_guard(&stop_test_gate) {
|
||||
intrinsics.atomic_store(&stop_test_index, local_test_index)
|
||||
intrinsics.atomic_store(&stop_test_reason, cast(libc.sig_atomic_t)sig)
|
||||
intrinsics.atomic_store(&stop_test_signal, cast(libc.sig_atomic_t)sig)
|
||||
passed: bool
|
||||
check_passing: {
|
||||
if location := local_test_assertion_raised.location; location != {} {
|
||||
for i in 0..<local_test_expected_failures.location_count {
|
||||
if local_test_expected_failures.locations[i] == location {
|
||||
passed = true
|
||||
break check_passing
|
||||
}
|
||||
}
|
||||
}
|
||||
if message := local_test_assertion_raised.message; message != "" {
|
||||
for i in 0..<local_test_expected_failures.message_count {
|
||||
if local_test_expected_failures.messages[i] == message {
|
||||
passed = true
|
||||
break check_passing
|
||||
}
|
||||
}
|
||||
}
|
||||
if signal := local_test_expected_failures.signal; signal == sig {
|
||||
passed = true
|
||||
}
|
||||
}
|
||||
intrinsics.atomic_store(&stop_test_passed, cast(libc.sig_atomic_t)passed)
|
||||
intrinsics.atomic_store(&stop_test_alert, 1)
|
||||
|
||||
for {
|
||||
@@ -154,11 +178,15 @@ _should_stop_test :: proc() -> (test_index: int, reason: Stop_Reason, ok: bool)
|
||||
intrinsics.atomic_store(&stop_test_alert, 0)
|
||||
|
||||
test_index = cast(int)intrinsics.atomic_load(&stop_test_index)
|
||||
switch intrinsics.atomic_load(&stop_test_reason) {
|
||||
case libc.SIGFPE: reason = .Arithmetic_Error
|
||||
case libc.SIGILL: reason = .Illegal_Instruction
|
||||
case libc.SIGSEGV: reason = .Segmentation_Fault
|
||||
case SIGTRAP: reason = .Unhandled_Trap
|
||||
if cast(bool)intrinsics.atomic_load(&stop_test_passed) {
|
||||
reason = .Successful_Stop
|
||||
} else {
|
||||
switch intrinsics.atomic_load(&stop_test_signal) {
|
||||
case libc.SIGFPE: reason = .Arithmetic_Error
|
||||
case libc.SIGILL: reason = .Illegal_Instruction
|
||||
case libc.SIGSEGV: reason = .Segmentation_Fault
|
||||
case SIGTRAP: reason = .Unhandled_Trap
|
||||
}
|
||||
}
|
||||
ok = true
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ import "core:mem"
|
||||
_ :: reflect // alias reflect to nothing to force visibility for -vet
|
||||
_ :: mem // in case TRACKING_MEMORY is not enabled
|
||||
|
||||
MAX_EXPECTED_ASSERTIONS_PER_TEST :: 5
|
||||
|
||||
// IMPORTANT NOTE: Compiler requires this layout
|
||||
Test_Signature :: proc(^T)
|
||||
|
||||
@@ -155,3 +157,74 @@ set_fail_timeout :: proc(t: ^T, duration: time.Duration, loc := #caller_location
|
||||
location = loc,
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
Let the test runner know that it should expect an assertion failure from a
|
||||
specific location in the source code for this test.
|
||||
|
||||
In the event that an assertion fails, a debug message will be logged with its
|
||||
exact message and location in a copyable format to make it convenient to write
|
||||
tests which use this API.
|
||||
|
||||
This procedure may be called up to 5 times with different locations.
|
||||
|
||||
This is a limitation for the sake of simplicity in the implementation, and you
|
||||
should consider breaking up your tests into smaller procedures if you need to
|
||||
check for asserts in more than 2 places.
|
||||
*/
|
||||
expect_assert_from :: proc(t: ^T, expected_place: runtime.Source_Code_Location, caller_loc := #caller_location) {
|
||||
count := local_test_expected_failures.location_count
|
||||
if count == MAX_EXPECTED_ASSERTIONS_PER_TEST {
|
||||
panic("This test cannot handle that many expected assertions based on matching the location.", caller_loc)
|
||||
}
|
||||
local_test_expected_failures.locations[count] = expected_place
|
||||
local_test_expected_failures.location_count += 1
|
||||
}
|
||||
|
||||
/*
|
||||
Let the test runner know that it should expect an assertion failure with a
|
||||
specific message for this test.
|
||||
|
||||
In the event that an assertion fails, a debug message will be logged with its
|
||||
exact message and location in a copyable format to make it convenient to write
|
||||
tests which use this API.
|
||||
|
||||
This procedure may be called up to 5 times with different messages.
|
||||
|
||||
This is a limitation for the sake of simplicity in the implementation, and you
|
||||
should consider breaking up your tests into smaller procedures if you need to
|
||||
check for more than a couple different assertion messages.
|
||||
*/
|
||||
expect_assert_message :: proc(t: ^T, expected_message: string, caller_loc := #caller_location) {
|
||||
count := local_test_expected_failures.message_count
|
||||
if count == MAX_EXPECTED_ASSERTIONS_PER_TEST {
|
||||
panic("This test cannot handle that many expected assertions based on matching the message.", caller_loc)
|
||||
}
|
||||
local_test_expected_failures.messages[count] = expected_message
|
||||
local_test_expected_failures.message_count += 1
|
||||
}
|
||||
|
||||
expect_assert :: proc {
|
||||
expect_assert_from,
|
||||
expect_assert_message,
|
||||
}
|
||||
|
||||
/*
|
||||
Let the test runner know that it should expect a signal to be raised within
|
||||
this test.
|
||||
|
||||
This API is for advanced users, as arbitrary signals will not be caught; only
|
||||
the ones already handled by the test runner, such as
|
||||
|
||||
- SIGINT, (interrupt)
|
||||
- SIGTERM, (polite termination)
|
||||
- SIGILL, (illegal instruction)
|
||||
- SIGFPE, (arithmetic error)
|
||||
- SIGSEGV, and (segmentation fault)
|
||||
- SIGTRAP (only on POSIX systems). (trap / debug trap)
|
||||
|
||||
Note that only one signal can be expected per test.
|
||||
*/
|
||||
expect_signal :: proc(t: ^T, #any_int sig: i32) {
|
||||
local_test_expected_failures.signal = sig
|
||||
}
|
||||
|
||||
@@ -28,8 +28,6 @@ Creation_Error :: enum {
|
||||
Expected_Delimiter,
|
||||
// An unknown letter was supplied to `create_by_user` after the last delimiter.
|
||||
Unknown_Flag,
|
||||
// An unsupported flag was supplied.
|
||||
Unsupported_Flag,
|
||||
}
|
||||
|
||||
Error :: union #shared_nil {
|
||||
@@ -69,7 +67,6 @@ Regular_Expression :: struct {
|
||||
|
||||
/*
|
||||
An iterator to repeatedly match a pattern against a string, to be used with `*_iterator` procedures.
|
||||
Note: Does not handle `.Multiline` properly.
|
||||
*/
|
||||
Match_Iterator :: struct {
|
||||
regex: Regular_Expression,
|
||||
@@ -436,7 +433,6 @@ match_with_preallocated_capture :: proc(
|
||||
|
||||
/*
|
||||
Iterate over a `Match_Iterator` and return successive captures.
|
||||
Note: Does not handle `.Multiline` properly.
|
||||
|
||||
Inputs:
|
||||
- it: Pointer to the `Match_Iterator` to iterate over.
|
||||
|
||||
@@ -39,7 +39,8 @@ Type representing a thread handle and the associated with that thread data.
|
||||
Thread :: struct {
|
||||
using specific: Thread_Os_Specific,
|
||||
flags: bit_set[Thread_State; u8],
|
||||
// Thread ID.
|
||||
// Thread ID. Depending on the platform, may start out as 0 (zero) until the thread
|
||||
// has had a chance to run.
|
||||
id: int,
|
||||
// The thread procedure.
|
||||
procedure: Thread_Proc,
|
||||
@@ -257,8 +258,10 @@ to execute. The thread will have priority specified by the `priority` parameter.
|
||||
|
||||
If `self_cleanup` is specified, after the thread finishes the execution of the
|
||||
`fn` procedure, the resources associated with the thread are going to be
|
||||
automatically freed. **Do not** dereference the `^Thread` pointer, if this
|
||||
flag is specified.
|
||||
automatically freed.
|
||||
|
||||
**Do not** dereference the `^Thread` pointer, if this flag is specified.
|
||||
That includes calling `join`, which needs to dereference ^Thread`.
|
||||
|
||||
**IMPORTANT**: If `init_context` is specified and the default temporary allocator
|
||||
is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
|
||||
@@ -290,8 +293,10 @@ to execute. The thread will have priority specified by the `priority` parameter.
|
||||
|
||||
If `self_cleanup` is specified, after the thread finishes the execution of the
|
||||
`fn` procedure, the resources associated with the thread are going to be
|
||||
automatically freed. **Do not** dereference the `^Thread` pointer, if this
|
||||
flag is specified.
|
||||
automatically freed.
|
||||
|
||||
**Do not** dereference the `^Thread` pointer, if this flag is specified.
|
||||
That includes calling `join`, which needs to dereference ^Thread`.
|
||||
|
||||
**IMPORTANT**: If `init_context` is specified and the default temporary allocator
|
||||
is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
|
||||
@@ -327,8 +332,10 @@ to execute. The thread will have priority specified by the `priority` parameter.
|
||||
|
||||
If `self_cleanup` is specified, after the thread finishes the execution of the
|
||||
`fn` procedure, the resources associated with the thread are going to be
|
||||
automatically freed. **Do not** dereference the `^Thread` pointer, if this
|
||||
flag is specified.
|
||||
automatically freed.
|
||||
|
||||
**Do not** dereference the `^Thread` pointer, if this flag is specified.
|
||||
That includes calling `join`, which needs to dereference ^Thread`.
|
||||
|
||||
**IMPORTANT**: If `init_context` is specified and the default temporary allocator
|
||||
is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
|
||||
@@ -370,8 +377,10 @@ to execute. The thread will have priority specified by the `priority` parameter.
|
||||
|
||||
If `self_cleanup` is specified, after the thread finishes the execution of the
|
||||
`fn` procedure, the resources associated with the thread are going to be
|
||||
automatically freed. **Do not** dereference the `^Thread` pointer, if this
|
||||
flag is specified.
|
||||
automatically freed.
|
||||
|
||||
**Do not** dereference the `^Thread` pointer, if this flag is specified.
|
||||
That includes calling `join`, which needs to dereference ^Thread`.
|
||||
|
||||
**IMPORTANT**: If `init_context` is specified and the default temporary allocator
|
||||
is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
|
||||
@@ -419,8 +428,10 @@ to execute. The thread will have priority specified by the `priority` parameter.
|
||||
|
||||
If `self_cleanup` is specified, after the thread finishes the execution of the
|
||||
`fn` procedure, the resources associated with the thread are going to be
|
||||
automatically freed. **Do not** dereference the `^Thread` pointer, if this
|
||||
flag is specified.
|
||||
automatically freed.
|
||||
|
||||
**Do not** dereference the `^Thread` pointer, if this flag is specified.
|
||||
That includes calling `join`, which needs to dereference ^Thread`.
|
||||
|
||||
**IMPORTANT**: If `init_context` is specified and the default temporary allocator
|
||||
is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
|
||||
@@ -470,8 +481,10 @@ to execute. The thread will have priority specified by the `priority` parameter.
|
||||
|
||||
If `self_cleanup` is specified, after the thread finishes the execution of the
|
||||
`fn` procedure, the resources associated with the thread are going to be
|
||||
automatically freed. **Do not** dereference the `^Thread` pointer, if this
|
||||
flag is specified.
|
||||
automatically freed.
|
||||
|
||||
**Do not** dereference the `^Thread` pointer, if this flag is specified.
|
||||
That includes calling `join`, which needs to dereference ^Thread`.
|
||||
|
||||
**IMPORTANT**: If `init_context` is specified and the default temporary allocator
|
||||
is used, the thread procedure needs to call `runtime.default_temp_allocator_destroy()`
|
||||
|
||||
@@ -120,6 +120,20 @@ pool_join :: proc(pool: ^Pool) {
|
||||
|
||||
yield()
|
||||
|
||||
unstarted_count: int
|
||||
for t in pool.threads {
|
||||
flags := intrinsics.atomic_load(&t.flags)
|
||||
if .Started not_in flags {
|
||||
unstarted_count += 1
|
||||
}
|
||||
}
|
||||
|
||||
// most likely the user forgot to call `pool_start`
|
||||
// exit here, so we don't hang forever
|
||||
if len(pool.threads) == unstarted_count {
|
||||
return
|
||||
}
|
||||
|
||||
started_count: int
|
||||
for started_count < len(pool.threads) {
|
||||
started_count = 0
|
||||
|
||||
@@ -29,14 +29,10 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
|
||||
|
||||
t.id = sync.current_thread_id()
|
||||
|
||||
if .Started not_in sync.atomic_load(&t.flags) {
|
||||
for (.Started not_in sync.atomic_load(&t.flags)) {
|
||||
sync.wait(&t.start_ok)
|
||||
}
|
||||
|
||||
if .Joined in sync.atomic_load(&t.flags) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Enable thread's cancelability.
|
||||
// NOTE(laytan): Darwin does not correctly/fully support all of this, not doing this does
|
||||
// actually make pthread_cancel work in the capacity of my tests, while executing this would
|
||||
@@ -124,7 +120,6 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
|
||||
free(thread, thread.creation_allocator)
|
||||
return nil
|
||||
}
|
||||
|
||||
return thread
|
||||
}
|
||||
|
||||
@@ -149,10 +144,13 @@ _join :: proc(t: ^Thread) {
|
||||
|
||||
// Prevent non-started threads from blocking main thread with initial wait
|
||||
// condition.
|
||||
if .Started not_in sync.atomic_load(&t.flags) {
|
||||
for (.Started not_in sync.atomic_load(&t.flags)) {
|
||||
_start(t)
|
||||
}
|
||||
|
||||
posix.pthread_join(t.unix_thread, nil)
|
||||
|
||||
t.flags += {.Joined}
|
||||
}
|
||||
|
||||
_join_multiple :: proc(threads: ..^Thread) {
|
||||
|
||||
@@ -13,6 +13,7 @@ Thread_Os_Specific :: struct {
|
||||
win32_thread: win32.HANDLE,
|
||||
win32_thread_id: win32.DWORD,
|
||||
mutex: sync.Mutex,
|
||||
start_ok: sync.Sema,
|
||||
}
|
||||
|
||||
_thread_priority_map := [Thread_Priority]i32{
|
||||
@@ -27,12 +28,10 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
|
||||
__windows_thread_entry_proc :: proc "system" (t_: rawptr) -> win32.DWORD {
|
||||
t := (^Thread)(t_)
|
||||
|
||||
if .Joined in sync.atomic_load(&t.flags) {
|
||||
return 0
|
||||
for (.Started not_in sync.atomic_load(&t.flags)) {
|
||||
sync.wait(&t.start_ok)
|
||||
}
|
||||
|
||||
t.id = sync.current_thread_id()
|
||||
|
||||
{
|
||||
init_context := t.init_context
|
||||
|
||||
@@ -76,6 +75,7 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
|
||||
thread.procedure = procedure
|
||||
thread.win32_thread = win32_thread
|
||||
thread.win32_thread_id = win32_thread_id
|
||||
thread.id = int(win32_thread_id)
|
||||
|
||||
ok := win32.SetThreadPriority(win32_thread, _thread_priority_map[priority])
|
||||
assert(ok == true)
|
||||
@@ -103,16 +103,15 @@ _join :: proc(t: ^Thread) {
|
||||
return
|
||||
}
|
||||
|
||||
t.flags += {.Joined}
|
||||
|
||||
if .Started not_in t.flags {
|
||||
t.flags += {.Started}
|
||||
win32.ResumeThread(t.win32_thread)
|
||||
for (.Started not_in sync.atomic_load(&t.flags)) {
|
||||
_start(t)
|
||||
}
|
||||
|
||||
win32.WaitForSingleObject(t.win32_thread, win32.INFINITE)
|
||||
win32.CloseHandle(t.win32_thread)
|
||||
t.win32_thread = win32.INVALID_HANDLE
|
||||
|
||||
t.flags += {.Joined}
|
||||
}
|
||||
|
||||
_join_multiple :: proc(threads: ..^Thread) {
|
||||
@@ -136,6 +135,7 @@ _join_multiple :: proc(threads: ..^Thread) {
|
||||
for t in threads {
|
||||
win32.CloseHandle(t.win32_thread)
|
||||
t.win32_thread = win32.INVALID_HANDLE
|
||||
t.flags += {.Joined}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -577,12 +577,7 @@ parse_tzif :: proc(_buffer: []u8, region_name: string, allocator := context.allo
|
||||
footer_str := string(buffer[:end_idx])
|
||||
|
||||
// UTC is a special case, we don't need to alloc
|
||||
if len(local_time_types) == 1 {
|
||||
name := cstring(raw_data(timezone_string_table[local_time_types[0].idx:]))
|
||||
if name != "UTC" {
|
||||
return
|
||||
}
|
||||
|
||||
if len(local_time_types) == 1 && local_time_types[0].utoff == 0 {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
|
||||
@@ -45,4 +45,7 @@ package all
|
||||
@(require) import stbi "vendor:stb/image"
|
||||
@(require) import "vendor:stb/rect_pack"
|
||||
@(require) import "vendor:stb/truetype"
|
||||
@(require) import "vendor:stb/vorbis"
|
||||
@(require) import "vendor:stb/vorbis"
|
||||
|
||||
|
||||
@(require) import "vendor:kb_text_shape"
|
||||
|
||||
@@ -540,6 +540,7 @@ gb_internal void report_os_info() {
|
||||
}
|
||||
|
||||
switch (major) {
|
||||
case 26: gb_printf("macOS Tahoe"); break;
|
||||
case 15: gb_printf("macOS Sequoia"); break;
|
||||
case 14: gb_printf("macOS Sonoma"); break;
|
||||
case 13: gb_printf("macOS Ventura"); break;
|
||||
|
||||
@@ -385,6 +385,13 @@ enum LinkerChoice : i32 {
|
||||
Linker_COUNT,
|
||||
};
|
||||
|
||||
enum SourceCodeLocationInfo : u8 {
|
||||
SourceCodeLocationInfo_Normal = 0,
|
||||
SourceCodeLocationInfo_Obfuscated = 1,
|
||||
SourceCodeLocationInfo_Filename = 2,
|
||||
SourceCodeLocationInfo_None = 3,
|
||||
};
|
||||
|
||||
String linker_choices[Linker_COUNT] = {
|
||||
str_lit("default"),
|
||||
str_lit("lld"),
|
||||
@@ -512,7 +519,7 @@ struct BuildContext {
|
||||
|
||||
bool dynamic_map_calls;
|
||||
|
||||
bool obfuscate_source_code_locations;
|
||||
SourceCodeLocationInfo source_code_location_info;
|
||||
|
||||
bool min_link_libs;
|
||||
|
||||
|
||||
@@ -148,6 +148,11 @@ gb_internal bool does_require_msgSend_stret(Type *return_type) {
|
||||
if (return_type == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (build_context.metrics.os != TargetOs_darwin) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (build_context.metrics.arch == TargetArch_i386 || build_context.metrics.arch == TargetArch_amd64) {
|
||||
i64 struct_limit = type_size_of(t_uintptr) << 1;
|
||||
return type_size_of(return_type) > struct_limit;
|
||||
|
||||
@@ -1334,12 +1334,16 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
||||
has_instrumentation = false;
|
||||
e->flags |= EntityFlag_Require;
|
||||
} else if (ac.instrumentation_enter) {
|
||||
init_core_source_code_location(ctx->checker);
|
||||
if (!is_valid_instrumentation_call(e->type)) {
|
||||
init_core_source_code_location(ctx->checker);
|
||||
gbString s = type_to_string(e->type);
|
||||
error(e->token, "@(instrumentation_enter) procedures must have the type '%s', got %s", instrumentation_proc_type_str, s);
|
||||
gb_string_free(s);
|
||||
}
|
||||
if ((e->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) == 0) {
|
||||
error(e->token, "@(instrumentation_enter) procedures must be declared at the file scope");
|
||||
}
|
||||
MUTEX_GUARD(&ctx->info->instrumentation_mutex);
|
||||
if (ctx->info->instrumentation_enter_entity != nullptr) {
|
||||
error(e->token, "@(instrumentation_enter) has already been set");
|
||||
@@ -1356,6 +1360,9 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
||||
error(e->token, "@(instrumentation_exit) procedures must have the type '%s', got %s", instrumentation_proc_type_str, s);
|
||||
gb_string_free(s);
|
||||
}
|
||||
if ((e->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) == 0) {
|
||||
error(e->token, "@(instrumentation_exit) procedures must be declared at the file scope");
|
||||
}
|
||||
MUTEX_GUARD(&ctx->info->instrumentation_mutex);
|
||||
if (ctx->info->instrumentation_exit_entity != nullptr) {
|
||||
error(e->token, "@(instrumentation_exit) has already been set");
|
||||
@@ -1370,6 +1377,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
||||
e->Procedure.has_instrumentation = has_instrumentation;
|
||||
|
||||
e->Procedure.no_sanitize_address = ac.no_sanitize_address;
|
||||
e->Procedure.no_sanitize_memory = ac.no_sanitize_memory;
|
||||
|
||||
e->deprecated_message = ac.deprecated_message;
|
||||
e->warning_message = ac.warning_message;
|
||||
|
||||
@@ -2424,27 +2424,27 @@ gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o
|
||||
Type *s = src->Array.elem;
|
||||
Type *d = dst->Slice.elem;
|
||||
if (are_types_identical(s, d)) {
|
||||
error_line("\tSuggestion: the array expression may be sliced with %s[:]\n", a);
|
||||
error_line("\tSuggestion: The array expression may be sliced with %s[:]\n", a);
|
||||
}
|
||||
} else if (is_type_dynamic_array(src) && is_type_slice(dst)) {
|
||||
Type *s = src->DynamicArray.elem;
|
||||
Type *d = dst->Slice.elem;
|
||||
if (are_types_identical(s, d)) {
|
||||
error_line("\tSuggestion: the dynamic array expression may be sliced with %s[:]\n", a);
|
||||
error_line("\tSuggestion: The dynamic array expression may be sliced with %s[:]\n", a);
|
||||
}
|
||||
}else if (are_types_identical(src, dst) && !are_types_identical(o->type, type)) {
|
||||
error_line("\tSuggestion: the expression may be directly casted to type %s\n", b);
|
||||
error_line("\tSuggestion: The expression may be directly casted to type %s\n", b);
|
||||
} else if (are_types_identical(src, t_string) && is_type_u8_slice(dst)) {
|
||||
error_line("\tSuggestion: a string may be transmuted to %s\n", b);
|
||||
error_line("\t This is an UNSAFE operation as string data is assumed to be immutable, \n");
|
||||
error_line("\tSuggestion: A string may be transmuted to %s\n", b);
|
||||
error_line("\t This is an UNSAFE operation as string data is assumed to be immutable,\n");
|
||||
error_line("\t whereas slices in general are assumed to be mutable.\n");
|
||||
} else if (is_type_u8_slice(src) && are_types_identical(dst, t_string) && o->mode != Addressing_Constant) {
|
||||
error_line("\tSuggestion: the expression may be casted to %s\n", b);
|
||||
error_line("\tSuggestion: The expression may be casted to %s\n", b);
|
||||
} else if (check_integer_exceed_suggestion(c, o, type, max_bit_size)) {
|
||||
return;
|
||||
} else if (is_expr_inferred_fixed_array(c->type_hint_expr) && is_type_array_like(type) && is_type_array_like(o->type)) {
|
||||
gbString s = expr_to_string(c->type_hint_expr);
|
||||
error_line("\tSuggestion: make sure that `%s` is attached to the compound literal directly\n", s);
|
||||
error_line("\tSuggestion: Make sure that `%s` is attached to the compound literal directly\n", s);
|
||||
gb_string_free(s);
|
||||
} else if (is_type_pointer(type) &&
|
||||
o->mode == Addressing_Variable &&
|
||||
@@ -3086,126 +3086,106 @@ gb_internal void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *nod
|
||||
GB_ASSERT(node->kind == Ast_BinaryExpr);
|
||||
ast_node(be, BinaryExpr, node);
|
||||
|
||||
ExactValue x_val = {};
|
||||
if (x->mode == Addressing_Constant) {
|
||||
x_val = exact_value_to_integer(x->value);
|
||||
}
|
||||
|
||||
bool x_is_untyped = is_type_untyped(x->type);
|
||||
if (!(is_type_integer(x->type) || (x_is_untyped && x_val.kind == ExactValue_Integer))) {
|
||||
gbString err_str = expr_to_string(x->expr);
|
||||
error(node, "Shifted operand '%s' must be an integer", err_str);
|
||||
gb_string_free(err_str);
|
||||
x->mode = Addressing_Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_type_unsigned(y->type)) {
|
||||
|
||||
} else if (is_type_untyped(y->type)) {
|
||||
bool y_is_untyped = is_type_untyped(y->type);
|
||||
if (y_is_untyped) {
|
||||
convert_to_typed(c, y, t_untyped_integer);
|
||||
if (y->mode == Addressing_Invalid) {
|
||||
x->mode = Addressing_Invalid;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
gbString err_str = expr_to_string(y->expr);
|
||||
error(node, "Shift amount '%s' must be an unsigned integer", err_str);
|
||||
gb_string_free(err_str);
|
||||
} else if (!is_type_unsigned(y->type)) {
|
||||
gbString y_str = expr_to_string(y->expr);
|
||||
error(y->expr, "Shift amount '%s' must be an unsigned integer", y_str);
|
||||
gb_string_free(y_str);
|
||||
x->mode = Addressing_Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
bool x_is_untyped = is_type_untyped(x->type);
|
||||
if (!(x_is_untyped || is_type_integer(x->type))) {
|
||||
gbString x_str = expr_to_string(x->expr);
|
||||
error(x->expr, "Shifted operand '%s' must be an integer", x_str);
|
||||
gb_string_free(x_str);
|
||||
x->mode = Addressing_Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
if (y->mode == Addressing_Constant) {
|
||||
if (big_int_is_neg(&y->value.value_integer)) {
|
||||
gbString y_str = expr_to_string(y->expr);
|
||||
error(y->expr, "Shift amount '%s' cannot be negative", y_str);
|
||||
gb_string_free(y_str);
|
||||
x->mode = Addressing_Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
BigInt max_shift = {};
|
||||
big_int_from_u64(&max_shift, MAX_BIG_INT_SHIFT);
|
||||
|
||||
if (big_int_cmp(&y->value.value_integer, &max_shift) > 0) {
|
||||
gbString y_str = expr_to_string(y->expr);
|
||||
error(y->expr, "Shift amount '%s' must be <= %u", y_str, MAX_BIG_INT_SHIFT);
|
||||
gb_string_free(y_str);
|
||||
x->mode = Addressing_Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
if (x->mode == Addressing_Constant) {
|
||||
if (x_is_untyped) {
|
||||
convert_to_typed(c, x, t_untyped_integer);
|
||||
if (x->mode == Addressing_Invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
x->expr = node;
|
||||
x->value = exact_value_shift(be->op.kind, exact_value_to_integer(x->value), exact_value_to_integer(y->value));
|
||||
|
||||
if (x->mode == Addressing_Constant) {
|
||||
if (y->mode == Addressing_Constant) {
|
||||
ExactValue y_val = exact_value_to_integer(y->value);
|
||||
if (y_val.kind != ExactValue_Integer) {
|
||||
gbString err_str = expr_to_string(y->expr);
|
||||
error(node, "Shift amount '%s' must be an unsigned integer", err_str);
|
||||
gb_string_free(err_str);
|
||||
x->mode = Addressing_Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
BigInt max_shift = {};
|
||||
big_int_from_u64(&max_shift, MAX_BIG_INT_SHIFT);
|
||||
|
||||
if (big_int_cmp(&y_val.value_integer, &max_shift) > 0) {
|
||||
gbString err_str = expr_to_string(y->expr);
|
||||
error(node, "Shift amount too large: '%s'", err_str);
|
||||
gb_string_free(err_str);
|
||||
x->mode = Addressing_Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_type_integer(x->type)) {
|
||||
// NOTE(bill): It could be an untyped float but still representable
|
||||
// as an integer
|
||||
x->type = t_untyped_integer;
|
||||
}
|
||||
|
||||
x->expr = node;
|
||||
x->value = exact_value_shift(be->op.kind, x_val, y_val);
|
||||
x->value = exact_value_shift(be->op.kind, x->value, y->value);
|
||||
|
||||
check_is_expressible(c, x, x->type);
|
||||
|
||||
if (is_type_typed(x->type)) {
|
||||
check_is_expressible(c, x, x->type);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
TokenPos pos = ast_token(x->expr).pos;
|
||||
if (x_is_untyped) {
|
||||
if (x->expr != nullptr) {
|
||||
x->expr->tav.is_lhs = true;
|
||||
}
|
||||
x->mode = Addressing_Value;
|
||||
if (type_hint) {
|
||||
if (is_type_integer(type_hint)) {
|
||||
convert_to_typed(c, x, type_hint);
|
||||
} else {
|
||||
gbString x_str = expr_to_string(x->expr);
|
||||
gbString to_type = type_to_string(type_hint);
|
||||
error(node, "Conversion of shifted operand '%s' to '%s' is not allowed", x_str, to_type);
|
||||
gb_string_free(x_str);
|
||||
gb_string_free(to_type);
|
||||
x->mode = Addressing_Invalid;
|
||||
}
|
||||
} else if (!is_type_integer(x->type)) {
|
||||
gbString x_str = expr_to_string(x->expr);
|
||||
error(node, "Non-integer shifted operand '%s' is not allowed", x_str);
|
||||
gb_string_free(x_str);
|
||||
x->mode = Addressing_Invalid;
|
||||
}
|
||||
// x->value = x_val;
|
||||
return;
|
||||
if (y_is_untyped) {
|
||||
convert_to_typed(c, y, t_uint);
|
||||
}
|
||||
}
|
||||
|
||||
if (y->mode == Addressing_Constant && big_int_is_neg(&y->value.value_integer)) {
|
||||
gbString err_str = expr_to_string(y->expr);
|
||||
error(node, "Shift amount cannot be negative: '%s'", err_str);
|
||||
gb_string_free(err_str);
|
||||
}
|
||||
|
||||
if (!is_type_integer(x->type)) {
|
||||
gbString err_str = expr_to_string(x->expr);
|
||||
error(node, "Shift operand '%s' must be an integer", err_str);
|
||||
gb_string_free(err_str);
|
||||
x->mode = Addressing_Invalid;
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_type_untyped(y->type)) {
|
||||
convert_to_typed(c, y, t_uint);
|
||||
if (x->mode == Addressing_Constant) {
|
||||
if (x_is_untyped) {
|
||||
if (type_hint) {
|
||||
if (is_type_integer(type_hint)) {
|
||||
convert_to_typed(c, x, type_hint);
|
||||
} else if (is_type_any(type_hint)) {
|
||||
convert_to_typed(c, x, default_type(t_untyped_integer));
|
||||
} else {
|
||||
gbString x_str = expr_to_string(x->expr);
|
||||
gbString type_str = type_to_string(type_hint);
|
||||
error(x->expr, "Shifted operand '%s' cannot convert to non-integer type '%s'", x_str, type_str);
|
||||
gb_string_free(x_str);
|
||||
gb_string_free(type_str);
|
||||
x->mode = Addressing_Invalid;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
check_is_expressible(c, x, default_type(t_untyped_integer));
|
||||
}
|
||||
if (x->mode == Addressing_Invalid) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
x->mode = Addressing_Value;
|
||||
}
|
||||
|
||||
x->mode = Addressing_Value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) {
|
||||
if (check_is_assignable_to(c, operand, y)) {
|
||||
return true;
|
||||
@@ -5884,12 +5864,12 @@ typedef u32 UnpackFlags;
|
||||
enum UnpackFlag : u32 {
|
||||
UnpackFlag_None = 0,
|
||||
UnpackFlag_AllowOk = 1<<0,
|
||||
UnpackFlag_IsVariadic = 1<<1,
|
||||
UnpackFlag_AllowUndef = 1<<2,
|
||||
UnpackFlag_AllowUndef = 1<<1,
|
||||
};
|
||||
|
||||
|
||||
gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array<Operand> *operands, Slice<Ast *> const &rhs_arguments, UnpackFlags flags) {
|
||||
gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array<Operand> *operands, Slice<Ast *> const &rhs_arguments, UnpackFlags flags,
|
||||
isize variadic_index = -1) {
|
||||
auto const &add_dependencies_from_unpacking = [](CheckerContext *c, Entity **lhs, isize lhs_count, isize tuple_index, isize tuple_count) -> isize {
|
||||
if (lhs == nullptr || c->decl == nullptr) {
|
||||
return tuple_count;
|
||||
@@ -5914,11 +5894,14 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize
|
||||
return tuple_count;
|
||||
};
|
||||
|
||||
|
||||
bool allow_ok = (flags & UnpackFlag_AllowOk) != 0;
|
||||
bool is_variadic = (flags & UnpackFlag_IsVariadic) != 0;
|
||||
bool allow_undef = (flags & UnpackFlag_AllowUndef) != 0;
|
||||
|
||||
bool is_variadic = variadic_index > -1;
|
||||
if (!is_variadic) {
|
||||
variadic_index = lhs_count;
|
||||
}
|
||||
|
||||
bool optional_ok = false;
|
||||
isize tuple_index = 0;
|
||||
for (Ast *rhs : rhs_arguments) {
|
||||
@@ -5934,26 +5917,18 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize
|
||||
|
||||
Type *type_hint = nullptr;
|
||||
|
||||
|
||||
if (lhs != nullptr && tuple_index < lhs_count) {
|
||||
// NOTE(bill): override DeclInfo for dependency
|
||||
Entity *e = lhs[tuple_index];
|
||||
if (e != nullptr) {
|
||||
type_hint = e->type;
|
||||
if (e->flags & EntityFlag_Ellipsis) {
|
||||
GB_ASSERT(is_type_slice(e->type));
|
||||
GB_ASSERT(e->type->kind == Type_Slice);
|
||||
type_hint = e->type->Slice.elem;
|
||||
if (lhs != nullptr) {
|
||||
if (tuple_index < variadic_index) {
|
||||
// NOTE(bill): override DeclInfo for dependency
|
||||
Entity *e = lhs[tuple_index];
|
||||
if (e != nullptr) {
|
||||
type_hint = e->type;
|
||||
}
|
||||
}
|
||||
} else if (lhs != nullptr && tuple_index >= lhs_count && is_variadic) {
|
||||
// NOTE(bill): override DeclInfo for dependency
|
||||
Entity *e = lhs[lhs_count-1];
|
||||
if (e != nullptr) {
|
||||
type_hint = e->type;
|
||||
if (e->flags & EntityFlag_Ellipsis) {
|
||||
} else if (is_variadic) {
|
||||
Entity *e = lhs[variadic_index];
|
||||
if (e != nullptr) {
|
||||
GB_ASSERT(e->flags & EntityFlag_Ellipsis);
|
||||
GB_ASSERT(is_type_slice(e->type));
|
||||
GB_ASSERT(e->type->kind == Type_Slice);
|
||||
type_hint = e->type->Slice.elem;
|
||||
}
|
||||
}
|
||||
@@ -6493,17 +6468,16 @@ gb_internal bool is_call_expr_field_value(AstCallExpr *ce) {
|
||||
return ce->args[0]->kind == Ast_FieldValue;
|
||||
}
|
||||
|
||||
gb_internal Entity **populate_proc_parameter_list(CheckerContext *c, Type *proc_type, isize *lhs_count_, bool *is_variadic) {
|
||||
gb_internal Entity **populate_proc_parameter_list(CheckerContext *c, Type *proc_type, isize *lhs_count_) {
|
||||
Entity **lhs = nullptr;
|
||||
isize lhs_count = -1;
|
||||
|
||||
if (proc_type == nullptr) {
|
||||
if (proc_type == nullptr || proc_type == t_invalid) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GB_ASSERT(is_type_proc(proc_type));
|
||||
TypeProc *pt = &base_type(proc_type)->Proc;
|
||||
*is_variadic = pt->variadic;
|
||||
|
||||
if (!pt->is_polymorphic || pt->is_poly_specialized) {
|
||||
if (pt->params != nullptr) {
|
||||
@@ -6697,6 +6671,9 @@ gb_internal bool check_call_arguments_single(CheckerContext *c, Ast *call, Opera
|
||||
|
||||
GB_ASSERT(proc_type != nullptr);
|
||||
proc_type = base_type(proc_type);
|
||||
if (proc_type == t_invalid) {
|
||||
return false;
|
||||
}
|
||||
GB_ASSERT(proc_type->kind == Type_Proc);
|
||||
|
||||
CallArgumentError err = check_call_arguments_internal(c, call, e, proc_type, positional_operands, named_operands, show_error_mode, data);
|
||||
@@ -6830,7 +6807,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
|
||||
|
||||
Entity **lhs = nullptr;
|
||||
isize lhs_count = -1;
|
||||
bool is_variadic = false;
|
||||
i32 variadic_index = -1;
|
||||
|
||||
auto positional_operands = array_make<Operand>(heap_allocator(), 0, 0);
|
||||
auto named_operands = array_make<Operand>(heap_allocator(), 0, 0);
|
||||
@@ -6839,9 +6816,14 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
|
||||
|
||||
if (procs.count == 1) {
|
||||
Entity *e = procs[0];
|
||||
|
||||
lhs = populate_proc_parameter_list(c, e->type, &lhs_count, &is_variadic);
|
||||
check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None);
|
||||
Type *pt = base_type(e->type);
|
||||
if (pt != nullptr && is_type_proc(pt)) {
|
||||
lhs = populate_proc_parameter_list(c, pt, &lhs_count);
|
||||
if (pt->Proc.variadic) {
|
||||
variadic_index = pt->Proc.variadic_index;
|
||||
}
|
||||
}
|
||||
check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, UnpackFlag_None, variadic_index);
|
||||
|
||||
if (check_named_arguments(c, e->type, named_args, &named_operands, true)) {
|
||||
check_call_arguments_single(c, call, operand,
|
||||
@@ -6901,11 +6883,30 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
|
||||
}
|
||||
lhs[param_index] = e;
|
||||
}
|
||||
|
||||
for (Entity *p : procs) {
|
||||
Type *pt = base_type(p->type);
|
||||
if (!(pt != nullptr && is_type_proc(pt))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pt->Proc.is_polymorphic) {
|
||||
if (variadic_index == -1) {
|
||||
variadic_index = pt->Proc.variadic_index;
|
||||
} else if (variadic_index != pt->Proc.variadic_index) {
|
||||
variadic_index = -1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
variadic_index = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None);
|
||||
check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, UnpackFlag_None, variadic_index);
|
||||
|
||||
for_array(i, named_args) {
|
||||
Ast *arg = named_args[i];
|
||||
@@ -7343,13 +7344,16 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op
|
||||
defer (array_free(&named_operands));
|
||||
|
||||
if (positional_args.count > 0) {
|
||||
isize lhs_count = -1;
|
||||
bool is_variadic = false;
|
||||
Entity **lhs = nullptr;
|
||||
isize lhs_count = -1;
|
||||
i32 variadic_index = -1;
|
||||
if (pt != nullptr) {
|
||||
lhs = populate_proc_parameter_list(c, proc_type, &lhs_count, &is_variadic);
|
||||
lhs = populate_proc_parameter_list(c, proc_type, &lhs_count);
|
||||
if (pt->variadic) {
|
||||
variadic_index = pt->variadic_index;
|
||||
}
|
||||
}
|
||||
check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None);
|
||||
check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, UnpackFlag_None, variadic_index);
|
||||
}
|
||||
|
||||
if (named_args.count > 0) {
|
||||
@@ -8756,23 +8760,52 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A
|
||||
String name = bd->name.string;
|
||||
if (name == "file") {
|
||||
String file = get_file_path_string(bd->token.pos.file_id);
|
||||
if (build_context.obfuscate_source_code_locations) {
|
||||
switch (build_context.source_code_location_info) {
|
||||
case SourceCodeLocationInfo_Normal:
|
||||
break;
|
||||
case SourceCodeLocationInfo_Obfuscated:
|
||||
file = obfuscate_string(file, "F");
|
||||
break;
|
||||
case SourceCodeLocationInfo_Filename:
|
||||
file = last_path_element(file);
|
||||
break;
|
||||
case SourceCodeLocationInfo_None:
|
||||
file = str_lit("");
|
||||
break;
|
||||
}
|
||||
o->type = t_untyped_string;
|
||||
o->value = exact_value_string(file);
|
||||
} else if (name == "directory") {
|
||||
String file = get_file_path_string(bd->token.pos.file_id);
|
||||
String path = dir_from_path(file);
|
||||
if (build_context.obfuscate_source_code_locations) {
|
||||
switch (build_context.source_code_location_info) {
|
||||
case SourceCodeLocationInfo_Normal:
|
||||
break;
|
||||
case SourceCodeLocationInfo_Obfuscated:
|
||||
path = obfuscate_string(path, "D");
|
||||
break;
|
||||
case SourceCodeLocationInfo_Filename:
|
||||
path = last_path_element(path);
|
||||
break;
|
||||
case SourceCodeLocationInfo_None:
|
||||
path = str_lit("");
|
||||
break;
|
||||
}
|
||||
o->type = t_untyped_string;
|
||||
o->value = exact_value_string(path);
|
||||
} else if (name == "line") {
|
||||
i32 line = bd->token.pos.line;
|
||||
if (build_context.obfuscate_source_code_locations) {
|
||||
switch (build_context.source_code_location_info) {
|
||||
case SourceCodeLocationInfo_Normal:
|
||||
break;
|
||||
case SourceCodeLocationInfo_Obfuscated:
|
||||
line = obfuscate_i32(line);
|
||||
break;
|
||||
case SourceCodeLocationInfo_Filename:
|
||||
break;
|
||||
case SourceCodeLocationInfo_None:
|
||||
line = 0;
|
||||
break;
|
||||
}
|
||||
o->type = t_untyped_integer;
|
||||
o->value = exact_value_i64(line);
|
||||
@@ -8783,8 +8816,17 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A
|
||||
o->value = exact_value_string(str_lit(""));
|
||||
} else {
|
||||
String p = c->proc_name;
|
||||
if (build_context.obfuscate_source_code_locations) {
|
||||
switch (build_context.source_code_location_info) {
|
||||
case SourceCodeLocationInfo_Normal:
|
||||
break;
|
||||
case SourceCodeLocationInfo_Obfuscated:
|
||||
p = obfuscate_string(p, "P");
|
||||
break;
|
||||
case SourceCodeLocationInfo_Filename:
|
||||
break;
|
||||
case SourceCodeLocationInfo_None:
|
||||
p = str_lit("");
|
||||
break;
|
||||
}
|
||||
o->type = t_untyped_string;
|
||||
o->value = exact_value_string(p);
|
||||
@@ -11087,7 +11129,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
|
||||
case_ast_node(u, Uninit, node);
|
||||
o->mode = Addressing_Value;
|
||||
o->type = t_untyped_uninit;
|
||||
error(node, "Use of --- outside of variable declaration");
|
||||
error(node, "Global variables will always be zeroed if left unassigned, --- is disallowed");
|
||||
case_end;
|
||||
|
||||
|
||||
@@ -11319,6 +11361,13 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
|
||||
node->viral_state_flags |= de->expr->viral_state_flags;
|
||||
|
||||
if (o->mode == Addressing_Invalid) {
|
||||
o->mode = Addressing_Invalid;
|
||||
o->expr = node;
|
||||
return kind;
|
||||
} else if (o->mode == Addressing_Type) {
|
||||
gbString str = expr_to_string(o->expr);
|
||||
error(o->expr, "Cannot dereference '%s' because it is a type", str);
|
||||
|
||||
o->mode = Addressing_Invalid;
|
||||
o->expr = node;
|
||||
return kind;
|
||||
|
||||
@@ -418,7 +418,7 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) {
|
||||
|
||||
|
||||
|
||||
gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, Operand *rhs) {
|
||||
gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, Operand *rhs, String context_name) {
|
||||
if (rhs->mode == Addressing_Invalid) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -430,7 +430,7 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O
|
||||
|
||||
Ast *node = unparen_expr(lhs->expr);
|
||||
|
||||
check_no_copy_assignment(*rhs, str_lit("assignment"));
|
||||
check_no_copy_assignment(*rhs, context_name);
|
||||
|
||||
// NOTE(bill): Ignore assignments to '_'
|
||||
if (is_blank_ident(node)) {
|
||||
@@ -630,7 +630,7 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O
|
||||
ctx->bit_field_bit_size = lhs_e->Variable.bit_field_bit_size;
|
||||
}
|
||||
|
||||
check_assignment(ctx, rhs, assignment_type, str_lit("assignment"));
|
||||
check_assignment(ctx, rhs, assignment_type, context_name);
|
||||
|
||||
ctx->bit_field_bit_size = prev_bit_field_bit_size;
|
||||
|
||||
@@ -2418,7 +2418,7 @@ gb_internal void check_assign_stmt(CheckerContext *ctx, Ast *node) {
|
||||
|
||||
isize lhs_count = as->lhs.count;
|
||||
if (lhs_count == 0) {
|
||||
error(as->op, "Missing lhs in assignment statement");
|
||||
error(as->op, "Missing LHS in assignment statement");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2451,7 +2451,7 @@ gb_internal void check_assign_stmt(CheckerContext *ctx, Ast *node) {
|
||||
if (lhs_to_ignore[i]) {
|
||||
continue;
|
||||
}
|
||||
check_assignment_variable(ctx, &lhs_operands[i], &rhs_operands[i]);
|
||||
check_assignment_variable(ctx, &lhs_operands[i], &rhs_operands[i], str_lit("assignment"));
|
||||
}
|
||||
if (lhs_count != rhs_count) {
|
||||
error(as->lhs[0], "Assignment count mismatch '%td' = '%td'", lhs_count, rhs_count);
|
||||
@@ -2461,11 +2461,11 @@ gb_internal void check_assign_stmt(CheckerContext *ctx, Ast *node) {
|
||||
// a += 1; // Single-sided
|
||||
Token op = as->op;
|
||||
if (as->lhs.count != 1 || as->rhs.count != 1) {
|
||||
error(op, "Assignment operation '%.*s' requires single-valued expressions", LIT(op.string));
|
||||
error(op, "Assignment operator '%.*s' requires single-valued operands", LIT(op.string));
|
||||
return;
|
||||
}
|
||||
if (!gb_is_between(op.kind, Token__AssignOpBegin+1, Token__AssignOpEnd-1)) {
|
||||
error(op, "Unknown Assignment operation '%.*s'", LIT(op.string));
|
||||
error(op, "Unknown assignment operator '%.*s'", LIT(op.string));
|
||||
return;
|
||||
}
|
||||
Operand lhs = {Addressing_Invalid};
|
||||
@@ -2474,15 +2474,16 @@ gb_internal void check_assign_stmt(CheckerContext *ctx, Ast *node) {
|
||||
ast_node(be, BinaryExpr, binary_expr);
|
||||
be->op = op;
|
||||
be->op.kind = cast(TokenKind)(cast(i32)be->op.kind - (Token_AddEq - Token_Add));
|
||||
// NOTE(bill): Only use the first one will be used
|
||||
// NOTE(bill): Only use the first one will be used
|
||||
be->left = as->lhs[0];
|
||||
be->right = as->rhs[0];
|
||||
|
||||
check_expr(ctx, &lhs, as->lhs[0]);
|
||||
check_binary_expr(ctx, &rhs, binary_expr, nullptr, true);
|
||||
if (rhs.mode != Addressing_Invalid) {
|
||||
// NOTE(bill): Only use the first one will be used
|
||||
check_assignment_variable(ctx, &lhs, &rhs);
|
||||
be->op.string = substring(be->op.string, 0, be->op.string.len - 1);
|
||||
rhs.expr = binary_expr;
|
||||
check_assignment_variable(ctx, &lhs, &rhs, str_lit("assignment operation"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user