mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 20:11:17 +00:00
Compare commits
186 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5961bcaf86 | ||
|
|
fd5489458a | ||
|
|
cf5dd5496a | ||
|
|
ccffa03de2 | ||
|
|
ce0ab62c78 | ||
|
|
0bc5cbca74 | ||
|
|
a2aa9fae6b | ||
|
|
9d79c3f33d | ||
|
|
a26055c932 | ||
|
|
b296875c05 | ||
|
|
a54658d37f | ||
|
|
0a0e097bdf | ||
|
|
2df51bfe07 | ||
|
|
cb065152ea | ||
|
|
07fa14f00b | ||
|
|
ff1b17d18b | ||
|
|
c3d5778ae0 | ||
|
|
373ad1a399 | ||
|
|
6deed2d4a4 | ||
|
|
6324d78c32 | ||
|
|
9e14338b77 | ||
|
|
6e4614b6a4 | ||
|
|
0b52819090 | ||
|
|
404a78943d | ||
|
|
7215a9fa12 | ||
|
|
463c6957fc | ||
|
|
8ec3a52ef7 | ||
|
|
ab1efdda73 | ||
|
|
4f3b6f922d | ||
|
|
869a1d93cb | ||
|
|
5d468ccbf0 | ||
|
|
887ed5b9e9 | ||
|
|
5c1a6d7623 | ||
|
|
1b49ebf855 | ||
|
|
98155b61f1 | ||
|
|
60cdea5292 | ||
|
|
49e836b1ab | ||
|
|
5b83108dd1 | ||
|
|
a50de26c5d | ||
|
|
7b50c87858 | ||
|
|
a816a78e98 | ||
|
|
39694e65c0 | ||
|
|
2a41fa6281 | ||
|
|
49b8cfe267 | ||
|
|
20dfdf5c5d | ||
|
|
1e543dc286 | ||
|
|
06884720e5 | ||
|
|
1ea181524e | ||
|
|
b16ee3119d | ||
|
|
4e66693065 | ||
|
|
5f96b8e4c6 | ||
|
|
748a2f6530 | ||
|
|
6360ddbc77 | ||
|
|
eccc6700dc | ||
|
|
52ececba37 | ||
|
|
ffc65bcbf4 | ||
|
|
0da6bf4455 | ||
|
|
7063e684ba | ||
|
|
07363c6ecd | ||
|
|
5070840da9 | ||
|
|
4a25bcc5ee | ||
|
|
d43d7100af | ||
|
|
791cbbfb62 | ||
|
|
9b05dfdef1 | ||
|
|
b072e88bb3 | ||
|
|
af33d2b1cc | ||
|
|
d438d7510e | ||
|
|
1673aef74f | ||
|
|
b3bce10699 | ||
|
|
3ff922e12b | ||
|
|
3b718335ec | ||
|
|
f25ad512a7 | ||
|
|
5a3c484ceb | ||
|
|
331a77c1c2 | ||
|
|
045053f6bf | ||
|
|
4809979898 | ||
|
|
a5b2636b2e | ||
|
|
c483c3b75f | ||
|
|
c10d449e43 | ||
|
|
54b110a367 | ||
|
|
ee8dc3d681 | ||
|
|
a38a627a1d | ||
|
|
8aaf54e8b1 | ||
|
|
423152202f | ||
|
|
f37e7460aa | ||
|
|
8f5d5a0bb5 | ||
|
|
883052a6bb | ||
|
|
9cf271f5fb | ||
|
|
5d8cad91b1 | ||
|
|
614c6989d8 | ||
|
|
03ad72afbb | ||
|
|
b924ede71a | ||
|
|
a81f857d8c | ||
|
|
6169d7acdf | ||
|
|
4af31da7ea | ||
|
|
0bd2c81757 | ||
|
|
5ed1281451 | ||
|
|
7b649314ec | ||
|
|
e37343fbe3 | ||
|
|
7b02907830 | ||
|
|
6eee760239 | ||
|
|
ae33d1a206 | ||
|
|
3430240c2a | ||
|
|
6f11260a5c | ||
|
|
df67b7dddd | ||
|
|
f3b7df2ab0 | ||
|
|
a000ae560a | ||
|
|
0d85caf21c | ||
|
|
e34a26422f | ||
|
|
fe70f10bcc | ||
|
|
d6be1cbf65 | ||
|
|
04cd079d4e | ||
|
|
b4b14674b4 | ||
|
|
5a1831c989 | ||
|
|
e9ec421b3b | ||
|
|
872f63eecc | ||
|
|
1eb8c0ced1 | ||
|
|
b5ae2485a7 | ||
|
|
fe6817f90d | ||
|
|
98a72007f8 | ||
|
|
87c1e09a7a | ||
|
|
e0fbe31f00 | ||
|
|
7d6c844b99 | ||
|
|
a03446a26d | ||
|
|
a7e77fec78 | ||
|
|
05c3fa1afd | ||
|
|
30c8435669 | ||
|
|
94497c968b | ||
|
|
281d4af464 | ||
|
|
cb2d0e798e | ||
|
|
da67cd4eb0 | ||
|
|
7d06ca6d35 | ||
|
|
6d45450130 | ||
|
|
27bbeaf79c | ||
|
|
3af5a537da | ||
|
|
6287e8dfbf | ||
|
|
1f49a5448e | ||
|
|
ece4a2b6fb | ||
|
|
e68bd0c57f | ||
|
|
eaeafb7299 | ||
|
|
44d736a537 | ||
|
|
122dbb3668 | ||
|
|
c2abbe2e2f | ||
|
|
3ccabd625c | ||
|
|
cfe6534c15 | ||
|
|
f5090eb158 | ||
|
|
d3db91536c | ||
|
|
9c42919e5a | ||
|
|
a6d33ec4af | ||
|
|
b03ae8bb17 | ||
|
|
59fd777198 | ||
|
|
d8286fa2a5 | ||
|
|
3345e70c63 | ||
|
|
12eea4a98d | ||
|
|
fdc20dc642 | ||
|
|
c5e3a1b2da | ||
|
|
35270fb0bf | ||
|
|
d782dad149 | ||
|
|
92aefb15f8 | ||
|
|
8342ac80d3 | ||
|
|
c71444e7a0 | ||
|
|
06e10bb69f | ||
|
|
fabd96552f | ||
|
|
2e99ae5ab9 | ||
|
|
8fea6ee8ab | ||
|
|
e6b10ee00c | ||
|
|
6aff6d66de | ||
|
|
8035991ac3 | ||
|
|
c0bd14cee7 | ||
|
|
3ba0beee96 | ||
|
|
8e6535f13c | ||
|
|
0d8f9520e9 | ||
|
|
3caaf0a3e8 | ||
|
|
a2206f1bcd | ||
|
|
7b5277d52c | ||
|
|
9f55a74d2e | ||
|
|
3eb8f68ded | ||
|
|
bd9bc118db | ||
|
|
95375a2591 | ||
|
|
b7115e9c70 | ||
|
|
078d9dc0b7 | ||
|
|
79c0c8e082 | ||
|
|
69b3263a00 | ||
|
|
cbd415881b | ||
|
|
6dbd81a6f9 | ||
|
|
e605a21fd3 |
233
.github/workflows/main.yml
vendored
233
.github/workflows/main.yml
vendored
@@ -8,10 +8,11 @@ on:
|
||||
|
||||
env:
|
||||
LLVM_RELEASE_VERSION_WINDOWS: 18
|
||||
LLVM_RELEASE_VERSION_MAC: 17
|
||||
LLVM_RELEASE_VERSION_LINUX: 17
|
||||
LLVM_RELEASE_VERSION_MAC: 18
|
||||
LLVM_RELEASE_VERSION_LINUX: 19
|
||||
LLVM_RELEASE_VERSION_OPENBSD: 19
|
||||
LLVM_RELEASE_VERSION_UBUNTU22: 17
|
||||
LLVM_RELEASE_VERSION_UBUNTU22: 19
|
||||
LLVM_RELEASE_VERSION_ALPINEv3_22: 20
|
||||
LLVM_DEV_VERSION: 22
|
||||
jobs:
|
||||
|
||||
@@ -78,26 +79,26 @@ jobs:
|
||||
|
||||
- name: Vendor-fetch
|
||||
run: |
|
||||
build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib55
|
||||
build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib
|
||||
|
||||
- name: Try raylib5
|
||||
run: |
|
||||
cd resources
|
||||
..\build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib55
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib55 --print-linking examples\raylib\raylib_arkanoid.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib55 --print-linking examples\raylib\raylib_snake.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib55 --print-linking examples\raylib\raylib_tetris.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib --print-linking examples\raylib\raylib_arkanoid.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib --print-linking examples\raylib\raylib_snake.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib --print-linking examples\raylib\raylib_tetris.c3
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
cd test
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile-run -O1 src/test_suite_runner.c3 -- ..\build\${{ matrix.build_type }}\c3c.exe test_suite/
|
||||
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
cd test
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile-test unit -O1 -D SLOW_TESTS
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
cd test
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile-run -O1 src/test_suite_runner.c3 -- ..\build\${{ matrix.build_type }}\c3c.exe test_suite/ --no-terminal
|
||||
|
||||
- name: Test python script
|
||||
run: |
|
||||
py msvc_build_libraries.py --accept-license
|
||||
@@ -159,7 +160,7 @@ jobs:
|
||||
|
||||
- name: Vendor-fetch
|
||||
run: |
|
||||
./build/c3c vendor-fetch raylib55
|
||||
./build/c3c vendor-fetch raylib
|
||||
|
||||
- name: Build testproject lib
|
||||
run: |
|
||||
@@ -236,7 +237,8 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install common deps
|
||||
run: |
|
||||
sudo apt-get install zlib1g zlib1g-dev python3 ninja-build curl
|
||||
sudo apt-get update
|
||||
sudo apt-get install zlib1g zlib1g-dev python3 ninja-build curl libcurl4-openssl-dev
|
||||
|
||||
- name: Install Clang ${{matrix.llvm_version}}
|
||||
run: |
|
||||
@@ -381,6 +383,10 @@ jobs:
|
||||
./build/c3c init myproject
|
||||
ls myproject
|
||||
|
||||
- name: Vendor-fetch
|
||||
run: |
|
||||
./build/c3c vendor-fetch raylib
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
cd test
|
||||
@@ -404,6 +410,187 @@ jobs:
|
||||
name: c3-linux-${{matrix.build_type}}
|
||||
path: c3-linux-${{matrix.build_type}}.tar.gz
|
||||
|
||||
build-linux-alpine:
|
||||
runs-on: ubuntu-22.04
|
||||
container:
|
||||
image: alpine:3.22
|
||||
strategy:
|
||||
# Don't abort runners if a single one fails
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [Release, Debug]
|
||||
llvm_version: [18, 19, 20]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install common deps
|
||||
run: |
|
||||
apk update
|
||||
apk add zlib-dev zlib-static python3 samurai cmake curl-dev curl-static openssl-dev
|
||||
|
||||
- name: Install Clang ${{matrix.llvm_version}}
|
||||
run: |
|
||||
apk add "llvm${{matrix.llvm_version}}-dev" "llvm${{matrix.llvm_version}}-static" \
|
||||
"llvm${{matrix.llvm_version}}-gtest" "llvm${{matrix.llvm_version}}-linker-tools" \
|
||||
"llvm${{matrix.llvm_version}}-test-utils" "llvm${{matrix.llvm_version}}-test-utils-pyc" \
|
||||
"lld${{matrix.llvm_version}}-dev" "clang${{matrix.llvm_version}}-dev" \
|
||||
"clang${{matrix.llvm_version}}-extra-tools" "clang${{matrix.llvm_version}}-static"
|
||||
- name: CMake
|
||||
if: matrix.llvm_version == env.LLVM_DEV_VERSION
|
||||
run: |
|
||||
cmake -B build \
|
||||
-G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=${{matrix.build_type}}" \
|
||||
-DCMAKE_C_COMPILER=clang-${{matrix.llvm_version}} \
|
||||
-DCMAKE_CXX_COMPILER=clang++-${{matrix.llvm_version}} \
|
||||
-DCMAKE_LINKER=lld-link-${{matrix.llvm_version}} \
|
||||
-DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \
|
||||
-DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \
|
||||
-DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \
|
||||
-DLLVM_ENABLE_LIBXML2=OFF \
|
||||
-DC3_LINK_DYNAMIC=ON \
|
||||
-DC3_LLVM_VERSION=${{matrix.llvm_version}}
|
||||
cmake --build build
|
||||
- name: CMake_Stable
|
||||
if: matrix.llvm_version >= 18 && matrix.llvm_version != env.LLVM_DEV_VERSION
|
||||
run: |
|
||||
cmake -B build \
|
||||
-G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
|
||||
-DCMAKE_C_COMPILER=clang-${{matrix.llvm_version}} \
|
||||
-DCMAKE_CXX_COMPILER=clang++-${{matrix.llvm_version}} \
|
||||
-DCMAKE_LINKER=lld-link-${{matrix.llvm_version}} \
|
||||
-DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \
|
||||
-DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \
|
||||
-DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \
|
||||
-DLLVM_ENABLE_LIBXML2=OFF \
|
||||
-DC3_LINK_DYNAMIC=ON \
|
||||
-DC3_LLVM_VERSION=${{matrix.llvm_version}}.1
|
||||
cmake --build build
|
||||
|
||||
- name: Compile and run some examples
|
||||
run: |
|
||||
export C3C="$(realpath ./build/c3c)"
|
||||
cd resources
|
||||
"$C3C" compile examples/base64.c3
|
||||
"$C3C" compile examples/binarydigits.c3
|
||||
"$C3C" compile examples/brainfk.c3
|
||||
"$C3C" compile examples/factorial_macro.c3
|
||||
"$C3C" compile examples/fasta.c3
|
||||
"$C3C" compile examples/gameoflife.c3
|
||||
"$C3C" compile examples/hash.c3
|
||||
"$C3C" compile-only examples/levenshtein.c3
|
||||
"$C3C" compile examples/load_world.c3
|
||||
"$C3C" compile-only examples/map.c3
|
||||
"$C3C" compile examples/mandelbrot.c3
|
||||
"$C3C" compile examples/plus_minus.c3
|
||||
"$C3C" compile examples/nbodies.c3
|
||||
"$C3C" compile examples/spectralnorm.c3
|
||||
"$C3C" compile examples/swap.c3
|
||||
"$C3C" compile examples/contextfree/boolerr.c3
|
||||
"$C3C" compile examples/contextfree/dynscope.c3
|
||||
"$C3C" compile examples/contextfree/guess_number.c3
|
||||
"$C3C" compile examples/contextfree/multi.c3
|
||||
"$C3C" compile examples/contextfree/cleanup.c3
|
||||
"$C3C" compile-run examples/hello_world_many.c3
|
||||
"$C3C" compile-run examples/time.c3
|
||||
"$C3C" compile-run examples/fannkuch-redux.c3
|
||||
"$C3C" compile-run examples/contextfree/boolerr.c3
|
||||
"$C3C" compile-run examples/load_world.c3
|
||||
"$C3C" compile-run examples/process.c3
|
||||
"$C3C" compile-run examples/ls.c3
|
||||
# "$C3C" compile-run --linker=builtin linux_stack.c3 # Program will hang due to incorrect linking to `/lib64/ld-linux-x86-64.so.2` instead of `/lib/ld-musl-x86_64.so.1`
|
||||
"$C3C" compile-run linux_stack.c3
|
||||
"$C3C" compile-run examples/args.c3 -- foo -bar "baz baz"
|
||||
|
||||
- name: Compile and run dynlib-test
|
||||
run: |
|
||||
export C3C="$(realpath ./build/c3c)"
|
||||
cd resources/examples/dynlib-test
|
||||
"$C3C" -vv dynamic-lib add.c3
|
||||
mv add.so libadd.so
|
||||
cc test.c -L. -ladd -Wl,-rpath=.
|
||||
./a.out
|
||||
"$C3C" compile-run test.c3 -L . -l add -z -Wl,-rpath=.
|
||||
|
||||
- name: Compile and run staticlib-test
|
||||
run: |
|
||||
export C3C="$(realpath ./build/c3c)"
|
||||
cd resources/examples/staticlib-test
|
||||
"$C3C" -vv static-lib add.c3
|
||||
mv add.a libadd.a
|
||||
cc test.c -L. -ladd
|
||||
./a.out
|
||||
"$C3C" compile-run test.c3 -L . -l add
|
||||
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
export C3C="$(realpath ./build/c3c)"
|
||||
cd test
|
||||
"$C3C" compile-test unit -D SLOW_TESTS
|
||||
|
||||
- name: Build testproject
|
||||
run: |
|
||||
export C3C="$(realpath ./build/c3c)"
|
||||
cd resources/testproject
|
||||
"$C3C" run -vvv --trust=full
|
||||
|
||||
- name: Test WASM
|
||||
run: |
|
||||
export C3C="$(realpath ./build/c3c)"
|
||||
cd resources/testfragments
|
||||
"$C3C" compile --target wasm32 -g0 --no-entry -Os wasm4.c3
|
||||
|
||||
- name: Install QEMU and Risc-V toolchain
|
||||
run: |
|
||||
apk add qemu-dev qemu-system-riscv32 gcc-riscv-none-elf make
|
||||
|
||||
- name: Compile and run Baremetal Risc-V Example
|
||||
run: |
|
||||
cd resources/examples/embedded/riscv-qemu
|
||||
make C3C_PATH=../../../../build/ run
|
||||
|
||||
- name: Build testproject direct linker
|
||||
run: |
|
||||
export C3C="$(realpath ./build/c3c)"
|
||||
cd resources/testproject
|
||||
"$C3C" run -vvv --linker=builtin --trust=full --linux-libc=musl
|
||||
|
||||
- name: Init a library & a project
|
||||
run: |
|
||||
./build/c3c init-lib mylib
|
||||
ls mylib.c3l
|
||||
./build/c3c init myproject
|
||||
ls myproject
|
||||
|
||||
- name: Vendor-fetch
|
||||
run: |
|
||||
"$(realpath ./build/c3c)" vendor-fetch raylib55
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
export C3C="$(realpath ./build/c3c)"
|
||||
cd test
|
||||
"$C3C" compile-run -O1 src/test_suite_runner.c3 -- ../build/c3c test_suite/
|
||||
|
||||
- name: bundle_output
|
||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_ALPINEv3_22
|
||||
run: |
|
||||
mkdir c3
|
||||
cp -r lib c3
|
||||
cp msvc_build_libraries.py c3
|
||||
cp build/c3c c3
|
||||
cp README.md c3
|
||||
cp releasenotes.md c3
|
||||
tar -czf c3-musl-${{matrix.build_type}}.tar.gz c3
|
||||
|
||||
- name: upload artifacts
|
||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_ALPINEv3_22
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: c3-musl-${{matrix.build_type}}
|
||||
path: c3-musl-${{matrix.build_type}}.tar.gz
|
||||
|
||||
build-linux-ubuntu22:
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
@@ -416,7 +603,8 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install common deps
|
||||
run: |
|
||||
sudo apt-get install zlib1g zlib1g-dev python3 ninja-build curl
|
||||
sudo apt-get update
|
||||
sudo apt-get install zlib1g zlib1g-dev python3 ninja-build curl libcurl4-openssl-dev
|
||||
|
||||
- name: Install Clang ${{matrix.llvm_version}}
|
||||
run: |
|
||||
@@ -460,6 +648,7 @@ jobs:
|
||||
-DLLVM_ENABLE_LIBXML2=OFF \
|
||||
-DC3_LLVM_VERSION=${{matrix.llvm_version}}.1
|
||||
cmake --build build
|
||||
|
||||
- name: Compile and run some examples
|
||||
run: |
|
||||
cd resources
|
||||
@@ -506,6 +695,10 @@ jobs:
|
||||
cd resources/testproject
|
||||
../../build/c3c run -vvv --linker=builtin --trust=full
|
||||
|
||||
- name: Vendor-fetch
|
||||
run: |
|
||||
./build/c3c vendor-fetch raylib
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
cd test
|
||||
@@ -647,7 +840,7 @@ jobs:
|
||||
|
||||
- name: Vendor-fetch
|
||||
run: |
|
||||
./build/c3c vendor-fetch raylib55
|
||||
./build/c3c vendor-fetch raylib
|
||||
|
||||
- name: Compile and run some examples
|
||||
run: |
|
||||
@@ -766,7 +959,7 @@ jobs:
|
||||
uses: vmactions/openbsd-vm@main
|
||||
with:
|
||||
prepare: |
|
||||
pkg_add cmake llvm-19.1.7p3 ninja
|
||||
pkg_add cmake llvm-20.1.8p1 ninja
|
||||
|
||||
run: |
|
||||
echo "CMake"
|
||||
@@ -893,7 +1086,7 @@ jobs:
|
||||
- run: mv c3-ubuntu-22-Debug/c3-ubuntu-22-Debug.tar.gz c3-ubuntu-22-Debug/c3-ubuntu-22-debug.tar.gz
|
||||
- run: mv c3-macos-Release/c3-macos-Release.zip c3-macos-Release/c3-macos.zip
|
||||
- run: mv c3-macos-Debug/c3-macos-Debug.zip c3-macos-Debug/c3-macos-debug.zip
|
||||
- run: gh release delete latest-prerelease --cleanup-tag -y || true
|
||||
- run: gh release delete latest-prerelease-tag --cleanup-tag -y || true
|
||||
- run: echo "RELEASE_NAME=latest-prerelease-$(date +'%Y%m%d-%H%M')" >> $GITHUB_ENV
|
||||
|
||||
- id: create_release
|
||||
@@ -901,7 +1094,7 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: latest-prerelease
|
||||
tag_name: latest-prerelease-tag
|
||||
name: ${{ env.RELEASE_NAME }}
|
||||
draft: false
|
||||
prerelease: true
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -92,3 +92,8 @@ result
|
||||
/test/tmp/*
|
||||
/test/testrun
|
||||
/test/test_suite_runner
|
||||
|
||||
# patches, originals and rejects
|
||||
*.patch
|
||||
*.rej
|
||||
*.orig
|
||||
|
||||
@@ -65,20 +65,21 @@ if(MSVC)
|
||||
else()
|
||||
add_compile_options(-gdwarf-3 -fno-exceptions)
|
||||
|
||||
# add_compile_options(-fsanitize=address,undefined)
|
||||
# add_link_options(-fsanitize=address,undefined)
|
||||
#add_compile_options(-fsanitize=address,undefined)
|
||||
#add_link_options(-fsanitize=address,undefined)
|
||||
endif()
|
||||
|
||||
# Options
|
||||
set(C3_LINK_DYNAMIC OFF CACHE BOOL "Link dynamically with LLVM/LLD libs")
|
||||
set(C3_WITH_LLVM ON CACHE BOOL "Build with LLVM")
|
||||
set(C3_LLVM_VERSION "auto" CACHE STRING "Use LLVM version [default: auto]")
|
||||
set(C3_USE_MIMALLOC OFF CACHE BOOL "Use built-in mimalloc")
|
||||
set(C3_MIMALLOC_TAG "v1.7.3" CACHE STRING "Used version of mimalloc")
|
||||
set(C3_USE_TB OFF CACHE BOOL "Use TB")
|
||||
set(C3_LLD_DIR "" CACHE STRING "Use custom LLD directory")
|
||||
set(C3_ENABLE_CLANGD_LSP OFF CACHE BOOL "Enable/Disable output of compile commands during generation")
|
||||
set(LLVM_CRT_LIBRARY_DIR "" CACHE STRING "Use custom llvm's compiler-rt directory")
|
||||
set(C3_LINK_DYNAMIC OFF CACHE BOOL "Link dynamically with LLVM/LLD libs")
|
||||
set(C3_WITH_LLVM ON CACHE BOOL "Build with LLVM")
|
||||
set(C3_LLVM_VERSION "auto" CACHE STRING "Use LLVM version [default: auto]")
|
||||
set(C3_USE_MIMALLOC OFF CACHE BOOL "Use built-in mimalloc")
|
||||
set(C3_MIMALLOC_TAG "v1.7.3" CACHE STRING "Used version of mimalloc")
|
||||
set(C3_USE_TB OFF CACHE BOOL "Use TB")
|
||||
set(C3_LLD_DIR "" CACHE STRING "Use custom LLD directory")
|
||||
set(C3_ENABLE_CLANGD_LSP OFF CACHE BOOL "Enable/Disable output of compile commands during generation")
|
||||
set(LLVM_CRT_LIBRARY_DIR "" CACHE STRING "Use custom llvm's compiler-rt directory")
|
||||
set(TCC_LIB_PATH "/usr/lib/tcc/libtcc1.a" CACHE STRING "Use custom libtcc1.a path")
|
||||
|
||||
set(C3_OPTIONS
|
||||
C3_LINK_DYNAMIC
|
||||
@@ -575,6 +576,17 @@ else()
|
||||
target_link_options(c3c PRIVATE -pthread)
|
||||
endif()
|
||||
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "TinyCC")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--no-eh-frame-hdr -z noexecstack")
|
||||
|
||||
# Link the static tcc runtime archive if it exists
|
||||
if(EXISTS "${TCC_LIB_PATH}")
|
||||
target_link_libraries(c3c "${TCC_LIB_PATH}")
|
||||
else()
|
||||
message(FATAL_ERROR "TCC runtime not found at ${TCC_LIB_PATH}; Ensure the path is correct.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
install(TARGETS c3c DESTINATION bin)
|
||||
install(DIRECTORY lib/ DESTINATION lib/c3)
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ further defined and clarified by project maintainers.
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at . All
|
||||
reported by contacting the project team at info@c3-lang.org. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
|
||||
91
README.md
91
README.md
@@ -8,11 +8,11 @@ for programmers who like C.
|
||||
|
||||
Precompiled binaries for the following operating systems are available:
|
||||
|
||||
- Windows x64 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-windows.zip), [install instructions](#installing-on-windows-with-precompiled-binaries).
|
||||
- Debian x64 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-linux.tar.gz), [install instructions](#installing-on-debian-with-precompiled-binaries).
|
||||
- Ubuntu x86 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-ubuntu-20.tar.gz), [install instructions](#installing-on-ubuntu-with-precompiled-binaries).
|
||||
- MacOS Arm64 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-macos.zip), [install instructions](#installing-on-macos-with-precompiled-binaries).
|
||||
- OpenBSD x64 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-openbsd.tar.gz), [install instructions](#installing-on-openbsd-with-precompiled-binaries).
|
||||
- Windows x64 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-windows.zip), [install instructions](#installing-on-windows-with-precompiled-binaries).
|
||||
- Debian x64 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-linux.tar.gz), [install instructions](#installing-on-debian-with-precompiled-binaries).
|
||||
- Ubuntu x86 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-ubuntu-20.tar.gz), [install instructions](#installing-on-ubuntu-with-precompiled-binaries).
|
||||
- MacOS Arm64 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-macos.zip), [install instructions](#installing-on-macos-with-precompiled-binaries).
|
||||
- OpenBSD x64 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-openbsd.tar.gz), [install instructions](#installing-on-openbsd-with-precompiled-binaries).
|
||||
|
||||
The manual for C3 can be found at [www.c3-lang.org](http://www.c3-lang.org).
|
||||
|
||||
@@ -142,7 +142,7 @@ fn void main()
|
||||
|
||||
### Current status
|
||||
|
||||
The current stable version of the compiler is **version 0.7.5**.
|
||||
The current stable version of the compiler is **version 0.7.8**.
|
||||
|
||||
The upcoming 0.7.x releases will focus on expanding the standard library,
|
||||
fixing bugs and improving compile time analysis.
|
||||
@@ -151,7 +151,7 @@ Follow the issues [here](https://github.com/c3lang/c3c/issues).
|
||||
If you have suggestions on how to improve the language, either [file an issue](https://github.com/c3lang/c3c/issues)
|
||||
or discuss C3 on its dedicated Discord: [https://discord.gg/qN76R87](https://discord.gg/qN76R87).
|
||||
|
||||
The compiler is currently verified to compile on Linux, Windows and MacOS.
|
||||
The compiler is currently verified to compile on Linux, OpenBSD, Windows and MacOS.
|
||||
|
||||
**Support matrix**
|
||||
|
||||
@@ -172,6 +172,7 @@ The compiler is currently verified to compile on Linux, Windows and MacOS.
|
||||
| ELF freestanding Aarch64 | No | Untested | No | No | No | Yes* |
|
||||
| ELF freestanding Riscv64 | No | Untested | No | No | No | Untested |
|
||||
| ELF freestanding Riscv32 | No | Untested | No | No | No | Untested |
|
||||
| ELF freestanding Xtensa* | No | Untested | No | No | No | Untested |
|
||||
| FreeBSD x86 | Untested | Untested | No | Yes | Untested | Yes* |
|
||||
| FreeBSD x64 | Untested | Untested | No | Yes | Untested | Yes* |
|
||||
| NetBSD x86 | Untested | Untested | No | Yes | Untested | Yes* |
|
||||
@@ -184,7 +185,8 @@ The compiler is currently verified to compile on Linux, Windows and MacOS.
|
||||
|
||||
*\* Inline asm is still a work in progress*<br>
|
||||
*\* OpenBSD 7.7 is the only tested version*<br>
|
||||
*\* OpenBSD has limited stacktrace, needs to be tested further*
|
||||
*\* OpenBSD has limited stacktrace, needs to be tested further*<br>
|
||||
*\* Xtensa support is enabled by compiling with `-DXTENSA_ENABLE`. The [espressif llvm fork](https://github.com/espressif/llvm-project) is recommended for best compatibility*
|
||||
|
||||
More platforms will be supported in the future.
|
||||
|
||||
@@ -203,36 +205,67 @@ More platforms will be supported in the future.
|
||||
This installs the latest prerelease build, as opposed to the latest released version.
|
||||
|
||||
#### Installing on Windows with precompiled binaries
|
||||
1. Download the zip file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-windows.zip](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-windows.zip)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-windows-debug.zip))
|
||||
1. Download the zip file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-windows.zip](https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-windows.zip)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-windows-debug.zip))
|
||||
2. Unzip exe and standard lib.
|
||||
3. If you don't have Visual Studio 17 installed you can either do so, or run the `msvc_build_libraries.py` Python script which will download the necessary files to compile on Windows.
|
||||
4. Run `c3c.exe`.
|
||||
|
||||
#### Installing on Windows with the install script
|
||||
|
||||
Open a PowerShell terminal (you may need to run it as an administrator) and run the following command:
|
||||
```bash
|
||||
iwr -useb https://raw.githubusercontent.com/c3lang/c3c/refs/heads/master/install/install.ps1 | iex
|
||||
```
|
||||
The script will inform you once the installation is successful and add the `~/.c3` directory to your PATH, which will allow you to run the c3c command from any location.
|
||||
|
||||
You can choose another version with option `C3_VERSION`.
|
||||
For example, you can force the installation of the 0.7.4 version:
|
||||
```bash
|
||||
$env:C3_VERSION='0.7.4'; powershell -ExecutionPolicy Bypass -Command "iwr -useb https://raw.githubusercontent.com/c3lang/c3c/refs/heads/master/install/install.ps1 | iex"
|
||||
```
|
||||
|
||||
If you don't have Visual Studio 17 installed you can either do so, or run the `msvc_build_libraries.py` Python script which will download the necessary files to compile on Windows.
|
||||
|
||||
|
||||
#### Installing on Debian with precompiled binaries
|
||||
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-linux.tar.gz](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-linux.tar.gz)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-linux-debug.tar.gz))
|
||||
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-linux.tar.gz](https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-linux.tar.gz)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-linux-debug.tar.gz))
|
||||
2. Unpack executable and standard lib.
|
||||
3. Run `./c3c`.
|
||||
|
||||
#### Installing on Debian with the install script
|
||||
|
||||
Open a terminal and run the following command:
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/c3lang/c3c/refs/heads/master/install/install.sh | bash
|
||||
```
|
||||
The C3 compiler will be installed, and the script will also update your ~/.bashrc to include `~/.c3` in your PATH, allowing you to invoke the c3c command from anywhere. You might need to restart your terminal or source your shell for the changes to take effect.
|
||||
|
||||
You can choose another version with option `C3_VERSION`.
|
||||
For example, you can force the installation of the 0.7.4 version:
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/c3lang/c3c/refs/heads/master/install/install.sh | C3_VERSION=0.7.4 bash
|
||||
```
|
||||
|
||||
#### Installing on Ubuntu with precompiled binaries
|
||||
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-ubuntu-20.tar.gz](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-ubuntu-20.tar.gz)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-ubuntu-20-debug.tar.gz))
|
||||
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-ubuntu-20.tar.gz](https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-ubuntu-20.tar.gz)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-ubuntu-20-debug.tar.gz))
|
||||
2. Unpack executable and standard lib.
|
||||
3. Run `./c3c`.
|
||||
|
||||
#### Installing on MacOS with precompiled binaries
|
||||
1. Make sure you have XCode with command line tools installed.
|
||||
2. Download the zip file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-macos.zip](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-macos.zip)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-macos-debug.zip))
|
||||
2. Download the zip file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-macos.zip](https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-macos.zip)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-macos-debug.zip))
|
||||
3. Unzip executable and standard lib.
|
||||
4. Run `./c3c`.
|
||||
|
||||
(*Note that there is a known issue with debug symbol generation on MacOS 13, see [issue #1086](https://github.com/c3lang/c3c/issues/1086))
|
||||
|
||||
#### Installing on OpenBSD with precompiled binaries
|
||||
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-openbsd.tar.gz](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-openbsd.tar.gz)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-openbsd-debug.tar.gz))
|
||||
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-openbsd.tar.gz](https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-openbsd.tar.gz)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-openbsd-debug.tar.gz))
|
||||
2. Unpack executable and standard lib.
|
||||
3. Run `./c3c`.
|
||||
|
||||
@@ -296,6 +329,28 @@ You can access `c3c` via [flake.nix](./flake.nix), which will contain the latest
|
||||
}
|
||||
```
|
||||
|
||||
### Installing on Gentoo
|
||||
|
||||
`c3c` is available in the [Gentoo GURU overlay](https://wiki.gentoo.org/wiki/Project:GURU).
|
||||
|
||||
Enable and sync the GURU repository (if not already done):
|
||||
|
||||
```sh
|
||||
sudo eselect repository enable guru
|
||||
sudo emaint sync -r guru
|
||||
```
|
||||
|
||||
Install `c3c` with:
|
||||
|
||||
```sh
|
||||
sudo emerge -av dev-lang/c3c
|
||||
```
|
||||
|
||||
* The compiler binary is installed to `/usr/bin/c3c`.
|
||||
* The standard library is installed to `/usr/lib/c3`.
|
||||
|
||||
For Gentoo-specific issues, please use the [Gentoo Bugzilla](https://bugs.gentoo.org/) (Product: *GURU*).
|
||||
|
||||
#### Building via Docker
|
||||
|
||||
You can build `c3c` using an Ubuntu container. By default, the script will build through Ubuntu 22.04. You can specify the version by passing the `UBUNTU_VERSION` environment variable.
|
||||
|
||||
@@ -107,7 +107,8 @@ fn void hash_speeds_of_many_random_values() => @pool()
|
||||
foreach (&v : vwideints) *v = (uint128)random::next(&rand, uint.max);
|
||||
|
||||
char[48][] zstrs = allocator::new_array(tmem, char[48], $arrsz)[:$arrsz];
|
||||
String[$arrsz] strs;
|
||||
|
||||
String[] strs = mem::temp_array(String, $arrsz);
|
||||
foreach (x, &v : zstrs)
|
||||
{
|
||||
foreach (&c : (*v)[:random::next(&rand, 48)]) *c = (char)random::next(&rand, char.max);
|
||||
@@ -195,7 +196,7 @@ fn void random_access_string_keys() => @pool()
|
||||
v.tinit();
|
||||
|
||||
usz pseudo_checksum = 0;
|
||||
String[5_000] saved;
|
||||
String[] saved = mem::temp_array(String, 5_000);
|
||||
|
||||
for (usz i = 0; i < saved.len; ++i)
|
||||
{
|
||||
|
||||
38
benchmarks/stdlib/collections/linkedlist.c3
Normal file
38
benchmarks/stdlib/collections/linkedlist.c3
Normal file
@@ -0,0 +1,38 @@
|
||||
module linkedlist_benchmarks;
|
||||
|
||||
import std::collections::linkedlist;
|
||||
|
||||
|
||||
LinkedList{int} long_list;
|
||||
const HAY = 2;
|
||||
const NEEDLE = 1000;
|
||||
|
||||
fn void bench_setup() @init
|
||||
{
|
||||
set_benchmark_warmup_iterations(3);
|
||||
set_benchmark_max_iterations(4096);
|
||||
|
||||
int[*] haystack = { [0..999] = HAY };
|
||||
long_list = linkedlist::@new{int}(mem, haystack[..]);
|
||||
long_list.push(NEEDLE);
|
||||
long_list.push_all(haystack[..]);
|
||||
}
|
||||
|
||||
|
||||
// ==============================================================================================
|
||||
module linkedlist_benchmarks @benchmark;
|
||||
|
||||
String die_str = "Failed to find the value `1`. Is something broken?";
|
||||
|
||||
|
||||
fn void foreach_iterator()
|
||||
{
|
||||
foreach (v : long_list.array_view()) if (v == NEEDLE) return;
|
||||
runtime::@kill_benchmark(die_str);
|
||||
}
|
||||
|
||||
fn void foreach_r_iterator()
|
||||
{
|
||||
foreach_r (v : long_list.array_view()) if (v == NEEDLE) return;
|
||||
runtime::@kill_benchmark(die_str);
|
||||
}
|
||||
@@ -19,7 +19,7 @@ macro void trim_bench($trim_str, String $target = WHITESPACE_TARGET) => @pool()
|
||||
$switch:
|
||||
$case $typeof($trim_str) == String:
|
||||
s1 = s2.trim($trim_str);
|
||||
$case $typeof($$trim_str) == AsciiCharset:
|
||||
$case $typeof($trim_str) == AsciiCharset:
|
||||
s1 = s2.trim_charset($trim_str);
|
||||
$default: $error "Unable to determine the right String `trim` operation to use.";
|
||||
$endswitch
|
||||
|
||||
31
benchmarks/stdlib/crypto/aes_bench.c3
Normal file
31
benchmarks/stdlib/crypto/aes_bench.c3
Normal file
@@ -0,0 +1,31 @@
|
||||
module std::crypto::aes_bench;
|
||||
|
||||
import std::crypto::aes;
|
||||
|
||||
fn void init() @init
|
||||
{
|
||||
set_benchmark_warmup_iterations(5);
|
||||
set_benchmark_max_iterations(10_000);
|
||||
}
|
||||
|
||||
|
||||
AesType aes = AES256;
|
||||
char[] key = x"603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4";
|
||||
char[] text = x"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710";
|
||||
char[] cipher = x"601ec313775789a5b7a7f504bbf3d228f443e3ca4d62b59aca84e990cacaf5c52b0930daa23de94ce87017ba2d84988ddfc9c58db67aada613c2dd08457941a6";
|
||||
char[16] iv = x"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
|
||||
|
||||
fn void bench_ctr_xcrypt() @benchmark
|
||||
{
|
||||
char[64] out;
|
||||
Aes ctx;
|
||||
|
||||
// encrypt
|
||||
ctx.init(aes, key, iv);
|
||||
ctx.encrypt_buffer(text, &out);
|
||||
|
||||
// decrypt
|
||||
ctx.init(aes, key, iv);
|
||||
ctx.decrypt_buffer(cipher, &out);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
: ${DOCKER:=docker}
|
||||
: ${IMAGE:="c3c-builder"}
|
||||
@@ -41,4 +41,4 @@ exec $DOCKER run -i --rm \
|
||||
-DCMAKE_DLLTOOL=llvm-dlltool-$LLVM_VERSION \
|
||||
-DC3_LLVM_VERSION=auto && \
|
||||
cmake --build build && \
|
||||
cp -r build/c3c build/lib bin"
|
||||
cp -r build/c3c build/lib bin"
|
||||
|
||||
@@ -6,7 +6,7 @@ ENV LLVM_DEV_VERSION=20
|
||||
|
||||
ARG CMAKE_VERSION=3.20
|
||||
|
||||
RUN apt-get update && apt-get install -y wget gnupg software-properties-common zlib1g zlib1g-dev python3 ninja-build curl g++ && \
|
||||
RUN apt-get update && apt-get install -y wget gnupg software-properties-common zlib1g zlib1g-dev python3 ninja-build curl g++ libcurl4-openssl-dev && \
|
||||
wget https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-$CMAKE_VERSION-linux-x86_64.sh && \
|
||||
mkdir -p /opt/cmake && \
|
||||
sh cmake-${CMAKE_VERSION}-linux-x86_64.sh --prefix=/opt/cmake --skip-license && \
|
||||
@@ -46,4 +46,4 @@ RUN groupadd -g 1337 c3c && \
|
||||
USER c3c
|
||||
ENV PATH="/opt/cmake/bin:${PATH}"
|
||||
|
||||
WORKDIR /home/c3c
|
||||
WORKDIR /home/c3c
|
||||
|
||||
189
install/install.ps1
Normal file
189
install/install.ps1
Normal file
@@ -0,0 +1,189 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
C3 install script.
|
||||
.DESCRIPTION
|
||||
This script installs C3 on Windows from the command line.
|
||||
.PARAMETER C3Version
|
||||
Specifies the version of C3 to install.
|
||||
Default is 'latest'. Can also be set via environment variable 'C3_VERSION'.
|
||||
.PARAMETER C3Home
|
||||
Specifies C3's installation directory.
|
||||
Default is '$Env:USERPROFILE\.c3'. Can also be set via environment variable 'C3_HOME'.
|
||||
.PARAMETER NoPathUpdate
|
||||
If specified, the script will not modify the PATH environment variable.
|
||||
.PARAMETER C3Repourl
|
||||
Specifies the repository URL of C3.
|
||||
Default is 'https://github.com/c3lang/c3c'. Can also be set via environment variable 'C3_REPOURL'.
|
||||
.LINK
|
||||
https://c3-lang.org/
|
||||
.LINK
|
||||
https://github.com/c3lang/c3c
|
||||
#>
|
||||
|
||||
# Script parameters with defaults
|
||||
param (
|
||||
[string] $C3Version = 'latest',
|
||||
[string] $C3Home = "$Env:USERPROFILE\.c3",
|
||||
[switch] $NoPathUpdate,
|
||||
[string] $C3Repourl = 'https://github.com/c3lang/c3c'
|
||||
)
|
||||
|
||||
# Enable strict mode for better error handling
|
||||
Set-StrictMode -Version Latest
|
||||
|
||||
# Function to broadcast environment variable changes to Windows system
|
||||
function Publish-Env {
|
||||
# Add P/Invoke type if it does not exist
|
||||
if (-not ("Win32.NativeMethods" -as [Type])) {
|
||||
Add-Type -Namespace Win32 -Name NativeMethods -MemberDefinition @"
|
||||
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
||||
public static extern IntPtr SendMessageTimeout(
|
||||
IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam,
|
||||
uint fuFlags, uint uTimeout, out UIntPtr lpdwResult);
|
||||
"@
|
||||
}
|
||||
|
||||
# Constants for broadcasting environment changes
|
||||
$HWND_BROADCAST = [IntPtr] 0xffff
|
||||
$WM_SETTINGCHANGE = 0x1a
|
||||
$result = [UIntPtr]::Zero
|
||||
|
||||
# Broadcast the message to all windows
|
||||
[Win32.Nativemethods]::SendMessageTimeout($HWND_BROADCAST,
|
||||
$WM_SETTINGCHANGE,
|
||||
[UIntPtr]::Zero,
|
||||
"Environment",
|
||||
2,
|
||||
5000,
|
||||
[ref] $result
|
||||
) | Out-Null
|
||||
}
|
||||
|
||||
# Function to write or update an environment variable in the registry
|
||||
function Write-Env {
|
||||
param(
|
||||
[String] $name,
|
||||
[String] $val,
|
||||
[Switch] $global
|
||||
)
|
||||
|
||||
# Determine the registry key based on scope (user or system)
|
||||
$RegisterKey = if ($global) {
|
||||
Get-Item -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager'
|
||||
} else {
|
||||
Get-Item -Path 'HKCU:'
|
||||
}
|
||||
|
||||
$EnvRegisterKey = $RegisterKey.OpenSubKey('Environment', $true)
|
||||
|
||||
# If value is null, delete the variable
|
||||
if ($null -eq $val) {
|
||||
$EnvRegisterKey.DeleteValue($name)
|
||||
} else {
|
||||
# Determine the correct registry value type
|
||||
$RegistryValueKind = if ($val.Contains('%')) {
|
||||
[Microsoft.Win32.RegistryValueKind]::ExpandString
|
||||
} elseif ($EnvRegisterKey.GetValue($name)) {
|
||||
$EnvRegisterKey.GetValueKind($name)
|
||||
} else {
|
||||
[Microsoft.Win32.RegistryValueKind]::String
|
||||
}
|
||||
$EnvRegisterKey.SetValue($name, $val, $RegistryValueKind)
|
||||
}
|
||||
|
||||
# Broadcast the change to the system
|
||||
Publish-Env
|
||||
}
|
||||
|
||||
# Function to get an environment variable from the registry
|
||||
function Get-Env {
|
||||
param(
|
||||
[String] $name,
|
||||
[Switch] $global
|
||||
)
|
||||
|
||||
# Determine registry key based on scope
|
||||
$RegisterKey = if ($global) {
|
||||
Get-Item -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager'
|
||||
} else {
|
||||
Get-Item -Path 'HKCU:'
|
||||
}
|
||||
|
||||
$EnvRegisterKey = $RegisterKey.OpenSubKey('Environment')
|
||||
$RegistryValueOption = [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames
|
||||
|
||||
# Retrieve the value without expanding environment variables
|
||||
$EnvRegisterKey.GetValue($name, $null, $RegistryValueOption)
|
||||
}
|
||||
|
||||
# Override defaults if environment variables exist
|
||||
if ($Env:C3_VERSION) { $C3Version = $Env:C3_VERSION }
|
||||
if ($Env:C3_HOME) { $C3Home = $Env:C3_HOME }
|
||||
if ($Env:C3_NO_PATH_UPDATE) { $NoPathUpdate = $true }
|
||||
if ($Env:C3_REPOURL) { $C3Repourl = $Env:C3_REPOURL -replace '/$', '' }
|
||||
|
||||
# Set binary name
|
||||
$BINARY = "c3-windows"
|
||||
|
||||
# Determine the download URL based on version
|
||||
if ($C3Version -eq 'latest') {
|
||||
$DOWNLOAD_URL = "$C3Repourl/releases/latest/download/$BINARY.zip"
|
||||
} else {
|
||||
# Ensure version starts with 'v'
|
||||
$C3Version = "v" + ($C3Version -replace '^v', '')
|
||||
$DOWNLOAD_URL = "$C3Repourl/releases/download/$C3Version/$BINARY.zip"
|
||||
}
|
||||
|
||||
$BinDir = $C3Home
|
||||
|
||||
Write-Host "This script will automatically download and install C3 ($C3Version) for you."
|
||||
Write-Host "Getting it from this url: $DOWNLOAD_URL"
|
||||
Write-Host "The binary will be installed into '$BinDir'"
|
||||
|
||||
# Create temporary file for download
|
||||
$TEMP_FILE = [System.IO.Path]::GetTempFileName()
|
||||
|
||||
try {
|
||||
# Download the binary
|
||||
Invoke-WebRequest -Uri $DOWNLOAD_URL -OutFile $TEMP_FILE
|
||||
|
||||
# Remove previous installation if it exists
|
||||
if (Test-Path -Path $BinDir) {
|
||||
Remove-Item -Path $BinDir -Recurse -Force | Out-Null
|
||||
}
|
||||
|
||||
# Rename temp file to .zip
|
||||
$ZIP_FILE = $TEMP_FILE + ".zip"
|
||||
Rename-Item -Path $TEMP_FILE -NewName $ZIP_FILE
|
||||
|
||||
# Extract downloaded zip
|
||||
Expand-Archive -Path $ZIP_FILE -DestinationPath $Env:USERPROFILE -Force
|
||||
|
||||
# Rename extracted folder to target installation directory
|
||||
Rename-Item -Path "$Env:USERPROFILE/c3-windows-Release" -NewName $BinDir
|
||||
} catch {
|
||||
Write-Host "Error: '$DOWNLOAD_URL' is not available or failed to download"
|
||||
exit 1
|
||||
} finally {
|
||||
# Cleanup temporary zip file
|
||||
Remove-Item -Path $ZIP_FILE
|
||||
}
|
||||
|
||||
# Update PATH environment variable if requested
|
||||
if (!$NoPathUpdate) {
|
||||
$PATH = Get-Env 'PATH'
|
||||
if ($PATH -notlike "*$BinDir*") {
|
||||
Write-Output "Adding $BinDir to PATH"
|
||||
|
||||
# Persist PATH for future sessions
|
||||
Write-Env -name 'PATH' -val "$BinDir;$PATH"
|
||||
|
||||
# Update PATH for current session
|
||||
$Env:PATH = "$BinDir;$PATH"
|
||||
Write-Output "You may need to restart your shell"
|
||||
} else {
|
||||
Write-Output "$BinDir is already in PATH"
|
||||
}
|
||||
} else {
|
||||
Write-Output "You may need to update your PATH manually to use c3"
|
||||
}
|
||||
137
install/install.sh
Normal file
137
install/install.sh
Normal file
@@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail # Exit on error, unset variables, and fail pipelines on any error
|
||||
|
||||
__wrap__() {
|
||||
# Version of C3 to install (default: latest)
|
||||
VERSION="${C3_VERSION:-latest}"
|
||||
# Installation directory (default: ~/.c3)
|
||||
C3_HOME="${C3_HOME:-$HOME/.c3}"
|
||||
# Expand '~' if present
|
||||
C3_HOME="${C3_HOME/#\~/$HOME}"
|
||||
BIN_DIR="$C3_HOME"
|
||||
# C3 compiler repository URL
|
||||
REPO="c3lang/c3c"
|
||||
REPOURL="${C3_REPOURL:-https://github.com/$REPO}"
|
||||
|
||||
detect_platform() {
|
||||
# Detects the operating system
|
||||
local os_type
|
||||
os_type="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
||||
|
||||
case "$os_type" in
|
||||
darwin) # macOS
|
||||
echo "macos"
|
||||
;;
|
||||
msys*|mingw*|cygwin*) # Windows (Git Bash / MSYS / Cygwin)
|
||||
IS_MSYS=true
|
||||
echo "windows"
|
||||
;;
|
||||
*)
|
||||
echo $os_type
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Determine platform string
|
||||
PLATFORM="$(detect_platform)"
|
||||
|
||||
# File extension for the archive (ZIP for Windows, TAR.GZ for others)
|
||||
EXT=".tar.gz"
|
||||
BINARY="c3-${PLATFORM}"
|
||||
if [[ "${IS_MSYS:-false}" == true ]]; then
|
||||
EXT=".zip"
|
||||
fi
|
||||
|
||||
# Determine the download URL (latest release or specific version)
|
||||
if [[ "$VERSION" == "latest" ]]; then
|
||||
URL="${REPOURL%/}/releases/latest/download/${BINARY}${EXT}"
|
||||
else
|
||||
URL="${REPOURL%/}/releases/download/v${VERSION#v}/${BINARY}${EXT}"
|
||||
fi
|
||||
|
||||
# Temporary file for the downloaded archive
|
||||
TEMP_FILE="$(mktemp "${TMPDIR:-/tmp}/.C3_install.XXXXXXXX")"
|
||||
trap 'rm -f "$TEMP_FILE"' EXIT # Ensure temp file is deleted on exit
|
||||
|
||||
download_file() {
|
||||
# Download the archive using curl or wget
|
||||
# Check that the curl version is not 8.8.0, which is broken for --write-out
|
||||
# https://github.com/curl/curl/issues/13845
|
||||
if command -v curl >/dev/null && [[ "$(curl --version | awk 'NR==1{print $2}')" != "8.8.0" ]]; then
|
||||
curl -SL "$URL" -o "$TEMP_FILE"
|
||||
elif command -v wget >/dev/null; then
|
||||
wget -O "$TEMP_FILE" "$URL"
|
||||
else
|
||||
echo "Error: curl or wget is required." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
echo "Downloading C3 ($VERSION) from $URL..."
|
||||
download_file
|
||||
|
||||
# Remove existing installation and extract the new one
|
||||
rm -rf "$BIN_DIR"
|
||||
if [[ "$EXT" == ".zip" ]]; then
|
||||
unzip "$TEMP_FILE" -d "$HOME"
|
||||
else
|
||||
tar -xzf "$TEMP_FILE" -C "$HOME"
|
||||
fi
|
||||
|
||||
# Move extracted folder to installation directory
|
||||
mv "$HOME/c3" "$BIN_DIR"
|
||||
chmod +x "$BIN_DIR/c3c" # Ensure compiler binary is executable
|
||||
echo "✅ Installation completed in $BIN_DIR"
|
||||
|
||||
# Update PATH unless suppressed by environment variable
|
||||
if [ -n "${C3_NO_PATH_UPDATE:-}" ]; then
|
||||
echo "No path update because C3_NO_PATH_UPDATE is set"
|
||||
else
|
||||
update_shell() {
|
||||
FILE="$1"
|
||||
LINE="$2"
|
||||
|
||||
# Create shell config file if missing
|
||||
if [ ! -f "$FILE" ]; then
|
||||
touch "$FILE"
|
||||
fi
|
||||
|
||||
# Add the PATH line if not already present
|
||||
if ! grep -Fxq "$LINE" "$FILE"; then
|
||||
echo "Updating '${FILE}'"
|
||||
echo "$LINE" >>"$FILE"
|
||||
echo "Please restart or source your shell."
|
||||
fi
|
||||
}
|
||||
|
||||
# Detect the current shell and add C3 to its PATH
|
||||
case "$(basename "${SHELL-}")" in
|
||||
bash)
|
||||
# Default to bashrc as that is used in non login shells instead of the profile.
|
||||
LINE="export PATH=\"${BIN_DIR}:\$PATH\""
|
||||
update_shell ~/.bashrc "$LINE"
|
||||
;;
|
||||
fish)
|
||||
LINE="fish_add_path ${BIN_DIR}"
|
||||
update_shell ~/.config/fish/config.fish "$LINE"
|
||||
;;
|
||||
zsh)
|
||||
LINE="export PATH=\"${BIN_DIR}:\$PATH\""
|
||||
update_shell ~/.zshrc "$LINE"
|
||||
;;
|
||||
tcsh)
|
||||
LINE="set path = ( ${BIN_DIR} \$path )"
|
||||
update_shell ~/.tcshrc "$LINE"
|
||||
;;
|
||||
'')
|
||||
echo "warn: Could not detect shell type." >&2
|
||||
echo " Please permanently add '${BIN_DIR}' to your \$PATH to enable the 'c3c' command." >&2
|
||||
;;
|
||||
*)
|
||||
echo "warn: Could not update shell $(basename "$SHELL")" >&2
|
||||
echo " Please permanently add '${BIN_DIR}' to your \$PATH to enable the 'c3c' command." >&2
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
__wrap__
|
||||
@@ -58,7 +58,7 @@ fn CInt __atomic_compare_exchange(CInt size, any ptr, any expected, any desired,
|
||||
nextcase;
|
||||
$endif
|
||||
default:
|
||||
unreachable("Unsuported size (%d) for atomic_compare_exchange", size);
|
||||
unreachable("Unsupported size (%d) for atomic_compare_exchange", size);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
// Use of self source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::collections::anylist;
|
||||
import std::io,std::math;
|
||||
import std::collections::interfacelist;
|
||||
|
||||
alias AnyPredicate = fn bool(any value);
|
||||
alias AnyTest = fn bool(any type, any context);
|
||||
alias AnyPredicate = InterfacePredicate {any};
|
||||
alias AnyTest = InterfaceTest {any};
|
||||
|
||||
<*
|
||||
The AnyList contains a heterogenous set of types. Anything placed in the
|
||||
@@ -18,282 +18,7 @@ alias AnyTest = fn bool(any type, any context);
|
||||
If we're not doing pop, then things are easier, since we can just hand over
|
||||
the existing any.
|
||||
*>
|
||||
struct AnyList (Printable)
|
||||
{
|
||||
usz size;
|
||||
usz capacity;
|
||||
Allocator allocator;
|
||||
any* entries;
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Initialize the list. If not initialized then it will use the temp allocator
|
||||
when something is pushed to it.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use"
|
||||
@param initial_capacity : "The initial capacity to reserve, defaults to 16"
|
||||
*>
|
||||
fn AnyList* AnyList.init(&self, Allocator allocator, usz initial_capacity = 16)
|
||||
{
|
||||
self.allocator = allocator;
|
||||
self.size = 0;
|
||||
if (initial_capacity > 0)
|
||||
{
|
||||
initial_capacity = math::next_power_of_2(initial_capacity);
|
||||
self.entries = allocator::alloc_array(allocator, any, initial_capacity);
|
||||
}
|
||||
else
|
||||
{
|
||||
self.entries = null;
|
||||
}
|
||||
self.capacity = initial_capacity;
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
Initialize the list using the temp allocator.
|
||||
|
||||
@param initial_capacity : "The initial capacity to reserve"
|
||||
*>
|
||||
fn AnyList* AnyList.tinit(&self, usz initial_capacity = 16)
|
||||
{
|
||||
return self.init(tmem, initial_capacity) @inline;
|
||||
}
|
||||
|
||||
fn bool AnyList.is_initialized(&self) @inline => self.allocator != null;
|
||||
|
||||
<*
|
||||
Push an element on the list by cloning it.
|
||||
*>
|
||||
macro void AnyList.push(&self, element)
|
||||
{
|
||||
if (!self.allocator) self.allocator = tmem;
|
||||
self._append(allocator::clone(self.allocator, element));
|
||||
}
|
||||
|
||||
<*
|
||||
Free a retained element removed using *_retained.
|
||||
*>
|
||||
fn void AnyList.free_element(&self, any element) @inline
|
||||
{
|
||||
allocator::free(self.allocator, element.ptr);
|
||||
}
|
||||
|
||||
<*
|
||||
Pop a value who's type is known. If the type is incorrect, this
|
||||
will still pop the element.
|
||||
|
||||
@param $Type : "The type we assume the value has"
|
||||
@return "The last value as the type given"
|
||||
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
|
||||
*>
|
||||
macro AnyList.pop(&self, $Type)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
defer self.free_element(self.entries[self.size]);
|
||||
return *anycast(self.entries[--self.size], $Type);
|
||||
}
|
||||
|
||||
<*
|
||||
Copy the last value, pop it and return the copy of it.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use for copying"
|
||||
@return "A copy of the last value if it exists"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any? AnyList.copy_pop(&self, Allocator allocator)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
defer self.free_element(self.entries[self.size]);
|
||||
return allocator::clone_any(allocator, self.entries[--self.size]);
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Copy the last value, pop it and return the copy of it.
|
||||
|
||||
@return "A temp copy of the last value if it exists"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any? AnyList.tcopy_pop(&self) => self.copy_pop(tmem);
|
||||
|
||||
|
||||
<*
|
||||
Pop the last value. It must later be released using `list.free_element()`.
|
||||
|
||||
@return "The last value if it exists"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any? AnyList.pop_retained(&self)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
return self.entries[--self.size];
|
||||
}
|
||||
|
||||
<*
|
||||
Remove all elements in the list.
|
||||
*>
|
||||
fn void AnyList.clear(&self)
|
||||
{
|
||||
for (usz i = 0; i < self.size; i++)
|
||||
{
|
||||
self.free_element(self.entries[i]);
|
||||
}
|
||||
self.size = 0;
|
||||
}
|
||||
|
||||
<*
|
||||
Pop a value who's type is known. If the type is incorrect, this
|
||||
will still pop the element.
|
||||
|
||||
@param $Type : "The type we assume the value has"
|
||||
@return "The first value as the type given"
|
||||
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
|
||||
*>
|
||||
macro AnyList.pop_first(&self, $Type)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
defer self.remove_at(0);
|
||||
return *anycast(self.entries[0], $Type);
|
||||
}
|
||||
|
||||
<*
|
||||
Pop the first value. It must later be released using `list.free_element()`.
|
||||
|
||||
@return "The first value if it exists"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any? AnyList.pop_first_retained(&self)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
defer self.remove_at(0);
|
||||
return self.entries[0];
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Copy the first value, pop it and return the copy of it.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use for copying"
|
||||
@return "A copy of the first value if it exists"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any? AnyList.copy_pop_first(&self, Allocator allocator)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
defer self.free_element(self.entries[self.size]);
|
||||
defer self.remove_at(0);
|
||||
return allocator::clone_any(allocator, self.entries[0]);
|
||||
}
|
||||
|
||||
<*
|
||||
Copy the first value, pop it and return the temp copy of it.
|
||||
|
||||
@return "A temp copy of the first value if it exists"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any? AnyList.tcopy_pop_first(&self) => self.copy_pop_first(tmem);
|
||||
|
||||
<*
|
||||
Remove the element at the particular index.
|
||||
|
||||
@param index : "The index of the element to remove"
|
||||
@require index < self.size
|
||||
*>
|
||||
fn void AnyList.remove_at(&self, usz index)
|
||||
{
|
||||
if (!--self.size || index == self.size) return;
|
||||
self.free_element(self.entries[index]);
|
||||
self.entries[index .. self.size - 1] = self.entries[index + 1 .. self.size];
|
||||
}
|
||||
|
||||
<*
|
||||
Add all the elements in another AnyList.
|
||||
|
||||
@param [&in] other_list : "The list to add"
|
||||
*>
|
||||
fn void AnyList.add_all(&self, AnyList* other_list)
|
||||
{
|
||||
if (!other_list.size) return;
|
||||
self.reserve(other_list.size);
|
||||
foreach (value : other_list)
|
||||
{
|
||||
self.entries[self.size++] = allocator::clone_any(self.allocator, value);
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Reverse the order of the elements in the list.
|
||||
*>
|
||||
fn void AnyList.reverse(&self)
|
||||
{
|
||||
if (self.size < 2) return;
|
||||
usz half = self.size / 2U;
|
||||
usz end = self.size - 1;
|
||||
for (usz i = 0; i < half; i++)
|
||||
{
|
||||
self.swap(i, end - i);
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Return a view of the data as a slice.
|
||||
|
||||
@return "The slice view"
|
||||
*>
|
||||
fn any[] AnyList.array_view(&self)
|
||||
{
|
||||
return self.entries[:self.size];
|
||||
}
|
||||
|
||||
<*
|
||||
Push an element to the front of the list.
|
||||
|
||||
@param value : "The value to push to the list"
|
||||
*>
|
||||
macro void AnyList.push_front(&self, value)
|
||||
{
|
||||
self.insert_at(0, value);
|
||||
}
|
||||
|
||||
<*
|
||||
Insert an element at a particular index.
|
||||
|
||||
@param index : "the index where the element should be inserted"
|
||||
@param type : "the value to insert"
|
||||
@require index <= self.size : "The index is out of bounds"
|
||||
*>
|
||||
macro void AnyList.insert_at(&self, usz index, type)
|
||||
{
|
||||
if (index == self.size)
|
||||
{
|
||||
self.push(type);
|
||||
return;
|
||||
}
|
||||
any value = allocator::copy(self.allocator, type);
|
||||
self._insert_at(self, index, value);
|
||||
}
|
||||
|
||||
<*
|
||||
Remove the last element in the list. The list may not be empty.
|
||||
|
||||
@require self.size > 0 : "The list was already empty"
|
||||
*>
|
||||
fn void AnyList.remove_last(&self)
|
||||
{
|
||||
self.free_element(self.entries[--self.size]);
|
||||
}
|
||||
|
||||
<*
|
||||
Remove the first element in the list, the list may not be empty.
|
||||
|
||||
@require self.size > 0
|
||||
*>
|
||||
fn void AnyList.remove_first(&self)
|
||||
{
|
||||
self.remove_at(0);
|
||||
}
|
||||
typedef AnyList = inline InterfaceList {any};
|
||||
|
||||
<*
|
||||
Return the first element by value, assuming it is the given type.
|
||||
@@ -313,10 +38,7 @@ macro AnyList.first(&self, $Type)
|
||||
@return "The first element"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any? AnyList.first_any(&self) @inline
|
||||
{
|
||||
return self.size ? self.entries[0] : NO_MORE_ELEMENT?;
|
||||
}
|
||||
fn any? AnyList.first_any(&self) @inline => InterfaceList {any}.first(self);
|
||||
|
||||
<*
|
||||
Return the last element by value, assuming it is the given type.
|
||||
@@ -336,29 +58,36 @@ macro AnyList.last(&self, $Type)
|
||||
@return "The last element"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any? AnyList.last_any(&self) @inline
|
||||
fn any? AnyList.last_any(&self) @inline => InterfaceList {any}.last(self);
|
||||
|
||||
<*
|
||||
Pop a value who's type is known. If the type is incorrect, this
|
||||
will still pop the element.
|
||||
|
||||
@param $Type : "The type we assume the value has"
|
||||
@return "The last value as the type given"
|
||||
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
|
||||
*>
|
||||
macro AnyList.pop(&self, $Type)
|
||||
{
|
||||
return self.size ? self.entries[self.size - 1] : NO_MORE_ELEMENT?;
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
defer self.free_element(self.entries[self.size]);
|
||||
return *anycast(self.entries[--self.size], $Type);
|
||||
}
|
||||
|
||||
<*
|
||||
Return whether the list is empty.
|
||||
Pop a value who's type is known. If the type is incorrect, this
|
||||
will still pop the element.
|
||||
|
||||
@return "True if the list is empty"
|
||||
@param $Type : "The type we assume the value has"
|
||||
@return "The first value as the type given"
|
||||
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
|
||||
*>
|
||||
fn bool AnyList.is_empty(&self) @inline
|
||||
macro AnyList.pop_first(&self, $Type)
|
||||
{
|
||||
return !self.size;
|
||||
}
|
||||
|
||||
<*
|
||||
Return the length of the list.
|
||||
|
||||
@return "The number of elements in the list"
|
||||
*>
|
||||
fn usz AnyList.len(&self) @operator(len) @inline
|
||||
{
|
||||
return self.size;
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
defer self.remove_at(0);
|
||||
return *anycast(self.entries[0], $Type);
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -383,222 +112,11 @@ macro AnyList.get(&self, usz index, $Type)
|
||||
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
|
||||
@require index < self.size : "Index out of range"
|
||||
*>
|
||||
fn any AnyList.get_any(&self, usz index) @inline @operator([])
|
||||
{
|
||||
return self.entries[index];
|
||||
}
|
||||
fn any AnyList.get_any(&self, usz index) @inline @operator([]) => InterfaceList {any}.get(self, index);
|
||||
|
||||
<*
|
||||
Completely free and clear a list.
|
||||
Return the length of the list.
|
||||
|
||||
@return "The number of elements in the list"
|
||||
*>
|
||||
fn void AnyList.free(&self)
|
||||
{
|
||||
if (!self.allocator) return;
|
||||
self.clear();
|
||||
allocator::free(self.allocator, self.entries);
|
||||
self.capacity = 0;
|
||||
self.entries = null;
|
||||
}
|
||||
|
||||
<*
|
||||
Swap two elements in a list.
|
||||
|
||||
@param i : "Index of one of the elements"
|
||||
@param j : "Index of the other element"
|
||||
@require i < self.size : "The first index is out of range"
|
||||
@require j < self.size : "The second index is out of range"
|
||||
*>
|
||||
fn void AnyList.swap(&self, usz i, usz j)
|
||||
{
|
||||
any temp = self.entries[i];
|
||||
self.entries[i] = self.entries[j];
|
||||
self.entries[j] = temp;
|
||||
}
|
||||
|
||||
<*
|
||||
Print the list to a formatter.
|
||||
*>
|
||||
fn usz? AnyList.to_format(&self, Formatter* formatter) @dynamic
|
||||
{
|
||||
switch (self.size)
|
||||
{
|
||||
case 0:
|
||||
return formatter.print("[]")!;
|
||||
case 1:
|
||||
return formatter.printf("[%s]", self.entries[0])!;
|
||||
default:
|
||||
usz n = formatter.print("[")!;
|
||||
foreach (i, element : self.entries[:self.size])
|
||||
{
|
||||
if (i != 0) formatter.print(", ")!;
|
||||
n += formatter.printf("%s", element)!;
|
||||
}
|
||||
n += formatter.print("]")!;
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Remove any elements matching the predicate.
|
||||
|
||||
@param filter : "The function to determine if it should be removed or not"
|
||||
@return "the number of deleted elements"
|
||||
*>
|
||||
fn usz AnyList.remove_if(&self, AnyPredicate filter)
|
||||
{
|
||||
return self._remove_if(filter, false);
|
||||
}
|
||||
|
||||
<*
|
||||
Retain the elements matching the predicate.
|
||||
|
||||
@param selection : "The function to determine if it should be kept or not"
|
||||
@return "the number of deleted elements"
|
||||
*>
|
||||
fn usz AnyList.retain_if(&self, AnyPredicate selection)
|
||||
{
|
||||
return self._remove_if(selection, true);
|
||||
}
|
||||
|
||||
<*
|
||||
Remove any elements matching the predicate.
|
||||
|
||||
@param filter : "The function to determine if it should be removed or not"
|
||||
@param context : "The context to the function"
|
||||
@return "the number of deleted elements"
|
||||
*>
|
||||
fn usz AnyList.remove_using_test(&self, AnyTest filter, any context)
|
||||
{
|
||||
return self._remove_using_test(filter, false, context);
|
||||
}
|
||||
|
||||
<*
|
||||
Retain any elements matching the predicate.
|
||||
|
||||
@param selection : "The function to determine if it should be retained or not"
|
||||
@param context : "The context to the function"
|
||||
@return "the number of deleted elements"
|
||||
*>
|
||||
fn usz AnyList.retain_using_test(&self, AnyTest selection, any context)
|
||||
{
|
||||
return self._remove_using_test(selection, true, context);
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Reserve memory so that at least the `min_capacity` exists.
|
||||
|
||||
@param min_capacity : "The min capacity to hold"
|
||||
*>
|
||||
fn void AnyList.reserve(&self, usz min_capacity)
|
||||
{
|
||||
if (!min_capacity) return;
|
||||
if (self.capacity >= min_capacity) return;
|
||||
if (!self.allocator) self.allocator = tmem;
|
||||
min_capacity = math::next_power_of_2(min_capacity);
|
||||
self.entries = allocator::realloc(self.allocator, self.entries, any.sizeof * min_capacity);
|
||||
self.capacity = min_capacity;
|
||||
}
|
||||
|
||||
<*
|
||||
Set the element at any index.
|
||||
|
||||
@param index : "The index where to set the value."
|
||||
@param value : "The value to set"
|
||||
@require index <= self.size : "Index out of range"
|
||||
*>
|
||||
macro void AnyList.set(&self, usz index, value)
|
||||
{
|
||||
if (index == self.size)
|
||||
{
|
||||
self.push(value);
|
||||
return;
|
||||
}
|
||||
self.free_element(self.entries[index]);
|
||||
self.entries[index] = allocator::copy(self.allocator, value);
|
||||
}
|
||||
|
||||
// -- private
|
||||
|
||||
fn void AnyList.ensure_capacity(&self, usz added = 1) @inline @private
|
||||
{
|
||||
usz new_size = self.size + added;
|
||||
if (self.capacity >= new_size) return;
|
||||
|
||||
assert(new_size < usz.max / 2U);
|
||||
usz new_capacity = self.capacity ? 2U * self.capacity : 16U;
|
||||
while (new_capacity < new_size) new_capacity *= 2U;
|
||||
self.reserve(new_capacity);
|
||||
}
|
||||
|
||||
fn void AnyList._append(&self, any element) @local
|
||||
{
|
||||
self.ensure_capacity();
|
||||
self.entries[self.size++] = element;
|
||||
}
|
||||
|
||||
<*
|
||||
@require index < self.size
|
||||
*>
|
||||
fn void AnyList._insert_at(&self, usz index, any value) @local
|
||||
{
|
||||
self.ensure_capacity();
|
||||
for (usz i = self.size; i > index; i--)
|
||||
{
|
||||
self.entries[i] = self.entries[i - 1];
|
||||
}
|
||||
self.size++;
|
||||
self.entries[index] = value;
|
||||
}
|
||||
|
||||
macro usz AnyList._remove_using_test(&self, AnyTest filter, bool $invert, ctx) @local
|
||||
{
|
||||
usz size = self.size;
|
||||
for (usz i = size, usz k = size; k > 0; k = i)
|
||||
{
|
||||
// Find last index of item to be deleted.
|
||||
$if $invert:
|
||||
while (i > 0 && !filter(&self.entries[i - 1], ctx)) i--;
|
||||
$else
|
||||
while (i > 0 && filter(&self.entries[i - 1], ctx)) i--;
|
||||
$endif
|
||||
// Remove the items from this index up to the one not to be deleted.
|
||||
usz n = self.size - k;
|
||||
for (usz j = i; j < k; j++) self.free_element(self.entries[j]);
|
||||
self.entries[i:n] = self.entries[k:n];
|
||||
self.size -= k - i;
|
||||
// Find last index of item not to be deleted.
|
||||
$if $invert:
|
||||
while (i > 0 && filter(&self.entries[i - 1], ctx)) i--;
|
||||
$else
|
||||
while (i > 0 && !filter(&self.entries[i - 1], ctx)) i--;
|
||||
$endif
|
||||
}
|
||||
return size - self.size;
|
||||
}
|
||||
|
||||
macro usz AnyList._remove_if(&self, AnyPredicate filter, bool $invert) @local
|
||||
{
|
||||
usz size = self.size;
|
||||
for (usz i = size, usz k = size; k > 0; k = i)
|
||||
{
|
||||
// Find last index of item to be deleted.
|
||||
$if $invert:
|
||||
while (i > 0 && !filter(&self.entries[i - 1])) i--;
|
||||
$else
|
||||
while (i > 0 && filter(&self.entries[i - 1])) i--;
|
||||
$endif
|
||||
// Remove the items from this index up to the one not to be deleted.
|
||||
usz n = self.size - k;
|
||||
for (usz j = i; j < k; j++) self.free_element(self.entries[j]);
|
||||
self.entries[i:n] = self.entries[k:n];
|
||||
self.size -= k - i;
|
||||
// Find last index of item not to be deleted.
|
||||
$if $invert:
|
||||
while (i > 0 && filter(&self.entries[i - 1])) i--;
|
||||
$else
|
||||
while (i > 0 && !filter(&self.entries[i - 1])) i--;
|
||||
$endif
|
||||
}
|
||||
return size - self.size;
|
||||
}
|
||||
fn usz AnyList.len(&self) @operator(len) @inline => InterfaceList {any}.len(self);
|
||||
|
||||
@@ -11,7 +11,7 @@ alias ElementPredicate = fn bool(Type *type);
|
||||
alias ElementTest = fn bool(Type *type, any context);
|
||||
const ELEMENT_IS_EQUATABLE = types::is_equatable_type(Type);
|
||||
const ELEMENT_IS_POINTER = Type.kindof == POINTER;
|
||||
macro type_is_overaligned() => Type.alignof > mem::DEFAULT_MEM_ALIGNMENT;
|
||||
macro bool type_is_overaligned() => Type.alignof > mem::DEFAULT_MEM_ALIGNMENT;
|
||||
|
||||
struct ElasticArray (Printable)
|
||||
{
|
||||
@@ -458,4 +458,4 @@ fn usz ElasticArray.compact_count(&self) @if(ELEMENT_IS_POINTER)
|
||||
fn usz ElasticArray.compact(&self) @if(ELEMENT_IS_POINTER)
|
||||
{
|
||||
return list_common::list_compact(self);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,10 @@ struct HashMap (Printable)
|
||||
{
|
||||
Entry*[] table;
|
||||
Allocator allocator;
|
||||
uint count; // Number of elements
|
||||
uint threshold; // Resize limit
|
||||
<* Last inserted LinkedEntry *>
|
||||
uint count;
|
||||
<* Resize limit *>
|
||||
uint threshold;
|
||||
float load_factor;
|
||||
}
|
||||
|
||||
@@ -605,4 +607,4 @@ macro uint index_for(uint hash, uint capacity) @private
|
||||
return hash & (capacity - 1);
|
||||
}
|
||||
|
||||
int dummy @local;
|
||||
int dummy @local;
|
||||
|
||||
@@ -25,8 +25,10 @@ struct HashSet (Printable)
|
||||
{
|
||||
Entry*[] table;
|
||||
Allocator allocator;
|
||||
usz count; // Number of elements
|
||||
usz threshold; // Resize limit
|
||||
<* Number of elements *>
|
||||
usz count;
|
||||
<* Resize limit *>
|
||||
usz threshold;
|
||||
float load_factor;
|
||||
}
|
||||
|
||||
|
||||
539
lib/std/collections/interfacelist.c3
Normal file
539
lib/std/collections/interfacelist.c3
Normal file
@@ -0,0 +1,539 @@
|
||||
// Copyright (c) 2024-2025 Christoffer Lerno. All rights reserved.
|
||||
// Use of self source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
<*
|
||||
@require Type.kindof == INTERFACE || Type.kindof == ANY : "The kind of an interfacelist must be an interface or `any`"
|
||||
*>
|
||||
module std::collections::interfacelist {Type};
|
||||
import std::io,std::math;
|
||||
|
||||
alias InterfacePredicate = fn bool(Type value);
|
||||
alias InterfaceTest = fn bool(Type type, Type context);
|
||||
|
||||
<*
|
||||
The InterfaceList contains a heterogenous set of types implementing an interface. anything placed in the
|
||||
list will shallowly copied in order to be stored as the interface. This means
|
||||
that the list will copy and free its elements.
|
||||
|
||||
However, because we're getting interface values back when we pop, those operations
|
||||
need to take an allocator, as we can only copy then pop then return the copy.
|
||||
|
||||
If we're not doing pop, then things are easier, since we can just hand over
|
||||
the existing value.
|
||||
*>
|
||||
struct InterfaceList (Printable)
|
||||
{
|
||||
usz size;
|
||||
usz capacity;
|
||||
Allocator allocator;
|
||||
Type* entries;
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Initialize the list. If not initialized then it will use the temp allocator
|
||||
when something is pushed to it.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use"
|
||||
@param initial_capacity : "The initial capacity to reserve, defaults to 16"
|
||||
*>
|
||||
fn InterfaceList* InterfaceList.init(&self, Allocator allocator, usz initial_capacity = 16)
|
||||
{
|
||||
self.allocator = allocator;
|
||||
self.size = 0;
|
||||
if (initial_capacity > 0)
|
||||
{
|
||||
initial_capacity = math::next_power_of_2(initial_capacity);
|
||||
self.entries = allocator::alloc_array(allocator, Type, initial_capacity);
|
||||
}
|
||||
else
|
||||
{
|
||||
self.entries = null;
|
||||
}
|
||||
self.capacity = initial_capacity;
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
Initialize the list using the temp allocator.
|
||||
|
||||
@param initial_capacity : "The initial capacity to reserve"
|
||||
*>
|
||||
fn InterfaceList* InterfaceList.tinit(&self, usz initial_capacity = 16)
|
||||
{
|
||||
return self.init(tmem, initial_capacity) @inline;
|
||||
}
|
||||
|
||||
fn bool InterfaceList.is_initialized(&self) @inline => self.allocator != null;
|
||||
|
||||
<*
|
||||
Push an element on the list by cloning it.
|
||||
@require $defined(Type t = &element) : "Element must implement the interface"
|
||||
*>
|
||||
macro void InterfaceList.push(&self, element)
|
||||
{
|
||||
if (!self.allocator) self.allocator = tmem;
|
||||
self._append(allocator::clone(self.allocator, element));
|
||||
}
|
||||
|
||||
<*
|
||||
Free a retained element removed using *_retained.
|
||||
*>
|
||||
fn void InterfaceList.free_element(&self, Type element) @inline
|
||||
{
|
||||
allocator::free(self.allocator, element.ptr);
|
||||
}
|
||||
|
||||
<*
|
||||
Copy the last value, pop it and return the copy of it.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use for copying"
|
||||
@return "A copy of the last value if it exists"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn Type? InterfaceList.copy_pop(&self, Allocator allocator)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
defer self.free_element(self.entries[self.size]);
|
||||
return (Type)allocator::clone_any(allocator, self.entries[--self.size]);
|
||||
}
|
||||
|
||||
<*
|
||||
Copy the last value, pop it and return the copy of it.
|
||||
|
||||
@return "A temp copy of the last value if it exists"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn Type? InterfaceList.tcopy_pop(&self) => self.copy_pop(tmem);
|
||||
|
||||
<*
|
||||
Pop the last value. It must later be released using `list.free_element()`.
|
||||
|
||||
@return "The last value if it exists"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn Type? InterfaceList.pop_retained(&self)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
return self.entries[--self.size];
|
||||
}
|
||||
|
||||
<*
|
||||
Remove all elements in the list.
|
||||
*>
|
||||
fn void InterfaceList.clear(&self)
|
||||
{
|
||||
for (usz i = 0; i < self.size; i++)
|
||||
{
|
||||
self.free_element(self.entries[i]);
|
||||
}
|
||||
self.size = 0;
|
||||
}
|
||||
|
||||
<*
|
||||
Pop the first value. It must later be released using `list.free_element()`.
|
||||
|
||||
@return "The first value if it exists"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn Type? InterfaceList.pop_first_retained(&self)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
defer self.remove_at(0);
|
||||
return self.entries[0];
|
||||
}
|
||||
|
||||
<*
|
||||
Copy the first value, pop it and return the copy of it.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use for copying"
|
||||
@return "A copy of the first value if it exists"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn Type? InterfaceList.copy_pop_first(&self, Allocator allocator)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
defer self.free_element(self.entries[self.size]);
|
||||
defer self.remove_at(0);
|
||||
return (Type)allocator::clone_any(allocator, self.entries[0]);
|
||||
}
|
||||
|
||||
<*
|
||||
Copy the first value, pop it and return the temp copy of it.
|
||||
|
||||
@return "A temp copy of the first value if it exists"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn Type? InterfaceList.tcopy_pop_first(&self) => self.copy_pop_first(tmem);
|
||||
|
||||
<*
|
||||
Remove the element at the particular index.
|
||||
|
||||
@param index : "The index of the element to remove"
|
||||
@require index < self.size
|
||||
*>
|
||||
fn void InterfaceList.remove_at(&self, usz index)
|
||||
{
|
||||
if (!--self.size || index == self.size) return;
|
||||
self.free_element(self.entries[index]);
|
||||
self.entries[index .. self.size - 1] = self.entries[index + 1 .. self.size];
|
||||
}
|
||||
|
||||
<*
|
||||
Add all the elements in another InterfaceList.
|
||||
|
||||
@param [&in] other_list : "The list to add"
|
||||
*>
|
||||
fn void InterfaceList.add_all(&self, InterfaceList* other_list)
|
||||
{
|
||||
if (!other_list.size) return;
|
||||
self.reserve(other_list.size);
|
||||
foreach (value : other_list)
|
||||
{
|
||||
self.entries[self.size++] = (Type)allocator::clone_any(self.allocator, value);
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Reverse the order of the elements in the list.
|
||||
*>
|
||||
fn void InterfaceList.reverse(&self)
|
||||
{
|
||||
if (self.size < 2) return;
|
||||
usz half = self.size / 2U;
|
||||
usz end = self.size - 1;
|
||||
for (usz i = 0; i < half; i++)
|
||||
{
|
||||
self.swap(i, end - i);
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Return a view of the data as a slice.
|
||||
|
||||
@return "The slice view"
|
||||
*>
|
||||
fn Type[] InterfaceList.array_view(&self)
|
||||
{
|
||||
return self.entries[:self.size];
|
||||
}
|
||||
|
||||
<*
|
||||
Push an element to the front of the list.
|
||||
|
||||
@param value : "The value to push to the list"
|
||||
@require $defined(Type t = &value) : "Value must implement the interface"
|
||||
*>
|
||||
macro void InterfaceList.push_front(&self, value)
|
||||
{
|
||||
self.insert_at(0, value);
|
||||
}
|
||||
|
||||
<*
|
||||
Insert an element at a particular index.
|
||||
|
||||
@param index : "the index where the element should be inserted"
|
||||
@param type : "the value to insert"
|
||||
@require index <= self.size : "The index is out of bounds"
|
||||
@require $defined(Type t = &type) : "Type must implement the interface"
|
||||
*>
|
||||
macro void InterfaceList.insert_at(&self, usz index, type)
|
||||
{
|
||||
if (index == self.size)
|
||||
{
|
||||
self.push(type);
|
||||
return;
|
||||
}
|
||||
Type value = allocator::clone(self.allocator, type);
|
||||
self._insert_at(self, index, value);
|
||||
}
|
||||
|
||||
<*
|
||||
Remove the last element in the list. The list may not be empty.
|
||||
|
||||
@require self.size > 0 : "The list was already empty"
|
||||
*>
|
||||
fn void InterfaceList.remove_last(&self)
|
||||
{
|
||||
self.free_element(self.entries[--self.size]);
|
||||
}
|
||||
|
||||
<*
|
||||
Remove the first element in the list, the list may not be empty.
|
||||
|
||||
@require self.size > 0
|
||||
*>
|
||||
fn void InterfaceList.remove_first(&self)
|
||||
{
|
||||
self.remove_at(0);
|
||||
}
|
||||
|
||||
<*
|
||||
Return the first element
|
||||
|
||||
@return "The first element"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn Type? InterfaceList.first(&self) @inline
|
||||
{
|
||||
return self.size ? self.entries[0] : NO_MORE_ELEMENT?;
|
||||
}
|
||||
|
||||
<*
|
||||
Return the last element
|
||||
|
||||
@return "The last element"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn Type? InterfaceList.last(&self) @inline
|
||||
{
|
||||
return self.size ? self.entries[self.size - 1] : NO_MORE_ELEMENT?;
|
||||
}
|
||||
|
||||
<*
|
||||
Return whether the list is empty.
|
||||
|
||||
@return "True if the list is empty"
|
||||
*>
|
||||
fn bool InterfaceList.is_empty(&self) @inline
|
||||
{
|
||||
return !self.size;
|
||||
}
|
||||
|
||||
<*
|
||||
Return the length of the list.
|
||||
|
||||
@return "The number of elements in the list"
|
||||
*>
|
||||
fn usz InterfaceList.len(&self) @operator(len) @inline
|
||||
{
|
||||
return self.size;
|
||||
}
|
||||
|
||||
<*
|
||||
Return an element in the list.
|
||||
|
||||
@param index : "The index of the element to retrieve"
|
||||
@return "The element at the index"
|
||||
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
|
||||
@require index < self.size : "Index out of range"
|
||||
*>
|
||||
fn Type InterfaceList.get(&self, usz index) @inline @operator([])
|
||||
{
|
||||
return self.entries[index];
|
||||
}
|
||||
|
||||
<*
|
||||
Completely free and clear a list.
|
||||
*>
|
||||
fn void InterfaceList.free(&self)
|
||||
{
|
||||
if (!self.allocator) return;
|
||||
self.clear();
|
||||
allocator::free(self.allocator, self.entries);
|
||||
self.capacity = 0;
|
||||
self.entries = null;
|
||||
}
|
||||
|
||||
<*
|
||||
Swap two elements in a list.
|
||||
|
||||
@param i : "Index of one of the elements"
|
||||
@param j : "Index of the other element"
|
||||
@require i < self.size : "The first index is out of range"
|
||||
@require j < self.size : "The second index is out of range"
|
||||
*>
|
||||
fn void InterfaceList.swap(&self, usz i, usz j)
|
||||
{
|
||||
Type temp = self.entries[i];
|
||||
self.entries[i] = self.entries[j];
|
||||
self.entries[j] = temp;
|
||||
}
|
||||
|
||||
<*
|
||||
Print the list to a formatter.
|
||||
*>
|
||||
fn usz? InterfaceList.to_format(&self, Formatter* formatter) @dynamic
|
||||
{
|
||||
switch (self.size)
|
||||
{
|
||||
case 0:
|
||||
return formatter.print("[]")!;
|
||||
case 1:
|
||||
return formatter.printf("[%s]", self.entries[0])!;
|
||||
default:
|
||||
usz n = formatter.print("[")!;
|
||||
foreach (i, element : self.entries[:self.size])
|
||||
{
|
||||
if (i != 0) formatter.print(", ")!;
|
||||
n += formatter.printf("%s", element)!;
|
||||
}
|
||||
n += formatter.print("]")!;
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Remove Type elements matching the predicate.
|
||||
|
||||
@param filter : "The function to determine if it should be removed or not"
|
||||
@return "the number of deleted elements"
|
||||
*>
|
||||
fn usz InterfaceList.remove_if(&self, InterfacePredicate filter)
|
||||
{
|
||||
return self._remove_if(filter, false);
|
||||
}
|
||||
|
||||
<*
|
||||
Retain the elements matching the predicate.
|
||||
|
||||
@param selection : "The function to determine if it should be kept or not"
|
||||
@return "the number of deleted elements"
|
||||
*>
|
||||
fn usz InterfaceList.retain_if(&self, InterfacePredicate selection)
|
||||
{
|
||||
return self._remove_if(selection, true);
|
||||
}
|
||||
|
||||
<*
|
||||
Remove Type elements matching the predicate.
|
||||
|
||||
@param filter : "The function to determine if it should be removed or not"
|
||||
@param context : "The context to the function"
|
||||
@return "the number of deleted elements"
|
||||
*>
|
||||
fn usz InterfaceList.remove_using_test(&self, InterfaceTest filter, Type context)
|
||||
{
|
||||
return self._remove_using_test(filter, false, context);
|
||||
}
|
||||
|
||||
<*
|
||||
Retain Type elements matching the predicate.
|
||||
|
||||
@param selection : "The function to determine if it should be retained or not"
|
||||
@param context : "The context to the function"
|
||||
@return "the number of deleted elements"
|
||||
*>
|
||||
fn usz InterfaceList.retain_using_test(&self, InterfaceTest selection, Type context)
|
||||
{
|
||||
return self._remove_using_test(selection, true, context);
|
||||
}
|
||||
|
||||
<*
|
||||
Reserve memory so that at least the `min_capacity` exists.
|
||||
|
||||
@param min_capacity : "The min capacity to hold"
|
||||
*>
|
||||
fn void InterfaceList.reserve(&self, usz min_capacity)
|
||||
{
|
||||
if (!min_capacity) return;
|
||||
if (self.capacity >= min_capacity) return;
|
||||
if (!self.allocator) self.allocator = tmem;
|
||||
min_capacity = math::next_power_of_2(min_capacity);
|
||||
self.entries = allocator::realloc(self.allocator, self.entries, Type.sizeof * min_capacity);
|
||||
self.capacity = min_capacity;
|
||||
}
|
||||
|
||||
<*
|
||||
Set the element at Type index.
|
||||
|
||||
@param index : "The index where to set the value."
|
||||
@param value : "The value to set"
|
||||
@require index <= self.size : "Index out of range"
|
||||
@require $defined(Type t = &value) : "Value must implement the interface"
|
||||
*>
|
||||
macro void InterfaceList.set(&self, usz index, value)
|
||||
{
|
||||
if (index == self.size)
|
||||
{
|
||||
self.push(value);
|
||||
return;
|
||||
}
|
||||
self.free_element(self.entries[index]);
|
||||
self.entries[index] = allocator::clone(self.allocator, value);
|
||||
}
|
||||
|
||||
// -- private
|
||||
|
||||
fn void InterfaceList.ensure_capacity(&self, usz added = 1) @inline @private
|
||||
{
|
||||
usz new_size = self.size + added;
|
||||
if (self.capacity >= new_size) return;
|
||||
|
||||
assert(new_size < usz.max / 2U);
|
||||
usz new_capacity = self.capacity ? 2U * self.capacity : 16U;
|
||||
while (new_capacity < new_size) new_capacity *= 2U;
|
||||
self.reserve(new_capacity);
|
||||
}
|
||||
|
||||
fn void InterfaceList._append(&self, Type element) @local
|
||||
{
|
||||
self.ensure_capacity();
|
||||
self.entries[self.size++] = element;
|
||||
}
|
||||
|
||||
<*
|
||||
@require index < self.size
|
||||
*>
|
||||
fn void InterfaceList._insert_at(&self, usz index, Type value) @local
|
||||
{
|
||||
self.ensure_capacity();
|
||||
for (usz i = self.size; i > index; i--)
|
||||
{
|
||||
self.entries[i] = self.entries[i - 1];
|
||||
}
|
||||
self.size++;
|
||||
self.entries[index] = value;
|
||||
}
|
||||
|
||||
macro usz InterfaceList._remove_using_test(&self, InterfaceTest filter, bool $invert, ctx) @local
|
||||
{
|
||||
usz size = self.size;
|
||||
for (usz i = size, usz k = size; k > 0; k = i)
|
||||
{
|
||||
// Find last index of item to be deleted.
|
||||
$if $invert:
|
||||
while (i > 0 && !filter(self.entries[i - 1], ctx)) i--;
|
||||
$else
|
||||
while (i > 0 && filter(self.entries[i - 1], ctx)) i--;
|
||||
$endif
|
||||
// Remove the items from this index up to the one not to be deleted.
|
||||
usz n = self.size - k;
|
||||
for (usz j = i; j < k; j++) self.free_element(self.entries[j]);
|
||||
self.entries[i:n] = self.entries[k:n];
|
||||
self.size -= k - i;
|
||||
// Find last index of item not to be deleted.
|
||||
$if $invert:
|
||||
while (i > 0 && filter(self.entries[i - 1], ctx)) i--;
|
||||
$else
|
||||
while (i > 0 && !filter(self.entries[i - 1], ctx)) i--;
|
||||
$endif
|
||||
}
|
||||
return size - self.size;
|
||||
}
|
||||
|
||||
macro usz InterfaceList._remove_if(&self, InterfacePredicate filter, bool $invert) @local
|
||||
{
|
||||
usz size = self.size;
|
||||
for (usz i = size, usz k = size; k > 0; k = i)
|
||||
{
|
||||
// Find last index of item to be deleted.
|
||||
$if $invert:
|
||||
while (i > 0 && !filter(self.entries[i - 1])) i--;
|
||||
$else
|
||||
while (i > 0 && filter(self.entries[i - 1])) i--;
|
||||
$endif
|
||||
// Remove the items from this index up to the one not to be deleted.
|
||||
usz n = self.size - k;
|
||||
for (usz j = i; j < k; j++) self.free_element(self.entries[j]);
|
||||
self.entries[i:n] = self.entries[k:n];
|
||||
self.size -= k - i;
|
||||
// Find last index of item not to be deleted.
|
||||
$if $invert:
|
||||
while (i > 0 && filter(self.entries[i - 1])) i--;
|
||||
$else
|
||||
while (i > 0 && !filter(self.entries[i - 1])) i--;
|
||||
$endif
|
||||
}
|
||||
return size - self.size;
|
||||
}
|
||||
@@ -7,16 +7,22 @@ const INITIAL_CAPACITY = 16;
|
||||
struct QueueEntry
|
||||
{
|
||||
Value value;
|
||||
QueueEntry* next; // Next in queue order
|
||||
QueueEntry* prev; // Previous in queue order
|
||||
<* Next in queue order *>
|
||||
QueueEntry* next;
|
||||
<* Previous in queue order *>
|
||||
QueueEntry* prev;
|
||||
}
|
||||
|
||||
struct LinkedBlockingQueue
|
||||
{
|
||||
QueueEntry* head; // First element in queue
|
||||
QueueEntry* tail; // Last element in queue
|
||||
usz count; // Current number of elements
|
||||
usz capacity; // Maximum capacity (0 for unbounded)
|
||||
<* First element in queue *>
|
||||
QueueEntry* head;
|
||||
<* Last element in queue *>
|
||||
QueueEntry* tail;
|
||||
<* Current number of elements *>
|
||||
usz count;
|
||||
<* Maximum capacity (0 for unbounded) *>
|
||||
usz capacity;
|
||||
Mutex lock;
|
||||
ConditionVariable not_empty;
|
||||
ConditionVariable not_full;
|
||||
|
||||
@@ -15,9 +15,12 @@ struct LinkedEntry
|
||||
uint hash;
|
||||
Key key;
|
||||
Value value;
|
||||
LinkedEntry* next; // For bucket chain
|
||||
LinkedEntry* before; // Previous in insertion order
|
||||
LinkedEntry* after; // Next in insertion order
|
||||
<* For bucket chain *>
|
||||
LinkedEntry* next;
|
||||
<* Previous in insertion order *>
|
||||
LinkedEntry* before;
|
||||
<* Next in insertion order *>
|
||||
LinkedEntry* after;
|
||||
}
|
||||
|
||||
struct LinkedHashMap (Printable)
|
||||
@@ -27,8 +30,10 @@ struct LinkedHashMap (Printable)
|
||||
usz count;
|
||||
usz threshold;
|
||||
float load_factor;
|
||||
LinkedEntry* head; // First inserted LinkedEntry
|
||||
LinkedEntry* tail; // Last inserted LinkedEntry
|
||||
<* First inserted LinkedEntry *>
|
||||
LinkedEntry* head;
|
||||
<* Last inserted LinkedEntry *>
|
||||
LinkedEntry* tail;
|
||||
}
|
||||
|
||||
|
||||
@@ -249,7 +254,7 @@ fn void? LinkedHashMap.remove(&map, Key key) @maydiscard
|
||||
fn void LinkedHashMap.clear(&map)
|
||||
{
|
||||
if (!map.count) return;
|
||||
|
||||
|
||||
LinkedEntry* entry = map.head;
|
||||
while (entry)
|
||||
{
|
||||
@@ -257,12 +262,12 @@ fn void LinkedHashMap.clear(&map)
|
||||
map.free_entry(entry);
|
||||
entry = next;
|
||||
}
|
||||
|
||||
|
||||
foreach (LinkedEntry** &bucket : map.table)
|
||||
{
|
||||
*bucket = null;
|
||||
}
|
||||
|
||||
|
||||
map.count = 0;
|
||||
map.head = null;
|
||||
map.tail = null;
|
||||
@@ -284,10 +289,10 @@ fn Key[] LinkedHashMap.tkeys(&self)
|
||||
fn Key[] LinkedHashMap.keys(&self, Allocator allocator)
|
||||
{
|
||||
if (!self.count) return {};
|
||||
|
||||
|
||||
Key[] list = allocator::alloc_array(allocator, Key, self.count);
|
||||
usz index = 0;
|
||||
|
||||
|
||||
LinkedEntry* entry = self.head;
|
||||
while (entry)
|
||||
{
|
||||
@@ -338,7 +343,7 @@ fn Value[] LinkedHashMap.values(&self, Allocator allocator)
|
||||
fn bool LinkedHashMap.has_value(&map, Value v) @if(VALUE_IS_EQUATABLE)
|
||||
{
|
||||
if (!map.count) return false;
|
||||
|
||||
|
||||
LinkedEntry* entry = map.head;
|
||||
while (entry)
|
||||
{
|
||||
@@ -396,7 +401,7 @@ fn void LinkedHashMap.add_entry(&map, uint hash, Key key, Value value, uint buck
|
||||
$if COPY_KEYS:
|
||||
key = key.copy(map.allocator);
|
||||
$endif
|
||||
|
||||
|
||||
LinkedEntry* entry = allocator::new(map.allocator, LinkedEntry, {
|
||||
.hash = hash,
|
||||
.key = key,
|
||||
@@ -405,10 +410,10 @@ fn void LinkedHashMap.add_entry(&map, uint hash, Key key, Value value, uint buck
|
||||
.before = map.tail,
|
||||
.after = null
|
||||
});
|
||||
|
||||
|
||||
// Update bucket chain
|
||||
map.table[bucket_index] = entry;
|
||||
|
||||
|
||||
// Update linked list
|
||||
if (map.tail)
|
||||
{
|
||||
@@ -420,7 +425,7 @@ fn void LinkedHashMap.add_entry(&map, uint hash, Key key, Value value, uint buck
|
||||
map.head = entry;
|
||||
}
|
||||
map.tail = entry;
|
||||
|
||||
|
||||
if (map.count++ >= map.threshold)
|
||||
{
|
||||
map.resize(map.table.len * 2);
|
||||
@@ -431,28 +436,28 @@ fn void LinkedHashMap.resize(&map, uint new_capacity) @private
|
||||
{
|
||||
LinkedEntry*[] old_table = map.table;
|
||||
uint old_capacity = old_table.len;
|
||||
|
||||
|
||||
if (old_capacity == MAXIMUM_CAPACITY)
|
||||
{
|
||||
map.threshold = uint.max;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
LinkedEntry*[] new_table = allocator::new_array(map.allocator, LinkedEntry*, new_capacity);
|
||||
map.table = new_table;
|
||||
map.threshold = (uint)(new_capacity * map.load_factor);
|
||||
|
||||
|
||||
// Rehash all entries - linked list order remains unchanged
|
||||
foreach (uint i, LinkedEntry *e : old_table)
|
||||
{
|
||||
if (!e) continue;
|
||||
|
||||
|
||||
// Split the bucket chain into two chains based on new bit
|
||||
LinkedEntry* lo_head = null;
|
||||
LinkedEntry* lo_tail = null;
|
||||
LinkedEntry* hi_head = null;
|
||||
LinkedEntry* hi_tail = null;
|
||||
|
||||
|
||||
do
|
||||
{
|
||||
LinkedEntry* next = e.next;
|
||||
@@ -484,7 +489,7 @@ fn void LinkedHashMap.resize(&map, uint new_capacity) @private
|
||||
e = next;
|
||||
}
|
||||
while (e);
|
||||
|
||||
|
||||
if (lo_tail)
|
||||
{
|
||||
lo_tail.next = null;
|
||||
@@ -496,7 +501,7 @@ fn void LinkedHashMap.resize(&map, uint new_capacity) @private
|
||||
new_table[i + old_capacity] = hi_head;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
map.free_internal(old_table.ptr);
|
||||
}
|
||||
|
||||
@@ -562,12 +567,12 @@ fn void LinkedHashMap.free_internal(&map, void* ptr) @inline @private
|
||||
fn bool LinkedHashMap.remove_entry_for_key(&map, Key key) @private
|
||||
{
|
||||
if (!map.count) return false;
|
||||
|
||||
|
||||
uint hash = rehash(key.hash());
|
||||
uint i = index_for(hash, map.table.len);
|
||||
LinkedEntry* prev = null;
|
||||
LinkedEntry* e = map.table[i];
|
||||
|
||||
|
||||
while (e)
|
||||
{
|
||||
if (e.hash == hash && equals(key, e.key))
|
||||
@@ -580,7 +585,7 @@ fn bool LinkedHashMap.remove_entry_for_key(&map, Key key) @private
|
||||
{
|
||||
map.table[i] = e.next;
|
||||
}
|
||||
|
||||
|
||||
if (e.before)
|
||||
{
|
||||
e.before.after = e.after;
|
||||
@@ -589,7 +594,7 @@ fn bool LinkedHashMap.remove_entry_for_key(&map, Key key) @private
|
||||
{
|
||||
map.head = e.after;
|
||||
}
|
||||
|
||||
|
||||
if (e.after)
|
||||
{
|
||||
e.after.before = e.before;
|
||||
@@ -598,7 +603,7 @@ fn bool LinkedHashMap.remove_entry_for_key(&map, Key key) @private
|
||||
{
|
||||
map.tail = e.before;
|
||||
}
|
||||
|
||||
|
||||
map.count--;
|
||||
map.free_entry(e);
|
||||
return true;
|
||||
|
||||
@@ -11,20 +11,27 @@ struct LinkedEntry
|
||||
{
|
||||
uint hash;
|
||||
Value value;
|
||||
LinkedEntry* next; // For bucket chain
|
||||
LinkedEntry* before; // Previous in insertion order
|
||||
LinkedEntry* after; // Next in insertion order
|
||||
<* For bucket chain *>
|
||||
LinkedEntry* next;
|
||||
<* Previous in insertion order *>
|
||||
LinkedEntry* before;
|
||||
<* Next in insertion order *>
|
||||
LinkedEntry* after;
|
||||
}
|
||||
|
||||
struct LinkedHashSet (Printable)
|
||||
{
|
||||
LinkedEntry*[] table;
|
||||
Allocator allocator;
|
||||
usz count; // Number of elements
|
||||
usz threshold; // Resize limit
|
||||
<* Number of elements *>
|
||||
usz count;
|
||||
<* Resize limit *>
|
||||
usz threshold;
|
||||
float load_factor;
|
||||
LinkedEntry* head; // First inserted LinkedEntry
|
||||
LinkedEntry* tail; // Last inserted LinkedEntry
|
||||
<* Resize limit *>
|
||||
LinkedEntry* head;
|
||||
<* First inserted LinkedEntry *>
|
||||
LinkedEntry* tail;
|
||||
}
|
||||
|
||||
fn int LinkedHashSet.len(&self) @operator(len) => (int) self.count;
|
||||
@@ -43,7 +50,7 @@ fn LinkedHashSet* LinkedHashSet.init(&self, Allocator allocator, usz capacity =
|
||||
self.threshold = (usz)(capacity * load_factor);
|
||||
self.load_factor = load_factor;
|
||||
self.table = allocator::new_array(allocator, LinkedEntry*, capacity);
|
||||
|
||||
|
||||
self.head = null;
|
||||
self.tail = null;
|
||||
return self;
|
||||
@@ -304,7 +311,7 @@ fn void LinkedHashSet.free(&set)
|
||||
fn void LinkedHashSet.clear(&set)
|
||||
{
|
||||
if (!set.count) return;
|
||||
|
||||
|
||||
LinkedEntry* entry = set.head;
|
||||
while (entry)
|
||||
{
|
||||
@@ -312,12 +319,12 @@ fn void LinkedHashSet.clear(&set)
|
||||
set.free_entry(entry);
|
||||
entry = next;
|
||||
}
|
||||
|
||||
|
||||
foreach (LinkedEntry** &bucket : set.table)
|
||||
{
|
||||
*bucket = null;
|
||||
}
|
||||
|
||||
|
||||
set.count = 0;
|
||||
set.head = null;
|
||||
set.tail = null;
|
||||
@@ -363,16 +370,16 @@ fn LinkedHashSet LinkedHashSet.intersection(&self, Allocator allocator, LinkedHa
|
||||
{
|
||||
LinkedHashSet result;
|
||||
result.init(allocator, math::min(self.table.len, other.table.len), self.load_factor);
|
||||
|
||||
|
||||
// Iterate through the smaller set for efficiency
|
||||
LinkedHashSet* smaller = self.count <= other.count ? self : other;
|
||||
LinkedHashSet* larger = self.count > other.count ? self : other;
|
||||
|
||||
smaller.@each(;Value value)
|
||||
|
||||
smaller.@each(;Value value)
|
||||
{
|
||||
if (larger.contains(value)) result.add(value);
|
||||
};
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -435,7 +442,7 @@ fn bool LinkedHashSet.is_subset(&self, LinkedHashSet* other)
|
||||
{
|
||||
if (self.count == 0) return true;
|
||||
if (self.count > other.count) return false;
|
||||
|
||||
|
||||
self.@each(; Value value) {
|
||||
if (!other.contains(value)) return false;
|
||||
};
|
||||
@@ -453,10 +460,10 @@ fn void LinkedHashSet.add_entry(&set, uint hash, Value value, uint bucket_index)
|
||||
.before = set.tail,
|
||||
.after = null
|
||||
});
|
||||
|
||||
|
||||
// Update bucket chain
|
||||
set.table[bucket_index] = entry;
|
||||
|
||||
|
||||
// Update linked list
|
||||
if (set.tail)
|
||||
{
|
||||
@@ -468,7 +475,7 @@ fn void LinkedHashSet.add_entry(&set, uint hash, Value value, uint bucket_index)
|
||||
set.head = entry;
|
||||
}
|
||||
set.tail = entry;
|
||||
|
||||
|
||||
if (set.count++ >= set.threshold)
|
||||
{
|
||||
set.resize(set.table.len * 2);
|
||||
@@ -479,28 +486,28 @@ fn void LinkedHashSet.resize(&set, usz new_capacity) @private
|
||||
{
|
||||
LinkedEntry*[] old_table = set.table;
|
||||
usz old_capacity = old_table.len;
|
||||
|
||||
|
||||
if (old_capacity == MAXIMUM_CAPACITY)
|
||||
{
|
||||
set.threshold = uint.max;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
LinkedEntry*[] new_table = allocator::new_array(set.allocator, LinkedEntry*, new_capacity);
|
||||
set.table = new_table;
|
||||
set.threshold = (uint)(new_capacity * set.load_factor);
|
||||
|
||||
|
||||
// Rehash all entries - linked list order remains unchanged
|
||||
foreach (uint i, LinkedEntry *e : old_table)
|
||||
{
|
||||
if (!e) continue;
|
||||
|
||||
|
||||
// Split the bucket chain into two chains based on new bit
|
||||
LinkedEntry* lo_head = null;
|
||||
LinkedEntry* lo_tail = null;
|
||||
LinkedEntry* hi_head = null;
|
||||
LinkedEntry* hi_tail = null;
|
||||
|
||||
|
||||
do
|
||||
{
|
||||
LinkedEntry* next = e.next;
|
||||
@@ -532,7 +539,7 @@ fn void LinkedHashSet.resize(&set, usz new_capacity) @private
|
||||
e = next;
|
||||
}
|
||||
while (e);
|
||||
|
||||
|
||||
if (lo_tail)
|
||||
{
|
||||
lo_tail.next = null;
|
||||
@@ -544,7 +551,7 @@ fn void LinkedHashSet.resize(&set, usz new_capacity) @private
|
||||
new_table[i + old_capacity] = hi_head;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
set.free_internal(old_table.ptr);
|
||||
}
|
||||
|
||||
@@ -601,16 +608,16 @@ fn void LinkedHashSet.free_internal(&set, void* ptr) @inline @private
|
||||
|
||||
fn void LinkedHashSet.create_entry(&set, uint hash, Value value, int bucket_index) @private
|
||||
{
|
||||
LinkedEntry* entry = allocator::new(set.allocator, LinkedEntry, {
|
||||
.hash = hash,
|
||||
.value = value,
|
||||
LinkedEntry* entry = allocator::new(set.allocator, LinkedEntry, {
|
||||
.hash = hash,
|
||||
.value = value,
|
||||
.next = set.table[bucket_index],
|
||||
.before = set.tail,
|
||||
.after = null
|
||||
});
|
||||
|
||||
|
||||
set.table[bucket_index] = entry;
|
||||
|
||||
|
||||
// Update linked list
|
||||
if (set.tail)
|
||||
{
|
||||
@@ -622,19 +629,19 @@ fn void LinkedHashSet.create_entry(&set, uint hash, Value value, int bucket_inde
|
||||
set.head = entry;
|
||||
}
|
||||
set.tail = entry;
|
||||
|
||||
|
||||
set.count++;
|
||||
}
|
||||
|
||||
fn bool LinkedHashSet.remove_entry_for_value(&set, Value value) @private
|
||||
{
|
||||
if (!set.count) return false;
|
||||
|
||||
|
||||
uint hash = rehash(value.hash());
|
||||
uint i = index_for(hash, set.table.len);
|
||||
LinkedEntry* prev = null;
|
||||
LinkedEntry* e = set.table[i];
|
||||
|
||||
|
||||
while (e)
|
||||
{
|
||||
if (e.hash == hash && equals(value, e.value))
|
||||
@@ -647,7 +654,7 @@ fn bool LinkedHashSet.remove_entry_for_value(&set, Value value) @private
|
||||
{
|
||||
set.table[i] = e.next;
|
||||
}
|
||||
|
||||
|
||||
if (e.before)
|
||||
{
|
||||
e.before.after = e.after;
|
||||
@@ -656,7 +663,7 @@ fn bool LinkedHashSet.remove_entry_for_value(&set, Value value) @private
|
||||
{
|
||||
set.head = e.after;
|
||||
}
|
||||
|
||||
|
||||
if (e.after)
|
||||
{
|
||||
e.after.before = e.before;
|
||||
@@ -665,7 +672,7 @@ fn bool LinkedHashSet.remove_entry_for_value(&set, Value value) @private
|
||||
{
|
||||
set.tail = e.before;
|
||||
}
|
||||
|
||||
|
||||
set.count--;
|
||||
set.free_entry(e);
|
||||
return true;
|
||||
@@ -715,9 +722,9 @@ fn bool LinkedHashSetIterator.has_next(&self)
|
||||
return self.current && self.current.after != null;
|
||||
}
|
||||
|
||||
fn usz LinkedHashSetIterator.len(&self) @operator(len)
|
||||
fn usz LinkedHashSetIterator.len(&self) @operator(len)
|
||||
{
|
||||
return self.set.count;
|
||||
}
|
||||
|
||||
int dummy @local;
|
||||
int dummy @local;
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
// Use of self source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::collections::linkedlist{Type};
|
||||
import std::io;
|
||||
|
||||
const ELEMENT_IS_EQUATABLE = types::is_equatable_type(Type);
|
||||
|
||||
struct Node @private
|
||||
struct Node
|
||||
{
|
||||
Node *next;
|
||||
Node *prev;
|
||||
Node* next;
|
||||
Node* prev;
|
||||
Type value;
|
||||
}
|
||||
|
||||
@@ -16,8 +17,31 @@ struct LinkedList
|
||||
{
|
||||
Allocator allocator;
|
||||
usz size;
|
||||
Node *_first;
|
||||
Node *_last;
|
||||
Node* _first;
|
||||
Node* _last;
|
||||
}
|
||||
|
||||
fn usz? LinkedList.to_format(&self, Formatter* f) @dynamic
|
||||
{
|
||||
usz len = f.print("{ ")!;
|
||||
for (Node* node = self._first; node != null; node.next)
|
||||
{
|
||||
len += f.printf(node.next ? "%s, " : "s", node.value)!;
|
||||
}
|
||||
return len + f.print(" }");
|
||||
}
|
||||
|
||||
macro LinkedList @new(Allocator allocator, Type[] #default_values = {})
|
||||
{
|
||||
LinkedList new_list;
|
||||
new_list.init(allocator);
|
||||
new_list.push_all(#default_values);
|
||||
return new_list;
|
||||
}
|
||||
|
||||
macro LinkedList @tnew(Type[] #default_values = {})
|
||||
{
|
||||
return @new(tmem, #default_values);
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -68,6 +92,11 @@ fn void LinkedList.push_front(&self, Type value)
|
||||
self.size++;
|
||||
}
|
||||
|
||||
fn void LinkedList.push_front_all(&self, Type[] value)
|
||||
{
|
||||
foreach_r (v : value) self.push_front(v);
|
||||
}
|
||||
|
||||
fn void LinkedList.push(&self, Type value)
|
||||
{
|
||||
Node *last = self._last;
|
||||
@@ -85,6 +114,11 @@ fn void LinkedList.push(&self, Type value)
|
||||
self.size++;
|
||||
}
|
||||
|
||||
fn void LinkedList.push_all(&self, Type[] value)
|
||||
{
|
||||
foreach (v : value) self.push(v);
|
||||
}
|
||||
|
||||
fn Type? LinkedList.peek(&self) => self.first() @inline;
|
||||
fn Type? LinkedList.peek_last(&self) => self.last() @inline;
|
||||
|
||||
@@ -133,6 +167,7 @@ macro Node* LinkedList.node_at_index(&self, usz index)
|
||||
while (index--) node = node.next;
|
||||
return node;
|
||||
}
|
||||
|
||||
<*
|
||||
@require index < self.size
|
||||
*>
|
||||
@@ -141,6 +176,14 @@ fn Type LinkedList.get(&self, usz index)
|
||||
return self.node_at_index(index).value;
|
||||
}
|
||||
|
||||
<*
|
||||
@require index < self.size
|
||||
*>
|
||||
fn Type* LinkedList.get_ref(&self, usz index)
|
||||
{
|
||||
return &self.node_at_index(index).value;
|
||||
}
|
||||
|
||||
<*
|
||||
@require index < self.size
|
||||
*>
|
||||
@@ -149,6 +192,26 @@ fn void LinkedList.set(&self, usz index, Type element)
|
||||
self.node_at_index(index).value = element;
|
||||
}
|
||||
|
||||
fn usz? LinkedList.index_of(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
|
||||
{
|
||||
for (Node* node = self._first, usz i = 0; node != null; node = node.next, ++i)
|
||||
{
|
||||
if (node.value == t) return i;
|
||||
}
|
||||
return NOT_FOUND?;
|
||||
}
|
||||
|
||||
fn usz? LinkedList.rindex_of(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
|
||||
{
|
||||
for (Node* node = self._last, usz i = self.size - 1; node != null; node = node.prev, --i)
|
||||
{
|
||||
if (node.value == t) return i;
|
||||
if (i == 0) break;
|
||||
}
|
||||
return NOT_FOUND?;
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
@require index < self.size
|
||||
*>
|
||||
@@ -334,3 +397,69 @@ fn void LinkedList.unlink(&self, Node* x) @private
|
||||
self.free_node(x);
|
||||
self.size--;
|
||||
}
|
||||
|
||||
|
||||
macro bool LinkedList.eq(&self, other) @operator(==) @if(ELEMENT_IS_EQUATABLE)
|
||||
{
|
||||
Node* node1 = self._first;
|
||||
Node* node2 = other._first;
|
||||
while (true)
|
||||
{
|
||||
if (!node1) return node2 == null;
|
||||
if (!node2) return false;
|
||||
if (node1.value != node2.value) return false;
|
||||
node1 = node1.next;
|
||||
node2 = node2.next;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
fn LinkedListArrayView LinkedList.array_view(&self)
|
||||
{
|
||||
return { .list = self, .current_node = self._first };
|
||||
}
|
||||
|
||||
|
||||
struct LinkedListArrayView
|
||||
{
|
||||
LinkedList* list;
|
||||
Node* current_node;
|
||||
usz current_index;
|
||||
}
|
||||
|
||||
fn usz LinkedListArrayView.len(&self) @operator(len) => self.list.size;
|
||||
|
||||
<*
|
||||
@require index < self.list.size
|
||||
*>
|
||||
fn Type LinkedListArrayView.get(&self, usz index) @operator([])
|
||||
{
|
||||
return *self.get_ref(index);
|
||||
}
|
||||
|
||||
<*
|
||||
@require index < self.list.size
|
||||
*>
|
||||
fn Type* LinkedListArrayView.get_ref(&self, usz index) @operator(&[])
|
||||
{
|
||||
if (index == self.list.size - 1)
|
||||
{
|
||||
self.current_node = self.list._last;
|
||||
self.current_index = index;
|
||||
}
|
||||
|
||||
while (self.current_index != index)
|
||||
{
|
||||
switch
|
||||
{
|
||||
case index < self.current_index: // reverse iteration
|
||||
self.current_node = self.current_node.prev;
|
||||
self.current_index--;
|
||||
case index > self.current_index:
|
||||
self.current_node = self.current_node.next;
|
||||
self.current_index++;
|
||||
}
|
||||
}
|
||||
|
||||
return &self.current_node.value;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ const Allocator LIST_HEAP_ALLOCATOR = (Allocator)&dummy;
|
||||
|
||||
const List ONHEAP = { .allocator = LIST_HEAP_ALLOCATOR };
|
||||
|
||||
macro type_is_overaligned() => Type.alignof > mem::DEFAULT_MEM_ALIGNMENT;
|
||||
macro bool type_is_overaligned() => Type.alignof > mem::DEFAULT_MEM_ALIGNMENT;
|
||||
|
||||
struct List (Printable)
|
||||
{
|
||||
@@ -532,7 +532,8 @@ fn bool List.remove_first_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
|
||||
fn usz List.remove_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
|
||||
{
|
||||
usz old_size = self.size;
|
||||
defer {
|
||||
defer
|
||||
{
|
||||
if (old_size != self.size) self._update_size_change(old_size, self.size);
|
||||
}
|
||||
return list_common::list_remove_item(self, value);
|
||||
|
||||
@@ -7,10 +7,12 @@ const uint PIXELS_MAX = 400000000;
|
||||
Purely informative. It will be saved to the file header,
|
||||
but does not affect how chunks are en-/decoded.
|
||||
*>
|
||||
enum QOIColorspace : char (char id)
|
||||
enum QOIColorspace : const char
|
||||
{
|
||||
SRGB = 0, // sRGB with linear alpha
|
||||
LINEAR = 1 // all channels linear
|
||||
<* sRGB with linear alpha *>
|
||||
SRGB = 0,
|
||||
<* all channels linear *>
|
||||
LINEAR = 1
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -19,7 +21,7 @@ enum QOIColorspace : char (char id)
|
||||
AUTO can be used when decoding to automatically determine
|
||||
the channels from the file's header.
|
||||
*>
|
||||
enum QOIChannels : char (char id)
|
||||
enum QOIChannels : const inline char
|
||||
{
|
||||
AUTO = 0,
|
||||
RGB = 3,
|
||||
@@ -132,7 +134,7 @@ fn char[]? encode(Allocator allocator, char[] input, QOIDesc* desc) @nodiscard
|
||||
if (pixels > PIXELS_MAX) return TOO_MANY_PIXELS?;
|
||||
|
||||
// check input data size
|
||||
uint image_size = pixels * desc.channels.id;
|
||||
uint image_size = pixels * desc.channels;
|
||||
if (image_size != input.len) return INVALID_DATA?;
|
||||
|
||||
// allocate memory for encoded data (output)
|
||||
@@ -146,13 +148,13 @@ fn char[]? encode(Allocator allocator, char[] input, QOIDesc* desc) @nodiscard
|
||||
.be_magic = bswap('qoif'),
|
||||
.be_width = bswap(desc.width),
|
||||
.be_height = bswap(desc.height),
|
||||
.channels = desc.channels.id,
|
||||
.colorspace = desc.colorspace.id
|
||||
.channels = desc.channels,
|
||||
.colorspace = desc.colorspace
|
||||
};
|
||||
|
||||
uint pos = Header.sizeof; // Current position in output
|
||||
uint loc; // Current position in image (top-left corner)
|
||||
uint loc_end = image_size - desc.channels.id; // End of image data
|
||||
uint loc_end = image_size - desc.channels; // End of image data
|
||||
char run_length = 0; // Length of the current run
|
||||
|
||||
Pixel[64] palette; // Zero-initialized by default
|
||||
@@ -163,7 +165,7 @@ fn char[]? encode(Allocator allocator, char[] input, QOIDesc* desc) @nodiscard
|
||||
ichar[<3>] luma; // ...and luma
|
||||
|
||||
// write chunks
|
||||
for (loc = 0; loc < image_size; loc += desc.channels.id)
|
||||
for (loc = 0; loc < image_size; loc += desc.channels)
|
||||
{
|
||||
// set previous pixel
|
||||
prev = p;
|
||||
@@ -292,8 +294,8 @@ fn char[]? decode(Allocator allocator, char[] data, QOIDesc* desc, QOIChannels c
|
||||
// copy header data to desc
|
||||
desc.width = bswap(header.be_width);
|
||||
desc.height = bswap(header.be_height);
|
||||
desc.channels = @enumcast(QOIChannels, header.channels)!; // Rethrow if invalid
|
||||
desc.colorspace = @enumcast(QOIColorspace, header.colorspace)!; // Rethrow if invalid
|
||||
desc.channels = header.channels;
|
||||
desc.colorspace = header.colorspace;
|
||||
if (desc.channels == AUTO) return INVALID_DATA?; // Channels must be specified in the header
|
||||
|
||||
// check width and height
|
||||
@@ -314,11 +316,11 @@ fn char[]? decode(Allocator allocator, char[] data, QOIDesc* desc, QOIChannels c
|
||||
if (channels == AUTO) channels = desc.channels;
|
||||
|
||||
// allocate memory for image data
|
||||
usz image_size = (usz)pixels * channels.id;
|
||||
usz image_size = (usz)pixels * channels;
|
||||
char[] image = allocator::alloc_array(allocator, char, image_size);
|
||||
defer catch allocator::free(allocator, image);
|
||||
|
||||
for (loc = 0; loc < image_size; loc += channels.id)
|
||||
for (loc = 0; loc < image_size; loc += channels)
|
||||
{
|
||||
// get chunk tag
|
||||
tag = data[pos];
|
||||
@@ -391,31 +393,22 @@ const OP_RUN = 0b11;
|
||||
|
||||
struct Header @packed
|
||||
{
|
||||
uint be_magic; // magic bytes "qoif"
|
||||
uint be_width; // image width in pixels (BE)
|
||||
uint be_height; // image height in pixels (BE)
|
||||
<* magic bytes "qoif" *>
|
||||
uint be_magic;
|
||||
<* image width in pixels (BE) *>
|
||||
uint be_width;
|
||||
<* image height in pixels (BE) *>
|
||||
uint be_height;
|
||||
|
||||
// informative fields
|
||||
char channels; // 3 = RGB, 4 = RGB
|
||||
char colorspace; // 0 = sRGB with linear alpha, 1 = all channels linear
|
||||
<* 3 = RGB, 4 = RGB *>
|
||||
QOIChannels channels;
|
||||
<* 0 = sRGB with linear alpha, 1 = all channels linear *>
|
||||
QOIColorspace colorspace;
|
||||
}
|
||||
|
||||
const char[*] END_OF_STREAM = {0, 0, 0, 0, 0, 0, 0, 1};
|
||||
|
||||
// inefficient, but it's only run once at a time
|
||||
|
||||
<*
|
||||
@return? INVALID_DATA
|
||||
*>
|
||||
macro @enumcast($Type, raw)
|
||||
{
|
||||
foreach (value : $Type.values)
|
||||
{
|
||||
if (value.id == raw) return value;
|
||||
}
|
||||
return INVALID_DATA?;
|
||||
}
|
||||
|
||||
typedef Pixel = inline char[<4>];
|
||||
macro char Pixel.hash(Pixel p)
|
||||
{
|
||||
|
||||
@@ -13,7 +13,7 @@ import std::math;
|
||||
|
||||
The advantage over the BackedArenaAllocator, is that when allocating beyond the first "page", it will
|
||||
retain the characteristics of an arena allocator (allocating a large piece of memory then handing off
|
||||
memory from that memory), wheras the BackedArenaAllocator will have heap allocator characteristics.
|
||||
memory from that memory), whereas the BackedArenaAllocator will have heap allocator characteristics.
|
||||
*>
|
||||
struct DynamicArenaAllocator (Allocator)
|
||||
{
|
||||
|
||||
@@ -19,7 +19,7 @@ alias AllocMap = HashMap { uptr, Allocation };
|
||||
// It tracks allocations using a hash map but
|
||||
// is not compatible with allocators that uses mark()
|
||||
//
|
||||
// It is also embarassingly single-threaded, so
|
||||
// It is also embarrassingly single-threaded, so
|
||||
// do not use it to track allocations that cross threads.
|
||||
|
||||
struct TrackingAllocator (Allocator)
|
||||
@@ -216,4 +216,4 @@ fn void? TrackingAllocator.fprint_report(&self, OutStream out) => @pool()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,9 +22,12 @@ struct Vmem (Allocator)
|
||||
|
||||
bitstruct VmemOptions : int
|
||||
{
|
||||
bool shrink_on_reset; // Release memory on reset
|
||||
bool protect_unused_pages; // Protect unused pages on reset
|
||||
bool scratch_released_data; // Overwrite released data with 0xAA
|
||||
<* Release memory on reset *>
|
||||
bool shrink_on_reset;
|
||||
<* Protect unused pages on reset *>
|
||||
bool protect_unused_pages;
|
||||
<* Overwrite released data with 0xAA *>
|
||||
bool scratch_released_data;
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -38,11 +41,11 @@ bitstruct VmemOptions : int
|
||||
fn void? Vmem.init(&self, usz preferred_size, usz reserve_page_size = 0, VmemOptions options = { true, true, env::COMPILER_SAFE_MODE }, usz min_size = 0)
|
||||
{
|
||||
static usz page_size = 0;
|
||||
if (!page_size) page_size = mem::os_pagesize();
|
||||
if (page_size < reserve_page_size) page_size = reserve_page_size;
|
||||
preferred_size = mem::aligned_offset(preferred_size, page_size);
|
||||
if (!min_size) min_size = max(preferred_size / 1024, 1);
|
||||
VirtualMemory? memory = mem::OUT_OF_MEMORY?;
|
||||
if (!page_size) page_size = mem::os_pagesize();
|
||||
if (page_size < reserve_page_size) page_size = reserve_page_size;
|
||||
preferred_size = mem::aligned_offset(preferred_size, page_size);
|
||||
if (!min_size) min_size = max(preferred_size / 1024, 1);
|
||||
VirtualMemory? memory = mem::OUT_OF_MEMORY?;
|
||||
while (preferred_size >= min_size)
|
||||
{
|
||||
memory = vm::virtual_alloc(preferred_size, PROTECTED);
|
||||
|
||||
@@ -3,7 +3,7 @@ import std::collections::pair, std::io;
|
||||
|
||||
<*
|
||||
Returns true if the array contains at least one element, else false
|
||||
|
||||
|
||||
@param [in] array
|
||||
@param [in] element
|
||||
@require $kindof(array) == SLICE || $kindof(array) == ARRAY
|
||||
@@ -15,13 +15,13 @@ macro bool contains(array, element)
|
||||
{
|
||||
if (*item == element) return true;
|
||||
}
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Return the first index of element found in the array, searching from the start.
|
||||
|
||||
|
||||
@param [in] array
|
||||
@param [in] element
|
||||
@require $kindof(array) == SLICE || $kindof(array) == ARRAY
|
||||
@@ -29,7 +29,7 @@ macro bool contains(array, element)
|
||||
@return "the first index of the element"
|
||||
@return? NOT_FOUND
|
||||
*>
|
||||
macro index_of(array, element)
|
||||
macro usz? index_of(array, element)
|
||||
{
|
||||
foreach (i, &e : array)
|
||||
{
|
||||
@@ -63,13 +63,13 @@ macro slice2d(array_ptr, x = 0, xlen = 0, y = 0, ylen = 0)
|
||||
|
||||
<*
|
||||
Return the first index of element found in the array, searching in reverse from the end.
|
||||
|
||||
|
||||
@param [in] array
|
||||
@param [in] element
|
||||
@return "the last index of the element"
|
||||
@return? NOT_FOUND
|
||||
*>
|
||||
macro rindex_of(array, element)
|
||||
macro usz? rindex_of(array, element)
|
||||
{
|
||||
foreach_r (i, &e : array)
|
||||
{
|
||||
@@ -142,7 +142,7 @@ macro tconcat(arr1, arr2) @nodiscard => concat(tmem, arr1, arr2);
|
||||
|
||||
@param [in] array
|
||||
@param identity
|
||||
@param #operation : "The reduction/folding labmda function or function pointer to apply."
|
||||
@param #operation : "The reduction/folding lambda function or function pointer to apply."
|
||||
|
||||
@require @is_valid_list(array) : "Expected a valid list"
|
||||
@require $defined($typefrom(@reduce_fn(array, identity)) $func = #operation) : "Invalid lambda or function pointer type"
|
||||
@@ -216,7 +216,7 @@ macro @product(array, identity_value = 1)
|
||||
*>
|
||||
macro usz[] @indices_of(Allocator allocator, array, #predicate)
|
||||
{
|
||||
usz[] results = allocator::new_array(allocator, usz, find_len(array));
|
||||
usz[] results = allocator::new_array(allocator, usz, lengthof(array));
|
||||
usz matches;
|
||||
|
||||
$typefrom(@predicate_fn(array)) $predicate = #predicate;
|
||||
@@ -387,7 +387,6 @@ macro bool @all(array, #predicate)
|
||||
@require @is_valid_list(left) &&& @is_valid_list(right) : "Left and right sides must be integer indexable"
|
||||
@require @is_valid_operation(left, right, ...#operation) : "The operator must take two parameters matching the elements of the left and right side"
|
||||
@require @is_valid_fill(left, right, ...fill_with) : "The specified fill value does not match either the left or the right array's underlying type."
|
||||
|
||||
*>
|
||||
macro @zip(Allocator allocator, left, right, #operation = ..., fill_with = ...) @nodiscard
|
||||
{
|
||||
@@ -400,8 +399,8 @@ macro @zip(Allocator allocator, left, right, #operation = ..., fill_with = ...)
|
||||
$Type = $typeof(#operation).returns;
|
||||
$endif
|
||||
|
||||
usz left_len = find_len(left);
|
||||
usz right_len = find_len(right);
|
||||
usz left_len = lengthof(left);
|
||||
usz right_len = lengthof(right);
|
||||
|
||||
$LeftType left_fill;
|
||||
$RightType right_fill;
|
||||
@@ -460,7 +459,6 @@ macro @zip(Allocator allocator, left, right, #operation = ..., fill_with = ...)
|
||||
@require @is_valid_list(left) &&& @is_valid_list(right) : "Left and right sides must be integer indexable"
|
||||
@require @is_valid_operation(left, right, ...#operation) : "The operator must take two parameters matching the elements of the left and right side"
|
||||
@require @is_valid_fill(left, right, ...fill_with) : "The specified fill value does not match either the left or the right array's underlying type."
|
||||
|
||||
*>
|
||||
macro @tzip(left, right, #operation = ..., fill_with = ...) @nodiscard
|
||||
{
|
||||
@@ -495,7 +493,7 @@ macro @tzip(left, right, #operation = ..., fill_with = ...) @nodiscard
|
||||
|
||||
@require @is_valid_list(left) : "Expected a valid list"
|
||||
@require @is_valid_list(right) : "Expected a valid list"
|
||||
@require find_len(right) >= find_len(left) : `Right side length must be >= the destination (left) side length; consider using a sub-array of data for the assignment.`
|
||||
@require lengthof(right) >= lengthof(left) : `Right side length must be >= the destination (left) side length; consider using a sub-array of data for the assignment.`
|
||||
@require $defined($typefrom(@zip_into_fn(left, right)) x = #operation) : "The functor must use the same types as the `left` and `right` inputs, and return a value of the `left` type."
|
||||
*>
|
||||
macro @zip_into(left, right, #operation)
|
||||
@@ -539,7 +537,7 @@ macro bool @is_valid_operation(#left, #right, #operation = ...) @const
|
||||
|
||||
macro bool @is_valid_list(#expr) @const
|
||||
{
|
||||
return $defined(#expr[0]) &&& ($defined(#expr.len) ||| $defined(#expr.len()));
|
||||
return $defined(#expr[0], lengthof(#expr));
|
||||
}
|
||||
|
||||
macro bool @is_valid_fill(left, right, fill_with = ...)
|
||||
@@ -547,11 +545,9 @@ macro bool @is_valid_fill(left, right, fill_with = ...)
|
||||
$if !$defined(fill_with):
|
||||
return true;
|
||||
$else
|
||||
usz left_len = $defined(left.len()) ??? left.len() : left.len;
|
||||
usz right_len = $defined(right.len()) ??? right.len() : right.len;
|
||||
usz left_len = lengthof(left);
|
||||
usz right_len = lengthof(right);
|
||||
if (left_len == right_len) return true;
|
||||
return left_len > right_len ? $defined(($typeof(right[0]))fill_with) : $defined(($typeof(left[0]))fill_with);
|
||||
$endif
|
||||
}
|
||||
|
||||
macro usz find_len(list) => $defined(list.len()) ??? list.len() : list.len;
|
||||
@@ -119,7 +119,7 @@ macro write(x, bytes, $Type)
|
||||
*($typeof(x)*)s.ptr = bitcast(x, $Type).val;
|
||||
}
|
||||
|
||||
macro is_bitorder($Type)
|
||||
macro bool is_bitorder($Type)
|
||||
{
|
||||
$switch $Type:
|
||||
$case UShortLE:
|
||||
@@ -181,4 +181,4 @@ macro bool @is_arrayptr_or_slice_of_char(#bytes) @const
|
||||
$default:
|
||||
return false;
|
||||
$endswitch
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import libc, std::hash, std::io, std::os::backtrace;
|
||||
|
||||
<*
|
||||
EMPTY_MACRO_SLOT is a value used for implementing optional arguments for macros in an efficient
|
||||
way. It relies on the fact that distinct types are not implicitly convertable.
|
||||
way. It relies on the fact that distinct types are not implicitly convertible.
|
||||
|
||||
You can use `@is_empty_macro_slot()` and `@is_valid_macro_slot()` to figure out whether
|
||||
the argument has been used or not.
|
||||
@@ -27,8 +27,8 @@ macro foo(a, #b = EMPTY_MACRO_SLOT)
|
||||
const EmptySlot EMPTY_MACRO_SLOT @builtin = null;
|
||||
|
||||
typedef EmptySlot = void*;
|
||||
macro @is_empty_macro_slot(#arg) @const @builtin => $typeof(#arg) == EmptySlot;
|
||||
macro @is_valid_macro_slot(#arg) @const @builtin => $typeof(#arg) != EmptySlot;
|
||||
macro bool @is_empty_macro_slot(#arg) @const @builtin => $typeof(#arg) == EmptySlot;
|
||||
macro bool @is_valid_macro_slot(#arg) @const @builtin => $typeof(#arg) != EmptySlot;
|
||||
|
||||
<*
|
||||
Returns a random value at compile time.
|
||||
@@ -108,7 +108,7 @@ macro anycast(any v, $Type) @builtin
|
||||
return ($Type*)v.ptr;
|
||||
}
|
||||
|
||||
macro bool @assignable_to(#foo, $Type) @const @builtin @deprecated("use '$define($Type x = #foo)'") => $defined(*&&($Type){} = #foo);
|
||||
macro bool @assignable_to(#foo, $Type) @const @builtin @deprecated("use '$defined($Type x = #foo)'") => $defined(*&&($Type){} = #foo);
|
||||
|
||||
macro @addr(#val) @builtin
|
||||
{
|
||||
@@ -135,11 +135,14 @@ macro bool @typeis(#value, $Type) @const @builtin @deprecated("Use `$typeof(#val
|
||||
}
|
||||
|
||||
|
||||
fn bool print_backtrace(String message, int backtraces_to_ignore) @if (env::NATIVE_STACKTRACE) => @stack_mem(0x1100; Allocator smem)
|
||||
fn bool print_backtrace(String message, int backtraces_to_ignore, void *added_backtrace = null) @if (env::NATIVE_STACKTRACE) => @stack_mem(0x1100; Allocator smem)
|
||||
{
|
||||
void*[256] buffer;
|
||||
void*[] backtraces = backtrace::capture_current(&buffer);
|
||||
backtraces_to_ignore++;
|
||||
if (added_backtrace)
|
||||
{
|
||||
backtraces[++backtraces_to_ignore] = added_backtrace;
|
||||
}
|
||||
@stack_mem(2048; Allocator mem)
|
||||
{
|
||||
BacktraceList? backtrace = backtrace::symbolize_backtrace(mem, backtraces);
|
||||
@@ -430,7 +433,7 @@ macro swizzle2(v, v2, ...) @builtin
|
||||
@require types::is_int($typeof($value)) : "Input value must be an integer"
|
||||
@require $sizeof($value) * 8 <= 128 : "Input value must be 128 bits wide or lower"
|
||||
*>
|
||||
macro @clz($value) @builtin @const
|
||||
macro uint @clz($value) @builtin @const
|
||||
{
|
||||
$if $value == 0:
|
||||
return $sizeof($value) * 8; // it's all leading zeroes
|
||||
@@ -500,7 +503,7 @@ macro void? @try(#v, #expr) @builtin @maydiscard
|
||||
{
|
||||
char[] data;
|
||||
// Read until end of file
|
||||
if (@try_catch(data, load_line(), io::EOF)) break;
|
||||
if (@try_catch(data, load_line(), io::EOF)!) break;
|
||||
.. use data ..
|
||||
}
|
||||
|
||||
@@ -547,6 +550,27 @@ macro isz @str_find(String $string, String $needle) @builtin => $$str_find($stri
|
||||
macro String @str_upper(String $str) @builtin => $$str_upper($str);
|
||||
macro String @str_lower(String $str) @builtin => $$str_lower($str);
|
||||
macro uint @str_hash(String $str) @builtin => $$str_hash($str);
|
||||
macro String @str_pascalcase(String $str) @builtin => $$str_pascalcase($str);
|
||||
macro String @str_snakecase(String $str) @builtin => $$str_snakecase($str);
|
||||
macro String @str_camelcase(String $str) @builtin => @str_capitalize($$str_pascalcase($str));
|
||||
macro String @str_constantcase(String $str) @builtin => @str_upper($$str_snakecase($str));
|
||||
macro String @str_replace(String $str, String $pattern, String $replace, uint $limit = 0) @builtin => $$str_replace($str, $pattern, $replace, $limit);
|
||||
macro String @str_capitalize(String $str) @builtin
|
||||
{
|
||||
$switch $str.len:
|
||||
$case 0: return $str;
|
||||
$case 1: return $$str_upper($str);
|
||||
$default: return $$str_upper($str[0:1]) +++ $str[1..];
|
||||
$endswitch
|
||||
}
|
||||
macro String @str_uncapitalize(String $str) @builtin
|
||||
{
|
||||
$switch $str.len:
|
||||
$case 0: return $str;
|
||||
$case 1: return $$str_lower($str);
|
||||
$default: return $$str_lower($str[0:1]) +++ $str[1..];
|
||||
$endswitch
|
||||
}
|
||||
|
||||
macro @generic_hash_core(h, value)
|
||||
{
|
||||
@@ -556,7 +580,7 @@ macro @generic_hash_core(h, value)
|
||||
return h;
|
||||
}
|
||||
|
||||
macro @generic_hash(value)
|
||||
macro uint @generic_hash(value)
|
||||
{
|
||||
uint h = @generic_hash_core((uint)0x3efd4391, value);
|
||||
$for var $cnt = 4; $cnt < $sizeof(value); $cnt += 4:
|
||||
@@ -920,23 +944,20 @@ macro void* get_returnaddress(int n)
|
||||
}
|
||||
|
||||
module std::core::builtin @if((env::LINUX || env::ANDROID || env::DARWIN) && env::COMPILER_SAFE_MODE && env::DEBUG_SYMBOLS);
|
||||
import libc, std::io;
|
||||
import libc, std::io, std::os::posix;
|
||||
|
||||
fn void sig_panic(String message)
|
||||
{
|
||||
default_panic(message, "???", "???", 0);
|
||||
}
|
||||
|
||||
SignalFunction old_bus_error;
|
||||
SignalFunction old_segmentation_fault;
|
||||
|
||||
fn void sig_bus_error(CInt i)
|
||||
fn void sig_bus_error(CInt i, void* info, void* context)
|
||||
{
|
||||
$if !env::NATIVE_STACKTRACE:
|
||||
sig_panic("Illegal memory access.");
|
||||
$else
|
||||
$if $defined(io::stderr):
|
||||
if (!print_backtrace("Illegal memory access.", 1))
|
||||
if (!print_backtrace("Illegal memory access.", 2, posix::stack_instruction(context)))
|
||||
{
|
||||
io::eprintn("\nERROR: 'Illegal memory access'.");
|
||||
}
|
||||
@@ -945,13 +966,13 @@ fn void sig_bus_error(CInt i)
|
||||
$$trap();
|
||||
}
|
||||
|
||||
fn void sig_segmentation_fault(CInt i)
|
||||
fn void sig_segmentation_fault(CInt i, void* p1, void* context)
|
||||
{
|
||||
$if !env::NATIVE_STACKTRACE:
|
||||
sig_panic("Out of bounds memory access.");
|
||||
$else
|
||||
$if $defined(io::stderr):
|
||||
if (!print_backtrace("Out of bounds memory access.", 1))
|
||||
if (!print_backtrace("Out of bounds memory access.", 2, posix::stack_instruction(context)))
|
||||
{
|
||||
io::eprintn("\nERROR: Memory error without backtrace, possible stack overflow.");
|
||||
}
|
||||
@@ -960,17 +981,12 @@ fn void sig_segmentation_fault(CInt i)
|
||||
$$trap();
|
||||
}
|
||||
|
||||
fn void install_signal_handler(CInt signal, SignalFunction func) @local
|
||||
{
|
||||
SignalFunction old = libc::signal(signal, func);
|
||||
// Restore
|
||||
if ((iptr)old > 1024) libc::signal(signal, old);
|
||||
}
|
||||
|
||||
|
||||
// Clean this up
|
||||
fn void install_signal_handlers() @init(101) @local @if(env::BACKTRACE)
|
||||
{
|
||||
install_signal_handler(libc::SIGBUS, &sig_bus_error);
|
||||
install_signal_handler(libc::SIGSEGV, &sig_segmentation_fault);
|
||||
posix::install_signal_handler(libc::SIGBUS, &sig_bus_error);
|
||||
posix::install_signal_handler(libc::SIGSEGV, &sig_segmentation_fault);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ module std::core::builtin;
|
||||
<*
|
||||
@require types::@comparable_value(a) && types::@comparable_value(b)
|
||||
*>
|
||||
macro less(a, b) @builtin
|
||||
macro bool less(a, b) @builtin
|
||||
{
|
||||
$switch:
|
||||
$case $defined(a.less):
|
||||
@@ -21,7 +21,7 @@ macro less(a, b) @builtin
|
||||
<*
|
||||
@require types::@comparable_value(a) && types::@comparable_value(b)
|
||||
*>
|
||||
macro less_eq(a, b) @builtin
|
||||
macro bool less_eq(a, b) @builtin
|
||||
{
|
||||
$switch:
|
||||
$case $defined(a.less):
|
||||
@@ -36,7 +36,7 @@ macro less_eq(a, b) @builtin
|
||||
<*
|
||||
@require types::@comparable_value(a) && types::@comparable_value(b)
|
||||
*>
|
||||
macro greater(a, b) @builtin
|
||||
macro bool greater(a, b) @builtin
|
||||
{
|
||||
$switch:
|
||||
$case $defined(a.less):
|
||||
@@ -65,7 +65,7 @@ macro int compare_to(a, b) @builtin
|
||||
<*
|
||||
@require types::@comparable_value(a) && types::@comparable_value(b)
|
||||
*>
|
||||
macro greater_eq(a, b) @builtin
|
||||
macro bool greater_eq(a, b) @builtin
|
||||
{
|
||||
$switch:
|
||||
$case $defined(a.less):
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::core::cinterop;
|
||||
import std::core::env;
|
||||
|
||||
|
||||
const C_INT_SIZE = $$C_INT_SIZE;
|
||||
const C_LONG_SIZE = $$C_LONG_SIZE;
|
||||
@@ -59,3 +61,57 @@ macro typeid unsigned_int_from_bitsize(usz $bitsize) @private
|
||||
$default: $error("Invalid bitsize");
|
||||
$endswitch
|
||||
}
|
||||
|
||||
const USE_STACK_VALIST = env::ARCH_32_BIT || env::WIN32 || (env::DARWIN && env::AARCH64);
|
||||
module std::core::cinterop @if(USE_STACK_VALIST);
|
||||
|
||||
typedef CVaList = void*;
|
||||
macro CVaList.next(&self, $Type)
|
||||
{
|
||||
void *ptr = mem::aligned_pointer((void*)*self, max($Type.alignof, 8));
|
||||
defer *self = (CVaList)(ptr + 1);
|
||||
return *($Type*)ptr;
|
||||
}
|
||||
|
||||
module std::core::cinterop @if(env::X86_64 && !env::WIN32);
|
||||
|
||||
struct CVaListData
|
||||
{
|
||||
uint gp_offset;
|
||||
uint fp_offset;
|
||||
void *overflow_arg_area;
|
||||
void *reg_save_area;
|
||||
}
|
||||
|
||||
typedef CVaList = CVaListData*;
|
||||
|
||||
macro CVaList.next(self, $Type)
|
||||
{
|
||||
CVaListData* data = (CVaListData*)self;
|
||||
$switch:
|
||||
$case $Type.kindof == FLOAT ||| ($Type.kindof == VECTOR && $Type.sizeof <= 16):
|
||||
var $LoadType = $Type.sizeof < 8 ? double : $Type;
|
||||
if (data.fp_offset < 6 * 8 + 8 * 16 )
|
||||
{
|
||||
defer data.fp_offset += (uint)mem::aligned_offset($Type.sizeof, 16);
|
||||
return ($Type)*($LoadType*)(data.reg_save_area + data.fp_offset);
|
||||
}
|
||||
void* ptr = mem::aligned_pointer(data.overflow_arg_area, max(8, $Type.alignof));
|
||||
defer data.overflow_arg_area = ptr + $Type.sizeof;
|
||||
return ($Type)*($LoadType*)ptr;
|
||||
$case $Type.kindof == SIGNED_INT || $Type.kindof == UNSIGNED_INT:
|
||||
var $LoadType = $Type.sizeof < 4 ? int : $Type;
|
||||
if (data.gp_offset < 6 * 8 && $Type.sizeof <= 8)
|
||||
{
|
||||
defer data.gp_offset += (uint)mem::aligned_offset($Type.sizeof, 8);
|
||||
return ($Type)*($LoadType*)(data.reg_save_area + data.gp_offset);
|
||||
}
|
||||
void* ptr = mem::aligned_pointer(data.overflow_arg_area, max(8, $Type.alignof));
|
||||
defer data.overflow_arg_area = ptr + $Type.sizeof;
|
||||
return ($Type)*($LoadType*)ptr;
|
||||
$default:
|
||||
void* ptr = mem::aligned_pointer(data.overflow_arg_area, max(8, $Type.alignof));
|
||||
defer data.overflow_arg_area = ptr + $Type.sizeof;
|
||||
return *($Type*)ptr;
|
||||
$endswitch
|
||||
}
|
||||
@@ -206,5 +206,6 @@ macro bool os_is_posix() @const
|
||||
}
|
||||
const String[] AUTHORS = $$AUTHORS;
|
||||
const String[] AUTHOR_EMAILS = $$AUTHOR_EMAILS;
|
||||
const String PROJECT_VERSION = $$PROJECT_VERSION;
|
||||
const BUILTIN_EXPECT_IS_DISABLED = $feature(DISABLE_BUILTIN_EXPECT);
|
||||
const BUILTIN_PREFETCH_IS_DISABLED = $feature(DISABLE_BUILTIN_PREFETCH);
|
||||
|
||||
@@ -134,7 +134,16 @@ macro void init()
|
||||
log_init.call(fn () => (void)logger_mutex.init());
|
||||
}
|
||||
|
||||
fn void call_log(LogPriority prio, LogCategory category, String fmt, args...)
|
||||
macro void call_log(LogPriority prio, LogCategory category, String fmt, args...)
|
||||
{
|
||||
$if FULL_LOG:
|
||||
call_log_internal(prio, category, $$FILE, $$FUNC, $$LINE, fmt, args);
|
||||
$else
|
||||
call_log_internal(prio, category, "", "", 0, fmt, args);
|
||||
$endif
|
||||
}
|
||||
|
||||
fn void call_log_internal(LogPriority prio, LogCategory category, String file, String func, int line, String fmt, any[] args)
|
||||
{
|
||||
LogPriority priority = mem::@atomic_load(config_priorities[category], UNORDERED);
|
||||
if (priority > prio) return;
|
||||
@@ -143,11 +152,7 @@ fn void call_log(LogPriority prio, LogCategory category, String fmt, args...)
|
||||
Logger logger = current_logger;
|
||||
LogFn logfn = current_logfn;
|
||||
defer if (locked) (void)logger_mutex.unlock();
|
||||
$if FULL_LOG:
|
||||
logfn(logger.ptr, prio, category, current_tag, $$FILE, $$FUNC, $$LINE, fmt, args);
|
||||
$else
|
||||
logfn(logger.ptr, prio, category, current_tag, "", "", 0, fmt, args);
|
||||
$endif
|
||||
logfn(logger.ptr, prio, category, current_tag, file, func, line, fmt, args);
|
||||
}
|
||||
|
||||
fn String? get_category_name(LogCategory category)
|
||||
@@ -199,7 +204,11 @@ fn void StderrLogger.log(&self, LogPriority priority, LogCategory category, LogT
|
||||
str.init(mem, 256);
|
||||
str.appendf(fmt, ...args);
|
||||
TzDateTime time = datetime::now().to_local();
|
||||
$if FULL_LOG:
|
||||
io::eprintfn("[%02d:%02d:%02d:%04d] %s:%d [%s] %s", time.hour, time.min, time.sec, (time.usec / 1000), file, line, priority, str);
|
||||
$else
|
||||
io::eprintfn("[%02d:%02d:%02d:%04d] [%s] %s", time.hour, time.min, time.sec, (time.usec / 1000), priority, str);
|
||||
$endif
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@ macro masked_load(ptr, bool[<*>] mask, passthru)
|
||||
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
|
||||
|
||||
@return "A vector with the loaded values where the mask is true, passthru where the mask is false"
|
||||
@ensure $typeof(return) == $typeof(*ptr)
|
||||
*>
|
||||
macro @masked_load_aligned(ptr, bool[<*>] mask, passthru, usz $alignment)
|
||||
{
|
||||
|
||||
@@ -64,7 +64,7 @@ macro int @main_to_void_main_args(#m, int argc, char** argv)
|
||||
|
||||
module std::core::main_stub @if(env::WIN32);
|
||||
|
||||
extern fn Char16** _win_command_line_to_argv_w(ushort* cmd_line, int* argc_ptr) @extern("CommandLineToArgvW");
|
||||
extern fn Char16** _win_command_line_to_argv_w(ushort* cmd_line, int* argc_ptr) @cname("CommandLineToArgvW");
|
||||
|
||||
macro String[] win_command_line_to_strings(ushort* cmd_line) @private
|
||||
{
|
||||
|
||||
@@ -42,7 +42,7 @@ macro @enum_lookup_new($Type, $name, value)
|
||||
module std::core::runtime @if(env::FREESTANDING_WASM);
|
||||
|
||||
extern fn void __wasm_call_ctors();
|
||||
fn void wasm_initialize() @extern("_initialize") @wasm
|
||||
fn void wasm_initialize() @cname("_initialize") @wasm
|
||||
{
|
||||
// The linker synthesizes this to call constructors.
|
||||
__wasm_call_ctors();
|
||||
|
||||
@@ -56,20 +56,27 @@ long cycle_stop @local;
|
||||
DString benchmark_log @local;
|
||||
bool benchmark_warming @local;
|
||||
uint this_iteration @local;
|
||||
bool benchmark_stop @local;
|
||||
|
||||
macro @start_benchmark()
|
||||
macro void @start_benchmark()
|
||||
{
|
||||
benchmark_clock = std::time::clock::now();
|
||||
benchmark_clock = clock::now();
|
||||
cycle_start = $$sysclock();
|
||||
}
|
||||
|
||||
macro @end_benchmark()
|
||||
macro void @end_benchmark()
|
||||
{
|
||||
benchmark_nano_seconds = benchmark_clock.mark();
|
||||
cycle_stop = $$sysclock();
|
||||
}
|
||||
|
||||
macro @log_benchmark(msg, args...) => @pool()
|
||||
macro void @kill_benchmark(String format, ...)
|
||||
{
|
||||
@log_benchmark(format, $vasplat);
|
||||
benchmark_stop = true;
|
||||
}
|
||||
|
||||
macro void @log_benchmark(msg, args...) => @pool()
|
||||
{
|
||||
if (benchmark_warming) return;
|
||||
|
||||
@@ -133,6 +140,7 @@ fn bool run_benchmarks(BenchmarkUnit[] benchmarks)
|
||||
@start_benchmark(); // can be overridden by calls inside the unit's func
|
||||
|
||||
unit.func() @inline;
|
||||
if (benchmark_stop) return false;
|
||||
|
||||
if (benchmark_nano_seconds == (NanoDuration){}) @end_benchmark(); // only mark when it wasn't already by the unit.func
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
module std::core::runtime;
|
||||
import std::core::test @public;
|
||||
import std::core::mem::allocator @public;
|
||||
import libc, std::time, std::io, std::sort;
|
||||
import std::os::env;
|
||||
import libc, std::time, std::io, std::sort, std::os;
|
||||
|
||||
|
||||
alias TestFn = fn void();
|
||||
|
||||
@@ -14,10 +14,12 @@ TestContext* test_context @private;
|
||||
struct TestContext
|
||||
{
|
||||
JmpBuf buf;
|
||||
// Allows filtering test cased or modules by substring, e.g. 'foo::', 'foo::test_add'
|
||||
<* Allows filtering test cased or modules by substring, e.g. 'foo::', 'foo::test_add' *>
|
||||
String test_filter;
|
||||
// Triggers debugger breakpoint when assert or test:: checks failed
|
||||
<* Triggers debugger breakpoint when assert or test:: checks failed *>
|
||||
bool breakpoint_on_assert;
|
||||
<* Controls level of printed logs *>
|
||||
LogPriority log_level;
|
||||
|
||||
// internal state
|
||||
bool assert_print_backtrace;
|
||||
@@ -86,7 +88,21 @@ fn bool terminal_has_ansi_codes() @local => @pool()
|
||||
$endif
|
||||
}
|
||||
|
||||
fn void sig_bus_error(CInt i, void*, void* context) @local @if(env::POSIX)
|
||||
{
|
||||
panic_test("Bus error", "Unknown", "Unknown", 1, posix::stack_instruction(context));
|
||||
}
|
||||
|
||||
fn void sig_segmentation_fault(CInt i, void*, void* context) @local @if(env::POSIX)
|
||||
{
|
||||
panic_test("Segmentation fault", "Unknown", "Unknown", 1, posix::stack_instruction(context));
|
||||
}
|
||||
|
||||
fn void test_panic(String message, String file, String function, uint line) @local
|
||||
{
|
||||
panic_test(message, file, function, line);
|
||||
}
|
||||
fn void panic_test(String message, String file, String function, uint line, void* extra_trace = null) @local
|
||||
{
|
||||
if (test_context.is_in_panic) return;
|
||||
test_context.is_in_panic = true;
|
||||
@@ -96,7 +112,7 @@ fn void test_panic(String message, String file, String function, uint line) @loc
|
||||
if (test_context.assert_print_backtrace)
|
||||
{
|
||||
$if env::NATIVE_STACKTRACE:
|
||||
builtin::print_backtrace(message, 0);
|
||||
builtin::print_backtrace(message, extra_trace ? 3 : 0, extra_trace);
|
||||
$endif
|
||||
}
|
||||
io::printf("\nTest failed ^^^ ( %s:%s ) %s\n", file, line, message);
|
||||
@@ -165,6 +181,7 @@ fn void unmute_output(bool has_error) @local
|
||||
(void)stdout.flush();
|
||||
}
|
||||
|
||||
|
||||
fn bool run_tests(String[] args, TestUnit[] tests) @private
|
||||
{
|
||||
usz max_name;
|
||||
@@ -175,6 +192,10 @@ fn bool run_tests(String[] args, TestUnit[] tests) @private
|
||||
io::printn("There are no test units to run.");
|
||||
return true; // no tests == technically a pass
|
||||
}
|
||||
$if !env::NO_LIBC && env::POSIX:
|
||||
posix::install_signal_handler(libc::SIGBUS, &sig_bus_error);
|
||||
posix::install_signal_handler(libc::SIGSEGV, &sig_segmentation_fault);
|
||||
$endif
|
||||
foreach (&unit : tests)
|
||||
{
|
||||
if (max_name < unit.name.len) max_name = unit.name.len;
|
||||
@@ -183,6 +204,7 @@ fn bool run_tests(String[] args, TestUnit[] tests) @private
|
||||
{
|
||||
.assert_print_backtrace = true,
|
||||
.breakpoint_on_assert = false,
|
||||
.log_level = LogPriority.ERROR,
|
||||
.test_filter = "",
|
||||
.has_ansi_codes = terminal_has_ansi_codes(),
|
||||
.stored.allocator = mem,
|
||||
@@ -200,6 +222,7 @@ fn bool run_tests(String[] args, TestUnit[] tests) @private
|
||||
case "--test-noleak":
|
||||
check_leaks = false;
|
||||
case "--test-nocapture":
|
||||
case "--test-show-output":
|
||||
context.is_no_capture = true;
|
||||
case "--noansi":
|
||||
context.has_ansi_codes = false;
|
||||
@@ -215,11 +238,28 @@ fn bool run_tests(String[] args, TestUnit[] tests) @private
|
||||
}
|
||||
context.test_filter = args[i + 1];
|
||||
i++;
|
||||
case "--test-log-level":
|
||||
if (i == args.len - 1)
|
||||
{
|
||||
io::printn("Missing log level for argument `--test-log-level`.");
|
||||
return false;
|
||||
}
|
||||
@pool()
|
||||
{
|
||||
String upper = args[i + 1].to_upper_copy(tmem);
|
||||
if (catch @try(context.log_level, enum_by_name(LogPriority, upper)))
|
||||
{
|
||||
io::printn("Log level given to `--test-log-level` is not one of verbose, debug, info, warn, error or critical.");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
i++;
|
||||
default:
|
||||
io::printfn("Unknown argument: %s", args[i]);
|
||||
}
|
||||
}
|
||||
test_context = &context;
|
||||
log::set_priority_all(test_context.log_level);
|
||||
|
||||
if (sort_tests)
|
||||
{
|
||||
|
||||
@@ -29,11 +29,11 @@ alias ErrorCallback = fn void (ZString);
|
||||
@param addr : "Start of memory region."
|
||||
@param size : "Size of memory region."
|
||||
*>
|
||||
macro poison_memory_region(void* addr, usz size)
|
||||
macro void poison_memory_region(void* addr, usz size)
|
||||
{
|
||||
$if env::ADDRESS_SANITIZER:
|
||||
$if env::ADDRESS_SANITIZER:
|
||||
__asan_poison_memory_region(addr, size);
|
||||
$endif
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -50,11 +50,11 @@ macro poison_memory_region(void* addr, usz size)
|
||||
@param addr : "Start of memory region."
|
||||
@param size : "Size of memory region."
|
||||
*>
|
||||
macro unpoison_memory_region(void* addr, usz size)
|
||||
macro void unpoison_memory_region(void* addr, usz size)
|
||||
{
|
||||
$if env::ADDRESS_SANITIZER:
|
||||
$if env::ADDRESS_SANITIZER:
|
||||
__asan_unpoison_memory_region(addr, size);
|
||||
$endif
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
|
||||
@@ -70,7 +70,7 @@ macro WString @wstring(String $string) @builtin
|
||||
}
|
||||
|
||||
<*
|
||||
Create a slice of an UTF32 encoded string at compile time.
|
||||
Create a slice of an UTF16 encoded string at compile time.
|
||||
|
||||
@param $string : "The string to encode"
|
||||
*>
|
||||
@@ -1067,14 +1067,10 @@ macro String.to_integer(self, $Type, int base = 10)
|
||||
{
|
||||
if (is_negative)
|
||||
{
|
||||
$Type new_value = value * base_used - c;
|
||||
if (new_value > value) return INTEGER_OVERFLOW?;
|
||||
value = new_value;
|
||||
value = value.overflow_mul(base_used).overflow_sub(c) ?? INTEGER_OVERFLOW?!;
|
||||
break;
|
||||
}
|
||||
$Type new_value = value * base_used + c;
|
||||
if (new_value < value) return INTEGER_OVERFLOW?;
|
||||
value = new_value;
|
||||
value = value.overflow_mul(base_used).overflow_add(c) ?? INTEGER_OVERFLOW?!;
|
||||
};
|
||||
}
|
||||
return value;
|
||||
|
||||
@@ -26,6 +26,7 @@ fn void? test_div() @test
|
||||
test::le(2, 3);
|
||||
test::eq_approx(m::divide(1, 3)!, 0.333, places: 3);
|
||||
test::@check(2 == 2, "divide: %d", divide(6, 3)!);
|
||||
test::@error(m::divide(3, 0));
|
||||
test::@error(m::divide(3, 0), MathError.DIVISION_BY_ZERO);
|
||||
}
|
||||
|
||||
@@ -78,14 +79,15 @@ macro @check(#condition, String format = "", args...)
|
||||
}
|
||||
|
||||
<*
|
||||
Check if function returns specific error
|
||||
Check if function returns (specific) error
|
||||
|
||||
@param #funcresult : `result of function execution`
|
||||
@param error_expected : `expected error of function execution`
|
||||
@require runtime::test_context != null : "Only allowed in @test functions"
|
||||
*>
|
||||
macro @error(#funcresult, fault error_expected)
|
||||
macro @error(#funcresult, fault error_expected = ...)
|
||||
{
|
||||
$if $defined(error_expected):
|
||||
if (catch err = #funcresult)
|
||||
{
|
||||
if (err != error_expected)
|
||||
@@ -96,6 +98,10 @@ macro @error(#funcresult, fault error_expected)
|
||||
return;
|
||||
}
|
||||
print_panicf("`%s` error [%s] was not returned.", $stringify(#funcresult), error_expected);
|
||||
$else
|
||||
if (catch err = #funcresult) return;
|
||||
print_panicf("`%s` unexpectedly did not return error.", $stringify(#funcresult));
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
|
||||
@@ -115,7 +115,9 @@ fn bool TypeKind.is_int(kind) @inline
|
||||
return kind == TypeKind.SIGNED_INT || kind == TypeKind.UNSIGNED_INT;
|
||||
}
|
||||
|
||||
macro bool is_slice_convertable($Type)
|
||||
macro bool is_slice_convertable($Type) @deprecated("Use is_slice_convertible") => is_slice_convertible($Type);
|
||||
|
||||
macro bool is_slice_convertible($Type)
|
||||
{
|
||||
$switch $Type.kindof:
|
||||
$case SLICE:
|
||||
|
||||
650
lib/std/crypto/aes.c3
Normal file
650
lib/std/crypto/aes.c3
Normal file
@@ -0,0 +1,650 @@
|
||||
<*
|
||||
This is an implementation of the AES algorithm with the ECB, CTR and CBC
|
||||
modes. The key size can be chosen among AES128, AES192, AES256.
|
||||
|
||||
Ported from github.com/kokke/tiny-aes-c by Koni Marti.
|
||||
|
||||
The implementation is verified against the test vectors from the National
|
||||
Institute of Standards and Technology Special Publication 800-38A 2001 ED.
|
||||
|
||||
Data length must be evenly divisible by 16 bytes (len % 16 == 0) unless CTR is
|
||||
used. You should pad the end of the string with zeros or use PKCS7 if this is not the case.
|
||||
For AES192/256 the key size is proportionally larger.
|
||||
|
||||
The following example demonstrates the AES encryption of a plaintext string
|
||||
with an AES 128-bit key:
|
||||
|
||||
```
|
||||
module app;
|
||||
import std::crypto::aes, std::io;
|
||||
fn void main()
|
||||
{
|
||||
char[] key = x"2b7e151628aed2a6abf7158809cf4f3c";
|
||||
char[] text = x"6bc1bee22e409f96e93d7e117393172a";
|
||||
char[16] iv = x"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
|
||||
Aes aes;
|
||||
aes.init(AES128, key, iv);
|
||||
defer aes.destroy();
|
||||
char[] cipher = aes.encrypt(mem, text);
|
||||
defer free(cipher);
|
||||
assert(cipher == x"874d6191b620e3261bef6864990db6ce");
|
||||
}
|
||||
```
|
||||
|
||||
*>
|
||||
module std::crypto::aes;
|
||||
|
||||
<* Block length in bytes. AES is 128-bit blocks only. *>
|
||||
const BLOCKLEN = 16;
|
||||
|
||||
<* Number of columns of a AES state. *>
|
||||
const COLNUM = 4;
|
||||
|
||||
<*
|
||||
Block modes:
|
||||
ECB - Electronic Code Book (Not recommended, indata be 16 byte multiple)
|
||||
CBC - Cipher Block Chaining (Indata be 16 byte multiple)
|
||||
CTR - Counter Mode (Recommended, data may be any size)
|
||||
*>
|
||||
enum BlockMode
|
||||
{
|
||||
ECB,
|
||||
CBC,
|
||||
CTR,
|
||||
}
|
||||
|
||||
<* AES type: 128, 192 or 256 bits *>
|
||||
enum AesType : (AesKey key)
|
||||
{
|
||||
AES128 = { 128, 16, 176, 4, 10 },
|
||||
AES192 = { 192, 24, 208, 6, 12 },
|
||||
AES256 = { 256, 32, 240, 8, 14 }
|
||||
}
|
||||
|
||||
struct AesKey
|
||||
{
|
||||
<* Size of key in bits *>
|
||||
usz key_size;
|
||||
<* Size of key in bytes *>
|
||||
int key_len;
|
||||
<* Size of the expanded round_key *>
|
||||
int key_exp_size; // expected size of round_key
|
||||
<* Number of 32 bit words in key *>
|
||||
usz nk;
|
||||
<* Number of rounds in the cipher *>
|
||||
usz nr;
|
||||
}
|
||||
|
||||
struct Aes
|
||||
{
|
||||
<* The type, AES128, AES192 or AES256 *>
|
||||
AesKey type;
|
||||
<* Block mode: ECB, CBC or CTR *>
|
||||
BlockMode mode;
|
||||
<* Initialization Vector *>
|
||||
char[BLOCKLEN] iv;
|
||||
<* Internal key state *>
|
||||
char[256] round_key;
|
||||
<* Internal state *>
|
||||
AesState state;
|
||||
}
|
||||
alias AesState = char[COLNUM][COLNUM];
|
||||
|
||||
<*
|
||||
Initializes the AES crypto. The initialization vector should be securely random for each encryption
|
||||
to mitigate things like replay attacks.
|
||||
|
||||
@param type : "The type or AES: 128, 192 or 256 bits"
|
||||
@param [in] key : "The key to use, should be the same bit size as the type, so 16, 24 or 32 bytes"
|
||||
@param iv : "The initialization vector"
|
||||
@param mode : "The block mode: EBC, CBC, CTR. Defaults to CTR"
|
||||
|
||||
@require key.len == type.key.key_len : "Key does not match expected length."
|
||||
*>
|
||||
fn Aes* Aes.init(&self, AesType type, char[] key, char[BLOCKLEN] iv, BlockMode mode = CTR)
|
||||
{
|
||||
*self = { .type = type.key, .mode = mode, .iv = iv };
|
||||
key_expansion(type, key, &self.round_key);
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
Completely erases data stored in the context.
|
||||
*>
|
||||
fn void Aes.destroy(&self)
|
||||
{
|
||||
*self = {};
|
||||
}
|
||||
|
||||
<*
|
||||
Check if the length is valid using the given block mode. It has to be a multiple of 16 bytes unless CTR is used.
|
||||
*>
|
||||
macro bool is_valid_encryption_len(BlockMode mode, usz len)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case CTR:
|
||||
return true;
|
||||
case ECB:
|
||||
case CBC:
|
||||
return len % BLOCKLEN == 0;
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@param [in] in : "Plaintext input."
|
||||
@param [out] out : "Cipher output."
|
||||
@require is_valid_encryption_len(self.mode, in.len) : "The input must be a multiple of 16 unless CTR is used"
|
||||
@require out.len >= in.len : "Out buffer must be sufficiently large to hold the data"
|
||||
*>
|
||||
fn void Aes.encrypt_buffer(&self, char[] in, char[] out)
|
||||
{
|
||||
switch (self.mode)
|
||||
{
|
||||
case CTR: ctr_xcrypt_buffer(self, in, out);
|
||||
case ECB: ecb_encrypt_buffer(self, in, out);
|
||||
case CBC: cbc_encrypt_buffer(self, in, out);
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@param [in] in : "Cipher input."
|
||||
@param [out] out : "Plaintext output."
|
||||
@require is_valid_encryption_len(self.mode, in.len) : "The encrypted data must be a multiple of 16 unless CTR is used"
|
||||
@require out.len >= in.len : "Out buffer must be sufficiently large to hold the data"
|
||||
*>
|
||||
fn void Aes.decrypt_buffer(&self, char[] in, char[] out)
|
||||
{
|
||||
switch (self.mode)
|
||||
{
|
||||
case ECB: ecb_decrypt_buffer(self, in, out);
|
||||
case CBC: cbc_decrypt_buffer(self, in, out);
|
||||
case CTR: ctr_xcrypt_buffer(self, in, out);
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Encrypt the data, allocating memory for the encrypted data.
|
||||
|
||||
@param [in] in : "Plaintext input."
|
||||
@param [&inout] allocator : "The allocator to use for the output"
|
||||
@require is_valid_encryption_len(self.mode, in.len) : "The in-data needs to be a multiple of 16 unless CTR is used"
|
||||
*>
|
||||
fn char[] Aes.encrypt(&self, Allocator allocator, char[] in)
|
||||
{
|
||||
char[] out = allocator::alloc_array(allocator, char, in.len);
|
||||
self.encrypt_buffer(in, out) @inline;
|
||||
return out;
|
||||
}
|
||||
|
||||
<*
|
||||
Encrypt the data, allocating temp memory for the encrypted data.
|
||||
|
||||
@param [in] in : "Plaintext input."
|
||||
@require is_valid_encryption_len(self.mode, in.len) : "The in-data needs to be a multiple of 16 unless CTR is used"
|
||||
*>
|
||||
fn char[] Aes.tencrypt(&self, char[] in)
|
||||
{
|
||||
return self.encrypt(tmem, in);
|
||||
}
|
||||
|
||||
<*
|
||||
Decrypt the data, allocating memory for the decrypted data.
|
||||
|
||||
@param [in] in : "Encrypted input."
|
||||
@param [&inout] allocator : "The allocator to use for the output"
|
||||
@require is_valid_encryption_len(self.mode, in.len) : "The in-data needs to be a multiple of 16 unless CTR is used"
|
||||
*>
|
||||
fn char[] Aes.decrypt(&self, Allocator allocator, char[] in)
|
||||
{
|
||||
char[] out = allocator::alloc_array(allocator, char, in.len);
|
||||
self.decrypt_buffer(in, out) @inline;
|
||||
return out;
|
||||
}
|
||||
|
||||
<*
|
||||
Decrypt the data, allocating temp memory for the decrypted data.
|
||||
|
||||
@param [in] in : "Encrypted input."
|
||||
@require is_valid_encryption_len(self.mode, in.len) : "The in-data needs to be a multiple of 16 unless CTR is used"
|
||||
|
||||
*>
|
||||
fn char[] Aes.tdecrypt(&self, char[] in)
|
||||
{
|
||||
return self.decrypt(tmem, in);
|
||||
}
|
||||
|
||||
module std::crypto::aes @private;
|
||||
|
||||
<*
|
||||
@param [&inout] aes : "AES context."
|
||||
@param [in] in : "Plaintext input."
|
||||
@param [out] out : "Cipher output."
|
||||
*>
|
||||
fn void ecb_encrypt_block(Aes *aes, char[BLOCKLEN]* in, char[BLOCKLEN]* out)
|
||||
{
|
||||
for (usz i = 0; i < 4; i++)
|
||||
{
|
||||
for (usz j = 0; j < 4; j++)
|
||||
{
|
||||
aes.state[i][j] = (*in)[i * 4 + j];
|
||||
}
|
||||
}
|
||||
aes_cipher(aes, &aes.round_key);
|
||||
for (usz i = 0; i < 4; i++)
|
||||
{
|
||||
for (usz j = 0; j < 4; j++)
|
||||
{
|
||||
(*out)[i * 4 + j] = aes.state[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] aes : "AES context."
|
||||
@param [in] in : "Cipher input."
|
||||
@param [out] out : "Plaintext output."
|
||||
*>
|
||||
fn void ecb_decrypt_block(Aes *aes, char[BLOCKLEN]* in, char[BLOCKLEN]* out)
|
||||
{
|
||||
for (usz i = 0; i < 4; i++)
|
||||
{
|
||||
for (usz j = 0; j < 4; j++)
|
||||
{
|
||||
aes.state[i][j] = (*in)[i * 4 + j];
|
||||
}
|
||||
}
|
||||
inv_cipher(aes, &aes.round_key);
|
||||
for (usz i = 0; i < 4; i++)
|
||||
{
|
||||
for (usz j = 0; j < 4; j++)
|
||||
{
|
||||
(*out)[i * 4 + j] = aes.state[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] aes : "AES context."
|
||||
@param [in] in : "Cipher input."
|
||||
@param [out] out : "Plaintext output."
|
||||
@require out.len >= in.len : "out must be at least as large as buf"
|
||||
*>
|
||||
fn void ecb_decrypt_buffer(Aes *aes, char[] in, char[] out)
|
||||
{
|
||||
usz len = in.len;
|
||||
for (usz i = 0; i < len; i += 4)
|
||||
{
|
||||
ecb_decrypt_block(aes, in[:BLOCKLEN], out[:BLOCKLEN]) @inline;
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] aes : "AES context."
|
||||
@param [in] in : "Plaintext input."
|
||||
@param [out] out : "Cipher output."
|
||||
*>
|
||||
fn void ecb_encrypt_buffer(Aes *aes, char[] in, char[] out)
|
||||
{
|
||||
usz len = in.len;
|
||||
for (usz i = 0; i < len; i += BLOCKLEN)
|
||||
{
|
||||
ecb_encrypt_block(aes, in[i:BLOCKLEN], out[i:BLOCKLEN]) @inline;
|
||||
}
|
||||
}
|
||||
|
||||
fn void xor_with_iv(char[] buf, char[BLOCKLEN]* iv) @local
|
||||
{
|
||||
foreach (i, b : *iv)
|
||||
{
|
||||
buf[i] ^= b;
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] aes : "AES context."
|
||||
@param [in] in : "Plaintext input."
|
||||
@param [out] out : "Cipher output."
|
||||
*>
|
||||
fn void cbc_encrypt_buffer(Aes *aes, char[] in, char[] out)
|
||||
{
|
||||
char[] iv = aes.iv[..];
|
||||
usz len = in.len;
|
||||
char[BLOCKLEN] tmp;
|
||||
char[BLOCKLEN] tmp2;
|
||||
for (usz i = 0; i < len; i += BLOCKLEN)
|
||||
{
|
||||
tmp[:BLOCKLEN] = in[i:BLOCKLEN];
|
||||
xor_with_iv(&tmp, iv);
|
||||
ecb_encrypt_block(aes, &tmp, &tmp2);
|
||||
out[i:BLOCKLEN] = tmp2[..];
|
||||
iv = tmp2[..];
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] aes : "AES context."
|
||||
@param [in] in : "Cipher input."
|
||||
@param [out] out : "Plaintext output."
|
||||
*>
|
||||
fn void cbc_decrypt_buffer(Aes *aes, char[] in, char[] out)
|
||||
{
|
||||
char[BLOCKLEN] tmp;
|
||||
usz len = in.len;
|
||||
for (usz i = 0; i < len; i += BLOCKLEN)
|
||||
{
|
||||
ecb_decrypt_block(aes, in[i:BLOCKLEN], &tmp);
|
||||
xor_with_iv(&tmp, aes.iv[..]);
|
||||
aes.iv[:BLOCKLEN] = in[i:BLOCKLEN];
|
||||
out[i:BLOCKLEN] = tmp[..];
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&inout] aes : "AES context."
|
||||
@param [in] in : "Plaintext/cipher input."
|
||||
@param [out] out : "Cipher/plaintext output."
|
||||
*>
|
||||
fn void ctr_xcrypt_buffer(Aes *aes, char[] in, char[] out)
|
||||
{
|
||||
char[BLOCKLEN] buffer @noinit;
|
||||
usz len = in.len;
|
||||
for (int bi = BLOCKLEN, usz i = 0; i < len; i++)
|
||||
{
|
||||
if (bi == BLOCKLEN)
|
||||
{
|
||||
buffer = aes.iv;
|
||||
ecb_encrypt_block(aes, &buffer, &buffer);
|
||||
|
||||
for LOOP: (bi = (BLOCKLEN - 1); bi >= 0; bi--)
|
||||
{
|
||||
if (aes.iv[bi] == 255)
|
||||
{
|
||||
aes.iv[bi] = 0;
|
||||
continue;
|
||||
}
|
||||
aes.iv[bi]++;
|
||||
break LOOP;
|
||||
}
|
||||
bi = 0;
|
||||
}
|
||||
out[i] = in[i] ^ buffer[bi];
|
||||
bi++;
|
||||
}
|
||||
}
|
||||
|
||||
macro char get_sbox_value(num) => SBOX[num];
|
||||
macro char get_sbox_invert(num) => RSBOX[num];
|
||||
|
||||
const char[256] SBOX =
|
||||
x`637c777bf26b6fc53001672bfed7ab76
|
||||
ca82c97dfa5947f0add4a2af9ca472c0
|
||||
b7fd9326363ff7cc34a5e5f171d83115
|
||||
04c723c31896059a071280e2eb27b275
|
||||
09832c1a1b6e5aa0523bd6b329e32f84
|
||||
53d100ed20fcb15b6acbbe394a4c58cf
|
||||
d0efaafb434d338545f9027f503c9fa8
|
||||
51a3408f929d38f5bcb6da2110fff3d2
|
||||
cd0c13ec5f974417c4a77e3d645d1973
|
||||
60814fdc222a908846eeb814de5e0bdb
|
||||
e0323a0a4906245cc2d3ac629195e479
|
||||
e7c8376d8dd54ea96c56f4ea657aae08
|
||||
ba78252e1ca6b4c6e8dd741f4bbd8b8a
|
||||
703eb5664803f60e613557b986c11d9e
|
||||
e1f8981169d98e949b1e87e9ce5528df
|
||||
8ca1890dbfe6426841992d0fb054bb16`;
|
||||
|
||||
const char[256] RSBOX =
|
||||
x`52096ad53036a538bf40a39e81f3d7fb
|
||||
7ce339829b2fff87348e4344c4dee9cb
|
||||
547b9432a6c2233dee4c950b42fac34e
|
||||
082ea16628d924b2765ba2496d8bd125
|
||||
72f8f66486689816d4a45ccc5d65b692
|
||||
6c704850fdedb9da5e154657a78d9d84
|
||||
90d8ab008cbcd30af7e45805b8b34506
|
||||
d02c1e8fca3f0f02c1afbd0301138a6b
|
||||
3a9111414f67dcea97f2cfcef0b4e673
|
||||
96ac7422e7ad3585e2f937e81c75df6e
|
||||
47f11a711d29c5896fb7620eaa18be1b
|
||||
fc563e4bc6d279209adbc0fe78cd5af4
|
||||
1fdda8338807c731b11210592780ec5f
|
||||
60517fa919b54a0d2de57a9f93c99cef
|
||||
a0e03b4dae2af5b0c8ebbb3c83539961
|
||||
172b047eba77d626e169146355210c7d`;
|
||||
|
||||
const char[11] RCON = x`8d01020408102040801b36`;
|
||||
|
||||
fn void add_round_key(Aes* aes, usz round, char[] round_key)
|
||||
{
|
||||
usz i, j;
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
for (j = 0; j < 4; j++)
|
||||
{
|
||||
aes.state[i][j] ^= round_key[(round * COLNUM * 4) + (i * COLNUM) + j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn void sub_bytes(Aes* aes)
|
||||
{
|
||||
for (usz i = 0; i < 4; i++)
|
||||
{
|
||||
for (usz j = 0; j < 4; j++)
|
||||
{
|
||||
aes.state[j][i] = get_sbox_value(aes.state[j][i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn void shift_rows(Aes* aes)
|
||||
{
|
||||
char temp;
|
||||
|
||||
temp = aes.state[0][1];
|
||||
aes.state[0][1] = aes.state[1][1];
|
||||
aes.state[1][1] = aes.state[2][1];
|
||||
aes.state[2][1] = aes.state[3][1];
|
||||
aes.state[3][1] = temp;
|
||||
|
||||
temp = aes.state[0][2];
|
||||
aes.state[0][2] = aes.state[2][2];
|
||||
aes.state[2][2] = temp;
|
||||
|
||||
temp = aes.state[1][2];
|
||||
aes.state[1][2] = aes.state[3][2];
|
||||
aes.state[3][2] = temp;
|
||||
|
||||
temp = aes.state[0][3];
|
||||
aes.state[0][3] = aes.state[3][3];
|
||||
aes.state[3][3] = aes.state[2][3];
|
||||
aes.state[2][3] = aes.state[1][3];
|
||||
aes.state[1][3] = temp;
|
||||
}
|
||||
|
||||
fn char xtime(char x) @local
|
||||
{
|
||||
return ((x << 1) ^ (((x >> 7) & 1) * 0x1b));
|
||||
}
|
||||
|
||||
fn void mix_columns(Aes* aes)
|
||||
{
|
||||
for (usz i = 0; i < 4; i++)
|
||||
{
|
||||
char t = aes.state[i][0];
|
||||
char tmp = aes.state[i][0] ^ aes.state[i][1] ^ aes.state[i][2] ^ aes.state[i][3];
|
||||
|
||||
char tm = aes.state[i][0] ^ aes.state[i][1];
|
||||
tm = xtime(tm);
|
||||
aes.state[i][0] ^= tm ^ tmp;
|
||||
|
||||
tm = aes.state[i][1] ^ aes.state[i][2];
|
||||
tm = xtime(tm);
|
||||
aes.state[i][1] ^= tm ^ tmp;
|
||||
|
||||
tm = aes.state[i][2] ^ aes.state[i][3];
|
||||
tm = xtime(tm);
|
||||
aes.state[i][2] ^= tm ^ tmp;
|
||||
|
||||
tm = aes.state[i][3] ^ t;
|
||||
tm = xtime(tm);
|
||||
aes.state[i][3] ^= tm ^ tmp;
|
||||
}
|
||||
}
|
||||
|
||||
fn char multiply(char x, char y) @local
|
||||
{
|
||||
return (((y & 1) * x) ^
|
||||
(((y>>1) & 1) * xtime(x)) ^
|
||||
(((y>>2) & 1) * xtime(xtime(x))) ^
|
||||
(((y>>3) & 1) * xtime(xtime(xtime(x)))) ^
|
||||
(((y>>4) & 1) * xtime(xtime(xtime(xtime(x))))));
|
||||
}
|
||||
|
||||
fn void inv_mix_columns(Aes* aes)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
char a = aes.state[i][0];
|
||||
char b = aes.state[i][1];
|
||||
char c = aes.state[i][2];
|
||||
char d = aes.state[i][3];
|
||||
|
||||
aes.state[i][0] = multiply(a, 0x0e) ^ multiply(b, 0x0b) ^ multiply(c, 0x0d) ^ multiply(d, 0x09);
|
||||
aes.state[i][1] = multiply(a, 0x09) ^ multiply(b, 0x0e) ^ multiply(c, 0x0b) ^ multiply(d, 0x0d);
|
||||
aes.state[i][2] = multiply(a, 0x0d) ^ multiply(b, 0x09) ^ multiply(c, 0x0e) ^ multiply(d, 0x0b);
|
||||
aes.state[i][3] = multiply(a, 0x0b) ^ multiply(b, 0x0d) ^ multiply(c, 0x09) ^ multiply(d, 0x0e);
|
||||
}
|
||||
}
|
||||
|
||||
fn void inv_sub_bytes(Aes* aes)
|
||||
{
|
||||
for (usz i = 0; i < 4; i++)
|
||||
{
|
||||
for (usz j = 0; j < 4; j++)
|
||||
{
|
||||
aes.state[j][i] = get_sbox_invert(aes.state[j][i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn void inv_shift_rows(Aes* aes)
|
||||
{
|
||||
char temp;
|
||||
|
||||
temp = aes.state[3][1];
|
||||
aes.state[3][1] = aes.state[2][1];
|
||||
aes.state[2][1] = aes.state[1][1];
|
||||
aes.state[1][1] = aes.state[0][1];
|
||||
aes.state[0][1] = temp;
|
||||
|
||||
temp = aes.state[0][2];
|
||||
aes.state[0][2] = aes.state[2][2];
|
||||
aes.state[2][2] = temp;
|
||||
|
||||
temp = aes.state[1][2];
|
||||
aes.state[1][2] = aes.state[3][2];
|
||||
aes.state[3][2] = temp;
|
||||
|
||||
temp = aes.state[0][3];
|
||||
aes.state[0][3] = aes.state[1][3];
|
||||
aes.state[1][3] = aes.state[2][3];
|
||||
aes.state[2][3] = aes.state[3][3];
|
||||
aes.state[3][3] = temp;
|
||||
}
|
||||
|
||||
fn void aes_cipher(Aes* aes, char[] round_key)
|
||||
{
|
||||
usz round = 0;
|
||||
add_round_key(aes, 0, round_key);
|
||||
|
||||
for LOOP: (round = 1;; round++)
|
||||
{
|
||||
sub_bytes(aes);
|
||||
shift_rows(aes);
|
||||
if (round == aes.type.nr) break LOOP;
|
||||
mix_columns(aes);
|
||||
add_round_key(aes, round, round_key);
|
||||
}
|
||||
add_round_key(aes, aes.type.nr, round_key);
|
||||
}
|
||||
|
||||
fn void inv_cipher(Aes* aes, char[] round_key)
|
||||
{
|
||||
add_round_key(aes, aes.type.nr, round_key);
|
||||
for (usz round = aes.type.nr - 1; ; round--)
|
||||
{
|
||||
inv_shift_rows(aes);
|
||||
inv_sub_bytes(aes);
|
||||
add_round_key(aes, round, round_key);
|
||||
if (!round) return;
|
||||
inv_mix_columns(aes);
|
||||
}
|
||||
}
|
||||
|
||||
<*¨
|
||||
@param type : "The AES variant to expant the key for"
|
||||
@param [inout] round_key : "Key to expand into"
|
||||
@param [in] key : "The key to expand"
|
||||
@require key.len == type.key.key_len : "Key does not match expected length."
|
||||
*>
|
||||
fn void key_expansion(AesType type, char[] key, char[] round_key) @private
|
||||
{
|
||||
usz nk = type.key.nk;
|
||||
for (usz i = 0; i < nk; i++)
|
||||
{
|
||||
round_key[(i * 4) + 0] = key[(i * 4) + 0];
|
||||
round_key[(i * 4) + 1] = key[(i * 4) + 1];
|
||||
round_key[(i * 4) + 2] = key[(i * 4) + 2];
|
||||
round_key[(i * 4) + 3] = key[(i * 4) + 3];
|
||||
}
|
||||
|
||||
for (usz i = nk; i < COLNUM * (type.key.nr + 1); i++)
|
||||
{
|
||||
usz k = (i - 1) * 4;
|
||||
|
||||
char[4] tempa @noinit;
|
||||
|
||||
tempa[0] = round_key[k + 0];
|
||||
tempa[1] = round_key[k + 1];
|
||||
tempa[2] = round_key[k + 2];
|
||||
tempa[3] = round_key[k + 3];
|
||||
|
||||
if (i % nk == 0)
|
||||
{
|
||||
// rotword
|
||||
char tmp = tempa[0];
|
||||
tempa[0] = tempa[1];
|
||||
tempa[1] = tempa[2];
|
||||
tempa[2] = tempa[3];
|
||||
tempa[3] = tmp;
|
||||
|
||||
// subword
|
||||
tempa[0] = get_sbox_value(tempa[0]);
|
||||
tempa[1] = get_sbox_value(tempa[1]);
|
||||
tempa[2] = get_sbox_value(tempa[2]);
|
||||
tempa[3] = get_sbox_value(tempa[3]);
|
||||
|
||||
tempa[0] = tempa[0] ^ RCON[i / nk];
|
||||
}
|
||||
|
||||
if (type.key.key_size == 256)
|
||||
{
|
||||
if (i % nk == 4)
|
||||
{
|
||||
// subword
|
||||
tempa[0] = get_sbox_value(tempa[0]);
|
||||
tempa[1] = get_sbox_value(tempa[1]);
|
||||
tempa[2] = get_sbox_value(tempa[2]);
|
||||
tempa[3] = get_sbox_value(tempa[3]);
|
||||
}
|
||||
}
|
||||
usz j = i * 4;
|
||||
k = (i - nk) * 4;
|
||||
round_key[j + 0] = round_key[k + 0] ^ tempa[0];
|
||||
round_key[j + 1] = round_key[k + 1] ^ tempa[1];
|
||||
round_key[j + 2] = round_key[k + 2] ^ tempa[2];
|
||||
round_key[j + 3] = round_key[k + 3] ^ tempa[3];
|
||||
}
|
||||
}
|
||||
|
||||
87
lib/std/crypto/aes_128_192_256.c3
Normal file
87
lib/std/crypto/aes_128_192_256.c3
Normal file
@@ -0,0 +1,87 @@
|
||||
// Experimental implementation
|
||||
module std::crypto::aes128;
|
||||
import std::crypto::aes;
|
||||
|
||||
fn char[] encrypt(Allocator allocator, char[16]* key, char[aes::BLOCKLEN] iv, char[] data)
|
||||
{
|
||||
Aes aes @noinit;
|
||||
aes.init(AES128, key, iv, CTR);
|
||||
defer aes.destroy();
|
||||
return aes.encrypt(allocator, data);
|
||||
}
|
||||
|
||||
fn char[] tencrypt(char[16]* key, char[aes::BLOCKLEN] iv, char[] data)
|
||||
{
|
||||
return encrypt(tmem, key, iv, data);
|
||||
}
|
||||
|
||||
fn char[] decrypt(Allocator allocator, char[16]* key, char[aes::BLOCKLEN] iv, char[] data)
|
||||
{
|
||||
Aes aes @noinit;
|
||||
aes.init(AES128, key, iv, CTR);
|
||||
defer aes.destroy();
|
||||
return aes.decrypt(allocator, data);
|
||||
}
|
||||
|
||||
fn char[] tdecrypt(char[16]* key, char[aes::BLOCKLEN] iv, char[] data)
|
||||
{
|
||||
return decrypt(tmem, key, iv, data);
|
||||
}
|
||||
|
||||
module std::crypto::aes192;
|
||||
import std::crypto::aes;
|
||||
|
||||
fn char[] encrypt(Allocator allocator, char[24]* key, char[aes::BLOCKLEN] iv, char[] data)
|
||||
{
|
||||
Aes aes @noinit;
|
||||
aes.init(AES192, key, iv, CTR);
|
||||
defer aes.destroy();
|
||||
return aes.encrypt(allocator, data);
|
||||
}
|
||||
|
||||
fn char[] tencrypt(char[24]* key, char[aes::BLOCKLEN] iv, char[] data)
|
||||
{
|
||||
return encrypt(tmem, key, iv, data);
|
||||
}
|
||||
|
||||
fn char[] decrypt(Allocator allocator, char[24]* key, char[aes::BLOCKLEN] iv, char[] data)
|
||||
{
|
||||
Aes aes @noinit;
|
||||
aes.init(AES192, key, iv, CTR);
|
||||
defer aes.destroy();
|
||||
return aes.decrypt(allocator, data);
|
||||
}
|
||||
|
||||
fn char[] tdecrypt(char[24]* key, char[aes::BLOCKLEN] iv, char[] data)
|
||||
{
|
||||
return decrypt(tmem, key, iv, data);
|
||||
}
|
||||
|
||||
module std::crypto::aes256;
|
||||
import std::crypto::aes;
|
||||
|
||||
fn char[] encrypt(Allocator allocator, char[32]* key, char[aes::BLOCKLEN] iv, char[] data)
|
||||
{
|
||||
Aes aes @noinit;
|
||||
aes.init(AES256, key, iv, CTR);
|
||||
defer aes.destroy();
|
||||
return aes.encrypt(allocator, data);
|
||||
}
|
||||
|
||||
fn char[] tencrypt(char[32]* key, char[aes::BLOCKLEN] iv, char[] data)
|
||||
{
|
||||
return encrypt(tmem, key, iv, data);
|
||||
}
|
||||
|
||||
fn char[] decrypt(Allocator allocator, char[32]* key, char[aes::BLOCKLEN] iv, char[] data)
|
||||
{
|
||||
Aes aes @noinit;
|
||||
aes.init(AES256, key, iv, CTR);
|
||||
defer aes.destroy();
|
||||
return aes.decrypt(allocator, data);
|
||||
}
|
||||
|
||||
fn char[] tdecrypt(char[32]* key, char[aes::BLOCKLEN] iv, char[] data)
|
||||
{
|
||||
return decrypt(tmem, key, iv, data);
|
||||
}
|
||||
@@ -9,4 +9,3 @@ fn bool safe_compare(void* data1, void* data2, usz len)
|
||||
}
|
||||
return match == 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -212,7 +212,8 @@ fn F25519Int pack(Point* p)
|
||||
struct Unpacking
|
||||
{
|
||||
Point point;
|
||||
char on_curve; // Non-zero if true.
|
||||
<* Non-zero if true. *>
|
||||
char on_curve;
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -365,7 +366,7 @@ fn void F25519Int.normalize(&s)
|
||||
{
|
||||
s.reduce_carry((*s)[^1] >> 7);
|
||||
|
||||
// Substract p
|
||||
// Subtract p
|
||||
F25519Int sub @noinit;
|
||||
ushort c = 19;
|
||||
foreach (i, v : (*s)[:^1])
|
||||
@@ -399,7 +400,7 @@ fn char eq(F25519Int* a, F25519Int* b)
|
||||
}
|
||||
|
||||
<*
|
||||
Constant-time conditonal selection. Result is undefined if condition is neither 0 nor 1.
|
||||
Constant-time conditional selection. Result is undefined if condition is neither 0 nor 1.
|
||||
|
||||
@param [&in] zero : "selected if condition is 0"
|
||||
@param [&in] one : "selected if condition is 1"
|
||||
@@ -441,7 +442,7 @@ fn F25519Int F25519Int.add(&s, F25519Int* n) @operator(+)
|
||||
macro F25519Int F25519Int.@sub(&s, F25519Int #n) @operator(-) => s.sub(@addr(#n));
|
||||
|
||||
<*
|
||||
Substraction.
|
||||
Subtraction.
|
||||
|
||||
@param [&in] s
|
||||
@param [&in] n
|
||||
@@ -638,7 +639,7 @@ fn FBaseInt from_bytes(char[] bytes)
|
||||
}
|
||||
|
||||
<*
|
||||
Constant-time conditonal selection. Result is undefined if condition is neither 0 nor 1.
|
||||
Constant-time conditional selection. Result is undefined if condition is neither 0 nor 1.
|
||||
|
||||
@param [&in] zero : "selected if condition is 0"
|
||||
@param [&in] one : "selected if condition is 1"
|
||||
@@ -676,7 +677,7 @@ fn FBaseInt FBaseInt.add(&s, FBaseInt* n) @operator(+)
|
||||
}
|
||||
|
||||
<*
|
||||
Substraction if RHS is less than LHS else identity.
|
||||
Subtraction if RHS is less than LHS else identity.
|
||||
|
||||
@param [&in] s
|
||||
@param [&in] n
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
module std::hash::a5hash;
|
||||
|
||||
|
||||
macro @a5mul(#u, #v, #lo, #hi) @local
|
||||
macro void @a5mul(#u, #v, #lo, #hi) @local
|
||||
{
|
||||
uint128 imd = (uint128)#u * (uint128)#v;
|
||||
#lo = (ulong)imd;
|
||||
|
||||
@@ -83,7 +83,7 @@ fn char[HASH_BYTES] Hmac.final(&self)
|
||||
const IPAD @private = 0x36;
|
||||
const OPAD @private = 0x5C;
|
||||
|
||||
macro @derive(Hmac *hmac_start, char[] salt, uint iterations, usz index, char[] out)
|
||||
macro void @derive(Hmac *hmac_start, char[] salt, uint iterations, usz index, char[] out)
|
||||
{
|
||||
assert(out.len == HASH_BYTES);
|
||||
char[HASH_BYTES] tmp @noinit;
|
||||
@@ -104,4 +104,4 @@ macro @derive(Hmac *hmac_start, char[] salt, uint iterations, usz index, char[]
|
||||
out[i] ^= v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
module std::hash::komi;
|
||||
|
||||
|
||||
macro @komimul(#u, #v, #lo, #hi) @local
|
||||
macro void @komimul(#u, #v, #lo, #hi) @local
|
||||
{
|
||||
uint128 imd = (uint128)#u * (uint128)#v;
|
||||
#lo = (ulong)imd;
|
||||
|
||||
@@ -106,7 +106,7 @@ macro @h(x, y, z) => (x ^ y) ^ z;
|
||||
macro @h2(x, y, z) => x ^ (y ^ z);
|
||||
macro @i(x, y, z) => y ^ (x | ~z);
|
||||
|
||||
macro @step(#f, a, b, c, d, ptr, n, t, s)
|
||||
macro void @step(#f, a, b, c, d, ptr, n, t, s)
|
||||
{
|
||||
*a += #f(b, c, d) + @unaligned_load(*(uint *)&ptr[n * 4], 2) + t;
|
||||
*a = (*a << s) | ((*a & 0xffffffff) >> (32 - s));
|
||||
|
||||
@@ -103,13 +103,13 @@ union Long16 @local
|
||||
uint[16] l;
|
||||
}
|
||||
|
||||
macro blk(Long16* block, i) @local
|
||||
macro uint blk(Long16* block, i) @local
|
||||
{
|
||||
return (block.l[i & 15] = (block.l[(i + 13) & 15] ^ block.l[(i + 8) & 15]
|
||||
^ block.l[(i + 2) & 15] ^ block.l[i & 15]).rotl(1));
|
||||
}
|
||||
|
||||
macro blk0(Long16* block, i) @local
|
||||
macro uint blk0(Long16* block, i) @local
|
||||
{
|
||||
$if env::BIG_ENDIAN:
|
||||
return block.l[i];
|
||||
@@ -119,35 +119,35 @@ macro blk0(Long16* block, i) @local
|
||||
$endif
|
||||
}
|
||||
|
||||
macro r0(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local
|
||||
macro void r0(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local
|
||||
{
|
||||
var w = *wref;
|
||||
*z += ((w & (x ^ y)) ^ y) + blk0(block, i) + 0x5A827999 + v.rotl(5);
|
||||
*wref = w.rotl(30);
|
||||
}
|
||||
|
||||
macro r1(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local
|
||||
macro void r1(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local
|
||||
{
|
||||
var w = *wref;
|
||||
*z += ((w & (x ^ y)) ^ y) + blk(block, i) + 0x5A827999 + v.rotl(5);
|
||||
*wref = w.rotl(30);
|
||||
}
|
||||
|
||||
macro r2(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local
|
||||
macro void r2(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local
|
||||
{
|
||||
var w = *wref;
|
||||
*z += (w ^ x ^ y) + blk(block, i) + 0x6ED9EBA1 + v.rotl(5);
|
||||
*wref = w.rotl(30);
|
||||
}
|
||||
|
||||
macro r3(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local
|
||||
macro void r3(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local
|
||||
{
|
||||
var w = *wref;
|
||||
*z += (((w | x) & y) | (w & x)) + blk(block, i) + 0x8F1BBCDC + v.rotl(5);
|
||||
*wref = w.rotl(30);
|
||||
}
|
||||
|
||||
macro r4(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local
|
||||
macro void r4(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local
|
||||
{
|
||||
var w = *wref;
|
||||
*z += (w ^ x ^ y) + blk(block, i) + 0xCA62C1D6 + v.rotl(5);
|
||||
@@ -254,4 +254,4 @@ fn void sha1_transform(uint[5]* state, char* buffer) @local
|
||||
(*state)[4] += e;
|
||||
a = b = c = d = e = 0;
|
||||
block = {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,52 +1,52 @@
|
||||
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
//
|
||||
// SipHash is a secure pseudorandom function (PRF) which digests a 128-bit key
|
||||
// and a variable-length message to produce a 64- or 128-bit hash value.
|
||||
//
|
||||
// SipHash can be employed in numerous useful ways and structures, e.g.:
|
||||
// - Hash Tables
|
||||
// - Message Authentication Codes
|
||||
// - Denial of Service (hash flooding) resistance
|
||||
// - Bloom filters
|
||||
// - Keyed runtime identifier derivation
|
||||
//
|
||||
// Read more: https://en.wikipedia.org/wiki/SipHash
|
||||
//
|
||||
//
|
||||
|
||||
// COMMON HASH VARIANTS.
|
||||
// These two forms of SipHash (24 and 48) are the most widely
|
||||
// used by many implementations.
|
||||
|
||||
// These provide typical 64-bit hash results.
|
||||
// -- Best for performance-critical applications.
|
||||
<*
|
||||
Best for performance-critical applications.
|
||||
See std::hash::siphash for more information.
|
||||
*>
|
||||
module std::hash::siphash24;
|
||||
import std::hash::siphash;
|
||||
alias SipHash24 = SipHash { ulong, 2, 4 };
|
||||
alias hash = siphash::hash { ulong, 2, 4 };
|
||||
|
||||
// -- Best for conservative security applications.
|
||||
<*
|
||||
Best for security-focused applications.
|
||||
See std::hash::siphash for more information.
|
||||
*>
|
||||
module std::hash::siphash48;
|
||||
import std::hash::siphash;
|
||||
alias SipHash48 = SipHash { ulong, 4, 8 };
|
||||
alias hash = siphash::hash { ulong, 4, 8 };
|
||||
|
||||
|
||||
// Exact same as above, but for 128-bit outputs. Algorithm internally changes slightly.
|
||||
<* Exact same as siphash24, but for 128-bit outputs. Algorithm internally changes slightly. *>
|
||||
module std::hash::siphash24_128;
|
||||
import std::hash::siphash;
|
||||
alias SipHash24_128 = SipHash { uint128, 2, 4 };
|
||||
alias hash = siphash::hash { uint128, 2, 4 };
|
||||
|
||||
<* Exact same as siphash48, but for 128-bit outputs. Algorithm internally changes slightly. *>
|
||||
module std::hash::siphash48_128;
|
||||
import std::hash::siphash;
|
||||
alias SipHash48_128 = SipHash { uint128, 4, 8 };
|
||||
alias hash = siphash::hash { uint128, 4, 8 };
|
||||
|
||||
<*
|
||||
@require OutType.typeid == uint128.typeid || OutType.typeid == ulong.typeid : "Module OutType must be either uint128 or ulong."
|
||||
SipHash is a secure pseudorandom function (PRF) which digests a 128-bit key
|
||||
and a variable-length message to produce a 64- or 128-bit hash value.
|
||||
|
||||
SipHash can be employed in numerous useful ways and structures, e.g.:
|
||||
- Hash Tables
|
||||
- Message Authentication Codes
|
||||
- Denial of Service (hash flooding) resistance
|
||||
- Bloom filters
|
||||
- Keyed runtime identifier derivation
|
||||
|
||||
Read more: https://en.wikipedia.org/wiki/SipHash
|
||||
|
||||
@require OutType.typeid == uint128.typeid || OutType.typeid == ulong.typeid : "Module OutType must be either uint128 or ulong."
|
||||
*>
|
||||
module std::hash::siphash { OutType, BLOCK_ROUNDS, FINALIZE_ROUNDS };
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ fn char[] FileMmap.bytes(&self)
|
||||
}
|
||||
|
||||
<*
|
||||
Destroys the underlyng VirtualMemory object ie. calls munmap()"
|
||||
Destroys the underlying VirtualMemory object ie. calls munmap()"
|
||||
*>
|
||||
fn void? FileMmap.destroy(&self) @maydiscard
|
||||
{
|
||||
@@ -41,7 +41,7 @@ module std::io::file @if(env::LIBC &&& env::POSIX);
|
||||
@return? mem::OUT_OF_MEMORY, vm::ACCESS_DENIED, vm::RANGE_OVERFLOW, vm::INVALID_ARGS, vm::UNKNOWN_ERROR, io::NO_PERMISSION, io::FILE_NOT_VALID, io::WOULD_BLOCK, io::FILE_NOT_FOUND
|
||||
@return "Memory mapped region. Must be released with FileMmap.destroy(). Provided File will not be closed"
|
||||
*>
|
||||
fn mmap::FileMmap? mmap_file(File file, usz offset = 0, usz len = 0, vm::VirtualMemoryAccess access = READ, bool shared = false)
|
||||
fn FileMmap? mmap_file(File file, usz offset = 0, usz len = 0, VirtualMemoryAccess access = READ, bool shared = false)
|
||||
{
|
||||
if (len == 0)
|
||||
{
|
||||
@@ -78,7 +78,7 @@ fn mmap::FileMmap? mmap_file(File file, usz offset = 0, usz len = 0, vm::Virtual
|
||||
@return? mem::OUT_OF_MEMORY, vm::ACCESS_DENIED, vm::RANGE_OVERFLOW, vm::INVALID_ARGS, vm::UNKNOWN_ERROR, io::NO_PERMISSION, io::FILE_NOT_VALID, io::WOULD_BLOCK, io::FILE_NOT_FOUND
|
||||
@return "Memory mapped region. Must be released with FileMmap.destroy()"
|
||||
*>
|
||||
fn mmap::FileMmap? mmap_open(String filename, String mode, usz offset = 0, usz len = 0, vm::VirtualMemoryAccess access = READ, bool shared = false)
|
||||
fn FileMmap? mmap_open(String filename, String mode, usz offset = 0, usz len = 0, VirtualMemoryAccess access = READ, bool shared = false)
|
||||
{
|
||||
File file = open(filename, mode)!;
|
||||
defer catch (void)file.close();
|
||||
|
||||
@@ -141,8 +141,6 @@ fn usz? Formatter.out_str(&self, any arg) @private
|
||||
{
|
||||
switch (arg.type.kindof)
|
||||
{
|
||||
case TYPEID:
|
||||
return self.out_substr("typeid");
|
||||
case VOID:
|
||||
return self.out_substr("void");
|
||||
case FAULT:
|
||||
@@ -188,6 +186,8 @@ fn usz? Formatter.out_str(&self, any arg) @private
|
||||
if (@catch(n) != NOT_FOUND) n!;
|
||||
switch (arg.type.kindof)
|
||||
{
|
||||
case TYPEID:
|
||||
return self.out_substr("typeid[")! + self.ntoa((iptr)*(typeid*)arg, false, 16)! + self.out_substr("]")!;
|
||||
case ENUM:
|
||||
usz i = types::any_to_enum_ordinal(arg, usz)!!;
|
||||
assert(i < arg.type.names.len, "Illegal enum value found, numerical value was %d.", i);
|
||||
|
||||
@@ -22,7 +22,7 @@ fn usz? print_hex_chars(Formatter* f, char[] out, bool uppercase) @inline
|
||||
return len;
|
||||
}
|
||||
|
||||
macro Formatter.first_err(&self, fault f)
|
||||
macro fault Formatter.first_err(&self, fault f)
|
||||
{
|
||||
if (self.first_fault) return self.first_fault;
|
||||
self.first_fault = f;
|
||||
@@ -526,6 +526,19 @@ fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
|
||||
return len;
|
||||
}
|
||||
|
||||
const char[201] DIGIT_PAIRS @private =
|
||||
"00102030405060708090"
|
||||
"01112131415161718191"
|
||||
"02122232425262728292"
|
||||
"03132333435363738393"
|
||||
"04142434445464748494"
|
||||
"05152535455565758595"
|
||||
"06162636465666768696"
|
||||
"07172737475767778797"
|
||||
"08182838485868788898"
|
||||
"09192939495969798999";
|
||||
|
||||
|
||||
fn usz? Formatter.ntoa(&self, uint128 value, bool negative, uint base) @private
|
||||
{
|
||||
char[PRINTF_NTOA_BUFFER_SIZE] buf @noinit;
|
||||
@@ -538,14 +551,56 @@ fn usz? Formatter.ntoa(&self, uint128 value, bool negative, uint base) @private
|
||||
if (!self.flags.precision || value)
|
||||
{
|
||||
char past_10 = (self.flags.uppercase ? 'A' : 'a') - 10;
|
||||
do
|
||||
switch (base)
|
||||
{
|
||||
if (len >= PRINTF_NTOA_BUFFER_SIZE) return INTERNAL_BUFFER_EXCEEDED?;
|
||||
char digit = (char)(value % base);
|
||||
buf[len++] = digit + (digit < 10 ? '0' : past_10);
|
||||
value /= base;
|
||||
case 2:
|
||||
do
|
||||
{
|
||||
if (len >= PRINTF_NTOA_BUFFER_SIZE) return INTERNAL_BUFFER_EXCEEDED?;
|
||||
buf[len++] = '0' + (char)value & 1;
|
||||
value >>= 1;
|
||||
}
|
||||
while (value);
|
||||
case 10:
|
||||
if (!value)
|
||||
{
|
||||
if (len >= PRINTF_NTOA_BUFFER_SIZE) return INTERNAL_BUFFER_EXCEEDED?;
|
||||
buf[len++] = '0';
|
||||
break;
|
||||
}
|
||||
while (value >= 10)
|
||||
{
|
||||
if (len + 1 >= PRINTF_NTOA_BUFFER_SIZE) return INTERNAL_BUFFER_EXCEEDED?;
|
||||
char digit = (char)(value % 100);
|
||||
buf[len:2] = DIGIT_PAIRS[2 * digit:2];
|
||||
len += 2;
|
||||
value /= 100;
|
||||
}
|
||||
if (value > 0)
|
||||
{
|
||||
if (len >= PRINTF_NTOA_BUFFER_SIZE) return INTERNAL_BUFFER_EXCEEDED?;
|
||||
buf[len++] = '0' + (char)value;
|
||||
}
|
||||
case 16:
|
||||
do
|
||||
{
|
||||
if (len >= PRINTF_NTOA_BUFFER_SIZE) return INTERNAL_BUFFER_EXCEEDED?;
|
||||
char digit = (char)value & 0xF;
|
||||
buf[len++] = digit + (digit < 10 ? '0' : past_10);
|
||||
value >>= 4;
|
||||
}
|
||||
while (value);
|
||||
case 8:
|
||||
do
|
||||
{
|
||||
if (len >= PRINTF_NTOA_BUFFER_SIZE) return INTERNAL_BUFFER_EXCEEDED?;
|
||||
buf[len++] = '0' + (char)value & 0x7;
|
||||
value >>= 3;
|
||||
}
|
||||
while (value);
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
while (value);
|
||||
}
|
||||
return self.ntoa_format((String)buf[:PRINTF_NTOA_BUFFER_SIZE], len, negative, base);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ faultdef
|
||||
NO_PERMISSION,
|
||||
OUT_OF_SPACE,
|
||||
OVERFLOW,
|
||||
PATH_COULD_NOT_BE_FOUND,
|
||||
READ_ONLY,
|
||||
SYMLINK_FAILED,
|
||||
TOO_MANY_DESCRIPTORS,
|
||||
@@ -266,7 +267,32 @@ fn void? out_putstream_fn(void* data, char c) @private
|
||||
return (*stream).write_byte(c);
|
||||
}
|
||||
|
||||
fn void? out_putchar_fn(void* data @unused, char c) @private
|
||||
macro usz putchar_buf_size() @const
|
||||
{
|
||||
$switch env::MEMORY_ENV:
|
||||
$case NORMAL: return 32 * 1024;
|
||||
$case SMALL: return 1024;
|
||||
$case TINY: return 256;
|
||||
$case NONE: return 256;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
struct PutcharBuffer
|
||||
{
|
||||
char[putchar_buf_size()] data;
|
||||
usz len;
|
||||
bool should_flush;
|
||||
}
|
||||
|
||||
fn void? write_putchar_buffer(PutcharBuffer* buff, bool flush) @private
|
||||
{
|
||||
File* stdout = io::stdout();
|
||||
libc::fwrite(&buff.data, 1, buff.len, stdout.file);
|
||||
buff.len = 0;
|
||||
if (flush) stdout.flush()!;
|
||||
}
|
||||
|
||||
fn void? out_putchar_buffer_fn(void* data @unused, char c) @private
|
||||
{
|
||||
$if env::TESTING:
|
||||
// HACK: this is used for the purpose of unit test output hijacking
|
||||
@@ -274,7 +300,10 @@ fn void? out_putchar_fn(void* data @unused, char c) @private
|
||||
assert(stdout.file);
|
||||
libc::fputc(c, stdout.file);
|
||||
$else
|
||||
libc::putchar(c);
|
||||
PutcharBuffer* buff = data;
|
||||
buff.data[buff.len++] = c;
|
||||
if (c == '\n') buff.should_flush = true;
|
||||
if (buff.len == buff.data.len) write_putchar_buffer(buff, false)!;
|
||||
$endif
|
||||
}
|
||||
|
||||
@@ -295,8 +324,11 @@ fn void? out_putchar_fn(void* data @unused, char c) @private
|
||||
fn usz? printf(String format, args...) @format(0) @maydiscard
|
||||
{
|
||||
Formatter formatter;
|
||||
formatter.init(&out_putchar_fn);
|
||||
return formatter.vprintf(format, args);
|
||||
PutcharBuffer buff;
|
||||
formatter.init(&out_putchar_buffer_fn, &buff);
|
||||
usz? len = formatter.vprintf(format, args);
|
||||
write_putchar_buffer(&buff, buff.should_flush)!;
|
||||
return len;
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -309,10 +341,11 @@ fn usz? printf(String format, args...) @format(0) @maydiscard
|
||||
fn usz? printfn(String format, args...) @format(0) @maydiscard
|
||||
{
|
||||
Formatter formatter;
|
||||
formatter.init(&out_putchar_fn);
|
||||
PutcharBuffer buff;
|
||||
formatter.init(&out_putchar_buffer_fn, &buff);
|
||||
usz? len = formatter.vprintf(format, args);
|
||||
out_putchar_fn(null, '\n')!;
|
||||
io::stdout().flush()!;
|
||||
formatter.out('\n')!;
|
||||
write_putchar_buffer(&buff, true)!;
|
||||
return len + 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,112 @@
|
||||
module std::io::os;
|
||||
|
||||
enum NativeSystemDir
|
||||
{
|
||||
DESKTOP,
|
||||
DOCUMENTS,
|
||||
VIDEOS,
|
||||
MUSIC,
|
||||
DOWNLOADS,
|
||||
PICTURES,
|
||||
TEMPLATES,
|
||||
PUBLIC_SHARE,
|
||||
SAVED_GAMES,
|
||||
SCREENSHOTS
|
||||
}
|
||||
|
||||
module std::io::os @if(env::LIBC);
|
||||
import std::io::path, std::os;
|
||||
import std::io, std::os;
|
||||
|
||||
fn String? win32_get_known_folder_temp(Win32_REFKNOWNFOLDERID rfid) @private @if(env::WIN32)
|
||||
{
|
||||
Win32_PWSTR path;
|
||||
Win32_HRESULT res = win32::shGetKnownFolderPath(rfid, 0x00008000 /* KF_FLAG_CREATE */, null, &path);
|
||||
if (res) return io::PATH_COULD_NOT_BE_FOUND?;
|
||||
return string::from_wstring(tmem, (WString)path);
|
||||
}
|
||||
|
||||
fn Path? native_home_directory(Allocator allocator) => @pool()
|
||||
{
|
||||
$switch env::OS_TYPE:
|
||||
$case IOS:
|
||||
$case MACOS:
|
||||
$case TVOS:
|
||||
$case WATCHOS:
|
||||
$case FREEBSD:
|
||||
$case KFREEBSD:
|
||||
$case LINUX:
|
||||
$case NETBSD:
|
||||
$case OPENBSD:
|
||||
$case HAIKU:
|
||||
return path::new(allocator, env::tget_var("HOME")) ?? io::PATH_COULD_NOT_BE_FOUND?;
|
||||
$case WIN32:
|
||||
return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_PROFILE));
|
||||
$default:
|
||||
return io::PATH_COULD_NOT_BE_FOUND?;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
fn Path? native_user_directory(Allocator allocator, NativeSystemDir dir) => @pool()
|
||||
{
|
||||
$switch env::OS_TYPE:
|
||||
$case FREEBSD:
|
||||
$case KFREEBSD:
|
||||
$case LINUX:
|
||||
$case NETBSD:
|
||||
$case OPENBSD:
|
||||
$case HAIKU:
|
||||
switch (dir)
|
||||
{
|
||||
case DESKTOP: return path::new(allocator, posix::xdg_user_dir_lookup(tmem, "DESKTOP"));
|
||||
case DOWNLOADS: return path::new(allocator, posix::xdg_user_dir_lookup(tmem, "DOWNLOAD"));
|
||||
case DOCUMENTS: return path::new(allocator, posix::xdg_user_dir_lookup(tmem, "DOCUMENTS"));
|
||||
case MUSIC: return path::new(allocator, posix::xdg_user_dir_lookup(tmem, "MUSIC"));
|
||||
case VIDEOS: return path::new(allocator, posix::xdg_user_dir_lookup(tmem, "VIDEOS"));
|
||||
case PICTURES: return path::new(allocator, posix::xdg_user_dir_lookup(tmem, "PICTURES"));
|
||||
case PUBLIC_SHARE: return path::new(allocator, posix::xdg_user_dir_lookup(tmem, "PUBLICSHARE"));
|
||||
case TEMPLATES: return path::new(allocator, posix::xdg_user_dir_lookup(tmem, "TEMPLATES"));
|
||||
case SAVED_GAMES:
|
||||
case SCREENSHOTS: nextcase;
|
||||
default: return io::PATH_COULD_NOT_BE_FOUND?;
|
||||
}
|
||||
$case IOS:
|
||||
$case MACOS:
|
||||
$case WATCHOS:
|
||||
$case TVOS:
|
||||
switch (dir)
|
||||
{
|
||||
case DESKTOP: return path::new(allocator, darwin::find_first_directory_temp(DESKTOP, USER));
|
||||
case DOWNLOADS: return path::new(allocator, darwin::find_first_directory_temp(DOWNLOADS, USER));
|
||||
case DOCUMENTS: return path::new(allocator, darwin::find_first_directory_temp(DOCUMENT, USER));
|
||||
case MUSIC: return path::new(allocator, darwin::find_first_directory_temp(MUSIC, USER));
|
||||
case VIDEOS: return path::new(allocator, darwin::find_first_directory_temp(MOVIES, USER));
|
||||
case PICTURES: return path::new(allocator, darwin::find_first_directory_temp(PICTURES, USER));
|
||||
case PUBLIC_SHARE: return path::new(allocator, darwin::find_first_directory_temp(SHARED_PUBLIC, USER));
|
||||
case SAVED_GAMES:
|
||||
case SCREENSHOTS:
|
||||
case TEMPLATES: nextcase;
|
||||
default: return io::PATH_COULD_NOT_BE_FOUND?;
|
||||
}
|
||||
$case WIN32:
|
||||
switch (dir)
|
||||
{
|
||||
case DOWNLOADS: return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_DOWNLOADS));
|
||||
case DOCUMENTS: return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_DOCUMENTS));
|
||||
case DESKTOP: return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_DESKTOP));
|
||||
case MUSIC: return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_MUSIC));
|
||||
case VIDEOS: return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_VIDEOS));
|
||||
case PICTURES: return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_PICTURES));
|
||||
case SAVED_GAMES: return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_SAVED_GAMES));
|
||||
case SCREENSHOTS: return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_SCREENSHOTS));
|
||||
case TEMPLATES: return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_TEMPLATES));
|
||||
case PUBLIC_SHARE: nextcase;
|
||||
default: return io::PATH_COULD_NOT_BE_FOUND?;
|
||||
}
|
||||
$default:
|
||||
return io::PATH_COULD_NOT_BE_FOUND?;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
|
||||
fn Path? native_temp_directory(Allocator allocator) @if(!env::WIN32)
|
||||
{
|
||||
@@ -23,7 +130,6 @@ fn Path? native_temp_directory(Allocator allocator) @if(env::WIN32) => @pool()
|
||||
module std::io::os @if(env::NO_LIBC);
|
||||
import std::io::path;
|
||||
|
||||
macro Path? native_temp_directory(Allocator allocator)
|
||||
{
|
||||
return io::UNSUPPORTED_OPERATION?;
|
||||
}
|
||||
macro Path? native_home_directory(Allocator allocator) => io::PATH_COULD_NOT_BE_FOUND?;
|
||||
macro Path? native_temp_directory(Allocator allocator) => io::PATH_COULD_NOT_BE_FOUND?;
|
||||
fn Path? native_user_directory(Allocator allocator, NativeSystemDir dir) => io::PATH_COULD_NOT_BE_FOUND?;
|
||||
|
||||
@@ -57,6 +57,18 @@ macro void? chdir(path)
|
||||
|
||||
fn Path? temp_directory(Allocator allocator) => os::native_temp_directory(allocator);
|
||||
|
||||
fn Path? home_directory(Allocator allocator) => os::native_home_directory(allocator);
|
||||
fn Path? desktop_directory(Allocator allocator) => os::native_user_directory(allocator, DESKTOP);
|
||||
fn Path? videos_directory(Allocator allocator) => os::native_user_directory(allocator, VIDEOS);
|
||||
fn Path? music_directory(Allocator allocator) => os::native_user_directory(allocator, MUSIC);
|
||||
fn Path? documents_directory(Allocator allocator) => os::native_user_directory(allocator, DOCUMENTS);
|
||||
fn Path? screenshots_directory(Allocator allocator) => os::native_user_directory(allocator, SCREENSHOTS);
|
||||
fn Path? saved_games_directory(Allocator allocator) => os::native_user_directory(allocator, SAVED_GAMES);
|
||||
fn Path? downloads_directory(Allocator allocator) => os::native_user_directory(allocator, DOWNLOADS);
|
||||
fn Path? pictures_directory(Allocator allocator) => os::native_user_directory(allocator, PICTURES);
|
||||
fn Path? templates_directory(Allocator allocator) => os::native_user_directory(allocator, TEMPLATES);
|
||||
fn Path? public_share_directory(Allocator allocator) => os::native_user_directory(allocator, PUBLIC_SHARE);
|
||||
|
||||
fn void? delete(Path path) => os::native_remove(path.str_view()) @inline;
|
||||
|
||||
macro bool @is_pathlike(#path) @const => $typeof(#path) == String ||| $typeof(#path) == Path;
|
||||
@@ -174,13 +186,11 @@ fn bool Path.equals(self, Path p2) @operator(==)
|
||||
fn Path? Path.append(self, Allocator allocator, String filename)
|
||||
{
|
||||
if (!self.path_string.len) return new(allocator, filename, self.env)!;
|
||||
assert(!is_separator(self.path_string[^1], self.env));
|
||||
|
||||
@pool()
|
||||
{
|
||||
DString dstr = dstring::temp_with_capacity(self.path_string.len + 1 + filename.len);
|
||||
dstr.append(self.path_string);
|
||||
dstr.append(PREFERRED_SEPARATOR);
|
||||
if (!is_separator(self.path_string[^1], self.env)) dstr.append(PREFERRED_SEPARATOR);
|
||||
dstr.append(filename);
|
||||
return new(allocator, dstr.str_view(), self.env);
|
||||
};
|
||||
@@ -387,6 +397,19 @@ fn Path? Path.parent(self)
|
||||
{
|
||||
if (is_separator(c, self.env))
|
||||
{
|
||||
if (i == 0) return { self.path_string[..0], self.env, null };
|
||||
if (self.env == WIN32 && i > 1)
|
||||
{
|
||||
if (try volume_len = volume_name_len(self.path_string, WIN32))
|
||||
{
|
||||
// Handle C:\foo
|
||||
if (volume_len == i)
|
||||
{
|
||||
if (i + 1 == self.path_string.len) return NO_PARENT?;
|
||||
return { self.path_string[:i + 1], WIN32, null };
|
||||
}
|
||||
}
|
||||
}
|
||||
return { self.path_string[:i], self.env, null };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
module std::io;
|
||||
import std::math;
|
||||
import std::core::env;
|
||||
|
||||
|
||||
interface InStream
|
||||
{
|
||||
@@ -114,6 +116,9 @@ macro usz? write_all(stream, char[] buffer)
|
||||
return n;
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_instream(s)
|
||||
*>
|
||||
macro usz? read_using_read_byte(s, char[] buffer)
|
||||
{
|
||||
usz len = 0;
|
||||
@@ -131,12 +136,18 @@ macro usz? read_using_read_byte(s, char[] buffer)
|
||||
return len;
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_outstream(s)
|
||||
*>
|
||||
macro void? write_byte_using_write(s, char c)
|
||||
{
|
||||
char[1] buff = { c };
|
||||
s.write(&buff)!;
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_instream(s)
|
||||
*>
|
||||
macro char? read_byte_using_read(s)
|
||||
{
|
||||
char[1] buffer;
|
||||
@@ -147,10 +158,12 @@ macro char? read_byte_using_read(s)
|
||||
|
||||
alias ReadByteFn = fn char?();
|
||||
|
||||
|
||||
<*
|
||||
@require @is_outstream(s)
|
||||
*>
|
||||
macro usz? write_using_write_byte(s, char[] bytes)
|
||||
{
|
||||
foreach (c : bytes) s.write_byte(self, c)!;
|
||||
foreach (c : bytes) s.write_byte(c)!;
|
||||
return bytes.len;
|
||||
}
|
||||
|
||||
@@ -260,6 +273,16 @@ macro ushort? read_be_ushort(stream)
|
||||
return (ushort)(hi_byte << 8 | lo_byte);
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
macro ushort? read_le_ushort(stream)
|
||||
{
|
||||
char lo_byte = stream.read_byte()!;
|
||||
char hi_byte = stream.read_byte()!;
|
||||
return (ushort)(hi_byte << 8 | lo_byte);
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
@@ -268,6 +291,14 @@ macro short? read_be_short(stream)
|
||||
return read_be_ushort(stream);
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
macro short? read_le_short(stream)
|
||||
{
|
||||
return read_le_ushort(stream);
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_outstream(stream)
|
||||
*>
|
||||
@@ -277,6 +308,15 @@ macro void? write_be_short(stream, ushort s)
|
||||
stream.write_byte((char)s)!;
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_outstream(stream)
|
||||
*>
|
||||
macro void? write_le_short(stream, ushort s)
|
||||
{
|
||||
stream.write_byte((char)s)!;
|
||||
stream.write_byte((char)(s >> 8))!;
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
@@ -288,6 +328,17 @@ macro uint? read_be_uint(stream)
|
||||
return val + stream.read_byte()!;
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
macro uint? read_le_uint(stream)
|
||||
{
|
||||
uint val = stream.read_byte()!;
|
||||
val += stream.read_byte()! << 8;
|
||||
val += stream.read_byte()! << 16;
|
||||
return val + stream.read_byte()! << 24;
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
@@ -296,6 +347,14 @@ macro int? read_be_int(stream)
|
||||
return read_be_uint(stream);
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
macro int? read_le_int(stream)
|
||||
{
|
||||
return read_le_uint(stream);
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_outstream(stream)
|
||||
*>
|
||||
@@ -307,6 +366,17 @@ macro void? write_be_int(stream, uint s)
|
||||
stream.write_byte((char)s)!;
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_outstream(stream)
|
||||
*>
|
||||
macro void? write_le_int(stream, uint s)
|
||||
{
|
||||
stream.write_byte((char)s)!;
|
||||
stream.write_byte((char)(s >> 8))!;
|
||||
stream.write_byte((char)(s >> 16))!;
|
||||
stream.write_byte((char)(s >> 24))!;
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
@@ -322,6 +392,21 @@ macro ulong? read_be_ulong(stream)
|
||||
return val + stream.read_byte()!;
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
macro ulong? read_le_ulong(stream)
|
||||
{
|
||||
ulong val = (ulong)stream.read_byte()!;
|
||||
val += (ulong)stream.read_byte()! << 8;
|
||||
val += (ulong)stream.read_byte()! << 16;
|
||||
val += (ulong)stream.read_byte()! << 24;
|
||||
val += (ulong)stream.read_byte()! << 32;
|
||||
val += (ulong)stream.read_byte()! << 40;
|
||||
val += (ulong)stream.read_byte()! << 48;
|
||||
return val + (ulong)stream.read_byte()! << 56;
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
@@ -330,6 +415,14 @@ macro long? read_be_long(stream)
|
||||
return read_be_ulong(stream);
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
macro long? read_le_long(stream)
|
||||
{
|
||||
return read_le_ulong(stream);
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_outstream(stream)
|
||||
*>
|
||||
@@ -345,6 +438,21 @@ macro void? write_be_long(stream, ulong s)
|
||||
stream.write_byte((char)s)!;
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_outstream(stream)
|
||||
*>
|
||||
macro void? write_le_long(stream, ulong s)
|
||||
{
|
||||
stream.write_byte((char)s)!;
|
||||
stream.write_byte((char)(s >> 8))!;
|
||||
stream.write_byte((char)(s >> 16))!;
|
||||
stream.write_byte((char)(s >> 24))!;
|
||||
stream.write_byte((char)(s >> 32))!;
|
||||
stream.write_byte((char)(s >> 40))!;
|
||||
stream.write_byte((char)(s >> 48))!;
|
||||
stream.write_byte((char)(s >> 56))!;
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
@@ -368,6 +476,29 @@ macro uint128? read_be_uint128(stream)
|
||||
return val + stream.read_byte()!;
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
macro uint128? read_le_uint128(stream)
|
||||
{
|
||||
uint128 val = stream.read_byte()!;
|
||||
val += (uint128)stream.read_byte()! << 8;
|
||||
val += (uint128)stream.read_byte()! << 16;
|
||||
val += (uint128)stream.read_byte()! << 24;
|
||||
val += (uint128)stream.read_byte()! << 32;
|
||||
val += (uint128)stream.read_byte()! << 40;
|
||||
val += (uint128)stream.read_byte()! << 48;
|
||||
val += (uint128)stream.read_byte()! << 56;
|
||||
val += (uint128)stream.read_byte()! << 64;
|
||||
val += (uint128)stream.read_byte()! << 72;
|
||||
val += (uint128)stream.read_byte()! << 80;
|
||||
val += (uint128)stream.read_byte()! << 88;
|
||||
val += (uint128)stream.read_byte()! << 96;
|
||||
val += (uint128)stream.read_byte()! << 104;
|
||||
val += (uint128)stream.read_byte()! << 112;
|
||||
return val + (uint128)stream.read_byte()! << 120;
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
@@ -376,6 +507,14 @@ macro int128? read_be_int128(stream)
|
||||
return read_be_uint128(stream);
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
macro int128? read_le_int128(stream)
|
||||
{
|
||||
return read_le_uint128(stream);
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_outstream(stream)
|
||||
*>
|
||||
@@ -399,6 +538,30 @@ macro void? write_be_int128(stream, uint128 s)
|
||||
stream.write_byte((char)s)!;
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_outstream(stream)
|
||||
*>
|
||||
macro void? write_le_int128(stream, uint128 s)
|
||||
{
|
||||
stream.write_byte((char)s)!;
|
||||
stream.write_byte((char)(s >> 8))!;
|
||||
stream.write_byte((char)(s >> 16))!;
|
||||
stream.write_byte((char)(s >> 24))!;
|
||||
stream.write_byte((char)(s >> 32))!;
|
||||
stream.write_byte((char)(s >> 40))!;
|
||||
stream.write_byte((char)(s >> 48))!;
|
||||
stream.write_byte((char)(s >> 56))!;
|
||||
stream.write_byte((char)(s >> 64))!;
|
||||
stream.write_byte((char)(s >> 72))!;
|
||||
stream.write_byte((char)(s >> 80))!;
|
||||
stream.write_byte((char)(s >> 88))!;
|
||||
stream.write_byte((char)(s >> 96))!;
|
||||
stream.write_byte((char)(s >> 104))!;
|
||||
stream.write_byte((char)(s >> 112))!;
|
||||
stream.write_byte((char)(s >> 120))!;
|
||||
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_outstream(stream)
|
||||
@require data.len < 256 : "Data exceeded 255"
|
||||
@@ -443,6 +606,34 @@ macro char[]? read_short_bytearray(stream, Allocator allocator)
|
||||
return data;
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_instream(stream)
|
||||
*>
|
||||
macro void? skip(stream, usz bytes)
|
||||
{
|
||||
if (!bytes) return;
|
||||
$switch:
|
||||
$case !$defined(stream.seek):
|
||||
for (usz i = 0; i < bytes; i++)
|
||||
{
|
||||
stream.read()!;
|
||||
}
|
||||
return;
|
||||
$case $typeof(stream) == InStream:
|
||||
if (!&stream.seek)
|
||||
{
|
||||
for (usz i = 0; i < bytes; i++)
|
||||
{
|
||||
stream.read()!;
|
||||
}
|
||||
return;
|
||||
}
|
||||
stream.seek(bytes, CURSOR)!;
|
||||
$default:
|
||||
stream.seek(bytes, CURSOR)!;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
<*
|
||||
Wrap bytes for reading using io functions.
|
||||
*>
|
||||
|
||||
@@ -13,7 +13,7 @@ struct ByteBuffer (InStream, OutStream)
|
||||
|
||||
<*
|
||||
ByteBuffer provides a streamable read/write buffer.
|
||||
max_read defines how many bytes might be kept before its internal buffer is shrinked.
|
||||
max_read defines how many bytes might be kept before its internal buffer is shrunk.
|
||||
@require self.bytes.len == 0 : "Buffer already initialized."
|
||||
*>
|
||||
fn ByteBuffer* ByteBuffer.init(&self, Allocator allocator, usz max_read, usz initial_capacity = 16)
|
||||
@@ -135,7 +135,7 @@ fn void ByteBuffer.grow(&self, usz n)
|
||||
self.bytes = p[:n];
|
||||
}
|
||||
|
||||
macro ByteBuffer.shrink(&self)
|
||||
macro void ByteBuffer.shrink(&self)
|
||||
{
|
||||
if (self.read_idx >= self.max_read)
|
||||
{
|
||||
@@ -145,4 +145,4 @@ macro ByteBuffer.shrink(&self)
|
||||
self.write_idx = 1 + readable;
|
||||
self.read_idx = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ extern fn CInt close(CInt fd) @if(!env::WIN32);
|
||||
extern fn double difftime(Time_t time1, Time_t time2) @if(!env::WIN32);
|
||||
extern fn DivResult div(CInt numer, CInt denom);
|
||||
extern fn void exit(CInt status);
|
||||
extern fn void _exit(CInt status) @extern("_Exit");
|
||||
extern fn void _exit(CInt status) @cname("_Exit");
|
||||
extern fn CInt fclose(CFile stream);
|
||||
extern fn CFile fdopen(CInt fd, ZString mode) @if(!env::WIN32);
|
||||
extern fn CInt feof(CFile stream);
|
||||
@@ -211,9 +211,9 @@ const CInt STDOUT_FD = 1;
|
||||
const CInt STDERR_FD = 2;
|
||||
|
||||
module libc @if(env::LINUX || env::ANDROID);
|
||||
extern CFile __stdin @extern("stdin");
|
||||
extern CFile __stdout @extern("stdout");
|
||||
extern CFile __stderr @extern("stderr");
|
||||
extern CFile __stdin @cname("stdin");
|
||||
extern CFile __stdout @cname("stdout");
|
||||
extern CFile __stderr @cname("stderr");
|
||||
extern fn usz malloc_usable_size(void* ptr);
|
||||
macro usz malloc_size(void* ptr) => malloc_usable_size(ptr);
|
||||
extern fn void* aligned_alloc(usz align, usz size);
|
||||
@@ -262,30 +262,30 @@ module libc @if(!env::LIBC);
|
||||
import std::core::mem;
|
||||
|
||||
|
||||
fn void longjmp(JmpBuf* buffer, CInt value) @weak @extern("longjmp") @nostrip
|
||||
fn void longjmp(JmpBuf* buffer, CInt value) @weak @cname("longjmp") @nostrip
|
||||
{
|
||||
unreachable("longjmp unavailable");
|
||||
}
|
||||
|
||||
fn CInt setjmp(JmpBuf* buffer) @weak @extern("setjmp") @nostrip
|
||||
fn CInt setjmp(JmpBuf* buffer) @weak @cname("setjmp") @nostrip
|
||||
{
|
||||
unreachable("setjmp unavailable");
|
||||
}
|
||||
|
||||
fn void* malloc(usz size) @weak @extern("malloc") @nostrip
|
||||
fn void* malloc(usz size) @weak @cname("malloc") @nostrip
|
||||
{
|
||||
unreachable("malloc unavailable");
|
||||
}
|
||||
fn void* calloc(usz count, usz size) @weak @extern("calloc") @nostrip
|
||||
fn void* calloc(usz count, usz size) @weak @cname("calloc") @nostrip
|
||||
{
|
||||
unreachable("calloc unavailable");
|
||||
}
|
||||
fn void* free(void*) @weak @extern("free")
|
||||
fn void* free(void*) @weak @cname("free")
|
||||
{
|
||||
unreachable("free unavailable");
|
||||
}
|
||||
|
||||
fn void* realloc(void* ptr, usz size) @weak @extern("realloc") @nostrip
|
||||
fn void* realloc(void* ptr, usz size) @weak @cname("realloc") @nostrip
|
||||
{
|
||||
unreachable("realloc unavailable");
|
||||
}
|
||||
@@ -294,69 +294,69 @@ alias memcpy = mem::__memcpy;
|
||||
alias memmove = mem::__memcpy;
|
||||
alias memset = mem::__memset;
|
||||
|
||||
fn int fseek(CFile stream, SeekIndex offset, int whence) @weak @extern("fseek") @nostrip
|
||||
fn int fseek(CFile stream, SeekIndex offset, int whence) @weak @cname("fseek") @nostrip
|
||||
{
|
||||
unreachable("'fseek' not available.");
|
||||
}
|
||||
fn CFile fopen(ZString filename, ZString mode) @weak @extern("fopen") @nostrip
|
||||
fn CFile fopen(ZString filename, ZString mode) @weak @cname("fopen") @nostrip
|
||||
{
|
||||
unreachable("'fopen' not available.");
|
||||
}
|
||||
|
||||
fn CFile freopen(ZString filename, ZString mode, CFile stream) @weak @extern("fopen") @nostrip
|
||||
fn CFile freopen(ZString filename, ZString mode, CFile stream) @weak @cname("fopen") @nostrip
|
||||
{
|
||||
unreachable("'freopen' not available.");
|
||||
}
|
||||
|
||||
fn usz fwrite(void* ptr, usz size, usz nmemb, CFile stream) @weak @extern("fwrite") @nostrip
|
||||
fn usz fwrite(void* ptr, usz size, usz nmemb, CFile stream) @weak @cname("fwrite") @nostrip
|
||||
{
|
||||
unreachable("'fwrite' not available.");
|
||||
}
|
||||
|
||||
fn usz fread(void* ptr, usz size, usz nmemb, CFile stream) @weak @extern("fread") @nostrip
|
||||
fn usz fread(void* ptr, usz size, usz nmemb, CFile stream) @weak @cname("fread") @nostrip
|
||||
{
|
||||
unreachable("'fread' not available.");
|
||||
}
|
||||
|
||||
fn CFile fclose(CFile) @weak @extern("fclose") @nostrip
|
||||
fn CFile fclose(CFile) @weak @cname("fclose") @nostrip
|
||||
{
|
||||
unreachable("'fclose' not available.");
|
||||
}
|
||||
|
||||
fn int fflush(CFile stream) @weak @extern("fflush") @nostrip
|
||||
fn int fflush(CFile stream) @weak @cname("fflush") @nostrip
|
||||
{
|
||||
unreachable("'fflush' not available.");
|
||||
}
|
||||
|
||||
fn int fputc(int c, CFile stream) @weak @extern("fputc") @nostrip
|
||||
fn int fputc(int c, CFile stream) @weak @cname("fputc") @nostrip
|
||||
{
|
||||
unreachable("'fputc' not available.");
|
||||
}
|
||||
|
||||
fn char* fgets(ZString str, int n, CFile stream) @weak @extern("fgets") @nostrip
|
||||
fn char* fgets(ZString str, int n, CFile stream) @weak @cname("fgets") @nostrip
|
||||
{
|
||||
unreachable("'fgets' not available.");
|
||||
}
|
||||
|
||||
fn int fgetc(CFile stream) @weak @extern("fgetc") @nostrip
|
||||
fn int fgetc(CFile stream) @weak @cname("fgetc") @nostrip
|
||||
{
|
||||
unreachable("'fgetc' not available.");
|
||||
}
|
||||
|
||||
fn int feof(CFile stream) @weak @extern("feof") @nostrip
|
||||
fn int feof(CFile stream) @weak @cname("feof") @nostrip
|
||||
{
|
||||
unreachable("'feof' not available.");
|
||||
}
|
||||
|
||||
fn int putc(int c, CFile stream) @weak @extern("putc") @nostrip
|
||||
fn int putc(int c, CFile stream) @weak @cname("putc") @nostrip
|
||||
{
|
||||
unreachable("'putc' not available.");
|
||||
}
|
||||
fn int putchar(int c) @weak @extern("putchar") @nostrip
|
||||
fn int putchar(int c) @weak @cname("putchar") @nostrip
|
||||
{
|
||||
unreachable("'putchar' not available.");
|
||||
}
|
||||
fn int puts(ZString str) @weak @extern("puts") @nostrip
|
||||
fn int puts(ZString str) @weak @cname("puts") @nostrip
|
||||
{
|
||||
unreachable("'puts' not available.");
|
||||
}
|
||||
@@ -407,17 +407,28 @@ alias SeekIndex = CLong;
|
||||
|
||||
struct Tm
|
||||
{
|
||||
CInt tm_sec; // seconds after the minute [0-60]
|
||||
CInt tm_min; // minutes after the hour [0-59]
|
||||
CInt tm_hour; // hours since midnight [0-23]
|
||||
CInt tm_mday; // day of the month [1-31]
|
||||
CInt tm_mon; // months since January [0-11]
|
||||
CInt tm_year; // years since 1900
|
||||
CInt tm_wday; // days since Sunday [0-6]
|
||||
CInt tm_yday; // days since January 1 [0-365]
|
||||
CInt tm_isdst; // Daylight Savings Time flag
|
||||
TimeOffset tm_gmtoff @if(!env::WIN32); /* offset from UTC in seconds */
|
||||
char* tm_zone @if(!env::WIN32); /* timezone abbreviation */
|
||||
<* seconds after the minute [0-60] *>
|
||||
CInt tm_sec;
|
||||
<* minutes after the hour [0-59] *>
|
||||
CInt tm_min;
|
||||
<* hours since midnight [0-23] *>
|
||||
CInt tm_hour;
|
||||
<* day of the month [1-31] *>
|
||||
CInt tm_mday;
|
||||
<* months since January [0-11] *>
|
||||
CInt tm_mon;
|
||||
<* years since 1900 *>
|
||||
CInt tm_year;
|
||||
<* days since Sunday [0-6] *>
|
||||
CInt tm_wday;
|
||||
<* days since January 1 [0-365] *>
|
||||
CInt tm_yday;
|
||||
<* Daylight Savings Time flag *>
|
||||
CInt tm_isdst;
|
||||
<* offset from UTC in seconds *>
|
||||
TimeOffset tm_gmtoff @if(!env::WIN32);
|
||||
<* timezone abbreviation *>
|
||||
char* tm_zone @if(!env::WIN32);
|
||||
CInt tm_nsec @if(env::WASI);
|
||||
}
|
||||
|
||||
@@ -433,12 +444,12 @@ alias Clock_t @if(env::WIN32) = int;
|
||||
alias Clock_t @if(!env::WIN32) = CLong;
|
||||
|
||||
alias TimeOffset @if(env::WASI) = int;
|
||||
alias TimeOffset @if(!env::WASI) = CLong ;
|
||||
alias TimeOffset @if(!env::WASI) = CLong;
|
||||
|
||||
const int TIME_UTC = 1;
|
||||
|
||||
|
||||
// This is a best-effort aproximation, but the C standard does not enforce
|
||||
// This is a best-effort approximation, but the C standard does not enforce
|
||||
// that this is a compile-time standard.
|
||||
const CLOCKS_PER_SEC @if(env::WIN32) = 1000;
|
||||
const CLOCKS_PER_SEC @if(!env::WIN32) = 1000000;
|
||||
|
||||
@@ -17,19 +17,30 @@ struct Stat
|
||||
Gid_t st_gid;
|
||||
Dev_t st_rdev;
|
||||
|
||||
TimeSpec st_atimespec; // time of last access
|
||||
TimeSpec st_mtimespec; // time of last data modification
|
||||
TimeSpec st_ctimespec; // time of last status change
|
||||
TimeSpec st_birthtimespec; // time of file creation(birth)
|
||||
Off_t st_size; // file size, in bytes
|
||||
Blkcnt_t st_blocks; // blocks allocated for file
|
||||
Blksize_t st_blocksize; // optimal blocksize for I/O
|
||||
uint st_flags; // user defined flags for file
|
||||
uint st_gen; // file generation number
|
||||
int st_lspare; // RESERVED
|
||||
long[2] st_qspare; // RESERVED
|
||||
<* time of last access *>
|
||||
TimeSpec st_atimespec;
|
||||
<* time of last data modification *>
|
||||
TimeSpec st_mtimespec;
|
||||
<* time of last status change *>
|
||||
TimeSpec st_ctimespec;
|
||||
<* time of file creation(birth) *>
|
||||
TimeSpec st_birthtimespec;
|
||||
<* file size, in bytes *>
|
||||
Off_t st_size;
|
||||
<* blocks allocated for file *>
|
||||
Blkcnt_t st_blocks;
|
||||
<* optimal blocksize for I/O *>
|
||||
Blksize_t st_blocksize;
|
||||
<* user defined flags for file *>
|
||||
uint st_flags;
|
||||
<* file generation number *>
|
||||
uint st_gen;
|
||||
<* RESERVED *>
|
||||
int st_lspare;
|
||||
<* RESERVED *>
|
||||
long[2] st_qspare;
|
||||
}
|
||||
|
||||
extern fn int stat(ZString str, Stat* stat) @extern("stat64");
|
||||
extern fn int stat(ZString str, Stat* stat) @cname("stat64");
|
||||
|
||||
extern fn CInt sysctl(CInt *name, CUInt namelen, void *oldp, usz *oldlenp, void *newp, usz newlen);
|
||||
|
||||
@@ -218,7 +218,7 @@ enum Speed : const CUInt
|
||||
MAX_BAUD = B4000000,
|
||||
}
|
||||
|
||||
enum Cc : const char
|
||||
enum Cc : const inline char
|
||||
{
|
||||
VINTR = 0,
|
||||
VQUIT = 1,
|
||||
|
||||
@@ -62,7 +62,7 @@ struct SystemInfo
|
||||
ushort wProcessorRevision;
|
||||
}
|
||||
|
||||
extern fn CInt get_system_info(SystemInfo*) @extern("GetSystemInfo");
|
||||
extern fn CInt get_system_info(SystemInfo*) @cname("GetSystemInfo");
|
||||
|
||||
// Aliases to simplify libc use
|
||||
macro Tm* localtime_r(Time_t* timer, Tm* buf) => _localtime64_s(buf, timer);
|
||||
|
||||
@@ -506,19 +506,56 @@ fn BigInt BigInt.abs(&self)
|
||||
return self.is_negative() ? self.unary_minus() : *self;
|
||||
}
|
||||
|
||||
fn usz? BigInt.to_format(&self, Formatter* format) @dynamic
|
||||
{
|
||||
@stack_mem(4100; Allocator mem)
|
||||
{
|
||||
return format.print(self.to_string_with_radix(10, mem));
|
||||
};
|
||||
}
|
||||
|
||||
fn String BigInt.to_string(&self, Allocator allocator) @dynamic
|
||||
{
|
||||
return self.to_string_with_radix(10, allocator);
|
||||
}
|
||||
|
||||
fn usz? BigInt.to_format(&self, Formatter* format) @dynamic
|
||||
{
|
||||
if (self.is_zero())
|
||||
{
|
||||
format.print("0")!;
|
||||
return 1;
|
||||
}
|
||||
BigInt a = *self;
|
||||
bool negative = self.is_negative();
|
||||
usz len;
|
||||
if (negative)
|
||||
{
|
||||
format.out('-')!;
|
||||
len++;
|
||||
a.negate();
|
||||
}
|
||||
uint[280] chunks @noinit;
|
||||
int chunk_count;
|
||||
|
||||
const BASE10_9 = 1000000000;
|
||||
foreach_r(d : self.data[:self.len])
|
||||
{
|
||||
ulong carry = d;
|
||||
for (int i = 0; i < chunk_count; i++)
|
||||
{
|
||||
ulong v = (ulong)chunks[i] << 32 + carry;
|
||||
carry = v / BASE10_9;
|
||||
chunks[i] = (uint)(v - carry * BASE10_9);
|
||||
}
|
||||
if (carry)
|
||||
{
|
||||
ulong new_carry = carry / BASE10_9;
|
||||
chunks[chunk_count++] = (uint)(carry - new_carry * BASE10_9);
|
||||
if (new_carry) chunks[chunk_count++] = (uint)new_carry;
|
||||
}
|
||||
}
|
||||
int ms = chunk_count - 1;
|
||||
len += format.printf("%d", chunks[ms])!;
|
||||
foreach_r (c : chunks[:ms])
|
||||
{
|
||||
len += format.printf("%09d", c)!;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
<*
|
||||
@require radix > 1 && radix <= 36 : "Radix must be 2-36"
|
||||
*>
|
||||
@@ -527,7 +564,7 @@ fn String BigInt.to_string_with_radix(&self, int radix, Allocator allocator)
|
||||
if (self.is_zero()) return "0".copy(allocator);
|
||||
|
||||
const char[*] CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
@stack_mem(4100; Allocator mem)
|
||||
@stack_mem(4120; Allocator mem)
|
||||
{
|
||||
BigInt a = *self;
|
||||
DString str;
|
||||
|
||||
@@ -2,8 +2,8 @@ module std::math;
|
||||
|
||||
// Complex number aliases.
|
||||
|
||||
alias Complexf = Complex {float};
|
||||
alias Complex = Complex {double};
|
||||
alias Complexf = ComplexNumber {float};
|
||||
alias Complex = ComplexNumber {double};
|
||||
alias COMPLEX_IDENTITY @builtin = complex::IDENTITY {double};
|
||||
alias COMPLEXF_IDENTITY @builtin = complex::IDENTITY {float};
|
||||
alias IMAGINARY @builtin @deprecated("Use I") = complex::IMAGINARY { double };
|
||||
@@ -19,7 +19,7 @@ alias I_F @builtin = complex::IMAGINARY { float };
|
||||
module std::math::complex {Real};
|
||||
import std::io;
|
||||
|
||||
union Complex (Printable)
|
||||
union ComplexNumber (Printable)
|
||||
{
|
||||
struct
|
||||
{
|
||||
@@ -28,39 +28,39 @@ union Complex (Printable)
|
||||
Real[<2>] v;
|
||||
}
|
||||
|
||||
const Complex IDENTITY = { 1, 0 };
|
||||
const Complex IMAGINARY = { 0, 1 };
|
||||
const ComplexNumber IDENTITY = { 1, 0 };
|
||||
const ComplexNumber IMAGINARY = { 0, 1 };
|
||||
|
||||
macro Complex Complex.add(self, Complex b) @operator(+) => { .v = self.v + b.v };
|
||||
macro Complex Complex.add_this(&self, Complex b) @operator(+=) => { .v = self.v += b.v };
|
||||
macro Complex Complex.add_real(self, Real r) @operator_s(+) => { .v = self.v + (Real[<2>]) { r, 0 } };
|
||||
macro Complex Complex.add_each(self, Real b) => { .v = self.v + b };
|
||||
macro Complex Complex.sub(self, Complex b) @operator(-) => { .v = self.v - b.v };
|
||||
macro Complex Complex.sub_this(&self, Complex b) @operator(-=) => { .v = self.v -= b.v };
|
||||
macro Complex Complex.sub_real(self, Real r) @operator(-) => { .v = self.v - (Real[<2>]) { r, 0 } };
|
||||
macro Complex Complex.sub_real_inverse(self, Real r) @operator_r(-) => { .v = (Real[<2>]) { r, 0 } - self.v };
|
||||
macro Complex Complex.sub_each(self, Real b) => { .v = self.v - b };
|
||||
macro Complex Complex.scale(self, Real r) @operator_s(*) => { .v = self.v * r };
|
||||
macro Complex Complex.mul(self, Complex b)@operator(*) => { self.r * b.r - self.c * b.c, self.r * b.c + b.r * self.c };
|
||||
macro Complex Complex.div_real(self, Real r) @operator(/) => { .v = self.v / r };
|
||||
macro Complex Complex.div_real_inverse(Complex c, Real r) @operator_r(/) => ((Complex) { .r = r }).div(c);
|
||||
macro Complex Complex.div(self, Complex b) @operator(/)
|
||||
macro ComplexNumber ComplexNumber.add(self, ComplexNumber b) @operator(+) => { .v = self.v + b.v };
|
||||
macro ComplexNumber ComplexNumber.add_this(&self, ComplexNumber b) @operator(+=) => { .v = self.v += b.v };
|
||||
macro ComplexNumber ComplexNumber.add_real(self, Real r) @operator_s(+) => { .v = self.v + (Real[<2>]) { r, 0 } };
|
||||
macro ComplexNumber ComplexNumber.add_each(self, Real b) => { .v = self.v + b };
|
||||
macro ComplexNumber ComplexNumber.sub(self, ComplexNumber b) @operator(-) => { .v = self.v - b.v };
|
||||
macro ComplexNumber ComplexNumber.sub_this(&self, ComplexNumber b) @operator(-=) => { .v = self.v -= b.v };
|
||||
macro ComplexNumber ComplexNumber.sub_real(self, Real r) @operator(-) => { .v = self.v - (Real[<2>]) { r, 0 } };
|
||||
macro ComplexNumber ComplexNumber.sub_real_inverse(self, Real r) @operator_r(-) => { .v = (Real[<2>]) { r, 0 } - self.v };
|
||||
macro ComplexNumber ComplexNumber.sub_each(self, Real b) => { .v = self.v - b };
|
||||
macro ComplexNumber ComplexNumber.scale(self, Real r) @operator_s(*) => { .v = self.v * r };
|
||||
macro ComplexNumber ComplexNumber.mul(self, ComplexNumber b)@operator(*) => { self.r * b.r - self.c * b.c, self.r * b.c + b.r * self.c };
|
||||
macro ComplexNumber ComplexNumber.div_real(self, Real r) @operator(/) => { .v = self.v / r };
|
||||
macro ComplexNumber ComplexNumber.div_real_inverse(ComplexNumber c, Real r) @operator_r(/) => ((ComplexNumber) { .r = r }).div(c);
|
||||
macro ComplexNumber ComplexNumber.div(self, ComplexNumber b) @operator(/)
|
||||
{
|
||||
Real div = b.v.dot(b.v);
|
||||
return { (self.r * b.r + self.c * b.c) / div, (self.c * b.r - self.r * b.c) / div };
|
||||
}
|
||||
macro Complex Complex.inverse(self)
|
||||
macro ComplexNumber ComplexNumber.inverse(self)
|
||||
{
|
||||
Real sqr = self.v.dot(self.v);
|
||||
return { self.r / sqr, -self.c / sqr };
|
||||
}
|
||||
macro Complex Complex.conjugate(self) => { .r = self.r, .c = -self.c };
|
||||
macro Complex Complex.negate(self) @operator(-) => { .v = -self.v };
|
||||
macro bool Complex.equals(self, Complex b) @operator(==) => self.v == b.v;
|
||||
macro bool Complex.equals_real(self, Real r) @operator_s(==) => self.v == { r, 0 };
|
||||
macro bool Complex.not_equals(self, Complex b) @operator(!=) => self.v != b.v;
|
||||
macro ComplexNumber ComplexNumber.conjugate(self) => { .r = self.r, .c = -self.c };
|
||||
macro ComplexNumber ComplexNumber.negate(self) @operator(-) => { .v = -self.v };
|
||||
macro bool ComplexNumber.equals(self, ComplexNumber b) @operator(==) => self.v == b.v;
|
||||
macro bool ComplexNumber.equals_real(self, Real r) @operator_s(==) => self.v == { r, 0 };
|
||||
macro bool ComplexNumber.not_equals(self, ComplexNumber b) @operator(!=) => self.v != b.v;
|
||||
|
||||
fn usz? Complex.to_format(&self, Formatter* f) @dynamic
|
||||
fn usz? ComplexNumber.to_format(&self, Formatter* f) @dynamic
|
||||
{
|
||||
return f.printf("%g%+gi", self.r, self.c);
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import std::math::complex;
|
||||
import std::math::matrix;
|
||||
import std::math::quaternion;
|
||||
|
||||
attrdef @MathLibc(name) = @extern(name), @link(env::POSIX, "m");
|
||||
attrdef @MathLibc(name) = @cname(name), @link(env::POSIX, "m");
|
||||
|
||||
const E = 2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427427466;
|
||||
const LOG2E = 1.44269504088896340735992468100189214; // log2(e)
|
||||
@@ -73,6 +73,11 @@ faultdef OVERFLOW, MATRIX_INVERSE_DOESNT_EXIST;
|
||||
*>
|
||||
macro deg_to_rad(x) => x * PI / 180;
|
||||
|
||||
<*
|
||||
@require types::is_numerical($typeof(x)) : `The input must be a numerical value or numerical vector`
|
||||
*>
|
||||
macro rad_to_deg(x) => x * 180 / PI;
|
||||
|
||||
<*
|
||||
@require types::is_numerical($typeof(x)) : `The input must be a numerical value or numerical vector`
|
||||
*>
|
||||
@@ -82,7 +87,7 @@ macro abs(x) => $$abs(x);
|
||||
@require values::@is_int(x) || values::@is_float(x) : "Expected an integer or floating point value"
|
||||
@require values::@is_int(y) || values::@is_float(y) : "Expected an integer or floating point value"
|
||||
*>
|
||||
macro is_approx(x, y, eps)
|
||||
macro bool is_approx(x, y, eps)
|
||||
{
|
||||
if (x == y) return true;
|
||||
if (is_nan(x) || is_nan(y)) return false;
|
||||
@@ -93,7 +98,7 @@ macro is_approx(x, y, eps)
|
||||
@require values::@is_int(x) || values::@is_float(x) : "Expected an integer or floating point value"
|
||||
@require values::@is_int(y) || values::@is_float(y) : "Expected an integer or floating point value"
|
||||
*>
|
||||
macro is_approx_rel(x, y, eps)
|
||||
macro bool is_approx_rel(x, y, eps)
|
||||
{
|
||||
if (x == y) return true;
|
||||
if (is_nan(x) || is_nan(y)) return false;
|
||||
@@ -132,7 +137,7 @@ macro atan2(x, y)
|
||||
@require @typematch(sinp, cosp) : "Expected sinp and cosp to have the same type"
|
||||
@require $defined(*sinp = x) : "Expected x and *sinp/*cosp to have the same type"
|
||||
*>
|
||||
macro sincos_ref(x, sinp, cosp)
|
||||
macro void sincos_ref(x, sinp, cosp)
|
||||
{
|
||||
$if $typeof(sinp) == float*:
|
||||
_sincosf(x, sinp, cosp);
|
||||
@@ -252,8 +257,8 @@ macro @ceil($input) @const => $$ceil($input);
|
||||
@return "lower if x < lower, upper if x > upper, otherwise return x."
|
||||
|
||||
@require types::is_numerical($typeof(x)) : `The input must be a numerical value or numerical vector`
|
||||
@require $defined(x = lower) : `The lower bound must be convertable to the value type.`
|
||||
@require $defined(x = upper) : `The upper bound must be convertable to the value type.`
|
||||
@require $defined(x = lower) : `The lower bound must be convertible to the value type.`
|
||||
@require $defined(x = upper) : `The upper bound must be convertible to the value type.`
|
||||
*>
|
||||
macro clamp(x, lower, upper) => $$max(($typeof(x))lower, $$min(x, ($typeof(x))upper));
|
||||
|
||||
@@ -540,7 +545,7 @@ macro bool is_finite(x)
|
||||
<*
|
||||
@require values::@is_promotable_to_float(x) : `The input must be a float`
|
||||
*>
|
||||
macro is_nan(x)
|
||||
macro bool is_nan(x)
|
||||
{
|
||||
$switch $typeof(x):
|
||||
$case float:
|
||||
@@ -554,7 +559,7 @@ macro is_nan(x)
|
||||
<*
|
||||
@require values::@is_promotable_to_float(x) : `The input must be a float`
|
||||
*>
|
||||
macro is_inf(x)
|
||||
macro bool is_inf(x)
|
||||
{
|
||||
$switch $typeof(x):
|
||||
$case float:
|
||||
@@ -1042,8 +1047,8 @@ extern fn void _sincosf(float, float*, float*) @MathLibc("__sincosf") @if(env::D
|
||||
extern fn void _sincos(double, double*, double*) @MathLibc("sincos") @if(!env::DARWIN && !env::WIN32);
|
||||
extern fn void _sincosf(float, float*, float*) @MathLibc("sincosf") @if(!env::DARWIN && !env::WIN32);
|
||||
|
||||
fn void _sincos(double a, double* s, double* c) @extern("sincos") @if(env::WIN32) { *s = sin(a); *c = cos(a); }
|
||||
fn void _sincosf(float a, float* s, float* c) @extern("sincosf") @if(env::WIN32) { *s = sin(a); *c = cos(a); }
|
||||
fn void _sincos(double a, double* s, double* c) @cname("sincos") @if(env::WIN32) { *s = sin(a); *c = cos(a); }
|
||||
fn void _sincosf(float a, float* s, float* c) @cname("sincosf") @if(env::WIN32) { *s = sin(a); *c = cos(a); }
|
||||
|
||||
extern fn double _tan(double x) @MathLibc("tan");
|
||||
extern fn float _tanf(float x) @MathLibc("tanf");
|
||||
@@ -1157,7 +1162,7 @@ macro bool overflow_sub(a, b, out) => $$overflow_sub(a, b, out);
|
||||
@require values::@is_flat_intlike(a) &&& values::@is_flat_intlike(b) : "a and b must both be integer or integer vector based"
|
||||
@require $defined(*out) &&& @typematch(*out, a) : "out must be a pointer of the same type as a and b"
|
||||
*>
|
||||
macro bool overflow_mul(a, b, out) => $$overflow_mul(a, b, out);
|
||||
macro overflow_mul(a, b, out) => $$overflow_mul(a, b, out);
|
||||
|
||||
<*
|
||||
@require types::is_vector($Type) || ($Type.kindof == ARRAY &&& types::is_numerical($typefrom($Type.inner)))
|
||||
|
||||
@@ -12,7 +12,7 @@ module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
fn double __cos(double x, double y) @extern("__cos") @weak @nostrip
|
||||
fn double __cos(double x, double y) @cname("__cos") @weak @nostrip
|
||||
{
|
||||
const C1 = 4.16666666666666019037e-02; /* 0x3FA55555, 0x5555554C */
|
||||
const C2 = -1.38888888888741095749e-03; /* 0xBF56C16C, 0x16C15177 */
|
||||
|
||||
@@ -22,7 +22,7 @@ const double C1 @private = 0x155553e1053a42.0p-57; /* 0.0416666233237390631894
|
||||
const double C2 @private = -0x16c087e80f1e27.0p-62; /* -0.00138867637746099294692 */
|
||||
const double C3 @private = 0x199342e0ee5069.0p-68; /* 0.0000243904487962774090654 */
|
||||
|
||||
fn float __cosdf(double x) @extern("__cosdf") @weak @nostrip
|
||||
fn float __cosdf(double x) @cname("__cosdf") @weak @nostrip
|
||||
{
|
||||
/* Try to optimize for parallel evaluation as in __tandf.c. */
|
||||
double z = x * x;
|
||||
|
||||
@@ -13,7 +13,7 @@ union FloatInternal
|
||||
}
|
||||
|
||||
// Based on the musl implementation
|
||||
fn double fmod(double x, double y) @extern("fmod") @weak @nostrip
|
||||
fn double fmod(double x, double y) @cname("fmod") @weak @nostrip
|
||||
{
|
||||
DoubleInternal ux = { .f = x };
|
||||
DoubleInternal uy = { .f = y };
|
||||
@@ -83,7 +83,7 @@ fn double fmod(double x, double y) @extern("fmod") @weak @nostrip
|
||||
return ux.f;
|
||||
}
|
||||
|
||||
fn float fmodf(float x, float y) @extern("fmodf") @weak @nostrip
|
||||
fn float fmodf(float x, float y) @cname("fmodf") @weak @nostrip
|
||||
{
|
||||
FloatInternal ux = { .f = x };
|
||||
FloatInternal uy = { .f = y };
|
||||
|
||||
@@ -11,7 +11,7 @@ module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
fn double __sin(double x, double y, int iy) @extern("__sin") @weak @nostrip
|
||||
fn double __sin(double x, double y, int iy) @cname("__sin") @weak @nostrip
|
||||
{
|
||||
|
||||
const S1 = -1.66666666666666324348e-01; /* 0xBFC55555, 0x55555549 */
|
||||
|
||||
@@ -16,7 +16,7 @@ module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
* ====================================================
|
||||
*/
|
||||
// |sin(x)/x - s(x)| < 2**-37.5 (~[-4.89e-12, 4.824e-12]).
|
||||
fn float __sindf(double x) @extern("__sindf") @weak @nostrip
|
||||
fn float __sindf(double x) @cname("__sindf") @weak @nostrip
|
||||
{
|
||||
const S1F = -0x15555554cbac77.0p-55; /* -0.166666666416265235595 */
|
||||
const S2F = 0x111110896efbb2.0p-59; /* 0.0083333293858894631756 */
|
||||
|
||||
@@ -27,7 +27,7 @@ const double[*] TAN_T = {
|
||||
2.59073051863633712884e-05, /* 3EFB2A70, 74BF7AD4 */
|
||||
};
|
||||
|
||||
fn double __tan(double x, double y, int odd) @extern("__tan") @weak @nostrip
|
||||
fn double __tan(double x, double y, int odd) @cname("__tan") @weak @nostrip
|
||||
{
|
||||
const double PIO4 = 7.85398163397448278999e-01; /* 3FE921FB, 54442D18 */
|
||||
const double PIO4LO = 3.06161699786838301793e-17; /* 3C81A626, 33145C07 */
|
||||
|
||||
@@ -25,7 +25,7 @@ const double[*] TANDF = {
|
||||
0x1362b9bf971bcd.0p-59, /* 0.00946564784943673166728 */
|
||||
};
|
||||
|
||||
fn float __tandf(double x, int odd) @extern("__tandf") @weak @nostrip
|
||||
fn float __tandf(double x, int odd) @cname("__tandf") @weak @nostrip
|
||||
{
|
||||
double z = x * x;
|
||||
/*
|
||||
|
||||
@@ -32,7 +32,7 @@ fn double _r(double z) @local
|
||||
return p / q;
|
||||
}
|
||||
|
||||
fn double _acos(double x) @weak @extern("acos") @nostrip
|
||||
fn double _acos(double x) @weak @cname("acos") @nostrip
|
||||
{
|
||||
uint hx = x.high_word();
|
||||
uint ix = hx & 0x7fffffff;
|
||||
@@ -100,7 +100,7 @@ fn float _r_f(float z) @local
|
||||
return p / q;
|
||||
}
|
||||
|
||||
fn float _acosf(float x) @weak @extern("acosf") @nostrip
|
||||
fn float _acosf(float x) @weak @cname("acosf") @nostrip
|
||||
{
|
||||
uint hx = bitcast(x, uint);
|
||||
uint ix = hx & 0x7fffffff;
|
||||
|
||||
@@ -32,7 +32,7 @@ fn double _r(double z) @local
|
||||
return p / q;
|
||||
}
|
||||
|
||||
fn double _asin(double x) @weak @extern("asin") @nostrip
|
||||
fn double _asin(double x) @weak @cname("asin") @nostrip
|
||||
{
|
||||
uint hx = x.high_word();
|
||||
uint ix = hx & 0x7fffffff;
|
||||
@@ -102,7 +102,7 @@ fn float _r_f(float z) @local
|
||||
return p / q;
|
||||
}
|
||||
|
||||
fn float _asinf(float x) @weak @extern("asinf") @nostrip
|
||||
fn float _asinf(float x) @weak @cname("asinf") @nostrip
|
||||
{
|
||||
uint hx = bitcast(x, uint);
|
||||
uint ix = hx & 0x7fffffff;
|
||||
|
||||
@@ -40,7 +40,7 @@ const double[*] AT @private = {
|
||||
1.62858201153657823623e-02, /* 0x3F90AD3A, 0xE322DA11 */
|
||||
};
|
||||
|
||||
fn double _atan(double x) @weak @extern("atan") @nostrip
|
||||
fn double _atan(double x) @weak @cname("atan") @nostrip
|
||||
{
|
||||
int id @noinit;
|
||||
uint ix = x.high_word();
|
||||
@@ -138,7 +138,7 @@ const float[*] ATF @private = {
|
||||
6.1687607318e-02,
|
||||
};
|
||||
|
||||
fn float _atanf(float x) @weak @extern("atanf") @nostrip
|
||||
fn float _atanf(float x) @weak @cname("atanf") @nostrip
|
||||
{
|
||||
int id @noinit;
|
||||
uint ix = x.word();
|
||||
@@ -217,7 +217,7 @@ fn float _atanf(float x) @weak @extern("atanf") @nostrip
|
||||
|
||||
const PI_LO @private = 1.2246467991473531772E-16; /* 0x3CA1A626, 0x33145C07 */
|
||||
|
||||
fn double _atan2(double y, double x) @weak @extern("atan2") @nostrip
|
||||
fn double _atan2(double y, double x) @weak @cname("atan2") @nostrip
|
||||
{
|
||||
if (math::is_nan(x) || math::is_nan(y)) return x + y;
|
||||
|
||||
@@ -301,7 +301,7 @@ fn double _atan2(double y, double x) @weak @extern("atan2") @nostrip
|
||||
const float PI_F @private = 3.1415927410e+00; /* 0x40490fdb */
|
||||
const float PI_LO_F @private = -8.7422776573e-08; /* 0xb3bbbd2e */
|
||||
|
||||
fn float _atan2f(float y, float x) @weak @extern("atan2f") @nostrip
|
||||
fn float _atan2f(float y, float x) @weak @cname("atan2f") @nostrip
|
||||
{
|
||||
if (math::is_nan(x) || math::is_nan(y)) return x + y;
|
||||
uint ix = x.word();
|
||||
|
||||
@@ -12,7 +12,7 @@ module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
fn double _atanh(double x) @weak @extern("atanh") @nostrip
|
||||
fn double _atanh(double x) @weak @cname("atanh") @nostrip
|
||||
{
|
||||
double t @noinit;
|
||||
uint hx = x.high_word();
|
||||
@@ -61,7 +61,7 @@ fn double _atanh(double x) @weak @extern("atanh") @nostrip
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
fn float _atanhf(float x) @weak @extern("atanhf") @nostrip
|
||||
fn float _atanhf(float x) @weak @cname("atanhf") @nostrip
|
||||
{
|
||||
float t @noinit;
|
||||
uint hx = bitcast(x, uint);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
fn double _ceil(double x) @weak @extern("ceil") @nostrip
|
||||
fn double _ceil(double x) @weak @cname("ceil") @nostrip
|
||||
{
|
||||
ulong ui = bitcast(x, ulong);
|
||||
int e = (int)((ui >> 52) & 0x7ff);
|
||||
@@ -17,7 +17,7 @@ fn double _ceil(double x) @weak @extern("ceil") @nostrip
|
||||
}
|
||||
|
||||
|
||||
fn float _ceilf(float x) @weak @extern("ceilf") @nostrip
|
||||
fn float _ceilf(float x) @weak @cname("ceilf") @nostrip
|
||||
{
|
||||
uint u = bitcast(x, uint);
|
||||
int e = (int)((u >> 23) & 0xff) - 0x7f;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
fn float _cosf(float x) @extern("cosf") @weak @nostrip
|
||||
fn float _cosf(float x) @cname("cosf") @weak @nostrip
|
||||
{
|
||||
uint ix = x.word();
|
||||
uint sign = ix >> 31;
|
||||
@@ -51,7 +51,7 @@ fn float _cosf(float x) @extern("cosf") @weak @nostrip
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
fn double _cos(double x) @extern("cos") @weak @nostrip
|
||||
fn double _cos(double x) @cname("cos") @weak @nostrip
|
||||
{
|
||||
// High word of x.
|
||||
uint ix = x.high_word() & 0x7fffffff;
|
||||
|
||||
@@ -17,7 +17,7 @@ const float EXPF_P2 = -2.7777778450e-03f;
|
||||
const float EXPF_P3 = 6.6137559770e-05f;
|
||||
const float EXPF_P4 = -1.6533901999e-06f;
|
||||
|
||||
fn double exp(double x) @extern("exp") @nostrip @weak
|
||||
fn double exp(double x) @cname("exp") @nostrip @weak
|
||||
{
|
||||
if (x != x) return x;
|
||||
if (x == double.inf) return double.inf;
|
||||
@@ -38,7 +38,7 @@ fn double exp(double x) @extern("exp") @nostrip @weak
|
||||
return ldexp(exp_r, (int)k);
|
||||
}
|
||||
|
||||
fn float expf(float x) @extern("expf") @nostrip @weak
|
||||
fn float expf(float x) @cname("expf") @nostrip @weak
|
||||
{
|
||||
if (x != x) return x;
|
||||
if (x == float.inf) return float.inf;
|
||||
|
||||
@@ -3,7 +3,7 @@ module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
macro uint _top12f(float x) @private => bitcast(x, uint) >> 20;
|
||||
|
||||
|
||||
fn float _exp2f(float x) @extern("exp2f") @weak @nostrip
|
||||
fn float _exp2f(float x) @cname("exp2f") @weak @nostrip
|
||||
{
|
||||
double xd = x;
|
||||
uint abstop = _top12f(x) & 0x7ff;
|
||||
@@ -80,7 +80,7 @@ macro uint _top12d(double x) @private
|
||||
return (uint)(bitcast(x, ulong) >> 52);
|
||||
}
|
||||
|
||||
fn double _exp2(double x) @extern("exp2") @weak @nostrip
|
||||
fn double _exp2(double x) @cname("exp2") @weak @nostrip
|
||||
{
|
||||
uint abstop = _top12d(x) & 0x7ff;
|
||||
ulong u = bitcast(x, ulong);
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
fn double _fabs(double x) @weak @extern("fabs") @nostrip
|
||||
fn double _fabs(double x) @weak @cname("fabs") @nostrip
|
||||
{
|
||||
ulong ix = bitcast(x, ulong);
|
||||
ix &= ~(1ul << 63);
|
||||
return bitcast(ix, double);
|
||||
}
|
||||
|
||||
fn float _fabsf(float x) @weak @extern("fabsf") @nostrip
|
||||
fn float _fabsf(float x) @weak @cname("fabsf") @nostrip
|
||||
{
|
||||
uint ix = bitcast(x, uint);
|
||||
ix &= 0x7fffffff;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
fn double _floor(double x) @weak @extern("floor") @nostrip
|
||||
fn double _floor(double x) @weak @cname("floor") @nostrip
|
||||
{
|
||||
ulong ui = bitcast(x, ulong);
|
||||
int e = (int)((ui >> 52) & 0x7ff);
|
||||
@@ -17,7 +17,7 @@ fn double _floor(double x) @weak @extern("floor") @nostrip
|
||||
}
|
||||
|
||||
|
||||
fn float _floorf(float x) @weak @extern("floorf") @nostrip
|
||||
fn float _floorf(float x) @weak @cname("floorf") @nostrip
|
||||
{
|
||||
uint u = bitcast(x, uint);
|
||||
int e = (int)((u >> 23) & 0xff) - 0x7f;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
fn double frexp(double x, int* exp) @extern("frexp")
|
||||
fn double frexp(double x, int* exp) @cname("frexp")
|
||||
{
|
||||
uint hx = x.high_word();
|
||||
uint ix = hx & 0x7fffffff;
|
||||
@@ -31,7 +31,7 @@ fn double frexp(double x, int* exp) @extern("frexp")
|
||||
}
|
||||
}
|
||||
|
||||
fn float frexpf(float x, int* exp) @extern("frexpf")
|
||||
fn float frexpf(float x, int* exp) @cname("frexpf")
|
||||
{
|
||||
uint ix = x.word();
|
||||
uint hx = ix & 0x7fffffff;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
fn double ldexp(double x, int exp) @extern("ldexp")
|
||||
fn double ldexp(double x, int exp) @cname("ldexp")
|
||||
{
|
||||
uint hx = x.high_word();
|
||||
int hexp = (int)((hx & 0x7ff00000) >> 20);
|
||||
@@ -35,7 +35,7 @@ fn double ldexp(double x, int exp) @extern("ldexp")
|
||||
}
|
||||
}
|
||||
|
||||
fn float ldexpf(float x, int exp) @extern("ldexpf")
|
||||
fn float ldexpf(float x, int exp) @cname("ldexpf")
|
||||
{
|
||||
uint ix = x.word();
|
||||
int hexp = (int)((ix & 0x7f800000) >> 23);
|
||||
|
||||
@@ -19,7 +19,7 @@ const float LOGF_L4 = 2.4279078841e-01f;
|
||||
const double SQRT2 = 1.41421356237309504880;
|
||||
const float SQRT2F = 1.41421356237309504880f;
|
||||
|
||||
fn double log(double x) @extern("log") @nostrip @weak
|
||||
fn double log(double x) @cname("log") @nostrip @weak
|
||||
{
|
||||
if (x != x) return x;
|
||||
if (x < 0.0) return double.nan;
|
||||
@@ -50,7 +50,7 @@ fn double log(double x) @extern("log") @nostrip @weak
|
||||
return k * LOG_LN2_HI - ((hfsq - (s * (hfsq + r) + k * LOG_LN2_LO)) - f);
|
||||
}
|
||||
|
||||
fn float logf(float x) @extern("logf") @nostrip @weak
|
||||
fn float logf(float x) @cname("logf") @nostrip @weak
|
||||
{
|
||||
if (x != x) return x;
|
||||
if (x < 0.0f) return float.nan;
|
||||
|
||||
@@ -48,7 +48,7 @@ const LG5 @local = 1.818357216161805012e-01; /* 3FC74664 96CB03DE */
|
||||
const LG6 @local = 1.531383769920937332e-01; /* 3FC39A09 D078C69F */
|
||||
const LG7 @local = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */
|
||||
|
||||
fn double _log1p(double x) @weak @extern("log1p") @nostrip
|
||||
fn double _log1p(double x) @weak @cname("log1p") @nostrip
|
||||
{
|
||||
uint hx = x.high_word();
|
||||
int k = 1;
|
||||
@@ -162,7 +162,7 @@ const float LG2_F @local = 0xccce13.0p-25; /* 0.40000972152 */
|
||||
const float LG3_F @local = 0x91e9ee.0p-25; /* 0.28498786688 */
|
||||
const float LG4_F @local = 0xf89e26.0p-26; /* 0.24279078841 */
|
||||
|
||||
fn float _log1pf(float x) @weak @extern("log1pf") @nostrip
|
||||
fn float _log1pf(float x) @weak @cname("log1pf") @nostrip
|
||||
{
|
||||
uint ix = x.word();
|
||||
int k = 1;
|
||||
|
||||
@@ -55,7 +55,8 @@ struct Exp2Data @private
|
||||
double shift;
|
||||
double negln2hiN;
|
||||
double negln2loN;
|
||||
double[4] poly; // Last four coefficients.
|
||||
<* Last four coefficients. *>
|
||||
double[4] poly;
|
||||
double exp2_shift;
|
||||
double[EXP2_POLY_ORDER] exp2_poly;
|
||||
ulong[2 * EXP_DATA_WIDTH] tab;
|
||||
@@ -253,4 +254,4 @@ macro force_eval_add(x, v)
|
||||
{
|
||||
$typeof(x) temp @noinit;
|
||||
@volatile_store(temp, x + v);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
fn double pow(double x, double y) @extern("pow")
|
||||
fn double pow(double x, double y) @cname("pow")
|
||||
{
|
||||
if (x != x || y != y) return double.nan;
|
||||
|
||||
@@ -54,7 +54,7 @@ fn double pow(double x, double y) @extern("pow")
|
||||
return result;
|
||||
}
|
||||
|
||||
fn float powf(float x, float y) @extern("powf")
|
||||
fn float powf(float x, float y) @cname("powf")
|
||||
{
|
||||
if (x != x || y != y) return float.nan;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
fn double _round(double x) @extern("round") @weak @nostrip
|
||||
fn double _round(double x) @cname("round") @weak @nostrip
|
||||
{
|
||||
ulong u = bitcast(x, ulong);
|
||||
int e = (int)((u >> 52) & 0x7ff);
|
||||
@@ -26,7 +26,7 @@ fn double _round(double x) @extern("round") @weak @nostrip
|
||||
return y;
|
||||
}
|
||||
|
||||
fn float _roundf(float x) @extern("roundf") @weak @nostrip
|
||||
fn float _roundf(float x) @cname("roundf") @weak @nostrip
|
||||
{
|
||||
uint u = bitcast(x, uint);
|
||||
int e = (u >> 23) & 0xff;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
fn double _scalbn(double x, int n) @weak @extern("scalbn") @nostrip
|
||||
fn double _scalbn(double x, int n) @weak @cname("scalbn") @nostrip
|
||||
{
|
||||
switch
|
||||
{
|
||||
|
||||
@@ -16,7 +16,7 @@ module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
fn float _sinf(float x) @weak @extern("sinf") @nostrip
|
||||
fn float _sinf(float x) @weak @cname("sinf") @nostrip
|
||||
{
|
||||
uint ix = x.word();
|
||||
int sign = ix >> 31;
|
||||
@@ -84,7 +84,7 @@ fn float _sinf(float x) @weak @extern("sinf") @nostrip
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
fn double sin(double x) @extern("sin") @weak @nostrip
|
||||
fn double sin(double x) @cname("sin") @weak @nostrip
|
||||
{
|
||||
// High word of x.
|
||||
uint ix = x.high_word() & 0x7fffffff;
|
||||
|
||||
@@ -16,7 +16,7 @@ module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
fn void sincosf(float x, float *sin, float *cos) @extern("__sincosf") @weak @nostrip
|
||||
fn void sincosf(float x, float *sin, float *cos) @cname("__sincosf") @weak @nostrip
|
||||
{
|
||||
uint ix = x.word();
|
||||
uint sign = ix >> 31;
|
||||
@@ -104,7 +104,7 @@ fn void sincosf(float x, float *sin, float *cos) @extern("__sincosf") @weak @nos
|
||||
|
||||
}
|
||||
|
||||
fn void sincos(double x, double *sin, double *cos) @extern("__sincos") @weak @nostrip
|
||||
fn void sincos(double x, double *sin, double *cos) @cname("__sincos") @weak @nostrip
|
||||
{
|
||||
// High word of x.
|
||||
uint ix = x.high_word() & 0x7fffffff;
|
||||
|
||||
@@ -12,7 +12,7 @@ module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
fn double tan(double x) @extern("tan") @weak @nostrip
|
||||
fn double tan(double x) @cname("tan") @weak @nostrip
|
||||
{
|
||||
uint ix = x.high_word();
|
||||
ix &= 0x7fffffff;
|
||||
@@ -57,7 +57,7 @@ fn double tan(double x) @extern("tan") @weak @nostrip
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
fn float tanf(float x) @extern("tanf") @weak @nostrip
|
||||
fn float tanf(float x) @cname("tanf") @weak @nostrip
|
||||
{
|
||||
uint ix = x.word();
|
||||
uint sign = ix >> 31;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
fn double sincos_broken(double x) @extern("sincos") @weak @nostrip
|
||||
fn double sincos_broken(double x) @cname("sincos") @weak @nostrip
|
||||
{
|
||||
unreachable("'sinccos' not supported");
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
fn double _trunc(double x) @weak @extern("trunc") @nostrip
|
||||
fn double _trunc(double x) @weak @cname("trunc") @nostrip
|
||||
{
|
||||
ulong i = bitcast(x, ulong);
|
||||
int e = (int)((i >> 52) & 0x7ff) - 0x3ff + 12;
|
||||
@@ -13,7 +13,7 @@ fn double _trunc(double x) @weak @extern("trunc") @nostrip
|
||||
return bitcast(i, double);
|
||||
}
|
||||
|
||||
fn float _truncf(float x) @weak @extern("truncf") @nostrip
|
||||
fn float _truncf(float x) @weak @cname("truncf") @nostrip
|
||||
{
|
||||
uint i = bitcast(x, uint);
|
||||
int e = (int)((i >> 23) & 0xff) - 0x7f + 9;
|
||||
|
||||
@@ -2,8 +2,8 @@ module std::math;
|
||||
|
||||
// Predefined quaternion aliases.
|
||||
|
||||
alias Quaternionf = Quaternion {float};
|
||||
alias Quaternion = Quaternion {double};
|
||||
alias Quaternionf = QuaternionNumber {float};
|
||||
alias Quaternion = QuaternionNumber {double};
|
||||
alias QUATERNION_IDENTITY @builtin = quaternion::IDENTITY {double};
|
||||
alias QUATERNIONF_IDENTITY @builtin = quaternion::IDENTITY {float};
|
||||
|
||||
@@ -15,7 +15,7 @@ alias QUATERNIONF_IDENTITY @builtin = quaternion::IDENTITY {float};
|
||||
|
||||
module std::math::quaternion {Real};
|
||||
import std::math::vector;
|
||||
union Quaternion
|
||||
union QuaternionNumber
|
||||
{
|
||||
struct
|
||||
{
|
||||
@@ -24,22 +24,25 @@ union Quaternion
|
||||
Real[<4>] v;
|
||||
}
|
||||
|
||||
const Quaternion IDENTITY = { 0, 0, 0, 1 };
|
||||
const QuaternionNumber IDENTITY = { 0, 0, 0, 1 };
|
||||
|
||||
macro Quaternion Quaternion.add(self, Quaternion b) @operator(+) => { .v = self.v + b.v };
|
||||
macro Quaternion Quaternion.add_each(self, Real b) => { .v = self.v + b };
|
||||
macro Quaternion Quaternion.sub(self, Quaternion b) @operator(-) => { .v = self.v - b.v };
|
||||
macro Quaternion Quaternion.negate(self) @operator(-) => { .v = -self.v };
|
||||
macro Quaternion Quaternion.sub_each(self, Real b) => { .v = self.v - b };
|
||||
macro Quaternion Quaternion.scale(self, Real s) @operator_s(*) => { .v = self.v * s };
|
||||
macro Quaternion Quaternion.normalize(self) => { .v = self.v.normalize() };
|
||||
macro Real Quaternion.length(self) => self.v.length();
|
||||
macro Quaternion Quaternion.lerp(self, Quaternion q2, Real amount) => { .v = self.v.lerp(q2.v, amount) };
|
||||
macro Matrix4f Quaternion.to_matrixf(&self) => into_matrix(self, Matrix4f);
|
||||
macro Matrix4 Quaternion.to_matrix(&self) => into_matrix(self, Matrix4);
|
||||
fn Quaternion Quaternion.nlerp(self, Quaternion q2, Real amount) => { .v = self.v.lerp(q2.v, amount).normalize() };
|
||||
macro QuaternionNumber QuaternionNumber.add(self, QuaternionNumber b) @operator(+) => { .v = self.v + b.v };
|
||||
macro QuaternionNumber QuaternionNumber.add_each(self, Real b) => { .v = self.v + b };
|
||||
macro QuaternionNumber QuaternionNumber.sub(self, QuaternionNumber b) @operator(-) => { .v = self.v - b.v };
|
||||
macro QuaternionNumber QuaternionNumber.negate(self) @operator(-) => { .v = -self.v };
|
||||
macro QuaternionNumber QuaternionNumber.sub_each(self, Real b) => { .v = self.v - b };
|
||||
macro QuaternionNumber QuaternionNumber.scale(self, Real s) @operator_s(*) => { .v = self.v * s };
|
||||
macro QuaternionNumber.to_angle(self) => 2 * math::acos(self.v.w);
|
||||
macro QuaternionNumber QuaternionNumber.normalize(self) => { .v = self.v.normalize() };
|
||||
macro Real QuaternionNumber.length(self) => self.v.length();
|
||||
macro QuaternionNumber QuaternionNumber.lerp(self, QuaternionNumber q2, Real amount) => { .v = self.v.lerp(q2.v, amount) };
|
||||
fn QuaternionNumber QuaternionNumber.nlerp(self, QuaternionNumber q2, Real amount) => { .v = self.v.lerp(q2.v, amount).normalize() };
|
||||
|
||||
fn Quaternion Quaternion.invert(self)
|
||||
macro Matrix4f QuaternionNumber.to_matrixf(&self) => into_matrix(self, Matrix4f);
|
||||
macro Matrix4 QuaternionNumber.to_matrix(&self) => into_matrix(self, Matrix4);
|
||||
|
||||
|
||||
fn QuaternionNumber QuaternionNumber.invert(self)
|
||||
{
|
||||
Real length_sq = self.v.dot(self.v);
|
||||
if (length_sq <= 0) return self;
|
||||
@@ -47,9 +50,11 @@ fn Quaternion Quaternion.invert(self)
|
||||
return { self.v[0] * -inv_length, self.v[1] * -inv_length, self.v[2] * -inv_length, self.v[3] * inv_length };
|
||||
}
|
||||
|
||||
fn Quaternion Quaternion.slerp(self, Quaternion q2, Real amount)
|
||||
fn QuaternionNumber QuaternionNumber.conjugate(&self) => { -self.v.x, -self.v.y, -self.v.z, self.v.w };
|
||||
|
||||
fn QuaternionNumber QuaternionNumber.slerp(self, QuaternionNumber q2, Real amount)
|
||||
{
|
||||
Quaternion result = {};
|
||||
QuaternionNumber result = {};
|
||||
|
||||
Real[<4>] q2v = q2.v;
|
||||
Real cos_half_theta = self.v.dot(q2v);
|
||||
@@ -76,26 +81,48 @@ fn Quaternion Quaternion.slerp(self, Quaternion q2, Real amount)
|
||||
return { .v = q1v * ratio_a + q2v * ratio_b };
|
||||
}
|
||||
|
||||
fn Quaternion Quaternion.mul(self, Quaternion b) @operator(*)
|
||||
{
|
||||
return { self.i * b.l + self.l * b.i + self.j * b.k - self.k * b.j,
|
||||
self.j * b.l + self.l * b.j + self.k * b.i - self.i * b.k,
|
||||
self.k * b.l + self.l * b.k + self.i * b.j - self.j * b.i,
|
||||
self.l * b.l - self.i * b.i - self.j * self.j - self.k * self.k };
|
||||
fn QuaternionNumber QuaternionNumber.mul(self, QuaternionNumber b) @operator(*)
|
||||
{
|
||||
Real[<3>] q1_axis = { self.v.x, self.v.y, self.v.z };
|
||||
Real[<3>] q2_axis = { b.v.x, b.v.y, b.v.z };
|
||||
|
||||
Real scalar = (self.v.w * b.v.w - q1_axis.dot(q2_axis));
|
||||
Real[<3>] axis = self.v.w * q2_axis + b.v.w * q1_axis + q1_axis.cross(q2_axis);
|
||||
|
||||
return { ...axis, scalar };
|
||||
}
|
||||
|
||||
macro into_matrix(Quaternion* q, $Type) @private
|
||||
|
||||
fn QuaternionNumber from_axis_angle(Real[<3>] axis, Real angle)
|
||||
{
|
||||
Quaternion rotation = q.normalize();
|
||||
Real[<3>] normal_axis = axis.normalize();
|
||||
Real half_angle = angle * 0.5;
|
||||
Real sin_half = math::sin(half_angle);
|
||||
|
||||
return { ...(normal_axis * sin_half), math::cos(half_angle) };
|
||||
}
|
||||
|
||||
fn Real[<3>] QuaternionNumber.rotate_vec3(self, Real[<3>] vector) @operator(*)
|
||||
{
|
||||
QuaternionNumber p = { ...vector, 0 };
|
||||
QuaternionNumber result = self * p * self.conjugate();
|
||||
return result.v.xyz;
|
||||
}
|
||||
|
||||
|
||||
|
||||
macro into_matrix(QuaternionNumber* q, $Type) @private
|
||||
{
|
||||
QuaternionNumber rotation = q.normalize();
|
||||
var x = rotation.i;
|
||||
var y = rotation.j;
|
||||
var z = rotation.k;
|
||||
var w = rotation.l;
|
||||
|
||||
return ($Type) {
|
||||
1 - 2*y*y - 2*z*z, 2*x*y - 2*z*w, 2*x*z + 2*y*w, 0,
|
||||
2*x*y + 2*z*w, 1 - 2*x*x - 2*z*z, 2*y*z - 2*x*w, 0,
|
||||
2*x*z - 2*y*w, 2*y*z + 2*x*w , 1 - 2*x*x - 2*y*y, 0,
|
||||
0.0, 0.0, 0.0, 1.0,
|
||||
1 - 2*y*y - 2*z*z, 2*x*y - 2*z*w, 2*x*z + 2*y*w, 0,
|
||||
2*x*y + 2*z*w, 1 - 2*x*x - 2*z*z, 2*y*z - 2*x*w, 0,
|
||||
2*x*z - 2*y*w, 2*y*z + 2*x*w, 1 - 2*x*x - 2*y*y, 0,
|
||||
0.0, 0.0, 0.0, 1.0,
|
||||
};
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user