mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-15 10:22:23 -07:00
Merge tag 'dev-2025-07'
This commit is contained in:
+56
-56
@@ -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 vendor/sdl3 -vet -strict-style -disallow-do -target:netbsd_amd64 -no-entry-point
|
||||
./odin check vendor/sdl3 -vet -strict-style -disallow-do -target:netbsd_arm64 -no-entry-point
|
||||
./odin 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 vendor/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
|
||||
- name: Odin check vendor/sdl3
|
||||
run: ./odin check vendor/sdl3 -strict-style -vet -disallow-do -no-entry-point
|
||||
run: ./odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do
|
||||
- name: Odin check examples/all/sdl3
|
||||
run: ./odin check examples/all/sdl3 -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -no-entry-point
|
||||
- name: 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,37 +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 -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -no-entry-point -target:js_wasm32
|
||||
- name: Odin check examples/all for js_wasm64p32
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -no-entry-point -target:js_wasm64p32
|
||||
|
||||
- name: Odin check vendor/sdl3 for Linux i386
|
||||
- name: Odin check examples/all/sdl3 for Linux i386
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check vendor/sdl3 -vet -strict-style -disallow-do -no-entry-point -target:linux_i386
|
||||
- name: Odin check vendor/sdl3 for 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_i386
|
||||
- name: Odin check examples/all/sdl3 for Linux arm64
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: ./odin check vendor/sdl3 -vet -strict-style -disallow-do -no-entry-point -target:linux_arm64
|
||||
- name: Odin check vendor/sdl3 for FreeBSD amd64
|
||||
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 vendor/sdl3 -vet -strict-style -disallow-do -no-entry-point -target:freebsd_amd64
|
||||
- name: Odin check vendor/sdl3 for OpenBSD 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 vendor/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
|
||||
@@ -202,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
|
||||
- name: Odin check vendor/sdl3
|
||||
odin check examples/all -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do
|
||||
- name: Odin check examples/all/sdl3
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin check vendor/sdl3 -vet -strict-style -disallow-do -no-entry-point
|
||||
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: |
|
||||
@@ -251,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: |
|
||||
@@ -293,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 vendor/sdl3
|
||||
run: ./odin check vendor/sdl3 -target:linux_riscv64 -vet -strict-style -disallow-do -no-entry-point
|
||||
- name: Odin check examples/all/sdl3
|
||||
run: ./odin check examples/all/sdl3 -target:linux_riscv64 -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -no-entry-point
|
||||
|
||||
- name: Install riscv64 toolchain and qemu
|
||||
run: sudo apt-get install -y qemu-user qemu-user-static gcc-12-riscv64-linux-gnu libc6-riscv64-cross
|
||||
|
||||
- name: Odin run
|
||||
run: ./odin run examples/demo -vet -strict-style -disallow-do -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static" -no-rpath
|
||||
run: ./odin run examples/demo -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static" -no-rpath
|
||||
|
||||
- name: Odin run -debug
|
||||
run: ./odin run examples/demo -debug -vet -strict-style -disallow-do -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static" -no-rpath
|
||||
run: ./odin run examples/demo -debug -vet -vet-tabs -strict-style -vet-style -warnings-as-errors -disallow-do -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static" -no-rpath
|
||||
|
||||
- name: 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
|
||||
|
||||
@@ -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
|
||||
@@ -277,6 +277,7 @@ odin
|
||||
*.bin
|
||||
demo.bin
|
||||
libLLVM*.so*
|
||||
*.a
|
||||
|
||||
# shared collection
|
||||
shared/
|
||||
|
||||
@@ -169,6 +169,7 @@ type_is_union :: proc($T: typeid) -> bool ---
|
||||
type_is_enum :: proc($T: typeid) -> bool ---
|
||||
type_is_proc :: proc($T: typeid) -> bool ---
|
||||
type_is_bit_set :: proc($T: typeid) -> bool ---
|
||||
type_is_bit_field :: proc($T: typeid) -> bool ---
|
||||
type_is_simd_vector :: proc($T: typeid) -> bool ---
|
||||
type_is_matrix :: proc($T: typeid) -> bool ---
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -649,6 +649,9 @@ append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (n: i
|
||||
|
||||
@builtin
|
||||
inject_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||
when !ODIN_NO_BOUNDS_CHECK {
|
||||
ensure(index >= 0, "Index must be positive.", loc)
|
||||
}
|
||||
if array == nil {
|
||||
return
|
||||
}
|
||||
@@ -667,6 +670,9 @@ inject_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcas
|
||||
|
||||
@builtin
|
||||
inject_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||
when !ODIN_NO_BOUNDS_CHECK {
|
||||
ensure(index >= 0, "Index must be positive.", loc)
|
||||
}
|
||||
if array == nil {
|
||||
return
|
||||
}
|
||||
@@ -690,6 +696,9 @@ inject_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadca
|
||||
|
||||
@builtin
|
||||
inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, #any_int index: int, arg: string, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||
when !ODIN_NO_BOUNDS_CHECK {
|
||||
ensure(index >= 0, "Index must be positive.", loc)
|
||||
}
|
||||
if array == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -169,7 +169,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
|
||||
}
|
||||
@@ -234,7 +234,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
|
||||
}
|
||||
}
|
||||
@@ -308,7 +308,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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
Executable
+78
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
comment: false
|
||||
@@ -139,9 +139,6 @@ Context_Memory_Input :: struct #packed {
|
||||
}
|
||||
when size_of(rawptr) == 8 {
|
||||
#assert(size_of(Context_Memory_Input) == 64)
|
||||
} else {
|
||||
// e.g. `-target:windows_i386`
|
||||
#assert(size_of(Context_Memory_Input) == 52)
|
||||
}
|
||||
|
||||
Context_Stream_Input :: struct #packed {
|
||||
|
||||
+282
-58
@@ -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
|
||||
|
||||
@@ -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 ""
|
||||
}
|
||||
@@ -385,17 +385,17 @@ to_diagnostic_format_writer :: proc(w: io.Writer, val: Value, padding := 0) -> i
|
||||
// which we want for the diagnostic format.
|
||||
case f16:
|
||||
buf: [64]byte
|
||||
str := strconv.append_float(buf[:], f64(v), 'f', 2*size_of(f16), 8*size_of(f16))
|
||||
str := strconv.write_float(buf[:], f64(v), 'f', 2*size_of(f16), 8*size_of(f16))
|
||||
if str[0] == '+' && str != "+Inf" { str = str[1:] }
|
||||
io.write_string(w, str) or_return
|
||||
case f32:
|
||||
buf: [128]byte
|
||||
str := strconv.append_float(buf[:], f64(v), 'f', 2*size_of(f32), 8*size_of(f32))
|
||||
str := strconv.write_float(buf[:], f64(v), 'f', 2*size_of(f32), 8*size_of(f32))
|
||||
if str[0] == '+' && str != "+Inf" { str = str[1:] }
|
||||
io.write_string(w, str) or_return
|
||||
case f64:
|
||||
buf: [256]byte
|
||||
str := strconv.append_float(buf[:], f64(v), 'f', 2*size_of(f64), 8*size_of(f64))
|
||||
str := strconv.write_float(buf[:], f64(v), 'f', 2*size_of(f64), 8*size_of(f64))
|
||||
if str[0] == '+' && str != "+Inf" { str = str[1:] }
|
||||
io.write_string(w, str) or_return
|
||||
|
||||
|
||||
@@ -612,6 +612,42 @@ _marshal_into_encoder :: proc(e: Encoder, v: any, ti: ^runtime.Type_Info) -> (er
|
||||
case:
|
||||
panic("unknown bit_size size")
|
||||
}
|
||||
case runtime.Type_Info_Matrix:
|
||||
count := info.column_count * info.elem_stride
|
||||
err_conv(_encode_u64(e, u64(count), .Array)) or_return
|
||||
|
||||
if impl, ok := _tag_implementations_type[info.elem.id]; ok {
|
||||
for i in 0..<count {
|
||||
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
||||
impl->marshal(e, any{rawptr(data), info.elem.id}) or_return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
elem_ti := runtime.type_info_core(type_info_of(info.elem.id))
|
||||
for i in 0..<count {
|
||||
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
||||
_marshal_into_encoder(e, any{rawptr(data), info.elem.id}, elem_ti) or_return
|
||||
}
|
||||
return
|
||||
|
||||
case runtime.Type_Info_Simd_Vector:
|
||||
err_conv(_encode_u64(e, u64(info.count), .Array)) or_return
|
||||
|
||||
if impl, ok := _tag_implementations_type[info.elem.id]; ok {
|
||||
for i in 0..<info.count {
|
||||
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
||||
impl->marshal(e, any{rawptr(data), info.elem.id}) or_return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
elem_ti := runtime.type_info_core(type_info_of(info.elem.id))
|
||||
for i in 0..<info.count {
|
||||
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
||||
_marshal_into_encoder(e, any{rawptr(data), info.elem.id}, elem_ti) or_return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
return _unsupported(v.id, nil)
|
||||
|
||||
@@ -591,6 +591,31 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
||||
if out_of_space { return _unsupported(v, hdr) }
|
||||
return
|
||||
|
||||
case reflect.Type_Info_Matrix:
|
||||
count := t.column_count * t.elem_stride
|
||||
length, _ := err_conv(_decode_len_container(d, add)) or_return
|
||||
if length > count {
|
||||
return _unsupported(v, hdr)
|
||||
}
|
||||
|
||||
da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, length, allocator }
|
||||
|
||||
out_of_space := assign_array(d, &da, t.elem, length, growable=false) or_return
|
||||
if out_of_space { return _unsupported(v, hdr) }
|
||||
return
|
||||
|
||||
case reflect.Type_Info_Simd_Vector:
|
||||
length, _ := err_conv(_decode_len_container(d, add)) or_return
|
||||
if length > t.count {
|
||||
return _unsupported(v, hdr)
|
||||
}
|
||||
|
||||
da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, length, allocator }
|
||||
|
||||
out_of_space := assign_array(d, &da, t.elem, length, growable=false) or_return
|
||||
if out_of_space { return _unsupported(v, hdr) }
|
||||
return
|
||||
|
||||
case: return _unsupported(v, hdr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,13 +108,13 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
if opt.write_uint_as_hex && (opt.spec == .JSON5 || opt.spec == .MJSON) {
|
||||
switch i in a {
|
||||
case u8, u16, u32, u64, u128:
|
||||
s = strconv.append_bits_128(buf[:], u, 16, info.signed, 8*ti.size, "0123456789abcdef", { .Prefix })
|
||||
s = strconv.write_bits_128(buf[:], u, 16, info.signed, 8*ti.size, "0123456789abcdef", { .Prefix })
|
||||
|
||||
case:
|
||||
s = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
|
||||
s = strconv.write_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
|
||||
}
|
||||
} else {
|
||||
s = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
|
||||
s = strconv.write_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
|
||||
}
|
||||
|
||||
io.write_string(w, s) or_return
|
||||
@@ -286,7 +286,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
case runtime.Type_Info_Integer:
|
||||
buf: [40]byte
|
||||
u := cast_any_int_to_u128(ka)
|
||||
name = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*kti.size, "0123456789", nil)
|
||||
name = strconv.write_bits_128(buf[:], u, 10, info.signed, 8*kti.size, "0123456789", nil)
|
||||
|
||||
opt_write_key(w, opt, name) or_return
|
||||
case: return .Unsupported_Type
|
||||
|
||||
@@ -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"
|
||||
|
||||
+16
-4
@@ -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
|
||||
|
||||
|
||||
+15
-15
@@ -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)
|
||||
|
||||
+4
-4
@@ -1122,7 +1122,7 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d
|
||||
flags: strconv.Int_Flags
|
||||
if fi.hash && !fi.zero && start == 0 { flags += {.Prefix} }
|
||||
if fi.plus { flags += {.Plus} }
|
||||
s := strconv.append_bits(buf[start:], u, base, is_signed, bit_size, digits, flags)
|
||||
s := strconv.write_bits(buf[start:], u, base, is_signed, bit_size, digits, flags)
|
||||
prev_zero := fi.zero
|
||||
defer fi.zero = prev_zero
|
||||
fi.zero = false
|
||||
@@ -1207,7 +1207,7 @@ _fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: i
|
||||
flags: strconv.Int_Flags
|
||||
if fi.hash && !fi.zero && start == 0 { flags += {.Prefix} }
|
||||
if fi.plus { flags += {.Plus} }
|
||||
s := strconv.append_bits_128(buf[start:], u, base, is_signed, bit_size, digits, flags)
|
||||
s := strconv.write_bits_128(buf[start:], u, base, is_signed, bit_size, digits, flags)
|
||||
|
||||
if fi.hash && fi.zero && fi.indent == 0 {
|
||||
c: byte = 0
|
||||
@@ -1272,7 +1272,7 @@ _fmt_memory :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, units: st
|
||||
}
|
||||
|
||||
buf: [256]byte
|
||||
str := strconv.append_float(buf[:], amt, 'f', prec, 64)
|
||||
str := strconv.write_float(buf[:], amt, 'f', prec, 64)
|
||||
|
||||
// Add the unit at the end.
|
||||
copy(buf[len(str):], units[off:off+unit_len])
|
||||
@@ -1424,7 +1424,7 @@ _fmt_float_as :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune, float_fmt: b
|
||||
buf: [386]byte
|
||||
|
||||
// Can return "NaN", "+Inf", "-Inf", "+<value>", "-<value>".
|
||||
str := strconv.append_float(buf[:], v, float_fmt, prec, bit_size)
|
||||
str := strconv.write_float(buf[:], v, float_fmt, prec, bit_size)
|
||||
|
||||
if !fi.plus {
|
||||
// Strip sign from "+<value>" but not "+Inf".
|
||||
|
||||
+8
-8
@@ -22,12 +22,12 @@ write_ptr_at :: proc(w: Writer_At, p: rawptr, byte_size: int, offset: i64, n_wri
|
||||
|
||||
write_u64 :: proc(w: Writer, i: u64, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) {
|
||||
buf: [32]byte
|
||||
s := strconv.append_bits(buf[:], i, base, false, 64, strconv.digits, nil)
|
||||
s := strconv.write_bits(buf[:], i, base, false, 64, strconv.digits, nil)
|
||||
return write_string(w, s, n_written)
|
||||
}
|
||||
write_i64 :: proc(w: Writer, i: i64, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) {
|
||||
buf: [32]byte
|
||||
s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil)
|
||||
s := strconv.write_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil)
|
||||
return write_string(w, s, n_written)
|
||||
}
|
||||
|
||||
@@ -40,18 +40,18 @@ write_int :: proc(w: Writer, i: int, base: int = 10, n_written: ^int = nil) -> (
|
||||
|
||||
write_u128 :: proc(w: Writer, i: u128, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) {
|
||||
buf: [39]byte
|
||||
s := strconv.append_bits_128(buf[:], i, base, false, 128, strconv.digits, nil)
|
||||
s := strconv.write_bits_128(buf[:], i, base, false, 128, strconv.digits, nil)
|
||||
return write_string(w, s, n_written)
|
||||
}
|
||||
write_i128 :: proc(w: Writer, i: i128, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) {
|
||||
buf: [40]byte
|
||||
s := strconv.append_bits_128(buf[:], u128(i), base, true, 128, strconv.digits, nil)
|
||||
s := strconv.write_bits_128(buf[:], u128(i), base, true, 128, strconv.digits, nil)
|
||||
return write_string(w, s, n_written)
|
||||
}
|
||||
write_f16 :: proc(w: Writer, val: f16, n_written: ^int = nil) -> (n: int, err: Error) {
|
||||
buf: [386]byte
|
||||
|
||||
str := strconv.append_float(buf[1:], f64(val), 'f', 2*size_of(val), 8*size_of(val))
|
||||
str := strconv.write_float(buf[1:], f64(val), 'f', 2*size_of(val), 8*size_of(val))
|
||||
s := buf[:len(str)+1]
|
||||
if s[1] == '+' || s[1] == '-' {
|
||||
s = s[1:]
|
||||
@@ -67,7 +67,7 @@ write_f16 :: proc(w: Writer, val: f16, n_written: ^int = nil) -> (n: int, err: E
|
||||
write_f32 :: proc(w: Writer, val: f32, n_written: ^int = nil) -> (n: int, err: Error) {
|
||||
buf: [386]byte
|
||||
|
||||
str := strconv.append_float(buf[1:], f64(val), 'f', 2*size_of(val), 8*size_of(val))
|
||||
str := strconv.write_float(buf[1:], f64(val), 'f', 2*size_of(val), 8*size_of(val))
|
||||
s := buf[:len(str)+1]
|
||||
if s[1] == '+' || s[1] == '-' {
|
||||
s = s[1:]
|
||||
@@ -83,7 +83,7 @@ write_f32 :: proc(w: Writer, val: f32, n_written: ^int = nil) -> (n: int, err: E
|
||||
write_f64 :: proc(w: Writer, val: f64, n_written: ^int = nil) -> (n: int, err: Error) {
|
||||
buf: [386]byte
|
||||
|
||||
str := strconv.append_float(buf[1:], val, 'f', 2*size_of(val), 8*size_of(val))
|
||||
str := strconv.write_float(buf[1:], val, 'f', 2*size_of(val), 8*size_of(val))
|
||||
s := buf[:len(str)+1]
|
||||
if s[1] == '+' || s[1] == '-' {
|
||||
s = s[1:]
|
||||
@@ -130,7 +130,7 @@ write_encoded_rune :: proc(w: Writer, r: rune, write_quote := true, n_written: ^
|
||||
write_string(w, `\x`, &n) or_return
|
||||
|
||||
buf: [2]byte
|
||||
s := strconv.append_bits(buf[:], u64(r), 16, true, 64, strconv.digits, nil)
|
||||
s := strconv.write_bits(buf[:], u64(r), 16, true, 64, strconv.digits, nil)
|
||||
switch len(s) {
|
||||
case 0:
|
||||
write_string(w, "00", &n) or_return
|
||||
|
||||
@@ -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, }
|
||||
|
||||
|
||||
@@ -1370,8 +1370,8 @@ _private_int_div_recursive :: proc(quotient, remainder, a, b: ^Int, allocator :=
|
||||
|
||||
/*
|
||||
Slower bit-bang division... also smaller.
|
||||
Prefer `_int_div_school` for speed.
|
||||
*/
|
||||
@(deprecated="Use `_int_div_school`, it's 3.5x faster.")
|
||||
_private_int_div_small :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: Error) {
|
||||
|
||||
ta, tb, tq, q := &Int{}, &Int{}, &Int{}, &Int{}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ round :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> Backing {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
append :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string {
|
||||
write :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string {
|
||||
Integer_Width :: 8*size_of(Backing) - Fraction_Width
|
||||
|
||||
x := x
|
||||
@@ -124,16 +124,16 @@ append :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string {
|
||||
|
||||
when size_of(Backing) < 16 {
|
||||
T :: u64
|
||||
append_uint :: strconv.append_uint
|
||||
write_uint :: strconv.write_uint
|
||||
} else {
|
||||
T :: u128
|
||||
append_uint :: strconv.append_u128
|
||||
write_uint :: strconv.write_u128
|
||||
}
|
||||
|
||||
integer := T(x.i) >> Fraction_Width
|
||||
fraction := T(x.i) & (1<<Fraction_Width - 1)
|
||||
|
||||
s := append_uint(buf[i:], integer, 10)
|
||||
s := write_uint(buf[i:], integer, 10)
|
||||
i += len(s)
|
||||
if fraction != 0 {
|
||||
buf[i] = '.'
|
||||
@@ -155,7 +155,7 @@ append :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string {
|
||||
@(require_results)
|
||||
to_string :: proc(x: $T/Fixed($Backing, $Fraction_Width), allocator := context.allocator) -> string {
|
||||
buf: [48]byte
|
||||
s := append(buf[:], x)
|
||||
s := write(buf[:], x)
|
||||
str := make([]byte, len(s), allocator)
|
||||
copy(str, s)
|
||||
return string(str)
|
||||
@@ -294,3 +294,8 @@ _power_of_two_table := [129]string{
|
||||
"85070591730234615865843651857942052864",
|
||||
"170141183460469231731687303715884105728",
|
||||
}
|
||||
|
||||
@(deprecated="Use write instead")
|
||||
append :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string {
|
||||
return write(dst, x)
|
||||
}
|
||||
|
||||
+412
-248
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
|
||||
}
|
||||
|
||||
+12
-12
@@ -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,
|
||||
|
||||
+29
-47
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -125,7 +125,7 @@ percent_encode :: proc(s: string, allocator := context.allocator) -> string {
|
||||
bytes, n := utf8.encode_rune(ch)
|
||||
for byte in bytes[:n] {
|
||||
buf: [2]u8 = ---
|
||||
t := strconv.append_int(buf[:], i64(byte), 16)
|
||||
t := strconv.write_int(buf[:], i64(byte), 16)
|
||||
strings.write_rune(&b, '%')
|
||||
strings.write_string(&b, t)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+54
-1
@@ -4,6 +4,7 @@ import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
import "core:io"
|
||||
import "core:strconv"
|
||||
import "core:strings"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
|
||||
@@ -57,7 +58,7 @@ write_encoded_rune :: proc(f: Handle, r: rune) -> (n: int, err: Error) {
|
||||
if r < 32 {
|
||||
if wrap(write_string(f, "\\x"), &n, &err) { return }
|
||||
b: [2]byte
|
||||
s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil)
|
||||
s := strconv.write_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil)
|
||||
switch len(s) {
|
||||
case 0: if wrap(write_string(f, "00"), &n, &err) { return }
|
||||
case 1: if wrap(write_rune(f, '0'), &n, &err) { return }
|
||||
@@ -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)
|
||||
}
|
||||
+79
-5
@@ -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
|
||||
|
||||
+19
-14
@@ -27,6 +27,9 @@ General_Error :: enum u32 {
|
||||
|
||||
Pattern_Has_Separator,
|
||||
|
||||
No_HOME_Variable,
|
||||
Env_Var_Not_Found,
|
||||
|
||||
Unsupported,
|
||||
}
|
||||
|
||||
@@ -59,20 +62,22 @@ error_string :: proc(ferr: Error) -> string {
|
||||
case General_Error:
|
||||
switch e {
|
||||
case .None: return ""
|
||||
case .Permission_Denied: return "permission denied"
|
||||
case .Exist: return "file already exists"
|
||||
case .Not_Exist: return "file does not exist"
|
||||
case .Closed: return "file already closed"
|
||||
case .Timeout: return "i/o timeout"
|
||||
case .Broken_Pipe: return "Broken pipe"
|
||||
case .No_Size: return "file has no definite size"
|
||||
case .Invalid_File: return "invalid file"
|
||||
case .Invalid_Dir: return "invalid directory"
|
||||
case .Invalid_Path: return "invalid path"
|
||||
case .Invalid_Callback: return "invalid callback"
|
||||
case .Invalid_Command: return "invalid command"
|
||||
case .Unsupported: return "unsupported"
|
||||
case .Pattern_Has_Separator: return "pattern has separator"
|
||||
case .Permission_Denied: return "permission denied"
|
||||
case .Exist: return "file already exists"
|
||||
case .Not_Exist: return "file does not exist"
|
||||
case .Closed: return "file already closed"
|
||||
case .Timeout: return "i/o timeout"
|
||||
case .Broken_Pipe: return "Broken pipe"
|
||||
case .No_Size: return "file has no definite size"
|
||||
case .Invalid_File: return "invalid file"
|
||||
case .Invalid_Dir: return "invalid directory"
|
||||
case .Invalid_Path: return "invalid path"
|
||||
case .Invalid_Callback: return "invalid callback"
|
||||
case .Invalid_Command: return "invalid command"
|
||||
case .Unsupported: return "unsupported"
|
||||
case .Pattern_Has_Separator: return "pattern has separator"
|
||||
case .No_HOME_Variable: return "no $HOME variable"
|
||||
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.
|
||||
|
||||
@@ -59,7 +59,7 @@ write_encoded_rune :: proc(f: ^File, r: rune) -> (n: int, err: Error) {
|
||||
if r < 32 {
|
||||
if wrap(write_string(f, "\\x"), &n, &err) { return }
|
||||
b: [2]byte
|
||||
s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil)
|
||||
s := strconv.write_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil)
|
||||
switch len(s) {
|
||||
case 0: if wrap(write_string(f, "00"), &n, &err) { return }
|
||||
case 1: if wrap(write_rune(f, '0'), &n, &err) { return }
|
||||
|
||||
+141
-71
@@ -2,78 +2,148 @@ package os2
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
@(require_results)
|
||||
user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
|
||||
|
||||
#partial switch ODIN_OS {
|
||||
case .Windows:
|
||||
dir = get_env("LocalAppData", temp_allocator)
|
||||
if dir != "" {
|
||||
dir = clone_string(dir, temp_allocator) or_return
|
||||
}
|
||||
case .Darwin:
|
||||
dir = get_env("HOME", temp_allocator)
|
||||
if dir != "" {
|
||||
dir = concatenate({dir, "/Library/Caches"}, temp_allocator) or_return
|
||||
}
|
||||
case: // All other UNIX systems
|
||||
dir = get_env("XDG_CACHE_HOME", allocator)
|
||||
if dir == "" {
|
||||
dir = get_env("HOME", temp_allocator)
|
||||
if dir == "" {
|
||||
return
|
||||
}
|
||||
dir = concatenate({dir, "/.cache"}, temp_allocator) or_return
|
||||
}
|
||||
}
|
||||
if dir == "" {
|
||||
err = .Invalid_Path
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
|
||||
|
||||
#partial switch ODIN_OS {
|
||||
case .Windows:
|
||||
dir = get_env("AppData", temp_allocator)
|
||||
if dir != "" {
|
||||
dir = clone_string(dir, allocator) or_return
|
||||
}
|
||||
case .Darwin:
|
||||
dir = get_env("HOME", temp_allocator)
|
||||
if dir != "" {
|
||||
dir = concatenate({dir, "/.config"}, allocator) or_return
|
||||
}
|
||||
case: // All other UNIX systems
|
||||
dir = get_env("XDG_CONFIG_HOME", allocator)
|
||||
if dir == "" {
|
||||
dir = get_env("HOME", temp_allocator)
|
||||
if dir == "" {
|
||||
return
|
||||
}
|
||||
dir = concatenate({dir, "/.config"}, allocator) or_return
|
||||
}
|
||||
}
|
||||
if dir == "" {
|
||||
err = .Invalid_Path
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ```
|
||||
// Windows: C:\Users\Alice
|
||||
// macOS: /Users/Alice
|
||||
// Linux: /home/alice
|
||||
// ```
|
||||
@(require_results)
|
||||
user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
env := "HOME"
|
||||
#partial switch ODIN_OS {
|
||||
case .Windows:
|
||||
env = "USERPROFILE"
|
||||
}
|
||||
if v := get_env(env, allocator); v != "" {
|
||||
return v, nil
|
||||
}
|
||||
return "", .Invalid_Path
|
||||
return _user_home_dir(allocator)
|
||||
}
|
||||
|
||||
// Files that applications can regenerate/refetch at a loss of speed, e.g. shader caches
|
||||
//
|
||||
// Sometimes deleted for system maintenance
|
||||
//
|
||||
// ```
|
||||
// Windows: C:\Users\Alice\AppData\Local
|
||||
// macOS: /Users/Alice/Library/Caches
|
||||
// Linux: /home/alice/.cache
|
||||
// ```
|
||||
@(require_results)
|
||||
user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
return _user_cache_dir(allocator)
|
||||
}
|
||||
|
||||
// User-hidden application data
|
||||
//
|
||||
// ```
|
||||
// Windows: C:\Users\Alice\AppData\Local ("C:\Users\Alice\AppData\Roaming" if `roaming`)
|
||||
// macOS: /Users/Alice/Library/Application Support
|
||||
// Linux: /home/alice/.local/share
|
||||
// ```
|
||||
//
|
||||
// NOTE: (Windows only) `roaming` is for syncing across multiple devices within a *domain network*
|
||||
@(require_results)
|
||||
user_data_dir :: proc(allocator: runtime.Allocator, roaming := false) -> (dir: string, err: Error) {
|
||||
return _user_data_dir(allocator, roaming)
|
||||
}
|
||||
|
||||
// Non-essential application data, e.g. history, ui layout state
|
||||
//
|
||||
// ```
|
||||
// Windows: C:\Users\Alice\AppData\Local
|
||||
// macOS: /Users/Alice/Library/Application Support
|
||||
// Linux: /home/alice/.local/state
|
||||
// ```
|
||||
@(require_results)
|
||||
user_state_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
return _user_state_dir(allocator)
|
||||
}
|
||||
|
||||
// Application log files
|
||||
//
|
||||
// ```
|
||||
// Windows: C:\Users\Alice\AppData\Local
|
||||
// macOS: /Users/Alice/Library/Logs
|
||||
// Linux: /home/alice/.local/state
|
||||
// ```
|
||||
@(require_results)
|
||||
user_log_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
return _user_log_dir(allocator)
|
||||
}
|
||||
|
||||
// Application settings/preferences
|
||||
//
|
||||
// ```
|
||||
// Windows: C:\Users\Alice\AppData\Local ("C:\Users\Alice\AppData\Roaming" if `roaming`)
|
||||
// macOS: /Users/Alice/Library/Application Support
|
||||
// Linux: /home/alice/.config
|
||||
// ```
|
||||
//
|
||||
// NOTE: (Windows only) `roaming` is for syncing across multiple devices within a *domain network*
|
||||
@(require_results)
|
||||
user_config_dir :: proc(allocator: runtime.Allocator, roaming := false) -> (dir: string, err: Error) {
|
||||
return _user_config_dir(allocator, roaming)
|
||||
}
|
||||
|
||||
// ```
|
||||
// Windows: C:\Users\Alice\Music
|
||||
// macOS: /Users/Alice/Music
|
||||
// Linux: /home/alice/Music
|
||||
// ```
|
||||
@(require_results)
|
||||
user_music_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
return _user_music_dir(allocator)
|
||||
}
|
||||
|
||||
// ```
|
||||
// Windows: C:\Users\Alice\Desktop
|
||||
// macOS: /Users/Alice/Desktop
|
||||
// Linux: /home/alice/Desktop
|
||||
// ```
|
||||
@(require_results)
|
||||
user_desktop_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
return _user_desktop_dir(allocator)
|
||||
}
|
||||
|
||||
// ```
|
||||
// Windows: C:\Users\Alice\Documents
|
||||
// macOS: /Users/Alice/Documents
|
||||
// Linux: /home/alice/Documents
|
||||
// ```
|
||||
@(require_results)
|
||||
user_documents_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
return _user_documents_dir(allocator)
|
||||
}
|
||||
|
||||
// ```
|
||||
// Windows: C:\Users\Alice\Downloads
|
||||
// macOS: /Users/Alice/Downloads
|
||||
// Linux: /home/alice/Downloads
|
||||
// ```
|
||||
@(require_results)
|
||||
user_downloads_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
return _user_downloads_dir(allocator)
|
||||
}
|
||||
|
||||
// ```
|
||||
// Windows: C:\Users\Alice\Pictures
|
||||
// macOS: /Users/Alice/Pictures
|
||||
// Linux: /home/alice/Pictures
|
||||
// ```
|
||||
@(require_results)
|
||||
user_pictures_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
return _user_pictures_dir(allocator)
|
||||
}
|
||||
|
||||
// ```
|
||||
// Windows: C:\Users\Alice\Public
|
||||
// macOS: /Users/Alice/Public
|
||||
// Linux: /home/alice/Public
|
||||
// ```
|
||||
@(require_results)
|
||||
user_public_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
return _user_public_dir(allocator)
|
||||
}
|
||||
|
||||
// ```
|
||||
// Windows: C:\Users\Alice\Videos
|
||||
// macOS: /Users/Alice/Movies
|
||||
// Linux: /home/alice/Videos
|
||||
// ```
|
||||
@(require_results)
|
||||
user_videos_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
return _user_videos_dir(allocator)
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
#+build !windows
|
||||
package os2
|
||||
|
||||
import "base:runtime"
|
||||
import "core:encoding/ini"
|
||||
import "core:strings"
|
||||
|
||||
_user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
#partial switch ODIN_OS {
|
||||
case .Darwin:
|
||||
return _xdg_lookup("", "/Library/Caches", allocator)
|
||||
case: // Unix
|
||||
return _xdg_lookup("XDG_CACHE_HOME", "/.cache", allocator)
|
||||
}
|
||||
}
|
||||
|
||||
_user_config_dir :: proc(allocator: runtime.Allocator, _roaming: bool) -> (dir: string, err: Error) {
|
||||
#partial switch ODIN_OS {
|
||||
case .Darwin:
|
||||
return _xdg_lookup("", "/Library/Application Support", allocator)
|
||||
case: // Unix
|
||||
return _xdg_lookup("XDG_CONFIG_HOME", "/.config", allocator)
|
||||
}
|
||||
}
|
||||
|
||||
_user_state_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
#partial switch ODIN_OS {
|
||||
case .Darwin:
|
||||
return _xdg_lookup("", "/Library/Application Support", allocator)
|
||||
case: // Unix
|
||||
return _xdg_lookup("XDG_STATE_HOME", "/.local/state", allocator)
|
||||
}
|
||||
}
|
||||
|
||||
_user_log_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
#partial switch ODIN_OS {
|
||||
case .Darwin:
|
||||
return _xdg_lookup("", "/Library/Logs", allocator)
|
||||
case: // Unix
|
||||
return _xdg_lookup("XDG_STATE_HOME", "/.local/state", allocator)
|
||||
}
|
||||
}
|
||||
|
||||
_user_data_dir :: proc(allocator: runtime.Allocator, _roaming: bool) -> (dir: string, err: Error) {
|
||||
#partial switch ODIN_OS {
|
||||
case .Darwin:
|
||||
return _xdg_lookup("", "/Library/Application Support", allocator)
|
||||
case: // Unix
|
||||
return _xdg_lookup("XDG_DATA_HOME", "/.local/share", allocator)
|
||||
}
|
||||
}
|
||||
|
||||
_user_music_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
#partial switch ODIN_OS {
|
||||
case .Darwin:
|
||||
return _xdg_lookup("", "/Music", allocator)
|
||||
case: // Unix
|
||||
return _xdg_lookup("XDG_MUSIC_DIR", "/Music", allocator)
|
||||
}
|
||||
}
|
||||
|
||||
_user_desktop_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
#partial switch ODIN_OS {
|
||||
case .Darwin:
|
||||
return _xdg_lookup("", "/Desktop", allocator)
|
||||
case: // Unix
|
||||
return _xdg_lookup("XDG_DESKTOP_DIR", "/Desktop", allocator)
|
||||
}
|
||||
}
|
||||
|
||||
_user_documents_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
#partial switch ODIN_OS {
|
||||
case .Darwin:
|
||||
return _xdg_lookup("", "/Documents", allocator)
|
||||
case: // Unix
|
||||
return _xdg_lookup("XDG_DOCUMENTS_DIR", "/Documents", allocator)
|
||||
}
|
||||
}
|
||||
|
||||
_user_downloads_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
#partial switch ODIN_OS {
|
||||
case .Darwin:
|
||||
return _xdg_lookup("", "/Downloads", allocator)
|
||||
case: // Unix
|
||||
return _xdg_lookup("XDG_DOWNLOAD_DIR", "/Downloads", allocator)
|
||||
}
|
||||
}
|
||||
|
||||
_user_pictures_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
#partial switch ODIN_OS {
|
||||
case .Darwin:
|
||||
return _xdg_lookup("", "/Pictures", allocator)
|
||||
case: // Unix
|
||||
return _xdg_lookup("XDG_PICTURES_DIR", "/Pictures", allocator)
|
||||
}
|
||||
}
|
||||
|
||||
_user_public_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
#partial switch ODIN_OS {
|
||||
case .Darwin:
|
||||
return _xdg_lookup("", "/Public", allocator)
|
||||
case: // Unix
|
||||
return _xdg_lookup("XDG_PUBLICSHARE_DIR", "/Public", allocator)
|
||||
}
|
||||
}
|
||||
|
||||
_user_videos_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
#partial switch ODIN_OS {
|
||||
case .Darwin:
|
||||
return _xdg_lookup("", "/Movies", allocator)
|
||||
case: // Unix
|
||||
return _xdg_lookup("XDG_VIDEOS_DIR", "/Videos", allocator)
|
||||
}
|
||||
}
|
||||
|
||||
_user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
if v := get_env("HOME", allocator); v != "" {
|
||||
return v, nil
|
||||
}
|
||||
err = .No_HOME_Variable
|
||||
return
|
||||
}
|
||||
|
||||
_xdg_lookup :: proc(xdg_key: string, fallback_suffix: string, allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
|
||||
|
||||
if xdg_key == "" { // Darwin doesn't have XDG paths.
|
||||
dir = get_env("HOME", temp_allocator)
|
||||
if dir == "" {
|
||||
err = .No_HOME_Variable
|
||||
return
|
||||
}
|
||||
return concatenate({dir, fallback_suffix}, allocator)
|
||||
} else {
|
||||
if strings.ends_with(xdg_key, "_DIR") {
|
||||
dir = _xdg_user_dirs_lookup(xdg_key, allocator) or_return
|
||||
} else {
|
||||
dir = get_env(xdg_key, allocator)
|
||||
}
|
||||
|
||||
if dir == "" {
|
||||
dir = get_env("HOME", temp_allocator)
|
||||
if dir == "" {
|
||||
err = .No_HOME_Variable
|
||||
return
|
||||
}
|
||||
dir = concatenate({dir, fallback_suffix}, allocator) or_return
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// If `<config-dir>/user-dirs.dirs` doesn't exist, or `xdg_key` can't be found there: returns `""`
|
||||
_xdg_user_dirs_lookup :: proc(xdg_key: string, allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
|
||||
config_dir := user_config_dir(temp_allocator) or_return
|
||||
user_dirs_path := concatenate({config_dir, "/user-dirs.dirs"}, temp_allocator) or_return
|
||||
content := read_entire_file(user_dirs_path, temp_allocator) or_return
|
||||
|
||||
it := ini.Iterator{
|
||||
section = "",
|
||||
_src = string(content),
|
||||
options = ini.Options{
|
||||
comment = "#",
|
||||
key_lower_case = false,
|
||||
},
|
||||
}
|
||||
|
||||
for k, v in ini.iterate(&it) {
|
||||
if k == xdg_key {
|
||||
return replace_environment_placeholders(v, allocator), nil
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package os2
|
||||
|
||||
import "base:runtime"
|
||||
@(require) import win32 "core:sys/windows"
|
||||
|
||||
_local_appdata :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
guid := win32.FOLDERID_LocalAppData
|
||||
return _get_known_folder_path(&guid, allocator)
|
||||
}
|
||||
|
||||
_local_appdata_or_roaming :: proc(allocator: runtime.Allocator, roaming: bool) -> (dir: string, err: Error) {
|
||||
guid := win32.FOLDERID_LocalAppData
|
||||
if roaming {
|
||||
guid = win32.FOLDERID_RoamingAppData
|
||||
}
|
||||
return _get_known_folder_path(&guid, allocator)
|
||||
}
|
||||
|
||||
_user_config_dir :: _local_appdata_or_roaming
|
||||
_user_data_dir :: _local_appdata_or_roaming
|
||||
|
||||
_user_state_dir :: _local_appdata
|
||||
_user_log_dir :: _local_appdata
|
||||
_user_cache_dir :: _local_appdata
|
||||
|
||||
_user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
guid := win32.FOLDERID_Profile
|
||||
return _get_known_folder_path(&guid, allocator)
|
||||
}
|
||||
|
||||
_user_music_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
guid := win32.FOLDERID_Music
|
||||
return _get_known_folder_path(&guid, allocator)
|
||||
}
|
||||
|
||||
_user_desktop_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
guid := win32.FOLDERID_Desktop
|
||||
return _get_known_folder_path(&guid, allocator)
|
||||
}
|
||||
|
||||
_user_documents_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
guid := win32.FOLDERID_Documents
|
||||
return _get_known_folder_path(&guid, allocator)
|
||||
}
|
||||
|
||||
_user_downloads_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
guid := win32.FOLDERID_Downloads
|
||||
return _get_known_folder_path(&guid, allocator)
|
||||
}
|
||||
|
||||
_user_pictures_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
guid := win32.FOLDERID_Pictures
|
||||
return _get_known_folder_path(&guid, allocator)
|
||||
}
|
||||
|
||||
_user_public_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
guid := win32.FOLDERID_Public
|
||||
return _get_known_folder_path(&guid, allocator)
|
||||
}
|
||||
|
||||
_user_videos_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
guid := win32.FOLDERID_Videos
|
||||
return _get_known_folder_path(&guid, allocator)
|
||||
}
|
||||
|
||||
_get_known_folder_path :: proc(rfid: win32.REFKNOWNFOLDERID, allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath
|
||||
// See also `known_folders.odin` in `core:sys/windows` for the GUIDs.
|
||||
path_w: win32.LPWSTR
|
||||
res := win32.SHGetKnownFolderPath(rfid, 0, nil, &path_w)
|
||||
defer win32.CoTaskMemFree(path_w)
|
||||
|
||||
if res != 0 {
|
||||
return "", .Invalid_Path
|
||||
}
|
||||
|
||||
dir, _ = win32.wstring_to_utf8(path_w, -1, allocator)
|
||||
return
|
||||
}
|
||||
+37
-3
@@ -1055,9 +1055,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
|
||||
@@ -1066,11 +1067,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)
|
||||
@@ -1196,7 +1225,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 {
|
||||
@@ -1205,6 +1234,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 {
|
||||
|
||||
+40
-7
@@ -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)
|
||||
}
|
||||
|
||||
+41
-6
@@ -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
|
||||
|
||||
@@ -249,3 +249,27 @@ exit :: proc "contextless" (code: int) -> ! {
|
||||
current_thread_id :: proc "contextless" () -> int {
|
||||
return 0
|
||||
}
|
||||
|
||||
@(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}
|
||||
+40
-7
@@ -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)
|
||||
|
||||
+40
-7
@@ -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)
|
||||
}
|
||||
|
||||
+40
-6
@@ -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)
|
||||
}
|
||||
|
||||
+30
-1
@@ -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.
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package filepath
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
import "core:strings"
|
||||
|
||||
SEPARATOR :: '/'
|
||||
SEPARATOR_STRING :: `/`
|
||||
LIST_SEPARATOR :: ':'
|
||||
|
||||
is_reserved_name :: proc(path: string) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
is_abs :: proc(path: string) -> bool {
|
||||
return strings.has_prefix(path, "/")
|
||||
}
|
||||
|
||||
abs :: proc(path: string, allocator := context.allocator) -> (string, bool) {
|
||||
if is_abs(path) {
|
||||
return strings.clone(string(path), allocator), true
|
||||
}
|
||||
|
||||
return path, false
|
||||
}
|
||||
|
||||
join :: proc(elems: []string, allocator := context.allocator) -> (joined: string, err: runtime.Allocator_Error) #optional_allocator_error {
|
||||
for e, i in elems {
|
||||
if e != "" {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
|
||||
p := strings.join(elems[i:], SEPARATOR_STRING, context.temp_allocator) or_return
|
||||
return clean(p, allocator)
|
||||
}
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -30,14 +30,6 @@ sort :: proc(it: Interface) {
|
||||
_quick_sort(it, 0, n, max_depth(n))
|
||||
}
|
||||
|
||||
|
||||
@(deprecated="use slice.sort")
|
||||
slice :: proc(array: $T/[]$E) where ORD(E) {
|
||||
_slice.sort(array)
|
||||
// s := array;
|
||||
// sort(slice_interface(&s));
|
||||
}
|
||||
|
||||
slice_interface :: proc(s: ^$T/[]$E) -> Interface where ORD(E) {
|
||||
return Interface{
|
||||
collection = rawptr(s),
|
||||
@@ -80,31 +72,6 @@ reverse_sort :: proc(it: Interface) {
|
||||
sort(reverse_interface(&it))
|
||||
}
|
||||
|
||||
@(deprecated="use slice.reverse")
|
||||
reverse_slice :: proc(array: $T/[]$E) where ORD(E) {
|
||||
_slice.reverse(array)
|
||||
/*
|
||||
s := array;
|
||||
sort(Interface{
|
||||
collection = rawptr(&s),
|
||||
len = proc(it: Interface) -> int {
|
||||
s := (^T)(it.collection);
|
||||
return len(s^);
|
||||
},
|
||||
less = proc(it: Interface, i, j: int) -> bool {
|
||||
s := (^T)(it.collection);
|
||||
return s[j] < s[i]; // manual set up
|
||||
},
|
||||
swap = proc(it: Interface, i, j: int) {
|
||||
s := (^T)(it.collection);
|
||||
s[i], s[j] = s[j], s[i];
|
||||
},
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
is_sorted :: proc(it: Interface) -> bool {
|
||||
n := it->len()
|
||||
for i := n-1; i > 0; i -= 1 {
|
||||
@@ -294,11 +261,6 @@ _insertion_sort :: proc(it: Interface, a, b: int) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// @(deprecated="use sort.sort or slice.sort_by")
|
||||
bubble_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
|
||||
assert(f != nil)
|
||||
count := len(array)
|
||||
@@ -327,7 +289,6 @@ bubble_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
|
||||
}
|
||||
}
|
||||
|
||||
// @(deprecated="use sort.sort_slice or slice.sort")
|
||||
bubble_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
|
||||
count := len(array)
|
||||
|
||||
@@ -355,7 +316,6 @@ bubble_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
|
||||
}
|
||||
}
|
||||
|
||||
// @(deprecated="use sort.sort or slice.sort_by")
|
||||
quick_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
|
||||
assert(f != nil)
|
||||
a := array
|
||||
@@ -384,7 +344,6 @@ quick_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
|
||||
quick_sort_proc(a[i:n], f)
|
||||
}
|
||||
|
||||
// @(deprecated="use sort.sort_slice or slice.sort")
|
||||
quick_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
|
||||
a := array
|
||||
n := len(a)
|
||||
@@ -420,7 +379,6 @@ _log2 :: proc(x: int) -> int {
|
||||
return res
|
||||
}
|
||||
|
||||
// @(deprecated="use sort.sort or slice.sort_by")
|
||||
merge_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
|
||||
merge :: proc(a: A, start, mid, end: int, f: proc(T, T) -> int) {
|
||||
s, m := start, mid
|
||||
@@ -462,7 +420,6 @@ merge_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
|
||||
internal_sort(array, 0, len(array)-1, f)
|
||||
}
|
||||
|
||||
// @(deprecated="use sort.sort_slice or slice.sort")
|
||||
merge_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
|
||||
merge :: proc(a: A, start, mid, end: int) {
|
||||
s, m := start, mid
|
||||
@@ -504,8 +461,6 @@ merge_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
|
||||
internal_sort(array, 0, len(array)-1)
|
||||
}
|
||||
|
||||
|
||||
// @(deprecated="use sort.sort or slice.sort_by")
|
||||
heap_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
|
||||
sift_proc :: proc(a: A, pi: int, n: int, f: proc(T, T) -> int) #no_bounds_check {
|
||||
p := pi
|
||||
@@ -540,7 +495,6 @@ heap_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
|
||||
}
|
||||
}
|
||||
|
||||
// @(deprecated="use sort.sort_slice or slice.sort")
|
||||
heap_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
|
||||
sift :: proc(a: A, pi: int, n: int) #no_bounds_check {
|
||||
p := pi
|
||||
|
||||
@@ -12,11 +12,11 @@ Decimal :: struct {
|
||||
Sets a Decimal from a given string `s`. The string is expected to represent a float. Stores parsed number in the given Decimal structure.
|
||||
If parsing fails, the Decimal will be left in an undefined state.
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- d: Pointer to a Decimal struct where the parsed result will be stored
|
||||
- s: The input string representing the floating-point number
|
||||
|
||||
**Returns**
|
||||
**Returns**
|
||||
- ok: A boolean indicating whether the parsing was successful
|
||||
*/
|
||||
set :: proc(d: ^Decimal, s: string) -> (ok: bool) {
|
||||
@@ -104,11 +104,11 @@ set :: proc(d: ^Decimal, s: string) -> (ok: bool) {
|
||||
/*
|
||||
Converts a Decimal to a string representation, using the provided buffer as storage.
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- buf: A byte slice buffer to hold the resulting string
|
||||
- a: The struct to be converted to a string
|
||||
|
||||
**Returns**
|
||||
**Returns**
|
||||
- A string representation of the Decimal
|
||||
*/
|
||||
decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string {
|
||||
@@ -150,7 +150,7 @@ decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string {
|
||||
/*
|
||||
Trims trailing zeros in the given Decimal, updating the count and decimal_point values as needed.
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- a: Pointer to the Decimal struct to be trimmed
|
||||
*/
|
||||
trim :: proc(a: ^Decimal) {
|
||||
@@ -166,7 +166,7 @@ Converts a given u64 integer `idx` to its Decimal representation in the provided
|
||||
|
||||
**Used for internal Decimal Operations.**
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- a: Where the result will be stored
|
||||
- idx: The value to be assigned to the Decimal
|
||||
*/
|
||||
@@ -190,11 +190,11 @@ assign :: proc(a: ^Decimal, idx: u64) {
|
||||
trim(a)
|
||||
}
|
||||
/*
|
||||
Shifts the Decimal value to the right by k positions.
|
||||
Shifts the Decimal value to the right by k positions.
|
||||
|
||||
**Used for internal Decimal Operations.**
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- a: The Decimal struct to be shifted
|
||||
- k: The number of positions to shift right
|
||||
*/
|
||||
@@ -344,7 +344,7 @@ Shifts the decimal of the input value to the left by `k` places
|
||||
|
||||
WARNING: asserts `k < 61`
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- a: The Decimal to be modified
|
||||
- k: The number of places to shift the decimal to the left
|
||||
*/
|
||||
@@ -405,7 +405,7 @@ shift_left :: proc(a: ^Decimal, k: uint) #no_bounds_check {
|
||||
/*
|
||||
Shifts the decimal of the input value by the specified number of places
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- a: The Decimal to be modified
|
||||
- i: The number of places to shift the decimal (positive for left shift, negative for right shift)
|
||||
*/
|
||||
@@ -435,7 +435,7 @@ shift :: proc(a: ^Decimal, i: int) {
|
||||
/*
|
||||
Determines if the Decimal can be rounded up at the given digit index
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- a: The Decimal to check
|
||||
- nd: The digit index to consider for rounding up
|
||||
|
||||
@@ -455,7 +455,7 @@ can_round_up :: proc(a: ^Decimal, nd: int) -> bool {
|
||||
/*
|
||||
Rounds the Decimal at the given digit index
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- a: The Decimal to be modified
|
||||
- nd: The digit index to round
|
||||
*/
|
||||
@@ -470,7 +470,7 @@ round :: proc(a: ^Decimal, nd: int) {
|
||||
/*
|
||||
Rounds the Decimal up at the given digit index
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- a: The Decimal to be modified
|
||||
- nd: The digit index to round up
|
||||
*/
|
||||
@@ -493,7 +493,7 @@ round_up :: proc(a: ^Decimal, nd: int) {
|
||||
/*
|
||||
Rounds down the decimal value to the specified number of decimal places
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- a: The Decimal value to be rounded down
|
||||
- nd: The number of decimal places to round down to
|
||||
|
||||
@@ -522,7 +522,7 @@ round_down :: proc(a: ^Decimal, nd: int) {
|
||||
/*
|
||||
Extracts the rounded integer part of a decimal value
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- a: A pointer to the Decimal value to extract the rounded integer part from
|
||||
|
||||
WARNING: There are no guarantees about overflow.
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package strconv
|
||||
|
||||
// (2025-06-05) These procedures are to be removed at a later release.
|
||||
|
||||
@(deprecated="Use write_bits instead")
|
||||
append_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
|
||||
return write_bits(buf, x, base, is_signed, bit_size, digits, flags)
|
||||
}
|
||||
|
||||
@(deprecated="Use write_bits_128 instead")
|
||||
append_bits_128 :: proc(buf: []byte, x: u128, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
|
||||
return write_bits_128(buf, x, base, is_signed, bit_size, digits, flags)
|
||||
}
|
||||
|
||||
@(deprecated="Use write_bool instead")
|
||||
append_bool :: proc(buf: []byte, b: bool) -> string {
|
||||
return write_bool(buf, b)
|
||||
}
|
||||
|
||||
@(deprecated="Use write_uint instead")
|
||||
append_uint :: proc(buf: []byte, u: u64, base: int) -> string {
|
||||
return write_uint(buf, u, base)
|
||||
}
|
||||
|
||||
@(deprecated="Use write_int instead")
|
||||
append_int :: proc(buf: []byte, i: i64, base: int) -> string {
|
||||
return write_int(buf, i, base)
|
||||
}
|
||||
|
||||
@(deprecated="Use write_u128 instead")
|
||||
append_u128 :: proc(buf: []byte, u: u128, base: int) -> string {
|
||||
return write_u128(buf, u, base)
|
||||
}
|
||||
|
||||
@(deprecated="Use write_float instead")
|
||||
append_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string {
|
||||
return write_float(buf, f, fmt, prec, bit_size)
|
||||
}
|
||||
@@ -23,7 +23,7 @@ _f64_info := Float_Info{52, 11, -1023}
|
||||
/*
|
||||
Converts a floating-point number to a string with the specified format and precision.
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
|
||||
buf: A byte slice to store the resulting string
|
||||
val: The floating-point value to be converted
|
||||
@@ -40,7 +40,7 @@ Example:
|
||||
bit_size := 64
|
||||
result := strconv.generic_ftoa(buf[:], val, fmt, precision, bit_size) -> "3.14"
|
||||
|
||||
**Returns**
|
||||
**Returns**
|
||||
- A byte slice containing the formatted string
|
||||
*/
|
||||
generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, precision, bit_size: int) -> []byte {
|
||||
@@ -122,7 +122,7 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, precision, bit_size: int)
|
||||
/*
|
||||
Converts a decimal floating-point number into a byte buffer with the given format
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- buf: The byte buffer to store the formatted number
|
||||
- shortest: If true, generates the shortest representation of the number
|
||||
- neg: If true, the number is negative
|
||||
@@ -130,7 +130,7 @@ Converts a decimal floating-point number into a byte buffer with the given forma
|
||||
- precision: The number of digits after the decimal point
|
||||
- fmt: The format specifier (accepted values: 'f', 'F', 'e', 'E', 'g', 'G')
|
||||
|
||||
**Returns**
|
||||
**Returns**
|
||||
- A byte slice containing the formatted decimal floating-point number
|
||||
*/
|
||||
format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slice, precision: int, fmt: byte) -> []byte {
|
||||
@@ -256,7 +256,7 @@ format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slic
|
||||
/*
|
||||
Rounds the given decimal number to its shortest representation, considering the provided floating-point format
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- d: The decimal number to round
|
||||
- mant: The mantissa of the floating-point number
|
||||
- exp: The exponent of the floating-point number
|
||||
@@ -331,11 +331,11 @@ round_shortest :: proc(d: ^decimal.Decimal, mant: u64, exp: int, flt: ^Float_Inf
|
||||
/*
|
||||
Converts a decimal number to its floating-point representation with the given format and returns the resulting bits
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- d: Pointer to the decimal number to convert
|
||||
- info: Pointer to the Float_Info structure containing information about the floating-point format
|
||||
|
||||
**Returns**
|
||||
**Returns**
|
||||
- b: The bits representing the floating-point number
|
||||
- overflow: A boolean indicating whether an overflow occurred during conversion
|
||||
*/
|
||||
|
||||
+15
-15
@@ -12,12 +12,12 @@ digits := "0123456789abcdefghijklmnopqrstuvwxyz"
|
||||
/*
|
||||
Determines whether the given unsigned 64-bit integer is a negative value by interpreting it as a signed integer with the specified bit size.
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- x: The unsigned 64-bit integer to check for negativity
|
||||
- is_signed: A boolean indicating if the input should be treated as a signed integer
|
||||
- bit_size: The bit size of the signed integer representation (8, 16, 32, or 64)
|
||||
|
||||
**Returns**
|
||||
**Returns**
|
||||
- u: The absolute value of the input integer
|
||||
- neg: A boolean indicating whether the input integer is negative
|
||||
*/
|
||||
@@ -48,9 +48,9 @@ is_integer_negative :: proc(x: u64, is_signed: bool, bit_size: int) -> (u: u64,
|
||||
return
|
||||
}
|
||||
/*
|
||||
Appends the string representation of an integer to a buffer with specified base, flags, and digit set.
|
||||
Writes the string representation of an integer to a buffer with specified base, flags, and digit set.
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- buf: The buffer to append the integer representation to
|
||||
- x: The integer value to convert
|
||||
- base: The base for the integer representation (2 <= base <= MAX_BASE)
|
||||
@@ -59,12 +59,12 @@ Appends the string representation of an integer to a buffer with specified base,
|
||||
- digits: The digit set used for the integer representation
|
||||
- flags: The Int_Flags bit set to control integer formatting
|
||||
|
||||
**Returns**
|
||||
**Returns**
|
||||
- The string containing the integer representation appended to the buffer
|
||||
*/
|
||||
append_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
|
||||
write_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
|
||||
if base < 2 || base > MAX_BASE {
|
||||
panic("strconv: illegal base passed to append_bits")
|
||||
panic("strconv: illegal base passed to write_bits")
|
||||
}
|
||||
|
||||
a: [129]byte
|
||||
@@ -106,12 +106,12 @@ append_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: i
|
||||
/*
|
||||
Determines whether the given unsigned 128-bit integer is a negative value by interpreting it as a signed integer with the specified bit size.
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- x: The unsigned 128-bit integer to check for negativity
|
||||
- is_signed: A boolean indicating if the input should be treated as a signed integer
|
||||
- bit_size: The bit size of the signed integer representation (8, 16, 32, 64, or 128)
|
||||
|
||||
**Returns**
|
||||
**Returns**
|
||||
- u: The absolute value of the input integer
|
||||
- neg: A boolean indicating whether the input integer is negative
|
||||
*/
|
||||
@@ -146,9 +146,9 @@ is_integer_negative_128 :: proc(x: u128, is_signed: bool, bit_size: int) -> (u:
|
||||
return
|
||||
}
|
||||
/*
|
||||
Appends the string representation of a 128-bit integer to a buffer with specified base, flags, and digit set.
|
||||
Writes the string representation of a 128-bit integer to a buffer with specified base, flags, and digit set.
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- buf: The buffer to append the integer representation to
|
||||
- x: The 128-bit integer value to convert
|
||||
- base: The base for the integer representation (2 <= base <= MAX_BASE)
|
||||
@@ -157,12 +157,12 @@ Appends the string representation of a 128-bit integer to a buffer with specifie
|
||||
- digits: The digit set used for the integer representation
|
||||
- flags: The Int_Flags bit set to control integer formatting
|
||||
|
||||
**Returns**
|
||||
- The string containing the integer representation appended to the buffer
|
||||
**Returns**
|
||||
- The string containing the integer representation written to the buffer
|
||||
*/
|
||||
append_bits_128 :: proc(buf: []byte, x: u128, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
|
||||
write_bits_128 :: proc(buf: []byte, x: u128, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
|
||||
if base < 2 || base > MAX_BASE {
|
||||
panic("strconv: illegal base passed to append_bits")
|
||||
panic("strconv: illegal base passed to write_bits")
|
||||
}
|
||||
|
||||
a: [140]byte
|
||||
|
||||
+138
-138
@@ -5,8 +5,8 @@ import "decimal"
|
||||
/*
|
||||
Parses a boolean value from the input string
|
||||
|
||||
**Inputs**
|
||||
- s: The input string
|
||||
**Inputs**
|
||||
- s: The input string
|
||||
- true: "1", "t", "T", "true", "TRUE", "True"
|
||||
- false: "0", "f", "F", "false", "FALSE", "False"
|
||||
- n: An optional pointer to an int to store the length of the parsed substring (default: nil)
|
||||
@@ -386,7 +386,7 @@ Parses an unsigned integer value from the input string, using the specified base
|
||||
- If base is not 0, it will be used for parsing regardless of any prefix in the input string
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
import "core:fmt"
|
||||
import "core:strconv"
|
||||
parse_uint_example :: proc() {
|
||||
@@ -399,14 +399,14 @@ Example:
|
||||
n, ok = strconv.parse_uint("0xffff") // with prefix and inferred base
|
||||
fmt.println(n,ok)
|
||||
}
|
||||
|
||||
|
||||
Output:
|
||||
|
||||
1234 true
|
||||
65535 true
|
||||
65535 true
|
||||
|
||||
**Returns**
|
||||
**Returns**
|
||||
|
||||
value: The parsed uint value
|
||||
ok: `false` if no appropriate value could be found; the value was negative; he input string contained more than just the number
|
||||
@@ -423,7 +423,7 @@ parse_uint :: proc(s: string, base := 0, n: ^int = nil) -> (value: uint, ok: boo
|
||||
/*
|
||||
Parses an integer value from a string in the given base, without any prefix
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- str: The input string containing the integer value
|
||||
- base: The base (radix) to use for parsing the integer (1-16)
|
||||
- n: An optional pointer to an int to store the length of the parsed substring (default: nil)
|
||||
@@ -436,12 +436,12 @@ Example:
|
||||
n, ok := strconv.parse_i128_of_base("-1234eeee", 10)
|
||||
fmt.println(n,ok)
|
||||
}
|
||||
|
||||
|
||||
Output:
|
||||
|
||||
-1234 false
|
||||
|
||||
**Returns**
|
||||
**Returns**
|
||||
- value: The parsed i128 value
|
||||
- ok: false if no numeric value of the appropriate base could be found, or if the input string contained more than just the number.
|
||||
*/
|
||||
@@ -491,7 +491,7 @@ parse_i128_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: i12
|
||||
/*
|
||||
Parses an integer value from a string in base 10, unless there's a prefix
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- str: The input string containing the integer value
|
||||
- n: An optional pointer to an int to store the length of the parsed substring (default: nil)
|
||||
|
||||
@@ -506,13 +506,13 @@ Example:
|
||||
n, ok = strconv.parse_i128_maybe_prefixed("0xeeee")
|
||||
fmt.println(n, ok)
|
||||
}
|
||||
|
||||
|
||||
Output:
|
||||
|
||||
1234 true
|
||||
61166 true
|
||||
|
||||
**Returns**
|
||||
|
||||
**Returns**
|
||||
- value: The parsed i128 value
|
||||
- ok: `false` if a valid integer could not be found, or if the input string contained more than just the number.
|
||||
*/
|
||||
@@ -574,7 +574,7 @@ parse_i128 :: proc{parse_i128_maybe_prefixed, parse_i128_of_base}
|
||||
/*
|
||||
Parses an unsigned integer value from a string in the given base, without any prefix
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- str: The input string containing the integer value
|
||||
- base: The base (radix) to use for parsing the integer (1-16)
|
||||
- n: An optional pointer to an int to store the length of the parsed substring (default: nil)
|
||||
@@ -590,13 +590,13 @@ Example:
|
||||
n, ok = strconv.parse_u128_of_base("5678eeee", 16)
|
||||
fmt.println(n, ok)
|
||||
}
|
||||
|
||||
|
||||
Output:
|
||||
|
||||
1234 false
|
||||
1450766062 true
|
||||
|
||||
**Returns**
|
||||
|
||||
**Returns**
|
||||
- value: The parsed u128 value
|
||||
- ok: `false` if no numeric value of the appropriate base could be found, or if the input string contained more than just the number.
|
||||
*/
|
||||
@@ -634,7 +634,7 @@ parse_u128_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: u12
|
||||
/*
|
||||
Parses an unsigned integer value from a string in base 10, unless there's a prefix
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- str: The input string containing the integer value
|
||||
- n: An optional pointer to an int to store the length of the parsed substring (default: nil)
|
||||
|
||||
@@ -649,13 +649,13 @@ Example:
|
||||
n, ok = strconv.parse_u128_maybe_prefixed("5678eeee")
|
||||
fmt.println(n, ok)
|
||||
}
|
||||
|
||||
|
||||
Output:
|
||||
|
||||
1234 true
|
||||
5678 false
|
||||
|
||||
**Returns**
|
||||
|
||||
**Returns**
|
||||
- value: The parsed u128 value
|
||||
- ok: false if a valid integer could not be found, if the value was negative, or if the input string contained more than just the number.
|
||||
*/
|
||||
@@ -706,10 +706,10 @@ parse_u128 :: proc{parse_u128_maybe_prefixed, parse_u128_of_base}
|
||||
/*
|
||||
Converts a byte to lowercase
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- ch: A byte character to be converted to lowercase.
|
||||
|
||||
**Returns**
|
||||
**Returns**
|
||||
- A lowercase byte character.
|
||||
*/
|
||||
@(private)
|
||||
@@ -717,7 +717,7 @@ lower :: #force_inline proc "contextless" (ch: byte) -> byte { return ('a' - 'A'
|
||||
/*
|
||||
Parses a 32-bit floating point number from a string
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- s: The input string containing a 32-bit floating point number.
|
||||
- n: An optional pointer to an int to store the length of the parsed substring (default: nil).
|
||||
|
||||
@@ -732,13 +732,13 @@ Example:
|
||||
n, ok = strconv.parse_f32("5678e2")
|
||||
fmt.printfln("%.3f %v", n, ok)
|
||||
}
|
||||
|
||||
|
||||
Output:
|
||||
|
||||
0.000 false
|
||||
567800.000 true
|
||||
|
||||
**Returns**
|
||||
|
||||
**Returns**
|
||||
- value: The parsed 32-bit floating point number.
|
||||
- ok: `false` if a base 10 float could not be found, or if the input string contained more than just the number.
|
||||
*/
|
||||
@@ -750,7 +750,7 @@ parse_f32 :: proc(s: string, n: ^int = nil) -> (value: f32, ok: bool) {
|
||||
/*
|
||||
Parses a 64-bit floating point number from a string
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- str: The input string containing a 64-bit floating point number.
|
||||
- n: An optional pointer to an int to store the length of the parsed substring (default: nil).
|
||||
|
||||
@@ -765,13 +765,13 @@ Example:
|
||||
n, ok = strconv.parse_f64("5678e2")
|
||||
fmt.printfln("%.3f %v", n, ok)
|
||||
}
|
||||
|
||||
|
||||
Output:
|
||||
|
||||
0.000 false
|
||||
567800.000 true
|
||||
|
||||
**Returns**
|
||||
|
||||
**Returns**
|
||||
- value: The parsed 64-bit floating point number.
|
||||
- ok: `false` if a base 10 float could not be found, or if the input string contained more than just the number.
|
||||
*/
|
||||
@@ -787,7 +787,7 @@ parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) {
|
||||
/*
|
||||
Parses a 32-bit floating point number from a string and returns the parsed number, the length of the parsed substring, and a boolean indicating whether the parsing was successful
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- str: The input string containing a 32-bit floating point number.
|
||||
|
||||
Example:
|
||||
@@ -801,14 +801,14 @@ Example:
|
||||
n, _, ok = strconv.parse_f32_prefix("5678e2")
|
||||
fmt.printfln("%.3f %v", n, ok)
|
||||
}
|
||||
|
||||
|
||||
Output:
|
||||
|
||||
0.000 false
|
||||
567800.000 true
|
||||
|
||||
|
||||
**Returns**
|
||||
|
||||
**Returns**
|
||||
- value: The parsed 32-bit floating point number.
|
||||
- nr: The length of the parsed substring.
|
||||
- ok: A boolean indicating whether the parsing was successful.
|
||||
@@ -822,7 +822,7 @@ parse_f32_prefix :: proc(str: string) -> (value: f32, nr: int, ok: bool) {
|
||||
/*
|
||||
Parses a 64-bit floating point number from a string and returns the parsed number, the length of the parsed substring, and a boolean indicating whether the parsing was successful
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- str: The input string containing a 64-bit floating point number.
|
||||
|
||||
Example:
|
||||
@@ -846,7 +846,7 @@ Output:
|
||||
1234.000 true
|
||||
13.370 true
|
||||
|
||||
**Returns**
|
||||
**Returns**
|
||||
- value: The parsed 64-bit floating point number.
|
||||
- nr: The length of the parsed substring.
|
||||
- ok: `false` if a base 10 float could not be found
|
||||
@@ -1184,7 +1184,7 @@ parse_f64_prefix :: proc(str: string) -> (value: f64, nr: int, ok: bool) {
|
||||
/*
|
||||
Parses a 128-bit complex number from a string
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- str: The input string containing a 128-bit complex number.
|
||||
- n: An optional pointer to an int to store the length of the parsed substring (default: nil).
|
||||
|
||||
@@ -1200,13 +1200,13 @@ Example:
|
||||
c, ok = strconv.parse_complex128("5+7i hellope", &n)
|
||||
fmt.printfln("%v %i %t", c, n, ok)
|
||||
}
|
||||
|
||||
|
||||
Output:
|
||||
|
||||
3+1i 4 true
|
||||
5+7i 4 false
|
||||
|
||||
**Returns**
|
||||
|
||||
**Returns**
|
||||
- value: The parsed 128-bit complex number.
|
||||
- ok: `false` if a complex number could not be found, or if the input string contained more than just the number.
|
||||
*/
|
||||
@@ -1232,12 +1232,12 @@ parse_complex128 :: proc(str: string, n: ^int = nil) -> (value: complex128, ok:
|
||||
}
|
||||
|
||||
value = complex(real_value, imag_value)
|
||||
return
|
||||
return
|
||||
}
|
||||
/*
|
||||
Parses a 64-bit complex number from a string
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- str: The input string containing a 64-bit complex number.
|
||||
- n: An optional pointer to an int to store the length of the parsed substring (default: nil).
|
||||
|
||||
@@ -1253,13 +1253,13 @@ Example:
|
||||
c, ok = strconv.parse_complex64("5+7i hellope", &n)
|
||||
fmt.printfln("%v %i %t", c, n, ok)
|
||||
}
|
||||
|
||||
|
||||
Output:
|
||||
|
||||
3+1i 4 true
|
||||
5+7i 4 false
|
||||
|
||||
**Returns**
|
||||
|
||||
**Returns**
|
||||
- value: The parsed 64-bit complex number.
|
||||
- ok: `false` if a complex number could not be found, or if the input string contained more than just the number.
|
||||
*/
|
||||
@@ -1271,7 +1271,7 @@ parse_complex64 :: proc(str: string, n: ^int = nil) -> (value: complex64, ok: bo
|
||||
/*
|
||||
Parses a 32-bit complex number from a string
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- str: The input string containing a 32-bit complex number.
|
||||
- n: An optional pointer to an int to store the length of the parsed substring (default: nil).
|
||||
|
||||
@@ -1287,13 +1287,13 @@ Example:
|
||||
c, ok = strconv.parse_complex32("5+7i hellope", &n)
|
||||
fmt.printfln("%v %i %t", c, n, ok)
|
||||
}
|
||||
|
||||
|
||||
Output:
|
||||
|
||||
3+1i 4 true
|
||||
5+7i 4 false
|
||||
|
||||
**Returns**
|
||||
|
||||
**Returns**
|
||||
- value: The parsed 32-bit complex number.
|
||||
- ok: `false` if a complex number could not be found, or if the input string contained more than just the number.
|
||||
*/
|
||||
@@ -1305,7 +1305,7 @@ parse_complex32 :: proc(str: string, n: ^int = nil) -> (value: complex32, ok: bo
|
||||
/*
|
||||
Parses a 256-bit quaternion from a string
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- str: The input string containing a 256-bit quaternion.
|
||||
- n: An optional pointer to an int to store the length of the parsed substring (default: nil).
|
||||
|
||||
@@ -1321,13 +1321,13 @@ Example:
|
||||
q, ok = strconv.parse_quaternion256("1+2i+3j+4k hellope", &n)
|
||||
fmt.printfln("%v %i %t", q, n, ok)
|
||||
}
|
||||
|
||||
|
||||
Output:
|
||||
|
||||
1+2i+3j+4k 10 true
|
||||
1+2i+3j+4k 10 false
|
||||
|
||||
**Returns**
|
||||
|
||||
**Returns**
|
||||
- value: The parsed 256-bit quaternion.
|
||||
- ok: `false` if a quaternion could not be found, or if the input string contained more than just the quaternion.
|
||||
*/
|
||||
@@ -1385,7 +1385,7 @@ parse_quaternion256 :: proc(str: string, n: ^int = nil) -> (value: quaternion256
|
||||
/*
|
||||
Parses a 128-bit quaternion from a string
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- str: The input string containing a 128-bit quaternion.
|
||||
- n: An optional pointer to an int to store the length of the parsed substring (default: nil).
|
||||
|
||||
@@ -1401,13 +1401,13 @@ Example:
|
||||
q, ok = strconv.parse_quaternion128("1+2i+3j+4k hellope", &n)
|
||||
fmt.printfln("%v %i %t", q, n, ok)
|
||||
}
|
||||
|
||||
|
||||
Output:
|
||||
|
||||
1+2i+3j+4k 10 true
|
||||
1+2i+3j+4k 10 false
|
||||
|
||||
**Returns**
|
||||
|
||||
**Returns**
|
||||
- value: The parsed 128-bit quaternion.
|
||||
- ok: `false` if a quaternion could not be found, or if the input string contained more than just the quaternion.
|
||||
*/
|
||||
@@ -1419,7 +1419,7 @@ parse_quaternion128 :: proc(str: string, n: ^int = nil) -> (value: quaternion128
|
||||
/*
|
||||
Parses a 64-bit quaternion from a string
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- str: The input string containing a 64-bit quaternion.
|
||||
- n: An optional pointer to an int to store the length of the parsed substring (default: nil).
|
||||
|
||||
@@ -1435,13 +1435,13 @@ Example:
|
||||
q, ok = strconv.parse_quaternion64("1+2i+3j+4k hellope", &n)
|
||||
fmt.printfln("%v %i %t", q, n, ok)
|
||||
}
|
||||
|
||||
|
||||
Output:
|
||||
|
||||
1+2i+3j+4k 10 true
|
||||
1+2i+3j+4k 10 false
|
||||
|
||||
**Returns**
|
||||
|
||||
**Returns**
|
||||
- value: The parsed 64-bit quaternion.
|
||||
- ok: `false` if a quaternion could not be found, or if the input string contained more than just the quaternion.
|
||||
*/
|
||||
@@ -1450,20 +1450,20 @@ parse_quaternion64 :: proc(str: string, n: ^int = nil) -> (value: quaternion64,
|
||||
v, ok = parse_quaternion256(str, n)
|
||||
return cast(quaternion64)v, ok
|
||||
}
|
||||
/*
|
||||
Appends a boolean value as a string to the given buffer
|
||||
/*
|
||||
Writes a boolean value as a string to the given buffer
|
||||
|
||||
**Inputs**
|
||||
- buf: The buffer to append the boolean value to
|
||||
- b: The boolean value to be appended
|
||||
**Inputs**
|
||||
- buf: The buffer to write the boolean value to
|
||||
- b: The boolean value to be written
|
||||
|
||||
Example:
|
||||
|
||||
import "core:fmt"
|
||||
import "core:strconv"
|
||||
append_bool_example :: proc() {
|
||||
write_bool_example :: proc() {
|
||||
buf: [6]byte
|
||||
result := strconv.append_bool(buf[:], true)
|
||||
result := strconv.write_bool(buf[:], true)
|
||||
fmt.println(result, buf)
|
||||
}
|
||||
|
||||
@@ -1471,10 +1471,10 @@ Output:
|
||||
|
||||
true [116, 114, 117, 101, 0, 0]
|
||||
|
||||
**Returns**
|
||||
- The resulting string after appending the boolean value
|
||||
**Returns**
|
||||
- The resulting string after writing the boolean value
|
||||
*/
|
||||
append_bool :: proc(buf: []byte, b: bool) -> string {
|
||||
write_bool :: proc(buf: []byte, b: bool) -> string {
|
||||
n := 0
|
||||
if b {
|
||||
n = copy(buf, "true")
|
||||
@@ -1483,21 +1483,21 @@ append_bool :: proc(buf: []byte, b: bool) -> string {
|
||||
}
|
||||
return string(buf[:n])
|
||||
}
|
||||
/*
|
||||
Appends an unsigned integer value as a string to the given buffer with the specified base
|
||||
/*
|
||||
Writes an unsigned integer value as a string to the given buffer with the specified base
|
||||
|
||||
**Inputs**
|
||||
- buf: The buffer to append the unsigned integer value to
|
||||
- u: The unsigned integer value to be appended
|
||||
**Inputs**
|
||||
- buf: The buffer to write the unsigned integer value to
|
||||
- u: The unsigned integer value to be written
|
||||
- base: The base to use for converting the integer value
|
||||
|
||||
Example:
|
||||
|
||||
import "core:fmt"
|
||||
import "core:strconv"
|
||||
append_uint_example :: proc() {
|
||||
write_uint_example :: proc() {
|
||||
buf: [4]byte
|
||||
result := strconv.append_uint(buf[:], 42, 16)
|
||||
result := strconv.write_uint(buf[:], 42, 16)
|
||||
fmt.println(result, buf)
|
||||
}
|
||||
|
||||
@@ -1505,27 +1505,27 @@ Output:
|
||||
|
||||
2a [50, 97, 0, 0]
|
||||
|
||||
**Returns**
|
||||
- The resulting string after appending the unsigned integer value
|
||||
**Returns**
|
||||
- The resulting string after writing the unsigned integer value
|
||||
*/
|
||||
append_uint :: proc(buf: []byte, u: u64, base: int) -> string {
|
||||
return append_bits(buf, u, base, false, 8*size_of(uint), digits, nil)
|
||||
write_uint :: proc(buf: []byte, u: u64, base: int) -> string {
|
||||
return write_bits(buf, u, base, false, 8*size_of(uint), digits, nil)
|
||||
}
|
||||
/*
|
||||
Appends a signed integer value as a string to the given buffer with the specified base
|
||||
/*
|
||||
Writes a signed integer value as a string to the given buffer with the specified base
|
||||
|
||||
**Inputs**
|
||||
- buf: The buffer to append the signed integer value to
|
||||
- i: The signed integer value to be appended
|
||||
**Inputs**
|
||||
- buf: The buffer to write the signed integer value to
|
||||
- i: The signed integer value to be written
|
||||
- base: The base to use for converting the integer value
|
||||
|
||||
Example:
|
||||
|
||||
import "core:fmt"
|
||||
import "core:strconv"
|
||||
append_int_example :: proc() {
|
||||
write_int_example :: proc() {
|
||||
buf: [4]byte
|
||||
result := strconv.append_int(buf[:], -42, 10)
|
||||
result := strconv.write_int(buf[:], -42, 10)
|
||||
fmt.println(result, buf)
|
||||
}
|
||||
|
||||
@@ -1533,23 +1533,23 @@ Output:
|
||||
|
||||
-42 [45, 52, 50, 0]
|
||||
|
||||
**Returns**
|
||||
- The resulting string after appending the signed integer value
|
||||
**Returns**
|
||||
- The resulting string after writing the signed integer value
|
||||
*/
|
||||
append_int :: proc(buf: []byte, i: i64, base: int) -> string {
|
||||
return append_bits(buf, u64(i), base, true, 8*size_of(int), digits, nil)
|
||||
write_int :: proc(buf: []byte, i: i64, base: int) -> string {
|
||||
return write_bits(buf, u64(i), base, true, 8*size_of(int), digits, nil)
|
||||
}
|
||||
|
||||
|
||||
|
||||
append_u128 :: proc(buf: []byte, u: u128, base: int) -> string {
|
||||
return append_bits_128(buf, u, base, false, 8*size_of(uint), digits, nil)
|
||||
write_u128 :: proc(buf: []byte, u: u128, base: int) -> string {
|
||||
return write_bits_128(buf, u, base, false, 8*size_of(uint), digits, nil)
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
Converts an integer value to a string and stores it in the given buffer
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- buf: The buffer to store the resulting string
|
||||
- i: The integer value to be converted
|
||||
|
||||
@@ -1567,16 +1567,16 @@ Output:
|
||||
|
||||
42 [52, 50, 0, 0]
|
||||
|
||||
**Returns**
|
||||
**Returns**
|
||||
- The resulting string after converting the integer value
|
||||
*/
|
||||
itoa :: proc(buf: []byte, i: int) -> string {
|
||||
return append_int(buf, i64(i), 10)
|
||||
return write_int(buf, i64(i), 10)
|
||||
}
|
||||
/*
|
||||
Converts a string to an integer value
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- s: The string to be converted
|
||||
|
||||
Example:
|
||||
@@ -1591,17 +1591,17 @@ Output:
|
||||
|
||||
42
|
||||
|
||||
**Returns**
|
||||
**Returns**
|
||||
- The resulting integer value
|
||||
*/
|
||||
atoi :: proc(s: string) -> int {
|
||||
v, _ := parse_int(s)
|
||||
return v
|
||||
}
|
||||
/*
|
||||
/*
|
||||
Converts a string to a float64 value
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- s: The string to be converted
|
||||
|
||||
Example:
|
||||
@@ -1616,21 +1616,21 @@ Output:
|
||||
|
||||
3.140
|
||||
|
||||
**Returns**
|
||||
**Returns**
|
||||
- The resulting float64 value after converting the string
|
||||
*/
|
||||
atof :: proc(s: string) -> f64 {
|
||||
v, _ := parse_f64(s)
|
||||
return v
|
||||
}
|
||||
// Alias to `append_float`
|
||||
ftoa :: append_float
|
||||
/*
|
||||
Appends a float64 value as a string to the given buffer with the specified format and precision
|
||||
// Alias to `write_float`
|
||||
ftoa :: write_float
|
||||
/*
|
||||
Writes a float64 value as a string to the given buffer with the specified format and precision
|
||||
|
||||
**Inputs**
|
||||
- buf: The buffer to append the float64 value to
|
||||
- f: The float64 value to be appended
|
||||
**Inputs**
|
||||
- buf: The buffer to write the float64 value to
|
||||
- f: The float64 value to be written
|
||||
- fmt: The byte specifying the format to use for the conversion
|
||||
- prec: The precision to use for the conversion
|
||||
- bit_size: The size of the float in bits (32 or 64)
|
||||
@@ -1639,9 +1639,9 @@ Example:
|
||||
|
||||
import "core:fmt"
|
||||
import "core:strconv"
|
||||
append_float_example :: proc() {
|
||||
write_float_example :: proc() {
|
||||
buf: [8]byte
|
||||
result := strconv.append_float(buf[:], 3.14159, 'f', 2, 64)
|
||||
result := strconv.write_float(buf[:], 3.14159, 'f', 2, 64)
|
||||
fmt.println(result, buf)
|
||||
}
|
||||
|
||||
@@ -1649,20 +1649,20 @@ Output:
|
||||
|
||||
+3.14 [43, 51, 46, 49, 52, 0, 0, 0]
|
||||
|
||||
**Returns**
|
||||
- The resulting string after appending the float
|
||||
**Returns**
|
||||
- The resulting string after writing the float
|
||||
*/
|
||||
append_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string {
|
||||
write_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string {
|
||||
return string(generic_ftoa(buf, f, fmt, prec, bit_size))
|
||||
}
|
||||
/*
|
||||
Appends a quoted string representation of the input string to a given byte slice and returns the result as a string
|
||||
Writes a quoted string representation of the input string to a given byte slice and returns the result as a string
|
||||
|
||||
**Inputs**
|
||||
- buf: The byte slice to which the quoted string will be appended
|
||||
**Inputs**
|
||||
- buf: The byte slice to which the quoted string will be written
|
||||
- str: The input string to be quoted
|
||||
|
||||
!! ISSUE !! NOT EXPECTED -- "\"hello\"" was expected
|
||||
!! ISSUE !! NOT EXPECTED -- "\"hello\"" was expected
|
||||
|
||||
Example:
|
||||
|
||||
@@ -1678,8 +1678,8 @@ Output:
|
||||
|
||||
"'h''e''l''l''o'" [34, 39, 104, 39, 39, 101, 39, 39, 108, 39, 39, 108, 39, 39, 111, 39, 34, 0, 0, 0]
|
||||
|
||||
**Returns**
|
||||
- The resulting string after appending the quoted string representation
|
||||
**Returns**
|
||||
- The resulting string after writing the quoted string representation
|
||||
*/
|
||||
quote :: proc(buf: []byte, str: string) -> string {
|
||||
write_byte :: proc(buf: []byte, i: ^int, bytes: ..byte) {
|
||||
@@ -1719,10 +1719,10 @@ quote :: proc(buf: []byte, str: string) -> string {
|
||||
return string(buf[:i])
|
||||
}
|
||||
/*
|
||||
Appends a quoted rune representation of the input rune to a given byte slice and returns the result as a string
|
||||
Writes a quoted rune representation of the input rune to a given byte slice and returns the result as a string
|
||||
|
||||
**Inputs**
|
||||
- buf: The byte slice to which the quoted rune will be appended
|
||||
**Inputs**
|
||||
- buf: The byte slice to which the quoted rune will be written
|
||||
- r: The input rune to be quoted
|
||||
|
||||
Example:
|
||||
@@ -1739,8 +1739,8 @@ Output:
|
||||
|
||||
'A' [39, 65, 39, 0]
|
||||
|
||||
**Returns**
|
||||
- The resulting string after appending the quoted rune representation
|
||||
**Returns**
|
||||
- The resulting string after writing the quoted rune representation
|
||||
*/
|
||||
quote_rune :: proc(buf: []byte, r: rune) -> string {
|
||||
write_byte :: proc(buf: []byte, i: ^int, bytes: ..byte) {
|
||||
@@ -1783,7 +1783,7 @@ quote_rune :: proc(buf: []byte, r: rune) -> string {
|
||||
if r < 32 {
|
||||
write_string(buf, &i, "\\x")
|
||||
b: [2]byte
|
||||
s := append_bits(b[:], u64(r), 16, true, 64, digits, nil)
|
||||
s := write_bits(b[:], u64(r), 16, true, 64, digits, nil)
|
||||
switch len(s) {
|
||||
case 0: write_string(buf, &i, "00")
|
||||
case 1: write_rune(buf, &i, '0')
|
||||
@@ -1800,11 +1800,11 @@ quote_rune :: proc(buf: []byte, r: rune) -> string {
|
||||
/*
|
||||
Unquotes a single character from the input string, considering the given quote character
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- str: The input string containing the character to unquote
|
||||
- quote: The quote character to consider (e.g., '"')
|
||||
|
||||
Example:
|
||||
Example:
|
||||
|
||||
import "core:fmt"
|
||||
import "core:strconv"
|
||||
@@ -1815,12 +1815,12 @@ Example:
|
||||
fmt.printf("r: <%v>, multiple_bytes:%v, tail_string:<%s>, success:%v\n",r, multiple_bytes, tail_string, success)
|
||||
}
|
||||
|
||||
Output:
|
||||
Output:
|
||||
|
||||
Source: 'The' raven
|
||||
r: <'>, multiple_bytes:false, tail_string:<The' raven>, success:true
|
||||
|
||||
**Returns**
|
||||
**Returns**
|
||||
- r: The unquoted rune
|
||||
- multiple_bytes: A boolean indicating if the rune has multiple bytes
|
||||
- tail_string: The remaining portion of the input string after unquoting the character
|
||||
@@ -1923,13 +1923,13 @@ unquote_char :: proc(str: string, quote: byte) -> (r: rune, multiple_bytes: bool
|
||||
/*
|
||||
Unquotes the input string considering any type of quote character and returns the unquoted string
|
||||
|
||||
**Inputs**
|
||||
**Inputs**
|
||||
- lit: The input string to unquote
|
||||
- allocator: (default: context.allocator)
|
||||
|
||||
WARNING: This procedure gives unexpected results if the quotes are not the first and last characters.
|
||||
|
||||
Example:
|
||||
Example:
|
||||
|
||||
import "core:fmt"
|
||||
import "core:strconv"
|
||||
@@ -1947,10 +1947,10 @@ Example:
|
||||
src="The raven \'Huginn\' is black."
|
||||
s, allocated, ok = strconv.unquote_string(src) // Will produce undesireable results
|
||||
fmt.println(src)
|
||||
fmt.printf("Unquoted: <%s>, alloc:%v, ok:%v\n", s, allocated, ok)
|
||||
fmt.printf("Unquoted: <%s>, alloc:%v, ok:%v\n", s, allocated, ok)
|
||||
}
|
||||
|
||||
Output:
|
||||
Output:
|
||||
|
||||
"The raven Huginn is black."
|
||||
Unquoted: <The raven Huginn is black.>, alloc:false, ok:true
|
||||
@@ -1961,7 +1961,7 @@ Output:
|
||||
The raven 'Huginn' is black.
|
||||
Unquoted: <he raven 'Huginn' is black>, alloc:false, ok:true
|
||||
|
||||
**Returns**
|
||||
**Returns**
|
||||
- res: The resulting unquoted string
|
||||
- allocated: A boolean indicating if the resulting string was allocated using the provided allocator
|
||||
- success: A boolean indicating whether the unquoting was successful
|
||||
@@ -2002,7 +2002,7 @@ unquote_string :: proc(lit: string, allocator := context.allocator) -> (res: str
|
||||
return s, false, true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
context.allocator = allocator
|
||||
|
||||
buf_len := 3*len(s) / 2
|
||||
|
||||
@@ -675,7 +675,7 @@ Returns:
|
||||
*/
|
||||
write_float :: proc(b: ^Builder, f: f64, fmt: byte, prec, bit_size: int, always_signed := false) -> (n: int) {
|
||||
buf: [384]byte
|
||||
s := strconv.append_float(buf[:], f, fmt, prec, bit_size)
|
||||
s := strconv.write_float(buf[:], f, fmt, prec, bit_size)
|
||||
// If the result starts with a `+` then unless we always want signed results,
|
||||
// we skip it unless it's followed by an `I` (because of +Inf).
|
||||
if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
|
||||
@@ -699,7 +699,7 @@ Returns:
|
||||
*/
|
||||
write_f16 :: proc(b: ^Builder, f: f16, fmt: byte, always_signed := false) -> (n: int) {
|
||||
buf: [384]byte
|
||||
s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f))
|
||||
s := strconv.write_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f))
|
||||
if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
|
||||
s = s[1:]
|
||||
}
|
||||
@@ -739,7 +739,7 @@ Output:
|
||||
*/
|
||||
write_f32 :: proc(b: ^Builder, f: f32, fmt: byte, always_signed := false) -> (n: int) {
|
||||
buf: [384]byte
|
||||
s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f))
|
||||
s := strconv.write_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f))
|
||||
if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
|
||||
s = s[1:]
|
||||
}
|
||||
@@ -761,7 +761,7 @@ Returns:
|
||||
*/
|
||||
write_f64 :: proc(b: ^Builder, f: f64, fmt: byte, always_signed := false) -> (n: int) {
|
||||
buf: [384]byte
|
||||
s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f))
|
||||
s := strconv.write_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f))
|
||||
if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
|
||||
s = s[1:]
|
||||
}
|
||||
@@ -782,7 +782,7 @@ Returns:
|
||||
*/
|
||||
write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) -> (n: int) {
|
||||
buf: [32]byte
|
||||
s := strconv.append_bits(buf[:], i, base, false, 64, strconv.digits, nil)
|
||||
s := strconv.write_bits(buf[:], i, base, false, 64, strconv.digits, nil)
|
||||
return write_string(b, s)
|
||||
}
|
||||
/*
|
||||
@@ -800,7 +800,7 @@ Returns:
|
||||
*/
|
||||
write_i64 :: proc(b: ^Builder, i: i64, base: int = 10) -> (n: int) {
|
||||
buf: [32]byte
|
||||
s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil)
|
||||
s := strconv.write_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil)
|
||||
return write_string(b, s)
|
||||
}
|
||||
/*
|
||||
|
||||
+82
-64
@@ -28,24 +28,7 @@ clone :: proc(s: string, allocator := context.allocator, loc := #caller_location
|
||||
copy(c, s)
|
||||
return string(c), nil
|
||||
}
|
||||
/*
|
||||
Clones a string safely (returns early with an allocation error on failure)
|
||||
|
||||
*Allocates Using Provided Allocator*
|
||||
|
||||
Inputs:
|
||||
- s: The string to be cloned
|
||||
- allocator: (default: context.allocator)
|
||||
- loc: The caller location for debugging purposes (default: #caller_location)
|
||||
|
||||
Returns:
|
||||
- res: The cloned string
|
||||
- err: An allocator error if one occured, `nil` otherwise
|
||||
*/
|
||||
@(deprecated="Prefer clone. It now returns an optional allocator error")
|
||||
clone_safe :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> (res: string, err: mem.Allocator_Error) {
|
||||
return clone(s, allocator, loc)
|
||||
}
|
||||
/*
|
||||
Clones a string and appends a null-byte to make it a cstring
|
||||
|
||||
@@ -66,6 +49,7 @@ clone_to_cstring :: proc(s: string, allocator := context.allocator, loc := #call
|
||||
c[len(s)] = 0
|
||||
return cstring(&c[0]), nil
|
||||
}
|
||||
|
||||
/*
|
||||
Transmutes a raw pointer into a string. Non-allocating.
|
||||
|
||||
@@ -81,6 +65,7 @@ Returns:
|
||||
string_from_ptr :: proc(ptr: ^byte, len: int) -> (res: string) {
|
||||
return transmute(string)mem.Raw_String{ptr, len}
|
||||
}
|
||||
|
||||
/*
|
||||
Transmutes a raw pointer (null-terminated) into a string. Non-allocating. Searches for a null-byte from `0..<len`, otherwise `len` will be the end size
|
||||
|
||||
@@ -99,20 +84,7 @@ string_from_null_terminated_ptr :: proc "contextless" (ptr: [^]byte, len: int) -
|
||||
s = truncate_to_byte(s, 0)
|
||||
return s
|
||||
}
|
||||
/*
|
||||
Gets the raw byte pointer for the start of a string `str`
|
||||
|
||||
Inputs:
|
||||
- str: The input string
|
||||
|
||||
Returns:
|
||||
- res: A pointer to the start of the string's bytes
|
||||
*/
|
||||
@(deprecated="Prefer the builtin raw_data.")
|
||||
ptr_from_string :: proc(str: string) -> (res: ^byte) {
|
||||
d := transmute(mem.Raw_String)str
|
||||
return d.data
|
||||
}
|
||||
/*
|
||||
Converts a string `str` to a cstring
|
||||
|
||||
@@ -128,6 +100,7 @@ unsafe_string_to_cstring :: proc(str: string) -> (res: cstring) {
|
||||
d := transmute(mem.Raw_String)str
|
||||
return cstring(d.data)
|
||||
}
|
||||
|
||||
/*
|
||||
Truncates a string `str` at the first occurrence of char/byte `b`
|
||||
|
||||
@@ -147,6 +120,7 @@ truncate_to_byte :: proc "contextless" (str: string, b: byte) -> (res: string) {
|
||||
}
|
||||
return str[:n]
|
||||
}
|
||||
|
||||
/*
|
||||
Truncates a string `str` at the first occurrence of rune `r` as a slice of the original, entire string if not found
|
||||
|
||||
@@ -164,6 +138,7 @@ truncate_to_rune :: proc(str: string, r: rune) -> (res: string) {
|
||||
}
|
||||
return str[:n]
|
||||
}
|
||||
|
||||
/*
|
||||
Clones a byte array `s` and appends a null-byte
|
||||
|
||||
@@ -184,6 +159,7 @@ clone_from_bytes :: proc(s: []byte, allocator := context.allocator, loc := #call
|
||||
c[len(s)] = 0
|
||||
return string(c[:len(s)]), nil
|
||||
}
|
||||
|
||||
/*
|
||||
Clones a cstring `s` as a string
|
||||
|
||||
@@ -201,6 +177,7 @@ Returns:
|
||||
clone_from_cstring :: proc(s: cstring, allocator := context.allocator, loc := #caller_location) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
|
||||
return clone(string(s), allocator, loc)
|
||||
}
|
||||
|
||||
/*
|
||||
Clones a string from a byte pointer `ptr` and a byte length `len`
|
||||
|
||||
@@ -222,6 +199,7 @@ clone_from_ptr :: proc(ptr: ^byte, len: int, allocator := context.allocator, loc
|
||||
s := string_from_ptr(ptr, len)
|
||||
return clone(s, allocator, loc)
|
||||
}
|
||||
|
||||
// Overloaded procedure to clone from a string, `[]byte`, `cstring` or a `^byte` + length
|
||||
clone_from :: proc{
|
||||
clone,
|
||||
@@ -229,6 +207,7 @@ clone_from :: proc{
|
||||
clone_from_cstring,
|
||||
clone_from_ptr,
|
||||
}
|
||||
|
||||
/*
|
||||
Clones a string from a null-terminated cstring `ptr` and a byte length `len`
|
||||
|
||||
@@ -251,6 +230,7 @@ clone_from_cstring_bounded :: proc(ptr: cstring, len: int, allocator := context.
|
||||
s = truncate_to_byte(s, 0)
|
||||
return clone(s, allocator, loc)
|
||||
}
|
||||
|
||||
/*
|
||||
Compares two strings, returning a value representing which one comes first lexicographically.
|
||||
-1 for `lhs`; 1 for `rhs`, or 0 if they are equal.
|
||||
@@ -265,6 +245,7 @@ Returns:
|
||||
compare :: proc "contextless" (lhs, rhs: string) -> (result: int) {
|
||||
return mem.compare(transmute([]byte)lhs, transmute([]byte)rhs)
|
||||
}
|
||||
|
||||
/*
|
||||
Checks if rune `r` in the string `s`
|
||||
|
||||
@@ -283,6 +264,7 @@ contains_rune :: proc(s: string, r: rune) -> (result: bool) {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/*
|
||||
Returns true when the string `substr` is contained inside the string `s`
|
||||
|
||||
@@ -314,6 +296,7 @@ Output:
|
||||
contains :: proc(s, substr: string) -> (res: bool) {
|
||||
return index(s, substr) >= 0
|
||||
}
|
||||
|
||||
/*
|
||||
Returns `true` when the string `s` contains any of the characters inside the string `chars`
|
||||
|
||||
@@ -386,6 +369,7 @@ Output:
|
||||
rune_count :: proc(s: string) -> (res: int) {
|
||||
return utf8.rune_count_in_string(s)
|
||||
}
|
||||
|
||||
/*
|
||||
Returns whether the strings `u` and `v` are the same alpha characters, ignoring different casings
|
||||
Works with UTF-8 string content
|
||||
@@ -508,6 +492,7 @@ prefix_length :: proc "contextless" (a, b: string) -> (n: int) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the common prefix between strings `a` and `b`
|
||||
|
||||
@@ -540,6 +525,7 @@ Output:
|
||||
common_prefix :: proc(a, b: string) -> string {
|
||||
return a[:prefix_length(a, b)]
|
||||
}
|
||||
|
||||
/*
|
||||
Determines if a string `s` starts with a given `prefix`
|
||||
|
||||
@@ -661,24 +647,7 @@ join :: proc(a: []string, sep: string, allocator := context.allocator, loc := #c
|
||||
}
|
||||
return string(b), nil
|
||||
}
|
||||
/*
|
||||
Joins a slice of strings `a` with a `sep` string, returns an error on allocation failure
|
||||
|
||||
*Allocates Using Provided Allocator*
|
||||
|
||||
Inputs:
|
||||
- a: A slice of strings to join
|
||||
- sep: The separator string
|
||||
- allocator: (default is context.allocator)
|
||||
|
||||
Returns:
|
||||
- str: A combined string from the slice of strings `a` separated with the `sep` string
|
||||
- err: An allocator error if one occured, `nil` otherwise
|
||||
*/
|
||||
@(deprecated="Prefer join. It now returns an optional allocator error")
|
||||
join_safe :: proc(a: []string, sep: string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) {
|
||||
return join(a, sep, allocator)
|
||||
}
|
||||
/*
|
||||
Returns a combined string from the slice of strings `a` without a separator
|
||||
|
||||
@@ -723,22 +692,6 @@ concatenate :: proc(a: []string, allocator := context.allocator, loc := #caller_
|
||||
}
|
||||
return string(b), nil
|
||||
}
|
||||
/*
|
||||
Returns a combined string from the slice of strings `a` without a separator, or an error if allocation fails
|
||||
|
||||
*Allocates Using Provided Allocator*
|
||||
|
||||
Inputs:
|
||||
- a: A slice of strings to concatenate
|
||||
- allocator: (default is context.allocator)
|
||||
|
||||
Returns:
|
||||
The concatenated string, and an error if allocation fails
|
||||
*/
|
||||
@(deprecated="Prefer concatenate. It now returns an optional allocator error")
|
||||
concatenate_safe :: proc(a: []string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) {
|
||||
return concatenate(a, allocator)
|
||||
}
|
||||
|
||||
/*
|
||||
Returns a substring of the input string `s` with the specified rune offset and length
|
||||
@@ -901,6 +854,7 @@ _split :: proc(s_, sep: string, sep_save, n_: int, allocator := context.allocato
|
||||
|
||||
return res[:i+1], nil
|
||||
}
|
||||
|
||||
/*
|
||||
Splits a string into parts based on a separator.
|
||||
|
||||
@@ -936,6 +890,7 @@ Output:
|
||||
split :: proc(s, sep: string, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error {
|
||||
return _split(s, sep, 0, -1, allocator)
|
||||
}
|
||||
|
||||
/*
|
||||
Splits a string into parts based on a separator. If n < count of seperators, the remainder of the string is returned in the last entry.
|
||||
|
||||
@@ -972,6 +927,7 @@ Output:
|
||||
split_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error {
|
||||
return _split(s, sep, 0, n, allocator)
|
||||
}
|
||||
|
||||
/*
|
||||
Splits a string into parts after the separator, retaining it in the substrings.
|
||||
|
||||
@@ -1007,6 +963,7 @@ Output:
|
||||
split_after :: proc(s, sep: string, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error {
|
||||
return _split(s, sep, len(sep), -1, allocator)
|
||||
}
|
||||
|
||||
/*
|
||||
Splits a string into a total of `n` parts after the separator.
|
||||
|
||||
@@ -1043,6 +1000,7 @@ Output:
|
||||
split_after_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error {
|
||||
return _split(s, sep, len(sep), n, allocator)
|
||||
}
|
||||
|
||||
/*
|
||||
Searches for the first occurrence of `sep` in the given string and returns the substring
|
||||
up to (but not including) the separator, as well as a boolean indicating success.
|
||||
@@ -1083,6 +1041,7 @@ _split_iterator :: proc(s: ^string, sep: string, sep_save: int) -> (res: string,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Splits the input string by the byte separator in an iterator fashion.
|
||||
|
||||
@@ -1129,6 +1088,7 @@ split_by_byte_iterator :: proc(s: ^string, sep: u8) -> (res: string, ok: bool) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Splits the input string by the separator string in an iterator fashion.
|
||||
|
||||
@@ -1164,6 +1124,7 @@ Output:
|
||||
split_iterator :: proc(s: ^string, sep: string) -> (res: string, ok: bool) {
|
||||
return _split_iterator(s, sep, 0)
|
||||
}
|
||||
|
||||
/*
|
||||
Splits the input string after every separator string in an iterator fashion.
|
||||
|
||||
@@ -1199,6 +1160,7 @@ Output:
|
||||
split_after_iterator :: proc(s: ^string, sep: string) -> (res: string, ok: bool) {
|
||||
return _split_iterator(s, sep, len(sep))
|
||||
}
|
||||
|
||||
/*
|
||||
Trims the carriage return character from the end of the input string.
|
||||
|
||||
@@ -1220,6 +1182,7 @@ _trim_cr :: proc(s: string) -> (res: string) {
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
/*
|
||||
Splits the input string at every line break `\n`.
|
||||
|
||||
@@ -1257,6 +1220,7 @@ split_lines :: proc(s: string, allocator := context.allocator) -> (res: []string
|
||||
}
|
||||
return lines, nil
|
||||
}
|
||||
|
||||
/*
|
||||
Splits the input string at every line break `\n` for `n` parts.
|
||||
|
||||
@@ -1297,6 +1261,7 @@ split_lines_n :: proc(s: string, n: int, allocator := context.allocator) -> (res
|
||||
}
|
||||
return lines, nil
|
||||
}
|
||||
|
||||
/*
|
||||
Splits the input string at every line break `\n` leaving the `\n` in the resulting strings.
|
||||
|
||||
@@ -1336,6 +1301,7 @@ split_lines_after :: proc(s: string, allocator := context.allocator) -> (res: []
|
||||
}
|
||||
return lines, nil
|
||||
}
|
||||
|
||||
/*
|
||||
Splits the input string at every line break `\n` leaving the `\n` in the resulting strings.
|
||||
Only runs for n parts.
|
||||
@@ -1377,6 +1343,7 @@ split_lines_after_n :: proc(s: string, n: int, allocator := context.allocator) -
|
||||
}
|
||||
return lines, nil
|
||||
}
|
||||
|
||||
/*
|
||||
Splits the input string at every line break `\n`.
|
||||
Returns the current split string every iteration until the string is consumed.
|
||||
@@ -1411,6 +1378,7 @@ split_lines_iterator :: proc(s: ^string) -> (line: string, ok: bool) {
|
||||
line = _split_iterator(s, sep, 0) or_return
|
||||
return _trim_cr(line), true
|
||||
}
|
||||
|
||||
/*
|
||||
Splits the input string at every line break `\n`.
|
||||
Returns the current split string with line breaks included every iteration until the string is consumed.
|
||||
@@ -1448,6 +1416,7 @@ split_lines_after_iterator :: proc(s: ^string) -> (line: string, ok: bool) {
|
||||
line = _split_iterator(s, sep, len(sep)) or_return
|
||||
return _trim_cr(line), true
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the byte offset of the first byte `c` in the string s it finds, -1 when not found.
|
||||
NOTE: Can't find UTF-8 based runes.
|
||||
@@ -1482,6 +1451,7 @@ Output:
|
||||
index_byte :: proc "contextless" (s: string, c: byte) -> (res: int) {
|
||||
return #force_inline bytes.index_byte(transmute([]u8)s, c)
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the byte offset of the last byte `c` in the string `s`, -1 when not found.
|
||||
|
||||
@@ -1517,6 +1487,7 @@ Output:
|
||||
last_index_byte :: proc "contextless" (s: string, c: byte) -> (res: int) {
|
||||
return #force_inline bytes.last_index_byte(transmute([]u8)s, c)
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the byte offset of the first rune `r` in the string `s` it finds, -1 when not found.
|
||||
Invalid runes return -1
|
||||
@@ -1657,6 +1628,7 @@ index :: proc "contextless" (s, substr: string) -> (res: int) {
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the last byte offset of the string `substr` in the string `s`, -1 when not found.
|
||||
|
||||
@@ -1734,6 +1706,7 @@ last_index :: proc(s, substr: string) -> (res: int) {
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the index of any first char of `chars` found in `s`, -1 if not found.
|
||||
|
||||
@@ -1797,6 +1770,7 @@ index_any :: proc(s, chars: string) -> (res: int) {
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
/*
|
||||
Finds the last occurrence of any character in `chars` within `s`. Iterates in reverse.
|
||||
|
||||
@@ -1878,6 +1852,7 @@ last_index_any :: proc(s, chars: string) -> (res: int) {
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
/*
|
||||
Finds the first occurrence of any substring in `substrs` within `s`
|
||||
|
||||
@@ -1919,6 +1894,7 @@ index_multi :: proc(s: string, substrs: []string) -> (idx: int, width: int) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Counts the number of non-overlapping occurrences of `substr` in `s`
|
||||
|
||||
@@ -1985,6 +1961,7 @@ count :: proc(s, substr: string) -> (res: int) {
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
/*
|
||||
Repeats the string `s` `count` times, concatenating the result
|
||||
|
||||
@@ -2030,6 +2007,7 @@ repeat :: proc(s: string, count: int, allocator := context.allocator, loc := #ca
|
||||
}
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
/*
|
||||
Replaces all occurrences of `old` in `s` with `new`
|
||||
|
||||
@@ -2063,9 +2041,11 @@ Output:
|
||||
zzzz true
|
||||
|
||||
*/
|
||||
|
||||
replace_all :: proc(s, old, new: string, allocator := context.allocator) -> (output: string, was_allocation: bool) {
|
||||
return replace(s, old, new, -1, allocator)
|
||||
}
|
||||
|
||||
/*
|
||||
Replaces n instances of old in the string s with the new string
|
||||
|
||||
@@ -2144,6 +2124,7 @@ replace :: proc(s, old, new: string, n: int, allocator := context.allocator, loc
|
||||
output = string(t[0:w])
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Removes the key string `n` times from the `s` string
|
||||
|
||||
@@ -2182,6 +2163,7 @@ Output:
|
||||
remove :: proc(s, key: string, n: int, allocator := context.allocator) -> (output: string, was_allocation: bool) {
|
||||
return replace(s, key, "", n, allocator)
|
||||
}
|
||||
|
||||
/*
|
||||
Removes all the `key` string instances from the `s` string
|
||||
|
||||
@@ -2217,6 +2199,7 @@ Output:
|
||||
remove_all :: proc(s, key: string, allocator := context.allocator) -> (output: string, was_allocation: bool) {
|
||||
return remove(s, key, -1, allocator)
|
||||
}
|
||||
|
||||
// Returns true if is an ASCII space character ('\t', '\n', '\v', '\f', '\r', ' ')
|
||||
@(private) _ascii_space := [256]bool{'\t' = true, '\n' = true, '\v' = true, '\f' = true, '\r' = true, ' ' = true}
|
||||
|
||||
@@ -2320,6 +2303,7 @@ index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> (res: int
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Same as `index_proc`, but the procedure p takes a raw pointer for state
|
||||
index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> (res: int) {
|
||||
for r, i in s {
|
||||
@@ -2329,6 +2313,7 @@ index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: r
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Finds the index of the *last* rune in the string s for which the procedure p returns the same value as truth
|
||||
last_index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> (res: int) {
|
||||
// TODO(bill): Probably use Rabin-Karp Search
|
||||
@@ -2341,6 +2326,7 @@ last_index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> (res
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Same as `index_proc_with_state`, runs through the string in reverse
|
||||
last_index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> (res: int) {
|
||||
// TODO(bill): Probably use Rabin-Karp Search
|
||||
@@ -2353,6 +2339,7 @@ last_index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, sta
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
/*
|
||||
Trims the input string `s` from the left until the procedure `p` returns false
|
||||
|
||||
@@ -2387,6 +2374,7 @@ trim_left_proc :: proc(s: string, p: proc(rune) -> bool) -> (res: string) {
|
||||
}
|
||||
return s[i:]
|
||||
}
|
||||
|
||||
/*
|
||||
Trims the input string `s` from the left until the procedure `p` with state returns false
|
||||
|
||||
@@ -2405,6 +2393,7 @@ trim_left_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, stat
|
||||
}
|
||||
return s[i:]
|
||||
}
|
||||
|
||||
/*
|
||||
Trims the input string `s` from the right until the procedure `p` returns `false`
|
||||
|
||||
@@ -2442,6 +2431,7 @@ trim_right_proc :: proc(s: string, p: proc(rune) -> bool) -> (res: string) {
|
||||
}
|
||||
return s[0:i]
|
||||
}
|
||||
|
||||
/*
|
||||
Trims the input string `s` from the right until the procedure `p` with state returns `false`
|
||||
|
||||
@@ -2463,6 +2453,7 @@ trim_right_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, sta
|
||||
}
|
||||
return s[0:i]
|
||||
}
|
||||
|
||||
// Procedure for `trim_*_proc` variants, which has a string rawptr cast + rune comparison
|
||||
is_in_cutset :: proc(state: rawptr, r: rune) -> (res: bool) {
|
||||
cutset := (^string)(state)^
|
||||
@@ -2473,6 +2464,7 @@ is_in_cutset :: proc(state: rawptr, r: rune) -> (res: bool) {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/*
|
||||
Trims the cutset string from the `s` string
|
||||
|
||||
@@ -2490,6 +2482,7 @@ trim_left :: proc(s: string, cutset: string) -> (res: string) {
|
||||
state := cutset
|
||||
return trim_left_proc_with_state(s, is_in_cutset, &state)
|
||||
}
|
||||
|
||||
/*
|
||||
Trims the cutset string from the `s` string from the right
|
||||
|
||||
@@ -2507,6 +2500,7 @@ trim_right :: proc(s: string, cutset: string) -> (res: string) {
|
||||
state := cutset
|
||||
return trim_right_proc_with_state(s, is_in_cutset, &state)
|
||||
}
|
||||
|
||||
/*
|
||||
Trims the cutset string from the `s` string, both from left and right
|
||||
|
||||
@@ -2520,6 +2514,7 @@ Returns:
|
||||
trim :: proc(s: string, cutset: string) -> (res: string) {
|
||||
return trim_right(trim_left(s, cutset), cutset)
|
||||
}
|
||||
|
||||
/*
|
||||
Trims until a valid non-space rune from the left, "\t\txyz\t\t" -> "xyz\t\t"
|
||||
|
||||
@@ -2532,6 +2527,7 @@ Returns:
|
||||
trim_left_space :: proc(s: string) -> (res: string) {
|
||||
return trim_left_proc(s, is_space)
|
||||
}
|
||||
|
||||
/*
|
||||
Trims from the right until a valid non-space rune, "\t\txyz\t\t" -> "\t\txyz"
|
||||
|
||||
@@ -2544,6 +2540,7 @@ Returns:
|
||||
trim_right_space :: proc(s: string) -> (res: string) {
|
||||
return trim_right_proc(s, is_space)
|
||||
}
|
||||
|
||||
/*
|
||||
Trims from both sides until a valid non-space rune, "\t\txyz\t\t" -> "xyz"
|
||||
|
||||
@@ -2556,6 +2553,7 @@ Returns:
|
||||
trim_space :: proc(s: string) -> (res: string) {
|
||||
return trim_right_space(trim_left_space(s))
|
||||
}
|
||||
|
||||
/*
|
||||
Trims null runes from the left, "\x00\x00testing\x00\x00" -> "testing\x00\x00"
|
||||
|
||||
@@ -2568,6 +2566,7 @@ Returns:
|
||||
trim_left_null :: proc(s: string) -> (res: string) {
|
||||
return trim_left_proc(s, is_null)
|
||||
}
|
||||
|
||||
/*
|
||||
Trims null runes from the right, "\x00\x00testing\x00\x00" -> "\x00\x00testing"
|
||||
|
||||
@@ -2580,6 +2579,7 @@ Returns:
|
||||
trim_right_null :: proc(s: string) -> (res: string) {
|
||||
return trim_right_proc(s, is_null)
|
||||
}
|
||||
|
||||
/*
|
||||
Trims null runes from both sides, "\x00\x00testing\x00\x00" -> "testing"
|
||||
|
||||
@@ -2591,6 +2591,7 @@ Returns:
|
||||
trim_null :: proc(s: string) -> (res: string) {
|
||||
return trim_right_null(trim_left_null(s))
|
||||
}
|
||||
|
||||
/*
|
||||
Trims a `prefix` string from the start of the `s` string and returns the trimmed string
|
||||
|
||||
@@ -2623,6 +2624,7 @@ trim_prefix :: proc(s, prefix: string) -> (res: string) {
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
/*
|
||||
Trims a `suffix` string from the end of the `s` string and returns the trimmed string
|
||||
|
||||
@@ -2655,6 +2657,7 @@ trim_suffix :: proc(s, suffix: string) -> (res: string) {
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
/*
|
||||
Splits the input string `s` by all possible `substrs` and returns an allocated array of strings
|
||||
|
||||
@@ -2727,6 +2730,7 @@ split_multi :: proc(s: string, substrs: []string, allocator := context.allocator
|
||||
assert(len(results) == n)
|
||||
return results[:], nil
|
||||
}
|
||||
|
||||
/*
|
||||
Splits the input string `s` by all possible `substrs` in an iterator fashion. The full string is returned if no match.
|
||||
|
||||
@@ -2786,6 +2790,7 @@ split_multi_iterate :: proc(it: ^string, substrs: []string) -> (res: string, ok:
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Replaces invalid UTF-8 characters in the input string with a specified replacement string. Adjacent invalid bytes are only replaced once.
|
||||
|
||||
@@ -2846,6 +2851,7 @@ scrub :: proc(s: string, replacement: string, allocator := context.allocator) ->
|
||||
|
||||
return to_string(b), nil
|
||||
}
|
||||
|
||||
/*
|
||||
Reverses the input string `s`
|
||||
|
||||
@@ -2889,6 +2895,7 @@ reverse :: proc(s: string, allocator := context.allocator, loc := #caller_locati
|
||||
}
|
||||
return string(buf), nil
|
||||
}
|
||||
|
||||
/*
|
||||
Expands the input string by replacing tab characters with spaces to align to a specified tab size
|
||||
|
||||
@@ -2920,6 +2927,7 @@ Output:
|
||||
abc1 abc2 abc3
|
||||
|
||||
*/
|
||||
|
||||
expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
|
||||
if tab_size <= 0 {
|
||||
panic("tab size must be positive")
|
||||
@@ -2961,6 +2969,7 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) ->
|
||||
|
||||
return to_string(b), nil
|
||||
}
|
||||
|
||||
/*
|
||||
Splits the input string `str` by the separator `sep` string and returns 3 parts. The values are slices of the original string.
|
||||
|
||||
@@ -3011,8 +3020,10 @@ partition :: proc(str, sep: string) -> (head, match, tail: string) {
|
||||
tail = str[i+len(sep):]
|
||||
return
|
||||
}
|
||||
|
||||
// Alias for centre_justify
|
||||
center_justify :: centre_justify // NOTE(bill): Because Americans exist
|
||||
|
||||
/*
|
||||
Centers the input string within a field of specified length by adding pad string on both sides, if its length is less than the target length.
|
||||
|
||||
@@ -3048,6 +3059,7 @@ centre_justify :: proc(str: string, length: int, pad: string, allocator := conte
|
||||
|
||||
return to_string(b), nil
|
||||
}
|
||||
|
||||
/*
|
||||
Left-justifies the input string within a field of specified length by adding pad string on the right side, if its length is less than the target length.
|
||||
|
||||
@@ -3082,6 +3094,7 @@ left_justify :: proc(str: string, length: int, pad: string, allocator := context
|
||||
|
||||
return to_string(b), nil
|
||||
}
|
||||
|
||||
/*
|
||||
Right-justifies the input string within a field of specified length by adding pad string on the left side, if its length is less than the target length.
|
||||
|
||||
@@ -3116,6 +3129,7 @@ right_justify :: proc(str: string, length: int, pad: string, allocator := contex
|
||||
|
||||
return to_string(b), nil
|
||||
}
|
||||
|
||||
/*
|
||||
Writes a given pad string a specified number of times to an `io.Writer`
|
||||
|
||||
@@ -3142,6 +3156,7 @@ write_pad_string :: proc(w: io.Writer, pad: string, pad_len, remains: int) {
|
||||
p = p[width:]
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Splits a string into a slice of substrings at each instance of one or more consecutive white space characters, as defined by `unicode.is_space`
|
||||
|
||||
@@ -3203,6 +3218,7 @@ fields :: proc(s: string, allocator := context.allocator, loc := #caller_locatio
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
/*
|
||||
Splits a string into a slice of substrings at each run of unicode code points `r` satisfying the predicate `f(r)`
|
||||
|
||||
@@ -3245,6 +3261,7 @@ fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.alloc
|
||||
|
||||
return substrings[:], nil
|
||||
}
|
||||
|
||||
/*
|
||||
Retrieves the first non-space substring from a mutable string reference and advances the reference. `s` is advanced from any space after the substring, or be an empty string if the substring was the remaining characters
|
||||
|
||||
@@ -3283,6 +3300,7 @@ fields_iterator :: proc(s: ^string) -> (field: string, ok: bool) {
|
||||
s^ = s[len(s):]
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Computes the Levenshtein edit distance between two strings
|
||||
|
||||
@@ -3460,4 +3478,4 @@ substring_to :: proc(s: string, rune_end: int) -> (sub: string, ok: bool) {
|
||||
}
|
||||
|
||||
return internal_substring(s, -1, rune_end)
|
||||
}
|
||||
}
|
||||
+107
-48
@@ -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
|
||||
|
||||
@@ -95,20 +95,16 @@ atomic_mutex_guard :: proc "contextless" (m: ^Atomic_Mutex) -> bool {
|
||||
|
||||
|
||||
Atomic_RW_Mutex_State :: distinct uint
|
||||
Atomic_RW_Mutex_State_Half_Width :: size_of(Atomic_RW_Mutex_State)*8/2
|
||||
Atomic_RW_Mutex_State_Is_Writing :: Atomic_RW_Mutex_State(1)
|
||||
Atomic_RW_Mutex_State_Writer :: Atomic_RW_Mutex_State(1)<<1
|
||||
Atomic_RW_Mutex_State_Reader :: Atomic_RW_Mutex_State(1)<<Atomic_RW_Mutex_State_Half_Width
|
||||
|
||||
Atomic_RW_Mutex_State_Writer_Mask :: Atomic_RW_Mutex_State(1<<(Atomic_RW_Mutex_State_Half_Width-1) - 1) << 1
|
||||
Atomic_RW_Mutex_State_Reader_Mask :: Atomic_RW_Mutex_State(1<<(Atomic_RW_Mutex_State_Half_Width-1) - 1) << Atomic_RW_Mutex_State_Half_Width
|
||||
Atomic_RW_Mutex_State_Is_Writing :: Atomic_RW_Mutex_State(1) << (size_of(Atomic_RW_Mutex_State)*8-1)
|
||||
Atomic_RW_Mutex_State_Reader :: Atomic_RW_Mutex_State(1)
|
||||
Atomic_RW_Mutex_State_Reader_Mask :: ~Atomic_RW_Mutex_State_Is_Writing
|
||||
|
||||
|
||||
// An Atomic_RW_Mutex is a reader/writer mutual exclusion lock
|
||||
// The lock can be held by any arbitrary number of readers or a single writer
|
||||
// The zero value for an Atomic_RW_Mutex is an unlocked mutex
|
||||
// An Atomic_RW_Mutex is a reader/writer mutual exclusion lock.
|
||||
// The lock can be held by any arbitrary number of readers or a single writer.
|
||||
// The zero value for an Atomic_RW_Mutex is an unlocked mutex.
|
||||
//
|
||||
// An Atomic_RW_Mutex must not be copied after first use
|
||||
// An Atomic_RW_Mutex must not be copied after first use.
|
||||
Atomic_RW_Mutex :: struct #no_copy {
|
||||
state: Atomic_RW_Mutex_State,
|
||||
mutex: Atomic_Mutex,
|
||||
@@ -118,11 +114,17 @@ Atomic_RW_Mutex :: struct #no_copy {
|
||||
// atomic_rw_mutex_lock locks rw for writing (with a single writer)
|
||||
// If the mutex is already locked for reading or writing, the mutex blocks until the mutex is available.
|
||||
atomic_rw_mutex_lock :: proc "contextless" (rw: ^Atomic_RW_Mutex) {
|
||||
_ = atomic_add(&rw.state, Atomic_RW_Mutex_State_Writer)
|
||||
atomic_mutex_lock(&rw.mutex)
|
||||
|
||||
state := atomic_or(&rw.state, Atomic_RW_Mutex_State_Writer)
|
||||
state := atomic_or(&rw.state, Atomic_RW_Mutex_State_Is_Writing)
|
||||
if state & Atomic_RW_Mutex_State_Reader_Mask != 0 {
|
||||
// There's at least one reader, so wait for the last one to post the semaphore.
|
||||
//
|
||||
// Because we hold the exclusive lock, no more readers can come in
|
||||
// during this time, which will prevent any situations where the last
|
||||
// reader is pre-empted around the count turning zero, which would
|
||||
// result in the potential for another reader to run amok after the
|
||||
// other posts.
|
||||
atomic_sema_wait(&rw.sema)
|
||||
}
|
||||
}
|
||||
@@ -138,10 +140,15 @@ atomic_rw_mutex_try_lock :: proc "contextless" (rw: ^Atomic_RW_Mutex) -> bool {
|
||||
if atomic_mutex_try_lock(&rw.mutex) {
|
||||
state := atomic_load(&rw.state)
|
||||
if state & Atomic_RW_Mutex_State_Reader_Mask == 0 {
|
||||
_ = atomic_or(&rw.state, Atomic_RW_Mutex_State_Is_Writing)
|
||||
return true
|
||||
// Compare-and-exchange for absolute certainty that no one has come in to read.
|
||||
_, ok := atomic_compare_exchange_strong(&rw.state, state, state | Atomic_RW_Mutex_State_Is_Writing)
|
||||
if ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// A reader is active or came in while we have the lock, so we need to
|
||||
// back out.
|
||||
atomic_mutex_unlock(&rw.mutex)
|
||||
}
|
||||
return false
|
||||
@@ -150,16 +157,22 @@ atomic_rw_mutex_try_lock :: proc "contextless" (rw: ^Atomic_RW_Mutex) -> bool {
|
||||
// atomic_rw_mutex_shared_lock locks rw for reading (with arbitrary number of readers)
|
||||
atomic_rw_mutex_shared_lock :: proc "contextless" (rw: ^Atomic_RW_Mutex) {
|
||||
state := atomic_load(&rw.state)
|
||||
for state & (Atomic_RW_Mutex_State_Is_Writing|Atomic_RW_Mutex_State_Writer_Mask) == 0 {
|
||||
for state & Atomic_RW_Mutex_State_Is_Writing == 0 {
|
||||
ok: bool
|
||||
state, ok = atomic_compare_exchange_weak(&rw.state, state, state + Atomic_RW_Mutex_State_Reader)
|
||||
if ok {
|
||||
// We succesfully took the shared reader lock without any writers intervening.
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// A writer is active or came in while we were trying to get a shared
|
||||
// reader lock, so now we must take the full lock in order to wait for the
|
||||
// writer to give it up.
|
||||
atomic_mutex_lock(&rw.mutex)
|
||||
// At this point, we have the lock, so we can add to the reader count.
|
||||
_ = atomic_add(&rw.state, Atomic_RW_Mutex_State_Reader)
|
||||
// Then we give up the lock to let other readers (or writers) come through.
|
||||
atomic_mutex_unlock(&rw.mutex)
|
||||
}
|
||||
|
||||
@@ -169,6 +182,8 @@ atomic_rw_mutex_shared_unlock :: proc "contextless" (rw: ^Atomic_RW_Mutex) {
|
||||
|
||||
if (state & Atomic_RW_Mutex_State_Reader_Mask == Atomic_RW_Mutex_State_Reader) &&
|
||||
(state & Atomic_RW_Mutex_State_Is_Writing != 0) {
|
||||
// We were the last reader, so post to the writer with the lock who's
|
||||
// waiting to continue.
|
||||
atomic_sema_post(&rw.sema)
|
||||
}
|
||||
}
|
||||
@@ -176,12 +191,21 @@ atomic_rw_mutex_shared_unlock :: proc "contextless" (rw: ^Atomic_RW_Mutex) {
|
||||
// atomic_rw_mutex_try_shared_lock tries to lock rw for reading (with arbitrary number of readers)
|
||||
atomic_rw_mutex_try_shared_lock :: proc "contextless" (rw: ^Atomic_RW_Mutex) -> bool {
|
||||
state := atomic_load(&rw.state)
|
||||
if state & (Atomic_RW_Mutex_State_Is_Writing|Atomic_RW_Mutex_State_Writer_Mask) == 0 {
|
||||
_, ok := atomic_compare_exchange_strong(&rw.state, state, state + Atomic_RW_Mutex_State_Reader)
|
||||
// NOTE: We need to check this in a for loop, because it is possible for
|
||||
// another reader to change the underlying state which would cause our
|
||||
// compare-and-exchange to fail.
|
||||
for state & (Atomic_RW_Mutex_State_Is_Writing) == 0 {
|
||||
ok: bool
|
||||
state, ok = atomic_compare_exchange_weak(&rw.state, state, state + Atomic_RW_Mutex_State_Reader)
|
||||
if ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
// A writer is active or came in during our lock attempt.
|
||||
|
||||
// We try to take the full lock, and if that succeeds (perhaps because the
|
||||
// writer finished during the time since we failed our CAS), we increment
|
||||
// the reader count and head on.
|
||||
if atomic_mutex_try_lock(&rw.mutex) {
|
||||
_ = atomic_add(&rw.state, Atomic_RW_Mutex_State_Reader)
|
||||
atomic_mutex_unlock(&rw.mutex)
|
||||
|
||||
@@ -82,7 +82,6 @@ Application_setActivationPolicy :: proc "c" (self: ^Application, activationPolic
|
||||
// NOTE: this is technically deprecated but still actively used (Sokol, glfw, SDL, etc.)
|
||||
// and has no clear alternative although `activate` is what Apple tells you to use,
|
||||
// that does not work the same way.
|
||||
// @(deprecated="Use NSApplication method activate instead.")
|
||||
@(objc_type=Application, objc_name="activateIgnoringOtherApps")
|
||||
Application_activateIgnoringOtherApps :: proc "c" (self: ^Application, ignoreOtherApps: BOOL) {
|
||||
msgSend(nil, self, "activateIgnoringOtherApps:", ignoreOtherApps)
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
+890
-890
File diff suppressed because it is too large
Load Diff
@@ -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")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package sysinfo
|
||||
|
||||
when !(ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 || ODIN_ARCH == .arm32 || ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64) {
|
||||
when !(ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 || ODIN_ARCH == .arm32 || ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 || ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32) {
|
||||
#assert(false, "This package is unsupported on this architecture.")
|
||||
}
|
||||
|
||||
|
||||
+18
-15
@@ -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
|
||||
|
||||
@@ -1413,7 +1413,7 @@ umask :: proc "contextless" (mask: Mode) -> Mode {
|
||||
Available since Linux 1.0.
|
||||
*/
|
||||
gettimeofday :: proc "contextless" (tv: ^Time_Val) -> (Errno) {
|
||||
ret := syscall(SYS_gettimeofday, tv)
|
||||
ret := syscall(SYS_gettimeofday, tv, rawptr(nil))
|
||||
return Errno(-ret)
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user